feat(mes): 添加甘特图数据项 VO 和相关接口逻辑

新增 GanttDataRespVO 类以支持甘特图数据结构,并在 MesProTaskController 中实现获取甘特图任务列表的接口。此变更旨在优化任务管理界面,提升用户体验。
pull/871/MERGE
YunaiV 2026-03-16 13:03:15 +08:00
parent 5f8a002654
commit 81a8f262aa
2 changed files with 70 additions and 63 deletions

View File

@ -4,6 +4,7 @@
</template>
<script setup lang="ts">
// TODO @AI绿progress绿
import { gantt } from 'dhtmlx-gantt'
import 'dhtmlx-gantt/codebase/dhtmlxgantt.css'
@ -42,7 +43,9 @@ const ganttInited = ref(false)
/** 初始化甘特图配置 */
const initGantt = () => {
if (!ganttContainer.value) return
if (!ganttContainer.value) {
return
}
// locale
gantt.locale = {
@ -78,6 +81,7 @@ const initGantt = () => {
day_full: ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'],
day_short: ['日', '一', '二', '三', '四', '五', '六']
},
// TODO @AIlinter
labels: {
new_task: '新任务',
icon_save: '保存',
@ -123,6 +127,7 @@ const initGantt = () => {
gantt.config.details_on_create = false
gantt.config.details_on_dblclick = false
gantt.config.show_progress = true
gantt.config.open_tree_initially = true
//
if (props.readonly) {
@ -135,35 +140,61 @@ const initGantt = () => {
gantt.config.drag_progress = false
}
// > 800:00 / 08:00 / 16:00
// > > 8 KTG
const weekScaleTemplate = (date: Date) => {
const dateToStr = gantt.date.date_to_str('%M %d')
const endDate = gantt.date.add(gantt.date.add(date, 1, 'week'), -1, 'day')
return dateToStr(date) + ' - ' + dateToStr(endDate)
}
const dayTemplate = (date: Date) => {
return gantt.date.date_to_str('%M %d')(date)
}
const daysStyle = (date: Date) => {
return date.getDay() === 0 || date.getDay() === 6 ? 'weekend' : ''
}
gantt.config.scales = [
{ unit: 'day', step: 1, format: '%Y-%m-%d %D' },
{
unit: 'hour',
step: 8,
format: (date: Date) => {
return String(date.getHours()).padStart(2, '0') + ':00'
}
}
{ unit: 'week', step: 1, format: weekScaleTemplate },
{ unit: 'day', step: 1, format: dayTemplate, css: daysStyle },
{ unit: 'hour', step: 8, format: '%H:%i' }
]
gantt.config.scale_height = 50
gantt.config.show_task_cells = true
//
gantt.config.columns = [
{ name: 'text', label: '任务名称', tree: true, width: 180, resize: true },
{ name: 'workstationName', label: '工作站', align: 'center', width: 100, resize: true },
{ name: 'processName', label: '工序', align: 'center', width: 100, resize: true },
{ name: 'workstation', label: '工作站', align: 'center', width: 100, resize: true },
{ name: 'process', label: '工序', align: 'center', width: 100, resize: true },
{ name: 'start_date', label: '开始时间', align: 'center', width: 130 },
{ name: 'end_date', label: '结束时间', align: 'center', width: 130 }
]
// 线
gantt.plugins({ marker: true })
// 线 + tooltip
gantt.plugins({ marker: true, tooltip: true })
gantt.addMarker({
start_date: new Date(),
css: 'today',
text: '今天'
})
//
gantt.templates.task_text = (_start: any, _end: any, task: any) => {
const percent = Math.round((task.progress || 0) * 100)
if (task.type === 'project') {
return `<b>生产工单:</b> ${task.text} <span>完成比例:${percent}%</span>`
}
return `<b>生产任务:</b> ${task.process || ''} ${task.text} <span>完成比例:${percent}%</span>`
}
//
gantt.templates.tooltip_text = (_start: any, _end: any, task: any) => {
const percent = Math.round((task.progress || 0) * 100)
if (task.type === 'project') {
return `<b>生产工单:</b> ${task.text} <span>完成比例:${percent}%</span>`
}
return `<b>生产任务:</b> ${task.process || ''} ${task.text} <span>完成比例:${percent}%</span>`
}
//
gantt.templates.task_class = (_start: any, _end: any, task: any) => {
if (task.type === gantt.config.types.project) return 'gantt-project-bar'
@ -177,7 +208,7 @@ const initGantt = () => {
gantt.attachEvent('onAfterTaskDrag', (id: string | number) => {
const task = gantt.getTask(id)
emit('task-update', {
id: task.taskId || id,
id: task.originalId || id,
startTime: task.start_date,
endTime: task.end_date,
duration: (task.duration as number) / 8 //
@ -185,76 +216,51 @@ const initGantt = () => {
})
}
// TODO @AI
gantt.attachEvent('onTaskClick', (id: string | number) => {
emit('task-click', id)
return true
})
//
gantt.init(ganttContainer.value)
ganttInited.value = true
}
/** 转换数据为甘特图格式 */
const convertToGanttData = (tasks: any[]) => {
const data: any[] = []
//
const workOrderMap = new Map<number, any[]>()
tasks.forEach((task) => {
const woId = task.workOrderId
if (!workOrderMap.has(woId)) {
workOrderMap.set(woId, [])
}
workOrderMap.get(woId)!.push(task)
})
workOrderMap.forEach((woTasks, woId) => {
const firstTask = woTasks[0]
// project
data.push({
id: 'wo_' + woId,
text: firstTask.workOrderCode + ' ' + firstTask.workOrderName,
type: gantt.config.types.project,
open: true
})
//
woTasks.forEach((t) => {
data.push({
id: 'task_' + t.id,
taskId: t.id,
text: t.name,
workstationName: t.workstationName || '',
processName: t.processName || '',
start_date: t.startTime ? new Date(t.startTime) : new Date(),
duration: (t.duration || 1) * 8, // -> end_date gantt
parent: 'wo_' + woId,
progress: t.quantity > 0 ? t.producedQuantity / t.quantity : 0,
color: t.colorCode || '#00AEF3'
})
})
})
return { data, links: [] }
}
/** 加载数据到甘特图 */
const loadData = (tasks: any[]) => {
if (!ganttInited.value) return
if (!ganttInited.value) {
return
}
gantt.clearAll()
const ganttData = convertToGanttData(tasks)
gantt.parse(ganttData)
// TODO @AI使mes utils
// type: 301=(project), 303=(task)
const TYPE_MAP: Record<number, string> = { 301: 'project', 303: 'task' }
const transformed = {
data: tasks.map((item: any) => ({
...item,
// TODO @AI TYPE_MAP?
type: TYPE_MAP[item.type] || item.type,
start_date: item.startDate ? new Date(item.startDate) : undefined,
end_date: item.endDate ? new Date(item.endDate) : undefined
})),
links: []
}
gantt.parse(transformed)
}
// tasks
/** 监听 tasks 变化 */
watch(
() => props.tasks,
(val) => {
if (val && ganttInited.value) {
if (val?.length && ganttInited.value) {
loadData(val)
}
},
{ deep: true }
)
// TODO @AI
onMounted(() => {
initGantt()
if (props.tasks?.length) {
@ -262,6 +268,7 @@ onMounted(() => {
}
})
// TODO @AI
onBeforeUnmount(() => {
if (ganttInited.value) {
gantt.clearAll()

View File

@ -189,7 +189,7 @@ const queryParams = reactive({
const queryFormRef = ref() // ref
const ganttLoading = ref(false) //
const ganttTasks = ref<any[]>([]) // ProTaskVO GanttChart
const ganttTasks = ref<any[]>([]) //
/** 查询待排产工单列表(支持父子工单树形展示) */
const getWorkOrderList = async () => {