style:甘特图显示样式优化

pull/1/head
黄伟杰 1 month ago
parent 02ba88a454
commit 824e5e77d9

@ -3,17 +3,17 @@
<div ref="ganttContainerRef" class="schedule-gantt-container" :style="{ height }"></div>
<div class="schedule-detail-panel">
<div class="schedule-detail-title">计划信息</div>
<el-descriptions :column="1" border size="small" v-if="activePreviewDevice">
<template v-if="activePreviewDevice">
<el-descriptions :column="1" border size="small">
<el-descriptions-item label="设备名称">{{ activePreviewDevice.deviceName }}</el-descriptions-item>
<el-descriptions-item label="设备ID">{{ activePreviewDevice.deviceId }}</el-descriptions-item>
<el-descriptions-item label="产能">{{ activePreviewDevice.ratedCapacity ?? '-' }}</el-descriptions-item>
<el-descriptions-item label="计划条数">{{ activePreviewDevice.plans?.length ?? 0 }}</el-descriptions-item>
</el-descriptions>
<el-empty v-else description="暂无计划信息" :image-size="80" />
<div class="schedule-plan-list-title">计划明细</div>
<div class="schedule-plan-list">
<div
v-for="(plan, index) in activePreviewDevice?.plans ?? []"
v-for="(plan, index) in activePreviewTask ? [activePreviewTask] : (activePreviewDevice?.plans ?? [])"
:key="`${activePreviewDevice?.deviceId}-${plan.taskDetailId}-${index}`"
:class="['schedule-plan-item', { 'schedule-plan-item-active': plan.sourceType === 'CURRENT' }]"
>
@ -28,6 +28,8 @@
<div>最晚开工{{ plan.latestStartTimeStr || '-' }}</div>
</div>
</div>
</template>
<el-empty v-else description="暂无计划信息" :image-size="80" />
</div>
</div>
@ -56,6 +58,24 @@
<el-button type="primary" @click="handleTaskAdjustSubmit"></el-button>
</template>
</el-dialog>
<el-dialog v-if="props.editable" v-model="startDateEditorVisible" title="修改开始时间" width="360px" append-to-body>
<el-form label-width="80px">
<el-form-item label="开始时间">
<el-date-picker
v-model="startDateEditorValue"
type="datetime"
value-format="YYYY-MM-DD HH:mm:ss"
placeholder="请选择开始时间"
class="!w-full"
/>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="startDateEditorVisible = false">取消</el-button>
<el-button type="primary" @click="handleStartDateEditorSubmit"></el-button>
</template>
</el-dialog>
</template>
<script setup lang="ts">
@ -80,6 +100,11 @@ const props = withDefaults(
const message = useMessage()
const ganttContainerRef = ref<HTMLDivElement>()
const activePreviewDevice = ref<any>()
const activePreviewTask = ref<any>()
const startDateEditorVisible = ref(false)
const startDateEditorTaskId = ref<string | number | null>(null)
const startDateEditorValue = ref('')
const tooltipCleanupFns = ref<(() => void)[]>([])
const ganttEventIds = ref<string[]>([])
const ganttSyncing = ref(false)
const taskAdjustDialogVisible = ref(false)
@ -225,6 +250,8 @@ const cleanupTaskTooltips = () => {
node.style.visibility = 'hidden'
node.style.opacity = '0'
}
tooltipCleanupFns.value.forEach((fn) => fn())
tooltipCleanupFns.value = []
}
const destroyGantt = () => {
@ -315,7 +342,28 @@ const initTaskTooltips = () => {
const tooltipsExt = (gantt.ext as any)?.tooltips
const tooltip = tooltipsExt?.tooltip
if (!tooltipsExt || !tooltip || !ganttContainerRef.value) return
let tooltipHideTimer: ReturnType<typeof setTimeout> | null = null
const forceHideTooltip = () => {
if (tooltipHideTimer) {
clearTimeout(tooltipHideTimer)
tooltipHideTimer = null
}
tooltip.hide()
const node = tooltip.getNode?.()
if (node) {
node.style.display = 'none'
node.style.visibility = 'hidden'
node.style.opacity = '0'
}
}
const showTooltip = (event: MouseEvent, html: string) => {
if (tooltipHideTimer) {
clearTimeout(tooltipHideTimer)
tooltipHideTimer = null
}
tooltip.setContent(html)
tooltip.show(event)
const node = tooltip.getNode?.()
@ -327,6 +375,7 @@ const initTaskTooltips = () => {
node.style.pointerEvents = 'none'
}
}
tooltipsExt.detach('.gantt_task_line')
tooltipsExt.detach('.gantt_task_content')
tooltipsExt.attach({
@ -342,7 +391,38 @@ const initTaskTooltips = () => {
showTooltip(event, buildTaskTooltipHtml(task))
},
onmouseleave: () => {
tooltip.hide()
forceHideTooltip()
}
})
const containerEl = ganttContainerRef.value
const containerMouseLeaveHandler = () => {
forceHideTooltip()
}
containerEl.addEventListener('mouseleave', containerMouseLeaveHandler)
const documentMouseMoveHandler = (e: MouseEvent) => {
const target = e.target as HTMLElement
if (!target) return
const isOverTask = target.closest('.gantt_task_line') || target.closest('.gantt_task_content')
if (!isOverTask) {
if (tooltipHideTimer) clearTimeout(tooltipHideTimer)
tooltipHideTimer = setTimeout(forceHideTooltip, 150)
} else {
if (tooltipHideTimer) {
clearTimeout(tooltipHideTimer)
tooltipHideTimer = null
}
}
}
document.addEventListener('mousemove', documentMouseMoveHandler)
tooltipCleanupFns.value.push(() => {
containerEl.removeEventListener('mouseleave', containerMouseLeaveHandler)
document.removeEventListener('mousemove', documentMouseMoveHandler)
if (tooltipHideTimer) {
clearTimeout(tooltipHideTimer)
tooltipHideTimer = null
}
})
}
@ -359,6 +439,9 @@ const syncPlanTimeFromTask = (task: any) => {
task._planData.planEndTimeStr = end.format('YYYY-MM-DD HH:mm:ss')
task._planData.scheduleDays = duration
activePreviewDevice.value = task._deviceData
if (activePreviewTask.value && String(activePreviewTask.value.taskDetailId) === String(task._planData.taskDetailId)) {
activePreviewTask.value = task._planData
}
}
const syncDeviceTaskRangeFromChildren = (task: any) => {
@ -515,6 +598,39 @@ const handleTaskAdjustSubmit = () => {
taskAdjustDialogVisible.value = false
}
const handleStartDateEditorSubmit = () => {
if (!startDateEditorTaskId.value || !startDateEditorValue.value) {
startDateEditorVisible.value = false
return
}
const task = gantt.getTask(startDateEditorTaskId.value)
if (!task?._planData) {
startDateEditorVisible.value = false
return
}
const newStart = dayjs(startDateEditorValue.value)
if (!newStart.isValid()) {
message.warning('请选择有效的时间')
return
}
const duration = Math.max(Number(task.duration) || 1, 1)
const newEnd = newStart.add(duration - 1, 'day').endOf('day')
ganttSyncing.value = true
try {
task.start_date = newStart.toDate()
task.end_date = newEnd.toDate()
task.duration = duration
syncPlanTimeFromTask(task)
normalizeDeviceChildren(task.parent)
refreshPlanLinksByRowOrder()
refreshTimelineRangeByTasks()
gantt.updateTask(task.id)
} finally {
ganttSyncing.value = false
}
startDateEditorVisible.value = false
}
const refreshTimelineRangeByTasks = () => {
let minStart = Number.POSITIVE_INFINITY
let maxEnd = Number.NEGATIVE_INFINITY
@ -546,10 +662,11 @@ const initGanttPreview = () => {
destroyGantt()
if (!previewScheduleList.value.length) {
activePreviewDevice.value = undefined
activePreviewTask.value = undefined
return
}
gantt.plugins({ tooltip: true })
gantt.plugins({ tooltip: true, inline_edit: true,undo: true })
gantt.config.readonly = !props.editable
gantt.config.drag_move = !!props.editable
gantt.config.drag_links = false
@ -579,12 +696,8 @@ const initGanttPreview = () => {
width: 210,
template: (task: any) =>
props.editable && task?._planData && String(task?._planData?.sourceType ?? '').toUpperCase() === 'CURRENT'
? `<span class="gantt-inline-editor-trigger" data-field="start_date">${formatGridDateText(task.start_date)}</span>`
: formatGridDateText(task.start_date),
editor: {
type: 'date',
map_to: 'start_date'
}
? `<span class="gantt-inline-editor-trigger" data-field="start_date" data-task-id="${task.id}">${formatGridDateText(task.start_date)}</span>`
: formatGridDateText(task.start_date)
},
{
name: 'duration',
@ -617,6 +730,7 @@ const initGanttPreview = () => {
const globalRange = getGlobalDateRange(previewScheduleList.value)
gantt.config.start_date = dayjs(globalRange.start).startOf('day').toDate()
gantt.config.end_date = dayjs(globalRange.end).endOf('day').toDate()
gantt.init(ganttContainerRef.value)
const ganttData = buildPreviewGanttData(previewScheduleList.value)
@ -625,18 +739,39 @@ const initGanttPreview = () => {
if (ganttData.data.length) {
activePreviewDevice.value = ganttData.data[0]._deviceData
gantt.showDate(gantt.config.start_date)
const today = dayjs().startOf('day').toDate()
const pos = gantt.posFromDate(today)
const scrollState = gantt.getScrollState()
if (scrollState && pos >= 0) {
const halfWidth = scrollState.inner_width / 3
gantt.scrollTo(Math.max(0, pos - halfWidth), 0)
} else {
gantt.showDate(today)
}
}
const clickEventId = gantt.attachEvent('onTaskClick', (id, event: MouseEvent) => {
const task = gantt.getTask(id)
if (task?._planData) {
activePreviewTask.value = task._planData
activePreviewDevice.value = task._deviceData
} else {
activePreviewTask.value = undefined
activePreviewDevice.value = task?._deviceData
}
if (!props.editable) return true
const target = event?.target as HTMLElement | null
const editableNode = target?.closest('.gantt-inline-editor-trigger') as HTMLElement | null
const field = editableNode?.dataset?.field
if (!field || !task?._planData || String(task?._planData?.sourceType ?? '').toUpperCase() !== 'CURRENT') return true
if (field === 'start_date') {
startDateEditorTaskId.value = id
startDateEditorValue.value = dayjs(task.start_date).format('YYYY-MM-DD HH:mm:ss')
startDateEditorVisible.value = true
return false
}
const inlineEditors = (gantt.ext as any)?.inlineEditors
if (field && task?._planData && String(task?._planData?.sourceType ?? '').toUpperCase() === 'CURRENT' && inlineEditors?.startEdit) {
if (inlineEditors?.startEdit) {
inlineEditors.startEdit(id, field)
return false
}
@ -644,6 +779,28 @@ const initGanttPreview = () => {
})
ganttEventIds.value.push(clickEventId)
if (props.editable && ganttContainerRef.value) {
const gridClickHandler = (e: MouseEvent) => {
const target = e.target as HTMLElement
const trigger = target.closest('.gantt-inline-editor-trigger[data-field="start_date"]') as HTMLElement | null
if (!trigger) return
const taskId = trigger.getAttribute('data-task-id')
if (!taskId) return
try {
const task = gantt.getTask(taskId)
if (!task?._planData || String(task?._planData?.sourceType ?? '').toUpperCase() !== 'CURRENT') return
e.stopPropagation()
startDateEditorTaskId.value = taskId
startDateEditorValue.value = dayjs(task.start_date).format('YYYY-MM-DD HH:mm:ss')
startDateEditorVisible.value = true
} catch {}
}
ganttContainerRef.value.addEventListener('click', gridClickHandler, true)
tooltipCleanupFns.value.push(() => {
ganttContainerRef.value?.removeEventListener('click', gridClickHandler, true)
})
}
if (!props.editable) return
const beforeDragEventId = gantt.attachEvent('onBeforeTaskDrag', (id) => {

Loading…
Cancel
Save