使用APICloud AVM多端框架開發app通訊錄功能

海的盡頭發表於2022-03-22

一、效果展示

二、專案結構圖

三、資料結構圖

1、服務端的原始好友資料結構

2、按字母分類排序後的好友資料結構

3、字母導航資料結構

四、功能實現的思路

本專案基於APICloud AVM框架編寫,因此思路要轉變下比如標籤的用法、CSS樣式表的寫法、專案的目錄結構、dom的操作等都不一樣了,完全是Vue、React的程式設計思維。

微信通訊錄功能是將所有聯絡人根據字母首字拼音排序分類,單擊右邊字母滑動到相應字母分組編。本專案的核心功能是對資料按首字母進行排序,頁面佈局可以按照微信的佈局進行設計,由於涉及到頁面滾動以及、滾動到指定位置,因此我們可以選用scroll-view元件。

本專案的頁面佈局結構圖如下

注意scroll-view組建必須設定高度否者不能正常顯示,高度計算公式如下:

高度=頁面視窗高度--狀態列高度--頂部導航高度--自定義tab-bar高度

程式碼如下

let top = 0;
if (api.safeArea) {
    top = api.safeArea.top;
} else {
    let res = wx.getSystemInfoSync();
    top = res.statusBarHeight;
}
this.list_h = api.winHeight - top - 44 - 53;

nav-bar和tab-bar是自定義的所以知道它的高度分別是44和53px

scroll-view 元件完整屬性如下

<scroll-view id="list" :show-scrollbar='false' :bounces='true' style={'height:'+list_h+'px'}>

接下來講解核心功能好友資料結構的轉換,從服務端拿到的好友資料一般是沒有按字母排序和分類的格式如下

[{
    "id": "1",
    "nick_name": "楊洋",
    "avatar": "../../res/avatar/tx7.jpg"
 }, 
 {
    "id": "2",
    "nick_name": "666",
    "avatar": "../../res/avatar/tx8.jpg"
}]

轉換後的資料格式如下

[{
    "letter": "A",
    "hasData": true,
    "users": [{
    "name": "abc1209",
    "unicode": 97,
    "avatar": "../../res/avatar/tx14.jpg",
    "id": "14"
    }]
}, {
     "letter": "B",
     "hasData": false,
     "users": []
    }, {
     "letter": "#",
     "hasData": true,
     "users": [{
     "name": "17115719973",
     "unicode": 49,
     "avatar": "../../res/avatar/tx1.jpg",
     "id": "1"
     }]
}]

轉換的原理就是提取nick_name欄位第一個字串獲取拼音字母以及unicode碼然後分組排序需要參照pinyin碼錶,網上可以下載,我這裡重新封裝了一下。

定義變數

用到的方法如下

init() {
    this.initName();
    this.NameIndex();
},
initName() {
    const letterArr = this.data.letter;
    for (let index = 0; index < letterArr.length; index++) {
        this.data.handleData.push({
            letter: letterArr[index].letter,
            hasData: false,
            users: []
        });
    }
},
NameIndex() {
    const that = this;
    for (let i = 0; i < that.data.list.length; i++) {
        const NameLetter = that.getLetter(that.data.list[i].nick_name).firstletter;
        const unicode = that.getLetter(that.data.list[i].nick_name).unicode;
        const index = that.indexPosition(NameLetter);
        if (that.data.nameIndex.indexOf(NameLetter) == -1) {
            that.data.handleData[index].hasData = true;
            that.data.nameIndex.push(NameLetter);
        }
        that.data.handleData[index].users.push({
            name: that.data.list[i].nick_name,
            unicode: unicode,
            avatar: that.data.list[i].avatar,
            id: that.data.list[i].id
        });
        that.paixu()//同一字母內排序
    }
},
indexPosition(letter) {
    if (!letter) { return ''; }
    const ACode = 65;
    return letter === '#' ? 26 : letter.charCodeAt(0) - ACode;
},
getLetter(str) {
    return this.getFirstLetter(str[0]);
},
getFirstLetter(str) {
    if (!str || /^ +$/g.test(str)) {
        return '';
    }
    const result = [];
    const unicode = str.charCodeAt(0);
    let ch = str.charAt(0);
    if (unicode >= 19968 && unicode <= 40869) {
        ch = this.data.firstletter.charAt(unicode - 19968);
    } else if ((unicode >= 97 && unicode <= 122) || (unicode >= 65 && unicode <= 90)) {
        ch = ch.toLocaleUpperCase();
    } else {
        ch = '#';
    }
    const obj = {
        unicode: unicode,
        firstletter: ch
    };
    return obj;
},
paixu() {
    for (let index = 0; index < this.handleData.length; index++) {
        if (this.handleData[index].hasData) {
            let userArr = this.handleData[index].users;
            userArr = userArr.sort((a, b) => {
                let value1 = a.unicode;
                let value2 = b.unicode;
                return value1 - value2;
            });
        }
    }
},

程式碼執行順序

轉換後handleData的資料格式如下

最後就是單擊字母滾動到指定區域,這裡用scroll-view組建的scrollTo方法

$('#list').scrollTo({ 'view': letter })。

這裡還要判斷當前字母是否在nameIndex陣列裡面,如果存在就滾動到指定區域,同時字母單擊後會新增綠色背景,所以這裡需要改變當前字母的active值為true

好友列表佈局(每個字母類別設定一個id, scrollTo方法根據id滾動到指定區域)

<view :id="rs.letter == '#' ? 'other' : rs.letter" v-for="(rs,index) in handleData" 
    :key="index" v-show="rs.hasData">
    <view class="list-item-title">
        <text class="letter">{rs.letter}</text>
    </view>
    <view class="list-item" v-for="(u, uIndex) in rs.users" :key="uIndex" @click="user(u)">
    <view class="list-item-left">
         <view class="img">
                <image class="avatar" :src="u.avatar"></image>
         </view>
    </view>
    <view class="list-item-right">
         <text class="name">{u.name}</text>
    </view>
    </view>
</view>

字母列表佈局

<view @click="letterClick(rs.letter,i)" v-for="(rs,i) in letter" :key="i">
    <text class="list-right-letter active" v-if="rs.active">{rs.letter}</text>
    <text class="list-right-letter" v-else>{rs.letter}</text>
</view>

單擊方法

letterClick(letter, key) {
    this.data.letter = []//字母陣列
    this.data.letter = base.letter()
    this.data.letter[key].active = true
    for (var i = 0; i < this.data.nameIndex.length; i++) {
        if (letter == this.data.nameIndex[i]) {
            if (letter == '#') {
                $('#list').scrollTo({ 'view': 'other' })
            } else {
                $('#list').scrollTo({ 'view': letter })
            }
        }
    }
},

相關文章