• 甘特图的这些新玩法,你都知道吗?


    摘要:本文由葡萄城技术团队于博客园发布。转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具、解决方案和服务,赋能开发者。

    前言

    甘特图是项目管理、生产排程、节点管理中非常常见的一个功能。那么,有没有一种方法能够帮助将甘特图引入到系统中,让数据的进度、排程数据的展示更加直观,让管理更加高效。

    本葡萄今天为大家带来一个新的插件——甘特图插件。该插件是结合 SpreadJS 本身强大电子表格能力提供的一种全新的可视化展示方案,能够有效地将生产计划、预算排期中的日程管理信息展示出来,显示关键路径上关键节点的进展或状态。其数据来自与集算表同源的数据管理器(DataManager),通过绑定 -> 视图的方式,将层级化的任务数据通过甘特图插件进行展示。

    (通过甘特图插件展示层级化的数据)

    1. 缩放和时间刻度滚动

    (通过甘特图插件实现缩放及刻度滚动)

    JavaScript代码:

    var myTable;
    var ganttSheet;
    var adjustTierUnit = true;
    
    window.onload = function() {
        var spread = new GC.Spread.Sheets.Workbook(document.getElementById("ss"), { sheetCount: 0 });
        initSpread(spread);
        initSplitView(spread);
    };
    
    function initSpread(spread) {
        spread.suspendPaint();
        initDataSource(spread);
        initGanttSheet(spread);
        spread.resumePaint();
    }
    
    function initDataSource(spread) {
        var tableName = "Gantt_Id";
        var baseApiUrl = getBaseApiUrl();
        var apiUrl = baseApiUrl + "/" + tableName;
        var dataManager = spread.dataManager();
        myTable = dataManager.addTable("myTable", {
            batch: true,
            remote: {
                read: {
                    url: apiUrl
                }
            },
            schema: {
                hierarchy: {
                    type: "Parent",
                    column: "parentId"
                },
                columns: {
                    id: { isPrimaryKey: true },
                    taskNumber: { dataType: "rowOrder" }
                }
            }
        });
    }
    
    function initGanttSheet(spread) {
        ganttSheet = spread.addSheetTab(0, "GanttSheet", GC.Spread.Sheets.SheetType.ganttSheet);
        var view = myTable.addView("ganttView", [
            { value: "taskNumber", caption: "NO.", width: 60 },
            { value: "name", caption: "Task Name", width: 200 },
            { value: "duration", caption: "Duration", width: 90 },
            { value: "predecessors", caption: "Predecessors", width: 120 }
        ]);
        view.fetch().then(function() {
            ganttSheet.bindGanttView(view);
        }).then(function() {
            ganttSheet.project.timescale.zoomOut();
        });
        initSidePanel(ganttSheet);
    }
    

    2.任务栏和网格线的样式调整

    (通过甘特图插件实现的任务栏和网格线的样式)

    JavaScript代码:

    /*REPLACE_MARKER*/
    /*DO NOT DELETE THESE COMMENTS*/
    var myTable;
    var ganttSheet;
    
    window.onload = function() {
        var spread = new GC.Spread.Sheets.Workbook(document.getElementById("ss"), { sheetCount: 0 });
        initSpread(spread);
        initSplitView(spread);
    };
    
    function initSpread(spread) {
        spread.suspendPaint();
        initDataSource(spread);
        initGanttSheet(spread);
        spread.resumePaint();
    }
    
    function initDataSource(spread) {
        var tableName = "Gantt_Id";
        var baseApiUrl = getBaseApiUrl();
        var apiUrl = baseApiUrl + "/" + tableName;
        var dataManager = spread.dataManager();
        myTable = dataManager.addTable("myTable", {
            batch: true,
            remote: {
                read: {
                    url: apiUrl
                }
            },
            schema: {
                hierarchy: {
                    type: "Parent",
                    column: "parentId"
                },
                columns: {
                    id: { isPrimaryKey: true },
                    taskNumber: { dataType: "rowOrder" }
                }
            }
        });
    }
    
    function initGanttSheet(spread) {
        ganttSheet = spread.addSheetTab(0, "GanttSheet", GC.Spread.Sheets.SheetType.ganttSheet);
        var view = myTable.addView("ganttView", [
            { value: "taskNumber", caption: "NO.", width: 60 },
            { value: "name", caption: "Task Name", width: 200 },
            { value: "duration", caption: "Duration", width: 90 },
            { value: "predecessors", caption: "Predecessors", width: 120, visible: false }
        ]);
        view.fetch().then(function() {
            ganttSheet.bindGanttView(view);
            ganttSheet.project.timescale.tierMode = GC.Spread.Sheets.GanttSheet.TimescaleTierMode.topMiddleBottom;
            ganttSheet.project.timescale.topTier.unit = GC.Spread.Sheets.GanttSheet.TimescaleUnit.thirdsOfMonth;
        }).then(function() {
            ganttSheet.gridlines.bottomTierColumn = {
                lineType: GC.Spread.Sheets.GanttSheet.GanttGridlineType.dashed,
                lineColor: "#c85b11"
            };
            ganttSheet.gridlines.ganttRows.lineType = GC.Spread.Sheets.GanttSheet.GanttGridlineType.thin;
            ganttSheet.gridlines.ganttRows.lineColor = "#abd08f";
            ganttSheet.gridlines.ganttRows.interval = 2;
            ganttSheet.gridlines.ganttRows.intervalLineType = GC.Spread.Sheets.GanttSheet.GanttGridlineType.dashDot;
            ganttSheet.gridlines.ganttRows.intervalLineColor = "#9cc3e5";
        });
        initSidePanel(ganttSheet);
    }
    
    function initSidePanel(ganttSheet) {
        var setButton = document.getElementById("set-gridline");
        var intervalPanel = document.getElementById("interval-panel");
        var affectItemOption = document.getElementById("affectItem");
        var lineTypeOption = document.getElementById("lineType");
        var lineColorOption = document.getElementById("lineColor");
        var intervalLineTypeOption = document.getElementById("intervalLineType");
        var intervalLineColorOption = document.getElementById("intervalLineColor");
        var intervalOption = document.getElementById("interval");
    
        var option = convertToNormalOptions(ganttSheet, "ganttRows");
        syncOptionToPanel(option);
    
        affectItemOption.addEventListener('change', function(e) {
            var target = affectItemOption.value;
            if (affectItemOption.value === "ganttRows") {
                intervalPanel.classList.remove("hide");
            } else {
                intervalPanel.classList.add("hide");
            }
            var option = convertToNormalOptions(ganttSheet, target);
            syncOptionToPanel(option);
        });
        setButton.addEventListener('click', function() {
            var target = affectItemOption.value;
            var lineType = Number(lineTypeOption.value);
            var lineColor = lineColorOption.value;
            var intervalLineType = Number(intervalLineTypeOption.value);
            var intervalLineColor = intervalLineColorOption.value;
            var interval = Number(intervalOption.value);
            var option = convertToGanttGridlineOptions(lineType, lineColor, intervalLineType, intervalLineColor, interval);
            if (option) {
                ganttSheet.gridlines[target] = option;
            }
        });
    

    3.数据展示布局调整

    (通过甘特图插件实现布局的调整)

    JavaScript代码:

    /*REPLACE_MARKER*/
    /*DO NOT DELETE THESE COMMENTS*/
    var myTable;
    var ganttSheet;
    var roundBarsToWholeDays = true;
    
    window.onload = function() {
        var spread = new GC.Spread.Sheets.Workbook(document.getElementById("ss"), { sheetCount: 0 });
        initSpread(spread);
        initSplitView(spread);
    };
    
    function initSpread(spread) {
        spread.suspendPaint();
        initDataSource(spread);
        initGanttSheet(spread);
        spread.resumePaint();
    }
    
    function initDataSource(spread) {
        var tableName = "Gantt_Id";
        var baseApiUrl = getBaseApiUrl();
        var apiUrl = baseApiUrl + "/" + tableName;
        var dataManager = spread.dataManager();
        myTable = dataManager.addTable("myTable", {
            batch: true,
            remote: {
                read: {
                    url: apiUrl
                }
            },
            schema: {
                hierarchy: {
                    type: "Parent",
                    column: "parentId"
                },
                columns: {
                    id: { isPrimaryKey: true },
                    taskNumber: { dataType: "rowOrder" }
                }
            }
        });
    }
    
    function initGanttSheet(spread) {
        ganttSheet = spread.addSheetTab(0, "GanttSheet", GC.Spread.Sheets.SheetType.ganttSheet);
        var view = myTable.addView("ganttView", [
            { value: "taskNumber", caption: "NO.", width: 60 },
            { value: "name", caption: "Task Name", width: 200 },
            { value: "duration", caption: "Duration", width: 90 },
            { value: "predecessors", caption: "Predecessors", width: 120, visible: false }
        ]);
        view.fetch().then(function() {
            ganttSheet.bindGanttView(view);
            ganttSheet.suspendPaint();
            var summaryStyleRule = ganttSheet.project.taskStyleRules.getRule("summary");
            var summaryStyle = summaryStyleRule.style.taskbarStyle;
            summaryStyle.leftText = "start";
            summaryStyle.rightText = "finish";
            summaryStyleRule.style.taskbarStyle = summaryStyle;
            var layout = ganttSheet.project.layout;
            layout.linkLineMode = "noLinks";
            layout.barHeight = 18;
            ganttSheet.resumePaint();
        });
        initSidePanel(ganttSheet);
    }
    
    function initSidePanel(ganttSheet) {
        var roundBarItem = document.getElementById("round-bar");
        var linkTypeItem = document.getElementById("link-type");
        var barHeightItem = document.getElementById("bar-height");
        var dateFormatItem = document.getElementById("date-format");
        var setLayoutItem = document.getElementById("set-layout");
    
        roundBarItem.addEventListener("click", function() {
            if (roundBarItem.classList.contains("active")) {
                roundBarItem.classList.remove("active");
                roundBarsToWholeDays = false;
            } else {
                roundBarItem.classList.add("active");
                roundBarsToWholeDays = true;
            }
        });
    
        setLayoutItem.addEventListener("click", function() {
            var layout = ganttSheet.project.layout;
            ganttSheet.suspendPaint();
            layout.linkLineMode = linkTypeItem.value;
            layout.barHeight = Number(barHeightItem.value);
            layout.barTextDateFormat = dateFormatItem.value;
            layout.roundBarsToWholeDays = roundBarsToWholeDays;
            ganttSheet.resumePaint();
        });
    }
    

    4.任务操作

    (可在甘特图插件实现任务操作)

    JavaScript代码:

    /*REPLACE_MARKER*/
    /*DO NOT DELETE THESE COMMENTS*/
    var myTable;
    var ganttSheet;
    var roundBarsToWholeDays = true;
    
    window.onload = function () {
        var spread = new GC.Spread.Sheets.Workbook(document.getElementById("ss"), { sheetCount: 0 });
        initSpread(spread);
        initSplitView(spread);
    };
    
    function initSpread(spread) {
        spread.suspendPaint();
        initDataSource(spread);
        initGanttSheet(spread);
        spread.resumePaint();
    }
    
    function initDataSource(spread) {
        var tableName = "Gantt_Id";
        var baseApiUrl = getBaseApiUrl();
        var apiUrl = baseApiUrl + "/" + tableName;
        var dataManager = spread.dataManager();
        myTable = dataManager.addTable("myTable", {
            batch: true,
            remote: {
                read: {
                    url: apiUrl
                },
                batch: {
                    url: apiUrl + "Collection"
                }
            },
            schema: {
                hierarchy: {
                    type: "Parent",
                    column: "parentId"
                },
                columns: {
                    id: { isPrimaryKey: true },
                    taskNumber: { dataType: "rowOrder" }
                }
            }
        });
    }
    
    function initGanttSheet(spread) {
        ganttSheet = spread.addSheetTab(0, "GanttSheet", GC.Spread.Sheets.SheetType.ganttSheet);
        var view = myTable.addView("ganttView", [
            { value: "id", caption: "Id", width: 40 },
            { value: "taskNumber", caption: "NO.", width: 60 },
            { value: "name", caption: "Task Name", width: 200 },
            { value: "duration", caption: "Duration", width: 90 },
            { value: "predecessors", caption: "Predecessors", width: 120 }
        ]);
        view.fetch().then(function () {
            ganttSheet.bindGanttView(view);
        });
        initSidePanel(ganttSheet);
    }
    
    function initSidePanel(ganttSheet) {
        var addTaskItem = document.getElementById("add-task");
        var addSummayItem = document.getElementById("add-summary");
        var addMilestoneItem = document.getElementById("add-milestone");
    
        var deleteItem = document.getElementById("delete-task");
    
        var indentTaskItem = document.getElementById("indent-task");
        var outdentTaskItem = document.getElementById("outdent-task");
    
        var linkTaskItem = document.getElementById("link-task");
        var unlinkTaskItem = document.getElementById("unlink-task");
    
        var submitItem = document.getElementById("submit-changes");
        var cancelItem = document.getElementById("cancel-changes");
    
        addTaskItem.addEventListener("click", function () {
            ganttSheet.suspendPaint()
            addTask();
            ganttSheet.resumePaint();
        });
        addSummayItem.addEventListener("click", function () {
            ganttSheet.suspendPaint()
            addSummary();
            ganttSheet.resumePaint();
        });
        addMilestoneItem.addEventListener("click", function () {
            ganttSheet.suspendPaint()
            addMilestone();
            ganttSheet.resumePaint();
        });
    
        deleteItem.addEventListener("click", function () {
            ganttSheet.suspendPaint()
            deleteTask();
            ganttSheet.resumePaint();
        });
    
        indentTaskItem.addEventListener("click", function () {
            ganttSheet.suspendPaint()
            indentTask();
            ganttSheet.resumePaint();
        });
        outdentTaskItem.addEventListener("click", function () {
            ganttSheet.suspendPaint()
            outdentTask();
            ganttSheet.resumePaint();
        });
    
        linkTaskItem.addEventListener("click", function () {
            ganttSheet.suspendPaint()
            linkTask();
            ganttSheet.resumePaint();
        });
        unlinkTaskItem.addEventListener("click", function () {
            ganttSheet.suspendPaint()
            unlinkTask();
            ganttSheet.resumePaint();
        });
    
        submitItem.addEventListener("click", function () {
            ganttSheet.submitChanges();
        });
        cancelItem.addEventListener("click", function () {
            ganttSheet.cancelChanges();
        });
    
        function addTask() {
            var project = ganttSheet.project;
            var selections = ganttSheet.getSelections();
            var insertedRow = Math.min(...selections.map(r => r.row), project.tasks.length)
            var rowCount = selections.map(r => r.rowCount).reduce((pre, current) => { return pre + current });
            var taskData = createTaskDataList(rowCount, () => ({ name: "<新任务>" }));
            project.insertTasksByRow(insertedRow, taskData);
        }
    
        function addSummary() {
            var project = ganttSheet.project;
            var selectedRange = ganttSheet.getSelections()[0];
            var insertedRow = selectedRange.row;
            var insertedTaskNumbers = ganttSheet.getTaskByRow(insertedRow).taskNumber || project.count;
            var selectedTasks = getSelectedRowIndexes().map((row) => ganttSheet.getTaskByRow(row)).filter((row) => !!row);
    
            if (selectedTasks.length == 0) {
                project.insertTasks(insertedTaskNumbers, [{ name: "<新摘要任务>" }, { name: "<新任务>" }]);
                project.indentTasks(insertedTaskNumbers + 1);
            } else {
                project.insertTasks(insertedTaskNumbers, { name: "<新摘要任务>" }, selectedTasks[0].level);
                project.indentTasks(selectedTasks.map((t) => t.taskNumber));
            }
            ganttSheet.setSelection(insertedRow, -1, 1, -1);
        }
    
        function addMilestone() {
            var project = ganttSheet.project;
            var selections = ganttSheet.getSelections();
            var rowCount = selections.map(r => r.rowCount).reduce((pre, current) => { return pre + current });
            var insertedRow = Math.min(...selections.map(r => r.row), project.tasks.length);
            var taskData = createTaskDataList(rowCount, () => ({ name: "<新里程碑>", duration: 0 }));
            project.insertTasksByRow(insertedRow, taskData);
            ganttSheet.setSelection(insertedRow, -1, rowCount, -1);
        }
    
        function deleteTask() {
            var project = ganttSheet.project;
            var rowIds = getSelectedTaskNumbers();
            project.removeTasks(rowIds);
        }
    
        function indentTask() {
            var project = ganttSheet.project;
            var rowIds = getSelectedTaskNumbers();
            project.indentTasks(rowIds);
        }
    
        function outdentTask() {
            var project = ganttSheet.project;
            var rowIds = getSelectedTaskNumbers();
            project.outdentTasks(rowIds);
        }
    
        function linkTask() {
            var project = ganttSheet.project;
            var links = [];
            var selections = ganttSheet.getSelections();
            var previous = -1;
            for (var range of selections) {
                if (previous != -1) {
                    links.push([previous, range.row]);
                }
                for (var row = range.row + 1; row < range.row + range.rowCount; row++) {
                    links.push([row - 1, row]);
                }
                previous = range.row + range.rowCount - 1;
            }
            if (links.length <= 0) {
                return;
            }
            project.suspendSchedule();
            for (var link of links) {
                var [fromTaskNumber, toTaskNumber] = link;
                try {
                    project.addDependency({ fromTaskNumber, toTaskNumber });
                } catch { }
            }
            project.resumeSchedule();
        }
    

    5.日历

    (可在甘特图插件内实现日历内容的调整)

    JavaScript代码:

    /*REPLACE_MARKER*/
    /*DO NOT DELETE THESE COMMENTS*/
    var myTable;
    var ganttSheet;
    var roundBarsToWholeDays = true;
    
    window.onload = function () {
        var spread = new GC.Spread.Sheets.Workbook(document.getElementById("ss"), { sheetCount: 0 });
        initSpread(spread);
        initSplitView(spread);
    };
    
    function initSpread(spread) {
        spread.suspendPaint();
        initDataSource(spread);
        initGanttSheet(spread);
        spread.resumePaint();
    }
    
    function initGanttSheet(spread) {
        ganttSheet = spread.addSheetTab(0, "GanttSheet", GC.Spread.Sheets.SheetType.ganttSheet);
        var view = myTable.addView("ganttView", [
            { value: "id", caption: "Id", width: 40 },
            { value: "taskNumber", caption: "NO.", width: 60 },
            { value: "name", caption: "Task Name", width: 200 },
            { value: "duration", caption: "Duration", width: 90 },
            { value: "predecessors", caption: "Predecessors", width: 120 }
        ]);
        view.fetch().then(function () {
            ganttSheet.bindGanttView(view);
        });
        initSidePanel(ganttSheet);
    }
    
    function initSidePanel(ganttSheet) {
        var addTaskItem = document.getElementById("add-task");
        var addSummayItem = document.getElementById("add-summary");
        var addMilestoneItem = document.getElementById("add-milestone");
    
        var deleteItem = document.getElementById("delete-task");
    
        var indentTaskItem = document.getElementById("indent-task");
        var outdentTaskItem = document.getElementById("outdent-task");
    
        var linkTaskItem = document.getElementById("link-task");
        var unlinkTaskItem = document.getElementById("unlink-task");
    
        var submitItem = document.getElementById("submit-changes");
        var cancelItem = document.getElementById("cancel-changes");
    
        addTaskItem.addEventListener("click", function () {
            ganttSheet.suspendPaint()
            addTask();
            ganttSheet.resumePaint();
        });
        addSummayItem.addEventListener("click", function () {
            ganttSheet.suspendPaint()
            addSummary();
            ganttSheet.resumePaint();
        });
        addMilestoneItem.addEventListener("click", function () {
            ganttSheet.suspendPaint()
            addMilestone();
            ganttSheet.resumePaint();
        });
    
        deleteItem.addEventListener("click", function () {
            ganttSheet.suspendPaint()
            deleteTask();
            ganttSheet.resumePaint();
        });
    
        indentTaskItem.addEventListener("click", function () {
            ganttSheet.suspendPaint()
            indentTask();
            ganttSheet.resumePaint();
        });
        outdentTaskItem.addEventListener("click", function () {
            ganttSheet.suspendPaint()
            outdentTask();
            ganttSheet.resumePaint();
        });
    
        linkTaskItem.addEventListener("click", function () {
            ganttSheet.suspendPaint()
            linkTask();
            ganttSheet.resumePaint();
        });
        unlinkTaskItem.addEventListener("click", function () {
            ganttSheet.suspendPaint()
            unlinkTask();
            ganttSheet.resumePaint();
        });
    
        submitItem.addEventListener("click", function () {
            ganttSheet.submitChanges();
        });
        cancelItem.addEventListener("click", function () {
            ganttSheet.cancelChanges();
        });
    
        function addTask() {
            var project = ganttSheet.project;
            var selections = ganttSheet.getSelections();
            var insertedRow = Math.min(...selections.map(r => r.row), project.tasks.length)
            var rowCount = selections.map(r => r.rowCount).reduce((pre, current) => { return pre + current });
            var taskData = createTaskDataList(rowCount, () => ({ name: "<新任务>" }));
            project.insertTasksByRow(insertedRow, taskData);
        }
    
        function addSummary() {
            var project = ganttSheet.project;
            var selectedRange = ganttSheet.getSelections()[0];
            var insertedRow = selectedRange.row;
            var insertedTaskNumbers = ganttSheet.getTaskByRow(insertedRow).taskNumber || project.count;
            var selectedTasks = getSelectedRowIndexes().map((row) => ganttSheet.getTaskByRow(row)).filter((row) => !!row);
    
            if (selectedTasks.length == 0) {
                project.insertTasks(insertedTaskNumbers, [{ name: "<新摘要任务>" }, { name: "<新任务>" }]);
                project.indentTasks(insertedTaskNumbers + 1);
            } else {
                project.insertTasks(insertedTaskNumbers, { name: "<新摘要任务>" }, selectedTasks[0].level);
                project.indentTasks(selectedTasks.map((t) => t.taskNumber));
            }
            ganttSheet.setSelection(insertedRow, -1, 1, -1);
        }
    
        function addMilestone() {
            var project = ganttSheet.project;
            var selections = ganttSheet.getSelections();
            var rowCount = selections.map(r => r.rowCount).reduce((pre, current) => { return pre + current });
            var insertedRow = Math.min(...selections.map(r => r.row), project.tasks.length);
            var taskData = createTaskDataList(rowCount, () => ({ name: "<新里程碑>", duration: 0 }));
            project.insertTasksByRow(insertedRow, taskData);
            ganttSheet.setSelection(insertedRow, -1, rowCount, -1);
        }
    
        function deleteTask() {
            var project = ganttSheet.project;
            var rowIds = getSelectedTaskNumbers();
            project.removeTasks(rowIds);
        }
    
        function indentTask() {
            var project = ganttSheet.project;
            var rowIds = getSelectedTaskNumbers();
            project.indentTasks(rowIds);
        }
    
        function outdentTask() {
            var project = ganttSheet.project;
            var rowIds = getSelectedTaskNumbers();
            project.outdentTasks(rowIds);
        }
    
        function linkTask() {
            var project = ganttSheet.project;
            var links = [];
            var selections = ganttSheet.getSelections();
            var previous = -1;
            for (var range of selections) {
                if (previous != -1) {
                    links.push([previous, range.row]);
                }
                for (var row = range.row + 1; row < range.row + range.rowCount; row++) {
                    links.push([row - 1, row]);
                }
                previous = range.row + range.rowCount - 1;
            }
            if (links.length <= 0) {
                return;
            }
            project.suspendSchedule();
            for (var link of links) {
                var [fromTaskNumber, toTaskNumber] = link;
                try {
                    project.addDependency({ fromTaskNumber, toTaskNumber });
                } catch { }
            }
            project.resumeSchedule();
        }
    
        function unlinkTask() {
            var project = ganttSheet.project;
            var taskNumbers = getSelectedTaskNumbers();
            var dependencies = [];
            if (taskNumbers.length == 1) {
                var task = project.getTaskByRow(taskNumbers[0]);
                dependencies = [...task.predecessorDependencies, ...task.successorDependencies];
            } else {
                var taskNumberSet = {};
                for (var taskNumber of taskNumbers) {
                    taskNumberSet[taskNumber] = true;
                }
                dependencies = project.dependencies.filter(dp => taskNumberSet[dp.from.taskNumber] && taskNumberSet[dp.to.taskNumber]);
            }
            if (dependencies.length >= 1) {
                project.removeDependency(dependencies);
            }
        }
    
        function getSelectedRowIndexes() {
            var rows = [];
            var selections = ganttSheet.getSelections();
            for (var range of selections) {
                for (var row = range.row; row < range.row + range.rowCount; row++) {
                    rows.push(row);
                }
            }
            return rows;
        }
    

    6.导入和导出操作

    甘特图插件中展示的内容导出至PDF及Excel的格式。

    (支持导入导出能力)

    JavaScript代码:

    /*REPLACE_MARKER*/
    /*DO NOT DELETE THESE COMMENTS*/
    var myTable;
    var ganttSheet;
    var roundBarsToWholeDays = true;
    
    window.onload = function() {
        var spread = new GC.Spread.Sheets.Workbook(document.getElementById("ss"), { sheetCount: 0 });
        initSpread(spread);
        initSplitView(spread);
    };
    
    function initSpread(spread) {
        spread.suspendPaint();
        initDataSource(spread);
        initGanttSheet(spread);
        spread.resumePaint();
    }
    
    function initDataSource(spread) {
        var tableName = "Gantt_Id";
        var baseApiUrl = getBaseApiUrl();
        var apiUrl = baseApiUrl + "/" + tableName;
        var dataManager = spread.dataManager();
        myTable = dataManager.addTable("myTable", {
            batch: true,
            remote: {
                read: {
                    url: apiUrl
                }
            },
            schema: {
                hierarchy: {
                    type: "Parent",
                    column: "parentId"
                },
                columns: {
                    id: { isPrimaryKey: true },
                    taskNumber: { dataType: "rowOrder" }
                }
            }
        });
    }
    
    function initGanttSheet(spread) {
        ganttSheet = spread.addSheetTab(0, "GanttSheet", GC.Spread.Sheets.SheetType.ganttSheet);
        var view = myTable.addView("ganttView", [
            { value: "taskNumber", caption: "NO.", width: 60 },
            { value: "name", caption: "Task Name", width: 200 },
            { value: "duration", caption: "Duration", width: 90 },
            { value: "predecessors", caption: "Predecessors", width: 120, visible: false }
        ]);
        view.fetch().then(function() {
            ganttSheet.bindGanttView(view);
        });
        initSidePanel(spread, ganttSheet);
    }
    
    function initSidePanel(spread, ganttSheet) {
        var excelIo = new GC.Spread.Excel.IO();
    
        var fileInput = document.getElementById("file-input");
        var openFileItem = document.getElementById("open-file");
        var fileNameItem = document.getElementById("file-name");
        var exportJSONItem = document.getElementById("export-to-json");
        var exportSJSItem = document.getElementById("export-to-sjs");
        var exportExcelItem = document.getElementById("export-to-excel");
        var printItem = document.getElementById("print");
        var pdfItem = document.getElementById("pdf");
    
        var exportFileName = "export";
    
        openFileItem.addEventListener('click', function() {
            fileInput.click();
        });
    
        fileInput.addEventListener('change', function(e) {
            fileNameItem.value = 'Loading file...';
            var file = fileInput.files[0];
            if (!file) {
                alert("Upload a file first.");
                return;
            }
            var fileName = file.name;
            var suffix = fileName.substr(fileName.lastIndexOf('.'));
            if (suffix === '.xlsx' || suffix === '.xlsm') {
                // here is excel IO API
                excelIo.open(file, function(json) {
                    spread.fromJSON(json);
                    fileNameItem.value = fileName;
                    fileInput.value = '';
                }, function(e) {
                    // process error
                    console.log(e);
                });
            } else if (suffix === '.ssjson' || suffix === '.json') {
                var reader = new FileReader();
                reader.onload = function() {
                    var spreadJson = JSON.parse(this.result);
                    spread.fromJSON(spreadJson);
                    fileNameItem.value = fileName;
                    fileInput.value = '';
                };
                reader.readAsText(file);
            } else if (suffix === '.sjs') {
                spread.open(file, () => {
                    fileNameItem.value = fileName;
                    fileInput.value = '';
                }, (e) => {
                    console.log(e);
                }, {
                    openMode: GC.Spread.Sheets.OpenMode.normal,
                    fullRecalc: true,
                });
            }
        });
    
        exportJSONItem.addEventListener('click', function () {
            var fileName = exportFileName + ".ssjson";
            var json = spread.toJSON();
            saveAs(new Blob([JSON.stringify(json)], { type: "text/plain;charset=utf-8" }), fileName);
        });
    
        exportSJSItem.addEventListener('click', function () {
            var fileName = exportFileName + ".sjs";
            spread.save(function (blob) {
                saveAs(blob, fileName);
            }, function (e) {
                // process error
                console.log(e);
            }, {
                fileType: GC.Spread.Sheets.FileType.ssjson,
                includeBindingSource: true,
                saveAsView: true
            });
        });
    
        exportExcelItem.addEventListener('click', function () {
            var fileName = exportFileName + ".xlsx";
            var json = spread.toJSON({ includeBindingSource: true, saveAsView: true });
            // here is excel IO API
            excelIo.save(json, function(blob) {
                saveAs(blob, fileName);
            }, function(e) {
                // process error
                console.log(e);
            }, { xlsxStrictMode: false });
        });
    
        printItem.addEventListener('click', function() {
            spread.print();
        });
    
        pdfItem.addEventListener('click', function() {
            spread.savePDF(function (blob) {
                var url = URL.createObjectURL(blob);
                pwin = window.open(url);
            });
        });
    }
    

    总结

    甘特图是一种强大的项目管理工具,通过直观的时间轴展示任务的安排和进度。它能帮助项目经理们创建详细的项目计划,有效跟踪进度,并合理分配资源。甘特图还可以管理任务间的依赖关系,及时调整时间表,并识别风险与关键路径。它在项目管理中扮演着重要的角色,提高了团队的协作效率和项目的成功率。通过使用甘特图,项目团队能够更好地掌握项目进展,作出决策,并确保项目按时交付。

    如果您对该插件感兴趣,可以访问葡萄城官网下载SpreadJS产品。

  • 相关阅读:
    彻底搞懂cookie、session和token
    Python机器视觉--OpenCV入门--OpenCV中的色彩空间简介与色彩空间转换
    Dubbo3应用开发—Dubbo3注册中心(zookeeper、nacos、consul)的使用
    什么是三高架构
    JNI技术解析
    C# 9.0 添加和增强的功能【基础篇】
    关于 /lib/modules/**内核版本号**/ build 和 /source
    Object.entries()
    linux 查看CPU架构是AMD还是ARM
    flutter系列之:flutter架构什么的,看完这篇文章就全懂了
  • 原文地址:https://www.cnblogs.com/powertoolsteam/p/17694219.html