🔀 合并 websoft/dev,统一待回单确认与公路配载校验
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -14,7 +14,7 @@ export const cancel = api.cancel;
|
|||||||
export const reassign = api.reassign;
|
export const reassign = api.reassign;
|
||||||
export const complete = api.complete;
|
export const complete = api.complete;
|
||||||
export const batchComplete = api.batchComplete;
|
export const batchComplete = api.batchComplete;
|
||||||
/** 确认回单:待确认回单运单完成后转为已完成 */
|
/** 确认回单:回单节点配成「需业务员确认」时用;后端校验司机打卡节点齐全且无未了结异常后才置完成 */
|
||||||
export const confirmReturn = id =>
|
export const confirmReturn = id =>
|
||||||
request({
|
request({
|
||||||
url: `${baseUrl}/confirm-return`,
|
url: `${baseUrl}/confirm-return`,
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ export const waybillStatusOptions = [
|
|||||||
{ label: '草稿', value: 'draft' },
|
{ label: '草稿', value: 'draft' },
|
||||||
{ label: '待执行', value: 'pending' },
|
{ label: '待执行', value: 'pending' },
|
||||||
{ label: '进行中', value: 'running' },
|
{ label: '进行中', value: 'running' },
|
||||||
{ label: '待确认回单', value: 'pending_return' },
|
{ label: '待回单确认', value: 'pending_return_confirm' },
|
||||||
{ label: '已完成', value: 'completed' },
|
{ label: '已完成', value: 'completed' },
|
||||||
{ label: '已取消', value: 'cancelled' },
|
{ label: '已取消', value: 'cancelled' },
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -471,26 +471,20 @@ export const config = {
|
|||||||
endDateRange: ['endDateStart', 'endDateEnd'],
|
endDateRange: ['endDateStart', 'endDateEnd'],
|
||||||
createTimeRange: ['createTimeStart', 'createTimeEnd'],
|
createTimeRange: ['createTimeStart', 'createTimeEnd'],
|
||||||
},
|
},
|
||||||
actions: ['copy', 'cancel', 'reassign', 'complete', 'batchComplete'],
|
actions: ['copy', 'cancel', 'reassign', 'complete', 'batchComplete', 'confirmReturn'],
|
||||||
detailButton: true,
|
detailButton: true,
|
||||||
statusProp: 'businessStatus',
|
statusProp: 'businessStatus',
|
||||||
statusTextProp: 'businessStatusName',
|
statusTextProp: 'businessStatusName',
|
||||||
formatStatus(row, prop, defaultText) {
|
formatStatus(row, prop, defaultText) {
|
||||||
if (prop !== 'businessStatus') return defaultText;
|
if (prop !== 'businessStatus') return defaultText;
|
||||||
const status = String(row.businessStatus || '');
|
const status = String(row.businessStatus || '');
|
||||||
const terminalStatuses = [
|
const terminalStatuses = ['draft', 'completed', 'cancelled', 'waiting_dispatch', 'dispatching'];
|
||||||
'draft',
|
|
||||||
'completed',
|
|
||||||
'cancelled',
|
|
||||||
'waiting_dispatch',
|
|
||||||
'dispatching',
|
|
||||||
'pending_return',
|
|
||||||
];
|
|
||||||
if (status === 'pending_return') return '待确认回单';
|
|
||||||
const needAccept =
|
const needAccept =
|
||||||
row.requireAccept === true ||
|
row.requireAccept === true ||
|
||||||
row.requireAccept === 1 ||
|
row.requireAccept === 1 ||
|
||||||
requiresDriverAcceptConfirmation(row);
|
requiresDriverAcceptConfirmation(row);
|
||||||
|
// 待回单确认由末位打卡产生,司机必然已接单
|
||||||
|
if (status === 'pending_return_confirm') return '待回单确认';
|
||||||
// 需司机接单且尚未接单 → 待执行(含库中仍为 running 的历史数据展示校正)
|
// 需司机接单且尚未接单 → 待执行(含库中仍为 running 的历史数据展示校正)
|
||||||
if (needAccept && !hasDriverAcceptRecord(row) && !terminalStatuses.includes(status)) {
|
if (needAccept && !hasDriverAcceptRecord(row) && !terminalStatuses.includes(status)) {
|
||||||
return '待执行';
|
return '待执行';
|
||||||
@@ -505,20 +499,14 @@ export const config = {
|
|||||||
getDriverRejectReason,
|
getDriverRejectReason,
|
||||||
isDriverRejectedWaybill,
|
isDriverRejectedWaybill,
|
||||||
canEdit(row) {
|
canEdit(row) {
|
||||||
// 进行中、待确认回单不允许编辑
|
// 进行中的运单不允许编辑
|
||||||
if (isInProgressStatus(row, row.businessStatusName)) {
|
if (isInProgressStatus(row, row.businessStatusName)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (String(row.businessStatus || '') === 'pending_return') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return !isDriverRejectedWaybill(row);
|
return !isDriverRejectedWaybill(row);
|
||||||
},
|
},
|
||||||
canCancel(row) {
|
canCancel(row) {
|
||||||
// 进行中、待确认回单不允许取消
|
// 进行中的运单不允许取消;待回单确认仍可取消(与页面 canCancel 一致)
|
||||||
if (String(row.businessStatus || '') === 'pending_return') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return !isInProgressStatus(row, row.businessStatusName);
|
return !isInProgressStatus(row, row.businessStatusName);
|
||||||
},
|
},
|
||||||
deleteStatus: ['draft'],
|
deleteStatus: ['draft'],
|
||||||
|
|||||||
@@ -622,7 +622,7 @@
|
|||||||
background: #fff;
|
background: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 合同管理新增/编辑弹窗(business-crud-page 封装 → avue-crud,弹窗带 avue-crud__dialog class)
|
// 合同管理新增/编辑弹窗(avue-crud,弹窗带 avue-crud__dialog class)
|
||||||
// 复刻 waybill-manage 方案:接近全屏、压过库内置 .avue-dialog 的 calc(100% - 200px),
|
// 复刻 waybill-manage 方案:接近全屏、压过库内置 .avue-dialog 的 calc(100% - 200px),
|
||||||
// 并解开 body 的 200px 上限、抵消全局 footer 的 -30px 上移
|
// 并解开 body 的 200px 上限、抵消全局 footer 的 -30px 上移
|
||||||
.contract-manage-dialog.el-dialog {
|
.contract-manage-dialog.el-dialog {
|
||||||
|
|||||||
@@ -26,6 +26,35 @@ const uniqueOrgNames = (values = []) => {
|
|||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 候选司机取数上限:超出后只能靠关键字缩小范围
|
||||||
|
const DRIVER_CANDIDATE_MAX_SIZE = 200;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 派单候选司机:租户内的司机档案,按岗位等条件过滤。
|
||||||
|
*
|
||||||
|
* 与组织、承运商都无关——司机档案与组织树、承运商之间没有外键,`organization_name`
|
||||||
|
* 只是自由文本,用它做匹配会让小程序自动建档的司机永远进不了候选。
|
||||||
|
*/
|
||||||
|
export const fetchDriverCandidates = async (query = {}) => {
|
||||||
|
const params = { ...query };
|
||||||
|
const size = Math.min(Math.max(Number(params.size) || 50, 50), DRIVER_CANDIDATE_MAX_SIZE);
|
||||||
|
delete params.size;
|
||||||
|
const res = await getDriverList(1, size, params);
|
||||||
|
return extractRecords(res).slice(0, size);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 司机候选项的展示文本:姓名(手机尾号后 4 位)。
|
||||||
|
*
|
||||||
|
* 只用于下拉渲染。写回表单的仍是姓名本身,别把带尾号的文本当值用。
|
||||||
|
*/
|
||||||
|
export const driverOptionText = (item = {}) => {
|
||||||
|
const name = normalizeOrgName(item.driverName || item.name);
|
||||||
|
const mobile = String(item.mobile || item.phone || item.driverPhone || '').trim();
|
||||||
|
if (!name) return '';
|
||||||
|
return mobile.length >= 4 ? `${name}(${mobile.slice(-4)})` : name;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 解析承运商客商档案上联系人的所属组织(branchName),无联系人时回退客商所属组织。
|
* 解析承运商客商档案上联系人的所属组织(branchName),无联系人时回退客商所属组织。
|
||||||
*/
|
*/
|
||||||
@@ -72,13 +101,6 @@ export const resolveCarrierOrganizationNames = async ({ carrierId, carrierName }
|
|||||||
return orgNames;
|
return orgNames;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const clearCarrierOrganizationCache = (carrier = {}) => {
|
|
||||||
const id = String(carrier.carrierId || '').trim();
|
|
||||||
const name = normalizeOrgName(carrier.carrierName);
|
|
||||||
if (id) orgNameCache.delete(id);
|
|
||||||
if (name) orgNameCache.delete(name);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const matchOrganizationName = (value, orgNames = []) => {
|
export const matchOrganizationName = (value, orgNames = []) => {
|
||||||
const text = normalizeOrgName(value);
|
const text = normalizeOrgName(value);
|
||||||
if (!text || !orgNames.length) return false;
|
if (!text || !orgNames.length) return false;
|
||||||
@@ -90,34 +112,9 @@ const filterByOrganizations = (records = [], orgNames = []) => {
|
|||||||
return records.filter(item => matchOrganizationName(item.organizationName, orgNames));
|
return records.filter(item => matchOrganizationName(item.organizationName, orgNames));
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* 按承运商联系人所属组织筛选司机。
|
|
||||||
* 未选择承运商时返回空列表(需先选承运商)。
|
|
||||||
*/
|
|
||||||
export const fetchDriversByCarrierOrganizations = async (
|
|
||||||
query = {},
|
|
||||||
{ carrierId, carrierName } = {}
|
|
||||||
) => {
|
|
||||||
if (!String(carrierId || '').trim() && !normalizeOrgName(carrierName)) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
const orgNames = await resolveCarrierOrganizationNames({ carrierId, carrierName });
|
|
||||||
if (!orgNames.length) return [];
|
|
||||||
|
|
||||||
const size = Math.max(Number(query.size) || 50, 50);
|
|
||||||
const params = { ...query };
|
|
||||||
delete params.size;
|
|
||||||
// 单组织时用后端模糊条件缩小范围;多组织再前端精确过滤
|
|
||||||
if (orgNames.length === 1) {
|
|
||||||
params.organizationName = orgNames[0];
|
|
||||||
}
|
|
||||||
const res = await getDriverList(1, Math.min(size * 5, 200), params);
|
|
||||||
return filterByOrganizations(extractRecords(res), orgNames).slice(0, size);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 按承运商联系人所属组织筛选车辆。
|
* 按承运商联系人所属组织筛选车辆。
|
||||||
* 未选择承运商时返回空列表。
|
* 未选择承运商时返回空列表(需先选承运商)。
|
||||||
*/
|
*/
|
||||||
export const fetchVehiclesByCarrierOrganizations = async (
|
export const fetchVehiclesByCarrierOrganizations = async (
|
||||||
query = {},
|
query = {},
|
||||||
|
|||||||
@@ -106,6 +106,9 @@ const CERT_SNAPSHOT_GROUPS = {
|
|||||||
fields: [
|
fields: [
|
||||||
{ key: 'realName', label: '真实姓名' },
|
{ key: 'realName', label: '真实姓名' },
|
||||||
{ key: 'idCardNo', label: '身份证号' },
|
{ key: 'idCardNo', label: '身份证号' },
|
||||||
|
{ key: 'birthday', label: '出生年月', type: TYPE_DATE },
|
||||||
|
{ key: 'addressRegion', label: '住址' },
|
||||||
|
{ key: 'address', label: '详细地址' },
|
||||||
{ key: 'idCardFrontUrl', label: '身份证正面照', type: TYPE_IMAGE },
|
{ key: 'idCardFrontUrl', label: '身份证正面照', type: TYPE_IMAGE },
|
||||||
{ key: 'idCardBackUrl', label: '身份证反面照', type: TYPE_IMAGE },
|
{ key: 'idCardBackUrl', label: '身份证反面照', type: TYPE_IMAGE },
|
||||||
{ key: 'idCardValidStart', label: '身份证有效期起', type: TYPE_DATE },
|
{ key: 'idCardValidStart', label: '身份证有效期起', type: TYPE_DATE },
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -294,8 +294,8 @@ export default {
|
|||||||
query: { id: String(customerId), name: '查看客商档案', view: '1' },
|
query: { id: String(customerId), name: '查看客商档案', view: '1' },
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
waybillStatusName(value) { return ({ pending: '待执行', waiting: '待执行', running: '进行中', processing: '进行中', completed: '已完成', cancelled: '已取消' })[value] || value || '-'; },
|
waybillStatusName(value) { return ({ pending: '待执行', waiting: '待执行', running: '进行中', processing: '进行中', pending_return_confirm: '待回单确认', completed: '已完成', cancelled: '已取消' })[value] || value || '-'; },
|
||||||
waybillStatusType(value) { return ({ pending: 'info', waiting: 'info', running: 'warning', processing: 'warning', completed: 'success', cancelled: 'danger' })[value] || 'info'; },
|
waybillStatusType(value) { return ({ pending: 'info', waiting: 'info', running: 'warning', processing: 'warning', pending_return_confirm: 'warning', completed: 'success', cancelled: 'danger' })[value] || 'info'; },
|
||||||
statusName(value) { return ({ waiting_dispatch: '待调度', dispatching: '调度中', completed: '调度完成', closed: '调度关闭' })[value] || value || '-'; },
|
statusName(value) { return ({ waiting_dispatch: '待调度', dispatching: '调度中', completed: '调度完成', closed: '调度关闭' })[value] || value || '-'; },
|
||||||
statusType(value) { return ({ waiting_dispatch: 'warning', dispatching: 'success', completed: 'primary', closed: 'danger' })[value] || 'info'; },
|
statusType(value) { return ({ waiting_dispatch: 'warning', dispatching: 'success', completed: 'primary', closed: 'danger' })[value] || 'info'; },
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -210,7 +210,11 @@
|
|||||||
@mousedown="() => handleDriverFocus(route)"
|
@mousedown="() => handleDriverFocus(route)"
|
||||||
@focus="() => handleDriverFocus(route)"
|
@focus="() => handleDriverFocus(route)"
|
||||||
@select="handleDriverSuggestionSelect"
|
@select="handleDriverSuggestionSelect"
|
||||||
/></el-form-item
|
>
|
||||||
|
<template #default="{ item }">
|
||||||
|
<span>{{ driverOptionText(item) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-autocomplete></el-form-item
|
||||||
></el-col>
|
></el-col>
|
||||||
<el-col :span="6"
|
<el-col :span="6"
|
||||||
><el-form-item
|
><el-form-item
|
||||||
@@ -239,7 +243,11 @@
|
|||||||
@mousedown="() => handleDriverFocus(route)"
|
@mousedown="() => handleDriverFocus(route)"
|
||||||
@focus="() => handleDriverFocus(route)"
|
@focus="() => handleDriverFocus(route)"
|
||||||
@select="handleEscortSuggestionSelect"
|
@select="handleEscortSuggestionSelect"
|
||||||
/></el-form-item
|
>
|
||||||
|
<template #default="{ item }">
|
||||||
|
<span>{{ driverOptionText(item) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-autocomplete></el-form-item
|
||||||
></el-col>
|
></el-col>
|
||||||
<el-col v-if="route.carrierType !== '承运商'" :span="6"
|
<el-col v-if="route.carrierType !== '承运商'" :span="6"
|
||||||
><el-form-item label="押运人手机号"
|
><el-form-item label="押运人手机号"
|
||||||
@@ -316,7 +324,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import * as api from '@/api/business/master-order';
|
import * as api from '@/api/business/master-order';
|
||||||
import { fetchDriversByCarrierOrganizations } from '@/utils/carrier-org-resource';
|
import { fetchDriverCandidates, driverOptionText } from '@/utils/carrier-org-resource';
|
||||||
import { formatContractOptionLabel } from '@/utils/contract';
|
import { formatContractOptionLabel } from '@/utils/contract';
|
||||||
import {
|
import {
|
||||||
currencyUnit,
|
currencyUnit,
|
||||||
@@ -403,6 +411,7 @@ export default {
|
|||||||
this.applySelfOperatedCarrierDefaults();
|
this.applySelfOperatedCarrierDefaults();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
driverOptionText,
|
||||||
loadQuantityUnitOptions() {
|
loadQuantityUnitOptions() {
|
||||||
if (this.quantityUnitLoading) return this.quantityUnitLoadPromise;
|
if (this.quantityUnitLoading) return this.quantityUnitLoadPromise;
|
||||||
this.quantityUnitLoading = true;
|
this.quantityUnitLoading = true;
|
||||||
@@ -944,12 +953,6 @@ export default {
|
|||||||
route.escortPhone = '';
|
route.escortPhone = '';
|
||||||
this.driverOptions = [];
|
this.driverOptions = [];
|
||||||
},
|
},
|
||||||
getRouteCarrierFilter(route = {}) {
|
|
||||||
return {
|
|
||||||
carrierId: route.carrierId || '',
|
|
||||||
carrierName: route.carrierName || '',
|
|
||||||
};
|
|
||||||
},
|
|
||||||
carrierOptionValue(item = {}) {
|
carrierOptionValue(item = {}) {
|
||||||
return item.contractId || item.id || item.value || item.carrierName || '';
|
return item.contractId || item.id || item.value || item.carrierName || '';
|
||||||
},
|
},
|
||||||
@@ -1026,36 +1029,22 @@ export default {
|
|||||||
this.activeSuggestRoute = route;
|
this.activeSuggestRoute = route;
|
||||||
},
|
},
|
||||||
async loadDriverOptions(route = {}, keyword = '') {
|
async loadDriverOptions(route = {}, keyword = '') {
|
||||||
const carrier = this.getRouteCarrierFilter(route);
|
|
||||||
if (!carrier.carrierId && !carrier.carrierName) {
|
|
||||||
this.driverOptions = [];
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
this.driverLoading = true;
|
this.driverLoading = true;
|
||||||
try {
|
try {
|
||||||
this.driverOptions = await fetchDriversByCarrierOrganizations(
|
this.driverOptions = await fetchDriverCandidates({
|
||||||
{ size: 20, ...(keyword ? { driverName: keyword } : {}), posts: '司机' },
|
size: 20,
|
||||||
carrier
|
...(keyword ? { driverName: keyword } : {}),
|
||||||
);
|
posts: '司机',
|
||||||
|
});
|
||||||
return this.driverOptions;
|
return this.driverOptions;
|
||||||
} finally {
|
} finally {
|
||||||
this.driverLoading = false;
|
this.driverLoading = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
fetchDriverSuggestions(queryString, callback) {
|
fetchDriverSuggestions(queryString, callback) {
|
||||||
const route = this.activeSuggestRoute || {};
|
|
||||||
const carrier = this.getRouteCarrierFilter(route);
|
|
||||||
const keyword = String(queryString || '').trim();
|
const keyword = String(queryString || '').trim();
|
||||||
if (!carrier.carrierId && !carrier.carrierName) {
|
|
||||||
callback([]);
|
|
||||||
if (!keyword) this.$message.warning('请先选择承运商');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.driverLoading = true;
|
this.driverLoading = true;
|
||||||
fetchDriversByCarrierOrganizations(
|
fetchDriverCandidates({ size: 20, ...(keyword ? { driverName: keyword } : {}), posts: '司机' })
|
||||||
{ size: 20, ...(keyword ? { driverName: keyword } : {}), posts: '司机' },
|
|
||||||
carrier
|
|
||||||
)
|
|
||||||
.then(records => {
|
.then(records => {
|
||||||
this.driverOptions = records;
|
this.driverOptions = records;
|
||||||
callback(
|
callback(
|
||||||
@@ -1080,19 +1069,13 @@ export default {
|
|||||||
if (drivingVehicle) route.vehicleNo = drivingVehicle;
|
if (drivingVehicle) route.vehicleNo = drivingVehicle;
|
||||||
},
|
},
|
||||||
fetchEscortSuggestions(queryString, callback) {
|
fetchEscortSuggestions(queryString, callback) {
|
||||||
const route = this.activeSuggestRoute || {};
|
|
||||||
const keyword = String(queryString || '').trim();
|
const keyword = String(queryString || '').trim();
|
||||||
const carrier = this.getRouteCarrierFilter(route);
|
|
||||||
if (!carrier.carrierId && !carrier.carrierName) {
|
|
||||||
callback([]);
|
|
||||||
if (!keyword) this.$message.warning('请先选择承运商');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.escortLoading = true;
|
this.escortLoading = true;
|
||||||
fetchDriversByCarrierOrganizations(
|
fetchDriverCandidates({
|
||||||
{ size: 20, ...(keyword ? { driverName: keyword } : {}), posts: '押运员' },
|
size: 20,
|
||||||
carrier
|
...(keyword ? { driverName: keyword } : {}),
|
||||||
)
|
posts: '押运员',
|
||||||
|
})
|
||||||
.then(records => {
|
.then(records => {
|
||||||
callback(
|
callback(
|
||||||
records.map(item => ({
|
records.map(item => ({
|
||||||
|
|||||||
@@ -1870,7 +1870,11 @@
|
|||||||
:fetch-suggestions="fetchTaskDriverSuggestions"
|
:fetch-suggestions="fetchTaskDriverSuggestions"
|
||||||
:loading="taskDriverLoading"
|
:loading="taskDriverLoading"
|
||||||
@select="item => handleTaskDriverSelect('dispatch', item)"
|
@select="item => handleTaskDriverSelect('dispatch', item)"
|
||||||
/>
|
>
|
||||||
|
<template #default="{ item }">
|
||||||
|
<span>{{ driverOptionText(item) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-autocomplete>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item
|
<el-form-item
|
||||||
:label="dispatchItemForm.carrierType === '承运商' ? '手机号' : '司机手机号'"
|
:label="dispatchItemForm.carrierType === '承运商' ? '手机号' : '司机手机号'"
|
||||||
@@ -1893,7 +1897,11 @@
|
|||||||
:fetch-suggestions="fetchTaskEscortSuggestions"
|
:fetch-suggestions="fetchTaskEscortSuggestions"
|
||||||
:loading="taskEscortLoading"
|
:loading="taskEscortLoading"
|
||||||
@select="handleTaskEscortSelect"
|
@select="handleTaskEscortSelect"
|
||||||
/>
|
>
|
||||||
|
<template #default="{ item }">
|
||||||
|
<span>{{ driverOptionText(item) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-autocomplete>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item v-if="dispatchItemForm.carrierType !== '承运商'" label="押运人手机号">
|
<el-form-item v-if="dispatchItemForm.carrierType !== '承运商'" label="押运人手机号">
|
||||||
<el-input v-model="dispatchItemForm.escortPhone" placeholder="请输入" />
|
<el-input v-model="dispatchItemForm.escortPhone" placeholder="请输入" />
|
||||||
@@ -2760,7 +2768,7 @@ import {
|
|||||||
getList as getProjectList,
|
getList as getProjectList,
|
||||||
} from '@/api/business/project-apply';
|
} from '@/api/business/project-apply';
|
||||||
import { getList as getCommonRouteList } from '@/api/business/common-route';
|
import { getList as getCommonRouteList } from '@/api/business/common-route';
|
||||||
import { fetchDriversByCarrierOrganizations } from '@/utils/carrier-org-resource';
|
import { fetchDriverCandidates, driverOptionText } from '@/utils/carrier-org-resource';
|
||||||
import { getDictionary } from '@/api/system/dictbiz';
|
import { getDictionary } from '@/api/system/dictbiz';
|
||||||
import { getDictionary as getSystemDictionary } from '@/api/system/dict';
|
import { getDictionary as getSystemDictionary } from '@/api/system/dict';
|
||||||
import { addressTypeOptions } from '@/option/base/common-address';
|
import { addressTypeOptions } from '@/option/base/common-address';
|
||||||
@@ -3809,6 +3817,7 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
formatContractOptionLabel,
|
formatContractOptionLabel,
|
||||||
|
driverOptionText,
|
||||||
initStandaloneFormPage() {
|
initStandaloneFormPage() {
|
||||||
if (!this.isStandaloneFormPage) return;
|
if (!this.isStandaloneFormPage) return;
|
||||||
const mode = this.$route.query.mode === 'edit' ? 'edit' : 'add';
|
const mode = this.$route.query.mode === 'edit' ? 'edit' : 'add';
|
||||||
@@ -4178,6 +4187,7 @@ export default {
|
|||||||
waiting: ['待执行', 'is-pending'],
|
waiting: ['待执行', 'is-pending'],
|
||||||
processing: ['进行中', 'is-processing'],
|
processing: ['进行中', 'is-processing'],
|
||||||
running: ['进行中', 'is-processing'],
|
running: ['进行中', 'is-processing'],
|
||||||
|
pending_return_confirm: ['待回单确认', 'is-pending'],
|
||||||
completed: ['已完成', 'is-completed'],
|
completed: ['已完成', 'is-completed'],
|
||||||
cancelled: ['已取消', 'is-cancelled'],
|
cancelled: ['已取消', 'is-cancelled'],
|
||||||
};
|
};
|
||||||
@@ -4988,16 +4998,8 @@ export default {
|
|||||||
},
|
},
|
||||||
fetchTaskDriverSuggestions(queryString, callback) {
|
fetchTaskDriverSuggestions(queryString, callback) {
|
||||||
const keyword = String(queryString || '').trim();
|
const keyword = String(queryString || '').trim();
|
||||||
const carrier = this.getDispatchCarrierFilter();
|
|
||||||
if (!carrier.carrierId && !carrier.carrierName) {
|
|
||||||
callback([]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.taskDriverLoading = true;
|
this.taskDriverLoading = true;
|
||||||
fetchDriversByCarrierOrganizations(
|
fetchDriverCandidates({ size: 20, ...(keyword ? { driverName: keyword } : {}), posts: '司机' })
|
||||||
{ size: 20, ...(keyword ? { driverName: keyword } : {}), posts: '司机' },
|
|
||||||
carrier
|
|
||||||
)
|
|
||||||
.then(records => {
|
.then(records => {
|
||||||
this.taskDriverOptions = records;
|
this.taskDriverOptions = records;
|
||||||
callback(
|
callback(
|
||||||
@@ -5017,16 +5019,12 @@ export default {
|
|||||||
},
|
},
|
||||||
fetchTaskEscortSuggestions(queryString, callback) {
|
fetchTaskEscortSuggestions(queryString, callback) {
|
||||||
const keyword = String(queryString || '').trim();
|
const keyword = String(queryString || '').trim();
|
||||||
const carrier = this.getDispatchCarrierFilter();
|
|
||||||
if (!carrier.carrierId && !carrier.carrierName) {
|
|
||||||
callback([]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.taskEscortLoading = true;
|
this.taskEscortLoading = true;
|
||||||
fetchDriversByCarrierOrganizations(
|
fetchDriverCandidates({
|
||||||
{ size: 20, ...(keyword ? { driverName: keyword } : {}), posts: '押运员' },
|
size: 20,
|
||||||
carrier
|
...(keyword ? { driverName: keyword } : {}),
|
||||||
)
|
posts: '押运员',
|
||||||
|
})
|
||||||
.then(records => {
|
.then(records => {
|
||||||
this.taskEscortOptions = records;
|
this.taskEscortOptions = records;
|
||||||
callback(records.map(item => ({ ...item, value: item.driverName || item.name || '' })));
|
callback(records.map(item => ({ ...item, value: item.driverName || item.name || '' })));
|
||||||
@@ -8044,12 +8042,6 @@ export default {
|
|||||||
this.taskDriverOptions = [];
|
this.taskDriverOptions = [];
|
||||||
this.taskEscortOptions = [];
|
this.taskEscortOptions = [];
|
||||||
},
|
},
|
||||||
getDispatchCarrierFilter() {
|
|
||||||
return {
|
|
||||||
carrierId: this.dispatchItemForm.carrierId || '',
|
|
||||||
carrierName: this.dispatchItemForm.carrierName || '',
|
|
||||||
};
|
|
||||||
},
|
|
||||||
handleDispatchCargoQuantityInput(row, value) {
|
handleDispatchCargoQuantityInput(row, value) {
|
||||||
const text = String(value || '').replace(/[^\d.]/g, '');
|
const text = String(value || '').replace(/[^\d.]/g, '');
|
||||||
const parts = text.split('.');
|
const parts = text.split('.');
|
||||||
|
|||||||
@@ -1381,11 +1381,23 @@ const formatOptionalNumber = value => {
|
|||||||
const normalized = normalizeOptionalImportNumber(value);
|
const normalized = normalizeOptionalImportNumber(value);
|
||||||
return normalized === null || normalized === undefined ? '' : normalized;
|
return normalized === null || normalized === undefined ? '' : normalized;
|
||||||
};
|
};
|
||||||
|
// 明细接口返回运单实体字段 startDate/endDate/estimatedStartTime/estimatedEndTime,
|
||||||
|
// 而明细表与编辑回填按导入列字段名 actualStartDate 等读取,重载后在此对齐,否则实际/预计时间列为空。
|
||||||
|
const detailDateAliases = {
|
||||||
|
actualStartDate: 'startDate',
|
||||||
|
actualEndDate: 'endDate',
|
||||||
|
planStartDate: 'estimatedStartTime',
|
||||||
|
planEndDate: 'estimatedEndTime',
|
||||||
|
};
|
||||||
const normalizeDetailRow = row => {
|
const normalizeDetailRow = row => {
|
||||||
const next = { ...(row || {}) };
|
const next = { ...(row || {}) };
|
||||||
OPTIONAL_NUMBER_PROPS.forEach(prop => {
|
OPTIONAL_NUMBER_PROPS.forEach(prop => {
|
||||||
next[prop] = formatOptionalNumber(next[prop]);
|
next[prop] = formatOptionalNumber(next[prop]);
|
||||||
});
|
});
|
||||||
|
Object.entries(detailDateAliases).forEach(([prop, source]) => {
|
||||||
|
if (String(next[prop] ?? '').trim()) return;
|
||||||
|
if (String(next[source] ?? '').trim()) next[prop] = next[source];
|
||||||
|
});
|
||||||
return next;
|
return next;
|
||||||
};
|
};
|
||||||
const normalizeImportRow = row => ({
|
const normalizeImportRow = row => ({
|
||||||
|
|||||||
@@ -1200,7 +1200,11 @@
|
|||||||
:loading="taskDriverLoading"
|
:loading="taskDriverLoading"
|
||||||
:disabled="dialogReadonly"
|
:disabled="dialogReadonly"
|
||||||
@select="handleTaskDriverSelect"
|
@select="handleTaskDriverSelect"
|
||||||
/>
|
>
|
||||||
|
<template #default="{ item }">
|
||||||
|
<span>{{ driverOptionText(item) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-autocomplete>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item
|
<el-form-item
|
||||||
v-if="isRoadTransport"
|
v-if="isRoadTransport"
|
||||||
@@ -1279,7 +1283,11 @@
|
|||||||
:loading="taskEscortLoading"
|
:loading="taskEscortLoading"
|
||||||
:disabled="dialogReadonly"
|
:disabled="dialogReadonly"
|
||||||
@select="handleTaskEscortSelect"
|
@select="handleTaskEscortSelect"
|
||||||
/>
|
>
|
||||||
|
<template #default="{ item }">
|
||||||
|
<span>{{ driverOptionText(item) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-autocomplete>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item v-if="showRoadEscortFields" label="押运人手机号">
|
<el-form-item v-if="showRoadEscortFields" label="押运人手机号">
|
||||||
<el-input
|
<el-input
|
||||||
@@ -1591,8 +1599,15 @@
|
|||||||
<el-link type="primary" v-if="canCancel(row)" @click="handleAction('cancel', row)">
|
<el-link type="primary" v-if="canCancel(row)" @click="handleAction('cancel', row)">
|
||||||
取消
|
取消
|
||||||
</el-link>
|
</el-link>
|
||||||
<el-link type="primary" v-if="canComplete(row)" @click="handleCompleteAction(row)">
|
<el-link type="primary" v-if="canComplete(row)" @click="handleAction('complete', row)">
|
||||||
{{ completeActionLabelFor(row) }}
|
{{ completeActionLabel }}
|
||||||
|
</el-link>
|
||||||
|
<el-link
|
||||||
|
type="primary"
|
||||||
|
v-if="canConfirmReturn(row)"
|
||||||
|
@click="handleAction('confirmReturn', row)"
|
||||||
|
>
|
||||||
|
确认回单
|
||||||
</el-link>
|
</el-link>
|
||||||
<el-link type="primary" v-if="canMaintainMileage(row)" @click="openMileageDialog(row)">
|
<el-link type="primary" v-if="canMaintainMileage(row)" @click="openMileageDialog(row)">
|
||||||
维护里程
|
维护里程
|
||||||
@@ -1653,7 +1668,11 @@
|
|||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
@select="handleReassignDriverSelect"
|
@select="handleReassignDriverSelect"
|
||||||
@change="handleReassignDriverChange"
|
@change="handleReassignDriverChange"
|
||||||
/>
|
>
|
||||||
|
<template #default="{ item }">
|
||||||
|
<span>{{ driverOptionText(item) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-autocomplete>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item
|
<el-form-item
|
||||||
label="手机号"
|
label="手机号"
|
||||||
@@ -3683,6 +3702,7 @@ import {
|
|||||||
getVoucherImages as getProcessConfigVoucherImages,
|
getVoucherImages as getProcessConfigVoucherImages,
|
||||||
} from '@/api/business/process-config';
|
} from '@/api/business/process-config';
|
||||||
import {
|
import {
|
||||||
|
getList as getWaybillList,
|
||||||
getPunchRecords as getWaybillPunchRecords,
|
getPunchRecords as getWaybillPunchRecords,
|
||||||
locateVehicle,
|
locateVehicle,
|
||||||
savePunchRecord,
|
savePunchRecord,
|
||||||
@@ -3692,9 +3712,7 @@ import VoucherExecutionDetail from './voucher-execution-detail.vue';
|
|||||||
import PunchRecordPhotos from './punch-record-photos.vue';
|
import PunchRecordPhotos from './punch-record-photos.vue';
|
||||||
import AddressMapPicker from '@/components/address-map-picker/main.vue';
|
import AddressMapPicker from '@/components/address-map-picker/main.vue';
|
||||||
import { getList as getDriverList } from '@/api/transportCapacity/driver';
|
import { getList as getDriverList } from '@/api/transportCapacity/driver';
|
||||||
import {
|
import { fetchDriverCandidates, driverOptionText } from '@/utils/carrier-org-resource';
|
||||||
fetchDriversByCarrierOrganizations,
|
|
||||||
} from '@/utils/carrier-org-resource';
|
|
||||||
import { getDictionary } from '@/api/system/dictbiz';
|
import { getDictionary } from '@/api/system/dictbiz';
|
||||||
import { getDictionary as getSystemDictionary } from '@/api/system/dict';
|
import { getDictionary as getSystemDictionary } from '@/api/system/dict';
|
||||||
import { addressTypeOptions } from '@/option/base/common-address';
|
import { addressTypeOptions } from '@/option/base/common-address';
|
||||||
@@ -4370,9 +4388,15 @@ export default {
|
|||||||
addBtn: this.canCreate,
|
addBtn: this.canCreate,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
// 打卡记录编辑入口与运单编辑同权限:能改运单的人才能订正它的打卡流水
|
// 打卡记录编辑入口与运单编辑同权限:能改运单的人才能订正它的打卡流水。
|
||||||
|
// 时间轴的「编辑打卡 / 补卡」链接是本页唯一写入入口,提交固定 punched=true。
|
||||||
|
// 取消打卡不在本页;待回单确认的运单不显示该链接。
|
||||||
canEditWaybillPunchRecord() {
|
canEditWaybillPunchRecord() {
|
||||||
return this.hasPermission(`${this.config.permission}_edit`) && !this.readonlyScope;
|
return (
|
||||||
|
this.hasPermission(`${this.config.permission}_edit`) &&
|
||||||
|
!this.readonlyScope &&
|
||||||
|
this.statusValue(this.detailRow) !== 'pending_return_confirm'
|
||||||
|
);
|
||||||
},
|
},
|
||||||
waybillPunchEditNode() {
|
waybillPunchEditNode() {
|
||||||
return this.findWaybillPunchEditNodeConfig(this.waybillPunchEditRecord);
|
return this.findWaybillPunchEditNodeConfig(this.waybillPunchEditRecord);
|
||||||
@@ -4800,6 +4824,7 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
formatContractOptionLabel,
|
formatContractOptionLabel,
|
||||||
|
driverOptionText,
|
||||||
parseRouteChangeContent,
|
parseRouteChangeContent,
|
||||||
// 页面被 keep-alive 缓存/卸载时调用,关闭本页所有 append-to-body 的弹窗,
|
// 页面被 keep-alive 缓存/卸载时调用,关闭本页所有 append-to-body 的弹窗,
|
||||||
// 避免 Teleport 出去的 DOM 残留在 body 上盖住后续页面(详见 data 里的说明)。
|
// 避免 Teleport 出去的 DOM 残留在 body 上盖住后续页面(详见 data 里的说明)。
|
||||||
@@ -5099,7 +5124,8 @@ export default {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const status = this.statusValue(row);
|
const status = this.statusValue(row);
|
||||||
return status === 'pending';
|
// 待回单确认可取消,取消不生成应收应付
|
||||||
|
return status === 'pending' || status === 'pending_return_confirm';
|
||||||
},
|
},
|
||||||
canComplete(row) {
|
canComplete(row) {
|
||||||
if (
|
if (
|
||||||
@@ -5111,29 +5137,17 @@ export default {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const status = this.statusValue(row);
|
const status = this.statusValue(row);
|
||||||
return status === 'processing' || status === 'running' || status === 'pending_return';
|
// 待回单确认也可直接完成,不经确认回单
|
||||||
|
return status === 'processing' || status === 'running' || status === 'pending_return_confirm';
|
||||||
},
|
},
|
||||||
completeActionLabelFor(row) {
|
canConfirmReturn(row) {
|
||||||
return this.statusValue(row) === 'pending_return' ? '确认回单' : this.completeActionLabel;
|
return (
|
||||||
},
|
this.hasAction('confirmReturn') &&
|
||||||
handleCompleteAction(row) {
|
this.hasPermission(`${this.config.permission}_complete`) &&
|
||||||
const isPendingReturn = this.statusValue(row) === 'pending_return';
|
!this.readonlyScope &&
|
||||||
const actionName = this.completeActionLabelFor(row);
|
!row.readonly &&
|
||||||
this.$confirm(`确定${actionName}该${this.config.title}?`, '提示', {
|
this.statusValue(row) === 'pending_return_confirm'
|
||||||
confirmButtonText: '确定',
|
);
|
||||||
cancelButtonText: '取消',
|
|
||||||
type: 'warning',
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
if (isPendingReturn && typeof this.api.confirmReturn === 'function') {
|
|
||||||
return this.api.confirmReturn(row.id);
|
|
||||||
}
|
|
||||||
return this.api.complete(row.id);
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
this.$message.success(`${actionName}成功`);
|
|
||||||
this.onLoad(this.page, this.query);
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
canMaintainMileage(row) {
|
canMaintainMileage(row) {
|
||||||
return (
|
return (
|
||||||
@@ -6565,7 +6579,7 @@ export default {
|
|||||||
if (status === 'completed') {
|
if (status === 'completed') {
|
||||||
return 'primary';
|
return 'primary';
|
||||||
}
|
}
|
||||||
if (status === 'pending_return') {
|
if (status === 'pending_return_confirm') {
|
||||||
return 'warning';
|
return 'warning';
|
||||||
}
|
}
|
||||||
return 'warning';
|
return 'warning';
|
||||||
@@ -7967,19 +7981,10 @@ export default {
|
|||||||
this.taskDriverOptions = [];
|
this.taskDriverOptions = [];
|
||||||
this.taskEscortOptions = [];
|
this.taskEscortOptions = [];
|
||||||
},
|
},
|
||||||
getTaskCarrierFilter() {
|
|
||||||
return {
|
|
||||||
carrierId: this.form.carrierId || '',
|
|
||||||
carrierName: this.form.carrierName || '',
|
|
||||||
};
|
|
||||||
},
|
|
||||||
loadTaskDriverOptions() {
|
loadTaskDriverOptions() {
|
||||||
if (!this.taskInfoFormEnabled || this.taskDriverLoading) return Promise.resolve([]);
|
if (!this.taskInfoFormEnabled || this.taskDriverLoading) return Promise.resolve([]);
|
||||||
this.taskDriverLoading = true;
|
this.taskDriverLoading = true;
|
||||||
return fetchDriversByCarrierOrganizations(
|
return fetchDriverCandidates({ size: 9999, posts: '司机' })
|
||||||
{ size: 9999, posts: '司机' },
|
|
||||||
this.getTaskCarrierFilter()
|
|
||||||
)
|
|
||||||
.then(records => {
|
.then(records => {
|
||||||
this.taskDriverOptions = records;
|
this.taskDriverOptions = records;
|
||||||
return this.taskDriverOptions;
|
return this.taskDriverOptions;
|
||||||
@@ -7990,16 +7995,8 @@ export default {
|
|||||||
},
|
},
|
||||||
fetchTaskDriverSuggestions(queryString, callback) {
|
fetchTaskDriverSuggestions(queryString, callback) {
|
||||||
const keyword = String(queryString || '').trim();
|
const keyword = String(queryString || '').trim();
|
||||||
const carrier = this.getTaskCarrierFilter();
|
|
||||||
if (!carrier.carrierId && !carrier.carrierName) {
|
|
||||||
callback([]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.taskDriverLoading = true;
|
this.taskDriverLoading = true;
|
||||||
fetchDriversByCarrierOrganizations(
|
fetchDriverCandidates({ size: 20, ...(keyword ? { driverName: keyword } : {}), posts: '司机' })
|
||||||
{ size: 20, ...(keyword ? { driverName: keyword } : {}), posts: '司机' },
|
|
||||||
carrier
|
|
||||||
)
|
|
||||||
.then(records => {
|
.then(records => {
|
||||||
this.taskDriverOptions = records;
|
this.taskDriverOptions = records;
|
||||||
callback(
|
callback(
|
||||||
@@ -8019,16 +8016,12 @@ export default {
|
|||||||
},
|
},
|
||||||
fetchTaskEscortSuggestions(queryString, callback) {
|
fetchTaskEscortSuggestions(queryString, callback) {
|
||||||
const keyword = String(queryString || '').trim();
|
const keyword = String(queryString || '').trim();
|
||||||
const carrier = this.getTaskCarrierFilter();
|
|
||||||
if (!carrier.carrierId && !carrier.carrierName) {
|
|
||||||
callback([]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.taskEscortLoading = true;
|
this.taskEscortLoading = true;
|
||||||
fetchDriversByCarrierOrganizations(
|
fetchDriverCandidates({
|
||||||
{ size: 20, ...(keyword ? { driverName: keyword } : {}), posts: '押运员' },
|
size: 20,
|
||||||
carrier
|
...(keyword ? { driverName: keyword } : {}),
|
||||||
)
|
posts: '押运员',
|
||||||
|
})
|
||||||
.then(records => {
|
.then(records => {
|
||||||
this.taskEscortOptions = records;
|
this.taskEscortOptions = records;
|
||||||
callback(records.map(item => ({ ...item, value: item.driverName || item.name || '' })));
|
callback(records.map(item => ({ ...item, value: item.driverName || item.name || '' })));
|
||||||
@@ -10175,8 +10168,14 @@ export default {
|
|||||||
cancel: '取消',
|
cancel: '取消',
|
||||||
complete: this.completeActionLabel,
|
complete: this.completeActionLabel,
|
||||||
reassign: '重新派单',
|
reassign: '重新派单',
|
||||||
|
confirmReturn: '确认回单',
|
||||||
}[action];
|
}[action];
|
||||||
this.$confirm(`确定${actionName}该${this.config.title}?`, '提示', {
|
// 确认回单是终态动作(本轮不做撤销),把后果写在提示里,别让人以为还能退回
|
||||||
|
const confirmText =
|
||||||
|
action === 'confirmReturn'
|
||||||
|
? '确认回单后该运单将置为已完成,且不可撤销。是否继续?'
|
||||||
|
: `确定${actionName}该${this.config.title}?`;
|
||||||
|
this.$confirm(confirmText, '提示', {
|
||||||
confirmButtonText: '确定',
|
confirmButtonText: '确定',
|
||||||
cancelButtonText: '取消',
|
cancelButtonText: '取消',
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
@@ -10229,20 +10228,8 @@ export default {
|
|||||||
},
|
},
|
||||||
fetchReassignDriverSuggestions(queryString, callback) {
|
fetchReassignDriverSuggestions(queryString, callback) {
|
||||||
const keyword = String(queryString || '').trim();
|
const keyword = String(queryString || '').trim();
|
||||||
const row = this.reassignDrawer.row || {};
|
|
||||||
const carrier = {
|
|
||||||
carrierId: row.carrierId || '',
|
|
||||||
carrierName: row.carrierName || '',
|
|
||||||
};
|
|
||||||
if (!carrier.carrierId && !carrier.carrierName) {
|
|
||||||
callback([]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.reassignDrawer.driverLoading = true;
|
this.reassignDrawer.driverLoading = true;
|
||||||
fetchDriversByCarrierOrganizations(
|
fetchDriverCandidates({ size: 20, ...(keyword ? { driverName: keyword } : {}), posts: '司机' })
|
||||||
{ size: 20, ...(keyword ? { driverName: keyword } : {}), posts: '司机' },
|
|
||||||
carrier
|
|
||||||
)
|
|
||||||
.then(records => {
|
.then(records => {
|
||||||
this.reassignDriverOptions = records;
|
this.reassignDriverOptions = records;
|
||||||
callback(
|
callback(
|
||||||
@@ -10382,11 +10369,11 @@ export default {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const pendingReturnCount = this.selectionList.filter(
|
const pendingReturnCount = this.selectionList.filter(
|
||||||
row => this.statusValue(row) === 'pending_return'
|
row => this.statusValue(row) === 'pending_return_confirm'
|
||||||
).length;
|
).length;
|
||||||
const tip =
|
const tip =
|
||||||
pendingReturnCount > 0
|
pendingReturnCount > 0
|
||||||
? `确定完成选中的运单?其中 ${pendingReturnCount} 条为待确认回单,确认后将转为已完成。`
|
? `确定完成选中的运单?其中 ${pendingReturnCount} 条为待回单确认,确认后将转为已完成。`
|
||||||
: '确定完成选中的运单?';
|
: '确定完成选中的运单?';
|
||||||
this.$confirm(tip, '提示', {
|
this.$confirm(tip, '提示', {
|
||||||
confirmButtonText: '确定',
|
confirmButtonText: '确定',
|
||||||
@@ -10402,25 +10389,38 @@ export default {
|
|||||||
this.onLoad(this.page, this.query);
|
this.onLoad(this.page, this.query);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
handleRoadLoading() {
|
async handleRoadLoading() {
|
||||||
if (!this.selectionList.length) {
|
if (!this.selectionList.length) {
|
||||||
this.$message.warning('请选择至少一条数据');
|
this.$message.warning('请选择至少一条数据');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const nonRoadWaybills = this.selectionList.filter(row => !this.waybillIsRoadTransport(row));
|
const selectedIds = this.selectionList.map(row => row.id).filter(Boolean);
|
||||||
if (nonRoadWaybills.length) {
|
if (!selectedIds.length) {
|
||||||
this.$message.warning('公路配载仅支持运输方式为“公路运输”的运单');
|
this.$message.warning('请选择至少一条数据');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const unavailableWaybills = this.selectionList.filter(
|
// 可配载与否只看后端查询结果
|
||||||
row =>
|
let loadableIds;
|
||||||
!['pending', 'processing', 'running'].includes(this.statusValue(row)) ||
|
try {
|
||||||
row.loadingId ||
|
const res = await getWaybillList(1, selectedIds.length, {
|
||||||
row.loadingManageId ||
|
loadableOnly: 1,
|
||||||
row.loadingNo
|
ids: selectedIds.join(','),
|
||||||
);
|
});
|
||||||
if (unavailableWaybills.length) {
|
loadableIds = new Set(extractRecords(res).map(row => String(row.id)));
|
||||||
this.$message.warning('公路配载仅支持待执行/进行中且未关联有效配载单的运单');
|
} catch (error) {
|
||||||
|
this.$message.error('校验运单可配载状态失败,请重试');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const rejectedRows = this.selectionList.filter(row => !loadableIds.has(String(row.id)));
|
||||||
|
if (rejectedRows.length) {
|
||||||
|
const rejectedText = rejectedRows
|
||||||
|
.map(row => {
|
||||||
|
const reason = this.roadLoadingRejectReasonText(row);
|
||||||
|
const no = row.waybillNo || row.id || '';
|
||||||
|
return `${no}(${reason})`;
|
||||||
|
})
|
||||||
|
.join('、');
|
||||||
|
this.$message.warning(`${rejectedText};公路配载仅支持公路、待执行且未配载的运单`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
@@ -10443,6 +10443,13 @@ export default {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
// 只给后端判定为不可配载的运单生成可读原因,本身不参与准入判定
|
||||||
|
roadLoadingRejectReasonText(row = {}) {
|
||||||
|
if (!this.waybillIsRoadTransport(row)) return '非公路运输';
|
||||||
|
if (String(row.businessStatus || '') !== 'pending') return '状态不是待执行';
|
||||||
|
if (row.loadingNo) return '已关联配载单';
|
||||||
|
return '运单数据可能已变化,请刷新后重试';
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1079,31 +1079,12 @@
|
|||||||
<el-option
|
<el-option
|
||||||
v-for="item in driverOptions"
|
v-for="item in driverOptions"
|
||||||
:key="item.id || item.driverName || item.name"
|
:key="item.id || item.driverName || item.name"
|
||||||
:label="item.driverName || item.name"
|
:label="driverOptionText(item)"
|
||||||
:value="item.driverName || item.name"
|
:value="item.driverName || item.name"
|
||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
|
||||||
<el-form-item label="运输方式">
|
|
||||||
<el-select
|
|
||||||
v-model="candidateQuery.transportType"
|
|
||||||
clearable
|
|
||||||
filterable
|
|
||||||
placeholder="请选择"
|
|
||||||
:disabled="dialogMode === 'add'"
|
|
||||||
@visible-change="visible => visible && loadTransportTypeOptions()"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="item in transportTypeOptions"
|
|
||||||
:key="item.value"
|
|
||||||
:label="item.label"
|
|
||||||
:value="item.value"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</template>
|
</template>
|
||||||
<el-col
|
<el-col
|
||||||
:span="candidateSearchVisible ? 24 : 6"
|
:span="candidateSearchVisible ? 24 : 6"
|
||||||
@@ -1511,7 +1492,11 @@
|
|||||||
placeholder="请输入"
|
placeholder="请输入"
|
||||||
:disabled="dialogReadonly || isRunningSelfDriverLocked"
|
:disabled="dialogReadonly || isRunningSelfDriverLocked"
|
||||||
@select="item => handleDriverSuggestionSelect('dialog', item)"
|
@select="item => handleDriverSuggestionSelect('dialog', item)"
|
||||||
/>
|
>
|
||||||
|
<template #default="{ item }">
|
||||||
|
<span>{{ driverOptionText(item) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-autocomplete>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item
|
<el-form-item
|
||||||
label="手机号"
|
label="手机号"
|
||||||
@@ -1550,7 +1535,11 @@
|
|||||||
placeholder="请输入"
|
placeholder="请输入"
|
||||||
:disabled="dialogReadonly"
|
:disabled="dialogReadonly"
|
||||||
@select="item => handleEscortSuggestionSelect('dialog', item)"
|
@select="item => handleEscortSuggestionSelect('dialog', item)"
|
||||||
/>
|
>
|
||||||
|
<template #default="{ item }">
|
||||||
|
<span>{{ driverOptionText(item) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-autocomplete>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item v-if="dialogForm.carrierType !== '承运商'" label="押运人手机号">
|
<el-form-item v-if="dialogForm.carrierType !== '承运商'" label="押运人手机号">
|
||||||
<el-input
|
<el-input
|
||||||
@@ -1880,7 +1869,7 @@ import { getList as getContractList } from '@/api/business/contract-manage';
|
|||||||
import { getParentOptions as getCargoTypeOptions } from '@/api/base/cargo-type';
|
import { getParentOptions as getCargoTypeOptions } from '@/api/base/cargo-type';
|
||||||
import { getList as getCustomerList } from '@/api/vehicle/customer-archive';
|
import { getList as getCustomerList } from '@/api/vehicle/customer-archive';
|
||||||
import { getList as getDriverList } from '@/api/transportCapacity/driver';
|
import { getList as getDriverList } from '@/api/transportCapacity/driver';
|
||||||
import { fetchDriversByCarrierOrganizations } from '@/utils/carrier-org-resource';
|
import { fetchDriverCandidates, driverOptionText } from '@/utils/carrier-org-resource';
|
||||||
import { formatContractOptionLabel } from '@/utils/contract';
|
import { formatContractOptionLabel } from '@/utils/contract';
|
||||||
import {
|
import {
|
||||||
getList as getProcessConfigList,
|
getList as getProcessConfigList,
|
||||||
@@ -1905,7 +1894,6 @@ import { normalizeMapSearchResults, searchAmapPlaceText } from '@/utils/map-sear
|
|||||||
import { parseRouteChangeContent } from '@/utils/route-change';
|
import { parseRouteChangeContent } from '@/utils/route-change';
|
||||||
|
|
||||||
const defaultTransportType = '';
|
const defaultTransportType = '';
|
||||||
const defaultCandidateTransportType = '';
|
|
||||||
const AMAP_KEY = '653b7cf105ad7fb8ec9b2f5198ade315';
|
const AMAP_KEY = '653b7cf105ad7fb8ec9b2f5198ade315';
|
||||||
const AMAP_SECURITY_CODE = '5aab65c632e48ebae0d0e28f5b28e21a';
|
const AMAP_SECURITY_CODE = '5aab65c632e48ebae0d0e28f5b28e21a';
|
||||||
const loadingCreateWaybillsStorageKey = 'loading-manage-create-waybills';
|
const loadingCreateWaybillsStorageKey = 'loading-manage-create-waybills';
|
||||||
@@ -1946,7 +1934,6 @@ const createCandidateSearchForm = () => ({
|
|||||||
vehicleNo: '',
|
vehicleNo: '',
|
||||||
carrierName: '',
|
carrierName: '',
|
||||||
driverName: '',
|
driverName: '',
|
||||||
transportType: defaultCandidateTransportType,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const createDialogForm = () => ({
|
const createDialogForm = () => ({
|
||||||
@@ -2401,6 +2388,7 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
parseRouteChangeContent,
|
parseRouteChangeContent,
|
||||||
|
driverOptionText,
|
||||||
formatTaskCarrierContractLabel(item = {}) {
|
formatTaskCarrierContractLabel(item = {}) {
|
||||||
const carrierName = item.carrierName || item.partyB || '';
|
const carrierName = item.carrierName || item.partyB || '';
|
||||||
const contractLabel = formatContractOptionLabel(item);
|
const contractLabel = formatContractOptionLabel(item);
|
||||||
@@ -3104,7 +3092,6 @@ export default {
|
|||||||
this.dialogMode = mode;
|
this.dialogMode = mode;
|
||||||
this.dialogLoading = true;
|
this.dialogLoading = true;
|
||||||
this.resetDialogData();
|
this.resetDialogData();
|
||||||
if (mode === 'add') this.candidateQuery.transportType = 'road';
|
|
||||||
try {
|
try {
|
||||||
if (row?.id) {
|
if (row?.id) {
|
||||||
const res = await loadingApi.getDetail(row.id);
|
const res = await loadingApi.getDetail(row.id);
|
||||||
@@ -3131,7 +3118,6 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
this.dialogForm = createDialogForm();
|
this.dialogForm = createDialogForm();
|
||||||
this.candidateQuery = createCandidateSearchForm();
|
this.candidateQuery = createCandidateSearchForm();
|
||||||
this.candidateQuery.transportType = 'road';
|
|
||||||
await this.loadCandidateWaybills();
|
await this.loadCandidateWaybills();
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
@@ -3912,16 +3898,17 @@ export default {
|
|||||||
try {
|
try {
|
||||||
const query = {
|
const query = {
|
||||||
...this.buildQueryParams(this.candidateQuery),
|
...this.buildQueryParams(this.candidateQuery),
|
||||||
// 与后端 LoadableAdmissionSupport 一致:公路 + 待执行/进行中 + 未关联有效配载单
|
// 公路、待执行、未配载的判定都在后端可配载开关内
|
||||||
|
// 该开关已把候选限定为公路,再按精确运输方式取值筛会漏掉「公路运输」这类历史写法
|
||||||
loadableOnly: 1,
|
loadableOnly: 1,
|
||||||
// 仅查当前账户所属组织及子组织下的运单
|
// allDept=0 表示不指定组织:超管与「数据权限范围=全部组织」的账户后端不过滤组织,
|
||||||
|
// 其余账户限定本组织(不展开下级)
|
||||||
allDept: 0,
|
allDept: 0,
|
||||||
};
|
};
|
||||||
const transportType = this.getCandidateTransportTypeQuery(query.transportType);
|
|
||||||
const res = await getWaybillList(
|
const res = await getWaybillList(
|
||||||
this.candidatePage.currentPage,
|
this.candidatePage.currentPage,
|
||||||
this.candidatePage.pageSize,
|
this.candidatePage.pageSize,
|
||||||
{ ...query, transportType }
|
query
|
||||||
);
|
);
|
||||||
const page = unwrapPage(res);
|
const page = unwrapPage(res);
|
||||||
this.candidateRows = page.records;
|
this.candidateRows = page.records;
|
||||||
@@ -3930,20 +3917,12 @@ export default {
|
|||||||
this.candidateLoading = false;
|
this.candidateLoading = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getCandidateTransportTypeQuery(value) {
|
|
||||||
const normalized = String(value || '')
|
|
||||||
.trim()
|
|
||||||
.toLowerCase();
|
|
||||||
if (['road', '公路运输'].includes(normalized)) return 'road,公路运输';
|
|
||||||
return value || '';
|
|
||||||
},
|
|
||||||
handleCandidateSearch() {
|
handleCandidateSearch() {
|
||||||
this.candidatePage.currentPage = 1;
|
this.candidatePage.currentPage = 1;
|
||||||
this.loadCandidateWaybills();
|
this.loadCandidateWaybills();
|
||||||
},
|
},
|
||||||
handleCandidateReset() {
|
handleCandidateReset() {
|
||||||
this.candidateQuery = createCandidateSearchForm();
|
this.candidateQuery = createCandidateSearchForm();
|
||||||
if (this.dialogMode === 'add') this.candidateQuery.transportType = 'road';
|
|
||||||
this.candidatePage.currentPage = 1;
|
this.candidatePage.currentPage = 1;
|
||||||
this.loadCandidateWaybills();
|
this.loadCandidateWaybills();
|
||||||
},
|
},
|
||||||
@@ -4005,8 +3984,31 @@ export default {
|
|||||||
const exists = new Set(
|
const exists = new Set(
|
||||||
this.selectedWaybillRows.map(row => String(row.id || row.waybillNo))
|
this.selectedWaybillRows.map(row => String(row.id || row.waybillNo))
|
||||||
);
|
);
|
||||||
|
// 一张配载单的全部子运单必须同项目:以已选运单(无已选时以本批首张)的项目为准
|
||||||
|
const anchorRow = [...this.selectedWaybillRows, ...selectedRows].find(row =>
|
||||||
|
this.waybillProjectKey(row)
|
||||||
|
);
|
||||||
|
const anchorKey = anchorRow ? this.waybillProjectKey(anchorRow) : '';
|
||||||
|
const mismatchedRows = anchorKey
|
||||||
|
? selectedRows.filter(
|
||||||
|
row => this.waybillProjectKey(row) && this.waybillProjectKey(row) !== anchorKey
|
||||||
|
)
|
||||||
|
: [];
|
||||||
|
if (mismatchedRows.length) {
|
||||||
|
const anchorName = anchorRow.projectName || anchorRow.projectId;
|
||||||
|
const mismatchedText = mismatchedRows
|
||||||
|
.map(
|
||||||
|
row =>
|
||||||
|
`${row.waybillNo || row.id}(项目:${row.projectName || row.projectId || '未知'})`
|
||||||
|
)
|
||||||
|
.join('、');
|
||||||
|
this.$message.warning(
|
||||||
|
`运单 ${mismatchedText} 与已选运单项目「${anchorName}」不一致,不能加入同一配载单`
|
||||||
|
);
|
||||||
|
}
|
||||||
const nextRows = [...this.selectedWaybillRows];
|
const nextRows = [...this.selectedWaybillRows];
|
||||||
selectedRows.forEach(row => {
|
selectedRows.forEach(row => {
|
||||||
|
if (mismatchedRows.includes(row)) return;
|
||||||
const key = String(row.id || row.waybillNo);
|
const key = String(row.id || row.waybillNo);
|
||||||
if (!exists.has(key)) {
|
if (!exists.has(key)) {
|
||||||
nextRows.push(row);
|
nextRows.push(row);
|
||||||
@@ -4029,6 +4031,9 @@ export default {
|
|||||||
async addSelectedWaybills() {
|
async addSelectedWaybills() {
|
||||||
await this.addWaybillRows(this.candidateSelection);
|
await this.addWaybillRows(this.candidateSelection);
|
||||||
},
|
},
|
||||||
|
waybillProjectKey(row = {}) {
|
||||||
|
return String(row.projectId || row.projectName || '').trim();
|
||||||
|
},
|
||||||
removeSelectedWaybill(index) {
|
removeSelectedWaybill(index) {
|
||||||
const row = this.selectedWaybillRows[index];
|
const row = this.selectedWaybillRows[index];
|
||||||
if (this.isSelectedWaybillRemoveLocked(row)) {
|
if (this.isSelectedWaybillRemoveLocked(row)) {
|
||||||
@@ -4551,12 +4556,6 @@ export default {
|
|||||||
this.dialogForm.escortPhone = '';
|
this.dialogForm.escortPhone = '';
|
||||||
this.driverOptions = [];
|
this.driverOptions = [];
|
||||||
},
|
},
|
||||||
getDialogCarrierFilter() {
|
|
||||||
return {
|
|
||||||
carrierId: this.dialogForm.carrierId || '',
|
|
||||||
carrierName: this.dialogForm.carrierName || '',
|
|
||||||
};
|
|
||||||
},
|
|
||||||
handleDriverChange(value) {
|
handleDriverChange(value) {
|
||||||
const driver = this.driverOptions.find(item => (item.driverName || item.name) === value);
|
const driver = this.driverOptions.find(item => (item.driverName || item.name) === value);
|
||||||
if (driver && !this.dialogForm.driverPhone) {
|
if (driver && !this.dialogForm.driverPhone) {
|
||||||
@@ -4590,16 +4589,12 @@ export default {
|
|||||||
},
|
},
|
||||||
fetchEscortSuggestions(queryString, callback) {
|
fetchEscortSuggestions(queryString, callback) {
|
||||||
const keyword = String(queryString || '').trim();
|
const keyword = String(queryString || '').trim();
|
||||||
const carrier = this.getDialogCarrierFilter();
|
|
||||||
if (!carrier.carrierId && !carrier.carrierName) {
|
|
||||||
callback([]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.escortLoading = true;
|
this.escortLoading = true;
|
||||||
fetchDriversByCarrierOrganizations(
|
fetchDriverCandidates({
|
||||||
{ size: 20, ...(keyword ? { driverName: keyword } : {}), posts: '押运员' },
|
size: 20,
|
||||||
carrier
|
...(keyword ? { driverName: keyword } : {}),
|
||||||
)
|
posts: '押运员',
|
||||||
|
})
|
||||||
.then(records => {
|
.then(records => {
|
||||||
callback(
|
callback(
|
||||||
records.map(item => ({
|
records.map(item => ({
|
||||||
@@ -4679,17 +4674,13 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
async loadDialogDriverOptions(keyword = '') {
|
async loadDialogDriverOptions(keyword = '') {
|
||||||
const carrier = this.getDialogCarrierFilter();
|
|
||||||
if (!carrier.carrierId && !carrier.carrierName) {
|
|
||||||
this.driverOptions = [];
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
this.driverLoading = true;
|
this.driverLoading = true;
|
||||||
try {
|
try {
|
||||||
this.driverOptions = await fetchDriversByCarrierOrganizations(
|
this.driverOptions = await fetchDriverCandidates({
|
||||||
{ size: 20, ...(keyword ? { driverName: keyword } : {}), posts: '司机' },
|
size: 20,
|
||||||
carrier
|
...(keyword ? { driverName: keyword } : {}),
|
||||||
);
|
posts: '司机',
|
||||||
|
});
|
||||||
return this.driverOptions;
|
return this.driverOptions;
|
||||||
} finally {
|
} finally {
|
||||||
this.driverLoading = false;
|
this.driverLoading = false;
|
||||||
@@ -4720,10 +4711,9 @@ export default {
|
|||||||
}
|
}
|
||||||
this.taskCarrierLoading = true;
|
this.taskCarrierLoading = true;
|
||||||
try {
|
try {
|
||||||
const projectIds = [
|
// 配载单只有一个项目,承运商合同也必须属于该项目
|
||||||
...new Set(this.selectedWaybillRows.map(row => row.projectId).filter(Boolean)),
|
const projectId = this.selectedWaybillRows.map(row => row.projectId).find(Boolean);
|
||||||
];
|
const response = await loadingApi.getCarrierContracts(projectId ? [projectId] : []);
|
||||||
const response = await loadingApi.getCarrierContracts(projectIds);
|
|
||||||
if (requestId !== this.taskCarrierRequestId) return [];
|
if (requestId !== this.taskCarrierRequestId) return [];
|
||||||
const contracts = response.data?.data || response.data || [];
|
const contracts = response.data?.data || response.data || [];
|
||||||
if (this.dialogForm.carrierType !== '自运') {
|
if (this.dialogForm.carrierType !== '自运') {
|
||||||
|
|||||||
@@ -577,10 +577,7 @@ export default {
|
|||||||
this.$message.warning(`第${index + 1}条调度明细缺少承运商信息`);
|
this.$message.warning(`第${index + 1}条调度明细缺少承运商信息`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!row.driverName) {
|
// 司机是否必填由调度明细弹窗按承运类型判定(承运商承运时司机选填),此处不再重复校验
|
||||||
this.$message.warning(`第${index + 1}条调度明细缺少司机信息`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!row.vehicleNo) {
|
if (!row.vehicleNo) {
|
||||||
this.$message.warning(`第${index + 1}条调度明细缺少车牌号`);
|
this.$message.warning(`第${index + 1}条调度明细缺少车牌号`);
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -143,7 +143,7 @@
|
|||||||
maxlength="18"
|
maxlength="18"
|
||||||
show-word-limit
|
show-word-limit
|
||||||
placeholder="请输入"
|
placeholder="请输入"
|
||||||
@input="handleIdCardInput"
|
@blur="handleIdCardBlur"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
@@ -153,7 +153,7 @@
|
|||||||
v-model="driverForm.birthday"
|
v-model="driverForm.birthday"
|
||||||
type="date"
|
type="date"
|
||||||
value-format="YYYY-MM-DD"
|
value-format="YYYY-MM-DD"
|
||||||
placeholder="请选择"
|
placeholder="请选择出生年月"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
@@ -201,12 +201,16 @@
|
|||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
<el-form-item label="住址" prop="addressRegion">
|
<el-form-item label="住址" prop="addressRegion">
|
||||||
|
<!-- 只读直接显示库里那串区划名称:级联要先按名称反解 path,区划改名后反解不出来,
|
||||||
|
同一串文字就会在灰字 placeholder 与黑字标签之间摇摆 -->
|
||||||
|
<el-input v-if="readonly" :model-value="driverForm.addressRegion || '—'" readonly />
|
||||||
<el-cascader
|
<el-cascader
|
||||||
|
v-else
|
||||||
ref="addressRegionCascader"
|
ref="addressRegionCascader"
|
||||||
v-model="driverForm.addressRegionPath"
|
v-model="driverForm.addressRegionPath"
|
||||||
:options="regionOptions"
|
:options="regionOptions"
|
||||||
:props="regionCascaderProps"
|
:props="regionCascaderProps"
|
||||||
:placeholder="driverForm.addressRegion || '请选择'"
|
:placeholder="driverForm.addressRegion || '请选择省/市/区'"
|
||||||
clearable
|
clearable
|
||||||
filterable
|
filterable
|
||||||
@change="handleAddressRegionChange"
|
@change="handleAddressRegionChange"
|
||||||
@@ -219,7 +223,7 @@
|
|||||||
v-model="driverForm.address"
|
v-model="driverForm.address"
|
||||||
maxlength="200"
|
maxlength="200"
|
||||||
show-word-limit
|
show-word-limit
|
||||||
placeholder="请输入"
|
placeholder="请输入详细地址(街道、门牌号)"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
@@ -1122,19 +1126,21 @@ export default {
|
|||||||
removeDigits(value = '') {
|
removeDigits(value = '') {
|
||||||
return String(value).replace(/\d/g, '');
|
return String(value).replace(/\d/g, '');
|
||||||
},
|
},
|
||||||
handleIdCardInput(value) {
|
/**
|
||||||
const idCardNo = String(value || '').toUpperCase();
|
* 身份证号失焦后补出生年月与性别,与小程序 person.vue 的 onIdCardNoChange 同口径:
|
||||||
|
* 只在字段还空着时补——用户手改过的值不被他随后修改身份证号的动作冲掉。
|
||||||
|
* 不回写空值:18 位不合法时清空生日会把已选日期当垃圾丢掉。
|
||||||
|
* OCR 识别结果仍直接覆盖(见 applyIdCardRecognition),那是「重新采一次证件」,与手输联动不是一回事。
|
||||||
|
*/
|
||||||
|
handleIdCardBlur() {
|
||||||
|
const idCardNo = String(this.driverForm.idCardNo || '').toUpperCase();
|
||||||
this.driverForm.idCardNo = idCardNo;
|
this.driverForm.idCardNo = idCardNo;
|
||||||
const idCardInfo = this.parseIdCardInfo(idCardNo);
|
const idCardInfo = this.parseIdCardInfo(idCardNo);
|
||||||
if (idCardInfo.birthday) {
|
if (!this.driverForm.birthday && idCardInfo.birthday) {
|
||||||
this.driverForm.birthday = idCardInfo.birthday;
|
this.driverForm.birthday = idCardInfo.birthday;
|
||||||
} else {
|
|
||||||
this.driverForm.birthday = '';
|
|
||||||
}
|
}
|
||||||
if (idCardInfo.gender) {
|
if (!this.driverForm.gender && idCardInfo.gender) {
|
||||||
this.driverForm.gender = idCardInfo.gender;
|
this.driverForm.gender = idCardInfo.gender;
|
||||||
} else {
|
|
||||||
this.driverForm.gender = '';
|
|
||||||
}
|
}
|
||||||
if (this.submitAttempted) {
|
if (this.submitAttempted) {
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user