✨ 打卡详情展示异常状态并新增异常节点展示
运单详情「打卡详情」时间轴按节点关联异常处置:存在未了结异常时显示「异常」标签 与「跟进」链接,全部处理完成时显示「已处理异常」与「详情」链接。异常跟节点走, 节点未打卡也可能有异常。 异常来源改用异常处置表而非打卡行的 exception_flag —— 后者由打卡接口写入且未随 异常上报维护,实测有异常的节点该值仍为 0,原「异常」标签从未显示过。 「已处理」的口径与后端打卡门禁一致(已完成、已撤销都放行),故「处理中」仍算 未处理。 异常节点字段补入各处异常信息:运单详情「异常上报」表格、编辑弹窗、跟进/详情 弹窗,统一放在异常类型之前;表格内空节点显示「-」而不是留白(存量异常无节点)。 过程节点列显示配置中的中文名而非原始 key(库里混存 key 与中文名两种格式)。 删除打卡详情中在途节点的「在途」标签 —— 节点名已表明是在途。
This commit is contained in:
@@ -98,6 +98,24 @@ const getProcessConfigNodes = row => {
|
|||||||
|
|
||||||
const isAcceptProcessNode = node => node.key === 'accept' || node.name === '接单';
|
const isAcceptProcessNode = node => node.key === 'accept' || node.name === '接单';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 过程节点列显示文案。
|
||||||
|
*
|
||||||
|
* 该列在库里存的是「原始值」,且历史上写入了两种格式:司机打卡推进写的是配置的
|
||||||
|
* key(unload / dispatch / load …),配载单新建默认值写的是中文名(接单)。
|
||||||
|
* 故这里按两种格式都认一次,认不出就原样显示,不把值吞掉。
|
||||||
|
*
|
||||||
|
* 运单创建后、司机首次打卡前该字段为空,属正常——显示「-」,避免留白看不出是没值还是没渲染。
|
||||||
|
*/
|
||||||
|
const formatCurrentProcessNode = row => {
|
||||||
|
const value = String(getFirstValue(row, ['currentProcessNode', 'currentNode']) || '').trim();
|
||||||
|
if (!value) return '-';
|
||||||
|
const node = getProcessConfigNodes(row).find(
|
||||||
|
item => item.key === value || item.name === value
|
||||||
|
);
|
||||||
|
return node?.name || node?.key || value;
|
||||||
|
};
|
||||||
|
|
||||||
const isTruthyFlag = value => {
|
const isTruthyFlag = value => {
|
||||||
if (value === true || value === 1) return true;
|
if (value === true || value === 1) return true;
|
||||||
if (value === false || value === 0 || value === null || value === undefined || value === '') {
|
if (value === false || value === 0 || value === null || value === undefined || value === '') {
|
||||||
@@ -608,6 +626,7 @@ export const option = {
|
|||||||
{
|
{
|
||||||
label: '过程节点',
|
label: '过程节点',
|
||||||
prop: 'currentProcessNode',
|
prop: 'currentProcessNode',
|
||||||
|
formatter: row => formatCurrentProcessNode(row),
|
||||||
minWidth: 140,
|
minWidth: 140,
|
||||||
addDisplay: false,
|
addDisplay: false,
|
||||||
editDisplay: false,
|
editDisplay: false,
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ export const tableColumns = [
|
|||||||
{ label: '上报时间', prop: 'reportTime', minWidth: 170 },
|
{ label: '上报时间', prop: 'reportTime', minWidth: 170 },
|
||||||
{ label: '异常类型', prop: 'exceptionTypeName', minWidth: 160 },
|
{ label: '异常类型', prop: 'exceptionTypeName', minWidth: 160 },
|
||||||
{ label: '上报说明', prop: 'reportDescription', minWidth: 180 },
|
{ label: '上报说明', prop: 'reportDescription', minWidth: 180 },
|
||||||
|
{ label: '异常节点', prop: 'nodeName', minWidth: 120 },
|
||||||
{ label: '承运商', prop: 'carrierName', minWidth: 160 },
|
{ label: '承运商', prop: 'carrierName', minWidth: 160 },
|
||||||
{ label: '最新跟进说明', prop: 'latestFollowContent', minWidth: 200 },
|
{ label: '最新跟进说明', prop: 'latestFollowContent', minWidth: 200 },
|
||||||
{ label: '状态', prop: 'disposalStatusName', minWidth: 120 },
|
{ label: '状态', prop: 'disposalStatusName', minWidth: 120 },
|
||||||
@@ -100,6 +101,7 @@ export const detailWaybillFields = [
|
|||||||
export const detailExceptionFields = [
|
export const detailExceptionFields = [
|
||||||
{ label: '上报人', prop: 'reporterName' },
|
{ label: '上报人', prop: 'reporterName' },
|
||||||
{ label: '上报时间', prop: 'reportTime' },
|
{ label: '上报时间', prop: 'reportTime' },
|
||||||
|
{ label: '异常节点', prop: 'nodeName' },
|
||||||
{ label: '异常类型', prop: 'exceptionTypeName' },
|
{ label: '异常类型', prop: 'exceptionTypeName' },
|
||||||
{ label: '异常原因', prop: 'exceptionReason' },
|
{ label: '异常原因', prop: 'exceptionReason' },
|
||||||
{ label: '上报说明', prop: 'reportDescription', span: 2 },
|
{ label: '上报说明', prop: 'reportDescription', span: 2 },
|
||||||
|
|||||||
@@ -2061,6 +2061,11 @@
|
|||||||
<el-table v-loading="waybillExceptionLoading" :data="waybillExceptionRows" border>
|
<el-table v-loading="waybillExceptionLoading" :data="waybillExceptionRows" border>
|
||||||
<el-table-column type="index" label="序号" width="70" align="center" />
|
<el-table-column type="index" label="序号" width="70" align="center" />
|
||||||
<el-table-column prop="reporterName" label="上报人" min-width="120" align="center" />
|
<el-table-column prop="reporterName" label="上报人" min-width="120" align="center" />
|
||||||
|
<el-table-column label="异常节点" min-width="120" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ row.nodeName || '-' }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
prop="exceptionTypeName"
|
prop="exceptionTypeName"
|
||||||
label="异常类型"
|
label="异常类型"
|
||||||
@@ -2159,7 +2164,7 @@
|
|||||||
<el-timeline v-if="waybillPunchRecords.length">
|
<el-timeline v-if="waybillPunchRecords.length">
|
||||||
<el-timeline-item
|
<el-timeline-item
|
||||||
v-for="(record, index) in orderedWaybillPunchRecords"
|
v-for="(record, index) in orderedWaybillPunchRecords"
|
||||||
:key="`${record.nodeCode || record.id || 'punch'}-${index}`"
|
:key="waybillPunchRecordKey(record, index)"
|
||||||
:timestamp="record.punchTime || record.statusName || '未打卡'"
|
:timestamp="record.punchTime || record.statusName || '未打卡'"
|
||||||
:type="record.punched ? 'primary' : 'info'"
|
:type="record.punched ? 'primary' : 'info'"
|
||||||
:color="record.punched ? '#2b6ce8' : '#c0c4cc'"
|
:color="record.punched ? '#2b6ce8' : '#c0c4cc'"
|
||||||
@@ -2174,20 +2179,42 @@
|
|||||||
effect="plain"
|
effect="plain"
|
||||||
>{{ record.statusName || (record.punched ? '已打卡' : '未打卡') }}</el-tag
|
>{{ record.statusName || (record.punched ? '已打卡' : '未打卡') }}</el-tag
|
||||||
>
|
>
|
||||||
<el-tag
|
<template
|
||||||
v-if="record.type === 'enroute'"
|
v-if="waybillPunchRecordExceptions[waybillPunchRecordKey(record, index)]"
|
||||||
size="small"
|
|
||||||
type="info"
|
|
||||||
effect="plain"
|
|
||||||
>在途</el-tag
|
|
||||||
>
|
|
||||||
<el-tag
|
|
||||||
v-if="record.exceptionFlag"
|
|
||||||
size="small"
|
|
||||||
type="danger"
|
|
||||||
effect="plain"
|
|
||||||
>异常</el-tag
|
|
||||||
>
|
>
|
||||||
|
<el-tag
|
||||||
|
size="small"
|
||||||
|
:type="
|
||||||
|
waybillPunchRecordExceptions[waybillPunchRecordKey(record, index)]
|
||||||
|
.unfinished
|
||||||
|
? 'danger'
|
||||||
|
: 'success'
|
||||||
|
"
|
||||||
|
effect="plain"
|
||||||
|
>{{
|
||||||
|
waybillPunchRecordExceptions[waybillPunchRecordKey(record, index)]
|
||||||
|
.unfinished
|
||||||
|
? '异常'
|
||||||
|
: '已处理异常'
|
||||||
|
}}</el-tag
|
||||||
|
>
|
||||||
|
<el-link
|
||||||
|
class="waybill-manage-page__punch-record-exception-link"
|
||||||
|
type="primary"
|
||||||
|
:underline="false"
|
||||||
|
@click="
|
||||||
|
openWaybillPunchRecordException(
|
||||||
|
waybillPunchRecordExceptions[waybillPunchRecordKey(record, index)]
|
||||||
|
)
|
||||||
|
"
|
||||||
|
>{{
|
||||||
|
waybillPunchRecordExceptions[waybillPunchRecordKey(record, index)]
|
||||||
|
.unfinished
|
||||||
|
? '跟进'
|
||||||
|
: '详情'
|
||||||
|
}}</el-link
|
||||||
|
>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<template v-if="record.punched">
|
<template v-if="record.punched">
|
||||||
<div
|
<div
|
||||||
@@ -2498,6 +2525,13 @@
|
|||||||
}}</span>
|
}}</span>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="异常节点">
|
||||||
|
<span class="waybill-manage-page__exception-readonly">{{
|
||||||
|
waybillExceptionValue('nodeName')
|
||||||
|
}}</span>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="异常类型" prop="exceptionType">
|
<el-form-item label="异常类型" prop="exceptionType">
|
||||||
<el-select
|
<el-select
|
||||||
@@ -3508,6 +3542,9 @@ const standaloneWaybillFormPaths = [standaloneWaybillListRoute, standaloneWaybil
|
|||||||
const standaloneWaybillDetailRoute = '/business/waybill-manage/detail';
|
const standaloneWaybillDetailRoute = '/business/waybill-manage/detail';
|
||||||
const loadingCreateWaybillsStorageKey = 'loading-manage-create-waybills';
|
const loadingCreateWaybillsStorageKey = 'loading-manage-create-waybills';
|
||||||
const waybillManageListRefreshKey = 'waybill-manage-list-refresh';
|
const waybillManageListRefreshKey = 'waybill-manage-list-refresh';
|
||||||
|
// 视为「已经处理完」的异常处置状态,与后端打卡门禁同一口径(已完成、已撤销都放行),
|
||||||
|
// 所以「处理中」仍算未完成,节点上继续显示异常。
|
||||||
|
const settledExceptionStatuses = ['completed', 'revoked'];
|
||||||
|
|
||||||
const attachmentViewerPlugins = [
|
const attachmentViewerPlugins = [
|
||||||
imagePlugin(),
|
imagePlugin(),
|
||||||
@@ -4072,6 +4109,7 @@ export default {
|
|||||||
return [
|
return [
|
||||||
{ label: '上报人', prop: 'reporterName' },
|
{ label: '上报人', prop: 'reporterName' },
|
||||||
{ label: '上报时间', prop: 'reportTime' },
|
{ label: '上报时间', prop: 'reportTime' },
|
||||||
|
{ label: '异常节点', prop: 'nodeName' },
|
||||||
{ label: '异常类型', prop: 'exceptionTypeName' },
|
{ label: '异常类型', prop: 'exceptionTypeName' },
|
||||||
{ label: '异常原因', prop: 'exceptionReason' },
|
{ label: '异常原因', prop: 'exceptionReason' },
|
||||||
{ label: '上报说明', prop: 'reportDescription', span: 2 },
|
{ label: '上报说明', prop: 'reportDescription', span: 2 },
|
||||||
@@ -4124,6 +4162,47 @@ export default {
|
|||||||
})
|
})
|
||||||
.map(item => item.record);
|
.map(item => item.record);
|
||||||
},
|
},
|
||||||
|
// 打卡记录 → 该节点的异常处置。异常跟节点走,不跟打卡走:节点未打卡也可能有异常。
|
||||||
|
// 数据源是异常处置表(waybillExceptionRows),不是打卡行上的 exception_flag
|
||||||
|
// ——后者由打卡接口写入且未随异常上报维护,实测有异常的节点它的值仍是 0。
|
||||||
|
// 匹配优先用 nodeCode,回退 nodeName:两者各有一批历史数据只填了一边。
|
||||||
|
waybillPunchRecordExceptions() {
|
||||||
|
const rows = Array.isArray(this.waybillExceptionRows) ? this.waybillExceptionRows : [];
|
||||||
|
const index = new Map();
|
||||||
|
rows.forEach(row => {
|
||||||
|
const nodeCode = String(row.nodeCode || '').trim().toLowerCase();
|
||||||
|
const nodeName = String(row.nodeName || '').trim();
|
||||||
|
if (nodeCode) {
|
||||||
|
if (!index.has(`code:${nodeCode}`)) index.set(`code:${nodeCode}`, []);
|
||||||
|
index.get(`code:${nodeCode}`).push(row);
|
||||||
|
}
|
||||||
|
if (nodeName) {
|
||||||
|
if (!index.has(`name:${nodeName}`)) index.set(`name:${nodeName}`, []);
|
||||||
|
index.get(`name:${nodeName}`).push(row);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const result = {};
|
||||||
|
(Array.isArray(this.waybillPunchRecords) ? this.waybillPunchRecords : []).forEach(
|
||||||
|
(record, position) => {
|
||||||
|
const nodeCode = String(record.nodeCode || '').trim().toLowerCase();
|
||||||
|
const nodeName = String(record.nodeName || '').trim();
|
||||||
|
const matched =
|
||||||
|
(nodeCode && index.get(`code:${nodeCode}`)) ||
|
||||||
|
(nodeName && index.get(`name:${nodeName}`)) ||
|
||||||
|
[];
|
||||||
|
if (!matched.length) return;
|
||||||
|
const isSettled = item => settledExceptionStatuses.includes(item.disposalStatus);
|
||||||
|
result[this.waybillPunchRecordKey(record, position)] = {
|
||||||
|
total: matched.length,
|
||||||
|
// 只要还有一条没处理完,这个节点就算「有事没完」,链接送去跟进
|
||||||
|
unfinished: matched.filter(item => !isSettled(item)).length,
|
||||||
|
// 取未完成的那条(没有则取第一条)作为跟进/详情的打开对象
|
||||||
|
target: matched.find(item => !isSettled(item)) || matched[0],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return result;
|
||||||
|
},
|
||||||
isStandaloneWaybillPage() {
|
isStandaloneWaybillPage() {
|
||||||
return this.standaloneFormPage && standaloneWaybillFormPaths.includes(this.$route.path);
|
return this.standaloneFormPage && standaloneWaybillFormPaths.includes(this.$route.path);
|
||||||
},
|
},
|
||||||
@@ -5483,6 +5562,16 @@ export default {
|
|||||||
const fallback = new Date(text.replace(/-/g, '/')).getTime();
|
const fallback = new Date(text.replace(/-/g, '/')).getTime();
|
||||||
return Number.isNaN(fallback) ? null : fallback;
|
return Number.isNaN(fallback) ? null : fallback;
|
||||||
},
|
},
|
||||||
|
// 时间轴 key:与模板 v-for 的 :key 保持同一算法,异常索引也用它做键,
|
||||||
|
// 两边各写一份迟早会错位。
|
||||||
|
waybillPunchRecordKey(record, index) {
|
||||||
|
return `${record?.nodeCode || record?.id || 'punch'}-${index}`;
|
||||||
|
},
|
||||||
|
// 未处理完 → 跟进;全处理完 → 只读详情(与异常处置列表的操作口径一致)
|
||||||
|
openWaybillPunchRecordException(exception) {
|
||||||
|
if (!exception?.target) return;
|
||||||
|
this.openWaybillException(exception.target, exception.unfinished ? 'follow' : 'detail');
|
||||||
|
},
|
||||||
loadWaybillPunchRecords() {
|
loadWaybillPunchRecords() {
|
||||||
const waybillId = this.detailRow?.id;
|
const waybillId = this.detailRow?.id;
|
||||||
if (!waybillId || this.waybillPunchRecordsLoading) return;
|
if (!waybillId || this.waybillPunchRecordsLoading) return;
|
||||||
@@ -11117,6 +11206,12 @@ export default {
|
|||||||
color: #303133;
|
color: #303133;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 异常标签与文本链接紧挨在节点名右边,不推到行尾
|
||||||
|
&__punch-record-exception-link {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
&__punch-record-line {
|
&__punch-record-line {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
color: #606266;
|
color: #606266;
|
||||||
|
|||||||
@@ -429,7 +429,7 @@ const NODE_TEMPLATES = [
|
|||||||
type: 'confirm',
|
type: 'confirm',
|
||||||
supportVoucher: false,
|
supportVoucher: false,
|
||||||
},
|
},
|
||||||
{ key: 'arrive_scene', name: '到场', type: 'punch', enabled: false },
|
{ key: 'arrive_scene', name: '到场', type: 'punch', enabled: false, supportCargo: false },
|
||||||
{
|
{
|
||||||
key: 'load',
|
key: 'load',
|
||||||
name: '装货',
|
name: '装货',
|
||||||
@@ -485,6 +485,7 @@ const NODE_TEMPLATES = [
|
|||||||
key: 'return',
|
key: 'return',
|
||||||
name: '回单',
|
name: '回单',
|
||||||
type: 'confirm',
|
type: 'confirm',
|
||||||
|
supportCargo: false,
|
||||||
supportVoucher: true,
|
supportVoucher: true,
|
||||||
voucherOptions: ['磅单', '签收单'],
|
voucherOptions: ['磅单', '签收单'],
|
||||||
},
|
},
|
||||||
@@ -654,7 +655,9 @@ export default {
|
|||||||
enabled: false,
|
enabled: false,
|
||||||
punch: true,
|
punch: true,
|
||||||
location: true,
|
location: true,
|
||||||
uploadCargo: true,
|
// 不支持货量的节点(到场/回单/接单等)不得默认勾上,否则会写出
|
||||||
|
// 「上传货量为是、货量类型为空」的配置,而这类节点在界面上没有货量开关可关
|
||||||
|
uploadCargo: !!template?.supportCargo,
|
||||||
cargoTypes: [],
|
cargoTypes: [],
|
||||||
uploadVoucher: undefined,
|
uploadVoucher: undefined,
|
||||||
voucherTypes: [],
|
voucherTypes: [],
|
||||||
@@ -669,6 +672,10 @@ export default {
|
|||||||
confirmInternal: false,
|
confirmInternal: false,
|
||||||
...template,
|
...template,
|
||||||
};
|
};
|
||||||
|
if (!node.supportCargo) {
|
||||||
|
node.uploadCargo = false;
|
||||||
|
node.cargoTypes = [];
|
||||||
|
}
|
||||||
if ((node.key === 'accept' || node.name === '接单') && node.confirmMode === 'yes') {
|
if ((node.key === 'accept' || node.name === '接单') && node.confirmMode === 'yes') {
|
||||||
node.confirmDriver = true;
|
node.confirmDriver = true;
|
||||||
} else if ((node.key === 'accept' || node.name === '接单') && node.confirmMode !== 'yes') {
|
} else if ((node.key === 'accept' || node.name === '接单') && node.confirmMode !== 'yes') {
|
||||||
@@ -941,6 +948,15 @@ export default {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 勾了上传货量却没选类型,在小程序侧等同于不采集货量;此处直接拦住,
|
||||||
|
// 避免保存出「显示采集、实际不采集」的配置
|
||||||
|
const cargoWithoutTypes = this.nodeRows.find(
|
||||||
|
item => item.enabled && item.supportCargo && item.uploadCargo && !(item.cargoTypes || []).length
|
||||||
|
);
|
||||||
|
if (cargoWithoutTypes) {
|
||||||
|
this.$message.warning(`「${cargoWithoutTypes.name}」已选择上传货量,请至少勾选一个货量类型`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
needsDuplicateVoucherConfirm() {
|
needsDuplicateVoucherConfirm() {
|
||||||
|
|||||||
@@ -215,6 +215,13 @@
|
|||||||
}}</span>
|
}}</span>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="异常节点">
|
||||||
|
<span class="exception-disposal-page__readonly">{{
|
||||||
|
detailValue('nodeName')
|
||||||
|
}}</span>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="异常类型" prop="exceptionType">
|
<el-form-item label="异常类型" prop="exceptionType">
|
||||||
<el-select
|
<el-select
|
||||||
|
|||||||
Reference in New Issue
Block a user