|
@@ -1,77 +1,74 @@
|
|
|
<template>
|
|
<template>
|
|
|
- <u-popup :show="show" mode="bottom" round="30rpx" closeable @close="close">
|
|
|
|
|
- <view class="up-picker-area d-flex flex-cln">
|
|
|
|
|
- <!-- 标题 -->
|
|
|
|
|
- <view class="area-title">{{ title }}</view>
|
|
|
|
|
- <!-- 顶部链路 -->
|
|
|
|
|
- <view class="area-breadcrumb">
|
|
|
|
|
- <!-- 全国根:selectCodeMax 为空或 000000 时显示 -->
|
|
|
|
|
- <template v-if="isNationRoot">
|
|
|
|
|
- <view class="crumb disabled" @click="onClickNationBreadcrumb">全国</view>
|
|
|
|
|
- <view class="sep" v-if="selectedCodes.length">/</view>
|
|
|
|
|
- </template>
|
|
|
|
|
- <!-- 基础链路(selectCodeMax 的完整链路),仅最后一个可点击以切换到第一页(市级) -->
|
|
|
|
|
- <template v-for="(code, idx) in baseChain" :key="'base-' + code">
|
|
|
|
|
- <view class="crumb" :class="idx === baseChain.length - 1 ? '' : 'disabled'" @click="idx === baseChain.length - 1 && onClickBaseBreadcrumb()">
|
|
|
|
|
- {{ getNameByCode(code) }}
|
|
|
|
|
- </view>
|
|
|
|
|
- <view class="sep" v-if="idx < baseChain.length - 1 || selectedCodes.length">/</view>
|
|
|
|
|
- </template>
|
|
|
|
|
-
|
|
|
|
|
- <!-- 选中链路 -->
|
|
|
|
|
- <template v-for="(code, idx) in selectedCodes" :key="'sel-' + code">
|
|
|
|
|
- <view class="crumb" :class="{ active: idx === currentSwiper }" @click="jumpToLevel(idx)">
|
|
|
|
|
- {{ getNameByCode(code) }}
|
|
|
|
|
- </view>
|
|
|
|
|
- <view class="sep" v-if="idx < selectedCodes.length - 1">/</view>
|
|
|
|
|
- </template>
|
|
|
|
|
- </view>
|
|
|
|
|
|
|
+ <root-portal>
|
|
|
|
|
+ <u-popup :show="show" mode="bottom" round="30rpx" closeable @close="close">
|
|
|
|
|
+ <view class="up-picker-area d-flex flex-cln">
|
|
|
|
|
+ <!-- 标题 -->
|
|
|
|
|
+ <view class="area-title">{{ title }}</view>
|
|
|
|
|
+ <!-- 顶部链路 -->
|
|
|
|
|
+ <view class="area-breadcrumb">
|
|
|
|
|
+ <!-- 全国根:selectCodeMax 为空或 000000 时显示 -->
|
|
|
|
|
+ <template v-if="isNationRoot">
|
|
|
|
|
+ <view class="crumb disabled" @click="onClickNationBreadcrumb">全国</view>
|
|
|
|
|
+ <view class="sep" v-if="selectedCodes.length">/</view>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ <!-- 基础链路(selectCodeMax 的完整链路),仅最后一个可点击以切换到第一页(市级) -->
|
|
|
|
|
+ <template v-for="(code, idx) in baseChain" :key="'base-' + code">
|
|
|
|
|
+ <view class="crumb" :class="idx === baseChain.length - 1 ? '' : 'disabled'" @click="idx === baseChain.length - 1 && onClickBaseBreadcrumb()">
|
|
|
|
|
+ {{ getNameByCode(code) }}
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="sep" v-if="idx < baseChain.length - 1 || selectedCodes.length">/</view>
|
|
|
|
|
+ </template>
|
|
|
|
|
|
|
|
- <!-- 滑动列区域 -->
|
|
|
|
|
- <view class="area-body">
|
|
|
|
|
- <swiper :current="currentSwiper" @change="onSwiperChange" class="area-swiper">
|
|
|
|
|
- <template v-for="(parent, colIdx) in columnParents" :key="'col-' + parent">
|
|
|
|
|
- <swiper-item>
|
|
|
|
|
- <scroll-view
|
|
|
|
|
- :id="`sv-${colIdx}`"
|
|
|
|
|
- scroll-y
|
|
|
|
|
- class="area-scroll"
|
|
|
|
|
- :scroll-into-view="scrollIntoViewArr[colIdx]"
|
|
|
|
|
- >
|
|
|
|
|
- <!-- 顶部特殊项:全国(仅全国范围且首列显示) -->
|
|
|
|
|
- <view v-if="isNationRoot && colIdx === 0" class="picker-item" :id="`area-item-0-000000`" @click="onPickNation">
|
|
|
|
|
- <view class="name" :class="{ selected: currentValue === '000000' }">全国</view>
|
|
|
|
|
- <up-icon v-if="currentValue === '000000'" name="checkbox-mark" color="#000" size="36rpx" />
|
|
|
|
|
- </view>
|
|
|
|
|
-
|
|
|
|
|
- <!-- 顶部特殊项:选择范围自身(非全国,首列显示),如 云南省 -->
|
|
|
|
|
- <view v-else-if="!isNationRoot && colIdx === 0" class="picker-item" :id="`area-item-0-${canonical6(baseRoot)}`" @click="onPickBaseRoot">
|
|
|
|
|
- <view class="name" :class="{ selected: currentValue === baseRoot }">{{ getNameByCode(baseRoot) }}</view>
|
|
|
|
|
- <up-icon v-if="currentValue === baseRoot" name="checkbox-mark" color="#000" size="36rpx" />
|
|
|
|
|
- </view>
|
|
|
|
|
-
|
|
|
|
|
- <!-- 正常子级项 -->
|
|
|
|
|
- <template v-for="item in childrenMap[parent] || []" :key="item.adcdCode">
|
|
|
|
|
- <view class="picker-item" :id="`area-item-${colIdx}-${canonical6(item.adcdCode)}`" @click="onPick(item, colIdx)">
|
|
|
|
|
- <view class="name" :class="{ selected: canonical6(selectedCodes[colIdx]) === canonical6(item.adcdCode) }">
|
|
|
|
|
- {{ item.adcdName }}
|
|
|
|
|
- </view>
|
|
|
|
|
- <up-icon v-if="canonical6(selectedCodes[colIdx]) === canonical6(item.adcdCode)" name="checkbox-mark" color="#000" size="36rpx" />
|
|
|
|
|
- </view>
|
|
|
|
|
- </template>
|
|
|
|
|
- </scroll-view>
|
|
|
|
|
- </swiper-item>
|
|
|
|
|
|
|
+ <!-- 选中链路 -->
|
|
|
|
|
+ <template v-for="(code, idx) in selectedCodes" :key="'sel-' + code">
|
|
|
|
|
+ <view class="crumb" :class="{ active: idx === currentSwiper }" @click="jumpToLevel(idx)">
|
|
|
|
|
+ {{ getNameByCode(code) }}
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="sep" v-if="idx < selectedCodes.length - 1">/</view>
|
|
|
</template>
|
|
</template>
|
|
|
- </swiper>
|
|
|
|
|
- </view>
|
|
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 滑动列区域 -->
|
|
|
|
|
+ <view class="area-body">
|
|
|
|
|
+ <swiper :current="currentSwiper" @change="onSwiperChange" class="area-swiper">
|
|
|
|
|
+ <template v-for="(parent, colIdx) in columnParents" :key="'col-' + parent">
|
|
|
|
|
+ <swiper-item>
|
|
|
|
|
+ <scroll-view :id="`sv-${colIdx}`" scroll-y class="area-scroll" :scroll-into-view="scrollIntoViewArr[colIdx]">
|
|
|
|
|
+ <!-- 顶部特殊项:全国(仅全国范围且首列显示) -->
|
|
|
|
|
+ <view v-if="isNationRoot && colIdx === 0" class="picker-item" :id="`area-item-0-000000`" @click="onPickNation">
|
|
|
|
|
+ <view class="name" :class="{ selected: currentValue === '000000' }">全国</view>
|
|
|
|
|
+ <up-icon v-if="currentValue === '000000'" name="checkbox-mark" color="#000" size="36rpx" />
|
|
|
|
|
+ </view>
|
|
|
|
|
|
|
|
- <!-- 底部操作 -->
|
|
|
|
|
- <view class="area-footer">
|
|
|
|
|
- <up-button class="btn" @click="close">取消</up-button>
|
|
|
|
|
- <up-button class="btn" color="#2A6D52" @click="confirmPick">确定</up-button>
|
|
|
|
|
|
|
+ <!-- 顶部特殊项:选择范围自身(非全国,首列显示),如 云南省 -->
|
|
|
|
|
+ <view v-else-if="!isNationRoot && colIdx === 0" class="picker-item" :id="`area-item-0-${canonical6(baseRoot)}`" @click="onPickBaseRoot">
|
|
|
|
|
+ <view class="name" :class="{ selected: currentValue === baseRoot }">{{ getNameByCode(baseRoot) }}</view>
|
|
|
|
|
+ <up-icon v-if="currentValue === baseRoot" name="checkbox-mark" color="#000" size="36rpx" />
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 正常子级项 -->
|
|
|
|
|
+ <template v-for="item in childrenMap[parent] || []" :key="item.adcdCode">
|
|
|
|
|
+ <view class="picker-item" :id="`area-item-${colIdx}-${canonical6(item.adcdCode)}`" @click="onPick(item, colIdx)">
|
|
|
|
|
+ <view class="name" :class="{ selected: canonical6(selectedCodes[colIdx]) === canonical6(item.adcdCode) }">
|
|
|
|
|
+ {{ item.adcdName }}
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <up-icon v-if="canonical6(selectedCodes[colIdx]) === canonical6(item.adcdCode)" name="checkbox-mark" color="#000" size="36rpx" />
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </scroll-view>
|
|
|
|
|
+ </swiper-item>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </swiper>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 底部操作 -->
|
|
|
|
|
+ <view class="area-footer">
|
|
|
|
|
+ <up-button class="btn" @click="close">取消</up-button>
|
|
|
|
|
+ <up-button class="btn" color="#2A6D52" @click="confirmPick">确定</up-button>
|
|
|
|
|
+ </view>
|
|
|
</view>
|
|
</view>
|
|
|
- </view>
|
|
|
|
|
- </u-popup>
|
|
|
|
|
|
|
+ </u-popup>
|
|
|
|
|
+ </root-portal>
|
|
|
<up-toast ref="uToastRef" />
|
|
<up-toast ref="uToastRef" />
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
@@ -395,7 +392,6 @@ const applyModelValue = async (val: MaybeStringNumber): Promise<void> => {
|
|
|
const maxCols = Math.max(0, props.maxLevel - baseL);
|
|
const maxCols = Math.max(0, props.maxLevel - baseL);
|
|
|
const picked = full.slice(base.length, base.length + maxCols);
|
|
const picked = full.slice(base.length, base.length + maxCols);
|
|
|
|
|
|
|
|
-
|
|
|
|
|
selectedCodes.value = picked;
|
|
selectedCodes.value = picked;
|
|
|
currentValue.value = picked.length ? picked[picked.length - 1] : baseRoot.value;
|
|
currentValue.value = picked.length ? picked[picked.length - 1] : baseRoot.value;
|
|
|
await ensureColumnsData();
|
|
await ensureColumnsData();
|
|
@@ -440,7 +436,7 @@ const onPick = async (item: AreaItem, colIdx: number) => {
|
|
|
// 是否达到允许的最后一列(相对 baseRoot 的列数)
|
|
// 是否达到允许的最后一列(相对 baseRoot 的列数)
|
|
|
const baseL = codeLevel(baseRoot.value);
|
|
const baseL = codeLevel(baseRoot.value);
|
|
|
const maxCols = Math.max(0, props.maxLevel - baseL);
|
|
const maxCols = Math.max(0, props.maxLevel - baseL);
|
|
|
- const reachedFinalColumn = (colIdx + 1) >= maxCols;
|
|
|
|
|
|
|
+ const reachedFinalColumn = colIdx + 1 >= maxCols;
|
|
|
if (reachedFinalColumn) {
|
|
if (reachedFinalColumn) {
|
|
|
scrollToAllSelected();
|
|
scrollToAllSelected();
|
|
|
confirmPick();
|
|
confirmPick();
|
|
@@ -486,11 +482,11 @@ const onPickBaseRoot = async (): Promise<void> => {
|
|
|
|
|
|
|
|
// 组装完整名称:包含基础链路与已选链路(全国仅返回“全国”)
|
|
// 组装完整名称:包含基础链路与已选链路(全国仅返回“全国”)
|
|
|
const buildFullName = (): string => {
|
|
const buildFullName = (): string => {
|
|
|
- if (currentValue.value === '000000') return '全国';
|
|
|
|
|
- const baseNames = baseChain.value.map((code) => getNameByCode(code));
|
|
|
|
|
- // 若当前值是 baseRoot 自身(选择范围自身),则不重复追加 selectedCodes
|
|
|
|
|
- const selNames = selectedCodes.value.map((code) => getNameByCode(code));
|
|
|
|
|
- return [...baseNames, ...selNames].filter(Boolean).join('');
|
|
|
|
|
|
|
+ if (currentValue.value === '000000') return '全国';
|
|
|
|
|
+ const baseNames = baseChain.value.map((code) => getNameByCode(code));
|
|
|
|
|
+ // 若当前值是 baseRoot 自身(选择范围自身),则不重复追加 selectedCodes
|
|
|
|
|
+ const selNames = selectedCodes.value.map((code) => getNameByCode(code));
|
|
|
|
|
+ return [...baseNames, ...selNames].filter(Boolean).join('');
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
const confirmPick = (): void => {
|
|
const confirmPick = (): void => {
|
|
@@ -505,7 +501,7 @@ const confirmPick = (): void => {
|
|
|
value: currentValue.value,
|
|
value: currentValue.value,
|
|
|
name: getNameByCode(currentValue.value),
|
|
name: getNameByCode(currentValue.value),
|
|
|
fullNames: nameList.join(''),
|
|
fullNames: nameList.join(''),
|
|
|
- fullName // 新增:完整地址名称
|
|
|
|
|
|
|
+ fullName, // 新增:完整地址名称
|
|
|
});
|
|
});
|
|
|
close();
|
|
close();
|
|
|
};
|
|
};
|
|
@@ -558,14 +554,14 @@ watch(
|
|
|
currentSwiper.value = 0;
|
|
currentSwiper.value = 0;
|
|
|
await rebuildByInputs();
|
|
await rebuildByInputs();
|
|
|
},
|
|
},
|
|
|
- { immediate: true }
|
|
|
|
|
|
|
+ { immediate: true },
|
|
|
);
|
|
);
|
|
|
|
|
|
|
|
watch(
|
|
watch(
|
|
|
() => props.modelValue,
|
|
() => props.modelValue,
|
|
|
async (nv) => {
|
|
async (nv) => {
|
|
|
await applyModelValue(String(nv || ''));
|
|
await applyModelValue(String(nv || ''));
|
|
|
- }
|
|
|
|
|
|
|
+ },
|
|
|
);
|
|
);
|
|
|
|
|
|
|
|
// 每次列父集合变化时,刷新列数据并滚动
|
|
// 每次列父集合变化时,刷新列数据并滚动
|
|
@@ -576,7 +572,7 @@ watch(
|
|
|
// 定位到当前列并滚动选中项
|
|
// 定位到当前列并滚动选中项
|
|
|
currentSwiper.value = Math.max(0, Math.min(currentSwiper.value, columnParents.value.length - 1));
|
|
currentSwiper.value = Math.max(0, Math.min(currentSwiper.value, columnParents.value.length - 1));
|
|
|
scrollToAllSelected();
|
|
scrollToAllSelected();
|
|
|
- }
|
|
|
|
|
|
|
+ },
|
|
|
);
|
|
);
|
|
|
|
|
|
|
|
// 点击顶部“全国”:切到省级并滚动到省级区域
|
|
// 点击顶部“全国”:切到省级并滚动到省级区域
|
|
@@ -672,7 +668,6 @@ const onClickBaseBreadcrumb = async (): Promise<void> => {
|
|
|
/* 移除 will-change 与像素滚动相关样式,仅保留稳定布局 */
|
|
/* 移除 will-change 与像素滚动相关样式,仅保留稳定布局 */
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-
|
|
|
|
|
.picker-item {
|
|
.picker-item {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
align-items: center;
|
|
align-items: center;
|