首字母排序省市区三级联动
根据公司项目要求, 新人,琢磨几天研究出来 想和大家分享一下, 如果有哪里可以改动增进的地方希望能提取大家意见, 共同成长, 录了个小视频看看效果先:
首先做下准备工作:
1.地址json文件:
地址json文件入口,可免费下载,下载是text文件,改成json就好了~
2.需要使用拼音转化文件, 自己创建一个文件夹并引入到地址页面; 两个js文件分别如下:
拼音资源码下载:
拼音资源码下载,免费下载
拼音转文字js代码
import { pinyin } from '@/pingYin/pingyinConst.js'
export default {
chineseToPinYin: function (l1) {
var l2 = l1.length
var I1 = ''
var reg = new RegExp('[a-zA-Z0-9]')
for (var i = 0; i < l2; i++) {
var val = l1.substr(i, 1)
var name = this.arraySearch(val, pinyin)
if (reg.test(val)) {
I1 += val
} else if (name !== false) {
I1 += name
}
}
I1 = I1.replace(/ /g, '-')
while (I1.indexOf('--') > 0) {
I1 = I1.replace('--', '-')
}
return I1
},
chineseToPinYinFirst: function (l1) {
var l2 = l1.length
var I1 = ''
var reg = new RegExp('[a-zA-Z0-9]')
var szmList = ''
for (var i = 0; i < l2; i++) {
var val = l1.substr(i, 1)
var name = this.arraySearch(val, pinyin)
szmList += name.substring(0,1)
if (reg.test(val)) {
I1 += val
} else if (name !== false) {
I1 += name
}
}
I1 = I1.replace(/ /g, '-')
while (I1.indexOf('--') > 0) {
I1 = I1.replace('--', '-')
}
// 只返回首字母
return szmList.toString()
},
arraySearch: function (l1) {
for (var name in pinyin) {
if (pinyin[name].indexOf(l1) !== -1) {
return this.ucfirst(name)
}
}
return false
},
ucfirst: function (l1) {
if (l1.length > 0) {
var first = l1.substr(0, 1).toUpperCase()
var spare = l1.substr(1, l1.length)
return first + spare
}
}
}
将上面代码放入新建js文件中; 最后在需要使用的地方使用:
import PinyinUtils from '@/pingYin/pingyinUtils.js'; //此处为上面自己创建js文件的代码路径
//使用方法
PinyinUtils.chineseToPinYinFirst('我').substr(0, 1); //获取首字母
// 则打印出来结果就为 'W', 此处转化为转化为大写, 如需转化小写 可以去百度一下, 当然我这也是百度的~~
HTML代码
<template>
<view class="city">
<!-- 选择地区 -->
<view class="city-title">
<text v-for="(item, index) in navTitle" :key="index" class="city-title-item" @click="jumpAddress(item, index)">{{item}}</text>
<text v-if="navTitle.length < 2" class="city-title-item">{{twoNavTitle}}</text>
</view>
<!-- 当前所在定位 -->
<view class="city-position">
<view class="city-position-l">
<text class="city-position-text">当前所在地区</text>
<view class="city-position-item">
<text class="city-position-item-text">龙华区</text>
</view>
</view>
<view class="city-position-r">
<image class="city-position-r-img" src="/static/index/resultPosition.png"></image>
<text class="city-position-r-text">重新定位</text>
</view>
</view>
<!-- 地址列表 -->
<view class="city-list">
<text class="city-list-title">已开通地区</text>
<view class="city-group">
<!-- 左侧dizhilan -->
<view class="city-group-l">
<!-- 字母排序 -->
<view class="city-group-l-item" v-for="(item, index) in addressList" :key="index" scroll-y>
<text class="city-group-l-item-title" :id="item.title">{{item.title}}</text>
<view class="city-group-l-item-address">
<!-- 地区列表 -->
<view class="city-group-l-item-address-item" v-for="(value, key) in item.data" :key="key" @click="twoList(value.name, index ,key)">
<text class="city-group-l-item-address-title">{{value.name}}</text>
<image class="city-group-l-item-address-item-img" :src=" value.checked ? '/static/index/Hook.png' : ''"></image>
</view>
</view>
</view>
</view>
<!-- 右侧a-z -->
<view class="city-group-r" :style="{height: `${addressList.length*68}rpx`}">
<text v-for="(item, index) in addressList" :key="index" class="city-group-r-text" @click="pageScrool(item)">{{item.title}}</text>
</view>
</view>
</view>
</view>
</template>
JS代码
<script>
import city_list from '@/static/cityDataArr.json';
import PinyinUtils from '@/pingYin/pingyinUtils.js';
export default {
data() {
return {
navTitle: [], // 头部名称
twoNavTitle: '', // 二级页面名称, 由于底部用索引判断, 临时放数据渲染页面
// aTob: ['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'],
addressData: [], // 用来排序的地址name
addressList: [], //全部数据加字母
newDataList: [], // 备份全部数据 用来传递给下一级
addressIndex: null,
}
},
mounted() {
// 初始从json 中拿数据
city_list.data.forEach(item => {
this.addressData.push(item.name);
this.newDataList.push(item);
})
this.addData();
},
methods:{
// 封装添加数据操作
addData() {
if(this.navTitle.length <= 2){
// 先将数据进行排序
var resultArray = this.addressData.sort(
function compareFunction(param1, param2) {
return param1.localeCompare(param2,"zh");
}
);
// console.log(resultArray)
// 循环改变原本变量的顺序
for(var a = 0; a<this.addressData.length; a++) {
for(var n = 0; n < this.newDataList.length; n++) {
if(this.addressData[a] === this.newDataList[n].name) {
// 存储交换值
let temp = this.newDataList[a]; //先将第一个值存储起来
this.newDataList[a] = this.newDataList[n]; //将当前值和第一个值交换
this.newDataList[n] = temp; //将第一个值赋给当前值
}
}
}
// console.log(this.addressData, this.newDataList)
// 循环数据 添加首字母
let letter = []; // 存在的字母数组
resultArray.forEach((item, index) => {
let getFirst = PinyinUtils.chineseToPinYinFirst(item).substr(0, 1); //获取首字母
// console.log(getFirst)
if(letter.indexOf(getFirst) === -1) {
letter.push(getFirst)
}
})
// console.log(letter);
// 给对象添加首字母
for(var j = 0; j<letter.length; j++) {
this.addressList.push({title: letter[j], data: []})
}
// console.log(this.addressList)
// 循环给对应字母添加数据
for(var i = 0; i<letter.length; i++) {
resultArray.forEach((item, index) => {
let getFirst = PinyinUtils.chineseToPinYinFirst(item).substr(0, 1);
// 判断首字母并添加至数据列表中
if(letter[i] === getFirst) {
this.addressList[i].data.push({name: item, checked: false})
}
});
}
return this.newDataList;
} else {
return '';
}
},
// 进入二级菜单
twoList(value, index ,key) {
this.addressList[index].data[key].checked = true;
// 判断头部导航状态是否是第三级
if(this.navTitle.length < 2){
// 筛选特别行政区
if(value === '澳门特别行政区' || value === '香港特别行政区') {
if(this.navTitle[0]) {
this.navTitle[0] = value
} else {
this.navTitle.push(value)
}
// 因为上面已经判断行政区, 所以直接判断是否等于value, 进行改变checked
this.addressList.forEach(item => {
item.data.forEach(dataVal => {
if(dataVal.name === value) {
dataVal.checked = true
} else {
dataVal.checked = false
}
})
});
this.$Router.push({
path: '/mainPack/shop/address/address',
query: {
data: this.navTitle
}
});
} else {
// 判断有无子集
if(this.addData()[0].citys || this.addData()[0].areas) {
// console.log(1)
let flagNum = 0;
this.addressData = []; //清空数组并重新加载方法
this.addressList = [];
// 循环判断是否有子集 有的话则继续点击操作
this.addData().forEach((item, index) => {
if(item.citys){
flagNum++;
// 对头部显示栏进行操作 判断是否已经存在
if(item.name === value) {
// console.log(this.navTitle)
if(this.navTitle[0]) {
if(this.navTitle[0] !== value) {
this.navTitle[0] = value
}
} else {
this.navTitle.push(value)
}
// 备份地址数据
this.newDataList = item.citys;
// console.log(item, index);
// 循环地址, 并给地址列表中添加数据
item.citys.forEach(key => {
// console.log('key',key)
if(this.addressData.indexOf(key.name) === -1) {
this.addressData.push(key.name);
// console.log(this.addressData)
}
});
} else {
return;
}
} else if(item.areas.length !== 0) { //判断是否有区县, 没有则直接跳转至地图页面
flagNum++;
// console.log(item);
if(item.name === value){
if(this.navTitle[1]) {
} else {
this.navTitle.push(value)
}
this.newDataList = item.areas;
// 循环地址, 并给地址列表中添加数据
item.areas.forEach(key => {
// 判断添加城市中有无
if(this.addressData.indexOf(key.name) === -1) {
this.addressData.push(key.name);
}
});
}
} else {
this.$Router.push({
path: '/mainPack/shop/address/address',
query: {
data: this.navTitle
}
})
}
});
if(flagNum > 0) {
this.addData();
}
} else {
this.addData().forEach(item => {
if(item.name === value) {
this.navTitle[2] = value;
}
});
}
}
} else {
// 判断点击添加头部并跳转页面
if(this.navTitle.length === 2) {
this.navTitle.push(value)
this.$Router.push({
path: '/mainPack/shop/address/address',
query: {
data: this.navTitle
}
})
} else {
this.navTitle.pop();
this.navTitle.push(value)
this.$Router.push({
path: '/mainPack/shop/address/address',
query: {
data: this.navTitle
}
})
}
}
},
// 点击名称索引切换地址
jumpAddress(value, key) {
// 点击一级索引
if(key === 0) {
this.addressData = []; //清空数组并重新加载方法
this.addressList = [];
this.newDataList = [];
this.navTitle = [];
this.navTitle.push(value);
this.twoNavTitle = '';
// 初始从json 中拿数据
city_list.data.forEach(item => {
this.addressData.push(item.name);
this.newDataList.push(item);
});
this.addData();
// 循环数据, 通过名称查找并改变checked的值
this.addressList.forEach(item => {
item.data.forEach(dataVal => {
if(dataVal.name === value) {
dataVal.checked = true
} else {
dataVal.checked = false
}
})
});
} else if(key === 1) { // 点击二级索引
// 取出第一级省
let province = this.navTitle[0];
city_list.data.forEach((item, index) => {
if(item.name === province) {
this.addressData = []; //清空数组并重新加载方法
this.addressList = [];
this.newDataList = [];
this.navTitle = [];
this.navTitle.push(province); //添加省
this.twoNavTitle = value; //添加市
this.newDataList = item.citys;
item.citys.forEach(key => {
if(this.addressData.indexOf(key.name) === -1) {
this.addressData.push(key.name);
}
});
this.addData();
// 循环数据, 通过名称查找并改变checked的值
this.addressList.forEach(item => {
item.data.forEach(dataVal => {
if(dataVal.name === value) {
dataVal.checked = true
} else {
dataVal.checked = false
}
})
});
}
})
}
},
// 跳转到对应索引
pageScrool(item) {
uni.pageScrollTo({
scrollTop: 0,
duration: 0
})
const query = uni.createSelectorQuery().in(this);
query.select('#' + item.title).boundingClientRect(res => {
console.log( res)
uni.pageScrollTo({
scrollTop: res.top - 15,
duration: 300
})
}).exec();
}
}
}
</script>
此处注意引入路径 以及跳转路径
CSS代码
<style lang="scss" scoped>
.city{
width: 100%;
display: flex;
flex-direction: column;
// 所选地区
.city-title{
width: 100%;
display: flex;
align-items: center;
padding: 30rpx 30rpx 0;
border-bottom: 1px solid #EEEEEE;
.city-title-item{
color: #70B370;
padding-bottom: 14rpx;
font-size: 32rpx;
border-bottom: 4rpx solid #70B370;
margin-right: 30rpx;
}
}
// 当前所在地区定位
.city-position{
padding: 34rpx 30rpx;
display: flex;
justify-content: space-between;
align-items: center;
.city-position-text{
color: #A5A5A5;
font-size: 28rpx;
}
// 左侧地址栏
.city-position-l{
display: flex;
align-items: center;
.city-position-item{
padding: 10rpx 30rpx;
border-radius: 36rpx;
background-color: #F2F2F2;
margin-left: 12rpx;
.city-position-item-text{
color: #292929;
font-size: 32rpx;
}
}
}
// 右侧重新定位
.city-position-r{
display: flex;
align-items: center;
.city-position-r-img{
width: 28rpx;
height: 28rpx;
}
.city-position-r-text{
font-size: 32rpx;
color: #70B370;
padding-left: 4rpx;
}
}
}
// 地址列表
.city-list{
display: flex;
flex-direction: column;
padding: 0 30rpx 30rpx;
.city-list-title{
font-size: 28rpx;
color: #A5A5A5;
}
.city-group{
display: flex;
justify-content: space-between;
// 左侧地区列表
.city-group-l{
display: flex;
flex-direction: column;
margin-top: 40rpx;
.city-group-l-item{
display: flex;
.city-group-l-item-title{
color: #8A8A8A;
font-size: 32rpx;
line-height: 48rpx;
}
// 地址列表
.city-group-l-item-address{
flex: 1;
padding-left: 58rpx;
display: flex;
flex-direction: column;
.city-group-l-item-address-item{
display: flex;
align-items: flex-start;
.city-group-l-item-address-title{
padding-bottom: 62rpx;
color: #292929;
font-size: 32rpx;
}
.city-group-l-item-address-item-img{
width: 32rpx;
height: 24rpx;
margin-left: 16rpx;
margin-top: 12rpx;
// display: none;
}
}
}
}
}
// 右侧a-z
.city-group-r{
padding: 0 16rpx;
margin-top: 76rpx;
display: flex;
flex-direction: column;
align-items: center;
border-radius: 26rpx;
// background-color: #EBEBEB;
max-height: 960rpx;
position: fixed;
right: 16rpx;
top: 300rpx;
.city-group-r-text{
padding: 18rpx 0;
color: #6D6D6D;
text-align: center;
font-size: 28rpx;
}
}
}
}
}
</style>
总结
说一下我个人整体思路:
1.首先进入之后显示省所以直接拿城市json文件里面内容, 循环并添加数据,不过这里需要是需要首字母进行排序, 所以多了一步
var resultArray = this.addressData.sort(
function compareFunction(param1, param2) {
return param1.localeCompare(param2,"zh");
}
);
此段代码是将筛选出地址名称的addressData数组进行排序; 并形成新数组;
2.虽然改变了值,但是地址数据列表里面是不会变的,且在下一级用到data字段,(就好比: 广东省->深圳市……,深圳市-> 龙华区……等等),所以一层叠一层,我就想到了使用新的字段newDataList当做备份(且这个备份是在和首字母排序之后名称同步顺序的情况下所以有了以下代码:
// 循环改变原本变量的顺序
for(var a = 0; a<this.addressData.length; a++) {
for(var n = 0; n < this.newDataList.length; n++) {
if(this.addressData[a] === this.newDataList[n].name) {
// 存储交换值
let temp = this.newDataList[a]; //先将第一个值存储起来
this.newDataList[a] = this.newDataList[n]; //将当前值和第一个值交换
this.newDataList[n] = temp; //将第一个值赋给当前值
}
}
}
将传递到下一层的数据结构和排序的数据结构搭建成一样的就可以不用考虑点击湖北省跳转的是广东省的顺序的问题了),在添加数据函数执行完毕之后return出去,这样在市或者区的时候就可以拿到省或者市的数据,在根据子集是citys还是areas辨别市或者区,依次内推;
好了,到此此次分享结束了,小白许多代码有很多不完善的地方,忘各为大佬指出,代码有点凌乱,不过结构效果还是挺不错的 亲测有用.纵享丝滑~