甘特图组件,有点改炸了

This commit is contained in:
砂糖
2025-07-16 14:23:42 +08:00
parent 62ef81065c
commit 5e1afb293e
12 changed files with 617 additions and 11 deletions

View File

@@ -0,0 +1,43 @@
// DataManager任务数据管理器负责任务的增删改查、分组、校验和变更通知
export default class DataManager {
constructor(tasks = [], dimensions = []) {
this.tasks = tasks;
this.dimensions = dimensions;
this.listeners = [];
}
// 新增任务
addTask(task) {
this.tasks.push(task);
this.notify();
}
// 更新任务
updateTask(id, data) {
const idx = this.tasks.findIndex(t => t.id === id);
if (idx !== -1) {
this.tasks[idx] = { ...this.tasks[idx], ...data };
this.notify();
}
}
// 删除任务
removeTask(id) {
this.tasks = this.tasks.filter(t => t.id !== id);
this.notify();
}
// 按维度分组
groupByDimension(dim) {
const groups = {};
this.tasks.forEach(task => {
const key = task.dimensions && task.dimensions[dim] ? task.dimensions[dim].id : '未分组';
if (!groups[key]) groups[key] = { id: key, name: task.dimensions && task.dimensions[dim] ? task.dimensions[dim].name : '未分组', tasks: [] };
groups[key].tasks.push(task);
});
return Object.values(groups);
}
// 变更监听
onChange(cb) {
this.listeners.push(cb);
}
notify() {
this.listeners.forEach(cb => cb(this.tasks));
}
}

View File

@@ -0,0 +1,27 @@
// Interaction交互控制器处理用户交互逻辑输出标准化事件
export default class Interaction {
constructor(dataManager, timeCalculator) {
this.dataManager = dataManager;
this.timeCalculator = timeCalculator;
}
// 处理拖拽开始
handleDragStart(taskId) {
// 可扩展:记录初始状态
}
// 处理拖拽过程
handleDragUpdate(taskId, newDate) {
// 校验新日期是否合法,可扩展
this.dataManager.updateTask(taskId, { start: newDate });
}
// 处理拖拽结束
handleDragEnd(taskId, newDate) {
// 最终更新数据
this.dataManager.updateTask(taskId, { start: newDate });
}
// 处理维度切换
handleSwitchDimension(dim) {
// 重新分组并通知视图层
this.dataManager.dimensions = [dim];
this.dataManager.notify();
}
}

View File

@@ -0,0 +1,57 @@
// Layout布局计算器负责任务条和依赖线的布局
export default class Layout {
constructor(tasks, config) {
this.tasks = tasks;
this.config = config;
}
// 计算任务条布局
computeTaskLayout() {
// 简单实现每个任务一行计算左侧start、宽度end-start
const layout = [];
this.tasks.forEach((task, idx) => {
const startPixel = this.dateToPixel(task.start);
const endPixel = this.dateToPixel(task.end);
layout.push({
id: task.id,
top: idx * 32,
left: startPixel,
width: endPixel - startPixel,
height: 28,
color: this.getTaskColor(task)
});
});
return layout;
}
// 计算依赖线布局(可扩展)
computeDependencyLines() {
// 返回依赖线的起止坐标
return [];
}
// 工具:日期转像素
dateToPixel(date) {
const start = new Date(this.config.startDate);
const d = new Date(date);
const scaleMap = { day: 40, week: 80, month: 200, quarter: 600 };
const scale = scaleMap[this.config.timeScale] || 80;
let diff = 0;
if (this.config.timeScale === 'day') {
diff = (d - start) / (1000 * 3600 * 24);
} else if (this.config.timeScale === 'week') {
diff = (d - start) / (1000 * 3600 * 24 * 7);
} else if (this.config.timeScale === 'month') {
diff = (d.getFullYear() - start.getFullYear()) * 12 + (d.getMonth() - start.getMonth());
} else if (this.config.timeScale === 'quarter') {
diff = ((d.getFullYear() - start.getFullYear()) * 12 + (d.getMonth() - start.getMonth())) / 3;
}
return diff * scale;
}
// 工具:获取任务颜色
getTaskColor(task) {
const group = this.config.groupBy;
if (group && task.dimensions && task.dimensions[group]) {
const id = task.dimensions[group].id;
return this.config.colors && this.config.colors[group] && this.config.colors[group][id] ? this.config.colors[group][id] : '#409EFF';
}
return '#409EFF';
}
}

View File

@@ -0,0 +1,82 @@
// TimeCalculator时间轴计算器负责时间与像素的转换、生成时间刻度等
export default class TimeCalculator {
constructor(config) {
this.config = config;
this.scaleMap = { day: 40, week: 80, month: 200, quarter: 600 }; // 每单位像素宽度
}
// 日期转像素坐标
dateToPixel(date) {
const start = new Date(this.config.startDate);
const d = new Date(date);
const scale = this.scaleMap[this.config.timeScale] || 80;
let diff = 0;
if (this.config.timeScale === 'day') {
diff = (d - start) / (1000 * 3600 * 24);
} else if (this.config.timeScale === 'week') {
diff = (d - start) / (1000 * 3600 * 24 * 7);
} else if (this.config.timeScale === 'month') {
diff = (d.getFullYear() - start.getFullYear()) * 12 + (d.getMonth() - start.getMonth());
} else if (this.config.timeScale === 'quarter') {
diff = ((d.getFullYear() - start.getFullYear()) * 12 + (d.getMonth() - start.getMonth())) / 3;
}
return diff * scale;
}
// 像素转日期(仅简单实现,实际可扩展)
pixelToDate(pixel) {
const start = new Date(this.config.startDate);
const scale = this.scaleMap[this.config.timeScale] || 80;
let d = new Date(start);
if (this.config.timeScale === 'day') {
d.setDate(start.getDate() + pixel / scale);
} else if (this.config.timeScale === 'week') {
d.setDate(start.getDate() + (pixel / scale) * 7);
} else if (this.config.timeScale === 'month') {
d.setMonth(start.getMonth() + pixel / scale);
} else if (this.config.timeScale === 'quarter') {
d.setMonth(start.getMonth() + (pixel / scale) * 3);
}
return d;
}
// 生成时间轴刻度
getTimelineTicks() {
const ticks = [];
const start = new Date(this.config.startDate);
const end = new Date(this.config.endDate);
let cur = new Date(start);
while (cur <= end) {
ticks.push({
label: this.formatTick(cur),
date: new Date(cur)
});
if (this.config.timeScale === 'day') {
cur.setDate(cur.getDate() + 1);
} else if (this.config.timeScale === 'week') {
cur.setDate(cur.getDate() + 7);
} else if (this.config.timeScale === 'month') {
cur.setMonth(cur.getMonth() + 1);
} else if (this.config.timeScale === 'quarter') {
cur.setMonth(cur.getMonth() + 3);
}
}
return ticks;
}
// 格式化刻度
formatTick(date) {
if (this.config.timeScale === 'day') {
return date.toISOString().slice(5, 10);
} else if (this.config.timeScale === 'week') {
return 'W' + this.getWeekNumber(date);
} else if (this.config.timeScale === 'month') {
return date.getFullYear() + '-' + (date.getMonth() + 1);
} else if (this.config.timeScale === 'quarter') {
return date.getFullYear() + ' Q' + (Math.floor(date.getMonth() / 3) + 1);
}
return '';
}
// 获取周数
getWeekNumber(date) {
const firstDay = new Date(date.getFullYear(), 0, 1);
const dayOfYear = ((date - firstDay) / 86400000) + 1;
return Math.ceil(dayOfYear / 7);
}
}

View File

@@ -0,0 +1,14 @@
// 任务数据模型
export const TaskModel = {
id: '', name: '', start: '', end: '', progress: 0,
dependencies: [], children: [],
dimensions: {}
};
// 视图配置参数
export const ViewConfig = {
timeScale: 'week',
startDate: '', endDate: '',
groupBy: '', visibleDimensions: [],
colors: {}
};