feat(componentEnv): 新增组件环境配置功能

- 修改getComponentClassify函数参数类型为string
- 新增ComponentEnv组件及相关子组件
- 实现环境配置的增删改查功能
- 添加环境测试与配置文件下载功能
- 创建环境类型与架构类型的下拉选项
- 实现分步表单提交与证书生成逻辑
- 添加环境删除二次确认与实例数量展示
- 集成环境配置API接口与数据请求逻辑
master
钟良源 3 months ago
parent 82f4001768
commit 319e917676

@ -4,6 +4,6 @@ import axios from 'axios';
const urlPrefix = '/api/v1/bpms-workbench'; const urlPrefix = '/api/v1/bpms-workbench';
// 我的组件 // 我的组件
export function getComponentClassify(classifyType: 'component' | 'language_type') { export function getComponentClassify(classifyType: string) {
return axios.get(`${urlPrefix}/componentClassify/list?classifyType=${classifyType}`); return axios.get(`${urlPrefix}/componentClassify/list?classifyType=${classifyType}`);
} }

@ -0,0 +1,24 @@
import axios from 'axios';
// 公共路径
const urlPrefix = '/api/v1/bpms-workbench';
// 提交环境配置信息
export function submitEnvConfig(params) {
return axios.post(`${urlPrefix}/componentDeployEnv/submit`, params);
}
// 环境配置列表
export function getEnvConfigList(current: string | number, size: string | number) {
return axios.get(`${urlPrefix}/componentDeployEnv/page?current=${current}&size=${size}`);
}
// 删除环境配置
export function deleteEnvConfig(id: string) {
return axios.post(`${urlPrefix}/componentDeployEnv/remove?id=${id}`);
}
// 下载环境配置文件
export function downloadEnvConfigFile(id: string) {
return axios.post(`${urlPrefix}/componentDeployEnv/download`, { id });
}

@ -0,0 +1,113 @@
import React, { useEffect, useState, useRef } from 'react';
import { Modal, Steps, Select, Grid, Input, Form, Message, Button } from '@arco-design/web-react';
import EnvExtra from '@/pages/componentDevelopment/componentEnv/envExtra';
import FormEditor, { FormEditorInstance } from '@/pages/componentDevelopment/componentEnv/formEditor';
const Step = Steps.Step;
// 定义环境类型选项的接口
interface EnvTypeOption {
id: string | number;
classifyType: string;
classifyName: string;
dictKey: string;
}
// 定义组件 Props 类型
interface AddModalProps {
visible: boolean;
envType: EnvTypeOption[];
setVisible: (visible: boolean) => void;
onOk?: () => void;
}
const AddModal = ({ visible, envType, setVisible, onOk }: AddModalProps) => {
const [currentStep, setCurrentStep] = useState(2);
const [envTypeOptions, setEnvTypeOptions] = useState<EnvTypeOption[]>([]);
const [confirmLoading, setConfirmLoading] = useState(false);
const [currentEnvData,setCurrentEnvData] = useState<any>({});
const formRef = useRef<FormEditorInstance>(null);
useEffect(() => {
if (envType.length > 0) setEnvTypeOptions(envType);
}, [envType]);
const handleOk = () => {
// 当前步骤为1时触发表单提交
if (currentStep === 1) {
formRef.current && formRef.current.handleOk();
return;
}
// 其他步骤的确认逻辑可以在这里添加
console.log('ok');
};
const handleCancel = () => {
setCurrentStep(1);
setVisible(false);
};
const handleStepOneSuccess = (value) => {
setCurrentStep(2);
setCurrentEnvData(value)
onOk && onOk();
};
// 返回第一步的处理函数
const handleBackToStepOne = () => {
setCurrentStep(1);
};
return (
<Modal
title="新增环境"
visible={visible}
onOk={handleOk}
onCancel={handleCancel}
autoFocus={false}
focusLock={true}
style={{ width: 1280 }}
okText={currentStep === 1 ? '创建并生成证书' : '完成'}
confirmLoading={confirmLoading}
footer={[
<Button key="cancel" onClick={handleCancel}></Button>,
currentStep === 2 && (
<Button key="back" onClick={handleBackToStepOne}></Button>
),
<Button
key="ok"
type="primary"
loading={confirmLoading}
onClick={handleOk}
>
{currentStep === 1 ? '创建并生成证书' : '完成'}
</Button>
]}
>
<Steps current={currentStep} style={{ maxWidth: 780, margin: '0 auto' }}>
<Step title="基础配置" />
<Step title="配置文件与环境测试" />
</Steps>
{currentStep === 1 && (
<FormEditor
ref={formRef}
envTypeOptions={envTypeOptions}
setVisible={setVisible}
setConfirmLoading={setConfirmLoading}
onSuccess={(value) => handleStepOneSuccess(value)}
/>
)}
{currentStep === 2 && (
<EnvExtra
currentEnvData={currentEnvData}
/>
)}
</Modal>
);
};
export default AddModal;

@ -0,0 +1,66 @@
import React from 'react';
import { Button, Space } from '@arco-design/web-react';
import { downloadEnvConfigFile } from '@/api/componentDeployEnv';
const EnvExtra = ({ currentEnvData }) => {
// 模拟下载配置证书文件
const handleDownloadConfig = () => {
// 这里可以添加实际的下载逻辑
console.log('下载配置证书文件');
downloadEnvConfigFile(currentEnvData.id);
};
// 模拟查看环境配置教程
const handleViewTutorial = () => {
console.log('查看环境配置教程');
// 这里可以打开一个新的页面或弹窗显示教程内容
};
// 模拟点击测试环境可用性
const handleTestAvailability = () => {
console.log('点击测试环境可用性');
// 这里可以添加实际的环境可用性测试逻辑
};
return (
<div style={{ padding: '20px', borderRadius: 8 }}>
<div style={{ marginBottom: 20 }}>
<div style={{ display: 'flex', alignItems: 'center', marginBottom: 10 }}>
<span style={{ fontSize: 14, fontWeight: 500 }}></span>
<Button
type="text"
onClick={handleDownloadConfig}
>
</Button>
</div>
<div style={{ display: 'flex', alignItems: 'center', marginLeft: 15 }}>
<span style={{ color: '#FFA500', marginRight: 8 }}></span>
<span style={{ fontSize: 13, color: '#666' }}></span>
<Button
type="text"
onClick={handleViewTutorial}
style={{ marginLeft: 4 }}
>
</Button>
</div>
</div>
<div>
<div style={{ display: 'flex', alignItems: 'center' }}>
<span style={{ fontSize: 14, fontWeight: 500 }}></span>
<Button
type="outline"
onClick={handleTestAvailability}
style={{ marginLeft: 16 }}
>
</Button>
</div>
</div>
</div>
);
};
export default EnvExtra;

@ -0,0 +1,182 @@
import React, { useState, forwardRef, useImperativeHandle } from 'react';
import { Form, Grid, Input, Message, Select } from '@arco-design/web-react';
import { submitEnvConfig } from '@/api/componentDeployEnv';
const FormItem = Form.Item;
const Option = Select.Option;
const TextArea = Input.TextArea;
// 定义组件 Props 类型
interface FormEditorProps {
envTypeOptions: { id: string | number; classifyType: string; classifyName: string, dictKey: string }[];
setVisible: (visible: boolean) => void;
onOk?: () => void;
setConfirmLoading?: (loading: boolean) => void;
onSuccess?: (value) => void;
}
// 定义暴露给父组件的实例类型
export interface FormEditorInstance {
handleOk: () => void;
}
const FormEditor = forwardRef<FormEditorInstance, FormEditorProps>(({
envTypeOptions,
setConfirmLoading,
onSuccess
}, ref) => {
const [form] = Form.useForm();
// 暴露方法给父组件调用
useImperativeHandle(ref, () => ({
handleOk
}));
// 处理完成/确认逻辑
const handleOk = async () => {
try {
const values = await form.validate();
const params = {
...values,
instanceCount: '',
description: values.description ? values.description : ''
};
// 设置确认按钮为加载状态
setConfirmLoading && setConfirmLoading(true);
try {
// 等待接口回调
const res = await submitEnvConfig(params);
Message.success('环境创建成功');
// 如果有onSuccess回调则调用它用于切换步骤
if (onSuccess) {
onSuccess(res.data);
}
} catch (error) {
Message.error('环境创建失败: ' + error.message);
// 保持模态框打开,让用户可以看到错误信息
} finally {
// 无论成功还是失败,都取消加载状态
setConfirmLoading && setConfirmLoading(false);
}
} catch (error) {
// 表单验证失败,不关闭模态框
console.log('表单验证失败:', error);
setConfirmLoading && setConfirmLoading(false);
}
};
return (
<Form form={form} layout="vertical">
<Grid.Row gutter={8}>
<Grid.Col span={12}>
<FormItem label="环境IP" field="ip" required rules={[
{
validator(value, cb) {
if (!value) {
return cb('请输入环境IP');
}
return cb();
}
}
]}>
<Input style={{ width: '90%' }} allowClear placeholder="请输入环境IP" />
</FormItem>
</Grid.Col>
<Grid.Col span={12}>
<FormItem label="docker端口" field="dockerTcpPort" required rules={[
{
validator(value, cb) {
if (!value) {
return cb('请输入docker端口');
}
return cb();
}
}
]}>
<Input style={{ width: '90%' }} allowClear placeholder="请输入docker端口" />
</FormItem>
</Grid.Col>
</Grid.Row>
<Grid.Row gutter={8}>
<Grid.Col span={12}>
<FormItem label="环境类型:" field="type" required rules={[
{
validator(value, cb) {
if (!value) {
return cb('请选择环境类型');
}
return cb();
}
}
]}>
<Select
placeholder="请选择环境类型"
style={{ width: '90%' }}
>
{envTypeOptions.map((option, index) => (
<Option key={option.id} value={option.dictKey}>
{option.classifyName}
</Option>
))}
</Select>
</FormItem>
</Grid.Col>
<Grid.Col span={12}>
<FormItem label="架构类型:" field="arch" required rules={[
{
validator(value, cb) {
if (!value) {
return cb('请选择架构类型');
}
return cb();
}
}
]}>
<Select
placeholder="请选择架构类型"
style={{ width: '90%' }}
>
{['x86_64', 'aarch64'].map((option, index) => (
<Option key={option} disabled={index === 3} value={option}>
{option}
</Option>
))}
</Select>
</FormItem>
</Grid.Col>
</Grid.Row>
<Grid.Row gutter={8}>
<Grid.Col span={12}>
<FormItem label="环境别名:" field="name" required rules={[
{
validator(value, cb) {
if (!value) {
return cb('请输入环境别名');
}
return cb();
}
}
]}>
<Input style={{ width: '90%' }} allowClear placeholder="请输入环境别名" />
</FormItem>
</Grid.Col>
<Grid.Col span={12}>
<FormItem label="备注:" field="description">
<TextArea style={{ width: '90%' }} allowClear placeholder="请输入备注" />
</FormItem>
</Grid.Col>
</Grid.Row>
</Form>
);
});
export default FormEditor;

@ -0,0 +1,196 @@
import React, { useEffect, useState } from 'react';
import styles from './style/index.module.less';
import { Button, Input, Space, Select, Divider, Table, TableColumnProps, Message, Modal } from '@arco-design/web-react';
import { getComponentClassify } from '@/api/componentClassify';
import { IconSearch } from '@arco-design/web-react/icon';
import AddModal from './addModal';
import { getEnvConfigList, deleteEnvConfig } from '@/api/componentDeployEnv';
const Option = Select.Option;
const ComponentEnv = () => {
const [envType, setEnvType] = useState([]); // 环境类型
const [architectureType, setArchitectureType] = useState(['x86_64', 'aarch64']); // 结构类型
const [visible, setVisible] = useState(false);
const [data, setData] = useState([]);
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: '操作',
width: 230,
align: 'center',
render: (_, record: any) => (
<Space>
<Button type="text" onClick={() => handleTestEnv(record)}></Button>
<Button type="text" onClick={() => handleConfigEnv(record)}></Button>
<Button type="text" status="danger" onClick={() => handleDeleteEnv(record)}></Button>
</Space>
)
}
];
// 环境测试处理函数
const handleTestEnv = (record: any) => {
Message.info(`正在测试环境 ${record.name}...`);
// 这里可以添加实际的环境测试逻辑
console.log('测试环境:', record);
};
// 环境配置处理函数
const handleConfigEnv = (record: any) => {
Message.info(`配置环境 ${record.name}`);
// 这里可以添加环境配置逻辑,比如打开配置模态框
console.log('配置环境:', record);
};
// 删除环境处理函数
const handleDeleteEnv = (record: any) => {
Modal.confirm({
title: '删除环境',
content: (
<div>
<p></p>
<p style={{ color: 'red', fontWeight: 'bold' }}> {record.name} IP: {record.ip}</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 () => {
const res: any = await getEnvConfigList(1, 10);
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 }}
>
{envType.map((option, index) => (
<Option key={option.id} value={option.id}>
{option.classifyName}
</Option>
))}
</Select>
</div>
<div className={styles['handle-row-item']}>
<span></span>
<Select
placeholder="选择架构类型"
style={{ width: 154 }}
>
{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" />}>
<Input
prefix={<IconSearch />}
placeholder={'搜索'}
style={{ width: 236 }}
/>
<Button
type="primary"
style={{ borderRadius: 4 }}
onClick={() => setVisible(true)}
>
+
</Button>
</Space>
</div>
</div>
<Table columns={columns} data={data} />
<AddModal
visible={visible}
envType={envType}
setVisible={setVisible}
onOk={() => {
console.log('Add component env');
getEnvList(); // 新增完成后刷新列表
}}
/>
</div>
);
};
export default ComponentEnv;

@ -0,0 +1,12 @@
.component-env {
height: 98%;
background-color: #ffffff;
padding: 17px 19px 0 24px;
.component-env-header {
display: flex;
justify-content: space-between;
padding-bottom: 15px;
border-bottom: 2px solid #E5E7EB;
}
}

@ -30,6 +30,7 @@ import ComponentList from '@/pages/componentDevelopment/componentList';
import ComponentCoding from '@/pages/componentDevelopment/componentCoding'; import ComponentCoding from '@/pages/componentDevelopment/componentCoding';
import ComponentDeployment from '@/pages/componentDevelopment/componentDeployment'; import ComponentDeployment from '@/pages/componentDevelopment/componentDeployment';
import ComponentTest from '@/pages/componentDevelopment/componentTest'; import ComponentTest from '@/pages/componentDevelopment/componentTest';
import ComponentEnv from "@/pages/componentDevelopment/componentEnv"
import { getUserToken } from '@/api/user'; import { getUserToken } from '@/api/user';
import { Message } from '@arco-design/web-react'; import { Message } from '@arco-design/web-react';
import { queryEventItemBySceneId, queryEventItemBySceneIdOld } from '@/api/event'; import { queryEventItemBySceneId, queryEventItemBySceneIdOld } from '@/api/event';
@ -45,7 +46,6 @@ const AppInstanceComponent = () => <div style={{ height: '100%', width: '100%' }
const MyComponents = () => <div style={{ height: '100%', width: '100%' }}></div>; const MyComponents = () => <div style={{ height: '100%', width: '100%' }}></div>;
const MatchingComponents = () => <div style={{ height: '100%', width: '100%' }}></div>; const MatchingComponents = () => <div style={{ height: '100%', width: '100%' }}></div>;
const ComponentReview = () => <div style={{ height: '100%', width: '100%' }}></div>; const ComponentReview = () => <div style={{ height: '100%', width: '100%' }}></div>;
const EnvConfig = () => <div style={{ height: '100%', width: '100%' }}></div>;
// 所有可显示的组件路径列表 // 所有可显示的组件路径列表
const ALL_PATHS = [ const ALL_PATHS = [
@ -344,7 +344,7 @@ function IDEContainer() {
case 'componentTest': case 'componentTest':
return <ComponentTest />; return <ComponentTest />;
case 'envConfig': case 'envConfig':
return <EnvConfig />; return <ComponentEnv />;
default: default:
return <div></div>; return <div></div>;
} }

Loading…
Cancel
Save