✨ 新增业务部门配置页并简化项目/合同部门选择
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -258,15 +258,21 @@
|
||||
<div class="project-apply-form__field-tooltip">{{ businessDeptTooltipText }}</div>
|
||||
</template>
|
||||
<span class="project-apply-form__field-tooltip-trigger">
|
||||
<el-cascader
|
||||
v-model="businessDeptCascaderValue"
|
||||
:options="businessDeptTreeOptions"
|
||||
:props="deptCascaderProps"
|
||||
<el-select
|
||||
v-model="form.businessDeptId"
|
||||
placeholder="请选择"
|
||||
filterable
|
||||
clearable
|
||||
:disabled="dialogReadonly"
|
||||
/>
|
||||
@change="handleBusinessDeptChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in deptOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</span>
|
||||
</el-tooltip>
|
||||
</el-form-item>
|
||||
@@ -995,7 +1001,7 @@ import {
|
||||
getList as getCustomerArchiveList,
|
||||
getDetail as getCustomerArchiveDetail,
|
||||
} from '@/api/vehicle/customer-archive';
|
||||
import { getBusinessDeptSelect, getDeptTree, getPlatformCompanySelect } from '@/api/system/dept';
|
||||
import { getBusinessDeptSelect, getPlatformCompanySelect } from '@/api/system/dept';
|
||||
import { getDictionary as getSystemDictionary } from '@/api/system/dict';
|
||||
import { getDictionary as getBizDictionary } from '@/api/system/dictbiz';
|
||||
import { getList as getUserList } from '@/api/system/user';
|
||||
@@ -1339,7 +1345,6 @@ export default {
|
||||
],
|
||||
},
|
||||
deptOptions: [],
|
||||
businessDeptTreeOptions: [],
|
||||
platformCompanyOptions: [],
|
||||
cargoTypeOptions: [],
|
||||
selectedCustomerId: [],
|
||||
@@ -1404,44 +1409,14 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['permission', 'userInfo']),
|
||||
// 部门级联配置:checkStrictly 允许选中任意层级,不必选到末级
|
||||
deptCascaderProps() {
|
||||
return {
|
||||
label: 'label',
|
||||
value: 'value',
|
||||
children: 'children',
|
||||
emitPath: true,
|
||||
checkStrictly: true,
|
||||
expandTrigger: 'click',
|
||||
};
|
||||
},
|
||||
// 业务部门:form 存部门 id,级联控件用根到节点的 id 路径,两者在此双向转换
|
||||
businessDeptCascaderValue: {
|
||||
get() {
|
||||
const id = this.form.businessDeptId;
|
||||
return id ? this.findDeptPath(id, this.businessDeptTreeOptions) || [id] : [];
|
||||
},
|
||||
set(path) {
|
||||
const value = Array.isArray(path)
|
||||
? path.filter(item => item !== undefined && item !== null && item !== '')
|
||||
: [];
|
||||
this.form.businessDeptId = value.length ? value.at(-1) : '';
|
||||
this.handleBusinessDeptChange(this.form.businessDeptId);
|
||||
},
|
||||
},
|
||||
// 查看态禁用控件无 hover,用完整路径/名称做 tooltip
|
||||
// 查看态禁用控件无 hover,用业务部门名称做 tooltip
|
||||
businessDeptTooltipText() {
|
||||
const pathIds = this.businessDeptCascaderValue || [];
|
||||
if (pathIds.length && this.businessDeptTreeOptions?.length) {
|
||||
const labels = [];
|
||||
let nodes = this.businessDeptTreeOptions;
|
||||
for (const id of pathIds) {
|
||||
const node = (nodes || []).find(item => String(item.value) === String(id));
|
||||
if (!node) break;
|
||||
labels.push(node.label);
|
||||
nodes = node.children;
|
||||
const id = this.form.businessDeptId;
|
||||
if (id) {
|
||||
const matched = this.deptOptions.find(item => String(item.value) === String(id));
|
||||
if (matched?.rawLabel || matched?.label) {
|
||||
return matched.rawLabel || matched.label;
|
||||
}
|
||||
if (labels.length) return labels.join(' / ');
|
||||
}
|
||||
return String(this.form.businessDeptName || '').trim();
|
||||
},
|
||||
@@ -1673,9 +1648,10 @@ export default {
|
||||
detail.settlementMode
|
||||
);
|
||||
if (detail.businessDeptId) {
|
||||
const label = detail.businessDeptName || String(detail.businessDeptId);
|
||||
this.businessDeptTreeOptions = [{ label, value: detail.businessDeptId }];
|
||||
this.deptOptions = [{ label, rawLabel: label, value: detail.businessDeptId }];
|
||||
const id = String(detail.businessDeptId);
|
||||
const label = detail.businessDeptName || id;
|
||||
this.form.businessDeptId = id;
|
||||
this.deptOptions = [{ label, rawLabel: label, value: id }];
|
||||
}
|
||||
const undertakeDeptIds = this.parseUndertakeDeptIds(detail.undertakeDeptId);
|
||||
const undertakeDeptNames = this.parseUndertakeDeptNames(detail.undertakeDeptName);
|
||||
@@ -2822,10 +2798,10 @@ export default {
|
||||
return `${base}-BG${this.$dayjs().format('YYYYMMDD')}`;
|
||||
},
|
||||
handleBusinessDeptChange(value) {
|
||||
// 兼容级联路径数组(取末级)与单个部门 id
|
||||
const id = Array.isArray(value) ? value.at(-1) : value;
|
||||
const dept = this.deptOptions.find(item => String(item.value) === String(id));
|
||||
this.form.businessDeptName = dept ? dept.rawLabel : '';
|
||||
const id = value == null || value === '' ? '' : String(value);
|
||||
this.form.businessDeptId = id;
|
||||
const dept = this.deptOptions.find(item => String(item.value) === id);
|
||||
this.form.businessDeptName = dept ? dept.rawLabel || dept.label : '';
|
||||
this.$refs.projectForm?.validateField('businessDeptId');
|
||||
},
|
||||
handleUndertakeDeptChange(value) {
|
||||
@@ -2863,77 +2839,44 @@ export default {
|
||||
firstUndertakeDeptId(value) {
|
||||
return this.parseUndertakeDeptIds(value)[0] || '';
|
||||
},
|
||||
// 由部门 id 反查「根 → 该节点」的 id 路径,供级联回显(找不到返回 null)
|
||||
findDeptPath(id, tree = [], parents = []) {
|
||||
const target = Array.isArray(id) ? id.at(-1) : id;
|
||||
if (target === undefined || target === null || target === '') return null;
|
||||
for (const node of tree || []) {
|
||||
const path = [...parents, node.value];
|
||||
if (String(node.value) === String(target)) return path;
|
||||
const matched = this.findDeptPath(target, node.children, path);
|
||||
if (matched) return matched;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
handleProjectTypeChange() {
|
||||
this.normalizeAttachmentFileTypes();
|
||||
this.$refs.projectForm?.validateField('projectType');
|
||||
},
|
||||
loadDeptOptions() {
|
||||
Promise.all([getDeptTree(), getBusinessDeptSelect()])
|
||||
.then(([treeRes, bizRes]) => {
|
||||
const deptTree = this.excludeExternalOrganization(treeRes.data.data || []);
|
||||
const bizList = bizRes.data.data || [];
|
||||
const bizIds = new Set(
|
||||
bizList
|
||||
.map(item => (item.id == null || item.id === '' ? '' : String(item.id)))
|
||||
.filter(Boolean)
|
||||
);
|
||||
// 仅保留「是否业务部门=是」的节点及其祖先,祖先不可选
|
||||
const businessDeptTree = this.filterBusinessDeptTree(deptTree, bizIds);
|
||||
this.deptOptions = this.flattenDept(businessDeptTree).filter(item =>
|
||||
bizIds.has(String(item.value))
|
||||
);
|
||||
this.businessDeptTreeOptions = this.toDeptCascaderOptions(businessDeptTree, bizIds);
|
||||
getBusinessDeptSelect()
|
||||
.then(res => {
|
||||
const bizList = res.data.data || [];
|
||||
// 仅展示「是否业务部门=是」的本级部门,扁平下拉
|
||||
this.deptOptions = bizList
|
||||
.map(item => {
|
||||
const value = item.id == null || item.id === '' ? '' : String(item.id);
|
||||
const label = item.deptName || item.fullName || item.title || item.name || value;
|
||||
return {
|
||||
label,
|
||||
rawLabel: label,
|
||||
value,
|
||||
};
|
||||
})
|
||||
.filter(item => item.value);
|
||||
this.fillDefaultBusinessDept();
|
||||
this.ensureCurrentBusinessDeptOption();
|
||||
})
|
||||
.catch(() => {
|
||||
this.deptOptions = [];
|
||||
this.businessDeptTreeOptions = [];
|
||||
});
|
||||
this.loadPlatformCompanyOptions();
|
||||
},
|
||||
/**
|
||||
* 剪枝组织树:只保留业务部门节点及其祖先路径。
|
||||
*/
|
||||
filterBusinessDeptTree(list = [], bizIds = new Set()) {
|
||||
return (list || []).reduce((result, item) => {
|
||||
const children = item.children?.length
|
||||
? this.filterBusinessDeptTree(item.children, bizIds)
|
||||
: [];
|
||||
const isBiz = bizIds.has(String(item.id));
|
||||
if (!isBiz && !children.length) {
|
||||
return result;
|
||||
}
|
||||
result.push({
|
||||
...item,
|
||||
children: children.length ? children : undefined,
|
||||
});
|
||||
return result;
|
||||
}, []);
|
||||
},
|
||||
ensureCurrentBusinessDeptOption() {
|
||||
const id = this.form.businessDeptId;
|
||||
const id = this.form.businessDeptId == null || this.form.businessDeptId === ''
|
||||
? ''
|
||||
: String(this.form.businessDeptId);
|
||||
if (!id) return;
|
||||
const exists = this.deptOptions.some(item => String(item.value) === String(id));
|
||||
this.form.businessDeptId = id;
|
||||
const exists = this.deptOptions.some(item => String(item.value) === id);
|
||||
if (exists) return;
|
||||
const label = this.form.businessDeptName || String(id);
|
||||
const label = this.form.businessDeptName || id;
|
||||
this.deptOptions.unshift({ label, rawLabel: label, value: id });
|
||||
const pathExists = this.findDeptPath(id, this.businessDeptTreeOptions);
|
||||
if (!pathExists) {
|
||||
this.businessDeptTreeOptions.unshift({ label, value: id });
|
||||
}
|
||||
},
|
||||
loadPlatformCompanyOptions() {
|
||||
getPlatformCompanySelect().then(res => {
|
||||
@@ -2965,50 +2908,6 @@ export default {
|
||||
return matched ? matched.value : id;
|
||||
});
|
||||
},
|
||||
excludeExternalOrganization(list = []) {
|
||||
return list.reduce((result, item) => {
|
||||
const deptName = item.title || item.deptName || item.name;
|
||||
if (String(deptName || '').trim() === '外部组织') return result;
|
||||
result.push({
|
||||
...item,
|
||||
children: item.children?.length
|
||||
? this.excludeExternalOrganization(item.children)
|
||||
: item.children,
|
||||
});
|
||||
return result;
|
||||
}, []);
|
||||
},
|
||||
toDeptCascaderOptions(list = [], bizIds = null) {
|
||||
return list.map(item => {
|
||||
const value = item.id == null || item.id === '' ? item.value : item.id;
|
||||
const option = {
|
||||
label: item.title || item.deptName || item.name || item.label,
|
||||
value: value == null || value === '' ? '' : String(value),
|
||||
children: item.children?.length
|
||||
? this.toDeptCascaderOptions(item.children, bizIds)
|
||||
: undefined,
|
||||
};
|
||||
// 仅业务部门可选;祖先节点保留路径但禁用
|
||||
if (bizIds && !bizIds.has(String(option.value))) {
|
||||
option.disabled = true;
|
||||
}
|
||||
return option;
|
||||
});
|
||||
},
|
||||
flattenDept(list = [], level = 0) {
|
||||
return list.reduce((result, item) => {
|
||||
const label = item.title || item.deptName || item.name;
|
||||
result.push({
|
||||
label: `${' '.repeat(level)}${label}`,
|
||||
rawLabel: label,
|
||||
value: item.id,
|
||||
});
|
||||
if (item.children && item.children.length) {
|
||||
result.push(...this.flattenDept(item.children, level + 1));
|
||||
}
|
||||
return result;
|
||||
}, []);
|
||||
},
|
||||
loadCargoTypeOptions() {
|
||||
getSystemDictionary({ code: 'type_of_goods' }).then(res => {
|
||||
this.cargoTypeOptions = (res.data.data || []).map(item => ({
|
||||
|
||||
Reference in New Issue
Block a user