From 6163b0baa51b5a11d4c970b4e292856390a7c0fd Mon Sep 17 00:00:00 2001 From: xudan Date: Fri, 12 Sep 2025 14:57:42 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E5=AF=BC=E5=85=A5Bin?= =?UTF-8?q?task=20Excel=E6=96=87=E4=BB=B6=E5=8A=9F=E8=83=BD=EF=BC=8C?= =?UTF-8?q?=E6=9B=B4=E6=96=B0API=E5=92=8C=E5=9C=BA=E6=99=AF=E7=BC=96?= =?UTF-8?q?=E8=BE=91=E5=99=A8=E4=BB=A5=E6=94=AF=E6=8C=81=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E4=B8=8A=E4=BC=A0=EF=BC=8C=E4=BC=98=E5=8C=96HTTP=E6=9C=8D?= =?UTF-8?q?=E5=8A=A1=E4=BB=A5=E5=A4=84=E7=90=86FormData?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/apis/scene/api.ts | 34 ++++++++++++++++++++++++++++++++++ src/pages/scene-editor.vue | 28 +++++++++++++++++++++++----- src/services/http.ts | 14 ++++++++++++++ 3 files changed, 71 insertions(+), 5 deletions(-) diff --git a/src/apis/scene/api.ts b/src/apis/scene/api.ts index 0b96dae..266b4df 100644 --- a/src/apis/scene/api.ts +++ b/src/apis/scene/api.ts @@ -12,6 +12,8 @@ const enum API { 获取组场景 = '/scene/getByGroupId', 保存组场景 = '/scene/saveByGroupId', + 导入Bintask = '/scene/importBinTaskExcel', + 实时监控场景 = '/scene/monitor/:id', 实时监控真实场景 = '/scene/monitor/real/:id', 实时监控库位状态 = '/ws/storage-location/:id', @@ -123,6 +125,38 @@ export async function monitorRealSceneById(id: SceneInfo['id']): Promise { + if (!id || !file) return false; + + // 验证文件类型和大小 + const allowedTypes = [ + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', // .xlsx + 'application/vnd.ms-excel' // .xls + ]; + + if (!allowedTypes.includes(file.type)) { + throw new Error('请选择Excel文件(.xlsx或.xls格式)'); + } + + // 验证文件大小(1MB = 1024 * 1024 bytes) + const maxSize = 1024 * 1024; + if (file.size > maxSize) { + throw new Error('文件大小不能超过1MB'); + } + + try { + const formData = new FormData(); + formData.append('file', file); + formData.append('id', id); + + await http.postFormData(API.导入Bintask, formData); + return true; + } catch (error) { + console.debug(error); + return false; + } +} + export async function monitorStorageLocationById( id: SceneInfo['id'], options?: { diff --git a/src/pages/scene-editor.vue b/src/pages/scene-editor.vue index 897c486..ea69a0f 100644 --- a/src/pages/scene-editor.vue +++ b/src/pages/scene-editor.vue @@ -3,7 +3,7 @@ import { message, Modal } from 'ant-design-vue'; import { computed, onMounted, provide, ref, type ShallowRef, shallowRef, watch } from 'vue'; import { useI18n } from 'vue-i18n'; -import { getSceneById, pushSceneById, saveSceneById } from '../apis/scene'; +import { getSceneById, importBinTaskExcel, pushSceneById, saveSceneById } from '../apis/scene'; import BatchEditToolbar from '../components/batch-edit-toolbar.vue'; import AutoCreateStorageModal from '../components/modal/auto-create-storage-modal.vue'; import { EditorService } from '../services/editor.service'; @@ -80,15 +80,15 @@ const autoCreateStorageData = ref<{ actionPoints: any[]; }>({ areaName: '', - actionPoints: [] + actionPoints: [], }); onMounted(() => { editor.value = new EditorService(container.value!); - + // 将 editor 存储到 store 中 editorStore.setEditor(editor as ShallowRef); - + // 监听自动生成库位对话框事件 editor.value?.on('autoCreateStorageDialog', (data: any) => { autoCreateStorageData.value = data; @@ -134,6 +134,23 @@ const exportScene = () => { URL.revokeObjectURL(url); }; +// 导入Bintask Excel文件 +const importBinTask = async () => { + try { + const file = await selectFile('.xlsx,.xls'); + if (!file || !file.size) return; + + const success = await importBinTaskExcel(props.id, file); + if (success) { + message.success('Bintask导入成功'); + } else { + message.error('Bintask导入失败'); + } + } catch (error: any) { + message.error(error.message || 'Bintask导入失败'); + } +}; + const show = ref(true); const current = ref<{ type: 'robot' | 'point' | 'line' | 'area'; id: string }>(); watch( @@ -187,7 +204,7 @@ const backToCards = () => { // 处理自动生成库位确认 const handleAutoCreateStorageConfirm = (actionPoints: any[]) => { if (!editor.value) return; - + editor.value.autoCreateStorageLocations(autoCreateStorageData.value.areaName, actionPoints); message.success(`已为 ${actionPoints.length} 个动作点自动生成库位`); }; @@ -216,6 +233,7 @@ const handleAutoCreateStorageCancel = () => { {{ $t('推送') }} {{ $t('导入') }} + 导入Bintask {{ $t('导出') }} diff --git a/src/services/http.ts b/src/services/http.ts index 1c0ff06..7dc1708 100644 --- a/src/services/http.ts +++ b/src/services/http.ts @@ -7,6 +7,19 @@ const http: HttpInstance = axios.create({ adapter: 'fetch', timeout: 30_000, }); + +// 添加FormData上传方法 +http.postFormData = async (url: string, formData: FormData, config?: AxiosRequestConfig): Promise => { + const response = await http.post(url, formData, { + ...config, + headers: { + 'Content-Type': 'multipart/form-data', + ...config?.headers, + }, + }); + return response; +}; + export default http; // 添加请求拦截器 @@ -63,6 +76,7 @@ http.interceptors.response.use( type HttpInstance = Omit & { get: (url: string, config?: AxiosRequestConfig) => Promise; post: (url: string, body?: B, config?: AxiosRequestConfig) => Promise; + postFormData: (url: string, formData: FormData, config?: AxiosRequestConfig) => Promise; }; type CommonRes = {