diff --git a/src/api/business/waybill-manage.js b/src/api/business/waybill-manage.js
index ac31975..eb66cb3 100644
--- a/src/api/business/waybill-manage.js
+++ b/src/api/business/waybill-manage.js
@@ -14,6 +14,13 @@ export const cancel = api.cancel;
export const reassign = api.reassign;
export const complete = api.complete;
export const batchComplete = api.batchComplete;
+/** 确认回单:待确认回单运单完成后转为已完成 */
+export const confirmReturn = id =>
+ request({
+ url: `${baseUrl}/confirm-return`,
+ method: 'post',
+ params: { id },
+ });
export const changeRoute = data =>
request({
url: `${baseUrl}/change-route`,
diff --git a/src/api/system/dept.js b/src/api/system/dept.js
index c7f22de..2964557 100644
--- a/src/api/system/dept.js
+++ b/src/api/system/dept.js
@@ -139,3 +139,10 @@ export const getPlatformCompanySelect = () => {
method: 'get',
});
};
+
+export const getBusinessDeptSelect = () => {
+ return request({
+ url: '/blade-system/dept/business-dept-select',
+ method: 'get',
+ });
+};
diff --git a/src/axios.js b/src/axios.js
index a3de4a3..455eb3d 100644
--- a/src/axios.js
+++ b/src/axios.js
@@ -105,12 +105,27 @@ const isJsonContentType = res => {
return contentType.includes('application/json') || contentType.includes('text/json');
};
+/**
+ * 是否为可按文本读取的 Blob / Blob-like(含跨 realm)。
+ * 禁止对普通业务 JSON 对象调用 .text()。
+ */
+const isBlobLikeData = value =>
+ value != null &&
+ typeof value === 'object' &&
+ typeof value.size === 'number' &&
+ typeof value.type === 'string' &&
+ typeof value.text === 'function';
+
/**
* 判断响应体是否已是解析好的对象(无需再解析)。
* 注意 Blob 也是 object,必须排除,否则会把待解析的文件流误判为已完成。
*/
const isPlainObject = value =>
- value !== null && typeof value === 'object' && !(value instanceof Blob);
+ value !== null &&
+ typeof value === 'object' &&
+ !(value instanceof Blob) &&
+ !isBlobLikeData(value) &&
+ !(value instanceof ArrayBuffer);
//跨域请求,允许保存cookie
axios.defaults.withCredentials = true;
// NProgress Configuration
@@ -190,15 +205,20 @@ axios.interceptors.response.use(
// 用户就会以为系统崩了——而后端其实返回了明确的业务提示(如「未读取到数据行…」)。
// 这里按 Content-Type 判断:JSON 才解析回对象;真·文件流(Excel 等)保持原样不动。
// 兼容 Blob(浏览器 responseType='blob')与 string(非浏览器环境)两种形态。
+ // 仅对真正的 Blob/Blob-like 调 .text(),避免业务 JSON 已解析后出现
+ // 「e.data.text is not a function」。
if (isJsonContentType(res) && !isPlainObject(res.data)) {
try {
let text = '';
- if (typeof res.data?.text === 'function') {
- text = await res.data.text();
- } else if (typeof res.data === 'string') {
- text = res.data;
- } else if (typeof res.data?.arrayBuffer === 'function') {
- text = new TextDecoder().decode(await res.data.arrayBuffer());
+ const raw = res.data;
+ if (isBlobLikeData(raw) || (typeof Blob !== 'undefined' && raw instanceof Blob)) {
+ text = await raw.text();
+ } else if (typeof raw === 'string') {
+ text = raw;
+ } else if (raw && typeof raw.arrayBuffer === 'function') {
+ text = new TextDecoder().decode(await raw.arrayBuffer());
+ } else if (raw instanceof ArrayBuffer) {
+ text = new TextDecoder().decode(raw);
}
const parsed = typeof text === 'string' && text ? JSON.parse(text) : null;
if (parsed && typeof parsed === 'object') {
@@ -208,10 +228,11 @@ axios.interceptors.response.use(
// 声明是 JSON 却解析失败:保持原样,交给下游按文件处理
}
}
- //获取状态信息
- const status = res.data.error_code || res.data.code || res.status;
+ // 获取状态信息:文件流没有 code/msg,必须退回 HTTP status,禁止读 Blob 上的幽灵字段
+ const payload = isPlainObject(res.data) ? res.data : {};
+ const status = Number(payload.error_code || payload.code || res.status);
const statusWhiteList = website.statusWhiteList || [];
- const message = res.data.msg || res.data.error_description || '系统错误';
+ const message = payload.msg || payload.error_description || '系统错误';
//如果在白名单里则自行catch逻辑处理
if (statusWhiteList.includes(status)) return Promise.reject(res);
diff --git a/src/option/business/common.js b/src/option/business/common.js
index 48ff87e..729199e 100644
--- a/src/option/business/common.js
+++ b/src/option/business/common.js
@@ -42,6 +42,7 @@ export const waybillStatusOptions = [
{ label: '草稿', value: 'draft' },
{ label: '待执行', value: 'pending' },
{ label: '进行中', value: 'running' },
+ { label: '待确认回单', value: 'pending_return' },
{ label: '已完成', value: 'completed' },
{ label: '已取消', value: 'cancelled' },
];
diff --git a/src/option/business/waybill-manage.js b/src/option/business/waybill-manage.js
index 9f617d3..9fd695b 100644
--- a/src/option/business/waybill-manage.js
+++ b/src/option/business/waybill-manage.js
@@ -478,7 +478,15 @@ export const config = {
formatStatus(row, prop, defaultText) {
if (prop !== 'businessStatus') return defaultText;
const status = String(row.businessStatus || '');
- const terminalStatuses = ['draft', 'completed', 'cancelled', 'waiting_dispatch', 'dispatching'];
+ const terminalStatuses = [
+ 'draft',
+ 'completed',
+ 'cancelled',
+ 'waiting_dispatch',
+ 'dispatching',
+ 'pending_return',
+ ];
+ if (status === 'pending_return') return '待确认回单';
const needAccept =
row.requireAccept === true ||
row.requireAccept === 1 ||
@@ -497,14 +505,20 @@ export const config = {
getDriverRejectReason,
isDriverRejectedWaybill,
canEdit(row) {
- // 进行中的运单不允许编辑
+ // 进行中、待确认回单不允许编辑
if (isInProgressStatus(row, row.businessStatusName)) {
return false;
}
+ if (String(row.businessStatus || '') === 'pending_return') {
+ return false;
+ }
return !isDriverRejectedWaybill(row);
},
canCancel(row) {
- // 进行中的运单不允许取消
+ // 进行中、待确认回单不允许取消
+ if (String(row.businessStatus || '') === 'pending_return') {
+ return false;
+ }
return !isInProgressStatus(row, row.businessStatusName);
},
deleteStatus: ['draft'],
diff --git a/src/option/system/mpUser.js b/src/option/system/mpUser.js
index 994f76f..bc06d41 100644
--- a/src/option/system/mpUser.js
+++ b/src/option/system/mpUser.js
@@ -14,6 +14,11 @@ const validatePhone = (rule, value, callback) => {
callback(new Error('请输入手机号'));
return;
}
+ // 详情回显为脱敏手机号(含 *)时放行,未修改则提交时剔除该字段
+ if (phone.includes('*')) {
+ callback();
+ return;
+ }
if (!/^1\d{10}$/.test(phone)) {
callback(new Error('请输入正确的手机号'));
return;
@@ -121,6 +126,62 @@ export default {
order: 60,
rules: [{ required: true, message: '请选择小程序身份', trigger: 'change' }],
},
+ {
+ label: '所属组织',
+ prop: 'deptId',
+ formslot: true,
+ hide: true,
+ span: 24,
+ order: 55,
+ rules: [{ required: true, message: '请选择所属组织', trigger: 'change' }],
+ },
+ {
+ label: '所属组织',
+ prop: 'deptName',
+ minWidth: 180,
+ overHidden: true,
+ addDisplay: false,
+ editDisplay: false,
+ viewDisplay: false,
+ order: 54,
+ },
+ {
+ label: '数据权限范围',
+ prop: 'dataScopeRange',
+ type: 'radio',
+ span: 24,
+ hide: true,
+ order: 53,
+ value: 1,
+ dicData: [
+ { label: '仅所属组织', value: 1 },
+ { label: '全部组织', value: 2 },
+ { label: '自定义', value: 3 },
+ ],
+ rules: [{ required: true, message: '请选择数据权限范围', trigger: 'change' }],
+ },
+ {
+ label: '数据层级范围',
+ prop: 'dataLevelRange',
+ type: 'radio',
+ span: 24,
+ hide: true,
+ order: 52,
+ value: 1,
+ dicData: [
+ { label: '包含下级', value: 1 },
+ { label: '仅本级', value: 2 },
+ ],
+ rules: [{ required: true, message: '请选择数据层级范围', trigger: 'change' }],
+ },
+ {
+ label: '数据权限',
+ prop: 'dataScopeDeptIds',
+ formslot: true,
+ span: 24,
+ hide: true,
+ order: 51,
+ },
{
label: '性别',
prop: 'sex',
diff --git a/src/utils/import-excel.js b/src/utils/import-excel.js
index b6a9093..1bd5e02 100644
--- a/src/utils/import-excel.js
+++ b/src/utils/import-excel.js
@@ -88,7 +88,7 @@ export const parseBlobJson = async data => {
*
* 这里补上这一步:从错误响应的 Blob 里取出后端 `msg`,取不到再退回通用文案。
*/
-const resolveErrorMessage = async error => {
+export const resolveImportErrorMessage = async error => {
const payload = error?.response?.data ?? error?.data;
if (isBusinessJson(payload) && payload.msg) {
return payload.msg;
@@ -109,6 +109,26 @@ const resolveErrorMessage = async error => {
return fallback;
};
+/**
+ * 统一消费导入接口响应:成功 JSON / 失败 Excel 流。
+ * 兼容 axios 已把 JSON Blob 解析成对象的情况,禁止再对普通对象调 .text()。
+ */
+export const consumeImportResponse = async (res, options = {}) => {
+ const { businessName = '导入', dayjsFormat } = options;
+ if (isExcelResponse(res)) {
+ return {
+ kind: 'excel',
+ blob: res?.data,
+ fileName: `${businessName}导入失败明细${dayjsFormat || ''}.xlsx`,
+ };
+ }
+ const result = isBusinessJson(res?.data) ? res.data : await parseBlobJson(res?.data);
+ if (Number(result?.code) !== 200) {
+ throw new Error(result?.msg || '导入失败');
+ }
+ return { kind: 'success', result };
+};
+
/**
* 加工后端返回的导入失败明细 Excel。
* 若传入 failDetailDecorator(函数),则动态加载 exceljs 读取 blob 流,
@@ -235,7 +255,7 @@ export const handleImportExcel = async (vm, option, column, businessName, onSucc
// 拦截器已提示过的错误(如后端 400 返回的业务提示)不再重复弹一次,
// 只保留一次清晰的中文提示。
if (!error?._notified) {
- vm.$message.error(await resolveErrorMessage(error));
+ vm.$message.error(await resolveImportErrorMessage(error));
}
notifyUploadError(option, error);
} finally {
diff --git a/src/views/business/components/business-crud-page.vue b/src/views/business/components/business-crud-page.vue
index eb23da3..36aa8b5 100644
--- a/src/views/business/components/business-crud-page.vue
+++ b/src/views/business/components/business-crud-page.vue
@@ -13824,13 +13824,13 @@ export default {
}
const unavailableWaybills = this.selectionList.filter(
row =>
- this.statusValue(row) !== 'pending' ||
+ !['pending', 'processing', 'running'].includes(this.statusValue(row)) ||
row.loadingId ||
row.loadingManageId ||
row.loadingNo
);
if (unavailableWaybills.length) {
- this.$message.warning('公路配载仅支持进行中且未配载的运单');
+ this.$message.warning('公路配载仅支持待执行/进行中且未关联有效配载单的运单');
return;
}
try {
diff --git a/src/views/business/components/master-order-editor.vue b/src/views/business/components/master-order-editor.vue
index 74ce647..6ad4fde 100644
--- a/src/views/business/components/master-order-editor.vue
+++ b/src/views/business/components/master-order-editor.vue
@@ -94,7 +94,7 @@
@@ -111,7 +111,7 @@
@@ -714,7 +714,7 @@ export default {
'msg',
'zip',
],
- transports: ['公路运输', '铁路运输', '水路运输', '航空运输'],
+ transports: ['铁路运输', '水路运输', '航空运输', '公路运输'],
quantityUnitOptions: [],
quantityUnitLoading: false,
form: {
@@ -798,6 +798,7 @@ export default {
methods: {
formatContractOptionLabel,
defaultQuantityUnit() {
+ if (this.quantityUnitOptions.includes('吨')) return '吨';
return this.quantityUnitOptions[0] || '吨';
},
loadQuantityUnitOptions() {
@@ -1164,22 +1165,30 @@ export default {
const field = this.addressModel(target);
if (!field) return false;
const siteCode = String(field.model[field.siteCode] || '').trim();
- // 地址展示模式由实际选择的数据决定:有站点编码表示非公路站点名称,
- // 没有站点编码则保留省市区级联,避免切换运输方式时改写已有地址。
+ // 展示模式由已选数据决定:有站点编码则显示站点名,否则省市区;暂不按运输方式强制站点限制。
return Boolean(siteCode && siteCode !== '/');
},
+ hasAddressValue(target) {
+ const field = this.addressModel(target);
+ if (!field) return false;
+ return Boolean(
+ String(field.model[field.address] || '').trim() || String(field.model[field.name] || '').trim()
+ );
+ },
+ tipEmptyAddressDetail(target) {
+ // 替代原「请先选择运输方式」placeholder:空地址点击时 toast,已有值不再提示。
+ if (!this.hasAddressValue(target)) {
+ this.$message.warning('请输入详细地址');
+ }
+ },
resolveAddressRegionCode(row = {}) {
return row.regionCode || row.districtCode || row.areaCode || row.adcode || '';
},
openCommonAddress(target) {
if (this.isBasicRouteReadonly) return;
+ this.tipEmptyAddressDetail(target);
this.addressTarget = target;
- if (this.addressTransportMode(target) !== 'road') {
- this.stationQuery = {};
- this.stationPage.current = 1;
- this.stationDialogVisible = true;
- return;
- }
+ // 暂不按运输方式限制站点:统一走常用地址选择。
this.addressQuery = {};
this.addressPage.current = 1;
this.addressDialogVisible = true;
@@ -1279,18 +1288,11 @@ export default {
},
handleAddressDetailClick(target) {
if (this.isBasicRouteReadonly) return;
- if (this.addressTransportMode(target) !== 'road') {
- this.openCommonAddress(target);
- return;
- }
+ this.tipEmptyAddressDetail(target);
this.openAddressMap(target);
},
openAddressMap(target) {
if (this.isBasicRouteReadonly) return;
- if (this.addressTransportMode(target) !== 'road') {
- this.$message.info('非公路运输地址不能使用地图选择');
- return;
- }
const field = this.addressModel(target);
if (!field) return;
this.mapTarget = target;
@@ -1690,7 +1692,11 @@ export default {
'',
cargoTypePath,
quantity: row.quantity || '',
- quantityUnit: row.quantityUnit || this.defaultQuantityUnit(),
+ // 常用货物单位若为空或「个」,新建总单统一默认「吨」
+ quantityUnit:
+ !row.quantityUnit || String(row.quantityUnit).trim() === '个'
+ ? this.defaultQuantityUnit()
+ : row.quantityUnit,
packageType: row.packageType || row.package || '',
brand: row.brand || '',
specification: row.specification || row.spec || '',
@@ -1775,17 +1781,21 @@ export default {
return formatUpdateUserName(row);
},
selectRoute(route) {
+ // 先写入运输方式(会触发地点清空),再回填线路地址,避免被清空逻辑覆盖。
+ this.form.finalTransportType = '公路运输';
+ if (!this.form.routes.length) this.addRoute();
+ if (this.form.routes[0]) this.form.routes[0].transportType = '公路运输';
this.form.departureName = route.departureName || '';
this.form.departureAddress = route.departureAddress || '';
this.form.departureContact = route.departureContact || '';
this.form.departurePhone = route.departurePhone || '';
+ this.form.departureSiteCode = '';
this.form.arrivalName = route.arrivalName || '';
this.form.arrivalAddress = route.arrivalAddress || '';
this.form.arrivalContact = route.arrivalContact || '';
this.form.arrivalPhone = route.arrivalPhone || '';
+ this.form.arrivalSiteCode = '';
this.syncAddressRegionPaths();
- this.form.finalTransportType = '公路运输';
- if (!this.form.routes.length) this.addRoute();
this.routeDialogVisible = false;
},
createRoute(index = this.form.routes.length) {
diff --git a/src/views/business/components/waybill-manage-page.vue b/src/views/business/components/waybill-manage-page.vue
index dad89a3..a701aaf 100644
--- a/src/views/business/components/waybill-manage-page.vue
+++ b/src/views/business/components/waybill-manage-page.vue
@@ -1591,8 +1591,8 @@
取消
-
- {{ completeActionLabel }}
+
+ {{ completeActionLabelFor(row) }}
维护里程
@@ -5111,7 +5111,29 @@ export default {
return false;
}
const status = this.statusValue(row);
- return status === 'processing' || status === 'running';
+ return status === 'processing' || status === 'running' || status === 'pending_return';
+ },
+ completeActionLabelFor(row) {
+ return this.statusValue(row) === 'pending_return' ? '确认回单' : this.completeActionLabel;
+ },
+ handleCompleteAction(row) {
+ const isPendingReturn = this.statusValue(row) === 'pending_return';
+ const actionName = this.completeActionLabelFor(row);
+ this.$confirm(`确定${actionName}该${this.config.title}?`, '提示', {
+ confirmButtonText: '确定',
+ cancelButtonText: '取消',
+ type: 'warning',
+ })
+ .then(() => {
+ if (isPendingReturn && typeof this.api.confirmReturn === 'function') {
+ return this.api.confirmReturn(row.id);
+ }
+ return this.api.complete(row.id);
+ })
+ .then(() => {
+ this.$message.success(`${actionName}成功`);
+ this.onLoad(this.page, this.query);
+ });
},
canMaintainMileage(row) {
return (
@@ -6543,6 +6565,9 @@ export default {
if (status === 'completed') {
return 'primary';
}
+ if (status === 'pending_return') {
+ return 'warning';
+ }
return 'warning';
},
normalizeRow(row, saveAsDraft = false) {
@@ -10356,7 +10381,14 @@ export default {
this.$message.warning('请选择至少一条数据');
return;
}
- this.$confirm('确定完成选中的运单?', '提示', {
+ const pendingReturnCount = this.selectionList.filter(
+ row => this.statusValue(row) === 'pending_return'
+ ).length;
+ const tip =
+ pendingReturnCount > 0
+ ? `确定完成选中的运单?其中 ${pendingReturnCount} 条为待确认回单,确认后将转为已完成。`
+ : '确定完成选中的运单?';
+ this.$confirm(tip, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
@@ -10382,13 +10414,13 @@ export default {
}
const unavailableWaybills = this.selectionList.filter(
row =>
- !['processing', 'running'].includes(this.statusValue(row)) ||
+ !['pending', 'processing', 'running'].includes(this.statusValue(row)) ||
row.loadingId ||
row.loadingManageId ||
row.loadingNo
);
if (unavailableWaybills.length) {
- this.$message.warning('公路配载仅支持进行中且未配载的运单');
+ this.$message.warning('公路配载仅支持待执行/进行中且未关联有效配载单的运单');
return;
}
try {
diff --git a/src/views/business/contract-manage-change.vue b/src/views/business/contract-manage-change.vue
index 6a5b6bf..e3fef3c 100644
--- a/src/views/business/contract-manage-change.vue
+++ b/src/views/business/contract-manage-change.vue
@@ -8,11 +8,17 @@
+
-
+
@@ -80,6 +87,7 @@
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
placeholder="请输入"
+ :disabled="isSettlementRuleChange"
/>
至
@@ -95,6 +104,7 @@
positiveIntegerInput('paymentDays', value)"
>
天
@@ -105,22 +115,36 @@
:model-value="contractAmountDisplay"
placeholder="请输入"
clearable
+ :disabled="isSettlementRuleChange"
@input="handleContractAmountInput"
@blur="formatContractAmountField"
@clear="form.contractAmount = null"
/>
-
+
-
+
-
+
@@ -129,13 +153,19 @@
positiveIntegerInput('invoiceCycle', value)"
>
天
-
+
-
+
positiveIntegerInput('copyCount', value)"
>
份
-
+
-
+
@@ -214,6 +262,7 @@
title="用印材料"
description
attachment-type
+ :readonly="isSettlementRuleChange"
:attachment-location-options="changeAttachmentLocationOptions"
:rows="contractFileRows"
:preview="previewAttachment"
@@ -224,12 +273,36 @@
- 系统生成账单导入生成
- {{ row.defaultPlan ? '是' : '否' }}编辑删除
+ 系统生成账单导入生成
+ {{
+ row.defaultPlan ? '是' : '否'
+ }}编辑删除
@@ -248,19 +321,106 @@
关闭
-
-
-
-
-
-
+
+
+
+
+
+
-
+
- handleCustomPeriodStartDayChange($index, value)">
- handleCustomPeriodEndDayChange($index, value)">
- 添加删除
- 添加
+ handleCustomPeriodStartDayChange($index, value)"
+ >
+ handleCustomPeriodEndDayChange($index, value)"
+ >
+ 添加删除
+ 添加
@@ -268,17 +428,48 @@
付款比例设置
-
+
-
-
- 删除
+
+
+ 删除
- 添加
+ 添加
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 (this.isSettlementRuleChange) {
+ if (location === '变更材料') {
+ this.changeMaterials = [...(this.changeMaterials || []), ...rows];
+ }
+ return;
+ }
if (isContractSealMaterialLocation(location)) {
this.contractFileRows = [...(this.contractFileRows || []), ...rows];
return;
@@ -759,7 +963,81 @@ export default {
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; },
- 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', bizId: this.form.id, subjectName: this.form.contractName || '', approvalStatus: this.form.approvalStatus || 'change_rejected', mkEnabled: await loadMkEnabled().catch(() => false) }); this.$message.success('变更已提交'); this.$router.back(); },
+ async submit() {
+ if (this.form.changeType === '终止合同') {
+ await api.terminate(this.form.id, this.form.changeReason);
+ this.$message.success('合同已终止');
+ this.$router.back();
+ return;
+ }
+ await this.$refs.formRef.validate();
+ if (!this.isSettlementRuleChange) {
+ 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,
+ };
+ const payload = this.isSettlementRuleChange
+ ? {
+ id: this.form.id,
+ changeType: this.form.changeType,
+ changeContent: this.form.changeContent,
+ changeReason: this.form.changeReason,
+ 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),
+ changeAttachmentsJson: JSON.stringify(this.changeMaterials),
+ }
+ : {
+ ...this.form,
+ changeType: this.form.changeType || '合同信息变更',
+ 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 api.submitChange(payload);
+ await submitMkApprovalFlow({
+ bizType: 'contract-manage',
+ bizId: this.form.id,
+ subjectName: this.form.contractName || '',
+ approvalStatus: this.form.approvalStatus || 'change_rejected',
+ mkEnabled: await loadMkEnabled().catch(() => false),
+ });
+ this.$message.success('变更已提交');
+ this.$router.back();
+ },
},
};
diff --git a/src/views/business/loading-manage.vue b/src/views/business/loading-manage.vue
index aac814e..9196fe0 100644
--- a/src/views/business/loading-manage.vue
+++ b/src/views/business/loading-manage.vue
@@ -635,10 +635,11 @@
min-width="100" />{{
+ formatCargoQuantityDisplay(row)
+ }}
+
+
可配载条件
+
+ 同时满足以下条件的运单可被配载:运输方式为「公路」;运单状态为「待执行」或「进行中」;未关联其他有效配载单。
+
+
配载后状态说明
+
+ 子运单:按项目过程配置是否需司机接单,无需接单则为进行中,需接单则为待执行。配载单:按所选运单所属项目的过程配置,需接单则为待执行,无需接单则为进行中。
+
+
@@ -1357,19 +1368,29 @@
-
+
删除
+ 已打卡
+
+ 已打卡地点置灰不可拖拽,新增地址仅可在未打卡节点间调整
+
-
-
+
+ {{ formatCargoQuantityDisplay(row) }}
+
@@ -1436,7 +1458,7 @@
@@ -1450,7 +1472,11 @@
"
clearable
filterable
- :disabled="dialogReadonly || !selectedWaybillRows.length"
+ :disabled="
+ dialogReadonly ||
+ isRunningTaskRequiredLocked ||
+ !selectedWaybillRows.length
+ "
:loading="taskCarrierLoading"
:placeholder="taskCarrierPlaceholder"
@change="handleTaskCarrierChange"
@@ -1483,6 +1509,7 @@
:fetch-suggestions="fetchDriverSuggestions"
:loading="driverLoading"
placeholder="请输入"
+ :disabled="dialogReadonly || isRunningSelfDriverLocked"
@select="item => handleDriverSuggestionSelect('dialog', item)"
/>
@@ -1490,13 +1517,28 @@
label="手机号"
:required="dialogForm.carrierType === '自运'"
>
-
+
-
+
-
+
handleEscortSuggestionSelect('dialog', item)"
/>
-
+
@@ -1537,6 +1586,7 @@
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
placeholder="请选择"
+ :disabled="dialogReadonly"
/>
@@ -1547,6 +1597,7 @@
maxlength="500"
show-word-limit
placeholder="请输入,500字以内"
+ :disabled="dialogReadonly"
/>
@@ -1561,11 +1612,17 @@
关闭
-
+
暂存
- {{ dialogMode === 'reassign' ? '确认派单' : '确认' }}
+ {{ dialogMode === 'reassign' ? '确认派单' : isRunningEdit ? '保存' : '确认' }}
@@ -2107,6 +2164,8 @@ export default {
routeNodes: [],
cargoRows: [],
routeDragIndex: -1,
+ /** 进行中编辑:子运单打卡记录映射 waybillId -> records[] */
+ waybillPunchMap: {},
transportTypeOptions: [],
projectOptions: [],
customerOptions: [],
@@ -2170,6 +2229,23 @@ export default {
dialogReadonly() {
return this.dialogMode === 'view';
},
+ /** 进行中配载编辑:可加子单/调未打卡路线,但任务必填字段锁定 */
+ isRunningEdit() {
+ return (
+ this.dialogMode === 'edit' &&
+ String(this.dialogForm.businessStatus || '') === 'running'
+ );
+ },
+ /** 进行中任务必填字段(已派车)不可改 */
+ isRunningTaskRequiredLocked() {
+ return this.isRunningEdit;
+ },
+ /** 自运进行中:司机/手机号同属必填派车信息,不可改 */
+ isRunningSelfDriverLocked() {
+ return (
+ this.isRunningEdit && String(this.dialogForm.carrierType || '').trim() === '自运'
+ );
+ },
// 打卡时间轴:已打卡按时间升序,未打卡保持原节点顺序排在其后
orderedPunchRecords() {
const records = Array.isArray(this.punchRecords) ? this.punchRecords : [];
@@ -2913,6 +2989,7 @@ export default {
if (status === 'running') {
return [
{ type: 'view', label: '详情' },
+ { type: 'edit', label: '编辑' },
{ type: 'complete', label: '完成' },
];
}
@@ -3014,7 +3091,12 @@ export default {
query: {
mode,
...(mode === 'edit' && row?.id ? { id: row.id } : {}),
- name: mode === 'edit' ? '编辑配载管理' : '新增配载管理',
+ name:
+ mode === 'edit'
+ ? row?.businessStatus === 'running'
+ ? '编辑进行中配载'
+ : '编辑配载管理'
+ : '新增配载管理',
},
});
return;
@@ -3037,6 +3119,9 @@ export default {
this.dialogForm.loadingSubNos
);
this.syncProcessWaybillOptions();
+ if (this.isRunningEdit) {
+ await this.refreshWaybillPunchMap();
+ }
this.restoreRouteAndCargo();
this.restoreRouteChangeRecords();
this.loadPunchRecords();
@@ -3071,6 +3156,8 @@ export default {
this.taskCarrierLoading = false;
this.routeNodes = [];
this.cargoRows = [];
+ this.routeDragIndex = -1;
+ this.waybillPunchMap = {};
this.candidatePage.currentPage = 1;
this.candidatePage.total = 0;
this.candidateSearchVisible = false;
@@ -3435,20 +3522,144 @@ export default {
const statusName = String(row.businessStatusName || row.statusName || '');
return statusName.includes('已完成');
},
+ isPunchRecordDone(record = {}) {
+ if (record.punched === false || record.statusName === '未打卡') return false;
+ return Boolean(record.punched || record.punchTime || record.punchAt || record.punchDateTime);
+ },
+ isLoadPunchRecord(record = {}) {
+ if (!this.isPunchRecordDone(record)) return false;
+ const code = String(record.nodeCode || '')
+ .trim()
+ .toLowerCase();
+ const name = String(record.nodeName || '').trim();
+ return code === 'load' || name.includes('装货');
+ },
+ isUnloadPunchRecord(record = {}) {
+ if (!this.isPunchRecordDone(record)) return false;
+ const code = String(record.nodeCode || '')
+ .trim()
+ .toLowerCase();
+ const name = String(record.nodeName || '').trim();
+ return (
+ code === 'unload' ||
+ name.includes('卸货') ||
+ code === 'arrival' ||
+ name.includes('到货') ||
+ code === 'sign' ||
+ name.includes('签收')
+ );
+ },
+ waybillPunchRecords(row = {}) {
+ const key = String(row?.id || '');
+ if (!key) return [];
+ const records = this.waybillPunchMap[key];
+ return Array.isArray(records) ? records : [];
+ },
+ isWaybillLoadPunched(row = {}) {
+ return this.waybillPunchRecords(row).some(record => this.isLoadPunchRecord(record));
+ },
+ isWaybillUnloadPunched(row = {}) {
+ return this.waybillPunchRecords(row).some(record => this.isUnloadPunchRecord(record));
+ },
+ isWaybillPunchLocked(row = {}) {
+ return this.isWaybillLoadPunched(row) || this.isWaybillUnloadPunched(row);
+ },
+ isSelectedWaybillRemoveLocked(row = {}) {
+ if (this.isWaybillCompleted(row)) return true;
+ return this.isRunningEdit && this.isWaybillPunchLocked(row);
+ },
+ isRouteNodeTagLocked(tag, rows = this.selectedWaybillRows) {
+ const match = String(tag || '').match(/^(装|卸)(\d+)$/);
+ if (!match) return false;
+ const row = rows[Number(match[2]) - 1];
+ if (!row) return false;
+ if (this.isRunningEdit) {
+ return match[1] === '装' ? this.isWaybillLoadPunched(row) : this.isWaybillUnloadPunched(row);
+ }
+ return this.isWaybillCompleted(row);
+ },
isRouteChangeNodeLocked(node = {}) {
return Boolean(node?.locked);
},
syncRouteNodeLockedFlags(nodes = [], rows = this.selectedWaybillRows) {
return (nodes || []).map(node => {
- const locked = (node.tags || []).some(tag => {
- const match = String(tag).match(/^(装|卸)(\d+)$/);
- if (!match) return false;
- const row = rows[Number(match[2]) - 1];
- return row ? this.isWaybillCompleted(row) : false;
- });
+ const locked = (node.tags || []).some(tag => this.isRouteNodeTagLocked(tag, rows));
return { ...node, locked };
});
},
+ async refreshWaybillPunchMap() {
+ const rows = (this.selectedWaybillRows || []).filter(row => row?.id);
+ if (!rows.length) {
+ this.waybillPunchMap = {};
+ return;
+ }
+ const entries = await Promise.all(
+ rows.map(async row => {
+ try {
+ const res = await getWaybillPunchRecords(row.id);
+ const data = res?.data?.data || res?.data || {};
+ return [String(row.id), Array.isArray(data.records) ? data.records : []];
+ } catch (error) {
+ return [String(row.id), []];
+ }
+ })
+ );
+ this.waybillPunchMap = Object.fromEntries(entries);
+ },
+ /**
+ * 进行中编辑:已打卡地点保持在前且不可插入,新增地址只能排在已打卡节点之后。
+ */
+ mergeRouteNodesKeepingPunched(prevNodes = [], builtNodes = []) {
+ const prev = Array.isArray(prevNodes) ? prevNodes : [];
+ const built = Array.isArray(builtNodes) ? builtNodes : [];
+ if (!this.isRunningEdit) {
+ return built;
+ }
+ const builtByAddress = new Map();
+ built.forEach(node => {
+ if (node?.address && !builtByAddress.has(node.address)) {
+ builtByAddress.set(node.address, node);
+ }
+ });
+ const lockedAddresses = new Set(
+ built.filter(node => node?.locked && node.address).map(node => node.address)
+ );
+ const used = new Set();
+ const lockedResult = [];
+ prev.forEach(node => {
+ if (!node?.address || !lockedAddresses.has(node.address) || used.has(node.address)) {
+ return;
+ }
+ const latest = builtByAddress.get(node.address);
+ if (!latest) return;
+ lockedResult.push({ ...latest, locked: true });
+ used.add(node.address);
+ });
+ built.forEach(node => {
+ if (!node?.locked || !node.address || used.has(node.address)) return;
+ lockedResult.push({ ...node, locked: true });
+ used.add(node.address);
+ });
+ const unlockedResult = [];
+ prev.forEach(node => {
+ if (!node?.address || lockedAddresses.has(node.address) || used.has(node.address)) {
+ return;
+ }
+ const latest = builtByAddress.get(node.address);
+ if (!latest) return;
+ unlockedResult.push({ ...latest, locked: false });
+ used.add(node.address);
+ });
+ built.forEach(node => {
+ if (node?.locked || !node?.address || used.has(node.address)) return;
+ unlockedResult.push({ ...node, locked: false });
+ used.add(node.address);
+ });
+ return [...lockedResult, ...unlockedResult].map((node, index) => ({
+ ...node,
+ key: `${node.address}-${index}`,
+ }));
+ },
openRouteChangeMapDialog(row, field) {
if (this.isWaybillCompleted(row)) return;
this.routeChangeMapTarget = { row, field };
@@ -3701,12 +3912,10 @@ export default {
try {
const query = {
...this.buildQueryParams(this.candidateQuery),
- // 待执行(需接单未接)与进行中(无需接单或已接单)均可配载
- businessStatus: 'pending,running',
- onlyUnassignedLoading: 1,
+ // 与后端 LoadableAdmissionSupport 一致:公路 + 待执行/进行中 + 未关联有效配载单
+ loadableOnly: 1,
// 仅查当前账户所属组织及子组织下的运单
allDept: 0,
- ...(this.dialogMode === 'add' ? { transportType: 'road' } : {}),
};
const transportType = this.getCandidateTransportTypeQuery(query.transportType);
const res = await getWaybillList(
@@ -3766,6 +3975,33 @@ export default {
}
})
);
+ if (this.isRunningEdit && String(this.dialogForm.carrierType || '').trim() === '承运商') {
+ const contractId = this.dialogForm.carrierContractId;
+ if (contractId) {
+ const projectIds = [
+ ...new Set(
+ [...this.selectedWaybillRows, ...selectedRows]
+ .map(row => row.projectId)
+ .filter(Boolean)
+ ),
+ ];
+ if (projectIds.length) {
+ try {
+ const response = await loadingApi.getCarrierContracts(projectIds);
+ const contracts = response?.data?.data || response?.data || [];
+ const stillAllowed = (Array.isArray(contracts) ? contracts : []).some(
+ item => String(item.id) === String(contractId)
+ );
+ if (!stillAllowed) {
+ ElMessage.warning('新增子运单须兼容当前配载单已选定的承运商合同');
+ return;
+ }
+ } catch (error) {
+ // 合同校验失败时仍允许添加,最终由后端确认拦截
+ }
+ }
+ }
+ }
const exists = new Set(
this.selectedWaybillRows.map(row => String(row.id || row.waybillNo))
);
@@ -3777,8 +4013,12 @@ export default {
exists.add(key);
}
});
+ const prevRouteNodes = [...this.routeNodes];
this.selectedWaybillRows = nextRows;
- this.rebuildSummaryFromWaybills();
+ if (this.isRunningEdit) {
+ await this.refreshWaybillPunchMap();
+ }
+ this.rebuildSummaryFromWaybills(true, prevRouteNodes);
await this.loadTaskCarrierOptions();
this.$refs.candidateTableRef?.clearSelection?.();
this.candidateSelection = [];
@@ -3790,11 +4030,23 @@ export default {
await this.addWaybillRows(this.candidateSelection);
},
removeSelectedWaybill(index) {
+ const row = this.selectedWaybillRows[index];
+ if (this.isSelectedWaybillRemoveLocked(row)) {
+ ElMessage.warning('已打卡的子运单不可移除');
+ return;
+ }
this.selectedWaybillRows.splice(index, 1);
+ if (this.isRunningEdit) {
+ this.refreshWaybillPunchMap().then(() => {
+ this.rebuildSummaryFromWaybills();
+ this.loadTaskCarrierOptions();
+ });
+ return;
+ }
this.rebuildSummaryFromWaybills();
this.loadTaskCarrierOptions();
},
- rebuildSummaryFromWaybills(rebuildRoute = true) {
+ rebuildSummaryFromWaybills(rebuildRoute = true, prevRouteNodes = null) {
const rows = this.selectedWaybillRows;
this.dialogForm.loadingSubNos = rows
.map(row => row.waybillNo)
@@ -3819,7 +4071,13 @@ export default {
this.dialogForm.transportType = rows.find(row => row.transportType)?.transportType || '';
}
if (rebuildRoute) {
- this.routeNodes = this.buildRouteNodes(rows);
+ const builtNodes = this.buildRouteNodes(rows);
+ this.routeNodes = this.isRunningEdit
+ ? this.mergeRouteNodesKeepingPunched(
+ prevRouteNodes || this.routeNodes,
+ builtNodes
+ )
+ : builtNodes;
}
this.cargoRows = this.buildCargoRows(rows);
// 编辑回填阶段 routeNodes 尚未恢复时跳过,避免清空已有发到货地
@@ -3839,7 +4097,9 @@ export default {
if (locked) node.locked = true;
};
rows.forEach((row, rowIndex) => {
- const locked = this.isWaybillCompleted(row);
+ const locked = this.isRunningEdit
+ ? this.isWaybillLoadPunched(row)
+ : this.isWaybillCompleted(row);
const departure = row.departureAddress || row.departureName;
appendNode(
departure,
@@ -3849,7 +4109,9 @@ export default {
);
});
rows.forEach((row, rowIndex) => {
- const locked = this.isWaybillCompleted(row);
+ const locked = this.isRunningEdit
+ ? this.isWaybillUnloadPunched(row)
+ : this.isWaybillCompleted(row);
const arrival = row.arrivalAddress || row.arrivalName;
appendNode(
arrival,
@@ -3901,7 +4163,9 @@ export default {
if (
originalLockedKeys.some((key, lockedIndex) => key && key !== nextLockedKeys[lockedIndex])
) {
- ElMessage.warning('已完成运单对应的路线不可调整');
+ ElMessage.warning(
+ this.isRunningEdit ? '已打卡地点不可调整,新地址只能在未打卡节点间拖拽' : '已完成运单对应的路线不可调整'
+ );
this.routeDragIndex = -1;
return;
}
@@ -3941,14 +4205,28 @@ export default {
return [this.normalizeCargoRow(row, row)];
});
},
+ formatCargoQuantityDisplay(row = {}) {
+ const quantity = blankCargoSentinel(row.quantity ?? row.cargoQuantity ?? '');
+ const unit = String(
+ blankCargoSentinel(
+ row.quantityUnit ||
+ row.goodsQuantityUnit ||
+ row.cargoUnit ||
+ row.unit ||
+ ''
+ ) || ''
+ ).trim();
+ if (quantity === '' || quantity === undefined || quantity === null) {
+ return unit || '';
+ }
+ return unit ? `${quantity} ${unit}` : String(quantity);
+ },
normalizeCargoRow(waybill, item = {}) {
return sanitizeCargoRow({
projectName: item.projectName || waybill.projectName || '',
cargoName: item.cargoName || item.goodsName || waybill.cargoName || '',
cargoType: item.cargoType || item.goodsType || waybill.cargoType || '',
packageType: item.packageType || item.package || '',
- weight: item.weight || item.cargoWeight || '',
- volume: item.volume || item.cargoVolume || '',
quantity: item.quantity || item.cargoQuantity || waybill.quantity || '',
quantityUnit:
item.quantityUnit ||
@@ -4145,15 +4423,15 @@ export default {
try {
const payload = {
...this.normalizePayload(),
- // 确认生成正式配载单(待执行),覆盖草稿状态
- businessStatus: 'pending',
+ // 进行中编辑保持 running;新建/草稿确认由后端按项目是否需接单写入 pending/running
+ ...(this.isRunningEdit ? { businessStatus: 'running' } : {}),
};
if (this.dialogMode === 'reassign') {
await loadingApi.reassign(payload);
ElMessage.success('派单成功');
} else {
await loadingApi.submit(payload);
- ElMessage.success('确认成功');
+ ElMessage.success(this.isRunningEdit ? '保存成功' : '确认成功');
}
sessionStorage.setItem(loadingManageListRefreshKey, '1');
this.closeLoadingDialog();
@@ -4715,6 +4993,30 @@ export default {
justify-content: space-between;
}
+ .loading-manage-dialog__admission-tip {
+ margin-bottom: 12px;
+ padding: 12px 14px;
+ background: #f5f9ff;
+ border: 1px solid #d9ecff;
+ border-radius: 4px;
+ line-height: 1.6;
+ color: #606266;
+ }
+
+ .loading-manage-dialog__admission-title {
+ margin-bottom: 4px;
+ color: #303133;
+ font-weight: 600;
+ }
+
+ .loading-manage-dialog__admission-title + .loading-manage-dialog__admission-text + .loading-manage-dialog__admission-title {
+ margin-top: 8px;
+ }
+
+ .loading-manage-dialog__admission-text {
+ font-size: 13px;
+ }
+
.loading-manage-dialog__candidate-search {
padding: 0;
margin-bottom: 0;
@@ -4787,6 +5089,16 @@ export default {
color: #909399;
}
+ .loading-manage-dialog__route-hint {
+ color: #909399;
+ font-size: 12px;
+ }
+
+ .loading-manage-dialog__locked-tip {
+ color: #909399;
+ font-size: 12px;
+ }
+
.loading-manage-dialog__route-type {
width: 28px;
height: 28px;
diff --git a/src/views/business/project-apply.vue b/src/views/business/project-apply.vue
index 1b0805d..965f9a0 100644
--- a/src/views/business/project-apply.vue
+++ b/src/views/business/project-apply.vue
@@ -955,7 +955,7 @@ import {
getList as getCustomerArchiveList,
getDetail as getCustomerArchiveDetail,
} from '@/api/vehicle/customer-archive';
-import { getDeptTree, getPlatformCompanySelect } from '@/api/system/dept';
+import { getBusinessDeptSelect, getDeptTree, getPlatformCompanySelect } from '@/api/system/dept';
import { getDictionary as getSystemDictionary } from '@/api/system/dict';
import { getDictionary as getBizDictionary } from '@/api/system/dictbiz';
import { getList as getUserList } from '@/api/system/user';
@@ -1596,6 +1596,7 @@ export default {
projectType: type === 'majorSupplement' ? '重大项目' : '普通项目',
});
this.fillDefaultUsers();
+ this.fillDefaultBusinessDept();
return;
}
if (!id) return;
@@ -2032,6 +2033,7 @@ export default {
.filter(Boolean);
this.syncSelectedCustomerOptions();
this.ensureCurrentPlatformOption();
+ this.ensureCurrentBusinessDeptOption();
},
fillDefaultUsers() {
const userId = this.userInfo?.userId || this.userInfo?.id || '';
@@ -2050,6 +2052,25 @@ export default {
this.form.principalUserName = userName;
}
},
+ /**
+ * 新增/重大补录:业务部门默认取当前用户所属部门中第一个「是否业务部门=是」的组织。
+ */
+ fillDefaultBusinessDept() {
+ if (!['add', 'majorSupplement'].includes(this.dialogType)) return;
+ if (this.form.businessDeptId) return;
+ const raw = this.userInfo?.deptId || this.userInfo?.dept_id || '';
+ const userDeptIds = String(raw)
+ .split(/[,,]/)
+ .map(item => String(item || '').trim())
+ .filter(item => item && item !== '-1');
+ if (!userDeptIds.length || !this.deptOptions.length) return;
+ const matched = userDeptIds.find(id =>
+ this.deptOptions.some(item => String(item.value) === String(id))
+ );
+ if (!matched) return;
+ this.form.businessDeptId = matched;
+ this.handleBusinessDeptChange(matched);
+ },
resolveUserRealName(row = {}, role) {
return (
row[`${role}RealName`] ||
@@ -2632,17 +2653,61 @@ export default {
this.$refs.projectForm?.validateField('projectType');
},
loadDeptOptions() {
- getDeptTree().then(res => {
- const deptTree = res.data.data || [];
- // 新增/编辑/重大补录/项目变更均可选择业务部门,排除外部组织
- const businessDeptTree = ['add', 'edit', 'majorSupplement', 'change'].includes(this.dialogType)
- ? this.excludeExternalOrganization(deptTree)
- : deptTree;
- this.deptOptions = this.flattenDept(businessDeptTree);
- this.businessDeptTreeOptions = this.toDeptCascaderOptions(businessDeptTree);
- });
+ Promise.all([getDeptTree(), getBusinessDeptSelect()])
+ .then(([treeRes, bizRes]) => {
+ const deptTree = this.excludeExternalOrganization(treeRes.data.data || []);
+ const bizList = bizRes.data.data || [];
+ const bizIds = new Set(
+ bizList
+ .map(item => (item.id == null || item.id === '' ? '' : String(item.id)))
+ .filter(Boolean)
+ );
+ // 仅保留「是否业务部门=是」的节点及其祖先,祖先不可选
+ const businessDeptTree = this.filterBusinessDeptTree(deptTree, bizIds);
+ this.deptOptions = this.flattenDept(businessDeptTree).filter(item =>
+ bizIds.has(String(item.value))
+ );
+ this.businessDeptTreeOptions = this.toDeptCascaderOptions(businessDeptTree, bizIds);
+ this.fillDefaultBusinessDept();
+ this.ensureCurrentBusinessDeptOption();
+ })
+ .catch(() => {
+ this.deptOptions = [];
+ this.businessDeptTreeOptions = [];
+ });
this.loadPlatformCompanyOptions();
},
+ /**
+ * 剪枝组织树:只保留业务部门节点及其祖先路径。
+ */
+ filterBusinessDeptTree(list = [], bizIds = new Set()) {
+ return (list || []).reduce((result, item) => {
+ const children = item.children?.length
+ ? this.filterBusinessDeptTree(item.children, bizIds)
+ : [];
+ const isBiz = bizIds.has(String(item.id));
+ if (!isBiz && !children.length) {
+ return result;
+ }
+ result.push({
+ ...item,
+ children: children.length ? children : undefined,
+ });
+ return result;
+ }, []);
+ },
+ ensureCurrentBusinessDeptOption() {
+ const id = this.form.businessDeptId;
+ if (!id) return;
+ const exists = this.deptOptions.some(item => String(item.value) === String(id));
+ if (exists) return;
+ const label = this.form.businessDeptName || String(id);
+ this.deptOptions.unshift({ label, rawLabel: label, value: id });
+ const pathExists = this.findDeptPath(id, this.businessDeptTreeOptions);
+ if (!pathExists) {
+ this.businessDeptTreeOptions.unshift({ label, value: id });
+ }
+ },
loadPlatformCompanyOptions() {
getPlatformCompanySelect().then(res => {
const list = res.data.data || [];
@@ -2686,12 +2751,22 @@ export default {
return result;
}, []);
},
- toDeptCascaderOptions(list = []) {
- return list.map(item => ({
- label: item.title || item.deptName || item.name,
- value: item.id,
- children: item.children?.length ? this.toDeptCascaderOptions(item.children) : undefined,
- }));
+ toDeptCascaderOptions(list = [], bizIds = null) {
+ return list.map(item => {
+ const value = item.id == null || item.id === '' ? item.value : item.id;
+ const option = {
+ label: item.title || item.deptName || item.name || item.label,
+ value: value == null || value === '' ? '' : String(value),
+ children: item.children?.length
+ ? this.toDeptCascaderOptions(item.children, bizIds)
+ : undefined,
+ };
+ // 仅业务部门可选;祖先节点保留路径但禁用
+ if (bizIds && !bizIds.has(String(option.value))) {
+ option.disabled = true;
+ }
+ return option;
+ });
},
flattenDept(list = [], level = 0) {
return list.reduce((result, item) => {
diff --git a/src/views/business/voucher-manage.vue b/src/views/business/voucher-manage.vue
index 79645e0..54fb8b2 100644
--- a/src/views/business/voucher-manage.vue
+++ b/src/views/business/voucher-manage.vue
@@ -203,7 +203,11 @@
@click="openBatchDialog(row)"
>更换运单批次更换运单批次上传请上传车辆的执行凭证压缩包,支持zip、7z格式。 上传附件大于50M时为超大附件,确认提交后可在“批量补录->上传任务”中查看上传进度。请上传车辆的执行凭证压缩包,仅支持 zip 格式;无法解析的文件将标记为「处理失败」,可重新上传。上传附件大于50M时为超大附件,确认提交后可在“批量补录->上传任务”中查看上传进度。
@@ -998,6 +1002,12 @@ const uploadVoucher = async file => {
};
const selectVoucherFile = file => {
if (!file?.raw) return;
+ const fileName = String(file.raw.name || '').toLowerCase();
+ if (!fileName.endsWith('.zip')) {
+ ElMessage.warning('仅支持 zip 压缩包,无法解析的文件将标记为处理失败');
+ clearFile();
+ return;
+ }
selectedVoucherFile.value = file.raw;
editing.fileName = file.raw.name;
editing.fileUrl = '';
diff --git a/src/views/payment/bill-payment-form.vue b/src/views/payment/bill-payment-form.vue
index 77297ed..d848ffd 100644
--- a/src/views/payment/bill-payment-form.vue
+++ b/src/views/payment/bill-payment-form.vue
@@ -271,7 +271,7 @@ export default {
this.unwrapData(
await billLedgerApi.getAvailableOptions(
this.billDialog.keyword,
- this.form.deptId,
+ undefined,
this.form.billLedgerId
)
) || [];
diff --git a/src/views/payment/payment-application-form.vue b/src/views/payment/payment-application-form.vue
index 836f9fd..3e91449 100644
--- a/src/views/payment/payment-application-form.vue
+++ b/src/views/payment/payment-application-form.vue
@@ -249,7 +249,7 @@
1;
- },
// 结算单号展示:单张显示单号;多张仅提示数量,明细在下方「结算信息」表,悬停可看全部单号
referenceDisplayText() {
if (this.referenceSettlementCount > 1) return `已选 ${this.referenceSettlementCount} 张结算单`;
return this.referenceLabel;
},
settlementRows() {
- if (this.referenceRows.length > 1) {
- // 多结算单:行内比例/金额独立维护,不回落到表头比例(表头与结算信息不关联)
- return this.referenceRows.map(item => {
- const paymentRatio =
- item.paymentRatio === undefined || item.paymentRatio === null || item.paymentRatio === ''
- ? 0
- : Number(item.paymentRatio);
- const appliedAmount = Number(item.appliedAmount || 0);
- return {
- ...item,
- paymentRatio,
- appliedAmount,
- ...this.buildSettlementRatioHint({ ...item, paymentRatio }),
- };
- });
- }
- if (this.referenceRows.length) {
- return this.referenceRows.map(item => {
- const appliedAmount =
- item.fixedAppliedAmount === true
- ? Number(item.appliedAmount || 0)
- : Number(
- (
- (Number(item.settlementAmount || 0) * Number(this.form.paymentRatio || 0)) /
- 100
- ).toFixed(2)
- );
- return {
- ...item,
- paymentRatio: this.form.paymentRatio,
- appliedAmount,
- ...this.buildSettlementRatioHint(item),
- };
- });
- }
- if (!this.referenceLabel) return [];
- return [
- {
- settlementNo: this.referenceLabel,
- settlementAmount: this.form.settlementAmount,
- payableAmount: this.form.payableAmount,
- paymentRatio: this.form.paymentRatio,
- appliedAmount: this.form.appliedAmount,
- paymentSequence: 1,
- ...this.buildSettlementRatioHint({ paymentSequence: 1 }),
- },
- ];
+ const source = this.referenceRows.length
+ ? this.referenceRows
+ : this.referenceLabel
+ ? [
+ {
+ settlementNo: this.referenceLabel,
+ settlementAmount: this.form.settlementAmount,
+ payableAmount: this.form.payableAmount,
+ paymentRatio: null,
+ appliedAmount: null,
+ paymentSequence: 1,
+ },
+ ]
+ : [];
+ // 结算行比例/金额只取行内数据,不回落到付款基本信息
+ return source.map(item => {
+ const paymentRatio = this.nullableNumber(item.paymentRatio);
+ const appliedAmount = this.nullableNumber(item.appliedAmount);
+ return {
+ ...item,
+ paymentRatio,
+ appliedAmount,
+ ...this.buildSettlementRatioHint({ ...item, paymentRatio }),
+ };
+ });
},
highRiskPayee() {
// 收款方 C101-1 高风险/黑名单,或客商等级 D/E/F(本地评级口径),命中须上传特批附件
@@ -1149,11 +1124,12 @@ export default {
if (
this.amountSyncing ||
this.form.paymentType === 'project_advance' ||
- !this.paymentAmountBase
+ !this.paymentAmountBase ||
+ this.nullableNumber(this.form.paymentRatio) === null
) {
return;
}
- // 结算金额变化时按上方比例重算申请付款金额,仍不联动结算信息明细
+ // 结算金额变化时只按上方比例重算申请付款金额,不改结算信息明细
this.amountSyncing = true;
this.form.appliedAmount = Number(
((this.paymentAmountBase * Number(this.form.paymentRatio || 0)) / 100).toFixed(2)
@@ -1251,14 +1227,6 @@ export default {
callback();
return;
}
- if (
- selected.billType &&
- selected.billType !== 'received' &&
- !this.billMatchesSettlementParties(selected)
- ) {
- callback(new Error('汇票付款仅可选择收票类型,或出票单位与收票单位均为当前结算收付款方的汇票'));
- return;
- }
if (selected.maturityDate) {
const maturity = this.$dayjs(selected.maturityDate).startOf('day');
const today = this.$dayjs().startOf('day');
@@ -1562,8 +1530,6 @@ export default {
paymentSequence: amount.paymentSequence,
}),
settlementNo: item.formalSettlementNo || item.settlementNo || '',
- appliedAmount: Number(item.appliedAmount || 0),
- fixedAppliedAmount: true,
};
});
this.seedMultipleRowPayments(referenceRows);
@@ -1575,27 +1541,6 @@ export default {
(total, item) => total + Number(item.payableAmount || 0),
0
);
- const multiple = referenceRows.length > 1;
- // 多结算单:表头比例/申请金额独立;单结算单:沿用行金额或按比例计算
- const paymentRatio = Number(this.form.paymentRatio || 0);
- const rowsAppliedTotal = referenceRows.reduce(
- (total, item) => total + Number(item.appliedAmount || 0),
- 0
- );
- const headerAppliedAmount = multiple
- ? Number(((settlementAmount * paymentRatio) / 100).toFixed(2))
- : Number(
- (
- rowsAppliedTotal > 0
- ? rowsAppliedTotal
- : (payableAmount * paymentRatio) / 100
- ).toFixed(2)
- );
- const headerPaymentRatio = multiple
- ? paymentRatio
- : settlementAmount
- ? Number(((headerAppliedAmount / settlementAmount) * 100).toFixed(2))
- : paymentRatio;
const settlementIds = rows.map(item => item.settlementId || item.id).filter(Boolean);
this.amountSyncing = true;
this.referenceRows = referenceRows;
@@ -1622,8 +1567,7 @@ export default {
settlementAmount: Number(settlementAmount.toFixed(2)),
payableAmount: Number(payableAmount.toFixed(2)),
billType: '',
- paymentRatio: headerPaymentRatio,
- appliedAmount: headerAppliedAmount,
+ appliedAmount: this.headerAppliedAmountByRatio(settlementAmount),
invoices: rows.flatMap(item =>
this.createInvoiceRows(item.invoices || [], item.formalSettlementNo || item.settlementNo)
),
@@ -1679,7 +1623,7 @@ export default {
referenceType: isPre ? 'pre' : 'formal',
settlementAmount: Number(Number(item.settlementAmount || 0).toFixed(2)),
payableAmount: Number(Number(item.payableAmount ?? item.settlementAmount ?? 0).toFixed(2)),
- appliedAmount: Number(Number(item.appliedAmount || 0).toFixed(2)),
+ appliedAmount: this.nullableNumber(item.appliedAmount),
paymentRatio: Number(item.settlementAmount || 0)
? Number(
(
@@ -1688,11 +1632,10 @@ export default {
).toFixed(2)
)
: null,
- fixedAppliedAmount: this.readonly,
};
})
.filter(Boolean);
- if (rows.length > 1) {
+ if (rows.length) {
this.form.preSettlementIds = rows.filter(item => item.referenceType === 'pre').map(item => item.id);
this.form.settlementIds = rows.filter(item => item.referenceType === 'formal').map(item => item.id);
if (this.form.preSettlementIds.length) this.form.preSettlementId = this.form.preSettlementIds[0];
@@ -1726,18 +1669,27 @@ export default {
settlementAmount: Number(Number(settlementAmount || 0).toFixed(2)),
payableAmount: Number(Math.max(0, payableAmount).toFixed(2)),
paymentSequence: Number.isFinite(paymentSequence) && paymentSequence > 0 ? paymentSequence : 1,
+ paymentRatio: null,
+ appliedAmount: null,
};
},
+ nullableNumber(value) {
+ if (value === undefined || value === null || value === '') return null;
+ const amount = Number(value);
+ return Number.isFinite(amount) ? amount : null;
+ },
+ /** 付款基本信息内部:已填写比例时,按结算金额重算申请付款金额;不读取结算行付款金额。 */
+ headerAppliedAmountByRatio(settlementAmount) {
+ if (this.nullableNumber(this.form.paymentRatio) === null) return this.form.appliedAmount;
+ return Number(((Number(settlementAmount || 0) * Number(this.form.paymentRatio)) / 100).toFixed(2));
+ },
/**
* 按合同付款比例配置,判断当前付款次序是否超上限。
* @param {Object} row 结算信息行(含 paymentSequence)
*/
buildSettlementRatioHint(row = {}) {
const sequence = Number(row.paymentSequence || 1);
- const rawRatio =
- row.paymentRatio !== undefined && row.paymentRatio !== null && row.paymentRatio !== ''
- ? row.paymentRatio
- : this.form.paymentRatio;
+ const rawRatio = row.paymentRatio;
const paymentRatio = Number(rawRatio);
const ratioConfig = this.contractPaymentRatioRows[sequence - 1];
const ratioLimitValue = ratioConfig?.ratioLimit;
@@ -1880,21 +1832,6 @@ export default {
(total, item) => total + Number(item.payableAmount || 0),
0
);
- const multiple = this.referenceRows.length > 1;
- const paymentRatio = Number(this.form.paymentRatio || 0);
- const rowsAppliedTotal = this.referenceRows.reduce(
- (total, item) => total + Number(item.appliedAmount || 0),
- 0
- );
- // 多结算单:表头申请金额只由上方比例计算;单结算单仍按可付款金额×比例
- const headerAppliedAmount = multiple
- ? Number(((settlementAmount * paymentRatio) / 100).toFixed(2))
- : Number(((payableAmount * paymentRatio) / 100).toFixed(2));
- const headerPaymentRatio = multiple
- ? paymentRatio
- : settlementAmount && rowsAppliedTotal > 0
- ? Number(((rowsAppliedTotal / settlementAmount) * 100).toFixed(2))
- : paymentRatio;
// 尾款付款场景下选择预付结算单时保留尾款付款类型,不强制改为进度预付
this.amountSyncing = true;
Object.assign(this.form, {
@@ -1920,8 +1857,7 @@ export default {
settlementAmount: Number(settlementAmount.toFixed(2)),
payableAmount: Number(payableAmount.toFixed(2)),
billType: '',
- paymentRatio: headerPaymentRatio,
- appliedAmount: headerAppliedAmount,
+ appliedAmount: this.headerAppliedAmountByRatio(settlementAmount),
});
this.rememberSettlementParties(first.payerName, first.payeeName);
this.paymentRecords = [];
@@ -2483,8 +2419,7 @@ export default {
}
},
payeeReceiptLabel(item) {
- const name = item.accountName || item.accountHolderName || '未命名收款方';
- return item.bankAccount ? `${name}(${item.bankAccount})` : name;
+ return item.accountName || item.accountHolderName || '未命名收款方';
},
receiptAccountLabel(item) {
return [item.accountName || item.accountHolderName, item.bankName, item.bankAccount]
@@ -2642,7 +2577,7 @@ export default {
this.unwrapData(
await billLedgerApi.getAvailableOptions(
keyword,
- this.form.deptId,
+ undefined,
selectedId,
this.settlementPayerName,
this.settlementPayeeName
@@ -2667,7 +2602,7 @@ export default {
this.billPage.current,
this.billPage.size,
String(this.billQuery.keyword || '').trim() || undefined,
- this.form.deptId,
+ undefined,
this.form.billLedgerId,
this.settlementPayerName,
this.settlementPayeeName
@@ -2706,14 +2641,6 @@ export default {
);
},
selectBill(row) {
- if (
- row?.billType &&
- row.billType !== 'received' &&
- !this.billMatchesSettlementParties(row)
- ) {
- ElMessage.warning('汇票付款仅可选择收票类型,或出票单位与收票单位均为当前结算收付款方的汇票');
- return;
- }
if (row?.maturityDate) {
const maturity = this.$dayjs(row.maturityDate).startOf('day');
const today = this.$dayjs().startOf('day');
@@ -3136,22 +3063,54 @@ export default {
return Number(Math.min(100, (payableAmount / settlementAmount) * 100).toFixed(2));
},
/**
- * 多结算单行金额规范化:仅在行内反算比例,不使用表头比例灌入(表头与结算信息不关联)。
+ * 结算行金额规范化:只在行内反算比例,不使用付款基本信息的比例和申请金额。
*/
seedMultipleRowPayments(rows = []) {
- if (rows.length <= 1) return;
rows.forEach(item => {
- const settlementAmount = Number(item.settlementAmount || 0);
- const payableAmount = Number(item.payableAmount || 0);
- let applied = Number(item.appliedAmount || 0);
- if (!Number.isFinite(applied) || applied < 0) applied = 0;
- if (applied > payableAmount) applied = Number(payableAmount.toFixed(2));
- item.appliedAmount = Number(applied.toFixed(2));
- item.paymentRatio = settlementAmount
- ? Number(((item.appliedAmount / settlementAmount) * 100).toFixed(2))
- : 0;
+ if (this.nullableNumber(item.appliedAmount) === null && this.nullableNumber(item.paymentRatio) === null) {
+ item.appliedAmount = null;
+ item.paymentRatio = null;
+ return;
+ }
+ this.syncRowPayment(item, 'amount');
});
},
+ /**
+ * 结算行付款比例与付款金额联动。比例 = 付款金额 / 结算总金额。
+ * @param {'ratio'|'amount'} source 以哪一侧为准
+ */
+ syncRowPayment(row, source) {
+ const settlementAmount = Number(row.settlementAmount || 0);
+ const payableAmount = Number(row.payableAmount || 0);
+ if (source === 'ratio') {
+ const ratio = this.nullableNumber(row.paymentRatio);
+ if (ratio === null) {
+ row.paymentRatio = null;
+ row.appliedAmount = null;
+ return;
+ }
+ let applied = Number(((settlementAmount * ratio) / 100).toFixed(2));
+ if (applied > payableAmount) applied = Number(payableAmount.toFixed(2));
+ row.appliedAmount = applied;
+ row.paymentRatio = settlementAmount
+ ? Number(((applied / settlementAmount) * 100).toFixed(2))
+ : Number(ratio.toFixed(2));
+ return;
+ }
+ const appliedValue = this.nullableNumber(row.appliedAmount);
+ if (appliedValue === null) {
+ row.appliedAmount = null;
+ row.paymentRatio = null;
+ return;
+ }
+ let applied = appliedValue;
+ if (applied < 0) applied = 0;
+ if (applied > payableAmount) applied = payableAmount;
+ row.appliedAmount = Number(applied.toFixed(2));
+ row.paymentRatio = settlementAmount
+ ? Number(((row.appliedAmount / settlementAmount) * 100).toFixed(2))
+ : 0;
+ },
findReferenceRow(row) {
return this.referenceRows.find(
item =>
@@ -3160,55 +3119,19 @@ export default {
item.settlementNo === row.settlementNo
);
},
- /** 结算信息付款金额合计 */
- settlementRowsAppliedTotal(rows = this.referenceRows) {
- return (rows || []).reduce((total, item) => total + Number(item.appliedAmount || 0), 0);
- },
- /** 结算信息合计不得超过上方申请付款金额 */
- ensureSettlementAppliedWithinHeader(showWarning = true) {
- if (!this.multipleSettlementRows) return true;
- const headerApplied = Number(this.form.appliedAmount || 0);
- const rowsTotal = Number(this.settlementRowsAppliedTotal().toFixed(2));
- if (rowsTotal - headerApplied > 1e-8) {
- if (showWarning) {
- this.$message.warning(
- `结算信息付款金额合计(${rowsTotal.toFixed(2)})不能大于申请付款金额(${headerApplied.toFixed(2)})`
- );
- }
- return false;
- }
- return true;
- },
handleRowPaymentRatioChange(row, value) {
const target = this.findReferenceRow(row);
if (!target) return;
- const settlementAmount = Number(target.settlementAmount || 0);
- const payableAmount = Number(target.payableAmount || 0);
- const ratio = value === null || value === undefined || value === '' ? 0 : Number(value);
- let applied = Number(((settlementAmount * ratio) / 100).toFixed(2));
- if (applied > payableAmount) applied = Number(payableAmount.toFixed(2));
- target.appliedAmount = applied;
- target.paymentRatio = settlementAmount
- ? Number(((applied / settlementAmount) * 100).toFixed(2))
- : Number(ratio.toFixed(2));
- // 仅刷新结算金额/可付款金额合计,不回写表头申请金额与比例
+ target.paymentRatio = value;
+ this.syncRowPayment(target, 'ratio');
this.syncHeaderSettlementTotalsFromRows();
- this.ensureSettlementAppliedWithinHeader(true);
},
handleRowAppliedAmountChange(row, value) {
const target = this.findReferenceRow(row);
if (!target) return;
- const settlementAmount = Number(target.settlementAmount || 0);
- const payableAmount = Number(target.payableAmount || 0);
- let applied = value === null || value === undefined || value === '' ? 0 : Number(value);
- if (!Number.isFinite(applied) || applied < 0) applied = 0;
- if (applied > payableAmount) applied = payableAmount;
- target.appliedAmount = Number(applied.toFixed(2));
- target.paymentRatio = settlementAmount
- ? Number(((target.appliedAmount / settlementAmount) * 100).toFixed(2))
- : 0;
+ target.appliedAmount = value;
+ this.syncRowPayment(target, 'amount');
this.syncHeaderSettlementTotalsFromRows();
- this.ensureSettlementAppliedWithinHeader(true);
},
/** 多结算单时仅同步结算金额/可付款金额合计,申请付款金额与比例由上方独立维护 */
syncHeaderSettlementTotalsFromRows() {
@@ -3317,8 +3240,8 @@ export default {
settlementLines: (this.referenceRows || []).map(item => ({
formalSettlementId: item.referenceType === 'formal' ? item.id : null,
preSettlementId: item.referenceType === 'pre' ? item.id : null,
- paymentRatio: item.paymentRatio,
- appliedAmount: item.appliedAmount,
+ paymentRatio: this.nullableNumber(item.paymentRatio),
+ appliedAmount: this.nullableNumber(item.appliedAmount),
})),
paymentMethod,
billLedgerId,
@@ -3379,21 +3302,22 @@ export default {
ElMessage.warning('申请付款金额必填且必须大于0');
return false;
}
- if (this.multipleSettlementRows) {
- const invalidRow = this.referenceRows.find(
- item => Number(item.appliedAmount || 0) > Number(item.payableAmount || 0)
- );
+ if (this.isSettlementRelatedType && this.referenceRows.length) {
+ const invalidRow = this.referenceRows.find(item => {
+ const rowApplied = Number(item.appliedAmount || 0);
+ return !Number.isFinite(rowApplied) || rowApplied <= 0;
+ });
if (invalidRow) {
this.clearMessages();
- ElMessage.warning(`${invalidRow.settlementNo || '结算单'}付款金额不能超过可付款金额`);
+ ElMessage.warning(`${invalidRow.settlementNo || '结算单'}请填写付款金额,且必须大于0`);
return false;
}
- const rowsTotal = Number(this.settlementRowsAppliedTotal().toFixed(2));
- if (rowsTotal - appliedAmount > 1e-8) {
+ const exceededRow = this.referenceRows.find(
+ item => Number(item.appliedAmount || 0) > Number(item.payableAmount || 0)
+ );
+ if (exceededRow) {
this.clearMessages();
- ElMessage.warning(
- `结算信息付款金额合计(${rowsTotal.toFixed(2)})不能大于申请付款金额(${appliedAmount.toFixed(2)})`
- );
+ ElMessage.warning(`${exceededRow.settlementNo || '结算单'}付款金额不能超过可付款金额`);
return false;
}
}
diff --git a/src/views/settlement/components/settlement-adjustment-editor.vue b/src/views/settlement/components/settlement-adjustment-editor.vue
index 0ee9530..9eda453 100644
--- a/src/views/settlement/components/settlement-adjustment-editor.vue
+++ b/src/views/settlement/components/settlement-adjustment-editor.vue
@@ -380,7 +380,13 @@ export default {
},
async loadFormalSettlements(keyword = '') {
const { data } = await api.getFormalSettlements(keyword);
- this.candidates = data?.data || data || [];
+ const payload = data?.data ?? data;
+ // 兼容误返回分页对象的情况,只取正式结算单列表
+ this.candidates = Array.isArray(payload)
+ ? payload
+ : Array.isArray(payload?.records)
+ ? payload.records
+ : [];
},
async loadFeeOptions() {
const { data } = await api.getFeeOptions();
diff --git a/src/views/settlement/components/settlement-import-page.vue b/src/views/settlement/components/settlement-import-page.vue
index 6a45c08..3d6152a 100644
--- a/src/views/settlement/components/settlement-import-page.vue
+++ b/src/views/settlement/components/settlement-import-page.vue
@@ -926,14 +926,34 @@ const stripInternalRowFields = row => {
};
};
+/** 合并费用列名与各行 feeItems 键,避免保存/回显时丢动态费用列 */
+const collectFeeItemNames = (preferred = [], detailRows = []) => {
+ const names = [];
+ const seen = new Set();
+ const pushName = value => {
+ const name = String(value || '').trim();
+ if (!name || seen.has(name)) return;
+ seen.add(name);
+ names.push(name);
+ };
+ (preferred || []).forEach(pushName);
+ (detailRows || []).forEach(row => {
+ const feeItems = row?.feeItems;
+ if (!feeItems || typeof feeItems !== 'object' || Array.isArray(feeItems)) return;
+ Object.keys(feeItems).forEach(pushName);
+ });
+ return names;
+};
+
const buildPayload = () => {
const { file, ...batch } = form;
+ const payloadRows = rows.value.map(stripInternalRowFields);
return {
...batch,
settlementBillType: form.settlementType === 'payable' ? form.settlementBillType || 'formal' : 'formal',
waybillMode: form.waybillMode === 'history' ? 'history' : 'new',
- feeItemNames: [...feeItemNames.value],
- rows: rows.value.map(stripInternalRowFields),
+ feeItemNames: collectFeeItemNames(feeItemNames.value, payloadRows),
+ rows: payloadRows,
};
};
@@ -1436,9 +1456,13 @@ const loadDetail = async id => {
if (detail.projectId) {
await loadContractsByProject(detail.projectId, detail.projectName || '');
}
- feeItemNames.value = Array.isArray(detail.feeItemNames) ? detail.feeItemNames : [];
parsedHeaders.value = [];
const detailRows = Array.isArray(detail.rows) ? detail.rows : [];
+ // 先按库中列名与明细 feeItems 合并,再规范化行数据,保证草稿再打开仍展示全部费用列
+ feeItemNames.value = collectFeeItemNames(
+ Array.isArray(detail.feeItemNames) ? detail.feeItemNames : [],
+ detailRows
+ );
rows.value = detailRows.map((item, index) => normalizeParsedRow(item, index));
refreshDuplicateFlags();
// 查看模式始终只读;编辑时仅草稿可改,已完成/已作废强制只读
diff --git a/src/views/settlement/components/transport-reconciliation-editor.vue b/src/views/settlement/components/transport-reconciliation-editor.vue
index 1619e92..923d9f4 100644
--- a/src/views/settlement/components/transport-reconciliation-editor.vue
+++ b/src/views/settlement/components/transport-reconciliation-editor.vue
@@ -125,15 +125,17 @@
货物名称
- 重置
- 查询
- 导出
+
+ 重置
+ 查询
+ 导出
+
{{ formatDate(row[column.prop]) }}
+ {{
+ formatOptionalMoney(row.unitPrice)
+ }}
{{ formatMoney(row[column.prop]) }}
{{ displayValue(row[column.prop]) }}
@@ -318,6 +323,9 @@
"
>{{ formatDate(row[column.prop]) }}
+ {{
+ formatOptionalMoney(row.unitPrice)
+ }}
{{ formatMoney(row[column.prop]) }}
{{ displayValue(row[column.prop]) }}
@@ -476,6 +484,7 @@
{{ formatMoney(getFeeItemAmount(row, column.feeItemName)) }}
{{ formatMileage(row.mileage) }}
{{ formatDate(row[column.prop]) }}
+ {{ formatOptionalMoney(row.unitPrice) }}
{{ formatMoney(row[column.prop]) }}
{{ displayValue(row[column.prop]) }}
@@ -630,7 +639,10 @@ import { getDictionary } from '@/api/system/dictbiz';
import { Search } from '@element-plus/icons-vue';
import { h } from 'vue';
import { downloadXls } from '@/utils/util';
-import { isExcelResponse, parseBlobJson } from '@/utils/import-excel';
+import {
+ consumeImportResponse,
+ resolveImportErrorMessage,
+} from '@/utils/import-excel';
import { transportReconciliationFormFields } from '@/option/settlement/transportReconciliationForm';
import {
internalColumns,
@@ -1042,12 +1054,24 @@ export default {
.map(normalized)
.join('|');
},
+ blankOptionalNumber(value) {
+ if (value === null || value === undefined || value === '' || Number(value) === -1) {
+ return null;
+ }
+ return value;
+ },
normalizeInternalRow(row) {
const feeItems =
row.feeItems && typeof row.feeItems === 'object' && Object.keys(row.feeItems).length
? row.feeItems
: this.parseFeeItems(row.feeItemsJson);
- return { ...row, feeItems };
+ return {
+ ...row,
+ feeItems,
+ unitPrice: this.blankOptionalNumber(row.unitPrice),
+ mileage: this.blankOptionalNumber(row.mileage),
+ freightAmount: this.blankOptionalNumber(row.freightAmount),
+ };
},
normalizeExternalRow(row, index) {
const feeItems =
@@ -1057,6 +1081,9 @@ export default {
return {
...row,
feeItems,
+ unitPrice: this.blankOptionalNumber(row.unitPrice),
+ mileage: this.blankOptionalNumber(row.mileage),
+ freightAmount: this.blankOptionalNumber(row.freightAmount),
_externalKey: row.id || row.externalLineNo || `external-${index}`,
};
},
@@ -1284,6 +1311,9 @@ export default {
snapshot.feeItemsJson = row.feeItemsJson || JSON.stringify(row.feeItems || {});
snapshot.actualDepartureTime = this.normalizeExternalDateTime(row.actualDepartureTime);
snapshot.actualCompletionTime = this.normalizeExternalDateTime(row.actualCompletionTime);
+ snapshot.unitPrice = this.blankOptionalNumber(row.unitPrice);
+ snapshot.mileage = this.blankOptionalNumber(row.mileage);
+ snapshot.freightAmount = this.blankOptionalNumber(row.freightAmount);
return snapshot;
});
},
@@ -1297,6 +1327,9 @@ export default {
snapshot.feeItemsJson = row.feeItemsJson || JSON.stringify(row.feeItems || {});
snapshot.actualDepartureTime = this.normalizeExternalDateTime(row.actualDepartureTime);
snapshot.actualCompletionTime = this.normalizeExternalDateTime(row.actualCompletionTime);
+ snapshot.unitPrice = this.blankOptionalNumber(row.unitPrice);
+ snapshot.mileage = this.blankOptionalNumber(row.mileage);
+ snapshot.freightAmount = this.blankOptionalNumber(row.freightAmount);
snapshot.suspectedDuplicate = duplicateIds.has(String(row.id));
return snapshot;
});
@@ -1326,7 +1359,7 @@ export default {
}
},
async handleUpdate() {
- await this.$confirm('将以外部账单数据更新运单及内部结算明细,是否继续?', '更新账单', {
+ await this.$confirm('将以外部账单数据更新内部结算明细,是否继续?', '更新账单', {
type: 'warning',
});
this.actionLoading = true;
@@ -1508,22 +1541,23 @@ export default {
? await api.importCargo(this.currentId, file)
: await api.importVehicle(this.currentId, file);
// 成功时 axios 拦截器可能已把 JSON Blob 解析成对象,不能再调 .text()
- if (isExcelResponse(response)) {
- downloadXls(
- response.data,
- `运输对账导入失败明细${this.$dayjs().format('YYYY-MM-DD HH-mm-ss')}.xlsx`
- );
+ const consumed = await consumeImportResponse(response, {
+ businessName: '运输对账',
+ dayjsFormat: this.$dayjs().format('YYYY-MM-DD HH-mm-ss'),
+ });
+ if (consumed.kind === 'excel') {
+ downloadXls(consumed.blob, consumed.fileName);
this.$message.warning('部分数据导入失败,已下载失败明细');
} else {
- const result = await parseBlobJson(response.data);
- if (Number(result.code) !== 200) throw new Error(result.msg || '导入失败');
this.$message.success('外部账单导入成功');
}
this.matchingStarted = false;
await this.loadDetail();
this.matchingStarted = false;
} catch (error) {
- this.$message.error(error.message || '外部账单导入失败');
+ if (!error?._notified) {
+ this.$message.error(await resolveImportErrorMessage(error));
+ }
}
},
async downloadTemplate(mode) {
@@ -1831,7 +1865,10 @@ export default {
: Number(value || 0).toFixed(2);
},
formatMileage(value) {
- return Number(value) === -1 ? '' : this.displayValue(value);
+ return this.blankOptionalNumber(value) === null ? '' : this.displayValue(value);
+ },
+ formatOptionalMoney(value, currency = 'RMB') {
+ return this.blankOptionalNumber(value) === null ? '-' : this.formatMoney(value, currency);
},
formatDate(value) {
if (value === null || value === undefined || value === '') return '-';
@@ -1952,15 +1989,26 @@ export default {
}
.reconciliation-editor__filter {
display: grid;
- grid-template-columns: repeat(4, minmax(160px, 1fr)) auto auto;
+ grid-template-columns: repeat(4, minmax(160px, 1fr)) auto;
gap: 8px;
margin-bottom: 12px;
+ align-items: center;
}
.reconciliation-editor__filter-item {
display: flex;
align-items: center;
min-width: 0;
}
+.reconciliation-editor__filter-actions {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ white-space: nowrap;
+}
+.reconciliation-editor__filter-actions .el-button {
+ min-width: 64px;
+ margin-left: 0;
+}
.reconciliation-editor__filter-label {
flex: 0 0 auto;
margin-right: 8px;
diff --git a/src/views/settlement/settlement-import-form.vue b/src/views/settlement/settlement-import-form.vue
index 6d0799f..fcbc784 100644
--- a/src/views/settlement/settlement-import-form.vue
+++ b/src/views/settlement/settlement-import-form.vue
@@ -6,15 +6,33 @@
diff --git a/src/views/system/user.vue b/src/views/system/user.vue
index dcdc6e3..ae2f53a 100644
--- a/src/views/system/user.vue
+++ b/src/views/system/user.vue
@@ -115,9 +115,10 @@
{{ name }}
-
@@ -666,6 +667,104 @@ export default {
this.page.currentPage = 1;
this.onLoad(this.page);
},
+ formatDeptIds(deptId) {
+ if (!deptId && deptId !== 0) {
+ return [];
+ }
+ return String(deptId)
+ .split(',')
+ .map(item => item.trim())
+ .filter(Boolean);
+ },
+ handleDeptTagClick(row, index) {
+ const deptIds = this.formatDeptIds(row.deptId);
+ let deptId = deptIds[index];
+ if (!deptId) {
+ const names = this.formatDeptNames(row.deptName);
+ deptId = this.findDeptIdByName(names[index]);
+ }
+ if (!deptId) {
+ this.$message.warning('未找到对应组织');
+ return;
+ }
+ this.selectDeptOnTree(deptId);
+ },
+ findDeptIdByName(name, tree = this.permissionDeptTree) {
+ if (!name) return null;
+ for (const node of tree || []) {
+ if (node.title === name || node.deptName === name || node.label === name) {
+ return node.id;
+ }
+ const matched = this.findDeptIdByName(name, node.children);
+ if (matched != null) return matched;
+ }
+ return null;
+ },
+ /**
+ * 展开左侧懒加载组织树到目标节点并设为选中,同时按该组织筛选用户列表
+ */
+ async selectDeptOnTree(deptId) {
+ if (deptId == null || deptId === '') return;
+ if (!this.permissionDeptTree?.length) {
+ const tenantId = this.userInfo.tenantId || this.website.tenantId;
+ try {
+ const res = await getDeptTree(tenantId);
+ this.permissionDeptTree = res.data.data || [];
+ } catch (error) {
+ this.$message.error('组织树加载失败');
+ return;
+ }
+ }
+ const path = this.findDeptPath(deptId);
+ if (!path?.length) {
+ this.$message.warning('未在组织树中找到该部门');
+ return;
+ }
+ const tree = this.$refs.deptTree;
+ if (!tree) return;
+ for (let i = 0; i < path.length - 1; i++) {
+ await this.expandLazyTreeNode(path[i]);
+ }
+ await this.$nextTick();
+ const targetId = path[path.length - 1];
+ tree.setCurrentKey?.(targetId);
+ this.$nextTick(() => {
+ const currentEl = tree.$el?.querySelector?.('.el-tree-node.is-current');
+ currentEl?.scrollIntoView?.({ block: 'nearest', behavior: 'smooth' });
+ });
+ if (String(this.treeDeptId) === String(targetId)) {
+ return;
+ }
+ this.treeDeptId = targetId;
+ this.page.currentPage = 1;
+ this.onLoad(this.page);
+ },
+ expandLazyTreeNode(key) {
+ return new Promise(resolve => {
+ const tree = this.$refs.deptTree;
+ if (!tree?.getNode) {
+ resolve();
+ return;
+ }
+ const tryExpand = (retries = 40) => {
+ const node = tree.getNode(key);
+ if (!node) {
+ if (retries <= 0) {
+ resolve();
+ return;
+ }
+ setTimeout(() => tryExpand(retries - 1), 50);
+ return;
+ }
+ if (node.expanded || node.isLeaf) {
+ resolve();
+ return;
+ }
+ node.expand(() => resolve());
+ };
+ tryExpand();
+ });
+ },
handleDropdownCommand(command, row, index) {
if (command === 'setLeader') {
this.handleSetLeader(row);
@@ -1537,6 +1636,10 @@ export default {
justify-content: flex-start;
}
+.user-dept-tag {
+ cursor: pointer;
+}
+
.user-dept-tag + .user-dept-tag {
margin-left: 6px;
}
diff --git a/src/views/vehicle/credit-score-quantification.vue b/src/views/vehicle/credit-score-quantification.vue
index 3a145b2..530a7f4 100644
--- a/src/views/vehicle/credit-score-quantification.vue
+++ b/src/views/vehicle/credit-score-quantification.vue
@@ -101,6 +101,17 @@
style="width: 500px"
/>
+
+
+ 分
+
+
({
...(map[category.categoryCode] || category),
categoryName: category.categoryName,
@@ -691,6 +704,14 @@ export default {
standards: (detail.standards || []).map(standard => this.normalizeStandard(standard)),
};
},
+ handleMaxMinusScoreInput(value) {
+ const raw = String(value ?? '')
+ .replace(/[^\d.]/g, '')
+ .replace(/^\./, '')
+ .replace(/(\..*)\./g, '$1')
+ .replace(/^(\d+)(\.\d{0,2})?.*$/, '$1$2');
+ this.configForm.maxMinusScore = raw;
+ },
openConfig(row, readonly = false) {
this.readonly = readonly;
if (row && row.id) {
@@ -816,6 +837,9 @@ export default {
},
prepareSubmitConfig(config) {
const payload = JSON.parse(JSON.stringify(config || {}));
+ const maxMinus = this.normalizePlainDecimal(payload.maxMinusScore);
+ payload.maxMinusScore =
+ maxMinus === '' || maxMinus == null ? null : this.toScoreValue(maxMinus);
(payload.categories || []).forEach(category => {
(category.items || []).forEach(item => {
item.score = this.toScoreValue(item.score);
@@ -1072,6 +1096,14 @@ export default {
this.$message.warning('请输入评定表名称');
return false;
}
+ if (
+ this.configForm.maxMinusScore !== '' &&
+ this.configForm.maxMinusScore != null &&
+ !this.isNonNegativeDecimal(this.configForm.maxMinusScore)
+ ) {
+ this.$message.warning('减分最高扣分须为不小于0的数字,最多两位小数');
+ return false;
+ }
const basic = this.configForm.categories.find(item => item.categoryCode === 'basic');
if (!basic || basic.items.length === 0) {
this.$message.warning('至少配置1个基础得分项目');
diff --git a/src/views/vehicle/customer-archive.vue b/src/views/vehicle/customer-archive.vue
index 36ea6f5..c08062d 100644
--- a/src/views/vehicle/customer-archive.vue
+++ b/src/views/vehicle/customer-archive.vue
@@ -398,47 +398,6 @@
/>
-
-
-
-
-
-
-
-
-
-
-
-
- 无固定期限
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+ 至
+
+
+ 无固定期限
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
handleDecimalInput('invoiceTaxRate', value, 13)"
+ v-model="archiveForm.paidInCapital"
+ maxlength="18"
+ :disabled="kingdeeLocked"
+ @input="value => handleDecimalInput('paidInCapital', value)"
/>
@@ -570,6 +530,29 @@
+
+
+ handleDecimalInput('usedCreditLimit', value)"
+ />
+
+
+
+
+
-
+
-
- 详情
-
-
- 删除
-
+
+
+ 详情
+
+
+ 复评
+
+
+ 删除
+
+
@@ -1155,7 +1147,14 @@
/>
-
+
@@ -1656,8 +1655,10 @@ export default {
const nonNegativeLabelMap = {
invoiceTaxRate: '开票税点',
registeredCapital: '注册资金',
+ paidInCapital: '实缴资本',
maxCreditLimit: '最大资金使用额度',
applyCreditLimit: '申请总资金使用额度',
+ usedCreditLimit: '已使用额度',
};
const validateNonNegative = (rule, value, callback) => {
if (value === undefined || value === null || value === '') {
@@ -1668,15 +1669,6 @@ export default {
callback();
}
};
- const validateInvoiceTaxRate = (rule, value, callback) => {
- if (value === undefined || value === null || value === '') {
- callback();
- } else if (Number(value) < 0 || Number(value) > 13) {
- callback(new Error('开票税点必须在0到13之间'));
- } else {
- callback();
- }
- };
const validateUnifiedCreditCode = (rule, value, callback) => {
const code = String(value || '')
.trim()
@@ -1806,7 +1798,7 @@ export default {
{ label: '是', value: 1 },
{ label: '否', value: 0 },
],
- businessScopeOptions: [],
+ bankListOptions: [],
qualificationTypeOptions: [],
creditLevelOptions: ['A', 'B', 'C', 'D', 'E', 'F'].map(item => ({
label: `${item}级`,
@@ -1845,15 +1837,12 @@ export default {
],
customerType: [{ required: true, message: '请选择客商分类', trigger: 'change' }],
legalPerson: [{ required: true, message: '请输入企业法人', trigger: 'blur' }],
- contactPhone: [
- { required: true, message: '请输入联系电话', trigger: 'blur' },
- { pattern: /^1\d{10}$/, message: '联系电话必须为11位手机号', trigger: 'blur' },
- ],
- deptName: [{ required: true, message: '请选择所属组织', trigger: 'change' }],
- invoiceTaxRate: [{ validator: validateInvoiceTaxRate, trigger: 'blur' }],
registeredCapital: [{ validator: validateNonNegative, trigger: 'blur' }],
+ paidInCapital: [{ validator: validateNonNegative, trigger: 'blur' }],
maxCreditLimit: [{ validator: validateNonNegative, trigger: 'blur' }],
applyCreditLimit: [{ validator: validateNonNegative, trigger: 'blur' }],
+ usedCreditLimit: [{ validator: validateNonNegative, trigger: 'blur' }],
+ businessScope: [{ max: 2000, message: '经营范围最多2000个字符', trigger: 'blur' }],
registeredRegionName: [{ required: true, message: '请选择省市区', trigger: 'change' }],
registeredDetailAddress: [
{ required: true, message: '请输入详细地址', trigger: 'blur' },
@@ -2296,7 +2285,8 @@ export default {
showScoreDelete() {
if (this.readonly) return false;
const status = this.archiveForm.approvalStatus || 'draft';
- return status === 'draft' || status === 'rejected';
+ // 草稿/驳回可删;审批通过后也允许删除并重建评分表(旧评分逻辑删除保留,新评分审核通过前仍用旧额度)
+ return status === 'draft' || status === 'rejected' || status === 'approved';
},
isNewScore() {
return this.scoreRecordIndex < 0 && !this.reviewMode;
@@ -2346,10 +2336,17 @@ export default {
{ categoryCode: 'plus', categoryName: '加分项目' },
{ categoryCode: 'minus', categoryName: '减分项目' },
];
+ const maxMinus = Number(this.currentScore?.maxMinusScore);
+ const hasMaxMinus = Number.isFinite(maxMinus) && maxMinus >= 0;
const rows = categoryMap.map((category, index) => {
const details = this.getScoreCategoryDetails(category.categoryCode);
- const selfScore = details.reduce((sum, item) => sum + Number(item.selfScore || 0), 0);
- const reviewScore = details.reduce((sum, item) => sum + Number(item.reviewScore || 0), 0);
+ let selfScore = details.reduce((sum, item) => sum + Number(item.selfScore || 0), 0);
+ let reviewScore = details.reduce((sum, item) => sum + Number(item.reviewScore || 0), 0);
+ // 减分项目合计受量化表「最高扣分」限制
+ if (category.categoryCode === 'minus' && hasMaxMinus) {
+ selfScore = Math.min(selfScore, maxMinus);
+ reviewScore = Math.min(reviewScore, maxMinus);
+ }
return {
...category,
index: index + 1,
@@ -2406,7 +2403,6 @@ export default {
ensurePublicViewOptions(archive = {}) {
this.mergeSelectOptions('customerNatureOptions', archive.customerNature);
this.mergeSelectOptions('customerTypeOptions', archive.customerType);
- this.mergeSelectOptions('businessScopeOptions', archive.businessScope);
this.mergeSelectOptions(
'qualificationTypeOptions',
(this.qualificationFiles || []).map(item => item.type)
@@ -2464,9 +2460,11 @@ export default {
form.fullName = '';
form.legalPerson = '';
form.registeredCapital = '';
+ form.paidInCapital = '';
form.registeredDetailAddress = '';
- form.businessScope = [];
+ form.businessScope = '';
form.businessTermType = '固定期限';
+ form.businessStartDate = '';
form.businessEndDate = '';
},
// 新增态下统一社会信用代码录入完成后自动查询金蝶(A902-1),防抖 500ms;
@@ -2525,14 +2523,7 @@ export default {
}
if (customer.businessterm) this.applyBusinessTermRecognition(customer.businessterm);
if (customer.businessscope) {
- const matchedScopes = this.businessScopeOptions
- .filter(
- item =>
- customer.businessscope.includes(item.label) ||
- customer.businessscope.includes(item.value)
- )
- .map(item => item.value);
- if (matchedScopes.length) form.businessScope = matchedScopes;
+ form.businessScope = String(customer.businessscope).trim();
}
if (!form.shortName && customer.simplename) form.shortName = customer.simplename;
this.$nextTick(() => {
@@ -2541,6 +2532,8 @@ export default {
'fullName',
'legalPerson',
'registeredCapital',
+ 'paidInCapital',
+ 'businessStartDate',
'businessEndDate',
'businessScope',
'registeredDetailAddress',
@@ -2563,8 +2556,13 @@ export default {
deptIds: '',
deptName: '',
customerType: [],
- businessScope: [],
+ businessScope: '',
businessTermType: '固定期限',
+ businessStartDate: '',
+ businessEndDate: '',
+ registeredCapital: '',
+ paidInCapital: '',
+ usedCreditLimit: '',
networkFreightPlatform: null,
guangxiTop100: null,
registeredRegionPath: [],
@@ -2699,6 +2697,12 @@ export default {
approvedTime: '审核通过时间',
status: '状态',
businessTermType: '营业期限类型',
+ businessStartDate: '营业期限开始日',
+ businessEndDate: '营业期限截止日',
+ registeredCapital: '注册资金(万元)',
+ paidInCapital: '实缴资本(万元)',
+ usedCreditLimit: '已使用额度(万元)',
+ businessScope: '经营范围',
networkFreightPlatform: '网络货运平台',
registeredRegionName: '注册地址',
registeredDetailAddress: '详细地址',
@@ -3147,11 +3151,21 @@ export default {
const customerTypeColumn = this.findColumn(this.option.column, 'customerType');
customerTypeColumn.dicData = [{ label: '全部', value: '' }, ...options];
});
- this.loadDictOptions('scope_of_business', 'businessScopeOptions');
+ this.loadDictOptions('bank_list', 'bankListOptions');
this.loadDictOptions('customer_attachment_types', 'qualificationTypeOptions', [], () => {
this.sortQualificationFiles();
});
},
+ queryBankNameSuggestions(queryString, callback) {
+ const query = String(queryString || '')
+ .trim()
+ .toLowerCase();
+ const options = Array.isArray(this.bankListOptions) ? this.bankListOptions : [];
+ const matched = query
+ ? options.filter(item => String(item.value || '').toLowerCase().includes(query))
+ : options;
+ callback(matched.map(item => ({ value: item.value })));
+ },
initMkEnabled() {
this._mkEnabledResolved = false;
loadMkEnabled()
@@ -3405,8 +3419,15 @@ export default {
});
},
formatBusinessLicenseValidPeriod(row = {}) {
- if (row.businessTermType === '长期') return '无固定期限';
- return row.businessEndDate || '';
+ if (row.businessTermType === '长期') {
+ return row.businessStartDate
+ ? `${row.businessStartDate} 至 无固定期限`
+ : '无固定期限';
+ }
+ if (row.businessStartDate && row.businessEndDate) {
+ return `${row.businessStartDate} 至 ${row.businessEndDate}`;
+ }
+ return row.businessEndDate || row.businessStartDate || '';
},
formatFundUseRate(row = {}) {
if (row.fundUseRate === undefined || row.fundUseRate === null || row.fundUseRate === '') {
@@ -3939,8 +3960,13 @@ export default {
const legalPerson = this.getBusinessLicenseOcrValue(result, ['法定代表人', '法人']);
const address = this.getBusinessLicenseOcrValue(result, ['地址', '住所']);
const capital = this.getBusinessLicenseOcrValue(result, ['注册资本']);
+ const paidInCapital = this.getBusinessLicenseOcrValue(result, ['实缴资本', '实收资本']);
const term = this.getBusinessLicenseOcrValue(result, ['营业期限', '有效期']);
- const termStartDate = this.getBusinessLicenseOcrValue(result, ['有效期起始日期']);
+ const termStartDate = this.getBusinessLicenseOcrValue(result, [
+ '有效期起始日期',
+ '成立日期',
+ '核准日期',
+ ]);
const scope = this.getBusinessLicenseOcrValue(result, ['经营范围']);
if (code) this.archiveForm.unifiedCreditCode = code.replace(/\s/g, '').toUpperCase();
if (name) this.archiveForm.fullName = name;
@@ -3955,15 +3981,16 @@ export default {
const normalizedCapital = capital.replace(/,/g, '').match(/\d+(?:\.\d+)?/);
if (normalizedCapital) this.archiveForm.registeredCapital = normalizedCapital[0];
}
- this.applyBusinessTermRecognition(term);
+ if (paidInCapital) {
+ const normalizedPaidIn = paidInCapital.replace(/,/g, '').match(/\d+(?:\.\d+)?/);
+ if (normalizedPaidIn) this.archiveForm.paidInCapital = normalizedPaidIn[0];
+ }
+ this.applyBusinessTermRecognition(term, termStartDate);
if (this.isNoFixedBusinessTerm(term) || this.isNoFixedBusinessTerm(termStartDate)) {
this.handleNoFixedTermChange(true);
}
if (scope) {
- const matchedScopes = this.businessScopeOptions
- .filter(item => scope.includes(item.label) || scope.includes(item.value))
- .map(item => item.value);
- if (matchedScopes.length) this.archiveForm.businessScope = matchedScopes;
+ this.archiveForm.businessScope = scope;
}
this.$nextTick(() => {
[
@@ -3971,27 +3998,49 @@ export default {
'fullName',
'legalPerson',
'registeredCapital',
+ 'paidInCapital',
+ 'businessStartDate',
'businessEndDate',
'businessScope',
'registeredDetailAddress',
].forEach(prop => this.$refs.archiveForm?.validateField(prop));
});
},
- applyBusinessTermRecognition(term) {
+ normalizeOcrDate(value) {
+ if (!value) return '';
+ const match = String(value).match(/(\d{4})[年./-](\d{1,2})[月./-](\d{1,2})/);
+ if (!match) return '';
+ const month = String(match[2]).padStart(2, '0');
+ const day = String(match[3]).padStart(2, '0');
+ return `${match[1]}-${month}-${day}`;
+ },
+ applyBusinessTermRecognition(term, termStartDate = '') {
+ const startFromField = this.normalizeOcrDate(termStartDate);
+ if (startFromField) {
+ this.archiveForm.businessStartDate = startFromField;
+ }
if (!term) return;
- if (/长期|永久/.test(term)) {
+ if (/长期|永久|无固定/.test(term) || this.isNoFixedBusinessTerm(term)) {
this.archiveForm.businessTermType = '长期';
this.archiveForm.businessEndDate = '';
return;
}
- const match = term.match(/(\d{4}[年./-]\d{1,2}[月./-]\d{1,2})/);
- if (!match) return;
- const date = match[1]
- .replace(/[年./]/g, '-')
- .replace('月', '-')
- .replace('日', '');
- this.archiveForm.businessTermType = '固定期限';
- this.archiveForm.businessEndDate = date;
+ const dateMatches = String(term).match(/\d{4}[年./-]\d{1,2}[月./-]\d{1,2}/g) || [];
+ const startDate = this.normalizeOcrDate(dateMatches[0]) || startFromField;
+ const endDate = this.normalizeOcrDate(dateMatches[1]);
+ if (startDate) {
+ this.archiveForm.businessStartDate = startDate;
+ }
+ if (endDate) {
+ this.archiveForm.businessTermType = '固定期限';
+ this.archiveForm.businessEndDate = endDate;
+ return;
+ }
+ if (startDate && dateMatches.length === 1 && !/至|到|-|—|~/.test(term)) {
+ // 仅识别到一个日期且无区间分隔时,按截止日处理(兼容旧 OCR 结果)
+ this.archiveForm.businessTermType = '固定期限';
+ this.archiveForm.businessEndDate = startDate;
+ }
},
getQualificationFileUrl(file = {}) {
return file.url || file.link || file.domain || '';
@@ -4159,7 +4208,6 @@ export default {
return value;
},
normalizeDetail(detail) {
- const businessScope = this.splitValue(detail.businessScope);
const registeredDetailAddress =
detail.registeredDetailAddress ||
(detail.registeredRegionName ? '' : detail.registeredAddress) ||
@@ -4172,10 +4220,14 @@ export default {
customerKind: detail.customerKind || 'external',
invoiceTaxRate: this.normalizeOptionalAmount(detail.invoiceTaxRate),
registeredCapital: this.normalizeOptionalAmount(detail.registeredCapital),
+ paidInCapital: this.normalizeOptionalAmount(detail.paidInCapital),
maxCreditLimit: this.normalizeOptionalAmount(detail.maxCreditLimit),
applyCreditLimit: this.normalizeOptionalAmount(detail.applyCreditLimit),
- businessScope,
+ usedCreditLimit: this.normalizeOptionalAmount(detail.usedCreditLimit),
+ businessScope: detail.businessScope || '',
businessTermType: detail.businessTermType || '固定期限',
+ businessStartDate: detail.businessStartDate || '',
+ businessEndDate: detail.businessEndDate || '',
networkFreightPlatform:
detail.networkFreightPlatform === 0 || detail.networkFreightPlatform === 1
? detail.networkFreightPlatform
@@ -4211,6 +4263,7 @@ export default {
scoreDate: this.$dayjs().format('YYYY-MM-DD'),
applyCreditLimit: this.archiveForm.applyCreditLimit || '',
maxCreditLimit: '',
+ maxMinusScore: '',
creditLevel: '',
standards: [],
tempApplyCreditLimit: '',
@@ -4230,6 +4283,10 @@ export default {
: score.applyCreditLimit
),
maxCreditLimit: this.normalizeOptionalAmount(score.maxCreditLimit),
+ maxMinusScore:
+ score.maxMinusScore === undefined || score.maxMinusScore === null
+ ? ''
+ : score.maxMinusScore,
tempApplyCreditLimit: this.normalizeOptionalAmount(score.tempApplyCreditLimit),
attachments: score.attachments || this.parseAttachments(score.proofAttachments),
details: (score.details || []).map(detail => ({
@@ -4268,7 +4325,14 @@ export default {
String(item.score.creditLevel || '').trim()
)[0]?.score;
},
+ /** 曾审核通过后:删除/重建评分在再次审核通过前,主表等级与额度保持旧值暂时生效 */
+ shouldFreezeEffectiveCredit(archive = this.archiveForm) {
+ return Boolean(archive?.approvedTime);
+ },
applyArchiveCreditFromScores(archive) {
+ if (this.shouldFreezeEffectiveCredit(archive)) {
+ return archive;
+ }
const latestScore = this.getLatestCreditScore(archive.scores || []);
archive.customerLevel = latestScore?.creditLevel || archive.customerLevel || '';
archive.maxCreditLimit = latestScore?.maxCreditLimit || archive.maxCreditLimit || '';
@@ -4397,16 +4461,21 @@ export default {
if (archive.businessTermType === '长期') {
archive.businessEndDate = null;
}
+ if (!archive.businessStartDate) {
+ archive.businessStartDate = null;
+ }
const invoiceTaxRate = this.normalizeOptionalAmount(archive.invoiceTaxRate);
const registeredCapital = this.normalizeOptionalAmount(archive.registeredCapital);
+ const paidInCapital = this.normalizeOptionalAmount(archive.paidInCapital);
+ const usedCreditLimit = this.normalizeOptionalAmount(archive.usedCreditLimit);
archive.invoiceTaxRate = invoiceTaxRate === '' ? null : invoiceTaxRate;
archive.registeredCapital = registeredCapital === '' ? null : registeredCapital;
+ archive.paidInCapital = paidInCapital === '' ? null : paidInCapital;
+ archive.usedCreditLimit = usedCreditLimit === '' ? null : usedCreditLimit;
archive.customerType = Array.isArray(archive.customerType)
? archive.customerType.join(',')
: archive.customerType;
- archive.businessScope = Array.isArray(archive.businessScope)
- ? archive.businessScope.join(',')
- : archive.businessScope;
+ archive.businessScope = String(archive.businessScope || '').trim();
archive.networkFreightPlatform =
archive.networkFreightPlatform === 0 || archive.networkFreightPlatform === 1
? archive.networkFreightPlatform
@@ -4437,6 +4506,11 @@ export default {
return contact;
})
.filter(item => item.contactName || item.contactPhone);
+ // 主表联系电话改由联系人维护,取第一条有效手机号回填
+ const firstContactPhone = (archive.contacts || [])
+ .map(item => String(item.contactPhone || '').trim())
+ .find(Boolean);
+ archive.contactPhone = firstContactPhone || null;
archive.receiptAccounts = (archive.receiptAccounts || [])
.map(item => this.normalizeReceipt(item))
.filter(item => item.accountName || item.accountHolderName || item.bankAccount || item.cnapsCode);
@@ -4475,16 +4549,29 @@ export default {
archive.applyCreditLimit = applyCreditLimit === '' ? null : applyCreditLimit;
return archive;
},
+ validateContactsRequired(archive = this.archiveForm) {
+ const contacts = (archive.contacts || []).filter(
+ item => String(item.contactName || '').trim() || String(item.contactPhone || '').trim()
+ );
+ if (!contacts.length) {
+ this.$message.warning('请至少维护一条联系人');
+ this.activeTab = 'contacts';
+ return false;
+ }
+ return true;
+ },
saveArchive() {
if (this.kingdeeBlocked) {
this.$message.warning('金蝶审核中,请稍后重新添加');
return;
}
+ this.applyFixedOwnerDept();
this.$refs.archiveForm.validate(valid => {
if (!valid) {
this.$message.warning('请先完整填写必填项');
return;
}
+ if (!this.validateContactsRequired()) return;
submit(this.normalizeArchive()).then(() => {
this.closeArchive();
this.onLoad(this.page);
@@ -4497,11 +4584,13 @@ export default {
this.$message.warning('金蝶审核中,请稍后重新添加');
return;
}
+ this.applyFixedOwnerDept();
this.$refs.archiveForm.validate(valid => {
if (!valid) {
this.$message.warning('请先完整填写必填项');
return;
}
+ if (!this.validateContactsRequired()) return;
const archive = this.normalizeArchive();
// 仅提交时落变更记录;保存不记录
archive.recordChange = true;
@@ -4577,9 +4666,31 @@ export default {
}
this.$nextTick(() => this.openScore(scores[0], 0));
},
+ /** 提交后详情页评分表:审批中且未完成复评时可复评 */
+ canReviewScore(row = {}) {
+ return (
+ this.hasPermission('customer_type_re_cert') &&
+ this.archiveForm.approvalStatus === 'reviewing' &&
+ row.reviewStatus !== '已完成'
+ );
+ },
+ startReviewScore(row, index = 0) {
+ if (!row) {
+ this.$message.warning('暂无评分记录,无法复评');
+ return;
+ }
+ // 详情只读进入时也要能改复评得分并确认
+ this.reviewMode = true;
+ this.openScore(row, index);
+ },
addScore() {
- if ((this.archiveForm.scores || []).length) {
- this.$message.warning('请勿重复添加');
+ const existingScores = this.archiveForm.scores || [];
+ if (existingScores.length) {
+ this.$message.warning(
+ this.shouldFreezeEffectiveCredit()
+ ? '请先删除原评分记录后再添加新评分表'
+ : '请勿重复添加'
+ );
return;
}
this.scoreRecordIndex = -1;
@@ -4600,9 +4711,9 @@ export default {
const loadOptions = this.scoreQuantificationOptions.length
? Promise.resolve()
: this.initScoreQuantificationOptions();
- loadOptions.then(() =>
- this.ensureScoreQuantificationOption(this.currentScore.quantificationId)
- );
+ loadOptions
+ .then(() => this.ensureScoreQuantificationOption(this.currentScore.quantificationId))
+ .then(() => this.applyMaxMinusScoreFromQuantification(this.currentScore.quantificationId));
},
resetScoreDialog() {
this.scoreRecordIndex = -1;
@@ -4637,6 +4748,11 @@ export default {
applyCreditLimit: this.currentScore.applyCreditLimit,
maxCreditLimit: '',
creditLevel: '',
+ maxMinusScore:
+ quantificationData.maxMinusScore ??
+ scoreTemplate.maxMinusScore ??
+ this.currentScore.maxMinusScore ??
+ '',
tempApplyCreditLimit: this.currentScore.tempApplyCreditLimit,
tempCreditStartDate: this.currentScore.tempCreditStartDate,
tempCreditEndDate: this.currentScore.tempCreditEndDate,
@@ -4653,6 +4769,17 @@ export default {
this.calculateScore(this.currentScore);
});
},
+ applyMaxMinusScoreFromQuantification(quantificationId) {
+ if (!quantificationId) return Promise.resolve();
+ return getCreditScoreQuantificationDetail(quantificationId).then(res => {
+ const data = res?.data?.data || {};
+ this.currentScore.maxMinusScore =
+ data.maxMinusScore === undefined || data.maxMinusScore === null
+ ? ''
+ : data.maxMinusScore;
+ this.calculateScore(this.currentScore);
+ });
+ },
normalizeScoreTemplateDetails(template = {}) {
const categoryDetails = (template.categories || []).flatMap(category =>
(category.items || []).map(item => ({
@@ -4797,13 +4924,24 @@ export default {
if (!valid) return null;
const distance = increase ? input - base : base - input;
+ const changeValue = Number(rule.changeValue);
+ const stepSize = Number.isFinite(changeValue) && changeValue > 0 ? changeValue : 1;
+ const steps = Math.floor(distance / stepSize + 1e-9);
const stepScore = Math.abs(Number(rule.score || 0));
- if (!Number.isFinite(stepScore)) return null;
+ if (!Number.isFinite(stepScore) || steps < 0) return null;
- const projectScore = this.getScoreItemFullScore(detail);
+ // 减分项:按阶梯累计扣分,存正数,总分计算时再减;不做项目满分截断
+ if (this.isMinusScoreDetail(detail)) {
+ return Number((steps * stepScore).toFixed(2));
+ }
+
+ // 基础/加分项:从项目分值起步,按阶梯加减,限制在 [0, 项目满分]
+ const baseScore = Number(detail.score || 0);
const signedStep = rule.scoreType === 'subtract' ? -stepScore : stepScore;
- const calculatedScore = Number(detail.score || 0) + distance * signedStep;
- return Number(Math.min(Math.max(calculatedScore, 0), projectScore).toFixed(2));
+ const calculatedScore = baseScore + steps * signedStep;
+ const projectScore = this.getScoreItemFullScore(detail);
+ const upper = Math.max(projectScore, baseScore, 0);
+ return Number(Math.min(Math.max(calculatedScore, 0), upper).toFixed(2));
},
handleScoreValueInput(detail, value) {
const raw = String(value ?? '')
@@ -4984,14 +5122,17 @@ export default {
const plusDetails = this.getScoreCategoryDetailsByList(details, 'plus');
const minusDetails = this.getScoreCategoryDetailsByList(details, 'minus');
const sumScore = (list, prop) => list.reduce((sum, item) => sum + Number(item[prop] || 0), 0);
+ const rawMinusSelf = sumScore(minusDetails, 'selfScore');
+ const rawMinusReview = sumScore(minusDetails, 'reviewScore');
+ // 量化表「减分最高扣分」:所有减分项合计不得超过该上限
+ const maxMinus = Number(score.maxMinusScore);
+ const hasMaxMinus = Number.isFinite(maxMinus) && maxMinus >= 0;
+ const minusSelf = hasMaxMinus ? Math.min(rawMinusSelf, maxMinus) : rawMinusSelf;
+ const minusReview = hasMaxMinus ? Math.min(rawMinusReview, maxMinus) : rawMinusReview;
const selfScore =
- sumScore(basicDetails, 'selfScore') +
- sumScore(plusDetails, 'selfScore') -
- sumScore(minusDetails, 'selfScore');
+ sumScore(basicDetails, 'selfScore') + sumScore(plusDetails, 'selfScore') - minusSelf;
const reviewScore =
- sumScore(basicDetails, 'reviewScore') +
- sumScore(plusDetails, 'reviewScore') -
- sumScore(minusDetails, 'reviewScore');
+ sumScore(basicDetails, 'reviewScore') + sumScore(plusDetails, 'reviewScore') - minusReview;
const hasReviewScore = details.some(
item => !this.isBlankScore(item.reviewScore)
);
@@ -5065,7 +5206,11 @@ export default {
return pending;
},
deleteScore(index) {
- this.$confirm('确定删除该评分记录?', '提示', {
+ const freezeCredit = this.shouldFreezeEffectiveCredit();
+ const confirmText = freezeCredit
+ ? '确定删除该评分记录?删除后可重新添加评分表;原评分数据会保留,新评分再次审核通过前,客商等级与额度仍按原评分暂时生效。'
+ : '确定删除该评分记录?';
+ this.$confirm(confirmText, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
@@ -5078,16 +5223,23 @@ export default {
} else if (this.scoreRecordIndex > index) {
this.scoreRecordIndex -= 1;
}
- const latestScore = this.getLatestCreditScore(this.archiveForm.scores);
- if (latestScore) {
- this.syncArchiveCreditFromScores();
- } else {
- this.archiveForm.customerLevel = '';
- this.archiveForm.maxCreditLimit = '';
- this.archiveForm.applyCreditLimit = '';
+ if (!freezeCredit) {
+ const latestScore = this.getLatestCreditScore(this.archiveForm.scores);
+ if (latestScore) {
+ this.syncArchiveCreditFromScores();
+ } else {
+ this.archiveForm.customerLevel = '';
+ this.archiveForm.maxCreditLimit = '';
+ this.archiveForm.applyCreditLimit = '';
+ }
}
this.handleScoreCurrentChange(this.scorePage.currentPage);
- this.saveScoreArchive('评分记录已删除', '评分记录已删除,请保存客商档案后生效');
+ this.saveScoreArchive(
+ freezeCredit
+ ? '评分记录已删除,原等级与额度暂时继续生效'
+ : '评分记录已删除',
+ '评分记录已删除,请保存客商档案后生效'
+ );
})
.catch(() => {});
},
@@ -5223,6 +5375,7 @@ export default {
getDetail(row.id).then(res => {
const archive = this.normalizeDetail(res.data.data);
archive.qualificationAttachments = archive.qualificationAttachments || '';
+ if (!this.validateContactsRequired(archive)) return;
if (!this.validateScoreForApproval(archive)) return;
this.$confirm('是否提交客商准入审批?', '提示', {
confirmButtonText: '确定',
@@ -5604,7 +5757,8 @@ export default {
display: flex;
align-items: center;
gap: 8px;
- flex-wrap: nowrap;
+ flex-wrap: wrap;
+ width: 100%;
:deep(.el-date-editor) {
flex: 0 0 140px;
@@ -5612,6 +5766,11 @@ export default {
min-width: 0;
}
+ .business-term-separator {
+ flex: 0 0 auto;
+ color: #606266;
+ }
+
:deep(.el-checkbox) {
flex: 0 0 auto;
margin-right: 0;
@@ -5619,6 +5778,10 @@ export default {
}
}
+.invoice-bank-autocomplete {
+ width: 100%;
+}
+
.archive-form__address {
:deep(.el-cascader) {
flex: 0 0 240px;
@@ -5827,6 +5990,14 @@ export default {
}
}
+.score-list-table__actions {
+ display: inline-flex;
+ flex-wrap: wrap;
+ align-items: center;
+ justify-content: center;
+ gap: 8px;
+}
+
.contact-form,
.receipt-form,
.invoice-form {
@@ -5846,11 +6017,13 @@ export default {
:deep(.el-input),
:deep(.el-cascader),
- :deep(.el-select) {
+ :deep(.el-select),
+ :deep(.el-autocomplete) {
width: 100%;
}
- :deep(.el-cascader .el-input) {
+ :deep(.el-cascader .el-input),
+ :deep(.el-autocomplete .el-input) {
width: 100%;
}
}
diff --git a/vite.config.mjs b/vite.config.mjs
index c2f339c..3e0426e 100644
--- a/vite.config.mjs
+++ b/vite.config.mjs
@@ -49,28 +49,28 @@ export default ({ mode, command }) => {
__VUE_I18N_LEGACY_API__: true,
__INTLIFY_PROD_DEVTOOLS__: false,
},
- // server: {
- // port: 2888,
- // proxy: {
- // '/api': {
- // // 本地网关默认 80 端口(blade-gateway bootstrap.yml);开发时需先启动网关及 blade-auth/blade-system 等服务
- // // 注意:本机 8080 一般是 Nacos,不是业务网关
- // target: 'http://localhost',
- // // target: 'http://172.16.203.228:8000', // 仅联调远程网关时启用(且不要 rewrite)
- // changeOrigin: true,
- // rewrite: path => path.replace(/^\/api/, ''),
- // },
- // },
- // },
server: {
- port: 2889,
+ port: 2888,
proxy: {
'/api': {
- target: 'http://172.16.203.228:8000',
+ // 本地网关默认 80 端口(blade-gateway bootstrap.yml);开发时需先启动网关及 blade-auth/blade-system 等服务
+ // 注意:本机 8080 一般是 Nacos,不是业务网关
+ target: 'http://localhost',
+ // target: 'http://172.16.203.228:8000', // 仅联调远程网关时启用(且不要 rewrite)
changeOrigin: true,
+ rewrite: path => path.replace(/^\/api/, ''),
},
},
},
+ // server: {
+ // port: 2889,
+ // proxy: {
+ // '/api': {
+ // target: 'http://172.16.203.228:8000',
+ // changeOrigin: true,
+ // },
+ // },
+ // },
resolve: {
alias: {
'~': resolve(__dirname, './'),