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

更多功能看演示系统

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、后端代码

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

@Overridepublic 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;//禁止拖放添加Linkgantt.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.mzph.cn/news/7731.shtml

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

相关文章

SpringCloud-Alibaba之Sentinel熔断与限流

一、下载安装运行 http://localhost:8080进行访问 登录账号和密码均为sentinel 二、创建工程&#xff0c;并注册到nacos服务中心 依赖spring-cloud-starter-alibaba-nacos-discovery,spring-cloud-starter-alibaba-sentinel sentine-datasource-nacos (持久化)配置文件 se…

python:基于GeoPandas和GeoViews库将GEDI激光高程数据映射到交互式地图

作者:CSDN @ _养乐多_ 本文将介绍 GEDI(Global Ecosystem Dynamics Investigation)激光雷达数据某数据点波形数据提取,并绘制图表,添加其他图表元素并使图表具有交互性。 在本文中,我们将探索如何打开、读取和处理GEDI数据,并利用地理信息处理库GeoPandas和地理空间数…

使用langchain与你自己的数据对话(二):向量存储与嵌入

之前我以前完成了“使用langchain与你自己的数据对话(一)&#xff1a;文档加载与切割”这篇博客&#xff0c;没有阅读的朋友可以先阅读一下&#xff0c;今天我们来继续讲解deepleaning.AI的在线课程“LangChain: Chat with Your Data”的第三门课&#xff1a;向量存储与嵌入。 …

抖音seo账号矩阵系统源码如何开发布局?

目录 一、 抖音SEO账号矩阵系统源码的开发布局步骤如下&#xff1a; 二。 开发部署源码 三、 开发部署功能设计 1. 短视频AI智能创作 2. 托管式账号管理: 3. 数据分析 4. 智能营销获客 四。 抖音seo源码开发部署交付技术文档包含 五。 开发代码展示&#xff1a; 一、 抖…

vuejs源码之模版编译原理

之前我们说过虚拟dom&#xff0c;也就是虚拟dom拿到vnode后所做的事情&#xff0c;而模版编译是如何让虚拟dom拿到vnode。 模版编译的目标就是生成渲染函数&#xff0c;而渲染函数的作用是每次执行它&#xff0c;它就会使用当前最新的状态生成一份新的vnode&#xff0c;然后用…

Idea 开启 lombook 注解插件处理器

Idea 开启 lombook 注解插件处理器 方便编译器识别 勾选 Enable annotation processing

线性DP--BOX

还没学&#xff0c;等学完再仔细写。 #include<bits/stdc.h> using namespace std; typedef long long ll; ll a[1000010]; ll vis[1000010]; ll f[1000010][3]; int main() {ll n,m;cin>>n;for(int i1;i<n;i){cin>>a[i];}for(int i1;i<n;i){cin>&g…

【Python机器学习】实验01 Numpy以及可视化回顾

文章目录 一、Numpy的基础知识实验1 生成由随机数组成的三通道图片&#xff0c;分别显示每个维度图片&#xff0c;并将三个通道的像素四周进行填充&#xff0c;分别从上下左右各填充若干数据。 二、Numpy的线性代数运算实验2 请准备一张图片&#xff0c;按照上面的过程进行矩阵…

C#月数计算器(主要用于社保、医保缴费月数计算)

1、为什么做这个&#xff1f; 工作中&#xff0c;经常需要计算参保人社保、医保缴费月数&#xff0c;之前都是在Excel中写一个DATEDIF公式&#xff0c;修改单元格中的日期&#xff0c;计算间隔的月数&#xff0c;公式如下&#xff1a; DATEDIF(起始日期, 终止日期, 返回类型) …

【wxWidgets】剪贴板和拖放操作

【wxWidgets】剪贴板和拖放操作 使用剪贴板传输数据时应用程序间的一种交互方式 剪贴板和拖放操作在wxWidgets中共享了一些类来实现数据的传输 数据对象 wxDataObject类时剪贴板操作和拖放操作的核心&#xff0c;该类实例代表了拖放操作中鼠标拖拽的事物和剪贴板中拷贝和粘贴…

Stephen Wolfram:一次只添加一个词

It’s Just Adding One Word at a Time 一次只添加一个词 That ChatGPT can automatically generate something that reads even superficially like human-written text is remarkable, and unexpected. But how does it do it? And why does it work? My purpose here is t…

mysql进阶1——proxysql中间件

文章目录 一、基本了解二、安装部署三、proxysql管理配置3.1 内置库3.1.1 main库表3.1.2 stats库表3.1.3 monitor库 3.2 常用管理变量3.2.1 添加管理用户3.2.2 添加普通用户3.2.3 修改监听套接字 四、多层配置系统4.1 系统结构4.2 修改变量加载配置4.3 启动加载流程 一、基本了…

单机和集群以及分布式的浅析

假设一个大系统分为A、B、C、D、E五个模块&#xff0c;也可以认为是五个基本的服务&#xff0c;该系统靠这五个模块协同工作&#xff0c;共同为用户提供服务。 单机 单机&#xff1a;显然&#xff0c;单机表名该系统完完全全的部署在该台机器上&#xff0c;拥有完整的服务&am…

集成学习——Boosting算法:Adaboost、GBDT、XGBOOST和lightGBM的简要原理和区别

1、Boosting算法 Boosting算法是通过串联的方式&#xff0c;将一组弱学习器提升为强学习器算法。它的工作机制如下&#xff1a; &#xff08;1&#xff09;用初始训练集训练出一个基学习器&#xff1b; &#xff08;2&#xff09;依据基学习器的表现对训练样本分布进行调整&…

opencv 图像距离变换 distanceTransform

图像距离变换&#xff1a;计算图像中每一个非零点距离离自己最近的零点的距离&#xff0c;然后通过二值化0与非0绘制图像。 #include "iostream" #include "opencv2/opencv.hpp" using namespace std; using namespace cv;int main() {Mat img, dst, dst…

洛必达法则和分部积分的应用之计算数学期望EX--概率论浙大版填坑记

如下图所示&#xff0c;概率论与数理统计浙大第四版有如下例题&#xff1a; 简单说就是&#xff1a;已知两个相互独立工作电子装置寿命的概率密度函数&#xff0c;将二者串联成整机&#xff0c;求整机寿命的数学期望。 这个题目解答中的微积分部分可谓是相当的坑爹&#xff0c;…

vue/cli 自定义配置

vue/cli 自定义配置 1、更改默认的端口号8080 只需要更改vue.config.js文件 1、更改默认的端口号8080 只需要更改vue.config.js文件

脑电信号处理与特征提取——4.脑电信号的预处理及数据分析要点(彭微微)

目录 四、脑电信号的预处理及数据分析要点 4.1 脑电基础知识回顾 4.2 伪迹 4.3 EEG预处理 4.3.1 滤波 4.3.2 重参考 4.3.3 分段和基线校正 4.3.4 坏段剔除 4.3.5 坏导剔除/插值 4.3.6 独立成分分析ICA 4.4 事件相关电位&#xff08;ERPs&#xff09; 4.4.1 如何获…

什么是UE像素流送,像素流推流是什么原理?

游戏开发者通常在运行游戏逻辑时会将游戏渲染到屏幕的同一台设备上来运行虚幻引擎应用&#xff0c;多人联网游戏可能会在应用程序的多个实例之间分发部分游戏逻辑&#xff0c;但每个单独的实例仍然会为自己的玩家在本地渲染游戏。即使是使用 HTML5 部署选项创建可以在 Web 浏览…

解决@Scope(“prototype“)不生效的问题

目录 Scope(“prototype“)不生效Scope(“prototype“)正确用法——解决Bean多例问题 1.问题&#xff0c;Spring管理的某个Bean需要使用多例2.问题升级3. Spring给出的解决问题的办法&#xff08;解决Bean链中某个Bean需要多例的问题&#xff09; Scope(“prototype“)不生效 …