运单调整
This commit is contained in:
@@ -92,13 +92,12 @@ export const createCrudApi = baseUrl => ({
|
||||
data,
|
||||
});
|
||||
},
|
||||
reassign(id) {
|
||||
reassign(data) {
|
||||
const payload = data && typeof data === 'object' ? data : { id: data };
|
||||
return request({
|
||||
url: `${baseUrl}/reassign`,
|
||||
method: 'post',
|
||||
params: {
|
||||
id,
|
||||
},
|
||||
data: payload,
|
||||
});
|
||||
},
|
||||
batchComplete(ids) {
|
||||
|
||||
@@ -26,6 +26,13 @@ export const maintainMileage = data =>
|
||||
method: 'post',
|
||||
data,
|
||||
});
|
||||
/** 运单打卡记录 + 司机上传凭证图 */
|
||||
export const getPunchRecords = waybillId =>
|
||||
request({
|
||||
url: `${baseUrl}/punch-records`,
|
||||
method: 'get',
|
||||
params: { waybillId },
|
||||
});
|
||||
export const roadLoading = ids =>
|
||||
request({
|
||||
url: `${baseUrl}/road-loading`,
|
||||
|
||||
@@ -41,7 +41,7 @@ export const planStatusOptions = [
|
||||
export const waybillStatusOptions = [
|
||||
{ label: '草稿', value: 'draft' },
|
||||
{ label: '待执行', value: 'pending' },
|
||||
{ label: '进行中', value: 'processing' },
|
||||
{ label: '进行中', value: 'running' },
|
||||
{ label: '已完成', value: 'completed' },
|
||||
{ label: '已取消', value: 'cancelled' },
|
||||
];
|
||||
|
||||
@@ -92,14 +92,32 @@ const getProcessConfigNodes = row => {
|
||||
|
||||
const isAcceptProcessNode = node => node.key === 'accept' || node.name === '接单';
|
||||
|
||||
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 =>
|
||||
isAcceptProcessNode(node) &&
|
||||
node.enabled !== false &&
|
||||
node.confirmMode === 'yes' &&
|
||||
node.confirmDriver === true
|
||||
);
|
||||
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;
|
||||
@@ -122,6 +140,7 @@ const isDriverRejectedStatus = value => {
|
||||
};
|
||||
|
||||
const hasDriverRejectRecord = row => {
|
||||
if (String(row.driverAcceptStatus || '').toLowerCase() === 'rejected') return true;
|
||||
const rejectRecordValues = [
|
||||
row.driverRejectTime,
|
||||
row.driverRejectedTime,
|
||||
@@ -161,9 +180,16 @@ const hasDriverRejectRecord = row => {
|
||||
};
|
||||
|
||||
const isDriverRejectedWaybill = row =>
|
||||
requiresDriverAcceptConfirmation(row) && hasDriverRejectRecord(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,
|
||||
@@ -172,6 +198,7 @@ const hasDriverAcceptRecord = row => {
|
||||
row.driverAcceptedBy,
|
||||
row.acceptUserId,
|
||||
row.acceptUserName,
|
||||
row.driverAcceptDriverId,
|
||||
];
|
||||
if (recordValues.some(value => value !== null && value !== undefined && value !== '')) {
|
||||
return true;
|
||||
@@ -425,26 +452,26 @@ export const config = {
|
||||
statusProp: 'businessStatus',
|
||||
statusTextProp: 'businessStatusName',
|
||||
formatStatus(row, prop, defaultText) {
|
||||
if (
|
||||
prop === 'businessStatus' &&
|
||||
requiresDriverAcceptConfirmation(row) &&
|
||||
isInProgressStatus(row, defaultText) &&
|
||||
!hasDriverAcceptRecord(row)
|
||||
) {
|
||||
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);
|
||||
// 需司机接单且尚未接单 → 待执行(含库中仍为 running 的历史数据展示校正)
|
||||
if (needAccept && !hasDriverAcceptRecord(row) && !terminalStatuses.includes(status)) {
|
||||
return '待执行';
|
||||
}
|
||||
if (
|
||||
prop === 'businessStatus' &&
|
||||
row.businessStatus === 'pending' &&
|
||||
!requiresDriverAcceptConfirmation(row)
|
||||
) {
|
||||
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)) {
|
||||
|
||||
@@ -6874,7 +6874,7 @@ export default {
|
||||
const status = this.statusValue(row);
|
||||
return this.config.permission === 'transport_plan'
|
||||
? ['waiting_dispatch', 'dispatching'].includes(status)
|
||||
: ['pending', 'processing'].includes(status);
|
||||
: ['pending', 'processing', 'running'].includes(status);
|
||||
},
|
||||
canComplete(row) {
|
||||
if (
|
||||
@@ -6888,7 +6888,7 @@ export default {
|
||||
const status = this.statusValue(row);
|
||||
return this.config.permission === 'transport_plan'
|
||||
? status === 'dispatching'
|
||||
: status === 'processing';
|
||||
: status === 'processing' || status === 'running';
|
||||
},
|
||||
canMaintainMileage(row) {
|
||||
return (
|
||||
|
||||
@@ -100,9 +100,20 @@
|
||||
</template>
|
||||
|
||||
<template #businessStatus="{ row }">
|
||||
<span class="waybill-manage-page__status-cell">
|
||||
<el-tag :type="statusTagType(row.businessStatus)" class="status-text">
|
||||
{{ displayStatus(row, 'businessStatus') }}
|
||||
</el-tag>
|
||||
<el-tooltip
|
||||
v-if="driverRejectReasonText(row)"
|
||||
:content="driverRejectReasonText(row)"
|
||||
placement="top"
|
||||
>
|
||||
<el-icon class="waybill-manage-page__reject-tip-icon">
|
||||
<WarnTriangleFilled />
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<template #approvalStatus="{ row }">
|
||||
@@ -1569,7 +1580,7 @@
|
||||
<el-link type="primary" v-if="canEdit(row)" @click="openBusinessForm('edit', row)">
|
||||
编辑
|
||||
</el-link>
|
||||
<el-link type="primary" v-if="canReassign(row)" @click="handleAction('reassign', row)">
|
||||
<el-link type="primary" v-if="canReassign(row)" @click="openReassignDrawer(row)">
|
||||
重新派单
|
||||
</el-link>
|
||||
<el-link type="primary" v-if="canCopy(row)" @click="handleCopy(row)"> 复制 </el-link>
|
||||
@@ -1601,6 +1612,87 @@
|
||||
</template>
|
||||
</component>
|
||||
|
||||
<el-drawer
|
||||
v-model="reassignDrawer.visible"
|
||||
title="重新派单"
|
||||
direction="btt"
|
||||
size="280px"
|
||||
append-to-body
|
||||
destroy-on-close
|
||||
class="waybill-manage-page__reassign-drawer"
|
||||
@closed="resetReassignDrawer"
|
||||
>
|
||||
<el-form
|
||||
ref="reassignFormRef"
|
||||
:model="reassignForm"
|
||||
:rules="reassignRules"
|
||||
label-width="72px"
|
||||
class="waybill-manage-page__reassign-form"
|
||||
>
|
||||
<div class="waybill-manage-page__reassign-row">
|
||||
<div class="waybill-manage-page__reassign-meta">
|
||||
<span class="waybill-manage-page__reassign-meta-label">运单号</span>
|
||||
<span class="waybill-manage-page__reassign-meta-value">{{
|
||||
reassignDrawer.row?.waybillNo || '-'
|
||||
}}</span>
|
||||
</div>
|
||||
<div class="waybill-manage-page__reassign-meta waybill-manage-page__reassign-meta--wide">
|
||||
<span class="waybill-manage-page__reassign-meta-label">拒绝原因</span>
|
||||
<span class="waybill-manage-page__reassign-meta-value">{{
|
||||
driverRejectReasonText(reassignDrawer.row) || '-'
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="waybill-manage-page__reassign-row">
|
||||
<el-form-item label="司机" prop="driverName" class="waybill-manage-page__reassign-span-1">
|
||||
<el-autocomplete
|
||||
v-model="reassignForm.driverName"
|
||||
placeholder="请输入"
|
||||
clearable
|
||||
:debounce="300"
|
||||
:fetch-suggestions="fetchReassignDriverSuggestions"
|
||||
:loading="reassignDrawer.driverLoading"
|
||||
style="width: 100%"
|
||||
@select="handleReassignDriverSelect"
|
||||
@change="handleReassignDriverChange"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
label="手机号"
|
||||
prop="driverPhone"
|
||||
class="waybill-manage-page__reassign-span-1"
|
||||
>
|
||||
<el-input
|
||||
v-model="reassignForm.driverPhone"
|
||||
placeholder="请输入"
|
||||
clearable
|
||||
maxlength="20"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="车牌号" prop="vehicleNo" class="waybill-manage-page__reassign-span-1">
|
||||
<el-input
|
||||
v-model="reassignForm.vehicleNo"
|
||||
placeholder="请输入车牌号"
|
||||
clearable
|
||||
maxlength="30"
|
||||
/>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="waybill-manage-page__reassign-footer">
|
||||
<el-button @click="reassignDrawer.visible = false">取消</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
:loading="reassignDrawer.submitting"
|
||||
@click="submitReassign"
|
||||
>
|
||||
确认派单
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-drawer>
|
||||
|
||||
<el-dialog
|
||||
v-model="mileageDialog.visible"
|
||||
width="520px"
|
||||
@@ -1934,15 +2026,88 @@
|
||||
<section-card title="执行详情">
|
||||
<el-tabs v-model="waybillProcessDetailTab" type="border-card">
|
||||
<el-tab-pane label="打卡详情" name="punch">
|
||||
<el-timeline v-if="waybillDetailProcessNodes.length">
|
||||
<div v-loading="waybillPunchRecordsLoading">
|
||||
<el-timeline v-if="waybillPunchRecords.length">
|
||||
<el-timeline-item
|
||||
v-for="(node, index) in waybillDetailProcessNodes"
|
||||
:key="node.key || index"
|
||||
:timestamp="node.time || ''"
|
||||
>{{ node.name || node.nodeName || node.label }}</el-timeline-item
|
||||
v-for="(record, index) in waybillPunchRecords"
|
||||
:key="record.nodeCode || record.id || index"
|
||||
:timestamp="record.punchTime || record.statusName || '未打卡'"
|
||||
:type="record.punched ? 'primary' : 'info'"
|
||||
:color="record.punched ? '#2b6ce8' : '#c0c4cc'"
|
||||
placement="top"
|
||||
>
|
||||
<div class="waybill-manage-page__punch-record">
|
||||
<div class="waybill-manage-page__punch-record-title">
|
||||
{{ record.nodeName || record.nodeCode || '打卡' }}
|
||||
<el-tag
|
||||
:type="record.punched ? 'success' : 'info'"
|
||||
size="small"
|
||||
effect="plain"
|
||||
>{{ record.statusName || (record.punched ? '已打卡' : '未打卡') }}</el-tag
|
||||
>
|
||||
<el-tag
|
||||
v-if="record.type === 'enroute'"
|
||||
size="small"
|
||||
type="info"
|
||||
effect="plain"
|
||||
>在途</el-tag
|
||||
>
|
||||
<el-tag
|
||||
v-if="record.exceptionFlag"
|
||||
size="small"
|
||||
type="danger"
|
||||
effect="plain"
|
||||
>异常</el-tag
|
||||
>
|
||||
</div>
|
||||
<template v-if="record.punched">
|
||||
<div
|
||||
v-if="record.address"
|
||||
class="waybill-manage-page__punch-record-line"
|
||||
>
|
||||
地点:{{ record.address }}
|
||||
</div>
|
||||
<div
|
||||
v-if="record.weight || record.volume || record.quantity"
|
||||
class="waybill-manage-page__punch-record-line"
|
||||
>
|
||||
<span v-if="record.weight">重量 {{ record.weight }}吨</span>
|
||||
<span v-if="record.volume">体积 {{ record.volume }}方</span>
|
||||
<span v-if="record.quantity">数量 {{ record.quantity }}</span>
|
||||
</div>
|
||||
<div
|
||||
v-if="record.remark"
|
||||
class="waybill-manage-page__punch-record-line"
|
||||
>
|
||||
备注:{{ record.remark }}
|
||||
</div>
|
||||
<div
|
||||
v-if="record.photos && record.photos.length"
|
||||
class="waybill-manage-page__punch-record-photos"
|
||||
>
|
||||
<div
|
||||
v-for="(photo, pi) in record.photos"
|
||||
:key="`${record.nodeCode || index}-${pi}`"
|
||||
class="waybill-manage-page__driver-upload-item"
|
||||
@click="previewDriverUploadPhoto(record.photos, pi)"
|
||||
>
|
||||
<el-image :src="photo.url" fit="cover" lazy />
|
||||
<div class="waybill-manage-page__driver-upload-label">
|
||||
{{ photo.label || `${record.nodeName}-凭证` }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div v-else class="waybill-manage-page__punch-record-line">暂未打卡</div>
|
||||
</div>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
<el-empty v-else description="暂无打卡详情" :image-size="50" />
|
||||
<el-empty
|
||||
v-else-if="!waybillPunchRecordsLoading"
|
||||
description="暂无打卡记录"
|
||||
:image-size="50"
|
||||
/>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane
|
||||
v-if="waybillHasRelatedVoucher"
|
||||
@@ -2012,9 +2177,30 @@
|
||||
/>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<!-- <el-tab-pane label="司机上传" name="driverUpload">
|
||||
<el-empty description="暂无司机上传数据" :image-size="50" />
|
||||
</el-tab-pane> -->
|
||||
<el-tab-pane label="司机上传" name="driverUpload">
|
||||
<div
|
||||
v-loading="waybillPunchRecordsLoading"
|
||||
class="waybill-manage-page__driver-upload-grid"
|
||||
>
|
||||
<div
|
||||
v-for="(photo, index) in waybillDriverUploads"
|
||||
:key="`${photo.url}-${index}`"
|
||||
class="waybill-manage-page__driver-upload-item"
|
||||
:title="photo.label"
|
||||
@click="previewDriverUploadPhoto(waybillDriverUploads, index)"
|
||||
>
|
||||
<el-image :src="photo.url" fit="cover" lazy />
|
||||
<div class="waybill-manage-page__driver-upload-label">
|
||||
{{ photo.label || '凭证' }}
|
||||
</div>
|
||||
</div>
|
||||
<el-empty
|
||||
v-if="!waybillPunchRecordsLoading && !waybillDriverUploads.length"
|
||||
description="暂无司机上传数据"
|
||||
:image-size="50"
|
||||
/>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</section-card>
|
||||
<section-card title="物流轨迹">
|
||||
@@ -2837,6 +3023,7 @@ import {
|
||||
getList as getProcessConfigList,
|
||||
getVoucherImages as getProcessConfigVoucherImages,
|
||||
} from '@/api/business/process-config';
|
||||
import { getPunchRecords as getWaybillPunchRecords } from '@/api/business/waybill-manage';
|
||||
import { getList as getDriverList } from '@/api/transportCapacity/driver';
|
||||
import { getDictionary } from '@/api/system/dictbiz';
|
||||
import { getDictionary as getSystemDictionary } from '@/api/system/dict';
|
||||
@@ -2848,7 +3035,7 @@ import { openImportDialog } from '@/utils/import-excel';
|
||||
import { applyTableMenuWidth } from '@/utils/table-menu';
|
||||
import { downloadFileByUrl, downloadXls } from '@/utils/util';
|
||||
import { isMobile } from '@/utils/validate';
|
||||
import { InfoFilled, Location, OfficeBuilding, Rank, Search } from '@element-plus/icons-vue';
|
||||
import { InfoFilled, Location, OfficeBuilding, Rank, Search, WarnTriangleFilled } from '@element-plus/icons-vue';
|
||||
import { ElImageViewer } from 'element-plus';
|
||||
import { OpenFileViewer } from '@open-file-viewer/vue';
|
||||
import {
|
||||
@@ -3023,6 +3210,7 @@ export default {
|
||||
PageAvueForm,
|
||||
PageDetail,
|
||||
InfoFilled,
|
||||
WarnTriangleFilled,
|
||||
Rank,
|
||||
ElImageViewer,
|
||||
OpenFileViewer,
|
||||
@@ -3079,6 +3267,9 @@ export default {
|
||||
detailLoading: false,
|
||||
detailRow: {},
|
||||
waybillDetailProcessNodes: [],
|
||||
waybillPunchRecords: [],
|
||||
waybillDriverUploads: [],
|
||||
waybillPunchRecordsLoading: false,
|
||||
waybillProcessDetailTab: 'punch',
|
||||
waybillHasRelatedVoucher: false,
|
||||
waybillVoucherImages: [],
|
||||
@@ -3100,6 +3291,41 @@ export default {
|
||||
submitting: false,
|
||||
row: null,
|
||||
},
|
||||
reassignDrawer: {
|
||||
visible: false,
|
||||
submitting: false,
|
||||
driverLoading: false,
|
||||
row: null,
|
||||
},
|
||||
reassignForm: {
|
||||
id: '',
|
||||
driverId: '',
|
||||
driverName: '',
|
||||
driverPhone: '',
|
||||
vehicleNo: '',
|
||||
},
|
||||
reassignDriverOptions: [],
|
||||
reassignRules: {
|
||||
driverName: [{ required: true, message: '请选择司机', trigger: 'change' }],
|
||||
driverPhone: [
|
||||
{ required: true, message: '请输入手机号', trigger: 'blur' },
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
if (!value) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
if (!isMobile(value)) {
|
||||
callback(new Error('请输入正确的手机号'));
|
||||
return;
|
||||
}
|
||||
callback();
|
||||
},
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
vehicleNo: [{ required: true, message: '请输入车牌号', trigger: 'blur' }],
|
||||
},
|
||||
mileageForm: {
|
||||
id: '',
|
||||
mileage: '',
|
||||
@@ -3602,6 +3828,7 @@ export default {
|
||||
},
|
||||
waybillProcessDetailTab(tab) {
|
||||
if (tab === 'batchSupplement') this.loadWaybillVoucherImages();
|
||||
if (tab === 'punch' || tab === 'driverUpload') this.loadWaybillPunchRecords();
|
||||
},
|
||||
'form.transportType'(value, oldValue) {
|
||||
if (!this.shippingInfoFormEnabled || this.suppressTransportTypeClear) return;
|
||||
@@ -3884,7 +4111,7 @@ export default {
|
||||
return false;
|
||||
}
|
||||
const status = this.statusValue(row);
|
||||
return ['pending', 'processing'].includes(status);
|
||||
return ['pending', 'processing', 'running'].includes(status);
|
||||
},
|
||||
canComplete(row) {
|
||||
if (
|
||||
@@ -3896,7 +4123,7 @@ export default {
|
||||
return false;
|
||||
}
|
||||
const status = this.statusValue(row);
|
||||
return status === 'processing';
|
||||
return status === 'processing' || status === 'running';
|
||||
},
|
||||
canMaintainMileage(row) {
|
||||
return (
|
||||
@@ -4163,6 +4390,9 @@ export default {
|
||||
this.detailRow = { ...row };
|
||||
this.waybillProcessDetailTab = 'punch';
|
||||
this.waybillHasRelatedVoucher = false;
|
||||
this.waybillDetailProcessNodes = [];
|
||||
this.waybillPunchRecords = [];
|
||||
this.waybillDriverUploads = [];
|
||||
this.waybillVoucherImages = [];
|
||||
this.waybillVoucherFolders = [];
|
||||
this.waybillVoucherFolder = null;
|
||||
@@ -4176,6 +4406,7 @@ export default {
|
||||
.then(res => {
|
||||
const detail = res?.data?.data || res?.data || row;
|
||||
this.detailRow = detail;
|
||||
this.loadWaybillPunchRecords();
|
||||
this.loadWaybillVoucherImages();
|
||||
if (detail.contractId) {
|
||||
return getContractDetail(detail.contractId)
|
||||
@@ -4259,6 +4490,37 @@ export default {
|
||||
})
|
||||
.catch(() => []);
|
||||
},
|
||||
loadWaybillPunchRecords() {
|
||||
const waybillId = this.detailRow?.id;
|
||||
if (!waybillId || this.waybillPunchRecordsLoading) return;
|
||||
this.waybillPunchRecordsLoading = true;
|
||||
const request =
|
||||
typeof this.api.getPunchRecords === 'function'
|
||||
? this.api.getPunchRecords(waybillId)
|
||||
: getWaybillPunchRecords(waybillId);
|
||||
request
|
||||
.then(res => {
|
||||
const data = res?.data?.data || res?.data || {};
|
||||
this.waybillPunchRecords = Array.isArray(data.records) ? data.records : [];
|
||||
this.waybillDriverUploads = Array.isArray(data.driverUploads)
|
||||
? data.driverUploads
|
||||
: [];
|
||||
})
|
||||
.catch(() => {
|
||||
this.waybillPunchRecords = [];
|
||||
this.waybillDriverUploads = [];
|
||||
})
|
||||
.finally(() => {
|
||||
this.waybillPunchRecordsLoading = false;
|
||||
});
|
||||
},
|
||||
previewDriverUploadPhoto(photos, index) {
|
||||
const list = (photos || []).map(item => item?.url).filter(Boolean);
|
||||
if (!list.length) return;
|
||||
this.attachmentImagePreviewUrls = list;
|
||||
this.attachmentImagePreviewIndex = Math.min(Math.max(Number(index) || 0, 0), list.length - 1);
|
||||
this.attachmentImagePreviewVisible = true;
|
||||
},
|
||||
loadWaybillVoucherImages() {
|
||||
if (
|
||||
!this.detailRow.id ||
|
||||
@@ -7813,6 +8075,10 @@ export default {
|
||||
this.$refs.mileageFormRef?.clearValidate();
|
||||
},
|
||||
handleAction(action, row) {
|
||||
if (action === 'reassign') {
|
||||
this.openReassignDrawer(row);
|
||||
return;
|
||||
}
|
||||
const actionName = {
|
||||
enable: '启用',
|
||||
disable: '停用',
|
||||
@@ -7831,6 +8097,113 @@ export default {
|
||||
this.onLoad(this.page, this.query);
|
||||
});
|
||||
},
|
||||
driverRejectReasonText(row) {
|
||||
if (!row) return '';
|
||||
if (typeof this.config.getDriverRejectReason === 'function') {
|
||||
return this.config.getDriverRejectReason(row) || '';
|
||||
}
|
||||
return String(
|
||||
row.driverRejectReason ||
|
||||
row.driverRefuseReason ||
|
||||
row.acceptRejectReason ||
|
||||
row.rejectReason ||
|
||||
''
|
||||
).trim();
|
||||
},
|
||||
openReassignDrawer(row) {
|
||||
if (!row?.id) return;
|
||||
this.reassignDrawer.row = row;
|
||||
this.reassignForm = {
|
||||
id: row.id,
|
||||
driverId: '',
|
||||
driverName: '',
|
||||
driverPhone: '',
|
||||
vehicleNo: '',
|
||||
};
|
||||
this.reassignDrawer.visible = true;
|
||||
this.$nextTick(() => this.$refs.reassignFormRef?.clearValidate());
|
||||
},
|
||||
resetReassignDrawer() {
|
||||
this.reassignDrawer.row = null;
|
||||
this.reassignDrawer.submitting = false;
|
||||
this.reassignDrawer.driverLoading = false;
|
||||
this.reassignForm = {
|
||||
id: '',
|
||||
driverId: '',
|
||||
driverName: '',
|
||||
driverPhone: '',
|
||||
vehicleNo: '',
|
||||
};
|
||||
this.reassignDriverOptions = [];
|
||||
this.$refs.reassignFormRef?.clearValidate();
|
||||
},
|
||||
fetchReassignDriverSuggestions(queryString, callback) {
|
||||
const keyword = String(queryString || '').trim();
|
||||
this.reassignDrawer.driverLoading = true;
|
||||
getDriverList(1, 20, { ...(keyword ? { driverName: keyword } : {}), posts: '司机' })
|
||||
.then(res => {
|
||||
const records = extractRecords(res);
|
||||
this.reassignDriverOptions = records;
|
||||
callback(
|
||||
records.map(item => ({
|
||||
...item,
|
||||
value: item.driverName || item.name || '',
|
||||
}))
|
||||
);
|
||||
})
|
||||
.catch(error => {
|
||||
window.console.log(error);
|
||||
callback([]);
|
||||
})
|
||||
.finally(() => {
|
||||
this.reassignDrawer.driverLoading = false;
|
||||
});
|
||||
},
|
||||
handleReassignDriverSelect(item = {}) {
|
||||
const value = item.driverName || item.name || '';
|
||||
const driverPhone = item.mobile || item.phone || item.driverPhone || '';
|
||||
const drivingVehicle = String(item.drivingVehicle || '').trim();
|
||||
this.reassignForm.driverId = item.id || '';
|
||||
this.reassignForm.driverName = value;
|
||||
if (driverPhone) this.reassignForm.driverPhone = driverPhone;
|
||||
if (drivingVehicle) this.reassignForm.vehicleNo = drivingVehicle;
|
||||
},
|
||||
handleReassignDriverChange(value) {
|
||||
const driver = this.reassignDriverOptions.find(item =>
|
||||
[item.driverName, item.name].some(name => String(name || '') === String(value || ''))
|
||||
);
|
||||
this.reassignForm.driverId = driver?.id || '';
|
||||
this.reassignForm.driverName = value || '';
|
||||
if (driver) {
|
||||
this.reassignForm.driverPhone =
|
||||
driver.mobile || driver.phone || driver.driverPhone || this.reassignForm.driverPhone || '';
|
||||
if (String(driver.drivingVehicle || '').trim()) {
|
||||
this.reassignForm.vehicleNo = String(driver.drivingVehicle).trim();
|
||||
}
|
||||
}
|
||||
},
|
||||
submitReassign() {
|
||||
this.$refs.reassignFormRef?.validate(valid => {
|
||||
if (!valid) return;
|
||||
this.reassignDrawer.submitting = true;
|
||||
this.api
|
||||
.reassign({
|
||||
id: this.reassignForm.id,
|
||||
driverId: this.reassignForm.driverId || null,
|
||||
driverName: String(this.reassignForm.driverName || '').trim(),
|
||||
driverPhone: String(this.reassignForm.driverPhone || '').trim(),
|
||||
vehicleNo: String(this.reassignForm.vehicleNo || '').trim(),
|
||||
})
|
||||
.then(() => {
|
||||
this.$message.success('重新派单成功');
|
||||
this.reassignDrawer.visible = false;
|
||||
this.onLoad(this.page, this.query);
|
||||
})
|
||||
.finally(() => {
|
||||
this.reassignDrawer.submitting = false;
|
||||
});
|
||||
});
|
||||
},
|
||||
handleCustomOperation(operation, row) {
|
||||
if (operation.action === 'createFromTemplate') {
|
||||
this.api.getDetail(row.id).then(res => {
|
||||
@@ -7933,7 +8306,7 @@ export default {
|
||||
}
|
||||
const unavailableWaybills = this.selectionList.filter(
|
||||
row =>
|
||||
this.statusValue(row) !== 'pending' ||
|
||||
!['processing', 'running'].includes(this.statusValue(row)) ||
|
||||
row.loadingId ||
|
||||
row.loadingManageId ||
|
||||
row.loadingNo
|
||||
@@ -9075,6 +9448,63 @@ export default {
|
||||
min-height: 120px;
|
||||
}
|
||||
|
||||
&__punch-record {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
&__punch-record-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
&__punch-record-line {
|
||||
font-size: 13px;
|
||||
color: #606266;
|
||||
|
||||
span + span {
|
||||
margin-left: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
&__punch-record-photos,
|
||||
&__driver-upload-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(5, minmax(0, 1fr));
|
||||
gap: 10px;
|
||||
margin-top: 4px;
|
||||
min-height: 80px;
|
||||
}
|
||||
|
||||
&__driver-upload-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
cursor: pointer;
|
||||
|
||||
.el-image {
|
||||
display: block;
|
||||
width: 100%;
|
||||
aspect-ratio: 1;
|
||||
border: 1px solid #eff1f7;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
&__driver-upload-label {
|
||||
font-size: 12px;
|
||||
line-height: 1.4;
|
||||
color: #606266;
|
||||
text-align: center;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
&__voucher-image-item {
|
||||
position: relative;
|
||||
aspect-ratio: 1;
|
||||
@@ -9456,6 +9886,78 @@ export default {
|
||||
:global(.waybill-manage-page__mileage-dialog .waybill-manage-page__mileage-form .el-textarea) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.waybill-manage-page__status-cell {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.waybill-manage-page__reject-tip-icon {
|
||||
color: #e6a23c;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
:global(.waybill-manage-page__reassign-drawer .el-drawer__body) {
|
||||
padding: 12px 20px 0;
|
||||
}
|
||||
|
||||
:global(.waybill-manage-page__reassign-drawer .waybill-manage-page__reassign-form) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.waybill-manage-page__reassign-row {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
column-gap: 16px;
|
||||
row-gap: 8px;
|
||||
margin-bottom: 12px;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.waybill-manage-page__reassign-span-1 {
|
||||
grid-column: span 1;
|
||||
margin-bottom: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.waybill-manage-page__reassign-meta {
|
||||
grid-column: span 1;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
min-width: 0;
|
||||
line-height: 32px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.waybill-manage-page__reassign-meta--wide {
|
||||
grid-column: span 3;
|
||||
}
|
||||
|
||||
.waybill-manage-page__reassign-meta-label {
|
||||
flex: none;
|
||||
width: 72px;
|
||||
color: #606266;
|
||||
text-align: right;
|
||||
padding-right: 12px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.waybill-manage-page__reassign-meta-value {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
color: #303133;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.waybill-manage-page__reassign-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.el-input__icon {
|
||||
color: #1e90ff !important;
|
||||
}
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
<el-radio-group
|
||||
v-model="row.confirmMode"
|
||||
:disabled="dialogReadonly || !row.enabled"
|
||||
@change="syncNodeFields"
|
||||
@change="handleAcceptConfirmChange(row)"
|
||||
>
|
||||
<el-radio label="yes">是</el-radio>
|
||||
<el-radio label="no_confirm_accept">无需确认接单</el-radio>
|
||||
@@ -235,12 +235,7 @@
|
||||
</div>
|
||||
<div class="process-config-form__require-line">
|
||||
<span class="process-config-form__require-label">确认接单人</span>
|
||||
<el-checkbox
|
||||
v-model="row.confirmDriver"
|
||||
:disabled="dialogReadonly || !row.enabled || row.confirmMode !== 'yes'"
|
||||
@change="syncNodeFields"
|
||||
>司机</el-checkbox
|
||||
>
|
||||
<el-checkbox v-model="row.confirmDriver" :disabled="true">司机</el-checkbox>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -551,10 +546,19 @@ export default {
|
||||
hasPermission(code) {
|
||||
return this.isAdmin || this.validData(this.permission && this.permission[code], false);
|
||||
},
|
||||
handleAcceptConfirmChange(row) {
|
||||
row.confirmDriver = row.confirmMode === 'yes';
|
||||
this.syncNodeFields();
|
||||
},
|
||||
handleReturnConfirmChange(row) {
|
||||
row.confirmInternal = row.confirmMode === 'yes';
|
||||
this.syncNodeFields();
|
||||
},
|
||||
enforceAcceptDriver(node) {
|
||||
if (!node || (node.key !== 'accept' && node.name !== '接单')) return node;
|
||||
node.confirmDriver = node.confirmMode === 'yes';
|
||||
return node;
|
||||
},
|
||||
initDeptOptions() {
|
||||
getDeptTree(this.userInfo.tenantId).then(res => {
|
||||
const deptColumn = this.findColumn(this.option.column, 'searchDeptId');
|
||||
@@ -610,7 +614,7 @@ export default {
|
||||
return 'warning';
|
||||
},
|
||||
canEdit(row) {
|
||||
return this.hasPermission('process_config_edit') && !row.readonly;
|
||||
return this.hasPermission('process_config_edit') && !row.readonly && !row.hasRelatedWaybill;
|
||||
},
|
||||
canDelete(row) {
|
||||
return (
|
||||
@@ -626,7 +630,7 @@ export default {
|
||||
return this.hasPermission('process_config_disable') && !row.readonly && row.status === 1;
|
||||
},
|
||||
cloneNode(template) {
|
||||
return {
|
||||
const node = {
|
||||
enabled: false,
|
||||
punch: true,
|
||||
location: true,
|
||||
@@ -641,10 +645,16 @@ export default {
|
||||
supportVoucher: false,
|
||||
voucherOptions: [],
|
||||
confirmMode: 'yes',
|
||||
confirmDriver: false,
|
||||
confirmDriver: true,
|
||||
confirmInternal: false,
|
||||
...template,
|
||||
};
|
||||
if ((node.key === 'accept' || node.name === '接单') && node.confirmMode === 'yes') {
|
||||
node.confirmDriver = true;
|
||||
} else if ((node.key === 'accept' || node.name === '接单') && node.confirmMode !== 'yes') {
|
||||
node.confirmDriver = false;
|
||||
}
|
||||
return node;
|
||||
},
|
||||
resetNodeRows() {
|
||||
this.nodeRows = NODE_TEMPLATES.map(item => this.cloneNode(item));
|
||||
@@ -769,6 +779,7 @@ export default {
|
||||
this.syncNodeFields();
|
||||
},
|
||||
syncNodeFields() {
|
||||
this.nodeRows.forEach(item => this.enforceAcceptDriver(item));
|
||||
const enabledNodes = this.nodeRows.filter(item => item.enabled);
|
||||
this.form.includedNodes = enabledNodes.map(item => item.name).join(',');
|
||||
this.form.nodeConfigJson = JSON.stringify(
|
||||
@@ -935,7 +946,7 @@ export default {
|
||||
);
|
||||
},
|
||||
openConfig(row, readonly = false) {
|
||||
this.dialogReadonly = readonly;
|
||||
this.dialogReadonly = readonly || !!(row && row.hasRelatedWaybill);
|
||||
if (!row || !row.id) {
|
||||
this.form = {
|
||||
configName: '',
|
||||
@@ -952,6 +963,7 @@ export default {
|
||||
}
|
||||
api.getDetail(row.id).then(res => {
|
||||
const detail = res.data.data || {};
|
||||
this.dialogReadonly = readonly || !!detail.hasRelatedWaybill;
|
||||
this.form = {
|
||||
...detail,
|
||||
defaultFinishDays:
|
||||
@@ -969,6 +981,10 @@ export default {
|
||||
},
|
||||
submitConfig(mode) {
|
||||
if (this.submitting) return;
|
||||
if (this.form.id && this.form.hasRelatedWaybill) {
|
||||
this.$message.warning('该项目已有运单,过程配置不可修改');
|
||||
return;
|
||||
}
|
||||
this.syncNodeFields();
|
||||
const submitRow = this.normalizeRow({
|
||||
...this.form,
|
||||
|
||||
Reference in New Issue
Block a user