Jelajahi Sumber

pms 任务功能优化 施工队伍选择 项目 任务 列表 宽度自适应

zhangcl 1 bulan lalu
induk
melakukan
ff1ed0ba5b

+ 256 - 77
src/views/pms/iotprojectinfo/index.vue

@@ -71,85 +71,81 @@
   </ContentWrap>
 
   <!-- 列表 -->
-  <ContentWrap>
-    <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
-      <el-table-column :label="t('iotDevice.serial')" width="70" align="center">
-        <template #default="scope">
-          {{ scope.$index + 1 }}
-        </template>
-      </el-table-column>
-      <el-table-column label="客户名称" align="center" prop="manufactureName" />
-      <el-table-column label="合同名称" align="center" prop="contractName" >
-        <template #default="scope">
-          <el-link type="primary" @click="showTaskList(scope.row)">
-            {{ scope.row.contractName }}
-          </el-link>
-        </template>
-      </el-table-column>
-      <el-table-column label="合同编号" align="center" prop="contractCode" />
-      <el-table-column label="工作量" align="center">
-        <el-table-column label="总数" align="center" prop="workloadTotal" />
-        <el-table-column label="完成" align="center" prop="workloadFinish" />
-      </el-table-column>
-      <el-table-column label="合同起止时间" align="center">
+  <ContentWrap ref="tableContainerRef">
+    <div class="table-container">
+      <el-table ref="tableRef" v-loading="loading" :data="list" :stripe="true" style="width: 100%" :cell-style="{padding: '5px'}">
+        <el-table-column :label="t('iotDevice.serial')" :width="columnWidths.serial" align="center" v-if="false">
+          <template #default="scope">
+            {{ scope.$index + 1 }}
+          </template>
+        </el-table-column>
+        <el-table-column label="客户名称" align="left" prop="manufactureName" :width="columnWidths.manufactureName" />
+        <el-table-column label="合同名称" align="center" prop="contractName" :width="columnWidths.contractName" >
+          <template #default="scope">
+            <el-link type="primary" @click="showTaskList(scope.row)">
+              {{ scope.row.contractName }}
+            </el-link>
+          </template>
+        </el-table-column>
+        <el-table-column label="合同编号" align="center" prop="contractCode" :width="columnWidths.contractCode" />
+        <el-table-column label="总工作量" align="center" prop="workloadTotal" :width="columnWidths.workloadTotal" />
+        <el-table-column label="已完成工作量" align="center" prop="workloadFinish" :width="columnWidths.workloadFinish" />
         <el-table-column
-          label="开始时间"
+          label="合同开始时间"
           align="center"
           prop="startTime"
           :formatter="dateFormatter2"
-          width="180px"
+          :width="columnWidths.startTime"
         />
         <el-table-column
-          label="完成时间"
+          label="合同完成时间"
           align="center"
           prop="endTime"
           :formatter="dateFormatter2"
-          width="180px"
+          :width="columnWidths.endTime"
         />
-      </el-table-column>
-<!--      <el-table-column label="施工地点" align="center" prop="location" />
-      <el-table-column label="施工工艺" align="center" prop="technique" />-->
-      <el-table-column :label="t('project.payment')" align="center" prop="payment" min-width="90">
-        <template #default="scope">
-          <dict-tag :type="DICT_TYPE.PMS_PROJECT_SETTLEMENT" :value="scope.row.payment" />
-        </template>
-      </el-table-column>
-      <el-table-column
-        label="创建时间"
-        align="center"
-        prop="createTime"
-        :formatter="dateFormatter"
-        width="180px"
-      />
-      <el-table-column label="操作" align="center" min-width="180px">
-        <template #default="scope">
-          <el-button
-            link
-            type="primary"
-            @click="openForm('update', scope.row.id)"
-            v-hasPermi="['rq:iot-project-info:update']"
-          >
-            编辑
-          </el-button>
-          <el-button
-            link
-            type="primary"
-            @click="assignTask(scope.row)"
-            v-hasPermi="['rq:iot-project-task:create']"
-          >
-            分配任务
-          </el-button>
-          <el-button
-            link
-            type="danger"
-            @click="handleDelete(scope.row.id)"
-            v-hasPermi="['rq:iot-project-info:delete']"
-          >
-            删除
-          </el-button>
-        </template>
-      </el-table-column>
-    </el-table>
+        <el-table-column :label="t('project.payment')" align="center" prop="payment" :width="columnWidths.payment">
+          <template #default="scope">
+            <dict-tag :type="DICT_TYPE.PMS_PROJECT_SETTLEMENT" :value="scope.row.payment" />
+          </template>
+        </el-table-column>
+        <el-table-column
+          label="创建时间"
+          align="center"
+          prop="createTime"
+          :formatter="dateFormatter"
+          :width="columnWidths.createTime"
+        />
+        <el-table-column label="操作" align="center" :width="columnWidths.operation" fixed="right">
+          <template #default="scope">
+            <el-button
+              link
+              type="primary"
+              @click="openForm('update', scope.row.id)"
+              v-hasPermi="['rq:iot-project-info:update']"
+            >
+              编辑
+            </el-button>
+            <el-button
+              link
+              type="primary"
+              @click="assignTask(scope.row)"
+              v-hasPermi="['rq:iot-project-task:create']"
+            >
+              分配任务
+            </el-button>
+            <el-button
+              link
+              type="danger"
+              @click="handleDelete(scope.row.id)"
+              v-hasPermi="['rq:iot-project-info:delete']"
+            >
+              删除
+            </el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
     <!-- 分页 -->
     <Pagination
       :total="total"
@@ -182,12 +178,6 @@
             <dict-tag :type="DICT_TYPE.PMS_PROJECT_WELL_CATEGORY" :value="scope.row.wellCategory" />
           </template>
         </el-table-column>
-        <!--
-        <el-table-column :label="t('project.status')" align="center" prop="status" min-width="70">
-          <template #default="scope">
-            <dict-tag :type="DICT_TYPE.PMS_PROJECT_TASK_SCHEDULE" :value="scope.row.status" />
-          </template>
-        </el-table-column> -->
         <el-table-column :label="t('project.status')" align="center" prop="status" min-width="70">
           <template #default="scope">
             <el-link type="primary" @click="openTimelineDialog(scope.row)">
@@ -279,8 +269,7 @@ import * as DeptApi from "@/api/system/dept"; // 引入部门API
 import { handleTree } from "@/utils/tree"; // 引入树形处理工具
 import { IotProjectTaskScheduleApi } from '@/api/pms/iotprojecttaskschedule'
 import dayjs from 'dayjs' // 引入 dayjs 用于时间格式化
-import { ref, reactive, onMounted, computed, nextTick } from 'vue'
-
+import { ref, reactive, onMounted, computed, nextTick, watch } from 'vue'
 
 /** 项目信息 列表 */
 defineOptions({ name: 'iotProjectInfo' })
@@ -304,6 +293,11 @@ const currentTaskRow = ref<any>(null) // 当前选中的任务行数据
 const stepsData = ref<Array<{title: string, description?: string, status?: string}>>([]) // 步骤数据
 const currentStepIndex = ref(0) // 当前步骤索引
 
+// 表格引用
+const tableRef = ref()
+// 表格容器引用
+const tableContainerRef = ref()
+
 const loading = ref(true) // 列表的加载中
 const list = ref<IotProjectInfoVO[]>([]) // 列表的数据
 const total = ref(0) // 列表的总页数
@@ -328,6 +322,39 @@ const queryParams = reactive({
 const queryFormRef = ref() // 搜索的表单
 const exportLoading = ref(false) // 导出的加载中
 const { push } = useRouter() // 路由跳转
+
+// 列宽度配置
+const columnWidths = ref({
+  serial: '50px',
+  manufactureName: '120px',
+  contractName: '150px',
+  contractCode: '120px',
+  workloadTotal: '100px',
+  workloadFinish: '120px',
+  startTime: '150px',
+  endTime: '150px',
+  payment: '100px',
+  createTime: '150px',
+  operation: '200px'
+})
+
+// 计算文本宽度
+const getTextWidth = (text: string, fontSize = 14) => {
+  const span = document.createElement('span');
+  span.style.visibility = 'hidden';
+  span.style.position = 'absolute';
+  span.style.whiteSpace = 'nowrap';
+  span.style.fontSize = `${fontSize}px`;
+  span.style.fontFamily = 'inherit';
+  span.innerText = text;
+
+  document.body.appendChild(span);
+  const width = span.offsetWidth;
+  document.body.removeChild(span);
+
+  return width;
+};
+
 /** 查询列表 */
 const getList = async () => {
   loading.value = true
@@ -342,6 +369,8 @@ const getList = async () => {
       // 使用 nextTick 确保 DOM 更新后再执行选中操作
       nextTick(() => {
         showTaskList(list.value[0]);
+        // 计算列宽
+        calculateColumnWidths();
       });
     } else {
       // 如果没有数据,确保任务列表区域关闭
@@ -353,6 +382,90 @@ const getList = async () => {
   }
 }
 
+// 计算列宽度
+const calculateColumnWidths = () => {
+  const MIN_WIDTH = 80; // 最小列宽
+  const PADDING = 25; // 列内边距
+  const FLEXIBLE_COLUMNS = ['manufactureName', 'contractName', 'contractCode']; // 可伸缩列
+
+  // 确保表格容器存在
+  if (!tableContainerRef.value?.$el) return;
+
+  const container = tableContainerRef.value.$el;
+  const containerWidth = container.clientWidth;
+
+  // 1. 计算所有列的最小宽度
+  const minWidths: Record<string, number> = {};
+  let totalMinWidth = 0;
+
+  // 计算列最小宽度的函数
+  const calculateColumnMinWidth = (key: string, label: string, getValue: Function) => {
+    const headerWidth = getTextWidth(label) * 1.2;
+    let contentMaxWidth = 0;
+
+    // 计算内容最大宽度
+    list.value.forEach((row, index) => {
+      const text = String(getValue ? getValue(row, index) : (row[key] || ''));
+      const textWidth = getTextWidth(text);
+      if (textWidth > contentMaxWidth) contentMaxWidth = textWidth;
+    });
+
+    const minWidth = Math.max(headerWidth, contentMaxWidth, MIN_WIDTH) + PADDING;
+    minWidths[key] = minWidth;
+    totalMinWidth += minWidth;
+    return minWidth;
+  };
+
+  // 计算各列最小宽度
+  calculateColumnMinWidth('serial', t('iotDevice.serial'), (row: any, index: number) => `${index + 1}`);
+  calculateColumnMinWidth('manufactureName', '客户名称', (row: any) => row.manufactureName);
+  calculateColumnMinWidth('contractName', '合同名称', (row: any) => row.contractName);
+  calculateColumnMinWidth('contractCode', '合同编号', (row: any) => row.contractCode);
+  calculateColumnMinWidth('workloadTotal', '总工作量', (row: any) => row.workloadTotal);
+  calculateColumnMinWidth('workloadFinish', '已完成工作量', (row: any) => row.workloadFinish);
+  calculateColumnMinWidth('startTime', '合同开始时间', (row: any) => dateFormatter2(null, null, row.startTime));
+  calculateColumnMinWidth('endTime', '合同完成时间', (row: any) => dateFormatter2(null, null, row.endTime));
+  calculateColumnMinWidth('payment', t('project.payment'), (row: any) => {
+    const dict = getIntDictOptions(DICT_TYPE.PMS_PROJECT_SETTLEMENT)
+      .find(d => d.value === row.payment);
+    return dict ? dict.label : '';
+  });
+  calculateColumnMinWidth('createTime', '创建时间', (row: any) => dateFormatter(null, null, row.createTime));
+
+  // 操作列固定宽度
+  minWidths.operation = 200;
+  totalMinWidth += 200;
+
+  // 2. 计算可伸缩列最终宽度
+  const newWidths: Record<string, string> = {};
+  const availableWidth = containerWidth - 17; // 减去滚动条宽度
+
+  // 应用最小宽度到所有列
+  Object.keys(minWidths).forEach(key => {
+    newWidths[key] = `${minWidths[key]}px`;
+  });
+
+  // 计算可伸缩列需要的宽度
+  if (totalMinWidth < availableWidth) {
+    // 有剩余空间:按比例分配给可伸缩列
+    const extraSpace = availableWidth - totalMinWidth;
+    const flexibleColumnCount = FLEXIBLE_COLUMNS.length;
+    const spacePerColumn = Math.floor(extraSpace / flexibleColumnCount);
+
+    FLEXIBLE_COLUMNS.forEach(key => {
+      newWidths[key] = `${minWidths[key] + spacePerColumn}px`;
+    });
+  }
+
+  // 3. 更新列宽配置
+  columnWidths.value = newWidths;
+
+  // 4. 触发表格重新布局
+  nextTick(() => {
+    tableRef.value?.doLayout();
+  });
+};
+
 /** 搜索按钮操作 */
 const handleQuery = () => {
   queryParams.pageNo = 1
@@ -711,13 +824,47 @@ const handleExport = async () => {
   }
 }
 
+// 声明 ResizeObserver 实例
+let resizeObserver: ResizeObserver | null = null;
+
 /** 初始化 **/
 onMounted(async () => {
   deptList.value = handleTree(await DeptApi.companyLevelChildrenDepts());
   getList()
   // 预加载任务进度字典
   getTaskScheduleDictOptions()
+
+  // 创建 ResizeObserver 监听表格容器尺寸变化
+  if (tableContainerRef.value?.$el) {
+    resizeObserver = new ResizeObserver(() => {
+      // 使用防抖避免频繁触发
+      clearTimeout((window as any).resizeTimer);
+      (window as any).resizeTimer = setTimeout(() => {
+        calculateColumnWidths();
+      }, 100);
+    });
+    resizeObserver.observe(tableContainerRef.value.$el);
+  }
+})
+
+onUnmounted(() => {
+  // 清除 ResizeObserver
+  if (resizeObserver && tableContainerRef.value?.$el) {
+    resizeObserver.unobserve(tableContainerRef.value.$el);
+    resizeObserver = null;
+  }
+
+  // 清除定时器
+  if ((window as any).resizeTimer) {
+    clearTimeout((window as any).resizeTimer);
+  }
 })
+
+// 监听列表数据变化重新计算列宽
+watch(list, () => {
+  nextTick(calculateColumnWidths)
+}, { deep: true })
+
 </script>
 
 <style scoped>
@@ -785,4 +932,36 @@ onMounted(async () => {
   overflow-x: auto;
 }
 
+/* 表格容器样式,确保水平滚动 */
+.table-container {
+  width: 100%;
+  overflow-x: auto;
+}
+
+/* 确保表格单元格内容不换行 */
+:deep(.el-table .cell) {
+  white-space: nowrap;
+}
+
+/* 确保表格列标题不换行 */
+:deep(.el-table th > .cell) {
+  white-space: nowrap;
+}
+
+/* 调整表格最小宽度,确保内容完全显示 */
+:deep(.el-table) {
+  min-width: 100%;
+}
+
+/* 强制显示所有内容,防止省略号 */
+:deep(.el-table td.el-table__cell),
+:deep(.el-table th.el-table__cell) {
+  overflow: visible !important;
+}
+
+:deep(.el-table .cell) {
+  overflow: visible !important;
+  text-overflow: clip !important;
+}
+
 </style>

+ 77 - 2
src/views/pms/iotprojecttask/IotProjectTaskForm.vue

@@ -272,11 +272,17 @@
               v-model="currentTask.deptIds"
               :data="deptList"
               :props="defaultProps"
+              :default-expanded-keys="defaultExpandedKeys"
               check-strictly
               node-key="id"
               filterable
               placeholder="请选择施工队伍"
               clearable
+              @visible-change="handleTreeVisibleChange"
+              collapse-tags
+              collapse-tags-tooltip
+              :max-collapse-tags="1"
+              class="department-tree-select"
             />
           </el-form-item>
         </el-col>
@@ -497,13 +503,11 @@
 import { IotProjectInfoApi, IotProjectInfoVO } from '@/api/pms/iotprojectinfo'
 import {defaultProps,handleTree} from "@/utils/tree";
 import * as DeptApi from "@/api/system/dept";
-import CustomerList from '@/views/pms/device/CustomerList.vue'
 import {ref, reactive, computed, onMounted, watch} from "vue";
 import {useUserStore} from "@/store/modules/user";
 import {IotProjectTaskApi, IotProjectTaskVO} from "@/api/pms/iotprojecttask";
 import {ElMessageBox, ElMessage} from "element-plus";
 import {useTagsViewStore} from "@/store/modules/tagsView";
-import {companyLevelChildrenDepts} from "@/api/system/dept";
 import { IotDeviceApi, IotDeviceVO } from '@/api/pms/device'
 import * as UserApi from "@/api/system/user";
 import {IotProjectTaskAttrsApi, IotProjectTaskAttrsVO} from "@/api/pms/iotprojecttaskattrs";
@@ -521,6 +525,10 @@ if (params.projectId) {
   projectId.value = Array.isArray(query.projectId) ? query.projectId[0] : query.projectId as string
 }
 
+// 施工队伍 选择树 响应式变量
+const defaultExpandedKeys = ref<number[]>([]); // 默认展开的部门节点keys
+const treeSelectRef = ref(); // 树选择组件的引用
+
 const { delView } = useTagsViewStore() // 视图操作
 const { currentRoute, push } = useRouter()
 const { t } = useI18n() // 国际化
@@ -1410,6 +1418,20 @@ const resetForm = () => {
   formRef.value?.resetFields()
 }
 
+// 监听当前任务的变化,设置默认展开的keys
+watch(() => currentTask.value.deptIds, (newVal) => {
+  if (newVal && newVal.length > 0) {
+    defaultExpandedKeys.value = [...newVal];
+  }
+}, { immediate: true, deep: true });
+
+// 监听部门列表加载完成
+watch(() => deptList.value, (newVal) => {
+  if (newVal && newVal.length > 0 && currentTask.value.deptIds && currentTask.value.deptIds.length > 0) {
+    defaultExpandedKeys.value = [...currentTask.value.deptIds];
+  }
+}, { immediate: true, deep: true });
+
 // 监听项目列表变化,确保列表加载完成后才执行自动选择
 watch(projectList, (newVal) => {
   if (newVal && newVal.length > 0) {
@@ -1425,6 +1447,25 @@ watch(projectList, (newVal) => {
   }
 });
 
+// 处理树形下拉框可见性变化
+const handleTreeVisibleChange = (visible: boolean) => {
+  if (visible && currentTask.value.deptIds && currentTask.value.deptIds.length > 0) {
+    // 当下拉框显示且当前任务有已选部门时,设置默认展开的keys
+    defaultExpandedKeys.value = [...currentTask.value.deptIds];
+
+    // 使用nextTick确保DOM更新后再执行展开操作
+    nextTick(() => {
+      if (treeSelectRef.value) {
+        // 尝试访问内部树实例并展开节点
+        const treeInstance = treeSelectRef.value.treeRef;
+        if (treeInstance && treeInstance.setExpandedKeys) {
+          treeInstance.setExpandedKeys(defaultExpandedKeys.value);
+        }
+      }
+    });
+  }
+};
+
 onMounted(async () => {
   // 查询当前登录人所属公司下的所有部门
   deptList.value = handleTree(await DeptApi.companyLevelChildrenDepts())
@@ -1511,6 +1552,40 @@ onMounted(async () => {
   vertical-align: bottom;
 }
 
+/* 部门选择器样式优化 */
+:deep(.department-tree-select .el-select__tags) {
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  flex-wrap: nowrap;
+}
+
+:deep(.department-tree-select .el-select__tags .el-tag) {
+  max-width: 120px;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  display: inline-block;
+}
+
+
+:deep(.department-tree-select .el-select__tags .el-tag + .el-tag) {
+  margin-left: 6px;
+}
+
+:deep(.department-tree-select .el-select__tags-text) {
+  display: inline-block;
+  max-width: 100px;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+
+
+/* 当有多个标签被折叠时显示的 "+N" 标签样式 */
+:deep(.department-tree-select .el-select__collapse-tags) {
+  white-space: nowrap;
+  display: inline-block;
+}
+
 .task-edit-form {
   margin-bottom: 20px;
   padding: 20px;

+ 287 - 64
src/views/pms/iotprojecttask/index.vue

@@ -81,69 +81,71 @@
   </ContentWrap>
 
   <!-- 列表 -->
-  <ContentWrap>
-    <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
-      <el-table-column :label="t('iotDevice.serial')" width="70" align="center">
-        <template #default="scope">
-          {{ scope.$index + 1 }}
-        </template>
-      </el-table-column>
-      <el-table-column label="客户名称" align="center" prop="manufactureName" />
-      <el-table-column label="合同名称" align="center" prop="contractName" />
-      <el-table-column label="合同编号" align="center" prop="contractCode" />
-      <el-table-column label="井号" align="center" prop="wellName" />
-      <!-- <el-table-column label="井型/井别" align="center" prop="wellType" /> -->
-      <el-table-column :label="t('project.wellType')" align="center" prop="wellType" min-width="90">
-        <template #default="scope">
-          <dict-tag :type="DICT_TYPE.PMS_PROJECT_WELL_TYPE" :value="scope.row.wellType" />
-        </template>
-      </el-table-column>
-      <el-table-column label="施工地点" align="center" prop="location" />
-      <!-- <el-table-column label="施工工艺" align="center" prop="technique" /> -->
-      <el-table-column :label="t('project.technology')" align="center" prop="technique" min-width="90">
-        <template #default="scope">
-          <dict-tag :type="DICT_TYPE.PMS_PROJECT_TECHNOLOGY" :value="scope.row.technique" />
-        </template>
-      </el-table-column>
-      <el-table-column label="设计工作量" align="center" prop="workloadDesign" />
-      <el-table-column
-        label="创建时间"
-        align="center"
-        prop="createTime"
-        :formatter="dateFormatter"
-        width="180px"
-      />
-      <!--
-      <el-table-column label="备注" align="center" prop="remark" /> -->
-      <el-table-column label="操作" align="center" min-width="120px">
-        <template #default="scope">
-          <el-button
-            link
-            type="primary"
-            @click="openPlanDialog(scope.row)"
-            v-hasPermi="['rq:iot-project-task:update']"
-          >
-            计划
-          </el-button>
-          <el-button
-            link
-            type="primary"
-            @click="openForm('update', scope.row.id,scope.row.projectId)"
-            v-hasPermi="['rq:iot-project-task:update']"
-          >
-            编辑
-          </el-button>
-          <el-button
-            link
-            type="danger"
-            @click="handleDelete(scope.row.id)"
-            v-hasPermi="['rq:iot-project-task:delete']"
-          >
-            删除
-          </el-button>
-        </template>
-      </el-table-column>
-    </el-table>
+  <ContentWrap  ref="tableContainerRef">
+    <div class="table-container">
+      <el-table ref="tableRef" v-loading="loading" :data="list" :stripe="true" style="width: 100%" :cell-style="{padding: '5px'}">
+        <el-table-column :label="t('iotDevice.serial')" :width="columnWidths.serial" align="center" v-if="false">
+          <template #default="scope">
+            {{ scope.$index + 1 }}
+          </template>
+        </el-table-column>
+        <el-table-column label="客户名称" align="center" prop="manufactureName" :width="columnWidths.manufactureName" />
+        <el-table-column label="合同名称" align="center" prop="contractName" :width="columnWidths.contractName" />
+        <el-table-column label="合同编号" align="center" prop="contractCode" :width="columnWidths.contractCode" />
+        <el-table-column label="井号" align="center" prop="wellName" :width="columnWidths.wellName" />
+        <!-- <el-table-column label="井型/井别" align="center" prop="wellType" /> -->
+        <el-table-column :label="t('project.wellType')" align="center" prop="wellType" :width="columnWidths.wellType">
+          <template #default="scope">
+            <dict-tag :type="DICT_TYPE.PMS_PROJECT_WELL_TYPE" :value="scope.row.wellType" />
+          </template>
+        </el-table-column>
+        <el-table-column label="施工地点" align="center" prop="location" :width="columnWidths.location" />
+        <!-- <el-table-column label="施工工艺" align="center" prop="technique" /> -->
+        <el-table-column :label="t('project.technology')" align="center" prop="technique" :width="columnWidths.technique">
+          <template #default="scope">
+            <dict-tag :type="DICT_TYPE.PMS_PROJECT_TECHNOLOGY" :value="scope.row.technique" />
+          </template>
+        </el-table-column>
+        <el-table-column label="设计工作量" align="center" prop="workloadDesign" :width="columnWidths.workloadDesign"/>
+        <el-table-column
+          label="创建时间"
+          align="center"
+          prop="createTime"
+          :formatter="dateFormatter"
+          :width="columnWidths.createTime"
+        />
+        <!--
+        <el-table-column label="备注" align="center" prop="remark" /> -->
+        <el-table-column label="操作" align="center" :width="columnWidths.operation" fixed="right">
+          <template #default="scope">
+            <el-button
+              link
+              type="primary"
+              @click="openPlanDialog(scope.row)"
+              v-hasPermi="['rq:iot-project-task:update']"
+            >
+              计划
+            </el-button>
+            <el-button
+              link
+              type="primary"
+              @click="openForm('update', scope.row.id,scope.row.projectId)"
+              v-hasPermi="['rq:iot-project-task:update']"
+            >
+              编辑
+            </el-button>
+            <el-button
+              link
+              type="danger"
+              @click="handleDelete(scope.row.id)"
+              v-hasPermi="['rq:iot-project-task:delete']"
+            >
+              删除
+            </el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
     <!-- 分页 -->
     <Pagination
       :total="total"
@@ -243,7 +245,7 @@ import * as DictDataApi from '@/api/system/dict/dict.data'
 import { DictDataVO } from '@/api/system/dict/dict.data'
 import dayjs from 'dayjs'
 import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
-import { ref, reactive, onMounted, computed } from 'vue'
+import { ref, reactive, onMounted, computed, nextTick, watch, onUnmounted } from 'vue'
 import { useRouter } from 'vue-router'
 
 /** 项目信息任务拆分 列表 */
@@ -284,6 +286,43 @@ const { push } = useRouter() // 路由跳转
 
 const COMPLETED_STATUS = 6;
 
+// 表格引用
+const tableRef = ref()
+// 表格容器引用
+const tableContainerRef = ref()
+
+// 列宽度配置
+const columnWidths = ref({
+  serial: '50px',
+  manufactureName: '120px',
+  contractName: '150px',
+  contractCode: '120px',
+  wellName: '100px',
+  wellType: '100px',
+  location: '120px',
+  technique: '100px',
+  workloadDesign: '100px',
+  createTime: '150px',
+  operation: '200px'
+})
+
+// 计算文本宽度
+const getTextWidth = (text: string, fontSize = 14) => {
+  const span = document.createElement('span');
+  span.style.visibility = 'hidden';
+  span.style.position = 'absolute';
+  span.style.whiteSpace = 'nowrap';
+  span.style.fontSize = `${fontSize}px`;
+  span.style.fontFamily = 'inherit';
+  span.innerText = text;
+
+  document.body.appendChild(span);
+  const width = span.offsetWidth;
+  document.body.removeChild(span);
+
+  return width;
+};
+
 // 计划相关状态
 const planDialogVisible = ref(false)
 // const planList = ref<Array<{name: string, value: string, startTime: string, endTime: string}>>([])
@@ -292,6 +331,29 @@ const saveLoading = ref(false)
 const currentRow = ref<IotProjectTaskVO | null>(null)
 const workProgressDictOptions = ref<any[]>([]) // 施工进度字典选项
 
+const wellTypeDictOptions = ref<any[]>([]) // 井型字典选项
+const technologyDictOptions = ref<any[]>([]) // 施工工艺字典选项
+
+/** 获取井型字典数据 */
+const getWellTypeDictOptions = async () => {
+  try {
+    wellTypeDictOptions.value = getIntDictOptions(DICT_TYPE.PMS_PROJECT_WELL_TYPE)
+  } catch (error) {
+    console.error('获取井型字典失败:', error)
+    wellTypeDictOptions.value = []
+  }
+}
+
+/** 获取施工工艺字典数据 */
+const getTechnologyDictOptions = async () => {
+  try {
+    technologyDictOptions.value = getIntDictOptions(DICT_TYPE.PMS_PROJECT_TECHNOLOGY)
+  } catch (error) {
+    console.error('获取施工工艺字典失败:', error)
+    technologyDictOptions.value = []
+  }
+}
+
 /** 时间戳转换为日期时间字符串(使用dayjs处理) */
 const timestampToDateTime = (timestamp: number | null): string => {
   if (!timestamp) return ''
@@ -433,11 +495,102 @@ const getList = async () => {
     const data = await IotProjectTaskApi.getIotProjectTaskList(queryParams)
     list.value = data.list
     total.value = data.total
+
+    // 获取数据后计算列宽
+    nextTick(() => {
+      calculateColumnWidths();
+    });
   } finally {
     loading.value = false
   }
 }
 
+// 计算列宽度
+const calculateColumnWidths = () => {
+  const MIN_WIDTH = 80; // 最小列宽
+  const PADDING = 25; // 列内边距
+  const FLEXIBLE_COLUMNS = ['manufactureName', 'contractName', 'contractCode', 'wellName', 'location']; // 可伸缩列
+
+  // 确保表格容器存在
+  if (!tableContainerRef.value?.$el) return;
+
+  const container = tableContainerRef.value.$el;
+  const containerWidth = container.clientWidth;
+
+  // 1. 计算所有列的最小宽度
+  const minWidths: Record<string, number> = {};
+  let totalMinWidth = 0;
+
+  // 计算列最小宽度的函数
+  const calculateColumnMinWidth = (key: string, label: string, getValue: Function) => {
+    const headerWidth = getTextWidth(label) * 1.2;
+    let contentMaxWidth = 0;
+
+    // 计算内容最大宽度
+    list.value.forEach((row, index) => {
+      const text = String(getValue ? getValue(row, index) : (row[key] || ''));
+      const textWidth = getTextWidth(text);
+      if (textWidth > contentMaxWidth) contentMaxWidth = textWidth;
+    });
+
+    const minWidth = Math.max(headerWidth, contentMaxWidth, MIN_WIDTH) + PADDING;
+    minWidths[key] = minWidth;
+    totalMinWidth += minWidth;
+    return minWidth;
+  };
+
+  // 计算各列最小宽度
+  calculateColumnMinWidth('serial', t('iotDevice.serial'), (row: any, index: number) => `${index + 1}`);
+  calculateColumnMinWidth('manufactureName', '客户名称', (row: any) => row.manufactureName);
+  calculateColumnMinWidth('contractName', '合同名称', (row: any) => row.contractName);
+  calculateColumnMinWidth('contractCode', '合同编号', (row: any) => row.contractCode);
+  calculateColumnMinWidth('wellName', '井号', (row: any) => row.wellName);
+  calculateColumnMinWidth('wellType', t('project.wellType'), (row: any) => {
+    const dict = wellTypeDictOptions.value.find(d => d.value === row.wellType);
+    return dict ? dict.label : '';
+  });
+  calculateColumnMinWidth('location', '施工地点', (row: any) => row.location);
+  calculateColumnMinWidth('technique', t('project.technology'), (row: any) => {
+    const dict = technologyDictOptions.value.find(d => d.value === row.technique);
+    return dict ? dict.label : '';
+  });
+  calculateColumnMinWidth('workloadDesign', '设计工作量', (row: any) => row.workloadDesign);
+  calculateColumnMinWidth('createTime', '创建时间', (row: any) => dateFormatter(null, null, row.createTime));
+
+  // 操作列固定宽度
+  minWidths.operation = 200;
+  totalMinWidth += 200;
+
+  // 2. 计算可伸缩列最终宽度
+  const newWidths: Record<string, string> = {};
+  const availableWidth = containerWidth - 17; // 减去滚动条宽度
+
+  // 应用最小宽度到所有列
+  Object.keys(minWidths).forEach(key => {
+    newWidths[key] = `${minWidths[key]}px`;
+  });
+
+  // 计算可伸缩列需要的宽度
+  if (totalMinWidth < availableWidth) {
+    // 有剩余空间:按比例分配给可伸缩列
+    const extraSpace = availableWidth - totalMinWidth;
+    const flexibleColumnCount = FLEXIBLE_COLUMNS.length;
+    const spacePerColumn = Math.floor(extraSpace / flexibleColumnCount);
+
+    FLEXIBLE_COLUMNS.forEach(key => {
+      newWidths[key] = `${minWidths[key] + spacePerColumn}px`;
+    });
+  }
+
+  // 3. 更新列宽配置
+  columnWidths.value = newWidths;
+
+  // 4. 触发表格重新布局
+  nextTick(() => {
+    tableRef.value?.doLayout();
+  });
+};
+
 /** 搜索按钮操作 */
 const handleQuery = () => {
   queryParams.pageNo = 1
@@ -488,10 +641,80 @@ const handleExport = async () => {
   }
 }
 
+// 声明 ResizeObserver 实例
+let resizeObserver: ResizeObserver | null = null;
+
 /** 初始化 **/
 onMounted(() => {
   getList()
   // 预加载字典数据
   getWorkProgressDictOptions()
+  getWellTypeDictOptions()
+  getTechnologyDictOptions()
+
+  // 创建 ResizeObserver 监听表格容器尺寸变化
+  if (tableContainerRef.value?.$el) {
+    resizeObserver = new ResizeObserver(() => {
+      // 使用防抖避免频繁触发
+      clearTimeout((window as any).resizeTimer);
+      (window as any).resizeTimer = setTimeout(() => {
+        calculateColumnWidths();
+      }, 100);
+    });
+    resizeObserver.observe(tableContainerRef.value.$el);
+  }
 })
+
+onUnmounted(() => {
+  // 清除 ResizeObserver
+  if (resizeObserver && tableContainerRef.value?.$el) {
+    resizeObserver.unobserve(tableContainerRef.value.$el);
+    resizeObserver = null;
+  }
+
+  // 清除定时器
+  if ((window as any).resizeTimer) {
+    clearTimeout((window as any).resizeTimer);
+  }
+})
+
+// 监听列表数据变化重新计算列宽
+watch(list, () => {
+  nextTick(calculateColumnWidths)
+}, { deep: true })
+
 </script>
+
+<style scoped>
+/* 表格容器样式,确保水平滚动 */
+.table-container {
+  width: 100%;
+  overflow-x: auto;
+}
+
+/* 确保表格单元格内容不换行 */
+:deep(.el-table .cell) {
+  white-space: nowrap;
+}
+
+/* 确保表格列标题不换行 */
+:deep(.el-table th > .cell) {
+  white-space: nowrap;
+}
+
+/* 调整表格最小宽度,确保内容完全显示 */
+:deep(.el-table) {
+  min-width: 100%;
+}
+
+/* 强制显示所有内容,防止省略号 */
+:deep(.el-table td.el-table__cell),
+:deep(.el-table th.el-table__cell) {
+  overflow: visible !important;
+}
+
+:deep(.el-table .cell) {
+  overflow: visible !important;
+  text-overflow: clip !important;
+}
+</style>