|
|
|
@ -10,7 +10,8 @@ import {
|
|
|
|
Message,
|
|
|
|
Message,
|
|
|
|
Dropdown,
|
|
|
|
Dropdown,
|
|
|
|
Menu,
|
|
|
|
Menu,
|
|
|
|
Popconfirm
|
|
|
|
Popconfirm,
|
|
|
|
|
|
|
|
Upload
|
|
|
|
} from '@arco-design/web-react';
|
|
|
|
} from '@arco-design/web-react';
|
|
|
|
import {
|
|
|
|
import {
|
|
|
|
IconApps,
|
|
|
|
IconApps,
|
|
|
|
@ -24,7 +25,8 @@ import {
|
|
|
|
IconExpand,
|
|
|
|
IconExpand,
|
|
|
|
IconShrink,
|
|
|
|
IconShrink,
|
|
|
|
IconLeft,
|
|
|
|
IconLeft,
|
|
|
|
IconLoading
|
|
|
|
IconLoading,
|
|
|
|
|
|
|
|
IconUpload
|
|
|
|
} from '@arco-design/web-react/icon';
|
|
|
|
} from '@arco-design/web-react/icon';
|
|
|
|
import { menuData1, menuData2 } from './config/menuData';
|
|
|
|
import { menuData1, menuData2 } from './config/menuData';
|
|
|
|
import { Selected } from '@/pages/ideContainer/types';
|
|
|
|
import { Selected } from '@/pages/ideContainer/types';
|
|
|
|
@ -39,7 +41,7 @@ import {
|
|
|
|
updateIsRunning,
|
|
|
|
updateIsRunning,
|
|
|
|
updateRuntimeId
|
|
|
|
updateRuntimeId
|
|
|
|
} from '@/store/ideContainer';
|
|
|
|
} from '@/store/ideContainer';
|
|
|
|
import { addApp, getProjectEnv, editApp, deleteApp } from '@/api/apps';
|
|
|
|
import { addApp, getProjectEnv, editApp, deleteApp, exportApp, importApp } from '@/api/apps';
|
|
|
|
import _ from 'lodash';
|
|
|
|
import _ from 'lodash';
|
|
|
|
import { getAppInfoNew } from '@/api/appRes';
|
|
|
|
import { getAppInfoNew } from '@/api/appRes';
|
|
|
|
import { getAppEventData } from '@/api/appEvent';
|
|
|
|
import { getAppEventData } from '@/api/appEvent';
|
|
|
|
@ -201,6 +203,9 @@ const SideBar: React.FC<SideBarProps> = ({
|
|
|
|
const [showModal, setShowModal] = useState(false);
|
|
|
|
const [showModal, setShowModal] = useState(false);
|
|
|
|
const [modalType, setModalType] = useState<'ADD' | 'EDIT'>('ADD');
|
|
|
|
const [modalType, setModalType] = useState<'ADD' | 'EDIT'>('ADD');
|
|
|
|
const [currentApp, setCurrentApp] = useState<any>(null);
|
|
|
|
const [currentApp, setCurrentApp] = useState<any>(null);
|
|
|
|
|
|
|
|
const [showImportModal, setShowImportModal] = useState(false); // 导入应用弹窗
|
|
|
|
|
|
|
|
const [importFile, setImportFile] = useState<File | null>(null); // 导入的文件
|
|
|
|
|
|
|
|
const [importing, setImporting] = useState(false); // 导入中状态
|
|
|
|
const [contextMenu, setContextMenu] = useState<{
|
|
|
|
const [contextMenu, setContextMenu] = useState<{
|
|
|
|
visible: boolean;
|
|
|
|
visible: boolean;
|
|
|
|
x: number;
|
|
|
|
x: number;
|
|
|
|
@ -685,6 +690,36 @@ const SideBar: React.FC<SideBarProps> = ({
|
|
|
|
// 只有当 node.dataRef.id 存在时才渲染操作按钮
|
|
|
|
// 只有当 node.dataRef.id 存在时才渲染操作按钮
|
|
|
|
if (!node.dataRef?.id) return null;
|
|
|
|
if (!node.dataRef?.id) return null;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 导出应用处理函数
|
|
|
|
|
|
|
|
const handleExportApp = async () => {
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
const response: any = await exportApp(node.dataRef.id);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 创建下载链接
|
|
|
|
|
|
|
|
const url = window.URL.createObjectURL(new Blob([response]));
|
|
|
|
|
|
|
|
const link = document.createElement('a');
|
|
|
|
|
|
|
|
link.href = url;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 设置文件名,使用应用名称
|
|
|
|
|
|
|
|
const fileName = `${node.dataRef.name || 'app'}_${new Date().getTime()}.zip`;
|
|
|
|
|
|
|
|
link.setAttribute('download', fileName);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 触发下载
|
|
|
|
|
|
|
|
document.body.appendChild(link);
|
|
|
|
|
|
|
|
link.click();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 清理
|
|
|
|
|
|
|
|
document.body.removeChild(link);
|
|
|
|
|
|
|
|
window.URL.revokeObjectURL(url);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Message.success('应用导出成功');
|
|
|
|
|
|
|
|
setContextMenu(prev => ({ ...prev, visible: false })); // 隐藏右键菜单
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
|
|
console.error('导出应用失败:', error);
|
|
|
|
|
|
|
|
Message.error('导出应用失败');
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const dropList = (
|
|
|
|
const dropList = (
|
|
|
|
<Menu>
|
|
|
|
<Menu>
|
|
|
|
<MenuItem
|
|
|
|
<MenuItem
|
|
|
|
@ -701,6 +736,15 @@ const SideBar: React.FC<SideBarProps> = ({
|
|
|
|
编辑信息
|
|
|
|
编辑信息
|
|
|
|
</span>
|
|
|
|
</span>
|
|
|
|
</MenuItem>
|
|
|
|
</MenuItem>
|
|
|
|
|
|
|
|
<MenuItem
|
|
|
|
|
|
|
|
key="exportApp"
|
|
|
|
|
|
|
|
onClick={handleExportApp}
|
|
|
|
|
|
|
|
>
|
|
|
|
|
|
|
|
<span style={{ color: 'rgba(161, 165, 194, 1)' }}>
|
|
|
|
|
|
|
|
<IconExpand style={{ marginRight: 6 }} />
|
|
|
|
|
|
|
|
导出应用
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</MenuItem>
|
|
|
|
<MenuItem
|
|
|
|
<MenuItem
|
|
|
|
key="deleteApp"
|
|
|
|
key="deleteApp"
|
|
|
|
onClick={() => {
|
|
|
|
onClick={() => {
|
|
|
|
@ -850,10 +894,10 @@ const SideBar: React.FC<SideBarProps> = ({
|
|
|
|
/>
|
|
|
|
/>
|
|
|
|
<Button
|
|
|
|
<Button
|
|
|
|
type="secondary"
|
|
|
|
type="secondary"
|
|
|
|
icon={isAllExpanded ? <IconShrink /> : <IconExpand />}
|
|
|
|
icon={<IconUpload />}
|
|
|
|
style={{ marginLeft: 5 }}
|
|
|
|
style={{ marginLeft: 5 }}
|
|
|
|
onClick={toggleExpandAll}
|
|
|
|
onClick={() => setShowImportModal(true)}
|
|
|
|
title={isAllExpanded ? '折叠全部' : '展开全部'}
|
|
|
|
title="导入应用"
|
|
|
|
/>
|
|
|
|
/>
|
|
|
|
<Button
|
|
|
|
<Button
|
|
|
|
type="primary"
|
|
|
|
type="primary"
|
|
|
|
@ -956,6 +1000,66 @@ const SideBar: React.FC<SideBarProps> = ({
|
|
|
|
onRefresh={onRefresh}
|
|
|
|
onRefresh={onRefresh}
|
|
|
|
></AppHandleModal>
|
|
|
|
></AppHandleModal>
|
|
|
|
)}
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{/* 导入应用 */}
|
|
|
|
|
|
|
|
<Modal
|
|
|
|
|
|
|
|
title="导入应用"
|
|
|
|
|
|
|
|
visible={showImportModal}
|
|
|
|
|
|
|
|
onCancel={() => {
|
|
|
|
|
|
|
|
setShowImportModal(false);
|
|
|
|
|
|
|
|
setImportFile(null);
|
|
|
|
|
|
|
|
}}
|
|
|
|
|
|
|
|
onOk={async () => {
|
|
|
|
|
|
|
|
if (!importFile) {
|
|
|
|
|
|
|
|
Message.warning('请选择要导入的文件');
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
setImporting(true);
|
|
|
|
|
|
|
|
const res: any = await importApp(importFile);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (res.code === 200) {
|
|
|
|
|
|
|
|
Message.success('应用导入成功');
|
|
|
|
|
|
|
|
setShowImportModal(false);
|
|
|
|
|
|
|
|
setImportFile(null);
|
|
|
|
|
|
|
|
onRefresh(); // 刷新应用列表
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else {
|
|
|
|
|
|
|
|
Message.error(res.msg || '应用导入失败');
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
|
|
console.error('导入应用失败:', error);
|
|
|
|
|
|
|
|
Message.error('应用导入失败');
|
|
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
|
|
setImporting(false);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}}
|
|
|
|
|
|
|
|
confirmLoading={importing}
|
|
|
|
|
|
|
|
okText="上传"
|
|
|
|
|
|
|
|
cancelText="取消"
|
|
|
|
|
|
|
|
>
|
|
|
|
|
|
|
|
<Upload
|
|
|
|
|
|
|
|
accept=".zip"
|
|
|
|
|
|
|
|
limit={1}
|
|
|
|
|
|
|
|
autoUpload={false}
|
|
|
|
|
|
|
|
fileList={importFile ? [{
|
|
|
|
|
|
|
|
uid: '-1',
|
|
|
|
|
|
|
|
name: importFile.name,
|
|
|
|
|
|
|
|
status: 'done',
|
|
|
|
|
|
|
|
originFile: importFile
|
|
|
|
|
|
|
|
}] : []}
|
|
|
|
|
|
|
|
onChange={(fileList) => {
|
|
|
|
|
|
|
|
if (fileList.length > 0) {
|
|
|
|
|
|
|
|
setImportFile(fileList[0].originFile as File);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else {
|
|
|
|
|
|
|
|
setImportFile(null);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}}
|
|
|
|
|
|
|
|
tip="只能上传 zip 格式的应用文件"
|
|
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
</Modal>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
);
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|