handleBillingLimitInput(row, rangeIndex, 'upperLimit', value)"
/>
-
添加
-
+
-
+
设置匹配条件
-
-
+
+
复制
-
-
+
删除
-
+
@@ -1386,9 +3223,9 @@
-
+
选择
-
+
@@ -1493,9 +3330,9 @@
-
+
选择
-
+
@@ -1575,9 +3412,9 @@
-
+
选择
-
+
@@ -1722,9 +3559,9 @@
-
+
选择
-
+
@@ -1792,6 +3629,9 @@ import { getList as getCommonCargoList } from '@/api/business/common-cargo';
import { getList as getContractList } from '@/api/business/contract-manage';
import { getList as getProjectList } from '@/api/business/project-apply';
import { getList as getCommonRouteList } from '@/api/business/common-route';
+import { getList as getTransportPlanList } from '@/api/business/transport-plan';
+import { getList as getCarrierCustomerList } from '@/api/vehicle/customer-archive';
+import { getList as getDriverList } from '@/api/transportCapacity/driver';
import { getDictionary } from '@/api/system/dictbiz';
import { getDeptTree } from '@/api/system/dept';
import { addressTypeOptions } from '@/option/base/common-address';
@@ -1805,6 +3645,7 @@ import { List, Location, Search } from '@element-plus/icons-vue';
import { mapGetters } from 'vuex';
import NProgress from 'nprogress';
import 'nprogress/nprogress.css';
+import WaybillImportDialog from './waybill-import-dialog.vue';
const AMAP_KEY = '653b7cf105ad7fb8ec9b2f5198ade315';
const AMAP_SECURITY_CODE = '5aab65c632e48ebae0d0e28f5b28e21a';
@@ -1824,6 +3665,30 @@ const extractRecords = res => {
return [];
};
+const transportPlanImportColumns = [
+ ['计划名称', 'planName'],
+ ['运输方式', 'transportType'],
+ ['计划开始日期', 'planStartDate'],
+ ['计划结束日期', 'planEndDate'],
+ ['货物名称', 'cargoName'],
+ ['货物类型', 'cargoType'],
+ ['数量', 'quantity'],
+ ['数量单位', 'quantityUnit'],
+ ['包装', 'packageType'],
+ ['品牌', 'brand'],
+ ['规格', 'specification'],
+ ['型号', 'model'],
+ ['物料编码', 'materialCode'],
+ ['设备编码', 'deviceCode'],
+ ['发货地址', 'departureAddress'],
+ ['发货联系人', 'departureContact'],
+ ['发货联系方式', 'departurePhone'],
+ ['收货地址', 'arrivalAddress'],
+ ['收货联系人', 'arrivalContact'],
+ ['收货联系方式', 'arrivalPhone'],
+ ['备注', 'remark'],
+];
+
const defaultBillingRule = () => ({
feeType: '',
feeItem: '',
@@ -1887,7 +3752,53 @@ const defaultTransportCargo = () => ({
remark: '',
});
+const defaultDispatchRow = () => ({
+ taskEntryMode: 'simple',
+ transportType: '',
+ carrierType: '承运商',
+ carrierId: '',
+ carrierName: '',
+ driverId: '',
+ driverName: '',
+ driverPhone: '',
+ vehicleNo: '',
+ trailerVehicleNo: '',
+ escortName: '',
+ escortPhone: '',
+ mileage: '',
+ cargoType: '',
+ cargoName: '',
+ cargoInfo: '',
+ quantity: '',
+ quantityUnit: '吨',
+ specification: '',
+ model: '',
+ packageType: '',
+ brand: '',
+ materialCode: '',
+ unitPrice: '',
+ priceUnit: '元/吨',
+ otherFeeTotal: '',
+ estimatedStartTime: '',
+ estimatedEndTime: '',
+ departureAddress: '',
+ departureName: '',
+ departureContact: '',
+ departurePhone: '',
+ arrivalAddress: '',
+ arrivalName: '',
+ arrivalContact: '',
+ arrivalPhone: '',
+ dispatchUserName: '',
+ dispatchTime: '',
+ attachmentsJson: '',
+ remark: '',
+});
+
+const taskCarrierTypes = ['承运商', '自运', '网货平台'];
+
export default {
+ components: { WaybillImportDialog },
name: 'BusinessCrudPage',
props: {
api: {
@@ -1919,6 +3830,35 @@ export default {
data: [],
allDept: 0,
dialogReadonly: false,
+ detailBox: false,
+ detailLoading: false,
+ detailRow: {},
+ waybillImportBox: false,
+ transportPlanImportBox: false,
+ transportPlanImportLoading: false,
+ transportPlanImportFiles: [],
+ transportPlanImportSourceFile: null,
+ transportPlanImportRows: [],
+ transportPlanImportSelection: [],
+ transportPlanImportTab: 'all',
+ transportPlanImportEditingKey: '',
+ transportPlanImportEditSnapshot: null,
+ transportPlanImportContractOptions: [],
+ transportPlanImportContractLoading: false,
+ transportPlanImportForm: {
+ projectId: '',
+ projectName: '',
+ customerName: '',
+ contractId: '',
+ contractName: '',
+ file: null,
+ },
+ transportPlanImportRules: {
+ projectId: [{ required: true, message: '请选择项目', trigger: 'change' }],
+ customerName: [{ required: true, message: '请选择项目后带出客户', trigger: 'change' }],
+ contractId: [{ required: true, message: '请选择项目后带出客户合同', trigger: 'change' }],
+ file: [{ required: true, message: '请上传计划明细表', trigger: 'change' }],
+ },
draftSaveLoading: false,
crudDialogType: '',
defaultDialogButtons: null,
@@ -1930,6 +3870,8 @@ export default {
projectLoading: false,
contractOptions: [],
contractLoading: false,
+ shippingPlanOptions: [],
+ shippingPlanLoading: false,
selectedOrganizationId: '',
organizationOptions: [],
organizationFlatOptions: [],
@@ -1938,6 +3880,15 @@ export default {
flowBox: false,
flowUrl: '',
processInstanceId: '',
+ dispatchBox: false,
+ dispatchLoading: false,
+ dispatchRow: {},
+ dispatchRows: [],
+ dispatchSaving: '',
+ dispatchItemBox: false,
+ dispatchItemIndex: -1,
+ dispatchItemForm: defaultDispatchRow(),
+ dispatchItemAttachmentRows: [],
attachmentRows: [],
contractFileRows: [],
transportCargoRows: [],
@@ -1991,6 +3942,17 @@ export default {
currentPage: 1,
total: 0,
},
+ taskCarrierTypes,
+ taskCarrierOptions: [],
+ taskCarrierLoading: false,
+ taskDriverOptions: [],
+ taskDriverLoading: false,
+ taskCargoOptionsMap: {},
+ taskCargoLoadingMap: {},
+ taskQuantityUnitOptions: [],
+ taskQuantityUnitLoading: false,
+ taskFeeUnitOptions: [],
+ taskFeeUnitLoading: false,
cargoImportBox: false,
cargoImportLoading: false,
billingPlanRows: [],
@@ -2085,6 +4047,12 @@ export default {
this.ensureBillingFeeCategoryOptions();
this.ensureBillingUnitOptions();
}
+ if (this.config.enableTaskInfoForm) {
+ this.loadTaskQuantityUnitOptions();
+ this.loadTaskFeeUnitOptions();
+ this.ensureBillingCargoTypeOptions();
+ }
+ this.consumeTemplateCreatePayload();
},
computed: {
...mapGetters(['permission', 'userInfo']),
@@ -2093,6 +4061,176 @@ export default {
addBtn: this.canCreate,
};
},
+ detailSections() {
+ return this.config.detailSections || [];
+ },
+ transportPlanImportDuplicateCount() {
+ return this.transportPlanImportRows.filter(row => row._duplicate).length;
+ },
+ transportPlanImportVisibleRows() {
+ return this.transportPlanImportTab === 'duplicate'
+ ? this.transportPlanImportRows.filter(row => row._duplicate)
+ : this.transportPlanImportRows;
+ },
+ actionButtonLikeSearch() {
+ return this.config.actionButtonLikeSearch === true;
+ },
+ contractSelectEnabled() {
+ return this.config.enableContractSelect || this.config.enableTransportPlanForm;
+ },
+ shippingPlanSelectEnabled() {
+ return this.config.enableShippingPlanSelect === true;
+ },
+ shippingInfoFormEnabled() {
+ return this.config.enableShippingInfoForm || this.config.enableTransportPlanForm;
+ },
+ taskInfoFormEnabled() {
+ return this.config.enableTaskInfoForm === true;
+ },
+ taskEntryModeOptions() {
+ return [
+ { label: '精简录入', value: 'simple' },
+ { label: '完整录入', value: 'full' },
+ ];
+ },
+ isTaskFullMode() {
+ return this.form.taskEntryMode === 'full';
+ },
+ isCarrierMode() {
+ return this.form.carrierType === '承运商';
+ },
+ taskCargoOptions() {
+ return this.getTaskCargoOptionsByPath(this.form.cargoTypePath);
+ },
+ taskCargoLoading() {
+ return this.isTaskCargoOptionsLoading(this.form.cargoTypePath);
+ },
+ taskCargoQuantityTotal() {
+ const total = this.transportCargoRows.reduce((sum, row) => {
+ const value = Number(row.quantity);
+ return Number.isFinite(value) ? sum + value : sum;
+ }, 0);
+ if (!total) return '';
+ return Number.isInteger(total) ? String(total) : String(Number(total.toFixed(3)));
+ },
+ dispatchPlanGoodsRows() {
+ return this.parseJsonArray(this.dispatchRow.goodsJson);
+ },
+ dispatchPlanCargoInfo() {
+ const cargoInfo = this.dispatchPlanGoodsRows
+ .map(row => this.formatDispatchCargoInfo({}, row))
+ .filter(Boolean)
+ .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
+ );
+ },
+ 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 ||
+ '吨'
+ );
+ },
+ 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}`;
+ },
+ dispatchAttachmentRows() {
+ return this.parseJsonArray(this.dispatchRow.attachmentsJson);
+ },
+ dispatchRouteStart() {
+ const address = this.dispatchRow.departureAddress || this.dispatchRow.departureName || '';
+ return {
+ label: this.dispatchRow.departureName || '起',
+ address,
+ detail: this.dispatchRow.departureDetailAddress || address,
+ contact: [this.dispatchRow.departureContact, this.dispatchRow.departurePhone]
+ .filter(Boolean)
+ .join(' - '),
+ };
+ },
+ dispatchRouteEnd() {
+ const address = this.dispatchRow.arrivalAddress || this.dispatchRow.arrivalName || '';
+ return {
+ label: this.dispatchRow.arrivalName || '终',
+ address,
+ detail: this.dispatchRow.arrivalDetailAddress || address,
+ contact: [this.dispatchRow.arrivalContact, this.dispatchRow.arrivalPhone]
+ .filter(Boolean)
+ .join(' - '),
+ };
+ },
+ dispatchDialogTitle() {
+ return '计划调度';
+ },
+ dispatchItemDialogTitle() {
+ const mode = this.dispatchItemForm.taskEntryMode === 'full' ? '完整录入' : '精简录入';
+ return this.dispatchItemIndex < 0 ? `新增/编辑(${mode})` : `编辑调度信息(${mode})`;
+ },
+ dispatchTransportMode() {
+ return this.resolveTransportMode(this.dispatchItemForm.transportType);
+ },
+ dispatchStationMode() {
+ return ['water', 'rail', 'air'].includes(this.dispatchTransportMode);
+ },
+ dispatchStationNamePlaceholder() {
+ if (!this.dispatchStationMode) return '行政区划自动带出';
+ const labelMap = {
+ water: '请选择港口/码头',
+ rail: '请选择车站',
+ air: '请选择空港',
+ };
+ return labelMap[this.dispatchTransportMode] || '请选择站点';
+ },
+ dispatchAddressDetailPlaceholder() {
+ return this.dispatchStationMode ? '选择站点后带出详细地址' : '请输入';
+ },
+ dispatchDialogNo() {
+ return this.dispatchRow.planNo || this.dispatchRow.loadingNo || '';
+ },
+ dispatchDialogStatusText() {
+ return this.displayStatus(this.dispatchRow, this.config.statusProp || 'businessStatus');
+ },
+ taskFreightAmount() {
+ const quantity = Number(this.taskCargoQuantityTotal || this.form.quantity || 0);
+ const unitPrice = Number(this.form.unitPrice || 0);
+ if (!quantity || !unitPrice) return '';
+ const amount = quantity * unitPrice;
+ return Number.isInteger(amount) ? String(amount) : String(Number(amount.toFixed(2)));
+ },
isAdmin() {
const authority = this.userInfo && this.userInfo.authority;
return Array.isArray(authority)
@@ -2246,7 +4384,7 @@ export default {
},
watch: {
'form.transportType'(value, oldValue) {
- if (!this.config.enableTransportPlanForm || this.suppressTransportTypeClear) return;
+ if (!this.shippingInfoFormEnabled || this.suppressTransportTypeClear) return;
if (String(value ?? '') === String(oldValue ?? '')) return;
this.handleTransportTypeChange(value);
},
@@ -2407,6 +4545,43 @@ export default {
}
this.handleCustomOperation(action, row);
},
+ consumeTemplateCreatePayload() {
+ const target = this.config.templateCreateTarget;
+ if (!target) return;
+ const storageKey = 'business-template-create';
+ const raw = sessionStorage.getItem(storageKey);
+ if (!raw) return;
+ let payload;
+ try {
+ payload = JSON.parse(raw);
+ } catch (error) {
+ sessionStorage.removeItem(storageKey);
+ return;
+ }
+ if (payload.target !== target) return;
+ sessionStorage.removeItem(storageKey);
+ this.$nextTick(() => {
+ this.$refs.crud.rowAdd();
+ this.$nextTick(() => {
+ const row = payload.data || {};
+ this.suppressTransportTypeClear = true;
+ Object.assign(this.form, row);
+ this.$nextTick(() => {
+ this.suppressTransportTypeClear = false;
+ });
+ this.form.planName = this.form.planName || this.form.templateName || '';
+ this.selectedProjectId = this.form.projectId || '';
+ this.attachmentRows = this.parseJsonArray(this.form.attachmentsJson);
+ if (this.contractSelectEnabled) {
+ this.syncCurrentContractOption();
+ this.loadContractOptionsForProject();
+ }
+ if (this.config.enableProjectSelect) this.syncCurrentProjectOption();
+ if (this.config.enableTransportPlanForm) this.initTransportPlanFormRows();
+ if (this.taskInfoFormEnabled) this.initTaskInfoForm();
+ });
+ });
+ },
canOperation(operation, row) {
const permission = operation.permission || `${this.config.permission}_${operation.action}`;
if (!this.hasPermission(permission) || this.readonlyScope || row.readonly) {
@@ -2422,6 +4597,42 @@ export default {
}
return true;
},
+ showDetailButton(row) {
+ if (!this.config.detailButton || !this.hasPermission(`${this.config.permission}_view`))
+ return false;
+ const status = this.statusValue(row);
+ return status !== 'draft';
+ },
+ formatDetailValue(row, prop) {
+ if (!row) return '-';
+ if (prop === 'businessStatus') return this.displayStatus(row, prop);
+ const column = this.findColumn(this.option.column, prop);
+ if (column?.formatter) return column.formatter(row, {}, row[prop]);
+ if (column?.dicData) {
+ const item = column.dicData.find(dic => String(dic.value) === String(row[prop]));
+ if (item) return item.label;
+ }
+ const value = row[prop];
+ return value === undefined || value === null || value === '' ? '-' : value;
+ },
+ openDetail(row) {
+ this.detailBox = true;
+ this.detailLoading = true;
+ this.detailRow = { ...row };
+ const request =
+ typeof this.api.getDetail === 'function'
+ ? this.api.getDetail(row.id)
+ : Promise.resolve({ data: { data: row } });
+ request
+ .then(res => {
+ const detail = res?.data?.data || res?.data || row;
+ this.detailRow = detail;
+ this.applyFormDetail(detail);
+ })
+ .finally(() => {
+ this.detailLoading = false;
+ });
+ },
displayStatus(row, prop) {
const textProp = prop === this.config.statusProp ? this.config.statusTextProp : '';
if (textProp && row[textProp]) {
@@ -2577,6 +4788,10 @@ export default {
if (!this.validateTransportPlanForm(submitRow)) return false;
submitRow.goodsJson = JSON.stringify(this.transportCargoRows);
}
+ if (this.taskInfoFormEnabled) {
+ if (!this.validateTaskInfoForm(submitRow)) return false;
+ this.syncTaskInfoToRow(submitRow);
+ }
if (this.config.enableAttachmentTable) {
submitRow.attachmentsJson = JSON.stringify(this.attachmentRows);
}
@@ -2600,7 +4815,10 @@ export default {
submitRow.approvalStatus = 'draft';
}
delete submitRow.basicInfoTitle;
+ delete submitRow.templateInfoTitle;
delete submitRow.shippingInfoTitle;
+ delete submitRow.taskInfoTitle;
+ delete submitRow.taskInfoForm;
delete submitRow.goodsInfoTitle;
delete submitRow.contractFileTitle;
delete submitRow.billingInfoTitle;
@@ -2617,7 +4835,7 @@ export default {
return submitRow;
},
validateTransportPlanForm(row) {
- const requiredFields = [
+ const requiredFields = this.config.transportFormRequiredFields || [
['projectName', '项目'],
['contractName', '客户合同'],
['planName', '计划名称'],
@@ -2883,21 +5101,40 @@ export default {
if (type === 'add') {
this.form = {
...(this.config.defaultForm || {}),
- ...this.form,
};
+ this.form.taskEntryMode = 'simple';
this.selectedProjectId = '';
this.selectedOrganizationId = '';
this.attachmentRows = [];
this.contractFileRows = [];
+ this.shippingPlanOptions = [];
+ if (this.contractSelectEnabled) {
+ this.contractOptions = [];
+ this.form.contractId = '';
+ this.form.contractName = '';
+ }
+ if (this.shippingPlanSelectEnabled) {
+ this.clearShippingPlan();
+ }
this.transportCargoRows = [];
this.billingPlanRows = [];
this.settlementRuleForm = defaultSettlementRule();
this.reconciliationForm = defaultReconciliation();
this.changeRecordRows = [];
this.applyCurrentUserHandler();
+ this.initTaskInfoForm();
this.initTransportPlanFormRows();
this.syncContractPeriod(false);
this.syncBillingPlanJson();
+ if (this.config.enableTemplateCodePreview && typeof this.api.nextCode === 'function') {
+ this.api
+ .nextCode()
+ .then(res => {
+ this.form.templateCode = res.data?.data || '';
+ })
+ .finally(done);
+ return;
+ }
done();
return;
}
@@ -2932,6 +5169,7 @@ export default {
};
this.changeRecordRows = this.parseJsonArray(this.form.changeRecordJson);
this.applyCurrentUserHandler();
+ this.initTaskInfoForm();
this.syncContractPeriod(false);
this.syncBillingPlanJson();
if (this.config.enableOrganizationSelect) {
@@ -2942,9 +5180,12 @@ export default {
this.form.remainingFundLimit = 0;
this.syncCurrentProjectOption();
}
- if (this.config.enableTransportPlanForm) {
+ if (this.contractSelectEnabled) {
this.syncCurrentContractOption();
}
+ if (this.shippingPlanSelectEnabled) {
+ this.syncCurrentShippingPlanOption();
+ }
},
parseJsonArray(value) {
if (!value) return [];
@@ -2966,13 +5207,26 @@ export default {
return {};
}
},
+ parseDispatchQuantity(value) {
+ if (value === undefined || value === null || value === '') return 0;
+ const text = String(value).replace(/,/g, '');
+ const match = text.match(/-?\d+(\.\d+)?/);
+ if (!match) return 0;
+ const number = Number(match[0]);
+ return Number.isFinite(number) ? number : 0;
+ },
+ formatDispatchQuantity(value) {
+ const number = Number(value || 0);
+ if (!Number.isFinite(number)) return '0';
+ const fixed = Math.round((number + Number.EPSILON) * 1000) / 1000;
+ return Number.isInteger(fixed) ? String(fixed) : String(fixed).replace(/\.?0+$/, '');
+ },
loadProjectOptions() {
if (!this.config.enableProjectSelect || this.projectLoading) return;
this.projectLoading = true;
getProjectList(1, 9999, {})
.then(res => {
- const records = res.data.data?.records || [];
- this.projectOptions = records;
+ this.projectOptions = extractRecords(res);
this.syncCurrentProjectOption();
})
.finally(() => {
@@ -3004,12 +5258,15 @@ export default {
this.form.projectFundLimit = '';
this.form.usedFundLimit = 0;
this.form.remainingFundLimit = 0;
- if (this.config.enableTransportPlanForm) {
- this.form.transportType = '';
+ if (this.contractSelectEnabled) {
this.form.contractId = '';
this.form.contractName = '';
this.contractOptions = [];
}
+ if (this.shippingPlanSelectEnabled) {
+ this.clearShippingPlan();
+ this.shippingPlanOptions = [];
+ }
return;
}
this.form.projectId = project.id;
@@ -3019,25 +5276,35 @@ export default {
this.form.projectFundLimit = project.fundLimit || project.projectFundLimit || 0;
this.form.usedFundLimit = 0;
this.form.remainingFundLimit = 0;
- if (this.config.enableTransportPlanForm) {
+ if (this.contractSelectEnabled || this.config.enableTransportPlanForm) {
this.form.customerName =
project.customerName ||
project.customerNames ||
project.customer ||
this.form.customerName ||
'';
+ }
+ if (this.config.enableTransportPlanForm) {
this.form.transportType =
project.transportType ||
project.transportMode ||
project.transportWay ||
this.form.transportType ||
'';
+ }
+ if (this.contractSelectEnabled) {
+ this.form.contractId = '';
+ this.form.contractName = '';
this.loadContractOptionsForProject(true);
}
+ if (this.shippingPlanSelectEnabled) {
+ this.clearShippingPlan();
+ this.loadShippingPlanOptions();
+ }
this.$refs.crud?.validateField?.('projectName');
},
loadContractOptionsForProject(autoPick = false) {
- if (!this.config.enableTransportPlanForm || !this.form.projectId || this.contractLoading) {
+ if (!this.contractSelectEnabled || !this.form.projectId || this.contractLoading) {
return Promise.resolve([]);
}
this.contractLoading = true;
@@ -3085,6 +5352,527 @@ export default {
contract.customerName || contract.customerNames || this.form.customerName || '';
this.$refs.crud?.validateField?.('contractName');
},
+ loadShippingPlanOptions() {
+ if (!this.shippingPlanSelectEnabled || this.shippingPlanLoading) {
+ return Promise.resolve([]);
+ }
+ this.shippingPlanLoading = true;
+ return getTransportPlanList(1, 9999, {
+ projectId: this.form.projectId,
+ projectName: this.form.projectName,
+ })
+ .then(res => {
+ this.shippingPlanOptions = extractRecords(res);
+ this.syncCurrentShippingPlanOption();
+ return this.shippingPlanOptions;
+ })
+ .finally(() => {
+ this.shippingPlanLoading = false;
+ });
+ },
+ syncCurrentShippingPlanOption() {
+ if (!this.form.planName) return;
+ const exists = this.shippingPlanOptions.some(
+ item =>
+ String(item.id) === String(this.form.planId) ||
+ String(item.planName) === String(this.form.planName)
+ );
+ if (!exists) {
+ this.shippingPlanOptions.unshift({
+ id: this.form.planId || this.form.planName,
+ planName: this.form.planName,
+ });
+ }
+ },
+ handleShippingPlanChange(planName) {
+ const plan = this.shippingPlanOptions.find(
+ item =>
+ String(item.planName) === String(planName) || String(item.id) === String(this.form.planId)
+ );
+ if (!plan) {
+ this.clearShippingPlan();
+ return;
+ }
+ this.applyShippingPlan(plan);
+ },
+ applyShippingPlan(plan = {}) {
+ this.form.planId = plan.id || '';
+ this.form.planName = plan.planName || '';
+ this.$refs.crud?.validateField?.('planName');
+ },
+ clearShippingPlan() {
+ this.form.planId = '';
+ this.form.planName = '';
+ },
+ initTaskInfoForm() {
+ if (!this.taskInfoFormEnabled) return;
+ const taskInfo = this.parseJsonObject(this.form.taskInfoJson);
+ const carrierRows = this.parseJsonArray(this.form.carrierJson);
+ const carrierInfo = carrierRows[0] || this.parseJsonObject(this.form.carrierJson);
+ const goodsRows = this.parseJsonArray(this.form.goodsJson);
+ const goodsInfo = goodsRows[0] || {};
+ this.form.taskEntryMode =
+ this.form.taskEntryMode || taskInfo.taskEntryMode || carrierInfo.taskEntryMode || 'simple';
+ this.form.carrierType =
+ this.form.carrierType ||
+ taskInfo.carrierType ||
+ carrierInfo.carrierType ||
+ carrierInfo.type ||
+ '承运商';
+ this.form.carrierName =
+ this.form.carrierName ||
+ taskInfo.carrierName ||
+ carrierInfo.carrierName ||
+ carrierInfo.customerName ||
+ carrierInfo.name ||
+ '';
+ this.form.driverName =
+ this.form.driverName || taskInfo.driverName || carrierInfo.driverName || '';
+ this.form.driverPhone =
+ this.form.driverPhone ||
+ taskInfo.driverPhone ||
+ carrierInfo.driverPhone ||
+ carrierInfo.mobile ||
+ '';
+ this.form.vehicleNo =
+ this.form.vehicleNo || taskInfo.vehicleNo || carrierInfo.vehicleNo || '';
+ this.form.trailerVehicleNo =
+ this.form.trailerVehicleNo ||
+ taskInfo.trailerVehicleNo ||
+ carrierInfo.trailerVehicleNo ||
+ '';
+ this.form.escortName =
+ this.form.escortName || taskInfo.escortName || carrierInfo.escortName || '';
+ this.form.escortPhone =
+ this.form.escortPhone || taskInfo.escortPhone || carrierInfo.escortPhone || '';
+ this.form.cargoType =
+ this.form.cargoType ||
+ taskInfo.cargoType ||
+ goodsInfo.cargoType ||
+ goodsInfo.goodsType ||
+ '';
+ this.form.cargoTypeCode =
+ this.form.cargoTypeCode ||
+ taskInfo.cargoTypeCode ||
+ goodsInfo.cargoTypeCode ||
+ goodsInfo.goodsTypeCode ||
+ '';
+ this.form.cargoName =
+ this.form.cargoName ||
+ taskInfo.cargoName ||
+ goodsInfo.cargoName ||
+ goodsInfo.goodsName ||
+ '';
+ this.form.specification =
+ this.form.specification || taskInfo.specification || goodsInfo.specification || '';
+ 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.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.otherFeeTotal =
+ this.form.otherFeeTotal || taskInfo.otherFeeTotal || goodsInfo.otherFeeTotal || '';
+ this.form.estimatedStartTime =
+ this.form.estimatedStartTime ||
+ taskInfo.estimatedStartTime ||
+ goodsInfo.estimatedStartTime ||
+ '';
+ this.form.estimatedEndTime =
+ this.form.estimatedEndTime || taskInfo.estimatedEndTime || goodsInfo.estimatedEndTime || '';
+ this.form.taskRemark = this.form.taskRemark || taskInfo.taskRemark || goodsInfo.remark || '';
+ this.form.cargoTypePath = this.normalizeBillingCargoTypePath(this.form.cargoTypePath);
+ if (!this.form.cargoTypePath.length) {
+ this.ensureBillingCargoTypeOptions().then(() => {
+ const cargoType = this.findBillingCargoTypeByCodeOrName({
+ cargoTypeCode: this.form.cargoTypeCode,
+ cargoType: this.form.cargoType,
+ });
+ if (cargoType?.path) {
+ this.form.cargoTypePath = cargoType.path;
+ }
+ });
+ }
+ if (this.isTaskFullMode) {
+ this.initTaskFullCargoRows(goodsRows);
+ }
+ },
+ handleTaskEntryModeChange(value) {
+ this.form.taskEntryMode = value || 'simple';
+ if (this.form.taskEntryMode === 'full') {
+ this.initTaskFullCargoRows(this.parseJsonArray(this.form.goodsJson));
+ }
+ },
+ handleTaskCarrierTypeChange(value) {
+ const nextValue = value || '承运商';
+ if (nextValue === '承运商') {
+ this.form.trailerVehicleNo = '';
+ this.form.escortName = '';
+ this.form.escortPhone = '';
+ } else {
+ this.form.carrierName = '';
+ this.form.carrierId = '';
+ }
+ this.form.carrierType = nextValue;
+ },
+ loadTaskCarrierOptions() {
+ if (!this.taskInfoFormEnabled || this.taskCarrierLoading) return Promise.resolve([]);
+ this.taskCarrierLoading = true;
+ return getCarrierCustomerList(1, 9999, { customerType: '承运商' })
+ .then(res => {
+ this.taskCarrierOptions = extractRecords(res);
+ return this.taskCarrierOptions;
+ })
+ .finally(() => {
+ this.taskCarrierLoading = false;
+ });
+ },
+ handleTaskCarrierChange(value) {
+ const carrier = this.taskCarrierOptions.find(item =>
+ [item.customerName, item.carrierName, item.fullName, item.name].some(
+ name => String(name || '') === value
+ )
+ );
+ this.form.carrierId = carrier?.id || '';
+ this.form.carrierName = value || '';
+ },
+ loadTaskDriverOptions() {
+ if (!this.taskInfoFormEnabled || this.taskDriverLoading) return Promise.resolve([]);
+ this.taskDriverLoading = true;
+ return getDriverList(1, 9999, {})
+ .then(res => {
+ this.taskDriverOptions = extractRecords(res);
+ return this.taskDriverOptions;
+ })
+ .finally(() => {
+ this.taskDriverLoading = false;
+ });
+ },
+ handleTaskDriverChange(value) {
+ const driver = this.taskDriverOptions.find(item =>
+ [item.driverName, item.name].some(name => String(name || '') === String(value || ''))
+ );
+ this.form.driverId = driver?.id || '';
+ this.form.driverName = value || '';
+ if (driver) {
+ this.form.driverPhone =
+ driver.mobile || driver.phone || driver.driverPhone || this.form.driverPhone || '';
+ }
+ },
+ getTaskCargoTypeOptionsKey(path = []) {
+ const normalizedPath = this.normalizeBillingCargoTypePath(path);
+ if (!normalizedPath.length) return '';
+ const cargoType = this.findBillingCargoTypeByPath(normalizedPath);
+ return String(cargoType?.cargoCode || cargoType?.code || normalizedPath.join('/'));
+ },
+ getTaskCargoOptionsByPath(path = []) {
+ const key = this.getTaskCargoTypeOptionsKey(path);
+ return key ? this.taskCargoOptionsMap[key] || [] : [];
+ },
+ isTaskCargoOptionsLoading(path = []) {
+ const key = this.getTaskCargoTypeOptionsKey(path);
+ return key ? !!this.taskCargoLoadingMap[key] : false;
+ },
+ setTaskCargoOptionsByKey(key, options = []) {
+ if (!key) return;
+ this.taskCargoOptionsMap = {
+ ...this.taskCargoOptionsMap,
+ [key]: options,
+ };
+ },
+ setTaskCargoLoadingByKey(key, loading) {
+ if (!key) return;
+ this.taskCargoLoadingMap = {
+ ...this.taskCargoLoadingMap,
+ [key]: loading,
+ };
+ },
+ buildTaskCargoListQueryParams(path = []) {
+ const normalizedPath = this.normalizeBillingCargoTypePath(path);
+ const labels = this.getBillingCargoTypePathLabels(normalizedPath);
+ const secondCargoType = this.findBillingCargoTypeByPath(normalizedPath);
+ return {
+ secondCargoTypeName: labels[1] || '',
+ secondCargoTypeCode: secondCargoType?.cargoCode || secondCargoType?.code || '',
+ };
+ },
+ loadTaskCargoOptionsByPath(path = []) {
+ const normalizedPath = this.normalizeBillingCargoTypePath(path);
+ if (!normalizedPath.length) return Promise.resolve([]);
+ return this.ensureBillingCargoTypeOptions().then(() => {
+ const key = this.getTaskCargoTypeOptionsKey(normalizedPath);
+ const params = this.buildTaskCargoListQueryParams(normalizedPath);
+ if (!params.secondCargoTypeName && !params.secondCargoTypeCode) {
+ this.setTaskCargoOptionsByKey(key, []);
+ return [];
+ }
+ this.setTaskCargoLoadingByKey(key, true);
+ return getCommonCargoList(1, 500, {
+ ...params,
+ allDept: 0,
+ })
+ .then(res => {
+ const records = extractRecords(res);
+ this.setTaskCargoOptionsByKey(key, records);
+ return records;
+ })
+ .catch(error => {
+ window.console.log(error);
+ this.setTaskCargoOptionsByKey(key, []);
+ return [];
+ })
+ .finally(() => {
+ this.setTaskCargoLoadingByKey(key, false);
+ });
+ });
+ },
+ loadTaskCargoOptionsForForm() {
+ return this.loadTaskCargoOptionsByPath(this.form.cargoTypePath);
+ },
+ handleTaskCargoTypeChange(value) {
+ const path = this.normalizeBillingCargoTypePath(value);
+ const cargoType = this.findBillingCargoTypeByPath(path);
+ const labels = this.getBillingCargoTypePathLabels(path);
+ this.form.cargoTypePath = path;
+ this.form.cargoType = labels.length ? labels[labels.length - 1] : '';
+ this.form.cargoTypeCode = cargoType?.cargoCode || cargoType?.code || cargoType?.id || '';
+ this.form.cargoName = '';
+ this.form.specification = '';
+ this.form.model = '';
+ this.loadTaskCargoOptionsForForm();
+ },
+ handleTaskCargoChange(value) {
+ const cargo = this.taskCargoOptions.find(
+ item => this.formatBillingCargoTypeLabel(item) === value
+ );
+ this.form.cargoName = value || '';
+ if (cargo) {
+ this.form.specification =
+ cargo.specification || cargo.spec || this.form.specification || '';
+ this.form.model = cargo.model || cargo.modelName || this.form.model || '';
+ }
+ },
+ getTaskCargoRowOptions(row = {}) {
+ return this.getTaskCargoOptionsByPath(row.cargoTypePath);
+ },
+ handleTaskCargoRowNameChange(row, value) {
+ const cargo = this.getTaskCargoRowOptions(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 || '';
+ }
+ this.syncTransportCargoJson();
+ },
+ initTaskFullCargoRows(goodsRows = []) {
+ if (!this.taskInfoFormEnabled) return;
+ const rows = (goodsRows.length ? goodsRows : this.transportCargoRows).map(row =>
+ this.normalizeTransportCargoRow(row)
+ );
+ while (rows.length < 1) {
+ rows.push(this.normalizeTransportCargoRow());
+ }
+ this.transportCargoRows = rows;
+ this.syncTransportCargoJson();
+ },
+ loadTaskQuantityUnitOptions() {
+ if (this.taskQuantityUnitOptions.length || this.taskQuantityUnitLoading) {
+ return Promise.resolve(this.taskQuantityUnitOptions);
+ }
+ this.taskQuantityUnitLoading = true;
+ return getDictionary({ code: 'quantity_unit' })
+ .then(res => {
+ this.taskQuantityUnitOptions = this.normalizeDictOptions(res.data.data || []);
+ return this.taskQuantityUnitOptions;
+ })
+ .finally(() => {
+ this.taskQuantityUnitLoading = false;
+ });
+ },
+ loadTaskFeeUnitOptions() {
+ if (this.taskFeeUnitOptions.length || this.taskFeeUnitLoading) {
+ return Promise.resolve(this.taskFeeUnitOptions);
+ }
+ this.taskFeeUnitLoading = true;
+ return getDictionary({ code: 'unit_fee' })
+ .then(res => {
+ this.taskFeeUnitOptions = this.normalizeDictOptions(res.data.data || []);
+ return this.taskFeeUnitOptions;
+ })
+ .finally(() => {
+ this.taskFeeUnitLoading = false;
+ });
+ },
+ normalizeDictOptions(list = []) {
+ return (list || []).map(item => ({
+ label: item.dictValue || item.label || item.value || '',
+ value: item.dictKey || item.value || item.dictValue || '',
+ }));
+ },
+ handleTaskNumberInput(prop, value) {
+ this.form[prop] = this.normalizeTaskNumber(value);
+ },
+ normalizeTaskNumber(value) {
+ const text = String(value || '').replace(/[^\d.]/g, '');
+ const parts = text.split('.');
+ return parts.length > 1 ? `${parts[0]}.${parts.slice(1).join('')}` : parts[0];
+ },
+ validateTaskCargoRows() {
+ if (!this.transportCargoRows.length) {
+ this.$message.warning('请至少添加一条货物信息');
+ return false;
+ }
+ for (const [index, cargo] of this.transportCargoRows.entries()) {
+ const requiredFields = [
+ ['cargoName', '货物名称'],
+ ['cargoType', '货物类型'],
+ ['quantity', '数量'],
+ ['quantityUnit', '数量单位'],
+ ];
+ const emptyField = requiredFields.find(([prop]) => this.isBillingFieldEmpty(cargo[prop]));
+ if (emptyField) {
+ this.$message.warning(`第${index + 1}行${emptyField[1]}不能为空`);
+ return false;
+ }
+ const invalidField = [['quantity', '数量']].find(([prop]) => {
+ const value = cargo[prop];
+ return value !== undefined && value !== null && value !== '' && Number(value) < 0;
+ });
+ if (invalidField) {
+ this.$message.warning(`第${index + 1}行${invalidField[1]}不能小于0`);
+ return false;
+ }
+ }
+ return true;
+ },
+ validateTaskInfoForm(row = {}) {
+ if (this.isTaskFullMode && !this.validateTaskCargoRows()) {
+ return false;
+ }
+ const requiredFields = this.config.transportFormRequiredFields || [
+ ['carrierType', '承运类型'],
+ ['vehicleNo', '车牌号'],
+ ];
+ if (row.carrierType === '承运商') {
+ requiredFields.push(['carrierName', '承运商']);
+ }
+ if (row.carrierType === '自运') {
+ requiredFields.push(['driverPhone', '手机号']);
+ }
+ const emptyField = requiredFields.find(([prop]) => this.isBillingFieldEmpty(row[prop]));
+ if (emptyField) {
+ this.$message.warning(`请选择或输入${emptyField[1]}`);
+ return false;
+ }
+ const numberFields = [
+ ['quantity', '数量'],
+ ['mileage', '里程'],
+ ['unitPrice', '单价'],
+ ['otherFeeTotal', '其他运费合计'],
+ ];
+ const invalidField = numberFields.find(([prop]) => {
+ const value = row[prop];
+ return value !== undefined && value !== null && value !== '' && Number(value) < 0;
+ });
+ if (invalidField) {
+ this.$message.warning(`${invalidField[1]}不能小于0`);
+ return false;
+ }
+ if (
+ row.estimatedStartTime &&
+ row.estimatedEndTime &&
+ row.estimatedEndTime < row.estimatedStartTime
+ ) {
+ this.$message.warning('预计完成日期不能早于预计发货日期');
+ return false;
+ }
+ if (row.taskRemark && row.taskRemark.length > 200) {
+ this.$message.warning('任务备注不能超过200个字符');
+ return false;
+ }
+ return true;
+ },
+ syncTaskInfoToRow(row) {
+ row.taskEntryMode = row.taskEntryMode || 'simple';
+ const taskInfo = {
+ taskEntryMode: row.taskEntryMode,
+ carrierType: row.carrierType,
+ carrierId: row.carrierId,
+ carrierName: row.carrierName,
+ driverId: row.driverId,
+ driverName: row.driverName,
+ driverPhone: row.driverPhone,
+ vehicleNo: row.vehicleNo,
+ trailerVehicleNo: row.trailerVehicleNo,
+ escortName: row.escortName,
+ escortPhone: row.escortPhone,
+ mileage: row.mileage,
+ estimatedStartTime: row.estimatedStartTime,
+ estimatedEndTime: row.estimatedEndTime,
+ unitPrice: row.unitPrice,
+ priceUnit: row.priceUnit,
+ otherFeeTotal: row.otherFeeTotal,
+ taskRemark: row.taskRemark,
+ };
+ row.taskInfoJson = JSON.stringify(taskInfo);
+ row.carrierJson = JSON.stringify([
+ {
+ carrierType: row.carrierType,
+ carrierId: row.carrierId,
+ carrierName: row.carrierName,
+ driverId: row.driverId,
+ driverName: row.driverName,
+ driverPhone: row.driverPhone,
+ vehicleNo: row.vehicleNo,
+ trailerVehicleNo: row.trailerVehicleNo,
+ escortName: row.escortName,
+ escortPhone: row.escortPhone,
+ mileage: row.mileage,
+ taskEntryMode: row.taskEntryMode,
+ },
+ ]);
+ const goodsRows = this.isTaskFullMode
+ ? this.transportCargoRows.map(item => this.normalizeTransportCargoRow(item))
+ : [
+ {
+ cargoType: row.cargoType,
+ cargoTypeCode: row.cargoTypeCode,
+ cargoTypePath: row.cargoTypePath,
+ cargoName: row.cargoName,
+ specification: row.specification,
+ model: row.model,
+ quantity: row.quantity,
+ quantityUnit: row.quantityUnit,
+ mileage: row.mileage,
+ unitPrice: row.unitPrice,
+ priceUnit: row.priceUnit,
+ remark: row.taskRemark,
+ },
+ ];
+ row.goodsJson = JSON.stringify(goodsRows);
+ const firstGoodsRow =
+ goodsRows.find(
+ item =>
+ !this.isBillingFieldEmpty(item.cargoName) || !this.isBillingFieldEmpty(item.cargoType)
+ ) ||
+ goodsRows[0] ||
+ {};
+ row.cargoType = firstGoodsRow.cargoType || row.cargoType || '';
+ row.cargoTypeCode = firstGoodsRow.cargoTypeCode || row.cargoTypeCode || '';
+ row.cargoTypePath = firstGoodsRow.cargoTypePath || row.cargoTypePath || [];
+ row.cargoName = firstGoodsRow.cargoName || row.cargoName || '';
+ row.specification = firstGoodsRow.specification || row.specification || '';
+ row.model = firstGoodsRow.model || row.model || '';
+ row.quantity = firstGoodsRow.quantity || row.quantity || '';
+ row.quantityUnit = firstGoodsRow.quantityUnit || row.quantityUnit || '';
+ row.mileage = firstGoodsRow.mileage || row.mileage || '';
+ row.unitPrice = firstGoodsRow.unitPrice || row.unitPrice || '';
+ row.priceUnit = firstGoodsRow.priceUnit || row.priceUnit || '';
+ row.taskRemark = firstGoodsRow.remark || row.taskRemark || '';
+ },
initTransportPlanFormRows() {
if (!this.config.enableTransportPlanForm) return;
this.transportCargoRows = this.parseJsonArray(this.form.goodsJson).map(row =>
@@ -3092,6 +5880,25 @@ export default {
);
this.syncTransportCargoJson();
this.syncCurrentContractOption();
+ this.restoreTransportCargoTypePaths();
+ },
+ restoreTransportCargoTypePaths() {
+ if (!this.transportCargoRows.length) return Promise.resolve();
+ return this.ensureBillingCargoTypeOptions().then(() => {
+ this.transportCargoRows.forEach(row => {
+ if (this.normalizeBillingCargoTypePath(row.cargoTypePath).length) return;
+ const cargoType = this.findBillingCargoTypeByCodeOrName({
+ cargoTypeCode: row.cargoTypeCode,
+ cargoType: row.cargoType,
+ });
+ if (!cargoType?.path) return;
+ row.cargoTypePath = cargoType.path;
+ row.cargoType =
+ this.getBillingCargoTypePathLabels(cargoType.path).at(-1) || row.cargoType;
+ row.cargoTypeCode = cargoType.cargoCode || cargoType.code || row.cargoTypeCode;
+ });
+ this.syncTransportCargoJson();
+ });
},
normalizeTransportCargoRow(row = {}) {
return {
@@ -3112,8 +5919,10 @@ export default {
};
},
syncTransportCargoJson() {
- if (!this.config.enableTransportPlanForm) return;
- this.form.goodsJson = JSON.stringify(this.transportCargoRows);
+ if (!this.config.enableTransportPlanForm && !this.isTaskFullMode) return;
+ this.form.goodsJson = JSON.stringify(
+ this.transportCargoRows.map(row => this.normalizeTransportCargoRow(row))
+ );
},
addTransportCargoRow(index = -1, row = {}) {
const nextRow = this.normalizeTransportCargoRow(row);
@@ -3126,6 +5935,12 @@ export default {
},
removeTransportCargoRow(index) {
this.transportCargoRows.splice(index, 1);
+ if (
+ !this.transportCargoRows.length &&
+ (this.isTaskFullMode || this.config.enableTransportPlanForm)
+ ) {
+ this.transportCargoRows.push(this.normalizeTransportCargoRow());
+ }
this.syncTransportCargoJson();
},
handleTransportCargoQuantityInput(row, value) {
@@ -3142,7 +5957,11 @@ export default {
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.syncTransportCargoJson();
+ this.loadTaskCargoOptionsByPath(path);
},
handleTransportTypeChange(value) {
this.form.transportTypeName = this.getTransportTypeLabel(value);
@@ -3266,10 +6085,7 @@ export default {
this.openTransportMapDialog(target);
},
ensureTransportTypeBeforeAddress() {
- if (
- !this.config.enableTransportPlanForm ||
- !this.isBillingFieldEmpty(this.form.transportType)
- ) {
+ if (!this.shippingInfoFormEnabled || !this.isBillingFieldEmpty(this.form.transportType)) {
return true;
}
this.$message.warning('请先选择运输方式');
@@ -4065,6 +6881,22 @@ export default {
handleBatchDownload() {
this.attachmentRows.forEach(row => this.downloadAttachment(row));
},
+ handleDispatchItemAttachmentChange(list) {
+ const userName = this.userInfo?.realName || this.userInfo?.userName || '';
+ const uploadTime = this.$dayjs().format('YYYY-MM-DD HH:mm:ss');
+ this.dispatchItemAttachmentRows = (list || []).map(item => ({
+ ...item,
+ description: item.description || '',
+ uploadUserName: item.uploadUserName || userName,
+ uploadTime: item.uploadTime || uploadTime,
+ }));
+ },
+ removeDispatchItemAttachment(index) {
+ this.dispatchItemAttachmentRows.splice(index, 1);
+ },
+ handleDispatchItemBatchDownload() {
+ this.dispatchItemAttachmentRows.forEach(row => this.downloadAttachment(row));
+ },
handleContractFileChange(list) {
const userName = this.userInfo?.realName || this.userInfo?.userName || '';
const uploadTime = this.$dayjs().format('YYYY-MM-DD HH:mm:ss');
@@ -4120,7 +6952,11 @@ export default {
return this.billingMatchRegionRequest;
},
ensureBillingCargoTypeOptions() {
- if (!this.config.enableBillingPlan && !this.config.enableTransportPlanForm) {
+ if (
+ !this.config.enableBillingPlan &&
+ !this.config.enableTransportPlanForm &&
+ !this.taskInfoFormEnabled
+ ) {
return Promise.resolve([]);
}
if (this.billingCargoTypeOptions.length) {
@@ -4923,10 +7759,309 @@ export default {
});
},
handleImport() {
+ if (this.config.enableWaybillImport) {
+ this.waybillImportBox = true;
+ return;
+ }
+ if (this.config.enableTransportPlanImport) {
+ this.transportPlanImportBox = true;
+ this.loadProjectOptions();
+ return;
+ }
const column = this.findColumn(this.excelOptionConfig.column, 'excelFile');
column.action = this.config.importUrl;
openImportDialog(this, this.config.title, () => this.onLoad(this.page, this.query));
},
+ resetTransportPlanImport() {
+ this.transportPlanImportForm = {
+ projectId: '',
+ projectName: '',
+ customerName: '',
+ contractId: '',
+ contractName: '',
+ file: null,
+ };
+ this.transportPlanImportFiles = [];
+ this.transportPlanImportSourceFile = null;
+ this.transportPlanImportRows = [];
+ this.transportPlanImportSelection = [];
+ this.transportPlanImportTab = 'all';
+ this.transportPlanImportEditingKey = '';
+ this.transportPlanImportEditSnapshot = null;
+ this.transportPlanImportContractOptions = [];
+ this.transportPlanImportContractLoading = false;
+ this.$refs.transportPlanImportFormRef?.clearValidate();
+ },
+ handleTransportPlanImportProjectChange(projectId) {
+ const project = this.projectOptions.find(item => String(item.id) === String(projectId));
+ this.transportPlanImportForm.customerName =
+ project?.customerName || project?.customerNames || project?.customer || '';
+ this.transportPlanImportForm.projectName = project?.projectName || '';
+ this.transportPlanImportForm.contractId = '';
+ this.transportPlanImportForm.contractName = '';
+ this.transportPlanImportContractOptions = [];
+ if (!projectId) return;
+ this.transportPlanImportContractLoading = true;
+ getContractList(1, 9999, { projectId, projectName: project?.projectName })
+ .then(res => {
+ this.transportPlanImportContractOptions = extractRecords(res);
+ const contract = this.transportPlanImportContractOptions[0];
+ this.transportPlanImportForm.customerName =
+ this.transportPlanImportForm.customerName || contract?.customerName || '';
+ this.transportPlanImportForm.contractId = contract?.id || '';
+ this.transportPlanImportForm.contractName = contract?.contractName || '';
+ })
+ .finally(() => {
+ this.transportPlanImportContractLoading = false;
+ });
+ },
+ handleTransportPlanImportContractChange(contractId) {
+ const contract = this.transportPlanImportContractOptions.find(
+ item => String(item.id) === String(contractId)
+ );
+ this.transportPlanImportForm.contractName = contract?.contractName || '';
+ },
+ async handleTransportPlanImportFileChange(file, fileList) {
+ const isExcel = /\.(xls|xlsx)$/i.test(file.name || '');
+ if (!isExcel) {
+ this.$message.error('请上传 .xls 或 .xlsx 格式文件');
+ this.transportPlanImportFiles = [];
+ this.transportPlanImportForm.file = null;
+ return;
+ }
+ try {
+ const XLSX = await import('xlsx');
+ const workbook = XLSX.read(await file.raw.arrayBuffer(), {
+ type: 'array',
+ cellDates: false,
+ });
+ const worksheet = workbook.Sheets[workbook.SheetNames[0]];
+ const values = XLSX.utils.sheet_to_json(worksheet, {
+ header: 1,
+ defval: '',
+ raw: false,
+ blankrows: false,
+ });
+ const headers = values[0] || [];
+ const headerMap = headers.reduce((result, label, index) => {
+ const normalized = String(label || '')
+ .replace(/^\*/, '')
+ .trim();
+ if (normalized) result[normalized] = index;
+ return result;
+ }, {});
+ const rows = values.slice(1).reduce((result, cells, index) => {
+ const row = {
+ _key: `${Date.now()}-${index}-${Math.random().toString(36).slice(2)}`,
+ };
+ transportPlanImportColumns.forEach(([label, prop]) => {
+ row[prop] = String(cells[headerMap[label]] ?? '').trim();
+ });
+ if (transportPlanImportColumns.some(([, prop]) => row[prop])) result.push(row);
+ return result;
+ }, []);
+ if (!rows.length) {
+ this.$message.warning('未读取到计划明细数据');
+ this.transportPlanImportFiles = [];
+ this.transportPlanImportForm.file = null;
+ return;
+ }
+ this.transportPlanImportFiles = fileList.slice(-1);
+ this.transportPlanImportSourceFile = file.raw;
+ this.transportPlanImportForm.file = file.raw;
+ this.transportPlanImportRows = rows;
+ this.markTransportPlanImportDuplicates();
+ this.$refs.transportPlanImportFormRef?.validateField('file');
+ this.$message.success(`已读取${rows.length}条计划明细`);
+ } catch (error) {
+ this.transportPlanImportFiles = [];
+ this.transportPlanImportSourceFile = null;
+ this.transportPlanImportForm.file = null;
+ this.transportPlanImportRows = [];
+ this.$message.error('计划明细表读取失败,请检查文件格式');
+ }
+ },
+ handleTransportPlanImportFileRemove() {
+ this.transportPlanImportFiles = [];
+ this.transportPlanImportSourceFile = null;
+ this.transportPlanImportForm.file = null;
+ this.transportPlanImportRows = [];
+ this.transportPlanImportSelection = [];
+ this.transportPlanImportEditingKey = '';
+ this.transportPlanImportEditSnapshot = null;
+ },
+ transportPlanImportDuplicateKey(row) {
+ return transportPlanImportColumns
+ .map(([, prop]) => String(row[prop] || '').trim())
+ .join('\u0001');
+ },
+ markTransportPlanImportDuplicates() {
+ const countMap = this.transportPlanImportRows.reduce((result, row) => {
+ const key = this.transportPlanImportDuplicateKey(row);
+ result[key] = (result[key] || 0) + 1;
+ return result;
+ }, {});
+ this.transportPlanImportRows.forEach(row => {
+ row._duplicate = countMap[this.transportPlanImportDuplicateKey(row)] > 1;
+ });
+ },
+ handleTransportPlanImportSelectionChange(rows) {
+ this.transportPlanImportSelection = rows;
+ },
+ isEditingTransportPlanImportRow(row) {
+ return this.transportPlanImportEditingKey === row._key;
+ },
+ editTransportPlanImportRow(row) {
+ if (this.transportPlanImportEditingKey && this.transportPlanImportEditingKey !== row._key) {
+ this.$message.warning('请先保存或取消当前编辑');
+ return;
+ }
+ this.transportPlanImportEditingKey = row._key;
+ this.transportPlanImportEditSnapshot = { ...row };
+ },
+ saveTransportPlanImportRow() {
+ this.transportPlanImportEditingKey = '';
+ this.transportPlanImportEditSnapshot = null;
+ this.markTransportPlanImportDuplicates();
+ },
+ cancelTransportPlanImportRow(row) {
+ Object.assign(row, this.transportPlanImportEditSnapshot || {});
+ this.transportPlanImportEditingKey = '';
+ this.transportPlanImportEditSnapshot = null;
+ this.markTransportPlanImportDuplicates();
+ },
+ removeTransportPlanImportRow(row) {
+ this.transportPlanImportRows = this.transportPlanImportRows.filter(
+ item => item._key !== row._key
+ );
+ this.transportPlanImportSelection = this.transportPlanImportSelection.filter(
+ item => item._key !== row._key
+ );
+ this.markTransportPlanImportDuplicates();
+ },
+ removeSelectedTransportPlanImportRows() {
+ const selectedKeys = new Set(this.transportPlanImportSelection.map(row => row._key));
+ this.transportPlanImportRows = this.transportPlanImportRows.filter(
+ row => !selectedKeys.has(row._key)
+ );
+ this.transportPlanImportSelection = [];
+ this.markTransportPlanImportDuplicates();
+ },
+ validateTransportPlanImportRows() {
+ if (!this.transportPlanImportRows.length) {
+ this.$message.warning('请保留至少一条计划明细');
+ return false;
+ }
+ const requiredFields = [
+ ['planName', '计划名称'],
+ ['transportType', '运输方式'],
+ ['planStartDate', '计划开始日期'],
+ ['planEndDate', '计划结束日期'],
+ ['cargoType', '货物类型'],
+ ['departureAddress', '发货地址'],
+ ['arrivalAddress', '收货地址'],
+ ];
+ for (const [index, row] of this.transportPlanImportRows.entries()) {
+ const emptyField = requiredFields.find(([prop]) => !String(row[prop] || '').trim());
+ if (emptyField) {
+ this.$message.warning(`第${index + 1}行${emptyField[1]}不能为空`);
+ 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;
+ };
+ if (!isValidDate(row.planStartDate) || !isValidDate(row.planEndDate)) {
+ this.$message.warning(`第${index + 1}行计划日期格式必须为 YYYY-MM-DD`);
+ return false;
+ }
+ if (row.planEndDate < row.planStartDate) {
+ this.$message.warning(`第${index + 1}行计划结束日期不能早于计划开始日期`);
+ return false;
+ }
+ if (
+ row.quantity !== '' &&
+ (!Number.isFinite(Number(row.quantity)) || Number(row.quantity) < 0)
+ ) {
+ this.$message.warning(`第${index + 1}行数量必须为非负数`);
+ return false;
+ }
+ }
+ return true;
+ },
+ async buildTransportPlanImportFile() {
+ const XLSX = await import('xlsx');
+ const headers = transportPlanImportColumns.map(([label]) => {
+ return [
+ '计划名称',
+ '运输方式',
+ '计划开始日期',
+ '计划结束日期',
+ '货物类型',
+ '发货地址',
+ '收货地址',
+ ].includes(label)
+ ? `*${label}`
+ : label;
+ });
+ const values = [
+ headers,
+ ...this.transportPlanImportRows.map(row =>
+ transportPlanImportColumns.map(([, prop]) => String(row[prop] ?? ''))
+ ),
+ ];
+ const workbook = XLSX.utils.book_new();
+ XLSX.utils.book_append_sheet(workbook, XLSX.utils.aoa_to_sheet(values), '运输计划导入模板');
+ const binary = XLSX.write(workbook, { bookType: 'xlsx', type: 'array' });
+ const fileName = (
+ this.transportPlanImportSourceFile?.name || '运输计划导入模板.xlsx'
+ ).replace(/\.xls$/i, '.xlsx');
+ return new File([binary], fileName, {
+ type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
+ });
+ },
+ handleTransportPlanTemplate() {
+ exportBlob(this.config.transportPlanTemplateUrl, {}, { feedback: true }).then(res => {
+ downloadXls(res.data, '运输计划导入模板.xlsx');
+ });
+ },
+ async submitTransportPlanImport() {
+ const valid = await this.$refs.transportPlanImportFormRef?.validate().catch(() => false);
+ if (!valid || this.transportPlanImportLoading) return;
+ if (!this.validateTransportPlanImportRows()) return;
+ if (typeof this.api.importTransportPlan !== 'function') {
+ this.$message.error('运输计划导入接口不可用');
+ return;
+ }
+ this.transportPlanImportLoading = true;
+ try {
+ const file = await this.buildTransportPlanImportFile();
+ const res = await this.api.importTransportPlan({ ...this.transportPlanImportForm, file });
+ const contentType = res.headers?.['content-type'] || res.data?.type || '';
+ if (
+ contentType.includes('application/vnd.ms-excel') ||
+ contentType.includes('spreadsheetml')
+ ) {
+ downloadXls(
+ res.data,
+ `运输计划导入失败明细${this.$dayjs().format('YYYY-MM-DD HH:mm:ss')}.xlsx`
+ );
+ this.$message.warning('部分数据导入失败,已下载失败明细');
+ } else {
+ const text = await res.data.text();
+ const result = text ? JSON.parse(text) : {};
+ if (result.code !== 200) throw new Error(result.msg || '导入失败');
+ this.$message.success('导入完成');
+ }
+ this.transportPlanImportBox = false;
+ this.onLoad(this.page, this.query);
+ } catch (error) {
+ this.$message.error(error.message || '导入失败');
+ } finally {
+ this.transportPlanImportLoading = false;
+ }
+ },
handleCopy(row) {
this.$confirm(`确定复制该${this.config.title}?`, '提示', {
confirmButtonText: '确定',
@@ -4959,10 +8094,28 @@ export default {
});
},
handleCustomOperation(operation, row) {
+ if (operation.action === 'createFromTemplate') {
+ this.api.getDetail(row.id).then(res => {
+ const template = res.data?.data || row;
+ const templateType = template.templateType || template.templateTypeName;
+ const target =
+ templateType === '运单' ? '/business/waybill-manage' : '/business/transport-plan';
+ sessionStorage.setItem(
+ 'business-template-create',
+ JSON.stringify({ target: target.slice('/business/'.length), data: template })
+ );
+ this.$router.push({ path: target, query: { fromTemplate: Date.now().toString() } });
+ });
+ return;
+ }
if (operation.action === 'flow') {
this.handleFlow(row);
return;
}
+ if (operation.action === 'dispatch') {
+ this.openDispatchDialog(row);
+ return;
+ }
const run = value => {
const args = operation.prompt ? [row.id, value] : [row.id];
this.api[operation.action](...args).then(() => {
@@ -5007,6 +8160,400 @@ export default {
}
this.flowBox = true;
},
+ openDispatchDialog(row) {
+ if (!row?.id) {
+ this.$message.warning('运输计划数据异常,无法调度');
+ return;
+ }
+ this.dispatchRow = { ...row };
+ this.dispatchBox = true;
+ this.dispatchLoading = true;
+ this.dispatchRows = [];
+ const request =
+ this.api && typeof this.api.getDetail === 'function'
+ ? this.api.getDetail(row.id)
+ : Promise.resolve({ data: { data: row } });
+ request
+ .then(res => {
+ const detail = {
+ ...row,
+ ...(res?.data?.data || {}),
+ };
+ this.dispatchRow = detail;
+ this.dispatchRows = this.buildDispatchRows(detail);
+ })
+ .finally(() => {
+ this.dispatchLoading = false;
+ });
+ },
+ resetDispatchDialog() {
+ this.dispatchRow = {};
+ this.dispatchRows = [];
+ this.dispatchLoading = false;
+ this.dispatchSaving = '';
+ this.dispatchItemBox = false;
+ this.dispatchItemIndex = -1;
+ this.dispatchItemForm = defaultDispatchRow();
+ this.dispatchItemAttachmentRows = [];
+ },
+ buildDispatchRows(plan = {}) {
+ const goodsRows = this.parseJsonArray(plan.goodsJson);
+ const sourceRows = 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);
+ return {
+ _key: `${plan.id || 'dispatch'}-${index}-${
+ item.id || item.cargoName || item.vehicleNo || 'row'
+ }`,
+ taskEntryMode: item.taskEntryMode || 'simple',
+ transportType: item.transportType || plan.transportType || '',
+ carrierType: item.carrierType || plan.carrierType || '承运商',
+ carrierName: item.carrierName || plan.carrierName || '',
+ driverName: item.driverName || plan.driverName || '',
+ driverPhone: item.driverPhone || plan.driverPhone || '',
+ vehicleNo: item.vehicleNo || plan.vehicleNo || '',
+ trailerVehicleNo: item.trailerVehicleNo || '',
+ escortName: item.escortName || '',
+ escortPhone: item.escortPhone || '',
+ mileage: item.mileage || '',
+ cargoType: item.cargoType || item.goodsType || plan.cargoType || '',
+ cargoName: 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 || '',
+ estimatedStartTime: item.estimatedStartTime || plan.planStartDate || '',
+ estimatedEndTime: item.estimatedEndTime || plan.planEndDate || '',
+ departureAddress: item.departureAddress || plan.departureAddress || '',
+ departureName: item.departureName || plan.departureName || '',
+ departureContact: item.departureContact || plan.departureContact || '',
+ departurePhone: item.departurePhone || plan.departurePhone || '',
+ arrivalAddress: item.arrivalAddress || plan.arrivalAddress || '',
+ 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 || '',
+ attachmentsJson: item.attachmentsJson || '',
+ remark: item.remark || item.taskRemark || plan.remark || '',
+ };
+ },
+ 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 || ''
+ );
+ },
+ openDispatchItemDialog(index = -1, row = defaultDispatchRow()) {
+ const defaultGoods = this.dispatchPlanGoodsRows[0] || {};
+ const baseRow = {
+ ...defaultDispatchRow(),
+ transportType: this.dispatchRow.transportType || '',
+ carrierType: this.dispatchRow.carrierType || '承运商',
+ carrierName: this.dispatchRow.carrierName || '',
+ driverName: this.dispatchRow.driverName || '',
+ driverPhone: this.dispatchRow.driverPhone || '',
+ vehicleNo: this.dispatchRow.vehicleNo || '',
+ cargoName: defaultGoods.cargoName || this.dispatchRow.cargoName || '',
+ cargoType: defaultGoods.cargoType || this.dispatchRow.cargoType || '',
+ quantity:
+ index >= 0
+ ? defaultGoods.quantity || ''
+ : this.formatDispatchQuantity(this.dispatchSummaryRemainingQuantity),
+ quantityUnit: defaultGoods.quantityUnit || '吨',
+ specification: defaultGoods.specification || '',
+ model: defaultGoods.model || '',
+ packageType: defaultGoods.packageType || '',
+ departureName: this.dispatchRow.departureName || '',
+ departureAddress: this.dispatchRow.departureAddress || '',
+ departureContact: this.dispatchRow.departureContact || '',
+ departurePhone: this.dispatchRow.departurePhone || '',
+ arrivalName: this.dispatchRow.arrivalName || '',
+ arrivalAddress: this.dispatchRow.arrivalAddress || '',
+ arrivalContact: this.dispatchRow.arrivalContact || '',
+ arrivalPhone: this.dispatchRow.arrivalPhone || '',
+ };
+ this.dispatchItemIndex = index;
+ this.dispatchItemForm = {
+ ...baseRow,
+ ...(index >= 0 ? row : {}),
+ };
+ this.dispatchItemForm.carrierType = this.dispatchItemForm.carrierType || '承运商';
+ this.dispatchItemAttachmentRows = this.parseJsonArray(
+ index >= 0 ? row.attachmentsJson : this.dispatchRow.attachmentsJson
+ );
+ this.dispatchItemBox = true;
+ },
+ handleDispatchCarrierTypeChange(value) {
+ const nextValue = value || '';
+ if (nextValue === '承运商') {
+ this.dispatchItemForm.trailerVehicleNo = '';
+ this.dispatchItemForm.escortName = '';
+ this.dispatchItemForm.escortPhone = '';
+ } else {
+ this.dispatchItemForm.carrierId = '';
+ this.dispatchItemForm.carrierName = '';
+ }
+ this.dispatchItemForm.carrierType = nextValue;
+ },
+ loadDispatchCarrierOptions() {
+ if (this.taskCarrierLoading) return Promise.resolve([]);
+ this.taskCarrierLoading = true;
+ return getCarrierCustomerList(1, 9999, { customerType: '承运商' })
+ .then(res => {
+ this.taskCarrierOptions = extractRecords(res);
+ return this.taskCarrierOptions;
+ })
+ .finally(() => {
+ this.taskCarrierLoading = false;
+ });
+ },
+ handleDispatchCarrierChange(value) {
+ const carrier = this.taskCarrierOptions.find(item =>
+ [item.customerName, item.carrierName, item.fullName, item.name].some(
+ name => String(name || '') === String(value || '')
+ )
+ );
+ this.dispatchItemForm.carrierId = carrier?.id || '';
+ this.dispatchItemForm.carrierName = value || '';
+ },
+ loadDispatchDriverOptions() {
+ if (this.taskDriverLoading) return Promise.resolve([]);
+ this.taskDriverLoading = true;
+ return getDriverList(1, 9999, {})
+ .then(res => {
+ this.taskDriverOptions = extractRecords(res);
+ return this.taskDriverOptions;
+ })
+ .finally(() => {
+ this.taskDriverLoading = false;
+ });
+ },
+ handleDispatchDriverChange(value) {
+ const driver = this.taskDriverOptions.find(item =>
+ [item.driverName, item.name].some(name => String(name || '') === String(value || ''))
+ );
+ this.dispatchItemForm.driverId = driver?.id || '';
+ this.dispatchItemForm.driverName = value || '';
+ if (driver) {
+ this.dispatchItemForm.driverPhone =
+ driver.mobile ||
+ driver.phone ||
+ driver.driverPhone ||
+ this.dispatchItemForm.driverPhone ||
+ '';
+ }
+ },
+ saveDispatchItem() {
+ const nextRow = {
+ ...this.dispatchItemForm,
+ attachmentsJson: JSON.stringify(this.dispatchItemAttachmentRows),
+ };
+ const otherDispatchedQuantity = this.dispatchRows.reduce((total, row, index) => {
+ if (index === this.dispatchItemIndex) return total;
+ return total + this.parseDispatchQuantity(row.quantity);
+ }, 0);
+ const currentQuantity = this.parseDispatchQuantity(nextRow.quantity);
+ const totalQuantity = this.dispatchSummaryTotalQuantity;
+ if (totalQuantity > 0 && otherDispatchedQuantity + currentQuantity > totalQuantity) {
+ this.$message.warning(
+ `已调度数量合计不能超过总数量${this.formatDispatchQuantity(totalQuantity)}${
+ this.dispatchSummaryUnit
+ }`
+ );
+ return;
+ }
+ if (this.dispatchItemIndex >= 0) {
+ this.dispatchRows.splice(this.dispatchItemIndex, 1, nextRow);
+ } else {
+ this.dispatchRows.push(nextRow);
+ }
+ this.dispatchItemBox = false;
+ this.dispatchItemIndex = -1;
+ this.dispatchItemForm = defaultDispatchRow();
+ this.dispatchItemAttachmentRows = [];
+ },
+ resetDispatchItemDialog() {
+ this.dispatchItemIndex = -1;
+ this.dispatchItemForm = defaultDispatchRow();
+ this.dispatchItemAttachmentRows = [];
+ },
+ removeDispatchRow(index) {
+ this.$confirm('确定删除该调度明细?', '提示', {
+ confirmButtonText: '确定',
+ cancelButtonText: '取消',
+ type: 'warning',
+ }).then(() => {
+ this.dispatchRows.splice(index, 1);
+ });
+ },
+ handleDispatchAddRow() {
+ if (this.dispatchSummaryRemainingQuantity <= 0) {
+ this.$message.warning('剩余数量为0,不能新增调度明细');
+ return;
+ }
+ this.openDispatchItemDialog();
+ },
+ handleDispatchEditRow(row, index) {
+ this.openDispatchItemDialog(index, row);
+ },
+ buildDispatchPayload(mode) {
+ const rows = this.dispatchRows.map(
+ ({ _key, cargoInfo, dispatchUserName, dispatchTime, ...rest }) => rest
+ );
+ 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: JSON.stringify([
+ {
+ cargoName: row.cargoName,
+ cargoType: row.cargoType,
+ quantity: row.quantity,
+ quantityUnit: row.quantityUnit,
+ specification: row.specification,
+ model: row.model,
+ packageType: row.packageType,
+ },
+ ]),
+ })),
+ };
+ },
+ formatDispatchDate(value) {
+ if (!value) return '';
+ const date = this.$dayjs(value);
+ return date.isValid() ? date.format('YYYY-MM-DD') : value;
+ },
+ validateDispatchRows(mode) {
+ if (!this.dispatchRows.length) {
+ this.$message.warning('请先添加调度明细');
+ return false;
+ }
+ if (mode === 'draft') return true;
+ const invalidRow = this.dispatchRows.find(row => {
+ const selfTransport = row.carrierType !== '承运商';
+ return (
+ !row.transportType ||
+ !row.cargoName ||
+ !row.cargoType ||
+ !row.quantity ||
+ !row.quantityUnit ||
+ !row.carrierType ||
+ !row.vehicleNo ||
+ !row.departureAddress ||
+ !row.arrivalAddress ||
+ (row.carrierType === '承运商' && !row.carrierName) ||
+ (selfTransport &&
+ (!row.driverName ||
+ !row.driverPhone ||
+ !row.trailerVehicleNo ||
+ !row.escortName ||
+ !row.escortPhone ||
+ !row.mileage))
+ );
+ });
+ if (invalidRow) {
+ this.$message.warning('请补全调度明细中的必填信息');
+ return false;
+ }
+ return true;
+ },
+ handleDispatchSubmit(mode) {
+ if (!this.dispatchRow?.id || typeof this.api.dispatch !== 'function') {
+ this.$message.warning('运输计划调度接口不可用');
+ return;
+ }
+ if (!this.validateDispatchRows(mode)) {
+ return;
+ }
+ if (mode === 'submit') {
+ this.$confirm(
+ `请确认调度信息,将生成
${this.dispatchRows.length} 条运单,是否继续?`,
+ '提示',
+ {
+ confirmButtonText: '确认',
+ cancelButtonText: '取消',
+ dangerouslyUseHTMLString: true,
+ type: 'warning',
+ }
+ )
+ .then(() => this.submitDispatch(mode))
+ .catch(() => {});
+ return;
+ }
+ this.submitDispatch(mode);
+ },
+ submitDispatch(mode) {
+ this.dispatchSaving = mode;
+ const shouldCompletePlan = mode === 'submit' && this.dispatchSummaryRemainingQuantity <= 0;
+ this.api
+ .dispatch(this.buildDispatchPayload(mode))
+ .then(() => {
+ if (shouldCompletePlan && typeof this.api.complete === 'function') {
+ return this.api.complete(this.dispatchRow.id);
+ }
+ return undefined;
+ })
+ .then(() => {
+ const message =
+ mode === 'draft'
+ ? '生成草稿成功'
+ : shouldCompletePlan
+ ? '确认生成成功,运输计划已完成'
+ : '确认生成成功';
+ this.$message.success(message);
+ this.dispatchBox = false;
+ this.onLoad(this.page, this.query);
+ })
+ .finally(() => {
+ this.dispatchSaving = '';
+ });
+ },
+ dispatchRowKey(row, index) {
+ return row._key || `${index}-${row.vehicleNo || row.cargoInfo || row.dispatchTime || 'row'}`;
+ },
+ dispatchAttachmentLabel(item) {
+ return (
+ item?.originalName ||
+ item?.name ||
+ item?.fileName ||
+ item?.attachmentName ||
+ item?.title ||
+ '附件'
+ );
+ },
+ dispatchAttachmentUrl(item) {
+ return item?.url || item?.fileUrl || item?.downloadUrl || '';
+ },
+ formatDispatchRouteContact(contact = '', phone = '') {
+ return [contact, phone].filter(Boolean).join(' - ');
+ },
+ parseDispatchRoutePoint(row, type) {
+ const prefix = type === 'start' ? 'departure' : 'arrival';
+ const name = row[`${prefix}Name`] || row[`${prefix}Address`] || '';
+ const address = row[`${prefix}Address`] || row[`${prefix}DetailAddress`] || '';
+ const contact = this.formatDispatchRouteContact(
+ row[`${prefix}Contact`],
+ row[`${prefix}Phone`]
+ );
+ return { name, address, contact };
+ },
+ handleDispatchConfirm() {
+ this.handleDispatchSubmit('submit');
+ },
handleChangeRecordFlow(row) {
const processInstanceId = row.processInstanceId || row.processInstanceID || row.procInstId;
if (!processInstanceId) {
@@ -5103,6 +8650,142 @@ export default {
gap: 8px;
}
+ &__task {
+ 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));
+ column-gap: 24px;
+ row-gap: 16px;
+ width: 100%;
+
+ :deep(.el-form-item) {
+ min-width: 0;
+ margin-bottom: 0;
+ }
+
+ :deep(.el-form-item__content) {
+ min-width: 0;
+ }
+
+ :deep(.el-select),
+ :deep(.el-cascader),
+ :deep(.el-date-editor.el-input) {
+ width: 100%;
+ }
+
+ :deep(.el-radio-group) {
+ width: 100%;
+ }
+
+ :deep(.el-radio-button) {
+ flex: 1;
+ }
+
+ :deep(.el-radio-button__inner) {
+ width: 100%;
+ }
+ }
+
+ &__task-form--full {
+ display: block;
+ }
+
+ &__task-row {
+ display: grid;
+ grid-template-columns: repeat(4, minmax(0, 1fr));
+ column-gap: 24px;
+ row-gap: 16px;
+ margin-bottom: 16px;
+ width: 100%;
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+
+ :deep(.el-form-item) {
+ min-width: 0;
+ margin-bottom: 0;
+ }
+
+ :deep(.el-form-item__content) {
+ min-width: 0;
+ }
+
+ :deep(.el-select),
+ :deep(.el-cascader),
+ :deep(.el-date-editor.el-input) {
+ width: 100%;
+ }
+ }
+
+ &__task-span-4 {
+ grid-column: 1 / -1;
+ }
+
+ &__task-span-1 {
+ grid-column: span 1;
+ }
+
+ &__task-carrier-type {
+ :deep(.el-form-item__content) {
+ flex: 0 1 260px;
+ max-width: 260px;
+ }
+ }
+
+ &__task-remark {
+ grid-column: span 3;
+ }
+
+ &__task-note {
+ grid-column: 1 / -1;
+
+ :deep(.el-input) {
+ width: 100%;
+ }
+ }
+
+ &__suffix-select {
+ width: 72px;
+ margin-right: -8px;
+
+ :deep(.el-select__wrapper),
+ :deep(.el-select__wrapper:hover),
+ :deep(.el-select__wrapper.is-focused) {
+ min-height: 24px;
+ padding: 0 4px;
+ border: 0;
+ box-shadow: none !important;
+ }
+ }
+
+ &__suffix-select--wide {
+ width: 104px;
+ }
+
+ &__append-select {
+ width: 92px;
+ margin-right: -8px;
+
+ :deep(.el-select__wrapper),
+ :deep(.el-select__wrapper:hover),
+ :deep(.el-select__wrapper.is-focused) {
+ min-height: 24px;
+ padding: 0 4px;
+ border: 0;
+ box-shadow: none !important;
+ }
+ }
+
:deep(.business-crud-page__goods-info-cell .cell) {
overflow: visible;
line-height: 20px;
@@ -5134,6 +8817,38 @@ export default {
}
}
+ &__dispatch-shipping-form {
+ :deep(.el-form-item) {
+ margin-bottom: 16px;
+ }
+ }
+
+ &__dispatch-shipping-form &__route-address-row {
+ grid-template-columns: 260px minmax(280px, 1fr) 58px minmax(160px, 220px) 72px minmax(
+ 160px,
+ 220px
+ );
+ }
+
+ &__dispatch-item-attachments {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 12px 20px;
+ min-height: 32px;
+ padding: 4px 0 16px 12px;
+ color: #909399;
+ font-size: 14px;
+ }
+
+ &__dispatch-carrier-title {
+ flex-basis: 100%;
+ margin-top: 16px;
+ }
+
+ &__dispatch-task-title {
+ margin-right: auto;
+ }
+
&__route-label {
color: #606266;
font-size: 14px;
@@ -5170,6 +8885,18 @@ export default {
}
}
+ &__cargo-actions {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ gap: 8px;
+ white-space: nowrap;
+
+ .el-button {
+ margin-left: 0;
+ }
+ }
+
:deep(.business-crud-page__full-form-item) {
width: 100%;
max-width: 100%;
@@ -5229,6 +8956,28 @@ export default {
justify-content: flex-end;
}
+ &__freight-form {
+ display: grid;
+ grid-template-columns: repeat(4, minmax(0, 1fr));
+ column-gap: 24px;
+ row-gap: 16px;
+ width: 100%;
+
+ :deep(.el-form-item) {
+ min-width: 0;
+ margin-bottom: 0;
+ }
+
+ :deep(.el-form-item__content) {
+ min-width: 0;
+ }
+
+ :deep(.el-input),
+ :deep(.el-select) {
+ width: 100%;
+ }
+ }
+
&__dialog-pagination {
display: flex;
justify-content: flex-end;
@@ -5376,6 +9125,275 @@ export default {
}
}
+ &__dispatch-dialog {
+ :deep(.el-dialog__header) {
+ display: none;
+ }
+
+ :deep(.el-dialog__body) {
+ padding: 0;
+ }
+ }
+
+ &__dispatch-shell {
+ display: flex;
+ flex-direction: column;
+ min-height: calc(100vh - 140px);
+ }
+
+ &__dispatch-top {
+ padding: 22px 32px 14px;
+ border-bottom: 1px solid #e9edf5;
+ }
+
+ &__dispatch-heading {
+ margin-bottom: 20px;
+ }
+
+ &__dispatch-heading-title {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ gap: 12px;
+ }
+
+ &__dispatch-heading-name {
+ font-size: 22px;
+ font-weight: 600;
+ color: #303133;
+ }
+
+ &__dispatch-heading-split {
+ font-size: 20px;
+ color: #606266;
+ }
+
+ &__dispatch-heading-no {
+ font-size: 15px;
+ color: #303133;
+ }
+
+ &__dispatch-summary {
+ display: grid;
+ grid-template-columns: minmax(0, 1.15fr) minmax(520px, 1fr);
+ gap: 32px;
+ align-items: start;
+ }
+
+ &__dispatch-summary-grid {
+ display: grid;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ column-gap: 48px;
+ row-gap: 18px;
+ }
+
+ &__dispatch-summary-item {
+ min-width: 0;
+ }
+
+ &__dispatch-summary-label {
+ margin-bottom: 8px;
+ font-size: 14px;
+ color: #666;
+ font-weight: 500;
+ }
+
+ &__dispatch-summary-value {
+ min-height: 24px;
+ font-size: 15px;
+ line-height: 1.45;
+ color: #333;
+ word-break: break-word;
+ }
+
+ &__dispatch-summary-item--attachments {
+ grid-column: 1 / -1;
+ }
+
+ &__dispatch-attachment-link {
+ display: inline-block;
+ margin-right: 22px;
+ margin-bottom: 6px;
+ }
+
+ &__dispatch-route {
+ display: grid;
+ grid-template-columns: minmax(0, 1fr) 72px minmax(0, 1fr);
+ gap: 24px;
+ align-items: center;
+ justify-items: center;
+ min-height: 200px;
+ }
+
+ &__dispatch-route-item {
+ display: flex;
+ align-items: flex-start;
+ gap: 16px;
+ max-width: 100%;
+ }
+
+ &__dispatch-route-badge {
+ display: inline-flex;
+ flex: 0 0 auto;
+ align-items: center;
+ justify-content: center;
+ width: 46px;
+ height: 46px;
+ border-radius: 6px;
+ color: #fff;
+ font-size: 16px;
+ font-weight: 600;
+
+ &.is-start {
+ background: #2a92ff;
+ }
+
+ &.is-end {
+ background: #ffb11a;
+ }
+ }
+
+ &__dispatch-route-body {
+ max-width: 340px;
+ text-align: center;
+ }
+
+ &__dispatch-route-title {
+ font-size: 22px;
+ line-height: 1.25;
+ color: #303133;
+ font-weight: 600;
+ }
+
+ &__dispatch-route-address {
+ margin-top: 12px;
+ font-size: 15px;
+ line-height: 1.45;
+ color: #333;
+ }
+
+ &__dispatch-route-contact {
+ margin-top: 10px;
+ font-size: 15px;
+ color: #333;
+ }
+
+ &__dispatch-route-line {
+ width: 72px;
+ height: 4px;
+ background: #3b3b3b;
+ border-radius: 999px;
+ }
+
+ &__dispatch-list {
+ padding: 12px 18px 18px;
+ }
+
+ &__dispatch-list-head {
+ display: flex;
+ align-items: center;
+ gap: 16px;
+ flex-wrap: wrap;
+ padding: 0 0 12px;
+ }
+
+ &__dispatch-list-title {
+ font-size: 18px;
+ font-weight: 600;
+ color: #303133;
+ }
+
+ &__dispatch-list-summary {
+ margin-left: 8px;
+ font-size: 14px;
+ color: #3c3c3c;
+ }
+
+ &__dispatch-table {
+ font-size: 13px;
+
+ :deep(.el-table__header .cell) {
+ font-size: 13px;
+ font-weight: 600;
+ }
+
+ :deep(.cell) {
+ white-space: normal;
+ line-height: 1.35;
+ }
+ }
+
+ &__dispatch-actions {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ gap: 8px;
+ white-space: nowrap;
+
+ .el-button {
+ margin-left: 0;
+ }
+ }
+
+ &__dispatch-footer {
+ display: flex;
+ justify-content: flex-end;
+ gap: 12px;
+ padding-right: 0;
+ }
+
+ &__dispatch-item-dialog {
+ :deep(.el-dialog__body) {
+ padding-top: 20px;
+ }
+ }
+
+ &__dispatch-item-form {
+ width: 100%;
+ }
+
+ &__dispatch-unit-select {
+ width: 104px;
+
+ :deep(.el-select__wrapper),
+ :deep(.el-select__wrapper:hover),
+ :deep(.el-select__wrapper.is-focused) {
+ width: 104px;
+ min-height: 24px;
+ padding: 0 8px;
+ border: 0;
+ box-shadow: none !important;
+ }
+ }
+
+ &__dispatch-item-grid {
+ display: grid;
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ gap: 16px 24px;
+
+ :deep(.el-form-item) {
+ margin-bottom: 0;
+ min-width: 0;
+ }
+
+ &--compact {
+ grid-template-columns: repeat(4, minmax(0, 1fr));
+ }
+ }
+
+ &__dispatch-item-span-2 {
+ grid-column: span 2;
+ }
+
+ &__dispatch-item-span-3 {
+ grid-column: span 3;
+ }
+
+ &__dispatch-item-carrier-type {
+ grid-column: 1 / -1;
+ width: calc((100% - 72px) / 4);
+ }
+
&__billing-form-row {
display: grid;
grid-template-columns: minmax(360px, 520px) 1fr;
@@ -5534,6 +9552,85 @@ export default {
}
}
+ &__detail-section {
+ margin-bottom: 20px;
+
+ :deep(.el-descriptions__label) {
+ width: 140px;
+ color: #606266;
+ font-weight: 600;
+ text-align: right;
+ }
+
+ :deep(.el-descriptions__content) {
+ min-height: 42px;
+ white-space: pre-wrap;
+ word-break: break-word;
+ }
+
+ :deep(.el-table) {
+ border-color: #eff1f7;
+ }
+ }
+
+ &__transport-plan-import-form {
+ padding: 12px 24px 4px;
+
+ .el-select,
+ .el-input {
+ width: 100%;
+ }
+ }
+
+ &__import-file-item {
+ margin-top: 28px;
+
+ :deep(.el-form-item__content) {
+ gap: 16px;
+ }
+ }
+
+ &__import-file-tip {
+ color: #606266;
+ }
+
+ &__import-preview {
+ margin: 8px 24px 0;
+ padding-top: 16px;
+ border-top: 1px solid #eff1f7;
+ }
+
+ &__import-preview-head {
+ display: flex;
+ align-items: center;
+ gap: 24px;
+ margin-bottom: 12px;
+
+ .dialog-section-title {
+ margin-bottom: 0;
+ }
+ }
+
+ &__import-preview-tabs {
+ margin-bottom: 12px;
+ }
+
+ &__import-preview-table {
+ overflow-x: auto;
+
+ :deep(.el-table) {
+ min-width: 1550px;
+ }
+
+ :deep(.el-table__cell) {
+ border-color: #eff1f7;
+ }
+
+ :deep(.el-table__row:nth-child(even) td) {
+ background: #fafafa;
+ }
+ }
+
@media (max-width: 1280px) {
&__route-address-row {
grid-template-columns: 240px minmax(240px, 1fr) 40px 58px minmax(140px, 1fr);
diff --git a/src/views/business/components/waybill-import-dialog.vue b/src/views/business/components/waybill-import-dialog.vue
new file mode 100644
index 0000000..e10af48
--- /dev/null
+++ b/src/views/business/components/waybill-import-dialog.vue
@@ -0,0 +1,167 @@
+
+
+
+
+
+
+
+
+
+
+ 查看编辑删除
+
+
+
+
+
+
+ 查询
+ -
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/business/loading-manage.vue b/src/views/business/loading-manage.vue
new file mode 100644
index 0000000..30386d0
--- /dev/null
+++ b/src/views/business/loading-manage.vue
@@ -0,0 +1,1997 @@
+
+
+
+
+
+
+
+
+
+ visible && loadProjectOptions(searchForm.projectName)"
+ >
+
+
+
+
+ visible && loadCustomerOptions(searchForm.customerName)"
+ >
+
+
+
+
+ visible && loadTransportTypeOptions()"
+ >
+
+
+
+
+
+
+
+
+
+ visible && loadCargoTypeOptions(searchForm.cargoType)"
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ visible && loadDriverOptions(searchForm.driverName)"
+ >
+
+
+
+
+
+
+
+
+
+
+
+ visible && loadCarrierOptions(searchForm.carrierName)"
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ visible && loadPlanOptions(searchForm.planName)"
+ >
+
+
+
+
+ 查询
+ 重置
+ {{ searchExpanded ? '收起筛选' : '展开筛选' }}
+
+
+
+
+
+
+
+ 新建配载
+ 批量导出
+ 批量完成
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ row.loadingNo }}
+
+
+
+
+
+ {{ item }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ statusText(row) }}
+
+
+
+
+
+
+ {{ action.label }}
+
+
+
+
+
+
+
+
+
+
+ 配载基本信息
+
+
+
+
+
+ visible && loadTransportTypeOptions()"
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ visible && loadPlanOptions(dialogForm.planName)"
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 筛选待配载运单
+ 筛选
+
+
+
+
+
+
+
+
+
+
+ visible && loadProjectOptions(candidateQuery.projectName)
+ "
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ visible && loadCustomerOptions(candidateQuery.customerName)
+ "
+ >
+
+
+
+
+
+
+ visible && loadCargoTypeOptions(candidateQuery.cargoType)
+ "
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ visible && loadCarrierOptions(candidateQuery.carrierName)
+ "
+ >
+
+
+
+
+
+
+ visible && loadDriverOptions(candidateQuery.driverName)
+ "
+ >
+
+
+
+
+
+
+ visible && loadTransportTypeOptions()"
+ >
+
+
+
+
+
+ 查询
+ 重置
+ 收起
+
+
+
+
+ 已选择 {{ selectedWaybillRows.length }} 条运单
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 待配载列表
+
+
+
+
+ {{ row.waybillNo }}
+
+
+
+
+
+
+
+ 移除
+
+
+
+
+ 路线信息
+
+
+
+ {{ routeTypeText(index) }}
+ {{ node.address }}
+
+ {{ tag }}
+
+
+
+
+
+ 货物信息
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 任务信息
+
+
+
+
+
+ visible && loadCarrierOptions(dialogForm.carrierName)"
+ >
+
+
+
+
+ visible && loadDriverOptions(dialogForm.driverName)"
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/business/process-config.vue b/src/views/business/process-config.vue
index 7f21472..6f93d30 100644
--- a/src/views/business/process-config.vue
+++ b/src/views/business/process-config.vue
@@ -333,54 +333,48 @@
-
查看
-
-
+
编辑
-
-
+
复制
-
-
+
启用
-
-
+
停用
-
-
+
删除
-
+
diff --git a/src/views/business/project-apply.vue b/src/views/business/project-apply.vue
index 301f96c..a5c8806 100644
--- a/src/views/business/project-apply.vue
+++ b/src/views/business/project-apply.vue
@@ -62,27 +62,25 @@
-
查看
-
-
+
+
编辑
-
-
+
{{ operation.label }}
-
- 删除
+
+ 删除
@@ -507,13 +505,12 @@
- 下载
- 下载
+ 删除删除
@@ -559,9 +556,9 @@
-
+
{{ row.action || '查看' }}
-
+
@@ -652,7 +649,7 @@
- 选择
+ 选择
diff --git a/src/views/flow/manager.vue b/src/views/flow/manager.vue
index 46e4a1d..44fd201 100644
--- a/src/views/flow/manager.vue
+++ b/src/views/flow/manager.vue
@@ -23,27 +23,24 @@
- 变更状态
-
-
+ 流程图
-
-
+ 删除
-
+
{{ row.tenantId === '' ? '通用' : row.tenantId }}
diff --git a/src/views/flow/model.vue b/src/views/flow/model.vue
index 2b56e0d..7bf9392 100644
--- a/src/views/flow/model.vue
+++ b/src/views/flow/model.vue
@@ -36,30 +36,27 @@
- 配置
-
-
+ 部署
-
-
+ 删除
-
+
v{{ row.version }}
diff --git a/src/views/job/jobinfo.vue b/src/views/job/jobinfo.vue
index d237025..f84df53 100644
--- a/src/views/job/jobinfo.vue
+++ b/src/views/job/jobinfo.vue
@@ -35,9 +35,9 @@
- 运 行
-
+
- 服务跳转
-
+
diff --git a/src/views/report/reportlist.vue b/src/views/report/reportlist.vue
index 2fcc7fc..8af35bf 100644
--- a/src/views/report/reportlist.vue
+++ b/src/views/report/reportlist.vue
@@ -23,20 +23,18 @@
- 设计
-
-
+ 预览
-
+
{{ row.name }}
diff --git a/src/views/resource/attach.vue b/src/views/resource/attach.vue
index 80bece2..0dc81c2 100644
--- a/src/views/resource/attach.vue
+++ b/src/views/resource/attach.vue
@@ -37,13 +37,12 @@
- 下载
-
+
{{ formatFileSize(row.attachSize) }}
diff --git a/src/views/resource/oss.vue b/src/views/resource/oss.vue
index 71bd156..7b087e0 100644
--- a/src/views/resource/oss.vue
+++ b/src/views/resource/oss.vue
@@ -31,20 +31,18 @@
- 调试
-
-
+ 启用
-
+
{{ row.statusName }}
diff --git a/src/views/resource/sms.vue b/src/views/resource/sms.vue
index 043793f..0c52fce 100644
--- a/src/views/resource/sms.vue
+++ b/src/views/resource/sms.vue
@@ -31,20 +31,18 @@
- 调试
-
-
+ 启用
-
+
{{ row.statusName }}
diff --git a/src/views/system/authlock.vue b/src/views/system/authlock.vue
index 4c7db05..52d7a01 100644
--- a/src/views/system/authlock.vue
+++ b/src/views/system/authlock.vue
@@ -46,19 +46,16 @@
{{ row.lockEndTime || '永久' }}
- 查看查看
- 解锁
+ >解锁
diff --git a/src/views/system/authlog.vue b/src/views/system/authlog.vue
index 22ab38f..88ad4cd 100644
--- a/src/views/system/authlog.vue
+++ b/src/views/system/authlog.vue
@@ -18,12 +18,10 @@
@on-load="authLogOnLoad"
>
- 查看
+ >查看
{{ row.account }}
diff --git a/src/views/system/dept.vue b/src/views/system/dept.vue
index c78e94e..b98bf12 100644
--- a/src/views/system/dept.vue
+++ b/src/views/system/dept.vue
@@ -32,13 +32,12 @@
- 新增子项
-
+
{{ row.deptCategoryName }}
diff --git a/src/views/system/dict.vue b/src/views/system/dict.vue
index 9dc8956..5de0e1e 100644
--- a/src/views/system/dict.vue
+++ b/src/views/system/dict.vue
@@ -32,13 +32,12 @@
- 字典配置
-
+
{{ row.code }}
@@ -79,13 +78,12 @@
- 新增子项
-
+
{{ row.isSealed === 0 ? '否' : '是' }}
diff --git a/src/views/system/dictbiz.vue b/src/views/system/dictbiz.vue
index e77df2b..74b50a9 100644
--- a/src/views/system/dictbiz.vue
+++ b/src/views/system/dictbiz.vue
@@ -32,13 +32,12 @@
- 字典配置
-
+
{{ row.code }}
@@ -79,13 +78,12 @@
- 新增子项
-
+
{{ row.isSealed === 0 ? '否' : '是' }}
diff --git a/src/views/system/menu.vue b/src/views/system/menu.vue
index fa4b587..852be51 100644
--- a/src/views/system/menu.vue
+++ b/src/views/system/menu.vue
@@ -32,13 +32,12 @@
- 新增子项
-
+
@@ -89,7 +88,7 @@ export default {
index: true,
selection: true,
viewBtn: true,
- menuWidth: 160,
+ menuWidth: 320,
dialogClickModal: false,
column: [
{
@@ -312,8 +311,42 @@ export default {
column.addDisabled = true;
this.$refs.crud.rowAdd();
},
+ normalizeSubmitRow(row = {}, isUpdate = false) {
+ const submitProps = [
+ 'id',
+ 'parentId',
+ 'code',
+ 'name',
+ 'alias',
+ 'path',
+ 'source',
+ 'component',
+ 'sort',
+ 'category',
+ 'action',
+ 'isOpen',
+ 'isDisplay',
+ 'remark',
+ ];
+ const submitRow = submitProps.reduce((result, prop) => {
+ if (row[prop] !== undefined) {
+ result[prop] = row[prop];
+ }
+ return result;
+ }, {});
+ if (isUpdate) {
+ submitRow.id = row.id || this.currentEditMenuId;
+ } else {
+ delete submitRow.id;
+ }
+ if (submitRow.parentId === '') {
+ submitRow.parentId = 0;
+ }
+ return submitRow;
+ },
rowSave(row, done, loading) {
- add(row).then(
+ const submitRow = this.normalizeSubmitRow(row);
+ add(submitRow).then(
res => {
// 获取新增数据的相关字段
const data = res.data.data;
@@ -332,15 +365,15 @@ export default {
);
},
rowUpdate(row, index, done, loading) {
- row.id = row.id || this.currentEditMenuId;
- update(row).then(
+ const submitRow = this.normalizeSubmitRow(row, true);
+ update(submitRow).then(
() => {
this.$message({
type: 'success',
message: '操作成功!',
});
// 数据回调进行刷新
- done(row);
+ done({ ...row, ...submitRow });
},
error => {
window.console.log(error);
@@ -417,6 +450,7 @@ export default {
this.initData();
}
if (type === 'add') {
+ this.form.id = undefined;
this.form.sort = 1;
this.form.isDisplay = 1;
}
diff --git a/src/views/system/param.vue b/src/views/system/param.vue
index a5355e7..ff5a7ce 100644
--- a/src/views/system/param.vue
+++ b/src/views/system/param.vue
@@ -83,27 +83,24 @@
- 查看
-
-
+ 编辑
-
-
+ 删除
-
+
diff --git a/src/views/system/tenant.vue b/src/views/system/tenant.vue
index 492ddcd..d81e6d9 100644
--- a/src/views/system/tenant.vue
+++ b/src/views/system/tenant.vue
@@ -115,20 +115,18 @@
- 数据源
-
-
+ 产品包
-
+
{{ row.accountNumber > 0 ? row.accountNumber : '不限制' }}
diff --git a/src/views/system/topmenu.vue b/src/views/system/topmenu.vue
index 867f4ac..a0f846d 100644
--- a/src/views/system/topmenu.vue
+++ b/src/views/system/topmenu.vue
@@ -38,13 +38,12 @@
- 配置
-
+
diff --git a/src/views/system/user.vue b/src/views/system/user.vue
index 4760c1d..bb09a37 100644
--- a/src/views/system/user.vue
+++ b/src/views/system/user.vue
@@ -125,22 +125,20 @@
- 查看
-
-
+ 编辑
-
+
handleDropdownCommand(command, row, index)">
- 更 多
+ 更 多
diff --git a/src/views/tool/code.vue b/src/views/tool/code.vue
index 6cfa1f6..d1a2822 100644
--- a/src/views/tool/code.vue
+++ b/src/views/tool/code.vue
@@ -82,22 +82,20 @@
- 复制
-
-
+ 生成
-
+
diff --git a/src/views/tool/codesetting.vue b/src/views/tool/codesetting.vue
index 72bd391..5fad20c 100644
--- a/src/views/tool/codesetting.vue
+++ b/src/views/tool/codesetting.vue
@@ -27,12 +27,11 @@
- 启 用
-
+
{{ row.status === 1 ? '否' : '是' }}
diff --git a/src/views/tool/formsetting.vue b/src/views/tool/formsetting.vue
index 303fe06..2ae6029 100644
--- a/src/views/tool/formsetting.vue
+++ b/src/views/tool/formsetting.vue
@@ -27,9 +27,9 @@
- 设 计
-
+
-
保存
-
+
diff --git a/src/views/tool/model.vue b/src/views/tool/model.vue
index f25228b..678af5c 100644
--- a/src/views/tool/model.vue
+++ b/src/views/tool/model.vue
@@ -26,14 +26,12 @@
- 模型配置
-
+
{{ row.modelTable }}
diff --git a/src/views/transportCapacity/driver.vue b/src/views/transportCapacity/driver.vue
index 9371a6a..656de4a 100644
--- a/src/views/transportCapacity/driver.vue
+++ b/src/views/transportCapacity/driver.vue
@@ -92,25 +92,23 @@
-
查看
-
-
+
+
修改
-
-
+
{{ row.status === 1 ? '停用' : '启用' }}
-
+
@@ -186,10 +184,15 @@
-
@@ -496,6 +499,7 @@ import {
recognitionTransportCertificates,
} from '@/api/transportCapacity/driver';
import { getDeptTree } from '@/api/system/dept';
+import { getLazyTree } from '@/api/base/region';
import { exportBlob } from '@/api/common';
import { getToken } from '@/utils/auth';
import { getUploadHeaders } from '@/utils/upload';
@@ -510,6 +514,7 @@ const emptyForm = () => ({
gender: '',
nation: '',
education: '',
+ addressRegionPath: [],
addressRegion: '',
address: '',
postList: ['司机'],
@@ -644,6 +649,7 @@ export default {
],
relationOptions: ['父母', '配偶', '子女', '兄弟姐妹', '朋友', '同事'],
organizationOptions: [],
+ regionOptions: [],
formRules: {
driverName: [{ required: true, message: '请输入司机姓名', trigger: 'blur' }],
idCardNo: [{ required: true, message: '请输入身份证号', trigger: 'blur' }],
@@ -709,9 +715,18 @@ export default {
}
return this.driverForm.id ? '修改司机' : '新增司机';
},
+ regionCascaderProps() {
+ return {
+ value: 'id',
+ label: 'title',
+ children: 'children',
+ emitPath: true,
+ };
+ },
},
created() {
this.initDeptTree();
+ this.initRegionOptions();
},
methods: {
hasPermission(code) {
@@ -738,6 +753,117 @@ export default {
});
return result;
},
+ initRegionOptions() {
+ getLazyTree().then(res => {
+ const regions = this.buildRegionTree(res.data.data || []);
+ this.regionOptions = this.extractChinaRegionOptions(regions);
+ this.syncAddressRegionPath();
+ });
+ },
+ buildRegionTree(regions = []) {
+ const flatRegions = [];
+ const collectRegions = list => {
+ (list || []).forEach(item => {
+ flatRegions.push({
+ ...item,
+ children: undefined,
+ });
+ if (item.children?.length) collectRegions(item.children);
+ });
+ };
+ collectRegions(regions);
+
+ const nodeMap = new Map();
+ flatRegions.forEach(item => {
+ const id = item.id === undefined || item.id === null ? item.value : item.id;
+ if (id === undefined || id === null) return;
+ nodeMap.set(String(id), {
+ ...item,
+ id: String(id),
+ parentId:
+ item.parentId === undefined || item.parentId === null ? '' : String(item.parentId),
+ children: [],
+ });
+ });
+
+ const rootNodes = [];
+ nodeMap.forEach(node => {
+ const parent = nodeMap.get(node.parentId);
+ if (parent && parent !== node) {
+ parent.children.push(node);
+ } else {
+ rootNodes.push(node);
+ }
+ });
+ return rootNodes.map(item => this.normalizeRegionTreeNode(item));
+ },
+ normalizeRegionTreeNode(region) {
+ const children = (region.children || []).map(item => this.normalizeRegionTreeNode(item));
+ return {
+ ...region,
+ children: children.length ? children : undefined,
+ leaf: !children.length,
+ };
+ },
+ isChinaRegion(region = {}) {
+ const title = String(region.title || region.name || '').trim();
+ return title === '中国' || title === '中华人民共和国';
+ },
+ extractChinaRegionOptions(regions) {
+ const china = (regions || []).find(item => this.isChinaRegion(item));
+ if (china?.children?.length) return china.children;
+ if (regions.length === 1 && regions[0].children?.length) return regions[0].children;
+ return regions;
+ },
+ findRegionOption(options, value) {
+ for (const item of options || []) {
+ if (String(item.id) === String(value)) return item;
+ const child = this.findRegionOption(item.children, value);
+ if (child) return child;
+ }
+ return null;
+ },
+ findRegionPathByName(options = [], addressRegion = '', parents = []) {
+ const target = String(addressRegion || '').replace(/\s+/g, '');
+ if (!target) return [];
+ for (const item of options || []) {
+ const nextParents = [...parents, item];
+ const nextName = nextParents.map(region => region.title || region.name || '').join('');
+ if (nextName === target) {
+ return nextParents.map(region => region.id);
+ }
+ const childPath = this.findRegionPathByName(item.children, target, nextParents);
+ if (childPath.length) return childPath;
+ }
+ return [];
+ },
+ getAddressRegionLabels(value) {
+ const nodes = this.$refs.addressRegionCascader?.getCheckedNodes?.() || [];
+ if (nodes.length && nodes[0].pathLabels?.length) {
+ return nodes[0].pathLabels;
+ }
+ return (value || [])
+ .map(code => this.findRegionOption(this.regionOptions, code)?.title)
+ .filter(Boolean);
+ },
+ handleAddressRegionChange(value) {
+ if (!value || !value.length) {
+ this.driverForm.addressRegion = '';
+ this.$refs.driverForm?.validateField('addressRegion');
+ return;
+ }
+ this.$nextTick(() => {
+ this.driverForm.addressRegion = this.getAddressRegionLabels(value).join('');
+ this.$refs.driverForm?.validateField('addressRegion');
+ });
+ },
+ syncAddressRegionPath() {
+ if (!this.driverForm.addressRegion || this.driverForm.addressRegionPath?.length) return;
+ const path = this.findRegionPathByName(this.regionOptions, this.driverForm.addressRegion);
+ if (path.length) {
+ this.driverForm.addressRegionPath = path;
+ }
+ },
validateDrivingLicenseEndDate(rule, value, callback) {
if (!this.driverForm.drivingLicenseStartDate) {
callback(new Error('请选择驾驶证有效期起'));
@@ -768,8 +894,12 @@ export default {
this.driverForm = {
...emptyForm(),
...detail,
+ addressRegionPath: Array.isArray(detail.addressRegionPath)
+ ? detail.addressRegionPath
+ : [],
postList: detail.posts ? detail.posts.split(',').filter(Boolean) : [],
};
+ this.syncAddressRegionPath();
this.driverBox = true;
});
},
@@ -817,11 +947,8 @@ export default {
});
},
recognizeDrivingLicense() {
- const objectKeys = [
- this.drivingLicenseUploads.drivingLicenseFront || this.driverForm.drivingLicenseFront,
- this.drivingLicenseUploads.drivingLicenseBack || this.driverForm.drivingLicenseBack,
- ].filter(Boolean);
- if (!objectKeys.length) {
+ const certificates = this.buildDrivingLicenseRecognitionCertificates();
+ if (!certificates.length) {
this.$message.warning('驾驶证图片上传成功,未获取到图片地址,无法自动识别');
return;
}
@@ -830,7 +957,7 @@ export default {
text: '驾驶证识别中',
background: 'rgba(255, 255, 255, 0.7)',
});
- recognitionTransportCertificates(objectKeys)
+ recognitionTransportCertificates(certificates)
.then(res => {
const data = res.data.data || {};
this.applyDrivingLicenseRecognition(data);
@@ -843,8 +970,20 @@ export default {
loading.close();
});
},
+ buildDrivingLicenseRecognitionCertificates() {
+ return ['drivingLicenseFront', 'drivingLicenseBack']
+ .map(prop => {
+ const objectKey = this.drivingLicenseUploads[prop] || this.driverForm[prop];
+ if (!objectKey) return null;
+ return {
+ driverLicenseUrl: objectKey,
+ };
+ })
+ .filter(Boolean);
+ },
applyDrivingLicenseRecognition(data = {}) {
- const driverName = data.driverLicenseName || data.name || this.getOcrValue(data, ['name', '姓名']);
+ const driverName =
+ data.driverLicenseName || data.name || this.getOcrValue(data, ['name', '姓名']);
const idCardNo = String(
data.driverLicenseIdNo ||
data.idNo ||
@@ -1021,6 +1160,7 @@ export default {
qualificationNo: this.driverForm.qualificationNo || this.driverForm.idCardNo,
posts: this.driverForm.postList.join(','),
};
+ delete row.addressRegionPath;
this.submitLoading = true;
submit(row)
.then(() => {
@@ -1183,18 +1323,6 @@ export default {
flex-shrink: 0;
}
- :deep(.avue-crud__search .avue-form__menu),
- :deep(.avue-crud__search .avue-form__menu--right) {
- flex: 0 0 auto;
- width: auto;
- max-width: none;
- }
-
- :deep(.avue-crud__search .avue-form__menu--right) {
- margin-left: 12px;
- justify-content: flex-end;
- }
-
:deep(.el-table th .cell),
:deep(.el-table td .cell) {
white-space: nowrap;
@@ -1217,6 +1345,7 @@ export default {
:deep(.el-date-editor.el-input),
:deep(.el-date-editor.el-input__wrapper),
+ :deep(.el-cascader),
:deep(.el-select),
:deep(.el-input) {
width: 100%;
diff --git a/src/views/transportCapacity/ship.vue b/src/views/transportCapacity/ship.vue
index 3c8d598..1056a35 100644
--- a/src/views/transportCapacity/ship.vue
+++ b/src/views/transportCapacity/ship.vue
@@ -87,30 +87,27 @@
-
修改
-
-
+
综合台账
-
-
+
{{ row.status === 1 ? '停用' : '启用' }}
-
+
diff --git a/src/views/transportCapacity/vehicle.vue b/src/views/transportCapacity/vehicle.vue
index 63154f1..cd7392b 100644
--- a/src/views/transportCapacity/vehicle.vue
+++ b/src/views/transportCapacity/vehicle.vue
@@ -79,30 +79,27 @@
-
查看
-
-
+
修改
-
-
+
{{ row.status === 1 ? '停用' : '启用' }}
-
+
diff --git a/src/views/util/crud-form.vue b/src/views/util/crud-form.vue
index 87550c4..edec9bb 100644
--- a/src/views/util/crud-form.vue
+++ b/src/views/util/crud-form.vue
@@ -6,9 +6,9 @@
add
- edit
-
+
diff --git a/src/views/util/demo/dict-classic.vue b/src/views/util/demo/dict-classic.vue
index 29af2da..3a65543 100644
--- a/src/views/util/demo/dict-classic.vue
+++ b/src/views/util/demo/dict-classic.vue
@@ -34,12 +34,11 @@
{{ row.isSealed === 0 ? '否' : '是' }}
- 新增子项
-
+
diff --git a/src/views/util/demo/dict.vue b/src/views/util/demo/dict.vue
index 0332252..0f7dc8a 100644
--- a/src/views/util/demo/dict.vue
+++ b/src/views/util/demo/dict.vue
@@ -88,13 +88,12 @@
- 新增子项
-
+
{{ row.isSealed === 0 ? '否' : '是' }}
diff --git a/src/views/util/demo/dictbiz.vue b/src/views/util/demo/dictbiz.vue
index 2e1c6e1..38c1653 100644
--- a/src/views/util/demo/dictbiz.vue
+++ b/src/views/util/demo/dictbiz.vue
@@ -88,13 +88,12 @@
- 新增子项
-
+
{{ row.isSealed === 0 ? '否' : '是' }}
diff --git a/src/views/vehicle/credit-score-quantification.vue b/src/views/vehicle/credit-score-quantification.vue
index 8f75f81..4da8b66 100644
--- a/src/views/vehicle/credit-score-quantification.vue
+++ b/src/views/vehicle/credit-score-quantification.vue
@@ -69,38 +69,34 @@
-
编辑
-
-
+
发布
-
-
+
{{ row.status === 1 ? '停用' : '启用' }}
-
-
+
删除
-
+
@@ -150,7 +146,7 @@
- 编辑
+ 编辑
@@ -177,9 +173,9 @@
- 编辑
- 删除编辑
+ 删除
@@ -216,16 +212,15 @@
- 编辑
- 编辑
+
删除
-
+
@@ -271,20 +266,19 @@
分
-