dhtmlxGantt.js右键菜单context-menu二次开发甘特图组件插件Demo gantt-charts

dhtmlxGantt.js右键菜单context-menu二次开发甘特图组件插件Demo gantt-charts

https://github.com/DHTMLX/gantt
demo 功能点:
  • js实现甘特图鼠标的右键菜单弹出面板
  • dhtmlxGantt.js 在甘特图任务上右键弹出复制、删除、移动等菜单,可以参考本demo实现
  • dhtmlxGantt.js 在线示例demo 例子example
  • dhtmlxGantt.js二次开发修改 onContextMenu event 鼠标右键菜单事件
  • dhtmlxGantt.js add context menu dialog
  • dhtmlxGantt.js online demo gantt-charts 网页版
  • dhtmlx Gantt.js 入门教程 案例
  • dhtmlxGantt.js demo 源码下载 csdn
  • 网页版 项目计划、任务图表:甘特图
  • 开源甘特图 js 插件 demo 在线版
demo 完整的源代码
cdn集成
https://gcore.jsdelivr.net/npm/dhtmlx-gantt@7.1.12/codebase/dhtmlxgantt.js
https://gcore.jsdelivr.net/npm/dhtmlx-gantt@7.1.12/codebase/dhtmlxgantt.css

demo 源码下载

<script src="https://gcore.jsdelivr.net/npm/dhtmlx-gantt@7.1.12/codebase/dhtmlxgantt.js"></script>
<link rel="stylesheet" href="https://gcore.jsdelivr.net/npm/dhtmlx-gantt@7.1.12/codebase/dhtmlxgantt.css">

<style>
	.weekend {
		background: #f4f7f4 !important;
	}

	[data-column-name='constraint_type'] .gantt_tree_content {
		padding: 1px;
		line-height: 17px;
		white-space: normal;
		text-align: right;
		box-sizing: border-box;
	}

	.gantt_grid_editor_placeholder[data-column-name='constraint_type'] select {
		line-height: 20px;
		white-space: normal;
	}

	.constraint-marker {
		position: absolute;

		-moz-box-sizing: border-box;
		box-sizing: border-box;

		width: 56px;
		height: 56px;
		margin-top: -11px;

		opacity: 0.4;
		z-index: 1;
		background: url("../common/constraint-arrow.svg") center no-repeat;
		background-size: cover;
	}

	.constraint-marker.earliest-start {
		margin-left: -53px;
	}

	.constraint-marker.latest-end {
		margin-left: -3px;
		transform: rotate(180deg);
	}

	.gantt_link_control {
		z-index: 1;
	}
</style>

<div id="gantt_here" style="width:100%; height:500px;"></div>
<script>
	gantt.plugins({
		auto_scheduling: true,
		marker: true
	});

	gantt.templates.scale_cell_class = function (date) {
		if (date.getDay() == 0 || date.getDay() == 6) {
			return "weekend";
		}
	};
	gantt.templates.timeline_cell_class = function (item, date) {
		if (date.getDay() == 0 || date.getDay() == 6) {
			return "weekend";
		}
	};

	gantt.config.work_time = true;
	gantt.config.min_column_width = 45;
	gantt.config.auto_scheduling = true;

	gantt.config.schedule_from_end = false;
	gantt.config.project_start = new Date(2019, 2, 1);

	if (gantt.addMarker) {
		gantt.addMarker({
			start_date: gantt.config.project_start,
			text: "project start"
		});
	}

	gantt.config.date_format = "%d-%m-%Y";

	var textEditor
	var dateEditor
	var durationEditor
	var constraintTypeEditor
	var constraintDateEditor

	gantt.config.columns = [
		{ name: "text", tree: true, width: '*', resize: true, width: 150, editor: textEditor },
		{ name: "start_date", align: "center", resize: true, width: 150, editor: dateEditor },
		{ name: "duration", align: "center", width: 80, resize: true, editor: durationEditor },
		{
			name: "constraint_type", align: "center", width: 100, template: function (task) {
				// return gantt.locale.labels[gantt.getConstraintType(task)];
			}, resize: true, editor: constraintTypeEditor
		},
		{
			name: "constraint_date", align: "center", width: 120, template: function (task) {
				var constraintTypes = gantt.config.constraint_types;

				if (task.constraint_date && task.constraint_type != constraintTypes.ASAP && task.constraint_type != constraintTypes.ALAP) {
					return gantt.templates.task_date(task.constraint_date);
				}
				return "";
			}, resize: true, editor: constraintDateEditor
		},
		{ name: "add", width: 44 }
	];


	function renderDiv(task, date, className) {
		var el = document.createElement('div');
		el.className = className;
		var sizes = gantt.getTaskPosition(task, date);
		el.style.left = sizes.left + 'px';
		el.style.top = sizes.top + 'px';
		return el;
	}

	gantt.config.lightbox.sections = [
		{ name: "description", height: 38, map_to: "text", type: "textarea", focus: true },
		{ name: "constraint", type: "constraint" },
		{ name: "time", type: "duration", map_to: "auto" }
	];

	gantt.attachEvent("onAfterTaskAutoSchedule", function (task, new_date, link, predecessor) {
		var reason = "";
		if (predecessor) {
			reason = predecessor.text;
		} else {
			// var constraint = gantt.getConstraintType(task);
			// reason = gantt.locale.labels[constraint];
		}
		var predecessor = predecessor ? predecessor : { text: task.constraint_type };
		console.log("<b>" + task.text + "</b> has been rescheduled to " + gantt.templates.task_date(new_date) + " due to <b>" + reason + "</b> constraint");
	});
	gantt.message({ text: "Project is scheduled as soon as possible starting from the project start date", expire: -1 });
	gantt.init("gantt_here");
	gantt.parse({
		data: [
			{ id: 11, text: "Project #1", type: "project", progress: 0.6, open: true },
			{ id: 12, text: "Task #1", start_date: "02-04-2018", duration: "5", parent: "11", progress: 1, open: true },
			{ id: 13, text: "Task #2", start_date: "03-04-2018", type: "project", parent: "11", progress: 0.5, open: true },
			{ id: 14, text: "Task #3", start_date: "02-04-2018", duration: "6", parent: "11", progress: 0.8, open: true },
			{ id: 15, text: "Task #4", type: "project", parent: "11", progress: 0.2, open: true },
			{ id: 16, text: "Final milestone", start_date: "15-04-2018", type: "milestone", parent: "11", progress: 0, open: true },
			{ id: 17, text: "Task #2.1", start_date: "03-04-2018", duration: "2", parent: "13", progress: 1, open: true },
			{ id: 18, text: "Task #2.2", start_date: "06-04-2018", duration: "3", parent: "13", progress: 0.8, open: true },
			{ id: 19, text: "Task #2.3", start_date: "10-04-2018", duration: "4", parent: "13", progress: 0.2, open: true },
			{ id: 20, text: "Task #2.4", start_date: "10-04-2018", duration: "4", parent: "13", progress: 0, open: true },
			{ id: 21, text: "Task #4.1", start_date: "02-04-2018", duration: "4", parent: "15", progress: 0.5, open: true },
			{ id: 22, text: "Task #4.2", start_date: "02-04-2018", duration: "4", parent: "15", progress: 0.1, open: true },
			{ id: 23, text: "Mediate milestone", start_date: "14-04-2018", type: "milestone", parent: "15", progress: 0, open: true }
		],
		links: [
			{ id: "14", source: "13", target: "15", type: "0" },
			{ id: "15", source: "15", target: "16", type: "0" },
			{ id: "16", source: "17", target: "18", type: "0" },
			{ id: "17", source: "18", target: "19", type: "0" },
			{ id: "18", source: "19", target: "20", type: "0" }
		]
	});

</script>

<script>
	gantt.attachEvent("onContextMenu", function (taskId, linkId, event) {
		console.log('onContextMenu');
		console.log(taskId, linkId, event);
		var x = event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft,
			y = event.clientY + document.body.scrollTop + document.documentElement.scrollTop;

		var btn = document.createElement('button')
		btn.innerHTML = '复制'
		btn.onclick = function () {
			console.log(taskId, linkId, event);
			btn.remove()
			alert(taskId)
		}
		btn.style.cssText = `position: fixed; left: ${x}px; top: ${y}px;`
		if (taskId) document.body.append(btn)

		// TODO 真正集成到项目使用,需处理:其他地方点击,隐藏右键按钮,不处理就会导致右键菜单一直在那儿
	});
</script>