You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

446 lines
14 KiB
TypeScript

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import React, { useEffect, useState } from 'react';
import styles from './style/index.module.less';
import {
Button,
Input,
Space,
Select,
Divider,
Table,
TableColumnProps,
Message,
Modal,
Notification,
Tag
} from '@arco-design/web-react';
import { getComponentClassify } from '@/api/componentClassify';
import { IconSearch } from '@arco-design/web-react/icon';
import ContainerModal from './containerModal';
import AddModal from './addModal';
import { getEnvConfigList, deleteEnvConfig, testEnv } from '@/api/componentDeployEnv';
import { startContainer, stopContainer } from '@/api/userContainer';
const Option = Select.Option;
const ComponentEnv = () => {
const [envType, setEnvType] = useState([]); // 环境类型
const [architectureType, setArchitectureType] = useState(['x86_64', 'aarch64']); // 结构类型
const [visible, setVisible] = useState(false);
const [visible1, setVisible1] = useState(false);
const [data, setData] = useState([]);
const [editingRecord, setEditingRecord] = useState(null);
const [selectedEnvType, setSelectedEnvType] = useState(null); // 选中的环境类型
const [selectedArch, setSelectedArch] = useState(null); // 选中的架构类型
const [searchText, setSearchText] = useState(''); // 搜索文本
const [addItem, setAddItem] = useState(null); // 点击环境配置按钮时记录当前信息
const [loadingActions, setLoadingActions] = useState<{ id: number | null; action: string | null }>({
id: null,
action: null
}); // 按钮loading状态
const columns: TableColumnProps[] = [
{
title: '环境IP',
dataIndex: 'ip'
},
{
title: 'docker端口',
dataIndex: 'dockerTcpPort'
},
{
title: '环境类型',
dataIndex: 'type'
},
{
title: '架构类型',
dataIndex: 'arch'
},
{
title: '环境别名',
dataIndex: 'name'
},
{
title: '备注',
dataIndex: 'description'
},
{
title: '实例数量',
dataIndex: 'instanceCount'
},
{
title: '用户容器状态',
dataIndex: 'isEnable',
render: (_, record: any) => (
<div>
{record.isEnable === 'NOT_EXIST' && (
<Tag color="gray">
</Tag>
)}
{record.isEnable === 'RUNNING' && (
<Tag color="green">
</Tag>
)}
{record.isEnable === 'STOPPED' && (
<Tag color="red">
</Tag>
)}
</div>
)
},
{
title: '测试状态',
dataIndex: 'available',
render: (_, record: any) => (
<div>
{record.available == '0' && (
<Tag color="gray">
</Tag>
)}
{record.available == '1' && (
<Tag color="green">
</Tag>
)}
{record.available == '-1' && (
<Tag color="red">
</Tag>
)}
</div>
)
},
{
title: '操作',
width: 230,
align: 'center',
render: (_, record: any) => (
<Space>
{record.isEnable === 'NOT_EXIST' && record.available == '1' && (
<Button type="text" onClick={() => {
setAddItem(record);
setVisible1(true);
}}></Button>
)}
{record.isEnable === 'RUNNING' && (
<Button
type="text"
status="danger"
loading={loadingActions.id === record.id && loadingActions.action === 'stop'}
onClick={() => {
handleStop(record.id);
}}
>
</Button>
)}
{record.isEnable === 'STOPPED' && (
<Button
type="text"
loading={loadingActions.id === record.id && loadingActions.action === 'start'}
onClick={() => handleStart(record.id)}
>
</Button>
)}
<Button
type="text"
loading={loadingActions.id === record.id && loadingActions.action === 'test'}
onClick={() => handleTestEnv(record)}
>
</Button>
<Button type="text" onClick={() => handleConfigEnv(record)}></Button>
<Button type="text" status="danger" onClick={() => handleDeleteEnv(record)}></Button>
</Space>
)
}
];
const handleStop = async (id) => {
setLoadingActions({ id, action: 'stop' });
const loadingMessage = Message.loading('正在停止容器,请稍候...');
try {
const res: any = await stopContainer(id);
loadingMessage();
if (res.code === 200) {
Message.success('停止成功');
getEnvList();
}
else {
Message.error('停止失败: ' + res.message);
}
} catch (error) {
loadingMessage();
Message.error('停止失败: ' + error.message);
} finally {
setLoadingActions({ id: null, action: null });
}
};
const handleStart = async (id) => {
setLoadingActions({ id, action: 'start' });
const loadingMessage = Message.loading('正在启动容器,请稍候...');
try {
const res: any = await startContainer(id);
loadingMessage();
if (res.code === 200) {
Message.success('启动成功');
getEnvList();
}
else {
Message.error('启动失败: ' + res.message);
}
} catch (error) {
loadingMessage();
Message.error('启动失败: ' + error.message);
} finally {
setLoadingActions({ id: null, action: null });
}
};
// 环境测试处理函数
const handleTestEnv = async (record: any) => {
setLoadingActions({ id: record.id, action: 'test' });
Message.loading({
content: `正在测试环境 ${record.name}...`,
duration: 0
});
try {
const res: any = await testEnv(record.id);
if (res.code === 200 && res.data) {
Notification.success({
closable: false,
title: '测试成功',
content: `环境 【${record.name}】 测试成功`
});
}
else {
Notification.error({
closable: false,
title: '测试失败',
content: `环境 【${record.name}】 测试失败: ${res.message}`
});
}
} finally {
Message.clear();
setLoadingActions({ id: null, action: null });
}
};
// 环境配置处理函数
const handleConfigEnv = (record: any) => {
// 设置编辑记录
setEditingRecord(record);
// 打开模态框
setVisible(true);
};
// 删除环境处理函数
const handleDeleteEnv = (record: any) => {
Modal.confirm({
title: '删除环境',
content: (
<div>
<p></p>
<p style={{ color: 'red', fontWeight: 'bold' }}> {record.name}</p>
<p> <strong>{record.name}</strong> :</p>
<Input placeholder={`请输入 ${record.name}`} id="confirm-env-name" />
</div>
),
okButtonProps: { status: 'danger' },
onOk: async () => {
// 获取用户输入的环境名称
const confirmInput = document.getElementById('confirm-env-name') as HTMLInputElement;
if (!confirmInput || confirmInput.value !== record.name) {
Message.error('环境名称输入不匹配,请重新输入');
return Promise.reject();
}
try {
const res: any = await deleteEnvConfig(record.id);
if (res.code === 200) {
Message.success('环境删除成功');
// 重新加载数据
getEnvList();
}
else {
Message.error('环境删除失败: ' + res.message);
}
} catch (error) {
Message.error('环境删除失败: ' + error.message);
}
}
});
};
// 获取环境列表,根据选择的类型和架构进行过滤
const getEnvList = async (filterParams?: any) => {
// 构造查询参数
const params: any = {
current: 1,
size: 10
};
// 合并筛选参数
if (filterParams) {
Object.assign(params, filterParams);
}
else {
// 如果没有传入参数,使用当前状态值
if (selectedEnvType) params.type = selectedEnvType;
if (selectedArch) params.arch = selectedArch;
if (searchText) params.name = searchText;
}
const res: any = await getEnvConfigList(params);
if (res.code === 200) setData(res.data.list);
};
const getEnvType = async () => {
const res: any = await getComponentClassify('docker-env');
if (res.code === 200) setEnvType(res.data);
};
useEffect(() => {
getEnvType();
getEnvList();
}, []);
return (
<>
<div className={styles['component-env']}>
<div className={styles['component-env-header']}>
<div className={styles['component-env-header-left']}>
<Space size={20}>
<div className={styles['handle-row-item']}>
<span></span>
<Select
placeholder="选择环境类型"
style={{ width: 154 }}
allowClear
value={selectedEnvType}
onChange={(value) => {
setSelectedEnvType(value);
// 构建查询参数,包含所有当前筛选条件
const params: any = {};
if (value) params.type = value;
if (selectedArch) params.arch = selectedArch;
if (searchText) params.name = searchText;
getEnvList(params);
}}
>
{envType.map((option, index) => (
<Option key={option.id} value={option.classifyName}>
{option.classifyName}
</Option>
))}
</Select>
</div>
<div className={styles['handle-row-item']}>
<span></span>
<Select
placeholder="选择架构类型"
style={{ width: 154 }}
allowClear
value={selectedArch}
onChange={(value) => {
setSelectedArch(value);
// 构建查询参数,包含所有当前筛选条件
const params: any = {};
if (selectedEnvType) params.type = selectedEnvType;
if (value) params.arch = value;
if (searchText) params.name = searchText;
getEnvList(params);
}}
>
{architectureType.map((option, index) => (
<Option key={option} value={option}>
{option}
</Option>
))}
</Select>
</div>
</Space>
</div>
<div className={styles['component-env-header-right']}>
<Space split={<Divider type="vertical" />}>
<div>
<Input
prefix={<IconSearch />}
placeholder={'搜索'}
style={{ width: 236, marginRight: 5 }}
value={searchText}
onChange={(value) => setSearchText(value)}
onPressEnter={() => {
// 构建查询参数,包含所有当前筛选条件
const params: any = {};
if (selectedEnvType) params.type = selectedEnvType;
if (selectedArch) params.arch = selectedArch;
if (searchText) params.name = searchText;
getEnvList(params);
}}
/>
<Button
type="primary"
style={{ borderRadius: 4 }}
onClick={() => {
// 构建查询参数,包含所有当前筛选条件
const params: any = {};
if (selectedEnvType) params.type = selectedEnvType;
if (selectedArch) params.arch = selectedArch;
if (searchText) params.name = searchText;
getEnvList(params);
}}
>
</Button>
</div>
<Button
type="primary"
style={{ borderRadius: 4 }}
onClick={() => {
setEditingRecord(null);
setVisible(true);
}}
>
+
</Button>
</Space>
</div>
</div>
<Table columns={columns} data={data} />
<AddModal
visible={visible}
envType={envType}
setVisible={setVisible}
onOk={(value) => {
setEditingRecord(value);
getEnvList(); // 新增完成后刷新列表
}}
record={editingRecord}
/>
</div>
<ContainerModal
addItem={addItem}
envType={envType}
visible={visible1}
setVisible={setVisible1}
onSuccess={getEnvList}
/>
</>
);
};
export default ComponentEnv;