✨ 运单详情页新增异常上报卡片
在运费信息之后展示该运单关联的异常处置记录,表格沿用异常处置页的操作与权限, 列只保留上报人、异常类型、上报说明、承运商、最新跟进说明、状态。 跟进/编辑/详情弹窗在本页内完成,操作后局部刷新该卡片,不跳转列表页。
This commit is contained in:
@@ -2057,6 +2057,100 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section-card>
|
</section-card>
|
||||||
|
<section-card title="异常上报">
|
||||||
|
<el-table v-loading="waybillExceptionLoading" :data="waybillExceptionRows" border>
|
||||||
|
<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="exceptionTypeName"
|
||||||
|
label="异常类型"
|
||||||
|
min-width="160"
|
||||||
|
align="center"
|
||||||
|
show-overflow-tooltip
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
prop="reportDescription"
|
||||||
|
label="上报说明"
|
||||||
|
min-width="180"
|
||||||
|
align="center"
|
||||||
|
show-overflow-tooltip
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
prop="carrierName"
|
||||||
|
label="承运商"
|
||||||
|
min-width="160"
|
||||||
|
align="center"
|
||||||
|
show-overflow-tooltip
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
prop="latestFollowContent"
|
||||||
|
label="最新跟进说明"
|
||||||
|
min-width="200"
|
||||||
|
align="center"
|
||||||
|
show-overflow-tooltip
|
||||||
|
/>
|
||||||
|
<el-table-column label="状态" min-width="120" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-link
|
||||||
|
:type="row.disposalStatus === 'completed' ? 'info' : 'primary'"
|
||||||
|
@click="
|
||||||
|
openWaybillException(
|
||||||
|
row,
|
||||||
|
row.disposalStatus === 'completed' ? 'detail' : 'follow'
|
||||||
|
)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ row.disposalStatusName || '-' }}
|
||||||
|
</el-link>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" width="220" fixed="right" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<div class="waybill-manage-page__exception-actions">
|
||||||
|
<template v-if="row.disposalStatus !== 'completed'">
|
||||||
|
<el-link
|
||||||
|
v-if="permission.exception_disposal_follow"
|
||||||
|
type="primary"
|
||||||
|
@click="openWaybillException(row, 'follow')"
|
||||||
|
>
|
||||||
|
跟进
|
||||||
|
</el-link>
|
||||||
|
<el-link
|
||||||
|
v-if="permission.exception_disposal_edit"
|
||||||
|
type="primary"
|
||||||
|
@click="openWaybillException(row, 'edit')"
|
||||||
|
>
|
||||||
|
编辑
|
||||||
|
</el-link>
|
||||||
|
<el-link
|
||||||
|
v-if="permission.exception_disposal_complete"
|
||||||
|
type="primary"
|
||||||
|
@click="completeWaybillExceptionRow(row)"
|
||||||
|
>
|
||||||
|
完成
|
||||||
|
</el-link>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<el-link
|
||||||
|
v-if="permission.exception_disposal_view"
|
||||||
|
type="primary"
|
||||||
|
@click="openWaybillException(row, 'detail')"
|
||||||
|
>
|
||||||
|
详情
|
||||||
|
</el-link>
|
||||||
|
<el-link
|
||||||
|
v-if="permission.exception_disposal_edit"
|
||||||
|
type="primary"
|
||||||
|
@click="openWaybillException(row, 'edit')"
|
||||||
|
>
|
||||||
|
编辑
|
||||||
|
</el-link>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</section-card>
|
||||||
<div class="waybill-manage-page__waybill-detail-bottom">
|
<div class="waybill-manage-page__waybill-detail-bottom">
|
||||||
<section-card title="执行详情">
|
<section-card title="执行详情">
|
||||||
<el-tabs v-model="waybillProcessDetailTab" type="border-card">
|
<el-tabs v-model="waybillProcessDetailTab" type="border-card">
|
||||||
@@ -2356,6 +2450,284 @@
|
|||||||
</template>
|
</template>
|
||||||
</component>
|
</component>
|
||||||
|
|
||||||
|
<el-dialog
|
||||||
|
v-model="waybillExceptionDialog.visible"
|
||||||
|
:title="waybillExceptionDialogTitle"
|
||||||
|
:width="waybillExceptionDialog.mode === 'edit' ? '1080px' : '960px'"
|
||||||
|
top="6vh"
|
||||||
|
append-to-body
|
||||||
|
destroy-on-close
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-loading="waybillExceptionDialog.loading"
|
||||||
|
class="waybill-manage-page__exception-dialog"
|
||||||
|
>
|
||||||
|
<el-form
|
||||||
|
v-if="waybillExceptionDialog.mode === 'edit'"
|
||||||
|
ref="waybillExceptionEditFormRef"
|
||||||
|
:model="waybillExceptionEditForm"
|
||||||
|
:rules="waybillExceptionEditRules"
|
||||||
|
label-position="right"
|
||||||
|
label-width="96px"
|
||||||
|
>
|
||||||
|
<section-card title="运单信息">
|
||||||
|
<el-descriptions :column="3" border size="small">
|
||||||
|
<el-descriptions-item
|
||||||
|
v-for="field in waybillExceptionWaybillFields"
|
||||||
|
:key="field.prop"
|
||||||
|
:label="field.label"
|
||||||
|
>
|
||||||
|
{{ waybillExceptionValue(field.prop) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
</section-card>
|
||||||
|
|
||||||
|
<section-card title="异常信息">
|
||||||
|
<el-row :gutter="18">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="上报人">
|
||||||
|
<span class="waybill-manage-page__exception-readonly">{{
|
||||||
|
waybillExceptionValue('reporterName')
|
||||||
|
}}</span>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="上报时间">
|
||||||
|
<span class="waybill-manage-page__exception-readonly">{{
|
||||||
|
waybillExceptionValue('reportTime')
|
||||||
|
}}</span>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="异常类型" prop="exceptionType">
|
||||||
|
<el-select
|
||||||
|
v-model="waybillExceptionEditForm.exceptionType"
|
||||||
|
placeholder="请选择"
|
||||||
|
@change="handleWaybillExceptionTypeChange"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in waybillExceptionTypeOptions"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="异常原因" prop="exceptionReason">
|
||||||
|
<el-select
|
||||||
|
v-model="waybillExceptionEditForm.exceptionReason"
|
||||||
|
clearable
|
||||||
|
placeholder="请选择"
|
||||||
|
:disabled="!waybillExceptionEditForm.exceptionType"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in waybillExceptionReasonOptions"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="24">
|
||||||
|
<el-form-item label="上报说明" prop="reportDescription">
|
||||||
|
<el-input
|
||||||
|
v-model="waybillExceptionEditForm.reportDescription"
|
||||||
|
type="textarea"
|
||||||
|
maxlength="500"
|
||||||
|
show-word-limit
|
||||||
|
:rows="3"
|
||||||
|
placeholder="请输入"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="24">
|
||||||
|
<el-form-item label="现场照片">
|
||||||
|
<div class="waybill-manage-page__exception-edit-photos">
|
||||||
|
<div
|
||||||
|
v-for="(url, index) in waybillExceptionEditForm.scenePhotoList"
|
||||||
|
:key="url"
|
||||||
|
class="waybill-manage-page__exception-edit-photo"
|
||||||
|
>
|
||||||
|
<el-image
|
||||||
|
:src="url"
|
||||||
|
:preview-src-list="waybillExceptionEditForm.scenePhotoList"
|
||||||
|
:initial-index="index"
|
||||||
|
fit="cover"
|
||||||
|
/>
|
||||||
|
<el-link
|
||||||
|
type="danger"
|
||||||
|
:underline="false"
|
||||||
|
@click="removeWaybillExceptionPhoto(index)"
|
||||||
|
>删除</el-link
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<el-upload
|
||||||
|
action="/api/blade-resource/oss/endpoint/put-file"
|
||||||
|
name="file"
|
||||||
|
:headers="waybillExceptionUploadHeaders"
|
||||||
|
:show-file-list="false"
|
||||||
|
:before-upload="beforeWaybillExceptionPhotoUpload"
|
||||||
|
:on-success="handleWaybillExceptionPhotoSuccess"
|
||||||
|
:on-error="handleWaybillExceptionPhotoError"
|
||||||
|
>
|
||||||
|
<div class="waybill-manage-page__exception-edit-photo-add">+ 添加</div>
|
||||||
|
</el-upload>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</section-card>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<template v-else>
|
||||||
|
<section-card title="运单信息">
|
||||||
|
<el-descriptions :column="3" border size="small">
|
||||||
|
<el-descriptions-item
|
||||||
|
v-for="field in waybillExceptionWaybillFields"
|
||||||
|
:key="field.prop"
|
||||||
|
:label="field.label"
|
||||||
|
>
|
||||||
|
{{ waybillExceptionValue(field.prop) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
</section-card>
|
||||||
|
|
||||||
|
<section-card title="异常信息">
|
||||||
|
<el-descriptions :column="2" border size="small">
|
||||||
|
<el-descriptions-item
|
||||||
|
v-for="field in waybillExceptionDetailFields"
|
||||||
|
:key="field.prop"
|
||||||
|
:label="field.label"
|
||||||
|
:span="field.span === 2 ? 2 : 1"
|
||||||
|
>
|
||||||
|
{{ waybillExceptionValue(field.prop) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
</section-card>
|
||||||
|
|
||||||
|
<section-card title="现场照片">
|
||||||
|
<div class="waybill-manage-page__exception-photos">
|
||||||
|
<el-image
|
||||||
|
v-for="(url, index) in waybillExceptionPhotoUrls"
|
||||||
|
:key="url"
|
||||||
|
:src="url"
|
||||||
|
:preview-src-list="waybillExceptionPhotoUrls"
|
||||||
|
:initial-index="index"
|
||||||
|
fit="cover"
|
||||||
|
/>
|
||||||
|
<el-empty
|
||||||
|
v-if="!waybillExceptionPhotoUrls.length"
|
||||||
|
description="暂无现场照片"
|
||||||
|
:image-size="56"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</section-card>
|
||||||
|
|
||||||
|
<section-card v-if="waybillExceptionDialog.mode === 'follow'" title="跟进">
|
||||||
|
<el-form
|
||||||
|
ref="waybillExceptionFollowFormRef"
|
||||||
|
:model="waybillExceptionFollowForm"
|
||||||
|
:rules="waybillExceptionFollowRules"
|
||||||
|
label-position="right"
|
||||||
|
label-width="96px"
|
||||||
|
>
|
||||||
|
<el-row :gutter="18">
|
||||||
|
<el-col :span="24">
|
||||||
|
<el-form-item label="跟进说明" prop="followContent">
|
||||||
|
<el-input
|
||||||
|
v-model="waybillExceptionFollowForm.followContent"
|
||||||
|
type="textarea"
|
||||||
|
maxlength="500"
|
||||||
|
show-word-limit
|
||||||
|
:rows="3"
|
||||||
|
placeholder="请输入"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="24">
|
||||||
|
<el-form-item label="跟进附件">
|
||||||
|
<vehicle-attachment-upload
|
||||||
|
v-model="waybillExceptionFollowForm.attachments"
|
||||||
|
:limit="20"
|
||||||
|
:max-size="500"
|
||||||
|
button-text="上传附件"
|
||||||
|
:show-tip="false"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-form>
|
||||||
|
</section-card>
|
||||||
|
|
||||||
|
<section-card title="历史跟进">
|
||||||
|
<el-table :data="waybillExceptionDetail.followRecords || []" border>
|
||||||
|
<el-table-column type="index" label="序号" width="70" align="center" />
|
||||||
|
<el-table-column
|
||||||
|
v-for="column in waybillExceptionFollowColumns"
|
||||||
|
:key="column.prop"
|
||||||
|
:prop="column.prop"
|
||||||
|
:label="column.label"
|
||||||
|
:min-width="column.minWidth"
|
||||||
|
align="center"
|
||||||
|
show-overflow-tooltip
|
||||||
|
/>
|
||||||
|
<el-table-column label="附件" min-width="120" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<template v-if="parseWaybillExceptionAttachments(row.attachmentsJson).length">
|
||||||
|
<el-link
|
||||||
|
v-for="(file, index) in parseWaybillExceptionAttachments(
|
||||||
|
row.attachmentsJson
|
||||||
|
)"
|
||||||
|
:key="index"
|
||||||
|
type="primary"
|
||||||
|
class="waybill-manage-page__exception-attachment-link"
|
||||||
|
:href="file.url"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
{{ file.name || '附件' + (index + 1) }}
|
||||||
|
</el-link>
|
||||||
|
</template>
|
||||||
|
<span v-else>-</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</section-card>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
<template #footer>
|
||||||
|
<el-button
|
||||||
|
v-if="waybillExceptionDialog.mode === 'edit' && permission.exception_disposal_edit"
|
||||||
|
type="primary"
|
||||||
|
:loading="waybillExceptionDialog.submitting"
|
||||||
|
@click="submitWaybillExceptionEdit"
|
||||||
|
>
|
||||||
|
提交
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
v-if="waybillExceptionDialog.mode === 'follow' && permission.exception_disposal_follow"
|
||||||
|
type="primary"
|
||||||
|
:loading="waybillExceptionDialog.submitting"
|
||||||
|
@click="submitWaybillExceptionFollow"
|
||||||
|
>
|
||||||
|
提交
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
v-if="
|
||||||
|
waybillExceptionDialog.mode === 'follow' && permission.exception_disposal_complete
|
||||||
|
"
|
||||||
|
type="primary"
|
||||||
|
plain
|
||||||
|
@click="completeWaybillExceptionFromDialog"
|
||||||
|
>
|
||||||
|
完成
|
||||||
|
</el-button>
|
||||||
|
<el-button @click="waybillExceptionDialog.visible = false">取消</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
<el-dialog
|
<el-dialog
|
||||||
v-model="attachmentDocumentPreviewVisible"
|
v-model="attachmentDocumentPreviewVisible"
|
||||||
:title="attachmentPreviewFile.name || '附件预览'"
|
:title="attachmentPreviewFile.name || '附件预览'"
|
||||||
@@ -3120,6 +3492,12 @@ import { h, resolveComponent } from 'vue';
|
|||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import NProgress from 'nprogress';
|
import NProgress from 'nprogress';
|
||||||
import 'nprogress/nprogress.css';
|
import 'nprogress/nprogress.css';
|
||||||
|
import {
|
||||||
|
disposalStatusOptions,
|
||||||
|
exceptionCategoryOptions,
|
||||||
|
} from '@/option/transit/exception-disposal';
|
||||||
|
import { getUploadHeaders } from '@/utils/upload';
|
||||||
|
import * as exceptionDisposalApi from '@/api/transit/exception-disposal';
|
||||||
|
|
||||||
const AMAP_KEY = '653b7cf105ad7fb8ec9b2f5198ade315';
|
const AMAP_KEY = '653b7cf105ad7fb8ec9b2f5198ade315';
|
||||||
const AMAP_SECURITY_CODE = '5aab65c632e48ebae0d0e28f5b28e21a';
|
const AMAP_SECURITY_CODE = '5aab65c632e48ebae0d0e28f5b28e21a';
|
||||||
@@ -3349,6 +3727,32 @@ export default {
|
|||||||
waybillVoucherFolderView: false,
|
waybillVoucherFolderView: false,
|
||||||
waybillVoucherImagesLoading: false,
|
waybillVoucherImagesLoading: false,
|
||||||
waybillVoucherImagesLoaded: false,
|
waybillVoucherImagesLoaded: false,
|
||||||
|
waybillExceptionRows: [],
|
||||||
|
waybillExceptionLoading: false,
|
||||||
|
waybillExceptionDialog: {
|
||||||
|
visible: false,
|
||||||
|
mode: 'follow',
|
||||||
|
loading: false,
|
||||||
|
submitting: false,
|
||||||
|
},
|
||||||
|
waybillExceptionDetail: {},
|
||||||
|
waybillExceptionFollowForm: { followContent: '', attachments: [] },
|
||||||
|
waybillExceptionFollowRules: {
|
||||||
|
followContent: [{ max: 500, message: '跟进说明不能超过500个字符', trigger: 'blur' }],
|
||||||
|
},
|
||||||
|
waybillExceptionEditForm: {
|
||||||
|
exceptionType: '',
|
||||||
|
exceptionReason: '',
|
||||||
|
reportDescription: '',
|
||||||
|
scenePhotoList: [],
|
||||||
|
},
|
||||||
|
waybillExceptionEditRules: {
|
||||||
|
exceptionType: [{ required: true, message: '请选择异常类型', trigger: 'change' }],
|
||||||
|
reportDescription: [
|
||||||
|
{ required: true, message: '请输入上报说明', trigger: 'blur' },
|
||||||
|
{ max: 500, message: '上报说明不能超过500个字符', trigger: 'blur' },
|
||||||
|
],
|
||||||
|
},
|
||||||
waybillRouteChangeBox: false,
|
waybillRouteChangeBox: false,
|
||||||
waybillRouteChangeTab: 'change',
|
waybillRouteChangeTab: 'change',
|
||||||
waybillRouteChangeRows: [],
|
waybillRouteChangeRows: [],
|
||||||
@@ -3648,6 +4052,54 @@ export default {
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters(['permission', 'userInfo']),
|
...mapGetters(['permission', 'userInfo']),
|
||||||
|
waybillExceptionDialogTitle() {
|
||||||
|
return (
|
||||||
|
{ detail: '异常详情', follow: '异常跟进', edit: '编辑异常' }[
|
||||||
|
this.waybillExceptionDialog.mode
|
||||||
|
] || '异常详情'
|
||||||
|
);
|
||||||
|
},
|
||||||
|
waybillExceptionWaybillFields() {
|
||||||
|
return [
|
||||||
|
{ label: '运单号', prop: 'waybillNo' },
|
||||||
|
{ label: '配载单号/总单号', prop: 'loadingOrMasterNo' },
|
||||||
|
{ label: '车牌号', prop: 'vehicleNo' },
|
||||||
|
{ label: '项目', prop: 'projectName' },
|
||||||
|
{ label: '承运商', prop: 'carrierName' },
|
||||||
|
];
|
||||||
|
},
|
||||||
|
waybillExceptionDetailFields() {
|
||||||
|
return [
|
||||||
|
{ label: '上报人', prop: 'reporterName' },
|
||||||
|
{ label: '上报时间', prop: 'reportTime' },
|
||||||
|
{ label: '异常类型', prop: 'exceptionTypeName' },
|
||||||
|
{ label: '异常原因', prop: 'exceptionReason' },
|
||||||
|
{ label: '上报说明', prop: 'reportDescription', span: 2 },
|
||||||
|
];
|
||||||
|
},
|
||||||
|
waybillExceptionFollowColumns() {
|
||||||
|
return [
|
||||||
|
{ label: '跟进说明', prop: 'followContent', minWidth: 260 },
|
||||||
|
{ label: '跟进人', prop: 'followUserName', minWidth: 130 },
|
||||||
|
{ label: '跟进时间', prop: 'followTime', minWidth: 170 },
|
||||||
|
];
|
||||||
|
},
|
||||||
|
waybillExceptionTypeOptions() {
|
||||||
|
return exceptionCategoryOptions.map(item => ({ label: item.label, value: item.value }));
|
||||||
|
},
|
||||||
|
waybillExceptionReasonOptions() {
|
||||||
|
return (
|
||||||
|
exceptionCategoryOptions.find(
|
||||||
|
item => item.value === this.waybillExceptionEditForm.exceptionType
|
||||||
|
)?.children || []
|
||||||
|
);
|
||||||
|
},
|
||||||
|
waybillExceptionPhotoUrls() {
|
||||||
|
return this.parseWaybillExceptionPhotos(this.waybillExceptionDetail.scenePhotos);
|
||||||
|
},
|
||||||
|
waybillExceptionUploadHeaders() {
|
||||||
|
return getUploadHeaders();
|
||||||
|
},
|
||||||
permissionList() {
|
permissionList() {
|
||||||
return {
|
return {
|
||||||
addBtn: this.canCreate,
|
addBtn: this.canCreate,
|
||||||
@@ -4629,6 +5081,7 @@ export default {
|
|||||||
this.waybillVoucherFolder = null;
|
this.waybillVoucherFolder = null;
|
||||||
this.waybillVoucherFolderView = false;
|
this.waybillVoucherFolderView = false;
|
||||||
this.waybillVoucherImagesLoaded = false;
|
this.waybillVoucherImagesLoaded = false;
|
||||||
|
this.waybillExceptionRows = [];
|
||||||
const request =
|
const request =
|
||||||
typeof this.api.getDetail === 'function'
|
typeof this.api.getDetail === 'function'
|
||||||
? this.api.getDetail(row.id)
|
? this.api.getDetail(row.id)
|
||||||
@@ -4639,6 +5092,7 @@ export default {
|
|||||||
this.detailRow = detail;
|
this.detailRow = detail;
|
||||||
this.loadWaybillPunchRecords();
|
this.loadWaybillPunchRecords();
|
||||||
this.loadWaybillVoucherImages();
|
this.loadWaybillVoucherImages();
|
||||||
|
this.loadWaybillExceptions(detail);
|
||||||
if (detail.contractId) {
|
if (detail.contractId) {
|
||||||
return getContractDetail(detail.contractId)
|
return getContractDetail(detail.contractId)
|
||||||
.then(contractRes => {
|
.then(contractRes => {
|
||||||
@@ -4668,6 +5122,206 @@ export default {
|
|||||||
this.detailLoading = false;
|
this.detailLoading = false;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
// 异常处置列表接口不支持按 waybillId 精确过滤,只有 waybillNo 模糊匹配,
|
||||||
|
// 而运单号存在前缀包含(YD001 会命中 YD0011),故取回后必须再按 waybillId 精确筛一次。
|
||||||
|
// 多取一页降低「同前缀运单占满首页」导致漏数据的概率。
|
||||||
|
// 注意 String() 比较只是避免类型不一致,挡不住 int64 精度:运单 id 超出 JS 安全整数范围,
|
||||||
|
// 两个相差很小的 id 在响应 JSON 解析时就已被解析成同一个浮点值,此处无从分辨。
|
||||||
|
async loadWaybillExceptions(detail) {
|
||||||
|
const waybillId = detail?.id;
|
||||||
|
const waybillNo = detail?.waybillNo;
|
||||||
|
if (!waybillId || !waybillNo) {
|
||||||
|
this.waybillExceptionRows = [];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.waybillExceptionLoading = true;
|
||||||
|
try {
|
||||||
|
const res = await exceptionDisposalApi.getList(1, 100, { waybillNo });
|
||||||
|
const data = res?.data?.data || res?.data || res || {};
|
||||||
|
this.waybillExceptionRows = (data.records || [])
|
||||||
|
.filter(item => String(item.waybillId) === String(waybillId))
|
||||||
|
.map(this.decorateWaybillExceptionRow);
|
||||||
|
} catch (error) {
|
||||||
|
this.waybillExceptionRows = [];
|
||||||
|
} finally {
|
||||||
|
this.waybillExceptionLoading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
decorateWaybillExceptionRow(row) {
|
||||||
|
return {
|
||||||
|
...row,
|
||||||
|
exceptionTypeName: this.waybillExceptionName(row.exceptionType),
|
||||||
|
disposalStatusName:
|
||||||
|
disposalStatusOptions.find(item => item.value === row.disposalStatus)?.label ||
|
||||||
|
row.disposalStatus ||
|
||||||
|
'-',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
waybillExceptionName(value) {
|
||||||
|
if (!value) return '-';
|
||||||
|
// 一级存中文大类名(如「交通异常」),二级存子类名(如「货损」),两级都查
|
||||||
|
const group = exceptionCategoryOptions.find(item => item.value === value);
|
||||||
|
if (group) return group.label;
|
||||||
|
for (const item of exceptionCategoryOptions) {
|
||||||
|
const match = item.children.find(child => child.value === value);
|
||||||
|
if (match) return match.label;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
async openWaybillException(row, mode) {
|
||||||
|
this.waybillExceptionDialog = {
|
||||||
|
visible: true,
|
||||||
|
mode,
|
||||||
|
loading: true,
|
||||||
|
submitting: false,
|
||||||
|
};
|
||||||
|
this.waybillExceptionFollowForm = { followContent: '', attachments: [] };
|
||||||
|
try {
|
||||||
|
const res = await exceptionDisposalApi.getDetail(row.id);
|
||||||
|
const detail = res?.data?.data || res?.data || res || {};
|
||||||
|
this.waybillExceptionDetail = this.decorateWaybillExceptionRow(detail);
|
||||||
|
if (mode === 'edit') {
|
||||||
|
this.waybillExceptionEditForm = {
|
||||||
|
exceptionType: this.waybillExceptionDetail.exceptionType || '',
|
||||||
|
exceptionReason: this.waybillExceptionDetail.exceptionReason || '',
|
||||||
|
reportDescription: this.waybillExceptionDetail.reportDescription || '',
|
||||||
|
scenePhotoList: this.parseWaybillExceptionPhotos(
|
||||||
|
this.waybillExceptionDetail.scenePhotos
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
this.waybillExceptionDialog.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
waybillExceptionValue(prop) {
|
||||||
|
const value = this.waybillExceptionDetail[prop];
|
||||||
|
return value === null || value === undefined || value === '' ? '-' : value;
|
||||||
|
},
|
||||||
|
handleWaybillExceptionTypeChange() {
|
||||||
|
// 一级类型变了,旧二级可能不属于新类型
|
||||||
|
const stillValid = this.waybillExceptionReasonOptions.some(
|
||||||
|
item => item.value === this.waybillExceptionEditForm.exceptionReason
|
||||||
|
);
|
||||||
|
if (!stillValid) {
|
||||||
|
this.waybillExceptionEditForm.exceptionReason = '';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
beforeWaybillExceptionPhotoUpload(file) {
|
||||||
|
const validType = ['image/jpeg', 'image/png', 'image/jpg', 'image/bmp'].includes(file.type);
|
||||||
|
if (!validType) {
|
||||||
|
this.$message.error('仅支持 JPG、PNG、BMP 图片');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (file.size / 1024 / 1024 >= 5) {
|
||||||
|
this.$message.error('图片大小不能超过 5MB');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
handleWaybillExceptionPhotoSuccess(res) {
|
||||||
|
if (res.code === 200 && res.data) {
|
||||||
|
const url = res.data.link || res.data.url || res.data.domain || '';
|
||||||
|
if (url) {
|
||||||
|
this.waybillExceptionEditForm.scenePhotoList = [
|
||||||
|
...this.waybillExceptionEditForm.scenePhotoList,
|
||||||
|
url,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.$message.error(res.msg || '上传失败');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleWaybillExceptionPhotoError() {
|
||||||
|
this.$message.error('上传失败');
|
||||||
|
},
|
||||||
|
removeWaybillExceptionPhoto(index) {
|
||||||
|
const list = [...this.waybillExceptionEditForm.scenePhotoList];
|
||||||
|
list.splice(index, 1);
|
||||||
|
this.waybillExceptionEditForm.scenePhotoList = list;
|
||||||
|
},
|
||||||
|
parseWaybillExceptionPhotos(value) {
|
||||||
|
if (!value) return [];
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(value);
|
||||||
|
if (Array.isArray(parsed)) return parsed.filter(Boolean);
|
||||||
|
} catch (error) {
|
||||||
|
return String(value)
|
||||||
|
.split(',')
|
||||||
|
.map(item => item.trim())
|
||||||
|
.filter(Boolean);
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
},
|
||||||
|
parseWaybillExceptionAttachments(value) {
|
||||||
|
if (!value) return [];
|
||||||
|
if (Array.isArray(value)) return value;
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(value);
|
||||||
|
return Array.isArray(parsed) ? parsed.filter(item => item && item.url) : [];
|
||||||
|
} catch (error) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async submitWaybillExceptionEdit() {
|
||||||
|
await this.$refs.waybillExceptionEditFormRef.validate();
|
||||||
|
this.waybillExceptionDialog.submitting = true;
|
||||||
|
try {
|
||||||
|
await exceptionDisposalApi.update({
|
||||||
|
id: this.waybillExceptionDetail.id,
|
||||||
|
exceptionType: this.waybillExceptionEditForm.exceptionType,
|
||||||
|
exceptionReason: this.waybillExceptionEditForm.exceptionReason,
|
||||||
|
reportDescription: this.waybillExceptionEditForm.reportDescription,
|
||||||
|
scenePhotos: this.waybillExceptionEditForm.scenePhotoList.join(','),
|
||||||
|
});
|
||||||
|
this.$message.success('编辑成功');
|
||||||
|
this.waybillExceptionDialog.visible = false;
|
||||||
|
this.refreshWaybillExceptions();
|
||||||
|
} finally {
|
||||||
|
this.waybillExceptionDialog.submitting = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async submitWaybillExceptionFollow() {
|
||||||
|
await this.$refs.waybillExceptionFollowFormRef.validate();
|
||||||
|
const followContent = (this.waybillExceptionFollowForm.followContent || '').trim();
|
||||||
|
const attachmentsJson = this.waybillExceptionFollowForm.attachments.length
|
||||||
|
? JSON.stringify(this.waybillExceptionFollowForm.attachments)
|
||||||
|
: '';
|
||||||
|
// 后端要求二者至少有其一
|
||||||
|
if (!followContent && !attachmentsJson) {
|
||||||
|
this.$message.warning('请填写跟进说明或上传附件');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.waybillExceptionDialog.submitting = true;
|
||||||
|
try {
|
||||||
|
await exceptionDisposalApi.follow({
|
||||||
|
id: this.waybillExceptionDetail.id,
|
||||||
|
followContent,
|
||||||
|
attachmentsJson,
|
||||||
|
});
|
||||||
|
this.$message.success('跟进成功');
|
||||||
|
this.waybillExceptionDialog.visible = false;
|
||||||
|
this.refreshWaybillExceptions();
|
||||||
|
} finally {
|
||||||
|
this.waybillExceptionDialog.submitting = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async completeWaybillExceptionRow(row) {
|
||||||
|
await this.$confirm('确认完成该异常处置?', '提示', { type: 'warning' });
|
||||||
|
await exceptionDisposalApi.complete({ id: row.id });
|
||||||
|
this.$message.success('完成成功');
|
||||||
|
this.refreshWaybillExceptions();
|
||||||
|
},
|
||||||
|
async completeWaybillExceptionFromDialog() {
|
||||||
|
await this.$confirm('确认完成该异常处置?', '提示', { type: 'warning' });
|
||||||
|
await exceptionDisposalApi.complete({ id: this.waybillExceptionDetail.id });
|
||||||
|
this.$message.success('完成成功');
|
||||||
|
this.waybillExceptionDialog.visible = false;
|
||||||
|
this.refreshWaybillExceptions();
|
||||||
|
},
|
||||||
|
refreshWaybillExceptions() {
|
||||||
|
this.loadWaybillExceptions(this.detailRow);
|
||||||
|
},
|
||||||
closeDetail() {
|
closeDetail() {
|
||||||
this.destroyWaybillLocateMap();
|
this.destroyWaybillLocateMap();
|
||||||
this.destroyWaybillTrackMap();
|
this.destroyWaybillTrackMap();
|
||||||
@@ -10046,6 +10700,81 @@ export default {
|
|||||||
padding-top: 12px;
|
padding-top: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__exception-actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__exception-dialog {
|
||||||
|
padding: 4px 0 0;
|
||||||
|
|
||||||
|
:deep(.section-card + .section-card) {
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__exception-readonly {
|
||||||
|
color: #606266;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__exception-attachment-link {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__exception-photos {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 12px;
|
||||||
|
min-height: 80px;
|
||||||
|
|
||||||
|
:deep(.el-image) {
|
||||||
|
width: 96px;
|
||||||
|
height: 96px;
|
||||||
|
border: 1px solid #eff1f7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__exception-edit-photos {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__exception-edit-photo {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
|
||||||
|
:deep(.el-image) {
|
||||||
|
width: 96px;
|
||||||
|
height: 96px;
|
||||||
|
border: 1px solid #eff1f7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__exception-edit-photo-add {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 96px;
|
||||||
|
height: 96px;
|
||||||
|
border: 1px dashed #dcdfe6;
|
||||||
|
border-radius: 4px;
|
||||||
|
color: #909399;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: #409eff;
|
||||||
|
color: #409eff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&__waybill-detail-summary {
|
&__waybill-detail-summary {
|
||||||
:deep(.el-card__body) {
|
:deep(.el-card__body) {
|
||||||
padding: 18px 22px;
|
padding: 18px 22px;
|
||||||
|
|||||||
Reference in New Issue
Block a user