Create by jsliang on 2018-11-21 20:46:36
Recently revised in 2018-11-25 00:24:14
Hello 小夥伴們,如果覺得本文還不錯,記得給個 star , 你們的 star 是我學習的動力!GitHub 地址
開篇點題:
這是一篇專研微信小程式各種功能實現的文章,例如佈局、通訊錄、元件之底部導航欄等……
感覺不錯的小夥伴,點贊點 Star走一波;
感覺文章有誤的小夥伴,評論區、QQ群 溜達一番。
虛心求教,不勝感激~
專案成果圖:
一 目錄
不折騰的前端,和鹹魚有什麼區別
文章篇幅甚多,請利用好目錄進行跳轉!
二 前言
寫文章無形中也會磨鍊自己的表達能力。
這周 (2018-11-19
) 在開發微信小程式的定製 通訊錄 時,突然發現 微信小程式 bug 集中營 這篇文章不能再繼續寫了,因為它變得 臃腫、醜陋 且 難維護,就連我這個寫作人都感慨:如果沒有 Ctrl + F
,以及我的 目錄 寫得還不錯,我真心不想再翻這篇文章。
為此,jsliang 單獨開了一篇文章:微信小程式功能清單。用來記錄小程式各種功能的實現,例如佈局、通訊錄、底部導航欄……
然後嘛,為了能吸引小夥伴點進來瞅瞅,起個標新立異的標題吧:微信小程式之奇技淫巧。
三 功能列表
為了小夥伴能快速瞭解程式碼中的意思,小夥伴可以去該 專案地址 下載程式碼到本地執行檢視。
敲了再說
敲 看
一 一
遍 遍
? ?
天 誰
差 都
地 可
別 以
! !
順帶附上一些資源網站:
3.1 排兵佈陣 - Flex佈局
如果你發現你的 CSS
水平還處於 float
佈局,你會發現在小程式中你舉步維艱,因為單單隻用浮動佈局,在小程式中它不好用。
所以,Flex
佈局,是你的不二選擇:佈局的傳統解決方案,基於盒狀模型,依賴 display
屬性 + position
屬性 + float
屬性。它對於那些特殊佈局非常不方便,比如,垂直居中就不容易實現。而 Flex
佈局。又稱彈性佈局,可以簡便、完整、響應式地實現各種頁面佈局。
網上較好的教程有:
如果你想全面瞭解 Flex
,推薦去看上面的文章。
如果你已經瞭解 Flex
佈局,點選 返回目錄 尋找更多精彩!
如果你想快速複習瀏覽 Flex
佈局,那麼,Here we go
~
Flex
最終實現效果:
3.1.1 樓起平地 - 基礎概念
萬丈高樓平地起,熟悉 Flex
需要先了解下面這 7
個 CSS
屬性:
/* 設定 Flex 模式 */
display: flex;
/* 決定元素是橫排還是豎著排,要不要倒序 */
flex-direction: column;
/* 決定元素換行格式,一行排不下的時候如何排 */
flex-wrap: wrap;
/* flex-flow = flex-direction + flex-wrap */
flex-flow: column wrap;
/* 同一排下對齊方式,空格如何隔開各個元素 */
justify-content: space-between;
/* 同一排下元素如何對齊,頂部對齊、中部對齊還是其他 */
align-items: center;
/* 多行對齊方式 */
align-content: space-between;
複製程式碼
下面我們詳細分析這些元素的情況:
flex-direction
:決定主軸的方向
row
- (預設)水平方向,起點在左端row-reverse
- 水平方向,起點在右端column
- 垂直方向,起點在上沿column-reverse
- 垂直方向,起點在下沿
display: flex;
flex-direction: row | row-reverse | column | column-reverse;
複製程式碼
flex-wrap
:一條軸線(一行)排不下時如何解決
nowrap
- (預設)不換行wrap
- 換行,第一行在上方wrap-reverse
- 換行,第一行在下方
display: flex;
flex-wrap: nowrap | wrap | wrap-reverse;
複製程式碼
flex-flow
:flex-flow = flex-direction + flex-wrap。即 flex-flow 是這兩個屬性的合集
row nowrap
- (預設)水平方向,起點在左端,不換行
display: flex;
flex-flow: <flex-direction> || <flex-wrap>;
複製程式碼
詳解參考 1
和 2
justify-content
:定義專案在主軸上的對齊方式
flex-start
- 左邊對齊flex-end
- 右邊對齊center
- 居中對齊space-between
- 兩端對齊,空格在中間space-around
- 空格環繞
display: flex;
justify-content: flex-start | flex-end | center | space-between | space-around;
複製程式碼
align-items
:定義專案在交叉軸上如何對齊
flex-start
- 頂部對齊,即文字圖片等頂部同一條線上flex-end
- 底部對其,即文字圖片等底部在同一條線上center
- 中間對其,即文字圖片不管多高,都拿它們的中間放在同一條線上stretch
- 將文字圖片充滿整個容器的高度,強制統一baseline
- 將每項的第一行文字做統一在一條線上對齊
display: flex;
align-items: flex-start | flex-end | center | stretch | baseline;
複製程式碼
align-content
:定義多根軸線的對齊方式。如果只有一根軸線(只有一行),該屬性不起作用
flex-start
- 這幾行頂部對齊flex-end
- 這幾行底部對齊center
- 這幾行居中對齊stretch
- 這幾行進行擴充套件或者縮放,從而填滿容器高space-between
- 這幾行中間使用空格進行填充space-around
- 這幾行兩邊及中間進行填充
display: flex;
align-content: flex-start | flex-end | center | space-between | space-around | stretch;
複製程式碼
3.1.2 搭磚建瓦 - 左右佈局
實現效果如下:
如圖,這是我們要實現的左右佈局效果。那麼,在微信小程式要怎麼做呢?
*.wxml
<view class="left-and-right-layout">
<view class="left-and-right-layout-floor-one">
<text>左右佈局</text>
</view>
<view class="left-and-right-layout-floor-two">
<text class="left-and-right-layout-floor-two-left">GitHub 地址</text>
<navigator class="left-and-right-layout-floor-two-right" url="https://github.com/LiangJunrong/document-library/blob/master/other-library/WeChatApplet/WeChatAppletFunctionList.md">檢視詳情</navigator>
</view>
</view>
複製程式碼
*.wxss
.left-and-right-layout {
padding: 0 30rpx;
}
.left-and-right-layout-floor-one {
font-size: 32rpx;
line-height: 32rpx;
font-weight: bold;
}
.left-and-right-layout-floor-two {
/* Flex 左右佈局關鍵點 */
display: flex;
justify-content: space-between;
padding: 30rpx 0;
font-size: 30rpx;
line-height: 30rpx;
border-bottom: 1rpx solid #ccc;
}
.left-and-right-layout-floor-two-right {
color: deepskyblue;
}
複製程式碼
3.1.3 層臺累榭 - 混合佈局
實現效果如下:
如圖,這是我們要實現的混合佈局效果,那麼在微信小程式中要如何程式設計呢?
*.wxml
<view class="mixed-layout">
<view class="mixed-layout-floor-one">
<text>混合佈局</text>
</view>
<view class="mixed-layout-floor-two">
<view class="mixed-layout-floor-two-left">
<text class="mixed-layout-floor-two-left-title">微信小程式之奇技淫巧</text>
<text class="mixed-layout-floor-two-left-author" url="https://github.com/LiangJunrong/document-library/blob/master/other-library/WeChatApplet/WeChatAppletFunctionList.md">作者:jsliang</text>
</view>
<view class="mixed-layout-floor-two-right">
<navigator>檢視詳情</navigator>
</view>
</view>
<view class="mixed-layout-floor-three">
<text>這是一篇專研小程式各種功能實現的文章,例如佈局、通訊錄、底部導航欄……如果你感覺不錯,可以點贊點 Star;如果感覺有錯,那就評論區溜達一番,虛心求教,不勝感激~ </text>
</view>
<view class="mixed-layout-floor-four">
<text>2018-11-23</text>
<text>2018閱讀</text>
<text class="mixed-layout-floor-four-classification">#小程式功能清單#</text>
</view>
</view>
複製程式碼
*.wxss
/* 混合佈局 */
/* 混合佈局包裹層 */
.mixed-layout {
margin-top: 30rpx;
padding: 0 30rpx 30rpx;
}
/* 混合佈局第一層 */
.mixed-layout-floor-one {
font-size: 32rpx;
line-height: 32rpx;
font-weight: bold;
}
/* 混合佈局第二層 */
.mixed-layout-floor-two {
/* 關鍵 Flex 佈局 */
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 40rpx;
font-size: 32rpx;
border-bottom: 1rpx dotted #ccc;
}
.mixed-layout-floor-two-left {
/* 左側豎行排序 */
display: flex;
flex-direction: column;
}
.mixed-layout-floor-two-left-title {
font-weight: bold;
}
.mixed-layout-floor-two-left-author {
margin-top: 10rpx;
color: rgb(146, 138, 138);
font-size: 30rpx;
}
.mixed-layout-floor-two-right {
color: deepskyblue;
}
/* 混合佈局第三層 */
.mixed-layout-floor-three {
margin-top: 20rpx;
font-size: 30rpx;
line-height: 36rpx;
color: rgb(110, 108, 108);
text-indent: 1em;
}
/* 混合佈局第四層 */
.mixed-layout-floor-four {
/* 關鍵 Flex 佈局 */
display: flex;
justify-content: space-between;
margin-top: 20rpx;
font-size: 30rpx;
line-height: 30rpx;
}
.mixed-layout-floor-four-classification {
color: #d0a763;
}
複製程式碼
3.2 沙場點兵 - 通訊錄
不知道小夥伴們在日常開發中,有沒有碰到各種稀奇古怪的功能效果,我們覺得不可思議,但是在專案經理的眼中它卻是能 “滿足客戶需求” 的。
所以,拿到 “奇怪的” 需求清單的時候不要恐慌,我們仔細分析,總能找到它的破綻,從而完成我們的任務。
通訊錄功能的開發如下:
開發時間:4 天
實現效果:
3.2.1 謀定蒼生 - 整體佈局
工欲善其事,必先利其器。
首先,我們先將該頁面命名為:addressList
,並編寫它的 json
門面:
addressList.json
{
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "通訊錄",
"navigationBarTextStyle": "black"
}
複製程式碼
接著,我們明確需要實現的功能點:
- 搜尋功能
- 彈窗新增功能
- 彈窗修改功能
- 刪除功能
- 拼音導航功能
- 底部導航欄
然後,我們明確下頁面佈局:
如上圖,它主要分三大塊:頭部、內容區、底部。
最後,我們根據功能實現及頁面佈局編寫 wxml
的佈局:
wxml 骨架
<!-- part1 - 搜尋區域 -->
<view class="search"></view>
<!-- part2 - 搜尋結果 -->
<view class="search-result"></view>
<!-- part3 - 內容區域 -->
<view class="contacts-list"></view>
<!-- part4 - 拼音導航 -->
<view class="pinyin-nav"></view>
<!-- part5 - 底部導航 -->
<view class="bottom-nav"></view>
<!-- part6 - 新增彈窗 -->
<view class="add-prompt"></view>
<!-- part7 - 修改彈窗 -->
<view class="edit-prompt"></view>
複製程式碼
如上,我們將頁面分為 7 種情況,其中:
- 搜尋功能 -
part1
、part2
、part4
、part5
- 彈窗新增功能 -
part1
、part3
、part4
、part5
、part6
- 彈窗修改功能 -
part1
、part3
、part4
、part5
、part7
- 刪除功能 -
part1
、part3
、part4
、part5
- 拼音導航功能 -
part1
、part3
、part4
、part5
- 底部導航欄 -
part1
、part3
、part4
、part5
請注意,出現的 part
部分表明在這種模式下,頁面要顯示的 part
都有哪些,其他的則暫時隱藏,而加粗的意味著這是這個功能特有的部分。為此,我們應該在 js
的 data
中定義好這些模式:
js 程式碼片段
Page({
data: {
/**
* 功能模式
* normalModel - 正常模式
* addModel - 新增模式
* editModel - 修改模式
* deleteModel - 刪除模式
* searchModel - 搜尋模式
* pinyinNavModel - 拼音導航模式
*/
normalModel: false,
addModel: false,
editModel: false,
deleteModel: false,
searchModel: true,
pinyinNavModel: false,
}
})
複製程式碼
這樣,我們除了底部導航欄外,為其他功能定義了一個模式,正常情況下我們開啟 normalModel
,其他暫時關閉。
在下文中,我們將根據模式的開啟與關閉,顯示/隱藏某些內容,並進行資料的管理,請小夥伴們稍微理解下這種思路。
3.2.2 千里尋敵 - 搜尋功能
- 檢視
wxml
程式碼:Go to wxml - 檢視
wxss
程式碼:Go to wxss - 檢視
js
程式碼`:Go to js
本章節實現效果:
實現思路、編碼及程式碼講解:
- 在
wxml
與wxss
結構上。
首先,我們通過 fixed
定位,將 search-form
固定在頂部。
然後,我們將 search-form
其內部分為 搜尋區 search
與 功能區 action
。
接著,我們將 search
分為 假的搜尋區 search-model-one
與 真的搜尋區 search-model-two
。為什麼要分兩種情況呢?因為這樣我們就不用煩惱 input
的 placeholder
一會居中一會靠邊要怎麼區分,思路不容易亂。
最後,根據功能,我們逐步完善 wxml
與 wxss
程式碼。
<!-- part1 - 搜尋區域 -->
<view class="search-form">
<!-- 搜尋區 -->
<view class="search">
<!-- 假的搜尋框 -->
<view wx:if="{{!searchModel}}" class="search-model search-model-one" bindtap="showSearch">
<image class="icon" src="../../public/img/icon_search.png"></image>
<text class="search-model-one-text">搜尋</text>
</view>
<!-- 真的搜尋框 -->
<view wx:if="{{searchModel}}" class="search-model search-model-two">
<image class="icon search-model-two-icon" src="../../public/img/icon_search.png"></image>
<!-- 多加層 view 的作用是做到 × 的定位作用 -->
<view class="search-model-two-form">
<input type="text" class="search-model-two-input" placeholder="搜尋" focus="{{inputFocus}}" value="{{searchVal}}" bindinput="monitorInputVal"></input>
<text wx:if="{{searchVal.length > 0}}" class="clear-input" bindtap="clearInput">×</text>
</view>
<text wx:if="{{searchVal.length <= 0}}" class="search-model-two-button search-model-two-button-cancel" bindtap="showSearch">取消</text>
<text wx:if="{{searchVal.length > 0}}" class="search-model-two-button search-model-two-button-submit" bindtap="searchSubmit">搜尋</text>
</view>
</view>
<!-- 功能區 -->
<view class="action">
<text class="action-button action-add" bindtap="showAdd">新增</text>
<text wx:if="{{!deleteModel}}" class="action-button action-delete" bindtap="showDelete">刪除</text>
<text wx:if="{{deleteModel}}" class="action-button action-delete-comfirm" bindtap="showDelete">完成</text>
</view>
</view>
<!-- part2 - 搜尋結果 -->
<view wx:if="{{searchModel}}" class="search-result">
<view class="search-result-item" wx:for="{{searchData}}" wx:key="{{searchData.index}}">
<view class="search-result-item-left">
<text class="search-result-item-left-name">{{item.userName}}</text>
<text class="search-result-item-left-phone">{{item.userPhone}}</text>
</view>
<view class="search-result-item-right">
<image class="icon search-result-item-right-edit" src="../../public/img/icon_edit.png"></image>
<image wx:if="{{deleteModel}}" class="icon search-result-item-right-delete" src="../../public/img/icon_delete.png"></image>
</view>
</view>
</view>
複製程式碼
/* 全域性樣式 */
view {
box-sizing: border-box;
}
.icon {
width: 32rpx;
height: 32rpx;
}
/* 搜尋區域 */
.search-form {
display: flex;
justify-content: space-around;
width: 100%;
height: 100rpx;
font-size: 32rpx;
padding: 0 30rpx;
/* 絕對定位 - 固定搜尋部分 */
position: fixed;
top: 0;
left: 0;
background: #fff;
}
/* 搜尋區域 - 結構 1 */
.search {
width: 60%;
}
.search-model {
height: 70rpx;
line-height: 50rpx;
padding: 10rpx 0;
}
.search-model-one {
margin: 15rpx 0;
background: #f5f5f5;
text-align: center;
border-radius: 50rpx;
}
.search-model-one-text {
margin-left: 30rpx;
color: #9b9b9b;
font-size: 30rpx;
}
.search-model-two {
position: relative;
display: flex;
margin-top: 6rpx;
}
.search-model-two-icon {
position: absolute;
left: 20rpx;
top: 30rpx;
z-index: 10;
}
.search-model-two-form {
width: 69%;
height: 70rpx;
background: #f5f5f5;
position: relative;
}
.search-model-two-input {
padding: 0 65rpx 0 65rpx;
height: 70rpx;
font-size: 30rpx;
}
.clear-input {
position: absolute;
right: 10rpx;
top: 15rpx;
display: inline-block;
width: 30rpx;
height: 30rpx;
line-height: 30rpx;
text-align: center;
padding: 5rpx;
color: #fff;
background: #ccc;
border-radius: 20rpx;
z-index: 10;
}
.search-model-two-button {
display: inline-block;
text-align: center;
width: 90rpx;
height: 60rpx;
line-height: 60rpx;
font-size: 24rpx;
padding: 5rpx 15rpx;
margin-left: 10rpx;
color: #fff;
}
.search-model-two-button-cancel {
background: rgb(8, 202, 186);
}
.search-model-two-button-submit {
background: rgb(8, 200, 248);
}
/* 搜尋區域 - 結構2 */
.action {
width: 39%;
}
.action-button {
display: inline-block;
text-align: center;
width: 90rpx;
height: 60rpx;
line-height: 60rpx;
font-size: 24rpx;
margin-top: 15rpx;
padding: 5rpx 15rpx;
border: 1rpx solid deepskyblue;
border-radius: 40rpx;
}
.action-add, .action-delete, .action-delete-comfirm {
margin-left: 10rpx;
}
.action-delete-comfirm {
color: #d0a763;
border: 1rpx solid #d0a763;
}
/* 搜尋結果 */
.search-result {
margin-top: 100rpx;
}
.search-result-item {
box-sizing: border-box;
height: 120rpx;
display: flex;
justify-content: space-between;
align-items: center;
padding: 27rpx 60rpx 27rpx 30rpx;
border-bottom: 1rpx solid #f3f3f3;
}
.search-result-item-left {
display: flex;
flex-direction: column;
}
.search-result-item-left-name {
font-size: 30rpx;
color: #333333;
}
.search-result-item-left-phone {
font-size: 26rpx;
color: #999999;
}
.search-result-item-right image {
width: 32rpx;
height: 32rpx;
}
.search-result-item-right-edit {
margin-right: 30rpx;
}
.search-result-item-right-delete {
margin-right: 30rpx;
}
複製程式碼
- 在
js
上。
我們仔細觀察本節開頭的 GIF
圖,發現它有這幾個特點:
- 點選假的搜尋區,進入真的搜尋區
- 輸入內容,按鈕由【取消】變為【搜尋】
- 點選【搜尋】按鈕,頁面顯示搜尋內容
- 上拉載入更多資料
- 點選
×
按鈕,輸入內容消失 - 點選【取消】按鈕,關閉搜尋頁面
Page({
/**
* 頁面的初始資料
*/
data: {
/**
* 功能模式
* normalModel - 正常模式
* addModel - 新增模式
* editModel - 修改模式
* deleteModel - 刪除模式
* searchModel - 搜尋模式
* pinyinNavModel - 拼音導航模式
*/
normalModel: true,
addModel: false,
editModel: false,
deleteModel: false,
searchModel: false,
pinyinNavModel: false,
/**
* 搜尋功能
* inputFocus - 搜尋框聚焦
* searchVal - 搜尋內容
* searchData - 搜尋結果
*/
inputFocus: false,
searchVal: '',
searchData: [],
},
/**
* 搜尋功能
* showSearch - 顯示搜尋框
* monitorInputVal - 監聽搜尋框的值
* searchSubmit - 提交搜尋
* clearInput - 清除搜尋
*/
showSearch(e) {
this.setData({
normalModel: !this.data.normalModel,
searchModel: !this.data.searchModel,
searchData: [],
inputFocus: true
})
},
monitorInputVal(e) {
this.setData({
searchVal: e.detail.value
})
},
searchSubmit(e) {
console.log("\n【API - 確認搜尋】");
console.log("搜素欄位:" + this.data.searchVal);
// 原資料
let searchData = this.data.searchData;
// 搜尋資料 - 假設搜尋資料是這個,實際應該是介面返回資料
let newSearchData = [
{
userName: '阿狸',
userPhone: '18811111111',
pinyin: 'ali'
},
{
userName: '貝吉塔',
userPhone: '18822222222',
pinyin: 'beijita'
},
{
userName: '楚怡',
userPhone: '18833333333',
pinyin: 'chuyi'
},
{
userName: '鄧婕',
userPhone: '18844444444',
pinyin: 'dengjie'
},
{
userName: '爾康',
userPhone: '18855555555',
pinyin: 'erkang'
},
{
userName: '福狸',
userPhone: '18866666666',
pinyin: 'fuli'
},
{
userName: '古狸',
userPhone: '18877777777',
pinyin: 'guli'
},
{
userName: '哈狸',
userPhone: '18888888888',
pinyin: 'hali'
},
{
userName: 'i狸',
userPhone: '18899999999',
pinyin: 'ili'
},
{
userName: '激狸',
userPhone: '18800000000',
pinyin: 'jli'
},
]
// 拼接新舊資料
searchData.push(...newSearchData);
console.log("\搜尋後資料:");
console.log(searchData);
this.setData({
searchData: searchData
})
},
clearInput(e) {
console.log("\n清除搜尋");
this.setData({
searchVal: ''
})
},
/**
* 刪除功能
*/
showDelete(e) {
this.setData({
deleteModel: !this.data.deleteModel
})
},
/**
* 生命週期函式--監聽頁面載入
*/
onLoad: function (options) {
console.log("\n通訊錄");
},
/**
* 頁面上拉觸底事件的處理函式
*/
onReachBottom: function () {
if (this.data.normalModel) { // 正常模式上拉
console.log("\n正常模式上拉")
} else if (this.data.searchModel) { // 搜尋模式上拉
console.log("\n搜尋模式上拉:");
// 新資料
let newSearchData = [
{
userName: '克狸',
userPhone: '18811121112',
pinyin: 'keli'
},
]
// 原資料
let searchData = this.data.searchData;
// 拼接新舊資料
searchData.push(...newSearchData);
console.log("\上拉載入後資料:");
console.log(searchData);
this.setData({
searchData: searchData
})
} else if (this.data.pinyinNavModel) { // 拼音模式上拉
console.log("\n拼音模式上拉");
}
},
})
複製程式碼
到此,我們就實現了搜尋功能。儘管它還有點小 bug
,就是不停上拉的時候,它會重複地載入一條資料。
在實際專案中,jsliang 會定義一個 searchNoData
來判斷介面是否還在返回資料,如果它不再返回資料,那麼通過判斷 searchNoData == true
來禁止繼續載入。
這樣,我們就完美搞定了搜尋功能的實現。
3.2.3 遙控追蹤 - 底部導航
本章節實現效果:
眾所周知,微信小程式的子頁面(除了設定 tabBar
的頁面)是沒有底部導航欄的。那麼,我們要如何設計,才能編寫一個 自定義的底部導航欄 呢?
在日常開發中,我們通過 fixed
佈局,在頁面實現一個 自定義的底部導航欄 是很容易的。
但是,考慮到其他頁面可能也需要使用這個底部導航欄,我們就需要想辦法將其封裝成元件了:
微信小程式 - 自定義元件
是的,微信小程式官方文件中是存在這個東西的。當然,僅有官方文件,是滿足不了我的,至於過程中我百度了幾篇文章來輔助寫出下面的程式碼,你猜?
下面貼出實現程式碼及如何使用:
- 建立目錄。
首先,在根目錄中新建 component
目錄,用來存放我們專案的元件。
然後,我們新建 navBar
目錄,用來存放我們的元件 navBar
。
最後,我們新建 Component
為 navBar
。
- 進行元件程式碼編寫。
navBar.wxml
<!-- 底部導航條 -->
<view class="navBar">
<!-- 首頁 -->
<view class="navBar-item navBar-home" bindtap='goHome'>
<image wx:if="{{homeActive}}" src="../../public/img/tabBar_home.png"></image>
<image wx:if="{{!homeActive}}" src="../../public/img/tabBar_home_nor.png"></image>
<text class="{{homeActive ? 'active-text' : 'nor-active-text'}}">首頁</text>
</view>
<!-- 探索 -->
<view class="navBar-item navBar-explore" bindtap='goExplore'>
<image wx:if="{{exploreActive}}" src="../../public/img/tabBar_explore.png"></image>
<image wx:if="{{!exploreActive}}" src="../../public/img/tabBar_explore_nor.png"></image>
<text class="{{exploreActive ? 'active-text' : 'nor-active-text'}}">探索</text>
</view>
<!-- 我的 -->
<view class="navBar-item navBar-user" bindtap='goUser'>
<image wx:if="{{userActive}}" src="../../public/img/tabBar_user.png"></image>
<image wx:if="{{!userActive}}" src="../../public/img/tabBar_user_nor.png"></image>
<text class="{{userActive ? 'active-text' : 'nor-active-text'}}">我的</text>
</view>
</view>
複製程式碼
navBar.wxss
/* 底部導航條 */
.navBar {
display: flex;
justify-content: space-around;
box-sizing: border-box;
width: 100%;
height: 97rpx;
padding: 5rpx 0;
border-top: 1rpx solid #cccccc;
position: fixed;
bottom: 0;
background: #F7F7FA;
}
.navBar image {
width: 55rpx;
height: 55rpx;
}
.navBar-item {
display: flex;
flex-direction: column;
align-items: center;
font-size: 20rpx;
color: #999999;
}
.nor-active-text {
padding-top: 5rpx;
}
.active-text {
padding-top: 5rpx;
color: #d0a763;
}
複製程式碼
navBar.js
Component({
/**
* 元件的屬性列表
*/
properties: {
homeActive: {
type: Boolean,
value: false
},
exploreActive: {
type: Boolean,
value: false
},
userActive: {
type: Boolean,
value: false
}
},
/**
* 元件的初始資料
*/
data: {
},
/**
* 元件的方法列表
*/
methods: {
// 返回首頁
goHome: function (e) {
wx.switchTab({
url: '../index/index',
})
},
// 返回探索頁
goExplore: function (e) {
wx.switchTab({
url: '../explore/explore',
})
},
// 返回我的
goUser: function (e) {
wx.switchTab({
url: '../user/user',
})
}
}
})
複製程式碼
navBar.json
{
"component": true,
"usingComponents": {}
}
複製程式碼
- 在需要引用的介面引用該元件
addressList.wxml
<!-- part5 - 底部導航 -->
<view class="bottom-nav">
<navBar homeActive="{{homeActive}}"></navBar>
</view>
複製程式碼
addressList.json
{
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "通訊錄",
"navigationBarTextStyle": "black",
"usingComponents": {
"navBar": "../../component/navBar/navBar"
}
}
複製程式碼
addressList.js
Page({
data: {
// 引用底部導航
homeActive: true,
}
})
複製程式碼
下次我們還需使用該底部導航欄的時候,我們只需要重複在 addressList
的步驟就行了。
當然,我們需要根據需要活躍的位置,進行 homeActive
、exploreActive
、userActive
這三個活躍狀態與否的設定。
這樣,我們就實現了底部導航欄元件的開發及引用。
3.2.4 拒敵長城 - 彈窗實現
本章節實現效果:
彈窗?微信小程式就有啊,為啥不用它的呢?
型別 | 說明 | 地址 |
---|---|---|
模態彈窗 | wx.showModal(Object) - 模態彈窗可以給你選擇【取消】或者【確定】 | 連結 |
<modal> | <modal>是可以提供使用者填寫 | 連結 |
訊息彈窗 | wx.showToast(Object) - 訊息彈窗就是操作成功或者操作失敗的那一刻,系統的提示彈窗,無需使用者操作,可設定幾秒自動關閉 | 連結 |
操作選單 | wx.showActionSheet(Object) - 操作選單類似於彈出的下拉選單,提供你選擇其中某項或者【取消】 | 連結 |
然而,逐一嘗試,你會發現,上面辣麼多彈窗,沒有一種符合你的需求的!所以,我們要畫一個屬於自己的彈窗:
首先,我們在 part6
中新增兩個層:遮罩層 jsliang-mask
和彈窗內容 jsliang-alert
。
然後,往彈窗內容中編寫我們需要的標題、 input
輸入框以及 text
按鈕。
最後,我們逐一細化編寫程式碼。
addressList.wxml
<!-- part6 - 新增彈窗 -->
<view wx:if="{{addModel}}" class="add-prompt">
<!-- 遮罩層 -->
<view class="jsliang-mask" bindtap='showAdd'></view>
<!-- 彈窗內容 -->
<view class="jsliang-alert">
<!-- 標題 -->
<view class="jsliang-alert-title">
<text>新增成員</text>
<text class="jsliang-alert-title-close" bindtap='showAdd'>×</text>
</view>
<!-- 輸入內容 -->
<view class="jsliang-alert-content">
<input type="text" placeholder='請輸入姓名' placeholder-class='jsliang-alert-content-user-name-placeholder'></input>
<input type="text" placeholder='請輸入電話號碼' placeholder-class='jsliang-alert-content-user-phone-placeholder'></input>
</view>
<!-- 確定 -->
<view class="jsliang-alert-submit">
<text bindtap='addConfirm'>新增</text>
</view>
</view>
</view>
複製程式碼
addressList.wxss
/* 彈窗-新增成員 */
.jsliang-mask {
z-index: 998;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: #404040;
filter: alpha(opacity=90);
-ms-filter: "alpha(opacity=90)";
opacity: 0.9;
}
.jsliang-alert {
z-index: 999;
position: fixed;
top: 15%;
left: 9%;
width: 620rpx;
height: 580rpx;
box-shadow: 2rpx 2rpx 4rpx #A0A0A0, -2rpx -2rpx 4rpx #A0A0A0;
background-color: #fff;
border-radius: 15rpx;
}
/* 彈窗標題 */
.jsliang-alert-title {
height: 120rpx;
line-height: 120rpx;
color: #333333;
background: #f8f0e3;
font-size: 40rpx;
font-weight: bold;
text-align: center;
position: relative;
border-radius: 15rpx;
}
.jsliang-alert-title-close {
display: inline-block;
color: #999999;
position: absolute;
font-size: 50rpx;
right: 40rpx;
}
/* 彈窗內容 */
.jsliang-alert-content {
padding: 0 70rpx;
}
.jsliang-alert-content input {
height: 120rpx;
line-height: 120rpx;
font-size: 30rpx;
border-bottom: 1rpx solid #e6e6e6;
}
.jsliang-alert-content-user-name-placeholder, .jsliang-alert-content-user-phone-placeholder {
font-size: 30rpx;
color: #b6b6b6;
}
.jsliang-alert-content-user-phone {
color: rgb(238, 227, 227);
}
.jsliang-alert-submit {
font-size: 30rpx;
margin: 60rpx auto;
text-align: center;
width: 400rpx;
height: 90rpx;
line-height: 90rpx;
color: #fff;
background: deepskyblue;
border-radius: 50rpx;
}
複製程式碼
這樣,我們就可以通過控制 addModel
的 true
或者 false
,來顯示隱藏新增彈窗。
同理,我們可以依法炮製通過 editModel
控制修改彈窗。
3.2.5 臥薪嚐膽 - 思路整理
文章寫到這裡,我們需要整理下我們都完成了什麼,還缺什麼?
如上,我們實現了:
- 搜尋功能
- 底部導航
- 彈窗顯示
那麼,我們還缺少:
- 新增成員功能
- 修改成員功能
- 刪除成員功能
- 拼音導航功能
很好!我們實現了一半功能了!但是,小夥伴有沒有發現,我們的主內容區是空白的。
所以,為了剩下的功能實現,我們應該編寫下 內容區域,並進行頁面的資料載入:
addressList.wxml
<!-- part3 - 內容區域 -->
<view class="contacts-list">
<!-- 每組字母資料 -->
<view class="contacts-item" wx:for="{{contactsData}}" wx:for-item="contactsDataItem" wx:key="{{contactsDataItem.index}}">
<!-- 字母標題 -->
<view wx:if="{{!contactsDataItem.users.length < 1}}" class="contacts-list-title">
<text>{{contactsDataItem.groupName}}</text>
</view>
<!-- 該組字母的成員 -->
<view class="contacts-list-user" wx:for="{{contactsDataItem.users}}" wx:for-item="usersItem" wx:key="{{usersItem.index}}">
<!-- 成員資訊展示 -->
<view class="contacts-list-user-left">
<text class="contacts-list-user-left-name">{{usersItem.userName}}</text>
<text class="contacts-list-user-left-phone">{{usersItem.userPhone}}</text>
</view>
<!-- 成員操作 -->
<view class="contacts-list-user-right">
<image class="icon contacts-list-user-right-edit" src="../../public/img/icon_edit.png"></image>
<image wx:if="{{deleteModel}}" class="icon contacts-list-user-right-delete" src="../../public/img/icon_delete.png"></image>
</view>
</view>
</view>
</view>
複製程式碼
addressList.wxss
/* 聯絡人列表 */
.contacts-list {
margin-top: 100rpx;
margin-bottom: 120rpx;
}
.contacts-list-title {
box-sizing: border-box;
font-size: 24rpx;
font-weight: bold;
height: 44rpx;
line-height: 44rpx;
color: #b2b2b2;
background: #f5f5f5;
border-bottom: 1rpx solid #efefef;
padding-left: 30rpx;
}
.contacts-list-user {
box-sizing: border-box;
height: 120rpx;
display: flex;
justify-content: space-between;
align-items: center;
padding: 27rpx 60rpx 27rpx 30rpx;
border-bottom: 1rpx solid #f3f3f3;
}
.contacts-list-user-left {
display: flex;
flex-direction: column;
}
.contacts-list-user-left-name {
font-size: 30rpx;
color: #333333;
}
.contacts-list-user-left-phone {
font-size: 26rpx;
color: #999999;
}
.contacts-list-user-right image {
width: 32rpx;
height: 32rpx;
}
.contacts-list-user-right-edit {
margin-right: 30rpx;
}
.contacts-list-user-right-delete {
margin-right: 30rpx;
}
複製程式碼
addressList.js
Page({
data: {
// 資料定義
contactsData: [
{ groupName: 'A', users: [] },
{ groupName: 'B', users: [] },
{ groupName: 'C', users: [] },
{ groupName: 'D', users: [] },
{ groupName: 'E', users: [] },
{ groupName: 'F', users: [] },
{ groupName: 'G', users: [] },
{ groupName: 'H', users: [] },
{ groupName: 'I', users: [] },
{ groupName: 'J', users: [] },
{ groupName: 'K', users: [] },
{ groupName: 'L', users: [] },
{ groupName: 'M', users: [] },
{ groupName: 'N', users: [] },
{ groupName: 'O', users: [] },
{ groupName: 'P', users: [] },
{ groupName: 'Q', users: [] },
{ groupName: 'R', users: [] },
{ groupName: 'S', users: [] },
{ groupName: 'T', users: [] },
{ groupName: 'U', users: [] },
{ groupName: 'V', users: [] },
{ groupName: 'W', users: [] },
{ groupName: 'X', users: [] },
{ groupName: 'Y', users: [] },
{ groupName: 'Z', users: [] }
],
},
/**
* 生命週期函式--監聽頁面載入
*/
onLoad: function (options) {
console.log("\n通訊錄");
let that = this;
// 原資料
let oldData = that.data.contactsData;
// 第一頁資料
let newData = [
{
userName: '阿狸',
userPhone: '18811111111',
pinyin: 'ali'
},
{
userName: '貝吉塔',
userPhone: '18822222222',
pinyin: 'beijita'
},
{
userName: '楚怡',
userPhone: '18833333333',
pinyin: 'chuyi'
},
{
userName: '鄧婕',
userPhone: '18844444444',
pinyin: 'dengjie'
},
{
userName: '爾康',
userPhone: '18855555555',
pinyin: 'erkang'
},
{
userName: '福狸',
userPhone: '18866666666',
pinyin: 'fuli'
},
{
userName: '古狸',
userPhone: '18877777777',
pinyin: 'guli'
},
{
userName: '哈狸',
userPhone: '18888888888',
pinyin: 'hali'
},
{
userName: 'i狸',
userPhone: '18899999999',
pinyin: 'ili'
},
{
userName: '激狸',
userPhone: '18800000000',
pinyin: 'jli'
},
]
// 迴圈新資料
for (let newDataItem in newData) {
// 轉換新資料拼音首字母為大寫
let initials = newData[newDataItem].pinyin.substr(0, 1).toUpperCase();
// 迴圈舊資料
for (let oldDataItem in oldData) {
// 獲取舊資料字母分組
let groupName = oldData[oldDataItem].groupName;
// 判斷兩個字母是否相同
if (initials == groupName) {
// 使用 array[array.length] 將資料加入到該組中
oldData[oldDataItem].users[oldData[oldDataItem].users.length] = newData[newDataItem];
}
}
}
console.log("\頁面初始載入資料:");
console.log(oldData);
that.setData({
contactsData: oldData
})
}
})
複製程式碼
如上,我們在前幾章節程式碼的前提下,將 part3
部分進行定義,並在 onLoad()
這個內建的頁面載入函式中,虛擬了介面返回的第一頁資料,最後將它迴圈判斷,放在不同的字母中,從而實現了首頁的載入。
所以,我們可以開始實現我們其他的功能咯~
3.2.6 廣聚民心 - 新增功能
本章節實現效果:
如上圖,我們實現了新增的功能。那麼,它在程式碼中是如何實現的呢?
首先,我們要知道彈窗效果是如何出來的:
addressList.wxml 程式碼片段
<!-- part1 - 搜尋區域 -->
<view class="search-form">
<!-- 搜尋區 -->
<!-- ...... 該部分程式碼並無修改,故省略 -->
<!-- 功能區 -->
<view class="action">
<text class="action-button action-add" bindtap="showAdd">新增</text>
<text wx:if="{{!deleteModel}}" class="action-button action-delete" bindtap="showDelete">刪除</text>
<text wx:if="{{deleteModel}}" class="action-button action-delete-comfirm" bindtap="showDelete">完成</text>
</view>
</view>
複製程式碼
然後,我們在 js
中設定彈窗事件:
addressList.js 程式碼片段
showAdd(e) {
this.setData({
addModel: !this.data.addModel
})
},
複製程式碼
是的,在這裡,我們通過 addModel
的模式來控制彈窗,那麼,彈窗要怎麼編寫呢?相信小夥伴在前一章瞭解過彈窗效果的實現,在這裡我們為了連貫,再貼下實現新增彈窗的程式碼:
addressList.wxml 程式碼片段
<!-- part6 - 新增彈窗 -->
<view wx:if="{{addModel}}" class="add-prompt">
<!-- 遮罩層 -->
<view class="jsliang-mask" bindtap='showAdd'></view>
<!-- 彈窗內容 -->
<view class="jsliang-alert">
<!-- 標題 -->
<view class="jsliang-alert-title">
<text>新增成員</text>
<text class="jsliang-alert-title-close" bindtap='showAdd'>×</text>
</view>
<!-- 輸入內容 -->
<view class="jsliang-alert-content">
<input type="text" placeholder='請輸入姓名' placeholder-class='jsliang-alert-content-user-name-placeholder' name="addUserName" bindinput='getAddUserName' maxlength='11' value="{{addUserName}}"></input>
<input type="text" placeholder='請輸入電話號碼' placeholder-class='jsliang-alert-content-user-phone-placeholder' name="addUserPhone" bindinput='getAddUserPhone' maxlength='11' value="{{addUserPhone}}"></input>
</view>
<!-- 確定 -->
<view class="jsliang-alert-submit" bindtap='addConfirm'>
<text>新增</text>
</view>
</view>
</view>
複製程式碼
addressList.wxss 程式碼片段
/* 彈窗-新增成員 */
.jsliang-mask {
z-index: 998;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: #404040;
filter: alpha(opacity=90);
-ms-filter: "alpha(opacity=90)";
opacity: 0.9;
}
.jsliang-alert {
z-index: 999;
position: fixed;
top: 15%;
left: 9%;
width: 620rpx;
height: 580rpx;
box-shadow: 2rpx 2rpx 4rpx #A0A0A0, -2rpx -2rpx 4rpx #A0A0A0;
background-color: #fff;
border-radius: 15rpx;
}
/* 彈窗標題 */
.jsliang-alert-title {
height: 120rpx;
line-height: 120rpx;
color: #333333;
background: #f8f0e3;
font-size: 40rpx;
font-weight: bold;
text-align: center;
position: relative;
border-radius: 15rpx;
}
.jsliang-alert-title-close {
display: inline-block;
color: #999999;
position: absolute;
font-size: 50rpx;
right: 40rpx;
}
/* 彈窗內容 */
.jsliang-alert-content {
padding: 0 70rpx;
}
.jsliang-alert-content input {
height: 120rpx;
line-height: 120rpx;
font-size: 30rpx;
border-bottom: 1rpx solid #e6e6e6;
}
.jsliang-alert-content-user-name-placeholder, .jsliang-alert-content-user-phone-placeholder {
font-size: 30rpx;
color: #b6b6b6;
}
.jsliang-alert-content-user-phone {
color: rgb(238, 227, 227);
}
.jsliang-alert-submit {
font-size: 30rpx;
margin: 60rpx auto;
text-align: center;
width: 400rpx;
height: 90rpx;
line-height: 90rpx;
color: #fff;
background: deepskyblue;
border-radius: 50rpx;
}
複製程式碼
最後,我們完善 js
程式碼,獲取 input
的值,動態新增到原資料中:
addressList.js
Page({
/**
* 頁面的初始資料
*/
data: {
/**
* 新增功能
* addUserName - 新增的使用者名稱
* addUserPhone - 新增的電話號碼
*/
addUserName: '',
addUserPhone: '',
},
/**
* 新增功能
* showAdd - 顯示/隱藏 新增彈窗
* getAddUserName - 雙向繫結成員姓名
* getAddUserPhone - 雙向繫結成員電話
* addConfirm - 確認新增
*/
showAdd(e) {
this.setData({
addModel: !this.data.addModel
})
},
getAddUserName(e) {
this.setData({
addUserName: e.detail.value
})
},
getAddUserPhone(e) {
this.setData({
addUserPhone: e.detail.value
})
},
addConfirm(e) {
console.log("\n【API -新增成員】");
let userName = this.data.addUserName;
let userPhone = this.data.addUserPhone;
if (userName == "") { // 不允許姓名為空
wx.showModal({
title: '新增失敗',
content: '姓名不能為空~',
showCancel: false
})
} else if (!(/^[\u4e00-\u9fa5a-zA-Z]{1,11}$/.test(userName))) { // 不允許非中文或者大小寫英文
wx.showModal({
title: '新增失敗',
content: '請用中文或者大小寫英文命名~',
showCancel: false
})
} else if (userPhone == "") { // 不允許電話號碼為空
wx.showModal({
title: '新增失敗',
content: '電話號碼不能為空~',
showCancel: false
})
} else if (!(/^1[345789]\d{9}$/.test(userPhone))) { // 不允許電話號碼不是 13/4/5/7/8/9 開頭的 11 位數字
wx.showModal({
title: '新增失敗',
content: '請輸入正確的 11 位電話號碼~',
showCancel: false
})
} else { // 新增成功
// 新資料。假設後端介面返回的資料為 newData
let newData = {
userName: this.data.addUserName,
userPhone: this.data.addUserPhone,
pinyin: 'ali'
}
// 舊資料
let oldData = this.data.contactsData;
// 獲取新資料的首字母並轉換為大寫
let initials = newData.pinyin.substr(0, 1).toUpperCase();
// 迴圈舊資料
for (let oldDataItem in oldData) {
// 獲取舊資料字母
let groupName = oldData[oldDataItem].groupName;
// 判斷這兩者字母是否相同
if (initials === groupName) {
// 往該字母最後一位資料新增新資料
oldData[oldDataItem].users[oldData[oldDataItem].users.length] = newData;
}
}
console.log("新增後資料:");
console.log(oldData);
this.setData({
contactsData: oldData,
normalModel: true,
addModel: false,
addUserName: '',
addUserPhone: ''
})
}
}
})
複製程式碼
到此,我們就實現了新增的功能!
3.2.7 化繁為簡 - 修改功能
本章節實現效果:
在新增功能的開發後,我們的修改功能就顯得比較容易了。
首先,我們整理下修改的思路:
- 使用者點選按鈕,傳遞資料給視窗:使用者姓名、使用者電話。
- 使用者點選修改,迴圈遍歷原資料,找到要修改的字母組下要修改的名字再進行修改,所以,單單是上面的兩個欄位還不夠,應該有:使用者所在組、使用者原姓名、使用者新姓名、使用者電話。
所以,在 wxml
中我們應該這麼寫:
addressList.wxml 程式碼片段
<!-- part3 - 內容區域 -->
<view class="contacts-list">
<!-- 每組字母資料 -->
<view class="contacts-item" wx:for="{{contactsData}}" wx:for-item="contactsDataItem" wx:key="{{contactsDataItem.index}}">
<!-- 字母標題 -->
<!-- ... 程式碼省略 ... -->
<!-- 該組字母的成員 -->
<view class="contacts-list-user" wx:for="{{contactsDataItem.users}}" wx:for-item="usersItem" wx:key="{{usersItem.index}}">
<!-- 成員資訊展示 -->
<!-- ... 程式碼省略 ... -->
<!-- 成員操作 -->
<view class="contacts-list-user-right">
<image class="icon contacts-list-user-right-edit" src="../../public/img/icon_edit.png" bindtap="showEdit" data-username="{{usersItem.userName}}" data-userphone="{{usersItem.userPhone}}" data-groupname="{{contactsDataItem.groupName}}"></image>
<image wx:if="{{deleteModel}}" class="icon contacts-list-user-right-delete" src="../../public/img/icon_delete.png"></image>
</view>
</view>
</view>
</view>
複製程式碼
然後,我們將新增的彈窗照搬過來並加入電話無法修改的效果:
addressList.wxml 程式碼片段
<!-- part7 - 修改彈窗 -->
<view wx:if="{{editModel}}" class="edit-prompt">
<!-- 遮罩層 -->
<view class="jsliang-mask" bindtap='showEdit'></view>
<!-- 彈窗內容 -->
<view class="jsliang-alert">
<!-- 標題 -->
<view class="jsliang-alert-title">
<text>修改成員</text>
<text class="jsliang-alert-title-close" bindtap='showEdit'>×</text>
</view>
<!-- 輸入內容 -->
<view class="jsliang-alert-content">
<input type="text" placeholder='請輸入姓名' placeholder-class='jsliang-alert-content-user-name-placeholder' name="editUserName" bindinput='getEditUserName' maxlength='11' value="{{editNewUserName}}"></input>
<input type="text" class="input-forbid" placeholder='請輸入電話號碼' placeholder-class='jsliang-alert-content-user-phone-placeholder' name="editUserPhone" bindinput='getEditUserPhone' maxlength='11' value="{{editUserPhone}}" disabled="disabled"></input>
</view>
<!-- 確定 -->
<view class="jsliang-alert-submit" bindtap='editConfirm'>
<text>修改</text>
</view>
</view>
</view>
複製程式碼
addressList.wxss 程式碼片段
/* 彈窗-新增成員 */
.jsliang-mask {
z-index: 998;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: #404040;
filter: alpha(opacity=90);
-ms-filter: "alpha(opacity=90)";
opacity: 0.9;
}
.jsliang-alert {
z-index: 999;
position: fixed;
top: 15%;
left: 9%;
width: 620rpx;
height: 580rpx;
box-shadow: 2rpx 2rpx 4rpx #A0A0A0, -2rpx -2rpx 4rpx #A0A0A0;
background-color: #fff;
border-radius: 15rpx;
}
/* 彈窗標題 */
.jsliang-alert-title {
height: 120rpx;
line-height: 120rpx;
color: #333333;
background: #f8f0e3;
font-size: 40rpx;
font-weight: bold;
text-align: center;
position: relative;
border-radius: 15rpx;
}
.jsliang-alert-title-close {
display: inline-block;
color: #999999;
position: absolute;
font-size: 50rpx;
right: 40rpx;
}
/* 彈窗內容 */
.jsliang-alert-content {
padding: 0 70rpx;
}
.jsliang-alert-content input {
height: 120rpx;
line-height: 120rpx;
font-size: 30rpx;
border-bottom: 1rpx solid #e6e6e6;
}
.jsliang-alert-content-user-name-placeholder, .jsliang-alert-content-user-phone-placeholder {
font-size: 30rpx;
color: #b6b6b6;
}
.jsliang-alert-content-user-phone {
color: rgb(238, 227, 227);
}
.jsliang-alert-submit {
font-size: 30rpx;
margin: 60rpx auto;
text-align: center;
width: 400rpx;
height: 90rpx;
line-height: 90rpx;
color: #fff;
background: deepskyblue;
border-radius: 50rpx;
}
/* 彈窗-修改成員 */
.input-forbid {
color: rgb(202, 196, 196);
}
複製程式碼
最後,我們在 js
中實現修改的功能:
addressList.js 程式碼片段
// pages/addressList/addressList.js
Page({
/**
* 頁面的初始資料
*/
data: {
/**
* 修改功能
* editOldUserName - 在哪組改動
* editOldUserName - 原名字
* editNewUserName - 新名字
* editUserPhone - 電話
*/
editGroupName: '',
editOldUserName: '',
editNewUserName: '',
editUserPhone: '',
},
/**
* 修改功能
* showEdit - 顯示修改框
* getEditUserName - 雙向繫結成員名
* getEditUserPhone - 雙向繫結成員電話
* editConfirm - 確認修改
*/
showEdit(e) {
if (!this.data.editModel) { // 顯示彈窗則傳遞資料
this.setData({
editModel: true,
editGroupName: e.currentTarget.dataset.groupname,
editOldUserName: e.currentTarget.dataset.username,
editNewUserName: e.currentTarget.dataset.username,
editUserPhone: e.currentTarget.dataset.userphone,
})
} else { // 否則只控制彈窗隱藏
this.setData({
editModel: false
})
}
},
getEditUserName(e) {
this.setData({
editNewUserName: e.detail.value
})
},
editUserPhone(e) {
this.setData({
editUserPhone: e.detail.value
})
},
editConfirm(e) {
console.log("\n【API - 修改成員】");
let userName = this.data.editNewUserName;
let userPhone = this.data.editUserPhone;
if (userName == "") { // 不允許姓名為空
wx.showModal({
title: '修改失敗',
content: '姓名不能為空~',
showCancel: false
})
} else if (!(/^[\u4e00-\u9fa5a-zA-Z]{1,11}$/.test(userName))) { // 不允許非中文或者大小寫英文
wx.showModal({
title: '修改失敗',
content: '請用中文或者大小寫英文命名~',
showCancel: false
})
} else {
let contactsData = this.data.contactsData;
// 迴圈遍歷原資料
for (let groupInfo in contactsData) {
// 找到原資料中的該字母組
if (this.data.editGroupName == contactsData[groupInfo].groupName) {
// 遍歷該組的使用者名稱
for (let userInfo in contactsData[groupInfo].users) {
// 找到原資料中相同的姓名
if (this.data.editOldUserName == contactsData[groupInfo].users[userInfo].userName) {
// 修改它的姓名
contactsData[groupInfo].users[userInfo].userName = this.data.editNewUserName;
console.log("新增後資料:");
console.log(contactsData);
this.setData({
contactsData: contactsData,
editModel: false,
normalModel: true
})
wx.showToast({
title: '修改成功~',
})
break;
}
}
}
}
}
}
})
複製程式碼
這樣,我們就實現了彈窗修改功能!
3.2.8 革新去舊 - 刪除功能
本章節實現效果:
如果有小夥伴是跟著前面章節一步一步走下來的,會發現我在寫 搜尋功能 的時候,寫上了刪除模式 deleteModel
,可以喚出刪除按鈕:
addressList.wxml 程式碼片段
<!-- part1 - 搜尋區域 -->
<view class="search-form">
<!-- 搜尋區 -->
<!-- ... 程式碼省略 ... -->
<!-- 功能區 -->
<view class="action">
<text class="action-button action-add" bindtap="showAdd">新增</text>
<text wx:if="{{!deleteModel}}" class="action-button action-delete" bindtap="showDelete">刪除</text>
<text wx:if="{{deleteModel}}" class="action-button action-delete-comfirm" bindtap="showDelete">完成</text>
</view>
</view>
複製程式碼
它繫結了個 showDelete
的事件,來控制刪除按鈕的顯示隱藏:
addressList.js 程式碼片段
showDelete(e) {
this.setData({
deleteModel: !this.data.deleteModel
})
},
複製程式碼
addressList.wxml 程式碼片段
<!-- part3 - 內容區域 -->
<view class="contacts-list">
<!-- 每組字母資料 -->
<view class="contacts-item" wx:for="{{contactsData}}" wx:for-item="contactsDataItem" wx:key="{{contactsDataItem.index}}">
<!-- 字母標題 -->
<!-- ... 程式碼省略 ... -->
<!-- 該組字母的成員 -->
<view class="contacts-list-user" wx:for="{{contactsDataItem.users}}" wx:for-item="usersItem" wx:for-index="userIndex" wx:key="{{usersItem.index}}">
<!-- 成員資訊展示 -->
<!-- ... 程式碼省略 ... -->
<!-- 成員操作 -->
<view class="contacts-list-user-right">
<image class="icon contacts-list-user-right-edit" src="../../public/img/icon_edit.png" bindtap="showEdit" data-groupname="{{contactsDataItem.groupName}}" data-username="{{usersItem.userName}}" data-userphone="{{usersItem.userPhone}}"></image>
<image wx:if="{{deleteModel}}" class="icon contacts-list-user-right-delete" src="../../public/img/icon_delete.png" bindtap="showConfirm" data-groupname="{{contactsDataItem.groupName}}" data-username="{{usersItem.userName}}" data-index="{{userIndex}}"></image>
</view>
</view>
</view>
</view>
複製程式碼
然後,如何實現刪除功能呢?我們需要傳遞什麼資料給 js
?
- 字母組名
- 該項所在索引
我們只需要遍歷原資料,找到對應的組,並根據傳遞過來的索引,刪除該組中對應索引的值,我們就完成了刪除的功能:
addressList.js 程式碼片段
Page({
/**
* 刪除功能
* showDelete - 顯示/隱藏 刪除圖示
* showConfirm - 確認刪除
*/
showDelete(e) {
this.setData({
deleteModel: !this.data.deleteModel
})
},
deleteConfirm(e) {
console.log("\n【API - 刪除使用者");
let userName = e.currentTarget.dataset.username;
let groupName = e.currentTarget.dataset.groupname;
let index = e.currentTarget.dataset.index;
wx.showModal({
title: '刪除確認',
content: '是否刪除成員【' + e.currentTarget.dataset.username + "】?",
success: (e) => {
if (e.confirm) { // 如果確認刪除
console.log("刪除成功!");
// 原資料
let contactsData = this.data.contactsData;
// 遍歷原資料
for (let groupInfo in contactsData) {
// 找到要刪除成員所在的組
if (groupName == contactsData[groupInfo].groupName) {
// 根據索引刪除該條記錄
contactsData[groupInfo].users.splice(index, 1);
}
}
this.setData({
contactsData: contactsData
})
wx.showToast({
title: '刪除成功~',
})
} else if (e.cancel) { // 如果取消
console.log("取消刪除!");
}
}
})
}
})
複製程式碼
3.2.9 兵分一路 - 正常載入
本章節實現效果:
寫到這裡,jsliang 終於可以鬆一口氣了,我們離勝利不遠了~
現在,我們實現正常情況下的不斷下拉載入:
正如我們在 搜尋功能 實現章節中提及到的,我們分三種上拉模式:正常模式上拉、搜尋模式上拉、拼音模式上拉:
addressList.js 程式碼片段
page({
/**
* 頁面上拉觸底事件的處理函式
*/
onReachBottom: function () {
if (this.data.normalModel) { // 正常模式上拉
console.log("\n正常模式上拉");
} else if (this.data.searchModel) { // 搜尋模式上拉
console.log("\n搜尋模式上拉");
} else if (this.data.pinyinNavModel) { // 拼音模式上拉
console.log("\n拼音模式上拉");
}
}
})
複製程式碼
那麼,我們只需要參考 onLoad
中的正常載入方式,往正常模式中模擬資料,實現上拉效果,就 OK 了:
addressList.js 程式碼片段
Page({
/**
* 頁面的初始資料
*/
data: {
/**
* 上拉觸底
* normalModelNoData - 正常模式沒資料載入了
*/
normalModelNoData: false,
},
/**
* 頁面上拉觸底事件的處理函式
*/
onReachBottom: function () {
if (this.data.normalModel) { // 正常模式上拉
console.log("\n正常模式上拉");
if (!this.data.normalModelNoData) { // 如果還有資料
// 新資料
let newData = [
{
userName: '克狸',
userPhone: '18811121112',
pinyin: 'keli'
},
{
userName: '拉狸',
userPhone: '18811131113',
pinyin: 'lali'
},
{
userName: '磨狸',
userPhone: '18811141114',
pinyin: 'moli'
},
{
userName: '尼狸',
userPhone: '18811151115',
pinyin: 'nili'
},
{
userName: '噢狸',
userPhone: '18811161116',
pinyin: 'oli'
},
{
userName: '皮皮狸',
userPhone: '18811171117',
pinyin: 'pipili'
},
{
userName: '曲狸',
userPhone: '18811181118',
pinyin: 'quli'
},
{
userName: '任狸',
userPhone: '18811191119',
pinyin: 'renli'
},
{
userName: '司馬狸',
userPhone: '18811211121',
pinyin: 'simali'
},
{
userName: '提狸',
userPhone: '18811221122',
pinyin: 'tili'
}
]
// 原資料
let oldData = this.data.contactsData;
// 迴圈新資料
for (let newDataItem in newData) {
// 轉換新資料拼音首字母為大寫
let initials = newData[newDataItem].pinyin.substr(0, 1).toUpperCase();
// 迴圈舊資料
for (let oldDataItem in oldData) {
// 獲取舊資料字母分組
let groupName = oldData[oldDataItem].groupName;
// 判斷兩個字母是否相同
if (initials == groupName) {
// 使用 array[array.length] 將資料加入到該組中
oldData[oldDataItem].users[oldData[oldDataItem].users.length] = newData[newDataItem];
}
}
}
console.log("\上拉載入後資料:");
console.log(oldData);
this.setData({
contactsData: oldData,
normalModelNoData: true
})
} else { // 如果沒資料了
console.log("正常模式沒資料");
}
} else if (this.data.searchModel) { // 搜尋模式上拉
console.log("\n搜尋模式上拉:");
} else if (this.data.pinyinNavModel) { // 拼音模式上拉
console.log("\n拼音模式上拉");
}
}
})
複製程式碼
3.2.10 兵分二路 - 拼音導航
本章節實現效果:
現在,我們完成最後且最重要的一步,實現 拼音導航 功能。
首先,我們先實現拼音導航的佈局:
addressList.wxml 程式碼片段
<!-- part4 - 拼音導航 -->
<view class="pinyin-nav">
<view wx:for="{{letters}}" wx:key="{{letters.index}}">
<text class="pinyin-nav-byte" data-byte="{{item}}" bindtap="pingyinNav">{{item}}</text>
</view>
</view>
複製程式碼
addressList.wxss 程式碼片段
/* 拼音導航 */
.pinyin-nav {
font-size: 28rpx;
line-height: 28rpx;
position: fixed;
right: 10rpx;
top: 9%;
height: 80%;
text-align: center;
}
.pinyin-nav-byte {
display: inline-block;
width: 30rpx;
border-radius: 20rpx;
padding: 5rpx 5rpx;
margin-top: 3rpx;
color: #fff;
background: rgb(129, 212, 238);
}
複製程式碼
addressList.js 程式碼片段
Page({
/**
* 頁面的初始資料
*/
data: {
/**
* 拼音導航功能
* letters - 導航字母
*/
letters: ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'],
},
/**
* 拼音導航功能
* pininNav - 點選字母
*/
pingyinNav(e) {
console.log(e.currentTarget.dataset.byte);
},
})
複製程式碼
然後,佈局有了,我們要如何實現滾動效果呢?
考慮到裝置的不同,它的高度也不同,所以我們是需要獲取到樣式的動態高度的。先看看我們在 wxss
中定義的高度吧:
addressList.wxss 程式碼片段
.contacts-list-title {
height: 44rpx;
}
.contacts-list-user {
height: 120rpx;
}
複製程式碼
因此,我們的一個字母的高度,為 44rpx
;而一個使用者資料的高度,為 120rpx
,即我們要滾動的高度 = 44 * 字母個數 + 120 * 使用者條數。
最後,我們先在正常模式下模擬實現一遍拼音導航:
addressList.js 程式碼片段
Page({
/**
* 頁面的初始資料
*/
data: {
/**
* 拼音導航功能
* letters - 導航字母
* equipmentOneRpx - 裝置中 1rpx 為多少 px
*/
letters: ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'],
equipmentOneRpx: '',
},
/**
* 拼音導航功能
* pininNav - 點選字母
*/
pingyinNav(e) {
console.log("\n【API - 拼音導航】");
let byte = e.currentTarget.dataset.byte;
let dataLength = 0;
let byteLength = 0;
let data = this.data.contactsData;
for (let item in data) {
// 如果該字母比點選的字母小,則新增資料長度
if (data[item].groupName < byte) {
dataLength = dataLength + data[item].users.length;
}
// 如果該字母有內容,則加上它的字母長度
if (data[item].users.length >= 1 && data[item].groupName != byte) {
byteLength = byteLength + 1;
}
// 如果該字母等於點選的字母,則中斷迴圈
if (data[item].groupName == byte) {
break;
}
}
console.log("title 長度為:" + byteLength);
console.log("data 條數為:" + dataLength);
console.log("\n現在陣列為:");
console.log(data);
wx.pageScrollTo({
// 滾動高度
scrollTop: byteLength * (44 / this.data.equipmentOneRpx) + dataLength * (120 / this.data.equipmentOneRpx)
})
},
/**
* 生命週期函式--監聽頁面載入
*/
onLoad: function (options) {
console.log("\n通訊錄");
// 裝置資訊
wx.getSystemInfo({
success: res => {
console.log("\n裝置資訊為:");
console.log(res);
let equipmentOneRpx = 750 / res.windowWidth;
console.log("換算資訊:1rpx = " + equipmentOneRpx + "px");
this.setData({
equipmentOneRpx: equipmentOneRpx
})
},
})
}
})
複製程式碼
我們在 onLoad
中獲取到使用者裝置的資訊,然後計算出 1rpx
等於多少 px
。在 iphone6
中,1rpx = 2px
。我們只需要將 css
中寫的樣式高度 / 比例,就能動態計算我們的高度,從而實現滾動到目標位置的效果。
—————— 分割線 ——————
現在,我們開始 真拼音導航 功能的實現:
首先,我們應該考慮到,正常載入模式與拼音導航模式,會對 contactsData
的使用產生衝突:假如使用者劃拉了幾頁資料,然後進入拼音導航,那麼,使用者想下拉重新整理頁面的時候,可能就載入原本資料了,而不是載入該字母上面的資料……為此,我們在第一次載入拼音模式的時候,應該清空 contactsData
(多了也不行,因為使用者可能點選其他字母)。
然後,我們關閉正常模式,並開啟拼音導航模式,設定拼音導航模式不是第一次載入了。
接著,我們遍歷空資料和新資料,刪除重複資料後,將資料新增到 contactsData
中。
最後,我們才用上我們前面的頁面滾動效果,滾動到我們希望跳轉到的位置。
以上,考慮到步驟繁雜,我們應該使用 Promise
來實現:
addressList.js 程式碼片段
Page({
/**
* 頁面的初始資料
*/
data: {
/**
* 拼音導航功能
* letters - 導航字母
* equipmentOneRpx - 裝置中 1rpx 為多少 px
* firstEntryPinyinModel - 第一次進入拼音導航模式
*/
letters: ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'],
equipmentOneRpx: '',
firstEntryPinyinModel: true,
},
/**
* 拼音導航功能
* pininNav - 點選字母
*/
pinyinNav(e) {
console.log("\n【API - 拼音導航】");
let byte = e.currentTarget.dataset.byte;
// 開啟 Promise
const promise = new Promise((resolve, reject) => {
console.log("\n第一步:清空原資料");
let contactsData = [
{
groupName: 'A',
users: []
},
{
groupName: 'B',
users: []
},
{
groupName: 'C',
users: []
},
{
groupName: 'D',
users: []
},
{
groupName: 'E',
users: []
},
{
groupName: 'F',
users: []
},
{
groupName: 'G',
users: []
},
{
groupName: 'H',
users: []
},
{
groupName: 'I',
users: []
},
{
groupName: 'J',
users: []
},
{
groupName: 'K',
users: []
},
{
groupName: 'L',
users: []
},
{
groupName: 'M',
users: []
},
{
groupName: 'N',
users: []
},
{
groupName: 'O',
users: []
},
{
groupName: 'P',
users: []
},
{
groupName: 'Q',
users: []
},
{
groupName: 'R',
users: []
},
{
groupName: 'S',
users: []
},
{
groupName: 'T',
users: []
},
{
groupName: 'U',
users: []
},
{
groupName: 'V',
users: []
},
{
groupName: 'W',
users: []
},
{
groupName: 'X',
users: []
},
{
groupName: 'Y',
users: []
},
{
groupName: 'Z',
users: []
}
];
if (this.data.firstEntryPinyinModel) { // 為防止無法下拉,第一次進入拼音導航模式,清空原資料
this.setData({
contactsData: contactsData
})
}
// 告訴下一步可以執行了
let success = true;
resolve(success);
}).then(() => {
console.log("\n第二步:開啟拼音導航模式");
this.setData({
normalModel: false,
pinyinNavModel: true,
firstEntryPinyinModel: false,
})
}).then(() => {
console.log("\n第三步:判斷並新增資料");
let data = this.data.contactsData;
console.log("\n現在的資料有:");
console.log(data);
let newData = [
{
userName: '克狸',
userPhone: '18811121112',
pinyin: 'keli'
},
{
userName: '拉狸',
userPhone: '18811131113',
pinyin: 'lali'
},
{
userName: '磨狸',
userPhone: '18811141114',
pinyin: 'moli'
},
{
userName: '尼狸',
userPhone: '18811151115',
pinyin: 'nili'
},
{
userName: '噢狸',
userPhone: '18811161116',
pinyin: 'oli'
},
{
userName: '皮皮狸',
userPhone: '18811171117',
pinyin: 'pipili'
},
{
userName: '曲狸',
userPhone: '18811181118',
pinyin: 'quli'
},
{
userName: '任狸',
userPhone: '18811191119',
pinyin: 'renli'
},
{
userName: '司馬狸',
userPhone: '18811211121',
pinyin: 'simali'
},
{
userName: '提狸',
userPhone: '18811221122',
pinyin: 'tili'
}
]
console.log("\n新資料有:");
console.log(newData);
console.log("\n組合資料:");
for (let groupInfo in data) { // 迴圈原資料
for (let item in newData) { // 迴圈新資料
if (data[groupInfo].groupName == newData[item].pinyin.substr(0, 1).toUpperCase()) { // 如果新資料字母 與 原資料字母相同
// 清君側,刪除重複資料
// 迴圈使用者資料,判斷 新資料的使用者名稱 是否存在於使用者資料,如果存在則刪除之
for (let userInfo in data[groupInfo].users) { // 迴圈使用者原資料
console.log(newData);
if (newData.length > 1) {
if (data[groupInfo].users[userInfo].userName == newData[item].userName) { // 判斷 新資料的使用者名稱 是否存在於原使用者資料
newData.splice(item, 1);
}
}
}
if (newData.length > 1) { // 判斷是否還有資料
if (data[groupInfo].groupName == newData[item].pinyin.substr(0, 1).toUpperCase()) { // 再判斷一次新資料與舊資料字母是否相同
console.log("新增到組:【" + data[groupInfo].groupName + "】");
data[groupInfo].users.push(newData[item]);
console.log(data);
}
}
}
}
}
this.setData({
contactsData: data,
})
}).then(() => {
console.log("\n第四步:滾動頁面");
let dataLength = 0;
let byteLength = 0;
let data = this.data.contactsData;
console.log(data);
for (let item in data) {
// 如果該字母比點選的字母小,則新增資料長度
if (data[item].groupName < byte) {
dataLength = dataLength + data[item].users.length;
}
// 如果該字母有內容,則加上它的字母長度
if (data[item].users.length >= 1 && data[item].groupName != byte) {
byteLength = byteLength + 1;
}
// 如果該字母等於點選的字母,則中斷迴圈
if (data[item].groupName == byte) {
break;
}
}
console.log("title 長度為:" + byteLength);
console.log("data 條數為:" + dataLength);
console.log("\n現在陣列為:");
console.log(data);
wx.pageScrollTo({
// 滾動高度
scrollTop: byteLength * (44 / this.data.equipmentOneRpx) + dataLength * (120 / this.data.equipmentOneRpx)
})
})
}
})
複製程式碼
如此,我們就實現了拼音導航的點選載入了!下面,我們緊接著將拼音導航功能的 下拉重新整理 和 上拉載入 搞定吧~
關於下拉重新整理,我們需要現在 json
中開啟下拉重新整理的功能:
addressList.json
{
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "通訊錄",
"navigationBarTextStyle": "black",
"enablePullDownRefresh": true,
"usingComponents": {
"navBar": "../../component/navBar/navBar"
}
}
複製程式碼
然後,我們在 onPullDownRefresh
中實現程式碼效果即可:
addressList.js 程式碼片段
Page({
/**
* 頁面相關事件處理函式--監聽使用者下拉動作
*/
onPullDownRefresh: function () {
if (this.data.pinyinNavModel) { // 拼音下拉重新整理
console.log("\n【API - 拼音下拉重新整理】");
let data = this.data.contactsData;
console.log("\n現在的資料有:");
console.log(data);
let newData = [
{
userName: '阿狸',
userPhone: '18811111111',
pinyin: 'ali'
},
{
userName: '貝吉塔',
userPhone: '18822222222',
pinyin: 'beijita'
},
{
userName: '楚怡',
userPhone: '18833333333',
pinyin: 'chuyi'
},
{
userName: '鄧婕',
userPhone: '18844444444',
pinyin: 'dengjie'
},
{
userName: '爾康',
userPhone: '18855555555',
pinyin: 'erkang'
},
{
userName: '福狸',
userPhone: '18866666666',
pinyin: 'fuli'
},
{
userName: '古狸',
userPhone: '18877777777',
pinyin: 'guli'
},
{
userName: '哈狸',
userPhone: '18888888888',
pinyin: 'hali'
},
{
userName: 'i狸',
userPhone: '18899999999',
pinyin: 'ili'
},
{
userName: '激狸',
userPhone: '18800000000',
pinyin: 'jli'
},
]
console.log("\n新資料有:");
console.log(newData);
console.log("\n組合資料:");
for (let groupInfo in data) { // 迴圈原資料
for (let item in newData) { // 迴圈新資料
if (data[groupInfo].groupName == newData[item].pinyin.substr(0, 1).toUpperCase()) { // 如果新資料字母 與 原資料字母相同
// 清君側,刪除重複資料
// 迴圈使用者資料,判斷 新資料的使用者名稱 是否存在於使用者資料,如果存在則刪除之
for (let userInfo in data[groupInfo].users) { // 迴圈使用者原資料
if (newData.length > 1) {
if (data[groupInfo].users[userInfo].userName == newData[item].userName) { // 判斷 新資料的使用者名稱 是否存在於原使用者資料
newData.splice(item, 1);
}
}
}
if (newData.length > 1) { // 判斷是否還有資料
if (data[groupInfo].groupName == newData[item].pinyin.substr(0, 1).toUpperCase()) { // 再判斷一次新資料與舊資料字母是否相同
console.log("新增到組:【" + data[groupInfo].groupName + "】");
data[groupInfo].users.unshift(newData[item]);
console.log(data);
}
}
}
}
}
this.setData({
contactsData: data
})
}
}
})
複製程式碼
同時,拼音導航功能的上拉功能實現如下:
addressList.js 程式碼片段
Page({
onReachBottom: function () {
if (this.data.normalModel) { // 正常模式上拉
console.log("\n正常模式上拉");
} else if (this.data.searchModel) { // 搜尋模式上拉
console.log("\n搜尋模式上拉:");
} else if (this.data.pinyinNavModel) { // 拼音模式上拉
console.log("\n拼音模式上拉");
let data = this.data.contactsData;
console.log("\n現在的資料有:");
console.log(data);
let newData = [
{
userName: 'u狸',
userPhone: '18811311131',
pinyin: 'uli'
},
{
userName: 'v狸',
userPhone: '18811321132',
pinyin: 'vli'
},
{
userName: '無狸',
userPhone: '18811331133',
pinyin: 'wuli'
},
{
userName: '犀狸',
userPhone: '18811341134',
pinyin: 'xili'
},
{
userName: '毅狸',
userPhone: '18811351135',
pinyin: 'yili'
},
{
userName: '醉狸',
userPhone: '18811361136',
pinyin: 'zuili'
}
]
console.log("\n新資料有:");
console.log(newData);
console.log("\n組合資料:");
for (let groupInfo in data) { // 迴圈原資料
for (let item in newData) { // 迴圈新資料
if (data[groupInfo].groupName == newData[item].pinyin.substr(0, 1).toUpperCase()) { // 如果新資料字母 與 原資料字母相同
// 清君側,刪除重複資料
// 迴圈使用者資料,判斷 新資料的使用者名稱 是否存在於使用者資料,如果存在則刪除之
for (let userInfo in data[groupInfo].users) { // 迴圈使用者原資料
console.log(newData);
if (newData.length > 1) {
if (data[groupInfo].users[userInfo].userName == newData[item].userName) { // 判斷 新資料的使用者名稱 是否存在於原使用者資料
newData.splice(item, 1);
}
}
}
if (newData.length > 1) { // 判斷是否還有資料
if (data[groupInfo].groupName == newData[item].pinyin.substr(0, 1).toUpperCase()) { // 再判斷一次新資料與舊資料字母是否相同
console.log("新增到組:【" + data[groupInfo].groupName + "】");
data[groupInfo].users.push(newData[item]);
console.log(data);
}
}
}
}
}
this.setData({
contactsData: data
})
}
}
})
複製程式碼
如上,我們成功實現拼音導航全部功能!!!
3.2.11 一統天下 - 歸納總結
天下大勢,分久必合,合久必分。
寫到這裡,我們的通訊錄已然完結,在此附上 jsliang 的程式碼地址:專案地址
然而,這是結束嗎?並不是,我們的通訊錄,還有個功能未實現:
如何在新增、刪除的時候,對新增的字母進行排序,並導航到具體位置?
在工作專案的開發中,jsliang 曾想到將新增的中文暱稱轉換為拼音,然後通過二分查詢法,找到對應的位置並進行插入……
但是,正印了那句話:我的能力,可以造火箭,我卻只有敲釘子的時間!
時間是一切程式猿的殺手,新增排序,我們們,有緣再會!
四 專案地址
不定期更新,詳情可關注 jsliang 的 GitHub 地址
最後的最後,奉上上面例項中的地址:
撰文不易,如果文章對小夥伴有幫助,希望小夥伴們給勤勞敲程式碼、辛苦撰文的 jsliang 進行微信打賞,讓他更有動力寫出更豐富、更精彩的文章,謝謝~
jsliang 的文件庫 由 樑峻榮 採用 知識共享 署名-非商業性使用-相同方式共享 4.0 國際 許可協議進行許可。
基於github.om/LiangJunron…上的作品創作。
本許可協議授權之外的使用許可權可以從 creativecommons.org/licenses/by… 處獲得。