+
附件
+
- {{ row.originalName || row.name }}
+
+
+ {{ attachmentName(row) }}
+
+
@@ -3840,8 +4208,7 @@
-
-
+
取消
@@ -4907,12 +5274,18 @@ const defaultDispatchRow = () => ({
driverName: '',
driverPhone: '',
vehicleNo: '',
+ captainName: '',
+ cabinNo: '',
+ containerNo: '',
trailerVehicleNo: '',
escortName: '',
escortPhone: '',
mileage: '',
cargoType: '',
+ cargoTypeCode: '',
+ cargoTypePath: [],
cargoName: '',
+ brand: '',
cargoInfo: '',
quantity: '',
quantityUnit: '吨',
@@ -4924,6 +5297,9 @@ const defaultDispatchRow = () => ({
unitPrice: '',
priceUnit: '元/吨',
otherFeeTotal: '',
+ freightCurrency: 'CNY',
+ freightJson: '',
+ goodsJson: '',
estimatedStartTime: '',
estimatedEndTime: '',
departureAddress: '',
@@ -5072,6 +5448,7 @@ export default {
dispatchItemBox: false,
dispatchItemIndex: -1,
dispatchItemForm: defaultDispatchRow(),
+ dispatchItemCargoRows: [],
dispatchItemAttachmentRows: [],
selectedDispatchItemAttachmentRows: [],
attachmentRows: [],
@@ -5163,6 +5540,13 @@ export default {
taskFeeUnitLoading: false,
dispatchTransportTypeOptions: [],
dispatchTransportTypeLoading: false,
+ transportPlanDetailTransportTypeOptions: [],
+ transportPlanDetailTransportTypeLoading: false,
+ transportPlanWaybillPage: {
+ pageSize: 10,
+ pageSizes: [10, 20, 50, 100],
+ currentPage: 1,
+ },
cargoImportBox: false,
cargoImportLoading: false,
cargoImportRows: [],
@@ -5282,6 +5666,9 @@ export default {
this.loadShippingTemplateCurrencyOptions();
this.loadTaskFeeUnitOptions();
}
+ if (this.config.enableWaybillFooterFreightSummary) {
+ this.loadShippingTemplateCurrencyOptions();
+ }
this.consumeTemplateCreatePayload();
},
computed: {
@@ -5312,6 +5699,60 @@ export default {
isTransportPlanPage() {
return this.config.permission === 'transport_plan';
},
+ isTransportPlanDetailLayout() {
+ return this.isTransportPlanPage && this.detailBox;
+ },
+ transportPlanGoodsText() {
+ const text = this.formatDetailValue(this.detailRow, 'goodsInfo');
+ return text && text !== '-' ? text : '暂无货物信息';
+ },
+ transportPlanDateRange() {
+ const start = this.detailRow.planStartDate || this.detailRow.planStartTime || '';
+ const end = this.detailRow.planEndDate || this.detailRow.planEndTime || '';
+ if (!start && !end) return '-';
+ return `${String(start).slice(0, 10)}-${String(end || start).slice(0, 10)}`;
+ },
+ transportPlanWaybillRows() {
+ const sources = [
+ this.detailRow.waybillList,
+ this.detailRow.waybillRows,
+ this.detailRow.waybills,
+ this.detailRow.transportPlanWaybills,
+ this.detailRow.transportPlanWaybillList,
+ this.detailRow.dispatchRecords,
+ this.detailRow.dispatchRecordList,
+ this.detailRow.dispatchList,
+ this.detailRow.dispatchRows,
+ ];
+ const rows = sources.reduce((result, source) => {
+ const parsed = this.parseTransportPlanWaybillSource(source);
+ return result.length ? result : parsed;
+ }, []);
+ const normalizedRows = rows.map((row, index) =>
+ this.normalizeTransportPlanWaybillRow(row, index)
+ );
+ return normalizedRows;
+ },
+ transportPlanWaybillPageRows() {
+ const start =
+ (this.transportPlanWaybillPage.currentPage - 1) * this.transportPlanWaybillPage.pageSize;
+ return this.transportPlanWaybillRows.slice(
+ start,
+ start + this.transportPlanWaybillPage.pageSize
+ );
+ },
+ transportPlanDispatchSummary() {
+ const goodsRows = this.parseJsonArray(this.detailRow.goodsJson);
+ const total = goodsRows.reduce(
+ (sum, row) => sum + Number(row.quantity || row.cargoQuantity || row.goodsQuantity || 0),
+ 0
+ ) || Number(this.detailRow.totalQuantity || this.detailRow.quantity || 0);
+ const assigned = this.transportPlanWaybillRows.reduce(
+ (sum, row) => sum + Number(row.quantity || row.cargoQuantity || row.goodsQuantity || 0),
+ 0
+ ) || Number(this.detailRow.dispatchedQuantity || this.detailRow.dispatchQuantity || 0);
+ return { total, assigned, remaining: Math.max(total - assigned, 0) };
+ },
isTemplateCreate() {
return (
['transport_plan', 'waybill_manage'].includes(this.config.permission) &&
@@ -5366,8 +5807,19 @@ export default {
return this.config.enableTaskInfoForm === true;
},
taskEntryModeOptions() {
+ const locked = this.hasMultipleConfiguredGoods(this.form, this.transportCargoRows);
return [
- { label: '精简录入', value: 'simple' },
+ { label: '精简录入', value: 'simple', disabled: locked },
+ { label: '完整录入', value: 'full' },
+ ];
+ },
+ dispatchTaskEntryModeOptions() {
+ const locked = this.hasMultipleConfiguredGoods(
+ this.dispatchItemForm,
+ this.dispatchItemCargoRows
+ );
+ return [
+ { label: '精简录入', value: 'simple', disabled: locked },
{ label: '完整录入', value: 'full' },
];
},
@@ -5428,9 +5880,11 @@ export default {
row.totalCargoQuantity
);
});
- this.parseJsonArray(this.dispatchRow.dispatchRows).forEach(row => {
- addQuantity(this.getDispatchQuantityUnit(row), 'assigned', row.quantity);
- });
+ if (!this.dispatchRows.length) {
+ this.parseJsonArray(this.dispatchRow.dispatchRows).forEach(row => {
+ addQuantity(this.getDispatchQuantityUnit(row), 'assigned', row.quantity);
+ });
+ }
this.dispatchRows.forEach(row => {
addQuantity(this.getDispatchQuantityUnit(row), 'assigned', row.quantity);
});
@@ -5487,6 +5941,15 @@ export default {
dispatchTransportMode() {
return this.resolveTransportMode(this.dispatchItemForm.transportType);
},
+ dispatchIsRoadTransport() {
+ return this.dispatchTransportMode === 'road';
+ },
+ dispatchIsNonRoadTransport() {
+ return ['water', 'rail', 'air'].includes(this.dispatchTransportMode);
+ },
+ dispatchIsCarrierMode() {
+ return this.dispatchItemForm.carrierType === '承运商';
+ },
dispatchStationMode() {
return ['water', 'rail', 'air'].includes(this.dispatchTransportMode);
},
@@ -5515,6 +5978,45 @@ export default {
const amount = quantity * unitPrice;
return Number.isInteger(amount) ? String(amount) : String(Number(amount.toFixed(2)));
},
+ dispatchItemFreightTotal() {
+ if (this.dispatchItemForm.taskEntryMode !== 'full') {
+ const quantity = Number(this.dispatchItemForm.quantity || 0);
+ const unitPrice = Number(this.dispatchItemForm.unitPrice || 0);
+ const otherFeeTotal = Number(this.dispatchItemForm.otherFeeTotal || 0);
+ const hasFreight = this.dispatchItemForm.unitPrice !== '' && quantity > 0;
+ const hasOtherFee = this.dispatchItemForm.otherFeeTotal !== '';
+ if (!hasFreight && !hasOtherFee) return '';
+ const total = (hasFreight ? quantity * unitPrice : 0) + (hasOtherFee ? otherFeeTotal : 0);
+ return Number.isInteger(total) ? String(total) : String(Number(total.toFixed(2)));
+ }
+ const otherFeeTotal = Number(this.dispatchItemForm.otherFeeTotal || 0);
+ const freightTotal = this.dispatchItemCargoRows.reduce(
+ (total, cargo) => total + Number(this.dispatchCargoFreightAmount(cargo) || 0),
+ 0
+ );
+ const hasOtherFee = this.dispatchItemForm.otherFeeTotal !== '';
+ if (!freightTotal && !hasOtherFee) return '';
+ const total = freightTotal + (hasOtherFee ? otherFeeTotal : 0);
+ return Number.isInteger(total) ? String(total) : String(Number(total.toFixed(2)));
+ },
+ dispatchItemFreightSubtotal() {
+ if (this.dispatchItemForm.taskEntryMode !== 'full') {
+ const quantity = Number(this.dispatchItemForm.quantity || 0);
+ const unitPrice = Number(this.dispatchItemForm.unitPrice || 0);
+ if (!quantity || !unitPrice) return '';
+ const total = quantity * unitPrice;
+ return Number.isInteger(total) ? String(total) : String(Number(total.toFixed(2)));
+ }
+ const total = this.dispatchItemCargoRows.reduce(
+ (sum, cargo) => sum + Number(this.dispatchCargoFreightAmount(cargo) || 0),
+ 0
+ );
+ return total ? (Number.isInteger(total) ? String(total) : String(Number(total.toFixed(2)))) : '';
+ },
+ dispatchItemFreightCurrencyLabel() {
+ const value = this.dispatchItemForm.freightCurrency || 'CNY';
+ return this.currencyRemark(value);
+ },
taskFullFreightTotal() {
const total = this.transportCargoRows.reduce(
(sum, cargo) => sum + Number(this.taskFullFreightAmount(cargo) || 0),
@@ -5526,7 +6028,7 @@ export default {
const currency = this.taskFreightCurrency;
if (!currency) return '';
const option = this.shippingTemplateCurrencyOptions.find(item => item.value === currency);
- return option?.label || currency;
+ return this.currencyName(option?.label || currency);
},
isAdmin() {
const authority = this.userInfo && this.userInfo.authority;
@@ -5663,7 +6165,7 @@ export default {
const currency = this.shippingTemplateFreight.currency;
if (!currency) return '';
const option = this.shippingTemplateCurrencyOptions.find(item => item.value === currency);
- return option?.label || currency;
+ return this.currencyName(option?.label || currency);
},
shippingTemplateRoadFreightTotal() {
const total = this.shippingTemplateFreight.freightItems.reduce(
@@ -5803,6 +6305,16 @@ export default {
}
return cityIndex > -1 ? text.slice(cityStart, cityIndex + 1) : text;
},
+ formatTransportPlanProvinceCityDistrict(value) {
+ const text = String(value || '').trim();
+ if (!text) return '-';
+ const districtMatch = text.match(/(?:自治县|自治旗|林区|矿区|新区|开发区|区|县|旗)/);
+ if (districtMatch) {
+ return text.slice(0, districtMatch.index + districtMatch[0].length);
+ }
+ const cityMatch = text.match(/市/);
+ return cityMatch ? text.slice(0, cityMatch.index + 1) : text;
+ },
hasPermission(code) {
return this.isAdmin || this.validData(this.permission && this.permission[code], false);
},
@@ -6084,17 +6596,178 @@ export default {
);
},
waybillIsRoadTransport(row = {}) {
- return /公路|道路|汽车|陆运/i.test(String(this.waybillTransportMode(row)));
+ return this.waybillTransportModeCode(row) === 'road';
+ },
+ waybillTransportModeCode(row = {}) {
+ const text = [
+ row.transportType,
+ row.transportTypeName,
+ row.transportMode,
+ row.transportModeName,
+ this.getTransportTypeLabel(row.transportType),
+ ]
+ .filter(Boolean)
+ .join(' ')
+ .toLowerCase();
+ if (/公路|道路|汽车|陆运|road|highway/.test(text) || text === 'gl') return 'road';
+ if (/水路|水运|海运|港|water|ship|sea/.test(text) || text === 'sl') return 'water';
+ if (/航空|空运|空港|air|airport/.test(text) || text === 'hk') return 'air';
+ if (/铁路|rail|train/.test(text) || text === 'tl') return 'rail';
+ return '';
+ },
+ waybillIsNonRoadTransport(row = {}) {
+ return ['water', 'rail', 'air'].includes(this.waybillTransportModeCode(row));
+ },
+ waybillIsCarrier(row = {}) {
+ return String(row.carrierTypeName || row.carrierType || '').trim() === '承运商';
+ },
+ waybillVehicleNoLabel(row = {}) {
+ return this.waybillIsRoadTransport(row) ? '车牌号' : '船/航/班列号';
},
waybillUnitPrice(row = {}) {
const price = row.unitPrice || row.price || '';
const unit = row.priceUnit || row.billingUnit || '';
return price === '' ? '' : unit ? `${price}(${unit})` : price;
},
+ waybillCurrencyRemark(row = {}) {
+ const freight = this.parseJsonObject(row.freightJson);
+ const value = row.currency || row.freightCurrency || freight.currency || '';
+ if (!value) return '';
+ const normalized = String(value).toUpperCase();
+ const option = this.shippingTemplateCurrencyOptions.find(item => {
+ const itemValue = String(item.value || '').toUpperCase();
+ return itemValue === normalized || (normalized === 'CNY' && itemValue === 'RMB');
+ });
+ const remark = String(option?.remark || '').trim();
+ if ((normalized === 'CNY' || normalized === 'RMB') && remark === '元') return '¥';
+ return remark;
+ },
+ waybillUnitPriceWithCurrency(row = {}) {
+ const text = this.waybillUnitPrice(row);
+ if (!text) return { value: '-', unit: '' };
+ const match = String(text).match(/^(.*)\((.*)\)$/);
+ const price = match ? match[1] : text;
+ const unit = match ? match[2] : '';
+ return {
+ value: `${this.waybillCurrencyRemark(row)}${price}`,
+ unit,
+ };
+ },
+ waybillAmountWithCurrency(row = {}, prop) {
+ const value = this.formatDetailValue(row, prop);
+ if (value === '-') return { value: '-' };
+ return { value: `${this.waybillCurrencyRemark(row)}${value}` };
+ },
+ normalizeTransportPlanWaybillRow(row = {}, index = 0) {
+ const status = row.businessStatus || row.status || row.waybillStatus || '';
+ const statusMap = {
+ draft: ['草稿', 'is-draft'],
+ pending: ['待执行', 'is-pending'],
+ waiting: ['待执行', 'is-pending'],
+ processing: ['进行中', 'is-processing'],
+ running: ['进行中', 'is-processing'],
+ completed: ['已完成', 'is-completed'],
+ cancelled: ['已取消', 'is-cancelled'],
+ };
+ const statusItem = statusMap[status] || [
+ row.businessStatusName || row.statusName || status || '-',
+ '',
+ ];
+ return {
+ ...row,
+ _detailIndex: index,
+ vehicleNo: row.vehicleNo || row.vehicleNumber || row.flightNo || row.shipNo || row.trainNo || '',
+ driverName: row.driverName || row.driver || '',
+ carrierName: row.carrierName || row.carrier || '',
+ transportType: row.transportTypeName || row.transportType || '',
+ carrierType: row.carrierTypeName || row.carrierType || '',
+ goodsInfo: row.goodsInfo || row.cargoInfo || this.formatDetailValue(row, 'goodsInfo'),
+ departureAddress: row.departureAddress || row.fromAddress || '',
+ statusText: statusItem[0],
+ statusClass: statusItem[1],
+ };
+ },
+ parseTransportPlanWaybillSource(value) {
+ if (Array.isArray(value)) return value;
+ const parsed = this.parseJsonArray(value);
+ if (parsed.length) return parsed;
+ if (!value || typeof value !== 'object') return [];
+ if (Array.isArray(value.records)) return value.records;
+ if (Array.isArray(value.rows)) return value.rows;
+ if (Array.isArray(value.data)) return value.data;
+ if (value.data && typeof value.data === 'object') {
+ return this.parseTransportPlanWaybillSource(value.data);
+ }
+ return [];
+ },
+ openTransportPlanWaybillDetail(row) {
+ if (!row?.id) {
+ this.$message.info('当前运单暂无详情数据');
+ return;
+ }
+ this.$router.push({ path: '/business/waybill-manage', query: { detailId: row.id } });
+ },
+ transportPlanWaybillIndex(index) {
+ return (
+ (this.transportPlanWaybillPage.currentPage - 1) *
+ this.transportPlanWaybillPage.pageSize +
+ index +
+ 1
+ );
+ },
+ handleTransportPlanWaybillCurrentChange(currentPage) {
+ this.transportPlanWaybillPage.currentPage = currentPage;
+ },
+ handleTransportPlanWaybillSizeChange(pageSize) {
+ this.transportPlanWaybillPage.pageSize = pageSize;
+ this.transportPlanWaybillPage.currentPage = 1;
+ },
+ loadTransportPlanDetailTransportTypeOptions() {
+ if (
+ this.transportPlanDetailTransportTypeOptions.length ||
+ this.transportPlanDetailTransportTypeLoading
+ ) {
+ return Promise.resolve(this.transportPlanDetailTransportTypeOptions);
+ }
+ this.transportPlanDetailTransportTypeLoading = true;
+ return getDictionary({ code: 'transport_type' })
+ .then(res => {
+ this.transportPlanDetailTransportTypeOptions = this.normalizeDictOptions(
+ res.data?.data || []
+ );
+ return this.transportPlanDetailTransportTypeOptions;
+ })
+ .finally(() => {
+ this.transportPlanDetailTransportTypeLoading = false;
+ });
+ },
+ ensureTransportPlanDetailTransportTypeName(detail = {}) {
+ if (!this.isTransportPlanPage) {
+ return;
+ }
+ const transportTypes = [
+ detail.transportType,
+ ...this.transportPlanWaybillRows.map(row => row.transportType),
+ ]
+ .map(value => String(value || '').trim())
+ .filter(Boolean);
+ if (transportTypes.some(value => !/[\u4e00-\u9fff]/.test(value))) {
+ this.loadTransportPlanDetailTransportTypeOptions();
+ }
+ },
+ formatTransportPlanDetailTransportType(value) {
+ const transportType = String(value || '').trim();
+ if (!transportType) return '-';
+ const item = this.transportPlanDetailTransportTypeOptions.find(
+ option => String(option.value) === transportType
+ );
+ return item?.label || transportType;
+ },
openDetail(row) {
this.detailBox = true;
this.detailLoading = true;
this.detailRow = { ...row };
+ this.transportPlanWaybillPage.currentPage = 1;
this.waybillProcessDetailTab = 'punch';
this.waybillHasRelatedVoucher = false;
this.waybillVoucherImages = [];
@@ -6107,6 +6780,7 @@ export default {
.then(res => {
const detail = res?.data?.data || res?.data || row;
this.detailRow = detail;
+ this.ensureTransportPlanDetailTransportTypeName(detail);
if (this.isWaybillDetailLayout && detail.contractId) {
return getContractDetail(detail.contractId)
.then(contractRes => {
@@ -6635,6 +7309,24 @@ export default {
}
return true;
},
+ currencyName(value) {
+ const text = String(value || '');
+ if (text.includes(' - ')) return text.slice(text.indexOf(' - ') + 3);
+ return {
+ CNY: '人民币',
+ RMB: '人民币',
+ USD: '美元',
+ EUR: '欧元',
+ JPY: '日元',
+ GBP: '英镑',
+ }[text.toUpperCase()] || text;
+ },
+ currencyRemark(value) {
+ const option = this.shippingTemplateCurrencyOptions.find(
+ item => String(item.value).toUpperCase() === String(value || '').toUpperCase()
+ );
+ return option?.remark || this.currencyName(option?.label || value);
+ },
loadShippingTemplateCurrencyOptions() {
if (this.shippingTemplateCurrencyOptions.length || this.shippingTemplateCurrencyLoading) {
return Promise.resolve(this.shippingTemplateCurrencyOptions);
@@ -6642,13 +7334,47 @@ export default {
this.shippingTemplateCurrencyLoading = true;
return getSystemDictionary({ code: 'currency_type' })
.then(res => {
- this.shippingTemplateCurrencyOptions = this.normalizeDictOptions(res.data?.data || []);
- const cny = this.shippingTemplateCurrencyOptions.find(item =>
- ['CNY', '人民币', 'RMB'].includes(String(item.value).toUpperCase())
+ const currencyNameMap = {
+ CNY: '人民币',
+ RMB: '人民币',
+ USD: '美元',
+ EUR: '欧元',
+ JPY: '日元',
+ GBP: '英镑',
+ };
+ this.shippingTemplateCurrencyOptions = this.normalizeDictOptions(
+ res.data?.data || []
+ ).map(item => {
+ const code = String(item.value || '').toUpperCase();
+ return {
+ ...item,
+ label: `${code} - ${currencyNameMap[code] || item.label || item.value}`,
+ };
+ });
+ const resolveCurrencyValue = value => {
+ const normalized = String(value || '').toUpperCase();
+ const exact = this.shippingTemplateCurrencyOptions.find(
+ item => String(item.value).toUpperCase() === normalized
+ );
+ if (exact) return exact.value;
+ if (normalized === 'CNY') {
+ const rmb = this.shippingTemplateCurrencyOptions.find(
+ item => String(item.value).toUpperCase() === 'RMB'
+ );
+ return rmb?.value || value;
+ }
+ return value;
+ };
+ this.dispatchItemForm.freightCurrency = resolveCurrencyValue(
+ this.dispatchItemForm.freightCurrency
+ );
+ this.taskFreightCurrency = resolveCurrencyValue(this.taskFreightCurrency);
+ this.shippingTemplateFreight.currency = resolveCurrencyValue(
+ this.shippingTemplateFreight.currency
);
if (!this.shippingTemplateFreight.currency) {
this.shippingTemplateFreight.currency =
- (cny || this.shippingTemplateCurrencyOptions[0])?.value || '';
+ this.shippingTemplateCurrencyOptions[0]?.value || '';
}
return this.shippingTemplateCurrencyOptions;
})
@@ -7458,6 +8184,7 @@ export default {
this.attachmentRows = this.parseJsonArray(plan.attachmentsJson);
this.form.attachmentsJson = JSON.stringify(this.attachmentRows);
const goodsRows = this.parseJsonArray(this.form.goodsJson);
+ this.form.taskEntryMode = goodsRows.length > 1 ? 'full' : 'simple';
const firstGoods = goodsRows[0] || {};
[
'cargoType',
@@ -7503,6 +8230,9 @@ export default {
const goodsInfo = goodsRows[0] || {};
this.form.taskEntryMode =
this.form.taskEntryMode || taskInfo.taskEntryMode || carrierInfo.taskEntryMode || 'simple';
+ if (this.hasMultipleConfiguredGoods(this.form, goodsRows)) {
+ this.form.taskEntryMode = 'full';
+ }
this.form.carrierType =
this.form.carrierType ||
taskInfo.carrierType ||
@@ -7602,11 +8332,30 @@ export default {
}
},
handleTaskEntryModeChange(value) {
+ if (value === 'simple' && this.hasMultipleConfiguredGoods(this.form, this.transportCargoRows)) {
+ this.form.taskEntryMode = 'full';
+ this.$message.warning('当前配置了多条货物信息,不能切换为精简录入');
+ return;
+ }
this.form.taskEntryMode = value || 'simple';
if (this.form.taskEntryMode === 'full') {
this.initTaskFullCargoRows(this.parseJsonArray(this.form.goodsJson));
}
},
+ handleDispatchTaskEntryModeChange(value) {
+ if (
+ value === 'simple' &&
+ this.hasMultipleConfiguredGoods(this.dispatchItemForm, this.dispatchItemCargoRows)
+ ) {
+ this.dispatchItemForm.taskEntryMode = 'full';
+ this.$message.warning('当前配置了多条货物信息,不能切换为精简录入');
+ return;
+ }
+ this.dispatchItemForm.taskEntryMode = value || 'simple';
+ if (this.dispatchItemForm.taskEntryMode === 'full' && !this.dispatchItemCargoRows.length) {
+ this.dispatchItemCargoRows = [this.normalizeTransportCargoRow()];
+ }
+ },
handleTaskFullFreightNumberInput(cargo, prop, value) {
const text = String(value || '').replace(/[^\d.]/g, '');
const parts = text.split('.');
@@ -7838,6 +8587,7 @@ export default {
return (list || []).map(item => ({
label: item.dictValue || item.label || item.value || '',
value: item.dictKey || item.value || item.dictValue || '',
+ remark: item.remark || '',
}));
},
handleTaskNumberInput(prop, value) {
@@ -8093,12 +8843,16 @@ export default {
},
initTransportPlanFormRows() {
if (!this.config.enableTransportPlanForm) return;
- this.transportCargoRows = this.parseJsonArray(this.form.goodsJson).map(row =>
+ const goodsRows = this.parseJsonArray(this.form.goodsJson);
+ this.transportCargoRows = goodsRows.map(row =>
this.normalizeTransportCargoRow(row)
);
if (!this.transportCargoRows.length) {
this.transportCargoRows.push(this.normalizeTransportCargoRow());
}
+ if (this.hasMultipleConfiguredGoods(this.form, this.transportCargoRows)) {
+ this.form.taskEntryMode = 'full';
+ }
this.initShippingTemplateFreight();
this.syncTransportCargoJson();
this.syncCurrentContractOption();
@@ -8144,6 +8898,13 @@ export default {
});
return cargo;
},
+ hasMultipleConfiguredGoods(source = {}, rows = []) {
+ const configuredRows = this.parseJsonArray(
+ source?.goodsJson || source?.goodsRows || source?.goodsList
+ );
+ const list = configuredRows.length ? configuredRows : Array.isArray(rows) ? rows : [];
+ return list.length > 1;
+ },
syncTransportCargoJson() {
if (!this.config.enableTransportPlanForm && !this.isTaskFullMode) return;
this.form.goodsJson = JSON.stringify(
@@ -8949,7 +9710,12 @@ export default {
async selectCommonCargoRow(row) {
this.commonCargoSelected = [row];
await this.ensureBillingCargoTypeOptions();
- this.addTransportCargoRow(-1, this.mapCommonCargoRow(row));
+ const cargo = this.mapCommonCargoRow(row);
+ if (this.dispatchItemBox && this.dispatchItemForm.taskEntryMode === 'full') {
+ this.addDispatchCargoRow(this.dispatchItemCargoRows.length - 1, cargo);
+ } else {
+ this.addTransportCargoRow(-1, cargo);
+ }
this.commonCargoBox = false;
},
mapCommonCargoRow(row = {}) {
@@ -9016,7 +9782,13 @@ export default {
this.$message.warning('请先上传货物文件');
return;
}
- this.cargoImportRows.forEach(row => this.addTransportCargoRow(-1, row));
+ this.cargoImportRows.forEach(row => {
+ if (this.dispatchItemBox && this.dispatchItemForm.taskEntryMode === 'full') {
+ this.addDispatchCargoRow(this.dispatchItemCargoRows.length - 1, row);
+ } else {
+ this.addTransportCargoRow(-1, row);
+ }
+ });
this.$message.success(`成功导入${this.cargoImportRows.length}条货物`);
this.closeCargoImportDialog();
},
@@ -9180,14 +9952,14 @@ export default {
isAttachmentImage(row) {
return ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'].includes(this.attachmentExtension(row));
},
- previewAttachment(row) {
+ previewAttachment(row, rows = this.attachmentRows) {
const url = this.attachmentUrl(row);
if (!url) {
this.$message.warning('附件地址为空,无法预览');
return;
}
if (this.isAttachmentImage(row)) {
- this.attachmentImagePreviewUrls = this.attachmentRows
+ this.attachmentImagePreviewUrls = (rows || [])
.filter(item => this.isAttachmentImage(item) && this.attachmentUrl(item))
.map(item => this.attachmentUrl(item));
this.attachmentImagePreviewIndex = Math.max(this.attachmentImagePreviewUrls.indexOf(url), 0);
@@ -10366,9 +11138,15 @@ export default {
return false;
}
const isValidDate = value => {
- if (!/^\d{4}-\d{2}-\d{2}$/.test(value)) return false;
- const date = new Date(`${value}T00:00:00`);
- return !Number.isNaN(date.getTime()) && date.toISOString().slice(0, 10) === value;
+ const match = String(value || '').match(/^(\d{4})-(\d{2})-(\d{2})$/);
+ if (!match) return false;
+ const [, year, month, day] = match;
+ const date = new Date(Number(year), Number(month) - 1, Number(day));
+ return (
+ date.getFullYear() === Number(year) &&
+ date.getMonth() === Number(month) - 1 &&
+ date.getDate() === Number(day)
+ );
};
if (!isValidDate(row.planStartDate) || !isValidDate(row.planEndDate)) {
this.$message.warning(`第${index + 1}行计划日期格式必须为 YYYY-MM-DD`);
@@ -10613,16 +11391,35 @@ export default {
this.dispatchItemBox = false;
this.dispatchItemIndex = -1;
this.dispatchItemForm = defaultDispatchRow();
+ this.dispatchItemCargoRows = [];
this.dispatchItemAttachmentRows = [];
this.selectedDispatchItemAttachmentRows = [];
},
buildDispatchRows(plan = {}) {
- const goodsRows = this.parseJsonArray(plan.goodsJson);
- const sourceRows = goodsRows.length ? goodsRows : [plan];
+ const persistedSources = [
+ plan.dispatchRows,
+ plan.dispatchRowList,
+ plan.dispatchRecords,
+ plan.dispatchRecordList,
+ plan.dispatchList,
+ plan.waybillList,
+ plan.waybillRows,
+ plan.waybills,
+ ];
+ const persistedRows = persistedSources.reduce((result, source) => {
+ const rows = this.parseTransportPlanWaybillSource(source);
+ return result.length ? result : rows;
+ }, []);
+ const goodsRows = this.dispatchGoodsRows(plan.goodsJson);
+ const sourceRows = persistedRows.length ? persistedRows : goodsRows.length ? goodsRows : [plan];
return sourceRows.map((item, index) => this.normalizeDispatchRow(plan, item, index));
},
normalizeDispatchRow(plan = {}, item = {}, index = 0) {
- const cargoInfo = this.formatDispatchCargoInfo(plan, item);
+ const itemGoodsRows = this.dispatchGoodsRows(item.goodsJson);
+ const itemGoods = itemGoodsRows[0] || {};
+ const cargoInfo = this.formatDispatchCargoInfo({}, item);
+ const waybillFallback = this.getDispatchWaybillFallback(plan, item, index);
+ const feeFields = this.getDispatchFeeFields(plan, item);
return {
_key: `${plan.id || 'dispatch'}-${index}-${
item.id || item.cargoName || item.vehicleNo || 'row'
@@ -10638,14 +11435,40 @@ export default {
escortName: item.escortName || '',
escortPhone: item.escortPhone || '',
mileage: item.mileage || '',
- cargoType: item.cargoType || item.goodsType || plan.cargoType || '',
- cargoName: item.cargoName || item.goodsName || plan.cargoName || '',
+ cargoType:
+ itemGoods.cargoType ||
+ itemGoods.goodsType ||
+ item.cargoType ||
+ item.goodsType ||
+ plan.cargoType ||
+ '',
+ cargoName:
+ itemGoods.cargoName ||
+ itemGoods.goodsName ||
+ item.cargoName ||
+ item.goodsName ||
+ plan.cargoName ||
+ '',
cargoInfo,
- quantity: item.quantity || item.cargoQuantity || item.goodsQuantity || '',
- quantityUnit: item.quantityUnit || item.goodsQuantityUnit || item.unit || '吨',
- specification: item.specification || item.spec || '',
- model: item.model || '',
- packageType: item.packageType || item.package || '',
+ quantity:
+ itemGoods.quantity ||
+ itemGoods.cargoQuantity ||
+ itemGoods.goodsQuantity ||
+ item.quantity ||
+ item.cargoQuantity ||
+ item.goodsQuantity ||
+ '',
+ quantityUnit:
+ itemGoods.quantityUnit ||
+ itemGoods.goodsQuantityUnit ||
+ itemGoods.unit ||
+ item.quantityUnit ||
+ item.goodsQuantityUnit ||
+ item.unit ||
+ '吨',
+ specification: item.specification || item.spec || itemGoods.specification || itemGoods.spec || '',
+ model: item.model || itemGoods.model || '',
+ packageType: item.packageType || item.package || itemGoods.packageType || itemGoods.package || '',
estimatedStartTime: item.estimatedStartTime || plan.planStartDate || '',
estimatedEndTime: item.estimatedEndTime || plan.planEndDate || '',
departureAddress: item.departureAddress || plan.departureAddress || '',
@@ -10656,15 +11479,107 @@ export default {
arrivalName: item.arrivalName || plan.arrivalName || '',
arrivalContact: item.arrivalContact || plan.arrivalContact || '',
arrivalPhone: item.arrivalPhone || plan.arrivalPhone || '',
- dispatchUserName: item.dispatchUserName || plan.dispatchUserName || '',
- dispatchTime: item.dispatchTime || plan.dispatchTime || '',
+ ...feeFields,
+ dispatchUserName:
+ item.dispatchUserName ||
+ plan.dispatchUserName ||
+ waybillFallback.createUserName ||
+ item.createUserName ||
+ '',
+ dispatchTime:
+ item.dispatchTime || plan.dispatchTime || waybillFallback.createTime || item.createTime || '',
attachmentsJson: item.attachmentsJson || '',
remark: item.remark || item.taskRemark || plan.remark || '',
};
},
+ getDispatchFeeFields(plan = {}, item = {}) {
+ const freightInfo = this.parseJsonObject(item.freightJson || plan.freightJson);
+ const itemGoods = this.dispatchGoodsRows(item.goodsJson)[0] || {};
+ const unitPrice =
+ item.unitPrice ??
+ itemGoods.unitPrice ??
+ plan.unitPrice ??
+ freightInfo.unitPrice ??
+ freightInfo.freightItems?.[0]?.unitPrice ??
+ '';
+ const otherFeeTotal =
+ item.otherFeeTotal ??
+ plan.otherFeeTotal ??
+ freightInfo.otherFeeTotal ??
+ freightInfo.otherFreightAmount ??
+ '';
+ const quantity = Number(
+ item.quantity ||
+ item.cargoQuantity ||
+ item.goodsQuantity ||
+ itemGoods.quantity ||
+ itemGoods.cargoQuantity ||
+ itemGoods.goodsQuantity ||
+ 0
+ );
+ const calculatedFreight = unitPrice !== '' && quantity ? Number(unitPrice) * quantity : '';
+ const freight =
+ calculatedFreight !== ''
+ ? calculatedFreight
+ : item.freight ??
+ item.freightAmount ??
+ item.transportFee ??
+ plan.freight ??
+ plan.freightAmount ??
+ freightInfo.freightAmount ??
+ freightInfo.transportFee ??
+ '';
+ const freightTotal =
+ freight !== '' || otherFeeTotal !== ''
+ ? Number(freight || 0) + Number(otherFeeTotal || 0)
+ : item.freightTotal ??
+ item.totalFreight ??
+ plan.freightTotal ??
+ plan.totalFreight ??
+ freightInfo.totalFreightAmount ??
+ freightInfo.freightTotal ??
+ '';
+ return { unitPrice, freight, otherFeeTotal, freightTotal };
+ },
+ getDispatchWaybillFallback(plan = {}, item = {}, index = 0) {
+ const sources = [
+ plan.waybillList,
+ plan.waybillRows,
+ plan.waybills,
+ plan.transportPlanWaybills,
+ plan.transportPlanWaybillList,
+ plan.dispatchRecords,
+ plan.dispatchRecordList,
+ plan.dispatchList,
+ ];
+ const rows = sources.reduce((result, source) => {
+ const parsed = this.parseTransportPlanWaybillSource(source);
+ return result.length ? result : parsed;
+ }, []);
+ if (!rows.length) return {};
+ const itemWaybillId = item.waybillId || item.waybillID || item.waybillNo;
+ const matched = itemWaybillId
+ ? rows.find(
+ row =>
+ String(row.id || row.waybillId || row.waybillID || row.waybillNo || '') ===
+ String(itemWaybillId)
+ )
+ : null;
+ const fallback = matched || rows[index] || rows[0] || {};
+ return {
+ createUserName:
+ fallback.createUserName ||
+ fallback.creatorName ||
+ fallback.createdUserName ||
+ fallback.createdByName ||
+ '',
+ createTime: fallback.createTime || fallback.createdAt || '',
+ };
+ },
formatDispatchCargoInfo(plan = {}, item = {}) {
- const goodsRows = this.parseJsonArray(plan.goodsJson);
- const sourceRows = goodsRows.length ? goodsRows : [item];
+ const goodsRows = this.dispatchGoodsRows(plan.goodsJson);
+ const itemGoodsRows = this.dispatchGoodsRows(item.goodsJson);
+ const sourceRows = goodsRows.length ? goodsRows : itemGoodsRows.length ? itemGoodsRows : [item];
const groups = sourceRows.reduce((result, goods = {}) => {
const cargoType =
goods.cargoType ||
@@ -10700,6 +11615,12 @@ export default {
.join(';');
return cargoInfo || plan.goodsInfo || '';
},
+ dispatchGoodsRows(value) {
+ const rows = this.parseJsonArray(value);
+ if (rows.length) return rows;
+ const item = this.parseJsonObject(value);
+ return Object.keys(item).length ? [item] : [];
+ },
openDispatchItemDialog(index = -1, row = defaultDispatchRow()) {
const defaultSummaryItem =
this.dispatchSummaryItems.find(item => item.remaining > 0) || this.dispatchSummaryItems[0];
@@ -10743,6 +11664,27 @@ export default {
...(index >= 0 ? row : {}),
};
this.dispatchItemForm.carrierType = this.dispatchItemForm.carrierType || '承运商';
+ const goodsRows = this.parseJsonArray(this.dispatchItemForm.goodsJson);
+ this.dispatchItemCargoRows = (goodsRows.length ? goodsRows : [this.dispatchItemForm]).map(
+ item => this.normalizeTransportCargoRow(item)
+ );
+ if (!this.dispatchItemCargoRows.length) {
+ this.dispatchItemCargoRows = [this.normalizeTransportCargoRow()];
+ }
+ if (this.hasMultipleConfiguredGoods(this.dispatchItemForm, this.dispatchItemCargoRows)) {
+ this.dispatchItemForm.taskEntryMode = 'full';
+ }
+ const freightInfo = this.parseJsonObject(this.dispatchItemForm.freightJson);
+ this.dispatchItemForm.freightCurrency =
+ freightInfo.currency || this.dispatchItemForm.freightCurrency || 'CNY';
+ this.loadShippingTemplateCurrencyOptions();
+ if (this.dispatchItemForm.taskEntryMode === 'full' && freightInfo.freightItems?.length) {
+ this.dispatchItemCargoRows = this.dispatchItemCargoRows.map((cargo, itemIndex) => ({
+ ...cargo,
+ unitPrice: cargo.unitPrice || freightInfo.freightItems[itemIndex]?.unitPrice || '',
+ priceUnit: cargo.priceUnit || freightInfo.freightItems[itemIndex]?.priceUnit || '元/吨',
+ }));
+ }
this.dispatchItemAttachmentRows = this.parseJsonArray(
index >= 0 ? row.attachmentsJson : this.dispatchRow.attachmentsJson
);
@@ -10809,11 +11751,107 @@ export default {
'';
}
},
+ getDispatchCargoRowOptions(row = {}) {
+ return this.getTaskCargoRowOptions(row);
+ },
+ handleDispatchCargoTypeChange(row, value) {
+ const path = this.normalizeBillingCargoTypePath(value);
+ const cargoType = this.findBillingCargoTypeByPath(path);
+ const labels = this.getBillingCargoTypePathLabels(path);
+ row.cargoTypePath = path;
+ row.cargoType = labels.length ? labels[labels.length - 1] : '';
+ row.cargoTypeCode = cargoType?.cargoCode || cargoType?.code || cargoType?.id || '';
+ row.cargoName = '';
+ row.specification = '';
+ row.model = '';
+ this.loadTaskCargoOptionsByPath(path);
+ },
+ handleDispatchCargoRowNameChange(row, value) {
+ const cargo = this.getDispatchCargoRowOptions(row).find(
+ item => this.formatBillingCargoTypeLabel(item) === value
+ );
+ row.cargoName = value || '';
+ if (cargo) {
+ row.specification = cargo.specification || cargo.spec || row.specification || '';
+ row.model = cargo.model || cargo.modelName || row.model || '';
+ }
+ },
+ handleDispatchCargoQuantityInput(row, value) {
+ const text = String(value || '').replace(/[^\d.]/g, '');
+ const parts = text.split('.');
+ row.quantity =
+ parts.length > 1 ? `${parts[0]}.${parts.slice(1).join('').slice(0, 3)}` : parts[0];
+ },
+ handleDispatchCargoNumberInput(row, prop, value) {
+ this.handleTaskFullFreightNumberInput(row, prop, value);
+ },
+ handleDispatchNumberInput(prop, value) {
+ const text = String(value || '').replace(/[^\d.]/g, '');
+ const parts = text.split('.');
+ this.dispatchItemForm[prop] =
+ parts.length > 1 ? `${parts[0]}.${parts.slice(1).join('').slice(0, 2)}` : parts[0];
+ },
+ addDispatchCargoRow(index = -1) {
+ const nextRow = this.normalizeTransportCargoRow({ priceUnit: '元/吨' });
+ if (index > -1) this.dispatchItemCargoRows.splice(index + 1, 0, nextRow);
+ else this.dispatchItemCargoRows.push(nextRow);
+ },
+ removeDispatchCargoRow(index) {
+ this.dispatchItemCargoRows.splice(index, 1);
+ if (!this.dispatchItemCargoRows.length) {
+ this.dispatchItemCargoRows.push(this.normalizeTransportCargoRow());
+ }
+ },
+ dispatchCargoFreightAmount(cargo = {}) {
+ const quantity = Number(cargo.quantity || 0);
+ const unitPrice = Number(cargo.unitPrice || 0);
+ if (!quantity || !unitPrice) return '';
+ const amount = quantity * unitPrice;
+ return Number.isInteger(amount) ? String(amount) : String(Number(amount.toFixed(2)));
+ },
saveDispatchItem() {
+ const goodsRows =
+ this.dispatchItemForm.taskEntryMode === 'full'
+ ? this.dispatchItemCargoRows.map(row => this.normalizeTransportCargoRow(row))
+ : [
+ this.normalizeTransportCargoRow({
+ ...this.dispatchItemForm,
+ remark: this.dispatchItemForm.remark || '',
+ }),
+ ];
+ const firstGoods = goodsRows[0] || {};
const nextRow = {
...this.dispatchItemForm,
+ goodsJson: JSON.stringify(goodsRows),
+ cargoType: firstGoods.cargoType || this.dispatchItemForm.cargoType || '',
+ cargoTypeCode: firstGoods.cargoTypeCode || this.dispatchItemForm.cargoTypeCode || '',
+ cargoTypePath: firstGoods.cargoTypePath || this.dispatchItemForm.cargoTypePath || [],
+ cargoName: firstGoods.cargoName || this.dispatchItemForm.cargoName || '',
+ quantity: firstGoods.quantity || this.dispatchItemForm.quantity || '',
+ quantityUnit: firstGoods.quantityUnit || this.dispatchItemForm.quantityUnit || '',
+ unitPrice: firstGoods.unitPrice || '',
+ priceUnit: firstGoods.priceUnit || '',
+ freightTotal: this.dispatchItemFreightTotal,
+ freightJson: JSON.stringify({
+ currency: this.dispatchItemForm.freightCurrency || 'CNY',
+ totalFreightAmount: this.dispatchItemFreightSubtotal,
+ otherFreightAmount: this.dispatchItemForm.otherFeeTotal || '',
+ freightItems: goodsRows.map((cargo, index) => ({
+ cargoIndex: index,
+ cargoName: cargo.cargoName || '',
+ cargoType: cargo.cargoType || '',
+ unitPrice: cargo.unitPrice || '',
+ priceUnit: cargo.priceUnit || '',
+ quantity: cargo.quantity || '',
+ quantityUnit: cargo.quantityUnit || '',
+ freightAmount: this.dispatchCargoFreightAmount(cargo),
+ })),
+ }),
attachmentsJson: JSON.stringify(this.dispatchItemAttachmentRows),
};
+ nextRow.cargoInfo = this.formatDispatchCargoInfo({}, nextRow);
+ nextRow.freight = this.getDispatchFeeFields({}, nextRow).freight;
+ nextRow.otherFeeTotal = this.dispatchItemForm.otherFeeTotal || '';
const quantityUnit = this.getDispatchQuantityUnit(nextRow);
const otherDispatchedQuantity = this.dispatchRows.reduce((total, row, index) => {
if (
@@ -10852,12 +11890,14 @@ export default {
this.dispatchItemBox = false;
this.dispatchItemIndex = -1;
this.dispatchItemForm = defaultDispatchRow();
+ this.dispatchItemCargoRows = [];
this.dispatchItemAttachmentRows = [];
this.selectedDispatchItemAttachmentRows = [];
},
resetDispatchItemDialog() {
this.dispatchItemIndex = -1;
this.dispatchItemForm = defaultDispatchRow();
+ this.dispatchItemCargoRows = [];
this.dispatchItemAttachmentRows = [];
this.selectedDispatchItemAttachmentRows = [];
},
@@ -10893,17 +11933,19 @@ export default {
taskRemark: row.remark || '',
estimatedStartTime: this.formatDispatchDate(row.estimatedStartTime),
estimatedEndTime: this.formatDispatchDate(row.estimatedEndTime),
- goodsJson: JSON.stringify([
- {
- cargoName: row.cargoName,
- cargoType: row.cargoType,
- quantity: row.quantity,
- quantityUnit: row.quantityUnit,
- specification: row.specification,
- model: row.model,
- packageType: row.packageType,
- },
- ]),
+ goodsJson:
+ row.goodsJson ||
+ JSON.stringify([
+ {
+ cargoName: row.cargoName,
+ cargoType: row.cargoType,
+ quantity: row.quantity,
+ quantityUnit: row.quantityUnit,
+ specification: row.specification,
+ model: row.model,
+ packageType: row.packageType,
+ },
+ ]),
})),
};
},
@@ -10918,6 +11960,13 @@ export default {
return false;
}
for (const [index, row] of this.dispatchRows.entries()) {
+ if (row.taskEntryMode === 'full') {
+ const goodsRows = this.parseJsonArray(row.goodsJson);
+ if (!goodsRows.length || goodsRows.some(item => !item.cargoName || !item.cargoType || !item.quantity || !item.quantityUnit)) {
+ this.$message.warning(`第${index + 1}条调度明细的完整录入货物信息不完整`);
+ return false;
+ }
+ }
const invalidField = [
[row.departurePhone, '发货联系方式'],
[row.arrivalPhone, '收货联系方式'],
@@ -10932,6 +11981,8 @@ export default {
if (mode === 'draft') return true;
const invalidRow = this.dispatchRows.find(row => {
const selfTransport = row.carrierType !== '承运商';
+ const carrierPlatform = row.carrierType === '网货平台';
+ const optionalSelfEscort = row.taskEntryMode === 'full' && row.carrierType === '自运';
return (
!row.transportType ||
!row.cargoName ||
@@ -10946,10 +11997,10 @@ export default {
(selfTransport &&
(!row.driverName ||
!row.driverPhone ||
- !row.trailerVehicleNo ||
- !row.escortName ||
- !row.escortPhone ||
- !row.mileage))
+ !row.mileage ||
+ (!optionalSelfEscort &&
+ (carrierPlatform || row.taskEntryMode !== 'full') &&
+ (!row.trailerVehicleNo || !row.escortName || !row.escortPhone))))
);
});
if (invalidRow) {
@@ -11916,7 +12967,15 @@ export default {
&__dispatch-dialog {
:deep(.el-dialog__header) {
- display: none;
+ display: block;
+ height: 0;
+ padding: 0;
+ }
+
+ :deep(.el-dialog__headerbtn) {
+ z-index: 2;
+ top: 12px;
+ right: 12px;
}
:deep(.el-dialog__body) {
@@ -12189,6 +13248,28 @@ export default {
&__dispatch-item-form {
width: 100%;
+ padding: 16px;
+ border: 1px solid #ebeef5;
+ background: #fff;
+
+ > .dialog-section-title {
+ margin-top: 16px;
+
+ &:first-child {
+ margin-top: 0;
+ }
+ }
+
+ > .business-crud-page__dispatch-shipping-form,
+ > .business-crud-page__cargo-wrap,
+ > .business-crud-page__dispatch-item-grid,
+ > .business-crud-page__attachment {
+ background: #fff;
+ }
+
+ > .business-crud-page__attachment {
+ padding: 0 0 4px;
+ }
}
&__dispatch-unit-select {
@@ -12218,6 +13299,14 @@ export default {
&--compact {
grid-template-columns: repeat(4, minmax(0, 1fr));
}
+
+ &--full {
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ }
+
+ &--carrier {
+ grid-template-columns: repeat(4, minmax(0, 1fr));
+ }
}
&__dispatch-item-span-2 {
@@ -12228,6 +13317,10 @@ export default {
grid-column: span 3;
}
+ &__dispatch-item-span-4 {
+ grid-column: 1 / -1;
+ }
+
&__dispatch-item-carrier-type {
grid-column: 1 / -1;
width: calc((100% - 72px) / 4);
@@ -12416,6 +13509,259 @@ export default {
}
}
+ &__detail-dialog.is-transport-plan-detail {
+ :deep(.el-dialog) {
+ max-width: none;
+ margin: 0 auto;
+ }
+
+ :deep(.el-dialog__header) {
+ display: none;
+ }
+
+ :deep(.el-dialog__body) {
+ padding: 8px;
+ background: #f5f6f8;
+ }
+ }
+
+ &__transport-plan-detail-head,
+ &__transport-plan-waybill-card {
+ border: 1px solid #dfe3e8;
+ background: #fff;
+ }
+
+ &__transport-plan-detail-head {
+ padding: 20px 40px 24px;
+ }
+
+ &__transport-plan-detail-title-row {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ margin-bottom: 18px;
+
+ h2 {
+ margin: 0;
+ color: #303133;
+ font-size: 20px;
+ font-weight: 600;
+
+ span {
+ margin: 0 8px;
+ color: #303133;
+ }
+ }
+
+ :deep(.el-tag) {
+ height: 36px;
+ padding: 0 14px;
+ border: 0;
+ border-radius: 8px;
+ font-size: 14px;
+ line-height: 36px;
+ }
+
+ :deep(.is-dispatching) {
+ color: #67c23a;
+ background: #e1f3d8;
+ }
+ }
+
+ &__transport-plan-detail-overview {
+ display: grid;
+ grid-template-columns: minmax(540px, 1fr) minmax(560px, 1.2fr);
+ gap: 22px;
+ }
+
+ &__transport-plan-detail-fields {
+ display: grid;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ gap: 16px 32px;
+ align-content: start;
+ }
+
+ &__transport-plan-detail-field {
+ display: flex;
+ min-width: 0;
+ flex-direction: column;
+ gap: 8px;
+
+ span {
+ color: #606266;
+ font-size: 14px;
+ }
+
+ strong {
+ overflow: hidden;
+ color: #409eff;
+ font-size: 16px;
+ font-weight: 500;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+
+ strong.is-normal {
+ color: #303133;
+ font-weight: 400;
+ white-space: normal;
+ word-break: break-word;
+ }
+
+ &.is-attachments {
+ grid-column: 1 / -1;
+ }
+ }
+
+ &__transport-plan-detail-attachments {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 14px 32px;
+
+ .el-link,
+ span {
+ font-size: 16px;
+ }
+ }
+
+ &__transport-plan-detail-route {
+ display: flex;
+ min-width: 0;
+ align-items: flex-start;
+ padding: 12px 0 0;
+ }
+
+ &__transport-plan-detail-route-point {
+ display: flex;
+ min-width: 0;
+ align-items: flex-start;
+ gap: 12px;
+
+ b {
+ display: inline-flex;
+ flex: 0 0 auto;
+ width: 36px;
+ height: 36px;
+ align-items: center;
+ justify-content: center;
+ color: #fff;
+ border-radius: 8px;
+ background: #409eff;
+ font-size: 14px;
+
+ &.is-end {
+ background: #e6a23c;
+ }
+ }
+
+ div {
+ min-width: 0;
+ text-align: center;
+ }
+
+ strong {
+ display: block;
+ color: #303133;
+ font-size: 18px;
+ white-space: nowrap;
+ }
+
+ p {
+ max-width: 280px;
+ margin: 10px 0 0;
+ color: #303133;
+ font-size: 14px;
+ line-height: 1.45;
+ word-break: break-word;
+ }
+ }
+
+ &__transport-plan-detail-route-line {
+ flex: 1;
+ min-width: 56px;
+ height: 22px;
+ margin: 0 14px;
+ border-bottom: 3px solid #303133;
+ }
+
+ &__transport-plan-waybill-card {
+ margin-top: 8px;
+ padding: 16px 14px 12px;
+ }
+
+ &__transport-plan-waybill-heading {
+ display: flex;
+ align-items: baseline;
+ flex-wrap: wrap;
+ gap: 0;
+ margin: 0 0 14px;
+ padding-left: 16px;
+
+ h3 {
+ margin: 0 30px 0 0;
+ color: #303133;
+ font-size: 18px;
+ }
+
+ span {
+ color: #303133;
+ font-size: 16px;
+ }
+
+ em {
+ color: #409eff;
+ font-style: normal;
+ }
+ }
+
+ &__transport-plan-waybill-table {
+ width: 100%;
+
+ :deep(.el-table__header th) {
+ color: #303133;
+ background: #f7f7f7;
+ font-size: 14px;
+ font-weight: 600;
+ }
+
+ :deep(.el-table__cell) {
+ height: 52px;
+ color: #303133;
+ border-color: #eff1f7;
+ font-size: 14px;
+ }
+
+ :deep(.el-table__body tr:nth-child(even) > td) {
+ background: #fafafa;
+ }
+
+ :deep(.el-table__fixed-right) {
+ background: #fff;
+ }
+ }
+
+ &__transport-plan-waybill-pagination {
+ display: flex;
+ justify-content: flex-end;
+ padding-top: 12px;
+ }
+
+ &__transport-plan-waybill-status {
+ &.is-processing {
+ color: #67c23a;
+ }
+
+ &.is-completed {
+ color: #409eff;
+ }
+
+ &.is-pending,
+ &.is-draft,
+ &.is-cancelled {
+ color: #606266;
+ }
+ }
+
&__waybill-detail-summary {
:deep(.el-card__body) {
padding: 18px 22px;
@@ -12584,7 +13930,7 @@ export default {
&__waybill-fee-grid {
grid-template-columns: repeat(4, minmax(0, 1fr));
- .is-emphasis {
+ .business-crud-page__waybill-fee-value {
color: #409eff;
font-size: 16px;
}
diff --git a/src/views/business/components/waybill-import-dialog.vue b/src/views/business/components/waybill-import-dialog.vue
index 4018a11..7d3461e 100644
--- a/src/views/business/components/waybill-import-dialog.vue
+++ b/src/views/business/components/waybill-import-dialog.vue
@@ -39,7 +39,7 @@
关闭保存草稿确认导入
- handleEditorChange(column, row, value)"> handleEditorChange(column, row, value)" /> visible && loadCommonCargoOptions(row)" @change="value => handleEditorChange(column, row, value)"> updateEditorValue(column, row, value)" />{{ formatCell(row, column) }}保存取消编辑删除
+ handleEditorChange(column, row, value)"> handleEditorChange(column, row, value)" /> visible && loadCommonCargoOptions(row)" @change="value => handleEditorChange(column, row, value)"> updateEditorValue(column, row, value)" />{{ formatCell(row, column) }}保存取消编辑删除
@@ -84,8 +84,8 @@ const detailColumns = [
{ prop: 'arrivalAddress', label: '到货地址', editor: 'textarea', minWidth: 260 },
{ prop: 'arrivalContact', label: '收货联系人', editor: 'input', minWidth: 140 },
{ prop: 'arrivalPhone', label: '收货联系人电话', editor: 'input', minWidth: 160 },
- { prop: 'startDate', label: '开始时间', editor: 'datetime', minWidth: 190 },
- { prop: 'endDate', label: '结束时间', editor: 'datetime', minWidth: 190 },
+ { prop: 'startDate', label: '开始时间', editor: 'date', minWidth: 150 },
+ { prop: 'endDate', label: '结束时间', editor: 'date', minWidth: 150 },
{ prop: 'unitPrice', label: '单价', editor: 'number', minWidth: 130 },
{ prop: 'freight', label: '运费', editor: 'number', minWidth: 130 },
{ prop: 'otherFeeTotal', label: '其他费用合计', editor: 'number', minWidth: 150 },
@@ -256,9 +256,17 @@ const fileChange = async (file, list) => {
const XLSX = await import('xlsx');
const workbook = XLSX.read(await file.raw.arrayBuffer(), { type: 'array', cellDates: true });
const source = XLSX.utils.sheet_to_json(workbook.Sheets[workbook.SheetNames[0]], { defval: '' }).map(item => Object.fromEntries(Object.entries(item).map(([key, value]) => [key.replace(/^\*/, ''), value])));
- const keyMap = { '原始单号': 'originalNo', '车牌号/航班号/船号/班列号': 'vehicleNo', '司机/船长': 'driverName', '运输类型': 'transportType', '货物名称': 'cargoName', '货物类型': 'cargoType', '重量': 'quantity', '发货地址': 'departureAddress', '发货联系人': 'departureContact', '发货联系人电话': 'departurePhone', '到货地址': 'arrivalAddress', '收货联系人': 'arrivalContact', '收货联系人电话': 'arrivalPhone', '开始时间': 'startDate', '结束时间': 'endDate', '单价': 'unitPrice', '运费': 'freight', '其他费用合计': 'otherFeeTotal', '运费合计': 'freightTotal', '备注': 'remark' };
+ const keyMap = { originalNo: ['原始单号'], vehicleNo: ['车牌号/航班号/船号/班列号'], driverName: ['司机/船长'], transportType: ['运输类型'], cargoName: ['货物名称'], cargoType: ['货物类型'], quantity: ['重量'], departureAddress: ['发货地址'], departureContact: ['发货联系人'], departurePhone: ['发货联系人电话'], arrivalAddress: ['到货地址'], arrivalContact: ['到货联系人', '收货联系人'], arrivalPhone: ['收货联系人电话'], startDate: ['开始时间'], endDate: ['结束时间'], unitPrice: ['单价'], freight: ['运费'], otherFeeTotal: ['其他费用合计'], freightTotal: ['运费合计'], remark: ['备注'] };
+ const normalizeImportDate = value => {
+ if (value instanceof Date && !Number.isNaN(value.getTime())) {
+ const pad = number => String(number).padStart(2, '0');
+ return `${value.getFullYear()}-${pad(value.getMonth() + 1)}-${pad(value.getDate())}`;
+ }
+ const text = String(value ?? '').trim();
+ return text.slice(0, 10);
+ };
const count = new Map();
- rows.value = source.map((item, index) => { const row = { _key: `${Date.now()}-${index}`, batchNo: '', ...item }; Object.entries(keyMap).forEach(([label, key]) => { row[key] = item[label] ?? item[key] ?? ''; }); const duplicateKey = JSON.stringify(Object.values(keyMap).map(key => row[key])); count.set(duplicateKey, (count.get(duplicateKey) || 0) + 1); row._duplicateKey = duplicateKey; return row; });
+ rows.value = source.map((item, index) => { const row = { _key: `${Date.now()}-${index}`, batchNo: '', ...item }; Object.entries(keyMap).forEach(([key, labels]) => { row[key] = labels.map(label => item[label]).find(value => String(value ?? '').trim()) ?? item[key] ?? ''; }); row.startDate = normalizeImportDate(row.startDate); row.endDate = normalizeImportDate(row.endDate); const duplicateKey = JSON.stringify(Object.keys(keyMap).map(key => row[key])); count.set(duplicateKey, (count.get(duplicateKey) || 0) + 1); row._duplicateKey = duplicateKey; return row; });
rows.value.forEach(row => { row._duplicate = count.get(row._duplicateKey) > 1; });
};
const fileRemove = () => { files.value = []; form.file = null; };
diff --git a/src/views/business/loading-manage.vue b/src/views/business/loading-manage.vue
index 6fa95ca..c2efde2 100644
--- a/src/views/business/loading-manage.vue
+++ b/src/views/business/loading-manage.vue
@@ -263,7 +263,19 @@
- {{ splitText(row.loadingSubNos).join(',') || '-' }}
+
+
+ {{ waybill }}
+ ,
+
+
+ -
@@ -285,17 +297,23 @@
min-width="180"
>
- {{ formatRouteAddress(row, 'departure') }}
+
+ {{ formatRouteAddress(row, 'departure') }}
+
- {{ formatRouteAddress(row, 'transit') }}
+
+ {{ formatRouteAddress(row, 'transit') }}
+
- {{ formatRouteAddress(row, 'arrival') }}
+
+ {{ formatRouteAddress(row, 'arrival') }}
+
@@ -1259,8 +1277,42 @@ export default {
.map(item => item.trim())
.filter(Boolean);
},
+ findWaybillId(row, waybillNo) {
+ const rows = this.splitText(row.loadingSubNos);
+ const index = rows.findIndex(item => item === waybillNo);
+ const linkedRows = this.parseJsonArray(row.waybillList || row.waybillRows || row.waybills);
+ if (linkedRows[index]?.id || linkedRows[index]?.waybillId) {
+ return linkedRows[index].id || linkedRows[index].waybillId;
+ }
+ const ids = this.parseJsonArray(row.waybillIdsJson);
+ return ids[index] || '';
+ },
+ async openWaybillDetail(row = {}) {
+ let id = row.id || row.waybillId || '';
+ if (!id && row.waybillNo) {
+ try {
+ const res = await getWaybillList(1, 1, { waybillNo: row.waybillNo });
+ const page = unwrapPage(res);
+ id = page.records?.[0]?.id || page[0]?.id || '';
+ } catch (error) {
+ id = '';
+ }
+ }
+ if (!id) {
+ ElMessage.info('当前子运单暂无详情数据');
+ return;
+ }
+ this.$router.push({ path: '/business/waybill-manage', query: { detailId: id } });
+ },
formatRouteAddress(row, type) {
const prefix = type === 'departure' ? 'departure' : type === 'arrival' ? 'arrival' : 'transit';
+ if (type !== 'transit') {
+ const address = row[`${prefix}Address`];
+ const region = this.routeProvinceName(row, prefix);
+ const district = row[`${prefix}DistrictName`];
+ const source = this.mergeRouteAddressParts(region, district, address);
+ return this.formatProvinceCityDistrict(source) || '-';
+ }
const values = [
row[`${prefix}RegionName`],
row[`${prefix}DistrictName`],
@@ -1268,24 +1320,50 @@ export default {
]
.filter(Boolean)
.flatMap(value => this.splitText(value))
- .map(value => this.formatCityDistrict(value))
+ .map(value => this.formatProvinceCityDistrict(value))
.filter(Boolean);
return [...new Set(values)].join(',') || '-';
},
- formatCityDistrict(value) {
+ formatFullRouteAddress(row, type) {
+ const prefix = type === 'departure' ? 'departure' : type === 'arrival' ? 'arrival' : 'transit';
+ if (type !== 'transit') {
+ const address = row[`${prefix}Address`];
+ const region = this.routeProvinceName(row, prefix);
+ const district = row[`${prefix}DistrictName`];
+ return this.mergeRouteAddressParts(region, district, address) || '-';
+ }
+ const values = [row.transitRegionName, row.transitDistrictName, row.transitAddress]
+ .filter(Boolean)
+ .flatMap(value => this.splitText(value));
+ return [...new Set(values)].join(',') || '-';
+ },
+ mergeRouteAddressParts(region, district, address) {
+ const values = [region, district, address].map(value => String(value || '').trim()).filter(Boolean);
+ if (!values.length) return '';
+ return values.reduce((result, value) => {
+ if (!result) return value;
+ if (result.includes(value) || value.includes(result)) return result.length >= value.length ? result : value;
+ return `${result}${value}`;
+ }, '');
+ },
+ routeProvinceName(row, prefix) {
+ return [
+ row[`${prefix}ProvinceName`],
+ row[`${prefix}Province`],
+ row[`${prefix}RegionName`],
+ row[`${prefix}Region`],
+ row[`${prefix}Name`],
+ row[`${prefix}AdministrativeRegionName`],
+ row[`${prefix}AddressRegionName`],
+ ].find(value => String(value || '').trim()) || '';
+ },
+ formatProvinceCityDistrict(value) {
const text = String(value || '').trim();
if (!text) return '';
- const cityIndex = text.indexOf('市');
- const provinceIndex = Math.max(text.lastIndexOf('省', cityIndex), text.lastIndexOf('自治区', cityIndex));
- const cityStart = provinceIndex > -1 ? provinceIndex + 1 : 0;
- const districtStart = cityIndex > -1 ? cityIndex + 1 : cityStart;
- const districtMatch = text
- .slice(districtStart)
- .match(/(?:自治县|自治旗|林区|矿区|新区|开发区|区|县|旗)/);
- if (districtMatch) {
- return text.slice(cityStart, districtStart + districtMatch.index + districtMatch[0].length);
- }
- return cityIndex > -1 ? text.slice(cityStart, cityIndex + 1) : text;
+ const districtMatch = text.match(/(?:自治县|自治旗|林区|矿区|新区|开发区|区|县|旗)/);
+ if (districtMatch) return text.slice(0, districtMatch.index + districtMatch[0].length);
+ const cityMatch = text.match(/市/);
+ return cityMatch ? text.slice(0, cityMatch.index + 1) : text;
},
rowActions(row) {
const status = row.businessStatus;
@@ -2125,9 +2203,17 @@ export default {
.loading-manage-page__sub-nos {
display: block;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
+ line-height: 20px;
+ white-space: normal;
+ overflow-wrap: anywhere;
+ word-break: break-word;
+ }
+
+ .loading-manage-page__sub-no-link {
+ display: inline;
+ white-space: normal;
+ line-height: 20px;
+ text-align: left;
}
.loading-manage-page__address-cell {
diff --git a/src/views/business/master-order.vue b/src/views/business/master-order.vue
index f6bd45d..2ad14e6 100644
--- a/src/views/business/master-order.vue
+++ b/src/views/business/master-order.vue
@@ -376,8 +376,13 @@ export default {
: 0;
},
async download() {
- const blob = await api.exportList(this.query);
- const url = URL.createObjectURL(new Blob([blob.data || blob]));
+ const response = await api.exportList(this.query);
+ const blob = response.data || response;
+ if (!(blob instanceof Blob) || !blob.size) {
+ this.$message.error('导出失败,未生成有效文件');
+ return;
+ }
+ const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = '总单运单明细.xlsx';
diff --git a/src/views/vehicle/credit-score-quantification.vue b/src/views/vehicle/credit-score-quantification.vue
index 3ef70c2..3a856e9 100644
--- a/src/views/vehicle/credit-score-quantification.vue
+++ b/src/views/vehicle/credit-score-quantification.vue
@@ -218,11 +218,11 @@
-
+