目次
序文
インターフェイスのプレビュー
インターフェイス分析
inputValue
hots
2.获取热搜榜
3.获取input文本
4.搜索框其他功能
5.搜索建议
6.搜索结果
7.搜索历史
8.歌曲跳转播放播放
9.search功能源码分享
总结
ホームページ WeChat アプレット ミニプログラム開発 当時、WeChat アプレットは NetEase Cloud Music のリアルタイム検索機能を模倣していました。

当時、WeChat アプレットは NetEase Cloud Music のリアルタイム検索機能を模倣していました。

Sep 14, 2020 pm 01:18 PM
WeChat アプレット 検索機能

当時、WeChat アプレットは NetEase Cloud Music のリアルタイム検索機能を模倣していました。

関連する学習の推奨事項: WeChat ミニ プログラム チュートリアル

序文

私のミニ プログラムについてago 私のパートナーが NetEase Cloud Music アプレットの音楽再生機能を詳しく紹介してくれました。フロントエンドの初心者として、私はしばらく勉強しています。最近とても忙しくて、リアルタイム検索のコンテンツを書き出していませんでした皆さんと共有するのに間に合うように (実際、コードと関数は以前にほぼ書かれています)、今日はその詳細と最適化について説明します。

検索機能は非常に一般的で、さまざまな場所で使用できます。役立つことを皆さんと共有できれば幸いです。同時に、不備な点があれば、指摘していただければ幸いです。変更についていくつかの提案をしてください。Xiaomi、ありがとうございます!

入力ボックスへの値の入力から検索候補、検索結果、そして最後に曲の再生にジャンプするまで、リアルタイム検索機能でも API インターフェイスを使用する必要があります。もはや、単に拾うだけではなく、値を渡すことが重要ですが、同時に、さまざまな機能の下でさまざまなコンテナ ボックスを非表示にしたり表示したり、検索に関連する詳細や最適化も行われます。見てみましょう!

インターフェイスのプレビュー

当時、WeChat アプレットは NetEase Cloud Music のリアルタイム検索機能を模倣していました。

インターフェイス分析

ヘッド検索バー: 左リターン矢印、中央入力ボックスを選択すると、右側の歌手ランキングページにジャンプします。クリアボタンは非表示にし、入力値を入力した後にのみ表示されます。

履歴レコードをたどる場合、ここでの各検索レコード値が等距離分布の小さな部分であるように、検索値はこの小さな部分の長さと同じ長さになります。ここではそれが使用されます。得られるものは display: flex;flex-wrap: Wrap;. このインターフェイス スタイルに興味のある友人は、後でコード全体を確認できます。

次はホット検索リストです。ここではあまり強調されていません。データを要求するインターフェースを開始し、データを埋め込んで表示するだけです。

入力が完了すると検索候補が表示され、非常に立体的でページ全体をカバーします。3 つを実現するには、box-shadow: 1px 1px 5px #888888 を使用します。 -次元効果.z-index にはカバー力の効果があります。

検索候補の 1 つをクリックするか、検索履歴またはホット検索をクリックすると、検索結果が表示されます。同時に、インターフェイス上の他のすべてのコンテナが非表示になります。これが実際の非表示です。コンテナボックスの外観については、細かい点なので機能の後半で詳しく説明します。ここではまず、次の関数でコンポーネント (コンテナー) の内容が表示されないように、コンポーネント (コンテナー) を非表示および表示する方法について説明します。これらのコンテナのヘッダーの内容のみがここに配置されます。

showClean、showSongResult、showSearchResult、showView
はそれぞれ

data

データ ソースに配置されます。これは

true これらのコンテナはデフォルトで :(コロン) の前のスタイルになり、false の場合、デフォルトは :(コロン) の後のスタイルになります; header_view_hide スタイルが設定されますdisplay: none;、つまり非表示で表示されないため、特定のメソッドで、showClean、showSongResult、showSearchResult、showViewtrue に変更できます。または、false を使用して、これらのコンテナをそれぞれ表示または非表示にすることができます。 インターフェイスのカプセル化インターフェイスのカプセル化については、私の友人が前の記事ですでにわかりやすく説明しています。ここではこれ以上説明しません。現在使用されているのと同じ関数も同様です。データをリクエストするようにインターフェイスを調整するだけと同じくらい簡単で、インターフェイスに値を渡し、値を受け取った後にインターフェイスが対応するデータを返すようにする必要があります。検索インターフェイスでは、検索候補にインターフェイスを使用し、の検索結果。ホット検索リストの場合、最も基本的な

wx.request
のみを使用してデータを直接取得します

api.js<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">&lt;!-- 点击×可以清空正在输入 --&gt; &lt;image class=&quot;{{showClean ? &amp;#39;header_view_hide&amp;#39; : &amp;#39;clean-pic&amp;#39;}}&quot; src=&quot;../../image/search_delete.png&quot; bindtap=&quot;clearInput&quot; /&gt;复制代码</pre><div class="contentsignin">ログイン後にコピー</div></div>

リアルタイム検索機能:

1. データ ソース分析

検索機能用に設計するデータは多数あります。詳細にリストできます: 入力値

inputValue

、入力中に取得、ホット検索 ランキング データ

hots

、ホット検索インターフェイスから取得、検索キーワード

searchKey、それ自体が入力ボックスの値であり、検索候補に渡すために使用されます。検索キーワード; searchSuggest、検索キーワードを取得した後に検索候補インターフェイスによって返されるデータ (検索候補); 検索結果 searchResult、検索候補の 1 つをクリックすると、値が検索ボックスに入力されて検索されます。キーワード 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=&#39;true&#39; type="text" class="weui-search-bar__input" placeholder="大家都在搜 " placeholder-style="color:#eaeaea" value=&#39;{{inputValue}}&#39; 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:

当時、WeChat アプレットは NetEase Cloud Music のリアルタイム検索機能を模倣していました。

解析:

疑惑的小伙伴可以将代码运行,打印出以上设计的几个数据进行分析

①当此时输入框的值和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=&#39;search_result&#39; data-value=&#39;{{item.keyword}} &#39; bindtap=&#39;fill_value&#39;>
    // 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:

    当時、WeChat アプレットは NetEase Cloud Music のリアルタイム検索機能を模倣していました。
  • 返回搜索结果
    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=&#39;search_result_song_item&#39; data-id="{{item.id}}" bindtap=&#39;handlePlayAudio&#39;>
    // 以下为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:

    当時、WeChat アプレットは NetEase Cloud Music のリアルタイム検索機能を模倣していました。

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=&#39;true&#39; type="text" class="weui-search-bar__input" placeholder="大家都在搜 " placeholder-style="color:#eaeaea" value=&#39;{{inputValue}}&#39; bindinput="getSearchKey" bindblur="routeSearchResPage" bindconfirm="searchOver" />
            </view>
            <!-- 点击×可以清空正在输入 -->
            <view class="clean-bar">
                <image class="{{showClean ? &#39;header_view_hide&#39; : &#39;clean-pic&#39;}}" 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 ? &#39;search_suggest&#39; : &#39;header_view_hide&#39;}}">
        <view wx:for="{{searchSuggest}}" wx:key="index" class=&#39;search_result&#39; data-value=&#39;{{item.keyword}} &#39; bindtap=&#39;fill_value&#39;>
            <image class="search-pic" src="../../image/search_search.png"></image>
            <view class="search_suggest_name">{{item.keyword}}</view>
        </view>
    </view>
    <!-- 搜索结果 -->
    <view class="{{showSearchResult ? &#39;header_view_hide&#39; : &#39;search_result_songs&#39;}}">
        <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=&#39;search_result_song_item&#39; data-id="{{item.id}}" bindtap=&#39;handlePlayAudio&#39;>
            <view class=&#39;search_result_song_song_name&#39;>{{item.name}}</view>
            <view class=&#39;search_result_song_song_art-album&#39;>
                {{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?&#39;option&#39;:&#39;header_view_hide&#39;}}">
        <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=&#39;{{item}}&#39; data-index="{{index}}" bindtap="fill_value">
                    <text class="historyname">{{item}}</text>
                </view>
            </view>
        </view>
    </view>
    <!-- 热搜榜 -->
    <view class="{{showView?&#39;option&#39;:&#39;header_view_hide&#39;}}">
        <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=&#39;fill_value&#39;>
                            {{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=&#39;fill_value&#39;>
                            {{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视频教程

以上が当時、WeChat アプレットは NetEase Cloud Music のリアルタイム検索機能を模倣していました。の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

Xianyu WeChat ミニプログラムが正式に開始 Xianyu WeChat ミニプログラムが正式に開始 Feb 10, 2024 pm 10:39 PM

Xianyu の公式 WeChat ミニ プログラムが静かに開始されました。ミニ プログラムでは、プライベート メッセージを投稿して購入者/販売者とコミュニケーションしたり、個人情報や注文を表示したり、商品を検索したりすることができます。プログラム、見てみましょう。 Xianyu WeChat アプレットの名前は何ですか? 回答: Xianyu、アイドル取引、中古品販売、評価、リサイクル。 1. ミニ プログラムでは、アイドル メッセージの投稿、プライベート メッセージを介した購入者/販売者とのコミュニケーション、個人情報と注文の表示、指定された商品の検索などができます。 2. ミニ プログラム ページには、ホームページ、近くに、アイドル投稿、メッセージ投稿、私の投稿 5つの機能; 3. 使用したい場合は、購入する前に WeChat 支払いを有効にする必要があります。

WeChatアプレットは画像アップロード機能を実装 WeChatアプレットは画像アップロード機能を実装 Nov 21, 2023 am 09:08 AM

WeChat アプレットが画像アップロード機能を実装 モバイル インターネットの発展に伴い、WeChat アプレットは人々の生活に欠かせないものになりました。 WeChat ミニ プログラムは、豊富なアプリケーション シナリオを提供するだけでなく、画像アップロード機能などの開発者定義の機能もサポートします。この記事では、WeChat アプレットに画像アップロード機能を実装する方法と具体的なコード例を紹介します。 1. 準備作業 コードを書き始める前に、WeChat 開発者ツールをダウンロードしてインストールし、WeChat 開発者として登録する必要があります。同時に、WeChat についても理解する必要があります。

WeChat アプレットにドロップダウン メニュー効果を実装する WeChat アプレットにドロップダウン メニュー効果を実装する Nov 21, 2023 pm 03:03 PM

WeChat ミニ プログラムでドロップダウン メニュー効果を実装するには、特定のコード サンプルが必要です。モバイル インターネットの普及に伴い、WeChat ミニ プログラムはインターネット開発の重要な部分となり、ますます多くの人が注目し始めています。 WeChat ミニ プログラムを使用します。 WeChat ミニ プログラムの開発は、従来の APP 開発よりも簡単かつ迅速ですが、特定の開発スキルを習得する必要もあります。 WeChat ミニ プログラムの開発では、ドロップダウン メニューが一般的な UI コンポーネントであり、より良いユーザー エクスペリエンスを実現します。この記事では、WeChat アプレットにドロップダウン メニュー効果を実装し、実用的な機能を提供する方法を詳しく紹介します。

WeChat ミニ プログラムに画像フィルター効果を実装する WeChat ミニ プログラムに画像フィルター効果を実装する Nov 21, 2023 pm 06:22 PM

WeChat ミニ プログラムでの画像フィルター効果の実装 ソーシャル メディア アプリケーションの人気に伴い、人々は写真にフィルター効果を適用して、写真の芸術的効果や魅力を高めることがますます好まれています。画像フィルター効果は WeChat ミニ プログラムにも実装でき、より興味深く創造的な写真編集機能をユーザーに提供します。この記事では、WeChat ミニ プログラムに画像フィルター効果を実装する方法を紹介し、具体的なコード例を示します。まず、WeChat アプレットのキャンバス コンポーネントを使用して画像を読み込み、編集する必要があります。 Canvasコンポーネントはページ上で使用できます

WeChat アプレットを使用してカルーセル切り替え効果を実現する WeChat アプレットを使用してカルーセル切り替え効果を実現する Nov 21, 2023 pm 05:59 PM

WeChat アプレットを使用してカルーセル スイッチング効果を実現する WeChat アプレットは、シンプルで効率的な開発と使用特性を備えた軽量のアプリケーションです。 WeChat ミニ プログラムでは、カルーセル スイッチング効果を実現することが一般的な要件です。この記事では、WeChat アプレットを使用してカルーセル切り替え効果を実現する方法と、具体的なコード例を紹介します。まず、カルーセル コンポーネントを WeChat アプレットのページ ファイルに追加します。たとえば、&lt;swiper&gt; タグを使用すると、カルーセルの切り替え効果を実現できます。このコンポーネントでは、 b を渡すことができます。

Xianyu WeChat アプレットの名前は何ですか? Xianyu WeChat アプレットの名前は何ですか? Feb 27, 2024 pm 01:11 PM

Xianyu の公式 WeChat ミニ プログラムが静かに開始され、アイドルアイテムを簡単に公開および交換できる便利なプラットフォームをユーザーに提供します。ミニ プログラムでは、プライベート メッセージを介して購入者または販売者とコミュニケーションしたり、個人情報や注文を表示したり、欲しい商品を検索したりできます。では、WeChat ミニ プログラムでは Xianyu とはいったい何と呼ばれているのでしょうか? このチュートリアル ガイドで詳しくご紹介しますので、知りたいユーザーは、この記事に従って読み続けてください。 Xianyu WeChat アプレットの名前は何ですか? 回答: Xianyu、アイドル取引、中古品販売、評価、リサイクル。 1. ミニ プログラムでは、アイドル メッセージの投稿、プライベート メッセージを介した購入者/販売者とのコミュニケーション、個人情報と注文の表示、指定された商品の検索などができます。 2. ミニ プログラム ページには、ホームページ、近くに、アイドル投稿、メッセージ投稿、マイ投稿の5つの機能、3.

PHPを使用してWeChatアプレットの中古取引機能を開発するにはどうすればよいですか? PHPを使用してWeChatアプレットの中古取引機能を開発するにはどうすればよいですか? Oct 27, 2023 pm 05:15 PM

PHPを使用してWeChatアプレットの中古取引機能を開発するにはどうすればよいですか?人気のモバイル アプリケーション開発プラットフォームとして、WeChat アプレットはますます多くの開発者によって使用されています。 WeChat ミニ プログラムでは、中古取引が一般的な機能要件です。この記事では、PHP を使用して WeChat アプレットの中古取引機能を開発する方法と、具体的なコード例を紹介します。 1. 準備作業 開発を開始する前に、以下の条件が満たされていることを確認する必要があります。ミニ プログラムの AppID の登録やミニ プログラムのバックグラウンドでの設定など、WeChat ミニ プログラムの開発環境がセットアップされていること。

WeChat アプレットに画像回転効果を実装する WeChat アプレットに画像回転効果を実装する Nov 21, 2023 am 08:26 AM

WeChat ミニ プログラムで画像回転効果を実装するには、特定のコード サンプルが必要です。WeChat ミニ プログラムは、ユーザーに豊富な機能と優れたユーザー エクスペリエンスを提供する軽量のアプリケーションです。ミニ プログラムでは、開発者はさまざまなコンポーネントと API を使用して、さまざまな効果を実現できます。その中で、画像回転効果は、ミニプログラムに興味と視覚効果を加えることができる一般的なアニメーション効果です。 WeChat ミニ プログラムで画像の回転効果を実現するには、ミニ プログラムが提供するアニメーション API を使用する必要があります。以下は、その方法を示す具体的なコード例です。

See all articles