diff --git a/src/api/transit/exception-disposal.js b/src/api/transit/exception-disposal.js index 4929b8b..adab6ce 100644 --- a/src/api/transit/exception-disposal.js +++ b/src/api/transit/exception-disposal.js @@ -9,6 +9,8 @@ export const getDetail = id => request({ url: `${baseUrl}/detail`, method: 'get' export const follow = data => request({ url: `${baseUrl}/follow`, method: 'post', data }); +export const update = data => request({ url: `${baseUrl}/update`, method: 'post', data }); + export const complete = data => request({ url: `${baseUrl}/complete`, method: 'post', data }); export const batchComplete = ids => diff --git a/src/option/transit/exception-disposal.js b/src/option/transit/exception-disposal.js index 6f0ba58..247e9c2 100644 --- a/src/option/transit/exception-disposal.js +++ b/src/option/transit/exception-disposal.js @@ -1,7 +1,10 @@ +// 一级 value 与二级 value 都用中文原文:库里 exception_type 存的是大类中文名、 +// exception_reason 存子类中文名(小程序上报即如此),此处必须与之一致, +// 否则按异常类型做精确匹配的查询会一行都查不到。 export const exceptionCategoryOptions = [ { label: '交通异常', - value: 'traffic', + value: '交通异常', children: [ { label: '道路拥堵', value: '道路拥堵' }, { label: '交通事故', value: '交通事故' }, @@ -12,7 +15,7 @@ export const exceptionCategoryOptions = [ }, { label: '车辆异常', - value: 'vehicle', + value: '车辆异常', children: [ { label: '车辆故障维修', value: '车辆故障维修' }, { label: '车辆事故', value: '车辆事故' }, @@ -24,7 +27,7 @@ export const exceptionCategoryOptions = [ }, { label: '货物异常', - value: 'cargo', + value: '货物异常', children: [ { label: '货损', value: '货损' }, { label: '货差', value: '货差' }, @@ -35,7 +38,7 @@ export const exceptionCategoryOptions = [ }, { label: '其他异常', - value: 'other', + value: '其他异常', children: [ { label: '变更路线地址', value: '变更路线地址' }, { label: '其他', value: '其他异常-其他' }, @@ -43,6 +46,16 @@ export const exceptionCategoryOptions = [ }, ]; +/** 一级类型的中文名列表,供编辑表单的级联选择使用 */ +export const exceptionTypeOptions = exceptionCategoryOptions.map(item => ({ + label: item.label, + value: item.value, +})); + +/** 按一级类型取二级选项 */ +export const exceptionReasonOptions = typeValue => + exceptionCategoryOptions.find(item => item.value === typeValue)?.children || []; + export const disposalStatusOptions = [ { label: '待处理', value: 'pending' }, { label: '处理中', value: 'processing' }, @@ -89,3 +102,21 @@ export const followColumns = [ { label: '跟进人', prop: 'followUserName', minWidth: 130 }, { label: '跟进时间', prop: 'followTime', minWidth: 170 }, ]; + +// 编辑表单只开放上报四件套。车牌、项目、承运商、配载单号由上报时从运单回填, +// 允许手改会让记录与运单脱钩;处置状态由跟进与完成流转,不在此处。 +export const editFormFields = [ + { label: '异常类型', prop: 'exceptionType', type: 'select', required: true }, + { label: '异常原因', prop: 'exceptionReason', type: 'select' }, + { label: '上报说明', prop: 'reportDescription', type: 'textarea', required: true, span: 2 }, +]; + +// 编辑弹窗顶部的运单信息,只读展示,帮助运管员确认改的是哪一单 +export const editWaybillFields = [ + { label: '运单号', prop: 'waybillNo' }, + { label: '配载单号/总单号', prop: 'loadingOrMasterNo' }, + { label: '车牌号', prop: 'vehicleNo' }, + { label: '项目', prop: 'projectName' }, + { label: '承运商', prop: 'carrierName' }, + { label: '上报人', prop: 'reporterName' }, +]; diff --git a/src/views/transit/exception-disposal.vue b/src/views/transit/exception-disposal.vue index 6af166a..483ce97 100644 --- a/src/views/transit/exception-disposal.vue +++ b/src/views/transit/exception-disposal.vue @@ -93,7 +93,11 @@ show-overflow-tooltip > - + {{ row[column.prop] }} {{ row[column.prop] || '-' }} - + @@ -117,6 +121,13 @@ > 跟进 + + 编辑 + - - 详情 - + + + 详情 + + + 编辑 + + @@ -151,76 +171,191 @@ - 异常信息 - - - - {{ field.label }} - - - {{ detail[field.prop] }} - - {{ detail[field.prop] || '-' }} - - - - - - 现场照片 - - - - - + - - - + + + + {{ detailValue(field.prop) }} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 删除 + + + + 添加 + + + + + + - 历史跟进 - - - - + + 异常信息 + + + + {{ field.label }} + + + {{ detail[field.prop] }} + + {{ detail[field.prop] || '-' }} + + + + + + 现场照片 + + + + + + + + + + + + 历史跟进 + + + + + + + 提交 + item.value === this.editForm.exceptionReason + ); + if (!stillValid) { + this.editForm.exceptionReason = ''; + } + }, + beforePhotoUpload(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; + }, + handlePhotoSuccess(res) { + if (res.code === 200 && res.data) { + const url = res.data.link || res.data.url || res.data.domain || ''; + if (url) { + this.editForm.scenePhotoList = [...this.editForm.scenePhotoList, url]; + } + } else { + this.$message.error(res.msg || '上传失败'); + } + }, + handlePhotoError() { + this.$message.error('上传失败'); + }, + removeEditPhoto(index) { + const list = [...this.editForm.scenePhotoList]; + list.splice(index, 1); + this.editForm.scenePhotoList = list; + }, + async submitEdit() { + await this.$refs.editFormRef.validate(); + this.dialog.submitting = true; + try { + await api.update({ + id: this.detail.id, + exceptionType: this.editForm.exceptionType, + exceptionReason: this.editForm.exceptionReason, + reportDescription: this.editForm.reportDescription, + scenePhotos: this.editForm.scenePhotoList.join(','), + }); + this.$message.success('编辑成功'); + this.dialog.visible = false; + this.loadTable(); + } finally { + this.dialog.submitting = false; + } + }, async submitFollow() { await this.$refs.followFormRef.validate(); this.dialog.submitting = true; @@ -400,11 +634,15 @@ export default { }; }, exceptionName(value) { - for (const group of exceptionCategoryOptions) { - const match = group.children.find(item => item.value === 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 || '-'; + return value; }, statusLinkType(status) { return status === 'completed' ? 'info' : 'primary'; @@ -565,6 +803,47 @@ export default { margin-top: 16px; } +.exception-disposal-page__dialog :deep(.section-card + .section-card) { + margin-top: 16px; +} + +.exception-disposal-page__edit-photos { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 12px; +} + +.exception-disposal-page__edit-photo { + display: flex; + flex-direction: column; + align-items: center; + gap: 4px; + + :deep(.el-image) { + width: 96px; + height: 96px; + border: 1px solid #eff1f7; + } +} + +.exception-disposal-page__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; + } +} + @media screen and (max-width: 1200px) { .exception-disposal-page__search-grid { grid-template-columns: repeat(2, minmax(220px, 1fr));