Răsfoiți Sursa

pms 查询保养项的 累计运行时长 累计运行里程 兼容 运行记录模板包含多个累计类型的属性

zhangcl 1 lună în urmă
părinte
comite
06da08d706

+ 16 - 0
src/api/pms/iotmaintenancebom/index.ts

@@ -42,6 +42,22 @@ export interface IotMaintenanceBomVO {
   assetProperty: string //资产性质
   totalMileage: number  // 累计运行公里数
   totalRunTime: number  // 累计运行时间
+  tempTotalMileage: number  // 临时 累计运行公里数
+  tempTotalRunTime: number  // 临时 累计运行时间
+  isRuntimeFromTemp: false
+  isMileageFromTemp: false
+  // 运行记录模板中 包含多个 累计时长 属性列表
+  timeAccumulatedAttrs: Array<{
+    pointName: string;
+    totalRunTime: number;
+    totalMileage: number;
+  }>;
+  // 运行记录模板中 包含多个 累计公里数 属性列表
+  mileageAccumulatedAttrs: Array<{
+    pointName: string;
+    totalRunTime: number;
+    totalMileage: number;
+  }>;
 }
 
 // PMS 保养计划明细BOM API

+ 4 - 1
src/locales/en.ts

@@ -975,7 +975,10 @@ export default {
     nextMaintDate:'NextMaintDate',
     completed: 'Completed',
     delayed: 'Delayed',
-    maintaining: 'Maintaining'
+    maintaining: 'Maintaining',
+    accumulatedParams: 'COP',
+    accumulatedRunTime: 'COT',
+    accumulatedMileage: 'COK'
   },
   inspect:{
     InspectionItems:'InspectionItems',

+ 4 - 1
src/locales/ru.ts

@@ -958,7 +958,10 @@ export default {
     nextMaintDate:'下次保养自然日期',
     completed: '完成',
     delayed: '延时',
-    maintaining: '保养中'
+    maintaining: '保养中',
+    accumulatedParams: '累计运行参数',
+    accumulatedRunTime: '累计运行时长',
+    accumulatedMileage: '累计运行公里数'
   },
   inspect:{
     InspectionItems:'巡检项',

+ 4 - 1
src/locales/zh-CN.ts

@@ -971,7 +971,10 @@ export default {
     nextMaintDate:'下次保养自然日期',
     completed: '完成',
     delayed: '延时',
-    maintaining: '保养中'
+    maintaining: '保养中',
+    accumulatedParams: '累计运行参数',
+    accumulatedRunTime: '累计运行时长',
+    accumulatedMileage: '累计运行公里数'
   },
   inspect:{
     InspectionItems:'巡检项',

+ 278 - 28
src/views/pms/maintenance/IotMaintenancePlan.vue

@@ -59,8 +59,18 @@
         <el-table-column label="设备id" align="center" prop="deviceId" v-if="false"/>
         <el-table-column :label="t('iotMaintain.deviceCode')" align="center" prop="deviceCode" />
         <el-table-column :label="t('iotMaintain.deviceName')" align="center" prop="deviceName" />
-        <el-table-column :label="t('operationFillForm.sumTime')" align="center" prop="totalRunTime" :formatter="erpPriceTableColumnFormatter"/>
-        <el-table-column :label="t('operationFillForm.sumKil')" align="center" prop="totalMileage" :formatter="erpPriceTableColumnFormatter"/>
+        <el-table-column :label="t('operationFillForm.sumTime')" align="center" prop="totalRunTime" :formatter="erpPriceTableColumnFormatter">
+          <template #default="{ row }">
+            {{ row.totalRunTime ?? row.tempTotalRunTime }}
+          </template>
+        </el-table-column>
+        <el-table-column :label="t('operationFillForm.sumKil')" align="center" prop="totalMileage" :formatter="erpPriceTableColumnFormatter">
+          <template #default="{ row }">
+            {{ row.totalMileage ?? row.tempTotalMileage }}
+          </template>
+        </el-table-column>
+        <el-table-column label="tempTotalRunTime" align="center" prop="tempTotalRunTime" :formatter="erpPriceTableColumnFormatter" v-if="false"/>
+        <el-table-column label="tempTotalMileage" align="center" prop="tempTotalMileage" :formatter="erpPriceTableColumnFormatter" v-if="false"/>
         <el-table-column :label="t('bomList.bomNode')" align="center" prop="name" />
         <el-table-column :label="t('main.mileage')" key="mileageRule" width="80">
           <template #default="scope">
@@ -68,6 +78,7 @@
               v-model="scope.row.mileageRule"
               :active-value="0"
               :inactive-value="1"
+              @change="handleRuleChange(scope.row, 'mileage')"
             />
           </template>
         </el-table-column>
@@ -77,6 +88,7 @@
               v-model="scope.row.runningTimeRule"
               :active-value="0"
               :inactive-value="1"
+              @change="handleRuleChange(scope.row, 'runningTime')"
             />
           </template>
         </el-table-column>
@@ -120,6 +132,7 @@
 
       <!-- 添加分页组件 -->
       <el-pagination
+        :key="`pagination-${list.length}-${currentPage}`"
         style="margin-top: 20px; justify-content: flex-end"
         :total="list.length"
         v-model:current-page="currentPage"
@@ -166,7 +179,7 @@
             :min="0"
             controls-position="right"
             :controls="false"
-            style="width: 60%"
+            style="width: 80%"
           />
         </el-form-item>
         <!-- 运行时间配置 -->
@@ -181,7 +194,7 @@
             :min="0"
             controls-position="right"
             :controls="false"
-            style="width: 60%"
+            style="width: 80%"
           />
         </el-form-item>
         <!-- 自然日期配置 -->
@@ -196,7 +209,7 @@
             placeholder="选择日期"
             format="YYYY-MM-DD"
             value-format="YYYY-MM-DD"
-            style="width: 60%"
+            style="width: 80%"
           />
         </el-form-item>
       </div>
@@ -215,7 +228,7 @@
             :min="0"
             controls-position="right"
             :controls="false"
-            style="width: 60%"
+            style="width: 80%"
           />
         </el-form-item>
         <el-form-item
@@ -229,7 +242,7 @@
             :min="0"
             controls-position="right"
             :controls="false"
-            style="width: 60%"
+            style="width: 80%"
           />
         </el-form-item>
       </div>
@@ -247,7 +260,7 @@
             :min="0"
             controls-position="right"
             :controls="false"
-            style="width: 60%"
+            style="width: 80%"
           />
         </el-form-item>
         <el-form-item
@@ -261,7 +274,7 @@
             :min="0"
             controls-position="right"
             :controls="false"
-            style="width: 60%"
+            style="width: 80%"
           />
         </el-form-item>
       </div>
@@ -278,7 +291,7 @@
             :min="0"
             controls-position="right"
             :controls="false"
-            style="width: 60%"
+            style="width: 80%"
           />
         </el-form-item>
         <el-form-item
@@ -291,10 +304,73 @@
             :min="0"
             controls-position="right"
             :controls="false"
-            style="width: 60%"
+            style="width: 80%"
           />
         </el-form-item>
       </div>
+
+      <!-- 运行记录模板中 多个 累计运行时长 累计运行里程 属性匹配-->
+      <div class="form-group"
+           v-if="(configDialog.current?.runningTimeRule === 0 || configDialog.current?.mileageRule === 0)
+            && (configDialog.current?.timeAccumulatedAttrs?.length || configDialog.current?.mileageAccumulatedAttrs?.length)
+            && !configDialog.current.totalRunTime && !configDialog.current.totalMileage" >
+        <div class="group-title">{{ t('mainPlan.accumulatedParams') }}</div>
+        <!-- 累计运行时长 -->
+        <el-form-item
+          v-if="configDialog.current?.runningTimeRule === 0
+          && configDialog.current?.timeAccumulatedAttrs?.length && !configDialog.current.totalRunTime"
+          :label="t('mainPlan.accumulatedRunTime')"
+          prop="accumulatedTimeOption"
+          :rules="[{
+            required: configDialog.current?.runningTimeRule === 0 && configDialog.current?.timeAccumulatedAttrs?.length,
+            message: '请选择累计运行时长',
+            trigger: 'change'
+          }]"
+        >
+          <el-select
+            v-model="configDialog.form.accumulatedTimeOption"
+            placeholder="请选择累计运行时长"
+            style="width: 80%"
+            clearable
+            @change="handleAccumulatedTimeChange"
+          >
+            <el-option
+              v-for="(item, index) in configDialog.current.timeAccumulatedAttrs"
+              :key="`time-${item.pointName}-${index}`"
+              :label="item.pointName"
+              :value="item.pointName"
+            />
+          </el-select>
+        </el-form-item>
+        <!-- 累计运行公里数 -->
+        <el-form-item
+          v-if="configDialog.current?.mileageRule === 0
+          && configDialog.current?.mileageAccumulatedAttrs?.length && !configDialog.current.totalMileage"
+          :label="t('mainPlan.accumulatedMileage')"
+          prop="accumulatedMileageOption"
+          :rules="[{
+            required: configDialog.current?.mileageRule === 0 && configDialog.current?.mileageAccumulatedAttrs?.length,
+            message: '请选择累计运行公里数',
+            trigger: 'change'
+          }]"
+        >
+          <el-select
+            v-model="configDialog.form.accumulatedMileageOption"
+            placeholder="请选择累计运行公里数"
+            style="width: 80%"
+            clearable
+            @change="handleAccumulatedMileageChange"
+          >
+            <el-option
+              v-for="(item, index) in configDialog.current.mileageAccumulatedAttrs"
+              :key="`mileage-${item.pointName}-${index}`"
+              :label="item.pointName"
+              :value="item.pointName"
+            />
+          </el-select>
+        </el-form-item>
+      </div>
+
     </el-form>
     <template #footer>
       <el-button @click="configDialog.visible = false">{{ t('common.cancel') }}</el-button>
@@ -307,7 +383,7 @@
 import { IotDeviceApi, IotDeviceVO } from '@/api/pms/device'
 import * as UserApi from '@/api/system/user'
 import { useUserStore } from '@/store/modules/user'
-import { ref, computed } from 'vue'
+import { ref, computed, nextTick } from 'vue'
 import { IotMaintenanceBomApi, IotMaintenanceBomVO } from '@/api/pms/iotmaintenancebom'
 import { IotMaintenancePlanApi, IotMaintenancePlanVO } from '@/api/pms/maintenance'
 import { useTagsViewStore } from '@/store/modules/tagsView'
@@ -332,7 +408,6 @@ const formLoading = ref(false) // 表单的加载中:1)修改时的数据加
 const formType = ref('') // 表单的类型:create - 新增;update - 修改
 const deviceLabel = ref('') // 表单的类型:create - 新增;update - 修改
 const list = ref<IotMaintenanceBomVO[]>([]) // 设备bom关联列表的数据
-
 const deviceIds = ref<number[]>([]) // 已经选择的设备id数组
 
 // 分页相关变量
@@ -341,10 +416,10 @@ const pageSize = ref(10)
 
 // 计算分页后的数据
 const pagedList = computed(() => {
-  const start = (currentPage.value - 1) * pageSize.value
-  const end = start + pageSize.value
-  return list.value.slice(start, end)
-})
+  const start = (currentPage.value - 1) * pageSize.value;
+  const end = Math.min(start + pageSize.value, list.value.length);
+  return list.value.slice(start, end);
+});
 
 // 处理页码变化
 const handlePageChange = (page: number) => {
@@ -354,7 +429,10 @@ const handlePageChange = (page: number) => {
 // 处理每页数量变化
 const handleSizeChange = (size: number) => {
   pageSize.value = size
-  currentPage.value = 1 // 重置到第一页
+  // currentPage.value = 1 // 重置到第一页
+  // 添加以下两行防止页码越界
+  const maxPage = Math.ceil(list.value.length / size);
+  if (currentPage.value > maxPage) currentPage.value = maxPage;
 }
 
 const { params, name } = useRoute() // 查询参数
@@ -392,7 +470,10 @@ const configDialog = reactive({
     // 提前量
     kiloCycleLead: 0,
     timePeriodLead: 0,
-    naturalDatePeriodLead: 0
+    naturalDatePeriodLead: 0,
+    // 多个累计时长 累计里程 匹配
+    accumulatedTimeOption: null,    // 累计运行时长选项
+    accumulatedMileageOption: null, // 累计运行公里数选项
   }
 })
 
@@ -424,8 +505,21 @@ const openConfigDialog = (row: IotMaintenanceBomVO) => {
     // 提前量
     kiloCycleLead: row.kiloCycleLead || 0,
     timePeriodLead: row.timePeriodLead || 0,
-    naturalDatePeriodLead: row.naturalDatePeriodLead || 0
+    naturalDatePeriodLead: row.naturalDatePeriodLead || 0,
+    // 多个累计时长 累计里程 匹配
+    accumulatedTimeOption: null,    // 累计运行时长选项
+    accumulatedMileageOption: null, // 累计运行公里数选项
   }
+
+  // 初始化累计参数选择
+  configDialog.form.accumulatedTimeOption = row.isRuntimeFromTemp
+    ? row.code
+    : null;
+
+  configDialog.form.accumulatedMileageOption = row.isMileageFromTemp
+    ? row.type
+    : null;
+
   configDialog.visible = true
 }
 
@@ -435,6 +529,21 @@ const saveConfig = () => {
     if (!valid) return
     if (!configDialog.current) return
 
+    // 累计运行时长配置 校验逻辑
+    if (configDialog.current.runningTimeRule === 0 &&
+      configDialog.current.timeAccumulatedAttrs?.length &&
+      !configDialog.form.accumulatedTimeOption) {
+      message.error('请选择累计运行时长');
+      return;
+    }
+    // 累计运行公里数配置 校验逻辑
+    if (configDialog.current.mileageRule === 0 &&
+      configDialog.current.mileageAccumulatedAttrs?.length &&
+      !configDialog.form.accumulatedMileageOption) {
+      message.error('请选择累计运行公里数');
+      return;
+    }
+
     // 动态校验逻辑
     const requiredFields = []
     if (configDialog.current.mileageRule === 0) {
@@ -476,22 +585,125 @@ const saveConfig = () => {
       ? dayjs(configDialog.form.lastNaturalDate).valueOf()
       : null // 改为null而不是0
 
+    const updateData = {
+      ...configDialog.form,
+      lastNaturalDate: finalDate,
+    };
+
+    // 处理累计运行时长
+    // 当规则关闭时,无论是否有选择值,都清除相关数据
+    if (configDialog.current.runningTimeRule !== 0) {
+      configDialog.current.code = null;
+      configDialog.current.totalRunTime = null;
+      configDialog.form.accumulatedTimeOption = null; // 清除选择值
+    } else if (configDialog.form.accumulatedTimeOption) {
+      // 查找选中的累计运行时长项
+      const selectedTimeOption = configDialog.current.timeAccumulatedAttrs?.find(
+        item => item.pointName === configDialog.form.accumulatedTimeOption
+      );
+      if (selectedTimeOption) {
+        configDialog.current.code = selectedTimeOption.pointName;
+        // 优先使用接口值,没有则使用临时值
+        configDialog.current.tempTotalRunTime = selectedTimeOption.totalRunTime;
+        configDialog.current.isRuntimeFromTemp = true;
+        // 只有接口未提供值时才使用临时值
+        if (!configDialog.current.totalRunTime) {
+          // configDialog.current.totalRunTime = selectedTimeOption.totalRunTime;
+        }
+      }
+    }
+
+    if (configDialog.current.mileageRule !== 0) {
+      configDialog.current.type = null;
+      configDialog.current.totalMileage = null;
+      configDialog.form.accumulatedMileageOption = null; // 清除选择值
+    } else if (configDialog.form.accumulatedMileageOption) {
+      // 查找选中的累计运行公里数项
+      const selectedMileageOption = configDialog.current.mileageAccumulatedAttrs?.find(
+        item => item.pointName === configDialog.form.accumulatedMileageOption
+      );
+      if (selectedMileageOption) {
+        configDialog.current.type = selectedMileageOption.pointName;
+        configDialog.current.tempTotalMileage = selectedMileageOption.totalRunTime;
+        configDialog.current.isMileageFromTemp = true;
+        if (!configDialog.current.totalMileage) {
+          // configDialog.current.totalMileage = selectedMileageOption.totalRunTime;
+        }
+      }
+    }
+
     // 更新当前行的数据
-    Object.assign(configDialog.current, {
+    Object.assign(configDialog.current, updateData);
+    /* Object.assign(configDialog.current, {
       ...configDialog.form,
       // Date对象 -> 时间戳
       lastNaturalDate: finalDate,
-    })
+    }) */
     configDialog.visible = false
   })
 }
 
+// 累计运行时长变更
+const handleAccumulatedTimeChange = (option) => {
+}
+
+// 累计运行公里数变更
+const handleAccumulatedMileageChange = (option) => {
+}
+
 const queryParams = reactive({
   deviceIds: undefined,
   planId: id,
   bomFlag: 'b'
 })
 
+// 处理保养规则变化 取消保养规则 时 清空已经设置的相应保养规则数据
+const handleRuleChange = (row: IotMaintenanceBomVO, ruleType: 'mileage' | 'runningTime') => {
+  // 当规则关闭时(inactive-value=1)
+  console.log('执行了保养规则变化事件' + row.totalRunTime + ' - ' + row.totalMileage)
+  // 当前保养项行已经返回了 totalRunTime totalMileage 数据 不需要再清空 累计运行时长 累计公里数
+
+  // 选择完设备匹配了保养项后 不能直接置空 因为可能是正常的累计时长 累计公里数
+    if (ruleType === 'runningTime' && row.runningTimeRule === 1) {
+      // 清除临时来源的值
+      if (row.isRuntimeFromTemp) {
+        row.totalRunTime = null;
+        row.tempTotalRunTime = null;
+        row.code = null;
+        // row.isRuntimeFromTemp = false;
+      }
+      // 强制清除配置对话框中的值(如果打开的是当前行)
+      if (configDialog.current?.deviceId === row.deviceId
+        && configDialog.current?.bomNodeId === row.bomNodeId) {
+        configDialog.form.accumulatedTimeOption = null;
+      }
+    } else if (ruleType === 'mileage' && row.mileageRule === 1) {
+      if (row.isMileageFromTemp) {
+        row.totalMileage = null;
+        row.tempTotalMileage = null;
+        row.type = null;
+        // row.isMileageFromTemp = false;
+      }
+      // 强制清除配置对话框中的值(如果打开的是当前行)
+      if (configDialog.current?.deviceId === row.deviceId &&
+        configDialog.current?.bomNodeId === row.bomNodeId) {
+        configDialog.form.accumulatedMileageOption = null;
+      }
+    }
+
+    // 如果配置对话框打开的是当前行,同步清除对话框中的选择值
+    if (configDialog.visible && configDialog.current &&
+      configDialog.current.deviceId === row.deviceId &&
+      configDialog.current.bomNodeId === row.bomNodeId) {
+
+      if (ruleType === 'runningTime') {
+        configDialog.form.accumulatedTimeOption = null;
+      } else if (ruleType === 'mileage') {
+        configDialog.form.accumulatedMileageOption = null;
+      }
+    }
+}
+
 const deviceChoose = async(selectedDevices) => {
   const newIds = selectedDevices.map(device => device.id)
   deviceIds.value = [...new Set([...deviceIds.value, ...newIds])]
@@ -501,7 +713,7 @@ const deviceChoose = async(selectedDevices) => {
   queryParams.deviceIds = JSON.parse(JSON.stringify(params.deviceIds))
   queryParams.bomFlag = 'b'
   // 根据选择的设备筛选出设备BOM中与保养相关的节点项
-  const res = await IotDeviceApi.deviceAssociateBomList(queryParams)
+    const res = await IotDeviceApi.deviceAssociateBomList(queryParams)
   const rawData = res || []
   if(rawData.length === 0){
     message.error('选择的设备不存在待保养BOM项')
@@ -544,7 +756,15 @@ const deviceChoose = async(selectedDevices) => {
     // 保养规则 提前量
     kiloCycleLead: 0,
     timePeriodLead: 0,
-    naturalDatePeriodLead: 0
+    naturalDatePeriodLead: 0,
+    tempTotalRunTime: null,
+    tempTotalMileage: null,
+    isRuntimeFromTemp: false,
+    isMileageFromTemp: false,
+    // 添加累计时长参数列表 属性
+    timeAccumulatedAttrs: device.timeAccumulatedAttrs || [],
+    // 添加累计里程参数列表 属性
+    mileageAccumulatedAttrs: device.mileageAccumulatedAttrs || []
   }))
   // 获取选择的设备相关的id数组
   newItems.forEach(item => {
@@ -560,8 +780,13 @@ const deviceChoose = async(selectedDevices) => {
     }
   })
 
+  // 数据加载完成后设置初始化标志为 false
+  /* setTimeout(() => {
+    isInitializing.value = false;
+  }, 500); */
+
   // 新增数据后自动跳转到第一页
-  currentPage.value = 1
+  // currentPage.value = 1
 }
 
 const deviceFormRef = ref<InstanceType<typeof MainPlanDeviceList>>()
@@ -585,6 +810,14 @@ const submitForm = async () => {
   // 提交请求
   formLoading.value = true
   try {
+    // 将值为NULL 的保养规则 设置为 1
+    list.value.forEach(item => {
+      // 确保保养规则不为null
+      item.mileageRule = item.mileageRule ?? 1
+      item.runningTimeRule = item.runningTimeRule ?? 1
+      item.naturalDateRule = item.naturalDateRule ?? 1
+    })
+
     // 转换日期格式
     const convertedList = list.value.map(item => ({
       ...item,
@@ -780,9 +1013,13 @@ onMounted(async () => {
     const userInfo = wsCache.get(CACHE_KEY.USER)
     formData.value.responsiblePerson = userInfo.user.id;
   }
+
 })
 const handleDelete = async (str: string) => {
   try {
+    // 1. 记录删除前状态
+    const currentPageBeforeDelete = currentPage.value;
+
     const [deviceIdStr, bomNodeId] = str.split('-')
     const deviceId = parseInt(deviceIdStr)
     // 删除列表项
@@ -797,15 +1034,28 @@ const handleDelete = async (str: string) => {
       deviceIds.value = deviceIds.value.filter(id => id !== deviceId)
     }
     // message.success('移除成功')
+    // 2. 计算新总记录数和总页数
+    const newTotal = list.value.length;
+    const newTotalPages = Math.ceil(newTotal / pageSize.value);
+
+    // 3. 智能页码调整(核心修正)[1,5](@ref)
+    if (newTotal === 0) {
+      currentPage.value = 1; // 无数据时回首页
+    } else {
+      currentPage.value = currentPageBeforeDelete > newTotalPages
+        ? newTotalPages  // 当前页超出范围时跳末页
+        : currentPageBeforeDelete; // 否则保持当前页
+    }
+
   } catch (error) {
     console.error('移除失败:', error)
     message.error('移除失败')
   }
 
   // 检查是否需要调整页码
-  if (list.value.length > 0 && pagedList.value.length === 0) {
-    currentPage.value = Math.max(1, currentPage.value - 1)
-  }
+  // if (list.value.length > 0 && pagedList.value.length === 0) {
+    // currentPage.value = Math.max(1, currentPage.value - 1)
+  // }
 }
 </script>
 <style scoped>

+ 8 - 0
src/views/pms/maintenance/IotMaintenancePlanEdit.vue

@@ -586,6 +586,14 @@ const submitForm = async () => {
   // 提交请求
   formLoading.value = true
   try {
+    // 将值为NULL 的保养规则 设置为 1
+    list.value.forEach(item => {
+      // 确保保养规则不为null
+      item.mileageRule = item.mileageRule ?? 1
+      item.runningTimeRule = item.runningTimeRule ?? 1
+      item.naturalDateRule = item.naturalDateRule ?? 1
+    })
+
     // 转换日期格式
     const convertedList = list.value.map(item => ({
       ...item,