@@ -671,21 +672,67 @@
v-model="form.vehicleNo"
placeholder="请输入"
clearable
+ maxlength="30"
:disabled="dialogReadonly"
/>
+
+
+
+
+
+
+
+
+
handleTaskNumberInput('mileage', value)"
+ @input="handleTaskMileageInput"
/>
-
+
+
+
-
+
-
+
-
+
@@ -759,6 +814,7 @@
v-model="form.vehicleNo"
placeholder="请输入"
clearable
+ maxlength="30"
:disabled="dialogReadonly"
/>
@@ -767,6 +823,7 @@
v-model="form.trailerVehicleNo"
placeholder="请输入"
clearable
+ maxlength="30"
:disabled="dialogReadonly"
/>
@@ -777,6 +834,7 @@
v-model="form.escortName"
placeholder="请输入"
clearable
+ maxlength="20"
:disabled="dialogReadonly"
/>
@@ -785,6 +843,7 @@
v-model="form.escortPhone"
placeholder="请输入"
clearable
+ maxlength="20"
:disabled="dialogReadonly"
/>
@@ -793,13 +852,17 @@
v-model="form.mileage"
placeholder="请输入"
clearable
+ maxlength="10"
:disabled="dialogReadonly"
- @input="value => handleTaskNumberInput('mileage', value)"
+ @input="handleTaskMileageInput"
/>
-
+
-
+
-
+
@@ -1000,6 +1067,34 @@
v-model="form.vehicleNo"
placeholder="请输入车牌号"
clearable
+ maxlength="30"
+ :disabled="dialogReadonly"
+ />
+
+
+
+
+
+
+
+
+
@@ -1078,16 +1173,17 @@
-
+
handleTaskNumberInput('mileage', value)"
+ @input="handleTaskMileageInput"
/>
-
+
-
+
-
+
+
+ 待调度列表
+
+ {{ dispatchSummaryText }}
+
+
新增
-
- {{ dispatchSummaryText }}
-
row._duplicate).length;
},
@@ -4479,50 +4581,50 @@ export default {
.join('; ');
return cargoInfo || this.dispatchRow.goodsInfo || '';
},
- dispatchSummaryTotalQuantity() {
- const goodsQuantity = this.dispatchPlanGoodsRows.reduce(
- (total, row) =>
- total +
- this.parseDispatchQuantity(row.quantity || row.cargoQuantity || row.goodsQuantity),
- 0
- );
- if (goodsQuantity > 0) return goodsQuantity;
- return this.parseDispatchQuantity(
- this.dispatchRow.totalQuantity ||
- this.dispatchRow.totalCargoQuantity ||
- this.dispatchRow.quantity
- );
+ dispatchSummaryItems() {
+ const summaryMap = new Map();
+ const addQuantity = (unit, field, quantity) => {
+ const normalizedUnit = unit || '吨';
+ if (!summaryMap.has(normalizedUnit)) {
+ summaryMap.set(normalizedUnit, { unit: normalizedUnit, total: 0, assigned: 0 });
+ }
+ summaryMap.get(normalizedUnit)[field] += this.parseDispatchQuantity(quantity);
+ };
+ const goodsRows = this.dispatchPlanGoodsRows.length
+ ? this.dispatchPlanGoodsRows
+ : [this.dispatchRow];
+
+ goodsRows.forEach(row => {
+ addQuantity(
+ this.getDispatchQuantityUnit(row),
+ 'total',
+ row.quantity ||
+ row.cargoQuantity ||
+ row.goodsQuantity ||
+ row.totalQuantity ||
+ row.totalCargoQuantity
+ );
+ });
+ this.dispatchRows.forEach(row => {
+ addQuantity(this.getDispatchQuantityUnit(row), 'assigned', row.quantity);
+ });
+
+ return Array.from(summaryMap.values()).map(item => ({
+ ...item,
+ remaining: Math.max(item.total - item.assigned, 0),
+ }));
},
- dispatchSummaryAssignedQuantity() {
- return this.dispatchRows.reduce(
- (total, row) => total + this.parseDispatchQuantity(row.quantity),
- 0
- );
- },
- dispatchSummaryRemainingQuantity() {
- const total = this.dispatchSummaryTotalQuantity;
- const assigned = this.dispatchSummaryAssignedQuantity;
- const remain = total - assigned;
- return remain > 0 ? remain : 0;
- },
- dispatchSummaryUnit() {
- const unitRow = this.dispatchPlanGoodsRows.find(
- row => row.quantityUnit || row.goodsQuantityUnit || row.unit
- );
- return (
- unitRow?.quantityUnit ||
- unitRow?.goodsQuantityUnit ||
- unitRow?.unit ||
- this.dispatchRow.quantityUnit ||
- this.dispatchRows.find(row => row.quantityUnit)?.quantityUnit ||
- '吨'
- );
+ dispatchHasRemainingQuantity() {
+ return this.dispatchSummaryItems.some(item => item.remaining > 0);
},
dispatchSummaryText() {
- const total = this.formatDispatchQuantity(this.dispatchSummaryTotalQuantity);
- const assigned = this.formatDispatchQuantity(this.dispatchSummaryAssignedQuantity);
- const remain = this.formatDispatchQuantity(this.dispatchSummaryRemainingQuantity);
- return `共${total}${this.dispatchSummaryUnit} | 已调度 ${assigned}${this.dispatchSummaryUnit},剩余${remain}${this.dispatchSummaryUnit}`;
+ const formatSummary = field =>
+ this.dispatchSummaryItems
+ .map(item => `${this.formatDispatchQuantity(item[field])}${item.unit}`)
+ .join('、');
+ return `共${formatSummary('total')} | 已调度 ${formatSummary('assigned')},剩余${formatSummary(
+ 'remaining'
+ )}`;
},
dispatchAttachmentRows() {
return this.parseJsonArray(this.dispatchRow.attachmentsJson);
@@ -4738,8 +4840,22 @@ export default {
: Number(this.shippingTemplateFreight.freightAmount || 0);
return freightAmount + Number(this.shippingTemplateFreight.otherFreightAmount || 0);
},
- formatShippingTemplateFooterFreightTotal() {
- return this.shippingTemplateFooterFreightTotal.toLocaleString('zh-CN', {
+ waybillFooterFreightTotal() {
+ return Number(this.taskFreightAmount || 0) + Number(this.form.otherFeeTotal || 0);
+ },
+ showFooterFreightSummary() {
+ return (
+ this.config.enableShippingTemplateFreight === true ||
+ this.config.enableWaybillFooterFreightSummary === true
+ );
+ },
+ footerFreightTotal() {
+ return this.config.enableShippingTemplateFreight === true
+ ? this.shippingTemplateFooterFreightTotal
+ : this.waybillFooterFreightTotal;
+ },
+ formatFooterFreightTotal() {
+ return this.footerFreightTotal.toLocaleString('zh-CN', {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
});
@@ -5053,13 +5169,15 @@ export default {
});
},
displayStatus(row, prop) {
+ const formatStatus = this.config.formatStatus;
const textProp = prop === this.config.statusProp ? this.config.statusTextProp : '';
- if (textProp && row[textProp]) {
- return row[textProp];
- }
const column = this.findColumn(this.option.column, prop);
const item = column && (column.dicData || []).find(dic => dic.value === row[prop]);
- return item ? item.label : row[prop] || '未知';
+ const defaultText =
+ textProp && row[textProp] ? row[textProp] : item ? item.label : row[prop] || '未知';
+ return typeof formatStatus === 'function'
+ ? formatStatus(row, prop, defaultText)
+ : defaultText;
},
syncContractNoDisabled(type) {
if (!this.config.enableContractForm) return;
@@ -5223,16 +5341,17 @@ export default {
},
normalizeRow(row, saveAsDraft = false) {
const submitRow = { ...row };
+ const skipValidation = saveAsDraft && this.config.skipDraftValidation === true;
if (this.config.enableTransportPlanForm) {
- if (!this.validateTransportPlanForm(submitRow)) return false;
+ if (!skipValidation && !this.validateTransportPlanForm(submitRow)) return false;
submitRow.goodsJson = JSON.stringify(this.transportCargoRows);
if (this.config.enableShippingTemplateFreight) {
- if (!this.validateShippingTemplateFreight()) return false;
+ if (!skipValidation && !this.validateShippingTemplateFreight()) return false;
submitRow.freightJson = this.serializeShippingTemplateFreight();
}
}
if (this.taskInfoFormEnabled) {
- if (!this.validateTaskInfoForm(submitRow)) return false;
+ if (!skipValidation && !this.validateTaskInfoForm(submitRow)) return false;
this.syncTaskInfoToRow(submitRow);
}
if (this.config.enableAttachmentTable) {
@@ -5245,7 +5364,9 @@ export default {
submitRow.billingPlanJson = JSON.stringify(this.billingPlanRows);
}
if (this.config.enableSettlementRule) {
- if (this.settlementRuleEnabled && !this.validateSettlementRule()) return false;
+ if (!skipValidation && this.settlementRuleEnabled && !this.validateSettlementRule()) {
+ return false;
+ }
submitRow.settlementRuleJson = JSON.stringify(this.normalizeSettlementRule());
}
if (this.config.enableReconciliation) {
@@ -5255,7 +5376,7 @@ export default {
submitRow.changeRecordJson = JSON.stringify(this.changeRecordRows);
}
if (this.config.saveAsDraft && saveAsDraft) {
- submitRow.approvalStatus = 'draft';
+ submitRow[this.config.draftStatusProp || 'approvalStatus'] = this.config.draftStatus || 'draft';
}
delete submitRow.basicInfoTitle;
delete submitRow.templateInfoTitle;
@@ -5619,7 +5740,7 @@ export default {
this.draftSaveLoading = true;
request(submitRow)
.then(() => {
- this.$message.success('保存成功');
+ this.$message.success(`${this.config.draftSaveText || '保存'}成功`);
this.onLoad(this.page, this.query);
this.closeCrudDialog();
})
@@ -5798,6 +5919,15 @@ export default {
const number = Number(match[0]);
return Number.isFinite(number) ? number : 0;
},
+ getDispatchQuantityUnit(row = {}) {
+ return row.quantityUnit || row.goodsQuantityUnit || row.unit || '吨';
+ },
+ getDispatchSummaryItem(unit) {
+ return this.dispatchSummaryItems.find(item => item.unit === unit);
+ },
+ getDispatchRemainingQuantity(unit) {
+ return this.getDispatchSummaryItem(unit)?.remaining || 0;
+ },
formatDispatchQuantity(value) {
const number = Number(value || 0);
if (!Number.isFinite(number)) return '0';
@@ -6060,10 +6190,10 @@ export default {
this.form.model = this.form.model || taskInfo.model || goodsInfo.model || '';
this.form.quantity = this.form.quantity || taskInfo.quantity || goodsInfo.quantity || '';
this.form.quantityUnit =
- this.form.quantityUnit || taskInfo.quantityUnit || goodsInfo.quantityUnit || '';
+ this.form.quantityUnit || taskInfo.quantityUnit || goodsInfo.quantityUnit || '吨';
this.form.mileage = this.form.mileage || taskInfo.mileage || goodsInfo.mileage || '';
this.form.unitPrice = this.form.unitPrice || taskInfo.unitPrice || goodsInfo.unitPrice || '';
- this.form.priceUnit = this.form.priceUnit || taskInfo.priceUnit || goodsInfo.priceUnit || '';
+ this.form.priceUnit = this.form.priceUnit || taskInfo.priceUnit || goodsInfo.priceUnit || '元/吨';
this.form.otherFeeTotal =
this.form.otherFeeTotal || taskInfo.otherFeeTotal || goodsInfo.otherFeeTotal || '';
this.form.estimatedStartTime =
@@ -6111,9 +6241,9 @@ export default {
loadTaskCarrierOptions() {
if (!this.taskInfoFormEnabled || this.taskCarrierLoading) return Promise.resolve([]);
this.taskCarrierLoading = true;
- return getCarrierCustomerList(1, 9999, { customerType: '承运商' })
+ return getCarrierCustomerList(1, 9999, { customerType: '承运商', status: 1 })
.then(res => {
- this.taskCarrierOptions = extractRecords(res);
+ this.taskCarrierOptions = extractRecords(res).filter(item => Number(item.status) === 1);
return this.taskCarrierOptions;
})
.finally(() => {
@@ -6307,6 +6437,11 @@ export default {
handleTaskNumberInput(prop, value) {
this.form[prop] = this.normalizeTaskNumber(value);
},
+ handleTaskMileageInput(value) {
+ this.form.mileage = this.isRoadTransport
+ ? String(value || '').replace(/\D/g, '').slice(0, 10)
+ : this.normalizeTaskNumber(value);
+ },
normalizeTaskNumber(value) {
const text = String(value || '').replace(/[^\d.]/g, '');
const parts = text.split('.');
@@ -6352,6 +6487,9 @@ export default {
requiredFields.push(['carrierName', '承运商']);
}
if (row.carrierType === '自运') {
+ if (this.isRoadTransport) {
+ requiredFields.push(['driverName', '司机']);
+ }
requiredFields.push(['driverPhone', '手机号']);
}
const emptyField = requiredFields.find(([prop]) => this.isBillingFieldEmpty(row[prop]));
@@ -6363,8 +6501,10 @@ export default {
['quantity', '数量'],
['mileage', '里程'],
['unitPrice', '单价'],
- ['otherFeeTotal', '其他运费合计'],
];
+ if (this.isTaskFullMode) {
+ numberFields.push(['otherFeeTotal', '其他运费合计']);
+ }
const invalidField = numberFields.find(([prop]) => {
const value = row[prop];
return value !== undefined && value !== null && value !== '' && Number(value) < 0;
@@ -6373,6 +6513,27 @@ export default {
this.$message.warning(`${invalidField[1]}不能小于0`);
return false;
}
+ if (this.isRoadTransport) {
+ const invalidLengthField = [
+ ['driverName', '司机', 20],
+ ['driverPhone', '手机号', 20],
+ ['vehicleNo', '车牌号', 30],
+ ['trailerVehicleNo', '挂车车牌号', 30],
+ ['escortName', '押运人', 20],
+ ['escortPhone', '押运人手机号', 20],
+ ].find(([prop, , max]) => String(row[prop] || '').length > max);
+ if (invalidLengthField) {
+ this.$message.warning(`${invalidLengthField[1]}不能超过${invalidLengthField[2]}个字符`);
+ return false;
+ }
+ if (
+ row.mileage &&
+ (!/^\d{1,10}$/.test(String(row.mileage)) || Number(row.mileage) <= 0)
+ ) {
+ this.$message.warning('里程必须为不超过10位的正整数');
+ return false;
+ }
+ }
if (
row.estimatedStartTime &&
row.estimatedEndTime &&
@@ -8886,17 +9047,51 @@ export default {
};
},
formatDispatchCargoInfo(plan = {}, item = {}) {
- const cargoName = item.cargoName || item.goodsName || plan.cargoName || '';
- const cargoType = item.cargoType || item.goodsType || plan.cargoType || '';
- const quantity = item.quantity || item.cargoQuantity || item.goodsQuantity || '';
- const unit = item.quantityUnit || item.goodsQuantityUnit || item.unit || '';
- const quantityText = quantity ? `${quantity}${unit}` : '';
- return (
- [cargoName, cargoType, quantityText].filter(Boolean).join(' - ') || plan.goodsInfo || ''
- );
+ const goodsRows = this.parseJsonArray(plan.goodsJson);
+ const sourceRows = goodsRows.length ? goodsRows : [item];
+ const groups = sourceRows.reduce((result, goods = {}) => {
+ const cargoType =
+ goods.cargoType ||
+ goods.secondCargoTypeName ||
+ goods.secondCargoType ||
+ goods.goodsType ||
+ goods.type ||
+ plan.cargoType ||
+ '货物';
+ const unit = this.getDispatchQuantityUnit(goods);
+ if (!result[unit]) {
+ result[unit] = {
+ cargoType,
+ count: 0,
+ quantity: 0,
+ unit,
+ };
+ }
+ result[unit].count += 1;
+ result[unit].quantity += this.parseDispatchQuantity(
+ goods.quantity || goods.cargoQuantity || goods.goodsQuantity
+ );
+ return result;
+ }, {});
+
+ const cargoInfo = Object.values(groups)
+ .map(
+ group =>
+ `${group.cargoType}等${group.count}种货物 | ${this.formatDispatchQuantity(
+ group.quantity
+ )}${group.unit || ''}`
+ )
+ .join(';');
+ return cargoInfo || plan.goodsInfo || '';
},
openDispatchItemDialog(index = -1, row = defaultDispatchRow()) {
- const defaultGoods = this.dispatchPlanGoodsRows[0] || {};
+ const defaultSummaryItem =
+ this.dispatchSummaryItems.find(item => item.remaining > 0) || this.dispatchSummaryItems[0];
+ const defaultGoods =
+ this.dispatchPlanGoodsRows.find(
+ item => this.getDispatchQuantityUnit(item) === defaultSummaryItem?.unit
+ ) || this.dispatchPlanGoodsRows[0] || {};
+ const defaultUnit = defaultSummaryItem?.unit || this.getDispatchQuantityUnit(defaultGoods);
const baseRow = {
...defaultDispatchRow(),
transportType: this.dispatchRow.transportType || '',
@@ -8910,8 +9105,8 @@ export default {
quantity:
index >= 0
? defaultGoods.quantity || ''
- : this.formatDispatchQuantity(this.dispatchSummaryRemainingQuantity),
- quantityUnit: defaultGoods.quantityUnit || '吨',
+ : this.formatDispatchQuantity(this.getDispatchRemainingQuantity(defaultUnit)),
+ quantityUnit: defaultUnit,
specification: defaultGoods.specification || '',
model: defaultGoods.model || '',
packageType: defaultGoods.packageType || '',
@@ -9000,17 +9195,18 @@ export default {
...this.dispatchItemForm,
attachmentsJson: JSON.stringify(this.dispatchItemAttachmentRows),
};
+ const quantityUnit = this.getDispatchQuantityUnit(nextRow);
const otherDispatchedQuantity = this.dispatchRows.reduce((total, row, index) => {
- if (index === this.dispatchItemIndex) return total;
+ if (index === this.dispatchItemIndex || this.getDispatchQuantityUnit(row) !== quantityUnit) {
+ return total;
+ }
return total + this.parseDispatchQuantity(row.quantity);
}, 0);
const currentQuantity = this.parseDispatchQuantity(nextRow.quantity);
- const totalQuantity = this.dispatchSummaryTotalQuantity;
+ const totalQuantity = this.getDispatchSummaryItem(quantityUnit)?.total || 0;
if (totalQuantity > 0 && otherDispatchedQuantity + currentQuantity > totalQuantity) {
this.$message.warning(
- `已调度数量合计不能超过总数量${this.formatDispatchQuantity(totalQuantity)}${
- this.dispatchSummaryUnit
- }`
+ `已调度${quantityUnit}合计不能超过总数量${this.formatDispatchQuantity(totalQuantity)}${quantityUnit}`
);
return;
}
@@ -9039,7 +9235,7 @@ export default {
});
},
handleDispatchAddRow() {
- if (this.dispatchSummaryRemainingQuantity <= 0) {
+ if (!this.dispatchHasRemainingQuantity) {
this.$message.warning('剩余数量为0,不能新增调度明细');
return;
}
@@ -9141,7 +9337,7 @@ export default {
},
submitDispatch(mode) {
this.dispatchSaving = mode;
- const shouldCompletePlan = mode === 'submit' && this.dispatchSummaryRemainingQuantity <= 0;
+ const shouldCompletePlan = mode === 'submit' && !this.dispatchHasRemainingQuantity;
this.api
.dispatch(this.buildDispatchPayload(mode))
.then(() => {
@@ -9329,12 +9525,6 @@ export default {
width: 100%;
}
- &__task-toolbar {
- display: flex;
- justify-content: flex-end;
- margin-bottom: 16px;
- }
-
&__task-form {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));