feat(mes): 优化生产任务保存请求和验证逻辑

调整生产任务保存请求的字段为可选,简化编辑时的请求参数。更新任务验证逻辑,确保在更新时只校验存在的字段,提升用户体验和代码可维护性。同时,新增甘特图编辑页面,支持批量保存任务修改。
pull/871/MERGE
YunaiV 2026-03-16 23:28:27 +08:00
parent 98c8b9a5cf
commit 0a0cd5f165
4 changed files with 102 additions and 101 deletions

View File

@ -127,43 +127,6 @@ const remainingRouter: AppRouteRecordRaw[] = [
}
]
},
{
path: '/mes',
component: Layout,
name: 'MesWmRouter',
meta: {
hidden: true
},
children: [
{
path: 'wm/warehouse/location',
component: () => import('@/views/mes/wm/warehouse/location/index.vue'),
name: 'MesWmLocation',
meta: {
noCache: true,
hidden: true,
canTo: true,
icon: '',
title: '库区设置',
activeMenu: '/mes/wm/warehouse'
}
},
{
path: 'wm/warehouse/area',
component: () => import('@/views/mes/wm/warehouse/area/index.vue'),
name: 'MesWmArea',
meta: {
noCache: true,
hidden: true,
canTo: true,
icon: '',
title: '库位设置',
activeMenu: '/mes/wm/warehouse'
}
}
]
},
{
path: '/codegen',
component: Layout,
@ -270,6 +233,16 @@ const remainingRouter: AppRouteRecordRaw[] = [
noTagsView: true
}
},
{
path: '/:pathMatch(.*)*',
component: () => import('@/views/Error/404.vue'),
name: '',
meta: {
title: '404',
hidden: true,
breadcrumb: false
}
},
{
path: '/bpm',
component: Layout,
@ -730,16 +703,6 @@ const remainingRouter: AppRouteRecordRaw[] = [
}
]
},
{
path: '/:pathMatch(.*)*',
component: () => import('@/views/Error/404.vue'),
name: '',
meta: {
title: '404',
hidden: true,
breadcrumb: false
}
},
{
path: '/iot',
component: Layout,
@ -782,6 +745,55 @@ const remainingRouter: AppRouteRecordRaw[] = [
component: () => import('@/views/iot/ota/firmware/detail/index.vue')
}
]
},
{
path: '/mes',
component: Layout,
name: 'MesWmRouter',
meta: {
hidden: true
},
children: [
{
path: 'wm/warehouse/location',
component: () => import('@/views/mes/wm/warehouse/location/index.vue'),
name: 'MesWmLocation',
meta: {
noCache: true,
hidden: true,
canTo: true,
icon: '',
title: '库区设置',
activeMenu: '/mes/wm/warehouse'
}
},
{
path: 'wm/warehouse/area',
component: () => import('@/views/mes/wm/warehouse/area/index.vue'),
name: 'MesWmArea',
meta: {
noCache: true,
hidden: true,
canTo: true,
icon: '',
title: '库位设置',
activeMenu: '/mes/wm/warehouse'
}
},
{
path: 'pro/task/gantt-edit',
component: () => import('@/views/mes/pro/task/edit/index.vue'),
name: 'MesProTaskGanttEdit',
meta: {
noCache: true,
hidden: true,
canTo: true,
icon: '',
title: '甘特图编辑',
activeMenu: '/mes/pro/task'
}
}
]
}
]

View File

@ -64,10 +64,16 @@ const initGantt = () => {
gantt.config.show_progress = true // KTG gantt progress
gantt.config.open_tree_initially = true // KTG open_tree_initially = true
gantt.config.auto_types = false // projectKTG auto_types = false
gantt.config.drag_resize = false //
gantt.config.drag_move = false //
gantt.config.drag_progress = false // KTG drag_progress = false
// xml_date date_format
gantt.config.drag_resize = false //
gantt.config.drag_progress = false //
// lightbox
gantt.config.lightbox.sections = [
{ name: 'time', type: 'duration', map_to: 'auto' }
]
gantt.config.buttons_left = ['gantt_save_btn']
gantt.config.buttons_right = ['gantt_cancel_btn']
// > > 8
const weekScaleTemplate = (date: Date) => {
@ -131,19 +137,18 @@ const initGantt = () => {
}
return ''
}
gantt.templates.task_cell_class = () => '' // KTG gantt
gantt.templates.timeline_cell_class = () => '' // gantt
gantt.templates.task_row_class = () => '' // KTG gantt
//
// KTG onAfterTaskUpdate id onAfterTaskDrag emit
// lightbox
if (!props.readonly) {
gantt.attachEvent('onAfterTaskDrag', (id: string | number) => {
gantt.attachEvent('onAfterTaskUpdate', (id: string | number) => {
const task = gantt.getTask(id)
emit('task-update', {
id: task.originalId || id,
startTime: task.start_date,
endTime: task.end_date,
duration: (task.duration as number) / 8 //
duration: task.duration
})
})
}

View File

@ -1,16 +1,14 @@
<!-- MES 甘特图编辑全屏 Dialog -->
<!-- MES 甘特图编辑 -->
<template>
<Dialog title="甘特图编辑" v-model="dialogVisible" fullscreen>
<ContentWrap>
<div class="mb-10px flex items-center justify-between">
<span class="text-14px text-gray-500">
拖拽任务条可调整开始时间和时长修改后点击"批量保存"
</span>
<span class="text-14px text-gray-500">双击任务条可编辑开始时间和时长修改后点击"批量保存"</span>
<div>
<el-badge :value="pendingCount" :hidden="pendingCount === 0" class="mr-10px">
<el-button
type="primary"
@click="handleSave"
:loading="saving"
:loading="formLoading"
:disabled="pendingCount === 0"
>
批量保存
@ -20,57 +18,44 @@
</div>
</div>
<GanttChart
ref="ganttRef"
:tasks="taskList"
:readonly="false"
:height="ganttHeight"
@task-update="handleTaskUpdate"
/>
</Dialog>
</ContentWrap>
</template>
<script setup lang="ts">
import { ProTaskApi, ProTaskVO } from '@/api/mes/pro/task'
import GanttChart from './components/GanttChart.vue'
import { ProTaskApi } from '@/api/mes/pro/task'
import GanttChart from '../components/GanttChart.vue'
defineOptions({ name: 'GanttEdit' })
defineOptions({ name: 'MesProTaskGanttEdit' })
const message = useMessage()
const message = useMessage() //
const dialogVisible = ref(false)
const taskList = ref<ProTaskVO[]>([])
const saving = ref(false)
const ganttRef = ref()
const formLoading = ref(false) //
const taskList = ref<any[]>([]) //
/** 待保存的修改 Map<taskId, changeData> */
const pendingChanges = ref(new Map<number, any>())
const pendingCount = computed(() => pendingChanges.value.size)
const pendingChanges = ref(new Map<number, any>()) // Map<taskId, changeData>
const pendingCount = computed(() => pendingChanges.value.size) //
const ganttHeight = computed(() => window.innerHeight - 180) //
/** 甘特图高度 = 视口高度 - 顶栏 - 操作栏 */
const ganttHeight = computed(() => window.innerHeight - 140)
/** 打开 */
const open = async () => {
dialogVisible.value = true
pendingChanges.value = new Map()
await loadGanttData()
}
/** 加载甘特图数据(复用 page 接口,传大 pageSize */
/** 加载甘特图数据 */
const loadGanttData = async () => {
const data = await ProTaskApi.getTaskPage({ pageNo: 1, pageSize: 999 })
taskList.value = data.list
taskList.value = await ProTaskApi.getGanttTaskList({})
}
/** 拖拽变更回调 */
/** 任务编辑回调 */
const handleTaskUpdate = (change: any) => {
pendingChanges.value.set(change.id, change)
}
/** 批量保存 */
const handleSave = async () => {
if (pendingChanges.value.size === 0) return
saving.value = true
if (pendingChanges.value.size === 0) {
return
}
formLoading.value = true
try {
const promises = Array.from(pendingChanges.value.values()).map((change) =>
ProTaskApi.updateTask({
@ -83,9 +68,10 @@ const handleSave = async () => {
await Promise.all(promises)
message.success(`已保存 ${pendingChanges.value.size} 条修改`)
pendingChanges.value = new Map()
//
await loadGanttData()
} finally {
saving.value = false
formLoading.value = false
}
}
@ -95,5 +81,8 @@ const handleRefresh = async () => {
await loadGanttData()
}
defineExpose({ open })
/** 初始化 */
onMounted(async () => {
await loadGanttData()
})
</script>

View File

@ -149,10 +149,7 @@
/>
</ContentWrap>
<!-- 排产对话框工单详情 + 工序步骤 + 任务列表 -->
<WorkOrderForm2 ref="formRef" />
<!-- 甘特图编辑 Dialog -->
<GanttEdit ref="ganttEditRef" />
</template>
<script setup lang="ts">
@ -165,7 +162,6 @@ import { MesProWorkOrderStatusEnum, MesProWorkOrderTypeEnum } from '@/views/mes/
import MdItemSelect from '@/views/mes/md/item/components/MdItemSelect.vue'
import MdClientSelect from '@/views/mes/md/client/components/MdClientSelect.vue'
import GanttChart from './components/GanttChart.vue'
import GanttEdit from './GanttEdit.vue'
import WorkOrderForm2 from './WorkOrderForm2.vue'
defineOptions({ name: 'MesProTask' })
@ -245,11 +241,10 @@ const handleFinish = async (id: number) => {
} catch {}
}
// TODO @
/** 打开甘特图编辑弹窗 */
const ganttEditRef = ref()
/** 打开甘特图编辑页面 */
const { push } = useRouter()
const openGanttEdit = () => {
ganttEditRef.value.open()
push({ name: 'MesProTaskGanttEdit' })
}
/** 初始化 */