调整项目

This commit is contained in:
2026-09-13 01:01:24 +08:00
parent 764c0f4581
commit dbeb174ae2
6 changed files with 493 additions and 110 deletions
+9 -2
View File
@@ -786,14 +786,21 @@ export default {
this.nodeRows.map(item => ({
key: item.key,
name: item.name,
type: item.type,
enabled: item.enabled,
confirmMode: item.confirmMode,
confirmDriver: item.confirmDriver,
confirmInternal: item.confirmInternal,
punch: item.punch,
location: item.location,
uploadCargo: item.uploadCargo,
cargoTypes: item.uploadCargo ? item.cargoTypes : [],
// 在途不支持货量;其它节点按配置
uploadCargo: item.type === 'transit' || item.key === 'transit' ? false : item.uploadCargo,
cargoTypes:
item.type === 'transit' || item.key === 'transit'
? []
: item.uploadCargo
? item.cargoTypes
: [],
frequencyDays: item.frequencyDays,
timeStart: item.timeStart,
timeEnd: item.timeEnd,
+359 -88
View File
@@ -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 {
+48 -5
View File
@@ -120,6 +120,7 @@
:model="driverForm"
:rules="formRules"
label-position="right"
label-width="calc(4em + 24px)"
:disabled="readonly"
class="driver-form archive-form"
>
@@ -158,7 +159,12 @@
</el-col>
<el-col :span="6">
<el-form-item label="性别" prop="gender">
<el-select v-model="driverForm.gender" clearable placeholder="请选择">
<el-select
v-model="driverForm.gender"
clearable
placeholder="请选择"
:validate-event="submitAttempted"
>
<el-option label="男" value="男" />
<el-option label="女" value="女" />
</el-select>
@@ -302,6 +308,7 @@
allow-create
default-first-option
placeholder="请选择"
:validate-event="submitAttempted"
>
<el-option
v-for="item in drivingTypeOptions"
@@ -321,7 +328,7 @@
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-col :span="6">
<el-form-item label="有效期" prop="drivingLicenseEndDate">
<div class="date-range">
<el-date-picker
@@ -343,7 +350,7 @@
</div>
</el-form-item>
</el-col>
<el-col :span="4">
<el-col :span="6">
<el-form-item label="长期有效" prop="drivingLicenseLongTerm">
<el-checkbox
v-model="driverForm.drivingLicenseLongTerm"
@@ -395,6 +402,7 @@
clearable
filterable
placeholder="请选择"
:validate-event="submitAttempted"
>
<el-option
v-for="item in qualificationTypeOptions"
@@ -414,7 +422,7 @@
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-col :span="6">
<el-form-item label="有效期" prop="qualificationEndDate">
<div class="date-range">
<el-date-picker
@@ -436,7 +444,7 @@
</div>
</el-form-item>
</el-col>
<el-col :span="4">
<el-col :span="6">
<el-form-item label="长期有效" prop="qualificationLongTerm">
<el-checkbox
v-model="driverForm.qualificationLongTerm"
@@ -746,6 +754,7 @@ export default {
total: 0,
},
readonly: false,
submitAttempted: false,
driverForm: emptyForm(),
drivingLicenseUploads: {},
uploadHeaders: getUploadHeaders(),
@@ -1114,6 +1123,7 @@ export default {
},
openDriver(row, readonly = false) {
this.readonly = readonly;
this.submitAttempted = false;
if (!row?.id) {
this.driverForm = emptyForm();
this.driverForm.organizationName = this.getCurrentOrganizationName();
@@ -1138,6 +1148,7 @@ export default {
this.driverForm = emptyForm();
this.drivingLicenseUploads = {};
this.readonly = false;
this.submitAttempted = false;
this.submitLoading = false;
this.$refs.driverForm?.clearValidate();
},
@@ -1600,6 +1611,7 @@ export default {
this.$refs.driverForm?.validateField(prop);
},
handleSubmit() {
this.submitAttempted = true;
this.$refs.driverForm.validate(valid => {
if (!valid) {
return;
@@ -1797,6 +1809,31 @@ export default {
}
.driver-form {
:deep(.el-form-item__label) {
flex: 0 0 calc(4em + 24px) !important;
width: calc(4em + 24px) !important;
height: auto;
white-space: normal !important;
word-break: break-all;
overflow-wrap: anywhere;
line-height: 18px;
}
:deep(.el-form-item) {
align-items: center;
}
:deep(.el-form-item.is-required .el-form-item__label) {
align-items: flex-start;
}
:deep(.el-form-item.is-required .el-form-item__label::before) {
display: inline-block;
height: 18px;
line-height: 18px;
margin-right: 4px;
}
:deep(.el-date-editor.el-input),
:deep(.el-date-editor.el-input__wrapper),
:deep(.el-cascader),
@@ -1809,6 +1846,12 @@ export default {
display: grid;
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
gap: 8px;
width: 100%;
}
.date-range :deep(.el-date-editor) {
width: 100%;
min-width: 0;
}
}
+62 -6
View File
@@ -127,7 +127,11 @@
top="4vh"
class="certification-audit-dialog"
>
<el-form label-position="right" label-width="auto" class="certification-audit archive-form">
<el-form
label-position="right"
label-width="calc(7em + 24px)"
class="certification-audit archive-form"
>
<section-card title="基础信息">
<el-row :gutter="18">
<el-col :span="6">
@@ -353,7 +357,7 @@
v-if="showRejectReason"
class="certification-audit__reason"
label-position="right"
label-width="auto"
label-width="calc(7em + 24px)"
>
<el-form-item label="驳回原因" required>
<el-input
@@ -385,6 +389,8 @@
:model="vehicleForm"
:rules="formRules"
label-position="right"
label-width="calc(4em + 24px)"
:validate-on-rule-change="false"
:disabled="readonly"
class="vehicle-form archive-form"
>
@@ -1632,6 +1638,50 @@ export default {
}
// 车辆认证审核弹窗:与编辑弹窗一致,灰底 + section-card 白卡分组
.certification-audit,
.certification-audit__reason,
.vehicle-form {
:deep(.el-form-item__label) {
height: auto;
padding-right: 12px;
white-space: normal !important;
word-break: break-all;
overflow-wrap: anywhere;
line-height: 18px;
text-align: right;
}
:deep(.el-form-item) {
align-items: center;
}
:deep(.el-form-item.is-required .el-form-item__label) {
align-items: flex-start;
}
:deep(.el-form-item.is-required .el-form-item__label::before) {
display: inline-block;
height: 18px;
line-height: 18px;
margin-right: 4px;
}
}
.certification-audit,
.certification-audit__reason {
:deep(.el-form-item__label) {
flex: 0 0 calc(7em + 24px) !important;
width: calc(7em + 24px) !important;
}
}
.vehicle-form {
:deep(.el-form-item__label) {
flex: 0 0 calc(4em + 24px) !important;
width: calc(4em + 24px) !important;
}
}
.certification-audit {
&__value {
display: block;
@@ -1679,6 +1729,7 @@ export default {
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 8px;
align-items: center;
width: 100%;
}
.dimension-group__item {
@@ -1688,6 +1739,12 @@ export default {
min-width: 0;
}
.dimension-group__item :deep(.el-input) {
flex: 1 1 auto;
min-width: 0;
width: 100%;
}
.dimension-group__label {
flex-shrink: 0;
color: #303133;
@@ -1697,11 +1754,13 @@ export default {
display: flex;
align-items: center;
gap: 8px;
width: 100%;
}
.date-with-flag :deep(.el-date-editor) {
flex: 1;
flex: 1 1 auto;
min-width: 0;
width: 100%;
}
.date-with-flag :deep(.el-checkbox) {
@@ -1719,9 +1778,6 @@ export default {
gap: 8px;
}
:deep(.el-form-item__label) {
white-space: nowrap;
}
}
// 大尺寸证件照:按身份证 1.586:1 比例缩放(约真实尺寸 75%,240×151),左对齐显示