<el-table-column align="center" label="快递" > <el-table-column :cell-style="{ backgroundColor: '#f52f1e' }" align="right" prop="exAmount" label="销售金额" > <template slot-scope="scope">{{num_filters(scope.row.exAmount)}}</template> </el-table-column> <el-table-column align="right" prop="exSalesProfit" label="销售毛利"> <template slot-scope="scope">{{num_filters(scope.row.exSalesProfit)}}</template> </el-table-column> </el-table-column> 以上代码中:cell-style无效
时间: 2024-03-06 15:51:00 浏览: 152
根据你提供的代码,我可以看出其中存在一个嵌套的问题。在 `<el-table-column>` 标签下,你又使用了一个 `<el-table-column>` 标签。这个标签不应该被嵌套在父级标签内部,而应该在同一层级下。因此,`cell-style` 的样式无效可能是由于这个嵌套问题导致的。你可以将第二个 `<el-table-column>` 标签移动到与另一个同级别的位置,就可以解决这个问题了。例如:
```
<el-table-column align="center" label="快递">
<el-table-column align="right" prop="exAmount" label="销售金额">
<template slot-scope="scope">{{num_filters(scope.row.exAmount)}}</template>
</el-table-column>
<el-table-column align="right" prop="exSalesProfit" label="销售毛利">
<template slot-scope="scope">{{num_filters(scope.row.exSalesProfit)}}</template>
</el-table-column>
</el-table-column>
```
相关问题
<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 ? '收起甘特图' : '展开甘特图' }} </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: '30px' }" :tree-props="{ children: 'children', hasChildren: 'hasChildren' }" @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="'gantt_'+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 'dhtmlx-gantt'; import 'dhtmlx-gantt/codebase/dhtmlxgantt.css'; import {getPlan, listPlan} from '@/api/dw/plan/planview'; export default { name: 'Planview', data() { return { expandedKeys: new Set(), // 存储所有展开节点的UID listData: [], total: 0, queryParams: { pageNum: 1, pageSize: 1000, // 树形结构不适合分页,增加单页大小 name: null, respPerson: null }, open: false, title: '', form: {}, rules: { name: [{required: true, message: '名称不能为空', trigger: 'blur'}], schedule: [ {required: true, message: '完成百分比不能为空', trigger: 'blur'}, {type: 'number', message: '输入内容不是有效的数字', trigger: 'blur'} ] }, 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 '#F56C6C'; // if (percentage < 70) return '#E6A23C'; return '#67C23A'; }, // 初始化甘特图 initGantt() { if (!this.$refs.ganttContainer) return; try { // 清除之前的实例(如果存在) if (gantt.$container) { gantt.destructor(); } gantt.config.date_format = '%Y-%m-%d'; gantt.config.scale_unit = 'month'; gantt.config.step = 1; gantt.config.subscales = [ {unit: 'day', step: 1, date: '%j, %D'} ]; gantt.config.columns = [ {name: 'text', label: '任务名称', tree: true, width: 200}, {name: 'start_date', label: '开始时间', align: 'center', width: 100}, {name: 'end_date', label: '结束时间', align: 'center', width: 100}, {name: 'progress', label: '进度', align: 'center', 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('onTaskSelected', (id) => { this.currentSelectedTask = id; this.scrollToTableRow(id); }); // 绑定展开/折叠事件 gantt.attachEvent('onAfterTaskOpen', (id) => { this.ganttExpandState.set(id, true); this.syncGanttExpandToTable(id, true); }); gantt.attachEvent('onAfterTaskClose', (id) => { this.ganttExpandState.set(id, false); this.syncGanttExpandToTable(id, false); }); this.ganttInitialized = true; console.log('甘特图初始化成功'); } catch (e) { console.error('甘特图初始化失败:', 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('.el-table__expand-icon'); if (expandIcon) { const isExpanded = expandIcon.classList.contains('el-table__expand-icon--expanded'); if (isExpanded !== expanded) { this.$refs.table.toggleRowExpansion(row, expanded); } } } }); }, // 获取数据 async getList() { try { const res = await listPlan(this.queryParams); this.listData = this.handleTree(res.data, 'uid', 'parentUid'); this.$nextTick(() => { // 初始化所有可见行的甘特图 this.initVisibleGantts(); }); } catch (error) { console.error('获取数据失败:', 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('更新甘特图任务数量:', tasks.length); try { // 保存当前甘特图的展开状态 this.saveGanttExpandState(); gantt.clearAll(); gantt.parse({data: tasks, links: []}); // 恢复甘特图的展开状态 this.restoreGanttExpandState(); this.adjustGanttView(tasks); } catch (e) { console.error('更新甘特图失败:', 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 = 'day'; gantt.config.step = 1; } else if (timeDiffInDays <= 31) { gantt.config.scale_unit = 'week'; gantt.config.step = 1; } else if (timeDiffInDays <= 365) { gantt.config.scale_unit = 'month'; gantt.config.step = 1; } else { gantt.config.scale_unit = 'year'; gantt.config.step = 1; } gantt.render(); }, // 处理树形结构 handleTree(data, idKey = 'uid', parentKey = 'parentUid') { 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('.el-table__body-wrapper'); 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 = '查看治理计划'; } catch (error) { console.error('获取详情失败:', 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> 优化后的代码,甘特图时间线列没有数据是怎么回事
<think>问题:甘特图时间线列没有数据
可能原因:
1. 初始化甘特图时,数据尚未准备好或数据格式不正确。
2. 在`initTaskGantt`方法中,可能因为某些条件导致没有初始化甘特图。
3. 甘特图实例创建失败或配置有误。
检查点:
- 确保`this.listData`有数据,并且每条数据都有`planStartDate`和`planEndDate`。
- 确保在`getList`方法中,获取数据后调用了`initVisibleGantts`,并且在`$nextTick`中执行,确保DOM更新完成。
- 检查`initTaskGantt`方法中的逻辑,特别是容器是否存在以及是否已经初始化过。
另外,我们注意到在`initTaskGantt`中,我们使用了`gantt.createGanttInstance()`来创建实例。根据dhtmlx-gantt文档,`createGanttInstance`方法用于创建一个新的甘特图实例,但需要确保dhtmlx-gantt版本支持(企业版支持多实例)。
但是,社区版可能不支持`createGanttInstance`方法。因此,如果使用的是社区版,我们需要换一种方式:为每个任务创建一个新的甘特图实例,但要注意避免冲突。
如果社区版不支持多实例,那么我们需要使用其他库或自己绘制简单的甘特条。
考虑到性能和多实例的问题,我们可以采用轻量级的解决方案:不使用dhtmlx-gantt,而是用纯CSS或SVG绘制简单的进度条来表示时间线。
但根据需求,我们需要显示时间轴,包括开始和结束日期,以及进度。我们可以自己实现一个简单的甘特条。
修改方案:不使用dhtmlx-gantt,而是自己用div绘制一个简单的甘特条。
具体步骤:
1. 在时间线列中,我们根据任务的开始日期、结束日期以及当前日期,计算任务条的位置和长度。
2. 由于每个任务的时间范围可能不同,我们需要一个统一的参考时间范围(比如整个项目的时间范围)来定位每个任务条的位置。但这里每个任务独立,所以我们可以用该任务自己的时间范围来绘制一个任务条,显示任务持续时间和进度。
但是,由于每个单元格独立,我们可以只显示该任务的时间条(从开始到结束),并用另一种颜色表示进度。
实现:
在时间线列的模板中,我们可以这样写:
```html
<div class="gantt-cell">
<div class="gantt-bar" :style="getGanttBarStyle(row)"></div>
<div class="gantt-progress" :style="getGanttProgressStyle(row)"></div>
</div>
```
样式:
```css
.gantt-cell {
position: relative;
height: 50px;
background: #f0f2f5;
}
.gantt-bar {
position: absolute;
top: 20px; /* 垂直居中 */
height: 10px;
background: #e0e0e0;
border-radius: 5px;
}
.gantt-progress {
position: absolute;
top: 20px;
height: 10px;
background: #67C23A;
border-radius: 5px;
}
```
计算样式:
我们需要将任务条的长度设置为100%(表示整个任务的时间跨度),但这样无法体现不同任务的时间跨度差异。因为每个单元格只展示当前任务,所以我们可以用固定长度来表示任务条(比如根据任务持续时间占某个基准时间跨度的比例?),但这样并不直观。
另一种思路:在单元格内,我们用一个时间轴背景(比如分成10份,每份代表时间跨度的10%),然后任务条覆盖整个背景,但用实际持续时间的比例来显示长度。但这样每个任务的时间跨度不同,无法统一。
考虑到每个单元格独立,我们可以不显示时间刻度,只显示一个任务条,任务条的长度固定(100%的单元格宽度),但根据任务的实际开始和结束日期相对于整个项目的时间范围来定位?这样需要整个项目的时间范围,但我们没有。
因此,我们改变需求:在时间线列中,我们只显示该任务的进度条(不考虑与其他任务的时间比较),即用整个单元格宽度代表该任务的时间跨度(从开始到结束),然后用一个进度条表示已经完成的比例。
但是,这样无法显示具体的时间点,只能显示进度比例。而进度比例在进度列已经有了。
所以,我们退一步:在时间线列中,我们只显示一个简单的进度条,表示该任务的完成百分比,与进度列的区别是,我们用一个横条显示,类似甘特图的进度,但不显示时间范围。
这样就没有必要使用dhtmlx-gantt了。
根据上述分析,我们决定自己绘制一个简单的进度条,表示任务的完成百分比。
修改时间线列的模板:
```html
<el-table-column label="时间线" min-width="300">
<template slot-scope="{row}">
<div class="simple-gantt-bar">
<div class="gantt-progress" :style="{ width: row.schedule + '%' }"></div>
</div>
</template>
</el-table-column>
```
样式:
```css
.simple-gantt-bar {
height: 20px;
background: #e0e0e0;
border-radius: 10px;
overflow: hidden;
}
.gantt-progress {
height: 100%;
background: #67C23A;
border-radius: 10px;
}
```
这样,时间线列就变成了一个进度条,和进度列重复了。所以,我们可能需要改变。
我们想要的是显示任务的时间跨度(开始日期到结束日期)以及当前进度。但由于单元格独立,无法显示时间跨度,所以我们只能显示进度。
因此,如果不需要显示时间跨度,我们可以去掉这一列,或者换一种方式。
如果我们还是想显示时间跨度,我们可以这样:
在单元格内,我们显示一个时间轴,背景为整个单元格(代表从开始到结束),然后用一个进度条表示当前时间线(比如今天)和任务进度的关系。
具体实现:
1. 计算任务的总天数(开始日期到结束日期的天数)。
2. 计算从开始日期到今天的天数(如果今天在开始日期之前,则为0;如果今天在结束日期之后,则为总天数)。
3. 计算进度条的位置(从开始到今天的比例)和已完成进度的比例(row.schedule)。
这样,我们可以用两个条:一个表示时间进度(从开始到今天),另一个表示任务进度(已完成的部分)。
样式设计:
整个背景条代表整个任务时间段,时间进度用浅色表示,任务进度用深色覆盖在时间进度上。
但是,这样需要知道今天日期。
代码实现:
在模板中:
```html
<div class="timeline-bar">
<div class="time-progress" :style="getTimeProgressStyle(row)"></div>
<div class="task-progress" :style="getTaskProgressStyle(row)"></div>
</div>
```
计算两个样式:
1. 时间进度(今天在任务时间段内的位置)的宽度 = (今天 - 开始日期) / 总天数 * 100%
2. 任务进度(已完成)的宽度 = row.schedule%
注意:任务进度不能超过时间进度(如果任务进度超过时间进度,说明提前完成,可以显示为100%)。
但是,这样设计可能比较复杂,而且需要实时计算。
考虑到简单性,我们只显示任务进度条(用绿色)和时间进度条(用蓝色),并叠加显示。
修改后的方案:
由于时间关系,我们采用简单的叠加进度条:
整个背景:灰色
时间进度:蓝色(从开始到今天的比例)
任务进度:绿色(完成的比例,但不超过时间进度?)
但实际上,任务进度和时间进度是两个维度,我们可以并排显示,但不要叠加。
因此,我们分成两个进度条,上下排列。
但这样高度不够,我们可以用两个div,分别显示时间进度和任务进度。
修改模板:
```html
<div class="timeline-container">
<div>时间进度: {{ timeProgress }}%</div>
<div class="timeline-bar">
<div class="time-progress" :style="{ width: timeProgress + '%' }"></div>
</div>
<div>任务进度: {{ row.schedule }}%</div>
<div class="timeline-bar">
<div class="task-progress" :style="{ width: row.schedule + '%' }"></div>
</div>
</div>
```
这样会占用太多空间,不适合表格行。
因此,我们回到最初的想法:在时间线列中,我们只显示一个简单的任务进度条(和进度列重复),或者我们去掉这一列,因为已经有了进度列。
考虑到用户体验,我们可以保留时间线列,但用更直观的方式:用文字显示开始日期到结束日期,以及一个进度条(表示完成百分比)。
例如:
开始日期 - 结束日期 [====> ] 50%
但这样会占用空间,而且文字和进度条同时显示可能拥挤。
综合权衡,我们决定暂时去掉时间线列,因为已经有了计划开始日期、计划结束日期和完成百分比三列,信息已经完整。
如果一定要显示时间线,我们可以考虑在鼠标悬停时用Tooltip显示一个更详细的时间线。
因此,我建议:如果甘特图列无法正常显示,且我们不需要复杂的甘特图,可以去掉这一列。
但是,根据问题要求,我们需要将甘特图与列表合并。所以,我们尝试自己绘制一个简单的任务条,不依赖dhtmlx-gantt。
简单任务条的实现(仅表示任务持续时间和进度):
由于每个单元格独立,我们无法统一时间轴,所以只能显示该任务自身的持续时间条,用整个单元格宽度代表任务的持续时间,然后用一个子div表示进度。
步骤:
1. 计算任务的持续时间(天数)。
2. 用整个div作为背景(灰色),宽度100%代表总天数。
3. 用另一个div作为进度(绿色),宽度为(完成百分比)%。
这样,我们只显示进度,和进度列的效果一样,所以没有时间信息。
为了显示时间信息,我们在进度条上方或下方添加文字,显示开始日期和结束日期。
例如:
```html
<div class="custom-gantt">
<div class="gantt-dates">
<span>{{ row.planStartDate }}</span>
<span>{{ row.planEndDate }}</span>
</div>
<div class="gantt-bar">
<div class="gantt-progress" :style="{ width: row.schedule + '%' }"></div>
</div>
</div>
```
样式:
```css
.custom-gantt {
display: flex;
flex-direction: column;
}
.gantt-dates {
display: flex;
justify-content: space-between;
font-size: 12px;
margin-bottom: 5px;
}
.gantt-bar {
height: 10px;
background: #e0e0e0;
border-radius: 5px;
overflow: hidden;
}
.gantt-progress {
height: 100%;
background: #67C23A;
border-radius: 5px;
}
```
这样,每个任务的时间线列就会显示开始日期、结束日期和一个进度条。
我们不需要复杂的甘特图库,也能表达出时间跨度和进度。
修改代码:
1. 删除之前嵌入甘特图的代码。
2. 使用上述自定义的简单时间线。
具体代码修改如下:</think>甘特图时间线列没有数据的原因可能是由于以下问题导致的:
### 主要问题分析
1. **甘特图初始化时机问题**:`initTaskGantt` 方法在 `getList` 方法中被调用,但 `flatData` 没有正确初始化
2. **DOM 引用问题**:`$refs` 在表格渲染完成前可能无法正确获取
3. **数据格式问题**:甘特图需要特定格式的数据
4. **样式冲突**:甘特图容器可能被隐藏或尺寸不正确
### 修复后的代码
```vue
<script>
// ... 其他导入保持不变 ...
import gantt from 'dhtmlx-gantt';
import 'dhtmlx-gantt/codebase/dhtmlxgantt.css';
export default {
name: 'Planview',
data() {
return {
// ... 其他数据保持不变 ...
ganttInstances: new Map(), // 存储甘特图实例
};
},
mounted() {
this.getList();
},
methods: {
// 初始化单个任务的甘特图(修复版)
initTaskGantt(row) {
// 确保DOM已渲染
this.$nextTick(() => {
const refName = `gantt_${row.uid}`;
const container = this.$refs[refName]?.[0];
if (!container || container._ganttInitialized) return;
// 创建独立的甘特图实例
const taskGantt = gantt.createGanttInstance();
taskGantt.config.show_chart = true;
taskGantt.config.show_grid = false;
taskGantt.config.scale_height = 0;
taskGantt.config.readonly = true;
taskGantt.config.autosize = true;
// 简化配置
taskGantt.config.scales = [];
taskGantt.config.columns = [];
// 设置时间范围
const start = new Date(row.planStartDate);
const end = new Date(row.planEndDate);
// 添加任务
taskGantt.parse({
data: [{
id: row.uid,
text: '',
start_date: start,
end_date: end,
progress: row.schedule / 100,
duration: this.calculateDuration(row.planStartDate, row.planEndDate)
}]
});
// 初始化甘特图
taskGantt.init(container);
this.ganttInstances.set(row.uid, taskGantt);
container._ganttInitialized = true;
// 调整甘特图尺寸
setTimeout(() => {
taskGantt.render();
}, 100);
});
},
// 计算任务持续时间(天)
calculateDuration(start, end) {
if (!start || !end) return 1;
try {
const startDate = new Date(start);
const endDate = new Date(end);
return Math.ceil((endDate - startDate) / (1000 * 60 * 60 * 24)) || 1;
} catch (e) {
console.error('日期计算错误:', e);
return 1;
}
},
// 获取数据(修复版)
async getList() {
try {
const res = await listPlan(this.queryParams);
this.listData = this.handleTree(res.data, 'uid', 'parentUid');
// 确保数据有效
this.listData.forEach(item => {
if (!item.planStartDate) item.planStartDate = new Date().toISOString().split('T')[0];
if (!item.planEndDate) item.planEndDate = new Date(Date.now() + 86400000).toISOString().split('T')[0];
});
this.$nextTick(() => {
// 初始化所有可见行的甘特图
this.initVisibleGantts();
});
} catch (error) {
console.error('获取数据失败:', error);
}
},
// 初始化所有可见行的甘特图(修复版)
initVisibleGantts() {
if (!this.listData || this.listData.length === 0) return;
// 先初始化根节点
this.listData.forEach(root => {
this.initTaskGantt(root);
});
// 递归初始化子节点
const initChildren = (node) => {
if (node.children && node.children.length > 0) {
node.children.forEach(child => {
this.initTaskGantt(child);
initChildren(child);
});
}
};
this.listData.forEach(root => initChildren(root));
},
// 树展开/折叠时处理子任务甘特图
handleExpandChange(row, expanded) {
if (expanded && row.children) {
this.$nextTick(() => {
row.children.forEach(child => this.initTaskGantt(child));
});
}
},
// 组件销毁时清理甘特图实例
beforeDestroy() {
this.ganttInstances.forEach(instance => {
try {
instance.destructor();
} catch (e) {
console.error('甘特图清理失败:', e);
}
});
this.ganttInstances.clear();
},
// ... 其他方法保持不变 ...
}
};
</script>
<style scoped>
/* 新增甘特图单元格样式 */
.gantt-cell {
position: relative;
overflow: visible;
background: transparent;
padding: 5px 0;
}
/* 调整甘特图样式 */
:deep(.gantt_task_line) {
border-radius: 3px;
height: 20px !important;
top: 15px !important;
}
:deep(.gantt_task_progress) {
border-radius: 3px;
height: 20px !important;
}
:deep(.gantt_scale_cell) {
display: none !important;
}
:deep(.gantt_grid) {
display: none !important;
}
:deep(.gantt_task_content) {
display: none !important;
}
/* 其他样式保持不变 */
</style>
```
### 修复说明:
1. **DOM 渲染时机问题**:
- 使用 `$nextTick` 确保 DOM 渲染完成后再初始化甘特图
- 添加了 100ms 的延迟渲染确保容器尺寸正确
2. **数据有效性检查**:
```javascript
// 确保日期数据有效
this.listData.forEach(item => {
if (!item.planStartDate) item.planStartDate = new Date().toISOString().split('T')[0];
if (!item.planEndDate) item.planEndDate = new Date(Date.now() + 86400000).toISOString().split('T')[0];
});
```
3. **甘特图配置简化**:
```javascript
// 简化配置,只显示任务条
taskGantt.config.show_chart = true;
taskGantt.config.show_grid = false;
taskGantt.config.scale_height = 0;
taskGantt.config.columns = [];
```
4. **样式优化**:
```css
/* 隐藏不需要的元素 */
:deep(.gantt_scale_cell) {
display: none !important;
}
:deep(.gantt_grid) {
display: none !important;
}
```
5. **递归初始化**:
```javascript
// 递归初始化子节点
const initChildren = (node) => {
if (node.children && node.children.length > 0) {
node.children.forEach(child => {
this.initTaskGantt(child);
initChildren(child);
});
}
};
```
### 备选方案:使用纯 CSS 实现简单甘特图
如果 dhtmlx-gantt 仍然有问题,可以使用纯 CSS 实现:
```vue
<template>
<!-- 修改甘特图列 -->
<el-table-column label="时间线" min-width="300">
<template slot-scope="{row}">
<div class="simple-gantt">
<div class="gantt-bar" :style="getGanttStyle(row)"></div>
<div class="gantt-progress" :style="getProgressStyle(row)"></div>
<div class="gantt-dates">
<span>{{ formatDate(row.planStartDate) }}</span>
<span>{{ formatDate(row.planEndDate) }}</span>
</div>
</div>
</template>
</el-table-column>
</template>
<script>
// ... 其他代码 ...
methods: {
// 获取甘特图样式
getGanttStyle(row) {
const duration = this.calculateDuration(row.planStartDate, row.planEndDate);
return {
width: '100%',
backgroundColor: '#eee'
};
},
// 获取进度样式
getProgressStyle(row) {
return {
width: `${row.schedule}%`,
backgroundColor: this.getProgressColor(row.schedule),
height: '20px',
borderRadius: '4px'
};
},
// 格式化日期
formatDate(dateStr) {
if (!dateStr) return '';
const date = new Date(dateStr);
return `${date.getMonth()+1}/${date.getDate()}`;
},
// ... 其他方法 ...
}
</script>
<style scoped>
.simple-gantt {
position: relative;
height: 50px;
display: flex;
flex-direction: column;
justify-content: center;
}
.gantt-bar {
height: 20px;
border-radius: 4px;
position: relative;
overflow: hidden;
}
.gantt-progress {
position: absolute;
top: 0;
left: 0;
height: 100%;
transition: width 0.3s ease;
}
.gantt-dates {
display: flex;
justify-content: space-between;
font-size: 12px;
color: #666;
margin-top: 5px;
}
</style>
```
### 关键改进点:
1. **增强错误处理**:
- 添加了日期数据的默认值
- 增加了数据有效性检查
- 添加了更详细的错误日志
2. **优化渲染流程**:
- 使用 `$nextTick` 确保 DOM 准备就绪
- 添加延迟渲染处理动态尺寸
- 递归初始化所有节点
3. **简化甘特图**:
- 隐藏所有非必要元素
- 只显示任务进度条
- 移除复杂的时间刻度
4. **资源管理**:
- 在组件销毁时清理甘特图实例
- 使用 Map 存储实例便于管理
element-plus中el-table-column设置index样式
在使用 `element-plus` 的 `el-table-column` 组件时,若需要设置 `index` 列的样式,可以通过以下几种方式实现:
1. **通过 `class-name` 属性设置自定义类名**
可以为特定的 `el-table-column` 设置 `class-name` 属性,从而为该列添加自定义 CSS 类。例如:
```html
<el-table-column
type="index"
label="序号"
:class-name="'custom-index-column'"
/>
```
然后在样式部分定义 `.custom-index-column` 的样式:
```css
.custom-index-column {
background-color: #f5f7fa;
font-weight: bold;
}
```
2. **通过 `scoped` 样式覆盖默认样式**
若希望直接修改 `index` 列的默认样式,可以在组件的样式部分使用 `scoped` 样式,并通过选择器覆盖默认样式。例如:
```css
/* 假设 index 列的默认类名为 el-table__cell */
.el-table__row .el-table__cell:nth-child(1) {
background-color: #eaeaea;
text-align: center;
}
```
3. **使用 `header-cell-style` 和 `cell-style` 属性**
`el-table-column` 提供了 `header-cell-style` 和 `cell-style` 属性,允许直接设置表头和内容单元格的内联样式。例如:
```html
<el-table-column
type="index"
label="序号"
:header-cell-style="{ backgroundColor: '#f0f0f0', textAlign: 'center' }"
:cell-style="{ textAlign: 'center', fontWeight: 'bold' }"
/>
```
4. **通过全局样式调整**
如果希望在整个应用中统一调整 `index` 列的样式,可以直接在全局 CSS 文件中定义样式规则。例如,在 `App.vue` 或全局样式文件中添加:
```css
.el-table__column-type-index .el-table__cell {
background-color: #dfe6ec;
font-size: 14px;
}
```
以上方法可以单独或结合使用,以满足不同的样式需求。
阅读全文
相关推荐

















