✨ 业务数量单位改走计量单位,并放开运输计划日期必填
发货模板、运输计划、运单与总单调度从计量单位取 unitName;修复调度提交顶层单位为空,并取消计划开始/结束日期必填。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -2,6 +2,9 @@ const dimensionOptions = [
|
||||
{ label: '重量', value: '重量' },
|
||||
{ label: '体积', value: '体积' },
|
||||
{ label: '数量', value: '数量' },
|
||||
{ label: '整车', value: '整车' },
|
||||
{ label: '里程', value: '里程' },
|
||||
{ label: '吨·公里', value: '吨·公里' },
|
||||
];
|
||||
|
||||
const statusOptions = [
|
||||
|
||||
@@ -400,7 +400,6 @@ export const option = {
|
||||
span: 6,
|
||||
order: 350,
|
||||
minWidth: 150,
|
||||
rules: [{ required: true, message: '请选择计划开始日期', trigger: 'change' }],
|
||||
},
|
||||
{
|
||||
label: '计划结束日期',
|
||||
@@ -411,7 +410,6 @@ export const option = {
|
||||
span: 6,
|
||||
order: 340,
|
||||
minWidth: 150,
|
||||
rules: [{ required: true, message: '请选择计划结束日期', trigger: 'change' }],
|
||||
},
|
||||
{
|
||||
label: '数据来源',
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import { getList as getMeasurementUnitList } from '@/api/base/measurement-unit';
|
||||
|
||||
/**
|
||||
* 加载启用状态的计量单位选项,展示/取值均为「计量单位」字段(unitName)。
|
||||
* @returns {Promise<Array<{ label: string, value: string, dimension: string, id: string|number }>>}
|
||||
*/
|
||||
export const loadMeasurementUnitOptions = () => {
|
||||
return getMeasurementUnitList(1, 9999, { status: 1 }).then(res => {
|
||||
const data = res?.data?.data || res?.data || {};
|
||||
const records = Array.isArray(data) ? data : data.records || [];
|
||||
const names = new Set();
|
||||
return records
|
||||
.filter(item => item.status === undefined || item.status === null || Number(item.status) === 1)
|
||||
.map(item => {
|
||||
const name = String(item.unitName || '').trim();
|
||||
if (!name || names.has(name)) return null;
|
||||
names.add(name);
|
||||
return {
|
||||
id: item.id,
|
||||
label: name,
|
||||
value: name,
|
||||
dimension: String(item.dimension || '').trim(),
|
||||
};
|
||||
})
|
||||
.filter(Boolean);
|
||||
});
|
||||
};
|
||||
@@ -386,12 +386,15 @@ const BILLING_ELEMENT_DIMENSION_MAP = {
|
||||
按重量: '重量',
|
||||
按体积: '体积',
|
||||
按数量: '数量',
|
||||
按里程: '里程',
|
||||
'按吨·公里': '吨·公里',
|
||||
'固定金额(整单一口价)': '整车',
|
||||
};
|
||||
const BILLING_UNIT_SELECTABLE_ELEMENTS = Object.keys(BILLING_ELEMENT_DIMENSION_MAP);
|
||||
/** 固定计费单位(不可编辑) */
|
||||
const FIXED_BILLING_UNIT_MAP = {
|
||||
/** 对应维度下无计量单位时的默认计费单位 */
|
||||
const DEFAULT_BILLING_UNIT_MAP = {
|
||||
按里程: '公里',
|
||||
'按吨·公里': '吨公里',
|
||||
'按吨·公里': '吨·公里',
|
||||
'固定金额(整单一口价)': '单',
|
||||
};
|
||||
const defaultRule = () => ({
|
||||
@@ -652,58 +655,61 @@ export default {
|
||||
}),
|
||||
]).finally(() => {
|
||||
this.unitLoading = false;
|
||||
(this.draft.rules || []).forEach(row => this.syncBillingUnit(row));
|
||||
});
|
||||
},
|
||||
measurementDimension(billingElement) {
|
||||
return BILLING_ELEMENT_DIMENSION_MAP[billingElement] || '';
|
||||
},
|
||||
fixedBillingUnit(billingElement) {
|
||||
return FIXED_BILLING_UNIT_MAP[String(billingElement || '').trim()] || '';
|
||||
defaultBillingUnit(billingElement) {
|
||||
return DEFAULT_BILLING_UNIT_MAP[String(billingElement || '').trim()] || '';
|
||||
},
|
||||
canSelectBillingUnit(row) {
|
||||
return BILLING_UNIT_SELECTABLE_ELEMENTS.includes(String(row?.billingElement || '').trim());
|
||||
},
|
||||
billingUnitPlaceholder(row) {
|
||||
if (!row?.billingElement) return '请先选择计费要素';
|
||||
if (this.fixedBillingUnit(row.billingElement)) return this.fixedBillingUnit(row.billingElement);
|
||||
if (!this.canSelectBillingUnit(row)) return '当前计费要素无需选择';
|
||||
return '请选择';
|
||||
},
|
||||
unitOptionsFor(row) {
|
||||
const fixedUnit = this.fixedBillingUnit(row?.billingElement);
|
||||
if (fixedUnit) {
|
||||
return [{ id: fixedUnit, label: fixedUnit, value: fixedUnit }];
|
||||
}
|
||||
if (!this.canSelectBillingUnit(row)) {
|
||||
return [];
|
||||
}
|
||||
const dimension = this.measurementDimension(row?.billingElement);
|
||||
if (dimension) {
|
||||
return this.measurementUnits
|
||||
.filter(item => String(item.dimension || '').trim() === dimension)
|
||||
.map(item => ({
|
||||
id: item.id,
|
||||
label: item.unitName,
|
||||
value: item.unitName,
|
||||
}))
|
||||
.filter(item => item.value);
|
||||
const defaultUnit = this.defaultBillingUnit(row?.billingElement);
|
||||
const options = dimension
|
||||
? this.measurementUnits
|
||||
.filter(item => String(item.dimension || '').trim() === dimension)
|
||||
.map(item => ({
|
||||
id: item.id,
|
||||
label: item.unitName,
|
||||
value: item.unitName,
|
||||
}))
|
||||
.filter(item => item.value)
|
||||
: [];
|
||||
// 对应维度未配置计量单位时,下拉仅展示默认值
|
||||
if (!options.length && defaultUnit) {
|
||||
return [{ id: `default-${defaultUnit}`, label: defaultUnit, value: defaultUnit }];
|
||||
}
|
||||
return [];
|
||||
return options;
|
||||
},
|
||||
syncBillingUnit(row) {
|
||||
const fixedUnit = this.fixedBillingUnit(row?.billingElement);
|
||||
if (fixedUnit) {
|
||||
row.billingUnit = fixedUnit;
|
||||
return;
|
||||
}
|
||||
if (!this.canSelectBillingUnit(row)) {
|
||||
row.billingUnit = '';
|
||||
return;
|
||||
}
|
||||
const options = this.unitOptionsFor(row);
|
||||
if (!options.some(item => String(item.value) === String(row.billingUnit || ''))) {
|
||||
row.billingUnit = '';
|
||||
const defaultUnit = this.defaultBillingUnit(row?.billingElement);
|
||||
const current = String(row.billingUnit || '').trim();
|
||||
if (options.some(item => String(item.value) === current)) {
|
||||
return;
|
||||
}
|
||||
if (defaultUnit && options.some(item => String(item.value) === defaultUnit)) {
|
||||
row.billingUnit = defaultUnit;
|
||||
return;
|
||||
}
|
||||
row.billingUnit = options[0]?.value || defaultUnit || '';
|
||||
},
|
||||
feeTypeKey(row) {
|
||||
const option = this.feeCategories.find(
|
||||
|
||||
@@ -5244,6 +5244,7 @@ import { formatUpdateUserName } from '@/utils/audit';
|
||||
import { getToken, isAdministrator } from '@/utils/auth';
|
||||
import { openImportDialog } from '@/utils/import-excel';
|
||||
import { normalizeMapSearchResults, searchAmapPlaceText } from '@/utils/map-search';
|
||||
import { loadMeasurementUnitOptions } from '@/utils/measurement-unit';
|
||||
import { applyTableMenuWidth } from '@/utils/table-menu';
|
||||
import { downloadFileByUrl, downloadXls } from '@/utils/util';
|
||||
import { isMobile } from '@/utils/validate';
|
||||
@@ -5822,7 +5823,7 @@ export default {
|
||||
billCycleTypeOptions: ['固定截单日', '自然月', '自定义多周期'],
|
||||
reconciliationModeOptions: ['明细逐行核对', '按账单汇总核对', '跳过自动核对'],
|
||||
packageOptions,
|
||||
quantityUnitOptions: ['吨', '千克', '立方米', '件', '车', '箱', '托盘'],
|
||||
quantityUnitOptions: [],
|
||||
attachmentFileTypes: [
|
||||
'pdf',
|
||||
'bmp',
|
||||
@@ -5865,7 +5866,6 @@ export default {
|
||||
this.ensureBillingUnitOptions();
|
||||
}
|
||||
if (this.config.enableTaskInfoForm) {
|
||||
this.loadTaskQuantityUnitOptions();
|
||||
this.loadTaskFeeUnitOptions();
|
||||
this.ensureBillingCargoTypeOptions();
|
||||
}
|
||||
@@ -5886,6 +5886,7 @@ export default {
|
||||
} else {
|
||||
this.consumeTemplateCreatePayload();
|
||||
}
|
||||
this.loadTaskQuantityUnitOptions();
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['permission', 'userInfo']),
|
||||
@@ -7789,8 +7790,6 @@ export default {
|
||||
['contractName', '客户合同'],
|
||||
['planName', '计划名称'],
|
||||
['transportType', '运输方式'],
|
||||
['planStartDate', '计划开始日期'],
|
||||
['planEndDate', '计划结束日期'],
|
||||
['departureAddress', '发货地址'],
|
||||
['arrivalAddress', '收货地址'],
|
||||
];
|
||||
@@ -9270,9 +9269,10 @@ export default {
|
||||
return Promise.resolve(this.taskQuantityUnitOptions);
|
||||
}
|
||||
this.taskQuantityUnitLoading = true;
|
||||
return getDictionary({ code: 'quantity_unit' })
|
||||
.then(res => {
|
||||
this.taskQuantityUnitOptions = this.normalizeDictOptions(res.data.data || []);
|
||||
return loadMeasurementUnitOptions()
|
||||
.then(options => {
|
||||
this.taskQuantityUnitOptions = options;
|
||||
this.quantityUnitOptions = options.map(item => item.value);
|
||||
return this.taskQuantityUnitOptions;
|
||||
})
|
||||
.finally(() => {
|
||||
|
||||
@@ -317,6 +317,7 @@
|
||||
<script>
|
||||
import * as api from '@/api/business/master-order';
|
||||
import { fetchDriversByCarrierOrganizations } from '@/utils/carrier-org-resource';
|
||||
import { loadMeasurementUnitOptions } from '@/utils/measurement-unit';
|
||||
import { isMobile } from '@/utils/validate';
|
||||
|
||||
const unwrapRecords = res => {
|
||||
@@ -347,7 +348,8 @@ export default {
|
||||
carrierLoading: false,
|
||||
driverLoading: false,
|
||||
escortLoading: false,
|
||||
quantityUnitOptions: ['吨', '千克', '立方米', '件', '车', '箱', '托盘'],
|
||||
quantityUnitOptions: [],
|
||||
quantityUnitLoading: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -389,10 +391,24 @@ export default {
|
||||
},
|
||||
},
|
||||
async mounted() {
|
||||
await this.loadQuantityUnitOptions();
|
||||
await Promise.all([this.load(), this.loadCarrierOptions()]);
|
||||
this.applySelfOperatedCarrierDefaults();
|
||||
},
|
||||
methods: {
|
||||
loadQuantityUnitOptions() {
|
||||
if (this.quantityUnitLoading) return this.quantityUnitLoadPromise;
|
||||
this.quantityUnitLoading = true;
|
||||
this.quantityUnitLoadPromise = loadMeasurementUnitOptions()
|
||||
.then(options => {
|
||||
this.quantityUnitOptions = options.map(item => item.value);
|
||||
return this.quantityUnitOptions;
|
||||
})
|
||||
.finally(() => {
|
||||
this.quantityUnitLoading = false;
|
||||
});
|
||||
return this.quantityUnitLoadPromise;
|
||||
},
|
||||
async load() {
|
||||
if (!this.id) return;
|
||||
this.loading = true;
|
||||
|
||||
@@ -597,10 +597,10 @@ import { fallbackPlugin, imagePlugin, officePlugin, pdfPlugin, textPlugin } from
|
||||
import '@open-file-viewer/core/style.css';
|
||||
import pdfWorkerSrc from 'pdfjs-dist/build/pdf.worker.mjs?url';
|
||||
import { mapGetters } from 'vuex';
|
||||
import { loadMeasurementUnitOptions } from '@/utils/measurement-unit';
|
||||
|
||||
const AMAP_KEY = '653b7cf105ad7fb8ec9b2f5198ade315';
|
||||
const AMAP_SECURITY_CODE = '5aab65c632e48ebae0d0e28f5b28e21a';
|
||||
const quantityUnitOptions = ['吨', '件', '方'];
|
||||
const attachmentViewerPlugins = [
|
||||
imagePlugin(),
|
||||
pdfPlugin({ workerSrc: pdfWorkerSrc, useFetchData: true }),
|
||||
@@ -703,10 +703,11 @@ export default {
|
||||
'zip',
|
||||
],
|
||||
transports: ['公路运输', '铁路运输', '水路运输', '航空运输'],
|
||||
quantityUnitOptions,
|
||||
quantityUnitOptions: [],
|
||||
quantityUnitLoading: false,
|
||||
form: {
|
||||
routes: [{ segmentNo: '段1', transportType: '' }],
|
||||
goods: [{ quantityUnit: quantityUnitOptions[0] }],
|
||||
goods: [{ quantityUnit: '吨' }],
|
||||
attachmentsJson: '[]',
|
||||
transportOrganizationType: '多式联运',
|
||||
},
|
||||
@@ -773,6 +774,7 @@ export default {
|
||||
async mounted() {
|
||||
this.loading = true;
|
||||
try {
|
||||
await this.loadQuantityUnitOptions();
|
||||
await Promise.all([this.loadProjects(), this.loadCargoTypeOptions(), this.loadRegionOptions()]);
|
||||
if (this.id) {
|
||||
await this.loadDetail(this.id);
|
||||
@@ -782,6 +784,22 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
defaultQuantityUnit() {
|
||||
return this.quantityUnitOptions[0] || '吨';
|
||||
},
|
||||
loadQuantityUnitOptions() {
|
||||
if (this.quantityUnitLoading) return this.quantityUnitLoadPromise;
|
||||
this.quantityUnitLoading = true;
|
||||
this.quantityUnitLoadPromise = loadMeasurementUnitOptions()
|
||||
.then(options => {
|
||||
this.quantityUnitOptions = options.map(item => item.value);
|
||||
return this.quantityUnitOptions;
|
||||
})
|
||||
.finally(() => {
|
||||
this.quantityUnitLoading = false;
|
||||
});
|
||||
return this.quantityUnitLoadPromise;
|
||||
},
|
||||
responseData(response) {
|
||||
return response?.data?.data || response?.data || response || {};
|
||||
},
|
||||
@@ -958,7 +976,7 @@ export default {
|
||||
finalTransportType: hasFinalSegment
|
||||
? finalSegment.transportType
|
||||
: detail.finalTransportType || '',
|
||||
goods: detail.goods || [{ quantityUnit: quantityUnitOptions[0] }],
|
||||
goods: detail.goods || [{ quantityUnit: this.defaultQuantityUnit() }],
|
||||
attachmentsJson: detail.attachmentsJson || '[]',
|
||||
transportOrganizationType: '多式联运',
|
||||
};
|
||||
@@ -1659,7 +1677,7 @@ export default {
|
||||
'',
|
||||
cargoTypePath,
|
||||
quantity: row.quantity || '',
|
||||
quantityUnit: row.quantityUnit || quantityUnitOptions[0],
|
||||
quantityUnit: row.quantityUnit || this.defaultQuantityUnit(),
|
||||
packageType: row.packageType || row.package || '',
|
||||
brand: row.brand || '',
|
||||
specification: row.specification || row.spec || '',
|
||||
@@ -1785,7 +1803,7 @@ export default {
|
||||
},
|
||||
addGoods(index) {
|
||||
this.form.goods.splice(index + 1, 0, {
|
||||
quantityUnit: quantityUnitOptions[0],
|
||||
quantityUnit: this.defaultQuantityUnit(),
|
||||
materialCode: '',
|
||||
deviceCode: '',
|
||||
remark: '',
|
||||
|
||||
@@ -1098,6 +1098,7 @@ import {
|
||||
import { getDictionary as getSystemDictionary } from '@/api/system/dict';
|
||||
import { getDictionary } from '@/api/system/dictbiz';
|
||||
import { packageOptions } from '@/option/business/common';
|
||||
import { loadMeasurementUnitOptions } from '@/utils/measurement-unit';
|
||||
import { normalizeMapSearchResults, searchAmapPlaceText } from '@/utils/map-search';
|
||||
import { downloadFileByUrl, downloadXls } from '@/utils/util';
|
||||
import { isMobile } from '@/utils/validate';
|
||||
@@ -1278,7 +1279,8 @@ export default {
|
||||
cargoTypeRequest: null,
|
||||
transportCargoRows: [],
|
||||
packageOptions,
|
||||
quantityUnitOptions: ['吨', '千克', '立方米', '立方', '公里', '单', '件', '车', '箱', '托盘'],
|
||||
quantityUnitOptions: [],
|
||||
quantityUnitLoading: false,
|
||||
feeUnitOptions: [],
|
||||
currencyOptions: [],
|
||||
currencyLoading: false,
|
||||
@@ -1525,11 +1527,25 @@ export default {
|
||||
created() {
|
||||
this.loadProjectOptions();
|
||||
this.ensureCargoTypeOptions();
|
||||
this.loadQuantityUnitOptions();
|
||||
this.loadFeeUnitOptions();
|
||||
this.loadCurrencyOptions();
|
||||
if (this.isStandaloneFormPage) this.$nextTick(() => this.initStandaloneFormPage());
|
||||
},
|
||||
methods: {
|
||||
loadQuantityUnitOptions() {
|
||||
if (this.quantityUnitLoading) return this.quantityUnitLoadPromise;
|
||||
this.quantityUnitLoading = true;
|
||||
this.quantityUnitLoadPromise = loadMeasurementUnitOptions()
|
||||
.then(options => {
|
||||
this.quantityUnitOptions = options.map(item => item.value);
|
||||
return this.quantityUnitOptions;
|
||||
})
|
||||
.finally(() => {
|
||||
this.quantityUnitLoading = false;
|
||||
});
|
||||
return this.quantityUnitLoadPromise;
|
||||
},
|
||||
buildOption(option) {
|
||||
const next = { ...option, column: (option.column || []).map(column => ({ ...column })) };
|
||||
const tableOrder = this.config.tableColumnOrder || [];
|
||||
|
||||
@@ -2742,6 +2742,7 @@ import { packageOptions } from '@/option/business/common';
|
||||
import { formatUpdateUserName } from '@/utils/audit';
|
||||
import { getToken, isAdministrator } from '@/utils/auth';
|
||||
import { normalizeMapSearchResults, searchAmapPlaceText } from '@/utils/map-search';
|
||||
import { loadMeasurementUnitOptions } from '@/utils/measurement-unit';
|
||||
import { applyTableMenuWidth } from '@/utils/table-menu';
|
||||
import { downloadFileByUrl, downloadXls } from '@/utils/util';
|
||||
import { isMobile } from '@/utils/validate';
|
||||
@@ -3206,7 +3207,7 @@ export default {
|
||||
billingCargoTypeLoading: false,
|
||||
billingCargoTypeRequest: null,
|
||||
packageOptions,
|
||||
quantityUnitOptions: ['吨', '千克', '立方米', '件', '车', '箱', '托盘'],
|
||||
quantityUnitOptions: [],
|
||||
attachmentFileTypes: [
|
||||
'pdf',
|
||||
'bmp',
|
||||
@@ -3245,6 +3246,7 @@ export default {
|
||||
} else {
|
||||
this.consumeTemplateCreatePayload();
|
||||
}
|
||||
this.loadTaskQuantityUnitOptions();
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['permission', 'userInfo']),
|
||||
@@ -4307,8 +4309,6 @@ export default {
|
||||
['contractName', '客户合同'],
|
||||
['planName', '计划名称'],
|
||||
['transportType', '运输方式'],
|
||||
['planStartDate', '计划开始日期'],
|
||||
['planEndDate', '计划结束日期'],
|
||||
['departureAddress', '发货地址'],
|
||||
['arrivalAddress', '收货地址'],
|
||||
];
|
||||
@@ -5076,15 +5076,40 @@ export default {
|
||||
return Promise.resolve(this.taskQuantityUnitOptions);
|
||||
}
|
||||
this.taskQuantityUnitLoading = true;
|
||||
return getDictionary({ code: 'quantity_unit' })
|
||||
.then(res => {
|
||||
this.taskQuantityUnitOptions = this.normalizeDictOptions(res.data.data || []);
|
||||
return loadMeasurementUnitOptions()
|
||||
.then(options => {
|
||||
this.taskQuantityUnitOptions = options;
|
||||
this.quantityUnitOptions = options.map(item => item.value);
|
||||
this.ensureQuantityUnitOptions([
|
||||
this.dispatchItemForm?.quantityUnit,
|
||||
...(this.dispatchItemCargoRows || []).map(row => row.quantityUnit),
|
||||
...(this.transportCargoRows || []).map(row => row.quantityUnit),
|
||||
]);
|
||||
return this.taskQuantityUnitOptions;
|
||||
})
|
||||
.finally(() => {
|
||||
this.taskQuantityUnitLoading = false;
|
||||
});
|
||||
},
|
||||
ensureQuantityUnitOptions(units = []) {
|
||||
const pending = [...new Set(
|
||||
(Array.isArray(units) ? units : [units])
|
||||
.map(item => String(item || '').trim())
|
||||
.filter(Boolean)
|
||||
)];
|
||||
if (!pending.length) return;
|
||||
pending.forEach(unit => {
|
||||
if (!this.quantityUnitOptions.includes(unit)) {
|
||||
this.quantityUnitOptions = [...this.quantityUnitOptions, unit];
|
||||
}
|
||||
if (!this.taskQuantityUnitOptions.some(item => item.value === unit)) {
|
||||
this.taskQuantityUnitOptions = [
|
||||
...this.taskQuantityUnitOptions,
|
||||
{ label: unit, value: unit },
|
||||
];
|
||||
}
|
||||
});
|
||||
},
|
||||
loadTaskFeeUnitOptions() {
|
||||
if (this.taskFeeUnitOptions.length || this.taskFeeUnitLoading) {
|
||||
return Promise.resolve(this.taskFeeUnitOptions);
|
||||
@@ -5146,6 +5171,9 @@ export default {
|
||||
});
|
||||
},
|
||||
normalizeTransportCargoRow(row = {}) {
|
||||
const quantityUnit = String(
|
||||
row.quantityUnit || row.goodsQuantityUnit || row.unit || ''
|
||||
).trim();
|
||||
const cargo = {
|
||||
...defaultTransportCargo(),
|
||||
...row,
|
||||
@@ -5162,10 +5190,14 @@ export default {
|
||||
packageType: row.packageType || row.package || '',
|
||||
specification: row.specification || row.spec || '',
|
||||
freightAmount: row.freightAmount ?? row.amount ?? row.totalAmount ?? '',
|
||||
quantityUnit,
|
||||
};
|
||||
['quantity', 'mileage', 'unitPrice', 'freightAmount'].forEach(prop => {
|
||||
if (Number(cargo[prop]) === -1) cargo[prop] = '';
|
||||
});
|
||||
if (quantityUnit) {
|
||||
this.ensureQuantityUnitOptions(quantityUnit);
|
||||
}
|
||||
return cargo;
|
||||
},
|
||||
hasMultipleConfiguredGoods(source = {}, rows = []) {
|
||||
@@ -7594,6 +7626,9 @@ export default {
|
||||
...baseRow,
|
||||
...(index >= 0 ? row : {}),
|
||||
};
|
||||
this.dispatchItemForm.quantityUnit =
|
||||
this.dispatchItemForm.quantityUnit || defaultUnit || TRANSPORT_PLAN_QUANTITY_UNIT;
|
||||
this.ensureQuantityUnitOptions(this.dispatchItemForm.quantityUnit);
|
||||
this.dispatchItemForm.otherFeeTotal = this.normalizeDispatchFeeValue(
|
||||
this.dispatchItemForm.otherFeeTotal
|
||||
);
|
||||
@@ -7603,7 +7638,11 @@ export default {
|
||||
});
|
||||
const goodsRows = this.parseJsonArray(this.dispatchItemForm.goodsJson);
|
||||
this.dispatchItemCargoRows = (goodsRows.length ? goodsRows : [this.dispatchItemForm]).map(
|
||||
item => this.normalizeTransportCargoRow(item)
|
||||
item =>
|
||||
this.normalizeTransportCargoRow({
|
||||
...item,
|
||||
quantityUnit: item.quantityUnit || this.dispatchItemForm.quantityUnit || defaultUnit,
|
||||
})
|
||||
);
|
||||
const hasMultiplePlanGoods = this.dispatchPlanGoodsRows.length > 1;
|
||||
const shouldAutoFillFullGoods =
|
||||
@@ -7613,11 +7652,18 @@ export default {
|
||||
const unscheduledGoodsRows = this.getDispatchUnscheduledGoodsRows();
|
||||
this.dispatchItemForm.taskEntryMode = 'full';
|
||||
this.dispatchItemForm.quantity = '';
|
||||
this.dispatchItemCargoRows = unscheduledGoodsRows.length
|
||||
? unscheduledGoodsRows
|
||||
: this.dispatchPlanGoodsRows.map(goods =>
|
||||
this.normalizeTransportCargoRow({ ...goods, quantity: '' })
|
||||
);
|
||||
this.dispatchItemCargoRows = (
|
||||
unscheduledGoodsRows.length
|
||||
? unscheduledGoodsRows
|
||||
: this.dispatchPlanGoodsRows.map(goods =>
|
||||
this.normalizeTransportCargoRow({ ...goods, quantity: '' })
|
||||
)
|
||||
).map(goods =>
|
||||
this.normalizeTransportCargoRow({
|
||||
...goods,
|
||||
quantityUnit: goods.quantityUnit || defaultUnit || TRANSPORT_PLAN_QUANTITY_UNIT,
|
||||
})
|
||||
);
|
||||
}
|
||||
if (!this.dispatchItemCargoRows.length) {
|
||||
this.dispatchItemCargoRows = [this.normalizeTransportCargoRow()];
|
||||
@@ -8309,7 +8355,10 @@ export default {
|
||||
this.dispatchItemForm.cargoName ||
|
||||
'',
|
||||
quantity: quantityText,
|
||||
quantityUnit: firstGoods.quantityUnit || this.dispatchItemForm.quantityUnit || '',
|
||||
quantityUnit:
|
||||
firstGoods.quantityUnit ||
|
||||
this.dispatchItemForm.quantityUnit ||
|
||||
TRANSPORT_PLAN_QUANTITY_UNIT,
|
||||
unitPrice: firstGoods.unitPrice || '',
|
||||
priceUnit: firstGoods.priceUnit || '',
|
||||
freightTotal: this.dispatchItemFreightTotal,
|
||||
@@ -8324,7 +8373,7 @@ export default {
|
||||
unitPrice: cargo.unitPrice || '',
|
||||
priceUnit: cargo.priceUnit || '',
|
||||
quantity: cargo.quantity || '',
|
||||
quantityUnit: cargo.quantityUnit || '',
|
||||
quantityUnit: cargo.quantityUnit || firstGoods.quantityUnit || TRANSPORT_PLAN_QUANTITY_UNIT,
|
||||
freightAmount: this.dispatchCargoFreightAmount(cargo),
|
||||
})),
|
||||
}),
|
||||
@@ -8413,26 +8462,40 @@ export default {
|
||||
return {
|
||||
id: this.dispatchRow.id,
|
||||
mode,
|
||||
waybills: rows.map(row => ({
|
||||
...row,
|
||||
taskEntryMode: row.taskEntryMode || 'simple',
|
||||
taskRemark: row.remark || '',
|
||||
estimatedStartTime: this.formatDispatchDate(row.estimatedStartTime),
|
||||
estimatedEndTime: this.formatDispatchDate(row.estimatedEndTime),
|
||||
goodsJson:
|
||||
row.goodsJson ||
|
||||
JSON.stringify([
|
||||
waybills: rows.map(row => {
|
||||
const quantityUnit = this.getDispatchQuantityUnit(row);
|
||||
let goodsJson = row.goodsJson;
|
||||
const goodsRows = this.parseJsonArray(goodsJson);
|
||||
if (goodsRows.length) {
|
||||
goodsJson = JSON.stringify(
|
||||
goodsRows.map(goods => ({
|
||||
...goods,
|
||||
quantityUnit: goods.quantityUnit || quantityUnit,
|
||||
}))
|
||||
);
|
||||
} else {
|
||||
goodsJson = JSON.stringify([
|
||||
{
|
||||
cargoName: row.cargoName,
|
||||
cargoType: row.cargoType,
|
||||
quantity: row.quantity,
|
||||
quantityUnit: row.quantityUnit,
|
||||
quantityUnit,
|
||||
specification: row.specification,
|
||||
model: row.model,
|
||||
packageType: row.packageType,
|
||||
},
|
||||
]),
|
||||
})),
|
||||
]);
|
||||
}
|
||||
return {
|
||||
...row,
|
||||
quantityUnit,
|
||||
taskEntryMode: row.taskEntryMode || 'simple',
|
||||
taskRemark: row.remark || '',
|
||||
estimatedStartTime: this.formatDispatchDate(row.estimatedStartTime),
|
||||
estimatedEndTime: this.formatDispatchDate(row.estimatedEndTime),
|
||||
goodsJson,
|
||||
};
|
||||
}),
|
||||
};
|
||||
},
|
||||
formatDispatchDate(value) {
|
||||
|
||||
@@ -3100,6 +3100,7 @@ import { formatUpdateUserName } from '@/utils/audit';
|
||||
import { getToken, isAdministrator } from '@/utils/auth';
|
||||
import { openImportDialog } from '@/utils/import-excel';
|
||||
import { normalizeMapSearchResults, searchAmapPlaceText } from '@/utils/map-search';
|
||||
import { loadMeasurementUnitOptions } from '@/utils/measurement-unit';
|
||||
import { applyTableMenuWidth } from '@/utils/table-menu';
|
||||
import { downloadFileByUrl, downloadXls } from '@/utils/util';
|
||||
import { isMobile } from '@/utils/validate';
|
||||
@@ -3556,7 +3557,7 @@ export default {
|
||||
cargoTypeLoading: false,
|
||||
cargoTypeRequest: null,
|
||||
packageOptions,
|
||||
quantityUnitOptions: ['吨', '千克', '立方米', '件', '车', '箱', '托盘'],
|
||||
quantityUnitOptions: [],
|
||||
attachmentFileTypes: [
|
||||
'pdf',
|
||||
'bmp',
|
||||
@@ -3613,7 +3614,6 @@ export default {
|
||||
this.loadProjectOptions();
|
||||
}
|
||||
if (this.config.enableTaskInfoForm) {
|
||||
this.loadTaskQuantityUnitOptions();
|
||||
this.loadTaskFeeUnitOptions();
|
||||
this.ensureCargoTypeOptions();
|
||||
}
|
||||
@@ -3628,6 +3628,7 @@ export default {
|
||||
} else {
|
||||
this.consumeTemplateCreatePayload();
|
||||
}
|
||||
this.loadTaskQuantityUnitOptions();
|
||||
},
|
||||
// 标签切走(实例被 keep-alive 缓存)或销毁时,收起本页所有 append-to-body 的弹窗。
|
||||
// 否则 Teleport 出去的弹窗 DOM 会一直留在 body 上,盖住后续打开的页面。
|
||||
@@ -6983,9 +6984,10 @@ export default {
|
||||
return Promise.resolve(this.taskQuantityUnitOptions);
|
||||
}
|
||||
this.taskQuantityUnitLoading = true;
|
||||
return getDictionary({ code: 'quantity_unit' })
|
||||
.then(res => {
|
||||
this.taskQuantityUnitOptions = this.normalizeDictOptions(res.data.data || []);
|
||||
return loadMeasurementUnitOptions()
|
||||
.then(options => {
|
||||
this.taskQuantityUnitOptions = options;
|
||||
this.quantityUnitOptions = options.map(item => item.value);
|
||||
return this.taskQuantityUnitOptions;
|
||||
})
|
||||
.finally(() => {
|
||||
|
||||
@@ -846,15 +846,15 @@
|
||||
v-model="userQuery.realName"
|
||||
placeholder="用户姓名"
|
||||
clearable
|
||||
@keyup.enter="loadUsers"
|
||||
@keyup.enter="searchUsers"
|
||||
/>
|
||||
<el-input
|
||||
v-model="userQuery.phone"
|
||||
placeholder="手机号"
|
||||
clearable
|
||||
@keyup.enter="loadUsers"
|
||||
@keyup.enter="searchUsers"
|
||||
/>
|
||||
<el-button type="primary" @click="loadUsers">查询</el-button>
|
||||
<el-button type="primary" @click="searchUsers">查询</el-button>
|
||||
<el-button @click="resetUserQuery">重置</el-button>
|
||||
</div>
|
||||
<el-table :data="userData" border v-loading="userLoading" @row-dblclick="selectUser">
|
||||
@@ -2756,11 +2756,20 @@ export default {
|
||||
if (this.dialogReadonly) return;
|
||||
this.userPickType = type;
|
||||
this.userBox = true;
|
||||
this.userPage.currentPage = 1;
|
||||
this.loadUsers();
|
||||
},
|
||||
searchUsers() {
|
||||
this.userPage.currentPage = 1;
|
||||
this.loadUsers();
|
||||
},
|
||||
loadUsers() {
|
||||
this.userLoading = true;
|
||||
getUserList(this.userPage.currentPage, this.userPage.pageSize, this.userQuery)
|
||||
const params = {
|
||||
realName: String(this.userQuery.realName || '').trim(),
|
||||
phone: String(this.userQuery.phone || '').trim(),
|
||||
};
|
||||
getUserList(this.userPage.currentPage, this.userPage.pageSize, params)
|
||||
.then(res => {
|
||||
const result = res.data.data || {};
|
||||
this.userData = result.records || [];
|
||||
|
||||
@@ -585,6 +585,10 @@ export default {
|
||||
this.$message.warning(`第${index + 1}条调度明细缺少车牌号`);
|
||||
return;
|
||||
}
|
||||
if (!this.resolveDispatchQuantityUnit(row)) {
|
||||
this.$message.warning(`第${index + 1}条调度明细缺少数量单位`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.$confirm(
|
||||
@@ -600,32 +604,57 @@ export default {
|
||||
this.submitDispatch();
|
||||
});
|
||||
},
|
||||
resolveDispatchQuantityUnit(row = {}) {
|
||||
const topUnit = String(row.quantityUnit || '').trim();
|
||||
if (topUnit) return topUnit;
|
||||
const goodsList = this.parseGoodsList(row.goodsJson);
|
||||
for (const goods of goodsList) {
|
||||
const unit = String(
|
||||
goods?.quantityUnit || goods?.goodsQuantityUnit || goods?.unit || ''
|
||||
).trim();
|
||||
if (unit) return unit;
|
||||
}
|
||||
return TRANSPORT_PLAN_QUANTITY_UNIT;
|
||||
},
|
||||
enrichDispatchGoodsJson(row = {}, quantityUnit) {
|
||||
const goodsList = this.parseGoodsList(row.goodsJson);
|
||||
if (goodsList.length) {
|
||||
const nextGoods = goodsList.map(goods => ({
|
||||
...goods,
|
||||
quantityUnit: String(goods.quantityUnit || quantityUnit || '').trim() || quantityUnit,
|
||||
}));
|
||||
return JSON.stringify(nextGoods);
|
||||
}
|
||||
return JSON.stringify([
|
||||
{
|
||||
cargoName: row.cargoName,
|
||||
cargoType: row.cargoType,
|
||||
quantity: row.quantity,
|
||||
quantityUnit,
|
||||
specification: row.specification,
|
||||
model: row.model,
|
||||
packageType: row.packageType,
|
||||
brand: row.brand,
|
||||
},
|
||||
]);
|
||||
},
|
||||
submitDispatch() {
|
||||
this.saving = true;
|
||||
const payload = {
|
||||
id: this.planId,
|
||||
mode: 'submit',
|
||||
waybills: this.dispatchList.map(row => ({
|
||||
...row,
|
||||
taskEntryMode: row.taskEntryMode || 'simple',
|
||||
taskRemark: row.remark || '',
|
||||
estimatedStartTime: row.estimatedStartTime || this.planData.planStartDate || '',
|
||||
estimatedEndTime: row.estimatedEndTime || this.planData.planEndDate || '',
|
||||
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,
|
||||
brand: row.brand,
|
||||
},
|
||||
]),
|
||||
})),
|
||||
waybills: this.dispatchList.map(row => {
|
||||
const quantityUnit = this.resolveDispatchQuantityUnit(row);
|
||||
return {
|
||||
...row,
|
||||
quantityUnit,
|
||||
taskEntryMode: row.taskEntryMode || 'simple',
|
||||
taskRemark: row.remark || '',
|
||||
estimatedStartTime: row.estimatedStartTime || this.planData.planStartDate || '',
|
||||
estimatedEndTime: row.estimatedEndTime || this.planData.planEndDate || '',
|
||||
goodsJson: this.enrichDispatchGoodsJson(row, quantityUnit),
|
||||
};
|
||||
}),
|
||||
};
|
||||
|
||||
dispatch(payload)
|
||||
@@ -639,7 +668,7 @@ export default {
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('调度失败:', err);
|
||||
this.$message.error('调度失败');
|
||||
this.$message.error(err?.response?.data?.msg || err?.message || '调度失败');
|
||||
})
|
||||
.finally(() => {
|
||||
this.saving = false;
|
||||
|
||||
+35
-38
@@ -189,6 +189,20 @@ import func from '@/utils/func';
|
||||
|
||||
const escapeRegExp = value => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
const ORGANIZATION_TYPE_DICT_CODE = 'organization_type';
|
||||
/**
|
||||
* 组织类型名称 → 字典数字键(兼容 dictKey 配成中文的情况)
|
||||
*/
|
||||
const ORGANIZATION_TYPE_CODE_BY_LABEL = {
|
||||
集团: 1,
|
||||
一级集团: 1,
|
||||
事业部: 2,
|
||||
二级事业部: 2,
|
||||
部门: 3,
|
||||
分公司: 4,
|
||||
三级分公司: 4,
|
||||
物流园区: 5,
|
||||
承运商: 6,
|
||||
};
|
||||
|
||||
export default {
|
||||
data() {
|
||||
@@ -755,7 +769,7 @@ export default {
|
||||
.filter(Boolean);
|
||||
this.resetDeptCategoryOptions();
|
||||
if (list.length && !this.organizationTypeOptions.length) {
|
||||
this.$message.warning('组织类型字典 dictKey 须为数字 1~6,当前无法解析,请检查业务字典配置');
|
||||
this.$message.warning('组织类型字典无法解析,请检查业务字典 organization_type 的字典键配置');
|
||||
}
|
||||
// 字典异步返回后触发表格组织类型标签重绘
|
||||
if (Array.isArray(this.data) && this.data.length) {
|
||||
@@ -765,29 +779,31 @@ export default {
|
||||
},
|
||||
/**
|
||||
* 业务字典项转下拉选项。后端 deptCategory 为 Integer,value 必须始终为数字。
|
||||
* dictKey 为纯数字时直接使用;若配成中文则回退用 sort(1~6)作为层级码。
|
||||
* 优先用数字 dictKey;若 dictKey 配成中文,则按名称映射已知编码。
|
||||
*/
|
||||
normalizeOrganizationTypeOption(item) {
|
||||
const label = item?.dictValue ?? item?.label;
|
||||
if (label === undefined || label === null || String(label).trim() === '') {
|
||||
return null;
|
||||
}
|
||||
const labelText = String(label).trim();
|
||||
const rawKey = item?.dictKey ?? item?.value;
|
||||
const keyText = rawKey === undefined || rawKey === null ? '' : String(rawKey).trim();
|
||||
const keyNumber = Number(keyText);
|
||||
const isPureNumber = keyText !== '' && !Number.isNaN(keyNumber) && String(keyNumber) === keyText;
|
||||
if (isPureNumber) {
|
||||
return {
|
||||
label: String(label),
|
||||
label: labelText,
|
||||
value: keyNumber,
|
||||
};
|
||||
}
|
||||
// 兼容 dictKey 误配成中文:用排序号作为 1~6 层级码,保证下拉有数据且可提交 Integer
|
||||
const sortNumber = Number(item?.sort);
|
||||
if (!Number.isNaN(sortNumber) && sortNumber >= 1 && sortNumber <= 6) {
|
||||
// dictKey / 名称 配成中文时,按标准名称映射到数字键
|
||||
const codeByLabel =
|
||||
ORGANIZATION_TYPE_CODE_BY_LABEL[labelText] ?? ORGANIZATION_TYPE_CODE_BY_LABEL[keyText];
|
||||
if (codeByLabel) {
|
||||
return {
|
||||
label: String(label),
|
||||
value: sortNumber,
|
||||
label: labelText,
|
||||
value: codeByLabel,
|
||||
};
|
||||
}
|
||||
return null;
|
||||
@@ -813,18 +829,25 @@ export default {
|
||||
this.$message.warning('请选择组织类型');
|
||||
return null;
|
||||
}
|
||||
if (typeof value === 'number' && !Number.isNaN(value)) {
|
||||
return value;
|
||||
}
|
||||
const asNumber = Number(value);
|
||||
if (!Number.isNaN(asNumber) && String(asNumber) === String(value).trim()) {
|
||||
return asNumber;
|
||||
}
|
||||
// 容错:表单里残留了中文标签时,按字典项 label 反查数字编码
|
||||
// 容错:表单里残留了中文标签时,按字典项 label / 标准名称反查数字编码
|
||||
const matched = this.organizationTypeOptions.find(
|
||||
item => String(item.label) === String(value) || this.isSameDeptCategory(item.value, value)
|
||||
);
|
||||
if (matched && typeof matched.value === 'number' && !Number.isNaN(matched.value)) {
|
||||
return matched.value;
|
||||
}
|
||||
this.$message.warning('组织类型无效,请重新选择(字典键须为数字 1~6)');
|
||||
const codeByLabel = ORGANIZATION_TYPE_CODE_BY_LABEL[String(value).trim()];
|
||||
if (codeByLabel) {
|
||||
return codeByLabel;
|
||||
}
|
||||
this.$message.warning('组织类型无效,请重新选择');
|
||||
return null;
|
||||
},
|
||||
resetDeptCategoryOptions() {
|
||||
@@ -860,35 +883,9 @@ export default {
|
||||
const text = matched?.label || value;
|
||||
return String(text).includes('承运商');
|
||||
},
|
||||
filterOrganizationTypeOptions(values) {
|
||||
const valueSet = new Set(values.map(item => String(item)));
|
||||
const filtered = this.organizationTypeOptions.filter(item => valueSet.has(String(item.value)));
|
||||
// 字典 key 非数字时无法按历史层级码过滤,回退展示全部,避免下拉无选项
|
||||
return filtered.length ? filtered : [...this.organizationTypeOptions];
|
||||
},
|
||||
updateDeptCategoryOptions() {
|
||||
const categoryColumn = this.findColumn(this.option.column, 'deptCategory');
|
||||
const parent = this.parentDept;
|
||||
if (!parent || !categoryColumn) {
|
||||
return;
|
||||
}
|
||||
let dicData = [];
|
||||
if (this.isRootDept(parent)) {
|
||||
dicData =
|
||||
parent.deptName === '外部组织'
|
||||
? this.filterOrganizationTypeOptions([6])
|
||||
: this.filterOrganizationTypeOptions([1]);
|
||||
} else if (Number(parent.deptCategory) === 1) {
|
||||
dicData = this.filterOrganizationTypeOptions([2, 3]);
|
||||
} else if ([2, 3].includes(Number(parent.deptCategory))) {
|
||||
dicData = this.filterOrganizationTypeOptions([4, 5, 6]);
|
||||
} else {
|
||||
dicData = [...this.organizationTypeOptions];
|
||||
}
|
||||
categoryColumn.dicData = dicData;
|
||||
if (!dicData.some(item => this.isSameDeptCategory(item.value, this.form.deptCategory))) {
|
||||
this.form.deptCategory = dicData[0]?.value;
|
||||
}
|
||||
// 组织层级仅由 parentId 决定,组织类型不再按上级过滤,始终展示全部字典项
|
||||
this.resetDeptCategoryOptions();
|
||||
if (this.isCarrierDeptCategory(this.form.deptCategory)) {
|
||||
this.openCarrierDialog();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user