.env
@@ -2,7 +2,7 @@ VITE_BASE_URL = / # 项目名称 VITE_APP_NAME = RT后台 VITE_APP_NAME = Robotaxi运营管理系统 # 路由模式 web | hash VITE_ROUTE_MODE = web index.html
@@ -3,9 +3,9 @@ <head> <meta charset="UTF-8" /> <link rel="icon" href="/favicon.svg" /> <link rel="icon" href="/favicon.ico" type="image/x-icon" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>RT后台</title> <title>Robotaxi运营管理系统</title> </head> <body> locales/en_US.json
@@ -237,7 +237,7 @@ "applyTime": "Apply time", "vehicleCode": "Vehicle code", "phoneNumber": "Phone number", "auditStatus": "Review status", "auditStatus": "Status", "auditUserName": "Reviewer", "auditTime": "Review time", "auditRemarks": "Review remarks", locales/zh_CN.json
@@ -403,7 +403,7 @@ "applyTime": "申请时间", "vehicleCode": "车辆编码", "phoneNumber": "联系电话", "auditStatus": "运营状态", "auditStatus": "状态", "auditUserName": "审核人", "auditTime": "审核时间", "auditRemarks": "审核备注", public/favicon.ico
public/logo.png
src/AppMain.vue
@@ -52,6 +52,7 @@ <naive-provider> <router-view /> <Watermark :show-watermark="appStore.showWatermark" /> <CopyrightFooter /> </naive-provider> </n-config-provider> </template> src/components/common/CopyrightFooter.vue
New file @@ -0,0 +1,29 @@ <script setup lang="ts"> // 全局版权信息底部条:固定在窗口底部 const COPYRIGHT_TEXT = '© 2015-2026 广州小鹏汽车科技有限公司 粤ICP备15067788号' </script> <template> <div class="copyright-footer"> {{ COPYRIGHT_TEXT }} </div> </template> <style scoped> .copyright-footer { position: fixed; left: 0; right: 0; bottom: 0; z-index: 9; height: 28px; line-height: 28px; text-align: center; font-size: 12px; color: rgba(0, 0, 0, 0.45); background-color: var(--card-color, #fff); border-top: 1px solid rgba(0, 0, 0, 0.06); pointer-events: none; user-select: none; } </style> src/layouts/components/common/Logo.vue
@@ -19,7 +19,7 @@ <template> <div class="h-68px flex-center cursor-pointer gap-2 p-x-2" @click="router.push('/')"> <img src="/favicon.svg" alt="logo" class="w-24px h-24px" /> <img src="/logo.png" alt="logo" class="h-18px w-auto" /> <span v-show="!hidenLogoText" class="text-ellipsis overflow-hidden whitespace-nowrap title">{{ name }}</span> </div> </template> src/layouts/components/header/UserCenter.vue
@@ -46,6 +46,11 @@ class="cursor-pointer w-[24px] h-[24px]" :src="userInfo?.avatar" > <!-- 无 avatar 时:走默认插槽 --> <div class="wh-full flex-center"> <icon-park-outline-user /> </div> <!-- avatar 加载失败时:走 fallback 插槽 --> <template #fallback> <div class="wh-full flex-center"> <icon-park-outline-user /> src/layouts/index.vue
@@ -41,7 +41,7 @@ // 移动端抽屉控制 const showMobileDrawer = ref(false) const sidebarWidth = ref(196) const sidebarWidth = ref(220) const sidebarCollapsedWidth = ref(64) const hasHorizontalMenu = computed(() => ['horizontal', 'mixed-two-column', 'mixed-sidebar'].includes(layoutMode.value)) @@ -67,6 +67,7 @@ :sidebar-width="sidebarWidth" :sidebar-collapsed-width="sidebarCollapsedWidth" class="robotaxi-content" style="padding-bottom: 28px;" > <template #logo> <Logo /> src/service/api/point.ts
@@ -33,7 +33,7 @@ return request.Get<Service.ResponseResult<any>>(`/map/api/v1/point/deletePoint?ids=${id}`) } export function fetchElectronicFenceAreaList(params: { cityCode?: string } = {}) { export function fetchElectronicFenceAreaList(params: { cityCode?: string, status?: number } = {}) { return request.Post<Service.ResponseResult<Entity.ElectronicFenceArea[]>>('/map/api/v1/point/listElectronicFenceArea', params) } src/views/assetManagement/ruleConfig/index.vue
@@ -539,7 +539,7 @@ <div class="pb-[12px] flex-shrink-0 z-10 bg-[#f5f7fa]"> <div class="bg-white rounded-sm py-[12px] px-[24px] shadow-sm"> <div class="text-lg font-bold mb-[12px] text-gray-800"> 任务模板管理 运维规则配置 </div> <n-radio-group v-model:value="activeTab"> src/views/assetManagement/vehicleManagement/inventoryVehicle/add.vue
@@ -48,7 +48,7 @@ vehiclePictureId: null as string | number | null, // 车辆图片ID purchaseSource: '', // 采购来源 remark: '', // 备注 operationArea: null as string | null, // 运营区域 operationArea: null as number | null, // 运营区域 vehicleAttribute: null as number | null, // 车辆属性 vehicleModelType: null as number | null, // 车辆类型 ownerType: '1', // 所有人类型 1企业 2个人 @@ -83,8 +83,9 @@ const formModel = ref(createInitialFormModel()) // 运营区域选项 const operationAreaOptions = ref<{ label: string, value: string | number }[]>([]) const operationAreaOptions = ref<{ label: string, value: number, electronicId?: string | number | null }[]>([]) const isOperationAreaSelectOpen = ref(false) const currentOperationElectronicId = ref<string | number | null>(null) // 车辆型号选项 const modelOptions = ref<{ label: string, value: string | number }[]>([]) @@ -159,6 +160,22 @@ return Number.isNaN(timestamp) ? null : timestamp } function syncOperationAreaValue() { if ( currentOperationElectronicId.value === null || currentOperationElectronicId.value === undefined || currentOperationElectronicId.value === '' ) { return } const matchedOption = operationAreaOptions.value.find( item => String(item.electronicId) === String(currentOperationElectronicId.value), ) if (matchedOption) { formModel.value.operationArea = Number(matchedOption.value) } } function toUploadFileList(value: unknown): UploadFileInfo[] { return parseFileJson(value).map((item: any, index: number) => ({ id: item?.id || `${index}`, @@ -198,7 +215,7 @@ vehiclePictureId: getVehiclePictureId(data.vehiclePictures), purchaseSource: data.procurementSource || '', remark: data.remarks || data.remark || '', operationArea: data.electronicId === null || data.electronicId === undefined || data.electronicId === '' ? null : String(data.electronicId), operationArea: null, vehicleAttribute: toNumberOrNull(data.vehicleAttribute), vehicleModelType: toNumberOrNull(data.vehicleModelType), ownerType: String(data.ownerType ?? '1'), @@ -225,6 +242,11 @@ insuranceFiles: toUploadFileList(data.policyInformation), onlineCarLicenseFiles: toUploadFileList(data.operationQualification), } currentOperationElectronicId.value = data.electronicId === null || data.electronicId === undefined || data.electronicId === '' ? null : String(data.electronicId) syncOperationAreaValue() } catch (error) { console.error('获取车辆详情失败:', error) @@ -249,15 +271,17 @@ // 获取运营区域列表 async function getOperationAreaList() { try { const { data } = await fetchElectronicFenceAreaList() const { data } = await fetchElectronicFenceAreaList({ status: 2 }) if (data) { // 过滤出 areaType 为 1 的运营区域 // 下拉选项使用区域明细 id,提交时也传该 id 给服务端 operationAreaOptions.value = data .filter((item: any) => item.areaType === 1) .map((item: any) => ({ label: item.areaName, value: item.electronicId, value: item.id, electronicId: item.electronicId, })) syncOperationAreaValue() } } catch (error) { src/views/build-in/public-access/index.vue
@@ -85,8 +85,8 @@ <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> <div class="flex justify-between items-center py-4"> <div class="flex items-center space-x-4"> <div class="w-10 h-10 flex items-center justify-center"> <img src="/favicon.svg" alt="Nova Admin Logo" class="w-10 h-10"> <div class="h-10 flex items-center justify-center"> <img src="/logo.png" alt="Nova Admin Logo" class="h-10 w-auto"> </div> <span class="text-2xl font-bold text-gray-900">Nova Admin</span> </div> @@ -447,8 +447,8 @@ <div class="grid md:grid-cols-4 gap-8"> <div> <div class="flex items-center space-x-2 mb-4"> <div class="w-10 h-10 flex items-center justify-center"> <img src="/favicon.svg" alt="Nova Admin Logo" class="w-8 h-8"> <div class="h-8 flex items-center justify-center"> <img src="/logo.png" alt="Nova Admin Logo" class="h-8 w-auto"> </div> <span class="text-xl font-bold text-white">Nova Admin</span> </div> src/views/operations/taskOrder/taskOrderCenter/components/RegularDetailModal.vue
@@ -97,6 +97,9 @@ return d.format("YYYY/MM/DD HH:mm"); } const fallbackImage = "https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png"; const currentType = computed(() => { if (formModel.value.detectionDetailVO?.id) return "detection"; if (formModel.value.chargingDetailVO?.id) return "charging"; @@ -155,10 +158,13 @@ > <div v-if="currentDetail" class="regular-detail"> <div class="vehicle-header"> <img :src="vehicleImage" <n-image :src="vehicleImage || defaultCarImage" :fallback-src="defaultCarImage" :width="112" :height="64" class="vehicle-image" @error="(e: any) => { e.target.src = 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png' }" object-fit="contain" /> <div class="vehicle-text"> <div class="plate-number"> @@ -252,61 +258,81 @@ <div class="grid-gap-3"> <div class="block"> <div class="block-label">传感器清洁:</div> <div class="thumb-list"> <img v-for="(item, idx) in getValidImageList( currentDetail.sensorCleaningPicture )" :key="idx" :src="item.url" class="thumb" @error="(e: any) => { e.target.src = 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png' }" /> </div> <n-image-group> <div class="thumb-list"> <n-image v-for="(item, idx) in getValidImageList( currentDetail.sensorCleaningPicture )" :key="idx" :src="item.url" :width="56" :height="56" class="thumb" object-fit="contain" :fallback-src="fallbackImage" /> </div> </n-image-group> </div> <div class="block"> <div class="block-label">紧急求组按钮/应答机:</div> <div class="thumb-list"> <img v-for="(item, idx) in getValidImageList( currentDetail.helpButtonPicture )" :key="idx" :src="item.url" class="thumb" @error="(e: any) => { e.target.src = 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png' }" /> </div> <n-image-group> <div class="thumb-list"> <n-image v-for="(item, idx) in getValidImageList( currentDetail.helpButtonPicture )" :key="idx" :src="item.url" :width="56" :height="56" class="thumb" object-fit="contain" :fallback-src="fallbackImage" /> </div> </n-image-group> </div> <div class="block"> <div class="block-label">订单系统检查:</div> <div class="thumb-list"> <img v-for="(item, idx) in getValidImageList( currentDetail.systemCheckPicture )" :key="idx" :src="item.url" class="thumb" @error="(e: any) => { e.target.src = 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png' }" /> </div> <n-image-group> <div class="thumb-list"> <n-image v-for="(item, idx) in getValidImageList( currentDetail.systemCheckPicture )" :key="idx" :src="item.url" :width="56" :height="56" class="thumb" object-fit="contain" :fallback-src="fallbackImage" /> </div> </n-image-group> </div> </div> <div class="grid-gap-3"> <div class="block"> <div class="block-label">车内交互屏检查:</div> <div class="thumb-list"> <img v-for="(item, idx) in getValidImageList( currentDetail.interactiveScreenPicture )" :key="idx" :src="item.url" class="thumb" @error="(e: any) => { e.target.src = 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png' }" /> </div> <n-image-group> <div class="thumb-list"> <n-image v-for="(item, idx) in getValidImageList( currentDetail.interactiveScreenPicture )" :key="idx" :src="item.url" :width="56" :height="56" class="thumb" object-fit="contain" :fallback-src="fallbackImage" /> </div> </n-image-group> </div> <!-- <div class="block"> @@ -352,6 +378,7 @@ <n-image :src="powerIcon" style="width: 25px; height: 12px" preview-disabled /> <span style="color: #e85050; font-size: 11px" >{{ currentDetail.startRemainingBattery ?? "-" }} km</span @@ -375,6 +402,7 @@ <n-image :src="poweredIcon" style="width: 25px; height: 12px" preview-disabled /> <span style="color: #06b752; font-size: 11px" >{{ currentDetail.endRemainingBattery ?? "-" }} km</span @@ -390,28 +418,38 @@ </div> <div class="mt-16px"> <div class="kv-label mt-4px">充电图片</div> <div class="thumb-list"> <img v-for="(item, idx) in getValidImageList( currentDetail.startChargingPicture )" :key="idx" :src="item.url" class="thumb" @error="(e: any) => { e.target.src = 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png' }" /> <template v-if="getValidImageList(currentDetail.endChargingPicture).length"> <img v-for="(item, idx) in getValidImageList( currentDetail.endChargingPicture )" :key="idx" :src="item.url" class="thumb" @error="(e: any) => { e.target.src = 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png' }" /> </template> </div> <n-image-group> <div class="thumb-list"> <n-image v-for="(item, idx) in getValidImageList( currentDetail.startChargingPicture )" :key="`start-${idx}`" :src="item.url" :width="56" :height="56" class="thumb" object-fit="contain" :fallback-src="fallbackImage" /> <template v-if="getValidImageList(currentDetail.endChargingPicture).length" > <n-image v-for="(item, idx) in getValidImageList( currentDetail.endChargingPicture )" :key="`end-${idx}`" :src="item.url" :width="56" :height="56" class="thumb" object-fit="contain" :fallback-src="fallbackImage" /> </template> </div> </n-image-group> </div> <div class="flex mt-16px"> <div class="flex-1"> @@ -489,45 +527,56 @@ <div class="grid-gap-3"> <div class="block"> <div class="block-label color-06">大灯情况:</div> <div class="thumb-list"> <img v-for="(item, idx) in getValidImageList( currentDetail.headLight )" :key="idx" :src="item.url" class="thumb" @error="(e: any) => { e.target.src = 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png' }" /> </div> <n-image-group> <div class="thumb-list"> <n-image v-for="(item, idx) in getValidImageList(currentDetail.headLight)" :key="idx" :src="item.url" :width="56" :height="56" class="thumb" object-fit="contain" :fallback-src="fallbackImage" /> </div> </n-image-group> </div> <div class="block"> <div class="block-label color-06">转向灯情况:</div> <div class="thumb-list"> <img v-for="(item, idx) in getValidImageList( currentDetail.turnSignal )" :key="idx" :src="item.url" class="thumb" @error="(e: any) => { e.target.src = 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png' }" /> </div> <n-image-group> <div class="thumb-list"> <n-image v-for="(item, idx) in getValidImageList(currentDetail.turnSignal)" :key="idx" :src="item.url" :width="56" :height="56" class="thumb" object-fit="contain" :fallback-src="fallbackImage" /> </div> </n-image-group> </div> <div class="block"> <div class="block-label">舱内清洁及消毒</div> <div class="thumb-list"> <img v-for="(item, idx) in getValidImageList( currentDetail.cabinCleaning )" :key="idx" :src="item.url" class="thumb" @error="(e: any) => { e.target.src = 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png' }" /> </div> <div class="block-label color-06">舱内清洁及消毒</div> <n-image-group> <div class="thumb-list"> <n-image v-for="(item, idx) in getValidImageList( currentDetail.cabinCleaning )" :key="idx" :src="item.url" :width="56" :height="56" class="thumb" object-fit="contain" :fallback-src="fallbackImage" /> </div> </n-image-group> </div> <!-- <div class="block"> <div class="block-label color-06">刹车灯情况:</div> @@ -676,15 +725,20 @@ <div v-else-if="currentType === 'clean'"> <div class="block mt-16px"> <div class="block-label color-06">图片</div> <div class="thumb-list"> <img v-for="(item, idx) in getValidImageList(currentDetail.taskImage)" :key="idx" :src="item.url" class="thumb" @error="(e: any) => { e.target.src = 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png' }" /> </div> <n-image-group> <div class="thumb-list"> <n-image v-for="(item, idx) in getValidImageList(currentDetail.taskImage)" :key="idx" :src="item.url" :width="56" :height="56" class="thumb" object-fit="contain" :fallback-src="fallbackImage" /> </div> </n-image-group> </div> <div style="display: grid; grid-template-columns: 1fr 1fr"> <div> @@ -729,17 +783,22 @@ </div> <div class="block mt-16px"> <div class="kv-label mb-4px">维修前图片</div> <div class="thumb-list"> <img v-for="(item, idx) in getValidImageList( currentDetail.startRepairPicture )" :key="idx" :src="item.url" class="thumb" @error="(e: any) => { e.target.src = 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png' }" /> </div> <n-image-group> <div class="thumb-list"> <n-image v-for="(item, idx) in getValidImageList( currentDetail.startRepairPicture )" :key="idx" :src="item.url" :width="56" :height="56" class="thumb" object-fit="contain" :fallback-src="fallbackImage" /> </div> </n-image-group> </div> <div class="flex mt-16px"> <div class="flex-1"> @@ -765,17 +824,22 @@ </div> <div class="block mt-16px"> <div class="kv-label mb-4px">维修费用明细单</div> <div class="thumb-list"> <img v-for="(item, idx) in getValidImageList( currentDetail.repairMoneyDetail )" :key="idx" :src="item.url" class="thumb" @error="(e: any) => { e.target.src = 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png' }" /> </div> <n-image-group> <div class="thumb-list"> <n-image v-for="(item, idx) in getValidImageList( currentDetail.repairMoneyDetail )" :key="idx" :src="item.url" :width="56" :height="56" class="thumb" object-fit="contain" :fallback-src="fallbackImage" /> </div> </n-image-group> </div> <div class="flex mt-16px"> <div class="flex-1"> @@ -802,15 +866,20 @@ </div> <div class="block"> <div class="block-label color-06">图片</div> <div class="thumb-list"> <img v-for="(item, idx) in getValidImageList(currentDetail.taskImage)" :key="idx" :src="item.url" class="thumb" @error="(e: any) => { e.target.src = 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png' }" /> </div> <n-image-group> <div class="thumb-list"> <n-image v-for="(item, idx) in getValidImageList(currentDetail.taskImage)" :key="idx" :src="item.url" :width="56" :height="56" class="thumb" object-fit="contain" :fallback-src="fallbackImage" /> </div> </n-image-group> </div> <div style="display: grid; grid-template-columns: 1fr 1fr"> <div> @@ -845,11 +914,11 @@ } .vehicle-image { width: 112px; height: 64px; border-radius: 4px; object-fit: contain; flex: none; } :deep(.vehicle-image img) { border-radius: 4px; } .vehicle-text { @@ -977,10 +1046,11 @@ } .thumb { width: 56px; height: 56px; overflow: hidden; } :deep(.thumb img) { border-radius: 4px; object-fit: contain; } .grid-gap-3 { display: grid; src/views/operations/taskOrder/taskOrderCenter/components/detailModal.vue
@@ -137,6 +137,8 @@ return d.format('YYYY/MM/DD HH:mm') } const fallbackImage = 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png' function getUrgencyLabel(level: number) { return taskUrgencyLevelOptions.find(it => it.value === level)?.label || '-' } @@ -225,10 +227,14 @@ </div> </div> <div class="mb-16px flex items-center"> <img <n-image :src="getVehicleImage(formModel.accidentDetailVO.vehiclePictures)" class="w-112px h-64px mr-16px accident-vehicle-image" @error="(e: any) => { e.target.src = 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png' }" > :fallback-src="fallbackImage" :width="112" :height="64" class="mr-16px accident-vehicle-image" object-fit="contain" /> <div> <div class="plateNumber mb-8px"> {{ formModel.accidentDetailVO.plateNumber }} @@ -255,22 +261,32 @@ <div class="value mb-8px"> {{ formModel.accidentDetailVO.taskDescription }} </div> <div v-if="getValidImageList(formModel.accidentDetailVO.taskImage).length" class="imgs"> <img v-for="(item, idx) in getValidImageList(formModel.accidentDetailVO.taskImage)" :key="idx" :src="item.url" class="w-56px h-56px accident-thumb" @error="(e: any) => { e.target.src = 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png' }" > </div> <n-image-group v-if="getValidImageList(formModel.accidentDetailVO.taskImage).length"> <div class="imgs"> <n-image v-for="(item, idx) in getValidImageList(formModel.accidentDetailVO.taskImage)" :key="idx" :src="item.url" :fallback-src="fallbackImage" :width="56" :height="56" class="accident-thumb" object-fit="contain" /> </div> </n-image-group> </div> <div class="mb-16px flex gap-24px"> <div class="flex items-center flex-1 gap-8px"> <img <n-image v-if="parseAvatarUrl(formModel.accidentDetailVO.reportUserAvatar)" :src="parseAvatarUrl(formModel.accidentDetailVO.reportUserAvatar)" class="w-40px h-40px accident-avatar" @error="(e: any) => { e.target.src = 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png' }" > :fallback-src="fallbackImage" :width="40" :height="40" class="accident-avatar" object-fit="cover" /> <div class="flex-1 min-w-0"> <div class="label mb-4px"> 上报人 @@ -331,14 +347,20 @@ <!-- <div class="value mb-8px"> {{ formModel.accidentDetailVO.situationDescription }} </div> --> <div v-if="getValidImageList(formModel.accidentDetailVO.responsibleImage).length" class="imgs"> <img v-for="(item, idx) in getValidImageList(formModel.accidentDetailVO.responsibleImage)" :key="idx" :src="item.url" class="w-56px h-56px accident-thumb" @error="(e: any) => { e.target.src = 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png' }" > </div> <n-image-group v-if="getValidImageList(formModel.accidentDetailVO.responsibleImage).length"> <div class="imgs"> <n-image v-for="(item, idx) in getValidImageList(formModel.accidentDetailVO.responsibleImage)" :key="idx" :src="item.url" :fallback-src="fallbackImage" :width="56" :height="56" class="accident-thumb" object-fit="contain" /> </div> </n-image-group> </div> <div class="mb-16px"> <div class="label mb-4px"> @@ -347,14 +369,20 @@ <div class="value mb-8px"> {{ formModel.accidentDetailVO.situationDescription }} </div> <div v-if="getValidImageList(formModel.accidentDetailVO.situationImage).length" class="imgs"> <img v-for="(item, idx) in getValidImageList(formModel.accidentDetailVO.situationImage)" :key="idx" :src="item.url" class="w-56px h-56px accident-thumb" @error="(e: any) => { e.target.src = 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png' }" > </div> <n-image-group v-if="getValidImageList(formModel.accidentDetailVO.situationImage).length"> <div class="imgs"> <n-image v-for="(item, idx) in getValidImageList(formModel.accidentDetailVO.situationImage)" :key="idx" :src="item.url" :fallback-src="fallbackImage" :width="56" :height="56" class="accident-thumb" object-fit="contain" /> </div> </n-image-group> </div> <div class="mb-16px"> <div class="label mb-4px"> @@ -363,22 +391,32 @@ <div class="value mb-8px"> {{ formModel.accidentDetailVO.disposalDescription }} </div> <div v-if="getValidImageList(formModel.accidentDetailVO.disposalImage).length" class="imgs"> <img v-for="(item, idx) in getValidImageList(formModel.accidentDetailVO.disposalImage)" :key="idx" :src="item.url" class="w-56px h-56px accident-thumb" @error="(e: any) => { e.target.src = 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png' }" > </div> <n-image-group v-if="getValidImageList(formModel.accidentDetailVO.disposalImage).length"> <div class="imgs"> <n-image v-for="(item, idx) in getValidImageList(formModel.accidentDetailVO.disposalImage)" :key="idx" :src="item.url" :fallback-src="fallbackImage" :width="56" :height="56" class="accident-thumb" object-fit="contain" /> </div> </n-image-group> </div> <div class="mb-16px flex gap-24px"> <div class="flex items-center flex-1 gap-8px"> <img <n-image v-if="parseAvatarUrl(formModel.accidentDetailVO.handleUserAvatar)" :src="parseAvatarUrl(formModel.accidentDetailVO.handleUserAvatar)" class="w-40px h-40px accident-avatar" @error="(e: any) => { e.target.src = 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png' }" > :fallback-src="fallbackImage" :width="40" :height="40" class="accident-avatar" object-fit="cover" /> <div class="flex-1 min-w-0"> <div class="label mb-4px"> 处理人 @@ -405,10 +443,15 @@ <div v-if="formModel.detectionDetailVO?.id"> <div class="content"> <div class="mb-16px flex items-center"> <img v-if="formModel.detectionDetailVO?.vehiclePictures" :src="formModel.detectionDetailVO.vehiclePictures" class="w-112px h-64px mr-16px" @error="(e: any) => { e.target.src = 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png' }" > <n-image v-if="getVehicleImage(formModel.detectionDetailVO?.vehiclePictures)" :src="getVehicleImage(formModel.detectionDetailVO?.vehiclePictures)" :fallback-src="fallbackImage" :width="112" :height="64" class="mr-16px" object-fit="contain" /> <div> <div class="plateNumber mb-8px"> {{ formModel.detectionDetailVO.plateNumber }} @@ -482,45 +525,77 @@ <div class="mb-8px"> 车内交互屏检查: </div> <div class="imgs"> <img v-for="(item, idx) in JSON.parse(formModel.detectionDetailVO.interactiveScreenPicture)" :key="idx" :src="item.url" class="w-56px h-56px mr-16px" @error="(e: any) => { e.target.src = 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png' }" > </div> <n-image-group> <div class="imgs"> <n-image v-for="(item, idx) in getValidImageList(formModel.detectionDetailVO.interactiveScreenPicture)" :key="idx" :src="item.url" :fallback-src="fallbackImage" :width="56" :height="56" class="mr-16px" object-fit="contain" /> </div> </n-image-group> </div> <div class="mb-16px"> <div class="mb-8px"> 紧急求助按钮/应答机: </div> <div class="imgs"> <img v-for="(item, idx) in JSON.parse(formModel.detectionDetailVO.helpButtonPicture)" :key="idx" :src="item.url" class="w-56px h-56px mr-16px" @error="(e: any) => { e.target.src = 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png' }" > </div> <n-image-group> <div class="imgs"> <n-image v-for="(item, idx) in getValidImageList(formModel.detectionDetailVO.helpButtonPicture)" :key="idx" :src="item.url" :fallback-src="fallbackImage" :width="56" :height="56" class="mr-16px" object-fit="contain" /> </div> </n-image-group> </div> <div class="mb-16px"> <div class="mb-8px"> 交付功能: </div> <div class="imgs"> <img v-for="(item, idx) in JSON.parse(formModel.detectionDetailVO.paymentFunctionPicture)" :key="idx" :src="item.url" class="w-56px h-56px mr-16px" @error="(e: any) => { e.target.src = 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png' }" > </div> <n-image-group> <div class="imgs"> <n-image v-for="(item, idx) in getValidImageList(formModel.detectionDetailVO.paymentFunctionPicture)" :key="idx" :src="item.url" :fallback-src="fallbackImage" :width="56" :height="56" class="mr-16px" object-fit="contain" /> </div> </n-image-group> </div> <div class="mb-16px"> <div class="mb-8px"> 订单系统检查: </div> <div class="imgs"> <img v-for="(item, idx) in JSON.parse(formModel.detectionDetailVO.systemCheckPicture)" :key="idx" :src="item.url" class="w-56px h-56px mr-16px" @error="(e: any) => { e.target.src = 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png' }" > </div> <n-image-group> <div class="imgs"> <n-image v-for="(item, idx) in getValidImageList(formModel.detectionDetailVO.systemCheckPicture)" :key="idx" :src="item.url" :fallback-src="fallbackImage" :width="56" :height="56" class="mr-16px" object-fit="contain" /> </div> </n-image-group> </div> <div class="mb-16px"> 任务单号:{{ formModel.detectionDetailVO.id }} @@ -538,10 +613,15 @@ <div v-if="formModel.chargingDetailVO?.id"> <div class="content"> <div class="mb-16px flex items-center"> <img v-if="formModel.chargingDetailVO?.vehiclePictures.url" :src="formModel.chargingDetailVO.vehiclePictures.url" class="w-112px h-64px mr-16px" @error="(e: any) => { e.target.src = 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png' }" > <n-image v-if="getVehicleImage(formModel.chargingDetailVO?.vehiclePictures)" :src="getVehicleImage(formModel.chargingDetailVO?.vehiclePictures)" :fallback-src="fallbackImage" :width="112" :height="64" class="mr-16px" object-fit="contain" /> <div> <div class="plateNumber mb-8px"> {{ formModel.chargingDetailVO.plateNumber }} @@ -572,11 +652,20 @@ <div class="mb-8px"> 开始充电图片: </div> <div class="imgs"> <div v-for="(item, idx) in JSON.parse(formModel.chargingDetailVO.startChargingPicture)" :key="idx"> <img v-if="item?.url" :src="item.url" class="w-56px h-56px mr-16px" @error="(e: any) => { e.target.src = 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png' }"> <n-image-group> <div class="imgs"> <n-image v-for="(item, idx) in getValidImageList(formModel.chargingDetailVO.startChargingPicture)" :key="idx" :src="item.url" :fallback-src="fallbackImage" :width="56" :height="56" class="mr-16px" object-fit="contain" /> </div> </div> </n-image-group> </div> <div class="mb-16px"> 充电后电量:{{ formModel.chargingDetailVO.endRemainingBattery }}% @@ -592,11 +681,20 @@ <div class="mb-8px"> 结束充电图片: </div> <div class="imgs"> <div v-for="(item, idx) in JSON.parse(formModel.chargingDetailVO.endChargingPicture)" :key="idx"> <img v-if="item?.url" :src="item.url" class="w-56px h-56px mr-16px" @error="(e: any) => { e.target.src = 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png' }"> <n-image-group> <div class="imgs"> <n-image v-for="(item, idx) in getValidImageList(formModel.chargingDetailVO.endChargingPicture)" :key="idx" :src="item.url" :fallback-src="fallbackImage" :width="56" :height="56" class="mr-16px" object-fit="contain" /> </div> </div> </n-image-group> </div> </div> </div> @@ -604,10 +702,15 @@ <div v-if="formModel.preparationDetailVO?.id"> <div class="content"> <div class="mb-16px flex items-center"> <img v-if="formModel.preparationDetailVO?.vehiclePictures" :src="formModel.preparationDetailVO.vehiclePictures" class="w-112px h-64px mr-16px" @error="(e: any) => { e.target.src = 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png' }" > <n-image v-if="getVehicleImage(formModel.preparationDetailVO?.vehiclePictures)" :src="getVehicleImage(formModel.preparationDetailVO?.vehiclePictures)" :fallback-src="fallbackImage" :width="112" :height="64" class="mr-16px" object-fit="contain" /> <div> <div class="plateNumber mb-8px"> {{ formModel.preparationDetailVO.plateNumber }} @@ -635,23 +738,39 @@ <div class="label mb-8px"> 大灯情况: </div> <div v-if="formModel.preparationDetailVO.headLight" class="imgs"> <img v-for="(item, idx) in JSON.parse(formModel.preparationDetailVO.headLight)" :key="idx" :src="item.url" class="w-56px h-56px mr-16px" @error="(e: any) => { e.target.src = 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png' }" > </div> <n-image-group v-if="getValidImageList(formModel.preparationDetailVO.headLight).length"> <div class="imgs"> <n-image v-for="(item, idx) in getValidImageList(formModel.preparationDetailVO.headLight)" :key="idx" :src="item.url" :fallback-src="fallbackImage" :width="56" :height="56" class="mr-16px" object-fit="contain" /> </div> </n-image-group> </div> <div class="mb-16px"> <div class="label mb-8px"> 转向灯情况: </div> <div v-if="formModel.preparationDetailVO.turnSignal" class="imgs"> <img v-for="(item, idx) in JSON.parse(formModel.preparationDetailVO.turnSignal)" :key="idx" :src="item.url" class="w-56px h-56px mr-16px" @error="(e: any) => { e.target.src = 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png' }" > </div> <n-image-group v-if="getValidImageList(formModel.preparationDetailVO.turnSignal).length"> <div class="imgs"> <n-image v-for="(item, idx) in getValidImageList(formModel.preparationDetailVO.turnSignal)" :key="idx" :src="item.url" :fallback-src="fallbackImage" :width="56" :height="56" class="mr-16px" object-fit="contain" /> </div> </n-image-group> </div> <!-- <div class="mb-16px"> <div class="label mb-8px"> @@ -679,34 +798,58 @@ <div class="label mb-8px"> 车身标识及清洁: </div> <div v-if="formModel.preparationDetailVO.bodyEmblem" class="imgs"> <img v-for="(item, idx) in JSON.parse(formModel.preparationDetailVO.bodyEmblem)" :key="idx" :src="item.url" class="w-56px h-56px mr-16px" @error="(e: any) => { e.target.src = 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png' }" > </div> <n-image-group v-if="getValidImageList(formModel.preparationDetailVO.bodyEmblem).length"> <div class="imgs"> <n-image v-for="(item, idx) in getValidImageList(formModel.preparationDetailVO.bodyEmblem)" :key="idx" :src="item.url" :fallback-src="fallbackImage" :width="56" :height="56" class="mr-16px" object-fit="contain" /> </div> </n-image-group> </div> <div class="mb-16px"> <div class="label mb-8px"> 舱内清洁及消毒: </div> <div v-if="formModel.preparationDetailVO.cabinCleaning" class="imgs"> <img v-for="(item, idx) in JSON.parse(formModel.preparationDetailVO.cabinCleaning)" :key="idx" :src="item.url" class="w-56px h-56px mr-16px" @error="(e: any) => { e.target.src = 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png' }" > </div> <n-image-group v-if="getValidImageList(formModel.preparationDetailVO.cabinCleaning).length"> <div class="imgs"> <n-image v-for="(item, idx) in getValidImageList(formModel.preparationDetailVO.cabinCleaning)" :key="idx" :src="item.url" :fallback-src="fallbackImage" :width="56" :height="56" class="mr-16px" object-fit="contain" /> </div> </n-image-group> </div> <div class="mb-16px"> <div class="label mb-8px"> 遗失物品上报: </div> <div v-if="formModel.preparationDetailVO.lostItems" class="imgs"> <img v-for="(item, idx) in JSON.parse(formModel.preparationDetailVO.lostItems)" :key="idx" :src="item.url" class="w-56px h-56px mr-16px" @error="(e: any) => { e.target.src = 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png' }" > </div> <n-image-group v-if="getValidImageList(formModel.preparationDetailVO.lostItems).length"> <div class="imgs"> <n-image v-for="(item, idx) in getValidImageList(formModel.preparationDetailVO.lostItems)" :key="idx" :src="item.url" :fallback-src="fallbackImage" :width="56" :height="56" class="mr-16px" object-fit="contain" /> </div> </n-image-group> <div class="label mt-8px"> 遗失说明:<div v-html="formModel.preparationDetailVO.lostItemsDetail" /> </div> @@ -727,10 +870,15 @@ <div v-if="formModel.cleanDetailVO?.id"> <div class="content"> <div class="mb-16px flex items-center"> <img v-if="formModel.cleanDetailVO?.vehiclePictures" :src="formModel.cleanDetailVO.vehiclePictures" class="w-112px h-64px mr-16px" @error="(e: any) => { e.target.src = 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png' }" > <n-image v-if="getVehicleImage(formModel.cleanDetailVO?.vehiclePictures)" :src="getVehicleImage(formModel.cleanDetailVO?.vehiclePictures)" :fallback-src="fallbackImage" :width="112" :height="64" class="mr-16px" object-fit="contain" /> <div> <div class="plateNumber mb-8px"> {{ formModel.cleanDetailVO.plateNumber }} @@ -748,12 +896,20 @@ <div class="label mb-8px"> 图片: </div> <div class="imgs"> <img v-for="(item, idx) in JSON.parse(formModel.cleanDetailVO.taskImage)" :key="idx" :src="item.url" class="w-56px h-56px mr-16px" @error="(e: any) => { e.target.src = 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png' }" > </div> <n-image-group> <div class="imgs"> <n-image v-for="(item, idx) in getValidImageList(formModel.cleanDetailVO.taskImage)" :key="idx" :src="item.url" :fallback-src="fallbackImage" :width="56" :height="56" class="mr-16px" object-fit="contain" /> </div> </n-image-group> </div> <div class="mb-16px"> 任务单号:{{ formModel.cleanDetailVO.id }} @@ -771,10 +927,15 @@ <div v-if="formModel.repairDetailVO?.id"> <div class="content"> <div class="mb-16px flex items-center"> <img v-if="formModel.repairDetailVO?.vehiclePictures" :src="formModel.repairDetailVO.vehiclePictures" class="w-112px h-64px mr-16px" @error="(e: any) => { e.target.src = 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png' }" > <n-image v-if="getVehicleImage(formModel.repairDetailVO?.vehiclePictures)" :src="getVehicleImage(formModel.repairDetailVO?.vehiclePictures)" :fallback-src="fallbackImage" :width="112" :height="64" class="mr-16px" object-fit="contain" /> <div> <div class="plateNumber mb-8px"> {{ formModel.repairDetailVO.plateNumber }} @@ -799,12 +960,20 @@ <div class="label mb-8px"> 维修前图片: </div> <div class="imgs"> <img v-for="(item, idx) in JSON.parse(formModel.repairDetailVO.startRepairPicture)" :key="idx" :src="item.url" class="w-56px h-56px mr-16px" @error="(e: any) => { e.target.src = 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png' }" > </div> <n-image-group> <div class="imgs"> <n-image v-for="(item, idx) in getValidImageList(formModel.repairDetailVO.startRepairPicture)" :key="idx" :src="item.url" :fallback-src="fallbackImage" :width="56" :height="56" class="mr-16px" object-fit="contain" /> </div> </n-image-group> </div> <div class="mb-16px"> 送修时间:{{ formModel.repairDetailVO.sendTime @@ -820,12 +989,20 @@ <div class="label mb-8px"> 维修费用明细单: </div> <div class="imgs"> <img v-for="(item, idx) in JSON.parse(formModel.repairDetailVO.repairMoneyDetail)" :key="idx" :src="item.url" class="w-56px h-56px mr-16px" @error="(e: any) => { e.target.src = 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png' }" > </div> <n-image-group> <div class="imgs"> <n-image v-for="(item, idx) in getValidImageList(formModel.repairDetailVO.repairMoneyDetail)" :key="idx" :src="item.url" :fallback-src="fallbackImage" :width="56" :height="56" class="mr-16px" object-fit="contain" /> </div> </n-image-group> </div> <div class="mb-16px"> 取回时间:{{ formModel.repairDetailVO.returnTime @@ -843,10 +1020,15 @@ <div v-if="formModel.routineDetailVO?.id"> <div class="content"> <div class="mb-16px flex items-center"> <img v-if="formModel.routineDetailVO?.vehiclePictures" :src="formModel.routineDetailVO.vehiclePictures" class="w-112px h-64px mr-16px" @error="(e: any) => { e.target.src = 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png' }" > <n-image v-if="getVehicleImage(formModel.routineDetailVO?.vehiclePictures)" :src="getVehicleImage(formModel.routineDetailVO?.vehiclePictures)" :fallback-src="fallbackImage" :width="112" :height="64" class="mr-16px" object-fit="contain" /> <div> <div class="plateNumber mb-8px"> {{ formModel.routineDetailVO.plateNumber }} @@ -867,12 +1049,20 @@ <div class="label mb-8px"> 图片: </div> <div class="imgs"> <img v-for="(item, idx) in JSON.parse(formModel.routineDetailVO.taskImage)" :key="idx" :src="item.url" class="w-56px h-56px mr-16px" @error="(e: any) => { e.target.src = 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png' }" > </div> <n-image-group> <div class="imgs"> <n-image v-for="(item, idx) in getValidImageList(formModel.routineDetailVO.taskImage)" :key="idx" :src="item.url" :fallback-src="fallbackImage" :width="56" :height="56" class="mr-16px" object-fit="contain" /> </div> </n-image-group> </div> <div class="mb-16px"> 任务单号:{{ formModel.routineDetailVO.id }} src/views/operations/taskOrder/taskOrderCenter/index.vue
@@ -463,7 +463,7 @@ align: 'left', resizable: true, render(row) { const option = taskUrgencyLevelOptions.find(item => item.value === row.taskUrgencyLevel) const option = taskUrgencyLevelOptions.find(item => item.value == row.taskUrgencyLevel) return ( <n-tag type={option?.type || 'default'} bordered={false}> {option?.label || ''} src/views/service/review/personnel/components/ReviewModal.vue
@@ -1,220 +1,241 @@ <script setup lang="ts"> import { computed, ref, watch } from "vue"; import type { FormInst, FormRules } from "naive-ui"; import { fetchQualificationAuditInfo } from "@/service/api/serviceCenter"; import { computed, ref, watch } from 'vue' import type { FormInst, FormRules } from 'naive-ui' import { fetchQualificationAuditInfo } from '@/service/api/serviceCenter' interface FormModel { result: "pass" | "reject"; auditRemarks: string; result: 'pass' | 'reject' auditRemarks: string } interface QualificationDetail { avatar?: string; certificateOfNoCriminalRecord?: string; driverLicense?: string; id?: number | string; idCardBack?: string; idCardFront?: string; nickName?: string; operationsUserId?: number | string; phoneNumber?: string; roleNames?: string[] | string; trainingGraduationCertificate?: string; avatar?: string auditRemarks?: string auditStatus?: number certificateOfNoCriminalRecord?: string driverLicense?: string id?: number | string idCardBack?: string idCardFront?: string nickName?: string operationsUserId?: number | string phoneNumber?: string roleNames?: string[] | string trainingGraduationCertificate?: string } const { t } = useI18n(); const props = withDefaults( defineProps<{ reviewId?: number | null; selectedCount?: number; show: boolean; reviewId?: number | null selectedCount?: number show: boolean mode?: 'review' | 'detail' }>(), { reviewId: null, selectedCount: 0, } ); mode: 'review', }, ) const emit = defineEmits<{ "update:show": [value: boolean]; confirm: [data: FormModel]; }>(); 'update:show': [value: boolean] 'confirm': [data: FormModel] }>() const { t } = useI18n() const show = computed({ get: () => props.show, set: (value) => emit("update:show", value), }); set: value => emit('update:show', value), }) const isDetailMode = computed(() => props.mode === 'detail') const formRef = ref<FormInst | null>(null); const formRef = ref<FormInst | null>(null) const formModel = ref<FormModel>({ result: "pass", auditRemarks: "", }); const detailLoading = ref(false); const reviewDetail = ref<QualificationDetail | null>(null); result: 'pass', auditRemarks: '', }) const detailLoading = ref(false) const reviewDetail = ref<QualificationDetail | null>(null) let currentRequestId = 0; let currentRequestId = 0 const rules: FormRules = { result: [ { required: true, message: t("serviceReview.common.chooseResult"), trigger: "change", message: t('serviceReview.common.chooseResult'), trigger: 'change', }, ], auditRemarks: [ { validator: (_rule, value) => { if ( formModel.value.result === "reject" && (!value || !String(value).trim()) formModel.value.result === 'reject' && (!value || !String(value).trim()) ) { return new Error(t("serviceReview.common.remarkRequired")); return new Error(t('serviceReview.common.remarkRequired')) } return true; return true }, trigger: ["input", "blur"], trigger: ['input', 'blur'], }, ], }; } function resetForm() { formModel.value = { result: "pass", auditRemarks: "", }; formRef.value?.restoreValidation(); result: 'pass', auditRemarks: '', } formRef.value?.restoreValidation() } function resetDetail() { currentRequestId += 1; detailLoading.value = false; reviewDetail.value = null; currentRequestId += 1 detailLoading.value = false reviewDetail.value = null } function getRoleNames(value: QualificationDetail["roleNames"]) { function getRoleNames(value: QualificationDetail['roleNames']) { if (Array.isArray(value)) { return value; return value } if (typeof value === "string" && value.trim()) { return value.split(",").map(item => item.trim()).filter(Boolean); if (typeof value === 'string' && value.trim()) { return value.split(',').map(item => item.trim()).filter(Boolean) } return []; return [] } function parseImageList(value: unknown) { if (!value) { return [] as string[]; return [] as string[] } if (Array.isArray(value)) { return value .map(item => { if (typeof item === "string") return item; return item?.url || ""; .map((item) => { if (typeof item === 'string') return item return item?.url || '' }) .filter(Boolean); .filter(Boolean) } if (typeof value === "object") { return [(value as { url?: string }).url || ""].filter(Boolean); if (typeof value === 'object') { return [(value as { url?: string }).url || ''].filter(Boolean) } try { const parsed = JSON.parse(String(value)); const parsed = JSON.parse(String(value)) if (Array.isArray(parsed)) { return parsed .map(item => { if (typeof item === "string") return item; return item?.url || ""; .map((item) => { if (typeof item === 'string') return item return item?.url || '' }) .filter(Boolean); .filter(Boolean) } if (parsed?.url) { return [parsed.url]; return [parsed.url] } } catch { return []; } catch { return [] } return []; return [] } function getIdCardImages(detail: QualificationDetail | null) { if (!detail) { return []; return [] } return [ ...parseImageList(detail.idCardFront), ...parseImageList(detail.idCardBack), ]; ] } function getAuditStatusText(status?: number) { if (status === 2) { return '通过' } if (status === 3) { return '不通过' } return '-' } async function loadDetail(id: number | string) { const requestId = ++currentRequestId; detailLoading.value = true; const requestId = ++currentRequestId detailLoading.value = true try { const res = await fetchQualificationAuditInfo(id); const res = await fetchQualificationAuditInfo(id) if (requestId !== currentRequestId) { return; return } if (res.code === 200 && res.data) { reviewDetail.value = res.data; return; reviewDetail.value = res.data return } reviewDetail.value = null; window.$message?.error(res.msg || "获取审核详情失败"); } catch (error) { reviewDetail.value = null window.$message?.error(res.msg || '获取审核详情失败') } catch (error) { if (requestId !== currentRequestId) { return; return } reviewDetail.value = null; console.error("获取审核详情失败", error); window.$message?.error("获取审核详情失败"); } finally { reviewDetail.value = null console.error('获取审核详情失败', error) window.$message?.error('获取审核详情失败') } finally { if (requestId === currentRequestId) { detailLoading.value = false; detailLoading.value = false } } } const handleCancel = () => { show.value = false; }; function handleCancel() { show.value = false } const handleConfirm = async () => { async function handleConfirm() { try { await formRef.value?.validate(); emit("confirm", { ...formModel.value }); show.value = false; } catch (error) { console.log(t("serviceReview.common.validateFail"), error); await formRef.value?.validate() emit('confirm', { ...formModel.value }) show.value = false } }; catch (error) { console.log(t('serviceReview.common.validateFail'), error) } } watch( () => [props.show, props.reviewId] as const, ([visible, reviewId]) => { if (!visible) { resetForm(); resetDetail(); return; resetForm() resetDetail() return } if (reviewId) { loadDetail(reviewId); return; loadDetail(reviewId) return } resetDetail(); resetDetail() }, { immediate: true } ); { immediate: true }, ) </script> <template> @@ -222,7 +243,7 @@ v-model:show="show" :mask-closable="false" preset="card" title="人员资质审核" :title="isDetailMode ? '人员资质详情' : '人员资质审核'" :style="{ width: '600px' }" :auto-focus="false" :segmented="{ @@ -234,11 +255,15 @@ <n-spin :show="detailLoading"> <div class="review-modal-content"> <div v-if="reviewDetail" class="section-container"> <div class="section-title">人员信息</div> <div class="section-title"> 人员信息 </div> <div class="info-grid"> <div class="info-item"> <div class="info-label">姓名</div> <div class="info-label"> 姓名 </div> <div class="info-value name-value"> <!-- <img v-if="parseImageList(reviewDetail.avatar)[0]" @@ -251,21 +276,27 @@ </div> <div class="info-item"> <div class="info-label">编号</div> <div class="info-label"> 编号 </div> <div class="info-value"> {{ reviewDetail.operationsUserId || "-" }} </div> </div> <div class="info-item"> <div class="info-label">手机</div> <div class="info-label"> 手机 </div> <div class="info-value"> {{ reviewDetail.phoneNumber || "-" }} </div> </div> <div class="info-item"> <div class="info-label">角色</div> <div class="info-label"> 角色 </div> <div class="info-value role-list"> <n-tag v-for="role in getRoleNames(reviewDetail.roleNames)" @@ -281,7 +312,9 @@ </div> <div class="info-item"> <div class="info-label">身份证</div> <div class="info-label"> 身份证 </div> <div class="info-value"> <div v-if="getIdCardImages(reviewDetail).length" class="image-list"> <n-image @@ -299,7 +332,9 @@ </div> <div class="info-item"> <div class="info-label">驾驶证</div> <div class="info-label"> 驾驶证 </div> <div class="info-value"> <div v-if="parseImageList(reviewDetail.driverLicense).length" @@ -320,7 +355,9 @@ </div> <div class="info-item"> <div class="info-label">无犯罪记录证明</div> <div class="info-label"> 无犯罪记录证明 </div> <div class="info-value"> <div v-if="parseImageList(reviewDetail.certificateOfNoCriminalRecord).length" @@ -341,7 +378,9 @@ </div> <div class="info-item"> <div class="info-label">培训结业证</div> <div class="info-label"> 培训结业证 </div> <div class="info-value"> <div v-if="parseImageList(reviewDetail.trainingGraduationCertificate).length" @@ -368,9 +407,12 @@ </div> <div class="section-container"> <div class="section-title">资质审核</div> <div class="section-title"> 资质审核 </div> <n-form v-if="!isDetailMode" ref="formRef" :model="formModel" :rules="rules" @@ -384,8 +426,12 @@ > <n-radio-group v-model:value="formModel.result"> <n-space> <n-radio value="pass">通过</n-radio> <n-radio value="reject">不通过</n-radio> <n-radio value="pass"> 通过 </n-radio> <n-radio value="reject"> 不通过 </n-radio> </n-space> </n-radio-group> </n-form-item> @@ -406,14 +452,36 @@ /> </n-form-item> </n-form> <div v-else class="info-grid"> <div class="info-item"> <div class="info-label"> 审核 </div> <div class="info-value"> {{ getAuditStatusText(reviewDetail?.auditStatus) }} </div> </div> <div class="info-item"> <div class="info-label"> 备注 </div> <div class="info-value"> {{ reviewDetail?.auditRemarks?.trim() || '-' }} </div> </div> </div> </div> </div> </n-spin> <template #action> <n-space justify="end"> <n-button @click="handleCancel">{{ t("common.cancel") }}</n-button> <n-button type="primary" @click="handleConfirm"> <n-button @click="handleCancel"> {{ isDetailMode ? t("common.close") : t("common.cancel") }} </n-button> <n-button v-if="!isDetailMode" type="primary" @click="handleConfirm"> {{ t("common.confirm") }} </n-button> </n-space> src/views/service/review/personnel/index.vue
@@ -146,7 +146,7 @@ { title: t('serviceReview.personnel.actions'), key: 'actions', width: 100, width: 140, fixed: 'right', render: (row) => { return ( @@ -160,6 +160,18 @@ onClick={() => handleApprove(row.id)} > {t('serviceReview.personnel.audit')} </NButton> ) : null} {row.auditStatus !== 1 ? ( <NButton text v-permission="80112" type="primary" onClick={() => handleViewDetail(row.id)} > 详情 </NButton> ) : null} @@ -188,6 +200,7 @@ const reviewIds = ref<number[]>([]) const selectedIds = ref<number[]>([]) const showOperationLogModal = ref(false) const reviewModalMode = ref<'review' | 'detail'>('review') function handleSearch() { pagination.value.page = 1 @@ -205,6 +218,7 @@ window.$message?.warning(t('serviceReview.common.selectPersonnelToReview')) return } reviewModalMode.value = 'review' currentReviewId.value = null reviewIds.value = [...selectedIds.value] showReviewModal.value = true @@ -215,8 +229,16 @@ } function handleApprove(id: number) { reviewModalMode.value = 'review' currentReviewId.value = id reviewIds.value = [id] showReviewModal.value = true } function handleViewDetail(id: number) { reviewModalMode.value = 'detail' currentReviewId.value = id reviewIds.value = [] showReviewModal.value = true } @@ -273,6 +295,7 @@ if (!visible) { currentReviewId.value = null reviewIds.value = [] reviewModalMode.value = 'review' } }) @@ -531,6 +554,7 @@ <ReviewModal v-model:show="showReviewModal" :review-id="currentReviewId" :mode="reviewModalMode" :selected-count="reviewIds.length" @confirm="handleConfirmReview" /> src/views/system/config/index.vue
@@ -214,26 +214,27 @@ :allow-input="onlyAllowNumberAndDash" /> </n-form-item> <n-form-item label="小程序附近热门站点推荐距离" path="nearPointDistance" required class="row-item"> <div class="inline-input w-full"> <n-input-group style="width: 100%;"> <n-input :value="formModel.nearPointDistance" placeholder="请输入" :allow-input="onlyAllowNumberAndDash" style="width: 100%;" @update:value="handleNearPointDistanceChange" /> <n-input-group-label>m</n-input-group-label> </n-input-group> <span class="inline-note">内</span> </div> <n-form-item label="常见问题首页展示数" path="headShowCount" required class="row-item"> <n-input :value="formModel.headShowCount" placeholder="请输入" :allow-input="onlyAllowNumberAndDash" style="width: 339px;" @update:value="handleHeadShowCountChange" /> </n-form-item> <n-form-item label="小程序首页地图推荐距离" path="nearPointDistanceSearch" required class="row-item"> <n-form-item label="是否开启运营期免收费" path="operateFree" required class="row-item"> <n-switch v-model:value="formModel.operateFree" :unchecked-value="0" :checked-value="1" /> </n-form-item> </div> <div class="form-row gap-x-20px!"> <n-form-item label="首页附近站点推荐距离" path="nearPointDistance" required class="row-item"> <div class="inline-input"> <n-input-group style="width: 222px;"> <n-input :value="formModel.nearPointDistanceSearch" placeholder="请输入" :allow-input="onlyAllowNumberAndDash" style="width: 100%;" @update:value="handleNearPointDistanceChange" @update:value="handleNearPointDistanceSearchChange" /> <n-input-group-label>m</n-input-group-label> @@ -241,16 +242,20 @@ <span class="inline-note">内</span> </div> </n-form-item> </div> <div class="form-row gap-x-40px!"> <n-form-item label="常见问题首页展示数" path="headShowCount" required class="row-item"> <n-input :value="formModel.headShowCount" placeholder="请输入" :allow-input="onlyAllowNumberAndDash" style="width: 100%;" @update:value="handleHeadShowCountChange" /> <n-form-item label="列表页附近热门站点推荐距离" path="nearPointDistanceSearch" required class="row-item"> <div class="inline-input"> <n-input-group style="width: 222px;"> <n-input :value="formModel.nearPointDistance" placeholder="请输入" :allow-input="onlyAllowNumberAndDash" @update:value="handleNearPointDistanceChange" /> <n-input-group-label>m</n-input-group-label> </n-input-group> <span class="inline-note">内</span> </div> </n-form-item> <n-form-item label="小程序天气单位" path="temperatureUnit" required class="row-item"> <n-select v-model:value="formModel.temperatureUnit" src/views/transaction/order/evaluationRecord/index.vue
@@ -67,7 +67,7 @@ width: 140, }, { title: '评价分数', title: '评价等级', key: 'orderRating', resizable: true, width: 120, @@ -128,7 +128,7 @@ render: () => <n-input v-model:value={model.value.phoneNumber} />, }, { label: '评价分数', label: '评价等级', path: 'orderRating', render: () => ( <n-select src/views/transaction/order/orderManagement/index.vue
@@ -286,11 +286,11 @@ }, { title: t('transaction.orderAmount'), key: 'orderMoney', key: 'payAmount', width: 120, resizable: true, render(row) { return row.orderMoney ? `¥${row.orderMoney.toFixed(2)}` : '-' return row.payAmount ? `¥${row.payAmount.toFixed(2)}` : '-' }, }, { src/views/userContent/banner/index.vue
@@ -234,36 +234,6 @@ ) }, }, { title: '上架状态', key: 'bannerStatus', width: 100, resizable: true, render: (row) => { return ( <n-switch value={row.bannerStatus === 1} onUpdate:value={async (value: boolean) => { try { const { error, code } = await updateBanner({ id: row.id, bannerStatus: value ? 1 : 2, }) as any if (!error && code === 200) { message.success('状态更新成功') fetchData() } } catch (e) { console.error('更新状态失败', e) // 恢复原状态 fetchData() } }} /> ) }, }, // { // title: '跳转类型', // key: 'jumpType', @@ -306,7 +276,7 @@ title: '操作', key: 'actions', align: 'left', width: 80, width: 180, fixed: 'right', render: row => ( <div class="flex justify-start gap-3"> @@ -331,6 +301,15 @@ > 删除 </span> <n-switch value={row.bannerStatus === 1} onUpdate:value={(value: boolean) => handleToggleBannerStatus(row, value)} > {{ checked: () => '上架', unchecked: () => '下架', }} </n-switch> </div> ), }, @@ -340,6 +319,23 @@ showOperationLogModal.value = true } async function handleToggleBannerStatus(row: Banner, value: boolean) { try { const { error, code } = await updateBanner({ id: row.id, bannerStatus: value ? 1 : 2, }) as any if (!error && code === 200) { message.success('状态更新成功') fetchData() } } catch (e) { console.error('更新状态失败', e) fetchData() } } async function fetchData() { startLoading() try {