- 下载整车对账模板
- 下载货物明细对账模板
导入
@@ -374,6 +382,11 @@
formatMoney(row.settlementAmount, row.currency)
}}
+ {{
+ formalApprovalStatusLabel(row.approvalStatus)
+ }}
选择
@@ -1081,6 +1094,19 @@ export default {
getFeeItemAmount(row, name) {
return (row.feeItems || this.parseFeeItems(row.feeItemsJson))[name] ?? 0;
},
+ formalApprovalStatusLabel(value) {
+ return (
+ {
+ draft: '草稿',
+ reviewing: '审批中',
+ approved: '审批通过',
+ returned: '已驳回',
+ voided: '已作废',
+ }[value] ||
+ value ||
+ '-'
+ );
+ },
async loadFormalOptions() {
this.formalDialog.loading = true;
try {
@@ -1096,20 +1122,8 @@ export default {
params
)
);
- let rows = data.records || [];
- let total = data.total || 0;
- if (!rows.length) {
- const fallback = await formalSettlementApi.getList(
- this.formalDialog.page.current,
- this.formalDialog.page.size,
- params
- );
- const fallbackData = this.unwrapData(fallback);
- rows = fallbackData.records || [];
- total = fallbackData.total || 0;
- }
- this.formalDialog.rows = rows;
- this.formalDialog.page.total = total;
+ this.formalDialog.rows = data.records || [];
+ this.formalDialog.page.total = data.total || 0;
} finally {
this.formalDialog.loading = false;
}
@@ -1120,6 +1134,9 @@ export default {
await this.selectFormal(this.formalDialog.selected[0]);
},
async selectFormal(selected) {
+ if (selected?.approvalStatus && selected.approvalStatus !== 'draft') {
+ return this.$message.warning('只能选择草稿状态的正式结算单');
+ }
const formalSettlementChanged = this.form.formalSettlementId !== selected.id;
this.form = {
...this.form,
@@ -1489,7 +1506,14 @@ export default {
}
},
async downloadTemplate(mode) {
- const response = await api.template(mode);
+ if (!this.form.formalSettlementId) {
+ return this.$message.warning('请先选择正式结算单');
+ }
+ const response = await api.template(mode, {
+ id: this.currentId,
+ formalSettlementId: this.form.formalSettlementId,
+ feeItems: this.internalFeeItemNames.join(','),
+ });
downloadXls(
response.data,
`${mode === 'cargo' ? '货物明细对账模板' : '整车总额对账模板'}.xlsx`
diff --git a/src/views/settlement/receivable-payable-detail.vue b/src/views/settlement/receivable-payable-detail.vue
index 8917264..171d21b 100644
--- a/src/views/settlement/receivable-payable-detail.vue
+++ b/src/views/settlement/receivable-payable-detail.vue
@@ -145,7 +145,9 @@
:align="column.align || 'center'"
show-overflow-tooltip
>
- {{ formatDetailCell(row, column.prop) }}
+
+ {{ formatFeeDetailCell(row, column.prop) }}
+
@@ -398,12 +400,14 @@
maxlength="300"
placeholder="请输入调整原因"
/>
-
- {{ fixedTwoDecimals(row.adjustAmount) }}
-
-
- {{ fixedTwoDecimals(row.afterAmount) }}
+
+ {{
+ Number(row.adjustAmount || 0) !== 0
+ ? fixedTwoDecimals(row.afterAmount)
+ : fixedTwoDecimals(row.originalAmount)
+ }}
+
{{ formatDetailCell(row, column.prop) }}
@@ -1135,7 +1139,13 @@ export default {
},
feeDetailColumns() {
const baseColumns = feeDetailBaseColumns.filter(column => column.prop !== 'freightAmount');
- return [...baseColumns, ...this.dynamicFeeColumns, ...feeDetailTailColumns];
+ const hasAdjustment = this.feeRows.some(row => Number(row.adjustAmount || 0) !== 0);
+ const tailColumns = feeDetailTailColumns.map(col =>
+ col.prop === 'originalAmountText' && hasAdjustment
+ ? { ...col, label: '调整后金额' }
+ : col
+ );
+ return [...baseColumns, ...this.dynamicFeeColumns, ...tailColumns];
},
displayTableColumns() {
const columns = this.tableColumns.filter(
@@ -1181,7 +1191,7 @@ export default {
adjustFeeColumns() {
const baseColumns = feeDetailBaseColumns.filter(column => column.prop !== 'freightAmount');
const tailColumns = feeDetailTailColumns.filter(column => column.prop !== 'remark');
- const afterAmountIndex = tailColumns.findIndex(column => column.prop === 'afterAmountText');
+ const afterAmountIndex = tailColumns.findIndex(column => column.prop === 'originalAmountText');
tailColumns.splice(afterAmountIndex + 1, 0, {
label: '调整原因',
prop: 'changeReason',
@@ -2596,6 +2606,22 @@ export default {
}
return this.formatCell(row?.[prop]);
},
+ formatFeeDetailCell(row, prop) {
+ if (prop !== 'originalAmountText') return this.formatDetailCell(row, prop);
+
+ const adjustAmount = Number(row?.adjustAmount || 0);
+ if (adjustAmount === 0) return this.formatDetailCell(row, prop);
+
+ const adjustedAmount = Number(
+ row?.afterAmount ?? row?.adjustedAmount ?? row?.settlementAmount
+ );
+ if (Number.isFinite(adjustedAmount)) return this.fixedTwoDecimals(adjustedAmount);
+
+ const originalAmount = Number(row?.originalAmount ?? row?.originalAmountText);
+ return Number.isFinite(originalAmount)
+ ? this.fixedTwoDecimals(originalAmount + adjustAmount)
+ : this.formatCell(row?.afterAmountText);
+ },
formatCell(value) {
return value === null || value === undefined || value === '' ? '-' : value;
},
diff --git a/src/views/transportCapacity/driver.vue b/src/views/transportCapacity/driver.vue
index 40389ee..7568f3a 100644
--- a/src/views/transportCapacity/driver.vue
+++ b/src/views/transportCapacity/driver.vue
@@ -225,7 +225,11 @@
v-model="driverForm.drivingVehicle"
maxlength="30"
clearable
- placeholder="请输入或选择车辆"
+ readonly
+ placeholder="请选择车辆"
+ :disabled="readonly"
+ @clear="driverForm.drivingVehicle = ''"
+ @click="!readonly && openVehicleSelector()"
>
@@ -582,8 +586,8 @@
{{ String(row.organizationName || '').replace(/^[\s\u3000]+/, '') }}
-
- -
+
+ {{ row.boundDriver || '-' }}
diff --git a/src/views/transportCapacity/vehicle.vue b/src/views/transportCapacity/vehicle.vue
index f4cd7d5..6e1f59a 100644
--- a/src/views/transportCapacity/vehicle.vue
+++ b/src/views/transportCapacity/vehicle.vue
@@ -66,8 +66,8 @@
{{ row.status === 1 ? '启用' : '停用' }}
-
- -
+
+ {{ row.boundDriver || '-' }}
@@ -541,19 +541,14 @@
-
-
-
+ />
@@ -908,6 +903,15 @@ export default {
this.vehicleForm.organizationName = this.resolveOrgName(path);
},
},
+ // 使用部门级联:与所属组织同一套部门树
+ useDepartmentCascaderValue: {
+ get() {
+ return this.resolveOrgPath(this.vehicleForm.useDepartment);
+ },
+ set(path) {
+ this.vehicleForm.useDepartment = this.resolveOrgName(path);
+ },
+ },
isAdmin() {
const authority = this.userInfo.authority || '';
return authority.includes('admin');
diff --git a/src/views/vehicle/customer-archive.vue b/src/views/vehicle/customer-archive.vue
index 1fbd0f8..4ab3444 100644
--- a/src/views/vehicle/customer-archive.vue
+++ b/src/views/vehicle/customer-archive.vue
@@ -57,6 +57,36 @@
{{ accessTypeMap[row.accessType] || '临时客商' }}
+
+
+ {{ formatBusinessLicenseValidPeriod(row) }}
+
+
+
+ {{ formatFundUseRate(row) }}
+
+
+
+ {{ row.fundUseRiskName || '高风险' }}
+
+
+ {{ row.fundUseRiskName || '中风险' }}
+
+ -
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
@@ -1676,6 +1707,7 @@ export default {
rejected: { label: '审核不通过', type: 'danger' },
},
formRules: {
+ customerCode: [{ max: 20, message: '客商编号最多20个字符', trigger: 'blur' }],
fullName: [{ required: true, message: '请输入客商名称', trigger: 'blur' }],
customerNature: [{ required: true, message: '请选择客商性质', trigger: 'change' }],
unifiedCreditCode: [
@@ -1856,6 +1888,24 @@ export default {
{ label: '正式', value: 'formal' },
],
},
+ {
+ label: '营业执照有效期',
+ prop: 'businessEndDate',
+ slot: true,
+ minWidth: 140,
+ },
+ {
+ label: '资金使用情况',
+ prop: 'fundUseRate',
+ slot: true,
+ minWidth: 130,
+ },
+ {
+ label: '资金使用额度风险',
+ prop: 'fundUseRisk',
+ slot: true,
+ minWidth: 150,
+ },
{
label: '审批状态',
prop: 'approvalStatus',
@@ -2098,6 +2148,7 @@ export default {
accessType: 'temporary',
approvalStatus: 'draft',
status: 1,
+ customerKind: 'external',
deptId: [],
deptIds: '',
deptName: '',
@@ -2268,6 +2319,40 @@ export default {
if (typeof normalized === 'object') return Object.keys(normalized).length === 0;
return false;
},
+ normalizeComparableChangeValue(value) {
+ const normalized = this.normalizeChangeFieldValue(value);
+ if (this.isEmptyChangeValue(normalized)) return null;
+ if (typeof normalized === 'number') return Number.isFinite(normalized) ? normalized : null;
+ if (typeof normalized === 'boolean') return normalized;
+ if (typeof normalized === 'string') {
+ const text = normalized.trim();
+ if (/^-?\d+(\.\d+)?$/.test(text)) {
+ const number = Number(text);
+ return Number.isFinite(number) ? number : text;
+ }
+ return text;
+ }
+ if (Array.isArray(normalized)) {
+ return normalized.map(item => this.normalizeComparableChangeValue(item));
+ }
+ if (normalized && typeof normalized === 'object') {
+ return Object.keys(normalized)
+ .sort()
+ .reduce((result, key) => {
+ result[key] = this.normalizeComparableChangeValue(normalized[key]);
+ return result;
+ }, {});
+ }
+ return normalized;
+ },
+ isSameChangeValue(beforeValue, afterValue) {
+ if (beforeValue === afterValue) return true;
+ if (this.isEmptyChangeValue(beforeValue) && this.isEmptyChangeValue(afterValue)) return true;
+ return (
+ JSON.stringify(this.normalizeComparableChangeValue(beforeValue)) ===
+ JSON.stringify(this.normalizeComparableChangeValue(afterValue))
+ );
+ },
formatScoreChangeValue(value) {
const scores = Array.isArray(value) ? value : [value];
const categoryNames = { basic: '基础得分项', plus: '加分项目', minus: '减分项目' };
@@ -2354,7 +2439,7 @@ export default {
fields.forEach(([field, label]) => {
const leftValue = left[field];
const rightValue = right[field];
- if (JSON.stringify(leftValue) === JSON.stringify(rightValue)) return;
+ if (this.isSameChangeValue(leftValue, rightValue)) return;
const prefix = size > 1 ? `第${index + 1}条-${label}` : label;
before.push(`${prefix}:${formatValue(field, leftValue)}`);
after.push(`${prefix}:${formatValue(field, rightValue)}`);
@@ -2437,11 +2522,7 @@ export default {
const rows = fields
.filter(
field =>
- field !== '变更内容' &&
- !(
- this.isEmptyChangeValue(beforeData[field]) &&
- this.isEmptyChangeValue(afterData[field])
- )
+ field !== '变更内容' && !this.isSameChangeValue(beforeData[field], afterData[field])
)
.map(field => ({
field: this.getChangeFieldLabel(field),
@@ -2451,7 +2532,8 @@ export default {
before: this.formatChangeFieldValue(field, beforeData[field]),
after: this.formatChangeFieldValue(field, afterData[field]),
}),
- }));
+ }))
+ .filter(item => item.before !== item.after);
const content = String(row.changeContent || '').trim();
if (content) {
rows.unshift({ field: '变更内容', before: '空', after: content });
@@ -2467,27 +2549,11 @@ export default {
this.changeRecordDetailVisible = true;
},
formatChangeContent(row) {
- const beforeData = this.parseChangeData(row.beforeData);
- const afterData = this.parseChangeData(row.afterData);
- const fields = [...new Set([...Object.keys(beforeData), ...Object.keys(afterData)])];
- const details = fields
- .filter(
- field =>
- field !== '变更内容' &&
- !(
- this.isEmptyChangeValue(beforeData[field]) &&
- this.isEmptyChangeValue(afterData[field])
- )
- )
- .map(field => {
- const scoreDiff = this.isScoreChangeField(field)
- ? this.formatScoreChangeDiff(beforeData[field], afterData[field])
- : null;
- const before = scoreDiff?.before || this.formatChangeFieldValue(field, beforeData[field]);
- const after = scoreDiff?.after || this.formatChangeFieldValue(field, afterData[field]);
- return `${this.getChangeFieldLabel(field)}:变更前:${before} → 变更后:${after}`;
- });
+ const rows = this.buildChangeRecordDetailRows(row);
const content = String(row.changeContent || '').trim();
+ const details = rows
+ .filter(item => item.field !== '变更内容')
+ .map(item => `${item.field}:变更前:${item.before} → 变更后:${item.after}`);
return [content, ...details].filter(Boolean).join(';');
},
emptyContact() {
@@ -2833,6 +2899,25 @@ export default {
}
});
},
+ formatBusinessLicenseValidPeriod(row = {}) {
+ if (row.businessTermType === '长期') return '无固定期限';
+ return row.businessEndDate || '';
+ },
+ formatFundUseRate(row = {}) {
+ if (row.fundUseRate === undefined || row.fundUseRate === null || row.fundUseRate === '') {
+ return '-';
+ }
+ const rate = Number(row.fundUseRate);
+ return Number.isFinite(rate) ? `${rate.toFixed(2)}%` : '-';
+ },
+ isBusinessLicenseExpiringSoon(row = {}) {
+ if (row.businessTermType === '长期' || !row.businessEndDate) return false;
+ const endDate = this.$dayjs(row.businessEndDate);
+ if (!endDate.isValid()) return false;
+ const today = this.$dayjs().startOf('day');
+ const expireSoonDate = today.add(30, 'day');
+ return !endDate.isAfter(expireSoonDate, 'day');
+ },
handleBusinessEndDateChange(value) {
if (value) {
this.archiveForm.businessTermType = '固定期限';
@@ -4737,6 +4822,26 @@ export default {
--el-tag-text-color: #409eff;
}
+.business-license-expiring {
+ color: var(--el-color-danger);
+}
+
+.fund-use-risk-none {
+ color: #c0c4cc;
+}
+
+.fund-use-risk-header {
+ display: inline-flex;
+ align-items: center;
+}
+
+.fund-use-risk-help {
+ margin-left: 4px;
+ color: #a8abb2;
+ cursor: help;
+ font-size: 14px;
+}
+
.archive-form {
:deep(.el-form-item__label) {
flex: 0 0 calc(6em + 24px) !important;