vue3:el-table中嵌入进度条progress

本文展示了如何在Vue3应用中使用Element UI的el-table组件嵌入进度条。通过在表格单元格内绑定进度条组件,并赋予固定值或动态从后端获取的值来显示进度。

实现效果:

 

 代码:

1.html:在表格的其中一列为进度条展示,需要绑定“progress”

<template>
    <el-table
      :data="tableData"
      height="600"
      style="width: 100%"
      :cell-style="rowClass"
      :header-cell-style="headClass"
    >
      <el-table-column prop="name" label="姓名" width="200" />
      <el-table-column  label="学习进度" prop="progress">
        <template #default="scope">
          <el-progress
            type="line"
            :text-inside="true"
            :stroke-width="20"
            :percentage="scope.row.progress"
            status="success"
          ></el-progress>
        </template>
      </el-table-column>
    </el-table>
  </div>
</template>

2.js文件:给进度条赋值(此处为固定的值,也可以将后端传过来的值进行赋值)

<script>
export default {
  data() {
    return {
      tableData: [
        {
          name: "完全",
          progress: 50,
        },
        {
          name: "金田一客人",
          progress: 80,
        },
      ],
    };
  },
methods: {
    // 表头样式设置
    headClass() {
      return "text-align: center;background:rgb(242,242,242);color:rgb(140,138,140)";
    },
    // 表格样式设置
    rowClass() {
      return "text-align: center;";
    },
}

<template> <div class="page-container"> <el-row :gutter="20"> <!-- 查询区域 --> <el-col :span="24"> <div class="search-wrapper"> <el-form :inline="true" label-width="100px" @submit.prevent="getList"> <el-form-item label="名称"> <el-input v-model="queryParams.name" placeholder="请输入名称" clearable /> </el-form-item> <el-form-item label="责任人"> <el-input v-model="queryParams.respPerson" placeholder="请输入责任人" clearable /> </el-form-item> <el-form-item> <el-button type="primary" @click="getList">查询</el-button> <el-button @click="resetQuery">重置</el-button> <el-button type="primary" @click="toggleGantt" style="margin-left: 10px;" > {{ showGantt ? &#39;收起甘特图&#39; : &#39;展开甘特图&#39; }} </el-button> </el-form-item> </el-form> </div> </el-col> <!-- 左侧列表 --> <el-col :span="showGantt ? 12 : 24"> <div class="table-container"> <el-table ref="table" :data="listData" row-key="uid" border :row-style="{ height: &#39;30px&#39; }" :tree-props="{ children: &#39;children&#39;, hasChildren: &#39;hasChildren&#39; }" @row-click="handleRowClick" @expand-change="handleExpandChange" highlight-current-row > <el-table-column prop="code" label="编号" width="120" /> <el-table-column prop="name" label="名称" min-width="180" /> <el-table-column prop="respPerson" label="责任人" width="120" /> <el-table-column prop="schedule" label="完成百分比" width="120"> <template slot-scope="{row}"> <el-progress :percentage="row.schedule" :show-text="row.schedule > 10" :stroke-width="18" :color="getProgressColor(row.schedule)" /> </template> </el-table-column> <el-table-column prop="planStartDate" label="计划开始日期" width="150" /> <el-table-column prop="planEndDate" label="计划结束日期" width="150" /> <el-table-column label="操作" width="100"> <template slot-scope="scope"> <el-button size="mini" icon="el-icon-view" @click.stop="handleUpdate(scope.row)">查看</el-button> </template> </el-table-column> </el-table> </div> </el-col> <!-- 右侧甘特图容器 --> <el-col v-if="showGantt" :span="12"> <div ref="ganttContainer" class="gantt-container" style="width: 100%; height: 600px;"></div> </el-col> </el-row> <!-- 查看弹窗 --> <el-dialog :title="title" :visible.sync="open" width="850px" append-to-body> <el-form ref="form" :model="form" :rules="rules" label-width="100px" :disabled="disable"> <el-row> <el-col :span="12"> <el-form-item label="编号" prop="code"> <el-input v-model="form.code" placeholder="请输入编号" /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="名称" prop="name"> <el-input v-model="form.name" placeholder="请输入名称" /> </el-form-item> </el-col> </el-row> <el-row> <el-col :span="24"> <el-form-item label="备注" prop="remarks"> <el-input v-model="form.remarks" type="textarea" placeholder="请输入备注" rows="3" /> </el-form-item> </el-col> </el-row> <div class="dialog-footer"> <el-button @click="cancel">取 消</el-button> </div> </el-form> </el-dialog> </div> </template> <script> import gantt from &#39;dhtmlx-gantt&#39;; import &#39;dhtmlx-gantt/codebase/dhtmlxgantt.css&#39;; import { getPlan, listPlan } from &#39;@/api/dw/plan/planview&#39;; export default { name: &#39;Planview&#39;, data() { return { expandedKeys: new Set(), // 存储所有展开节点的UID listData: [], total: 0, queryParams: { pageNum: 1, pageSize: 1000, // 树形结构不适合分页,增加单页大小 name: null, respPerson: null }, open: false, title: &#39;&#39;, form: {}, rules: { name: [{ required: true, message: &#39;名称不能为空&#39;, trigger: &#39;blur&#39; }], schedule: [ { required: true, message: &#39;完成百分比不能为空&#39;, trigger: &#39;blur&#39; }, { type: &#39;number&#39;, message: &#39;输入内容不是有效的数字&#39;, trigger: &#39;blur&#39; } ] }, disable: true, showGantt: true, // 控制甘特图显示 flatData: [], // 扁平化数据 ganttInitialized: false, // 甘特图初始化标志 currentSelectedTask: null, // 当前选中的任务ID ganttExpandState: new Map() // 存储甘特图的展开状态 }; }, mounted() { this.getList(); }, methods: { // 获取进度条颜色 getProgressColor(percentage) { if (percentage < 30) return &#39;#F56C6C&#39;; if (percentage < 70) return &#39;#E6A23C&#39;; return &#39;#67C23A&#39;; }, // 初始化甘特图 initGantt() { if (!this.$refs.ganttContainer) return; try { // 清除之前的实例(如果存在) if (gantt.$container) { gantt.destructor(); } gantt.config.date_format = &#39;%Y-%m-%d&#39;; gantt.config.scale_unit = &#39;month&#39;; gantt.config.step = 1; gantt.config.subscales = [ { unit: &#39;day&#39;, step: 1, date: &#39;%j, %D&#39; } ]; gantt.config.columns = [ { name: &#39;text&#39;, label: &#39;任务名称&#39;, tree: true, width: 200 }, { name: &#39;start_date&#39;, label: &#39;开始时间&#39;, align: &#39;center&#39;, width: 100 }, { name: &#39;end_date&#39;, label: &#39;结束时间&#39;, align: &#39;center&#39;, width: 100 }, { name: &#39;progress&#39;, label: &#39;进度&#39;, align: &#39;center&#39;, width: 80, template: (task) => `${task.progress * 100}%` } ]; gantt.config.row_height = 30; gantt.config.grid_width = 500; gantt.templates.task_text = (start, end, task) => task.text; gantt.init(this.$refs.ganttContainer); // 绑定事件 gantt.attachEvent(&#39;onTaskSelected&#39;, (id) => { this.currentSelectedTask = id; this.scrollToTableRow(id); }); // 绑定展开/折叠事件 gantt.attachEvent(&#39;onAfterTaskOpen&#39;, (id) => { this.ganttExpandState.set(id, true); this.syncGanttExpandToTable(id, true); }); gantt.attachEvent(&#39;onAfterTaskClose&#39;, (id) => { this.ganttExpandState.set(id, false); this.syncGanttExpandToTable(id, false); }); this.ganttInitialized = true; console.log(&#39;甘特图初始化成功&#39;); } catch (e) { console.error(&#39;甘特图初始化失败:&#39;, e); } }, // 将甘特图的展开状态同步到表格 syncGanttExpandToTable(taskId, expanded) { const row = this.flatData.find(item => item.uid === taskId); if (!row) return; // 更新展开状态 if (expanded) { this.expandedKeys.add(row.uid); } else { this.expandedKeys.delete(row.uid); } // 更新表格UI this.$nextTick(() => { const tableRow = this.$refs.table.$el.querySelector(`[data-id="${row.uid}"]`); if (tableRow) { const expandIcon = tableRow.querySelector(&#39;.el-table__expand-icon&#39;); if (expandIcon) { const isExpanded = expandIcon.classList.contains(&#39;el-table__expand-icon--expanded&#39;); if (isExpanded !== expanded) { this.$refs.table.toggleRowExpansion(row, expanded); } } } }); }, // 获取数据 async getList() { try { const res = await listPlan(this.queryParams); this.listData = this.handleTree(res.data, &#39;uid&#39;, &#39;parentUid&#39;); this.flatData = this.flattenTree(this.listData); // 初始展开所有节点 this.expandedKeys = new Set(this.flatData.map(item => item.uid)); this.$nextTick(() => { // 初始化甘特图 if (this.showGantt) { this.initGantt(); this.updateGantt(); } // 展开所有节点 this.expandAllNodes(); }); } catch (error) { console.error(&#39;获取数据失败:&#39;, error); } }, // 递归展开所有节点 expandAllNodes() { if (!this.$refs.table || !this.listData.length) return; const expandNode = (node) => { this.$refs.table.toggleRowExpansion(node, true); if (node.children && node.children.length) { node.children.forEach(child => expandNode(child)); } }; this.listData.forEach(root => expandNode(root)); }, // 更新甘特图数据 updateGantt() { if (!this.ganttInitialized) return; const tasks = this.getVisibleTasks(); console.log(&#39;更新甘特图任务数量:&#39;, tasks.length); try { // 保存当前甘特图的展开状态 this.saveGanttExpandState(); gantt.clearAll(); gantt.parse({ data: tasks, links: [] }); // 恢复甘特图的展开状态 this.restoreGanttExpandState(); this.adjustGanttView(tasks); } catch (e) { console.error(&#39;更新甘特图失败:&#39;, e); } }, // 保存甘特图的展开状态 saveGanttExpandState() { if (!this.flatData.length) return; // 遍历所有任务,保存展开状态 this.flatData.forEach(item => { if (gantt.isTaskExists(item.uid)) { this.ganttExpandState.set(item.uid, gantt.isTaskOpen(item.uid)); } }); }, // 恢复甘特图的展开状态 restoreGanttExpandState() { this.ganttExpandState.forEach((isOpen, taskId) => { if (gantt.isTaskExists(taskId)) { gantt.openTask(taskId, isOpen); } }); }, // 获取当前可见的任务(根据展开状态) getVisibleTasks() { const visibleTasks = []; const collectVisible = (nodes) => { nodes.forEach(node => { visibleTasks.push({ id: node.uid, text: node.name, start_date: node.planStartDate, duration: node.planDuration || 1, progress: (node.schedule || 0) / 100, parent: node.parentUid || 0, open: this.expandedKeys.has(node.uid) // 设置初始展开状态 }); // 如果节点是展开的,递归收集子节点 if (this.expandedKeys.has(node.uid) && node.children) { collectVisible(node.children); } }); }; collectVisible(this.listData); return visibleTasks; }, // 自动调整甘特图视图 adjustGanttView(tasks) { if (!tasks.length) return; // 计算时间范围 const dates = tasks .filter(t => t.start_date) .map(t => new Date(t.start_date)); if (!dates.length) return; const minDate = new Date(Math.min(...dates.map(d => d.getTime()))); const maxDate = new Date(Math.max(...dates.map(t => { const endDate = new Date(t.start_date); endDate.setDate(endDate.getDate() + (t.duration || 0)); return endDate.getTime(); }))); // 设置时间范围 gantt.setWorkTime({ start_date: minDate, end_date: maxDate }); // 根据时间跨度调整缩放级别 const timeDiffInDays = Math.ceil((maxDate - minDate) / (1000 * 60 * 60 * 24)); if (timeDiffInDays <= 7) { gantt.config.scale_unit = &#39;day&#39;; gantt.config.step = 1; } else if (timeDiffInDays <= 31) { gantt.config.scale_unit = &#39;week&#39;; gantt.config.step = 1; } else if (timeDiffInDays <= 365) { gantt.config.scale_unit = &#39;month&#39;; gantt.config.step = 1; } else { gantt.config.scale_unit = &#39;year&#39;; gantt.config.step = 1; } gantt.render(); }, // 树形结构转扁平结构 flattenTree(data) { const result = []; const stack = [...data]; while (stack.length) { const node = stack.pop(); result.push(node); if (node.children) { stack.push(...node.children); } } return result; }, // 处理树形结构 handleTree(data, idKey = &#39;uid&#39;, parentKey = &#39;parentUid&#39;) { const map = {}; const tree = []; // 创建映射 data.forEach(item => { map[item[idKey]] = { ...item, children: [] }; }); // 构建树 data.forEach(item => { const parentId = item[parentKey]; if (parentId && map[parentId]) { map[parentId].children.push(map[item[idKey]]); } else { tree.push(map[item[idKey]]); } }); return tree; }, // 行点击事件 handleRowClick(row) { this.$nextTick(() => { // 高亮当前行 this.$refs.table.setCurrentRow(row); // 在甘特图中选中对应任务 if (this.ganttInitialized) { gantt.selectTask(row.uid); gantt.showTask(row.uid); } }); }, // 滚动到表格行 scrollToTableRow(taskId) { const row = this.flatData.find(item => item.uid === taskId); if (!row) return; this.$nextTick(() => { // 确保所有父节点都展开 this.expandParents(row); // 高亮当前行 this.$refs.table.setCurrentRow(row); // 滚动到元素 const tableBody = this.$refs.table.$el.querySelector(&#39;.el-table__body-wrapper&#39;); const rowEl = this.$refs.table.$el.querySelector(`[data-id="${row.uid}"]`); if (tableBody && rowEl) { const rowTop = rowEl.offsetTop; const tableHeight = tableBody.clientHeight; tableBody.scrollTop = rowTop - tableHeight / 2; } }); }, // 展开父节点 expandParents(row) { if (!row.parentUid) return; const parent = this.flatData.find(item => item.uid === row.parentUid); if (parent && !this.expandedKeys.has(parent.uid)) { this.expandedKeys.add(parent.uid); this.$refs.table.toggleRowExpansion(parent, true); this.expandParents(parent); } }, // 树展开/折叠更新甘特图 handleExpandChange(row, expanded) { // 更新展开状态 if (expanded) { this.expandedKeys.add(row.uid); } else { this.expandedKeys.delete(row.uid); // 折叠时同时折叠所有子节点 this.collapseChildren(row); } // 更新甘特图 this.$nextTick(() => { this.updateGantt(); // 同步到甘特图展开状态 if (this.ganttInitialized && gantt.isTaskExists(row.uid)) { gantt.openTask(row.uid, expanded); } }); }, // 递归折叠子节点 collapseChildren(node) { if (node.children && node.children.length) { node.children.forEach(child => { this.expandedKeys.delete(child.uid); this.$refs.table.toggleRowExpansion(child, false); this.collapseChildren(child); }); } }, // 切换甘特图显示 - 解决重新初始化问题 toggleGantt() { this.showGantt = !this.showGantt; if (this.showGantt) { this.$nextTick(() => { // 确保每次展开都重新初始化甘特图 this.ganttInitialized = false; this.initGantt(); this.updateGantt(); }); } }, // 获取数据详情 async handleUpdate(row) { try { const res = await getPlan(row.uid); this.form = res.data; this.open = true; this.title = &#39;查看治理计划&#39;; } catch (error) { console.error(&#39;获取详情失败:&#39;, error); } }, // 取消按钮 cancel() { this.open = false; }, // 重置查询 resetQuery() { this.queryParams = { pageNum: 1, pageSize: 1000, name: null, respPerson: null }; this.getList(); } } }; </script> <style scoped> .page-container { padding: 20px; background-color: #f5f7fa; } .search-wrapper { background-color: #fff; padding: 15px 20px; border-radius: 4px; box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); margin-bottom: 20px; } .table-container { background-color: #fff; padding: 15px; border-radius: 4px; box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); } .gantt-container { background-color: #fff; border-radius: 4px; box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); padding: 5px; } .dialog-footer { text-align: right; margin-top: 20px; } .toggle-button { margin-bottom: 15px; } .el-table { width: 100%; } .el-table--border { border: 1px solid #ebeef5; } .el-table__row:hover { background-color: #f5f7fa !important; } .el-progress { margin-top: 8px; } .el-form-item { margin-bottom: 18px; } </style> 能否把甘特图与列表合并呢,这样是不是就不用考虑太多联动性了
07-24
<template> <div class="page-container"> <el-row :gutter="20"> <!-- 查询区域 --> <div class="search-wrapper"> <el-form :inline="true" label-width="100px" @submit.prevent="getList"> <el-form-item label="名称"> <el-input v-model="queryParams.name" placeholder="请输入名称" clearable/> </el-form-item> <el-form-item label="责任人"> <el-input v-model="queryParams.respPerson" placeholder="请输入责任人" clearable/> </el-form-item> <el-form-item> <el-button type="primary" @click="getList">查询</el-button> <el-button @click="resetQuery">重置</el-button> <el-button type="primary" @click="toggleGantt" style="margin-left: 10px;" > {{ showGantt ? &#39;收起甘特图&#39; : &#39;展开甘特图&#39; }} </el-button> </el-form-item> </el-form> </div> <div class="table-container"> <el-table ref="table" :data="listData" row-key="uid" border :row-style="{ height: &#39;30px&#39; }" :tree-props="{ children: &#39;children&#39;, hasChildren: &#39;hasChildren&#39; }" @row-click="handleRowClick" @expand-change="handleExpandChange" highlight-current-row > <el-table-column prop="code" label="编号" width="120"/> <el-table-column prop="name" label="名称" min-width="180"/> <el-table-column prop="respPerson" label="责任人" width="120"/> <el-table-column prop="schedule" label="完成百分比" width="120"> <template slot-scope="{row}"> <el-progress :percentage="Number(row.schedule)" :show-text="row.schedule > 10" :stroke-width="18" :color="getProgressColor(row.schedule)" /> </template> </el-table-column> <el-table-column prop="planStartDate" label="计划开始日期" width="150"/> <el-table-column prop="planEndDate" label="计划结束日期" width="150"/> <!-- 新增甘特图列 --> <el-table-column label="时间线" min-width="300"> <template slot-scope="{row}"> <div class="gantt-cell" :ref="&#39;gantt_&#39;+row.uid" style="height: 50px;"></div> </template> </el-table-column> <!-- <el-table-column label="操作" width="100">--> <!-- <template slot-scope="scope">--> <!-- <el-button size="mini" icon="el-icon-view" @click.stop="handleUpdate(scope.row)">查看</el-button>--> <!-- </template>--> <!-- </el-table-column>--> </el-table> </div> <!-- <!– 右侧甘特图容器 –>--> <!-- <el-col v-if="showGantt" :span="12">--> <!-- <div ref="ganttContainer" class="gantt-container" style="width: 100%; height: 600px;"></div>--> <!-- </el-col>--> </el-row> <!-- 查看弹窗 --> <el-dialog :title="title" :visible.sync="open" width="850px" append-to-body> <el-form ref="form" :model="form" :rules="rules" label-width="100px" :disabled="disable"> <el-row> <el-col :span="12"> <el-form-item label="编号" prop="code"> <el-input v-model="form.code" placeholder="请输入编号"/> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="名称" prop="name"> <el-input v-model="form.name" placeholder="请输入名称"/> </el-form-item> </el-col> </el-row> <el-row> <el-col :span="24"> <el-form-item label="备注" prop="remarks"> <el-input v-model="form.remarks" type="textarea" placeholder="请输入备注" rows="3"/> </el-form-item> </el-col> </el-row> <div class="dialog-footer"> <el-button @click="cancel">取 消</el-button> </div> </el-form> </el-dialog> </div> </template> <script> import gantt from &#39;dhtmlx-gantt&#39;; import &#39;dhtmlx-gantt/codebase/dhtmlxgantt.css&#39;; import {getPlan, listPlan} from &#39;@/api/dw/plan/planview&#39;; export default { name: &#39;Planview&#39;, data() { return { expandedKeys: new Set(), // 存储所有展开节点的UID listData: [], total: 0, queryParams: { pageNum: 1, pageSize: 1000, // 树形结构不适合分页,增加单页大小 name: null, respPerson: null }, open: false, title: &#39;&#39;, form: {}, rules: { name: [{required: true, message: &#39;名称不能为空&#39;, trigger: &#39;blur&#39;}], schedule: [ {required: true, message: &#39;完成百分比不能为空&#39;, trigger: &#39;blur&#39;}, {type: &#39;number&#39;, message: &#39;输入内容不是有效的数字&#39;, trigger: &#39;blur&#39;} ] }, disable: true, showGantt: true, // 控制甘特图显示 flatData: [], // 扁平化数据 ganttInitialized: false, // 甘特图初始化标志 currentSelectedTask: null, // 当前选中的任务ID ganttExpandState: new Map() // 存储甘特图的展开状态 }; }, mounted() { this.getList(); }, methods: { // 初始化单个任务的甘特图 initTaskGantt(row) { const container = this.$refs[`gantt_${row.uid}`]?.[0]; if (!container || container._ganttInitialized) return; // 创建独立的甘特图实例 const taskGantt = gantt.createGanttInstance(); taskGantt.config.show_chart = false; taskGantt.config.show_grid = false; taskGantt.config.scale_height = 0; taskGantt.config.readonly = true; // 配置时间轴 taskGantt.config.scales = [ {unit: "day", step: 1, format: "%j %D"} ]; // 设置时间范围 const start = new Date(row.planStartDate); const end = new Date(row.planEndDate); taskGantt.setWorkTime({start_date: start, end_date: end}); // 添加任务 taskGantt.parse({ data: [{ id: row.uid, text: row.name, start_date: row.planStartDate, end_date: row.planEndDate, progress: row.schedule / 100, duration: this.calculateDuration(row.planStartDate, row.planEndDate) }] }); // 初始化甘特图 taskGantt.init(container); container._ganttInstance = taskGantt; container._ganttInitialized = true; }, // 计算任务持续时间(天) calculateDuration(start, end) { const startDate = new Date(start); const endDate = new Date(end); return Math.ceil((endDate - startDate) / (1000 * 60 * 60 * 24)); }, // 获取进度条颜色 getProgressColor(percentage) { // if (percentage < 30) return &#39;#F56C6C&#39;; // if (percentage < 70) return &#39;#E6A23C&#39;; return &#39;#67C23A&#39;; }, // 初始化甘特图 initGantt() { if (!this.$refs.ganttContainer) return; try { // 清除之前的实例(如果存在) if (gantt.$container) { gantt.destructor(); } gantt.config.date_format = &#39;%Y-%m-%d&#39;; gantt.config.scale_unit = &#39;month&#39;; gantt.config.step = 1; gantt.config.subscales = [ {unit: &#39;day&#39;, step: 1, date: &#39;%j, %D&#39;} ]; gantt.config.columns = [ {name: &#39;text&#39;, label: &#39;任务名称&#39;, tree: true, width: 200}, {name: &#39;start_date&#39;, label: &#39;开始时间&#39;, align: &#39;center&#39;, width: 100}, {name: &#39;end_date&#39;, label: &#39;结束时间&#39;, align: &#39;center&#39;, width: 100}, {name: &#39;progress&#39;, label: &#39;进度&#39;, align: &#39;center&#39;, width: 80, template: (task) => `${task.progress * 100}%`} ]; gantt.config.row_height = 30; gantt.config.grid_width = 500; gantt.templates.task_text = (start, end, task) => task.text; gantt.init(this.$refs.ganttContainer); // 绑定事件 gantt.attachEvent(&#39;onTaskSelected&#39;, (id) => { this.currentSelectedTask = id; this.scrollToTableRow(id); }); // 绑定展开/折叠事件 gantt.attachEvent(&#39;onAfterTaskOpen&#39;, (id) => { this.ganttExpandState.set(id, true); this.syncGanttExpandToTable(id, true); }); gantt.attachEvent(&#39;onAfterTaskClose&#39;, (id) => { this.ganttExpandState.set(id, false); this.syncGanttExpandToTable(id, false); }); this.ganttInitialized = true; console.log(&#39;甘特图初始化成功&#39;); } catch (e) { console.error(&#39;甘特图初始化失败:&#39;, e); } }, // 将甘特图的展开状态同步到表格 syncGanttExpandToTable(taskId, expanded) { const row = this.flatData.find(item => item.uid === taskId); if (!row) return; // 更新展开状态 if (expanded) { this.expandedKeys.add(row.uid); } else { this.expandedKeys.delete(row.uid); } // 更新表格UI this.$nextTick(() => { const tableRow = this.$refs.table.$el.querySelector(`[data-id="${row.uid}"]`); if (tableRow) { const expandIcon = tableRow.querySelector(&#39;.el-table__expand-icon&#39;); if (expandIcon) { const isExpanded = expandIcon.classList.contains(&#39;el-table__expand-icon--expanded&#39;); if (isExpanded !== expanded) { this.$refs.table.toggleRowExpansion(row, expanded); } } } }); }, // 获取数据 async getList() { try { const res = await listPlan(this.queryParams); this.listData = this.handleTree(res.data, &#39;uid&#39;, &#39;parentUid&#39;); this.$nextTick(() => { // 初始化所有可见行的甘特图 this.initVisibleGantts(); }); } catch (error) { console.error(&#39;获取数据失败:&#39;, error); } }, // 初始化所有可见行的甘特图 initVisibleGantts() { this.listData.forEach(item => { this.initTaskGantt(item); if (item.children) { item.children.forEach(child => this.initTaskGantt(child)); } }); }, // 更新甘特图数据 updateGantt() { if (!this.ganttInitialized) return; const tasks = this.getVisibleTasks(); console.log(&#39;更新甘特图任务数量:&#39;, tasks.length); try { // 保存当前甘特图的展开状态 this.saveGanttExpandState(); gantt.clearAll(); gantt.parse({data: tasks, links: []}); // 恢复甘特图的展开状态 this.restoreGanttExpandState(); this.adjustGanttView(tasks); } catch (e) { console.error(&#39;更新甘特图失败:&#39;, e); } }, // 保存甘特图的展开状态 saveGanttExpandState() { if (!this.flatData.length) return; // 遍历所有任务,保存展开状态 this.flatData.forEach(item => { if (gantt.isTaskExists(item.uid)) { this.ganttExpandState.set(item.uid, gantt.isTaskOpen(item.uid)); } }); }, // 恢复甘特图的展开状态 restoreGanttExpandState() { this.ganttExpandState.forEach((isOpen, taskId) => { if (gantt.isTaskExists(taskId)) { gantt.openTask(taskId, isOpen); } }); }, // 获取当前可见的任务(根据展开状态) getVisibleTasks() { const visibleTasks = []; const collectVisible = (nodes) => { nodes.forEach(node => { visibleTasks.push({ id: node.uid, text: node.name, start_date: node.planStartDate, duration: node.planDuration || 1, progress: (node.schedule || 0) / 100, parent: node.parentUid || 0, open: this.expandedKeys.has(node.uid) // 设置初始展开状态 }); // 如果节点是展开的,递归收集子节点 if (this.expandedKeys.has(node.uid) && node.children) { collectVisible(node.children); } }); }; collectVisible(this.listData); return visibleTasks; }, // 自动调整甘特图视图 adjustGanttView(tasks) { if (!tasks.length) return; // 计算时间范围 const dates = tasks .filter(t => t.start_date) .map(t => new Date(t.start_date)); if (!dates.length) return; const minDate = new Date(Math.min(...dates.map(d => d.getTime()))); const maxDate = new Date(Math.max(...dates.map(t => { const endDate = new Date(t.start_date); endDate.setDate(endDate.getDate() + (t.duration || 0)); return endDate.getTime(); }))); // 设置时间范围 gantt.setWorkTime({ start_date: minDate, end_date: maxDate }); // 根据时间跨度调整缩放级别 const timeDiffInDays = Math.ceil((maxDate - minDate) / (1000 * 60 * 60 * 24)); if (timeDiffInDays <= 7) { gantt.config.scale_unit = &#39;day&#39;; gantt.config.step = 1; } else if (timeDiffInDays <= 31) { gantt.config.scale_unit = &#39;week&#39;; gantt.config.step = 1; } else if (timeDiffInDays <= 365) { gantt.config.scale_unit = &#39;month&#39;; gantt.config.step = 1; } else { gantt.config.scale_unit = &#39;year&#39;; gantt.config.step = 1; } gantt.render(); }, // 处理树形结构 handleTree(data, idKey = &#39;uid&#39;, parentKey = &#39;parentUid&#39;) { const map = {}; const tree = []; // 创建映射 data.forEach(item => { map[item[idKey]] = {...item, children: []}; }); // 构建树 data.forEach(item => { const parentId = item[parentKey]; if (parentId && map[parentId]) { map[parentId].children.push(map[item[idKey]]); } else { tree.push(map[item[idKey]]); } }); return tree; }, // 行点击事件 handleRowClick(row) { this.$nextTick(() => { // 高亮当前行 this.$refs.table.setCurrentRow(row); // 在甘特图中选中对应任务 if (this.ganttInitialized) { gantt.selectTask(row.uid); gantt.showTask(row.uid); } }); }, // 滚动到表格行 scrollToTableRow(taskId) { const row = this.flatData.find(item => item.uid === taskId); if (!row) return; this.$nextTick(() => { // 确保所有父节点都展开 this.expandParents(row); // 高亮当前行 this.$refs.table.setCurrentRow(row); // 滚动到元素 const tableBody = this.$refs.table.$el.querySelector(&#39;.el-table__body-wrapper&#39;); const rowEl = this.$refs.table.$el.querySelector(`[data-id="${row.uid}"]`); if (tableBody && rowEl) { const rowTop = rowEl.offsetTop; const tableHeight = tableBody.clientHeight; tableBody.scrollTop = rowTop - tableHeight / 2; } }); }, // 展开父节点 expandParents(row) { if (!row.parentUid) return; const parent = this.flatData.find(item => item.uid === row.parentUid); if (parent && !this.expandedKeys.has(parent.uid)) { this.expandedKeys.add(parent.uid); this.$refs.table.toggleRowExpansion(parent, true); this.expandParents(parent); } }, // 树展开/折叠更新甘特图 handleExpandChange(row, expanded) { if (expanded && row.children) { this.$nextTick(() => { row.children.forEach(child => this.initTaskGantt(child)); }); } }, // 递归折叠子节点 collapseChildren(node) { if (node.children && node.children.length) { node.children.forEach(child => { this.expandedKeys.delete(child.uid); this.$refs.table.toggleRowExpansion(child, false); this.collapseChildren(child); }); } }, // 切换甘特图显示 - 解决重新初始化问题 toggleGantt() { this.showGantt = !this.showGantt; if (this.showGantt) { this.$nextTick(() => { // 确保每次展开都重新初始化甘特图 this.ganttInitialized = false; this.initGantt(); this.updateGantt(); }); } }, // 获取数据详情 async handleUpdate(row) { try { const res = await getPlan(row.uid); this.form = res.data; this.open = true; this.title = &#39;查看治理计划&#39;; } catch (error) { console.error(&#39;获取详情失败:&#39;, error); } }, // 取消按钮 cancel() { this.open = false; }, // 重置查询 resetQuery() { this.queryParams = { pageNum: 1, pageSize: 1000, name: null, respPerson: null }; this.getList(); } } }; </script> <style scoped> .page-container { padding: 20px; background-color: #f5f7fa; } .search-wrapper { background-color: #fff; padding: 15px 20px; border-radius: 4px; box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); margin-bottom: 20px; } .table-container { background-color: #fff; padding: 15px; border-radius: 4px; box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); } .gantt-container { background-color: #fff; border-radius: 4px; box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); padding: 5px; } .dialog-footer { text-align: right; margin-top: 20px; } .toggle-button { margin-bottom: 15px; } .el-table { width: 100%; } .el-table--border { border: 1px solid #ebeef5; } .el-table__row:hover { background-color: #f5f7fa !important; } .el-progress { margin-top: 8px; } .el-form-item { margin-bottom: 18px; } </style> 优化后的代码,甘特图时间线列没有数据是怎么回事
07-24
评论 4
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值