调整项目
This commit is contained in:
@@ -66,9 +66,10 @@
|
||||
<template #content>
|
||||
<div>中风险:项目资金额度使用率 ≥ 80%</div>
|
||||
<div>高风险:项目资金额度使用率 ≥ 90%</div>
|
||||
<div>使用率 = 已使用资金 / 项目资金使用额度 × 100%</div>
|
||||
</template>
|
||||
<i class="el-icon-question project-apply-page__risk-help" />
|
||||
<el-icon class="project-apply-page__risk-help">
|
||||
<el-icon-question-filled />
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
</span>
|
||||
<el-check-tag
|
||||
@@ -83,15 +84,31 @@
|
||||
</template>
|
||||
|
||||
<template #fundUseRisk="{ row }">
|
||||
<el-tag v-if="row.fundUseRisk === 'high'" type="danger">
|
||||
{{ row.fundUseRiskName || '高风险' }}
|
||||
</el-tag>
|
||||
<el-tag v-else-if="row.fundUseRisk === 'medium'" type="warning">
|
||||
{{ row.fundUseRiskName || '中风险' }}
|
||||
</el-tag>
|
||||
<span v-if="row.fundUseRisk === 'high'" class="project-apply-page__risk-high">高</span>
|
||||
<span
|
||||
v-else-if="row.fundUseRisk === 'medium'"
|
||||
class="project-apply-page__risk-medium"
|
||||
>
|
||||
中
|
||||
</span>
|
||||
<span v-else class="project-apply-page__risk-none">-</span>
|
||||
</template>
|
||||
|
||||
<template #fundUseRisk-header>
|
||||
<span class="project-apply-page__risk-header">
|
||||
资金使用风险
|
||||
<el-tooltip placement="top" effect="dark">
|
||||
<template #content>
|
||||
<div>中风险:项目资金额度使用率 ≥ 80%</div>
|
||||
<div>高风险:项目资金额度使用率 ≥ 90%</div>
|
||||
</template>
|
||||
<el-icon class="project-apply-page__risk-help">
|
||||
<el-icon-question-filled />
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<template #menu="{ row, index }">
|
||||
<el-link
|
||||
type="primary"
|
||||
@@ -145,7 +162,7 @@
|
||||
<div class="dialog-section-title">项目基本信息</div>
|
||||
<div class="project-apply-form__grid">
|
||||
<el-form-item v-if="isChangeDialog" label="变更类型" prop="changeType">
|
||||
<el-select v-model="form.changeType" placeholder="请选择变更类型">
|
||||
<el-select v-model="form.changeType" placeholder="请选择">
|
||||
<el-option
|
||||
v-for="item in changeTypeOptions"
|
||||
:key="item.value"
|
||||
@@ -155,15 +172,15 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="isChangeDialog" label="变更编号" prop="changeNo">
|
||||
<el-input v-model="form.changeNo" disabled placeholder="系统自动生成" />
|
||||
<el-input v-model="form.changeNo" disabled placeholder="请输入" />
|
||||
</el-form-item>
|
||||
<el-form-item v-if="isChangeDialog" label="变更日期" prop="changeDate">
|
||||
<el-input v-model="form.changeDate" disabled placeholder="系统自动生成" />
|
||||
<el-input v-model="form.changeDate" disabled placeholder="请输入" />
|
||||
</el-form-item>
|
||||
<el-form-item label="项目类型" prop="projectType">
|
||||
<el-select
|
||||
v-model="form.projectType"
|
||||
placeholder="请选择项目类型"
|
||||
placeholder="请选择"
|
||||
clearable
|
||||
:disabled="isBasicInfoReadonly || isMajorProjectSupplementDialog"
|
||||
@change="handleProjectTypeChange"
|
||||
@@ -179,14 +196,14 @@
|
||||
<el-form-item label="项目编号" prop="projectCode">
|
||||
<el-input
|
||||
v-model="form.projectCode"
|
||||
placeholder="请输入项目编号,不填自动生成"
|
||||
placeholder="请输入"
|
||||
:disabled="dialogType === 'edit'"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="项目名称" prop="projectName">
|
||||
<el-input
|
||||
v-model="form.projectName"
|
||||
placeholder="请填写项目名称"
|
||||
placeholder="请输入"
|
||||
maxlength="50"
|
||||
:disabled="isBasicInfoReadonly"
|
||||
/>
|
||||
@@ -194,7 +211,7 @@
|
||||
<el-form-item label="项目简称" prop="projectShortName">
|
||||
<el-input
|
||||
v-model="form.projectShortName"
|
||||
placeholder="请填写项目简称"
|
||||
placeholder="请输入"
|
||||
maxlength="20"
|
||||
:disabled="isBasicInfoReadonly"
|
||||
/>
|
||||
@@ -204,7 +221,7 @@
|
||||
v-model="businessDeptCascaderValue"
|
||||
:options="businessDeptTreeOptions"
|
||||
:props="deptCascaderProps"
|
||||
placeholder="请选择业务部门"
|
||||
placeholder="请选择"
|
||||
filterable
|
||||
clearable
|
||||
:disabled="isBasicInfoReadonly"
|
||||
@@ -215,7 +232,7 @@
|
||||
v-model="undertakeDeptCascaderValue"
|
||||
:options="deptTreeOptions"
|
||||
:props="deptCascaderProps"
|
||||
placeholder="请选择承办部门"
|
||||
placeholder="请选择"
|
||||
clearable
|
||||
filterable
|
||||
:disabled="isBasicInfoReadonly"
|
||||
@@ -224,7 +241,7 @@
|
||||
<el-form-item label="项目由来" prop="projectSource">
|
||||
<el-select
|
||||
v-model="form.projectSource"
|
||||
placeholder="请选择项目由来"
|
||||
placeholder="请选择"
|
||||
clearable
|
||||
:disabled="isBasicInfoReadonly"
|
||||
>
|
||||
@@ -237,7 +254,14 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="项目由来说明" prop="sourceRemark" class="project-apply-form__item--full">
|
||||
<el-input v-model="form.sourceRemark" type="textarea" rows="2" maxlength="300" :disabled="isBasicInfoReadonly" />
|
||||
<el-input
|
||||
v-model="form.sourceRemark"
|
||||
type="textarea"
|
||||
rows="2"
|
||||
placeholder="请输入"
|
||||
maxlength="300"
|
||||
:disabled="isBasicInfoReadonly"
|
||||
/>
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
@@ -320,6 +344,7 @@
|
||||
<el-form-item label="应收账款回款期限" prop="receivableDays">
|
||||
<el-input
|
||||
v-model="form.receivableDays"
|
||||
placeholder="请输入"
|
||||
@input="value => handleIntegerInput('receivableDays', value)"
|
||||
>
|
||||
<template #suffix>天</template>
|
||||
@@ -328,6 +353,7 @@
|
||||
<el-form-item label="回款账期" prop="paymentDays">
|
||||
<el-input
|
||||
v-model="form.paymentDays"
|
||||
placeholder="请输入"
|
||||
@input="value => handleIntegerInput('paymentDays', value)"
|
||||
>
|
||||
<template #suffix>天</template>
|
||||
@@ -350,22 +376,29 @@
|
||||
@input="value => handleIntegerInput('cargoQuantity', value)"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="业务周期" prop="businessDateRange">
|
||||
<el-form-item label="业务周期起" prop="businessStartDate">
|
||||
<el-date-picker
|
||||
v-model="form.businessDateRange"
|
||||
type="daterange"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
v-model="form.businessStartDate"
|
||||
type="date"
|
||||
placeholder="请选择"
|
||||
format="YYYY-MM-DD"
|
||||
value-format="YYYY-MM-DD"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="业务周期止" prop="businessEndDate">
|
||||
<el-date-picker
|
||||
v-model="form.businessEndDate"
|
||||
type="date"
|
||||
placeholder="请选择"
|
||||
format="YYYY-MM-DD"
|
||||
value-format="YYYY-MM-DD"
|
||||
unlink-panels
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="运输线路" prop="transportRoute">
|
||||
<el-input v-model="form.transportRoute" maxlength="100" />
|
||||
<el-input v-model="form.transportRoute" placeholder="请输入" maxlength="100" />
|
||||
</el-form-item>
|
||||
<el-form-item label="运输类型" prop="transportType">
|
||||
<el-select v-model="form.transportType" placeholder="请选择运输类型" clearable>
|
||||
<el-select v-model="form.transportType" placeholder="请选择" clearable>
|
||||
<el-option
|
||||
v-for="item in transportTypeOptions"
|
||||
:key="item.value"
|
||||
@@ -375,7 +408,7 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="业务类型" prop="businessType">
|
||||
<el-select v-model="form.businessType" placeholder="请选择业务类型" clearable>
|
||||
<el-select v-model="form.businessType" placeholder="请选择" clearable>
|
||||
<el-option
|
||||
v-for="item in businessTypeOptions"
|
||||
:key="item.value"
|
||||
@@ -385,7 +418,7 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="业务模式" prop="businessMode">
|
||||
<el-select v-model="form.businessMode" placeholder="请选择业务模式">
|
||||
<el-select v-model="form.businessMode" placeholder="请选择">
|
||||
<el-option
|
||||
v-for="item in businessModeOptions"
|
||||
:key="item.value"
|
||||
@@ -397,13 +430,14 @@
|
||||
<el-form-item label="项目规模" prop="projectScale">
|
||||
<el-input
|
||||
v-model="form.projectScale"
|
||||
placeholder="请输入"
|
||||
@input="value => handleNumberInput('projectScale', value)"
|
||||
>
|
||||
<template #suffix>万元</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="结算方式" prop="settlementMode">
|
||||
<el-select v-model="form.settlementMode" placeholder="请选择结算方式" clearable>
|
||||
<el-select v-model="form.settlementMode" placeholder="请选择" clearable>
|
||||
<el-option
|
||||
v-for="item in settlementModeOptions"
|
||||
:key="item.value"
|
||||
@@ -415,6 +449,7 @@
|
||||
<el-form-item label="预估利润" prop="estimatedProfit">
|
||||
<el-input
|
||||
v-model="form.estimatedProfit"
|
||||
placeholder="请输入"
|
||||
@input="value => handleNumberInput('estimatedProfit', value)"
|
||||
>
|
||||
<template #suffix>万元</template>
|
||||
@@ -423,6 +458,7 @@
|
||||
<el-form-item label="利润率" prop="profitRate">
|
||||
<el-input
|
||||
v-model="form.profitRate"
|
||||
placeholder="请输入"
|
||||
@input="value => handleNumberInput('profitRate', value)"
|
||||
>
|
||||
<template #suffix>%</template>
|
||||
@@ -431,6 +467,7 @@
|
||||
<el-form-item label="履约保证金" prop="fundDemand">
|
||||
<el-input
|
||||
v-model="form.fundDemand"
|
||||
placeholder="请输入"
|
||||
@input="value => handleNumberInput('fundDemand', value)"
|
||||
>
|
||||
<template #suffix>万元</template>
|
||||
@@ -440,7 +477,7 @@
|
||||
<el-input
|
||||
v-model="form.handlerUserName"
|
||||
readonly
|
||||
placeholder="弹窗搜索选择"
|
||||
placeholder="请输入"
|
||||
@click="openUserPicker('handler')"
|
||||
>
|
||||
<template #suffix>
|
||||
@@ -452,7 +489,7 @@
|
||||
<el-input
|
||||
v-model="form.principalUserName"
|
||||
readonly
|
||||
placeholder="弹窗搜索选择"
|
||||
placeholder="请输入"
|
||||
@click="openUserPicker('principal')"
|
||||
>
|
||||
<template #suffix>
|
||||
@@ -524,7 +561,7 @@
|
||||
v-model="form.projectIntro"
|
||||
type="textarea"
|
||||
:rows="2"
|
||||
placeholder="请填写备注信息"
|
||||
placeholder="请输入"
|
||||
maxlength="200"
|
||||
show-word-limit
|
||||
/>
|
||||
@@ -534,7 +571,7 @@
|
||||
v-model="form.profitRemark"
|
||||
type="textarea"
|
||||
:rows="2"
|
||||
placeholder="请填写备注信息"
|
||||
placeholder="请输入"
|
||||
maxlength="200"
|
||||
show-word-limit
|
||||
/>
|
||||
@@ -544,7 +581,7 @@
|
||||
v-model="form.riskPoint"
|
||||
type="textarea"
|
||||
:rows="2"
|
||||
placeholder="请填写备注信息"
|
||||
placeholder="请输入"
|
||||
maxlength="200"
|
||||
show-word-limit
|
||||
/>
|
||||
@@ -554,7 +591,7 @@
|
||||
v-model="form.emergencyPlan"
|
||||
type="textarea"
|
||||
:rows="2"
|
||||
placeholder="请填写备注信息"
|
||||
placeholder="请输入"
|
||||
maxlength="200"
|
||||
show-word-limit
|
||||
/>
|
||||
@@ -565,26 +602,18 @@
|
||||
<div class="project-apply-form__material-head">
|
||||
<div class="dialog-section-title project-apply-form__material-title">项目材料</div>
|
||||
<span
|
||||
v-if="!dialogReadonly && missingRequiredAttachmentTypes.length"
|
||||
v-if="missingRequiredAttachmentTypes.length"
|
||||
class="project-apply-form__material-warn"
|
||||
>
|
||||
未上传:{{ missingRequiredAttachmentTypes.join('、') }}
|
||||
</span>
|
||||
<el-button
|
||||
type="primary"
|
||||
:disabled="!attachmentRows.length"
|
||||
class="project-apply-form__material-download"
|
||||
@click="handleBatchDownload"
|
||||
>批量下载</el-button
|
||||
>
|
||||
</div>
|
||||
<el-alert
|
||||
v-if="dialogReadonly && missingRequiredAttachmentTypes.length"
|
||||
class="project-apply-form__material-warning"
|
||||
type="warning"
|
||||
:closable="false"
|
||||
show-icon
|
||||
:title="`缺少必需材料:${missingRequiredAttachmentTypes.join('、')}`"
|
||||
/>
|
||||
<el-table
|
||||
:data="attachmentRows"
|
||||
border
|
||||
@@ -616,7 +645,7 @@
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-model="row.description"
|
||||
placeholder="请输入附件描述"
|
||||
placeholder="请输入"
|
||||
:disabled="dialogReadonly"
|
||||
/>
|
||||
</template>
|
||||
@@ -668,8 +697,16 @@
|
||||
label="变更内容"
|
||||
min-width="180"
|
||||
align="center"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-tooltip placement="top" :show-after="200">
|
||||
<template #content>
|
||||
<div class="project-apply-form__change-record-content-tooltip">{{ row.content || '空' }}</div>
|
||||
</template>
|
||||
<span class="project-apply-form__change-record-content-cell">{{ row.content || '空' }}</span>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="reason"
|
||||
label="变更原因"
|
||||
@@ -687,6 +724,44 @@
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-dialog
|
||||
v-model="changeRecordDetailVisible"
|
||||
title="变更记录详情"
|
||||
append-to-body
|
||||
destroy-on-close
|
||||
width="1100px"
|
||||
top="10px"
|
||||
class="project-change-record-detail-dialog"
|
||||
>
|
||||
<div v-if="changeRecordDetail" class="project-change-record-detail-meta">
|
||||
<span>变更日期:{{ changeRecordDetail.changeDate || '-' }}</span>
|
||||
<span>变更账号:{{ changeRecordDetail.handler || '-' }}</span>
|
||||
</div>
|
||||
<el-table :data="changeRecordDetailRows" border :show-overflow-tooltip="false">
|
||||
<el-table-column prop="field" label="变更字段" min-width="180" />
|
||||
<el-table-column
|
||||
prop="before"
|
||||
label="变更前"
|
||||
min-width="360"
|
||||
class-name="project-change-record-detail-value"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="after"
|
||||
label="变更后"
|
||||
min-width="500"
|
||||
class-name="project-change-record-detail-value"
|
||||
/>
|
||||
</el-table>
|
||||
<el-empty
|
||||
v-if="!changeRecordDetailRows.length"
|
||||
description="暂无变更内容"
|
||||
:image-size="60"
|
||||
/>
|
||||
<template #footer>
|
||||
<el-button type="primary" @click="changeRecordDetailVisible = false">关闭</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<template v-if="isChangeDialog">
|
||||
<div class="dialog-section-title">变更原因</div>
|
||||
<el-form-item prop="changeReason" class="project-apply-form__change-textarea">
|
||||
@@ -694,7 +769,7 @@
|
||||
v-model="form.changeReason"
|
||||
type="textarea"
|
||||
:rows="2"
|
||||
placeholder="2000字以内"
|
||||
placeholder="请输入"
|
||||
maxlength="2000"
|
||||
show-word-limit
|
||||
/>
|
||||
@@ -888,7 +963,6 @@ const emptyForm = () => ({
|
||||
paymentDays: '',
|
||||
cargoType: '',
|
||||
cargoQuantity: '',
|
||||
businessDateRange: [],
|
||||
businessStartDate: '',
|
||||
businessEndDate: '',
|
||||
transportRoute: '',
|
||||
@@ -938,6 +1012,8 @@ const attachmentViewerPlugins = [
|
||||
fallbackPlugin(),
|
||||
];
|
||||
|
||||
const otherAttachmentType = { label: '其他附件', value: '其他附件' };
|
||||
|
||||
const normalProjectAttachmentTypeOptions = [
|
||||
'利润测算表',
|
||||
'单一来源供应商申请表/三方比价表',
|
||||
@@ -945,7 +1021,7 @@ const normalProjectAttachmentTypeOptions = [
|
||||
'项目操作说明',
|
||||
'项目分析报告',
|
||||
'项目考察报告',
|
||||
].map(item => ({ label: item, value: item }));
|
||||
].map(item => ({ label: item, value: item })).concat(otherAttachmentType);
|
||||
|
||||
const majorProjectAttachmentTypeOptions = [
|
||||
'会议纪要',
|
||||
@@ -959,7 +1035,7 @@ const majorProjectAttachmentTypeOptions = [
|
||||
'上游尽职调查报告',
|
||||
'下游尽职调查报告',
|
||||
'业务承诺函',
|
||||
].map(item => ({ label: item, value: item }));
|
||||
].map(item => ({ label: item, value: item })).concat(otherAttachmentType);
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -1148,6 +1224,9 @@ export default {
|
||||
'zip',
|
||||
],
|
||||
changeRows: [],
|
||||
changeRecordDetailVisible: false,
|
||||
changeRecordDetail: null,
|
||||
changeRecordDetailRows: [],
|
||||
userBox: false,
|
||||
userPickType: '',
|
||||
userLoading: false,
|
||||
@@ -1279,6 +1358,7 @@ export default {
|
||||
const uploadedTypes = new Set(this.attachmentRows.map(item => item.fileType).filter(Boolean));
|
||||
return this.projectAttachmentTypeOptions
|
||||
.map(item => item.value)
|
||||
.filter(fileType => fileType !== otherAttachmentType.value)
|
||||
.filter(fileType => !uploadedTypes.has(fileType));
|
||||
},
|
||||
},
|
||||
@@ -1399,7 +1479,7 @@ export default {
|
||||
this.page.total = result.total || 0;
|
||||
this.data = result.records || [];
|
||||
this.selectionClear();
|
||||
// this.refreshFundRiskStats({ ...params, ...this.query, allDept: this.allDept });
|
||||
this.refreshFundRiskStats({ ...params, ...this.query, allDept: this.allDept });
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
@@ -1591,10 +1671,6 @@ export default {
|
||||
...displayRow,
|
||||
handlerUserName: this.resolveUserRealName(displayRow, 'handler'),
|
||||
principalUserName: this.resolveUserRealName(displayRow, 'principal'),
|
||||
businessDateRange:
|
||||
displayRow.businessStartDate && displayRow.businessEndDate
|
||||
? [displayRow.businessStartDate, displayRow.businessEndDate]
|
||||
: [],
|
||||
};
|
||||
const situation = this.parseSituation(displayRow.situationRemark);
|
||||
const customerRows = this.parseJsonArray(displayRow.customerJson);
|
||||
@@ -1608,6 +1684,7 @@ export default {
|
||||
this.customerRows = customerRows;
|
||||
this.carrierRows = carrierRows;
|
||||
this.attachmentRows = this.parseJsonArray(displayRow.attachmentsJson);
|
||||
this.normalizeAttachmentFileTypes();
|
||||
this.selectedAttachmentRows = [];
|
||||
this.changeRows = this.buildChangeRows(displayRow);
|
||||
this.selectedCustomerId = this.customerRows
|
||||
@@ -1747,7 +1824,6 @@ export default {
|
||||
});
|
||||
},
|
||||
normalizeSubmitForm(options = {}) {
|
||||
const [businessStartDate, businessEndDate] = this.form.businessDateRange || [];
|
||||
const submitRow = {
|
||||
...this.form,
|
||||
fundLimit: this.formatAmount(this.form.fundLimit),
|
||||
@@ -1755,8 +1831,6 @@ export default {
|
||||
...Object.fromEntries(
|
||||
optionalSentinelFields.map(key => [key, this.normalizeOptionalSentinel(this.form[key], null)])
|
||||
),
|
||||
businessStartDate,
|
||||
businessEndDate,
|
||||
customerJson: JSON.stringify(this.customerRows),
|
||||
carrierJson: JSON.stringify(this.carrierRows),
|
||||
attachmentsJson: JSON.stringify(this.attachmentRows),
|
||||
@@ -1767,7 +1841,6 @@ export default {
|
||||
emergencyPlan: this.form.emergencyPlan || '',
|
||||
}),
|
||||
};
|
||||
delete submitRow.businessDateRange;
|
||||
if (!options.includeChangeType) {
|
||||
delete submitRow.changeType;
|
||||
}
|
||||
@@ -1779,6 +1852,7 @@ export default {
|
||||
['projectIntro', 'profitRemark', 'riskPoint', 'emergencyPlan'].forEach(
|
||||
key => delete submitRow[key]
|
||||
);
|
||||
delete submitRow.changeRecordJson;
|
||||
Object.keys(submitRow).forEach(key => {
|
||||
if (typeof submitRow[key] === 'string') {
|
||||
submitRow[key] = submitRow[key].trim();
|
||||
@@ -1814,13 +1888,13 @@ export default {
|
||||
const values = this.projectAttachmentTypeOptions.map(item => item.value);
|
||||
if (values.includes(row.fileType)) return row.fileType;
|
||||
const fileName = this.normalizeAttachmentTypeText(this.attachmentName(row));
|
||||
if (!fileName) return '';
|
||||
if (!fileName) return otherAttachmentType.value;
|
||||
const option = this.projectAttachmentTypeOptions.find(item =>
|
||||
this.attachmentTypeKeywords(item.value).some(keyword =>
|
||||
fileName.includes(this.normalizeAttachmentTypeText(keyword))
|
||||
)
|
||||
);
|
||||
return option?.value || '';
|
||||
return option?.value || otherAttachmentType.value;
|
||||
},
|
||||
validateAttachmentFileTypes() {
|
||||
const values = this.projectAttachmentTypeOptions.map(item => item.value);
|
||||
@@ -1868,7 +1942,8 @@ export default {
|
||||
if (Array.isArray(value)) return value;
|
||||
try {
|
||||
const list = JSON.parse(value);
|
||||
return Array.isArray(list) ? list : [];
|
||||
if (Array.isArray(list)) return list;
|
||||
return list && typeof list === 'object' ? [list] : [];
|
||||
} catch (error) {
|
||||
return [];
|
||||
}
|
||||
@@ -1942,20 +2017,147 @@ export default {
|
||||
);
|
||||
},
|
||||
buildChangeRows(row) {
|
||||
if (!row || (!row.changeContent && !row.changeReason)) return [];
|
||||
return [
|
||||
{
|
||||
changeDate: row.updateTime || row.createTime || '',
|
||||
changeType:
|
||||
row.changeType ||
|
||||
(String(row.currentNode || '').includes('备案') ? '项目备案调整' : '项目变更'),
|
||||
handler: this.resolveUserRealName(row, 'handler'),
|
||||
content: row.changeContent || '',
|
||||
reason: row.changeReason || '',
|
||||
status: row.approvalStatusName || row.approvalStatus || '',
|
||||
action: this.dialogReadonly ? '流程' : '编辑',
|
||||
},
|
||||
const records = this.parseJsonArray(row?.changeRecordJson);
|
||||
return records.map((record, recordIndex) => {
|
||||
const changeRow = {
|
||||
...record,
|
||||
projectId: row?.id,
|
||||
recordIndex,
|
||||
changeDate: record.changeDate || record.changeTime || record.createTime || '',
|
||||
changeType: record.changeType || '项目变更',
|
||||
handler: record.handlerUserName || record.changeUserName || record.handler || '',
|
||||
reason: record.changeReason || '',
|
||||
status: record.statusName || record.status || '',
|
||||
action: '详情',
|
||||
};
|
||||
return { ...changeRow, content: this.formatChangeContent(changeRow) };
|
||||
});
|
||||
},
|
||||
parseChangeData(value) {
|
||||
if (!value || value === '-1') return {};
|
||||
if (typeof value === 'object') return value;
|
||||
const text = String(value).trim();
|
||||
if (!text || text === '-1') return {};
|
||||
try {
|
||||
const parsed = JSON.parse(text);
|
||||
return parsed && typeof parsed === 'object' ? parsed : { 变更内容: parsed };
|
||||
} catch (error) {
|
||||
return { 变更内容: text };
|
||||
}
|
||||
},
|
||||
getProjectChangeFieldLabel(field) {
|
||||
const labels = {
|
||||
projectType: '项目类型',
|
||||
projectName: '项目名称',
|
||||
projectShortName: '项目简称',
|
||||
businessDeptName: '业务部门',
|
||||
undertakeDeptName: '承办部门',
|
||||
projectSource: '项目由来',
|
||||
sourceRemark: '项目由来说明',
|
||||
fundLimit: '项目资金使用额度',
|
||||
receivableLimit: '项目应收账款额度',
|
||||
receivableDays: '应收账款回款期限',
|
||||
paymentDays: '回款账期',
|
||||
cargoType: '货物类型',
|
||||
cargoQuantity: '预估货物数量',
|
||||
businessStartDate: '业务周期起',
|
||||
businessEndDate: '业务周期止',
|
||||
transportRoute: '运输线路',
|
||||
transportType: '运输类型',
|
||||
businessType: '业务类型',
|
||||
businessMode: '业务模式',
|
||||
projectScale: '项目规模',
|
||||
settlementMode: '结算方式',
|
||||
estimatedProfit: '预计利润',
|
||||
profitRate: '利润率',
|
||||
fundDemand: '履约保证金',
|
||||
handlerUserName: '项目经办人',
|
||||
principalUserName: '项目负责人',
|
||||
customerNames: '客户名称',
|
||||
carrierNames: '下游承运商',
|
||||
situationRemark: '项目情况说明',
|
||||
attachmentsJson: '项目材料',
|
||||
};
|
||||
return labels[field] || field;
|
||||
},
|
||||
normalizeProjectChangeValue(value) {
|
||||
if (typeof value !== 'string') return value;
|
||||
const text = value.trim();
|
||||
if (!text || (!text.startsWith('[') && !text.startsWith('{') && text !== 'null')) {
|
||||
return value;
|
||||
}
|
||||
try {
|
||||
return JSON.parse(text);
|
||||
} catch (error) {
|
||||
return value;
|
||||
}
|
||||
},
|
||||
isEmptyProjectChangeValue(value) {
|
||||
const normalized = this.normalizeProjectChangeValue(value);
|
||||
return (
|
||||
normalized === undefined ||
|
||||
normalized === null ||
|
||||
normalized === '' ||
|
||||
normalized === '-1' ||
|
||||
(Array.isArray(normalized) && normalized.length === 0) ||
|
||||
(typeof normalized === 'object' && !Array.isArray(normalized) && Object.keys(normalized).length === 0)
|
||||
);
|
||||
},
|
||||
isSameProjectChangeValue(before, after) {
|
||||
if (this.isEmptyProjectChangeValue(before) && this.isEmptyProjectChangeValue(after)) {
|
||||
return true;
|
||||
}
|
||||
return JSON.stringify(this.normalizeProjectChangeValue(before)) === JSON.stringify(this.normalizeProjectChangeValue(after));
|
||||
},
|
||||
formatProjectChangeValue(field, value) {
|
||||
const normalized = this.normalizeProjectChangeValue(value);
|
||||
if (this.isEmptyProjectChangeValue(normalized)) return '空';
|
||||
if (field === 'attachmentsJson') {
|
||||
const attachments = Array.isArray(normalized)
|
||||
? normalized
|
||||
: normalized && typeof normalized === 'object'
|
||||
? [normalized]
|
||||
: [];
|
||||
const names = attachments
|
||||
.map(item => (typeof item === 'string' ? item : item?.originalName || item?.name || item?.fileName || item?.url || ''))
|
||||
.map(item => String(item).split('?')[0].split('/').pop())
|
||||
.filter(Boolean);
|
||||
if (names.length) return names.join('、');
|
||||
}
|
||||
if (Array.isArray(normalized)) {
|
||||
return normalized.map(item => this.formatProjectChangeValue(field, item)).join('、');
|
||||
}
|
||||
if (typeof normalized === 'object') return JSON.stringify(normalized);
|
||||
return String(normalized);
|
||||
},
|
||||
buildChangeRecordDetailRows(row = {}) {
|
||||
const beforeData = this.parseChangeData(row.beforeData);
|
||||
const afterData = this.parseChangeData(row.afterData);
|
||||
const fields = [
|
||||
...new Set([
|
||||
...(Array.isArray(row.changedFields) ? row.changedFields : []),
|
||||
...Object.keys(beforeData),
|
||||
...Object.keys(afterData),
|
||||
]),
|
||||
];
|
||||
const rows = fields
|
||||
.filter(field => field !== '变更内容' && !this.isSameProjectChangeValue(beforeData[field], afterData[field]))
|
||||
.map(field => ({
|
||||
field: this.getProjectChangeFieldLabel(field),
|
||||
before: this.formatProjectChangeValue(field, beforeData[field]),
|
||||
after: this.formatProjectChangeValue(field, afterData[field]),
|
||||
}));
|
||||
if (!rows.length && String(row.changeContent || '').trim()) {
|
||||
rows.push({ field: '变更内容', before: '空', after: String(row.changeContent).trim() });
|
||||
}
|
||||
return rows;
|
||||
},
|
||||
formatChangeContent(row = {}) {
|
||||
const rows = this.buildChangeRecordDetailRows(row);
|
||||
return rows
|
||||
.filter(item => item.field !== '变更内容')
|
||||
.map(item => `${item.field}:变更前:${item.before} → 变更后:${item.after}`)
|
||||
.join(';') || String(row.changeContent || '').trim();
|
||||
},
|
||||
generateChangeNo(detail) {
|
||||
const base = detail && detail.projectCode ? detail.projectCode : 'XM';
|
||||
@@ -2277,8 +2479,20 @@ export default {
|
||||
window.setTimeout(() => this.downloadAttachment(row), index * 300);
|
||||
});
|
||||
},
|
||||
handleChangeRecordAction() {
|
||||
this.$message.info('变更记录详情由后端明细能力生成后展示');
|
||||
async handleChangeRecordAction(row) {
|
||||
if (!row?.projectId && !row?.id) {
|
||||
this.$message.warning('项目ID为空,无法查看变更详情');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const response = await api.getChangeRecordDetail(row.projectId || row.id, row.recordIndex);
|
||||
const detail = response?.data?.data || {};
|
||||
this.changeRecordDetail = { ...row, ...detail };
|
||||
this.changeRecordDetailRows = this.buildChangeRecordDetailRows(this.changeRecordDetail);
|
||||
this.changeRecordDetailVisible = true;
|
||||
} catch (error) {
|
||||
this.$message.error('变更记录详情加载失败');
|
||||
}
|
||||
},
|
||||
openUserPicker(type) {
|
||||
if (this.dialogReadonly) return;
|
||||
@@ -2432,6 +2646,21 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
&__change-record-content-cell {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&__change-record-content-tooltip {
|
||||
max-width: 900px;
|
||||
max-height: 320px;
|
||||
overflow: auto;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
&__textarea-list {
|
||||
margin-bottom: 12px;
|
||||
background: #fff;
|
||||
@@ -2439,19 +2668,26 @@ export default {
|
||||
padding: 14px 16px 4px;
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
|
||||
|
||||
// 与项目基本信息网格使用相同的 6 字符 label 宽度,长文本自动换行
|
||||
:deep(.el-form-item__label) {
|
||||
width: 108px !important;
|
||||
min-width: 0 !important;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
:deep(.el-form-item) {
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
:deep(.el-form-item__content) {
|
||||
width: calc(100% - 180px);
|
||||
width: calc(100% - 108px);
|
||||
}
|
||||
}
|
||||
|
||||
&__material-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
justify-content: flex-start;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
@@ -2476,21 +2712,21 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
&__material-warning {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
&__material-title {
|
||||
flex: 1;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
&__material-warn {
|
||||
margin-left: 24px;
|
||||
margin-left: 8px;
|
||||
color: #f56c6c;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
&__material-download {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
&__upload {
|
||||
margin: 0 0 16px;
|
||||
background: #fff;
|
||||
@@ -2530,6 +2766,27 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.project-change-record-detail-dialog .el-dialog__body) {
|
||||
max-height: 65vh;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
:deep(.project-change-record-detail-dialog .project-change-record-detail-value .cell) {
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
overflow: visible;
|
||||
text-overflow: clip;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.project-change-record-detail-meta {
|
||||
display: flex;
|
||||
gap: 32px;
|
||||
margin-bottom: 16px;
|
||||
color: #606266;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
:global(.project-apply-dialog .el-dialog__body) {
|
||||
max-height: 76vh;
|
||||
overflow-y: auto;
|
||||
@@ -2565,6 +2822,12 @@ export default {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
&__risk-header {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
&__risk-help {
|
||||
margin-left: 4px;
|
||||
color: #a8abb2;
|
||||
@@ -2575,6 +2838,14 @@ export default {
|
||||
&__risk-none {
|
||||
color: #c0c4cc;
|
||||
}
|
||||
|
||||
&__risk-high {
|
||||
color: #f56c6c;
|
||||
}
|
||||
|
||||
&__risk-medium {
|
||||
color: #e6a23c;
|
||||
}
|
||||
}
|
||||
|
||||
.project-apply-dialog__footer {
|
||||
|
||||
Reference in New Issue
Block a user