溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

微信小程序仿網(wǎng)易云音樂(lè)有關(guān)實(shí)時(shí)搜索功能的案例

發(fā)布時(shí)間:2021-03-11 12:36:31 來(lái)源:億速云 閱讀:240 作者:小新 欄目:移動(dòng)開(kāi)發(fā)

小編給大家分享一下微信小程序仿網(wǎng)易云音樂(lè)有關(guān)實(shí)時(shí)搜索功能的案例,希望大家閱讀完這篇文章之后都有所收獲,下面讓我們一起去探討吧!


界面預(yù)覽

微信小程序仿網(wǎng)易云音樂(lè)有關(guān)實(shí)時(shí)搜索功能的案例

界面分析

頭部搜索欄中:左邊返回箭頭,中間輸入框,右邊歌手排行榜頁(yè)面跳轉(zhuǎn);至于清除按鈕呢我們隱藏了起來(lái),只有在輸入輸入值之后才會(huì)出現(xiàn)。

往下時(shí)歷史記錄,像每個(gè)搜搜的記錄值這里都是一小塊一小塊等隔距離分布,搜索值有多長(zhǎng),這小塊就有多長(zhǎng),這里用到的是display: flex;flex-wrap: wrap;,對(duì)這個(gè)界面樣式感興趣的小伙伴可以待會(huì)看看全部代碼。

接下來(lái)是熱搜榜,這里沒(méi)有太多講究,就是發(fā)起接口請(qǐng)求數(shù)據(jù),把數(shù)據(jù)埋進(jìn)去顯示出來(lái)就行了。

搜索建議會(huì)在輸入結(jié)束后才會(huì)出現(xiàn),并且是很立體的一塊覆蓋在整個(gè)頁(yè)面上,用box-shadow: 1px 1px 5px #888888達(dá)到立體效果,z-index起到覆蓋的效果。

搜索結(jié)果會(huì)在點(diǎn)擊搜索建議中的某一條或者點(diǎn)擊搜索歷史或者熱搜才出現(xiàn),同時(shí)界面上其他所有的容器快都會(huì)隱藏起來(lái),這里其實(shí)就是一個(gè)容器框的隱藏與出現(xiàn)的小細(xì)節(jié)了,待會(huì)在功能中我們會(huì)詳細(xì)講到。這里我們先講一下組件(容器)如何進(jìn)行隱藏與顯示,以免下面的功能中看到這幾項(xiàng)內(nèi)容蒙圈

幾個(gè)容器的頭部展示

<!-- 點(diǎn)擊×可以清空正在輸入 -->
<image class="{{showClean ? 'header_view_hide' : 'clean-pic'}}" src="../../image/search_delete.png" bindtap="clearInput" />復(fù)制代碼
<!-- 搜索建議 -->
<view class="{{showSongResult ? 'search_suggest' : 'header_view_hide'}}">復(fù)制代碼
<!-- 搜索結(jié)果 -->
<view class="{{showSearchResult ? 'header_view_hide' : 'search_result_songs'}}">復(fù)制代碼
<!-- 搜索歷史 -->
<view class="{{showView?'option':'header_view_hide'}}">復(fù)制代碼
<!-- 熱搜榜 -->
<view class="{{showView?'option':'header_view_hide'}}">復(fù)制代碼

解析:這里只放了這幾塊容器的頭部的內(nèi)容,在data數(shù)據(jù)源中分別放了showClean,showSongResult,showSearchResult,showView, 為true 則這幾塊容器默認(rèn)為:(冒號(hào))前面的樣式,為false則默認(rèn)為:(冒號(hào))后面的樣式;header_view_hide樣式設(shè)置為display: none;,即隱藏不顯示;所以當(dāng)在某一個(gè)方法中可以去改變showClean,showSongResult,showSearchResult,showViewtrue還是false可以讓這幾塊容器分別為顯示或是隱藏。

接口封裝

接口封裝在上一篇我的小伙伴已經(jīng)講的十分清晰了,我們這里不再多去講解了,同樣現(xiàn)在用到的功能也不只是光調(diào)接口請(qǐng)求數(shù)據(jù)那么簡(jiǎn)單了,我們需要傳值給接口,讓接口收到值后再給我們返回相應(yīng)的數(shù)據(jù);在搜索界面我們用到的是搜索建議以及搜索結(jié)果的接口。熱搜榜我們暫時(shí)只使用最基礎(chǔ)的wx.request直接獲取數(shù)據(jù)

api.js

const API = {
    getSearchSuggest: (data) => request(GET, `/search/suggest`, data),  // 搜索建議接口
    getSearchResult: (data) => request(GET, `/search`, data),  // 搜索結(jié)果接口
}復(fù)制代碼

實(shí)時(shí)搜索功能:

1.數(shù)據(jù)源分析

一個(gè)搜索功能我們?cè)O(shè)計(jì)到的數(shù)據(jù)會(huì)有很多,可以細(xì)列一下:輸入的值inputValue,在輸入時(shí)獲??;熱搜榜數(shù)據(jù)hots,熱搜接口獲??;搜索關(guān)鍵詞searchKey,本身就是輸入框的值,用來(lái)傳遞給搜索建議作為搜索關(guān)鍵詞;searchSuggest,搜索建議接口拿到搜索關(guān)鍵詞后返回的的數(shù)據(jù)(搜索建議);搜索結(jié)果searchResult,當(dāng)點(diǎn)擊搜索建議中的某一條,該值將填入搜索框,此時(shí)搜索關(guān)鍵詞searchKey將變?yōu)樵撝涤謧鬟f給搜索結(jié)果接口,并返回?cái)?shù)據(jù)放入searchResult;最后是搜索歷史history,每當(dāng)進(jìn)行一次搜索,將原本輸入框的值放到history數(shù)據(jù)源中。關(guān)于其他數(shù)據(jù)源涉及到組件隱藏與展示,即每一塊的容器框在何種情況下隱藏,何種情況下顯示。

數(shù)據(jù)源展示

data: {
    inputValue: null,//輸入框輸入的值
    history: [], //搜索歷史存放數(shù)組
    searchSuggest: [], //搜索建議
    showView: true,//組件的顯示與隱藏
    showSongResult: true,
    searchResult: [],//搜索結(jié)果
    searchKey: [],
    showSearchResult: true,
    showClean: true,
    hots: [] //熱門搜索
 }復(fù)制代碼

2.獲取熱搜榜

這里我們直接在頁(yè)面的初始數(shù)據(jù)中調(diào)用接口,直接獲取到數(shù)據(jù)使用

onLoad: function (options) {
    wx.request({
      url: 'http://neteasecloudmusicapi.zhaoboy.com/search/hot/detail',
      header: { "Content-Type": "application/json" },
      success: (res) => {  // console.log(res)
        this.setData({
          hots: res.data.result.hots })
      }
    })
  },復(fù)制代碼

3.獲取input文本

前面已將講過(guò),搜索建議和結(jié)果的接口并沒(méi)有直接的獲取方式,需要我們進(jìn)行傳值,所以首先我們需要獲取到輸入框的值

input框內(nèi)容分析

<input focus='true' type="text" class="weui-search-bar__input" placeholder="大家都在搜 " placeholder-style="color:#eaeaea" value='{{inputValue}}' bindinput="getSearchKey" bindblur="routeSearchResPage" bindconfirm="searchOver" />復(fù)制代碼

小程序中關(guān)于input輸入框的相關(guān)屬性大家可以去詳細(xì)了解一下;placeholder為輸入框?yàn)榭諘r(shí)占位符,即還沒(méi)輸入前輸入框顯示的內(nèi)容,placeholder-style可以去設(shè)置placeholder的樣式;value是輸入框的初始內(nèi)容,即自己在輸入框輸入的內(nèi)容,我們?cè)谶@里直接將輸入的內(nèi)容value直接作為了data數(shù)據(jù)源中inputValue的內(nèi)容;bindinput是在鍵盤輸入時(shí)觸發(fā),即我們一進(jìn)行打字,就能觸發(fā)我們的自定義事件getSearchKey,并且會(huì)返還相應(yīng)數(shù)據(jù);bindblur在輸入框失去焦點(diǎn)時(shí)觸發(fā),進(jìn)行搜索功能時(shí),需要在搜索框輸值,此時(shí)焦點(diǎn)一直在輸入框,當(dāng)點(diǎn)擊輸入框以外的地方即輸入框失去焦點(diǎn),同時(shí)觸發(fā)routeSearchResPage事件,還會(huì)返回相應(yīng)的數(shù)據(jù),在下面功能中會(huì)講到;bindconfirm在點(diǎn)擊完成按鈕時(shí)觸發(fā),這里綁定一個(gè)searchOver,用來(lái)隱藏組件(容器塊),再次觸發(fā)搜索功能,在下面的功能中也會(huì)講到。

獲取input文本

getSearchKey: function (e) {
    // console.log(e.detail) //打印出輸入框的值
    if (e.detail.cursor != this.data.cursor) { //實(shí)時(shí)獲取輸入框的值
      this.setData({
        showSongResult: true,
        searchKey: e.detail.value })
      this.searchSuggest();
    }
    if (e.detail.value) { // 當(dāng)input框有值時(shí),才顯示清除按鈕'x'
      this.setData({
        showClean: false  // 出現(xiàn)清除按鈕 })
    }
    if(e.detail.cursor === 0){
      this.setData({  // 當(dāng)輸入框沒(méi)有值時(shí),即沒(méi)有輸入時(shí),隱藏搜索建議界面,返回到最開(kāi)始的狀態(tài)
        showSongResult: false })
      return
    }
  }復(fù)制代碼

bindinput本身是會(huì)返回?cái)?shù)據(jù),在代碼運(yùn)行時(shí),可以打印出來(lái)先看看; e.detail.value即為輸入框的值,將它賦值給searchKey; 查看打印數(shù)據(jù)e:

微信小程序仿網(wǎng)易云音樂(lè)有關(guān)實(shí)時(shí)搜索功能的案例

解析:

疑惑的小伙伴可以將代碼運(yùn)行,打印出以上設(shè)計(jì)的幾個(gè)數(shù)據(jù)進(jìn)行分析

①當(dāng)此時(shí)輸入框的值和bindinput返回的輸入框的值時(shí)一樣的,就將輸入框的值賦給搜索關(guān)鍵詞searchKey,此時(shí)顯示搜索建議欄(showSongResult寫在wxml當(dāng)中,用來(lái)控制該容器是否展示,可以看到最后面發(fā)的整個(gè)界面的wxml中的詳情);同時(shí)searchSuggest事件(方法)生效。

②當(dāng)輸入框沒(méi)值時(shí),清除按鈕x是不會(huì)顯示的,只有當(dāng)輸入框有值是才會(huì)出現(xiàn)清除按鈕x

③當(dāng)輸入框沒(méi)有值時(shí),隱藏搜索建議欄,其實(shí)本身我們最開(kāi)始進(jìn)入這個(gè)頁(yè)面時(shí),輸入框是沒(méi)值的,搜索建議欄也是不展示的,為沒(méi)進(jìn)行輸入就沒(méi)有數(shù)據(jù);但是當(dāng)我們輸入內(nèi)容后,出現(xiàn)搜索建議,此時(shí)我們點(diǎn)擊清除按鈕,輸入框的內(nèi)容沒(méi)了,但是搜索建議還停留在之前的狀態(tài),所以這里我們優(yōu)化一下,讓showSongResultfalse,即一清空輸入框內(nèi)容,隱藏掉搜索建議欄。另外我們?yōu)槭裁匆?code>return呢?這里還有一個(gè)bug,當(dāng)清除輸入框內(nèi)容后,再輸入發(fā)現(xiàn)已經(jīng)不再具備搜索功能了,所以需要return回到初始的狀態(tài),就能重新進(jìn)行輸入并且搜索。同時(shí)當(dāng)輸入框?yàn)榭諘r(shí)進(jìn)行搜索功能還會(huì)報(bào)錯(cuò),這也是一個(gè)bug,所以有了return即使空值搜索也會(huì)立馬回到初始狀態(tài),解決了空值搜索報(bào)錯(cuò)的bug。

4.搜索框其他功能

  • 清空輸入框內(nèi)容

     clearInput: function (e) {
        // console.log(e)  
        this.setData({
          inputValue: '',  // 將輸入框的值為空
          showSongResult: false,  // 隱藏搜索建議欄
          showClean: true // 隱藏清除按鈕 (不加此項(xiàng)會(huì)出現(xiàn)清除輸入框內(nèi)容后清除按鈕不消失的bug)
        })
      },復(fù)制代碼

    點(diǎn)擊清除按鈕,就讓inputValue值為空,即輸入框的內(nèi)容為空,達(dá)到清除文本的效果;在獲取輸入框文本那里我們也提到了清除按鈕,也提到輸入框文本清空時(shí),之前的搜索建議欄還會(huì)留下,所以這里我們讓showSongResultfalse,使得搜索建議欄隱藏。清除文本的同時(shí)再隱藏掉清除按鈕。

  • 取消搜索返回上頁(yè)

    back: function () {
        wx: wx.navigateBack({  // 關(guān)閉當(dāng)前頁(yè)面,返回上一頁(yè)面或多級(jí)頁(yè)面
          delta: 0   // 返回的頁(yè)面數(shù),如果 delta 大于現(xiàn)有頁(yè)面數(shù),則返回到首頁(yè)
         });
      }復(fù)制代碼

    這里用到的小程序自帶的返回頁(yè)面的功能,當(dāng)給delta值為0即回到上一個(gè)頁(yè)面。(可去文檔查看詳情)

  • 跳轉(zhuǎn)歌手排行榜

    singerPage: function () {
        wx.navigateTo({  // 保留當(dāng)前頁(yè)面,跳轉(zhuǎn)到應(yīng)用內(nèi)的某個(gè)頁(yè)面。但是不能跳到 tabbar 頁(yè)面
          url: `../singer/singer` // 要跳轉(zhuǎn)去的界面
        })
      },復(fù)制代碼

    在微信官方文檔可以查看到navigateTo的功能及其屬性,這里不多講。

5.搜索建議

 searchSuggest() {
    $api. getSearchSuggest({ keywords: this.data.searchKey, type: 'mobile' }).then(res => {
      //請(qǐng)求成功 
      // console.log(res);  // 打印出返回?cái)?shù)據(jù)進(jìn)行查看
      if(res.statusCode === 200){
        this.setData({
          searchSuggest: res.data.result.allMatch  // 將返回?cái)?shù)據(jù)里的歌名傳給搜索建議
        })
       }
    })
    .catch(err => {  // 請(qǐng)求失敗
      console.log('錯(cuò)誤')   })
  }復(fù)制代碼

解析:開(kāi)始我們將接口進(jìn)行了封裝,在上一篇講播放的文章中我的小伙伴已經(jīng)把接口跟封裝講的很仔細(xì)了,這里我們就不在講這個(gè)了,就分析我們的接口。searchKey作為搜索關(guān)鍵詞需要傳遞給接口,在前面的getSearchKey方法中,我們已經(jīng)講輸入框的內(nèi)容傳給了searchKey作為它的值;所以此時(shí)我們拿到有值的searchKey傳遞給接口,讓接口返回相關(guān)數(shù)據(jù),返回的數(shù)據(jù)中的res.data.result.allMatch就是從搜索關(guān)鍵詞返回的搜索建議里的所有歌名,在將這些數(shù)據(jù)放到searchSuggest數(shù)據(jù)源中,這樣在wxml埋好的空就能拿到數(shù)據(jù),將搜索建議欄顯示出。

6.搜索結(jié)果

  • 搜索建議內(nèi)的歌曲點(diǎn)擊事件

    // 看看 wxml中的點(diǎn)擊事件展示
    // <view wx:for="{{searchSuggest}}" wx:key="index" class='search_result' data-value='{{item.keyword}} ' bindtap='fill_value'>
    // js如下:
    fill_value: function (e) {   // 點(diǎn)擊搜索建議,熱門搜索值或搜索歷史,填入搜索框
        // console.log(e.currentTarget.dataset.value)  // 打印`e`中的數(shù)據(jù)->點(diǎn)擊的值
        this.setData({
          searchKey: e.currentTarget.dataset.value, // 點(diǎn)擊時(shí)把值給searchKey進(jìn)行搜索
          inputValue: e.currentTarget.dataset.value, // 在輸入框顯示內(nèi)容
          showSongResult: false, // 給false值,隱藏搜索建議頁(yè)面
          showClean: false // 顯示清除按鈕 (不加此項(xiàng),會(huì)出現(xiàn)點(diǎn)擊后輸入框有值但不顯示清除按鈕的bug)
        })
        this.searchResult();  // 執(zhí)行搜索功能
      },復(fù)制代碼

    解析:首先點(diǎn)擊事件可以攜帶額外信息,如 id, dataset, touches;返回參數(shù)eventevent本身會(huì)有一個(gè)currentTarget屬性;這里解釋一下data-value='{{item.keyword}}=>data就是dataset;item.keyword是搜索建議完成之后返回的數(shù)據(jù)賦值給searchSuggest里面的某個(gè)數(shù)據(jù);當(dāng)一點(diǎn)擊搜索建議里面的某一個(gè)歌名時(shí),此歌名即為此時(shí)的item.keyword,并將該值存入點(diǎn)擊事件的參數(shù)event內(nèi)的dataset。大家也可操作一波打印出來(lái)看看結(jié)果,currentTarget.dataset.value就是我們點(diǎn)擊的那個(gè)歌曲名字。所以一點(diǎn)擊搜索建議中的某個(gè)歌名或者搜索歷史以及熱搜榜單中的某個(gè)歌名時(shí),點(diǎn)擊事件生效,返回這樣該歌曲名稱,并將該值給到此時(shí)的searchKeyinputValue,此時(shí)輸入框的值會(huì)變成該值,搜索結(jié)果的關(guān)鍵詞的值也會(huì)變成該值;同時(shí)this.searchResult()可讓此時(shí)執(zhí)行搜索結(jié)果功能。showSongResult: false這里還將搜索建議欄給隱藏了。增加showClean: false是為了解決點(diǎn)擊后輸入框有值但不顯示清除按鈕的bug。 查看打印數(shù)據(jù)e:

    微信小程序仿網(wǎng)易云音樂(lè)有關(guān)實(shí)時(shí)搜索功能的案例
  • 返回搜索結(jié)果

    searchResult: function () {
        // console.log(this.data.searchKey)  // 打印此時(shí)的搜索關(guān)鍵詞
        $api.getSearchResult({ keywords: this.data.searchKey, type: 1, limit: 100, offset: 2 }).then(res => {
          // 請(qǐng)求成功
          if (res.statusCode === 200) {
            // console.log(res)  // 打印返回?cái)?shù)據(jù)
            this.setData({
              searchResult: res.data.result.songs, // 將搜索出的歌曲名稱給到搜索結(jié)果
              showSearchResult: false, // 顯示搜索結(jié)果欄
              showView: false,  // 隱藏搜索歷史欄和熱搜榜單欄
            });
          }
        })
        .catch(ree => {
          //請(qǐng)求失敗
        })
      },復(fù)制代碼

    解析:上面的歌曲名稱點(diǎn)擊同時(shí)觸發(fā)了搜索結(jié)果的功能,將點(diǎn)擊后的新的keywords傳遞給了搜索結(jié)果的接口,接口請(qǐng)求后返回給我們數(shù)據(jù),數(shù)據(jù)中的res.data.result.songs為搜索到的歌曲,此時(shí)將它賦值給到searchResult,這樣搜索結(jié)果欄中會(huì)拿到數(shù)據(jù),并且showSearchResult: false讓搜索結(jié)果欄顯示出來(lái);這里還做了搜索歷史欄和熱搜欄的隱藏功能注:搜索結(jié)果和搜索建議都需要將搜索關(guān)鍵詞傳遞給接口,不清楚的小伙伴可以去查看接口文檔研究一下:https://binaryify.github.io/NeteaseCloudMusicApi/#/

  • 搜索完成后的優(yōu)化

      searchOver: function () { // 搜索結(jié)果完成后(再次點(diǎn)擊輸入框)
       this.setData({
         showSongResult: false  // 搜索建議這塊容器消失
       })
       this.searchResult();  // 執(zhí)行搜索結(jié)果
     },復(fù)制代碼

    解析:前面我們講到過(guò), searchOver是綁定在input框中的bindconfirm事件,即點(diǎn)擊完成按鈕時(shí)觸發(fā)。當(dāng)我們搜索完成之后,界面上還有搜索欄以及搜索結(jié)果的顯示,此時(shí)我們?cè)俅吸c(diǎn)擊輸入框,可以進(jìn)行清除文本,同時(shí)我們還需要增加一個(gè)功能,即在此種情況下,我們還可以進(jìn)行再次輸入并且返回搜索建議以及點(diǎn)擊搜索建議中的歌曲時(shí)再次執(zhí)行搜索結(jié)果功能。

7.搜索歷史

  • input失去焦點(diǎn)

    routeSearchResPage: function (e) {  
        // console.log(this.data.searchKey)  // 打印此時(shí)的搜索關(guān)鍵詞
        // console.log(this.data.searchKey.length)  
        if (this.data.searchKey.length > 0) {  // 當(dāng)搜索框有值的情況下才把搜索值存放到歷史中,避免將空值存入歷史記錄
          let history = wx.getStorageSync("history") || [];  // 從本地緩存中同步獲取指定 key 對(duì)應(yīng)的內(nèi)容,key指定為history
          // console.log(history);
          history = history.filter(item => item !== this.data.searchKey)  // 歷史去重
          history.unshift(this.data.searchKey)  // 排序傳入
          wx.setStorageSync("history", history);
        }
      }復(fù)制代碼

    解析:之前講過(guò)routeSearchResPage事件時(shí)放在input框中的,輸入框失去焦點(diǎn)時(shí)觸發(fā),即不在輸入框內(nèi)進(jìn)行輸入,點(diǎn)擊輸入框以外的內(nèi)容時(shí)觸發(fā)。當(dāng)輸入完成時(shí)會(huì)出現(xiàn)搜索建議,此時(shí)焦點(diǎn)還在輸入框,當(dāng)我們點(diǎn)擊搜索建議中的某一天時(shí),輸入框即失去焦點(diǎn),此時(shí)該事件觸發(fā)。失去焦點(diǎn)函數(shù)是在搜索建議事件后發(fā)生,此時(shí)的搜索關(guān)鍵詞為搜索建議的搜索關(guān)鍵詞,前面也講到過(guò),這個(gè)搜索關(guān)鍵詞就是我們?cè)谳斎肟蜉斎氲奈谋緝?nèi)容,所以將此時(shí)的搜索關(guān)鍵詞賦值給搜索歷史history。注:關(guān)于搜索歷史,我們這里增加了一個(gè)判斷,即當(dāng)搜索關(guān)鍵詞不為空時(shí),才會(huì)拿到搜索關(guān)鍵詞給到搜索歷史里面,否則,每一次不輸入值也去點(diǎn)擊輸入框以外,會(huì)將一個(gè)空值傳給搜索歷史,導(dǎo)致搜索歷史中會(huì)有空值得顯示,這也是一個(gè)`bug得解決。同時(shí)還進(jìn)一步將代碼進(jìn)行優(yōu)化,用到filter達(dá)到歷史去重得效果,即判斷新拿到得搜索關(guān)鍵詞是否與已有得搜索歷史中的搜索關(guān)鍵詞相同,同則過(guò)濾掉先前的那個(gè),并使用到unshift向數(shù)組開(kāi)頭增加這個(gè)作為新的歷史記錄。

  • 歷史緩存

    onShow: function () {  //每次顯示變動(dòng)就去獲取緩存,給history,并for出來(lái)。
      // console.log('a')
      this.setData({
        history: wx.getStorageSync("history") || []
      })
    }復(fù)制代碼

    解析:雖然上一步將拿到的搜索記錄存入到了搜索歷史,但是還不能顯示出來(lái),讓數(shù)據(jù)源拿到數(shù)據(jù),這里要做一個(gè)歷史緩存的操作。onShow為監(jiān)聽(tīng)頁(yè)面顯示,每次在搜素建議功能后進(jìn)行點(diǎn)擊歌名出現(xiàn)搜索結(jié)果欄時(shí)觸發(fā),此時(shí)將上一步拿到的historygetStorageSync進(jìn)行本地緩存,使得在刷新或者跳轉(zhuǎn)時(shí),不會(huì)講搜索歷史丟失,一直保存下來(lái)。

  • 刪除歷史

    clearHistory: function () {  // 清空page對(duì)象data的history數(shù)組 重置緩存為[](空)
        const that = this;
        wx.showModal({
          content: '確認(rèn)清空全部歷史記錄',
          cancelColor: '#DE655C',
          confirmColor: '#DE655C',
          success(res) {
            if (res.confirm) { // 點(diǎn)擊確認(rèn)
              that.setData({
                history: []
              })
              wx.setStorageSync("history", []) //把空數(shù)組給history,即清空歷史記錄
            } else if (res.cancel) {
            }
          }
        })
      }復(fù)制代碼

    解析:showModal() 方法用于顯示對(duì)話窗,當(dāng)點(diǎn)擊刪除按鈕時(shí)觸發(fā),顯示出確認(rèn)清空全部歷史記錄的窗口,并有兩個(gè)點(diǎn)擊按鈕:確認(rèn)取消;當(dāng)點(diǎn)擊確認(rèn)時(shí),將history數(shù)組中的內(nèi)容重置為空,即達(dá)到清空搜索歷史中的數(shù)據(jù)的功能;同時(shí)也需要將此時(shí)沒(méi)有數(shù)據(jù)的的搜索歷史進(jìn)行緩存。點(diǎn)擊取消,提示窗消失,界面不會(huì)發(fā)生任何變化。

8.歌曲跳轉(zhuǎn)播放播放

  • 傳值跳轉(zhuǎn)播放界面

    // 先來(lái)看看handlePlayAudio綁定的地方
    // <view wx:for="{{searchResult}}" wx:key="index" class='search_result_song_item' data-id="{{item.id}}" bindtap='handlePlayAudio'>
    // 以下為js:
    handlePlayAudio: function (e) { //event 對(duì)象,自帶,點(diǎn)擊事件后觸發(fā),event有type,target,timeStamp,currentTarget屬性
      // console.log(e)   // 打印出返回參數(shù)內(nèi)容
      const musicId = e.currentTarget.dataset.id; //獲取到event里面的歌曲id賦值給musicId
      wx.navigateTo({                       //獲取到musicId帶著完整url后跳轉(zhuǎn)到play頁(yè)面
        url: `../play/play?musicId=${musicId}`  // 跳轉(zhuǎn)到已經(jīng)傳值完成的歌曲播放界面
      })
    }復(fù)制代碼

    解析:handlePlayAudio綁定在每天搜索結(jié)果上,即點(diǎn)擊搜索建議后完成搜索結(jié)果功能顯示出搜索結(jié)果欄,點(diǎn)擊每一天搜索結(jié)果都可以觸發(fā)handlePlayAudio。前面也講到過(guò)bindtap是帶有參數(shù)返回,攜帶額外信息dataset,event本身會(huì)有一個(gè)currentTarget屬性,data-id="{{item.id}}"的作用跟上面的搜索建議內(nèi)的歌曲點(diǎn)擊事件是同樣的效果,item.id為執(zhí)行搜索結(jié)果時(shí)接口返回給searchResult的數(shù)據(jù),也就是搜索結(jié)果中每首歌曲各自對(duì)應(yīng)的id。當(dāng)點(diǎn)擊搜索結(jié)果內(nèi)的某一首歌,即將這首歌的id傳給event中的dataset,數(shù)據(jù)名為dataset里的id。此時(shí)我們定義一個(gè)musicId,將event里面的歌曲id賦值給musicId,用 wx.navigateTo跳轉(zhuǎn)到播放界面,同時(shí)將musicId作為播放請(qǐng)求接口需要的傳入數(shù)據(jù)。 查看打印數(shù)據(jù)e:

    微信小程序仿網(wǎng)易云音樂(lè)有關(guān)實(shí)時(shí)搜索功能的案例

9.search功能源碼分享

wxml

<nav-bar></nav-bar>
<view class="wrapper">
    <!-- 上部整個(gè)搜索框 -->
    <view class="weui-search-bar">
        <!-- 返回箭頭按鈕 -->
        <view class="weui-search-bar__cancel-btn" bindtap="back">
            <image class="return-pic" src="../../image/search_return.png" bindtap="cancel" />
        </view>
        <!-- 搜索欄 -->
        <view class="weui-search-bar__form">
            <view class="weui-search-bar__box">
                <input focus='true' type="text" class="weui-search-bar__input" placeholder="大家都在搜 " placeholder-style="color:#eaeaea" value='{{inputValue}}' bindinput="getSearchKey" bindblur="routeSearchResPage" bindconfirm="searchOver" />
            </view>
            <!-- 點(diǎn)擊×可以清空正在輸入 -->
            <view class="clean-bar">
                <image class="{{showClean ? 'header_view_hide' : 'clean-pic'}}" src="../../image/search_delete.png" bindtap="clearInput" />
            </view>
        </view>
        <!-- 跳轉(zhuǎn)歌手分類界面 -->
        <view class="songer">
            <image class="songer-pic" src="../../image/search_songner.png" bindtap="singerPage" />
        </view>
    </view>
    <!-- 搜索建議 -->
    <view class="{{showSongResult ? 'search_suggest' : 'header_view_hide'}}">
        <view wx:for="{{searchSuggest}}" wx:key="index" class='search_result' data-value='{{item.keyword}} ' bindtap='fill_value'>
            <image class="search-pic" src="../../image/search_search.png"></image>
            <view class="search_suggest_name">{{item.keyword}}</view>
        </view>
    </view>
    <!-- 搜索結(jié)果 -->
    <view class="{{showSearchResult ? 'header_view_hide' : 'search_result_songs'}}">
        <view class="search-title">
            <text class="songTitle">單曲</text>
            <view class="openBox">
                <image class="openTap" src="../../image/search_openTap.png" />
                <text class="openDes">播放全部</text>
            </view>
        </view>
        <view wx:for="{{searchResult}}" wx:key="index" class='search_result_song_item' data-id="{{item.id}}" bindtap='handlePlayAudio'>
            <view class='search_result_song_song_name'>{{item.name}}</view>
            <view class='search_result_song_song_art-album'>
                {{item.artists[0].name}} - {{item.album.name}}
            </view>
            <image class="broadcast" src="../../image/search_nav-open.png" />
            <image class="navigation" src="../../image/mine_lan.png" />
        </view>
    </view>
    <!-- 搜索歷史 -->
    <view class="{{showView?'option':'header_view_hide'}}">
        <view class="history">
            <view class="history-wrapper">
                <text class="history-name">歷史記錄</text>
                <image bindtap="clearHistory" class="history-delete" src="../../image/search_del.png" />
            </view>
            <view class="allhistory">
                <view class="allhistorybox" wx:for="{{history}}" wx:key="index" data-value='{{item}}' data-index="{{index}}" bindtap="fill_value">
                    <text class="historyname">{{item}}</text>
                </view>
            </view>
        </view>
    </view>
    <!-- 熱搜榜 -->
    <view class="{{showView?'option':'header_view_hide'}}">
        <view class="ranking">
            <text class="ranking-name">熱搜榜</text>
        </view>
        <view class="rankingList">
            <view class="rankingList-box" wx:for="{{hots}}" wx:key="index">
                <view wx:if="{{index <= 2}}">
                    <text class="rankingList-num" style="color:red">{{index+1}}</text>
                    <view class="song">
                        <text class="rankigList-songname" style="color:black;font-weight:600" data-value="{{item.first}}" bindtap='fill_value'>
                            {{item.first}}
                        </text>
                        <block wx:for="{{detail}}" wx:key="index">
                            <text class="rankigList-hotsong" style="color:red">{{item.hot}}</text>
                        </block>
                    </view>
                </view>
                <view wx:if="{{index > 2}}">
                    <text class="rankingList-num">{{index+1}}</text>
                    <view class="othersong">
                        <text class="rankigList-songname" data-value="{{item.first}}" bindtap='fill_value'>
                            {{item.first}}
                        </text>
                    </view>
                </view>
            </view>
        </view>
    </view>
</view>復(fù)制代碼

wxss

  /* pages/search/search.wxss */
.weui-search-bar{
    position:relative;
    /* padding:8px; */
    display:flex;
    box-sizing:border-box;
    /* background-color:#EDEDED; */
    -webkit-text-size-adjust:100%;
    align-items:center
}
.weui-icon-search{
    margin-right:8px;font-size:14px;vertical-align:top;margin-top:.64em;
    height:1em;line-height:1em
}
.weui-icon-search_in-box{
    position:absolute;left:12px;top:50%;margin-top:-8px
}
.weui-search-bar__text{
    display:inline-block;font-size:14px;vertical-align:top
}
.weui-search-bar__form{
    position:relative;
    /* flex:auto;
    border-radius:4px;
    background:#FFFFFF */
    border-bottom: 1px solid #000;
    margin-left: 30rpx;
    width: 400rpx;
    padding-right: 80rpx;
}
.weui-search-bar__box{
    position:relative;
    padding-right: 80rpx;
    box-sizing:border-box;
    z-index:1;
}
.weui-search-bar__input{
    height:32px;line-height:32px;font-size:14px;caret-color:#07C160
}
.weui-icon-clear{
    position:absolute;top:0;right:0;bottom:0;padding:0 12px;font-size:0
}
.weui-icon-clear:after{
    content:"";height:100%;vertical-align:middle;display:inline-block;width:0;overflow:hidden
}
.weui-search-bar__label{
    position:absolute;top:0;right:0;bottom:0;left:0;z-index:2;border-radius:4px;
    text-align:center;color:rgba(0,0,0,0.5);background:#FFFFFF;line-height:32px
}
.weui-search-bar__cancel-btn{
    margin-left:8px;line-height:32px;color:#576B95;white-space:nowrap
}
.clean-bar {
    /* width: 20rpx;
    height: 20rpx; */
}
.clean-pic {
    width: 20rpx;
    height: 20rpx;
    float: right;
    position: absolute;
     margin-top: -30rpx; 
     margin-left: 450rpx;
}
.return-pic {
    width: 60rpx;
    height: 60rpx;
    margin-left: 20rpx;
}
.songer-pic{
    width: 60rpx;
    height: 60rpx;
    margin-left: 40rpx;
}
.wrapper {
    width: 100%;
    height: 100%;
    position: relative;
    z-index: 1;
}
.poster {
    width: 670rpx;
    height: 100rpx;
    margin-top: 40rpx;
    margin-left: 40rpx;
}
.postername {
    font-size: 15rpx;
    position: absolute;
    margin-top: 10rpx;
    margin-left: 10rpx;
}
.poster-outside {
    border-radius: 10rpx;
    background-color: slategrey;
}
.poster-pic0 {
    width: 80rpx;
    height: 80rpx;
    margin-top: 10rpx;
}
.test-title {
    position: absolute;
    font-size: 30rpx;
    line-height: 100rpx;
    margin-left: 20rpx;
    color: red;
}
.test-age {
    position: absolute;
    font-size: 30rpx;
    line-height: 100rpx;
    margin-left: 80rpx;
}
.test-red {
    position: absolute;
    font-size: 30rpx;
    line-height: 100rpx;
    margin-left: 270rpx;
    color: red;
}
.test-black {
    position: absolute;
    font-size: 30rpx;
    line-height: 100rpx;
    margin-left: 400rpx;
}
.poster-pic1 {
    width: 80rpx;
    height: 80rpx;
    margin-left: 510rpx;
}
.history {
    margin: 50rpx 0 0 40rpx;
}
.history-name {
    font-size: 28rpx;
    font-weight: 550;
}
.history-delete {
    width: 50rpx;
    height: 50rpx;
    position: absolute;
    margin-left: 510rpx;
}
.allhistory {
    display: flex;
    flex-wrap: wrap;
}
.allhistorybox {
    margin: 30rpx 20rpx 0 0;
    background-color: dimgray;
    border-radius: 10rpx;
}
.historyname {
    font-size: 28rpx;
    margin: 20rpx 20rpx 20rpx 20rpx;
}
.ranking {
    margin-left: 40rpx;
    margin-top: 100rpx;
}
.ranking-name {
    font-size: 28rpx;
    color: black;
    font-weight: 550;
}
.rankingList {
    margin-left: 50rpx;
    margin-top: 30rpx;
}
.rankingList-box {
    width: 100%;
    height: 80rpx;
    margin: 0 0 30rpx 0;
}
.rankingList-num {
    line-height: 80rpx;
    align-content: center;
}
.song {
    margin: -100rpx 0 0 30rpx;
    display: flex;
    flex-wrap: wrap;
}
.othersong {
    margin-top: -100rpx;
    margin-left: 70rpx;
}
.rankigList-songname {
    font-size: 30rpx;
    margin-left: 40rpx;
}
.rankigList-hotsong {
    font-size: 25rpx;
    font-weight: 550;
    margin-top: 45rpx;
    margin-left: 20rpx;
}
.rankigList-hotnum {
    float: right;
    position: absolute;
    line-height: 80rpx;
    margin-left: 600rpx;
    font-size: 20rpx;
    color: darkgrey;
}
.rankingList-songdes {
    font-size: 22rpx;
    color: darkgrey;
    position: absolute;
    margin-left: 60rpx;
    margin-top: -30rpx;
}
.search_suggest{
    width:570rpx;
    margin-left: 40rpx;
    position: absolute;
    z-index: 2;
    background: #fff;
    box-shadow: 1px 1px 5px #888888;
    margin-top: 20rpx;
}
.header_view_hide{
    display: none;
  }
.search-pic {
      width: 50rpx;
      height: 50rpx;
     margin-top: 25rpx;
     margin-left: 20rpx;
}
.search-title {
    color: #000;
    margin-left: 15rpx;
    margin-bottom: 30rpx;
}
.songTitle {
    font-size: 30rpx;
    font-weight: 700;
}
.openBox {
    float: right;
    border-radius: 30rpx;
    margin-right: 30rpx;
    border-radius: 30rpx;
    border-bottom: 1px solid #eaeaea;
}
.openTap {
    width: 30rpx;
    height: 30rpx;
    position: absolute;
    margin: 6rpx 10rpx 0rpx 20rpx;
}
.openDes {
    font-size: 25rpx;
    color: rgba(0,0,0,0.5);
    margin-right: 20rpx;
    margin-left: 58rpx;
}
.broadcast {
    width: 20px;
    height: 20px;
    display: inline-block;
    overflow: hidden;
    float: right;
    margin-top: -70rpx;
    margin-left: -120rpx;
    margin-right: 80rpx;
}
.navigation {
    width: 20px;
    height: 20px;
    display: inline-block;
    overflow: hidden;
    float: right;
    margin-top: -70rpx;
    margin-right: 20rpx;
}
  .search_result{
    /* display: block;
    font-size: 14px;
    color: #000000;
    padding: 15rpx;
    margin: 15rpx; */
    /* border-bottom: 1px solid #eaeaea; */
    /* float: right; */
    /* margin-left: -450rpx; */
    width: 570rpx;    
    height: 100rpx;
    border-bottom: 1px solid #eaeaea;
  }
  .search_suggest_name {
    display: block;
    float: right;
    position: absolute;
    margin-left: 85rpx;
    margin-top: -46rpx;
    font-size: 14px;
    color: darkgrey;
    /* padding: 15rpx;
    margin: 15rpx; */
  }
  .search_result_songs{
    margin-top: 10rpx;
    width: 100%;
    height: 100%;
    margin-left: 15rpx;
  }
  .search_result_song_item{
     display: block;
     margin: 15rpx;
     border-bottom: 1px solid #EDEEF0;
  }
  .search_result_song_song_name{
    font-size: 15px;
    color: #000000;
    margin-bottom: 15rpx;
  }
  .search_result_song_song_art-album{
    font-size: 11px;
    color: #000000;
    font-weight:lighter;
    margin-bottom: 5rpx;
  }復(fù)制代碼

js

// pages/search/search.js
// const API = require('../../utils/req')
const $api = require('../../utils/api.js').API;
const app = getApp();
Page({
  data: {
    inputValue: null,//輸入框輸入的值
    history: [], //搜索歷史存放數(shù)組
    searchSuggest: [], //搜索建議
    showView: true,//組件的顯示與隱藏
    showSongResult: true,
    searchResult: [],//搜索結(jié)果
    searchKey: [],
    showSearchResult: true,
    showClean: true,
    hots: [], //熱門搜索
    detail: [
      {
        hot: 'HOT'
      }
    ],
  },
  onLoad: function (options) {
    wx.request({
      url: 'http://neteasecloudmusicapi.zhaoboy.com/search/hot/detail',
      data: {
      },
      header: {
        "Content-Type": "application/json"
      },
      success: (res) => {
        // console.log(res)
        this.setData({
          hots: res.data.result.hots
        })
      }
    })
  },
  // 點(diǎn)x將輸入框的內(nèi)容清空
  clearInput: function (e) {
    // console.log(e)
    this.setData({
      inputValue: '',
      showSongResult: false,
      showClean: true // 隱藏清除按鈕
    })
  },
  //實(shí)現(xiàn)直接返回返回上一頁(yè)的功能,退出搜索界面
  back: function () {
    wx: wx.navigateBack({
      delta: 0
    });
  },
  // 跳轉(zhuǎn)到歌手排行界面
  singerPage: function () {
    // console.log('a')
    wx.navigateTo({
      url: `../singer/singer`
    })
  },
  //獲取input文本并且實(shí)時(shí)搜索
  getSearchKey: function (e) {
    if(e.detail.cursor === 0){
      this.setData({
        showSongResult: false
      })
      return
    }
    // console.log(e.detail) //打印出輸入框的值
    if (e.detail.cursor != this.data.cursor) { //實(shí)時(shí)獲取輸入框的值
      this.setData({
        showSongResult: true,
        searchKey: e.detail.value
      })
      this.searchSuggest();
    }
    if (e.detail.value) { // 當(dāng)input框有值時(shí),才顯示清除按鈕'x'
      this.setData({
        showClean: false  // 出現(xiàn)清除按鈕
      })
    }
  },
  // 搜索建議
  searchSuggest() {
    $api. getSearchSuggest({ keywords: this.data.searchKey, type: 'mobile' }).then(res => {
      //請(qǐng)求成功 
      // console.log(res);
      if(res.statusCode === 200){
        this.setData({
          searchSuggest: res.data.result.allMatch 
        })
       }
    })
    .catch(err => {
      //請(qǐng)求失敗
      console.log('錯(cuò)誤')
    })
  },
  // 搜索結(jié)果
  searchResult: function () {
    // console.log(this.data.searchKey)
    $api.getSearchResult({ keywords: this.data.searchKey, type: 1, limit: 100, offset: 2 }).then(res => {
      // 請(qǐng)求成功
      if (res.statusCode === 200) {
        // console.log(res)
        this.setData({
          searchResult: res.data.result.songs,
          showSearchResult: false,
          showView: false,
        });
      }
    })
    .catch(ree => {
      //請(qǐng)求失敗
    })
  },
  handlePlayAudio: function (e) { //event 對(duì)象,自帶,點(diǎn)擊事件后觸發(fā),event有type,target,timeStamp,currentTarget屬性
    // console.log(e)
    const musicId = e.currentTarget.dataset.id; //獲取到event里面的歌曲id賦值給musicId
    wx.navigateTo({                                 //獲取到musicId帶著完整url后跳轉(zhuǎn)到play頁(yè)面
      url: `../play/play?musicId=${musicId}`
    })
  },
  // input失去焦點(diǎn)函數(shù)
  routeSearchResPage: function (e) {
    // console.log(e)
    // console.log(e.detail.value)
    // console.log(this.data.searchKey)
    // console.log(this.data.searchKey.length)  
    if (this.data.searchKey.length > 0) {  // 當(dāng)搜索框有值的情況下才把搜索值存放到歷史中,避免將空值存入歷史記錄
      let history = wx.getStorageSync("history") || [];
      // console.log(history);
      history = history.filter(item => item !== this.data.searchKey)  // 歷史去重
      history.unshift(this.data.searchKey)
      wx.setStorageSync("history", history);
    }  
  },
  // 清空page對(duì)象data的history數(shù)組 重置緩存為[](空)
  clearHistory: function () {
    const that = this;
    wx.showModal({
      content: '確認(rèn)清空全部歷史記錄',
      cancelColor: '#DE655C',
      confirmColor: '#DE655C',
      success(res) {
        if (res.confirm) {
          that.setData({
            history: []
          })
          wx.setStorageSync("history", []) //把空數(shù)組給history,即清空歷史記錄
        } else if (res.cancel) {
        }
      }
    })
  },
    // 搜索結(jié)果完成后(再次點(diǎn)擊輸入框)
  searchOver: function () {
    this.searchSuggest();  // 執(zhí)行搜索結(jié)果
    this.searchResult()
  },
  // 點(diǎn)擊熱門搜索值或搜索歷史,填入搜索框
  fill_value: function (e) {
    console.log(e)
    // console.log(this.data.history)
    // console.log(e.currentTarget.dataset.value)
    this.setData({
      searchKey: e.currentTarget.dataset.value,//點(diǎn)擊=把值給searchKey,讓他去搜索
      inputValue: e.currentTarget.dataset.value,//在輸入框顯示內(nèi)容
      showSongResult: false, //給false值,隱藏搜索建議頁(yè)面
      showClean: false // 顯示 清除按鈕
    })
    this.searchResult(); //執(zhí)行搜索功能
  },
  /**
   * 生命周期函數(shù)--監(jiān)聽(tīng)頁(yè)面顯示
   */
  //每次顯示變動(dòng)就去獲取緩存,給history,并for出來(lái)。
  onShow: function () {
    // console.log('a')
    this.setData({
      history: wx.getStorageSync("history") || []
    })
  },
})復(fù)制代碼

api.js

const app = getApp();
// method(HTTP 請(qǐng)求方法),網(wǎng)易云API提供get和post兩種請(qǐng)求方式
const GET = 'GET';
const POST = 'POST';
// 定義全局常量baseUrl用來(lái)存儲(chǔ)前綴
const baseURL = 'http://neteasecloudmusicapi.zhaoboy.com';
function request(method, url, data) {
  return new Promise(function (resolve, reject) {
    let header = {
      'content-type': 'application/json',
      'cookie': app.globalData.login_token
    };
    wx.request({
      url: baseURL + url,
      method: method,
      data: method === POST ? JSON.stringify(data) : data,
      header: header,
      success(res) {
        //請(qǐng)求成功
        //判斷狀態(tài)碼---errCode狀態(tài)根據(jù)后端定義來(lái)判斷
        if (res.data.code == 200) {  //請(qǐng)求成功
          resolve(res);
        } else {
          //其他異常
          reject('運(yùn)行時(shí)錯(cuò)誤,請(qǐng)稍后再試');
        }
      },
      fail(err) {
        //請(qǐng)求失敗
        reject(err)
      }
    })
  })
}
const API = {
  getSearchSuggest: (data) => request(GET, `/search/suggest`, data),  // 搜索建議接口
  getSearchResult: (data) => request(GET, `/search`, data),  // 搜索結(jié)果接口
};
module.exports = {
  API: API
}復(fù)制代碼

看完了這篇文章,相信你對(duì)“微信小程序仿網(wǎng)易云音樂(lè)有關(guān)實(shí)時(shí)搜索功能的案例”有了一定的了解,如果想了解更多相關(guān)知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道,感謝各位的閱讀!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI