このサンプルでは、ガントシートを利用してガントチャートを作成し、タイムスケールの設定、カレンダー構成のカスタマイズ、タスクを追加/削除、Excel出力などの使い方を示しています。
タスク設定
ガントシートにタスクとリソースの割り当てを定義しています。
タイムスケールのレイアウトを3つの層(上段、中段、下段)に分けてセパレーター表示するように設定し、書式と配置位置をカスタマイズしています。
稼働時間に土曜日の10:00から16:00を追加し、週間稼働日数を変更しています。
稼働時間の単位を「月」に設定し、サマリータスクの工数を月単位で表示しています。
スタイル
組み込みのスタイルルールを利用して、タスク/サマリー/進捗をそれぞれのスタイルを設定しています。
特定のタスク(地鎮祭と検査タスク)に対してタスクバーのスタイルを設定しています。
チャート領域のグリッド線を線種と表示間隔をカスタマイズしています。
非稼働時間の塗りつぶし色とパターンをカスタマイズしています。
操作
タスクの追加、削除、リンクの作成、およびExcelへの出力機能を実装しています。
2つ以上のタスクを選択し、リンクボタンを押下すると選択したタスクをリンクできます。
import { Component, NgModule, enableProdMode } from '@angular/core';
import { bootstrapApplication, BrowserModule } from '@angular/platform-browser';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { SpreadSheetsModule } from '@mescius/spread-sheets-angular';
import GC from "@mescius/spread-sheets";
import '@mescius/spread-sheets-io';
import '@mescius/spread-sheets-tablesheet';
import '@mescius/spread-sheets-ganttsheet';
import '@mescius/spread-sheets-resources-ja';
GC.Spread.Common.CultureManager.culture("ja-jp");
import './styles.css';
@Component({
standalone: true,
imports: [SpreadSheetsModule, BrowserModule],
selector: 'app-component',
templateUrl: 'src/app.component.html'
})
export class AppComponent {
hostStyle = {
width: '100%',
height: '100%',
overflow: 'hidden',
float: 'left'
};
init($event: any) {
initSpread($event.spread);
initDataSourceSheet($event.spread)
$event.spread.setActiveSheetTab(0);
$event.spread.changeSheetPosition("スプレッド邸新築工事", 0);
}
}
const GCSG = GC.Spread.Sheets.GanttSheet;
const GCSS = GC.Spread.Sheets;
var ganttSheet, spread;
function initDataSourceSheet(spread) {
let sheet = spread.getSheet(0);
sheet.name("DataSource");
sheet.setDataSource(Gantt_Data);
for (let columnIndex = 0; columnIndex < 11; columnIndex++) {
sheet.setColumnWidth(columnIndex, 80);
}
sheet.setColumnWidth(4, 250);
}
function initSpread(spreadObj) {
spread = spreadObj;
spread.suspendPaint();
// タブ表示の設定
spread.options.tabEditable = false;
spread.options.newTabVisible = false;
spread.options.tabNavigationVisible = false;
spread.options.allowSheetReorder = false;
spread.options.allSheetsListVisible = GCSS.AllSheetsListVisibility.hide;
spread.options.scrollbarMaxAlign = true;
// ガントシートの初期化
initGanttSheet(spread);
spread.resumePaint();
}
// ガントシートの作成
function initGanttSheet(spread) {
var dataManager = spread.dataManager();
var taskTable = dataManager.addTable('taskTable', {
data: Gantt_Data,
batch: true,
schema: {
hierarchy: {
type: 'Parent',
column: 'parentId',
},
columns: {
id: { isPrimaryKey: true },
taskNumber: { dataType: 'rowOrder' },
},
},
});
var assignmentTable = dataManager.addTable("assignmentTable", {
data: Assignment,
batch: true,
});
var resourceTable = dataManager.addTable("resourceTable", {
data: Resources,
batch: true,
});
dataManager.addRelationship(assignmentTable, "taskId", "task", taskTable, "id", "taskId");
dataManager.addRelationship(assignmentTable, "resourceId", "resource", resourceTable, "id", "assignment");
// ガントシートを追加する
ganttSheet = spread.addSheetTab(
0,
'スプレッド邸新築工事',
GCSS.SheetType.ganttSheet
);
// データ書式
var dataBarRule1 = {
ruleType: 'dataBarRule',
color: '#3eb370',
gradient: true,
};
var fitModeHeaderStyle = {
backColor: "#c0a2c7",
foreColor: "#333333",
font: 'normal bold 13px Georgia',
}
// ビューを追加
var taskView = taskTable.addView('taskView', [
{ value: "id", caption: "id", width: 60, visible: false },
{
value: 'taskNumber', caption: 'NO',
headerStyle: fitModeHeaderStyle, width: 60
},//必須フィールド
{
value: 'name', caption: 'WBS', style: { font: 'normal normal 13px Georgia' },
headerStyle: fitModeHeaderStyle, width: 180
},//必須フィールド
{
value: 'duration', caption: '計画',
headerStyle: fitModeHeaderStyle, width: 70
},//必須フィールド
{
value: 'predecessors', caption: '先行タスク',
headerStyle: fitModeHeaderStyle,
width: 90,
},//必須フィールド
{
value: "complete", caption: "進捗率", headerStyle: fitModeHeaderStyle,
width: 80, conditionalFormats: [dataBarRule1]
},
{
value: "taskId.resource.name", caption: "担当", style: { font: 'normal normal 10px Georgia', wordWrap: true },
headerStyle: fitModeHeaderStyle, dataType: "TaskResources", width: 100
},
]);
// ホバースタイルを追加
var hoverStyle = new GC.Spread.Sheets.Style();
hoverStyle.backColor = '#fce2c4';
hoverStyle.foreColor = '#674598';
taskView.addStyleRule('hoverStyle', hoverStyle, {
state: GC.Data.RowColumnStates.hover,
direction: GC.Data.StateRuleDirection.row,
});
// ガントビューを追加
taskView
.fetch().then(function () {
ganttSheet.bindGanttView(taskView);
})
.then(function () {
// プロジェクトの開始日を設定
ganttSheet.project.startDate = new Date('2024/04/01 09:00');
// タイムスケールの階層モード
let tierMode = GCSG.TimescaleTierMode.topMiddleBottom;
ganttSheet.project.timescale.tierMode = tierMode;
// 上段
ganttSheet.project.timescale.topTier.unit = GCSG.TimescaleUnit.months;
ganttSheet.project.timescale.topTier.formatter = 'yyyy年mm月';
// 中段
ganttSheet.project.timescale.middleTier.unit = GCSG.TimescaleUnit.weeks;
ganttSheet.project.timescale.middleTier.labelAlign = 'Center';
ganttSheet.project.timescale.middleTier.formatter = `"第"{!WEEK_FROM_START}"週"`;
// 下段
ganttSheet.project.timescale.bottomTier.unit = GCSG.TimescaleUnit.days;
ganttSheet.project.timescale.bottomTier.formatter = `m/d(ddd)`;
// タスクバーのスタイルルール
let taskStyleRule = ganttSheet.project.taskStyleRules.getRule('task');
let taskStyle = taskStyleRule.style.taskbarStyle;
taskStyle.insideText = "duration";
taskStyle.middleShape = 'rectangleBar';
taskStyle.middleColor = '#abd08f';
taskStyle.leftText = 'taskNumber';
taskStyleRule.style.taskbarStyle = taskStyle;
// 進捗のスタイルルール
let progressStyleRule = ganttSheet.project.taskStyleRules.getRule('progress');
let progressStyle = progressStyleRule.style.taskbarStyle;
progressStyle.middlePattern = 'diagonalCross';
progressStyle.middleColor = '#dbd0e6';
progressStyleRule.style.taskbarStyle = progressStyle;
// サマリーのスタイル
var summaryStyleRule = ganttSheet.project.taskStyleRules.getRule('summary');
var summaryStyle = summaryStyleRule.style.taskbarStyle;
summaryStyle.startShape = 'star';
summaryStyle.startColor = '#e6b422';
summaryStyle.endShape = 'circleArrowDown';
summaryStyle.endColor = '#f08300';
summaryStyle.topText = 'name';
summaryStyle.leftText = "start";
summaryStyle.rightText = "finish";
summaryStyleRule.style.taskbarStyle = summaryStyle;
// マイルストーンのスタイル
var milestoneStyleRule = ganttSheet.project.taskStyleRules.getRule('milestone');
var milestoneStyle = milestoneStyleRule.style.taskbarStyle;
milestoneStyle.startShape = 'circle';
milestoneStyle.startColor = 'red';
milestoneStyle.rightText = 'name';
milestoneStyle.rightTextStyle = {
color: 'red',
};;
milestoneStyleRule.style.taskbarStyle = milestoneStyle;
// 重要なタスクのスタイル
var importantBarStyles = {};
importantBarStyles['task'] = {
taskbarStyle: {
startShape: "lineShape",
startType: "solid",
startColor: "#e7609e",
middlePattern: "diagonalLeft",
middleShape: "rectangleBar",
middleColor: "#e7609e",
endShape: "lineShape",
endType: "solid",
endColor: "#e7609e",
leftText: "taskNumber",
topText: "name",
}
}
// 検査の日程をマーク
var importantTaskIds = [1, 8, 26, 27, 64];
for (var i = 0; i < importantTaskIds.length; i++) {
var task = ganttSheet.project.getTask(importantTaskIds[i]);
task.style = importantBarStyles;
}
// タイムスケールの最下層の目盛りに揃えるグリッド線
ganttSheet.gridlines.bottomTierColumn = {
lineType: GCSG.GanttGridlineType.dashed,
lineColor: "#c0a2c7"
};
// プロジェクト開始の目盛線
ganttSheet.gridlines.projectStart = {
lineType: GCSG.GanttGridlineType.thin,
lineColor: "purple"
};
// プロジェクト終了の目盛線
ganttSheet.gridlines.projectFinish = {
lineType: GCSG.GanttGridlineType.dashed,
lineColor: "purple"
};
// ガント行の間隔目盛線
ganttSheet.gridlines.ganttRows = {
lineType: GCSG.GanttGridlineType.dashDot,
lineColor: "#f6ad49",
interval: 3,
intervalLineType: GCSG.GanttGridlineType.thin,
intervalLineColor: "#9cc3e5"
};
// 非稼働時間のパターンをlineCross
ganttSheet.project.timescale.nonWorkingTime.color = "#c0c6c9";
ganttSheet.project.timescale.nonWorkingTime.pattern = "mediumFill";
// タスクバーのレイアウト
ganttSheet.project.layout.barHeight = 20;
ganttSheet.project.layout.linkLineMode = "toEnd";
ganttSheet.project.layout.barTextDateFormat = "yyyy/mm/dd";
ganttSheet.project.layout.roundBarsToWholeDays = true;
// 週のはじまりを月曜日に設定
ganttSheet.project.calendarSettings.weekStartOn = GCSG.DayOfWeek.Monday;
// 作業開始/終了時刻
ganttSheet.project.calendarSettings.defaultStartTime = { hour: 8, minute: 30 };
ganttSheet.project.calendarSettings.defaultFinishTime = { hour: 17, minute: 30 };
// 稼働時間の単位を月に設定
ganttSheet.project.calendarSettings.defaultDurationUnit = "Month";
// 土曜日の10:00~16:00を稼働時間に設定
var calendar = ganttSheet.project.calendar;
var currentCustomWorkWeek = calendar.defaultWorkWeek;
var workday = calendar.defaultWorkWeek.getWorkDay(GCSG.DayOfWeek.Saturday);
var workTime = { start: { hour: 10, minute: 0 }, end: { hour: 16, minute: 0 } };
workday.push(workTime);
calendar.defaultWorkWeek = currentCustomWorkWeek;
ganttSheet.project.calendar = calendar;
// タイムスケールの部分をズーム表示
ganttSheet.project.timescale.zoomTo(1.5, false);
});
// タスク追加
document.getElementById("insertTask").addEventListener("click", function () {
addTask();
});
// タスク削除
document.getElementById("deleteTask").addEventListener("click", function () {
deleteTask();
});
// タスクリンク
document.getElementById("addLink").addEventListener("click", function () {
addLink();
});
// 出力
document.getElementById("exportExcel").addEventListener('click', function () {
exportFile();
});
}
// タスク追加
function addTask() {
var project = ganttSheet.project;
var selections = ganttSheet.getSelections();
if (isContainsRootTask(selections)) return;
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 createTaskDataList(count, initializer) {
var array = new Array(count);
for (var index = 0; index < count; index++) {
array[index] = initializer();
}
return array;
}
// タスク削除
function deleteTask() {
var selections = ganttSheet.getSelections();
if (isContainsRootTask(selections)) return;
var project = ganttSheet.project;
var rowIds = getSelectedRowIndexes();
project.removeTasks(rowIds);
}
// 親タスクの存在チェック
function isContainsRootTask(selections) {
var isRoot = false;
selections.forEach(s => {
if (s.row === 0) isRoot = true;
})
return isRoot;
}
// タスクのリンクを追加
function addLink() {
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;
project.addDependency({ fromTaskNumber, toTaskNumber });
}
project.resumeSchedule();
}
// 選択行のインデックス取得
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;
}
// Excelに出力
function exportFile() {
spread.export(function (blob) {
saveAs(blob, 'スプレッド邸新築工事工程管理表.xlsx');
}, function (e) {
}, {
fileType: GCSS.FileType.excel,
includeBindingSource: true,
saveAsView: true
});
}
enableProdMode();
bootstrapApplication(AppComponent);
<!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" />
<link rel="stylesheet" type="text/css" href="$DEMOROOT$/ja/angular/node_modules/@mescius/spread-sheets/styles/gc.spread.sheets.excel2013white.css">
<!-- Polyfills -->
<script src="$DEMOROOT$/ja/angular/node_modules/core-js/client/shim.min.js"></script>
<script src="$DEMOROOT$/ja/angular/node_modules/zone.js/fesm2015/zone.min.js"></script>
<!-- SystemJS -->
<script src="$DEMOROOT$/ja/angular/node_modules/systemjs/dist/system.js"></script>
<script src="systemjs.config.js"></script>
<script src="$DEMOROOT$/spread/source/js/FileSaver.js" type="text/javascript"></script>
<script src="data.js" type="text/javascript"></script>
<script>
// workaround to load 'rxjs/operators' from the rxjs bundle
System.import('rxjs').then(function (m) {
System.import('@angular/compiler');
System.set(SystemJS.resolveSync('rxjs/operators'), System.newModule(m.operators));
System.import('$DEMOROOT$/ja/lib/angular/license.ts');
System.import('./src/app.component');
});
</script>
</head>
<body>
<app-component></app-component>
</body>
</html>
<div class="sample-tutorial">
<gc-spread-sheets [hostStyle]="hostStyle" (workbookInitialized)="init($event)">
<gc-worksheet></gc-worksheet>
</gc-spread-sheets>
</div>
<div class="btn-container">
<button id="insertTask" class="floatBtn">追加</button>
<button id="deleteTask" class="floatBtn">削除</button>
<button id="addLink" class="floatBtn">リンク</button>
<button id="exportExcel" class="floatBtn">出力</button>
</div>
.sample-tutorial {
position: relative;
height: 100%;
overflow: hidden;
}
.sample {
width: 100%;
height: 100%;
}
body,
html {
padding: 0;
margin: 0;
width: 100%;
height: 100%;
position: relative;
overflow: hidden;
}
.spreadsheets {
width: 100%;
height: 100%;
}
.btn-container {
position: fixed;
bottom: 40px;
right: 5px;
padding: 6px 40px;
z-index: 1;
}
.floatBtn {
font-family: Arial, sans-serif;
font-size: 16px;
padding: 10px 15px;
margin: 10px;
color: white;
background-color: #2196f3;
border: none;
border-radius: 25px;
cursor: pointer;
transition: background-color 0.3s ease, transform 0.3s ease;
width: 80px;
text-align: center;
}
.floatBtn:hover {
background-color: #1976d2;
transform: scale(1.05);
}
.floatBtn:active {
background-color: #1565c0;
transform: scale(0.95);
}
(function (global) {
System.config({
transpiler: 'ts',
typescriptOptions: {
tsconfig: true
},
meta: {
'typescript': {
"exports": "ts"
},
'*.css': { loader: 'css' }
},
paths: {
// paths serve as alias
'npm:': 'node_modules/'
},
// map tells the System loader where to look for things
map: {
'core-js': 'npm:core-js/client/shim.min.js',
'zone': 'npm:zone.js/fesm2015/zone.min.js',
'rxjs': 'npm:rxjs/dist/bundles/rxjs.umd.min.js',
'@angular/core': 'npm:@angular/core/fesm2022',
'@angular/common': 'npm:@angular/common/fesm2022/common.mjs',
'@angular/compiler': 'npm:@angular/compiler/fesm2022/compiler.mjs',
'@angular/platform-browser': 'npm:@angular/platform-browser/fesm2022/platform-browser.mjs',
'@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/fesm2022/platform-browser-dynamic.mjs',
'@angular/common/http': 'npm:@angular/common/fesm2022/http.mjs',
'@angular/router': 'npm:@angular/router/fesm2022/router.mjs',
'@angular/forms': 'npm:@angular/forms/fesm2022/forms.mjs',
'jszip': 'npm:jszip/dist/jszip.min.js',
'typescript': 'npm:typescript/lib/typescript.js',
'ts': './plugin.js',
'tslib':'npm:tslib/tslib.js',
'css': 'npm:systemjs-plugin-css/css.js',
'plugin-babel': 'npm:systemjs-plugin-babel/plugin-babel.js',
'systemjs-babel-build':'npm:systemjs-plugin-babel/systemjs-babel-browser.js',
'@mescius/spread-sheets': 'npm:@mescius/spread-sheets/index.js',
'@mescius/spread-sheets-io': 'npm:@mescius/spread-sheets-io/index.js',
'@mescius/spread-sheets-tablesheet': 'npm:@mescius/spread-sheets-tablesheet/index.js',
'@mescius/spread-sheets-ganttsheet': 'npm:@mescius/spread-sheets-ganttsheet/index.js',
'@mescius/spread-sheets-angular': 'npm:@mescius/spread-sheets-angular/fesm2020/mescius-spread-sheets-angular.mjs',
'@mescius/spread-sheets-resources-ja': 'npm:@mescius/spread-sheets-resources-ja/index.js',
'@grapecity/jsob-test-dependency-package/react-components': 'npm:@grapecity/jsob-test-dependency-package/react-components/index.js'
},
// packages tells the System loader how to load when no filename and/or no extension
packages: {
src: {
defaultExtension: 'ts',
meta: {
"*.component.ts": {
loader: "system.component-loader.js"
}
}
},
rxjs: {
defaultExtension: 'js'
},
"node_modules": {
defaultExtension: 'js'
},
"node_modules/@angular": {
defaultExtension: 'mjs'
},
"@mescius/spread-sheets-angular": {
defaultExtension: 'mjs'
},
'@angular/core': {
defaultExtension: 'mjs',
main: 'core.mjs'
}
}
});
})(this);