Create by jsliang on 2018-11-21 20:46:36
Recently revised in 2018-11-25 00:24:14css
Hello 小夥伴們,若是以爲本文還不錯,記得給個 star , 大家的 star 是我學習的動力!GitHub 地址html
【2019-08-16】Hello 小夥伴們,因爲 jsliang 對文檔庫進行了重構,這篇文章的一些連接可能失效,而 jsliang 沒有精力維護掘金這邊的舊文章,對此深感抱歉。請須要獲取最新文章的小夥伴,點擊上面的 GitHub 地址,去文檔庫查看調整後的文章。前端
開篇點題:
這是一篇專研微信小程序各類功能實現的文章,例如佈局、通信錄、組件之底部導航欄等……
感受不錯的小夥伴,點贊點 Star走一波;
感受文章有誤的小夥伴,評論區、QQ羣 溜達一番。
虛心求教,不勝感激~git
項目成果圖:es6
不折騰的前端,和鹹魚有什麼區別github
文章篇幅甚多,請利用好目錄進行跳轉!編程
返回目錄json
寫文章無形中也會磨鍊本身的表達能力。
這周 (2018-11-19
) 在開發微信小程序的定製 通信錄 時,忽然發現 微信小程序 bug 集中營 這篇文章不能再繼續寫了,由於它變得 臃腫、醜陋 且 難維護,就連我這個寫做人都感慨:若是沒有 Ctrl + F
,以及個人 目錄 寫得還不錯,我真心不想再翻這篇文章。
爲此,jsliang 單獨開了一篇文章:微信小程序功能清單。用來記錄小程序各類功能的實現,例如佈局、通信錄、底部導航欄……
而後嘛,爲了能吸引小夥伴點進來瞅瞅,起個標新立異的標題吧:微信小程序之奇技淫巧。小程序
返回目錄後端
爲了小夥伴能快速瞭解代碼中的意思,小夥伴能夠去該 項目地址 下載代碼到本地運行查看。
敲了再說
敲 看
一 一
遍 遍
? ?
天 誰
差 都
地 可
別 以
! !
順帶附上一些資源網站:
若是你發現你的 CSS
水平還處於 float
佈局,你會發如今小程序中你舉步維艱,由於單單隻用浮動佈局,在小程序中它很差用。
因此,Flex
佈局,是你的不二選擇:佈局的傳統解決方案,基於盒狀模型,依賴 display
屬性 + position
屬性 + float
屬性。它對於那些特殊佈局很是不方便,好比,垂直居中就不容易實現。而 Flex
佈局。又稱彈性佈局,能夠簡便、完整、響應式地實現各類頁面佈局。
網上較好的教程有:
若是你想全面瞭解 Flex
,推薦去看上面的文章。
若是你已經瞭解 Flex
佈局,點擊 返回目錄 尋找更多精彩!
若是你想快速複習瀏覽 Flex
佈局,那麼,Here we go
~
Flex
最終實現效果:
萬丈高樓平地起,熟悉 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;
複製代碼
實現效果以下:
如圖,這是咱們要實現的左右佈局效果。那麼,在微信小程序要怎麼作呢?
*.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;
}
複製代碼
實現效果以下:
如圖,這是咱們要實現的混合佈局效果,那麼在微信小程序中要如何編程呢?
*.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;
}
複製代碼
不知道小夥伴們在平常開發中,有沒有碰到各類稀奇古怪的功能效果,咱們以爲難以想象,可是在項目經理的眼中它倒是能 「知足客戶需求」 的。
因此,拿到 「奇怪的」 需求清單的時候不要恐慌,咱們仔細分析,總能找到它的破綻,從而完成咱們的任務。
通信錄功能的開發以下:
開發時間:4 天
實現效果:
工欲善其事,必先利其器。
首先,咱們先將該頁面命名爲: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
,其餘暫時關閉。
在下文中,咱們將根據模式的開啓與關閉,顯示/隱藏某些內容,並進行數據的管理,請小夥伴們稍微理解下這種思路。
wxml
代碼:Go to wxmlwxss
代碼:Go to wxssjs
代碼`: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
來禁止繼續加載。
這樣,咱們就完美搞定了搜索功能的實現。
本章節實現效果:
衆所周知,微信小程序的子頁面(除了設置 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
這三個活躍狀態與否的設置。
這樣,咱們就實現了底部導航欄組件的開發及引用。
本章節實現效果:
彈窗?微信小程序就有啊,爲啥不用它的呢?
類型 | 說明 | 地址 |
---|---|---|
模態彈窗 | 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
控制修改彈窗。
文章寫到這裏,咱們須要整理下咱們都完成了什麼,還缺什麼?
如上,咱們實現了:
那麼,咱們還缺乏:
很好!咱們實現了一半功能了!可是,小夥伴有沒有發現,咱們的主內容區是空白的。
因此,爲了剩下的功能實現,咱們應該編寫下 內容區域,並進行頁面的數據加載:
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()
這個內置的頁面加載函數中,虛擬了接口返回的第一頁數據,最後將它循環判斷,放在不一樣的字母中,從而實現了首頁的加載。
因此,咱們能夠開始實現咱們其餘的功能咯~
本章節實現效果:
如上圖,咱們實現了新增的功能。那麼,它在代碼中是如何實現的呢?
首先,咱們要知道彈窗效果是如何出來的:
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: ''
})
}
}
})
複製代碼
到此,咱們就實現了新增的功能!
本章節實現效果:
在新增功能的開發後,咱們的修改功能就顯得比較容易了。
首先,咱們整理下修改的思路:
因此,在 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;
}
}
}
}
}
}
})
複製代碼
這樣,咱們就實現了彈窗修改功能!
本章節實現效果:
若是有小夥伴是跟着前面章節一步一步走下來的,會發現我在寫 搜索功能 的時候,寫上了刪除模式 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("取消刪除!");
}
}
})
}
})
複製代碼
本章節實現效果:
寫到這裏,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拼音模式上拉");
}
}
})
複製代碼
本章節實現效果:
如今,咱們完成最後且最重要的一步,實現 拼音導航 功能。
首先,咱們先實現拼音導航的佈局:
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
})
}
}
})
複製代碼
如上,咱們成功實現拼音導航所有功能!!!
天下大勢,分久必合,合久必分。
寫到這裏,咱們的通信錄已然完結,在此附上 jsliang 的代碼地址:項目地址
然而,這是結束嗎?並非,咱們的通信錄,還有個功能未實現:
如何在新增、刪除的時候,對新增的字母進行排序,並導航到具體位置?
在工做項目的開發中,jsliang 曾想到將新增的中文暱稱轉換爲拼音,而後經過二分查找法,找到對應的位置並進行插入……
可是,正印了那句話:個人能力,能夠造火箭,我卻只有敲釘子的時間!
時間是一切程序猿的殺手,新增排序,我們,有緣再會!
不按期更新,詳情可關注 jsliang 的 GitHub 地址
最後的最後,奉上上面實例中的地址:
撰文不易,若是文章對小夥伴有幫助,但願小夥伴們給勤勞敲代碼、辛苦撰文的 jsliang 進行微信打賞,讓他更有動力寫出更豐富、更精彩的文章,謝謝~
jsliang 的文檔庫 由 梁峻榮 採用 知識共享 署名-非商業性使用-相同方式共享 4.0 國際 許可協議進行許可。
基於github.om/LiangJunron…上的做品創做。
本許可協議受權以外的使用權限能夠從 creativecommons.org/licenses/by… 處得到。