vue-music 關於Search(搜索頁面)--上拉加載

  

創建搜索框組件頁面,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
  }
}
相關文章
相關標籤/搜索