From f8d6dc6304b4ed3930a6d81ed637659479685c1d Mon Sep 17 00:00:00 2001 From: qiuchengw Date: Thu, 25 Sep 2025 09:44:58 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=B7=A6=E4=BE=A7=E5=88=97?= =?UTF-8?q?=E8=A1=A8=E9=BB=98=E8=AE=A4=E5=B1=95=E5=BC=80=E7=9A=84=E5=AE=BD?= =?UTF-8?q?=E5=BA=A6=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README-EN.md | 34 ++++++ README.md | 34 ++++++ demo/App.vue | 162 +++++++++++++++++++++++++-- src/components/GanttChart.vue | 65 ++++++++--- src/components/TaskRow.vue | 4 +- src/models/configs/TaskListConfig.ts | 8 ++ 6 files changed, 281 insertions(+), 26 deletions(-) diff --git a/README-EN.md b/README-EN.md index 37b05e3..0a5d463 100644 --- a/README-EN.md +++ b/README-EN.md @@ -136,6 +136,7 @@ jordium-gantt-vue3/ | `useDefaultDrawer` | `boolean` | `true` | Use default edit drawer | | `showToolbar` | `boolean` | `true` | Show toolbar | | `toolbarConfig` | `ToolbarConfig` | `{}` | Toolbar configuration | +| `taskListConfig` | `TaskListConfig` | `{}` | Task list configuration (including default width, min/max width limits, etc.) | | `localeMessages` | `Partial` | - | Custom locale messages | | `workingHours` | `WorkingHours` | - | Working hours configuration | | `onTaskDoubleClick` | `(task: Task) => void` | - | Task double-click event callback | @@ -299,6 +300,31 @@ interface ToolbarConfig { } ``` +**TaskListConfig** +```typescript +interface TaskListConfig { + columns?: TaskListColumnConfig[] // Column configuration array + showAllColumns?: boolean // Show all columns, default true + defaultWidth?: number // Default expanded width in pixels, default 320px + minWidth?: number // Minimum width in pixels, default 280px, cannot be less than 280px + maxWidth?: number // Maximum width in pixels, default 1160px +} + +interface TaskListColumnConfig { + type?: TaskListColumnType // Column type + key: string // Key for internationalization, also used as identifier + label?: string // Display label + cssClass?: string // CSS class name + width?: number // Optional column width + visible?: boolean // Whether to display, default true +} + +type TaskListColumnType = + | 'name' | 'predecessor' | 'assignee' + | 'startDate' | 'endDate' | 'estimatedHours' + | 'actualHours' | 'progress' +``` + **WorkingHours Configuration** ```typescript interface WorkingHours { @@ -445,6 +471,13 @@ const milestones = ref([ type: 'milestone' } ]) + +// TaskList width configuration example +const taskListConfig = { + defaultWidth: 400, // Default expanded width 400px (default 320px) + minWidth: 300, // Minimum width 300px (default 280px) + maxWidth: 1200 // Maximum width 1200px (default 1160px) +} diff --git a/README.md b/README.md index 438abe8..63e731d 100644 --- a/README.md +++ b/README.md @@ -137,6 +137,7 @@ jordium-gantt-vue3/ | `useDefaultDrawer` | `boolean` | `true` | 是否使用默认编辑抽屉 | | `showToolbar` | `boolean` | `true` | 是否显示工具栏 | | `toolbarConfig` | `ToolbarConfig` | `{}` | 工具栏配置 | +| `taskListConfig` | `TaskListConfig` | `{}` | 任务列表配置(包括默认宽度、最小最大宽度限制等) | | `localeMessages` | `Partial` | - | 自定义多语言配置 | | `workingHours` | `WorkingHours` | - | 工作时间配置 | | `onTaskDoubleClick` | `(task: Task) => void` | - | 任务双击事件回调 | @@ -313,6 +314,31 @@ interface ToolbarConfig { } ``` +**TaskListConfig 任务列表配置** +```typescript +interface TaskListConfig { + columns?: TaskListColumnConfig[] // 列配置数组 + showAllColumns?: boolean // 是否显示所有列,默认true + defaultWidth?: number // 默认展开宽度,单位像素,默认320px + minWidth?: number // 最小宽度,单位像素,默认280px,不能小于280px + maxWidth?: number // 最大宽度,单位像素,默认1160px +} + +interface TaskListColumnConfig { + type?: TaskListColumnType // 列类型 + key: string // 用于国际化的key,也可以作为识别符 + label?: string // 显示标签 + cssClass?: string // CSS类名 + width?: number // 可选的列宽度 + visible?: boolean // 是否显示,默认true +} + +type TaskListColumnType = + | 'name' | 'predecessor' | 'assignee' + | 'startDate' | 'endDate' | 'estimatedHours' + | 'actualHours' | 'progress' +``` + **WorkingHours 工作时间配置** ```typescript interface WorkingHours { @@ -459,6 +485,13 @@ const milestones = ref([ type: 'milestone' } ]) + +// TaskList宽度配置示例 +const taskListConfig = { + defaultWidth: 400, // 默认展开宽度400px(默认320px) + minWidth: 300, // 最小宽度300px(默认280px) + maxWidth: 1200 // 最大宽度1200px(默认1160px) +} diff --git a/demo/App.vue b/demo/App.vue index 5f257a1..a0743ea 100644 --- a/demo/App.vue +++ b/demo/App.vue @@ -55,8 +55,18 @@ const availableColumns = ref([ { key: 'progress', label: '进度', visible: true }, ]) +// TaskList宽度配置 +const taskListWidth = ref({ + defaultWidth: 400, // 默认宽度400px(比默认320px更宽) + minWidth: 300, // 最小宽度300px(比默认280px略大) + maxWidth: 1200, // 最大宽度1200px(比默认1160px略大) +}) + const taskListConfig = computed(() => ({ columns: availableColumns.value, + defaultWidth: taskListWidth.value.defaultWidth, + minWidth: taskListWidth.value.minWidth, + maxWidth: taskListWidth.value.maxWidth, })) // 切换列显示状态 @@ -660,24 +670,91 @@ function onTimerStopped(task: Task) { - +

- TaskList 列配置 + TaskList 配置

-
- + + +
+

+ + + + + 宽度设置 +

+
+
+ + + px +
+
+ + + px +
+
+ + + px +
+
+
+ + +
+

+ + + + 列显示 +

+
+ +
@@ -781,6 +858,69 @@ function onTimerStopped(task: Task) { color: var(--gantt-primary-color, #409eff); } +.config-section { + margin-bottom: 24px; +} + +.section-title { + display: flex; + align-items: center; + gap: 8px; + font-size: 14px; + font-weight: 600; + color: var(--text-primary); + margin-bottom: 12px; + padding-bottom: 6px; + border-bottom: 1px solid var(--border-color); +} + +.section-icon { + width: 16px; + height: 16px; + color: var(--primary-color); +} + +.width-controls { + display: flex; + flex-direction: column; + gap: 12px; +} + +.width-control { + display: flex; + align-items: center; + gap: 8px; +} + +.width-label { + flex: 0 0 80px; + font-size: 13px; + color: var(--text-secondary); +} + +.width-input { + flex: 1; + padding: 4px 8px; + border: 1px solid var(--border-color); + border-radius: 4px; + background: var(--input-background, #fff); + color: var(--text-primary); + font-size: 13px; + max-width: 100px; +} + +.width-input:focus { + outline: none; + border-color: var(--primary-color); + box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2); +} + +.width-unit { + flex: 0 0 20px; + font-size: 12px; + color: var(--text-tertiary); +} + .column-controls { display: flex; flex-wrap: wrap; diff --git a/src/components/GanttChart.vue b/src/components/GanttChart.vue index 182da43..4bf9132 100644 --- a/src/components/GanttChart.vue +++ b/src/components/GanttChart.vue @@ -117,7 +117,35 @@ interface Props { taskListConfig?: TaskListConfig } -const leftPanelWidth = ref(320) +// 使用taskListConfig中的默认宽度,如果未配置则使用320px +const leftPanelWidth = ref(props.taskListConfig?.defaultWidth || 320) + +// 监听taskListConfig变化,更新相关配置 +watch( + () => props.taskListConfig, + (newConfig) => { + if (newConfig) { + // 更新默认宽度 + if (newConfig.defaultWidth !== undefined) { + leftPanelWidth.value = newConfig.defaultWidth + } + + // 更新最小最大宽度限制 + ganttPanelLeftMinWidth.value = getTaskListMinWidth() + taskListBodyWidth.value = getTaskListMaxWidth() + taskListBodyProposedWidth.value = getTaskListMaxWidth() + taskListBodyWidthLimit.value = getTaskListMaxWidth() + ganttPanelLeftCurrentWidth.value = getTaskListMinWidth() + + // 确保当前宽度在新的限制范围内 + const adjustedWidth = checkWidthLimits(leftPanelWidth.value) + if (adjustedWidth !== leftPanelWidth.value) { + leftPanelWidth.value = adjustedWidth + } + } + }, + { immediate: false, deep: true }, +) // Timeline组件的引用 const timelineRef = ref | null>(null) @@ -139,14 +167,25 @@ watch( // 边框: 7个列间边框 * 1px = 7px // 滚动条预留: 20px // 额外边距: 13px (task-list-header的左边距3px + 其他10px预留) -const TASK_LIST_MAX_WIDTH = 1120 + 7 + 20 + 13 // = 1160px -const TASK_LIST_MIN_WIDTH = 320 // TaskList最小宽度 +const DEFAULT_TASK_LIST_MAX_WIDTH = 1120 + 7 + 20 + 13 // = 1160px +const DEFAULT_TASK_LIST_MIN_WIDTH = 280 // 最小宽度不能小于280px -const taskListBodyWidth = ref(TASK_LIST_MAX_WIDTH) // TaskList默认宽度 -const ganttPanelLeftMinWidth = ref(TASK_LIST_MIN_WIDTH) // 左侧面板最小宽度 -const ganttPanelLeftCurrentWidth = ref(TASK_LIST_MIN_WIDTH) // 当前左侧面板宽度 -const taskListBodyProposedWidth = ref(TASK_LIST_MAX_WIDTH) -const taskListBodyWidthLimit = ref(TASK_LIST_MAX_WIDTH) +// TaskList最小宽度,支持通过taskListConfig配置 +const getTaskListMinWidth = () => { + const configMinWidth = props.taskListConfig?.minWidth || DEFAULT_TASK_LIST_MIN_WIDTH + return Math.max(configMinWidth, DEFAULT_TASK_LIST_MIN_WIDTH) // 确保不小于280px +} + +// TaskList最大宽度,支持通过taskListConfig配置 +const getTaskListMaxWidth = () => { + return props.taskListConfig?.maxWidth || DEFAULT_TASK_LIST_MAX_WIDTH +} + +const taskListBodyWidth = ref(getTaskListMaxWidth()) // TaskList默认宽度 +const ganttPanelLeftMinWidth = ref(getTaskListMinWidth()) // 左侧面板最小宽度 +const ganttPanelLeftCurrentWidth = ref(getTaskListMinWidth()) // 当前左侧面板宽度 +const taskListBodyProposedWidth = ref(getTaskListMaxWidth()) +const taskListBodyWidthLimit = ref(getTaskListMaxWidth()) // 简化的限制检查函数:直接基于面板实际宽度判断 const checkWidthLimits = (proposedLeftWidth: number): number => { @@ -225,11 +264,11 @@ function onMouseDown(e: MouseEvent) { document.body.style.webkitUserSelect = '' document.body.style.cursor = '' - taskListBodyWidth.value = TASK_LIST_MAX_WIDTH // TaskList默认宽度 - ganttPanelLeftMinWidth.value = TASK_LIST_MIN_WIDTH // 左侧面板最小宽度 - taskListBodyProposedWidth.value = TASK_LIST_MAX_WIDTH - taskListBodyWidthLimit.value = TASK_LIST_MAX_WIDTH - ganttPanelLeftCurrentWidth.value = TASK_LIST_MIN_WIDTH // 当前左侧面板宽度 + taskListBodyWidth.value = getTaskListMaxWidth() // TaskList默认宽度 + ganttPanelLeftMinWidth.value = getTaskListMinWidth() // 左侧面板最小宽度 + taskListBodyProposedWidth.value = getTaskListMaxWidth() + taskListBodyWidthLimit.value = getTaskListMaxWidth() + ganttPanelLeftCurrentWidth.value = getTaskListMinWidth() // 当前左侧面板宽度 window.removeEventListener('mousemove', onMouseMove) window.removeEventListener('mouseup', onMouseUp) diff --git a/src/components/TaskRow.vue b/src/components/TaskRow.vue index 61c1d9f..dc39105 100644 --- a/src/components/TaskRow.vue +++ b/src/components/TaskRow.vue @@ -465,8 +465,8 @@ onUnmounted(() => { @add-successor="emit('add-successor', $event)" @delete="handleTaskDelete" > - diff --git a/src/models/configs/TaskListConfig.ts b/src/models/configs/TaskListConfig.ts index 24bb35a..bbe3576 100644 --- a/src/models/configs/TaskListConfig.ts +++ b/src/models/configs/TaskListConfig.ts @@ -22,8 +22,16 @@ export interface TaskListColumnConfig { export interface TaskListConfig { columns?: TaskListColumnConfig[] showAllColumns?: boolean // 是否显示所有列,默认true + defaultWidth?: number // 默认展开宽度,单位像素,默认320px + minWidth?: number // 最小宽度,单位像素,默认280px,不能小于280px + maxWidth?: number // 最大宽度,单位像素,默认1160px } +// 默认宽度配置 +export const DEFAULT_TASK_LIST_WIDTH = 320 // 默认展开宽度 +export const DEFAULT_TASK_LIST_MIN_WIDTH = 280 // 最小宽度 +export const DEFAULT_TASK_LIST_MAX_WIDTH = 1160 // 最大宽度 + // 默认列配置 export const DEFAULT_TASK_LIST_COLUMNS: TaskListColumnConfig[] = [ {