You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
455 lines
11 KiB
455 lines
11 KiB
<!-- |
|
* @Author: weisheng |
|
* @Date: 2022-07-26 14:03:50 |
|
* @LastEditTime: 2023-05-19 15:26:21 |
|
* @LastEditors: weisheng |
|
* @Description: |
|
* @FilePath: \fant-mini-plus\src\uni_modules\fant-mini-plus\components\hd-area\hd-area.vue |
|
* 记得注释 |
|
--> |
|
<template> |
|
<hd-popup type="bottom" id="areapop" :maskClick="true" @onTap="doMaskClick"> |
|
<view class="hd-area"> |
|
<view class="title"> |
|
请选择所在地区 |
|
<view class="close" @click="doClose"> |
|
<hd-icon name="ic_close_line" size="44rpx" color="#666666"></hd-icon> |
|
</view> |
|
</view> |
|
<view class="header"> |
|
<view |
|
@click="doChange('province')" |
|
:class="['header-item', activeType === 'province' ? 'header-item--active' : '']" |
|
v-if="activeType === 'province' || activeType === 'city' || activeType === 'district' || innerProvince" |
|
> |
|
{{ innerProvince ? innerProvince.name : '请选择省' }} |
|
</view> |
|
<view |
|
@click="doChange('city')" |
|
:class="['header-item', activeType === 'city' ? 'header-item--active' : '']" |
|
v-if="activeType === 'city' || activeType === 'district' || innerProvince" |
|
> |
|
{{ innerProvince && innerCity ? innerCity.name : '请选择市' }} |
|
</view> |
|
<view |
|
@click="doChange('district')" |
|
:class="['header-item', activeType === 'district' ? 'header-item--active' : '']" |
|
v-if="activeType === 'district' || innerCity" |
|
> |
|
{{ innerProvince && innerCity && innerCounty ? innerCounty.name : '请选择区' }} |
|
</view> |
|
</view> |
|
<scroll-view scroll-y class="main" :key="mainKey" :scroll-into-view="init ? viewId : ''" :scroll-with-animation="true"> |
|
<view |
|
:id="`tag-${item.id}`" |
|
:class="['main-item', select(item.name) ? 'main-item--active' : '']" |
|
@click="doSelect(item)" |
|
v-for="item in showList" |
|
:key="item.id" |
|
> |
|
<hd-icon v-if="select(item.name)" name="ic_right_line" size="44rpx" color="#00925A"></hd-icon> |
|
{{ item.name }} |
|
</view> |
|
</scroll-view> |
|
</view> |
|
</hd-popup> |
|
</template> |
|
<script lang="ts" setup> |
|
import { computed, inject, nextTick, ref, watch } from 'vue' |
|
import { usePopup } from '../hd-popup' |
|
import { CommonUtil, areaDefaultKey } from '../..' |
|
interface AreaList { |
|
province_list: Record<number, string> |
|
city_list: Record<number, string> |
|
county_list: Record<number, string> |
|
} |
|
interface Props { |
|
// 省市区数据 |
|
areaData: AreaList |
|
// 当前选中的省市区 |
|
area: Ucn[] |
|
id?: string |
|
} |
|
|
|
type AreaType = 'province' | 'city' | 'district' // 区域类型 |
|
|
|
const areapop = usePopup('areapop') // 弹出框 |
|
|
|
const props = withDefaults(defineProps<Props>(), { |
|
areaData: () => { |
|
return { |
|
province_list: {}, |
|
city_list: {}, |
|
county_list: {} |
|
} |
|
}, |
|
area: () => [], |
|
id: '' |
|
}) |
|
|
|
let innerProvince = ref<Nullable<Ucn>>(null) // 选择的省 |
|
let innerCity = ref<Nullable<Ucn>>(null) // 选择的市 |
|
let innerCounty = ref<Nullable<Ucn>>(null) // 选择的区 |
|
let init = ref<boolean>(false) // 页面初始化完成 |
|
let activeType = ref<AreaType>('province') // 当前所选的area类型 |
|
const mainKey = ref<string>(CommonUtil.s4()) // scroll-view键 |
|
const viewId = ref<string | null>(null) // 应当展示在视图中的节点id |
|
const areaKey = props.id ? '__AREA__' + props.id : areaDefaultKey |
|
const areaShow = inject(areaKey) || ref<boolean>(false) // 是否展示popup组件 |
|
|
|
const emit = defineEmits(['close', 'confirm']) // 事件 |
|
|
|
// 监听函数式调用是否展示弹出框 |
|
watch( |
|
() => areaShow.value, |
|
(newVal: boolean) => { |
|
if (newVal) { |
|
open() |
|
} else { |
|
close() |
|
} |
|
} |
|
) |
|
|
|
watch(activeType, (newVal) => { |
|
mainKey.value = CommonUtil.s4() |
|
let timer = setTimeout(() => { |
|
setViewId() |
|
clearTimeout(timer) |
|
}, 50) |
|
}) |
|
|
|
/** |
|
* 是否被选中 |
|
*/ |
|
const select = computed(() => { |
|
return (item: string) => { |
|
switch (activeType.value) { |
|
case 'province': |
|
return innerProvince.value ? item === innerProvince.value!.name : false |
|
case 'city': |
|
return innerCity.value ? item === innerCity.value!.name : false |
|
case 'district': |
|
return innerCounty.value ? item === innerCounty.value!.name : false |
|
default: |
|
return innerProvince.value ? item === innerProvince.value!.name : false |
|
} |
|
} |
|
}) |
|
|
|
/** |
|
* 展示的列表 |
|
*/ |
|
const showList = computed(() => { |
|
switch (activeType.value) { |
|
case 'province': |
|
return provinceList.value |
|
case 'city': |
|
return cityList.value |
|
case 'district': |
|
return countyList.value |
|
default: |
|
return provinceList.value |
|
} |
|
}) |
|
|
|
/** |
|
* 省列表 |
|
*/ |
|
const provinceList = computed(() => { |
|
const provinceList: Ucn[] = [] |
|
if (props.areaData && props.areaData.province_list) { |
|
for (const key in props.areaData.province_list) { |
|
if (props.areaData.province_list[key]) { |
|
provinceList.push({ |
|
id: key, |
|
name: props.areaData.province_list[key] |
|
}) |
|
} |
|
} |
|
} |
|
return provinceList |
|
}) |
|
|
|
/** |
|
* 市列表 |
|
*/ |
|
const cityList = computed(() => { |
|
const cityList: Ucn[] = [] |
|
if (props.areaData && props.areaData.city_list) { |
|
for (const key in props.areaData.city_list) { |
|
if (props.areaData.city_list[key] && innerProvince.value && innerProvince.value.id.slice(0, 2) === key.slice(0, 2)) { |
|
cityList.push({ |
|
id: key, |
|
name: props.areaData.city_list[key] |
|
}) |
|
} |
|
} |
|
} |
|
return cityList |
|
}) |
|
|
|
/** |
|
* 区列表 |
|
*/ |
|
const countyList = computed(() => { |
|
const countyList: Ucn[] = [] |
|
if (props.areaData && props.areaData.county_list) { |
|
for (const key in props.areaData.county_list) { |
|
if (props.areaData.county_list[key] && (!innerProvince.value || (innerCity.value && innerCity.value.id.slice(0, 4) === key.slice(0, 4)))) { |
|
countyList.push({ |
|
id: key, |
|
name: props.areaData.county_list[key] |
|
}) |
|
} |
|
} |
|
} |
|
return countyList |
|
}) |
|
|
|
/** |
|
* 设置应当展示在视图中的节点id |
|
*/ |
|
function setViewId() { |
|
let id: Nullable<string> = null |
|
switch (activeType.value) { |
|
case 'province': |
|
id = innerProvince.value ? `tag-${innerProvince.value.id}` : null |
|
break |
|
case 'city': |
|
id = innerCity.value ? `tag-${innerCity.value.id}` : null |
|
break |
|
case 'district': |
|
id = innerCounty.value ? `tag-${innerCounty.value.id}` : null |
|
break |
|
default: |
|
id = null |
|
break |
|
} |
|
viewId.value = id |
|
} |
|
|
|
/** |
|
* 打开 |
|
*/ |
|
function open() { |
|
doInit() |
|
areapop.showPopup() |
|
let timer = setTimeout(() => { |
|
init.value = true |
|
clearTimeout(timer) |
|
setViewId() |
|
}, 100) |
|
} |
|
|
|
/** |
|
* 关闭 |
|
*/ |
|
function close() { |
|
areapop.closePopup() |
|
doReset() |
|
nextTick(() => { |
|
init.value = false |
|
viewId.value = null |
|
}) |
|
} |
|
|
|
/** |
|
* 主动关闭 |
|
*/ |
|
function doClose() { |
|
close() |
|
/** |
|
* 地区选择器关闭时触发 |
|
*/ |
|
emit('close') |
|
} |
|
|
|
/** |
|
* 点击蒙层关闭 |
|
*/ |
|
function doMaskClick() { |
|
nextTick(() => { |
|
init.value = false |
|
}) |
|
emit('close') |
|
} |
|
|
|
/** |
|
* 将组件内的选中值重置为null |
|
*/ |
|
function doReset() { |
|
innerCity.value = null |
|
innerCounty.value = null |
|
innerProvince.value = null |
|
activeType.value = 'province' |
|
} |
|
|
|
/** |
|
* 初始化组件内的值 |
|
*/ |
|
function doInit() { |
|
innerProvince.value = props.area && props.area[0] ? props.area[0] : null |
|
innerCity.value = props.area && props.area[1] ? props.area[1] : null |
|
innerCounty.value = props.area && props.area[2] ? props.area[2] : null |
|
if (innerProvince.value && innerCity.value && innerCounty.value) { |
|
activeType.value = 'district' |
|
} else if (innerProvince.value && innerCity.value) { |
|
activeType.value = 'city' |
|
} else if (innerProvince.value) { |
|
activeType.value = 'province' |
|
} else { |
|
activeType.value = 'province' |
|
} |
|
} |
|
|
|
/** |
|
* 地址选择完成 |
|
*/ |
|
function doConfirm() { |
|
// 地址选择完成后触发 |
|
// @arg value: 当前选中的省市区 Ucn[] |
|
emit('confirm', [innerProvince.value, innerCity.value, innerCounty.value]) |
|
close() |
|
} |
|
|
|
/** |
|
* 切换当前选择的省市区类型 |
|
* @param type 类型 |
|
*/ |
|
function doChange(type: 'province' | 'city' | 'district') { |
|
activeType.value = type |
|
} |
|
|
|
/** |
|
* 点击选中 |
|
* @param item 选中省市区项 |
|
*/ |
|
function doSelect(item: Ucn) { |
|
switch (activeType.value) { |
|
case 'province': |
|
if (innerProvince.value && innerProvince.value.id === item.id) { |
|
innerProvince.value = null |
|
} else { |
|
innerProvince.value = item |
|
activeType.value = 'city' |
|
} |
|
innerCity.value = null |
|
innerCounty.value = null |
|
break |
|
case 'city': |
|
if (innerCity.value && innerCity.value.id === item.id) { |
|
innerCity.value = null |
|
} else { |
|
innerCity.value = item |
|
activeType.value = 'district' |
|
} |
|
innerCounty.value = null |
|
break |
|
case 'district': |
|
if (innerCounty.value && innerCounty.value.id === item.id) { |
|
innerCounty.value = null |
|
} else { |
|
innerCounty.value = item |
|
doConfirm() |
|
} |
|
break |
|
default: |
|
if (innerProvince.value && innerProvince.value.id === item.id) { |
|
innerProvince.value = null |
|
} else { |
|
innerProvince.value = item |
|
activeType.value = 'city' |
|
} |
|
innerCity.value = null |
|
innerCounty.value = null |
|
break |
|
} |
|
} |
|
</script> |
|
|
|
<style lang="scss" scoped> |
|
@import '../../libs/css/index.scss'; |
|
|
|
.hd-area { |
|
position: relative; |
|
height: 846rpx; |
|
height: calc(846rpx + constant(safe-area-inset-bottom)); |
|
height: calc(846rpx + env(safe-area-inset-bottom)); |
|
width: calc(100vw - 80rpx); |
|
background: #ffffff; |
|
padding: 0 40rpx; |
|
border-radius: 20rpx 20rpx 0px 0px; |
|
padding-bottom: 0; |
|
padding-bottom: constant(safe-area-inset-bottom); |
|
padding-bottom: env(safe-area-inset-bottom); |
|
|
|
.title { |
|
height: 114rpx; |
|
width: 100%; |
|
display: flex; |
|
align-items: center; |
|
justify-content: center; |
|
font-size: 36rpx; |
|
font-family: PingFangSC-Medium, PingFang SC; |
|
font-weight: 500; |
|
color: #202124; |
|
.close { |
|
position: absolute; |
|
top: 57rpx; |
|
right: 0; |
|
padding: 19rpx; |
|
transform: translateY(-50%); |
|
} |
|
} |
|
.header { |
|
display: flex; |
|
margin-bottom: 24rpx; |
|
|
|
&-item { |
|
height: 44rpx; |
|
font-size: 32rpx; |
|
font-family: PingFangSC-Medium, PingFang SC; |
|
font-weight: 500; |
|
color: $color-text-secondary; |
|
max-width: 186rpx; |
|
@include ellipsis(); |
|
&:not(:last-child) { |
|
margin-right: 56rpx; |
|
} |
|
&--active { |
|
color: $color-primary; |
|
} |
|
} |
|
} |
|
.main { |
|
height: calc(100% - 182rpx); |
|
overflow: auto; |
|
::-webkit-scrollbar { |
|
width: 0; |
|
height: 0; |
|
color: transparent; |
|
} |
|
|
|
&-item { |
|
display: flex; |
|
align-items: center; |
|
@include ellipsis(); |
|
width: 100%; |
|
height: 84rpx; |
|
background: #ffffff; |
|
font-size: 28rpx; |
|
color: $color-text-secondary; |
|
image { |
|
width: 44rpx; |
|
height: 44rpx; |
|
} |
|
&--active { |
|
font-family: PingFangSC-Medium, PingFang SC; |
|
font-weight: 500; |
|
color: $color-text-secondary; |
|
} |
|
} |
|
} |
|
} |
|
</style>
|
|
|