From 8b9ceabbb9b44ef71dfdcaae4c0ad107f27a5e03 Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Thu, 14 Nov 2024 20:32:11 +0800 Subject: [PATCH 01/40] feat: multiple rows tasks --- .../examples/gantt/gantt-Sub_Tasks_Inline.ts | 510 +++++++++ .../examples/gantt/gantt-table-groupBy.ts | 986 ++++++++++++++++++ packages/vtable-gantt/examples/menu.ts | 8 + packages/vtable-gantt/src/Gantt.ts | 91 +- packages/vtable-gantt/src/data/DataSource.ts | 18 +- .../vtable-gantt/src/event/event-manager.ts | 11 +- packages/vtable-gantt/src/gantt-helper.ts | 18 +- .../src/scenegraph/dependency-link.ts | 51 +- .../vtable-gantt/src/scenegraph/task-bar.ts | 42 +- .../vtable-gantt/src/state/state-manager.ts | 35 +- .../src/sub-tasks-inline-helper.ts | 0 .../vtable-gantt/src/ts-types/gantt-engine.ts | 11 + 12 files changed, 1739 insertions(+), 42 deletions(-) create mode 100644 packages/vtable-gantt/examples/gantt/gantt-Sub_Tasks_Inline.ts create mode 100644 packages/vtable-gantt/examples/gantt/gantt-table-groupBy.ts create mode 100644 packages/vtable-gantt/src/sub-tasks-inline-helper.ts diff --git a/packages/vtable-gantt/examples/gantt/gantt-Sub_Tasks_Inline.ts b/packages/vtable-gantt/examples/gantt/gantt-Sub_Tasks_Inline.ts new file mode 100644 index 000000000..4273e9771 --- /dev/null +++ b/packages/vtable-gantt/examples/gantt/gantt-Sub_Tasks_Inline.ts @@ -0,0 +1,510 @@ +import type { ColumnsDefine } from '@visactor/vtable'; +import { register } from '@visactor/vtable'; +import { DateInputEditor, InputEditor } from '@visactor/vtable-editors'; +import type { GanttConstructorOptions, TYPES } from '../../src/index'; +import { Gantt } from '../../src/index'; +import { bindDebugTool } from '../../../vtable/src/scenegraph/debug-tool'; +import { scale } from '@visactor/vutils'; +import { DependencyType, ShowHierarchyMode } from '../../src/ts-types'; +const CONTAINER_ID = 'vTable'; +const date_input_editor = new DateInputEditor({}); +const input_editor = new InputEditor({}); +register.editor('input', input_editor); +register.editor('date-input', date_input_editor); +export function createTable() { + const records = [ + { + id: 100, + title: 'Software Development', + children: [ + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-07-27', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024/08/01', + end: '2024/08/04', + progress: 100, + priority: 'P1' + } + ] + }, + { + id: 200, + title: 'Scope' + }, + { + id: 300, + title: 'Determine project scope', + children: [ + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-08-04', + end: '2024-08-04', + progress: 90, + priority: 'P0' + }, + + { + id: 4, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024.07.06', + end: '2024.07.08', + progress: 60, + priority: 'P0' + }, + { + id: 5, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '07.24.2024', + end: '08.02.2024', + progress: 31, + priority: 'P0' + }, + { + id: 6, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 7, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-08-09', + end: '2024-09-11', + progress: 100, + priority: 'P1' + } + ] + }, + + { + id: 100, + title: 'Software Development' + }, + { + id: 200, + title: 'Scope', + children: [ + { + id: 8, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1', + children: [ + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + } + ] + }, + { + id: 9, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0', + children: [ + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + } + ] + }, + { + id: 10, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 11, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + }, + { + id: 12, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 13, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-08-01', + end: '2024-08-04', + progress: 60, + priority: 'P0' + }, + { + id: 14, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-08-03', + end: '2024-08-05', + progress: 100, + priority: 'P1' + } + ] + }, + + { + id: 300, + title: 'Determine project scope', + children: [ + { + id: 15, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 16, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 17, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + }, + { + id: 18, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-30', + end: '2024-08-14', + progress: 31, + priority: 'P0' + }, + { + id: 19, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 60, + priority: 'P0' + } + ] + }, + { + id: 100, + title: 'Software Development', + children: [ + { + id: 20, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024/07/24', + end: '2024/08/04', + progress: 100, + priority: 'P1' + }, + { + id: 21, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-08-04', + end: '2024-08-04', + progress: 90, + priority: 'P0' + }, + { + id: 22, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '07/24/2024', + end: '08/04/2024', + progress: 60, + priority: 'P0' + } + ] + } + ]; + + const columns: ColumnsDefine = [ + { + field: 'title', + title: 'title', + width: 200, + tree: true, + sort: true + } + ]; + const option: GanttConstructorOptions = { + records, + taskListTable: { + columns: columns, + minTableWidth: 100, + hierarchyExpandLevel: 5, + menu: { + contextMenuItems: ['copy', 'paste', 'delete', '...'] + } + }, + groupBy: true, + showHierarchyMode: ShowHierarchyMode.Sub_Tasks_Inline, + frame: { + outerFrameStyle: { + borderLineWidth: 2, + borderColor: 'red', + cornerRadius: 8 + }, + verticalSplitLineHighlight: { + lineColor: 'green', + lineWidth: 3 + }, + verticalSplitLineMoveable: true + }, + grid: { + backgroundColor: 'gray', + verticalLine: { + lineWidth: 1, + lineColor: '#e1e4e8' + } + }, + headerRowHeight: 60, + rowHeight: 40, + taskBar: { + startDateField: 'start', + endDateField: 'end', + progressField: 'progress', + labelText: '{title} {progress}%', + labelTextStyle: { + fontFamily: 'Arial', + fontSize: 16, + textAlign: 'left' + }, + barStyle: { + width: 20, + /** 任务条的颜色 */ + barColor: '#ee8800', + /** 已完成部分任务条的颜色 */ + completedBarColor: '#91e8e0', + /** 任务条的圆角 */ + cornerRadius: 8 + } + }, + dependency: { + links: [ + { + type: DependencyType.FinishToStart, + linkedFromTaskKey: 1, + linkedToTaskKey: 2 + } + // { + // type: DependencyType.StartToFinish, + // linkedFromTaskKey: 2, + // linkedToTaskKey: 3 + // }, + // { + // type: DependencyType.StartToStart, + // linkedFromTaskKey: 3, + // linkedToTaskKey: 4 + // }, + // { + // type: DependencyType.FinishToFinish, + // linkedFromTaskKey: 4, + // linkedToTaskKey: 5 + // } + ] + }, + timelineHeader: { + verticalLine: { + lineColor: '#e1e4e8', + lineWidth: 1 + }, + scales: [ + { + unit: 'week', + step: 1, + startOfWeek: 'sunday', + format(date: TYPES.DateFormatArgumentType) { + return `Week ${date.dateIndex}`; + }, + style: { + fontSize: 20, + fontWeight: 'bold', + color: 'red', + backgroundColor: '#EEF1F5' + } + }, + { + unit: 'day', + step: 1, + format(date: TYPES.DateFormatArgumentType) { + return date.dateIndex.toString(); + }, + style: { + fontSize: 20, + fontWeight: 'bold', + color: 'red', + backgroundColor: '#EEF1F5' + } + } + // { + // unit: 'quarter', + // step: 1, + // format(date: TYPES.DateFormatArgumentType) { + // return '第' + date.index + '季度'; + // } + // } + ] + }, + minDate: '2024-07-07', + maxDate: '2024-10-15', + markLine: [ + { + date: '2024-07-17', + style: { + lineWidth: 1, + lineColor: 'blue', + lineDash: [8, 4] + } + }, + { + date: '2024-08-17', + style: { + lineWidth: 2, + lineColor: 'red', + lineDash: [8, 4] + } + } + ], + rowSeriesNumber: { + title: '行号', + dragOrder: true, + headerStyle: { + bgColor: '#EEF1F5', + + borderColor: '#e1e4e8' + }, + style: { + borderColor: '#e1e4e8' + } + }, + scrollStyle: { + scrollRailColor: 'RGBA(246,246,246,0.5)', + visible: 'none', + width: 6, + scrollSliderCornerRadius: 2, + scrollSliderColor: '#5cb85c' + } + }; + // columns:[ + // { + // title:'2024-07', + // columns:[ + // { + // title:'01' + // }, + // { + // title:'02' + // }, + // ... + // ] + // }, + // ... + // ] + const ganttInstance = new Gantt(document.getElementById(CONTAINER_ID)!, option); + window.ganttInstance = ganttInstance; + ganttInstance.on('scroll', e => { + console.log('scroll', e); + }); + + ganttInstance.listTableInstance?.on('scroll', e => { + console.log('listTable scroll', e); + }); + // bindDebugTool(ganttInstance.scenegraph.stage as any, { + // customGrapicKeys: ['role', '_updateTag'] + // }); +} diff --git a/packages/vtable-gantt/examples/gantt/gantt-table-groupBy.ts b/packages/vtable-gantt/examples/gantt/gantt-table-groupBy.ts new file mode 100644 index 000000000..31e6de5ec --- /dev/null +++ b/packages/vtable-gantt/examples/gantt/gantt-table-groupBy.ts @@ -0,0 +1,986 @@ +import type { ColumnsDefine } from '@visactor/vtable'; +import type { GanttConstructorOptions, TYPES } from '../../src/index'; +import { Gantt } from '../../src/index'; +import { bindDebugTool } from '../../../vtable/src/scenegraph/debug-tool'; +const CONTAINER_ID = 'vTable'; + +export function createTable() { + const records = [ + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + progress: 31, + priority: 'P0' + }, + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + progress: 100, + priority: 'P1' + }, + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-08-04', + end: '2024-08-04', + progress: 90, + priority: 'P0' + }, + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '07/24/2024', + end: '08/04/2024', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 100, + priority: 'P1' + }, + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024.07.06', + end: '2024.07.08', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024/07/09', + end: '2024/07/11', + progress: 100, + priority: 'P1' + }, + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '07.24.2024', + end: '08.04.2024', + progress: 31, + priority: 'P0' + }, + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-08-09', + end: '2024-09-11', + progress: 100, + priority: 'P1' + }, + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + }, + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + }, + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + }, + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + }, + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + }, + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + }, + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + }, + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + }, + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + }, + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + }, + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-30', + end: '2024-08-14', + progress: 31, + priority: 'P0' + }, + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024/07/24', + end: '2024/08/04', + progress: 100, + priority: 'P1' + }, + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-08-04', + end: '2024-08-04', + progress: 90, + priority: 'P0' + }, + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '07/24/2024', + end: '08/04/2024', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 100, + priority: 'P1' + }, + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024.07.06', + end: '2024.07.08', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024/07/09', + end: '2024/07/11', + progress: 100, + priority: 'P1' + }, + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '07.24.2024', + end: '08.04.2024', + progress: 31, + priority: 'P0' + }, + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-08-09', + end: '2024-09-11', + progress: 100, + priority: 'P1' + }, + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + }, + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + }, + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + }, + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + }, + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + }, + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + }, + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + }, + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + }, + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + }, + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + } + ]; + + const columns: ColumnsDefine = [ + // { + // field: 'id', + // title: 'ID', + // width: 80, + // sort: true + // }, + { + field: 'title', + title: 'title', + width: 200, + sort: true + }, + { + field: 'start', + title: 'start', + width: 150, + sort: true + }, + { + field: 'end', + title: 'end', + width: 150, + sort: true + }, + { + field: 'priority', + title: 'priority', + width: 100, + sort: true + }, + + { + field: 'progress', + title: 'progress', + width: 200, + sort: true + } + ]; + const option: GanttConstructorOptions = { + records, + taskListTable: { + columns: columns, + tableWidth: 400, + minTableWidth: 100, + maxTableWidth: 600, + groupBy: 'priority' + }, + + frame: { + verticalSplitLineMoveable: true, + outerFrameStyle: { + borderLineWidth: 2, + borderColor: 'red', + cornerRadius: 8 + }, + verticalSplitLine: { + lineWidth: 3, + lineColor: '#e1e4e8' + }, + verticalSplitLineHighlight: { + lineColor: 'green', + lineWidth: 3 + } + }, + grid: { + // backgroundColor: 'gray', + verticalLine: { + lineWidth: 1, + lineColor: '#e1e4e8' + }, + horizontalLine: { + lineWidth: 1, + lineColor: '#e1e4e8' + } + }, + headerRowHeight: 60, + rowHeight: 40, + + taskBar: { + selectable: false, + startDateField: 'start', + endDateField: 'end', + progressField: 'progress', + labelText: '{title} {progress}%', + labelTextStyle: { + fontFamily: 'Arial', + fontSize: 16, + textAlign: 'left' + }, + barStyle: { + width: 20, + /** 任务条的颜色 */ + barColor: '#ee8800', + /** 已完成部分任务条的颜色 */ + completedBarColor: '#91e8e0', + /** 任务条的圆角 */ + cornerRadius: 10 + } + }, + timelineHeader: { + verticalLine: { + lineWidth: 1, + lineColor: '#e1e4e8' + }, + horizontalLine: { + lineWidth: 1, + lineColor: '#e1e4e8' + }, + backgroundColor: '#EEF1F5', + colWidth: 60, + scales: [ + { + unit: 'week', + step: 1, + startOfWeek: 'sunday', + format(date: TYPES.DateFormatArgumentType) { + return `Week ${date.dateIndex}`; + }, + style: { + fontSize: 20, + fontWeight: 'bold', + color: 'red' + } + }, + { + unit: 'day', + step: 1, + format(date: TYPES.DateFormatArgumentType) { + return date.dateIndex.toString(); + }, + style: { + fontSize: 20, + fontWeight: 'bold', + color: 'red' + } + } + // { + // unit: 'quarter', + // step: 1, + // format(date: TYPES.DateFormatArgumentType) { + // return '第' + date.index + '季度'; + // } + // } + ] + }, + minDate: '2024-07-01', + maxDate: '2024-10-15', + markLine: [ + { + date: '2024-07-17', + style: { + lineWidth: 1, + lineColor: 'blue', + lineDash: [8, 4] + } + }, + { + date: '2024-08-17', + position: 'middle', + scrollToMarkLine: true, + style: { + lineWidth: 2, + lineColor: 'red', + lineDash: [8, 4] + } + } + ], + rowSeriesNumber: { + title: '行号', + dragOrder: true, + headerStyle: { + bgColor: '#EEF1F5', + + borderColor: '#e1e4e8' + }, + style: { + borderColor: '#e1e4e8' + } + }, + scrollStyle: { + visible: 'scrolling' + }, + overscrollBehavior: 'none' + }; + // columns:[ + // { + // title:'2024-07', + // columns:[ + // { + // title:'01' + // }, + // { + // title:'02' + // }, + // ... + // ] + // }, + // ... + // ] + const ganttInstance = new Gantt(document.getElementById(CONTAINER_ID)!, option); + window.ganttInstance = ganttInstance; + ganttInstance.on('scroll', e => { + console.log('scroll', e); + }); + ganttInstance.on('change_date_range', e => { + console.log('change_date_range', e); + }); + ganttInstance.on('mouseenter_task_bar', e => { + console.log('mouseenter_taskbar', e); + }); + ganttInstance.on('mouseleave_task_bar', e => { + console.log('mouseleave_taskbar', e); + }); + ganttInstance.on('click_task_bar', e => { + console.log('click_task_bar', e); + }); + ganttInstance.listTableInstance?.on('scroll', e => { + console.log('listTable scroll', e); + }); + bindDebugTool(ganttInstance.scenegraph.stage as any, { + customGrapicKeys: ['role', '_updateTag'] + }); +} diff --git a/packages/vtable-gantt/examples/menu.ts b/packages/vtable-gantt/examples/menu.ts index 35a4780e0..d3b00dc3b 100644 --- a/packages/vtable-gantt/examples/menu.ts +++ b/packages/vtable-gantt/examples/menu.ts @@ -65,6 +65,14 @@ export const menus = [ { path: 'gantt', name: 'gantt-listTable' + }, + { + path: 'gantt', + name: 'gantt-table-groupBy' + }, + { + path: 'gantt', + name: 'gantt-Sub_Tasks_Inline' } // ] // } diff --git a/packages/vtable-gantt/src/Gantt.ts b/packages/vtable-gantt/src/Gantt.ts index 0f401fe4e..4e2dc5a92 100644 --- a/packages/vtable-gantt/src/Gantt.ts +++ b/packages/vtable-gantt/src/Gantt.ts @@ -24,6 +24,7 @@ import type { ITaskLinkSelectedStyle, IPointStyle } from './ts-types'; +import { ShowHierarchyMode } from './ts-types'; import type { ListTableConstructorOptions } from '@visactor/vtable'; import { themes, registerCheckboxCell, registerProgressBarCell, registerRadioCell, ListTable } from '@visactor/vtable'; import { EventManager } from './event/event-manager'; @@ -42,6 +43,7 @@ import { import { EventTarget } from './event/EventTarget'; import { createDateAtMidnight, formatDate, isPropertyWritable, parseDateFormat } from './tools/util'; import { DataSource } from './data/DataSource'; +import { isValid } from '@visactor/vutils'; // import { generateGanttChartColumns } from './gantt-helper'; export function createRootElement(padding: any, className: string = 'vtable-gantt'): HTMLElement { const element = document.createElement('div'); @@ -122,6 +124,7 @@ export class Gantt extends EventTarget { outerFrameStyle: IFrameStyle; pixelRatio: number; + showHierarchyMode: ShowHierarchyMode; startDateField: string; endDateField: string; @@ -323,6 +326,19 @@ export class Gantt extends EventTarget { listTable_options[key] = this.options.taskListTable[key]; if (key === 'columns') { listTable_options[key][listTable_options[key].length - 1].disableColumnResize = true; + if (this.parsedOptions.showHierarchyMode === ShowHierarchyMode.Sub_Tasks_Inline) { + for (let i = 0; i < listTable_options.columns.length; i++) { + if (listTable_options.columns[i].tree) { + listTable_options.columns[i].tree = false; + } + } + } + } + if ( + key === 'hierarchyExpandLevel' && + this.parsedOptions.showHierarchyMode === ShowHierarchyMode.Sub_Tasks_Inline + ) { + delete listTable_options[key]; } } // lineWidthArr[1] = 0; @@ -604,7 +620,10 @@ export class Gantt extends EventTarget { getTaskShowIndexByRecordIndex(index: number | number[]) { return this.taskListTableInstance.getBodyRowIndexByRecordIndex(index); } - getRecordByIndex(taskShowIndex: number) { + getRecordByIndex(taskShowIndex: number, sub_task_id?: number) { + if (isValid(sub_task_id)) { + return this.records[taskShowIndex]?.children?.[sub_task_id]; + } if (this.taskListTableInstance) { return this.taskListTableInstance.getRecordByRowCol( 0, @@ -674,14 +693,80 @@ export class Gantt extends EventTarget { progress }; } + /** + * 获取指定index处任务数据的具体信息 + * @param index + * @returns 当前任务信息 + */ + getTaskInfoByTaskListIndexs( + taskShowIndex: number, + subTaskIndex: number + ): { + taskRecord: any; + taskDays: number; + startDate: Date; + endDate: Date; + progress: number; + } { + const taskParentRecord = this.getRecordByIndex(taskShowIndex); + if (taskParentRecord.children?.length) { + const taskRecord = taskParentRecord.children[subTaskIndex]; + const startDateField = this.parsedOptions.startDateField; + const endDateField = this.parsedOptions.endDateField; + const progressField = this.parsedOptions.progressField; + const rawDateStartDateTime = createDateAtMidnight(taskRecord?.[startDateField]).getTime(); + const rawDateEndDateTime = createDateAtMidnight(taskRecord?.[endDateField]).getTime(); + if ( + rawDateEndDateTime < this.parsedOptions._minDateTime || + rawDateStartDateTime > this.parsedOptions._maxDateTime || + !taskRecord?.[startDateField] || + !taskRecord?.[endDateField] + ) { + return { + taskDays: 0, + progress: 0, + startDate: null, + endDate: null, + taskRecord + }; + } + const startDate = createDateAtMidnight( + Math.min(Math.max(this.parsedOptions._minDateTime, rawDateStartDateTime), this.parsedOptions._maxDateTime) + ); + const endDate = createDateAtMidnight( + Math.max(Math.min(this.parsedOptions._maxDateTime, rawDateEndDateTime), this.parsedOptions._minDateTime) + ); + const progress = convertProgress(taskRecord[progressField]); + const taskDays = Math.ceil(Math.abs(endDate.getTime() - startDate.getTime()) / (1000 * 60 * 60 * 24)) + 1; + return { + taskRecord, + taskDays, + startDate, + endDate, + progress + }; + } + return { + taskDays: 0, + progress: 0, + startDate: null, + endDate: null, + taskRecord: null + }; + } /** * 拖拽任务条或者调整任务条尺寸修改日期更新到数据中 * @param updateDateType * @param days * @param index */ - _updateDateToTaskRecord(updateDateType: 'move' | 'start-move' | 'end-move', days: number, index: number) { - const taskRecord = this.getRecordByIndex(index); + _updateDateToTaskRecord( + updateDateType: 'move' | 'start-move' | 'end-move', + days: number, + index: number, + sub_task_id?: number + ) { + const taskRecord = this.getRecordByIndex(index, sub_task_id); const startDateField = this.parsedOptions.startDateField; const endDateField = this.parsedOptions.endDateField; const dateFormat = this.parsedOptions.dateFormat ?? parseDateFormat(taskRecord[startDateField]); diff --git a/packages/vtable-gantt/src/data/DataSource.ts b/packages/vtable-gantt/src/data/DataSource.ts index d2931b208..1537d58d9 100644 --- a/packages/vtable-gantt/src/data/DataSource.ts +++ b/packages/vtable-gantt/src/data/DataSource.ts @@ -1,5 +1,6 @@ import type { Gantt } from '../Gantt'; import { createDateAtMidnight } from '../tools/util'; +import { ShowHierarchyMode } from '../ts-types'; export class DataSource { records: any[]; @@ -19,7 +20,11 @@ export class DataSource { let minDate = Number.MAX_SAFE_INTEGER; let maxDate = Number.MIN_SAFE_INTEGER; - if (needMinDate || needMaxDate) { + if ( + needMinDate || + needMaxDate || + this._gantt.parsedOptions.showHierarchyMode === ShowHierarchyMode.Sub_Tasks_Inline + ) { for (let i = 0; i < this.records.length; i++) { const record = this.records[i]; if (needMinDate) { @@ -30,6 +35,17 @@ export class DataSource { const recordMaxDate = createDateAtMidnight(record[this._gantt.parsedOptions.endDateField]); maxDate = Math.max(maxDate, recordMaxDate.getTime()); } + + if (this._gantt.parsedOptions.showHierarchyMode === ShowHierarchyMode.Sub_Tasks_Inline) { + // 将子任务按开始时间升序排列 + record.children && + record.children.sort((a: any, b: any) => { + return ( + createDateAtMidnight(a[this._gantt.parsedOptions.startDateField]).getTime() - + createDateAtMidnight(b[this._gantt.parsedOptions.startDateField]).getTime() + ); + }); + } } needMinDate && (this.minDate = createDateAtMidnight(minDate)); diff --git a/packages/vtable-gantt/src/event/event-manager.ts b/packages/vtable-gantt/src/event/event-manager.ts index 0fd516318..6165a4ccd 100644 --- a/packages/vtable-gantt/src/event/event-manager.ts +++ b/packages/vtable-gantt/src/event/event-manager.ts @@ -4,7 +4,7 @@ import type { Gantt } from '../Gantt'; import { EventHandler } from '../event/EventHandler'; import { handleWhell } from '../event/scroll'; import { formatDate, parseDateFormat, throttle } from '../tools/util'; -import { GANTT_EVENT_TYPE, InteractionState } from '../ts-types'; +import { GANTT_EVENT_TYPE, InteractionState, ShowHierarchyMode } from '../ts-types'; import { isValid } from '@visactor/vutils'; import { getPixelRatio } from '../tools/pixel-ratio'; import { DayTimes, getDateIndexByX, getTaskIndexByY } from '../gantt-helper'; @@ -163,10 +163,13 @@ function bindTableGroupListener(event: EventManager) { } } //#region hover到某一个任务 检查有没有日期安排,没有的话显示创建按钮 - if (gantt.parsedOptions.taskBarCreatable) { + if ( + gantt.parsedOptions.showHierarchyMode !== ShowHierarchyMode.Sub_Tasks_Inline && + gantt.parsedOptions.taskBarCreatable + ) { const taskIndex = getTaskIndexByY(e.offset.y, gantt); const recordTaskInfo = gantt.getTaskInfoByTaskListIndex(taskIndex); - if (!recordTaskInfo.taskDays && recordTaskInfo.taskRecord) { + if (!recordTaskInfo.taskDays && recordTaskInfo.taskRecord && !recordTaskInfo.taskRecord.vtableMerge) { const dateIndex = getDateIndexByX(e.offset.x, gantt); const showX = dateIndex * gantt.parsedOptions.timelineColWidth - gantt.stateManager.scroll.horizontalBarPos; const showY = taskIndex * gantt.parsedOptions.rowHeight - gantt.stateManager.scroll.verticalBarPos; @@ -509,7 +512,7 @@ function bindContainerDomListener(eventManager: EventManager) { if (stateManager.isResizingTableWidth()) { stateManager.endResizeTableWidth(); } else if (stateManager.isMoveingTaskBar()) { - stateManager.endMoveTaskBar(e.x); + stateManager.endMoveTaskBar(); } else if (stateManager.isResizingTaskBar()) { stateManager.endResizeTaskBar(e.x); } diff --git a/packages/vtable-gantt/src/gantt-helper.ts b/packages/vtable-gantt/src/gantt-helper.ts index 005e6af66..01d4b40cb 100644 --- a/packages/vtable-gantt/src/gantt-helper.ts +++ b/packages/vtable-gantt/src/gantt-helper.ts @@ -1,6 +1,12 @@ import { text } from 'stream/consumers'; import type { Gantt } from './Gantt'; -import type { IMarkLine, IScrollStyle, ITimelineDateInfo, ITimelineScale } from './ts-types'; +import { + ShowHierarchyMode, + type IMarkLine, + type IScrollStyle, + type ITimelineDateInfo, + type ITimelineScale +} from './ts-types'; import { createDateAtMidnight, getWeekNumber } from './tools/util'; const isNode = typeof window === 'undefined' || typeof window.window === 'undefined'; @@ -88,6 +94,7 @@ export { isNode }; export function initOptions(gantt: Gantt) { const options = gantt.options; + gantt.parsedOptions.showHierarchyMode = options?.showHierarchyMode ?? ShowHierarchyMode.Full; gantt.parsedOptions.pixelRatio = options?.pixelRatio ?? 1; gantt.parsedOptions.rowHeight = options?.rowHeight ?? 40; gantt.parsedOptions.timelineColWidth = options?.timelineHeader?.colWidth ?? 60; @@ -597,7 +604,8 @@ export function updateSplitLineAndResizeLine(gantt: Gantt) { export function findRecordByTaskKey( records: any[], taskKeyField: string, - taskKey: string | number | (string | number)[] + taskKey: string | number | (string | number)[], + childrenField: string = 'children' ): { record: any; index: number[] } | undefined { for (let i = 0; i < records.length; i++) { if ( @@ -605,10 +613,10 @@ export function findRecordByTaskKey( records[i][taskKeyField] === taskKey ) { return { record: records[i], index: [i] }; - } else if (records[i].children?.length) { + } else if (records[i][childrenField]?.length) { if (Array.isArray(taskKey) && taskKey[0] === records[i][taskKeyField]) { const result: { record: any; index: number[] } | undefined = findRecordByTaskKey( - records[i].children, + records[i][childrenField], taskKeyField, taskKey.slice(1) ); @@ -618,7 +626,7 @@ export function findRecordByTaskKey( } } else if (!Array.isArray(taskKey)) { const result: { record: any; index: number[] } | undefined = findRecordByTaskKey( - records[i].children, + records[i][childrenField], taskKeyField, taskKey ); diff --git a/packages/vtable-gantt/src/scenegraph/dependency-link.ts b/packages/vtable-gantt/src/scenegraph/dependency-link.ts index 727eaee0b..d3751f52b 100644 --- a/packages/vtable-gantt/src/scenegraph/dependency-link.ts +++ b/packages/vtable-gantt/src/scenegraph/dependency-link.ts @@ -7,7 +7,7 @@ import { isValid } from '@visactor/vutils'; import { findRecordByTaskKey, getTextPos } from '../gantt-helper'; import type { GanttTaskBarNode } from './gantt-node'; import type { ITaskLink } from '../ts-types'; -import { DependencyType } from '../ts-types'; +import { DependencyType, ShowHierarchyMode } from '../ts-types'; export class DependencyLink { group: Group; @@ -65,19 +65,44 @@ export class DependencyLink { } linkedFromTaskRecord.record.vtable_gantt_linkedFrom.push(link); - const linkedFromTaskShowIndex = this._scene._gantt.getTaskShowIndexByRecordIndex(linkedFromTaskRecord.index); - const linkedToTaskShowIndex = this._scene._gantt.getTaskShowIndexByRecordIndex(linkedToTaskRecord.index); + let linkedToTaskStartDate; + let linkedToTaskEndDate; + let linkedToTaskTaskDays; + let linkedFromTaskStartDate; + let linkedFromTaskEndDate; + let linkedFromTaskTaskDays; - const { - startDate: linkedToTaskStartDate, - endDate: linkedToTaskEndDate, - taskDays: linkedToTaskTaskDays - } = this._scene._gantt.getTaskInfoByTaskListIndex(linkedToTaskShowIndex); - const { - startDate: linkedFromTaskStartDate, - endDate: linkedFromTaskEndDate, - taskDays: linkedFromTaskTaskDays - } = this._scene._gantt.getTaskInfoByTaskListIndex(linkedFromTaskShowIndex); + let linkedToTaskShowIndex; + let linkedFromTaskShowIndex; + + if (this._scene._gantt.parsedOptions.showHierarchyMode === ShowHierarchyMode.Sub_Tasks_Inline) { + linkedFromTaskShowIndex = linkedFromTaskRecord.index[0]; + linkedToTaskShowIndex = linkedToTaskRecord.index[0]; + ({ + startDate: linkedToTaskStartDate, + endDate: linkedToTaskEndDate, + taskDays: linkedToTaskTaskDays + } = this._scene._gantt.getTaskInfoByTaskListIndexs(linkedToTaskRecord.index[0], linkedToTaskRecord.index[1])); + ({ + startDate: linkedFromTaskStartDate, + endDate: linkedFromTaskEndDate, + taskDays: linkedFromTaskTaskDays + } = this._scene._gantt.getTaskInfoByTaskListIndexs(linkedFromTaskRecord.index[0], linkedFromTaskRecord.index[1])); + } else { + linkedFromTaskShowIndex = this._scene._gantt.getTaskShowIndexByRecordIndex(linkedFromTaskRecord.index); + linkedToTaskShowIndex = this._scene._gantt.getTaskShowIndexByRecordIndex(linkedToTaskRecord.index); + + ({ + startDate: linkedToTaskStartDate, + endDate: linkedToTaskEndDate, + taskDays: linkedToTaskTaskDays + } = this._scene._gantt.getTaskInfoByTaskListIndex(linkedToTaskShowIndex)); + ({ + startDate: linkedFromTaskStartDate, + endDate: linkedFromTaskEndDate, + taskDays: linkedFromTaskTaskDays + } = this._scene._gantt.getTaskInfoByTaskListIndex(linkedFromTaskShowIndex)); + } if (!linkedFromTaskTaskDays || !linkedToTaskTaskDays) { return; } diff --git a/packages/vtable-gantt/src/scenegraph/task-bar.ts b/packages/vtable-gantt/src/scenegraph/task-bar.ts index b73cd44cd..a2c1dab63 100644 --- a/packages/vtable-gantt/src/scenegraph/task-bar.ts +++ b/packages/vtable-gantt/src/scenegraph/task-bar.ts @@ -5,6 +5,7 @@ import { createDateAtMidnight, parseStringTemplate, toBoxArray } from '../tools/ import { isValid } from '@visactor/vutils'; import { getTextPos } from '../gantt-helper'; import { GanttTaskBarNode } from './gantt-node'; +import { ShowHierarchyMode } from '../ts-types'; const TASKBAR_HOVER_ICON = ` @@ -54,15 +55,40 @@ export class TaskBar { this.group.appendChild(this.barContainer); for (let i = 0; i < this._scene._gantt.itemCount; i++) { - const barGroup = this.initBar(i); - if (barGroup) { - this.barContainer.appendChild(barGroup); + if (this._scene._gantt.parsedOptions.showHierarchyMode === ShowHierarchyMode.Sub_Tasks_Inline) { + const record = this._scene._gantt.getRecordByIndex(i); + if (record.children?.length > 0) { + for (let j = 0; j < record.children.length; j++) { + const barGroup = this.initBar(i, j); + if (barGroup) { + this.barContainer.appendChild(barGroup); + } + } + } + continue; + } else { + const barGroup = this.initBar(i); + if (barGroup) { + this.barContainer.appendChild(barGroup); + } } } } - initBar(index: number) { + initBar(index: number, childIndex?: number) { const taskBarCustomLayout = this._scene._gantt.parsedOptions.taskBarCustomLayout; - const { startDate, endDate, taskDays, progress, taskRecord } = this._scene._gantt.getTaskInfoByTaskListIndex(index); + let startDate; + let endDate; + let taskDays; + let progress; + let taskRecord; + if (isValid(childIndex)) { + ({ startDate, endDate, taskDays, progress, taskRecord } = this._scene._gantt.getTaskInfoByTaskListIndexs( + index, + childIndex + )); + } else { + ({ startDate, endDate, taskDays, progress, taskRecord } = this._scene._gantt.getTaskInfoByTaskListIndex(index)); + } if (taskDays <= 0 || !startDate || !endDate) { return null; } @@ -85,6 +111,8 @@ export class TaskBar { }); barGroup.name = 'task-bar'; barGroup.id = index; + barGroup.sub_task_id = childIndex; + barGroup.record = taskRecord; // this.barContainer.appendChild(barGroup); let rootContainer; let renderDefaultBar = true; @@ -397,13 +425,13 @@ export class TaskBar { this.selectedBorders[0].appendChild(line); } - getTaskBarNodeByIndex(index: number) { + getTaskBarNodeByIndex(index: number, sub_task_id?: number) { let c = this.barContainer.firstChild as Group; if (!c) { return null; } for (let i = 0; i < this.barContainer.childrenCount; i++) { - if (c.id === index) { + if (c.id === index && (!isValid(sub_task_id) || (isValid(sub_task_id) && c.sub_task_id === sub_task_id))) { return c; } c = c._next as Group; diff --git a/packages/vtable-gantt/src/state/state-manager.ts b/packages/vtable-gantt/src/state/state-manager.ts index f3722ca6e..3b1516f3c 100644 --- a/packages/vtable-gantt/src/state/state-manager.ts +++ b/packages/vtable-gantt/src/state/state-manager.ts @@ -1,7 +1,7 @@ import { clone, cloneDeep, isValid } from '@visactor/vutils'; import type { Gantt } from '../Gantt'; import type { ITaskLink } from '../ts-types'; -import { InteractionState, GANTT_EVENT_TYPE, DependencyType } from '../ts-types'; +import { InteractionState, GANTT_EVENT_TYPE, DependencyType, ShowHierarchyMode } from '../ts-types'; import type { Group, FederatedPointerEvent, Polygon, Line, Circle } from '@visactor/vtable/es/vrender'; import { syncEditCellFromTable, @@ -319,7 +319,7 @@ export class StateManager { isMoveingTaskBar() { return this.moveTaskBar.moving; } - endMoveTaskBar(x: number) { + endMoveTaskBar() { if (this.moveTaskBar.moveTaskBarXInertia.isInertiaScrolling()) { this.moveTaskBar.moveTaskBarXInertia.endInertia(); } @@ -340,11 +340,13 @@ export class StateManager { // } if (Math.abs(days) >= 1) { - const taskIndex = getTaskIndexByY(this.moveTaskBar.startOffsetY, this._gantt); - const oldRecord = this._gantt.getRecordByIndex(taskIndex); + // const taskIndex = getTaskIndexByY(this.moveTaskBar.startOffsetY, this._gantt); + const taskIndex = this.moveTaskBar.target.id; + const sub_task_id = this.moveTaskBar.target.sub_task_id; + const oldRecord = this._gantt.getRecordByIndex(taskIndex, sub_task_id); const oldStartDate = oldRecord[this._gantt.parsedOptions.startDateField]; const oldEndDate = oldRecord[this._gantt.parsedOptions.endDateField]; - this._gantt._updateDateToTaskRecord('move', days, taskIndex); + this._gantt._updateDateToTaskRecord('move', days, taskIndex, sub_task_id); if (this._gantt.hasListeners(GANTT_EVENT_TYPE.CHANGE_DATE_RANGE)) { const newRecord = this._gantt.getRecordByIndex(taskIndex); this._gantt.fireListeners(GANTT_EVENT_TYPE.CHANGE_DATE_RANGE, { @@ -692,12 +694,24 @@ export class StateManager { const { taskKeyField, dependencyLinks } = this._gantt.parsedOptions; const { linkedToTaskKey, linkedFromTaskKey, type } = link; + let linkFrom_index; + let linkFrom_sub_task_index; + let linkTo_index; + let linkTo_sub_task_index; const linkedToTaskRecord = findRecordByTaskKey(this._gantt.records, taskKeyField, linkedToTaskKey); const linkedFromTaskRecord = findRecordByTaskKey(this._gantt.records, taskKeyField, linkedFromTaskKey); - const linkedFromTaskShowIndex = this._gantt.getTaskShowIndexByRecordIndex(linkedFromTaskRecord.index); - const linkedToTaskShowIndex = this._gantt.getTaskShowIndexByRecordIndex(linkedToTaskRecord.index); + if (this._gantt.parsedOptions.showHierarchyMode === ShowHierarchyMode.Sub_Tasks_Inline) { + linkFrom_index = linkedFromTaskRecord.index[0]; + linkFrom_sub_task_index = linkedFromTaskRecord.index[1]; + linkTo_index = linkedToTaskRecord.index[0]; + linkTo_sub_task_index = linkedToTaskRecord.index[1]; + } else { + linkFrom_index = this._gantt.getTaskShowIndexByRecordIndex(linkedFromTaskRecord.index); + linkTo_index = this._gantt.getTaskShowIndexByRecordIndex(linkedToTaskRecord.index); + } const fromTaskNode = this._gantt.scenegraph.taskBar.getTaskBarNodeByIndex( - linkedFromTaskShowIndex + linkFrom_index, + linkFrom_sub_task_index ) as GanttTaskBarNode; this._gantt.scenegraph.taskBar.createSelectedBorder( fromTaskNode.attribute.x, @@ -707,7 +721,10 @@ export class StateManager { fromTaskNode, false ); - const toTaskNode = this._gantt.scenegraph.taskBar.getTaskBarNodeByIndex(linkedToTaskShowIndex) as GanttTaskBarNode; + const toTaskNode = this._gantt.scenegraph.taskBar.getTaskBarNodeByIndex( + linkTo_index, + linkTo_sub_task_index + ) as GanttTaskBarNode; this._gantt.scenegraph.taskBar.createSelectedBorder( toTaskNode.attribute.x, toTaskNode.attribute.y, diff --git a/packages/vtable-gantt/src/sub-tasks-inline-helper.ts b/packages/vtable-gantt/src/sub-tasks-inline-helper.ts new file mode 100644 index 000000000..e69de29bb diff --git a/packages/vtable-gantt/src/ts-types/gantt-engine.ts b/packages/vtable-gantt/src/ts-types/gantt-engine.ts index aaac755d2..ebab08bd8 100644 --- a/packages/vtable-gantt/src/ts-types/gantt-engine.ts +++ b/packages/vtable-gantt/src/ts-types/gantt-engine.ts @@ -187,6 +187,9 @@ export interface GanttConstructorOptions { /** 表格绘制范围外的canvas上填充的颜色 */ underlayBackgroundColor?: string; + groupBy?: true | string | string[]; + /** 展示嵌套结构数据时的模式,默认为full。*/ + showHierarchyMode?: ShowHierarchyMode; } /** * IBarLabelText @@ -329,6 +332,14 @@ export enum DependencyType { FinishToFinish = 'finish_to_finish', StartToFinish = 'start_to_finish' } +export enum ShowHierarchyMode { + /** 每一个节点用单独一行来展示,也就是父子任务的节点都占用一行 */ + Full = 'full', + /** 省去父节点,把所有子任务的节点都放到同一行来展示。 */ + Sub_Tasks_Inline = 'sub_tasks_inline', + /** 所有子任务的节点分别用一行展示。*/ + Sub_Tasks = 'sub_tasks' +} export type ITaskBarSelectedStyle = { shadowBlur?: number; //阴影宽度 shadowOffsetX?: number; //x方向偏移 From cd50941c0fff68f6c47738ce78897f510fdf3a25 Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Fri, 15 Nov 2024 16:22:32 +0800 Subject: [PATCH 02/40] feat: dependency line can create with sub_task case --- .../examples/gantt/gantt-Sub_Tasks_Inline.ts | 67 +------ packages/vtable-gantt/src/Gantt.ts | 144 +++++++------- .../vtable-gantt/src/event/event-manager.ts | 21 +- .../src/scenegraph/dependency-link.ts | 55 +++++- .../vtable-gantt/src/scenegraph/gantt-node.ts | 4 + .../vtable-gantt/src/scenegraph/scenegraph.ts | 3 - .../vtable-gantt/src/scenegraph/task-bar.ts | 13 +- .../vtable-gantt/src/state/state-manager.ts | 183 +++++++++++++----- packages/vtable-gantt/src/ts-types/events.ts | 5 + 9 files changed, 293 insertions(+), 202 deletions(-) diff --git a/packages/vtable-gantt/examples/gantt/gantt-Sub_Tasks_Inline.ts b/packages/vtable-gantt/examples/gantt/gantt-Sub_Tasks_Inline.ts index 4273e9771..41316a8d8 100644 --- a/packages/vtable-gantt/examples/gantt/gantt-Sub_Tasks_Inline.ts +++ b/packages/vtable-gantt/examples/gantt/gantt-Sub_Tasks_Inline.ts @@ -109,36 +109,7 @@ export function createTable() { start: '2024-07-09', end: '2024-07-11', progress: 100, - priority: 'P1', - children: [ - { - id: 1, - title: 'Software Development', - developer: 'liufangfang.jane@bytedance.com', - start: '2024-07-24', - end: '2024-08-04', - progress: 31, - priority: 'P0' - }, - { - id: 2, - title: 'Scope', - developer: 'liufangfang.jane@bytedance.com', - start: '2024-07-06', - end: '2024-07-08', - progress: 60, - priority: 'P0' - }, - { - id: 3, - title: 'Determine project scope', - developer: 'liufangfang.jane@bytedance.com', - start: '2024-07-09', - end: '2024-07-11', - progress: 100, - priority: 'P1' - } - ] + priority: 'P1' }, { id: 9, @@ -147,36 +118,7 @@ export function createTable() { start: '2024-07-24', end: '2024-08-04', progress: 31, - priority: 'P0', - children: [ - { - id: 1, - title: 'Software Development', - developer: 'liufangfang.jane@bytedance.com', - start: '2024-07-24', - end: '2024-08-04', - progress: 31, - priority: 'P0' - }, - { - id: 2, - title: 'Scope', - developer: 'liufangfang.jane@bytedance.com', - start: '2024-07-06', - end: '2024-07-08', - progress: 60, - priority: 'P0' - }, - { - id: 3, - title: 'Determine project scope', - developer: 'liufangfang.jane@bytedance.com', - start: '2024-07-09', - end: '2024-07-11', - progress: 100, - priority: 'P1' - } - ] + priority: 'P0' }, { id: 10, @@ -375,10 +317,11 @@ export function createTable() { } }, dependency: { + linkCreatable: true, links: [ { - type: DependencyType.FinishToStart, - linkedFromTaskKey: 1, + type: DependencyType.StartToFinish, + linkedFromTaskKey: 3, linkedToTaskKey: 2 } // { diff --git a/packages/vtable-gantt/src/Gantt.ts b/packages/vtable-gantt/src/Gantt.ts index 4e2dc5a92..54213b577 100644 --- a/packages/vtable-gantt/src/Gantt.ts +++ b/packages/vtable-gantt/src/Gantt.ts @@ -620,9 +620,9 @@ export class Gantt extends EventTarget { getTaskShowIndexByRecordIndex(index: number | number[]) { return this.taskListTableInstance.getBodyRowIndexByRecordIndex(index); } - getRecordByIndex(taskShowIndex: number, sub_task_id?: number) { - if (isValid(sub_task_id)) { - return this.records[taskShowIndex]?.children?.[sub_task_id]; + getRecordByIndex(taskShowIndex: number, sub_task_index?: number) { + if (isValid(sub_task_index)) { + return this.records[taskShowIndex]?.children?.[sub_task_index]; } if (this.taskListTableInstance) { return this.taskListTableInstance.getRecordByRowCol( @@ -650,14 +650,17 @@ export class Gantt extends EventTarget { * @param index * @returns 当前任务信息 */ - getTaskInfoByTaskListIndex(taskShowIndex: number): { + getTaskInfoByTaskListIndex( + taskShowIndex: number, + sub_task_index?: number + ): { taskRecord: any; taskDays: number; startDate: Date; endDate: Date; progress: number; } { - const taskRecord = this.getRecordByIndex(taskShowIndex); + const taskRecord = this.getRecordByIndex(taskShowIndex, sub_task_index); const startDateField = this.parsedOptions.startDateField; const endDateField = this.parsedOptions.endDateField; const progressField = this.parsedOptions.progressField; @@ -693,67 +696,67 @@ export class Gantt extends EventTarget { progress }; } - /** - * 获取指定index处任务数据的具体信息 - * @param index - * @returns 当前任务信息 - */ - getTaskInfoByTaskListIndexs( - taskShowIndex: number, - subTaskIndex: number - ): { - taskRecord: any; - taskDays: number; - startDate: Date; - endDate: Date; - progress: number; - } { - const taskParentRecord = this.getRecordByIndex(taskShowIndex); - if (taskParentRecord.children?.length) { - const taskRecord = taskParentRecord.children[subTaskIndex]; - const startDateField = this.parsedOptions.startDateField; - const endDateField = this.parsedOptions.endDateField; - const progressField = this.parsedOptions.progressField; - const rawDateStartDateTime = createDateAtMidnight(taskRecord?.[startDateField]).getTime(); - const rawDateEndDateTime = createDateAtMidnight(taskRecord?.[endDateField]).getTime(); - if ( - rawDateEndDateTime < this.parsedOptions._minDateTime || - rawDateStartDateTime > this.parsedOptions._maxDateTime || - !taskRecord?.[startDateField] || - !taskRecord?.[endDateField] - ) { - return { - taskDays: 0, - progress: 0, - startDate: null, - endDate: null, - taskRecord - }; - } - const startDate = createDateAtMidnight( - Math.min(Math.max(this.parsedOptions._minDateTime, rawDateStartDateTime), this.parsedOptions._maxDateTime) - ); - const endDate = createDateAtMidnight( - Math.max(Math.min(this.parsedOptions._maxDateTime, rawDateEndDateTime), this.parsedOptions._minDateTime) - ); - const progress = convertProgress(taskRecord[progressField]); - const taskDays = Math.ceil(Math.abs(endDate.getTime() - startDate.getTime()) / (1000 * 60 * 60 * 24)) + 1; - return { - taskRecord, - taskDays, - startDate, - endDate, - progress - }; - } - return { - taskDays: 0, - progress: 0, - startDate: null, - endDate: null, - taskRecord: null - }; - } + // /** + // * 获取指定index处任务数据的具体信息 + // * @param index + // * @returns 当前任务信息 + // */ + // getTaskInfoByTaskListIndexs( + // taskShowIndex: number, + // subTaskIndex: number + // ): { + // taskRecord: any; + // taskDays: number; + // startDate: Date; + // endDate: Date; + // progress: number; + // } { + // const taskParentRecord = this.getRecordByIndex(taskShowIndex); + // if (taskParentRecord.children?.length) { + // const taskRecord = taskParentRecord.children[subTaskIndex]; + // const startDateField = this.parsedOptions.startDateField; + // const endDateField = this.parsedOptions.endDateField; + // const progressField = this.parsedOptions.progressField; + // const rawDateStartDateTime = createDateAtMidnight(taskRecord?.[startDateField]).getTime(); + // const rawDateEndDateTime = createDateAtMidnight(taskRecord?.[endDateField]).getTime(); + // if ( + // rawDateEndDateTime < this.parsedOptions._minDateTime || + // rawDateStartDateTime > this.parsedOptions._maxDateTime || + // !taskRecord?.[startDateField] || + // !taskRecord?.[endDateField] + // ) { + // return { + // taskDays: 0, + // progress: 0, + // startDate: null, + // endDate: null, + // taskRecord + // }; + // } + // const startDate = createDateAtMidnight( + // Math.min(Math.max(this.parsedOptions._minDateTime, rawDateStartDateTime), this.parsedOptions._maxDateTime) + // ); + // const endDate = createDateAtMidnight( + // Math.max(Math.min(this.parsedOptions._maxDateTime, rawDateEndDateTime), this.parsedOptions._minDateTime) + // ); + // const progress = convertProgress(taskRecord[progressField]); + // const taskDays = Math.ceil(Math.abs(endDate.getTime() - startDate.getTime()) / (1000 * 60 * 60 * 24)) + 1; + // return { + // taskRecord, + // taskDays, + // startDate, + // endDate, + // progress + // }; + // } + // return { + // taskDays: 0, + // progress: 0, + // startDate: null, + // endDate: null, + // taskRecord: null + // }; + // } /** * 拖拽任务条或者调整任务条尺寸修改日期更新到数据中 * @param updateDateType @@ -764,9 +767,9 @@ export class Gantt extends EventTarget { updateDateType: 'move' | 'start-move' | 'end-move', days: number, index: number, - sub_task_id?: number + sub_task_index?: number ) { - const taskRecord = this.getRecordByIndex(index, sub_task_id); + const taskRecord = this.getRecordByIndex(index, sub_task_index); const startDateField = this.parsedOptions.startDateField; const endDateField = this.parsedOptions.endDateField; const dateFormat = this.parsedOptions.dateFormat ?? parseDateFormat(taskRecord[startDateField]); @@ -784,7 +787,10 @@ export class Gantt extends EventTarget { const newEndDate = formatDate(createDateAtMidnight(days * DayTimes + endDate.getTime()), dateFormat); taskRecord[endDateField] = newEndDate; } - this._updateRecordToListTable(taskRecord, index); + if (!isValid(sub_task_index)) { + //子任务不是独占左侧表格一行的情况 + this._updateRecordToListTable(taskRecord, index); + } } /** 目前不支持树形tree的情况更新单条数据 需要的话目前可以setRecords。 */ updateTaskRecord(record: any, index: number) { diff --git a/packages/vtable-gantt/src/event/event-manager.ts b/packages/vtable-gantt/src/event/event-manager.ts index 6165a4ccd..bf7f56cd8 100644 --- a/packages/vtable-gantt/src/event/event-manager.ts +++ b/packages/vtable-gantt/src/event/event-manager.ts @@ -140,11 +140,14 @@ function bindTableGroupListener(event: EventManager) { scene._gantt.stateManager.hoverTaskBar.target = taskBarTarget as any as GanttTaskBarNode; stateManager.showTaskBarHover(); if (scene._gantt.hasListeners(GANTT_EVENT_TYPE.MOUSEENTER_TASK_BAR)) { - const taskIndex = getTaskIndexByY(e.offset.y, scene._gantt); - const record = scene._gantt.getRecordByIndex(taskIndex); + // const taskIndex = getTaskIndexByY(e.offset.y, scene._gantt); + const taskIndex = taskBarTarget.task_index; + const sub_task_index = taskBarTarget.sub_task_index; + const record = scene._gantt.getRecordByIndex(taskIndex, sub_task_index); scene._gantt.fireListeners(GANTT_EVENT_TYPE.MOUSEENTER_TASK_BAR, { event: e.nativeEvent, index: taskIndex, + sub_task_index, record }); } @@ -153,11 +156,14 @@ function bindTableGroupListener(event: EventManager) { if (scene._gantt.stateManager.hoverTaskBar.target) { stateManager.hideTaskBarHover(e); if (scene._gantt.hasListeners(GANTT_EVENT_TYPE.MOUSELEAVE_TASK_BAR)) { - const taskIndex = getTaskIndexByY(e.offset.y, scene._gantt); - const record = scene._gantt.getRecordByIndex(taskIndex); + // const taskIndex = getTaskIndexByY(e.offset.y, scene._gantt); + const taskIndex = taskBarTarget.task_index; + const sub_task_index = taskBarTarget.sub_task_index; + const record = scene._gantt.getRecordByIndex(taskIndex, sub_task_index); scene._gantt.fireListeners(GANTT_EVENT_TYPE.MOUSELEAVE_TASK_BAR, { event: e.nativeEvent, index: taskIndex, + sub_task_index, record }); } @@ -301,11 +307,14 @@ function bindTableGroupListener(event: EventManager) { scene._gantt.stateManager.selectedTaskBar.target = taskBarTarget as any as GanttTaskBarNode; stateManager.showTaskBarSelectedBorder(); if (gantt.hasListeners(GANTT_EVENT_TYPE.CLICK_TASK_BAR)) { - const taskIndex = getTaskIndexByY(e.offset.y, gantt); - const record = gantt.getRecordByIndex(taskIndex); + // const taskIndex = getTaskIndexByY(e.offset.y, gantt); + const taskIndex = taskBarTarget.task_index; + const sub_task_index = taskBarTarget.sub_task_index; + const record = gantt.getRecordByIndex(taskIndex, sub_task_index); gantt.fireListeners(GANTT_EVENT_TYPE.CLICK_TASK_BAR, { event: e.nativeEvent, index: taskIndex, + sub_task_index, record }); } diff --git a/packages/vtable-gantt/src/scenegraph/dependency-link.ts b/packages/vtable-gantt/src/scenegraph/dependency-link.ts index d3751f52b..c27d4b5b8 100644 --- a/packages/vtable-gantt/src/scenegraph/dependency-link.ts +++ b/packages/vtable-gantt/src/scenegraph/dependency-link.ts @@ -82,12 +82,12 @@ export class DependencyLink { startDate: linkedToTaskStartDate, endDate: linkedToTaskEndDate, taskDays: linkedToTaskTaskDays - } = this._scene._gantt.getTaskInfoByTaskListIndexs(linkedToTaskRecord.index[0], linkedToTaskRecord.index[1])); + } = this._scene._gantt.getTaskInfoByTaskListIndex(linkedToTaskRecord.index[0], linkedToTaskRecord.index[1])); ({ startDate: linkedFromTaskStartDate, endDate: linkedFromTaskEndDate, taskDays: linkedFromTaskTaskDays - } = this._scene._gantt.getTaskInfoByTaskListIndexs(linkedFromTaskRecord.index[0], linkedFromTaskRecord.index[1])); + } = this._scene._gantt.getTaskInfoByTaskListIndex(linkedFromTaskRecord.index[0], linkedFromTaskRecord.index[1])); } else { linkedFromTaskShowIndex = this._scene._gantt.getTaskShowIndexByRecordIndex(linkedFromTaskRecord.index); linkedToTaskShowIndex = this._scene._gantt.getTaskShowIndexByRecordIndex(linkedToTaskRecord.index); @@ -377,11 +377,32 @@ export function generateLinkLinePoints( y: rowHeight * (linkedFromTaskRecordRowIndex + 0.5) }, { - x: Math.min(linkFromPointX, linkToPointX) - distanceToTaskBar, + x: + (linkedFromTaskRecordRowIndex === linkedToTaskRecordRowIndex + ? linkFromPointX + : Math.min(linkFromPointX, linkToPointX)) - distanceToTaskBar, y: rowHeight * (linkedFromTaskRecordRowIndex + 0.5) }, { - x: Math.min(linkFromPointX, linkToPointX) - distanceToTaskBar, + x: + (linkedFromTaskRecordRowIndex === linkedToTaskRecordRowIndex + ? linkFromPointX + : Math.min(linkFromPointX, linkToPointX)) - distanceToTaskBar, + y: + rowHeight * + (linkedToTaskRecordRowIndex + (linkedFromTaskRecordRowIndex === linkedToTaskRecordRowIndex ? 1 : 0.5)) + }, + { + x: linkToPointX - distanceToTaskBar, + y: + rowHeight * + (linkedFromTaskRecordRowIndex + (linkedFromTaskRecordRowIndex === linkedToTaskRecordRowIndex ? 1 : 0.5)) + }, + { + x: + (linkedFromTaskRecordRowIndex === linkedToTaskRecordRowIndex + ? linkToPointX + : Math.min(linkFromPointX, linkToPointX)) - distanceToTaskBar, y: rowHeight * (linkedToTaskRecordRowIndex + 0.5) }, { @@ -421,11 +442,33 @@ export function generateLinkLinePoints( y: rowHeight * (linkedFromTaskRecordRowIndex + 0.5) }, { - x: Math.max(linkFromPointX, linkToPointX) + distanceToTaskBar, + x: + (linkedFromTaskRecordRowIndex === linkedToTaskRecordRowIndex + ? linkFromPointX + : Math.max(linkFromPointX, linkToPointX)) + distanceToTaskBar, y: rowHeight * (linkedFromTaskRecordRowIndex + 0.5) }, + // { + // x: Math.max(linkFromPointX, linkToPointX) + distanceToTaskBar, + // y: rowHeight * (linkedToTaskRecordRowIndex + 0.5) + // }, { - x: Math.max(linkFromPointX, linkToPointX) + distanceToTaskBar, + x: + (linkedFromTaskRecordRowIndex === linkedToTaskRecordRowIndex + ? linkFromPointX + : Math.max(linkFromPointX, linkToPointX)) + distanceToTaskBar, + y: + rowHeight * + (linkedToTaskRecordRowIndex + (linkedFromTaskRecordRowIndex === linkedToTaskRecordRowIndex ? 1 : 0.5)) + }, + { + x: linkToPointX + distanceToTaskBar, + y: + rowHeight * + (linkedFromTaskRecordRowIndex + (linkedFromTaskRecordRowIndex === linkedToTaskRecordRowIndex ? 1 : 0.5)) + }, + { + x: linkToPointX + distanceToTaskBar, y: rowHeight * (linkedToTaskRecordRowIndex + 0.5) }, { diff --git a/packages/vtable-gantt/src/scenegraph/gantt-node.ts b/packages/vtable-gantt/src/scenegraph/gantt-node.ts index 45575b4e9..e45472a21 100644 --- a/packages/vtable-gantt/src/scenegraph/gantt-node.ts +++ b/packages/vtable-gantt/src/scenegraph/gantt-node.ts @@ -5,6 +5,10 @@ export class GanttTaskBarNode extends Group { barRect?: IRect; progressRect?: IRect; textLabel?: IText; + name: string; + task_index: number; + sub_task_index?: number; + record?: any; constructor(attrs: IGroupGraphicAttribute) { super(attrs); } diff --git a/packages/vtable-gantt/src/scenegraph/scenegraph.ts b/packages/vtable-gantt/src/scenegraph/scenegraph.ts index ce1ac74b9..8422b1e0f 100644 --- a/packages/vtable-gantt/src/scenegraph/scenegraph.ts +++ b/packages/vtable-gantt/src/scenegraph/scenegraph.ts @@ -246,9 +246,6 @@ export class Scenegraph { release() { this.stage.release(); } - getTaskBarNodeByY(y: number) { - const taskIndex = getTaskIndexByY(y, this._gantt); - } showTaskCreationButton(x: number, y: number, taskIndex: number, record: any) { if (!this.taskCreationButton) { diff --git a/packages/vtable-gantt/src/scenegraph/task-bar.ts b/packages/vtable-gantt/src/scenegraph/task-bar.ts index a2c1dab63..b70fe765e 100644 --- a/packages/vtable-gantt/src/scenegraph/task-bar.ts +++ b/packages/vtable-gantt/src/scenegraph/task-bar.ts @@ -82,7 +82,7 @@ export class TaskBar { let progress; let taskRecord; if (isValid(childIndex)) { - ({ startDate, endDate, taskDays, progress, taskRecord } = this._scene._gantt.getTaskInfoByTaskListIndexs( + ({ startDate, endDate, taskDays, progress, taskRecord } = this._scene._gantt.getTaskInfoByTaskListIndex( index, childIndex )); @@ -110,8 +110,8 @@ export class TaskBar { clip: true }); barGroup.name = 'task-bar'; - barGroup.id = index; - barGroup.sub_task_id = childIndex; + barGroup.task_index = index; + barGroup.sub_task_index = childIndex; barGroup.record = taskRecord; // this.barContainer.appendChild(barGroup); let rootContainer; @@ -425,13 +425,16 @@ export class TaskBar { this.selectedBorders[0].appendChild(line); } - getTaskBarNodeByIndex(index: number, sub_task_id?: number) { + getTaskBarNodeByIndex(index: number, sub_task_index?: number) { let c = this.barContainer.firstChild as Group; if (!c) { return null; } for (let i = 0; i < this.barContainer.childrenCount; i++) { - if (c.id === index && (!isValid(sub_task_id) || (isValid(sub_task_id) && c.sub_task_id === sub_task_id))) { + if ( + c.task_index === index && + (!isValid(sub_task_index) || (isValid(sub_task_index) && c.sub_task_index === sub_task_index)) + ) { return c; } c = c._next as Group; diff --git a/packages/vtable-gantt/src/state/state-manager.ts b/packages/vtable-gantt/src/state/state-manager.ts index 3b1516f3c..308864763 100644 --- a/packages/vtable-gantt/src/state/state-manager.ts +++ b/packages/vtable-gantt/src/state/state-manager.ts @@ -323,7 +323,7 @@ export class StateManager { if (this.moveTaskBar.moveTaskBarXInertia.isInertiaScrolling()) { this.moveTaskBar.moveTaskBarXInertia.endInertia(); } - const taskIndex = getTaskIndexByY(this.moveTaskBar.startOffsetY, this._gantt); + // const taskIndex = getTaskIndexByY(this.moveTaskBar.startOffsetY, this._gantt); // const deltaX = x - this.moveTaskBar.startX; const deltaX = this.moveTaskBar.deltaX; const days = Math.round(deltaX / this._gantt.parsedOptions.colWidthPerDay); @@ -332,7 +332,7 @@ export class StateManager { const targetEndX = this.moveTaskBar.targetStartX + correctX; const target = this._gantt.stateManager.moveTaskBar.target; // target.setAttribute('x', targetEndX); - resizeOrMoveTaskBar(taskIndex, target, targetEndX - target.attribute.x, null, this); + resizeOrMoveTaskBar(target, targetEndX - target.attribute.x, null, this); // if (target.attribute.x < this._gantt.stateManager.scrollLeft - 2) { // this._gantt.stateManager.setScrollLeft(target.attribute.x); // } @@ -341,14 +341,14 @@ export class StateManager { // } if (Math.abs(days) >= 1) { // const taskIndex = getTaskIndexByY(this.moveTaskBar.startOffsetY, this._gantt); - const taskIndex = this.moveTaskBar.target.id; - const sub_task_id = this.moveTaskBar.target.sub_task_id; - const oldRecord = this._gantt.getRecordByIndex(taskIndex, sub_task_id); + const taskIndex = this.moveTaskBar.target.task_index; + const sub_task_index = this.moveTaskBar.target.sub_task_index; + const oldRecord = this._gantt.getRecordByIndex(taskIndex, sub_task_index); const oldStartDate = oldRecord[this._gantt.parsedOptions.startDateField]; const oldEndDate = oldRecord[this._gantt.parsedOptions.endDateField]; - this._gantt._updateDateToTaskRecord('move', days, taskIndex, sub_task_id); + this._gantt._updateDateToTaskRecord('move', days, taskIndex, sub_task_index); if (this._gantt.hasListeners(GANTT_EVENT_TYPE.CHANGE_DATE_RANGE)) { - const newRecord = this._gantt.getRecordByIndex(taskIndex); + const newRecord = this._gantt.getRecordByIndex(taskIndex, sub_task_index); this._gantt.fireListeners(GANTT_EVENT_TYPE.CHANGE_DATE_RANGE, { startDate: newRecord[this._gantt.parsedOptions.startDateField], endDate: newRecord[this._gantt.parsedOptions.endDateField], @@ -367,13 +367,13 @@ export class StateManager { } dealTaskBarMove(e: FederatedPointerEvent) { const target = this.moveTaskBar.target; - const taskIndex = getTaskIndexByY(this.moveTaskBar.startOffsetY, this._gantt); + // const taskIndex = getTaskIndexByY(this.moveTaskBar.startOffsetY, this._gantt); const x1 = this._gantt.eventManager.lastDragPointerXYOnWindow.x; const x2 = e.x; const dx = x2 - x1; this.moveTaskBar.deltaX += dx; // target.setAttribute('x', target.attribute.x + dx); - resizeOrMoveTaskBar(taskIndex, target, dx, null, this); + resizeOrMoveTaskBar(target, dx, null, this); // 处理向左拖拽任务条时,整体向左滚动 if (target.attribute.x <= this._gantt.stateManager.scrollLeft && dx < 0) { @@ -382,7 +382,7 @@ export class StateManager { this.moveTaskBar.moveTaskBarXInertia.startInertia(this.moveTaskBar.moveTaskBarXSpeed, 0, 1); this.moveTaskBar.moveTaskBarXInertia.setScrollHandle((dx: number, dy: number) => { this.moveTaskBar.deltaX += dx; - resizeOrMoveTaskBar(taskIndex, target, dx, null, this); + resizeOrMoveTaskBar(target, dx, null, this); this._gantt.stateManager.setScrollLeft(target.attribute.x); if (this._gantt.stateManager.scrollLeft === 0) { @@ -400,7 +400,7 @@ export class StateManager { this.moveTaskBar.moveTaskBarXInertia.startInertia(this.moveTaskBar.moveTaskBarXSpeed, 0, 1); this.moveTaskBar.moveTaskBarXInertia.setScrollHandle((dx: number, dy: number) => { this.moveTaskBar.deltaX += dx; - resizeOrMoveTaskBar(taskIndex, target, dx, null, this); + resizeOrMoveTaskBar(target, dx, null, this); this._gantt.stateManager.setScrollLeft( target.attribute.x + target.attribute.width - this._gantt.tableNoFrameWidth @@ -442,14 +442,16 @@ export class StateManager { let diff_days = Math.round(deltaX / this._gantt.parsedOptions.colWidthPerDay); diff_days = direction === 'left' ? -diff_days : diff_days; - const taskBarGroup = this._gantt.stateManager.resizeTaskBar.target; - const rect = this._gantt.stateManager.resizeTaskBar.target.barRect; - const progressRect = this._gantt.stateManager.resizeTaskBar.target.progressRect; - const taskIndex = getTaskIndexByY(this.resizeTaskBar.startOffsetY, this._gantt); - const oldRecord = this._gantt.getRecordByIndex(taskIndex); + const taskBarGroup = this.resizeTaskBar.target; + const rect = this.resizeTaskBar.target.barRect; + const progressRect = this.resizeTaskBar.target.progressRect; + // const taskIndex = getTaskIndexByY(this.resizeTaskBar.startOffsetY, this._gantt); + const taskIndex = this.resizeTaskBar.target.task_index; + const sub_task_index = this.resizeTaskBar.target.sub_task_index; + const oldRecord = this._gantt.getRecordByIndex(taskIndex, sub_task_index); const oldStartDate = oldRecord[this._gantt.parsedOptions.startDateField]; const oldEndDate = oldRecord[this._gantt.parsedOptions.endDateField]; - const { taskDays, progress } = this._gantt.getTaskInfoByTaskListIndex(taskIndex); + const { taskDays, progress } = this._gantt.getTaskInfoByTaskListIndex(taskIndex, sub_task_index); if (diff_days < 0 && taskDays + diff_days <= 0) { diff_days = 1 - taskDays; } @@ -460,16 +462,16 @@ export class StateManager { if (direction === 'left') { // taskBarGroup.setAttribute('x', targetEndX); // taskBarGroup.setAttribute('width', taskBarSize); - resizeOrMoveTaskBar(taskIndex, taskBarGroup, targetEndX - taskBarGroup.attribute.x, taskBarSize, this); + resizeOrMoveTaskBar(taskBarGroup, targetEndX - taskBarGroup.attribute.x, taskBarSize, this); rect?.setAttribute('width', taskBarGroup.attribute.width); progressRect?.setAttribute('width', (progress / 100) * taskBarGroup.attribute.width); - this._gantt._updateDateToTaskRecord('start-move', -diff_days, taskIndex); + this._gantt._updateDateToTaskRecord('start-move', -diff_days, taskIndex, sub_task_index); } else if (direction === 'right') { // taskBarGroup.setAttribute('width', taskBarSize); - resizeOrMoveTaskBar(taskIndex, taskBarGroup, 0, taskBarSize, this); + resizeOrMoveTaskBar(taskBarGroup, 0, taskBarSize, this); rect?.setAttribute('width', taskBarGroup.attribute.width); progressRect?.setAttribute('width', (progress / 100) * taskBarGroup.attribute.width); - this._gantt._updateDateToTaskRecord('end-move', diff_days, taskIndex); + this._gantt._updateDateToTaskRecord('end-move', diff_days, taskIndex, sub_task_index); } this.showTaskBarHover(); reCreateCustomNode(this._gantt, taskBarGroup, taskIndex); @@ -477,7 +479,7 @@ export class StateManager { this.resizeTaskBar.target = null; if (Math.abs(diff_days) >= 1 && this._gantt.hasListeners(GANTT_EVENT_TYPE.CHANGE_DATE_RANGE)) { - const newRecord = this._gantt.getRecordByIndex(taskIndex); + const newRecord = this._gantt.getRecordByIndex(taskIndex, sub_task_index); this._gantt.fireListeners(GANTT_EVENT_TYPE.CHANGE_DATE_RANGE, { startDate: newRecord[this._gantt.parsedOptions.startDateField], endDate: newRecord[this._gantt.parsedOptions.endDateField], @@ -501,8 +503,10 @@ export class StateManager { const textLabel = taskBarGroup.textLabel; const progressField = this._gantt.parsedOptions.progressField; - const taskIndex = getTaskIndexByY(this.resizeTaskBar.startOffsetY, this._gantt); - const taskRecord = this._gantt.getRecordByIndex(taskIndex); + // const taskIndex = getTaskIndexByY(this.resizeTaskBar.startOffsetY, this._gantt); + const taskIndex = taskBarGroup.task_index; + const sub_task_index = taskBarGroup.sub_task_index; + const taskRecord = this._gantt.getRecordByIndex(taskIndex, sub_task_index); const progress = taskRecord[progressField]; let diffWidth = this._gantt.stateManager.resizeTaskBar.onIconName === 'left' ? -dx : dx; @@ -517,7 +521,6 @@ export class StateManager { // taskBarGroup.setAttribute('x', taskBarGroup.attribute.x - diffWidth); // } resizeOrMoveTaskBar( - taskIndex, taskBarGroup, this._gantt.stateManager.resizeTaskBar.onIconName === 'left' ? -diffWidth : 0, taskBarSize, @@ -553,11 +556,15 @@ export class StateManager { } endCreateDependencyLine(offsetY: number) { const taskKeyField = this._gantt.parsedOptions.taskKeyField; - const fromTaskIndex = getTaskIndexByY(this.creatingDenpendencyLink.startOffsetY, this._gantt); - const toTaskIndex = getTaskIndexByY(offsetY, this._gantt); - const fromRecord = this._gantt.getRecordByIndex(fromTaskIndex); + // const fromTaskIndex = getTaskIndexByY(this.creatingDenpendencyLink.startOffsetY, this._gantt); + const fromTaskIndex = this.selectedTaskBar.target.task_index; + const from_sub_task_id = this.selectedTaskBar.target.sub_task_index; + // const toTaskIndex = getTaskIndexByY(offsetY, this._gantt); + const toTaskIndex = this.creatingDenpendencyLink.secondTaskBarNode.task_index; + const to_sub_task_id = this.creatingDenpendencyLink.secondTaskBarNode.sub_task_index; + const fromRecord = this._gantt.getRecordByIndex(fromTaskIndex, from_sub_task_id); const linkedFromTaskKey = fromRecord[taskKeyField]; - const toRecord = this._gantt.getRecordByIndex(toTaskIndex); + const toRecord = this._gantt.getRecordByIndex(toTaskIndex, to_sub_task_id); const linkedToTaskKey = toRecord[taskKeyField]; const link = { linkedFromTaskKey, @@ -798,15 +805,11 @@ function reCreateCustomNode(gantt: Gantt, taskBarGroup: Group, taskIndex: number } } -function resizeOrMoveTaskBar( - taskIndex: number, - target: GanttTaskBarNode, - dx: number, - newWidth: number, - state: StateManager -) { +function resizeOrMoveTaskBar(target: GanttTaskBarNode, dx: number, newWidth: number, state: StateManager) { // const taskIndex = getTaskIndexByY(state.moveTaskBar.startOffsetY, state._gantt); - const record = state._gantt.getRecordByIndex(taskIndex); + const taskIndex = target.task_index; + const sub_task_index = target.sub_task_index; + const record = state._gantt.getRecordByIndex(taskIndex, sub_task_index); if (dx) { target.setAttribute('x', target.attribute.x + dx); } @@ -823,17 +826,56 @@ function resizeOrMoveTaskBar( const { linkedToTaskKey, linkedFromTaskKey, type } = link; const { taskKeyField, minDate } = state._gantt.parsedOptions; const linkedFromTaskRecord = findRecordByTaskKey(state._gantt.records, taskKeyField, linkedFromTaskKey); - - const { startDate: linkedToTaskStartDate, endDate: linkedToTaskEndDate } = - state._gantt.getTaskInfoByTaskListIndex(taskIndex); - const taskShowIndex = state._gantt.getTaskShowIndexByRecordIndex(linkedFromTaskRecord.index); - const { startDate: linkedFromTaskStartDate, endDate: linkedFromTaskEndDate } = - state._gantt.getTaskInfoByTaskListIndex(taskShowIndex); + const linkedToTaskRecord = findRecordByTaskKey(state._gantt.records, taskKeyField, linkedToTaskKey); + // const { startDate: linkedToTaskStartDate, endDate: linkedToTaskEndDate } = + // state._gantt.getTaskInfoByTaskListIndex(taskIndex); + // const taskShowIndex = state._gantt.getTaskShowIndexByRecordIndex(linkedFromTaskRecord.index); + // const { startDate: linkedFromTaskStartDate, endDate: linkedFromTaskEndDate } = + // state._gantt.getTaskInfoByTaskListIndex(taskShowIndex); + + let linkedToTaskStartDate; + let linkedToTaskEndDate; + let linkedToTaskTaskDays; + let linkedFromTaskStartDate; + let linkedFromTaskEndDate; + let linkedFromTaskTaskDays; + + let linkedToTaskShowIndex; + let linkedFromTaskShowIndex; + + if (state._gantt.parsedOptions.showHierarchyMode === ShowHierarchyMode.Sub_Tasks_Inline) { + linkedFromTaskShowIndex = linkedFromTaskRecord.index[0]; + linkedToTaskShowIndex = linkedToTaskRecord.index[0]; + ({ + startDate: linkedToTaskStartDate, + endDate: linkedToTaskEndDate, + taskDays: linkedToTaskTaskDays + } = state._gantt.getTaskInfoByTaskListIndex(linkedToTaskRecord.index[0], linkedToTaskRecord.index[1])); + ({ + startDate: linkedFromTaskStartDate, + endDate: linkedFromTaskEndDate, + taskDays: linkedFromTaskTaskDays + } = state._gantt.getTaskInfoByTaskListIndex(linkedFromTaskRecord.index[0], linkedFromTaskRecord.index[1])); + } else { + linkedFromTaskShowIndex = state._gantt.getTaskShowIndexByRecordIndex(linkedFromTaskRecord.index); + linkedToTaskShowIndex = state._gantt.getTaskShowIndexByRecordIndex(linkedToTaskRecord.index); + + ({ + startDate: linkedToTaskStartDate, + endDate: linkedToTaskEndDate, + taskDays: linkedToTaskTaskDays + } = state._gantt.getTaskInfoByTaskListIndex(linkedToTaskShowIndex)); + ({ + startDate: linkedFromTaskStartDate, + endDate: linkedFromTaskEndDate, + taskDays: linkedFromTaskTaskDays + } = state._gantt.getTaskInfoByTaskListIndex(linkedFromTaskShowIndex)); + } const { linePoints, arrowPoints } = updateLinkLinePoints( type, linkedFromTaskStartDate, linkedFromTaskEndDate, - taskShowIndex, + linkedFromTaskShowIndex, linkedToTaskStartDate, linkedToTaskEndDate, taskIndex, @@ -855,12 +897,51 @@ function resizeOrMoveTaskBar( const { linkedToTaskKey, linkedFromTaskKey, type } = link; const { taskKeyField, minDate } = state._gantt.parsedOptions; const linkedToTaskRecord = findRecordByTaskKey(state._gantt.records, taskKeyField, linkedToTaskKey); - - const { startDate: linkedFromTaskStartDate, endDate: linkedFromTaskEndDate } = - state._gantt.getTaskInfoByTaskListIndex(taskIndex); - const taskShowIndex = state._gantt.getTaskShowIndexByRecordIndex(linkedToTaskRecord.index); - const { startDate: linkedToTaskStartDate, endDate: linkedToTaskEndDate } = - state._gantt.getTaskInfoByTaskListIndex(taskShowIndex); + const linkedFromTaskRecord = findRecordByTaskKey(state._gantt.records, taskKeyField, linkedFromTaskKey); + // const { startDate: linkedFromTaskStartDate, endDate: linkedFromTaskEndDate } = + // state._gantt.getTaskInfoByTaskListIndex(taskIndex); + // const taskShowIndex = state._gantt.getTaskShowIndexByRecordIndex(linkedToTaskRecord.index); + // const { startDate: linkedToTaskStartDate, endDate: linkedToTaskEndDate } = + // state._gantt.getTaskInfoByTaskListIndex(taskShowIndex); + + let linkedToTaskStartDate; + let linkedToTaskEndDate; + let linkedToTaskTaskDays; + let linkedFromTaskStartDate; + let linkedFromTaskEndDate; + let linkedFromTaskTaskDays; + + let linkedToTaskShowIndex; + let linkedFromTaskShowIndex; + + if (state._gantt.parsedOptions.showHierarchyMode === ShowHierarchyMode.Sub_Tasks_Inline) { + linkedFromTaskShowIndex = linkedFromTaskRecord.index[0]; + linkedToTaskShowIndex = linkedToTaskRecord.index[0]; + ({ + startDate: linkedToTaskStartDate, + endDate: linkedToTaskEndDate, + taskDays: linkedToTaskTaskDays + } = state._gantt.getTaskInfoByTaskListIndex(linkedToTaskRecord.index[0], linkedToTaskRecord.index[1])); + ({ + startDate: linkedFromTaskStartDate, + endDate: linkedFromTaskEndDate, + taskDays: linkedFromTaskTaskDays + } = state._gantt.getTaskInfoByTaskListIndex(linkedFromTaskRecord.index[0], linkedFromTaskRecord.index[1])); + } else { + linkedFromTaskShowIndex = state._gantt.getTaskShowIndexByRecordIndex(linkedFromTaskRecord.index); + linkedToTaskShowIndex = state._gantt.getTaskShowIndexByRecordIndex(linkedToTaskRecord.index); + + ({ + startDate: linkedToTaskStartDate, + endDate: linkedToTaskEndDate, + taskDays: linkedToTaskTaskDays + } = state._gantt.getTaskInfoByTaskListIndex(linkedToTaskShowIndex)); + ({ + startDate: linkedFromTaskStartDate, + endDate: linkedFromTaskEndDate, + taskDays: linkedFromTaskTaskDays + } = state._gantt.getTaskInfoByTaskListIndex(linkedFromTaskShowIndex)); + } const { linePoints, arrowPoints } = updateLinkLinePoints( type, linkedFromTaskStartDate, @@ -868,7 +949,7 @@ function resizeOrMoveTaskBar( taskIndex, linkedToTaskStartDate, linkedToTaskEndDate, - taskShowIndex, + linkedToTaskShowIndex, minDate, state._gantt.parsedOptions.rowHeight, state._gantt.parsedOptions.colWidthPerDay, diff --git a/packages/vtable-gantt/src/ts-types/events.ts b/packages/vtable-gantt/src/ts-types/events.ts index 4e1c3f4bd..9a03e7658 100644 --- a/packages/vtable-gantt/src/ts-types/events.ts +++ b/packages/vtable-gantt/src/ts-types/events.ts @@ -16,24 +16,28 @@ export interface TableEventHandlersEventArgumentMap { mouseenter_task_bar: { /** 第几条数据 */ index: number; + sub_task_index?: number; record: any; event: Event; }; mouseleave_task_bar: { /** 第几条数据 */ index: number; + sub_task_index?: number; record: any; event: Event; }; click_task_bar: { /** 第几条数据 */ index: number; + sub_task_index?: number; record: any; event: Event; }; change_date_range: { /** 第几条数据 */ index: number; + sub_task_index?: number; /** 改变后的起始日期 */ startDate: Date; /** 改变后的结束日期 */ @@ -49,6 +53,7 @@ export interface TableEventHandlersEventArgumentMap { event: Event; /** 第几条数据 */ index: number; + sub_task_index?: number; /** 改变后的起始日期 */ startDate: Date; /** 改变后的结束日期 */ From d89c667406beabb4c79126b8e050fc9ad09307ac Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Tue, 19 Nov 2024 16:56:12 +0800 Subject: [PATCH 03/40] feat: add option customComputeRowHeight and defaultRowHeight can set "auto" --- .../basic_function/row_height_column_width.md | 4 + .../basic_function/row_height_column_width.md | 4 + .../option/en/common/option-secondary.md | 26 +++++-- docs/assets/option/en/table/listTable.md | 8 +- .../option/zh/common/option-secondary.md | 24 +++++- docs/assets/option/zh/table/listTable.md | 8 ++ packages/vtable/src/core/BaseTable.ts | 9 ++- packages/vtable/src/index.ts | 2 - .../scenegraph/layout/compute-row-height.ts | 17 +++- packages/vtable/src/ts-types/base-table.ts | 6 +- packages/vtable/src/ts-types/table-engine.ts | 77 +------------------ 11 files changed, 90 insertions(+), 95 deletions(-) diff --git a/docs/assets/guide/en/basic_function/row_height_column_width.md b/docs/assets/guide/en/basic_function/row_height_column_width.md index fd8e889fc..5a53fbab2 100644 --- a/docs/assets/guide/en/basic_function/row_height_column_width.md +++ b/docs/assets/guide/en/basic_function/row_height_column_width.md @@ -61,6 +61,10 @@ const table = new VTable.ListTable({ }); ``` +## Custom calculated row height + +If you need to customize the logic for calculating row height, you can configure the `customComputeRowHeight` function to proxy the logic for calculating row height inside VTable. + # Column width related configuration ## Column width width diff --git a/docs/assets/guide/zh/basic_function/row_height_column_width.md b/docs/assets/guide/zh/basic_function/row_height_column_width.md index bd340cc53..4cb91109c 100644 --- a/docs/assets/guide/zh/basic_function/row_height_column_width.md +++ b/docs/assets/guide/zh/basic_function/row_height_column_width.md @@ -61,6 +61,10 @@ const table = new VTable.ListTable({ }); ``` +## 自定义计算行高 + +如果需要自定义计算行高的逻辑,可以配置`customComputeRowHeight`函数来代理 VTable 内部计算行高的逻辑。 + # 列宽相关配置 ## 列宽 width diff --git a/docs/assets/option/en/common/option-secondary.md b/docs/assets/option/en/common/option-secondary.md index 7860d75ce..332aa749b 100644 --- a/docs/assets/option/en/common/option-secondary.md +++ b/docs/assets/option/en/common/option-secondary.md @@ -88,21 +88,37 @@ Allow the number of frozen columns, indicating how many columns will show the fr Whether to show the fixed column pin icon, effective for basic tables -#${prefix} defaultRowHeight(number) = 40 +#${prefix} defaultRowHeight(number|'auto') = 40 -Default row height +Default row height. + +- 'auto': The default row height calculated based on the row height. Combined with defaultHeaderRowHeight, it can achieve the effect of automatic row height calculation for the header or body part. +- Specific value: Set a specific row height. #${prefix} defaultHeaderRowHeight(Array|number) -Default row height for list header, can be set row by row. If not set, defaultRowHeight is used. +The default row height of the column header can be set row by row. If not set, the defaultRowHeight value will be used as the row height of the table header. + +Specific definition: + +``` +defaultHeaderRowHeight?: (number | 'auto') | (number | 'auto')[]; +``` #${prefix} defaultColWidth(number) = 80 -Default column width value +Column width default value #${prefix} defaultHeaderColWidth(Array|number) -Default column width for row headers, can be set column by column. If not set, defaultColWidth is used. +The default column width of the row header can be set column by column. If not set, the value of defaultColWidth will be used as the column width and height of the header. + +Specific definition: + +``` +/** The default column width of the row header can be set column by column. If not, defaultColWidth is used */ +defaultHeaderColWidth?: (number | 'auto') | (number | 'auto')[]; +``` #${prefix} keyboardOptions(Object) diff --git a/docs/assets/option/en/table/listTable.md b/docs/assets/option/en/table/listTable.md index 3e50395a3..32f4b90b4 100644 --- a/docs/assets/option/en/table/listTable.md +++ b/docs/assets/option/en/table/listTable.md @@ -145,4 +145,10 @@ Enable the group title sticking function. Customize the group title layout. - groupTitleStyle(CustomLayoutStyle) \ No newline at end of file +## customComputeRowHeight(Function) + +Code VTable internally calculates the row height. Users can customize the method for calculating row height. + +``` +customComputeRowHeight?: (computeArgs: { row: number; table: ListTableAPI }) => number; +``` diff --git a/docs/assets/option/zh/common/option-secondary.md b/docs/assets/option/zh/common/option-secondary.md index 66b220b66..befb707b5 100644 --- a/docs/assets/option/zh/common/option-secondary.md +++ b/docs/assets/option/zh/common/option-secondary.md @@ -88,13 +88,22 @@ adaptive 模式下高度的适应策略,默认为 'only-body'。 是否显示固定列图钉(基本表格生效) -#${prefix} defaultRowHeight(number) = 40 +#${prefix} defaultRowHeight(number|'auto') = 40 -默认行高 +默认行高。 + +- 'auto':根据行高计算出的默认行高。结合 defaultHeaderRowHeight 使用可以实现表头或者 body 部分的行自动行高计算的效果。 +- 具体数值:设置具体的行高。 #${prefix} defaultHeaderRowHeight(Array|number) -列表头默认行高 可以按逐行设置 如果没有就取 defaultRowHeight +列表头默认行高 可以按逐行设置, 如果没有设置的话会取 defaultRowHeight 的值作为表头的行高。 + +具体定义: + +``` + defaultHeaderRowHeight?: (number | 'auto') | (number | 'auto')[]; +``` #${prefix} defaultColWidth(number) = 80 @@ -102,7 +111,14 @@ adaptive 模式下高度的适应策略,默认为 'only-body'。 #${prefix} defaultHeaderColWidth(Array|number) -行表头默认列宽 可以按逐列设置 如果没有就取 defaultColWidth +行表头默认列宽 可以按逐列设置, 如果没有设置的话会取 defaultColWidth 的值作为表头的列宽高。 + +具体定义: + +``` + /** 行表头默认列宽 可以按逐列设置 如果没有就取defaultColWidth */ + defaultHeaderColWidth?: (number | 'auto') | (number | 'auto')[]; +``` #${prefix} keyboardOptions(Object) diff --git a/docs/assets/option/zh/table/listTable.md b/docs/assets/option/zh/table/listTable.md index 79e027a42..026a24862 100644 --- a/docs/assets/option/zh/table/listTable.md +++ b/docs/assets/option/zh/table/listTable.md @@ -141,3 +141,11 @@ type CustomAggregation = { ## groupTitleCustomLayout(CustomLayout) 分组标题自定义布局。 + +## customComputeRowHeight(Function) + +代码 VTable 内部计算行高的方法,用户可以自定义计算行高的方法。 + +``` +customComputeRowHeight?: (computeArgs: { row: number; table: ListTableAPI }) => number; +``` diff --git a/packages/vtable/src/core/BaseTable.ts b/packages/vtable/src/core/BaseTable.ts index aca3e2799..65efe29a5 100644 --- a/packages/vtable/src/core/BaseTable.ts +++ b/packages/vtable/src/core/BaseTable.ts @@ -664,12 +664,15 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI { * */ get defaultRowHeight(): number { - return this.internalProps.defaultRowHeight; + if (isNumber(this.internalProps.defaultRowHeight)) { + return this.internalProps.defaultRowHeight as number; + } + return 40; } /** * Set the default row height. */ - set defaultRowHeight(defaultRowHeight: number) { + set defaultRowHeight(defaultRowHeight: number | 'auto') { this.internalProps.defaultRowHeight = defaultRowHeight; this.options.defaultRowHeight = defaultRowHeight; } @@ -1238,6 +1241,7 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI { endRow = Math.min(endRow, (this.rowCount ?? Infinity) - 1); let h = 0; + const isDefaultRowHeightIsAuto = this.options.defaultRowHeight === 'auto'; // autoRowHeight || all rows in header, use accumulation if ( this.heightMode === 'standard' && @@ -1246,6 +1250,7 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI { // endRow >= this.columnHeaderLevelCount && // !this.bottomFrozenRowCount && !this.hasAutoImageColumn() && + !isDefaultRowHeightIsAuto && this.internalProps._heightResizedRowMap.size === 0 ) { // part in header diff --git a/packages/vtable/src/index.ts b/packages/vtable/src/index.ts index 7c0b9bb25..857130942 100644 --- a/packages/vtable/src/index.ts +++ b/packages/vtable/src/index.ts @@ -22,7 +22,6 @@ import type { ListTableConstructorOptions, PivotTableConstructorOptions, PivotChartConstructorOptions, - GanttConstructorOptions, IHeaderTreeDefine, IDimension, IIndicator, @@ -73,7 +72,6 @@ export { PivotTableConstructorOptions, PivotChartConstructorOptions, PivotChart, - GanttConstructorOptions, IHeaderTreeDefine, IDimension, IIndicator, diff --git a/packages/vtable/src/scenegraph/layout/compute-row-height.ts b/packages/vtable/src/scenegraph/layout/compute-row-height.ts index 2f00cd649..71d22a889 100644 --- a/packages/vtable/src/scenegraph/layout/compute-row-height.ts +++ b/packages/vtable/src/scenegraph/layout/compute-row-height.ts @@ -2,7 +2,13 @@ import type { Group as VGroup } from '@src/vrender'; import { RichText, Text } from '@src/vrender'; import type { PivotHeaderLayoutMap } from '../../layout/pivot-header-layout'; import { validToString } from '../../tools/util'; -import type { ColumnIconOption, ColumnTypeOption, IRowSeriesNumber } from '../../ts-types'; +import type { + ColumnIconOption, + ColumnTypeOption, + IRowSeriesNumber, + ListTableAPI, + ListTableConstructorOptions +} from '../../ts-types'; import { IconPosition } from '../../ts-types'; import type { BaseTableAPI, HeaderData } from '../../ts-types/base-table'; import type { ColumnData, ColumnDefine, TextColumnDefine } from '../../ts-types/list-table/layout-map/api'; @@ -60,8 +66,9 @@ export function computeRowsHeight( const isAllRowsAuto = table.heightMode === 'autoHeight' || (table.heightMode === 'adaptive' && table.options.autoHeightInAdaptiveMode !== false); + const isDefaultRowHeightIsAuto = table.options.defaultRowHeight === 'auto'; - if (isAllRowsAuto || isDefaultHeaderHasAuto) { + if (isAllRowsAuto || isDefaultHeaderHasAuto || isDefaultRowHeightIsAuto) { rowStart = rowStart ?? 0; rowEnd = rowEnd ?? table.rowCount - 1; @@ -111,7 +118,7 @@ export function computeRowsHeight( } } - if (rowEnd < table.columnHeaderLevelCount || !isAllRowsAuto) { + if (rowEnd < table.columnHeaderLevelCount || (!isAllRowsAuto && !isDefaultRowHeightIsAuto)) { // do nothing } else { // compute body row @@ -121,6 +128,7 @@ export function computeRowsHeight( table.internalProps.transpose || (table.isPivotTable() && !(table.internalProps.layoutMap as PivotHeaderLayoutMap).indicatorsAsCol) ) && + !(table.options as ListTableConstructorOptions).customComputeRowHeight && checkFixedStyleAndNoWrap(table) ) { // check fixed style and no wrap situation, fill all row width single compute @@ -342,6 +350,9 @@ export function computeRowsHeight( export function computeRowHeight(row: number, startCol: number, endCol: number, table: BaseTableAPI): number { let maxHeight; + if ((table.options as ListTableConstructorOptions).customComputeRowHeight) { + return (table.options as ListTableConstructorOptions).customComputeRowHeight({ row, table: table as ListTableAPI }); + } // 如果是透视图 if ( table.isPivotChart() && diff --git a/packages/vtable/src/ts-types/base-table.ts b/packages/vtable/src/ts-types/base-table.ts index d58842ea3..339446a19 100644 --- a/packages/vtable/src/ts-types/base-table.ts +++ b/packages/vtable/src/ts-types/base-table.ts @@ -118,7 +118,7 @@ export interface IBaseTableProtected { frozenRowCount: number; rightFrozenColCount: number; bottomFrozenRowCount: number; - defaultRowHeight: number; + defaultRowHeight: number | 'auto'; /**表头默认行高 可以按逐行设置 如果没有就取defaultRowHeight */ defaultHeaderRowHeight: (number | 'auto') | (number | 'auto')[]; defaultColWidth: number; @@ -302,7 +302,7 @@ export interface BaseTableConstructorOptions { /** * 默认行高. 默认 40 */ - defaultRowHeight?: number; + defaultRowHeight?: number | 'auto'; /** 列表头默认行高 可以按逐行设置 如果没有就取defaultRowHeight */ defaultHeaderRowHeight?: (number | 'auto') | (number | 'auto')[]; /** @@ -541,7 +541,7 @@ export interface BaseTableAPI { /** 当前表格默认表头行高 */ defaultHeaderRowHeight: (number | 'auto') | (number | 'auto')[]; /** 当前表格默认行高 */ - defaultRowHeight: number; + defaultRowHeight: number | 'auto'; /** 当前表格默认列宽 */ defaultColWidth: number; /** 当前表格默认表头列宽 */ diff --git a/packages/vtable/src/ts-types/table-engine.ts b/packages/vtable/src/ts-types/table-engine.ts index a969f8895..c4fdcb838 100644 --- a/packages/vtable/src/ts-types/table-engine.ts +++ b/packages/vtable/src/ts-types/table-engine.ts @@ -263,6 +263,8 @@ export interface ListTableConstructorOptions extends BaseTableConstructorOptions groupTitleCustomLayout?: ICustomLayout; enableTreeStickCell?: boolean; + + customComputeRowHeight?: (computeArgs: { row: number; table: ListTableAPI }) => number; } export type GroupByOption = string | string[] | GroupConfig | GroupConfig[]; @@ -593,78 +595,3 @@ export type CustomMerge = { export type ColumnInfo = { col: number; left: number; right: number; width: number }; export type RowInfo = { row: number; top: number; bottom: number; height: number }; - -//#region gantt -export interface GanttConstructorOptions { - container?: HTMLElement; - /** - * 数据集合 - */ - records?: any[]; - /** 时间刻度 */ - timelineScales: { - unit: 'day' | 'week' | 'month' | 'quarter' | 'year'; - step: number; - format: (date: Date) => string; - // 时间刻度对应的字段名 - headerStyle?: ITextStyleOption | ((styleArg: StylePropertyFunctionArg) => ITextStyleOption); - }[]; - /** 定义列 */ - infoTableColumns?: ColumnsDefine; // (string | IDimension)[]; - infoTableWidth?: 'auto' | number; - gridStyle?: { - vertical: { - lineColor?: string; - lineWidth?: number; - }; - horizontal: { - lineColor?: string; - lineWidth?: number; - }; - }; - timelineStyle?: {} | {}[]; - /** 时间刻度对应的字段名 */ - startField: string; - /** 时间刻度对应的字段名 */ - endField: string; - /** 指定整个甘特图的最小日期 */ - minDate?: string; - /** 指定整个甘特图的最大日期 不设置的话用默认规则*/ - maxDate?: string; - - /** 设置的表格主题 */ - theme?: TableTheme; - /** 设置任务条样式 可以设置多组 依次循环使用 */ - barStyle?: IBarStyleOption[]; - defaultHeaderRowHeight?: number; - defaultRowHeight?: number; - timelineColWidth?: number; - - rowSeriesNumber?: IRowSeriesNumber; - dragHeader?: boolean; - - /** - * 'auto':和浏览器滚动行为一致 表格滚动到顶部/底部时 触发浏览器默认行为; - * 设置为 'none' 时, 表格滚动到顶部/底部时, 不再触发父容器滚动 - * */ - overscrollBehavior?: 'auto' | 'none'; - - // infoTableTheme?: ITableThemeDefine; -} - -export type IBarStyleOption = { - /** 任务条的颜色 */ - barColor?: ColorPropertyDefine; - /** 已完成部分任务条的颜色 */ - barColor2?: ColorPropertyDefine; - /** 任务条的宽度 */ - width?: number; - /** 任务条的圆角 */ - cornerRadius?: number; - /** 任务条的边框 */ - borderWidth?: number; - /** 边框颜色 */ - borderColor?: ColorPropertyDefine; - font?: ITextStyleOption; -}; -//#endregion From 93e13448aefcb3c3582c44e1c9978f4b36ddd510 Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Tue, 19 Nov 2024 16:57:03 +0800 Subject: [PATCH 04/40] feat: gantt chart support showHierarchyMode "sub_tasks" --- .../examples/gantt/gantt-Sub_Tasks.ts | 461 ++++++++++++++++++ packages/vtable-gantt/examples/menu.ts | 4 + packages/vtable-gantt/src/Gantt.ts | 43 +- .../vtable-gantt/src/event/event-manager.ts | 1 + packages/vtable-gantt/src/gantt-helper.ts | 4 +- .../src/scenegraph/dependency-link.ts | 74 ++- packages/vtable-gantt/src/scenegraph/grid.ts | 2 +- .../vtable-gantt/src/scenegraph/task-bar.ts | 19 +- .../vtable-gantt/src/state/state-manager.ts | 57 ++- .../vtable-gantt/src/ts-types/gantt-engine.ts | 2 +- 10 files changed, 630 insertions(+), 37 deletions(-) create mode 100644 packages/vtable-gantt/examples/gantt/gantt-Sub_Tasks.ts diff --git a/packages/vtable-gantt/examples/gantt/gantt-Sub_Tasks.ts b/packages/vtable-gantt/examples/gantt/gantt-Sub_Tasks.ts new file mode 100644 index 000000000..f32a21df3 --- /dev/null +++ b/packages/vtable-gantt/examples/gantt/gantt-Sub_Tasks.ts @@ -0,0 +1,461 @@ +import type { ColumnsDefine } from '@visactor/vtable'; +import { register } from '@visactor/vtable'; +import { DateInputEditor, InputEditor } from '@visactor/vtable-editors'; +import type { GanttConstructorOptions, TYPES } from '../../src/index'; +import { Gantt } from '../../src/index'; +import { bindDebugTool } from '../../../vtable/src/scenegraph/debug-tool'; +import { scale } from '@visactor/vutils'; +import { DependencyType, ShowHierarchyMode } from '../../src/ts-types'; +const CONTAINER_ID = 'vTable'; +const date_input_editor = new DateInputEditor({}); +const input_editor = new InputEditor({}); +register.editor('input', input_editor); +register.editor('date-input', date_input_editor); +export function createTable() { + const records = [ + { + id: 100, + title: 'Software Development', + children: [ + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-07-27', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024/08/01', + end: '2024/08/04', + progress: 100, + priority: 'P1' + } + ] + }, + { + id: 200, + title: 'Scope' + }, + { + id: 300, + title: 'Determine project scope', + children: [ + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-08-04', + end: '2024-08-04', + progress: 90, + priority: 'P0' + }, + + { + id: 4, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024.07.06', + end: '2024.07.08', + progress: 60, + priority: 'P0' + }, + { + id: 5, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '07.24.2024', + end: '08.02.2024', + progress: 31, + priority: 'P0' + }, + { + id: 6, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 7, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-08-09', + end: '2024-09-11', + progress: 100, + priority: 'P1' + } + ] + }, + + { + id: 100, + title: 'Software Development' + }, + { + id: 200, + title: 'Scope', + children: [ + { + id: 8, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + }, + { + id: 9, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 10, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 11, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + }, + { + id: 12, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 13, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-08-01', + end: '2024-08-04', + progress: 60, + priority: 'P0' + }, + { + id: 14, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-08-03', + end: '2024-08-05', + progress: 100, + priority: 'P1' + } + ] + }, + + { + id: 300, + title: 'Determine project scope', + children: [ + { + id: 15, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 16, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 17, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + }, + { + id: 18, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-30', + end: '2024-08-14', + progress: 31, + priority: 'P0' + }, + { + id: 19, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 60, + priority: 'P0' + } + ] + }, + { + id: 100, + title: 'Software Development', + children: [ + { + id: 20, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024/07/24', + end: '2024/08/04', + progress: 100, + priority: 'P1' + }, + { + id: 21, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-08-04', + end: '2024-08-04', + progress: 90, + priority: 'P0' + }, + { + id: 22, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '07/24/2024', + end: '08/04/2024', + progress: 60, + priority: 'P0' + } + ] + } + ]; + + const columns: ColumnsDefine = [ + { + field: 'title', + title: 'title', + width: 200, + tree: true, + sort: true + } + ]; + const option: GanttConstructorOptions = { + records, + rowHeight: 30, + taskListTable: { + columns: columns, + minTableWidth: 100, + hierarchyExpandLevel: 5, + menu: { + contextMenuItems: ['copy', 'paste', 'delete', '...'] + } + }, + groupBy: true, + showHierarchyMode: ShowHierarchyMode.Sub_Tasks, + frame: { + outerFrameStyle: { + borderLineWidth: 2, + borderColor: 'red', + cornerRadius: 8 + }, + verticalSplitLineHighlight: { + lineColor: 'green', + lineWidth: 3 + }, + verticalSplitLineMoveable: true + }, + grid: { + // backgroundColor: 'gray', + verticalLine: { + lineWidth: 1, + lineColor: '#e1e4e8' + }, + horizontalLine: { + lineWidth: 1, + lineColor: '#e1e4e8' + } + }, + headerRowHeight: 60, + taskBar: { + startDateField: 'start', + endDateField: 'end', + progressField: 'progress', + labelText: '{title} {progress}%', + labelTextStyle: { + fontFamily: 'Arial', + fontSize: 16, + textAlign: 'left' + }, + barStyle: { + width: 20, + /** 任务条的颜色 */ + barColor: '#ee8800', + /** 已完成部分任务条的颜色 */ + completedBarColor: '#91e8e0', + /** 任务条的圆角 */ + cornerRadius: 8 + } + }, + dependency: { + linkCreatable: true, + links: [ + { + type: DependencyType.StartToFinish, + linkedFromTaskKey: 3, + linkedToTaskKey: 2 + } + // { + // type: DependencyType.StartToFinish, + // linkedFromTaskKey: 2, + // linkedToTaskKey: 3 + // }, + // { + // type: DependencyType.StartToStart, + // linkedFromTaskKey: 3, + // linkedToTaskKey: 4 + // }, + // { + // type: DependencyType.FinishToFinish, + // linkedFromTaskKey: 4, + // linkedToTaskKey: 5 + // } + ] + }, + timelineHeader: { + verticalLine: { + lineColor: '#e1e4e8', + lineWidth: 1 + }, + horizontalLine: { + lineColor: '#e1e4e8', + lineWidth: 1 + }, + scales: [ + { + unit: 'week', + step: 1, + startOfWeek: 'sunday', + format(date: TYPES.DateFormatArgumentType) { + return `Week ${date.dateIndex}`; + }, + style: { + fontSize: 20, + fontWeight: 'bold', + color: 'red', + backgroundColor: '#EEF1F5' + } + }, + { + unit: 'day', + step: 1, + format(date: TYPES.DateFormatArgumentType) { + return date.dateIndex.toString(); + }, + style: { + fontSize: 20, + fontWeight: 'bold', + color: 'red', + backgroundColor: '#EEF1F5' + } + } + // { + // unit: 'quarter', + // step: 1, + // format(date: TYPES.DateFormatArgumentType) { + // return '第' + date.index + '季度'; + // } + // } + ] + }, + minDate: '2024-07-07', + maxDate: '2024-10-15', + markLine: [ + { + date: '2024-07-17', + style: { + lineWidth: 1, + lineColor: 'blue', + lineDash: [8, 4] + } + }, + { + date: '2024-08-17', + style: { + lineWidth: 2, + lineColor: 'red', + lineDash: [8, 4] + } + } + ], + rowSeriesNumber: { + title: '行号', + dragOrder: true, + headerStyle: { + bgColor: '#EEF1F5', + + borderColor: '#e1e4e8' + }, + style: { + borderColor: '#e1e4e8' + } + }, + scrollStyle: { + scrollRailColor: 'RGBA(246,246,246,0.5)', + visible: 'none', + width: 6, + scrollSliderCornerRadius: 2, + scrollSliderColor: '#5cb85c' + } + }; + // columns:[ + // { + // title:'2024-07', + // columns:[ + // { + // title:'01' + // }, + // { + // title:'02' + // }, + // ... + // ] + // }, + // ... + // ] + const ganttInstance = new Gantt(document.getElementById(CONTAINER_ID)!, option); + window.ganttInstance = ganttInstance; + ganttInstance.on('scroll', e => { + console.log('scroll', e); + }); + + ganttInstance.listTableInstance?.on('scroll', e => { + console.log('listTable scroll', e); + }); + // bindDebugTool(ganttInstance.scenegraph.stage as any, { + // customGrapicKeys: ['role', '_updateTag'] + // }); +} diff --git a/packages/vtable-gantt/examples/menu.ts b/packages/vtable-gantt/examples/menu.ts index d3b00dc3b..b91571086 100644 --- a/packages/vtable-gantt/examples/menu.ts +++ b/packages/vtable-gantt/examples/menu.ts @@ -73,6 +73,10 @@ export const menus = [ { path: 'gantt', name: 'gantt-Sub_Tasks_Inline' + }, + { + path: 'gantt', + name: 'gantt-Sub_Tasks' } // ] // } diff --git a/packages/vtable-gantt/src/Gantt.ts b/packages/vtable-gantt/src/Gantt.ts index 96f2e180c..2db741499 100644 --- a/packages/vtable-gantt/src/Gantt.ts +++ b/packages/vtable-gantt/src/Gantt.ts @@ -326,7 +326,10 @@ export class Gantt extends EventTarget { listTable_options[key] = this.options.taskListTable[key]; if (key === 'columns') { listTable_options[key][listTable_options[key].length - 1].disableColumnResize = true; - if (this.parsedOptions.showHierarchyMode === ShowHierarchyMode.Sub_Tasks_Inline) { + if ( + this.parsedOptions.showHierarchyMode === ShowHierarchyMode.Sub_Tasks_Inline || + this.parsedOptions.showHierarchyMode === ShowHierarchyMode.Sub_Tasks + ) { for (let i = 0; i < listTable_options.columns.length; i++) { if (listTable_options.columns[i].tree) { listTable_options.columns[i].tree = false; @@ -336,11 +339,13 @@ export class Gantt extends EventTarget { } if ( key === 'hierarchyExpandLevel' && - this.parsedOptions.showHierarchyMode === ShowHierarchyMode.Sub_Tasks_Inline + (this.parsedOptions.showHierarchyMode === ShowHierarchyMode.Sub_Tasks_Inline || + this.parsedOptions.showHierarchyMode === ShowHierarchyMode.Sub_Tasks) ) { delete listTable_options[key]; } } + // lineWidthArr[1] = 0; //Object.assign浅拷贝 会直接覆盖第一层属性 。theme.ARCO.extends 其中extends不能连续调用,且赋值也只是第一层 if (this.options.taskListTable?.theme) { @@ -512,7 +517,16 @@ export class Gantt extends EventTarget { listTable_options.canvasWidth = this.taskTableWidth as number; listTable_options.canvasHeight = this.canvas.height; listTable_options.defaultHeaderRowHeight = this.getAllHeaderRowsHeight(); - listTable_options.defaultRowHeight = this.parsedOptions.rowHeight; + if (this.parsedOptions.showHierarchyMode === ShowHierarchyMode.Sub_Tasks) { + listTable_options.customComputeRowHeight = (args: { row: number; table: ListTable }) => { + const { row, table } = args; + const record = table.getRecordByRowCol(0, row); + return (record.children?.length ?? 1) * this.parsedOptions.rowHeight; + }; + listTable_options.defaultRowHeight = 'auto'; + } else { + listTable_options.defaultRowHeight = this.options.rowHeight ?? 40; + } listTable_options.clearDOM = false; return listTable_options; } @@ -589,8 +603,18 @@ export class Gantt extends EventTarget { } this.parsedOptions.colWidthPerDay = this.parsedOptions.timelineColWidth / colWidthIncludeDays; } + getRowHeightByIndex(index: number) { + return this.taskListTableInstance.getRowHeight(index + this.taskListTableInstance.columnHeaderLevelCount); + } + getRowsHeightByIndex(startIndex: number, endIndex: number) { + return this.taskListTableInstance.getRowsHeight( + startIndex + this.taskListTableInstance.columnHeaderLevelCount, + endIndex + this.taskListTableInstance.columnHeaderLevelCount + ); + } getAllRowsHeight() { - return this.getAllHeaderRowsHeight() + this.itemCount * this.parsedOptions.rowHeight; + // return this.getAllHeaderRowsHeight() + this.itemCount * this.parsedOptions.rowHeight; + return this.taskListTableInstance.getAllRowsHeight(); } getAllHeaderRowsHeight() { // if (Array.isArray(this.parsedOptions.timeLineHeaderRowHeights)) { @@ -615,7 +639,11 @@ export class Gantt extends EventTarget { } getAllTaskBarsHeight() { - return this.itemCount * this.parsedOptions.rowHeight; + // return this.itemCount * this.parsedOptions.rowHeight; + return this.taskListTableInstance.getRowsHeight( + this.taskListTableInstance.columnHeaderLevelCount, + this.taskListTableInstance.rowCount - 1 + ); } getTaskShowIndexByRecordIndex(index: number | number[]) { return this.taskListTableInstance.getBodyRowIndexByRecordIndex(index); @@ -824,10 +852,7 @@ export class Gantt extends EventTarget { ? this.taskListTableInstance.rowCount - this.taskListTableInstance.columnHeaderLevelCount : this.records.length; this.headerHeight = this.getAllHeaderRowsHeight(); - this.drawHeight = Math.min( - this.headerHeight + this.parsedOptions.rowHeight * this.itemCount, - this.tableNoFrameHeight - ); + this.drawHeight = Math.min(this.getAllRowsHeight(), this.tableNoFrameHeight); this.gridHeight = this.drawHeight - this.headerHeight; } /** 获取绘制画布的canvas上下文 */ diff --git a/packages/vtable-gantt/src/event/event-manager.ts b/packages/vtable-gantt/src/event/event-manager.ts index bf7f56cd8..dc4270331 100644 --- a/packages/vtable-gantt/src/event/event-manager.ts +++ b/packages/vtable-gantt/src/event/event-manager.ts @@ -171,6 +171,7 @@ function bindTableGroupListener(event: EventManager) { //#region hover到某一个任务 检查有没有日期安排,没有的话显示创建按钮 if ( gantt.parsedOptions.showHierarchyMode !== ShowHierarchyMode.Sub_Tasks_Inline && + gantt.parsedOptions.showHierarchyMode !== ShowHierarchyMode.Sub_Tasks && gantt.parsedOptions.taskBarCreatable ) { const taskIndex = getTaskIndexByY(e.offset.y, gantt); diff --git a/packages/vtable-gantt/src/gantt-helper.ts b/packages/vtable-gantt/src/gantt-helper.ts index 01d4b40cb..ecf2fa514 100644 --- a/packages/vtable-gantt/src/gantt-helper.ts +++ b/packages/vtable-gantt/src/gantt-helper.ts @@ -94,7 +94,7 @@ export { isNode }; export function initOptions(gantt: Gantt) { const options = gantt.options; - gantt.parsedOptions.showHierarchyMode = options?.showHierarchyMode ?? ShowHierarchyMode.Full; + gantt.parsedOptions.showHierarchyMode = options?.showHierarchyMode ?? ShowHierarchyMode.All; gantt.parsedOptions.pixelRatio = options?.pixelRatio ?? 1; gantt.parsedOptions.rowHeight = options?.rowHeight ?? 40; gantt.parsedOptions.timelineColWidth = options?.timelineHeader?.colWidth ?? 60; @@ -208,7 +208,7 @@ export function initOptions(gantt: Gantt) { // options?.taskBar?.hoverColor === null ? 'rgba(0,0,0,0)' : options?.taskBar?.hoverColor ?? 'rgba(0,0,0,0.1)'; gantt.parsedOptions.taskBarLabelStyle = { fontFamily: options?.taskBar?.labelTextStyle?.fontFamily ?? 'Arial', - fontSize: options?.taskBar?.labelTextStyle?.fontSize ?? gantt.parsedOptions.rowHeight / 2, + fontSize: options?.taskBar?.labelTextStyle?.fontSize ?? 20, color: options?.taskBar?.labelTextStyle?.color ?? '#F01', textAlign: options?.taskBar?.labelTextStyle?.textAlign ?? 'left', textBaseline: options?.taskBar?.labelTextStyle?.textBaseline ?? 'middle', diff --git a/packages/vtable-gantt/src/scenegraph/dependency-link.ts b/packages/vtable-gantt/src/scenegraph/dependency-link.ts index c27d4b5b8..9bf52c374 100644 --- a/packages/vtable-gantt/src/scenegraph/dependency-link.ts +++ b/packages/vtable-gantt/src/scenegraph/dependency-link.ts @@ -88,6 +88,25 @@ export class DependencyLink { endDate: linkedFromTaskEndDate, taskDays: linkedFromTaskTaskDays } = this._scene._gantt.getTaskInfoByTaskListIndex(linkedFromTaskRecord.index[0], linkedFromTaskRecord.index[1])); + } else if (this._scene._gantt.parsedOptions.showHierarchyMode === ShowHierarchyMode.Sub_Tasks) { + const beforeRowCountLinkedFrom = + this._scene._gantt.getRowsHeightByIndex(0, linkedFromTaskRecord.index[0] - 1) / + this._scene._gantt.parsedOptions.rowHeight; // 耦合了listTableOption的customComputeRowHeight + linkedFromTaskShowIndex = beforeRowCountLinkedFrom + linkedFromTaskRecord.index[1]; + const beforeRowCountLinkedTo = + this._scene._gantt.getRowsHeightByIndex(0, linkedToTaskRecord.index[0] - 1) / + this._scene._gantt.parsedOptions.rowHeight; // 耦合了listTableOption的customComputeRowHeight + linkedToTaskShowIndex = beforeRowCountLinkedTo + linkedToTaskRecord.index[1]; + ({ + startDate: linkedToTaskStartDate, + endDate: linkedToTaskEndDate, + taskDays: linkedToTaskTaskDays + } = this._scene._gantt.getTaskInfoByTaskListIndex(linkedToTaskRecord.index[0], linkedToTaskRecord.index[1])); + ({ + startDate: linkedFromTaskStartDate, + endDate: linkedFromTaskEndDate, + taskDays: linkedFromTaskTaskDays + } = this._scene._gantt.getTaskInfoByTaskListIndex(linkedFromTaskRecord.index[0], linkedFromTaskRecord.index[1])); } else { linkedFromTaskShowIndex = this._scene._gantt.getTaskShowIndexByRecordIndex(linkedFromTaskRecord.index); linkedToTaskShowIndex = this._scene._gantt.getTaskShowIndexByRecordIndex(linkedToTaskRecord.index); @@ -396,7 +415,7 @@ export function generateLinkLinePoints( x: linkToPointX - distanceToTaskBar, y: rowHeight * - (linkedFromTaskRecordRowIndex + (linkedFromTaskRecordRowIndex === linkedToTaskRecordRowIndex ? 1 : 0.5)) + (linkedToTaskRecordRowIndex + (linkedFromTaskRecordRowIndex === linkedToTaskRecordRowIndex ? 1 : 0.5)) }, { x: @@ -448,10 +467,6 @@ export function generateLinkLinePoints( : Math.max(linkFromPointX, linkToPointX)) + distanceToTaskBar, y: rowHeight * (linkedFromTaskRecordRowIndex + 0.5) }, - // { - // x: Math.max(linkFromPointX, linkToPointX) + distanceToTaskBar, - // y: rowHeight * (linkedToTaskRecordRowIndex + 0.5) - // }, { x: (linkedFromTaskRecordRowIndex === linkedToTaskRecordRowIndex @@ -465,7 +480,7 @@ export function generateLinkLinePoints( x: linkToPointX + distanceToTaskBar, y: rowHeight * - (linkedFromTaskRecordRowIndex + (linkedFromTaskRecordRowIndex === linkedToTaskRecordRowIndex ? 1 : 0.5)) + (linkedToTaskRecordRowIndex + (linkedFromTaskRecordRowIndex === linkedToTaskRecordRowIndex ? 1 : 0.5)) }, { x: linkToPointX + distanceToTaskBar, @@ -665,11 +680,32 @@ export function updateLinkLinePoints( y: rowHeight * (linkedFromTaskRecordRowIndex + 0.5) }, { - x: Math.min(linkFromPointX, linkToPointX) - distanceToTaskBar, + x: + (linkedFromTaskRecordRowIndex === linkedToTaskRecordRowIndex + ? linkFromPointX + : Math.min(linkFromPointX, linkToPointX)) - distanceToTaskBar, y: rowHeight * (linkedFromTaskRecordRowIndex + 0.5) }, { - x: Math.min(linkFromPointX, linkToPointX) - distanceToTaskBar, + x: + (linkedFromTaskRecordRowIndex === linkedToTaskRecordRowIndex + ? linkFromPointX + : Math.min(linkFromPointX, linkToPointX)) - distanceToTaskBar, + y: + rowHeight * + (linkedToTaskRecordRowIndex + (linkedFromTaskRecordRowIndex === linkedToTaskRecordRowIndex ? 1 : 0.5)) + }, + { + x: linkToPointX - distanceToTaskBar, + y: + rowHeight * + (linkedToTaskRecordRowIndex + (linkedFromTaskRecordRowIndex === linkedToTaskRecordRowIndex ? 1 : 0.5)) + }, + { + x: + (linkedFromTaskRecordRowIndex === linkedToTaskRecordRowIndex + ? linkToPointX + : Math.min(linkFromPointX, linkToPointX)) - distanceToTaskBar, y: rowHeight * (linkedToTaskRecordRowIndex + 0.5) }, { @@ -711,11 +747,29 @@ export function updateLinkLinePoints( y: rowHeight * (linkedFromTaskRecordRowIndex + 0.5) }, { - x: Math.max(linkFromPointX, linkToPointX) + distanceToTaskBar, + x: + (linkedFromTaskRecordRowIndex === linkedToTaskRecordRowIndex + ? linkFromPointX + : Math.max(linkFromPointX, linkToPointX)) + distanceToTaskBar, y: rowHeight * (linkedFromTaskRecordRowIndex + 0.5) }, { - x: Math.max(linkFromPointX, linkToPointX) + distanceToTaskBar, + x: + (linkedFromTaskRecordRowIndex === linkedToTaskRecordRowIndex + ? linkFromPointX + : Math.max(linkFromPointX, linkToPointX)) + distanceToTaskBar, + y: + rowHeight * + (linkedToTaskRecordRowIndex + (linkedFromTaskRecordRowIndex === linkedToTaskRecordRowIndex ? 1 : 0.5)) + }, + { + x: linkToPointX + distanceToTaskBar, + y: + rowHeight * + (linkedToTaskRecordRowIndex + (linkedFromTaskRecordRowIndex === linkedToTaskRecordRowIndex ? 1 : 0.5)) + }, + { + x: linkToPointX + distanceToTaskBar, y: rowHeight * (linkedToTaskRecordRowIndex + 0.5) }, { diff --git a/packages/vtable-gantt/src/scenegraph/grid.ts b/packages/vtable-gantt/src/scenegraph/grid.ts index e9b318ed1..107525fe9 100644 --- a/packages/vtable-gantt/src/scenegraph/grid.ts +++ b/packages/vtable-gantt/src/scenegraph/grid.ts @@ -127,7 +127,7 @@ export class Grid { y += 0.5; } for (let i = 0; i < this.rowCount - 1; i++) { - y = y + Math.floor(this.rowHeight); + y = y + this._scene._gantt.getRowHeightByIndex(i); // Math.floor(this.rowHeight); const line = createLine({ pickable: false, stroke: this.gridStyle?.horizontalLine.lineColor, diff --git a/packages/vtable-gantt/src/scenegraph/task-bar.ts b/packages/vtable-gantt/src/scenegraph/task-bar.ts index b70fe765e..98d7857ca 100644 --- a/packages/vtable-gantt/src/scenegraph/task-bar.ts +++ b/packages/vtable-gantt/src/scenegraph/task-bar.ts @@ -55,11 +55,14 @@ export class TaskBar { this.group.appendChild(this.barContainer); for (let i = 0; i < this._scene._gantt.itemCount; i++) { - if (this._scene._gantt.parsedOptions.showHierarchyMode === ShowHierarchyMode.Sub_Tasks_Inline) { + if ( + this._scene._gantt.parsedOptions.showHierarchyMode === ShowHierarchyMode.Sub_Tasks_Inline || + this._scene._gantt.parsedOptions.showHierarchyMode === ShowHierarchyMode.Sub_Tasks + ) { const record = this._scene._gantt.getRecordByIndex(i); if (record.children?.length > 0) { for (let j = 0; j < record.children.length; j++) { - const barGroup = this.initBar(i, j); + const barGroup = this.initBar(i, j, record.children.length); if (barGroup) { this.barContainer.appendChild(barGroup); } @@ -74,7 +77,7 @@ export class TaskBar { } } } - initBar(index: number, childIndex?: number) { + initBar(index: number, childIndex?: number, childrenLength?: number) { const taskBarCustomLayout = this._scene._gantt.parsedOptions.taskBarCustomLayout; let startDate; let endDate; @@ -95,14 +98,20 @@ export class TaskBar { const taskBarSize = this._scene._gantt.parsedOptions.colWidthPerDay * taskDays; const taskbarHeight = this._scene._gantt.parsedOptions.taskBarStyle.width; const minDate = createDateAtMidnight(this._scene._gantt.parsedOptions.minDate); + const oneTaskHeigth = + this._scene._gantt.getRowHeightByIndex(index) / + (this._scene._gantt.parsedOptions.showHierarchyMode === ShowHierarchyMode.Sub_Tasks ? childrenLength : 1); const barGroup = new GanttTaskBarNode({ x: this._scene._gantt.parsedOptions.colWidthPerDay * Math.ceil(Math.abs(startDate.getTime() - minDate.getTime()) / (1000 * 60 * 60 * 24)), // y: this._scene._gantt.parsedOptions.rowHeight * i, y: - this._scene._gantt.parsedOptions.rowHeight * index + - (this._scene._gantt.parsedOptions.rowHeight - taskbarHeight) / 2, + this._scene._gantt.getRowsHeightByIndex(0, index - 1) + + (this._scene._gantt.parsedOptions.showHierarchyMode === ShowHierarchyMode.Sub_Tasks + ? childIndex * oneTaskHeigth + : 0) + + (oneTaskHeigth - taskbarHeight) / 2, width: taskBarSize, // height: this._scene._gantt.parsedOptions.rowHeight, height: taskbarHeight, diff --git a/packages/vtable-gantt/src/state/state-manager.ts b/packages/vtable-gantt/src/state/state-manager.ts index 308864763..b27235c2c 100644 --- a/packages/vtable-gantt/src/state/state-manager.ts +++ b/packages/vtable-gantt/src/state/state-manager.ts @@ -652,12 +652,14 @@ export class StateManager { showTaskBarHover() { const target = this._gantt.stateManager.hoverTaskBar.target; - const x = target.attribute.x; - const y = target.attribute.y; - const width = target.attribute.width; - const height = target.attribute.height; - this._gantt.scenegraph.taskBar.showHoverBar(x, y, width, height, target); - this._gantt.scenegraph.updateNextFrame(); + if (target) { + const x = target.attribute.x; + const y = target.attribute.y; + const width = target.attribute.width; + const height = target.attribute.height; + this._gantt.scenegraph.taskBar.showHoverBar(x, y, width, height, target); + this._gantt.scenegraph.updateNextFrame(); + } } hideTaskBarHover(e: FederatedPointerEvent) { this._gantt.stateManager.hoverTaskBar.target = null; @@ -707,7 +709,10 @@ export class StateManager { let linkTo_sub_task_index; const linkedToTaskRecord = findRecordByTaskKey(this._gantt.records, taskKeyField, linkedToTaskKey); const linkedFromTaskRecord = findRecordByTaskKey(this._gantt.records, taskKeyField, linkedFromTaskKey); - if (this._gantt.parsedOptions.showHierarchyMode === ShowHierarchyMode.Sub_Tasks_Inline) { + if ( + this._gantt.parsedOptions.showHierarchyMode === ShowHierarchyMode.Sub_Tasks_Inline || + this._gantt.parsedOptions.showHierarchyMode === ShowHierarchyMode.Sub_Tasks + ) { linkFrom_index = linkedFromTaskRecord.index[0]; linkFrom_sub_task_index = linkedFromTaskRecord.index[1]; linkTo_index = linkedToTaskRecord.index[0]; @@ -856,6 +861,23 @@ function resizeOrMoveTaskBar(target: GanttTaskBarNode, dx: number, newWidth: num endDate: linkedFromTaskEndDate, taskDays: linkedFromTaskTaskDays } = state._gantt.getTaskInfoByTaskListIndex(linkedFromTaskRecord.index[0], linkedFromTaskRecord.index[1])); + } else if (state._gantt.parsedOptions.showHierarchyMode === ShowHierarchyMode.Sub_Tasks) { + const beforeRowCountLinkedFrom = + state._gantt.getRowsHeightByIndex(0, linkedFromTaskRecord.index[0] - 1) / state._gantt.parsedOptions.rowHeight; // 耦合了listTableOption的customComputeRowHeight + linkedFromTaskShowIndex = beforeRowCountLinkedFrom + linkedFromTaskRecord.index[1]; + const beforeRowCountLinkedTo = + state._gantt.getRowsHeightByIndex(0, linkedToTaskRecord.index[0] - 1) / state._gantt.parsedOptions.rowHeight; // 耦合了listTableOption的customComputeRowHeight + linkedToTaskShowIndex = beforeRowCountLinkedTo + linkedToTaskRecord.index[1]; + ({ + startDate: linkedToTaskStartDate, + endDate: linkedToTaskEndDate, + taskDays: linkedToTaskTaskDays + } = state._gantt.getTaskInfoByTaskListIndex(linkedToTaskRecord.index[0], linkedToTaskRecord.index[1])); + ({ + startDate: linkedFromTaskStartDate, + endDate: linkedFromTaskEndDate, + taskDays: linkedFromTaskTaskDays + } = state._gantt.getTaskInfoByTaskListIndex(linkedFromTaskRecord.index[0], linkedFromTaskRecord.index[1])); } else { linkedFromTaskShowIndex = state._gantt.getTaskShowIndexByRecordIndex(linkedFromTaskRecord.index); linkedToTaskShowIndex = state._gantt.getTaskShowIndexByRecordIndex(linkedToTaskRecord.index); @@ -878,7 +900,7 @@ function resizeOrMoveTaskBar(target: GanttTaskBarNode, dx: number, newWidth: num linkedFromTaskShowIndex, linkedToTaskStartDate, linkedToTaskEndDate, - taskIndex, + linkedToTaskShowIndex, minDate, state._gantt.parsedOptions.rowHeight, state._gantt.parsedOptions.colWidthPerDay, @@ -927,6 +949,23 @@ function resizeOrMoveTaskBar(target: GanttTaskBarNode, dx: number, newWidth: num endDate: linkedFromTaskEndDate, taskDays: linkedFromTaskTaskDays } = state._gantt.getTaskInfoByTaskListIndex(linkedFromTaskRecord.index[0], linkedFromTaskRecord.index[1])); + } else if (state._gantt.parsedOptions.showHierarchyMode === ShowHierarchyMode.Sub_Tasks) { + const beforeRowCountLinkedFrom = + state._gantt.getRowsHeightByIndex(0, linkedFromTaskRecord.index[0] - 1) / state._gantt.parsedOptions.rowHeight; // 耦合了listTableOption的customComputeRowHeight + linkedFromTaskShowIndex = beforeRowCountLinkedFrom + linkedFromTaskRecord.index[1]; + const beforeRowCountLinkedTo = + state._gantt.getRowsHeightByIndex(0, linkedToTaskRecord.index[0] - 1) / state._gantt.parsedOptions.rowHeight; // 耦合了listTableOption的customComputeRowHeight + linkedToTaskShowIndex = beforeRowCountLinkedTo + linkedToTaskRecord.index[1]; + ({ + startDate: linkedToTaskStartDate, + endDate: linkedToTaskEndDate, + taskDays: linkedToTaskTaskDays + } = state._gantt.getTaskInfoByTaskListIndex(linkedToTaskRecord.index[0], linkedToTaskRecord.index[1])); + ({ + startDate: linkedFromTaskStartDate, + endDate: linkedFromTaskEndDate, + taskDays: linkedFromTaskTaskDays + } = state._gantt.getTaskInfoByTaskListIndex(linkedFromTaskRecord.index[0], linkedFromTaskRecord.index[1])); } else { linkedFromTaskShowIndex = state._gantt.getTaskShowIndexByRecordIndex(linkedFromTaskRecord.index); linkedToTaskShowIndex = state._gantt.getTaskShowIndexByRecordIndex(linkedToTaskRecord.index); @@ -946,7 +985,7 @@ function resizeOrMoveTaskBar(target: GanttTaskBarNode, dx: number, newWidth: num type, linkedFromTaskStartDate, linkedFromTaskEndDate, - taskIndex, + linkedFromTaskShowIndex, linkedToTaskStartDate, linkedToTaskEndDate, linkedToTaskShowIndex, diff --git a/packages/vtable-gantt/src/ts-types/gantt-engine.ts b/packages/vtable-gantt/src/ts-types/gantt-engine.ts index 65c54774d..d096a03bd 100644 --- a/packages/vtable-gantt/src/ts-types/gantt-engine.ts +++ b/packages/vtable-gantt/src/ts-types/gantt-engine.ts @@ -336,7 +336,7 @@ export enum DependencyType { } export enum ShowHierarchyMode { /** 每一个节点用单独一行来展示,也就是父子任务的节点都占用一行 */ - Full = 'full', + All = 'all', /** 省去父节点,把所有子任务的节点都放到同一行来展示。 */ Sub_Tasks_Inline = 'sub_tasks_inline', /** 所有子任务的节点分别用一行展示。*/ From 11e625b3557c84f503172e2e38b2a6b96fa00d64 Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Tue, 19 Nov 2024 17:04:22 +0800 Subject: [PATCH 05/40] feat: add option customComputeRowHeight and defaultRowHeight can set "auto" --- .../vtable/src/core/utils/get-cell-position.ts | 2 +- .../src/scenegraph/group-creater/column.ts | 16 ++++++---------- .../scenegraph/group-creater/progress/proxy.ts | 6 +++--- packages/vtable/src/ts-types/base-table.ts | 2 +- 4 files changed, 11 insertions(+), 15 deletions(-) diff --git a/packages/vtable/src/core/utils/get-cell-position.ts b/packages/vtable/src/core/utils/get-cell-position.ts index bff4d80a4..9db48b813 100644 --- a/packages/vtable/src/core/utils/get-cell-position.ts +++ b/packages/vtable/src/core/utils/get-cell-position.ts @@ -298,7 +298,7 @@ export function getTargetRowAtConsiderBottomFrozen( * @returns */ export function computeTargetRowByY(absoluteY: number, _this: BaseTableAPI): number { - let defaultRowHeight = _this.internalProps.defaultRowHeight; + let defaultRowHeight = _this.defaultRowHeight; //使用二分法计算出row if (_this._rowRangeHeightsMap.get(`$0$${_this.rowCount - 1}`)) { diff --git a/packages/vtable/src/scenegraph/group-creater/column.ts b/packages/vtable/src/scenegraph/group-creater/column.ts index 5e82d9d53..c16375932 100644 --- a/packages/vtable/src/scenegraph/group-creater/column.ts +++ b/packages/vtable/src/scenegraph/group-creater/column.ts @@ -125,21 +125,15 @@ export function createColGroup( if (colStart > colEnd || rowStart > rowEnd) { return; } - const { layoutMap, defaultRowHeight, defaultHeaderRowHeight, defaultColWidth } = table.internalProps; + const { layoutMap, defaultHeaderRowHeight, defaultColWidth } = table.internalProps; + const defaultRowHeight = table.defaultRowHeight; let x = 0; let heightMax = 0; for (let i = colStart; i <= colEnd; i++) { const col = i; const colWidth = table.getColWidth(col); - const columnGroup = new Group({ - x: xOrigin + x, - y: yOrigin, - width: colWidth, - height: 0, - clip: false, - pickable: false - }); + const columnGroup = new Group(); columnGroup.role = 'column'; columnGroup.col = i; containerGroup.addChild(columnGroup); @@ -150,7 +144,9 @@ export function createColGroup( rowStart, rowEnd, table.scenegraph.mergeMap, - cellLocation === 'columnHeader' && isNumber(defaultHeaderRowHeight) ? defaultHeaderRowHeight : defaultRowHeight, + cellLocation === 'columnHeader' && isNumber(defaultHeaderRowHeight) + ? (defaultHeaderRowHeight as number) + : defaultRowHeight, table, // cellLocation, rowLimit diff --git a/packages/vtable/src/scenegraph/group-creater/progress/proxy.ts b/packages/vtable/src/scenegraph/group-creater/progress/proxy.ts index 1c98ed78f..948cfb07f 100644 --- a/packages/vtable/src/scenegraph/group-creater/progress/proxy.ts +++ b/packages/vtable/src/scenegraph/group-creater/progress/proxy.ts @@ -313,7 +313,7 @@ export class SceneProxy { this.currentRow + 1, endRow, this.table.scenegraph.mergeMap, - this.table.internalProps.defaultRowHeight, + this.table.defaultRowHeight, this.table // cellLocation ); @@ -335,7 +335,7 @@ export class SceneProxy { this.currentRow + 1, endRow, this.table.scenegraph.mergeMap, - this.table.internalProps.defaultRowHeight, + this.table.defaultRowHeight, this.table // cellLocation ); @@ -359,7 +359,7 @@ export class SceneProxy { this.currentRow + 1, endRow, this.table.scenegraph.mergeMap, - this.table.internalProps.defaultRowHeight, + this.table.defaultRowHeight, this.table // cellLocation ); diff --git a/packages/vtable/src/ts-types/base-table.ts b/packages/vtable/src/ts-types/base-table.ts index 339446a19..9ed130ab6 100644 --- a/packages/vtable/src/ts-types/base-table.ts +++ b/packages/vtable/src/ts-types/base-table.ts @@ -541,7 +541,7 @@ export interface BaseTableAPI { /** 当前表格默认表头行高 */ defaultHeaderRowHeight: (number | 'auto') | (number | 'auto')[]; /** 当前表格默认行高 */ - defaultRowHeight: number | 'auto'; + defaultRowHeight: number; /** 当前表格默认列宽 */ defaultColWidth: number; /** 当前表格默认表头列宽 */ From 2d82776e0de6d05462a816ec7b7e13a83c508927 Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Tue, 19 Nov 2024 17:05:36 +0800 Subject: [PATCH 06/40] docs: update changlog of rush --- ...-chart-support-table-groupby_2024-11-19-09-05.json | 11 +++++++++++ .../vtable/src/scenegraph/group-creater/column.ts | 9 ++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 common/changes/@visactor/vtable/2829-feature-gantt-chart-support-table-groupby_2024-11-19-09-05.json diff --git a/common/changes/@visactor/vtable/2829-feature-gantt-chart-support-table-groupby_2024-11-19-09-05.json b/common/changes/@visactor/vtable/2829-feature-gantt-chart-support-table-groupby_2024-11-19-09-05.json new file mode 100644 index 000000000..9c4f9fdb2 --- /dev/null +++ b/common/changes/@visactor/vtable/2829-feature-gantt-chart-support-table-groupby_2024-11-19-09-05.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "feat: add option customComputeRowHeight and defaultRowHeight can set \"auto\"\n\n", + "type": "none", + "packageName": "@visactor/vtable" + } + ], + "packageName": "@visactor/vtable", + "email": "892739385@qq.com" +} \ No newline at end of file diff --git a/packages/vtable/src/scenegraph/group-creater/column.ts b/packages/vtable/src/scenegraph/group-creater/column.ts index c16375932..41ab682f7 100644 --- a/packages/vtable/src/scenegraph/group-creater/column.ts +++ b/packages/vtable/src/scenegraph/group-creater/column.ts @@ -133,7 +133,14 @@ export function createColGroup( const col = i; const colWidth = table.getColWidth(col); - const columnGroup = new Group(); + const columnGroup = new Group({ + x: xOrigin + x, + y: yOrigin, + width: colWidth, + height: 0, + clip: false, + pickable: false + }); columnGroup.role = 'column'; columnGroup.col = i; containerGroup.addChild(columnGroup); From d1175bef6b0da73e4137c4f6244d17c29ce0b370 Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Tue, 19 Nov 2024 17:54:23 +0800 Subject: [PATCH 07/40] fix: when not left table columns render normally --- packages/vtable-gantt/src/Gantt.ts | 67 +++++++++++-------- packages/vtable-gantt/src/gantt-helper.ts | 2 + .../vtable-gantt/src/ts-types/gantt-engine.ts | 4 +- 3 files changed, 42 insertions(+), 31 deletions(-) diff --git a/packages/vtable-gantt/src/Gantt.ts b/packages/vtable-gantt/src/Gantt.ts index 2db741499..5bdb34486 100644 --- a/packages/vtable-gantt/src/Gantt.ts +++ b/packages/vtable-gantt/src/Gantt.ts @@ -113,6 +113,7 @@ export class Gantt extends EventTarget { taskBarLabelText: ITaskBarLabelText; taskBarMoveable: boolean; taskBarResizable: boolean; + taskBarDragOrder: boolean; taskBarLabelStyle: ITaskBarLabelTextStyle; taskBarCustomLayout: ITaskBarCustomLayout; taskBarCreatable: boolean; @@ -279,40 +280,40 @@ export class Gantt extends EventTarget { } } _generateListTable() { - if (this.taskTableColumns.length >= 1 || this.options?.rowSeriesNumber) { - const listTableOption = this._generateListTableOptions(); - this.taskListTableInstance = new ListTable(this.container, listTableOption); - - if (this.options?.taskListTable?.tableWidth === 'auto' || this.taskTableWidth === -1) { - this.taskTableWidth = - this.taskListTableInstance.getAllColsWidth() + this.parsedOptions.outerFrameStyle.borderLineWidth; - if (this.options?.taskListTable?.maxTableWidth) { - this.taskTableWidth = Math.min(this.options?.taskListTable?.maxTableWidth, this.taskTableWidth); - } - if (this.options?.taskListTable?.minTableWidth) { - this.taskTableWidth = Math.max(this.options?.taskListTable?.minTableWidth, this.taskTableWidth); - } - this.element.style.left = this.taskTableWidth ? `${this.taskTableWidth}px` : '0px'; - this.taskListTableInstance.setCanvasSize( - this.taskTableWidth, - this.tableNoFrameHeight + this.parsedOptions.outerFrameStyle.borderLineWidth * 2 - ); - this._updateSize(); + // if (this.taskTableColumns.length >= 1 || this.options?.rowSeriesNumber) { + const listTableOption = this._generateListTableOptions(); + this.taskListTableInstance = new ListTable(this.container, listTableOption); + + if (this.options?.taskListTable?.tableWidth === 'auto' || this.taskTableWidth === -1) { + this.taskTableWidth = + this.taskListTableInstance.getAllColsWidth() + this.parsedOptions.outerFrameStyle.borderLineWidth; + if (this.options?.taskListTable?.maxTableWidth) { + this.taskTableWidth = Math.min(this.options?.taskListTable?.maxTableWidth, this.taskTableWidth); } + if (this.options?.taskListTable?.minTableWidth) { + this.taskTableWidth = Math.max(this.options?.taskListTable?.minTableWidth, this.taskTableWidth); + } + this.element.style.left = this.taskTableWidth ? `${this.taskTableWidth}px` : '0px'; + this.taskListTableInstance.setCanvasSize( + this.taskTableWidth, + this.tableNoFrameHeight + this.parsedOptions.outerFrameStyle.borderLineWidth * 2 + ); + this._updateSize(); + } - if (this.taskListTableInstance.columnHeaderLevelCount > 1) { - if (this.taskListTableInstance.columnHeaderLevelCount === this.parsedOptions.timeLineHeaderRowHeights.length) { - for (let i = 0; i < this.taskListTableInstance.columnHeaderLevelCount; i++) { - this.taskListTableInstance.setRowHeight(i, this.parsedOptions.timeLineHeaderRowHeights[i]); - } - } else { - const newRowHeight = this.getAllHeaderRowsHeight() / this.taskListTableInstance.columnHeaderLevelCount; - for (let i = 0; i < this.taskListTableInstance.columnHeaderLevelCount; i++) { - this.taskListTableInstance.setRowHeight(i, newRowHeight); - } + if (this.taskListTableInstance.columnHeaderLevelCount > 1) { + if (this.taskListTableInstance.columnHeaderLevelCount === this.parsedOptions.timeLineHeaderRowHeights.length) { + for (let i = 0; i < this.taskListTableInstance.columnHeaderLevelCount; i++) { + this.taskListTableInstance.setRowHeight(i, this.parsedOptions.timeLineHeaderRowHeights[i]); + } + } else { + const newRowHeight = this.getAllHeaderRowsHeight() / this.taskListTableInstance.columnHeaderLevelCount; + for (let i = 0; i < this.taskListTableInstance.columnHeaderLevelCount; i++) { + this.taskListTableInstance.setRowHeight(i, newRowHeight); } } } + // } } _generateListTableOptions() { const listTable_options: ListTableConstructorOptions = {}; @@ -528,6 +529,14 @@ export class Gantt extends EventTarget { listTable_options.defaultRowHeight = this.options.rowHeight ?? 40; } listTable_options.clearDOM = false; + if (!listTable_options.rowSeriesNumber && !listTable_options.columns) { + // 左侧表格没有内容的时候 为了正常初始化ListTable 这里强制设置了序号列 + listTable_options.rowSeriesNumber = { + width: 0 + }; + this.parsedOptions.verticalSplitLineMoveable = false; + delete this.parsedOptions.verticalSplitLineHighlight; + } return listTable_options; } diff --git a/packages/vtable-gantt/src/gantt-helper.ts b/packages/vtable-gantt/src/gantt-helper.ts index ecf2fa514..9e00048b9 100644 --- a/packages/vtable-gantt/src/gantt-helper.ts +++ b/packages/vtable-gantt/src/gantt-helper.ts @@ -204,6 +204,8 @@ export function initOptions(gantt: Gantt) { gantt.parsedOptions.taskBarLabelText = options?.taskBar?.labelText ?? ''; gantt.parsedOptions.taskBarMoveable = options?.taskBar?.moveable ?? true; gantt.parsedOptions.taskBarResizable = options?.taskBar?.resizable ?? true; + gantt.parsedOptions.taskBarDragOrder = options?.taskBar?.dragOrder ?? true; + // gantt.parsedOptions.taskBarHoverColor = // options?.taskBar?.hoverColor === null ? 'rgba(0,0,0,0)' : options?.taskBar?.hoverColor ?? 'rgba(0,0,0,0.1)'; gantt.parsedOptions.taskBarLabelStyle = { diff --git a/packages/vtable-gantt/src/ts-types/gantt-engine.ts b/packages/vtable-gantt/src/ts-types/gantt-engine.ts index d096a03bd..15b232f28 100644 --- a/packages/vtable-gantt/src/ts-types/gantt-engine.ts +++ b/packages/vtable-gantt/src/ts-types/gantt-engine.ts @@ -88,14 +88,14 @@ export interface GanttConstructorOptions { resizable?: boolean; /** 任务条是否可移动 */ moveable?: boolean; + /** 任务条是否可以被拖拽来改变顺序 */ + dragOrder?: boolean; /** 任务条hover时的样式 */ hoverBarStyle?: ITaskBarHoverStyle; /** 任务条选择时的样式 TODO */ selectedBarStyle?: ITaskBarSelectedStyle; /** 任务条是否可选择,默认为true */ selectable?: boolean; - /** 任务条是否可以被拖拽来改变顺序 */ - dragOrder?: boolean; /** 任务条右键菜单 */ menu?: { /** 右键菜单。代替原来的option.contextmenu */ From 0b9a38a4569fd7eb09b6f1b542b4bc6a17aba61f Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Tue, 19 Nov 2024 18:31:05 +0800 Subject: [PATCH 08/40] fix: when not left table columns render normally --- .../examples/gantt/gantt-noLeftListTable.ts | 22 ++-- packages/vtable-gantt/src/Gantt.ts | 100 +++++++++--------- .../vtable-gantt/src/state/state-manager.ts | 25 +++-- 3 files changed, 81 insertions(+), 66 deletions(-) diff --git a/packages/vtable-gantt/examples/gantt/gantt-noLeftListTable.ts b/packages/vtable-gantt/examples/gantt/gantt-noLeftListTable.ts index 357d557cd..a7926168f 100644 --- a/packages/vtable-gantt/examples/gantt/gantt-noLeftListTable.ts +++ b/packages/vtable-gantt/examples/gantt/gantt-noLeftListTable.ts @@ -931,18 +931,18 @@ export function createTable() { } } ], - rowSeriesNumber: { - title: '行号', - dragOrder: true, - headerStyle: { - bgColor: '#EEF1F5', + // rowSeriesNumber: { + // title: '行号', + // dragOrder: true, + // headerStyle: { + // bgColor: '#EEF1F5', - borderColor: '#e1e4e8' - }, - style: { - borderColor: '#e1e4e8' - } - }, + // borderColor: '#e1e4e8' + // }, + // style: { + // borderColor: '#e1e4e8' + // } + // }, scrollStyle: { visible: 'scrolling' }, diff --git a/packages/vtable-gantt/src/Gantt.ts b/packages/vtable-gantt/src/Gantt.ts index 5bdb34486..3aa1cc734 100644 --- a/packages/vtable-gantt/src/Gantt.ts +++ b/packages/vtable-gantt/src/Gantt.ts @@ -280,40 +280,40 @@ export class Gantt extends EventTarget { } } _generateListTable() { - // if (this.taskTableColumns.length >= 1 || this.options?.rowSeriesNumber) { - const listTableOption = this._generateListTableOptions(); - this.taskListTableInstance = new ListTable(this.container, listTableOption); - - if (this.options?.taskListTable?.tableWidth === 'auto' || this.taskTableWidth === -1) { - this.taskTableWidth = - this.taskListTableInstance.getAllColsWidth() + this.parsedOptions.outerFrameStyle.borderLineWidth; - if (this.options?.taskListTable?.maxTableWidth) { - this.taskTableWidth = Math.min(this.options?.taskListTable?.maxTableWidth, this.taskTableWidth); - } - if (this.options?.taskListTable?.minTableWidth) { - this.taskTableWidth = Math.max(this.options?.taskListTable?.minTableWidth, this.taskTableWidth); + if (this.taskTableColumns.length >= 1 || this.options?.rowSeriesNumber) { + const listTableOption = this._generateListTableOptions(); + this.taskListTableInstance = new ListTable(this.container, listTableOption); + + if (this.options?.taskListTable?.tableWidth === 'auto' || this.taskTableWidth === -1) { + this.taskTableWidth = + this.taskListTableInstance.getAllColsWidth() + this.parsedOptions.outerFrameStyle.borderLineWidth; + if (this.options?.taskListTable?.maxTableWidth) { + this.taskTableWidth = Math.min(this.options?.taskListTable?.maxTableWidth, this.taskTableWidth); + } + if (this.options?.taskListTable?.minTableWidth) { + this.taskTableWidth = Math.max(this.options?.taskListTable?.minTableWidth, this.taskTableWidth); + } + this.element.style.left = this.taskTableWidth ? `${this.taskTableWidth}px` : '0px'; + this.taskListTableInstance.setCanvasSize( + this.taskTableWidth, + this.tableNoFrameHeight + this.parsedOptions.outerFrameStyle.borderLineWidth * 2 + ); + this._updateSize(); } - this.element.style.left = this.taskTableWidth ? `${this.taskTableWidth}px` : '0px'; - this.taskListTableInstance.setCanvasSize( - this.taskTableWidth, - this.tableNoFrameHeight + this.parsedOptions.outerFrameStyle.borderLineWidth * 2 - ); - this._updateSize(); - } - if (this.taskListTableInstance.columnHeaderLevelCount > 1) { - if (this.taskListTableInstance.columnHeaderLevelCount === this.parsedOptions.timeLineHeaderRowHeights.length) { - for (let i = 0; i < this.taskListTableInstance.columnHeaderLevelCount; i++) { - this.taskListTableInstance.setRowHeight(i, this.parsedOptions.timeLineHeaderRowHeights[i]); - } - } else { - const newRowHeight = this.getAllHeaderRowsHeight() / this.taskListTableInstance.columnHeaderLevelCount; - for (let i = 0; i < this.taskListTableInstance.columnHeaderLevelCount; i++) { - this.taskListTableInstance.setRowHeight(i, newRowHeight); + if (this.taskListTableInstance.columnHeaderLevelCount > 1) { + if (this.taskListTableInstance.columnHeaderLevelCount === this.parsedOptions.timeLineHeaderRowHeights.length) { + for (let i = 0; i < this.taskListTableInstance.columnHeaderLevelCount; i++) { + this.taskListTableInstance.setRowHeight(i, this.parsedOptions.timeLineHeaderRowHeights[i]); + } + } else { + const newRowHeight = this.getAllHeaderRowsHeight() / this.taskListTableInstance.columnHeaderLevelCount; + for (let i = 0; i < this.taskListTableInstance.columnHeaderLevelCount; i++) { + this.taskListTableInstance.setRowHeight(i, newRowHeight); + } } } } - // } } _generateListTableOptions() { const listTable_options: ListTableConstructorOptions = {}; @@ -529,14 +529,6 @@ export class Gantt extends EventTarget { listTable_options.defaultRowHeight = this.options.rowHeight ?? 40; } listTable_options.clearDOM = false; - if (!listTable_options.rowSeriesNumber && !listTable_options.columns) { - // 左侧表格没有内容的时候 为了正常初始化ListTable 这里强制设置了序号列 - listTable_options.rowSeriesNumber = { - width: 0 - }; - this.parsedOptions.verticalSplitLineMoveable = false; - delete this.parsedOptions.verticalSplitLineHighlight; - } return listTable_options; } @@ -613,17 +605,25 @@ export class Gantt extends EventTarget { this.parsedOptions.colWidthPerDay = this.parsedOptions.timelineColWidth / colWidthIncludeDays; } getRowHeightByIndex(index: number) { - return this.taskListTableInstance.getRowHeight(index + this.taskListTableInstance.columnHeaderLevelCount); + if (this.taskListTableInstance) { + return this.taskListTableInstance.getRowHeight(index + this.taskListTableInstance.columnHeaderLevelCount); + } + return this.parsedOptions.rowHeight; } getRowsHeightByIndex(startIndex: number, endIndex: number) { - return this.taskListTableInstance.getRowsHeight( - startIndex + this.taskListTableInstance.columnHeaderLevelCount, - endIndex + this.taskListTableInstance.columnHeaderLevelCount - ); + if (this.taskListTableInstance) { + return this.taskListTableInstance.getRowsHeight( + startIndex + this.taskListTableInstance.columnHeaderLevelCount, + endIndex + this.taskListTableInstance.columnHeaderLevelCount + ); + } + return this.parsedOptions.rowHeight * (endIndex - startIndex + 1); } getAllRowsHeight() { - // return this.getAllHeaderRowsHeight() + this.itemCount * this.parsedOptions.rowHeight; - return this.taskListTableInstance.getAllRowsHeight(); + if (this.taskListTableInstance) { + return this.taskListTableInstance.getAllRowsHeight(); + } + return this.getAllHeaderRowsHeight() + this.itemCount * this.parsedOptions.rowHeight; } getAllHeaderRowsHeight() { // if (Array.isArray(this.parsedOptions.timeLineHeaderRowHeights)) { @@ -648,11 +648,13 @@ export class Gantt extends EventTarget { } getAllTaskBarsHeight() { - // return this.itemCount * this.parsedOptions.rowHeight; - return this.taskListTableInstance.getRowsHeight( - this.taskListTableInstance.columnHeaderLevelCount, - this.taskListTableInstance.rowCount - 1 - ); + if (this.taskListTableInstance) { + return this.taskListTableInstance.getRowsHeight( + this.taskListTableInstance.columnHeaderLevelCount, + this.taskListTableInstance.rowCount - 1 + ); + } + return this.itemCount * this.parsedOptions.rowHeight; } getTaskShowIndexByRecordIndex(index: number | number[]) { return this.taskListTableInstance.getBodyRowIndexByRecordIndex(index); diff --git a/packages/vtable-gantt/src/state/state-manager.ts b/packages/vtable-gantt/src/state/state-manager.ts index b27235c2c..53fe67581 100644 --- a/packages/vtable-gantt/src/state/state-manager.ts +++ b/packages/vtable-gantt/src/state/state-manager.ts @@ -40,6 +40,7 @@ export class StateManager { startX: number; startY: number; deltaX: number; + deltaY: number; targetStartX: number; startOffsetY: number; moving: boolean; @@ -103,6 +104,7 @@ export class StateManager { this.moveTaskBar = { targetStartX: null, deltaX: 0, + deltaY: 0, startOffsetY: null, startX: null, startY: null, @@ -371,9 +373,14 @@ export class StateManager { const x1 = this._gantt.eventManager.lastDragPointerXYOnWindow.x; const x2 = e.x; const dx = x2 - x1; + const y1 = this._gantt.eventManager.lastDragPointerXYOnWindow.y; + const y2 = e.y; + const dy = y2 - y1; + this.moveTaskBar.deltaX += dx; + this.moveTaskBar.deltaY += dy; // target.setAttribute('x', target.attribute.x + dx); - resizeOrMoveTaskBar(target, dx, null, this); + resizeOrMoveTaskBar(target, dx, dy, null, this); // 处理向左拖拽任务条时,整体向左滚动 if (target.attribute.x <= this._gantt.stateManager.scrollLeft && dx < 0) { @@ -382,7 +389,8 @@ export class StateManager { this.moveTaskBar.moveTaskBarXInertia.startInertia(this.moveTaskBar.moveTaskBarXSpeed, 0, 1); this.moveTaskBar.moveTaskBarXInertia.setScrollHandle((dx: number, dy: number) => { this.moveTaskBar.deltaX += dx; - resizeOrMoveTaskBar(target, dx, null, this); + this.moveTaskBar.deltaY += dy; + resizeOrMoveTaskBar(target, dx, dy, null, this); this._gantt.stateManager.setScrollLeft(target.attribute.x); if (this._gantt.stateManager.scrollLeft === 0) { @@ -400,7 +408,8 @@ export class StateManager { this.moveTaskBar.moveTaskBarXInertia.startInertia(this.moveTaskBar.moveTaskBarXSpeed, 0, 1); this.moveTaskBar.moveTaskBarXInertia.setScrollHandle((dx: number, dy: number) => { this.moveTaskBar.deltaX += dx; - resizeOrMoveTaskBar(target, dx, null, this); + this.moveTaskBar.deltaY += dy; + resizeOrMoveTaskBar(target, dx, dy, null, this); this._gantt.stateManager.setScrollLeft( target.attribute.x + target.attribute.width - this._gantt.tableNoFrameWidth @@ -462,13 +471,13 @@ export class StateManager { if (direction === 'left') { // taskBarGroup.setAttribute('x', targetEndX); // taskBarGroup.setAttribute('width', taskBarSize); - resizeOrMoveTaskBar(taskBarGroup, targetEndX - taskBarGroup.attribute.x, taskBarSize, this); + resizeOrMoveTaskBar(taskBarGroup, targetEndX - taskBarGroup.attribute.x, 0, taskBarSize, this); rect?.setAttribute('width', taskBarGroup.attribute.width); progressRect?.setAttribute('width', (progress / 100) * taskBarGroup.attribute.width); this._gantt._updateDateToTaskRecord('start-move', -diff_days, taskIndex, sub_task_index); } else if (direction === 'right') { // taskBarGroup.setAttribute('width', taskBarSize); - resizeOrMoveTaskBar(taskBarGroup, 0, taskBarSize, this); + resizeOrMoveTaskBar(taskBarGroup, 0, 0, taskBarSize, this); rect?.setAttribute('width', taskBarGroup.attribute.width); progressRect?.setAttribute('width', (progress / 100) * taskBarGroup.attribute.width); this._gantt._updateDateToTaskRecord('end-move', diff_days, taskIndex, sub_task_index); @@ -523,6 +532,7 @@ export class StateManager { resizeOrMoveTaskBar( taskBarGroup, this._gantt.stateManager.resizeTaskBar.onIconName === 'left' ? -diffWidth : 0, + 0, taskBarSize, this ); @@ -810,7 +820,7 @@ function reCreateCustomNode(gantt: Gantt, taskBarGroup: Group, taskIndex: number } } -function resizeOrMoveTaskBar(target: GanttTaskBarNode, dx: number, newWidth: number, state: StateManager) { +function resizeOrMoveTaskBar(target: GanttTaskBarNode, dx: number, dy: number, newWidth: number, state: StateManager) { // const taskIndex = getTaskIndexByY(state.moveTaskBar.startOffsetY, state._gantt); const taskIndex = target.task_index; const sub_task_index = target.sub_task_index; @@ -818,6 +828,9 @@ function resizeOrMoveTaskBar(target: GanttTaskBarNode, dx: number, newWidth: num if (dx) { target.setAttribute('x', target.attribute.x + dx); } + if (dy) { + target.setAttribute('y', target.attribute.y + dy); + } if (newWidth) { target.setAttribute('width', newWidth); } From b2b73e3d8a83f4bc09b7cb30853d042a56fe3142 Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Wed, 20 Nov 2024 20:12:11 +0800 Subject: [PATCH 09/40] feat: show dependencyline in multiple task lines --- .../examples/gantt/gantt-Sub_Tasks.ts | 2 +- .../gantt/gantt-customLayout-Sub_Tasks.ts | 391 +++++++++++++++++ .../gantt-customLayout-Sub_Tasks_Inline.ts | 407 ++++++++++++++++++ packages/vtable-gantt/examples/menu.ts | 8 + packages/vtable-gantt/src/Gantt.ts | 25 +- .../vtable-gantt/src/event/event-manager.ts | 7 +- packages/vtable-gantt/src/gantt-helper.ts | 14 + .../src/scenegraph/dependency-link.ts | 80 ++-- .../vtable-gantt/src/scenegraph/task-bar.ts | 21 +- .../vtable-gantt/src/state/state-manager.ts | 126 ++++-- 10 files changed, 1005 insertions(+), 76 deletions(-) create mode 100644 packages/vtable-gantt/examples/gantt/gantt-customLayout-Sub_Tasks.ts create mode 100644 packages/vtable-gantt/examples/gantt/gantt-customLayout-Sub_Tasks_Inline.ts diff --git a/packages/vtable-gantt/examples/gantt/gantt-Sub_Tasks.ts b/packages/vtable-gantt/examples/gantt/gantt-Sub_Tasks.ts index f32a21df3..579d295ee 100644 --- a/packages/vtable-gantt/examples/gantt/gantt-Sub_Tasks.ts +++ b/packages/vtable-gantt/examples/gantt/gantt-Sub_Tasks.ts @@ -324,7 +324,7 @@ export function createTable() { linkCreatable: true, links: [ { - type: DependencyType.StartToFinish, + type: DependencyType.FinishToFinish, linkedFromTaskKey: 3, linkedToTaskKey: 2 } diff --git a/packages/vtable-gantt/examples/gantt/gantt-customLayout-Sub_Tasks.ts b/packages/vtable-gantt/examples/gantt/gantt-customLayout-Sub_Tasks.ts new file mode 100644 index 000000000..09137e42f --- /dev/null +++ b/packages/vtable-gantt/examples/gantt/gantt-customLayout-Sub_Tasks.ts @@ -0,0 +1,391 @@ +import type { ColumnsDefine } from '@visactor/vtable'; +import { register } from '@visactor/vtable'; +import { DateInputEditor, InputEditor } from '@visactor/vtable-editors'; +import type { GanttConstructorOptions, TYPES } from '../../src/index'; +import { Group, Image, Text } from '@visactor/vtable/es/vrender'; +import { Gantt } from '../../src/index'; +import { bindDebugTool } from '../../../vtable/src/scenegraph/debug-tool'; +import { scale } from '@visactor/vutils'; +import { DependencyType, ShowHierarchyMode } from '../../src/ts-types'; +const CONTAINER_ID = 'vTable'; + +const barColors0 = ['#aecde6', '#c6a49a', '#ffb582', '#eec1de', '#b3d9b3', '#d9d1a5', '#cccccc', '#e59a9c', '#c9bede']; +const barColors = ['#1f77b4', '#8c564b', '#ff7f0e', '#e377c2', '#2ca02c', '#bcbd22', '#7f7f7f', '#d62728', '#9467bd']; +export function createTable() { + const records = [ + { + title: 'Planning', + children: [ + { + id: 1, + name: 'Emily', + start: '2024-11-17', + end: '2024-11-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 2, + name: 'Michael Smith', + start: '2024-11-19', + end: '2024-11-20', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + } + ] + }, + { + id: 300, + title: 'Research', + children: [ + { + id: 3, + name: 'Ryan', + start: '2024-11-18', + end: '2024-11-21', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/rabbit.jpg' + } + ] + }, + { + title: 'Goal Setting', + children: [ + { + id: 4, + name: 'Daniel Davis', + start: '2024-11-21', + end: '2024-11-22', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/cat.jpg' + }, + { + id: 5, + name: 'Lauren', + start: '2024-11-18', + end: '2024-11-19', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg' + } + ] + }, + + { + title: 'Strategy', + children: [ + { + id: 6, + name: 'Sarah Miller', + start: '2024-11-20', + end: '2024-11-21', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 7, + name: 'Matthew Wilson', + start: '2024-11-22', + end: '2024-11-25', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + } + ] + }, + { + title: 'Execution', + children: [ + { + id: 8, + name: 'Ashley Taylor', + start: '2024-11-22', + end: '2024-11-25', + + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/rabbit.jpg' + }, + { + id: 9, + name: 'Megan', + start: '2024-11-27', + end: '2024-11-30', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/cat.jpg' + }, + { + id: 10, + name: 'David', + start: '2024-12-10', + end: '2024-12-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg' + } + ] + }, + { + title: 'Monitoring', + children: [ + { + id: 11, + name: 'Hannah', + start: '2024-11-20', + end: '2024-11-30', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 12, + name: 'Andrew', + start: '2024-12-02', + end: '2024-12-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + } + ] + }, + { + title: 'Reporting', + children: [ + { + id: 13, + name: 'Joshua Anderson', + start: '2024-12-22', + end: '2024-12-28', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/rabbit.jpg' + } + ] + }, + { + title: 'Process review', + children: [ + { + id: 14, + name: 'Christopher Moore', + start: '2024-11-25', + end: '2024-11-30', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/cat.jpg' + }, + { + id: 15, + name: 'Emma', + start: '2024-12-01', + end: '2024-12-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg' + } + ] + } + ]; + + const columns: ColumnsDefine = [ + { + field: 'title', + title: 'PROCESS', + width: 150 + } + ]; + const option: GanttConstructorOptions = { + records, + taskListTable: { + columns: columns, + minTableWidth: 100, + hierarchyExpandLevel: 5, + menu: { + contextMenuItems: ['copy', 'paste', 'delete', '...'] + } + }, + groupBy: true, + showHierarchyMode: ShowHierarchyMode.Sub_Tasks, + frame: { + outerFrameStyle: { + borderLineWidth: 1, + borderColor: '#e1e4e8', + cornerRadius: 8 + }, + verticalSplitLineMoveable: false + }, + grid: { + horizontalLine: { + lineWidth: 1, + lineColor: '#e1e4e8' + }, + verticalLine: { + lineWidth: 1, + lineColor: '#e1e4e8' + } + }, + headerRowHeight: 60, + rowHeight: 80, + taskBar: { + startDateField: 'start', + endDateField: 'end', + progressField: 'progress', + labelText: '{name}', + labelTextStyle: { + fontFamily: 'Arial', + fontSize: 16, + textAlign: 'left' + }, + barStyle: { + width: 50, + /** 任务条的颜色 */ + barColor: '#ee8800', + /** 已完成部分任务条的颜色 */ + completedBarColor: '#91e8e0', + /** 任务条的圆角 */ + cornerRadius: 25 + }, + customLayout: (args: any) => { + const colorLength = barColors.length; + const { width, height, index, startDate, endDate, taskDays, progress, taskRecord, ganttInstance } = args; + const container = new Group({ + width, + height, + fill: { + gradient: 'linear', + x0: 0, + y0: 0, + x1: 1, + y1: 0, + stops: [ + { + offset: 0, + color: barColors0[index % colorLength] + }, + { + offset: 0.5, + color: barColors[index % colorLength] + }, + { + offset: 1, + color: barColors0[index % colorLength] + } + ] + }, + display: 'flex', + flexDirection: 'row', + flexWrap: 'nowrap' + }); + const containerLeft = new Group({ + height, + width: 60, + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'space-around' + // fill: 'red' + }); + container.add(containerLeft); + + const icon0 = new Image({ + width: 40, + height: 40, + image: taskRecord.avatar, + cornerRadius: 20 + }); + containerLeft.add(icon0); + + const containerRight = new Group({ + height, + width: width - 60, + display: 'flex', + flexDirection: 'column' + // alignItems: 'left' + }); + container.add(containerRight); + + const bloggerName = new Text({ + text: taskRecord.name, + fontSize: 16, + fontFamily: 'sans-serif', + fill: 'white', + maxLineWidth: width - 60, + boundsPadding: [10, 0, 0, 0] + }); + containerRight.add(bloggerName); + + const days = new Text({ + text: `${taskDays}天`, + fontSize: 13, + fontFamily: 'sans-serif', + fill: 'white', + boundsPadding: [10, 0, 0, 0] + }); + containerRight.add(days); + return { + rootContainer: container + // renderDefaultBar: true + // renderDefaultText: true + }; + } + }, + dependency: { + linkCreatable: true, + links: [ + { + type: DependencyType.StartToStart, + linkedFromTaskKey: 1, + linkedToTaskKey: 2 + }, + { + type: DependencyType.FinishToStart, + linkedFromTaskKey: 2, + linkedToTaskKey: 3 + }, + { + type: DependencyType.StartToStart, + linkedFromTaskKey: 3, + linkedToTaskKey: 5 + }, + { + type: DependencyType.FinishToFinish, + linkedFromTaskKey: 5, + linkedToTaskKey: 4 + }, + { + type: DependencyType.StartToStart, + linkedFromTaskKey: 8, + linkedToTaskKey: 9 + }, + { + type: DependencyType.FinishToStart, + linkedFromTaskKey: 9, + linkedToTaskKey: 10 + } + ] + }, + timelineHeader: { + verticalLine: { + lineColor: '#e1e4e8', + lineWidth: 1 + }, + horizontalLine: { + lineColor: '#e1e4e8', + lineWidth: 1 + }, + backgroundColor: '#63a8ff', + scales: [ + { + unit: 'day', + step: 1, + format(date: TYPES.DateFormatArgumentType) { + return date.dateIndex.toString(); + }, + style: { + fontSize: 20, + fontWeight: 'bold', + color: 'red' + } + } + ] + }, + minDate: '2024-11-17', + maxDate: '2024-12-31', + scrollStyle: { + scrollRailColor: 'RGBA(246,246,246,0.5)', + visible: 'none', + width: 6, + scrollSliderCornerRadius: 2, + scrollSliderColor: '#5cb85c' + } + }; + + const ganttInstance = new Gantt(document.getElementById(CONTAINER_ID)!, option); + window.ganttInstance = ganttInstance; + ganttInstance.on('scroll', e => { + console.log('scroll', e); + }); + + ganttInstance.listTableInstance?.on('scroll', e => { + console.log('listTable scroll', e); + }); + // bindDebugTool(ganttInstance.scenegraph.stage as any, { + // customGrapicKeys: ['role', '_updateTag'] + // }); +} diff --git a/packages/vtable-gantt/examples/gantt/gantt-customLayout-Sub_Tasks_Inline.ts b/packages/vtable-gantt/examples/gantt/gantt-customLayout-Sub_Tasks_Inline.ts new file mode 100644 index 000000000..9eb087ebb --- /dev/null +++ b/packages/vtable-gantt/examples/gantt/gantt-customLayout-Sub_Tasks_Inline.ts @@ -0,0 +1,407 @@ +import type { ColumnsDefine } from '@visactor/vtable'; +import { register } from '@visactor/vtable'; +import { DateInputEditor, InputEditor } from '@visactor/vtable-editors'; +import type { GanttConstructorOptions, TYPES } from '../../src/index'; +import { Group, Image, Text } from '@visactor/vtable/es/vrender'; +import { Gantt } from '../../src/index'; +import { bindDebugTool } from '../../../vtable/src/scenegraph/debug-tool'; +import { scale } from '@visactor/vutils'; +import { DependencyType, ShowHierarchyMode } from '../../src/ts-types'; +const CONTAINER_ID = 'vTable'; + +const barColors0 = ['#aecde6', '#c6a49a', '#ffb582', '#eec1de', '#b3d9b3', '#d9d1a5', '#cccccc', '#e59a9c', '#c9bede']; +const barColors = ['#1f77b4', '#8c564b', '#ff7f0e', '#e377c2', '#2ca02c', '#bcbd22', '#7f7f7f', '#d62728', '#9467bd']; +export function createTable() { + const records = [ + { + title: 'Planning', + children: [ + { + id: 1, + name: 'Emily', + start: '2024-11-17', + end: '2024-11-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 2, + name: 'Michael Smith', + start: '2024-11-19', + end: '2024-11-20', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + } + ] + }, + { + id: 300, + title: 'Research', + children: [ + { + id: 3, + name: 'Ryan', + start: '2024-11-18', + end: '2024-11-21', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/rabbit.jpg' + } + ] + }, + { + title: 'Goal Setting', + children: [ + { + id: 4, + name: 'Daniel Davis', + start: '2024-11-21', + end: '2024-11-22', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/cat.jpg' + }, + { + id: 5, + name: 'Lauren', + start: '2024-11-18', + end: '2024-11-19', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg' + } + ] + }, + + { + title: 'Strategy', + children: [ + { + id: 6, + name: 'Sarah Miller', + start: '2024-11-20', + end: '2024-11-21', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 7, + name: 'Matthew Wilson', + start: '2024-11-22', + end: '2024-11-25', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + } + ] + }, + { + title: 'Execution', + children: [ + { + id: 8, + name: 'Ashley Taylor', + start: '2024-11-22', + end: '2024-11-25', + + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/rabbit.jpg' + }, + { + id: 9, + name: 'Megan', + start: '2024-11-27', + end: '2024-11-30', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/cat.jpg' + }, + { + id: 10, + name: 'David', + start: '2024-12-10', + end: '2024-12-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg' + } + ] + }, + { + title: 'Monitoring', + children: [ + { + id: 11, + name: 'Hannah', + start: '2024-11-20', + end: '2024-11-30', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 12, + name: 'Andrew', + start: '2024-12-02', + end: '2024-12-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + } + ] + }, + { + title: 'Reporting', + children: [ + { + id: 13, + name: 'Joshua Anderson', + start: '2024-12-22', + end: '2024-12-28', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/rabbit.jpg' + } + ] + }, + { + title: 'Process review', + children: [ + { + id: 14, + name: 'Christopher Moore', + start: '2024-11-25', + end: '2024-11-30', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/cat.jpg' + }, + { + id: 15, + name: 'Emma', + start: '2024-12-01', + end: '2024-12-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg' + } + ] + } + ]; + + const columns: ColumnsDefine = [ + { + field: 'title', + title: 'PROCESS', + width: 150 + } + ]; + const option: GanttConstructorOptions = { + records, + taskListTable: { + columns: columns, + minTableWidth: 100, + hierarchyExpandLevel: 5, + menu: { + contextMenuItems: ['copy', 'paste', 'delete', '...'] + } + }, + groupBy: true, + showHierarchyMode: ShowHierarchyMode.Sub_Tasks_Inline, + frame: { + outerFrameStyle: { + borderLineWidth: 1, + borderColor: '#e1e4e8', + cornerRadius: 8 + }, + verticalSplitLineMoveable: false + }, + grid: { + horizontalLine: { + lineWidth: 1, + lineColor: '#e1e4e8' + }, + verticalLine: { + lineWidth: 1, + lineColor: '#e1e4e8' + } + }, + headerRowHeight: 60, + rowHeight: 80, + taskBar: { + startDateField: 'start', + endDateField: 'end', + progressField: 'progress', + labelText: '{name}', + labelTextStyle: { + fontFamily: 'Arial', + fontSize: 16, + textAlign: 'left' + }, + barStyle: { + width: 50, + /** 任务条的颜色 */ + barColor: '#ee8800', + /** 已完成部分任务条的颜色 */ + completedBarColor: '#91e8e0', + /** 任务条的圆角 */ + cornerRadius: 25 + }, + customLayout: (args: any) => { + const colorLength = barColors.length; + const { width, height, index, startDate, endDate, taskDays, progress, taskRecord, ganttInstance } = args; + const container = new Group({ + width, + height, + fill: { + gradient: 'linear', + x0: 0, + y0: 0, + x1: 1, + y1: 0, + stops: [ + { + offset: 0, + color: barColors0[index % colorLength] + }, + { + offset: 0.5, + color: barColors[index % colorLength] + }, + { + offset: 1, + color: barColors0[index % colorLength] + } + ] + }, + display: 'flex', + flexDirection: 'row', + flexWrap: 'nowrap' + }); + const containerLeft = new Group({ + height, + width: 60, + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'space-around' + // fill: 'red' + }); + container.add(containerLeft); + + const icon0 = new Image({ + width: 40, + height: 40, + image: taskRecord.avatar, + cornerRadius: 20 + }); + containerLeft.add(icon0); + + const containerRight = new Group({ + height, + width: width - 60, + display: 'flex', + flexDirection: 'column' + // alignItems: 'left' + }); + container.add(containerRight); + + const bloggerName = new Text({ + text: taskRecord.name, + fontSize: 16, + fontFamily: 'sans-serif', + fill: 'white', + maxLineWidth: width - 60, + boundsPadding: [10, 0, 0, 0] + }); + containerRight.add(bloggerName); + + const days = new Text({ + text: `${taskDays}天`, + fontSize: 13, + fontFamily: 'sans-serif', + fill: 'white', + boundsPadding: [10, 0, 0, 0] + }); + containerRight.add(days); + return { + rootContainer: container + // renderDefaultBar: true + // renderDefaultText: true + }; + } + }, + dependency: { + linkCreatable: true, + links: [ + { + type: DependencyType.FinishToStart, + linkedFromTaskKey: 2, + linkedToTaskKey: 3 + }, + { + type: DependencyType.StartToStart, + linkedFromTaskKey: 3, + linkedToTaskKey: 5 + }, + { + type: DependencyType.FinishToFinish, + linkedFromTaskKey: 5, + linkedToTaskKey: 4 + }, + { + type: DependencyType.StartToStart, + linkedFromTaskKey: 8, + linkedToTaskKey: 9 + }, + { + type: DependencyType.FinishToStart, + linkedFromTaskKey: 9, + linkedToTaskKey: 10 + } + ] + }, + timelineHeader: { + verticalLine: { + lineColor: '#e1e4e8', + lineWidth: 1 + }, + horizontalLine: { + lineColor: '#e1e4e8', + lineWidth: 1 + }, + backgroundColor: '#63a8ff', + scales: [ + { + unit: 'day', + step: 1, + format(date: TYPES.DateFormatArgumentType) { + return date.dateIndex.toString(); + }, + style: { + fontSize: 20, + fontWeight: 'bold', + color: 'red' + } + } + // { + // unit: 'quarter', + // step: 1, + // format(date: TYPES.DateFormatArgumentType) { + // return '第' + date.index + '季度'; + // } + // } + ] + }, + minDate: '2024-11-17', + maxDate: '2024-12-31', + scrollStyle: { + scrollRailColor: 'RGBA(246,246,246,0.5)', + visible: 'none', + width: 6, + scrollSliderCornerRadius: 2, + scrollSliderColor: '#5cb85c' + } + }; + // columns:[ + // { + // title:'2024-07', + // columns:[ + // { + // title:'01' + // }, + // { + // title:'02' + // }, + // ... + // ] + // }, + // ... + // ] + const ganttInstance = new Gantt(document.getElementById(CONTAINER_ID)!, option); + window.ganttInstance = ganttInstance; + ganttInstance.on('scroll', e => { + console.log('scroll', e); + }); + + ganttInstance.listTableInstance?.on('scroll', e => { + console.log('listTable scroll', e); + }); + // bindDebugTool(ganttInstance.scenegraph.stage as any, { + // customGrapicKeys: ['role', '_updateTag'] + // }); +} diff --git a/packages/vtable-gantt/examples/menu.ts b/packages/vtable-gantt/examples/menu.ts index b91571086..358f289d1 100644 --- a/packages/vtable-gantt/examples/menu.ts +++ b/packages/vtable-gantt/examples/menu.ts @@ -74,6 +74,14 @@ export const menus = [ path: 'gantt', name: 'gantt-Sub_Tasks_Inline' }, + { + path: 'gantt', + name: 'gantt-customLayout-Sub_Tasks_Inline' + }, + { + path: 'gantt', + name: 'gantt-customLayout-Sub_Tasks' + }, { path: 'gantt', name: 'gantt-Sub_Tasks' diff --git a/packages/vtable-gantt/src/Gantt.ts b/packages/vtable-gantt/src/Gantt.ts index 3aa1cc734..fdde5e753 100644 --- a/packages/vtable-gantt/src/Gantt.ts +++ b/packages/vtable-gantt/src/Gantt.ts @@ -522,7 +522,7 @@ export class Gantt extends EventTarget { listTable_options.customComputeRowHeight = (args: { row: number; table: ListTable }) => { const { row, table } = args; const record = table.getRecordByRowCol(0, row); - return (record.children?.length ?? 1) * this.parsedOptions.rowHeight; + return (record.children?.length || 1) * this.parsedOptions.rowHeight; }; listTable_options.defaultRowHeight = 'auto'; } else { @@ -831,6 +831,29 @@ export class Gantt extends EventTarget { this._updateRecordToListTable(taskRecord, index); } } + + /** + * 拖拽任务条或者调整任务条尺寸修改日期更新到数据中 + * @param updateDateType + * @param days + * @param index + */ + _dragOrderTaskRecord( + source_index: number, + source_sub_task_index: number, + target_index: number, + target_sub_task_index: number + ) { + // const source_taskRecord = this.getRecordByIndex(source_index, source_sub_task_index); + if (isValid(source_sub_task_index) && isValid(target_sub_task_index)) { + const sub_task_record = this.records[source_index].children[source_sub_task_index]; + this.records[source_index].children.splice(source_sub_task_index, 1); + if (!this.records[target_index].children) { + this.records[target_index].children = []; + } + this.records[target_index].children.splice(target_sub_task_index, 0, sub_task_record); + } + } /** 目前不支持树形tree的情况更新单条数据 需要的话目前可以setRecords。 */ updateTaskRecord(record: any, index: number) { //const taskRecord = this.getRecordByIndex(index); diff --git a/packages/vtable-gantt/src/event/event-manager.ts b/packages/vtable-gantt/src/event/event-manager.ts index dc4270331..1d1d9299a 100644 --- a/packages/vtable-gantt/src/event/event-manager.ts +++ b/packages/vtable-gantt/src/event/event-manager.ts @@ -305,8 +305,7 @@ function bindTableGroupListener(event: EventManager) { }); if (isClickBar && scene._gantt.parsedOptions.taskBarSelectable && event.poniterState === 'down') { stateManager.hideDependencyLinkSelectedLine(); - scene._gantt.stateManager.selectedTaskBar.target = taskBarTarget as any as GanttTaskBarNode; - stateManager.showTaskBarSelectedBorder(); + stateManager.showTaskBarSelectedBorder(taskBarTarget); if (gantt.hasListeners(GANTT_EVENT_TYPE.CLICK_TASK_BAR)) { // const taskIndex = getTaskIndexByY(e.offset.y, gantt); const taskIndex = taskBarTarget.task_index; @@ -488,8 +487,8 @@ function bindContainerDomListener(eventManager: EventManager) { } if (stateManager.interactionState === InteractionState.grabing && gantt.eventManager.poniterState === 'draging') { const lastX = gantt.eventManager.lastDragPointerXYOnWindow?.x ?? e.x; - // const lastY = gantt.eventManager.lastDragPointerXYOnWindow?.y ?? e.y; - if (Math.abs(lastX - e.x) >= 1) { + const lastY = gantt.eventManager.lastDragPointerXYOnWindow?.y ?? e.y; + if (Math.abs(lastX - e.x) >= 1 || Math.abs(lastY - e.y) >= 1) { if (stateManager.isResizingTableWidth()) { stateManager.hideDependencyLinkSelectedLine(); stateManager.hideTaskBarSelectedBorder(); diff --git a/packages/vtable-gantt/src/gantt-helper.ts b/packages/vtable-gantt/src/gantt-helper.ts index 9e00048b9..6fdaa1473 100644 --- a/packages/vtable-gantt/src/gantt-helper.ts +++ b/packages/vtable-gantt/src/gantt-helper.ts @@ -640,3 +640,17 @@ export function findRecordByTaskKey( } } } + +export function getTaskIndexsByTaskY(y: number, gantt: Gantt) { + let task_index; + let sub_task_index; + if (gantt.taskListTableInstance) { + const { row } = gantt.taskListTableInstance.getTargetRowAt(y + gantt.headerHeight); + task_index = row - gantt.taskListTableInstance.columnHeaderLevelCount; + const beforeRowsHeight = gantt.getRowsHeightByIndex(0, task_index - 1); // 耦合了listTableOption的customComputeRowHeight + sub_task_index = Math.floor((y - beforeRowsHeight) / gantt.parsedOptions.rowHeight); + } else { + task_index = Math.floor(y / gantt.parsedOptions.rowHeight); + } + return { task_index, sub_task_index }; +} diff --git a/packages/vtable-gantt/src/scenegraph/dependency-link.ts b/packages/vtable-gantt/src/scenegraph/dependency-link.ts index 9bf52c374..e21f5662f 100644 --- a/packages/vtable-gantt/src/scenegraph/dependency-link.ts +++ b/packages/vtable-gantt/src/scenegraph/dependency-link.ts @@ -514,14 +514,34 @@ export function generateLinkLinePoints( return { linePoints, arrowPoints }; } +/** + * + * @param type 依赖关联类型 + * @param linkedFromTaskStartDate 关联源任务的开始时间 + * @param linkedFromTaskEndDate 关联源任务的结束时间 + * @param linkedFromTaskRecordRowIndex 关联源任务所在的行索引 + * @param fromNodeDiffY 关联源任务的偏移量,如果在拖拽过程中,会有偏移量 + * @param linkedToTaskStartDate 关联目标任务的开始时间 + * @param linkedToTaskEndDate 关联目标任务的结束时间 + * @param linkedToTaskRecordRowIndex 关联目标任务所在的行索引 + * @param toNodeDiffY 关联目标任务的偏移量,如果在拖拽过程中,会有偏移量 + * @param minDate 甘特图设置的最开始时间 + * @param rowHeight 单个任务条占用的行高 + * @param colWidthPerDay 单个日期占用的列宽 + * @param linkedFromMovedTaskBarNode 关联源任务的任务场景树节点 + * @param linkedToMovedTaskBarNode 关联目标任务的任务场景树节点 + * @returns + */ export function updateLinkLinePoints( type: DependencyType, linkedFromTaskStartDate: Date, linkedFromTaskEndDate: Date, linkedFromTaskRecordRowIndex: number, + fromNodeDiffY: number, linkedToTaskStartDate: Date, linkedToTaskEndDate: Date, linkedToTaskRecordRowIndex: number, + toNodeDiffY: number, minDate: Date, rowHeight: number, colWidthPerDay: number, @@ -548,37 +568,39 @@ export function updateLinkLinePoints( linePoints = [ { x: linkFromPointX, - y: rowHeight * (linkedFromTaskRecordRowIndex + 0.5) + y: rowHeight * (linkedFromTaskRecordRowIndex + 0.5) + fromNodeDiffY }, { x: linkFromPointX + distanceToTaskBar, - y: rowHeight * (linkedFromTaskRecordRowIndex + 0.5) + y: rowHeight * (linkedFromTaskRecordRowIndex + 0.5) + fromNodeDiffY }, { x: linkFromPointX + distanceToTaskBar, y: rowHeight * - (linkedFromTaskRecordRowIndex + (linkedFromTaskRecordRowIndex > linkedToTaskRecordRowIndex ? 0 : 1)) + (linkedFromTaskRecordRowIndex + (linkedFromTaskRecordRowIndex > linkedToTaskRecordRowIndex ? 0 : 1)) + + fromNodeDiffY }, { x: linkToPointX - distanceToTaskBar, y: rowHeight * - (linkedFromTaskRecordRowIndex + (linkedFromTaskRecordRowIndex > linkedToTaskRecordRowIndex ? 0 : 1)) + (linkedFromTaskRecordRowIndex + (linkedFromTaskRecordRowIndex > linkedToTaskRecordRowIndex ? 0 : 1)) + + fromNodeDiffY }, { x: linkToPointX - distanceToTaskBar, - y: rowHeight * (linkedToTaskRecordRowIndex + 0.5) + y: rowHeight * (linkedToTaskRecordRowIndex + 0.5) + toNodeDiffY }, { x: linkToPointX, - y: rowHeight * (linkedToTaskRecordRowIndex + 0.5) + y: rowHeight * (linkedToTaskRecordRowIndex + 0.5) + toNodeDiffY } ]; if (linkFromPointX + distanceToTaskBar <= linkToPointX - distanceToTaskBar) { linePoints.splice(2, 3, { x: linkFromPointX + distanceToTaskBar, - y: rowHeight * (linkedToTaskRecordRowIndex + 0.5) + y: rowHeight * (linkedToTaskRecordRowIndex + 0.5) + toNodeDiffY }); } const lastPoint = linePoints[linePoints.length - 1]; @@ -613,37 +635,39 @@ export function updateLinkLinePoints( linePoints = [ { x: linkFromPointX, - y: rowHeight * (linkedFromTaskRecordRowIndex + 0.5) + y: rowHeight * (linkedFromTaskRecordRowIndex + 0.5) + fromNodeDiffY }, { x: linkFromPointX - distanceToTaskBar, - y: rowHeight * (linkedFromTaskRecordRowIndex + 0.5) + y: rowHeight * (linkedFromTaskRecordRowIndex + 0.5) + fromNodeDiffY }, { x: linkFromPointX - distanceToTaskBar, y: rowHeight * - (linkedFromTaskRecordRowIndex + (linkedFromTaskRecordRowIndex > linkedToTaskRecordRowIndex ? 0 : 1)) + (linkedFromTaskRecordRowIndex + (linkedFromTaskRecordRowIndex > linkedToTaskRecordRowIndex ? 0 : 1)) + + fromNodeDiffY }, { x: linkToPointX + distanceToTaskBar, y: rowHeight * - (linkedFromTaskRecordRowIndex + (linkedFromTaskRecordRowIndex > linkedToTaskRecordRowIndex ? 0 : 1)) + (linkedFromTaskRecordRowIndex + (linkedFromTaskRecordRowIndex > linkedToTaskRecordRowIndex ? 0 : 1)) + + fromNodeDiffY }, { x: linkToPointX + distanceToTaskBar, - y: rowHeight * (linkedToTaskRecordRowIndex + 0.5) + y: rowHeight * (linkedToTaskRecordRowIndex + 0.5) + toNodeDiffY }, { x: linkToPointX, - y: rowHeight * (linkedToTaskRecordRowIndex + 0.5) + y: rowHeight * (linkedToTaskRecordRowIndex + 0.5) + toNodeDiffY } ]; if (linkFromPointX - distanceToTaskBar >= linkToPointX + distanceToTaskBar) { linePoints.splice(2, 3, { x: linkFromPointX - distanceToTaskBar, - y: rowHeight * (linkedToTaskRecordRowIndex + 0.5) + y: rowHeight * (linkedToTaskRecordRowIndex + 0.5) + toNodeDiffY }); } const lastPoint = linePoints[linePoints.length - 1]; @@ -677,14 +701,14 @@ export function updateLinkLinePoints( linePoints = [ { x: linkFromPointX, - y: rowHeight * (linkedFromTaskRecordRowIndex + 0.5) + y: rowHeight * (linkedFromTaskRecordRowIndex + 0.5) + fromNodeDiffY }, { x: (linkedFromTaskRecordRowIndex === linkedToTaskRecordRowIndex ? linkFromPointX : Math.min(linkFromPointX, linkToPointX)) - distanceToTaskBar, - y: rowHeight * (linkedFromTaskRecordRowIndex + 0.5) + y: rowHeight * (linkedFromTaskRecordRowIndex + 0.5) + fromNodeDiffY }, { x: @@ -693,24 +717,26 @@ export function updateLinkLinePoints( : Math.min(linkFromPointX, linkToPointX)) - distanceToTaskBar, y: rowHeight * - (linkedToTaskRecordRowIndex + (linkedFromTaskRecordRowIndex === linkedToTaskRecordRowIndex ? 1 : 0.5)) + (linkedToTaskRecordRowIndex + (linkedFromTaskRecordRowIndex === linkedToTaskRecordRowIndex ? 1 : 0.5)) + + toNodeDiffY }, { x: linkToPointX - distanceToTaskBar, y: rowHeight * - (linkedToTaskRecordRowIndex + (linkedFromTaskRecordRowIndex === linkedToTaskRecordRowIndex ? 1 : 0.5)) + (linkedToTaskRecordRowIndex + (linkedFromTaskRecordRowIndex === linkedToTaskRecordRowIndex ? 1 : 0.5)) + + toNodeDiffY }, { x: (linkedFromTaskRecordRowIndex === linkedToTaskRecordRowIndex ? linkToPointX : Math.min(linkFromPointX, linkToPointX)) - distanceToTaskBar, - y: rowHeight * (linkedToTaskRecordRowIndex + 0.5) + y: rowHeight * (linkedToTaskRecordRowIndex + 0.5) + toNodeDiffY }, { x: linkToPointX, - y: rowHeight * (linkedToTaskRecordRowIndex + 0.5) + y: rowHeight * (linkedToTaskRecordRowIndex + 0.5) + toNodeDiffY } ]; const lastPoint = linePoints[linePoints.length - 1]; @@ -744,14 +770,14 @@ export function updateLinkLinePoints( linePoints = [ { x: linkFromPointX, - y: rowHeight * (linkedFromTaskRecordRowIndex + 0.5) + y: rowHeight * (linkedFromTaskRecordRowIndex + 0.5) + fromNodeDiffY }, { x: (linkedFromTaskRecordRowIndex === linkedToTaskRecordRowIndex ? linkFromPointX : Math.max(linkFromPointX, linkToPointX)) + distanceToTaskBar, - y: rowHeight * (linkedFromTaskRecordRowIndex + 0.5) + y: rowHeight * (linkedFromTaskRecordRowIndex + 0.5) + fromNodeDiffY }, { x: @@ -760,21 +786,23 @@ export function updateLinkLinePoints( : Math.max(linkFromPointX, linkToPointX)) + distanceToTaskBar, y: rowHeight * - (linkedToTaskRecordRowIndex + (linkedFromTaskRecordRowIndex === linkedToTaskRecordRowIndex ? 1 : 0.5)) + (linkedToTaskRecordRowIndex + (linkedFromTaskRecordRowIndex === linkedToTaskRecordRowIndex ? 1 : 0.5)) + + toNodeDiffY }, { x: linkToPointX + distanceToTaskBar, y: rowHeight * - (linkedToTaskRecordRowIndex + (linkedFromTaskRecordRowIndex === linkedToTaskRecordRowIndex ? 1 : 0.5)) + (linkedToTaskRecordRowIndex + (linkedFromTaskRecordRowIndex === linkedToTaskRecordRowIndex ? 1 : 0.5)) + + toNodeDiffY }, { x: linkToPointX + distanceToTaskBar, - y: rowHeight * (linkedToTaskRecordRowIndex + 0.5) + y: rowHeight * (linkedToTaskRecordRowIndex + 0.5) + toNodeDiffY }, { x: linkToPointX, - y: rowHeight * (linkedToTaskRecordRowIndex + 0.5) + y: rowHeight * (linkedToTaskRecordRowIndex + 0.5) + toNodeDiffY } ]; const lastPoint = linePoints[linePoints.length - 1]; diff --git a/packages/vtable-gantt/src/scenegraph/task-bar.ts b/packages/vtable-gantt/src/scenegraph/task-bar.ts index 98d7857ca..a97854c21 100644 --- a/packages/vtable-gantt/src/scenegraph/task-bar.ts +++ b/packages/vtable-gantt/src/scenegraph/task-bar.ts @@ -79,20 +79,13 @@ export class TaskBar { } initBar(index: number, childIndex?: number, childrenLength?: number) { const taskBarCustomLayout = this._scene._gantt.parsedOptions.taskBarCustomLayout; - let startDate; - let endDate; - let taskDays; - let progress; - let taskRecord; - if (isValid(childIndex)) { - ({ startDate, endDate, taskDays, progress, taskRecord } = this._scene._gantt.getTaskInfoByTaskListIndex( - index, - childIndex - )); - } else { - ({ startDate, endDate, taskDays, progress, taskRecord } = this._scene._gantt.getTaskInfoByTaskListIndex(index)); - } - if (taskDays <= 0 || !startDate || !endDate) { + + const { startDate, endDate, taskDays, progress, taskRecord } = this._scene._gantt.getTaskInfoByTaskListIndex( + index, + childIndex + ); + + if (taskDays <= 0 || !startDate || !endDate || startDate.getTime() > endDate.getTime()) { return null; } const taskBarSize = this._scene._gantt.parsedOptions.colWidthPerDay * taskDays; diff --git a/packages/vtable-gantt/src/state/state-manager.ts b/packages/vtable-gantt/src/state/state-manager.ts index 53fe67581..c5431daa9 100644 --- a/packages/vtable-gantt/src/state/state-manager.ts +++ b/packages/vtable-gantt/src/state/state-manager.ts @@ -11,7 +11,7 @@ import { syncTreeChangeFromTable, syncSortFromTable } from './gantt-table-sync'; -import { findRecordByTaskKey, getTaskIndexByY } from '../gantt-helper'; +import { findRecordByTaskKey, getTaskIndexByY, getTaskIndexsByTaskY } from '../gantt-helper'; import { debounce } from '../tools/debounce'; import type { GanttTaskBarNode } from '../scenegraph/gantt-node'; import { TASKBAR_HOVER_ICON_WIDTH } from '../scenegraph/task-bar'; @@ -42,6 +42,7 @@ export class StateManager { deltaX: number; deltaY: number; targetStartX: number; + targetStartY: number; startOffsetY: number; moving: boolean; target: GanttTaskBarNode; @@ -103,6 +104,7 @@ export class StateManager { }; this.moveTaskBar = { targetStartX: null, + targetStartY: null, deltaX: 0, deltaY: 0, startOffsetY: null, @@ -313,9 +315,11 @@ export class StateManager { this.moveTaskBar.moving = true; this.moveTaskBar.target = target; this.moveTaskBar.targetStartX = target.attribute.x; + this.moveTaskBar.targetStartY = target.attribute.y; this.moveTaskBar.startX = x; this.moveTaskBar.startY = y; this.moveTaskBar.startOffsetY = offsetY; + target.setAttribute('zIndex', 100000); } isMoveingTaskBar() { @@ -325,32 +329,27 @@ export class StateManager { if (this.moveTaskBar.moveTaskBarXInertia.isInertiaScrolling()) { this.moveTaskBar.moveTaskBarXInertia.endInertia(); } - // const taskIndex = getTaskIndexByY(this.moveTaskBar.startOffsetY, this._gantt); - // const deltaX = x - this.moveTaskBar.startX; + const deltaX = this.moveTaskBar.deltaX; + const deltaY = this.moveTaskBar.deltaY; const days = Math.round(deltaX / this._gantt.parsedOptions.colWidthPerDay); const correctX = days * this._gantt.parsedOptions.colWidthPerDay; const targetEndX = this.moveTaskBar.targetStartX + correctX; - const target = this._gantt.stateManager.moveTaskBar.target; - // target.setAttribute('x', targetEndX); - resizeOrMoveTaskBar(target, targetEndX - target.attribute.x, null, this); - // if (target.attribute.x < this._gantt.stateManager.scrollLeft - 2) { - // this._gantt.stateManager.setScrollLeft(target.attribute.x); - // } - // if(this._gantt.stateManager.scrollLeft===0){ - - // } + const targetEndY = + this.moveTaskBar.targetStartY + + this._gantt.parsedOptions.rowHeight * Math.round(deltaY / this._gantt.parsedOptions.rowHeight); + const target = this.moveTaskBar.target; + // 判断横向拖动 更新数据的date if (Math.abs(days) >= 1) { - // const taskIndex = getTaskIndexByY(this.moveTaskBar.startOffsetY, this._gantt); - const taskIndex = this.moveTaskBar.target.task_index; - const sub_task_index = this.moveTaskBar.target.sub_task_index; + const taskIndex = target.task_index; + const sub_task_index = target.sub_task_index; const oldRecord = this._gantt.getRecordByIndex(taskIndex, sub_task_index); const oldStartDate = oldRecord[this._gantt.parsedOptions.startDateField]; const oldEndDate = oldRecord[this._gantt.parsedOptions.endDateField]; this._gantt._updateDateToTaskRecord('move', days, taskIndex, sub_task_index); + const newRecord = this._gantt.getRecordByIndex(taskIndex, sub_task_index); if (this._gantt.hasListeners(GANTT_EVENT_TYPE.CHANGE_DATE_RANGE)) { - const newRecord = this._gantt.getRecordByIndex(taskIndex, sub_task_index); this._gantt.fireListeners(GANTT_EVENT_TYPE.CHANGE_DATE_RANGE, { startDate: newRecord[this._gantt.parsedOptions.startDateField], endDate: newRecord[this._gantt.parsedOptions.endDateField], @@ -361,10 +360,32 @@ export class StateManager { }); } } + // 判断纵向拖动 处理数据的位置 + if (Math.abs(Math.round(deltaY / this._gantt.parsedOptions.rowHeight)) >= 1) { + // if (this._gantt.parsedOptions.showHierarchyMode !== ShowHierarchyMode.Sub_Tasks_Inline) { + //TODO 移动位置的数据更新到record中 + const indexs = getTaskIndexsByTaskY(targetEndY, this._gantt); + this._gantt._dragOrderTaskRecord( + target.task_index, + target.sub_task_index, + indexs.task_index, + indexs.sub_task_index + ); + this._gantt.taskListTableInstance.renderWithRecreateCells(); + this._gantt.scenegraph.refreshTaskBarsAndGrid(); + // target = this._gantt.scenegraph.taskBar.getTaskBarNodeByIndex(indexs.task_index, indexs.sub_task_index); + } else { + resizeOrMoveTaskBar(target, targetEndX - target.attribute.x, targetEndY - target.attribute.y, null, this); + } + this._gantt.scenegraph.updateNextFrame(); this.moveTaskBar.moving = false; + if (this.selectedTaskBar.target !== target) { + target.setAttribute('zIndex', undefined); + } this.moveTaskBar.target = null; this.moveTaskBar.deltaX = 0; + this.moveTaskBar.deltaY = 0; this.moveTaskBar.moveTaskBarXSpeed = 0; } dealTaskBarMove(e: FederatedPointerEvent) { @@ -483,7 +504,7 @@ export class StateManager { this._gantt._updateDateToTaskRecord('end-move', diff_days, taskIndex, sub_task_index); } this.showTaskBarHover(); - reCreateCustomNode(this._gantt, taskBarGroup, taskIndex); + reCreateCustomNode(this._gantt, taskBarGroup, taskIndex, sub_task_index); this.resizeTaskBar.resizing = false; this.resizeTaskBar.target = null; @@ -543,7 +564,7 @@ export class StateManager { this.showTaskBarHover(); - reCreateCustomNode(this._gantt, taskBarGroup, taskIndex); + reCreateCustomNode(this._gantt, taskBarGroup, taskIndex, sub_task_index); this._gantt.scenegraph.updateNextFrame(); // } @@ -677,9 +698,11 @@ export class StateManager { this._gantt.scenegraph.updateNextFrame(); } - showTaskBarSelectedBorder() { + showTaskBarSelectedBorder(target: GanttTaskBarNode) { + this._gantt.stateManager.selectedTaskBar.target?.setAttribute('zIndex', undefined); + this._gantt.stateManager.selectedTaskBar.target = target as any as GanttTaskBarNode; const linkCreatable = this._gantt.parsedOptions.dependencyLinkCreatable; - const target = this._gantt.stateManager.selectedTaskBar.target; + target.setAttribute('zIndex', 10000); const x = target.attribute.x; const y = target.attribute.y; const width = target.attribute.width; @@ -689,6 +712,7 @@ export class StateManager { } hideTaskBarSelectedBorder() { + this._gantt.stateManager.selectedTaskBar.target?.setAttribute('zIndex', undefined); this._gantt.stateManager.selectedTaskBar.target = null; this._gantt.scenegraph.taskBar.removeSelectedBorder(); this._gantt.scenegraph.updateNextFrame(); @@ -786,12 +810,15 @@ export class StateManager { } } -function reCreateCustomNode(gantt: Gantt, taskBarGroup: Group, taskIndex: number) { +function reCreateCustomNode(gantt: Gantt, taskBarGroup: Group, taskIndex: number, sub_task_index?: number) { const taskBarCustomLayout = gantt.parsedOptions.taskBarCustomLayout; if (taskBarCustomLayout) { let customLayoutObj; if (typeof taskBarCustomLayout === 'function') { - const { startDate, endDate, taskDays, progress, taskRecord } = gantt.getTaskInfoByTaskListIndex(taskIndex); + const { startDate, endDate, taskDays, progress, taskRecord } = gantt.getTaskInfoByTaskListIndex( + taskIndex, + sub_task_index + ); const arg = { width: taskBarGroup.attribute.width, height: taskBarGroup.attribute.height, @@ -834,6 +861,7 @@ function resizeOrMoveTaskBar(target: GanttTaskBarNode, dx: number, dy: number, n if (newWidth) { target.setAttribute('width', newWidth); } + const vtable_gantt_linkedTo = record.vtable_gantt_linkedTo; const vtable_gantt_linkedFrom = record.vtable_gantt_linkedFrom; for (let i = 0; i < vtable_gantt_linkedTo?.length; i++) { @@ -860,10 +888,14 @@ function resizeOrMoveTaskBar(target: GanttTaskBarNode, dx: number, dy: number, n let linkedToTaskShowIndex; let linkedFromTaskShowIndex; - + let diffY: number; if (state._gantt.parsedOptions.showHierarchyMode === ShowHierarchyMode.Sub_Tasks_Inline) { + const new_indexs = getTaskIndexsByTaskY(target.attribute.y + dy, state._gantt); linkedFromTaskShowIndex = linkedFromTaskRecord.index[0]; - linkedToTaskShowIndex = linkedToTaskRecord.index[0]; + // linkedToTaskShowIndex = linkedToTaskRecord.index[0]; + const beforeRowCountLinkedTo = + state._gantt.getRowsHeightByIndex(0, new_indexs.task_index - 1) / state._gantt.parsedOptions.rowHeight; // 耦合了listTableOption的customComputeRowHeight + linkedToTaskShowIndex = beforeRowCountLinkedTo; ({ startDate: linkedToTaskStartDate, endDate: linkedToTaskEndDate, @@ -874,13 +906,23 @@ function resizeOrMoveTaskBar(target: GanttTaskBarNode, dx: number, dy: number, n endDate: linkedFromTaskEndDate, taskDays: linkedFromTaskTaskDays } = state._gantt.getTaskInfoByTaskListIndex(linkedFromTaskRecord.index[0], linkedFromTaskRecord.index[1])); + + const taskbarHeight = state._gantt.parsedOptions.taskBarStyle.width; + diffY = + target.attribute.y + taskbarHeight / 2 - (linkedToTaskShowIndex + 0.5) * state._gantt.parsedOptions.rowHeight; + console.log('diffY', diffY); } else if (state._gantt.parsedOptions.showHierarchyMode === ShowHierarchyMode.Sub_Tasks) { + const new_indexs = getTaskIndexsByTaskY(target.attribute.y + dy, state._gantt); const beforeRowCountLinkedFrom = state._gantt.getRowsHeightByIndex(0, linkedFromTaskRecord.index[0] - 1) / state._gantt.parsedOptions.rowHeight; // 耦合了listTableOption的customComputeRowHeight linkedFromTaskShowIndex = beforeRowCountLinkedFrom + linkedFromTaskRecord.index[1]; + // const beforeRowCountLinkedTo = + // state._gantt.getRowsHeightByIndex(0, linkedToTaskRecord.index[0] - 1) / state._gantt.parsedOptions.rowHeight; // 耦合了listTableOption的customComputeRowHeight + // linkedToTaskShowIndex = beforeRowCountLinkedTo + linkedToTaskRecord.index[1]; const beforeRowCountLinkedTo = - state._gantt.getRowsHeightByIndex(0, linkedToTaskRecord.index[0] - 1) / state._gantt.parsedOptions.rowHeight; // 耦合了listTableOption的customComputeRowHeight - linkedToTaskShowIndex = beforeRowCountLinkedTo + linkedToTaskRecord.index[1]; + state._gantt.getRowsHeightByIndex(0, new_indexs.task_index - 1) / state._gantt.parsedOptions.rowHeight; // 耦合了listTableOption的customComputeRowHeight + linkedToTaskShowIndex = beforeRowCountLinkedTo + new_indexs.sub_task_index; + ({ startDate: linkedToTaskStartDate, endDate: linkedToTaskEndDate, @@ -891,6 +933,10 @@ function resizeOrMoveTaskBar(target: GanttTaskBarNode, dx: number, dy: number, n endDate: linkedFromTaskEndDate, taskDays: linkedFromTaskTaskDays } = state._gantt.getTaskInfoByTaskListIndex(linkedFromTaskRecord.index[0], linkedFromTaskRecord.index[1])); + + const taskbarHeight = state._gantt.parsedOptions.taskBarStyle.width; + diffY = + target.attribute.y + taskbarHeight / 2 - (linkedToTaskShowIndex + 0.5) * state._gantt.parsedOptions.rowHeight; } else { linkedFromTaskShowIndex = state._gantt.getTaskShowIndexByRecordIndex(linkedFromTaskRecord.index); linkedToTaskShowIndex = state._gantt.getTaskShowIndexByRecordIndex(linkedToTaskRecord.index); @@ -911,9 +957,11 @@ function resizeOrMoveTaskBar(target: GanttTaskBarNode, dx: number, dy: number, n linkedFromTaskStartDate, linkedFromTaskEndDate, linkedFromTaskShowIndex, + 0, linkedToTaskStartDate, linkedToTaskEndDate, linkedToTaskShowIndex, + diffY ?? 0, minDate, state._gantt.parsedOptions.rowHeight, state._gantt.parsedOptions.colWidthPerDay, @@ -948,9 +996,14 @@ function resizeOrMoveTaskBar(target: GanttTaskBarNode, dx: number, dy: number, n let linkedToTaskShowIndex; let linkedFromTaskShowIndex; - + let diffY: number; if (state._gantt.parsedOptions.showHierarchyMode === ShowHierarchyMode.Sub_Tasks_Inline) { - linkedFromTaskShowIndex = linkedFromTaskRecord.index[0]; + const new_indexs = getTaskIndexsByTaskY(target.attribute.y + dy, state._gantt); + const beforeRowCountLinkedFrom = + state._gantt.getRowsHeightByIndex(0, new_indexs.task_index - 1) / state._gantt.parsedOptions.rowHeight; // 耦合了listTableOption的customComputeRowHeight + linkedFromTaskShowIndex = beforeRowCountLinkedFrom; + + // linkedFromTaskShowIndex = linkedFromTaskRecord.index[0]; linkedToTaskShowIndex = linkedToTaskRecord.index[0]; ({ startDate: linkedToTaskStartDate, @@ -962,10 +1015,18 @@ function resizeOrMoveTaskBar(target: GanttTaskBarNode, dx: number, dy: number, n endDate: linkedFromTaskEndDate, taskDays: linkedFromTaskTaskDays } = state._gantt.getTaskInfoByTaskListIndex(linkedFromTaskRecord.index[0], linkedFromTaskRecord.index[1])); + const taskbarHeight = state._gantt.parsedOptions.taskBarStyle.width; + diffY = + target.attribute.y + taskbarHeight / 2 - (linkedFromTaskShowIndex + 0.5) * state._gantt.parsedOptions.rowHeight; } else if (state._gantt.parsedOptions.showHierarchyMode === ShowHierarchyMode.Sub_Tasks) { + const new_indexs = getTaskIndexsByTaskY(target.attribute.y + dy, state._gantt); + // const beforeRowCountLinkedFrom = + // state._gantt.getRowsHeightByIndex(0, linkedFromTaskRecord.index[0] - 1) / state._gantt.parsedOptions.rowHeight; // 耦合了listTableOption的customComputeRowHeight + // linkedFromTaskShowIndex = beforeRowCountLinkedFrom + linkedFromTaskRecord.index[1]; const beforeRowCountLinkedFrom = - state._gantt.getRowsHeightByIndex(0, linkedFromTaskRecord.index[0] - 1) / state._gantt.parsedOptions.rowHeight; // 耦合了listTableOption的customComputeRowHeight - linkedFromTaskShowIndex = beforeRowCountLinkedFrom + linkedFromTaskRecord.index[1]; + state._gantt.getRowsHeightByIndex(0, new_indexs.task_index - 1) / state._gantt.parsedOptions.rowHeight; // 耦合了listTableOption的customComputeRowHeight + linkedFromTaskShowIndex = beforeRowCountLinkedFrom + new_indexs.sub_task_index; + const beforeRowCountLinkedTo = state._gantt.getRowsHeightByIndex(0, linkedToTaskRecord.index[0] - 1) / state._gantt.parsedOptions.rowHeight; // 耦合了listTableOption的customComputeRowHeight linkedToTaskShowIndex = beforeRowCountLinkedTo + linkedToTaskRecord.index[1]; @@ -979,6 +1040,9 @@ function resizeOrMoveTaskBar(target: GanttTaskBarNode, dx: number, dy: number, n endDate: linkedFromTaskEndDate, taskDays: linkedFromTaskTaskDays } = state._gantt.getTaskInfoByTaskListIndex(linkedFromTaskRecord.index[0], linkedFromTaskRecord.index[1])); + const taskbarHeight = state._gantt.parsedOptions.taskBarStyle.width; + diffY = + target.attribute.y + taskbarHeight / 2 - (linkedFromTaskShowIndex + 0.5) * state._gantt.parsedOptions.rowHeight; } else { linkedFromTaskShowIndex = state._gantt.getTaskShowIndexByRecordIndex(linkedFromTaskRecord.index); linkedToTaskShowIndex = state._gantt.getTaskShowIndexByRecordIndex(linkedToTaskRecord.index); @@ -999,9 +1063,11 @@ function resizeOrMoveTaskBar(target: GanttTaskBarNode, dx: number, dy: number, n linkedFromTaskStartDate, linkedFromTaskEndDate, linkedFromTaskShowIndex, + diffY ?? 0, linkedToTaskStartDate, linkedToTaskEndDate, linkedToTaskShowIndex, + 0, minDate, state._gantt.parsedOptions.rowHeight, state._gantt.parsedOptions.colWidthPerDay, From fa878f091a4ed9819a78d6e4bbc60eb4ab6cc1e6 Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Thu, 21 Nov 2024 20:01:24 +0800 Subject: [PATCH 10/40] feat: rename tasksShowMode type --- .../examples/gantt/gantt-Sub_Tasks.ts | 4 +- .../examples/gantt/gantt-Sub_Tasks_Inline.ts | 4 +- .../gantt/gantt-customLayout-Sub_Tasks.ts | 4 +- .../gantt-customLayout-Sub_Tasks_Inline.ts | 4 +- packages/vtable-gantt/examples/menu.ts | 5 +- packages/vtable-gantt/package.json | 2 +- packages/vtable-gantt/src/Gantt.ts | 23 +++---- packages/vtable-gantt/src/data/DataSource.ts | 33 ++++++--- .../vtable-gantt/src/event/event-manager.ts | 6 +- packages/vtable-gantt/src/gantt-helper.ts | 4 +- .../src/scenegraph/dependency-link.ts | 6 +- .../vtable-gantt/src/scenegraph/task-bar.ts | 10 +-- .../vtable-gantt/src/state/state-manager.ts | 67 ++++++++++++++----- .../vtable-gantt/src/ts-types/gantt-engine.ts | 16 +++-- 14 files changed, 119 insertions(+), 69 deletions(-) diff --git a/packages/vtable-gantt/examples/gantt/gantt-Sub_Tasks.ts b/packages/vtable-gantt/examples/gantt/gantt-Sub_Tasks.ts index 579d295ee..a32349b3b 100644 --- a/packages/vtable-gantt/examples/gantt/gantt-Sub_Tasks.ts +++ b/packages/vtable-gantt/examples/gantt/gantt-Sub_Tasks.ts @@ -5,7 +5,7 @@ import type { GanttConstructorOptions, TYPES } from '../../src/index'; import { Gantt } from '../../src/index'; import { bindDebugTool } from '../../../vtable/src/scenegraph/debug-tool'; import { scale } from '@visactor/vutils'; -import { DependencyType, ShowHierarchyMode } from '../../src/ts-types'; +import { DependencyType, TasksShowMode } from '../../src/ts-types'; const CONTAINER_ID = 'vTable'; const date_input_editor = new DateInputEditor({}); const input_editor = new InputEditor({}); @@ -275,7 +275,7 @@ export function createTable() { } }, groupBy: true, - showHierarchyMode: ShowHierarchyMode.Sub_Tasks, + tasksShowMode: TasksShowMode.Sub_Tasks_Separate, frame: { outerFrameStyle: { borderLineWidth: 2, diff --git a/packages/vtable-gantt/examples/gantt/gantt-Sub_Tasks_Inline.ts b/packages/vtable-gantt/examples/gantt/gantt-Sub_Tasks_Inline.ts index 41316a8d8..5f7d26551 100644 --- a/packages/vtable-gantt/examples/gantt/gantt-Sub_Tasks_Inline.ts +++ b/packages/vtable-gantt/examples/gantt/gantt-Sub_Tasks_Inline.ts @@ -5,7 +5,7 @@ import type { GanttConstructorOptions, TYPES } from '../../src/index'; import { Gantt } from '../../src/index'; import { bindDebugTool } from '../../../vtable/src/scenegraph/debug-tool'; import { scale } from '@visactor/vutils'; -import { DependencyType, ShowHierarchyMode } from '../../src/ts-types'; +import { DependencyType, TasksShowMode } from '../../src/ts-types'; const CONTAINER_ID = 'vTable'; const date_input_editor = new DateInputEditor({}); const input_editor = new InputEditor({}); @@ -274,7 +274,7 @@ export function createTable() { } }, groupBy: true, - showHierarchyMode: ShowHierarchyMode.Sub_Tasks_Inline, + tasksShowMode: TasksShowMode.Sub_Tasks_Inline, frame: { outerFrameStyle: { borderLineWidth: 2, diff --git a/packages/vtable-gantt/examples/gantt/gantt-customLayout-Sub_Tasks.ts b/packages/vtable-gantt/examples/gantt/gantt-customLayout-Sub_Tasks.ts index 09137e42f..c2b95b62a 100644 --- a/packages/vtable-gantt/examples/gantt/gantt-customLayout-Sub_Tasks.ts +++ b/packages/vtable-gantt/examples/gantt/gantt-customLayout-Sub_Tasks.ts @@ -6,7 +6,7 @@ import { Group, Image, Text } from '@visactor/vtable/es/vrender'; import { Gantt } from '../../src/index'; import { bindDebugTool } from '../../../vtable/src/scenegraph/debug-tool'; import { scale } from '@visactor/vutils'; -import { DependencyType, ShowHierarchyMode } from '../../src/ts-types'; +import { DependencyType, TasksShowMode } from '../../src/ts-types'; const CONTAINER_ID = 'vTable'; const barColors0 = ['#aecde6', '#c6a49a', '#ffb582', '#eec1de', '#b3d9b3', '#d9d1a5', '#cccccc', '#e59a9c', '#c9bede']; @@ -181,7 +181,7 @@ export function createTable() { } }, groupBy: true, - showHierarchyMode: ShowHierarchyMode.Sub_Tasks, + tasksShowMode: TasksShowMode.Sub_Tasks_Separate, frame: { outerFrameStyle: { borderLineWidth: 1, diff --git a/packages/vtable-gantt/examples/gantt/gantt-customLayout-Sub_Tasks_Inline.ts b/packages/vtable-gantt/examples/gantt/gantt-customLayout-Sub_Tasks_Inline.ts index 9eb087ebb..91884c589 100644 --- a/packages/vtable-gantt/examples/gantt/gantt-customLayout-Sub_Tasks_Inline.ts +++ b/packages/vtable-gantt/examples/gantt/gantt-customLayout-Sub_Tasks_Inline.ts @@ -6,7 +6,7 @@ import { Group, Image, Text } from '@visactor/vtable/es/vrender'; import { Gantt } from '../../src/index'; import { bindDebugTool } from '../../../vtable/src/scenegraph/debug-tool'; import { scale } from '@visactor/vutils'; -import { DependencyType, ShowHierarchyMode } from '../../src/ts-types'; +import { DependencyType, TasksShowMode } from '../../src/ts-types'; const CONTAINER_ID = 'vTable'; const barColors0 = ['#aecde6', '#c6a49a', '#ffb582', '#eec1de', '#b3d9b3', '#d9d1a5', '#cccccc', '#e59a9c', '#c9bede']; @@ -181,7 +181,7 @@ export function createTable() { } }, groupBy: true, - showHierarchyMode: ShowHierarchyMode.Sub_Tasks_Inline, + tasksShowMode: TasksShowMode.Sub_Tasks_Inline, frame: { outerFrameStyle: { borderLineWidth: 1, diff --git a/packages/vtable-gantt/examples/menu.ts b/packages/vtable-gantt/examples/menu.ts index 358f289d1..b62f9c902 100644 --- a/packages/vtable-gantt/examples/menu.ts +++ b/packages/vtable-gantt/examples/menu.ts @@ -78,13 +78,14 @@ export const menus = [ path: 'gantt', name: 'gantt-customLayout-Sub_Tasks_Inline' }, + { path: 'gantt', - name: 'gantt-customLayout-Sub_Tasks' + name: 'gantt-Sub_Tasks' }, { path: 'gantt', - name: 'gantt-Sub_Tasks' + name: 'gantt-customLayout-Sub_Tasks' } // ] // } diff --git a/packages/vtable-gantt/package.json b/packages/vtable-gantt/package.json index be5044f1b..b4c9500d9 100644 --- a/packages/vtable-gantt/package.json +++ b/packages/vtable-gantt/package.json @@ -120,4 +120,4 @@ "url": "https://github.com/VisActor/VTable.git", "directory": "packages/vtable-gantt" } -} +} \ No newline at end of file diff --git a/packages/vtable-gantt/src/Gantt.ts b/packages/vtable-gantt/src/Gantt.ts index fdde5e753..ceb2648c5 100644 --- a/packages/vtable-gantt/src/Gantt.ts +++ b/packages/vtable-gantt/src/Gantt.ts @@ -24,7 +24,7 @@ import type { ITaskLinkSelectedStyle, IPointStyle } from './ts-types'; -import { ShowHierarchyMode } from './ts-types'; +import { TasksShowMode } from './ts-types'; import type { ListTableConstructorOptions } from '@visactor/vtable'; import { themes, registerCheckboxCell, registerProgressBarCell, registerRadioCell, ListTable } from '@visactor/vtable'; import { EventManager } from './event/event-manager'; @@ -125,7 +125,7 @@ export class Gantt extends EventTarget { outerFrameStyle: IFrameStyle; pixelRatio: number; - showHierarchyMode: ShowHierarchyMode; + tasksShowMode: TasksShowMode; startDateField: string; endDateField: string; @@ -328,8 +328,8 @@ export class Gantt extends EventTarget { if (key === 'columns') { listTable_options[key][listTable_options[key].length - 1].disableColumnResize = true; if ( - this.parsedOptions.showHierarchyMode === ShowHierarchyMode.Sub_Tasks_Inline || - this.parsedOptions.showHierarchyMode === ShowHierarchyMode.Sub_Tasks + this.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Inline || + this.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Separate ) { for (let i = 0; i < listTable_options.columns.length; i++) { if (listTable_options.columns[i].tree) { @@ -340,8 +340,8 @@ export class Gantt extends EventTarget { } if ( key === 'hierarchyExpandLevel' && - (this.parsedOptions.showHierarchyMode === ShowHierarchyMode.Sub_Tasks_Inline || - this.parsedOptions.showHierarchyMode === ShowHierarchyMode.Sub_Tasks) + (this.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Inline || + this.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Separate) ) { delete listTable_options[key]; } @@ -518,7 +518,7 @@ export class Gantt extends EventTarget { listTable_options.canvasWidth = this.taskTableWidth as number; listTable_options.canvasHeight = this.canvas.height; listTable_options.defaultHeaderRowHeight = this.getAllHeaderRowsHeight(); - if (this.parsedOptions.showHierarchyMode === ShowHierarchyMode.Sub_Tasks) { + if (this.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Separate) { listTable_options.customComputeRowHeight = (args: { row: number; table: ListTable }) => { const { row, table } = args; const record = table.getRecordByRowCol(0, row); @@ -845,14 +845,7 @@ export class Gantt extends EventTarget { target_sub_task_index: number ) { // const source_taskRecord = this.getRecordByIndex(source_index, source_sub_task_index); - if (isValid(source_sub_task_index) && isValid(target_sub_task_index)) { - const sub_task_record = this.records[source_index].children[source_sub_task_index]; - this.records[source_index].children.splice(source_sub_task_index, 1); - if (!this.records[target_index].children) { - this.records[target_index].children = []; - } - this.records[target_index].children.splice(target_sub_task_index, 0, sub_task_record); - } + this.data.adjustOrder(source_index, source_sub_task_index, target_index, target_sub_task_index); } /** 目前不支持树形tree的情况更新单条数据 需要的话目前可以setRecords。 */ updateTaskRecord(record: any, index: number) { diff --git a/packages/vtable-gantt/src/data/DataSource.ts b/packages/vtable-gantt/src/data/DataSource.ts index 1537d58d9..3b0864a28 100644 --- a/packages/vtable-gantt/src/data/DataSource.ts +++ b/packages/vtable-gantt/src/data/DataSource.ts @@ -1,7 +1,7 @@ import type { Gantt } from '../Gantt'; import { createDateAtMidnight } from '../tools/util'; -import { ShowHierarchyMode } from '../ts-types'; - +import { TasksShowMode } from '../ts-types'; +import { isValid } from '@visactor/vutils'; export class DataSource { records: any[]; minDate: Date; @@ -20,11 +20,7 @@ export class DataSource { let minDate = Number.MAX_SAFE_INTEGER; let maxDate = Number.MIN_SAFE_INTEGER; - if ( - needMinDate || - needMaxDate || - this._gantt.parsedOptions.showHierarchyMode === ShowHierarchyMode.Sub_Tasks_Inline - ) { + if (needMinDate || needMaxDate || this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Inline) { for (let i = 0; i < this.records.length; i++) { const record = this.records[i]; if (needMinDate) { @@ -36,7 +32,7 @@ export class DataSource { maxDate = Math.max(maxDate, recordMaxDate.getTime()); } - if (this._gantt.parsedOptions.showHierarchyMode === ShowHierarchyMode.Sub_Tasks_Inline) { + if (this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Inline) { // 将子任务按开始时间升序排列 record.children && record.children.sort((a: any, b: any) => { @@ -56,4 +52,25 @@ export class DataSource { this._gantt.parsedOptions._maxDateTime = this._gantt.parsedOptions.maxDate.getTime(); } } + adjustOrder( + source_index: number, + source_sub_task_index: number, + target_index: number, + target_sub_task_index: number + ) { + if (isValid(source_sub_task_index) && isValid(target_sub_task_index)) { + const sub_task_record = this.records[source_index].children[source_sub_task_index]; + this.records[source_index].children.splice(source_sub_task_index, 1); + if (!this.records[target_index].children) { + this.records[target_index].children = []; + } + this.records[target_index].children.splice(target_sub_task_index, 0, sub_task_record); + this.records[target_index].children.sort((a: any, b: any) => { + return ( + createDateAtMidnight(a[this._gantt.parsedOptions.startDateField]).getTime() - + createDateAtMidnight(b[this._gantt.parsedOptions.startDateField]).getTime() + ); + }); + } + } } diff --git a/packages/vtable-gantt/src/event/event-manager.ts b/packages/vtable-gantt/src/event/event-manager.ts index 1d1d9299a..3fd5c5062 100644 --- a/packages/vtable-gantt/src/event/event-manager.ts +++ b/packages/vtable-gantt/src/event/event-manager.ts @@ -4,7 +4,7 @@ import type { Gantt } from '../Gantt'; import { EventHandler } from '../event/EventHandler'; import { handleWhell } from '../event/scroll'; import { formatDate, parseDateFormat, throttle } from '../tools/util'; -import { GANTT_EVENT_TYPE, InteractionState, ShowHierarchyMode } from '../ts-types'; +import { GANTT_EVENT_TYPE, InteractionState, TasksShowMode } from '../ts-types'; import { isValid } from '@visactor/vutils'; import { getPixelRatio } from '../tools/pixel-ratio'; import { DayTimes, getDateIndexByX, getTaskIndexByY } from '../gantt-helper'; @@ -170,8 +170,8 @@ function bindTableGroupListener(event: EventManager) { } //#region hover到某一个任务 检查有没有日期安排,没有的话显示创建按钮 if ( - gantt.parsedOptions.showHierarchyMode !== ShowHierarchyMode.Sub_Tasks_Inline && - gantt.parsedOptions.showHierarchyMode !== ShowHierarchyMode.Sub_Tasks && + gantt.parsedOptions.tasksShowMode !== TasksShowMode.Sub_Tasks_Inline && + gantt.parsedOptions.tasksShowMode !== TasksShowMode.Sub_Tasks_Separate && gantt.parsedOptions.taskBarCreatable ) { const taskIndex = getTaskIndexByY(e.offset.y, gantt); diff --git a/packages/vtable-gantt/src/gantt-helper.ts b/packages/vtable-gantt/src/gantt-helper.ts index 6fdaa1473..4af4b2dd6 100644 --- a/packages/vtable-gantt/src/gantt-helper.ts +++ b/packages/vtable-gantt/src/gantt-helper.ts @@ -1,7 +1,7 @@ import { text } from 'stream/consumers'; import type { Gantt } from './Gantt'; import { - ShowHierarchyMode, + TasksShowMode, type IMarkLine, type IScrollStyle, type ITimelineDateInfo, @@ -94,7 +94,7 @@ export { isNode }; export function initOptions(gantt: Gantt) { const options = gantt.options; - gantt.parsedOptions.showHierarchyMode = options?.showHierarchyMode ?? ShowHierarchyMode.All; + gantt.parsedOptions.tasksShowMode = options?.tasksShowMode ?? TasksShowMode.Tasks_Separate; gantt.parsedOptions.pixelRatio = options?.pixelRatio ?? 1; gantt.parsedOptions.rowHeight = options?.rowHeight ?? 40; gantt.parsedOptions.timelineColWidth = options?.timelineHeader?.colWidth ?? 60; diff --git a/packages/vtable-gantt/src/scenegraph/dependency-link.ts b/packages/vtable-gantt/src/scenegraph/dependency-link.ts index e21f5662f..b691f0e0e 100644 --- a/packages/vtable-gantt/src/scenegraph/dependency-link.ts +++ b/packages/vtable-gantt/src/scenegraph/dependency-link.ts @@ -7,7 +7,7 @@ import { isValid } from '@visactor/vutils'; import { findRecordByTaskKey, getTextPos } from '../gantt-helper'; import type { GanttTaskBarNode } from './gantt-node'; import type { ITaskLink } from '../ts-types'; -import { DependencyType, ShowHierarchyMode } from '../ts-types'; +import { DependencyType, TasksShowMode } from '../ts-types'; export class DependencyLink { group: Group; @@ -75,7 +75,7 @@ export class DependencyLink { let linkedToTaskShowIndex; let linkedFromTaskShowIndex; - if (this._scene._gantt.parsedOptions.showHierarchyMode === ShowHierarchyMode.Sub_Tasks_Inline) { + if (this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Inline) { linkedFromTaskShowIndex = linkedFromTaskRecord.index[0]; linkedToTaskShowIndex = linkedToTaskRecord.index[0]; ({ @@ -88,7 +88,7 @@ export class DependencyLink { endDate: linkedFromTaskEndDate, taskDays: linkedFromTaskTaskDays } = this._scene._gantt.getTaskInfoByTaskListIndex(linkedFromTaskRecord.index[0], linkedFromTaskRecord.index[1])); - } else if (this._scene._gantt.parsedOptions.showHierarchyMode === ShowHierarchyMode.Sub_Tasks) { + } else if (this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Separate) { const beforeRowCountLinkedFrom = this._scene._gantt.getRowsHeightByIndex(0, linkedFromTaskRecord.index[0] - 1) / this._scene._gantt.parsedOptions.rowHeight; // 耦合了listTableOption的customComputeRowHeight diff --git a/packages/vtable-gantt/src/scenegraph/task-bar.ts b/packages/vtable-gantt/src/scenegraph/task-bar.ts index a97854c21..8886f32dc 100644 --- a/packages/vtable-gantt/src/scenegraph/task-bar.ts +++ b/packages/vtable-gantt/src/scenegraph/task-bar.ts @@ -5,7 +5,7 @@ import { createDateAtMidnight, parseStringTemplate, toBoxArray } from '../tools/ import { isValid } from '@visactor/vutils'; import { getTextPos } from '../gantt-helper'; import { GanttTaskBarNode } from './gantt-node'; -import { ShowHierarchyMode } from '../ts-types'; +import { TasksShowMode } from '../ts-types'; const TASKBAR_HOVER_ICON = ` @@ -56,8 +56,8 @@ export class TaskBar { for (let i = 0; i < this._scene._gantt.itemCount; i++) { if ( - this._scene._gantt.parsedOptions.showHierarchyMode === ShowHierarchyMode.Sub_Tasks_Inline || - this._scene._gantt.parsedOptions.showHierarchyMode === ShowHierarchyMode.Sub_Tasks + this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Inline || + this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Separate ) { const record = this._scene._gantt.getRecordByIndex(i); if (record.children?.length > 0) { @@ -93,7 +93,7 @@ export class TaskBar { const minDate = createDateAtMidnight(this._scene._gantt.parsedOptions.minDate); const oneTaskHeigth = this._scene._gantt.getRowHeightByIndex(index) / - (this._scene._gantt.parsedOptions.showHierarchyMode === ShowHierarchyMode.Sub_Tasks ? childrenLength : 1); + (this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Separate ? childrenLength : 1); const barGroup = new GanttTaskBarNode({ x: this._scene._gantt.parsedOptions.colWidthPerDay * @@ -101,7 +101,7 @@ export class TaskBar { // y: this._scene._gantt.parsedOptions.rowHeight * i, y: this._scene._gantt.getRowsHeightByIndex(0, index - 1) + - (this._scene._gantt.parsedOptions.showHierarchyMode === ShowHierarchyMode.Sub_Tasks + (this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Separate ? childIndex * oneTaskHeigth : 0) + (oneTaskHeigth - taskbarHeight) / 2, diff --git a/packages/vtable-gantt/src/state/state-manager.ts b/packages/vtable-gantt/src/state/state-manager.ts index c5431daa9..7aa8d5b83 100644 --- a/packages/vtable-gantt/src/state/state-manager.ts +++ b/packages/vtable-gantt/src/state/state-manager.ts @@ -1,7 +1,7 @@ import { clone, cloneDeep, isValid } from '@visactor/vutils'; import type { Gantt } from '../Gantt'; import type { ITaskLink } from '../ts-types'; -import { InteractionState, GANTT_EVENT_TYPE, DependencyType, ShowHierarchyMode } from '../ts-types'; +import { InteractionState, GANTT_EVENT_TYPE, DependencyType, TasksShowMode } from '../ts-types'; import type { Group, FederatedPointerEvent, Polygon, Line, Circle } from '@visactor/vtable/es/vrender'; import { syncEditCellFromTable, @@ -16,7 +16,7 @@ import { debounce } from '../tools/debounce'; import type { GanttTaskBarNode } from '../scenegraph/gantt-node'; import { TASKBAR_HOVER_ICON_WIDTH } from '../scenegraph/task-bar'; import { Inertia } from '../tools/inertia'; -import { generateLinkLinePoints, updateLinkLinePoints } from '../scenegraph/dependency-link'; +import { updateLinkLinePoints } from '../scenegraph/dependency-link'; export class StateManager { _gantt: Gantt; @@ -362,7 +362,6 @@ export class StateManager { } // 判断纵向拖动 处理数据的位置 if (Math.abs(Math.round(deltaY / this._gantt.parsedOptions.rowHeight)) >= 1) { - // if (this._gantt.parsedOptions.showHierarchyMode !== ShowHierarchyMode.Sub_Tasks_Inline) { //TODO 移动位置的数据更新到record中 const indexs = getTaskIndexsByTaskY(targetEndY, this._gantt); this._gantt._dragOrderTaskRecord( @@ -371,17 +370,57 @@ export class StateManager { indexs.task_index, indexs.sub_task_index ); - this._gantt.taskListTableInstance.renderWithRecreateCells(); - this._gantt.scenegraph.refreshTaskBarsAndGrid(); + if (this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Separate) { + this._gantt.taskListTableInstance.renderWithRecreateCells(); + this._gantt.scenegraph.refreshTaskBarsAndGrid(); + } else { + this._gantt.scenegraph.taskBar.refresh(); + this._gantt.scenegraph.dependencyLink.refresh(); + } // target = this._gantt.scenegraph.taskBar.getTaskBarNodeByIndex(indexs.task_index, indexs.sub_task_index); } else { - resizeOrMoveTaskBar(target, targetEndX - target.attribute.x, targetEndY - target.attribute.y, null, this); + resizeOrMoveTaskBar( + target, + targetEndX - (target as Group).attribute.x, + targetEndY - (target as Group).attribute.y, + null, + this + ); + + // 为了确保拖拽后 保持startDate日期晚的显示在上层不被盖住 这里需要重新排序一下 + if (days > 0) { + let insertAfterNode = target; + while ( + (insertAfterNode as Group).nextSibling && + (insertAfterNode as Group).nextSibling.attribute.y === (target as Group).attribute.y && + (insertAfterNode as Group).nextSibling.record[this._gantt.parsedOptions.startDateField] <= + target.record[this._gantt.parsedOptions.startDateField] + ) { + insertAfterNode = (insertAfterNode as Group).nextSibling; + } + if (insertAfterNode !== target) { + (insertAfterNode as Group).parent.insertAfter(target, insertAfterNode); + } + } else if (days < 0) { + let insertBeforeNode = target; + while ( + (insertBeforeNode as Group).previousSibling && + (insertBeforeNode as Group).previousSibling.attribute.y === (target as Group).attribute.y && + (insertBeforeNode as Group).previousSibling.record[this._gantt.parsedOptions.startDateField] >= + target.record[this._gantt.parsedOptions.startDateField] + ) { + insertBeforeNode = (insertBeforeNode as Group).previousSibling; + } + if (insertBeforeNode !== target) { + (insertBeforeNode as Group).parent.insertBefore(target, insertBeforeNode); + } + } } this._gantt.scenegraph.updateNextFrame(); this.moveTaskBar.moving = false; if (this.selectedTaskBar.target !== target) { - target.setAttribute('zIndex', undefined); + target.setAttribute('zIndex', 0); } this.moveTaskBar.target = null; this.moveTaskBar.deltaX = 0; @@ -744,8 +783,8 @@ export class StateManager { const linkedToTaskRecord = findRecordByTaskKey(this._gantt.records, taskKeyField, linkedToTaskKey); const linkedFromTaskRecord = findRecordByTaskKey(this._gantt.records, taskKeyField, linkedFromTaskKey); if ( - this._gantt.parsedOptions.showHierarchyMode === ShowHierarchyMode.Sub_Tasks_Inline || - this._gantt.parsedOptions.showHierarchyMode === ShowHierarchyMode.Sub_Tasks + this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Inline || + this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Separate ) { linkFrom_index = linkedFromTaskRecord.index[0]; linkFrom_sub_task_index = linkedFromTaskRecord.index[1]; @@ -861,7 +900,6 @@ function resizeOrMoveTaskBar(target: GanttTaskBarNode, dx: number, dy: number, n if (newWidth) { target.setAttribute('width', newWidth); } - const vtable_gantt_linkedTo = record.vtable_gantt_linkedTo; const vtable_gantt_linkedFrom = record.vtable_gantt_linkedFrom; for (let i = 0; i < vtable_gantt_linkedTo?.length; i++) { @@ -889,7 +927,7 @@ function resizeOrMoveTaskBar(target: GanttTaskBarNode, dx: number, dy: number, n let linkedToTaskShowIndex; let linkedFromTaskShowIndex; let diffY: number; - if (state._gantt.parsedOptions.showHierarchyMode === ShowHierarchyMode.Sub_Tasks_Inline) { + if (state._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Inline) { const new_indexs = getTaskIndexsByTaskY(target.attribute.y + dy, state._gantt); linkedFromTaskShowIndex = linkedFromTaskRecord.index[0]; // linkedToTaskShowIndex = linkedToTaskRecord.index[0]; @@ -910,8 +948,7 @@ function resizeOrMoveTaskBar(target: GanttTaskBarNode, dx: number, dy: number, n const taskbarHeight = state._gantt.parsedOptions.taskBarStyle.width; diffY = target.attribute.y + taskbarHeight / 2 - (linkedToTaskShowIndex + 0.5) * state._gantt.parsedOptions.rowHeight; - console.log('diffY', diffY); - } else if (state._gantt.parsedOptions.showHierarchyMode === ShowHierarchyMode.Sub_Tasks) { + } else if (state._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Separate) { const new_indexs = getTaskIndexsByTaskY(target.attribute.y + dy, state._gantt); const beforeRowCountLinkedFrom = state._gantt.getRowsHeightByIndex(0, linkedFromTaskRecord.index[0] - 1) / state._gantt.parsedOptions.rowHeight; // 耦合了listTableOption的customComputeRowHeight @@ -997,7 +1034,7 @@ function resizeOrMoveTaskBar(target: GanttTaskBarNode, dx: number, dy: number, n let linkedToTaskShowIndex; let linkedFromTaskShowIndex; let diffY: number; - if (state._gantt.parsedOptions.showHierarchyMode === ShowHierarchyMode.Sub_Tasks_Inline) { + if (state._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Inline) { const new_indexs = getTaskIndexsByTaskY(target.attribute.y + dy, state._gantt); const beforeRowCountLinkedFrom = state._gantt.getRowsHeightByIndex(0, new_indexs.task_index - 1) / state._gantt.parsedOptions.rowHeight; // 耦合了listTableOption的customComputeRowHeight @@ -1018,7 +1055,7 @@ function resizeOrMoveTaskBar(target: GanttTaskBarNode, dx: number, dy: number, n const taskbarHeight = state._gantt.parsedOptions.taskBarStyle.width; diffY = target.attribute.y + taskbarHeight / 2 - (linkedFromTaskShowIndex + 0.5) * state._gantt.parsedOptions.rowHeight; - } else if (state._gantt.parsedOptions.showHierarchyMode === ShowHierarchyMode.Sub_Tasks) { + } else if (state._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Separate) { const new_indexs = getTaskIndexsByTaskY(target.attribute.y + dy, state._gantt); // const beforeRowCountLinkedFrom = // state._gantt.getRowsHeightByIndex(0, linkedFromTaskRecord.index[0] - 1) / state._gantt.parsedOptions.rowHeight; // 耦合了listTableOption的customComputeRowHeight diff --git a/packages/vtable-gantt/src/ts-types/gantt-engine.ts b/packages/vtable-gantt/src/ts-types/gantt-engine.ts index 15b232f28..4e15590c5 100644 --- a/packages/vtable-gantt/src/ts-types/gantt-engine.ts +++ b/packages/vtable-gantt/src/ts-types/gantt-engine.ts @@ -191,7 +191,7 @@ export interface GanttConstructorOptions { underlayBackgroundColor?: string; groupBy?: true | string | string[]; /** 展示嵌套结构数据时的模式,默认为full。*/ - showHierarchyMode?: ShowHierarchyMode; + tasksShowMode?: TasksShowMode; } /** * IBarLabelText @@ -334,13 +334,15 @@ export enum DependencyType { FinishToFinish = 'finish_to_finish', StartToFinish = 'start_to_finish' } -export enum ShowHierarchyMode { - /** 每一个节点用单独一行来展示,也就是父子任务的节点都占用一行 */ - All = 'all', - /** 省去父节点,把所有子任务的节点都放到同一行来展示。 */ +export enum TasksShowMode { + /** 每一个任务节点用单独一行来展示,父任务占用一行,子任务分别占用一行 */ + Tasks_Separate = 'tasks_separate', + /** 省去父任务节点不展示,并把所有子任务的节点都放到同一行来展示。 */ Sub_Tasks_Inline = 'sub_tasks_inline', - /** 所有子任务的节点分别用一行展示。*/ - Sub_Tasks = 'sub_tasks' + /** 省去父任务节点不展示,且所有子任务的节点分别用一行展示。*/ + Sub_Tasks_Separate = 'sub_tasks_separate', + /** 省去父任务节点不展示,且所有子任务的节点均不重叠展示 */ + Sub_Tasks_Arrange = 'sub_tasks_arrange' } export type ITaskBarSelectedStyle = { shadowBlur?: number; //阴影宽度 From ec331dd37184312f7b98c1a4d0c544cc911d712e Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Thu, 21 Nov 2024 20:12:04 +0800 Subject: [PATCH 11/40] fix: set taskbar node zindex --- packages/vtable-gantt/src/state/state-manager.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/vtable-gantt/src/state/state-manager.ts b/packages/vtable-gantt/src/state/state-manager.ts index 7aa8d5b83..5b6d379b9 100644 --- a/packages/vtable-gantt/src/state/state-manager.ts +++ b/packages/vtable-gantt/src/state/state-manager.ts @@ -319,7 +319,7 @@ export class StateManager { this.moveTaskBar.startX = x; this.moveTaskBar.startY = y; this.moveTaskBar.startOffsetY = offsetY; - target.setAttribute('zIndex', 100000); + target.setAttribute('zIndex', 10000); } isMoveingTaskBar() { @@ -429,6 +429,7 @@ export class StateManager { } dealTaskBarMove(e: FederatedPointerEvent) { const target = this.moveTaskBar.target; + target.setAttribute('zIndex', 10000); // const taskIndex = getTaskIndexByY(this.moveTaskBar.startOffsetY, this._gantt); const x1 = this._gantt.eventManager.lastDragPointerXYOnWindow.x; const x2 = e.x; @@ -544,6 +545,9 @@ export class StateManager { } this.showTaskBarHover(); reCreateCustomNode(this._gantt, taskBarGroup, taskIndex, sub_task_index); + + taskBarGroup.setAttribute('zIndex', 0); + this.resizeTaskBar.resizing = false; this.resizeTaskBar.target = null; @@ -567,6 +571,7 @@ export class StateManager { const dx = x2 - x1; // debugger; const taskBarGroup = this._gantt.stateManager.resizeTaskBar.target; + taskBarGroup.setAttribute('zIndex', 10000); const rect = taskBarGroup.barRect; const progressRect = taskBarGroup.progressRect; const textLabel = taskBarGroup.textLabel; @@ -738,7 +743,7 @@ export class StateManager { } showTaskBarSelectedBorder(target: GanttTaskBarNode) { - this._gantt.stateManager.selectedTaskBar.target?.setAttribute('zIndex', undefined); + this._gantt.stateManager.selectedTaskBar.target?.setAttribute('zIndex', 0); this._gantt.stateManager.selectedTaskBar.target = target as any as GanttTaskBarNode; const linkCreatable = this._gantt.parsedOptions.dependencyLinkCreatable; target.setAttribute('zIndex', 10000); @@ -751,7 +756,7 @@ export class StateManager { } hideTaskBarSelectedBorder() { - this._gantt.stateManager.selectedTaskBar.target?.setAttribute('zIndex', undefined); + this._gantt.stateManager.selectedTaskBar.target?.setAttribute('zIndex', 0); this._gantt.stateManager.selectedTaskBar.target = null; this._gantt.scenegraph.taskBar.removeSelectedBorder(); this._gantt.scenegraph.updateNextFrame(); From 4dda8e745fcdf377d74f077509679910e8defd35 Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Mon, 25 Nov 2024 17:52:15 +0800 Subject: [PATCH 12/40] feat: add tasksShowMode Sub_Tasks_Arrange --- .../examples/gantt/gantt-Sub_Tasks_Arrange.ts | 461 ++++++++++++++++++ .../examples/gantt/gantt-Sub_Tasks_Inline.ts | 2 +- ...b_Tasks.ts => gantt-Sub_Tasks_Separate.ts} | 0 packages/vtable-gantt/examples/menu.ts | 6 +- packages/vtable-gantt/src/Gantt.ts | 18 +- packages/vtable-gantt/src/data/DataSource.ts | 34 +- .../vtable-gantt/src/event/event-manager.ts | 1 + packages/vtable-gantt/src/gantt-helper.ts | 99 +++- .../src/scenegraph/dependency-link.ts | 29 +- .../vtable-gantt/src/scenegraph/scenegraph.ts | 2 + .../vtable-gantt/src/scenegraph/task-bar.ts | 22 +- .../src/state/gantt-table-sync.ts | 2 +- .../vtable-gantt/src/state/state-manager.ts | 160 ++++-- 13 files changed, 756 insertions(+), 80 deletions(-) create mode 100644 packages/vtable-gantt/examples/gantt/gantt-Sub_Tasks_Arrange.ts rename packages/vtable-gantt/examples/gantt/{gantt-Sub_Tasks.ts => gantt-Sub_Tasks_Separate.ts} (100%) diff --git a/packages/vtable-gantt/examples/gantt/gantt-Sub_Tasks_Arrange.ts b/packages/vtable-gantt/examples/gantt/gantt-Sub_Tasks_Arrange.ts new file mode 100644 index 000000000..5cd18105f --- /dev/null +++ b/packages/vtable-gantt/examples/gantt/gantt-Sub_Tasks_Arrange.ts @@ -0,0 +1,461 @@ +import type { ColumnsDefine } from '@visactor/vtable'; +import { register } from '@visactor/vtable'; +import { DateInputEditor, InputEditor } from '@visactor/vtable-editors'; +import type { GanttConstructorOptions, TYPES } from '../../src/index'; +import { Gantt } from '../../src/index'; +import { bindDebugTool } from '../../../vtable/src/scenegraph/debug-tool'; +import { scale } from '@visactor/vutils'; +import { DependencyType, TasksShowMode } from '../../src/ts-types'; +const CONTAINER_ID = 'vTable'; +const date_input_editor = new DateInputEditor({}); +const input_editor = new InputEditor({}); +register.editor('input', input_editor); +register.editor('date-input', date_input_editor); +export function createTable() { + const records = [ + { + id: 100, + title: 'Software Development', + children: [ + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-07-27', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024/08/01', + end: '2024/08/04', + progress: 100, + priority: 'P1' + } + ] + }, + { + id: 200, + title: 'Scope' + }, + { + id: 300, + title: 'Determine project scope', + children: [ + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-08-04', + end: '2024-08-04', + progress: 90, + priority: 'P0' + }, + + { + id: 4, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024.07.06', + end: '2024.07.08', + progress: 60, + priority: 'P0' + }, + { + id: 5, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '07.24.2024', + end: '08.02.2024', + progress: 31, + priority: 'P0' + }, + { + id: 6, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 7, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-08-09', + end: '2024-09-11', + progress: 100, + priority: 'P1' + } + ] + }, + + { + id: 100, + title: 'Software Development' + }, + { + id: 200, + title: 'Scope', + children: [ + { + id: 8, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + }, + { + id: 9, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 10, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 11, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + }, + { + id: 12, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 13, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-08-01', + end: '2024-08-04', + progress: 60, + priority: 'P0' + }, + { + id: 14, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-08-03', + end: '2024-08-05', + progress: 100, + priority: 'P1' + } + ] + }, + + { + id: 300, + title: 'Determine project scope', + children: [ + { + id: 15, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 16, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 17, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + }, + { + id: 18, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-30', + end: '2024-08-14', + progress: 31, + priority: 'P0' + }, + { + id: 19, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 60, + priority: 'P0' + } + ] + }, + { + id: 100, + title: 'Software Development', + children: [ + { + id: 20, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024/07/24', + end: '2024/08/04', + progress: 100, + priority: 'P1' + }, + { + id: 21, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-08-04', + end: '2024-08-04', + progress: 90, + priority: 'P0' + }, + { + id: 22, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '07/24/2024', + end: '08/04/2024', + progress: 60, + priority: 'P0' + } + ] + } + ]; + + const columns: ColumnsDefine = [ + { + field: 'title', + title: 'title', + width: 200, + tree: true, + sort: true + } + ]; + const option: GanttConstructorOptions = { + records, + rowHeight: 30, + taskListTable: { + columns: columns, + minTableWidth: 100, + hierarchyExpandLevel: 5, + menu: { + contextMenuItems: ['copy', 'paste', 'delete', '...'] + } + }, + groupBy: true, + tasksShowMode: TasksShowMode.Sub_Tasks_Arrange, + frame: { + outerFrameStyle: { + borderLineWidth: 2, + borderColor: 'red', + cornerRadius: 8 + }, + verticalSplitLineHighlight: { + lineColor: 'green', + lineWidth: 3 + }, + verticalSplitLineMoveable: true + }, + grid: { + // backgroundColor: 'gray', + verticalLine: { + lineWidth: 1, + lineColor: '#e1e4e8' + }, + horizontalLine: { + lineWidth: 1, + lineColor: '#e1e4e8' + } + }, + headerRowHeight: 60, + taskBar: { + startDateField: 'start', + endDateField: 'end', + progressField: 'progress', + labelText: '{title}_{id} {progress}%', + labelTextStyle: { + fontFamily: 'Arial', + fontSize: 16, + textAlign: 'left' + }, + barStyle: { + width: 20, + /** 任务条的颜色 */ + barColor: '#ee8800', + /** 已完成部分任务条的颜色 */ + completedBarColor: '#91e8e0', + /** 任务条的圆角 */ + cornerRadius: 8 + } + }, + dependency: { + linkCreatable: true, + links: [ + { + type: DependencyType.FinishToFinish, + linkedFromTaskKey: 3, + linkedToTaskKey: 2 + }, + { + type: DependencyType.StartToFinish, + linkedFromTaskKey: 2, + linkedToTaskKey: 3 + }, + { + type: DependencyType.StartToStart, + linkedFromTaskKey: 3, + linkedToTaskKey: 4 + }, + { + type: DependencyType.FinishToFinish, + linkedFromTaskKey: 6, + linkedToTaskKey: 5 + } + ] + }, + timelineHeader: { + verticalLine: { + lineColor: '#e1e4e8', + lineWidth: 1 + }, + horizontalLine: { + lineColor: '#e1e4e8', + lineWidth: 1 + }, + scales: [ + { + unit: 'week', + step: 1, + startOfWeek: 'sunday', + format(date: TYPES.DateFormatArgumentType) { + return `Week ${date.dateIndex}`; + }, + style: { + fontSize: 20, + fontWeight: 'bold', + color: 'red', + backgroundColor: '#EEF1F5' + } + }, + { + unit: 'day', + step: 1, + format(date: TYPES.DateFormatArgumentType) { + return date.dateIndex.toString(); + }, + style: { + fontSize: 20, + fontWeight: 'bold', + color: 'red', + backgroundColor: '#EEF1F5' + } + } + // { + // unit: 'quarter', + // step: 1, + // format(date: TYPES.DateFormatArgumentType) { + // return '第' + date.index + '季度'; + // } + // } + ] + }, + minDate: '2024-07-01', + maxDate: '2024-10-15', + markLine: [ + { + date: '2024-07-17', + style: { + lineWidth: 1, + lineColor: 'blue', + lineDash: [8, 4] + } + }, + { + date: '2024-08-17', + style: { + lineWidth: 2, + lineColor: 'red', + lineDash: [8, 4] + } + } + ], + rowSeriesNumber: { + title: '行号', + dragOrder: true, + headerStyle: { + bgColor: '#EEF1F5', + + borderColor: '#e1e4e8' + }, + style: { + borderColor: '#e1e4e8' + } + }, + scrollStyle: { + scrollRailColor: 'RGBA(246,246,246,0.5)', + visible: 'none', + width: 6, + scrollSliderCornerRadius: 2, + scrollSliderColor: '#5cb85c' + } + }; + // columns:[ + // { + // title:'2024-07', + // columns:[ + // { + // title:'01' + // }, + // { + // title:'02' + // }, + // ... + // ] + // }, + // ... + // ] + const ganttInstance = new Gantt(document.getElementById(CONTAINER_ID)!, option); + window.ganttInstance = ganttInstance; + ganttInstance.on('scroll', e => { + console.log('scroll', e); + }); + + ganttInstance.listTableInstance?.on('scroll', e => { + console.log('listTable scroll', e); + }); + // bindDebugTool(ganttInstance.scenegraph.stage as any, { + // customGrapicKeys: ['role', '_updateTag'] + // }); +} diff --git a/packages/vtable-gantt/examples/gantt/gantt-Sub_Tasks_Inline.ts b/packages/vtable-gantt/examples/gantt/gantt-Sub_Tasks_Inline.ts index 5f7d26551..d72400e97 100644 --- a/packages/vtable-gantt/examples/gantt/gantt-Sub_Tasks_Inline.ts +++ b/packages/vtable-gantt/examples/gantt/gantt-Sub_Tasks_Inline.ts @@ -320,7 +320,7 @@ export function createTable() { linkCreatable: true, links: [ { - type: DependencyType.StartToFinish, + type: DependencyType.FinishToFinish, linkedFromTaskKey: 3, linkedToTaskKey: 2 } diff --git a/packages/vtable-gantt/examples/gantt/gantt-Sub_Tasks.ts b/packages/vtable-gantt/examples/gantt/gantt-Sub_Tasks_Separate.ts similarity index 100% rename from packages/vtable-gantt/examples/gantt/gantt-Sub_Tasks.ts rename to packages/vtable-gantt/examples/gantt/gantt-Sub_Tasks_Separate.ts diff --git a/packages/vtable-gantt/examples/menu.ts b/packages/vtable-gantt/examples/menu.ts index b62f9c902..eac8b5922 100644 --- a/packages/vtable-gantt/examples/menu.ts +++ b/packages/vtable-gantt/examples/menu.ts @@ -81,11 +81,15 @@ export const menus = [ { path: 'gantt', - name: 'gantt-Sub_Tasks' + name: 'gantt-Sub_Tasks_Separate' }, { path: 'gantt', name: 'gantt-customLayout-Sub_Tasks' + }, + { + path: 'gantt', + name: 'gantt-Sub_Tasks_Arrange' } // ] // } diff --git a/packages/vtable-gantt/src/Gantt.ts b/packages/vtable-gantt/src/Gantt.ts index ceb2648c5..1f25a90f5 100644 --- a/packages/vtable-gantt/src/Gantt.ts +++ b/packages/vtable-gantt/src/Gantt.ts @@ -30,6 +30,7 @@ import { themes, registerCheckboxCell, registerProgressBarCell, registerRadioCel import { EventManager } from './event/event-manager'; import { StateManager } from './state/state-manager'; import { + computeRowsCountByRecordDate, convertProgress, createSplitLineAndResizeLine, DayTimes, @@ -329,7 +330,8 @@ export class Gantt extends EventTarget { listTable_options[key][listTable_options[key].length - 1].disableColumnResize = true; if ( this.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Inline || - this.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Separate + this.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Separate || + this.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange ) { for (let i = 0; i < listTable_options.columns.length; i++) { if (listTable_options.columns[i].tree) { @@ -341,7 +343,8 @@ export class Gantt extends EventTarget { if ( key === 'hierarchyExpandLevel' && (this.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Inline || - this.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Separate) + this.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Separate || + this.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange) ) { delete listTable_options[key]; } @@ -525,6 +528,16 @@ export class Gantt extends EventTarget { return (record.children?.length || 1) * this.parsedOptions.rowHeight; }; listTable_options.defaultRowHeight = 'auto'; + } else if (this.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange) { + listTable_options.customComputeRowHeight = (args: { row: number; table: ListTable }) => { + const { row, table } = args; + const record = table.getRecordByRowCol(0, row); + return ( + computeRowsCountByRecordDate(record, this.parsedOptions.startDateField, this.parsedOptions.endDateField) * + this.parsedOptions.rowHeight + ); + }; + listTable_options.defaultRowHeight = 'auto'; } else { listTable_options.defaultRowHeight = this.options.rowHeight ?? 40; } @@ -903,7 +916,6 @@ export class Gantt extends EventTarget { this.records = records; this.taskListTableInstance.setRecords(records); this._syncPropsFromTable(); - this.verticalSplitResizeLine.style.height = this.drawHeight + 'px'; //'100%'; this.scenegraph.refreshTaskBarsAndGrid(); const left = this.stateManager.scroll.horizontalBarPos; const top = this.stateManager.scroll.verticalBarPos; diff --git a/packages/vtable-gantt/src/data/DataSource.ts b/packages/vtable-gantt/src/data/DataSource.ts index 3b0864a28..b79196f56 100644 --- a/packages/vtable-gantt/src/data/DataSource.ts +++ b/packages/vtable-gantt/src/data/DataSource.ts @@ -20,7 +20,12 @@ export class DataSource { let minDate = Number.MAX_SAFE_INTEGER; let maxDate = Number.MIN_SAFE_INTEGER; - if (needMinDate || needMaxDate || this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Inline) { + if ( + needMinDate || + needMaxDate || + this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Inline || + this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange + ) { for (let i = 0; i < this.records.length; i++) { const record = this.records[i]; if (needMinDate) { @@ -32,7 +37,10 @@ export class DataSource { maxDate = Math.max(maxDate, recordMaxDate.getTime()); } - if (this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Inline) { + if ( + this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Inline || + this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange + ) { // 将子任务按开始时间升序排列 record.children && record.children.sort((a: any, b: any) => { @@ -58,19 +66,25 @@ export class DataSource { target_index: number, target_sub_task_index: number ) { - if (isValid(source_sub_task_index) && isValid(target_sub_task_index)) { - const sub_task_record = this.records[source_index].children[source_sub_task_index]; - this.records[source_index].children.splice(source_sub_task_index, 1); - if (!this.records[target_index].children) { - this.records[target_index].children = []; - } - this.records[target_index].children.splice(target_sub_task_index, 0, sub_task_record); - this.records[target_index].children.sort((a: any, b: any) => { + if ( + this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Inline || + this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange + ) { + this.records[target_index]?.children?.sort((a: any, b: any) => { return ( createDateAtMidnight(a[this._gantt.parsedOptions.startDateField]).getTime() - createDateAtMidnight(b[this._gantt.parsedOptions.startDateField]).getTime() ); }); + } else { + if (isValid(source_sub_task_index) && isValid(target_sub_task_index) && isValid(target_index)) { + const sub_task_record = this.records[source_index].children[source_sub_task_index]; + this.records[source_index].children.splice(source_sub_task_index, 1); + if (!this.records[target_index].children) { + this.records[target_index].children = []; + } + this.records[target_index].children.splice(target_sub_task_index, 0, sub_task_record); + } } } } diff --git a/packages/vtable-gantt/src/event/event-manager.ts b/packages/vtable-gantt/src/event/event-manager.ts index 3fd5c5062..f60b0b07f 100644 --- a/packages/vtable-gantt/src/event/event-manager.ts +++ b/packages/vtable-gantt/src/event/event-manager.ts @@ -172,6 +172,7 @@ function bindTableGroupListener(event: EventManager) { if ( gantt.parsedOptions.tasksShowMode !== TasksShowMode.Sub_Tasks_Inline && gantt.parsedOptions.tasksShowMode !== TasksShowMode.Sub_Tasks_Separate && + gantt.parsedOptions.tasksShowMode !== TasksShowMode.Sub_Tasks_Arrange && gantt.parsedOptions.taskBarCreatable ) { const taskIndex = getTaskIndexByY(e.offset.y, gantt); diff --git a/packages/vtable-gantt/src/gantt-helper.ts b/packages/vtable-gantt/src/gantt-helper.ts index 4af4b2dd6..61b7b5bf2 100644 --- a/packages/vtable-gantt/src/gantt-helper.ts +++ b/packages/vtable-gantt/src/gantt-helper.ts @@ -645,12 +645,103 @@ export function getTaskIndexsByTaskY(y: number, gantt: Gantt) { let task_index; let sub_task_index; if (gantt.taskListTableInstance) { - const { row } = gantt.taskListTableInstance.getTargetRowAt(y + gantt.headerHeight); - task_index = row - gantt.taskListTableInstance.columnHeaderLevelCount; - const beforeRowsHeight = gantt.getRowsHeightByIndex(0, task_index - 1); // 耦合了listTableOption的customComputeRowHeight - sub_task_index = Math.floor((y - beforeRowsHeight) / gantt.parsedOptions.rowHeight); + const rowInfo = gantt.taskListTableInstance.getTargetRowAt(y + gantt.headerHeight); + if (rowInfo) { + const { row } = rowInfo; + task_index = row - gantt.taskListTableInstance.columnHeaderLevelCount; + const beforeRowsHeight = gantt.getRowsHeightByIndex(0, task_index - 1); // 耦合了listTableOption的customComputeRowHeight + sub_task_index = Math.floor((y - beforeRowsHeight) / gantt.parsedOptions.rowHeight); + } } else { task_index = Math.floor(y / gantt.parsedOptions.rowHeight); } return { task_index, sub_task_index }; } + +export function computeRowsCountByRecordDate(record: any, startDateField: string, endDateField: string) { + if (!record.children || record.children.length === 1) { + return 1; + } + // 排序在datasource中已经排过了 + // // 创建一个浅拷贝并排序子任务,根据开始日期排序 + // const sortedChildren = record.children.slice().sort((a: any, b: any) => { + // return createDateAtMidnight(a[startDateField]).getTime() - createDateAtMidnight(b[startDateField]).getTime(); + // }); + const count = 0; + // 用于存储每一行的结束日期 + const rows = []; + for (let i = 0; i <= record.children.length - 1; i++) { + const newRecord = record.children[i]; + const startDate = createDateAtMidnight(newRecord[startDateField]).getTime(); + const endDate = createDateAtMidnight(newRecord[endDateField]).getTime(); + + let placed = false; + + // 尝试将当前任务放入已有的行中 + for (let j = 0; j < rows.length; j++) { + if (startDate > rows[j]) { + // 如果当前任务的开始日期在该行的结束日期之后,则可以放在这一行 + rows[j] = endDate; + placed = true; + break; + } + } + + // 如果不能放在已有的行中,则需要新开一行 + if (!placed) { + rows.push(endDate); + } + } + + return rows.length; +} + +export function getSubTaskRowIndexByRecordDate( + record: any, + childIndex: number, + startDateField: string, + endDateField: string +) { + if (childIndex === 0) { + return 0; + } + // 排序在datasource中已经排过了 + // 创建一个浅拷贝并排序子任务,根据开始日期排序 + // const sortedChildren = record.children.slice().sort((a: any, b: any) => { + // return createDateAtMidnight(a[startDateField]).getTime() - createDateAtMidnight(b[startDateField]).getTime(); + // }); + + // 用于存储每一行的结束日期 + const rows = []; + if (record?.children) { + for (let i = 0; i <= record.children.length - 1; i++) { + const newRecord = record.children[i]; + const startDate = createDateAtMidnight(newRecord[startDateField]).getTime(); + const endDate = createDateAtMidnight(newRecord[endDateField]).getTime(); + + let placed = false; + + // 尝试将当前任务放入已有的行中 + for (let j = 0; j < rows.length; j++) { + if (startDate > rows[j]) { + // 如果当前任务的开始日期在该行的结束日期之后,则可以放在这一行 + rows[j] = endDate; + placed = true; + if (i === childIndex) { + return j; + } + break; + } + } + // 如果不能放在已有的行中,则需要新开一行 + if (!placed) { + rows.push(endDate); + } + if (i === childIndex) { + return rows.length - 1; + } + } + } + + return 0; +} diff --git a/packages/vtable-gantt/src/scenegraph/dependency-link.ts b/packages/vtable-gantt/src/scenegraph/dependency-link.ts index b691f0e0e..eab1c1429 100644 --- a/packages/vtable-gantt/src/scenegraph/dependency-link.ts +++ b/packages/vtable-gantt/src/scenegraph/dependency-link.ts @@ -4,7 +4,7 @@ import type { Scenegraph } from './scenegraph'; // import { Icon } from './icon'; import { createDateAtMidnight, parseStringTemplate, toBoxArray } from '../tools/util'; import { isValid } from '@visactor/vutils'; -import { findRecordByTaskKey, getTextPos } from '../gantt-helper'; +import { findRecordByTaskKey, getSubTaskRowIndexByRecordDate, getTextPos } from '../gantt-helper'; import type { GanttTaskBarNode } from './gantt-node'; import type { ITaskLink } from '../ts-types'; import { DependencyType, TasksShowMode } from '../ts-types'; @@ -88,15 +88,36 @@ export class DependencyLink { endDate: linkedFromTaskEndDate, taskDays: linkedFromTaskTaskDays } = this._scene._gantt.getTaskInfoByTaskListIndex(linkedFromTaskRecord.index[0], linkedFromTaskRecord.index[1])); - } else if (this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Separate) { + } else if ( + this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Separate || + this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange + ) { const beforeRowCountLinkedFrom = this._scene._gantt.getRowsHeightByIndex(0, linkedFromTaskRecord.index[0] - 1) / this._scene._gantt.parsedOptions.rowHeight; // 耦合了listTableOption的customComputeRowHeight - linkedFromTaskShowIndex = beforeRowCountLinkedFrom + linkedFromTaskRecord.index[1]; + linkedFromTaskShowIndex = + beforeRowCountLinkedFrom + + (this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange + ? getSubTaskRowIndexByRecordDate( + this._scene._gantt.records[linkedFromTaskRecord.index[0]], + linkedFromTaskRecord.index[1], + this._scene._gantt.parsedOptions.startDateField, + this._scene._gantt.parsedOptions.endDateField + ) + : linkedFromTaskRecord.index[1]); const beforeRowCountLinkedTo = this._scene._gantt.getRowsHeightByIndex(0, linkedToTaskRecord.index[0] - 1) / this._scene._gantt.parsedOptions.rowHeight; // 耦合了listTableOption的customComputeRowHeight - linkedToTaskShowIndex = beforeRowCountLinkedTo + linkedToTaskRecord.index[1]; + linkedToTaskShowIndex = + beforeRowCountLinkedTo + + (this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange + ? getSubTaskRowIndexByRecordDate( + this._scene._gantt.records[linkedToTaskRecord.index[0]], + linkedToTaskRecord.index[1], + this._scene._gantt.parsedOptions.startDateField, + this._scene._gantt.parsedOptions.endDateField + ) + : linkedToTaskRecord.index[1]); ({ startDate: linkedToTaskStartDate, endDate: linkedToTaskEndDate, diff --git a/packages/vtable-gantt/src/scenegraph/scenegraph.ts b/packages/vtable-gantt/src/scenegraph/scenegraph.ts index fa051363a..2091944f9 100644 --- a/packages/vtable-gantt/src/scenegraph/scenegraph.ts +++ b/packages/vtable-gantt/src/scenegraph/scenegraph.ts @@ -129,6 +129,7 @@ export class Scenegraph { this.timelineHeader.refresh(); this.grid.refresh(); this.taskBar.refresh(); + this.dependencyLink.refresh(); this.markLine.refresh(); this.frameBorder.resize(); this.scrollbarComponent.updateScrollBar(); @@ -146,6 +147,7 @@ export class Scenegraph { this.updateNextFrame(); } refreshTaskBarsAndGrid() { + this._gantt.verticalSplitResizeLine.style.height = this._gantt.drawHeight + 'px'; //'100%'; this.tableGroupHeight = Math.min(this._gantt.tableNoFrameHeight, this._gantt.drawHeight); this.tableGroup.setAttribute('height', this.tableGroupHeight); // this.timelineHeader.refresh(); diff --git a/packages/vtable-gantt/src/scenegraph/task-bar.ts b/packages/vtable-gantt/src/scenegraph/task-bar.ts index 8886f32dc..3b4592c44 100644 --- a/packages/vtable-gantt/src/scenegraph/task-bar.ts +++ b/packages/vtable-gantt/src/scenegraph/task-bar.ts @@ -3,7 +3,7 @@ import type { Scenegraph } from './scenegraph'; // import { Icon } from './icon'; import { createDateAtMidnight, parseStringTemplate, toBoxArray } from '../tools/util'; import { isValid } from '@visactor/vutils'; -import { getTextPos } from '../gantt-helper'; +import { computeRowsCountByRecordDate, getSubTaskRowIndexByRecordDate, getTextPos } from '../gantt-helper'; import { GanttTaskBarNode } from './gantt-node'; import { TasksShowMode } from '../ts-types'; @@ -57,7 +57,8 @@ export class TaskBar { for (let i = 0; i < this._scene._gantt.itemCount; i++) { if ( this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Inline || - this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Separate + this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Separate || + this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange ) { const record = this._scene._gantt.getRecordByIndex(i); if (record.children?.length > 0) { @@ -93,7 +94,15 @@ export class TaskBar { const minDate = createDateAtMidnight(this._scene._gantt.parsedOptions.minDate); const oneTaskHeigth = this._scene._gantt.getRowHeightByIndex(index) / - (this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Separate ? childrenLength : 1); + (this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Separate + ? childrenLength + : this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange + ? computeRowsCountByRecordDate( + this._scene._gantt.records[index], + this._scene._gantt.parsedOptions.startDateField, + this._scene._gantt.parsedOptions.endDateField + ) + : 1); const barGroup = new GanttTaskBarNode({ x: this._scene._gantt.parsedOptions.colWidthPerDay * @@ -103,6 +112,13 @@ export class TaskBar { this._scene._gantt.getRowsHeightByIndex(0, index - 1) + (this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Separate ? childIndex * oneTaskHeigth + : this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange + ? getSubTaskRowIndexByRecordDate( + this._scene._gantt.records[index], + childIndex, + this._scene._gantt.parsedOptions.startDateField, + this._scene._gantt.parsedOptions.endDateField + ) * oneTaskHeigth : 0) + (oneTaskHeigth - taskbarHeight) / 2, width: taskBarSize, diff --git a/packages/vtable-gantt/src/state/gantt-table-sync.ts b/packages/vtable-gantt/src/state/gantt-table-sync.ts index ed600107f..0a00402b3 100644 --- a/packages/vtable-gantt/src/state/gantt-table-sync.ts +++ b/packages/vtable-gantt/src/state/gantt-table-sync.ts @@ -29,7 +29,7 @@ export function syncEditCellFromTable(gantt: Gantt) { export function syncTreeChangeFromTable(gantt: Gantt) { gantt.taskListTableInstance?.on('tree_hierarchy_state_change', (args: any) => { gantt._syncPropsFromTable(); - gantt.verticalSplitResizeLine.style.height = gantt.drawHeight + 'px'; //'100%'; + gantt.scenegraph.refreshTaskBarsAndGrid(); const left = gantt.stateManager.scroll.horizontalBarPos; const top = gantt.stateManager.scroll.verticalBarPos; diff --git a/packages/vtable-gantt/src/state/state-manager.ts b/packages/vtable-gantt/src/state/state-manager.ts index 5b6d379b9..35bd5cf7d 100644 --- a/packages/vtable-gantt/src/state/state-manager.ts +++ b/packages/vtable-gantt/src/state/state-manager.ts @@ -11,7 +11,7 @@ import { syncTreeChangeFromTable, syncSortFromTable } from './gantt-table-sync'; -import { findRecordByTaskKey, getTaskIndexByY, getTaskIndexsByTaskY } from '../gantt-helper'; +import { findRecordByTaskKey, getSubTaskRowIndexByRecordDate, getTaskIndexsByTaskY } from '../gantt-helper'; import { debounce } from '../tools/debounce'; import type { GanttTaskBarNode } from '../scenegraph/gantt-node'; import { TASKBAR_HOVER_ICON_WIDTH } from '../scenegraph/task-bar'; @@ -360,9 +360,7 @@ export class StateManager { }); } } - // 判断纵向拖动 处理数据的位置 - if (Math.abs(Math.round(deltaY / this._gantt.parsedOptions.rowHeight)) >= 1) { - //TODO 移动位置的数据更新到record中 + if (this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange) { const indexs = getTaskIndexsByTaskY(targetEndY, this._gantt); this._gantt._dragOrderTaskRecord( target.task_index, @@ -370,53 +368,66 @@ export class StateManager { indexs.task_index, indexs.sub_task_index ); - if (this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Separate) { - this._gantt.taskListTableInstance.renderWithRecreateCells(); - this._gantt.scenegraph.refreshTaskBarsAndGrid(); - } else { - this._gantt.scenegraph.taskBar.refresh(); - this._gantt.scenegraph.dependencyLink.refresh(); - } - // target = this._gantt.scenegraph.taskBar.getTaskBarNodeByIndex(indexs.task_index, indexs.sub_task_index); + this._gantt.taskListTableInstance.renderWithRecreateCells(); + this._gantt._syncPropsFromTable(); + this._gantt.scenegraph.refreshTaskBarsAndGrid(); } else { - resizeOrMoveTaskBar( - target, - targetEndX - (target as Group).attribute.x, - targetEndY - (target as Group).attribute.y, - null, - this - ); - - // 为了确保拖拽后 保持startDate日期晚的显示在上层不被盖住 这里需要重新排序一下 - if (days > 0) { - let insertAfterNode = target; - while ( - (insertAfterNode as Group).nextSibling && - (insertAfterNode as Group).nextSibling.attribute.y === (target as Group).attribute.y && - (insertAfterNode as Group).nextSibling.record[this._gantt.parsedOptions.startDateField] <= - target.record[this._gantt.parsedOptions.startDateField] - ) { - insertAfterNode = (insertAfterNode as Group).nextSibling; - } - if (insertAfterNode !== target) { - (insertAfterNode as Group).parent.insertAfter(target, insertAfterNode); - } - } else if (days < 0) { - let insertBeforeNode = target; - while ( - (insertBeforeNode as Group).previousSibling && - (insertBeforeNode as Group).previousSibling.attribute.y === (target as Group).attribute.y && - (insertBeforeNode as Group).previousSibling.record[this._gantt.parsedOptions.startDateField] >= - target.record[this._gantt.parsedOptions.startDateField] - ) { - insertBeforeNode = (insertBeforeNode as Group).previousSibling; + // 判断纵向拖动 处理数据的位置 + if (Math.abs(Math.round(deltaY / this._gantt.parsedOptions.rowHeight)) >= 1) { + const indexs = getTaskIndexsByTaskY(targetEndY, this._gantt); + this._gantt._dragOrderTaskRecord( + target.task_index, + target.sub_task_index, + indexs.task_index, + indexs.sub_task_index + ); + if (this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Separate) { + this._gantt.taskListTableInstance.renderWithRecreateCells(); + this._gantt.scenegraph.refreshTaskBarsAndGrid(); + } else { + this._gantt.scenegraph.taskBar.refresh(); + this._gantt.scenegraph.dependencyLink.refresh(); } - if (insertBeforeNode !== target) { - (insertBeforeNode as Group).parent.insertBefore(target, insertBeforeNode); + // target = this._gantt.scenegraph.taskBar.getTaskBarNodeByIndex(indexs.task_index, indexs.sub_task_index); + } else { + resizeOrMoveTaskBar( + target, + targetEndX - (target as Group).attribute.x, + targetEndY - (target as Group).attribute.y, + null, + this + ); + + // 为了确保拖拽后 保持startDate日期晚的显示在上层不被盖住 这里需要重新排序一下 + if (days > 0) { + let insertAfterNode = target; + while ( + (insertAfterNode as Group).nextSibling && + (insertAfterNode as Group).nextSibling.attribute.y === (target as Group).attribute.y && + (insertAfterNode as Group).nextSibling.record[this._gantt.parsedOptions.startDateField] <= + target.record[this._gantt.parsedOptions.startDateField] + ) { + insertAfterNode = (insertAfterNode as Group).nextSibling; + } + if (insertAfterNode !== target) { + (insertAfterNode as Group).parent.insertAfter(target, insertAfterNode); + } + } else if (days < 0) { + let insertBeforeNode = target; + while ( + (insertBeforeNode as Group).previousSibling && + (insertBeforeNode as Group).previousSibling.attribute.y === (target as Group).attribute.y && + (insertBeforeNode as Group).previousSibling.record[this._gantt.parsedOptions.startDateField] >= + target.record[this._gantt.parsedOptions.startDateField] + ) { + insertBeforeNode = (insertBeforeNode as Group).previousSibling; + } + if (insertBeforeNode !== target) { + (insertBeforeNode as Group).parent.insertBefore(target, insertBeforeNode); + } } } } - this._gantt.scenegraph.updateNextFrame(); this.moveTaskBar.moving = false; if (this.selectedTaskBar.target !== target) { @@ -789,7 +800,8 @@ export class StateManager { const linkedFromTaskRecord = findRecordByTaskKey(this._gantt.records, taskKeyField, linkedFromTaskKey); if ( this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Inline || - this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Separate + this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Separate || + this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange ) { linkFrom_index = linkedFromTaskRecord.index[0]; linkFrom_sub_task_index = linkedFromTaskRecord.index[1]; @@ -953,17 +965,38 @@ function resizeOrMoveTaskBar(target: GanttTaskBarNode, dx: number, dy: number, n const taskbarHeight = state._gantt.parsedOptions.taskBarStyle.width; diffY = target.attribute.y + taskbarHeight / 2 - (linkedToTaskShowIndex + 0.5) * state._gantt.parsedOptions.rowHeight; - } else if (state._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Separate) { + } else if ( + state._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Separate || + state._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange + ) { const new_indexs = getTaskIndexsByTaskY(target.attribute.y + dy, state._gantt); const beforeRowCountLinkedFrom = state._gantt.getRowsHeightByIndex(0, linkedFromTaskRecord.index[0] - 1) / state._gantt.parsedOptions.rowHeight; // 耦合了listTableOption的customComputeRowHeight - linkedFromTaskShowIndex = beforeRowCountLinkedFrom + linkedFromTaskRecord.index[1]; + linkedFromTaskShowIndex = + beforeRowCountLinkedFrom + + (state._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange + ? getSubTaskRowIndexByRecordDate( + state._gantt.records[linkedFromTaskRecord.index[0]], + linkedFromTaskRecord.index[1], + state._gantt.parsedOptions.startDateField, + state._gantt.parsedOptions.endDateField + ) + : linkedFromTaskRecord.index[1]); // const beforeRowCountLinkedTo = // state._gantt.getRowsHeightByIndex(0, linkedToTaskRecord.index[0] - 1) / state._gantt.parsedOptions.rowHeight; // 耦合了listTableOption的customComputeRowHeight // linkedToTaskShowIndex = beforeRowCountLinkedTo + linkedToTaskRecord.index[1]; const beforeRowCountLinkedTo = state._gantt.getRowsHeightByIndex(0, new_indexs.task_index - 1) / state._gantt.parsedOptions.rowHeight; // 耦合了listTableOption的customComputeRowHeight - linkedToTaskShowIndex = beforeRowCountLinkedTo + new_indexs.sub_task_index; + linkedToTaskShowIndex = + beforeRowCountLinkedTo + + (state._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange + ? getSubTaskRowIndexByRecordDate( + state._gantt.records[new_indexs.task_index], + new_indexs.sub_task_index, + state._gantt.parsedOptions.startDateField, + state._gantt.parsedOptions.endDateField + ) + : new_indexs.sub_task_index); ({ startDate: linkedToTaskStartDate, @@ -1060,18 +1093,39 @@ function resizeOrMoveTaskBar(target: GanttTaskBarNode, dx: number, dy: number, n const taskbarHeight = state._gantt.parsedOptions.taskBarStyle.width; diffY = target.attribute.y + taskbarHeight / 2 - (linkedFromTaskShowIndex + 0.5) * state._gantt.parsedOptions.rowHeight; - } else if (state._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Separate) { + } else if ( + state._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Separate || + state._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange + ) { const new_indexs = getTaskIndexsByTaskY(target.attribute.y + dy, state._gantt); // const beforeRowCountLinkedFrom = // state._gantt.getRowsHeightByIndex(0, linkedFromTaskRecord.index[0] - 1) / state._gantt.parsedOptions.rowHeight; // 耦合了listTableOption的customComputeRowHeight // linkedFromTaskShowIndex = beforeRowCountLinkedFrom + linkedFromTaskRecord.index[1]; const beforeRowCountLinkedFrom = state._gantt.getRowsHeightByIndex(0, new_indexs.task_index - 1) / state._gantt.parsedOptions.rowHeight; // 耦合了listTableOption的customComputeRowHeight - linkedFromTaskShowIndex = beforeRowCountLinkedFrom + new_indexs.sub_task_index; + linkedFromTaskShowIndex = + beforeRowCountLinkedFrom + + (state._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange + ? getSubTaskRowIndexByRecordDate( + state._gantt.records[new_indexs.task_index], + new_indexs.sub_task_index, + state._gantt.parsedOptions.startDateField, + state._gantt.parsedOptions.endDateField + ) + : new_indexs.sub_task_index); const beforeRowCountLinkedTo = state._gantt.getRowsHeightByIndex(0, linkedToTaskRecord.index[0] - 1) / state._gantt.parsedOptions.rowHeight; // 耦合了listTableOption的customComputeRowHeight - linkedToTaskShowIndex = beforeRowCountLinkedTo + linkedToTaskRecord.index[1]; + linkedToTaskShowIndex = + beforeRowCountLinkedTo + + (state._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange + ? getSubTaskRowIndexByRecordDate( + state._gantt.records[linkedToTaskRecord.index[0]], + linkedToTaskRecord.index[1], + state._gantt.parsedOptions.startDateField, + state._gantt.parsedOptions.endDateField + ) + : linkedToTaskRecord.index[1]); ({ startDate: linkedToTaskStartDate, endDate: linkedToTaskEndDate, From 24614ee61abeacab58a630f881754b491036b047 Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Mon, 25 Nov 2024 19:58:46 +0800 Subject: [PATCH 13/40] feat: resize taskbar recreate nodes --- packages/vtable-gantt/src/Gantt.ts | 2 +- packages/vtable-gantt/src/gantt-helper.ts | 11 ++++ .../src/scenegraph/dependency-link.ts | 3 +- .../vtable-gantt/src/state/state-manager.ts | 61 +++++++++++-------- packages/vtable-gantt/src/tools/util.ts | 4 +- 5 files changed, 51 insertions(+), 30 deletions(-) diff --git a/packages/vtable-gantt/src/Gantt.ts b/packages/vtable-gantt/src/Gantt.ts index 1f25a90f5..a02bd0a38 100644 --- a/packages/vtable-gantt/src/Gantt.ts +++ b/packages/vtable-gantt/src/Gantt.ts @@ -824,7 +824,7 @@ export class Gantt extends EventTarget { const taskRecord = this.getRecordByIndex(index, sub_task_index); const startDateField = this.parsedOptions.startDateField; const endDateField = this.parsedOptions.endDateField; - const dateFormat = this.parsedOptions.dateFormat ?? parseDateFormat(taskRecord[startDateField]); + const dateFormat = this.parsedOptions.dateFormat ?? parseDateFormat(taskRecord[endDateField]); const startDate = createDateAtMidnight(taskRecord[startDateField]); const endDate = createDateAtMidnight(taskRecord[endDateField]); if (updateDateType === 'move') { diff --git a/packages/vtable-gantt/src/gantt-helper.ts b/packages/vtable-gantt/src/gantt-helper.ts index 61b7b5bf2..85ec0d65a 100644 --- a/packages/vtable-gantt/src/gantt-helper.ts +++ b/packages/vtable-gantt/src/gantt-helper.ts @@ -641,6 +641,17 @@ export function findRecordByTaskKey( } } +export function clearRecordLinkInfos(records: any[], childrenField: string = 'children') { + for (let i = 0; i < records.length; i++) { + if (records[i][childrenField]?.length) { + clearRecordLinkInfos(records[i][childrenField], childrenField); + } else { + delete records[i].vtable_gantt_linkedTo; + delete records[i].vtable_gantt_linkedFrom; + } + } +} + export function getTaskIndexsByTaskY(y: number, gantt: Gantt) { let task_index; let sub_task_index; diff --git a/packages/vtable-gantt/src/scenegraph/dependency-link.ts b/packages/vtable-gantt/src/scenegraph/dependency-link.ts index eab1c1429..b19a449fe 100644 --- a/packages/vtable-gantt/src/scenegraph/dependency-link.ts +++ b/packages/vtable-gantt/src/scenegraph/dependency-link.ts @@ -4,7 +4,7 @@ import type { Scenegraph } from './scenegraph'; // import { Icon } from './icon'; import { createDateAtMidnight, parseStringTemplate, toBoxArray } from '../tools/util'; import { isValid } from '@visactor/vutils'; -import { findRecordByTaskKey, getSubTaskRowIndexByRecordDate, getTextPos } from '../gantt-helper'; +import { clearRecordLinkInfos, findRecordByTaskKey, getSubTaskRowIndexByRecordDate, getTextPos } from '../gantt-helper'; import type { GanttTaskBarNode } from './gantt-node'; import type { ITaskLink } from '../ts-types'; import { DependencyType, TasksShowMode } from '../ts-types'; @@ -36,6 +36,7 @@ export class DependencyLink { } initLinkLines() { + clearRecordLinkInfos(this._scene._gantt.records, 'children'); this.linkLinesContainer = new Group({ x: 0, y: 0, diff --git a/packages/vtable-gantt/src/state/state-manager.ts b/packages/vtable-gantt/src/state/state-manager.ts index 35bd5cf7d..a4c3cc33c 100644 --- a/packages/vtable-gantt/src/state/state-manager.ts +++ b/packages/vtable-gantt/src/state/state-manager.ts @@ -332,6 +332,9 @@ export class StateManager { const deltaX = this.moveTaskBar.deltaX; const deltaY = this.moveTaskBar.deltaY; + if (Math.abs(deltaX) < 1 && Math.abs(deltaY) < 1) { + return; + } const days = Math.round(deltaX / this._gantt.parsedOptions.colWidthPerDay); const correctX = days * this._gantt.parsedOptions.colWidthPerDay; @@ -536,29 +539,35 @@ export class StateManager { if (diff_days < 0 && taskDays + diff_days <= 0) { diff_days = 1 - taskDays; } - const correctX = (direction === 'left' ? -diff_days : diff_days) * this._gantt.parsedOptions.colWidthPerDay; - const targetEndX = this.resizeTaskBar.targetStartX + correctX; - - const taskBarSize = this._gantt.parsedOptions.colWidthPerDay * (taskDays + diff_days); - if (direction === 'left') { - // taskBarGroup.setAttribute('x', targetEndX); - // taskBarGroup.setAttribute('width', taskBarSize); - resizeOrMoveTaskBar(taskBarGroup, targetEndX - taskBarGroup.attribute.x, 0, taskBarSize, this); - rect?.setAttribute('width', taskBarGroup.attribute.width); - progressRect?.setAttribute('width', (progress / 100) * taskBarGroup.attribute.width); - this._gantt._updateDateToTaskRecord('start-move', -diff_days, taskIndex, sub_task_index); - } else if (direction === 'right') { - // taskBarGroup.setAttribute('width', taskBarSize); - resizeOrMoveTaskBar(taskBarGroup, 0, 0, taskBarSize, this); - rect?.setAttribute('width', taskBarGroup.attribute.width); - progressRect?.setAttribute('width', (progress / 100) * taskBarGroup.attribute.width); - this._gantt._updateDateToTaskRecord('end-move', diff_days, taskIndex, sub_task_index); - } - this.showTaskBarHover(); - reCreateCustomNode(this._gantt, taskBarGroup, taskIndex, sub_task_index); - - taskBarGroup.setAttribute('zIndex', 0); + this._gantt._updateDateToTaskRecord( + direction === 'left' ? 'start-move' : 'end-move', + direction === 'left' ? -diff_days : diff_days, + taskIndex, + sub_task_index + ); + if (this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange) { + this._gantt.taskListTableInstance.renderWithRecreateCells(); + this._gantt._syncPropsFromTable(); + this._gantt.scenegraph.refreshTaskBarsAndGrid(); + } else { + const correctX = (direction === 'left' ? -diff_days : diff_days) * this._gantt.parsedOptions.colWidthPerDay; + const targetEndX = this.resizeTaskBar.targetStartX + correctX; + + const taskBarSize = this._gantt.parsedOptions.colWidthPerDay * (taskDays + diff_days); + if (direction === 'left') { + resizeOrMoveTaskBar(taskBarGroup, targetEndX - taskBarGroup.attribute.x, 0, taskBarSize, this); + rect?.setAttribute('width', taskBarGroup.attribute.width); + progressRect?.setAttribute('width', (progress / 100) * taskBarGroup.attribute.width); + } else if (direction === 'right') { + resizeOrMoveTaskBar(taskBarGroup, 0, 0, taskBarSize, this); + rect?.setAttribute('width', taskBarGroup.attribute.width); + progressRect?.setAttribute('width', (progress / 100) * taskBarGroup.attribute.width); + } + this.showTaskBarHover(); + reCreateCustomNode(this._gantt, taskBarGroup, taskIndex, sub_task_index); + taskBarGroup.setAttribute('zIndex', 0); + } this.resizeTaskBar.resizing = false; this.resizeTaskBar.target = null; @@ -991,8 +1000,8 @@ function resizeOrMoveTaskBar(target: GanttTaskBarNode, dx: number, dy: number, n beforeRowCountLinkedTo + (state._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange ? getSubTaskRowIndexByRecordDate( - state._gantt.records[new_indexs.task_index], - new_indexs.sub_task_index, + state._gantt.records[linkedToTaskRecord.index[0]], + linkedToTaskRecord.index[1], state._gantt.parsedOptions.startDateField, state._gantt.parsedOptions.endDateField ) @@ -1107,8 +1116,8 @@ function resizeOrMoveTaskBar(target: GanttTaskBarNode, dx: number, dy: number, n beforeRowCountLinkedFrom + (state._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange ? getSubTaskRowIndexByRecordDate( - state._gantt.records[new_indexs.task_index], - new_indexs.sub_task_index, + state._gantt.records[linkedFromTaskRecord.index[0]], + linkedFromTaskRecord.index[1], state._gantt.parsedOptions.startDateField, state._gantt.parsedOptions.endDateField ) diff --git a/packages/vtable-gantt/src/tools/util.ts b/packages/vtable-gantt/src/tools/util.ts index 8b758890a..93dbd4c28 100644 --- a/packages/vtable-gantt/src/tools/util.ts +++ b/packages/vtable-gantt/src/tools/util.ts @@ -158,8 +158,8 @@ export function parseDateFormat(dateString: string) { 'yyyy/mm/dd', 'dd/mm/yyyy', 'yyyy.mm.dd', - 'dd.mm.yyyy', - 'mm.dd.yyyy' + 'mm.dd.yyyy', + 'dd.mm.yyyy' ]; dateString = dateString.replace(/\s+/g, ''); // 移除空格 for (let i = 0; i < formats.length; i++) { From f305c040dd0b53d367a70e1261084854adab4441 Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Tue, 26 Nov 2024 20:04:50 +0800 Subject: [PATCH 14/40] feat: add show mode sub_tasks_compact --- .../gantt/gantt-Arrange-customLayout.ts | 427 ++++++++++++++++++ .../gantt/gantt-Compact-customLayout.ts | 427 ++++++++++++++++++ ...Inline.ts => gantt-Inline-customLayout.ts} | 0 ...asks.ts => gantt-Separate-customLayout.ts} | 0 .../gantt/gantt-Sub_Tasks_Separate.ts | 2 +- packages/vtable-gantt/examples/menu.ts | 18 +- packages/vtable-gantt/src/Gantt.ts | 20 +- packages/vtable-gantt/src/data/DataSource.ts | 32 +- .../vtable-gantt/src/event/event-manager.ts | 7 +- packages/vtable-gantt/src/gantt-helper.ts | 75 ++- .../src/scenegraph/dependency-link.ts | 37 +- .../vtable-gantt/src/scenegraph/task-bar.ts | 41 +- .../vtable-gantt/src/state/state-manager.ts | 267 ++++++----- .../vtable-gantt/src/ts-types/gantt-engine.ts | 6 +- 14 files changed, 1187 insertions(+), 172 deletions(-) create mode 100644 packages/vtable-gantt/examples/gantt/gantt-Arrange-customLayout.ts create mode 100644 packages/vtable-gantt/examples/gantt/gantt-Compact-customLayout.ts rename packages/vtable-gantt/examples/gantt/{gantt-customLayout-Sub_Tasks_Inline.ts => gantt-Inline-customLayout.ts} (100%) rename packages/vtable-gantt/examples/gantt/{gantt-customLayout-Sub_Tasks.ts => gantt-Separate-customLayout.ts} (100%) diff --git a/packages/vtable-gantt/examples/gantt/gantt-Arrange-customLayout.ts b/packages/vtable-gantt/examples/gantt/gantt-Arrange-customLayout.ts new file mode 100644 index 000000000..83d6ff01e --- /dev/null +++ b/packages/vtable-gantt/examples/gantt/gantt-Arrange-customLayout.ts @@ -0,0 +1,427 @@ +import type { ColumnsDefine } from '@visactor/vtable'; +import { register } from '@visactor/vtable'; +import { DateInputEditor, InputEditor } from '@visactor/vtable-editors'; +import type { GanttConstructorOptions, TYPES } from '../../src/index'; +import { Group, Image, Text } from '@visactor/vtable/es/vrender'; +import { Gantt } from '../../src/index'; +import { bindDebugTool } from '../../../vtable/src/scenegraph/debug-tool'; +import { scale } from '@visactor/vutils'; +import { DependencyType, TasksShowMode } from '../../src/ts-types'; +const CONTAINER_ID = 'vTable'; + +const barColors0 = ['#aecde6', '#c6a49a', '#ffb582', '#eec1de', '#b3d9b3', '#d9d1a5', '#cccccc', '#e59a9c', '#c9bede']; +const barColors = ['#1f77b4', '#8c564b', '#ff7f0e', '#e377c2', '#2ca02c', '#bcbd22', '#7f7f7f', '#d62728', '#9467bd']; +export function createTable() { + const records = [ + { + title: 'Planning', + children: [ + { + id: 2, + name: 'Michael Smith', + start: '2024-11-15', + end: '2024-11-17', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + }, + { + id: 1, + name: 'Emily', + start: '2024-11-17', + end: '2024-11-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + + { + id: 3, + name: 'Rramily', + start: '2024-11-19', + end: '2024-11-20', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 4, + name: 'Lichael Join', + start: '2024-11-18', + end: '2024-11-19', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + } + ] + }, + { + id: 300, + title: 'Research', + children: [ + { + id: 5, + name: 'Ryan', + start: '2024-11-18', + end: '2024-11-21', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/rabbit.jpg' + } + ] + }, + { + title: 'Goal Setting', + children: [ + { + id: 6, + name: 'Daniel Davis', + start: '2024-11-21', + end: '2024-11-22', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/cat.jpg' + }, + { + id: 7, + name: 'Lauren', + start: '2024-11-18', + end: '2024-11-19', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg' + } + ] + }, + + { + title: 'Strategy', + children: [ + { + id: 8, + name: 'Tacarah Siller', + start: '2024-11-20', + end: '2024-11-21', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 9, + name: 'Camentew Olision', + start: '2024-11-25', + end: '2024-11-26', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + }, + { + id: 10, + name: 'Sarah Miller', + start: '2024-11-17', + end: '2024-11-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 11, + name: 'Matthew Wilson', + start: '2024-11-22', + end: '2024-11-25', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + }, + { + id: 12, + name: 'Grarah Poliller', + start: '2024-11-23', + end: '2024-11-24', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + } + ] + }, + { + title: 'Execution', + children: [ + { + id: 13, + name: 'Ashley Taylor', + start: '2024-11-22', + end: '2024-11-25', + + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/rabbit.jpg' + }, + { + id: 14, + name: 'Megan', + start: '2024-11-27', + end: '2024-11-30', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/cat.jpg' + }, + { + id: 15, + name: 'David', + start: '2024-12-10', + end: '2024-12-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg' + } + ] + }, + { + title: 'Monitoring', + children: [ + { + id: 16, + name: 'Hannah', + start: '2024-11-20', + end: '2024-11-30', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 17, + name: 'Andrew', + start: '2024-12-02', + end: '2024-12-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + } + ] + }, + { + title: 'Reporting', + children: [ + { + id: 18, + name: 'Joshua Anderson', + start: '2024-12-22', + end: '2024-12-28', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/rabbit.jpg' + } + ] + }, + { + title: 'Process review', + children: [ + { + id: 19, + name: 'Christopher Moore', + start: '2024-11-25', + end: '2024-11-30', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/cat.jpg' + }, + { + id: 20, + name: 'Emma', + start: '2024-12-01', + end: '2024-12-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg' + } + ] + } + ]; + + const columns: ColumnsDefine = [ + { + field: 'title', + title: 'PROCESS', + width: 150 + } + ]; + const option: GanttConstructorOptions = { + records, + taskListTable: { + columns: columns, + minTableWidth: 100, + hierarchyExpandLevel: 5, + menu: { + contextMenuItems: ['copy', 'paste', 'delete', '...'] + } + }, + groupBy: true, + tasksShowMode: TasksShowMode.Sub_Tasks_Arrange, + frame: { + outerFrameStyle: { + borderLineWidth: 1, + borderColor: '#e1e4e8', + cornerRadius: 8 + }, + verticalSplitLineMoveable: false + }, + grid: { + horizontalLine: { + lineWidth: 1, + lineColor: '#e1e4e8' + }, + verticalLine: { + lineWidth: 1, + lineColor: '#e1e4e8' + } + }, + headerRowHeight: 60, + rowHeight: 80, + taskBar: { + startDateField: 'start', + endDateField: 'end', + progressField: 'progress', + labelText: '{name}', + labelTextStyle: { + fontFamily: 'Arial', + fontSize: 16, + textAlign: 'left' + }, + barStyle: { + width: 50, + /** 任务条的颜色 */ + barColor: '#ee8800', + /** 已完成部分任务条的颜色 */ + completedBarColor: '#91e8e0', + /** 任务条的圆角 */ + cornerRadius: 25 + }, + customLayout: (args: any) => { + const colorLength = barColors.length; + const { width, height, index, startDate, endDate, taskDays, progress, taskRecord, ganttInstance } = args; + const container = new Group({ + width, + height, + fill: { + gradient: 'linear', + x0: 0, + y0: 0, + x1: 1, + y1: 0, + stops: [ + { + offset: 0, + color: barColors0[index % colorLength] + }, + { + offset: 0.5, + color: barColors[index % colorLength] + }, + { + offset: 1, + color: barColors0[index % colorLength] + } + ] + }, + display: 'flex', + flexDirection: 'row', + flexWrap: 'nowrap' + }); + const containerLeft = new Group({ + height, + width: 60, + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'space-around' + // fill: 'red' + }); + container.add(containerLeft); + + const icon0 = new Image({ + width: 40, + height: 40, + image: taskRecord.avatar, + cornerRadius: 20 + }); + containerLeft.add(icon0); + + const containerRight = new Group({ + height, + width: width - 60, + display: 'flex', + flexDirection: 'column' + // alignItems: 'left' + }); + container.add(containerRight); + + const bloggerName = new Text({ + text: taskRecord.name + ' ' + taskRecord.id, + fontSize: 16, + fontFamily: 'sans-serif', + fill: 'white', + maxLineWidth: width - 60, + boundsPadding: [10, 0, 0, 0] + }); + containerRight.add(bloggerName); + + const days = new Text({ + text: `${taskDays}天`, + fontSize: 13, + fontFamily: 'sans-serif', + fill: 'white', + boundsPadding: [10, 0, 0, 0] + }); + containerRight.add(days); + return { + rootContainer: container + // renderDefaultBar: true + // renderDefaultText: true + }; + } + }, + dependency: { + linkCreatable: true, + links: [ + { + type: DependencyType.StartToStart, + linkedFromTaskKey: 1, + linkedToTaskKey: 2 + }, + { + type: DependencyType.FinishToStart, + linkedFromTaskKey: 2, + linkedToTaskKey: 3 + }, + { + type: DependencyType.StartToStart, + linkedFromTaskKey: 3, + linkedToTaskKey: 5 + }, + { + type: DependencyType.FinishToFinish, + linkedFromTaskKey: 5, + linkedToTaskKey: 4 + }, + { + type: DependencyType.StartToStart, + linkedFromTaskKey: 8, + linkedToTaskKey: 9 + }, + { + type: DependencyType.FinishToStart, + linkedFromTaskKey: 9, + linkedToTaskKey: 10 + } + ] + }, + timelineHeader: { + verticalLine: { + lineColor: '#e1e4e8', + lineWidth: 1 + }, + horizontalLine: { + lineColor: '#e1e4e8', + lineWidth: 1 + }, + backgroundColor: '#63a8ff', + scales: [ + { + unit: 'day', + step: 1, + format(date: TYPES.DateFormatArgumentType) { + return date.dateIndex.toString(); + }, + style: { + fontSize: 20, + fontWeight: 'bold', + color: 'red' + } + } + ] + }, + minDate: '2024-11-14', + maxDate: '2024-12-31', + scrollStyle: { + scrollRailColor: 'RGBA(246,246,246,0.5)', + visible: 'none', + width: 6, + scrollSliderCornerRadius: 2, + scrollSliderColor: '#5cb85c' + } + }; + + const ganttInstance = new Gantt(document.getElementById(CONTAINER_ID)!, option); + window.ganttInstance = ganttInstance; + ganttInstance.on('scroll', e => { + console.log('scroll', e); + }); + + ganttInstance.listTableInstance?.on('scroll', e => { + console.log('listTable scroll', e); + }); + // bindDebugTool(ganttInstance.scenegraph.stage as any, { + // customGrapicKeys: ['role', '_updateTag'] + // }); +} diff --git a/packages/vtable-gantt/examples/gantt/gantt-Compact-customLayout.ts b/packages/vtable-gantt/examples/gantt/gantt-Compact-customLayout.ts new file mode 100644 index 000000000..1ffc3c198 --- /dev/null +++ b/packages/vtable-gantt/examples/gantt/gantt-Compact-customLayout.ts @@ -0,0 +1,427 @@ +import type { ColumnsDefine } from '@visactor/vtable'; +import { register } from '@visactor/vtable'; +import { DateInputEditor, InputEditor } from '@visactor/vtable-editors'; +import type { GanttConstructorOptions, TYPES } from '../../src/index'; +import { Group, Image, Text } from '@visactor/vtable/es/vrender'; +import { Gantt } from '../../src/index'; +import { bindDebugTool } from '../../../vtable/src/scenegraph/debug-tool'; +import { scale } from '@visactor/vutils'; +import { DependencyType, TasksShowMode } from '../../src/ts-types'; +const CONTAINER_ID = 'vTable'; + +const barColors0 = ['#aecde6', '#c6a49a', '#ffb582', '#eec1de', '#b3d9b3', '#d9d1a5', '#cccccc', '#e59a9c', '#c9bede']; +const barColors = ['#1f77b4', '#8c564b', '#ff7f0e', '#e377c2', '#2ca02c', '#bcbd22', '#7f7f7f', '#d62728', '#9467bd']; +export function createTable() { + const records = [ + { + title: 'Planning', + children: [ + { + id: 2, + name: 'Michael Smith', + start: '2024-11-15', + end: '2024-11-17', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + }, + { + id: 1, + name: 'Emily', + start: '2024-11-17', + end: '2024-11-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + + { + id: 3, + name: 'Rramily', + start: '2024-11-19', + end: '2024-11-20', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 4, + name: 'Lichael Join', + start: '2024-11-18', + end: '2024-11-19', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + } + ] + }, + { + id: 300, + title: 'Research', + children: [ + { + id: 5, + name: 'Ryan', + start: '2024-11-18', + end: '2024-11-21', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/rabbit.jpg' + } + ] + }, + { + title: 'Goal Setting', + children: [ + { + id: 6, + name: 'Daniel Davis', + start: '2024-11-21', + end: '2024-11-22', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/cat.jpg' + }, + { + id: 7, + name: 'Lauren', + start: '2024-11-18', + end: '2024-11-19', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg' + } + ] + }, + + { + title: 'Strategy', + children: [ + { + id: 8, + name: 'Tacarah Siller', + start: '2024-11-20', + end: '2024-11-21', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 9, + name: 'Camentew Olision', + start: '2024-11-25', + end: '2024-11-26', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + }, + { + id: 10, + name: 'Sarah Miller', + start: '2024-11-17', + end: '2024-11-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 11, + name: 'Matthew Wilson', + start: '2024-11-22', + end: '2024-11-25', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + }, + { + id: 12, + name: 'Grarah Poliller', + start: '2024-11-23', + end: '2024-11-24', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + } + ] + }, + { + title: 'Execution', + children: [ + { + id: 13, + name: 'Ashley Taylor', + start: '2024-11-22', + end: '2024-11-25', + + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/rabbit.jpg' + }, + { + id: 14, + name: 'Megan', + start: '2024-11-27', + end: '2024-11-30', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/cat.jpg' + }, + { + id: 15, + name: 'David', + start: '2024-12-10', + end: '2024-12-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg' + } + ] + }, + { + title: 'Monitoring', + children: [ + { + id: 16, + name: 'Hannah', + start: '2024-11-20', + end: '2024-11-30', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 17, + name: 'Andrew', + start: '2024-12-02', + end: '2024-12-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + } + ] + }, + { + title: 'Reporting', + children: [ + { + id: 18, + name: 'Joshua Anderson', + start: '2024-12-22', + end: '2024-12-28', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/rabbit.jpg' + } + ] + }, + { + title: 'Process review', + children: [ + { + id: 19, + name: 'Christopher Moore', + start: '2024-11-25', + end: '2024-11-30', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/cat.jpg' + }, + { + id: 20, + name: 'Emma', + start: '2024-12-01', + end: '2024-12-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg' + } + ] + } + ]; + + const columns: ColumnsDefine = [ + { + field: 'title', + title: 'PROCESS', + width: 150 + } + ]; + const option: GanttConstructorOptions = { + records, + taskListTable: { + columns: columns, + minTableWidth: 100, + hierarchyExpandLevel: 5, + menu: { + contextMenuItems: ['copy', 'paste', 'delete', '...'] + } + }, + groupBy: true, + tasksShowMode: TasksShowMode.Sub_Tasks_Compact, + frame: { + outerFrameStyle: { + borderLineWidth: 1, + borderColor: '#e1e4e8', + cornerRadius: 8 + }, + verticalSplitLineMoveable: false + }, + grid: { + horizontalLine: { + lineWidth: 1, + lineColor: '#e1e4e8' + }, + verticalLine: { + lineWidth: 1, + lineColor: '#e1e4e8' + } + }, + headerRowHeight: 60, + rowHeight: 80, + taskBar: { + startDateField: 'start', + endDateField: 'end', + progressField: 'progress', + labelText: '{name}', + labelTextStyle: { + fontFamily: 'Arial', + fontSize: 16, + textAlign: 'left' + }, + barStyle: { + width: 50, + /** 任务条的颜色 */ + barColor: '#ee8800', + /** 已完成部分任务条的颜色 */ + completedBarColor: '#91e8e0', + /** 任务条的圆角 */ + cornerRadius: 25 + }, + customLayout: (args: any) => { + const colorLength = barColors.length; + const { width, height, index, startDate, endDate, taskDays, progress, taskRecord, ganttInstance } = args; + const container = new Group({ + width, + height, + fill: { + gradient: 'linear', + x0: 0, + y0: 0, + x1: 1, + y1: 0, + stops: [ + { + offset: 0, + color: barColors0[index % colorLength] + }, + { + offset: 0.5, + color: barColors[index % colorLength] + }, + { + offset: 1, + color: barColors0[index % colorLength] + } + ] + }, + display: 'flex', + flexDirection: 'row', + flexWrap: 'nowrap' + }); + const containerLeft = new Group({ + height, + width: 60, + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'space-around' + // fill: 'red' + }); + container.add(containerLeft); + + const icon0 = new Image({ + width: 40, + height: 40, + image: taskRecord.avatar, + cornerRadius: 20 + }); + containerLeft.add(icon0); + + const containerRight = new Group({ + height, + width: width - 60, + display: 'flex', + flexDirection: 'column' + // alignItems: 'left' + }); + container.add(containerRight); + + const bloggerName = new Text({ + text: taskRecord.name + ' ' + taskRecord.id, + fontSize: 16, + fontFamily: 'sans-serif', + fill: 'white', + maxLineWidth: width - 60, + boundsPadding: [10, 0, 0, 0] + }); + containerRight.add(bloggerName); + + const days = new Text({ + text: `${taskDays}天`, + fontSize: 13, + fontFamily: 'sans-serif', + fill: 'white', + boundsPadding: [10, 0, 0, 0] + }); + containerRight.add(days); + return { + rootContainer: container + // renderDefaultBar: true + // renderDefaultText: true + }; + } + }, + dependency: { + linkCreatable: true, + links: [ + { + type: DependencyType.StartToStart, + linkedFromTaskKey: 1, + linkedToTaskKey: 2 + }, + { + type: DependencyType.FinishToStart, + linkedFromTaskKey: 2, + linkedToTaskKey: 3 + }, + { + type: DependencyType.StartToStart, + linkedFromTaskKey: 3, + linkedToTaskKey: 5 + }, + { + type: DependencyType.FinishToFinish, + linkedFromTaskKey: 5, + linkedToTaskKey: 4 + }, + { + type: DependencyType.StartToStart, + linkedFromTaskKey: 8, + linkedToTaskKey: 9 + }, + { + type: DependencyType.FinishToStart, + linkedFromTaskKey: 9, + linkedToTaskKey: 10 + } + ] + }, + timelineHeader: { + verticalLine: { + lineColor: '#e1e4e8', + lineWidth: 1 + }, + horizontalLine: { + lineColor: '#e1e4e8', + lineWidth: 1 + }, + backgroundColor: '#63a8ff', + scales: [ + { + unit: 'day', + step: 1, + format(date: TYPES.DateFormatArgumentType) { + return date.dateIndex.toString(); + }, + style: { + fontSize: 20, + fontWeight: 'bold', + color: 'red' + } + } + ] + }, + minDate: '2024-11-14', + maxDate: '2024-12-31', + scrollStyle: { + scrollRailColor: 'RGBA(246,246,246,0.5)', + visible: 'none', + width: 6, + scrollSliderCornerRadius: 2, + scrollSliderColor: '#5cb85c' + } + }; + + const ganttInstance = new Gantt(document.getElementById(CONTAINER_ID)!, option); + window.ganttInstance = ganttInstance; + ganttInstance.on('scroll', e => { + console.log('scroll', e); + }); + + ganttInstance.listTableInstance?.on('scroll', e => { + console.log('listTable scroll', e); + }); + // bindDebugTool(ganttInstance.scenegraph.stage as any, { + // customGrapicKeys: ['role', '_updateTag'] + // }); +} diff --git a/packages/vtable-gantt/examples/gantt/gantt-customLayout-Sub_Tasks_Inline.ts b/packages/vtable-gantt/examples/gantt/gantt-Inline-customLayout.ts similarity index 100% rename from packages/vtable-gantt/examples/gantt/gantt-customLayout-Sub_Tasks_Inline.ts rename to packages/vtable-gantt/examples/gantt/gantt-Inline-customLayout.ts diff --git a/packages/vtable-gantt/examples/gantt/gantt-customLayout-Sub_Tasks.ts b/packages/vtable-gantt/examples/gantt/gantt-Separate-customLayout.ts similarity index 100% rename from packages/vtable-gantt/examples/gantt/gantt-customLayout-Sub_Tasks.ts rename to packages/vtable-gantt/examples/gantt/gantt-Separate-customLayout.ts diff --git a/packages/vtable-gantt/examples/gantt/gantt-Sub_Tasks_Separate.ts b/packages/vtable-gantt/examples/gantt/gantt-Sub_Tasks_Separate.ts index a32349b3b..760d0b73e 100644 --- a/packages/vtable-gantt/examples/gantt/gantt-Sub_Tasks_Separate.ts +++ b/packages/vtable-gantt/examples/gantt/gantt-Sub_Tasks_Separate.ts @@ -274,7 +274,7 @@ export function createTable() { contextMenuItems: ['copy', 'paste', 'delete', '...'] } }, - groupBy: true, + tasksShowMode: TasksShowMode.Sub_Tasks_Separate, frame: { outerFrameStyle: { diff --git a/packages/vtable-gantt/examples/menu.ts b/packages/vtable-gantt/examples/menu.ts index eac8b5922..21c04ce94 100644 --- a/packages/vtable-gantt/examples/menu.ts +++ b/packages/vtable-gantt/examples/menu.ts @@ -74,22 +74,30 @@ export const menus = [ path: 'gantt', name: 'gantt-Sub_Tasks_Inline' }, + { path: 'gantt', - name: 'gantt-customLayout-Sub_Tasks_Inline' + name: 'gantt-Sub_Tasks_Separate' }, - { path: 'gantt', - name: 'gantt-Sub_Tasks_Separate' + name: 'gantt-Sub_Tasks_Arrange' }, { path: 'gantt', - name: 'gantt-customLayout-Sub_Tasks' + name: 'gantt-Inline-customLayout' }, { path: 'gantt', - name: 'gantt-Sub_Tasks_Arrange' + name: 'gantt-Separate-customLayout' + }, + { + path: 'gantt', + name: 'gantt-Arrange-customLayout' + }, + { + path: 'gantt', + name: 'gantt-Compact-customLayout' } // ] // } diff --git a/packages/vtable-gantt/src/Gantt.ts b/packages/vtable-gantt/src/Gantt.ts index a02bd0a38..58d4dfd2b 100644 --- a/packages/vtable-gantt/src/Gantt.ts +++ b/packages/vtable-gantt/src/Gantt.ts @@ -31,6 +31,7 @@ import { EventManager } from './event/event-manager'; import { StateManager } from './state/state-manager'; import { computeRowsCountByRecordDate, + computeRowsCountByRecordDateForCompact, convertProgress, createSplitLineAndResizeLine, DayTimes, @@ -331,7 +332,8 @@ export class Gantt extends EventTarget { if ( this.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Inline || this.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Separate || - this.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange + this.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange || + this.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Compact ) { for (let i = 0; i < listTable_options.columns.length; i++) { if (listTable_options.columns[i].tree) { @@ -344,7 +346,8 @@ export class Gantt extends EventTarget { key === 'hierarchyExpandLevel' && (this.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Inline || this.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Separate || - this.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange) + this.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange || + this.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Compact) ) { delete listTable_options[key]; } @@ -528,6 +531,19 @@ export class Gantt extends EventTarget { return (record.children?.length || 1) * this.parsedOptions.rowHeight; }; listTable_options.defaultRowHeight = 'auto'; + } else if (this.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Compact) { + listTable_options.customComputeRowHeight = (args: { row: number; table: ListTable }) => { + const { row, table } = args; + const record = table.getRecordByRowCol(0, row); + return ( + computeRowsCountByRecordDateForCompact( + record, + this.parsedOptions.startDateField, + this.parsedOptions.endDateField + ) * this.parsedOptions.rowHeight + ); + }; + listTable_options.defaultRowHeight = 'auto'; } else if (this.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange) { listTable_options.customComputeRowHeight = (args: { row: number; table: ListTable }) => { const { row, table } = args; diff --git a/packages/vtable-gantt/src/data/DataSource.ts b/packages/vtable-gantt/src/data/DataSource.ts index b79196f56..8d214b4d9 100644 --- a/packages/vtable-gantt/src/data/DataSource.ts +++ b/packages/vtable-gantt/src/data/DataSource.ts @@ -24,7 +24,7 @@ export class DataSource { needMinDate || needMaxDate || this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Inline || - this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange + this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Compact ) { for (let i = 0; i < this.records.length; i++) { const record = this.records[i]; @@ -39,7 +39,7 @@ export class DataSource { if ( this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Inline || - this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange + this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Compact ) { // 将子任务按开始时间升序排列 record.children && @@ -67,9 +67,26 @@ export class DataSource { target_sub_task_index: number ) { if ( - this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Inline || - this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange + (this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange || + this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Compact) && + source_index === target_index ) { + return; + } + if (this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Inline) { + if ( + isValid(source_sub_task_index) && + isValid(target_sub_task_index) && + isValid(source_index) && + isValid(target_index) + ) { + const sub_task_record = this.records[source_index].children[source_sub_task_index]; + this.records[source_index].children.splice(source_sub_task_index, 1); + if (!this.records[target_index].children) { + this.records[target_index].children = []; + } + this.records[target_index].children.splice(target_sub_task_index, 0, sub_task_record); + } this.records[target_index]?.children?.sort((a: any, b: any) => { return ( createDateAtMidnight(a[this._gantt.parsedOptions.startDateField]).getTime() - @@ -77,7 +94,12 @@ export class DataSource { ); }); } else { - if (isValid(source_sub_task_index) && isValid(target_sub_task_index) && isValid(target_index)) { + if ( + isValid(source_sub_task_index) && + isValid(target_sub_task_index) && + isValid(source_index) && + isValid(target_index) + ) { const sub_task_record = this.records[source_index].children[source_sub_task_index]; this.records[source_index].children.splice(source_sub_task_index, 1); if (!this.records[target_index].children) { diff --git a/packages/vtable-gantt/src/event/event-manager.ts b/packages/vtable-gantt/src/event/event-manager.ts index f60b0b07f..aae0cb844 100644 --- a/packages/vtable-gantt/src/event/event-manager.ts +++ b/packages/vtable-gantt/src/event/event-manager.ts @@ -154,11 +154,10 @@ function bindTableGroupListener(event: EventManager) { } } else { if (scene._gantt.stateManager.hoverTaskBar.target) { - stateManager.hideTaskBarHover(e); if (scene._gantt.hasListeners(GANTT_EVENT_TYPE.MOUSELEAVE_TASK_BAR)) { // const taskIndex = getTaskIndexByY(e.offset.y, scene._gantt); - const taskIndex = taskBarTarget.task_index; - const sub_task_index = taskBarTarget.sub_task_index; + const taskIndex = scene._gantt.stateManager.hoverTaskBar.target.task_index; + const sub_task_index = scene._gantt.stateManager.hoverTaskBar.target.sub_task_index; const record = scene._gantt.getRecordByIndex(taskIndex, sub_task_index); scene._gantt.fireListeners(GANTT_EVENT_TYPE.MOUSELEAVE_TASK_BAR, { event: e.nativeEvent, @@ -167,12 +166,14 @@ function bindTableGroupListener(event: EventManager) { record }); } + stateManager.hideTaskBarHover(e); } //#region hover到某一个任务 检查有没有日期安排,没有的话显示创建按钮 if ( gantt.parsedOptions.tasksShowMode !== TasksShowMode.Sub_Tasks_Inline && gantt.parsedOptions.tasksShowMode !== TasksShowMode.Sub_Tasks_Separate && gantt.parsedOptions.tasksShowMode !== TasksShowMode.Sub_Tasks_Arrange && + gantt.parsedOptions.tasksShowMode !== TasksShowMode.Sub_Tasks_Compact && gantt.parsedOptions.taskBarCreatable ) { const taskIndex = getTaskIndexByY(e.offset.y, gantt); diff --git a/packages/vtable-gantt/src/gantt-helper.ts b/packages/vtable-gantt/src/gantt-helper.ts index 85ec0d65a..982fe8945 100644 --- a/packages/vtable-gantt/src/gantt-helper.ts +++ b/packages/vtable-gantt/src/gantt-helper.ts @@ -652,6 +652,15 @@ export function clearRecordLinkInfos(records: any[], childrenField: string = 'ch } } +export function clearRecordShowIndex(records: any[], childrenField: string = 'children') { + for (let i = 0; i < records.length; i++) { + if (records[i][childrenField]?.length) { + clearRecordShowIndex(records[i][childrenField], childrenField); + } else { + delete records[i].vtable_gantt_showIndex; + } + } +} export function getTaskIndexsByTaskY(y: number, gantt: Gantt) { let task_index; let sub_task_index; @@ -669,15 +678,65 @@ export function getTaskIndexsByTaskY(y: number, gantt: Gantt) { return { task_index, sub_task_index }; } +export function computeRowsCountByRecordDateForCompact(record: any, startDateField: string, endDateField: string) { + if (!record.children || record.children.length === 1) { + if (record.children.length === 1) { + record.children[0].vtable_gantt_showIndex = 0; + } + return 1; + } + // 创建一个浅拷贝并排序子任务,根据开始日期排序 + const sortedChildren = record.children.slice().sort((a: any, b: any) => { + return createDateAtMidnight(a[startDateField]).getTime() - createDateAtMidnight(b[startDateField]).getTime(); + }); + const count = 0; + // 用于存储每一行的结束日期 + const rows = []; + for (let i = 0; i <= sortedChildren.length - 1; i++) { + const newRecord = sortedChildren[i]; + const startDate = createDateAtMidnight(newRecord[startDateField]).getTime(); + const endDate = createDateAtMidnight(newRecord[endDateField]).getTime(); + + let placed = false; + + // 尝试将当前任务放入已有的行中 + for (let j = 0; j < rows.length; j++) { + if (startDate > rows[j]) { + // 如果当前任务的开始日期在该行的结束日期之后,则可以放在这一行 + rows[j] = endDate; + placed = true; + newRecord.vtable_gantt_showIndex = j; + break; + } + } + + // 如果不能放在已有的行中,则需要新开一行 + if (!placed) { + rows.push(endDate); + newRecord.vtable_gantt_showIndex = rows.length - 1; + } + } + + return rows.length; +} +// 检查两个日期范围是否重叠 +function isOverlapping(task: any, rowTasks: any[], startDateField: string, endDateField: string) { + const start1 = createDateAtMidnight(task[startDateField]).getTime(); + const end1 = createDateAtMidnight(task[endDateField]).getTime(); + return rowTasks.some(rowTask => { + const start2 = createDateAtMidnight(rowTask[startDateField]).getTime(); + const end2 = createDateAtMidnight(rowTask[endDateField]).getTime(); + return start1 <= end2 && start2 <= end1; + }); +} export function computeRowsCountByRecordDate(record: any, startDateField: string, endDateField: string) { if (!record.children || record.children.length === 1) { + if (record.children.length === 1) { + record.children[0].vtable_gantt_showIndex = 0; + } return 1; } - // 排序在datasource中已经排过了 - // // 创建一个浅拷贝并排序子任务,根据开始日期排序 - // const sortedChildren = record.children.slice().sort((a: any, b: any) => { - // return createDateAtMidnight(a[startDateField]).getTime() - createDateAtMidnight(b[startDateField]).getTime(); - // }); + const count = 0; // 用于存储每一行的结束日期 const rows = []; @@ -690,10 +749,12 @@ export function computeRowsCountByRecordDate(record: any, startDateField: string // 尝试将当前任务放入已有的行中 for (let j = 0; j < rows.length; j++) { - if (startDate > rows[j]) { + const rowTasks = record.children.filter((t: any) => t !== newRecord && t.vtable_gantt_showIndex === j); + if (!isOverlapping(newRecord, rowTasks, startDateField, endDateField)) { // 如果当前任务的开始日期在该行的结束日期之后,则可以放在这一行 rows[j] = endDate; placed = true; + newRecord.vtable_gantt_showIndex = j; break; } } @@ -701,12 +762,12 @@ export function computeRowsCountByRecordDate(record: any, startDateField: string // 如果不能放在已有的行中,则需要新开一行 if (!placed) { rows.push(endDate); + newRecord.vtable_gantt_showIndex = rows.length - 1; } } return rows.length; } - export function getSubTaskRowIndexByRecordDate( record: any, childIndex: number, diff --git a/packages/vtable-gantt/src/scenegraph/dependency-link.ts b/packages/vtable-gantt/src/scenegraph/dependency-link.ts index b19a449fe..9b9b8fee3 100644 --- a/packages/vtable-gantt/src/scenegraph/dependency-link.ts +++ b/packages/vtable-gantt/src/scenegraph/dependency-link.ts @@ -36,7 +36,7 @@ export class DependencyLink { } initLinkLines() { - clearRecordLinkInfos(this._scene._gantt.records, 'children'); + clearRecordLinkInfos(this._scene._gantt.records); this.linkLinesContainer = new Group({ x: 0, y: 0, @@ -91,33 +91,38 @@ export class DependencyLink { } = this._scene._gantt.getTaskInfoByTaskListIndex(linkedFromTaskRecord.index[0], linkedFromTaskRecord.index[1])); } else if ( this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Separate || - this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange + this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange || + this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Compact ) { const beforeRowCountLinkedFrom = this._scene._gantt.getRowsHeightByIndex(0, linkedFromTaskRecord.index[0] - 1) / this._scene._gantt.parsedOptions.rowHeight; // 耦合了listTableOption的customComputeRowHeight linkedFromTaskShowIndex = beforeRowCountLinkedFrom + - (this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange - ? getSubTaskRowIndexByRecordDate( - this._scene._gantt.records[linkedFromTaskRecord.index[0]], - linkedFromTaskRecord.index[1], - this._scene._gantt.parsedOptions.startDateField, - this._scene._gantt.parsedOptions.endDateField - ) + (this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange || + this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Compact + ? // getSubTaskRowIndexByRecordDate( + // this._scene._gantt.records[linkedFromTaskRecord.index[0]], + // linkedFromTaskRecord.index[1], + // this._scene._gantt.parsedOptions.startDateField, + // this._scene._gantt.parsedOptions.endDateField + // ) + linkedFromTaskRecord.record.vtable_gantt_showIndex : linkedFromTaskRecord.index[1]); const beforeRowCountLinkedTo = this._scene._gantt.getRowsHeightByIndex(0, linkedToTaskRecord.index[0] - 1) / this._scene._gantt.parsedOptions.rowHeight; // 耦合了listTableOption的customComputeRowHeight linkedToTaskShowIndex = beforeRowCountLinkedTo + - (this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange - ? getSubTaskRowIndexByRecordDate( - this._scene._gantt.records[linkedToTaskRecord.index[0]], - linkedToTaskRecord.index[1], - this._scene._gantt.parsedOptions.startDateField, - this._scene._gantt.parsedOptions.endDateField - ) + (this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange || + this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Compact + ? //getSubTaskRowIndexByRecordDate( + // this._scene._gantt.records[linkedToTaskRecord.index[0]], + // linkedToTaskRecord.index[1], + // this._scene._gantt.parsedOptions.startDateField, + // this._scene._gantt.parsedOptions.endDateField + // ) + linkedToTaskRecord.record.vtable_gantt_showIndex : linkedToTaskRecord.index[1]); ({ startDate: linkedToTaskStartDate, diff --git a/packages/vtable-gantt/src/scenegraph/task-bar.ts b/packages/vtable-gantt/src/scenegraph/task-bar.ts index 3b4592c44..926c8bfa0 100644 --- a/packages/vtable-gantt/src/scenegraph/task-bar.ts +++ b/packages/vtable-gantt/src/scenegraph/task-bar.ts @@ -3,7 +3,12 @@ import type { Scenegraph } from './scenegraph'; // import { Icon } from './icon'; import { createDateAtMidnight, parseStringTemplate, toBoxArray } from '../tools/util'; import { isValid } from '@visactor/vutils'; -import { computeRowsCountByRecordDate, getSubTaskRowIndexByRecordDate, getTextPos } from '../gantt-helper'; +import { + computeRowsCountByRecordDate, + computeRowsCountByRecordDateForCompact, + getSubTaskRowIndexByRecordDate, + getTextPos +} from '../gantt-helper'; import { GanttTaskBarNode } from './gantt-node'; import { TasksShowMode } from '../ts-types'; @@ -58,7 +63,8 @@ export class TaskBar { if ( this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Inline || this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Separate || - this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange + this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange || + this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Compact ) { const record = this._scene._gantt.getRecordByIndex(i); if (record.children?.length > 0) { @@ -92,9 +98,9 @@ export class TaskBar { const taskBarSize = this._scene._gantt.parsedOptions.colWidthPerDay * taskDays; const taskbarHeight = this._scene._gantt.parsedOptions.taskBarStyle.width; const minDate = createDateAtMidnight(this._scene._gantt.parsedOptions.minDate); - const oneTaskHeigth = - this._scene._gantt.getRowHeightByIndex(index) / - (this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Separate + + const subTaskShowRowCount = + this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Separate ? childrenLength : this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange ? computeRowsCountByRecordDate( @@ -102,7 +108,14 @@ export class TaskBar { this._scene._gantt.parsedOptions.startDateField, this._scene._gantt.parsedOptions.endDateField ) - : 1); + : this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Compact + ? computeRowsCountByRecordDateForCompact( + this._scene._gantt.records[index], + this._scene._gantt.parsedOptions.startDateField, + this._scene._gantt.parsedOptions.endDateField + ) + : 1; + const oneTaskHeigth = this._scene._gantt.getRowHeightByIndex(index) / subTaskShowRowCount; const barGroup = new GanttTaskBarNode({ x: this._scene._gantt.parsedOptions.colWidthPerDay * @@ -112,13 +125,15 @@ export class TaskBar { this._scene._gantt.getRowsHeightByIndex(0, index - 1) + (this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Separate ? childIndex * oneTaskHeigth - : this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange - ? getSubTaskRowIndexByRecordDate( - this._scene._gantt.records[index], - childIndex, - this._scene._gantt.parsedOptions.startDateField, - this._scene._gantt.parsedOptions.endDateField - ) * oneTaskHeigth + : this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange || + this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Compact + ? // getSubTaskRowIndexByRecordDate( + // this._scene._gantt.records[index], + // childIndex, + // this._scene._gantt.parsedOptions.startDateField, + // this._scene._gantt.parsedOptions.endDateField + // ) + taskRecord.vtable_gantt_showIndex * oneTaskHeigth : 0) + (oneTaskHeigth - taskbarHeight) / 2, width: taskBarSize, diff --git a/packages/vtable-gantt/src/state/state-manager.ts b/packages/vtable-gantt/src/state/state-manager.ts index a4c3cc33c..830392702 100644 --- a/packages/vtable-gantt/src/state/state-manager.ts +++ b/packages/vtable-gantt/src/state/state-manager.ts @@ -11,7 +11,12 @@ import { syncTreeChangeFromTable, syncSortFromTable } from './gantt-table-sync'; -import { findRecordByTaskKey, getSubTaskRowIndexByRecordDate, getTaskIndexsByTaskY } from '../gantt-helper'; +import { + clearRecordShowIndex, + findRecordByTaskKey, + getSubTaskRowIndexByRecordDate, + getTaskIndexsByTaskY +} from '../gantt-helper'; import { debounce } from '../tools/debounce'; import type { GanttTaskBarNode } from '../scenegraph/gantt-node'; import { TASKBAR_HOVER_ICON_WIDTH } from '../scenegraph/task-bar'; @@ -332,106 +337,116 @@ export class StateManager { const deltaX = this.moveTaskBar.deltaX; const deltaY = this.moveTaskBar.deltaY; - if (Math.abs(deltaX) < 1 && Math.abs(deltaY) < 1) { - return; - } - const days = Math.round(deltaX / this._gantt.parsedOptions.colWidthPerDay); - - const correctX = days * this._gantt.parsedOptions.colWidthPerDay; - const targetEndX = this.moveTaskBar.targetStartX + correctX; - const targetEndY = - this.moveTaskBar.targetStartY + - this._gantt.parsedOptions.rowHeight * Math.round(deltaY / this._gantt.parsedOptions.rowHeight); const target = this.moveTaskBar.target; - // 判断横向拖动 更新数据的date - if (Math.abs(days) >= 1) { - const taskIndex = target.task_index; - const sub_task_index = target.sub_task_index; - const oldRecord = this._gantt.getRecordByIndex(taskIndex, sub_task_index); - const oldStartDate = oldRecord[this._gantt.parsedOptions.startDateField]; - const oldEndDate = oldRecord[this._gantt.parsedOptions.endDateField]; - this._gantt._updateDateToTaskRecord('move', days, taskIndex, sub_task_index); - const newRecord = this._gantt.getRecordByIndex(taskIndex, sub_task_index); - if (this._gantt.hasListeners(GANTT_EVENT_TYPE.CHANGE_DATE_RANGE)) { - this._gantt.fireListeners(GANTT_EVENT_TYPE.CHANGE_DATE_RANGE, { - startDate: newRecord[this._gantt.parsedOptions.startDateField], - endDate: newRecord[this._gantt.parsedOptions.endDateField], - oldStartDate, - oldEndDate, - index: taskIndex, - record: newRecord - }); + if (Math.abs(deltaX) >= 1 || Math.abs(deltaY) >= 1) { + const days = Math.round(deltaX / this._gantt.parsedOptions.colWidthPerDay); + + const correctX = days * this._gantt.parsedOptions.colWidthPerDay; + const targetEndX = this.moveTaskBar.targetStartX + correctX; + const targetEndY = + this.moveTaskBar.targetStartY + + this._gantt.parsedOptions.rowHeight * Math.round(deltaY / this._gantt.parsedOptions.rowHeight); + // 判断横向拖动 更新数据的date + if (Math.abs(days) >= 1) { + const taskIndex = target.task_index; + const sub_task_index = target.sub_task_index; + const oldRecord = this._gantt.getRecordByIndex(taskIndex, sub_task_index); + const oldStartDate = oldRecord[this._gantt.parsedOptions.startDateField]; + const oldEndDate = oldRecord[this._gantt.parsedOptions.endDateField]; + this._gantt._updateDateToTaskRecord('move', days, taskIndex, sub_task_index); + const newRecord = this._gantt.getRecordByIndex(taskIndex, sub_task_index); + if (this._gantt.hasListeners(GANTT_EVENT_TYPE.CHANGE_DATE_RANGE)) { + this._gantt.fireListeners(GANTT_EVENT_TYPE.CHANGE_DATE_RANGE, { + startDate: newRecord[this._gantt.parsedOptions.startDateField], + endDate: newRecord[this._gantt.parsedOptions.endDateField], + oldStartDate, + oldEndDate, + index: taskIndex, + record: newRecord + }); + } } - } - if (this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange) { - const indexs = getTaskIndexsByTaskY(targetEndY, this._gantt); - this._gantt._dragOrderTaskRecord( - target.task_index, - target.sub_task_index, - indexs.task_index, - indexs.sub_task_index - ); - this._gantt.taskListTableInstance.renderWithRecreateCells(); - this._gantt._syncPropsFromTable(); - this._gantt.scenegraph.refreshTaskBarsAndGrid(); - } else { - // 判断纵向拖动 处理数据的位置 - if (Math.abs(Math.round(deltaY / this._gantt.parsedOptions.rowHeight)) >= 1) { + if ( + this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange || + this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Compact + ) { const indexs = getTaskIndexsByTaskY(targetEndY, this._gantt); + console.log( + 'source target', + target.task_index, + target.sub_task_index, + indexs.task_index, + indexs.sub_task_index + ); this._gantt._dragOrderTaskRecord( target.task_index, target.sub_task_index, indexs.task_index, indexs.sub_task_index ); - if (this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Separate) { - this._gantt.taskListTableInstance.renderWithRecreateCells(); - this._gantt.scenegraph.refreshTaskBarsAndGrid(); - } else { - this._gantt.scenegraph.taskBar.refresh(); - this._gantt.scenegraph.dependencyLink.refresh(); - } - // target = this._gantt.scenegraph.taskBar.getTaskBarNodeByIndex(indexs.task_index, indexs.sub_task_index); + clearRecordShowIndex(this._gantt.records); + this._gantt.taskListTableInstance.renderWithRecreateCells(); + this._gantt._syncPropsFromTable(); + this._gantt.scenegraph.refreshTaskBarsAndGrid(); } else { - resizeOrMoveTaskBar( - target, - targetEndX - (target as Group).attribute.x, - targetEndY - (target as Group).attribute.y, - null, - this - ); - - // 为了确保拖拽后 保持startDate日期晚的显示在上层不被盖住 这里需要重新排序一下 - if (days > 0) { - let insertAfterNode = target; - while ( - (insertAfterNode as Group).nextSibling && - (insertAfterNode as Group).nextSibling.attribute.y === (target as Group).attribute.y && - (insertAfterNode as Group).nextSibling.record[this._gantt.parsedOptions.startDateField] <= - target.record[this._gantt.parsedOptions.startDateField] - ) { - insertAfterNode = (insertAfterNode as Group).nextSibling; - } - if (insertAfterNode !== target) { - (insertAfterNode as Group).parent.insertAfter(target, insertAfterNode); + // 判断纵向拖动 处理数据的位置 + if (Math.abs(Math.round(deltaY / this._gantt.parsedOptions.rowHeight)) >= 1) { + const indexs = getTaskIndexsByTaskY(targetEndY, this._gantt); + this._gantt._dragOrderTaskRecord( + target.task_index, + target.sub_task_index, + indexs.task_index, + indexs.sub_task_index + ); + if (this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Separate) { + this._gantt.taskListTableInstance.renderWithRecreateCells(); + this._gantt.scenegraph.refreshTaskBarsAndGrid(); + } else { + this._gantt.scenegraph.taskBar.refresh(); + this._gantt.scenegraph.dependencyLink.refresh(); } - } else if (days < 0) { - let insertBeforeNode = target; - while ( - (insertBeforeNode as Group).previousSibling && - (insertBeforeNode as Group).previousSibling.attribute.y === (target as Group).attribute.y && - (insertBeforeNode as Group).previousSibling.record[this._gantt.parsedOptions.startDateField] >= - target.record[this._gantt.parsedOptions.startDateField] - ) { - insertBeforeNode = (insertBeforeNode as Group).previousSibling; - } - if (insertBeforeNode !== target) { - (insertBeforeNode as Group).parent.insertBefore(target, insertBeforeNode); + // target = this._gantt.scenegraph.taskBar.getTaskBarNodeByIndex(indexs.task_index, indexs.sub_task_index); + } else { + resizeOrMoveTaskBar( + target, + targetEndX - (target as Group).attribute.x, + targetEndY - (target as Group).attribute.y, + null, + this + ); + + // 为了确保拖拽后 保持startDate日期晚的显示在上层不被盖住 这里需要重新排序一下 + if (days > 0) { + let insertAfterNode = target; + while ( + (insertAfterNode as Group).nextSibling && + (insertAfterNode as Group).nextSibling.attribute.y === (target as Group).attribute.y && + (insertAfterNode as Group).nextSibling.record[this._gantt.parsedOptions.startDateField] <= + target.record[this._gantt.parsedOptions.startDateField] + ) { + insertAfterNode = (insertAfterNode as Group).nextSibling; + } + if (insertAfterNode !== target) { + (insertAfterNode as Group).parent.insertAfter(target, insertAfterNode); + } + } else if (days < 0) { + let insertBeforeNode = target; + while ( + (insertBeforeNode as Group).previousSibling && + (insertBeforeNode as Group).previousSibling.attribute.y === (target as Group).attribute.y && + (insertBeforeNode as Group).previousSibling.record[this._gantt.parsedOptions.startDateField] >= + target.record[this._gantt.parsedOptions.startDateField] + ) { + insertBeforeNode = (insertBeforeNode as Group).previousSibling; + } + if (insertBeforeNode !== target) { + (insertBeforeNode as Group).parent.insertBefore(target, insertBeforeNode); + } } } } + this._gantt.scenegraph.updateNextFrame(); } - this._gantt.scenegraph.updateNextFrame(); this.moveTaskBar.moving = false; if (this.selectedTaskBar.target !== target) { target.setAttribute('zIndex', 0); @@ -545,7 +560,10 @@ export class StateManager { taskIndex, sub_task_index ); - if (this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange) { + if ( + this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange || + this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Compact + ) { this._gantt.taskListTableInstance.renderWithRecreateCells(); this._gantt._syncPropsFromTable(); this._gantt.scenegraph.refreshTaskBarsAndGrid(); @@ -810,7 +828,8 @@ export class StateManager { if ( this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Inline || this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Separate || - this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange + this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange || + this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Compact ) { linkFrom_index = linkedFromTaskRecord.index[0]; linkFrom_sub_task_index = linkedFromTaskRecord.index[1]; @@ -946,9 +965,11 @@ function resizeOrMoveTaskBar(target: GanttTaskBarNode, dx: number, dy: number, n let linkedToTaskStartDate; let linkedToTaskEndDate; let linkedToTaskTaskDays; + // let linkedToTaskTaskRecord; let linkedFromTaskStartDate; let linkedFromTaskEndDate; let linkedFromTaskTaskDays; + // let linkedFromTaskTaskRecord; let linkedToTaskShowIndex; let linkedFromTaskShowIndex; @@ -976,20 +997,23 @@ function resizeOrMoveTaskBar(target: GanttTaskBarNode, dx: number, dy: number, n target.attribute.y + taskbarHeight / 2 - (linkedToTaskShowIndex + 0.5) * state._gantt.parsedOptions.rowHeight; } else if ( state._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Separate || - state._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange + state._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange || + state._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Compact ) { const new_indexs = getTaskIndexsByTaskY(target.attribute.y + dy, state._gantt); const beforeRowCountLinkedFrom = state._gantt.getRowsHeightByIndex(0, linkedFromTaskRecord.index[0] - 1) / state._gantt.parsedOptions.rowHeight; // 耦合了listTableOption的customComputeRowHeight linkedFromTaskShowIndex = beforeRowCountLinkedFrom + - (state._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange - ? getSubTaskRowIndexByRecordDate( - state._gantt.records[linkedFromTaskRecord.index[0]], - linkedFromTaskRecord.index[1], - state._gantt.parsedOptions.startDateField, - state._gantt.parsedOptions.endDateField - ) + (state._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange || + state._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Compact + ? // getSubTaskRowIndexByRecordDate( + // state._gantt.records[linkedFromTaskRecord.index[0]], + // linkedFromTaskRecord.index[1], + // state._gantt.parsedOptions.startDateField, + // state._gantt.parsedOptions.endDateField + // ) + linkedFromTaskRecord.record.vtable_gantt_showIndex : linkedFromTaskRecord.index[1]); // const beforeRowCountLinkedTo = // state._gantt.getRowsHeightByIndex(0, linkedToTaskRecord.index[0] - 1) / state._gantt.parsedOptions.rowHeight; // 耦合了listTableOption的customComputeRowHeight @@ -998,13 +1022,15 @@ function resizeOrMoveTaskBar(target: GanttTaskBarNode, dx: number, dy: number, n state._gantt.getRowsHeightByIndex(0, new_indexs.task_index - 1) / state._gantt.parsedOptions.rowHeight; // 耦合了listTableOption的customComputeRowHeight linkedToTaskShowIndex = beforeRowCountLinkedTo + - (state._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange - ? getSubTaskRowIndexByRecordDate( - state._gantt.records[linkedToTaskRecord.index[0]], - linkedToTaskRecord.index[1], - state._gantt.parsedOptions.startDateField, - state._gantt.parsedOptions.endDateField - ) + (state._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange || + state._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Compact + ? // ? getSubTaskRowIndexByRecordDate( + // state._gantt.records[linkedToTaskRecord.index[0]], + // linkedToTaskRecord.index[1], + // state._gantt.parsedOptions.startDateField, + // state._gantt.parsedOptions.endDateField + // ) + linkedToTaskRecord.record.vtable_gantt_showIndex : new_indexs.sub_task_index); ({ @@ -1104,7 +1130,8 @@ function resizeOrMoveTaskBar(target: GanttTaskBarNode, dx: number, dy: number, n target.attribute.y + taskbarHeight / 2 - (linkedFromTaskShowIndex + 0.5) * state._gantt.parsedOptions.rowHeight; } else if ( state._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Separate || - state._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange + state._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange || + state._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Compact ) { const new_indexs = getTaskIndexsByTaskY(target.attribute.y + dy, state._gantt); // const beforeRowCountLinkedFrom = @@ -1114,26 +1141,30 @@ function resizeOrMoveTaskBar(target: GanttTaskBarNode, dx: number, dy: number, n state._gantt.getRowsHeightByIndex(0, new_indexs.task_index - 1) / state._gantt.parsedOptions.rowHeight; // 耦合了listTableOption的customComputeRowHeight linkedFromTaskShowIndex = beforeRowCountLinkedFrom + - (state._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange - ? getSubTaskRowIndexByRecordDate( - state._gantt.records[linkedFromTaskRecord.index[0]], - linkedFromTaskRecord.index[1], - state._gantt.parsedOptions.startDateField, - state._gantt.parsedOptions.endDateField - ) + (state._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange || + state._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Compact + ? // ? getSubTaskRowIndexByRecordDate( + // state._gantt.records[linkedFromTaskRecord.index[0]], + // linkedFromTaskRecord.index[1], + // state._gantt.parsedOptions.startDateField, + // state._gantt.parsedOptions.endDateField + // ) + linkedFromTaskRecord.record.vtable_gantt_showIndex : new_indexs.sub_task_index); const beforeRowCountLinkedTo = state._gantt.getRowsHeightByIndex(0, linkedToTaskRecord.index[0] - 1) / state._gantt.parsedOptions.rowHeight; // 耦合了listTableOption的customComputeRowHeight linkedToTaskShowIndex = beforeRowCountLinkedTo + - (state._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange - ? getSubTaskRowIndexByRecordDate( - state._gantt.records[linkedToTaskRecord.index[0]], - linkedToTaskRecord.index[1], - state._gantt.parsedOptions.startDateField, - state._gantt.parsedOptions.endDateField - ) + (state._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange || + state._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Compact + ? // ? getSubTaskRowIndexByRecordDate( + // state._gantt.records[linkedToTaskRecord.index[0]], + // linkedToTaskRecord.index[1], + // state._gantt.parsedOptions.startDateField, + // state._gantt.parsedOptions.endDateField + // ) + linkedToTaskRecord.record.vtable_gantt_showIndex : linkedToTaskRecord.index[1]); ({ startDate: linkedToTaskStartDate, diff --git a/packages/vtable-gantt/src/ts-types/gantt-engine.ts b/packages/vtable-gantt/src/ts-types/gantt-engine.ts index 4e15590c5..27bd71c05 100644 --- a/packages/vtable-gantt/src/ts-types/gantt-engine.ts +++ b/packages/vtable-gantt/src/ts-types/gantt-engine.ts @@ -341,8 +341,10 @@ export enum TasksShowMode { Sub_Tasks_Inline = 'sub_tasks_inline', /** 省去父任务节点不展示,且所有子任务的节点分别用一行展示。*/ Sub_Tasks_Separate = 'sub_tasks_separate', - /** 省去父任务节点不展示,且所有子任务的节点均不重叠展示 */ - Sub_Tasks_Arrange = 'sub_tasks_arrange' + /** 省去父任务节点不展示,且所有子任务会维持records中的数据顺序布局,并保证节点不重叠展示 */ + Sub_Tasks_Arrange = 'sub_tasks_arrange', + /** 省去父任务节点不展示,且所有子任务会按照日期早晚的属性来布局,并保证节点不重叠的紧凑型展示 */ + Sub_Tasks_Compact = 'sub_tasks_compact' } export type ITaskBarSelectedStyle = { shadowBlur?: number; //阴影宽度 From 65e674d2211ba3d7e6334584098722442c85b5a8 Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Fri, 29 Nov 2024 15:40:10 +0800 Subject: [PATCH 15/40] docs: add getTaskBarRelativeRect api #2920 --- docs/assets/api/en/GanttAPI.md | 21 ++++++++++++++++++++ docs/assets/api/zh/GanttAPI.md | 21 ++++++++++++++++++++ packages/vtable-gantt/src/Gantt.ts | 31 ++++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+) diff --git a/docs/assets/api/en/GanttAPI.md b/docs/assets/api/en/GanttAPI.md index 233695a55..2639d344c 100644 --- a/docs/assets/api/en/GanttAPI.md +++ b/docs/assets/api/en/GanttAPI.md @@ -71,6 +71,27 @@ removeLink: (link: ITaskLink) => void ``` +### scrollTop + +Get or set the vertical scroll value to a specified position. + +### scrollLeft + +Get or set the horizontal scroll value to a specified position. + +### getTaskBarRelativeRect(Function) + +Get the position of the task bar. The position relative to the top-left corner of the Gantt chart. + +``` + getTaskBarRelativeRect:(index: number) =>{ + left: number; + top: number; + width: number; + height: number; + } +``` + ## Events The Gantt chart event list allows you to listen to the required events and implement custom business logic as needed. diff --git a/docs/assets/api/zh/GanttAPI.md b/docs/assets/api/zh/GanttAPI.md index 5b1ee373c..d0c35b046 100644 --- a/docs/assets/api/zh/GanttAPI.md +++ b/docs/assets/api/zh/GanttAPI.md @@ -71,6 +71,27 @@ ``` +### scrollTop + +竖向滚动到指定位置的滚动值获取或者设置 + +### scrollLeft + +横向滚动到指定位置的滚动值获取或者设置 + +### getTaskBarRelativeRect(Function) + +获取任务条的位置。相对应甘特图表左上角的位置。 + +``` + getTaskBarRelativeRect:(index: number) =>{ + left: number; + top: number; + width: number; + height: number; + } +``` + ## Events 甘特图事件列表,可以根据实际需要,监听所需事件,实现自定义业务。 diff --git a/packages/vtable-gantt/src/Gantt.ts b/packages/vtable-gantt/src/Gantt.ts index 57de3dd37..e9be20d42 100644 --- a/packages/vtable-gantt/src/Gantt.ts +++ b/packages/vtable-gantt/src/Gantt.ts @@ -828,4 +828,35 @@ export class Gantt extends EventTarget { this.scenegraph.updateNextFrame(); } } + get scrollTop(): number { + return this.stateManager.scrollTop; + } + set scrollTop(value: number) { + this.stateManager.setScrollTop(value); + } + get scrollLeft(): number { + return this.stateManager.scrollLeft; + } + set scrollLeft(value: number) { + this.stateManager.setScrollLeft(value); + } + /** 获取任务条的位置。相对应甘特图表左上角的位置。 */ + getTaskBarRelativeRect(index: number) { + const taskBarNode = this.scenegraph.taskBar.getTaskBarNodeByIndex(index); + const left = + taskBarNode.attribute.x + + this.taskListTableInstance.tableNoFrameWidth + + this.taskListTableInstance.tableX + + this.tableX - + this.scrollLeft; + const top = taskBarNode.attribute.y + this.tableY + this.headerHeight - this.scrollTop; + const width = taskBarNode.attribute.width; + const height = taskBarNode.attribute.height; + return { + left, + top, + width, + height + }; + } } From 552858d32d33e786fdcb91ae562ec5b3b294c94b Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Fri, 29 Nov 2024 15:40:39 +0800 Subject: [PATCH 16/40] docs: update changlog of rush --- ...table-gantt-taskbar-position_2024-11-29-07-40.json | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 common/changes/@visactor/vtable/2920-feature-vtable-gantt-taskbar-position_2024-11-29-07-40.json diff --git a/common/changes/@visactor/vtable/2920-feature-vtable-gantt-taskbar-position_2024-11-29-07-40.json b/common/changes/@visactor/vtable/2920-feature-vtable-gantt-taskbar-position_2024-11-29-07-40.json new file mode 100644 index 000000000..772948e96 --- /dev/null +++ b/common/changes/@visactor/vtable/2920-feature-vtable-gantt-taskbar-position_2024-11-29-07-40.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "docs: add getTaskBarRelativeRect api #2920\n\n", + "type": "none", + "packageName": "@visactor/vtable" + } + ], + "packageName": "@visactor/vtable", + "email": "892739385@qq.com" +} \ No newline at end of file From ae5de20bd551d573470b789735c5f8f31c99f119 Mon Sep 17 00:00:00 2001 From: Rui-Sun Date: Fri, 29 Nov 2024 08:36:08 +0000 Subject: [PATCH 17/40] docs: generate changelog of release v1.11.5 --- docs/assets/changelog/en/release.md | 16 ++++++++++++++++ docs/assets/changelog/zh/release.md | 16 ++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/docs/assets/changelog/en/release.md b/docs/assets/changelog/en/release.md index a578a8c00..c8572ae8c 100644 --- a/docs/assets/changelog/en/release.md +++ b/docs/assets/changelog/en/release.md @@ -1,3 +1,19 @@ +# v1.11.5 + +2024-11-29 + + +**🆕 New feature** + +- **@visactor/vtable**: add @visactor/vtable-plugins package + +**📖 Site / documentation update** + +- **@visactor/vtable**: 更新进入或离开节点时的事件文档 +- **@visactor/vtable**: 更新甘特图事件文档,暴露整个e的信息 + +[more detail about v1.11.5](https://github.com/VisActor/VTable/releases/tag/v1.11.5) + # v1.11.3 2024-11-28 diff --git a/docs/assets/changelog/zh/release.md b/docs/assets/changelog/zh/release.md index bfd89064a..b0d235661 100644 --- a/docs/assets/changelog/zh/release.md +++ b/docs/assets/changelog/zh/release.md @@ -1,3 +1,19 @@ +# v1.11.5 + +2024-11-29 + + +**🆕 新增功能** + +- **@visactor/vtable**: add @visactor/vtable-plugins package + +**📖 文档更新** + +- **@visactor/vtable**: 更新进入或离开节点时的事件文档 +- **@visactor/vtable**: 更新甘特图事件文档,暴露整个e的信息 + +[更多详情请查看 v1.11.5](https://github.com/VisActor/VTable/releases/tag/v1.11.5) + # v1.11.3 2024-11-28 From 5cf1a4711e5d67ac82c27f2c2596c6346d6c96e1 Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Fri, 29 Nov 2024 16:48:59 +0800 Subject: [PATCH 18/40] docs: update setRecords usage --- docs/assets/api/en/methods.md | 5 +++-- docs/assets/api/zh/methods.md | 6 +++--- packages/vtable/src/ListTable.ts | 4 ++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/docs/assets/api/en/methods.md b/docs/assets/api/en/methods.md index 47dd8c353..579dd1143 100644 --- a/docs/assets/api/en/methods.md +++ b/docs/assets/api/en/methods.md @@ -140,12 +140,12 @@ Set the table data interface, which can be called as an update interface. Basic table updates: -The basic table can also set the sorting status to sort the table data. Set sortState to null to clear the sorting status. If not set, the incoming data will be sorted according to the current sorting status. +The basic table can also set the sorting status to sort the table data. Set sortState to null to clear the sorting status. If not set, the incoming data will be sorted according to the current sorting status.In a scenario where internal sorting is disabled, be sure to clear the current sorting state before invoking the interface. ``` setRecords( records: Array, - option?: { sortState?: SortState | SortState[]} + option?: { sortState?: SortState | SortState[] | null } ): void; ``` @@ -1322,6 +1322,7 @@ setCanvasSize: (width: number, height: number) => void; ## setLoadingHierarchyState(Function) Set the loading state of the tree expansion and collapse of the cell + ``` /** Set the loading state of the tree expansion and collapse of the cell */ setLoadingHierarchyState: (col: number, row: number) => void; diff --git a/docs/assets/api/zh/methods.md b/docs/assets/api/zh/methods.md index 285cf781d..3df1a4027 100644 --- a/docs/assets/api/zh/methods.md +++ b/docs/assets/api/zh/methods.md @@ -140,12 +140,12 @@ tableInstance.renderWithRecreateCells(); 基本表格更新: -基本表格可同时设置排序状态对表格数据排序,sortState 设置为 null 清空排序状态,如果不设置则按当前排序状态对传入数据排序。 +基本表格可同时设置排序状态对表格数据排序,sortState 设置为 null 清空当前的排序状态,如果不设置则按当前排序状态对传入数据排序。如果是禁用内部排序的场景,请务必在调用该接口前清空当前的排序状态。 ``` setRecords( records: Array, - option?: { sortState?: SortState | SortState[] } + option?: { sortState?: SortState | SortState[] | null } ): void; ``` @@ -1319,7 +1319,7 @@ interface ISortedMapItem { ## setLoadingHierarchyState(Function) -设置单元格的树形展开收起状态为 loading +设置单元格的树形展开收起状态为 loading ``` /** 设置单元格的树形展开收起状态为 loading */ diff --git a/packages/vtable/src/ListTable.ts b/packages/vtable/src/ListTable.ts index 88a8d2425..daad48206 100644 --- a/packages/vtable/src/ListTable.ts +++ b/packages/vtable/src/ListTable.ts @@ -1096,9 +1096,9 @@ export class ListTable extends BaseTable implements ListTableAPI { /** * 设置表格数据 及排序状态 * @param records - * @param sort + * @param option 附近参数,其中的sortState为排序状态,如果设置null 将清除目前的排序状态 */ - setRecords(records: Array, option?: { sortState?: SortState | SortState[] }): void { + setRecords(records: Array, option?: { sortState?: SortState | SortState[] | null }): void { // 释放事件 及 对象 this.internalProps.dataSource?.release(); // 过滤掉dataSource的引用 From bd670ec4525376734a62917492be5773c93d6b7f Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Fri, 29 Nov 2024 18:31:50 +0800 Subject: [PATCH 19/40] fix: when edit record task date update taskbar occor error #2938 --- .../examples/gantt/gantt-editor.ts | 41 +++++++++- .../examples/gantt/gantt-tree-dependency.ts | 10 ++- packages/vtable-gantt/src/Gantt.ts | 1 + .../vtable-gantt/src/scenegraph/scenegraph.ts | 75 ++++++++++++++++++- .../vtable-gantt/src/state/state-manager.ts | 68 +---------------- packages/vtable/src/data/DataSource.ts | 3 +- 6 files changed, 122 insertions(+), 76 deletions(-) diff --git a/packages/vtable-gantt/examples/gantt/gantt-editor.ts b/packages/vtable-gantt/examples/gantt/gantt-editor.ts index 23b7bf7ba..a6caa8eb2 100644 --- a/packages/vtable-gantt/examples/gantt/gantt-editor.ts +++ b/packages/vtable-gantt/examples/gantt/gantt-editor.ts @@ -1,7 +1,7 @@ import type { ColumnsDefine } from '@visactor/vtable'; import { DateInputEditor, InputEditor } from '@visactor/vtable-editors'; -import type { GanttConstructorOptions, TYPES } from '../../src/index'; -import { Gantt, VTable } from '../../src/index'; +import type { GanttConstructorOptions } from '../../src/index'; +import { Gantt, VTable, TYPES } from '../../src/index'; import { bindDebugTool } from '../../../vtable/src/scenegraph/debug-tool'; const CONTAINER_ID = 'vTable'; const date_input_editor = new DateInputEditor({}); @@ -38,7 +38,7 @@ export function createTable() { priority: 'P1' }, { - id: 1, + id: 4, title: 'Software Development', developer: 'liufangfang.jane@bytedance.com', start: '2024-08-04', @@ -47,7 +47,7 @@ export function createTable() { priority: 'P0' }, { - id: 2, + id: 5, title: 'Scope', developer: 'liufangfang.jane@bytedance.com', start: '07/24/2024', @@ -944,6 +944,39 @@ export function createTable() { } } ], + + dependency: { + links: [ + { + type: TYPES.DependencyType.FinishToStart, + linkedFromTaskKey: 1, + linkedToTaskKey: 2 + }, + { + type: TYPES.DependencyType.StartToFinish, + linkedFromTaskKey: 2, + linkedToTaskKey: 3 + }, + { + type: TYPES.DependencyType.StartToStart, + linkedFromTaskKey: 3, + linkedToTaskKey: 4 + }, + { + type: TYPES.DependencyType.FinishToFinish, + linkedFromTaskKey: 4, + linkedToTaskKey: 5 + } + ], + // linkLineSelectable: false, + linkSelectedLineStyle: { + shadowBlur: 5, //阴影宽度 + shadowColor: 'red', + lineColor: 'red', + lineWidth: 1 + }, + linkCreatable: true + }, rowSeriesNumber: { title: '行号', dragOrder: true, diff --git a/packages/vtable-gantt/examples/gantt/gantt-tree-dependency.ts b/packages/vtable-gantt/examples/gantt/gantt-tree-dependency.ts index 1d0a2534b..92f7d8cdc 100644 --- a/packages/vtable-gantt/examples/gantt/gantt-tree-dependency.ts +++ b/packages/vtable-gantt/examples/gantt/gantt-tree-dependency.ts @@ -1,11 +1,15 @@ import type { ColumnsDefine } from '@visactor/vtable'; import type { GanttConstructorOptions, TYPES } from '../../src/index'; -import { Gantt } from '../../src/index'; +import { DateInputEditor, InputEditor } from '@visactor/vtable-editors'; +import { Gantt, VTable } from '../../src/index'; import { bindDebugTool } from '../../../vtable/src/scenegraph/debug-tool'; import { DependencyType } from '../../src/ts-types'; import { HierarchyState } from '../../../vtable/src/ts-types'; const CONTAINER_ID = 'vTable'; - +const date_input_editor = new DateInputEditor({}); +const input_editor = new InputEditor({}); +VTable.register.editor('input', input_editor); +VTable.register.editor('date-input', date_input_editor); export function createTable() { const records = [ { @@ -179,12 +183,14 @@ export function createTable() { field: 'start', title: 'start', width: 150, + editor: 'date-input', sort: true }, { field: 'end', title: 'end', width: 150, + editor: 'date-input', sort: true }, { diff --git a/packages/vtable-gantt/src/Gantt.ts b/packages/vtable-gantt/src/Gantt.ts index 57de3dd37..49be92678 100644 --- a/packages/vtable-gantt/src/Gantt.ts +++ b/packages/vtable-gantt/src/Gantt.ts @@ -621,6 +621,7 @@ export class Gantt extends EventTarget { _refreshTaskBar(taskShowIndex: number) { // this.taskListTableInstance.updateRecords([record], [index]); this.scenegraph.taskBar.updateTaskBarNode(taskShowIndex); + this.scenegraph.refreshRecordLinkNodes(taskShowIndex, this.scenegraph.taskBar.getTaskBarNodeByIndex(taskShowIndex)); this.scenegraph.updateNextFrame(); } _updateRecordToListTable(record: any, index: number) { diff --git a/packages/vtable-gantt/src/scenegraph/scenegraph.ts b/packages/vtable-gantt/src/scenegraph/scenegraph.ts index 162973caa..d5446d90e 100644 --- a/packages/vtable-gantt/src/scenegraph/scenegraph.ts +++ b/packages/vtable-gantt/src/scenegraph/scenegraph.ts @@ -9,11 +9,12 @@ import { TimelineHeader } from './timeline-header'; import { TaskBar } from './task-bar'; import { MarkLine } from './mark-line'; import { FrameBorder } from './frame-border'; -import { getTaskIndexByY } from '../gantt-helper'; +import { findRecordByTaskKey, getTaskIndexByY } from '../gantt-helper'; import graphicContribution from './graphic'; import { TaskCreationButton } from './task-creation-button'; -import { DependencyLink } from './dependency-link'; +import { DependencyLink, updateLinkLinePoints } from './dependency-link'; import { DragOrderLine } from './drag-order-line'; +import type { GanttTaskBarNode } from './gantt-node'; container.load(graphicContribution); export class Scenegraph { dateStepWidth: number; @@ -274,4 +275,74 @@ export class Scenegraph { this.updateNextFrame(); } } + refreshRecordLinkNodes(taskIndex: number, target: GanttTaskBarNode) { + const gantt: Gantt = this._gantt; + const record = gantt.getRecordByIndex(taskIndex); + const vtable_gantt_linkedTo = record.vtable_gantt_linkedTo; + const vtable_gantt_linkedFrom = record.vtable_gantt_linkedFrom; + for (let i = 0; i < vtable_gantt_linkedTo?.length; i++) { + const link = vtable_gantt_linkedTo[i]; + const linkLineNode = link.vtable_gantt_linkLineNode; + const lineArrowNode = link.vtable_gantt_linkArrowNode; + + const { linkedToTaskKey, linkedFromTaskKey, type } = link; + const { taskKeyField, minDate } = gantt.parsedOptions; + const linkedFromTaskRecord = findRecordByTaskKey(gantt.records, taskKeyField, linkedFromTaskKey); + + const { startDate: linkedToTaskStartDate, endDate: linkedToTaskEndDate } = + gantt.getTaskInfoByTaskListIndex(taskIndex); + const taskShowIndex = gantt.getTaskShowIndexByRecordIndex(linkedFromTaskRecord.index); + const { startDate: linkedFromTaskStartDate, endDate: linkedFromTaskEndDate } = + gantt.getTaskInfoByTaskListIndex(taskShowIndex); + const { linePoints, arrowPoints } = updateLinkLinePoints( + type, + linkedFromTaskStartDate, + linkedFromTaskEndDate, + taskShowIndex, + linkedToTaskStartDate, + linkedToTaskEndDate, + taskIndex, + minDate, + gantt.parsedOptions.rowHeight, + gantt.parsedOptions.colWidthPerDay, + null, + target + ); + linkLineNode.setAttribute('points', linePoints); + lineArrowNode.setAttribute('points', arrowPoints); + } + + for (let i = 0; i < vtable_gantt_linkedFrom?.length; i++) { + const link = vtable_gantt_linkedFrom[i]; + const linkLineNode = link.vtable_gantt_linkLineNode; + const lineArrowNode = link.vtable_gantt_linkArrowNode; + + const { linkedToTaskKey, linkedFromTaskKey, type } = link; + const { taskKeyField, minDate } = gantt.parsedOptions; + const linkedToTaskRecord = findRecordByTaskKey(gantt.records, taskKeyField, linkedToTaskKey); + + const { startDate: linkedFromTaskStartDate, endDate: linkedFromTaskEndDate } = + gantt.getTaskInfoByTaskListIndex(taskIndex); + const taskShowIndex = gantt.getTaskShowIndexByRecordIndex(linkedToTaskRecord.index); + const { startDate: linkedToTaskStartDate, endDate: linkedToTaskEndDate } = + gantt.getTaskInfoByTaskListIndex(taskShowIndex); + const { linePoints, arrowPoints } = updateLinkLinePoints( + type, + linkedFromTaskStartDate, + linkedFromTaskEndDate, + taskIndex, + linkedToTaskStartDate, + linkedToTaskEndDate, + taskShowIndex, + minDate, + gantt.parsedOptions.rowHeight, + gantt.parsedOptions.colWidthPerDay, + target, + null + ); + + linkLineNode.setAttribute('points', linePoints); + lineArrowNode.setAttribute('points', arrowPoints); + } + } } diff --git a/packages/vtable-gantt/src/state/state-manager.ts b/packages/vtable-gantt/src/state/state-manager.ts index f3722ca6e..420565145 100644 --- a/packages/vtable-gantt/src/state/state-manager.ts +++ b/packages/vtable-gantt/src/state/state-manager.ts @@ -789,77 +789,11 @@ function resizeOrMoveTaskBar( state: StateManager ) { // const taskIndex = getTaskIndexByY(state.moveTaskBar.startOffsetY, state._gantt); - const record = state._gantt.getRecordByIndex(taskIndex); if (dx) { target.setAttribute('x', target.attribute.x + dx); } if (newWidth) { target.setAttribute('width', newWidth); } - const vtable_gantt_linkedTo = record.vtable_gantt_linkedTo; - const vtable_gantt_linkedFrom = record.vtable_gantt_linkedFrom; - for (let i = 0; i < vtable_gantt_linkedTo?.length; i++) { - const link = vtable_gantt_linkedTo[i]; - const linkLineNode = link.vtable_gantt_linkLineNode; - const lineArrowNode = link.vtable_gantt_linkArrowNode; - - const { linkedToTaskKey, linkedFromTaskKey, type } = link; - const { taskKeyField, minDate } = state._gantt.parsedOptions; - const linkedFromTaskRecord = findRecordByTaskKey(state._gantt.records, taskKeyField, linkedFromTaskKey); - - const { startDate: linkedToTaskStartDate, endDate: linkedToTaskEndDate } = - state._gantt.getTaskInfoByTaskListIndex(taskIndex); - const taskShowIndex = state._gantt.getTaskShowIndexByRecordIndex(linkedFromTaskRecord.index); - const { startDate: linkedFromTaskStartDate, endDate: linkedFromTaskEndDate } = - state._gantt.getTaskInfoByTaskListIndex(taskShowIndex); - const { linePoints, arrowPoints } = updateLinkLinePoints( - type, - linkedFromTaskStartDate, - linkedFromTaskEndDate, - taskShowIndex, - linkedToTaskStartDate, - linkedToTaskEndDate, - taskIndex, - minDate, - state._gantt.parsedOptions.rowHeight, - state._gantt.parsedOptions.colWidthPerDay, - null, - target - ); - linkLineNode.setAttribute('points', linePoints); - lineArrowNode.setAttribute('points', arrowPoints); - } - - for (let i = 0; i < vtable_gantt_linkedFrom?.length; i++) { - const link = vtable_gantt_linkedFrom[i]; - const linkLineNode = link.vtable_gantt_linkLineNode; - const lineArrowNode = link.vtable_gantt_linkArrowNode; - - const { linkedToTaskKey, linkedFromTaskKey, type } = link; - const { taskKeyField, minDate } = state._gantt.parsedOptions; - const linkedToTaskRecord = findRecordByTaskKey(state._gantt.records, taskKeyField, linkedToTaskKey); - - const { startDate: linkedFromTaskStartDate, endDate: linkedFromTaskEndDate } = - state._gantt.getTaskInfoByTaskListIndex(taskIndex); - const taskShowIndex = state._gantt.getTaskShowIndexByRecordIndex(linkedToTaskRecord.index); - const { startDate: linkedToTaskStartDate, endDate: linkedToTaskEndDate } = - state._gantt.getTaskInfoByTaskListIndex(taskShowIndex); - const { linePoints, arrowPoints } = updateLinkLinePoints( - type, - linkedFromTaskStartDate, - linkedFromTaskEndDate, - taskIndex, - linkedToTaskStartDate, - linkedToTaskEndDate, - taskShowIndex, - minDate, - state._gantt.parsedOptions.rowHeight, - state._gantt.parsedOptions.colWidthPerDay, - target, - null - ); - - linkLineNode.setAttribute('points', linePoints); - lineArrowNode.setAttribute('points', arrowPoints); - } + state._gantt.scenegraph.refreshRecordLinkNodes(taskIndex, target); } diff --git a/packages/vtable/src/data/DataSource.ts b/packages/vtable/src/data/DataSource.ts index 59b86a1ec..7ca3f9d99 100644 --- a/packages/vtable/src/data/DataSource.ts +++ b/packages/vtable/src/data/DataSource.ts @@ -687,7 +687,8 @@ export class DataSource extends EventTarget implements DataSourceAPI { if (!this.beforeChangedRecordsMap[dataIndex]) { const originRecord = this.getOriginalRecord(dataIndex); - this.beforeChangedRecordsMap[dataIndex] = cloneDeep(originRecord) ?? {}; + this.beforeChangedRecordsMap[dataIndex] = + cloneDeep(originRecord, undefined, ['vtable_gantt_linkedFrom', 'vtable_gantt_linkedTo']) ?? {}; } if (typeof field === 'string' || typeof field === 'number') { const beforeChangedValue = this.beforeChangedRecordsMap[dataIndex][field as any]; // this.getOriginalField(index, field, col, row, table); From 9f014d80222dfa3d50b685a0fed52a1daa041491 Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Fri, 29 Nov 2024 18:32:13 +0800 Subject: [PATCH 20/40] docs: update changlog of rush --- ...38-bug-edit-createLink-error_2024-11-29-10-32.json | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 common/changes/@visactor/vtable/2938-bug-edit-createLink-error_2024-11-29-10-32.json diff --git a/common/changes/@visactor/vtable/2938-bug-edit-createLink-error_2024-11-29-10-32.json b/common/changes/@visactor/vtable/2938-bug-edit-createLink-error_2024-11-29-10-32.json new file mode 100644 index 000000000..82a94d0bd --- /dev/null +++ b/common/changes/@visactor/vtable/2938-bug-edit-createLink-error_2024-11-29-10-32.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "fix: when edit record task date update taskbar occor error #2938\n\n", + "type": "none", + "packageName": "@visactor/vtable" + } + ], + "packageName": "@visactor/vtable", + "email": "892739385@qq.com" +} \ No newline at end of file From 86fad3d2b98f1a66e4aaad48b55c8b55eec394e5 Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Fri, 29 Nov 2024 18:34:17 +0800 Subject: [PATCH 21/40] test: update gantt test demo --- .../examples/gantt/gantt-Arrange-customLayout.ts | 4 ++-- .../examples/gantt/gantt-Compact-customLayout.ts | 4 ++-- packages/vtable-gantt/src/state/state-manager.ts | 7 ------- 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/packages/vtable-gantt/examples/gantt/gantt-Arrange-customLayout.ts b/packages/vtable-gantt/examples/gantt/gantt-Arrange-customLayout.ts index 83d6ff01e..d79ecab06 100644 --- a/packages/vtable-gantt/examples/gantt/gantt-Arrange-customLayout.ts +++ b/packages/vtable-gantt/examples/gantt/gantt-Arrange-customLayout.ts @@ -17,14 +17,14 @@ export function createTable() { title: 'Planning', children: [ { - id: 2, + id: 1, name: 'Michael Smith', start: '2024-11-15', end: '2024-11-17', avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' }, { - id: 1, + id: 2, name: 'Emily', start: '2024-11-17', end: '2024-11-18', diff --git a/packages/vtable-gantt/examples/gantt/gantt-Compact-customLayout.ts b/packages/vtable-gantt/examples/gantt/gantt-Compact-customLayout.ts index 1ffc3c198..a0c2594ca 100644 --- a/packages/vtable-gantt/examples/gantt/gantt-Compact-customLayout.ts +++ b/packages/vtable-gantt/examples/gantt/gantt-Compact-customLayout.ts @@ -17,14 +17,14 @@ export function createTable() { title: 'Planning', children: [ { - id: 2, + id: 1, name: 'Michael Smith', start: '2024-11-15', end: '2024-11-17', avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' }, { - id: 1, + id: 2, name: 'Emily', start: '2024-11-17', end: '2024-11-18', diff --git a/packages/vtable-gantt/src/state/state-manager.ts b/packages/vtable-gantt/src/state/state-manager.ts index 830392702..482489070 100644 --- a/packages/vtable-gantt/src/state/state-manager.ts +++ b/packages/vtable-gantt/src/state/state-manager.ts @@ -371,13 +371,6 @@ export class StateManager { this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Compact ) { const indexs = getTaskIndexsByTaskY(targetEndY, this._gantt); - console.log( - 'source target', - target.task_index, - target.sub_task_index, - indexs.task_index, - indexs.sub_task_index - ); this._gantt._dragOrderTaskRecord( target.task_index, target.sub_task_index, From dc49ad13b5862eb2d4e4616f09b0d7d070cdcbfb Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Fri, 29 Nov 2024 19:00:26 +0800 Subject: [PATCH 22/40] fix: when can not find record occor error --- packages/vtable-gantt/src/scenegraph/scenegraph.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/vtable-gantt/src/scenegraph/scenegraph.ts b/packages/vtable-gantt/src/scenegraph/scenegraph.ts index f4195f409..2414b9121 100644 --- a/packages/vtable-gantt/src/scenegraph/scenegraph.ts +++ b/packages/vtable-gantt/src/scenegraph/scenegraph.ts @@ -381,7 +381,9 @@ export class Scenegraph { } else { linkedFromTaskShowIndex = gantt.getTaskShowIndexByRecordIndex(linkedFromTaskRecord.index); linkedToTaskShowIndex = gantt.getTaskShowIndexByRecordIndex(linkedToTaskRecord.index); - + if (linkedFromTaskShowIndex === -1 || linkedToTaskShowIndex === -1) { + continue; + } ({ startDate: linkedToTaskStartDate, endDate: linkedToTaskEndDate, @@ -513,7 +515,9 @@ export class Scenegraph { } else { linkedFromTaskShowIndex = gantt.getTaskShowIndexByRecordIndex(linkedFromTaskRecord.index); linkedToTaskShowIndex = gantt.getTaskShowIndexByRecordIndex(linkedToTaskRecord.index); - + if (linkedFromTaskShowIndex === -1 || linkedToTaskShowIndex === -1) { + continue; + } ({ startDate: linkedToTaskStartDate, endDate: linkedToTaskEndDate, From d6d6e7149078b1fa1943046028ea3386c6e293f7 Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Mon, 2 Dec 2024 14:08:04 +0800 Subject: [PATCH 23/40] fix: drag row order height error --- packages/vtable-gantt/src/scenegraph/scenegraph.ts | 2 +- packages/vtable-gantt/src/state/gantt-table-sync.ts | 11 ++++++++++- packages/vtable-gantt/src/ts-types/gantt-engine.ts | 2 +- packages/vtable/src/layout/row-height-map.ts | 4 ++-- packages/vtable/src/tools/NumberMap.ts | 4 ++-- 5 files changed, 16 insertions(+), 7 deletions(-) diff --git a/packages/vtable-gantt/src/scenegraph/scenegraph.ts b/packages/vtable-gantt/src/scenegraph/scenegraph.ts index 2414b9121..802514253 100644 --- a/packages/vtable-gantt/src/scenegraph/scenegraph.ts +++ b/packages/vtable-gantt/src/scenegraph/scenegraph.ts @@ -277,7 +277,7 @@ export class Scenegraph { } refreshRecordLinkNodes(taskIndex: number, sub_task_index: number, target: GanttTaskBarNode, dy: number = 0) { const gantt: Gantt = this._gantt; - const record = gantt.getRecordByIndex(taskIndex); + const record = gantt.getRecordByIndex(taskIndex, sub_task_index); const vtable_gantt_linkedTo = record.vtable_gantt_linkedTo; const vtable_gantt_linkedFrom = record.vtable_gantt_linkedFrom; for (let i = 0; i < vtable_gantt_linkedTo?.length; i++) { diff --git a/packages/vtable-gantt/src/state/gantt-table-sync.ts b/packages/vtable-gantt/src/state/gantt-table-sync.ts index 0a00402b3..77da9c7fd 100644 --- a/packages/vtable-gantt/src/state/gantt-table-sync.ts +++ b/packages/vtable-gantt/src/state/gantt-table-sync.ts @@ -1,4 +1,5 @@ import type { Gantt } from '../Gantt'; +import { TasksShowMode } from '../ts-types'; export function syncScrollStateToTable(gantt: Gantt) { const { scroll } = gantt.stateManager; @@ -48,7 +49,15 @@ export function syncSortFromTable(gantt: Gantt) { } export function syncDragOrderFromTable(gantt: Gantt) { gantt.taskListTableInstance?.on('change_header_position', (args: any) => { - gantt.scenegraph.refreshTaskBars(); + if ( + gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange || + gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Compact || + gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Separate + ) { + gantt.scenegraph.refreshTaskBarsAndGrid(); + } else { + gantt.scenegraph.refreshTaskBars(); + } gantt.scenegraph.dragOrderLine.hideDragLine(); const left = gantt.stateManager.scroll.horizontalBarPos; const top = gantt.stateManager.scroll.verticalBarPos; diff --git a/packages/vtable-gantt/src/ts-types/gantt-engine.ts b/packages/vtable-gantt/src/ts-types/gantt-engine.ts index 27bd71c05..60881425a 100644 --- a/packages/vtable-gantt/src/ts-types/gantt-engine.ts +++ b/packages/vtable-gantt/src/ts-types/gantt-engine.ts @@ -335,7 +335,7 @@ export enum DependencyType { StartToFinish = 'start_to_finish' } export enum TasksShowMode { - /** 每一个任务节点用单独一行来展示,父任务占用一行,子任务分别占用一行 */ + /** 每一个任务节点用单独一行来展示,父任务占用一行,子任务分别占用一行。这是默认的显示效果 */ Tasks_Separate = 'tasks_separate', /** 省去父任务节点不展示,并把所有子任务的节点都放到同一行来展示。 */ Sub_Tasks_Inline = 'sub_tasks_inline', diff --git a/packages/vtable/src/layout/row-height-map.ts b/packages/vtable/src/layout/row-height-map.ts index 571741af3..eee1199d2 100644 --- a/packages/vtable/src/layout/row-height-map.ts +++ b/packages/vtable/src/layout/row-height-map.ts @@ -300,7 +300,7 @@ export class NumberRangeMap { //先将target部分的值存起来 const targetVals = []; const sourceVals = []; - for (let i = indexFirst(keys, targetIndex); i < sourceIndex + sourceCount; i++) { + for (let i = indexFirst(keys, targetIndex); i < indexFirst(keys, sourceIndex) + sourceCount; i++) { const key = keys[i]; if (key >= sourceIndex && key < sourceIndex + sourceCount) { sourceVals.push(this.get(key)); @@ -319,7 +319,7 @@ export class NumberRangeMap { //先将target部分的值存起来 const targetVals = []; const sourceVals = []; - for (let i = indexFirst(keys, sourceIndex); i < targetIndex + targetCount; i++) { + for (let i = indexFirst(keys, sourceIndex); i < indexFirst(keys, targetIndex) + targetCount; i++) { const key = keys[i]; if (key >= sourceIndex && key < sourceIndex + sourceCount) { sourceVals.push(this.get(key)); diff --git a/packages/vtable/src/tools/NumberMap.ts b/packages/vtable/src/tools/NumberMap.ts index 68ae1bb4f..025aa6efb 100644 --- a/packages/vtable/src/tools/NumberMap.ts +++ b/packages/vtable/src/tools/NumberMap.ts @@ -107,7 +107,7 @@ export class NumberMap { //先将target部分的值存起来 const targetVals = []; const sourceVals = []; - for (let i = indexFirst(keys, targetIndex); i < sourceIndex + sourceCount; i++) { + for (let i = indexFirst(keys, targetIndex); i < indexFirst(keys, sourceIndex) + sourceCount; i++) { const key = keys[i]; if (key >= sourceIndex && key < sourceIndex + sourceCount) { sourceVals.push(this.get(key)); @@ -126,7 +126,7 @@ export class NumberMap { //先将target部分的值存起来 const targetVals = []; const sourceVals = []; - for (let i = indexFirst(keys, sourceIndex); i < targetIndex + targetCount; i++) { + for (let i = indexFirst(keys, sourceIndex); i < indexFirst(keys, targetIndex) + targetCount; i++) { const key = keys[i]; if (key >= sourceIndex && key < sourceIndex + sourceCount) { sourceVals.push(this.get(key)); From 98bdb1caafcd4a8f31e5fcffbb2e1da81ae17639 Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Mon, 2 Dec 2024 14:36:57 +0800 Subject: [PATCH 24/40] fix: records not children occor error --- packages/vtable-gantt/src/gantt-helper.ts | 2 +- packages/vtable-gantt/src/scenegraph/task-bar.ts | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/vtable-gantt/src/gantt-helper.ts b/packages/vtable-gantt/src/gantt-helper.ts index 982fe8945..880161976 100644 --- a/packages/vtable-gantt/src/gantt-helper.ts +++ b/packages/vtable-gantt/src/gantt-helper.ts @@ -731,7 +731,7 @@ function isOverlapping(task: any, rowTasks: any[], startDateField: string, endDa } export function computeRowsCountByRecordDate(record: any, startDateField: string, endDateField: string) { if (!record.children || record.children.length === 1) { - if (record.children.length === 1) { + if (record.children?.length === 1) { record.children[0].vtable_gantt_showIndex = 0; } return 1; diff --git a/packages/vtable-gantt/src/scenegraph/task-bar.ts b/packages/vtable-gantt/src/scenegraph/task-bar.ts index 926c8bfa0..241284f00 100644 --- a/packages/vtable-gantt/src/scenegraph/task-bar.ts +++ b/packages/vtable-gantt/src/scenegraph/task-bar.ts @@ -378,7 +378,8 @@ export class TaskBar { shadowOffsetX: this._scene._gantt.parsedOptions.taskBarSelectedStyle.shadowOffsetX, shadowOffsetY: this._scene._gantt.parsedOptions.taskBarSelectedStyle.shadowOffsetY, shadowBlur: this._scene._gantt.parsedOptions.taskBarSelectedStyle.shadowBlur, - attachedToTaskBarNode: attachedToTaskBarNode + attachedToTaskBarNode: attachedToTaskBarNode, + zIndex: 10000 }); selectedBorder.name = 'task-bar-select-border'; this.barContainer.appendChild(selectedBorder); From faaf8fa88334c400eef7ec5f3dad2802dbca15d1 Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Mon, 2 Dec 2024 19:38:39 +0800 Subject: [PATCH 25/40] docs: add taskShowMode demos and guide --- .../demo/en/gantt/gantt-table-mergeCell.md | 453 ++++++++++++++++++ .../demo/en/gantt/gantt-taskShowMode.md | 358 ++++++++++++++ docs/assets/demo/menu.json | 14 + .../demo/zh/gantt/gantt-table-mergeCell.md | 452 +++++++++++++++++ .../demo/zh/gantt/gantt-taskShowMode.md | 358 ++++++++++++++ docs/assets/guide/en/gantt/subtask_layout.md | 115 +++++ docs/assets/guide/menu.json | 7 + docs/assets/guide/zh/gantt/subtask_layout.md | 115 +++++ docs/assets/option/en/table/gantt.md | 12 + docs/assets/option/zh/table/gantt.md | 12 + .../gantt/gantt-Arrange-customLayout.ts | 135 ++---- .../gantt/gantt-taskMerge-customLayout.ts | 448 +++++++++++++++++ packages/vtable-gantt/examples/menu.ts | 4 + packages/vtable-gantt/src/Gantt.ts | 6 +- packages/vtable-gantt/src/gantt-helper.ts | 4 +- .../vtable-gantt/src/scenegraph/task-bar.ts | 33 +- .../vtable-gantt/src/state/state-manager.ts | 13 +- .../vtable-gantt/src/ts-types/gantt-engine.ts | 8 +- 18 files changed, 2419 insertions(+), 128 deletions(-) create mode 100644 docs/assets/demo/en/gantt/gantt-table-mergeCell.md create mode 100644 docs/assets/demo/en/gantt/gantt-taskShowMode.md create mode 100644 docs/assets/demo/zh/gantt/gantt-table-mergeCell.md create mode 100644 docs/assets/demo/zh/gantt/gantt-taskShowMode.md create mode 100644 docs/assets/guide/en/gantt/subtask_layout.md create mode 100644 docs/assets/guide/zh/gantt/subtask_layout.md create mode 100644 packages/vtable-gantt/examples/gantt/gantt-taskMerge-customLayout.ts diff --git a/docs/assets/demo/en/gantt/gantt-table-mergeCell.md b/docs/assets/demo/en/gantt/gantt-table-mergeCell.md new file mode 100644 index 000000000..622b4971d --- /dev/null +++ b/docs/assets/demo/en/gantt/gantt-table-mergeCell.md @@ -0,0 +1,453 @@ +--- +category: examples +group: gantt +title: cellMerge In Gantt +cover: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/gantt/gantt-table-mergeCell.png +link: '../guide/basic_function/merge_cell' +option: ListTable-columns-text#mergeCell +--- + +# cellMerge In Gantt + +In Gantt, if the task names are the same, the visual effect of parent tasks containing subtasks can be achieved through cell merging. This can be done by setting the `mergeCell` property in `ListTable#columns`. + +## Key Configuration + +- `Gantt` +- `VTable#ListTable#Column#mergeCell` + +## Code Demo + +```javascript livedemo template=vtable +// import * as VTableGantt from '@visactor/vtable-gantt'; +// 使用时需要引入插件包@visactor/vtable-editors +// import * as VTable_editors from '@visactor/vtable-editors'; +// 正常使用方式 const input_editor = new VTable.editors.InputEditor(); +// 官网编辑器中将 VTable.editors重命名成了VTable_editors +const input_editor = new VTable_editors.InputEditor(); +const date_input_editor = new VTable_editors.DateInputEditor(); +VTableGantt.VTable.register.editor('input', input_editor); +VTableGantt.VTable.register.editor('date-input', date_input_editor); +const barColors0 = ['#aecde6', '#c6a49a', '#ffb582', '#eec1de', '#b3d9b3', '#d9d1a5', '#cccccc', '#e59a9c', '#c9bede']; +const barColors = ['#1f77b4', '#8c564b', '#ff7f0e', '#e377c2', '#2ca02c', '#bcbd22', '#7f7f7f', '#d62728', '#9467bd']; + +let ganttInstance; +const records = [ + { + id: 1, + name: 'Michael Smith', + start: '2024-11-15', + end: '2024-11-17', + parentTask: 'Planning', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + }, + { + id: 2, + name: 'Emily', + start: '2024-11-17', + end: '2024-11-18', + parentTask: 'Planning', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + + { + id: 3, + name: 'Rramily', + start: '2024-11-19', + end: '2024-11-20', + parentTask: 'Planning', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 4, + name: 'Lichael Join', + start: '2024-11-18', + end: '2024-11-19', + parentTask: 'Planning', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + }, + + { + id: 5, + name: 'Ryan', + start: '2024-11-18', + end: '2024-11-21', + parentTask: 'Research', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/rabbit.jpg' + }, + { + id: 6, + name: 'Daniel Davis', + start: '2024-11-21', + end: '2024-11-22', + parentTask: 'Goal Setting', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/cat.jpg' + }, + { + id: 7, + name: 'Lauren', + start: '2024-11-18', + end: '2024-11-19', + parentTask: 'Goal Setting', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg' + }, + { + id: 8, + name: 'Tacarah Siller', + start: '2024-11-20', + end: '2024-11-21', + parentTask: 'Strategy', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 9, + name: 'Camentew Olision', + start: '2024-11-25', + end: '2024-11-26', + parentTask: 'Strategy', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + }, + { + id: 10, + name: 'Sarah Miller', + start: '2024-11-17', + end: '2024-11-18', + parentTask: 'Strategy', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 11, + name: 'Matthew Wilson', + start: '2024-11-22', + end: '2024-11-25', + parentTask: 'Strategy', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + }, + { + id: 12, + name: 'Grarah Poliller', + start: '2024-11-23', + end: '2024-11-24', + parentTask: 'Strategy', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 13, + name: 'Ashley Taylor', + start: '2024-11-22', + end: '2024-11-25', + parentTask: 'Execution', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/rabbit.jpg' + }, + { + id: 14, + name: 'Megan', + start: '2024-11-27', + end: '2024-11-30', + parentTask: 'Execution', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/cat.jpg' + }, + { + id: 15, + name: 'David', + start: '2024-12-10', + end: '2024-12-18', + parentTask: 'Execution', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg' + }, + { + id: 16, + name: 'Hannah', + start: '2024-11-20', + end: '2024-11-30', + parentTask: 'Monitoring', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 17, + name: 'Andrew', + start: '2024-12-02', + end: '2024-12-18', + parentTask: 'Monitoring', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + }, + { + id: 18, + name: 'Joshua Anderson', + start: '2024-12-22', + end: '2024-12-28', + parentTask: 'Reporting', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/rabbit.jpg' + }, + { + id: 19, + name: 'Christopher Moore', + start: '2024-11-25', + end: '2024-11-30', + parentTask: 'Process review', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/cat.jpg' + }, + { + id: 20, + name: 'Emma', + start: '2024-12-01', + end: '2024-12-18', + parentTask: 'Process review', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg' + } +]; + +const columns = [ + { + field: 'parentTask', + title: 'Task', + width: 100, + mergeCell: true, + editor: 'input', + style: { + bgColor: '#EEF1F5', + color: '#141414', + fontWeight: 'bold', + fontSize: 16, + autoWrapText: true + } + }, + { + field: 'name', + title: 'Master', + width: 100, + editor: 'input' + }, + { + field: 'start', + title: 'start', + width: 100, + sort: true, + editor: 'date-input' + }, + { + field: 'end', + title: 'end', + width: 100, + sort: true, + editor: 'date-input' + } +]; +const option = { + records, + taskListTable: { + columns: columns, + minTableWidth: 100, + hierarchyExpandLevel: 5, + menu: { + contextMenuItems: ['copy', 'paste', 'delete', '...'] + }, + theme: { + bodyStyle: { + padding: 5, + bgColor: 'white' + }, + headerStyle: { + color: 'white' + } + } + }, + groupBy: true, + tasksShowMode: VTableGantt.TYPES.TasksShowMode.Tasks_Separate, + frame: { + outerFrameStyle: { + borderLineWidth: 1, + borderColor: '#e1e4e8', + cornerRadius: 8 + }, + verticalSplitLineMoveable: false + }, + grid: { + horizontalLine: { + lineWidth: 1, + lineColor: '#e1e4e8' + }, + verticalLine: { + lineWidth: 1, + lineColor: '#e1e4e8' + } + }, + headerRowHeight: 60, + rowHeight: 60, + taskBar: { + startDateField: 'start', + endDateField: 'end', + progressField: 'progress', + labelText: '{name}', + labelTextStyle: { + fontFamily: 'Arial', + fontSize: 16, + textAlign: 'left' + }, + barStyle: { + width: 50, + /** 任务条的颜色 */ + barColor: '#ee8800', + /** 已完成部分任务条的颜色 */ + completedBarColor: '#91e8e0', + /** 任务条的圆角 */ + cornerRadius: 25 + }, + customLayout: args => { + const colorLength = barColors.length; + const { width, height, index, startDate, endDate, taskDays, progress, taskRecord, ganttInstance } = args; + const container = new VRender.Group({ + width, + height, + fill: { + gradient: 'linear', + x0: 0, + y0: 0, + x1: 1, + y1: 0, + stops: [ + { + offset: 0, + color: barColors0[index % colorLength] + }, + { + offset: 0.5, + color: barColors[index % colorLength] + }, + { + offset: 1, + color: barColors0[index % colorLength] + } + ] + }, + display: 'flex', + flexDirection: 'row', + flexWrap: 'nowrap' + }); + const containerLeft = new VRender.Group({ + height, + width: 60, + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'space-around' + // fill: 'red' + }); + container.add(containerLeft); + + const icon0 = new VRender.Image({ + width: 40, + height: 40, + image: taskRecord.avatar, + cornerRadius: 20 + }); + containerLeft.add(icon0); + + const containerRight = new VRender.Group({ + height, + width: width - 60, + display: 'flex', + flexDirection: 'column' + // alignItems: 'left' + }); + container.add(containerRight); + + const bloggerName = new VRender.Text({ + text: taskRecord.name + ' ' + taskRecord.id, + fontSize: 16, + fontFamily: 'sans-serif', + fill: 'white', + maxLineWidth: width - 60, + boundsPadding: [10, 0, 0, 0] + }); + containerRight.add(bloggerName); + + const days = new VRender.Text({ + text: `${taskDays}天`, + fontSize: 13, + fontFamily: 'sans-serif', + fill: 'white', + boundsPadding: [10, 0, 0, 0] + }); + containerRight.add(days); + return { + rootContainer: container + }; + } + }, + dependency: { + linkCreatable: true, + links: [ + { + type: VTableGantt.TYPES.DependencyType.StartToStart, + linkedFromTaskKey: 1, + linkedToTaskKey: 2 + }, + { + type: VTableGantt.TYPES.DependencyType.FinishToStart, + linkedFromTaskKey: 2, + linkedToTaskKey: 3 + }, + { + type: VTableGantt.TYPES.DependencyType.StartToStart, + linkedFromTaskKey: 3, + linkedToTaskKey: 5 + }, + { + type: VTableGantt.TYPES.DependencyType.FinishToFinish, + linkedFromTaskKey: 5, + linkedToTaskKey: 4 + }, + { + type: VTableGantt.TYPES.DependencyType.StartToStart, + linkedFromTaskKey: 8, + linkedToTaskKey: 9 + }, + { + type: VTableGantt.TYPES.DependencyType.FinishToStart, + linkedFromTaskKey: 9, + linkedToTaskKey: 10 + } + ] + }, + timelineHeader: { + colWidth: 100, + verticalLine: { + lineColor: '#e1e4e8', + lineWidth: 1 + }, + horizontalLine: { + lineColor: '#e1e4e8', + lineWidth: 1 + }, + backgroundColor: '#63a8ff', + scales: [ + { + unit: 'day', + step: 1, + format(date) { + return date.dateIndex.toString(); + }, + style: { + fontSize: 20, + fontWeight: 'bold', + color: 'white' + } + } + ] + }, + minDate: '2024-11-14', + maxDate: '2024-12-31', + scrollStyle: { + scrollRailColor: 'RGBA(246,246,246,0.5)', + visible: 'none', + width: 6, + scrollSliderCornerRadius: 2, + scrollSliderColor: '#5cb85c' + } +}; + +ganttInstance = new VTableGantt.Gantt(document.getElementById(CONTAINER_ID), option); + +window['ganttInstance'] = ganttInstance; +``` diff --git a/docs/assets/demo/en/gantt/gantt-taskShowMode.md b/docs/assets/demo/en/gantt/gantt-taskShowMode.md new file mode 100644 index 000000000..a1c63c684 --- /dev/null +++ b/docs/assets/demo/en/gantt/gantt-taskShowMode.md @@ -0,0 +1,358 @@ +--- +category: examples +group: gantt +title: Gantt Chart Sub-task Layout Mode +cover: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/gantt/gantt-taskShowMode.gif +link: '../guide/gantt/subtask_layout' +option: Gantt#tasksShowMode +--- + +# Gantt Chart Sub-task Layout Mode + +In Gantt, the task bar layout mode determines the display effect of the task bars. Gantt provides the following several task bar layout modes: + +- `Tasks_Separate`: Each task node is displayed in a separate row, with the parent task occupying one row and the subtasks occupying one row respectively. This is the default display effect. +- `Sub_Tasks_Separate`: The parent task node is omitted and not displayed, and all subtask nodes are displayed in separate rows. +- `Sub_Tasks_Inline`: The parent task node is omitted and not displayed, and all subtask nodes are placed in the same row. +- `Sub_Tasks_Arrange`: The parent task node is omitted and not displayed, and all subtasks will maintain the data sequence in the records for layout, ensuring that the nodes do not overlap. +- `Sub_Tasks_Compact`: The parent task node is omitted and not displayed, and all subtasks will be laid out according to the date order attribute, ensuring a compact display without node overlap. + +## Key Configuration + +- `Gantt` +- `Gantt#tasksShowMode` + +## Code Demo + +```javascript livedemo template=vtable +// import * as VTableGantt from '@visactor/vtable-gantt'; +let ganttInstance; +const records = [ + { + id: 0, + name: 'Planning', + start: '2024-11-15', + end: '2024-11-21', + children: [ + { + id: 1, + name: 'Michael Smith', + start: '2024-11-15', + end: '2024-11-17', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + }, + { + id: 2, + name: 'Emily', + start: '2024-11-17', + end: '2024-11-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 3, + name: 'Rramily', + start: '2024-11-19', + end: '2024-11-20', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 4, + name: 'Lichael Join', + start: '2024-11-18', + end: '2024-11-19', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + } + ] + }, + { + id: 300, + name: 'Research', + children: [ + { + id: 5, + name: 'Ryan', + start: '2024-11-18', + end: '2024-11-21', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/rabbit.jpg' + } + ] + }, + { + name: 'Goal Setting', + children: [ + { + id: 6, + name: 'Daniel Davis', + start: '2024-11-21', + end: '2024-11-22', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/cat.jpg' + }, + { + id: 7, + name: 'Lauren', + start: '2024-11-18', + end: '2024-11-19', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg' + } + ] + }, + + { + name: 'Strategy', + children: [ + { + id: 8, + name: 'Tacarah Siller', + start: '2024-11-20', + end: '2024-11-21', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 9, + name: 'Camentew Olision', + start: '2024-11-25', + end: '2024-11-26', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + }, + { + id: 10, + name: 'Sarah Miller', + start: '2024-11-17', + end: '2024-11-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 11, + name: 'Matthew Wilson', + start: '2024-11-22', + end: '2024-11-25', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + }, + { + id: 12, + name: 'Grarah Poliller', + start: '2024-11-23', + end: '2024-11-24', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + } + ] + }, + { + name: 'Execution', + children: [ + { + id: 13, + name: 'Ashley Taylor', + start: '2024-11-22', + end: '2024-11-25', + + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/rabbit.jpg' + }, + { + id: 14, + name: 'Megan', + start: '2024-11-27', + end: '2024-11-30', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/cat.jpg' + }, + { + id: 15, + name: 'David', + start: '2024-12-10', + end: '2024-12-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg' + } + ] + }, + { + name: 'Monitoring', + children: [ + { + id: 16, + name: 'Hannah', + start: '2024-11-20', + end: '2024-11-30', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 17, + name: 'Andrew', + start: '2024-12-02', + end: '2024-12-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + } + ] + }, + { + title: 'Reporting', + children: [ + { + id: 18, + name: 'Joshua Anderson', + start: '2024-12-22', + end: '2024-12-28', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/rabbit.jpg' + } + ] + }, + { + name: 'Process review', + children: [ + { + id: 19, + name: 'Christopher Moore', + start: '2024-11-25', + end: '2024-11-30', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/cat.jpg' + }, + { + id: 20, + name: 'Emma', + start: '2024-12-01', + end: '2024-12-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg' + } + ] + } +]; + +const columns = [ + { + field: 'name', + title: 'PROCESS', + width: 150, + tree: true + } +]; +const option = { + records, + taskListTable: { + columns: columns, + theme: { + bodyStyle: { + bgColor: 'white', + color: 'rgb(115 115 115)' + }, + headerStyle: { + color: 'white' + } + } + }, + groupBy: true, + tasksShowMode: VTableGantt.TYPES.TasksShowMode.Sub_Tasks_Arrange, + frame: { + outerFrameStyle: { + borderLineWidth: 1, + borderColor: '#e1e4e8', + cornerRadius: 8 + }, + verticalSplitLineMoveable: false + }, + grid: { + horizontalLine: { + lineWidth: 1, + lineColor: '#e1e4e8' + }, + verticalLine: { + lineWidth: 1, + lineColor: '#e1e4e8' + } + }, + headerRowHeight: 60, + rowHeight: 40, + taskBar: { + startDateField: 'start', + endDateField: 'end', + progressField: 'progress', + labelText: '{name}', + labelTextStyle: { + fontFamily: 'Arial', + fontSize: 14, + textAlign: 'center', + color: 'white' + }, + barStyle: { + width: 22, + /** 任务条的颜色 */ + barColor: 'rgb(68 99 244)', + /** 已完成部分任务条的颜色 */ + completedBarColor: '#91e8e0', + /** 任务条的圆角 */ + cornerRadius: 15, + borderColor: 'black', + borderWidth: 1 + } + }, + dependency: { + linkCreatable: true, + links: [ + { + type: VTableGantt.TYPES.DependencyType.StartToStart, + linkedFromTaskKey: 1, + linkedToTaskKey: 2 + }, + { + type: VTableGantt.TYPES.DependencyType.FinishToStart, + linkedFromTaskKey: 2, + linkedToTaskKey: 3 + }, + { + type: VTableGantt.TYPES.DependencyType.StartToStart, + linkedFromTaskKey: 3, + linkedToTaskKey: 5 + }, + { + type: VTableGantt.TYPES.DependencyType.FinishToFinish, + linkedFromTaskKey: 5, + linkedToTaskKey: 4 + }, + { + type: VTableGantt.TYPES.DependencyType.StartToStart, + linkedFromTaskKey: 8, + linkedToTaskKey: 9 + }, + { + type: VTableGantt.TYPES.DependencyType.FinishToStart, + linkedFromTaskKey: 9, + linkedToTaskKey: 10 + } + ] + }, + timelineHeader: { + verticalLine: { + lineColor: '#e1e4e8', + lineWidth: 1 + }, + horizontalLine: { + lineColor: '#e1e4e8', + lineWidth: 1 + }, + backgroundColor: '#63a8ff', + scales: [ + { + unit: 'day', + step: 1, + format(date) { + return date.dateIndex.toString(); + }, + style: { + fontSize: 20, + fontWeight: 'bold', + color: 'white' + } + } + ] + }, + minDate: '2024-11-14', + maxDate: '2024-12-31', + scrollStyle: { + scrollRailColor: 'RGBA(246,246,246,0.5)', + visible: 'none', + width: 6, + scrollSliderCornerRadius: 2, + scrollSliderColor: '#5cb85c' + } +}; +ganttInstance = new VTableGantt.Gantt(document.getElementById(CONTAINER_ID), option); +window['ganttInstance'] = ganttInstance; +``` diff --git a/docs/assets/demo/menu.json b/docs/assets/demo/menu.json index e4fe1b18a..f5b0c5c94 100644 --- a/docs/assets/demo/menu.json +++ b/docs/assets/demo/menu.json @@ -302,6 +302,20 @@ "zh": "甘特图交互——创建排期", "en": "Gantt Interaction —— Create Schedule" } + }, + { + "path": "gantt-taskShowMode", + "title": { + "zh": "甘特图子任务布局模式", + "en": "Gantt Subtask Show Mode" + } + }, + { + "path": "gantt-table-mergeCell", + "title": { + "zh": "任务名称单元格合并", + "en": "Gantt Task Merge Cell" + } } ] }, diff --git a/docs/assets/demo/zh/gantt/gantt-table-mergeCell.md b/docs/assets/demo/zh/gantt/gantt-table-mergeCell.md new file mode 100644 index 000000000..1485c3db8 --- /dev/null +++ b/docs/assets/demo/zh/gantt/gantt-table-mergeCell.md @@ -0,0 +1,452 @@ +--- +category: examples +group: gantt +title: 任务名称单元格合并 +cover: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/gantt/gantt-table-mergeCell.png +link: '../guide/basic_function/merge_cell' +option: ListTable-columns-text#mergeCell +--- + +# 任务名称单元格合并 + +在 Gantt 中,任务名称如果是一样的,可以通过单元格合来实现父级任务包含子任务的视觉效果。可以通过设置 `ListTable#columns` 中的 `mergeCell` 属性来实现。 + +## 关键配置 + +- `Gantt` +- `VTable#ListTable#Column#mergeCell` + +## 代码演示 + +```javascript livedemo template=vtable +// import * as VTableGantt from '@visactor/vtable-gantt'; +// 使用时需要引入插件包@visactor/vtable-editors +// import * as VTable_editors from '@visactor/vtable-editors'; +// 正常使用方式 const input_editor = new VTable.editors.InputEditor(); +// 官网编辑器中将 VTable.editors重命名成了VTable_editors +const input_editor = new VTable_editors.InputEditor(); +const date_input_editor = new VTable_editors.DateInputEditor(); +VTableGantt.VTable.register.editor('input', input_editor); +VTableGantt.VTable.register.editor('date-input', date_input_editor); +const barColors0 = ['#aecde6', '#c6a49a', '#ffb582', '#eec1de', '#b3d9b3', '#d9d1a5', '#cccccc', '#e59a9c', '#c9bede']; +const barColors = ['#1f77b4', '#8c564b', '#ff7f0e', '#e377c2', '#2ca02c', '#bcbd22', '#7f7f7f', '#d62728', '#9467bd']; + +let ganttInstance; +const records = [ + { + id: 1, + name: 'Michael Smith', + start: '2024-11-15', + end: '2024-11-17', + parentTask: 'Planning', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + }, + { + id: 2, + name: 'Emily', + start: '2024-11-17', + end: '2024-11-18', + parentTask: 'Planning', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + + { + id: 3, + name: 'Rramily', + start: '2024-11-19', + end: '2024-11-20', + parentTask: 'Planning', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 4, + name: 'Lichael Join', + start: '2024-11-18', + end: '2024-11-19', + parentTask: 'Planning', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + }, + + { + id: 5, + name: 'Ryan', + start: '2024-11-18', + end: '2024-11-21', + parentTask: 'Research', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/rabbit.jpg' + }, + { + id: 6, + name: 'Daniel Davis', + start: '2024-11-21', + end: '2024-11-22', + parentTask: 'Goal Setting', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/cat.jpg' + }, + { + id: 7, + name: 'Lauren', + start: '2024-11-18', + end: '2024-11-19', + parentTask: 'Goal Setting', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg' + }, + { + id: 8, + name: 'Tacarah Siller', + start: '2024-11-20', + end: '2024-11-21', + parentTask: 'Strategy', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 9, + name: 'Camentew Olision', + start: '2024-11-25', + end: '2024-11-26', + parentTask: 'Strategy', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + }, + { + id: 10, + name: 'Sarah Miller', + start: '2024-11-17', + end: '2024-11-18', + parentTask: 'Strategy', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 11, + name: 'Matthew Wilson', + start: '2024-11-22', + end: '2024-11-25', + parentTask: 'Strategy', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + }, + { + id: 12, + name: 'Grarah Poliller', + start: '2024-11-23', + end: '2024-11-24', + parentTask: 'Strategy', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 13, + name: 'Ashley Taylor', + start: '2024-11-22', + end: '2024-11-25', + parentTask: 'Execution', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/rabbit.jpg' + }, + { + id: 14, + name: 'Megan', + start: '2024-11-27', + end: '2024-11-30', + parentTask: 'Execution', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/cat.jpg' + }, + { + id: 15, + name: 'David', + start: '2024-12-10', + end: '2024-12-18', + parentTask: 'Execution', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg' + }, + { + id: 16, + name: 'Hannah', + start: '2024-11-20', + end: '2024-11-30', + parentTask: 'Monitoring', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 17, + name: 'Andrew', + start: '2024-12-02', + end: '2024-12-18', + parentTask: 'Monitoring', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + }, + { + id: 18, + name: 'Joshua Anderson', + start: '2024-12-22', + end: '2024-12-28', + parentTask: 'Reporting', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/rabbit.jpg' + }, + { + id: 19, + name: 'Christopher Moore', + start: '2024-11-25', + end: '2024-11-30', + parentTask: 'Process review', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/cat.jpg' + }, + { + id: 20, + name: 'Emma', + start: '2024-12-01', + end: '2024-12-18', + parentTask: 'Process review', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg' + } +]; + +const columns = [ + { + field: 'parentTask', + title: 'Task', + width: 100, + mergeCell: true, + editor: 'input', + style: { + bgColor: '#EEF1F5', + color: '#141414', + fontWeight: 'bold', + fontSize: 16, + autoWrapText: true + } + }, + { + field: 'name', + title: 'Master', + width: 100, + editor: 'input' + }, + { + field: 'start', + title: 'start', + width: 100, + sort: true, + editor: 'date-input' + }, + { + field: 'end', + title: 'end', + width: 100, + sort: true, + editor: 'date-input' + } +]; +const option = { + records, + taskListTable: { + columns: columns, + minTableWidth: 100, + hierarchyExpandLevel: 5, + menu: { + contextMenuItems: ['copy', 'paste', 'delete', '...'] + }, + theme: { + bodyStyle: { + padding: 5, + bgColor: 'white' + }, + headerStyle: { + color: 'white' + } + } + }, + groupBy: true, + tasksShowMode: VTableGantt.TYPES.TasksShowMode.Tasks_Separate, + frame: { + outerFrameStyle: { + borderLineWidth: 1, + borderColor: '#e1e4e8', + cornerRadius: 8 + }, + verticalSplitLineMoveable: false + }, + grid: { + horizontalLine: { + lineWidth: 1, + lineColor: '#e1e4e8' + }, + verticalLine: { + lineWidth: 1, + lineColor: '#e1e4e8' + } + }, + headerRowHeight: 60, + rowHeight: 60, + taskBar: { + startDateField: 'start', + endDateField: 'end', + progressField: 'progress', + labelText: '{name}', + labelTextStyle: { + fontFamily: 'Arial', + fontSize: 16, + textAlign: 'left' + }, + barStyle: { + width: 50, + /** 任务条的颜色 */ + barColor: '#ee8800', + /** 已完成部分任务条的颜色 */ + completedBarColor: '#91e8e0', + /** 任务条的圆角 */ + cornerRadius: 25 + }, + customLayout: args => { + const colorLength = barColors.length; + const { width, height, index, startDate, endDate, taskDays, progress, taskRecord, ganttInstance } = args; + const container = new VRender.Group({ + width, + height, + fill: { + gradient: 'linear', + x0: 0, + y0: 0, + x1: 1, + y1: 0, + stops: [ + { + offset: 0, + color: barColors0[index % colorLength] + }, + { + offset: 0.5, + color: barColors[index % colorLength] + }, + { + offset: 1, + color: barColors0[index % colorLength] + } + ] + }, + display: 'flex', + flexDirection: 'row', + flexWrap: 'nowrap' + }); + const containerLeft = new VRender.Group({ + height, + width: 60, + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'space-around' + // fill: 'red' + }); + container.add(containerLeft); + + const icon0 = new VRender.Image({ + width: 40, + height: 40, + image: taskRecord.avatar, + cornerRadius: 20 + }); + containerLeft.add(icon0); + + const containerRight = new VRender.Group({ + height, + width: width - 60, + display: 'flex', + flexDirection: 'column' + // alignItems: 'left' + }); + container.add(containerRight); + + const bloggerName = new VRender.Text({ + text: taskRecord.name + ' ' + taskRecord.id, + fontSize: 16, + fontFamily: 'sans-serif', + fill: 'white', + maxLineWidth: width - 60, + boundsPadding: [10, 0, 0, 0] + }); + containerRight.add(bloggerName); + + const days = new VRender.Text({ + text: `${taskDays}天`, + fontSize: 13, + fontFamily: 'sans-serif', + fill: 'white', + boundsPadding: [10, 0, 0, 0] + }); + containerRight.add(days); + return { + rootContainer: container + }; + } + }, + dependency: { + linkCreatable: true, + links: [ + { + type: VTableGantt.TYPES.DependencyType.StartToStart, + linkedFromTaskKey: 1, + linkedToTaskKey: 2 + }, + { + type: VTableGantt.TYPES.DependencyType.FinishToStart, + linkedFromTaskKey: 2, + linkedToTaskKey: 3 + }, + { + type: VTableGantt.TYPES.DependencyType.StartToStart, + linkedFromTaskKey: 3, + linkedToTaskKey: 5 + }, + { + type: VTableGantt.TYPES.DependencyType.FinishToFinish, + linkedFromTaskKey: 5, + linkedToTaskKey: 4 + }, + { + type: VTableGantt.TYPES.DependencyType.StartToStart, + linkedFromTaskKey: 8, + linkedToTaskKey: 9 + }, + { + type: VTableGantt.TYPES.DependencyType.FinishToStart, + linkedFromTaskKey: 9, + linkedToTaskKey: 10 + } + ] + }, + timelineHeader: { + colWidth: 100, + verticalLine: { + lineColor: '#e1e4e8', + lineWidth: 1 + }, + horizontalLine: { + lineColor: '#e1e4e8', + lineWidth: 1 + }, + backgroundColor: '#63a8ff', + scales: [ + { + unit: 'day', + step: 1, + format(date) { + return date.dateIndex.toString(); + }, + style: { + fontSize: 20, + fontWeight: 'bold', + color: 'white' + } + } + ] + }, + minDate: '2024-11-14', + maxDate: '2024-12-31', + scrollStyle: { + scrollRailColor: 'RGBA(246,246,246,0.5)', + visible: 'none', + width: 6, + scrollSliderCornerRadius: 2, + scrollSliderColor: '#5cb85c' + } +}; + +ganttInstance = new VTableGantt.Gantt(document.getElementById(CONTAINER_ID), option); +window['ganttInstance'] = ganttInstance; +``` diff --git a/docs/assets/demo/zh/gantt/gantt-taskShowMode.md b/docs/assets/demo/zh/gantt/gantt-taskShowMode.md new file mode 100644 index 000000000..a1a5ac32e --- /dev/null +++ b/docs/assets/demo/zh/gantt/gantt-taskShowMode.md @@ -0,0 +1,358 @@ +--- +category: examples +group: gantt +title: 甘特图子任务布局模式 +cover: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/gantt/gantt-taskShowMode.gif +link: '../guide/gantt/subtask_layout' +option: Gantt#tasksShowMode +--- + +# 甘特图子任务布局模式 + +在 Gantt 中,任务条布局模式决定了任务条的显示效果。Gantt 提供了以下几种任务条布局模式: + +- `Tasks_Separate`: 每一个任务节点用单独一行来展示,父任务占用一行,子任务分别占用一行。这是默认的显示效果 +- `Sub_Tasks_Separate`: 省去父任务节点不展示,且所有子任务的节点分别用一行展示。 +- `Sub_Tasks_Inline`: 省去父任务节点不展示,并把所有子任务的节点都放到同一行来展示。 +- `Sub_Tasks_Arrange`: 省去父任务节点不展示,且所有子任务会维持 records 中的数据顺序布局,并保证节点不重叠展示。 +- `Sub_Tasks_Compact`: 省去父任务节点不展示,且所有子任务会按照日期早晚的属性来布局,并保证节点不重叠的紧凑型展示。 + +## 关键配置 + +- `Gantt` +- `Gantt#tasksShowMode` + +## 代码演示 + +```javascript livedemo template=vtable +// import * as VTableGantt from '@visactor/vtable-gantt'; +let ganttInstance; +const records = [ + { + id: 0, + name: 'Planning', + start: '2024-11-15', + end: '2024-11-21', + children: [ + { + id: 1, + name: 'Michael Smith', + start: '2024-11-15', + end: '2024-11-17', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + }, + { + id: 2, + name: 'Emily', + start: '2024-11-17', + end: '2024-11-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 3, + name: 'Rramily', + start: '2024-11-19', + end: '2024-11-20', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 4, + name: 'Lichael Join', + start: '2024-11-18', + end: '2024-11-19', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + } + ] + }, + { + id: 300, + name: 'Research', + children: [ + { + id: 5, + name: 'Ryan', + start: '2024-11-18', + end: '2024-11-21', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/rabbit.jpg' + } + ] + }, + { + name: 'Goal Setting', + children: [ + { + id: 6, + name: 'Daniel Davis', + start: '2024-11-21', + end: '2024-11-22', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/cat.jpg' + }, + { + id: 7, + name: 'Lauren', + start: '2024-11-18', + end: '2024-11-19', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg' + } + ] + }, + + { + name: 'Strategy', + children: [ + { + id: 8, + name: 'Tacarah Siller', + start: '2024-11-20', + end: '2024-11-21', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 9, + name: 'Camentew Olision', + start: '2024-11-25', + end: '2024-11-26', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + }, + { + id: 10, + name: 'Sarah Miller', + start: '2024-11-17', + end: '2024-11-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 11, + name: 'Matthew Wilson', + start: '2024-11-22', + end: '2024-11-25', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + }, + { + id: 12, + name: 'Grarah Poliller', + start: '2024-11-23', + end: '2024-11-24', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + } + ] + }, + { + name: 'Execution', + children: [ + { + id: 13, + name: 'Ashley Taylor', + start: '2024-11-22', + end: '2024-11-25', + + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/rabbit.jpg' + }, + { + id: 14, + name: 'Megan', + start: '2024-11-27', + end: '2024-11-30', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/cat.jpg' + }, + { + id: 15, + name: 'David', + start: '2024-12-10', + end: '2024-12-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg' + } + ] + }, + { + name: 'Monitoring', + children: [ + { + id: 16, + name: 'Hannah', + start: '2024-11-20', + end: '2024-11-30', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 17, + name: 'Andrew', + start: '2024-12-02', + end: '2024-12-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + } + ] + }, + { + title: 'Reporting', + children: [ + { + id: 18, + name: 'Joshua Anderson', + start: '2024-12-22', + end: '2024-12-28', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/rabbit.jpg' + } + ] + }, + { + name: 'Process review', + children: [ + { + id: 19, + name: 'Christopher Moore', + start: '2024-11-25', + end: '2024-11-30', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/cat.jpg' + }, + { + id: 20, + name: 'Emma', + start: '2024-12-01', + end: '2024-12-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg' + } + ] + } +]; + +const columns = [ + { + field: 'name', + title: 'PROCESS', + width: 150, + tree: true + } +]; +const option = { + records, + taskListTable: { + columns: columns, + theme: { + bodyStyle: { + bgColor: 'white', + color: 'rgb(115 115 115)' + }, + headerStyle: { + color: 'white' + } + } + }, + groupBy: true, + tasksShowMode: VTableGantt.TYPES.TasksShowMode.Sub_Tasks_Arrange, + frame: { + outerFrameStyle: { + borderLineWidth: 1, + borderColor: '#e1e4e8', + cornerRadius: 8 + }, + verticalSplitLineMoveable: false + }, + grid: { + horizontalLine: { + lineWidth: 1, + lineColor: '#e1e4e8' + }, + verticalLine: { + lineWidth: 1, + lineColor: '#e1e4e8' + } + }, + headerRowHeight: 60, + rowHeight: 40, + taskBar: { + startDateField: 'start', + endDateField: 'end', + progressField: 'progress', + labelText: '{name}', + labelTextStyle: { + fontFamily: 'Arial', + fontSize: 14, + textAlign: 'center', + color: 'white' + }, + barStyle: { + width: 22, + /** 任务条的颜色 */ + barColor: 'rgb(68 99 244)', + /** 已完成部分任务条的颜色 */ + completedBarColor: '#91e8e0', + /** 任务条的圆角 */ + cornerRadius: 15, + borderColor: 'black', + borderWidth: 1 + } + }, + dependency: { + linkCreatable: true, + links: [ + { + type: VTableGantt.TYPES.DependencyType.StartToStart, + linkedFromTaskKey: 1, + linkedToTaskKey: 2 + }, + { + type: VTableGantt.TYPES.DependencyType.FinishToStart, + linkedFromTaskKey: 2, + linkedToTaskKey: 3 + }, + { + type: VTableGantt.TYPES.DependencyType.StartToStart, + linkedFromTaskKey: 3, + linkedToTaskKey: 5 + }, + { + type: VTableGantt.TYPES.DependencyType.FinishToFinish, + linkedFromTaskKey: 5, + linkedToTaskKey: 4 + }, + { + type: VTableGantt.TYPES.DependencyType.StartToStart, + linkedFromTaskKey: 8, + linkedToTaskKey: 9 + }, + { + type: VTableGantt.TYPES.DependencyType.FinishToStart, + linkedFromTaskKey: 9, + linkedToTaskKey: 10 + } + ] + }, + timelineHeader: { + verticalLine: { + lineColor: '#e1e4e8', + lineWidth: 1 + }, + horizontalLine: { + lineColor: '#e1e4e8', + lineWidth: 1 + }, + backgroundColor: '#63a8ff', + scales: [ + { + unit: 'day', + step: 1, + format(date) { + return date.dateIndex.toString(); + }, + style: { + fontSize: 20, + fontWeight: 'bold', + color: 'white' + } + } + ] + }, + minDate: '2024-11-14', + maxDate: '2024-12-31', + scrollStyle: { + scrollRailColor: 'RGBA(246,246,246,0.5)', + visible: 'none', + width: 6, + scrollSliderCornerRadius: 2, + scrollSliderColor: '#5cb85c' + } +}; +ganttInstance = new VTableGantt.Gantt(document.getElementById(CONTAINER_ID), option); +window['ganttInstance'] = ganttInstance; +``` diff --git a/docs/assets/guide/en/gantt/subtask_layout.md b/docs/assets/guide/en/gantt/subtask_layout.md new file mode 100644 index 000000000..49d7e8795 --- /dev/null +++ b/docs/assets/guide/en/gantt/subtask_layout.md @@ -0,0 +1,115 @@ +# Gantt Chart Task Bar Layout Modes + +In the Gantt chart, the layout mode of the task bar determines the display effect of the task bar. The Gantt chart provides the following several task bar layout modes: + +- `Tasks_Separate`: Each task node is displayed in a separate row, with the parent task occupying one row and the subtasks occupying separate rows respectively. This is the default display effect. +- `Sub_Tasks_Separate`: The parent task node is omitted and not displayed, and all subtask nodes are displayed in separate rows. +- `Sub_Tasks_Inline`: The parent task node is omitted and not displayed, and all subtask nodes are placed in the same row. +- `Sub_Tasks_Arrange`: The parent task node is omitted and not displayed, and all subtasks will maintain the data order in the records for layout, ensuring that the nodes do not overlap. +- `Sub_Tasks_Compact`: The parent task node is omitted and not displayed, and all subtasks will be laid out according to the date attribute from earliest to latest, ensuring a compact display without node overlap. + +Among these layout modes, except for the default `Tasks_Separate` mode, the other modes only display the task bars of subtasks. + +# Configuration Method + +The configuration method for the Gantt chart task bar layout mode is as follows: + +``` +import * as VTableGantt from '@visactor/vtable-gantt'; +const options={ + ... + tasksShowMode: VTableGantt.TYPES.TasksShowMode.Sub_Tasks_Compact, + ... +} +``` + +# Example Display + +The following shows the effects of different task bar layout patterns, such as the following data: + +``` +const options={ + ... + records:[ + { + id: 0, + name: 'Planning', + start: '2024-11-15', + end: '2024-11-21', + children: [ + { + id: 1, + name: 'Michael Smith', + start: '2024-11-15', + end: '2024-11-17', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + }, + { + id: 2, + name: 'Emily', + start: '2024-11-17', + end: '2024-11-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 3, + name: 'Rramily', + start: '2024-11-19', + end: '2024-11-20', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 4, + name: 'Lichael Join', + start: '2024-11-18', + end: '2024-11-19', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + } + ] + }, + ... + ] +} +``` + +## Tasks_Separate + +`Tasks_Separate` is the default display effect, that is, each task node is displayed in a separate row. + +The display effect using the above data is: the parent task `id: 0` occupies one row, and the subtasks `id: 1~4` occupy one row respectively. The layout effect is as follows: + +![image](https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/gantt/gantt-task-separate.png) + +## Sub_Tasks_Separate + +`Sub_Tasks_Separate` omits the parent task node and does not display it, and places all subtask nodes in separate rows. + +The display effect using the above data is: the parent task `id: 0` only shows the task name in the first column, and the subtasks `id: 1~4` occupy one row respectively (note that there is no row separator here). The layout effect is as follows: + +![image](https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/gantt/gantt-sub-task-separate.png) + +## Sub_Tasks_Inline + +`Sub_Tasks_Inline` omits the parent task node and does not display it, and places all subtask nodes in the same row. + +The display effect using the above data is: the parent task `id: 0` only shows the task name in the first column, and the subtasks `id: 1~4` are all displayed in the same row. The layout effect is as follows: + +![image](https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/gantt/gantt-sub-task-inline.png) + +## Sub_Tasks_Arrange + +`Sub_Tasks_Arrange` omits the parent task node and does not display it, and all subtasks will maintain the data order in the records for layout, ensuring that the nodes do not overlap. + +The display effect using the above data is: the parent task `id: 0` only shows the task name in the first column, and the subtasks `id: 1~4` will maintain the data order in the records for layout, ensuring that the nodes do not overlap. The layout effect is as follows: + +![image](https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/gantt/gantt-sub-task-arrange.png) + +## Sub_Tasks_Compact + +`Sub_Tasks_Compact` omits the parent task node and does not display it, and all subtasks will be laid out according to the date attribute from earliest to latest, ensuring a compact display without node overlap. + +The display effect using the above data is: the parent task `id: 0` only shows the task name in the first column, and the subtasks `id: 1~4` will be laid out according to the `start` date from earliest to latest, ensuring a compact display without node overlap. The layout effect is as follows: + +![image](https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/gantt/gantt-sub-task-compact.png) + +**Note: The difference between `Sub_Tasks_Compact` and `Sub_Tasks_Arrange` is that for `Arrange`, the data order in the records will not be changed, but the layout is not the most compact. For `Compact`, the data order in the records will be changed, but the layout is the most compact, and when the position of the task is moved, re-layout will be triggered.** diff --git a/docs/assets/guide/menu.json b/docs/assets/guide/menu.json index cca7e45af..174b845c5 100644 --- a/docs/assets/guide/menu.json +++ b/docs/assets/guide/menu.json @@ -176,6 +176,13 @@ "zh": "甘特图编辑", "en": "Gantt Edit" } + }, + { + "path": "subtask_layout", + "title": { + "zh": "子任务布局", + "en": "Subtask Layout" + } } ] }, diff --git a/docs/assets/guide/zh/gantt/subtask_layout.md b/docs/assets/guide/zh/gantt/subtask_layout.md new file mode 100644 index 000000000..b926fa44b --- /dev/null +++ b/docs/assets/guide/zh/gantt/subtask_layout.md @@ -0,0 +1,115 @@ +# 甘特图任务条布局模式 + +在 Gantt 中,任务条的布局模式决定了任务条的显示效果。Gantt 提供了以下几种任务条布局模式: + +- `Tasks_Separate`: 每一个任务节点用单独一行来展示,父任务占用一行,子任务分别占用一行。这是默认的显示效果 +- `Sub_Tasks_Separate`: 省去父任务节点不展示,且所有子任务的节点分别用一行展示。 +- `Sub_Tasks_Inline`: 省去父任务节点不展示,并把所有子任务的节点都放到同一行来展示。 +- `Sub_Tasks_Arrange`: 省去父任务节点不展示,且所有子任务会维持 records 中的数据顺序布局,并保证节点不重叠展示。 +- `Sub_Tasks_Compact`: 省去父任务节点不展示,且所有子任务会按照日期早晚的属性来布局,并保证节点不重叠的紧凑型展示。 + +在这几种布局模式中,除了默认的`Tasks_Separate`模式以外,其他几种模式都是只显示子任务的任务条。 + +# 配置方式 + +Gantt 任务条布局模式的配置方式如下: + +``` +import * as VTableGantt from '@visactor/vtable-gantt'; +const options={ + ... + tasksShowMode: VTableGantt.TYPES.TasksShowMode.Sub_Tasks_Compact, + ... +} +``` + +# 示例展示 + +下面分别展示不同的任务条布局模式的效果,以下面的一份数据为例: + +``` +const options={ + ... + records:[ + { + id: 0, + name: 'Planning', + start: '2024-11-15', + end: '2024-11-21', + children: [ + { + id: 1, + name: 'Michael Smith', + start: '2024-11-15', + end: '2024-11-17', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + }, + { + id: 2, + name: 'Emily', + start: '2024-11-17', + end: '2024-11-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 3, + name: 'Rramily', + start: '2024-11-19', + end: '2024-11-20', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 4, + name: 'Lichael Join', + start: '2024-11-18', + end: '2024-11-19', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + } + ] + }, + ... + ] +} +``` + +## Tasks_Separate + +`Tasks_Separate`是默认的显示效果,即每一个任务节点用单独一行来展示。 + +利用上面数据的展示效果为:父任务`id: 0`占用一行,子任务`id: 1~4`分别占用一行。布局效果如下: + +![image](https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/gantt/gantt-task-separate.png) + +## Sub_Tasks_Separate + +`Sub_Tasks_Separate`省去父任务节点不展示,并把所有子任务的节点分别放置到单独的一行来展示。 + +利用上面数据的展示效果为:父任务`id: 0`只在第一列展示了任务名称,子任务`id: 1~4`分别占用一行(注意这里没有行分割线)。布局效果如下: + +![image](https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/gantt/gantt-sub-task-separate.png) + +## Sub_Tasks_Inline + +`Sub_Tasks_Inline`省去父任务节点不展示,并把所有子任务的节点都放到同一行来展示。 + +利用上面数据的展示效果为:父任务`id: 0`只在第一列展示了任务名称,子任务`id: 1~4`都在同一行展示。布局效果如下: + +![image](https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/gantt/gantt-sub-task-inline.png) + +## Sub_Tasks_Arrange + +`Sub_Tasks_Arrange`省去父任务节点不展示,且所有子任务会维持 records 中的数据顺序布局,并保证节点不重叠展示。 + +利用上面数据的展示效果为:父任务`id: 0`只在第一列展示了任务名称,子任务`id: 1~4`会按照 records 中的数据顺序布局,并保证节点不重叠展示。布局效果如下: + +![image](https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/gantt/gantt-sub-task-arrange.png) + +## Sub_Tasks_Compact + +`Sub_Tasks_Compact`省去父任务节点不展示,且所有子任务会按照日期早晚的属性来布局,并保证节点不重叠的紧凑型展示。 + +利用上面数据的展示效果为:父任务`id: 0`只在第一列展示了任务名称,子任务`id: 1~4`会按照日期`start`早晚的属性来布局,并保证节点不重叠的紧凑型展示。布局效果如下: + +![image](https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/gantt/gantt-sub-task-compact.png) + +**注意:`Sub_Tasks_Compact`和`Sub_Tasks_Arrange`的区别在于`Arrange`的话,不会改变 records 中的数据顺序,但是布局不是最紧密的。而`Compact`的话,会改变 records 中的数据顺序,但是布局是最紧密的,且当移动任务的位置时都会触发重新布局** diff --git a/docs/assets/option/en/table/gantt.md b/docs/assets/option/en/table/gantt.md index aa11f36c0..c1635cd1e 100644 --- a/docs/assets/option/en/table/gantt.md +++ b/docs/assets/option/en/table/gantt.md @@ -68,6 +68,18 @@ Optional {{ use: common-gantt-task-bar(prefix = '###')}} +## taskShowMode(TaskShowMode) + +Task bar display mode. It is configured using the enumeration type `TaskShowMode`. + +Optional + +- `TaskShowMode.Tasks_Separate`: Each task node is displayed in a separate row, with the parent task occupying one row and child tasks occupying one row each. This is the default display effect! +- `TaskShowMode.Sub_Tasks_Separate`: The parent task node is omitted and not displayed, and all child task nodes are displayed in separate rows. +- `TaskShowMode.Sub_Tasks_Inline`: The parent task node is omitted and not displayed, and all child task nodes are placed in the same row for display. +- `TaskShowMode.Sub_Tasks_Arrange`: The parent task node is omitted and not displayed, and all child tasks will maintain the data order in the records and ensure that the nodes are displayed without overlapping. +- `TaskShowMode.Sub_Tasks_Compact`: The parent task node is omitted and not displayed, and all child tasks will be arranged according to the date attribute and ensure a compact display without overlapping nodes. + ## taskKeyField(string) The field name that uniquely identifies the data entry, default is 'id' diff --git a/docs/assets/option/zh/table/gantt.md b/docs/assets/option/zh/table/gantt.md index 14632b609..7d5c2295f 100644 --- a/docs/assets/option/zh/table/gantt.md +++ b/docs/assets/option/zh/table/gantt.md @@ -68,6 +68,18 @@ {{ use: common-gantt-task-bar(prefix = '###')}} +## taskShowMode(TaskShowMode) + +任务条展示模式。使用枚举类型`TaskShowMode`进行配置 + +非必填 + +- `TaskShowMode.Tasks_Separate`: 每一个任务节点用单独一行来展示,父任务占用一行,子任务分别占用一行。这是默认的显示效果! +- `TaskShowMode.Sub_Tasks_Separate`: 省去父任务节点不展示,且所有子任务的节点分别用一行展示。 +- `TaskShowMode.Sub_Tasks_Inline`: 省去父任务节点不展示,并把所有子任务的节点都放到同一行来展示。 +- `TaskShowMode.Sub_Tasks_Arrange`: 省去父任务节点不展示,且所有子任务会维持 records 中的数据顺序布局,并保证节点不重叠展示。 +- `TaskShowMode.Sub_Tasks_Compact`: 省去父任务节点不展示,且所有子任务会按照日期早晚的属性来布局,并保证节点不重叠的紧凑型展示。 + ## taskKeyField(string) 数据条目可唯一标识的字段名, 默认为'id' diff --git a/packages/vtable-gantt/examples/gantt/gantt-Arrange-customLayout.ts b/packages/vtable-gantt/examples/gantt/gantt-Arrange-customLayout.ts index d79ecab06..449eb13ff 100644 --- a/packages/vtable-gantt/examples/gantt/gantt-Arrange-customLayout.ts +++ b/packages/vtable-gantt/examples/gantt/gantt-Arrange-customLayout.ts @@ -14,7 +14,10 @@ const barColors = ['#1f77b4', '#8c564b', '#ff7f0e', '#e377c2', '#2ca02c', '#bcbd export function createTable() { const records = [ { - title: 'Planning', + id: 0, + name: 'Planning', + start: '2024-11-15', + end: '2024-11-21', children: [ { id: 1, @@ -30,7 +33,6 @@ export function createTable() { end: '2024-11-18', avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' }, - { id: 3, name: 'Rramily', @@ -49,7 +51,7 @@ export function createTable() { }, { id: 300, - title: 'Research', + name: 'Research', children: [ { id: 5, @@ -61,7 +63,7 @@ export function createTable() { ] }, { - title: 'Goal Setting', + name: 'Goal Setting', children: [ { id: 6, @@ -81,7 +83,7 @@ export function createTable() { }, { - title: 'Strategy', + name: 'Strategy', children: [ { id: 8, @@ -121,7 +123,7 @@ export function createTable() { ] }, { - title: 'Execution', + name: 'Execution', children: [ { id: 13, @@ -148,7 +150,7 @@ export function createTable() { ] }, { - title: 'Monitoring', + name: 'Monitoring', children: [ { id: 16, @@ -179,7 +181,7 @@ export function createTable() { ] }, { - title: 'Process review', + name: 'Process review', children: [ { id: 19, @@ -201,19 +203,24 @@ export function createTable() { const columns: ColumnsDefine = [ { - field: 'title', + field: 'name', title: 'PROCESS', - width: 150 + width: 150, + tree: true } ]; const option: GanttConstructorOptions = { records, taskListTable: { columns: columns, - minTableWidth: 100, - hierarchyExpandLevel: 5, - menu: { - contextMenuItems: ['copy', 'paste', 'delete', '...'] + theme: { + bodyStyle: { + bgColor: 'white', + color: 'rgb(115 115 115)' + }, + headerStyle: { + color: 'white' + } } }, groupBy: true, @@ -237,7 +244,7 @@ export function createTable() { } }, headerRowHeight: 60, - rowHeight: 80, + rowHeight: 40, taskBar: { startDateField: 'start', endDateField: 'end', @@ -245,100 +252,20 @@ export function createTable() { labelText: '{name}', labelTextStyle: { fontFamily: 'Arial', - fontSize: 16, - textAlign: 'left' + fontSize: 14, + textAlign: 'center', + color: 'white' }, barStyle: { - width: 50, + width: 22, /** 任务条的颜色 */ - barColor: '#ee8800', + barColor: 'rgb(68 99 244)', /** 已完成部分任务条的颜色 */ completedBarColor: '#91e8e0', /** 任务条的圆角 */ - cornerRadius: 25 - }, - customLayout: (args: any) => { - const colorLength = barColors.length; - const { width, height, index, startDate, endDate, taskDays, progress, taskRecord, ganttInstance } = args; - const container = new Group({ - width, - height, - fill: { - gradient: 'linear', - x0: 0, - y0: 0, - x1: 1, - y1: 0, - stops: [ - { - offset: 0, - color: barColors0[index % colorLength] - }, - { - offset: 0.5, - color: barColors[index % colorLength] - }, - { - offset: 1, - color: barColors0[index % colorLength] - } - ] - }, - display: 'flex', - flexDirection: 'row', - flexWrap: 'nowrap' - }); - const containerLeft = new Group({ - height, - width: 60, - display: 'flex', - flexDirection: 'column', - alignItems: 'center', - justifyContent: 'space-around' - // fill: 'red' - }); - container.add(containerLeft); - - const icon0 = new Image({ - width: 40, - height: 40, - image: taskRecord.avatar, - cornerRadius: 20 - }); - containerLeft.add(icon0); - - const containerRight = new Group({ - height, - width: width - 60, - display: 'flex', - flexDirection: 'column' - // alignItems: 'left' - }); - container.add(containerRight); - - const bloggerName = new Text({ - text: taskRecord.name + ' ' + taskRecord.id, - fontSize: 16, - fontFamily: 'sans-serif', - fill: 'white', - maxLineWidth: width - 60, - boundsPadding: [10, 0, 0, 0] - }); - containerRight.add(bloggerName); - - const days = new Text({ - text: `${taskDays}天`, - fontSize: 13, - fontFamily: 'sans-serif', - fill: 'white', - boundsPadding: [10, 0, 0, 0] - }); - containerRight.add(days); - return { - rootContainer: container - // renderDefaultBar: true - // renderDefaultText: true - }; + cornerRadius: 15, + borderColor: 'black', + borderWidth: 1 } }, dependency: { @@ -396,7 +323,7 @@ export function createTable() { style: { fontSize: 20, fontWeight: 'bold', - color: 'red' + color: 'white' } } ] diff --git a/packages/vtable-gantt/examples/gantt/gantt-taskMerge-customLayout.ts b/packages/vtable-gantt/examples/gantt/gantt-taskMerge-customLayout.ts new file mode 100644 index 000000000..36c6945b2 --- /dev/null +++ b/packages/vtable-gantt/examples/gantt/gantt-taskMerge-customLayout.ts @@ -0,0 +1,448 @@ +import type { ColumnsDefine } from '@visactor/vtable'; +import { register } from '@visactor/vtable'; +import { DateInputEditor, InputEditor } from '@visactor/vtable-editors'; +import type { GanttConstructorOptions, TYPES } from '../../src/index'; +import { Group, Image, Text } from '@visactor/vtable/es/vrender'; +import { Gantt, VTable } from '../../src/index'; +import { bindDebugTool } from '../../../vtable/src/scenegraph/debug-tool'; +import { scale } from '@visactor/vutils'; +import { DependencyType, TasksShowMode } from '../../src/ts-types'; +const date_input_editor = new DateInputEditor({}); +const input_editor = new InputEditor({}); +VTable.register.editor('input', input_editor); +VTable.register.editor('date-input', date_input_editor); +const CONTAINER_ID = 'vTable'; + +const barColors0 = ['#aecde6', '#c6a49a', '#ffb582', '#eec1de', '#b3d9b3', '#d9d1a5', '#cccccc', '#e59a9c', '#c9bede']; +const barColors = ['#1f77b4', '#8c564b', '#ff7f0e', '#e377c2', '#2ca02c', '#bcbd22', '#7f7f7f', '#d62728', '#9467bd']; +export function createTable() { + const records = [ + { + id: 1, + name: 'Michael Smith', + start: '2024-11-15', + end: '2024-11-17', + parentTask: 'Planning', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + }, + { + id: 2, + name: 'Emily', + start: '2024-11-17', + end: '2024-11-18', + parentTask: 'Planning', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + + { + id: 3, + name: 'Rramily', + start: '2024-11-19', + end: '2024-11-20', + parentTask: 'Planning', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 4, + name: 'Lichael Join', + start: '2024-11-18', + end: '2024-11-19', + parentTask: 'Planning', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + }, + + { + id: 5, + name: 'Ryan', + start: '2024-11-18', + end: '2024-11-21', + parentTask: 'Research', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/rabbit.jpg' + }, + { + id: 6, + name: 'Daniel Davis', + start: '2024-11-21', + end: '2024-11-22', + parentTask: 'Goal Setting', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/cat.jpg' + }, + { + id: 7, + name: 'Lauren', + start: '2024-11-18', + end: '2024-11-19', + parentTask: 'Goal Setting', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg' + }, + { + id: 8, + name: 'Tacarah Siller', + start: '2024-11-20', + end: '2024-11-21', + parentTask: 'Strategy', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 9, + name: 'Camentew Olision', + start: '2024-11-25', + end: '2024-11-26', + parentTask: 'Strategy', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + }, + { + id: 10, + name: 'Sarah Miller', + start: '2024-11-17', + end: '2024-11-18', + parentTask: 'Strategy', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 11, + name: 'Matthew Wilson', + start: '2024-11-22', + end: '2024-11-25', + parentTask: 'Strategy', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + }, + { + id: 12, + name: 'Grarah Poliller', + start: '2024-11-23', + end: '2024-11-24', + parentTask: 'Strategy', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 13, + name: 'Ashley Taylor', + start: '2024-11-22', + end: '2024-11-25', + parentTask: 'Execution', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/rabbit.jpg' + }, + { + id: 14, + name: 'Megan', + start: '2024-11-27', + end: '2024-11-30', + parentTask: 'Execution', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/cat.jpg' + }, + { + id: 15, + name: 'David', + start: '2024-12-10', + end: '2024-12-18', + parentTask: 'Execution', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg' + }, + { + id: 16, + name: 'Hannah', + start: '2024-11-20', + end: '2024-11-30', + parentTask: 'Monitoring', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 17, + name: 'Andrew', + start: '2024-12-02', + end: '2024-12-18', + parentTask: 'Monitoring', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + }, + { + id: 18, + name: 'Joshua Anderson', + start: '2024-12-22', + end: '2024-12-28', + parentTask: 'Reporting', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/rabbit.jpg' + }, + { + id: 19, + name: 'Christopher Moore', + start: '2024-11-25', + end: '2024-11-30', + parentTask: 'Process review', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/cat.jpg' + }, + { + id: 20, + name: 'Emma', + start: '2024-12-01', + end: '2024-12-18', + parentTask: 'Process review', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg' + } + ]; + + const columns: ColumnsDefine = [ + { + field: 'parentTask', + title: 'Task', + width: 100, + mergeCell: true, + editor: 'input', + style: { + bgColor: '#EEF1F5', + color: '#141414', + fontWeight: 'bold', + fontSize: 16, + autoWrapText: true + } + }, + { + field: 'name', + title: 'Master', + width: 100, + editor: 'input' + }, + { + field: 'start', + title: 'start', + width: 100, + sort: true, + editor: 'date-input' + }, + { + field: 'end', + title: 'end', + width: 100, + sort: true, + editor: 'date-input' + } + ]; + const option: GanttConstructorOptions = { + records, + taskListTable: { + columns: columns, + minTableWidth: 100, + hierarchyExpandLevel: 5, + menu: { + contextMenuItems: ['copy', 'paste', 'delete', '...'] + }, + theme: { + bodyStyle: { + padding: 5, + bgColor: 'white' + }, + headerStyle: { + color: 'white' + } + } + }, + groupBy: true, + tasksShowMode: TasksShowMode.Tasks_Separate, + frame: { + outerFrameStyle: { + borderLineWidth: 1, + borderColor: '#e1e4e8', + cornerRadius: 8 + }, + verticalSplitLineMoveable: false + }, + grid: { + horizontalLine: { + lineWidth: 1, + lineColor: '#e1e4e8' + }, + verticalLine: { + lineWidth: 1, + lineColor: '#e1e4e8' + } + }, + headerRowHeight: 60, + rowHeight: 60, + taskBar: { + startDateField: 'start', + endDateField: 'end', + progressField: 'progress', + labelText: '{name}', + labelTextStyle: { + fontFamily: 'Arial', + fontSize: 16, + textAlign: 'left' + }, + barStyle: { + width: 50, + /** 任务条的颜色 */ + barColor: '#ee8800', + /** 已完成部分任务条的颜色 */ + completedBarColor: '#91e8e0', + /** 任务条的圆角 */ + cornerRadius: 25 + }, + customLayout: (args: any) => { + const colorLength = barColors.length; + const { width, height, index, startDate, endDate, taskDays, progress, taskRecord, ganttInstance } = args; + const container = new Group({ + width, + height, + fill: { + gradient: 'linear', + x0: 0, + y0: 0, + x1: 1, + y1: 0, + stops: [ + { + offset: 0, + color: barColors0[index % colorLength] + }, + { + offset: 0.5, + color: barColors[index % colorLength] + }, + { + offset: 1, + color: barColors0[index % colorLength] + } + ] + }, + display: 'flex', + flexDirection: 'row', + flexWrap: 'nowrap' + }); + const containerLeft = new Group({ + height, + width: 60, + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'space-around' + // fill: 'red' + }); + container.add(containerLeft); + + const icon0 = new Image({ + width: 40, + height: 40, + image: taskRecord.avatar, + cornerRadius: 20 + }); + containerLeft.add(icon0); + + const containerRight = new Group({ + height, + width: width - 60, + display: 'flex', + flexDirection: 'column' + // alignItems: 'left' + }); + container.add(containerRight); + + const bloggerName = new Text({ + text: taskRecord.name + ' ' + taskRecord.id, + fontSize: 16, + fontFamily: 'sans-serif', + fill: 'white', + maxLineWidth: width - 60, + boundsPadding: [10, 0, 0, 0] + }); + containerRight.add(bloggerName); + + const days = new Text({ + text: `${taskDays}天`, + fontSize: 13, + fontFamily: 'sans-serif', + fill: 'white', + boundsPadding: [10, 0, 0, 0] + }); + containerRight.add(days); + return { + rootContainer: container + // renderDefaultBar: true + // renderDefaultText: true + }; + } + }, + dependency: { + linkCreatable: true, + links: [ + { + type: DependencyType.StartToStart, + linkedFromTaskKey: 1, + linkedToTaskKey: 2 + }, + { + type: DependencyType.FinishToStart, + linkedFromTaskKey: 2, + linkedToTaskKey: 3 + }, + { + type: DependencyType.StartToStart, + linkedFromTaskKey: 3, + linkedToTaskKey: 5 + }, + { + type: DependencyType.FinishToFinish, + linkedFromTaskKey: 5, + linkedToTaskKey: 4 + }, + { + type: DependencyType.StartToStart, + linkedFromTaskKey: 8, + linkedToTaskKey: 9 + }, + { + type: DependencyType.FinishToStart, + linkedFromTaskKey: 9, + linkedToTaskKey: 10 + } + ] + }, + timelineHeader: { + colWidth: 100, + verticalLine: { + lineColor: '#e1e4e8', + lineWidth: 1 + }, + horizontalLine: { + lineColor: '#e1e4e8', + lineWidth: 1 + }, + backgroundColor: '#63a8ff', + scales: [ + { + unit: 'day', + step: 1, + format(date: TYPES.DateFormatArgumentType) { + return date.dateIndex.toString(); + }, + style: { + fontSize: 20, + fontWeight: 'bold', + color: 'white' + } + } + ] + }, + minDate: '2024-11-14', + maxDate: '2024-12-31', + scrollStyle: { + scrollRailColor: 'RGBA(246,246,246,0.5)', + visible: 'none', + width: 6, + scrollSliderCornerRadius: 2, + scrollSliderColor: '#5cb85c' + } + }; + + const ganttInstance = new Gantt(document.getElementById(CONTAINER_ID)!, option); + window.ganttInstance = ganttInstance; + ganttInstance.on('scroll', e => { + console.log('scroll', e); + }); + + ganttInstance.listTableInstance?.on('scroll', e => { + console.log('listTable scroll', e); + }); + // bindDebugTool(ganttInstance.scenegraph.stage as any, { + // customGrapicKeys: ['role', '_updateTag'] + // }); +} diff --git a/packages/vtable-gantt/examples/menu.ts b/packages/vtable-gantt/examples/menu.ts index 21c04ce94..912210bd0 100644 --- a/packages/vtable-gantt/examples/menu.ts +++ b/packages/vtable-gantt/examples/menu.ts @@ -98,6 +98,10 @@ export const menus = [ { path: 'gantt', name: 'gantt-Compact-customLayout' + }, + { + path: 'gantt', + name: 'gantt-taskMerge-customLayout' } // ] // } diff --git a/packages/vtable-gantt/src/Gantt.ts b/packages/vtable-gantt/src/Gantt.ts index 61505dea9..2e537ef21 100644 --- a/packages/vtable-gantt/src/Gantt.ts +++ b/packages/vtable-gantt/src/Gantt.ts @@ -708,7 +708,11 @@ export class Gantt extends EventTarget { _refreshTaskBar(taskShowIndex: number) { // this.taskListTableInstance.updateRecords([record], [index]); this.scenegraph.taskBar.updateTaskBarNode(taskShowIndex); - this.scenegraph.refreshRecordLinkNodes(taskShowIndex, this.scenegraph.taskBar.getTaskBarNodeByIndex(taskShowIndex)); + this.scenegraph.refreshRecordLinkNodes( + taskShowIndex, + undefined, + this.scenegraph.taskBar.getTaskBarNodeByIndex(taskShowIndex) + ); this.scenegraph.updateNextFrame(); } _updateRecordToListTable(record: any, index: number) { diff --git a/packages/vtable-gantt/src/gantt-helper.ts b/packages/vtable-gantt/src/gantt-helper.ts index 880161976..f231b2c8f 100644 --- a/packages/vtable-gantt/src/gantt-helper.ts +++ b/packages/vtable-gantt/src/gantt-helper.ts @@ -173,9 +173,9 @@ export function initOptions(gantt: Gantt) { /** 任务条的圆角 */ cornerRadius: 3, /** 任务条的边框 */ - borderWidth: 1, + borderWidth: 0, /** 边框颜色 */ - borderColor: 'red', + // borderColor: 'red', fontFamily: 'Arial', fontSize: 14 }, diff --git a/packages/vtable-gantt/src/scenegraph/task-bar.ts b/packages/vtable-gantt/src/scenegraph/task-bar.ts index 241284f00..e2368d8bf 100644 --- a/packages/vtable-gantt/src/scenegraph/task-bar.ts +++ b/packages/vtable-gantt/src/scenegraph/task-bar.ts @@ -116,7 +116,7 @@ export class TaskBar { ) : 1; const oneTaskHeigth = this._scene._gantt.getRowHeightByIndex(index) / subTaskShowRowCount; - const barGroup = new GanttTaskBarNode({ + const barGroupBox = new GanttTaskBarNode({ x: this._scene._gantt.parsedOptions.colWidthPerDay * Math.ceil(Math.abs(startDate.getTime() - minDate.getTime()) / (1000 * 60 * 60 * 24)), @@ -127,26 +127,31 @@ export class TaskBar { ? childIndex * oneTaskHeigth : this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange || this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Compact - ? // getSubTaskRowIndexByRecordDate( - // this._scene._gantt.records[index], - // childIndex, - // this._scene._gantt.parsedOptions.startDateField, - // this._scene._gantt.parsedOptions.endDateField - // ) - taskRecord.vtable_gantt_showIndex * oneTaskHeigth + ? taskRecord.vtable_gantt_showIndex * oneTaskHeigth : 0) + (oneTaskHeigth - taskbarHeight) / 2, width: taskBarSize, // height: this._scene._gantt.parsedOptions.rowHeight, height: taskbarHeight, cornerRadius: this._scene._gantt.parsedOptions.taskBarStyle.cornerRadius, + lineWidth: this._scene._gantt.parsedOptions.taskBarStyle.borderWidth * 2, + stroke: this._scene._gantt.parsedOptions.taskBarStyle.borderColor + // clip: true + }); + barGroupBox.name = 'task-bar'; + barGroupBox.task_index = index; + barGroupBox.sub_task_index = childIndex; + barGroupBox.record = taskRecord; + + const barGroup = new Group({ + x: 0, + y: 0, + width: taskBarSize, + height: taskbarHeight, + cornerRadius: this._scene._gantt.parsedOptions.taskBarStyle.cornerRadius, clip: true }); - barGroup.name = 'task-bar'; - barGroup.task_index = index; - barGroup.sub_task_index = childIndex; - barGroup.record = taskRecord; - // this.barContainer.appendChild(barGroup); + barGroupBox.appendChild(barGroup); let rootContainer; let renderDefaultBar = true; let renderDefaultText = true; @@ -243,7 +248,7 @@ export class TaskBar { barGroup.appendChild(label); barGroup.textLabel = label; } - return barGroup; + return barGroupBox; } updateTaskBarNode(index: number) { const taskbarGroup = this.getTaskBarNodeByIndex(index); diff --git a/packages/vtable-gantt/src/state/state-manager.ts b/packages/vtable-gantt/src/state/state-manager.ts index ed657f75d..5e49ce267 100644 --- a/packages/vtable-gantt/src/state/state-manager.ts +++ b/packages/vtable-gantt/src/state/state-manager.ts @@ -383,7 +383,10 @@ export class StateManager { this._gantt.scenegraph.refreshTaskBarsAndGrid(); } else { // 判断纵向拖动 处理数据的位置 - if (Math.abs(Math.round(deltaY / this._gantt.parsedOptions.rowHeight)) >= 1) { + if ( + this._gantt.parsedOptions.tasksShowMode !== TasksShowMode.Tasks_Separate && + Math.abs(Math.round(deltaY / this._gantt.parsedOptions.rowHeight)) >= 1 + ) { const indexs = getTaskIndexsByTaskY(targetEndY, this._gantt); this._gantt._dragOrderTaskRecord( target.task_index, @@ -931,8 +934,12 @@ function resizeOrMoveTaskBar(target: GanttTaskBarNode, dx: number, dy: number, n if (dx) { target.setAttribute('x', target.attribute.x + dx); } - if (dy) { - target.setAttribute('y', target.attribute.y + dy); + if (state._gantt.parsedOptions.tasksShowMode !== TasksShowMode.Tasks_Separate) { + if (dy) { + target.setAttribute('y', target.attribute.y + dy); + } + } else { + dy = 0; } if (newWidth) { target.setAttribute('width', newWidth); diff --git a/packages/vtable-gantt/src/ts-types/gantt-engine.ts b/packages/vtable-gantt/src/ts-types/gantt-engine.ts index 60881425a..58713cad7 100644 --- a/packages/vtable-gantt/src/ts-types/gantt-engine.ts +++ b/packages/vtable-gantt/src/ts-types/gantt-engine.ts @@ -225,10 +225,10 @@ export interface ITaskBarStyle { width?: number; /** 任务条的圆角 */ cornerRadius?: number; - // /** 任务条的边框 */ - // borderWidth?: number; - // /** 边框颜色 */ - // borderColor?: string; + /** 任务条的边框 */ + borderWidth?: number; + /** 边框颜色 */ + borderColor?: string; } export type ILineStyle = { lineColor?: string; From 305e0250e819f46ea59fd99ad1f9c3205da5f183 Mon Sep 17 00:00:00 2001 From: LIANG OU Date: Tue, 3 Dec 2024 11:15:34 +0800 Subject: [PATCH 26/40] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E7=94=98?= =?UTF-8?q?=E7=89=B9=E5=9B=BE=E6=89=A7=E8=A1=8CupdateScales=E6=97=B6?= =?UTF-8?q?=EF=BC=8Cgrid=E5=AE=BD=E5=BA=A6=E8=AE=A1=E7=AE=97=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/vtable-gantt/src/scenegraph/grid.ts | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/packages/vtable-gantt/src/scenegraph/grid.ts b/packages/vtable-gantt/src/scenegraph/grid.ts index 1d1635599..a32eebfe9 100644 --- a/packages/vtable-gantt/src/scenegraph/grid.ts +++ b/packages/vtable-gantt/src/scenegraph/grid.ts @@ -13,8 +13,6 @@ export class Grid { y: number; width: number; height: number; - timelineDates: any; - colWidthPerDay: number; rowHeight: number; rowCount: number; group: Group; @@ -34,8 +32,6 @@ export class Grid { this.y = scene._gantt.getAllHeaderRowsHeight(); this.width = scene.tableGroup.attribute.width; this.height = scene.tableGroup.attribute.height - scene.timelineHeader.group.attribute.height; - this.timelineDates = scene._gantt.parsedOptions.reverseSortedTimelineScales[0].timelineDates; - this.colWidthPerDay = scene._gantt.parsedOptions.colWidthPerDay; this.rowHeight = scene._gantt.parsedOptions.rowHeight; this.rowCount = scene._gantt.itemCount; this.allGridWidth = scene._gantt._getAllColsWidth(); @@ -93,9 +89,11 @@ export class Grid { if (this.gridStyle?.verticalLine.lineWidth & 1) { x = 0.5; } - for (let i = 0; i < this.timelineDates?.length - 1; i++) { - const dateline = this.timelineDates[i]; - x = x + Math.floor(this.colWidthPerDay * dateline.days); + const timelineDates = this._scene._gantt.parsedOptions.reverseSortedTimelineScales[0].timelineDates; + const colWidthPerDay = this._scene._gantt.parsedOptions.colWidthPerDay; + for (let i = 0; i < timelineDates?.length - 1; i++) { + const dateline = timelineDates[i]; + x = x + Math.floor(colWidthPerDay * dateline.days); const line = createLine({ pickable: false, stroke: this.gridStyle?.verticalLine.lineColor, From 5ef3209e88e2fc256a098523d25465da53e870db Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Tue, 3 Dec 2024 11:25:27 +0800 Subject: [PATCH 27/40] chore: update dependencies --- docs/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/package.json b/docs/package.json index c003e9e41..616ce4e6d 100644 --- a/docs/package.json +++ b/docs/package.json @@ -13,6 +13,7 @@ "@arco-design/web-react": "2.60.2", "@arco-design/web-vue": "^2.11.0", "@visactor/vtable": "workspace:*", + "@visactor/vtable-gantt": "workspace:*", "@visactor/react-vtable": "workspace:*", "@visactor/vue-vtable": "workspace:*", "@visactor/openinula-vtable": "workspace:*", From 00f18f5b9fde60c2e569eaa4ee1b6186a657d71b Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Tue, 3 Dec 2024 11:31:41 +0800 Subject: [PATCH 28/40] fix: when record no dependency link not exist occor error --- packages/vtable-gantt/src/gantt-helper.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vtable-gantt/src/gantt-helper.ts b/packages/vtable-gantt/src/gantt-helper.ts index f231b2c8f..5af878165 100644 --- a/packages/vtable-gantt/src/gantt-helper.ts +++ b/packages/vtable-gantt/src/gantt-helper.ts @@ -680,7 +680,7 @@ export function getTaskIndexsByTaskY(y: number, gantt: Gantt) { export function computeRowsCountByRecordDateForCompact(record: any, startDateField: string, endDateField: string) { if (!record.children || record.children.length === 1) { - if (record.children.length === 1) { + if (record.children?.length === 1) { record.children[0].vtable_gantt_showIndex = 0; } return 1; From 9389b61a5d70880278277269b89308594b24b73e Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Tue, 3 Dec 2024 11:51:54 +0800 Subject: [PATCH 29/40] chore: update dependencies --- common/config/rush/pnpm-lock.yaml | 410 +++++++++++++++++------------- 1 file changed, 232 insertions(+), 178 deletions(-) diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index 645af30ce..af1231ec6 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -22,6 +22,7 @@ importers: '@visactor/vtable': workspace:* '@visactor/vtable-editors': workspace:* '@visactor/vtable-export': workspace:* + '@visactor/vtable-gantt': workspace:* '@visactor/vtable-search': workspace:* '@visactor/vue-vtable': workspace:* '@visactor/vutils': ~0.18.14 @@ -55,6 +56,7 @@ importers: '@visactor/vtable': link:../packages/vtable '@visactor/vtable-editors': link:../packages/vtable-editors '@visactor/vtable-export': link:../packages/vtable-export + '@visactor/vtable-gantt': link:../packages/vtable-gantt '@visactor/vtable-search': link:../packages/vtable-search '@visactor/vue-vtable': link:../packages/vue-vtable '@visactor/vutils': 0.18.18 @@ -143,7 +145,7 @@ importers: '@types/chai': 4.2.22 '@types/jest': 26.0.24 '@types/mocha': 9.0.0 - '@types/node': 22.10.0 + '@types/node': 22.10.1 '@types/offscreencanvas': 2019.6.4 '@visactor/vchart': 1.12.12 axios: 1.7.8 @@ -165,12 +167,12 @@ importers: sass: 1.43.5 ts-jest: 26.5.6_xuote2qreek47x2di7kesslrai ts-loader: 9.2.6_typescript@4.9.5 - ts-node: 10.9.0_gvyh6auciohtph3635nyjnikui + ts-node: 10.9.0_c27w6cdkixrdosqhzsswatltde tslib: 2.3.1 ttypescript: 1.5.13_fxi2xlggroal5l3a4znftvxz2m typescript: 4.9.5 typescript-transform-paths: 3.3.1_typescript@4.9.5 - vite: 3.2.6_3fcrixghlk5j7efp7olgstdjb4 + vite: 3.2.6_girnrryua4ny3ya6dfs4crajjq vite-plugin-markdown: 2.2.0_vite@3.2.6 ../../packages/react-vtable: @@ -240,7 +242,7 @@ importers: '@types/chai': 4.2.22 '@types/jest': 26.0.24 '@types/mocha': 9.0.0 - '@types/node': 22.10.0 + '@types/node': 22.10.1 '@types/offscreencanvas': 2019.6.4 '@types/react': 18.3.12 '@types/react-dom': 18.3.1 @@ -268,12 +270,12 @@ importers: sass: 1.43.5 ts-jest: 26.5.6_xuote2qreek47x2di7kesslrai ts-loader: 9.2.6_typescript@4.9.5 - ts-node: 10.9.0_gvyh6auciohtph3635nyjnikui + ts-node: 10.9.0_c27w6cdkixrdosqhzsswatltde tslib: 2.3.1 ttypescript: 1.5.13_fxi2xlggroal5l3a4znftvxz2m typescript: 4.9.5 typescript-transform-paths: 3.3.1_typescript@4.9.5 - vite: 3.2.6_3fcrixghlk5j7efp7olgstdjb4 + vite: 3.2.6_girnrryua4ny3ya6dfs4crajjq vite-plugin-markdown: 2.2.0_vite@3.2.6 ../../packages/vtable: @@ -360,7 +362,7 @@ importers: '@types/chai': 4.2.22 '@types/jest': 26.0.24 '@types/mocha': 9.0.0 - '@types/node': 22.10.0 + '@types/node': 22.10.1 '@types/offscreencanvas': 2019.6.4 '@types/react': 18.3.12 '@types/react-dom': 18.3.1 @@ -393,12 +395,12 @@ importers: sass: 1.43.5 ts-jest: 26.5.6_xuote2qreek47x2di7kesslrai ts-loader: 9.2.6_typescript@4.9.5 - ts-node: 10.9.0_gvyh6auciohtph3635nyjnikui + ts-node: 10.9.0_c27w6cdkixrdosqhzsswatltde tslib: 2.3.1 ttypescript: 1.5.13_fxi2xlggroal5l3a4znftvxz2m typescript: 4.9.5 typescript-transform-paths: 3.3.1_typescript@4.9.5 - vite: 3.2.6_3fcrixghlk5j7efp7olgstdjb4 + vite: 3.2.6_girnrryua4ny3ya6dfs4crajjq vite-plugin-markdown: 2.2.0_vite@3.2.6 ../../packages/vtable-calendar: @@ -463,7 +465,7 @@ importers: '@types/chai': 4.2.22 '@types/jest': 26.0.24 '@types/mocha': 9.0.0 - '@types/node': 22.10.0 + '@types/node': 22.10.1 '@types/offscreencanvas': 2019.6.4 '@types/react': 18.3.12 '@types/react-dom': 18.3.1 @@ -490,12 +492,12 @@ importers: sass: 1.43.5 ts-jest: 26.5.6_xuote2qreek47x2di7kesslrai ts-loader: 9.2.6_typescript@4.9.5 - ts-node: 10.9.0_gvyh6auciohtph3635nyjnikui + ts-node: 10.9.0_c27w6cdkixrdosqhzsswatltde tslib: 2.3.1 ttypescript: 1.5.13_fxi2xlggroal5l3a4znftvxz2m typescript: 4.9.5 typescript-transform-paths: 3.3.1_typescript@4.9.5 - vite: 3.2.6_3fcrixghlk5j7efp7olgstdjb4 + vite: 3.2.6_girnrryua4ny3ya6dfs4crajjq vite-plugin-markdown: 2.2.0_vite@3.2.6 ../../packages/vtable-editors: @@ -527,7 +529,7 @@ importers: '@internal/ts-config': link:../../share/ts-config '@rushstack/eslint-patch': 1.1.4 '@types/jest': 26.0.24 - '@types/node': 22.10.0 + '@types/node': 22.10.1 '@types/offscreencanvas': 2019.6.4 eslint: 8.18.0 husky: 7.0.4 @@ -538,11 +540,11 @@ importers: react-device-detect: 2.2.3 ts-jest: 26.5.6_xuote2qreek47x2di7kesslrai ts-loader: 9.2.6_typescript@4.9.5 - ts-node: 10.9.0_gvyh6auciohtph3635nyjnikui + ts-node: 10.9.0_c27w6cdkixrdosqhzsswatltde tslib: 2.3.1 tslint: 5.12.1_typescript@4.9.5 typescript: 4.9.5 - vite: 3.2.6_@types+node@22.10.0 + vite: 3.2.6_@types+node@22.10.1 ../../packages/vtable-export: specifiers: @@ -611,7 +613,7 @@ importers: '@types/chai': 4.2.22 '@types/jest': 26.0.24 '@types/mocha': 9.0.0 - '@types/node': 22.10.0 + '@types/node': 22.10.1 '@types/offscreencanvas': 2019.6.4 '@types/react': 18.3.12 '@types/react-dom': 18.3.1 @@ -639,12 +641,12 @@ importers: sass: 1.43.5 ts-jest: 26.5.6_xuote2qreek47x2di7kesslrai ts-loader: 9.2.6_typescript@4.9.5 - ts-node: 10.9.0_gvyh6auciohtph3635nyjnikui + ts-node: 10.9.0_c27w6cdkixrdosqhzsswatltde tslib: 2.3.1 ttypescript: 1.5.13_fxi2xlggroal5l3a4znftvxz2m typescript: 4.9.5 typescript-transform-paths: 3.3.1_typescript@4.9.5 - vite: 3.2.6_3fcrixghlk5j7efp7olgstdjb4 + vite: 3.2.6_girnrryua4ny3ya6dfs4crajjq vite-plugin-markdown: 2.2.0_vite@3.2.6 ../../packages/vtable-gantt: @@ -722,7 +724,7 @@ importers: '@types/chai': 4.2.22 '@types/jest': 26.0.24 '@types/mocha': 9.0.0 - '@types/node': 22.10.0 + '@types/node': 22.10.1 '@types/offscreencanvas': 2019.6.4 '@types/react': 18.3.12 '@types/react-dom': 18.3.1 @@ -754,12 +756,12 @@ importers: sass: 1.43.5 ts-jest: 26.5.6_xuote2qreek47x2di7kesslrai ts-loader: 9.2.6_typescript@4.9.5 - ts-node: 10.9.0_gvyh6auciohtph3635nyjnikui + ts-node: 10.9.0_c27w6cdkixrdosqhzsswatltde tslib: 2.3.1 ttypescript: 1.5.13_fxi2xlggroal5l3a4znftvxz2m typescript: 4.9.5 typescript-transform-paths: 3.3.1_typescript@4.9.5 - vite: 3.2.6_3fcrixghlk5j7efp7olgstdjb4 + vite: 3.2.6_girnrryua4ny3ya6dfs4crajjq vite-plugin-markdown: 2.2.0_vite@3.2.6 ../../packages/vtable-plugins: @@ -822,7 +824,7 @@ importers: '@types/chai': 4.2.22 '@types/jest': 26.0.24 '@types/mocha': 9.0.0 - '@types/node': 22.10.0 + '@types/node': 22.10.1 '@types/offscreencanvas': 2019.6.4 '@types/react': 18.3.12 '@types/react-dom': 18.3.1 @@ -851,12 +853,12 @@ importers: sass: 1.43.5 ts-jest: 26.5.6_xuote2qreek47x2di7kesslrai ts-loader: 9.2.6_typescript@4.9.5 - ts-node: 10.9.0_gvyh6auciohtph3635nyjnikui + ts-node: 10.9.0_c27w6cdkixrdosqhzsswatltde tslib: 2.3.1 ttypescript: 1.5.13_fxi2xlggroal5l3a4znftvxz2m typescript: 4.9.5 typescript-transform-paths: 3.3.1_typescript@4.9.5 - vite: 3.2.6_3fcrixghlk5j7efp7olgstdjb4 + vite: 3.2.6_girnrryua4ny3ya6dfs4crajjq vite-plugin-markdown: 2.2.0_vite@3.2.6 ../../packages/vtable-search: @@ -920,7 +922,7 @@ importers: '@types/chai': 4.2.22 '@types/jest': 26.0.24 '@types/mocha': 9.0.0 - '@types/node': 22.10.0 + '@types/node': 22.10.1 '@types/offscreencanvas': 2019.6.4 '@types/react': 18.3.12 '@types/react-dom': 18.3.1 @@ -948,12 +950,12 @@ importers: sass: 1.43.5 ts-jest: 26.5.6_xuote2qreek47x2di7kesslrai ts-loader: 9.2.6_typescript@4.9.5 - ts-node: 10.9.0_gvyh6auciohtph3635nyjnikui + ts-node: 10.9.0_c27w6cdkixrdosqhzsswatltde tslib: 2.3.1 ttypescript: 1.5.13_fxi2xlggroal5l3a4znftvxz2m typescript: 4.9.5 typescript-transform-paths: 3.3.1_typescript@4.9.5 - vite: 3.2.6_3fcrixghlk5j7efp7olgstdjb4 + vite: 3.2.6_girnrryua4ny3ya6dfs4crajjq vite-plugin-markdown: 2.2.0_vite@3.2.6 ../../packages/vue-vtable: @@ -1017,7 +1019,7 @@ importers: '@types/chai': 4.2.22 '@types/jest': 26.0.24 '@types/mocha': 9.0.0 - '@types/node': 22.10.0 + '@types/node': 22.10.1 '@types/offscreencanvas': 2019.6.4 '@visactor/vchart': 1.12.12 '@vitejs/plugin-vue': 5.2.1_vite@3.2.6+vue@3.5.13 @@ -1025,7 +1027,7 @@ importers: chai: 4.3.4 cross-env: 7.0.3 eslint: 8.18.0 - eslint-plugin-vue: 9.31.0_eslint@8.18.0 + eslint-plugin-vue: 9.32.0_eslint@8.18.0 form-data: 4.0.1 inversify: 6.0.1 jest: 26.6.3_ts-node@10.9.0 @@ -1041,12 +1043,12 @@ importers: sass: 1.43.5 ts-jest: 26.5.6_xuote2qreek47x2di7kesslrai ts-loader: 9.2.6_typescript@4.9.5 - ts-node: 10.9.0_gvyh6auciohtph3635nyjnikui + ts-node: 10.9.0_c27w6cdkixrdosqhzsswatltde tslib: 2.3.1 ttypescript: 1.5.13_fxi2xlggroal5l3a4znftvxz2m typescript: 4.9.5 typescript-transform-paths: 3.3.1_typescript@4.9.5 - vite: 3.2.6_3fcrixghlk5j7efp7olgstdjb4 + vite: 3.2.6_girnrryua4ny3ya6dfs4crajjq vite-plugin-markdown: 2.2.0_vite@3.2.6 vue: 3.5.13_typescript@4.9.5 vue-eslint-parser: 9.4.3_eslint@8.18.0 @@ -1072,7 +1074,7 @@ importers: eslint-plugin-promise: 6.0.0_eslint@8.18.0 eslint-plugin-react: 7.30.1_eslint@8.18.0 eslint-plugin-react-hooks: 4.6.0_eslint@8.18.0 - eslint-plugin-vue: 9.31.0_eslint@8.18.0 + eslint-plugin-vue: 9.32.0_eslint@8.18.0 prettier: 2.8.8 devDependencies: eslint: 8.18.0 @@ -1107,13 +1109,13 @@ importers: '@internal/eslint-config': link:../../share/eslint-config '@internal/ts-config': link:../../share/ts-config '@rushstack/eslint-patch': 1.1.4 - '@types/node': 22.10.0 + '@types/node': 22.10.1 '@types/node-fetch': 2.6.4 cross-env: 7.0.3 eslint: 8.18.0 form-data: 4.0.1 node-fetch: 2.6.7 - ts-node: 10.9.0_gvyh6auciohtph3635nyjnikui + ts-node: 10.9.0_c27w6cdkixrdosqhzsswatltde typescript: 4.9.5 ../../tools/bundler: @@ -1262,7 +1264,7 @@ importers: '@types/merge2': 1.4.0 '@types/minimist': 1.2.2 '@types/ms': 0.7.31 - '@types/node': 22.10.0 + '@types/node': 22.10.1 '@types/semver': 7.3.12 '@types/terser': 3.12.0 '@types/through2': 2.0.38 @@ -1271,7 +1273,7 @@ importers: '@types/yargs-parser': 21.0.0 eslint: 8.18.0 rimraf: 3.0.2 - ts-node: 10.9.0_gvyh6auciohtph3635nyjnikui + ts-node: 10.9.0_c27w6cdkixrdosqhzsswatltde typescript: 4.9.5 vitest: 0.30.1_less@4.1.3+terser@5.17.1 @@ -2757,7 +2759,7 @@ packages: engines: {node: '>= 10.14.2'} dependencies: '@jest/types': 26.6.2 - '@types/node': 22.10.0 + '@types/node': 22.10.1 chalk: 4.1.2 jest-message-util: 26.6.2 jest-util: 26.6.2 @@ -2773,7 +2775,7 @@ packages: '@jest/test-result': 26.6.2 '@jest/transform': 26.6.2 '@jest/types': 26.6.2 - '@types/node': 22.10.0 + '@types/node': 22.10.1 ansi-escapes: 4.3.2 chalk: 4.1.2 exit: 0.1.2 @@ -2786,7 +2788,7 @@ packages: jest-resolve: 26.6.2 jest-resolve-dependencies: 26.6.3 jest-runner: 26.6.3_ts-node@10.9.0 - jest-runtime: 26.6.3_ts-node@10.9.0 + jest-runtime: 26.6.3 jest-snapshot: 26.6.2 jest-util: 26.6.2 jest-validate: 26.6.2 @@ -2822,7 +2824,7 @@ packages: dependencies: '@jest/fake-timers': 26.6.2 '@jest/types': 26.6.2 - '@types/node': 22.10.0 + '@types/node': 22.10.1 jest-mock: 26.6.2 dev: true @@ -2841,7 +2843,7 @@ packages: dependencies: '@jest/types': 26.6.2 '@sinonjs/fake-timers': 6.0.1 - '@types/node': 22.10.0 + '@types/node': 22.10.1 jest-message-util: 26.6.2 jest-mock: 26.6.2 jest-util: 26.6.2 @@ -2947,7 +2949,7 @@ packages: graceful-fs: 4.2.11 jest-haste-map: 26.6.2 jest-runner: 26.6.3_ts-node@10.9.0 - jest-runtime: 26.6.3_ts-node@10.9.0 + jest-runtime: 26.6.3 transitivePeerDependencies: - bufferutil - canvas @@ -3018,7 +3020,7 @@ packages: dependencies: '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 22.10.0 + '@types/node': 22.10.1 '@types/yargs': 15.0.19 chalk: 4.1.2 dev: true @@ -3663,7 +3665,7 @@ packages: /@types/clean-css/4.2.6: resolution: {integrity: sha512-Ze1tf+LnGPmG6hBFMi0B4TEB0mhF7EiMM5oyjLDNPE9hxrPU0W+5+bHvO+eFPA+bt0iC1zkQMoU/iGdRVjcRbw==} dependencies: - '@types/node': 22.10.0 + '@types/node': 22.10.1 source-map: 0.6.1 dev: true @@ -3691,13 +3693,13 @@ packages: /@types/fs-extra/9.0.13: resolution: {integrity: sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==} dependencies: - '@types/node': 22.10.0 + '@types/node': 22.10.1 dev: true /@types/glob-stream/8.0.2: resolution: {integrity: sha512-kyuRfGE+yiSJWzSO3t74rXxdZNdYfLcllO0IUha4eX1fl40pm9L02Q/TEc3mykTLjoWz4STBNwYnUWdFu3I0DA==} dependencies: - '@types/node': 22.10.0 + '@types/node': 22.10.1 '@types/picomatch': 3.0.1 '@types/streamx': 2.9.5 dev: true @@ -3705,19 +3707,19 @@ packages: /@types/glob-watcher/5.0.2: resolution: {integrity: sha512-MZeh2nIzibl/euv5UV0femkGzcKTSE4G2+zv48d6ymeitWwCx52+4X+FqzML9oH2mQnPs+N/JHp3CsBPj1x1Ug==} dependencies: - '@types/node': 22.10.0 + '@types/node': 22.10.1 dev: true /@types/graceful-fs/4.1.9: resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} dependencies: - '@types/node': 22.10.0 + '@types/node': 22.10.1 dev: true /@types/gulp-if/0.0.34: resolution: {integrity: sha512-r2A04hHDC+ZWMRAm+3q6/UeC3ggvl+TZm9P1+2umnp4q9bOlBmUQnR178Io3c0DkZPQAwup8VNtOvmvaWCpP5w==} dependencies: - '@types/node': 22.10.0 + '@types/node': 22.10.1 '@types/vinyl': 2.0.7 dev: true @@ -3731,7 +3733,7 @@ packages: /@types/gulp-sourcemaps/0.0.35: resolution: {integrity: sha512-vUBuizwA4CAV3Mke0DJYHQxyN4YOB1aAql284qAO7Et7fe0hmnPi/R9Fhu2UhxMuSxAwFktsJUOQk5dJHOU1eA==} dependencies: - '@types/node': 22.10.0 + '@types/node': 22.10.1 '@types/vinyl': 2.0.7 dev: true @@ -3807,7 +3809,7 @@ packages: /@types/merge2/1.4.0: resolution: {integrity: sha512-MRHDvln2ldZELrUC8n1PGaQzZ33aNh8uDcsGehREW0zR1Fr818a4/JTZjO9eloHPPxnpUp8fz/YFTRc5CWm7Xw==} dependencies: - '@types/node': 22.10.0 + '@types/node': 22.10.1 dev: true /@types/minimatch/5.1.2: @@ -3829,7 +3831,7 @@ packages: /@types/node-fetch/2.6.4: resolution: {integrity: sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg==} dependencies: - '@types/node': 22.10.0 + '@types/node': 22.10.1 form-data: 3.0.2 dev: true @@ -3841,8 +3843,8 @@ packages: resolution: {integrity: sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==} dev: false - /@types/node/22.10.0: - resolution: {integrity: sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==} + /@types/node/22.10.1: + resolution: {integrity: sha512-qKgsUwfHZV2WCWLAnVP1JqnpE6Im6h3Y0+fYgMTasNQ7V++CBX5OT1as0g0f+OyubbFqhf6XVNIsmN4IIhEgGQ==} dependencies: undici-types: 6.20.0 @@ -3900,7 +3902,7 @@ packages: /@types/resolve/0.0.8: resolution: {integrity: sha512-auApPaJf3NPfe18hSoJkp8EbZzer2ISk7o8mCC3M9he/a04+gbMF97NkpD2S8riMGvm4BMRI59/SZQSaLTKpsQ==} dependencies: - '@types/node': 22.10.0 + '@types/node': 22.10.1 dev: true /@types/resolve/1.20.2: @@ -3926,7 +3928,7 @@ packages: /@types/streamx/2.9.5: resolution: {integrity: sha512-IHYsa6jYrck8VEdSwpY141FTTf6D7boPeMq9jy4qazNrFMA4VbRz/sw5LSsfR7jwdDcx0QKWkUexZvsWBC2eIQ==} dependencies: - '@types/node': 22.10.0 + '@types/node': 22.10.1 dev: true /@types/terser/3.12.0: @@ -3939,7 +3941,7 @@ packages: /@types/through2/2.0.38: resolution: {integrity: sha512-YFu+nHmjxMurkH1BSzA0Z1WrKDAY8jUKPZctNQn7mc+/KKtp2XxnclHFXxdB1m7Iqnzb5aywgP8TMK283LezGQ==} dependencies: - '@types/node': 22.10.0 + '@types/node': 22.10.1 dev: true /@types/undertaker-registry/1.0.4: @@ -3949,7 +3951,7 @@ packages: /@types/undertaker/1.2.8: resolution: {integrity: sha512-gW3PRqCHYpo45XFQHJBhch7L6hytPsIe0QeLujlnFsjHPnXLhJcPdN6a9368d7aIQgH2I/dUTPFBlGeSNA3qOg==} dependencies: - '@types/node': 22.10.0 + '@types/node': 22.10.1 '@types/undertaker-registry': 1.0.4 async-done: 1.3.2 dev: true @@ -3958,7 +3960,7 @@ packages: resolution: {integrity: sha512-ckYz9giHgV6U10RFuf9WsDQ3X86EFougapxHmmoxLK7e6ICQqO8CE+4V/3lBN148V5N1pb4nQMmMjyScleVsig==} dependencies: '@types/glob-stream': 8.0.2 - '@types/node': 22.10.0 + '@types/node': 22.10.1 '@types/vinyl': 2.0.7 dev: true @@ -3966,7 +3968,7 @@ packages: resolution: {integrity: sha512-4UqPv+2567NhMQuMLdKAyK4yzrfCqwaTt6bLhHEs8PFcxbHILsrxaY63n4wgE/BRLDWDQeI+WcTmkXKExh9hQg==} dependencies: '@types/expect': 1.20.4 - '@types/node': 22.10.0 + '@types/node': 22.10.1 /@types/yargs-parser/21.0.0: resolution: {integrity: sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==} @@ -4336,7 +4338,7 @@ packages: vite: ^5.0.0 || ^6.0.0 vue: ^3.2.25 dependencies: - vite: 3.2.6_3fcrixghlk5j7efp7olgstdjb4 + vite: 3.2.6_girnrryua4ny3ya6dfs4crajjq vue: 3.5.13_typescript@4.9.5 dev: true @@ -4794,7 +4796,7 @@ packages: es-abstract: 1.23.5 es-object-atoms: 1.0.0 get-intrinsic: 1.2.4 - is-string: 1.0.7 + is-string: 1.1.0 dev: false /array-initial/1.1.0: @@ -4872,7 +4874,7 @@ packages: es-array-method-boxes-properly: 1.0.0 es-errors: 1.3.0 es-object-atoms: 1.0.0 - is-string: 1.0.7 + is-string: 1.1.0 dev: true /arraybuffer.prototype.slice/1.0.3: @@ -4956,7 +4958,7 @@ packages: postcss: ^8.1.0 dependencies: browserslist: 4.24.2 - caniuse-lite: 1.0.30001684 + caniuse-lite: 1.0.30001685 fraction.js: 4.3.7 normalize-range: 0.1.2 picocolors: 1.1.1 @@ -5316,8 +5318,8 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001684 - electron-to-chromium: 1.5.65 + caniuse-lite: 1.0.30001685 + electron-to-chromium: 1.5.68 node-releases: 2.0.18 update-browserslist-db: 1.1.1_browserslist@4.24.2 @@ -5449,13 +5451,13 @@ packages: resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} dependencies: browserslist: 4.24.2 - caniuse-lite: 1.0.30001684 + caniuse-lite: 1.0.30001685 lodash.memoize: 4.1.2 lodash.uniq: 4.5.0 dev: false - /caniuse-lite/1.0.30001684: - resolution: {integrity: sha512-G1LRwLIQjBQoyq0ZJGqGIJUXzJ8irpbjHLpVRXDvBEScFJ9b17sgK6vlx0GAJFE21okD7zXl08rRRUfq6HdoEQ==} + /caniuse-lite/1.0.30001685: + resolution: {integrity: sha512-e/kJN1EMyHQzgcMEEgoo+YTCO1NGCmIYHk5Qk8jT6AazWemS5QFKJ5ShCJlH3GZrNIdZofcNCEwZqbMjjKzmnA==} /capture-exit/2.0.0: resolution: {integrity: sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==} @@ -6340,7 +6342,7 @@ packages: dependencies: es-define-property: 1.0.0 es-errors: 1.3.0 - gopd: 1.0.1 + gopd: 1.1.0 /define-properties/1.2.1: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} @@ -6535,8 +6537,8 @@ packages: safer-buffer: 2.1.2 dev: true - /electron-to-chromium/1.5.65: - resolution: {integrity: sha512-PWVzBjghx7/wop6n22vS2MLU8tKGd4Q91aCEGhG/TYmW6PP5OcSXcdnxTe1NNt0T66N8D6jxh4kC8UsdzOGaIw==} + /electron-to-chromium/1.5.68: + resolution: {integrity: sha512-FgMdJlma0OzUYlbrtZ4AeXjKxKPk6KT8WOP8BjcqxWtlg8qyJQjRzPJzUtUn5GBg1oQ26hFs7HOOHJMYiJRnvQ==} /electron/11.5.0: resolution: {integrity: sha512-WjNDd6lGpxyiNjE3LhnFCAk/D9GIj1rU3GSDealVShhkkkPR3Vh4q8ErXGDl1OAO/faomVa10KoFPUN/pLbNxg==} @@ -6635,24 +6637,24 @@ packages: es-errors: 1.3.0 es-object-atoms: 1.0.0 es-set-tostringtag: 2.0.3 - es-to-primitive: 1.2.1 + es-to-primitive: 1.3.0 function.prototype.name: 1.1.6 get-intrinsic: 1.2.4 get-symbol-description: 1.0.2 globalthis: 1.0.4 - gopd: 1.0.1 + gopd: 1.1.0 has-property-descriptors: 1.0.2 - has-proto: 1.0.3 - has-symbols: 1.0.3 + has-proto: 1.1.0 + has-symbols: 1.1.0 hasown: 2.0.2 internal-slot: 1.0.7 is-array-buffer: 3.0.4 is-callable: 1.2.7 is-data-view: 1.0.1 is-negative-zero: 2.0.3 - is-regex: 1.1.4 + is-regex: 1.2.0 is-shared-array-buffer: 1.0.3 - is-string: 1.0.7 + is-string: 1.1.0 is-typed-array: 1.1.13 is-weakref: 1.0.2 object-inspect: 1.13.3 @@ -6669,7 +6671,7 @@ packages: typed-array-byte-offset: 1.0.3 typed-array-length: 1.0.7 unbox-primitive: 1.0.2 - which-typed-array: 1.1.15 + which-typed-array: 1.1.16 /es-array-method-boxes-properly/1.0.0: resolution: {integrity: sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==} @@ -6705,13 +6707,13 @@ packages: hasown: 2.0.2 dev: false - /es-to-primitive/1.2.1: - resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} + /es-to-primitive/1.3.0: + resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} engines: {node: '>= 0.4'} dependencies: is-callable: 1.2.7 is-date-object: 1.0.5 - is-symbol: 1.0.4 + is-symbol: 1.1.0 /es5-ext/0.10.64: resolution: {integrity: sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==} @@ -7073,8 +7075,8 @@ packages: string.prototype.matchall: 4.0.11 dev: false - /eslint-plugin-vue/9.31.0_eslint@8.18.0: - resolution: {integrity: sha512-aYMUCgivhz1o4tLkRHj5oq9YgYPM4/EJc0M7TAKRLCUA5OYxRLAhYEVD2nLtTwLyixEFI+/QXSvKU9ESZFgqjQ==} + /eslint-plugin-vue/9.32.0_eslint@8.18.0: + resolution: {integrity: sha512-b/Y05HYmnB/32wqVcjxjHZzNpwxj1onBOvqW89W+V+XNG1dRuaFbNd3vT9CLbr2LXjEoq+3vn8DanWf7XU22Ug==} engines: {node: ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.2.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 @@ -7811,8 +7813,8 @@ packages: dependencies: es-errors: 1.3.0 function-bind: 1.1.2 - has-proto: 1.0.3 - has-symbols: 1.0.3 + has-proto: 1.1.0 + has-symbols: 1.1.0 hasown: 2.0.2 /get-package-type/0.1.0: @@ -8011,7 +8013,7 @@ packages: engines: {node: '>= 0.4'} dependencies: define-properties: 1.2.1 - gopd: 1.0.1 + gopd: 1.1.0 /globby/11.1.0: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} @@ -8031,8 +8033,9 @@ packages: sparkles: 1.0.1 dev: false - /gopd/1.0.1: - resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + /gopd/1.1.0: + resolution: {integrity: sha512-FQoVQnqcdk4hVM4JN1eromaun4iuS34oStkdlLENLdpULsuQcTyXj8w7ayhuUfPwEYZ1ZOooOTT6fdA9Vmx/RA==} + engines: {node: '>= 0.4'} dependencies: get-intrinsic: 1.2.4 @@ -8120,7 +8123,7 @@ packages: resolution: {integrity: sha512-SVSF7ikuWKhpAW4l4wapAqPPSToJoiNKsbDoUnRrSgwZHH7lH8pbPeQj1aOVYQrbZKhfSVBxVW+Py7vtulRktw==} engines: {node: '>=10'} dependencies: - '@types/node': 22.10.0 + '@types/node': 22.10.1 '@types/vinyl': 2.0.7 istextorbinary: 3.3.0 replacestream: 4.0.3 @@ -8214,19 +8217,21 @@ packages: dependencies: es-define-property: 1.0.0 - /has-proto/1.0.3: - resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} + /has-proto/1.1.0: + resolution: {integrity: sha512-QLdzI9IIO1Jg7f9GT1gXpPpXArAn6cS31R1eEZqz08Gc+uQ8/XiqHWt17Fiw+2p6oTTIq5GXEpQkAlA88YRl/Q==} engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 - /has-symbols/1.0.3: - resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + /has-symbols/1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} engines: {node: '>= 0.4'} /has-tostringtag/1.0.2: resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} engines: {node: '>= 0.4'} dependencies: - has-symbols: 1.0.3 + has-symbols: 1.1.0 /has-value/0.3.1: resolution: {integrity: sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==} @@ -8527,8 +8532,9 @@ packages: dependencies: has-tostringtag: 1.0.2 - /is-bigint/1.0.4: - resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} + /is-bigint/1.1.0: + resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} + engines: {node: '>= 0.4'} dependencies: has-bigints: 1.0.2 @@ -8546,8 +8552,8 @@ packages: binary-extensions: 2.3.0 dev: true - /is-boolean-object/1.1.2: - resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} + /is-boolean-object/1.2.0: + resolution: {integrity: sha512-kR5g0+dXf/+kXnqI+lu0URKYPKgICtHGGNCDSB10AaUFj3o/HkB3u7WfpRBJGFopxxY0oH3ux7ZsDjLtK7xqvw==} engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.7 @@ -8701,10 +8707,11 @@ packages: resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} engines: {node: '>= 0.4'} - /is-number-object/1.0.7: - resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} + /is-number-object/1.1.0: + resolution: {integrity: sha512-KVSZV0Dunv9DTPkhXwcZ3Q+tUc9TsaE1ZwX5J2WMvsSGS6Md8TFPun5uwh0yRdrNerI6vf/tbJxqSx4c1ZI1Lw==} engines: {node: '>= 0.4'} dependencies: + call-bind: 1.0.7 has-tostringtag: 1.0.2 /is-number/3.0.0: @@ -8752,12 +8759,14 @@ packages: '@types/estree': 1.0.6 dev: false - /is-regex/1.1.4: - resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} + /is-regex/1.2.0: + resolution: {integrity: sha512-B6ohK4ZmoftlUe+uvenXSbPJFo6U37BH7oO1B3nQH8f/7h27N56s85MhUtbFJAziz5dcmuR3i8ovUl35zp8pFA==} engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.7 + gopd: 1.1.0 has-tostringtag: 1.0.2 + hasown: 2.0.2 /is-relative/1.0.0: resolution: {integrity: sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==} @@ -8786,23 +8795,26 @@ packages: engines: {node: '>=8'} dev: true - /is-string/1.0.7: - resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} + /is-string/1.1.0: + resolution: {integrity: sha512-PlfzajuF9vSo5wErv3MJAKD/nqf9ngAs1NFQYm16nUYFO2IzxJ2hcm+IOCg+EEopdykNNUhVq5cz35cAUxU8+g==} engines: {node: '>= 0.4'} dependencies: + call-bind: 1.0.7 has-tostringtag: 1.0.2 - /is-symbol/1.0.4: - resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} + /is-symbol/1.1.0: + resolution: {integrity: sha512-qS8KkNNXUZ/I+nX6QT8ZS1/Yx0A444yhzdTKxCzKkNjQ9sHErBxJnJAgh+f5YhusYECEcjo4XcyH87hn6+ks0A==} engines: {node: '>= 0.4'} dependencies: - has-symbols: 1.0.3 + call-bind: 1.0.7 + has-symbols: 1.1.0 + safe-regex-test: 1.0.3 /is-typed-array/1.1.13: resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==} engines: {node: '>= 0.4'} dependencies: - which-typed-array: 1.1.15 + which-typed-array: 1.1.16 /is-typedarray/1.0.0: resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} @@ -9059,7 +9071,7 @@ packages: jest-validate: 26.6.2 micromatch: 4.0.8 pretty-format: 26.6.2 - ts-node: 10.9.0_gvyh6auciohtph3635nyjnikui + ts-node: 10.9.0_c27w6cdkixrdosqhzsswatltde transitivePeerDependencies: - bufferutil - canvas @@ -9164,7 +9176,7 @@ packages: '@jest/environment': 26.6.2 '@jest/fake-timers': 26.6.2 '@jest/types': 26.6.2 - '@types/node': 22.10.0 + '@types/node': 22.10.1 jest-mock: 26.6.2 jest-util: 26.6.2 jsdom: 16.7.0 @@ -9195,7 +9207,7 @@ packages: '@jest/environment': 26.6.2 '@jest/fake-timers': 26.6.2 '@jest/types': 26.6.2 - '@types/node': 22.10.0 + '@types/node': 22.10.1 jest-mock: 26.6.2 jest-util: 26.6.2 dev: true @@ -9235,7 +9247,7 @@ packages: dependencies: '@jest/types': 26.6.2 '@types/graceful-fs': 4.1.9 - '@types/node': 22.10.0 + '@types/node': 22.10.1 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -9283,7 +9295,7 @@ packages: '@jest/source-map': 26.6.2 '@jest/test-result': 26.6.2 '@jest/types': 26.6.2 - '@types/node': 22.10.0 + '@types/node': 22.10.1 chalk: 4.1.2 co: 4.6.0 expect: 26.6.2 @@ -9381,7 +9393,7 @@ packages: engines: {node: '>= 10.14.2'} dependencies: '@jest/types': 26.6.2 - '@types/node': 22.10.0 + '@types/node': 22.10.1 dev: true /jest-pnp-resolver/1.2.3_jest-resolve@24.9.0: @@ -9487,7 +9499,7 @@ packages: '@jest/environment': 26.6.2 '@jest/test-result': 26.6.2 '@jest/types': 26.6.2 - '@types/node': 22.10.0 + '@types/node': 22.10.1 chalk: 4.1.2 emittery: 0.7.2 exit: 0.1.2 @@ -9498,7 +9510,7 @@ packages: jest-leak-detector: 26.6.2 jest-message-util: 26.6.2 jest-resolve: 26.6.2 - jest-runtime: 26.6.3_ts-node@10.9.0 + jest-runtime: 26.6.3 jest-util: 26.6.2 jest-worker: 26.6.2 source-map-support: 0.5.21 @@ -9543,6 +9555,42 @@ packages: - supports-color dev: true + /jest-runtime/26.6.3: + resolution: {integrity: sha512-lrzyR3N8sacTAMeonbqpnSka1dHNux2uk0qqDXVkMv2c/A3wYnvQ4EXuI013Y6+gSKSCxdaczvf4HF0mVXHRdw==} + engines: {node: '>= 10.14.2'} + hasBin: true + dependencies: + '@jest/console': 26.6.2 + '@jest/environment': 26.6.2 + '@jest/fake-timers': 26.6.2 + '@jest/globals': 26.6.2 + '@jest/source-map': 26.6.2 + '@jest/test-result': 26.6.2 + '@jest/transform': 26.6.2 + '@jest/types': 26.6.2 + '@types/yargs': 15.0.19 + chalk: 4.1.2 + cjs-module-lexer: 0.6.0 + collect-v8-coverage: 1.0.2 + exit: 0.1.2 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-config: 26.6.3_ts-node@10.9.0 + jest-haste-map: 26.6.2 + jest-message-util: 26.6.2 + jest-mock: 26.6.2 + jest-regex-util: 26.0.0 + jest-resolve: 26.6.2 + jest-snapshot: 26.6.2 + jest-util: 26.6.2 + jest-validate: 26.6.2 + slash: 3.0.0 + strip-bom: 4.0.0 + yargs: 15.4.1 + transitivePeerDependencies: + - supports-color + dev: true + /jest-runtime/26.6.3_ts-node@10.9.0: resolution: {integrity: sha512-lrzyR3N8sacTAMeonbqpnSka1dHNux2uk0qqDXVkMv2c/A3wYnvQ4EXuI013Y6+gSKSCxdaczvf4HF0mVXHRdw==} engines: {node: '>= 10.14.2'} @@ -9592,7 +9640,7 @@ packages: resolution: {integrity: sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g==} engines: {node: '>= 10.14.2'} dependencies: - '@types/node': 22.10.0 + '@types/node': 22.10.1 graceful-fs: 4.2.11 dev: true @@ -9664,7 +9712,7 @@ packages: engines: {node: '>= 10.14.2'} dependencies: '@jest/types': 26.6.2 - '@types/node': 22.10.0 + '@types/node': 22.10.1 chalk: 4.1.2 graceful-fs: 4.2.11 is-ci: 2.0.0 @@ -9701,7 +9749,7 @@ packages: dependencies: '@jest/test-result': 26.6.2 '@jest/types': 26.6.2 - '@types/node': 22.10.0 + '@types/node': 22.10.1 ansi-escapes: 4.3.2 chalk: 4.1.2 jest-util: 26.6.2 @@ -9720,7 +9768,7 @@ packages: resolution: {integrity: sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==} engines: {node: '>= 10.13.0'} dependencies: - '@types/node': 22.10.0 + '@types/node': 22.10.1 merge-stream: 2.0.0 supports-color: 7.2.0 dev: true @@ -9789,7 +9837,7 @@ packages: escodegen: 1.14.3 html-encoding-sniffer: 1.0.2 left-pad: 1.3.0 - nwsapi: 2.2.13 + nwsapi: 2.2.16 parse5: 4.0.0 pn: 1.1.0 request: 2.88.2 @@ -9829,7 +9877,7 @@ packages: http-proxy-agent: 4.0.1 https-proxy-agent: 5.0.1 is-potential-custom-element-name: 1.0.1 - nwsapi: 2.2.13 + nwsapi: 2.2.16 parse5: 6.0.1 saxes: 5.0.1 symbol-tree: 3.2.4 @@ -10800,8 +10848,8 @@ packages: /number-precision/1.6.0: resolution: {integrity: sha512-05OLPgbgmnixJw+VvEh18yNPUo3iyp4BEWJcrLu4X9W05KmMifN7Mu5exYvQXqxxeNWhvIF+j3Rij+HmddM/hQ==} - /nwsapi/2.2.13: - resolution: {integrity: sha512-cTGB9ptp9dY9A5VbMSe7fQBcl/tt22Vcqdq8+eN93rblOuE0aCFu4aZ2vMwct/2t+lFnosm8RkQW1I0Omb1UtQ==} + /nwsapi/2.2.16: + resolution: {integrity: sha512-F1I/bimDpj3ncaNDhfyMWuFqmQDBwDB0Fogc2qpL3BWvkQteFD/8BzWuIRl83rq0DXfm8SGt/HFhLXZyljTXcQ==} dev: true /oauth-sign/0.9.0: @@ -10840,7 +10888,7 @@ packages: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - has-symbols: 1.0.3 + has-symbols: 1.1.0 object-keys: 1.1.1 /object.defaults/1.1.0: @@ -10881,7 +10929,7 @@ packages: define-properties: 1.2.1 es-abstract: 1.23.5 es-object-atoms: 1.0.0 - gopd: 1.0.1 + gopd: 1.1.0 safe-array-concat: 1.1.2 dev: true @@ -11437,7 +11485,7 @@ packages: dependencies: lilconfig: 2.1.0 postcss: 8.4.21 - ts-node: 10.9.0_gvyh6auciohtph3635nyjnikui + ts-node: 10.9.0_c27w6cdkixrdosqhzsswatltde yaml: 1.10.2 dev: false @@ -11857,8 +11905,8 @@ packages: resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==} optional: true - /psl/1.13.0: - resolution: {integrity: sha512-BFwmFXiJoFqlUpZ5Qssolv15DMyc84gTBds1BjsV1BfXEo1UyyD7GsmN67n7J77uRhoSNW1AXtXKPLcBFQn9Aw==} + /psl/1.15.0: + resolution: {integrity: sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==} dependencies: punycode: 2.3.1 dev: true @@ -12143,7 +12191,7 @@ packages: es-abstract: 1.23.5 es-errors: 1.3.0 get-intrinsic: 1.2.4 - gopd: 1.0.1 + gopd: 1.1.0 which-builtin-type: 1.2.0 /regenerate-unicode-properties/10.2.0: @@ -12570,7 +12618,7 @@ packages: dependencies: call-bind: 1.0.7 get-intrinsic: 1.2.4 - has-symbols: 1.0.3 + has-symbols: 1.1.0 isarray: 2.0.5 /safe-buffer/5.1.2: @@ -12589,7 +12637,7 @@ packages: dependencies: call-bind: 1.0.7 es-errors: 1.3.0 - is-regex: 1.1.4 + is-regex: 1.2.0 /safe-regex/1.1.0: resolution: {integrity: sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==} @@ -12711,7 +12759,7 @@ packages: es-errors: 1.3.0 function-bind: 1.1.2 get-intrinsic: 1.2.4 - gopd: 1.0.1 + gopd: 1.1.0 has-property-descriptors: 1.0.2 /set-function-name/2.0.2: @@ -13108,8 +13156,8 @@ packages: es-errors: 1.3.0 es-object-atoms: 1.0.0 get-intrinsic: 1.2.4 - gopd: 1.0.1 - has-symbols: 1.0.3 + gopd: 1.1.0 + has-symbols: 1.1.0 internal-slot: 1.0.7 regexp.prototype.flags: 1.5.3 set-function-name: 2.0.2 @@ -13536,7 +13584,7 @@ packages: resolution: {integrity: sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==} engines: {node: '>=0.8'} dependencies: - psl: 1.13.0 + psl: 1.15.0 punycode: 2.3.1 dev: true @@ -13544,7 +13592,7 @@ packages: resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==} engines: {node: '>=6'} dependencies: - psl: 1.13.0 + psl: 1.15.0 punycode: 2.3.1 universalify: 0.2.0 url-parse: 1.5.10 @@ -13590,7 +13638,7 @@ packages: mkdirp: 1.0.4 semver: 7.3.4 typescript: 4.9.5 - yargs-parser: 20.2.4 + yargs-parser: 20.2.9 dev: true /ts-loader/9.2.6_typescript@4.9.5: @@ -13607,7 +13655,7 @@ packages: typescript: 4.9.5 dev: true - /ts-node/10.9.0_gvyh6auciohtph3635nyjnikui: + /ts-node/10.9.0_c27w6cdkixrdosqhzsswatltde: resolution: {integrity: sha512-bunW18GUyaCSYRev4DPf4SQpom3pWH29wKl0sDk5zE7ze19RImEVhCW7K4v3hHKkUyfWotU08ToE2RS+Y49aug==} hasBin: true peerDependencies: @@ -13626,7 +13674,7 @@ packages: '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 22.10.0 + '@types/node': 22.10.1 acorn: 8.14.0 acorn-walk: 8.3.4 arg: 4.1.3 @@ -13696,7 +13744,7 @@ packages: typescript: '>=3.2.2' dependencies: resolve: 1.22.8 - ts-node: 10.9.0_gvyh6auciohtph3635nyjnikui + ts-node: 10.9.0_c27w6cdkixrdosqhzsswatltde typescript: 4.9.5 dev: true @@ -13782,8 +13830,8 @@ packages: dependencies: call-bind: 1.0.7 for-each: 0.3.3 - gopd: 1.0.1 - has-proto: 1.0.3 + gopd: 1.1.0 + has-proto: 1.1.0 is-typed-array: 1.1.13 /typed-array-byte-offset/1.0.3: @@ -13793,8 +13841,8 @@ packages: available-typed-arrays: 1.0.7 call-bind: 1.0.7 for-each: 0.3.3 - gopd: 1.0.1 - has-proto: 1.0.3 + gopd: 1.1.0 + has-proto: 1.1.0 is-typed-array: 1.1.13 reflect.getprototypeof: 1.0.7 @@ -13804,7 +13852,7 @@ packages: dependencies: call-bind: 1.0.7 for-each: 0.3.3 - gopd: 1.0.1 + gopd: 1.1.0 is-typed-array: 1.1.13 possible-typed-array-names: 1.0.0 reflect.getprototypeof: 1.0.7 @@ -13852,8 +13900,8 @@ packages: dependencies: call-bind: 1.0.7 has-bigints: 1.0.2 - has-symbols: 1.0.3 - which-boxed-primitive: 1.0.2 + has-symbols: 1.1.0 + which-boxed-primitive: 1.1.0 /unc-path-regex/0.1.2: resolution: {integrity: sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg==} @@ -14058,8 +14106,8 @@ packages: call-bind: 1.0.7 define-properties: 1.2.1 for-each: 0.3.3 - has-proto: 1.0.3 - has-symbols: 1.0.3 + has-proto: 1.1.0 + has-symbols: 1.1.0 object.getownpropertydescriptors: 2.1.8 safe-array-concat: 1.1.2 dev: true @@ -14164,7 +14212,7 @@ packages: replace-ext: 1.0.1 dev: false - /vite-node/0.30.1_bynzxunm26yds2qzned2henjoq: + /vite-node/0.30.1_umpqsa3rsshwmingabuhbdi534: resolution: {integrity: sha512-vTikpU/J7e6LU/8iM3dzBo8ZhEiKZEKRznEMm+mJh95XhWaPrJQraT/QsT2NWmuEf+zgAoMe64PKT7hfZ1Njmg==} engines: {node: '>=v14.18.0'} hasBin: true @@ -14174,7 +14222,7 @@ packages: mlly: 1.7.3 pathe: 1.1.2 picocolors: 1.1.1 - vite: 3.2.6_bynzxunm26yds2qzned2henjoq + vite: 3.2.6_umpqsa3rsshwmingabuhbdi534 transitivePeerDependencies: - '@types/node' - less @@ -14194,7 +14242,7 @@ packages: front-matter: 4.0.2 htmlparser2: 6.1.0 markdown-it: 12.3.2 - vite: 3.2.6_3fcrixghlk5j7efp7olgstdjb4 + vite: 3.2.6_girnrryua4ny3ya6dfs4crajjq dev: true /vite/3.2.6: @@ -14230,7 +14278,7 @@ packages: fsevents: 2.3.3 dev: true - /vite/3.2.6_3fcrixghlk5j7efp7olgstdjb4: + /vite/3.2.6_@types+node@22.10.1: resolution: {integrity: sha512-nTXTxYVvaQNLoW5BQ8PNNQ3lPia57gzsQU/Khv+JvzKPku8kNZL6NMUR/qwXhMG6E+g1idqEPanomJ+VZgixEg==} engines: {node: ^14.18.0 || >=16.0.0} hasBin: true @@ -14255,17 +14303,16 @@ packages: terser: optional: true dependencies: - '@types/node': 22.10.0 + '@types/node': 22.10.1 esbuild: 0.15.18 postcss: 8.4.21 resolve: 1.22.8 rollup: 2.79.2 - sass: 1.43.5 optionalDependencies: fsevents: 2.3.3 dev: true - /vite/3.2.6_@types+node@22.10.0: + /vite/3.2.6_girnrryua4ny3ya6dfs4crajjq: resolution: {integrity: sha512-nTXTxYVvaQNLoW5BQ8PNNQ3lPia57gzsQU/Khv+JvzKPku8kNZL6NMUR/qwXhMG6E+g1idqEPanomJ+VZgixEg==} engines: {node: ^14.18.0 || >=16.0.0} hasBin: true @@ -14290,16 +14337,17 @@ packages: terser: optional: true dependencies: - '@types/node': 22.10.0 + '@types/node': 22.10.1 esbuild: 0.15.18 postcss: 8.4.21 resolve: 1.22.8 rollup: 2.79.2 + sass: 1.43.5 optionalDependencies: fsevents: 2.3.3 dev: true - /vite/3.2.6_bynzxunm26yds2qzned2henjoq: + /vite/3.2.6_umpqsa3rsshwmingabuhbdi534: resolution: {integrity: sha512-nTXTxYVvaQNLoW5BQ8PNNQ3lPia57gzsQU/Khv+JvzKPku8kNZL6NMUR/qwXhMG6E+g1idqEPanomJ+VZgixEg==} engines: {node: ^14.18.0 || >=16.0.0} hasBin: true @@ -14324,7 +14372,7 @@ packages: terser: optional: true dependencies: - '@types/node': 22.10.0 + '@types/node': 22.10.1 esbuild: 0.15.18 less: 4.1.3 postcss: 8.4.21 @@ -14368,7 +14416,7 @@ packages: dependencies: '@types/chai': 4.3.20 '@types/chai-subset': 1.3.5 - '@types/node': 22.10.0 + '@types/node': 22.10.1 '@vitest/expect': 0.30.1 '@vitest/runner': 0.30.1 '@vitest/snapshot': 0.30.1 @@ -14389,8 +14437,8 @@ packages: strip-literal: 1.3.0 tinybench: 2.9.0 tinypool: 0.4.0 - vite: 3.2.6_bynzxunm26yds2qzned2henjoq - vite-node: 0.30.1_bynzxunm26yds2qzned2henjoq + vite: 3.2.6_umpqsa3rsshwmingabuhbdi534 + vite-node: 0.30.1_umpqsa3rsshwmingabuhbdi534 why-is-node-running: 2.3.0 transitivePeerDependencies: - less @@ -14518,14 +14566,15 @@ packages: webidl-conversions: 6.1.0 dev: true - /which-boxed-primitive/1.0.2: - resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} + /which-boxed-primitive/1.1.0: + resolution: {integrity: sha512-Ei7Miu/AXe2JJ4iNF5j/UphAgRoma4trE6PtisM09bPygb3egMH3YLW/befsWb1A1AxvNSFidOFTB18XtnIIng==} + engines: {node: '>= 0.4'} dependencies: - is-bigint: 1.0.4 - is-boolean-object: 1.1.2 - is-number-object: 1.0.7 - is-string: 1.0.7 - is-symbol: 1.0.4 + is-bigint: 1.1.0 + is-boolean-object: 1.2.0 + is-number-object: 1.1.0 + is-string: 1.1.0 + is-symbol: 1.1.0 /which-builtin-type/1.2.0: resolution: {integrity: sha512-I+qLGQ/vucCby4tf5HsLmGueEla4ZhwTBSqaooS+Y0BuxN4Cp+okmGuV+8mXZ84KDI9BA+oklo+RzKg0ONdSUA==} @@ -14538,12 +14587,12 @@ packages: is-date-object: 1.0.5 is-finalizationregistry: 1.1.0 is-generator-function: 1.0.10 - is-regex: 1.1.4 + is-regex: 1.2.0 is-weakref: 1.0.2 isarray: 2.0.5 - which-boxed-primitive: 1.0.2 + which-boxed-primitive: 1.1.0 which-collection: 1.0.2 - which-typed-array: 1.1.15 + which-typed-array: 1.1.16 /which-collection/1.0.2: resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} @@ -14562,14 +14611,14 @@ packages: resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} dev: true - /which-typed-array/1.1.15: - resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==} + /which-typed-array/1.1.16: + resolution: {integrity: sha512-g+N+GAWiRj66DngFwHvISJd+ITsyphZvD1vChfVg6cEdnzy53GzB3oy0fUNlvhz7H7+MiqhYr26qxQShCpKTTQ==} engines: {node: '>= 0.4'} dependencies: available-typed-arrays: 1.0.7 call-bind: 1.0.7 for-each: 0.3.3 - gopd: 1.0.1 + gopd: 1.1.0 has-tostringtag: 1.0.2 /which/1.3.1: @@ -14735,6 +14784,11 @@ packages: engines: {node: '>=10'} dev: true + /yargs-parser/20.2.9: + resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} + engines: {node: '>=10'} + dev: true + /yargs-parser/21.1.1: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} engines: {node: '>=12'} @@ -14798,7 +14852,7 @@ packages: require-directory: 2.1.1 string-width: 4.2.3 y18n: 5.0.8 - yargs-parser: 20.2.4 + yargs-parser: 20.2.9 dev: true /yargs/17.7.2: From 793f30ba9c64c7406293f186f3b9dc85613b005b Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Tue, 3 Dec 2024 14:40:48 +0800 Subject: [PATCH 30/40] fix: after add taskbarclip group resize error --- packages/vtable-gantt/src/scenegraph/gantt-node.ts | 1 + packages/vtable-gantt/src/scenegraph/task-bar.ts | 7 ++++--- packages/vtable-gantt/src/state/state-manager.ts | 5 +++++ 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/packages/vtable-gantt/src/scenegraph/gantt-node.ts b/packages/vtable-gantt/src/scenegraph/gantt-node.ts index e45472a21..166da1c3f 100644 --- a/packages/vtable-gantt/src/scenegraph/gantt-node.ts +++ b/packages/vtable-gantt/src/scenegraph/gantt-node.ts @@ -2,6 +2,7 @@ import type { IRect, IText, IGroupGraphicAttribute } from '@visactor/vtable/es/v import { Group } from '@visactor/vtable/es/vrender'; export class GanttTaskBarNode extends Group { + clipGroupBox: Group; barRect?: IRect; progressRect?: IRect; textLabel?: IText; diff --git a/packages/vtable-gantt/src/scenegraph/task-bar.ts b/packages/vtable-gantt/src/scenegraph/task-bar.ts index e2368d8bf..ed4b2c82a 100644 --- a/packages/vtable-gantt/src/scenegraph/task-bar.ts +++ b/packages/vtable-gantt/src/scenegraph/task-bar.ts @@ -152,6 +152,7 @@ export class TaskBar { clip: true }); barGroupBox.appendChild(barGroup); + barGroupBox.clipGroupBox = barGroup; let rootContainer; let renderDefaultBar = true; let renderDefaultText = true; @@ -197,7 +198,7 @@ export class TaskBar { }); rect.name = 'task-bar-rect'; barGroup.appendChild(rect); - barGroup.barRect = rect; + barGroupBox.barRect = rect; // 创建已完成部分任务条rect const progress_rect = createRect({ x: 0, @@ -209,7 +210,7 @@ export class TaskBar { }); progress_rect.name = 'task-bar-progress-rect'; barGroup.appendChild(progress_rect); - barGroup.progressRect = progress_rect; + barGroupBox.progressRect = progress_rect; } rootContainer && barGroup.appendChild(rootContainer); @@ -246,7 +247,7 @@ export class TaskBar { // dy: this._scene._gantt.barLabelStyle.fontSize / 2 }); barGroup.appendChild(label); - barGroup.textLabel = label; + barGroupBox.textLabel = label; } return barGroupBox; } diff --git a/packages/vtable-gantt/src/state/state-manager.ts b/packages/vtable-gantt/src/state/state-manager.ts index 5e49ce267..27a6104fb 100644 --- a/packages/vtable-gantt/src/state/state-manager.ts +++ b/packages/vtable-gantt/src/state/state-manager.ts @@ -538,6 +538,7 @@ export class StateManager { diff_days = direction === 'left' ? -diff_days : diff_days; const taskBarGroup = this.resizeTaskBar.target; + const clipGroupBox = taskBarGroup.clipGroupBox; const rect = this.resizeTaskBar.target.barRect; const progressRect = this.resizeTaskBar.target.progressRect; // const taskIndex = getTaskIndexByY(this.resizeTaskBar.startOffsetY, this._gantt); @@ -570,10 +571,12 @@ export class StateManager { const taskBarSize = this._gantt.parsedOptions.colWidthPerDay * (taskDays + diff_days); if (direction === 'left') { resizeOrMoveTaskBar(taskBarGroup, targetEndX - taskBarGroup.attribute.x, 0, taskBarSize, this); + clipGroupBox.setAttribute('width', taskBarGroup.attribute.width); rect?.setAttribute('width', taskBarGroup.attribute.width); progressRect?.setAttribute('width', (progress / 100) * taskBarGroup.attribute.width); } else if (direction === 'right') { resizeOrMoveTaskBar(taskBarGroup, 0, 0, taskBarSize, this); + clipGroupBox.setAttribute('width', taskBarGroup.attribute.width); rect?.setAttribute('width', taskBarGroup.attribute.width); progressRect?.setAttribute('width', (progress / 100) * taskBarGroup.attribute.width); } @@ -606,6 +609,7 @@ export class StateManager { // debugger; const taskBarGroup = this._gantt.stateManager.resizeTaskBar.target; taskBarGroup.setAttribute('zIndex', 10000); + const clipGroupBox = taskBarGroup.clipGroupBox; const rect = taskBarGroup.barRect; const progressRect = taskBarGroup.progressRect; const textLabel = taskBarGroup.textLabel; @@ -635,6 +639,7 @@ export class StateManager { taskBarSize, this ); + clipGroupBox.setAttribute('width', taskBarGroup.attribute.width); rect?.setAttribute('width', taskBarGroup.attribute.width); progressRect?.setAttribute('width', (progress / 100) * taskBarGroup.attribute.width); From 035ff44c5061fc740fc8e8ebaf637ca6ef793a56 Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Tue, 3 Dec 2024 17:59:10 +0800 Subject: [PATCH 31/40] chore: add changelog --- ...949-feature-add-taskShowMode_2024-11-29-10-32.json | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 common/changes/@visactor/vtable/2949-feature-add-taskShowMode_2024-11-29-10-32.json diff --git a/common/changes/@visactor/vtable/2949-feature-add-taskShowMode_2024-11-29-10-32.json b/common/changes/@visactor/vtable/2949-feature-add-taskShowMode_2024-11-29-10-32.json new file mode 100644 index 000000000..26dac1513 --- /dev/null +++ b/common/changes/@visactor/vtable/2949-feature-add-taskShowMode_2024-11-29-10-32.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "feat: add taskShowMode for gantt chart #2849\n\n", + "type": "minor", + "packageName": "@visactor/vtable" + } + ], + "packageName": "@visactor/vtable", + "email": "892739385@qq.com" +} \ No newline at end of file From 67882a52a1c089c2de011a4efa1584bf8d7e8f05 Mon Sep 17 00:00:00 2001 From: Rui-Sun Date: Mon, 2 Dec 2024 16:24:09 +0800 Subject: [PATCH 32/40] fix: fix last col&row editor size #2926 --- .../@visactor/vtable/feat-fs-bs_2024-12-02-08-23.json | 10 ++++++++++ packages/vtable/src/edit/edit-manager.ts | 8 ++++++++ 2 files changed, 18 insertions(+) create mode 100644 common/changes/@visactor/vtable/feat-fs-bs_2024-12-02-08-23.json diff --git a/common/changes/@visactor/vtable/feat-fs-bs_2024-12-02-08-23.json b/common/changes/@visactor/vtable/feat-fs-bs_2024-12-02-08-23.json new file mode 100644 index 000000000..d47990475 --- /dev/null +++ b/common/changes/@visactor/vtable/feat-fs-bs_2024-12-02-08-23.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@visactor/vtable", + "comment": "fix: fix last col&row editor size #2926", + "type": "none" + } + ], + "packageName": "@visactor/vtable" +} \ No newline at end of file diff --git a/packages/vtable/src/edit/edit-manager.ts b/packages/vtable/src/edit/edit-manager.ts index 7a73d4891..adebe9052 100644 --- a/packages/vtable/src/edit/edit-manager.ts +++ b/packages/vtable/src/edit/edit-manager.ts @@ -94,6 +94,14 @@ export class EditManeger { const rect = this.table.getCellRangeRelativeRect(this.table.getCellRange(col, row)); const referencePosition = { rect: { left: rect.left, top: rect.top, width: rect.width, height: rect.height } }; + // adjust last col&row, same as packages/vtable/src/scenegraph/graphic/contributions/group-contribution-render.ts getCellSizeForDraw + if (col === this.table.colCount - 1) { + referencePosition.rect.width = rect.width - 1; + } + if (row === this.table.rowCount - 1) { + referencePosition.rect.height = rect.height - 1; + } + editor.beginEditing && console.warn('VTable Warn: `beginEditing` is deprecated, please use `onStart` instead.'); editor.beginEditing?.(this.table.getElement(), referencePosition, dataValue); From dd1424b5ab984fd89711fc12f7352fa3f3b1436f Mon Sep 17 00:00:00 2001 From: Rui-Sun Date: Mon, 2 Dec 2024 16:27:49 +0800 Subject: [PATCH 33/40] fix: fix cell update event problem in CustomCellStylePlugin #2927 --- .../vtable/feat-fs-bs_2024-12-02-08-27.json | 10 ++++ common/config/rush/pnpm-lock.yaml | 2 + .../demo/header-highlight/header-highlight.ts | 49 ++++++++++++------- packages/vtable-plugins/demo/index.html | 7 --- packages/vtable-plugins/demo/main.ts | 31 ------------ packages/vtable-plugins/package.json | 3 +- .../vtable-plugins/src/header-highlight.ts | 2 +- .../vtable/src/plugins/custom-cell-style.ts | 24 +++++++-- .../scenegraph/group-creater/cell-helper.ts | 11 ++++- packages/vtable/src/scenegraph/scenegraph.ts | 4 +- packages/vtable/src/ts-types/column/style.ts | 2 + 11 files changed, 81 insertions(+), 64 deletions(-) create mode 100644 common/changes/@visactor/vtable/feat-fs-bs_2024-12-02-08-27.json diff --git a/common/changes/@visactor/vtable/feat-fs-bs_2024-12-02-08-27.json b/common/changes/@visactor/vtable/feat-fs-bs_2024-12-02-08-27.json new file mode 100644 index 000000000..c2c2dd544 --- /dev/null +++ b/common/changes/@visactor/vtable/feat-fs-bs_2024-12-02-08-27.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@visactor/vtable", + "comment": "fix: fix cell update event problem in CustomCellStylePlugin #2927", + "type": "none" + } + ], + "packageName": "@visactor/vtable" +} \ No newline at end of file diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index af1231ec6..5f0829773 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -782,6 +782,7 @@ importers: '@types/react-is': ^17.0.3 '@visactor/vchart': 1.12.12 '@visactor/vtable': workspace:* + '@visactor/vtable-editors': workspace:* '@visactor/vutils': ~0.18.14 '@vitejs/plugin-react': 3.1.0 axios: ^1.4.0 @@ -831,6 +832,7 @@ importers: '@types/react-is': 17.0.7 '@visactor/vchart': 1.12.12 '@visactor/vtable': link:../vtable + '@visactor/vtable-editors': link:../vtable-editors '@vitejs/plugin-react': 3.1.0_vite@3.2.6 axios: 1.7.8 chai: 4.3.4 diff --git a/packages/vtable-plugins/demo/header-highlight/header-highlight.ts b/packages/vtable-plugins/demo/header-highlight/header-highlight.ts index 88d852bb3..da1c564b2 100644 --- a/packages/vtable-plugins/demo/header-highlight/header-highlight.ts +++ b/packages/vtable-plugins/demo/header-highlight/header-highlight.ts @@ -1,5 +1,7 @@ import * as VTable from '@visactor/vtable'; import { bindDebugTool } from '@visactor/vtable/es/scenegraph/debug-tool'; +import * as VTable_editors from '@visactor/vtable-editors'; + import { HeaderHighlightPlugin } from '../../src'; const CONTAINER_ID = 'vTable'; const generatePersons = count => { @@ -19,6 +21,9 @@ const generatePersons = count => { }; export function createTable() { + const input_editor = new VTable_editors.InputEditor(); + VTable.register.editor('input-editor', input_editor); + const records = generatePersons(20); const columns: VTable.ColumnsDefine = [ { @@ -26,7 +31,9 @@ export function createTable() { title: 'ID', width: 'auto', minWidth: 50, - sort: true + sort: true, + headerEditor: 'input-editor', + editor: 'input-editor' }, { field: 'email1', @@ -39,21 +46,6 @@ export function createTable() { underlineOffset: 3 } }, - { - title: 'full name', - columns: [ - { - field: 'name', - title: 'First Name', - width: 200 - }, - { - field: 'name', - title: 'Last Name', - width: 200 - } - ] - }, { field: 'date1', title: 'birthday', @@ -69,7 +61,16 @@ export function createTable() { container: document.getElementById(CONTAINER_ID), records, columns, - rowSeriesNumber: {} + rowSeriesNumber: {}, + select: { + outsideClickDeselect: true, + headerSelectMode: 'body' + }, + autoWrapText: true, + editor: 'input-editor', + menu: { + contextMenuItems: ['copy', 'paste', 'delete', '...'] + } }; const tableInstance = new VTable.ListTable(option); window.tableInstance = tableInstance; @@ -101,5 +102,19 @@ export function createTable() { const highlightPlugin = new HeaderHighlightPlugin(tableInstance); window.highlightPlugin = highlightPlugin; + const onShowMenu = () => { + console.log('show_menu'); + // 菜单dom位置 及 层级处理 + const menuElement = document.getElementsByClassName('vtable__menu-element')[0]; + if (menuElement) { + console.log('find_menu'); + // menuElement.setAttribute('style', `top: -${menuElement.clientHeight}px !important`) + menuElement.setAttribute('style', `top: -${0}px !important`); + menuElement.setAttribute('style', `z-index: 9999`); + } + }; + + tableInstance.on('show_menu', onShowMenu); + // tableInstance.scenegraph.temporarilyUpdateSelectRectStyle({stroke: false}) } diff --git a/packages/vtable-plugins/demo/index.html b/packages/vtable-plugins/demo/index.html index 922a54969..8af448ba9 100644 --- a/packages/vtable-plugins/demo/index.html +++ b/packages/vtable-plugins/demo/index.html @@ -15,13 +15,6 @@
-
- - 0/0 - - - -
diff --git a/packages/vtable-plugins/demo/main.ts b/packages/vtable-plugins/demo/main.ts index 334ab9ec9..dbf5efc7e 100644 --- a/packages/vtable-plugins/demo/main.ts +++ b/packages/vtable-plugins/demo/main.ts @@ -130,35 +130,4 @@ const run = () => { ); }; -function bindSearch() { - const searchInput = document.getElementById('search-component-input') as HTMLInputElement; - const searchBtn = document.getElementById('search-component-search') as HTMLButtonElement; - const searchResult = document.getElementById('search-component-result') as HTMLSpanElement; - const searchPrevBtn = document.getElementById('search-component-prev') as HTMLButtonElement; - const searchNextBtn = document.getElementById('search-component-next') as HTMLButtonElement; - - searchBtn.addEventListener('click', () => { - if (window.search) { - const result = window.search.search(searchInput.value); - searchResult.innerText = `${result.index + 1}/${result.results.length}`; - } - }); - - searchPrevBtn.addEventListener('click', () => { - if (window.search) { - const result = window.search.prev(); - searchResult.innerText = `${result.index + 1}/${result.results.length}`; - } - }); - - searchNextBtn.addEventListener('click', () => { - if (window.search) { - const result = window.search.next(); - searchResult.innerText = `${result.index + 1}/${result.results.length}`; - } - }); -} - run(); - -bindSearch(); diff --git a/packages/vtable-plugins/package.json b/packages/vtable-plugins/package.json index 925df00e7..34db1862c 100644 --- a/packages/vtable-plugins/package.json +++ b/packages/vtable-plugins/package.json @@ -41,6 +41,7 @@ }, "devDependencies": { "@visactor/vtable": "workspace:*", + "@visactor/vtable-editors": "workspace:*", "@visactor/vchart": "1.12.12", "@internal/bundler": "workspace:*", "@internal/eslint-config": "workspace:*", @@ -86,4 +87,4 @@ "@types/react-is": "^17.0.3", "rollup-plugin-node-resolve": "5.2.0" } -} +} \ No newline at end of file diff --git a/packages/vtable-plugins/src/header-highlight.ts b/packages/vtable-plugins/src/header-highlight.ts index ba2c3eb20..03e541ea1 100644 --- a/packages/vtable-plugins/src/header-highlight.ts +++ b/packages/vtable-plugins/src/header-highlight.ts @@ -36,7 +36,7 @@ export class HeaderHighlightPlugin { } bindEvent() { - this.table.on('selected_cell', e => { + this.table.on('selected_cell', () => { this.updateHighlight(); }); diff --git a/packages/vtable/src/plugins/custom-cell-style.ts b/packages/vtable/src/plugins/custom-cell-style.ts index 7fa91555f..4799146de 100644 --- a/packages/vtable/src/plugins/custom-cell-style.ts +++ b/packages/vtable/src/plugins/custom-cell-style.ts @@ -1,6 +1,12 @@ import { merge } from '@visactor/vutils'; import type { BaseTableAPI } from '../ts-types/base-table'; -import type { CellRange, ColumnStyleOption, CustomCellStyle, CustomCellStyleArrangement } from '../ts-types'; +import { + cellStyleKeys, + type CellRange, + type ColumnStyleOption, + type CustomCellStyle, + type CustomCellStyleArrangement +} from '../ts-types'; import type { Style } from '../body-helper/style'; import { Factory } from '../core/factory'; @@ -163,6 +169,18 @@ export class CustomCellStylePlugin { this.customCellStyleArrangement.splice(index, 1); } + const style = this.getCustomCellStyleOption(customStyleId)?.style; + let forceFastUpdate; + if (style) { + forceFastUpdate = true; + for (const key in style) { + if (cellStyleKeys.indexOf(key) === -1) { + forceFastUpdate = false; + break; + } + } + } + // update cell group if (cellPos.range) { for ( @@ -178,14 +196,14 @@ export class CustomCellStylePlugin { const range = this.table.getCellRange(col, row); for (let c = range.start.col; c <= range.end.col; c++) { for (let r = range.start.row; r <= range.end.row; r++) { - this.table.scenegraph.updateCellContent(c, r); + this.table.scenegraph.updateCellContent(c, r, forceFastUpdate); } } // this.table.scenegraph.updateCellContent(col, row); } } } else { - this.table.scenegraph.updateCellContent(cellPos.col, cellPos.row); + this.table.scenegraph.updateCellContent(cellPos.col, cellPos.row, forceFastUpdate); } this.table.scenegraph.updateNextFrame(); diff --git a/packages/vtable/src/scenegraph/group-creater/cell-helper.ts b/packages/vtable/src/scenegraph/group-creater/cell-helper.ts index 5bdf39a01..5623cc9c7 100644 --- a/packages/vtable/src/scenegraph/group-creater/cell-helper.ts +++ b/packages/vtable/src/scenegraph/group-creater/cell-helper.ts @@ -392,7 +392,14 @@ export function createCell( return cellGroup; } -export function updateCell(col: number, row: number, table: BaseTableAPI, addNew?: boolean, isShadow?: boolean) { +export function updateCell( + col: number, + row: number, + table: BaseTableAPI, + addNew?: boolean, + isShadow?: boolean, + forceFastUpdate?: boolean +) { // const oldCellGroup = table.scenegraph.getCell(col, row, true); const oldCellGroup = table.scenegraph.highPerformanceGetCell(col, row, true); const cellLocation = table.getCellLocation(col, row); @@ -515,7 +522,7 @@ export function updateCell(col: number, row: number, table: BaseTableAPI, addNew !addNew && !isMerge && !(define?.customLayout || define?.customRender || define?.headerCustomLayout || define?.headerCustomRender) && - canUseFastUpdate(col, row, oldCellGroup, autoWrapText, mayHaveIcon, table) + (forceFastUpdate || canUseFastUpdate(col, row, oldCellGroup, autoWrapText, mayHaveIcon, table)) ) { // update group const cellWidth = table.getColWidth(col); diff --git a/packages/vtable/src/scenegraph/scenegraph.ts b/packages/vtable/src/scenegraph/scenegraph.ts index 4670fd710..264e1d50a 100644 --- a/packages/vtable/src/scenegraph/scenegraph.ts +++ b/packages/vtable/src/scenegraph/scenegraph.ts @@ -1920,11 +1920,11 @@ export class Scenegraph { this.component.drillIcon.update(visible, x, y, drillDown, drillUp, this); } - updateCellContent(col: number, row: number) { + updateCellContent(col: number, row: number, forceFastUpdate: boolean = false) { if (this.clear) { return undefined; } - return updateCell(col, row, this.table); + return updateCell(col, row, this.table, undefined, undefined, forceFastUpdate); } setPixelRatio(pixelRatio: number) { diff --git a/packages/vtable/src/ts-types/column/style.ts b/packages/vtable/src/ts-types/column/style.ts index ee9a224dc..3e827824a 100644 --- a/packages/vtable/src/ts-types/column/style.ts +++ b/packages/vtable/src/ts-types/column/style.ts @@ -99,6 +99,8 @@ export interface IStyleOption { marked?: MarkedPropertyDefine; } +export const cellStyleKeys = ['bgColor', 'color', 'strokeColor', 'borderColor', 'linkColor']; // keys of style not change cell layout + export interface ITextStyleOption extends IStyleOption { // lineHeight?: string | number;//移入IStyleOption中 单行文本类型也可以有 autoWrapText?: boolean; From 6fee05105b278bde4c5d8b8f6dd52c61983775bc Mon Sep 17 00:00:00 2001 From: Rui-Sun Date: Mon, 2 Dec 2024 17:42:08 +0800 Subject: [PATCH 34/40] fix: fix react-component in tree mode update --- .../fix-custom-component-update_2024-12-02-09-42.json | 10 ++++++++++ packages/vtable/src/ListTable.ts | 3 +++ packages/vtable/src/PivotTable.ts | 3 +++ 3 files changed, 16 insertions(+) create mode 100644 common/changes/@visactor/vtable/fix-custom-component-update_2024-12-02-09-42.json diff --git a/common/changes/@visactor/vtable/fix-custom-component-update_2024-12-02-09-42.json b/common/changes/@visactor/vtable/fix-custom-component-update_2024-12-02-09-42.json new file mode 100644 index 000000000..3c84e625c --- /dev/null +++ b/common/changes/@visactor/vtable/fix-custom-component-update_2024-12-02-09-42.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@visactor/vtable", + "comment": "fix: fix react-component in tree mode update", + "type": "none" + } + ], + "packageName": "@visactor/vtable" +} \ No newline at end of file diff --git a/packages/vtable/src/ListTable.ts b/packages/vtable/src/ListTable.ts index daad48206..e1fc278ee 100644 --- a/packages/vtable/src/ListTable.ts +++ b/packages/vtable/src/ListTable.ts @@ -924,12 +924,15 @@ export class ListTable extends BaseTable implements ListTableAPI { // reset proxy row config this.scenegraph.proxy.refreshRowCount(); } + this.reactCustomLayout?.clearCache(); this.scenegraph.updateRow( diffPositions.removeCellPositions, diffPositions.addCellPositions, updateCells, recalculateColWidths ); + this.reactCustomLayout?.updateAllCustomCell(); + if (checkHasChart) { // 检查更新节点状态后总宽高未撑满autoFill是否在起作用 if (this.autoFillWidth && !notFillWidth) { diff --git a/packages/vtable/src/PivotTable.ts b/packages/vtable/src/PivotTable.ts index 6c6d0c9bc..448704a38 100644 --- a/packages/vtable/src/PivotTable.ts +++ b/packages/vtable/src/PivotTable.ts @@ -1416,12 +1416,15 @@ export class PivotTable extends BaseTable implements PivotTableAPI { // this.invalidate(); this.clearCellStyleCache(); this.scenegraph.updateHierarchyIcon(col, row); + this.reactCustomLayout?.clearCache(); this.scenegraph.updateRow( result.removeCellPositions, result.addCellPositions, result.updateCellPositions, recalculateColWidths ); + this.reactCustomLayout?.updateAllCustomCell(); + if (checkHasChart) { // 检查更新节点状态后总宽高未撑满autoFill是否在起作用 if (this.autoFillWidth && !notFillWidth) { From ad3c2baf970debddd8bf1b6de31982f6137dca7f Mon Sep 17 00:00:00 2001 From: Rui-Sun Date: Mon, 2 Dec 2024 20:11:47 +0800 Subject: [PATCH 35/40] fix: fix strokeArrayWidth update in updateCell() #2811 --- .../fix-strokeArrayWidth-update_2024-12-02-12-11.json | 10 ++++++++++ .../vtable/src/scenegraph/group-creater/cell-helper.ts | 5 ++++- 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 common/changes/@visactor/vtable/fix-strokeArrayWidth-update_2024-12-02-12-11.json diff --git a/common/changes/@visactor/vtable/fix-strokeArrayWidth-update_2024-12-02-12-11.json b/common/changes/@visactor/vtable/fix-strokeArrayWidth-update_2024-12-02-12-11.json new file mode 100644 index 000000000..0c349dc49 --- /dev/null +++ b/common/changes/@visactor/vtable/fix-strokeArrayWidth-update_2024-12-02-12-11.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@visactor/vtable", + "comment": "fix: fix strokeArrayWidth update in updateCell() #2811", + "type": "none" + } + ], + "packageName": "@visactor/vtable" +} \ No newline at end of file diff --git a/packages/vtable/src/scenegraph/group-creater/cell-helper.ts b/packages/vtable/src/scenegraph/group-creater/cell-helper.ts index 5623cc9c7..e3a6b0a0e 100644 --- a/packages/vtable/src/scenegraph/group-creater/cell-helper.ts +++ b/packages/vtable/src/scenegraph/group-creater/cell-helper.ts @@ -37,6 +37,7 @@ import { isArray, isValid } from '@visactor/vutils'; import { breakString } from '../utils/break-string'; import type { CreateRadioCellGroup } from './cell-type/radio-cell'; import { onBeforeAttributeUpdateForInvertHighlight } from '../../plugins/invert-highlight'; +import { getCellBorderStrokeWidth } from '../utils/cell-border-stroke-width'; export function createCell( type: ColumnTypeOption, @@ -527,6 +528,8 @@ export function updateCell( // update group const cellWidth = table.getColWidth(col); const cellHeight = table.getRowHeight(row); + const strokeArrayWidth = getCellBorderStrokeWidth(col, row, cellTheme, table); + oldCellGroup.setAttributes({ width: cellWidth, height: cellHeight, @@ -534,7 +537,7 @@ export function updateCell( lineWidth: cellTheme?.group?.lineWidth ?? undefined, fill: cellTheme?.group?.fill ?? undefined, stroke: cellTheme?.group?.stroke ?? undefined, - strokeArrayWidth: (cellTheme?.group as any)?.strokeArrayWidth ?? undefined, + strokeArrayWidth: strokeArrayWidth ?? undefined, strokeArrayColor: (cellTheme?.group as any)?.strokeArrayColor ?? undefined, cursor: (cellTheme?.group as any)?.cursor ?? undefined, cornerRadius: cellTheme?.group?.cornerRadius ?? 0, From 125f1b359731742fec9d60ef9a8cccc519a9de13 Mon Sep 17 00:00:00 2001 From: Rui-Sun Date: Mon, 2 Dec 2024 21:31:03 +0800 Subject: [PATCH 36/40] fix: fix legend visible problem when reize table --- .../vtable/fix-legeng-resize_2024-12-02-13-30.json | 10 ++++++++++ .../legend/continue-legend/continue-legend.ts | 12 +++++++++++- .../legend/discrete-legend/discrete-legend.ts | 13 ++++++------- 3 files changed, 27 insertions(+), 8 deletions(-) create mode 100644 common/changes/@visactor/vtable/fix-legeng-resize_2024-12-02-13-30.json diff --git a/common/changes/@visactor/vtable/fix-legeng-resize_2024-12-02-13-30.json b/common/changes/@visactor/vtable/fix-legeng-resize_2024-12-02-13-30.json new file mode 100644 index 000000000..96482e957 --- /dev/null +++ b/common/changes/@visactor/vtable/fix-legeng-resize_2024-12-02-13-30.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@visactor/vtable", + "comment": "fix: fix legend visible problem when reize table", + "type": "none" + } + ], + "packageName": "@visactor/vtable" +} \ No newline at end of file diff --git a/packages/vtable/src/components/legend/continue-legend/continue-legend.ts b/packages/vtable/src/components/legend/continue-legend/continue-legend.ts index 9c0b09eb4..a8d790ea9 100644 --- a/packages/vtable/src/components/legend/continue-legend/continue-legend.ts +++ b/packages/vtable/src/components/legend/continue-legend/continue-legend.ts @@ -52,13 +52,20 @@ export class ContinueTableLegend { } legend.name = 'legend'; this.legendComponent = legend; + if (this.visible === false) { + legend.setAttributes({ + visible: false, + visibleAll: false + }); + legend.hideAll(); + } this.table.scenegraph.stage.defaultLayer.appendChild(legend); this.adjustTableSize(attrs); } resize() { - if (!this.legendComponent) { + if (!this.legendComponent || this.visible === false) { return; } @@ -71,6 +78,9 @@ export class ContinueTableLegend { } adjustTableSize(attrs: any) { + if (!this.legendComponent || this.visible === false) { + return; + } // 调整位置 let width = isFinite(this.legendComponent.AABBBounds.width()) ? this.legendComponent.AABBBounds.width() : 0; let height = isFinite(this.legendComponent.AABBBounds.height()) ? this.legendComponent.AABBBounds.height() : 0; diff --git a/packages/vtable/src/components/legend/discrete-legend/discrete-legend.ts b/packages/vtable/src/components/legend/discrete-legend/discrete-legend.ts index 9ab2bf14f..533145cc6 100644 --- a/packages/vtable/src/components/legend/discrete-legend/discrete-legend.ts +++ b/packages/vtable/src/components/legend/discrete-legend/discrete-legend.ts @@ -43,12 +43,8 @@ export class DiscreteTableLegend { this.legendComponent = legend; if (this.visible === false) { legend.setAttributes({ - maxWidth: 0, - width: 0, - maxHeight: 0, - height: 0, - clip: true - // visible: false + visible: false, + visibleAll: false }); legend.hideAll(); } @@ -58,7 +54,7 @@ export class DiscreteTableLegend { } resize() { - if (!this.legendComponent) { + if (!this.legendComponent || this.visible === false) { return; } @@ -71,6 +67,9 @@ export class DiscreteTableLegend { } adjustTableSize(attrs: any) { + if (!this.legendComponent || this.visible === false) { + return; + } // 调整位置 let width = isFinite(this.legendComponent.AABBBounds.width()) ? this.legendComponent.AABBBounds.width() : 0; let height = isFinite(this.legendComponent.AABBBounds.height()) ? this.legendComponent.AABBBounds.height() : 0; From c85b1a15bbd9904f871617dd6888dfa2822a3ff9 Mon Sep 17 00:00:00 2001 From: Rui-Sun Date: Tue, 3 Dec 2024 15:55:43 +0800 Subject: [PATCH 37/40] fix: fix merge radio cell check update #2881 --- .../vtable/fix-radio-update_2024-12-03-07-55.json | 10 ++++++++++ .../src/scenegraph/group-creater/cell-helper.ts | 3 ++- .../scenegraph/group-creater/cell-type/radio-cell.ts | 11 +++++++---- 3 files changed, 19 insertions(+), 5 deletions(-) create mode 100644 common/changes/@visactor/vtable/fix-radio-update_2024-12-03-07-55.json diff --git a/common/changes/@visactor/vtable/fix-radio-update_2024-12-03-07-55.json b/common/changes/@visactor/vtable/fix-radio-update_2024-12-03-07-55.json new file mode 100644 index 000000000..fba0c364f --- /dev/null +++ b/common/changes/@visactor/vtable/fix-radio-update_2024-12-03-07-55.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@visactor/vtable", + "comment": "fix: fix merge radio cell check update #2881", + "type": "none" + } + ], + "packageName": "@visactor/vtable" +} \ No newline at end of file diff --git a/packages/vtable/src/scenegraph/group-creater/cell-helper.ts b/packages/vtable/src/scenegraph/group-creater/cell-helper.ts index e3a6b0a0e..824eb379b 100644 --- a/packages/vtable/src/scenegraph/group-creater/cell-helper.ts +++ b/packages/vtable/src/scenegraph/group-creater/cell-helper.ts @@ -385,7 +385,8 @@ export function createCell( textBaseline, table, cellTheme, - define as RadioColumnDefine + define as RadioColumnDefine, + range ); } diff --git a/packages/vtable/src/scenegraph/group-creater/cell-type/radio-cell.ts b/packages/vtable/src/scenegraph/group-creater/cell-type/radio-cell.ts index e62c97f9a..c881ce397 100644 --- a/packages/vtable/src/scenegraph/group-creater/cell-type/radio-cell.ts +++ b/packages/vtable/src/scenegraph/group-creater/cell-type/radio-cell.ts @@ -1,6 +1,6 @@ import type { IThemeSpec } from '@src/vrender'; import { Group } from '../../graphic/group'; -import type { RadioColumnDefine, RadioStyleOption } from '../../../ts-types'; +import type { CellRange, RadioColumnDefine, RadioStyleOption } from '../../../ts-types'; import type { BaseTableAPI } from '../../../ts-types/base-table'; import { cos, isArray, isBoolean, isNumber, isObject, isValid, merge } from '@visactor/vutils'; import type { RadioAttributes } from '@visactor/vrender-components'; @@ -26,7 +26,8 @@ export function createRadioCellGroup( textBaseline: CanvasTextBaseline, table: BaseTableAPI, cellTheme: IThemeSpec, - define: RadioColumnDefine + define: RadioColumnDefine, + range: CellRange ) { // cell if (!cellGroup) { @@ -71,6 +72,7 @@ export function createRadioCellGroup( cellTheme, define, cellGroup, + range, table ); @@ -110,6 +112,7 @@ function createRadio( cellTheme: IThemeSpec, define: RadioColumnDefine, cellGroup: Group, + range: CellRange, table: BaseTableAPI ) { const style = table._getCellStyle(col, row) as RadioStyle; @@ -206,7 +209,7 @@ function createRadio( if (radioComponent) { cellGroup.appendChild(radioComponent); } - radioComponent.id = `radio-${col}-${row}-${index}`; + radioComponent.id = `radio-${range?.start.col ?? col}-${range?.start.row ?? row}-${index}`; radioComponent.render(); const bounds = radioComponent.AABBBounds; @@ -235,7 +238,7 @@ function createRadio( if (radioComponent) { cellGroup.appendChild(radioComponent); } - radioComponent.id = `radio-${col}-${row}`; + radioComponent.id = `radio-${range?.start.col ?? col}-${range?.start.row ?? row}`; radioComponent.render(); const bounds = radioComponent.AABBBounds; width = bounds.width(); From 61578b7154de1cb271ed62454a7f8336b3a03e09 Mon Sep 17 00:00:00 2001 From: Rui-Sun Date: Tue, 3 Dec 2024 17:19:24 +0800 Subject: [PATCH 38/40] fix: fix cache problem in Icon.loadGif() #2905 --- .../vtable/fix-load-gif_2024-12-03-09-19.json | 10 ++++++++++ packages/vtable/src/scenegraph/graphic/icon.ts | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 common/changes/@visactor/vtable/fix-load-gif_2024-12-03-09-19.json diff --git a/common/changes/@visactor/vtable/fix-load-gif_2024-12-03-09-19.json b/common/changes/@visactor/vtable/fix-load-gif_2024-12-03-09-19.json new file mode 100644 index 000000000..9f356fd82 --- /dev/null +++ b/common/changes/@visactor/vtable/fix-load-gif_2024-12-03-09-19.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@visactor/vtable", + "comment": "fix: fix cache problem in Icon.loadGif() #2905", + "type": "none" + } + ], + "packageName": "@visactor/vtable" +} \ No newline at end of file diff --git a/packages/vtable/src/scenegraph/graphic/icon.ts b/packages/vtable/src/scenegraph/graphic/icon.ts index e77ae3a8e..731240e9c 100644 --- a/packages/vtable/src/scenegraph/graphic/icon.ts +++ b/packages/vtable/src/scenegraph/graphic/icon.ts @@ -57,7 +57,7 @@ export class Icon extends Image { loadGif() { this.playing = false; - ResourceLoader.GetFile((this.attribute as any).gif, 'arrayBuffer') + ResourceLoader.GetFile((this.attribute as any).gif + '?role=gif', 'arrayBuffer') // ?role=gif: hack for ResourceLoader cache .then((res: ArrayBuffer) => { const gif = parseGIF(res); const frames = decompressFrames(gif, true); From d74e7cd1ef8dd561c8fd36e9416c2e4a31685ea4 Mon Sep 17 00:00:00 2001 From: Rui-Sun Date: Tue, 3 Dec 2024 16:44:41 +0800 Subject: [PATCH 39/40] fix: fix default row height in computeRowHeight() #2903 --- ...ix-default-row-height-compute_2024-12-03-08-44.json | 10 ++++++++++ .../vtable/src/scenegraph/layout/compute-row-height.ts | 7 +++++++ 2 files changed, 17 insertions(+) create mode 100644 common/changes/@visactor/vtable/fix-default-row-height-compute_2024-12-03-08-44.json diff --git a/common/changes/@visactor/vtable/fix-default-row-height-compute_2024-12-03-08-44.json b/common/changes/@visactor/vtable/fix-default-row-height-compute_2024-12-03-08-44.json new file mode 100644 index 000000000..2e8b307df --- /dev/null +++ b/common/changes/@visactor/vtable/fix-default-row-height-compute_2024-12-03-08-44.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@visactor/vtable", + "comment": "fix: fix default row height in computeRowHeight() #2903", + "type": "none" + } + ], + "packageName": "@visactor/vtable" +} \ No newline at end of file diff --git a/packages/vtable/src/scenegraph/layout/compute-row-height.ts b/packages/vtable/src/scenegraph/layout/compute-row-height.ts index 71d22a889..ae5af2347 100644 --- a/packages/vtable/src/scenegraph/layout/compute-row-height.ts +++ b/packages/vtable/src/scenegraph/layout/compute-row-height.ts @@ -349,6 +349,13 @@ export function computeRowsHeight( } export function computeRowHeight(row: number, startCol: number, endCol: number, table: BaseTableAPI): number { + const isAllRowsAuto = + table.heightMode === 'autoHeight' || + (table.heightMode === 'adaptive' && table.options.autoHeightInAdaptiveMode !== false); + if (!isAllRowsAuto && table.getDefaultRowHeight(row) !== 'auto') { + return table.getDefaultRowHeight(row) as number; + } + let maxHeight; if ((table.options as ListTableConstructorOptions).customComputeRowHeight) { return (table.options as ListTableConstructorOptions).customComputeRowHeight({ row, table: table as ListTableAPI }); From 1248bf7b418b42180fa0f8e824d4ef2f19243c7c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 3 Dec 2024 11:16:26 +0000 Subject: [PATCH 40/40] build: prelease version 1.12.0 --- ...upport-table-groupby_2024-11-19-09-05.json | 11 ----- ...ntt-taskbar-position_2024-11-29-07-40.json | 11 ----- ...dit-createLink-error_2024-11-29-10-32.json | 11 ----- ...ure-add-taskShowMode_2024-11-29-10-32.json | 11 ----- .../vtable/feat-fs-bs_2024-12-02-08-23.json | 10 ---- .../vtable/feat-fs-bs_2024-12-02-08-27.json | 10 ---- ...tom-component-update_2024-12-02-09-42.json | 10 ---- ...t-row-height-compute_2024-12-03-08-44.json | 10 ---- .../fix-legeng-resize_2024-12-02-13-30.json | 10 ---- .../vtable/fix-load-gif_2024-12-03-09-19.json | 10 ---- .../fix-radio-update_2024-12-03-07-55.json | 10 ---- ...okeArrayWidth-update_2024-12-02-12-11.json | 10 ---- common/config/rush/version-policies.json | 2 +- packages/openinula-vtable/package.json | 2 +- packages/react-vtable/package.json | 2 +- packages/vtable-calendar/package.json | 2 +- packages/vtable-editors/package.json | 2 +- packages/vtable-export/package.json | 2 +- packages/vtable-gantt/package.json | 4 +- packages/vtable-plugins/package.json | 4 +- packages/vtable-search/package.json | 2 +- packages/vtable/CHANGELOG.json | 47 +++++++++++++++++++ packages/vtable/CHANGELOG.md | 31 +++++++++++- packages/vtable/package.json | 2 +- packages/vue-vtable/package.json | 2 +- 25 files changed, 90 insertions(+), 138 deletions(-) delete mode 100644 common/changes/@visactor/vtable/2829-feature-gantt-chart-support-table-groupby_2024-11-19-09-05.json delete mode 100644 common/changes/@visactor/vtable/2920-feature-vtable-gantt-taskbar-position_2024-11-29-07-40.json delete mode 100644 common/changes/@visactor/vtable/2938-bug-edit-createLink-error_2024-11-29-10-32.json delete mode 100644 common/changes/@visactor/vtable/2949-feature-add-taskShowMode_2024-11-29-10-32.json delete mode 100644 common/changes/@visactor/vtable/feat-fs-bs_2024-12-02-08-23.json delete mode 100644 common/changes/@visactor/vtable/feat-fs-bs_2024-12-02-08-27.json delete mode 100644 common/changes/@visactor/vtable/fix-custom-component-update_2024-12-02-09-42.json delete mode 100644 common/changes/@visactor/vtable/fix-default-row-height-compute_2024-12-03-08-44.json delete mode 100644 common/changes/@visactor/vtable/fix-legeng-resize_2024-12-02-13-30.json delete mode 100644 common/changes/@visactor/vtable/fix-load-gif_2024-12-03-09-19.json delete mode 100644 common/changes/@visactor/vtable/fix-radio-update_2024-12-03-07-55.json delete mode 100644 common/changes/@visactor/vtable/fix-strokeArrayWidth-update_2024-12-02-12-11.json diff --git a/common/changes/@visactor/vtable/2829-feature-gantt-chart-support-table-groupby_2024-11-19-09-05.json b/common/changes/@visactor/vtable/2829-feature-gantt-chart-support-table-groupby_2024-11-19-09-05.json deleted file mode 100644 index 9c4f9fdb2..000000000 --- a/common/changes/@visactor/vtable/2829-feature-gantt-chart-support-table-groupby_2024-11-19-09-05.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "changes": [ - { - "comment": "feat: add option customComputeRowHeight and defaultRowHeight can set \"auto\"\n\n", - "type": "none", - "packageName": "@visactor/vtable" - } - ], - "packageName": "@visactor/vtable", - "email": "892739385@qq.com" -} \ No newline at end of file diff --git a/common/changes/@visactor/vtable/2920-feature-vtable-gantt-taskbar-position_2024-11-29-07-40.json b/common/changes/@visactor/vtable/2920-feature-vtable-gantt-taskbar-position_2024-11-29-07-40.json deleted file mode 100644 index 772948e96..000000000 --- a/common/changes/@visactor/vtable/2920-feature-vtable-gantt-taskbar-position_2024-11-29-07-40.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "changes": [ - { - "comment": "docs: add getTaskBarRelativeRect api #2920\n\n", - "type": "none", - "packageName": "@visactor/vtable" - } - ], - "packageName": "@visactor/vtable", - "email": "892739385@qq.com" -} \ No newline at end of file diff --git a/common/changes/@visactor/vtable/2938-bug-edit-createLink-error_2024-11-29-10-32.json b/common/changes/@visactor/vtable/2938-bug-edit-createLink-error_2024-11-29-10-32.json deleted file mode 100644 index 82a94d0bd..000000000 --- a/common/changes/@visactor/vtable/2938-bug-edit-createLink-error_2024-11-29-10-32.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "changes": [ - { - "comment": "fix: when edit record task date update taskbar occor error #2938\n\n", - "type": "none", - "packageName": "@visactor/vtable" - } - ], - "packageName": "@visactor/vtable", - "email": "892739385@qq.com" -} \ No newline at end of file diff --git a/common/changes/@visactor/vtable/2949-feature-add-taskShowMode_2024-11-29-10-32.json b/common/changes/@visactor/vtable/2949-feature-add-taskShowMode_2024-11-29-10-32.json deleted file mode 100644 index 26dac1513..000000000 --- a/common/changes/@visactor/vtable/2949-feature-add-taskShowMode_2024-11-29-10-32.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "changes": [ - { - "comment": "feat: add taskShowMode for gantt chart #2849\n\n", - "type": "minor", - "packageName": "@visactor/vtable" - } - ], - "packageName": "@visactor/vtable", - "email": "892739385@qq.com" -} \ No newline at end of file diff --git a/common/changes/@visactor/vtable/feat-fs-bs_2024-12-02-08-23.json b/common/changes/@visactor/vtable/feat-fs-bs_2024-12-02-08-23.json deleted file mode 100644 index d47990475..000000000 --- a/common/changes/@visactor/vtable/feat-fs-bs_2024-12-02-08-23.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "changes": [ - { - "packageName": "@visactor/vtable", - "comment": "fix: fix last col&row editor size #2926", - "type": "none" - } - ], - "packageName": "@visactor/vtable" -} \ No newline at end of file diff --git a/common/changes/@visactor/vtable/feat-fs-bs_2024-12-02-08-27.json b/common/changes/@visactor/vtable/feat-fs-bs_2024-12-02-08-27.json deleted file mode 100644 index c2c2dd544..000000000 --- a/common/changes/@visactor/vtable/feat-fs-bs_2024-12-02-08-27.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "changes": [ - { - "packageName": "@visactor/vtable", - "comment": "fix: fix cell update event problem in CustomCellStylePlugin #2927", - "type": "none" - } - ], - "packageName": "@visactor/vtable" -} \ No newline at end of file diff --git a/common/changes/@visactor/vtable/fix-custom-component-update_2024-12-02-09-42.json b/common/changes/@visactor/vtable/fix-custom-component-update_2024-12-02-09-42.json deleted file mode 100644 index 3c84e625c..000000000 --- a/common/changes/@visactor/vtable/fix-custom-component-update_2024-12-02-09-42.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "changes": [ - { - "packageName": "@visactor/vtable", - "comment": "fix: fix react-component in tree mode update", - "type": "none" - } - ], - "packageName": "@visactor/vtable" -} \ No newline at end of file diff --git a/common/changes/@visactor/vtable/fix-default-row-height-compute_2024-12-03-08-44.json b/common/changes/@visactor/vtable/fix-default-row-height-compute_2024-12-03-08-44.json deleted file mode 100644 index 2e8b307df..000000000 --- a/common/changes/@visactor/vtable/fix-default-row-height-compute_2024-12-03-08-44.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "changes": [ - { - "packageName": "@visactor/vtable", - "comment": "fix: fix default row height in computeRowHeight() #2903", - "type": "none" - } - ], - "packageName": "@visactor/vtable" -} \ No newline at end of file diff --git a/common/changes/@visactor/vtable/fix-legeng-resize_2024-12-02-13-30.json b/common/changes/@visactor/vtable/fix-legeng-resize_2024-12-02-13-30.json deleted file mode 100644 index 96482e957..000000000 --- a/common/changes/@visactor/vtable/fix-legeng-resize_2024-12-02-13-30.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "changes": [ - { - "packageName": "@visactor/vtable", - "comment": "fix: fix legend visible problem when reize table", - "type": "none" - } - ], - "packageName": "@visactor/vtable" -} \ No newline at end of file diff --git a/common/changes/@visactor/vtable/fix-load-gif_2024-12-03-09-19.json b/common/changes/@visactor/vtable/fix-load-gif_2024-12-03-09-19.json deleted file mode 100644 index 9f356fd82..000000000 --- a/common/changes/@visactor/vtable/fix-load-gif_2024-12-03-09-19.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "changes": [ - { - "packageName": "@visactor/vtable", - "comment": "fix: fix cache problem in Icon.loadGif() #2905", - "type": "none" - } - ], - "packageName": "@visactor/vtable" -} \ No newline at end of file diff --git a/common/changes/@visactor/vtable/fix-radio-update_2024-12-03-07-55.json b/common/changes/@visactor/vtable/fix-radio-update_2024-12-03-07-55.json deleted file mode 100644 index fba0c364f..000000000 --- a/common/changes/@visactor/vtable/fix-radio-update_2024-12-03-07-55.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "changes": [ - { - "packageName": "@visactor/vtable", - "comment": "fix: fix merge radio cell check update #2881", - "type": "none" - } - ], - "packageName": "@visactor/vtable" -} \ No newline at end of file diff --git a/common/changes/@visactor/vtable/fix-strokeArrayWidth-update_2024-12-02-12-11.json b/common/changes/@visactor/vtable/fix-strokeArrayWidth-update_2024-12-02-12-11.json deleted file mode 100644 index 0c349dc49..000000000 --- a/common/changes/@visactor/vtable/fix-strokeArrayWidth-update_2024-12-02-12-11.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "changes": [ - { - "packageName": "@visactor/vtable", - "comment": "fix: fix strokeArrayWidth update in updateCell() #2811", - "type": "none" - } - ], - "packageName": "@visactor/vtable" -} \ No newline at end of file diff --git a/common/config/rush/version-policies.json b/common/config/rush/version-policies.json index c32d6d378..64d3345a6 100644 --- a/common/config/rush/version-policies.json +++ b/common/config/rush/version-policies.json @@ -1 +1 @@ -[{"definitionName":"lockStepVersion","policyName":"vtableMain","version":"1.11.5","mainProject":"@visactor/vtable","nextBump":"patch"}] +[{"definitionName":"lockStepVersion","policyName":"vtableMain","version":"1.12.0","mainProject":"@visactor/vtable","nextBump":"minor"}] diff --git a/packages/openinula-vtable/package.json b/packages/openinula-vtable/package.json index 6d06cae46..89423b482 100644 --- a/packages/openinula-vtable/package.json +++ b/packages/openinula-vtable/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/openinula-vtable", - "version": "1.11.5", + "version": "1.12.0", "description": "The openinula version of VTable", "keywords": [ "openinula", diff --git a/packages/react-vtable/package.json b/packages/react-vtable/package.json index 76dd1dba8..b66669dfe 100644 --- a/packages/react-vtable/package.json +++ b/packages/react-vtable/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/react-vtable", - "version": "1.11.5", + "version": "1.12.0", "description": "The react version of VTable", "keywords": [ "react", diff --git a/packages/vtable-calendar/package.json b/packages/vtable-calendar/package.json index 58688a381..abd18bb91 100644 --- a/packages/vtable-calendar/package.json +++ b/packages/vtable-calendar/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/vtable-calendar", - "version": "1.11.5", + "version": "1.12.0", "description": "The calendar component of VTable", "author": { "name": "VisActor", diff --git a/packages/vtable-editors/package.json b/packages/vtable-editors/package.json index 28b47420d..9c5fcfbee 100644 --- a/packages/vtable-editors/package.json +++ b/packages/vtable-editors/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/vtable-editors", - "version": "1.11.5", + "version": "1.12.0", "description": "", "sideEffects": false, "main": "cjs/index.js", diff --git a/packages/vtable-export/package.json b/packages/vtable-export/package.json index c0493ad38..7555877ed 100644 --- a/packages/vtable-export/package.json +++ b/packages/vtable-export/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/vtable-export", - "version": "1.11.5", + "version": "1.12.0", "description": "The export util of VTable", "author": { "name": "VisActor", diff --git a/packages/vtable-gantt/package.json b/packages/vtable-gantt/package.json index 900ae670b..cd2554a0a 100644 --- a/packages/vtable-gantt/package.json +++ b/packages/vtable-gantt/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/vtable-gantt", - "version": "1.11.5", + "version": "1.12.0", "description": "canvas table width high performance", "keywords": [ "vtable-gantt", @@ -120,4 +120,4 @@ "url": "https://github.com/VisActor/VTable.git", "directory": "packages/vtable-gantt" } -} \ No newline at end of file +} diff --git a/packages/vtable-plugins/package.json b/packages/vtable-plugins/package.json index 34db1862c..134f7250e 100644 --- a/packages/vtable-plugins/package.json +++ b/packages/vtable-plugins/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/vtable-plugins", - "version": "1.11.5", + "version": "1.12.0", "description": "The search util of VTable", "author": { "name": "VisActor", @@ -87,4 +87,4 @@ "@types/react-is": "^17.0.3", "rollup-plugin-node-resolve": "5.2.0" } -} \ No newline at end of file +} diff --git a/packages/vtable-search/package.json b/packages/vtable-search/package.json index c4a7e0df9..d65b10ee5 100644 --- a/packages/vtable-search/package.json +++ b/packages/vtable-search/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/vtable-search", - "version": "1.11.5", + "version": "1.12.0", "description": "The search util of VTable", "author": { "name": "VisActor", diff --git a/packages/vtable/CHANGELOG.json b/packages/vtable/CHANGELOG.json index 5146c79e0..ae1f44eff 100644 --- a/packages/vtable/CHANGELOG.json +++ b/packages/vtable/CHANGELOG.json @@ -1,6 +1,53 @@ { "name": "@visactor/vtable", "entries": [ + { + "version": "1.12.0", + "tag": "@visactor/vtable_v1.12.0", + "date": "Tue, 03 Dec 2024 11:06:35 GMT", + "comments": { + "none": [ + { + "comment": "feat: add option customComputeRowHeight and defaultRowHeight can set \"auto\"\n\n" + }, + { + "comment": "docs: add getTaskBarRelativeRect api #2920\n\n" + }, + { + "comment": "fix: when edit record task date update taskbar occor error #2938\n\n" + }, + { + "comment": "fix: fix last col&row editor size #2926" + }, + { + "comment": "fix: fix cell update event problem in CustomCellStylePlugin #2927" + }, + { + "comment": "fix: fix react-component in tree mode update" + }, + { + "comment": "fix: fix default row height in computeRowHeight() #2903" + }, + { + "comment": "fix: fix legend visible problem when reize table" + }, + { + "comment": "fix: fix cache problem in Icon.loadGif() #2905" + }, + { + "comment": "fix: fix merge radio cell check update #2881" + }, + { + "comment": "fix: fix strokeArrayWidth update in updateCell() #2811" + } + ], + "minor": [ + { + "comment": "feat: add taskShowMode for gantt chart #2849\n\n" + } + ] + } + }, { "version": "1.11.5", "tag": "@visactor/vtable_v1.11.5", diff --git a/packages/vtable/CHANGELOG.md b/packages/vtable/CHANGELOG.md index 39b3be65b..7c0aa571f 100644 --- a/packages/vtable/CHANGELOG.md +++ b/packages/vtable/CHANGELOG.md @@ -1,6 +1,35 @@ # Change Log - @visactor/vtable -This log was last generated on Fri, 29 Nov 2024 07:59:16 GMT and should not be manually modified. +This log was last generated on Tue, 03 Dec 2024 11:06:35 GMT and should not be manually modified. + +## 1.12.0 +Tue, 03 Dec 2024 11:06:35 GMT + +### Minor changes + +- feat: add taskShowMode for gantt chart #2849 + + + +### Updates + +- feat: add option customComputeRowHeight and defaultRowHeight can set "auto" + + +- docs: add getTaskBarRelativeRect api #2920 + + +- fix: when edit record task date update taskbar occor error #2938 + + +- fix: fix last col&row editor size #2926 +- fix: fix cell update event problem in CustomCellStylePlugin #2927 +- fix: fix react-component in tree mode update +- fix: fix default row height in computeRowHeight() #2903 +- fix: fix legend visible problem when reize table +- fix: fix cache problem in Icon.loadGif() #2905 +- fix: fix merge radio cell check update #2881 +- fix: fix strokeArrayWidth update in updateCell() #2811 ## 1.11.5 Fri, 29 Nov 2024 07:59:16 GMT diff --git a/packages/vtable/package.json b/packages/vtable/package.json index dda2ceb19..de46d6276 100644 --- a/packages/vtable/package.json +++ b/packages/vtable/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/vtable", - "version": "1.11.5", + "version": "1.12.0", "description": "canvas table width high performance", "keywords": [ "grid", diff --git a/packages/vue-vtable/package.json b/packages/vue-vtable/package.json index a4689e510..589914468 100644 --- a/packages/vue-vtable/package.json +++ b/packages/vue-vtable/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/vue-vtable", - "version": "1.11.5", + "version": "1.12.0", "description": "The vue version of VTable", "keywords": [ "vue",