1. 程式人生 > >Day12-微信小程式實戰-交友小程式-優化“附近的人”頁面與serach元件的佈局和樣式以及搜尋歷史記錄和本地快取*內附程式碼)

Day12-微信小程式實戰-交友小程式-優化“附近的人”頁面與serach元件的佈局和樣式以及搜尋歷史記錄和本地快取*內附程式碼)

回顧/:我們已經實現了顯示附近的人的功能了,可以多個人看到附近的人頁面了

但是還是要進行優化有幾個問題:1、我們使用者選擇了其他的自定義頭像之後,在首頁可以看到頭像的變化,但是在附近的人中頭像會變成報錯的樣式:如:

 

 

 也就是500了,也就是找不到這個圖片了,解決方法:看開發文件-》雲開發

https://developers.weixin.qq.com/miniprogram/dev/wxcloud/guide/storage/api.html

其中有一個 “換取臨時連結”的功能(通過這個方法可以臨時的拿到一個圖片的路徑了),然後這個路徑就可以對應到我們的iconpath中了,有直接看demo

wx.cloud.getTempFileURL({
  fileList: ['cloud://xxx.png'],
  success: res => {
    // fileList 是一個有如下結構的物件陣列
    // [{
    //    fileID: 'cloud://xxx.png', // 檔案 ID
    //    tempFileURL: '', // 臨時檔案網路連結
    //    maxAge: 120 * 60 * 1000, // 有效期
    // }]
    console.log(res.fileList)
  },
  fail: console.error
})

我們剛剛換了頭像的測試號,可以看到在資料庫中

 

 

 

 正常的試https這樣的,但是我們修改了之後,它的路徑變成了我們設定的預設的,cloud開始的了

所以我們就可以直接在near.js裡面用for來判斷每個欄位符不符合條件即可了,一旦找到了這個cloud開頭的路徑的話,也就是if裡面進行的東西

我們就要換取臨時的路徑即可了,如果else的話,我們還是和之前一樣的,直接push進去即可了

if裡面的話直接copy文件裡面的demo即可了

我們通過

console.log(res.fileList) 打印出來的東西試一個數組: 

 

 裡面的那個tempFileURL就是一個臨時的路徑了

 getNearUsers(){
    db.collection('users').where({
      location: _.geoNear({
        geometry: db.Geo.Point(this.data.longitude, this.data.latitude),
        minDistance: 0,
        maxDistance: 5000
        //這1000和5000的單位是米
      }),
      islocation : true
    }).field({
      longitude : true,
      latitude : true ,
      userPhoto : true
    }).get().then((res)=>{
      console.log(res.data);
      let data = res.data;
      let result = [];
      if(data.length){

        for(let i=0;i<data.length;i++){
          if(data[i].userPhoto.includes('cloud://')){
            wx.cloud.getTempFileURL({
              fileList: [data[i].userPhoto ],
              success: res => {
                // console.log(res.fileList[0].tempFileURL)
                result.push({
                  // 然後就是把我們獲取到的臨時路徑直接賦值給iconpath即可了
                  iconPath: res.fileList[0].tempFileURL,
                  id: data[i]._id,
                  latitude: data[i].latitude,
                  longitude: data[i].longitude,
                  width: 30,
                  height: 30
                });
                
              }
            })
          }
          else{
            result.push({
              iconPath: data[i].userPhoto,
              id: data[i]._id,
              latitude: data[i].latitude,
              longitude: data[i].longitude,
              width: 30,
              height: 30
            });
          }
        
        }
        this.setData({
          markers : result
        });
      }
    });
  }

如果只是這個程式碼的話,會發現我們測試賬號的如何資訊都無法渲染出來,這個是因為js是非同步操作的,我們要在if之後立馬就進行 setdata操作即可了

如何在全部for結束之後也再次的進行setdata操作即可了,完整程式碼就是

getNearUsers(){
    db.collection('users').where({
      location: _.geoNear({
        geometry: db.Geo.Point(this.data.longitude, this.data.latitude),
        minDistance: 0,
        maxDistance: 5000
        //這1000和5000的單位是米
      }),
      islocation : true
    }).field({
      longitude : true,
      latitude : true ,
      userPhoto : true
    }).get().then((res)=>{
      console.log(res.data);
      let data = res.data;
      let result = [];
      if(data.length){

        for(let i=0;i<data.length;i++){
          if(data[i].userPhoto.includes('cloud://')){
            wx.cloud.getTempFileURL({
              fileList: [data[i].userPhoto ],
              success: res => {
                // console.log(res.fileList[0].tempFileURL)
                result.push({
                  // 然後就是把我們獲取到的臨時路徑直接賦值給iconpath即可了
                  iconPath: res.fileList[0].tempFileURL,
                  id: data[i]._id,
                  latitude: data[i].latitude,
                  longitude: data[i].longitude,
                  width: 30,
                  height: 30
                });
                this.setData({
                  markers: result
                });
              }
            })
          }
          else{
            result.push({
              iconPath: data[i].userPhoto,
              id: data[i]._id,
              latitude: data[i].latitude,
              longitude: data[i].longitude,
              width: 30,
              height: 30
            });
          }
        
        }
        this.setData({
          markers : result
        });
      }
    });
  }

 

 

 得到的效果就是,可以看到另外一個使用者剛剛它換的頭像了

(後面的優化就是可以點選這個使用者的頭像之後我們就可以跳轉到它的詳情頁面了

這個功能在實現起來其實頁不復雜的,有一個和markers對應的事件,也就是點選了這個markers就會觸發這個事件了  

 

通過這個事件其實我們是可以拿到id值得

 

 markertap(ev){
    console.log(ev);
  }

通過在near.js裡面得這個函式,然後我們點選一下地圖裡面的marker圖片之後,我們得到的值就是:

 

這個markerID其實對應的就是使用者的id值了

  markertap(ev){
    // console.log(ev);
    wx.navigateTo({
      url: '/pages/detail/detail?userId=' + ev.markerId
    })
  }

通過這個程式碼其實就可以實現,點選地圖裡面的圖示的話我們就可以跳轉到這個使用者的詳情頁面去了

3、後面要測試的就是假如測試賬號關閉了共享位置的話

通過測試我們發現,測試號關閉了共享位置的話,在地圖裡面即使是重新整理了還是會看到這個使用者的頭像的

 (其實程式碼是沒有錯的,把專案關了再重啟之後會看到這個關閉了共享位置的使用者頭像就消失了

(其實還有其他可以優化的,就是可以在地圖的頭像上面加一段語音介紹自己等等的,因為小程式其實也是支援的,或者是可以計算我和你的距離

或者是我去你那邊的話我過去的導航和路線是怎麼樣的

 

二、search元件的佈局和樣式

(就是在主頁的上面新增一個查詢的框)

1、實現新建一個叫search的元件

 

 創立好了之後,就可以在首頁進行引用了

2、先在index.JSON檔案裡面引入這個元件

{
  "usingComponents": {
    "search" : "/components/search/search"
  }
}

3、在主頁裡面和用標籤一樣引用就可以了

可以直接在index.wxml中通過 <search /> 來使用即可了

 

該search元件就被引入了

通過基本的結構wxml

<!--components/search/search.wxml-->
<view class="container">
  <view class="search"> 
    <view class="search-text">
      <text class="iconfont iconsousuo"></text>
      <input type="text" placeholder="搜尋喵星人" />
    </view>
    <view class="search-cancel">取消</view>
  </view>
</view>

得到的效果:

 

會發現我們放大鏡圖示沒有顯示出來,所以我們要配置一下,讓這個圖示可以穿透出來即可了

也就是之前copyText.js寫過的

  options: {
    styleIsolation: 'apply-shared'
  },

就是為了讓這個圖示可以生效的

 

 

 這樣的話,我們的放大鏡就進來了

之後就可以對search.wxss的樣式進行設計了

 

/* components/search/search.wxss */
.container{position: fixed;left: 0;top: 0;width: 100%;height: 70rpx;z-index: 999;}
.search{ display: flex ; align-items: center;}
.search-text{ display: flex; align-items: center;flex: 1;} 

 

但是發現,圖片和這個元件融合在一起了

 

 這是因為因為是元件的引入的話,就不像在主頁面一樣,可以佔位置的,所以就要到index.wxss設定一下讓index騰出一個空間來放這個搜尋框的

通過在

 

 就是直接通過margin來騰出位置即可了

 

 上面其實是在index.wxss中給上面的騰出來100rpx的空間

/* components/search/search.wxss */
.container{position: fixed;left: 0;top: 0;width: 100%;height: 70rpx;z-index: 999;}
.search{ display: flex ; align-items: center; margin:20rpx;}
.search-text{ display: flex; align-items: center;flex: 1;border: 1px #cdcdcd solid;border-radius:10rpx; height: 65rpx} 
.search-text .iconsousuo{margin: 0 10rpx;}
.search-cancel{margin: 0 10rpx;}

得到的效果就是:

 

 但是有一個問題就是:我們在還沒點選搜尋的時候,其實不用顯示後面的“取消”按鈕的,這個的話就要通過js邏輯來實現了

定義了一個isfocus來表示游標有沒有顯示的(這個取消的按鈕其實是在我們獲取了游標之後才會有的)

通過在取消按鈕加上了一個wx:if判斷之後,得到的效果就是:

 

 並且當我們獲取到了游標之後,這個搜尋框會適應整個頁面的高度了

 給contaner加上了  overflow: hidden; 之後得到的效果就是這個搜尋框的下邊框“不見了”

 

 這個是因為,我們得container這個大得塊要比我們輸入框得高度要小了,這個時候就可以在wxss裡面通過調節container得height

 

 即可了

因為如果我們點選了那個輸入框得胡,也就是聚焦了得話,我們得上面得搜尋框的大容器顯示的樣式是和沒聚焦的時候顯示的不同的,所以我們就可以用三目運算子來通過這個isfocus來決定使用哪個容器,也就是說我們可以定義兩個樣式不同的容器了

<view class="{{ isFocus ? 'containerFocus' : 'container' }}">
.containerFocus{position: fixed;left: 0;top: 0;width: 100%;height: 100%;z-index: 999;
background: #ccc}

然後我們自行的吧js檔案裡面定義的isFocus變數 定義weighted是true來看看我們獲取游標之後的效果是怎麼樣的:

 

之後我們就要通過邏輯裡控制他們的聚焦切換不同的container了,如果是已經點選聚焦的了話,還有一個就是可以看到我們搜尋的歷史記錄,還有列表等等

 

通過:

<view class="search-history">
    <text>歷史記錄</text>
    <text class="iconfont iconshanchu"></text>
  </view>
.search-history{ display: flex;justify-content: space-between;margin:20rpx;}

效果:

 

 然後就是要搞一個搜尋池了:

  <view class="search-history-btn">
    <text>小明</text>
    <text>123213</text>
    <text>dsadasd</text>
  </view>
.search-history{ display: flex;justify-content: space-between;margin:20rpx;}
.search-history-btn text{ border: 1px #cdcdcd solid; padding: 10rpx 20rpx;background: white;
border-radius: 20rpx; margin:10rpx;}

效果:(注意上面是給每一個搜尋的text進行樣式的定義

上面就吧搜尋的關鍵詞的佈局搞好了,下面就是要對搜尋的列表進行定義了(其實這個搜尋的列表和我們好友的列表是很像的,可以直接直接copy 在friendList.wxml裡面的這個結構了

  <navigator wx:for="{{ friendList }}" wx:key="{{ index }}" url="{{ '../detail/detail?userId=' + item._id}}" open-type="navigate">
      <view class="friendList-item">
        <view>
         <image src="{{ item.userPhoto }}" />
         <text> {{ item.nickName }} </text>
        </view>
        <text class="iconfont iconyoujiantou"></text>
      </view>
     </navigator>

然後對  searchList-item 的樣式也是直接拷貝friendList的wxss

.friendList-item{
  /* 這裡可以直接把user.wxss中的樣式影印過來了 */
  height: 120rpx;border-bottom:1px #b4b5b6 dashed;
padding: 10rpx; display: flex;align-items: center;justify-content: space-between;
}
.friendList-item view{display : flex; align-items: center;}
.friendList-item image{width: 100rpx;height: 100rpx;border-radius: 50%;}

 

綜上所述,我們的程式碼就是:

CSS
<!--components/search/search.wxml-->
<view class="{{ isFocus ? 'containerFocus' : 'container' }}">
  <view class="search"> 
    <view class="search-text">
      <text class="iconfont iconsousuo"></text>
      <input type="text" placeholder="搜尋喵星人" />
    </view>
    <view wx:if="{{ isFocus }}" class="search-cancel">取消</view>
  </view>

  <view class="search-history">
    <text>歷史記錄</text>
    <text class="iconfont iconshanchu"></text>
  </view>
  <view class="search-history-btn">
    <text>小明</text>
    <text>123213</text>
    <text>dsadasd</text>
  </view>

    <navigator url="" open-type="navigate">
      <view class="searchList-item">
        <view>
         <image src="" />
         <text>小喵喵</text>
        </view>
        <text class="iconfont iconyoujiantou"></text>
      </view>
     </navigator>

</view>
html

然後還要在search.js裡面通過

options: { styleIsolation: 'apply-shared' } 引入外部樣式 效果圖:(選中搜索框時)

 

(未選中搜索框時

 

 

 三、實現搜尋歷史記錄及本地快取

1、我們先在searc.wxml的輸入框標籤加一個處理點選這個輸入框的一個點選事件

bindfocus="handleFocus"

 還有我們在取消的標籤中,也要加一個點選事件,點選了的話就吧isFocus變成是false即可了

 <input type="text" placeholder="搜尋喵星人" bindfocus="handleFocus" />


<view wx:if="{{ isFocus }}" class="search-cancel" bindtap="handleCancel">取消</view>
 methods: {
    handleFocus(){
     this.setData({
       isFocus : true
     }); 
    },
    handleCancel(){
      this.setData({
        isFocus: false
      }); 
    }
  }

得到的效果就是:點選輸入框,就跳轉到輸入,點選取消,就跳轉到首頁

還有一個小bug就是,因為輸入框的話,會預設只有在一個範圍以內,才可以輸入的,所以我們就可以讓這個輸入框適應整個範圍,可以在

給 search.wxss中新增一個程式碼:

.search-text input {flex: 1;}

就讓這個輸入框可以自動的填滿整個的搜尋框了

3、之後就是對輸入的東西進行處理了,可以是邊輸入邊搜尋,也可以是輸入之後回車了才進行搜尋,如果是邊輸入就邊搜尋的話,我們可以通過bindinput來進行監聽的,那如果要是按回車的時候搜尋怎麼辦呢---這個其實小程式幫我們搞好了

https://developers.weixin.qq.com/miniprogram/dev/wxcloud/guide/storage/api.html

就可以通過在input中加上 bindconfirm 這個屬性來完成的,我們就定義了一個 handleConfirm 這個方法是隻有我們回車了才會進行觸發的

 

 

 在手機端裡面的回車 其實預設的是 “完成”兩個字的(就是點選這個輸入框的時候,手機就會彈出軟鍵盤了,它的確定按鈕是“搜尋”兩個字的,那這個該怎麼樣去修改呢==微信也提供了

 

 預設的是我們的 done 也就是完成

所以就在input標籤中,吧confirm-type 屬性變成是 search 即可了,(這樣的話在手機的軟鍵盤就會顯示 搜尋 兩個字了)

(下面我們要做的就是 吧這個搜尋的 放在歷史裡面管理起來了)

https://developers.weixin.qq.com/miniprogram/dev/wxcloud/guide/storage/api.html

demo:

wx.setStorage({
  key:"key",
  data:"value"
})

設定的話,就是我們使用者點選回車 之後,就可以吧這個搜尋裡面的 ev.detail.value放到本地儲存裡面即可了

因為這個setStorage的話,我們要讓這個data是一個數組才行的,然後我們先通過

data : [111]看看能不能吧這個111存放到這個數組裡面

 

 可以在下面的除錯板中 找到Storage 讓我們檢視一下

可以看到,我們隨便輸入一點東西,然後按 回車 之後可以看到

先在search.js的data裡面定義一個 陣列

然後我們就可以在wxml中,吧我們的歷史訊息text,用一個數組來for出來了

 <view class="search-history-btn">
    <text wx:for="{{ historyList }}" wx:key="{{ index }}">{{ item }}</text>
  </view>

 

然後我們在一開始 聚焦了之後,就立馬從storage裡面吧陣列拿出來,用getStorage方法:

https://developers.weixin.qq.com/miniprogram/dev/wxcloud/guide/storage/api.html

wx.getStorage({
  key: 'key',
  success (res) {
    console.log(res.data)
  }
})

 

使用上面的demo之後,會報錯,這個報錯一般都是因為success的回撥的時候要用箭頭函式才行的

   wx.getStorage({
        key: 'searchHistory',
        success:(res)=> {
          this.setData({
            historyList: res.data
          });
        }
      })

修改了之後,我們點選 聚焦 之後

這個 111 就是我們剛剛寫入到 searchStorage 數組裡面的

 

 (這個有一個小bug,就是,假如我們輸入了兩次相同的搜尋,然後存入到歷史記錄再打印出來的話,會有兩個的,我們不應該有兩個相同的歷史記錄的

 但是我們搜尋重複詞的話,我們也是顯示一次,然後把這個搜尋的提升到最前面去),表示最近搜尋,並且歷史記錄也要有一個數量的,不能把在一年之間的全部搜尋記錄都顯示出來的

這個去重的功能:1、實現克隆一份陣列

 (unshift的話就是往陣列的頭新增東西的,ES6本身就帶有一個set來完成去重功能的)

   handleConfirm(ev){
      // console.log(ev.detail.value);
      let cloneHistoryList = [...this.data.historyList];
      cloneHistoryList.unshift(ev.detail.value);
      wx.setStorage({
        key: "searchHistory",
        data: [...new Set(cloneHistoryList)]
      })
    }

我們的效果就達到了,重複輸入的話,會被提前,=

然後下面我們就要實現 歷史記錄的刪除功能了

就可以直接在這個刪除圖示的wxml中新增一個 bindtap點選事件  handleDelete 即可了(這個刪除的話,是刪除掉全部的歷史記錄的)

(微信給我們提供的對storage的操作中,remove是操作某一項的,而clear是刪除掉所有的

https://developers.weixin.qq.com/miniprogram/dev/wxcloud/guide/storage/api.html

wx.clearStorage()

直接這樣寫即可了

====**但是這樣可能如果我們後面在storage裡面也定義了其他的東西,這個語句的話會把其他快取也會清理掉的,所以我們這裡還是使用remove好點的

wx.removeStorage({
  key: 'key',
  success (res) {
    console.log(res)
  }
})

因為我們也是要在這個成功的回到中,把這個歷史資料陣列設定為空陣列,所以我們就要使用成功返回的箭頭函式才行的

即可實現刪除功能了,

效果就是:

 

 

 之後再次輸入1的時候,

 

 然後就是清空 歷史記錄:

 

 

 

 

下面是這個部分的程式碼

//components/search/search.js
Component({
  /**
   * 元件的屬性列表
   */
  options: {
    styleIsolation: 'apply-shared'
  },
  properties: {

  },

  /**
   * 元件的初始資料
   */
  data: {
    isFocus : false,
    historyList : []
  },

  /**
   * 元件的方法列表
   */
  methods: {

    handleFocus(){

      wx.getStorage({
        key: 'searchHistory',
        success:(res)=> {
          this.setData({
            historyList: res.data
          });
        }
      })

     this.setData({
       isFocus : true
     }); 
    },
    handleCancel(){
      this.setData({
        isFocus: false
      }); 
    },
    handleConfirm(ev){
      // console.log(ev.detail.value);
      let cloneHistoryList = [...this.data.historyList];
      cloneHistoryList.unshift(ev.detail.value);
      wx.setStorage({
        key: "searchHistory",
        data: [...new Set(cloneHistoryList)]
      })
    },
    handleHistoryDelete(){
      wx.removeStorage({
        key: 'searchHistory',
        success:(res)=>{
          this.setData({
            historyList : []
          });

        }
      })
    }
  }
})
<!--components/search/search.wxml-->
<view class="{{ isFocus ? 'containerFocus' : 'container' }}">
  <view class="search"> 
    <view class="search-text">
      <text class="iconfont iconsousuo"></text>
      <input type="text" placeholder="搜尋喵星人" bindfocus="handleFocus" bindconfirm="handleConfirm" confirm-type="search"/>
    </view>
    <view wx:if="{{ isFocus }}" class="search-cancel" bindtap="handleCancel">取消</view>
  </view>

  <view class="search-history">
    <text>歷史記錄</text>
    <text bindtap="handleHistoryDelete" class="iconfont iconshanchu"></text>
  </view>
  <view class="search-history-btn">
    <text wx:for="{{ historyList }}" wx:key="{{ index }}">{{ item }}</text>
  </view>

    <navigator url="" open-type="navigate">
      <view class="searchList-item">
        <view>
         <image src="" />
         <text>小喵喵</text>
        </view>
        <text class="iconfont iconyoujiantou"></text>
      </view>
     </navigator>

</view>
/* components/search/search.wxss */
.container{position: fixed;left: 0;top: 0;width: 100%;height: 90rpx;z-index: 999;overflow: hidden;}
.containerFocus{position: fixed;left: 0;top: 0;width: 100%;height: 100%;z-index: 999;
background: #ccc}
.search{ display: flex ; align-items: center; margin:20rpx;}
.search-text{ display: flex; align-items: center;flex: 1;border: 1px #cdcdcd solid;border-radius:10rpx; height: 65rpx; background: white;} 
.search-text input {flex: 1;}
.search-text .iconsousuo{margin: 0 10rpx;}
.search-cancel{margin: 0 10rpx;}

.search-history{ display: flex;justify-content: space-between;margin:20rpx;margin-bottom: 30rpx;}
.search-history-btn{ margin-bottom: 30rpx; }
.search-history-btn text{ border: 1px #cdcdcd solid; padding: 10rpx 20rpx;background: white;
border-radius: 20rpx; margin:10rpx;}


.searchList-item{
  /* 這裡可以直接把user.wxss中的樣式影印過來了 */
  height: 120rpx;border-bottom:1px #b4b5b6 dashed;
padding: 10rpx; display: flex;align-items: center;justify-content: space-between;
}
.searchList-item view{display : flex; align-items: center;}
.searchList-item image{width: 100rpx;height: 100rpx;border-radius: 50%;}