|
|
|
|
@ -1,4 +1,4 @@
|
|
|
|
|
import React, { useEffect, useState } from 'react';
|
|
|
|
|
import React, { useEffect, useRef, useState } from 'react';
|
|
|
|
|
import {
|
|
|
|
|
Button,
|
|
|
|
|
Space,
|
|
|
|
|
@ -22,20 +22,41 @@ import {
|
|
|
|
|
refreshInstanceDependency,
|
|
|
|
|
getComponentResource
|
|
|
|
|
} from '@/api/componentInstance';
|
|
|
|
|
import { runStatusConstant, runStatusDic, runTypeConstant, runTypeDic } from '@/const/isdp/componentDeploy';
|
|
|
|
|
import {
|
|
|
|
|
compileStatusDic,
|
|
|
|
|
runStatusConstant,
|
|
|
|
|
runStatusDic,
|
|
|
|
|
runTypeConstant,
|
|
|
|
|
runTypeDic
|
|
|
|
|
} from '@/const/isdp/componentDeploy';
|
|
|
|
|
import dayjs from 'dayjs';
|
|
|
|
|
import EditInstanceModal from './editInstanceModal';
|
|
|
|
|
import EnvConfigModal from './envConfigModal';
|
|
|
|
|
import ResourceMonitorModal from '@/components/ResourceMonitorModal';
|
|
|
|
|
import { getToken } from '@/utils/auth';
|
|
|
|
|
|
|
|
|
|
const { RangePicker } = DatePicker;
|
|
|
|
|
const { TextArea } = Input;
|
|
|
|
|
|
|
|
|
|
interface ListNodeProps {
|
|
|
|
|
componentData: any; // 组件数据
|
|
|
|
|
compileLogsMap: Record<string, string>;
|
|
|
|
|
compileStatusMap: Record<string, 'idle' | 'running' | 'success' | 'failed' | 'canceled'>;
|
|
|
|
|
subscribeInstanceLog: (instanceId: string) => void;
|
|
|
|
|
unsubscribeInstanceLog: (instanceId: string) => void;
|
|
|
|
|
updateCompileStatus: (instanceId: string, status: 'idle' | 'running' | 'success' | 'failed' | 'canceled') => void;
|
|
|
|
|
isWsConnected: boolean;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const ListNode: React.FC<ListNodeProps> = ({ componentData }) => {
|
|
|
|
|
const ListNode: React.FC<ListNodeProps> = ({
|
|
|
|
|
componentData,
|
|
|
|
|
compileLogsMap,
|
|
|
|
|
compileStatusMap,
|
|
|
|
|
subscribeInstanceLog,
|
|
|
|
|
unsubscribeInstanceLog,
|
|
|
|
|
updateCompileStatus,
|
|
|
|
|
isWsConnected
|
|
|
|
|
}) => {
|
|
|
|
|
const [data, setData] = useState([]);
|
|
|
|
|
const [loading, setLoading] = useState(false);
|
|
|
|
|
const [refreshingIds, setRefreshingIds] = useState<Set<string>>(new Set()); // 记录正在刷新的实例ID
|
|
|
|
|
@ -65,6 +86,10 @@ const ListNode: React.FC<ListNodeProps> = ({ componentData }) => {
|
|
|
|
|
const [resourceModalVisible, setResourceModalVisible] = useState(false);
|
|
|
|
|
const [resourceData, setResourceData] = useState<any>(null);
|
|
|
|
|
|
|
|
|
|
// 编译日志 Modal 相关状态
|
|
|
|
|
const [compileModalVisible, setCompileModalVisible] = useState(false);
|
|
|
|
|
const [compileInstance, setCompileInstance] = useState<any>(null);
|
|
|
|
|
|
|
|
|
|
// 获取实例列表
|
|
|
|
|
const fetchInstanceList = async () => {
|
|
|
|
|
if (!componentData?.identifier) return;
|
|
|
|
|
@ -164,35 +189,20 @@ const ListNode: React.FC<ListNodeProps> = ({ componentData }) => {
|
|
|
|
|
// 添加到刷新中的集合
|
|
|
|
|
setRefreshingIds(prev => new Set(prev).add(record.id));
|
|
|
|
|
|
|
|
|
|
// 显示提示消息
|
|
|
|
|
const messageKey = `refresh_${record.id}`;
|
|
|
|
|
Message.loading({
|
|
|
|
|
id: messageKey,
|
|
|
|
|
content: '正在编译组件,请稍候...',
|
|
|
|
|
duration: 0 // 不自动关闭
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
// 调用编译接口
|
|
|
|
|
const res: any = await refreshInstanceDependency(record.id);
|
|
|
|
|
|
|
|
|
|
// 关闭 loading 消息
|
|
|
|
|
Message.clear();
|
|
|
|
|
|
|
|
|
|
if (res.code === 200 && res.data) {
|
|
|
|
|
Message.success('依赖刷新成功');
|
|
|
|
|
// 刷新列表
|
|
|
|
|
fetchInstanceList();
|
|
|
|
|
if (res.code === 200) {
|
|
|
|
|
Message.success('编译任务已提交');
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
Message.error(res.msg || '依赖刷新失败');
|
|
|
|
|
Message.error(res.msg || '编译失败');
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
// 关闭 loading 消息
|
|
|
|
|
Message.clear();
|
|
|
|
|
console.error('编译组件失败:', error);
|
|
|
|
|
Message.error('依赖刷新失败,请稍后重试');
|
|
|
|
|
Message.error('编译组件失败,请稍后重试');
|
|
|
|
|
} finally {
|
|
|
|
|
// 从刷新中的集合移除
|
|
|
|
|
setRefreshingIds(prev => {
|
|
|
|
|
const newSet = new Set(prev);
|
|
|
|
|
newSet.delete(record.id);
|
|
|
|
|
@ -201,6 +211,27 @@ const ListNode: React.FC<ListNodeProps> = ({ componentData }) => {
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 打开编译日志 Modal
|
|
|
|
|
const handleOpenCompileLog = (record) => {
|
|
|
|
|
setCompileInstance(record);
|
|
|
|
|
setCompileModalVisible(true);
|
|
|
|
|
|
|
|
|
|
// 通过父组件传递的方法订阅该实例的编译日志
|
|
|
|
|
subscribeInstanceLog(record.id);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 关闭编译日志 Modal
|
|
|
|
|
const handleCloseCompileModal = () => {
|
|
|
|
|
setCompileModalVisible(false);
|
|
|
|
|
|
|
|
|
|
// 取消订阅当前实例的日志
|
|
|
|
|
if (compileInstance?.id) {
|
|
|
|
|
unsubscribeInstanceLog(compileInstance.id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setCompileInstance(null);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 打开日志 Modal
|
|
|
|
|
const handleOpenLog = (record) => {
|
|
|
|
|
setCurrentInstance(record);
|
|
|
|
|
@ -319,7 +350,8 @@ const ListNode: React.FC<ListNodeProps> = ({ componentData }) => {
|
|
|
|
|
if (res.code === 200 && res.data) {
|
|
|
|
|
setResourceData(res.data);
|
|
|
|
|
setResourceModalVisible(true);
|
|
|
|
|
} else {
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
Message.error(res.msg || '获取资源信息失败');
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
@ -338,7 +370,17 @@ const ListNode: React.FC<ListNodeProps> = ({ componentData }) => {
|
|
|
|
|
{
|
|
|
|
|
title: '组件标识',
|
|
|
|
|
dataIndex: 'identifier',
|
|
|
|
|
align: 'center'
|
|
|
|
|
align: 'center',
|
|
|
|
|
render: (identifier, record) => {
|
|
|
|
|
return (
|
|
|
|
|
<span
|
|
|
|
|
style={{ color: '#3491FA', cursor: 'pointer' }}
|
|
|
|
|
onClick={() => handleOpenEdit(record)}
|
|
|
|
|
>
|
|
|
|
|
{identifier}
|
|
|
|
|
</span>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: '实例名称',
|
|
|
|
|
@ -355,7 +397,7 @@ const ListNode: React.FC<ListNodeProps> = ({ componentData }) => {
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: '运行状态',
|
|
|
|
|
title: '实例运行状态',
|
|
|
|
|
dataIndex: 'runStatus',
|
|
|
|
|
align: 'center',
|
|
|
|
|
render: (runStatus) => {
|
|
|
|
|
@ -363,6 +405,15 @@ const ListNode: React.FC<ListNodeProps> = ({ componentData }) => {
|
|
|
|
|
return item ? <Tag color={item.color}>{item.label}</Tag> : '-';
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: '实例编译状态',
|
|
|
|
|
dataIndex: 'currentTaskStatus',
|
|
|
|
|
align: 'center',
|
|
|
|
|
render: (currentTaskStatus) => {
|
|
|
|
|
const item = compileStatusDic.find(d => d.value === currentTaskStatus);
|
|
|
|
|
return item ? <Tag color={item.color}>{item.label}</Tag> : '-';
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: '创建时间',
|
|
|
|
|
dataIndex: 'createTime',
|
|
|
|
|
@ -381,6 +432,11 @@ const ListNode: React.FC<ListNodeProps> = ({ componentData }) => {
|
|
|
|
|
const isRefreshing = refreshingIds.has(record.id);
|
|
|
|
|
const isLocalRun = record.runType === 'LOCAL';
|
|
|
|
|
|
|
|
|
|
// 获取当前实例的编译状态
|
|
|
|
|
const compileStatus = compileStatusMap[record.id] || 'idle';
|
|
|
|
|
// 编译状态为 running、failed、canceled 时不显示启停按钮
|
|
|
|
|
const hideStartStopButtons = compileStatus === 'running' || compileStatus === 'failed' || compileStatus === 'canceled';
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className={styles['table-handle-box']}>
|
|
|
|
|
<Space size={20}>
|
|
|
|
|
@ -394,6 +450,12 @@ const ListNode: React.FC<ListNodeProps> = ({ componentData }) => {
|
|
|
|
|
>
|
|
|
|
|
编译组件
|
|
|
|
|
</Button>
|
|
|
|
|
<Button
|
|
|
|
|
type="text"
|
|
|
|
|
onClick={() => handleOpenCompileLog(record)}
|
|
|
|
|
>
|
|
|
|
|
编译日志
|
|
|
|
|
</Button>
|
|
|
|
|
<Button type="text" onClick={() => handleOpenLog(record)}>运行日志</Button>
|
|
|
|
|
</>
|
|
|
|
|
)}
|
|
|
|
|
@ -403,36 +465,34 @@ const ListNode: React.FC<ListNodeProps> = ({ componentData }) => {
|
|
|
|
|
{!isLocalRun && isRunning && (
|
|
|
|
|
<Button type="text" onClick={() => handeViewResource(record)}>组件资源</Button>
|
|
|
|
|
)}
|
|
|
|
|
<Button type="text"
|
|
|
|
|
onClick={() => handleOpenEdit(record)}
|
|
|
|
|
{!hideStartStopButtons && (
|
|
|
|
|
<>
|
|
|
|
|
{isRunning ? (
|
|
|
|
|
<Button
|
|
|
|
|
type="text"
|
|
|
|
|
style={{ color: 'rgb(var(--danger-6))' }}
|
|
|
|
|
icon={<img
|
|
|
|
|
src={'/icons/powerUpIcon.png'}
|
|
|
|
|
style={{
|
|
|
|
|
width: 16,
|
|
|
|
|
height: 16,
|
|
|
|
|
marginRight: 5,
|
|
|
|
|
verticalAlign: 'middle',
|
|
|
|
|
filter: 'invert(27%) sepia(96%) saturate(4392%) hue-rotate(348deg) brightness(95%) contrast(95%)'
|
|
|
|
|
}} />}
|
|
|
|
|
onClick={() => handleStop(record)}
|
|
|
|
|
>停止</Button>
|
|
|
|
|
) : (
|
|
|
|
|
<Button
|
|
|
|
|
type="text"
|
|
|
|
|
icon={<img
|
|
|
|
|
src={'/icons/editIcon.png'}
|
|
|
|
|
src={'/icons/powerUpIcon.png'}
|
|
|
|
|
style={{ width: 16, height: 16, marginRight: 5, verticalAlign: 'middle' }} />}
|
|
|
|
|
>编辑</Button>
|
|
|
|
|
{isRunning ? (
|
|
|
|
|
<Button
|
|
|
|
|
type="text"
|
|
|
|
|
style={{ color: 'rgb(var(--danger-6))' }}
|
|
|
|
|
icon={<img
|
|
|
|
|
src={'/icons/powerUpIcon.png'}
|
|
|
|
|
style={{
|
|
|
|
|
width: 16,
|
|
|
|
|
height: 16,
|
|
|
|
|
marginRight: 5,
|
|
|
|
|
verticalAlign: 'middle',
|
|
|
|
|
filter: 'invert(27%) sepia(96%) saturate(4392%) hue-rotate(348deg) brightness(95%) contrast(95%)'
|
|
|
|
|
}} />}
|
|
|
|
|
onClick={() => handleStop(record)}
|
|
|
|
|
>停止</Button>
|
|
|
|
|
) : (
|
|
|
|
|
<Button
|
|
|
|
|
type="text"
|
|
|
|
|
icon={<img
|
|
|
|
|
src={'/icons/powerUpIcon.png'}
|
|
|
|
|
style={{ width: 16, height: 16, marginRight: 5, verticalAlign: 'middle' }} />}
|
|
|
|
|
onClick={() => handleStart(record)}
|
|
|
|
|
loading={startingId === record.id && startLoading}
|
|
|
|
|
>启动</Button>
|
|
|
|
|
onClick={() => handleStart(record)}
|
|
|
|
|
loading={startingId === record.id && startLoading}
|
|
|
|
|
>启动</Button>
|
|
|
|
|
)}
|
|
|
|
|
</>
|
|
|
|
|
)}
|
|
|
|
|
{!isRunning && (
|
|
|
|
|
<Button
|
|
|
|
|
@ -518,6 +578,26 @@ const ListNode: React.FC<ListNodeProps> = ({ componentData }) => {
|
|
|
|
|
/>
|
|
|
|
|
</Modal>
|
|
|
|
|
|
|
|
|
|
{/* 编译日志 Modal */}
|
|
|
|
|
<Modal
|
|
|
|
|
title={`编译日志 - ${compileInstance?.name || compileInstance?.identifier || ''}`}
|
|
|
|
|
visible={compileModalVisible}
|
|
|
|
|
onCancel={handleCloseCompileModal}
|
|
|
|
|
footer={null}
|
|
|
|
|
style={{ width: '70%' }}
|
|
|
|
|
>
|
|
|
|
|
<TextArea
|
|
|
|
|
value={compileLogsMap[compileInstance?.id] || ''}
|
|
|
|
|
readOnly
|
|
|
|
|
placeholder="等待编译输出..."
|
|
|
|
|
style={{
|
|
|
|
|
minHeight: 400,
|
|
|
|
|
fontFamily: 'monospace',
|
|
|
|
|
fontSize: 13
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
</Modal>
|
|
|
|
|
|
|
|
|
|
{/* 编辑实例信息 Modal */}
|
|
|
|
|
<EditInstanceModal
|
|
|
|
|
visible={editModalVisible}
|
|
|
|
|
|