From 0e3ecc528fbff3bc3b51a5a053d81b8774e0703d Mon Sep 17 00:00:00 2001
From: yyb <995253665@qq.com>
Date: 星期五, 27 三月 2026 14:49:01 +0800
Subject: [PATCH] refactor(客户档案): 更新地区字段名称并添加地区ID管理
---
src/views/basicData/customerFile/index.vue | 346 +++++++++++++++++++++++++++++++++++++++++++++++----------
1 files changed, 282 insertions(+), 64 deletions(-)
diff --git a/src/views/basicData/customerFile/index.vue b/src/views/basicData/customerFile/index.vue
index bd65a22..dae1f7f 100644
--- a/src/views/basicData/customerFile/index.vue
+++ b/src/views/basicData/customerFile/index.vue
@@ -4,9 +4,17 @@
<div class="left-panel">
<div class="left-header">
<div class="left-title">鍦板尯</div>
- <el-button type="primary"
- size="small"
- @click="openAddRegionDialog">鏂板鍦板尯</el-button>
+ <div class="left-actions">
+ <el-button type="primary"
+ size="small"
+ @click="openAddRegionDialog">鏂板</el-button>
+ <el-button size="small"
+ @click="openEditRegionDialog">淇敼</el-button>
+ <el-button type="danger"
+ plain
+ size="small"
+ @click="handleDeleteRegion">鍒犻櫎</el-button>
+ </div>
</div>
<div class="left-search">
@@ -22,15 +30,18 @@
animated />
<template v-else>
<div class="region-item"
- :class="{ active: selectedRegion === '' }"
+ :class="{ active: selectedRegionId === 0 }"
@click="selectRegion('')">鍏ㄩ儴</div>
- <div v-for="item in filteredRegions"
- :key="item.__key"
- class="region-item"
- :class="{ active: selectedRegion === item.regionName }"
- @click="selectRegion(item.regionName)"
- :title="item.regionName">{{ item.regionName }}</div>
- <div v-if="filteredRegions.length === 0"
+ <el-tree ref="regionTreeRef"
+ :data="regionTreeData"
+ node-key="id"
+ :props="regionTreeProps"
+ :filter-node-method="filterRegionNode"
+ highlight-current
+ default-expand-all
+ :expand-on-click-node="false"
+ @node-click="handleRegionNodeClick" />
+ <div v-if="regionTreeData.length === 0"
class="empty-tip">鏆傛棤鍦板尯</div>
</template>
</div>
@@ -96,8 +107,16 @@
@close="closeAddRegionDialog">
<el-form :model="addRegionForm"
label-width="90px">
+ <el-form-item label="涓婄骇鍦板尯">
+ <el-cascader v-model="addRegionForm.parentPath"
+ :options="regionTreeData"
+ :props="regionCascaderProps"
+ clearable
+ filterable
+ placeholder="涓嶉�夊垯涓洪《绾у湴鍖�" />
+ </el-form-item>
<el-form-item label="鍦板尯鍚嶇О">
- <el-input v-model="addRegionForm.regionName"
+ <el-input v-model="addRegionForm.regionsName"
placeholder="璇疯緭鍏�"
clearable />
</el-form-item>
@@ -107,6 +126,30 @@
<el-button type="primary"
@click="submitAddRegion">纭</el-button>
<el-button @click="closeAddRegionDialog">鍙栨秷</el-button>
+ </div>
+ </template>
+ </el-dialog>
+ <el-dialog v-model="editRegionDialogVisible"
+ title="淇敼鍦板尯"
+ width="420px"
+ @close="closeEditRegionDialog">
+ <el-form :model="editRegionForm"
+ label-width="90px">
+ <el-form-item label="涓婄骇鍦板尯">
+ <el-input :value="editRegionParentLabel"
+ disabled />
+ </el-form-item>
+ <el-form-item label="鍦板尯鍚嶇О">
+ <el-input v-model="editRegionForm.regionsName"
+ placeholder="璇疯緭鍏�"
+ clearable />
+ </el-form-item>
+ </el-form>
+ <template #footer>
+ <div class="dialog-footer">
+ <el-button type="primary"
+ @click="submitEditRegion">纭</el-button>
+ <el-button @click="closeEditRegionDialog">鍙栨秷</el-button>
</div>
</template>
</el-dialog>
@@ -139,6 +182,19 @@
</el-row>
<el-row :gutter="30">
<el-col :span="12">
+ <el-form-item label="瀹㈡埛鍦板尯锛�"
+ prop="regions">
+ <el-cascader v-model="formRegionPath"
+ :options="regionTreeData"
+ :props="regionCascaderProps"
+ clearable
+ filterable
+ style="width: 100%"
+ placeholder="璇烽�夋嫨"
+ @change="handleFormRegionChange" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
<el-form-item label="瀹㈡埛鍦板潃锛�"
prop="companyAddress">
<el-input v-model="form.companyAddress"
@@ -146,15 +202,6 @@
clearable />
</el-form-item>
</el-col>
- <el-col :span="12">
- <el-form-item label="瀹㈡埛鍦板尯锛�"
- prop="regions">
- <el-input v-model="form.regions"
- placeholder="璇疯緭鍏�"
- clearable />
- </el-form-item>
- </el-col>
-
</el-row>
<el-row :gutter="30">
<el-col :span="12">
@@ -350,7 +397,7 @@
</el-col>
<el-col :span="12">
<div class="info-item">
- <span class="info-label">鍏徃鐢佃瘽锛�</span>
+ <span class="info-label">瀹㈡埛鐢佃瘽锛�</span>
<span class="info-value">{{ detailForm.companyPhone }}</span>
</div>
</el-col>
@@ -358,52 +405,60 @@
<el-row :gutter="20">
<el-col :span="12">
<div class="info-item">
- <span class="info-label">鍏徃鍦板潃锛�</span>
+ <span class="info-label">瀹㈡埛鍦板尯锛�</span>
+ <span class="info-value">{{ detailForm.regionsName || detailForm.regions }}</span>
+ </div>
+ </el-col>
+ <el-col :span="12">
+ <div class="info-item">
+ <span class="info-label">瀹㈡埛鍦板潃锛�</span>
<span class="info-value">{{ detailForm.companyAddress }}</span>
</div>
</el-col>
+ </el-row>
+ <el-row :gutter="20">
<el-col :span="12">
<div class="info-item">
<span class="info-label">閾惰鍩烘湰鎴凤細</span>
<span class="info-value">{{ detailForm.basicBankAccount }}</span>
</div>
</el-col>
- </el-row>
- <el-row :gutter="20">
<el-col :span="12">
<div class="info-item">
<span class="info-label">閾惰璐﹀彿锛�</span>
<span class="info-value">{{ detailForm.bankAccount }}</span>
</div>
</el-col>
+ </el-row>
+ <el-row :gutter="20">
<el-col :span="12">
<div class="info-item">
<span class="info-label">寮�鎴疯鍙凤細</span>
<span class="info-value">{{ detailForm.bankCode }}</span>
</div>
</el-col>
- </el-row>
- <el-row :gutter="20">
<el-col :span="12">
<div class="info-item">
<span class="info-label">鑱旂郴浜猴細</span>
<span class="info-value">{{ detailForm.contactPerson }}</span>
</div>
</el-col>
+ </el-row>
+ <el-row :gutter="20">
<el-col :span="12">
<div class="info-item">
<span class="info-label">鑱旂郴鐢佃瘽锛�</span>
<span class="info-value">{{ detailForm.contactPhone }}</span>
</div>
</el-col>
- </el-row>
- <el-row :gutter="20">
<el-col :span="12">
<div class="info-item">
<span class="info-label">缁存姢浜猴細</span>
<span class="info-value">{{ detailForm.maintainer }}</span>
</div>
</el-col>
+ </el-row>
+ <el-row :gutter="20">
<el-col :span="12">
<div class="info-item">
<span class="info-label">缁存姢鏃堕棿锛�</span>
@@ -428,7 +483,7 @@
</template>
<script setup>
- import { onMounted, ref, reactive, getCurrentInstance, toRefs, computed } from "vue";
+ import { onMounted, ref, reactive, getCurrentInstance, toRefs, computed, watch } from "vue";
import { Search } from "@element-plus/icons-vue";
import {
addCustomer,
@@ -436,6 +491,9 @@
getCustomer,
listCustomer,
listCustomerRegions,
+ addCustomerRegions,
+ updateCustomerRegions,
+ delCustomerRegions,
updateCustomer,
// addCustomerFollow,
// updateCustomerFollow,
@@ -497,6 +555,8 @@
const detailDialogVisible = ref(false);
const detailForm = reactive({
customerName: "",
+ regionsName: "",
+ regions: "",
customerType: "",
taxpayerIdentificationNumber: "",
companyPhone: "",
@@ -532,7 +592,7 @@
},
{
label: "瀹㈡埛鍦板尯",
- prop: "regions",
+ prop: "regionsName",
width: 120,
},
{
@@ -654,11 +714,15 @@
customerName: "",
customerType: "",
regions: "",
+ regionsId: "",
},
form: {
customerName: "",
taxpayerIdentificationNumber: "",
companyAddress: "",
+ regions: "",
+ regionsId: "",
+ regionsld: "",
companyPhone: "",
contactPerson: "",
contactPhone: "",
@@ -675,6 +739,7 @@
{ required: true, message: "璇疯緭鍏�", trigger: "blur" },
],
companyAddress: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+ regions: [{ required: true, message: "璇烽�夋嫨瀹㈡埛鍦板尯", trigger: "change" }],
companyPhone: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
// contactPerson: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
// contactPhone: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
@@ -746,19 +811,48 @@
const { searchForm, form, rules } = toRefs(data);
// 宸︿晶鍦板尯鏍�
+ const regionTreeRef = ref();
const regionsLoading = ref(false);
- const regions = ref([]);
+ const regionTreeData = ref([]);
const regionKeyword = ref("");
- const selectedRegion = ref(""); // '' 琛ㄧず鍏ㄩ儴
+ const selectedRegionId = ref(0); // 0 琛ㄧず鍏ㄩ儴
+ const selectedRegionNode = ref(null);
+ const formRegionPath = ref([]);
const addRegionDialogVisible = ref(false);
- const addRegionForm = reactive({ regionName: "" });
+ const editRegionDialogVisible = ref(false);
+ const addRegionForm = reactive({ parentPath: [], regionsName: "" });
+ const editRegionForm = reactive({ id: undefined, parentId: 0, regionsName: "" });
+ const regionTreeProps = { label: "label", children: "children" };
+ const regionCascaderProps = {
+ value: "id",
+ label: "label",
+ children: "children",
+ checkStrictly: true,
+ emitPath: true,
+ };
+ const regionNodeMap = computed(() => {
+ const map = new Map();
+ const walk = list => {
+ (list || []).forEach(node => {
+ map.set(node.id, node);
+ walk(node.children || []);
+ });
+ };
+ walk(regionTreeData.value);
+ return map;
+ });
+ const editRegionParentLabel = computed(() => {
+ if (!editRegionForm.parentId) return "椤剁骇鍦板尯";
+ return regionNodeMap.value.get(editRegionForm.parentId)?.label || "鏈煡";
+ });
- const normalizeRegionItem = (raw, index) => {
- if (typeof raw === "string") {
- return { regionName: raw, __key: `s_${raw}_${index}`, __local: false };
- }
- const name = raw?.regionName ?? raw?.regions ?? raw?.name ?? raw?.label ?? "";
- return { ...raw, regionName: name, __key: raw?.id ?? `o_${name}_${index}` };
+ const normalizeRegionTree = list => {
+ return (list || []).map(item => ({
+ ...item,
+ label: item.label || item.regionsName || "",
+ regionsName: item.regionsName || item.label || "",
+ children: normalizeRegionTree(item.children || []),
+ }));
};
const fetchRegions = async () => {
@@ -766,50 +860,125 @@
try {
const res = await listCustomerRegions({});
const list = res?.data ?? res?.rows ?? res ?? [];
- regions.value = Array.isArray(list)
- ? list.map(normalizeRegionItem).filter(i => i.regionName)
- : [];
+ regionTreeData.value = Array.isArray(list) ? normalizeRegionTree(list) : [];
} catch (e) {
console.error("鍦板尯鏌ヨ澶辫触:", e);
- regions.value = [];
+ regionTreeData.value = [];
} finally {
regionsLoading.value = false;
}
};
- const filteredRegions = computed(() => {
- const kw = (regionKeyword.value || "").trim();
- if (!kw) return regions.value;
- return regions.value.filter(r => (r.regionName || "").includes(kw));
- });
+ const filterRegionNode = (value, data) => {
+ if (!value) return true;
+ return (data.label || "").includes(value);
+ };
const selectRegion = regionName => {
- selectedRegion.value = regionName ?? "";
- searchForm.value.regions = selectedRegion.value || "";
+ selectedRegionId.value = 0;
+ selectedRegionNode.value = null;
+ searchForm.value.regions = regionName || "";
+ searchForm.value.regionsId = "";
+ handleQuery();
+ };
+ const handleRegionNodeClick = data => {
+ selectedRegionId.value = data.id;
+ selectedRegionNode.value = data;
+ searchForm.value.regions = data.regionsName || data.label || "";
+ searchForm.value.regionsId = data.id;
handleQuery();
};
const openAddRegionDialog = () => {
- addRegionForm.regionName = "";
+ addRegionForm.parentPath = [];
+ addRegionForm.regionsName = "";
addRegionDialogVisible.value = true;
};
const closeAddRegionDialog = () => {
addRegionDialogVisible.value = false;
- addRegionForm.regionName = "";
+ addRegionForm.parentPath = [];
+ addRegionForm.regionsName = "";
};
- const submitAddRegion = () => {
- const name = (addRegionForm.regionName || "").trim();
+ const submitAddRegion = async () => {
+ const name = (addRegionForm.regionsName || "").trim();
if (!name) return proxy.$modal.msgWarning("璇疯緭鍏ュ湴鍖哄悕绉�");
- const exists = regions.value.some(r => r.regionName === name);
- if (!exists) {
- regions.value.unshift({
- regionName: name,
- __key: `local_${Date.now()}`,
- __local: true,
- });
- }
- proxy.$modal.msgWarning("鍦板尯鏂板鎺ュ彛鏈彁渚涳紝宸叉湰鍦版柊澧烇紙鍒锋柊鍚庡け鏁堬級");
+ const parentPath = addRegionForm.parentPath || [];
+ const parentId = parentPath.length ? parentPath[parentPath.length - 1] : 0;
+ await addCustomerRegions({ parentId, regionsName: name });
+ proxy.$modal.msgSuccess("鏂板鎴愬姛");
+ await fetchRegions();
closeAddRegionDialog();
+ };
+
+ const openEditRegionDialog = () => {
+ if (!selectedRegionNode.value || selectedRegionId.value === 0) {
+ return proxy.$modal.msgWarning("璇峰厛閫夋嫨瑕佷慨鏀圭殑鍦板尯");
+ }
+ editRegionForm.id = selectedRegionNode.value.id;
+ editRegionForm.parentId = selectedRegionNode.value.parentId || 0;
+ editRegionForm.regionsName =
+ selectedRegionNode.value.regionsName || selectedRegionNode.value.label || "";
+ editRegionDialogVisible.value = true;
+ };
+ const closeEditRegionDialog = () => {
+ editRegionDialogVisible.value = false;
+ editRegionForm.id = undefined;
+ editRegionForm.parentId = 0;
+ editRegionForm.regionsName = "";
+ };
+ const submitEditRegion = async () => {
+ const name = (editRegionForm.regionsName || "").trim();
+ if (!name) return proxy.$modal.msgWarning("璇疯緭鍏ュ湴鍖哄悕绉�");
+ await updateCustomerRegions({
+ id: editRegionForm.id,
+ parentId: editRegionForm.parentId,
+ regionsName: name,
+ });
+ proxy.$modal.msgSuccess("淇敼鎴愬姛");
+ await fetchRegions();
+ closeEditRegionDialog();
+ };
+ const handleDeleteRegion = () => {
+ if (!selectedRegionNode.value || selectedRegionId.value === 0) {
+ return proxy.$modal.msgWarning("璇峰厛閫夋嫨瑕佸垹闄ょ殑鍦板尯");
+ }
+ ElMessageBox.confirm("鍒犻櫎鍚庝笉鍙仮澶嶏紝鏄惁缁х画锛�", "鍒犻櫎鍦板尯", {
+ confirmButtonText: "纭",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ })
+ .then(async () => {
+ await delCustomerRegions(selectedRegionNode.value.id);
+ proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+ selectRegion("");
+ await fetchRegions();
+ })
+ .catch(() => {});
+ };
+
+ const handleFormRegionChange = value => {
+ const ids = value || [];
+ if (!ids.length) {
+ form.value.regions = "";
+ form.value.regionsId = "";
+ form.value.regionsld = "";
+ return;
+ }
+ const lastId = ids[ids.length - 1];
+ form.value.regions = regionNodeMap.value.get(lastId)?.regionsName || "";
+ form.value.regionsId = lastId;
+ form.value.regionsld = lastId;
+ };
+ const findRegionPathByName = (tree, targetName, parentPath = []) => {
+ for (const item of tree || []) {
+ const currentPath = [...parentPath, item.id];
+ if ((item.regionsName || item.label) === targetName) {
+ return currentPath;
+ }
+ const childResult = findRegionPathByName(item.children || [], targetName, currentPath);
+ if (childResult.length) return childResult;
+ }
+ return [];
};
const addNewContact = () => {
formYYs.value.contactList.push({
@@ -864,6 +1033,9 @@
const openForm = (type, row) => {
operationType.value = type;
form.value = {};
+ formRegionPath.value = [];
+ form.value.regionsId = "";
+ form.value.regionsld = "";
form.value.maintainer = userStore.nickName;
formYYs.value.contactList = [
{
@@ -878,6 +1050,16 @@
if (type === "edit") {
getCustomer(row.id).then(res => {
form.value = { ...res.data };
+ formRegionPath.value = findRegionPathByName(
+ regionTreeData.value,
+ form.value.regions || ""
+ );
+ const selectedRegionId =
+ formRegionPath.value.length > 0
+ ? formRegionPath.value[formRegionPath.value.length - 1]
+ : "";
+ form.value.regionsId = form.value.regionsId || selectedRegionId;
+ form.value.regionsld = form.value.regionsld || form.value.regionsId || selectedRegionId;
formYYs.value.contactList = res.data.contactPerson
.split(",")
.map((item, index) => {
@@ -913,6 +1095,10 @@
form.value.contactPhone = formYYs.value.contactList
.map(item => item.contactPhone)
.join(",");
+ if (!form.value.regionsId && formRegionPath.value.length) {
+ form.value.regionsId = formRegionPath.value[formRegionPath.value.length - 1];
+ }
+ form.value.regionsld = form.value.regionsId || "";
addCustomer(form.value).then(res => {
proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
closeDia();
@@ -927,6 +1113,10 @@
form.value.contactPhone = formYYs.value.contactList
.map(item => item.contactPhone)
.join(",");
+ if (!form.value.regionsId && formRegionPath.value.length) {
+ form.value.regionsId = formRegionPath.value[formRegionPath.value.length - 1];
+ }
+ form.value.regionsld = form.value.regionsId || "";
updateCustomer(form.value).then(res => {
proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
closeDia();
@@ -936,6 +1126,7 @@
// 鍏抽棴寮规
const closeDia = () => {
proxy.resetForm("formRef");
+ formRegionPath.value = [];
dialogFormVisible.value = false;
};
// 瀵煎嚭
@@ -1039,6 +1230,10 @@
onMounted(() => {
fetchRegions();
getList();
+ });
+
+ watch(regionKeyword, value => {
+ regionTreeRef.value?.filter((value || "").trim());
});
</script>
@@ -1153,6 +1348,16 @@
color: #303133;
}
+ .left-actions {
+ display: flex;
+ gap: 6px;
+ }
+
+ .left-actions :deep(.el-button) {
+ margin-left: 0;
+ padding: 5px 10px;
+ }
+
.left-search {
margin-bottom: 10px;
}
@@ -1184,6 +1389,19 @@
font-weight: 600;
}
+ .left-list :deep(.el-tree) {
+ background: transparent;
+ }
+
+ .left-list :deep(.el-tree-node__content) {
+ height: 30px;
+ border-radius: 6px;
+ }
+
+ .left-list :deep(.el-tree-node__content:hover) {
+ background: #f5f7fa;
+ }
+
.empty-tip {
padding: 16px 10px;
color: #909399;
--
Gitblit v1.9.3