✨ 结算汇率反算、开票合计填报、配载承运商交集与车辆组织悬停

外币结算按本位币/汇率反算;开票改填合计金额并反算不含税单价;配载展示承运商合同并按乙方交集限选;评分加减分封顶15分;车辆所有组织/使用部门悬停显示完整路径。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-10-26 10:50:58 +08:00
parent fa78a68df6
commit e1d87f5788
10 changed files with 431 additions and 131 deletions
+149 -23
View File
@@ -59,7 +59,22 @@
</el-button>
</template>
<template #organizationName="{ row }">
{{ String(row.organizationName || '').replace(/^[\s\u3000]+/, '') }}
<el-tooltip
:content="formatOrgDisplayName(row.organizationName)"
placement="top"
:disabled="!formatOrgDisplayName(row.organizationName)"
>
<span class="org-cell-text">{{ formatOrgDisplayName(row.organizationName) }}</span>
</el-tooltip>
</template>
<template #useDepartment="{ row }">
<el-tooltip
:content="formatOrgDisplayName(row.useDepartment)"
placement="top"
:disabled="!formatOrgDisplayName(row.useDepartment)"
>
<span class="org-cell-text">{{ formatOrgDisplayName(row.useDepartment) }}</span>
</el-tooltip>
</template>
<template #status="{ row }">
<el-tag :type="row.status === 1 ? 'primary' : 'info'" class="status-text">
@@ -315,26 +330,58 @@
</el-col>
<el-col :span="6">
<el-form-item label="所有组织" prop="organizationName">
<el-cascader
v-model="organizationCascaderValue"
:options="organizationTreeOptions"
:props="organizationCascaderProps"
filterable
clearable
placeholder="请选择"
/>
<el-tooltip
:content="
formatOrgCascaderPath(organizationCascaderPath) ||
formatOrgDisplayName(vehicleForm.organizationName)
"
placement="top"
:disabled="
!(
formatOrgCascaderPath(organizationCascaderPath) ||
formatOrgDisplayName(vehicleForm.organizationName)
)
"
>
<div class="org-cascader-tooltip">
<el-cascader
v-model="organizationCascaderValue"
:options="organizationFormTreeOptions"
:props="formOrgCascaderProps"
filterable
clearable
placeholder="请选择"
/>
</div>
</el-tooltip>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="使用部门" prop="useDepartment">
<el-cascader
v-model="useDepartmentCascaderValue"
:options="organizationTreeOptions"
:props="organizationCascaderProps"
filterable
clearable
placeholder="请选择"
/>
<el-tooltip
:content="
formatOrgCascaderPath(useDepartmentCascaderPath) ||
formatOrgDisplayName(vehicleForm.useDepartment)
"
placement="top"
:disabled="
!(
formatOrgCascaderPath(useDepartmentCascaderPath) ||
formatOrgDisplayName(vehicleForm.useDepartment)
)
"
>
<div class="org-cascader-tooltip">
<el-cascader
v-model="useDepartmentCascaderValue"
:options="useDepartmentFormTreeOptions"
:props="formOrgCascaderProps"
filterable
clearable
placeholder="请选择"
/>
</div>
</el-tooltip>
</el-form-item>
</el-col>
</el-row>
@@ -652,6 +699,17 @@ export default {
vehicleCertificateUploads: {},
uploadHeaders: getUploadHeaders(),
organizationOptions: [],
// 表单两个级联各自维护路径,避免名称回写互相覆盖;不要与搜索栏共用同一棵树节点
organizationCascaderPath: [],
useDepartmentCascaderPath: [],
formOrgCascaderProps: {
value: 'id',
label: 'label',
children: 'children',
emitPath: true,
checkStrictly: true,
expandTrigger: 'hover',
},
vehicleTypeOptions: [
{ label: '高栏车', value: '高栏' },
{ label: '低栏车', value: '低栏' },
@@ -697,22 +755,31 @@ export default {
: [{ required: true, message: '请选择行驶证有效期', trigger: 'change' }],
};
},
// 所属组织级联:表单存组织名称,级联控件用 id 路径,两者在此双向转换
// 两个级联必须使用克隆后的独立树,否则 checkStrictly 会在节点上互相抢选中态
organizationFormTreeOptions() {
return this.cloneOrgTree(this.organizationTreeOptions);
},
useDepartmentFormTreeOptions() {
return this.cloneOrgTree(this.organizationTreeOptions);
},
// 所属组织级联:本地保存 id 路径,名称同步到表单字段供后端使用
organizationCascaderValue: {
get() {
return this.resolveOrgPath(this.vehicleForm.organizationName);
return this.organizationCascaderPath;
},
set(path) {
this.vehicleForm.organizationName = this.resolveOrgName(path);
this.organizationCascaderPath = Array.isArray(path) ? [...path] : [];
this.vehicleForm.organizationName = this.resolveOrgName(this.organizationCascaderPath);
},
},
// 使用部门级联:与所属组织同一套部门树
// 使用部门级联:独立路径状态,避免与所属组织共用计算回写导致选不中
useDepartmentCascaderValue: {
get() {
return this.resolveOrgPath(this.vehicleForm.useDepartment);
return this.useDepartmentCascaderPath;
},
set(path) {
this.vehicleForm.useDepartment = this.resolveOrgName(path);
this.useDepartmentCascaderPath = Array.isArray(path) ? [...path] : [];
this.vehicleForm.useDepartment = this.resolveOrgName(this.useDepartmentCascaderPath);
},
},
isAdmin() {
@@ -745,10 +812,49 @@ export default {
this.initDeptTree();
this.loadOrganizationOptions();
},
watch: {
organizationTreeOptions() {
if (this.vehicleBox) {
this.syncOrgCascaderPaths();
}
},
},
methods: {
hasPermission(code) {
return this.isAdmin || this.permission?.[code] === true;
},
/** 去掉组织名称前导空白,供列表与提示展示 */
formatOrgDisplayName(name) {
return String(name || '').replace(/^[\s\u3000]+/, '');
},
/** 级联路径还原完整组织层级文案,悬停展示用 */
formatOrgCascaderPath(path) {
const ids = Array.isArray(path)
? path.filter(item => item !== undefined && item !== null && item !== '')
: [];
if (!ids.length) return '';
const labels = [];
let nodes = this.organizationTreeOptions || [];
for (const id of ids) {
const node = nodes.find(item => String(item.id) === String(id));
if (!node) break;
labels.push(node.rawLabel || node.label || '');
nodes = node.children || [];
}
return labels.filter(Boolean).join(' / ');
},
cloneOrgTree(tree = []) {
return (tree || []).map(item => ({
id: String(item.id),
label: item.label,
rawLabel: item.rawLabel,
children: item.children?.length ? this.cloneOrgTree(item.children) : undefined,
}));
},
syncOrgCascaderPaths() {
this.organizationCascaderPath = this.resolveOrgPath(this.vehicleForm.organizationName);
this.useDepartmentCascaderPath = this.resolveOrgPath(this.vehicleForm.useDepartment);
},
initDeptTree() {
getDeptTree(this.userInfo.tenantId).then(res => {
this.organizationOptions = this.flattenDeptOptions(res.data.data || []);
@@ -760,6 +866,9 @@ export default {
if (this.vehicleBox && !this.vehicleForm.useDepartment) {
this.vehicleForm.useDepartment = this.getCurrentOrganizationName();
}
if (this.vehicleBox) {
this.syncOrgCascaderPaths();
}
});
},
flattenDeptOptions(tree = [], level = 0) {
@@ -854,6 +963,7 @@ export default {
this.vehicleForm = emptyForm();
this.vehicleForm.organizationName = this.getCurrentOrganizationName();
this.vehicleForm.useDepartment = this.getCurrentOrganizationName();
this.syncOrgCascaderPaths();
this.syncPlateNo(false);
this.vehicleBox = true;
return;
@@ -867,6 +977,7 @@ export default {
if (!this.vehicleForm.useDepartment) {
this.vehicleForm.useDepartment = this.getCurrentOrganizationName();
}
this.syncOrgCascaderPaths();
this.splitPlateNo();
this.syncPlateNo(false);
this.vehicleBox = true;
@@ -878,6 +989,8 @@ export default {
},
resetVehicle() {
this.vehicleForm = emptyForm();
this.organizationCascaderPath = [];
this.useDepartmentCascaderPath = [];
this.vehicleCertificateUploads = {};
this.readonly = false;
this.submitLoading = false;
@@ -1530,7 +1643,20 @@ export default {
}
}
.org-cell-text {
display: inline-block;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
vertical-align: bottom;
}
.vehicle-form {
.org-cascader-tooltip {
width: 100%;
}
:deep(.el-date-editor.el-input),
:deep(.el-date-editor.el-input__wrapper),
:deep(.el-select),