详解gantt甘特图可拖拽、编辑(vue、react都可用 highcharts)
2022-04-15 22:20:47 来源:易采站长站 作者:
前言
Excel功能强大,应用广泛。随着web应用的兴起和完善,用户的要求也越来越高。很多Excel的功能都搬到了sass里面。恨不得给他们做个Excel出来。。。程序员太难了。。。
去年我遇到了一个甘特图的需求,做了很多工作,也写了两篇博客。一篇是用 GSTC 这个包做的甘特图,另一篇是自己手写了一个简易的甘特图。两个的效果都不理想,特别是GSTC,问题很多,好多道友看了博客遇到了问题,惭愧,没能帮大家解决这个问题。之前太忙了,这个甘特图就再没搞,直到今天发现了新的包,几乎是完全符合我们的需求。
首先,我们用的是 highcharts;其次,大团队的产品,后期维护有保障,文档也齐全。
我用 vue3 写的,但是highcharts不区分他,是js包,所以无论 vue react 还是原生多页面都没问题。
接下来先看一下我们的需求,也是最基本的,需要实现的功能,然后会有效果图的gif,最后就是源代码,我放在了Git上,觉得好用,麻烦给个star。
需求
1、横轴左侧是表格数据,可以展示基本信息eComponent, onMounted, ref } from 'vue';import * as Highcharts from '@jsModule/highcharts/highcharts-gantt.src.js'import dayjs from 'dayjs'import{ WEEKS } from './constants'// api文档: https://api.Highcharts.com.cn/gantt/index.html// 社区地址: https://forum.jianshukeji.com/tags/c/Highcharts/35/Highcharts-gantt// 官方示例: https://www.highcharts.com.cn/demo/gantt/interactive-gantt// 待解决问题// 1、拖拽中断: 用户操作应该需要校验,但是现在对中断用户操作这块还没搞明白。// 解决方案: 目前的做法是,在 drop 里面做判断,根据业务逻辑,做出提示,重新渲染数据。能实现,不够友好。export default defineComponent({ name: 'hightCharts-gantt', components: {}, setup () { const gantt = ref({}); // 官方建议用UTC的时间,鉴于业务需要,我们需要和数据库时间保持统一,得看数据库的存储格式 const data = [ {start: '2021-6-1 0',end: '2021-6-1 18',factory: '华为',material: 'P50', uid: 1, y: 0, completed: 0.35}, {start: '2021-6-2 8',end: '2021-6-2 16',factory: '华为',material: 'P50', uid: 2, y: 0}, {start: '2021-6-3 8',end: '2021-6-4 24',factory: '华为',material: 'P50', uid: 3, y: 0}, {start: '2021-6-4 12',end: '2021-6-5 15',factory: '华为',material: 'P50', uid: 4, y: 0}, {start: '2021-6-1 8',end: '2021-6-1 12',factory: '小米',material: '红米3', uid: 5, y: 1}, {start: '2021-6-3 3',end: '2021-6-3 9',factory: '小米',material: '红米3', uid: 6, y: 1}, {start: '2021-6-1 6',end: '2021-6-1 16',factory: '苹果',material: 'iPhone13', uid: 7, y: 2}, {start: '2021-6-2 3',end: '2021-6-2 19',factory: '苹果',material: 'iPhone13', uid: 8, y: 2}, {start: '2021-6-3 8',end: '2021-6-3 17',factory: '苹果',material: 'iPhone13', uid: 9, y: 2}, {start: '2021-6-1 12',end: '2021-6-1 24',factory: 'OPPO',material: 'Reno7', uid: 10, y: 3}, {start: '2021-6-2 5',end: '2021-6-2 18',factory: 'OPPO',material: 'Reno7', uid: 11, y: 3}, {start: '2021-6-3 1',end: '2021-6-5 12',factory: 'OPPO',material: 'Reno7', uid: 12, y: 3}, ]; let newData = data.map(item => { item.start = dayjs(item.start).valueOf(); item.end = dayjs(item.end).valueOf(); return item }); // 全局配置,需要在图标初始化之前配置 Highcharts.setOptions({ global: { useUTC: false // 不使用utc时间 }, // 默认都是英文的,这里做了部分中文翻译 lang: { noData: '暂无数据', weekdays: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'], months: ['一月', '儿月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'] }, }); const dragStart = (e) => {www.easck.com } const drag = (e) => { } const drop = (e) => { const { newPoint = {}, target = {} } = e; if(newPoint.y || newPoint.y === 0) { let list = [], tar = newData.find(item => item.y === newPoint.y && item.uid !== target.uid); list = newData.map(item => { // 当前拖拽数据 if(item.uid === target.uid) { return { ...item, factory: tar.factory, material: tar.material, ...newPoint } } else { return item } }) gantt.value.update({ series: [{ data: list }] }) } } // 选中,可以弹窗,编辑一些业务数据 const handleSelect = (e) => { console.log('选中') } // 获取最终数据 const getData = () => { let data = gantt.value.series[0].data.map(item => { return { uid: item.uid, factory: item.factory, material: item.material, start: item.start, end: item.end } }) console.log(data) } onMounted(() => { try { gantt.value = Highcharts.ganttChart('container', { title: { text: 'hightCharts甘特图示例' }, xAxis: [{ currentDateIndicator: true, tickPixelInterval: 70, grid: { borderWidth: 1, // 右侧表头边框宽度 cellHeight: 35, // 右侧日期表头高度 }, labels: { align: 'center', formatter: function() { return `${dayjs(this.value).format('M月D')} ${WEEKS[dayjs(this.value).day()]}`; } }, }, { labels: { align: 'center', formatter: function() { return `${dayjs(this.value).format('YYYY年M月')}`; } }, }], yAxis: { type: 'category', grid: { enabled: true, borderColor: 'rgba(0,0,0,0.3)', borderWidth: 1, columns: [ { title: { text: '工厂' }, labels: { format: '{point.factory}' } }, { title: { text: '型号' }, labels: { format: '{point.material}' } }, ] } }, tooltip: { formatter: function () { return `<div> 工厂: ${this.point.factory}<br/> 开始时间: ${dayjs(this.point.start).format('YYYY-MM-DD HH:mm:ss')}<br/> 结束时间: ${dayjs(this.point.end).format('YYYY-MM-DD HH:mm:ss')}<br/> </div>` } }, series: [{ data: newData }], plotOptions: { series: { animation: false, // Do not animate dependency connectors dragDrop: { draggableX: true, // 横向拖拽 draggableY: true, // 纵向拖拽 dragMinY: 0, // 纵向拖拽下限 dragMaxY: 3, // 纵向拖拽上限 dragPrecisionX: 3600000 // 横向拖拽精度,单位毫秒 }, dataLabels: { enabled: true, format: '{point.factory}-{point.uid}', style: { cursor: 'default', pointerEvents: 'none' } }, allowPointSelect: true, point: { events: { dragStart: dragStart, drag: drag, drop: drop, select: handleSelect } } } }, exporting: { sourceWidth: 1000 }, credits: { // 去掉右下角版权信息 enabled: false }, }); } catch (error) { console.log(error) } }) return { gantt, getData } },})</script><style scoped>.hightChart-gantt { overflow-x: auto; -webkit-overflow-scrolling: touch;}#container { max-width: 1200px; min-width: 800px; height: 400px; margin: 1em auto;}</style>













闽公网安备 35020302000061号