JavaScript甘特图 dhtmlx-gantt

news2025/1/2 22:38:54

背景

需求是在后台中,需要用甘特图去展示管理任务相关视图,并且不用依赖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 样库例提供了很多功能的案例,需要发掘

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

领域驱动设计第一篇-DP主题

一&#xff1a;领域驱动设计概述 领域驱动设计。Domain-Driven Design 可以理解为基于领域的工程设计。 1&#xff1a;什么是领域&#xff1f; 初步理解领域&#xff1a;业务问题的范畴。 领域可大可小&#xff0c;对应着大小业务问题的边界。业务上要做的几个事&#xff0…

EMNLP'24 最佳论文解读 | 大语言模型的预训练数据检测:基于散度的校准方法

点击蓝字 关注我们 AI TIME欢迎每一位AI爱好者的加入&#xff01; 点击 阅读原文 观看作者讲解回放&#xff01; 作者简介 张伟超&#xff0c;中国科学院计算所网络数据科学与技术重点实验室三年级直博生 内容简介 近年来&#xff0c;大语言模型&#xff08;LLMs&#xff09;的…

IntelliJ IDEA 远程调试

IntelliJ IDEA 远程调试 在平时开发 JAVA 程序时&#xff0c;在遇到比较棘手的 Bug 或者是线上线下结果不一致的情况下&#xff0c;我们会通过打 Log 或者 Debug 的方式去定位并解决问题&#xff0c;两种方式各有利弊&#xff0c;今天就简要介绍下如何通过远程 Debug 的情况下…

【Webug】攻防实战详情

世界上只有一种真正的英雄主义&#xff0c;那就是认清了生活的真相后&#xff0c;仍然热爱她 显错注入 首先整体浏览网站 注入点&#xff1a; control/sqlinject/manifest_error.php?id1 判断注入类型 输入: and 11 正常, 再输入: and 12 还正常, 排除数字型 输入单引号:…

SpringMVC核心、两种视图解析方法、过滤器拦截器 “ / “ 的意义

SpringMVC的执行流程 1. Spring MVC 的视图解析机制 Spring MVC 的核心职责之一是将数据绑定到视图并呈现给用户。它通过 视图解析器&#xff08;View Resolver&#xff09; 来将逻辑视图名称解析为具体的视图文件&#xff08;如 HTML、JSP&#xff09;。 核心流程 Controlle…

CyclicBarrier线程辅助类的简单使用

文章目录 简述内部机制构造函数使用案例异常处理 简述 CyclicBarrier 是另一个用于协调多个线程之间操作的同步辅助类&#xff0c;它允许一组线程互相等待彼此到达一个共同的屏障点&#xff08;barrier&#xff09;。与 CountDownLatch 不同的是&#xff0c;CyclicBarrier 可以…

B站推荐模型数据流的一致性架构

01 背景 推荐系统的模型&#xff0c;通过学习用户历史行为来达到个性化精准推荐的目的&#xff0c;因此模型训练依赖的样本数据&#xff0c;需要包括用户特征、服务端推荐的视频特征&#xff0c;以及用户在推荐视频上是否有一系列的消费行为。 推荐模型数据流&#xff0c;即为…

无需训练!多提示视频生成最新SOTA!港中文腾讯等发布DiTCtrl:基于MM-DiT架构

文章链接&#xff1a;https://arxiv.org/pdf/2412.18597 项目链接&#xff1a;https://github.com/TencentARC/DiTCtrl 亮点直击 DiTCtrl&#xff0c;这是一种基于MM-DiT架构的、首次无需调优的多提示视频生成方法。本文的方法结合了新颖的KV共享机制和隐混合策略&#xff0c;使…

尔湾市圣诞节文化交流会成功举办,展示多元文化魅力

洛杉矶——12月21日,圣诞节文化交流会在尔湾成功举办。圣诞节文化交流会旨在促进不同文化之间的交流与理解。通过举办舞蹈表演、演讲和互动游戏等,为参与者提供了一个展示和欣赏多元文化艺术的平台。这些活动不仅增加了社区成员之间的互动,也加深了他们对不同文化传统和艺术形式…

适用于项目经理的跨团队协作实践:Atlassian Jira与Confluence集成

适用于项目经理的跨团队协作实践&#xff1a;Atlassian Jira与Confluence集成 现代项目经理的核心职责是提供可视性、保持团队一致&#xff0c;并确保团队拥有交付出色工作所需的资源。在过去几年中&#xff0c;由于分布式团队的需求不断增加&#xff0c;项目经理这一角色已迅速…

Spring Cloud LoadBalancer (负载均衡)

目录 什么是负载均衡 服务端负载均衡 客户端负载均衡 Spring Cloud LoadBalancer快速上手 启动多个product-service实例 测试负载均衡 负载均衡策略 自定义负载均衡策略 什么是负载均衡 负载均衡(Load Balance&#xff0c;简称 LB) , 是高并发, 高可用系统必不可少的关…

探究步进电机与输入脉冲的关系

深入了解步进电机 前言一、 步进电机原理二、 细分三、脉冲数总结 前言 主要是探究以下内容&#xff1a; 1、步进电机的步进角。 2、什么是细分。 3、脉冲的计算。 最后再扩展以下STM32定时器的计算方法。 一、 步进电机原理 其实语言描述怎么样都不直观&#xff0c;我更建议…

HCIA-Access V2.5_7_1_XG(S)原理_系统概述

近年来&#xff0c;随着全球范围内接入市场的飞快发展以及全业务运营的快速开展&#xff0c;已有的PON技术标准在带宽需求&#xff0c;业务支撑能力以及接入节点设备和配套设备的性能提升等方面都面临新的升级需求&#xff0c;而GPON已经向10G GPON演示&#xff0c;本章将介绍1…

安装了python,环境变量也设置了,但是输入python不报错也没反应是为什么?window的锅!

目录 问题 结论总结 衍生问题 1 第1步&#xff1a;小白python安装&#xff0c;不要埋头一直点下一步&#xff01;&#xff01;&#xff01; 2 第2步&#xff1a;可以选择删了之前的&#xff0c;重新安装python 3 第3步&#xff1a;如果你不想或不能删了重装python&#…

留学生交流互动系统|Java|SSM|VUE| 前后端分离

【技术栈】 1⃣️&#xff1a;架构: B/S、MVC 2⃣️&#xff1a;系统环境&#xff1a;Windowsh/Mac 3⃣️&#xff1a;开发环境&#xff1a;IDEA、JDK1.8、Maven、Mysql5.7 4⃣️&#xff1a;技术栈&#xff1a;Java、Mysql、SSM、Mybatis-Plus、VUE、jquery,html 5⃣️数据库可…

docker redis安装

一.镜像拉取 docker pull redis:5.0新建文件 touch /home/redis/redis.conf touch /home/redis/redis_6379.pid # bind 192.168.1.100 10.0.0.1 # bind 127.0.0.1 ::1 #bind 127.0.0.1protected-mode noport 6379tcp-backlog 511requirepass roottimeout 0tcp-keepali…

计算机网络 (13)信道复用技术

前言 计算机网络中的信道复用技术是一种提高网络资源利用率的关键技术。它允许在一条物理信道上同时传输多个用户的信号&#xff0c;从而提高了信道的传输效率和带宽利用率。 一、信道复用技术的定义 信道复用&#xff08;Multiplexing&#xff09;就是在一条传输媒体上同时传输…

2236. 判断根结点是否等于子结点之和

给你一个 二叉树 的根结点 root&#xff0c;该二叉树由恰好 3 个结点组成&#xff1a;根结点、左子结点和右子结点。 如果根结点值等于两个子结点值之和&#xff0c;返回 true &#xff0c;否则返回 false 。 示例 1&#xff1a; 输入&#xff1a;root [10,4,6] 输出&#xf…

orm03

admin后台管理 什么是admin后台管理 django提供了比较完善的后台数据库的接口&#xff0c;可供开发过程中调用和测试使用 django会搜集所有已注册的模型类&#xff0c;为这些模型类提供数据管理界面&#xff0c;供开发使用 admin配置步骤 创建后台管理账号 该账号为管理后…

UE5材质节点Panner

Panner节点可以让贴图动起来&#xff0c;快捷键是P&#xff0c;Speed的数值大小就是贴图移动的快慢&#xff0c;x和y是方向 这个节点可以用来做&#xff0c;传送带&#xff0c;护盾&#xff0c;河流&#xff0c;岩浆&#xff0c;瀑布等 制作岩浆流动效果 创建材质&#xff0c;…