創建搜索框組件頁面,searchBox,組件接受一個能夠自定義傳入的placeholder 屬性。input v-model 雙向綁定數據關聯到query 中, 在created中監聽 query 變量將改變的新值派發給外部父組件,在search.vue 組件中將其引入html
<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.vuevue
熱門搜索模塊,經過search.js 的getHotKey 函數異步獲取數據,並渲染,給每一個item 上綁定addQuery(item.k) 將其值賦值給input 的value 值,經過調用 this.$refs.searchBox.setQuery(query)api
<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'this
if(this.pullup){ this.scroll.on('scrollEnd',()=>{ if(this.scroll.y <= (this.scroll.maxScrollY + 50)){ //當滾動距離離底部50 像素的時候,派發事件,父級監聽此事件作再次請求數據接口 this.$emit('scrollToEnd') } }) }
監聽派發上拉加載事件 @scrollToEnd="searchMore" spa
在首次加載數據的時候設置 監測還有沒有數據的標誌位 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 } }