站長資訊網
        最全最豐富的資訊網站

        那些年,微信小程序仿網易云音樂有關實時搜索功能

        那些年,微信小程序仿網易云音樂有關實時搜索功能

        相關學習推薦:微信小程序教程

        前言

        前段時間我的小伙伴已經將網易云音樂小程序的音樂播放功能詳細的介紹出來了,作為前端小白學習了一段時間,最近也比較忙,沒有及時將實時搜索這塊內容及時寫出來跟大家分享(其實代碼和功能之前就寫的差不多了),那么今天就給大家講一講個人在里面的一些細節和優化吧。

        搜索功能很常見,很多地方都能用到,希望能夠給大家分享到有用的東西,同時,有不足的地方,也希望各位大佬指出,給出一些修改的意見,小白在此感謝了!

        實時搜索功能里面我們也需要用到API接口,從input框輸入值到搜索建議,再到搜索結果,最后到跳轉歌曲播放,不再只是接那么簡單,傳值很關鍵,同時不同功能下不同容器框的隱藏與顯示,還有一些搜索當中涉及的細節內容和優化。讓我們一起來看看吧!

        界面預覽

        那些年,微信小程序仿網易云音樂有關實時搜索功能

        界面分析

        頭部搜索欄中:左邊返回箭頭,中間輸入框,右邊歌手排行榜頁面跳轉;至于清除按鈕呢我們隱藏了起來,只有在輸入輸入值之后才會出現。

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

        接下來是熱搜榜,這里沒有太多講究,就是發起接口請求數據,把數據埋進去顯示出來就行了。

        搜索建議會在輸入結束后才會出現,并且是很立體的一塊覆蓋在整個頁面上,用box-shadow: 1px 1px 5px #888888達到立體效果,z-index起到覆蓋的效果。

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

        幾個容器的頭部展示

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

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

        接口封裝

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

        api.js

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

        實時搜索功能:

        1.數據源分析

        一個搜索功能我們設計到的數據會有很多,可以細列一下:輸入的值inputValue,在輸入時獲取;熱搜榜數據hots,熱搜接口獲取;搜索關鍵詞searchKey,本身就是輸入框的值,用來傳遞給搜索建議作為搜索關鍵詞;searchSuggest,搜索建議接口拿到搜索關鍵詞后返回的的數據(搜索建議);搜索結果searchResult,當點擊搜索建議中的某一條,該值將填入搜索框,此時搜索關鍵詞searchKey將變為該值又傳遞給搜索結果接口,并返回數據放入searchResult;最后是搜索歷史history,每當進行一次搜索,將原本輸入框的值放到history數據源中。關于其他數據源涉及到組件隱藏與展示,即每一塊的容器框在何種情況下隱藏,何種情況下顯示。

        數據源展示

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

        2.獲取熱搜榜

        這里我們直接在頁面的初始數據中調用接口,直接獲取到數據使用

        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 })       }     })   },復制代碼

        3.獲取input文本

        前面已將講過,搜索建議和結果的接口并沒有直接的獲取方式,需要我們進行傳值,所以首先我們需要獲取到輸入框的值

        input框內容分析

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

        小程序中關于input輸入框的相關屬性大家可以去詳細了解一下;placeholder為輸入框為空時占位符,即還沒輸入前輸入框顯示的內容,placeholder-style可以去設置placeholder的樣式;value是輸入框的初始內容,即自己在輸入框輸入的內容,我們在這里直接將輸入的內容value直接作為了data數據源中inputValue的內容;bindinput是在鍵盤輸入時觸發,即我們一進行打字,就能觸發我們的自定義事件getSearchKey,并且會返還相應數據;bindblur在輸入框失去焦點時觸發,進行搜索功能時,需要在搜索框輸值,此時焦點一直在輸入框,當點擊輸入框以外的地方即輸入框失去焦點,同時觸發routeSearchResPage事件,還會返回相應的數據,在下面功能中會講到;bindconfirm在點擊完成按鈕時觸發,這里綁定一個searchOver,用來隱藏組件(容器塊),再次觸發搜索功能,在下面的功能中也會講到。

        獲取input文本

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

        bindinput本身是會返回數據,在代碼運行時,可以打印出來先看看; e.detail.value即為輸入框的值,將它賦值給searchKey; 查看打印數據e:

        那些年,微信小程序仿網易云音樂有關實時搜索功能

        解析:

        疑惑的小伙伴可以將代碼運行,打印出以上設計的幾個數據進行分析

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

        ②當輸入框沒值時,清除按鈕x是不會顯示的,只有當輸入框有值是才會出現清除按鈕x

        ③當輸入框沒有值時,隱藏搜索建議欄,其實本身我們最開始進入這個頁面時,輸入框是沒值的,搜索建議欄也是不展示的,為沒進行輸入就沒有數據;但是當我們輸入內容后,出現搜索建議,此時我們點擊清除按鈕,輸入框的內容沒了,但是搜索建議還停留在之前的狀態,所以這里我們優化一下,讓showSongResultfalse,即一清空輸入框內容,隱藏掉搜索建議欄。另外我們為什么要return呢?這里還有一個bug,當清除輸入框內容后,再輸入發現已經不再具備搜索功能了,所以需要return回到初始的狀態,就能重新進行輸入并且搜索。同時當輸入框為空時進行搜索功能還會報錯,這也是一個bug,所以有了return即使空值搜索也會立馬回到初始狀態,解決了空值搜索報錯的bug

        4.搜索框其他功能

        • 清空輸入框內容

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

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

        • 取消搜索返回上頁

          back: function () {     wx: wx.navigateBack({  // 關閉當前頁面,返回上一頁面或多級頁面       delta: 0   // 返回的頁面數,如果 delta 大于現有頁面數,則返回到首頁      });   }復制代碼

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

        • 跳轉歌手排行榜

          singerPage: function () {     wx.navigateTo({  // 保留當前頁面,跳轉到應用內的某個頁面。但是不能跳到 tabbar 頁面       url: `../singer/singer` // 要跳轉去的界面     })   },復制代碼

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

        5.搜索建議

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

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

        6.搜索結果

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

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

          那些年,微信小程序仿網易云音樂有關實時搜索功能
        • 返回搜索結果
          searchResult: function () {     // console.log(this.data.searchKey)  // 打印此時的搜索關鍵詞     $api.getSearchResult({ keywords: this.data.searchKey, type: 1, limit: 100, offset: 2 }).then(res => {       // 請求成功       if (res.statusCode === 200) {         // console.log(res)  // 打印返回數據         this.setData({           searchResult: res.data.result.songs, // 將搜索出的歌曲名稱給到搜索結果           showSearchResult: false, // 顯示搜索結果欄           showView: false,  // 隱藏搜索歷史欄和熱搜榜單欄         });       }     })     .catch(ree => {       //請求失敗     })   },復制代碼

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

        • 搜索完成后的優化
            searchOver: function () { // 搜索結果完成后(再次點擊輸入框)    this.setData({      showSongResult: false  // 搜索建議這塊容器消失    })    this.searchResult();  // 執行搜索結果  },復制代碼

          解析:前面我們講到過, searchOver是綁定在input框中的bindconfirm事件,即點擊完成按鈕時觸發。當我們搜索完成之后,界面上還有搜索欄以及搜索結果的顯示,此時我們再次點擊輸入框,可以進行清除文本,同時我們還需要增加一個功能,即在此種情況下,我們還可以進行再次輸入并且返回搜索建議以及點擊搜索建議中的歌曲時再次執行搜索結果功能。

        7.搜索歷史

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

          解析:之前講過routeSearchResPage事件時放在input框中的,輸入框失去焦點時觸發,即不在輸入框內進行輸入,點擊輸入框以外的內容時觸發。當輸入完成時會出現搜索建議,此時焦點還在輸入框,當我們點擊搜索建議中的某一天時,輸入框即失去焦點,此時該事件觸發。失去焦點函數是在搜索建議事件后發生,此時的搜索關鍵詞為搜索建議的搜索關鍵詞,前面也講到過,這個搜索關鍵詞就是我們在輸入框輸入的文本內容,所以將此時的搜索關鍵詞賦值給搜索歷史history注:關于搜索歷史,我們這里增加了一個判斷,即當搜索關鍵詞不為空時,才會拿到搜索關鍵詞給到搜索歷史里面,否則,每一次不輸入值也去點擊輸入框以外,會將一個空值傳給搜索歷史,導致搜索歷史中會有空值得顯示,這也是一個`bug得解決。同時還進一步將代碼進行優化,用到filter達到歷史去重得效果,即判斷新拿到得搜索關鍵詞是否與已有得搜索歷史中的搜索關鍵詞相同,同則過濾掉先前的那個,并使用到unshift向數組開頭增加這個作為新的歷史記錄。

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

          解析:雖然上一步將拿到的搜索記錄存入到了搜索歷史,但是還不能顯示出來,讓數據源拿到數據,這里要做一個歷史緩存的操作。onShow為監聽頁面顯示,每次在搜素建議功能后進行點擊歌名出現搜索結果欄時觸發,此時將上一步拿到的historygetStorageSync進行本地緩存,使得在刷新或者跳轉時,不會講搜索歷史丟失,一直保存下來。

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

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

        8.歌曲跳轉播放播放

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

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

          那些年,微信小程序仿網易云音樂有關實時搜索功能

        9.search功能源碼分享

        wxml

        <nav-bar></nav-bar> <view class="wrapper">     <!-- 上部整個搜索框 -->     <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>             <!-- 點擊×可以清空正在輸入 -->             <view class="clean-bar">                 <image class="{{showClean ? 'header_view_hide' : 'clean-pic'}}" src="../../image/search_delete.png" bindtap="clearInput" />             </view>         </view>         <!-- 跳轉歌手分類界面 -->         <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>     <!-- 搜索結果 -->     <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>復制代碼

        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;   }復制代碼

        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: [], //搜索歷史存放數組     searchSuggest: [], //搜索建議     showView: true,//組件的顯示與隱藏     showSongResult: true,     searchResult: [],//搜索結果     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         })       }     })   },   // 點x將輸入框的內容清空   clearInput: function (e) {     // console.log(e)     this.setData({       inputValue: '',       showSongResult: false,       showClean: true // 隱藏清除按鈕     })   },   //實現直接返回返回上一頁的功能,退出搜索界面   back: function () {     wx: wx.navigateBack({       delta: 0     });   },   // 跳轉到歌手排行界面   singerPage: function () {     // console.log('a')     wx.navigateTo({       url: `../singer/singer`     })   },   //獲取input文本并且實時搜索   getSearchKey: function (e) {     if(e.detail.cursor === 0){       this.setData({         showSongResult: false       })       return     }     // console.log(e.detail) //打印出輸入框的值     if (e.detail.cursor != this.data.cursor) { //實時獲取輸入框的值       this.setData({         showSongResult: true,         searchKey: e.detail.value       })       this.searchSuggest();     }     if (e.detail.value) { // 當input框有值時,才顯示清除按鈕'x'       this.setData({         showClean: false  // 出現清除按鈕       })     }   },   // 搜索建議   searchSuggest() {     $api. getSearchSuggest({ keywords: this.data.searchKey, type: 'mobile' }).then(res => {       //請求成功        // console.log(res);       if(res.statusCode === 200){         this.setData({           searchSuggest: res.data.result.allMatch          })        }     })     .catch(err => {       //請求失敗       console.log('錯誤')     })   },   // 搜索結果   searchResult: function () {     // console.log(this.data.searchKey)     $api.getSearchResult({ keywords: this.data.searchKey, type: 1, limit: 100, offset: 2 }).then(res => {       // 請求成功       if (res.statusCode === 200) {         // console.log(res)         this.setData({           searchResult: res.data.result.songs,           showSearchResult: false,           showView: false,         });       }     })     .catch(ree => {       //請求失敗     })   },   handlePlayAudio: function (e) { //event 對象,自帶,點擊事件后觸發,event有type,target,timeStamp,currentTarget屬性     // console.log(e)     const musicId = e.currentTarget.dataset.id; //獲取到event里面的歌曲id賦值給musicId     wx.navigateTo({                                 //獲取到musicId帶著完整url后跳轉到play頁面       url: `../play/play?musicId=${musicId}`     })   },   // input失去焦點函數   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) {  // 當搜索框有值的情況下才把搜索值存放到歷史中,避免將空值存入歷史記錄       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對象data的history數組 重置緩存為[](空)   clearHistory: function () {     const that = this;     wx.showModal({       content: '確認清空全部歷史記錄',       cancelColor: '#DE655C',       confirmColor: '#DE655C',       success(res) {         if (res.confirm) {           that.setData({             history: []           })           wx.setStorageSync("history", []) //把空數組給history,即清空歷史記錄         } else if (res.cancel) {         }       }     })   },     // 搜索結果完成后(再次點擊輸入框)   searchOver: function () {     this.searchSuggest();  // 執行搜索結果     this.searchResult()   },   // 點擊熱門搜索值或搜索歷史,填入搜索框   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,//點擊=把值給searchKey,讓他去搜索       inputValue: e.currentTarget.dataset.value,//在輸入框顯示內容       showSongResult: false, //給false值,隱藏搜索建議頁面       showClean: false // 顯示 清除按鈕     })     this.searchResult(); //執行搜索功能   },   /**    * 生命周期函數--監聽頁面顯示    */   //每次顯示變動就去獲取緩存,給history,并for出來。   onShow: function () {     // console.log('a')     this.setData({       history: wx.getStorageSync("history") || []     })   }, })復制代碼

        api.js

        const app = getApp(); // method(HTTP 請求方法),網易云API提供get和post兩種請求方式 const GET = 'GET'; const POST = 'POST'; // 定義全局常量baseUrl用來存儲前綴 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) {         //請求成功         //判斷狀態碼---errCode狀態根據后端定義來判斷         if (res.data.code == 200) {  //請求成功           resolve(res);         } else {           //其他異常           reject('運行時錯誤,請稍后再試');         }       },       fail(err) {         //請求失敗         reject(err)       }     })   }) } const API = {   getSearchSuggest: (data) => request(GET, `/search/suggest`, data),  // 搜索建議接口   getSearchResult: (data) => request(GET, `/search`, data),  // 搜索結果接口 }; module.exports = {   API: API }復制代碼

        總結

        其實一點一點的捋清楚會發現也不是很難操作,首先思路要清晰,知道每一個功能是什么作用,同時在調試是時候去發現一些bug,再去對代碼進行優化。關于搜索這個功能用處廣泛,希望本次的分享能給大家帶來一點用處。

        相關學習推薦:微信公眾號開發教程,javascript視頻教程

        贊(0)
        分享到: 更多 (0)
        網站地圖   滬ICP備18035694號-2    滬公網安備31011702889846號
        主站蜘蛛池模板: 亚洲国产欧美日韩精品一区二区三区| 色婷婷噜噜久久国产精品12p| 精品一区二区无码AV| 精品爆乳一区二区三区无码av| 国产精品自在线拍国产电影| 99国产精品久久| 日韩精品专区AV无码| 日本免费精品一区二区三区| 91精品国产福利在线观看麻豆| 99国产精品永久免费视频| 色国产精品一区在线观看| 亚洲精品专区| 无码欧精品亚洲日韩一区夜夜嗨| 精品国产青草久久久久福利| 国产成人精品久久综合 | 国产午夜精品一区二区三区漫画| 久久久国产精品| 国产精品极品| 一区二区三区四区精品视频| 999成人精品视频在线| 国产一精品一AV一免费| 自拍偷在线精品自拍偷无码专区 | 久久亚洲国产成人精品无码区| 国产精品igao视频| 四虎最新永久在线精品免费| 精品久久久久久国产91| 国产成人精品免费视| 99re8这里有精品热视频免费| 精品一区二区三区免费毛片爱| 亚洲一级Av无码毛片久久精品 | 嫩草伊人久久精品少妇AV| 四虎永久在线精品免费一区二区| 精品偷自拍另类在线观看丰满白嫩大屁股ass | 国产精品自在线拍国产| 精品久久久久香蕉网| 国产三级久久久精品麻豆三级| 久久99久久99精品免视看动漫| 久久精品人人做人人爽97| 精品国产sm捆绑最大网免费站| 精品国精品无码自拍自在线| 国产美女精品一区二区三区|