您好,登錄后才能下訂單哦!
小編給大家分享一下微信小程序仿網(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,showView
為true
還是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ù)制代碼
一個(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ù)制代碼
這里我們直接在頁(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ù)制代碼
前面已將講過(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
:
解析:
疑惑的小伙伴可以將代碼運(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)化一下,讓showSongResult
為false
,即一清空輸入框內(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
。
清空輸入框內(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ì)留下,所以這里我們讓showSongResult
為false
,使得搜索建議欄隱藏。清除文本的同時(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
的功能及其屬性,這里不多講。
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ù),將搜索建議欄顯示出。
搜索建議內(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ù)
event
,event
本身會(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í)的searchKey
和inputValue
,此時(shí)輸入框的值會(huì)變成該值,搜索結(jié)果的關(guān)鍵詞的值也會(huì)變成該值;同時(shí)this.searchResult()
可讓此時(shí)執(zhí)行搜索結(jié)果功能。showSongResult: false
這里還將搜索建議欄給隱藏了。增加showClean: false
是為了解決點(diǎn)擊后輸入框有值但不顯示清除按鈕的bug
。 查看打印數(shù)據(jù)e
:
返回搜索結(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é)果功能。
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í)將上一步拿到的history
用getStorageSync
進(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ā)生任何變化。
傳值跳轉(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
:
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è)資訊頻道,感謝各位的閱讀!
免責(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)容。