v1.4.0 - new features

This commit is contained in:
LINING-PC\lining
2025-10-11 18:57:38 +08:00
parent 811477852a
commit 0c317b1450
27 changed files with 159 additions and 5459 deletions
+3 -2
View File
@@ -126,7 +126,7 @@ const leftPanelWidth = ref(props.taskListConfig?.defaultWidth || 320)
// 监听taskListConfig变化,更新相关配置
watch(
() => props.taskListConfig,
(newConfig) => {
newConfig => {
if (newConfig) {
// 更新默认宽度
if (newConfig.defaultWidth !== undefined) {
@@ -607,7 +607,8 @@ const tasksForTaskList = computed(() => {
// 不排序:直接使用原始任务数据
result.push(...props.tasks)
}
} return result
}
return result
})
// 为Timeline提供正确的扁平化数据
+36 -31
View File
@@ -42,6 +42,8 @@ interface TaskBarSlotProps {
currentTimeScale?: TimelineScale
rowHeight: number
dayWidth: number
// 新增:动态样式对象
dynamicStyles: Record<string, string>
}
const props = defineProps<Props>()
@@ -165,6 +167,9 @@ const quarterDragOverride = ref<{
const barRef = ref<HTMLElement | null>(null)
const taskBarNameRef = ref<HTMLElement | null>(null)
const nameTextWidth = ref(0)
// 计算任务条位置和宽度
const taskBarStyle = computed(() => {
// 季度视图拖拽时使用位置覆盖
@@ -390,6 +395,7 @@ const slotPayload = computed(() => ({
currentTimeScale: props.currentTimeScale,
rowHeight: props.rowHeight,
dayWidth: props.dayWidth,
dynamicStyles: getNameStyles(),
}))
// 判断是否已完成
@@ -506,7 +512,7 @@ const handleMouseMove = (e: MouseEvent) => {
dragTooltipVisible.value = true
dragTooltipPosition.value = {
x: e.clientX + 15, // 鼠标右侧偏移
y: e.clientY - 60, // 鼠标上方偏移
y: e.clientY - 60, // 鼠标上方偏移
}
}
@@ -569,9 +575,9 @@ const handleMouseMove = (e: MouseEvent) => {
}
// 同时计算日期用于提示框(使用与Timeline相同的季度视图计算)
const quarterWidth = 60 // 与Timeline.vue保持一致
const daysInQuarter = 90 // 季度平均天数
const pixelsPerDay = quarterWidth / daysInQuarter // 约0.67px/天
const quarterWidth = 60 // 与Timeline.vue保持一致
const daysInQuarter = 90 // 季度平均天数
const pixelsPerDay = quarterWidth / daysInQuarter // 约0.67px/天
const dayOffset = Math.round(deltaX / pixelsPerDay)
// 基于原始任务日期计算新日期
@@ -659,9 +665,9 @@ const handleMouseMove = (e: MouseEvent) => {
}
// 计算日期用于提示框(使用与Timeline相同的季度视图计算)
const quarterWidth = 60 // 与Timeline.vue保持一致
const daysInQuarter = 90 // 季度平均天数
const pixelsPerDay = quarterWidth / daysInQuarter // 约0.67px/天
const quarterWidth = 60 // 与Timeline.vue保持一致
const daysInQuarter = 90 // 季度平均天数
const pixelsPerDay = quarterWidth / daysInQuarter // 约0.67px/天
const dayOffset = Math.round(deltaX / pixelsPerDay)
const originalStartDate = createLocalDate(props.task.startDate) || props.startDate
@@ -737,9 +743,9 @@ const handleMouseMove = (e: MouseEvent) => {
}
// 计算日期用于提示框(使用与Timeline相同的季度视图计算)
const quarterWidth = 60 // 与Timeline.vue保持一致
const daysInQuarter = 90 // 季度平均天数
const pixelsPerDay = quarterWidth / daysInQuarter // 约0.67px/天
const quarterWidth = 60 // 与Timeline.vue保持一致
const daysInQuarter = 90 // 季度平均天数
const pixelsPerDay = quarterWidth / daysInQuarter // 约0.67px/天
const dayOffset = Math.round(deltaX / pixelsPerDay)
const originalEndDate = createLocalDate(props.task.endDate) || props.startDate
@@ -835,6 +841,9 @@ const handleMouseUp = () => {
onMounted(() => {
nextTick(() => {
reportBarPosition()
if (taskBarNameRef.value) {
nameTextWidth.value = taskBarNameRef.value.getBoundingClientRect().width
}
})
// 监听时间刻度变化事件,重新计算位置
@@ -936,8 +945,7 @@ const stickyStyles = computed(() => {
let progressTop = ''
// 估算文字内容的实际位置
const nameText = props.task.name || ''
const nameWidth = Math.max(nameText.length * 7, 40)
const nameWidth = Math.max(nameTextWidth.value, 40) // 最小40px
const progressWidth = 35
// 计算名称和进度在默认居中状态下的位置
@@ -959,12 +967,12 @@ const stickyStyles = computed(() => {
const offset = leftBoundary - taskLeft
nameLeft = `${offset + 8}px`
namePosition = 'absolute'
nameTop = '6px'
nameTop = '2px'
} else if (nameNeedsRightSticky) {
const offset = rightBoundary - taskLeft - nameWidth
nameLeft = `${offset - 8}px`
namePosition = 'absolute'
nameTop = '6px'
nameTop = '2px'
}
// 进度左侧边界粘性逻辑
@@ -980,12 +988,12 @@ const stickyStyles = computed(() => {
const offset = leftBoundary - taskLeft
progressLeft = `${offset + 8}px`
progressPosition = 'absolute'
progressTop = '24px'
progressTop = '18px'
} else if (progressNeedsRightSticky) {
const offset = rightBoundary - taskLeft - progressWidth
progressLeft = `${offset - 8}px`
progressPosition = 'absolute'
progressTop = '24px'
progressTop = '18px'
}
return {
@@ -1529,14 +1537,8 @@ onUnmounted(() => {
>
<!-- 父级任务的标签 -->
<div v-if="isParent" class="parent-label">
<slot
v-if="hasContentSlot"
name="custom-task-content"
v-bind="slotPayload"
/>
<template v-else>
{{ task.name }} ({{ task.progress || 0 }}%)
</template>
<slot v-if="hasContentSlot" name="custom-task-content" v-bind="slotPayload" />
<template v-else> {{ task.name }} ({{ task.progress || 0 }}%) </template>
</div>
<!-- 完成进度条非父级任务 -->
@@ -1564,13 +1566,16 @@ onUnmounted(() => {
@mousedown="e => (isInteractionDisabled ? null : handleMouseDown(e, 'drag'))"
>
<!-- 任务名称 -->
<slot
v-if="hasContentSlot"
name="custom-task-content"
v-bind="slotPayload"
/>
<div v-else class="task-name" :style="getNameStyles()">
{{ task.name }}
<div ref="taskBarNameRef">
<slot
v-if="hasContentSlot"
name="custom-task-content"
v-bind="slotPayload"
:style="getNameStyles()"
/>
<div v-else class="task-name" :style="getNameStyles()">
{{ task.name }}
</div>
</div>
<!-- 进度百分比 -->
+2 -2
View File
@@ -313,7 +313,7 @@ const handleTaskListScroll = (event: Event) => {
detail: { scrollTop },
}),
)
}// 处理Timeline垂直滚动同步
} // 处理Timeline垂直滚动同步
const handleTimelineVerticalScroll = (event: CustomEvent) => {
const { scrollTop } = event.detail
const taskListBodyElement = document.querySelector('.task-list-body') as HTMLElement
@@ -406,7 +406,7 @@ onUnmounted(() => {
:class="column.cssClass || `col-${column.key}`"
:style="column.width ? { width: column.width + 'px' } : undefined"
>
{{ column.label || (t as any)[column.key] }}
{{ (t as any)[column.key] || column.label }}
</div>
</div>
<div class="task-list-body" @scroll="handleTaskListScroll">
+9 -14
View File
@@ -67,7 +67,7 @@ const isStoryTask = computed(() => props.task.type === 'story')
const isMilestoneGroup = computed(() => props.task.type === 'milestone-group')
const isMilestoneTask = computed(() => props.task.type === 'milestone')
const isParentTask = computed(
() => isStoryTask.value || hasChildren.value || isMilestoneGroup.value,
() => isStoryTask.value || hasChildren.value || isMilestoneGroup.value
)
function handleToggle() {
emit('toggle', props.task)
@@ -75,10 +75,7 @@ function handleToggle() {
function handleRowClick() {
// 如果是普通父级任务(story类型或有子任务的任务,非里程碑分组),点击行也可以展开/收起
if (
(isStoryTask.value || hasChildren.value) &&
!isMilestoneGroup.value
) {
if ((isStoryTask.value || hasChildren.value) && !isMilestoneGroup.value) {
emit('toggle', props.task)
}
}
@@ -224,7 +221,7 @@ watch(
updateTimer()
}
},
{ immediate: true },
{ immediate: true }
)
// 右键菜单相关状态
@@ -308,7 +305,7 @@ onUnmounted(() => {
'milestone-group-row': isMilestoneGroup,
'task-type-story': isStoryTask,
'task-type-task': props.task.type === 'task',
'task-type-milestone': isMilestoneTask
'task-type-milestone': isMilestoneTask,
}"
@click="handleRowClick"
@dblclick="handleTaskRowDoubleClick"
@@ -318,7 +315,7 @@ onUnmounted(() => {
>
<div class="col col-name" :style="{ paddingLeft: indent }">
<span
v-if="(isStoryTask || hasChildren) && !isMilestoneGroup"
v-if="(isStoryTask || hasChildren) && !isMilestoneGroup"
class="collapse-btn"
@click.stop="handleToggle"
>
@@ -470,9 +467,7 @@ onUnmounted(() => {
</template>
</div>
</div>
<template
v-if="hasChildren && !props.task.collapsed && !isMilestoneGroup"
>
<template v-if="hasChildren && !props.task.collapsed && !isMilestoneGroup">
<TaskRow
v-for="child in props.task.children"
:key="child.id"
@@ -492,7 +487,7 @@ onUnmounted(() => {
@delete="handleTaskDelete"
>
<template v-if="hasContentSlot" #custom-task-content="slotProps">
<slot name="custom-task-content" v-bind="(slotProps as TaskRowSlotProps)" />
<slot name="custom-task-content" v-bind="slotProps as TaskRowSlotProps" />
</template>
</TaskRow>
</template>
@@ -696,8 +691,8 @@ onUnmounted(() => {
}
.avatar {
width: 24px;
height: 24px;
min-width: 25px;
min-height: 25px;
border-radius: 50%;
background: var(--gantt-primary);
color: var(--gantt-text-white);
+9 -5
View File
@@ -2419,8 +2419,10 @@ const handleAddSuccessor = (task: Task) => {
v-if="subDay.date && subDay.date.getDate() === 1"
class="month-first-flag"
:style="{
left: `${getGlobalWeekPosition(monthIndex, weekIndex) + dayIndex * (60/7)}px`,
transform: 'translateX(-50%)' // 使旗帜中心(杆子)对齐日期位置
left: `${
getGlobalWeekPosition(monthIndex, weekIndex) + dayIndex * (60 / 7)
}px`,
transform: 'translateX(-50%)', // 使旗帜中心(杆子)对齐日期位置
}"
>
<div class="flag-pole"></div>
@@ -2551,8 +2553,10 @@ const handleAddSuccessor = (task: Task) => {
v-if="subDay.date && subDay.date.getDate() === 1"
class="month-first-vertical-line"
:style="{
left: `${getGlobalWeekPosition(monthIndex, weekIndex) + dayIndex * (60/7)}px`,
height: `${contentHeight}px`
left: `${
getGlobalWeekPosition(monthIndex, weekIndex) + dayIndex * (60 / 7)
}px`,
height: `${contentHeight}px`,
}"
></div>
</template>
@@ -2691,7 +2695,7 @@ const handleAddSuccessor = (task: Task) => {
class="sub-day-column"
:class="{
weekend: subDay.dayOfWeek === 0 || subDay.dayOfWeek === 6,
today: isToday(subDay.date)
today: isToday(subDay.date),
}"
:style="{ height: `${contentHeight}px`, width: '8.57px' }"
></div>