仿网易云音乐-搜索歌曲功能

思路:
1.搜索静态页面,展示热门搜索关键词:点击关键词,自动填入搜索框,并实时得到搜索结果。

 

 

 小细节:如果搜索框里有内容,则会出现“X”清除搜索内容,并回到默认页面。
2.手动输入搜索关键词,会给相似推荐的关键词,点击关键词进行搜索相关歌曲

 

API接口使用: https://binaryify.github.io/NeteaseCloudMusicApi/#/?id=neteasecloudmusicapi

视图层:搜索框和热门搜索词列表

 1 <view class="m-input">
 2         <i class="u-svg u-svg-srch">
 3           <image src="/images/s.svg"></image>
 4           </i>
 5         <input class="inputcover" id="input" 
 6         bindinput="bindKeyInput" bindconfirm="getsearchList"
 7         confirm-type="search" 
 8         placeholder="搜索歌曲、歌手、专辑"
 9         value="{{searchValue}}"
10         />
11         <i class="close u-svg u-svg-empty {{showClean ? 'header_view_hide' : 'clean-pic'}}" bindtap='clearInput'>
12           <image src="/images/close.svg"></image>
13           </i>
14       </view>
15 
16 <view class="m-default  {{showView?'option':'header_view_hide'}}">
17         <view class="m-hotlist">
18           <h3 class="title">热门搜索</h3>
19           <view class="list">
20           <view class="item f-bd f-bd-full" 
21           wx:for="{{hotLists}}" 
22           wx:key="index" 
23           data-value='{{item.first}}'
24           bindtap='fill_value'>
25             {{item.first}}
26           </view>    
27           </view>
28         </view>
29       </view>

A. 用到的方法:

bindinput="bindKeyInput": 输入搜索关键词,实时输出相关搜索词

bindconfirm="getsearchList":输入完成,enter得到歌曲搜索列表

bindtap='clearInput':点击“x”清空搜索框里的内容,并清除搜索结果回到默认状态

B. 所用到的状态控制显示与隐藏

 <i class="close u-svg u-svg-empty {{showClean ? 'header_view_hide' : 'clean-pic'}}" bindtap='clearInput'>

 <view class="m-default {{showView?'option':'header_view_hide'}}">

 showClean默认为ture,当前的class为'header_view_hide':display:none

 showView: true,显示当前模块 getSearchSuggest()方法设置showView为false,

showSongResult: true,显示当前模块,showView为false,热门搜索隐藏

<view class="m-recom {{showSongResult ? 'search_suggest' : 'header_view_hide'}}">
        <h3 class="title f-bd f-bd-btm f-thide">搜索“{{searchValue}}”</h3>
        <ul>
          <li class="recomitem" 
          wx:for="{{searchSuggest}}" 
          wx:key="index"
          data-value='{{item.keyword}}'
          bindtap='fill_value'
          >
          <i class="u-svg u-svg-search">
          <image src="/images/s.svg"></image>
          </i>
          <span class="f-bd f-bd-btm f-thide">{{item.keyword}}</span>
          </li>
        </ul>
      </view>
data-value='{{item.keyword}}':设定当前点击的值,并传值给fill_value方法
bindtap='fill_value':fill_value方法,获取当前的data-value的值e.currentTarget.dataset.value

 

 

最重要的就是获取搜索列表:用到的方法:getSearchList(),getSearchList()

      <view class="m-searchresult {{showSearchResult ? 'search_result_songs':'header_view_hide'}}">
        <view class="m-matchlist">
          <h3 class="title">最佳匹配</h3>
            <ul>
              <li class="matchitem artist">
                <navigator url="/artist?id={{searchResult[0].id}}">
                  <div class="linkcover f-bd f-bd-btm">
                    <figure class="piccover">
                      <image class="pic" src="{{searchResult[0].al.picUrl}}"></image>
                    </figure>
                    <article class="describe">
                      <h4 class="maindes f-thide">
                        歌手: <p class="hcover">{{searchResult[0].ar[0].name}}</p>
                      </h4>
                    </article>
                    <i class="u-svg u-svg-arr">
                      <image src="/images/arrow.svg"></image>
                    </i>
                  </div>
                </navigator>
              </li>
              <li class="matchitem album">
                <navigator url="/album?id={{searchAlbum.id}}">
                  <div class="linkcover f-bd f-bd-btm">
                    <figure class="piccover">
                      <image class="pic" src="{{searchAlbum.picUrl}}"></image>
                    </figure>
                    <article class="describe">
                      <h4 class="maindes f-thide">
                      专辑: 
                      <p class="hcover">
                        <span>
                          <span class="highlight">{{searchAlbum.name}}</span>
                        </span>
                        <span>
                          <span class="normal">
                            {{searchAlbum.alias}}
                          </span>
                        </span>
                      </p>
                      </h4>
                      <p class="hcover addtional f-thide"> {{searchAlbum.artist.name}}</p>
                    </article>
                    <i class="u-svg u-svg-arr">
                      <image src="/images/arrow.svg"></image>
                    </i>
                  </div>
                </navigator>
              </li>
            </ul>
        </view>
        <view class="m-songlist">
          <view class="m-sglst">
          <block wx:for="{{searchResult}}" wx:key="index">
            <navigator 
            class="m-sgitem" 
            url="../musicplay/musicplay?id={{item.id}}&title={{item.name}}">
                <view class="sgfr f-bd f-bd-btm">
                  <view class="sgchfl">
                    <view class="f-thide sgtl">
                        <p class="hcover">
                            <span>
                                <span class="highlight">{{item.name}}</span>
                            </span>
                            <span>
                            </span>
                        </p>
                    </view>
                    <view class="f-thide sginfo">
                        <i class="u-hmsprt sghot"></i>
                        <p class="hcover">{{item.ar[0].name}}</p> - <p class="hcover">{{item.al.name}}</p>
                    </view>
                </view>
                <view class="sgchfr">
                  <span class="u-hmsprt sgchply">
                  </span>
                </view>
              </view>
          </navigator> 
          </block>
          </view>
        </view>
      </view>
获取搜索音乐列表

完整的代码:
WXML:

<view class="page-section">
      <view class="m-input">
        <i class="u-svg u-svg-srch">
          <image src="/images/s.svg"></image>
          </i>
        <input class="inputcover" id="input" 
        bindinput="bindKeyInput" bindconfirm="fill_value"
        confirm-type="search" 
        placeholder="搜索歌曲、歌手、专辑"
        value="{{searchValue}}"
        />
        <i class="close u-svg u-svg-empty {{showClean ? 'header_view_hide' : 'clean-pic'}}" bindtap='clearInput'>
          <image src="/images/close.svg"></image>
          </i>
      </view>
      <view class="m-recom {{showSongResult ? 'search_suggest' : 'header_view_hide'}}">
        <h3 class="title f-bd f-bd-btm f-thide">搜索“{{searchValue}}”</h3>
        <ul>
          <li class="recomitem" 
          wx:for="{{searchSuggest}}" 
          wx:key="index"
          data-value='{{item.keyword}}'
          bindtap='fill_value'
          >
          <i class="u-svg u-svg-search">
          <image src="/images/s.svg"></image>
          </i>
          <span class="f-bd f-bd-btm f-thide">{{item.keyword}}</span>
          </li>
        </ul>
      </view>
      <view class="m-searchresult {{showSearchResult ? 'search_result_songs':'header_view_hide'}}">
        <view class="m-matchlist" >
          <h3 class="title">最佳匹配</h3>
            <ul>
              <li class="matchitem artist">
                <navigator url="/artist?id={{searchResult[0].id}}">
                  <div class="linkcover f-bd f-bd-btm">
                    <figure class="piccover">
                      <image class="pic" src="{{searchResult[0].al.picUrl}}"></image>
                    </figure>
                    <article class="describe">
                      <h4 class="maindes f-thide">
                        歌手: <p class="hcover">{{searchResult[0].ar[0].name}}</p>
                      </h4>
                    </article>
                    <i class="u-svg u-svg-arr">
                      <image src="/images/arrow.svg"></image>
                    </i>
                  </div>
                </navigator>
              </li>
              <li class="matchitem album">
                <navigator url="/album?id={{searchAlbum.id}}">
                  <div class="linkcover f-bd f-bd-btm">
                    <figure class="piccover">
                      <image class="pic" src="{{searchAlbum.picUrl}}"></image>
                    </figure>
                    <article class="describe">
                      <h4 class="maindes f-thide">
                      专辑: 
                      <p class="hcover">
                        <span>
                          <span class="highlight">{{searchAlbum.name}}</span>
                        </span>
                        <span>
                          <span class="normal">
                            {{searchAlbum.alias}}
                          </span>
                        </span>
                      </p>
                      </h4>
                      <p class="hcover addtional f-thide"> {{searchAlbum.artist.name}}</p>
                    </article>
                    <i class="u-svg u-svg-arr">
                      <image src="/images/arrow.svg"></image>
                    </i>
                  </div>
                </navigator>
              </li>
            </ul>
        </view>
        <view class="m-songlist">
          <view class="m-sglst">
          <block wx:for="{{searchResult}}" wx:key="index">
            <navigator 
            class="m-sgitem" 
            url="../musicplay/musicplay?id={{item.id}}&title={{item.name}}">
                <view class="sgfr f-bd f-bd-btm">
                  <view class="sgchfl">
                    <view class="f-thide sgtl">
                        <p class="hcover">
                            <span>
                                <span class="highlight">{{item.name}}</span>
                            </span>
                            <span>
                            </span>
                        </p>
                    </view>
                    <view class="f-thide sginfo">
                        <i class="u-hmsprt sghot"></i>
                        <p class="hcover">{{item.ar[0].name}}</p> - <p class="hcover">{{item.al.name}}</p>
                    </view>
                </view>
                <view class="sgchfr">
                  <span class="u-hmsprt sgchply">
                  </span>
                </view>
              </view>
          </navigator> 
          </block>
          </view>
        </view>
      </view>
      <view class="m-default  {{showView?'option':'header_view_hide'}}">
        <view class="m-hotlist">
          <h3 class="title">热门搜索</h3>
          <view class="list">
          <view class="item f-bd f-bd-full" 
          wx:for="{{hotLists}}" 
          wx:key="index" 
          data-value='{{item.first}}'
          bindtap='fill_value'>
            {{item.first}}
          </view>    
          </view>
        </view>
      </view>
</view>
search.wxml
// pages/search/search.js
import {get} from "../../utils/request.js"
Page({

  /**
   * 页面的初始数据
   */
  data: {
    searchValue: '',
    cursor:0,
    hotLists:null,
    searchSuggest:null,
    searchResult:[],
    showView: true,
    showSearchResult:true,
    showSongResult: false,
    showSearchResult: false,
    showClean: true,
    offset:1
  },
  gethotList: async function(){
    const url =`/search/hot`
    const res = await get(url)
    // console.log(res.data.result.hots)
    this.setData({
      hotLists: res.data.result.hots
    })
  },
  getSearchSuggest:async function(searchWords){
    const url =`/search/suggest?keywords=${searchWords}&type=mobile`
    const res = await get(url)
    // console.log(res.data)
    this.setData({
      searchSuggest: res.data.result.allMatch,
      showView: false,
      showSearchResult: false
    })
  },
  // splitData: function (list) {
  //   var res = [];
  //   for (var i = 0, len = songCount; i < len; i += 10) {
  //     list.push(res.data.list[i]);
  //   }
  //   return res;
  // },
  getsearchList: async function(searchWords){
    var that= this;
    const offset = this.data.offset;
    const url =`/cloudsearch?keywords=${searchWords}&limit=6&offset=${offset}`
    // const url_album =`/cloudsearch?keywords=${searchWords}&limit=1&type=10`
    const res = await get(url)
    // const res_album = await get(url_album)
    const arr1 = this.data.searchResult;
    const arr2= res.data.result.songs;
    const musicLists = arr1.concat(arr2);
    console.log(res.data.result)
    // console.log(res_album.data.result)
    this.setData({
      count:res.data.result.songCount,
      searchResult: musicLists,
      // searchAlbum:res_album.data.result.albums[0],
      showSearchResult: true,
      showView: false,
      showSongResult:false

    })
  },
  getsearchAlbum: async function(searchWords){
    const url_album =`/cloudsearch?keywords=${searchWords}&limit=1&type=10`
    const res_album = await get(url_album)
    // console.log(res_album.data.result)
    this.setData({
      searchAlbum:res_album.data.result.albums[0],
    })
  },
  bindKeyInput: function (e) {
    console.log(e.detail.value);
    var that = this;
    if(e.detail.cursor != this.data.cursor) {
      this.setData({
        showSongResult: true,
        // cursor: e.detail.cursor,
        searchValue: e.detail.value
      })
      // 假设现在需要检测到用户输入的值,用户 500 毫秒内没有继续输入就将该值打印出来
      that.throttle(that.getSearchSuggest, null, 1000, this.data.searchValue);
    } 
    if (e.detail.value) { // 当input框有值时,才显示清除按钮'x'
      this.setData({
        showClean: false  
      })
    }
    
    if(e.detail.cursor===0){
      this.setData({
        showSongResult: false,
        showClean: true
      })
      return
    }
  },
  // 节流
  throttle: function (fn, context, delay, text) {
    clearTimeout(fn.timeoutId);
    fn.timeoutId = setTimeout(function () {
      fn.call(context, text);
    }, delay);
  },
  //热门歌曲点击
  fill_value: function (e) {
    var that=this;
    console.log(e.currentTarget.dataset.value)
    this.setData({
      searchValue: e.currentTarget.dataset.value||e.detail.value,
      showSongResult: false,
      showClean: false
    })
    that.getsearchList(that.data.searchValue)
    that.getsearchAlbum(that.data.searchValue)
  },
  clearInput: function(e) {
    console.log(e)
    this.setData({
      searchValue: '',
      showSongResult: false,
      showSearchResult: false,
      showView:true,
      showClean: true // 隐藏清除按钮

    })
  },
  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {
    this.gethotList()
  },

  /**
   * 生命周期函数--监听页面初次渲染完成
   */
  onReady: function () {

  },

  /**
   * 生命周期函数--监听页面显示
   */
  onShow: function () {

  },

  /**
   * 生命周期函数--监听页面隐藏
   */
  onHide: function () {

  },

  /**
   * 生命周期函数--监听页面卸载
   */
  onUnload: function () {

  },

  /**
   * 页面相关事件处理函数--监听用户下拉动作
   */
  onPullDownRefresh: function () {
    wx.showNavigationBarLoading();
    wx.stopPullDownRefresh;
  },

  /**
   * 页面上拉触底事件的处理函数
   */
  onReachBottom: function () {
    var that=this;
    var total = Math.ceil(this.data.count/6);
    console.log(total);
    var offset = that.data.offset + 1; //获取当前页数并+1
    that.setData({
      offset: offset, //更新当前页数
    })
    this.getsearchList(this.data.searchValue)
  },

  /**
   * 用户点击右上角分享
   */
  onShareAppMessage: function () {

  }
})
search.js
.m-input {
  padding: 15px 10px;
}
.m-input .inputcover {
  position: relative;
  width: 100%;
  height: 30px;
  padding: 0 30px;
  box-sizing: border-box;
  background: #ebecec;
  border-radius: 30px;
  font-size: 14px;
}
.m-hotlist {
  padding: 15px 10px 0;
}
.m-hotlist .title {
  font-size: 12px;
  line-height: 12px;
  color: #666;
}
.m-hotlist .list {
  margin: 10px 0 7px;
  display: flex;
  flex-wrap: wrap;
}
.m-hotlist .item{
  min-height: unset;
  height: 32px;
  margin-right: 8px;
  margin-bottom: 8px;
  padding: 0 14px;
  font-size: 14px;
  line-height: 32px;
  color: #333;
  border:1px solid #d3d4da;
  border-radius: 32px;
  display: inline-flex;
  align-items: center;
}
.header_view_hide{
  display: none;
}
.m-recom .title {
  height: 50px;
  margin-left: 10px;
  padding-right: 10px;
  font-size: 15px;
  line-height: 50px;
  color: #507daf;
}
.m-recom .recomitem {
  display: -webkit-box;
  display: -webkit-flex;
  display: -ms-flexbox;
  display: flex;
  -webkit-box-align: center;
  -webkit-align-items: center;
  -ms-flex-align: center;
  align-items: center;
  height: 45px;
  padding-left: 10px;
}
.m-recom i {
  -webkit-box-flex: 0;
  -webkit-flex: 0 0 auto;
  -ms-flex: 0 0 auto;
  flex: 0 0 auto;
  margin-right: 7px;
}
.u-svg-search {
  width: 15px;
  height: 15px;
}
.m-input{
  position: relative;
}
.m-input .u-svg-srch {
  position: absolute;
  left: 15px;
  top: 20px;
  margin: 0 8px;
  vertical-align: middle;
  z-index: 10;
}
.m-input .close {
  position: absolute;
  right: 20px;
  top: 16px;
  line-height: 28px;
  text-align: center;
}
.u-svg-srch image{
  width: 13px;
  height: 13px;
}
.u-svg-empty image{
  width: 14px;
  height: 14px;
}
.u-svg-search image{
  width: 15px;
  height: 15px;
}
.m-matchlist .title {
  margin-left: 10px;
  font-size: 12px;
  line-height: 16px;
  color: #666;
}
.m-matchlist .linkcover {
  display: -webkit-box;
  display: -webkit-flex;
  display: -ms-flexbox;
  display: flex;
  -webkit-box-align: center;
  -webkit-align-items: center;
  -ms-flex-align: center;
  align-items: center;
  height: 66px;
  margin-left: 10px;
  padding: 8px 10px 8px 0;
  box-sizing: border-box;
}
.m-matchlist .piccover {
  position: relative;
  width: 50px;
  height: 50px;
  margin-right: 15px;
  line-height: 0;
}
.m-matchlist .pic {
  display: block;
  width: 100%;
  height: 100%;
}
.hcover .highlight {
  color: #507daf;
}
.m-matchlist .describe {
  -webkit-box-flex: 1;
  -webkit-flex: 1;
  -ms-flex: 1;
  flex: 1;
  display: inline-block;
  width: 1%;
}
.f-thide {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  word-break: normal;
}
.m-matchlist .describe .addtional {
  font-size: 12px;
  line-height: 15px;
  color: #999;
  width: 100%;
  display: flex;
}
.u-svg-arr image{
  width: 8px;
  height: 13px;
}
.m-sgitem .sginfo {
  font-size: 12px;
  color: #888;
}
.m-sgitem, .m-sgitem .sgfl {
  display: -webkit-box;
  display: -webkit-flex;
  display: -ms-flexbox;
  display: flex;
}
.m-sgitem .sgchfl, .m-sgitem .sgfr {
  -webkit-box-flex: 1;
  -webkit-flex: 1 1 auto;
  -ms-flex: 1 1 auto;
  flex: 1 1 auto;
}
.m-sgitem .sgfr {
  display: -webkit-box;
  display: -webkit-flex;
  display: -ms-flexbox;
  display: flex;
  position: relative;
}
.m-sgitem {
  padding-left: 10px;
}
.m-sgitem .sgchfl {
  padding: 6px 0;
  width: 0;
}
.m-sgitem .sgchfr {
  display: -webkit-box;
  display: -webkit-flex;
  display: -ms-flexbox;
  display: flex;
  -webkit-box-align: center;
  -webkit-align-items: center;
  -ms-flex-align: center;
  align-items: center;
  padding: 0 10px;
}
.m-sgitem .sghot {
  display: inline-block;
  width: 12px;
  height: 8px;
  margin-right: 4px;
}
.m-sgitem .sgchply {
  display: inline-block;
  width: 22px;
  height: 22px;
  background-position: -24px 0;
}
.u-hmsprt {
  background: url(//s3.music.126.net/mobile-new/img/index_icon_2x.png?5207a28…=) no-repeat;
  background-size: 166px 97px;
}
search.wxss

 

posted @ 2021-06-01 16:06  cheryshi  阅读(782)  评论(0编辑  收藏  举报