ソースを参照

pms 保养工单填报 保养项名称 与 保养项组分开 显示

zhangcl 2 週間 前
コミット
7a7bd66252
1 ファイル変更190 行追加29 行削除
  1. 190 29
      src/views/pms/iotmainworkorder/IotMainWorkOrder.vue

+ 190 - 29
src/views/pms/iotmainworkorder/IotMainWorkOrder.vue

@@ -89,8 +89,9 @@
   <ContentWrap>
     <!-- 列表 -->
     <ContentWrap>
-      <el-table v-loading="loading" :data="paginatedList" :stripe="true" :show-overflow-tooltip="true" :header-cell-style="tableHeaderStyle">
-        <!-- 添加序号列 -->
+      <el-table v-loading="loading" :data="paginatedList" :stripe="true"
+                :show-overflow-tooltip="true" :header-cell-style="tableHeaderStyle" :span-method="handleSpanMethod">
+        <!-- 序号列 -->
         <el-table-column
           type="index"
           :label="t('maintain.serial')"
@@ -122,6 +123,7 @@
           prop="group"
           fixed="left"
           :width="columnWidths.group"
+          :cell-class-name="groupCellClassName"
         >
           <template #default="{ row }">
             <div class="full-content-cell">
@@ -132,8 +134,9 @@
         <el-table-column :label="t('mainPlan.MaintItems')" align="center" prop="name"
                          :show-overflow-tooltip="false" :width="columnWidths.name" fixed="left">
           <template #default="{ row }">
-            <div class="full-content-cell"> <!-- 自定义样式 -->
-              {{ row.name }}
+            <div class="full-content-cell">
+              <!-- 保养项 只显示'->'后的内容 -->
+              {{ formatMaintItemName(row.name) }}
             </div>
           </template>
         </el-table-column>
@@ -693,7 +696,6 @@ const materialList = ref<IotMainWorkOrderBomMaterialVO[]>([]) // 保养工单bom
 const deviceIds = ref<number[]>([]) // 已经选择的设备id数组
 const { params, name } = useRoute() // 查询参数
 const id = params.id
-const devicePersonsMap = ref<Map<number, Set<string>>>(new Map()) // 存储设备-责任人映射
 // 控制抽屉额外列的显示
 const hideExtraColumnsInDrawer = ref(false)
 
@@ -709,8 +711,14 @@ const maintItemsWidth = ref('auto')
 const lastNaturalDateWatchers = ref(new Map())
 
 const columnWidths = ref<Record<string, string>>({});
-// 响应式变量
-const currentPageSpanArr = ref<number[]>([])
+
+// 计算当前页是否有分组数据
+const hasGroupInCurrentPage = computed(() => {
+  return paginatedList.value.some(item => item.group && item.group !== '');
+});
+
+// 分组合并计算逻辑
+const groupSpans = ref<Record<string, { span: number, index: number }>>({})
 
 const formData = ref({
   id: undefined,
@@ -868,6 +876,104 @@ const openConfigDialog = (row: IotMainWorkOrderBomVO) => {
   configDialog.visible = true
 }
 
+// 格式化 保养项名称 方法 只显示 -> 后面的内容
+const formatMaintItemName = (name: string) => {
+  if (!name) return '';
+
+  // 包含'->'时只取后半部分
+  if (name.includes('->')) {
+    return name.split('->').pop()?.trim() || name;
+  }
+
+  // 不含'->'时显示完整内容
+  return name;
+};
+
+// 行合并方法 (优化后符合图片效果)
+const handleSpanMethod = ({ row, column, rowIndex, columnIndex }) => {
+  // 只处理保养项组列
+  if (column.property === 'group' && row.group) {
+    const groupKey = `${currentPage.value}-${row.group}`;
+    const spanInfo = groupSpans.value[groupKey];
+
+    if (spanInfo && rowIndex === spanInfo.index) {
+      return {
+        rowspan: spanInfo.span,
+        colspan: 1
+      };
+    } else {
+      return {
+        rowspan: 0,
+        colspan: 0
+      };
+    }
+  }
+
+  // 其他列不合并
+  return {
+    rowspan: 1,
+    colspan: 1
+  };
+};
+
+// 计算分组合并信息 (优化后符合图片效果)
+const calculateGroupSpans = () => {
+  // 重置分组信息
+  groupSpans.value = {};
+
+  const pageKey = currentPage.value;
+  let currentGroup = '';
+  let groupStartIndex = 0;
+  let groupCount = 0;
+
+  // 先清除所有行的分组标记
+  paginatedList.value.forEach(item => {
+    delete item.isGroupFirstRow;
+  });
+
+  paginatedList.value.forEach((item, index) => {
+    // 获取当前行的分组(从name中提取)
+    const group = item.name && item.name.includes('->')
+      ? item.name.split('->')[0].trim()
+      : '';
+
+    // 如果分组变化
+    if (group !== currentGroup) {
+      // 保存上一个分组的信息
+      if (currentGroup && groupCount > 0) {
+        const groupKey = `${pageKey}-${currentGroup}`;
+        groupSpans.value[groupKey] = {
+          span: groupCount,
+          index: groupStartIndex
+        };
+
+        // 标记上一个分组的起始行(添加这行)
+        paginatedList.value[groupStartIndex].isGroupFirstRow = true;
+      }
+
+      // 开始新分组
+      currentGroup = group;
+      groupStartIndex = index;
+      groupCount = 1;
+    } else {
+      // 相同分组,计数增加
+      groupCount++;
+    }
+  });
+
+  // 保存最后一个分组的信息
+  if (currentGroup && groupCount > 0) {
+    const groupKey = `${pageKey}-${currentGroup}`;
+    groupSpans.value[groupKey] = {
+      span: groupCount,
+      index: groupStartIndex
+    };
+
+    // 标记最后一个分组的起始行(添加这行)
+    paginatedList.value[groupStartIndex].isGroupFirstRow = true;
+  }
+};
+
 // 运行时间周期 单行校验方法
 const validateRunningTime = (row: IotMainWorkOrderBomVO) => {
   if (row.runningTimeRule === 0 && (!row.nextRunningTime || row.nextRunningTime <= 0)) {
@@ -1095,6 +1201,14 @@ const close = () => {
   push({ name: 'IotMainWorkOrder', params:{}})
 }
 
+// 分组单元格类名方法
+const groupCellClassName = ({ row, column }) => {
+  if (column.property === 'group' && row.isGroupFirstRow) {
+    return 'group-first-row';
+  }
+  return '';
+};
+
 /** 提交表单 */
 const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
 const submitForm = async () => {
@@ -1187,12 +1301,15 @@ const configFormRules = reactive({
 
 // 计算文本宽度的辅助函数
 const getTextWidth = (text: string, fontSize = 14): number => {
+  if (!text) return 0;
+
   const span = document.createElement('span')
   span.style.visibility = 'hidden'
   span.style.position = 'absolute'
   span.style.whiteSpace = 'nowrap'
-  span.style.fontSize = '14px' // 与表格实际字体一致
+  span.style.fontSize = `${fontSize}px` // 与表格实际字体一致
   span.style.fontFamily = 'inherit'
+  span.style.fontWeight = 'normal';
   span.innerText = text
   document.body.appendChild(span)
   const width = span.offsetWidth
@@ -1358,7 +1475,7 @@ const calculateAllColumnsWidth = () => {
   const MIN_WIDTH = 70; // 最小列宽
   const PADDING = 10; // 列内边距
   const FIXED_COLUMN_PADDING = 10;  // 固定列额外内边距
-  const GROUP_COLUMN_EXTRA = 20; // 分组列额外宽度
+  const GROUP_COLUMN_EXTRA = 10; // 分组列额外宽度
 
   // 需要自适应的列配置
   const autoColumns = [
@@ -1418,8 +1535,9 @@ const calculateAllColumnsWidth = () => {
     }
     // 取最大值并添加内边距
     let finalWidth = Math.max(headerWidth, contentMaxWidth, MIN_WIDTH) + PADDING;
-    // 为分组列增加额外宽度(重点修改)
+    // 为分组列增加额外宽度
     if ([
+      'group',
       'lastRunningKilometers',
       'nextMaintenanceKm',
       'remainKm',
@@ -1485,6 +1603,13 @@ watch([paginatedList, hasMileageRuleInCurrentPage, hasTimeRuleInCurrentPage, has
   });
 });
 
+// 监听分页变化时重新计算分组信息
+watch([paginatedList, currentPage], () => {
+  calculateGroupSpans();
+  calculateAllColumnsWidth();
+  tableRef.value?.doLayout(); // 确保表格重新布局
+});
+
 // 下拉菜单命令处理
 const handleDropdownCommand = (command: { action: string; row: IotMainWorkOrderBomVO }) => {
   switch (command.action) {
@@ -1631,24 +1756,24 @@ onMounted(async () => {
     list.value = []
     if (Array.isArray(data)) {
       list.value = data.map(item => {
-        // 提取分组名称
-        const group = item.name && item.name.includes('->')
-          ? item.name.split('->')[0].trim()
-          : '';
-
-        if (item.mileageRule === 0) {
-          item.nextMaintenanceKm = calculateNextMaintenanceKm(item);
-          item.remainKm = calculateRemainKm(item);
-        }
-        if (item.runningTimeRule === 0) {
-          item.nextMaintenanceH = calculateNextMaintenanceH(item);
-          item.remainH = calculateRemainH(item);
-        }
-        if (item.naturalDateRule === 0) {
-          item.nextMaintenanceDate = calculateNextMaintenanceDate(item);
-          item.remainDay = calculateRemainDay(item);
-        }
-        setupNaturalDateSync(item);
+          // 提取分组名称
+          const group = item.name && item.name.includes('->')
+            ? item.name.split('->')[0].trim()
+            : '';
+
+          if (item.mileageRule === 0) {
+            item.nextMaintenanceKm = calculateNextMaintenanceKm(item);
+            item.remainKm = calculateRemainKm(item);
+          }
+          if (item.runningTimeRule === 0) {
+            item.nextMaintenanceH = calculateNextMaintenanceH(item);
+            item.remainH = calculateRemainH(item);
+          }
+          if (item.naturalDateRule === 0) {
+            item.nextMaintenanceDate = calculateNextMaintenanceDate(item);
+            item.remainDay = calculateRemainDay(item);
+          }
+          setupNaturalDateSync(item);
           return {
             ...item,
             group,
@@ -1679,6 +1804,9 @@ onMounted(async () => {
     calculateAllColumnsWidth()
     window.addEventListener('resize', calculateAllColumnsWidth);
   })
+
+  // 初始化 保养项 分组信息
+  calculateGroupSpans();
 })
 
 onUnmounted(async () => {
@@ -1739,7 +1867,7 @@ onUnmounted(async () => {
   overflow: visible;   /* 允许内容溢出单元格 */
 }
 
-/* 新增分组表头样式 */
+/* 分组表头样式 */
 :deep(.el-table__header) {
   border: 1px solid #dcdfe6 !important;
 }
@@ -1761,4 +1889,37 @@ onUnmounted(async () => {
 :deep(.el-table__header .el-table__cell:not(.is-group)) {
   border-top: 1px solid #dcdfe6 !important; /* 添加顶部边框连接分组标题 */
 }
+
+/* 分组行样式 - 符合图片效果 */
+:deep(.el-table .group-row) {
+  background-color: #f8f8f9; /* 轻微的背景色区分 */
+}
+
+/* 分组单元格样式 */
+:deep(.el-table .group-cell) {
+  font-weight: 600; /* 加粗字体 */
+  vertical-align: middle; /* 垂直居中 */
+}
+
+/* 分组第一行单元格样式 - 添加底部边框 */
+:deep(.el-table .group-first-row) {
+  position: relative;
+}
+
+/* 使用伪元素创建更明显的底部边框 */
+:deep(.el-table .group-first-row::after) {
+  content: '';
+  position: absolute;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  height: 2px;
+  background-color: #606266; /* 深灰色边框 */
+  z-index: 1;
+}
+
+/* 调整分组行的高度,使边框更明显 */
+:deep(.el-table .el-table__row.group-first-row) {
+  border-bottom: none; /* 移除默认边框 */
+}
 </style>