Browse Source

pms 保养查询明细 样式优化 列宽度自适应

zhangcl 3 weeks ago
parent
commit
f3f4783121
1 changed files with 380 additions and 60 deletions
  1. 380 60
      src/views/pms/iotmainworkorder/DeviceAlarmBomList.vue

+ 380 - 60
src/views/pms/iotmainworkorder/DeviceAlarmBomList.vue

@@ -1,48 +1,85 @@
 <template>
   <Dialog v-model="dialogVisible"
           :title="t('monitor.details')"
-          style="width: 1500px"
+          :width="dialogWidth"
           class="fixed-height-dialog"
           @close="handleClose" >
-      <ContentWrap>
+    <ContentWrap>
+
+      <!-- 添加设备信息展示区域 -->
+      <div v-if="deviceInfo" class="device-info-card">
+        <div class="info-item">
+          <span class="info-label">{{ t('iotDevice.code') }}:</span>
+          <span class="info-value">{{ deviceInfo.deviceCode }}</span>
+        </div>
+        <div class="info-item">
+          <span class="info-label">{{ t('iotDevice.name') }}:</span>
+          <span class="info-value">{{ deviceInfo.deviceName }}</span>
+        </div>
+      </div>
+
+      <div class="table-container">
         <!-- 添加表格容器并设置滚动 -->
         <el-table
           v-loading="loading"
           :data="paginatedList"
           :stripe="true"
           :show-overflow-tooltip="true"
-          style="width: 100%"
+          style="width: auto"
           height="100%"
           class="scrollable-table"
+          ref="tableRef"
         >
-          <el-table-column :label="t('iotDevice.code')" align="center" prop="deviceCode" />
-          <el-table-column :label="t('iotDevice.name')" align="center" prop="deviceName" />
-          <el-table-column :label="t('operationFillForm.sumTime')" align="center" prop="totalRunTime" >
+          <el-table-column :label="t('iotDevice.code')" align="center" prop="deviceCode"
+                           :width="columnWidths.deviceCode" v-if="false"/>
+          <el-table-column :label="t('iotDevice.name')" align="center" prop="deviceName"
+                           :width="columnWidths.deviceName" v-if="false"/>
+          <el-table-column :label="t('mainPlan.MaintItems')" align="center" prop="name"
+                           :width="columnWidths.name"/>
+
+          <el-table-column :label="t('operationFillForm.sumTime')" align="center" prop="totalRunTime"
+                           :width="columnWidths.totalRunTime">
             <template #default="{ row }">
               {{ row.totalRunTime ?? row.tempTotalRunTime }}
             </template>
           </el-table-column>
-          <el-table-column :label="t('operationFillForm.sumKil')" align="center" prop="totalMileage" >
+          <el-table-column :label="t('operationFillForm.sumKil')" align="center" prop="totalMileage"
+                           :width="columnWidths.totalMileage">
             <template #default="{ row }">
               {{ row.totalMileage ?? row.tempTotalMileage }}
             </template>
           </el-table-column>
-          <el-table-column :label="t('mainPlan.MaintItems')" align="center" prop="name" />
-
-          <template v-if="showTimeColumns">
-            <el-table-column :label="t('mainPlan.lastMaintenanceOperationTime')" align="center" prop="lastRunningTime" />
-            <el-table-column :label="t('mainPlan.RunTimeCycle')" align="center" prop="nextRunningTime" />
-            <el-table-column :label="t('mainPlan.nextMaintTime')" align="center" prop="timePeriod" />
-          </template>
-
-          <template v-if="showMileageColumns">
-            <el-table-column :label="t('mainPlan.lastMaintenanceMileage')" align="center" prop="lastRunningKilometers" />
-            <el-table-column :label="t('mainPlan.operatingMileageCycle')" align="center" prop="nextRunningKilometers" />
-            <el-table-column :label="t('mainPlan.nextMaintKil')" align="center" prop="kilometerCycle" />
-          </template>
-
-          <template v-if="showNaturalDateColumns">
-            <el-table-column :label="t('mainPlan.lastMaintenanceOperationTime')" align="center" prop="lastNaturalDate"  width="220">
+
+          <!-- 时间分组列 -->
+          <el-table-column v-if="showTimeColumns" label="保养时长"
+                           align="center"
+                           :width="columnWidths.timeGroup">
+            <el-table-column :label="t('mainPlan.lastMaintenanceOperationTime')" align="center" prop="lastRunningTime"
+                             :width="columnWidths.lastRunningTime"/>
+            <el-table-column :label="t('mainPlan.RunTimeCycle')" align="center" prop="nextRunningTime"
+                             :width="columnWidths.nextRunningTime"/>
+            <el-table-column :label="t('mainPlan.nextMaintTime')" align="center" prop="timePeriod"
+                             :width="columnWidths.timePeriod"/>
+          </el-table-column>
+
+          <!-- 里程分组列 -->
+          <el-table-column v-if="showMileageColumns" label="保养里程"
+                           align="center"
+                           :width="columnWidths.mileageGroup">
+            <el-table-column :label="t('mainPlan.lastMaintenanceMileage')" align="center" prop="lastRunningKilometers"
+                             :width="columnWidths.lastRunningKilometers"/>
+            <el-table-column :label="t('mainPlan.operatingMileageCycle')" align="center" prop="nextRunningKilometers"
+                             :width="columnWidths.nextRunningKilometers"/>
+            <el-table-column :label="t('mainPlan.nextMaintKil')" align="center" prop="kilometerCycle"
+                             :width="columnWidths.kilometerCycle"/>
+          </el-table-column>
+
+          <!-- 日期分组列 -->
+          <el-table-column v-if="showNaturalDateColumns" label="保养日期"
+                           align="center"
+                           :width="columnWidths.dateGroup">
+            <el-table-column :label="t('mainPlan.lastMaintenanceOperationTime')" align="center" prop="lastNaturalDate"
+                             :width="columnWidths.lastNaturalDate">
               <template #default="scope">
                 <el-date-picker
                   v-model="scope.row.lastNaturalDate"
@@ -50,22 +87,25 @@
                   placeholder="选择日期"
                   format="YYYY-MM-DD"
                   value-format="YYYY-MM-DD"
-                  style="width: 60%"
+                  style="width: 100%"
                   :disabled="true"
                 />
               </template>
             </el-table-column>
-            <el-table-column :label="t('mainPlan.NaturalDailyCycle')" align="center" prop="nextNaturalDate" />
-            <el-table-column :label="t('mainPlan.nextMaintDate')" align="center" prop="naturalDatePeriod" />
-          </template>
+            <el-table-column :label="t('mainPlan.NaturalDailyCycle')" align="center" prop="nextNaturalDate"
+                             :width="columnWidths.nextNaturalDate"/>
+            <el-table-column :label="t('mainPlan.nextMaintDate')" align="center" prop="naturalDatePeriod"
+                             :width="columnWidths.naturalDatePeriod"/>
+          </el-table-column>
         </el-table>
-        <Pagination
-          :total="total"
-          v-model:page="queryParams.pageNo"
-          v-model:limit="queryParams.pageSize"
-          @pagination="handlePagination"
-        />
-      </ContentWrap>
+      </div>
+      <Pagination
+        :total="total"
+        v-model:page="queryParams.pageNo"
+        v-model:limit="queryParams.pageSize"
+        @pagination="handlePagination"
+      />
+    </ContentWrap>
   </Dialog>
 </template>
 
@@ -82,6 +122,12 @@ const loading = ref(true) // 列表的加载中
 const queryFormRef = ref() // 搜索的表单
 const list = ref<IotMaintenanceBomVO[]>([]) // 列表的数据
 const total = ref(0) // 列表的总页数
+
+const dialogWidth = '1500px';
+
+const tableRef = ref(null)    // 表格实例引用
+const columnWidths = ref({}) // 存储列宽的对象
+
 const queryParams = reactive({
   pageNo: 1,
   pageSize: 10,
@@ -96,6 +142,64 @@ const props = defineProps({
 
 const selectedRow = ref(null)
 
+const getTextWidth = (str: string) => {
+  if (!str) return 0;
+  if (widthCache.has(str)) return widthCache.get(str)!;
+
+  const span = document.createElement('span');
+  span.style.visibility = 'hidden';
+  span.style.position = 'absolute';
+  span.style.whiteSpace = 'nowrap';
+  span.style.font = '14px Microsoft YaHei';
+  span.textContent = str;
+
+  document.body.appendChild(span);
+  const width = span.offsetWidth;
+  document.body.removeChild(span);
+
+  widthCache.set(str, width);
+  return width;
+};
+
+// 添加列宽计算函数
+const widthCache = new Map<string, number>();
+
+const flexColumnWidth = (label: string, prop: keyof IotMaintenanceBomVO) => {
+  // 确保有数据时才计算
+  if (!paginatedList.value.length) return "auto";
+  // 基础内边距30px
+  const basePadding = 20;
+  const labelWidth = getActualWidth(label) + basePadding; // 文本宽度 + 内边距
+  // 计算内容最大宽度
+  let maxContentWidth = 0;
+  // 获取该列所有内容的宽度
+  for (const row of paginatedList.value) {
+    let value = "";
+
+    // 特殊列处理
+    if (prop === "totalRunTime") {
+      value = (row.totalRunTime ?? row.tempTotalRunTime)?.toString() || "";
+    } else if (prop === "totalMileage") {
+      value = (row.totalMileage ?? row.tempTotalMileage)?.toString() || "";
+    } else {
+      value = row[prop]?.toString() || "";
+    }
+
+    // 数值格式化
+    if (value && !isNaN(Number(value))) {
+      value = Number(value).toLocaleString();
+    }
+
+    const contentWidth = getActualWidth(value) + basePadding;
+    if (contentWidth > maxContentWidth) {
+      maxContentWidth = contentWidth;
+    }
+  }
+
+  // 返回较大值加上安全边距
+  return Math.max(labelWidth, maxContentWidth, 120) + "px";
+};
+
 // 处理单选逻辑
 const selectRow = (row) => {
   selectedRow.value = selectedRow.value?.id === row.id ? null : row
@@ -108,6 +212,67 @@ const handlePagination = () => {
   console.log("分页变化,当前页:", queryParams.pageNo);
 };
 
+// 改进的宽度计算函数
+const getActualWidth = (text: string) => {
+  if (!text) return 0;
+
+  // 更精准的字符宽度计算
+  const chineseChars = (text.match(/[\u4e00-\u9fa5]/g) || []).length;
+  const otherChars = text.length - chineseChars;
+
+  // 中文16px,英文9px
+  return chineseChars * 16 + otherChars * 9;
+};
+
+// 设置列宽
+const setColumnWidths = () => {
+  const widths: Record<string, string> = {};
+
+  // 固定列
+  // widths.deviceCode = flexColumnWidth(t('iotDevice.code'), 'deviceCode');
+  // widths.deviceName = flexColumnWidth(t('iotDevice.name'), 'deviceName');
+  widths.totalRunTime = flexColumnWidth(t('operationFillForm.sumTime'), 'totalRunTime');
+  widths.totalMileage = flexColumnWidth(t('operationFillForm.sumKil'), 'totalMileage');
+  widths.name = flexColumnWidth(t('mainPlan.MaintItems'), 'name');
+
+  // 动态列
+  if (showTimeColumns.value) {
+    widths.lastRunningTime = flexColumnWidth(t('mainPlan.lastMaintenanceOperationTime'), 'lastRunningTime');
+    widths.nextRunningTime = flexColumnWidth(t('mainPlan.RunTimeCycle'), 'nextRunningTime');
+    widths.timePeriod = flexColumnWidth(t('mainPlan.nextMaintTime'), 'timePeriod');
+    // 分组列宽度 = 子列宽度之和 + 边框补偿
+    widths.timeGroup = `${[
+      parseFloat(widths.lastRunningTime),
+      parseFloat(widths.nextRunningTime),
+      parseFloat(widths.timePeriod)
+    ].reduce((sum, w) => sum + w, 0) + 2}px`;
+  }
+
+  if (showMileageColumns.value) {
+    widths.lastRunningKilometers = flexColumnWidth(t('mainPlan.lastMaintenanceMileage'), 'lastRunningKilometers');
+    widths.nextRunningKilometers = flexColumnWidth(t('mainPlan.operatingMileageCycle'), 'nextRunningKilometers');
+    widths.kilometerCycle = flexColumnWidth(t('mainPlan.nextMaintKil'), 'kilometerCycle');
+    widths.mileageGroup = `${[
+      parseFloat(widths.lastRunningKilometers),
+      parseFloat(widths.nextRunningKilometers),
+      parseFloat(widths.kilometerCycle)
+    ].reduce((sum, w) => sum + w, 0) + 2}px`;
+  }
+
+  if (showNaturalDateColumns.value) {
+    widths.lastNaturalDate = flexColumnWidth(t('mainPlan.lastMaintenanceOperationTime'), 'lastNaturalDate'); // 固定日期选择器的宽度
+    widths.nextNaturalDate = flexColumnWidth(t('mainPlan.NaturalDailyCycle'), 'nextNaturalDate');
+    widths.naturalDatePeriod = flexColumnWidth(t('mainPlan.nextMaintDate'), 'naturalDatePeriod');
+    widths.dateGroup = `${[
+      parseFloat(widths.lastNaturalDate),
+      parseFloat(widths.nextNaturalDate),
+      parseFloat(widths.naturalDatePeriod)
+    ].reduce((sum, w) => sum + w, 0) + 2}px`;
+  }
+
+  columnWidths.value = widths;
+};
+
 // 分页计算属性
 const paginatedList = computed(() => {
   const start = (queryParams.pageNo - 1) * queryParams.pageSize;
@@ -137,7 +302,7 @@ const getWorkOrderList = async () => {
   loading.value = true
   try {
     const data = await IotMainWorkOrderBomApi.getWorkOrderBOMs(queryParams)
-    // 格式化日期字段 - 新增代码
+    // 格式化日期字段
     data.forEach(item => {
       if (item.lastNaturalDate) {
         // 将时间戳转换为 YYYY-MM-DD 格式
@@ -161,10 +326,11 @@ const getWorkOrderList = async () => {
 }
 
 const getPlanList = async () => {
+  widthCache.clear();
   loading.value = true
   try {
     const data = await IotMaintenanceBomApi.getMainPlanBOMs(queryParams)
-    // 格式化日期字段 - 新增代码
+    // 格式化日期字段
     data.forEach(item => {
       if (item.lastNaturalDate) {
         // 将时间戳转换为 YYYY-MM-DD 格式
@@ -184,9 +350,22 @@ const getPlanList = async () => {
     total.value = data.length
   } finally {
     loading.value = false
+    nextTick(setColumnWidths); // 数据加载后计算列宽
   }
 }
 
+// 添加设备信息计算属性
+const deviceInfo = computed(() => {
+  if (list.value.length > 0) {
+    const firstRecord = list.value[0];
+    return {
+      deviceCode: firstRecord.deviceCode,
+      deviceName: firstRecord.deviceName
+    };
+  }
+  return null;
+});
+
 const handleClose = () => {
   // 重置状态避免多个弹窗出现
   dialogVisible.value = false
@@ -235,17 +414,17 @@ const calculateNextNaturalDate = (item: IotMaintenanceBomVO): string => {
   return dayjs(item.lastNaturalDate).add(item.nextNaturalDate, 'day').format('YYYY-MM-DD')
 }
 
-// 新增计算属性:控制时间相关列的显示
+// 计算属性:控制时间相关列的显示
 const showTimeColumns = computed(() => {
   return paginatedList.value.some(item => item.runningTimeRule === 0);
 });
 
-// 新增计算属性:控制里程相关列的显示
+// 计算属性:控制里程相关列的显示
 const showMileageColumns = computed(() => {
   return paginatedList.value.some(item => item.mileageRule === 0);
 });
 
-// 新增计算属性:自然日期相关列的显示
+// 计算属性:自然日期相关列的显示
 const showNaturalDateColumns = computed(() => {
   return paginatedList.value.some(item => item.naturalDateRule === 0);
 });
@@ -265,41 +444,182 @@ const resetQuery = () => {
   handleQuery()
 }
 
+// 监听分页数据变化,重新设置列宽
+watch(paginatedList, () => {
+  nextTick(() => {
+    setColumnWidths();
+  });
+});
+
+// 监听动态列的变化
+watch([showTimeColumns, showMileageColumns, showNaturalDateColumns], () => {
+  nextTick(setColumnWidths);
+});
+
 </script>
 
 <style lang="scss" scoped>
-/* 新版CSS解决方案 */
-.fixed-height-dialog :deep(.el-dialog) {
-  width: 1500px !important; /* 合并原内联样式 */
-  height: 800px !important; /* 关键:固定高度 */
+
+/* 设备信息卡片样式 */
+.device-info-card {
   display: flex;
-  flex-direction: column;
+  flex-wrap: wrap;
+  gap: 24px; /* 项间距 */
+  margin-bottom: 16px;
+  padding: 16px;
+  background-color: #f8f9fa;
+  border-radius: 8px;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
+
+  .info-item {
+    display: flex;
+    align-items: center;
+
+    .info-label {
+      font-weight: 600;
+      color: #606266;
+      margin-right: 8px;
+      white-space: nowrap;
+    }
+
+    .info-value {
+      font-weight: 500;
+      color: #303133;
+      padding: 4px 12px;
+      background: #ffffff;
+      border-radius: 4px;
+      border: 1px solid #ebeef5;
+      min-width: 200px;
+    }
+  }
+}
+
+/* 分组表头样式 */
+:deep(.el-table .el-table__header .el-table-column--group) {
+  background-color: #f5f7fa;
+  font-weight: bold;
+
+  > .cell {
+    font-weight: bold;
+    color: #303133;
+  }
+
+  background-color: #f0f7ff;
+  border: 1px solid #409eff !important;
+  box-shadow: 0 2px 4px rgba(64, 158, 255, 0.2);
+
+  > .cell {
+    font-weight: 700;
+    color: #1d63dc;
+  }
 }
 
-.fixed-height-dialog :deep(.el-dialog__header) {
-  padding: 20px;
-  flex-shrink: 0; /* 防止标题栏压缩 */
+/* 加深所有单元格边框 */
+:deep(.el-table__header th) {
+  border: 1px solid #c0c4cc !important;
 }
 
-.fixed-height-dialog :deep(.el-dialog__body) {
+/* 分组内部单元格特殊样式 */
+:deep(.el-table-column--group .el-table__cell) {
+  border-top: 1px dashed #a0cfff !important;
+}
+
+/* 分组间垂直线 */
+:deep(.el-table-column--group) {
+  position: relative;
+
+  &::after {
+    content: "";
+    position: absolute;
+    right: -1px;
+    top: 0;
+    bottom: 0;
+    width: 2px;
+    background: linear-gradient(to bottom, #409eff, #79bbff);
+  }
+}
+
+/* 新版CSS解决方案 */
+.fixed-height-dialog {
+  :deep(.el-dialog) {
+    width: 1500px !important; /* 固定宽度 */
+    height: 85vh !important; /* 使用视口高度 */
+    display: flex;
+    flex-direction: column;
+    margin-top: 5vh !important; /* 垂直居中 */
+  }
+
+  /* 添加媒体查询,确保在小屏幕上也能完整显示 */
+  @media (max-width: 1500px) {
+    :deep(.el-dialog) {
+      width: 95% !important;
+      max-width: 1500px; /* 仍然限制最大宽度 */
+    }
+  }
+
+  :deep(.el-dialog__header) {
+    padding: 20px;
+    flex-shrink: 0;
+  }
+
+  :deep(.el-dialog__body) {
+    flex: 1;
+    padding: 10px 20px;
+    display: flex;
+    flex-direction: column;
+    overflow: hidden; /* 防止内容溢出 */
+  }
+}
+
+/* 表格容器 */
+.table-container {
   flex: 1;
-  overflow: hidden; /* 隐藏外层滚动 */
-  padding: 10px 20px; /* 适当内边距 */
+  overflow: auto;
+  position: relative;
+
+  .scrollable-table {
+    width: 100%; /* 自适应宽度 */
+
+    :deep(.el-table__header) {
+      th {
+        white-space: nowrap !important; /* 强制表头不换行 */
+        text-overflow: ellipsis;
+        overflow: hidden;
+        padding: 8px 0; /* 增加内边距 */
+      }
+    }
+
+    :deep(.el-table__body) {
+      td {
+        white-space: nowrap !important; /* 强制内容不换行 */
+        text-overflow: ellipsis;
+        overflow: hidden;
+        padding: 8px 0; /* 增加内边距 */
+      }
+    }
+
+    :deep(.el-table__body-wrapper) {
+      overflow-x: hidden !important; /* 隐藏X轴滚动条 */
+    }
+  }
 }
 
-/* 表格滚动核心 */
-.scrollable-table {
-  height: 100% !important;
+/* 分页样式 */
+.pagination-footer {
+  margin-top: 15px;
+  flex-shrink: 0;
+}
 
-  :deep(.el-table__body-wrapper) {
-    overflow-y: auto !important;
-    max-height: calc(100% - 40px); /* 扣除表头高度 */
+/* 响应式处理 - 确保在小屏幕上布局合理 */
+@media (max-width: 1500px) {
+  .fixed-height-dialog :deep(.el-dialog) {
+    width: 95% !important;
+    max-width: 98vw;
   }
 
-  :deep(.el-table__header-wrapper) {
-    position: sticky;
-    top: 0;
-    z-index: 10;
+  .table-container {
+    overflow-x: auto;
   }
 }
+
 </style>