项目开发中,很多的项目都会涉及到城市的选择。比如房产相关的,社区,商城等等,都需要按照城市去搞业务。当选择城市时候,感觉之前弹个窗选择省市区的方式有点low,都是一个索引页读取出主要城市。像是这样。
![image.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0949a9ffa25a43daa599f0cbd0e6f56f~tplv-k3u1fbpfcp-zoom-in-crop-mark:3024:0:0:0.image?)
正常的索引页应该是有下面的业务的
顶部能搜索筛选出想要切换的城市。
右侧点击相应的字母能话滑动到当前字母相关的城市。
右侧滑动可以放大显示当前选中字母,松开以后滑动到相应位置。。
具体实现
1 静态页面可以自己写一个,当然像我这种菜菜的前端肯定是去拷贝一份了。colorUi扩展里面的索引页,拷贝一份到自己项目中即可。这就得到一个基本结构的索引页了。
这样做的好处是,它已经帮我们写好了滑动,长按滚动的一些效果了。我们要做的只需要填充上数据,做做搜索即可。
2 省市区数据的获取。我使用的是uni-cloud自带的云数据。
在前端,获取数据我们用云函数直接获取到即可。具体代码:
//顶部初始化数据库链接
const db = uniCloud.database()
const collection = db.collection('opendb-city-china')
//获取数据
let city_store = uni.getStorageSync('ind_city');
if (!city_store) {
let res = await collection
.where('type == 1')
.orderBy('first_letter asc')
.limit(500)
.get()
city_store = res.result.data
uni.setStorageSync('ind_city',res.result.data)
}
复制代码
我们将数据存储到缓存中,这样就不用每次都从云端拿取数据。拿取到的数据,是根据首字母进行分组的。要达到 a ... b ... c...根据首字母分组渲染。这就需要把数据进行处理。这就用到了三方库 lodash 的groupBy方法。
import {groupBy} from 'lodash'
this.cityData = groupBy(city_store,'first_letter')
复制代码
这样就得到了根据首字母分组的,主要城市的数据。下面就是把数据填充到页面中。
将页面中的数字部分换成我们拼接好的 cityData 渲染数据即可。
3 最后处理一下搜索的逻辑,有搜索关键词的时候筛选一下数据即可。
if(this.kw.trim() != ""){
city_store = city_store.filter(x=>x.name.indexOf(this.kw) > -1)
}
复制代码
效果
![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e4526bca96e94d7e8e779f724d27fa7e~tplv-k3u1fbpfcp-zoom-in-crop-mark:3024:0:0:0.image?)
完整代码
搜索
{{item.name}}
{{items.name[0]}}
{{items.name}}
{{item.name}}
{{listCur}}
import {groupBy} from 'lodash'
const db = uniCloud.database()
const collection = db.collection('opendb-city-china')
export default {
data() {
return {
StatusBar: this.StatusBar,
CustomBar: this.CustomBar,
hidden: true,
listCurID: '',
list: [],
listCur: '',
cityData:[],
kw:''
};
},
onLoad() {
this.getData()
let list = [{}];
for (let i = 0; i < 26; i++) {
list[i] = {};
list[i].name = String.fromCharCode(65 + i);
}
this.list = list;
this.listCur = list[0];
},
onReady() {
let that = this;
uni.createSelectorQuery().select('.indexBar-box').boundingClientRect(function(res) {
that.boxTop = res.top
}).exec();
uni.createSelectorQuery().select('.indexes').boundingClientRect(function(res) {
that.barTop = res.top
}).exec()
},
methods: {
search(){
this.getData()
},
//查询数据
async getData(){
let city_store = uni.getStorageSync('ind_city');
if (!city_store) {
let res = await collection
.where('type == 1')
.orderBy('first_letter asc')
.limit(500)
.get()
city_store = res.result.data
uni.setStorageSync('ind_city',res.result.data)
}
if(this.kw.trim() != ""){
city_store = city_store.filter(x=>x.name.indexOf(this.kw) > -1)
}
this.cityData = groupBy(city_store,'first_letter')
},
//获取文字信息
getCur(e) {
this.hidden = false;
this.listCur = this.list[e.target.id].name;
},
setCur(e) {
this.hidden = true;
this.listCur = this.listCur
},
//滑动选择Item
tMove(e) {
let y = e.touches[0].clientY,
offsettop = this.boxTop,
that = this;
//判断选择区域,只有在选择区才会生效
if (y > offsettop) {
let num = parseInt((y - offsettop) / 20);
this.listCur = that.list[num].name
};
},
//触发全部开始选择
tStart() {
this.hidden = false
},
//触发结束选择
tEnd() {
this.hidden = true;
this.listCurID = this.listCur
},
indexSelect(e) {
let that = this;
let barHeight = this.barHeight;
let list = this.list;
let scrollY = Math.ceil(list.length * e.detail.y / barHeight);
for (let i = 0; i < list.length; i++) {
if (scrollY < i + 1) {
that.listCur = list[i].name;
that.movableY = i * 20
return false
}
}
}
}
}
page {
padding-top: 100upx;
}
.indexes {
position: relative;
}
.indexBar {
position: fixed;
right: 0px;
bottom: 0px;
padding: 20upx 20upx 20upx 60upx;
display: flex;
align-items: center;
}
.indexBar .indexBar-box {
width: 40upx;
height: auto;
background: #fff;
display: flex;
flex-direction: column;
box-shadow: 0 0 20upx rgba(0, 0, 0, 0.1);
border-radius: 10upx;
}
.indexBar-item {
flex: 1;
width: 40upx;
height: 40upx;
display: flex;
align-items: center;
justify-content: center;
font-size: 24upx;
color: #888;
}
movable-view.indexBar-item {
width: 40upx;
height: 40upx;
z-index: 9;
position: relative;
}
movable-view.indexBar-item::before {
content: "";
display: block;
position: absolute;
left: 0;
top: 10upx;
height: 20upx;
width: 4upx;
background-color: #f37b1d;
}
.indexToast {
position: fixed;
top: 0;
right: 80upx;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
width: 100upx;
height: 100upx;
border-radius: 10upx;
margin: auto;
color: #fff;
line-height: 100upx;
text-align: center;
font-size: 48upx;
}
复制代码
就是这些。
|