完整效果演示
首先完成这个伪搜索框
src/components/search/index.vue (通用搜索框组件)
<template> <div class="mine-search-box-wrapper"> <i class="iconfont icon-search"></i> <div class="mine-search-box" v-if="fake">{{placeholder}}</div> <input class="mine-search-box" type="text" title="搜索框" :placeholder="placeholder" ref="input" v-model="query" v-if="!fake" > <i class="iconfont icon-close" v-show="query" @click="reset" ></i> </div> </template> <script> import {debounde} from 'assets/js/util'; export default { name:'Search', props:{//接收的参数 placeholder:{ type:String, default:'请输入搜索内容' }, fake:{ type:Boolean, default:false } }, data(){ return{ query:'', } }, watch:{ query:debounde(function(){ this.$emit('query',this.query); }) }, methods:{ focus(){ this.$refs.input && this.$refs.input.focus(); }, clear(){ this.query=''; }, reset(){//重置 this.clear(); this.focus(); } } } </script> <style lang="scss" scoped> $search-box-height: 30px; $icon-color: #ccc; $icon-font-size-sm: 18px; .mine-search-box-wrapper { display: flex; align-items: center; width: 85%; height: $search-box-height; padding: 0 7px; background-color: #fff; border-radius: $search-box-height / 2; margin-left:15px; } .iconfont { color: $icon-color; font-size: $icon-font-size-sm; font-weight: bold; } .mine-search-box { flex: 1; background: none; border: none; margin: 0 6px; color: #666; line-height: 1.5; } </style>
src/assets/js/util.js 节流函数(防止请求数据时频率过快消耗性能)
//函数节流 export const debounde=(func,delay=200)=>{ let timer=null; return function(...args){ timer && clearTimeout(timer); timer=setTimeout(()=>{ func.apply(this,args); },delay); } }
在分类页的头部组件中引入搜索框组件
src/pages/category/header.vue
<template> <div class="header"> <i class="iconfont icon-scan header-left"></i> <div class="header-center"> <search placeholder="开学季有礼,好货五折起" @query='getQuery' fake @click.native="goToSearch" /> </div> <i class="iconfont icon-msg header-right"></i> </div> </template> <script> import Search from 'components/search'; export default { name:'CategoryHeader', components:{ Search }, methods:{ getQuery(query){ console.log(query); }, goToSearch(){ this.$router.push('/search'); } } } </script> <style lang="scss" scoped> .header{ background-color:rgba(222, 24, 27, 0.9); transition:background-color 0.5s; display: flex; justify-content: space-between; align-items: center; padding:5px 20px; .iconfont{ font-size:24px; color:#fff; } .header-center{ flex:1; } } </style>
点击搜索框之后会跳转到真正的搜索页
热门搜索组件
src/pages/search/hot.vue
<template> <div class="hot"> <h4 class="hot-title">热门搜索</h4> <div class="loading-container" v-if="!hots.length"> <me-loading/> </div> <ul class="hot-list" v-else> <li class="hot-item" v-for="(item,index) in hots" :key="index" @click="$_selectItem(item.hotWord)"> {{item.hotWord}} </li> </ul> </div> </template> <script> import Search from 'components/search'; import MeLoading from 'components/loading'; import {getHot} from 'api/search'; import {searchMixin} from 'api/mixins'; export default { name:'SearchHot', components:{ MeLoading }, data(){ return{ hots:[] } }, mixins:[searchMixin], created(){ this.getHot().then(()=>{ this.$emit('loaded'); }) }, methods:{ getHot(){ return getHot().then(data=>{ return new Promise(resolve=>{ if(data){ this.hots=data; resolve(); } }) }) } } } </script> <style lang="scss" scoped> $border-color: #e5e5e5; $font-size-base: 12px; $font-size-l: $font-size-base + 2; .hot { padding-left: 10px; background-color: #fff; border-bottom: 1px solid $border-color; margin-bottom: 10px; &-title { height: 34px; line-height: 34px; font-size: $font-size-l; font-weight: bold; } &-list { display: flex; flex-wrap: wrap; } &-item { padding: 8px; background-color: #f0f2f5; border-radius: 4px; margin: 0 10px 10px 0; color: #686868; } } .loading-container { padding: 10px 0; } </style>
axios获取热门搜索数据
src/api/search.js
import axios from 'axios'; //获取热门搜索数据 ajax export const getHot=()=>{ return axios.get('http://www.imooc.com/api/search/hot').then(res=>{ res=res.data.hotKeyWord; if(res && res.owner){ return res.owner; } throw new Error('没有成功获取到数据'); }).catch(err=>{ console.log(err); }); }
点击搜索的关键词,跳转到淘宝搜索程序
src/api/mixins.js
import storage from 'assets/js/storage'; import {SEARCH_HISTORY_KEYWORD_KEY} from 'pages/search/config'; export const searchMixin={ methods:{ $_selectItem(keyword){ let keywords=storage.get(SEARCH_HISTORY_KEYWORD_KEY,[]);//找到所有搜索历史 if(keywords.length!=0){ keywords=keywords.filter(val=>val!=keyword);//这次的关键词如果在搜索历史里已存在,先剔除掉 } keywords.unshift(keyword);//把这次的关键词放在搜索历史的最开头 storage.set(SEARCH_HISTORY_KEYWORD_KEY,keywords);//更新搜索历史 //跳转到淘宝搜索页 location.href = `https://s.m.taobao.com/h5"htmlcode">const storage = window.localStorage; export default { set(key, val) { if (val === undefined) { return; } storage.setItem(key, serialize(val)); }, get(key, def) { const val = deserialize(storage.getItem(key)); return val === undefined "htmlcode">const prefix = 'mall-search'; const suffix = 'key'; export const SEARCH_HISTORY_KEYWORD_KEY = `${prefix}-history-keyword-${suffix}`;历史搜索组件
src/pages/search/history.vue
<template> <div class="history" v-if="historys.length"> <h4 class="history-title">历史搜索</h4> <transition-group class="g-list" tag="ul" name="list"> <li class="g-list-item" v-for="item in historys" :key="item" @click="$_selectItem(item)"> <span class="g-list-text">{{item}}</span> <!-- .stop 禁止事件冒泡 --> <i class="iconfont icon-delete" @click.stop="removeItem(item)"></i> </li> </transition-group> <a href="javascript:;" rel="external nofollow" class="history-btn" @click="showConfirm"> <i class="iconfont icon-clear" ></i> 清空历史搜索 </a> </div> </template> <script> import storage from 'assets/js/storage'; import {SEARCH_HISTORY_KEYWORD_KEY} from 'pages/search/config'; import {searchMixin} from 'api/mixins'; export default { name:'SearchHistory', data(){ return{ historys:[] } }, mixins:[searchMixin], created(){ this.getKeyword(); }, methods:{ update(){ this.getKeyword(); }, getKeyword(){ this.historys=storage.get(SEARCH_HISTORY_KEYWORD_KEY,[]); this.$emit('loaded'); }, removeItem(item){ this.historys=this.historys.filter(val=>val!==item);//点击后删除该项 storage.set(SEARCH_HISTORY_KEYWORD_KEY,this.historys);//更新缓存 this.$emit('remove-item'); }, showConfirm(){ this.$emit('show-confirm'); }, clear(){ storage.remove(SEARCH_HISTORY_KEYWORD_KEY); } } } </script> <style lang="scss" scoped> $border-color: #e5e5e5; $font-size-base: 12px; $font-size-l: $font-size-base + 2; $border-color: #e5e5e5; @mixin flex-center($direction: row) { display: flex; justify-content: center; align-items: center; flex-direction: $direction; } .history { padding-bottom: 30px; background-color: #fff; &-title { height: 34px; line-height: 34px; padding: 0 10px; font-size: $font-size-l; font-weight: bold; } &-btn { @include flex-center(); width: 80%; height: 40px; background: none; border: 1px solid #ccc; border-radius: 4px; margin: 0 auto; color: #686868; .iconfont { margin-right: 5px; } } } .g-list { border-top: 1px solid $border-color; border-bottom: 1px solid $border-color; margin-bottom: 20px; } .list { &-enter-active, &-leave-active { transition: height 0.1s; } &-enter, &-leave-to { height: 0; } } </style>列表样式统一抽离出去
src/assets/scss/_list.scss
// list @mixin flex-between() { display: flex; justify-content: space-between; align-items: center; } //ellipsis @mixin ellipsis() { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } $border-color: #e5e5e5; .g-list { padding-left: 10px; } .g-list-item { overflow: hidden; @include flex-between(); height: 44px; padding-right: 10px; border-bottom: 1px solid $border-color; color: #686868; &:last-child { border-bottom: none; } } .g-list-text { flex: 1; line-height: 1.5; @include ellipsis(); }src/assets/scss/index.scss
@import 'icons'; @import 'list'; *{ margin:0; padding:0; } html,body{ // 必须设置,否则内容滚动效果无法实现 width:100%; height:100%; } ul,li{ list-style:none; } a{ text-decoration: none; color:#333; }确认框组件
src/components/comfirm/index.vue
<template> <transition name="mine-confirm"> <div class="mine-confirm-wrapper" v-show="visible"> <div class="mine-confirm"> <div class="mine-confirm-title">{{title}}</div> <div class="mine-confirm-msg">{{msg}}</div> <div class="mine-confirm-btns"> <button class="mine-confirm-btn mine-confirm-cancel" @click="cancel">{{cancelBtnText}}</button> <button class="mine-confirm-btn mine-confirm-ok" @click="confirm">{{confirmBtnText}}</button> </div> </div> </div> </transition> </template> <script> export default { name:'MineConfirm', props:{ title:{ type:String, default:'' }, msg:{ type:String, default:'确定执行此操作吗?' }, cancelBtnText:{ type:String, default:'取消' }, confirmBtnText:{ type:String, default:'确定' } }, data(){ return{ visible:false } }, methods:{ show(){ this.visible=true; }, hide(){ this.visible=false; }, cancel(){ this.hide(); this.$emit('cancel'); }, confirm(){ this.hide(); this.$emit('confirm'); } } } </script> <style lang="scss" scoped> $search-z-index: 1200; $search-popup-z-index: $search-z-index + 10; $modal-bgc: rgba(0, 0, 0, 0.4); @mixin flex-center($direction: row) { display: flex; justify-content: center; align-items: center; flex-direction: $direction; } @mixin ellipsis() { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .mine-confirm-wrapper { position: absolute; top: 0; right: 0; bottom: 0; left: 0; z-index: $search-popup-z-index; @include flex-center(); background-color: $modal-bgc; } .mine-confirm { overflow: hidden; width: 88%; background-color: #fff; border-radius: 10px; font-size: 16px; &-title { padding: 20px 15px 0; font-size: 18px; text-align: center; @include ellipsis(); & + .mine-confirm-msg { padding-top: 20px; padding-bottom: 20px; } } &-msg { padding: 40px 15px; text-align: center; line-height: 1.5; } &-btns { display: flex; } &-btn { flex: 1; height: 44px; line-height: 44px; background: none; border: none; } &-cancel { border-top: 1px solid #e3e5e9; } &-ok { background-color: #f23030; color: #fff; } } .mine-confirm { &-enter-active, &-leave-active { transition: opacity 0.3s; } &-enter, &-leave-to { opacity: 0; } &-enter-active { .mine-confirm { animation: bounce-in 0.3s; } } } // https://cn.vuejs.org/v2/guide/transitions.html#CSS-动画 @keyframes bounce-in { 0% { transform: scale(0); } 50% { transform: scale(1.1); } 100% { transform: scale(1); } } </style>搜索结果页
src/pages/search/result.vue
<template> <div class="result"> <div class="loading-container" v-show="loading"> <me-loading/> </div> <ul class="g-list" v-show="!loading && results.length"> <li class="g-list-item" v-for="(item, index) in results" :key="index" @click="$_selectItem(item[0])" > <span class="g-list-text">{{item[0]}}</span> </li> </ul> <div class="no-result" v-show="!loading && !results.length">没有结果</div> </div> </template> <script> import MeLoading from 'components/loading'; import {getSearchResult} from 'api/search'; import {searchMixin} from 'api/mixins'; export default { name:'SearchResult', components:{ MeLoading }, data(){ return{ results:[], loading:false } }, props:{ query:{ type:String, default:'' } }, mixins:[searchMixin], watch:{ query(query){ this.getResults(query); } }, methods:{ getResults(keyword){ if(!keyword){ return; } this.loading=true; getSearchResult(keyword).then(data=>{ console.log(data); if(data){ this.results=data; this.loading=false; } }) } } } </script>修改src/api/search.js
import axios from 'axios'; import jsonp from 'assets/js/jsonp'; //获取热门搜索数据 ajax export const getHot=()=>{ return axios.get('http://www.imooc.com/api/search/hot').then(res=>{ res=res.data.hotKeyWord; if(res && res.owner){ return res.owner; } throw new Error('没有成功获取到数据'); }).catch(err=>{ console.log(err); }); } //获取搜索框的搜索结果 export const getSearchResult=keyword=>{ const url='https://suggest.taobao.com/sug'; const params={ q:keyword, code:'utf-8', area:'c2c', nick:'', sid:null }; //https://suggest.taobao.com/sug"text-align: center">修改src/pages/search/history.vue
(因为页面加载时有100ms延迟的动画,因此这里更新滚动条也需要相同的延迟)
注意滚动条组件的更新操作,需要使用
$nextTick( )
实现异步总结
华山资源网 Design By www.eoogi.com
免责声明:本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除!
稳了!魔兽国服回归的3条重磅消息!官宣时间再确认!
昨天有一位朋友在大神群里分享,自己亚服账号被封号之后居然弹出了国服的封号信息对话框。
这里面让他访问的是一个国服的战网网址,com.cn和后面的zh都非常明白地表明这就是国服战网。
而他在复制这个网址并且进行登录之后,确实是网易的网址,也就是我们熟悉的停服之后国服发布的暴雪游戏产品运营到期开放退款的说明。这是一件比较奇怪的事情,因为以前都没有出现这样的情况,现在突然提示跳转到国服战网的网址,是不是说明了简体中文客户端已经开始进行更新了呢?
更新日志
- 小骆驼-《草原狼2(蓝光CD)》[原抓WAV+CUE]
- 群星《欢迎来到我身边 电影原声专辑》[320K/MP3][105.02MB]
- 群星《欢迎来到我身边 电影原声专辑》[FLAC/分轨][480.9MB]
- 雷婷《梦里蓝天HQⅡ》 2023头版限量编号低速原抓[WAV+CUE][463M]
- 群星《2024好听新歌42》AI调整音效【WAV分轨】
- 王思雨-《思念陪着鸿雁飞》WAV
- 王思雨《喜马拉雅HQ》头版限量编号[WAV+CUE]
- 李健《无时无刻》[WAV+CUE][590M]
- 陈奕迅《酝酿》[WAV分轨][502M]
- 卓依婷《化蝶》2CD[WAV+CUE][1.1G]
- 群星《吉他王(黑胶CD)》[WAV+CUE]
- 齐秦《穿乐(穿越)》[WAV+CUE]
- 发烧珍品《数位CD音响测试-动向效果(九)》【WAV+CUE】
- 邝美云《邝美云精装歌集》[DSF][1.6G]
- 吕方《爱一回伤一回》[WAV+CUE][454M]