基于jeecg-boot的任务甘特图显示

news2025/1/11 10:46:44

更多功能看演示系统

gitee源代码地址

后端代码: https://gitee.com/nbacheng/nbcio-boot

前端代码:https://gitee.com/nbacheng/nbcio-vue.git

在线演示(包括H5) : http://122.227.135.243:9888

基于项目的任务显示,最直观的就是甘特图显示,所以今天就说甘特图的显示

经过选择,最终选择dhtmlx-gantt组件,使用最新的8.0.3版本,当然这个组件就是一些高级功能需要付费。

1、后端代码

获取项目任务相关信息如下:

@Override
	public Result<?> taskGantt(Map<String, Object> mmap) {
		String projectId = MapUtils.getString(mmap, "projectId");
		List<Map> listStagesGantt = taskStagesMapper.selectTaskStagesGanttByProjectId(projectId);
		List<Map> listTasksGantt = baseMapper.selectTaskGanttByProjectId(projectId);
		if (!CollectionUtils.isEmpty(listStagesGantt)) {
			if (!CollectionUtils.isEmpty(listTasksGantt)) {
				for (Map stagesmap : listStagesGantt) {
					for (Map tasksmap : listTasksGantt) {
						if (ObjectUtils.isEmpty(tasksmap.get("parent"))) {
							tasksmap.replace("parent", stagesmap.get("id"));
						}
					}
				}
				Map<String, Object> tasksmap = new HashMap<String, Object>();
				listStagesGantt.addAll(listTasksGantt);
				tasksmap.put("data", listStagesGantt);
				return Result.OK(tasksmap);
			} else {
				Map<String, Object> tasksmap = new HashMap<String, Object>();
				tasksmap.put("data", listStagesGantt);
				return Result.OK(tasksmap);
			}

		} else {
			return Result.error("获取不到数据");
		}
	}

其中用到的两个sql如下,注意下面对日期做了格式转换:

 @Select("select id,name as text,null assign_to,null as start_date,null as end_date,sort,null parent from tw_task_stages where project_id = #{projectId} order by sort" )
    List<Map> selectTaskStagesGanttByProjectId(@Param("projectId") String  projectId);

@Select("select id,name as text,assign_to,DATE_FORMAT(begin_time,'%d-%m-%Y') as start_date,DATE_FORMAT(end_time,'%d-%m-%Y') as end_date, id_num as sort, pid as parent from tw_task where project_id = #{projectId} order by sort")
    List<Map> selectTaskGanttByProjectId(@Param("projectId") String  projectId);

2、前端代码

<template>
  <div class="project-space-gantt">
    <div class="project-navigation">
      <div class="project-nav-header">
        <a-breadcrumb>
          <a-breadcrumb-item>
            <a>
              <a-icon type="home" />
              首页
            </a>
          </a-breadcrumb-item>
        </a-breadcrumb>
      </div>
      <section class="nav-body">
        <ul class="nav-wrapper nav nav-underscore pull-left">
          <li><a class="app" data-app="tasks" @click="$router.push('/estar/teamwork/space/task/' + id)">任务</a></li>
          <li class="app"><a class="app" data-app="works" @click="$router.push('/estar/teamwork/space/files/' + id)">
              文件</a>
          <li><a class="app" data-app="build" @click="$router.push('/estar/teamwork/space/overview/' + id)">
              概览</a>
          </li>
          <li class=""><a class="app" data-app="build" @click="$router.push('/estar/teamwork/space/features/' + id)">
              版本</a>
          </li>
          <li class="actives"><a class="app" data-app="build"
              @click="$router.push('/estar/teamwork/space/gantt/' + id)">
              甘特图</a>
          </li>
        </ul>
      </section>
    </div>
    <wrapper-content :showHeader="false">
      <div class="content-wrapper">
        <div class="ganntClass" :style="{ height: ganttHeight }" v-loading="ganttLoading">
          <div ref="gantt" class="gantt-container" />
        </div>
      </div>
    </wrapper-content>
  </div>
</template>

<script>
  import {
    mapState
  } from 'vuex'
  import {
    getTasksGanttByProjectId
  } from "@/api/teamwork/task";
  import WrapperContent from '../components/WrapperContent'
  import '@/assets/tw/css/theme.less';
  import gantt from 'dhtmlx-gantt';
  import 'dhtmlx-gantt/codebase/dhtmlxgantt.css';

  export default {
    name: "project-space-gantt",
    components: {
      WrapperContent
    },
    data() {
      return {
        id: this.$route.params.id,
        loading: true,
        showLoading: false,
        loadingMore: false,
        //gantt高度
        ganttHeight: innerHeight - 50 + 'px',
        ganttLoading: false,
        projectId: '',
        tasksGantt: {
        },

      }
    },
    created() {
      //清空gantt数据
      gantt.clearAll();
      this.projectId = this.$route.params.id;
      this.getTasksGantt();
    },
    mounted() {
      var that = this;
      //本地化
      gantt.i18n.setLocale("cn");

      //自适应甘特图的尺寸大小, 使得在不出现滚动条的情况下, 显示全部任务
      gantt.config.autosize = false;
      //只读模式:打开后不可以操作甘特图
      gantt.config.readonly = false;
      //是否显示左侧树表格
      gantt.config.show_grid = true;
      //表格列设置:我们在后台获取数据后,会解析到这个表格列中,这里面会含有很多隐藏列,作用是甘特图中不需要看隐藏列,但当我们获取甘特图的任务时,这些隐藏列会跟随任务方便使用
      gantt.config.columns = [{
          //最左侧新增符号列,甘特图内置可选使用列
          name: 'add',
          label: '',
          width: '40'
        },
        {
          name: 'text',
          label: '任务名称',
          tree: true,
          width: '150'
        },
        {
          name: 'assign_to',
          label: '执行人',
          width: '100'
        },
        {
          name: 'start_date',
          label: '开始时间',
          align: 'center',
          width: '90'
        },
        {
          name: 'end_date',
          label: '结束时间',
          align: 'center',
          width: '90'
        }
      ];

      //自适应
      //gantt.config.fit_tasks = true;

      //开启提示:鼠标悬浮在gantt行上显示
      gantt.plugins({
        tooltip: true
      });

      gantt.attachEvent('onGanttReady', function() {
        var tooltips = gantt.ext.tooltips;
        gantt.templates.tooltip_text = function(start, end, task) {
          return '任务编号:' + task.id + '<br/>任务:' + task.text + '<br/>执行人:' +
            task.assign_to + '<br/>计划开始时间:' + gantt.templates.tooltip_date_format(start) + '<br/>结束时间:' + gantt
            .templates.tooltip_date_format(end);
        };
      });

      //禁用双击事件
      gantt.config.details_on_dblclick = false;
      //关闭所有错误提示信息:gantt有自己的异常消息,如果不关闭可能页面会弹出异常消息
      gantt.config.show_errors = false;

      //灯箱事件
      gantt.attachEvent('onBeforeLightbox', function(task_id) {
        //刷新灯箱数据
        //gantt.resetLightbox();
        //true:打开灯箱
        //return true;

        //这里调用了自己的页面,没有打开默认灯箱
        that.addTask(task_id);
      });

      //禁止拖动设置任务长度
      gantt.attachEvent('onBeforeTaskDrag', function(id, mode, e) {
        return false;
      });
      //禁止拖动任务
      gantt.config.drag_move = false;
      //禁止拖动任务进度
      gantt.config.drag_progress = false;
      //禁止拖放添加Link
      gantt.config.drag_links = false;

      //开启标记
      gantt.plugins({
        marker: true
      });

      //标记当前日期
      var dateToStr = gantt.date.date_to_str(gantt.config.task_date);
      var markerId = gantt.addMarker({
        start_date: new Date(),
        css: 'today', //标记样式,style中对应
        text: 'Today',
        title: dateToStr(new Date())
      });
      gantt.getMarker(markerId);

      //设置 scale_unit 属性为 month,以显示月刻度
      gantt.config.scale_unit = "month";
      //设置 step 属性为 1,以每个月显示一个刻度
      gantt.config.step = 1;
      //设置 date_scale 属性为 %Y-%m-%d,以显示年月日格式的刻度
      gantt.config.date_scale = "%Y-%m-%d";
      //设置 scale_date 属性为 gantt.date.monthStart,以从每个月的第一天开始显示刻度。
      gantt.config.scale_date = gantt.date.monthStart;
      //表头高度
      gantt.config.scale_height = 60;
      
      gantt.config.scales = [
      	{unit: "month", format: "%F, %Y"},
      	{unit: "day", step: 1, format: "%j, %D"}
      ];
  
      //设置 subscale 属性为一个包含两个刻度的对象,分别为 day 和 week。
      gantt.config.subscales = [ // 配置时间
        {
          unit: "day",
          step: 1,
          date: "%j %D"
        },
      ];
      // 初始化
      gantt.init(this.$refs.gantt);
      //gantt.clearAll(); // 防止数据缓存问题
      //gantt.parse(tasks);
    },
    methods: {
      //获取甘特图数据
      
      getTasksGantt() {
        let that = this;
        getTasksGanttByProjectId({
          projectId: that.id
        }).then((res) => {
          console.log("getTasksGanttByProjectId res=", res);
          this.tasksGantt = res.result;
          // 数据解析:将数据解析到gantt列数据中
          gantt.parse(this.tasksGantt);
          // 刷新数据
          gantt.refreshData();
          this.ganttLoading = false;
        });
      },
      //自定义新增任务
      addTask(taskId) {
        var that = this;
        this.$nextTick(() => {
          that.$refs.taskAdd.init(task, action, parentTask, $this.milestoneOriginalData);
        });
        //删除任务:每次调用gantt内置新增事件时,gantt会直接新增任务到甘特图中,而我们需要的是自定义新增任务
        gantt.deleteTask(taskId);
        //灯箱事件必须返回布尔值,这里使用了自定义灯箱返回false,即不打开灯箱
        return false;
      }
    },
  }
</script>

<style lang="less">
  .project-space-gantt {

    .project-navigation {
      top: 0px;
      z-index: 4;
    }

    .layout-content {
      padding: 0px;
      width: 100%;
      margin: 0px 0px 0px;
      background: initial;

      .content-item {
        background: #fff;
        padding: 0px 0px 0px 0px;
        border-radius: 4px;
      }
    }

    .wrapper-main {
      padding: 24px 0 12px 0px;
      background: initial;
    }
  }

  .gantt-container {
    height: 100%;
    width: 100%;
  }

  .ganntClass {
    background-color: #fff;
    padding: 10px;
    border-radius: 4px;
  }

  //今日标记样式
  .today {}
</style>

2、效果如下:

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

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

相关文章

Redis项目 PART1

第一部分&#xff1a;含注册登入商户查询&#xff08;使用缓存) 一、注册登入 1.1 session共享问题 使用redis而不用传统的session的原因&#xff08;session共享问题&#xff09;&#xff1a;每个tomcat中都有一份属于自己的session,假设用户第一次访问第一台tomcat&#x…

网络子系统学习3:网络访问层

目录 网络访问层 网络设备的表示 数据结构 注册网络设备 接收分组 传统方法 对高速接口的支持 发送分组 网络访问层 网络实现的第一层&#xff0c;即网络访问层。该层主要负责在计算机之间传输信息&#xff0c;与网卡的设备驱动程序直接协作。 本次不会讲驱动程序的设计…

全面助力AI人工智能在科研、教学与实践技能

目录 模块一 编程入门与进阶提高 模块二 科研数据可视化 模块三 信息检索与常用科研工具 模块四 科技论文写作与技巧 模块五 数据预处理与特征工程 模块六 多元线性回归 模块七 机器学习 模块八 深度学习 模块九 答疑讨论 更多推荐 在人工智能领域进行研究和深耕&…

PCB封装设计指导(十二)画出器件禁布,过孔走线禁布

PCB封装设计指导(十二)画出器件禁布,过孔走线禁布 对于分离器件或者Datasheet中有标注出走线或者过孔禁布,在封装中需要把这些信息体现出来,如何添加,见如下说明 1. 一般来讲,只有分离器件,比如电阻,电容,晶振才会在中间加上route keepout 和via keepout Via keep o…

Qt与opencv学习记录2

我希望把这篇文章中的效果实现。 【Qt学习】 OpenCV美图特效_qt图像处理_顾城沐心的博客-CSDN博客 问题1&#xff1a; 我发现是因为我使用的是MSVC2017 32位套件&#xff0c;改为MSVC2017 64位套件debug就好了。 感觉这是因为我选用的lib库也是64位的。 E:\opencv454\opencv…

使用STM32 再实现PWM小车两轮分别调速

关于PWM调速的原理&#xff0c;其实在之前89C52开发小车的时候也已经详细的描述过&#xff0c;所以主要的区别还是STM32和89C52的PWM实现区别。 关于STM32的PWM实现&#xff0c;是从CubeMX的配置开始的&#xff1a; CubeMX 1. 在上节的CubeMX项目基础上进行修改 2. 两路PWM分…

2023牛客暑期多校训练营2

题目顺序不分难度 KBox 状态dp&#xff0c;因为每个棋子只能移动到 i-1 到 i1的位置&#xff0c;所以直接用4个状态表示棋子在哪 f[i][0] 表示前i个位置中&#xff0c;i-1到i1都没有棋子 f[i][1] 表示前i个位置中&#xff0c;i-1有棋子 f[i][2] 表示前i个位置中&#xff0…

Autosar - PDUR简介与配置

文章目录 一、PDUR是什么二、不同报文类型的信号流ECUC(EcucPduCollection)三、时序图CanIfs之间的路由CanIf与Com之间的路由CanTp通道间的路由一、PDUR是什么 PDU Router(路由器)在本文将简称为PduR,在AUTOSAR的架构中,通信部份中很重要的一个模块就是PduR,它能将IPdu…

C++: day6

1 思维导图 2 顺序栈模板和顺序队列模板 #include <iostream>using namespace std;template <typename T> class My_stack { private:T *ptr; //指向堆区空间int top; //记录栈顶元素public://无参构造My_stack():ptr(new T[10]), top(-1){}//有参构造My_sta…

ROS-Moveit机械臂追踪二维码(四)

ROS-Moveit机械臂追踪二维码(四) 在仿真环境增加相机 <gazebo reference"camera_depth_frame"><sensor name"camera1" type"depth"><always_on>true</always_on><update_rate>20.0</update_rate><came…

多路选择器设计实现

文章目录 一、多路选择器二、二选一多路选择器三、四选一多路选择器设计 一、多路选择器 多路选择器是数据选择器的别称。在多路数据传送过程中&#xff0c;能够根据需要将其中任意一路选出来的电路&#xff0c;叫做数据选择器&#xff0c;也称多路选择器或多路开关。 二、二…

【从删库到跑路】MySQL数据库的索引(一)——索引的结构(BTree B+Tree Hash),语法等

&#x1f38a;专栏【MySQL】 &#x1f354;喜欢的诗句&#xff1a;更喜岷山千里雪 三军过后尽开颜。 &#x1f386;音乐分享【如愿】 &#x1f970;欢迎并且感谢大家指出小吉的问题 文章目录 &#x1f354;概述&#x1f354;索引结构⭐B-Tree多路平衡查找树&#x1f3f3;️‍&a…

【分布式事务】CAP定理和Base理论

文章目录 1、事务的ACID原则2、分布式服务案例3、CAP定理4、Base理论5、分布式事务模型 1、事务的ACID原则 所有的事务都要满足ACID原则&#xff0c;在单体架构中&#xff0c;只有一个服务&#xff0c;这个服务访问一个数据库&#xff0c;场景简单。基于数据库本身的特性&…

React 组件使用

React 组件是一个 js 函数&#xff0c;函数可以添加 jsx 标记 当前页使用组件&#xff0c;基本使用 注意&#xff1a;组件的名称&#xff0c;第一个字母一定要大写&#xff0c;否则会报错 import { createRoot } from "react-dom/client"; import "./index.c…

深度学习(30)—— DeformableDETR(1)

深度学习&#xff08;30&#xff09;—— DeformableDETR&#xff08;1&#xff09; 原本想在一篇文章中就把理论和debug的过程都呈现&#xff0c;但是发现内容很多&#xff0c;所以就分开两篇&#xff0c;照常先记录理论学习过程&#xff0c;然后是实践过程。 注&#xff1a;…

Flutter学习—— Vscode创建项目

目录 一、Vscode创建项目 二、补充五种项目类型 Application: Module 模块开发&#xff0c; Package开发 Plugin 插件开发 Skeleton 骨架开发 一、Vscode创建项目 1.快捷键 CtrlShiftP 打开命令面板&#xff0c;选择新项目 2.选择需要开发的项目类型 Application 应用开…

勾股dev部署

1.克隆项目 项目的地址&#xff1a; https://gitee.com/gouguopen/dev?_fromgitee_search#-%E5%BC%80%E6%BA%90%E5%8A%A9%E5%8A%9B 可以采用git clone https://gitee.com/gouguopen/dev.git 或者使用下载压缩包的形式 2.进入项目的根目录 cd gougudev 3.下载php依赖 需要…

三种策略改进的沙猫群优化算法(MSCSO),与白鲸、蜣螂、麻雀等多种算法进行比较,MATLAB代码...

沙猫群优化算法(sand cat swarm optimiza⁃ tion,SCSO)是 2022年提出的元启发式优化算法&#xff0c;该算法灵感来源于沙猫的捕食行为&#xff0c;沙猫群会通过搜索阶段和捕食阶段获得食物。其中算法额外使用自适应的rG和R以达到搜索阶段和捕食阶段的无缝 切换。该算法具有寻优…

刷题日记09《图论基础》

图的存储结构 对于图结构而言&#xff0c;常见的存储结构主要有两种&#xff1a;邻接表和邻接矩阵&#xff1a; 邻接表很直观&#xff0c;我把每个节点 x 的邻居都存到一个列表里&#xff0c;然后把 x 和这个列表关联起来&#xff0c;这样就可以通过一个节点 x 找到它的所有相邻…

java 8树结构返回前端

接口&#xff1a; EntityResult getOrgReal(Map<String, Object> mapParam); 实现类&#xff1a; PMethodHandle(runMethodName "TQmsZjxmzbImpl.getOrgReal", timeout 600) Override public EntityResult getOrgReal(Map<String, Object> mapParam…