| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266 |
- <template>
- <view class="region-picker" @click="showPicker">
- <view class="picker-display">
- <text :class="['region-text', selectedText ? '' : 'placeholder']">
- {{ selectedText || placeholder }}
- </text>
- <u-icon class="arrow" name='arrow-right' size="18"></u-icon>
- </view>
- <picker mode="multiSelector" :range="range" range-key="name" :value="indexes" @change="onPickerChange"
- @columnchange="onColumnChange" :disabled="disabled">
- <view class="picker-mask"></view>
- </picker>
- </view>
- </template>
- <script setup>
- import {
- ref,
- computed,
- watch,
- nextTick,
- onMounted
- } from 'vue'
- import areaData from '@/utils/area-data.js'
- const props = defineProps({
- province: {
- type: String,
- default: ''
- },
- city: {
- type: String,
- default: ''
- },
- district: {
- type: String,
- default: ''
- },
- placeholder: {
- type: String,
- default: '省市区'
- },
- disabled: {
- type: Boolean,
- default: false
- }
- })
- const emit = defineEmits(['change', 'update:province', 'update:city', 'update:district'])
- // 省市区列表
- const provinces = ref([])
- const cities = ref([])
- const districts = ref([])
- // 选中索引
- const provinceIndex = ref(0)
- const cityIndex = ref(0)
- const districtIndex = ref(0)
- // picker range
- const range = computed(() => [provinces.value, cities.value, districts.value])
- const indexes = computed(() => [provinceIndex.value, cityIndex.value, districtIndex.value])
- // 显示文本
- const selectedText = computed(() => {
- if (provinceIndex.value >= 0 && provinces.value[provinceIndex.value]) {
- const p = provinces.value[provinceIndex.value]?.name || ''
- const c = cities.value[cityIndex.value]?.name || ''
- const d = districts.value[districtIndex.value]?.name || ''
- return `${p} ${c} ${d}`.trim()
- }
- return ''
- })
- // 初始化省份数据
- function initProvinces() {
- provinces.value = areaData.filter(item => item.type === 0).map(item => ({
- code: item.code,
- name: item.name
- }))
- if (provinces.value.length > 0) {
- provinceIndex.value = 0
- // 默认加载第一个省份的城市
- updateCities(provinces.value[0].code)
- }
- }
- // 根据省份代码更新城市列表
- function updateCities(provinceCode) {
- cities.value = areaData.filter(item => item.parent_code === provinceCode && item.type === 1).map(item => ({
- code: item.code,
- name: item.name
- }))
- if (cities.value.length > 0) {
- cityIndex.value = 0
- updateDistricts(cities.value[0].code)
- } else {
- cities.value = []
- districts.value = []
- }
- }
- // 根据城市代码更新区县列表
- function updateDistricts(cityCode) {
- districts.value = areaData.filter(item => item.parent_code === cityCode && item.type === 2).map(item => ({
- code: item.code,
- name: item.name
- }))
- if (districts.value.length > 0) {
- districtIndex.value = 0
- } else {
- districts.value = []
- }
- }
- // 根据省市区名称设置索引(用于回显)
- function setIndexesByNames(provinceName, cityName, districtName) {
- if (!provinceName || provinces.value.length === 0) return
- // 查找省份
- const provinceItem = provinces.value.find(p => p.name === provinceName)
- if (!provinceItem) return
- provinceIndex.value = provinces.value.indexOf(provinceItem)
- // 更新城市列表(同步执行)
- updateCities(provinceItem.code)
- // 等待城市列表更新后(由于 updateCities 是同步,但 cities 已更新),设置城市索引
- if (cityName) {
- const cityItem = cities.value.find(c => c.name === cityName)
- if (cityItem) {
- cityIndex.value = cities.value.indexOf(cityItem)
- // 更新区县列表
- updateDistricts(cityItem.code)
- // 等待区县列表更新后设置区县索引
- if (districtName) {
- const districtItem = districts.value.find(d => d.name === districtName)
- if (districtItem) {
- districtIndex.value = districts.value.indexOf(districtItem)
- }
- }
- }
- }
- }
- // 列变化
- function onColumnChange(e) {
- const {
- column,
- value
- } = e.detail
- if (column === 0) {
- provinceIndex.value = value
- const provinceCode = provinces.value[value].code
- updateCities(provinceCode)
- } else if (column === 1) {
- cityIndex.value = value
- const cityCode = cities.value[value].code
- updateDistricts(cityCode)
- }
- }
- // 确认选择
- function onPickerChange(e) {
- const values = e.detail.value
- provinceIndex.value = values[0]
- cityIndex.value = values[1]
- districtIndex.value = values[2]
- const province = provinces.value[provinceIndex.value]
- const city = cities.value[cityIndex.value]
- const district = districts.value[districtIndex.value]
- emit('update:province', province?.name || '')
- emit('update:city', city?.name || '')
- emit('update:district', district?.name || '')
- emit('change', {
- province: province?.name || '',
- city: city?.name || '',
- district: district?.name || '',
- provinceCode: province?.code || '',
- cityCode: city?.code || '',
- districtCode: district?.code || ''
- })
- }
- function showPicker() {}
- // 监听外部传入的省市区名称变化(用于编辑回显)
- watch(
- () => [props.province, props.city, props.district],
- ([newProvince, newCity, newDistrict]) => {
- if (newProvince && provinces.value.length > 0) {
- setIndexesByNames(newProvince, newCity, newDistrict)
- }
- }, {
- immediate: true,
- deep: true
- }
- )
- onMounted(() => {
- initProvinces()
- })
- </script>
- <style scoped lang="less">
-
- .region-picker {
- position: relative;
- width: 100%;
- height: 100rpx;
- /* 与 input 高度一致 */
- }
- .picker-display {
- display: flex;
- align-items: center;
- justify-content: space-between;
- height: 100%;
- /* 与输入框背景一致 */
- border-radius: 8rpx;
- /* 圆角一致 */
- font-size: 28rpx;
- color: #333;
- .region-text {
- flex: 1;
- font-size: 28rpx;
- line-height: 100rpx;
- color: #333;
- &.placeholder {
- color: #999;
- }
- }
- .arrow {
- margin-left: 16rpx;
- color: #999;
- font-size: 28rpx;
- }
- }
- .region-text {
- flex: 1;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- }
- .region-text.placeholder {
- color: #999;
- }
- .picker-mask {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- opacity: 0;
- }
- </style>
|