安装依赖
pnpm add dhtmlx-gantt -save
页面使用
<template>
<section class="my-gantt">
<div class="time-box">
<a-radio-group v-model:value="timeState" @change="changeTime" button-style="solid">
<a-radio-button v-for="(time, t_index) in data.timeList" :key="t_index" :value="time.code" size="default" border>{{
time.name
}}</a-radio-button>
</a-radio-group>
</div>
<div id="gantt_here" class="gantt-container"></div>
</section>
</template>
<script setup lang="tsx">
import { reactive, toRefs, onBeforeMount, onMounted, watchEffect, defineExpose, onBeforeUnmount } from 'vue';
import { gantt } from 'dhtmlx-gantt';
import 'dhtmlx-gantt/codebase/dhtmlxgantt.css';
import { ref } from 'vue';
// import center from '@/views/demo/page/account/center/index.vue';
// import demoData from './ganttData.json';
//初始化甘特图
const initGantt = () => {
gantt.config.grid_width = 350;
gantt.config.add_column = false; //添加符号
//时间轴图表中,如果不设置,只有行边框,区分上下的任务,设置之后带有列的边框,整个时间轴变成格子状。
gantt.config.autofit = true;
gantt.config.row_height = 50; //设置行高
gantt.config.bar_height = 30; //设置时间条高度
gantt.config.fit_tasks = false; //自动延长时间刻度,以适应所有显示的任务
gantt.config.auto_types = true; //将包含子任务的任务转换为项目,将没有子任务的项目转换回任务
gantt.config.xml_date = '%Y-%m-%d'; //甘特图时间格式
gantt.config.readonly = true; //是否只读
gantt.config.details_on_dblclick = true; //双击任务是否打开详细信息
gantt.i18n.setLocale('cn'); //设置语言
// gantt.config.scale_unit = 'day'; //时间轴单位
// gantt.config.step = 1; //单位数
// gantt.config.date_scale = '%Y年%M'; //时间轴展现方式
//设置时间轴上的文字内容
gantt.templates.task_text = function (start, end, task) {
return task.name;
};
// 配置表格列
gantt.config.columns = [
{
name: 'billNo',
label: '单据/任务编号',
tree: false,
width: '100',
align: 'center',
},
{
name: 'name',
label: '名称',
width: '100',
align: 'center',
},
{
name: 'number',
label: '数量',
width: '100',
align: 'center',
},
{
name: 'start_date',
label: '开工日期',
width: '100',
align: 'center',
},
{
name: 'end_date',
label: '完工日期',
width: '100',
align: 'center',
},
{
name: 'action',
label: '操作',
width: '100',
align: 'center',
template: function (task) {
return `<a href="javascript:void(0)" class="gantt-action-btn" data-task-id="${task.id}">排产</a>`;
},
},
];
// 设置提示框
gantt.plugins({
tooltip: true,
});
gantt.templates.tooltip_text = function (start, end, task) {
return ` <div>
名称:<strong>${task.name}</strong><br/>
开工日期: ${gantt.templates.tooltip_date_format(start)}<br/>
完工日期: ${gantt.templates.tooltip_date_format(end)}<br/>
数量: ${task.number}</div>`;
};
gantt.init('gantt_here'); //初始化
gantt.parse(demoData); //填充数据
scrollInit(); //拖拽滚动视图
gantt.ext.zoom.init(zoomConfig); //配置初始化扩展
gantt.ext.zoom.setLevel('month'); //切换到指定的缩放级别
};
// 选项卡选择值
const data = reactive({
timeList: [
{
name: '日',
code: 'day',
},
{
name: '周',
code: 'week',
},
{
name: '月',
code: 'month',
},
{
name: '季',
code: 'quarter',
},
{
name: '年',
code: 'year',
},
],
timeState: 'month',
});
// 选项卡默认值
const timeState = ref('month');
// 选项卡配置
const zoomConfig = {
levels: [
{
name: 'day',
scale_height: 60,
scales: [{ unit: 'day', step: 1, format: '%d %M' }],
},
{
name: 'week',
scale_height: 60,
scales: [
{
unit: 'week',
step: 1,
format: function (date) {
let dateToStr = gantt.date.date_to_str('%m-%d');
let endDate = gantt.date.add(date, -6, 'day');
let weekNum = gantt.date.date_to_str('%W')(date); //第几周
return dateToStr(endDate) + ' 至 ' + dateToStr(date);
},
},
{
unit: 'day',
step: 1,
format: '%d', // + "周%D"
css: function (date) {
if (date.getDay() === 0 || date.getDay() === 6) {
return 'day-item weekend weekend-border-bottom';
} else {
return 'day-item';
}
},
},
],
},
{
name: 'month',
scale_height: 60,
min_column_width: 18,
scales: [
{ unit: 'month', format: '%Y-%m' },
{
unit: 'day',
step: 1,
format: '%d',
css: function (date) {
if (date.getDay() === 0 || date.getDay() === 6) {
return 'day-item weekend weekend-border-bottom';
} else {
return 'day-item';
}
},
},
],
},
{
name: 'quarter',
height: 60,
min_column_width: 110,
scales: [
{
unit: 'quarter',
step: 1,
format: function (date) {
let yearStr = new Date(date).getFullYear() + '年';
let dateToStr = gantt.date.date_to_str('%M');
let endDate = gantt.date.add(gantt.date.add(date, 3, 'month'), -1, 'day');
return yearStr + dateToStr(date) + ' - ' + dateToStr(endDate);
},
},
{
unit: 'week',
step: 1,
format: function (date) {
let dateToStr = gantt.date.date_to_str('%m-%d');
let endDate = gantt.date.add(date, 6, 'day');
let weekNum = gantt.date.date_to_str('%W')(date);
return dateToStr(date) + ' 至 ' + dateToStr(endDate);
},
},
],
},
{
name: 'year',
scale_height: 50,
min_column_width: 150,
scales: [
{ unit: 'year', step: 1, format: '%Y年' },
{ unit: 'month', format: '%Y-%m' },
],
},
],
};
//拖拽滚动视图
const scrollInit = () => {
const nav = document.querySelectorAll('.gantt_task')[0];
const parNav = document.querySelectorAll('.gantt_hor_scroll')[0];
parNav.scrollLeft = 0;
let flag;
let downX;
let scrollLeft;
nav.addEventListener('mousedown', function (event) {
flag = true;
downX = event.clientX; // 获取到点击的x下标
scrollLeft = this.scrollLeft; // 获取当前元素滚动条的偏移量
});
nav.addEventListener('mousemove', function (event) {
if (flag) {
let moveX = event.clientX;
let scrollX = moveX - downX;
parNav.scrollLeft = scrollLeft - scrollX;
}
});
// 鼠标抬起停止拖动
nav.addEventListener('mouseup', function () {
flag = false;
});
// 鼠标离开元素停止拖动
nav.addEventListener('mouseleave', function (event) {
flag = false;
});
};
// 数据列表
const demoData = {
data: [
{
id: 1,
billNo: 'WORK39847',
name: 'A.101.1.0001XX物料名称',
number: 0,
start_date: '2023-09-01',
end_date: '2023-09-10',
action: '操作',
color: '#009dff',
},
{
id: 2,
billNo: 'TASK001',
name: '机加工车间工作中心1',
number: 5,
start_date: '2021-01-01',
end_date: '2021-01-15',
action: '操作',
color: '#36c67d',
},
{
id: 3,
billNo: 'BG-004',
name: '车窗清洗',
number: 3,
start_date: '2021-01-01',
end_date: '2021-01-15',
action: '操作',
color: '#36c67d',
},
{
id: 4,
billNo: 'BG-004',
name: '车窗清洗',
number: 3,
start_date: '2021-01-01',
end_date: '2021-01-15',
action: '操作',
color: '#36c67d',
},
],
};
// 选项卡切换事件
const changeTime = (e) => {
timeState.value = e.target.value;
gantt.ext.zoom.setLevel(timeState.value);
};
onBeforeMount(() => {});
onBeforeUnmount(() => {
document.addEventListener('click', handleButtonClick);
});
onMounted(() => {
initGantt();
document.addEventListener('click', handleButtonClick);
});
watchEffect(() => {});
defineExpose({
...toRefs(data),
});
// 点击排产
function handleButtonClick(e) {
const target = e.target.closest('.gantt-action-btn');
if (target) {
const taskId = target.dataset.taskId;
handleEdit(taskId);
}
}
function handleEdit(taskId) {
// 实现编辑任务的逻辑
console.log(`编辑任务ID: ${taskId}`);
}
</script>
<style scoped lang="less">
.my-gantt {
margin: 15px;
height: calc(100vh - 130px);
width: 87.5vw;
.time-box {
text-align: center;
margin-bottom: 20px;
}
:deep(.gantt-container) {
width: 87.5vw;
height: calc(100vh - 180px);
.weekend {
background: #ff9e2f;
color: #fff;
}
}
}
</style>