<template>
|
<view class="chose-city">
|
<!-- #ifdef APP-PLUS -->
|
<header-bar></header-bar>
|
<!-- #endif -->
|
<!-- 城市搜索 -->
|
<view class="city-search-wrap" :style="topBarHeight() == 0 ? '': 'padding-top:'+topBarTop()+'px'" id="searchBox">
|
<view class="search">
|
<view class="r-cancel" @click="closeModal">
|
<text class="iconfont icon-guanbi"></text>
|
</view>
|
<view class="l-search">
|
<view class="icon-search">
|
<view class="cuIcon-search"></view>
|
</view>
|
<input class="input-search" type="text" :value="inputValue" placeholder="城市名/拼音" placeholder-style="color:#8E8F97"
|
:focus="searchFocus" @input="searchChange" />
|
<text class="clear-input iconfont icon-icon-test" v-if="isClearBtn" @click="inputValue = ''"></text>
|
</view>
|
</view>
|
<!-- 搜索列表 -->
|
<view class="reach-content" v-show="inputValue">
|
<block v-show="searchData.length">
|
<view class="li" v-for="item in searchData" :key="item.citycode" @click="selectCity(item)">
|
{{item.name}}
|
</view>
|
</block>
|
<view class="has-no-data" v-show="hasNoData">没有找到匹配数据~</view>
|
</view>
|
</view>
|
<!-- 城市列表 -->
|
<scroll-view class="scroll-view" scroll-y scroll-with-animation="true" enable-back-to-top="true" :scroll-into-view="toIndex"
|
@scroll="scrollHandle" :style="'height:'+scrollviewHigh+'px;'" v-if="!inputValue">
|
<view class="block">
|
<!-- 您所在的地区 -->
|
<view class="area list-item myarea" id="area">
|
<view class="title-wrapp p-0-30 d-s-c">
|
<view class="c-title">
|
<text class="l flex-1">当前:{{myCityObj.name}}{{myCityObj.city==myCityObj.name?'全城':myCityObj.city}}</text>
|
<view class="d-s-c" @click="showArea = !showArea" :class="{'area-icon' : showArea}">
|
<text>切换区县</text>
|
<view class="iconfont icon-icon1"></view>
|
</view>
|
</view>
|
</view>
|
<view class="ul bg-white p-0-30 pt10" v-if="showArea">
|
<view class="li font-clamp" @click="selectCity(realCityObj)">{{realCityObj.name}}全城</view>
|
<view class="li font-clamp" :class="{'now' : item.name==myCityObj.city}" v-for="(item, key) of regionList" :key="key" :id="item.id" @click="selectCity(item)">
|
<text class="text">{{ item.name }}</text>
|
</view>
|
</view>
|
</view>
|
<!-- 历史记录 -->
|
<view class="area list-item history p-0-30" id="record">
|
<view class="title-wrapp pt20">
|
<view class="c-title">
|
<text class="l">定位/最近访问</text>
|
</view>
|
</view>
|
<view class="ul">
|
<view class="li font-clamp" @click="selectCity(nowCityObj, 'refresh')">
|
<text class="f26 dominant icon iconfont icon-dizhi1" v-if="hasLocation"></text>
|
<text class="f26 red icon iconfont icon-fasong" v-else></text>
|
<text class="text">{{ hasLocation ? nowCityObj.name:'定位失败,点击重新定位' }}</text>
|
</view>
|
<view class="li font-clamp" v-for="item in recordList" :key="item.id" @click="selectCity(item)">
|
{{ item.name }}
|
</view>
|
</view>
|
</view>
|
</view>
|
<!-- 城市列表 -->
|
<view class="city-list">
|
<view class="list list-item" v-for="(item, key) of cityList" :key="key" :id="item.nameType">
|
<view class="c-title">{{ item.nameType }}</view>
|
<view class="item" v-for="innerItem in item.list" :key="innerItem.id" @click="selectCity(innerItem)">
|
{{ innerItem.name }}
|
</view>
|
</view>
|
</view>
|
</scroll-view>
|
|
<!-- 字母列表 -->
|
<view class="alphabet" @touchstart="touchStart" @touchend="touchEnd" @touchmove.stop="touchMove">
|
<view v-for="(item, index) in alphabet" :key="index" @touchstart="getLetter" @touchend="setLetter" :id="item">
|
<view class="item" :class="{ active: currentLetter == item }">
|
{{ item == 'area' ? '当前' : item == 'record' ? '定位' : item }}
|
</view>
|
</view>
|
</view>
|
</view>
|
</template>
|
|
<script>
|
// import cityJson from '@/static/dataJson/city.json'
|
export default {
|
data() {
|
return {
|
isIPX: null,
|
regionId: null, // 区域ID
|
isToggle: true,
|
isReach: false,
|
inputValue: '',
|
searchData: [], // 搜索的数据
|
isClearBtn: false,
|
toIndex: '', // 跳转的索引的字母
|
tipsLetter: '', // 滑动显示字母
|
timer: null,
|
hasNoData: false,
|
searchFocus: false,
|
letterDetails: [],
|
currentLetter: 'area', //默认选择
|
cityArr: [],
|
recordList: [],
|
cityList: [],
|
hasLocation: true,
|
alphabet:[],
|
refresh: false,
|
// 是否显示区县
|
showArea: false,
|
// 区县列表
|
regionList: [],
|
// 当前存储的二级城市
|
realCityObj: [],
|
/*手机高度*/
|
phoneHeight: 0,
|
/*可滚动视图区域高度*/
|
scrollviewHigh: 0,
|
};
|
},
|
props: ['myCityObj', 'nowCityObj', 'citydata'],
|
mounted() {
|
this.cityArr = this.citydata.cityList
|
if (this.cityArr && this.cityArr[0]) {
|
this.cityArr.map(v => {
|
v.nameType = v.pinyin.substr(0, 1).toUpperCase()
|
})
|
this.cityList = this.groupArr(this.cityArr, 'nameType')
|
}
|
this.recordList = uni.getStorageSync('recordList') || [];
|
this.alphabet = this.citydata.alphabet
|
this.getLocation()
|
this.realCityObj = uni.getStorageSync('realCityObj');
|
this.regionList = uni.getStorageSync('regionObj');
|
this.init();
|
},
|
watch: {
|
// 城市搜索输入框
|
inputValue(newVal) {
|
this.isClearBtn = newVal ? true : false;
|
|
if (this.timer) {
|
clearTimeout(this.timer);
|
}
|
|
if (!this.inputValue) {
|
this.searchData = [];
|
return;
|
}
|
this.timer = setTimeout(() => {
|
const result = [];
|
this.cityList.map(v => {
|
v.list.forEach((item) => {
|
if (/^[a-zA-Z]+$/.test(item.pinyin) && item.pinyin.toLowerCase().includes(this.inputValue.toLowerCase()) ||
|
item.name.includes(this.inputValue)) {
|
result.push(item);
|
}
|
});
|
})
|
this.searchData = result;
|
if (this.searchData.length === 0) {
|
this.hasNoData = true;
|
} else {
|
this.hasNoData = false;
|
}
|
}, 500);
|
},
|
isReach(val) {
|
this.searchFocus = val;
|
},
|
},
|
methods: {
|
/*初始化*/
|
init() {
|
let _this = this;
|
uni.getSystemInfo({
|
success(res) {
|
_this.phoneHeight = res.windowHeight;
|
// 计算组件的高度
|
let h = _this.phoneHeight - 60;
|
_this.scrollviewHigh = h;
|
let view = uni.createSelectorQuery().in(this).select('#searchBox');
|
view.boundingClientRect(data => {
|
|
|
}).exec();
|
|
}
|
});
|
},
|
getLocation() {
|
const that = this
|
uni.getLocation({
|
// #ifdef MP-ALIPAY
|
type: 'wgs84',
|
// #endif
|
success(res) {
|
console.log('---',res)
|
that.hasLocation = true
|
that.$emit('selectCityByLatLng', res, that.refresh)
|
},
|
fail(err) {
|
that.hasLocation = false
|
uni.showToast({
|
icon:'none',
|
title: '获取定位失败,请手动选择或重试'
|
})
|
},
|
})
|
},
|
groupArr(list, field) {
|
var fieldList = [],
|
att = [];
|
list.map((e) => {
|
fieldList.push(e[field])
|
})
|
//数组去重
|
fieldList = fieldList.filter((e, i, self) => {
|
return self.indexOf(e) == i
|
})
|
for (var j = 0; j < fieldList.length; j++) {
|
//过滤出匹配到的数据
|
var arr = list.filter((e) => {
|
return e[field] == fieldList[j];
|
})
|
att.push({
|
nameType: arr[0].nameType,
|
list: arr
|
})
|
}
|
return att;
|
},
|
selectCity(item, type) {
|
// if(type === 'refresh' && !this.hasLocation){
|
// this.refresh = true;
|
// // 获取定位
|
// return this.getLocation()
|
// }
|
// 如果是选择区县
|
if (item.level == 2) {
|
this.regionList = item.regions
|
this.realCityObj = item;
|
uni.setStorageSync('realCityObj', item);
|
uni.setStorageSync('regionObj', this.regionList);
|
}
|
// 保存历史
|
if (uni.getStorageSync('myCityObj')) {
|
this.setRecord(uni.getStorageSync('myCityObj'));
|
}
|
uni.setStorageSync('myCityObj', item)
|
this.$emit('selectCity', item)
|
// 当前项目是需要选择到区域,所以选择城市后回到区县的地方
|
this.toIndex = 'area';
|
setTimeout(() => {
|
this.toIndex = '';
|
}, 1000);
|
},
|
setRecord(item) {
|
let recordList = this.recordList;
|
console.log(recordList);
|
if (recordList.length > 0) {
|
for (let i = 0; i < recordList.length; i++) {
|
if (recordList[i].name == item.name) {
|
return;
|
}
|
}
|
}
|
recordList.unshift(item);
|
uni.setStorageSync('recordList', recordList.slice(0, 2)); // 只存两个历史
|
},
|
closeModal(){
|
this.$emit('closeModal')
|
},
|
//列表滚动,和右边字母表对应
|
scrollHandle(e) {
|
let view = uni.createSelectorQuery().in(this).selectAll('.list-item');
|
view
|
.boundingClientRect((d) => {
|
let top = d[0].top;
|
d.forEach((item) => {
|
item.top = item.top - top;
|
item.bottom = item.bottom - top;
|
this.letterDetails.push({
|
id: item.id,
|
top: item.top,
|
bottom: item.bottom,
|
});
|
});
|
})
|
.exec();
|
|
const scrollTop = e.detail.scrollTop;
|
this.letterDetails.some((item) => {
|
if (scrollTop >= item.top && scrollTop <= item.bottom - 20) {
|
this.currentLetter = item.id;
|
//当前固定用的是粘性定位,如果不用粘性定位,在这里设置
|
return true;
|
}
|
});
|
},
|
|
//搜索
|
searchChange(e) {
|
let {
|
value
|
} = e.detail;
|
this.inputValue = value;
|
},
|
// 触发开始
|
touchStart(e) {
|
// console.log(e);
|
},
|
//移动时
|
touchMove(e) {
|
uni.vibrateShort();
|
let y = e.touches[0].clientY;
|
let offsettop = e.currentTarget.offsetTop;
|
|
//判断选择区域,只在选择区才会生效
|
if (y > offsettop) {
|
let num = parseInt((y - offsettop) / 15); //右边每个字母元素的高度
|
let letter = this.alphabet[num];
|
this.tipsLetter = letter;
|
|
let curentLetter = this.letterTransform(letter);
|
uni.showToast({
|
title: curentLetter,
|
icon: 'none',
|
});
|
}
|
},
|
//触发结束
|
touchEnd() {
|
this.toIndex = this.tipsLetter;
|
},
|
//移动开始获取字母,并放大提示
|
getLetter(e) {
|
uni.vibrateShort();
|
let {
|
id
|
} = e.currentTarget;
|
this.tipsLetter = id;
|
|
let curentLetter = this.letterTransform(id);
|
uni.showToast({
|
title: curentLetter,
|
icon: 'none',
|
});
|
},
|
//移动结束设置字母,赋值到toIndex
|
setLetter() {
|
this.toIndex = this.tipsLetter;
|
},
|
|
//提示字母转换
|
letterTransform(letter) {
|
let str = '';
|
if (letter == 'area') {
|
str = '当前';
|
} else if (letter == 'record') {
|
str = '定位';
|
} else {
|
str = letter;
|
}
|
return str;
|
},
|
},
|
}
|
</script>
|
|
<style lang="scss" scoped>
|
.area-icon .iconfont {
|
transform: rotateX(180deg);
|
}
|
.area.history .title-wrapp {
|
background: transparent;
|
}
|
.area.myarea .c-title {
|
color: #666666;
|
}
|
.chose-city {
|
position: fixed;
|
top: 0;
|
left: 0;
|
right: 0;
|
bottom: 0;
|
z-index: 99;
|
background: #fff;
|
}
|
|
.city-search-wrap {
|
width: 100%;
|
box-sizing: border-box;
|
height: 128rpx;
|
|
.search {
|
width: 750rpx;
|
height: 60rpx;
|
display: flex;
|
align-items: center;
|
font-size: 28rpx;
|
color: #222;
|
padding: 0 30rpx;
|
box-sizing: border-box;
|
background: #fff;
|
|
.l-search {
|
width: 450rpx;
|
position: relative;
|
height: 60rpx;
|
line-height: 60rpx;
|
|
.icon-search {
|
font-size: 28rpx;
|
position: absolute;
|
left: 30rpx;
|
top: 0;
|
color: #8e8f97;
|
font-weight: 700;
|
height: 60rpx;
|
line-height: 60rpx;
|
}
|
.input-search {
|
width: 450rpx;
|
height: 60rpx;
|
box-sizing: border-box;
|
padding: 0 84rpx 0 84rpx;
|
text-align: left;
|
background: #f4f5f9;
|
border-radius: 60rpx;
|
border: 0;
|
}
|
|
.clear-input {
|
font-size: 30rpx;
|
position: absolute;
|
right: 10rpx;
|
top: 50%;
|
transform: translateY(-50%);
|
padding: 10rpx;
|
color: #8e8f97;
|
}
|
}
|
|
.r-cancel {
|
width: 80rpx;
|
box-sizing: border-box;
|
font-size: 28rpx;
|
height: 60rpx;
|
line-height: 60rpx;
|
background: transparent;
|
border: 0;
|
color: #519AD2;
|
|
.iconfont {
|
color: #333333;
|
}
|
}
|
}
|
}
|
|
.reach-content {
|
padding-left: 36rpx;
|
box-sizing: border-box;
|
.li {
|
width: 714rpx;
|
font-size: 28rpx;
|
height: 100rpx;
|
line-height: 100rpx;
|
color: #333;
|
position: relative;
|
box-sizing: border-box;
|
border-bottom: 2rpx solid #F5F5F5;
|
}
|
}
|
|
.block {
|
// padding: 0 36rpx;
|
box-sizing: border-box;
|
background-color: #f7f7f7;
|
}
|
|
.top-search {
|
line-height: 72rpx;
|
padding: 14rpx 30rpx 0;
|
box-sizing: border-box;
|
margin-bottom: 26rpx;
|
|
.item {
|
background: #F5F5F5;
|
border-radius: 12rpx;
|
font-size: 28rpx;
|
text-align: center;
|
color: #999999;
|
/* #ifdef MP-ALIPAY */
|
height: 72rpx;
|
line-height: 72rpx;
|
|
/* #endif */
|
text {
|
padding-left: 20rpx;
|
color: #c1c2cd;
|
vertical-align: middle;
|
position: relative;
|
top: -4rpx;
|
}
|
|
}
|
}
|
|
.scroll-view {
|
width: 100%;
|
height: calc(100vh - 110rpx);
|
box-sizing: border-box;
|
}
|
|
.area {
|
padding-bottom: 8rpx;
|
|
.title-wrapp {
|
// position: sticky;
|
// top: 0;
|
// left: 0;
|
background: #fff;
|
}
|
|
.c-title {
|
width: 100%;
|
box-sizing: border-box;
|
font-size: 28rpx;
|
color: #999999;
|
margin-bottom: 24rpx;
|
display: inline-flex;
|
justify-content: space-between;
|
align-items: center;
|
|
.r {
|
font-size: 24rpx;
|
color: #8e8f97;
|
display: inline-block;
|
align-items: center;
|
|
.iconfont {
|
font-size: 24rpx;
|
}
|
}
|
}
|
|
.ul {
|
display: flex;
|
flex-wrap: wrap;
|
|
.li {
|
width: 205rpx;
|
padding: 0 10rpx;
|
box-sizing: border-box;
|
height: 72rpx;
|
line-height: 68rpx;
|
text-align: center;
|
font-size: 26rpx;
|
color: #333;
|
border-radius: 8rpx;
|
margin: 0 18rpx 28rpx 0;
|
border: 2rpx solid #E2E2E2;
|
background-color: #ffffff;
|
text-overflow: ellipsis;
|
white-space: nowrap;
|
overflow: hidden;
|
|
&:nth-child(3n) {
|
margin-right: 0;
|
}
|
|
&.now {
|
padding: 0 32rpx 0 22rpx;
|
background-color: #f7f7f7;
|
|
.icon {
|
width: 50rpx;
|
height: 50rpx;
|
background-size: 100%;
|
vertical-align: middle;
|
position: relative;
|
top: -4rpx;
|
}
|
|
.text {
|
padding-left: 10rpx;
|
}
|
}
|
|
&.active {
|
font-weight: 500;
|
background: #ffde45;
|
}
|
}
|
|
.hover {
|
background: #ffde45;
|
}
|
}
|
}
|
|
.city-list {
|
width: 750rpx;
|
padding-bottom: 50rpx;
|
|
.c-title {
|
height: 60rpx;
|
line-height: 60rpx;
|
font-size: 30rpx;
|
font-weight: 500;
|
color: #272636;
|
background: #f7f7f7;
|
box-sizing: border-box;
|
padding-left: 30rpx;
|
position: sticky;
|
top: 0;
|
left: 0;
|
z-index: 2;
|
border-top: 2rpx solid #eee;
|
}
|
|
.item {
|
width: 714rpx;
|
margin-left: 30rpx;
|
padding: 0 30rpx 0 0;
|
height: 80rpx;
|
line-height: 80rpx;
|
color: #333;
|
font-size: 28rpx;
|
box-sizing: border-box;
|
border-bottom: 2rpx solid #F5F5F5;
|
|
&:last-child {
|
border-bottom: 0;
|
}
|
}
|
}
|
|
.alphabet {
|
position: fixed;
|
right: 0;
|
bottom: 20%;
|
width: calc(750rpx - 680rpx);
|
text-align: center;
|
font-size: 20rpx;
|
font-weight: 700;
|
color: #8e8f97;
|
z-index: 99;
|
|
.item {
|
height: 15px;
|
line-height: 15px;
|
}
|
|
.active {
|
color: #222;
|
}
|
}
|
|
.has-no-data {
|
font-size: 24rpx;
|
text-align: center;
|
color: #8e8f97;
|
margin-top: 50rpx;
|
}
|
</style>
|