| src/components/file-preview/use-file-preview.ts | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/ai/chat/index/modules/message/files.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/mes/pd/archive/modules/detail.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/mes/pro/task/components/gantt-chart.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/mes/pro/task/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
src/components/file-preview/use-file-preview.ts
@@ -1,34 +1,31 @@ import type { Ref } from 'vue'; import { reactive } from 'vue'; import { computed, ref } from 'vue'; import { getFileNameFromUrl, getFileType } from '@vben/utils'; import { getFileNameFromUrl } from '@vben/utils'; export function useFilePreview() { const visible = ref(false); const fileUrl = ref(''); const fileName = ref(''); const fileType = computed(() => getFileType(fileName.value || fileUrl.value)); /** * 用 reactive 而不是 ref 包装:调用方通过 `v-bind="previewState"` 传参时, * 普通对象里的 ref 不会被模板自动解包,会把 Ref 对象当 prop 传给预览组件。 */ const previewState = reactive({ fileUrl: '', fileName: '', visible: false, }); function openPreview(url: string, name?: string) { fileUrl.value = url; fileName.value = name || getFileNameFromUrl(url); visible.value = true; previewState.fileUrl = url; previewState.fileName = name || getFileNameFromUrl(url); previewState.visible = true; } function closePreview() { visible.value = false; previewState.visible = false; } return { closePreview, openPreview, previewState: { fileUrl: fileUrl as Ref<string>, fileName: fileName as Ref<string>, fileType, visible: visible as Ref<boolean>, }, previewState, }; } src/views/ai/chat/index/modules/message/files.vue
@@ -59,8 +59,8 @@ </div> </div> <FilePreviewModal v-model:visible="previewState.visible.value" :file-url="previewState.fileUrl.value" :file-name="previewState.fileName.value" v-model:visible="previewState.visible" :file-url="previewState.fileUrl" :file-name="previewState.fileName" /> </template> src/views/mes/pd/archive/modules/detail.vue
@@ -129,7 +129,10 @@ </script> <template> <FilePreviewModal v-bind="previewState" /> <FilePreviewModal v-bind="previewState" @update:visible="previewState.visible = $event" /> <Modal v-model:open="open" title="归档详情" width="80%" :footer="null" @cancel="handleClose"> <div v-if="loading" class="flex justify-center py-8"> <Spin /> src/views/mes/pro/task/components/gantt-chart.vue
@@ -157,6 +157,15 @@ ganttInited.value = true; } /** 解析时间,非法或空值返回 null(dhtmlx 收到 null 时间会在计算时间轴时崩溃) */ function parseDate(value: any): Date | null { if (value === null || value === undefined || value === '') { return null; } const date = value instanceof Date ? value : new Date(value); return Number.isNaN(date.getTime()) ? null : date; } /** 加载数据到甘特图 */ function loadData(tasks: any[]) { if (!ganttInited.value) { @@ -168,22 +177,69 @@ [BarcodeBizTypeEnum.WORKORDER]: 'project', [BarcodeBizTypeEnum.TASK]: 'task', }; gantt.parse({ data: tasks.map((item: any) => ({ ...item, type: typeMap[item.type] || item.type, start_date: item.startDate ? new Date(item.startDate) : undefined, end_date: item.endDate ? new Date(item.endDate) : undefined, })), links: [], }); const rows = tasks.map((item: any) => ({ ...item, type: typeMap[item.type] || item.type, start_date: parseDate(item.startDate), end_date: parseDate(item.endDate), })); // 工单(project)行本身不带计划时间,用其任务的最早开始、最晚结束补齐 const children = new Map<string, any[]>(); for (const row of rows) { if (row.parent) { const list = children.get(String(row.parent)) ?? []; list.push(row); children.set(String(row.parent), list); } } for (const row of rows) { if (row.type !== 'project' || (row.start_date && row.end_date)) { continue; } const childRows: any[] = children.get(String(row.id)) ?? []; const starts = childRows .map((child) => child.start_date) .filter((date): date is Date => date instanceof Date); const ends = childRows .map((child) => child.end_date) .filter((date): date is Date => date instanceof Date); if (!row.start_date && starts.length > 0) { row.start_date = new Date(Math.min(...starts.map((date) => date.getTime()))); } if (!row.end_date && ends.length > 0) { row.end_date = new Date(Math.max(...ends.map((date) => date.getTime()))); } } // 没有有效计划时间的节点(如从未排产的工单)不进入甘特图,否则整页渲染会抛异常 const data = rows.filter( (row) => row.start_date && row.end_date && row.start_date.getTime() <= row.end_date.getTime(), ); if (data.length === 0) { return; } try { gantt.parse({ data, links: [] }); } catch (error) { // 第三方甘特图异常不能冒泡打断路由/页面渲染 console.error('甘特图渲染失败', error); } } watch( () => props.tasks, (val) => { if (val?.length && ganttInited.value) { if (!ganttInited.value) { return; } if (val?.length) { loadData(val); } else { gantt.clearAll(); } }, { deep: true }, src/views/mes/pro/task/index.vue
@@ -184,7 +184,10 @@ <template #extra> <span class="text-xs text-gray-400">展示已确认自制订单的任务计划</span> </template> <GanttChart :height="350" :readonly="true" :tasks="ganttTasks" /> <GanttChart v-if="ganttTasks.length" :height="350" :readonly="true" :tasks="ganttTasks" /> <div v-else class="py-10 text-center text-sm text-gray-400"> 暂无已排产的生产任务 </div> </Card> <Grid>