GanttSheet 主题

本示例展示了如何为 GanttSheet 应用主题,包括工作簿级主题设置(工作簿主题、主题颜色、主题字体)以及表级主题设置(GanttSheet 主题)。

概述 本示例展示了如何为 GanttSheet 同时应用工作簿级主题和表级主题,并通过右侧面板动态切换主题、颜色、字体以及 GanttSheet 专属主题样式。 页面左侧展示一个绑定远程任务数据的 GanttSheet,右侧提供四组控制项:工作簿主题、主题颜色、主题字体和 GanttSheet 主题。用户可以在运行时实时观察不同主题组合对甘特表外观的影响。 实现思路 页面加载后创建一个不带初始工作表的 Workbook,并初始化拆分视图布局。 通过 dataManager.addTable 注册远程数据源,建立带有父子层级关系的任务表。 新建 ganttSheet 类型工作表,并绑定自定义视图字段作为甘特表列。 默认先应用一个内置表主题,再设置非工作时间样式和任务条样式规则。 右侧面板分别控制工作簿主题、主题颜色、主题字体和 GanttSheet 表主题,并在切换时立即重新应用。 当勾选“覆盖 GanttSheet 主题字体”时,会复制当前表主题并移除特定列的字体设置,使其跟随当前工作簿主题字体。 代码解析 示例入口会先创建工作簿,再初始化数据源、GanttSheet 和左右拆分面板: 数据源并不是写死在本地数组里,而是通过数据管理器绑定远程接口,并声明父子层级关系与主键字段。这是 GanttSheet 能正确展示任务树结构的基础: GanttSheet 创建后,示例先设置默认表主题和时间轴中的非工作时间样式,然后定义甘特表视图列,并在数据抓取完成后绑定到工作表: 示例还额外修改了任务条样式规则,让任务条中部颜色跟随主题,并直接显示任务名称: 右侧“工作簿主题”“主题颜色”“主题字体”三组控制都采用相同思路:从当前主题克隆出一个新主题,修改颜色或字体后再重新设置回当前 GanttSheet: 字体覆盖是这个 demo 里比较关键的一段逻辑。勾选覆盖选项后,代码会复制当前表主题,并把 highlightLastColumnStyle 的字体相关配置清空,使该列跟随当前工作簿主题字体: 如果取消覆盖选项,则会直接恢复当前选中的原始 GanttSheet 表主题: 运行效果 页面打开后会显示一个绑定任务数据的甘特表,以及右侧的主题控制面板。 切换“工作簿主题”时,GanttSheet 会整体套用不同的内置工作簿主题。 切换“主题颜色”时,当前主题的配色方案会被替换,但主题结构保持不变。 切换“主题字体”时,工作簿主题字体会实时更新。 切换“GanttSheet 主题”时,甘特表本身的表格样式会在浅色、中等、深色和专业主题之间切换。 勾选“覆盖 GanttSheet 主题字体”后,最后高亮列会改为跟随当前主题字体,而不是继续使用表主题中的固定字体设置。 API 参考 spread.dataManager().addTable(name, options) 用于注册远程任务数据表并定义层级结构。 spread.addSheetTab(index, name, GC.Spread.Sheets.SheetType.ganttSheet) 用于创建 GanttSheet 工作表。 ganttSheet.bindGanttView(view) 用于将数据视图绑定到 GanttSheet。 ganttSheet.currentTheme(theme) 用于应用或更新工作簿级主题。 ganttSheet.applyTableTheme(theme) 用于应用 GanttSheet 的表格主题。
/*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(); spread.options.autoFitType = GC.Spread.Sheets.AutoFitType.cellWithHeader; 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, "甘特表", GC.Spread.Sheets.SheetType.ganttSheet); ganttSheet.options.allowAddNew = false; ganttSheet.setDefaultRowHeight(40, GC.Spread.Sheets.SheetArea.colHeader); ganttSheet.applyTableTheme(GC.Spread.Sheets.Tables.TableThemes.light2); ganttSheet.project.timescale.nonWorkingTime.color = 'Accent 4 60'; ganttSheet.project.timescale.nonWorkingTime.pattern = 'darkFil'; var view = myTable.addView("ganttView", [ { value: "taskNumber", caption: "编号", width: 60 }, { value: "name", caption: "任务名称", width: 200 }, { value: "duration", caption: "工期", width: 90 }, { value: "predecessors", caption: "前置任务", width: 120 } ]); view.fetch().then(function() { ganttSheet.bindGanttView(view); }).then(function(){ var taskStyle = ganttSheet.project.taskStyleRules.getRule('task'); taskStyle.style.taskbarStyle.middleColor = 'Accent 1'; taskStyle.style.taskbarStyle.topText = 'name'; var taskStyleIndex = ganttSheet.project.taskStyleRules.getIndexByItem(taskStyle); ganttSheet.project.taskStyleRules.setItemAt(taskStyleIndex, taskStyle); }); // Populate Workbook Theme options var workbookThemeSelect = document.getElementById("workbookTheme"); Object.keys(GC.Spread.Sheets.Themes).forEach(function(themeName) { var option = document.createElement("option"); option.value = themeName; option.text = themeName; if (themeName === "Office") { option.selected = true; } workbookThemeSelect.appendChild(option); }); workbookThemeSelect.addEventListener("change", function () { let selectedTheme = workbookThemeSelect.value; let theme = GC.Spread.Sheets.Themes[selectedTheme]; ganttSheet.currentTheme(theme); }); // Populate Theme Colors options var themeColorsSelect = document.getElementById("themeColors"); Object.keys(GC.Spread.Sheets.ThemeColors).forEach(function(colorName) { var option = document.createElement("option"); option.value = colorName; option.text = colorName; if (colorName === "Office") { option.selected = true; } themeColorsSelect.appendChild(option); }); themeColorsSelect.addEventListener("change", function () { let selectedColorScheme = themeColorsSelect.value; let currentTheme = ganttSheet.currentTheme(); let newTheme = new GC.Spread.Sheets.Theme(); newTheme.fromJSON(currentTheme.toJSON()); newTheme.colors(GC.Spread.Sheets.ThemeColors[selectedColorScheme]); newTheme.name('custom-theme'); ganttSheet.currentTheme(newTheme); }); // Populate Theme Fonts options var themeFontsSelect = document.getElementById("themeFonts"); var overrideGanttFontCheckbox = document.getElementById("override-gantt-font"); var shouldOverrideGanttFont = false; var currentGanttThemeName = "light2"; // Track current gantt theme Object.keys(GC.Spread.Sheets.ThemeFonts).forEach(function(fontName) { var option = document.createElement("option"); option.value = fontName; option.text = fontName; if (fontName === "Office") { option.selected = true; } themeFontsSelect.appendChild(option); }); overrideGanttFontCheckbox.addEventListener("click", function() { if (overrideGanttFontCheckbox.classList.contains("active")) { overrideGanttFontCheckbox.classList.remove("active"); shouldOverrideGanttFont = false; } else { overrideGanttFontCheckbox.classList.add("active"); shouldOverrideGanttFont = true; } applyGanttTableTheme(); }); themeFontsSelect.addEventListener("change", function () { applyThemeFont(); }); function applyThemeFont() { let selectedFont = themeFontsSelect.value; let currentTheme = ganttSheet.currentTheme(); let newTheme = new GC.Spread.Sheets.Theme(); newTheme.fromJSON(currentTheme.toJSON()); newTheme.font(GC.Spread.Sheets.ThemeFonts[selectedFont]); newTheme.name('custom-theme'); ganttSheet.currentTheme(newTheme); if (shouldOverrideGanttFont) { applyGanttTableTheme(); } } function applyGanttTableTheme() { if (shouldOverrideGanttFont) { // Clone the current table theme var baseTheme = GC.Spread.Sheets.Tables.TableThemes[currentGanttThemeName]; var customTableTheme = new GC.Spread.Sheets.Tables.TableTheme(); customTableTheme.fromJSON(baseTheme.toJSON()); // Get and modify the highlightLastColumnStyle var lastColStyle = customTableTheme.highlightLastColumnStyle(); if (!lastColStyle) { lastColStyle = new GC.Spread.Sheets.Tables.TableStyle(); } // Set font and fontFamily to undefined to use theme font lastColStyle.font = undefined; lastColStyle.fontFamily = undefined; customTableTheme.highlightLastColumnStyle(lastColStyle); // Apply the modified theme ganttSheet.applyTableTheme(customTableTheme); } else { // Apply the original table theme ganttSheet.applyTableTheme(GC.Spread.Sheets.Tables.TableThemes[currentGanttThemeName]); } } // GanttSheet Theme selector var ganttThemeSelect = document.getElementById("ganttSheetTheme"); ganttThemeSelect.addEventListener("change", function () { let selectedTheme = ganttThemeSelect.value; currentGanttThemeName = selectedTheme; applyGanttTableTheme(); }); } function getBaseApiUrl() { var url = window.location.href; return url.match(/http.+spreadjs\/SpreadJSTutorial\//)[0] + 'server/api'; } function initSplitView(spread) { var host = document.getElementById("split-view"); var content = host.getElementsByClassName("split-content")[0]; var panel = host.getElementsByClassName("split-panel")[0]; new SplitView({ host: host, content: content, panel: panel, refreshContent: function() { spread.refresh(); } }); }
<!doctype html> <html style="height:100%;font-size:14px;"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="spreadjs culture" content="zh-cn" /> <link rel="stylesheet" type="text/css" href="$DEMOROOT$/zh/purejs/node_modules/@grapecity-software/spread-sheets/styles/gc.spread.sheets.excel2013white.css"> <link rel="stylesheet" type="text/css" href="$DEMOROOT$/spread/source/splitView/splitView.css"> <!-- Promise Polyfill for IE, https://www.npmjs.com/package/promise-polyfill --> <script src="https://cdn.jsdelivr.net/npm/promise-polyfill@8/dist/polyfill.min.js"></script> <script src="$DEMOROOT$/zh/purejs/node_modules/@grapecity-software/spread-sheets/dist/gc.spread.sheets.all.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/zh/purejs/node_modules/@grapecity-software/spread-sheets-resources-zh/dist/gc.spread.sheets.resources.zh.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/zh/purejs/node_modules/@grapecity-software/spread-sheets-tablesheet/dist/gc.spread.sheets.tablesheet.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/zh/purejs/node_modules/@grapecity-software/spread-sheets-ganttsheet/dist/gc.spread.sheets.ganttsheet.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/spread/source/splitView/splitView.js" type="text/javascript"></script> <script src="$DEMOROOT$/spread/source/js/license.js" type="text/javascript"></script> <script src="app.js" type="text/javascript"></script> <link rel="stylesheet" type="text/css" href="styles.css"> </head> <body> <div id="split-view" class="sample-tutorial"> <div id="ss" class="sample-spreadsheets split-content"></div> <div class="options-container split-panel"> <div class="option-block"> <div class="option-row option-title"> 工作簿主题 </div> <div class="option-row selection-box"> <label>主题</label> <select id="workbookTheme"></select> </div> </div> <div class="option-block"> <div class="option-row option-title"> 主题颜色 </div> <div class="option-row selection-box"> <label>颜色</label> <select id="themeColors"></select> </div> </div> <div class="option-block"> <div class="option-row option-title"> 主题字体 </div> <div class="option-row selection-box"> <label>字体</label> <select id="themeFonts"></select> </div> <div class="option-row"> <label class="option-checkbox" id="override-gantt-font">覆盖 GanttSheet 主题字体</label> <div class="option-info">* 使用主题字体覆盖 highlightLastColumn 的 fontFamily。</div> </div> </div> <div class="option-block"> <div class="option-row option-title"> GanttSheet 主题 </div> <div class="option-row selection-box"> <label>主题</label> <select id="ganttSheetTheme"> <optgroup label="浅色主题"> <option value="light1">light1</option> <option value="light2" selected>light2</option> <option value="light3">light3</option> <option value="light4">light4</option> <option value="light5">light5</option> <option value="light6">light6</option> <option value="light7">light7</option> <option value="light8">light8</option> <option value="light9">light9</option> <option value="light10">light10</option> <option value="light11">light11</option> <option value="light12">light12</option> <option value="light13">light13</option> <option value="light14">light14</option> <option value="light15">light15</option> <option value="light16">light16</option> <option value="light17">light17</option> <option value="light18">light18</option> <option value="light19">light19</option> <option value="light20">light20</option> <option value="light21">light21</option> </optgroup> <optgroup label="中等主题"> <option value="medium1">medium1</option> <option value="medium2">medium2</option> <option value="medium3">medium3</option> <option value="medium4">medium4</option> <option value="medium5">medium5</option> <option value="medium6">medium6</option> <option value="medium7">medium7</option> <option value="medium8">medium8</option> <option value="medium9">medium9</option> <option value="medium10">medium10</option> <option value="medium11">medium11</option> <option value="medium12">medium12</option> <option value="medium13">medium13</option> <option value="medium14">medium14</option> <option value="medium15">medium15</option> <option value="medium16">medium16</option> <option value="medium17">medium17</option> <option value="medium18">medium18</option> <option value="medium19">medium19</option> <option value="medium20">medium20</option> <option value="medium21">medium21</option> <option value="medium22">medium22</option> <option value="medium23">medium23</option> <option value="medium24">medium24</option> <option value="medium25">medium25</option> <option value="medium26">medium26</option> <option value="medium27">medium27</option> <option value="medium28">medium28</option> </optgroup> <optgroup label="深色主题"> <option value="dark1">dark1</option> <option value="dark2">dark2</option> <option value="dark3">dark3</option> <option value="dark4">dark4</option> <option value="dark5">dark5</option> <option value="dark6">dark6</option> <option value="dark7">dark7</option> <option value="dark8">dark8</option> <option value="dark9">dark9</option> <option value="dark10">dark10</option> <option value="dark11">dark11</option> </optgroup> <optgroup label="专业主题"> <option value="professional1">professional1</option> <option value="professional2">professional2</option> <option value="professional3">professional3</option> <option value="professional4">professional4</option> <option value="professional5">professional5</option> <option value="professional6">professional6</option> <option value="professional7">professional7</option> <option value="professional8">professional8</option> <option value="professional9">professional9</option> <option value="professional10">professional10</option> <option value="professional11">professional11</option> <option value="professional12">professional12</option> <option value="professional13">professional13</option> <option value="professional14">professional14</option> <option value="professional15">professional15</option> <option value="professional16">professional16</option> <option value="professional17">professional17</option> <option value="professional18">professional18</option> <option value="professional19">professional19</option> <option value="professional20">professional20</option> <option value="professional21">professional21</option> <option value="professional22">professional22</option> <option value="professional23">professional23</option> <option value="professional24">professional24</option> </optgroup> </select> </div> </div> </div> </html>
.sample-tutorial { position: relative; height: 100%; overflow: hidden; } .sample-spreadsheets { width: calc(100% - 280px); height: 100%; overflow: hidden; float: left; } .options-container { float: right; width: 280px; padding: 12px; height: 100%; box-sizing: border-box; background: #fbfbfb; overflow: auto; box-shadow: inset 0px 0 4px 0 rgba(0,0,0,0.4); } .option-block { background: #fff; padding: 8px; margin: 12px 0; border-radius: 4px; border: 1px dashed #82bc00; box-shadow: 0px 0 6px 0 rgba(0,0,0,0.1); } .option-row { font-size: 14px; box-sizing: border-box; padding: 4px 0; } .option-title { font-weight: bold; color: #656565; } .option-info { font-size: 12px; color: #919191; margin-top: 6px; font-weight: normal; } .option-checkbox { background: #fff; border: 1px dashed #f7a711; color: #f7a711; padding: 2px 4px; transition: 0.3s; box-sizing: border-box; cursor: pointer; display: inline-block; } .option-checkbox.active { color: #fff; background: #f7a711; box-shadow: 0 1px 4px 0 rgba(0,0,0,0.3); border-radius: 4px; } .selection-box { position: relative; } .selection-box > select { text-align: left; width: 100%; height: 20px; padding: 0; line-height: 20px; background: transparent; border: none; border-bottom: 2px solid #656565; color: #656565; transition: 0.3s; cursor: pointer; outline: none; box-sizing: border-box; } .selection-box > select > option { background: white; } .selection-box > select:focus { border-bottom: 2px solid #82bc00; color: #82bc00; box-shadow: 0 2px 6px 0 rgba(0,0,0,0.3); } .selection-box > label { position: absolute; cursor: pointer; font-size: 12px; color: #fff; background: #656565; padding: 0 4px; right: 0; top: 6px; box-shadow: 0 1px 4px 0 rgba(0,0,0,0.3); } body { position: absolute; top: 0; bottom: 0; left: 0; right: 0; }