v1.0.9 - Add Story and sub-tasks deletion logic; l18n updates; GanttConfirmDialog updates
This commit is contained in:
@@ -5,6 +5,15 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [1.0.9] - 2025-07-04
|
||||
|
||||
### Changed
|
||||
- 更新多语言标签
|
||||
|
||||
### Added
|
||||
- 增加Story删除时选择是否删除子任务的选项
|
||||
- 删除Story或Task时同时删除前后的关系线路
|
||||
|
||||
## [1.0.6] - 2025-07-03
|
||||
|
||||
### Changed
|
||||
|
||||
+195
-19
@@ -9,9 +9,11 @@ import packageInfo from '../package.json'
|
||||
import '../src/styles/theme-variables.css'
|
||||
import VersionHistoryDrawer from './VersionHistoryDrawer.vue'
|
||||
import { useMessage } from '../src/composables/useMessage'
|
||||
import { useI18n } from '../src/composables/useI18n'
|
||||
import type { Task } from '../src/models/Task'
|
||||
|
||||
const { showMessage } = useMessage()
|
||||
const { t, formatTranslation } = useI18n()
|
||||
|
||||
const tasks = ref<Task[]>([])
|
||||
const milestones = ref<Task[]>([])
|
||||
@@ -42,7 +44,7 @@ const toolbarConfig = {
|
||||
|
||||
// 自定义CSV导出处理器(可选)
|
||||
const handleCustomCsvExport = () => {
|
||||
showMessage('自定义CSV导出被调用', 'info', { closable: true })
|
||||
showMessage(t.value.customCsvExportCalled, 'info', { closable: true })
|
||||
|
||||
// 这里可以实现自定义的CSV导出逻辑
|
||||
// 例如:添加额外的数据处理、格式化、或发送到服务器等
|
||||
@@ -70,11 +72,17 @@ const handleAddMilestone = () => {
|
||||
}
|
||||
|
||||
const handleLanguageChange = (lang: 'zh' | 'en') => {
|
||||
showMessage(`语言切换到:${lang}`, 'info', { closable: true })
|
||||
const languageText = lang === 'zh' ? '中文' : 'English'
|
||||
showMessage(formatTranslation('languageSwitchedTo', { language: languageText }), 'info', {
|
||||
closable: true,
|
||||
})
|
||||
}
|
||||
|
||||
const handleThemeChange = (isDark: boolean) => {
|
||||
showMessage(`主题切换到:${isDark ? '暗黑模式' : '明亮模式'}`, 'info', { closable: true })
|
||||
const themeText = isDark ? t.value.darkModeText : t.value.lightModeText
|
||||
showMessage(formatTranslation('themeSwitchedTo', { theme: themeText }), 'info', {
|
||||
closable: true,
|
||||
})
|
||||
}
|
||||
|
||||
// 里程碑保存处理器示例
|
||||
@@ -104,7 +112,7 @@ const handleMilestoneDelete = async (milestoneId: number) => {
|
||||
const milestoneIndex = milestones.value.findIndex(m => m.id === milestoneId)
|
||||
if (milestoneIndex !== -1) {
|
||||
milestones.value.splice(milestoneIndex, 1)
|
||||
showMessage('里程碑删除成功', 'success', { closable: false })
|
||||
showMessage(t.value.milestoneDeleteSuccess, 'success', { closable: false })
|
||||
|
||||
// 等待DOM更新完成
|
||||
await nextTick()
|
||||
@@ -146,7 +154,9 @@ const handleTaskUpdate = (updatedTask: Task) => {
|
||||
|
||||
const originalTask = findOriginalTask(tasks.value)
|
||||
if (!originalTask) {
|
||||
showMessage(`未找到要更新的任务,ID: ${updatedTask.id}`, 'warning', { closable: true })
|
||||
showMessage(formatTranslation('taskNotFound', { id: updatedTask.id }), 'warning', {
|
||||
closable: true,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -203,9 +213,11 @@ const handleTaskUpdate = (updatedTask: Task) => {
|
||||
}
|
||||
|
||||
if (!addToParentChildren(tasks.value)) {
|
||||
showMessage(`未找到新父任务,ID: ${taskToAdd.parentId},将作为顶级任务添加`, 'warning', {
|
||||
closable: true,
|
||||
})
|
||||
showMessage(
|
||||
formatTranslation('newParentTaskNotFound', { parentId: taskToAdd.parentId }),
|
||||
'warning',
|
||||
{ closable: true },
|
||||
)
|
||||
tasks.value.push(taskToAdd)
|
||||
}
|
||||
} else {
|
||||
@@ -237,7 +249,9 @@ const handleTaskUpdate = (updatedTask: Task) => {
|
||||
}
|
||||
|
||||
if (!updateTaskInPlace(tasks.value)) {
|
||||
showMessage(`就地更新失败,未找到任务,ID: ${updatedTask.id}`, 'warning', { closable: true })
|
||||
showMessage(formatTranslation('inPlaceUpdateFailed', { id: updatedTask.id }), 'warning', {
|
||||
closable: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -285,7 +299,6 @@ const handleTaskAdd = (newTask: Task) => {
|
||||
|
||||
// 尝试添加到父任务的children中
|
||||
if (!addToParentChildren(tasks.value)) {
|
||||
console.warn('未找到父任务,ID:', newTask.parentId, ',将作为顶级任务添加')
|
||||
// 如果没找到父任务,作为顶级任务添加
|
||||
tasks.value.push({ ...newTask })
|
||||
}
|
||||
@@ -297,13 +310,16 @@ const handleTaskAdd = (newTask: Task) => {
|
||||
|
||||
// 任务删除处理器
|
||||
const handleTaskDelete = (taskToDelete: Task) => {
|
||||
// 收集要删除的所有任务ID(包括子任务)
|
||||
const deletedTaskIds = collectAllTaskIds(taskToDelete)
|
||||
|
||||
// 递归查找和删除任务(支持嵌套结构)
|
||||
const deleteTaskFromArray = (taskArray: Task[]): boolean => {
|
||||
for (let i = 0; i < taskArray.length; i++) {
|
||||
if (taskArray[i].id === taskToDelete.id) {
|
||||
// 找到任务,删除它
|
||||
taskArray.splice(i, 1)
|
||||
showMessage('已删除任务', 'success', { closable: false })
|
||||
showMessage(t.value.taskDeletedSuccess, 'success', { closable: false })
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -317,8 +333,116 @@ const handleTaskDelete = (taskToDelete: Task) => {
|
||||
return false
|
||||
}
|
||||
|
||||
if (!deleteTaskFromArray(tasks.value)) {
|
||||
showMessage(`未找到要删除的任务,ID: ${taskToDelete.id}`, 'warning', { closable: true })
|
||||
if (deleteTaskFromArray(tasks.value)) {
|
||||
// 删除成功后,清理predecessor依赖关系
|
||||
cleanupPredecessorReferences(deletedTaskIds)
|
||||
} else {
|
||||
showMessage(formatTranslation('taskToDeleteNotFound', { id: taskToDelete.id }), 'warning', {
|
||||
closable: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 删除story及其所有子任务
|
||||
const handleStoryDeleteWithChildren = (storyToDelete: Task) => {
|
||||
// 收集要删除的所有任务ID(story及其所有子任务)
|
||||
const deletedTaskIds = collectAllTaskIds(storyToDelete)
|
||||
|
||||
// 递归查找和删除story(包含其所有子任务)
|
||||
const deleteStoryFromArray = (taskArray: Task[]): boolean => {
|
||||
for (let i = 0; i < taskArray.length; i++) {
|
||||
if (taskArray[i].id === storyToDelete.id) {
|
||||
// 找到story,直接删除它(包含所有子任务)
|
||||
taskArray.splice(i, 1)
|
||||
showMessage(
|
||||
formatTranslation('storyDeleteAllSuccess', { name: storyToDelete.name }),
|
||||
'success',
|
||||
{
|
||||
closable: false,
|
||||
},
|
||||
)
|
||||
return true
|
||||
}
|
||||
|
||||
// 如果有子任务,递归查找
|
||||
if (taskArray[i].children && taskArray[i].children.length > 0) {
|
||||
if (deleteStoryFromArray(taskArray[i].children!)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
if (deleteStoryFromArray(tasks.value)) {
|
||||
// 删除成功后,清理predecessor依赖关系
|
||||
cleanupPredecessorReferences(deletedTaskIds)
|
||||
} else {
|
||||
showMessage(formatTranslation('storyNotFound', { id: storyToDelete.id }), 'warning', {
|
||||
closable: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 仅删除story,保留子任务
|
||||
const handleStoryDeleteOnly = (storyToDelete: Task) => {
|
||||
// 递归查找story并处理其子任务
|
||||
const deleteStoryOnlyFromArray = (taskArray: Task[]): boolean => {
|
||||
for (let i = 0; i < taskArray.length; i++) {
|
||||
if (taskArray[i].id === storyToDelete.id) {
|
||||
// 找到story
|
||||
const childrenToPromote = taskArray[i].children || []
|
||||
|
||||
// 步骤a: 克隆并升级子任务
|
||||
const upgradedChildren: Task[] = []
|
||||
if (childrenToPromote.length > 0) {
|
||||
childrenToPromote.forEach(child => {
|
||||
// 深度克隆子任务
|
||||
const clonedChild = JSON.parse(JSON.stringify(child))
|
||||
// 升级:移除parentId,使其成为顶级任务
|
||||
delete clonedChild.parentId
|
||||
upgradedChildren.push(clonedChild)
|
||||
})
|
||||
}
|
||||
|
||||
// 步骤b: 删除story数据
|
||||
taskArray.splice(i, 1)
|
||||
|
||||
// 将升级后的子任务push到tasks集合顶层
|
||||
if (upgradedChildren.length > 0) {
|
||||
tasks.value.push(...upgradedChildren)
|
||||
}
|
||||
|
||||
showMessage(
|
||||
formatTranslation('storyDeleteOnlySuccess', {
|
||||
name: storyToDelete.name,
|
||||
count: upgradedChildren.length,
|
||||
}),
|
||||
'success',
|
||||
{
|
||||
closable: false,
|
||||
},
|
||||
)
|
||||
return true
|
||||
}
|
||||
|
||||
// 如果有子任务,递归查找
|
||||
if (taskArray[i].children && taskArray[i].children.length > 0) {
|
||||
if (deleteStoryOnlyFromArray(taskArray[i].children!)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
if (deleteStoryOnlyFromArray(tasks.value)) {
|
||||
// 删除成功后,清理story的predecessor依赖关系(只清理story本身的ID,不影响子任务)
|
||||
cleanupPredecessorReferences([storyToDelete.id])
|
||||
} else {
|
||||
showMessage(formatTranslation('storyNotFound', { id: storyToDelete.id }), 'warning', {
|
||||
closable: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -328,7 +452,9 @@ const handleMilestoneIconChange = (milestoneId: number, icon: string) => {
|
||||
if (milestoneIndex !== -1) {
|
||||
milestones.value[milestoneIndex].icon = icon
|
||||
} else {
|
||||
showMessage(`未找到要更新图标的里程碑,ID: ${milestoneId}`, 'warning', { closable: true })
|
||||
showMessage(formatTranslation('milestoneIconUpdateNotFound', { id: milestoneId }), 'warning', {
|
||||
closable: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -350,10 +476,16 @@ const handleTaskDrawerClose = () => {
|
||||
isEditMode.value = false
|
||||
}
|
||||
|
||||
const handleTaskDrawerDelete = (taskId: number) => {
|
||||
const taskToDelete = tasks.value.find(t => t.id === taskId)
|
||||
if (taskToDelete) {
|
||||
handleTaskDelete(taskToDelete)
|
||||
const handleTaskDrawerDelete = (task: Task, deleteChildren?: boolean) => {
|
||||
if (task.type === 'story' && deleteChildren === true) {
|
||||
// 删除story及其所有子任务
|
||||
handleStoryDeleteWithChildren(task)
|
||||
} else if (task.type === 'story' && deleteChildren === false) {
|
||||
// 仅删除story,保留子任务
|
||||
handleStoryDeleteOnly(task)
|
||||
} else {
|
||||
// 普通任务删除
|
||||
handleTaskDelete(task)
|
||||
}
|
||||
showTaskDrawer.value = false
|
||||
}
|
||||
@@ -411,6 +543,50 @@ function findTaskDeep(taskArray: Task[], id: number): Task | null {
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
// 清理被删除任务的predecessor依赖关系
|
||||
const cleanupPredecessorReferences = (deletedTaskIds: number[]) => {
|
||||
// 递归清理所有任务(包括嵌套的子任务)的predecessor
|
||||
const cleanupTaskArray = (taskArray: Task[]) => {
|
||||
taskArray.forEach(task => {
|
||||
if (task.predecessor) {
|
||||
// 将predecessor字符串解析为ID数组
|
||||
const predecessorIds = task.predecessor
|
||||
.split(',')
|
||||
.map(id => parseInt(id.trim()))
|
||||
.filter(id => !isNaN(id))
|
||||
|
||||
// 过滤掉被删除的任务ID
|
||||
const validPredecessorIds = predecessorIds.filter(id => !deletedTaskIds.includes(id))
|
||||
|
||||
// 更新predecessor属性
|
||||
if (validPredecessorIds.length === 0) {
|
||||
delete task.predecessor
|
||||
} else {
|
||||
task.predecessor = validPredecessorIds.join(',')
|
||||
}
|
||||
}
|
||||
|
||||
// 递归处理子任务
|
||||
if (task.children && task.children.length > 0) {
|
||||
cleanupTaskArray(task.children)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
cleanupTaskArray(tasks.value)
|
||||
}
|
||||
|
||||
// 收集被删除任务及其所有子任务的ID
|
||||
const collectAllTaskIds = (task: Task): number[] => {
|
||||
const ids = [task.id]
|
||||
if (task.children && task.children.length > 0) {
|
||||
task.children.forEach(child => {
|
||||
ids.push(...collectAllTaskIds(child))
|
||||
})
|
||||
}
|
||||
return ids
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -459,7 +635,7 @@ function findTaskDeep(taskArray: Task[], id: number): Task | null {
|
||||
:on-milestone-delete="handleMilestoneDelete"
|
||||
:on-task-update="handleTaskUpdate"
|
||||
:on-task-add="handleTaskAdd"
|
||||
:on-task-delete="handleTaskDelete"
|
||||
:on-task-delete="handleTaskDrawerDelete"
|
||||
:on-milestone-icon-change="handleMilestoneIconChange"
|
||||
@taskbar-drag-end="handleTaskbarDragOrResizeEnd"
|
||||
@taskbar-resize-end="handleTaskbarDragOrResizeEnd"
|
||||
|
||||
@@ -115,5 +115,14 @@
|
||||
"Story进度根据子任务集进度改编重新计算",
|
||||
"同时保留Story进度单独的调整方式"
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "1.0.9",
|
||||
"date": "2025-07-04",
|
||||
"notes": [
|
||||
"更新多语言标签",
|
||||
"增加Story删除时选择是否删除子任务的选项",
|
||||
"删除Story或Task时同时删除前后的关系线路"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
Generated
+3
-2
@@ -1,12 +1,13 @@
|
||||
{
|
||||
"name": "jordium-gantt-vue3",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.9",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "jordium-gantt-vue3",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.9",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"date-fns": "^4.1.0",
|
||||
"html2canvas": "^1.4.1",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jordium-gantt-vue3",
|
||||
"version": "1.0.6",
|
||||
"version": "1.0.9",
|
||||
"type": "module",
|
||||
"main": "dist/jordium-gantt-vue3.cjs.js",
|
||||
"module": "dist/jordium-gantt-vue3.es.js",
|
||||
|
||||
+195
-19
@@ -9,9 +9,11 @@ import packageInfo from '../package.json'
|
||||
import '../src/styles/theme-variables.css'
|
||||
import VersionHistoryDrawer from './VersionHistoryDrawer.vue'
|
||||
import { useMessage } from '../src/composables/useMessage'
|
||||
import { useI18n } from '../src/composables/useI18n'
|
||||
import type { Task } from '../src/models/Task'
|
||||
|
||||
const { showMessage } = useMessage()
|
||||
const { t, formatTranslation } = useI18n()
|
||||
|
||||
const tasks = ref<Task[]>([])
|
||||
const milestones = ref<Task[]>([])
|
||||
@@ -42,7 +44,7 @@ const toolbarConfig = {
|
||||
|
||||
// 自定义CSV导出处理器(可选)
|
||||
const handleCustomCsvExport = () => {
|
||||
showMessage('自定义CSV导出被调用', 'info', { closable: true })
|
||||
showMessage(t.value.customCsvExportCalled, 'info', { closable: true })
|
||||
|
||||
// 这里可以实现自定义的CSV导出逻辑
|
||||
// 例如:添加额外的数据处理、格式化、或发送到服务器等
|
||||
@@ -70,11 +72,17 @@ const handleAddMilestone = () => {
|
||||
}
|
||||
|
||||
const handleLanguageChange = (lang: 'zh' | 'en') => {
|
||||
showMessage(`语言切换到:${lang}`, 'info', { closable: true })
|
||||
const languageText = lang === 'zh' ? '中文' : 'English'
|
||||
showMessage(formatTranslation('languageSwitchedTo', { language: languageText }), 'info', {
|
||||
closable: true,
|
||||
})
|
||||
}
|
||||
|
||||
const handleThemeChange = (isDark: boolean) => {
|
||||
showMessage(`主题切换到:${isDark ? '暗黑模式' : '明亮模式'}`, 'info', { closable: true })
|
||||
const themeText = isDark ? t.value.darkModeText : t.value.lightModeText
|
||||
showMessage(formatTranslation('themeSwitchedTo', { theme: themeText }), 'info', {
|
||||
closable: true,
|
||||
})
|
||||
}
|
||||
|
||||
// 里程碑保存处理器示例
|
||||
@@ -104,7 +112,7 @@ const handleMilestoneDelete = async (milestoneId: number) => {
|
||||
const milestoneIndex = milestones.value.findIndex(m => m.id === milestoneId)
|
||||
if (milestoneIndex !== -1) {
|
||||
milestones.value.splice(milestoneIndex, 1)
|
||||
showMessage('里程碑删除成功', 'success', { closable: false })
|
||||
showMessage(t.value.milestoneDeleteSuccess, 'success', { closable: false })
|
||||
|
||||
// 等待DOM更新完成
|
||||
await nextTick()
|
||||
@@ -146,7 +154,9 @@ const handleTaskUpdate = (updatedTask: Task) => {
|
||||
|
||||
const originalTask = findOriginalTask(tasks.value)
|
||||
if (!originalTask) {
|
||||
showMessage(`未找到要更新的任务,ID: ${updatedTask.id}`, 'warning', { closable: true })
|
||||
showMessage(formatTranslation('taskNotFound', { id: updatedTask.id }), 'warning', {
|
||||
closable: true,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -203,9 +213,11 @@ const handleTaskUpdate = (updatedTask: Task) => {
|
||||
}
|
||||
|
||||
if (!addToParentChildren(tasks.value)) {
|
||||
showMessage(`未找到新父任务,ID: ${taskToAdd.parentId},将作为顶级任务添加`, 'warning', {
|
||||
closable: true,
|
||||
})
|
||||
showMessage(
|
||||
formatTranslation('newParentTaskNotFound', { parentId: taskToAdd.parentId }),
|
||||
'warning',
|
||||
{ closable: true },
|
||||
)
|
||||
tasks.value.push(taskToAdd)
|
||||
}
|
||||
} else {
|
||||
@@ -237,7 +249,9 @@ const handleTaskUpdate = (updatedTask: Task) => {
|
||||
}
|
||||
|
||||
if (!updateTaskInPlace(tasks.value)) {
|
||||
showMessage(`就地更新失败,未找到任务,ID: ${updatedTask.id}`, 'warning', { closable: true })
|
||||
showMessage(formatTranslation('inPlaceUpdateFailed', { id: updatedTask.id }), 'warning', {
|
||||
closable: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -285,7 +299,6 @@ const handleTaskAdd = (newTask: Task) => {
|
||||
|
||||
// 尝试添加到父任务的children中
|
||||
if (!addToParentChildren(tasks.value)) {
|
||||
console.warn('未找到父任务,ID:', newTask.parentId, ',将作为顶级任务添加')
|
||||
// 如果没找到父任务,作为顶级任务添加
|
||||
tasks.value.push({ ...newTask })
|
||||
}
|
||||
@@ -297,13 +310,16 @@ const handleTaskAdd = (newTask: Task) => {
|
||||
|
||||
// 任务删除处理器
|
||||
const handleTaskDelete = (taskToDelete: Task) => {
|
||||
// 收集要删除的所有任务ID(包括子任务)
|
||||
const deletedTaskIds = collectAllTaskIds(taskToDelete)
|
||||
|
||||
// 递归查找和删除任务(支持嵌套结构)
|
||||
const deleteTaskFromArray = (taskArray: Task[]): boolean => {
|
||||
for (let i = 0; i < taskArray.length; i++) {
|
||||
if (taskArray[i].id === taskToDelete.id) {
|
||||
// 找到任务,删除它
|
||||
taskArray.splice(i, 1)
|
||||
showMessage('已删除任务', 'success', { closable: false })
|
||||
showMessage(t.value.taskDeletedSuccess, 'success', { closable: false })
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -317,8 +333,116 @@ const handleTaskDelete = (taskToDelete: Task) => {
|
||||
return false
|
||||
}
|
||||
|
||||
if (!deleteTaskFromArray(tasks.value)) {
|
||||
showMessage(`未找到要删除的任务,ID: ${taskToDelete.id}`, 'warning', { closable: true })
|
||||
if (deleteTaskFromArray(tasks.value)) {
|
||||
// 删除成功后,清理predecessor依赖关系
|
||||
cleanupPredecessorReferences(deletedTaskIds)
|
||||
} else {
|
||||
showMessage(formatTranslation('taskToDeleteNotFound', { id: taskToDelete.id }), 'warning', {
|
||||
closable: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 删除story及其所有子任务
|
||||
const handleStoryDeleteWithChildren = (storyToDelete: Task) => {
|
||||
// 收集要删除的所有任务ID(story及其所有子任务)
|
||||
const deletedTaskIds = collectAllTaskIds(storyToDelete)
|
||||
|
||||
// 递归查找和删除story(包含其所有子任务)
|
||||
const deleteStoryFromArray = (taskArray: Task[]): boolean => {
|
||||
for (let i = 0; i < taskArray.length; i++) {
|
||||
if (taskArray[i].id === storyToDelete.id) {
|
||||
// 找到story,直接删除它(包含所有子任务)
|
||||
taskArray.splice(i, 1)
|
||||
showMessage(
|
||||
formatTranslation('storyDeleteAllSuccess', { name: storyToDelete.name }),
|
||||
'success',
|
||||
{
|
||||
closable: false,
|
||||
},
|
||||
)
|
||||
return true
|
||||
}
|
||||
|
||||
// 如果有子任务,递归查找
|
||||
if (taskArray[i].children && taskArray[i].children.length > 0) {
|
||||
if (deleteStoryFromArray(taskArray[i].children!)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
if (deleteStoryFromArray(tasks.value)) {
|
||||
// 删除成功后,清理predecessor依赖关系
|
||||
cleanupPredecessorReferences(deletedTaskIds)
|
||||
} else {
|
||||
showMessage(formatTranslation('storyNotFound', { id: storyToDelete.id }), 'warning', {
|
||||
closable: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 仅删除story,保留子任务
|
||||
const handleStoryDeleteOnly = (storyToDelete: Task) => {
|
||||
// 递归查找story并处理其子任务
|
||||
const deleteStoryOnlyFromArray = (taskArray: Task[]): boolean => {
|
||||
for (let i = 0; i < taskArray.length; i++) {
|
||||
if (taskArray[i].id === storyToDelete.id) {
|
||||
// 找到story
|
||||
const childrenToPromote = taskArray[i].children || []
|
||||
|
||||
// 步骤a: 克隆并升级子任务
|
||||
const upgradedChildren: Task[] = []
|
||||
if (childrenToPromote.length > 0) {
|
||||
childrenToPromote.forEach(child => {
|
||||
// 深度克隆子任务
|
||||
const clonedChild = JSON.parse(JSON.stringify(child))
|
||||
// 升级:移除parentId,使其成为顶级任务
|
||||
delete clonedChild.parentId
|
||||
upgradedChildren.push(clonedChild)
|
||||
})
|
||||
}
|
||||
|
||||
// 步骤b: 删除story数据
|
||||
taskArray.splice(i, 1)
|
||||
|
||||
// 将升级后的子任务push到tasks集合顶层
|
||||
if (upgradedChildren.length > 0) {
|
||||
tasks.value.push(...upgradedChildren)
|
||||
}
|
||||
|
||||
showMessage(
|
||||
formatTranslation('storyDeleteOnlySuccess', {
|
||||
name: storyToDelete.name,
|
||||
count: upgradedChildren.length,
|
||||
}),
|
||||
'success',
|
||||
{
|
||||
closable: false,
|
||||
},
|
||||
)
|
||||
return true
|
||||
}
|
||||
|
||||
// 如果有子任务,递归查找
|
||||
if (taskArray[i].children && taskArray[i].children.length > 0) {
|
||||
if (deleteStoryOnlyFromArray(taskArray[i].children!)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
if (deleteStoryOnlyFromArray(tasks.value)) {
|
||||
// 删除成功后,清理story的predecessor依赖关系(只清理story本身的ID,不影响子任务)
|
||||
cleanupPredecessorReferences([storyToDelete.id])
|
||||
} else {
|
||||
showMessage(formatTranslation('storyNotFound', { id: storyToDelete.id }), 'warning', {
|
||||
closable: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -328,7 +452,9 @@ const handleMilestoneIconChange = (milestoneId: number, icon: string) => {
|
||||
if (milestoneIndex !== -1) {
|
||||
milestones.value[milestoneIndex].icon = icon
|
||||
} else {
|
||||
showMessage(`未找到要更新图标的里程碑,ID: ${milestoneId}`, 'warning', { closable: true })
|
||||
showMessage(formatTranslation('milestoneIconUpdateNotFound', { id: milestoneId }), 'warning', {
|
||||
closable: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -350,10 +476,16 @@ const handleTaskDrawerClose = () => {
|
||||
isEditMode.value = false
|
||||
}
|
||||
|
||||
const handleTaskDrawerDelete = (taskId: number) => {
|
||||
const taskToDelete = tasks.value.find(t => t.id === taskId)
|
||||
if (taskToDelete) {
|
||||
handleTaskDelete(taskToDelete)
|
||||
const handleTaskDrawerDelete = (task: Task, deleteChildren?: boolean) => {
|
||||
if (task.type === 'story' && deleteChildren === true) {
|
||||
// 删除story及其所有子任务
|
||||
handleStoryDeleteWithChildren(task)
|
||||
} else if (task.type === 'story' && deleteChildren === false) {
|
||||
// 仅删除story,保留子任务
|
||||
handleStoryDeleteOnly(task)
|
||||
} else {
|
||||
// 普通任务删除
|
||||
handleTaskDelete(task)
|
||||
}
|
||||
showTaskDrawer.value = false
|
||||
}
|
||||
@@ -411,6 +543,50 @@ function findTaskDeep(taskArray: Task[], id: number): Task | null {
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
// 清理被删除任务的predecessor依赖关系
|
||||
const cleanupPredecessorReferences = (deletedTaskIds: number[]) => {
|
||||
// 递归清理所有任务(包括嵌套的子任务)的predecessor
|
||||
const cleanupTaskArray = (taskArray: Task[]) => {
|
||||
taskArray.forEach(task => {
|
||||
if (task.predecessor) {
|
||||
// 将predecessor字符串解析为ID数组
|
||||
const predecessorIds = task.predecessor
|
||||
.split(',')
|
||||
.map(id => parseInt(id.trim()))
|
||||
.filter(id => !isNaN(id))
|
||||
|
||||
// 过滤掉被删除的任务ID
|
||||
const validPredecessorIds = predecessorIds.filter(id => !deletedTaskIds.includes(id))
|
||||
|
||||
// 更新predecessor属性
|
||||
if (validPredecessorIds.length === 0) {
|
||||
delete task.predecessor
|
||||
} else {
|
||||
task.predecessor = validPredecessorIds.join(',')
|
||||
}
|
||||
}
|
||||
|
||||
// 递归处理子任务
|
||||
if (task.children && task.children.length > 0) {
|
||||
cleanupTaskArray(task.children)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
cleanupTaskArray(tasks.value)
|
||||
}
|
||||
|
||||
// 收集被删除任务及其所有子任务的ID
|
||||
const collectAllTaskIds = (task: Task): number[] => {
|
||||
const ids = [task.id]
|
||||
if (task.children && task.children.length > 0) {
|
||||
task.children.forEach(child => {
|
||||
ids.push(...collectAllTaskIds(child))
|
||||
})
|
||||
}
|
||||
return ids
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -459,7 +635,7 @@ function findTaskDeep(taskArray: Task[], id: number): Task | null {
|
||||
:on-milestone-delete="handleMilestoneDelete"
|
||||
:on-task-update="handleTaskUpdate"
|
||||
:on-task-add="handleTaskAdd"
|
||||
:on-task-delete="handleTaskDelete"
|
||||
:on-task-delete="handleTaskDrawerDelete"
|
||||
:on-milestone-icon-change="handleMilestoneIconChange"
|
||||
@taskbar-drag-end="handleTaskbarDragOrResizeEnd"
|
||||
@taskbar-resize-end="handleTaskbarDragOrResizeEnd"
|
||||
|
||||
Generated
+39
-227
@@ -8,9 +8,6 @@
|
||||
"name": "packagedemo",
|
||||
"version": "1.0.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"jordium-gantt-vue3": "^1.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
||||
"@typescript-eslint/parser": "^6.21.0",
|
||||
@@ -30,6 +27,7 @@
|
||||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
|
||||
"integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
@@ -38,6 +36,7 @@
|
||||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz",
|
||||
"integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
@@ -46,6 +45,7 @@
|
||||
"version": "7.27.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.7.tgz",
|
||||
"integrity": "sha512-qnzXzDXdr/po3bOTbTIQZ7+TxNKxpkN5IifVLXS+r7qwynkZfPyjZfE7hCXbo7IoO9TNcSyibgONsf2HauUd3Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.27.7"
|
||||
},
|
||||
@@ -56,18 +56,11 @@
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/runtime": {
|
||||
"version": "7.27.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.6.tgz",
|
||||
"integrity": "sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==",
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/types": {
|
||||
"version": "7.27.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.7.tgz",
|
||||
"integrity": "sha512-8OLQgDScAOHXnAz2cV+RfzzNMipuLVBz2biuAJFMV9bfkNf393je3VM8CLkjQodW5+iWsSJdSgSWT6rsZoXHPw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-string-parser": "^7.27.1",
|
||||
"@babel/helper-validator-identifier": "^7.27.1"
|
||||
@@ -617,7 +610,9 @@
|
||||
"node_modules/@jridgewell/sourcemap-codec": {
|
||||
"version": "1.5.3",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.3.tgz",
|
||||
"integrity": "sha512-AiR5uKpFxP3PjO4R19kQGIMwxyRyPuXmKEEy301V1C0+1rVjS94EZQXf1QKZYN8Q0YM+estSPhmx5JwNftv6nw=="
|
||||
"integrity": "sha512-AiR5uKpFxP3PjO4R19kQGIMwxyRyPuXmKEEy301V1C0+1rVjS94EZQXf1QKZYN8Q0YM+estSPhmx5JwNftv6nw==",
|
||||
"dev": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@nodelib/fs.scandir": {
|
||||
"version": "2.1.5",
|
||||
@@ -938,24 +933,12 @@
|
||||
"integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/raf": {
|
||||
"version": "3.4.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/raf/-/raf-3.4.3.tgz",
|
||||
"integrity": "sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/@types/semver": {
|
||||
"version": "7.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.0.tgz",
|
||||
"integrity": "sha512-k107IF4+Xr7UHjwDc7Cfd6PRQfbdkiRabXGRjo07b4WyPahFBZCZ1sE+BNxYIJPPg73UkfOsVOLwqVc/6ETrIA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/trusted-types": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
|
||||
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "6.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz",
|
||||
@@ -1195,6 +1178,7 @@
|
||||
"version": "3.5.17",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.17.tgz",
|
||||
"integrity": "sha512-Xe+AittLbAyV0pabcN7cP7/BenRBNcteM4aSDCtRvGw0d9OL+HG1u/XHLY/kt1q4fyMeZYXyIYrsHuPSiDPosA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.27.5",
|
||||
"@vue/shared": "3.5.17",
|
||||
@@ -1207,6 +1191,7 @@
|
||||
"version": "3.5.17",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.17.tgz",
|
||||
"integrity": "sha512-+2UgfLKoaNLhgfhV5Ihnk6wB4ljyW1/7wUIog2puUqajiC29Lp5R/IKDdkebh9jTbTogTbsgB+OY9cEWzG95JQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@vue/compiler-core": "3.5.17",
|
||||
"@vue/shared": "3.5.17"
|
||||
@@ -1216,6 +1201,8 @@
|
||||
"version": "3.5.17",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.17.tgz",
|
||||
"integrity": "sha512-rQQxbRJMgTqwRugtjw0cnyQv9cP4/4BxWfTdRBkqsTfLOHWykLzbOc3C4GGzAmdMDxhzU/1Ija5bTjMVrddqww==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.27.5",
|
||||
"@vue/compiler-core": "3.5.17",
|
||||
@@ -1232,6 +1219,8 @@
|
||||
"version": "3.5.17",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.17.tgz",
|
||||
"integrity": "sha512-hkDbA0Q20ZzGgpj5uZjb9rBzQtIHLS78mMilwrlpWk2Ep37DYntUz0PonQ6kr113vfOEdM+zTBuJDaceNIW0tQ==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@vue/compiler-dom": "3.5.17",
|
||||
"@vue/shared": "3.5.17"
|
||||
@@ -1313,6 +1302,8 @@
|
||||
"version": "3.5.17",
|
||||
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.17.tgz",
|
||||
"integrity": "sha512-l/rmw2STIscWi7SNJp708FK4Kofs97zc/5aEPQh4bOsReD/8ICuBcEmS7KGwDj5ODQLYWVN2lNibKJL1z5b+Lw==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@vue/shared": "3.5.17"
|
||||
}
|
||||
@@ -1321,6 +1312,8 @@
|
||||
"version": "3.5.17",
|
||||
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.17.tgz",
|
||||
"integrity": "sha512-QQLXa20dHg1R0ri4bjKeGFKEkJA7MMBxrKo2G+gJikmumRS7PTD4BOU9FKrDQWMKowz7frJJGqBffYMgQYS96Q==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@vue/reactivity": "3.5.17",
|
||||
"@vue/shared": "3.5.17"
|
||||
@@ -1330,6 +1323,8 @@
|
||||
"version": "3.5.17",
|
||||
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.17.tgz",
|
||||
"integrity": "sha512-8El0M60TcwZ1QMz4/os2MdlQECgGoVHPuLnQBU3m9h3gdNRW9xRmI8iLS4t/22OQlOE6aJvNNlBiCzPHur4H9g==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@vue/reactivity": "3.5.17",
|
||||
"@vue/runtime-core": "3.5.17",
|
||||
@@ -1341,6 +1336,8 @@
|
||||
"version": "3.5.17",
|
||||
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.17.tgz",
|
||||
"integrity": "sha512-BOHhm8HalujY6lmC3DbqF6uXN/K00uWiEeF22LfEsm9Q93XeJ/plHTepGwf6tqFcF7GA5oGSSAAUock3VvzaCA==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@vue/compiler-ssr": "3.5.17",
|
||||
"@vue/shared": "3.5.17"
|
||||
@@ -1352,7 +1349,8 @@
|
||||
"node_modules/@vue/shared": {
|
||||
"version": "3.5.17",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.17.tgz",
|
||||
"integrity": "sha512-CabR+UN630VnsJO/jHWYBC1YVXyMq94KKp6iF5MQgZJs5I8cmjw6oVMO1oDbtBkENSHSSn/UadWlW/OAgdmKrg=="
|
||||
"integrity": "sha512-CabR+UN630VnsJO/jHWYBC1YVXyMq94KKp6iF5MQgZJs5I8cmjw6oVMO1oDbtBkENSHSSn/UadWlW/OAgdmKrg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@vue/tsconfig": {
|
||||
"version": "0.7.0",
|
||||
@@ -1454,31 +1452,12 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/atob": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
|
||||
"integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
|
||||
"bin": {
|
||||
"atob": "bin/atob.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 4.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/base64-arraybuffer": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
|
||||
"integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==",
|
||||
"engines": {
|
||||
"node": ">= 0.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/boolbase": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
|
||||
@@ -1506,17 +1485,6 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/btoa": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz",
|
||||
"integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==",
|
||||
"bin": {
|
||||
"btoa": "bin/btoa.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/callsites": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
|
||||
@@ -1526,25 +1494,6 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/canvg": {
|
||||
"version": "3.0.11",
|
||||
"resolved": "https://registry.npmjs.org/canvg/-/canvg-3.0.11.tgz",
|
||||
"integrity": "sha512-5ON+q7jCTgMp9cjpu4Jo6XbvfYwSB2Ow3kzHKfIyJfaCAOHLbdKPQqGKgfED/R5B+3TFFfe8pegYA+b423SRyA==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"@types/raf": "^3.4.0",
|
||||
"core-js": "^3.8.3",
|
||||
"raf": "^3.4.1",
|
||||
"regenerator-runtime": "^0.13.7",
|
||||
"rgbcolor": "^1.0.1",
|
||||
"stackblur-canvas": "^2.0.0",
|
||||
"svg-pathdata": "^6.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/chalk": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||
@@ -1585,17 +1534,6 @@
|
||||
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/core-js": {
|
||||
"version": "3.43.0",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.43.0.tgz",
|
||||
"integrity": "sha512-N6wEbTTZSYOY2rYAn85CuvWWkCK6QweMn7/4Nr3w+gDBeBhk/x4EJeY6FPo4QzDoJZxVTv8U7CMvgWk6pOHHqA==",
|
||||
"hasInstallScript": true,
|
||||
"optional": true,
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/core-js"
|
||||
}
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||
@@ -1610,14 +1548,6 @@
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/css-line-break": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz",
|
||||
"integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==",
|
||||
"dependencies": {
|
||||
"utrie": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/cssesc": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
|
||||
@@ -1633,16 +1563,9 @@
|
||||
"node_modules/csstype": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
|
||||
},
|
||||
"node_modules/date-fns": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz",
|
||||
"integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/kossnocorp"
|
||||
}
|
||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
|
||||
"dev": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/de-indent": {
|
||||
"version": "1.0.2",
|
||||
@@ -1697,19 +1620,11 @@
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/dompurify": {
|
||||
"version": "3.2.6",
|
||||
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.6.tgz",
|
||||
"integrity": "sha512-/2GogDQlohXPZe6D6NOgQvXLPSYBqIWMnZ8zzOhn09REE4eyAzb+Hed3jhoM9OkuaJ8P6ZGTTVWQKAi8ieIzfQ==",
|
||||
"optional": true,
|
||||
"optionalDependencies": {
|
||||
"@types/trusted-types": "^2.0.7"
|
||||
}
|
||||
},
|
||||
"node_modules/entities": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
||||
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.12"
|
||||
},
|
||||
@@ -1992,7 +1907,8 @@
|
||||
"node_modules/estree-walker": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
|
||||
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
|
||||
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/esutils": {
|
||||
"version": "2.0.3",
|
||||
@@ -2064,11 +1980,6 @@
|
||||
"reusify": "^1.0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/fflate": {
|
||||
"version": "0.8.2",
|
||||
"resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz",
|
||||
"integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A=="
|
||||
},
|
||||
"node_modules/file-entry-cache": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
|
||||
@@ -2263,18 +2174,6 @@
|
||||
"he": "bin/he"
|
||||
}
|
||||
},
|
||||
"node_modules/html2canvas": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz",
|
||||
"integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==",
|
||||
"dependencies": {
|
||||
"css-line-break": "^2.1.0",
|
||||
"text-segmentation": "^1.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ignore": {
|
||||
"version": "5.3.2",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
|
||||
@@ -2371,20 +2270,6 @@
|
||||
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/jordium-gantt-vue3": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/jordium-gantt-vue3/-/jordium-gantt-vue3-1.0.1.tgz",
|
||||
"integrity": "sha512-LaFzGN6LZnlSGhUxp8sEKK1YCtQX4NoE4V0yKBOIiUhF22ykJjtHXkQnOu5jH/Wtm0WR+WhQC/AOzA+WLl+VUA==",
|
||||
"dependencies": {
|
||||
"date-fns": "^4.1.0",
|
||||
"html2canvas": "^1.4.1",
|
||||
"jspdf": "^3.0.1",
|
||||
"vue": "^3.5.13"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "^3.5.13"
|
||||
}
|
||||
},
|
||||
"node_modules/js-yaml": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
|
||||
@@ -2415,23 +2300,6 @@
|
||||
"integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/jspdf": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/jspdf/-/jspdf-3.0.1.tgz",
|
||||
"integrity": "sha512-qaGIxqxetdoNnFQQXxTKUD9/Z7AloLaw94fFsOiJMxbfYdBbrBuhWmbzI8TVjrw7s3jBY1PFHofBKMV/wZPapg==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.26.7",
|
||||
"atob": "^2.1.2",
|
||||
"btoa": "^1.2.1",
|
||||
"fflate": "^0.8.1"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"canvg": "^3.0.11",
|
||||
"core-js": "^3.6.0",
|
||||
"dompurify": "^3.2.4",
|
||||
"html2canvas": "^1.0.0-rc.5"
|
||||
}
|
||||
},
|
||||
"node_modules/keyv": {
|
||||
"version": "4.5.4",
|
||||
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
|
||||
@@ -2485,6 +2353,8 @@
|
||||
"version": "0.30.17",
|
||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz",
|
||||
"integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/sourcemap-codec": "^1.5.0"
|
||||
}
|
||||
@@ -2542,6 +2412,7 @@
|
||||
"version": "3.3.11",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
|
||||
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
@@ -2683,16 +2554,11 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/performance-now": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
|
||||
"integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/picocolors": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="
|
||||
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/picomatch": {
|
||||
"version": "2.3.1",
|
||||
@@ -2710,6 +2576,7 @@
|
||||
"version": "8.5.6",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
|
||||
"integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
@@ -2811,21 +2678,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/raf": {
|
||||
"version": "3.4.1",
|
||||
"resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz",
|
||||
"integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"performance-now": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/regenerator-runtime": {
|
||||
"version": "0.13.11",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
|
||||
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/resolve-from": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
|
||||
@@ -2845,15 +2697,6 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/rgbcolor": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/rgbcolor/-/rgbcolor-1.0.1.tgz",
|
||||
"integrity": "sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">= 0.8.15"
|
||||
}
|
||||
},
|
||||
"node_modules/rimraf": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
||||
@@ -2978,19 +2821,11 @@
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/stackblur-canvas": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/stackblur-canvas/-/stackblur-canvas-2.7.0.tgz",
|
||||
"integrity": "sha512-yf7OENo23AGJhBriGx0QivY5JP6Y1HbrrDI6WLt6C5auYZXlQrheoY8hD4ibekFKz1HOfE48Ww8kMWMnJD/zcQ==",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=0.1.14"
|
||||
}
|
||||
},
|
||||
"node_modules/strip-ansi": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||
@@ -3027,15 +2862,6 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/svg-pathdata": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/svg-pathdata/-/svg-pathdata-6.0.3.tgz",
|
||||
"integrity": "sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/synckit": {
|
||||
"version": "0.11.8",
|
||||
"resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.8.tgz",
|
||||
@@ -3051,14 +2877,6 @@
|
||||
"url": "https://opencollective.com/synckit"
|
||||
}
|
||||
},
|
||||
"node_modules/text-segmentation": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz",
|
||||
"integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==",
|
||||
"dependencies": {
|
||||
"utrie": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/text-table": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
|
||||
@@ -3159,7 +2977,7 @@
|
||||
"version": "5.8.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
|
||||
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
@@ -3183,14 +3001,6 @@
|
||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/utrie": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz",
|
||||
"integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==",
|
||||
"dependencies": {
|
||||
"base64-arraybuffer": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "6.3.5",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz",
|
||||
@@ -3301,6 +3111,8 @@
|
||||
"version": "3.5.17",
|
||||
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.17.tgz",
|
||||
"integrity": "sha512-LbHV3xPN9BeljML+Xctq4lbz2lVHCR6DtbpTf5XIO6gugpXUN49j2QQPcMj086r9+AkJ0FfUT8xjulKKBkkr9g==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@vue/compiler-dom": "3.5.17",
|
||||
"@vue/compiler-sfc": "3.5.17",
|
||||
|
||||
@@ -19,9 +19,6 @@
|
||||
],
|
||||
"author": "Jordium.com",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"jordium-gantt-vue3": "^1.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
||||
"@typescript-eslint/parser": "^6.21.0",
|
||||
|
||||
@@ -90,5 +90,39 @@
|
||||
"version": "1.0.2",
|
||||
"date": "2025-07-01",
|
||||
"notes": ["升级README文档", "增加贡献说明", "增加贡献者列表"]
|
||||
},
|
||||
{
|
||||
"version": "1.0.3",
|
||||
"date": "2025-07-02",
|
||||
"notes": ["增加Github Pages", "修改README, 添加GIthub在线Demo入口", "移除大体积静态资源"]
|
||||
},
|
||||
{
|
||||
"version": "1.0.4",
|
||||
"date": "2025-07-02",
|
||||
"notes": ["修改NPM徽章CDN"]
|
||||
},
|
||||
{
|
||||
"version": "1.0.5",
|
||||
"date": "2025-07-02",
|
||||
"notes": ["更新README中所有NPM徽章CDN地址"]
|
||||
},
|
||||
{
|
||||
"version": "1.0.6",
|
||||
"date": "2025-07-03",
|
||||
"notes": [
|
||||
"更新TaskDrawer组件的Progressbar样式",
|
||||
"可通过拖拽进度条和文本修改的方式更改任务的进度",
|
||||
"Story进度根据子任务集进度改编重新计算",
|
||||
"同时保留Story进度单独的调整方式"
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "1.0.9",
|
||||
"date": "2025-07-04",
|
||||
"notes": [
|
||||
"更新多语言标签",
|
||||
"增加Story删除时选择是否删除子任务的选项",
|
||||
"删除Story或Task时同时删除前后的关系线路"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -51,7 +51,7 @@ interface Props {
|
||||
// 是否使用默认的TaskDrawer
|
||||
useDefaultDrawer?: boolean
|
||||
// 自定义删除处理器API
|
||||
onTaskDelete?: (task: Task) => void
|
||||
onTaskDelete?: (task: Task, deleteChildren?: boolean) => void
|
||||
// 里程碑保存处理器API
|
||||
onMilestoneSave?: (milestone: Task) => void
|
||||
// 里程碑删除处理器API
|
||||
|
||||
@@ -7,10 +7,15 @@ const props = defineProps({
|
||||
message: { type: String, default: '' },
|
||||
confirmText: { type: String, default: '确认' },
|
||||
cancelText: { type: String, default: '取消' },
|
||||
yesText: { type: String, default: '是' },
|
||||
noText: { type: String, default: '否' },
|
||||
type: { type: String, default: 'confirm-cancel' }, // 'confirm-cancel' | 'yes-no-cancel'
|
||||
})
|
||||
const emit = defineEmits(['confirm', 'cancel'])
|
||||
const emit = defineEmits(['confirm', 'cancel', 'yes', 'no'])
|
||||
const onConfirm = () => emit('confirm')
|
||||
const onCancel = () => emit('cancel')
|
||||
const onYes = () => emit('yes')
|
||||
const onNo = () => emit('no')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -23,12 +28,27 @@ const onCancel = () => emit('cancel')
|
||||
<p>{{ props.message }}</p>
|
||||
</div>
|
||||
<div class="gantt-confirm-footer">
|
||||
<button type="button" class="btn btn-default" @click="onCancel">
|
||||
{{ props.cancelText }}
|
||||
</button>
|
||||
<button type="button" class="btn btn-danger" @click="onConfirm">
|
||||
{{ props.confirmText }}
|
||||
</button>
|
||||
<template v-if="props.type === 'yes-no-cancel'">
|
||||
<button type="button" class="btn btn-default" @click="onCancel">
|
||||
{{ props.cancelText }}
|
||||
</button>
|
||||
<div class="gantt-confirm-footer-right">
|
||||
<button type="button" class="btn btn-warning" @click="onNo">
|
||||
{{ props.noText }}
|
||||
</button>
|
||||
<button type="button" class="btn btn-danger" @click="onYes">
|
||||
{{ props.yesText }}
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<button type="button" class="btn btn-default" @click="onCancel">
|
||||
{{ props.cancelText }}
|
||||
</button>
|
||||
<button type="button" class="btn btn-danger" @click="onConfirm">
|
||||
{{ props.confirmText }}
|
||||
</button>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -73,7 +93,13 @@ const onCancel = () => emit('cancel')
|
||||
}
|
||||
.gantt-confirm-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.gantt-confirm-footer-right {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ const emit = defineEmits<{
|
||||
'update:visible': [value: boolean]
|
||||
submit: [task: Task]
|
||||
close: []
|
||||
delete: [task: Task]
|
||||
delete: [task: Task, deleteChildren?: boolean]
|
||||
}>()
|
||||
|
||||
const { t } = useI18n()
|
||||
@@ -35,6 +35,19 @@ const submitting = ref(false)
|
||||
const isVisible = ref(props.visible)
|
||||
const showDeleteConfirm = ref(false)
|
||||
|
||||
// 根据任务类型确定dialog类型
|
||||
const dialogType = computed(() => {
|
||||
return props.task?.type === 'story' ? 'yes-no-cancel' : 'confirm-cancel'
|
||||
})
|
||||
|
||||
// 根据任务类型确定dialog消息
|
||||
const dialogMessage = computed(() => {
|
||||
if (props.task?.type === 'story') {
|
||||
return t.value.confirmDeleteStory.replace('{name}', props.task?.name || '')
|
||||
}
|
||||
return t.value.confirmDeleteTask.replace('{name}', props.task?.name || '')
|
||||
})
|
||||
|
||||
// 表单数据
|
||||
const formData = reactive<Task>({
|
||||
id: 0, // 默认值,创建时会被重新分配
|
||||
@@ -156,7 +169,7 @@ const handleProgressKeydown = (event: KeyboardEvent) => {
|
||||
]
|
||||
|
||||
// 允许数字
|
||||
const isNumber = /^[0-9]$/.test(key)
|
||||
const isNumber = /^\d$/.test(key)
|
||||
const isAllowedKey = allowedKeys.includes(key)
|
||||
|
||||
// 如果是Ctrl+A、Ctrl+C、Ctrl+V、Ctrl+X等组合键,允许通过
|
||||
@@ -167,7 +180,6 @@ const handleProgressKeydown = (event: KeyboardEvent) => {
|
||||
// 如果不是允许的按键或数字,阻止输入
|
||||
if (!isNumber && !isAllowedKey) {
|
||||
event.preventDefault()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -311,6 +323,39 @@ const cancelDelete = () => {
|
||||
showDeleteConfirm.value = false
|
||||
}
|
||||
|
||||
// Story删除:选择"是" - 删除story及其所有子任务
|
||||
const handleDeleteYes = () => {
|
||||
showDeleteConfirm.value = false
|
||||
if (props.task && props.isEdit) {
|
||||
try {
|
||||
submitting.value = true
|
||||
emit('delete', props.task, true) // 传递true表示删除所有子任务
|
||||
handleClose()
|
||||
} catch (error) {
|
||||
showMessage(t.value.taskDeleteFailed, 'error')
|
||||
} finally {
|
||||
submitting.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Story删除:选择"否" - 仅删除story,保留子任务
|
||||
// Story删除:选择"否" - 仅删除story,保留子任务
|
||||
const handleDeleteNo = () => {
|
||||
showDeleteConfirm.value = false
|
||||
if (props.task && props.isEdit) {
|
||||
try {
|
||||
submitting.value = true
|
||||
emit('delete', props.task, false) // 传递false表示仅删除story
|
||||
handleClose()
|
||||
} catch (error) {
|
||||
showMessage(t.value.taskDeleteFailed, 'error')
|
||||
} finally {
|
||||
submitting.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取任务数据的事件处理器
|
||||
const handleTasksChanged = (event: CustomEvent) => {
|
||||
allTasks.value = event.detail || []
|
||||
@@ -390,7 +435,7 @@ watch(
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="task-assignee">{{ t.assignee }}</label>
|
||||
<select id="task-assignee" v-model="formData.assignee" class="form-select">
|
||||
<option value="">请选择负责人</option>
|
||||
<option value="">{{ t.selectAssignee }}</option>
|
||||
<option value="张三">张三</option>
|
||||
<option value="李四">李四</option>
|
||||
<option value="王五">王五</option>
|
||||
@@ -546,10 +591,15 @@ watch(
|
||||
<GanttConfirmDialog
|
||||
:visible="showDeleteConfirm"
|
||||
:title="t.delete"
|
||||
:message="t.confirmDeleteTask.replace('{name}', task?.name || '')"
|
||||
:type="dialogType"
|
||||
:message="dialogMessage"
|
||||
:confirm-text="t.confirm"
|
||||
:cancel-text="t.cancel"
|
||||
:yes-text="t.storyDeleteYes"
|
||||
:no-text="t.storyDeleteNo"
|
||||
@confirm="confirmDelete"
|
||||
@yes="handleDeleteYes"
|
||||
@no="handleDeleteNo"
|
||||
@cancel="cancelDelete"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -22,7 +22,7 @@ interface Props {
|
||||
// 是否使用默认的TaskDrawer
|
||||
useDefaultDrawer?: boolean
|
||||
// 自定义删除处理器
|
||||
onTaskDelete?: (task: Task) => void
|
||||
onTaskDelete?: (task: Task, deleteChildren?: boolean) => void
|
||||
// 里程碑保存事件处理器
|
||||
onMilestoneSave?: (milestone: Task) => void
|
||||
// 新增:外部传入的时间轴起止
|
||||
@@ -810,12 +810,12 @@ onUnmounted(() => {
|
||||
}
|
||||
})
|
||||
|
||||
const handleTaskDelete = (taskId: number) => {
|
||||
const handleTaskDelete = (taskId: number, deleteChildren?: boolean) => {
|
||||
// 调用父组件传入的删除处理器
|
||||
if (props.onTaskDelete && typeof props.onTaskDelete === 'function') {
|
||||
const taskToDelete = tasks.value.find(task => task.id === taskId)
|
||||
if (taskToDelete) {
|
||||
props.onTaskDelete(taskToDelete)
|
||||
props.onTaskDelete(taskToDelete, deleteChildren)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -833,8 +833,8 @@ const handleTaskDelete = (taskId: number) => {
|
||||
}
|
||||
|
||||
// TaskDrawer删除事件适配器
|
||||
const handleDrawerTaskDelete = (task: Task) => {
|
||||
handleTaskDelete(task.id)
|
||||
const handleDrawerTaskDelete = (task: Task, deleteChildren?: boolean) => {
|
||||
handleTaskDelete(task.id, deleteChildren)
|
||||
}
|
||||
|
||||
// 暴露公共API
|
||||
|
||||
@@ -119,8 +119,31 @@ const messages = {
|
||||
taskUpdateSuccess: '任务更新成功',
|
||||
taskCreateSuccess: '任务创建成功',
|
||||
confirmDeleteTask: '确定要删除任务"{name}"吗?此操作不可撤销。',
|
||||
taskDeleteSuccess: '任务删除成功',
|
||||
taskDeleteFailed: '删除失败,请重试',
|
||||
// Story删除相关
|
||||
confirmDeleteStory: '是否删除该需求{name}及其所有的子任务?该操作不可撤回',
|
||||
storyDeleteYes: '是, 继续',
|
||||
storyDeleteNo: '不,仅删除需求',
|
||||
storyDeleteAllSuccess: '已删除需求【{name}】及其所有子任务',
|
||||
storyDeleteOnlySuccess: '已删除需求【{name}】,{count}个子任务已升级保留',
|
||||
storyNotFound: '未找到要删除的需求,ID:{id}',
|
||||
// 里程碑删除成功
|
||||
milestoneDeleteSuccess: '里程碑删除成功',
|
||||
// 其他删除成功消息
|
||||
taskDeletedSuccess: '已删除任务',
|
||||
// 选择器占位符
|
||||
selectAssignee: '请选择负责人',
|
||||
// 通用消息
|
||||
customCsvExportCalled: '自定义CSV导出被调用',
|
||||
languageSwitchedTo: '语言切换到:{language}',
|
||||
themeSwitchedTo: '主题切换到:{theme}',
|
||||
lightModeText: '明亮模式',
|
||||
darkModeText: '暗黑模式',
|
||||
taskNotFound: '未找到要更新的任务,ID:{id}',
|
||||
newParentTaskNotFound: '未找到新父任务,ID:{parentId},将作为顶级任务添加',
|
||||
inPlaceUpdateFailed: '就地更新失败,未找到任务,ID:{id}',
|
||||
taskToDeleteNotFound: '未找到要删除的任务,ID:{id}',
|
||||
milestoneIconUpdateNotFound: '未找到要更新图标的里程碑,ID:{id}',
|
||||
overtime: '超',
|
||||
overdue: '逾期',
|
||||
days: '天',
|
||||
@@ -238,8 +261,33 @@ const messages = {
|
||||
taskCreateSuccess: 'Task created successfully',
|
||||
confirmDeleteTask:
|
||||
'Are you sure you want to delete task "{name}"? This action cannot be undone.',
|
||||
taskDeleteSuccess: 'Task deleted successfully',
|
||||
taskDeleteFailed: 'Delete failed, please try again',
|
||||
// Story deletion related
|
||||
confirmDeleteStory:
|
||||
'Do you want to delete the requirement {name} and all its subtasks? This action cannot be undone',
|
||||
storyDeleteYes: 'Yes, Continue',
|
||||
storyDeleteNo: 'No, Delete Requirement Only',
|
||||
storyDeleteAllSuccess: 'Deleted requirement [{name}] and all its subtasks',
|
||||
storyDeleteOnlySuccess: 'Deleted requirement [{name}], {count} subtasks have been promoted',
|
||||
storyNotFound: 'Requirement not found, ID: {id}',
|
||||
// Milestone deletion success
|
||||
milestoneDeleteSuccess: 'Milestone deleted successfully',
|
||||
// Other deletion success messages
|
||||
taskDeletedSuccess: 'Task deleted',
|
||||
// Selector placeholders
|
||||
selectAssignee: 'Select assignee',
|
||||
// Common messages
|
||||
customCsvExportCalled: 'Custom CSV export called',
|
||||
languageSwitchedTo: 'Language switched to: {language}',
|
||||
themeSwitchedTo: 'Theme switched to: {theme}',
|
||||
lightModeText: 'Light Mode',
|
||||
darkModeText: 'Dark Mode',
|
||||
taskNotFound: 'Task not found for update, ID: {id}',
|
||||
newParentTaskNotFound:
|
||||
'New parent task not found, ID: {parentId}, will be added as top-level task',
|
||||
inPlaceUpdateFailed: 'In-place update failed, task not found, ID: {id}',
|
||||
taskToDeleteNotFound: 'Task to delete not found, ID: {id}',
|
||||
milestoneIconUpdateNotFound: 'Milestone not found for icon update, ID: {id}',
|
||||
overtime: 'Over',
|
||||
overdue: 'Overdue',
|
||||
days: ' days',
|
||||
@@ -282,6 +330,15 @@ export function useI18n() {
|
||||
return typeof translation === 'string' ? translation : key
|
||||
}
|
||||
|
||||
// 格式化带参数的翻译文本
|
||||
const formatTranslation = (key: string, params: Record<string, string | number>): string => {
|
||||
let text = getTranslation(key)
|
||||
Object.keys(params).forEach(param => {
|
||||
text = text.replace(new RegExp(`{${param}}`, 'g'), String(params[param]))
|
||||
})
|
||||
return text
|
||||
}
|
||||
|
||||
// 切换语言
|
||||
const setLocale = (locale: Locale) => {
|
||||
currentLocale.value = locale
|
||||
@@ -305,6 +362,7 @@ export function useI18n() {
|
||||
return {
|
||||
t,
|
||||
getTranslation,
|
||||
formatTranslation,
|
||||
locale,
|
||||
setLocale,
|
||||
formatYearMonth,
|
||||
|
||||
@@ -65,3 +65,14 @@
|
||||
background: #f78989;
|
||||
border-color: #f78989;
|
||||
}
|
||||
|
||||
/* 通用 .btn-warning 按钮样式 */
|
||||
.btn-warning {
|
||||
background: var(--gantt-warning, #e6a23c);
|
||||
border-color: var(--gantt-warning, #e6a23c);
|
||||
color: #fff;
|
||||
}
|
||||
.btn-warning:hover:not(:disabled) {
|
||||
background: #ebb563;
|
||||
border-color: #ebb563;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user