1153 lines
32 KiB
JavaScript
1153 lines
32 KiB
JavaScript
import {
|
||
auditColumns,
|
||
createCrudOption,
|
||
dataSourceOptions,
|
||
phoneRule,
|
||
selectRule,
|
||
textRule,
|
||
waybillStatusOptions,
|
||
withSearchPlaceholders,
|
||
} from './common';
|
||
|
||
const transportTypeDict = {
|
||
dicUrl: '/blade-system/dict-biz/dictionary?code=transport_type',
|
||
props: {
|
||
label: 'dictValue',
|
||
value: 'dictKey',
|
||
},
|
||
};
|
||
|
||
const isEmpty = value => value === undefined || value === null || value === '';
|
||
const normalizeNumericDisplayValue = value => (Number(value) === -1 ? '' : value);
|
||
const formatMoneyDisplay = value => {
|
||
if (isEmpty(value) || Number(value) === -1) return '';
|
||
const number = Number(value);
|
||
if (!Number.isFinite(number)) return '';
|
||
return number.toFixed(2);
|
||
};
|
||
|
||
const parseJsonArray = value => {
|
||
if (Array.isArray(value)) return value;
|
||
if (typeof value !== 'string' || !value.trim()) return [];
|
||
try {
|
||
const result = JSON.parse(value);
|
||
if (Array.isArray(result)) return result;
|
||
if (Array.isArray(result.records)) return result.records;
|
||
if (Array.isArray(result.rows)) return result.rows;
|
||
return [];
|
||
} catch (error) {
|
||
return [];
|
||
}
|
||
};
|
||
|
||
const parseJsonObject = value => {
|
||
if (!value) return {};
|
||
if (typeof value === 'object' && !Array.isArray(value)) return value;
|
||
if (typeof value !== 'string' || !value.trim()) return {};
|
||
try {
|
||
const result = JSON.parse(value);
|
||
return result && typeof result === 'object' && !Array.isArray(result) ? result : {};
|
||
} catch (error) {
|
||
return {};
|
||
}
|
||
};
|
||
|
||
const parseProcessNodes = value => {
|
||
const result = parseJsonArray(value);
|
||
if (result.length) return result;
|
||
if (typeof value === 'string' && value.trim()) {
|
||
try {
|
||
const parsed = JSON.parse(value);
|
||
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
||
return Array.isArray(parsed.nodes) ? parsed.nodes : [];
|
||
}
|
||
} catch (error) {
|
||
return [];
|
||
}
|
||
}
|
||
if (!value || typeof value !== 'object' || Array.isArray(value)) return [];
|
||
return Array.isArray(value.nodes) ? value.nodes : [];
|
||
};
|
||
|
||
const getProcessConfigNodes = row => {
|
||
const sources = [
|
||
row.processJson,
|
||
row.processConfigJson,
|
||
row.projectProcessJson,
|
||
row.processConfig,
|
||
row.projectProcessConfig,
|
||
];
|
||
return sources.flatMap(source => {
|
||
const nodes = parseProcessNodes(source);
|
||
if (nodes.length || !source || typeof source !== 'object' || Array.isArray(source)) {
|
||
return nodes;
|
||
}
|
||
const configuredNodes = parseProcessNodes(source.nodeConfigJson);
|
||
const includedNodes = String(source.includedNodes || '')
|
||
.split(',')
|
||
.map(item => item.trim())
|
||
.filter(Boolean);
|
||
return configuredNodes.filter(
|
||
node =>
|
||
!includedNodes.length ||
|
||
includedNodes.includes(node.name) ||
|
||
includedNodes.includes(node.key)
|
||
);
|
||
});
|
||
};
|
||
|
||
const isAcceptProcessNode = node => node.key === 'accept' || node.name === '接单';
|
||
|
||
/**
|
||
* 过程节点列显示文案。
|
||
*
|
||
* 该列在库里存的是「原始值」,且历史上写入了两种格式:司机打卡推进写的是配置的
|
||
* key(unload / dispatch / load …),配载单新建默认值写的是中文名(接单)。
|
||
* 故这里按两种格式都认一次,认不出就原样显示,不把值吞掉。
|
||
*
|
||
* 运单创建后、司机首次打卡前该字段为空,属正常——显示「-」,避免留白看不出是没值还是没渲染。
|
||
*/
|
||
const formatCurrentProcessNode = row => {
|
||
const value = String(getFirstValue(row, ['currentProcessNode', 'currentNode']) || '').trim();
|
||
if (!value) return '-';
|
||
const node = getProcessConfigNodes(row).find(
|
||
item => item.key === value || item.name === value
|
||
);
|
||
return node?.name || node?.key || value;
|
||
};
|
||
|
||
const isTruthyFlag = value => {
|
||
if (value === true || value === 1) return true;
|
||
if (value === false || value === 0 || value === null || value === undefined || value === '') {
|
||
return false;
|
||
}
|
||
return ['true', '1', 'yes'].includes(String(value).trim().toLowerCase());
|
||
};
|
||
|
||
const requiresDriverAcceptConfirmation = row =>
|
||
getProcessConfigNodes(row).some(node => {
|
||
if (!isAcceptProcessNode(node) || node.enabled === false) return false;
|
||
if (node.confirmMode === 'no_confirm_accept') return false;
|
||
// 与后端一致:是否确认=是 即需接单(不强制依赖 confirmDriver)
|
||
return node.confirmMode === 'yes' || !node.confirmMode;
|
||
});
|
||
|
||
const getDriverRejectReason = row => {
|
||
if (!row) return '';
|
||
const reason =
|
||
row.driverRejectReason ||
|
||
row.driverRefuseReason ||
|
||
row.acceptRejectReason ||
|
||
row.rejectReason ||
|
||
'';
|
||
return String(reason || '').trim();
|
||
};
|
||
|
||
const isDriverRejectedStatus = value => {
|
||
if (value === null || value === undefined || typeof value === 'boolean') return false;
|
||
return [
|
||
'-1',
|
||
'2',
|
||
'reject',
|
||
'rejected',
|
||
'refuse',
|
||
'refused',
|
||
'decline',
|
||
'declined',
|
||
'deny',
|
||
'denied',
|
||
'拒绝',
|
||
'已拒绝',
|
||
'拒绝接单',
|
||
'已拒绝接单',
|
||
].includes(String(value).trim().toLowerCase());
|
||
};
|
||
|
||
const hasDriverRejectRecord = row => {
|
||
if (String(row.driverAcceptStatus || '').toLowerCase() === 'rejected') return true;
|
||
const rejectRecordValues = [
|
||
row.driverRejectTime,
|
||
row.driverRejectedTime,
|
||
row.driverRefuseTime,
|
||
row.acceptRejectTime,
|
||
row.driverRejectReason,
|
||
row.driverRefuseReason,
|
||
row.acceptRejectReason,
|
||
];
|
||
if (rejectRecordValues.some(value => !isEmpty(value))) return true;
|
||
|
||
const driverStatusProps = [
|
||
'driverAcceptStatus',
|
||
'driverAccepted',
|
||
'driverAccept',
|
||
'accepted',
|
||
'isAccepted',
|
||
'acceptStatus',
|
||
'driverResponseStatus',
|
||
'driverOrderStatus',
|
||
'driverAcceptResult',
|
||
'acceptResult',
|
||
];
|
||
if (driverStatusProps.some(prop => isDriverRejectedStatus(row[prop]))) return true;
|
||
|
||
const nodeStatusProps = [
|
||
...driverStatusProps,
|
||
'confirmStatus',
|
||
'executionStatus',
|
||
'nodeStatus',
|
||
'status',
|
||
'result',
|
||
];
|
||
return getProcessConfigNodes(row)
|
||
.filter(isAcceptProcessNode)
|
||
.some(node => nodeStatusProps.some(prop => isDriverRejectedStatus(node[prop])));
|
||
};
|
||
|
||
const isDriverRejectedWaybill = row =>
|
||
(row.requireAccept === true ||
|
||
row.requireAccept === 1 ||
|
||
requiresDriverAcceptConfirmation(row)) &&
|
||
hasDriverRejectRecord(row);
|
||
|
||
const hasDriverAcceptRecord = row => {
|
||
if (String(row.driverAcceptStatus || '').toLowerCase() === 'accepted') return true;
|
||
if (['rejected', 'pending'].includes(String(row.driverAcceptStatus || '').toLowerCase())) {
|
||
return false;
|
||
}
|
||
const recordValues = [
|
||
row.driverAcceptTime,
|
||
row.driverAcceptedTime,
|
||
row.acceptTime,
|
||
row.driverAcceptId,
|
||
row.driverAcceptedBy,
|
||
row.acceptUserId,
|
||
row.acceptUserName,
|
||
row.driverAcceptDriverId,
|
||
];
|
||
if (recordValues.some(value => value !== null && value !== undefined && value !== '')) {
|
||
return true;
|
||
}
|
||
const statusValues = [
|
||
row.driverAcceptStatus,
|
||
row.driverAccepted,
|
||
row.driverAccept,
|
||
row.accepted,
|
||
row.isAccepted,
|
||
row.acceptStatus,
|
||
];
|
||
return statusValues.some(value => {
|
||
if (value === true) return true;
|
||
if (value === false || value === null || value === undefined || value === '') return false;
|
||
return ['1', 'true', 'accepted', 'confirmed', 'success', '已接单', '已确认'].includes(
|
||
String(value).toLowerCase()
|
||
);
|
||
});
|
||
};
|
||
|
||
const isInProgressStatus = (row, defaultText) =>
|
||
['processing', 'running', 'in_progress', 'inProgress'].includes(
|
||
String(row.businessStatus || row.status || row.waybillStatus || '')
|
||
) ||
|
||
String(defaultText || '').includes('进行中') ||
|
||
String(row.businessStatusName || row.statusName || '').includes('进行中');
|
||
|
||
const getFirstValue = (row, props) => {
|
||
const prop = props.find(item => !isEmpty(row[item]));
|
||
return prop ? row[prop] : '';
|
||
};
|
||
|
||
const getGoodsRows = row => parseJsonArray(row.goodsList || row.goodsRows || row.goodsJson);
|
||
|
||
const getDetailedGoodsRows = row => {
|
||
const goodsRows = [
|
||
row.goodsJson,
|
||
row.goodsList,
|
||
row.goodsRows,
|
||
row.cargoList,
|
||
row.cargoRows,
|
||
row.goods,
|
||
]
|
||
.map(source => {
|
||
if (source === undefined || source === null || source === '') return [];
|
||
if (Array.isArray(source)) return source;
|
||
if (typeof source === 'object') return [source];
|
||
return parseJsonArray(source);
|
||
})
|
||
.filter(source => source.length)
|
||
.sort((left, right) => right.length - left.length);
|
||
return goodsRows[0] || [];
|
||
};
|
||
|
||
const getBillingRows = row => {
|
||
const freightRows = parseJsonArray(row.freightList || row.freightRows || row.freightJson);
|
||
if (freightRows.length) return freightRows;
|
||
return parseJsonArray(row.billingPlanJson).flatMap(item =>
|
||
Array.isArray(item.rules) ? item.rules : []
|
||
);
|
||
};
|
||
|
||
const joinText = list => list.filter(item => !isEmpty(item)).join('/');
|
||
|
||
const formatGoodsInfo = row => {
|
||
const goodsRows = [
|
||
row.goodsJson,
|
||
row.goodsList,
|
||
row.goodsRows,
|
||
row.cargoList,
|
||
row.cargoRows,
|
||
row.goods,
|
||
]
|
||
.map(source => {
|
||
if (source === undefined || source === null || source === '') return [];
|
||
if (Array.isArray(source)) return source;
|
||
if (typeof source === 'object') return [source];
|
||
return parseJsonArray(source);
|
||
})
|
||
.filter(source => source.length)
|
||
.sort((left, right) => right.length - left.length)[0] || [];
|
||
const text = getFirstValue(row, ['goodsInfo', 'cargoInfo']);
|
||
if (!goodsRows.length) return text || '';
|
||
return goodsRows
|
||
.map(item => {
|
||
const name = getFirstValue(item, ['cargoName', 'goodsName', 'name']);
|
||
const type = getFirstValue(item, ['cargoType', 'goodsType', 'typeName']);
|
||
const quantity = joinText([
|
||
normalizeNumericDisplayValue(
|
||
getFirstValue(item, ['quantity', 'cargoQuantity', 'goodsQuantity'])
|
||
),
|
||
getFirstValue(item, ['quantityUnit', 'goodsQuantityUnit', 'cargoUnit', 'unit']) ||
|
||
getFirstValue(row, ['quantityUnit', 'goodsQuantityUnit', 'cargoUnit', 'unit']),
|
||
]);
|
||
return joinText([name, type, quantity]);
|
||
})
|
||
.filter(Boolean)
|
||
.join('; ') || text || '';
|
||
};
|
||
|
||
const formatGoodsField = (row, props) => {
|
||
const value = getFirstValue(row, props);
|
||
if (value) return value;
|
||
const goods = getGoodsRows(row).find(item => getFirstValue(item, props));
|
||
return goods ? getFirstValue(goods, props) : '';
|
||
};
|
||
|
||
const formatUnitPrice = row => {
|
||
const value = normalizeNumericDisplayValue(getFirstValue(row, ['unitPrice', 'price']));
|
||
const unit = getFirstValue(row, ['priceUnit', 'billingUnit', 'unit']);
|
||
|
||
const goodsRows = getDetailedGoodsRows(row);
|
||
if (goodsRows.length > 1) {
|
||
const billingRows = getBillingRows(row);
|
||
const goodsPrices = goodsRows
|
||
.map((item, index) => {
|
||
const itemPrice = normalizeNumericDisplayValue(
|
||
getFirstValue(item, ['unitPrice', 'price'])
|
||
);
|
||
if (!isEmpty(itemPrice)) return itemPrice;
|
||
return normalizeNumericDisplayValue(
|
||
getFirstValue(billingRows[index] || {}, ['unitPrice', 'price'])
|
||
);
|
||
})
|
||
.filter(itemPrice => !isEmpty(itemPrice));
|
||
const priceKeys = goodsPrices.map(itemPrice => {
|
||
const numericPrice = Number(itemPrice);
|
||
return Number.isFinite(numericPrice) ? String(numericPrice) : String(itemPrice).trim();
|
||
});
|
||
if (new Set(priceKeys).size > 1) return '-';
|
||
}
|
||
|
||
if (!isEmpty(value)) return unit ? `${value}(${unit})` : value;
|
||
const billing = getBillingRows(row).find(
|
||
item => !isEmpty(normalizeNumericDisplayValue(item.unitPrice))
|
||
);
|
||
if (!billing) return '';
|
||
const billingUnit = getFirstValue(billing, ['priceUnit', 'billingUnit', 'unit']);
|
||
const billingPrice = normalizeNumericDisplayValue(billing.unitPrice);
|
||
return billingUnit ? `${billingPrice}(${billingUnit})` : billingPrice;
|
||
};
|
||
|
||
const formatAmount = (row, props) => formatMoneyDisplay(getFirstValue(row, props));
|
||
|
||
const sumAmount = (rows = []) =>
|
||
rows.reduce((sum, item) => {
|
||
const value = getFirstValue(item, ['freightAmount', 'amount', 'totalAmount']);
|
||
return isEmpty(value) ? sum : sum + Number(value || 0);
|
||
}, 0);
|
||
|
||
const calculateFreightTotal = rows => {
|
||
if (!rows.length) return '';
|
||
let hasAmountField = false;
|
||
const total = rows.reduce((sum, item) => {
|
||
const amount = getFirstValue(item, ['freightAmount', 'amount']);
|
||
if (!isEmpty(amount)) {
|
||
hasAmountField = true;
|
||
return sum + Number(amount || 0);
|
||
}
|
||
if (isEmpty(item?.unitPrice) || isEmpty(item?.quantity)) return sum;
|
||
hasAmountField = true;
|
||
return sum + Number(item.unitPrice || 0) * Number(item.quantity || 0);
|
||
}, 0);
|
||
if (!hasAmountField) return '';
|
||
return Number.isFinite(total) ? total.toFixed(2) : '';
|
||
};
|
||
|
||
const formatFreight = row => {
|
||
const value = formatAmount(row, ['freight', 'freightAmount', 'transportFee']);
|
||
if (!isEmpty(value)) return value;
|
||
const freight = parseJsonObject(row.freightJson);
|
||
const freightValue = getFirstValue(freight, ['freightAmount', 'transportFee']);
|
||
if (!isEmpty(freightValue)) return formatMoneyDisplay(freightValue);
|
||
const total = sumAmount(Array.isArray(freight.freightItems) ? freight.freightItems : []);
|
||
return total ? formatMoneyDisplay(total) : '';
|
||
};
|
||
|
||
const formatOtherFeeTotal = row => {
|
||
const value = formatAmount(row, ['otherFeeTotal', 'otherAmount', 'otherFeeAmount']);
|
||
return Number(value) === -1 ? '' : value;
|
||
};
|
||
|
||
const formatFreightTotal = row => {
|
||
const freight = parseJsonObject(row.freightJson);
|
||
const freightItems = Array.isArray(freight.freightItems) ? freight.freightItems : [];
|
||
const goodsRows = getGoodsRows(row);
|
||
const calculated = calculateFreightTotal(freightItems.length ? freightItems : goodsRows);
|
||
const otherFee = getFirstValue(freight, ['otherFreightAmount', 'otherFeeTotal']);
|
||
const otherFeeTotal = !isEmpty(otherFee) ? otherFee : formatOtherFeeTotal(row);
|
||
if (!isEmpty(calculated)) {
|
||
const total = Number(calculated || 0) + Number(otherFeeTotal || 0);
|
||
return Number.isFinite(total) ? total.toFixed(2) : calculated;
|
||
}
|
||
const value = getFirstValue(freight, ['totalFreightAmount', 'totalFreight', 'totalAmount']);
|
||
if (!isEmpty(value)) return formatMoneyDisplay(value);
|
||
return formatAmount(row, ['freightTotal', 'totalFreight', 'totalAmount']);
|
||
};
|
||
|
||
const auditColumn = prop => ({ ...auditColumns.find(item => item.prop === prop) });
|
||
|
||
export const config = {
|
||
title: '运单管理',
|
||
permission: 'waybill_manage',
|
||
importUrl: '/blade-transport/waybill-manage/import-waybill-manage',
|
||
exportUrl: '/blade-transport/waybill-manage/export-waybill-manage',
|
||
exportName: '运单管理',
|
||
defaultForm: {
|
||
carrierType: '承运商',
|
||
transportType: 'road',
|
||
quantityUnit: '吨',
|
||
priceUnit: '元/吨',
|
||
},
|
||
enableAllDept: false,
|
||
enableProjectSelect: true,
|
||
projectQueryParams: {
|
||
approvalStatuses: 'approved,change_approved',
|
||
},
|
||
contractQueryParams: {
|
||
contractCategory: '客户合同',
|
||
},
|
||
templateCreateTarget: 'waybill-manage',
|
||
enableContractSelect: true,
|
||
enableShippingPlanSelect: true,
|
||
enableShippingInfoForm: true,
|
||
enableAttachmentTable: true,
|
||
attachmentTitle: '附件',
|
||
fixedTransportAddressType: true,
|
||
enableTaskInfoForm: true,
|
||
enableWaybillFooterFreightSummary: true,
|
||
enableDraftSave: true,
|
||
saveAsDraft: true,
|
||
skipDraftValidation: true,
|
||
draftStatusProp: 'businessStatus',
|
||
draftStatus: 'draft',
|
||
draftSaveText: '暂存',
|
||
batchDelete: false,
|
||
actionButtonLikeSearch: true,
|
||
createText: '新建运单',
|
||
importText: '导入运单',
|
||
enableWaybillImport: true,
|
||
roadLoading: true,
|
||
roadLoadingText: '公路配载',
|
||
searchRangeMap: {
|
||
startDateRange: ['startDateStart', 'startDateEnd'],
|
||
endDateRange: ['endDateStart', 'endDateEnd'],
|
||
createTimeRange: ['createTimeStart', 'createTimeEnd'],
|
||
},
|
||
actions: ['copy', 'cancel', 'reassign', 'complete', 'batchComplete', 'confirmReturn'],
|
||
detailButton: true,
|
||
statusProp: 'businessStatus',
|
||
statusTextProp: 'businessStatusName',
|
||
formatStatus(row, prop, defaultText) {
|
||
if (prop !== 'businessStatus') return defaultText;
|
||
const status = String(row.businessStatus || '');
|
||
const terminalStatuses = ['draft', 'completed', 'cancelled', 'waiting_dispatch', 'dispatching'];
|
||
const needAccept =
|
||
row.requireAccept === true ||
|
||
row.requireAccept === 1 ||
|
||
requiresDriverAcceptConfirmation(row);
|
||
// 待回单确认由末位打卡产生,司机必然已接单,先于下面的接单校正返回
|
||
if (status === 'pending_return_confirm') return '待回单确认';
|
||
// 需司机接单且尚未接单 → 待执行(含库中仍为 running 的历史数据展示校正)
|
||
if (needAccept && !hasDriverAcceptRecord(row) && !terminalStatuses.includes(status)) {
|
||
return '待执行';
|
||
}
|
||
if (status === 'pending') return '待执行';
|
||
if (status === 'running' || status === 'processing') return '进行中';
|
||
return defaultText;
|
||
},
|
||
canReassign(row) {
|
||
return isDriverRejectedWaybill(row);
|
||
},
|
||
getDriverRejectReason,
|
||
isDriverRejectedWaybill,
|
||
canEdit(row) {
|
||
// 进行中的运单不允许编辑
|
||
if (isInProgressStatus(row, row.businessStatusName)) {
|
||
return false;
|
||
}
|
||
return !isDriverRejectedWaybill(row);
|
||
},
|
||
canCancel(row) {
|
||
// 进行中的运单不允许取消
|
||
return !isInProgressStatus(row, row.businessStatusName);
|
||
},
|
||
deleteStatus: ['draft'],
|
||
editStatus: ['draft', 'pending'],
|
||
};
|
||
|
||
export const option = {
|
||
dialogCustomClass: 'waybill-manage-dialog',
|
||
...createCrudOption(
|
||
withSearchPlaceholders([
|
||
{
|
||
label: '',
|
||
prop: 'basicInfoTitle',
|
||
formslot: true,
|
||
span: 24,
|
||
order: 900,
|
||
hide: true,
|
||
labelWidth: 0,
|
||
},
|
||
{
|
||
label: '运单号',
|
||
prop: 'waybillNo',
|
||
search: true,
|
||
searchOrder: 23,
|
||
minWidth: 150,
|
||
addDisplay: false,
|
||
editDisabled: true,
|
||
},
|
||
{
|
||
label: '配载单号',
|
||
prop: 'loadingNo',
|
||
search: true,
|
||
searchOrder: 5,
|
||
span: 6,
|
||
order: 840,
|
||
minWidth: 140,
|
||
addDisplay: false,
|
||
editDisplay: false,
|
||
},
|
||
{
|
||
label: '总单号',
|
||
prop: 'masterNo',
|
||
search: true,
|
||
searchLabel: '多联总单',
|
||
searchOrder: 6,
|
||
span: 6,
|
||
order: 830,
|
||
minWidth: 140,
|
||
addDisplay: false,
|
||
editDisplay: false,
|
||
},
|
||
{
|
||
label: '项目名称',
|
||
prop: 'projectName',
|
||
formslot: true,
|
||
search: true,
|
||
searchLabel: '项目名称',
|
||
searchPlaceholder: '请选择或输入',
|
||
searchOrder: 22,
|
||
span: 6,
|
||
order: 890,
|
||
minWidth: 150,
|
||
rules: textRule('项目', 100, true),
|
||
},
|
||
{
|
||
label: '客户',
|
||
prop: 'customerName',
|
||
search: true,
|
||
searchLabel: '客户名称',
|
||
searchPlaceholder: '请选择或输入',
|
||
searchOrder: 21,
|
||
minWidth: 150,
|
||
addDisplay: false,
|
||
editDisplay: false,
|
||
viewDisplay: false,
|
||
display: false,
|
||
},
|
||
{
|
||
label: '车牌号/船班号/船次号/班次号',
|
||
prop: 'vehicleNo',
|
||
search: true,
|
||
searchLabel: '车牌号/船班号/船次号/班次号',
|
||
searchOrder: 13,
|
||
minWidth: 210,
|
||
display: false,
|
||
},
|
||
{
|
||
label: '司机',
|
||
prop: 'driverName',
|
||
search: true,
|
||
searchLabel: '司机名称',
|
||
searchPlaceholder: '请选择或输入',
|
||
searchOrder: 14,
|
||
minWidth: 120,
|
||
display: false,
|
||
},
|
||
{
|
||
label: '联系方式',
|
||
prop: 'driverPhone',
|
||
formatter: row => getFirstValue(row, ['driverPhone', 'driverMobile', 'driverTel']),
|
||
minWidth: 140,
|
||
addDisplay: false,
|
||
editDisplay: false,
|
||
viewDisplay: false,
|
||
display: false,
|
||
},
|
||
{
|
||
label: '运输方式',
|
||
prop: 'transportType',
|
||
type: 'select',
|
||
search: true,
|
||
searchOrder: 20,
|
||
span: 6,
|
||
order: 850,
|
||
...transportTypeDict,
|
||
minWidth: 130,
|
||
rules: selectRule('运输方式'),
|
||
},
|
||
{
|
||
label: '过程节点',
|
||
prop: 'currentProcessNode',
|
||
formatter: row => formatCurrentProcessNode(row),
|
||
minWidth: 140,
|
||
addDisplay: false,
|
||
editDisplay: false,
|
||
viewDisplay: false,
|
||
display: false,
|
||
},
|
||
{
|
||
label: '承运类型',
|
||
prop: 'carrierType',
|
||
formatter: row => getFirstValue(row, ['carrierTypeName', 'carrierType']),
|
||
minWidth: 120,
|
||
addDisplay: false,
|
||
editDisplay: false,
|
||
viewDisplay: false,
|
||
display: false,
|
||
},
|
||
{
|
||
label: '承运商',
|
||
prop: 'carrierName',
|
||
search: true,
|
||
searchLabel: '承运商名称',
|
||
searchPlaceholder: '请选择或输入',
|
||
searchOrder: 15,
|
||
minWidth: 150,
|
||
display: false,
|
||
},
|
||
{
|
||
label: '货物信息',
|
||
prop: 'goodsInfo',
|
||
formatter: row => formatGoodsInfo(row),
|
||
minWidth: 260,
|
||
overHidden: true,
|
||
addDisplay: false,
|
||
editDisplay: false,
|
||
viewDisplay: false,
|
||
},
|
||
{
|
||
label: '发货地址',
|
||
prop: 'departureAddress',
|
||
formslot: true,
|
||
search: true,
|
||
searchOrder: 17,
|
||
span: 24,
|
||
order: 790,
|
||
minWidth: 220,
|
||
rules: textRule('发货地址', 255, true),
|
||
},
|
||
{
|
||
label: '到货地址',
|
||
prop: 'arrivalAddress',
|
||
formslot: true,
|
||
search: true,
|
||
searchLabel: '收货地址',
|
||
searchOrder: 16,
|
||
span: 24,
|
||
order: 780,
|
||
minWidth: 220,
|
||
rules: textRule('收货地址', 255, true),
|
||
},
|
||
{
|
||
label: '发货联系人',
|
||
prop: 'departureContact',
|
||
minWidth: 120,
|
||
addDisplay: false,
|
||
editDisplay: false,
|
||
viewDisplay: false,
|
||
},
|
||
{
|
||
label: '收货联系人',
|
||
prop: 'arrivalContact',
|
||
minWidth: 120,
|
||
addDisplay: false,
|
||
editDisplay: false,
|
||
viewDisplay: false,
|
||
},
|
||
{
|
||
label: '单价',
|
||
prop: 'unitPrice',
|
||
formatter: row => formatUnitPrice(row),
|
||
minWidth: 140,
|
||
addDisplay: false,
|
||
editDisplay: false,
|
||
viewDisplay: false,
|
||
},
|
||
{
|
||
label: '运费',
|
||
prop: 'freight',
|
||
formatter: row => formatFreight(row),
|
||
minWidth: 120,
|
||
addDisplay: false,
|
||
editDisplay: false,
|
||
viewDisplay: false,
|
||
},
|
||
{
|
||
label: '其他费用合计',
|
||
prop: 'otherFeeTotal',
|
||
formatter: row => formatOtherFeeTotal(row),
|
||
minWidth: 140,
|
||
addDisplay: false,
|
||
editDisplay: false,
|
||
viewDisplay: false,
|
||
},
|
||
{
|
||
label: '运费合计',
|
||
prop: 'freightTotal',
|
||
formatter: row => formatFreightTotal(row),
|
||
minWidth: 120,
|
||
addDisplay: false,
|
||
editDisplay: false,
|
||
viewDisplay: false,
|
||
},
|
||
{
|
||
label: '客户合同',
|
||
prop: 'contractName',
|
||
formslot: true,
|
||
span: 6,
|
||
order: 880,
|
||
minWidth: 160,
|
||
rules: textRule('客户合同', 100, true),
|
||
},
|
||
{
|
||
label: '计划名称',
|
||
prop: 'planName',
|
||
formslot: true,
|
||
search: true,
|
||
searchPlaceholder: '请选择或输入',
|
||
searchOrder: 7,
|
||
span: 6,
|
||
order: 870,
|
||
minWidth: 150,
|
||
formatter: row => {
|
||
const value = row?.planName;
|
||
if (
|
||
value === undefined ||
|
||
value === null ||
|
||
value === '' ||
|
||
String(value) === '-1' ||
|
||
Number(value) === -1
|
||
) {
|
||
return '';
|
||
}
|
||
return value;
|
||
},
|
||
},
|
||
{
|
||
label: '货物类型',
|
||
prop: 'cargoType',
|
||
search: true,
|
||
searchPlaceholder: '请选择或输入',
|
||
searchOrder: 18,
|
||
formatter: row => formatGoodsField(row, ['cargoType', 'goodsType', 'typeName']),
|
||
minWidth: 130,
|
||
display: false,
|
||
rules: textRule('货物类型', 100, true),
|
||
},
|
||
{
|
||
label: '货物名称',
|
||
prop: 'cargoName',
|
||
search: true,
|
||
searchOrder: 19,
|
||
formatter: row => formatGoodsField(row, ['cargoName', 'goodsName', 'name']),
|
||
minWidth: 150,
|
||
display: false,
|
||
rules: textRule('货物名称', 100, true),
|
||
},
|
||
{
|
||
label: '实际发货时间',
|
||
prop: 'startDate',
|
||
sortable: true,
|
||
type: 'datetime',
|
||
format: 'YYYY-MM-DD HH:mm:ss',
|
||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||
minWidth: 170,
|
||
addDisplay: false,
|
||
editDisplay: false,
|
||
},
|
||
{
|
||
label: '实际完成时间',
|
||
prop: 'endDate',
|
||
sortable: true,
|
||
type: 'datetime',
|
||
format: 'YYYY-MM-DD HH:mm:ss',
|
||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||
minWidth: 170,
|
||
addDisplay: false,
|
||
editDisplay: false,
|
||
},
|
||
{
|
||
label: '原始单号',
|
||
prop: 'originalNo',
|
||
search: true,
|
||
searchOrder: 12,
|
||
span: 6,
|
||
order: 860,
|
||
minWidth: 140,
|
||
},
|
||
{
|
||
label: '运单批次号',
|
||
prop: 'batchNo',
|
||
search: true,
|
||
searchLabel: '运输批次',
|
||
searchOrder: 3,
|
||
minWidth: 140,
|
||
addDisplay: false,
|
||
editDisplay: false,
|
||
viewDisplay: false,
|
||
display: false,
|
||
},
|
||
{
|
||
label: '关联单号',
|
||
prop: 'relationNo',
|
||
search: true,
|
||
searchOrder: 2,
|
||
span: 6,
|
||
order: 820,
|
||
minWidth: 140,
|
||
hide: false,
|
||
addDisplay: true,
|
||
editDisplay: true,
|
||
viewDisplay: true,
|
||
},
|
||
{
|
||
label: '预计发货日期',
|
||
prop: 'estimatedStartTime',
|
||
sortable: true,
|
||
type: 'date',
|
||
format: 'YYYY-MM-DD',
|
||
valueFormat: 'YYYY-MM-DD',
|
||
formatter: row => getFirstValue(row, ['estimatedStartTime', 'planStartDate']),
|
||
minWidth: 170,
|
||
addDisplay: false,
|
||
editDisplay: false,
|
||
viewDisplay: false,
|
||
display: false,
|
||
},
|
||
{
|
||
label: '预计完成日期',
|
||
prop: 'estimatedEndTime',
|
||
sortable: true,
|
||
type: 'date',
|
||
format: 'YYYY-MM-DD',
|
||
valueFormat: 'YYYY-MM-DD',
|
||
formatter: row => getFirstValue(row, ['estimatedEndTime', 'planEndDate']),
|
||
minWidth: 170,
|
||
addDisplay: false,
|
||
editDisplay: false,
|
||
viewDisplay: false,
|
||
display: false,
|
||
},
|
||
{
|
||
label: '备注',
|
||
prop: 'remark',
|
||
type: 'textarea',
|
||
minRows: 2,
|
||
span: 24,
|
||
order: 810,
|
||
minWidth: 180,
|
||
overHidden: true,
|
||
maxlength: 200,
|
||
showWordLimit: true,
|
||
rules: textRule('备注', 200),
|
||
},
|
||
{
|
||
label: '数据来源',
|
||
prop: 'dataSource',
|
||
type: 'select',
|
||
search: true,
|
||
searchOrder: 10,
|
||
dicData: dataSourceOptions,
|
||
minWidth: 120,
|
||
addDisplay: false,
|
||
editDisplay: false,
|
||
viewDisplay: false,
|
||
display: false,
|
||
},
|
||
{
|
||
...auditColumn('createUserName'),
|
||
},
|
||
{
|
||
...auditColumn('createTime'),
|
||
},
|
||
{
|
||
...auditColumn('updateTime'),
|
||
},
|
||
{
|
||
label: '状态',
|
||
prop: 'businessStatus',
|
||
type: 'select',
|
||
slot: true,
|
||
search: true,
|
||
searchLabel: '状态',
|
||
searchOrder: 11,
|
||
dicData: waybillStatusOptions,
|
||
minWidth: 82,
|
||
fixed: 'right',
|
||
addDisplay: false,
|
||
editDisplay: false,
|
||
},
|
||
{
|
||
label: '',
|
||
prop: 'shippingInfoTitle',
|
||
formslot: true,
|
||
span: 24,
|
||
order: 800,
|
||
hide: true,
|
||
labelWidth: 0,
|
||
},
|
||
{
|
||
label: '',
|
||
prop: 'taskInfoTitle',
|
||
formslot: true,
|
||
span: 24,
|
||
order: 770,
|
||
hide: true,
|
||
labelWidth: 0,
|
||
},
|
||
{
|
||
label: '',
|
||
prop: 'taskInfoForm',
|
||
formslot: true,
|
||
span: 24,
|
||
order: 760,
|
||
hide: true,
|
||
labelWidth: '0px',
|
||
className: 'waybill-manage-page__full-form-item',
|
||
},
|
||
{
|
||
label: '实际发货时间',
|
||
prop: 'startDateRange',
|
||
type: 'datetime',
|
||
format: 'YYYY-MM-DD HH:mm:ss',
|
||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||
search: true,
|
||
searchRange: true,
|
||
searchOrder: 9,
|
||
hide: true,
|
||
addDisplay: false,
|
||
editDisplay: false,
|
||
viewDisplay: false,
|
||
display: false,
|
||
},
|
||
{
|
||
label: '实际完成时间',
|
||
prop: 'endDateRange',
|
||
type: 'datetime',
|
||
format: 'YYYY-MM-DD HH:mm:ss',
|
||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||
search: true,
|
||
searchRange: true,
|
||
searchOrder: 8,
|
||
hide: true,
|
||
addDisplay: false,
|
||
editDisplay: false,
|
||
viewDisplay: false,
|
||
display: false,
|
||
},
|
||
{
|
||
label: '创建时间',
|
||
prop: 'createTimeRange',
|
||
type: 'datetime',
|
||
format: 'YYYY-MM-DD HH:mm:ss',
|
||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||
search: true,
|
||
searchRange: true,
|
||
searchOrder: 4,
|
||
hide: true,
|
||
addDisplay: false,
|
||
editDisplay: false,
|
||
viewDisplay: false,
|
||
},
|
||
{
|
||
label: '货物信息',
|
||
prop: 'goodsJson',
|
||
type: 'textarea',
|
||
minRows: 2,
|
||
span: 24,
|
||
hide: true,
|
||
display: false,
|
||
addDisplay: false,
|
||
editDisplay: false,
|
||
viewDisplay: false,
|
||
},
|
||
{
|
||
label: '承运信息',
|
||
prop: 'carrierJson',
|
||
type: 'textarea',
|
||
minRows: 2,
|
||
span: 24,
|
||
hide: true,
|
||
display: false,
|
||
addDisplay: false,
|
||
editDisplay: false,
|
||
viewDisplay: false,
|
||
},
|
||
{
|
||
label: '过程配置',
|
||
prop: 'processJson',
|
||
type: 'textarea',
|
||
minRows: 2,
|
||
span: 24,
|
||
hide: true,
|
||
display: false,
|
||
addDisplay: false,
|
||
editDisplay: false,
|
||
viewDisplay: false,
|
||
},
|
||
{
|
||
label: '运费信息',
|
||
prop: 'freightJson',
|
||
type: 'textarea',
|
||
minRows: 2,
|
||
span: 24,
|
||
hide: true,
|
||
display: false,
|
||
addDisplay: false,
|
||
editDisplay: false,
|
||
viewDisplay: false,
|
||
},
|
||
{
|
||
label: '',
|
||
prop: 'attachmentTitle',
|
||
formslot: true,
|
||
span: 24,
|
||
order: 750,
|
||
hide: true,
|
||
labelWidth: 0,
|
||
},
|
||
{
|
||
label: '',
|
||
prop: 'attachmentsJson',
|
||
type: 'textarea',
|
||
formslot: true,
|
||
span: 24,
|
||
order: 740,
|
||
hide: true,
|
||
labelWidth: '0px',
|
||
className: 'waybill-manage-page__full-form-item',
|
||
},
|
||
{
|
||
label: '所属组织',
|
||
prop: 'deptName',
|
||
hide: true,
|
||
minWidth: 150,
|
||
addDisplay: false,
|
||
editDisplay: false,
|
||
},
|
||
{
|
||
label: '发货地',
|
||
prop: 'departureName',
|
||
hide: true,
|
||
display: false,
|
||
minWidth: 140,
|
||
rules: textRule('发货地', 100, true),
|
||
},
|
||
{
|
||
label: '收货地',
|
||
prop: 'arrivalName',
|
||
hide: true,
|
||
display: false,
|
||
minWidth: 140,
|
||
rules: textRule('收货地', 100, true),
|
||
},
|
||
{
|
||
label: '发货联系方式',
|
||
prop: 'departurePhone',
|
||
hide: true,
|
||
display: false,
|
||
rules: [phoneRule('发货联系方式'), ...textRule('发货联系方式', 50)],
|
||
},
|
||
{
|
||
label: '收货联系方式',
|
||
prop: 'arrivalPhone',
|
||
hide: true,
|
||
display: false,
|
||
rules: [phoneRule('收货联系方式'), ...textRule('收货联系方式', 50)],
|
||
},
|
||
{
|
||
label: '项目ID',
|
||
prop: 'projectId',
|
||
hide: true,
|
||
display: false,
|
||
},
|
||
{
|
||
label: '客户合同ID',
|
||
prop: 'contractId',
|
||
hide: true,
|
||
display: false,
|
||
},
|
||
{
|
||
label: '计划名称ID',
|
||
prop: 'planId',
|
||
hide: true,
|
||
display: false,
|
||
},
|
||
{
|
||
label: '任务录入模式',
|
||
prop: 'taskEntryMode',
|
||
hide: true,
|
||
display: false,
|
||
value: 'simple',
|
||
},
|
||
{
|
||
label: '任务备注',
|
||
prop: 'taskRemark',
|
||
hide: true,
|
||
display: false,
|
||
},
|
||
{
|
||
label: '任务信息',
|
||
prop: 'taskInfoJson',
|
||
hide: true,
|
||
display: false,
|
||
},
|
||
])
|
||
),
|
||
// label 固定宽度(容纳 6 汉字),覆盖 createCrudOption 默认 labelWidth:'auto'
|
||
labelWidth: 100,
|
||
dialogWidth: '96%',
|
||
dialogTop: '4vh',
|
||
menuWidth: 280,
|
||
};
|