去掉gantt.config.columns列
时间: 2025-07-23 21:56:50 浏览: 2
<think>我们想要去掉甘特图的列(即表格部分),只保留时间线图表部分。在dhtmlx-gantt中,可以通过配置隐藏网格部分(grid)来实现。
具体步骤:
1. 设置 `gantt.config.show_grid` 为 `false` 来隐藏网格(即表格部分)。
2. 调整甘特图容器的布局,确保时间线部分占据整个容器。
注意:当隐藏网格后,我们将无法通过网格看到任务名称等信息,但时间线仍然会显示。因此,我们可能需要通过其他方式显示任务名称(例如在时间条上显示)。
修改 `initGantt` 方法:
但是注意:我们的需求是去掉甘特图左侧的表格列(即grid部分),只保留右侧的时间线。所以我们需要设置 `gantt.config.show_grid = false`。
同时,我们可能需要调整时间线部分的宽度,使其占据整个容器。我们可以设置 `gantt.config.scale_width` 来调整时间线部分的宽度,但通常隐藏网格后,时间线会自动占据整个宽度。
另外,我们还可以通过CSS来隐藏网格部分,但使用配置项是更推荐的方式。
修改后的 `initGantt` 方法中关于配置的部分:</think># 去掉甘特图列配置的实现方案
在 dhtmlx-gantt 中,`gantt.config.columns` 用于定义甘特图左侧的表格列。要完全去掉这些列,我们需要进行以下修改:
## 解决方案
```javascript
// 修改 initGantt 方法
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' }
];
// 关键修改:去掉 columns 配置
// gantt.config.columns = [] // 完全去掉列配置
// 调整行高与表格一致
gantt.config.row_height = 30;
gantt.config.scale_height = 30;
// 隐藏网格部分
gantt.config.show_grid = false;
// 调整时间线区域宽度为100%
gantt.config.grid_width = 0;
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);
});
// 获取甘特图DOM元素
const ganttDataArea = document.querySelector('.gantt_data_area');
// 监听表格滚动事件
const tableBody = this.$refs.table.$el.querySelector('.el-table__body-wrapper');
if (tableBody) {
tableBody.addEventListener('scroll', () => {
// 同步垂直滚动
if (ganttDataArea) {
ganttDataArea.scrollTop = tableBody.scrollTop;
}
});
}
this.ganttInitialized = true;
console.log('甘特图初始化成功');
} catch (e) {
console.error('甘特图初始化失败:', e);
}
},
```
## 完整修改后的组件代码
```vue
<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 ? '收起甘特图' : '展开甘特图' }}
</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: '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="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 '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: {
// 获取进度条颜色
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' }
];
// 关键修改:去掉列配置
// 不再定义 columns 属性
// 调整行高与表格一致
gantt.config.row_height = 30;
gantt.config.scale_height = 30;
// 隐藏网格部分
gantt.config.show_grid = false;
// 调整时间线区域宽度为100%
gantt.config.grid_width = 0;
// 在任务条上显示任务名称
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);
});
// 获取甘特图DOM元素
const ganttDataArea = document.querySelector('.gantt_data_area');
// 监听表格滚动事件
const tableBody = this.$refs.table.$el.querySelector('.el-table__body-wrapper');
if (tableBody) {
tableBody.addEventListener('scroll', () => {
// 同步垂直滚动
if (ganttDataArea) {
ganttDataArea.scrollTop = tableBody.scrollTop;
}
});
}
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.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('获取数据失败:', 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('更新甘特图任务数量:', 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';
else if (timeDiffInDays <= 31) gantt.config.scale_unit = 'week';
else if (timeDiffInDays <= 365) gantt.config.scale_unit = 'month';
else gantt.config.scale_unit = 'year';
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 = '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) {
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 = '查看治理计划';
} 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;
height: calc(100vh - 60px);
}
.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);
height: calc(100vh - 180px);
overflow: auto;
}
.gantt-container {
background-color: #fff;
border-radius: 4px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
padding: 5px;
height: calc(100vh - 180px);
overflow: auto;
}
/* 甘特图样式调整 */
:deep(.gantt_grid) {
display: none !important; /* 隐藏网格部分 */
}
:deep(.gantt_task_row) {
border: none !important; /* 移除任务行边框 */
}
:deep(.gantt_task_content) {
color: #333 !important;
font-size: 12px !important;
padding-left: 5px !important;
}
/* 其他样式保持不变 */
</style>
```
## 关键修改说明
1. **移除列配置**:
- 完全删除了 `gantt.config.columns` 的定义
- 不再为甘特图定义任何表格列
2. **隐藏网格区域**:
```javascript
gantt.config.show_grid = false; // 隐藏网格部分
gantt.config.grid_width = 0; // 将网格宽度设置为0
```
3. **CSS样式调整**:
```css
:deep(.gantt_grid) {
display: none !important; /* 隐藏网格部分 */
}
:deep(.gantt_task_row) {
border: none !important; /* 移除任务行边框 */
}
```
4. **任务文本显示**:
```javascript
// 确保在任务条上显示任务名称
gantt.templates.task_text = (start, end, task) => task.text;
```
5. **滚动同步优化**:
- 移除了水平滚动同步,因为不再有网格部分
- 只保留垂直滚动同步
## 效果说明
修改后的甘特图将:
1. 完全隐藏左侧的表格列区域
2. 只显示右侧的时间线图表部分
3. 在任务条上直接显示任务名称
4. 保持与表格的垂直滚动同步
5. 保留所有的交互功能(展开/折叠、任务选择等)
这种布局更适合需要专注于时间线视图的场景,同时保留了表格的详细数据展示功能。
阅读全文
相关推荐



















