|
|
|
@ -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']}>请点击下方"添加文件"按钮选择组件压缩包</p>
|
|
|
|
<p className={styles['empty-text']}>请点击下方"添加文件"按钮选择组件压缩包</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)}
|
|
|
|
|