간트차트로는 https://dhtmlx.com/docs/products/dhtmlxGantt/ 가 문서 양이 많긴 하지만
그만큼 기능이 잘 되어 있다는 의미이기도 하닝, 쓸만해 보인당.
조금 느린거시 아닌가 하는 생각이 쪼메씩 고개 들기도 하지만,
현재로선 따로 좋은 대안을 찾지 못했당.(직접 맹글깡?~~)
pro(유료말공) standard 다운로드(무료) 하거나
https://dhtmlx.com/x/download/regular/dhtmlxGantt.zip
그냥 아래 코드 복사/붙여넣기로 넣장 (추가 필요한 라이브러리가 있다면 알아서 추강!)
<link rel="stylesheet" href="https://docs.dhtmlx.com/gantt/codebase/dhtmlxgantt.css">
<script src="https://docs.dhtmlx.com/gantt/codebase/dhtmlxgantt.js"></script>
포맷설명: https://docs.dhtmlx.com/gantt/desktop__date_format.html
사용가능 skin은 아래와 같당. 골라서 붙인당!
<!-- available skins
<link rel="stylesheet" href="https://cdn.dhtmlx.com/gantt/edge/dhtmlxgantt.css">
<link rel="stylesheet" href="https://docs.dhtmlx.com/gantt/codebase/skins/dhtmlxgantt.css">
<link rel="stylesheet" href="https://docs.dhtmlx.com/gantt/codebase/skins/dhtmlxgantt_skyblue.css">
<link rel="stylesheet" href="https://docs.dhtmlx.com/gantt/codebase/skins/dhtmlxgantt_meadow.css">
<link rel="stylesheet" href="https://docs.dhtmlx.com/gantt/codebase/skins/dhtmlxgantt_broadway.css">
<link rel="stylesheet" href="https://docs.dhtmlx.com/gantt/codebase/skins/dhtmlxgantt_contrast_black.css">
<link rel="stylesheet" href="https://docs.dhtmlx.com/gantt/codebase/skins/dhtmlxgantt_contrast_white.css">
<link rel="stylesheet" href="https://docs.dhtmlx.com/gantt/codebase/skins/dhtmlxgantt_material.css">
-->
홈페이지의 샘플데이터의 날짜 포맷이 영어권 포맷이어서 우리 스타일로 바꾼 샘플데이터
코드로 붙이자닝 역시 너무 기럭지가 김! (그냥 다운받장!)
위 json 파일은 아래 html문서와 같은 폴더에 넣고, 서버를 실행하장.
억지로 합쳐버린 sampleGantt.html
<!DOCTYPE html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>Gantt chart with resource panel</title>
<link rel="stylesheet" href="https://docs.dhtmlx.com/gantt/codebase/dhtmlxgantt.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/spectrum/1.8.1/spectrum.min.css">
<script src="https://docs.dhtmlx.com/gantt/codebase/dhtmlxgantt.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/spectrum/1.8.1/spectrum.min.js"></script>
<style>
html,
body {
padding: 0px;
margin: 0px;
height: 100%;
}
.gantt_grid_scale .gantt_grid_head_cell,
.gantt_task .gantt_task_scale .gantt_scale_cell {
font-weight: bold;
font-size: 14px;
color: rgba(0, 0, 0, 0.7);
}
.task-color-cell {
vertical-align: middle;
width: 12px;
height: 12px;
border: 1px solid #cecece;
display: inline-block;
border-radius: 6px;
}
</style>
</head>
<body>
<h1 style="text-align: center;background-color: black;color:yellow">DHTMLX GANTT 예젱</h1>
<div id="e7eGantt" style='width:99vw; height:93vh;'></div>
<script>
function calculateResourceLoad(tasks, scale) {
var step = scale.unit;
var timegrid = {};
for (var i = 0; i < tasks.length; i++) {
var task = tasks[i];
var currDate = gantt.date[step + "_start"](new Date(task.start_date));
while (currDate < task.end_date) {
var date = currDate;
currDate = gantt.date.add(currDate, 1, step);
if (!gantt.isWorkTime({ date: date, task: task })) {
continue;
}
var timestamp = date.valueOf();
if (!timegrid[timestamp])
timegrid[timestamp] = 0;
timegrid[timestamp] += 8;
}
}
var timetable = [];
var start, end;
for (var i in timegrid) {
start = new Date(i * 1);
end = gantt.date.add(start, 1, step);
timetable.push({
start_date: start,
end_date: end,
value: timegrid[i]
});
}
return timetable;
}
var renderResourceLine = function (resource, timeline) {
var tasks = gantt.getTaskBy("user", resource.id);
var timetable = calculateResourceLoad(tasks, timeline.getScale());
var row = document.createElement("div");
for (var i = 0; i < timetable.length; i++) {
var day = timetable[i];
var css = "";
if (day.value <= 8) {
css = "gantt_resource_marker gantt_resource_marker_ok";
} else {
css = "gantt_resource_marker gantt_resource_marker_overtime";
}
var sizes = timeline.getItemPosition(resource, day.start_date, day.end_date);
var el = document.createElement('div');
el.className = css;
el.style.cssText = [
'left:' + sizes.left + 'px',
'width:' + sizes.width + 'px',
'position:absolute',
'height:' + (gantt.config.row_height - 1) + 'px',
'line-height:' + sizes.height + 'px',
'top:' + sizes.top + 'px'
].join(";");
el.innerHTML = day.value;
row.appendChild(el);
}
return row;
};
var resourceLayers = [
renderResourceLine,
"taskBg"
];
// 색깔 에디터 추가
let editor;
gantt.config.editor_types.color = {
show: function (id, column, config, placeholder) {
var html = "<div><input type='color' name='" + column.name + "'></div>";
placeholder.innerHTML = html;
editor = $(placeholder).find("input").spectrum({
change: () => {
gantt.ext.inlineEditors.save();
}
});
setTimeout(() => {
editor.spectrum("show")
})
},
hide: function () {
if (editor) {
editor.spectrum("destroy");
editor = null;
}
},
set_value: function (value, id, column, node) {
editor.spectrum("set", value);
},
get_value: function (id, column, node) {
return editor.spectrum("get").toHexString();
},
is_changed: function (value, id, column, node) {
// console.log("THIS: ", this);
var newValue = this.get_value(id, column, node);
return newValue !== value;
},
is_valid: function (value, id, column, node) {
var newValue = this.get_value(id, column, node);
return !!newValue;
},
save: function (id, column, node) {
// only for inputs with map_to:auto. complex save behavior goes here
},
focus: function (node) {
editor.spectrum("show");
}
}
const colorEditor = { type: "color", map_to: "color" };
var mainGridConfig = {
columns: [
{ name: "text", tree: true, width: 200, resize: true },
{ name: "start_date", align: "center", width: 80, resize: true },
{
name: "owner", align: "center", width: 60, label: "담당자", template: function (task) {
var store = gantt.getDatastore("resources");
var owner = store.getItem(task.user);
if (owner) {
return owner.label;
} else {
return "디폴트";
}
}
},
{ name: "duration", width: 50, align: "center" },
{
name: "color", label: "색깔", align: "center", width: 50, resize: true, editor: colorEditor,
template: (task) => {
if (!task.color) {
if (task.type == "project") task.color = "#7fbc64";
if (task.type == "milestone") task.color = "#d18c7f";
}
task.color = task.color || "#7e9667";
return `<div class="task-color-cell" style="background:${task.color}"></div>`;
}
},
{ name: "add", width: 44 }
]
};
var resourcePanelConfig = {
columns: [
{
name: "name", label: "Name", align: "center", template: function (resource) {
//console.log("체킁:", resource);
return resource.label;
}
},
{
name: "workload", label: "Workload", align: "center", template: function (resource) {
var tasks = gantt.getTaskBy("user", resource.id);
var totalDuration = 0;
for (var i = 0; i < tasks.length; i++) {
totalDuration += tasks[i].duration;
}
return (totalDuration || 0) * 8 + "";
}
}
]
};
gantt.config.layout = {
css: "gantt_container",
rows: [
{
cols: [
{ view: "grid", group: "grids", config: mainGridConfig, scrollY: "scrollVer" },
{ resizer: true, width: 1, group: "vertical" },
{ view: "timeline", id: "timeline", scrollX: "scrollHor", scrollY: "scrollVer" },
{ view: "scrollbar", id: "scrollVer", group: "vertical" }
]
},
{ resizer: true, width: 1 },
{
config: resourcePanelConfig,
cols: [
{
view: "grid",
id: "resourceGrid",
group: "grids",
bind: "resources",
scrollY: "resourceVScroll"
},
{ resizer: true, width: 1, group: "vertical" },
{
view: "timeline",
id: "resourceTimeline",
bind: "resources",
bindLinks: null,
layers: resourceLayers,
scrollX: "scrollHor",
scrollY: "resourceVScroll"
},
{ view: "scrollbar", id: "resourceVScroll", group: "vertical" }
]
},
{ view: "scrollbar", id: "scrollHor" }
]
};
var resourcesStore = gantt.createDatastore({
name: "resources",
initItem: function (item) {
item.id = item.key || gantt.uid();
return item;
}
});
var tasksStore = gantt.getDatastore("task");
//console.log("체킁taskStore:", tasksStore);
tasksStore.attachEvent("onStoreUpdated", function (id, item, mode) {
resourcesStore.refresh();
});
// 기본 설정
gantt.i18n.setLocale("kr");
gantt.config.scales = [
{ unit: "month", step: 1, format: "%Y, %F" },
{ unit: "day", step: 1, format: "%j, %D" }
];
//날짜 형식
gantt.config.date_format = "%Y-%m-%d %H:%i"; // 실제 전달되는 데이타의 start_date등의 포맷
gantt.config.task_date = "%Y년 %m월 %d일";
gantt.init("e7eGantt");
resourcesStore.parse([// resources
{ key: '0', label: "없음" },
{ key: '1', label: "E7E" },
{ key: '2', label: "메롱" },
{ key: '3', label: "황당" },
{ key: '4', label: "당황" },
{ key: '5', label: "우앙" },
{ key: '6', label: "오호" },
{ key: '7', label: "눈물" }
]);
// 비동기롱 데이터 가져오깅, 괘니 fetch 써보깅
const fData = async () => {
let response = await fetch("./sampleData.json");
let taskData = await response.json();
gantt.parse(taskData);
}
fData();
</script>
실행하닝, 아래와 같은 결과가 나왔당. 그대로 쓰거낭,
커스터마이징을 원한다면 문서를 쪼메 욜씨미 읽는 시간 투자가 필요하당!
쌀쌀함의 그늘아래 그리움의 씨앗이 알아서 싹을 연다.
그립다. 멀어지는 뒷모습
추앙한다. 쫓아가고 싶은 걸음걸이
그 꽃이 피어야 , 지고, 어떤 꽃이 필텐데.....
달아나는 눈물 보다 뒷모습에 그려진 찬란한 걸음걸이
DNA의 오류는 급발진을 부른당.
연신 고개만 돌려질 뿐, 딴 곳을 재촉하는 내 걸음 걸음
그저 볼 수 없으닝, 그립당
그....녀가 원한다면 그럴 것이다.
https://www.youtube.com/watch?v=5MyP1LrGAEU
이벤트 동적 바인딩 당신만 몰라용!! (2) | 2023.11.23 |
---|---|
검색 드롭다운 원리 (Search Drop Down) (4) | 2023.11.06 |
GridStack 가안딴 사용법 (1) | 2023.09.08 |
FullCalendar(풀캘린더) 어거지 사용법 (6) | 2023.09.06 |
브라우져에서 자바스크립트 Module 사용하당 (0) | 2023.09.06 |