v1.9.0-rc.8 - SIT finished

This commit is contained in:
LINING-PC\lining
2026-02-07 18:44:23 +08:00
parent cf4d410e95
commit 430015e3fe
28 changed files with 1676 additions and 994 deletions
+533 -285
View File
@@ -1,287 +1,3 @@
<script setup lang="ts">
import { ref, computed, watch } from 'vue'
import { GanttChart, Task } from 'jordium-gantt-vue3'
import 'jordium-gantt-vue3/dist/assets/jordium-gantt-vue3.css'
// GanttChart ref
const ganttRef = ref(null)
// 控制模式:'expose' 使用expose方法,'props' 使用Props
const controlMode = ref<'expose' | 'props'>('expose')
// 状态变量
const fullscreenStatus = ref(false)
const expandStatus = ref(false)
const currentLocaleStatus = ref<'zh-CN' | 'en-US'>('zh-CN')
const currentScaleStatus = ref<'hour' | 'day' | 'week' | 'month' | 'quarter' | 'year'>('week')
const currentThemeStatus = ref<'light' | 'dark'>('light')
// Props控制变量
const propsLocale = ref<'zh-CN' | 'en-US'>('zh-CN')
const propsTheme = ref<'light' | 'dark'>('light')
const propsTimeScale = ref<'hour' | 'day' | 'week' | 'month' | 'quarter' | 'year'>('week')
const propsFullscreen = ref(false)
const propsExpandAll = ref(false)
// 监听 Props 变化,同步 status
watch(propsLocale, (newLocale) => {
currentLocaleStatus.value = newLocale
})
watch(propsTheme, (newTheme) => {
currentThemeStatus.value = newTheme
})
watch(propsTimeScale, (newScale) => {
currentScaleStatus.value = newScale
})
watch(propsFullscreen, (newFullscreen) => {
fullscreenStatus.value = newFullscreen
})
watch(propsExpandAll, (newExpandAll) => {
expandStatus.value = newExpandAll
})
// 更新状态函数
const updateStatus = () => {
if (ganttRef.value) {
fullscreenStatus.value = ganttRef.value.isFullscreen()
expandStatus.value = ganttRef.value.isExpandAll()
currentLocaleStatus.value = ganttRef.value.currentLocale()
currentScaleStatus.value = ganttRef.value.currentScale()
currentThemeStatus.value = ganttRef.value.currentTheme()
}
}
// Expose 方法处理器
const handleToggleFullscreen = () => {
ganttRef.value?.toggleFullscreen()
updateStatus()
propsFullscreen.value = ganttRef.value?.isFullscreen() ?? false
}
const handleToggleExpandAll = () => {
ganttRef.value?.toggleExpandAll()
updateStatus()
propsExpandAll.value = ganttRef.value?.isExpandAll() ?? false
}
const handleSetLocale = (locale: 'zh-CN' | 'en-US') => {
ganttRef.value?.setLocale(locale)
currentLocaleStatus.value = locale
propsLocale.value = locale
}
const handleSetTimeScale = (scale: 'hour' | 'day' | 'week' | 'month' | 'quarter' | 'year') => {
ganttRef.value?.setTimeScale(scale)
updateStatus()
propsTimeScale.value = scale
}
const handleSetTheme = (mode: 'light' | 'dark') => {
ganttRef.value?.setTheme(mode)
updateStatus()
propsTheme.value = mode
}
// 定义类型接口
interface TaskListColumnConfig {
key: string;
label: string;
visible: boolean;
width?: number;
}
interface ToolbarConfig {
showAddTask?: boolean;
showAddMilestone?: boolean;
showTodayLocate?: boolean;
showExportCsv?: boolean;
showExportPdf?: boolean;
showLanguage?: boolean;
showTheme?: boolean;
showFullscreen?: boolean;
showTimeScale?: boolean;
timeScaleDimensions?: string[];
defaultTimeScale?: string;
showExpandCollapse?: boolean;
}
const tasks = ref([
{
id: 1,
name: '项目启动',
startDate: '2025-10-30',
endDate: '2025-11-5',
progress: 100,
department: '管理部',
departmentCode: 'D001',
type: 'task',
assignee: '',
assigneeName: '',
},
])
const milestones = ref([
{
id: 101,
name: '项目立项',
startDate: '2025-10-29',
type: 'milestone',
icon: 'diamond',
},
])
const customMessages = {
'zh-CN': {
department: '部门',
departmentCode: '部门编号',
},
'en-US': {
department: 'Department',
departmentCode: 'Department Code',
},
}
// const tasks = ref([])
// const milestones = ref([])
const showAddTaskDrawer = ref(false)
const showAddMilestoneDialog = ref(false)
// 定义可动态配置的列
const availableColumns = ref<TaskListColumnConfig[]>([
{ key: 'startDate', label: '开始日期', visible: true },
{ key: 'endDate', label: '结束日期', visible: true },
{ key: 'progress', label: '进度', visible: true },
{ key: 'department', label: '部门', visible: true, width: 120 },
{ key: 'departmentCode', label: '部门编号', visible: true },
{ key: 'assigneeName', label: '负责人', visible: true },
])
// 自定义负责人列表
const assigneeOptions = ref([
{ value: 'zhangsan', label: '张三' },
{ value: 'lisi', label: '李四' },
{ value: 'wangwu', label: '王五' },
])
// TaskList宽度配置示例
const taskListConfig = {
defaultWidth: '50%', // 默认展开宽度50%
minWidth: '300px', // 最小宽度300px(默认280px
maxWidth: '1200px', // 最大宽度1200px(默认1160px
columns: availableColumns.value,
}
// toolbar配置示例
const toolbarConfig: ToolbarConfig = {
showAddTask: true, // 显示添加任务按钮
showAddMilestone: true, // 显示添加里程碑按钮
showTodayLocate: true, // 显示定位到今天按钮
showExportCsv: true, // 显示导出CSV按钮
showExportPdf: true, // 显示导出PDF按钮
showLanguage: true, // 显示语言切换按钮
showTheme: true, // 显示主题切换按钮
showFullscreen: true, // 显示全屏按钮
showTimeScale: true, // 显示时间刻度按钮组
timeScaleDimensions: [ // 显示所有时间刻度维度
'hour', 'day', 'week', 'month', 'quarter', 'year',
],
defaultTimeScale: 'week', // 默认选中周视图
showExpandCollapse: false, // 显示展开/折叠按钮
}
const newTask = ref({
name: '',
startDate: '',
endDate: '',
})
const addTask = () => {
tasks.value.push({
id: tasks.value.length + 1,
name: newTask.value.name,
startDate: newTask.value.startDate,
endDate: newTask.value.endDate,
progress: 0,
department: '未分配',
departmentCode: 'D000',
assignee: '',
assigneeName: '',
type: 'task',
})
newTask.value = { name: '', startDate: '', endDate: '' }
showAddTaskDrawer.value = false
}
const addMilestone = () => {
milestones.value.push({
id: milestones.value.length + 101,
name: newTask.value.name,
startDate: newTask.value.startDate,
type: 'milestone',
icon: 'diamond',
})
//console.log('milestones: ', milestones.value)
newTask.value = { name: '', startDate: '', endDate: '' }
showAddMilestoneDialog.value = false
}
const onTaskDblclick = (task: any) => {
alert(`双击任务: ${task.name}`)
}
const onTaskClick = (task: any) => {
alert(`单击任务: ${task.name}`)
}
const onMilestoneDblclick = (milestone: any) => {
alert(`双击里程碑: ${milestone.name}`)
}
// 任务行拖拽完成事件
const handleTaskRowMoved = (payload: {
draggedTask: Task
targetTask: Task
position: 'after' | 'child'
}) => {
const { draggedTask, targetTask, position } = payload
alert(`任务 [${draggedTask.name}] 被拖拽到任务 [${targetTask.name}] ${position === 'after' ? '之后' : '下方作为子任务'}`)
// 组件已自动更新任务的层级关系和位置
// position === 'after': 任务被放置在目标任务之后(同级)
// position === 'child': 任务被放置为目标任务的子任务(第一个子任务位置)
// 这里可以:
// 1. 显示确认对话框,让用户确认是否移动
// 2. 调用后端 API 保存新的任务层级关系
// 3. 更新相关的依赖关系
// 示例:调用后端 API
// await api.updateTaskHierarchy({
// taskId: draggedTask.id,
// targetTaskId: targetTask.id,
// position: position
// })
}
// 任务添加后回调
const onTaskAdded = (res) => {
const addedTask = tasks.value.find(t => t.id === res.task.id)
if (addedTask) {
// 使用addedTask.assignee去查找assigneeOptions的label进行赋值
const assigneeOption = assigneeOptions.value.find(option => option.value === addedTask.assignee)
if (assigneeOption) {
addedTask.assigneeName = assigneeOption.label
}
} else {
// 使用addedTask.assignee去查找assigneeOptions的label进行赋值
const assigneeOption = assigneeOptions.value.find(option => option.value === res.task.assignee)
if (assigneeOption) {
res.task.assigneeName = assigneeOption.label
}
tasks.value.push(res.task)
}
}
</script>
<template>
<div>
<!-- 工具设置面板 -->
@@ -511,6 +227,7 @@ const onTaskAdded = (res) => {
ref="ganttRef"
:tasks="tasks"
:milestones="milestones"
:resources="resources"
:locale="controlMode === 'props' ? propsLocale : undefined"
:theme="controlMode === 'props' ? propsTheme : undefined"
:time-scale="controlMode === 'props' ? propsTimeScale : undefined"
@@ -574,8 +291,8 @@ const onTaskAdded = (res) => {
<!-- 自定义Dialog组件基于element plus -->
<el-dialog
v-model="showAddMilestoneDialog"
title="自定义添加里程碑组件 - Element Plus"
v-model="showAddMilestoneDialog"
width="400px"
@close="newTask = { name: '', startDate: '', endDate: '' }"
>
@@ -599,6 +316,537 @@ const onTaskAdded = (res) => {
</div>
</template>
<script setup lang="ts">
import { ref, computed, watch } from 'vue';
import { GanttChart, Task } from 'jordium-gantt-vue3'
import 'jordium-gantt-vue3/dist/assets/jordium-gantt-vue3.css'
// GanttChart ref
const ganttRef = ref(null)
// 控制模式:'expose' 使用expose方法,'props' 使用Props
const controlMode = ref<'expose' | 'props'>('expose')
// 状态变量
const fullscreenStatus = ref(false)
const expandStatus = ref(false)
const currentLocaleStatus = ref<'zh-CN' | 'en-US'>('zh-CN')
const currentScaleStatus = ref<'hour' | 'day' | 'week' | 'month' | 'quarter' | 'year'>('week')
const currentThemeStatus = ref<'light' | 'dark'>('light')
// Props控制变量
const propsLocale = ref<'zh-CN' | 'en-US'>('zh-CN')
const propsTheme = ref<'light' | 'dark'>('light')
const propsTimeScale = ref<'hour' | 'day' | 'week' | 'month' | 'quarter' | 'year'>('week')
const propsFullscreen = ref(false)
const propsExpandAll = ref(false)
// 监听 Props 变化,同步 status
watch(propsLocale, (newLocale) => {
currentLocaleStatus.value = newLocale
})
watch(propsTheme, (newTheme) => {
currentThemeStatus.value = newTheme
})
watch(propsTimeScale, (newScale) => {
currentScaleStatus.value = newScale
})
watch(propsFullscreen, (newFullscreen) => {
fullscreenStatus.value = newFullscreen
})
watch(propsExpandAll, (newExpandAll) => {
expandStatus.value = newExpandAll
})
// 更新状态函数
const updateStatus = () => {
if (ganttRef.value) {
fullscreenStatus.value = ganttRef.value.isFullscreen()
expandStatus.value = ganttRef.value.isExpandAll()
currentLocaleStatus.value = ganttRef.value.currentLocale()
currentScaleStatus.value = ganttRef.value.currentScale()
currentThemeStatus.value = ganttRef.value.currentTheme()
}
}
// Expose 方法处理器
const handleToggleFullscreen = () => {
ganttRef.value?.toggleFullscreen()
updateStatus()
propsFullscreen.value = ganttRef.value?.isFullscreen() ?? false
}
const handleToggleExpandAll = () => {
ganttRef.value?.toggleExpandAll()
updateStatus()
propsExpandAll.value = ganttRef.value?.isExpandAll() ?? false
}
const handleSetLocale = (locale: 'zh-CN' | 'en-US') => {
ganttRef.value?.setLocale(locale)
currentLocaleStatus.value = locale
propsLocale.value = locale
}
const handleSetTimeScale = (scale: 'hour' | 'day' | 'week' | 'month' | 'quarter' | 'year') => {
ganttRef.value?.setTimeScale(scale)
updateStatus()
propsTimeScale.value = scale
}
const handleSetTheme = (mode: 'light' | 'dark') => {
ganttRef.value?.setTheme(mode)
updateStatus()
propsTheme.value = mode
}
// 定义类型接口
interface TaskListColumnConfig {
key: string;
label: string;
visible: boolean;
width?: number;
}
interface ToolbarConfig {
showAddTask?: boolean;
showAddMilestone?: boolean;
showTodayLocate?: boolean;
showExportCsv?: boolean;
showExportPdf?: boolean;
showLanguage?: boolean;
showTheme?: boolean;
showFullscreen?: boolean;
showTimeScale?: boolean;
timeScaleDimensions?: string[];
defaultTimeScale?: string;
showExpandCollapse?: boolean;
}
const tasks = ref([
{
id: 1,
name: '项目启动',
startDate: '2025-10-30',
endDate: '2025-11-5',
progress: 100,
department: '管理部',
departmentCode: 'D001',
type: 'task',
assignee: '',
assigneeName: ''
}
])
const milestones = ref([
{
id: 101,
name: '项目立项',
startDate: '2025-10-29',
type: 'milestone',
icon: 'diamond'
}
])
// 资源数据源 - 按照Resource类型结构定义
const resources = ref([
{
id: 'R001',
name: '张三',
type: '开发工程师',
avatar: 'https://i.pravatar.cc/150?img=1',
description: '前端开发专家,擅长Vue.js和React',
department: '技术部',
skills: ['Vue.js', 'React', 'TypeScript', 'Node.js'],
capacity: 85,
color: '#1890ff',
tasks: [
{
id: 1000,
name: '前端框架搭建',
startDate: '2025-11-01',
endDate: '2025-11-05',
progress: 80,
type: 'task'
},
{
id: 1001,
name: '组件库开发',
startDate: '2025-11-06',
endDate: '2025-11-15',
progress: 50,
type: 'task'
},
{
id: 1002,
name: '页面开发',
startDate: '2025-11-16',
endDate: '2025-11-25',
progress: 20,
type: 'task'
}
]
},
{
id: 'R002',
name: '李四',
type: '后端工程师',
avatar: 'https://i.pravatar.cc/150?img=2',
description: 'Java后端开发,熟悉Spring全家桶',
department: '技术部',
skills: ['Java', 'Spring Boot', 'MySQL', 'Redis'],
capacity: 85,
color: '#52c41a',
tasks: [
{
id: 1003,
name: '数据库设计',
startDate: '2025-11-01',
endDate: '2025-11-03',
progress: 100,
type: 'task'
},
{
id: 1004,
name: 'API接口开发',
startDate: '2025-11-04',
endDate: '2025-11-12',
progress: 60,
type: 'task'
},
{
id: 1005,
name: '性能优化',
startDate: '2025-11-20',
endDate: '2025-11-28',
progress: 0,
type: 'task'
}
]
},
{
id: 'R003',
name: '王五',
type: 'UI设计师',
avatar: 'https://i.pravatar.cc/150?img=3',
description: '资深UI/UX设计师',
department: '设计部',
skills: ['Figma', 'Sketch', 'Photoshop', 'Illustrator'],
capacity: 8,
color: '#faad14',
tasks: [
{
id: 1006,
name: 'UI原型设计',
startDate: '2025-10-28',
endDate: '2025-11-02',
progress: 100,
type: 'task'
},
{
id: 1007,
name: '视觉规范制定',
startDate: '2025-11-03',
endDate: '2025-11-08',
progress: 90,
type: 'task'
},
{
id: 1008,
name: '界面设计',
startDate: '2025-11-09',
endDate: '2025-11-18',
progress: 40,
type: 'task'
}
]
},
{
id: 'R004',
name: '赵六',
type: '测试工程师',
avatar: 'https://i.pravatar.cc/150?img=4',
description: '软件测试专家,自动化测试经验丰富',
department: '质量部',
skills: ['Selenium', 'Jest', 'Cypress', 'JMeter'],
capacity: 8,
color: '#f5222d',
tasks: [
{
id: 1009,
name: '测试计划编写',
startDate: '2025-11-10',
endDate: '2025-11-12',
progress: 70,
type: 'task'
},
{
id: 1010,
name: '自动化测试脚本',
startDate: '2025-11-13',
endDate: '2025-11-20',
progress: 30,
type: 'task'
},
{
id: 1011,
name: '功能测试',
startDate: '2025-11-21',
endDate: '2025-11-30',
progress: 0,
type: 'task'
}
]
},
{
id: 'R005',
name: '钱七',
type: '产品经理',
avatar: 'https://i.pravatar.cc/150?img=5',
description: '5年产品经验,擅长用户需求分析',
department: '产品部',
skills: ['需求分析', 'Axure', 'PRD撰写', '用户研究'],
capacity: 8,
color: '#722ed1',
tasks: [
{
id: 1012,
name: '需求调研',
startDate: '2025-10-25',
endDate: '2025-10-30',
progress: 100,
type: 'task'
},
{
id: 1013,
name: 'PRD文档编写',
startDate: '2025-10-31',
endDate: '2025-11-05',
progress: 85,
type: 'task'
},
{
id: 1014,
name: '产品验收',
startDate: '2025-11-25',
endDate: '2025-11-30',
progress: 0,
type: 'task'
}
]
},
{
id: 'R006',
name: '孙八',
type: '全栈工程师',
avatar: 'https://i.pravatar.cc/150?img=6',
description: '全栈开发,前后端通吃',
department: '技术部',
skills: ['Vue.js', 'Node.js', 'Python', 'Docker'],
capacity: 8,
color: '#13c2c2',
tasks: [
{
id: 1015,
name: '服务器部署',
startDate: '2025-11-01',
endDate: '2025-11-04',
progress: 100,
type: 'task'
},
{
id: 1016,
name: 'CI/CD配置',
startDate: '2025-11-05',
endDate: '2025-11-10',
progress: 75,
type: 'task'
},
{
id: 1017,
name: '微服务架构',
startDate: '2025-11-11',
endDate: '2025-11-22',
progress: 35,
type: 'task'
}
]
}
])
const customMessages = {
'zh-CN': {
department: '部门',
departmentCode: '部门编号',
resourceView: {
desc: '资源视图',
capacity: '利用率',
overloaded: '超负荷',
duration: '时间周期',
overloadWarning: '资源超负荷警告',
conflictDuration: '冲突时段',
conflictWith: '与',
conflictSuffix: '冲突',
},
},
'en-US': {
department: 'Department',
departmentCode: 'Department Code',
resourceView: {
desc: 'Resource View',
capacity: 'capacity',
overloaded: 'overLoaded',
duration: 'duration',
overloadWarning: 'Overload Warning',
conflictDuration: 'conflict duration',
conflictWith: 'conflict with',
conflictSuffix: '',
},
}
}
// const tasks = ref([])
// const milestones = ref([])
const showAddTaskDrawer = ref(false);
const showAddMilestoneDialog = ref(false);
// 定义可动态配置的列
const availableColumns = ref<TaskListColumnConfig[]>([
{ key: 'startDate', label: '开始日期', visible: true },
{ key: 'endDate', label: '结束日期', visible: true },
{ key: 'progress', label: '进度', visible: true },
{ key: 'department', label: '部门', visible: true, width: 120 },
{ key: 'departmentCode', label: '部门编号', visible: true },
{ key: 'assigneeName', label: '负责人', visible: true },
])
// 自定义负责人列表
const assigneeOptions = ref([
{ value: 'zhangsan', label: '张三' },
{ value: 'lisi', label: '李四' },
{ value: 'wangwu', label: '王五' },
])
// TaskList宽度配置示例
const taskListConfig = {
defaultWidth: '50%', // 默认展开宽度50%
minWidth: '300px', // 最小宽度300px(默认280px
maxWidth: '1200px', // 最大宽度1200px(默认1160px
columns: availableColumns.value
}
// toolbar配置示例
const toolbarConfig: ToolbarConfig = {
showAddTask: true, // 显示添加任务按钮
showAddMilestone: true, // 显示添加里程碑按钮
showTodayLocate: true, // 显示定位到今天按钮
showExportCsv: true, // 显示导出CSV按钮
showExportPdf: true, // 显示导出PDF按钮
showLanguage: true, // 显示语言切换按钮
showTheme: true, // 显示主题切换按钮
showFullscreen: true, // 显示全屏按钮
showTimeScale: true, // 显示时间刻度按钮组
timeScaleDimensions: [ // 显示所有时间刻度维度
'hour', 'day', 'week', 'month', 'quarter', 'year'
],
defaultTimeScale: 'week', // 默认选中周视图
showExpandCollapse: false // 显示展开/折叠按钮
}
const newTask = ref({
name: '',
startDate: '',
endDate: ''
});
const addTask = () => {
tasks.value.push({
id: tasks.value.length + 1,
name: newTask.value.name,
startDate: newTask.value.startDate,
endDate: newTask.value.endDate,
progress: 0,
department: '未分配',
departmentCode: 'D000',
assignee: '',
assigneeName: '',
type: 'task',
});
newTask.value = { name: '', startDate: '', endDate: '' };
showAddTaskDrawer.value = false;
};
const addMilestone = () => {
milestones.value.push({
id: milestones.value.length + 101,
name: newTask.value.name,
startDate: newTask.value.startDate,
type: 'milestone',
icon: 'diamond'
});
console.log('milestones: ', milestones.value)
newTask.value = { name: '', startDate: '', endDate: '' };
showAddMilestoneDialog.value = false;
}
const onTaskDblclick = (task: any) => {
alert(`双击任务: ${task.name}`)
}
const onTaskClick = (task: any) => {
alert(`单击任务: ${task.name}`)
}
const onMilestoneDblclick = (milestone: any) => {
alert(`双击里程碑: ${milestone.name}`)
}
// 任务行拖拽完成事件
const handleTaskRowMoved = (payload: {
draggedTask: Task
targetTask: Task
position: 'after' | 'child'
}) => {
const { draggedTask, targetTask, position } = payload
alert(`任务 [${draggedTask.name}] 被拖拽到任务 [${targetTask.name}] ${position === 'after' ? '之后' : '下方作为子任务'}`)
// 组件已自动更新任务的层级关系和位置
// position === 'after': 任务被放置在目标任务之后(同级)
// position === 'child': 任务被放置为目标任务的子任务(第一个子任务位置)
// 这里可以:
// 1. 显示确认对话框,让用户确认是否移动
// 2. 调用后端 API 保存新的任务层级关系
// 3. 更新相关的依赖关系
// 示例:调用后端 API
// await api.updateTaskHierarchy({
// taskId: draggedTask.id,
// targetTaskId: targetTask.id,
// position: position
// })
}
// 任务添加后回调
const onTaskAdded = (res) => {
const addedTask = tasks.value.find(t => t.id === res.task.id);
if (addedTask) {
// 使用addedTask.assignee去查找assigneeOptions的label进行赋值
const assigneeOption = assigneeOptions.value.find(option => option.value === addedTask.assignee);
if (assigneeOption) {
addedTask.assigneeName = assigneeOption.label;
}
} else {
// 使用addedTask.assignee去查找assigneeOptions的label进行赋值
const assigneeOption = assigneeOptions.value.find(option => option.value === res.task.assignee);
if (assigneeOption) {
res.task.assigneeName = assigneeOption.label;
}
tasks.value.push(res.task);
}
};
</script>
<style scoped>
/* 工具设置面板 */
.tool-settings-panel {