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

前言

甘特图是项目管理、生产排程、节点治理中十分常见的一个性能。那么,有没有一种办法可能帮忙将甘特图引入到零碎中,让数据的进度、排程数据的展现更加直观,让治理更加高效。

本葡萄明天为大家带来一个新的插件——甘特图插件。该插件是联合 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产品。