(null)
+
+const getCapacityTypeLabel = (value: any) => {
+ if (value === undefined || value === null) return '-'
+ const options = getDictOptions('capacity_sources')
+ const found = options.find((opt) => Number(opt.value) === Number(value))
+ return found?.label ?? String(value)
+}
const previewScheduleList = computed(() => (Array.isArray(props.scheduleList) ? props.scheduleList : []))
const previewDeviceOptions = computed(() =>
@@ -122,15 +135,9 @@ const getGlobalDateRange = (scheduleList: any[]) => {
const ends = allPlans.map((item: any) => dayjs(item?.planEndTimeStr).valueOf()).filter((item: number) => Number.isFinite(item))
if (!starts.length || !ends.length) {
const now = Date.now()
- return {
- start: now,
- end: now + 7 * 24 * 60 * 60 * 1000
- }
- }
- return {
- start: Math.min(...starts),
- end: Math.max(...ends)
+ return { start: now, end: now + 7 * 24 * 60 * 60 * 1000 }
}
+ return { start: Math.min(...starts), end: Math.max(...ends) }
}
const formatGanttDate = (value: unknown) => {
@@ -139,27 +146,10 @@ const formatGanttDate = (value: unknown) => {
return date.format('YYYY-MM-DD HH:mm')
}
-const getDeviceTaskRangeByChildren = (planTasks: any[]) => {
- const starts = planTasks.map((item: any) => dayjs(item?.start_date)).filter((item: any) => item.isValid())
- const ends = planTasks.map((item: any) => dayjs(item?.end_date)).filter((item: any) => item.isValid())
- if (!starts.length || !ends.length) return undefined
- const earliestStart = starts.reduce((min: any, current: any) => (current.valueOf() < min.valueOf() ? current : min))
- const latestEnd = ends.reduce((max: any, current: any) => (current.valueOf() > max.valueOf() ? current : max))
- const duration = Math.max(latestEnd.endOf('day').diff(earliestStart.startOf('day'), 'day') + 1, 1)
- return {
- start_date: earliestStart.toDate(),
- end_date: latestEnd.toDate(),
- duration
- }
-}
-
const buildPreviewGanttData = (scheduleList: any[]) => {
const tasks: any[] = []
- const links: any[] = []
- let linkIndex = 1
scheduleList.forEach((device: any) => {
- const deviceId = `device-${device.deviceId}`
const plans = (device?.plans ?? []).map((plan: any) => ({
...plan,
_start: dayjs(plan?.planStartTimeStr),
@@ -168,628 +158,499 @@ const buildPreviewGanttData = (scheduleList: any[]) => {
const validPlans = plans
.filter((plan: any) => plan._start.isValid() && plan._end.isValid())
.sort((a: any, b: any) => a._start.valueOf() - b._start.valueOf())
- const firstPlan = validPlans[0]
- const deviceRange = getDeviceTaskRangeByChildren(
- validPlans.map((item: any) => ({
- start_date: item?._start,
- end_date: item?._end
- }))
- )
+
+ if (!validPlans.length) return
+
+ const allStarts = validPlans.map((p: any) => p._start.valueOf())
+ const allEnds = validPlans.map((p: any) => p._end.valueOf())
+ const earliestStart = dayjs(Math.min(...allStarts))
+ const latestEnd = dayjs(Math.max(...allEnds))
+ const totalDays = Math.max(latestEnd.endOf('day').diff(earliestStart.startOf('day'), 'day') + 1, 1)
tasks.push({
- id: deviceId,
- text: `${device.deviceName ?? '-'}`,
- start_date: formatGanttDate(deviceRange?.start_date ?? firstPlan?._start),
- end_date: formatGanttDate(deviceRange?.end_date ?? firstPlan?._end),
- duration: deviceRange?.duration ?? 1,
+ id: `device-${device.deviceId}`,
+ text: device.deviceName ?? '-',
+ start_date: formatGanttDate(earliestStart),
+ end_date: formatGanttDate(latestEnd),
+ duration: totalDays,
parent: 0,
progress: 0,
- open: true,
readonly: true,
deviceName: device.deviceName ?? '-',
- _deviceData: device
- })
-
- let previousPlanTaskId: string | null = null
- validPlans.forEach((plan: any, index: number) => {
- const startDate = formatGanttDate(plan.planStartTimeStr)
- const endDate = formatGanttDate(plan.planEndTimeStr)
- const duration = Number(plan.scheduleDays) > 0 ? Number(plan.scheduleDays) : Math.max(plan._end.diff(plan._start, 'day') + 1, 1)
- if (!startDate || !endDate) return
- const isHistory = String(plan.sourceType ?? '').toUpperCase() === 'HISTORY'
- const planTaskId = `plan-${device.deviceId}-${plan.taskDetailId ?? index}-${index}`
- tasks.push({
- id: planTaskId,
- text: `${plan.productCode ?? '-'} / ${plan.productName ?? '-'} / ${plan.taskCode ?? '-'}`,
- start_date: startDate,
- end_date: endDate,
- duration,
- parent: deviceId,
- progress: 0,
- readonly: isHistory,
- _planData: plan,
- _deviceData: device
- })
- if (previousPlanTaskId) {
- links.push({
- id: `link-${linkIndex++}`,
- source: previousPlanTaskId,
- target: planTaskId,
- type: '0'
- })
- }
- previousPlanTaskId = planTaskId
+ totalDays,
+ _deviceData: device,
+ _validPlans: validPlans
})
})
- return { data: tasks, links }
-}
-
-const cleanupTaskTooltips = () => {
- const tooltipsExt = (gantt.ext as any)?.tooltips
- const tooltip = tooltipsExt?.tooltip
- if (tooltipsExt?.detach) {
- tooltipsExt.detach('.gantt_task_line')
- tooltipsExt.detach('.gantt_task_content')
- }
- if (tooltip?.hide) {
- tooltip.hide()
- }
- const node = tooltip?.getNode?.()
- if (node) {
- node.style.display = 'none'
- node.style.visibility = 'hidden'
- node.style.opacity = '0'
- }
- tooltipCleanupFns.value.forEach((fn) => fn())
- tooltipCleanupFns.value = []
+ return { data: tasks, links: [] }
}
const destroyGantt = () => {
- try {
- cleanupTaskTooltips()
- } catch {}
- try {
- ganttEventIds.value.forEach((eventId) => {
- try {
- gantt.detachEvent(eventId)
- } catch {}
- })
- } catch {}
+ ganttEventIds.value.forEach((eventId) => {
+ try { gantt.detachEvent(eventId) } catch {}
+ })
ganttEventIds.value = []
- try {
- gantt.clearAll()
- } catch {}
+ try { gantt.clearAll() } catch {}
}
-const formatGridDateText = (value: unknown) => {
- const date = dayjs(value)
- if (!date.isValid()) return '-'
- return date.format('YYYY-MM-DD HH:mm:ss')
-}
const formatTooltipDateTime = (value: unknown) => {
const date = dayjs(value)
if (!date.isValid()) return '-'
return date.format('YYYY-MM-DD HH:mm:ss')
}
-const getDeviceTaskSummary = (task: any) => {
- if (!task) {
- return {
- planCount: 0,
- totalPlanNumber: 0,
- earliestStart: '-',
- latestEnd: '-'
- }
- }
- const childTaskIds = gantt.getChildren(task.id)
- const childTasks = (Array.isArray(childTaskIds) ? childTaskIds : [])
- .map((childId: string | number) => gantt.getTask(childId))
- .filter((child: any) => child?._planData)
- if (!childTasks.length) {
- return {
- planCount: 0,
- totalPlanNumber: 0,
- earliestStart: '-',
- latestEnd: '-'
- }
- }
- const starts = childTasks.map((child: any) => dayjs(child.start_date).valueOf()).filter((val: number) => Number.isFinite(val))
- const ends = childTasks.map((child: any) => dayjs(child.end_date).valueOf()).filter((val: number) => Number.isFinite(val))
- const totalPlanNumber = childTasks.reduce((sum: number, child: any) => sum + Number(child?._planData?.planNumber ?? 0), 0)
- return {
- planCount: childTasks.length,
- totalPlanNumber,
- earliestStart: starts.length ? formatTooltipDateTime(Math.min(...starts)) : '-',
- latestEnd: ends.length ? formatTooltipDateTime(Math.max(...ends)) : '-'
- }
+
+const clearCustomPlanBars = () => {
+ if (!ganttContainerRef.value) return
+ const existing = ganttContainerRef.value.querySelectorAll('.custom-plan-bar')
+ existing.forEach((el) => el.remove())
}
-const buildTaskTooltipHtml = (task: any, start?: Date, end?: Date) => {
- const plan = task?._planData
- if (plan) {
- return `
- 任务明细
- 任务单:${plan.taskCode ?? '-'}
- 产品:${plan.productCode ?? '-'} / ${plan.productName ?? '-'}
- 明细ID:${plan.taskDetailId ?? '-'}
- 计划数量:${plan.planNumber ?? '-'}
- 开始:${formatTooltipDateTime(start ?? task?.start_date)}
- 结束:${formatTooltipDateTime(end ?? task?.end_date)}
- 最晚开工:${formatTooltipDateTime(plan.latestStartTimeStr)}
- `
- }
- const device = task?._deviceData
- const summary = getDeviceTaskSummary(task)
+
+const buildPlanBarTooltipHtml = (plan: any, device: any) => {
return `
- 汇总
+ 任务明细
设备:${device?.deviceName ?? '-'}
- 任务明细条数:${summary.planCount}
- 计划总数:${summary.totalPlanNumber}
- 最早计划开始:${summary.earliestStart}
- 最晚计划结束:${summary.latestEnd}
+ 任务单:${plan.taskCode ?? '-'}
+ 产品:${plan.productCode ?? '-'} / ${plan.productName ?? '-'}
+ 计划数量:${plan.planNumber ?? '-'}
+ 开始:${formatTooltipDateTime(plan.planStartTimeStr)}
+ 结束:${formatTooltipDateTime(plan.planEndTimeStr)}
+ 最晚开工:${formatTooltipDateTime(plan.latestStartTimeStr)}
`
}
-const getTaskByTooltipNode = (node: HTMLElement) => {
- const holder = node.closest('[task_id]') || node.closest('[data-task-id]') || node.closest('.gantt_task_line') || node
- const taskId = holder?.getAttribute('task_id') || holder?.getAttribute('data-task-id')
- if (!taskId) return undefined
- try {
- return gantt.getTask(taskId)
- } catch {
- return undefined
+
+const makePlanIdentity = (plan: any) =>
+ `${plan?.taskId ?? ''}-${plan?.taskDetailId ?? ''}-${plan?.planId ?? ''}`
+
+const updatePlanTime = (plan: any, newStart: dayjs.Dayjs, newEnd: dayjs.Dayjs) => {
+ plan.planStartTimeStr = newStart.format('YYYY-MM-DD HH:mm:ss')
+ plan.planEndTimeStr = newEnd.format('YYYY-MM-DD HH:mm:ss')
+ plan.planStartTime = newStart.valueOf()
+ plan.planEndTime = newEnd.valueOf()
+ plan.scheduleDays = Math.max(newEnd.diff(newStart, 'day') + 1, 1)
+}
+
+const movePlanToDevice = (plan: any, sourceDevice: any, targetDevice: any) => {
+ if (sourceDevice?.deviceId === targetDevice?.deviceId) return
+ const identity = makePlanIdentity(plan)
+ if (sourceDevice?.plans && Array.isArray(sourceDevice.plans)) {
+ sourceDevice.plans = sourceDevice.plans.filter((item: any) => makePlanIdentity(item) !== identity)
}
+ if (!Array.isArray(targetDevice.plans)) {
+ targetDevice.plans = []
+ }
+ const exists = targetDevice.plans.some((item: any) => makePlanIdentity(item) === identity)
+ if (!exists) {
+ targetDevice.plans.push(plan)
+ }
+ plan.deviceId = targetDevice.deviceId
+ plan.feedingPipeline = targetDevice.deviceId
}
-const initTaskTooltips = () => {
- const tooltipsExt = (gantt.ext as any)?.tooltips
- const tooltip = tooltipsExt?.tooltip
- if (!tooltipsExt || !tooltip || !ganttContainerRef.value) return
+const renderCustomPlanBars = () => {
+ clearCustomPlanBars()
+ if (!ganttContainerRef.value) return
- let tooltipHideTimer: ReturnType | null = null
+ const taskRows = ganttContainerRef.value.querySelectorAll('.gantt_task_row')
+ taskRows.forEach((row: Element) => {
+ const taskId = row.getAttribute('task_id')
+ if (!taskId) return
+ const task = gantt.getTask(taskId)
+ if (!task?._validPlans) return
+
+ const plans = task._validPlans as any[]
+ const rowHeight = gantt.config.row_height
+ const barHeight = Math.min(gantt.config.bar_height, rowHeight - 8)
+ const barTop = (rowHeight - barHeight) / 2
+
+ plans.forEach((plan: any) => {
+ const planStart = dayjs(plan.planStartTimeStr)
+ const planEnd = dayjs(plan.planEndTimeStr)
+ if (!planStart.isValid() || !planEnd.isValid()) return
+
+ const leftPos = gantt.posFromDate(planStart.toDate())
+ const rightPos = gantt.posFromDate(planEnd.toDate())
+ const width = Math.max(rightPos - leftPos, 4)
+ const isCurrent = String(plan.sourceType ?? '').toUpperCase() === 'CURRENT'
+
+ const bar = document.createElement('div')
+ bar.className = 'custom-plan-bar'
+ if (isCurrent) {
+ bar.classList.add('custom-plan-bar-current')
+ }
+ bar.style.cssText = `
+ position: absolute;
+ left: ${leftPos}px;
+ top: ${barTop}px;
+ width: ${width}px;
+ height: ${barHeight}px;
+ border-radius: 6px;
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
+ cursor: ${isCurrent ? 'grab' : 'pointer'};
+ z-index: 2;
+ display: flex;
+ align-items: center;
+ padding: 0 8px;
+ font-size: 12px;
+ font-weight: 500;
+ color: #ffffff;
+ text-shadow: 0 1px 2px rgba(0,0,0,0.1);
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ box-sizing: border-box;
+ `
+
+ if (isCurrent) {
+ bar.style.backgroundColor = '#67c23a'
+ } else {
+ const planStatus = plan.planStatus
+ const statusColors: Record = {
+ 1: '#3b82f6',
+ 8: '#10b981',
+ 3: '#f59e0b',
+ 4: '#f56c6c',
+ 5: '#8e7cc3'
+ }
+ bar.style.backgroundColor = statusColors[planStatus] || '#6b7280'
+ }
- 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'
- }
- }
+ bar.textContent = `${plan.productCode ?? '-'} / ${plan.productName ?? '-'}`
- const showTooltip = (event: MouseEvent, html: string) => {
- if (tooltipHideTimer) {
- clearTimeout(tooltipHideTimer)
- tooltipHideTimer = null
- }
- tooltip.setContent(html)
- tooltip.show(event)
- const node = tooltip.getNode?.()
- if (node) {
- node.style.display = 'block'
- node.style.visibility = 'visible'
- node.style.opacity = '1'
- node.style.zIndex = '10000'
- node.style.pointerEvents = 'none'
- }
- }
+ bar.setAttribute('data-plan-id', String(plan.planId ?? ''))
+ bar.setAttribute('data-device-id', String(task._deviceData?.deviceId ?? ''))
- tooltipsExt.detach('.gantt_task_line')
- tooltipsExt.detach('.gantt_task_content')
- tooltipsExt.attach({
- selector: '.gantt_task_line,.gantt_task_content',
- onmouseenter: (event: MouseEvent, node: HTMLElement) => {
- const task = getTaskByTooltipNode(node)
- if (!task) return
- showTooltip(event, buildTaskTooltipHtml(task))
- },
- onmousemove: (event: MouseEvent, node: HTMLElement) => {
- const task = getTaskByTooltipNode(node)
- if (!task) return
- showTooltip(event, buildTaskTooltipHtml(task))
- },
- onmouseleave: () => {
- forceHideTooltip()
- }
- })
+ bar.addEventListener('click', (e) => {
+ e.stopPropagation()
+ activePreviewTask.value = plan
+ activePreviewDevice.value = task._deviceData
+ })
- const containerEl = ganttContainerRef.value
- const containerMouseLeaveHandler = () => {
- forceHideTooltip()
- }
- containerEl.addEventListener('mouseleave', containerMouseLeaveHandler)
+ bar.addEventListener('mouseenter', (e) => {
+ const tooltipsExt = (gantt.ext as any)?.tooltips
+ const tooltip = tooltipsExt?.tooltip
+ if (!tooltip) return
+ const html = buildPlanBarTooltipHtml(plan, task._deviceData)
+ tooltip.setContent(html)
+ tooltip.show(e)
+ const node = tooltip.getNode?.()
+ if (node) {
+ node.style.display = 'block'
+ node.style.visibility = 'visible'
+ node.style.opacity = '1'
+ node.style.zIndex = '10000'
+ node.style.pointerEvents = 'none'
+ }
+ })
- 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
- }
- })
-}
+ bar.addEventListener('mouseleave', () => {
+ const tooltipsExt = (gantt.ext as any)?.tooltips
+ const tooltip = tooltipsExt?.tooltip
+ if (tooltip) { tooltip.hide() }
+ })
-const syncPlanTimeFromTask = (task: any) => {
- if (!task?._planData) return
- if (String(task?._planData?.sourceType ?? '').toUpperCase() !== 'CURRENT') return
- const start = dayjs(task.start_date)
- const end = dayjs(task.end_date)
- if (!start.isValid() || !end.isValid()) return
- const duration = Math.max(Number(task.duration) || end.diff(start, 'day') + 1, 1)
- task.duration = duration
- task._planData.planStartTimeStr = start.format('YYYY-MM-DD HH:mm:ss')
- 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
- }
-}
+ bar.addEventListener('contextmenu', (e) => {
+ if (!isCurrent) { e.preventDefault(); return }
+ e.preventDefault()
+ openTaskAdjustDialogForPlan(plan, task._deviceData)
+ })
-const syncDeviceTaskRangeFromChildren = (task: any) => {
- if (!task?._planData) return
- const deviceTaskId = task.parent
- if (!deviceTaskId) return
- const childTaskIds = gantt.getChildren(deviceTaskId)
- if (!Array.isArray(childTaskIds) || !childTaskIds.length) return
- const childTasks = childTaskIds.map((childId: string | number) => gantt.getTask(childId)).filter((item: any) => item?._planData)
- if (!childTasks.length) return
- const deviceRange = getDeviceTaskRangeByChildren(childTasks)
- if (!deviceRange) return
- const deviceTask = gantt.getTask(deviceTaskId)
- deviceTask.start_date = deviceRange.start_date
- deviceTask.end_date = deviceRange.end_date
- deviceTask.duration = deviceRange.duration
- gantt.updateTask(deviceTaskId)
-}
+ if (isCurrent) {
+ attachPlanBarDrag(bar, plan, task)
+ attachPlanBarResize(bar, plan, task)
+ }
-const movePlanDataToDevice = (task: any, targetDeviceTaskId: string | number, sourceDeviceTaskId?: string | number) => {
- if (!task?._planData) return
- const targetDeviceTask = gantt.getTask(targetDeviceTaskId)
- const targetDeviceData = targetDeviceTask?._deviceData
- if (!targetDeviceData) return
- const planIdentity = `${task?._planData?.taskId ?? ''}-${task?._planData?.taskDetailId ?? ''}`
- const sourceDeviceTask = sourceDeviceTaskId ? gantt.getTask(sourceDeviceTaskId) : undefined
- const sourceDeviceData = sourceDeviceTask?._deviceData
- if (sourceDeviceData?.plans && Array.isArray(sourceDeviceData.plans)) {
- sourceDeviceData.plans = sourceDeviceData.plans.filter((item: any) => {
- const itemIdentity = `${item?.taskId ?? ''}-${item?.taskDetailId ?? ''}`
- return itemIdentity !== planIdentity
+ row.appendChild(bar)
})
- }
- if (!Array.isArray(targetDeviceData.plans)) {
- targetDeviceData.plans = []
- }
- const targetExists = targetDeviceData.plans.some((item: any) => {
- const itemIdentity = `${item?.taskId ?? ''}-${item?.taskDetailId ?? ''}`
- return itemIdentity === planIdentity
})
- if (!targetExists) {
- targetDeviceData.plans.push(task._planData)
- }
- task._planData.deviceId = targetDeviceData.deviceId
- task._planData.feedingPipeline = targetDeviceData.deviceId
- task._deviceData = targetDeviceData
}
-const normalizeDeviceChildren = (deviceTaskId: string | number, priorityTaskId?: string | number) => {
- try {
- gantt.getTask(deviceTaskId)
- } catch {
- return
+const findOriginalPlan = (planIdentity: string) => {
+ for (const device of previewScheduleList.value) {
+ const plan = (device?.plans ?? []).find(
+ (p: any) => makePlanIdentity(p) === planIdentity
+ )
+ if (plan) return { plan, device }
}
- const childTaskIds = gantt.getChildren(deviceTaskId)
- if (!Array.isArray(childTaskIds) || !childTaskIds.length) return
- const childTasks = childTaskIds
- .map((childId: string | number) => gantt.getTask(childId))
- .filter((item: any) => item?._planData)
- .sort((a: any, b: any) => {
- const startDiff = dayjs(a.start_date).valueOf() - dayjs(b.start_date).valueOf()
- if (startDiff !== 0) return startDiff
- if (priorityTaskId !== undefined && String(a.id) === String(priorityTaskId)) return -1
- if (priorityTaskId !== undefined && String(b.id) === String(priorityTaskId)) return 1
- return 0
- })
- childTasks.forEach((item: any) => {
- syncPlanTimeFromTask(item)
- gantt.updateTask(item.id)
- })
- const fakePlanTask = { _planData: true, parent: deviceTaskId }
- syncDeviceTaskRangeFromChildren(fakePlanTask)
+ return null
}
-const refreshPlanLinksByRowOrder = () => {
- try {
- const allLinks = gantt.getLinks()
- allLinks.forEach((item: any) => gantt.deleteLink(item.id))
- let linkIndex = 1
- const deviceTaskIds = gantt.getChildren(0)
- deviceTaskIds.forEach((deviceTaskId: string | number) => {
- try {
- const childTaskIds = gantt.getChildren(deviceTaskId).filter((childId: string | number) => {
- const task = gantt.getTask(childId)
- return !!task?._planData
- })
- let previousTaskId: string | number | null = null
- childTaskIds.forEach((taskId: string | number) => {
- if (previousTaskId !== null) {
- gantt.addLink({
- id: `link-${linkIndex++}`,
- source: previousTaskId,
- target: taskId,
- type: '0'
- })
- }
- previousTaskId = taskId
- })
- } catch {}
- })
- } catch {}
-}
+const attachPlanBarDrag = (bar: HTMLElement, plan: any, task: any) => {
+ let startX = 0
+ let startLeft = 0
+ let dragging = false
+ let moved = false
-const getDeviceInsertIndex = (deviceTaskId: string | number, startDate: dayjs.Dayjs) => {
- const childTaskIds = gantt.getChildren(deviceTaskId)
- if (!Array.isArray(childTaskIds) || !childTaskIds.length) return 0
- const targetStart = startDate.startOf('day').valueOf()
- for (let index = 0; index < childTaskIds.length; index += 1) {
- const childTask = gantt.getTask(childTaskIds[index])
- if (!childTask?._planData) continue
- const childStart = dayjs(childTask.start_date).startOf('day').valueOf()
- if (childStart >= targetStart) return index
+ const onMouseDown = (e: MouseEvent) => {
+ if (e.button !== 0) return
+ const target = e.target as HTMLElement
+ if (target.classList.contains('custom-plan-resize-handle')) return
+ e.preventDefault()
+ e.stopPropagation()
+ startX = e.clientX
+ startLeft = bar.offsetLeft
+ dragging = true
+ moved = false
+ bar.style.cursor = 'grabbing'
+ bar.style.zIndex = '10'
+ bar.style.opacity = '0.85'
+ document.addEventListener('mousemove', onMouseMove)
+ document.addEventListener('mouseup', onMouseUp)
+ }
+
+ const onMouseMove = (e: MouseEvent) => {
+ if (!dragging) return
+ const dx = e.clientX - startX
+ if (Math.abs(dx) < 3 && !moved) return
+ moved = true
+ const newLeft = Math.max(0, startLeft + dx)
+ bar.style.left = `${newLeft}px`
+ }
+
+ const onMouseUp = (e: MouseEvent) => {
+ document.removeEventListener('mousemove', onMouseMove)
+ document.removeEventListener('mouseup', onMouseUp)
+ dragging = false
+ bar.style.cursor = 'grab'
+ bar.style.zIndex = '2'
+ bar.style.opacity = '1'
+
+ if (!moved) return
+ moved = false
+
+ const dx = e.clientX - startX
+ const newLeft = Math.max(0, startLeft + dx)
+ const newStartDate = gantt.dateFromPos(newLeft)
+ const planDuration = dayjs(plan.planEndTimeStr).diff(dayjs(plan.planStartTimeStr), 'millisecond')
+ const newEndDate = new Date(newStartDate.getTime() + planDuration)
+
+ const newStart = dayjs(newStartDate)
+ const newEnd = dayjs(newEndDate)
+
+ const identity = makePlanIdentity(plan)
+ const original = findOriginalPlan(identity)
+ if (original) {
+ updatePlanTime(original.plan, newStart, newEnd)
+ } else {
+ updatePlanTime(plan, newStart, newEnd)
+ }
+
+ const targetDeviceEl = findDeviceRowAtPosition(bar, newLeft)
+ if (targetDeviceEl) {
+ const targetDeviceId = targetDeviceEl.getAttribute('data-device-id')
+ const targetDevice = previewScheduleList.value.find((d: any) => String(d.deviceId) === targetDeviceId)
+ if (targetDevice && targetDevice.deviceId !== task._deviceData?.deviceId) {
+ const sourceDevice = original?.device ?? task._deviceData
+ movePlanToDevice(original?.plan ?? plan, sourceDevice, targetDevice)
+ }
+ }
+
+ editingPlanIdentity.value = identity
+ initGanttPreview()
}
- return childTaskIds.length
+
+ bar.addEventListener('mousedown', onMouseDown)
}
-const addDeviceToGantt = (device: any, excludePlanIdentity?: string) => {
- const deviceId = `device-${device.deviceId}`
- try {
- gantt.getTask(deviceId)
- return
- } catch {}
- const plans = (device?.plans ?? []).map((plan: any) => ({
- ...plan,
- _start: dayjs(plan?.planStartTimeStr),
- _end: dayjs(plan?.planEndTimeStr)
- }))
- const validPlans = plans
- .filter((plan: any) => plan._start.isValid() && plan._end.isValid())
- .sort((a: any, b: any) => a._start.valueOf() - b._start.valueOf())
- const firstPlan = validPlans[0]
- const deviceRange = getDeviceTaskRangeByChildren(
- validPlans.map((item: any) => ({ start_date: item?._start, end_date: item?._end }))
- )
- gantt.addTask({
- id: deviceId,
- text: `${device.deviceName ?? '-'}`,
- start_date: formatGanttDate(deviceRange?.start_date ?? firstPlan?._start),
- end_date: formatGanttDate(deviceRange?.end_date ?? firstPlan?._end),
- duration: deviceRange?.duration ?? 1,
- parent: 0,
- progress: 0,
- open: true,
- readonly: true,
- deviceName: device.deviceName ?? '-',
- _deviceData: device
- })
- validPlans.forEach((plan: any, index: number) => {
- const planIdentity = `${plan?.taskId ?? ''}-${plan?.taskDetailId ?? ''}`
- if (excludePlanIdentity && planIdentity === excludePlanIdentity) return
- const startDate = formatGanttDate(plan.planStartTimeStr)
- const endDate = formatGanttDate(plan.planEndTimeStr)
- if (!startDate || !endDate) return
- const duration = Number(plan.scheduleDays) > 0 ? Number(plan.scheduleDays) : Math.max(plan._end.diff(plan._start, 'day') + 1, 1)
- const isHistory = String(plan.sourceType ?? '').toUpperCase() === 'HISTORY'
- const planTaskId = `plan-${device.deviceId}-${plan.taskDetailId ?? index}-${index}`
- gantt.addTask({
- id: planTaskId,
- text: `${plan.productCode ?? '-'} / ${plan.productName ?? '-'} / ${plan.taskCode ?? '-'}`,
- start_date: startDate,
- end_date: endDate,
- duration,
- parent: deviceId,
- progress: 0,
- readonly: isHistory,
- _planData: plan,
- _deviceData: device
- })
- })
+const findDeviceRowAtPosition = (bar: HTMLElement, left: number) => {
+ const barRect = bar.getBoundingClientRect()
+ const centerY = barRect.top + barRect.height / 2
+ const centerX = barRect.left + left + barRect.width / 2
+ const elAtCenter = document.elementFromPoint(centerX, centerY)
+ if (!elAtCenter) return null
+ const row = elAtCenter.closest('.gantt_task_row') as HTMLElement
+ if (!row) return null
+ const taskId = row.getAttribute('task_id')
+ if (!taskId) return null
+ const ganttTask = gantt.getTask(taskId)
+ if (!ganttTask?._deviceData) return null
+ return { getAttribute: (attr: string) => String(ganttTask._deviceData.deviceId ?? '') }
}
-const removeDeviceFromGanttIfNoCurrent = (deviceTaskId: string | number) => {
- try {
- const deviceTask = gantt.getTask(deviceTaskId)
- if (!deviceTask || deviceTask?._planData) return
- const childTaskIds = gantt.getChildren(deviceTaskId)
- const hasCurrent = (Array.isArray(childTaskIds) ? childTaskIds : []).some((childId: string | number) => {
- const childTask = gantt.getTask(childId)
- return childTask?._planData && String(childTask._planData.sourceType ?? '').toUpperCase() === 'CURRENT'
- })
- if (!hasCurrent) {
- gantt.deleteTask(String(deviceTaskId))
+const attachPlanBarResize = (bar: HTMLElement, plan: any, task: any) => {
+ const leftHandle = document.createElement('div')
+ leftHandle.className = 'custom-plan-resize-handle custom-plan-resize-left'
+ leftHandle.style.cssText = `
+ position: absolute; left: 0; top: 0; width: 8px; height: 100%;
+ cursor: w-resize; z-index: 3;
+ `
+ const rightHandle = document.createElement('div')
+ rightHandle.className = 'custom-plan-resize-handle custom-plan-resize-right'
+ rightHandle.style.cssText = `
+ position: absolute; right: 0; top: 0; width: 8px; height: 100%;
+ cursor: e-resize; z-index: 3;
+ `
+ bar.appendChild(leftHandle)
+ bar.appendChild(rightHandle)
+
+ const attachResize = (handle: HTMLElement, isLeft: boolean) => {
+ let startX = 0
+ let startLeft = 0
+ let startWidth = 0
+ let resizing = false
+
+ const onMouseDown = (e: MouseEvent) => {
+ e.preventDefault()
+ e.stopPropagation()
+ startX = e.clientX
+ startLeft = bar.offsetLeft
+ startWidth = bar.offsetWidth
+ resizing = true
+ bar.style.zIndex = '10'
+ document.addEventListener('mousemove', onMouseMove)
+ document.addEventListener('mouseup', onMouseUp)
}
- } catch {}
-}
-const applyTaskAdjust = (task: any, targetDeviceTaskId: string | number, startDate: string, endDate: string) => {
- const sourceDeviceTaskId = task.parent
- const nextStart = dayjs(startDate)
- const nextEnd = dayjs(endDate)
- const duration = Math.max(nextEnd.diff(nextStart, 'day') + 1, 1)
- const planIdentity = `${task?._planData?.taskId ?? ''}-${task?._planData?.taskDetailId ?? ''}`
-
- const targetDeviceData = previewScheduleList.value.find((d: any) => `device-${d.deviceId}` === targetDeviceTaskId)
- if (!targetDeviceData) return
-
- const sourceDeviceData = task._deviceData
- if (sourceDeviceData?.plans && Array.isArray(sourceDeviceData.plans)) {
- sourceDeviceData.plans = sourceDeviceData.plans.filter((item: any) => {
- const itemId = `${item?.taskId ?? ''}-${item?.taskDetailId ?? ''}`
- return itemId !== planIdentity
- })
- }
- if (!Array.isArray(targetDeviceData.plans)) {
- targetDeviceData.plans = []
- }
- const targetExists = targetDeviceData.plans.some((item: any) => {
- const itemId = `${item?.taskId ?? ''}-${item?.taskDetailId ?? ''}`
- return itemId === planIdentity
- })
- if (!targetExists) {
- targetDeviceData.plans.push(task._planData)
- }
- task._planData.deviceId = targetDeviceData.deviceId
- task._planData.feedingPipeline = targetDeviceData.deviceId
- task._deviceData = targetDeviceData
-
- let targetDeviceInGantt = true
- try {
- gantt.getTask(targetDeviceTaskId)
- } catch {
- targetDeviceInGantt = false
- }
+ const onMouseMove = (e: MouseEvent) => {
+ if (!resizing) return
+ const dx = e.clientX - startX
+ if (isLeft) {
+ const newLeft = Math.max(0, startLeft + dx)
+ const newWidth = Math.max(20, startWidth - dx)
+ bar.style.left = `${newLeft}px`
+ bar.style.width = `${newWidth}px`
+ } else {
+ const newWidth = Math.max(20, startWidth + dx)
+ bar.style.width = `${newWidth}px`
+ }
+ }
- if (!targetDeviceInGantt) {
- addDeviceToGantt(targetDeviceData, planIdentity)
- }
+ const onMouseUp = () => {
+ document.removeEventListener('mousemove', onMouseMove)
+ document.removeEventListener('mouseup', onMouseUp)
+ if (!resizing) return
+ resizing = false
+ bar.style.zIndex = '2'
+
+ const newLeft = parseFloat(bar.style.left) || startLeft
+ const newWidth = parseFloat(bar.style.width) || startWidth
+ const newStartDate = gantt.dateFromPos(newLeft)
+ const newEndDate = gantt.dateFromPos(newLeft + newWidth)
+
+ const identity = makePlanIdentity(plan)
+ const original = findOriginalPlan(identity)
+ if (original) {
+ updatePlanTime(original.plan, dayjs(newStartDate), dayjs(newEndDate))
+ } else {
+ updatePlanTime(plan, dayjs(newStartDate), dayjs(newEndDate))
+ }
+ editingPlanIdentity.value = identity
+ initGanttPreview()
+ }
- task.parent = targetDeviceTaskId
- task.start_date = nextStart.toDate()
- task.end_date = nextEnd.toDate()
- task.duration = duration
- const targetIndex = getDeviceInsertIndex(targetDeviceTaskId, nextStart)
- gantt.moveTask(task.id, targetIndex, targetDeviceTaskId)
- gantt.updateTask(task.id)
- syncPlanTimeFromTask(task)
- normalizeDeviceChildren(targetDeviceTaskId, task.id)
- if (sourceDeviceTaskId && sourceDeviceTaskId !== targetDeviceTaskId) {
- removeDeviceFromGanttIfNoCurrent(sourceDeviceTaskId)
- normalizeDeviceChildren(sourceDeviceTaskId)
+ handle.addEventListener('mousedown', onMouseDown)
}
- refreshPlanLinksByRowOrder()
- refreshTimelineRangeByTasks()
- gantt.render()
+
+ attachResize(leftHandle, true)
+ attachResize(rightHandle, false)
}
-const openTaskAdjustDialog = (task: any) => {
- if (!task?._planData) return
- taskAdjustTaskId.value = task.id
- taskAdjustForm.deviceTaskId = String(task.parent ?? '')
- taskAdjustForm.startDate = dayjs(task.start_date).format('YYYY-MM-DD HH:mm:ss')
- taskAdjustForm.endDate = dayjs(task.end_date).format('YYYY-MM-DD HH:mm:ss')
+const openTaskAdjustDialogForPlan = (plan: any, device: any) => {
+ taskAdjustForm.deviceTaskId = `device-${device?.deviceId ?? ''}`
+ taskAdjustForm.startDate = plan.planStartTimeStr || ''
+ taskAdjustForm.endDate = plan.planEndTimeStr || ''
+ editingPlanIdentity.value = makePlanIdentity(plan)
taskAdjustDialogVisible.value = true
}
const handleTaskAdjustSubmit = () => {
- if (!taskAdjustTaskId.value) return
if (!taskAdjustForm.deviceTaskId || !taskAdjustForm.startDate || !taskAdjustForm.endDate) {
message.warning('请完善设备、计划开始日期和计划结束日期')
return
}
- const task = gantt.getTask(taskAdjustTaskId.value)
- if (!task?._planData) return
- ganttSyncing.value = true
- try {
- applyTaskAdjust(task, taskAdjustForm.deviceTaskId, taskAdjustForm.startDate, taskAdjustForm.endDate)
- } finally {
- ganttSyncing.value = false
+ if (!editingPlanIdentity.value) return
+
+ const targetDevice = previewScheduleList.value.find(
+ (d: any) => `device-${d.deviceId}` === taskAdjustForm.deviceTaskId
+ )
+ if (!targetDevice) return
+
+ let foundPlan: any = null
+ let sourceDevice: any = null
+ for (const device of previewScheduleList.value) {
+ const plan = (device?.plans ?? []).find(
+ (p: any) => makePlanIdentity(p) === editingPlanIdentity.value
+ )
+ if (plan) {
+ foundPlan = plan
+ sourceDevice = device
+ break
+ }
}
- taskAdjustDialogVisible.value = false
-}
+ if (!foundPlan) return
-const refreshTimelineRangeByTasks = () => {
- let minStart = Number.POSITIVE_INFINITY
- let maxEnd = Number.NEGATIVE_INFINITY
- gantt.eachTask((task: any) => {
- if (!task?._planData) return
- const start = dayjs(task.start_date).valueOf()
- const end = dayjs(task.end_date).valueOf()
- if (!Number.isFinite(start) || !Number.isFinite(end)) return
- minStart = Math.min(minStart, start)
- maxEnd = Math.max(maxEnd, end)
- })
- if (!Number.isFinite(minStart) || !Number.isFinite(maxEnd)) return
-
- const nextStartDate = dayjs(minStart).startOf('day').toDate()
- const nextEndDate = dayjs(maxEnd).endOf('day').toDate()
- const currentStart = dayjs(gantt.config.start_date).valueOf()
- const currentEnd = dayjs(gantt.config.end_date).valueOf()
- const nextStart = dayjs(nextStartDate).valueOf()
- const nextEnd = dayjs(nextEndDate).valueOf()
- if (currentStart === nextStart && currentEnd === nextEnd) return
-
- gantt.config.start_date = nextStartDate
- gantt.config.end_date = nextEndDate
- gantt.render()
+ const newStart = dayjs(taskAdjustForm.startDate)
+ const newEnd = dayjs(taskAdjustForm.endDate)
+ if (!newStart.isValid() || !newEnd.isValid()) {
+ message.warning('请选择有效的时间')
+ return
+ }
+ if (newEnd.isBefore(newStart)) {
+ message.warning('结束时间不能早于开始时间')
+ return
+ }
+
+ updatePlanTime(foundPlan, newStart, newEnd)
+ if (sourceDevice?.deviceId !== targetDevice.deviceId) {
+ movePlanToDevice(foundPlan, sourceDevice, targetDevice)
+ }
+
+ taskAdjustDialogVisible.value = false
+ editingPlanIdentity.value = makePlanIdentity(foundPlan)
+ initGanttPreview()
}
const initGanttPreview = () => {
if (!ganttContainerRef.value) return
destroyGantt()
- if (!previewScheduleList.value.length) {
+ clearCustomPlanBars()
+
+ const ganttScheduleData = getGanttScheduleList()
+ if (!ganttScheduleData.length) {
activePreviewDevice.value = undefined
activePreviewTask.value = undefined
return
}
- gantt.plugins({ tooltip: true, inline_edit: true,undo: true })
-
- // 编辑相关配置
- gantt.config.readonly = false // 是否只读模式
- gantt.config.drag_move = true // 是否允许拖动任务
- gantt.config.drag_links = false // 是否允许拖动链接
- gantt.config.drag_progress = false // 是否允许拖动进度
- gantt.config.drag_resize = true // 是否允许调整任务大小
- gantt.config.order_branch = true // 是否允许分支内排序
- gantt.config.order_branch_free = true // 是否允许跨分支排序
- gantt.config.details_on_dblclick = false // 双击是否显示详情
- gantt.config.show_progress = false // 是否显示进度条
-
- // 布局相关配置
- gantt.config.row_height = 48 // 任务行高度
- gantt.config.scale_height = 70 // 时间轴高度
- gantt.config.xml_date = '%Y-%m-%d %H:%i' // 日期格式
- gantt.config.task_height = 36 // 任务条高度
- gantt.config.min_column_width = 80 // 最小列宽
- gantt.config.column_width = 90 // 默认列宽
- gantt.config.bar_height = 34 // 任务条实际高度
- gantt.config.resize_step = 15 // 调整步长
+ gantt.plugins({ tooltip: true })
+
+ gantt.config.tooltip_timeout = 0
+ gantt.templates.tooltip_text = () => ''
+
+ gantt.config.readonly = true
+ gantt.config.drag_move = false
+ gantt.config.drag_links = false
+ gantt.config.drag_progress = false
+ gantt.config.drag_resize = false
+ gantt.config.order_branch = false
+ gantt.config.order_branch_free = false
+ gantt.config.details_on_dblclick = false
+ gantt.config.show_progress = false
+
+ gantt.config.row_height = 48
+ gantt.config.scale_height = 70
+ gantt.config.xml_date = '%Y-%m-%d %H:%i'
+ gantt.config.task_height = 36
+ gantt.config.min_column_width = 80
+ gantt.config.column_width = 90
+ gantt.config.bar_height = 34
+ gantt.config.resize_step = 15
gantt.config.columns = [
{
name: 'text',
- label: '任务名称',
- tree: true,
+ label: '设备名称',
+ tree: false,
width: '*',
- min_width: 200
+ min_width: 100,
+ template: (task: any) => task.deviceName ?? task.text ?? '-'
},
{
- name: 'start_date',
- label: '计划时间',
+ name: 'duration',
+ label: '天数',
align: 'center',
- width: 210,
- template: (task: any) =>
- task?._planData && String(task?._planData?.sourceType ?? '').toUpperCase() === 'CURRENT'
- ? `${formatGridDateText(task.start_date)}`
- : formatGridDateText(task.start_date)
+ width: 80,
+ template: (task: any) => String(task.totalDays ?? task.duration ?? 0)
}
]
@@ -798,14 +659,11 @@ const initGanttPreview = () => {
{ unit: 'day', step: 1, format: (date) => dayjs(date).format('MM-DD') }
]
- gantt.templates.tooltip_text = (start, end, task: any) => buildTaskTooltipHtml(task, start, end)
gantt.templates.task_class = (_start, _end, task: any) => {
- if (!task?._planData) return ''
- const sourceType = String(task?._planData?.sourceType ?? '').toUpperCase()
- return sourceType === 'HISTORY' ? 'schedule-plan-task-history' : 'schedule-plan-task'
+ if (task._validPlans) return 'schedule-device-row'
+ return ''
}
- const ganttScheduleData = getGanttScheduleList()
const globalRange = getGlobalDateRange(ganttScheduleData)
gantt.config.start_date = dayjs(globalRange.start).startOf('day').toDate()
gantt.config.end_date = dayjs(globalRange.end).endOf('day').toDate()
@@ -814,16 +672,14 @@ const initGanttPreview = () => {
const ganttData = buildPreviewGanttData(ganttScheduleData)
gantt.parse(ganttData)
-
- // 强制刷新所有任务的样式,确保颜色正确应用
- gantt.eachTask((task: any) => {
- gantt.refreshTask(task.id)
- })
-
- initTaskTooltips()
if (ganttData.data.length) {
- activePreviewDevice.value = ganttData.data[0]._deviceData
+ const restoreDevice = editingPlanIdentity.value
+ ? ganttScheduleData.find((d: any) =>
+ (d?.plans ?? []).some((p: any) => makePlanIdentity(p) === editingPlanIdentity.value)
+ )
+ : null
+ activePreviewDevice.value = restoreDevice || ganttData.data[0]._deviceData
const today = dayjs().startOf('day').toDate()
const pos = gantt.posFromDate(today)
const scrollState = gantt.getScrollState()
@@ -835,140 +691,29 @@ const initGanttPreview = () => {
}
}
- 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 {
+ const clickEventId = gantt.attachEvent('onTaskClick', (id) => {
+ const ganttTask = gantt.getTask(id)
+ if (ganttTask?._deviceData) {
activePreviewTask.value = undefined
- activePreviewDevice.value = task?._deviceData
+ activePreviewDevice.value = ganttTask._deviceData
}
- 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') {
- openTaskAdjustDialog(task)
- return false
- }
- const inlineEditors = (gantt.ext as any)?.inlineEditors
- if (inlineEditors?.startEdit) {
- inlineEditors.startEdit(id, field)
- return false
- }
- return true
- })
- ganttEventIds.value.push(clickEventId)
-
- if (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()
- openTaskAdjustDialog(task)
- } catch {}
- }
- ganttContainerRef.value.addEventListener('click', gridClickHandler, true)
- tooltipCleanupFns.value.push(() => {
- ganttContainerRef.value?.removeEventListener('click', gridClickHandler, true)
+ nextTick(() => {
+ renderCustomPlanBars()
})
- }
-
- const beforeDragEventId = gantt.attachEvent('onBeforeTaskDrag', (id) => {
- const task = gantt.getTask(id)
- if (!task?._planData) return false
- taskDragDurationMap.value[String(id)] = Math.max(Number(task.duration) || 1, 1)
- return String(task?._planData?.sourceType ?? '').toUpperCase() === 'CURRENT'
- })
-
- const beforeMoveEventId = gantt.attachEvent('onBeforeTaskMove', (id, parent) => {
- const task = gantt.getTask(id)
- if (!task?._planData) return false
- if (String(task?._planData?.sourceType ?? '').toUpperCase() !== 'CURRENT') return false
- const targetTask = gantt.getTask(parent)
- if (!targetTask || targetTask?._planData) return false
- taskMoveFromParentMap.value[String(id)] = task.parent
return true
})
+ ganttEventIds.value.push(clickEventId)
- const afterMoveEventId = gantt.attachEvent('onAfterTaskMove', (id, parent) => {
- const task = gantt.getTask(id)
- if (!task?._planData) return
- const sourceParent = taskMoveFromParentMap.value[String(id)]
- delete taskMoveFromParentMap.value[String(id)]
- if (!sourceParent && sourceParent !== 0) return
- if (sourceParent === parent) {
- refreshPlanLinksByRowOrder()
- return
- }
- if (ganttSyncing.value) return
- ganttSyncing.value = true
- try {
- movePlanDataToDevice(task, parent, sourceParent)
- syncPlanTimeFromTask(task)
- normalizeDeviceChildren(parent)
- normalizeDeviceChildren(sourceParent)
- removeDeviceFromGanttIfNoCurrent(sourceParent)
- refreshPlanLinksByRowOrder()
- refreshTimelineRangeByTasks()
- } finally {
- ganttSyncing.value = false
- }
- })
-
- const contextMenuEventId = gantt.attachEvent('onContextMenu', (taskId, _linkId, event: MouseEvent) => {
- const task = gantt.getTask(taskId)
- if (!task?._planData) return true
- if (String(task?._planData?.sourceType ?? '').toUpperCase() !== 'CURRENT') return false
- event.preventDefault()
- openTaskAdjustDialog(task)
- return false
- })
-
- const dragEventId = gantt.attachEvent('onAfterTaskDrag', (id, mode) => {
- if (ganttSyncing.value) return
- const task = gantt.getTask(id)
- const taskId = String(id)
- const originalDuration = taskDragDurationMap.value[taskId]
- delete taskDragDurationMap.value[taskId]
- ganttSyncing.value = true
- try {
- if (mode === gantt.config.drag_mode.move && Number(originalDuration) > 0) {
- task.duration = originalDuration
- }
- syncPlanTimeFromTask(task)
- normalizeDeviceChildren(task.parent)
- refreshPlanLinksByRowOrder()
- refreshTimelineRangeByTasks()
- } finally {
- ganttSyncing.value = false
- }
+ nextTick(() => {
+ renderCustomPlanBars()
})
- const updateEventId = gantt.attachEvent('onAfterTaskUpdate', (id) => {
- if (ganttSyncing.value) return
- const task = gantt.getTask(id)
- ganttSyncing.value = true
- try {
- syncPlanTimeFromTask(task)
- normalizeDeviceChildren(task.parent)
- refreshPlanLinksByRowOrder()
- refreshTimelineRangeByTasks()
- gantt.refreshTask(id)
- } finally {
- ganttSyncing.value = false
- }
+ const renderEventId = gantt.attachEvent('onGanttRender', () => {
+ nextTick(() => {
+ renderCustomPlanBars()
+ })
})
-
- ganttEventIds.value.push(beforeDragEventId, beforeMoveEventId, afterMoveEventId, contextMenuEventId, dragEventId, updateEventId)
- refreshPlanLinksByRowOrder()
+ ganttEventIds.value.push(renderEventId)
}
onMounted(async () => {
@@ -996,11 +741,20 @@ onBeforeUnmount(() => {
.schedule-preview-wrap {
display: flex;
width: 100%;
+ height: 100%;
gap: 12px;
+ overflow: hidden;
}
-.schedule-gantt-container {
+.gantt-main-area {
flex: 1;
+ min-width: 0;
+ display: flex;
+ flex-direction: column;
+ overflow: hidden;
+}
+
+.schedule-gantt-container {
min-width: 0;
border: 1px solid var(--el-border-color);
border-radius: 4px;
@@ -1036,6 +790,7 @@ onBeforeUnmount(() => {
border-radius: 4px;
background: var(--el-fill-color-light);
}
+
.schedule-plan-item-head {
display: flex;
align-items: center;
@@ -1043,6 +798,7 @@ onBeforeUnmount(() => {
gap: 8px;
margin-bottom: 4px;
}
+
.schedule-plan-item-title {
font-weight: 600;
color: var(--el-text-color-primary);
@@ -1060,108 +816,9 @@ onBeforeUnmount(() => {
margin-top: 8px;
}
-.schedule-gantt-container :deep(.gantt_grid_data .gantt_row) {
- font-weight: 600;
- background: linear-gradient(180deg, #f8fafc 0%, #f0f4f8 100%);
- border-bottom: 1px solid #e2e8f0;
- height: 48px;
- display: flex;
- align-items: flex-end;
-}
-
-.schedule-gantt-container :deep(.gantt_grid_data .gantt_row:nth-child(even)) {
- background: linear-gradient(180deg, #ffffff 0%, #f9fafb 100%);
-}
-
-.schedule-gantt-container :deep(.gantt_grid_data .gantt_row:hover) {
- background: linear-gradient(180deg, #f3f4f6 0%, #e5e7eb 100%) !important;
-}
-
-.schedule-gantt-container :deep(.gantt_grid_data .gantt_cell) {
- text-overflow: clip;
- font-size: 13px;
- color: #4b5563;
- border-right: 1px solid #e5e7eb;
- padding: 0 12px;
- line-height: 36px;
- display: flex;
- align-items: center;
- justify-content: flex-start;
-}
-
-.schedule-gantt-container :deep(.gantt_scale_line) {
- border-bottom: 2px solid #d1d5db;
- background: linear-gradient(180deg, #ffffff 0%, #f9fafb 100%);
- display: flex;
- align-items: center;
- margin-bottom: 2px;
-}
-
-.schedule-gantt-container :deep(.gantt_scale_line:last-child) {
- margin-bottom: 0;
- border-bottom: 1px solid #e5e7eb;
-}
-
-.schedule-gantt-container :deep(.gantt_scale_cell) {
- font-size: 14px;
- font-weight: 600;
- color: #374151;
- border-right: 1px solid #e5e7eb;
- padding: 0 8px;
- text-align: center;
- background: linear-gradient(180deg, #ffffff 0%, #f9fafb 100%);
- display: flex;
- align-items: center;
- justify-content: center;
- height: 35px;
-}
-
-.schedule-gantt-container :deep(.gantt_scale_line:last-child .gantt_scale_cell) {
- font-size: 13px;
- font-weight: 500;
- color: #6b7280;
- background: linear-gradient(180deg, #f9fafb 0%, #f3f4f6 100%);
- border-bottom: 1px solid #e5e7eb;
- height: 35px;
-}
-
-.schedule-gantt-container :deep(.gantt_task_bg) {
- background: #ffffff;
-}
-
-.schedule-gantt-container :deep(.gantt_task_bg .gantt_task_cell) {
- border-right: 1px solid #f3f4f6;
- border-bottom: 1px solid #f3f4f6;
-}
-
-.schedule-gantt-container :deep(.gantt_task_bg .gantt_task_row:nth-child(even)) {
- background: #f9fafb;
-}
-
-.schedule-gantt-container :deep(.gantt_task_bg .gantt_task_row:hover) {
- background: #f3f4f6 !important;
-}
-
-.schedule-gantt-container :deep(.gantt_task_line) {
- border-radius: 10px;
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
- border: none;
-}
-
-.schedule-gantt-container :deep(.gantt_task_content) {
- font-size: 12px;
- font-weight: 500;
- color: #ffffff;
- padding: 0 12px;
- line-height: 32px;
- text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
-}
-
-.schedule-gantt-container :deep(.gantt_inline-editor-trigger) {
- display: inline-block;
- width: 100%;
- overflow: visible;
- cursor: pointer;
+.schedule-gantt-container :deep(.gantt_task_line.schedule-device-row) {
+ visibility: hidden;
+ pointer-events: none;
}
.schedule-gantt-container :deep(.gantt_task_line.schedule-plan-task) {
@@ -1193,14 +850,15 @@ onBeforeUnmount(() => {
}
:deep(.gantt_tooltip) div {
- margin-bottom: 6px !important;
+ margin-bottom: 4px;
}
:deep(.gantt_tooltip) div:last-child {
- margin-bottom: 0 !important;
+ margin-bottom: 0;
}
-:deep(.gantt_tree_content) {
- line-height: 50px;
+:deep(.gantt_tooltip) b {
+ font-size: 14px;
+ color: var(--el-text-color-primary);
}
diff --git a/src/views/mes/tasksummary/components/TaskScheduleDialog.vue b/src/views/mes/tasksummary/components/TaskScheduleDialog.vue
index 73eb984b..e06b122f 100644
--- a/src/views/mes/tasksummary/components/TaskScheduleDialog.vue
+++ b/src/views/mes/tasksummary/components/TaskScheduleDialog.vue
@@ -216,7 +216,8 @@