|
|
|
|
@ -33,6 +33,31 @@
|
|
|
|
|
<el-button @click="previewVisible = false">关闭</el-button>
|
|
|
|
|
</template>
|
|
|
|
|
</Dialog>
|
|
|
|
|
<el-dialog v-model="taskAdjustDialogVisible" title="调整任务" width="420px" append-to-body>
|
|
|
|
|
<el-form label-width="90px">
|
|
|
|
|
<el-form-item label="设备">
|
|
|
|
|
<el-select v-model="taskAdjustForm.deviceTaskId" placeholder="请选择设备" class="!w-full">
|
|
|
|
|
<el-option v-for="item in previewDeviceOptions" :key="item.value" :label="item.label" :value="item.value" />
|
|
|
|
|
</el-select>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="开始日期">
|
|
|
|
|
<el-date-picker
|
|
|
|
|
v-model="taskAdjustForm.startDate"
|
|
|
|
|
type="date"
|
|
|
|
|
value-format="YYYY-MM-DD"
|
|
|
|
|
placeholder="请选择开始日期"
|
|
|
|
|
class="!w-full"
|
|
|
|
|
/>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="天数">
|
|
|
|
|
<el-input-number v-model="taskAdjustForm.duration" :min="1" :max="365" class="!w-full" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-form>
|
|
|
|
|
<template #footer>
|
|
|
|
|
<el-button @click="taskAdjustDialogVisible = false">取消</el-button>
|
|
|
|
|
<el-button type="primary" @click="handleTaskAdjustSubmit">确定</el-button>
|
|
|
|
|
</template>
|
|
|
|
|
</el-dialog>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
@ -58,6 +83,16 @@ const previewSaveLoading = ref(false)
|
|
|
|
|
const ganttContainerRef = ref<HTMLDivElement>()
|
|
|
|
|
const activePreviewDevice = ref<any>()
|
|
|
|
|
const ganttEventIds = ref<string[]>([])
|
|
|
|
|
const ganttSyncing = ref(false)
|
|
|
|
|
const taskAdjustDialogVisible = ref(false)
|
|
|
|
|
const taskAdjustTaskId = ref<string | number | null>(null)
|
|
|
|
|
const taskAdjustForm = reactive({
|
|
|
|
|
deviceTaskId: '',
|
|
|
|
|
startDate: '',
|
|
|
|
|
duration: 1
|
|
|
|
|
})
|
|
|
|
|
const taskMoveFromParentMap = ref<Record<string, string | number | undefined>>({})
|
|
|
|
|
const taskDragDurationMap = ref<Record<string, number>>({})
|
|
|
|
|
|
|
|
|
|
const previewVisible = computed({
|
|
|
|
|
get: () => props.modelValue,
|
|
|
|
|
@ -65,6 +100,12 @@ const previewVisible = computed({
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const previewScheduleList = computed(() => (Array.isArray(props.scheduleList) ? props.scheduleList : []))
|
|
|
|
|
const previewDeviceOptions = computed(() =>
|
|
|
|
|
previewScheduleList.value.map((device: any) => ({
|
|
|
|
|
label: device?.deviceName ?? '-',
|
|
|
|
|
value: `device-${device?.deviceId}`
|
|
|
|
|
}))
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const getGlobalDateRange = (scheduleList: any[]) => {
|
|
|
|
|
const allPlans = scheduleList.flatMap((item: any) => item?.plans ?? [])
|
|
|
|
|
@ -191,11 +232,14 @@ 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
|
|
|
|
|
if (!start.isValid()) return
|
|
|
|
|
const duration = Number(task.duration) > 0 ? Number(task.duration) : 1
|
|
|
|
|
const nextEnd = start.startOf('day').add(duration - 1, 'day').endOf('day')
|
|
|
|
|
task.end_date = nextEnd.toDate()
|
|
|
|
|
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 = Math.max(end.endOf('day').diff(start.startOf('day'), 'day') + 1, 1)
|
|
|
|
|
task._planData.planEndTimeStr = nextEnd.format('YYYY-MM-DD HH:mm:ss')
|
|
|
|
|
task._planData.scheduleDays = duration
|
|
|
|
|
activePreviewDevice.value = task._deviceData
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -216,6 +260,153 @@ const syncDeviceTaskRangeFromChildren = (task: any) => {
|
|
|
|
|
gantt.updateTask(deviceTaskId)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
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) => {
|
|
|
|
|
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
|
|
|
|
|
})
|
|
|
|
|
let previousEnd: dayjs.Dayjs | null = null
|
|
|
|
|
childTasks.forEach((item: any) => {
|
|
|
|
|
const start = dayjs(item.start_date).startOf('day')
|
|
|
|
|
const end = dayjs(item.end_date).endOf('day')
|
|
|
|
|
const duration = Math.max(end.diff(start, 'day') + 1, 1)
|
|
|
|
|
const nextStart = previousEnd && !start.isAfter(previousEnd, 'day') ? previousEnd.add(1, 'day').startOf('day') : start
|
|
|
|
|
const nextEnd = nextStart.add(duration - 1, 'day').endOf('day')
|
|
|
|
|
item.start_date = nextStart.toDate()
|
|
|
|
|
item.end_date = nextEnd.toDate()
|
|
|
|
|
item.duration = duration
|
|
|
|
|
syncPlanTimeFromTask(item)
|
|
|
|
|
gantt.updateTask(item.id)
|
|
|
|
|
previousEnd = nextEnd
|
|
|
|
|
})
|
|
|
|
|
const fakePlanTask = { _planData: true, parent: deviceTaskId }
|
|
|
|
|
syncDeviceTaskRangeFromChildren(fakePlanTask)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const refreshPlanLinksByRowOrder = () => {
|
|
|
|
|
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) => {
|
|
|
|
|
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
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
return childTaskIds.length
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const applyTaskAdjust = (task: any, targetDeviceTaskId: string | number, startDate: string, durationValue: number) => {
|
|
|
|
|
const targetDeviceTask = gantt.getTask(targetDeviceTaskId)
|
|
|
|
|
if (!targetDeviceTask || targetDeviceTask?._planData) return
|
|
|
|
|
const sourceDeviceTaskId = task.parent
|
|
|
|
|
const nextStart = dayjs(startDate).startOf('day')
|
|
|
|
|
const duration = Math.max(Number(durationValue) || 1, 1)
|
|
|
|
|
task.parent = targetDeviceTaskId
|
|
|
|
|
task.start_date = nextStart.toDate()
|
|
|
|
|
task.end_date = nextStart.add(duration - 1, 'day').endOf('day').toDate()
|
|
|
|
|
task.duration = duration
|
|
|
|
|
movePlanDataToDevice(task, targetDeviceTaskId, sourceDeviceTaskId)
|
|
|
|
|
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) {
|
|
|
|
|
normalizeDeviceChildren(sourceDeviceTaskId)
|
|
|
|
|
}
|
|
|
|
|
refreshPlanLinksByRowOrder()
|
|
|
|
|
refreshTimelineRangeByTasks()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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')
|
|
|
|
|
taskAdjustForm.duration = Math.max(Number(task.duration) || 1, 1)
|
|
|
|
|
taskAdjustDialogVisible.value = true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const handleTaskAdjustSubmit = () => {
|
|
|
|
|
if (!taskAdjustTaskId.value) return
|
|
|
|
|
if (!taskAdjustForm.deviceTaskId || !taskAdjustForm.startDate) {
|
|
|
|
|
message.warning('请完善设备和开始日期')
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
const task = gantt.getTask(taskAdjustTaskId.value)
|
|
|
|
|
if (!task?._planData) return
|
|
|
|
|
ganttSyncing.value = true
|
|
|
|
|
try {
|
|
|
|
|
applyTaskAdjust(task, taskAdjustForm.deviceTaskId, taskAdjustForm.startDate, taskAdjustForm.duration)
|
|
|
|
|
} finally {
|
|
|
|
|
ganttSyncing.value = false
|
|
|
|
|
}
|
|
|
|
|
taskAdjustDialogVisible.value = false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const refreshTimelineRangeByTasks = () => {
|
|
|
|
|
let minStart = Number.POSITIVE_INFINITY
|
|
|
|
|
let maxEnd = Number.NEGATIVE_INFINITY
|
|
|
|
|
@ -252,6 +443,8 @@ const initGanttPreview = () => {
|
|
|
|
|
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 = 40
|
|
|
|
|
@ -356,27 +549,101 @@ const initGanttPreview = () => {
|
|
|
|
|
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 dragEventId = gantt.attachEvent('onAfterTaskDrag', (id) => {
|
|
|
|
|
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
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
syncDeviceTaskRangeFromChildren(task)
|
|
|
|
|
normalizeDeviceChildren(parent)
|
|
|
|
|
normalizeDeviceChildren(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
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const updateEventId = gantt.attachEvent('onAfterTaskUpdate', (id) => {
|
|
|
|
|
if (ganttSyncing.value) return
|
|
|
|
|
const task = gantt.getTask(id)
|
|
|
|
|
ganttSyncing.value = true
|
|
|
|
|
try {
|
|
|
|
|
syncPlanTimeFromTask(task)
|
|
|
|
|
syncDeviceTaskRangeFromChildren(task)
|
|
|
|
|
normalizeDeviceChildren(task.parent)
|
|
|
|
|
refreshPlanLinksByRowOrder()
|
|
|
|
|
refreshTimelineRangeByTasks()
|
|
|
|
|
gantt.refreshTask(id)
|
|
|
|
|
} finally {
|
|
|
|
|
ganttSyncing.value = false
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
ganttEventIds.value.push(clickEventId, beforeDragEventId, dragEventId, updateEventId)
|
|
|
|
|
ganttEventIds.value.push(
|
|
|
|
|
clickEventId,
|
|
|
|
|
beforeDragEventId,
|
|
|
|
|
beforeMoveEventId,
|
|
|
|
|
afterMoveEventId,
|
|
|
|
|
contextMenuEventId,
|
|
|
|
|
dragEventId,
|
|
|
|
|
updateEventId
|
|
|
|
|
)
|
|
|
|
|
refreshPlanLinksByRowOrder()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const handlePreviewSave = async () => {
|
|
|
|
|
const createReqVOList = previewScheduleList.value.flatMap((device: any) => {
|
|
|
|
|
const plans = Array.isArray(device?.plans) ? device.plans : []
|
|
|
|
|
|