Merge remote-tracking branch 'origin/main'

main
liutao 8 hours ago
commit 2295788977

@ -17,9 +17,10 @@
/>
</el-select>
</ElDialog>
<div v-else class="custom-hover" @click.stop="showTopSearch = !showTopSearch">
<div v-else class="custom-hover" @click.stop="toggleTopSearch">
<Icon icon="ep:search" />
<el-select
ref="topSelectRef"
@click.stop
filterable
:reserve-keyword="false"
@ -51,7 +52,8 @@ defineProps({
const router = useRouter() //
const showSearch = ref(false) //
const showTopSearch = ref(false) //
const value: Ref = ref('') //
const value: Ref = ref('')
const topSelectRef = ref<InstanceType<typeof ElSelect>>()
const routers = router.getRoutes() //
const options = computed(() => {
@ -86,6 +88,15 @@ function hiddenTopSearch() {
showTopSearch.value = false
}
function toggleTopSearch() {
showTopSearch.value = !showTopSearch.value
if (showTopSearch.value) {
setTimeout(() => {
topSelectRef.value?.focus()
}, 600)
}
}
onMounted(() => {
window.addEventListener('keydown', listenKey)
window.addEventListener('click', hiddenTopSearch)

@ -4575,6 +4575,7 @@ export default {
planCountLabel: 'Plan Count',
planDetailTitle: 'Plan Details',
planCodeColon: 'Plan Code: ',
taskCodeColon: 'Task Code: ',
planNumberColon: 'Plan Qty: ',
deliveryDateColon: 'Delivery Date: ',
startColon: 'Start: ',
@ -4582,18 +4583,24 @@ export default {
latestStartColon: 'Latest Start: ',
emptyDescription: 'No Schedule Info',
adjustTaskTitle: 'Adjust Task',
taskLabel: 'Task',
taskPlaceholder: 'Please select task',
deviceLabel: 'Device',
devicePlaceholder: 'Please select device',
startDateLabel: 'Start Date',
startDatePlaceholder: 'Please select start date',
startDatePlaceholder: 'Please select plan start date',
endDateLabel: 'End Date',
endDatePlaceholder: 'Please select plan end date',
durationLabel: 'Days',
buttonCancel: 'Cancel',
buttonConfirm: 'Confirm',
editStartDateTitle: 'Edit Start Time',
startTimeLabel: 'Start Time',
startTimePlaceholder: 'Please select start time',
warningCompleteDeviceDate: 'Please complete device and start date',
capacityTypeLabel: 'Capacity Source',
warningCompleteDeviceDate: 'Please complete device, start date and end date',
warningValidTime: 'Please select a valid time',
warningEndBeforeStart: 'End time cannot be earlier than start time',
columnTaskName: 'Task Name',
columnDeviceName: 'Device Name',
columnPlanInfo: 'Plan Info',
@ -4618,7 +4625,18 @@ export default {
statusStarted: 'Started',
statusPaused: 'Paused',
statusPendingStorage: 'Pending Storage',
statusStored: 'Stored'
statusStored: 'Stored',
dialogTitle: 'Schedule Gantt Preview',
workerLabel: 'Worker',
workerPlaceholder: 'Please select worker',
calcLossLabel: 'Calculate Loss',
lockBtn: 'Lock',
unlockBtn: 'Unlock',
undoBtn: 'Undo',
buttonSave: 'Save',
buttonClose: 'Close',
warningNoPlanData: 'No plan data to save',
saveSuccess: 'Schedule saved successfully'
}
}
}

@ -4786,6 +4786,7 @@ export default {
planCountLabel: '计划条数',
planDetailTitle: '计划明细',
planCodeColon: '计划编码:',
taskCodeColon: '任务编码:',
planNumberColon: '计划数量:',
deliveryDateColon: '交货日期:',
startColon: '开始:',
@ -4793,18 +4794,24 @@ export default {
latestStartColon: '最晚开工:',
emptyDescription: '暂无计划信息',
adjustTaskTitle: '调整任务',
taskLabel: '任务',
taskPlaceholder: '请选择任务',
deviceLabel: '设备',
devicePlaceholder: '请选择设备',
startDateLabel: '开始日期',
startDatePlaceholder: '请选择开始日期',
startDatePlaceholder: '请选择计划开始日期',
endDateLabel: '结束日期',
endDatePlaceholder: '请选择计划结束日期',
durationLabel: '天数',
buttonCancel: '取消',
buttonConfirm: '确定',
editStartDateTitle: '修改开始时间',
startTimeLabel: '开始时间',
startTimePlaceholder: '请选择开始时间',
warningCompleteDeviceDate: '请完善设备和开始日期',
capacityTypeLabel: '产能来源',
warningCompleteDeviceDate: '请完善设备、计划开始日期和计划结束日期',
warningValidTime: '请选择有效的时间',
warningEndBeforeStart: '结束时间不能早于开始时间',
columnTaskName: '任务名称',
columnDeviceName: '设备名称',
columnPlanInfo: '计划信息',
@ -4829,7 +4836,18 @@ export default {
statusStarted: '已开工',
statusPaused: '暂停',
statusPendingStorage: '待入库',
statusStored: '已入库'
statusStored: '已入库',
dialogTitle: '排产甘特图预览',
workerLabel: '领料人',
workerPlaceholder: '请选择领料人',
calcLossLabel: '是否计算损耗',
lockBtn: '锁定',
unlockBtn: '解锁',
undoBtn: '撤回',
buttonSave: '保存',
buttonClose: '关闭',
warningNoPlanData: '暂无可保存的计划数据',
saveSuccess: '排产计划保存成功'
}
}
}

@ -37,7 +37,7 @@
{{ PLAN_STATUS_COLOR_MAP[plan.planStatus].label }}
</span>
</div>
<div>{{ t('GanttChart.GanttPanel.planCodeColon') }}{{ plan.taskCode ?? '-' }}</div>
<div>{{ t('GanttChart.GanttPanel.planCodeColon') }}{{ plan.planCode ?? '-' }}</div>
<div>{{ t('GanttChart.GanttPanel.planNumberColon') }}{{ plan.planNumber ?? '-' }}</div>
<div>{{ t('GanttChart.GanttPanel.deliveryDateColon') }}{{ plan.deliveryDateStr ?? '-' }}</div>
<div>{{ t('GanttChart.GanttPanel.startColon') }}{{ plan.planStartTimeStr || '-' }}</div>

@ -4,18 +4,18 @@
<div ref="ganttContainerRef" class="schedule-gantt-container" :style="{ height }"></div>
</div>
<div class="schedule-detail-panel">
<div class="schedule-detail-title">计划信息</div>
<div class="schedule-detail-title">{{ t('GanttChart.GanttPanel.detailTitle') }}</div>
<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 v-if="activePreviewDevice.capacityType !== undefined" label="产能来源">{{ getCapacityTypeLabel(activePreviewDevice.capacityType) }}</el-descriptions-item>
<el-descriptions-item v-if="activePreviewDevice.capacityType === 1" label="产能">{{ activePreviewDevice.ratedCapacity ?? '-' }}</el-descriptions-item>
<el-descriptions-item v-if="'dailyAverageValue' in activePreviewDevice" label="每日报工平均值">{{ activePreviewDevice.dailyAverageValue ?? '-' }}</el-descriptions-item>
<el-descriptions-item v-if="'dataCollectionCapacity' in activePreviewDevice" label="数据采集产能">{{ activePreviewDevice.dataCollectionCapacity ?? '-' }}</el-descriptions-item>
<el-descriptions-item label="计划条数">{{ activePreviewDevice.plans?.length ?? 0 }}</el-descriptions-item>
<el-descriptions-item :label="t('GanttChart.GanttPanel.deviceNameLabel')">{{ activePreviewDevice.deviceName }}</el-descriptions-item>
<el-descriptions-item :label="t('GanttChart.GanttPanel.deviceIdLabel')">{{ activePreviewDevice.deviceId }}</el-descriptions-item>
<el-descriptions-item v-if="activePreviewDevice.capacityType !== undefined" :label="t('GanttChart.GanttPanel.capacityTypeLabel')">{{ getCapacityTypeLabel(activePreviewDevice.capacityType) }}</el-descriptions-item>
<el-descriptions-item v-if="activePreviewDevice.capacityType === 1" :label="t('GanttChart.GanttPanel.capacityLabel')">{{ activePreviewDevice.ratedCapacity ?? '-' }}</el-descriptions-item>
<el-descriptions-item v-if="'dailyAverageValue' in activePreviewDevice" :label="t('GanttChart.GanttPanel.dailyAvgLabel')">{{ activePreviewDevice.dailyAverageValue ?? '-' }}</el-descriptions-item>
<el-descriptions-item v-if="'dataCollectionCapacity' in activePreviewDevice" :label="t('GanttChart.GanttPanel.dataCollectionCapacityLabel')">{{ activePreviewDevice.dataCollectionCapacity ?? '-' }}</el-descriptions-item>
<el-descriptions-item :label="t('GanttChart.GanttPanel.planCountLabel')">{{ activePreviewDevice.plans?.length ?? 0 }}</el-descriptions-item>
</el-descriptions>
<div class="schedule-plan-list-title">计划明细</div>
<div class="schedule-plan-list-title">{{ t('GanttChart.GanttPanel.planDetailTitle') }}</div>
<div class="schedule-plan-list">
<div
v-for="(plan, index) in activePreviewTask ? [activePreviewTask] : (activePreviewDevice?.plans ?? [])"
@ -25,53 +25,57 @@
<div class="schedule-plan-item-head">
<span class="schedule-plan-item-title">{{ plan.productCode ?? '-' }} / {{ plan.productName ?? '-' }}</span>
</div>
<div>计划编码{{ plan.taskCode ?? '-' }}</div>
<div>计划数量{{ plan.planNumber ?? '-' }}</div>
<div>{{ t('GanttChart.GanttPanel.taskCodeColon') }}{{ plan.taskCode ?? '-' }}</div>
<div>{{ t('GanttChart.GanttPanel.planNumberColon') }}{{ plan.planNumber ?? '-' }}</div>
<template v-if="activePreviewDevice.capacityType !== 1">
<div>产能{{ plan.ratedCapacity ?? '-' }}</div>
<div>产能来源{{ getCapacityTypeLabel(plan.capacityType) }}</div>
<div>{{ t('GanttChart.GanttPanel.capacityLabel') }}{{ plan.ratedCapacity ?? '-' }}</div>
<div>{{ t('GanttChart.GanttPanel.capacityTypeLabel') }}{{ getCapacityTypeLabel(plan.capacityType) }}</div>
</template>
<div>交货日期{{ plan.deliveryDateStr ?? '-' }}</div>
<div>开始{{ plan.planStartTimeStr || '-' }}</div>
<div>结束{{ plan.planEndTimeStr || '-' }}</div>
<div>最晚开工{{ plan.latestStartTimeStr || '-' }}</div>
<div>{{ t('GanttChart.GanttPanel.deliveryDateColon') }}{{ plan.deliveryDateStr ?? '-' }}</div>
<div>{{ t('GanttChart.GanttPanel.startColon') }}{{ plan.planStartTimeStr || '-' }}</div>
<div>{{ t('GanttChart.GanttPanel.endColon') }}{{ plan.planEndTimeStr || '-' }}</div>
<div>{{ t('GanttChart.GanttPanel.latestStartColon') }}{{ plan.latestStartTimeStr || '-' }}</div>
</div>
</div>
</template>
<el-empty v-else description="暂无计划信息" :image-size="80" />
<el-empty v-else :description="t('GanttChart.GanttPanel.emptyDescription')" :image-size="80" />
</div>
</div>
<el-dialog v-model="taskAdjustDialogVisible" title="调整任务" width="420px" append-to-body>
<el-dialog v-model="taskAdjustDialogVisible" :title="t('GanttChart.GanttPanel.adjustTaskTitle')" width="420px" append-to-body draggable>
<el-form label-width="110px">
<el-form-item label="设备">
<el-select v-model="taskAdjustForm.deviceTaskId" placeholder="请选择设备" class="!w-full">
<el-form-item v-if="adjustFromGrid" :label="t('GanttChart.GanttPanel.taskLabel')">
<el-select v-model="taskAdjustForm.planIdentity" :placeholder="t('GanttChart.GanttPanel.taskPlaceholder')" class="!w-full" @change="handleAdjustPlanChange">
<el-option v-for="item in currentDevicePlanOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item :label="t('GanttChart.GanttPanel.deviceLabel')">
<el-select v-model="taskAdjustForm.deviceTaskId" :placeholder="t('GanttChart.GanttPanel.devicePlaceholder')" 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-form-item :label="t('GanttChart.GanttPanel.startDateLabel')">
<el-date-picker
v-model="taskAdjustForm.startDate"
type="datetime"
value-format="YYYY-MM-DD HH:mm:ss"
placeholder="请选择计划开始日期"
:placeholder="t('GanttChart.GanttPanel.startDatePlaceholder')"
class="!w-full"
/>
</el-form-item>
<el-form-item label="计划结束日期">
<el-form-item :label="t('GanttChart.GanttPanel.endDateLabel')">
<el-date-picker
v-model="taskAdjustForm.endDate"
type="datetime"
value-format="YYYY-MM-DD HH:mm:ss"
placeholder="请选择计划结束日期"
:placeholder="t('GanttChart.GanttPanel.endDatePlaceholder')"
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>
<el-button @click="taskAdjustDialogVisible = false">{{ t('GanttChart.GanttPanel.buttonCancel') }}</el-button>
<el-button type="primary" @click="handleTaskAdjustSubmit">{{ t('GanttChart.GanttPanel.buttonConfirm') }}</el-button>
</template>
</el-dialog>
</template>
@ -81,16 +85,21 @@ import dayjs from 'dayjs'
import { gantt } from 'dhtmlx-gantt'
import 'dhtmlx-gantt/codebase/dhtmlxgantt.css'
import { getDictOptions } from '@/utils/dict'
import { useI18n } from '@/hooks/web/useI18n'
defineOptions({ name: 'ScheduleGanttPanelEditable' })
const { t } = useI18n()
const props = withDefaults(
defineProps<{
scheduleList: any[]
height?: string
editable?: boolean
}>(),
{
height: '800px'
height: '800px',
editable: false
}
)
@ -101,12 +110,26 @@ const activePreviewTask = ref<any>()
const ganttEventIds = ref<string[]>([])
const ganttSyncing = ref(false)
const taskAdjustDialogVisible = ref(false)
const adjustFromGrid = ref(false)
const taskAdjustForm = reactive({
deviceTaskId: '',
planIdentity: '',
startDate: '',
endDate: ''
})
const editingPlanIdentity = ref<string | null>(null)
const undoStack = ref<string[]>([])
const saveUndoSnapshot = () => {
const data = previewScheduleList.value.map((device: any) => ({
deviceId: device.deviceId,
deviceName: device.deviceName,
capacityType: device.capacityType,
ratedCapacity: device.ratedCapacity,
plans: (device.plans ?? []).map((plan: any) => ({ ...plan }))
}))
undoStack.value.push(JSON.stringify(data))
}
const getCapacityTypeLabel = (value: any) => {
if (value === undefined || value === null) return '-'
@ -123,6 +146,27 @@ const previewDeviceOptions = computed(() =>
}))
)
const currentDevicePlanOptions = computed(() => {
const device = previewScheduleList.value.find(
(d: any) => `device-${d.deviceId}` === taskAdjustForm.deviceTaskId
)
if (!device) return []
return (device.plans ?? [])
.filter((plan: any) => String(plan.sourceType ?? '').toUpperCase() === 'CURRENT')
.map((plan: any) => ({
label: `${plan.productCode ?? '-'} / ${plan.productName ?? '-'} / ${plan.taskCode ?? '-'}`,
value: makePlanIdentity(plan)
}))
})
const handleAdjustPlanChange = (identity: string) => {
const found = findOriginalPlan(identity)
if (found) {
taskAdjustForm.startDate = found.plan.planStartTimeStr || ''
taskAdjustForm.endDate = found.plan.planEndTimeStr || ''
}
}
const hasCurrentPlan = (device: any) =>
(device?.plans ?? []).some((plan: any) => String(plan.sourceType ?? '').toUpperCase() === 'CURRENT')
@ -208,14 +252,14 @@ const clearCustomPlanBars = () => {
const buildPlanBarTooltipHtml = (plan: any, device: any) => {
return `
<div><b>任务明细</b></div>
<div>设备${device?.deviceName ?? '-'}</div>
<div>任务单${plan.taskCode ?? '-'}</div>
<div>产品${plan.productCode ?? '-'} / ${plan.productName ?? '-'}</div>
<div>计划数量${plan.planNumber ?? '-'}</div>
<div>开始${formatTooltipDateTime(plan.planStartTimeStr)}</div>
<div>结束${formatTooltipDateTime(plan.planEndTimeStr)}</div>
<div>最晚开工${formatTooltipDateTime(plan.latestStartTimeStr)}</div>
<div><b>${t('GanttChart.GanttPanel.tooltipTaskDetail')}</b></div>
<div>${t('GanttChart.GanttPanel.tooltipDevice')}${device?.deviceName ?? '-'}</div>
<div>${t('GanttChart.GanttPanel.tooltipTaskCode')}${plan.taskCode ?? '-'}</div>
<div>${t('GanttChart.GanttPanel.tooltipProduct')}${plan.productCode ?? '-'} / ${plan.productName ?? '-'}</div>
<div>${t('GanttChart.GanttPanel.tooltipPlanNumber')}${plan.planNumber ?? '-'}</div>
<div>${t('GanttChart.GanttPanel.tooltipStart')}${formatTooltipDateTime(plan.planStartTimeStr)}</div>
<div>${t('GanttChart.GanttPanel.tooltipEnd')}${formatTooltipDateTime(plan.planEndTimeStr)}</div>
<div>${t('GanttChart.GanttPanel.tooltipLatestStart')}${formatTooltipDateTime(plan.latestStartTimeStr)}</div>
`
}
@ -286,7 +330,7 @@ const renderCustomPlanBars = () => {
height: ${barHeight}px;
border-radius: 6px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
cursor: ${isCurrent ? 'grab' : 'pointer'};
cursor: ${isCurrent && props.editable ? 'grab' : 'pointer'};
z-index: 2;
display: flex;
align-items: center;
@ -350,12 +394,12 @@ const renderCustomPlanBars = () => {
})
bar.addEventListener('contextmenu', (e) => {
if (!isCurrent) { e.preventDefault(); return }
if (!isCurrent || !props.editable) { e.preventDefault(); return }
e.preventDefault()
openTaskAdjustDialogForPlan(plan, task._deviceData)
})
if (isCurrent) {
if (isCurrent && props.editable) {
attachPlanBarDrag(bar, plan, task)
attachPlanBarResize(bar, plan, task)
}
@ -418,6 +462,8 @@ const attachPlanBarDrag = (bar: HTMLElement, plan: any, task: any) => {
if (!moved) return
moved = false
saveUndoSnapshot()
const dx = e.clientX - startX
const newLeft = Math.max(0, startLeft + dx)
const newStartDate = gantt.dateFromPos(newLeft)
@ -522,6 +568,8 @@ const attachPlanBarResize = (bar: HTMLElement, plan: any, task: any) => {
resizing = false
bar.style.zIndex = '2'
saveUndoSnapshot()
const newLeft = parseFloat(bar.style.left) || startLeft
const newWidth = parseFloat(bar.style.width) || startWidth
const newStartDate = gantt.dateFromPos(newLeft)
@ -546,19 +594,45 @@ const attachPlanBarResize = (bar: HTMLElement, plan: any, task: any) => {
}
const openTaskAdjustDialogForPlan = (plan: any, device: any) => {
adjustFromGrid.value = false
taskAdjustForm.deviceTaskId = `device-${device?.deviceId ?? ''}`
taskAdjustForm.planIdentity = makePlanIdentity(plan)
taskAdjustForm.startDate = plan.planStartTimeStr || ''
taskAdjustForm.endDate = plan.planEndTimeStr || ''
editingPlanIdentity.value = makePlanIdentity(plan)
taskAdjustDialogVisible.value = true
}
const openTaskAdjustDialogFromGrid = (device: any) => {
adjustFromGrid.value = true
taskAdjustForm.deviceTaskId = `device-${device?.deviceId ?? ''}`
taskAdjustForm.planIdentity = ''
taskAdjustForm.startDate = ''
taskAdjustForm.endDate = ''
editingPlanIdentity.value = null
const currentPlans = (device?.plans ?? []).filter(
(plan: any) => String(plan.sourceType ?? '').toUpperCase() === 'CURRENT'
)
if (currentPlans.length === 1) {
const plan = currentPlans[0]
taskAdjustForm.planIdentity = makePlanIdentity(plan)
taskAdjustForm.startDate = plan.planStartTimeStr || ''
taskAdjustForm.endDate = plan.planEndTimeStr || ''
editingPlanIdentity.value = makePlanIdentity(plan)
}
taskAdjustDialogVisible.value = true
}
const handleTaskAdjustSubmit = () => {
if (!taskAdjustForm.deviceTaskId || !taskAdjustForm.startDate || !taskAdjustForm.endDate) {
message.warning('请完善设备、计划开始日期和计划结束日期')
message.warning(t('GanttChart.GanttPanel.warningCompleteDeviceDate'))
return
}
if (!editingPlanIdentity.value) return
const planIdentity = adjustFromGrid.value ? taskAdjustForm.planIdentity : editingPlanIdentity.value
if (!planIdentity) return
saveUndoSnapshot()
const targetDevice = previewScheduleList.value.find(
(d: any) => `device-${d.deviceId}` === taskAdjustForm.deviceTaskId
@ -569,7 +643,7 @@ const handleTaskAdjustSubmit = () => {
let sourceDevice: any = null
for (const device of previewScheduleList.value) {
const plan = (device?.plans ?? []).find(
(p: any) => makePlanIdentity(p) === editingPlanIdentity.value
(p: any) => makePlanIdentity(p) === planIdentity
)
if (plan) {
foundPlan = plan
@ -582,11 +656,11 @@ const handleTaskAdjustSubmit = () => {
const newStart = dayjs(taskAdjustForm.startDate)
const newEnd = dayjs(taskAdjustForm.endDate)
if (!newStart.isValid() || !newEnd.isValid()) {
message.warning('请选择有效的时间')
message.warning(t('GanttChart.GanttPanel.warningValidTime'))
return
}
if (newEnd.isBefore(newStart)) {
message.warning('结束时间不能早于开始时间')
message.warning(t('GanttChart.GanttPanel.warningEndBeforeStart'))
return
}
@ -639,7 +713,7 @@ const initGanttPreview = () => {
gantt.config.columns = [
{
name: 'text',
label: '设备名称',
label: t('GanttChart.GanttPanel.columnDeviceName'),
tree: false,
width: '*',
min_width: 100,
@ -647,7 +721,7 @@ const initGanttPreview = () => {
},
{
name: 'duration',
label: '天数',
label: t('GanttChart.GanttPanel.columnDays'),
align: 'center',
width: 80,
template: (task: any) => String(task.totalDays ?? task.duration ?? 0)
@ -655,7 +729,7 @@ const initGanttPreview = () => {
]
gantt.config.scales = [
{ unit: 'month', step: 1, format: (date) => dayjs(date).format('YYYY年M月') },
{ unit: 'month', step: 1, format: (date) => dayjs(date).format(t('GanttChart.GanttPanel.scaleMonthFormat')) },
{ unit: 'day', step: 1, format: (date) => dayjs(date).format('MM-DD') }
]
@ -704,6 +778,15 @@ const initGanttPreview = () => {
})
ganttEventIds.value.push(clickEventId)
const dblClickEventId = gantt.attachEvent('onTaskDblClick', (id) => {
const ganttTask = gantt.getTask(id)
if (ganttTask?._deviceData && props.editable) {
openTaskAdjustDialogFromGrid(ganttTask._deviceData)
}
return false
})
ganttEventIds.value.push(dblClickEventId)
nextTick(() => {
renderCustomPlanBars()
})
@ -717,6 +800,7 @@ const initGanttPreview = () => {
}
onMounted(async () => {
undoStack.value = []
await nextTick()
initGanttPreview()
})
@ -735,6 +819,30 @@ watch(
onBeforeUnmount(() => {
destroyGantt()
})
const undo = () => {
if (!undoStack.value.length) return
const saved = JSON.parse(undoStack.value.pop()!)
for (const savedDevice of saved) {
const originalDevice = previewScheduleList.value.find(
(d: any) => d.deviceId === savedDevice.deviceId
)
if (originalDevice) {
originalDevice.plans.splice(0, originalDevice.plans.length, ...savedDevice.plans)
}
}
initGanttPreview()
}
watch(
() => props.editable,
async () => {
await nextTick()
renderCustomPlanBars()
}
)
defineExpose({ undo })
</script>
<style scoped>

@ -1,21 +1,33 @@
<template>
<Dialog v-model="previewVisible" title="排产甘特图预览" width="100%" align-center>
<Dialog v-model="previewVisible" :title="t('GanttChart.GanttPanel.dialogTitle')" width="100%" align-center>
<div class="preview-options">
<el-form :inline="true">
<el-form-item label="领料人">
<el-select v-model="scheduleOptions.workerId" clearable filterable placeholder="请选择领料人" style="width: 200px">
<el-form-item :label="t('GanttChart.GanttPanel.workerLabel')">
<el-select v-model="scheduleOptions.workerId" clearable filterable :placeholder="t('GanttChart.GanttPanel.workerPlaceholder')" style="width: 200px">
<el-option v-for="item in workerList" :key="item.id" :label="item.nickname" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="是否计算损耗">
<el-form-item :label="t('GanttChart.GanttPanel.calcLossLabel')">
<el-switch v-model="scheduleOptions.isCalculateLoss" />
</el-form-item>
<el-form-item>
<el-button :type="ganttEditable ? 'primary' : 'default'" @click="toggleGanttEditable">
<Icon :icon="ganttEditable ? 'ep:unlock' : 'ep:lock'" class="mr-4px" />
{{ ganttEditable ? t('GanttChart.GanttPanel.unlockBtn') : t('GanttChart.GanttPanel.lockBtn') }}
</el-button>
</el-form-item>
<el-form-item>
<el-button :disabled="!ganttEditable" @click="handleUndo">
<Icon icon="ep:refresh-left" class="mr-4px" />
{{ t('GanttChart.GanttPanel.undoBtn') }}
</el-button>
</el-form-item>
</el-form>
</div>
<ScheduleGanttPanelEditable :schedule-list="previewScheduleList" height="800px" />
<ScheduleGanttPanelEditable ref="ganttPanelRef" :schedule-list="previewScheduleList" :editable="ganttEditable" height="800px" />
<template #footer>
<el-button type="primary" :loading="previewSaveLoading" @click="handlePreviewSave"></el-button>
<el-button @click="previewVisible = false">关闭</el-button>
<el-button type="primary" :loading="previewSaveLoading" @click="handlePreviewSave">{{ t('GanttChart.GanttPanel.buttonSave') }}</el-button>
<el-button @click="previewVisible = false">{{ t('GanttChart.GanttPanel.buttonClose') }}</el-button>
</template>
</Dialog>
</template>
@ -25,9 +37,12 @@ import { PlanApi } from '@/api/mes/plan'
import * as UserApi from '@/api/system/user'
import ScheduleGanttPanelEditable from './ScheduleGanttPanelEditable.vue'
import dayjs from 'dayjs'
import { useI18n } from '@/hooks/web/useI18n'
defineOptions({ name: 'TaskSchedulePreviewDialog' })
const { t } = useI18n()
const props = defineProps<{
modelValue: boolean
scheduleList: any[]
@ -40,6 +55,16 @@ const emit = defineEmits<{
const message = useMessage()
const previewSaveLoading = ref(false)
const ganttEditable = ref(false)
const ganttPanelRef = ref<InstanceType<typeof ScheduleGanttPanelEditable>>()
const toggleGanttEditable = () => {
ganttEditable.value = !ganttEditable.value
}
const handleUndo = () => {
ganttPanelRef.value?.undo()
}
const workerList = ref<UserApi.UserVO[]>([])
const scheduleOptions = reactive({
@ -61,8 +86,11 @@ const previewVisible = computed({
})
watch(previewVisible, (visible) => {
if (visible && workerList.value.length === 0) {
loadWorkerList()
if (visible) {
if (workerList.value.length === 0) {
loadWorkerList()
}
ganttEditable.value = false
}
})
@ -100,14 +128,14 @@ const handlePreviewSave = async () => {
})
if (!createReqVOList.length) {
message.warning('暂无可保存的计划数据')
message.warning(t('GanttChart.GanttPanel.warningNoPlanData'))
return
}
previewSaveLoading.value = true
try {
await PlanApi.createBatch({ createReqVOList })
message.success('排产计划保存成功')
message.success(t('GanttChart.GanttPanel.saveSuccess'))
previewVisible.value = false
emit('saved')
} finally {

Loading…
Cancel
Save