상세 컨텐츠

본문 제목

DHTMLX Gantt

자바스크립트

by e7e 2023. 11. 3. 19:20

본문

간트차트로는  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">
    -->

 

홈페이지의 샘플데이터의 날짜 포맷이 영어권 포맷이어서 우리 스타일로 바꾼 샘플데이터

코드로 붙이자닝 역시 너무 기럭지가 김!  (그냥 다운받장!)

sampleData.json
0.01MB

 

위 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

 

관련글 더보기