Merge - merge updates from gitmigor fork

This commit is contained in:
LINING-PC\lining
2025-08-12 22:38:30 +08:00
parent 63f03ec9d8
commit 221f20e322
6 changed files with 166 additions and 50 deletions
+10 -6
View File
@@ -1,5 +1,7 @@
<script setup lang="ts">
import { ref, computed, watch, onMounted, onUnmounted } from 'vue'
import { useI18n } from '../composables/useI18n'
const { t } = useI18n()
interface Props {
modelValue?: string | [string, string]
@@ -97,7 +99,7 @@ watch(
singleValue.value = (newValue as string) || ''
}
},
{ immediate: true },
{ immediate: true }
)
// 处理单日期输入变化(预留,当前版本不使用)
@@ -464,6 +466,7 @@ const yearList = computed(() => {
return years
})
/*
// 月份名称
const monthNames = [
'一月',
@@ -480,6 +483,7 @@ const monthNames = [
'十二月',
]
const weekDays = ['日', '一', '二', '三', '四', '五', '六']
*/
// 生命周期
onMounted(() => {
@@ -736,7 +740,7 @@ const panelStyle = computed(() => {
&lt;&lt;
</button>
<span class="el-month-picker__header-label" @click="showYearSelector($event)">
{{ currentYear }}
{{ currentYear }}
</span>
<button type="button" class="el-picker-panel__icon-btn" @click="nextYear">
&gt;&gt;
@@ -744,7 +748,7 @@ const panelStyle = computed(() => {
</div>
<div class="el-month-picker__content">
<div
v-for="(monthName, index) in monthNames"
v-for="(monthName, index) in t.monthNames"
:key="index"
class="el-month-picker__item"
:class="{ 'is-current': index === currentMonth }"
@@ -766,10 +770,10 @@ const panelStyle = computed(() => {
</button>
<span class="el-date-picker__header-label">
<span class="el-date-picker__header-year" @click="showYearSelector($event)">
{{ currentYear }}
{{ currentYear }}
</span>
<span class="el-date-picker__header-month" @click="showMonthSelector($event)">
{{ monthNames[currentMonth] }}
{{ t.monthNames[currentMonth] }}
</span>
</span>
<button type="button" class="el-picker-panel__icon-btn" @click="nextMonth">
@@ -783,7 +787,7 @@ const panelStyle = computed(() => {
<div class="el-date-picker__content">
<!-- 星期标题 -->
<div class="el-date-table__header">
<div v-for="day in weekDays" :key="day" class="el-date-table__header-cell">
<div v-for="day in t.weekDays" :key="day" class="el-date-table__header-cell">
{{ day }}
</div>
</div>
+19 -13
View File
@@ -2,6 +2,12 @@
import { computed, ref, onUnmounted } from 'vue'
import type { Milestone } from '../models/classes/Milestone'
import { TimelineScale } from '../models/types/TimelineScale'
import { useI18n } from '../composables/useI18n'
const { getTranslation } = useI18n()
const t = (key: string): string => {
return getTranslation(key)
}
interface Props {
date: string // 里程碑日期
@@ -151,7 +157,7 @@ const handleMouseMove = (e: MouseEvent) => {
mouseX: e.clientX,
isDragging: isDragging.value,
},
}),
})
)
const deltaX = e.clientX - dragStartX.value
@@ -178,7 +184,7 @@ const handleMouseUp = () => {
mouseX: 0,
isDragging: false,
},
}),
})
)
// 只有在真正拖拽了(有临时数据)且状态为拖拽中时才触发更新
@@ -240,7 +246,7 @@ const handleMilestoneClick = (e: MouseEvent) => {
scrollLeft: targetScrollLeft,
smooth: true,
},
}),
})
)
}
}
@@ -274,13 +280,13 @@ const milestoneStyle = computed(() => {
const centerPosition = calculateMilestonePositionFromTimelineData(
milestoneDate,
props.timelineData,
props.currentTimeScale,
props.currentTimeScale
)
left = centerPosition - size / 2 // 从中心位置偏移到图标左上角
} else {
// 日视图:保持原有逻辑
const startDiff = Math.floor(
(milestoneDate.getTime() - props.startDate.getTime()) / (1000 * 60 * 60 * 24),
(milestoneDate.getTime() - props.startDate.getTime()) / (1000 * 60 * 60 * 24)
)
left = startDiff * props.dayWidth + props.dayWidth / 2 - size / 2
}
@@ -346,7 +352,7 @@ const milestoneVisibility = computed(() => {
if (iconRight <= leftBoundary + iconSize / 2) {
// 检查左侧是否有其他停靠的里程碑,需要判断推挤优先级
const leftStickyMilestones = otherMilestones.filter(
m => m.id !== currentId && m.stickyPosition === 'left' && m.isSticky,
m => m.id !== currentId && m.stickyPosition === 'left' && m.isSticky
)
// 如果有其他里程碑已经停靠在左侧,比较优先级决定推挤顺序
@@ -392,7 +398,7 @@ const milestoneVisibility = computed(() => {
if (iconLeft >= rightBoundary - iconSize / 2) {
// 检查右侧是否有其他停靠的里程碑,需要判断推挤优先级
const rightStickyMilestones = otherMilestones.filter(
m => m.id !== currentId && m.stickyPosition === 'right' && m.isSticky,
m => m.id !== currentId && m.stickyPosition === 'right' && m.isSticky
)
// 如果有其他里程碑已经停靠在右侧,比较优先级决定推挤顺序
@@ -478,26 +484,26 @@ const handleMilestoneMouseLeave = () => {
// 格式化日期显示
const formatDisplayDate = (dateStr: string): string => {
if (!dateStr) return '未设置'
if (!dateStr) return t('dateNotSet') //Not Set
try {
const date = new Date(dateStr)
if (isNaN(date.getTime())) return '未设置'
if (isNaN(date.getTime())) return t('dateNotSet')
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
return `${year}-${month}-${day}`
} catch {
return '未设置'
return t('dateNotSet')
}
}
// Tooltip内容
const tooltipContent = computed(() => {
const milestoneName = props.name || props.milestone?.name || '里程碑'
const milestoneName = props.name || props.milestone?.name || t('milestone')
const targetDate = formatDisplayDate(props.date || props.milestone?.startDate || '')
return `里程碑:${milestoneName} - 目标日期${targetDate}`
return `${t('milestone')}${milestoneName} <br> ${t('targetDate')}${targetDate}`
})
// 组件销毁时清理事件监听器
@@ -527,7 +533,7 @@ const calculateMilestonePositionFromTimelineData = (
subDays: Array<{ date: Date; dayOfWeek?: number }>
}>
}>,
timeScale: TimelineScale,
timeScale: TimelineScale
) => {
let cumulativePosition = 0
+14 -7
View File
@@ -4,6 +4,8 @@ import type { Task } from '../models/classes/Task'
import { TimelineScale } from '../models/types/TimelineScale'
import TaskContextMenu from './TaskContextMenu.vue'
import { useI18n } from '../composables/useI18n'
interface Props {
task: Task
rowHeight: number
@@ -31,6 +33,11 @@ interface Props {
const props = defineProps<Props>()
const { getTranslation } = useI18n()
const t = (key: string): string => {
return getTranslation(key)
}
const emit = defineEmits([
'update:task',
'bar-mounted',
@@ -833,9 +840,9 @@ const handleBubbleMouseDown = (event: MouseEvent) => {
// 格式化日期显示
const formatDisplayDate = (dateStr: string | undefined): string => {
if (!dateStr) return '未设置'
if (!dateStr) return t('dateNotSet')
const date = createLocalDate(dateStr)
if (!date) return '未设置'
if (!date) return t('dateNotSet')
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
@@ -1120,23 +1127,23 @@ onUnmounted(() => {
<div class="tooltip-title">{{ task.name }}</div>
<div class="tooltip-content">
<div class="tooltip-row">
<span class="tooltip-label">计划开始:</span>
<span class="tooltip-label"> {{ t('startDate') }}:</span>
<span class="tooltip-value">{{ formatDisplayDate(task.startDate) }}</span>
</div>
<div class="tooltip-row">
<span class="tooltip-label">计划结束:</span>
<span class="tooltip-label">{{ t('endDate') }}:</span>
<span class="tooltip-value">{{ formatDisplayDate(task.endDate) }}</span>
</div>
<div class="tooltip-row">
<span class="tooltip-label">计划工时:</span>
<span class="tooltip-label">{{ t('estimatedHours') }}:</span>
<span class="tooltip-value">{{ workHourInfo.total }}h</span>
</div>
<div class="tooltip-row">
<span class="tooltip-label">已用工时:</span>
<span class="tooltip-label"> {{ t('actualHours') }}:</span>
<span class="tooltip-value">{{ workHourInfo.used }}h</span>
</div>
<div class="tooltip-row">
<span class="tooltip-label">完成率:</span>
<span class="tooltip-label"> {{ t('progress') }}:</span>
<span class="tooltip-value">{{ task.progress || 0 }}%</span>
</div>
</div>
+23 -23
View File
@@ -235,7 +235,7 @@ const computeAllMilestonesPositions = () => {
if (!isNaN(milestoneDate.getTime())) {
const startDiff = Math.floor(
(milestoneDate.getTime() - timelineConfig.value.startDate.getTime()) /
(1000 * 60 * 60 * 24),
(1000 * 60 * 60 * 24)
)
const left = startDiff * 30 + 30 / 2 - 12 // 30dayWidth12
@@ -269,7 +269,7 @@ const computeAllMilestonesPositions = () => {
if (!isNaN(milestoneDate.getTime())) {
const startDiff = Math.floor(
(milestoneDate.getTime() - timelineConfig.value.startDate.getTime()) /
(1000 * 60 * 60 * 24),
(1000 * 60 * 60 * 24)
)
const left = startDiff * 30 + 30 / 2 - 12
@@ -372,7 +372,7 @@ const handleTaskRowHover = (taskId: number | null) => {
window.dispatchEvent(
new CustomEvent('timeline-task-hover', {
detail: taskId,
}),
})
)
}
@@ -424,7 +424,7 @@ const handleMilestoneIconChange = (milestoneId: number, icon: string) => {
window.dispatchEvent(
new CustomEvent('milestone-icon-changed', {
detail: { milestoneId, icon },
}),
})
)
}
@@ -442,7 +442,7 @@ const handleMilestoneSave = (updatedMilestone: Milestone) => {
window.dispatchEvent(
new CustomEvent('milestone-data-updated', {
detail: { milestone: updatedMilestone },
}),
})
)
}
@@ -455,14 +455,14 @@ const handleMilestoneDelete = (milestoneId: number) => {
window.dispatchEvent(
new CustomEvent('milestone-deleted', {
detail: { milestoneId },
}),
})
)
// 广Timeline
window.dispatchEvent(
new CustomEvent('milestone-data-changed', {
detail: { milestoneId },
}),
})
)
}
@@ -477,7 +477,7 @@ const handleMilestoneUpdate = (updatedMilestone: Milestone) => {
window.dispatchEvent(
new CustomEvent('milestone-data-updated', {
detail: { milestone: updatedMilestone },
}),
})
)
}
@@ -683,7 +683,7 @@ watch(
if (!isUpdatingTimelineConfig) {
timelineData.value = generateTimelineData()
}
},
}
)
// props/
@@ -698,7 +698,7 @@ watch(
})
}
},
{ deep: true },
{ deep: true }
)
// 线
@@ -715,7 +715,7 @@ const scrollToTodayCenter = (retry = 0) => {
const startNormalized = new Date(
timelineStart.getFullYear(),
timelineStart.getMonth(),
timelineStart.getDate(),
timelineStart.getDate()
)
// 线
@@ -806,7 +806,7 @@ const scrollToToday = () => {
const startNormalized = new Date(
timelineStart.getFullYear(),
timelineStart.getMonth(),
timelineStart.getDate(),
timelineStart.getDate()
)
// 线
@@ -857,7 +857,7 @@ const updateTask = (updatedTask: Task) => {
window.dispatchEvent(
new CustomEvent('task-updated', {
detail: updatedTask,
}),
})
)
}
@@ -917,7 +917,7 @@ const handleTaskBarContextMenu = (event: { task: Task; position: { x: number; y:
window.dispatchEvent(
new CustomEvent('context-menu', {
detail: event,
}),
})
)
}
@@ -1000,7 +1000,7 @@ onMounted(() => {
// TaskList
window.addEventListener(
'task-list-vertical-scroll',
handleTaskListVerticalScroll as EventListener,
handleTaskListVerticalScroll as EventListener
)
//
window.addEventListener('locale-changed', handleLocaleChange as EventListener)
@@ -1010,7 +1010,7 @@ onMounted(() => {
// TimelineresizeTaskList
window.addEventListener(
'timeline-container-resized',
handleTimelineContainerResized as EventListener,
handleTimelineContainerResized as EventListener
)
//
@@ -1100,7 +1100,7 @@ const handleTimelineBodyScroll = (event: Event) => {
window.dispatchEvent(
new CustomEvent('timeline-vertical-scroll', {
detail: { scrollTop },
}),
})
)
}
}
@@ -1113,7 +1113,7 @@ watch(
updateSvgSize()
})
},
{ immediate: true },
{ immediate: true }
)
//
@@ -1281,7 +1281,7 @@ const startAutoScroll = (direction: 'left' | 'right') => {
window.dispatchEvent(
new CustomEvent('timeline-auto-scroll', {
detail: { scrollDelta: newScrollLeft - currentScrollLeft },
}),
})
)
autoScrollTimer = window.setTimeout(scroll, 16) // 60fps
@@ -1335,14 +1335,14 @@ onUnmounted(() => {
window.removeEventListener('task-list-hover', handleTaskListHover as EventListener)
window.removeEventListener(
'task-list-vertical-scroll',
handleTaskListVerticalScroll as EventListener,
handleTaskListVerticalScroll as EventListener
)
window.removeEventListener('locale-changed', handleLocaleChange as EventListener)
window.removeEventListener('splitter-drag-start', handleSplitterDragStart as EventListener)
window.removeEventListener('splitter-drag-end', handleSplitterDragEnd as EventListener)
window.removeEventListener(
'timeline-container-resized',
handleTimelineContainerResized as EventListener,
handleTimelineContainerResized as EventListener
)
window.removeEventListener('milestone-click-locate', handleMilestoneClickLocate as EventListener)
window.removeEventListener('drag-boundary-check', handleDragBoundaryCheck as EventListener)
@@ -1436,7 +1436,7 @@ watch(
}
})
},
{ deep: true },
{ deep: true }
)
//
@@ -1620,7 +1620,7 @@ const handleAddSuccessor = (task: Task) => {
class="timeline-year"
:style="{ width: '719px' }"
>
<div class="year-label">{{ yearValue }}</div>
<div class="year-label">{{ yearValue }}</div>
</div>
</div>