refactor(organization): 将所属组织搜索组件统一替换为级联选择器

- 全站搜索栏所属组织由树形下拉 (el-tree-select) 统一替换为级联选择器 (el-cascader)
- 修改多个视图和组件中所属组织相关表单与搜索逻辑,适配级联选择器数据结构
- 组织数据结构调整及新增 findOrgPath、findDeptPath 等方法支持路径反查与回显
- 表单模型与搜索参数中所属组织字段改用 ID 路径数组,提交时转换为名称
- 优化所属组织搜索UI,统一清除、过滤、提示等功能样式和行为
- 引入 organization-search mixin 支持组织数据加载、路径转换及搜索参数格式化
- 更新相关依赖组件 Element Plus 引入 ElCascader 替换 ElTreeSelect
- 修正搜索表单CSS,新增el-cascader全宽样式支持搜索栏布局一致性
- 兼容老数据,支持多种部门ID格式自动转换到单选路径数组格式
- 业务部门与承办部门表单同样应用级联选择器及路径转换逻辑
- 相关搜索配置项所属组织字段类型由 input 改为 cascader,提升搜索交互体验
This commit is contained in:
2026-08-31 12:19:18 +08:00
parent 3a08e12c65
commit 9b9e652afd
26 changed files with 515 additions and 192 deletions
+87 -39
View File
@@ -335,17 +335,13 @@
</el-col>
<el-col :span="6">
<el-form-item label="所属组织" prop="deptName">
<el-tree-select
v-model="archiveForm.deptId"
:data="deptTree"
:props="{ label: 'label', value: 'value', children: 'children' }"
multiple
node-key="value"
check-strictly
filterable
<el-cascader
v-model="deptCascaderValue"
:options="deptTree"
:props="deptCascaderProps"
clearable
:render-after-expand="false"
@change="onDeptChange"
filterable
placeholder="请选择 所属组织"
/>
</el-form-item>
</el-col>
@@ -1381,7 +1377,7 @@
<script>
import { h } from 'vue';
import { ElTreeSelect } from 'element-plus';
import { ElCascader } from 'element-plus';
import {
getList,
getDetail,
@@ -1890,6 +1886,34 @@ export default {
emitPath: true,
};
},
deptCascaderProps() {
return {
value: 'value',
label: 'label',
children: 'children',
emitPath: true,
checkStrictly: true,
expandTrigger: 'click',
};
},
// 所属组织级联值:archiveForm.deptId 只存选中的部门 ID(单选),
// 级联控件需要根到选中节点的完整路径,两者在此做双向转换——
// 部门树是异步加载的,依赖 deptTree 可保证树加载完成后回显自动刷新。
deptCascaderValue: {
get() {
const ids = Array.isArray(this.archiveForm.deptId)
? this.archiveForm.deptId
: this.archiveForm.deptId
? [this.archiveForm.deptId]
: [];
const currentId = ids.length ? String(ids[ids.length - 1]) : '';
if (!currentId) return [];
return this.findDeptPath(currentId) || [currentId];
},
set(path) {
this.onDeptChange(path);
},
},
ids() {
let ids = [];
this.selectionList.forEach(ele => {
@@ -2661,38 +2685,57 @@ export default {
this.onDeptChange([firstTopLevelDept.value]);
},
onDeptChange(value) {
const deptIds = Array.isArray(value) ? value : value ? [value] : [];
const deptNames = deptIds
.map(id => this.deptOptions.find(item => String(item.value) === String(id))?.rawLabel)
.filter(Boolean);
this.archiveForm.deptId = deptIds;
this.archiveForm.deptIds = deptIds.join(',');
this.archiveForm.deptName = deptNames.join(',');
// 级联单选:value 为根到选中节点的 ID 路径,取末级作为客商所属组织
const path = Array.isArray(value)
? value.filter(item => item !== undefined && item !== null && item !== '')
: [];
const deptId = path.length ? String(path[path.length - 1]) : '';
this.archiveForm.deptId = deptId ? [deptId] : [];
this.archiveForm.deptIds = deptId;
this.archiveForm.deptName = deptId ? this.findDeptLabel(deptId) : '';
this.$refs.archiveForm?.validateField('deptName');
},
findDeptPath(id, tree = this.deptTree, parents = []) {
for (const node of tree || []) {
const path = [...parents, String(node.value)];
if (String(node.value) === String(id)) return path;
const children = node.children || [];
if (children.length) {
const matched = this.findDeptPath(id, children, path);
if (matched) return matched;
}
}
return null;
},
findDeptLabel(id, tree = this.deptTree) {
for (const node of tree || []) {
if (String(node.value) === String(id)) return node.label;
const matched = this.findDeptLabel(id, node.children || []);
if (matched) return matched;
}
return '';
},
renderDeptSearch(scope) {
// 搜索区所属组织:多选树形下拉(与新增/编辑表单一致)
// 多选返回 ID 数组,转换为部门名称按逗号拼接写入 searchForm.deptName
return h(ElTreeSelect, {
// 搜索区所属组织:单选级联(与新增/编辑表单一致)
// 级联返回的是根到选中节点的 ID 路径数组,提交时取末级 ID 转部门名称
return h(ElCascader, {
modelValue: Array.isArray(scope.row?.deptName) ? scope.row.deptName : [],
'onUpdate:modelValue': value => {
// 多选模式modelValue 必须是 ID 数组,否则 el-tree-select 内部状态会错乱导致无法再次选中
const ids = Array.isArray(value) ? value : value ? [value] : [];
if (scope.row) scope.row.deptName = ids;
// 级联单选modelValue 必须是 ID 路径数组,否则控件内部状态会错乱导致无法再次选中
if (scope.row) scope.row.deptName = Array.isArray(value) ? value : [];
},
options: this.deptTree,
props: {
label: 'label',
value: 'value',
children: 'children',
checkStrictly: true,
expandTrigger: 'click',
},
data: this.deptTree,
'node-key': 'value',
'check-strictly': true,
multiple: true,
filterable: true,
clearable: true,
'render-after-expand': false,
collapseTags: true,
'collapse-tags-tooltip': true,
'default-expand-all': false,
style: 'width: 100%',
placeholder: '请选择 所属组织',
props: { label: 'label', value: 'value', children: 'children' },
});
},
handleNoFixedTermChange(checked) {
@@ -2733,13 +2776,15 @@ export default {
: [];
},
normalizeDeptIds(detail) {
// 所属组织为单选级联,只保留第一个部门 ID(历史多选数据自动收敛)
const value = detail.deptIds || detail.deptId;
return value
const ids = value
? String(value)
.split(',')
.map(item => item.trim())
.filter(item => item !== '')
: [];
return ids.length ? [ids[0]] : [];
},
formatArchiveAddress(archive = {}) {
return [archive.registeredRegionName, archive.registeredDetailAddress]
@@ -4555,12 +4600,15 @@ export default {
this.onLoad(this.page);
},
searchChange(params, done) {
// 搜索区所属组织多选返回的是部门 ID 数组,转成部门名称逗号串传给后端(与列表接口 deptName 字段一致)
// 搜索区所属组织级联返回的是 ID 路径数组,取末级 ID 转成部门名称传给后端(与列表接口 deptName 字段一致)
if (Array.isArray(params.deptName)) {
params.deptName = params.deptName
.map(id => this.deptOptions.find(item => String(item.value) === String(id))?.rawLabel)
.filter(Boolean)
.join(',');
const path = params.deptName.filter(
item => item !== undefined && item !== null && item !== ''
);
const deptId = path.length ? path[path.length - 1] : '';
params.deptName = deptId
? this.deptOptions.find(item => String(item.value) === String(deptId))?.rawLabel || ''
: '';
}
this.query = this.buildQuery(params);
this.page.currentPage = 1;