✨ feat(mes): 添加甘特图数据项 VO 和相关接口逻辑
新增 GanttDataRespVO 类以支持甘特图数据结构,并在 MesProTaskController 中实现获取甘特图任务列表的接口。此变更旨在优化任务管理界面,提升用户体验。pull/871/MERGE
parent
5f8a002654
commit
81a8f262aa
|
|
@ -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 @AI:linter 报错;
|
||||
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
|
||||
}
|
||||
|
||||
// 时间刻度:日 > 8小时(00: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()
|
||||
|
|
|
|||
|
|
@ -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 () => {
|
||||
|
|
|
|||
Loading…
Reference in New Issue