瀏覽代碼

fix(specialEquipment): 维护模块存取逻辑重构

按用户需求明确存取逻辑:
1. 查询条件修复
   - 维护状态下拉绑定 queryParams.maintenanceStatus(之前错绑 inspectionStatus)
   - 日期范围 params 改 beginNextMaintenanceDate/endNextMaintenanceDate
2. 列表字段
   - 加'下次维护日期'列(取 row.nextMaintenanceDate)
   - 维护开始/结束时间改用 row.maintenanceStartTime/maintenanceEndTime(后端最新记录注入)
3. 设备维护按钮(用户需求 ③)
   - 先调 getLatestInProgressMaintenance 判断
   - 若有维护中记录 -> 回填表单,打开维护编辑(标题:'继续维护(维护中)')
   - 若无 -> 打开新增维护(标题:'新增设备维护')
4. 维护完成必填(用户需求 ④)
   - 维护状态='已维护'时,表单显示'下次维护时间'输入框
   - 必填校验规则 nextMaintenanceDate 联动 maintenanceStatus
   - 后端 updateByBo 同步更新设备 next_maintenance_date
5. 列表判定(用户需求 ⑤)
   - 维护状态列展示后端 fillMaintenanceStatus 动态判定的值
   - '待维护'/'维护中'/'已维护'/'未维护' 4 态按规则自动转换

变更文件:
- views/risk/specialEquipment/index.vue
- api/risk/equipmentMaintenance.ts (加 getLatestInProgressMaintenance)
- api/risk/types.ts (RiskEquipmentMaintenanceVo 加 nextMaintenanceDate)
klxj001 3 天之前
父節點
當前提交
bc897b2171
共有 3 個文件被更改,包括 224 次插入36 次删除
  1. 9 0
      src/api/risk/equipmentMaintenance.ts
  2. 2 0
      src/api/risk/types.ts
  3. 213 36
      src/views/risk/specialEquipment/index.vue

+ 9 - 0
src/api/risk/equipmentMaintenance.ts

@@ -52,3 +52,12 @@ export const listEquipmentMaintenanceByEquipment = (equipmentId: string | number
     method: 'get'
   });
 };
+
+// 查询设备当前是否有"维护中"状态的记录
+// 用于"设备维护"按钮交互(用户需求 ③):若有"维护中"则继续维护编辑,否则打开新增维护页面
+export const getLatestInProgressMaintenance = (equipmentId: string | number): AxiosPromise<RiskEquipmentMaintenanceVo> => {
+  return request({
+    url: '/risk/equipmentMaintenance/latestInProgress/' + equipmentId,
+    method: 'get'
+  });
+};

+ 2 - 0
src/api/risk/types.ts

@@ -484,6 +484,8 @@ export interface RiskEquipmentMaintenanceVo {
   planEndTime: string;
   actualStartTime: string;
   actualEndTime: string;
+  /** 下次维护时间(维护完成时由用户录入) */
+  nextMaintenanceDate: string;
   maintainerName: string;
   siteName: string;
   maintenanceContent: string;

+ 213 - 36
src/views/risk/specialEquipment/index.vue

@@ -30,7 +30,7 @@
           </el-select>
         </el-form-item>
         <el-form-item label="维护状态">
-          <el-select v-model="queryParams.inspectionStatus" placeholder="全部状态" clearable style="width: 200px" @change="handleQuery">
+          <el-select v-model="queryParams.maintenanceStatus" placeholder="全部状态" clearable style="width: 200px" @change="handleQuery">
             <el-option label="全部状态" value="" />
             <el-option
               v-for="d in maintenanceStatusOptions"
@@ -74,6 +74,9 @@
           </template>
         </el-table-column>
         <el-table-column label="安装位置" align="center" prop="location" :show-overflow-tooltip="true" min-width="160" />
+        <el-table-column label="下次维护日期" align="center" prop="nextMaintenanceDate" width="130">
+          <template #default="{ row }">{{ row.nextMaintenanceDate ? String(row.nextMaintenanceDate).substring(0, 10) : '—' }}</template>
+        </el-table-column>
         <el-table-column label="维护开始时间" align="center" width="170">
           <template #default="{ row }">{{ formatMaintenanceTime(row, 'start') }}</template>
         </el-table-column>
@@ -253,6 +256,54 @@
             show-word-limit
           />
         </el-form-item>
+        <el-form-item label="维护结果">
+          <el-input
+            v-model="maintainForm.maintenanceResult"
+            type="textarea"
+            :rows="2"
+            placeholder="请输入维护结果(选填)"
+            maxlength="500"
+            show-word-limit
+          />
+        </el-form-item>
+        <!-- ④ 维护完成时必须录入下一次维护时间 -->
+        <el-form-item
+          v-if="maintainForm.maintenanceStatus === '已维护'"
+          label="下次维护时间"
+          prop="nextMaintenanceDate"
+        >
+          <el-date-picker
+            v-model="maintainForm.nextMaintenanceDate"
+            type="date"
+            placeholder="请选择下次维护时间(必填)"
+            value-format="YYYY-MM-DD"
+            format="YYYY-MM-DD"
+            style="width: 100%"
+          />
+        </el-form-item>
+        <!-- 维护照片上传(可选项) -->
+        <el-form-item label="维护照片">
+          <div class="upload-row">
+            <el-button type="primary" plain @click="triggerMaintainUpload">选择文件</el-button>
+            <span class="upload-tip">{{ maintainPreviewPhotos.length > 0 ? `已选择 ${maintainPreviewPhotos.length} 个文件` : '未选择任何文件' }}</span>
+            <el-button v-if="maintainPreviewPhotos.length > 0" link type="danger" style="margin-left: 8px" @click="clearMaintainPhotos">清空</el-button>
+          </div>
+          <input
+            ref="maintainPhotoInputRef"
+            type="file"
+            multiple
+            accept=".jpg,.jpeg,.png"
+            style="display: none"
+            @change="onMaintainPhotoInputChange"
+          />
+          <div v-if="maintainPreviewPhotos.length > 0" class="photo-preview-list">
+            <div v-for="(url, idx) in maintainPreviewPhotos" :key="idx" class="photo-thumb">
+              <el-image :src="url" :preview-src-list="maintainPreviewPhotos" :initial-index="idx" fit="cover" />
+              <el-icon class="photo-remove" @click="removeMaintainPhoto(idx)"><CircleCloseFilled /></el-icon>
+            </div>
+          </div>
+          <div class="el-upload__tip">支持 JPG、PNG 格式</div>
+        </el-form-item>
       </el-form>
       <template #footer>
         <div class="dialog-footer">
@@ -338,7 +389,10 @@ import {
 } from '@/api/risk/specialEquipment';
 import {
   listEquipmentMaintenance,
-  addEquipmentMaintenance
+  addEquipmentMaintenance,
+  updateEquipmentMaintenance,
+  getLatestInProgressMaintenance,
+  listEquipmentMaintenanceByEquipment
 } from '@/api/risk/equipmentMaintenance';
 import { uploadFile } from '@/api/risk/file';
 import { getDicts } from '@/api/system/dict/data';
@@ -365,6 +419,10 @@ const equipmentPhotoList = ref<any[]>([]);
 const equipmentPhotoPreview = ref<string[]>([]);
 const equipmentPhotoInputRef = ref<HTMLInputElement>();
 
+// 维护照片队列(自管文件)
+const maintainPreviewPhotos = ref<string[]>([]);
+const maintainPhotoInputRef = ref<HTMLInputElement>();
+
 const queryFormRef = ref<FormInstance>();
 const formRef = ref<FormInstance>();
 const maintainFormRef = ref<FormInstance>();
@@ -421,14 +479,20 @@ const maintainForm = reactive<any>({
   equipmentId: undefined,
   equipmentName: '',
   equipmentCode: '',
-  maintenanceStatus: '维护',
+  maintenanceStatus: '维护',
   planStartTime: '',
   planEndTime: '',
   actualStartTime: '',
   actualEndTime: '',
+  /** 下次维护时间:仅当 maintenanceStatus='已维护' 时必填 */
+  nextMaintenanceDate: '',
+  /** 维护结果(可选项) */
+  maintenanceResult: '',
   maintainerName: '',
   siteName: '',
-  maintenanceContent: ''
+  maintenanceContent: '',
+  /** 维护照片 JSON 字符串 */
+  photoList: '[]'
 });
 
 const maintainRules = {
@@ -448,7 +512,20 @@ const maintainRules = {
     }
   ],
   maintainerName: [{ required: true, message: '请选择维护人员', trigger: 'blur' }],
-  siteName: [{ required: true, message: '请选择现场人员', trigger: 'blur' }]
+  siteName: [{ required: true, message: '请选择现场人员', trigger: 'blur' }],
+  // 已维护时下次维护时间必填(④)
+  nextMaintenanceDate: [
+    {
+      validator: (_rule: any, value: any, callback: any) => {
+        if (maintainForm.maintenanceStatus === '已维护' && !value) {
+          callback(new Error('维护完成时,请录入下次维护时间'));
+        } else {
+          callback();
+        }
+      },
+      trigger: 'change'
+    }
+  ]
 };
 
 const recordList = ref<RiskEquipmentMaintenanceVo[]>([]);
@@ -509,11 +586,9 @@ const formatDateTime = (val: any): string => {
   return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`;
 };
 
+// 取最新一条维护记录注入的字段(由后端 fillLatestMaintenance 填充)
 const formatMaintenanceTime = (row: any, type: 'start' | 'end') => {
-  if (row.maintenanceStatus === '已维护') {
-    return formatDateTime(type === 'start' ? row.actualStartTime : row.actualEndTime);
-  }
-  return formatDateTime(type === 'start' ? row.planStartTime : row.planEndTime);
+  return formatDateTime(type === 'start' ? row.maintenanceStartTime : row.maintenanceEndTime);
 };
 
 const disabledEndDate = (time: Date, startTime: string) => {
@@ -525,8 +600,8 @@ const getList = async () => {
   loading.value = true;
   if (nextDateRange.value && nextDateRange.value.length === 2) {
     queryParams.value.params = {
-      beginNextInspectionDate: nextDateRange.value[0],
-      endNextInspectionDate: nextDateRange.value[1]
+      beginNextMaintenanceDate: nextDateRange.value[0],
+      endNextMaintenanceDate: nextDateRange.value[1]
     };
   } else {
     queryParams.value.params = {};
@@ -547,7 +622,7 @@ const resetQuery = () => {
   nextDateRange.value = [];
   queryParams.value.equipmentName = '';
   queryParams.value.equipmentType = '';
-  queryParams.value.inspectionStatus = '';
+  queryParams.value.maintenanceStatus = '';
   handleQuery();
 };
 
@@ -626,6 +701,52 @@ const clearEquipmentPhotos = () => {
   equipmentPhotoPreview.value = [];
 };
 
+// ============ 维护照片上传 ============
+const triggerMaintainUpload = () => {
+  maintainPhotoInputRef.value?.click();
+};
+
+const onMaintainPhotoInputChange = async (e: Event) => {
+  const input = e.target as HTMLInputElement;
+  const files = Array.from(input.files || []);
+  if (files.length === 0) return;
+  for (const file of files) {
+    if (!['image/jpeg', 'image/png', 'image/jpg'].includes(file.type)) {
+      ElMessage.error(`文件 ${file.name} 格式不支持,仅允许 JPG/PNG`);
+      continue;
+    }
+    if (file.size / 1024 / 1024 >= 10) {
+      ElMessage.error(`文件 ${file.name} 超过 10MB`);
+      continue;
+    }
+    await uploadMaintainPhoto(file);
+  }
+  input.value = '';
+};
+
+const uploadMaintainPhoto = async (file: File) => {
+  try {
+    const res: any = await uploadFile(file as any);
+    const url: string | undefined = res?.url || res?.data?.url;
+    if (url) {
+      maintainPreviewPhotos.value.push(url);
+      ElMessage.success(`${file.name} 上传成功`);
+    } else {
+      ElMessage.error(`${file.name} 上传失败`);
+    }
+  } catch (e: any) {
+    ElMessage.error(`${file.name} 上传失败: ${e?.message || '网络异常'}`);
+  }
+};
+
+const removeMaintainPhoto = (idx: number) => {
+  maintainPreviewPhotos.value.splice(idx, 1);
+};
+
+const clearMaintainPhotos = () => {
+  maintainPreviewPhotos.value = [];
+};
+
 const submitForm = () => {
   formRef.value?.validate(async (valid: boolean) => {
     if (valid) {
@@ -667,40 +788,90 @@ const closeDialog = () => {
   reset();
 };
 
-const handleMaintain = (row: RiskSpecialEquipmentVo) => {
-  maintainForm.id = undefined;
-  maintainForm.equipmentId = row.id;
-  maintainForm.equipmentName = row.equipmentName;
-  maintainForm.equipmentCode = row.equipmentCode;
-  maintainForm.maintenanceStatus = '待维护';
-  maintainForm.planStartTime = '';
-  maintainForm.planEndTime = '';
-  maintainForm.actualStartTime = '';
-  maintainForm.actualEndTime = '';
-  maintainForm.maintainerName = '';
-  maintainForm.siteName = '';
-  maintainForm.maintenanceContent = '';
-  maintainDialog.visible = true;
+// 设备维护按钮 - 按用户需求 ③:
+//   先查 latestInProgress: 若有"维护中" -> 打开维护编辑页(继续);若无 -> 打开新增维护页
+const handleMaintain = async (row: RiskSpecialEquipmentVo) => {
+  try {
+    const res: any = await getLatestInProgressMaintenance(row.id);
+    const inProgress: RiskEquipmentMaintenanceVo | null = res?.data || null;
+    if (inProgress && inProgress.id) {
+      // 有维护中:回填到维护表单,编辑模式
+      maintainForm.id = inProgress.id;
+      maintainForm.equipmentId = row.id;
+      maintainForm.equipmentName = row.equipmentName;
+      maintainForm.equipmentCode = row.equipmentCode;
+      maintainForm.maintenanceStatus = inProgress.maintenanceStatus || '维护中';
+      maintainForm.planStartTime = inProgress.planStartTime || '';
+      maintainForm.planEndTime = inProgress.planEndTime || '';
+      maintainForm.actualStartTime = inProgress.actualStartTime || '';
+      maintainForm.actualEndTime = inProgress.actualEndTime || '';
+      maintainForm.maintainerName = inProgress.maintainerName || '';
+      maintainForm.siteName = inProgress.siteName || '';
+      maintainForm.maintenanceContent = inProgress.maintenanceContent || '';
+      maintainForm.nextMaintenanceDate = inProgress.nextMaintenanceDate || '';
+      maintainForm.maintenanceResult = inProgress.maintenanceResult || '';
+      // 维护中的照片(只读展示)
+      maintainForm.photoList = inProgress.photoList || '[]';
+      maintainDialog.title = '继续维护(维护中)';
+    } else {
+      // 无维护中:新增维护
+      maintainForm.id = undefined;
+      maintainForm.equipmentId = row.id;
+      maintainForm.equipmentName = row.equipmentName;
+      maintainForm.equipmentCode = row.equipmentCode;
+      maintainForm.maintenanceStatus = '维护中';
+      maintainForm.planStartTime = '';
+      maintainForm.planEndTime = '';
+      maintainForm.actualStartTime = '';
+      maintainForm.actualEndTime = '';
+      maintainForm.maintainerName = '';
+      maintainForm.siteName = '';
+      maintainForm.maintenanceContent = '';
+      maintainForm.nextMaintenanceDate = '';
+      maintainForm.maintenanceResult = '';
+      maintainForm.photoList = '[]';
+      maintainDialog.title = '新增设备维护';
+    }
+    maintainDialog.visible = true;
+  } catch (e: any) {
+    ElMessage.error('查询维护中记录失败: ' + (e?.message || '网络异常'));
+  }
 };
 
 const submitMaintainForm = () => {
   maintainFormRef.value?.validate(async (valid: boolean) => {
-    if (valid) {
-      const submit = { ...maintainForm };
-      if (submit.maintenanceStatus === '已维护' && !submit.actualStartTime) {
-        submit.actualStartTime = submit.planStartTime;
-        submit.actualEndTime = submit.planEndTime;
-      }
+    if (!valid) return;
+    // ④ 已维护 -> 必须录入下次维护时间
+    if (maintainForm.maintenanceStatus === '已维护' && !maintainForm.nextMaintenanceDate) {
+      ElMessage.error('请录入下次维护时间');
+      return;
+    }
+    const submit: any = { ...maintainForm };
+    // 同步照片列表到 photoList(JSON 字符串)
+    submit.photoList = JSON.stringify(maintainPreviewPhotos.value);
+    // 已维护:自动回填实际开始/结束时间为预计时间(若未填)
+    if (submit.maintenanceStatus === '已维护') {
+      if (!submit.actualStartTime) submit.actualStartTime = submit.planStartTime;
+      if (!submit.actualEndTime) submit.actualEndTime = submit.planEndTime;
+    }
+    // 新增或更新(按 maintainForm.id 判断)
+    if (submit.id) {
+      await updateEquipmentMaintenance(submit);
+      proxy?.$modal.msgSuccess('维护记录已更新');
+    } else {
       await addEquipmentMaintenance(submit);
       proxy?.$modal.msgSuccess('维护记录已保存');
-      maintainDialog.visible = false;
     }
+    maintainDialog.visible = false;
+    await getList();
   });
 };
 
 const closeMaintainDialog = () => {
   maintainDialog.visible = false;
   maintainFormRef.value?.resetFields();
+  // 关闭时同时清空照片队列,避免下次打开看到旧照片
+  clearMaintainPhotos();
 };
 
 const handleViewRecords = async (row: RiskSpecialEquipmentVo) => {
@@ -712,8 +883,9 @@ const handleViewRecords = async (row: RiskSpecialEquipmentVo) => {
 const loadRecordList = async (equipmentId: string | number) => {
   recordLoading.value = true;
   try {
-    const res = await listEquipmentMaintenance({ equipmentId } as any);
-    recordList.value = res.rows || [];
+    // 使用 listByEquipment 接口直接获取列表,无需分页参数
+    const res: any = await listEquipmentMaintenanceByEquipment(equipmentId);
+    recordList.value = (res?.data || []) as RiskEquipmentMaintenanceVo[];
   } catch (e) {
     recordList.value = [];
   }
@@ -727,7 +899,7 @@ const handleAddRecord = () => {
     maintainForm.equipmentId = last.equipmentId;
     maintainForm.equipmentName = last.equipmentName;
     maintainForm.equipmentCode = last.equipmentCode;
-    maintainForm.maintenanceStatus = '维护';
+    maintainForm.maintenanceStatus = '维护';
     maintainForm.planStartTime = '';
     maintainForm.planEndTime = '';
     maintainForm.actualStartTime = '';
@@ -735,6 +907,11 @@ const handleAddRecord = () => {
     maintainForm.maintainerName = '';
     maintainForm.siteName = '';
     maintainForm.maintenanceContent = '';
+    maintainForm.nextMaintenanceDate = '';
+    maintainForm.maintenanceResult = '';
+    maintainForm.photoList = '[]';
+    clearMaintainPhotos();
+    maintainDialog.title = '新增设备维护';
     maintainDialog.visible = true;
   }
 };