From 8ffaad6d2d69a81ace44177580bfca2610be858f Mon Sep 17 00:00:00 2001
From: ai-coding <ai-coding@devgenie.local>
Date: 星期四, 20 八月 2026 20:25:52 +0800
Subject: [PATCH] Merge origin/feat-0819-opt into feat/deploy-uat
---
src/views/assetManagement/vehicleManagement/operatingVehicle/components/PreparationDetailModal.vue | 61 +++++++++++++++
src/views/assetManagement/vehicleManagement/operatingVehicle/detail.vue | 40 ++++++++++
src/views/userContent/contentCenter/opsApp.vue | 46 +++-------
src/views/assetManagement/vehicleManagement/operatingVehicle/components/DetectionDetailModal.vue | 70 +++++++++++++++++
4 files changed, 185 insertions(+), 32 deletions(-)
diff --git a/src/views/assetManagement/vehicleManagement/operatingVehicle/components/DetectionDetailModal.vue b/src/views/assetManagement/vehicleManagement/operatingVehicle/components/DetectionDetailModal.vue
index 4b79c1c..f950075 100644
--- a/src/views/assetManagement/vehicleManagement/operatingVehicle/components/DetectionDetailModal.vue
+++ b/src/views/assetManagement/vehicleManagement/operatingVehicle/components/DetectionDetailModal.vue
@@ -1,5 +1,5 @@
<script setup lang="ts">
-import { NImage, NModal } from 'naive-ui'
+import { NImage, NModal, NTag } from 'naive-ui'
import { computed } from 'vue'
import dayjs from 'dayjs'
@@ -29,6 +29,24 @@
function handleClose() {
emit('update:show', false)
}
+
+/**
+ * 车辆运维字段优化:AEB调至"中" 胶囊状态(只读回显,不可编辑)
+ *
+ * 状态枚举:0=历史无数据(存量/老版本未上报,DB DEFAULT)、1=检测完成、2=待检测。
+ * 判定规则:检测完成 = (aebMiddle === 1) || (存在照片)
+ *
+ * 为什么要「有照片」兜底:公司 DDL 门禁要求新增 int 列显式 NOT NULL,
+ * 故 `aeb_middle` 为 NOT NULL DEFAULT 0——"老版本 APP 只传照片、不传状态"的记录会落为 0,
+ * 此时以有照片判定为检测完成,与 APP 端"识别到照片上传自动流转"的语义保持一致。
+ * 注:枚举里 1 固定表示"检测完成"(与同表既有检测项 1=pass 对齐),故此处判 === 1。
+ */
+const aebMiddleDone = computed(() => {
+ const status = props.detailData?.aebMiddleStatus
+ if (status !== null && status !== undefined && Number(status) === 1)
+ return true
+ return Boolean(props.detailData?.aebMiddleImages?.length)
+})
</script>
<template>
@@ -181,6 +199,56 @@
</div>
</div>
+ <!--
+ 车辆运维字段优化:AEB调至"中"
+ 回显胶囊状态(不可编辑)+ 照片原图;状态优先取后端 aebMiddle,为空(历史数据)时按有无照片派生
+ -->
+ <div class="manual-item">
+ <div class="check-label">
+ AEB调至"中":
+ </div>
+ <div class="image-list">
+ <NTag :bordered="false" :type="aebMiddleDone ? 'success' : 'default'" size="small">
+ {{ aebMiddleDone ? '检测完成' : '待检测' }}
+ </NTag>
+ <template v-if="detailData.aebMiddleImages && detailData.aebMiddleImages.length">
+ <NImage
+ v-for="(img, index) in detailData.aebMiddleImages"
+ :key="index"
+ :src="img || 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png'"
+ :width="56"
+ :height="56"
+ object-fit="cover"
+ fallback-src="https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png"
+ class="check-image"
+ />
+ </template>
+ <span v-else class="no-data">-</span>
+ </div>
+ </div>
+
+ <!-- 车辆运维字段优化:二排座椅调节(按需求仅回显照片) -->
+ <div class="manual-item">
+ <div class="check-label">
+ 二排座椅调节:
+ </div>
+ <div class="image-list">
+ <template v-if="detailData.secondRowSeatImages && detailData.secondRowSeatImages.length">
+ <NImage
+ v-for="(img, index) in detailData.secondRowSeatImages"
+ :key="index"
+ :src="img || 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png'"
+ :width="56"
+ :height="56"
+ object-fit="cover"
+ fallback-src="https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png"
+ class="check-image"
+ />
+ </template>
+ <span v-else class="no-data">-</span>
+ </div>
+ </div>
+
<!-- 支付功能 -->
<!-- <div class="manual-item">
<div class="check-label">
diff --git a/src/views/assetManagement/vehicleManagement/operatingVehicle/components/PreparationDetailModal.vue b/src/views/assetManagement/vehicleManagement/operatingVehicle/components/PreparationDetailModal.vue
index 073930e..96212d4 100644
--- a/src/views/assetManagement/vehicleManagement/operatingVehicle/components/PreparationDetailModal.vue
+++ b/src/views/assetManagement/vehicleManagement/operatingVehicle/components/PreparationDetailModal.vue
@@ -30,6 +30,17 @@
emit("update:show", false);
}
+/**
+ * 车辆运维字段优化:车内摄像头分组
+ * 「车内摄像头」标签下分摄像头1/2/3 三行展示,逐个可追溯;
+ * 数据由 detail.vue 用 collectImageUrls 解析后传入,历史数据为空时模板显示 "-"
+ */
+const cameraGroups = computed(() => [
+ { label: '摄像头1', images: props.detailData?.camera1Images ?? [] },
+ { label: '摄像头2', images: props.detailData?.camera2Images ?? [] },
+ { label: '摄像头3', images: props.detailData?.camera3Images ?? [] },
+])
+
function formatPressure(val: any) {
if (val === null || val === undefined || val === "") return "-";
const num = Number(val);
@@ -104,6 +115,30 @@
</div>
<div class="value" style="min-width: 200px">
右后轮:{{ formatPressure(detailData.tirePressures[3].value) }}
+ </div>
+ </div>
+ <!-- 车辆运维字段优化:胎压照片(最多2张,无数据显示 "-") -->
+ <div class="flex mt-12px">
+ <div style="flex: 1">
+ <div class="label">胎压照片</div>
+ <div
+ class="flex gap-16px"
+ v-if="detailData.tirePressureImages && detailData.tirePressureImages.length"
+ >
+ <div
+ class="pic"
+ v-for="(img, idx) in detailData.tirePressureImages"
+ :key="idx"
+ >
+ <NImage
+ :src="img"
+ width="56"
+ height="56"
+ fallback-src="https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png"
+ />
+ </div>
+ </div>
+ <div v-else class="value">-</div>
</div>
</div>
<div class="flex mt-12px">
@@ -247,6 +282,32 @@
</div>
</div>
</div>
+ <!--
+ 车辆运维字段优化:车内摄像头
+ 「车内摄像头」一个标签下分 摄像头1/2/3 三行展示,便于追溯具体摄像头;
+ 每个摄像头最多 2 张,历史数据为空时显示 "-"
+ -->
+ <div class="mt-12px">
+ <div class="label">车内摄像头</div>
+ <div
+ class="mt-8px"
+ v-for="cam in cameraGroups"
+ :key="cam.label"
+ >
+ <div class="label">{{ cam.label }}</div>
+ <div class="flex gap-16px" v-if="cam.images && cam.images.length">
+ <div class="pic" v-for="(img, idx) in cam.images" :key="idx">
+ <NImage
+ :src="img"
+ width="56"
+ height="56"
+ fallback-src="https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png"
+ />
+ </div>
+ </div>
+ <div v-else class="value">-</div>
+ </div>
+ </div>
</div>
</div>
</NModal>
diff --git a/src/views/assetManagement/vehicleManagement/operatingVehicle/detail.vue b/src/views/assetManagement/vehicleManagement/operatingVehicle/detail.vue
index d63330f..2b52707 100644
--- a/src/views/assetManagement/vehicleManagement/operatingVehicle/detail.vue
+++ b/src/views/assetManagement/vehicleManagement/operatingVehicle/detail.vue
@@ -582,6 +582,7 @@
{ title: '清洁时间', key: 'operationTime', align: 'left' as const, width: 180, render: (row: any) => row.operationTime ? dayjs(row.operationTime).format('YYYY/MM/DD HH:mm:ss') : '-' },
{ title: '车身标识及清洁', key: 'bodyEmblem', align: 'left' as const, width: 160, render: (row: any) => renderImageCell(row.bodyEmblem) },
{ title: '产生总费用', key: 'totalCost', align: 'left' as const, width: 140, render: (row: any) => (row.totalCost != null ? `¥${row.totalCost}` : '-') },
+ { title: '车损', key: 'vehicleDamage', align: 'left' as const, width: 180, render: (row: any) => renderVehicleDamageCell(row) },
{ title: '操作人', key: 'operationsUserName', align: 'left' as const, width: 140 },
{
title: '操作',
@@ -944,6 +945,35 @@
return <span>-</span>
return (
<NSpace size="small" justify="flex-start">
+ {urls.map((u: string, idx: number) => (
+ <NImage
+ key={idx}
+ src={u || 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png'}
+ width={40}
+ height={40}
+ object-fit="cover"
+ fallback-src="https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png"
+ style={{ borderRadius: '4px', cursor: 'pointer' }}
+ />
+ ))}
+ </NSpace>
+ )
+}
+
+/**
+ * 车辆运维字段优化:车身清洁列表「车损」列
+ * 需求:回显车损照片(原图);若 APP 选"否"未上传照片,后台回显 "-"。
+ * vehicleDamage: 0=否 1=是(历史数据为 null,按"否"处理)
+ */
+function renderVehicleDamageCell(row: any) {
+ const isDamaged = Number(row?.vehicleDamage) === 1
+ const urls = collectImageUrls(row?.vehicleDamagePicture)
+ // 选"否"或无照片:按需求统一显示 "-"
+ if (!isDamaged && !urls.length)
+ return <span>-</span>
+ return (
+ <NSpace size="small" justify="start" align="center">
+ <span>{isDamaged ? '是' : '否'}</span>
{urls.map((u: string, idx: number) => (
<NImage
key={idx}
@@ -1349,6 +1379,11 @@
orderSystemCheckImages: collectImageUrls(row.systemCheckPicture),
interactiveScreenImages: collectImageUrls(row.interactiveScreenPicture),
paymentCheckImages: collectImageUrls(row.paymentFunctionPicture),
+ // 车辆运维字段优化:AEB调至"中"(状态+照片)、二排座椅调节(照片)
+ // 胶囊状态优先取后端 aebMiddle(0待检测/1检测完成),为空(历史数据)时按有无照片派生
+ aebMiddleImages: collectImageUrls(row.aebMiddlePicture),
+ aebMiddleStatus: row.aebMiddle ?? null,
+ secondRowSeatImages: collectImageUrls(row.secondRowSeatPicture),
vehicleLocation: row.vehicleLocation || '-',
operator: row.operatorStartName || '-',
detectionTime: row.operationTime,
@@ -1415,6 +1450,11 @@
// bodyCleanImages: collectImageUrls(row.bodyEmblem),
interiorCleanImages: collectImageUrls(row.cabinCleaning),
lostItemImages: collectImageUrls(row.lostItems),
+ // 车辆运维字段优化:胎压照片 + 车内摄像头1/2/3(历史数据为空时弹窗内显示 "-")
+ tirePressureImages: collectImageUrls(row.tirePressurePicture),
+ camera1Images: collectImageUrls(row.camera1Picture),
+ camera2Images: collectImageUrls(row.camera2Picture),
+ camera3Images: collectImageUrls(row.camera3Picture),
vehicleLocation: row.vehicleLocation || '-',
lostItemRemark: row.lostItemsRemark || row.lostRemark || row.lostDescribe || '',
operator: row.operationsUserName || '-',
diff --git a/src/views/userContent/contentCenter/opsApp.vue b/src/views/userContent/contentCenter/opsApp.vue
index 0d688b3..1df439b 100644
--- a/src/views/userContent/contentCenter/opsApp.vue
+++ b/src/views/userContent/contentCenter/opsApp.vue
@@ -5,7 +5,8 @@
getVehicleDescription,
saveVehicleDescription,
} from '@/service/api/contentCenter'
-import VehiclePrepareForm from './components/VehiclePrepareForm.vue'
+// 车辆运维字段优化:整备标准说明收敛为单一富文本后,不再使用 VehiclePrepareForm(子分项表单)
+// 该组件文件暂予保留,如后续确认无回滚需求可一并删除
import SimpleRichTextForm from './components/SimpleRichTextForm.vue'
import AgreementListForm from './components/AgreementListForm.vue'
@@ -35,21 +36,14 @@
const data = currentForm.getData()
let saveRes: any = null
- if (activeTab.value === 'prepare') {
- const map: Record<string, number> = {
- tirePressure: 1,
- lighting: 2,
- cabinClean: 4,
- }
- const activeKey = typeof currentForm.getActiveKey === 'function' ? currentForm.getActiveKey() : 'tirePressure'
- const t = map[activeKey] ?? 1
- const content = data[activeKey] || ''
- saveRes = await saveVehicleDescription({ descriptionType: t, descriptionContent: content })
- }
- else if (['charge', 'inspect', 'bodyClean', 'insurance', 'annual', 'maintenance', 'qualification'].includes(activeTab.value)) {
+ if (['prepare', 'charge', 'inspect', 'bodyClean', 'insurance', 'annual', 'maintenance', 'qualification'].includes(activeTab.value)) {
const typeMap: Record<string, number> = {
+ // 车辆运维字段优化:车辆整备标准说明收敛为单一富文本,写入新增的 descriptionType=12
+ // (原按胎压1/灯光2/舱内清洁4 三个子分项配置,收敛后不再区分模块)
+ prepare: 12,
charge: 6,
inspect: 7,
+ // 车辆清洁标准说明复用既有 type 3(车身标识及清洁),不新增类型,避免与存量文案数据分裂
bodyClean: 3,
insurance: 8,
annual: 9,
@@ -90,20 +84,10 @@
return
}
- if (tabKey === 'prepare') {
- const types = [1, 2, 4]
- const res = await Promise.all(types.map(t => getVehicleDescription(t)))
- if (res.every(r => r && r.code === 200)) {
- const data: Record<string, string> = {
- tirePressure: res[0].data?.descriptionContent || '',
- lighting: res[1].data?.descriptionContent || '',
- cabinClean: res[2].data?.descriptionContent || '',
- }
- currentForm.setData(data)
- }
- }
- else if (['charge', 'inspect', 'bodyClean', 'insurance', 'annual', 'maintenance', 'qualification'].includes(tabKey)) {
+ if (['prepare', 'charge', 'inspect', 'bodyClean', 'insurance', 'annual', 'maintenance', 'qualification'].includes(tabKey)) {
const typeMap: Record<string, number> = {
+ // 车辆运维字段优化:整备读取收敛后的单一富文本 type=12
+ prepare: 12,
charge: 6,
inspect: 7,
bodyClean: 3,
@@ -153,12 +137,12 @@
>
<n-tab-pane v-for="tab in tabs" :key="tab.key" :name="tab.key" :tab="tab.label">
<div class="tab-scroll-area">
- <VehiclePrepareForm
- v-if="tab.key === 'prepare'"
- :ref="(el) => { if (el) contentFormRefs[tab.key] = el }"
- />
+ <!--
+ 车辆运维字段优化:车辆整备标准说明已收敛为单一富文本(descriptionType=12),
+ 与车辆清洁(3)/车辆检测(7)等一致复用 SimpleRichTextForm,不再按子分项配置
+ -->
<SimpleRichTextForm
- v-else-if="['charge', 'inspect', 'bodyClean', 'insurance', 'annual', 'maintenance', 'qualification'].includes(tab.key)"
+ v-if="['prepare', 'charge', 'inspect', 'bodyClean', 'insurance', 'annual', 'maintenance', 'qualification'].includes(tab.key)"
:ref="(el) => { if (el) contentFormRefs[tab.key] = el }"
/>
<AgreementListForm
--
Gitblit v1.7.1