一、效果展示
二、项目结构图
三、数据结构图
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 }) } } } },