ct-gantt-core 1.0.6 → 1.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +43 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +45 -3
- package/dist/index.d.ts +45 -3
- package/dist/index.js +39 -8
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -21,8 +21,12 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
DAY_MS: () => DAY_MS,
|
|
24
|
+
DEFAULT_HOUR_COLORS: () => DEFAULT_HOUR_COLORS,
|
|
25
|
+
DEFAULT_TASK_COLORS: () => DEFAULT_TASK_COLORS,
|
|
26
|
+
EDITOR_COLOR_OPTIONS: () => EDITOR_COLOR_OPTIONS,
|
|
24
27
|
GanttEngine: () => GanttEngine,
|
|
25
28
|
HOUR_MS: () => HOUR_MS,
|
|
29
|
+
OWNER_COLOR_PALETTE: () => OWNER_COLOR_PALETTE,
|
|
26
30
|
RESOURCE_COLOR_PALETTE: () => RESOURCE_COLOR_PALETTE,
|
|
27
31
|
addDays: () => addDays,
|
|
28
32
|
addHours: () => addHours,
|
|
@@ -56,6 +60,40 @@ __export(index_exports, {
|
|
|
56
60
|
});
|
|
57
61
|
module.exports = __toCommonJS(index_exports);
|
|
58
62
|
|
|
63
|
+
// src/styles/colors.ts
|
|
64
|
+
var DEFAULT_TASK_COLORS = {
|
|
65
|
+
task: "#4d77b3",
|
|
66
|
+
// 普通任务实际条
|
|
67
|
+
summary: "#475467",
|
|
68
|
+
// 阶段条
|
|
69
|
+
milestone: "#b57e4a",
|
|
70
|
+
// 里程碑菱形 / 参考线默认
|
|
71
|
+
plan: "#cbd5e1",
|
|
72
|
+
// 计划条外部底色
|
|
73
|
+
progress: "#367d73"
|
|
74
|
+
// 计划条内部进度色(任务未设置 planColor 时)
|
|
75
|
+
};
|
|
76
|
+
var DEFAULT_HOUR_COLORS = {
|
|
77
|
+
plan: "#cbd5e1",
|
|
78
|
+
// 计划条外部底色
|
|
79
|
+
progress: "#367d73",
|
|
80
|
+
// 计划条内部进度色(任务未设置 planColor 时)
|
|
81
|
+
milestone: "#b57e4a"
|
|
82
|
+
// 里程碑菱形 / 参考线默认
|
|
83
|
+
};
|
|
84
|
+
var RESOURCE_COLOR_PALETTE = ["#f10755", "#9b87c4", "#509b81", "#c18f67", "#5e7fb0"];
|
|
85
|
+
var EDITOR_COLOR_OPTIONS = [
|
|
86
|
+
"#4d77b3",
|
|
87
|
+
"#8f77c5",
|
|
88
|
+
"#c16794",
|
|
89
|
+
"#b57e4a",
|
|
90
|
+
"#b3a24d",
|
|
91
|
+
"#47ae8f",
|
|
92
|
+
"#3f99a6",
|
|
93
|
+
"#64748b"
|
|
94
|
+
];
|
|
95
|
+
var OWNER_COLOR_PALETTE = ["#c2708b", "#9b87c4", "#509b81", "#c18f67", "#5e7fb0"];
|
|
96
|
+
|
|
59
97
|
// src/types/index.ts
|
|
60
98
|
var defaultConfig = {
|
|
61
99
|
viewMode: "month",
|
|
@@ -76,13 +114,7 @@ var defaultConfig = {
|
|
|
76
114
|
enableLinkCreation: true,
|
|
77
115
|
showLinkRejectionNotice: true,
|
|
78
116
|
virtualScroll: true,
|
|
79
|
-
taskColors: {
|
|
80
|
-
task: "#2563eb",
|
|
81
|
-
summary: "#475467",
|
|
82
|
-
milestone: "#d97706",
|
|
83
|
-
plan: "#cbd5e1",
|
|
84
|
-
progress: "#0f766e"
|
|
85
|
-
},
|
|
117
|
+
taskColors: { ...DEFAULT_TASK_COLORS },
|
|
86
118
|
timeUnit: "day",
|
|
87
119
|
hourWidth: 12,
|
|
88
120
|
autoSchedule: true,
|
|
@@ -621,7 +653,6 @@ function computeImpact(taskId, patch, tasks, links, config) {
|
|
|
621
653
|
}
|
|
622
654
|
|
|
623
655
|
// src/engines/computeResourceLayout.ts
|
|
624
|
-
var RESOURCE_COLOR_PALETTE = ["#d85f87", "#9b63d9", "#47b987", "#d58a5f", "#4f83d8"];
|
|
625
656
|
function colorHash(source) {
|
|
626
657
|
return [...source].reduce((sum, char) => sum + (char.codePointAt(0) ?? 0), 0);
|
|
627
658
|
}
|
|
@@ -1136,8 +1167,12 @@ function createGanttEngine(options = {}) {
|
|
|
1136
1167
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1137
1168
|
0 && (module.exports = {
|
|
1138
1169
|
DAY_MS,
|
|
1170
|
+
DEFAULT_HOUR_COLORS,
|
|
1171
|
+
DEFAULT_TASK_COLORS,
|
|
1172
|
+
EDITOR_COLOR_OPTIONS,
|
|
1139
1173
|
GanttEngine,
|
|
1140
1174
|
HOUR_MS,
|
|
1175
|
+
OWNER_COLOR_PALETTE,
|
|
1141
1176
|
RESOURCE_COLOR_PALETTE,
|
|
1142
1177
|
addDays,
|
|
1143
1178
|
addHours,
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/types/index.ts","../src/services/checkCyclicDependency.ts","../src/services/normalizeLinks.ts","../src/services/flattenTasks.ts","../src/utils/date.ts","../src/services/computeTimeScale.ts","../src/services/computeHourScale.ts","../src/engines/scheduling.ts","../src/engines/computeLayout.ts","../src/engines/computeImpact.ts","../src/engines/computeResourceLayout.ts","../src/engine/GanttEngine.ts"],"sourcesContent":["export * from \"./types\"\r\nexport * from \"./services/checkCyclicDependency\"\r\nexport * from \"./services/normalizeLinks\"\r\nexport * from \"./services/flattenTasks\"\r\nexport * from \"./services/computeTimeScale\"\r\nexport * from \"./services/computeHourScale\"\r\nexport * from \"./engines/computeLayout\"\r\nexport * from \"./engines/computeImpact\"\r\nexport * from \"./engines/computeResourceLayout\"\r\nexport * from \"./engines/scheduling\"\r\nexport * from \"./utils/date\"\r\nexport * from \"./engine/GanttEngine\"\r\n","export type ViewMode = \"day\" | \"week\" | \"month\" | \"quarter\" | \"year\"\r\n\r\nexport type LinkType = \"FS\" | \"SS\" | \"FF\" | \"SF\"\r\nexport type LagUnit = \"calendar\" | \"working\"\r\n\r\nexport type Result<T> =\r\n | { ok: true; data: T }\r\n | { ok: false; error: GanttError }\r\n\r\nexport interface GanttError {\r\n code:\r\n | \"CYCLE_DEPENDENCY\"\r\n | \"INVALID_DATE\"\r\n | \"MISSING_TASK\"\r\n | \"ORPHAN_DEPENDENCY\"\r\n | \"INVALID_DEPENDENCY\"\r\n message: string\r\n details?: unknown\r\n}\r\n\r\nexport interface DateRange {\r\n start: string | Date\r\n end: string | Date\r\n progress?: number\r\n}\r\n\r\nexport interface Dependency {\r\n id?: string\r\n predecessorId: string\r\n type: LinkType\r\n lag: number\r\n lagUnit: LagUnit\r\n}\r\n\r\nexport interface GanttTask {\r\n id: string\r\n name: string\r\n type: \"task\" | \"milestone\" | \"summary\"\r\n plan: DateRange\r\n actual: Required<DateRange>\r\n dependencies?: Dependency[]\r\n parentId?: string | null\r\n color?: string\r\n planColor?: string\r\n calendarId?: string\r\n resources?: string[]\r\n segments?: Array<{ start: string | Date; end: string | Date }>\r\n constraint?: {\r\n type: \"SNET\" | \"SNLT\" | \"MSO\" | \"MFO\" | \"ASAP\" | \"ALAP\"\r\n date?: string | Date\r\n }\r\n schedulingMode?: \"auto\" | \"manual\"\r\n duration?: number\r\n custom?: Record<string, unknown>\r\n}\r\n\r\nexport interface GanttMarker {\r\n id: string\r\n name: string\r\n date: string | Date\r\n color?: string\r\n}\r\n\r\nexport interface GanttEditorField {\r\n key: string\r\n label: string\r\n visible?: boolean\r\n editable?: boolean\r\n type?: \"text\" | \"number\" | \"date\" | \"select\"\r\n options?: Array<{ label: string; value: string | number }>\r\n placeholder?: string\r\n}\r\n\r\nexport interface CustomColumn {\r\n key: string\r\n label: string\r\n width?: number\r\n visible?: boolean\r\n align?: \"left\" | \"center\" | \"right\"\r\n editable?: boolean\r\n type?: \"text\" | \"number\" | \"date\" | \"select\"\r\n options?: Array<{ label: string; value: string | number }>\r\n placeholder?: string\r\n editor?: Omit<GanttEditorField, \"key\" | \"label\">\r\n formatter?: (value: unknown, task: GanttTask) => string\r\n cellClass?: string | ((task: GanttTask) => string)\r\n cellStyle?: Record<string, string | number> | ((task: GanttTask) => Record<string, string | number>)\r\n render?: (task: GanttTask) => { text: string; className?: string }\r\n}\r\n\r\nexport interface GanttConfig {\r\n viewMode: ViewMode\r\n rowHeight: number\r\n columnWidth: number\r\n headerHeight: number\r\n taskListWidth: number\r\n width?: string | number\r\n height?: string | number\r\n locale: string\r\n firstDayOfWeek: 0 | 1\r\n dateFormat: string\r\n customColumns?: CustomColumn[]\r\n columns?: CustomColumn[]\r\n editorFields?: GanttEditorField[]\r\n columnWidths?: Partial<Record<ViewMode, number>>\r\n theme?: \"light\" | \"dark\" | string\r\n visibleRange?: {\r\n start: string | Date\r\n end: string | Date\r\n }\r\n showPlanBar?: boolean\r\n showActualBar?: boolean\r\n showTimelineWhenEmpty?: boolean\r\n builtInTaskEditor?: boolean\r\n builtInMarkerEditor?: boolean\r\n editablePlan?: boolean\r\n editableActual?: boolean\r\n enableLinkCreation?: boolean\r\n showLinkRejectionNotice?: boolean\r\n virtualScroll?: boolean\r\n taskColors?: {\r\n task?: string\r\n summary?: string\r\n milestone?: string\r\n plan?: string\r\n progress?: string\r\n }\r\n resourceColors?: Record<string, string>\r\n /** 资源视图时间粒度;day 按整天渲染,hour 按占用携带的小时渲染(仅 ResourceTimeline 使用) */\r\n timeUnit?: \"day\" | \"hour\"\r\n /** 资源视图 hour 粒度下每小时像素宽 */\r\n hourWidth?: number\r\n autoSchedule?: boolean\r\n editable?: boolean\r\n}\r\n\r\nexport interface GanttLink {\r\n id: string\r\n sourceId: string\r\n targetId: string\r\n type: LinkType\r\n lag?: number\r\n lagUnit?: LagUnit\r\n}\r\n\r\nexport interface TaskLayout {\r\n taskId: string\r\n rowIndex: number\r\n left: number\r\n width: number\r\n top: number\r\n depth: number\r\n isCritical: boolean\r\n}\r\n\r\n/** 资源(会议室、负责人等),资源视图的 Y 轴主体 */\r\nexport interface Resource {\r\n id: string\r\n name: string\r\n color?: string\r\n custom?: Record<string, unknown>\r\n}\r\n\r\nexport type ResourceUsageType = \"booking\" | \"assignment\"\r\n\r\n/** 资源在某时间段的占用(会议室预订、负责人承接的项目等) */\r\nexport interface ResourceUsage {\r\n id: string\r\n resourceId: string\r\n /** 日历日粒度,与 GanttTask 一致;同一天内的时段(如 9:00-11:00)按当天整格渲染,时刻体现在 label/tooltip */\r\n start: string | Date\r\n end: string | Date\r\n label?: string\r\n color?: string\r\n type?: ResourceUsageType\r\n custom?: Record<string, unknown>\r\n}\r\n\r\nexport interface ResourceRowLayout {\r\n resourceId: string\r\n rowIndex: number\r\n /** 行内占用车道数(同时段重叠数),>1 时条需要堆叠变薄 */\r\n laneCount: number\r\n color: string\r\n usageIds: string[]\r\n}\r\n\r\nexport interface ResourceUsageLayout {\r\n usageId: string\r\n resourceId: string\r\n rowIndex: number\r\n laneIndex: number\r\n laneCount: number\r\n left: number\r\n width: number\r\n /** 绝对 top(含 headerHeight + rowIndex*rowHeight + 行内 lane 偏移) */\r\n top: number\r\n /** 条高度,由 laneCount 与 rowHeight 决定 */\r\n height: number\r\n}\r\n\r\nexport interface ResourceLayout {\r\n rows: ResourceRowLayout[]\r\n usages: ResourceUsageLayout[]\r\n}\r\n\r\nexport interface FlatTask {\r\n task: GanttTask\r\n depth: number\r\n rowIndex: number\r\n hasChildren: boolean\r\n collapsed: boolean\r\n}\r\n\r\nexport interface Calendar {\r\n id: string\r\n workingDays: number[]\r\n holidays: string[]\r\n hours: [number, number][]\r\n}\r\n\r\nexport interface TimeScale {\r\n start: Date\r\n end: Date\r\n left: number\r\n width: number\r\n}\r\n\r\nexport interface Viewport {\r\n scrollTop: number\r\n scrollLeft: number\r\n clientWidth: number\r\n clientHeight: number\r\n dpr: number\r\n}\r\n\r\nexport interface AffectedTasks {\r\n changed: Array<{\r\n taskId: string\r\n actualStart: string | Date\r\n actualEnd: string | Date\r\n rowIndex: number\r\n }>\r\n conflicts: Array<{\r\n taskId: string\r\n constraintType: string\r\n message: string\r\n }>\r\n}\r\n\r\nexport interface PatchTask {\r\n planStart?: string | Date\r\n planEnd?: string | Date\r\n actualStart?: string | Date\r\n actualEnd?: string | Date\r\n progress?: number\r\n name?: string\r\n type?: GanttTask[\"type\"]\r\n parentId?: string | null\r\n color?: string\r\n planColor?: string\r\n duration?: number\r\n schedulingMode?: \"auto\" | \"manual\"\r\n resources?: string[]\r\n calendarId?: string\r\n custom?: Record<string, unknown>\r\n}\r\n\r\nexport const defaultConfig: GanttConfig = {\r\n viewMode: \"month\",\r\n rowHeight: 44,\r\n columnWidth: 30,\r\n headerHeight: 50,\r\n taskListWidth: 280,\r\n locale: \"zh-CN\",\r\n firstDayOfWeek: 0,\r\n dateFormat: \"YYYY-MM-DD\",\r\n showPlanBar: true,\r\n showActualBar: true,\r\n showTimelineWhenEmpty: false,\r\n builtInTaskEditor: true,\r\n builtInMarkerEditor: true,\r\n editablePlan: false,\r\n editableActual: true,\r\n enableLinkCreation: true,\r\n showLinkRejectionNotice: true,\r\n virtualScroll: true,\r\n taskColors: {\r\n task: \"#2563eb\",\r\n summary: \"#475467\",\r\n milestone: \"#d97706\",\r\n plan: \"#cbd5e1\",\r\n progress: \"#0f766e\"\r\n },\r\n timeUnit: \"day\",\r\n hourWidth: 12,\r\n autoSchedule: true,\r\n editable: true\r\n}\r\n","import type { GanttLink } from \"../types\"\r\n\r\nexport function checkCyclicDependency(\r\n links: GanttLink[]\r\n): { hasCycle: boolean; cyclePath?: string[] } {\r\n const graph = new Map<string, string[]>()\r\n\r\n for (const link of links) {\r\n const targets = graph.get(link.sourceId) ?? []\r\n targets.push(link.targetId)\r\n graph.set(link.sourceId, targets)\r\n if (!graph.has(link.targetId)) {\r\n graph.set(link.targetId, [])\r\n }\r\n }\r\n\r\n const visiting = new Set<string>()\r\n const visited = new Set<string>()\r\n const path: string[] = []\r\n\r\n const dfs = (node: string): string[] | undefined => {\r\n if (visiting.has(node)) {\r\n const start = path.indexOf(node)\r\n return [...path.slice(start), node]\r\n }\r\n if (visited.has(node)) {\r\n return undefined\r\n }\r\n\r\n visiting.add(node)\r\n path.push(node)\r\n\r\n for (const next of graph.get(node) ?? []) {\r\n const cycle = dfs(next)\r\n if (cycle) {\r\n return cycle\r\n }\r\n }\r\n\r\n path.pop()\r\n visiting.delete(node)\r\n visited.add(node)\r\n return undefined\r\n }\r\n\r\n for (const node of graph.keys()) {\r\n const cyclePath = dfs(node)\r\n if (cyclePath) {\r\n return { hasCycle: true, cyclePath }\r\n }\r\n }\r\n\r\n return { hasCycle: false }\r\n}\r\n","import type { GanttLink, GanttTask } from \"../types\"\r\n\r\nexport function normalizeLinks(tasks: GanttTask[], standaloneLinks: GanttLink[] = []): GanttLink[] {\r\n const taskIds = new Set(tasks.map((task) => task.id))\r\n const byTaskPair = new Map<string, GanttLink>()\r\n const outgoing = new Map<string, Set<string>>()\r\n\r\n const createsCycle = (sourceId: string, targetId: string): boolean => {\r\n const pending = [targetId]\r\n const visited = new Set<string>()\r\n while (pending.length > 0) {\r\n const current = pending.pop()!\r\n if (current === sourceId) {\r\n return true\r\n }\r\n if (visited.has(current)) {\r\n continue\r\n }\r\n visited.add(current)\r\n pending.push(...(outgoing.get(current) ?? []))\r\n }\r\n return false\r\n }\r\n\r\n const append = (link: GanttLink) => {\r\n if (\r\n !taskIds.has(link.sourceId)\r\n || !taskIds.has(link.targetId)\r\n || link.sourceId === link.targetId\r\n ) {\r\n return\r\n }\r\n\r\n const normalized: GanttLink = {\r\n ...link,\r\n id: link.id || `link-${link.sourceId}-${link.targetId}`,\r\n lag: link.lag ?? 0,\r\n lagUnit: link.lagUnit ?? \"calendar\"\r\n }\r\n const pairKey = `${normalized.sourceId}|${normalized.targetId}`\r\n if (byTaskPair.has(pairKey) || createsCycle(normalized.sourceId, normalized.targetId)) {\r\n return\r\n }\r\n\r\n byTaskPair.set(pairKey, normalized)\r\n const targets = outgoing.get(normalized.sourceId) ?? new Set<string>()\r\n targets.add(normalized.targetId)\r\n outgoing.set(normalized.sourceId, targets)\r\n }\r\n\r\n for (const task of tasks) {\r\n for (const dependency of task.dependencies ?? []) {\r\n append({\r\n id: dependency.id || `link-${dependency.predecessorId}-${task.id}`,\r\n sourceId: dependency.predecessorId,\r\n targetId: task.id,\r\n type: dependency.type,\r\n lag: dependency.lag,\r\n lagUnit: dependency.lagUnit\r\n })\r\n }\r\n }\r\n\r\n for (const link of standaloneLinks) {\r\n append(link)\r\n }\r\n\r\n return [...byTaskPair.values()]\r\n}\r\n","import type { FlatTask, GanttTask } from \"../types\"\r\n\r\nexport function flattenTasks(tasks: GanttTask[], collapsedIds: Set<string> = new Set()): FlatTask[] {\r\n const children = new Map<string | null, GanttTask[]>()\r\n\r\n for (const task of tasks) {\r\n const parentId = task.parentId ?? null\r\n const bucket = children.get(parentId) ?? []\r\n bucket.push(task)\r\n children.set(parentId, bucket)\r\n }\r\n\r\n const flattened: FlatTask[] = []\r\n const visit = (task: GanttTask, depth: number) => {\r\n const childTasks = children.get(task.id) ?? []\r\n const collapsed = collapsedIds.has(task.id)\r\n\r\n flattened.push({\r\n task,\r\n depth,\r\n rowIndex: flattened.length,\r\n hasChildren: childTasks.length > 0,\r\n collapsed\r\n })\r\n\r\n if (!collapsed) {\r\n for (const child of childTasks) {\r\n visit(child, depth + 1)\r\n }\r\n }\r\n }\r\n\r\n for (const root of children.get(null) ?? []) {\r\n visit(root, 0)\r\n }\r\n\r\n return flattened\r\n}\r\n","import type { Result } from \"../types\"\r\n\r\nexport const DAY_MS = 24 * 60 * 60 * 1000\r\nexport const HOUR_MS = 60 * 60 * 1000\r\n\r\nexport function toDate(value: string | Date): Date {\r\n if (value instanceof Date) {\r\n return startOfDay(value)\r\n }\r\n\r\n const match = /^(\\d{4})-(\\d{2})-(\\d{2})/.exec(value)\r\n if (match) {\r\n return new Date(Number(match[1]), Number(match[2]) - 1, Number(match[3]))\r\n }\r\n\r\n return startOfDay(new Date(value))\r\n}\r\n\r\n/** 字符串是否携带时间(\"T09:00\" 或 \" 09:00\")。Date 实例一律视为已带时间 */\r\nexport function hasTimeComponent(value: string | Date): boolean {\r\n return typeof value === \"string\" && /T\\d|:\\d/.test(value)\r\n}\r\n\r\n/**\r\n * 解析为保留时间部分的 Date。\r\n * 时间字符串(如 \"2026-07-02T09:00\" / \"2026-07-02 09:00\")按本地时间解析;\r\n * 纯日期字符串(如 \"2026-07-02\")回退到 toDate 的本地零点语义。\r\n */\r\nexport function toDateTime(value: string | Date): Date {\r\n if (value instanceof Date) {\r\n return new Date(value.getTime())\r\n }\r\n if (hasTimeComponent(value)) {\r\n const date = new Date(value.replace(\" \", \"T\"))\r\n if (!Number.isNaN(date.getTime())) {\r\n return date\r\n }\r\n }\r\n return toDate(value)\r\n}\r\n\r\nexport function startOfDay(date: Date): Date {\r\n return new Date(date.getFullYear(), date.getMonth(), date.getDate())\r\n}\r\n\r\nexport function addDays(date: string | Date, days: number): Date {\r\n const next = toDate(date)\r\n next.setDate(next.getDate() + days)\r\n return startOfDay(next)\r\n}\r\n\r\nexport function diffDays(start: string | Date, end: string | Date): number {\r\n return Math.round((toDate(end).getTime() - toDate(start).getTime()) / DAY_MS)\r\n}\r\n\r\nexport function addHours(date: string | Date, hours: number): Date {\r\n return new Date(toDateTime(date).getTime() + hours * HOUR_MS)\r\n}\r\n\r\n/** 小时差(可含分钟的小数,如 09:00-11:30 = 2.5) */\r\nexport function diffHours(start: string | Date, end: string | Date): number {\r\n return (toDateTime(end).getTime() - toDateTime(start).getTime()) / HOUR_MS\r\n}\r\n\r\nexport function inclusiveDays(start: string | Date, end: string | Date): number {\r\n return Math.max(0, diffDays(start, end) + 1)\r\n}\r\n\r\nexport function isValidDate(value: string | Date): boolean {\r\n const date = value instanceof Date ? value : new Date(value)\r\n return !Number.isNaN(date.getTime())\r\n}\r\n\r\nexport function formatDate(date: string | Date): string {\r\n const value = toDate(date)\r\n const year = value.getFullYear()\r\n const month = String(value.getMonth() + 1).padStart(2, \"0\")\r\n const day = String(value.getDate()).padStart(2, \"0\")\r\n return `${year}-${month}-${day}`\r\n}\r\n\r\nexport function formatDateTime(date: string | Date): string {\r\n const value = toDateTime(date)\r\n const hours = String(value.getHours()).padStart(2, \"0\")\r\n const minutes = String(value.getMinutes()).padStart(2, \"0\")\r\n return `${formatDate(value)} ${hours}:${minutes}`\r\n}\r\n\r\nexport function resultInvalidDate<T>(message: string, details?: unknown): Result<T> {\r\n return {\r\n ok: false,\r\n error: { code: \"INVALID_DATE\", message, details }\r\n }\r\n}\r\n","import type { TimeScale, ViewMode } from \"../types\"\r\nimport { addDays, diffDays, startOfDay } from \"../utils/date\"\r\n\r\nexport function computeTimeScale(\r\n start: Date,\r\n end: Date,\r\n viewMode: ViewMode,\r\n columnWidth: number,\r\n firstDayOfWeek: 0 | 1\r\n): TimeScale[] {\r\n const rangeStart = alignStart(start, viewMode, firstDayOfWeek)\r\n const endDate = startOfDay(end)\r\n const rangeEnd = viewMode === \"day\"\r\n ? addDays(endDate, (firstDayOfWeek + 6 - endDate.getDay() + 7) % 7)\r\n : endDate\r\n const scale: TimeScale[] = []\r\n let cursor = rangeStart\r\n let left = 0\r\n\r\n while (cursor.getTime() <= rangeEnd.getTime()) {\r\n const next = nextUnit(cursor, viewMode)\r\n const unitEnd = addDays(next, -1)\r\n const width = Math.max(1, diffDays(cursor, next)) * columnWidth\r\n\r\n scale.push({\r\n start: cursor,\r\n end: unitEnd,\r\n left,\r\n width\r\n })\r\n\r\n left += width\r\n cursor = next\r\n }\r\n\r\n return scale\r\n}\r\n\r\nfunction alignStart(date: Date, viewMode: ViewMode, firstDayOfWeek: 0 | 1): Date {\r\n const start = startOfDay(date)\r\n if (viewMode === \"day\" || viewMode === \"week\") {\r\n const delta = (start.getDay() - firstDayOfWeek + 7) % 7\r\n return addDays(start, -delta)\r\n }\r\n if (viewMode === \"month\") {\r\n return new Date(start.getFullYear(), start.getMonth(), 1)\r\n }\r\n if (viewMode === \"quarter\") {\r\n return new Date(start.getFullYear(), Math.floor(start.getMonth() / 3) * 3, 1)\r\n }\r\n if (viewMode === \"year\") {\r\n return new Date(start.getFullYear(), 0, 1)\r\n }\r\n return start\r\n}\r\n\r\nfunction nextUnit(date: Date, viewMode: ViewMode): Date {\r\n if (viewMode === \"week\") {\r\n return addDays(date, 7)\r\n }\r\n if (viewMode === \"month\") {\r\n return new Date(date.getFullYear(), date.getMonth() + 1, 1)\r\n }\r\n if (viewMode === \"quarter\") {\r\n return new Date(date.getFullYear(), date.getMonth() + 3, 1)\r\n }\r\n if (viewMode === \"year\") {\r\n return new Date(date.getFullYear() + 1, 0, 1)\r\n }\r\n return addDays(date, 1)\r\n}\r\n","import type { TimeScale } from \"../types\"\nimport { addDays, addHours, inclusiveDays, startOfDay } from \"../utils/date\"\n\nexport interface HourScale {\n /** 每日一格(顶行表头用) */\n days: TimeScale[]\n /** 每小时一格(底部刻度用) */\n hours: TimeScale[]\n start: Date\n end: Date\n}\n\n/** 小时级时间刻度:范围对齐到整天,每天拆成 24 个整点小时,width = hourWidth */\nexport function computeHourScale(start: Date, end: Date, hourWidth: number): HourScale {\n const rangeStart = startOfDay(start)\n const rangeEnd = startOfDay(end)\n const totalDays = Math.max(1, inclusiveDays(rangeStart, rangeEnd))\n const days: TimeScale[] = []\n const hours: TimeScale[] = []\n\n for (let d = 0; d < totalDays; d += 1) {\n const dayStart = addDays(rangeStart, d)\n days.push({\n start: dayStart,\n end: addHours(dayStart, 24),\n left: d * 24 * hourWidth,\n width: 24 * hourWidth\n })\n for (let h = 0; h < 24; h += 1) {\n const hourStart = addHours(dayStart, h)\n hours.push({\n start: hourStart,\n end: addHours(hourStart, 1),\n left: (d * 24 + h) * hourWidth,\n width: hourWidth\n })\n }\n }\n\n return { days, hours, start: rangeStart, end: rangeEnd }\n}\n","import type { GanttLink, GanttTask, LinkType, Result } from \"../types\"\r\nimport { addDays, diffDays, inclusiveDays, toDate } from \"../utils/date\"\r\nimport { checkCyclicDependency } from \"../services/checkCyclicDependency\"\r\n\r\nexport function scheduleByDependencies(tasks: GanttTask[], links: GanttLink[]): Result<GanttTask[]> {\r\n const cycle = checkCyclicDependency(links)\r\n if (cycle.hasCycle) {\r\n return {\r\n ok: false,\r\n error: {\r\n code: \"CYCLE_DEPENDENCY\",\r\n message: \"Dependency graph contains a cycle.\",\r\n details: cycle.cyclePath\r\n }\r\n }\r\n }\r\n\r\n const taskMap = new Map(tasks.map((task) => [task.id, cloneTask(task)]))\r\n const incoming = new Map<string, GanttLink[]>()\r\n const outgoing = new Map<string, GanttLink[]>()\r\n\r\n for (const link of links) {\r\n const source = taskMap.get(link.sourceId)\r\n const target = taskMap.get(link.targetId)\r\n if (!source || !target) {\r\n return {\r\n ok: false,\r\n error: {\r\n code: \"ORPHAN_DEPENDENCY\",\r\n message: `Dependency ${link.id} references a missing task.`\r\n }\r\n }\r\n }\r\n incoming.set(link.targetId, [...(incoming.get(link.targetId) ?? []), link])\r\n outgoing.set(link.sourceId, [...(outgoing.get(link.sourceId) ?? []), link])\r\n }\r\n\r\n const ordered = topologicalOrder(tasks.map((task) => task.id), links)\r\n\r\n for (const taskId of ordered) {\r\n const task = taskMap.get(taskId)\r\n if (!task || task.schedulingMode === \"manual\") {\r\n continue\r\n }\r\n\r\n const linksToTask = incoming.get(taskId) ?? []\r\n let start = toDate(task.actual.start)\r\n let end = toDate(task.actual.end)\r\n const duration = Math.max(0, task.duration ?? inclusiveDays(start, end))\r\n\r\n for (const link of linksToTask) {\r\n const source = taskMap.get(link.sourceId)\r\n if (!source) {\r\n continue\r\n }\r\n\r\n const candidate = applyDependency(source, start, end, duration, link.type, link.lag ?? 0)\r\n if (candidate.start.getTime() > start.getTime()) {\r\n start = candidate.start\r\n }\r\n if (candidate.end.getTime() > end.getTime()) {\r\n end = candidate.end\r\n }\r\n }\r\n\r\n const normalizedEnd = task.type === \"milestone\" ? start : addDays(start, Math.max(0, duration - 1))\r\n task.actual = {\r\n ...task.actual,\r\n start,\r\n end: normalizedEnd\r\n }\r\n }\r\n\r\n return { ok: true, data: tasks.map((task) => taskMap.get(task.id) ?? task) }\r\n}\r\n\r\nfunction applyDependency(\r\n source: GanttTask,\r\n currentStart: Date,\r\n currentEnd: Date,\r\n duration: number,\r\n type: LinkType,\r\n lag: number\r\n): { start: Date; end: Date } {\r\n const sourceStart = toDate(source.actual.start)\r\n const sourceEnd = toDate(source.actual.end)\r\n const span = Math.max(0, duration - 1)\r\n\r\n if (type === \"FS\") {\r\n const start = addDays(sourceEnd, lag + 1)\r\n return { start, end: addDays(start, span) }\r\n }\r\n\r\n if (type === \"SS\") {\r\n const start = addDays(sourceStart, lag)\r\n return { start, end: addDays(start, span) }\r\n }\r\n\r\n if (type === \"FF\") {\r\n const end = addDays(sourceEnd, lag)\r\n return { start: addDays(end, -span), end }\r\n }\r\n\r\n const end = addDays(sourceStart, lag)\r\n return { start: addDays(end, -span), end: end.getTime() > currentEnd.getTime() ? end : currentEnd }\r\n}\r\n\r\nfunction topologicalOrder(ids: string[], links: GanttLink[]): string[] {\r\n const indegree = new Map(ids.map((id) => [id, 0]))\r\n const outgoing = new Map<string, string[]>()\r\n\r\n for (const link of links) {\r\n indegree.set(link.targetId, (indegree.get(link.targetId) ?? 0) + 1)\r\n outgoing.set(link.sourceId, [...(outgoing.get(link.sourceId) ?? []), link.targetId])\r\n }\r\n\r\n const queue = ids.filter((id) => (indegree.get(id) ?? 0) === 0)\r\n const ordered: string[] = []\r\n\r\n while (queue.length) {\r\n const current = queue.shift()!\r\n ordered.push(current)\r\n for (const target of outgoing.get(current) ?? []) {\r\n indegree.set(target, (indegree.get(target) ?? 1) - 1)\r\n if (indegree.get(target) === 0) {\r\n queue.push(target)\r\n }\r\n }\r\n }\r\n\r\n return ordered.length === ids.length ? ordered : ids\r\n}\r\n\r\nfunction cloneTask(task: GanttTask): GanttTask {\r\n return {\r\n ...task,\r\n plan: { ...task.plan },\r\n actual: { ...task.actual },\r\n dependencies: task.dependencies?.map((dependency) => ({ ...dependency }))\r\n }\r\n}\r\n\r\nexport function shiftTask(task: GanttTask, start: string | Date): GanttTask {\r\n const currentStart = toDate(task.actual.start)\r\n const nextStart = toDate(start)\r\n const delta = diffDays(currentStart, nextStart)\r\n return {\r\n ...task,\r\n actual: {\r\n ...task.actual,\r\n start: nextStart,\r\n end: addDays(toDate(task.actual.end), delta)\r\n }\r\n }\r\n}\r\n","import type { GanttConfig, GanttLink, GanttTask, Result, TaskLayout, Viewport } from \"../types\"\r\nimport { defaultConfig } from \"../types\"\r\nimport { flattenTasks } from \"../services/flattenTasks\"\r\nimport { normalizeLinks } from \"../services/normalizeLinks\"\r\nimport { scheduleByDependencies } from \"./scheduling\"\r\nimport { diffDays, inclusiveDays, isValidDate, toDate } from \"../utils/date\"\r\n\r\nexport function computeLayout(\r\n tasks: GanttTask[],\r\n links: GanttLink[],\r\n config: Partial<GanttConfig>,\r\n collapsedIds: Set<string> = new Set(),\r\n viewport?: Viewport\r\n): Result<TaskLayout[]> {\r\n const mergedConfig = { ...defaultConfig, ...config }\r\n\r\n for (const task of tasks) {\r\n if (!isValidDate(task.actual.start) || !isValidDate(task.actual.end)) {\r\n return {\r\n ok: false,\r\n error: {\r\n code: \"INVALID_DATE\",\r\n message: `Task ${task.id} has invalid actual dates.`\r\n }\r\n }\r\n }\r\n }\r\n\r\n const normalizedLinks = normalizeLinks(tasks, links)\r\n const scheduled = mergedConfig.autoSchedule === false\r\n ? { ok: true as const, data: tasks }\r\n : scheduleByDependencies(tasks, normalizedLinks)\r\n\r\n if (!scheduled.ok) {\r\n return scheduled\r\n }\r\n\r\n const flatTasks = flattenTasks(scheduled.data, collapsedIds)\r\n const firstDate = mergedConfig.visibleRange\r\n ? toDate(mergedConfig.visibleRange.start)\r\n : minDate(scheduled.data.map((task) => toDate(task.actual.start)))\r\n\r\n const visibleStart = viewport\r\n ? Math.max(0, Math.floor(viewport.scrollTop / mergedConfig.rowHeight) - 8)\r\n : 0\r\n const visibleEnd = viewport\r\n ? Math.ceil((viewport.scrollTop + viewport.clientHeight) / mergedConfig.rowHeight) + 8\r\n : Number.POSITIVE_INFINITY\r\n\r\n const layouts = flatTasks\r\n .filter((flat) => flat.rowIndex >= visibleStart && flat.rowIndex <= visibleEnd)\r\n .map<TaskLayout>((flat) => {\r\n const start = toDate(flat.task.actual.start)\r\n const end = toDate(flat.task.actual.end)\r\n const duration = flat.task.type === \"milestone\" ? 0 : inclusiveDays(start, end)\r\n\r\n return {\r\n taskId: flat.task.id,\r\n rowIndex: flat.rowIndex,\r\n left: diffDays(firstDate, start) * mergedConfig.columnWidth,\r\n width: Math.max(flat.task.type === \"milestone\" ? mergedConfig.columnWidth / 2 : mergedConfig.columnWidth, duration * mergedConfig.columnWidth),\r\n top: mergedConfig.headerHeight + flat.rowIndex * mergedConfig.rowHeight,\r\n depth: flat.depth,\r\n isCritical: false\r\n }\r\n })\r\n\r\n return { ok: true, data: layouts }\r\n}\r\n\r\nfunction minDate(dates: Date[]): Date {\r\n return new Date(Math.min(...dates.map((date) => date.getTime())))\r\n}\r\n","import type { AffectedTasks, GanttConfig, GanttLink, GanttTask, PatchTask, Result } from \"../types\"\r\nimport { flattenTasks } from \"../services/flattenTasks\"\r\nimport { scheduleByDependencies, shiftTask } from \"./scheduling\"\r\nimport { addDays, inclusiveDays, toDate } from \"../utils/date\"\r\n\r\nexport function computeImpact(\r\n taskId: string,\r\n patch: PatchTask,\r\n tasks: GanttTask[],\r\n links: GanttLink[],\r\n config: Partial<GanttConfig>\r\n): Result<AffectedTasks> {\r\n const target = tasks.find((task) => task.id === taskId)\r\n if (!target) {\r\n return {\r\n ok: false,\r\n error: {\r\n code: \"MISSING_TASK\",\r\n message: `Task ${taskId} does not exist.`\r\n }\r\n }\r\n }\r\n\r\n const patchedTasks = tasks.map((task) => {\r\n if (task.id !== taskId) {\r\n return task\r\n }\r\n\r\n let next = { ...task, actual: { ...task.actual } }\r\n if (patch.actualStart) {\r\n next = shiftTask(next, patch.actualStart)\r\n }\r\n if (patch.actualEnd) {\r\n next.actual.end = patch.actualEnd\r\n }\r\n if (typeof patch.progress === \"number\") {\r\n next.actual.progress = patch.progress\r\n }\r\n if (patch.duration) {\r\n next.duration = patch.duration\r\n next.actual.end = addDays(toDate(next.actual.start), Math.max(0, patch.duration - 1))\r\n }\r\n if (patch.name) {\r\n next.name = patch.name\r\n }\r\n if (patch.type) {\r\n next.type = patch.type\r\n }\r\n if (\"parentId\" in patch) {\r\n next.parentId = patch.parentId\r\n }\r\n if (patch.color) {\r\n next.color = patch.color\r\n }\r\n if (\"planColor\" in patch) {\r\n next.planColor = patch.planColor\r\n }\r\n if (patch.schedulingMode) {\r\n next.schedulingMode = patch.schedulingMode\r\n }\r\n return next\r\n })\r\n\r\n const scheduled = config.autoSchedule === false\r\n ? { ok: true as const, data: patchedTasks }\r\n : scheduleByDependencies(patchedTasks, links)\r\n\r\n if (!scheduled.ok) {\r\n return scheduled\r\n }\r\n\r\n const rows = new Map(flattenTasks(scheduled.data).map((flat) => [flat.task.id, flat.rowIndex]))\r\n const originalById = new Map(tasks.map((task) => [task.id, task]))\r\n const changed = scheduled.data\r\n .filter((task) => {\r\n const original = originalById.get(task.id)\r\n return original && (\r\n String(original.actual.start) !== String(task.actual.start) ||\r\n String(original.actual.end) !== String(task.actual.end)\r\n )\r\n })\r\n .map((task) => ({\r\n taskId: task.id,\r\n actualStart: task.actual.start,\r\n actualEnd: task.actual.end,\r\n rowIndex: rows.get(task.id) ?? 0\r\n }))\r\n\r\n const conflicts = scheduled.data\r\n .filter((task) => task.constraint?.date && inclusiveDays(task.actual.start, task.constraint.date) < 0)\r\n .map((task) => ({\r\n taskId: task.id,\r\n constraintType: task.constraint!.type,\r\n message: \"Task violates its configured constraint date.\"\r\n }))\r\n\r\n return { ok: true, data: { changed, conflicts } }\r\n}\r\n","import type {\n GanttConfig,\n Resource,\n ResourceLayout,\n ResourceRowLayout,\n ResourceUsage,\n ResourceUsageLayout,\n Result,\n Viewport\n} from \"../types\"\nimport { defaultConfig } from \"../types\"\nimport {\n addHours,\n diffDays,\n diffHours,\n hasTimeComponent,\n inclusiveDays,\n isValidDate,\n startOfDay,\n toDate,\n toDateTime\n} from \"../utils/date\"\n\n/** 资源色条调色板,与 GanttChart owner 头像的 5 色调色板一致 */\nexport const RESOURCE_COLOR_PALETTE = [\"#d85f87\", \"#9b63d9\", \"#47b987\", \"#d58a5f\", \"#4f83d8\"]\n\nfunction colorHash(source: string): number {\n return [...source].reduce((sum, char) => sum + (char.codePointAt(0) ?? 0), 0)\n}\n\n/** 解析资源主色:resource.color → config.resourceColors[id] → 名称 hash 调色板(确定性) */\nexport function resolveResourceColor(\n resource: Resource,\n resourceColors?: Record<string, string>\n): string {\n if (resource.color) {\n return resource.color\n }\n if (resourceColors?.[resource.id]) {\n return resourceColors[resource.id]\n }\n const source = resource.name || resource.id\n return RESOURCE_COLOR_PALETTE[colorHash(source) % RESOURCE_COLOR_PALETTE.length]\n}\n\n/** 解析占用条颜色:usage.color → 所属资源色 */\nexport function resolveUsageColor(\n usage: ResourceUsage,\n resource: Resource,\n resourceColors?: Record<string, string>\n): string {\n return usage.color || resolveResourceColor(resource, resourceColors)\n}\n\n/**\n * 将占用解析为布局用的起止 Date。\n * - day:与 GanttTask 一致,截断到天。\n * - hour:保留时间;纯日期(未携带时间)按\"含结束日整天\"处理,与 day 模式的含末语义一致。\n */\nexport function resolveUsageTimes(\n usage: ResourceUsage,\n timeUnit: \"day\" | \"hour\"\n): { start: Date; end: Date } {\n if (timeUnit === \"hour\") {\n const start = toDateTime(usage.start)\n const endRaw = toDateTime(usage.end)\n return { start, end: hasTimeComponent(usage.end) ? endRaw : addHours(endRaw, 24) }\n }\n return { start: toDate(usage.start), end: toDate(usage.end) }\n}\n\n/**\n * 资源视图布局:每个资源一行(Y 轴),占用时间段以横向色条展示。\n * 同一资源同时段多个占用按贪心 first-fit 车道分配,行内堆叠子条。\n * day 粒度像素数学与 computeLayout 一致(diffDays × columnWidth);\n * hour 粒度按 diffHours × hourWidth 渲染,纯日期占用含结束日整天。\n * 传入 viewport 时仅产出可见行窗口内的占用几何(±8 overscan),rows 始终全量返回。\n */\nexport function computeResourceLayout(\n resources: Resource[],\n usages: ResourceUsage[],\n config: Partial<GanttConfig>,\n viewport?: Viewport\n): Result<ResourceLayout> {\n const mergedConfig = { ...defaultConfig, ...config }\n const timeUnit = mergedConfig.timeUnit ?? \"day\"\n const hourMode = timeUnit === \"hour\"\n const hourW = mergedConfig.hourWidth ?? 12\n\n for (const usage of usages) {\n const { start, end } = resolveUsageTimes(usage, timeUnit)\n if (!isValidDate(start) || !isValidDate(end)) {\n return {\n ok: false,\n error: {\n code: \"INVALID_DATE\",\n message: `Resource usage ${usage.id} has invalid dates.`\n }\n }\n }\n }\n\n // 行序:resources 顺序在前,usage 引用但未声明的 resourceId 追加在后(容错)\n const orderedIds: string[] = []\n const seen = new Set<string>()\n for (const resource of resources) {\n if (!seen.has(resource.id)) {\n seen.add(resource.id)\n orderedIds.push(resource.id)\n }\n }\n for (const usage of usages) {\n if (!seen.has(usage.resourceId)) {\n seen.add(usage.resourceId)\n orderedIds.push(usage.resourceId)\n }\n }\n\n const resourceById = new Map(resources.map((resource) => [resource.id, resource]))\n const usagesByResource = new Map<string, ResourceUsage[]>()\n for (const usage of usages) {\n usagesByResource.set(usage.resourceId, [...(usagesByResource.get(usage.resourceId) ?? []), usage])\n }\n\n const rows: ResourceRowLayout[] = []\n const usageLayouts: ResourceUsageLayout[] = []\n const laneIndexByUsage = new Map<string, number>()\n\n orderedIds.forEach((resourceId, rowIndex) => {\n const resource = resourceById.get(resourceId) ?? {\n id: resourceId,\n name: resourceId\n } satisfies Resource\n const grouped = [...(usagesByResource.get(resourceId) ?? [])].sort((a, b) => {\n const as = resolveUsageTimes(a, timeUnit)\n const bs = resolveUsageTimes(b, timeUnit)\n return as.start.getTime() - bs.start.getTime() || as.end.getTime() - bs.end.getTime()\n })\n\n // 贪心 first-fit 车道分配:占用放入第一个\"前一个占用结束不晚于其开始\"的车道。\n // day:diffDays(end, start) > 0(相邻日复用);hour:diffHours(end, start) >= 0(首尾相接复用)\n const laneEnds: Array<Date | null> = []\n for (const usage of grouped) {\n const { start, end } = resolveUsageTimes(usage, timeUnit)\n let laneIndex = -1\n for (let k = 0; k < laneEnds.length; k += 1) {\n const laneEnd = laneEnds[k]\n if (laneEnd === null || isLaneFree(laneEnd, start, hourMode)) {\n laneIndex = k\n break\n }\n }\n if (laneIndex === -1) {\n laneIndex = laneEnds.length\n laneEnds.push(null)\n }\n laneEnds[laneIndex] = end\n laneIndexByUsage.set(usage.id, laneIndex)\n }\n\n rows.push({\n resourceId,\n rowIndex,\n laneCount: laneEnds.length,\n color: resolveResourceColor(resource, mergedConfig.resourceColors),\n usageIds: grouped.map((usage) => usage.id)\n })\n })\n\n const usageById = new Map(usages.map((usage) => [usage.id, usage]))\n const visibleStart = viewport\n ? Math.max(0, Math.floor(viewport.scrollTop / mergedConfig.rowHeight) - 8)\n : 0\n const visibleEnd = viewport\n ? Math.ceil((viewport.scrollTop + viewport.clientHeight) / mergedConfig.rowHeight) + 8\n : Number.POSITIVE_INFINITY\n\n const firstDate = hourMode\n ? startOfDay(\n mergedConfig.visibleRange\n ? toDateTime(mergedConfig.visibleRange.start)\n : usages.length\n ? minDate(usages.map((usage) => resolveUsageTimes(usage, \"hour\").start))\n : toDateTime(new Date())\n )\n : mergedConfig.visibleRange\n ? toDate(mergedConfig.visibleRange.start)\n : usages.length\n ? minDate(usages.map((usage) => toDate(usage.start)))\n : toDate(new Date())\n\n for (const row of rows) {\n if (row.rowIndex < visibleStart || row.rowIndex > visibleEnd) {\n continue\n }\n const resource = resourceById.get(row.resourceId) ?? { id: row.resourceId, name: row.resourceId }\n const laneCount = Math.max(1, row.laneCount)\n const slot = mergedConfig.rowHeight / laneCount\n const barHeight = Math.max(6, Math.min(14, slot - 4))\n\n for (const usageId of row.usageIds) {\n const usage = usageById.get(usageId)\n if (!usage) {\n continue\n }\n const { start, end } = resolveUsageTimes(usage, timeUnit)\n const laneIndex = laneIndexByUsage.get(usageId) ?? 0\n const laneTop = laneIndex * slot + (slot - barHeight) / 2\n const left = hourMode\n ? diffHours(firstDate, start) * hourW\n : diffDays(firstDate, start) * mergedConfig.columnWidth\n const width = hourMode\n ? Math.max(hourW, diffHours(start, end) * hourW)\n : Math.max(mergedConfig.columnWidth, inclusiveDays(start, end) * mergedConfig.columnWidth)\n\n usageLayouts.push({\n usageId,\n resourceId: row.resourceId,\n rowIndex: row.rowIndex,\n laneIndex,\n laneCount,\n left,\n width,\n top: mergedConfig.headerHeight + row.rowIndex * mergedConfig.rowHeight + laneTop,\n height: barHeight\n })\n }\n }\n\n return { ok: true, data: { rows, usages: usageLayouts } }\n}\n\nfunction isLaneFree(laneEnd: Date, start: Date, hourMode: boolean): boolean {\n return hourMode ? diffHours(laneEnd, start) >= 0 : diffDays(laneEnd, start) > 0\n}\n\nfunction minDate(dates: Date[]): Date {\n return new Date(Math.min(...dates.map((date) => date.getTime())))\n}\n","import type {\r\n GanttConfig,\r\n GanttTask,\r\n GanttLink,\r\n GanttMarker,\r\n PatchTask,\r\n TaskLayout,\r\n TimeScale,\r\n FlatTask,\r\n Result\r\n} from \"../types\"\r\nimport { defaultConfig } from \"../types\"\r\nimport { computeLayout } from \"../engines/computeLayout\"\r\nimport { computeTimeScale } from \"../services/computeTimeScale\"\r\nimport { flattenTasks } from \"../services/flattenTasks\"\r\nimport { toDate, diffDays, addDays } from \"../utils/date\"\r\n\r\n/**\r\n * 把扁平的 PatchTask 合并回嵌套的 GanttTask。\r\n * PatchTask 用 planStart/planEnd/actualStart/actualEnd 表示日期,\r\n * GanttTask 用 plan.start/end、actual.start/end 嵌套表示——这是组件层\r\n * 和数据层之间的桥,本该在引擎层而不是散落在各 demo 里。\r\n */\r\nexport function mergeTaskPatch(task: GanttTask, patch: PatchTask): GanttTask {\r\n return {\r\n ...task,\r\n ...patch,\r\n plan: {\r\n ...task.plan,\r\n start: patch.planStart ?? task.plan.start,\r\n end: patch.planEnd ?? task.plan.end,\r\n progress: patch.progress ?? task.plan.progress\r\n },\r\n actual: {\r\n ...task.actual,\r\n start: patch.actualStart ?? task.actual.start,\r\n end: patch.actualEnd ?? task.actual.end,\r\n progress: patch.progress ?? task.actual.progress\r\n },\r\n custom: patch.custom ? { ...task.custom, ...patch.custom } : task.custom\r\n }\r\n}\r\n\r\nexport interface GanttEngineOptions {\r\n tasks?: GanttTask[]\r\n links?: GanttLink[]\r\n config?: Partial<GanttConfig>\r\n /** 滚动容器元素;命令式视口 API(scrollToDate / zoomToFit)需要它。纯计算场景可不传。 */\r\n container?: HTMLElement | null\r\n /** 初始折叠的任务 id */\r\n collapsedIds?: Iterable<string>\r\n /** 里程碑标记 */\r\n markers?: GanttMarker[]\r\n}\r\n\r\nexport type GanttEngineEvent =\r\n | \"ready\"\r\n | \"taskschange\"\r\n | \"taskchange\"\r\n | \"taskcreate\"\r\n | \"taskdelete\"\r\n | \"linkschange\"\r\n | \"configchange\"\r\n | \"collapsechange\"\r\n | \"previewchange\"\r\n | \"markerschange\"\r\n | \"destroy\"\r\n\r\ntype Listener = (...args: unknown[]) => void\r\n\r\n/** 命令式甘特图引擎。框架无关,可被 Vue/React/原生 各自的薄壳组件驱动。 */\r\nexport class GanttEngine {\r\n private tasks: GanttTask[]\r\n private links: GanttLink[]\r\n private config: GanttConfig\r\n private readonly container: HTMLElement | null\r\n private collapsedIds: Set<string>\r\n private dragPreview: { taskId: string; patch: PatchTask; affected?: Record<string, PatchTask> } | null = null\r\n private markers: GanttMarker[]\r\n private readonly listeners: Map<GanttEngineEvent, Set<Listener>>\r\n private destroyed = false\r\n\r\n constructor(options: GanttEngineOptions = {}) {\r\n this.tasks = options.tasks ? options.tasks.map((task) => ({ ...task })) : []\r\n this.links = options.links ? options.links.map((link) => ({ ...link })) : []\r\n this.config = { ...defaultConfig, ...options.config }\r\n this.container = options.container ?? null\r\n this.collapsedIds = new Set(options.collapsedIds ?? [])\r\n this.markers = options.markers ? options.markers.map((marker) => ({ ...marker })) : []\r\n this.listeners = new Map()\r\n // 异步发 ready,确保订阅者能在构造后、首次 emit 前注册\r\n queueMicrotask(() => this.emit(\"ready\"))\r\n }\r\n\r\n // ── 状态查询 ──\r\n getTasks(): GanttTask[] {\r\n return this.tasks.map((task) => ({ ...task }))\r\n }\r\n getLinks(): GanttLink[] {\r\n return this.links.map((link) => ({ ...link }))\r\n }\r\n getMarkers(): GanttMarker[] {\r\n return this.markers.map((marker) => ({ ...marker }))\r\n }\r\n getConfig(): GanttConfig {\r\n return { ...this.config }\r\n }\r\n getTask(id: string): GanttTask | undefined {\r\n const task = this.tasks.find((item) => item.id === id)\r\n return task ? { ...task } : undefined\r\n }\r\n getCollapsedIds(): string[] {\r\n return Array.from(this.collapsedIds)\r\n }\r\n isDestroyed(): boolean {\r\n return this.destroyed\r\n }\r\n\r\n // ── 命令式变更(均触发对应事件) ──\r\n setTasks(tasks: GanttTask[]): void {\r\n this.assertNotDestroyed()\r\n this.tasks = tasks.map((task) => ({ ...task }))\r\n this.emit(\"taskschange\", this.getTasks())\r\n }\r\n setLinks(links: GanttLink[]): void {\r\n this.assertNotDestroyed()\r\n this.links = links.map((link) => ({ ...link }))\r\n this.emit(\"linkschange\", this.getLinks())\r\n }\r\n setMarkers(markers: GanttMarker[]): void {\r\n this.assertNotDestroyed()\r\n this.markers = markers.map((marker) => ({ ...marker }))\r\n this.emit(\"markerschange\", this.getMarkers())\r\n }\r\n /** 合并配置 patch,等价于 setConfig(mergeConfig(patch)) */\r\n mergeConfig(patch: Partial<GanttConfig>): void {\r\n this.assertNotDestroyed()\r\n this.config = { ...this.config, ...patch }\r\n this.emit(\"configchange\", this.getConfig())\r\n }\r\n setConfig(config: Partial<GanttConfig>): void {\r\n this.mergeConfig(config)\r\n }\r\n /** 用 mergeTaskPatch 把扁平 patch 合并回嵌套结构后更新单个任务 */\r\n setTask(id: string, patch: PatchTask): void {\r\n this.assertNotDestroyed()\r\n const target = this.tasks.find((task) => task.id === id)\r\n if (!target) {\r\n return\r\n }\r\n const updated = mergeTaskPatch(target, patch)\r\n this.tasks = this.tasks.map((task) => (task.id === id ? updated : task))\r\n this.emit(\"taskchange\", id, patch, { ...updated })\r\n }\r\n addTask(task: GanttTask): void {\r\n this.assertNotDestroyed()\r\n this.tasks = [...this.tasks, { ...task }]\r\n this.emit(\"taskcreate\", { ...task })\r\n }\r\n removeTask(id: string): void {\r\n this.assertNotDestroyed()\r\n const removed = this.tasks.find((task) => task.id === id)\r\n if (!removed) return\r\n this.tasks = this.tasks.filter((task) => task.id !== id)\r\n this.collapsedIds.delete(id)\r\n this.emit(\"taskdelete\", id)\r\n }\r\n collapse(id: string, collapsed = true): void {\r\n this.assertNotDestroyed()\r\n if (collapsed) this.collapsedIds.add(id)\r\n else this.collapsedIds.delete(id)\r\n this.emit(\"collapsechange\", this.getCollapsedIds())\r\n }\r\n toggleCollapse(id: string): void {\r\n this.collapse(id, !this.collapsedIds.has(id))\r\n }\r\n /** 批量折叠/展开多个任务(如\"折叠全部分组\") */\r\n setCollapsed(ids: string[], collapsed = true): void {\r\n this.assertNotDestroyed()\r\n const next = new Set(this.collapsedIds)\r\n for (const id of ids) {\r\n if (collapsed) {\r\n next.add(id)\r\n } else {\r\n next.delete(id)\r\n }\r\n }\r\n this.collapsedIds = next\r\n this.emit(\"collapsechange\", this.getCollapsedIds())\r\n }\r\n\r\n // ── 拖拽预览(交互态源;组件写入迁移到 engine,dateRange 可据此扩展) ──\r\n setPreview(preview: { taskId: string; patch: PatchTask; affected?: Record<string, PatchTask> }): void {\r\n this.assertNotDestroyed()\r\n this.dragPreview = {\r\n taskId: preview.taskId,\r\n patch: { ...preview.patch },\r\n affected: preview.affected ? { ...preview.affected } : undefined\r\n }\r\n this.emit(\"previewchange\", this.getPreview())\r\n }\r\n clearPreview(): void {\r\n if (!this.dragPreview) {\r\n return\r\n }\r\n this.dragPreview = null\r\n this.emit(\"previewchange\", null)\r\n }\r\n getPreview(): { taskId: string; patch: PatchTask; affected?: Record<string, PatchTask> } | null {\r\n if (!this.dragPreview) {\r\n return null\r\n }\r\n return {\r\n taskId: this.dragPreview.taskId,\r\n patch: { ...this.dragPreview.patch },\r\n affected: this.dragPreview.affected ? { ...this.dragPreview.affected } : undefined\r\n }\r\n }\r\n\r\n // ── 计算(delegate 到 core 纯函数) ──\r\n /** 数据真实日期范围(未含 viewport pad) */\r\n getDateRange(): { start: Date; end: Date } {\r\n const dates: Date[] = []\r\n for (const task of this.tasks) {\r\n dates.push(\r\n toDate(task.plan.start),\r\n toDate(task.plan.end),\r\n toDate(task.actual.start),\r\n toDate(task.actual.end)\r\n )\r\n }\r\n for (const marker of this.markers) {\r\n dates.push(toDate(marker.date))\r\n }\r\n // 合并拖拽预览:用 mergeTaskPatch 算 patch 后日期扩范围(只扩不缩)\r\n const preview = this.dragPreview\r\n if (preview) {\r\n const ids = [preview.taskId, ...Object.keys(preview.affected ?? {})]\r\n for (const id of ids) {\r\n const task = this.tasks.find((item) => item.id === id)\r\n if (!task) {\r\n continue\r\n }\r\n const patch = preview.taskId === id ? preview.patch : preview.affected?.[id]\r\n if (!patch) {\r\n continue\r\n }\r\n const merged = mergeTaskPatch(task, patch)\r\n dates.push(\r\n toDate(merged.plan.start),\r\n toDate(merged.plan.end),\r\n toDate(merged.actual.start),\r\n toDate(merged.actual.end)\r\n )\r\n }\r\n }\r\n if (!dates.length) {\r\n const today = toDate(new Date())\r\n return { start: today, end: addDays(today, 30) }\r\n }\r\n return {\r\n start: new Date(Math.min(...dates.map((d) => d.getTime()))),\r\n end: new Date(Math.max(...dates.map((d) => d.getTime())))\r\n }\r\n }\r\n getTimeScale(): TimeScale[] {\r\n const range = this.getDateRange()\r\n return computeTimeScale(\r\n range.start,\r\n range.end,\r\n this.config.viewMode,\r\n this.config.columnWidth,\r\n this.config.firstDayOfWeek\r\n )\r\n }\r\n getLayout(): Result<TaskLayout[]> {\r\n return computeLayout(\r\n this.tasks,\r\n this.links,\r\n this.config,\r\n this.collapsedIds\r\n )\r\n }\r\n getFlatTasks(): FlatTask[] {\r\n return flattenTasks(this.tasks, this.collapsedIds)\r\n }\r\n getTotalWidth(): number {\r\n return this.getTimeScale().reduce((sum, tick) => sum + tick.width, 0)\r\n }\r\n getTotalHeight(): number {\r\n const rows = this.getFlatTasks().length\r\n return this.config.headerHeight + rows * this.config.rowHeight\r\n }\r\n\r\n // ── 视口命令(需要 container) ──\r\n getScrollLeft(): number {\r\n return this.container?.scrollLeft ?? 0\r\n }\r\n setScrollLeft(px: number): void {\r\n if (!this.container) return\r\n const max = Math.max(0, this.container.scrollWidth - this.container.clientWidth)\r\n this.container.scrollLeft = Math.max(0, Math.min(max, px))\r\n }\r\n /** 把当前 scrollLeft 限制到 [0, maxScrollLeft]。\r\n * 拖拽预览收缩(松手/取消)后 timeline 总宽度变小,scrollLeft 可能超出新边界,\r\n * 导致 canvas translate 超出内容区、右侧网格未覆盖。此时调用本方法防超界。 */\r\n clampScrollLeft(): void {\r\n if (!this.container) return\r\n const max = Math.max(0, this.container.scrollWidth - this.container.clientWidth)\r\n if (this.container.scrollLeft > max) {\r\n this.container.scrollLeft = max\r\n }\r\n }\r\n getScrollTop(): number {\r\n return this.container?.scrollTop ?? 0\r\n }\r\n setScrollTop(px: number): void {\r\n if (!this.container) return\r\n const max = Math.max(0, this.container.scrollHeight - this.container.clientHeight)\r\n this.container.scrollTop = Math.max(0, Math.min(max, px))\r\n }\r\n /** 滚动到包含目标日期的 tick 列(跨 viewMode 精确) */\r\n scrollToDate(date: string | Date): void {\r\n if (!this.container) return\r\n const target = toDate(date)\r\n const scale = this.getTimeScale()\r\n const tick = scale.find((t) => target >= t.start && target <= t.end)\r\n const left = tick ? tick.left : diffDays(this.getDateRange().start, target) * this.config.columnWidth\r\n this.setScrollLeft(left)\r\n }\r\n scrollToTask(id: string): void {\r\n const layout = this.getLayout()\r\n if (!layout.ok) return\r\n const item = layout.data.find((row) => row.taskId === id)\r\n if (item) this.setScrollLeft(item.left)\r\n }\r\n scrollToStart(): void {\r\n this.setScrollLeft(0)\r\n }\r\n scrollToEnd(): void {\r\n if (!this.container) return\r\n this.setScrollLeft(this.getTotalWidth())\r\n }\r\n /** 让整个 timeline 总宽度≈视口宽度(调 columnWidth) */\r\n zoomToFit(padding = 1): void {\r\n if (!this.container) return\r\n const spanDays = Math.max(1, diffDays(this.getDateRange().start, this.getDateRange().end) + 1)\r\n const target = (this.container.clientWidth * padding) / spanDays\r\n this.mergeConfig({ columnWidth: this.clampColumnWidth(target) })\r\n }\r\n /** 让指定日期范围占满视口宽度(调 columnWidth) */\r\n zoomToRange(start: string | Date, end: string | Date): void {\r\n if (!this.container) return\r\n const span = Math.max(1, diffDays(start, end) + 1)\r\n const target = this.container.clientWidth / span\r\n this.mergeConfig({ columnWidth: this.clampColumnWidth(target) })\r\n }\r\n private clampColumnWidth(value: number): number {\r\n return Math.max(4, Math.min(400, Math.round(value)))\r\n }\r\n\r\n // ── 事件订阅 ──\r\n on(event: GanttEngineEvent, fn: Listener): () => void {\r\n this.assertNotDestroyed()\r\n let set = this.listeners.get(event)\r\n if (!set) {\r\n set = new Set()\r\n this.listeners.set(event, set)\r\n }\r\n set.add(fn)\r\n return () => {\r\n const current = this.listeners.get(event)\r\n if (current) {\r\n current.delete(fn)\r\n if (!current.size) this.listeners.delete(event)\r\n }\r\n }\r\n }\r\n once(event: GanttEngineEvent, fn: Listener): () => void {\r\n const off = this.on(event, (...args: unknown[]) => {\r\n off()\r\n fn(...args)\r\n })\r\n return off\r\n }\r\n off(event: GanttEngineEvent, fn?: Listener): void {\r\n const set = this.listeners.get(event)\r\n if (!set) return\r\n if (fn) {\r\n set.delete(fn)\r\n if (!set.size) this.listeners.delete(event)\r\n } else {\r\n this.listeners.delete(event)\r\n }\r\n }\r\n emit(event: GanttEngineEvent, ...args: unknown[]): void {\r\n const set = this.listeners.get(event)\r\n if (!set) return\r\n // 复制遍历,避免回调中增删导致迭代错乱\r\n for (const fn of Array.from(set)) {\r\n try {\r\n fn(...args)\r\n } catch (error) {\r\n // 单个监听器抛错不应打断其他监听器\r\n if (typeof console !== \"undefined\") {\r\n console.error(`[GanttEngine] listener for \"${event}\" threw`, error)\r\n }\r\n }\r\n }\r\n }\r\n\r\n // ── 生命周期 ──\r\n destroy(): void {\r\n if (this.destroyed) return\r\n this.destroyed = true\r\n this.emit(\"destroy\")\r\n this.listeners.clear()\r\n this.tasks = []\r\n this.links = []\r\n }\r\n\r\n private assertNotDestroyed(): void {\r\n if (this.destroyed) {\r\n throw new Error(\"GanttEngine: operation on destroyed instance\")\r\n }\r\n }\r\n}\r\n\r\n/** 工厂函数,对应成熟做法的 new GanttEngine(container, config) 入口 */\r\nexport function createGanttEngine(options: GanttEngineOptions = {}): GanttEngine {\r\n return new GanttEngine(options)\r\n}\r\n"],"mappings":"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","names":["end","minDate"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/styles/colors.ts","../src/types/index.ts","../src/services/checkCyclicDependency.ts","../src/services/normalizeLinks.ts","../src/services/flattenTasks.ts","../src/utils/date.ts","../src/services/computeTimeScale.ts","../src/services/computeHourScale.ts","../src/engines/scheduling.ts","../src/engines/computeLayout.ts","../src/engines/computeImpact.ts","../src/engines/computeResourceLayout.ts","../src/engine/GanttEngine.ts"],"sourcesContent":["export * from \"./types\"\r\nexport * from \"./styles/colors\"\r\nexport * from \"./services/checkCyclicDependency\"\r\nexport * from \"./services/normalizeLinks\"\r\nexport * from \"./services/flattenTasks\"\r\nexport * from \"./services/computeTimeScale\"\r\nexport * from \"./services/computeHourScale\"\r\nexport * from \"./engines/computeLayout\"\r\nexport * from \"./engines/computeImpact\"\r\nexport * from \"./engines/computeResourceLayout\"\r\nexport * from \"./engines/scheduling\"\r\nexport * from \"./utils/date\"\r\nexport * from \"./engine/GanttEngine\"\r\n","/**\n * 默认颜色统一配置 —— 甘特图 / 资源视图 / 小时视图的默认条色与调色板都从这里读取。\n *\n * 想更换「默认配色」时改这一个文件即可全局生效;单个实例仍可通过\n * config.taskColors / config.hourColors / config.resourceColors / 任务 color、planColor 字段按需覆盖。\n *\n * 默认色按视图拆分:\n * - DEFAULT_TASK_COLORS:甘特图(实际条 / 计划条 / 阶段 / 里程碑),也是资源视图与小时视图的共享回退。\n * - DEFAULT_HOUR_COLORS:小时视图计划条与里程碑的独立默认,互不影响甘特图。\n */\n\n/** 甘特图默认语义色(对应 GanttConfig.taskColors) */\nexport const DEFAULT_TASK_COLORS: {\n task: string\n summary: string\n milestone: string\n plan: string\n progress: string\n} = {\n task: \"#4d77b3\", // 普通任务实际条\n summary: \"#475467\", // 阶段条\n milestone: \"#b57e4a\", // 里程碑菱形 / 参考线默认\n plan: \"#cbd5e1\", // 计划条外部底色\n progress: \"#367d73\" // 计划条内部进度色(任务未设置 planColor 时)\n}\n\n/** 小时视图计划条与里程碑默认色(对应 GanttConfig.hourColors);独立于甘特图,可单独修改 */\nexport const DEFAULT_HOUR_COLORS: {\n plan: string\n progress: string\n milestone: string\n} = {\n plan: \"#cbd5e1\", // 计划条外部底色\n progress: \"#367d73\", // 计划条内部进度色(任务未设置 planColor 时)\n milestone: \"#b57e4a\" // 里程碑菱形 / 参考线默认\n}\n\n/** 资源视图未指定颜色时,按资源名哈希从该调色板确定性取色 */\nexport const RESOURCE_COLOR_PALETTE: string[] = [\"#f10755\", \"#9b87c4\", \"#509b81\", \"#c18f67\", \"#5e7fb0\"]\n\n/** 内置任务编辑器 / 里程碑编辑器的可选颜色板 */\nexport const EDITOR_COLOR_OPTIONS: string[] = [\n \"#4d77b3\",\n \"#8f77c5\",\n \"#c16794\",\n \"#b57e4a\",\n \"#b3a24d\",\n \"#47ae8f\",\n \"#3f99a6\",\n \"#64748b\"\n]\n\n/** 左侧列表负责人头像兜底色板 */\nexport const OWNER_COLOR_PALETTE: string[] = [\"#c2708b\", \"#9b87c4\", \"#509b81\", \"#c18f67\", \"#5e7fb0\"]\n","import { DEFAULT_TASK_COLORS } from \"../styles/colors\"\r\n\r\nexport type ViewMode = \"day\" | \"week\" | \"month\" | \"quarter\" | \"year\"\r\n\r\nexport type LinkType = \"FS\" | \"SS\" | \"FF\" | \"SF\"\r\nexport type LagUnit = \"calendar\" | \"working\"\r\n\r\nexport type Result<T> =\r\n | { ok: true; data: T }\r\n | { ok: false; error: GanttError }\r\n\r\nexport interface GanttError {\r\n code:\r\n | \"CYCLE_DEPENDENCY\"\r\n | \"INVALID_DATE\"\r\n | \"MISSING_TASK\"\r\n | \"ORPHAN_DEPENDENCY\"\r\n | \"INVALID_DEPENDENCY\"\r\n message: string\r\n details?: unknown\r\n}\r\n\r\nexport interface DateRange {\r\n start: string | Date\r\n end: string | Date\r\n progress?: number\r\n}\r\n\r\nexport interface Dependency {\r\n id?: string\r\n predecessorId: string\r\n type: LinkType\r\n lag: number\r\n lagUnit: LagUnit\r\n}\r\n\r\nexport interface GanttTask {\r\n id: string\r\n name: string\r\n type: \"task\" | \"milestone\" | \"summary\"\r\n plan: DateRange\r\n actual: Required<DateRange>\r\n dependencies?: Dependency[]\r\n parentId?: string | null\r\n color?: string\r\n planColor?: string\r\n calendarId?: string\r\n resources?: string[]\r\n segments?: Array<{ start: string | Date; end: string | Date }>\r\n constraint?: {\r\n type: \"SNET\" | \"SNLT\" | \"MSO\" | \"MFO\" | \"ASAP\" | \"ALAP\"\r\n date?: string | Date\r\n }\r\n schedulingMode?: \"auto\" | \"manual\"\r\n duration?: number\r\n custom?: Record<string, unknown>\r\n}\r\n\r\nexport interface GanttMarker {\r\n id: string\r\n name: string\r\n date: string | Date\r\n color?: string\r\n}\r\n\r\nexport interface GanttEditorField {\r\n key: string\r\n label: string\r\n visible?: boolean\r\n editable?: boolean\r\n type?: \"text\" | \"number\" | \"date\" | \"select\"\r\n options?: Array<{ label: string; value: string | number }>\r\n placeholder?: string\r\n}\r\n\r\n/** 内置任务字段键。列可通过 CustomColumn.field 绑定到这些内置字段。\r\n * 除 status / owner 外都对应任务对象上的真实字段;status 与 owner 是派生展示字段。 */\r\nexport type BuiltinFieldKey =\r\n | \"name\"\r\n | \"status\"\r\n | \"owner\"\r\n | \"resources\"\r\n | \"type\"\r\n | \"parentId\"\r\n | \"planStart\"\r\n | \"planEnd\"\r\n | \"actualStart\"\r\n | \"actualEnd\"\r\n | \"progress\"\r\n | \"duration\"\r\n | \"calendarId\"\r\n | \"schedulingMode\"\r\n | \"color\"\r\n | \"planColor\"\r\n\r\nexport interface CustomColumn {\r\n key: string\r\n /** 绑定到的内置任务字段键。缺省时列按自定义列处理,从 task.custom[key] 读取/编辑;设置后列读取/编辑该内置字段,key 仍作为列标识。 */\r\n field?: BuiltinFieldKey\r\n label: string\r\n width?: number\r\n visible?: boolean\r\n align?: \"left\" | \"center\" | \"right\"\r\n editable?: boolean\r\n type?: \"text\" | \"number\" | \"date\" | \"select\"\r\n options?: Array<{ label: string; value: string | number }>\r\n placeholder?: string\r\n editor?: Omit<GanttEditorField, \"key\" | \"label\">\r\n formatter?: (value: unknown, task: GanttTask) => string\r\n cellClass?: string | ((task: GanttTask) => string)\r\n cellStyle?: Record<string, string | number> | ((task: GanttTask) => Record<string, string | number>)\r\n render?: (task: GanttTask) => { text: string; className?: string }\r\n}\r\n\r\nexport interface GanttConfig {\r\n viewMode: ViewMode\r\n rowHeight: number\r\n columnWidth: number\r\n headerHeight: number\r\n taskListWidth: number\r\n width?: string | number\r\n height?: string | number\r\n locale: string\r\n firstDayOfWeek: 0 | 1\r\n dateFormat: string\r\n customColumns?: CustomColumn[]\r\n columns?: CustomColumn[]\r\n editorFields?: GanttEditorField[]\r\n columnWidths?: Partial<Record<ViewMode, number>>\r\n theme?: \"light\" | \"dark\" | string\r\n visibleRange?: {\r\n start: string | Date\r\n end: string | Date\r\n }\r\n showPlanBar?: boolean\r\n showActualBar?: boolean\r\n showTimelineWhenEmpty?: boolean\r\n builtInTaskEditor?: boolean\r\n builtInMarkerEditor?: boolean\r\n editablePlan?: boolean\r\n editableActual?: boolean\r\n enableLinkCreation?: boolean\r\n showLinkRejectionNotice?: boolean\r\n virtualScroll?: boolean\r\n taskColors?: {\r\n task?: string\r\n summary?: string\r\n milestone?: string\r\n plan?: string\r\n progress?: string\r\n }\r\n /** 小时视图独立配色:plan/progress/milestone 缺省时回退到 taskColors 与 DEFAULT_HOUR_COLORS */\r\n hourColors?: {\r\n plan?: string\r\n progress?: string\r\n milestone?: string\r\n }\r\n resourceColors?: Record<string, string>\r\n /** 资源视图时间粒度;day 按整天渲染,hour 按占用携带的小时渲染(仅 ResourceTimeline 使用) */\r\n timeUnit?: \"day\" | \"hour\"\r\n /** 资源视图 hour 粒度下每小时像素宽 */\r\n hourWidth?: number\r\n /** 小时视图计划条高度;缺省时随 rowHeight 自动推导(与资源视图单车道条高公式一致) */\r\n barHeight?: number\r\n autoSchedule?: boolean\r\n editable?: boolean\r\n}\r\n\r\nexport interface GanttLink {\r\n id: string\r\n sourceId: string\r\n targetId: string\r\n type: LinkType\r\n lag?: number\r\n lagUnit?: LagUnit\r\n}\r\n\r\nexport interface TaskLayout {\r\n taskId: string\r\n rowIndex: number\r\n left: number\r\n width: number\r\n top: number\r\n depth: number\r\n isCritical: boolean\r\n}\r\n\r\n/** 资源(会议室、负责人等),资源视图的 Y 轴主体 */\r\nexport interface Resource {\r\n id: string\r\n name: string\r\n color?: string\r\n custom?: Record<string, unknown>\r\n}\r\n\r\nexport type ResourceUsageType = \"booking\" | \"assignment\"\r\n\r\n/** 资源在某时间段的占用(会议室预订、负责人承接的项目等) */\r\nexport interface ResourceUsage {\r\n id: string\r\n resourceId: string\r\n /** 日历日粒度,与 GanttTask 一致;同一天内的时段(如 9:00-11:00)按当天整格渲染,时刻体现在 label/tooltip */\r\n start: string | Date\r\n end: string | Date\r\n label?: string\r\n color?: string\r\n type?: ResourceUsageType\r\n custom?: Record<string, unknown>\r\n}\r\n\r\nexport interface ResourceRowLayout {\r\n resourceId: string\r\n rowIndex: number\r\n /** 行内占用车道数(同时段重叠数),>1 时条需要堆叠变薄 */\r\n laneCount: number\r\n color: string\r\n usageIds: string[]\r\n}\r\n\r\nexport interface ResourceUsageLayout {\r\n usageId: string\r\n resourceId: string\r\n rowIndex: number\r\n laneIndex: number\r\n laneCount: number\r\n left: number\r\n width: number\r\n /** 绝对 top(含 headerHeight + rowIndex*rowHeight + 行内 lane 偏移) */\r\n top: number\r\n /** 条高度,由 laneCount 与 rowHeight 决定 */\r\n height: number\r\n}\r\n\r\nexport interface ResourceLayout {\r\n rows: ResourceRowLayout[]\r\n usages: ResourceUsageLayout[]\r\n}\r\n\r\nexport interface FlatTask {\r\n task: GanttTask\r\n depth: number\r\n rowIndex: number\r\n hasChildren: boolean\r\n collapsed: boolean\r\n}\r\n\r\nexport interface Calendar {\r\n id: string\r\n workingDays: number[]\r\n holidays: string[]\r\n hours: [number, number][]\r\n}\r\n\r\nexport interface TimeScale {\r\n start: Date\r\n end: Date\r\n left: number\r\n width: number\r\n}\r\n\r\nexport interface Viewport {\r\n scrollTop: number\r\n scrollLeft: number\r\n clientWidth: number\r\n clientHeight: number\r\n dpr: number\r\n}\r\n\r\nexport interface AffectedTasks {\r\n changed: Array<{\r\n taskId: string\r\n actualStart: string | Date\r\n actualEnd: string | Date\r\n rowIndex: number\r\n }>\r\n conflicts: Array<{\r\n taskId: string\r\n constraintType: string\r\n message: string\r\n }>\r\n}\r\n\r\nexport interface PatchTask {\r\n planStart?: string | Date\r\n planEnd?: string | Date\r\n actualStart?: string | Date\r\n actualEnd?: string | Date\r\n progress?: number\r\n name?: string\r\n type?: GanttTask[\"type\"]\r\n parentId?: string | null\r\n color?: string\r\n planColor?: string\r\n duration?: number\r\n schedulingMode?: \"auto\" | \"manual\"\r\n resources?: string[]\r\n calendarId?: string\r\n custom?: Record<string, unknown>\r\n}\r\n\r\nexport const defaultConfig: GanttConfig = {\r\n viewMode: \"month\",\r\n rowHeight: 44,\r\n columnWidth: 30,\r\n headerHeight: 50,\r\n taskListWidth: 280,\r\n locale: \"zh-CN\",\r\n firstDayOfWeek: 0,\r\n dateFormat: \"YYYY-MM-DD\",\r\n showPlanBar: true,\r\n showActualBar: true,\r\n showTimelineWhenEmpty: false,\r\n builtInTaskEditor: true,\r\n builtInMarkerEditor: true,\r\n editablePlan: false,\r\n editableActual: true,\r\n enableLinkCreation: true,\r\n showLinkRejectionNotice: true,\r\n virtualScroll: true,\r\n taskColors: { ...DEFAULT_TASK_COLORS },\r\n timeUnit: \"day\",\r\n hourWidth: 12,\r\n autoSchedule: true,\r\n editable: true\r\n}\r\n","import type { GanttLink } from \"../types\"\r\n\r\nexport function checkCyclicDependency(\r\n links: GanttLink[]\r\n): { hasCycle: boolean; cyclePath?: string[] } {\r\n const graph = new Map<string, string[]>()\r\n\r\n for (const link of links) {\r\n const targets = graph.get(link.sourceId) ?? []\r\n targets.push(link.targetId)\r\n graph.set(link.sourceId, targets)\r\n if (!graph.has(link.targetId)) {\r\n graph.set(link.targetId, [])\r\n }\r\n }\r\n\r\n const visiting = new Set<string>()\r\n const visited = new Set<string>()\r\n const path: string[] = []\r\n\r\n const dfs = (node: string): string[] | undefined => {\r\n if (visiting.has(node)) {\r\n const start = path.indexOf(node)\r\n return [...path.slice(start), node]\r\n }\r\n if (visited.has(node)) {\r\n return undefined\r\n }\r\n\r\n visiting.add(node)\r\n path.push(node)\r\n\r\n for (const next of graph.get(node) ?? []) {\r\n const cycle = dfs(next)\r\n if (cycle) {\r\n return cycle\r\n }\r\n }\r\n\r\n path.pop()\r\n visiting.delete(node)\r\n visited.add(node)\r\n return undefined\r\n }\r\n\r\n for (const node of graph.keys()) {\r\n const cyclePath = dfs(node)\r\n if (cyclePath) {\r\n return { hasCycle: true, cyclePath }\r\n }\r\n }\r\n\r\n return { hasCycle: false }\r\n}\r\n","import type { GanttLink, GanttTask } from \"../types\"\r\n\r\nexport function normalizeLinks(tasks: GanttTask[], standaloneLinks: GanttLink[] = []): GanttLink[] {\r\n const taskIds = new Set(tasks.map((task) => task.id))\r\n const byTaskPair = new Map<string, GanttLink>()\r\n const outgoing = new Map<string, Set<string>>()\r\n\r\n const createsCycle = (sourceId: string, targetId: string): boolean => {\r\n const pending = [targetId]\r\n const visited = new Set<string>()\r\n while (pending.length > 0) {\r\n const current = pending.pop()!\r\n if (current === sourceId) {\r\n return true\r\n }\r\n if (visited.has(current)) {\r\n continue\r\n }\r\n visited.add(current)\r\n pending.push(...(outgoing.get(current) ?? []))\r\n }\r\n return false\r\n }\r\n\r\n const append = (link: GanttLink) => {\r\n if (\r\n !taskIds.has(link.sourceId)\r\n || !taskIds.has(link.targetId)\r\n || link.sourceId === link.targetId\r\n ) {\r\n return\r\n }\r\n\r\n const normalized: GanttLink = {\r\n ...link,\r\n id: link.id || `link-${link.sourceId}-${link.targetId}`,\r\n lag: link.lag ?? 0,\r\n lagUnit: link.lagUnit ?? \"calendar\"\r\n }\r\n const pairKey = `${normalized.sourceId}|${normalized.targetId}`\r\n if (byTaskPair.has(pairKey) || createsCycle(normalized.sourceId, normalized.targetId)) {\r\n return\r\n }\r\n\r\n byTaskPair.set(pairKey, normalized)\r\n const targets = outgoing.get(normalized.sourceId) ?? new Set<string>()\r\n targets.add(normalized.targetId)\r\n outgoing.set(normalized.sourceId, targets)\r\n }\r\n\r\n for (const task of tasks) {\r\n for (const dependency of task.dependencies ?? []) {\r\n append({\r\n id: dependency.id || `link-${dependency.predecessorId}-${task.id}`,\r\n sourceId: dependency.predecessorId,\r\n targetId: task.id,\r\n type: dependency.type,\r\n lag: dependency.lag,\r\n lagUnit: dependency.lagUnit\r\n })\r\n }\r\n }\r\n\r\n for (const link of standaloneLinks) {\r\n append(link)\r\n }\r\n\r\n return [...byTaskPair.values()]\r\n}\r\n","import type { FlatTask, GanttTask } from \"../types\"\r\n\r\nexport function flattenTasks(tasks: GanttTask[], collapsedIds: Set<string> = new Set()): FlatTask[] {\r\n const children = new Map<string | null, GanttTask[]>()\r\n\r\n for (const task of tasks) {\r\n const parentId = task.parentId ?? null\r\n const bucket = children.get(parentId) ?? []\r\n bucket.push(task)\r\n children.set(parentId, bucket)\r\n }\r\n\r\n const flattened: FlatTask[] = []\r\n const visit = (task: GanttTask, depth: number) => {\r\n const childTasks = children.get(task.id) ?? []\r\n const collapsed = collapsedIds.has(task.id)\r\n\r\n flattened.push({\r\n task,\r\n depth,\r\n rowIndex: flattened.length,\r\n hasChildren: childTasks.length > 0,\r\n collapsed\r\n })\r\n\r\n if (!collapsed) {\r\n for (const child of childTasks) {\r\n visit(child, depth + 1)\r\n }\r\n }\r\n }\r\n\r\n for (const root of children.get(null) ?? []) {\r\n visit(root, 0)\r\n }\r\n\r\n return flattened\r\n}\r\n","import type { Result } from \"../types\"\r\n\r\nexport const DAY_MS = 24 * 60 * 60 * 1000\r\nexport const HOUR_MS = 60 * 60 * 1000\r\n\r\nexport function toDate(value: string | Date): Date {\r\n if (value instanceof Date) {\r\n return startOfDay(value)\r\n }\r\n\r\n const match = /^(\\d{4})-(\\d{2})-(\\d{2})/.exec(value)\r\n if (match) {\r\n return new Date(Number(match[1]), Number(match[2]) - 1, Number(match[3]))\r\n }\r\n\r\n return startOfDay(new Date(value))\r\n}\r\n\r\n/** 字符串是否携带时间(\"T09:00\" 或 \" 09:00\")。Date 实例一律视为已带时间 */\r\nexport function hasTimeComponent(value: string | Date): boolean {\r\n return typeof value === \"string\" && /T\\d|:\\d/.test(value)\r\n}\r\n\r\n/**\r\n * 解析为保留时间部分的 Date。\r\n * 时间字符串(如 \"2026-07-02T09:00\" / \"2026-07-02 09:00\")按本地时间解析;\r\n * 纯日期字符串(如 \"2026-07-02\")回退到 toDate 的本地零点语义。\r\n */\r\nexport function toDateTime(value: string | Date): Date {\r\n if (value instanceof Date) {\r\n return new Date(value.getTime())\r\n }\r\n if (hasTimeComponent(value)) {\r\n const date = new Date(value.replace(\" \", \"T\"))\r\n if (!Number.isNaN(date.getTime())) {\r\n return date\r\n }\r\n }\r\n return toDate(value)\r\n}\r\n\r\nexport function startOfDay(date: Date): Date {\r\n return new Date(date.getFullYear(), date.getMonth(), date.getDate())\r\n}\r\n\r\nexport function addDays(date: string | Date, days: number): Date {\r\n const next = toDate(date)\r\n next.setDate(next.getDate() + days)\r\n return startOfDay(next)\r\n}\r\n\r\nexport function diffDays(start: string | Date, end: string | Date): number {\r\n return Math.round((toDate(end).getTime() - toDate(start).getTime()) / DAY_MS)\r\n}\r\n\r\nexport function addHours(date: string | Date, hours: number): Date {\r\n return new Date(toDateTime(date).getTime() + hours * HOUR_MS)\r\n}\r\n\r\n/** 小时差(可含分钟的小数,如 09:00-11:30 = 2.5) */\r\nexport function diffHours(start: string | Date, end: string | Date): number {\r\n return (toDateTime(end).getTime() - toDateTime(start).getTime()) / HOUR_MS\r\n}\r\n\r\nexport function inclusiveDays(start: string | Date, end: string | Date): number {\r\n return Math.max(0, diffDays(start, end) + 1)\r\n}\r\n\r\nexport function isValidDate(value: string | Date): boolean {\r\n const date = value instanceof Date ? value : new Date(value)\r\n return !Number.isNaN(date.getTime())\r\n}\r\n\r\nexport function formatDate(date: string | Date): string {\r\n const value = toDate(date)\r\n const year = value.getFullYear()\r\n const month = String(value.getMonth() + 1).padStart(2, \"0\")\r\n const day = String(value.getDate()).padStart(2, \"0\")\r\n return `${year}-${month}-${day}`\r\n}\r\n\r\nexport function formatDateTime(date: string | Date): string {\r\n const value = toDateTime(date)\r\n const hours = String(value.getHours()).padStart(2, \"0\")\r\n const minutes = String(value.getMinutes()).padStart(2, \"0\")\r\n return `${formatDate(value)} ${hours}:${minutes}`\r\n}\r\n\r\nexport function resultInvalidDate<T>(message: string, details?: unknown): Result<T> {\r\n return {\r\n ok: false,\r\n error: { code: \"INVALID_DATE\", message, details }\r\n }\r\n}\r\n","import type { TimeScale, ViewMode } from \"../types\"\r\nimport { addDays, diffDays, startOfDay } from \"../utils/date\"\r\n\r\nexport function computeTimeScale(\r\n start: Date,\r\n end: Date,\r\n viewMode: ViewMode,\r\n columnWidth: number,\r\n firstDayOfWeek: 0 | 1\r\n): TimeScale[] {\r\n const rangeStart = alignStart(start, viewMode, firstDayOfWeek)\r\n const endDate = startOfDay(end)\r\n const rangeEnd = viewMode === \"day\"\r\n ? addDays(endDate, (firstDayOfWeek + 6 - endDate.getDay() + 7) % 7)\r\n : endDate\r\n const scale: TimeScale[] = []\r\n let cursor = rangeStart\r\n let left = 0\r\n\r\n while (cursor.getTime() <= rangeEnd.getTime()) {\r\n const next = nextUnit(cursor, viewMode)\r\n const unitEnd = addDays(next, -1)\r\n const width = Math.max(1, diffDays(cursor, next)) * columnWidth\r\n\r\n scale.push({\r\n start: cursor,\r\n end: unitEnd,\r\n left,\r\n width\r\n })\r\n\r\n left += width\r\n cursor = next\r\n }\r\n\r\n return scale\r\n}\r\n\r\nfunction alignStart(date: Date, viewMode: ViewMode, firstDayOfWeek: 0 | 1): Date {\r\n const start = startOfDay(date)\r\n if (viewMode === \"day\" || viewMode === \"week\") {\r\n const delta = (start.getDay() - firstDayOfWeek + 7) % 7\r\n return addDays(start, -delta)\r\n }\r\n if (viewMode === \"month\") {\r\n return new Date(start.getFullYear(), start.getMonth(), 1)\r\n }\r\n if (viewMode === \"quarter\") {\r\n return new Date(start.getFullYear(), Math.floor(start.getMonth() / 3) * 3, 1)\r\n }\r\n if (viewMode === \"year\") {\r\n return new Date(start.getFullYear(), 0, 1)\r\n }\r\n return start\r\n}\r\n\r\nfunction nextUnit(date: Date, viewMode: ViewMode): Date {\r\n if (viewMode === \"week\") {\r\n return addDays(date, 7)\r\n }\r\n if (viewMode === \"month\") {\r\n return new Date(date.getFullYear(), date.getMonth() + 1, 1)\r\n }\r\n if (viewMode === \"quarter\") {\r\n return new Date(date.getFullYear(), date.getMonth() + 3, 1)\r\n }\r\n if (viewMode === \"year\") {\r\n return new Date(date.getFullYear() + 1, 0, 1)\r\n }\r\n return addDays(date, 1)\r\n}\r\n","import type { TimeScale } from \"../types\"\nimport { addDays, addHours, inclusiveDays, startOfDay } from \"../utils/date\"\n\nexport interface HourScale {\n /** 每日一格(顶行表头用) */\n days: TimeScale[]\n /** 每小时一格(底部刻度用) */\n hours: TimeScale[]\n start: Date\n end: Date\n}\n\n/** 小时级时间刻度:范围对齐到整天,每天拆成 24 个整点小时,width = hourWidth */\nexport function computeHourScale(start: Date, end: Date, hourWidth: number): HourScale {\n const rangeStart = startOfDay(start)\n const rangeEnd = startOfDay(end)\n const totalDays = Math.max(1, inclusiveDays(rangeStart, rangeEnd))\n const days: TimeScale[] = []\n const hours: TimeScale[] = []\n\n for (let d = 0; d < totalDays; d += 1) {\n const dayStart = addDays(rangeStart, d)\n days.push({\n start: dayStart,\n end: addHours(dayStart, 24),\n left: d * 24 * hourWidth,\n width: 24 * hourWidth\n })\n for (let h = 0; h < 24; h += 1) {\n const hourStart = addHours(dayStart, h)\n hours.push({\n start: hourStart,\n end: addHours(hourStart, 1),\n left: (d * 24 + h) * hourWidth,\n width: hourWidth\n })\n }\n }\n\n return { days, hours, start: rangeStart, end: rangeEnd }\n}\n","import type { GanttLink, GanttTask, LinkType, Result } from \"../types\"\r\nimport { addDays, diffDays, inclusiveDays, toDate } from \"../utils/date\"\r\nimport { checkCyclicDependency } from \"../services/checkCyclicDependency\"\r\n\r\nexport function scheduleByDependencies(tasks: GanttTask[], links: GanttLink[]): Result<GanttTask[]> {\r\n const cycle = checkCyclicDependency(links)\r\n if (cycle.hasCycle) {\r\n return {\r\n ok: false,\r\n error: {\r\n code: \"CYCLE_DEPENDENCY\",\r\n message: \"Dependency graph contains a cycle.\",\r\n details: cycle.cyclePath\r\n }\r\n }\r\n }\r\n\r\n const taskMap = new Map(tasks.map((task) => [task.id, cloneTask(task)]))\r\n const incoming = new Map<string, GanttLink[]>()\r\n const outgoing = new Map<string, GanttLink[]>()\r\n\r\n for (const link of links) {\r\n const source = taskMap.get(link.sourceId)\r\n const target = taskMap.get(link.targetId)\r\n if (!source || !target) {\r\n return {\r\n ok: false,\r\n error: {\r\n code: \"ORPHAN_DEPENDENCY\",\r\n message: `Dependency ${link.id} references a missing task.`\r\n }\r\n }\r\n }\r\n incoming.set(link.targetId, [...(incoming.get(link.targetId) ?? []), link])\r\n outgoing.set(link.sourceId, [...(outgoing.get(link.sourceId) ?? []), link])\r\n }\r\n\r\n const ordered = topologicalOrder(tasks.map((task) => task.id), links)\r\n\r\n for (const taskId of ordered) {\r\n const task = taskMap.get(taskId)\r\n if (!task || task.schedulingMode === \"manual\") {\r\n continue\r\n }\r\n\r\n const linksToTask = incoming.get(taskId) ?? []\r\n let start = toDate(task.actual.start)\r\n let end = toDate(task.actual.end)\r\n const duration = Math.max(0, task.duration ?? inclusiveDays(start, end))\r\n\r\n for (const link of linksToTask) {\r\n const source = taskMap.get(link.sourceId)\r\n if (!source) {\r\n continue\r\n }\r\n\r\n const candidate = applyDependency(source, start, end, duration, link.type, link.lag ?? 0)\r\n if (candidate.start.getTime() > start.getTime()) {\r\n start = candidate.start\r\n }\r\n if (candidate.end.getTime() > end.getTime()) {\r\n end = candidate.end\r\n }\r\n }\r\n\r\n const normalizedEnd = task.type === \"milestone\" ? start : addDays(start, Math.max(0, duration - 1))\r\n task.actual = {\r\n ...task.actual,\r\n start,\r\n end: normalizedEnd\r\n }\r\n }\r\n\r\n return { ok: true, data: tasks.map((task) => taskMap.get(task.id) ?? task) }\r\n}\r\n\r\nfunction applyDependency(\r\n source: GanttTask,\r\n currentStart: Date,\r\n currentEnd: Date,\r\n duration: number,\r\n type: LinkType,\r\n lag: number\r\n): { start: Date; end: Date } {\r\n const sourceStart = toDate(source.actual.start)\r\n const sourceEnd = toDate(source.actual.end)\r\n const span = Math.max(0, duration - 1)\r\n\r\n if (type === \"FS\") {\r\n const start = addDays(sourceEnd, lag + 1)\r\n return { start, end: addDays(start, span) }\r\n }\r\n\r\n if (type === \"SS\") {\r\n const start = addDays(sourceStart, lag)\r\n return { start, end: addDays(start, span) }\r\n }\r\n\r\n if (type === \"FF\") {\r\n const end = addDays(sourceEnd, lag)\r\n return { start: addDays(end, -span), end }\r\n }\r\n\r\n const end = addDays(sourceStart, lag)\r\n return { start: addDays(end, -span), end: end.getTime() > currentEnd.getTime() ? end : currentEnd }\r\n}\r\n\r\nfunction topologicalOrder(ids: string[], links: GanttLink[]): string[] {\r\n const indegree = new Map(ids.map((id) => [id, 0]))\r\n const outgoing = new Map<string, string[]>()\r\n\r\n for (const link of links) {\r\n indegree.set(link.targetId, (indegree.get(link.targetId) ?? 0) + 1)\r\n outgoing.set(link.sourceId, [...(outgoing.get(link.sourceId) ?? []), link.targetId])\r\n }\r\n\r\n const queue = ids.filter((id) => (indegree.get(id) ?? 0) === 0)\r\n const ordered: string[] = []\r\n\r\n while (queue.length) {\r\n const current = queue.shift()!\r\n ordered.push(current)\r\n for (const target of outgoing.get(current) ?? []) {\r\n indegree.set(target, (indegree.get(target) ?? 1) - 1)\r\n if (indegree.get(target) === 0) {\r\n queue.push(target)\r\n }\r\n }\r\n }\r\n\r\n return ordered.length === ids.length ? ordered : ids\r\n}\r\n\r\nfunction cloneTask(task: GanttTask): GanttTask {\r\n return {\r\n ...task,\r\n plan: { ...task.plan },\r\n actual: { ...task.actual },\r\n dependencies: task.dependencies?.map((dependency) => ({ ...dependency }))\r\n }\r\n}\r\n\r\nexport function shiftTask(task: GanttTask, start: string | Date): GanttTask {\r\n const currentStart = toDate(task.actual.start)\r\n const nextStart = toDate(start)\r\n const delta = diffDays(currentStart, nextStart)\r\n return {\r\n ...task,\r\n actual: {\r\n ...task.actual,\r\n start: nextStart,\r\n end: addDays(toDate(task.actual.end), delta)\r\n }\r\n }\r\n}\r\n","import type { GanttConfig, GanttLink, GanttTask, Result, TaskLayout, Viewport } from \"../types\"\r\nimport { defaultConfig } from \"../types\"\r\nimport { flattenTasks } from \"../services/flattenTasks\"\r\nimport { normalizeLinks } from \"../services/normalizeLinks\"\r\nimport { scheduleByDependencies } from \"./scheduling\"\r\nimport { diffDays, inclusiveDays, isValidDate, toDate } from \"../utils/date\"\r\n\r\nexport function computeLayout(\r\n tasks: GanttTask[],\r\n links: GanttLink[],\r\n config: Partial<GanttConfig>,\r\n collapsedIds: Set<string> = new Set(),\r\n viewport?: Viewport\r\n): Result<TaskLayout[]> {\r\n const mergedConfig = { ...defaultConfig, ...config }\r\n\r\n for (const task of tasks) {\r\n if (!isValidDate(task.actual.start) || !isValidDate(task.actual.end)) {\r\n return {\r\n ok: false,\r\n error: {\r\n code: \"INVALID_DATE\",\r\n message: `Task ${task.id} has invalid actual dates.`\r\n }\r\n }\r\n }\r\n }\r\n\r\n const normalizedLinks = normalizeLinks(tasks, links)\r\n const scheduled = mergedConfig.autoSchedule === false\r\n ? { ok: true as const, data: tasks }\r\n : scheduleByDependencies(tasks, normalizedLinks)\r\n\r\n if (!scheduled.ok) {\r\n return scheduled\r\n }\r\n\r\n const flatTasks = flattenTasks(scheduled.data, collapsedIds)\r\n const firstDate = mergedConfig.visibleRange\r\n ? toDate(mergedConfig.visibleRange.start)\r\n : minDate(scheduled.data.map((task) => toDate(task.actual.start)))\r\n\r\n const visibleStart = viewport\r\n ? Math.max(0, Math.floor(viewport.scrollTop / mergedConfig.rowHeight) - 8)\r\n : 0\r\n const visibleEnd = viewport\r\n ? Math.ceil((viewport.scrollTop + viewport.clientHeight) / mergedConfig.rowHeight) + 8\r\n : Number.POSITIVE_INFINITY\r\n\r\n const layouts = flatTasks\r\n .filter((flat) => flat.rowIndex >= visibleStart && flat.rowIndex <= visibleEnd)\r\n .map<TaskLayout>((flat) => {\r\n const start = toDate(flat.task.actual.start)\r\n const end = toDate(flat.task.actual.end)\r\n const duration = flat.task.type === \"milestone\" ? 0 : inclusiveDays(start, end)\r\n\r\n return {\r\n taskId: flat.task.id,\r\n rowIndex: flat.rowIndex,\r\n left: diffDays(firstDate, start) * mergedConfig.columnWidth,\r\n width: Math.max(flat.task.type === \"milestone\" ? mergedConfig.columnWidth / 2 : mergedConfig.columnWidth, duration * mergedConfig.columnWidth),\r\n top: mergedConfig.headerHeight + flat.rowIndex * mergedConfig.rowHeight,\r\n depth: flat.depth,\r\n isCritical: false\r\n }\r\n })\r\n\r\n return { ok: true, data: layouts }\r\n}\r\n\r\nfunction minDate(dates: Date[]): Date {\r\n return new Date(Math.min(...dates.map((date) => date.getTime())))\r\n}\r\n","import type { AffectedTasks, GanttConfig, GanttLink, GanttTask, PatchTask, Result } from \"../types\"\r\nimport { flattenTasks } from \"../services/flattenTasks\"\r\nimport { scheduleByDependencies, shiftTask } from \"./scheduling\"\r\nimport { addDays, inclusiveDays, toDate } from \"../utils/date\"\r\n\r\nexport function computeImpact(\r\n taskId: string,\r\n patch: PatchTask,\r\n tasks: GanttTask[],\r\n links: GanttLink[],\r\n config: Partial<GanttConfig>\r\n): Result<AffectedTasks> {\r\n const target = tasks.find((task) => task.id === taskId)\r\n if (!target) {\r\n return {\r\n ok: false,\r\n error: {\r\n code: \"MISSING_TASK\",\r\n message: `Task ${taskId} does not exist.`\r\n }\r\n }\r\n }\r\n\r\n const patchedTasks = tasks.map((task) => {\r\n if (task.id !== taskId) {\r\n return task\r\n }\r\n\r\n let next = { ...task, actual: { ...task.actual } }\r\n if (patch.actualStart) {\r\n next = shiftTask(next, patch.actualStart)\r\n }\r\n if (patch.actualEnd) {\r\n next.actual.end = patch.actualEnd\r\n }\r\n if (typeof patch.progress === \"number\") {\r\n next.actual.progress = patch.progress\r\n }\r\n if (patch.duration) {\r\n next.duration = patch.duration\r\n next.actual.end = addDays(toDate(next.actual.start), Math.max(0, patch.duration - 1))\r\n }\r\n if (patch.name) {\r\n next.name = patch.name\r\n }\r\n if (patch.type) {\r\n next.type = patch.type\r\n }\r\n if (\"parentId\" in patch) {\r\n next.parentId = patch.parentId\r\n }\r\n if (patch.color) {\r\n next.color = patch.color\r\n }\r\n if (\"planColor\" in patch) {\r\n next.planColor = patch.planColor\r\n }\r\n if (patch.schedulingMode) {\r\n next.schedulingMode = patch.schedulingMode\r\n }\r\n return next\r\n })\r\n\r\n const scheduled = config.autoSchedule === false\r\n ? { ok: true as const, data: patchedTasks }\r\n : scheduleByDependencies(patchedTasks, links)\r\n\r\n if (!scheduled.ok) {\r\n return scheduled\r\n }\r\n\r\n const rows = new Map(flattenTasks(scheduled.data).map((flat) => [flat.task.id, flat.rowIndex]))\r\n const originalById = new Map(tasks.map((task) => [task.id, task]))\r\n const changed = scheduled.data\r\n .filter((task) => {\r\n const original = originalById.get(task.id)\r\n return original && (\r\n String(original.actual.start) !== String(task.actual.start) ||\r\n String(original.actual.end) !== String(task.actual.end)\r\n )\r\n })\r\n .map((task) => ({\r\n taskId: task.id,\r\n actualStart: task.actual.start,\r\n actualEnd: task.actual.end,\r\n rowIndex: rows.get(task.id) ?? 0\r\n }))\r\n\r\n const conflicts = scheduled.data\r\n .filter((task) => task.constraint?.date && inclusiveDays(task.actual.start, task.constraint.date) < 0)\r\n .map((task) => ({\r\n taskId: task.id,\r\n constraintType: task.constraint!.type,\r\n message: \"Task violates its configured constraint date.\"\r\n }))\r\n\r\n return { ok: true, data: { changed, conflicts } }\r\n}\r\n","import type {\r\n GanttConfig,\r\n Resource,\r\n ResourceLayout,\r\n ResourceRowLayout,\r\n ResourceUsage,\r\n ResourceUsageLayout,\r\n Result,\r\n Viewport\r\n} from \"../types\"\r\nimport { defaultConfig } from \"../types\"\r\nimport {\r\n addHours,\r\n diffDays,\r\n diffHours,\r\n hasTimeComponent,\r\n inclusiveDays,\r\n isValidDate,\r\n startOfDay,\r\n toDate,\r\n toDateTime\r\n} from \"../utils/date\"\r\nimport { RESOURCE_COLOR_PALETTE } from \"../styles/colors\"\r\n\r\n// 向后兼容:RESOURCE_COLOR_PALETTE 原在本文件导出,继续从这里再导出一次\r\nexport { RESOURCE_COLOR_PALETTE }\r\n\r\nfunction colorHash(source: string): number {\r\n return [...source].reduce((sum, char) => sum + (char.codePointAt(0) ?? 0), 0)\r\n}\r\n\r\n/** 解析资源主色:resource.color → config.resourceColors[id] → 名称 hash 调色板(确定性) */\r\nexport function resolveResourceColor(\r\n resource: Resource,\r\n resourceColors?: Record<string, string>\r\n): string {\r\n if (resource.color) {\r\n return resource.color\r\n }\r\n if (resourceColors?.[resource.id]) {\r\n return resourceColors[resource.id]\r\n }\r\n const source = resource.name || resource.id\r\n return RESOURCE_COLOR_PALETTE[colorHash(source) % RESOURCE_COLOR_PALETTE.length]\r\n}\r\n\r\n/** 解析占用条颜色:usage.color → 所属资源色 */\r\nexport function resolveUsageColor(\r\n usage: ResourceUsage,\r\n resource: Resource,\r\n resourceColors?: Record<string, string>\r\n): string {\r\n return usage.color || resolveResourceColor(resource, resourceColors)\r\n}\r\n\r\n/**\r\n * 将占用解析为布局用的起止 Date。\r\n * - day:与 GanttTask 一致,截断到天。\r\n * - hour:保留时间;纯日期(未携带时间)按\"含结束日整天\"处理,与 day 模式的含末语义一致。\r\n */\r\nexport function resolveUsageTimes(\r\n usage: ResourceUsage,\r\n timeUnit: \"day\" | \"hour\"\r\n): { start: Date; end: Date } {\r\n if (timeUnit === \"hour\") {\r\n const start = toDateTime(usage.start)\r\n const endRaw = toDateTime(usage.end)\r\n return { start, end: hasTimeComponent(usage.end) ? endRaw : addHours(endRaw, 24) }\r\n }\r\n return { start: toDate(usage.start), end: toDate(usage.end) }\r\n}\r\n\r\n/**\r\n * 资源视图布局:每个资源一行(Y 轴),占用时间段以横向色条展示。\r\n * 同一资源同时段多个占用按贪心 first-fit 车道分配,行内堆叠子条。\r\n * day 粒度像素数学与 computeLayout 一致(diffDays × columnWidth);\r\n * hour 粒度按 diffHours × hourWidth 渲染,纯日期占用含结束日整天。\r\n * 传入 viewport 时仅产出可见行窗口内的占用几何(±8 overscan),rows 始终全量返回。\r\n */\r\nexport function computeResourceLayout(\r\n resources: Resource[],\r\n usages: ResourceUsage[],\r\n config: Partial<GanttConfig>,\r\n viewport?: Viewport\r\n): Result<ResourceLayout> {\r\n const mergedConfig = { ...defaultConfig, ...config }\r\n const timeUnit = mergedConfig.timeUnit ?? \"day\"\r\n const hourMode = timeUnit === \"hour\"\r\n const hourW = mergedConfig.hourWidth ?? 12\r\n\r\n for (const usage of usages) {\r\n const { start, end } = resolveUsageTimes(usage, timeUnit)\r\n if (!isValidDate(start) || !isValidDate(end)) {\r\n return {\r\n ok: false,\r\n error: {\r\n code: \"INVALID_DATE\",\r\n message: `Resource usage ${usage.id} has invalid dates.`\r\n }\r\n }\r\n }\r\n }\r\n\r\n // 行序:resources 顺序在前,usage 引用但未声明的 resourceId 追加在后(容错)\r\n const orderedIds: string[] = []\r\n const seen = new Set<string>()\r\n for (const resource of resources) {\r\n if (!seen.has(resource.id)) {\r\n seen.add(resource.id)\r\n orderedIds.push(resource.id)\r\n }\r\n }\r\n for (const usage of usages) {\r\n if (!seen.has(usage.resourceId)) {\r\n seen.add(usage.resourceId)\r\n orderedIds.push(usage.resourceId)\r\n }\r\n }\r\n\r\n const resourceById = new Map(resources.map((resource) => [resource.id, resource]))\r\n const usagesByResource = new Map<string, ResourceUsage[]>()\r\n for (const usage of usages) {\r\n usagesByResource.set(usage.resourceId, [...(usagesByResource.get(usage.resourceId) ?? []), usage])\r\n }\r\n\r\n const rows: ResourceRowLayout[] = []\r\n const usageLayouts: ResourceUsageLayout[] = []\r\n const laneIndexByUsage = new Map<string, number>()\r\n\r\n orderedIds.forEach((resourceId, rowIndex) => {\r\n const resource = resourceById.get(resourceId) ?? {\r\n id: resourceId,\r\n name: resourceId\r\n } satisfies Resource\r\n const grouped = [...(usagesByResource.get(resourceId) ?? [])].sort((a, b) => {\r\n const as = resolveUsageTimes(a, timeUnit)\r\n const bs = resolveUsageTimes(b, timeUnit)\r\n return as.start.getTime() - bs.start.getTime() || as.end.getTime() - bs.end.getTime()\r\n })\r\n\r\n // 贪心 first-fit 车道分配:占用放入第一个\"前一个占用结束不晚于其开始\"的车道。\r\n // day:diffDays(end, start) > 0(相邻日复用);hour:diffHours(end, start) >= 0(首尾相接复用)\r\n const laneEnds: Array<Date | null> = []\r\n for (const usage of grouped) {\r\n const { start, end } = resolveUsageTimes(usage, timeUnit)\r\n let laneIndex = -1\r\n for (let k = 0; k < laneEnds.length; k += 1) {\r\n const laneEnd = laneEnds[k]\r\n if (laneEnd === null || isLaneFree(laneEnd, start, hourMode)) {\r\n laneIndex = k\r\n break\r\n }\r\n }\r\n if (laneIndex === -1) {\r\n laneIndex = laneEnds.length\r\n laneEnds.push(null)\r\n }\r\n laneEnds[laneIndex] = end\r\n laneIndexByUsage.set(usage.id, laneIndex)\r\n }\r\n\r\n rows.push({\r\n resourceId,\r\n rowIndex,\r\n laneCount: laneEnds.length,\r\n color: resolveResourceColor(resource, mergedConfig.resourceColors),\r\n usageIds: grouped.map((usage) => usage.id)\r\n })\r\n })\r\n\r\n const usageById = new Map(usages.map((usage) => [usage.id, usage]))\r\n const visibleStart = viewport\r\n ? Math.max(0, Math.floor(viewport.scrollTop / mergedConfig.rowHeight) - 8)\r\n : 0\r\n const visibleEnd = viewport\r\n ? Math.ceil((viewport.scrollTop + viewport.clientHeight) / mergedConfig.rowHeight) + 8\r\n : Number.POSITIVE_INFINITY\r\n\r\n const firstDate = hourMode\r\n ? startOfDay(\r\n mergedConfig.visibleRange\r\n ? toDateTime(mergedConfig.visibleRange.start)\r\n : usages.length\r\n ? minDate(usages.map((usage) => resolveUsageTimes(usage, \"hour\").start))\r\n : toDateTime(new Date())\r\n )\r\n : mergedConfig.visibleRange\r\n ? toDate(mergedConfig.visibleRange.start)\r\n : usages.length\r\n ? minDate(usages.map((usage) => toDate(usage.start)))\r\n : toDate(new Date())\r\n\r\n for (const row of rows) {\r\n if (row.rowIndex < visibleStart || row.rowIndex > visibleEnd) {\r\n continue\r\n }\r\n const resource = resourceById.get(row.resourceId) ?? { id: row.resourceId, name: row.resourceId }\r\n const laneCount = Math.max(1, row.laneCount)\r\n const slot = mergedConfig.rowHeight / laneCount\r\n const barHeight = Math.max(6, Math.min(14, slot - 4))\r\n\r\n for (const usageId of row.usageIds) {\r\n const usage = usageById.get(usageId)\r\n if (!usage) {\r\n continue\r\n }\r\n const { start, end } = resolveUsageTimes(usage, timeUnit)\r\n const laneIndex = laneIndexByUsage.get(usageId) ?? 0\r\n const laneTop = laneIndex * slot + (slot - barHeight) / 2\r\n const left = hourMode\r\n ? diffHours(firstDate, start) * hourW\r\n : diffDays(firstDate, start) * mergedConfig.columnWidth\r\n const width = hourMode\r\n ? Math.max(hourW, diffHours(start, end) * hourW)\r\n : Math.max(mergedConfig.columnWidth, inclusiveDays(start, end) * mergedConfig.columnWidth)\r\n\r\n usageLayouts.push({\r\n usageId,\r\n resourceId: row.resourceId,\r\n rowIndex: row.rowIndex,\r\n laneIndex,\r\n laneCount,\r\n left,\r\n width,\r\n top: mergedConfig.headerHeight + row.rowIndex * mergedConfig.rowHeight + laneTop,\r\n height: barHeight\r\n })\r\n }\r\n }\r\n\r\n return { ok: true, data: { rows, usages: usageLayouts } }\r\n}\r\n\r\nfunction isLaneFree(laneEnd: Date, start: Date, hourMode: boolean): boolean {\r\n return hourMode ? diffHours(laneEnd, start) >= 0 : diffDays(laneEnd, start) > 0\r\n}\r\n\r\nfunction minDate(dates: Date[]): Date {\r\n return new Date(Math.min(...dates.map((date) => date.getTime())))\r\n}\r\n","import type {\r\n GanttConfig,\r\n GanttTask,\r\n GanttLink,\r\n GanttMarker,\r\n PatchTask,\r\n TaskLayout,\r\n TimeScale,\r\n FlatTask,\r\n Result\r\n} from \"../types\"\r\nimport { defaultConfig } from \"../types\"\r\nimport { computeLayout } from \"../engines/computeLayout\"\r\nimport { computeTimeScale } from \"../services/computeTimeScale\"\r\nimport { flattenTasks } from \"../services/flattenTasks\"\r\nimport { toDate, diffDays, addDays } from \"../utils/date\"\r\n\r\n/**\r\n * 把扁平的 PatchTask 合并回嵌套的 GanttTask。\r\n * PatchTask 用 planStart/planEnd/actualStart/actualEnd 表示日期,\r\n * GanttTask 用 plan.start/end、actual.start/end 嵌套表示——这是组件层\r\n * 和数据层之间的桥,本该在引擎层而不是散落在各 demo 里。\r\n */\r\nexport function mergeTaskPatch(task: GanttTask, patch: PatchTask): GanttTask {\r\n return {\r\n ...task,\r\n ...patch,\r\n plan: {\r\n ...task.plan,\r\n start: patch.planStart ?? task.plan.start,\r\n end: patch.planEnd ?? task.plan.end,\r\n progress: patch.progress ?? task.plan.progress\r\n },\r\n actual: {\r\n ...task.actual,\r\n start: patch.actualStart ?? task.actual.start,\r\n end: patch.actualEnd ?? task.actual.end,\r\n progress: patch.progress ?? task.actual.progress\r\n },\r\n custom: patch.custom ? { ...task.custom, ...patch.custom } : task.custom\r\n }\r\n}\r\n\r\nexport interface GanttEngineOptions {\r\n tasks?: GanttTask[]\r\n links?: GanttLink[]\r\n config?: Partial<GanttConfig>\r\n /** 滚动容器元素;命令式视口 API(scrollToDate / zoomToFit)需要它。纯计算场景可不传。 */\r\n container?: HTMLElement | null\r\n /** 初始折叠的任务 id */\r\n collapsedIds?: Iterable<string>\r\n /** 里程碑标记 */\r\n markers?: GanttMarker[]\r\n}\r\n\r\nexport type GanttEngineEvent =\r\n | \"ready\"\r\n | \"taskschange\"\r\n | \"taskchange\"\r\n | \"taskcreate\"\r\n | \"taskdelete\"\r\n | \"linkschange\"\r\n | \"configchange\"\r\n | \"collapsechange\"\r\n | \"previewchange\"\r\n | \"markerschange\"\r\n | \"destroy\"\r\n\r\ntype Listener = (...args: unknown[]) => void\r\n\r\n/** 命令式甘特图引擎。框架无关,可被 Vue/React/原生 各自的薄壳组件驱动。 */\r\nexport class GanttEngine {\r\n private tasks: GanttTask[]\r\n private links: GanttLink[]\r\n private config: GanttConfig\r\n private readonly container: HTMLElement | null\r\n private collapsedIds: Set<string>\r\n private dragPreview: { taskId: string; patch: PatchTask; affected?: Record<string, PatchTask> } | null = null\r\n private markers: GanttMarker[]\r\n private readonly listeners: Map<GanttEngineEvent, Set<Listener>>\r\n private destroyed = false\r\n\r\n constructor(options: GanttEngineOptions = {}) {\r\n this.tasks = options.tasks ? options.tasks.map((task) => ({ ...task })) : []\r\n this.links = options.links ? options.links.map((link) => ({ ...link })) : []\r\n this.config = { ...defaultConfig, ...options.config }\r\n this.container = options.container ?? null\r\n this.collapsedIds = new Set(options.collapsedIds ?? [])\r\n this.markers = options.markers ? options.markers.map((marker) => ({ ...marker })) : []\r\n this.listeners = new Map()\r\n // 异步发 ready,确保订阅者能在构造后、首次 emit 前注册\r\n queueMicrotask(() => this.emit(\"ready\"))\r\n }\r\n\r\n // ── 状态查询 ──\r\n getTasks(): GanttTask[] {\r\n return this.tasks.map((task) => ({ ...task }))\r\n }\r\n getLinks(): GanttLink[] {\r\n return this.links.map((link) => ({ ...link }))\r\n }\r\n getMarkers(): GanttMarker[] {\r\n return this.markers.map((marker) => ({ ...marker }))\r\n }\r\n getConfig(): GanttConfig {\r\n return { ...this.config }\r\n }\r\n getTask(id: string): GanttTask | undefined {\r\n const task = this.tasks.find((item) => item.id === id)\r\n return task ? { ...task } : undefined\r\n }\r\n getCollapsedIds(): string[] {\r\n return Array.from(this.collapsedIds)\r\n }\r\n isDestroyed(): boolean {\r\n return this.destroyed\r\n }\r\n\r\n // ── 命令式变更(均触发对应事件) ──\r\n setTasks(tasks: GanttTask[]): void {\r\n this.assertNotDestroyed()\r\n this.tasks = tasks.map((task) => ({ ...task }))\r\n this.emit(\"taskschange\", this.getTasks())\r\n }\r\n setLinks(links: GanttLink[]): void {\r\n this.assertNotDestroyed()\r\n this.links = links.map((link) => ({ ...link }))\r\n this.emit(\"linkschange\", this.getLinks())\r\n }\r\n setMarkers(markers: GanttMarker[]): void {\r\n this.assertNotDestroyed()\r\n this.markers = markers.map((marker) => ({ ...marker }))\r\n this.emit(\"markerschange\", this.getMarkers())\r\n }\r\n /** 合并配置 patch,等价于 setConfig(mergeConfig(patch)) */\r\n mergeConfig(patch: Partial<GanttConfig>): void {\r\n this.assertNotDestroyed()\r\n this.config = { ...this.config, ...patch }\r\n this.emit(\"configchange\", this.getConfig())\r\n }\r\n setConfig(config: Partial<GanttConfig>): void {\r\n this.mergeConfig(config)\r\n }\r\n /** 用 mergeTaskPatch 把扁平 patch 合并回嵌套结构后更新单个任务 */\r\n setTask(id: string, patch: PatchTask): void {\r\n this.assertNotDestroyed()\r\n const target = this.tasks.find((task) => task.id === id)\r\n if (!target) {\r\n return\r\n }\r\n const updated = mergeTaskPatch(target, patch)\r\n this.tasks = this.tasks.map((task) => (task.id === id ? updated : task))\r\n this.emit(\"taskchange\", id, patch, { ...updated })\r\n }\r\n addTask(task: GanttTask): void {\r\n this.assertNotDestroyed()\r\n this.tasks = [...this.tasks, { ...task }]\r\n this.emit(\"taskcreate\", { ...task })\r\n }\r\n removeTask(id: string): void {\r\n this.assertNotDestroyed()\r\n const removed = this.tasks.find((task) => task.id === id)\r\n if (!removed) return\r\n this.tasks = this.tasks.filter((task) => task.id !== id)\r\n this.collapsedIds.delete(id)\r\n this.emit(\"taskdelete\", id)\r\n }\r\n collapse(id: string, collapsed = true): void {\r\n this.assertNotDestroyed()\r\n if (collapsed) this.collapsedIds.add(id)\r\n else this.collapsedIds.delete(id)\r\n this.emit(\"collapsechange\", this.getCollapsedIds())\r\n }\r\n toggleCollapse(id: string): void {\r\n this.collapse(id, !this.collapsedIds.has(id))\r\n }\r\n /** 批量折叠/展开多个任务(如\"折叠全部分组\") */\r\n setCollapsed(ids: string[], collapsed = true): void {\r\n this.assertNotDestroyed()\r\n const next = new Set(this.collapsedIds)\r\n for (const id of ids) {\r\n if (collapsed) {\r\n next.add(id)\r\n } else {\r\n next.delete(id)\r\n }\r\n }\r\n this.collapsedIds = next\r\n this.emit(\"collapsechange\", this.getCollapsedIds())\r\n }\r\n\r\n // ── 拖拽预览(交互态源;组件写入迁移到 engine,dateRange 可据此扩展) ──\r\n setPreview(preview: { taskId: string; patch: PatchTask; affected?: Record<string, PatchTask> }): void {\r\n this.assertNotDestroyed()\r\n this.dragPreview = {\r\n taskId: preview.taskId,\r\n patch: { ...preview.patch },\r\n affected: preview.affected ? { ...preview.affected } : undefined\r\n }\r\n this.emit(\"previewchange\", this.getPreview())\r\n }\r\n clearPreview(): void {\r\n if (!this.dragPreview) {\r\n return\r\n }\r\n this.dragPreview = null\r\n this.emit(\"previewchange\", null)\r\n }\r\n getPreview(): { taskId: string; patch: PatchTask; affected?: Record<string, PatchTask> } | null {\r\n if (!this.dragPreview) {\r\n return null\r\n }\r\n return {\r\n taskId: this.dragPreview.taskId,\r\n patch: { ...this.dragPreview.patch },\r\n affected: this.dragPreview.affected ? { ...this.dragPreview.affected } : undefined\r\n }\r\n }\r\n\r\n // ── 计算(delegate 到 core 纯函数) ──\r\n /** 数据真实日期范围(未含 viewport pad) */\r\n getDateRange(): { start: Date; end: Date } {\r\n const dates: Date[] = []\r\n for (const task of this.tasks) {\r\n dates.push(\r\n toDate(task.plan.start),\r\n toDate(task.plan.end),\r\n toDate(task.actual.start),\r\n toDate(task.actual.end)\r\n )\r\n }\r\n for (const marker of this.markers) {\r\n dates.push(toDate(marker.date))\r\n }\r\n // 合并拖拽预览:用 mergeTaskPatch 算 patch 后日期扩范围(只扩不缩)\r\n const preview = this.dragPreview\r\n if (preview) {\r\n const ids = [preview.taskId, ...Object.keys(preview.affected ?? {})]\r\n for (const id of ids) {\r\n const task = this.tasks.find((item) => item.id === id)\r\n if (!task) {\r\n continue\r\n }\r\n const patch = preview.taskId === id ? preview.patch : preview.affected?.[id]\r\n if (!patch) {\r\n continue\r\n }\r\n const merged = mergeTaskPatch(task, patch)\r\n dates.push(\r\n toDate(merged.plan.start),\r\n toDate(merged.plan.end),\r\n toDate(merged.actual.start),\r\n toDate(merged.actual.end)\r\n )\r\n }\r\n }\r\n if (!dates.length) {\r\n const today = toDate(new Date())\r\n return { start: today, end: addDays(today, 30) }\r\n }\r\n return {\r\n start: new Date(Math.min(...dates.map((d) => d.getTime()))),\r\n end: new Date(Math.max(...dates.map((d) => d.getTime())))\r\n }\r\n }\r\n getTimeScale(): TimeScale[] {\r\n const range = this.getDateRange()\r\n return computeTimeScale(\r\n range.start,\r\n range.end,\r\n this.config.viewMode,\r\n this.config.columnWidth,\r\n this.config.firstDayOfWeek\r\n )\r\n }\r\n getLayout(): Result<TaskLayout[]> {\r\n return computeLayout(\r\n this.tasks,\r\n this.links,\r\n this.config,\r\n this.collapsedIds\r\n )\r\n }\r\n getFlatTasks(): FlatTask[] {\r\n return flattenTasks(this.tasks, this.collapsedIds)\r\n }\r\n getTotalWidth(): number {\r\n return this.getTimeScale().reduce((sum, tick) => sum + tick.width, 0)\r\n }\r\n getTotalHeight(): number {\r\n const rows = this.getFlatTasks().length\r\n return this.config.headerHeight + rows * this.config.rowHeight\r\n }\r\n\r\n // ── 视口命令(需要 container) ──\r\n getScrollLeft(): number {\r\n return this.container?.scrollLeft ?? 0\r\n }\r\n setScrollLeft(px: number): void {\r\n if (!this.container) return\r\n const max = Math.max(0, this.container.scrollWidth - this.container.clientWidth)\r\n this.container.scrollLeft = Math.max(0, Math.min(max, px))\r\n }\r\n /** 把当前 scrollLeft 限制到 [0, maxScrollLeft]。\r\n * 拖拽预览收缩(松手/取消)后 timeline 总宽度变小,scrollLeft 可能超出新边界,\r\n * 导致 canvas translate 超出内容区、右侧网格未覆盖。此时调用本方法防超界。 */\r\n clampScrollLeft(): void {\r\n if (!this.container) return\r\n const max = Math.max(0, this.container.scrollWidth - this.container.clientWidth)\r\n if (this.container.scrollLeft > max) {\r\n this.container.scrollLeft = max\r\n }\r\n }\r\n getScrollTop(): number {\r\n return this.container?.scrollTop ?? 0\r\n }\r\n setScrollTop(px: number): void {\r\n if (!this.container) return\r\n const max = Math.max(0, this.container.scrollHeight - this.container.clientHeight)\r\n this.container.scrollTop = Math.max(0, Math.min(max, px))\r\n }\r\n /** 滚动到包含目标日期的 tick 列(跨 viewMode 精确) */\r\n scrollToDate(date: string | Date): void {\r\n if (!this.container) return\r\n const target = toDate(date)\r\n const scale = this.getTimeScale()\r\n const tick = scale.find((t) => target >= t.start && target <= t.end)\r\n const left = tick ? tick.left : diffDays(this.getDateRange().start, target) * this.config.columnWidth\r\n this.setScrollLeft(left)\r\n }\r\n scrollToTask(id: string): void {\r\n const layout = this.getLayout()\r\n if (!layout.ok) return\r\n const item = layout.data.find((row) => row.taskId === id)\r\n if (item) this.setScrollLeft(item.left)\r\n }\r\n scrollToStart(): void {\r\n this.setScrollLeft(0)\r\n }\r\n scrollToEnd(): void {\r\n if (!this.container) return\r\n this.setScrollLeft(this.getTotalWidth())\r\n }\r\n /** 让整个 timeline 总宽度≈视口宽度(调 columnWidth) */\r\n zoomToFit(padding = 1): void {\r\n if (!this.container) return\r\n const spanDays = Math.max(1, diffDays(this.getDateRange().start, this.getDateRange().end) + 1)\r\n const target = (this.container.clientWidth * padding) / spanDays\r\n this.mergeConfig({ columnWidth: this.clampColumnWidth(target) })\r\n }\r\n /** 让指定日期范围占满视口宽度(调 columnWidth) */\r\n zoomToRange(start: string | Date, end: string | Date): void {\r\n if (!this.container) return\r\n const span = Math.max(1, diffDays(start, end) + 1)\r\n const target = this.container.clientWidth / span\r\n this.mergeConfig({ columnWidth: this.clampColumnWidth(target) })\r\n }\r\n private clampColumnWidth(value: number): number {\r\n return Math.max(4, Math.min(400, Math.round(value)))\r\n }\r\n\r\n // ── 事件订阅 ──\r\n on(event: GanttEngineEvent, fn: Listener): () => void {\r\n this.assertNotDestroyed()\r\n let set = this.listeners.get(event)\r\n if (!set) {\r\n set = new Set()\r\n this.listeners.set(event, set)\r\n }\r\n set.add(fn)\r\n return () => {\r\n const current = this.listeners.get(event)\r\n if (current) {\r\n current.delete(fn)\r\n if (!current.size) this.listeners.delete(event)\r\n }\r\n }\r\n }\r\n once(event: GanttEngineEvent, fn: Listener): () => void {\r\n const off = this.on(event, (...args: unknown[]) => {\r\n off()\r\n fn(...args)\r\n })\r\n return off\r\n }\r\n off(event: GanttEngineEvent, fn?: Listener): void {\r\n const set = this.listeners.get(event)\r\n if (!set) return\r\n if (fn) {\r\n set.delete(fn)\r\n if (!set.size) this.listeners.delete(event)\r\n } else {\r\n this.listeners.delete(event)\r\n }\r\n }\r\n emit(event: GanttEngineEvent, ...args: unknown[]): void {\r\n const set = this.listeners.get(event)\r\n if (!set) return\r\n // 复制遍历,避免回调中增删导致迭代错乱\r\n for (const fn of Array.from(set)) {\r\n try {\r\n fn(...args)\r\n } catch (error) {\r\n // 单个监听器抛错不应打断其他监听器\r\n if (typeof console !== \"undefined\") {\r\n console.error(`[GanttEngine] listener for \"${event}\" threw`, error)\r\n }\r\n }\r\n }\r\n }\r\n\r\n // ── 生命周期 ──\r\n destroy(): void {\r\n if (this.destroyed) return\r\n this.destroyed = true\r\n this.emit(\"destroy\")\r\n this.listeners.clear()\r\n this.tasks = []\r\n this.links = []\r\n }\r\n\r\n private assertNotDestroyed(): void {\r\n if (this.destroyed) {\r\n throw new Error(\"GanttEngine: operation on destroyed instance\")\r\n }\r\n }\r\n}\r\n\r\n/** 工厂函数,对应成熟做法的 new GanttEngine(container, config) 入口 */\r\nexport function createGanttEngine(options: GanttEngineOptions = {}): GanttEngine {\r\n return new GanttEngine(options)\r\n}\r\n"],"mappings":"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","names":["end","minDate"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -67,8 +67,13 @@ interface GanttEditorField {
|
|
|
67
67
|
}>;
|
|
68
68
|
placeholder?: string;
|
|
69
69
|
}
|
|
70
|
+
/** 内置任务字段键。列可通过 CustomColumn.field 绑定到这些内置字段。
|
|
71
|
+
* 除 status / owner 外都对应任务对象上的真实字段;status 与 owner 是派生展示字段。 */
|
|
72
|
+
type BuiltinFieldKey = "name" | "status" | "owner" | "resources" | "type" | "parentId" | "planStart" | "planEnd" | "actualStart" | "actualEnd" | "progress" | "duration" | "calendarId" | "schedulingMode" | "color" | "planColor";
|
|
70
73
|
interface CustomColumn {
|
|
71
74
|
key: string;
|
|
75
|
+
/** 绑定到的内置任务字段键。缺省时列按自定义列处理,从 task.custom[key] 读取/编辑;设置后列读取/编辑该内置字段,key 仍作为列标识。 */
|
|
76
|
+
field?: BuiltinFieldKey;
|
|
72
77
|
label: string;
|
|
73
78
|
width?: number;
|
|
74
79
|
visible?: boolean;
|
|
@@ -126,11 +131,19 @@ interface GanttConfig {
|
|
|
126
131
|
plan?: string;
|
|
127
132
|
progress?: string;
|
|
128
133
|
};
|
|
134
|
+
/** 小时视图独立配色:plan/progress/milestone 缺省时回退到 taskColors 与 DEFAULT_HOUR_COLORS */
|
|
135
|
+
hourColors?: {
|
|
136
|
+
plan?: string;
|
|
137
|
+
progress?: string;
|
|
138
|
+
milestone?: string;
|
|
139
|
+
};
|
|
129
140
|
resourceColors?: Record<string, string>;
|
|
130
141
|
/** 资源视图时间粒度;day 按整天渲染,hour 按占用携带的小时渲染(仅 ResourceTimeline 使用) */
|
|
131
142
|
timeUnit?: "day" | "hour";
|
|
132
143
|
/** 资源视图 hour 粒度下每小时像素宽 */
|
|
133
144
|
hourWidth?: number;
|
|
145
|
+
/** 小时视图计划条高度;缺省时随 rowHeight 自动推导(与资源视图单车道条高公式一致) */
|
|
146
|
+
barHeight?: number;
|
|
134
147
|
autoSchedule?: boolean;
|
|
135
148
|
editable?: boolean;
|
|
136
149
|
}
|
|
@@ -254,6 +267,37 @@ interface PatchTask {
|
|
|
254
267
|
}
|
|
255
268
|
declare const defaultConfig: GanttConfig;
|
|
256
269
|
|
|
270
|
+
/**
|
|
271
|
+
* 默认颜色统一配置 —— 甘特图 / 资源视图 / 小时视图的默认条色与调色板都从这里读取。
|
|
272
|
+
*
|
|
273
|
+
* 想更换「默认配色」时改这一个文件即可全局生效;单个实例仍可通过
|
|
274
|
+
* config.taskColors / config.hourColors / config.resourceColors / 任务 color、planColor 字段按需覆盖。
|
|
275
|
+
*
|
|
276
|
+
* 默认色按视图拆分:
|
|
277
|
+
* - DEFAULT_TASK_COLORS:甘特图(实际条 / 计划条 / 阶段 / 里程碑),也是资源视图与小时视图的共享回退。
|
|
278
|
+
* - DEFAULT_HOUR_COLORS:小时视图计划条与里程碑的独立默认,互不影响甘特图。
|
|
279
|
+
*/
|
|
280
|
+
/** 甘特图默认语义色(对应 GanttConfig.taskColors) */
|
|
281
|
+
declare const DEFAULT_TASK_COLORS: {
|
|
282
|
+
task: string;
|
|
283
|
+
summary: string;
|
|
284
|
+
milestone: string;
|
|
285
|
+
plan: string;
|
|
286
|
+
progress: string;
|
|
287
|
+
};
|
|
288
|
+
/** 小时视图计划条与里程碑默认色(对应 GanttConfig.hourColors);独立于甘特图,可单独修改 */
|
|
289
|
+
declare const DEFAULT_HOUR_COLORS: {
|
|
290
|
+
plan: string;
|
|
291
|
+
progress: string;
|
|
292
|
+
milestone: string;
|
|
293
|
+
};
|
|
294
|
+
/** 资源视图未指定颜色时,按资源名哈希从该调色板确定性取色 */
|
|
295
|
+
declare const RESOURCE_COLOR_PALETTE: string[];
|
|
296
|
+
/** 内置任务编辑器 / 里程碑编辑器的可选颜色板 */
|
|
297
|
+
declare const EDITOR_COLOR_OPTIONS: string[];
|
|
298
|
+
/** 左侧列表负责人头像兜底色板 */
|
|
299
|
+
declare const OWNER_COLOR_PALETTE: string[];
|
|
300
|
+
|
|
257
301
|
declare function checkCyclicDependency(links: GanttLink[]): {
|
|
258
302
|
hasCycle: boolean;
|
|
259
303
|
cyclePath?: string[];
|
|
@@ -280,8 +324,6 @@ declare function computeLayout(tasks: GanttTask[], links: GanttLink[], config: P
|
|
|
280
324
|
|
|
281
325
|
declare function computeImpact(taskId: string, patch: PatchTask, tasks: GanttTask[], links: GanttLink[], config: Partial<GanttConfig>): Result<AffectedTasks>;
|
|
282
326
|
|
|
283
|
-
/** 资源色条调色板,与 GanttChart owner 头像的 5 色调色板一致 */
|
|
284
|
-
declare const RESOURCE_COLOR_PALETTE: string[];
|
|
285
327
|
/** 解析资源主色:resource.color → config.resourceColors[id] → 名称 hash 调色板(确定性) */
|
|
286
328
|
declare function resolveResourceColor(resource: Resource, resourceColors?: Record<string, string>): string;
|
|
287
329
|
/** 解析占用条颜色:usage.color → 所属资源色 */
|
|
@@ -432,4 +474,4 @@ declare class GanttEngine {
|
|
|
432
474
|
/** 工厂函数,对应成熟做法的 new GanttEngine(container, config) 入口 */
|
|
433
475
|
declare function createGanttEngine(options?: GanttEngineOptions): GanttEngine;
|
|
434
476
|
|
|
435
|
-
export { type AffectedTasks, type Calendar, type CustomColumn, DAY_MS, type DateRange, type Dependency, type FlatTask, type GanttConfig, type GanttEditorField, GanttEngine, type GanttEngineEvent, type GanttEngineOptions, type GanttError, type GanttLink, type GanttMarker, type GanttTask, HOUR_MS, type HourScale, type LagUnit, type LinkType, type PatchTask, RESOURCE_COLOR_PALETTE, type Resource, type ResourceLayout, type ResourceRowLayout, type ResourceUsage, type ResourceUsageLayout, type ResourceUsageType, type Result, type TaskLayout, type TimeScale, type ViewMode, type Viewport, addDays, addHours, checkCyclicDependency, computeHourScale, computeImpact, computeLayout, computeResourceLayout, computeTimeScale, createGanttEngine, defaultConfig, diffDays, diffHours, flattenTasks, formatDate, formatDateTime, hasTimeComponent, inclusiveDays, isValidDate, mergeTaskPatch, normalizeLinks, resolveResourceColor, resolveUsageColor, resolveUsageTimes, resultInvalidDate, scheduleByDependencies, shiftTask, startOfDay, toDate, toDateTime };
|
|
477
|
+
export { type AffectedTasks, type BuiltinFieldKey, type Calendar, type CustomColumn, DAY_MS, DEFAULT_HOUR_COLORS, DEFAULT_TASK_COLORS, type DateRange, type Dependency, EDITOR_COLOR_OPTIONS, type FlatTask, type GanttConfig, type GanttEditorField, GanttEngine, type GanttEngineEvent, type GanttEngineOptions, type GanttError, type GanttLink, type GanttMarker, type GanttTask, HOUR_MS, type HourScale, type LagUnit, type LinkType, OWNER_COLOR_PALETTE, type PatchTask, RESOURCE_COLOR_PALETTE, type Resource, type ResourceLayout, type ResourceRowLayout, type ResourceUsage, type ResourceUsageLayout, type ResourceUsageType, type Result, type TaskLayout, type TimeScale, type ViewMode, type Viewport, addDays, addHours, checkCyclicDependency, computeHourScale, computeImpact, computeLayout, computeResourceLayout, computeTimeScale, createGanttEngine, defaultConfig, diffDays, diffHours, flattenTasks, formatDate, formatDateTime, hasTimeComponent, inclusiveDays, isValidDate, mergeTaskPatch, normalizeLinks, resolveResourceColor, resolveUsageColor, resolveUsageTimes, resultInvalidDate, scheduleByDependencies, shiftTask, startOfDay, toDate, toDateTime };
|
package/dist/index.d.ts
CHANGED
|
@@ -67,8 +67,13 @@ interface GanttEditorField {
|
|
|
67
67
|
}>;
|
|
68
68
|
placeholder?: string;
|
|
69
69
|
}
|
|
70
|
+
/** 内置任务字段键。列可通过 CustomColumn.field 绑定到这些内置字段。
|
|
71
|
+
* 除 status / owner 外都对应任务对象上的真实字段;status 与 owner 是派生展示字段。 */
|
|
72
|
+
type BuiltinFieldKey = "name" | "status" | "owner" | "resources" | "type" | "parentId" | "planStart" | "planEnd" | "actualStart" | "actualEnd" | "progress" | "duration" | "calendarId" | "schedulingMode" | "color" | "planColor";
|
|
70
73
|
interface CustomColumn {
|
|
71
74
|
key: string;
|
|
75
|
+
/** 绑定到的内置任务字段键。缺省时列按自定义列处理,从 task.custom[key] 读取/编辑;设置后列读取/编辑该内置字段,key 仍作为列标识。 */
|
|
76
|
+
field?: BuiltinFieldKey;
|
|
72
77
|
label: string;
|
|
73
78
|
width?: number;
|
|
74
79
|
visible?: boolean;
|
|
@@ -126,11 +131,19 @@ interface GanttConfig {
|
|
|
126
131
|
plan?: string;
|
|
127
132
|
progress?: string;
|
|
128
133
|
};
|
|
134
|
+
/** 小时视图独立配色:plan/progress/milestone 缺省时回退到 taskColors 与 DEFAULT_HOUR_COLORS */
|
|
135
|
+
hourColors?: {
|
|
136
|
+
plan?: string;
|
|
137
|
+
progress?: string;
|
|
138
|
+
milestone?: string;
|
|
139
|
+
};
|
|
129
140
|
resourceColors?: Record<string, string>;
|
|
130
141
|
/** 资源视图时间粒度;day 按整天渲染,hour 按占用携带的小时渲染(仅 ResourceTimeline 使用) */
|
|
131
142
|
timeUnit?: "day" | "hour";
|
|
132
143
|
/** 资源视图 hour 粒度下每小时像素宽 */
|
|
133
144
|
hourWidth?: number;
|
|
145
|
+
/** 小时视图计划条高度;缺省时随 rowHeight 自动推导(与资源视图单车道条高公式一致) */
|
|
146
|
+
barHeight?: number;
|
|
134
147
|
autoSchedule?: boolean;
|
|
135
148
|
editable?: boolean;
|
|
136
149
|
}
|
|
@@ -254,6 +267,37 @@ interface PatchTask {
|
|
|
254
267
|
}
|
|
255
268
|
declare const defaultConfig: GanttConfig;
|
|
256
269
|
|
|
270
|
+
/**
|
|
271
|
+
* 默认颜色统一配置 —— 甘特图 / 资源视图 / 小时视图的默认条色与调色板都从这里读取。
|
|
272
|
+
*
|
|
273
|
+
* 想更换「默认配色」时改这一个文件即可全局生效;单个实例仍可通过
|
|
274
|
+
* config.taskColors / config.hourColors / config.resourceColors / 任务 color、planColor 字段按需覆盖。
|
|
275
|
+
*
|
|
276
|
+
* 默认色按视图拆分:
|
|
277
|
+
* - DEFAULT_TASK_COLORS:甘特图(实际条 / 计划条 / 阶段 / 里程碑),也是资源视图与小时视图的共享回退。
|
|
278
|
+
* - DEFAULT_HOUR_COLORS:小时视图计划条与里程碑的独立默认,互不影响甘特图。
|
|
279
|
+
*/
|
|
280
|
+
/** 甘特图默认语义色(对应 GanttConfig.taskColors) */
|
|
281
|
+
declare const DEFAULT_TASK_COLORS: {
|
|
282
|
+
task: string;
|
|
283
|
+
summary: string;
|
|
284
|
+
milestone: string;
|
|
285
|
+
plan: string;
|
|
286
|
+
progress: string;
|
|
287
|
+
};
|
|
288
|
+
/** 小时视图计划条与里程碑默认色(对应 GanttConfig.hourColors);独立于甘特图,可单独修改 */
|
|
289
|
+
declare const DEFAULT_HOUR_COLORS: {
|
|
290
|
+
plan: string;
|
|
291
|
+
progress: string;
|
|
292
|
+
milestone: string;
|
|
293
|
+
};
|
|
294
|
+
/** 资源视图未指定颜色时,按资源名哈希从该调色板确定性取色 */
|
|
295
|
+
declare const RESOURCE_COLOR_PALETTE: string[];
|
|
296
|
+
/** 内置任务编辑器 / 里程碑编辑器的可选颜色板 */
|
|
297
|
+
declare const EDITOR_COLOR_OPTIONS: string[];
|
|
298
|
+
/** 左侧列表负责人头像兜底色板 */
|
|
299
|
+
declare const OWNER_COLOR_PALETTE: string[];
|
|
300
|
+
|
|
257
301
|
declare function checkCyclicDependency(links: GanttLink[]): {
|
|
258
302
|
hasCycle: boolean;
|
|
259
303
|
cyclePath?: string[];
|
|
@@ -280,8 +324,6 @@ declare function computeLayout(tasks: GanttTask[], links: GanttLink[], config: P
|
|
|
280
324
|
|
|
281
325
|
declare function computeImpact(taskId: string, patch: PatchTask, tasks: GanttTask[], links: GanttLink[], config: Partial<GanttConfig>): Result<AffectedTasks>;
|
|
282
326
|
|
|
283
|
-
/** 资源色条调色板,与 GanttChart owner 头像的 5 色调色板一致 */
|
|
284
|
-
declare const RESOURCE_COLOR_PALETTE: string[];
|
|
285
327
|
/** 解析资源主色:resource.color → config.resourceColors[id] → 名称 hash 调色板(确定性) */
|
|
286
328
|
declare function resolveResourceColor(resource: Resource, resourceColors?: Record<string, string>): string;
|
|
287
329
|
/** 解析占用条颜色:usage.color → 所属资源色 */
|
|
@@ -432,4 +474,4 @@ declare class GanttEngine {
|
|
|
432
474
|
/** 工厂函数,对应成熟做法的 new GanttEngine(container, config) 入口 */
|
|
433
475
|
declare function createGanttEngine(options?: GanttEngineOptions): GanttEngine;
|
|
434
476
|
|
|
435
|
-
export { type AffectedTasks, type Calendar, type CustomColumn, DAY_MS, type DateRange, type Dependency, type FlatTask, type GanttConfig, type GanttEditorField, GanttEngine, type GanttEngineEvent, type GanttEngineOptions, type GanttError, type GanttLink, type GanttMarker, type GanttTask, HOUR_MS, type HourScale, type LagUnit, type LinkType, type PatchTask, RESOURCE_COLOR_PALETTE, type Resource, type ResourceLayout, type ResourceRowLayout, type ResourceUsage, type ResourceUsageLayout, type ResourceUsageType, type Result, type TaskLayout, type TimeScale, type ViewMode, type Viewport, addDays, addHours, checkCyclicDependency, computeHourScale, computeImpact, computeLayout, computeResourceLayout, computeTimeScale, createGanttEngine, defaultConfig, diffDays, diffHours, flattenTasks, formatDate, formatDateTime, hasTimeComponent, inclusiveDays, isValidDate, mergeTaskPatch, normalizeLinks, resolveResourceColor, resolveUsageColor, resolveUsageTimes, resultInvalidDate, scheduleByDependencies, shiftTask, startOfDay, toDate, toDateTime };
|
|
477
|
+
export { type AffectedTasks, type BuiltinFieldKey, type Calendar, type CustomColumn, DAY_MS, DEFAULT_HOUR_COLORS, DEFAULT_TASK_COLORS, type DateRange, type Dependency, EDITOR_COLOR_OPTIONS, type FlatTask, type GanttConfig, type GanttEditorField, GanttEngine, type GanttEngineEvent, type GanttEngineOptions, type GanttError, type GanttLink, type GanttMarker, type GanttTask, HOUR_MS, type HourScale, type LagUnit, type LinkType, OWNER_COLOR_PALETTE, type PatchTask, RESOURCE_COLOR_PALETTE, type Resource, type ResourceLayout, type ResourceRowLayout, type ResourceUsage, type ResourceUsageLayout, type ResourceUsageType, type Result, type TaskLayout, type TimeScale, type ViewMode, type Viewport, addDays, addHours, checkCyclicDependency, computeHourScale, computeImpact, computeLayout, computeResourceLayout, computeTimeScale, createGanttEngine, defaultConfig, diffDays, diffHours, flattenTasks, formatDate, formatDateTime, hasTimeComponent, inclusiveDays, isValidDate, mergeTaskPatch, normalizeLinks, resolveResourceColor, resolveUsageColor, resolveUsageTimes, resultInvalidDate, scheduleByDependencies, shiftTask, startOfDay, toDate, toDateTime };
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,37 @@
|
|
|
1
|
+
// src/styles/colors.ts
|
|
2
|
+
var DEFAULT_TASK_COLORS = {
|
|
3
|
+
task: "#4d77b3",
|
|
4
|
+
// 普通任务实际条
|
|
5
|
+
summary: "#475467",
|
|
6
|
+
// 阶段条
|
|
7
|
+
milestone: "#b57e4a",
|
|
8
|
+
// 里程碑菱形 / 参考线默认
|
|
9
|
+
plan: "#cbd5e1",
|
|
10
|
+
// 计划条外部底色
|
|
11
|
+
progress: "#367d73"
|
|
12
|
+
// 计划条内部进度色(任务未设置 planColor 时)
|
|
13
|
+
};
|
|
14
|
+
var DEFAULT_HOUR_COLORS = {
|
|
15
|
+
plan: "#cbd5e1",
|
|
16
|
+
// 计划条外部底色
|
|
17
|
+
progress: "#367d73",
|
|
18
|
+
// 计划条内部进度色(任务未设置 planColor 时)
|
|
19
|
+
milestone: "#b57e4a"
|
|
20
|
+
// 里程碑菱形 / 参考线默认
|
|
21
|
+
};
|
|
22
|
+
var RESOURCE_COLOR_PALETTE = ["#f10755", "#9b87c4", "#509b81", "#c18f67", "#5e7fb0"];
|
|
23
|
+
var EDITOR_COLOR_OPTIONS = [
|
|
24
|
+
"#4d77b3",
|
|
25
|
+
"#8f77c5",
|
|
26
|
+
"#c16794",
|
|
27
|
+
"#b57e4a",
|
|
28
|
+
"#b3a24d",
|
|
29
|
+
"#47ae8f",
|
|
30
|
+
"#3f99a6",
|
|
31
|
+
"#64748b"
|
|
32
|
+
];
|
|
33
|
+
var OWNER_COLOR_PALETTE = ["#c2708b", "#9b87c4", "#509b81", "#c18f67", "#5e7fb0"];
|
|
34
|
+
|
|
1
35
|
// src/types/index.ts
|
|
2
36
|
var defaultConfig = {
|
|
3
37
|
viewMode: "month",
|
|
@@ -18,13 +52,7 @@ var defaultConfig = {
|
|
|
18
52
|
enableLinkCreation: true,
|
|
19
53
|
showLinkRejectionNotice: true,
|
|
20
54
|
virtualScroll: true,
|
|
21
|
-
taskColors: {
|
|
22
|
-
task: "#2563eb",
|
|
23
|
-
summary: "#475467",
|
|
24
|
-
milestone: "#d97706",
|
|
25
|
-
plan: "#cbd5e1",
|
|
26
|
-
progress: "#0f766e"
|
|
27
|
-
},
|
|
55
|
+
taskColors: { ...DEFAULT_TASK_COLORS },
|
|
28
56
|
timeUnit: "day",
|
|
29
57
|
hourWidth: 12,
|
|
30
58
|
autoSchedule: true,
|
|
@@ -563,7 +591,6 @@ function computeImpact(taskId, patch, tasks, links, config) {
|
|
|
563
591
|
}
|
|
564
592
|
|
|
565
593
|
// src/engines/computeResourceLayout.ts
|
|
566
|
-
var RESOURCE_COLOR_PALETTE = ["#d85f87", "#9b63d9", "#47b987", "#d58a5f", "#4f83d8"];
|
|
567
594
|
function colorHash(source) {
|
|
568
595
|
return [...source].reduce((sum, char) => sum + (char.codePointAt(0) ?? 0), 0);
|
|
569
596
|
}
|
|
@@ -1077,8 +1104,12 @@ function createGanttEngine(options = {}) {
|
|
|
1077
1104
|
}
|
|
1078
1105
|
export {
|
|
1079
1106
|
DAY_MS,
|
|
1107
|
+
DEFAULT_HOUR_COLORS,
|
|
1108
|
+
DEFAULT_TASK_COLORS,
|
|
1109
|
+
EDITOR_COLOR_OPTIONS,
|
|
1080
1110
|
GanttEngine,
|
|
1081
1111
|
HOUR_MS,
|
|
1112
|
+
OWNER_COLOR_PALETTE,
|
|
1082
1113
|
RESOURCE_COLOR_PALETTE,
|
|
1083
1114
|
addDays,
|
|
1084
1115
|
addHours,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/types/index.ts","../src/services/checkCyclicDependency.ts","../src/services/normalizeLinks.ts","../src/services/flattenTasks.ts","../src/utils/date.ts","../src/services/computeTimeScale.ts","../src/services/computeHourScale.ts","../src/engines/scheduling.ts","../src/engines/computeLayout.ts","../src/engines/computeImpact.ts","../src/engines/computeResourceLayout.ts","../src/engine/GanttEngine.ts"],"sourcesContent":["export type ViewMode = \"day\" | \"week\" | \"month\" | \"quarter\" | \"year\"\r\n\r\nexport type LinkType = \"FS\" | \"SS\" | \"FF\" | \"SF\"\r\nexport type LagUnit = \"calendar\" | \"working\"\r\n\r\nexport type Result<T> =\r\n | { ok: true; data: T }\r\n | { ok: false; error: GanttError }\r\n\r\nexport interface GanttError {\r\n code:\r\n | \"CYCLE_DEPENDENCY\"\r\n | \"INVALID_DATE\"\r\n | \"MISSING_TASK\"\r\n | \"ORPHAN_DEPENDENCY\"\r\n | \"INVALID_DEPENDENCY\"\r\n message: string\r\n details?: unknown\r\n}\r\n\r\nexport interface DateRange {\r\n start: string | Date\r\n end: string | Date\r\n progress?: number\r\n}\r\n\r\nexport interface Dependency {\r\n id?: string\r\n predecessorId: string\r\n type: LinkType\r\n lag: number\r\n lagUnit: LagUnit\r\n}\r\n\r\nexport interface GanttTask {\r\n id: string\r\n name: string\r\n type: \"task\" | \"milestone\" | \"summary\"\r\n plan: DateRange\r\n actual: Required<DateRange>\r\n dependencies?: Dependency[]\r\n parentId?: string | null\r\n color?: string\r\n planColor?: string\r\n calendarId?: string\r\n resources?: string[]\r\n segments?: Array<{ start: string | Date; end: string | Date }>\r\n constraint?: {\r\n type: \"SNET\" | \"SNLT\" | \"MSO\" | \"MFO\" | \"ASAP\" | \"ALAP\"\r\n date?: string | Date\r\n }\r\n schedulingMode?: \"auto\" | \"manual\"\r\n duration?: number\r\n custom?: Record<string, unknown>\r\n}\r\n\r\nexport interface GanttMarker {\r\n id: string\r\n name: string\r\n date: string | Date\r\n color?: string\r\n}\r\n\r\nexport interface GanttEditorField {\r\n key: string\r\n label: string\r\n visible?: boolean\r\n editable?: boolean\r\n type?: \"text\" | \"number\" | \"date\" | \"select\"\r\n options?: Array<{ label: string; value: string | number }>\r\n placeholder?: string\r\n}\r\n\r\nexport interface CustomColumn {\r\n key: string\r\n label: string\r\n width?: number\r\n visible?: boolean\r\n align?: \"left\" | \"center\" | \"right\"\r\n editable?: boolean\r\n type?: \"text\" | \"number\" | \"date\" | \"select\"\r\n options?: Array<{ label: string; value: string | number }>\r\n placeholder?: string\r\n editor?: Omit<GanttEditorField, \"key\" | \"label\">\r\n formatter?: (value: unknown, task: GanttTask) => string\r\n cellClass?: string | ((task: GanttTask) => string)\r\n cellStyle?: Record<string, string | number> | ((task: GanttTask) => Record<string, string | number>)\r\n render?: (task: GanttTask) => { text: string; className?: string }\r\n}\r\n\r\nexport interface GanttConfig {\r\n viewMode: ViewMode\r\n rowHeight: number\r\n columnWidth: number\r\n headerHeight: number\r\n taskListWidth: number\r\n width?: string | number\r\n height?: string | number\r\n locale: string\r\n firstDayOfWeek: 0 | 1\r\n dateFormat: string\r\n customColumns?: CustomColumn[]\r\n columns?: CustomColumn[]\r\n editorFields?: GanttEditorField[]\r\n columnWidths?: Partial<Record<ViewMode, number>>\r\n theme?: \"light\" | \"dark\" | string\r\n visibleRange?: {\r\n start: string | Date\r\n end: string | Date\r\n }\r\n showPlanBar?: boolean\r\n showActualBar?: boolean\r\n showTimelineWhenEmpty?: boolean\r\n builtInTaskEditor?: boolean\r\n builtInMarkerEditor?: boolean\r\n editablePlan?: boolean\r\n editableActual?: boolean\r\n enableLinkCreation?: boolean\r\n showLinkRejectionNotice?: boolean\r\n virtualScroll?: boolean\r\n taskColors?: {\r\n task?: string\r\n summary?: string\r\n milestone?: string\r\n plan?: string\r\n progress?: string\r\n }\r\n resourceColors?: Record<string, string>\r\n /** 资源视图时间粒度;day 按整天渲染,hour 按占用携带的小时渲染(仅 ResourceTimeline 使用) */\r\n timeUnit?: \"day\" | \"hour\"\r\n /** 资源视图 hour 粒度下每小时像素宽 */\r\n hourWidth?: number\r\n autoSchedule?: boolean\r\n editable?: boolean\r\n}\r\n\r\nexport interface GanttLink {\r\n id: string\r\n sourceId: string\r\n targetId: string\r\n type: LinkType\r\n lag?: number\r\n lagUnit?: LagUnit\r\n}\r\n\r\nexport interface TaskLayout {\r\n taskId: string\r\n rowIndex: number\r\n left: number\r\n width: number\r\n top: number\r\n depth: number\r\n isCritical: boolean\r\n}\r\n\r\n/** 资源(会议室、负责人等),资源视图的 Y 轴主体 */\r\nexport interface Resource {\r\n id: string\r\n name: string\r\n color?: string\r\n custom?: Record<string, unknown>\r\n}\r\n\r\nexport type ResourceUsageType = \"booking\" | \"assignment\"\r\n\r\n/** 资源在某时间段的占用(会议室预订、负责人承接的项目等) */\r\nexport interface ResourceUsage {\r\n id: string\r\n resourceId: string\r\n /** 日历日粒度,与 GanttTask 一致;同一天内的时段(如 9:00-11:00)按当天整格渲染,时刻体现在 label/tooltip */\r\n start: string | Date\r\n end: string | Date\r\n label?: string\r\n color?: string\r\n type?: ResourceUsageType\r\n custom?: Record<string, unknown>\r\n}\r\n\r\nexport interface ResourceRowLayout {\r\n resourceId: string\r\n rowIndex: number\r\n /** 行内占用车道数(同时段重叠数),>1 时条需要堆叠变薄 */\r\n laneCount: number\r\n color: string\r\n usageIds: string[]\r\n}\r\n\r\nexport interface ResourceUsageLayout {\r\n usageId: string\r\n resourceId: string\r\n rowIndex: number\r\n laneIndex: number\r\n laneCount: number\r\n left: number\r\n width: number\r\n /** 绝对 top(含 headerHeight + rowIndex*rowHeight + 行内 lane 偏移) */\r\n top: number\r\n /** 条高度,由 laneCount 与 rowHeight 决定 */\r\n height: number\r\n}\r\n\r\nexport interface ResourceLayout {\r\n rows: ResourceRowLayout[]\r\n usages: ResourceUsageLayout[]\r\n}\r\n\r\nexport interface FlatTask {\r\n task: GanttTask\r\n depth: number\r\n rowIndex: number\r\n hasChildren: boolean\r\n collapsed: boolean\r\n}\r\n\r\nexport interface Calendar {\r\n id: string\r\n workingDays: number[]\r\n holidays: string[]\r\n hours: [number, number][]\r\n}\r\n\r\nexport interface TimeScale {\r\n start: Date\r\n end: Date\r\n left: number\r\n width: number\r\n}\r\n\r\nexport interface Viewport {\r\n scrollTop: number\r\n scrollLeft: number\r\n clientWidth: number\r\n clientHeight: number\r\n dpr: number\r\n}\r\n\r\nexport interface AffectedTasks {\r\n changed: Array<{\r\n taskId: string\r\n actualStart: string | Date\r\n actualEnd: string | Date\r\n rowIndex: number\r\n }>\r\n conflicts: Array<{\r\n taskId: string\r\n constraintType: string\r\n message: string\r\n }>\r\n}\r\n\r\nexport interface PatchTask {\r\n planStart?: string | Date\r\n planEnd?: string | Date\r\n actualStart?: string | Date\r\n actualEnd?: string | Date\r\n progress?: number\r\n name?: string\r\n type?: GanttTask[\"type\"]\r\n parentId?: string | null\r\n color?: string\r\n planColor?: string\r\n duration?: number\r\n schedulingMode?: \"auto\" | \"manual\"\r\n resources?: string[]\r\n calendarId?: string\r\n custom?: Record<string, unknown>\r\n}\r\n\r\nexport const defaultConfig: GanttConfig = {\r\n viewMode: \"month\",\r\n rowHeight: 44,\r\n columnWidth: 30,\r\n headerHeight: 50,\r\n taskListWidth: 280,\r\n locale: \"zh-CN\",\r\n firstDayOfWeek: 0,\r\n dateFormat: \"YYYY-MM-DD\",\r\n showPlanBar: true,\r\n showActualBar: true,\r\n showTimelineWhenEmpty: false,\r\n builtInTaskEditor: true,\r\n builtInMarkerEditor: true,\r\n editablePlan: false,\r\n editableActual: true,\r\n enableLinkCreation: true,\r\n showLinkRejectionNotice: true,\r\n virtualScroll: true,\r\n taskColors: {\r\n task: \"#2563eb\",\r\n summary: \"#475467\",\r\n milestone: \"#d97706\",\r\n plan: \"#cbd5e1\",\r\n progress: \"#0f766e\"\r\n },\r\n timeUnit: \"day\",\r\n hourWidth: 12,\r\n autoSchedule: true,\r\n editable: true\r\n}\r\n","import type { GanttLink } from \"../types\"\r\n\r\nexport function checkCyclicDependency(\r\n links: GanttLink[]\r\n): { hasCycle: boolean; cyclePath?: string[] } {\r\n const graph = new Map<string, string[]>()\r\n\r\n for (const link of links) {\r\n const targets = graph.get(link.sourceId) ?? []\r\n targets.push(link.targetId)\r\n graph.set(link.sourceId, targets)\r\n if (!graph.has(link.targetId)) {\r\n graph.set(link.targetId, [])\r\n }\r\n }\r\n\r\n const visiting = new Set<string>()\r\n const visited = new Set<string>()\r\n const path: string[] = []\r\n\r\n const dfs = (node: string): string[] | undefined => {\r\n if (visiting.has(node)) {\r\n const start = path.indexOf(node)\r\n return [...path.slice(start), node]\r\n }\r\n if (visited.has(node)) {\r\n return undefined\r\n }\r\n\r\n visiting.add(node)\r\n path.push(node)\r\n\r\n for (const next of graph.get(node) ?? []) {\r\n const cycle = dfs(next)\r\n if (cycle) {\r\n return cycle\r\n }\r\n }\r\n\r\n path.pop()\r\n visiting.delete(node)\r\n visited.add(node)\r\n return undefined\r\n }\r\n\r\n for (const node of graph.keys()) {\r\n const cyclePath = dfs(node)\r\n if (cyclePath) {\r\n return { hasCycle: true, cyclePath }\r\n }\r\n }\r\n\r\n return { hasCycle: false }\r\n}\r\n","import type { GanttLink, GanttTask } from \"../types\"\r\n\r\nexport function normalizeLinks(tasks: GanttTask[], standaloneLinks: GanttLink[] = []): GanttLink[] {\r\n const taskIds = new Set(tasks.map((task) => task.id))\r\n const byTaskPair = new Map<string, GanttLink>()\r\n const outgoing = new Map<string, Set<string>>()\r\n\r\n const createsCycle = (sourceId: string, targetId: string): boolean => {\r\n const pending = [targetId]\r\n const visited = new Set<string>()\r\n while (pending.length > 0) {\r\n const current = pending.pop()!\r\n if (current === sourceId) {\r\n return true\r\n }\r\n if (visited.has(current)) {\r\n continue\r\n }\r\n visited.add(current)\r\n pending.push(...(outgoing.get(current) ?? []))\r\n }\r\n return false\r\n }\r\n\r\n const append = (link: GanttLink) => {\r\n if (\r\n !taskIds.has(link.sourceId)\r\n || !taskIds.has(link.targetId)\r\n || link.sourceId === link.targetId\r\n ) {\r\n return\r\n }\r\n\r\n const normalized: GanttLink = {\r\n ...link,\r\n id: link.id || `link-${link.sourceId}-${link.targetId}`,\r\n lag: link.lag ?? 0,\r\n lagUnit: link.lagUnit ?? \"calendar\"\r\n }\r\n const pairKey = `${normalized.sourceId}|${normalized.targetId}`\r\n if (byTaskPair.has(pairKey) || createsCycle(normalized.sourceId, normalized.targetId)) {\r\n return\r\n }\r\n\r\n byTaskPair.set(pairKey, normalized)\r\n const targets = outgoing.get(normalized.sourceId) ?? new Set<string>()\r\n targets.add(normalized.targetId)\r\n outgoing.set(normalized.sourceId, targets)\r\n }\r\n\r\n for (const task of tasks) {\r\n for (const dependency of task.dependencies ?? []) {\r\n append({\r\n id: dependency.id || `link-${dependency.predecessorId}-${task.id}`,\r\n sourceId: dependency.predecessorId,\r\n targetId: task.id,\r\n type: dependency.type,\r\n lag: dependency.lag,\r\n lagUnit: dependency.lagUnit\r\n })\r\n }\r\n }\r\n\r\n for (const link of standaloneLinks) {\r\n append(link)\r\n }\r\n\r\n return [...byTaskPair.values()]\r\n}\r\n","import type { FlatTask, GanttTask } from \"../types\"\r\n\r\nexport function flattenTasks(tasks: GanttTask[], collapsedIds: Set<string> = new Set()): FlatTask[] {\r\n const children = new Map<string | null, GanttTask[]>()\r\n\r\n for (const task of tasks) {\r\n const parentId = task.parentId ?? null\r\n const bucket = children.get(parentId) ?? []\r\n bucket.push(task)\r\n children.set(parentId, bucket)\r\n }\r\n\r\n const flattened: FlatTask[] = []\r\n const visit = (task: GanttTask, depth: number) => {\r\n const childTasks = children.get(task.id) ?? []\r\n const collapsed = collapsedIds.has(task.id)\r\n\r\n flattened.push({\r\n task,\r\n depth,\r\n rowIndex: flattened.length,\r\n hasChildren: childTasks.length > 0,\r\n collapsed\r\n })\r\n\r\n if (!collapsed) {\r\n for (const child of childTasks) {\r\n visit(child, depth + 1)\r\n }\r\n }\r\n }\r\n\r\n for (const root of children.get(null) ?? []) {\r\n visit(root, 0)\r\n }\r\n\r\n return flattened\r\n}\r\n","import type { Result } from \"../types\"\r\n\r\nexport const DAY_MS = 24 * 60 * 60 * 1000\r\nexport const HOUR_MS = 60 * 60 * 1000\r\n\r\nexport function toDate(value: string | Date): Date {\r\n if (value instanceof Date) {\r\n return startOfDay(value)\r\n }\r\n\r\n const match = /^(\\d{4})-(\\d{2})-(\\d{2})/.exec(value)\r\n if (match) {\r\n return new Date(Number(match[1]), Number(match[2]) - 1, Number(match[3]))\r\n }\r\n\r\n return startOfDay(new Date(value))\r\n}\r\n\r\n/** 字符串是否携带时间(\"T09:00\" 或 \" 09:00\")。Date 实例一律视为已带时间 */\r\nexport function hasTimeComponent(value: string | Date): boolean {\r\n return typeof value === \"string\" && /T\\d|:\\d/.test(value)\r\n}\r\n\r\n/**\r\n * 解析为保留时间部分的 Date。\r\n * 时间字符串(如 \"2026-07-02T09:00\" / \"2026-07-02 09:00\")按本地时间解析;\r\n * 纯日期字符串(如 \"2026-07-02\")回退到 toDate 的本地零点语义。\r\n */\r\nexport function toDateTime(value: string | Date): Date {\r\n if (value instanceof Date) {\r\n return new Date(value.getTime())\r\n }\r\n if (hasTimeComponent(value)) {\r\n const date = new Date(value.replace(\" \", \"T\"))\r\n if (!Number.isNaN(date.getTime())) {\r\n return date\r\n }\r\n }\r\n return toDate(value)\r\n}\r\n\r\nexport function startOfDay(date: Date): Date {\r\n return new Date(date.getFullYear(), date.getMonth(), date.getDate())\r\n}\r\n\r\nexport function addDays(date: string | Date, days: number): Date {\r\n const next = toDate(date)\r\n next.setDate(next.getDate() + days)\r\n return startOfDay(next)\r\n}\r\n\r\nexport function diffDays(start: string | Date, end: string | Date): number {\r\n return Math.round((toDate(end).getTime() - toDate(start).getTime()) / DAY_MS)\r\n}\r\n\r\nexport function addHours(date: string | Date, hours: number): Date {\r\n return new Date(toDateTime(date).getTime() + hours * HOUR_MS)\r\n}\r\n\r\n/** 小时差(可含分钟的小数,如 09:00-11:30 = 2.5) */\r\nexport function diffHours(start: string | Date, end: string | Date): number {\r\n return (toDateTime(end).getTime() - toDateTime(start).getTime()) / HOUR_MS\r\n}\r\n\r\nexport function inclusiveDays(start: string | Date, end: string | Date): number {\r\n return Math.max(0, diffDays(start, end) + 1)\r\n}\r\n\r\nexport function isValidDate(value: string | Date): boolean {\r\n const date = value instanceof Date ? value : new Date(value)\r\n return !Number.isNaN(date.getTime())\r\n}\r\n\r\nexport function formatDate(date: string | Date): string {\r\n const value = toDate(date)\r\n const year = value.getFullYear()\r\n const month = String(value.getMonth() + 1).padStart(2, \"0\")\r\n const day = String(value.getDate()).padStart(2, \"0\")\r\n return `${year}-${month}-${day}`\r\n}\r\n\r\nexport function formatDateTime(date: string | Date): string {\r\n const value = toDateTime(date)\r\n const hours = String(value.getHours()).padStart(2, \"0\")\r\n const minutes = String(value.getMinutes()).padStart(2, \"0\")\r\n return `${formatDate(value)} ${hours}:${minutes}`\r\n}\r\n\r\nexport function resultInvalidDate<T>(message: string, details?: unknown): Result<T> {\r\n return {\r\n ok: false,\r\n error: { code: \"INVALID_DATE\", message, details }\r\n }\r\n}\r\n","import type { TimeScale, ViewMode } from \"../types\"\r\nimport { addDays, diffDays, startOfDay } from \"../utils/date\"\r\n\r\nexport function computeTimeScale(\r\n start: Date,\r\n end: Date,\r\n viewMode: ViewMode,\r\n columnWidth: number,\r\n firstDayOfWeek: 0 | 1\r\n): TimeScale[] {\r\n const rangeStart = alignStart(start, viewMode, firstDayOfWeek)\r\n const endDate = startOfDay(end)\r\n const rangeEnd = viewMode === \"day\"\r\n ? addDays(endDate, (firstDayOfWeek + 6 - endDate.getDay() + 7) % 7)\r\n : endDate\r\n const scale: TimeScale[] = []\r\n let cursor = rangeStart\r\n let left = 0\r\n\r\n while (cursor.getTime() <= rangeEnd.getTime()) {\r\n const next = nextUnit(cursor, viewMode)\r\n const unitEnd = addDays(next, -1)\r\n const width = Math.max(1, diffDays(cursor, next)) * columnWidth\r\n\r\n scale.push({\r\n start: cursor,\r\n end: unitEnd,\r\n left,\r\n width\r\n })\r\n\r\n left += width\r\n cursor = next\r\n }\r\n\r\n return scale\r\n}\r\n\r\nfunction alignStart(date: Date, viewMode: ViewMode, firstDayOfWeek: 0 | 1): Date {\r\n const start = startOfDay(date)\r\n if (viewMode === \"day\" || viewMode === \"week\") {\r\n const delta = (start.getDay() - firstDayOfWeek + 7) % 7\r\n return addDays(start, -delta)\r\n }\r\n if (viewMode === \"month\") {\r\n return new Date(start.getFullYear(), start.getMonth(), 1)\r\n }\r\n if (viewMode === \"quarter\") {\r\n return new Date(start.getFullYear(), Math.floor(start.getMonth() / 3) * 3, 1)\r\n }\r\n if (viewMode === \"year\") {\r\n return new Date(start.getFullYear(), 0, 1)\r\n }\r\n return start\r\n}\r\n\r\nfunction nextUnit(date: Date, viewMode: ViewMode): Date {\r\n if (viewMode === \"week\") {\r\n return addDays(date, 7)\r\n }\r\n if (viewMode === \"month\") {\r\n return new Date(date.getFullYear(), date.getMonth() + 1, 1)\r\n }\r\n if (viewMode === \"quarter\") {\r\n return new Date(date.getFullYear(), date.getMonth() + 3, 1)\r\n }\r\n if (viewMode === \"year\") {\r\n return new Date(date.getFullYear() + 1, 0, 1)\r\n }\r\n return addDays(date, 1)\r\n}\r\n","import type { TimeScale } from \"../types\"\nimport { addDays, addHours, inclusiveDays, startOfDay } from \"../utils/date\"\n\nexport interface HourScale {\n /** 每日一格(顶行表头用) */\n days: TimeScale[]\n /** 每小时一格(底部刻度用) */\n hours: TimeScale[]\n start: Date\n end: Date\n}\n\n/** 小时级时间刻度:范围对齐到整天,每天拆成 24 个整点小时,width = hourWidth */\nexport function computeHourScale(start: Date, end: Date, hourWidth: number): HourScale {\n const rangeStart = startOfDay(start)\n const rangeEnd = startOfDay(end)\n const totalDays = Math.max(1, inclusiveDays(rangeStart, rangeEnd))\n const days: TimeScale[] = []\n const hours: TimeScale[] = []\n\n for (let d = 0; d < totalDays; d += 1) {\n const dayStart = addDays(rangeStart, d)\n days.push({\n start: dayStart,\n end: addHours(dayStart, 24),\n left: d * 24 * hourWidth,\n width: 24 * hourWidth\n })\n for (let h = 0; h < 24; h += 1) {\n const hourStart = addHours(dayStart, h)\n hours.push({\n start: hourStart,\n end: addHours(hourStart, 1),\n left: (d * 24 + h) * hourWidth,\n width: hourWidth\n })\n }\n }\n\n return { days, hours, start: rangeStart, end: rangeEnd }\n}\n","import type { GanttLink, GanttTask, LinkType, Result } from \"../types\"\r\nimport { addDays, diffDays, inclusiveDays, toDate } from \"../utils/date\"\r\nimport { checkCyclicDependency } from \"../services/checkCyclicDependency\"\r\n\r\nexport function scheduleByDependencies(tasks: GanttTask[], links: GanttLink[]): Result<GanttTask[]> {\r\n const cycle = checkCyclicDependency(links)\r\n if (cycle.hasCycle) {\r\n return {\r\n ok: false,\r\n error: {\r\n code: \"CYCLE_DEPENDENCY\",\r\n message: \"Dependency graph contains a cycle.\",\r\n details: cycle.cyclePath\r\n }\r\n }\r\n }\r\n\r\n const taskMap = new Map(tasks.map((task) => [task.id, cloneTask(task)]))\r\n const incoming = new Map<string, GanttLink[]>()\r\n const outgoing = new Map<string, GanttLink[]>()\r\n\r\n for (const link of links) {\r\n const source = taskMap.get(link.sourceId)\r\n const target = taskMap.get(link.targetId)\r\n if (!source || !target) {\r\n return {\r\n ok: false,\r\n error: {\r\n code: \"ORPHAN_DEPENDENCY\",\r\n message: `Dependency ${link.id} references a missing task.`\r\n }\r\n }\r\n }\r\n incoming.set(link.targetId, [...(incoming.get(link.targetId) ?? []), link])\r\n outgoing.set(link.sourceId, [...(outgoing.get(link.sourceId) ?? []), link])\r\n }\r\n\r\n const ordered = topologicalOrder(tasks.map((task) => task.id), links)\r\n\r\n for (const taskId of ordered) {\r\n const task = taskMap.get(taskId)\r\n if (!task || task.schedulingMode === \"manual\") {\r\n continue\r\n }\r\n\r\n const linksToTask = incoming.get(taskId) ?? []\r\n let start = toDate(task.actual.start)\r\n let end = toDate(task.actual.end)\r\n const duration = Math.max(0, task.duration ?? inclusiveDays(start, end))\r\n\r\n for (const link of linksToTask) {\r\n const source = taskMap.get(link.sourceId)\r\n if (!source) {\r\n continue\r\n }\r\n\r\n const candidate = applyDependency(source, start, end, duration, link.type, link.lag ?? 0)\r\n if (candidate.start.getTime() > start.getTime()) {\r\n start = candidate.start\r\n }\r\n if (candidate.end.getTime() > end.getTime()) {\r\n end = candidate.end\r\n }\r\n }\r\n\r\n const normalizedEnd = task.type === \"milestone\" ? start : addDays(start, Math.max(0, duration - 1))\r\n task.actual = {\r\n ...task.actual,\r\n start,\r\n end: normalizedEnd\r\n }\r\n }\r\n\r\n return { ok: true, data: tasks.map((task) => taskMap.get(task.id) ?? task) }\r\n}\r\n\r\nfunction applyDependency(\r\n source: GanttTask,\r\n currentStart: Date,\r\n currentEnd: Date,\r\n duration: number,\r\n type: LinkType,\r\n lag: number\r\n): { start: Date; end: Date } {\r\n const sourceStart = toDate(source.actual.start)\r\n const sourceEnd = toDate(source.actual.end)\r\n const span = Math.max(0, duration - 1)\r\n\r\n if (type === \"FS\") {\r\n const start = addDays(sourceEnd, lag + 1)\r\n return { start, end: addDays(start, span) }\r\n }\r\n\r\n if (type === \"SS\") {\r\n const start = addDays(sourceStart, lag)\r\n return { start, end: addDays(start, span) }\r\n }\r\n\r\n if (type === \"FF\") {\r\n const end = addDays(sourceEnd, lag)\r\n return { start: addDays(end, -span), end }\r\n }\r\n\r\n const end = addDays(sourceStart, lag)\r\n return { start: addDays(end, -span), end: end.getTime() > currentEnd.getTime() ? end : currentEnd }\r\n}\r\n\r\nfunction topologicalOrder(ids: string[], links: GanttLink[]): string[] {\r\n const indegree = new Map(ids.map((id) => [id, 0]))\r\n const outgoing = new Map<string, string[]>()\r\n\r\n for (const link of links) {\r\n indegree.set(link.targetId, (indegree.get(link.targetId) ?? 0) + 1)\r\n outgoing.set(link.sourceId, [...(outgoing.get(link.sourceId) ?? []), link.targetId])\r\n }\r\n\r\n const queue = ids.filter((id) => (indegree.get(id) ?? 0) === 0)\r\n const ordered: string[] = []\r\n\r\n while (queue.length) {\r\n const current = queue.shift()!\r\n ordered.push(current)\r\n for (const target of outgoing.get(current) ?? []) {\r\n indegree.set(target, (indegree.get(target) ?? 1) - 1)\r\n if (indegree.get(target) === 0) {\r\n queue.push(target)\r\n }\r\n }\r\n }\r\n\r\n return ordered.length === ids.length ? ordered : ids\r\n}\r\n\r\nfunction cloneTask(task: GanttTask): GanttTask {\r\n return {\r\n ...task,\r\n plan: { ...task.plan },\r\n actual: { ...task.actual },\r\n dependencies: task.dependencies?.map((dependency) => ({ ...dependency }))\r\n }\r\n}\r\n\r\nexport function shiftTask(task: GanttTask, start: string | Date): GanttTask {\r\n const currentStart = toDate(task.actual.start)\r\n const nextStart = toDate(start)\r\n const delta = diffDays(currentStart, nextStart)\r\n return {\r\n ...task,\r\n actual: {\r\n ...task.actual,\r\n start: nextStart,\r\n end: addDays(toDate(task.actual.end), delta)\r\n }\r\n }\r\n}\r\n","import type { GanttConfig, GanttLink, GanttTask, Result, TaskLayout, Viewport } from \"../types\"\r\nimport { defaultConfig } from \"../types\"\r\nimport { flattenTasks } from \"../services/flattenTasks\"\r\nimport { normalizeLinks } from \"../services/normalizeLinks\"\r\nimport { scheduleByDependencies } from \"./scheduling\"\r\nimport { diffDays, inclusiveDays, isValidDate, toDate } from \"../utils/date\"\r\n\r\nexport function computeLayout(\r\n tasks: GanttTask[],\r\n links: GanttLink[],\r\n config: Partial<GanttConfig>,\r\n collapsedIds: Set<string> = new Set(),\r\n viewport?: Viewport\r\n): Result<TaskLayout[]> {\r\n const mergedConfig = { ...defaultConfig, ...config }\r\n\r\n for (const task of tasks) {\r\n if (!isValidDate(task.actual.start) || !isValidDate(task.actual.end)) {\r\n return {\r\n ok: false,\r\n error: {\r\n code: \"INVALID_DATE\",\r\n message: `Task ${task.id} has invalid actual dates.`\r\n }\r\n }\r\n }\r\n }\r\n\r\n const normalizedLinks = normalizeLinks(tasks, links)\r\n const scheduled = mergedConfig.autoSchedule === false\r\n ? { ok: true as const, data: tasks }\r\n : scheduleByDependencies(tasks, normalizedLinks)\r\n\r\n if (!scheduled.ok) {\r\n return scheduled\r\n }\r\n\r\n const flatTasks = flattenTasks(scheduled.data, collapsedIds)\r\n const firstDate = mergedConfig.visibleRange\r\n ? toDate(mergedConfig.visibleRange.start)\r\n : minDate(scheduled.data.map((task) => toDate(task.actual.start)))\r\n\r\n const visibleStart = viewport\r\n ? Math.max(0, Math.floor(viewport.scrollTop / mergedConfig.rowHeight) - 8)\r\n : 0\r\n const visibleEnd = viewport\r\n ? Math.ceil((viewport.scrollTop + viewport.clientHeight) / mergedConfig.rowHeight) + 8\r\n : Number.POSITIVE_INFINITY\r\n\r\n const layouts = flatTasks\r\n .filter((flat) => flat.rowIndex >= visibleStart && flat.rowIndex <= visibleEnd)\r\n .map<TaskLayout>((flat) => {\r\n const start = toDate(flat.task.actual.start)\r\n const end = toDate(flat.task.actual.end)\r\n const duration = flat.task.type === \"milestone\" ? 0 : inclusiveDays(start, end)\r\n\r\n return {\r\n taskId: flat.task.id,\r\n rowIndex: flat.rowIndex,\r\n left: diffDays(firstDate, start) * mergedConfig.columnWidth,\r\n width: Math.max(flat.task.type === \"milestone\" ? mergedConfig.columnWidth / 2 : mergedConfig.columnWidth, duration * mergedConfig.columnWidth),\r\n top: mergedConfig.headerHeight + flat.rowIndex * mergedConfig.rowHeight,\r\n depth: flat.depth,\r\n isCritical: false\r\n }\r\n })\r\n\r\n return { ok: true, data: layouts }\r\n}\r\n\r\nfunction minDate(dates: Date[]): Date {\r\n return new Date(Math.min(...dates.map((date) => date.getTime())))\r\n}\r\n","import type { AffectedTasks, GanttConfig, GanttLink, GanttTask, PatchTask, Result } from \"../types\"\r\nimport { flattenTasks } from \"../services/flattenTasks\"\r\nimport { scheduleByDependencies, shiftTask } from \"./scheduling\"\r\nimport { addDays, inclusiveDays, toDate } from \"../utils/date\"\r\n\r\nexport function computeImpact(\r\n taskId: string,\r\n patch: PatchTask,\r\n tasks: GanttTask[],\r\n links: GanttLink[],\r\n config: Partial<GanttConfig>\r\n): Result<AffectedTasks> {\r\n const target = tasks.find((task) => task.id === taskId)\r\n if (!target) {\r\n return {\r\n ok: false,\r\n error: {\r\n code: \"MISSING_TASK\",\r\n message: `Task ${taskId} does not exist.`\r\n }\r\n }\r\n }\r\n\r\n const patchedTasks = tasks.map((task) => {\r\n if (task.id !== taskId) {\r\n return task\r\n }\r\n\r\n let next = { ...task, actual: { ...task.actual } }\r\n if (patch.actualStart) {\r\n next = shiftTask(next, patch.actualStart)\r\n }\r\n if (patch.actualEnd) {\r\n next.actual.end = patch.actualEnd\r\n }\r\n if (typeof patch.progress === \"number\") {\r\n next.actual.progress = patch.progress\r\n }\r\n if (patch.duration) {\r\n next.duration = patch.duration\r\n next.actual.end = addDays(toDate(next.actual.start), Math.max(0, patch.duration - 1))\r\n }\r\n if (patch.name) {\r\n next.name = patch.name\r\n }\r\n if (patch.type) {\r\n next.type = patch.type\r\n }\r\n if (\"parentId\" in patch) {\r\n next.parentId = patch.parentId\r\n }\r\n if (patch.color) {\r\n next.color = patch.color\r\n }\r\n if (\"planColor\" in patch) {\r\n next.planColor = patch.planColor\r\n }\r\n if (patch.schedulingMode) {\r\n next.schedulingMode = patch.schedulingMode\r\n }\r\n return next\r\n })\r\n\r\n const scheduled = config.autoSchedule === false\r\n ? { ok: true as const, data: patchedTasks }\r\n : scheduleByDependencies(patchedTasks, links)\r\n\r\n if (!scheduled.ok) {\r\n return scheduled\r\n }\r\n\r\n const rows = new Map(flattenTasks(scheduled.data).map((flat) => [flat.task.id, flat.rowIndex]))\r\n const originalById = new Map(tasks.map((task) => [task.id, task]))\r\n const changed = scheduled.data\r\n .filter((task) => {\r\n const original = originalById.get(task.id)\r\n return original && (\r\n String(original.actual.start) !== String(task.actual.start) ||\r\n String(original.actual.end) !== String(task.actual.end)\r\n )\r\n })\r\n .map((task) => ({\r\n taskId: task.id,\r\n actualStart: task.actual.start,\r\n actualEnd: task.actual.end,\r\n rowIndex: rows.get(task.id) ?? 0\r\n }))\r\n\r\n const conflicts = scheduled.data\r\n .filter((task) => task.constraint?.date && inclusiveDays(task.actual.start, task.constraint.date) < 0)\r\n .map((task) => ({\r\n taskId: task.id,\r\n constraintType: task.constraint!.type,\r\n message: \"Task violates its configured constraint date.\"\r\n }))\r\n\r\n return { ok: true, data: { changed, conflicts } }\r\n}\r\n","import type {\n GanttConfig,\n Resource,\n ResourceLayout,\n ResourceRowLayout,\n ResourceUsage,\n ResourceUsageLayout,\n Result,\n Viewport\n} from \"../types\"\nimport { defaultConfig } from \"../types\"\nimport {\n addHours,\n diffDays,\n diffHours,\n hasTimeComponent,\n inclusiveDays,\n isValidDate,\n startOfDay,\n toDate,\n toDateTime\n} from \"../utils/date\"\n\n/** 资源色条调色板,与 GanttChart owner 头像的 5 色调色板一致 */\nexport const RESOURCE_COLOR_PALETTE = [\"#d85f87\", \"#9b63d9\", \"#47b987\", \"#d58a5f\", \"#4f83d8\"]\n\nfunction colorHash(source: string): number {\n return [...source].reduce((sum, char) => sum + (char.codePointAt(0) ?? 0), 0)\n}\n\n/** 解析资源主色:resource.color → config.resourceColors[id] → 名称 hash 调色板(确定性) */\nexport function resolveResourceColor(\n resource: Resource,\n resourceColors?: Record<string, string>\n): string {\n if (resource.color) {\n return resource.color\n }\n if (resourceColors?.[resource.id]) {\n return resourceColors[resource.id]\n }\n const source = resource.name || resource.id\n return RESOURCE_COLOR_PALETTE[colorHash(source) % RESOURCE_COLOR_PALETTE.length]\n}\n\n/** 解析占用条颜色:usage.color → 所属资源色 */\nexport function resolveUsageColor(\n usage: ResourceUsage,\n resource: Resource,\n resourceColors?: Record<string, string>\n): string {\n return usage.color || resolveResourceColor(resource, resourceColors)\n}\n\n/**\n * 将占用解析为布局用的起止 Date。\n * - day:与 GanttTask 一致,截断到天。\n * - hour:保留时间;纯日期(未携带时间)按\"含结束日整天\"处理,与 day 模式的含末语义一致。\n */\nexport function resolveUsageTimes(\n usage: ResourceUsage,\n timeUnit: \"day\" | \"hour\"\n): { start: Date; end: Date } {\n if (timeUnit === \"hour\") {\n const start = toDateTime(usage.start)\n const endRaw = toDateTime(usage.end)\n return { start, end: hasTimeComponent(usage.end) ? endRaw : addHours(endRaw, 24) }\n }\n return { start: toDate(usage.start), end: toDate(usage.end) }\n}\n\n/**\n * 资源视图布局:每个资源一行(Y 轴),占用时间段以横向色条展示。\n * 同一资源同时段多个占用按贪心 first-fit 车道分配,行内堆叠子条。\n * day 粒度像素数学与 computeLayout 一致(diffDays × columnWidth);\n * hour 粒度按 diffHours × hourWidth 渲染,纯日期占用含结束日整天。\n * 传入 viewport 时仅产出可见行窗口内的占用几何(±8 overscan),rows 始终全量返回。\n */\nexport function computeResourceLayout(\n resources: Resource[],\n usages: ResourceUsage[],\n config: Partial<GanttConfig>,\n viewport?: Viewport\n): Result<ResourceLayout> {\n const mergedConfig = { ...defaultConfig, ...config }\n const timeUnit = mergedConfig.timeUnit ?? \"day\"\n const hourMode = timeUnit === \"hour\"\n const hourW = mergedConfig.hourWidth ?? 12\n\n for (const usage of usages) {\n const { start, end } = resolveUsageTimes(usage, timeUnit)\n if (!isValidDate(start) || !isValidDate(end)) {\n return {\n ok: false,\n error: {\n code: \"INVALID_DATE\",\n message: `Resource usage ${usage.id} has invalid dates.`\n }\n }\n }\n }\n\n // 行序:resources 顺序在前,usage 引用但未声明的 resourceId 追加在后(容错)\n const orderedIds: string[] = []\n const seen = new Set<string>()\n for (const resource of resources) {\n if (!seen.has(resource.id)) {\n seen.add(resource.id)\n orderedIds.push(resource.id)\n }\n }\n for (const usage of usages) {\n if (!seen.has(usage.resourceId)) {\n seen.add(usage.resourceId)\n orderedIds.push(usage.resourceId)\n }\n }\n\n const resourceById = new Map(resources.map((resource) => [resource.id, resource]))\n const usagesByResource = new Map<string, ResourceUsage[]>()\n for (const usage of usages) {\n usagesByResource.set(usage.resourceId, [...(usagesByResource.get(usage.resourceId) ?? []), usage])\n }\n\n const rows: ResourceRowLayout[] = []\n const usageLayouts: ResourceUsageLayout[] = []\n const laneIndexByUsage = new Map<string, number>()\n\n orderedIds.forEach((resourceId, rowIndex) => {\n const resource = resourceById.get(resourceId) ?? {\n id: resourceId,\n name: resourceId\n } satisfies Resource\n const grouped = [...(usagesByResource.get(resourceId) ?? [])].sort((a, b) => {\n const as = resolveUsageTimes(a, timeUnit)\n const bs = resolveUsageTimes(b, timeUnit)\n return as.start.getTime() - bs.start.getTime() || as.end.getTime() - bs.end.getTime()\n })\n\n // 贪心 first-fit 车道分配:占用放入第一个\"前一个占用结束不晚于其开始\"的车道。\n // day:diffDays(end, start) > 0(相邻日复用);hour:diffHours(end, start) >= 0(首尾相接复用)\n const laneEnds: Array<Date | null> = []\n for (const usage of grouped) {\n const { start, end } = resolveUsageTimes(usage, timeUnit)\n let laneIndex = -1\n for (let k = 0; k < laneEnds.length; k += 1) {\n const laneEnd = laneEnds[k]\n if (laneEnd === null || isLaneFree(laneEnd, start, hourMode)) {\n laneIndex = k\n break\n }\n }\n if (laneIndex === -1) {\n laneIndex = laneEnds.length\n laneEnds.push(null)\n }\n laneEnds[laneIndex] = end\n laneIndexByUsage.set(usage.id, laneIndex)\n }\n\n rows.push({\n resourceId,\n rowIndex,\n laneCount: laneEnds.length,\n color: resolveResourceColor(resource, mergedConfig.resourceColors),\n usageIds: grouped.map((usage) => usage.id)\n })\n })\n\n const usageById = new Map(usages.map((usage) => [usage.id, usage]))\n const visibleStart = viewport\n ? Math.max(0, Math.floor(viewport.scrollTop / mergedConfig.rowHeight) - 8)\n : 0\n const visibleEnd = viewport\n ? Math.ceil((viewport.scrollTop + viewport.clientHeight) / mergedConfig.rowHeight) + 8\n : Number.POSITIVE_INFINITY\n\n const firstDate = hourMode\n ? startOfDay(\n mergedConfig.visibleRange\n ? toDateTime(mergedConfig.visibleRange.start)\n : usages.length\n ? minDate(usages.map((usage) => resolveUsageTimes(usage, \"hour\").start))\n : toDateTime(new Date())\n )\n : mergedConfig.visibleRange\n ? toDate(mergedConfig.visibleRange.start)\n : usages.length\n ? minDate(usages.map((usage) => toDate(usage.start)))\n : toDate(new Date())\n\n for (const row of rows) {\n if (row.rowIndex < visibleStart || row.rowIndex > visibleEnd) {\n continue\n }\n const resource = resourceById.get(row.resourceId) ?? { id: row.resourceId, name: row.resourceId }\n const laneCount = Math.max(1, row.laneCount)\n const slot = mergedConfig.rowHeight / laneCount\n const barHeight = Math.max(6, Math.min(14, slot - 4))\n\n for (const usageId of row.usageIds) {\n const usage = usageById.get(usageId)\n if (!usage) {\n continue\n }\n const { start, end } = resolveUsageTimes(usage, timeUnit)\n const laneIndex = laneIndexByUsage.get(usageId) ?? 0\n const laneTop = laneIndex * slot + (slot - barHeight) / 2\n const left = hourMode\n ? diffHours(firstDate, start) * hourW\n : diffDays(firstDate, start) * mergedConfig.columnWidth\n const width = hourMode\n ? Math.max(hourW, diffHours(start, end) * hourW)\n : Math.max(mergedConfig.columnWidth, inclusiveDays(start, end) * mergedConfig.columnWidth)\n\n usageLayouts.push({\n usageId,\n resourceId: row.resourceId,\n rowIndex: row.rowIndex,\n laneIndex,\n laneCount,\n left,\n width,\n top: mergedConfig.headerHeight + row.rowIndex * mergedConfig.rowHeight + laneTop,\n height: barHeight\n })\n }\n }\n\n return { ok: true, data: { rows, usages: usageLayouts } }\n}\n\nfunction isLaneFree(laneEnd: Date, start: Date, hourMode: boolean): boolean {\n return hourMode ? diffHours(laneEnd, start) >= 0 : diffDays(laneEnd, start) > 0\n}\n\nfunction minDate(dates: Date[]): Date {\n return new Date(Math.min(...dates.map((date) => date.getTime())))\n}\n","import type {\r\n GanttConfig,\r\n GanttTask,\r\n GanttLink,\r\n GanttMarker,\r\n PatchTask,\r\n TaskLayout,\r\n TimeScale,\r\n FlatTask,\r\n Result\r\n} from \"../types\"\r\nimport { defaultConfig } from \"../types\"\r\nimport { computeLayout } from \"../engines/computeLayout\"\r\nimport { computeTimeScale } from \"../services/computeTimeScale\"\r\nimport { flattenTasks } from \"../services/flattenTasks\"\r\nimport { toDate, diffDays, addDays } from \"../utils/date\"\r\n\r\n/**\r\n * 把扁平的 PatchTask 合并回嵌套的 GanttTask。\r\n * PatchTask 用 planStart/planEnd/actualStart/actualEnd 表示日期,\r\n * GanttTask 用 plan.start/end、actual.start/end 嵌套表示——这是组件层\r\n * 和数据层之间的桥,本该在引擎层而不是散落在各 demo 里。\r\n */\r\nexport function mergeTaskPatch(task: GanttTask, patch: PatchTask): GanttTask {\r\n return {\r\n ...task,\r\n ...patch,\r\n plan: {\r\n ...task.plan,\r\n start: patch.planStart ?? task.plan.start,\r\n end: patch.planEnd ?? task.plan.end,\r\n progress: patch.progress ?? task.plan.progress\r\n },\r\n actual: {\r\n ...task.actual,\r\n start: patch.actualStart ?? task.actual.start,\r\n end: patch.actualEnd ?? task.actual.end,\r\n progress: patch.progress ?? task.actual.progress\r\n },\r\n custom: patch.custom ? { ...task.custom, ...patch.custom } : task.custom\r\n }\r\n}\r\n\r\nexport interface GanttEngineOptions {\r\n tasks?: GanttTask[]\r\n links?: GanttLink[]\r\n config?: Partial<GanttConfig>\r\n /** 滚动容器元素;命令式视口 API(scrollToDate / zoomToFit)需要它。纯计算场景可不传。 */\r\n container?: HTMLElement | null\r\n /** 初始折叠的任务 id */\r\n collapsedIds?: Iterable<string>\r\n /** 里程碑标记 */\r\n markers?: GanttMarker[]\r\n}\r\n\r\nexport type GanttEngineEvent =\r\n | \"ready\"\r\n | \"taskschange\"\r\n | \"taskchange\"\r\n | \"taskcreate\"\r\n | \"taskdelete\"\r\n | \"linkschange\"\r\n | \"configchange\"\r\n | \"collapsechange\"\r\n | \"previewchange\"\r\n | \"markerschange\"\r\n | \"destroy\"\r\n\r\ntype Listener = (...args: unknown[]) => void\r\n\r\n/** 命令式甘特图引擎。框架无关,可被 Vue/React/原生 各自的薄壳组件驱动。 */\r\nexport class GanttEngine {\r\n private tasks: GanttTask[]\r\n private links: GanttLink[]\r\n private config: GanttConfig\r\n private readonly container: HTMLElement | null\r\n private collapsedIds: Set<string>\r\n private dragPreview: { taskId: string; patch: PatchTask; affected?: Record<string, PatchTask> } | null = null\r\n private markers: GanttMarker[]\r\n private readonly listeners: Map<GanttEngineEvent, Set<Listener>>\r\n private destroyed = false\r\n\r\n constructor(options: GanttEngineOptions = {}) {\r\n this.tasks = options.tasks ? options.tasks.map((task) => ({ ...task })) : []\r\n this.links = options.links ? options.links.map((link) => ({ ...link })) : []\r\n this.config = { ...defaultConfig, ...options.config }\r\n this.container = options.container ?? null\r\n this.collapsedIds = new Set(options.collapsedIds ?? [])\r\n this.markers = options.markers ? options.markers.map((marker) => ({ ...marker })) : []\r\n this.listeners = new Map()\r\n // 异步发 ready,确保订阅者能在构造后、首次 emit 前注册\r\n queueMicrotask(() => this.emit(\"ready\"))\r\n }\r\n\r\n // ── 状态查询 ──\r\n getTasks(): GanttTask[] {\r\n return this.tasks.map((task) => ({ ...task }))\r\n }\r\n getLinks(): GanttLink[] {\r\n return this.links.map((link) => ({ ...link }))\r\n }\r\n getMarkers(): GanttMarker[] {\r\n return this.markers.map((marker) => ({ ...marker }))\r\n }\r\n getConfig(): GanttConfig {\r\n return { ...this.config }\r\n }\r\n getTask(id: string): GanttTask | undefined {\r\n const task = this.tasks.find((item) => item.id === id)\r\n return task ? { ...task } : undefined\r\n }\r\n getCollapsedIds(): string[] {\r\n return Array.from(this.collapsedIds)\r\n }\r\n isDestroyed(): boolean {\r\n return this.destroyed\r\n }\r\n\r\n // ── 命令式变更(均触发对应事件) ──\r\n setTasks(tasks: GanttTask[]): void {\r\n this.assertNotDestroyed()\r\n this.tasks = tasks.map((task) => ({ ...task }))\r\n this.emit(\"taskschange\", this.getTasks())\r\n }\r\n setLinks(links: GanttLink[]): void {\r\n this.assertNotDestroyed()\r\n this.links = links.map((link) => ({ ...link }))\r\n this.emit(\"linkschange\", this.getLinks())\r\n }\r\n setMarkers(markers: GanttMarker[]): void {\r\n this.assertNotDestroyed()\r\n this.markers = markers.map((marker) => ({ ...marker }))\r\n this.emit(\"markerschange\", this.getMarkers())\r\n }\r\n /** 合并配置 patch,等价于 setConfig(mergeConfig(patch)) */\r\n mergeConfig(patch: Partial<GanttConfig>): void {\r\n this.assertNotDestroyed()\r\n this.config = { ...this.config, ...patch }\r\n this.emit(\"configchange\", this.getConfig())\r\n }\r\n setConfig(config: Partial<GanttConfig>): void {\r\n this.mergeConfig(config)\r\n }\r\n /** 用 mergeTaskPatch 把扁平 patch 合并回嵌套结构后更新单个任务 */\r\n setTask(id: string, patch: PatchTask): void {\r\n this.assertNotDestroyed()\r\n const target = this.tasks.find((task) => task.id === id)\r\n if (!target) {\r\n return\r\n }\r\n const updated = mergeTaskPatch(target, patch)\r\n this.tasks = this.tasks.map((task) => (task.id === id ? updated : task))\r\n this.emit(\"taskchange\", id, patch, { ...updated })\r\n }\r\n addTask(task: GanttTask): void {\r\n this.assertNotDestroyed()\r\n this.tasks = [...this.tasks, { ...task }]\r\n this.emit(\"taskcreate\", { ...task })\r\n }\r\n removeTask(id: string): void {\r\n this.assertNotDestroyed()\r\n const removed = this.tasks.find((task) => task.id === id)\r\n if (!removed) return\r\n this.tasks = this.tasks.filter((task) => task.id !== id)\r\n this.collapsedIds.delete(id)\r\n this.emit(\"taskdelete\", id)\r\n }\r\n collapse(id: string, collapsed = true): void {\r\n this.assertNotDestroyed()\r\n if (collapsed) this.collapsedIds.add(id)\r\n else this.collapsedIds.delete(id)\r\n this.emit(\"collapsechange\", this.getCollapsedIds())\r\n }\r\n toggleCollapse(id: string): void {\r\n this.collapse(id, !this.collapsedIds.has(id))\r\n }\r\n /** 批量折叠/展开多个任务(如\"折叠全部分组\") */\r\n setCollapsed(ids: string[], collapsed = true): void {\r\n this.assertNotDestroyed()\r\n const next = new Set(this.collapsedIds)\r\n for (const id of ids) {\r\n if (collapsed) {\r\n next.add(id)\r\n } else {\r\n next.delete(id)\r\n }\r\n }\r\n this.collapsedIds = next\r\n this.emit(\"collapsechange\", this.getCollapsedIds())\r\n }\r\n\r\n // ── 拖拽预览(交互态源;组件写入迁移到 engine,dateRange 可据此扩展) ──\r\n setPreview(preview: { taskId: string; patch: PatchTask; affected?: Record<string, PatchTask> }): void {\r\n this.assertNotDestroyed()\r\n this.dragPreview = {\r\n taskId: preview.taskId,\r\n patch: { ...preview.patch },\r\n affected: preview.affected ? { ...preview.affected } : undefined\r\n }\r\n this.emit(\"previewchange\", this.getPreview())\r\n }\r\n clearPreview(): void {\r\n if (!this.dragPreview) {\r\n return\r\n }\r\n this.dragPreview = null\r\n this.emit(\"previewchange\", null)\r\n }\r\n getPreview(): { taskId: string; patch: PatchTask; affected?: Record<string, PatchTask> } | null {\r\n if (!this.dragPreview) {\r\n return null\r\n }\r\n return {\r\n taskId: this.dragPreview.taskId,\r\n patch: { ...this.dragPreview.patch },\r\n affected: this.dragPreview.affected ? { ...this.dragPreview.affected } : undefined\r\n }\r\n }\r\n\r\n // ── 计算(delegate 到 core 纯函数) ──\r\n /** 数据真实日期范围(未含 viewport pad) */\r\n getDateRange(): { start: Date; end: Date } {\r\n const dates: Date[] = []\r\n for (const task of this.tasks) {\r\n dates.push(\r\n toDate(task.plan.start),\r\n toDate(task.plan.end),\r\n toDate(task.actual.start),\r\n toDate(task.actual.end)\r\n )\r\n }\r\n for (const marker of this.markers) {\r\n dates.push(toDate(marker.date))\r\n }\r\n // 合并拖拽预览:用 mergeTaskPatch 算 patch 后日期扩范围(只扩不缩)\r\n const preview = this.dragPreview\r\n if (preview) {\r\n const ids = [preview.taskId, ...Object.keys(preview.affected ?? {})]\r\n for (const id of ids) {\r\n const task = this.tasks.find((item) => item.id === id)\r\n if (!task) {\r\n continue\r\n }\r\n const patch = preview.taskId === id ? preview.patch : preview.affected?.[id]\r\n if (!patch) {\r\n continue\r\n }\r\n const merged = mergeTaskPatch(task, patch)\r\n dates.push(\r\n toDate(merged.plan.start),\r\n toDate(merged.plan.end),\r\n toDate(merged.actual.start),\r\n toDate(merged.actual.end)\r\n )\r\n }\r\n }\r\n if (!dates.length) {\r\n const today = toDate(new Date())\r\n return { start: today, end: addDays(today, 30) }\r\n }\r\n return {\r\n start: new Date(Math.min(...dates.map((d) => d.getTime()))),\r\n end: new Date(Math.max(...dates.map((d) => d.getTime())))\r\n }\r\n }\r\n getTimeScale(): TimeScale[] {\r\n const range = this.getDateRange()\r\n return computeTimeScale(\r\n range.start,\r\n range.end,\r\n this.config.viewMode,\r\n this.config.columnWidth,\r\n this.config.firstDayOfWeek\r\n )\r\n }\r\n getLayout(): Result<TaskLayout[]> {\r\n return computeLayout(\r\n this.tasks,\r\n this.links,\r\n this.config,\r\n this.collapsedIds\r\n )\r\n }\r\n getFlatTasks(): FlatTask[] {\r\n return flattenTasks(this.tasks, this.collapsedIds)\r\n }\r\n getTotalWidth(): number {\r\n return this.getTimeScale().reduce((sum, tick) => sum + tick.width, 0)\r\n }\r\n getTotalHeight(): number {\r\n const rows = this.getFlatTasks().length\r\n return this.config.headerHeight + rows * this.config.rowHeight\r\n }\r\n\r\n // ── 视口命令(需要 container) ──\r\n getScrollLeft(): number {\r\n return this.container?.scrollLeft ?? 0\r\n }\r\n setScrollLeft(px: number): void {\r\n if (!this.container) return\r\n const max = Math.max(0, this.container.scrollWidth - this.container.clientWidth)\r\n this.container.scrollLeft = Math.max(0, Math.min(max, px))\r\n }\r\n /** 把当前 scrollLeft 限制到 [0, maxScrollLeft]。\r\n * 拖拽预览收缩(松手/取消)后 timeline 总宽度变小,scrollLeft 可能超出新边界,\r\n * 导致 canvas translate 超出内容区、右侧网格未覆盖。此时调用本方法防超界。 */\r\n clampScrollLeft(): void {\r\n if (!this.container) return\r\n const max = Math.max(0, this.container.scrollWidth - this.container.clientWidth)\r\n if (this.container.scrollLeft > max) {\r\n this.container.scrollLeft = max\r\n }\r\n }\r\n getScrollTop(): number {\r\n return this.container?.scrollTop ?? 0\r\n }\r\n setScrollTop(px: number): void {\r\n if (!this.container) return\r\n const max = Math.max(0, this.container.scrollHeight - this.container.clientHeight)\r\n this.container.scrollTop = Math.max(0, Math.min(max, px))\r\n }\r\n /** 滚动到包含目标日期的 tick 列(跨 viewMode 精确) */\r\n scrollToDate(date: string | Date): void {\r\n if (!this.container) return\r\n const target = toDate(date)\r\n const scale = this.getTimeScale()\r\n const tick = scale.find((t) => target >= t.start && target <= t.end)\r\n const left = tick ? tick.left : diffDays(this.getDateRange().start, target) * this.config.columnWidth\r\n this.setScrollLeft(left)\r\n }\r\n scrollToTask(id: string): void {\r\n const layout = this.getLayout()\r\n if (!layout.ok) return\r\n const item = layout.data.find((row) => row.taskId === id)\r\n if (item) this.setScrollLeft(item.left)\r\n }\r\n scrollToStart(): void {\r\n this.setScrollLeft(0)\r\n }\r\n scrollToEnd(): void {\r\n if (!this.container) return\r\n this.setScrollLeft(this.getTotalWidth())\r\n }\r\n /** 让整个 timeline 总宽度≈视口宽度(调 columnWidth) */\r\n zoomToFit(padding = 1): void {\r\n if (!this.container) return\r\n const spanDays = Math.max(1, diffDays(this.getDateRange().start, this.getDateRange().end) + 1)\r\n const target = (this.container.clientWidth * padding) / spanDays\r\n this.mergeConfig({ columnWidth: this.clampColumnWidth(target) })\r\n }\r\n /** 让指定日期范围占满视口宽度(调 columnWidth) */\r\n zoomToRange(start: string | Date, end: string | Date): void {\r\n if (!this.container) return\r\n const span = Math.max(1, diffDays(start, end) + 1)\r\n const target = this.container.clientWidth / span\r\n this.mergeConfig({ columnWidth: this.clampColumnWidth(target) })\r\n }\r\n private clampColumnWidth(value: number): number {\r\n return Math.max(4, Math.min(400, Math.round(value)))\r\n }\r\n\r\n // ── 事件订阅 ──\r\n on(event: GanttEngineEvent, fn: Listener): () => void {\r\n this.assertNotDestroyed()\r\n let set = this.listeners.get(event)\r\n if (!set) {\r\n set = new Set()\r\n this.listeners.set(event, set)\r\n }\r\n set.add(fn)\r\n return () => {\r\n const current = this.listeners.get(event)\r\n if (current) {\r\n current.delete(fn)\r\n if (!current.size) this.listeners.delete(event)\r\n }\r\n }\r\n }\r\n once(event: GanttEngineEvent, fn: Listener): () => void {\r\n const off = this.on(event, (...args: unknown[]) => {\r\n off()\r\n fn(...args)\r\n })\r\n return off\r\n }\r\n off(event: GanttEngineEvent, fn?: Listener): void {\r\n const set = this.listeners.get(event)\r\n if (!set) return\r\n if (fn) {\r\n set.delete(fn)\r\n if (!set.size) this.listeners.delete(event)\r\n } else {\r\n this.listeners.delete(event)\r\n }\r\n }\r\n emit(event: GanttEngineEvent, ...args: unknown[]): void {\r\n const set = this.listeners.get(event)\r\n if (!set) return\r\n // 复制遍历,避免回调中增删导致迭代错乱\r\n for (const fn of Array.from(set)) {\r\n try {\r\n fn(...args)\r\n } catch (error) {\r\n // 单个监听器抛错不应打断其他监听器\r\n if (typeof console !== \"undefined\") {\r\n console.error(`[GanttEngine] listener for \"${event}\" threw`, error)\r\n }\r\n }\r\n }\r\n }\r\n\r\n // ── 生命周期 ──\r\n destroy(): void {\r\n if (this.destroyed) return\r\n this.destroyed = true\r\n this.emit(\"destroy\")\r\n this.listeners.clear()\r\n this.tasks = []\r\n this.links = []\r\n }\r\n\r\n private assertNotDestroyed(): void {\r\n if (this.destroyed) {\r\n throw new Error(\"GanttEngine: operation on destroyed instance\")\r\n }\r\n }\r\n}\r\n\r\n/** 工厂函数,对应成熟做法的 new GanttEngine(container, config) 入口 */\r\nexport function createGanttEngine(options: GanttEngineOptions = {}): GanttEngine {\r\n return new GanttEngine(options)\r\n}\r\n"],"mappings":"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","names":["end","minDate"]}
|
|
1
|
+
{"version":3,"sources":["../src/styles/colors.ts","../src/types/index.ts","../src/services/checkCyclicDependency.ts","../src/services/normalizeLinks.ts","../src/services/flattenTasks.ts","../src/utils/date.ts","../src/services/computeTimeScale.ts","../src/services/computeHourScale.ts","../src/engines/scheduling.ts","../src/engines/computeLayout.ts","../src/engines/computeImpact.ts","../src/engines/computeResourceLayout.ts","../src/engine/GanttEngine.ts"],"sourcesContent":["/**\n * 默认颜色统一配置 —— 甘特图 / 资源视图 / 小时视图的默认条色与调色板都从这里读取。\n *\n * 想更换「默认配色」时改这一个文件即可全局生效;单个实例仍可通过\n * config.taskColors / config.hourColors / config.resourceColors / 任务 color、planColor 字段按需覆盖。\n *\n * 默认色按视图拆分:\n * - DEFAULT_TASK_COLORS:甘特图(实际条 / 计划条 / 阶段 / 里程碑),也是资源视图与小时视图的共享回退。\n * - DEFAULT_HOUR_COLORS:小时视图计划条与里程碑的独立默认,互不影响甘特图。\n */\n\n/** 甘特图默认语义色(对应 GanttConfig.taskColors) */\nexport const DEFAULT_TASK_COLORS: {\n task: string\n summary: string\n milestone: string\n plan: string\n progress: string\n} = {\n task: \"#4d77b3\", // 普通任务实际条\n summary: \"#475467\", // 阶段条\n milestone: \"#b57e4a\", // 里程碑菱形 / 参考线默认\n plan: \"#cbd5e1\", // 计划条外部底色\n progress: \"#367d73\" // 计划条内部进度色(任务未设置 planColor 时)\n}\n\n/** 小时视图计划条与里程碑默认色(对应 GanttConfig.hourColors);独立于甘特图,可单独修改 */\nexport const DEFAULT_HOUR_COLORS: {\n plan: string\n progress: string\n milestone: string\n} = {\n plan: \"#cbd5e1\", // 计划条外部底色\n progress: \"#367d73\", // 计划条内部进度色(任务未设置 planColor 时)\n milestone: \"#b57e4a\" // 里程碑菱形 / 参考线默认\n}\n\n/** 资源视图未指定颜色时,按资源名哈希从该调色板确定性取色 */\nexport const RESOURCE_COLOR_PALETTE: string[] = [\"#f10755\", \"#9b87c4\", \"#509b81\", \"#c18f67\", \"#5e7fb0\"]\n\n/** 内置任务编辑器 / 里程碑编辑器的可选颜色板 */\nexport const EDITOR_COLOR_OPTIONS: string[] = [\n \"#4d77b3\",\n \"#8f77c5\",\n \"#c16794\",\n \"#b57e4a\",\n \"#b3a24d\",\n \"#47ae8f\",\n \"#3f99a6\",\n \"#64748b\"\n]\n\n/** 左侧列表负责人头像兜底色板 */\nexport const OWNER_COLOR_PALETTE: string[] = [\"#c2708b\", \"#9b87c4\", \"#509b81\", \"#c18f67\", \"#5e7fb0\"]\n","import { DEFAULT_TASK_COLORS } from \"../styles/colors\"\r\n\r\nexport type ViewMode = \"day\" | \"week\" | \"month\" | \"quarter\" | \"year\"\r\n\r\nexport type LinkType = \"FS\" | \"SS\" | \"FF\" | \"SF\"\r\nexport type LagUnit = \"calendar\" | \"working\"\r\n\r\nexport type Result<T> =\r\n | { ok: true; data: T }\r\n | { ok: false; error: GanttError }\r\n\r\nexport interface GanttError {\r\n code:\r\n | \"CYCLE_DEPENDENCY\"\r\n | \"INVALID_DATE\"\r\n | \"MISSING_TASK\"\r\n | \"ORPHAN_DEPENDENCY\"\r\n | \"INVALID_DEPENDENCY\"\r\n message: string\r\n details?: unknown\r\n}\r\n\r\nexport interface DateRange {\r\n start: string | Date\r\n end: string | Date\r\n progress?: number\r\n}\r\n\r\nexport interface Dependency {\r\n id?: string\r\n predecessorId: string\r\n type: LinkType\r\n lag: number\r\n lagUnit: LagUnit\r\n}\r\n\r\nexport interface GanttTask {\r\n id: string\r\n name: string\r\n type: \"task\" | \"milestone\" | \"summary\"\r\n plan: DateRange\r\n actual: Required<DateRange>\r\n dependencies?: Dependency[]\r\n parentId?: string | null\r\n color?: string\r\n planColor?: string\r\n calendarId?: string\r\n resources?: string[]\r\n segments?: Array<{ start: string | Date; end: string | Date }>\r\n constraint?: {\r\n type: \"SNET\" | \"SNLT\" | \"MSO\" | \"MFO\" | \"ASAP\" | \"ALAP\"\r\n date?: string | Date\r\n }\r\n schedulingMode?: \"auto\" | \"manual\"\r\n duration?: number\r\n custom?: Record<string, unknown>\r\n}\r\n\r\nexport interface GanttMarker {\r\n id: string\r\n name: string\r\n date: string | Date\r\n color?: string\r\n}\r\n\r\nexport interface GanttEditorField {\r\n key: string\r\n label: string\r\n visible?: boolean\r\n editable?: boolean\r\n type?: \"text\" | \"number\" | \"date\" | \"select\"\r\n options?: Array<{ label: string; value: string | number }>\r\n placeholder?: string\r\n}\r\n\r\n/** 内置任务字段键。列可通过 CustomColumn.field 绑定到这些内置字段。\r\n * 除 status / owner 外都对应任务对象上的真实字段;status 与 owner 是派生展示字段。 */\r\nexport type BuiltinFieldKey =\r\n | \"name\"\r\n | \"status\"\r\n | \"owner\"\r\n | \"resources\"\r\n | \"type\"\r\n | \"parentId\"\r\n | \"planStart\"\r\n | \"planEnd\"\r\n | \"actualStart\"\r\n | \"actualEnd\"\r\n | \"progress\"\r\n | \"duration\"\r\n | \"calendarId\"\r\n | \"schedulingMode\"\r\n | \"color\"\r\n | \"planColor\"\r\n\r\nexport interface CustomColumn {\r\n key: string\r\n /** 绑定到的内置任务字段键。缺省时列按自定义列处理,从 task.custom[key] 读取/编辑;设置后列读取/编辑该内置字段,key 仍作为列标识。 */\r\n field?: BuiltinFieldKey\r\n label: string\r\n width?: number\r\n visible?: boolean\r\n align?: \"left\" | \"center\" | \"right\"\r\n editable?: boolean\r\n type?: \"text\" | \"number\" | \"date\" | \"select\"\r\n options?: Array<{ label: string; value: string | number }>\r\n placeholder?: string\r\n editor?: Omit<GanttEditorField, \"key\" | \"label\">\r\n formatter?: (value: unknown, task: GanttTask) => string\r\n cellClass?: string | ((task: GanttTask) => string)\r\n cellStyle?: Record<string, string | number> | ((task: GanttTask) => Record<string, string | number>)\r\n render?: (task: GanttTask) => { text: string; className?: string }\r\n}\r\n\r\nexport interface GanttConfig {\r\n viewMode: ViewMode\r\n rowHeight: number\r\n columnWidth: number\r\n headerHeight: number\r\n taskListWidth: number\r\n width?: string | number\r\n height?: string | number\r\n locale: string\r\n firstDayOfWeek: 0 | 1\r\n dateFormat: string\r\n customColumns?: CustomColumn[]\r\n columns?: CustomColumn[]\r\n editorFields?: GanttEditorField[]\r\n columnWidths?: Partial<Record<ViewMode, number>>\r\n theme?: \"light\" | \"dark\" | string\r\n visibleRange?: {\r\n start: string | Date\r\n end: string | Date\r\n }\r\n showPlanBar?: boolean\r\n showActualBar?: boolean\r\n showTimelineWhenEmpty?: boolean\r\n builtInTaskEditor?: boolean\r\n builtInMarkerEditor?: boolean\r\n editablePlan?: boolean\r\n editableActual?: boolean\r\n enableLinkCreation?: boolean\r\n showLinkRejectionNotice?: boolean\r\n virtualScroll?: boolean\r\n taskColors?: {\r\n task?: string\r\n summary?: string\r\n milestone?: string\r\n plan?: string\r\n progress?: string\r\n }\r\n /** 小时视图独立配色:plan/progress/milestone 缺省时回退到 taskColors 与 DEFAULT_HOUR_COLORS */\r\n hourColors?: {\r\n plan?: string\r\n progress?: string\r\n milestone?: string\r\n }\r\n resourceColors?: Record<string, string>\r\n /** 资源视图时间粒度;day 按整天渲染,hour 按占用携带的小时渲染(仅 ResourceTimeline 使用) */\r\n timeUnit?: \"day\" | \"hour\"\r\n /** 资源视图 hour 粒度下每小时像素宽 */\r\n hourWidth?: number\r\n /** 小时视图计划条高度;缺省时随 rowHeight 自动推导(与资源视图单车道条高公式一致) */\r\n barHeight?: number\r\n autoSchedule?: boolean\r\n editable?: boolean\r\n}\r\n\r\nexport interface GanttLink {\r\n id: string\r\n sourceId: string\r\n targetId: string\r\n type: LinkType\r\n lag?: number\r\n lagUnit?: LagUnit\r\n}\r\n\r\nexport interface TaskLayout {\r\n taskId: string\r\n rowIndex: number\r\n left: number\r\n width: number\r\n top: number\r\n depth: number\r\n isCritical: boolean\r\n}\r\n\r\n/** 资源(会议室、负责人等),资源视图的 Y 轴主体 */\r\nexport interface Resource {\r\n id: string\r\n name: string\r\n color?: string\r\n custom?: Record<string, unknown>\r\n}\r\n\r\nexport type ResourceUsageType = \"booking\" | \"assignment\"\r\n\r\n/** 资源在某时间段的占用(会议室预订、负责人承接的项目等) */\r\nexport interface ResourceUsage {\r\n id: string\r\n resourceId: string\r\n /** 日历日粒度,与 GanttTask 一致;同一天内的时段(如 9:00-11:00)按当天整格渲染,时刻体现在 label/tooltip */\r\n start: string | Date\r\n end: string | Date\r\n label?: string\r\n color?: string\r\n type?: ResourceUsageType\r\n custom?: Record<string, unknown>\r\n}\r\n\r\nexport interface ResourceRowLayout {\r\n resourceId: string\r\n rowIndex: number\r\n /** 行内占用车道数(同时段重叠数),>1 时条需要堆叠变薄 */\r\n laneCount: number\r\n color: string\r\n usageIds: string[]\r\n}\r\n\r\nexport interface ResourceUsageLayout {\r\n usageId: string\r\n resourceId: string\r\n rowIndex: number\r\n laneIndex: number\r\n laneCount: number\r\n left: number\r\n width: number\r\n /** 绝对 top(含 headerHeight + rowIndex*rowHeight + 行内 lane 偏移) */\r\n top: number\r\n /** 条高度,由 laneCount 与 rowHeight 决定 */\r\n height: number\r\n}\r\n\r\nexport interface ResourceLayout {\r\n rows: ResourceRowLayout[]\r\n usages: ResourceUsageLayout[]\r\n}\r\n\r\nexport interface FlatTask {\r\n task: GanttTask\r\n depth: number\r\n rowIndex: number\r\n hasChildren: boolean\r\n collapsed: boolean\r\n}\r\n\r\nexport interface Calendar {\r\n id: string\r\n workingDays: number[]\r\n holidays: string[]\r\n hours: [number, number][]\r\n}\r\n\r\nexport interface TimeScale {\r\n start: Date\r\n end: Date\r\n left: number\r\n width: number\r\n}\r\n\r\nexport interface Viewport {\r\n scrollTop: number\r\n scrollLeft: number\r\n clientWidth: number\r\n clientHeight: number\r\n dpr: number\r\n}\r\n\r\nexport interface AffectedTasks {\r\n changed: Array<{\r\n taskId: string\r\n actualStart: string | Date\r\n actualEnd: string | Date\r\n rowIndex: number\r\n }>\r\n conflicts: Array<{\r\n taskId: string\r\n constraintType: string\r\n message: string\r\n }>\r\n}\r\n\r\nexport interface PatchTask {\r\n planStart?: string | Date\r\n planEnd?: string | Date\r\n actualStart?: string | Date\r\n actualEnd?: string | Date\r\n progress?: number\r\n name?: string\r\n type?: GanttTask[\"type\"]\r\n parentId?: string | null\r\n color?: string\r\n planColor?: string\r\n duration?: number\r\n schedulingMode?: \"auto\" | \"manual\"\r\n resources?: string[]\r\n calendarId?: string\r\n custom?: Record<string, unknown>\r\n}\r\n\r\nexport const defaultConfig: GanttConfig = {\r\n viewMode: \"month\",\r\n rowHeight: 44,\r\n columnWidth: 30,\r\n headerHeight: 50,\r\n taskListWidth: 280,\r\n locale: \"zh-CN\",\r\n firstDayOfWeek: 0,\r\n dateFormat: \"YYYY-MM-DD\",\r\n showPlanBar: true,\r\n showActualBar: true,\r\n showTimelineWhenEmpty: false,\r\n builtInTaskEditor: true,\r\n builtInMarkerEditor: true,\r\n editablePlan: false,\r\n editableActual: true,\r\n enableLinkCreation: true,\r\n showLinkRejectionNotice: true,\r\n virtualScroll: true,\r\n taskColors: { ...DEFAULT_TASK_COLORS },\r\n timeUnit: \"day\",\r\n hourWidth: 12,\r\n autoSchedule: true,\r\n editable: true\r\n}\r\n","import type { GanttLink } from \"../types\"\r\n\r\nexport function checkCyclicDependency(\r\n links: GanttLink[]\r\n): { hasCycle: boolean; cyclePath?: string[] } {\r\n const graph = new Map<string, string[]>()\r\n\r\n for (const link of links) {\r\n const targets = graph.get(link.sourceId) ?? []\r\n targets.push(link.targetId)\r\n graph.set(link.sourceId, targets)\r\n if (!graph.has(link.targetId)) {\r\n graph.set(link.targetId, [])\r\n }\r\n }\r\n\r\n const visiting = new Set<string>()\r\n const visited = new Set<string>()\r\n const path: string[] = []\r\n\r\n const dfs = (node: string): string[] | undefined => {\r\n if (visiting.has(node)) {\r\n const start = path.indexOf(node)\r\n return [...path.slice(start), node]\r\n }\r\n if (visited.has(node)) {\r\n return undefined\r\n }\r\n\r\n visiting.add(node)\r\n path.push(node)\r\n\r\n for (const next of graph.get(node) ?? []) {\r\n const cycle = dfs(next)\r\n if (cycle) {\r\n return cycle\r\n }\r\n }\r\n\r\n path.pop()\r\n visiting.delete(node)\r\n visited.add(node)\r\n return undefined\r\n }\r\n\r\n for (const node of graph.keys()) {\r\n const cyclePath = dfs(node)\r\n if (cyclePath) {\r\n return { hasCycle: true, cyclePath }\r\n }\r\n }\r\n\r\n return { hasCycle: false }\r\n}\r\n","import type { GanttLink, GanttTask } from \"../types\"\r\n\r\nexport function normalizeLinks(tasks: GanttTask[], standaloneLinks: GanttLink[] = []): GanttLink[] {\r\n const taskIds = new Set(tasks.map((task) => task.id))\r\n const byTaskPair = new Map<string, GanttLink>()\r\n const outgoing = new Map<string, Set<string>>()\r\n\r\n const createsCycle = (sourceId: string, targetId: string): boolean => {\r\n const pending = [targetId]\r\n const visited = new Set<string>()\r\n while (pending.length > 0) {\r\n const current = pending.pop()!\r\n if (current === sourceId) {\r\n return true\r\n }\r\n if (visited.has(current)) {\r\n continue\r\n }\r\n visited.add(current)\r\n pending.push(...(outgoing.get(current) ?? []))\r\n }\r\n return false\r\n }\r\n\r\n const append = (link: GanttLink) => {\r\n if (\r\n !taskIds.has(link.sourceId)\r\n || !taskIds.has(link.targetId)\r\n || link.sourceId === link.targetId\r\n ) {\r\n return\r\n }\r\n\r\n const normalized: GanttLink = {\r\n ...link,\r\n id: link.id || `link-${link.sourceId}-${link.targetId}`,\r\n lag: link.lag ?? 0,\r\n lagUnit: link.lagUnit ?? \"calendar\"\r\n }\r\n const pairKey = `${normalized.sourceId}|${normalized.targetId}`\r\n if (byTaskPair.has(pairKey) || createsCycle(normalized.sourceId, normalized.targetId)) {\r\n return\r\n }\r\n\r\n byTaskPair.set(pairKey, normalized)\r\n const targets = outgoing.get(normalized.sourceId) ?? new Set<string>()\r\n targets.add(normalized.targetId)\r\n outgoing.set(normalized.sourceId, targets)\r\n }\r\n\r\n for (const task of tasks) {\r\n for (const dependency of task.dependencies ?? []) {\r\n append({\r\n id: dependency.id || `link-${dependency.predecessorId}-${task.id}`,\r\n sourceId: dependency.predecessorId,\r\n targetId: task.id,\r\n type: dependency.type,\r\n lag: dependency.lag,\r\n lagUnit: dependency.lagUnit\r\n })\r\n }\r\n }\r\n\r\n for (const link of standaloneLinks) {\r\n append(link)\r\n }\r\n\r\n return [...byTaskPair.values()]\r\n}\r\n","import type { FlatTask, GanttTask } from \"../types\"\r\n\r\nexport function flattenTasks(tasks: GanttTask[], collapsedIds: Set<string> = new Set()): FlatTask[] {\r\n const children = new Map<string | null, GanttTask[]>()\r\n\r\n for (const task of tasks) {\r\n const parentId = task.parentId ?? null\r\n const bucket = children.get(parentId) ?? []\r\n bucket.push(task)\r\n children.set(parentId, bucket)\r\n }\r\n\r\n const flattened: FlatTask[] = []\r\n const visit = (task: GanttTask, depth: number) => {\r\n const childTasks = children.get(task.id) ?? []\r\n const collapsed = collapsedIds.has(task.id)\r\n\r\n flattened.push({\r\n task,\r\n depth,\r\n rowIndex: flattened.length,\r\n hasChildren: childTasks.length > 0,\r\n collapsed\r\n })\r\n\r\n if (!collapsed) {\r\n for (const child of childTasks) {\r\n visit(child, depth + 1)\r\n }\r\n }\r\n }\r\n\r\n for (const root of children.get(null) ?? []) {\r\n visit(root, 0)\r\n }\r\n\r\n return flattened\r\n}\r\n","import type { Result } from \"../types\"\r\n\r\nexport const DAY_MS = 24 * 60 * 60 * 1000\r\nexport const HOUR_MS = 60 * 60 * 1000\r\n\r\nexport function toDate(value: string | Date): Date {\r\n if (value instanceof Date) {\r\n return startOfDay(value)\r\n }\r\n\r\n const match = /^(\\d{4})-(\\d{2})-(\\d{2})/.exec(value)\r\n if (match) {\r\n return new Date(Number(match[1]), Number(match[2]) - 1, Number(match[3]))\r\n }\r\n\r\n return startOfDay(new Date(value))\r\n}\r\n\r\n/** 字符串是否携带时间(\"T09:00\" 或 \" 09:00\")。Date 实例一律视为已带时间 */\r\nexport function hasTimeComponent(value: string | Date): boolean {\r\n return typeof value === \"string\" && /T\\d|:\\d/.test(value)\r\n}\r\n\r\n/**\r\n * 解析为保留时间部分的 Date。\r\n * 时间字符串(如 \"2026-07-02T09:00\" / \"2026-07-02 09:00\")按本地时间解析;\r\n * 纯日期字符串(如 \"2026-07-02\")回退到 toDate 的本地零点语义。\r\n */\r\nexport function toDateTime(value: string | Date): Date {\r\n if (value instanceof Date) {\r\n return new Date(value.getTime())\r\n }\r\n if (hasTimeComponent(value)) {\r\n const date = new Date(value.replace(\" \", \"T\"))\r\n if (!Number.isNaN(date.getTime())) {\r\n return date\r\n }\r\n }\r\n return toDate(value)\r\n}\r\n\r\nexport function startOfDay(date: Date): Date {\r\n return new Date(date.getFullYear(), date.getMonth(), date.getDate())\r\n}\r\n\r\nexport function addDays(date: string | Date, days: number): Date {\r\n const next = toDate(date)\r\n next.setDate(next.getDate() + days)\r\n return startOfDay(next)\r\n}\r\n\r\nexport function diffDays(start: string | Date, end: string | Date): number {\r\n return Math.round((toDate(end).getTime() - toDate(start).getTime()) / DAY_MS)\r\n}\r\n\r\nexport function addHours(date: string | Date, hours: number): Date {\r\n return new Date(toDateTime(date).getTime() + hours * HOUR_MS)\r\n}\r\n\r\n/** 小时差(可含分钟的小数,如 09:00-11:30 = 2.5) */\r\nexport function diffHours(start: string | Date, end: string | Date): number {\r\n return (toDateTime(end).getTime() - toDateTime(start).getTime()) / HOUR_MS\r\n}\r\n\r\nexport function inclusiveDays(start: string | Date, end: string | Date): number {\r\n return Math.max(0, diffDays(start, end) + 1)\r\n}\r\n\r\nexport function isValidDate(value: string | Date): boolean {\r\n const date = value instanceof Date ? value : new Date(value)\r\n return !Number.isNaN(date.getTime())\r\n}\r\n\r\nexport function formatDate(date: string | Date): string {\r\n const value = toDate(date)\r\n const year = value.getFullYear()\r\n const month = String(value.getMonth() + 1).padStart(2, \"0\")\r\n const day = String(value.getDate()).padStart(2, \"0\")\r\n return `${year}-${month}-${day}`\r\n}\r\n\r\nexport function formatDateTime(date: string | Date): string {\r\n const value = toDateTime(date)\r\n const hours = String(value.getHours()).padStart(2, \"0\")\r\n const minutes = String(value.getMinutes()).padStart(2, \"0\")\r\n return `${formatDate(value)} ${hours}:${minutes}`\r\n}\r\n\r\nexport function resultInvalidDate<T>(message: string, details?: unknown): Result<T> {\r\n return {\r\n ok: false,\r\n error: { code: \"INVALID_DATE\", message, details }\r\n }\r\n}\r\n","import type { TimeScale, ViewMode } from \"../types\"\r\nimport { addDays, diffDays, startOfDay } from \"../utils/date\"\r\n\r\nexport function computeTimeScale(\r\n start: Date,\r\n end: Date,\r\n viewMode: ViewMode,\r\n columnWidth: number,\r\n firstDayOfWeek: 0 | 1\r\n): TimeScale[] {\r\n const rangeStart = alignStart(start, viewMode, firstDayOfWeek)\r\n const endDate = startOfDay(end)\r\n const rangeEnd = viewMode === \"day\"\r\n ? addDays(endDate, (firstDayOfWeek + 6 - endDate.getDay() + 7) % 7)\r\n : endDate\r\n const scale: TimeScale[] = []\r\n let cursor = rangeStart\r\n let left = 0\r\n\r\n while (cursor.getTime() <= rangeEnd.getTime()) {\r\n const next = nextUnit(cursor, viewMode)\r\n const unitEnd = addDays(next, -1)\r\n const width = Math.max(1, diffDays(cursor, next)) * columnWidth\r\n\r\n scale.push({\r\n start: cursor,\r\n end: unitEnd,\r\n left,\r\n width\r\n })\r\n\r\n left += width\r\n cursor = next\r\n }\r\n\r\n return scale\r\n}\r\n\r\nfunction alignStart(date: Date, viewMode: ViewMode, firstDayOfWeek: 0 | 1): Date {\r\n const start = startOfDay(date)\r\n if (viewMode === \"day\" || viewMode === \"week\") {\r\n const delta = (start.getDay() - firstDayOfWeek + 7) % 7\r\n return addDays(start, -delta)\r\n }\r\n if (viewMode === \"month\") {\r\n return new Date(start.getFullYear(), start.getMonth(), 1)\r\n }\r\n if (viewMode === \"quarter\") {\r\n return new Date(start.getFullYear(), Math.floor(start.getMonth() / 3) * 3, 1)\r\n }\r\n if (viewMode === \"year\") {\r\n return new Date(start.getFullYear(), 0, 1)\r\n }\r\n return start\r\n}\r\n\r\nfunction nextUnit(date: Date, viewMode: ViewMode): Date {\r\n if (viewMode === \"week\") {\r\n return addDays(date, 7)\r\n }\r\n if (viewMode === \"month\") {\r\n return new Date(date.getFullYear(), date.getMonth() + 1, 1)\r\n }\r\n if (viewMode === \"quarter\") {\r\n return new Date(date.getFullYear(), date.getMonth() + 3, 1)\r\n }\r\n if (viewMode === \"year\") {\r\n return new Date(date.getFullYear() + 1, 0, 1)\r\n }\r\n return addDays(date, 1)\r\n}\r\n","import type { TimeScale } from \"../types\"\nimport { addDays, addHours, inclusiveDays, startOfDay } from \"../utils/date\"\n\nexport interface HourScale {\n /** 每日一格(顶行表头用) */\n days: TimeScale[]\n /** 每小时一格(底部刻度用) */\n hours: TimeScale[]\n start: Date\n end: Date\n}\n\n/** 小时级时间刻度:范围对齐到整天,每天拆成 24 个整点小时,width = hourWidth */\nexport function computeHourScale(start: Date, end: Date, hourWidth: number): HourScale {\n const rangeStart = startOfDay(start)\n const rangeEnd = startOfDay(end)\n const totalDays = Math.max(1, inclusiveDays(rangeStart, rangeEnd))\n const days: TimeScale[] = []\n const hours: TimeScale[] = []\n\n for (let d = 0; d < totalDays; d += 1) {\n const dayStart = addDays(rangeStart, d)\n days.push({\n start: dayStart,\n end: addHours(dayStart, 24),\n left: d * 24 * hourWidth,\n width: 24 * hourWidth\n })\n for (let h = 0; h < 24; h += 1) {\n const hourStart = addHours(dayStart, h)\n hours.push({\n start: hourStart,\n end: addHours(hourStart, 1),\n left: (d * 24 + h) * hourWidth,\n width: hourWidth\n })\n }\n }\n\n return { days, hours, start: rangeStart, end: rangeEnd }\n}\n","import type { GanttLink, GanttTask, LinkType, Result } from \"../types\"\r\nimport { addDays, diffDays, inclusiveDays, toDate } from \"../utils/date\"\r\nimport { checkCyclicDependency } from \"../services/checkCyclicDependency\"\r\n\r\nexport function scheduleByDependencies(tasks: GanttTask[], links: GanttLink[]): Result<GanttTask[]> {\r\n const cycle = checkCyclicDependency(links)\r\n if (cycle.hasCycle) {\r\n return {\r\n ok: false,\r\n error: {\r\n code: \"CYCLE_DEPENDENCY\",\r\n message: \"Dependency graph contains a cycle.\",\r\n details: cycle.cyclePath\r\n }\r\n }\r\n }\r\n\r\n const taskMap = new Map(tasks.map((task) => [task.id, cloneTask(task)]))\r\n const incoming = new Map<string, GanttLink[]>()\r\n const outgoing = new Map<string, GanttLink[]>()\r\n\r\n for (const link of links) {\r\n const source = taskMap.get(link.sourceId)\r\n const target = taskMap.get(link.targetId)\r\n if (!source || !target) {\r\n return {\r\n ok: false,\r\n error: {\r\n code: \"ORPHAN_DEPENDENCY\",\r\n message: `Dependency ${link.id} references a missing task.`\r\n }\r\n }\r\n }\r\n incoming.set(link.targetId, [...(incoming.get(link.targetId) ?? []), link])\r\n outgoing.set(link.sourceId, [...(outgoing.get(link.sourceId) ?? []), link])\r\n }\r\n\r\n const ordered = topologicalOrder(tasks.map((task) => task.id), links)\r\n\r\n for (const taskId of ordered) {\r\n const task = taskMap.get(taskId)\r\n if (!task || task.schedulingMode === \"manual\") {\r\n continue\r\n }\r\n\r\n const linksToTask = incoming.get(taskId) ?? []\r\n let start = toDate(task.actual.start)\r\n let end = toDate(task.actual.end)\r\n const duration = Math.max(0, task.duration ?? inclusiveDays(start, end))\r\n\r\n for (const link of linksToTask) {\r\n const source = taskMap.get(link.sourceId)\r\n if (!source) {\r\n continue\r\n }\r\n\r\n const candidate = applyDependency(source, start, end, duration, link.type, link.lag ?? 0)\r\n if (candidate.start.getTime() > start.getTime()) {\r\n start = candidate.start\r\n }\r\n if (candidate.end.getTime() > end.getTime()) {\r\n end = candidate.end\r\n }\r\n }\r\n\r\n const normalizedEnd = task.type === \"milestone\" ? start : addDays(start, Math.max(0, duration - 1))\r\n task.actual = {\r\n ...task.actual,\r\n start,\r\n end: normalizedEnd\r\n }\r\n }\r\n\r\n return { ok: true, data: tasks.map((task) => taskMap.get(task.id) ?? task) }\r\n}\r\n\r\nfunction applyDependency(\r\n source: GanttTask,\r\n currentStart: Date,\r\n currentEnd: Date,\r\n duration: number,\r\n type: LinkType,\r\n lag: number\r\n): { start: Date; end: Date } {\r\n const sourceStart = toDate(source.actual.start)\r\n const sourceEnd = toDate(source.actual.end)\r\n const span = Math.max(0, duration - 1)\r\n\r\n if (type === \"FS\") {\r\n const start = addDays(sourceEnd, lag + 1)\r\n return { start, end: addDays(start, span) }\r\n }\r\n\r\n if (type === \"SS\") {\r\n const start = addDays(sourceStart, lag)\r\n return { start, end: addDays(start, span) }\r\n }\r\n\r\n if (type === \"FF\") {\r\n const end = addDays(sourceEnd, lag)\r\n return { start: addDays(end, -span), end }\r\n }\r\n\r\n const end = addDays(sourceStart, lag)\r\n return { start: addDays(end, -span), end: end.getTime() > currentEnd.getTime() ? end : currentEnd }\r\n}\r\n\r\nfunction topologicalOrder(ids: string[], links: GanttLink[]): string[] {\r\n const indegree = new Map(ids.map((id) => [id, 0]))\r\n const outgoing = new Map<string, string[]>()\r\n\r\n for (const link of links) {\r\n indegree.set(link.targetId, (indegree.get(link.targetId) ?? 0) + 1)\r\n outgoing.set(link.sourceId, [...(outgoing.get(link.sourceId) ?? []), link.targetId])\r\n }\r\n\r\n const queue = ids.filter((id) => (indegree.get(id) ?? 0) === 0)\r\n const ordered: string[] = []\r\n\r\n while (queue.length) {\r\n const current = queue.shift()!\r\n ordered.push(current)\r\n for (const target of outgoing.get(current) ?? []) {\r\n indegree.set(target, (indegree.get(target) ?? 1) - 1)\r\n if (indegree.get(target) === 0) {\r\n queue.push(target)\r\n }\r\n }\r\n }\r\n\r\n return ordered.length === ids.length ? ordered : ids\r\n}\r\n\r\nfunction cloneTask(task: GanttTask): GanttTask {\r\n return {\r\n ...task,\r\n plan: { ...task.plan },\r\n actual: { ...task.actual },\r\n dependencies: task.dependencies?.map((dependency) => ({ ...dependency }))\r\n }\r\n}\r\n\r\nexport function shiftTask(task: GanttTask, start: string | Date): GanttTask {\r\n const currentStart = toDate(task.actual.start)\r\n const nextStart = toDate(start)\r\n const delta = diffDays(currentStart, nextStart)\r\n return {\r\n ...task,\r\n actual: {\r\n ...task.actual,\r\n start: nextStart,\r\n end: addDays(toDate(task.actual.end), delta)\r\n }\r\n }\r\n}\r\n","import type { GanttConfig, GanttLink, GanttTask, Result, TaskLayout, Viewport } from \"../types\"\r\nimport { defaultConfig } from \"../types\"\r\nimport { flattenTasks } from \"../services/flattenTasks\"\r\nimport { normalizeLinks } from \"../services/normalizeLinks\"\r\nimport { scheduleByDependencies } from \"./scheduling\"\r\nimport { diffDays, inclusiveDays, isValidDate, toDate } from \"../utils/date\"\r\n\r\nexport function computeLayout(\r\n tasks: GanttTask[],\r\n links: GanttLink[],\r\n config: Partial<GanttConfig>,\r\n collapsedIds: Set<string> = new Set(),\r\n viewport?: Viewport\r\n): Result<TaskLayout[]> {\r\n const mergedConfig = { ...defaultConfig, ...config }\r\n\r\n for (const task of tasks) {\r\n if (!isValidDate(task.actual.start) || !isValidDate(task.actual.end)) {\r\n return {\r\n ok: false,\r\n error: {\r\n code: \"INVALID_DATE\",\r\n message: `Task ${task.id} has invalid actual dates.`\r\n }\r\n }\r\n }\r\n }\r\n\r\n const normalizedLinks = normalizeLinks(tasks, links)\r\n const scheduled = mergedConfig.autoSchedule === false\r\n ? { ok: true as const, data: tasks }\r\n : scheduleByDependencies(tasks, normalizedLinks)\r\n\r\n if (!scheduled.ok) {\r\n return scheduled\r\n }\r\n\r\n const flatTasks = flattenTasks(scheduled.data, collapsedIds)\r\n const firstDate = mergedConfig.visibleRange\r\n ? toDate(mergedConfig.visibleRange.start)\r\n : minDate(scheduled.data.map((task) => toDate(task.actual.start)))\r\n\r\n const visibleStart = viewport\r\n ? Math.max(0, Math.floor(viewport.scrollTop / mergedConfig.rowHeight) - 8)\r\n : 0\r\n const visibleEnd = viewport\r\n ? Math.ceil((viewport.scrollTop + viewport.clientHeight) / mergedConfig.rowHeight) + 8\r\n : Number.POSITIVE_INFINITY\r\n\r\n const layouts = flatTasks\r\n .filter((flat) => flat.rowIndex >= visibleStart && flat.rowIndex <= visibleEnd)\r\n .map<TaskLayout>((flat) => {\r\n const start = toDate(flat.task.actual.start)\r\n const end = toDate(flat.task.actual.end)\r\n const duration = flat.task.type === \"milestone\" ? 0 : inclusiveDays(start, end)\r\n\r\n return {\r\n taskId: flat.task.id,\r\n rowIndex: flat.rowIndex,\r\n left: diffDays(firstDate, start) * mergedConfig.columnWidth,\r\n width: Math.max(flat.task.type === \"milestone\" ? mergedConfig.columnWidth / 2 : mergedConfig.columnWidth, duration * mergedConfig.columnWidth),\r\n top: mergedConfig.headerHeight + flat.rowIndex * mergedConfig.rowHeight,\r\n depth: flat.depth,\r\n isCritical: false\r\n }\r\n })\r\n\r\n return { ok: true, data: layouts }\r\n}\r\n\r\nfunction minDate(dates: Date[]): Date {\r\n return new Date(Math.min(...dates.map((date) => date.getTime())))\r\n}\r\n","import type { AffectedTasks, GanttConfig, GanttLink, GanttTask, PatchTask, Result } from \"../types\"\r\nimport { flattenTasks } from \"../services/flattenTasks\"\r\nimport { scheduleByDependencies, shiftTask } from \"./scheduling\"\r\nimport { addDays, inclusiveDays, toDate } from \"../utils/date\"\r\n\r\nexport function computeImpact(\r\n taskId: string,\r\n patch: PatchTask,\r\n tasks: GanttTask[],\r\n links: GanttLink[],\r\n config: Partial<GanttConfig>\r\n): Result<AffectedTasks> {\r\n const target = tasks.find((task) => task.id === taskId)\r\n if (!target) {\r\n return {\r\n ok: false,\r\n error: {\r\n code: \"MISSING_TASK\",\r\n message: `Task ${taskId} does not exist.`\r\n }\r\n }\r\n }\r\n\r\n const patchedTasks = tasks.map((task) => {\r\n if (task.id !== taskId) {\r\n return task\r\n }\r\n\r\n let next = { ...task, actual: { ...task.actual } }\r\n if (patch.actualStart) {\r\n next = shiftTask(next, patch.actualStart)\r\n }\r\n if (patch.actualEnd) {\r\n next.actual.end = patch.actualEnd\r\n }\r\n if (typeof patch.progress === \"number\") {\r\n next.actual.progress = patch.progress\r\n }\r\n if (patch.duration) {\r\n next.duration = patch.duration\r\n next.actual.end = addDays(toDate(next.actual.start), Math.max(0, patch.duration - 1))\r\n }\r\n if (patch.name) {\r\n next.name = patch.name\r\n }\r\n if (patch.type) {\r\n next.type = patch.type\r\n }\r\n if (\"parentId\" in patch) {\r\n next.parentId = patch.parentId\r\n }\r\n if (patch.color) {\r\n next.color = patch.color\r\n }\r\n if (\"planColor\" in patch) {\r\n next.planColor = patch.planColor\r\n }\r\n if (patch.schedulingMode) {\r\n next.schedulingMode = patch.schedulingMode\r\n }\r\n return next\r\n })\r\n\r\n const scheduled = config.autoSchedule === false\r\n ? { ok: true as const, data: patchedTasks }\r\n : scheduleByDependencies(patchedTasks, links)\r\n\r\n if (!scheduled.ok) {\r\n return scheduled\r\n }\r\n\r\n const rows = new Map(flattenTasks(scheduled.data).map((flat) => [flat.task.id, flat.rowIndex]))\r\n const originalById = new Map(tasks.map((task) => [task.id, task]))\r\n const changed = scheduled.data\r\n .filter((task) => {\r\n const original = originalById.get(task.id)\r\n return original && (\r\n String(original.actual.start) !== String(task.actual.start) ||\r\n String(original.actual.end) !== String(task.actual.end)\r\n )\r\n })\r\n .map((task) => ({\r\n taskId: task.id,\r\n actualStart: task.actual.start,\r\n actualEnd: task.actual.end,\r\n rowIndex: rows.get(task.id) ?? 0\r\n }))\r\n\r\n const conflicts = scheduled.data\r\n .filter((task) => task.constraint?.date && inclusiveDays(task.actual.start, task.constraint.date) < 0)\r\n .map((task) => ({\r\n taskId: task.id,\r\n constraintType: task.constraint!.type,\r\n message: \"Task violates its configured constraint date.\"\r\n }))\r\n\r\n return { ok: true, data: { changed, conflicts } }\r\n}\r\n","import type {\r\n GanttConfig,\r\n Resource,\r\n ResourceLayout,\r\n ResourceRowLayout,\r\n ResourceUsage,\r\n ResourceUsageLayout,\r\n Result,\r\n Viewport\r\n} from \"../types\"\r\nimport { defaultConfig } from \"../types\"\r\nimport {\r\n addHours,\r\n diffDays,\r\n diffHours,\r\n hasTimeComponent,\r\n inclusiveDays,\r\n isValidDate,\r\n startOfDay,\r\n toDate,\r\n toDateTime\r\n} from \"../utils/date\"\r\nimport { RESOURCE_COLOR_PALETTE } from \"../styles/colors\"\r\n\r\n// 向后兼容:RESOURCE_COLOR_PALETTE 原在本文件导出,继续从这里再导出一次\r\nexport { RESOURCE_COLOR_PALETTE }\r\n\r\nfunction colorHash(source: string): number {\r\n return [...source].reduce((sum, char) => sum + (char.codePointAt(0) ?? 0), 0)\r\n}\r\n\r\n/** 解析资源主色:resource.color → config.resourceColors[id] → 名称 hash 调色板(确定性) */\r\nexport function resolveResourceColor(\r\n resource: Resource,\r\n resourceColors?: Record<string, string>\r\n): string {\r\n if (resource.color) {\r\n return resource.color\r\n }\r\n if (resourceColors?.[resource.id]) {\r\n return resourceColors[resource.id]\r\n }\r\n const source = resource.name || resource.id\r\n return RESOURCE_COLOR_PALETTE[colorHash(source) % RESOURCE_COLOR_PALETTE.length]\r\n}\r\n\r\n/** 解析占用条颜色:usage.color → 所属资源色 */\r\nexport function resolveUsageColor(\r\n usage: ResourceUsage,\r\n resource: Resource,\r\n resourceColors?: Record<string, string>\r\n): string {\r\n return usage.color || resolveResourceColor(resource, resourceColors)\r\n}\r\n\r\n/**\r\n * 将占用解析为布局用的起止 Date。\r\n * - day:与 GanttTask 一致,截断到天。\r\n * - hour:保留时间;纯日期(未携带时间)按\"含结束日整天\"处理,与 day 模式的含末语义一致。\r\n */\r\nexport function resolveUsageTimes(\r\n usage: ResourceUsage,\r\n timeUnit: \"day\" | \"hour\"\r\n): { start: Date; end: Date } {\r\n if (timeUnit === \"hour\") {\r\n const start = toDateTime(usage.start)\r\n const endRaw = toDateTime(usage.end)\r\n return { start, end: hasTimeComponent(usage.end) ? endRaw : addHours(endRaw, 24) }\r\n }\r\n return { start: toDate(usage.start), end: toDate(usage.end) }\r\n}\r\n\r\n/**\r\n * 资源视图布局:每个资源一行(Y 轴),占用时间段以横向色条展示。\r\n * 同一资源同时段多个占用按贪心 first-fit 车道分配,行内堆叠子条。\r\n * day 粒度像素数学与 computeLayout 一致(diffDays × columnWidth);\r\n * hour 粒度按 diffHours × hourWidth 渲染,纯日期占用含结束日整天。\r\n * 传入 viewport 时仅产出可见行窗口内的占用几何(±8 overscan),rows 始终全量返回。\r\n */\r\nexport function computeResourceLayout(\r\n resources: Resource[],\r\n usages: ResourceUsage[],\r\n config: Partial<GanttConfig>,\r\n viewport?: Viewport\r\n): Result<ResourceLayout> {\r\n const mergedConfig = { ...defaultConfig, ...config }\r\n const timeUnit = mergedConfig.timeUnit ?? \"day\"\r\n const hourMode = timeUnit === \"hour\"\r\n const hourW = mergedConfig.hourWidth ?? 12\r\n\r\n for (const usage of usages) {\r\n const { start, end } = resolveUsageTimes(usage, timeUnit)\r\n if (!isValidDate(start) || !isValidDate(end)) {\r\n return {\r\n ok: false,\r\n error: {\r\n code: \"INVALID_DATE\",\r\n message: `Resource usage ${usage.id} has invalid dates.`\r\n }\r\n }\r\n }\r\n }\r\n\r\n // 行序:resources 顺序在前,usage 引用但未声明的 resourceId 追加在后(容错)\r\n const orderedIds: string[] = []\r\n const seen = new Set<string>()\r\n for (const resource of resources) {\r\n if (!seen.has(resource.id)) {\r\n seen.add(resource.id)\r\n orderedIds.push(resource.id)\r\n }\r\n }\r\n for (const usage of usages) {\r\n if (!seen.has(usage.resourceId)) {\r\n seen.add(usage.resourceId)\r\n orderedIds.push(usage.resourceId)\r\n }\r\n }\r\n\r\n const resourceById = new Map(resources.map((resource) => [resource.id, resource]))\r\n const usagesByResource = new Map<string, ResourceUsage[]>()\r\n for (const usage of usages) {\r\n usagesByResource.set(usage.resourceId, [...(usagesByResource.get(usage.resourceId) ?? []), usage])\r\n }\r\n\r\n const rows: ResourceRowLayout[] = []\r\n const usageLayouts: ResourceUsageLayout[] = []\r\n const laneIndexByUsage = new Map<string, number>()\r\n\r\n orderedIds.forEach((resourceId, rowIndex) => {\r\n const resource = resourceById.get(resourceId) ?? {\r\n id: resourceId,\r\n name: resourceId\r\n } satisfies Resource\r\n const grouped = [...(usagesByResource.get(resourceId) ?? [])].sort((a, b) => {\r\n const as = resolveUsageTimes(a, timeUnit)\r\n const bs = resolveUsageTimes(b, timeUnit)\r\n return as.start.getTime() - bs.start.getTime() || as.end.getTime() - bs.end.getTime()\r\n })\r\n\r\n // 贪心 first-fit 车道分配:占用放入第一个\"前一个占用结束不晚于其开始\"的车道。\r\n // day:diffDays(end, start) > 0(相邻日复用);hour:diffHours(end, start) >= 0(首尾相接复用)\r\n const laneEnds: Array<Date | null> = []\r\n for (const usage of grouped) {\r\n const { start, end } = resolveUsageTimes(usage, timeUnit)\r\n let laneIndex = -1\r\n for (let k = 0; k < laneEnds.length; k += 1) {\r\n const laneEnd = laneEnds[k]\r\n if (laneEnd === null || isLaneFree(laneEnd, start, hourMode)) {\r\n laneIndex = k\r\n break\r\n }\r\n }\r\n if (laneIndex === -1) {\r\n laneIndex = laneEnds.length\r\n laneEnds.push(null)\r\n }\r\n laneEnds[laneIndex] = end\r\n laneIndexByUsage.set(usage.id, laneIndex)\r\n }\r\n\r\n rows.push({\r\n resourceId,\r\n rowIndex,\r\n laneCount: laneEnds.length,\r\n color: resolveResourceColor(resource, mergedConfig.resourceColors),\r\n usageIds: grouped.map((usage) => usage.id)\r\n })\r\n })\r\n\r\n const usageById = new Map(usages.map((usage) => [usage.id, usage]))\r\n const visibleStart = viewport\r\n ? Math.max(0, Math.floor(viewport.scrollTop / mergedConfig.rowHeight) - 8)\r\n : 0\r\n const visibleEnd = viewport\r\n ? Math.ceil((viewport.scrollTop + viewport.clientHeight) / mergedConfig.rowHeight) + 8\r\n : Number.POSITIVE_INFINITY\r\n\r\n const firstDate = hourMode\r\n ? startOfDay(\r\n mergedConfig.visibleRange\r\n ? toDateTime(mergedConfig.visibleRange.start)\r\n : usages.length\r\n ? minDate(usages.map((usage) => resolveUsageTimes(usage, \"hour\").start))\r\n : toDateTime(new Date())\r\n )\r\n : mergedConfig.visibleRange\r\n ? toDate(mergedConfig.visibleRange.start)\r\n : usages.length\r\n ? minDate(usages.map((usage) => toDate(usage.start)))\r\n : toDate(new Date())\r\n\r\n for (const row of rows) {\r\n if (row.rowIndex < visibleStart || row.rowIndex > visibleEnd) {\r\n continue\r\n }\r\n const resource = resourceById.get(row.resourceId) ?? { id: row.resourceId, name: row.resourceId }\r\n const laneCount = Math.max(1, row.laneCount)\r\n const slot = mergedConfig.rowHeight / laneCount\r\n const barHeight = Math.max(6, Math.min(14, slot - 4))\r\n\r\n for (const usageId of row.usageIds) {\r\n const usage = usageById.get(usageId)\r\n if (!usage) {\r\n continue\r\n }\r\n const { start, end } = resolveUsageTimes(usage, timeUnit)\r\n const laneIndex = laneIndexByUsage.get(usageId) ?? 0\r\n const laneTop = laneIndex * slot + (slot - barHeight) / 2\r\n const left = hourMode\r\n ? diffHours(firstDate, start) * hourW\r\n : diffDays(firstDate, start) * mergedConfig.columnWidth\r\n const width = hourMode\r\n ? Math.max(hourW, diffHours(start, end) * hourW)\r\n : Math.max(mergedConfig.columnWidth, inclusiveDays(start, end) * mergedConfig.columnWidth)\r\n\r\n usageLayouts.push({\r\n usageId,\r\n resourceId: row.resourceId,\r\n rowIndex: row.rowIndex,\r\n laneIndex,\r\n laneCount,\r\n left,\r\n width,\r\n top: mergedConfig.headerHeight + row.rowIndex * mergedConfig.rowHeight + laneTop,\r\n height: barHeight\r\n })\r\n }\r\n }\r\n\r\n return { ok: true, data: { rows, usages: usageLayouts } }\r\n}\r\n\r\nfunction isLaneFree(laneEnd: Date, start: Date, hourMode: boolean): boolean {\r\n return hourMode ? diffHours(laneEnd, start) >= 0 : diffDays(laneEnd, start) > 0\r\n}\r\n\r\nfunction minDate(dates: Date[]): Date {\r\n return new Date(Math.min(...dates.map((date) => date.getTime())))\r\n}\r\n","import type {\r\n GanttConfig,\r\n GanttTask,\r\n GanttLink,\r\n GanttMarker,\r\n PatchTask,\r\n TaskLayout,\r\n TimeScale,\r\n FlatTask,\r\n Result\r\n} from \"../types\"\r\nimport { defaultConfig } from \"../types\"\r\nimport { computeLayout } from \"../engines/computeLayout\"\r\nimport { computeTimeScale } from \"../services/computeTimeScale\"\r\nimport { flattenTasks } from \"../services/flattenTasks\"\r\nimport { toDate, diffDays, addDays } from \"../utils/date\"\r\n\r\n/**\r\n * 把扁平的 PatchTask 合并回嵌套的 GanttTask。\r\n * PatchTask 用 planStart/planEnd/actualStart/actualEnd 表示日期,\r\n * GanttTask 用 plan.start/end、actual.start/end 嵌套表示——这是组件层\r\n * 和数据层之间的桥,本该在引擎层而不是散落在各 demo 里。\r\n */\r\nexport function mergeTaskPatch(task: GanttTask, patch: PatchTask): GanttTask {\r\n return {\r\n ...task,\r\n ...patch,\r\n plan: {\r\n ...task.plan,\r\n start: patch.planStart ?? task.plan.start,\r\n end: patch.planEnd ?? task.plan.end,\r\n progress: patch.progress ?? task.plan.progress\r\n },\r\n actual: {\r\n ...task.actual,\r\n start: patch.actualStart ?? task.actual.start,\r\n end: patch.actualEnd ?? task.actual.end,\r\n progress: patch.progress ?? task.actual.progress\r\n },\r\n custom: patch.custom ? { ...task.custom, ...patch.custom } : task.custom\r\n }\r\n}\r\n\r\nexport interface GanttEngineOptions {\r\n tasks?: GanttTask[]\r\n links?: GanttLink[]\r\n config?: Partial<GanttConfig>\r\n /** 滚动容器元素;命令式视口 API(scrollToDate / zoomToFit)需要它。纯计算场景可不传。 */\r\n container?: HTMLElement | null\r\n /** 初始折叠的任务 id */\r\n collapsedIds?: Iterable<string>\r\n /** 里程碑标记 */\r\n markers?: GanttMarker[]\r\n}\r\n\r\nexport type GanttEngineEvent =\r\n | \"ready\"\r\n | \"taskschange\"\r\n | \"taskchange\"\r\n | \"taskcreate\"\r\n | \"taskdelete\"\r\n | \"linkschange\"\r\n | \"configchange\"\r\n | \"collapsechange\"\r\n | \"previewchange\"\r\n | \"markerschange\"\r\n | \"destroy\"\r\n\r\ntype Listener = (...args: unknown[]) => void\r\n\r\n/** 命令式甘特图引擎。框架无关,可被 Vue/React/原生 各自的薄壳组件驱动。 */\r\nexport class GanttEngine {\r\n private tasks: GanttTask[]\r\n private links: GanttLink[]\r\n private config: GanttConfig\r\n private readonly container: HTMLElement | null\r\n private collapsedIds: Set<string>\r\n private dragPreview: { taskId: string; patch: PatchTask; affected?: Record<string, PatchTask> } | null = null\r\n private markers: GanttMarker[]\r\n private readonly listeners: Map<GanttEngineEvent, Set<Listener>>\r\n private destroyed = false\r\n\r\n constructor(options: GanttEngineOptions = {}) {\r\n this.tasks = options.tasks ? options.tasks.map((task) => ({ ...task })) : []\r\n this.links = options.links ? options.links.map((link) => ({ ...link })) : []\r\n this.config = { ...defaultConfig, ...options.config }\r\n this.container = options.container ?? null\r\n this.collapsedIds = new Set(options.collapsedIds ?? [])\r\n this.markers = options.markers ? options.markers.map((marker) => ({ ...marker })) : []\r\n this.listeners = new Map()\r\n // 异步发 ready,确保订阅者能在构造后、首次 emit 前注册\r\n queueMicrotask(() => this.emit(\"ready\"))\r\n }\r\n\r\n // ── 状态查询 ──\r\n getTasks(): GanttTask[] {\r\n return this.tasks.map((task) => ({ ...task }))\r\n }\r\n getLinks(): GanttLink[] {\r\n return this.links.map((link) => ({ ...link }))\r\n }\r\n getMarkers(): GanttMarker[] {\r\n return this.markers.map((marker) => ({ ...marker }))\r\n }\r\n getConfig(): GanttConfig {\r\n return { ...this.config }\r\n }\r\n getTask(id: string): GanttTask | undefined {\r\n const task = this.tasks.find((item) => item.id === id)\r\n return task ? { ...task } : undefined\r\n }\r\n getCollapsedIds(): string[] {\r\n return Array.from(this.collapsedIds)\r\n }\r\n isDestroyed(): boolean {\r\n return this.destroyed\r\n }\r\n\r\n // ── 命令式变更(均触发对应事件) ──\r\n setTasks(tasks: GanttTask[]): void {\r\n this.assertNotDestroyed()\r\n this.tasks = tasks.map((task) => ({ ...task }))\r\n this.emit(\"taskschange\", this.getTasks())\r\n }\r\n setLinks(links: GanttLink[]): void {\r\n this.assertNotDestroyed()\r\n this.links = links.map((link) => ({ ...link }))\r\n this.emit(\"linkschange\", this.getLinks())\r\n }\r\n setMarkers(markers: GanttMarker[]): void {\r\n this.assertNotDestroyed()\r\n this.markers = markers.map((marker) => ({ ...marker }))\r\n this.emit(\"markerschange\", this.getMarkers())\r\n }\r\n /** 合并配置 patch,等价于 setConfig(mergeConfig(patch)) */\r\n mergeConfig(patch: Partial<GanttConfig>): void {\r\n this.assertNotDestroyed()\r\n this.config = { ...this.config, ...patch }\r\n this.emit(\"configchange\", this.getConfig())\r\n }\r\n setConfig(config: Partial<GanttConfig>): void {\r\n this.mergeConfig(config)\r\n }\r\n /** 用 mergeTaskPatch 把扁平 patch 合并回嵌套结构后更新单个任务 */\r\n setTask(id: string, patch: PatchTask): void {\r\n this.assertNotDestroyed()\r\n const target = this.tasks.find((task) => task.id === id)\r\n if (!target) {\r\n return\r\n }\r\n const updated = mergeTaskPatch(target, patch)\r\n this.tasks = this.tasks.map((task) => (task.id === id ? updated : task))\r\n this.emit(\"taskchange\", id, patch, { ...updated })\r\n }\r\n addTask(task: GanttTask): void {\r\n this.assertNotDestroyed()\r\n this.tasks = [...this.tasks, { ...task }]\r\n this.emit(\"taskcreate\", { ...task })\r\n }\r\n removeTask(id: string): void {\r\n this.assertNotDestroyed()\r\n const removed = this.tasks.find((task) => task.id === id)\r\n if (!removed) return\r\n this.tasks = this.tasks.filter((task) => task.id !== id)\r\n this.collapsedIds.delete(id)\r\n this.emit(\"taskdelete\", id)\r\n }\r\n collapse(id: string, collapsed = true): void {\r\n this.assertNotDestroyed()\r\n if (collapsed) this.collapsedIds.add(id)\r\n else this.collapsedIds.delete(id)\r\n this.emit(\"collapsechange\", this.getCollapsedIds())\r\n }\r\n toggleCollapse(id: string): void {\r\n this.collapse(id, !this.collapsedIds.has(id))\r\n }\r\n /** 批量折叠/展开多个任务(如\"折叠全部分组\") */\r\n setCollapsed(ids: string[], collapsed = true): void {\r\n this.assertNotDestroyed()\r\n const next = new Set(this.collapsedIds)\r\n for (const id of ids) {\r\n if (collapsed) {\r\n next.add(id)\r\n } else {\r\n next.delete(id)\r\n }\r\n }\r\n this.collapsedIds = next\r\n this.emit(\"collapsechange\", this.getCollapsedIds())\r\n }\r\n\r\n // ── 拖拽预览(交互态源;组件写入迁移到 engine,dateRange 可据此扩展) ──\r\n setPreview(preview: { taskId: string; patch: PatchTask; affected?: Record<string, PatchTask> }): void {\r\n this.assertNotDestroyed()\r\n this.dragPreview = {\r\n taskId: preview.taskId,\r\n patch: { ...preview.patch },\r\n affected: preview.affected ? { ...preview.affected } : undefined\r\n }\r\n this.emit(\"previewchange\", this.getPreview())\r\n }\r\n clearPreview(): void {\r\n if (!this.dragPreview) {\r\n return\r\n }\r\n this.dragPreview = null\r\n this.emit(\"previewchange\", null)\r\n }\r\n getPreview(): { taskId: string; patch: PatchTask; affected?: Record<string, PatchTask> } | null {\r\n if (!this.dragPreview) {\r\n return null\r\n }\r\n return {\r\n taskId: this.dragPreview.taskId,\r\n patch: { ...this.dragPreview.patch },\r\n affected: this.dragPreview.affected ? { ...this.dragPreview.affected } : undefined\r\n }\r\n }\r\n\r\n // ── 计算(delegate 到 core 纯函数) ──\r\n /** 数据真实日期范围(未含 viewport pad) */\r\n getDateRange(): { start: Date; end: Date } {\r\n const dates: Date[] = []\r\n for (const task of this.tasks) {\r\n dates.push(\r\n toDate(task.plan.start),\r\n toDate(task.plan.end),\r\n toDate(task.actual.start),\r\n toDate(task.actual.end)\r\n )\r\n }\r\n for (const marker of this.markers) {\r\n dates.push(toDate(marker.date))\r\n }\r\n // 合并拖拽预览:用 mergeTaskPatch 算 patch 后日期扩范围(只扩不缩)\r\n const preview = this.dragPreview\r\n if (preview) {\r\n const ids = [preview.taskId, ...Object.keys(preview.affected ?? {})]\r\n for (const id of ids) {\r\n const task = this.tasks.find((item) => item.id === id)\r\n if (!task) {\r\n continue\r\n }\r\n const patch = preview.taskId === id ? preview.patch : preview.affected?.[id]\r\n if (!patch) {\r\n continue\r\n }\r\n const merged = mergeTaskPatch(task, patch)\r\n dates.push(\r\n toDate(merged.plan.start),\r\n toDate(merged.plan.end),\r\n toDate(merged.actual.start),\r\n toDate(merged.actual.end)\r\n )\r\n }\r\n }\r\n if (!dates.length) {\r\n const today = toDate(new Date())\r\n return { start: today, end: addDays(today, 30) }\r\n }\r\n return {\r\n start: new Date(Math.min(...dates.map((d) => d.getTime()))),\r\n end: new Date(Math.max(...dates.map((d) => d.getTime())))\r\n }\r\n }\r\n getTimeScale(): TimeScale[] {\r\n const range = this.getDateRange()\r\n return computeTimeScale(\r\n range.start,\r\n range.end,\r\n this.config.viewMode,\r\n this.config.columnWidth,\r\n this.config.firstDayOfWeek\r\n )\r\n }\r\n getLayout(): Result<TaskLayout[]> {\r\n return computeLayout(\r\n this.tasks,\r\n this.links,\r\n this.config,\r\n this.collapsedIds\r\n )\r\n }\r\n getFlatTasks(): FlatTask[] {\r\n return flattenTasks(this.tasks, this.collapsedIds)\r\n }\r\n getTotalWidth(): number {\r\n return this.getTimeScale().reduce((sum, tick) => sum + tick.width, 0)\r\n }\r\n getTotalHeight(): number {\r\n const rows = this.getFlatTasks().length\r\n return this.config.headerHeight + rows * this.config.rowHeight\r\n }\r\n\r\n // ── 视口命令(需要 container) ──\r\n getScrollLeft(): number {\r\n return this.container?.scrollLeft ?? 0\r\n }\r\n setScrollLeft(px: number): void {\r\n if (!this.container) return\r\n const max = Math.max(0, this.container.scrollWidth - this.container.clientWidth)\r\n this.container.scrollLeft = Math.max(0, Math.min(max, px))\r\n }\r\n /** 把当前 scrollLeft 限制到 [0, maxScrollLeft]。\r\n * 拖拽预览收缩(松手/取消)后 timeline 总宽度变小,scrollLeft 可能超出新边界,\r\n * 导致 canvas translate 超出内容区、右侧网格未覆盖。此时调用本方法防超界。 */\r\n clampScrollLeft(): void {\r\n if (!this.container) return\r\n const max = Math.max(0, this.container.scrollWidth - this.container.clientWidth)\r\n if (this.container.scrollLeft > max) {\r\n this.container.scrollLeft = max\r\n }\r\n }\r\n getScrollTop(): number {\r\n return this.container?.scrollTop ?? 0\r\n }\r\n setScrollTop(px: number): void {\r\n if (!this.container) return\r\n const max = Math.max(0, this.container.scrollHeight - this.container.clientHeight)\r\n this.container.scrollTop = Math.max(0, Math.min(max, px))\r\n }\r\n /** 滚动到包含目标日期的 tick 列(跨 viewMode 精确) */\r\n scrollToDate(date: string | Date): void {\r\n if (!this.container) return\r\n const target = toDate(date)\r\n const scale = this.getTimeScale()\r\n const tick = scale.find((t) => target >= t.start && target <= t.end)\r\n const left = tick ? tick.left : diffDays(this.getDateRange().start, target) * this.config.columnWidth\r\n this.setScrollLeft(left)\r\n }\r\n scrollToTask(id: string): void {\r\n const layout = this.getLayout()\r\n if (!layout.ok) return\r\n const item = layout.data.find((row) => row.taskId === id)\r\n if (item) this.setScrollLeft(item.left)\r\n }\r\n scrollToStart(): void {\r\n this.setScrollLeft(0)\r\n }\r\n scrollToEnd(): void {\r\n if (!this.container) return\r\n this.setScrollLeft(this.getTotalWidth())\r\n }\r\n /** 让整个 timeline 总宽度≈视口宽度(调 columnWidth) */\r\n zoomToFit(padding = 1): void {\r\n if (!this.container) return\r\n const spanDays = Math.max(1, diffDays(this.getDateRange().start, this.getDateRange().end) + 1)\r\n const target = (this.container.clientWidth * padding) / spanDays\r\n this.mergeConfig({ columnWidth: this.clampColumnWidth(target) })\r\n }\r\n /** 让指定日期范围占满视口宽度(调 columnWidth) */\r\n zoomToRange(start: string | Date, end: string | Date): void {\r\n if (!this.container) return\r\n const span = Math.max(1, diffDays(start, end) + 1)\r\n const target = this.container.clientWidth / span\r\n this.mergeConfig({ columnWidth: this.clampColumnWidth(target) })\r\n }\r\n private clampColumnWidth(value: number): number {\r\n return Math.max(4, Math.min(400, Math.round(value)))\r\n }\r\n\r\n // ── 事件订阅 ──\r\n on(event: GanttEngineEvent, fn: Listener): () => void {\r\n this.assertNotDestroyed()\r\n let set = this.listeners.get(event)\r\n if (!set) {\r\n set = new Set()\r\n this.listeners.set(event, set)\r\n }\r\n set.add(fn)\r\n return () => {\r\n const current = this.listeners.get(event)\r\n if (current) {\r\n current.delete(fn)\r\n if (!current.size) this.listeners.delete(event)\r\n }\r\n }\r\n }\r\n once(event: GanttEngineEvent, fn: Listener): () => void {\r\n const off = this.on(event, (...args: unknown[]) => {\r\n off()\r\n fn(...args)\r\n })\r\n return off\r\n }\r\n off(event: GanttEngineEvent, fn?: Listener): void {\r\n const set = this.listeners.get(event)\r\n if (!set) return\r\n if (fn) {\r\n set.delete(fn)\r\n if (!set.size) this.listeners.delete(event)\r\n } else {\r\n this.listeners.delete(event)\r\n }\r\n }\r\n emit(event: GanttEngineEvent, ...args: unknown[]): void {\r\n const set = this.listeners.get(event)\r\n if (!set) return\r\n // 复制遍历,避免回调中增删导致迭代错乱\r\n for (const fn of Array.from(set)) {\r\n try {\r\n fn(...args)\r\n } catch (error) {\r\n // 单个监听器抛错不应打断其他监听器\r\n if (typeof console !== \"undefined\") {\r\n console.error(`[GanttEngine] listener for \"${event}\" threw`, error)\r\n }\r\n }\r\n }\r\n }\r\n\r\n // ── 生命周期 ──\r\n destroy(): void {\r\n if (this.destroyed) return\r\n this.destroyed = true\r\n this.emit(\"destroy\")\r\n this.listeners.clear()\r\n this.tasks = []\r\n this.links = []\r\n }\r\n\r\n private assertNotDestroyed(): void {\r\n if (this.destroyed) {\r\n throw new Error(\"GanttEngine: operation on destroyed instance\")\r\n }\r\n }\r\n}\r\n\r\n/** 工厂函数,对应成熟做法的 new GanttEngine(container, config) 入口 */\r\nexport function createGanttEngine(options: GanttEngineOptions = {}): GanttEngine {\r\n return new GanttEngine(options)\r\n}\r\n"],"mappings":"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","names":["end","minDate"]}
|