Sfoglia il codice sorgente

feat(pressure2/orderConfirm): 优化管道详情页表格选中交互和缓存逻辑

1.  新增子表选中状态缓存,收起展开时保留选中项
2.  支持点击行任意位置勾选复选框,修复展开收起逻辑
3.  添加表格选中行样式美化
4.  重构选中状态更新逻辑,避免重复触发选择事件
xuzhancheng 1 settimana fa
parent
commit
edfae53338

+ 195 - 61
yudao-ui-admin-vue3/src/views/pressure2/orderConfirm/pipeDetail.vue

@@ -152,26 +152,32 @@
       <!-- 设备列表 -->
       <ContentWrap>
         <el-table
-          v-loading="loading"
-          ref="tableRef"
-          :data="equipList"
-          stripe
-          border
-          @selection-change="handleSelectionChange"
-          @sort-change="handleSortChange"
-          :row-key="(row) => row.id"
-        >
+      v-loading="loading"
+      ref="tableRef"
+      :data="equipList"
+      stripe
+      border
+      @selection-change="handleSelectionChange"
+      @sort-change="handleSortChange"
+      :row-key="(row) => row.id"
+      :row-class-name="getMainRowClassName"
+      @row-click="handleMainRowClick"
+      @expand-change="handleExpandChange"
+      :expand-row-keys="expandRowKeys"
+    >
           <el-table-column type="selection" width="50" fixed="left"/>
-          <el-table-column type="expand" width="1">
+          <el-table-column type="expand" width="50">
             <template #default="props">
               <div class="ml-15px mr-15px">
 
                 <el-table :data="props.row.detailSaveReqVOS" border
-                          :header-cell-style="{background: '#f5f7fa', color: '#606266'}"
-                          :ref="(el) => setDetailTableRef(el, props.row.id)"
-                          @selection-change="(selection) => handleDetailSelectionChange(selection, props.row)"
-
-                >
+            :header-cell-style="{background: '#f5f7fa', color: '#606266'}"
+            :ref="(el) => setDetailTableRef(el, props.row.id)"
+            @selection-change="(selection) => handleDetailSelectionChange(selection, props.row)"
+            :row-class-name="getRowClassName"
+            class="inner-table"
+            @row-click="handleRowClick"
+          >
                   <el-table-column type="selection" width="50" fixed="left"/>
                   <el-table-column label="注册代码" prop="equipCode" min-width="120" show-overflow-tooltip/>
                   <el-table-column label="管道名称" prop="pipeName" min-width="120" show-overflow-tooltip/>
@@ -197,7 +203,7 @@
               <div
                 v-if="row.projectNo"
                 class="cursor-pointer"
-                @click="toggleExpand(row)"
+                @click.stop="toggleExpand(row)"
               >
                 <div class="flex items-center justify-center gap-1 schedule-link">
                   <span class="text-xs color-blue">{{row.projectNo}}</span>
@@ -660,7 +666,7 @@
 </template>
 
 <script setup lang="tsx">
-import { ref, onMounted, computed, watch } from 'vue'
+import { ref, onMounted, computed, watch, nextTick } from 'vue'
 import { useRoute, useRouter } from 'vue-router'
 import { ElMessage, ElMessageBox } from 'element-plus'
 import PipeBatchEditForm from '../planNew/components/PipeBatchEditForm.vue'
@@ -1063,21 +1069,38 @@ const getDetailTableRef = (parentId: string) => {
 }
 
 const handleDetailSelectionChange = (selection: any[], mainRow: any) => {
+  // 如果正在恢复选中状态,不更新缓存(避免展开时 toggleRowSelection 触发此函数覆盖缓存)
+  if (isRestoringSelection.value) {
+    return
+  }
+  
+  const rowId = mainRow.id
+  
+  // 更新缓存中的选中状态(如果缓存不存在则创建)
+  if (!expandedRowsCache.value.has(rowId)) {
+    // 如果该行还未展开,初始化缓存
+    expandedRowsCache.value.set(rowId, {
+      pipes: mainRow.detailSaveReqVOS || [],
+      selectedIds: new Set<string>()
+    })
+  }
+  const cache = expandedRowsCache.value.get(rowId)!
+  cache.selectedIds = new Set(selection.map((item: any) => item.id))
+  
+  // 更新全局选中的子表数据
   selectedDetailRows.value = selectedDetailRows.value.filter((item) => item.equipPipeId !== mainRow.id)
   selectedDetailRows.value = [...selectedDetailRows.value.filter(row => !selection.some(sel => sel.id === row.id)), ...selection]
-  // 过滤掉集合内id相同的
-
-  // tableRef.value.toggleRowSelection(mainRow, true)
+  
   // 如果子表有选中项,则自动选中主表行;否则取消选中主表行
   nextTick(() => {
     if (selection.length > 0) {
       // 子表有选中项,选中主表行
       tableRef.value.toggleRowSelection(mainRow, true);
-    }else if (!selectedDetailRows.value.map(row => row.equipPipeId).includes(mainRow.id)){
+    } else if (!selectedDetailRows.value.map(row => row.equipPipeId).includes(mainRow.id)) {
       const currentlySelected = selectedRows.value.some(row => row.id === mainRow.id);
       if (currentlySelected && selection.length === 0) {
         // 只有当主行当前是选中状态且子表没有任何选中项时才取消主行选择
-        tableRef.value.toggleRowSelection(mainRow,false);
+        tableRef.value.toggleRowSelection(mainRow, false);
       }
     }
   });
@@ -1089,48 +1112,132 @@ const handleSelectionChange = async (selection: any[]) => {
     item => !selection.includes(item)
   );
   selectedRows.value = selection
-  // 每一个勾选了主表的要找到对应的子表
-  for (const item of selection) {
-    // 如果没有子表,展开当前子表
-    if (!expandRowKeys.value.includes(item.id)) {
-      await toggleExpand(item)
-    }
-    // 如果没有勾选,勾选子表
-    if (selectedDetailRows.value.findIndex(selectedDetailRow => selectedDetailRow.equipPipeId === item.id) === -1){
-      getDetailTableRef(item.id)?.toggleAllSelection(true)
+  
+  // 清除取消选中行的子表选择(包括展开的和收起的)
+  deselectedRows.forEach(item => {
+    const rowId = item.id
+    // 如果该行是展开状态,直接清除表格选中
+    const tableRef = getDetailTableRef(rowId)
+    if (tableRef) {
+      tableRef.clearSelection()
     }
-  }
-  equipList.value.forEach(item => {
-    if (selection.length==0|| !selection.map(selectedDetailRow => selectedDetailRow.id).includes(item.id)){
-      getDetailTableRef(item.id)?.clearSelection()
+    
+    // 无论是否展开,都清除缓存中的选中状态
+    if (expandedRowsCache.value.has(rowId)) {
+      expandedRowsCache.value.get(rowId)!.selectedIds.clear()
     }
-  })
-  deselectedRows.forEach(item => {
-    getDetailTableRef(item.id)?.clearSelection()
+    
+    // 从全局选中的子表数据中移除
+    selectedDetailRows.value = selectedDetailRows.value.filter(
+      detailRow => detailRow.equipPipeId !== rowId
+    )
   })
 }
 
+// 标志位:防止展开时恢复选中状态触发 handleDetailSelectionChange 覆盖缓存
+const isRestoringSelection = ref(false)
+
+// 已展开行的 key 列表
 const expandRowKeys = ref<string[]>([])
-const expandRows = ref([])
+// 缓存展开行的子表数据和选中状态
+const expandedRowsCache = ref<Map<string, { pipes: any[], selectedIds: Set<string> }>>(new Map())
+
+const getMainRowClassName = ({ row }) => {
+  const isSelected = tableRef.value?.getSelectionRows().some((item) =>
+    item.id === row.id
+  )
+  return isSelected ? 'selected-row' : ''
+}
+
+const getRowClassName = ({row}) => {
+  // 找到当前行所在的分组,从 Map 中获取对应的表格实例
+  const tableRef = getDetailTableRef(row.equipPipeId)
+  if (tableRef) {
+    const isSelected = tableRef.getSelectionRows().some((item) =>
+      item.id === row.id
+    )
+    return isSelected ? 'selected-row' : ''
+  }
+
+  return ''
+}
+
+const handleExpandChange = (row, expandedRows: any[]) => {
+  const rowId = row.id
+  const isExpanded = expandedRows.some((r) => r.id === rowId)
+  
+  if (isExpanded) {
+    // 展开时,从缓存中恢复或初始化
+    if (!expandedRowsCache.value.has(rowId)) {
+      // 首次展开,初始化缓存
+      expandedRowsCache.value.set(rowId, {
+        pipes: row.detailSaveReqVOS || [],
+        selectedIds: new Set<string>()
+      })
+    }
+    // 恢复选中状态
+    nextTick(() => {
+      const cache = expandedRowsCache.value.get(rowId)
+      const tableRef = getDetailTableRef(rowId)
+      if (cache && tableRef && cache.pipes.length > 0) {
+        // 设置标志位,防止触发 handleDetailSelectionChange
+        isRestoringSelection.value = true
+        
+        cache.pipes.forEach((pipe) => {
+          if (cache.selectedIds.has(pipe.id)) {
+            tableRef.toggleRowSelection(pipe, true)
+          }
+        })
+        
+        // 恢复完成后,延迟重置标志位
+        nextTick(() => {
+          isRestoringSelection.value = false
+        })
+      }
+    })
+  } else {
+    // 收起时,保存当前选中状态
+    const tableRef = getDetailTableRef(rowId)
+    if (tableRef) {
+      const currentSelection = tableRef.getSelectionRows()
+      const selectedIds = new Set(currentSelection.map((item: any) => item.id))
+      expandedRowsCache.value.set(rowId, {
+        pipes: row.detailSaveReqVOS || [],
+        selectedIds: selectedIds
+      })
+    }
+  }
+  
+  // 更新展开的行 key 列表
+  expandRowKeys.value = expandedRows.map((r) => r.id)
+}
+
+/** 处理行点击勾选 */
+const handleMainRowClick = (row, _event,_column) => {
+  tableRef.value?.toggleRowSelection(row)
+}
+
+const handleRowClick = (row, _event, column) => {
+  // 如果点击的是选择列,不处理
+  if (column.type === 'selection') {
+    return
+  }
+  // 找到当前行所在的分组,从 Map 中获取对应的表格实例
+  const tableRef = getDetailTableRef(row.equipPipeId)
+  if (tableRef && tableRef.toggleRowSelection) {
+    tableRef.toggleRowSelection(row)
+  }
+}
+
 const toggleExpand = async (row: any) => {
   const key = row.id
-  // 第一次点击展开,第二次点击同一行收起
-  if (expandRowKeys.value.includes(key)) {
-    expandRowKeys.value = expandRowKeys.value.filter((key) => key !== key)
-    expandRows.value = expandRows.value.filter((row) => row.id !== key)
-    selectedDetailRows.value = selectedDetailRows.value.filter((row) => row.pipeEquipmentId !== key)
-    tableRef.value.toggleRowExpansion(row, false)
-    tableRef.value.toggleRowSelection(row, false)
-  } else {
-    expandRowKeys.value.push(key)
-    expandRows.value.push(row)
-    tableRef.value.toggleRowExpansion(row, true)
+  if (!expandRowKeys.value.includes(key)) {
     await PipeAppointmentConfirmOrderApi.getPipeEquipmentDetailListByPipeEquipmentId(orderDetail.value.id, key).then((res) => {
       let find = equipList.value.find(item => item.id === key);
       find.detailSaveReqVOS = res.list
     })
   }
-
+  tableRef.value.toggleRowExpansion(row)
 }
 
 const handleSortChange = ({ prop, order }) => {
@@ -1183,15 +1290,11 @@ const handleEquipQuery = async (isUpdateContact: boolean = false) => {
       }
     })
 
-    expandRows.value.forEach(row => {
-      tableRef.value?.toggleRowExpansion(row, false)
-    })
-    // 清空展开行记录
+    // 重置展开状态、选中状态和缓存
     expandRowKeys.value = []
-    expandRows.value = []
-
-    selectedRows.value = []
     selectedDetailRows.value = []
+    selectedRows.value = []
+    expandedRowsCache.value.clear()
 
     // 调用后端API获取数据
     const res = await PipeAppointmentConfirmOrderApi.getAppointmentConfirmOrderEquipmentList(params)
@@ -2006,9 +2109,7 @@ const formattedCheckers = computed(() => {
   }
 }
 
-:deep(.el-table__expand-icon) {
-  display: none !important;
-}
+
 
 .reject-dialog {
   :deep(.el-dialog__header) {
@@ -2070,4 +2171,37 @@ const formattedCheckers = computed(() => {
     }
   }
 }
+
+// 内层表格复选框样式
+::v-deep .inner-table .el-checkbox__input.is-checked .el-checkbox__inner,
+::v-deep .inner-table .el-checkbox__input.is-indeterminate .el-checkbox__inner {
+  background-color: #67c23a; // 绿色
+  border-color: #67c23a;
+}
+
+::v-deep .inner-table .el-checkbox__input.is-checked .el-checkbox__inner::after {
+  border-color: #fff;
+}
+
+// 内层表格选中行背景色
+::v-deep .inner-table .el-table__body tr.current-row > td {
+  background-color: #f0f9eb;
+}
+
+:deep(.selected-row){
+  background-color: #ecf5ff !important;
+}
+
+// 子表选中行特殊背景色(浅绿色)
+:deep(.inner-table .selected-row) {
+  background-color: #e8f5e9 !important;
+}
+
+.schedule-link {
+  color: var(--el-color-primary);
+
+  &:hover {
+    color: var(--el-color-primary-light-3);
+  }
+}
 </style>