diff --git a/src/api/userContainer.ts b/src/api/userContainer.ts new file mode 100644 index 0000000..1152b61 --- /dev/null +++ b/src/api/userContainer.ts @@ -0,0 +1,19 @@ +import axios from 'axios'; + +// 公共路径 +const urlPrefix = '/api/v1/bpms-workbench'; + +// 容器注册/容器配置 +export function containerRegister(params) { + return axios.post(`${urlPrefix}/userContainer/register`, params); +} + +// 根据组件实例ID启动容器 +export function startContainer(deployEnvId) { + return axios.get(`${urlPrefix}/userContainer/${deployEnvId}/start`); +} + +// 根据组件实例ID停止容器 +export function stopContainer(deployEnvId) { + return axios.get(`${urlPrefix}/userContainer/${deployEnvId}/stop`); +} \ No newline at end of file diff --git a/src/pages/componentDevelopment/componentDeployment/listNode.tsx b/src/pages/componentDevelopment/componentDeployment/listNode.tsx index 81cc4a5..f7b63ff 100644 --- a/src/pages/componentDevelopment/componentDeployment/listNode.tsx +++ b/src/pages/componentDevelopment/componentDeployment/listNode.tsx @@ -59,7 +59,6 @@ const ListNode: React.FC = ({ componentData }) => { // 获取实例列表 const fetchInstanceList = async () => { - console.log("componentData:",componentData); if (!componentData?.identifier) return; setLoading(true); diff --git a/src/pages/componentDevelopment/componentEnv/containerModal.tsx b/src/pages/componentDevelopment/componentEnv/containerModal.tsx new file mode 100644 index 0000000..771026a --- /dev/null +++ b/src/pages/componentDevelopment/componentEnv/containerModal.tsx @@ -0,0 +1,374 @@ +import React, { useEffect, useState } from 'react' ; +import { Modal, Form, Select, Grid, Slider, Switch, Input, Message } from '@arco-design/web-react'; +import EditableTable from '@/components/EditableTable'; +import { createInstance, localStart } from '@/api/componentInstance'; +import { getHostList } from '@/api/componentDeployEnv'; + +const FormItem = Form.Item; +const Option = Select.Option; + +const runTypes = [ + { + label: '本地运行', + value: 'local' + }, + { + label: '线上运行', + value: 'online' + } +]; + +const portColumns = [ + { + title: '主机端口', + dataIndex: 'hostPort', + editable: true + }, + { + title: '容器端口', + dataIndex: 'containerPort', + editable: true + }, + { + title: '备注', + dataIndex: 'remake', + editable: true + } +]; +const directoryColumns = [ + { + title: '主机路径', + dataIndex: 'hostPath', + editable: true + }, + { + title: '容器路径', + dataIndex: 'containerPath', + editable: true + }, + { + title: '备注', + dataIndex: 'remake', + editable: true + } +]; +const deviceColumns = [ + { + title: '主机路径', + dataIndex: 'hostPath', + editable: true + }, + { + title: '容器路径', + dataIndex: 'containerPath', + editable: true + }, + { + title: '权限', + dataIndex: 'permissions', + editable: true + }, + { + title: '备注', + dataIndex: 'remake', + editable: true + } +]; + +const ContainerModal = ({ addItem, visible, envType, setVisible, onSuccess }) => { + const [form] = Form.useForm(); + const [currentRunType, setCurrentRunType] = useState('local'); + const [envTypeOptions, setEnvTypeOptions] = useState([]); + const [portMappingData, setPortMappingData] = useState([]); // 端口映射数据 + const [directoryMountData, setDirectoryMountData] = useState([]); // 目录挂载数据 + const [deviceMountData, setDeviceMountData] = useState([]); // 设备挂载数据 + const [loading, setLoading] = useState(false); + const [hostOptions, setHostOptions] = useState([]); // 主机 + + const getHostOptions = async () => { + const res: any = await getHostList({ + type: 'docker-env', + componentBaseId: addItem.componentBaseId + }); + if (res.code === 200) setHostOptions(res.data); + }; + + // 处理取消 + const handleCancel = () => { + form.resetFields(); + setCurrentRunType('local'); + setPortMappingData([]); + setDirectoryMountData([]); + setDeviceMountData([]); + setVisible(false); + }; + + // 处理确认 + const handleOk = async () => { + try { + setLoading(true); + const values = await form.validate(); + + // 组装提交数据 + const submitData = { + ...values, + portMapping: portMappingData, // 端口映射 + directoryMount: directoryMountData, // 目录挂载 + deviceMount: deviceMountData, // 设备挂载 + componentBaseId: addItem?.componentBaseId + }; + + console.log('提交数据:', submitData); + + // TODO: 调用接口提交数据 + // const res = await createContainerConfig(submitData); + // if (res.code === 200) { + // Message.success('容器配置成功'); + // handleCancel(); + // onSuccess?.(); + // } else { + // Message.error('容器配置失败: ' + res.message); + // } + + } catch (error) { + console.error('表单校验失败:', error); + Message.error('请检查表单必填项'); + } finally { + setLoading(false); + } + }; + + useEffect(() => { + if (envType.length > 0) setEnvTypeOptions(envType); + }, [envType]); + + + useEffect(() => { + if (visible) { + getHostOptions(); + } + else { + // 关闭时重置表单和数据 + form.resetFields(); + setPortMappingData([]); + setDirectoryMountData([]); + setDeviceMountData([]); + } + }, [visible]); + + return ( + +
+ <> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + setPortMappingData(newData)} + showAddButton={true} + addButtonText="添加行" + showDeleteButton={true} + deleteButtonText="删除" + tableProps={{ + pagination: { pageSize: 5 }, + scroll: { y: 400 } + }} + /> + + + setDirectoryMountData(newData)} + showAddButton={true} + addButtonText="添加行" + showDeleteButton={true} + deleteButtonText="删除" + tableProps={{ + pagination: { pageSize: 5 }, + scroll: { y: 400 } + }} + /> + + + setDeviceMountData(newData)} + showAddButton={true} + addButtonText="添加行" + showDeleteButton={true} + deleteButtonText="删除" + tableProps={{ + pagination: { pageSize: 5 }, + scroll: { y: 400 } + }} + /> + + +
+ +
+ ); +}; + +export default ContainerModal; \ No newline at end of file diff --git a/src/pages/componentDevelopment/componentEnv/index.tsx b/src/pages/componentDevelopment/componentEnv/index.tsx index 1fac240..8b3fe00 100644 --- a/src/pages/componentDevelopment/componentEnv/index.tsx +++ b/src/pages/componentDevelopment/componentEnv/index.tsx @@ -10,12 +10,15 @@ import { TableColumnProps, Message, Modal, - Notification + 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; @@ -23,11 +26,13 @@ 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 columns: TableColumnProps[] = [ { @@ -58,12 +63,49 @@ const ComponentEnv = () => { title: '实例数量', dataIndex: 'instanceCount' }, + { + title: '用户容器状态', + dataIndex: 'isEnable', + render: (_, record: any) => ( +
+ {record.isEnable === 'NOT_EXIST' && ( + + 未注册 + + )} + {record.isEnable === 'RUNNING' && ( + + 运行 + + )} + {record.isEnable === 'STOPPED' && ( + + 停止 + + )} +
+ ) + }, { title: '操作', width: 230, align: 'center', render: (_, record: any) => ( + {record.isEnable === 'NOT_EXIST' && ( + + )} + {record.isEnable === 'RUNNING' && ( + + )} + {record.isEnable === 'STOPPED' && ( + + )} @@ -72,6 +114,42 @@ const ComponentEnv = () => { } ]; + const handleStop = async (id) => { + 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); + } + }; + + const handleStart = async (id) => { + 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); + } + }; + // 环境测试处理函数 const handleTestEnv = async (record: any) => { Message.info(`正在测试环境 ${record.name}...`); @@ -166,96 +244,107 @@ const ComponentEnv = () => { }, []); return ( -
-
-
- -
- 环境类型: - -
-
- 架构类型: - -
-
-
+ <> +
+
+
+ +
+ 环境类型: + +
+
+ 架构类型: + +
+
+
-
- }> -
- } - placeholder={'搜索'} - style={{ width: 236, marginRight: 5 }} - value={searchText} - onChange={(value) => setSearchText(value)} - /> +
+ }> +
+ } + placeholder={'搜索'} + style={{ width: 236, marginRight: 5 }} + value={searchText} + onChange={(value) => setSearchText(value)} + /> + +
-
- - + +
+ + + + { + setEditingRecord(value); + getEnvList(); // 新增完成后刷新列表 + }} + record={editingRecord} + /> -
- { - setEditingRecord(value); - getEnvList(); // 新增完成后刷新列表 - }} - record={editingRecord} + visible={visible1} + setVisible={setVisible1} + onSuccess={getEnvList} /> - + ); };