JavaScript甘特图 dhtmlx-gantt

背景

需求是在后台中,需要用甘特图去展示管理任务相关视图,并且不用依赖vue,兼容JavaScript原生开发。最终使用dhtmlx-gantt,一个半开源的库,基础功能免费,更多功能付费。
甘特图需求如图:
在这里插入图片描述

调研对比不同的库可参考:https://juejin.cn/post/7337114587122597900?searchId=20241220110156B288C2A4E8F21C0FB170

功能分析

  1. 基础元素:左侧任务树 & 右侧图例任务 Progress
  2. 新增任务
  3. 删除任务
  4. 编辑任务

Gantt 的 NPM地址 docs.dhtmlx.com

官网 Gannt

优点: 功能丰富,支持多种视图和自定义样式,适合构建复杂的项目甘特图。

缺点: 库比较重,半开源,不支持后续定制开发

开发 Demo

1. 安装

npm i dhtmlx-gantt

2. 组件导入
<script lang="ts">
import { defineComponent, onMounted, ref } from 'vue';
import { gantt } from 'dhtmlx-gantt'; // 核心模块
import 'dhtmlx-gantt/codebase/dhtmlxgantt.css'; // 样式模块
....
</script>
3. 准备引入DOM
<template>
    <div class="gantt-no" ref="ganttRef"></div>
</template>
<script>
    setup() {
        const ganttRef = ref<HTMLElement | null>(null);
        ...
        return {
            ganttRef
        }
    }
</script>
4. 准备 MOCK 数据
const tasks = {
    data: [
        { id: 1, text: '任务 1', start_date: '2021-10-17', duration: 3, progress: 0.6 },
        { id: 2, text: '任务 2', start_date: '2021-10-20', duration: 10, progress: 0.4 }
    ],
    links: []
}

参数简析:

整体数据是以对象的形式存放,其中的data是一个 Task[],links是任务连线,其结构是 Link[]

单个 Task 可能包含以下的字段:

  • id: 任务唯一标识
  • text: 任务名称
  • start_date: 任务开始时间
  • duration: 任务时长
  • progress: 任务进度
  • parent: 父级的ID(树结构关系)
  • … 其他参数要查阅其官方文档

单个 Link 可能包含:

  • id: 连线的唯一标识
  • source: 源节点
  • target: 目标节点
  • type: 连线类型(0|1|2)标识是否有箭头
5. 初始化以及传入 tasks
onMounted((0 => {
    if (ganttRef.value) {
        gantt.init(ganttRef.value); // 初始化 DOM
        gantt.parse(tasks); // 传入 tasks
    }
}) 
6. 配置图例参数
  • 禁用连线 (本需求是不需要连线功能的)
gantt.config.show_links = false;
  • 禁用工作进度拖拽 (必须通过界面弹窗的方式进行修改信息)
gantt.config.drag_progress = false;
  • 设置任务分段参数以及单位
gantt.config.duration_unit = 'day';
gantt.config.duration_step = 1;
  • 配置左侧表格栏目
    gantt.config.columns = [
      {
        name: 'text',
        label: '任务名称',
        tree: true,
        width: '*',
        align: 'left',
        template: function (obj: any) {
          return obj.text;
        }
      },
      {
        name: 'start_date',
        label: '时间',
        width: '*',
        align: 'center',
        template: function (obj: any) {
          return obj.start_date;
        }
      },
      {
        name: 'progress',
        label: '进度',
        width: '*',
        align: 'center',
        template: function (obj: any) {
          return `${obj.progress * 100}%`;
        }
      }
    ];
    参数简析: **ColumsItem[]**
    1. name: 'text' [String] , 索取的 tasks 里 **Task[]** 的 Task 的属性
    2. label: 'xxx' [String], 当前栏显示的文本
    3. tree: true [Boolean],当前的任务是否为树结构这样
    4. align: [String: left|right|center],label文本位置属性
    5. template: [Function],函数类型,入参是 obj,即为当前的 Task 对象
    6. ... 其他参数要查阅文档
  • 配置右侧表头日期栏
    gantt.config.xml_date = '%Y-%m-%d'; // 日期格式化的匹配格式
    gantt.config.scale_height = 90; // 日期栏的高度 
    const weekScaleTemplate = function (date: any) {
        const mouthMap: { [key: string]: string } = {
            Jan: '一月',
            Feb: '二月',
            Mar: '三月',
            Apr: '四月',
            May: '五月',
            Jun: '六月',
            Jul: '七月',
            Aug: '八月',
            Sept: '九月',
            Oct: '十月',
            Nov: '十一月',
            Dec: '十二月'
        };
        // 可以时使用dayjs 处理返回
        const dateToStr = gantt.date.date_to_str('%d');
        const mToStr = gantt.date.date_to_str('%M');
        const endDate = gantt.date.add(gantt.date.add(date, 1, 'week'), -1, 'day');
          // 处理一下月份
         return `${dateToStr(date)} 号 - ${dateToStr(endDate)} 号 (${
            mouthMap[mToStr(date) as string]
        })`;
    };
    const dayFormat = function (date: any) {
        const weeks = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
        return weeks[Dayjs(date).day()];
    };
    gantt.config.scales = [
        { unit: 'year', step: 1, format: '%Y' },
        { unit: 'week', step: 1, format: weekScaleTemplate },
        { unit: 'day', step: 1, format: dayFormat }
    ];
  • 添加今日的 Marker Line
    gantt.plugins({
        marker: true
    });
    gantt.addMarker({
        start_date: new Date(),
        text: '今日'
    });
任务菜单以及事件
  • 右键菜单功能
// menu.vue
<template>
  <div class="menu" :style="{ left: x + 'px', top: y + 'px' }">
    <el-menu
      @select="handleSelect"
      background-color="#545c64"
      text-color="#fff"
      active-text-color="#fff"
    >
      <el-menu-item index="add">新增任务</el-menu-item>
      <el-menu-item index="edit">编辑任务</el-menu-item>
      <el-menu-item index="del">删除任务</el-menu-item>
    </el-menu>
  </div>
</template>

<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
  props: {
    x: {
      type: Number,
      default: 0
    },
    y: {
      type: Number,
      default: 0
    }
  },
  emits: ['menu-item'],
  setup(props, ctx) {
    const handleSelect = (action: string) => {
      ctx.emit('menu-item', action);
    };
    return {
      handleSelect
    };
  }
});
</script>

<style lang="less" scoped>
.menu {
  position: fixed;
  transition: all 1s ease;
  ::v-deep(.el-menu-item) {
    height: 40px;
    line-height: 40px;
    width: 200px;
  }
}
</style>
// Gantt.vue
<template>
    <transition name="el-fade-in-linear">
      <Menu :x="menuX" :y="menuY" v-show="menuVisible" @menu-item="handleItemClick" />
    </transition>
</template>
<script lang="ts">
const menuVisible = ref<boolean>(false); // 控制菜单显示
const menuX = ref<number>(0); // left
const menuY = ref<number>(0); // top
const handleItemClick = (item: any) => {
  menuVisible.value = false; // 隐藏菜单
  dialogVisible.value = true; // 显示编辑弹窗
};
gantt.attachEvent(
  'onContextMenu',
  function (taskId, linkId, event) {
    var x = event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft,
      y = event.clientY + document.body.scrollTop + document.documentElement.scrollTop;
    // 判断要是在树上的右键菜单才有效果
    if (taskId && event.target.className === 'gantt_tree_content') {
      console.log('task ContentMenu', taskId, linkId, event);
      menuX.value = x;
      menuY.value = y;
      menuVisible.value = true;
    }

    if (taskId || linkId) {
      return false;
    }

    return true;
  },
  {}
);
// 取消菜单显示
gantt.attachEvent(
  'onEmptyClick',
  function (e) {
    //any custom logic here
    menuVisible.value = false;
  },
  {}
);
</script>

效果
在这里插入图片描述

  • 其他事件 (禁用原来自带的弹窗)
gantt.attachEvent(
  'onBeforeLightbox',
  function (id) {
    console.log(1);
    return false; // 返回 false
  },
  {}
);
  • 任务双击进入编辑事件
gantt.attachEvent(
  'onTaskDblClick',
  function (id, e) {
    console.log('id', id, e);
    dialogVisible.value = true;
    return false;
  },
  {}
);

在这里插入图片描述

总结
  1. 具体代码,只处于一个 Dome 级别
  2. 至于npm源码方面,开源出来的功能从其官网看还是基本满足需求
  3. 库的稳定和功能升级方面,每周下载还是处于活跃的状态
  4. 官网Base是英文的,然后 Samples 样库例提供了很多功能的案例,需要发掘

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/944653.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SQLite本地数据库的简介和适用场景——集成SpringBoot的图文说明

前言&#xff1a;现在项目普遍使用的数据库都是MySQL&#xff0c;而有些项目实际上使用SQLite既足矣。在一些特定的项目中&#xff0c;要比MySQL更适用。 这一篇文章简单的介绍一下SQLite&#xff0c;对比MySQL的优缺点、以及适用的项目类型和集成SpringBoot。 1. SQLite 简介 …

python编译环境安装

一、安装pycharm 下载pycharm-professional-2022.2.5.exe, 根据网上找的破解安装方法进行安装&#xff0c;然后激活。 二、安装python 启动pycharm创建第一个工程时会要求选择python解释器版本&#xff0c;并自动安装。默认安装路径为&#xff1a; C:\Users\Administrator\…

仓颉编程笔记1:变量函数定义,常用关键字,实际编写示例

本文就在网页版上体验一下仓颉编程&#xff0c;就先不下载它的SDK了 基本围绕着实际摸索的编程规则来写的 也没心思多看它的文档&#xff0c;写的不太明确&#xff0c;至少我是看的一知半解的 文章提供测试代码讲解、测试效果图&#xff1a; 目录 仓颉编程在线体验网址&…

学习记录—正则表达式-基本语法

正则表达式简介-《菜鸟教程》 正则表达式是一种用于匹配和操作文本的强大工具&#xff0c;它是由一系列字符和特殊字符组成的模式&#xff0c;用于描述要匹配的文本模式。 正则表达式可以在文本中查找、替换、提取和验证特定的模式。 本期内容将介绍普通字符&#xff0c;特殊…

剑指Offer|LCR 014. 字符串的排列

LCR 014. 字符串的排列 给定两个字符串 s1 和 s2&#xff0c;写一个函数来判断 s2 是否包含 s1 的某个变位词。 换句话说&#xff0c;第一个字符串的排列之一是第二个字符串的 子串 。 示例 1&#xff1a; 输入: s1 "ab" s2 "eidbaooo" 输出: True 解…

# 光速上手 - JPA 原生 sql DTO 投影

前言 使用 JPA 时&#xff0c;我们一般通过 Entity 进行实体类映射&#xff0c;从数据库中查询出对象。然而&#xff0c;在实际开发中&#xff0c;有时需要自定义查询结果并将其直接映射到 DTO&#xff0c;而不是实体类。这种需求可以通过 JPA 原生 SQL 查询和 DTO 投影 来实现…

区块链安全常见的攻击合约和简单复现,附带详细分析——不安全调用漏洞 (Unsafe Call Vulnerability)【6】

区块链安全常见的攻击分析——不安全调用漏洞 Unsafe Call Vulnerability 区块链安全常见的攻击合约和简单复现&#xff0c;附带详细分析——不安全调用漏洞 (Unsafe Call Vulnerability)【6】1.1 漏洞合约1.2 漏洞分析1.3 攻击步骤分析1.4 攻击合约 区块链安全常见的攻击合约和…

MVCC实现原理以及解决脏读、不可重复读、幻读问题

MVCC实现原理以及解决脏读、不可重复读、幻读问题 MVCC是什么&#xff1f;有什么作用&#xff1f;MVCC的实现原理行隐藏的字段undo log日志版本链Read View MVCC在RC下避免脏读MVCC在RC造成不可重复读、丢失修改MVCC在RR下解决不可重复读问题RR下仍然存在幻读的问题 MVCC是什么…

FFmpeg 4.3 音视频-多路H265监控录放C++开发二十一.4,SDP协议分析

SDP在4566 中有详细描述。 SDP 全称是 Session Description Protocol&#xff0c; 翻译过来就是描述会话的协议。 主要用于两个会话实体之间的媒体协商。 什么叫会话呢&#xff0c;比如一次网络电话、一次电话会议、一次视频聊天&#xff0c;这些都可以称之为一次会话。 那为什…

【Go】:Sentinel 动态数据源配置指南

前言 在现代微服务架构中&#xff0c;流量控制是确保系统高可用性和稳定性的关键。Sentinel 是一款由阿里巴巴开源的流量控制组件&#xff0c;它不仅支持熔断降级和流量整形&#xff0c;还能通过动态数据源&#xff08;如本地文件或 Nacos&#xff09;加载规则&#xff0c;从而…

VM虚拟机配置ubuntu网络

目录 桥接模式 NAT模式 桥接模式 特点&#xff1a;ubuntu的IP地址与主机IP的ip地址不同 第一部分&#xff1a;VM虚拟机给ubuntu的网络适配器&#xff0c;调为桥接模式 第二部分&#xff1a;保证所桥接的网络可以上网 第三部分&#xff1a;ubuntu使用DHCP&#xff08;默认&…

贝叶斯分类——数学模型

贝叶斯分类是一类分类算法的总称&#xff0c;这类算法均以贝叶斯定理为基础&#xff0c;故统称为贝叶斯分类。 贝叶斯分类是一类利用概率统计知识进行分类的算法&#xff0c;其分类原理是贝叶斯定理。贝叶斯定理是由18世纪概率论和决策论的早期研究者Thomas Bayes发明的&#…

爬虫与反爬虫实现全流程

我选取的网页爬取的是ppt nba版 需要的工具:pycharm,浏览器 爬虫需要观察它的网页信息,然后开始首先爬取它的html,可以看到有人气,标题,日期,咨询 可以看到用get方法 import requests url"https://img-home.csdnimg.cn/images/20230724024159.png?origin_urlhttps%3A%2…

云计算学习架构篇之HTTP协议、Nginx常用模块与Nginx服务实战

一.HTTP协议讲解 1.1rsync服务重构 bash 部署服务端: 1.安装服务 [rootbackup ~]# yum -y install rsync 2.配置服务 [rootbackup ~]# vim /etc/rsyncd.conf uid rsync gid rsync port 873 fake super yes use chroot no max connections 200 timeout 600 ignore erro…

【210】成绩管理系统

--基于springboot毕业设计成绩管理系统 主要功能: 个人中心 管理员管理 毕业论文管理 答辩秘书管理 基础数据管理 公告信息管理 公告信息管理 评阅教师管理 用户管理 指导教师管理 开发技术栈: 开发语言 : Java 开发软件 : Eclipse/MyEclipse/IDEA JDK版本 : JDK8…

Delphi历史版本对照及主要版本特性

Delphi编程的关键特性包括&#xff1a; 可视化开发&#xff1a;Delphi以其独特的开发方法而闻名&#xff0c;它允许开发者通过直观的表单设计器来创建用户界面。这种快速应用程序开发&#xff08;RAD&#xff09;的方法大大简化并加速了图形用户界面&#xff08;GUI&#xff09…

嵌入式系统 第九讲 设备驱动程序设计基础

• 9.1 Linux设备驱动程序简介 • 系统调用&#xff1a;是操作系统内核&#xff08;Linux系统内核&#xff09;和应用程序之间 的接口。 • 设备驱动程序&#xff1a;是操作系统内核&#xff08;Linux系统内核&#xff09;和机器硬件 之间的接口&#xff0c;设备驱动程序为应用…

算法学习(19)—— 队列与 BFS

关于bfs bfs又称宽搜&#xff0c;全称是“宽度优先遍历”&#xff0c;然后就是关于bfs的三个说法&#xff1a;“宽度优先搜索”&#xff0c;“宽度优先遍历”&#xff0c;“层序遍历”&#xff0c;这三个都是同一个东西&#xff0c;前面我们介绍了大量的深度优先遍历的题目已经…

cellphoneDB进行CCI以及可视化

除了cellchat&#xff0c;在单细胞转录组或者空间组的分析中&#xff0c;cellphoneDB也是一个常用的细胞通讯软件&#xff0c;这个数据库更注重配受体关系&#xff0c;对于有明确先验知识的配受体研究比较友好。 但值得注意的是&#xff0c;它的数据库只包括人的基因名称信息&…

003 字节码

字节码的位置 当我们讨论到字节码&#xff0c;我们需要清楚它在整个学习框架中的位置 如图&#xff0c;字节码是我们写的代码编译之后的结果&#xff0c;与虚拟机很近。 字节码是Java能实现跨平台的基础。 字节码基本知识体系 我们需要关注的点在于class文件的构成上。 字节…