
建立搜索框組件頁面,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
}
}
