diff --git a/components/Gantt/core/DataManager.js b/components/Gantt/core/DataManager.js
new file mode 100644
index 0000000..f38ecde
--- /dev/null
+++ b/components/Gantt/core/DataManager.js
@@ -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));
+ }
+}
\ No newline at end of file
diff --git a/components/Gantt/core/Interaction.js b/components/Gantt/core/Interaction.js
new file mode 100644
index 0000000..9013939
--- /dev/null
+++ b/components/Gantt/core/Interaction.js
@@ -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();
+ }
+}
\ No newline at end of file
diff --git a/components/Gantt/core/Layout.js b/components/Gantt/core/Layout.js
new file mode 100644
index 0000000..14e5cc9
--- /dev/null
+++ b/components/Gantt/core/Layout.js
@@ -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';
+ }
+}
\ No newline at end of file
diff --git a/components/Gantt/core/TimeCalculator.js b/components/Gantt/core/TimeCalculator.js
new file mode 100644
index 0000000..0148f09
--- /dev/null
+++ b/components/Gantt/core/TimeCalculator.js
@@ -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);
+ }
+}
\ No newline at end of file
diff --git a/components/Gantt/core/types.js b/components/Gantt/core/types.js
new file mode 100644
index 0000000..d4c6107
--- /dev/null
+++ b/components/Gantt/core/types.js
@@ -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: {}
+};
\ No newline at end of file
diff --git a/components/Gantt/index.vue b/components/Gantt/index.vue
new file mode 100644
index 0000000..e69de29
diff --git a/components/Gantt/uniapp/DimensionPanel.vue b/components/Gantt/uniapp/DimensionPanel.vue
new file mode 100644
index 0000000..6dd2a9e
--- /dev/null
+++ b/components/Gantt/uniapp/DimensionPanel.vue
@@ -0,0 +1,84 @@
+
+
+
+
+ 切换维度
+
+
+
+
+
\ No newline at end of file
diff --git a/components/Gantt/uniapp/Gantt.vue b/components/Gantt/uniapp/Gantt.vue
new file mode 100644
index 0000000..3d6ffb5
--- /dev/null
+++ b/components/Gantt/uniapp/Gantt.vue
@@ -0,0 +1,146 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/components/Gantt/uniapp/GanttCanvas.vue b/components/Gantt/uniapp/GanttCanvas.vue
new file mode 100644
index 0000000..aa8aee9
--- /dev/null
+++ b/components/Gantt/uniapp/GanttCanvas.vue
@@ -0,0 +1,79 @@
+
+
+
+
+
+ {{ task.name }}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/components/Gantt/uniapp/Legend.vue b/components/Gantt/uniapp/Legend.vue
new file mode 100644
index 0000000..11f6c16
--- /dev/null
+++ b/components/Gantt/uniapp/Legend.vue
@@ -0,0 +1,49 @@
+
+
+ {{ dimensionName ? `当前维度:${dimensionName}` : '分组' }}
+
+
+ {{ group.label || group.value }}
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/components/Gantt/uniapp/TimelineHeader.vue b/components/Gantt/uniapp/TimelineHeader.vue
new file mode 100644
index 0000000..27d41a0
--- /dev/null
+++ b/components/Gantt/uniapp/TimelineHeader.vue
@@ -0,0 +1,36 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/pages/workbench/reportSchedule/reportSchedule.vue b/pages/workbench/reportSchedule/reportSchedule.vue
index bfc0c11..5bdb35d 100644
--- a/pages/workbench/reportSchedule/reportSchedule.vue
+++ b/pages/workbench/reportSchedule/reportSchedule.vue
@@ -385,14 +385,12 @@ export default {
this.$refs.popupRef.open('bottom')
},
handleUpdate(row) {
- console.log('handleUpdate called', row);
getReportSchedule(row.scheduleId).then(res => {
this.form = res.data || {}
this.$refs.popupRef.open('bottom')
})
},
handleDelete(item) {
- console.log('handleDelete called', item);
uni.showModal({
title: '确认删除',
content: `确定要删除排产“${item.scheduleName}”吗?`,
@@ -449,12 +447,6 @@ export default {
return options;
},
swipeActionClick(e, item) {
- console.log('swipeActionClick e:', e);
- console.log('swipeActionClick item:', item);
- if (e && e.content) {
- console.log('swipeActionClick e.content:', e.content);
- console.log('swipeActionClick e.content.text:', e.content.text);
- }
const text = e.content.text;
if (text === '编辑') {
this.handleUpdate(item);
@@ -465,7 +457,6 @@ export default {
}
},
handleStart(row) {
- console.log('handleStart called', row);
const now = new Date();
const pad = n => n < 10 ? '0' + n : n;
const formatDate = d => `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
@@ -526,8 +517,6 @@ export default {
// 只保留唯一 projectId
const uniqueProjectIds = Array.from(new Set(list.map(item => item.projectId)))
const categories = uniqueProjectIds.map(id => this.getProjectName(id))
- console.log(categories, '获取分类', list)
-
const data = list.map(item => ({
...item,