Merge branch 'master' into feature/20261103-bfi-warehouse-web
# Conflicts: # src/option/settlement/formalSettlement.js
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>
|
||||
@@ -755,6 +761,96 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<template v-if="showProjectContractSection">
|
||||
<div class="dialog-section-title project-apply-form__table-title">项目合同</div>
|
||||
<el-table
|
||||
v-loading="projectContractLoading"
|
||||
:data="projectContractRows"
|
||||
border
|
||||
class="project-apply-form__table"
|
||||
>
|
||||
<el-table-column type="index" label="序号" width="80" align="center" />
|
||||
<el-table-column
|
||||
prop="contractCategory"
|
||||
label="合同类型"
|
||||
min-width="120"
|
||||
align="center"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
prop="contractNo"
|
||||
label="合同编号"
|
||||
min-width="140"
|
||||
align="center"
|
||||
show-overflow-tooltip
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-link
|
||||
v-if="row.id && row.contractNo"
|
||||
type="primary"
|
||||
@click.stop="openProjectContractDetail(row)"
|
||||
>
|
||||
{{ row.contractNo }}
|
||||
</el-link>
|
||||
<span v-else>{{ row.contractNo || '' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="contractName"
|
||||
label="合同名称"
|
||||
min-width="160"
|
||||
align="center"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
prop="partyA"
|
||||
label="甲方"
|
||||
min-width="140"
|
||||
align="center"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
prop="partyB"
|
||||
label="乙方"
|
||||
min-width="140"
|
||||
align="center"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
prop="partyC"
|
||||
label="丙方"
|
||||
min-width="140"
|
||||
align="center"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
prop="startDate"
|
||||
label="开始日期"
|
||||
min-width="120"
|
||||
align="center"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
prop="endDate"
|
||||
label="结束日期"
|
||||
min-width="120"
|
||||
align="center"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
prop="contractAmount"
|
||||
label="合同金额"
|
||||
min-width="120"
|
||||
align="center"
|
||||
show-overflow-tooltip
|
||||
>
|
||||
<template #default="{ row }">
|
||||
{{ formatAmount(row.contractAmount) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</template>
|
||||
|
||||
<div
|
||||
v-if="!isChangeDialog && !isNewProjectDialog"
|
||||
class="dialog-section-title project-apply-form__table-title"
|
||||
@@ -984,6 +1080,7 @@
|
||||
import { exportBlob } from '@/api/common';
|
||||
import { defineAsyncComponent } from 'vue';
|
||||
import * as api from '@/api/business/project-apply';
|
||||
import { getList as getContractManageList } from '@/api/business/contract-manage';
|
||||
import {
|
||||
getRisk,
|
||||
loadHcsImmediateEnabled,
|
||||
@@ -996,7 +1093,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';
|
||||
@@ -1340,7 +1437,6 @@ export default {
|
||||
],
|
||||
},
|
||||
deptOptions: [],
|
||||
businessDeptTreeOptions: [],
|
||||
platformCompanyOptions: [],
|
||||
cargoTypeOptions: [],
|
||||
selectedCustomerId: [],
|
||||
@@ -1383,6 +1479,8 @@ export default {
|
||||
'zip',
|
||||
],
|
||||
changeRows: [],
|
||||
projectContractRows: [],
|
||||
projectContractLoading: false,
|
||||
changeRecordDetailVisible: false,
|
||||
changeRecordDetail: null,
|
||||
changeRecordDetailRows: [],
|
||||
@@ -1405,44 +1503,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();
|
||||
},
|
||||
@@ -1489,6 +1557,9 @@ export default {
|
||||
isNewProjectDialog() {
|
||||
return ['add', 'majorSupplement'].includes(this.dialogType);
|
||||
},
|
||||
showProjectContractSection() {
|
||||
return this.dialogReadonly && !this.isChangeDialog && !this.isNewProjectDialog && !this.isPublicViewPage;
|
||||
},
|
||||
isProjectFormPage() {
|
||||
return this.isFormPageInstance;
|
||||
},
|
||||
@@ -1674,9 +1745,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);
|
||||
@@ -2013,15 +2085,20 @@ export default {
|
||||
const run = value => {
|
||||
const args = operation.prompt ? [row.id, value] : [row.id];
|
||||
const request = () => this.api[operation.action](...args);
|
||||
// 先本地提交再调 MK:MK 成功后会把状态同步成 reviewing,
|
||||
// 若先 MK 再本地,已撤回/已驳回单据会被「当前状态不允许编辑」拦住。
|
||||
const afterMk =
|
||||
operation.action === 'submitApproval'
|
||||
? submitMkApprovalFlow({
|
||||
bizType: 'project-apply',
|
||||
bizId: row.id,
|
||||
subjectName: row.projectName || '',
|
||||
approvalStatus: row.approvalStatus || '',
|
||||
mkEnabled: this.mkEnabled,
|
||||
}).then(() => request())
|
||||
? request().then(() =>
|
||||
submitMkApprovalFlow({
|
||||
bizType: 'project-apply',
|
||||
bizId: row.id,
|
||||
subjectName: row.projectName || '',
|
||||
approvalStatus: row.approvalStatus || '',
|
||||
mkEnabled: this.mkEnabled,
|
||||
tempVarData: { projectType: row.projectType || '' },
|
||||
})
|
||||
)
|
||||
: request();
|
||||
// 项目通过会同步等待合规推送,加遮罩防重复点击
|
||||
const loading =
|
||||
@@ -2131,6 +2208,8 @@ export default {
|
||||
this.attachmentRows = [];
|
||||
this.selectedAttachmentRows = [];
|
||||
this.changeRows = [];
|
||||
this.projectContractRows = [];
|
||||
this.projectContractLoading = false;
|
||||
this.selectedCustomerId = [];
|
||||
this.selectedCarrierIds = [];
|
||||
},
|
||||
@@ -2179,6 +2258,40 @@ export default {
|
||||
this.syncSelectedCustomerOptions();
|
||||
this.ensureCurrentPlatformOption();
|
||||
this.ensureCurrentBusinessDeptOption();
|
||||
this.loadProjectContracts(row?.id);
|
||||
},
|
||||
loadProjectContracts(projectId) {
|
||||
if (!this.showProjectContractSection || !projectId) {
|
||||
this.projectContractRows = [];
|
||||
this.projectContractLoading = false;
|
||||
return;
|
||||
}
|
||||
this.projectContractLoading = true;
|
||||
getContractManageList(1, 1000, { projectId })
|
||||
.then(res => {
|
||||
const data = res.data?.data;
|
||||
const records = Array.isArray(data) ? data : data?.records || [];
|
||||
this.projectContractRows = records.filter(
|
||||
item => String(item.projectId) === String(projectId)
|
||||
);
|
||||
})
|
||||
.catch(() => {
|
||||
this.projectContractRows = [];
|
||||
this.$message.error('项目合同加载失败');
|
||||
})
|
||||
.finally(() => {
|
||||
this.projectContractLoading = false;
|
||||
});
|
||||
},
|
||||
openProjectContractDetail(row) {
|
||||
if (!row?.id) {
|
||||
this.$message.warning('合同信息不完整,无法跳转');
|
||||
return;
|
||||
}
|
||||
this.$router.push({
|
||||
path: '/business/contract-manage/detail',
|
||||
query: { id: row.id, name: '合同详情' },
|
||||
});
|
||||
},
|
||||
fillDefaultUsers() {
|
||||
const userId = this.userInfo?.userId || this.userInfo?.id || '';
|
||||
@@ -2265,6 +2378,9 @@ export default {
|
||||
subjectName: name,
|
||||
approvalStatus: status,
|
||||
mkEnabled: this.mkEnabled,
|
||||
tempVarData: {
|
||||
projectType: this.form.projectType || data.projectType || '',
|
||||
},
|
||||
}).then(() => {
|
||||
this.$message.success('提交成功!');
|
||||
this.closeProjectForm();
|
||||
@@ -2385,6 +2501,7 @@ export default {
|
||||
subjectName: this.form.projectName || '',
|
||||
approvalStatus,
|
||||
mkEnabled: this.mkEnabled,
|
||||
tempVarData: { projectType: this.form.projectType || '' },
|
||||
}).then(() => {
|
||||
this.$message.success('提交成功!');
|
||||
this.closeProjectForm();
|
||||
@@ -2410,6 +2527,7 @@ export default {
|
||||
subjectName: this.form.projectName || '',
|
||||
approvalStatus: this.form.approvalStatus || 'change_rejected',
|
||||
mkEnabled: this.mkEnabled,
|
||||
tempVarData: { projectType: this.form.projectType || '' },
|
||||
}).then(() => {
|
||||
this.$message.success('提交成功!');
|
||||
this.closeProjectForm();
|
||||
@@ -2813,10 +2931,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) {
|
||||
@@ -2854,77 +2972,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 => {
|
||||
@@ -2956,50 +3041,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