Add linked Chinese postal code input and lookup data

This commit is contained in:
biss committed 2026-08-17 19:24:58 +08:00
1 parent 3f685ca068
commit 1e43d910c0
8 files changed
+141 -4

No files matched your search

+1
View File
@@ -12,6 +12,7 @@ export {}
declare module 'vue' {
export interface GlobalComponents {
AppUpdateManagementPanel: typeof import('./components/AppUpdateManagementPanel.vue')['default']
ChinesePostalCodeInput: typeof import('./components/ChinesePostalCodeInput.vue')['default']
ElAlert: typeof import('element-plus/es')['ElAlert']
ElBadge: typeof import('element-plus/es')['ElBadge']
ElButton: typeof import('element-plus/es')['ElButton']
@@ -0,0 +1,66 @@
<script setup lang="ts">
import { computed, ref, watch } from 'vue'
import { nativePlaceCandidatesForPostcode, postcodeForNativePlace } from '../data/nativePlaceLookup'
const postcode = defineModel<string>({ default: '' })
const nativePlace = defineModel<string>('nativePlace', { default: '' })
const props = defineProps<{ linked?: boolean }>()
const candidates = ref<string[]>([])
const suggestedPostcode = computed(() => props.linked === false ? undefined : postcodeForNativePlace(nativePlace.value))
const lockedPrefix = computed(() => suggestedPostcode.value?.slice(0, 4) ?? '')
const suffix = computed({
get: () => lockedPrefix.value && postcode.value.startsWith(lockedPrefix.value) ? postcode.value.slice(4, 6) : '',
set: (value: string) => { postcode.value = `${lockedPrefix.value}${value.replace(/\D/g, '').slice(0, 2)}` },
})
function normalize(value: string) {
return value.replace(/\D/g, '').slice(0, 6)
}
function updatePostcode(value: string) {
postcode.value = normalize(value)
}
function selectCandidate(value: string) {
nativePlace.value = value
candidates.value = []
}
watch(suggestedPostcode, (suggested) => {
if (!suggested || props.linked === false) return
if (!postcode.value.startsWith(suggested.slice(0, 4))) postcode.value = suggested
}, { immediate: true })
watch(postcode, (value) => {
if (props.linked === false || nativePlace.value || lockedPrefix.value) return
const matches = nativePlaceCandidatesForPostcode(value)
candidates.value = matches
if (matches.length === 1) selectCandidate(matches[0])
})
watch(() => props.linked, (linked) => {
if (linked === false) candidates.value = []
})
</script>
<template>
<div class="postcode-control">
<template v-if="linked !== false && lockedPrefix">
<el-input :model-value="lockedPrefix" disabled class="postcode-prefix" aria-label="邮政编码前四位" />
<el-input v-model="suffix" inputmode="numeric" maxlength="2" placeholder="后两位" aria-label="邮政编码后两位" />
</template>
<el-input v-else-if="linked !== false" :model-value="postcode" inputmode="numeric" maxlength="6" placeholder="6位邮政编码" @update:model-value="updatePostcode" />
<el-input v-else v-model="postcode" maxlength="20" placeholder="邮政编码" />
<el-select v-if="linked !== false && candidates.length > 1" placeholder="请选择匹配的区县" @update:model-value="selectCandidate">
<el-option v-for="candidate in candidates" :key="candidate" :label="candidate" :value="candidate" />
</el-select>
</div>
</template>
<style scoped>
.postcode-control { display: grid; grid-template-columns: minmax(0, 1fr); gap: 8px; width: 100%; }
.postcode-prefix { width: 100%; }
.postcode-control:has(.postcode-prefix) { grid-template-columns: 1fr 1fr; }
@media (max-width: 480px) { .postcode-control:has(.postcode-prefix) { grid-template-columns: 1fr; } }
</style>
+1 -2
View File
@@ -53,7 +53,6 @@ function pathFor(place: Place) {
}
function selectedName() {
if (selectedIds.value.length !== 4) return ''
return selectedIds.value
.map((id) => placesById.get(id)?.name)
.filter((name): name is string => Boolean(name))
@@ -63,7 +62,7 @@ function selectedName() {
function syncFromModel(value: string) {
const normalized = value.trim()
const matched = places.find((place) =>
place.deep === 3 && pathFor(place).map((item) => item.name).join(' / ') === normalized,
pathFor(place).map((item) => item.name).join(' / ') === normalized,
)
selectedIds.value = matched ? pathFor(matched).map((place) => place.id) : []
}
Loaded 3 of 8 files, more files were not shown because too many files have changed in this diff. Show more