feat(componentList): 导入组件增加多选导入功能

feature
钟良源 3 weeks ago
parent fd610e2bd8
commit 8273b7eebb

@ -1,20 +1,24 @@
import React, { useState, useRef, useEffect } from 'react'; import React, { useState, useRef, useEffect } from 'react';
import { Modal, Button, Message, Divider, Tabs, Checkbox } from '@arco-design/web-react'; import { Modal, Button, Message, Tabs } from '@arco-design/web-react';
import { IconUpload, IconDelete, IconFile } from '@arco-design/web-react/icon'; import { IconUpload, IconDelete, IconFile } from '@arco-design/web-react/icon';
import styles from './style/importComponentModal.module.less'; import styles from './style/importComponentModal.module.less';
interface ParsedComponentInfo {
baseInfo?: any;
operates?: any[];
}
interface ImportComponentModalProps { interface ImportComponentModalProps {
visible: boolean; visible: boolean;
onCancel: () => void; onCancel: () => void;
onOk: (files: FileItem[]) => void; onOk: (files: FileItem[]) => void;
onFileSelect: (file: File) => void; onFileSelect: (file: File) => Promise<ParsedComponentInfo[] | ParsedComponentInfo | null>;
componentInfo: any; // 单次文件解析的结果
loading: boolean; loading: boolean;
} }
interface FileItem { interface FileItem {
file: File; file: File;
componentInfo: any[]; // 该文件包含的组件列表 componentInfo: ParsedComponentInfo[]; // 该文件包含的组件列表
} }
const ImportComponentModal: React.FC<ImportComponentModalProps> = ({ const ImportComponentModal: React.FC<ImportComponentModalProps> = ({
@ -22,12 +26,10 @@ const ImportComponentModal: React.FC<ImportComponentModalProps> = ({
onCancel, onCancel,
onOk, onOk,
onFileSelect, onFileSelect,
componentInfo,
loading loading
}) => { }) => {
const [fileItems, setFileItems] = useState<FileItem[]>([]); const [fileItems, setFileItems] = useState<FileItem[]>([]);
const [activeTabKey, setActiveTabKey] = useState<string>('0'); const [activeTabKey, setActiveTabKey] = useState<string>('0');
const [pendingFile, setPendingFile] = useState<File | null>(null); // 等待解析的文件
const fileInputRef = useRef<HTMLInputElement>(null); const fileInputRef = useRef<HTMLInputElement>(null);
// 当弹窗关闭时清空所有状态 // 当弹窗关闭时清空所有状态
@ -35,56 +37,60 @@ const ImportComponentModal: React.FC<ImportComponentModalProps> = ({
if (!visible) { if (!visible) {
setFileItems([]); setFileItems([]);
setActiveTabKey('0'); setActiveTabKey('0');
setPendingFile(null);
} }
}, [visible]); }, [visible]);
// 当 componentInfo 更新时,添加到 fileItems` const updateFileItem = (file: File, componentInfo: ParsedComponentInfo[] | ParsedComponentInfo) => {
useEffect(() => { const componentList = Array.isArray(componentInfo) ? componentInfo : [componentInfo];
if (pendingFile && componentInfo) { let nextActiveKey = '0';
const componentList = Array.isArray(componentInfo) ? componentInfo : [componentInfo];
// 检查文件是否已存在 setFileItems((prev) => {
const existingIndex = fileItems.findIndex(item => item.file.name === pendingFile.name); const existingIndex = prev.findIndex((item) => item.file.name === file.name);
if (existingIndex >= 0) { if (existingIndex >= 0) {
// 更新现有文件 nextActiveKey = String(existingIndex);
const newFileItems = [...fileItems]; const nextItems = [...prev];
newFileItems[existingIndex] = { nextItems[existingIndex] = {
file: pendingFile, file,
componentInfo: componentList componentInfo: componentList
}; };
setFileItems(newFileItems); return nextItems;
} }
else {
// 添加新文件 nextActiveKey = String(prev.length);
const newFileItem: FileItem = { return [
file: pendingFile, ...prev,
{
file,
componentInfo: componentList componentInfo: componentList
}; }
const updatedFileItems = [...fileItems, newFileItem]; ];
setFileItems(updatedFileItems); });
setActiveTabKey(String(fileItems.length)); // 切换到新添加的标签页
}
setPendingFile(null); setActiveTabKey(nextActiveKey);
};
const handleFileChange = async (event: React.ChangeEvent<HTMLInputElement>) => {
const selectedFiles = Array.from(event.target.files ?? []);
if (selectedFiles.length === 0) return;
const validFiles = selectedFiles.filter((file) => file.name.match(/\.zip$/i));
const invalidFileCount = selectedFiles.length - validFiles.length;
if (invalidFileCount > 0) {
Message.error('请选择 ZIP 压缩包文件');
} }
}, [componentInfo]);
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => { for (const file of validFiles) {
const files = event.target.files; const parsedComponentInfo = await onFileSelect(file);
if (!files || files.length === 0) return;
// 验证文件类型 (.zip) if (!parsedComponentInfo) {
const file = files[0]; continue;
if (!file.name.match(/\.zip$/i)) { }
Message.error('请选择ZIP压缩包文件');
event.target.value = ''; updateFileItem(file, parsedComponentInfo);
return;
} }
setPendingFile(file);
onFileSelect(file);
event.target.value = ''; event.target.value = '';
}; };
@ -116,7 +122,6 @@ const ImportComponentModal: React.FC<ImportComponentModalProps> = ({
const handleClose = () => { const handleClose = () => {
setFileItems([]); setFileItems([]);
setActiveTabKey('0'); setActiveTabKey('0');
setPendingFile(null);
onCancel(); onCancel();
}; };
@ -151,6 +156,7 @@ const ImportComponentModal: React.FC<ImportComponentModalProps> = ({
> >
<input <input
ref={fileInputRef} ref={fileInputRef}
multiple
type="file" type="file"
accept=".zip" accept=".zip"
style={{ display: 'none' }} style={{ display: 'none' }}
@ -158,7 +164,7 @@ const ImportComponentModal: React.FC<ImportComponentModalProps> = ({
/> />
<div className={styles['import-modal-content']}> <div className={styles['import-modal-content']}>
{fileItems.length === 0 && !pendingFile && ( {fileItems.length === 0 && (
<div className={styles['empty-state']}> <div className={styles['empty-state']}>
<p className={styles['empty-text']}>&#34;&#34;</p> <p className={styles['empty-text']}>&#34;&#34;</p>
<p className={styles['empty-hint']}> .zip </p> <p className={styles['empty-hint']}> .zip </p>
@ -172,9 +178,9 @@ const ImportComponentModal: React.FC<ImportComponentModalProps> = ({
type="card" type="card"
> >
{fileItems.map((fileItem, fileIndex) => { {fileItems.map((fileItem, fileIndex) => {
const current = fileItems[activeTabKey]; const currentComponent = fileItem.componentInfo[0];
const baseInfo = current.componentInfo[0].baseInfo; const baseInfo = currentComponent?.baseInfo || {};
const operates = current.componentInfo[0].operates; const operates = currentComponent?.operates || [];
return ( return (
<Tabs.TabPane <Tabs.TabPane
key={String(fileIndex)} key={String(fileIndex)}

@ -84,7 +84,6 @@ const GlobalVarContainer = () => {
{ label: 'Python', value: 'Python' } { label: 'Python', value: 'Python' }
]; ];
const [importModalVisible, setImportModalVisible] = useState(false); // 导入弹窗 const [importModalVisible, setImportModalVisible] = useState(false); // 导入弹窗
const [importComponentInfo, setImportComponentInfo] = useState(null); // 导入组件信息
const [importLoading, setImportLoading] = useState(false); // 导入加载状态 const [importLoading, setImportLoading] = useState(false); // 导入加载状态
const [showOffSaleModal, setShowOffSaleModal] = useState(false); const [showOffSaleModal, setShowOffSaleModal] = useState(false);
const [offSaleComponent, setOffSaleComponent] = useState(null); const [offSaleComponent, setOffSaleComponent] = useState(null);
@ -423,16 +422,16 @@ const GlobalVarContainer = () => {
const res: any = await getImportComponentInfo({ file }); const res: any = await getImportComponentInfo({ file });
if (res.code === 200) { if (res.code === 200) {
setImportComponentInfo(res.data);
Message.success('组件信息解析成功'); Message.success('组件信息解析成功');
return res.data;
} }
else { else {
Message.error(res.msg || '解析组件信息失败'); Message.error(res.msg || '解析组件信息失败');
setImportComponentInfo(null); return null;
} }
} catch (error) { } catch (error) {
Message.error('解析组件信息失败'); Message.error('解析组件信息失败');
setImportComponentInfo(null); return null;
} finally { } finally {
setImportLoading(false); setImportLoading(false);
} }
@ -453,7 +452,6 @@ const GlobalVarContainer = () => {
if (res.code === 200) { if (res.code === 200) {
Message.success('组件导入成功'); Message.success('组件导入成功');
setImportModalVisible(false); setImportModalVisible(false);
setImportComponentInfo(null);
fetchComponentData(); // 刷新列表 fetchComponentData(); // 刷新列表
} }
else { else {
@ -471,7 +469,6 @@ const GlobalVarContainer = () => {
// 关闭导入弹窗 // 关闭导入弹窗
const handleImportCancel = () => { const handleImportCancel = () => {
setImportModalVisible(false); setImportModalVisible(false);
setImportComponentInfo(null);
}; };
// 查看审核历史 // 查看审核历史
@ -890,7 +887,6 @@ const GlobalVarContainer = () => {
onCancel={handleImportCancel} onCancel={handleImportCancel}
onOk={handleImportConfirm} onOk={handleImportConfirm}
onFileSelect={handleImportFileSelect} onFileSelect={handleImportFileSelect}
componentInfo={importComponentInfo}
loading={importLoading} loading={importLoading}
/> />
@ -1026,4 +1022,4 @@ const GlobalVarContainer = () => {
); );
}; };
export default GlobalVarContainer; export default GlobalVarContainer;

Loading…
Cancel
Save