统一组件slot的使用方式
This commit is contained in:
+41
-21
@@ -25,6 +25,25 @@ interface Props {
|
|||||||
currentTimeScale?: TimelineScale
|
currentTimeScale?: TimelineScale
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface TaskStatus {
|
||||||
|
type: string
|
||||||
|
color: string
|
||||||
|
bgColor: string
|
||||||
|
borderColor: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TaskBarSlotProps {
|
||||||
|
type: string
|
||||||
|
task: Task
|
||||||
|
status: TaskStatus
|
||||||
|
statusType: string
|
||||||
|
isParent?: boolean
|
||||||
|
progress: number
|
||||||
|
currentTimeScale?: TimelineScale
|
||||||
|
rowHeight: number
|
||||||
|
dayWidth: number
|
||||||
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>()
|
||||||
|
|
||||||
const emit = defineEmits([
|
const emit = defineEmits([
|
||||||
@@ -42,13 +61,17 @@ const emit = defineEmits([
|
|||||||
'context-menu',
|
'context-menu',
|
||||||
])
|
])
|
||||||
|
|
||||||
|
defineSlots<{
|
||||||
|
'custom-task-content'(props: TaskBarSlotProps): unknown
|
||||||
|
}>()
|
||||||
|
|
||||||
const slots = useSlots()
|
const slots = useSlots()
|
||||||
const { getTranslation } = useI18n()
|
const { getTranslation } = useI18n()
|
||||||
const t = (key: string): string => {
|
const t = (key: string): string => {
|
||||||
return getTranslation(key)
|
return getTranslation(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasContentSlot = computed(() => Boolean(slots.content))
|
const hasContentSlot = computed(() => Boolean(slots['custom-task-content']))
|
||||||
|
|
||||||
// 日期工具函数 - 处理时区安全的日期创建和操作
|
// 日期工具函数 - 处理时区安全的日期创建和操作
|
||||||
const createLocalDate = (dateString: string | Date | undefined | null): Date | null => {
|
const createLocalDate = (dateString: string | Date | undefined | null): Date | null => {
|
||||||
@@ -340,6 +363,19 @@ const taskStatus = computed(() => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Slot payload for content slot - 使用 v-bind 方式传递所有属性
|
||||||
|
const slotPayload = computed(() => ({
|
||||||
|
type: 'task-bar',
|
||||||
|
task: props.task,
|
||||||
|
status: taskStatus.value,
|
||||||
|
statusType: taskStatus.value.type,
|
||||||
|
isParent: props.isParent ?? false,
|
||||||
|
progress: props.task.progress || 0,
|
||||||
|
currentTimeScale: props.currentTimeScale,
|
||||||
|
rowHeight: props.rowHeight,
|
||||||
|
dayWidth: props.dayWidth,
|
||||||
|
}))
|
||||||
|
|
||||||
// 判断是否已完成
|
// 判断是否已完成
|
||||||
const isCompleted = computed(() => (props.task.progress || 0) >= 100)
|
const isCompleted = computed(() => (props.task.progress || 0) >= 100)
|
||||||
|
|
||||||
@@ -1320,16 +1356,8 @@ onUnmounted(() => {
|
|||||||
<div v-if="isParent" class="parent-label">
|
<div v-if="isParent" class="parent-label">
|
||||||
<slot
|
<slot
|
||||||
v-if="hasContentSlot"
|
v-if="hasContentSlot"
|
||||||
name="content"
|
name="custom-task-content"
|
||||||
type="task-bar"
|
v-bind="slotPayload"
|
||||||
:task="task"
|
|
||||||
:status="taskStatus"
|
|
||||||
:status-type="taskStatus.type"
|
|
||||||
:is-parent="isParent"
|
|
||||||
:progress="task.progress || 0"
|
|
||||||
:current-time-scale="currentTimeScale"
|
|
||||||
:row-height="rowHeight"
|
|
||||||
:day-width="dayWidth"
|
|
||||||
/>
|
/>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
{{ task.name }} ({{ task.progress || 0 }}%)
|
{{ task.name }} ({{ task.progress || 0 }}%)
|
||||||
@@ -1363,16 +1391,8 @@ onUnmounted(() => {
|
|||||||
<!-- 任务名称 -->
|
<!-- 任务名称 -->
|
||||||
<slot
|
<slot
|
||||||
v-if="hasContentSlot"
|
v-if="hasContentSlot"
|
||||||
name="content"
|
name="custom-task-content"
|
||||||
type="task-bar"
|
v-bind="slotPayload"
|
||||||
:task="task"
|
|
||||||
:status="taskStatus"
|
|
||||||
:status-type="taskStatus.type"
|
|
||||||
:is-parent="isParent"
|
|
||||||
:progress="task.progress || 0"
|
|
||||||
:current-time-scale="currentTimeScale"
|
|
||||||
:row-height="rowHeight"
|
|
||||||
:day-width="dayWidth"
|
|
||||||
/>
|
/>
|
||||||
<div v-else class="task-name" :style="getNameStyles()">
|
<div v-else class="task-name" :style="getNameStyles()">
|
||||||
{{ task.name }}
|
{{ task.name }}
|
||||||
|
|||||||
@@ -6,6 +6,28 @@ import type { Task } from '../models/classes/Task'
|
|||||||
import type { TaskListColumnConfig } from '../models/configs/TaskListConfig'
|
import type { TaskListColumnConfig } from '../models/configs/TaskListConfig'
|
||||||
import TaskContextMenu from './TaskContextMenu.vue'
|
import TaskContextMenu from './TaskContextMenu.vue'
|
||||||
|
|
||||||
|
interface TaskRowSlotProps {
|
||||||
|
isRowContent: boolean
|
||||||
|
task: Task
|
||||||
|
level: number
|
||||||
|
indent: string
|
||||||
|
isHovered: boolean
|
||||||
|
hoveredTaskId: number | null
|
||||||
|
isParent: boolean
|
||||||
|
hasChildren: boolean
|
||||||
|
collapsed: boolean
|
||||||
|
formattedTimer: string
|
||||||
|
timerRunning: boolean
|
||||||
|
timerElapsed: number
|
||||||
|
isOvertime: number | boolean | undefined
|
||||||
|
overdueDays: number
|
||||||
|
overtimeText: string
|
||||||
|
overdueText: string
|
||||||
|
daysText: string
|
||||||
|
progressClass: string
|
||||||
|
type?: string
|
||||||
|
}
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
task: Task
|
task: Task
|
||||||
level: number
|
level: number
|
||||||
@@ -26,6 +48,10 @@ const emit = defineEmits([
|
|||||||
'add-successor',
|
'add-successor',
|
||||||
'delete',
|
'delete',
|
||||||
])
|
])
|
||||||
|
|
||||||
|
defineSlots<{
|
||||||
|
'custom-task-content'(props: TaskRowSlotProps): unknown
|
||||||
|
}>()
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const overtimeText = computed(() => t.value?.overtime ?? '')
|
const overtimeText = computed(() => t.value?.overtime ?? '')
|
||||||
const overdueText = computed(() => t.value?.overdue ?? '')
|
const overdueText = computed(() => t.value?.overdue ?? '')
|
||||||
@@ -466,7 +492,7 @@ onUnmounted(() => {
|
|||||||
@delete="handleTaskDelete"
|
@delete="handleTaskDelete"
|
||||||
>
|
>
|
||||||
<template v-if="hasContentSlot" #custom-task-content="slotProps">
|
<template v-if="hasContentSlot" #custom-task-content="slotProps">
|
||||||
<slot name="custom-task-content" v-bind="slotProps" />
|
<slot name="custom-task-content" v-bind="(slotProps as TaskRowSlotProps)" />
|
||||||
</template>
|
</template>
|
||||||
</TaskRow>
|
</TaskRow>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -2750,7 +2750,7 @@ const handleAddSuccessor = (task: Task) => {
|
|||||||
@add-successor="handleAddSuccessor"
|
@add-successor="handleAddSuccessor"
|
||||||
@delete="handleTaskDelete"
|
@delete="handleTaskDelete"
|
||||||
>
|
>
|
||||||
<template v-if="$slots['custom-task-content']" #content="barScope">
|
<template v-if="$slots['custom-task-content']" #custom-task-content="barScope">
|
||||||
<slot name="custom-task-content" v-bind="barScope" />
|
<slot name="custom-task-content" v-bind="barScope" />
|
||||||
</template>
|
</template>
|
||||||
</TaskBar>
|
</TaskBar>
|
||||||
|
|||||||
Reference in New Issue
Block a user