vue-music 关于Search(搜索页面)--上拉加载
建立搜索框组件页面,searchBox,组件接受一个可以自定义传入的placeholder 属性。input v-model 双向绑定数据关联到query 中, 在created中监听 query 变量将改变的新值派发给外部父组件,在search.vue 组件中将其引入
<div class="search-box"> <i class="icon-search"></i> <input type="text" class="box" :placeholder="placeholder" v-model="query"> <i class="icon-dismiss" v-show="query" @click="clear"></i> </div>
export default { props:{ placeholder:{ type:String, default:'搜索歌曲、歌手' } }, data(){ return { query:'' } }, created(){ this.$watch('query',(newQuery) => { this.$emit('query',newQuery); }) }, methods:{ clear(){ this.query = ''; }, setQuery(query){ // 创建主动设置input 变量值,赋值给父级data中 this.query = query; } } }
search.vue
热门搜索模块,通过search.js 的getHotKey 函数异步获取数据,并渲染,给每个item 上绑定addQuery(item.k) 将其值赋值给input 的value 值,通过调用 this.$refs.searchBox.setQuery(query)
<li class="item" v-for="item in hotKey" @click="addQuery(item.k)"><span>{{item.k}}</span></li> methods:{ addQuery(query){ this.$refs.searchBox.setQuery(query); }, onQueryChange(query){ //监听派发过来的query 属性,报存到父级的data 变量中,可以用于判断用户是否有输入搜索值做相应的业务逻辑 this.query = query; }, _getHotKey(){ getHotKey().then((res) => { if(res.code === ERR_OK){ this.hotKey = res.data.hotkey.slice(0,10); } }) } },
当搜索框有关键词的时候,就应该发出请求 搜索对应的歌曲或者歌手。请求数据格式返回 包含两个字段,song 为搜索歌曲数据,zhida 为搜索关键字为歌手的数据,上拉加载后面再说,首先要把获取的数据格式化成想要的数据格式
通过_genResult 方法判断如果搜索结果中有zhida字段并且有zhida.singerid 则说明搜索关键字为歌手,将其push到新数组中,判断是否有相关歌曲song 字段 ,再把歌曲列表追加到新数组中并返回
创建suggest.vue 组件展示搜索结果列表,如果列表项是歌手前面的图标根据返回的结果来判断替换相应图标。歌手名称和歌曲名称同理
<scroll class="suggest" :data="result" :pullup="pullup" @scrollToEnd="searchMore" ref="suggest"> <ul class="suggest-list"> <li class="suggest-item" v-for="item in result"> <div class="icon"> <i :class="getIconCls(item)"></i> </div> <div class="name"> <p class="text" v-html="getDisplayName(item)"></p> </div> </li> <loading v-show="hasMore" title=""></loading> <div class="under-line" v-show="!hasMore">到我底线了</div> </ul> </scroll>
引入sceoll 组件,开启上拉加载事件 :pullup:'true'
if(this.pullup){ this.scroll.on('scrollEnd',()=>{ if(this.scroll.y <= (this.scroll.maxScrollY + 50)){ //当滚动距离离底部50 像素的时候,派发事件,父级监听此事件做再次请求数据接口 this.$emit('scrollToEnd') } }) }
监听派发上拉加载事件 @scrollToEnd="searchMore"
在首次加载数据的时候设置 监测还有没有数据的标志位 hasMore = true 在成功获取前20条数据的时候调用监测函数 this.checkMore(res.data); checkMore根据传来的请求值判断 当前页数乘以每页数量 加上 每页加载数量 如果大于等于数据的总数量,则表示下一页已无数据,设置标志位为false ,在上拉加载函数 searchMore 中 首先判断标志位,如果有数据,页码加一,再请求数据接口,将结果 concat追加到result 数据中。注意这里再一次请求的时候,将showSinger 参数设置为false,表示第二页请求不再显示歌手
import {search} from "api/search.js"; import {ERR_OK} from "api/config.js"; import {createSong} from "common/js/song.js"; import Scroll from 'base/scroll/scroll.vue' import Loading from 'base/loading/loading.vue' const TYPE_SINGER = 'singer'; const perpage = 20; export default { props:{ query:{ type:String, default:'' }, showSinger:{ type:Boolean, default:true } }, data(){ return { page:1, result:[], pullup:true, hasMore:true } }, methods:{ search(){ this.page = 1; this.hasMore = true; this.$refs.suggest.scrollTo(0,0); search(this.query,this.page,this.showSinger,perpage).then((res) => { if(res.code === ERR_OK){ this.result= this._genResult(res.data); this.checkMore(res.data); } }) }, searchMore(){ if(!this.hasMore){ return; } this.page++; search(this.query,this.page,false,perpage).then((res) => { if(res.code === ERR_OK){ this.result= this.result.concat(this._genResult(res.data)); console.log(this.result) this.checkMore(res.data); } }) }, checkMore(data){ let song = data.song; if(!song.list.length || (song.curnum + song.curpage * perpage) >= song.totalnum){ this.hasMore = false; } }, getIconCls(item){ if(item.type === TYPE_SINGER){ return 'icon-mine' }else{ return 'icon-music' } }, getDisplayName(item){ if(item.type === TYPE_SINGER){ return item.singername; }else{ return `${item.name} - ${item.singer}` } }, _genResult(data){ let ret = []; if(data.zhida && data.zhida.singerid){ ret.push({...data.zhida,...{type:TYPE_SINGER}}) } if(data.song){ ret = ret.concat(this._normalizeSongs(data.song.list)); } return ret; }, _normalizeSongs(list){ let ret = []; list.forEach((musicData) => { if(musicData.songid && musicData.albumid){ ret.push(createSong(musicData)) } }) return ret; } }, watch:{ query(){ this.search(); } }, components:{ Scroll, Loading } }