MK公开页、预结算体验优化、地图选址与认证密码规则完善

This commit is contained in:
2026-09-22 12:06:48 +08:00
parent 57368f0179
commit ac87208ba9
41 changed files with 1882 additions and 391 deletions
@@ -162,9 +162,9 @@
v-model="row.billingUnit"
clearable
filterable
:disabled="!row.billingElement"
:disabled="!canSelectBillingUnit(row)"
:loading="unitLoading"
:placeholder="row.billingElement ? '请选择' : '请先选择计费要素'"
:placeholder="billingUnitPlaceholder(row)"
><el-option
v-for="item in unitOptionsFor(row)"
:key="item.id || item.value"
@@ -381,11 +381,18 @@ import { getDictionary } from '@/api/system/dictbiz';
import SectionCard from '@/components/section-card/main.vue';
const clone = value => JSON.parse(JSON.stringify(value));
/** 计费要素与计量单位维度的对应关系 */
/** 计费要素与计量单位维度的对应关系(这些要素可选计费单位) */
const BILLING_ELEMENT_DIMENSION_MAP = {
按重量: '重量',
按体积: '体积',
车辆: '数量',
数量: '数量',
};
const BILLING_UNIT_SELECTABLE_ELEMENTS = Object.keys(BILLING_ELEMENT_DIMENSION_MAP);
/** 固定计费单位(不可编辑) */
const FIXED_BILLING_UNIT_MAP = {
按里程: '公里',
'按吨·公里': '吨公里',
'固定金额(整单一口价)': '单',
};
const defaultRule = () => ({
feeType: '',
@@ -436,7 +443,6 @@ export default {
billingElements: [
'按重量',
'按体积',
'按车辆',
'按里程',
'按吨·公里',
'固定金额(整单一口价)',
@@ -445,7 +451,6 @@ export default {
typeMap: {
按重量: ['固定单价', '区间单价', '阶梯单价', '区间阶梯一口价'],
按体积: ['固定单价', '区间单价', '阶梯单价', '区间阶梯一口价'],
按车辆: ['固定单价'],
按里程: ['固定单价', '区间单价', '阶梯单价', '区间阶梯一口价'],
'按吨·公里': ['固定单价', '区间单价', '阶梯单价', '区间阶梯一口价'],
'固定金额(整单一口价)': ['固定一口价'],
@@ -560,6 +565,7 @@ export default {
next.limitRanges = next.limitRanges.map(item => ({ ...item, minimumBillingWeight: '' }));
this.syncLegacyLimit(next);
next.matchCondition = { ...defaultRule().matchCondition, ...(rule.matchCondition || {}) };
this.syncBillingUnit(next);
return next;
},
normalizeRanges(row) {
@@ -651,7 +657,26 @@ export default {
measurementDimension(billingElement) {
return BILLING_ELEMENT_DIMENSION_MAP[billingElement] || '';
},
fixedBillingUnit(billingElement) {
return FIXED_BILLING_UNIT_MAP[String(billingElement || '').trim()] || '';
},
canSelectBillingUnit(row) {
return BILLING_UNIT_SELECTABLE_ELEMENTS.includes(String(row?.billingElement || '').trim());
},
billingUnitPlaceholder(row) {
if (!row?.billingElement) return '请先选择计费要素';
if (this.fixedBillingUnit(row.billingElement)) return this.fixedBillingUnit(row.billingElement);
if (!this.canSelectBillingUnit(row)) return '当前计费要素无需选择';
return '请选择';
},
unitOptionsFor(row) {
const fixedUnit = this.fixedBillingUnit(row?.billingElement);
if (fixedUnit) {
return [{ id: fixedUnit, label: fixedUnit, value: fixedUnit }];
}
if (!this.canSelectBillingUnit(row)) {
return [];
}
const dimension = this.measurementDimension(row?.billingElement);
if (dimension) {
return this.measurementUnits
@@ -663,15 +688,18 @@ export default {
}))
.filter(item => item.value);
}
return (this.unitOptions || [])
.map(item => ({
id: item.id || item.dictKey || item.dictValue,
label: item.dictValue,
value: item.dictValue,
}))
.filter(item => item.value);
return [];
},
syncBillingUnit(row) {
const fixedUnit = this.fixedBillingUnit(row?.billingElement);
if (fixedUnit) {
row.billingUnit = fixedUnit;
return;
}
if (!this.canSelectBillingUnit(row)) {
row.billingUnit = '';
return;
}
const options = this.unitOptionsFor(row);
if (!options.some(item => String(item.value) === String(row.billingUnit || ''))) {
row.billingUnit = '';
@@ -892,7 +920,6 @@ export default {
['taxRate', '税率'],
['billingElement', '计费要素'],
['billingType', '计费类型'],
['billingUnit', '计费单位'],
];
for (const [i, row] of this.draft.rules.entries()) {
const empty = required.find(
@@ -902,6 +929,15 @@ export default {
this.$message.warning(`${i + 1}${empty[1]}不能为空`);
return false;
}
if (
this.canSelectBillingUnit(row) &&
(row.billingUnit === undefined ||
row.billingUnit === null ||
String(row.billingUnit).trim() === '')
) {
this.$message.warning(`${i + 1}行计费单位不能为空`);
return false;
}
const feeItem = String(row.feeItem).trim();
if (feeItemSet.has(feeItem)) {
this.$message.warning(`费用项“${feeItem}”不能重复`);
@@ -1899,11 +1899,11 @@
label-width="auto"
class="business-crud-page__module-form business-crud-page__module-form--three"
>
<el-form-item label="账单起始日" required :error="settlementRuleErrors.billStartDate">
<el-form-item label="账单起始日" required :error="settlementRuleErrors.billStartDate">
<el-date-picker
v-model="settlementRuleForm.billStartDate"
type="date"
placeholder="请选择账单起始日"
placeholder="请选择账单起始日"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
:disabled="dialogReadonly"
@@ -7784,8 +7784,8 @@ export default {
const rule = this.normalizeSettlementRule(source);
this.settlementRuleErrors = {};
if (this.isBillingFieldEmpty(rule.billStartDate)) {
this.settlementRuleErrors = { billStartDate: '请选择账单起始日' };
this.$message.warning(`${label}${label ? '' : ''}请选择账单起始日`);
this.settlementRuleErrors = { billStartDate: '请选择账单起始日' };
this.$message.warning(`${label}${label ? '' : ''}请选择账单起始日`);
return false;
}
if (this.isBillingFieldEmpty(rule.settlementType)) {
@@ -103,8 +103,13 @@
<div class="contract-attachment-upload-dialog__body">
<div class="contract-attachment-upload-dialog__field">
<span class="contract-attachment-upload-dialog__label">附件位置</span>
<el-select :model-value="attachmentLocation" disabled style="width: 100%">
<el-option :label="attachmentLocation" :value="attachmentLocation" />
<el-select v-model="uploadDialogLocation" placeholder="请选择附件位置" style="width: 100%">
<el-option
v-for="item in resolvedLocationOptions"
:key="item"
:label="item"
:value="item"
/>
</el-select>
</div>
<div class="contract-attachment-upload-dialog__field">
@@ -273,15 +278,17 @@ export default {
markApprovedOnUpload: Boolean,
useUploadDialog: Boolean,
attachmentLocation: { type: String, default: '合同文件' },
attachmentLocationOptions: { type: Array, default: null },
preview: { type: Function, default: null },
},
emits: ['update:rows'],
emits: ['update:rows', 'upload-to-location'],
data() {
return {
selected: [],
attachmentFileTypes,
contractAttachmentTypeOptions: [...defaultContractAttachmentTypeOptions],
uploadDialogVisible: false,
uploadDialogLocation: '合同文件',
uploadDialogType: resolveOtherAttachmentType(defaultContractAttachmentTypeOptions),
uploadDialogFiles: [],
uploadDialogFileList: [],
@@ -300,6 +307,11 @@ export default {
defaultAttachmentType() {
return resolveOtherAttachmentType(this.contractAttachmentTypeOptions);
},
resolvedLocationOptions() {
const options = (this.attachmentLocationOptions || []).filter(Boolean);
if (options.length) return options;
return ['合同文件', '其它附件'];
},
},
created() {
this.loadAttachmentTypeOptions();
@@ -340,6 +352,10 @@ export default {
this.update([...this.rows]);
},
openUploadDialog() {
const options = this.resolvedLocationOptions;
this.uploadDialogLocation = options.includes(this.attachmentLocation)
? this.attachmentLocation
: options[0] || this.attachmentLocation;
this.uploadDialogType = this.defaultAttachmentType;
this.uploadDialogFiles = [];
this.uploadDialogFileList = [];
@@ -348,6 +364,7 @@ export default {
resetUploadDialog() {
this.uploadDialogFiles = [];
this.uploadDialogFileList = [];
this.uploadDialogLocation = this.attachmentLocation;
this.uploadDialogType = this.defaultAttachmentType;
},
beforeUpload(file) {
@@ -416,6 +433,10 @@ export default {
this.$message.warning('请先上传附件');
return;
}
if (!this.uploadDialogLocation) {
this.$message.warning('请选择附件位置');
return;
}
if (!this.uploadDialogType) {
this.$message.warning('请选择附件类型');
return;
@@ -430,7 +451,14 @@ export default {
uploadTime: row.uploadTime || uploadTime,
...(this.markApprovedOnUpload ? { approved: true } : {}),
}));
this.update([...(this.rows || []), ...appended]);
if (this.uploadDialogLocation === this.attachmentLocation) {
this.update([...(this.rows || []), ...appended]);
} else {
this.$emit('upload-to-location', {
location: this.uploadDialogLocation,
rows: appended,
});
}
this.uploadDialogVisible = false;
},
handleChange(rows) {
@@ -1927,7 +1927,8 @@
v-if="!waybillIsCarrier(detailRow)"
class="waybill-manage-page__waybill-detail-field"
>
<span>里程(km)</span><strong>{{ detailRow.mileage || '-' }}</strong>
<span>里程(km)</span
><strong>{{ normalizeMileageValue(detailRow.mileage) || '-' }}</strong>
</div>
</template>
<template v-else-if="waybillIsNonRoadTransport(detailRow)">
@@ -2315,7 +2316,7 @@
</div>
</template>
</div>
<div v-if="detailPageLocked" class="waybill-manage-page__detail-footer">
<div v-if="detailPageLocked && !isPublicWaybillView" class="waybill-manage-page__detail-footer">
<el-button type="primary" @click="closeDetail">关闭</el-button>
</div>
<template v-if="!detailPageLocked" #footer>
@@ -3122,6 +3123,7 @@ import {
getVoucherImages as getProcessConfigVoucherImages,
} from '@/api/business/process-config';
import { getPunchRecords as getWaybillPunchRecords, locateVehicle, trackVehicle } from '@/api/business/waybill-manage';
import { getMkPublicDetail } from '@/api/mk-process';
import { getList as getDriverList } from '@/api/transportCapacity/driver';
import { getDictionary } from '@/api/system/dictbiz';
import { getDictionary as getSystemDictionary } from '@/api/system/dict';
@@ -3646,6 +3648,7 @@ export default {
this.formPageLocked = this.isStandaloneWaybillFormPage;
this.detailPageLocked = this.isStandaloneWaybillDetailPage;
this.formModeLocked = this.$route.query.mode || '';
if (this.isPublicWaybillView) return;
if (this.config.enableAllDept && this.isAdmin) {
this.allDept = 1;
}
@@ -3709,8 +3712,14 @@ export default {
isStandaloneWaybillFormPage() {
return this.isStandaloneWaybillPage && ['add', 'edit'].includes(this.$route.query.mode);
},
isPublicWaybillView() {
return this.$route.path === '/business/waybill-manage/public-view';
},
isStandaloneWaybillDetailPage() {
return this.standaloneDetailPage && this.$route.path === standaloneWaybillDetailRoute;
return (
(this.standaloneDetailPage && this.$route.path === standaloneWaybillDetailRoute) ||
this.isPublicWaybillView
);
},
// $route data formPageLocked
// tab.js fullPath path
@@ -4591,14 +4600,19 @@ export default {
this.waybillVoucherFolder = null;
this.waybillVoucherFolderView = false;
this.waybillVoucherImagesLoaded = false;
const request =
typeof this.api.getDetail === 'function'
const request = this.isPublicWaybillView
? getMkPublicDetail('waybill-manage', row.id)
: typeof this.api.getDetail === 'function'
? this.api.getDetail(row.id)
: Promise.resolve({ data: { data: row } });
request
.then(res => {
const detail = res?.data?.data || res?.data || row;
this.detailRow = detail;
if (this.isPublicWaybillView) {
this.applyFormDetail(detail);
return;
}
this.loadWaybillPunchRecords();
this.loadWaybillVoucherImages();
if (detail.contractId) {
@@ -8560,7 +8574,7 @@ export default {
};
this.mileageForm = {
id: row.id,
mileage: row.mileage === null || row.mileage === undefined ? '' : String(row.mileage),
mileage: this.normalizeMileageValue(row.mileage),
mileageRemark: row.mileageRemark || '',
};
this.$nextTick(() => this.$refs.mileageFormRef?.clearValidate());
+69 -10
View File
@@ -70,7 +70,7 @@
type="date"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
placeholder="YYYY-MM-DD"
placeholder="请输入"
/>
<span></span>
<el-date-picker
@@ -78,7 +78,7 @@
type="date"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
placeholder="YYYY-MM-DD"
placeholder="请输入"
/>
</div>
</el-form-item>
@@ -92,12 +92,12 @@
</el-input>
</el-form-item>
<el-form-item label="合同金额">
<el-input-number
v-model="form.contractAmount"
:min="0"
:precision="2"
:controls="false"
<el-input
:model-value="contractAmountDisplay"
placeholder="请输入"
clearable
@input="handleContractAmountInput"
@clear="form.contractAmount = null"
/>
</el-form-item>
<el-form-item label="是否范本">
@@ -190,9 +190,11 @@
description
attachment-type
use-upload-dialog
:attachment-location-options="changeAttachmentLocationOptions"
:rows="contractFileRows"
:preview="previewAttachment"
@update:rows="contractFileRows = $event"
@upload-to-location="handleAttachmentUploadToLocation"
/>
<section class="change-section">
@@ -218,7 +220,7 @@
</el-radio-group>
</div>
<el-form v-if="settlementRule.autoGenerate === 1" :model="settlementRule" label-position="right" label-width="auto" class="settlement-form">
<el-form-item label="账单起始日" required><el-date-picker v-model="settlementRule.billStartDate" type="date" placeholder="请选择账单起始日" format="YYYY-MM-DD" value-format="YYYY-MM-DD" /></el-form-item>
<el-form-item label="账单起始日" required><el-date-picker v-model="settlementRule.billStartDate" type="date" placeholder="请选择账单起始日" format="YYYY-MM-DD" value-format="YYYY-MM-DD" /></el-form-item>
<el-form-item label="结算类型" required><el-select v-model="settlementRule.settlementType" placeholder="请选择结算类型" @change="handleSettlementTypeChange"><el-option v-for="item in settlementTypeOptions" :key="item" :label="item" :value="item" /></el-select></el-form-item>
<el-form-item v-if="showSettlementBillCycleType" label="结算周期" required><el-select v-model="settlementRule.billCycleType" placeholder="请选择结算周期" @change="handleCycleTypeChange"><el-option v-for="item in billCycleTypeOptions" :key="item" :label="item" :value="item" /></el-select></el-form-item>
<el-form-item v-if="showSettlementBillCutoffDay" label="账单截单日" required><el-select v-model="settlementRule.billCutoffDay" placeholder="请选择账单截单日"><el-option v-for="item in billCutoffDayOptions" :key="item.value" :label="item.label" :value="item.value" /></el-select></el-form-item>
@@ -258,9 +260,11 @@
attachment-type
use-upload-dialog
attachment-location="其它附件"
:attachment-location-options="changeAttachmentLocationOptions"
:rows="attachments"
:preview="previewAttachment"
@update:rows="attachments = $event"
@upload-to-location="handleAttachmentUploadToLocation"
/>
<section class="change-section change-reason-section">
@@ -283,9 +287,11 @@
attachment-type
use-upload-dialog
attachment-location="变更材料"
:attachment-location-options="changeAttachmentLocationOptions"
:rows="changeMaterials"
:preview="previewAttachment"
@update:rows="changeMaterials = $event"
@upload-to-location="handleAttachmentUploadToLocation"
/>
<div class="page-footer">
<el-button @click="$router.back()">取消</el-button>
@@ -343,7 +349,15 @@ const normalizeOptionalPositiveInteger = value => {
return Number.isInteger(number) && number > 0 ? number : null;
};
const normalizeOptionalAmount = value => {
if (value === undefined || value === null || value === '') return null;
if (
value === undefined ||
value === null ||
value === '' ||
Number(value) === -1 ||
String(value) === '-1'
) {
return null;
}
const number = Number(value);
return Number.isFinite(number) && number >= 0 ? Number(number.toFixed(2)) : null;
};
@@ -421,6 +435,22 @@ export default {
pageTitle() {
return this.$route.query.name || '合同变更';
},
changeAttachmentLocationOptions() {
return ['合同文件', '其它附件', '变更材料'];
},
contractAmountDisplay() {
const value = this.form.contractAmount;
if (
value === undefined ||
value === null ||
value === '' ||
Number(value) === -1 ||
String(value) === '-1'
) {
return '';
}
return value;
},
contractCategoryOptions() {
return this.contractCategoryDictOptions.length
? this.contractCategoryDictOptions
@@ -464,6 +494,13 @@ export default {
},
},
methods: {
handleContractAmountInput(value) {
const text = String(value || '').replace(/[^\d.]/g, '');
const parts = text.split('.');
const normalized =
parts.length > 1 ? `${parts[0]}.${parts.slice(1).join('').slice(0, 2)}` : parts[0];
this.form.contractAmount = normalized === '' ? null : normalized;
},
loadDictionaries() {
Promise.all([
getSystemDictionary({ code: 'currency_type' }),
@@ -619,6 +656,20 @@ export default {
attachmentExtension(row = {}) { const source = String(this.attachmentName(row) || this.attachmentUrl(row)).split('?')[0]; const index = source.lastIndexOf('.'); return index > -1 ? source.slice(index + 1).toLowerCase() : ''; },
isAttachmentImage(row) { return ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'].includes(this.attachmentExtension(row)); },
previewAttachment(row, rows = this.attachments) { const url = this.attachmentUrl(row); if (!url) { this.$message.warning('附件地址为空,无法预览'); return; } if (this.isAttachmentImage(row)) { this.imagePreviewUrls = (rows || []).filter(item => this.isAttachmentImage(item) && this.attachmentUrl(item)).map(item => this.attachmentUrl(item)); this.imagePreviewIndex = Math.max(this.imagePreviewUrls.indexOf(url), 0); this.imagePreviewVisible = true; return; } this.previewFile = { name: this.attachmentName(row), url, mimeType: row.mimeType || row.contentType || '' }; this.documentPreviewVisible = true; },
handleAttachmentUploadToLocation({ location, rows = [] }) {
if (!rows.length) return;
if (location === '合同文件') {
this.contractFileRows = [...(this.contractFileRows || []), ...rows];
return;
}
if (location === '其它附件') {
this.attachments = [...(this.attachments || []), ...rows];
return;
}
if (location === '变更材料') {
this.changeMaterials = [...(this.changeMaterials || []), ...rows];
}
},
handlePreviewUnsupported() { this.$message.warning('当前文件暂不支持在线预览'); },
handlePreviewError() { this.$message.error('附件预览失败'); },
formatFileSize(value) { const size = Number(value || 0); return size > 1024 * 1024 ? `${(size / 1024 / 1024).toFixed(2)}MB` : `${Math.max(1, Math.ceil(size / 1024))}KB`; },
@@ -629,7 +680,7 @@ export default {
addCustomPeriodRow() { const periods = normalizeCustomPeriods(this.settlementRule.customPeriods || []); periods.push({ startDay: '', endDay: '' }); this.settlementRule.customPeriods = periods; },
removeCustomPeriodRow(index) { if (index <= 0) return; const periods = [...(this.settlementRule.customPeriods || [])]; periods.splice(index, 1); this.settlementRule.customPeriods = normalizeCustomPeriods(periods); },
validateCustomPeriods(periods = [], label) { const rows = normalizeCustomPeriods(periods); if (!rows.length) { this.$message.warning(`${label}:请至少配置一段自定义周期`); return false; } for (let index = 0; index < rows.length; index += 1) { const row = rows[index]; const startDay = Number(row.startDay); const endDay = Number(row.endDay); if (!Number.isFinite(startDay) || startDay < 1 || startDay > 31) { this.$message.warning(`${label}:请选择第${index + 1}行运单区间开始日`); return false; } if (!Number.isFinite(endDay) || endDay < 1 || endDay > 31) { this.$message.warning(`${label}:请选择第${index + 1}行运单区间结束日`); return false; } if (endDay < startDay) { this.$message.warning(`${label}:第${index + 1}行结束日不能早于开始日`); return false; } if (index > 0 && startDay !== Number(rows[index - 1].endDay) + 1) { this.$message.warning(`${label}:自定义多周期区间必须连续,不允许重叠或存在日期缺口`); return false; } } return true; },
validateSettlementRule(rule, label) { if (Number(rule.autoGenerate) !== 1) return true; if (!rule.billStartDate || !rule.settlementType) { this.$message.warning(`${label}:请完整填写账单起始日和结算类型`); return false; } if (rule.settlementType === '月结' && !rule.billCycleType) { this.$message.warning(`${label}:请选择结算周期`); return false; } if (rule.settlementType === '月结' && rule.billCycleType === '固定截单日' && !rule.billCutoffDay) { this.$message.warning(`${label}:请选择账单截单日`); return false; } if (rule.settlementType === '月结' && rule.billCycleType === '自定义多周期') return this.validateCustomPeriods(rule.customPeriods, label); if (rule.settlementType === '固定天数周期结算' && !rule.cycleDays) { this.$message.warning(`${label}:请选择周期天数`); return false; } return true; },
validateSettlementRule(rule, label) { if (Number(rule.autoGenerate) !== 1) return true; if (!rule.billStartDate || !rule.settlementType) { this.$message.warning(`${label}:请完整填写账单起始日和结算类型`); return false; } if (rule.settlementType === '月结' && !rule.billCycleType) { this.$message.warning(`${label}:请选择结算周期`); return false; } if (rule.settlementType === '月结' && rule.billCycleType === '固定截单日' && !rule.billCutoffDay) { this.$message.warning(`${label}:请选择账单截单日`); return false; } if (rule.settlementType === '月结' && rule.billCycleType === '自定义多周期') return this.validateCustomPeriods(rule.customPeriods, label); if (rule.settlementType === '固定天数周期结算' && !rule.cycleDays) { this.$message.warning(`${label}:请选择周期天数`); return false; } return true; },
async submit() { await this.$refs.formRef.validate(); const total = this.paymentRatioRows.reduce((sum, row) => sum + Number(row.ratioLimit || 0), 0); if (this.paymentRatioRows.length && Math.abs(total - 100) > 0.0001) { this.$message.warning('付款比例上限合计必须等于100%'); return; } if (this.settlementConfigTab === 'pre') this.preSettlementConfig = { ...this.settlementRule }; else this.formalSettlementConfig = { ...this.settlementRule }; if (!this.validateSettlementRule(this.preSettlementConfig, '预结算配置') || !this.validateSettlementRule(this.formalSettlementConfig, '正式结算配置')) return; const settlementRule = { preSettlementConfig: this.preSettlementConfig, formalSettlementConfig: this.formalSettlementConfig }; await api.submitChange({ ...this.form, settlementCurrency: String(this.form.settlementCurrency || '').trim() || 'RMB', copyCount: normalizeOptionalPositiveInteger(this.form.copyCount), invoiceCycle: normalizeOptionalPositiveInteger(this.form.invoiceCycle), paymentDays: normalizeOptionalPositiveInteger(this.form.paymentDays), contractAmount: normalizeOptionalAmount(this.form.contractAmount), startDate: this.period[0], endDate: this.period[1], feeGenerationMode: this.feeGenerationMode, billingEnabled: this.feeGenerationMode === 'system' ? 1 : 0, billingPlanJson: JSON.stringify(this.plans), settlementRuleJson: JSON.stringify(settlementRule), preSettlementConfigJson: JSON.stringify(this.preSettlementConfig), formalSettlementConfigJson: JSON.stringify(this.formalSettlementConfig), paymentRatioJson: JSON.stringify(this.paymentRatioRows), contractFileJson: JSON.stringify(this.contractFileRows), attachmentsJson: JSON.stringify(this.attachments), changeContent: this.form.changeContent, changeReason: this.form.changeReason, changeAttachmentsJson: JSON.stringify(this.changeMaterials) }); await submitMkApprovalFlow({ bizType: 'contract-manage', formInstanceId: this.form.id, subjectName: this.form.contractName || '', approvalStatus: this.form.approvalStatus || 'change_rejected' }); this.$message.success('变更已提交'); this.$router.back(); },
},
};
@@ -654,6 +705,14 @@ export default {
.contract-basic-section :deep(.el-input),
.contract-basic-section :deep(.el-select),
.contract-basic-section :deep(.el-date-editor) { width: 360px; max-width: 100%; }
.contract-basic-section :deep(.el-input-number) {
width: 360px;
max-width: 100%;
.el-input__inner {
text-align: left;
}
}
.dialog-section-title { margin-bottom: 18px; font-size: 16px; font-weight: 600; }
.dialog-section-title::before { display: inline-block; width: 4px; height: 16px; margin-right: 8px; vertical-align: -2px; background: #409eff; content: ''; }
.section-head { display: flex; align-items: center; justify-content: space-between; }
@@ -0,0 +1,21 @@
<template>
<mk-public-shell biz-type="contract-manage" :get-form="getForm">
<contract-manage ref="page" />
</mk-public-shell>
</template>
<script>
import MkPublicShell from '@/views/mk/mk-public-shell.vue';
import ContractManage from '@/views/business/contract-manage.vue';
export default {
name: 'ContractManagePublicView',
components: { MkPublicShell, ContractManage },
methods: {
getForm() {
const row = this.$refs.page?.detailRow || {};
return { ...row, subject: row.contractName || row.contractNo || '' };
},
},
};
</script>
+107 -37
View File
@@ -222,7 +222,7 @@
Number(detailSettlementRule.autoGenerate) === 1 ? '开启' : '关闭'
}}</el-descriptions-item>
<template v-if="Number(detailSettlementRule.autoGenerate) === 1">
<el-descriptions-item label="账单起始日">{{
<el-descriptions-item label="账单起始日">{{
displayValue(detailSettlementRule.billStartDate)
}}</el-descriptions-item>
<el-descriptions-item label="结算类型">{{
@@ -244,7 +244,7 @@
}}</el-descriptions-item
>
<el-descriptions-item
v-if="detailSettlementRule.settlementType === '固定天数周期结算'"
v-if="isFixedCycleSettlementType(detailSettlementRule.settlementType)"
label="周期天数"
>{{
detailObjectUnitValue(detailSettlementRule, 'cycleDays', '天')
@@ -345,7 +345,7 @@
</section>
</div>
<div class="contract-manage-page__footer">
<el-button @click="closeDetail">关闭</el-button>
<el-button v-if="!isPublicViewPage" @click="closeDetail">关闭</el-button>
</div>
</template>
@@ -478,7 +478,7 @@
type="date"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
placeholder="YYYY-MM-DD"
placeholder="请输入"
/>
<span>至</span>
<el-date-picker
@@ -486,7 +486,7 @@
type="date"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
placeholder="YYYY-MM-DD"
placeholder="请输入"
/>
</div>
</el-form-item>
@@ -498,12 +498,12 @@
><template #suffix>天</template></el-input>
</el-form-item>
<el-form-item label="合同金额">
<el-input-number
v-model="form.contractAmount"
:min="0"
:precision="2"
:controls="false"
<el-input
:model-value="contractAmountDisplay"
placeholder="请输入"
clearable
@input="handleContractAmountInput"
@clear="form.contractAmount = null"
/>
</el-form-item>
<el-form-item label="是否范本">
@@ -593,11 +593,13 @@
description
attachment-type
use-upload-dialog
:attachment-location-options="contractAttachmentLocationOptions"
:lock-approved="isAttachmentUploadMode || hasApprovedContractFiles"
:mark-approved-on-upload="isAttachmentUploadMode"
:rows="contractFileRows"
:preview="previewAttachment"
@update:rows="contractFileRows = $event"
@upload-to-location="handleAttachmentUploadToLocation"
/>
<section class="contract-manage-form__section contract-manage-form__section--panel">
@@ -646,7 +648,7 @@
content="开启时,系统根据配置规则归集运单,定时生成结算单"
placement="top"
>
<el-icon class="settlement-switch-tip"><QuestionFilled /></el-icon>
<el-icon class="contract-manage-form__fee-mode-tip"><QuestionFilled /></el-icon>
</el-tooltip>
<el-radio :label="0">关闭</el-radio>
</el-radio-group>
@@ -655,13 +657,13 @@
v-if="settlementRuleEnabled"
class="contract-manage-form__grid contract-manage-form__settlement-grid"
>
<el-form-item label="账单起始日" required>
<el-form-item label="账单起始日" required>
<el-date-picker
v-model="settlementRuleForm.billStartDate"
type="date"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
placeholder="请选择账单起始日"
placeholder="请选择账单起始日"
/>
</el-form-item>
<el-form-item label="结算类型" required>
@@ -813,10 +815,12 @@
attachment-type
use-upload-dialog
attachment-location="其它附件"
:attachment-location-options="contractAttachmentLocationOptions"
:readonly="isAttachmentUploadMode"
:rows="attachmentRows"
:preview="previewAttachment"
@update:rows="attachmentRows = $event"
@upload-to-location="handleAttachmentUploadToLocation"
/>
<section
@@ -1027,6 +1031,7 @@ import NProgress from 'nprogress';
import 'nprogress/nprogress.css';
import { exportBlob } from '@/api/common';
import * as api from '@/api/business/contract-manage';
import { getMkPublicDetail } from '@/api/mk-process';
import { getList as getProjectList } from '@/api/business/project-apply';
import { getList as getCustomerArchiveList } from '@/api/vehicle/customer-archive';
import { getDeptTree } from '@/api/system/dept';
@@ -1058,7 +1063,15 @@ const normalizeOptionalInteger = (value, emptyValue = null) => {
return Number.isFinite(number) ? Math.trunc(number) : emptyValue;
};
const normalizeOptionalAmount = (value, emptyValue = null) => {
if (value === undefined || value === null || value === '') return emptyValue;
if (
value === undefined ||
value === null ||
value === '' ||
Number(value) === -1 ||
String(value) === '-1'
) {
return emptyValue;
}
const number = Number(value);
return Number.isFinite(number) && number >= 0 ? Number(number.toFixed(2)) : emptyValue;
};
@@ -1100,7 +1113,7 @@ const defaultForm = () => ({
settlementCurrency: '',
invoiceCycle: '',
paymentDays: '',
contractAmount: '',
contractAmount: null,
templateFlag: 0,
originalContractNo: '',
electronicSealFlag: 0,
@@ -1272,7 +1285,7 @@ export default {
formalSettlementRuleForm: defaultSettlementRule(),
paymentRatioRows: [],
changeRecordRows: [],
settlementTypeOptions: ['结', '日结', '周结', '月结', '固定天数周期结算'],
settlementTypeOptions: ['结', '日结', '周结', '月结', '固定天数周期'],
billCycleTypeOptions: ['固定截单日', '自然月', '自定义多周期'],
detailLoading: false,
detailRow: {},
@@ -1316,7 +1329,12 @@ export default {
return this.$route.path === '/business/contract-manage/form';
},
isDetailPage() {
return this.$route.path === '/business/contract-manage/detail';
return (
this.$route.path === '/business/contract-manage/detail' || this.isPublicViewPage
);
},
isPublicViewPage() {
return this.$route.path === '/business/contract-manage/public-view';
},
formMode() {
return this.$route.query.mode === 'edit' ? 'edit' : 'add';
@@ -1331,6 +1349,23 @@ export default {
isAttachmentUploadMode() {
return this.$route.query.attachmentUpload === '1';
},
contractAttachmentLocationOptions() {
if (this.isAttachmentUploadMode) return ['合同文件'];
return ['合同文件', '其它附件'];
},
contractAmountDisplay() {
const value = this.form.contractAmount;
if (
value === undefined ||
value === null ||
value === '' ||
Number(value) === -1 ||
String(value) === '-1'
) {
return '';
}
return value;
},
hasApprovedContractFiles() {
return (this.contractFileRows || []).some(
row =>
@@ -1412,7 +1447,7 @@ export default {
return this.showBillCycleType && this.settlementRuleForm.billCycleType === '自定义多周期';
},
showCycleDays() {
return this.settlementRuleForm.settlementType === '固定天数周期结算';
return this.isFixedCycleSettlementType(this.settlementRuleForm.settlementType);
},
billCutoffDayOptions() {
return Array.from({ length: 31 }, (_, index) => ({
@@ -1473,9 +1508,11 @@ export default {
},
},
created() {
this.loadProjectOptions();
this.loadSettlementDictionaries();
this.loadOrganizationOptions();
if (!this.isPublicViewPage) {
this.loadProjectOptions();
this.loadSettlementDictionaries();
this.loadOrganizationOptions();
}
if (this.isFormPage) this.initFormPage();
if (this.isDetailPage) this.initDetailPage();
},
@@ -1675,7 +1712,7 @@ export default {
copyCount: normalizeOptionalInteger(detail.copyCount, ''),
invoiceCycle: normalizeOptionalInteger(detail.invoiceCycle, ''),
paymentDays: normalizeOptionalInteger(detail.paymentDays, ''),
contractAmount: normalizeOptionalAmount(detail.contractAmount, ''),
contractAmount: normalizeOptionalAmount(detail.contractAmount, null),
archiveStatus: detail.archiveStatus || '未归档',
feeGenerationMode:
detail.feeGenerationMode || (Number(detail.billingEnabled) === 0 ? 'manual' : 'system'),
@@ -2118,6 +2155,13 @@ export default {
integerInput(prop, value) {
this.form[prop] = String(value || '').replace(/\D/g, '');
},
handleContractAmountInput(value) {
const text = String(value || '').replace(/[^\d.]/g, '');
const parts = text.split('.');
const normalized =
parts.length > 1 ? `${parts[0]}.${parts.slice(1).join('').slice(0, 2)}` : parts[0];
this.form.contractAmount = normalized === '' ? null : normalized;
},
positiveIntegerInput(prop, value) {
const normalized = String(value || '').replace(/\D/g, '').replace(/^0+/, '');
this.form[prop] = normalized;
@@ -2148,6 +2192,16 @@ export default {
};
this.attachmentDocumentPreviewVisible = true;
},
handleAttachmentUploadToLocation({ location, rows = [] }) {
if (!rows.length) return;
if (location === '合同文件') {
this.contractFileRows = [...(this.contractFileRows || []), ...rows];
return;
}
if (location === '其它附件') {
this.attachmentRows = [...(this.attachmentRows || []), ...rows];
}
},
handleAttachmentPreviewUnsupported() {
this.$message.warning('当前文件暂不支持在线预览');
},
@@ -2206,8 +2260,12 @@ export default {
}
return true;
},
isFixedCycleSettlementType(value) {
return value === '按固定天数周期' || value === '固定天数周期结算';
},
normalizeSettlementRule(rule = {}) {
const next = { ...defaultSettlementRule(), ...rule };
if (next.settlementType === '固定天数周期结算') next.settlementType = '按固定天数周期';
if (next.settlementType !== '月结') {
next.billCycleType = '';
next.billCutoffDay = '';
@@ -2222,7 +2280,7 @@ export default {
} else {
next.customPeriods = [];
}
if (next.settlementType !== '固定天数周期结算') next.cycleDays = '';
if (!this.isFixedCycleSettlementType(next.settlementType)) next.cycleDays = '';
return next;
},
syncSettlementConfig() {
@@ -2245,7 +2303,7 @@ export default {
this.settlementRuleForm.billCycleType = '';
this.settlementRuleForm.billCutoffDay = '';
this.settlementRuleForm.customPeriods = [];
if (value !== '固定天数周期结算') this.settlementRuleForm.cycleDays = '';
if (!this.isFixedCycleSettlementType(value)) this.settlementRuleForm.cycleDays = '';
},
handleBillCycleTypeChange(value) {
if (value !== '固定截单日') this.settlementRuleForm.billCutoffDay = '';
@@ -2336,7 +2394,7 @@ export default {
validateSettlementRule(rule, label) {
if (Number(rule.autoGenerate) !== 1) return true;
if (!rule.billStartDate || !rule.settlementType) {
this.$message.warning(`${label}:请完整填写账单起始日和结算类型`);
this.$message.warning(`${label}:请完整填写账单起始日和结算类型`);
return false;
}
if (rule.settlementType === '月结' && !rule.billCycleType) {
@@ -2354,7 +2412,7 @@ export default {
if (rule.settlementType === '月结' && rule.billCycleType === '自定义多周期') {
return this.validateCustomPeriods(rule.customPeriods, label);
}
if (rule.settlementType === '固定天数周期结算' && !rule.cycleDays) {
if (this.isFixedCycleSettlementType(rule.settlementType) && !rule.cycleDays) {
this.$message.warning(`${label}:请选择周期天数`);
return false;
}
@@ -2406,8 +2464,10 @@ export default {
if (!id) return;
this.detailLoading = true;
this.applyDetailState({});
this.api
.getDetail(id)
const request = this.isPublicViewPage
? getMkPublicDetail('contract-manage', id)
: this.api.getDetail(id);
request
.then(res => {
this.applyDetailState(res.data?.data || {});
})
@@ -2576,7 +2636,7 @@ export default {
.join('')}`
);
}
if (config.billStartDate) parts.push(`账单起始日${config.billStartDate}`);
if (config.billStartDate) parts.push(`账单起始日:${config.billStartDate}`);
return parts.join('');
};
if (normalized.preSettlementConfig || normalized.formalSettlementConfig) {
@@ -2672,6 +2732,15 @@ export default {
},
detailValue(prop) {
const value = this.detailRow[prop];
if (prop === 'contractAmount') {
return value === undefined ||
value === null ||
value === '' ||
Number(value) === -1 ||
String(value) === '-1'
? '-'
: value;
}
if (
prop === 'contractStage' ||
prop === 'approvalStatus' ||
@@ -3018,13 +3087,6 @@ export default {
align-items: center;
gap: 20px;
margin-bottom: 16px;
.settlement-switch-tip {
margin: 0 4px;
color: #a8abb2;
cursor: help;
font-size: 14px;
}
}
&__settlement-grid {
@@ -3082,11 +3144,19 @@ export default {
min-width: 0;
}
:deep(.el-input-number) {
width: 360px;
max-width: 100%;
.el-input__inner {
text-align: left;
}
}
:deep(.el-input),
:deep(.el-select),
:deep(.el-cascader),
:deep(.el-tree-select),
:deep(.el-input-number),
:deep(.el-date-editor) {
width: 360px;
max-width: 100%;
@@ -0,0 +1,149 @@
<template>
<div ref="page" class="project-apply-public-view">
<project-apply ref="project" />
</div>
</template>
<script>
import ProjectApply from './project-apply.vue';
import { postMkPublicProcessMessage } from '@/api/mk-process';
export default {
name: 'ProjectApplyPublicView',
components: {
ProjectApply,
},
created() {
this.handleIframeHeight = () => this.sendIframeHeight();
this.handleProcessMessage = event => this.onProcessMessage(event);
document.addEventListener('DOMContentLoaded', this.handleIframeHeight, false);
window.addEventListener('message', this.handleProcessMessage);
},
mounted() {
document.documentElement.classList.add('mk-iframe-page');
document.body.classList.add('mk-iframe-page');
const app = document.getElementById('app');
if (app) app.classList.add('mk-iframe-page');
this.handleIframeHeight();
window.addEventListener('load', this.handleIframeHeight);
this.mkHeightTimers = [300, 800, 1600].map(delay => setTimeout(this.handleIframeHeight, delay));
if (typeof ResizeObserver === 'undefined') return;
this.mkHeightObserver = new ResizeObserver(() => this.handleIframeHeight());
this.$nextTick(() => {
if (this.$refs.page) this.mkHeightObserver.observe(this.$refs.page);
this.mkHeightObserver.observe(document.body);
});
},
beforeUnmount() {
document.removeEventListener('DOMContentLoaded', this.handleIframeHeight, false);
window.removeEventListener('load', this.handleIframeHeight);
window.removeEventListener('message', this.handleProcessMessage);
(this.mkHeightTimers || []).forEach(timer => clearTimeout(timer));
if (this.mkHeightObserver) {
this.mkHeightObserver.disconnect();
this.mkHeightObserver = null;
}
document.documentElement.classList.remove('mk-iframe-page');
document.body.classList.remove('mk-iframe-page');
const app = document.getElementById('app');
if (app) app.classList.remove('mk-iframe-page');
},
methods: {
sendIframeHeight() {
this.$nextTick(() => {
window.parent.postMessage({ height: document.body.clientHeight }, '*');
});
},
onProcessMessage(event) {
const data = event && event.data;
if (!data || typeof data !== 'object') return;
if (data.height && !data.status && !data.type) return;
if (data.type === 'formValues' || data.type === 'afterSubmit') return;
const formValues = data.formValues;
if (data.status === 'submit') {
this.submitData(formValues);
} else if (data.status === 'save') {
this.saveData(formValues);
}
if (data.type === 'getFormValues') {
window.parent.postMessage({ type: 'formValues', formData: this.buildFormData() }, '*');
}
},
submitData(lbpmFormValues) {
const formData = this.buildFormData();
this.postFormData('submit', lbpmFormValues, formData);
if (!lbpmFormValues) return;
const parameters = Object.assign({}, lbpmFormValues, {
loginName: this.getLoginName(lbpmFormValues),
formInstanceId: this.getFormId(),
subject: formData.subject,
});
window.parent.postMessage({ type: 'afterSubmit', success: true, parameters }, '*');
},
saveData(lbpmFormValues) {
this.postFormData('save', lbpmFormValues, this.buildFormData());
},
postFormData(status, formValues, formData) {
postMkPublicProcessMessage('project-apply', {
status,
formValues: formValues || {},
formData,
}).catch(error => {
console.error('项目公开页提交流程数据失败:', error);
});
},
buildFormData() {
const form = this.getProjectForm();
return {
...form,
subject: form.projectName || form.projectShortName || '',
formInstanceId: this.getFormId(),
};
},
getProjectForm() {
const form = this.$refs.project && this.$refs.project.form;
if (!form) return {};
try {
return JSON.parse(JSON.stringify(form));
} catch (error) {
return { ...form };
}
},
getFormId() {
return this.$route.query.id || this.getProjectForm().id || '';
},
getLoginName(formValues = {}) {
return (
formValues.loginName ||
this.$route.query.loginName ||
this.$route.query.submitIdentity ||
''
);
},
},
};
</script>
<style lang="scss">
html.mk-iframe-page,
html.mk-iframe-page body,
html.mk-iframe-page #app,
html.mk-iframe-page #app.mk-iframe-page {
height: auto !important;
min-height: 100%;
overflow: visible;
}
</style>
<style lang="scss" scoped>
.project-apply-public-view {
min-height: 100%;
padding: 12px 0 24px;
box-sizing: border-box;
background: #f0f2f5;
:deep(.basic-container) {
padding: 0 12px;
}
}
</style>
+126 -1
View File
@@ -1,5 +1,5 @@
<template>
<basic-container class="project-apply-page">
<basic-container class="project-apply-page" :class="{ 'is-public-view': isPublicViewPage }">
<avue-crud
v-if="!isProjectFormPage"
:option="tableOption"
@@ -784,6 +784,7 @@
</el-form>
<div
v-if="!isPublicViewPage"
class="project-apply-dialog__footer"
:class="{ 'project-apply-dialog__footer--change': isChangeDialog }"
>
@@ -909,12 +910,29 @@
/>
</div>
</el-dialog>
<el-dialog
v-model="publicCustomerVisible"
title="查看客商档案"
append-to-body
destroy-on-close
width="92%"
top="4vh"
class="project-apply-public-customer-dialog"
>
<customer-archive
v-if="publicCustomerVisible"
:embedded-public-id="publicCustomerId"
/>
</el-dialog>
</basic-container>
</template>
<script>
import { exportBlob } from '@/api/common';
import { defineAsyncComponent } from 'vue';
import * as api from '@/api/business/project-apply';
import { getMkPublicDetail } from '@/api/mk-process';
import {
getList as getCustomerArchiveList,
getDetail as getCustomerArchiveDetail,
@@ -1045,7 +1063,9 @@ const majorProjectAttachmentTypeOptions = [
].map(item => ({ label: item, value: item })).concat(otherAttachmentType);
export default {
name: 'ProjectApply',
components: {
CustomerArchive: defineAsyncComponent(() => import('@/views/vehicle/customer-archive.vue')),
ElImageViewer,
OpenFileViewer,
PdfPreview,
@@ -1239,6 +1259,8 @@ export default {
changeRecordDetail: null,
changeRecordDetailRows: [],
userBox: false,
publicCustomerVisible: false,
publicCustomerId: '',
userPickType: '',
userLoading: false,
userData: [],
@@ -1315,6 +1337,9 @@ export default {
isProjectFormPage() {
return this.isFormPageInstance;
},
isPublicViewPage() {
return this.$route.path === '/business/project-apply/public-view';
},
projectFormContainer() {
// 表单页实例始终用页面容器,避免 keep-alive 失活时误切弹窗
return this.isFormPageInstance ? 'div' : 'el-dialog';
@@ -1361,6 +1386,11 @@ export default {
},
},
created() {
if (this.isPublicViewPage) {
this.isFormPageInstance = true;
this.openPublicProjectForm();
return;
}
this.loadDeptOptions();
this.loadCargoTypeOptions();
this.loadTransportTypeOptions();
@@ -1384,6 +1414,7 @@ export default {
this.attachmentDocumentPreviewVisible = false;
this.attachmentImagePreviewVisible = false;
this.userBox = false;
this.publicCustomerVisible = false;
},
buildTableOption() {
return {
@@ -1406,6 +1437,75 @@ export default {
hasPermission(code) {
return this.isAdmin || this.validData(this.permission && this.permission[code], false);
},
openPublicProjectForm() {
const id = this.$route.query.id;
this.dialogType = 'view';
this.dialogReadonly = true;
this.projectBox = true;
if (!id) {
this.$message.error('缺少项目ID');
return;
}
getMkPublicDetail('project-apply', id)
.then(res => {
const detail = res.data?.data || {};
this.applyPublicDictOptions(detail);
const displayDetail = { ...detail };
delete displayDetail.cargoTypeOptions;
delete displayDetail.transportTypeOptions;
delete displayDetail.settlementModeOptions;
this.applyProjectDetail(displayDetail);
})
.catch(() => {
this.$message.error('项目信息加载失败');
});
},
applyPublicDictOptions(detail = {}) {
this.cargoTypeOptions = this.resolvePublicDictOptions(
detail.cargoTypeOptions,
detail.cargoType
);
this.transportTypeOptions = this.resolvePublicDictOptions(
detail.transportTypeOptions,
detail.transportType
);
this.settlementModeOptions = this.resolvePublicDictOptions(
detail.settlementModeOptions,
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 }];
}
if (detail.undertakeDeptId) {
this.platformCompanyOptions = [
{
label: detail.undertakeDeptName || String(detail.undertakeDeptId),
value: detail.undertakeDeptId,
},
];
}
},
resolvePublicDictOptions(options, currentValue) {
const list = Array.isArray(options)
? options
.filter(item => item && item.value !== undefined && item.value !== null && item.value !== '')
.map(item => ({
label: item.label || String(item.value),
value: item.value,
}))
: [];
if (
currentValue !== undefined &&
currentValue !== null &&
currentValue !== '' &&
!list.some(item => String(item.value) === String(currentValue))
) {
list.unshift({ label: String(currentValue), value: currentValue });
}
return list;
},
openProjectFormPage() {
const type = this.$route.query.mode || 'add';
const id = this.$route.query.id;
@@ -2012,6 +2112,11 @@ export default {
this.$message.warning('客商档案 ID 为空无法打开');
return;
}
if (this.isPublicViewPage) {
this.publicCustomerId = String(id);
this.publicCustomerVisible = true;
return;
}
this.$router.push({
path: '/vehicle/customer-archive/form',
query: { id: String(id), name: '查看客商档案', view: '1' },
@@ -2594,6 +2699,12 @@ export default {
this.$message.warning('项目ID为空无法查看变更详情');
return;
}
if (this.isPublicViewPage) {
this.changeRecordDetail = { ...row };
this.changeRecordDetailRows = this.buildChangeRecordDetailRows(this.changeRecordDetail);
this.changeRecordDetailVisible = true;
return;
}
try {
if (!this.transportTypeOptions.length) {
await this.loadTransportTypeOptions();
@@ -2650,6 +2761,12 @@ export default {
</script>
<style lang="scss" scoped>
.project-apply-page.is-public-view {
:deep(.el-link) {
pointer-events: auto;
}
}
.project-apply-form {
padding: 0;
@@ -3019,3 +3136,11 @@ export default {
}
}
</style>
<style lang="scss">
.project-apply-public-customer-dialog .el-dialog__body {
max-height: 78vh;
overflow: auto;
padding-top: 8px;
}
</style>
+69 -13
View File
@@ -99,13 +99,25 @@
<el-table :data="attachmentRows" border @selection-change="selectedAttachments = $event">
<el-table-column type="selection" width="55" align="center" />
<el-table-column type="index" label="序号" width="70" align="center" />
<el-table-column label="文件名" min-width="240" show-overflow-tooltip>
<el-table-column label="文件名" min-width="240" show-overflow-tooltip>
<template #default="{ row }">
<el-link type="primary" @click="previewAttachment(row)">
{{ attachmentName(row) }}
</el-link>
</template>
</el-table-column>
<el-table-column label="附件描述" min-width="220">
<template #default="{ row }">
<span v-if="dialogReadonly">{{ row.description || '-' }}</span>
<el-input
v-else
v-model="row.description"
maxlength="200"
placeholder="请输入"
@input="syncAttachmentsJson"
/>
</template>
</el-table-column>
<el-table-column label="文件大小" width="120" align="center">
<template #default="{ row }">{{ formatFileSize(row.size) }}</template>
</el-table-column>
@@ -172,7 +184,7 @@
title="查看临时额度申请"
append-to-body
destroy-on-close
width="96%"
width="1100px"
class="temporary-credit-limit-dialog temporary-credit-limit-detail-dialog"
>
<div v-loading="detailLoading" class="business-crud-page__detail-content">
@@ -204,13 +216,16 @@
</div>
<el-table :data="attachmentRows" empty-text="暂无附件">
<el-table-column type="index" label="序号" width="70" align="center" />
<el-table-column label="文件名" min-width="240" show-overflow-tooltip>
<el-table-column label="文件名" min-width="240" show-overflow-tooltip>
<template #default="{ row }">
<el-link type="primary" @click="previewAttachment(row)">
{{ attachmentName(row) }}
</el-link>
</template>
</el-table-column>
<el-table-column label="附件描述" min-width="220" show-overflow-tooltip>
<template #default="{ row }">{{ row.description || '-' }}</template>
</el-table-column>
<el-table-column label="文件大小" width="120" align="center">
<template #default="{ row }">{{ formatFileSize(row.size) }}</template>
</el-table-column>
@@ -783,24 +798,37 @@ export default {
const uploadTime = this.$dayjs().format('YYYY-MM-DD HH:mm:ss');
this.attachmentRows = (list || []).map(item => ({
...item,
description: item.description || '',
uploadUserName: item.uploadUserName || uploadUserName,
uploadTime: item.uploadTime || uploadTime,
}));
this.form.attachmentsJson = JSON.stringify(this.attachmentRows);
this.syncAttachmentsJson();
},
removeAttachment(index) {
this.attachmentRows.splice(index, 1);
this.form.attachmentsJson = JSON.stringify(this.attachmentRows);
this.syncAttachmentsJson();
},
syncAttachmentsJson() {
this.form.attachmentsJson = JSON.stringify(this.attachmentRows || []);
},
parseJsonArray(value) {
if (Array.isArray(value)) return value;
if (!value) return [];
try {
const data = JSON.parse(value);
return Array.isArray(data) ? data : [];
} catch (error) {
let list = [];
if (Array.isArray(value)) {
list = value;
} else if (!value) {
return [];
} else {
try {
const data = JSON.parse(value);
list = Array.isArray(data) ? data : [];
} catch (error) {
return [];
}
}
return list.map(item => ({
...item,
description: item?.description || '',
}));
},
attachmentName(row = {}) {
return row.originalName || row.name || row.fileName || '附件';
@@ -907,6 +935,7 @@ export default {
.temporary-credit-limit-page {
&__field {
width: 100%;
max-width: 240px;
}
&__attachment-head {
@@ -993,7 +1022,7 @@ export default {
align-items: center;
gap: 8px;
width: 100%;
padding: 14px 16px 4px;
padding: 14px 0 4px;
margin-bottom: 0;
color: #303133;
font-size: 15px;
@@ -1086,9 +1115,36 @@ export default {
background: transparent !important;
box-shadow: none !important;
margin: 0 !important;
padding: 0 !important;
padding: 0 16px 8px !important;
border-radius: 0 !important;
}
// 新增/编辑:控件限宽,避免宽屏下撑满列宽贴到弹窗右边缘
&:not(.temporary-credit-limit-detail-dialog) {
.el-form-item__content {
min-width: 0;
}
.el-form-item__content > .el-input,
.el-form-item__content > .el-select,
.el-form-item__content > .el-date-editor,
.el-form-item__content > .el-cascader,
.el-form-item__content > .el-textarea,
.temporary-credit-limit-page__field {
width: 100%;
max-width: 240px;
}
.el-form-item__content > .el-textarea {
max-width: 100%;
}
.el-date-editor.el-input,
.el-date-editor.el-input__wrapper {
width: 100%;
max-width: 240px;
}
}
}
.temporary-credit-limit-dialog .business-crud-page__detail-content .el-descriptions__label {
@@ -0,0 +1,38 @@
<template>
<mk-public-shell biz-type="waybill-manage" :get-form="getForm">
<waybill-manage-page
ref="page"
:api="api"
:config="config"
:crud-option="option"
:detail-id="detailId"
standalone-detail-page
/>
</mk-public-shell>
</template>
<script>
import MkPublicShell from '@/views/mk/mk-public-shell.vue';
import WaybillManagePage from '@/views/business/components/waybill-manage-page.vue';
import * as api from '@/api/business/waybill-manage';
import { config, option } from '@/option/business/waybill-manage';
export default {
name: 'WaybillManagePublicView',
components: { MkPublicShell, WaybillManagePage },
data() {
return { api, config, option };
},
computed: {
detailId() {
return this.$route.query.id || '';
},
},
methods: {
getForm() {
const row = this.$refs.page?.detailRow || {};
return { ...row, subject: row.waybillNo || '' };
},
},
};
</script>