如何将仿iPhone通讯录制作小程序中的自定义选择组件改写为长尾关键词?

2026-04-09 18:511阅读0评论SEO资源
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计2605个文字,预计阅读时间需要11分钟。

如何将仿iPhone通讯录制作小程序中的自定义选择组件改写为长尾关键词?

前言:近期闲暇时光无所事事,想着闲着也是闲着,不如给自己找点事情做!于是,我选择了一个给微信小程序做一个仿iPhone通讯录+效果的定制组件。

首先,整体梳理一下,需要实现的核心功能有:仿iPhone通讯录界面、搜索功能、添加联系人、编辑联系人等。接下来,详细说明一下每个功能的实现方法。

前言

近期闲来无事,想着闲着也是闲着,不如给自己搞点事情做!敢想敢做,于是选择了给微信小程序做个 仿iPhone通讯录 效果的自定义组件。

先来整理一下,瞧瞧需要实现的核心功能。

  1. 按照第一个字的首字母排序;
  2. 实现输入搜索功能;
  3. 侧边栏字母导航;

基本上分为3块:

  1. 顶部的搜索区域;
  2. 内容的展示区域;
  3. 侧边字母导航栏区域;

// index.wxml <view class="main"> <!-- 顶部搜索区域 --> <view class="header"> </view> <!-- 内容区域 --> <scroll-view class="scroll"> </scroll-view> <!-- 侧边导航 --> <view class="sub_nav"> </view> </view>

一目了然就直接贴代码了。

<view class="header"> // 这里或许有人要问,为啥不用小程序的label组件呢。?_? // 原因就是...我就不用,你还能咬我?!^(oo)^ // 哈哈哈哈~开个玩笑,其实是小程序的label组件还没支持input! <view class="label"> <icon></icon> <input type="text" placeholder="搜索" /> </view> </view>

再说一目了然会不会被打呢?:joy:

根据图片就可以看出来,存在2个区域。

  1. 红框包围的外框,负责圈定展示的范围;
  2. 绿框包围的范围,包含有字母标题和对应的子项。

代码如下:

<scroll-view class="scroll"> <view class="dl"> <view class="dt">这里是字母标题。</view> <view class="dd"> <span>这里当然是展示的内容啦。</span> </view> </view> </scroll-view>

为了节省一下文章的篇幅,这里就不贴图了,很简单,就是并排下来就好了。

<view class="sub_nav"> <view class="option">这里是输出字母。</view> </view>

接下来是wxss的样式了。

考虑到wxss的样式较多,我就直接贴 代码链接 吧,有兴趣的童鞋可以瞧瞧。

完成之后,是时候贴个效果图了。(不许吐槽丑,宝宝会不开心的!:pensive:)

结构样式弄完了,也贴一下自定组件的基础文件

// index.json { "component": true }

// index.js Component({ properties: {}, // 组件的对外属性 data: {}, // 组件的内部数据 lifetimes: {}, // 生命周期 methods: {} // 事件 });

现在开始实现功能了!!!

按照第一个字的首字母排序

说实话,实现这块功能呢,我是没啥头绪的,所以这个时候就要求助伟大的“度娘/Google”了。

经过楼主“遍寻网络”,查找到如下页面的源码参考:

因楼主问题,遗忘了该网址,如有知道的童鞋,贴个链接告诉下楼主,楼主立马麻溜的加上。 源码的原理大概描述下:

收录 20902 个汉字和 375 个多音字的 Unicode 编码,然后用JS切割首字母并转换成 Unicode 进行对比,最后返回对应首字母的拼音。

// 汉字对应的Unicode编码文件 // oMultiDiff = 多音字 | firstLetterMap = 汉字 import firstStore from './firstChineseLetter'; // 获取首字母拼音 function getFirstLetter (val) { const firstVal = val.charAt(0); if (/.*[\u4e00-\u9fa5]+.*/.test(firstVal)) { // 处理中文字符 // 转换成Unicode编码,与firstStore里面的数据进行对比,然后返回对应的参数 const code = firstVal.charCodeAt(0); // 转换成Unicode编码 return code in firstStore.oMultiDiff ? firstStore.oMultiDiff[code] : firstStore.firstLetterMap.charAt(code - 19968); } else { // 这里处理非中文 // 检测是否字母,如果是就直接返回大写的字母 // 不是的话,返回“#” return /^[a-zA-Z]+$/.test(firstVal) ? firstVal.toUpperCase() : '#'; } } getFirstLetter('东城区'); // 输出结果:D

firstChineseLetter.js地址

获取首字母的方法有了之后,就该对数据进行处理了。

首先定义一下组件所需要的参数。

Component({ // 组件的对外属性 properties: { data: { type: Array, value: [], }, // 组件外传递进来的数据 attr: { type: String, value: 'label' }, // 需要进行首字母处理的属性,默认是"label" }, ... })

然后,针对组件外传递进来的数据,做一次转换。

// 静态数据的存储 const Static = { list: [] } Component({ ... methods: { // 初始/重置数据 init () { const { data, attr } = this.properties; let changeData = [], // 转换后的数据 inChangeData = {}; // 存储转换后的数据对应字母的索引值 data.map(v => { // 获取首字母拼音 let firstLetter = this.getFirstLetter(v[attr]); // 循环对比检测 firstLetter.split('').map(str => { if (str in inChangeData) { // 有首字母相同的项, // 则添加入已有的项里面 changeData[inChangeData[str]].list.push(v); } else { // 没有首字母相同的项, // 则在尾部追加一条新的数据, // 储存对应的字母值(firstLetter), // 同时存储该字母对应的索引 changeData.push({ firstLetter: str, list: [v] }); inChangeData[str] = changeData.length - 1; } }); }); // 此时转换后的数组属于乱序, // 需要对乱序的数组进行排序 changeData.sort((pre, next) => pre.firstLetter < next.firstLetter ? -1 : 1); // 若存在“#”项,将位置位移至底部 if (changeData[0].firstLetter === '#') { const firstArr = changeData.splice(0, 1); changeData = [...changeData, ...firstArr]; } // 存储转换后的数据, // this.data.list的数据对应页面的展示数据,因为有搜索功能,数据可能会变更, // 在静态的数据里面,也存储1份数据,方便后续的搜索等功能。 this.setData({ list: changeData }); Static.list = changeData; }, } ... });

初始化函数有了之后呢,当然是调用它啦。

Component({ lifetimes: { // 在组件实例进入页面节点树时执行初始化数据 attached () { this.init(); } }, observers: { // 考虑到组件传递的数据存在变更的可能, // 在数据变更的时候,也要做一次初始化 'data, attr, icon' (data, attr) { this.init(); } }, })

接下来是搜索功能啦~

先给页面搜索框加个监听事件(input)

<view class="main"> ... <view class="header"> <view class="label"> <icon></icon> <input type="text" placeholder="搜索" value="{{ search }}" bindinput="searchData" /> </view> </view> ... </view>

接着是JS的事件

const Static = { list: [] } Component({ ... methods: { searchData (e) { const { value } = e.detail; // 用户输入的值 const { list } = Static; // init存储的静态数据,用来做数据对比 const { attr } = this.properties; // 要对比的属性值 let result = [], tem = {}; // 没有搜索内容,返回全部内容 if (value.length === 0) { this.setData({ list: Static.list }); return; } // 检索搜索内容 list.map(v => { // 获取所有跟value匹配上的数据 const searchList = v.list.filter(v => v[attr].indexOf(value) !== -1); if (searchList.length > 0) { // 此处原理类似楼上init的对比,此处不细说, // 反正我懒我有理(0.0) if (v.firstLetter in tem) { const _list = result[tem[v.firstLetter]].lish; result[tem[v.firstLetter]].lish = [..._list, ...searchList]; } else { result.push({ firstLetter: v.firstLetter, list: [...searchList] }); tem[v.firstLetter] = result.length - 1; } } }); // 存储数据 this.setData({ list: result, search: value }); } }, ... });

侧边栏字母导航

(突然觉得,写文好累啊!!!)

写这块的时候呢,楼主发现了iPhone通讯录侧边导航栏有个问题, 手指在字母导航栏上滑动的时候,有时候很难确认自己滑到了哪个区域?!

然鹅这个问题呢,楼主发现了微信的通讯录,针对这块添加了手指滑动的时候,添加了个结构来帮助用户确认目前所处的区域。

楼主本着学习的精神,借(chao)鉴(xi)了这个效果,来个效果图。

贴一下新的wxml结构

<!-- 侧边导航 --> <view class="sub_nav" id="subNav" catchtouchstart="subTouchStart" catchtouchmove="subTouchMove" catchtouchend="subTouchEnd"> <view class="option" wx:for="{{ list }}" data-firstLetter="{{ item.firstLetter }}" wx:key="firstLetter"> {{ item.firstLetter }} <!-- 以下这块就是新增的结构啦 S --> <view class="max {{ item.firstLetter === scrollIntoView && subNavHint ? 'show' : '' }}" data-desc="{{ item.firstLetter }}" ></view> <!-- 以上这块就是新增的结构啦 E --> </view> </view>

const Static = { list: [], timer: null } Component({ ... data: { scrollIntoView: '', // 标记当前处于哪个字母 subNavHint: false, // 控制借(chao)鉴(xi)微信效果的元素 }, methods: { subTouchStart () { this.setData({ subNavHint: true, scrollIntoView: '' }); }, subTouchEnd () { this.setData({ subNavHint: false }); }, subTouchMove (e) { // 获取字母导航栏元素对应的值 const query = this.createSelectorQuery(); query.select('#subNav').boundingClientRect(); query.selectViewport().scrollOffset(); query.exec(res => { const { clientY } = e.touches[0]; // Y轴的位置 const DomTop = res[0].top; // 导航元素距离顶部的位置 const { list } = this.data; // 计算索引, // 或许看到这里有人会疑问,为什么是除以20? // 因为样式里面,我写的高度是20px,所以每个字母的区域是20px。 let index = Math.round((clientY - DomTop) / 20); index = index >= list.length ? list.length - 1 : index; // 限制索引大于0 index = index < 0 ? 0 : index; // 限制索引小于0 // 限制结果重复赋值 if (list[index].firstLetter !== this.data.scrollIntoView) { this.setData({ scrollIntoView: list[index].firstLetter }); // 加个抖动效果 wx.vibrateShort(); } }); } }, } ... });

结语

文章写到这呢,基本上核心的功能都已经实现啦~ :stuck_out_tongue_closed_eyes:(终于写完了...)

通过自己封装组件,楼主还是有挺大收获的!

当然,这个组件还有很多可以继续完善的地方,有兴趣的童鞋呢,可以提出你的优化建议,楼主有时(xing)间(qu)的话,会继续完善下去。

如何将仿iPhone通讯录制作小程序中的自定义选择组件改写为长尾关键词?

最后,还是推一下这个组件啦,希望它能帮到有需要的童鞋。

github地址

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持自由互联。

本文共计2605个文字,预计阅读时间需要11分钟。

如何将仿iPhone通讯录制作小程序中的自定义选择组件改写为长尾关键词?

前言:近期闲暇时光无所事事,想着闲着也是闲着,不如给自己找点事情做!于是,我选择了一个给微信小程序做一个仿iPhone通讯录+效果的定制组件。

首先,整体梳理一下,需要实现的核心功能有:仿iPhone通讯录界面、搜索功能、添加联系人、编辑联系人等。接下来,详细说明一下每个功能的实现方法。

前言

近期闲来无事,想着闲着也是闲着,不如给自己搞点事情做!敢想敢做,于是选择了给微信小程序做个 仿iPhone通讯录 效果的自定义组件。

先来整理一下,瞧瞧需要实现的核心功能。

  1. 按照第一个字的首字母排序;
  2. 实现输入搜索功能;
  3. 侧边栏字母导航;

基本上分为3块:

  1. 顶部的搜索区域;
  2. 内容的展示区域;
  3. 侧边字母导航栏区域;

// index.wxml <view class="main"> <!-- 顶部搜索区域 --> <view class="header"> </view> <!-- 内容区域 --> <scroll-view class="scroll"> </scroll-view> <!-- 侧边导航 --> <view class="sub_nav"> </view> </view>

一目了然就直接贴代码了。

<view class="header"> // 这里或许有人要问,为啥不用小程序的label组件呢。?_? // 原因就是...我就不用,你还能咬我?!^(oo)^ // 哈哈哈哈~开个玩笑,其实是小程序的label组件还没支持input! <view class="label"> <icon></icon> <input type="text" placeholder="搜索" /> </view> </view>

再说一目了然会不会被打呢?:joy:

根据图片就可以看出来,存在2个区域。

  1. 红框包围的外框,负责圈定展示的范围;
  2. 绿框包围的范围,包含有字母标题和对应的子项。

代码如下:

<scroll-view class="scroll"> <view class="dl"> <view class="dt">这里是字母标题。</view> <view class="dd"> <span>这里当然是展示的内容啦。</span> </view> </view> </scroll-view>

为了节省一下文章的篇幅,这里就不贴图了,很简单,就是并排下来就好了。

<view class="sub_nav"> <view class="option">这里是输出字母。</view> </view>

接下来是wxss的样式了。

考虑到wxss的样式较多,我就直接贴 代码链接 吧,有兴趣的童鞋可以瞧瞧。

完成之后,是时候贴个效果图了。(不许吐槽丑,宝宝会不开心的!:pensive:)

结构样式弄完了,也贴一下自定组件的基础文件

// index.json { "component": true }

// index.js Component({ properties: {}, // 组件的对外属性 data: {}, // 组件的内部数据 lifetimes: {}, // 生命周期 methods: {} // 事件 });

现在开始实现功能了!!!

按照第一个字的首字母排序

说实话,实现这块功能呢,我是没啥头绪的,所以这个时候就要求助伟大的“度娘/Google”了。

经过楼主“遍寻网络”,查找到如下页面的源码参考:

因楼主问题,遗忘了该网址,如有知道的童鞋,贴个链接告诉下楼主,楼主立马麻溜的加上。 源码的原理大概描述下:

收录 20902 个汉字和 375 个多音字的 Unicode 编码,然后用JS切割首字母并转换成 Unicode 进行对比,最后返回对应首字母的拼音。

// 汉字对应的Unicode编码文件 // oMultiDiff = 多音字 | firstLetterMap = 汉字 import firstStore from './firstChineseLetter'; // 获取首字母拼音 function getFirstLetter (val) { const firstVal = val.charAt(0); if (/.*[\u4e00-\u9fa5]+.*/.test(firstVal)) { // 处理中文字符 // 转换成Unicode编码,与firstStore里面的数据进行对比,然后返回对应的参数 const code = firstVal.charCodeAt(0); // 转换成Unicode编码 return code in firstStore.oMultiDiff ? firstStore.oMultiDiff[code] : firstStore.firstLetterMap.charAt(code - 19968); } else { // 这里处理非中文 // 检测是否字母,如果是就直接返回大写的字母 // 不是的话,返回“#” return /^[a-zA-Z]+$/.test(firstVal) ? firstVal.toUpperCase() : '#'; } } getFirstLetter('东城区'); // 输出结果:D

firstChineseLetter.js地址

获取首字母的方法有了之后,就该对数据进行处理了。

首先定义一下组件所需要的参数。

Component({ // 组件的对外属性 properties: { data: { type: Array, value: [], }, // 组件外传递进来的数据 attr: { type: String, value: 'label' }, // 需要进行首字母处理的属性,默认是"label" }, ... })

然后,针对组件外传递进来的数据,做一次转换。

// 静态数据的存储 const Static = { list: [] } Component({ ... methods: { // 初始/重置数据 init () { const { data, attr } = this.properties; let changeData = [], // 转换后的数据 inChangeData = {}; // 存储转换后的数据对应字母的索引值 data.map(v => { // 获取首字母拼音 let firstLetter = this.getFirstLetter(v[attr]); // 循环对比检测 firstLetter.split('').map(str => { if (str in inChangeData) { // 有首字母相同的项, // 则添加入已有的项里面 changeData[inChangeData[str]].list.push(v); } else { // 没有首字母相同的项, // 则在尾部追加一条新的数据, // 储存对应的字母值(firstLetter), // 同时存储该字母对应的索引 changeData.push({ firstLetter: str, list: [v] }); inChangeData[str] = changeData.length - 1; } }); }); // 此时转换后的数组属于乱序, // 需要对乱序的数组进行排序 changeData.sort((pre, next) => pre.firstLetter < next.firstLetter ? -1 : 1); // 若存在“#”项,将位置位移至底部 if (changeData[0].firstLetter === '#') { const firstArr = changeData.splice(0, 1); changeData = [...changeData, ...firstArr]; } // 存储转换后的数据, // this.data.list的数据对应页面的展示数据,因为有搜索功能,数据可能会变更, // 在静态的数据里面,也存储1份数据,方便后续的搜索等功能。 this.setData({ list: changeData }); Static.list = changeData; }, } ... });

初始化函数有了之后呢,当然是调用它啦。

Component({ lifetimes: { // 在组件实例进入页面节点树时执行初始化数据 attached () { this.init(); } }, observers: { // 考虑到组件传递的数据存在变更的可能, // 在数据变更的时候,也要做一次初始化 'data, attr, icon' (data, attr) { this.init(); } }, })

接下来是搜索功能啦~

先给页面搜索框加个监听事件(input)

<view class="main"> ... <view class="header"> <view class="label"> <icon></icon> <input type="text" placeholder="搜索" value="{{ search }}" bindinput="searchData" /> </view> </view> ... </view>

接着是JS的事件

const Static = { list: [] } Component({ ... methods: { searchData (e) { const { value } = e.detail; // 用户输入的值 const { list } = Static; // init存储的静态数据,用来做数据对比 const { attr } = this.properties; // 要对比的属性值 let result = [], tem = {}; // 没有搜索内容,返回全部内容 if (value.length === 0) { this.setData({ list: Static.list }); return; } // 检索搜索内容 list.map(v => { // 获取所有跟value匹配上的数据 const searchList = v.list.filter(v => v[attr].indexOf(value) !== -1); if (searchList.length > 0) { // 此处原理类似楼上init的对比,此处不细说, // 反正我懒我有理(0.0) if (v.firstLetter in tem) { const _list = result[tem[v.firstLetter]].lish; result[tem[v.firstLetter]].lish = [..._list, ...searchList]; } else { result.push({ firstLetter: v.firstLetter, list: [...searchList] }); tem[v.firstLetter] = result.length - 1; } } }); // 存储数据 this.setData({ list: result, search: value }); } }, ... });

侧边栏字母导航

(突然觉得,写文好累啊!!!)

写这块的时候呢,楼主发现了iPhone通讯录侧边导航栏有个问题, 手指在字母导航栏上滑动的时候,有时候很难确认自己滑到了哪个区域?!

然鹅这个问题呢,楼主发现了微信的通讯录,针对这块添加了手指滑动的时候,添加了个结构来帮助用户确认目前所处的区域。

楼主本着学习的精神,借(chao)鉴(xi)了这个效果,来个效果图。

贴一下新的wxml结构

<!-- 侧边导航 --> <view class="sub_nav" id="subNav" catchtouchstart="subTouchStart" catchtouchmove="subTouchMove" catchtouchend="subTouchEnd"> <view class="option" wx:for="{{ list }}" data-firstLetter="{{ item.firstLetter }}" wx:key="firstLetter"> {{ item.firstLetter }} <!-- 以下这块就是新增的结构啦 S --> <view class="max {{ item.firstLetter === scrollIntoView && subNavHint ? 'show' : '' }}" data-desc="{{ item.firstLetter }}" ></view> <!-- 以上这块就是新增的结构啦 E --> </view> </view>

const Static = { list: [], timer: null } Component({ ... data: { scrollIntoView: '', // 标记当前处于哪个字母 subNavHint: false, // 控制借(chao)鉴(xi)微信效果的元素 }, methods: { subTouchStart () { this.setData({ subNavHint: true, scrollIntoView: '' }); }, subTouchEnd () { this.setData({ subNavHint: false }); }, subTouchMove (e) { // 获取字母导航栏元素对应的值 const query = this.createSelectorQuery(); query.select('#subNav').boundingClientRect(); query.selectViewport().scrollOffset(); query.exec(res => { const { clientY } = e.touches[0]; // Y轴的位置 const DomTop = res[0].top; // 导航元素距离顶部的位置 const { list } = this.data; // 计算索引, // 或许看到这里有人会疑问,为什么是除以20? // 因为样式里面,我写的高度是20px,所以每个字母的区域是20px。 let index = Math.round((clientY - DomTop) / 20); index = index >= list.length ? list.length - 1 : index; // 限制索引大于0 index = index < 0 ? 0 : index; // 限制索引小于0 // 限制结果重复赋值 if (list[index].firstLetter !== this.data.scrollIntoView) { this.setData({ scrollIntoView: list[index].firstLetter }); // 加个抖动效果 wx.vibrateShort(); } }); } }, } ... });

结语

文章写到这呢,基本上核心的功能都已经实现啦~ :stuck_out_tongue_closed_eyes:(终于写完了...)

通过自己封装组件,楼主还是有挺大收获的!

当然,这个组件还有很多可以继续完善的地方,有兴趣的童鞋呢,可以提出你的优化建议,楼主有时(xing)间(qu)的话,会继续完善下去。

如何将仿iPhone通讯录制作小程序中的自定义选择组件改写为长尾关键词?

最后,还是推一下这个组件啦,希望它能帮到有需要的童鞋。

github地址

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持自由互联。