??最近项目开发中有个需求是做形如手机通讯录的字母导航,如下图示例那种。
??最近是用智能小程序开发的项目,所以就贴一下智能小程序实现的代码,大家可以根据自己的需要更改样式或者逻辑。目前自测了是没有问题的。
??首先是一个函数,这个函数是实现,把中文开头的拼音的首字母提出,比如上面图片里的S;把英文开头的首字母提出;把数字等归为"#"。 ??这里我当时卡壳了半天,思维有所受限,还是带我的导师分分钟搞定的,哈哈,夸张了一下。
pySegSort(arr, dataIndex) {
if (!String.prototype.localeCompare) return null;
let letters = '#ABCDEFGHJKLMNOPQRSTWXYZ'.split('');
let zh = '阿八嚓哒妸发旮哈讥咔垃痳拏噢妑七呥扨它穵夕丫帀'.split('');
let segs = [];
let zh_arr = [];
let en_arr = [];
arr.forEach((item) => {
let name = dataIndex ? item[dataIndex] : item;
if (/[a-zA-Z]/.test(name.charAt(0))) {
en_arr.push(item);
} else {
zh_arr.push(item);
}
})
letters.forEach((letter, i) => {
let curr = { letter, data: [] };
zh_arr.forEach(item => {
let name = dataIndex ? item[dataIndex] : item;
if ((!zh[i - 1] || zh[i - 1].localeCompare(name) <= 0) && name.localeCompare(zh[i]) === -1) {
curr.data.push(item);
}
});
if (curr.data.length) {
segs.push(curr);
curr.data.sort((a, b) => (dataIndex ? a[dataIndex].localeCompare(b[dataIndex]) : a.localeCompare(b)));
}
});
en_arr.forEach((item) => {
let name = dataIndex ? item[dataIndex] : item;
let en = name.charAt(0).toUpperCase();
let index = segs.findIndex(({ letter }) => letter === en)
if (index > -1) {
segs[index].data.push(item);
} else {
segs.push({
letter: en, data: [item]
})
}
})
return segs.sort((a, b) => a.letter < b.letter ? -1 : 1);
},
??然后我mock了一堆假数据,复制到 js 里又改了改,并传入函数中执行,如下是我的假数据和函数执行后打印出来的结果。
let arr = [
{
"name": "12345678989"
},
{
"name": "hhh"
},
{
"name": "地这究图选由"
},
{
"name": "42245678989"
},
{
"name": "支高动经可接"
},
{
"name": "www"
},
{
"name": "革百看回压采"
},
{
"name": "然场干总每起"
},
{
"name": "d切"
},
{
"name": "知再议速看青"
},
{
"name": "哈哈"
},
{
"name": "术着白治志接"
},
{
"name": "斗用重从水品"
},
{
"name": "入治说约开图"
},
{
"name": "道与需走应任"
},
{
"name": "程中标以县林"
},
{
"name": "器者红北门正"
},
{
"name": "准果素造展内"
},
{
"name": "区线志反从说"
},
{
"name": "见题通般行越"
},
{
"name": "间此代之委准"
},
{
"name": "用何极段织林"
},
{
"name": "阶都实快极共"
},
{
"name": "都京米应今下"
},
{
"name": "开车也观车算"
},
{
"name": "已入金本无划"
},
{
"name": "压取它应便关"
},
{
"name": "当无农快品导"
},
{
"name": "起公维业等民"
},
{
"name": "命素府利华新"
},
{
"name": "教想适治器该"
},
{
"name": "查需国价政增"
},
{
"name": "管起来西毛般"
},
{
"name": "广资接最更千"
},
{
"name": "来命确两界地"
},
{
"name": "政团铁层状很"
},
{
"name": "长西信两效加"
},
{
"name": "般则就级好示"
},
{
"name": "方际出产党满"
},
{
"name": "思革青许龙所"
},
{
"name": "从加月引成支"
},
{
"name": "立此验解包众"
},
{
"name": "受器整广速物"
},
{
"name": "已记却界六明"
},
{
"name": "么所指往通情"
},
{
"name": "型界新习此满"
},
{
"name": "争算原究感府"
},
{
"name": "求米当标得任"
},
{
"name": "西响所酸即和"
},
{
"name": "最具展小量目"
},
{
"name": "类再复际方也"
},
{
"name": "到手了议常入"
}
];
console.log(this.pySegSort(arr, "name"));
??然后可以把数据更新到data 里的list 里,再根据数据格式去.swan 文件渲染数据,这里我是用了智能小程序的scroll-view 组件来实现滑动和字母定位,先粘一下代码:
<view class="container">
<scroll-view
class="scroll-view"
scroll-y
scroll-top="{= scrollTop =}">
<block s-for="item,index in list">
<view class="letter">{{item.letter}}</view>
<view id="{{item.letter}}" s-for="c,i in item.data">
<view class="part">{{c.name}}</view>
</view>
</block>
</scroll-view>
<view class="navigation">
<text s-for="item,index in letters" bindtap="scrollToTop" data-id="{{item}}">{{item}}</text>
</view>
</view>
??其中有一个scrollToTop 点击事件,可以通过这个事件更改scrollTop 中的值,进而达到点击右侧字母时,左侧内容定位到同样字母开头的位置,如下:
scrollToTop(e) {
let arr = [...this.data.list];
let l = [];
let count = 0;
let len = 0;
for (let i = 0; i < arr.length; i++) {
l.push(arr[i].letter);
if (arr[i].letter === "#" && e.target.dataset.id === "#") {
count = len = 0;
} else if ((arr[i].letter < e.target.dataset.id) || (arr[i].letter === "#" && e.target.dataset.id !== "#")) {
count += arr[i].data.length;
len++;
} else {
break;
}
}
if (l.indexOf(e.target.dataset.id) !== -1) {
this.setData({
scrollTop: count * 50.5 + 37.5 * len
})
}
},
??这样就ok了,下面粘贴一下效果图:
??css样式可以根据自己需要去写哈。比如字母那加个粘性定位啥的。
??js文件的详细代码如下,在实际项目开发,list数据可以调用后端接口获取一下再用this.setData 更新。
Page({
data: {
list: [],
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'],
scrollTop: 0,
},
pySegSort(arr, dataIndex) {
if (!String.prototype.localeCompare) return null;
let letters = '#ABCDEFGHJKLMNOPQRSTWXYZ'.split('');
let zh = '阿八嚓哒妸发旮哈讥咔垃痳拏噢妑七呥扨它穵夕丫帀'.split('');
let segs = [];
let zh_arr = [];
let en_arr = [];
arr.forEach((item) => {
let name = dataIndex ? item[dataIndex] : item;
if (/[a-zA-Z]/.test(name.charAt(0))) {
en_arr.push(item);
} else {
zh_arr.push(item);
}
})
letters.forEach((letter, i) => {
let curr = { letter, data: [] };
zh_arr.forEach(item => {
let name = dataIndex ? item[dataIndex] : item;
if ((!zh[i - 1] || zh[i - 1].localeCompare(name) <= 0) && name.localeCompare(zh[i]) === -1) {
curr.data.push(item);
}
});
if (curr.data.length) {
segs.push(curr);
curr.data.sort((a, b) => (dataIndex ? a[dataIndex].localeCompare(b[dataIndex]) : a.localeCompare(b)));
}
});
en_arr.forEach((item) => {
let name = dataIndex ? item[dataIndex] : item;
let en = name.charAt(0).toUpperCase();
let index = segs.findIndex(({ letter }) => letter === en)
if (index > -1) {
segs[index].data.push(item);
} else {
segs.push({
letter: en, data: [item]
})
}
})
return segs.sort((a, b) => a.letter < b.letter ? -1 : 1);
},
scrollToTop(e) {
let arr = [...this.data.list];
let l = [];
let count = 0;
let len = 0;
for (let i = 0; i < arr.length; i++) {
l.push(arr[i].letter);
if (arr[i].letter === "#" && e.target.dataset.id === "#") {
count = len = 0;
} else if ((arr[i].letter < e.target.dataset.id) || (arr[i].letter === "#" && e.target.dataset.id !== "#")) {
count += arr[i].data.length;
len++;
} else {
break;
}
}
if (l.indexOf(e.target.dataset.id) !== -1) {
this.setData({
scrollTop: count * 50.5 + 37.5 * len
})
}
},
onLoad: function (options) {
let arr = [
{
"name": "12345678989"
},
{
"name": "hhh"
},
{
"name": "地这究图选由"
},
{
"name": "42245678989"
},
{
"name": "支高动经可接"
},
{
"name": "www"
},
{
"name": "革百看回压采"
},
{
"name": "然场干总每起"
},
{
"name": "d切"
},
{
"name": "知再议速看青"
},
{
"name": "哈哈"
},
{
"name": "术着白治志接"
},
{
"name": "斗用重从水品"
},
{
"name": "入治说约开图"
},
{
"name": "道与需走应任"
},
{
"name": "程中标以县林"
},
{
"name": "器者红北门正"
},
{
"name": "准果素造展内"
},
{
"name": "区线志反从说"
},
{
"name": "见题通般行越"
},
{
"name": "间此代之委准"
},
{
"name": "用何极段织林"
},
{
"name": "阶都实快极共"
},
{
"name": "都京米应今下"
},
{
"name": "开车也观车算"
},
{
"name": "已入金本无划"
},
{
"name": "压取它应便关"
},
{
"name": "当无农快品导"
},
{
"name": "起公维业等民"
},
{
"name": "命素府利华新"
},
{
"name": "教想适治器该"
},
{
"name": "查需国价政增"
},
{
"name": "管起来西毛般"
},
{
"name": "广资接最更千"
},
{
"name": "来命确两界地"
},
{
"name": "政团铁层状很"
},
{
"name": "长西信两效加"
},
{
"name": "般则就级好示"
},
{
"name": "方际出产党满"
},
{
"name": "思革青许龙所"
},
{
"name": "从加月引成支"
},
{
"name": "立此验解包众"
},
{
"name": "受器整广速物"
},
{
"name": "已记却界六明"
},
{
"name": "么所指往通情"
},
{
"name": "型界新习此满"
},
{
"name": "争算原究感府"
},
{
"name": "求米当标得任"
},
{
"name": "西响所酸即和"
},
{
"name": "最具展小量目"
},
{
"name": "类再复际方也"
},
{
"name": "到手了议常入"
}
];
this.setData({
list: this.pySegSort(arr, "name"),
})
}
});
|