支持配置左侧table 列显示

This commit is contained in:
qiuchengw
2025-09-24 20:15:58 +08:00
parent 253ed21fd2
commit ade1d81c95
8 changed files with 647 additions and 39 deletions
+153
View File
@@ -13,6 +13,7 @@ import { useMessage } from '../src/composables/useMessage'
import { useI18n } from '../src/composables/useI18n'
import { getPredecessorIds, predecessorIdsToString } from '../src/utils/predecessorUtils'
import type { Task } from '../src/models/Task'
import type { TaskListConfig, TaskListColumnConfig } from '../src/models/configs/TaskListConfig'
const { showMessage } = useMessage()
const { t, formatTranslation } = useI18n()
@@ -41,6 +42,36 @@ const toolbarConfig = {
timeScaleDimensions: ['hour', 'day', 'week', 'month', 'quarter', 'year'], // 设置时间刻度按钮的展示维度,包含所有时间维度
}
// TaskList列配置
const availableColumns = ref<TaskListColumnConfig[]>([
{ key: 'predecessor', label: '前置任务', visible: true },
{ key: 'assignee', label: '负责人', visible: true },
{ key: 'startDate', label: '开始日期', visible: true },
{ key: 'endDate', label: '结束日期', visible: true },
{ key: 'estimatedHours', label: '预估工时', visible: true },
{ key: 'actualHours', label: '实际工时', visible: true },
{ key: 'progress', label: '进度', visible: true },
])
const taskListConfig = ref<TaskListConfig>({
columns: availableColumns.value,
})
// 切换列显示状态
const toggleColumn = (columnKey: string, event: Event) => {
const target = event.target as HTMLInputElement
const visible = target?.checked ?? false
const column = availableColumns.value.find(col => col.key === columnKey)
if (column) {
column.visible = visible
// 更新taskListConfig以触发响应式更新
taskListConfig.value = {
columns: [...availableColumns.value],
}
}
}
// 工作时间配置示例
const workingHoursConfig = {
morning: { start: 8, end: 11 }, // 上午8:00-11:59为工作时间
@@ -632,11 +663,34 @@ function onTimerStopped(task: Task) {
</div>
</h1>
<VersionHistoryDrawer :visible="showVersionDrawer" @close="showVersionDrawer = false" />
<!-- TaskList列配置面板 -->
<div class="config-panel">
<h3 class="config-title">
<svg class="config-icon" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 15l3-3H9l3 3z" fill="currentColor"/>
<path d="M3 6h18v2H3V6zm0 5h18v2H3v-2zm0 5h18v2H3v-2z" fill="currentColor"/>
</svg>
TaskList 列配置
</h3>
<div class="column-controls">
<label v-for="column in availableColumns" :key="column.key" class="column-control">
<input
type="checkbox"
:checked="column.visible"
@change="toggleColumn(column.key, $event)"
/>
<span class="column-label">{{ column.label }}</span>
</label>
</div>
</div>
<div class="gantt-wrapper">
<GanttChart
:tasks="tasks"
:milestones="milestones"
:toolbar-config="toolbarConfig"
:task-list-config="taskListConfig"
:working-hours="workingHoursConfig"
:on-add-task="handleAddTask"
:on-add-milestone="handleAddMilestone"
@@ -700,6 +754,79 @@ function onTimerStopped(task: Task) {
flex-direction: column;
}
/* TaskList列配置面板样式 */
.config-panel {
background: var(--gantt-bg-primary, #ffffff);
border: 1px solid var(--gantt-border-color, #e4e7ed);
border-radius: 8px;
padding: 16px;
margin-bottom: 20px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
}
.config-panel:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.config-title {
margin: 0 0 12px 0;
font-size: 16px;
font-weight: 600;
color: var(--gantt-text-primary, #333);
display: flex;
align-items: center;
gap: 8px;
}
.config-icon {
width: 20px;
height: 20px;
color: var(--gantt-primary-color, #409eff);
}
.column-controls {
display: flex;
flex-wrap: wrap;
gap: 16px;
}
.column-control {
display: flex;
align-items: center;
gap: 8px;
cursor: pointer;
padding: 6px 12px;
border-radius: 6px;
transition: all 0.2s ease;
background: var(--gantt-bg-secondary, #f8f9fa);
border: 1px solid transparent;
}
.column-control:hover {
background: var(--gantt-hover-bg, #e8f4fd);
border-color: var(--gantt-primary-color, #409eff);
}
.column-control input[type="checkbox"] {
width: 16px;
height: 16px;
cursor: pointer;
accent-color: var(--gantt-primary-color, #409eff);
}
.column-label {
font-size: 14px;
font-weight: 500;
color: var(--gantt-text-primary, #333);
user-select: none;
transition: color 0.2s ease;
}
.column-control:hover .column-label {
color: var(--gantt-primary-color, #409eff);
}
.page-title {
margin: 20px 0;
font-size: 1.8rem;
@@ -821,6 +948,32 @@ function onTimerStopped(task: Task) {
color: #e5e5e5 !important;
}
/* 暗色主题下的配置面板样式 */
:global(html[data-theme='dark']) .config-panel {
background: var(--gantt-bg-primary, #2d3748);
border-color: var(--gantt-border-color, #4a5568);
}
:global(html[data-theme='dark']) .config-title {
color: var(--gantt-text-primary, #e2e8f0);
}
:global(html[data-theme='dark']) .column-control {
background: var(--gantt-bg-secondary, #1a202c);
}
:global(html[data-theme='dark']) .column-control:hover {
background: var(--gantt-hover-bg, #2d3748);
}
:global(html[data-theme='dark']) .column-label {
color: var(--gantt-text-primary, #e2e8f0);
}
:global(html[data-theme='dark']) .column-control:hover .column-label {
color: var(--gantt-primary-color, #66b3ff);
}
/* 暗黑模式下的版本标签 */
:global(html[data-theme='dark']) .version-badge {
background: linear-gradient(135deg, #1a73e8 0%, #00bcd4 50%, #3f51b5 100%);
+239
View File
@@ -0,0 +1,239 @@
<script setup lang="ts">
import { ref, reactive } from 'vue'
import { GanttChart } from '../src/index'
import type { Task } from '../src/models/classes/Task'
import type { TaskListConfig, TaskListColumnConfig } from '../src/models/configs/TaskListConfig'
// 示例任务数据
const tasks = ref<Task[]>([
{
id: 1,
name: '项目启动',
startDate: '2024-01-15',
endDate: '2024-01-20',
progress: 100,
assignee: '张三',
estimatedHours: 40,
actualHours: 35,
predecessor: null,
type: 'task',
},
{
id: 2,
name: '需求分析',
startDate: '2024-01-21',
endDate: '2024-01-30',
progress: 80,
assignee: '李四',
estimatedHours: 80,
actualHours: 75,
predecessor: [1],
type: 'task',
},
{
id: 3,
name: '系统设计',
startDate: '2024-02-01',
endDate: '2024-02-15',
progress: 60,
assignee: '王五',
estimatedHours: 120,
actualHours: 90,
predecessor: [3],
type: 'task',
},
{
id: 4,
name: '开发实现',
startDate: '2024-02-16',
endDate: '2024-03-30',
progress: 30,
assignee: '赵六',
estimatedHours: 200,
actualHours: 80,
predecessor: [3],
type: 'task',
},
] as Task[])
// 可用的列配置
const availableColumns = reactive<TaskListColumnConfig[]>([
{ key: 'predecessor', label: '前置任务', visible: true },
{ key: 'assignee', label: '负责人', visible: true },
{ key: 'startDate', label: '开始日期', visible: true },
{ key: 'endDate', label: '结束日期', visible: true },
{ key: 'estimatedHours', label: '预估工时', visible: true },
{ key: 'actualHours', label: '实际工时', visible: true },
{ key: 'progress', label: '进度', visible: true },
])
// 任务列表配置
const taskListConfig = reactive<TaskListConfig>({
columns: availableColumns,
})
// 切换列显示状态
const toggleColumn = (columnKey: string, visible: boolean) => {
const column = availableColumns.find(col => col.key === columnKey)
if (column) {
column.visible = visible
}
}
</script>
<template>
<div class="demo-container">
<h1>TaskList 列配置演示</h1>
<div class="config-section">
<h3>列配置:</h3>
<div class="column-controls">
<label v-for="column in availableColumns" :key="column.key" class="column-control">
<input
type="checkbox"
:checked="column.visible"
@change="toggleColumn(column.key, $event.target.checked)"
/>
{{ column.label }}
</label>
</div>
</div>
<div class="gantt-container">
<GanttChart
:tasks="tasks"
:task-list-config="taskListConfig"
:show-toolbar="false"
/>
</div>
</div>
</template>
import { ref, reactive } from 'vue'
import { GanttChart } from '../src/index'
import type { Task } from '../src/models/classes/Task'
import type { TaskListConfig, TaskListColumnConfig } from '../src/models/configs/TaskListConfig'
// 示例任务数据
const tasks = ref<Task[]>([
{
id: 1,
name: '项目启动',
startDate: '2024-01-15',
endDate: '2024-01-20',
progress: 100,
assignee: '张三',
estimatedHours: 40,
actualHours: 35,
predecessor: null,
type: 'task'
},
{
id: 2,
name: '需求分析',
startDate: '2024-01-21',
endDate: '2024-01-30',
progress: 80,
assignee: '李四',
estimatedHours: 80,
actualHours: 75,
predecessor: [1],
type: 'task'
},
{
id: 3,
name: '系统设计',
startDate: '2024-02-01',
endDate: '2024-02-15',
progress: 60,
assignee: '王五',
estimatedHours: 120,
actualHours: 90,
predecessor: [2],
type: 'task'
},
{
id: 4,
name: '开发实现',
startDate: '2024-02-16',
endDate: '2024-03-30',
progress: 30,
assignee: '赵六',
estimatedHours: 200,
actualHours: 80,
predecessor: [3],
type: 'task'
}
] as Task[])
// 可用的列配置
const availableColumns = reactive<TaskListColumnConfig[]>([
{ key: 'predecessor', label: '前置任务', visible: true },
{ key: 'assignee', label: '负责人', visible: true },
{ key: 'startDate', label: '开始日期', visible: true },
{ key: 'endDate', label: '结束日期', visible: true },
{ key: 'estimatedHours', label: '预估工时', visible: true },
{ key: 'actualHours', label: '实际工时', visible: true },
{ key: 'progress', label: '进度', visible: true }
])
// 任务列表配置
const taskListConfig = reactive<TaskListConfig>({
columns: availableColumns
})
// 切换列显示状态
const toggleColumn = (columnKey: string, visible: boolean) => {
const column = availableColumns.find(col => col.key === columnKey)
if (column) {
column.visible = visible
}
}
</script>
<style scoped>
.demo-container {
padding: 20px;
max-width: 1200px;
margin: 0 auto;
}
.config-section {
background: #f5f5f5;
padding: 15px;
border-radius: 8px;
margin-bottom: 20px;
}
.column-controls {
display: flex;
flex-wrap: wrap;
gap: 15px;
}
.column-control {
display: flex;
align-items: center;
gap: 8px;
cursor: pointer;
}
.column-control input[type="checkbox"] {
cursor: pointer;
}
.gantt-container {
border: 1px solid #ddd;
border-radius: 8px;
overflow: hidden;
height: 400px;
}
h1 {
color: #333;
margin-bottom: 20px;
}
h3 {
color: #666;
margin: 0 0 10px 0;
}
</style>
+78
View File
@@ -0,0 +1,78 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TaskList 列配置演示</title>
</head>
<body>
<div id="app"></div>
<script type="module">
// 模拟测试数据
const testData = {
availableColumns: [
{ key: 'predecessor', label: '前置任务', visible: true },
{ key: 'assignee', label: '负责人', visible: true },
{ key: 'startDate', label: '开始日期', visible: true },
{ key: 'endDate', label: '结束日期', visible: true },
{ key: 'estimatedHours', label: '预估工时', visible: false },
{ key: 'actualHours', label: '实际工时', visible: false },
{ key: 'progress', label: '进度', visible: true }
]
}
// 创建配置界面
const app = document.getElementById('app')
app.innerHTML = `
<div style="padding: 20px; font-family: Arial, sans-serif;">
<h2>🎉 TaskList 列配置功能实现完成!</h2>
<div style="background: #f5f5f5; padding: 15px; border-radius: 8px; margin: 20px 0;">
<h3>✅ 已完成的功能</h3>
<ul>
<li><strong>Timeline Scale 定制化</strong>:支持只显示"周/月/年",支持默认时间刻度</li>
<li><strong>TaskList 列配置</strong>:支持外部传入配置控制列的显示/隐藏</li>
<li><strong>类型安全</strong>:完整的 TypeScript 类型支持</li>
<li><strong>响应式设计</strong>:配置变更时 UI 自动更新</li>
<li><strong>向后兼容</strong>:未传配置时使用默认显示</li>
</ul>
</div>
<div style="background: #e8f4fd; padding: 15px; border-radius: 8px; margin: 20px 0;">
<h3>🎮 演示说明</h3>
<p>在 <code>demo/App.vue</code> 中已经添加了完整的列配置控制界面:</p>
<ol>
<li>运行 <code>npm run dev</code> 启动演示应用</li>
<li>在页面顶部可以看到 "TaskList 列配置" 面板</li>
<li>通过复选框控制各列的显示/隐藏</li>
<li>配置实时生效,甘特图 TaskList 部分会相应更新</li>
</ol>
</div>
<div style="background: #f0f9ff; padding: 15px; border-radius: 8px; margin: 20px 0;">
<h3>📝 使用方式</h3>
<pre style="background: #1e1e1e; color: #d4d4d4; padding: 15px; border-radius: 6px; overflow-x: auto;"><code>// 在组件中配置 TaskList 列显示
const taskListConfig = {
columns: [
{ key: 'predecessor', label: '前置任务', visible: false },
{ key: 'assignee', label: '负责人', visible: true },
{ key: 'startDate', label: '开始日期', visible: true },
{ key: 'endDate', label: '结束日期', visible: true },
{ key: 'estimatedHours', label: '预估工时', visible: false },
{ key: 'actualHours', label: '实际工时', visible: false },
{ key: 'progress', label: '进度', visible: true }
]
}
// 传递给 GanttChart 组件
&lt;GanttChart :task-list-config="taskListConfig" /&gt;</code></pre>
</div>
<div style="text-align: center; margin: 30px 0;">
<h3 style="color: #67c23a;">🎊 功能完成,可以开始使用了!</h3>
<p style="color: #909399;">现在可以根据不同业务场景灵活控制 TaskList 中显示的列</p>
</div>
</div>
`
</script>
</body>
</html>