Files
klp-oa/klp-ui/src/components/ChinaAreaSelect/index.vue
砂糖 1fa4c55869 feat(CoilSelector): 新增入场卷号字段并调整当前卷号显示
feat(customer): 新增客户相关配卷和财务信息查询接口

fix(base.vue): 修复发货单时间条件显示问题

refactor(CustomerEdit): 替换地址选择组件为普通输入框

feat(CoilSelector): 增加入场卷号查询条件并调整对话框宽度

style(OrderEdit): 调整客户名称和销售员选择框宽度

refactor(ChinaAreaSelect): 优化地址解析逻辑并支持空对象处理

feat(FileUpload/FileList): 新增文件预览功能组件

refactor(KLPService/CustomerSelect): 优化客户选择组件并支持自定义字段绑定

fix(AbnormalForm): 修复异常位置校验逻辑并保留当前卷号

feat(ContractTabs): 新增合同附件展示功能

refactor(warehouse/record): 重构操作记录统计展示方式

feat(contract): 集成客户选择组件并优化合同信息填充

refactor(order): 调整订单表单布局并集成合同信息

feat(FilePreview): 新增文件预览组件

feat(customer): 新增财务状态和发货配卷展示

refactor(CustomerOrder): 移除冗余代码并优化布局

feat(PlanDetailForm): 新增合同附件查看功能

feat(dict): 新增字典管理页面
2026-04-06 13:16:45 +08:00

234 lines
7.2 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="area-select-container">
<!-- 区域选择器 -->
<el-cascader
v-model="areaValue"
:options="areaTree"
:props="cascaderProps"
filterable
clearable
placeholder="请选择中国区域"
collapse-tags
@change="handleAreaChange"
v-if="isAreaTreeReady"
style="width: 100%; margin-bottom: 8px;"
/>
<!-- 详细地址输入框 -->
<el-input
v-model="detailAddress"
placeholder="请输入详细地址XX街道XX小区XX号楼"
@input="handleDetailChange"
style="width: 100%;"
/>
</div>
</template>
<script>
import areaData from './data.js'
import { formatAreaTextEnhanced } from './index.js'
export default {
name: 'ChinaAreaSelectWithDetail',
props: {
// 外部传入的组合值:格式为「[标准地址](自定义地址)」
value: {
type: String,
default: ''
},
// 配置绑定值类型code行政区划代码/ name区域名称
keyType: {
type: String,
default: 'code',
validator: val => ['code', 'name'].includes(val)
},
// 是否只选择到市级(省/市),默认选到区级(省/市/区)
onlyCityLevel: {
type: Boolean,
default: false
}
},
data() {
return {
areaTree: [],
// 特殊区域代码(直辖市+港澳特别行政区)
specialAreaCodes: ['110000', '120000', '310000', '500000', '810000', '820000'],
isAreaTreeReady: false,
// 区域选择器的内部值数组格式存储code
areaValue: [],
// 详细地址输入值
detailAddress: '',
cascaderProps: {
value: 'code',
label: 'name',
children: 'children',
checkStrictly: false,
expandTrigger: 'click',
multiple: false,
leaf: node => !node.children
}
}
},
watch: {
// 监听外部传入的value自动拆分并回显
value: {
immediate: true,
handler(val) {
this.parseCombineValue(val)
}
}
},
computed: {
// 组合值:对外输出「[标准地址](自定义地址)」格式
combineValue() {
// 1. 转换区域值为标准字符串code/name格式
let standardAddress = ''
if (this.areaValue.length) {
standardAddress = this.keyType === 'name'
? this.convertCodeToName(this.areaValue).join('/')
: this.areaValue.join('/')
}
// 2. 组合最终格式:[标准地址](自定义地址)
if (!standardAddress && !this.detailAddress) return ''
if (standardAddress && !this.detailAddress) return `[${standardAddress}]()`
if (!standardAddress && this.detailAddress) return `[()](${this.detailAddress})`
return `[${standardAddress}](${this.detailAddress})`
}
},
created() {
this.$nextTick(() => {
this.buildAreaTree()
})
},
methods: {
/**
* 正则解析组合值:提取[标准地址]和(自定义地址)部分
* @param {String} val - 外部传入的「[标准地址](自定义地址)」格式值
*/
parseCombineValue(val) {
const formattedAddress = formatAreaTextEnhanced(val)
this.areaValue = formattedAddress.standard.split('/').filter(Boolean)
this.detailAddress = formattedAddress.custom.trim()
},
/**
* 构建树形结构:兼容直辖市+港澳特别行政区的两级结构
*/
buildAreaTree() {
const tree = {};
// 第一步初始化所有省级节点非叶子节点带children
Object.keys(areaData).forEach(code => {
if (code.endsWith('0000') && !tree[code]) {
tree[code] = {
code: code,
name: areaData[code],
children: [] // 省级节点是父节点保留children
};
}
});
// 第二步:遍历所有非省级节点,构建层级
Object.entries(areaData).forEach(([code, name]) => {
if (code.length !== 6) return;
const provinceCode = code.substring(0, 2) + '0000';
// 跳过省级节点(已初始化)
if (code === provinceCode) return;
// 判断是否为特殊区域(直辖市/港澳)
const isSpecialArea = this.specialAreaCodes.includes(provinceCode);
// 1. 特殊区域处理区级直接挂载到省级且区级是叶子节点无children
if (isSpecialArea) {
if (tree[provinceCode]) {
tree[provinceCode].children.push({ code, name });
}
}
// 2. 普通省份处理:省→市→区三级
else {
const cityCode = code.substring(0, 4) + '00';
const provinceNode = tree[provinceCode];
if (!provinceNode) return;
// 2.1 初始化市级节点非叶子节点带children
if (!provinceNode.children.some(item => item.code === cityCode)) {
provinceNode.children.push({
code: cityCode,
name: areaData[cityCode] || '',
children: [] // 市级节点是父节点保留children
});
}
// 2.2 挂载区级节点叶子节点无children
if (code !== cityCode) {
const cityNode = provinceNode.children.find(item => item.code === cityCode);
if (cityNode) {
cityNode.children.push({ code, name });
}
}
}
});
// 第三步:格式化树形结构,过滤空节点
this.areaTree = Object.values(tree)
.filter(province => province.name && province.children.length > 0)
.map(province => {
province.children = province.children.filter(city => city.name);
return province;
});
// 标记数据就绪,渲染组件
this.isAreaTreeReady = true;
},
/**
* code转name容错处理
*/
convertCodeToName(codeArr) {
if (!Array.isArray(codeArr) || !codeArr.length) return [];
return codeArr.map(code => areaData[code] || '').filter(Boolean);
},
/**
* name转code容错处理
*/
convertNameToCode(nameArr) {
if (!Array.isArray(nameArr) || !nameArr.length) return [];
const nameToCodeMap = Object.fromEntries(
Object.entries(areaData).map(([code, name]) => [name, code])
);
return nameArr.map(name => nameToCodeMap[name] || '').filter(Boolean);
},
/**
* 区域选择器变化回调
*/
handleAreaChange() {
this.$emit('input', this.combineValue)
this.$emit('change', this.combineValue, {
standard: this.keyType === 'name' ? this.convertCodeToName(this.areaValue).join('/') : this.areaValue.join('/'),
custom: this.detailAddress
})
},
/**
* 详细地址变化回调
*/
handleDetailChange() {
this.$emit('input', this.combineValue)
this.$emit('change', this.combineValue, {
standard: this.keyType === 'name' ? this.convertCodeToName(this.areaValue).join('/') : this.areaValue.join('/'),
custom: this.detailAddress
})
}
}
}
</script>
<style scoped>
.area-select-container {
width: 100%;
}
</style>