feat(apps): 添加应用导入导出功能并优化资源监控仪表盘

master
钟良源 1 week ago
parent 3ef8df6087
commit 0146db8b59

@ -55,17 +55,17 @@ export function copyApp(data: any) {
} }
// 导入应用 // 导入应用
export function importApp(data: any) { export function importApp(file: File) {
return axios.post(`${urlPrefix}/apps/import`, data); return axios.post(`${urlPrefix}/apps/import`, { file });
} }
// 导出应用 // 导出应用
export function exportApp(data: any) { export function exportApp(srcId: any) {
return axios({ return axios({
method: 'post', method: 'post',
url: `${urlPrefix}/apps/export`, url: `${urlPrefix}/apps/export`,
responseType: 'blob', responseType: 'blob',
data: data data: { srcId }
}); });
} }

@ -53,11 +53,19 @@ const ResourceMonitorModal: React.FC<ResourceMonitorModalProps> = ({
// CPU 使用率仪表盘配置 // CPU 使用率仪表盘配置
const getCpuGaugeOption = (): EChartsOption => { const getCpuGaugeOption = (): EChartsOption => {
return { return {
grid: {
top: '25%',
bottom: '10%',
left: '10%',
right: '10%',
},
series: [ series: [
{ {
type: 'gauge', type: 'gauge',
startAngle: 180, startAngle: 180,
endAngle: 0, endAngle: 0,
center: ['50%', '70%'],
radius: '90%',
min: 0, min: 0,
max: 100, max: 100,
splitNumber: 10, splitNumber: 10,
@ -128,11 +136,19 @@ const ResourceMonitorModal: React.FC<ResourceMonitorModalProps> = ({
// 内存使用率仪表盘配置 // 内存使用率仪表盘配置
const getMemoryGaugeOption = (): EChartsOption => { const getMemoryGaugeOption = (): EChartsOption => {
return { return {
grid: {
top: '25%',
bottom: '10%',
left: '10%',
right: '10%',
},
series: [ series: [
{ {
type: 'gauge', type: 'gauge',
startAngle: 180, startAngle: 180,
endAngle: 0, endAngle: 0,
center: ['50%', '70%'],
radius: '90%',
min: 0, min: 0,
max: 100, max: 100,
splitNumber: 10, splitNumber: 10,

@ -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>
); );
}; };

Loading…
Cancel
Save