甘特图的这些新玩法,你都知道吗?
摘要:本文由葡萄城技术团队于OSCHINA发布。转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具、解决方案和服务,赋能开发者。
前言
甘特图是项目管理、生产排程、节点管理中非常常见的一个功能。那么,有没有一种方法能够帮助将甘特图引入到系统中,让数据的进度、排程数据的展示更加直观,让管理更加高效。
本葡萄今天为大家带来一个新的插件——甘特图插件。该插件是结合 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); }
(通过甘特图插件实现的任务栏和网格线的样式)
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产品。
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
好用!这些工具国庆一定要研究下「GitHub 热点速览」
再过 3 天就要开始一年最长的假期——国庆长假了,这次除了宅家、出游之外,多了一个新选项:研究下哪些项目可以安排上,来辅助自己的日常开发。 你觉得一周获得 4k star 的 hyperdx 如何,它能让你快速定位生产环境哪里有问题;不停机也能部署新服务的 kamal 也许可以上你的研究榜单。 还有非常好玩的知识图谱可视化生成工具 instagraph,丢个链接给它,它就能帮你生成各种关系图。 以下内容摘录自微博@HelloGitHub 的 GitHub Trending 及 Hacker News 热帖(简称 HN 热帖),选项标准:新发布 | 实用 | 有趣,根据项目 release 时间分类,发布时间不超过 14 day 的项目会标注 New,无该标志则说明项目 release 超过半月。由于本文篇幅有限,还有部分项目未能在本文展示,望周知 🌝 本文目录 1. 本周特推 1.1 云基础管理:opentofu 1.2 持续集成交付:gitness 2. GitHub Trending 周榜 2.1 高性能游戏引擎:bevy 2.2 部署利器:kamal 2.3 知识图谱生成:in...
- 下一篇
Embeddig技术与应用 (1) :Embedding技术发展概述及Word2Vec
编者按:嵌入(Embedding)是机器学习中一种将高维稀疏向量转换为低维稠密向量的技术。其通常用于处理自然语言、图像等高维离散数据。 嵌入能够有效地解决维度灾难问题,减少存储和计算成本,同时提高模型的表达能力。我们还可以通过得到的嵌入向量进行语义相似度计算、推荐系统、分类任务等多种应用。嵌入还可以用于处理非结构化数据,如文本、图像、音频等,帮助机器理解和处理人类的语言和其他感知信息。 IDP开启Embedding系列专栏,详细介绍Embedding的发展史、主要技术和应用。本文是《Embedding技术与应用系列》的第一篇,重点介绍Embedding技术的发展历程,并着重讲解Word2Vec的工作原理、具体实现等内容。 文章作者首先介绍了以往的编码表示方法的局限,指出它们在处理大规模语料时会产生维度灾难和计算复杂度高的问题。然后作者详细解释了Word2Vec模型的工作原理,并给出了具体的PyTorch实现代码。作者认为Word2Vec克服了传统方法的局限性,是一种优雅的大规模语料处理方案。 希望本文能给读者带来Embeddings技术发展脉络的系统性理解,对希望了解和使用Word2V...
相关文章
文章评论
共有0条评论来说两句吧...