From 069c45b80e9b5810bbe33714d1c0c2f04f53af53 Mon Sep 17 00:00:00 2001 From: ZLY Date: Wed, 26 Nov 2025 11:46:56 +0800 Subject: [PATCH] =?UTF-8?q?feat(component):=20=E5=AE=9E=E7=8E=B0=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=E5=AF=BC=E5=85=A5=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/componentBase.ts | 22 +++ .../componentList/importComponentModal.tsx | 185 ++++++++++++++++++ .../componentList/index.tsx | 74 ++++++- .../style/importComponentModal.module.less | 185 ++++++++++++++++++ 4 files changed, 464 insertions(+), 2 deletions(-) create mode 100644 src/pages/componentDevelopment/componentList/importComponentModal.tsx create mode 100644 src/pages/componentDevelopment/componentList/style/importComponentModal.module.less diff --git a/src/api/componentBase.ts b/src/api/componentBase.ts index 3a7c0bf..b38aa17 100644 --- a/src/api/componentBase.ts +++ b/src/api/componentBase.ts @@ -33,6 +33,28 @@ export const remove = (ids) => { return axios.post(`${urlPrefix}/componentBase/remove?ids=${ids}`); }; +// 获取导入组件的信息 +export const getImportComponentInfo = (params) => { + const formData = new FormData(); + formData.append('file', params.file); + return axios.post(`${urlPrefix}/componentBase/import/check`, formData, { + headers: { + 'Content-Type': 'multipart/form-data' + } + }); +}; + +// 组件导入 +export const importComponent = (params) => { + const formData = new FormData(); + formData.append('file', params.file); + return axios.post(`${urlPrefix}/componentBase/import`, formData, { + headers: { + 'Content-Type': 'multipart/form-data' + } + }); +}; + // 组件导出 export const exportComponent = (id) => { return axios.get(`${urlPrefix}/componentBase/export?id=${id}`); diff --git a/src/pages/componentDevelopment/componentList/importComponentModal.tsx b/src/pages/componentDevelopment/componentList/importComponentModal.tsx new file mode 100644 index 0000000..53b45fb --- /dev/null +++ b/src/pages/componentDevelopment/componentList/importComponentModal.tsx @@ -0,0 +1,185 @@ +import React, { useState, useRef } from 'react'; +import { Modal, Button, Message, Divider, Upload } from '@arco-design/web-react'; +import { IconUpload } from '@arco-design/web-react/icon'; +import styles from './style/importComponentModal.module.less'; + +interface ImportComponentModalProps { + visible: boolean; + onCancel: () => void; + onOk: (file: File) => void; + onFileSelect: (file: File) => void; + componentInfo: any; + loading: boolean; +} + +const ImportComponentModal: React.FC = ({ + visible, + onCancel, + onOk, + onFileSelect, + componentInfo, + loading + }) => { + const [selectedFile, setSelectedFile] = useState(null); + const fileInputRef = useRef(null); + + const handleFileChange = (event: React.ChangeEvent) => { + const file = event.target.files?.[0]; + if (!file) return; + + // 验证文件类型 (.zip) + if (!file.name.match(/\.zip$/i)) { + Message.error('请选择ZIP压缩包文件'); + event.target.value = ''; + return; + } + + setSelectedFile(file); + onFileSelect(file); + event.target.value = ''; + }; + + const handleSelectFile = () => { + fileInputRef.current?.click(); + }; + + const handleUpload = () => { + if (selectedFile && componentInfo) { + onOk(selectedFile); + } + }; + + const handleClose = () => { + setSelectedFile(null); + onCancel(); + }; + + return ( + + +
+ + +
+ + } + > + + +
+ {!selectedFile && !componentInfo && ( +
+

请点击下方"选择文件"按钮选择组件压缩包

+

支持 .zip 格式文件

+
+ )} + + {selectedFile && !componentInfo && loading && ( +
+
+

正在解析组件信息...

+
+ )} + + {selectedFile && componentInfo && ( +
+
+
+ 文件名: + {selectedFile.name} +
+
+ + + +
+
+

组件信息

+
+ +
+ 组件标识: + {componentInfo.identifier || '-'} +
+ +
+ 组件名称: + {componentInfo.name || '-'} +
+ +
+ 组件描述: + {componentInfo.description || '-'} +
+ + {componentInfo.operates && componentInfo.operates.length > 0 && ( + <> + +
+

接口列表 ({componentInfo.operates.length})

+
+ {componentInfo.operates.map((operate: any, index: number) => ( +
+
+ {operate.ident} + {operate.type} +
+ {operate.name && ( +
{operate.name}
+ )} +
+ {operate.parameters && operate.parameters.length > 0 && ( +
+ 输入: + + {operate.parameters.length} 个参数 + +
+ )} + {operate.responses && operate.responses.length > 0 && ( +
+ 输出: + + {operate.responses.length} 个参数 + +
+ )} +
+
+ ))} +
+
+ + )} +
+
+ )} +
+
+ ); +}; + +export default ImportComponentModal; diff --git a/src/pages/componentDevelopment/componentList/index.tsx b/src/pages/componentDevelopment/componentList/index.tsx index f1d19ac..61ec80e 100644 --- a/src/pages/componentDevelopment/componentList/index.tsx +++ b/src/pages/componentDevelopment/componentList/index.tsx @@ -2,12 +2,13 @@ import React, { useState, useEffect } from 'react'; import styles from './style/index.module.less'; import { Button, Divider, Input, Space, Table, Radio, Pagination, Modal, Message } from '@arco-design/web-react'; import { IconSearch } from '@arco-design/web-react/icon'; -import { getMyComponentList, getCooperationComponentList, remove, exportComponent } from '@/api/componentBase'; +import { getMyComponentList, getCooperationComponentList, remove, exportComponent, getImportComponentInfo, importComponent } from '@/api/componentBase'; import { getReviewGroupByNew } from '@/api/componentMarket'; import { componentRelease, componentRevoke } from '@/api/componentRelease'; import { ComponentItem } from '@/api/interface'; import AddComponentModal from '@/pages/componentDevelopment/componentList/addComponentModal'; import PublishComponentModal from '@/pages/componentDevelopment/componentList/publishComponentModal'; +import ImportComponentModal from '@/pages/componentDevelopment/componentList/importComponentModal'; import HandleButtonGroup from '@/pages/componentDevelopment/componentList/handleButtonGroup'; import { componentStatusConstant, @@ -38,6 +39,9 @@ const GlobalVarContainer = () => { const [mode, setMode] = useState<'create' | 'edit' | 'copy'>('create'); // 添加模式状态 const [searchValue, setSearchValue] = useState(''); // 添加搜索状态 const [componentStatus, setComponentStatus] = useState(''); // 添加组件状态筛选 + const [importModalVisible, setImportModalVisible] = useState(false); // 导入弹窗 + const [importComponentInfo, setImportComponentInfo] = useState(null); // 导入组件信息 + const [importLoading, setImportLoading] = useState(false); // 导入加载状态 const dispatch = useDispatch(); const menuItems = [ @@ -245,6 +249,56 @@ const GlobalVarContainer = () => { } }, [selectedItem, searchValue]); + // 处理导入组件 - 文件选择后获取组件信息 + const handleImportFileSelect = async (file: File) => { + try { + setImportLoading(true); + const res: any = await getImportComponentInfo({ file }); + + if (res.code === 200) { + setImportComponentInfo(res.data); + Message.success('组件信息解析成功'); + } else { + Message.error(res.msg || '解析组件信息失败'); + setImportComponentInfo(null); + } + } catch (error) { + console.error('解析组件信息失败:', error); + Message.error('解析组件信息失败'); + setImportComponentInfo(null); + } finally { + setImportLoading(false); + } + }; + + // 处理导入组件 - 确认上传 + const handleImportConfirm = async (file: File) => { + try { + setImportLoading(true); + const res: any = await importComponent({ file }); + + if (res.code === 200) { + Message.success('组件导入成功'); + setImportModalVisible(false); + setImportComponentInfo(null); + fetchComponentData(); // 刷新列表 + } else { + Message.error(res.msg || '导入组件失败'); + } + } catch (error) { + console.error('导入组件失败:', error); + Message.error('导入组件失败'); + } finally { + setImportLoading(false); + } + }; + + // 关闭导入弹窗 + const handleImportCancel = () => { + setImportModalVisible(false); + setImportComponentInfo(null); + }; + // 获取组件列表数据,支持传入额外参数 const fetchComponentData = async (extraParams: any = {}) => { setLoading(true); @@ -424,7 +478,13 @@ const GlobalVarContainer = () => { {selectedItem === '我的组件' && }> {/**/} - +