1
This commit is contained in:
parent
8f5c05d16f
commit
224a3fe5cc
@ -1,11 +1,11 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { Modal, Form, Input, InputNumber, Radio, message, Select } from 'antd';
|
||||
import React, {useState} from 'react';
|
||||
import {Modal, Form, Input, Select, Switch, InputNumber, App} from 'antd';
|
||||
import type {Application} from '../types';
|
||||
import { createApplication, updateApplication } from '../service';
|
||||
import {DevelopmentLanguageTypeEnum} from '../types';
|
||||
import {createApplication, updateApplication} from '../service';
|
||||
|
||||
interface ApplicationModalProps {
|
||||
visible: boolean;
|
||||
open: boolean;
|
||||
onCancel: () => void;
|
||||
onSuccess: () => void;
|
||||
initialValues?: Application;
|
||||
@ -13,68 +13,58 @@ interface ApplicationModalProps {
|
||||
}
|
||||
|
||||
const ApplicationModal: React.FC<ApplicationModalProps> = ({
|
||||
visible,
|
||||
open,
|
||||
onCancel,
|
||||
onSuccess,
|
||||
initialValues,
|
||||
projectGroupId,
|
||||
}) => {
|
||||
const [form] = Form.useForm();
|
||||
const isEdit = !!initialValues;
|
||||
|
||||
useEffect(() => {
|
||||
if (visible) {
|
||||
if (initialValues) {
|
||||
form.setFieldsValue({
|
||||
...initialValues,
|
||||
});
|
||||
} else {
|
||||
form.setFieldsValue({
|
||||
projectGroupId,
|
||||
enabled: true,
|
||||
sort: 0,
|
||||
language: DevelopmentLanguageTypeEnum.JAVA,
|
||||
});
|
||||
}
|
||||
}
|
||||
}, [visible, initialValues, form, projectGroupId]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const {message: messageApi} = App.useApp();
|
||||
const isEdit = !!initialValues?.id;
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const values = await form.validateFields();
|
||||
const submitData = {
|
||||
if (isEdit) {
|
||||
await updateApplication({
|
||||
...values,
|
||||
id: initialValues.id,
|
||||
});
|
||||
} else {
|
||||
await createApplication({
|
||||
...values,
|
||||
projectGroupId,
|
||||
};
|
||||
|
||||
if (isEdit) {
|
||||
await updateApplication({ ...submitData, id: initialValues?.id });
|
||||
message.success('更新成功');
|
||||
} else {
|
||||
await createApplication(submitData);
|
||||
message.success('创建成功');
|
||||
});
|
||||
}
|
||||
messageApi.success(`${isEdit ? '更新' : '创建'}成功`);
|
||||
form.resetFields();
|
||||
onSuccess();
|
||||
onCancel();
|
||||
form.resetFields();
|
||||
} catch (error) {
|
||||
message.error(isEdit ? '更新失败' : '创建失败');
|
||||
if (error instanceof Error) {
|
||||
messageApi.error(`${isEdit ? '更新' : '创建'}失败: ${error.message}`);
|
||||
} else {
|
||||
messageApi.error(`${isEdit ? '更新' : '创建'}失败`);
|
||||
}
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
form.resetFields();
|
||||
onCancel();
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={isEdit ? '编辑应用' : '新建应用'}
|
||||
open={visible}
|
||||
title={`${isEdit ? '编辑' : '新建'}应用`}
|
||||
open={open}
|
||||
onCancel={() => {
|
||||
form.resetFields();
|
||||
onCancel();
|
||||
}}
|
||||
onOk={handleSubmit}
|
||||
onCancel={handleCancel}
|
||||
confirmLoading={loading}
|
||||
maskClosable={false}
|
||||
destroyOnClose
|
||||
width={600}
|
||||
>
|
||||
<Form
|
||||
form={form}
|
||||
@ -82,38 +72,55 @@ const ApplicationModal: React.FC<ApplicationModalProps> = ({
|
||||
initialValues={{
|
||||
enabled: true,
|
||||
sort: 0,
|
||||
language: DevelopmentLanguageTypeEnum.JAVA,
|
||||
...initialValues,
|
||||
}}
|
||||
>
|
||||
{/* 隐藏的项目组ID字段 */}
|
||||
<Form.Item name="projectGroupId" hidden>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="appCode"
|
||||
label="应用编码"
|
||||
rules={[
|
||||
{required: true, message: '请输入应用编码'},
|
||||
{ pattern: /^[A-Za-z0-9_-]+$/, message: '应用编码只能包含字母、数字、下划线和连字符' }
|
||||
{max: 50, message: '应用编码不能超过50个字符'},
|
||||
]}
|
||||
tooltip="应用的唯一标识,创建后不可修改"
|
||||
>
|
||||
<Input placeholder="请输入应用编码" disabled={isEdit} />
|
||||
<Input
|
||||
placeholder="请输入应用编码"
|
||||
disabled={isEdit}
|
||||
maxLength={50}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="appName"
|
||||
label="应用名称"
|
||||
rules={[{ required: true, message: '请输入应用名称' }]}
|
||||
rules={[
|
||||
{required: true, message: '请输入应用名称'},
|
||||
{max: 50, message: '应用名称不能超过50个字符'},
|
||||
]}
|
||||
tooltip="应用的显示名称"
|
||||
>
|
||||
<Input placeholder="请输入应用名称" />
|
||||
<Input
|
||||
placeholder="请输入应用名称"
|
||||
maxLength={50}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="appDesc"
|
||||
label="应用描述"
|
||||
name="language"
|
||||
label="开发语言"
|
||||
rules={[{required: true, message: '请选择开发语言'}]}
|
||||
tooltip="应用的主要开发语言,创建后不可修改"
|
||||
>
|
||||
<Input.TextArea rows={4} placeholder="请输入应用描述" />
|
||||
<Select
|
||||
placeholder="请选择开发语言"
|
||||
disabled={isEdit}
|
||||
>
|
||||
<Select.Option value={DevelopmentLanguageTypeEnum.JAVA}>Java</Select.Option>
|
||||
<Select.Option value={DevelopmentLanguageTypeEnum.NODE_JS}>NodeJS</Select.Option>
|
||||
<Select.Option value={DevelopmentLanguageTypeEnum.PYTHON}>Python</Select.Option>
|
||||
<Select.Option value={DevelopmentLanguageTypeEnum.GO}>Go</Select.Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
@ -123,40 +130,42 @@ const ApplicationModal: React.FC<ApplicationModalProps> = ({
|
||||
{required: true, message: '请输入仓库地址'},
|
||||
{type: 'url', message: '请输入有效的URL地址'},
|
||||
]}
|
||||
tooltip="应用代码仓库的URL地址"
|
||||
>
|
||||
<Input placeholder="请输入仓库地址" />
|
||||
<Input
|
||||
placeholder="请输入仓库地址"
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="language"
|
||||
label="开发语言"
|
||||
rules={[{ required: true, message: '请选择开发语言' }]}
|
||||
name="appDesc"
|
||||
label="应用描述"
|
||||
rules={[{max: 200, message: '应用描述不能超过200个字符'}]}
|
||||
tooltip="应用的详细描述信息"
|
||||
>
|
||||
<Select placeholder="请选择开发语言">
|
||||
<Select.Option value={DevelopmentLanguageTypeEnum.JAVA}>Java</Select.Option>
|
||||
<Select.Option value={DevelopmentLanguageTypeEnum.NODE_JS}>NodeJS</Select.Option>
|
||||
<Select.Option value={DevelopmentLanguageTypeEnum.PYTHON}>Python</Select.Option>
|
||||
<Select.Option value={DevelopmentLanguageTypeEnum.GO}>Go</Select.Option>
|
||||
</Select>
|
||||
<Input.TextArea
|
||||
placeholder="请输入应用描述"
|
||||
maxLength={200}
|
||||
showCount
|
||||
rows={4}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="enabled"
|
||||
label="应用状态"
|
||||
rules={[{ required: true, message: '请选择应用状态' }]}
|
||||
label="状态"
|
||||
valuePropName="checked"
|
||||
tooltip="是否启用该应用"
|
||||
>
|
||||
<Radio.Group>
|
||||
<Radio value={true}>启用</Radio>
|
||||
<Radio value={false}>禁用</Radio>
|
||||
</Radio.Group>
|
||||
<Switch checkedChildren="启用" unCheckedChildren="禁用"/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="sort"
|
||||
label="排序"
|
||||
rules={[{ required: true, message: '请输入排序值' }]}
|
||||
tooltip="数字越小越靠前"
|
||||
>
|
||||
<InputNumber min={0} placeholder="请输入排序值" style={{ width: '100%' }} />
|
||||
<InputNumber min={0} style={{width: '100%'}}/>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
|
||||
@ -1,13 +1,11 @@
|
||||
import React, {useState, useEffect} from 'react';
|
||||
import {PageContainer} from '@ant-design/pro-layout';
|
||||
import {Button, message, Popconfirm, Tag, Space, Tooltip, Select} from 'antd';
|
||||
import {Button, Space, Popconfirm, Tag, Select, App} from 'antd';
|
||||
import {
|
||||
PlusOutlined,
|
||||
EditOutlined,
|
||||
DeleteOutlined,
|
||||
CodeOutlined,
|
||||
CloudUploadOutlined,
|
||||
ApiOutlined,
|
||||
GithubOutlined,
|
||||
JavaOutlined,
|
||||
NodeIndexOutlined,
|
||||
@ -32,6 +30,7 @@ const ApplicationList: React.FC = () => {
|
||||
const [modalVisible, setModalVisible] = useState(false);
|
||||
const [currentApplication, setCurrentApplication] = useState<Application>();
|
||||
const actionRef = React.useRef<ActionType>();
|
||||
const {message: messageApi} = App.useApp();
|
||||
|
||||
// 获取项目列表
|
||||
const fetchProjects = async () => {
|
||||
@ -42,7 +41,7 @@ const ApplicationList: React.FC = () => {
|
||||
setSelectedProjectGroupId(data[0].id);
|
||||
}
|
||||
} catch (error) {
|
||||
message.error('获取项目组列表失败');
|
||||
messageApi.error('获取项目组列表失败');
|
||||
}
|
||||
};
|
||||
|
||||
@ -53,16 +52,16 @@ const ApplicationList: React.FC = () => {
|
||||
const handleDelete = async (id: number) => {
|
||||
try {
|
||||
await deleteApplication(id);
|
||||
message.success('删除成功');
|
||||
messageApi.success('删除成功');
|
||||
actionRef.current?.reload();
|
||||
} catch (error) {
|
||||
message.error('删除失败');
|
||||
messageApi.error('删除失败');
|
||||
}
|
||||
};
|
||||
|
||||
const handleAdd = () => {
|
||||
if (!selectedProjectGroupId) {
|
||||
message.warning('请先选择项目组');
|
||||
messageApi.warning('请先选择项目组');
|
||||
return;
|
||||
}
|
||||
setCurrentApplication(undefined);
|
||||
@ -79,6 +78,17 @@ const ApplicationList: React.FC = () => {
|
||||
actionRef.current?.reload();
|
||||
};
|
||||
|
||||
const handleModalClose = () => {
|
||||
setModalVisible(false);
|
||||
setCurrentApplication(undefined);
|
||||
};
|
||||
|
||||
const handleSuccess = () => {
|
||||
setModalVisible(false);
|
||||
setCurrentApplication(undefined);
|
||||
actionRef.current?.reload();
|
||||
};
|
||||
|
||||
// 获取开发语言信息
|
||||
const getLanguageInfo = (language: DevelopmentLanguageTypeEnum) => {
|
||||
switch (language) {
|
||||
@ -207,19 +217,20 @@ const ApplicationList: React.FC = () => {
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
width: 150,
|
||||
width: 180,
|
||||
key: 'action',
|
||||
valueType: 'option',
|
||||
fixed: 'right',
|
||||
align: 'center',
|
||||
render: (_, record) => [
|
||||
<Button
|
||||
key="edit"
|
||||
type="link"
|
||||
icon={<EditOutlined/>}
|
||||
onClick={() => handleEdit(record)}
|
||||
>
|
||||
<Space>
|
||||
<EditOutlined/>
|
||||
编辑
|
||||
</Space>
|
||||
</Button>,
|
||||
<Popconfirm
|
||||
key="delete"
|
||||
@ -230,22 +241,68 @@ const ApplicationList: React.FC = () => {
|
||||
<Button
|
||||
type="link"
|
||||
danger
|
||||
icon={<DeleteOutlined/>}
|
||||
>
|
||||
<Space>
|
||||
<DeleteOutlined/>
|
||||
删除
|
||||
</Space>
|
||||
</Button>
|
||||
</Popconfirm>,
|
||||
</Popconfirm>
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageContainer
|
||||
header={{
|
||||
title: '应用管理',
|
||||
extra: [
|
||||
<Select
|
||||
key="project-select"
|
||||
value={selectedProjectGroupId}
|
||||
onChange={handleProjectChange}
|
||||
style={{width: 200}}
|
||||
placeholder="请选择项目组"
|
||||
>
|
||||
{projectGroups.map((project) => (
|
||||
<Option key={project.id} value={project.id}>
|
||||
{project.projectGroupName}
|
||||
</Option>
|
||||
))}
|
||||
</Select>,
|
||||
],
|
||||
}}
|
||||
>
|
||||
<ProTable<Application>
|
||||
columns={columns}
|
||||
actionRef={actionRef}
|
||||
scroll={{x: 'max-content'}}
|
||||
cardBordered
|
||||
rowKey="id"
|
||||
search={false}
|
||||
options={{
|
||||
setting: false,
|
||||
density: false,
|
||||
fullScreen: false,
|
||||
reload: false,
|
||||
}}
|
||||
toolbar={{
|
||||
actions: [
|
||||
<Button
|
||||
key="add"
|
||||
type="primary"
|
||||
onClick={handleAdd}
|
||||
icon={<PlusOutlined/>}
|
||||
disabled={!selectedProjectGroupId}
|
||||
>
|
||||
新建应用
|
||||
</Button>
|
||||
],
|
||||
}}
|
||||
pagination={{
|
||||
pageSize: 10,
|
||||
showQuickJumper: true,
|
||||
}}
|
||||
request={async (params) => {
|
||||
if (!selectedProjectGroupId) {
|
||||
return {
|
||||
@ -254,9 +311,11 @@ const ApplicationList: React.FC = () => {
|
||||
total: 0,
|
||||
};
|
||||
}
|
||||
try {
|
||||
const queryParams: ApplicationQuery = {
|
||||
pageSize: params.pageSize,
|
||||
pageNum: params.current,
|
||||
projectGroupId: selectedProjectGroupId,
|
||||
appCode: params.appCode as string,
|
||||
appName: params.appName as string,
|
||||
enabled: params.enabled as boolean,
|
||||
@ -267,58 +326,27 @@ const ApplicationList: React.FC = () => {
|
||||
success: true,
|
||||
total: data.totalElements || 0,
|
||||
};
|
||||
} catch (error) {
|
||||
messageApi.error('获取应用列表失败');
|
||||
return {
|
||||
data: [],
|
||||
success: false,
|
||||
total: 0,
|
||||
};
|
||||
}
|
||||
}}
|
||||
rowKey="id"
|
||||
search={{
|
||||
labelWidth: 'auto',
|
||||
span: {
|
||||
xs: 24,
|
||||
sm: 12,
|
||||
md: 8,
|
||||
lg: 6,
|
||||
xl: 6,
|
||||
xxl: 6,
|
||||
},
|
||||
}}
|
||||
options={{
|
||||
setting: {
|
||||
listsHeight: 400,
|
||||
},
|
||||
}}
|
||||
form={{
|
||||
syncToUrl: true,
|
||||
}}
|
||||
pagination={{
|
||||
pageSize: 10,
|
||||
showQuickJumper: true,
|
||||
}}
|
||||
dateFormatter="string"
|
||||
toolBarRender={() => [
|
||||
<Button
|
||||
key="add"
|
||||
type="primary"
|
||||
onClick={handleAdd}
|
||||
icon={<PlusOutlined/>}
|
||||
disabled={!selectedProjectGroupId}
|
||||
>
|
||||
新建应用
|
||||
</Button>,
|
||||
]}
|
||||
/>
|
||||
|
||||
{selectedProjectGroupId && (
|
||||
{modalVisible && selectedProjectGroupId && (
|
||||
<ApplicationModal
|
||||
visible={modalVisible}
|
||||
onCancel={() => setModalVisible(false)}
|
||||
onSuccess={() => {
|
||||
setModalVisible(false);
|
||||
actionRef.current?.reload();
|
||||
}}
|
||||
open={modalVisible}
|
||||
onCancel={handleModalClose}
|
||||
onSuccess={handleSuccess}
|
||||
initialValues={currentApplication}
|
||||
projectGroupId={selectedProjectGroupId}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import React, {useEffect, useState, useCallback} from 'react';
|
||||
import {Modal, Form, Select, message, Switch, InputNumber} from 'antd';
|
||||
import {Modal, Form, Select, message, Switch, InputNumber, App} from 'antd';
|
||||
import type {DeploymentConfig, DeployConfigTemplate} from '../types';
|
||||
import {createDeploymentConfig, updateDeploymentConfig, getDeployConfigTemplates} from '../service';
|
||||
import {getApplicationList} from '../../../Application/List/service';
|
||||
@ -33,6 +33,7 @@ const DeploymentConfigModal: React.FC<DeploymentConfigModalProps> = ({
|
||||
const [selectedTemplate, setSelectedTemplate] = useState<DeployConfigTemplate>();
|
||||
const [buildVariables, setBuildVariables] = useState<Record<string, any>>({});
|
||||
const [loading, setLoading] = useState(false);
|
||||
const {message: messageApi} = App.useApp();
|
||||
const isEdit = !!initialValues?.id;
|
||||
|
||||
// 获取应用列表
|
||||
@ -41,9 +42,9 @@ const DeploymentConfigModal: React.FC<DeploymentConfigModalProps> = ({
|
||||
const data = await getApplicationList();
|
||||
setApplications(data);
|
||||
} catch (error) {
|
||||
message.error('获取应用列表失败');
|
||||
messageApi.error('获取应用列表失败');
|
||||
}
|
||||
}, []);
|
||||
}, [messageApi]);
|
||||
|
||||
// 获取配置模板
|
||||
const fetchTemplates = useCallback(async () => {
|
||||
@ -51,9 +52,9 @@ const DeploymentConfigModal: React.FC<DeploymentConfigModalProps> = ({
|
||||
const data = await getDeployConfigTemplates();
|
||||
setTemplates(data);
|
||||
} catch (error) {
|
||||
message.error('获取配置模板失败');
|
||||
messageApi.error('获取配置模板失败');
|
||||
}
|
||||
}, []);
|
||||
}, [messageApi]);
|
||||
|
||||
// 在模态框显示时获取数据
|
||||
useEffect(() => {
|
||||
@ -115,14 +116,14 @@ const DeploymentConfigModal: React.FC<DeploymentConfigModalProps> = ({
|
||||
} else {
|
||||
await createDeploymentConfig(submitData);
|
||||
}
|
||||
message.success(`${isEdit ? '更新' : '创建'}成功`);
|
||||
messageApi.success(`${isEdit ? '更新' : '创建'}成功`);
|
||||
form.resetFields();
|
||||
onSuccess();
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
message.error(`${isEdit ? '更新' : '创建'}失败: ${error.message}`);
|
||||
messageApi.error(`${isEdit ? '更新' : '创建'}失败: ${error.message}`);
|
||||
} else {
|
||||
message.error(`${isEdit ? '更新' : '创建'}失败`);
|
||||
messageApi.error(`${isEdit ? '更新' : '创建'}失败`);
|
||||
}
|
||||
} finally {
|
||||
setLoading(false);
|
||||
|
||||
@ -1,34 +1,30 @@
|
||||
import React, {useEffect} from 'react';
|
||||
import {Modal, Form, Input, InputNumber, Select, message} from 'antd';
|
||||
import React, {useState} from 'react';
|
||||
import {Modal, Form, Input, Select, Switch, InputNumber, App} from 'antd';
|
||||
import type {Environment} from '../types';
|
||||
import {BuildTypeEnum, DeployTypeEnum} from '../types';
|
||||
import {createEnvironment, updateEnvironment} from '../service';
|
||||
import {getBuildTypeInfo, getDeployTypeInfo} from '../utils';
|
||||
|
||||
interface EnvironmentModalProps {
|
||||
visible: boolean;
|
||||
open: boolean;
|
||||
onCancel: () => void;
|
||||
onSuccess: () => void;
|
||||
initialValues?: Environment;
|
||||
}
|
||||
|
||||
const EnvironmentModal: React.FC<EnvironmentModalProps> = ({
|
||||
visible,
|
||||
open,
|
||||
onCancel,
|
||||
onSuccess,
|
||||
initialValues,
|
||||
}) => {
|
||||
const [form] = Form.useForm();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const {message: messageApi} = App.useApp();
|
||||
const isEdit = !!initialValues?.id;
|
||||
|
||||
useEffect(() => {
|
||||
if (visible && initialValues) {
|
||||
form.setFieldsValue(initialValues);
|
||||
}
|
||||
}, [visible, initialValues, form]);
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const values = await form.validateFields();
|
||||
if (isEdit) {
|
||||
await updateEnvironment({
|
||||
@ -38,30 +34,40 @@ const EnvironmentModal: React.FC<EnvironmentModalProps> = ({
|
||||
} else {
|
||||
await createEnvironment(values);
|
||||
}
|
||||
message.success(`${isEdit ? '更新' : '创建'}成功`);
|
||||
messageApi.success(`${isEdit ? '更新' : '创建'}成功`);
|
||||
form.resetFields();
|
||||
onSuccess();
|
||||
} catch (error) {
|
||||
message.error(`${isEdit ? '更新' : '创建'}失败`);
|
||||
if (error instanceof Error) {
|
||||
messageApi.error(`${isEdit ? '更新' : '创建'}失败: ${error.message}`);
|
||||
} else {
|
||||
messageApi.error(`${isEdit ? '更新' : '创建'}失败`);
|
||||
}
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={`${isEdit ? '编辑' : '新建'}环境`}
|
||||
open={visible}
|
||||
open={open}
|
||||
onCancel={() => {
|
||||
form.resetFields();
|
||||
onCancel();
|
||||
}}
|
||||
onOk={handleSubmit}
|
||||
width={600}
|
||||
confirmLoading={loading}
|
||||
maskClosable={false}
|
||||
destroyOnClose
|
||||
>
|
||||
<Form
|
||||
form={form}
|
||||
layout="vertical"
|
||||
initialValues={{
|
||||
enabled: true,
|
||||
sort: 0,
|
||||
...initialValues,
|
||||
}}
|
||||
>
|
||||
<Form.Item
|
||||
@ -69,10 +75,15 @@ const EnvironmentModal: React.FC<EnvironmentModalProps> = ({
|
||||
label="环境编码"
|
||||
rules={[
|
||||
{required: true, message: '请输入环境编码'},
|
||||
{max: 50, message: '环境编码长度不能超过50个字符'},
|
||||
{max: 50, message: '环境编码不能超过50个字符'},
|
||||
]}
|
||||
tooltip="环境的唯一标识,创建后不可修改"
|
||||
>
|
||||
<Input placeholder="请输入环境编码"/>
|
||||
<Input
|
||||
placeholder="请输入环境编码"
|
||||
disabled={isEdit}
|
||||
maxLength={50}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
@ -80,74 +91,75 @@ const EnvironmentModal: React.FC<EnvironmentModalProps> = ({
|
||||
label="环境名称"
|
||||
rules={[
|
||||
{required: true, message: '请输入环境名称'},
|
||||
{max: 50, message: '环境名称长度不能超过50个字符'},
|
||||
{max: 50, message: '环境名称不能超过50个字符'},
|
||||
]}
|
||||
tooltip="环境的显示名称"
|
||||
>
|
||||
<Input placeholder="请输入环境名称"/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="envDesc"
|
||||
label="环境描述"
|
||||
rules={[{max: 200, message: '环境描述长度不能超过200个字符'}]}
|
||||
>
|
||||
<Input.TextArea rows={4} placeholder="请输入环境描述"/>
|
||||
<Input
|
||||
placeholder="请输入环境名称"
|
||||
maxLength={50}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="buildType"
|
||||
label="构建方式"
|
||||
rules={[{required: true, message: '请选择构建方式'}]}
|
||||
label="构建类型"
|
||||
rules={[{required: true, message: '请选择构建类型'}]}
|
||||
tooltip="环境的构建类型,创建后不可修改"
|
||||
>
|
||||
<Select placeholder="请选择构建方式">
|
||||
{Object.values(BuildTypeEnum).map((type) => {
|
||||
const typeInfo = getBuildTypeInfo(type);
|
||||
return (
|
||||
<Select.Option key={type} value={type}>
|
||||
<div style={{
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
gap: '4px',
|
||||
}}>
|
||||
<span style={{ color: typeInfo.color }}>{typeInfo.icon}</span>
|
||||
<span style={{ color: typeInfo.color }}>{typeInfo.label}</span>
|
||||
</div>
|
||||
</Select.Option>
|
||||
);
|
||||
})}
|
||||
<Select
|
||||
placeholder="请选择构建类型"
|
||||
disabled={isEdit}
|
||||
>
|
||||
<Select.Option value={BuildTypeEnum.LOCAL}>本地构建</Select.Option>
|
||||
<Select.Option value={BuildTypeEnum.REMOTE}>远程构建</Select.Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="deployType"
|
||||
label="部署方式"
|
||||
rules={[{required: true, message: '请选择部署方式'}]}
|
||||
label="部署类型"
|
||||
rules={[{required: true, message: '请选择部署类型'}]}
|
||||
tooltip="环境的部署类型,创建后不可修改"
|
||||
>
|
||||
<Select placeholder="请选择部署方式">
|
||||
{Object.values(DeployTypeEnum).map((type) => {
|
||||
const typeInfo = getDeployTypeInfo(type);
|
||||
return (
|
||||
<Select.Option key={type} value={type}>
|
||||
<div style={{
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
gap: '4px',
|
||||
}}>
|
||||
<span style={{ color: typeInfo.color }}>{typeInfo.icon}</span>
|
||||
<span style={{ color: typeInfo.color }}>{typeInfo.label}</span>
|
||||
</div>
|
||||
</Select.Option>
|
||||
);
|
||||
})}
|
||||
<Select
|
||||
placeholder="请选择部署类型"
|
||||
disabled={isEdit}
|
||||
>
|
||||
<Select.Option value={DeployTypeEnum.KUBERNETES}>Kubernetes</Select.Option>
|
||||
<Select.Option value={DeployTypeEnum.DOCKER}>Docker</Select.Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="envDesc"
|
||||
label="环境描述"
|
||||
rules={[{max: 200, message: '环境描述不能超过200个字符'}]}
|
||||
tooltip="环境的详细描述信息"
|
||||
>
|
||||
<Input.TextArea
|
||||
placeholder="请输入环境描述"
|
||||
maxLength={200}
|
||||
showCount
|
||||
rows={4}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="enabled"
|
||||
label="状态"
|
||||
valuePropName="checked"
|
||||
tooltip="是否启用该环境"
|
||||
>
|
||||
<Switch checkedChildren="启用" unCheckedChildren="禁用"/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="sort"
|
||||
label="排序"
|
||||
rules={[{required: true, message: '请输入排序号'}]}
|
||||
tooltip="数字越小越靠前"
|
||||
>
|
||||
<InputNumber min={0} max={999} style={{width: '100%'}}/>
|
||||
<InputNumber min={0} style={{width: '100%'}}/>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import React, {useState} from 'react';
|
||||
import {PageContainer} from '@ant-design/pro-layout';
|
||||
import {Button, message, Popconfirm, Tag} from 'antd';
|
||||
import {Button, Space, Popconfirm, Tag, App} from 'antd';
|
||||
import {PlusOutlined, EditOutlined, DeleteOutlined} from '@ant-design/icons';
|
||||
import {getEnvironmentPage, deleteEnvironment} from './service';
|
||||
import type {Environment, EnvironmentQueryParams} from './types';
|
||||
@ -14,14 +14,15 @@ const EnvironmentList: React.FC = () => {
|
||||
const [modalVisible, setModalVisible] = useState(false);
|
||||
const [currentEnvironment, setCurrentEnvironment] = useState<Environment>();
|
||||
const actionRef = React.useRef<ActionType>();
|
||||
const {message: messageApi} = App.useApp();
|
||||
|
||||
const handleDelete = async (id: number) => {
|
||||
try {
|
||||
await deleteEnvironment(id);
|
||||
message.success('删除成功');
|
||||
messageApi.success('删除成功');
|
||||
actionRef.current?.reload();
|
||||
} catch (error) {
|
||||
message.error('删除失败');
|
||||
messageApi.error('删除失败');
|
||||
}
|
||||
};
|
||||
|
||||
@ -35,24 +36,16 @@ const EnvironmentList: React.FC = () => {
|
||||
setModalVisible(true);
|
||||
};
|
||||
|
||||
const renderTag = (icon: React.ReactNode, label: string, color: string) => (
|
||||
<div style={{
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
padding: '0 7px',
|
||||
fontSize: '14px',
|
||||
lineHeight: '22px',
|
||||
whiteSpace: 'nowrap',
|
||||
background: color + '10',
|
||||
border: `1px solid ${color}`,
|
||||
borderRadius: '4px',
|
||||
cursor: 'default',
|
||||
gap: '4px',
|
||||
}}>
|
||||
{icon}
|
||||
<span style={{ color }}>{label}</span>
|
||||
</div>
|
||||
);
|
||||
const handleModalClose = () => {
|
||||
setModalVisible(false);
|
||||
setCurrentEnvironment(undefined);
|
||||
};
|
||||
|
||||
const handleSuccess = () => {
|
||||
setModalVisible(false);
|
||||
setCurrentEnvironment(undefined);
|
||||
actionRef.current?.reload();
|
||||
};
|
||||
|
||||
const columns: ProColumns<Environment>[] = [
|
||||
{
|
||||
@ -73,16 +66,21 @@ const EnvironmentList: React.FC = () => {
|
||||
title: '环境描述',
|
||||
dataIndex: 'envDesc',
|
||||
ellipsis: true,
|
||||
width: '30%',
|
||||
},
|
||||
{
|
||||
title: '构建方式',
|
||||
title: '构建类型',
|
||||
dataIndex: 'buildType',
|
||||
width: 180,
|
||||
width: 120,
|
||||
render: (buildType) => {
|
||||
if (!buildType) return '-';
|
||||
const typeInfo = getBuildTypeInfo(buildType as BuildTypeEnum);
|
||||
return renderTag(typeInfo.icon, typeInfo.label, typeInfo.color);
|
||||
return (
|
||||
<Tag color={typeInfo.color}>
|
||||
<Space>
|
||||
{typeInfo.icon}
|
||||
{typeInfo.label}
|
||||
</Space>
|
||||
</Tag>
|
||||
);
|
||||
},
|
||||
filters: [
|
||||
{text: 'Jenkins构建', value: BuildTypeEnum.JENKINS},
|
||||
@ -93,13 +91,19 @@ const EnvironmentList: React.FC = () => {
|
||||
filtered: false,
|
||||
},
|
||||
{
|
||||
title: '部署方式',
|
||||
title: '部署类型',
|
||||
dataIndex: 'deployType',
|
||||
width: 180,
|
||||
width: 120,
|
||||
render: (deployType) => {
|
||||
if (!deployType) return '-';
|
||||
const typeInfo = getDeployTypeInfo(deployType as DeployTypeEnum);
|
||||
return renderTag(typeInfo.icon, typeInfo.label, typeInfo.color);
|
||||
return (
|
||||
<Tag color={typeInfo.color}>
|
||||
<Space>
|
||||
{typeInfo.icon}
|
||||
{typeInfo.label}
|
||||
</Space>
|
||||
</Tag>
|
||||
);
|
||||
},
|
||||
filters: [
|
||||
{text: 'Kubernetes集群部署', value: DeployTypeEnum.K8S},
|
||||
@ -109,11 +113,19 @@ const EnvironmentList: React.FC = () => {
|
||||
filterMode: 'menu',
|
||||
filtered: false,
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'enabled',
|
||||
width: 100,
|
||||
valueEnum: {
|
||||
true: {text: '启用', status: 'Success'},
|
||||
false: {text: '禁用', status: 'Default'},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '排序',
|
||||
dataIndex: 'sort',
|
||||
width: 80,
|
||||
align: 'center',
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
@ -122,44 +134,73 @@ const EnvironmentList: React.FC = () => {
|
||||
key: 'action',
|
||||
valueType: 'option',
|
||||
fixed: 'right',
|
||||
align: 'center',
|
||||
render: (_, record) => (
|
||||
<div style={{ display: 'flex', gap: '8px', justifyContent: 'center' }}>
|
||||
render: (_, record) => [
|
||||
<Button
|
||||
key="edit"
|
||||
type="link"
|
||||
size="small"
|
||||
style={{ padding: 0 }}
|
||||
onClick={() => handleEdit(record)}
|
||||
>
|
||||
<EditOutlined /> 编辑
|
||||
</Button>
|
||||
<Space>
|
||||
<EditOutlined/>
|
||||
编辑
|
||||
</Space>
|
||||
</Button>,
|
||||
<Popconfirm
|
||||
key="delete"
|
||||
title="确定要删除该环境吗?"
|
||||
description="删除后将无法恢复,请谨慎操作"
|
||||
onConfirm={() => handleDelete(record.id)}
|
||||
>
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
danger
|
||||
style={{ padding: 0 }}
|
||||
>
|
||||
<DeleteOutlined /> 删除
|
||||
<Space>
|
||||
<DeleteOutlined/>
|
||||
删除
|
||||
</Space>
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
</div>
|
||||
),
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageContainer>
|
||||
<ProTable<Environment>
|
||||
columns={columns}
|
||||
actionRef={actionRef}
|
||||
scroll={{x: 'max-content'}}
|
||||
cardBordered
|
||||
rowKey="id"
|
||||
search={false}
|
||||
options={{
|
||||
setting: false,
|
||||
density: false,
|
||||
fullScreen: false,
|
||||
reload: false,
|
||||
}}
|
||||
toolbar={{
|
||||
actions: [
|
||||
<Button
|
||||
key="add"
|
||||
type="primary"
|
||||
onClick={handleAdd}
|
||||
icon={<PlusOutlined/>}
|
||||
>
|
||||
新建环境
|
||||
</Button>
|
||||
],
|
||||
}}
|
||||
form={{
|
||||
syncToUrl: true,
|
||||
}}
|
||||
pagination={{
|
||||
pageSize: 10,
|
||||
showQuickJumper: true,
|
||||
}}
|
||||
request={async (params) => {
|
||||
try {
|
||||
const queryParams: EnvironmentQueryParams = {
|
||||
pageSize: params.pageSize,
|
||||
pageNum: params.current,
|
||||
@ -174,54 +215,26 @@ const EnvironmentList: React.FC = () => {
|
||||
success: true,
|
||||
total: data.totalElements || 0,
|
||||
};
|
||||
} catch (error) {
|
||||
messageApi.error('获取环境列表失败');
|
||||
return {
|
||||
data: [],
|
||||
success: false,
|
||||
total: 0,
|
||||
};
|
||||
}
|
||||
}}
|
||||
rowKey="id"
|
||||
search={{
|
||||
labelWidth: 'auto',
|
||||
span: {
|
||||
xs: 24,
|
||||
sm: 12,
|
||||
md: 8,
|
||||
lg: 6,
|
||||
xl: 6,
|
||||
xxl: 6,
|
||||
},
|
||||
}}
|
||||
options={{
|
||||
setting: {
|
||||
listsHeight: 400,
|
||||
},
|
||||
}}
|
||||
form={{
|
||||
syncToUrl: true,
|
||||
}}
|
||||
pagination={{
|
||||
pageSize: 10,
|
||||
showQuickJumper: true,
|
||||
}}
|
||||
dateFormatter="string"
|
||||
toolBarRender={() => [
|
||||
<Button
|
||||
key="add"
|
||||
type="primary"
|
||||
onClick={handleAdd}
|
||||
icon={<PlusOutlined/>}
|
||||
>
|
||||
新建环境
|
||||
</Button>,
|
||||
]}
|
||||
/>
|
||||
|
||||
{modalVisible && (
|
||||
<EnvironmentModal
|
||||
visible={modalVisible}
|
||||
onCancel={() => setModalVisible(false)}
|
||||
onSuccess={() => {
|
||||
setModalVisible(false);
|
||||
actionRef.current?.reload();
|
||||
}}
|
||||
open={modalVisible}
|
||||
onCancel={handleModalClose}
|
||||
onSuccess={handleSuccess}
|
||||
initialValues={currentEnvironment}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -1,87 +1,73 @@
|
||||
import React, {useEffect} from 'react';
|
||||
import {Modal, Form, Input, InputNumber, Radio, message} from 'antd';
|
||||
import React, {useState} from 'react';
|
||||
import {Modal, Form, Input, Select, Switch, InputNumber, App} from 'antd';
|
||||
import type {ProjectGroup} from '../types';
|
||||
import {createProjectGroup, updateProjectGroup} from '../service';
|
||||
import {ProjectGroupTypeEnum} from '../types';
|
||||
import {createProjectGroup, updateProjectGroup} from '../service';
|
||||
|
||||
interface ProjectGroupModalProps {
|
||||
visible: boolean;
|
||||
open: boolean;
|
||||
onCancel: () => void;
|
||||
onSuccess: () => void;
|
||||
initialValues?: ProjectGroup;
|
||||
}
|
||||
|
||||
const ProjectGroupModal: React.FC<ProjectGroupModalProps> = ({
|
||||
visible,
|
||||
open,
|
||||
onCancel,
|
||||
onSuccess,
|
||||
initialValues,
|
||||
}) => {
|
||||
const [form] = Form.useForm();
|
||||
const isEdit = !!initialValues;
|
||||
|
||||
useEffect(() => {
|
||||
if (visible) {
|
||||
if (initialValues) {
|
||||
// 将布尔值转换为字符串
|
||||
form.setFieldsValue({
|
||||
...initialValues,
|
||||
enabled: initialValues.enabled?.toString()
|
||||
});
|
||||
} else {
|
||||
form.setFieldsValue({
|
||||
type: ProjectGroupTypeEnum.PROJECT,
|
||||
enabled: 'true',
|
||||
sort: 0
|
||||
});
|
||||
}
|
||||
}
|
||||
}, [visible, initialValues, form]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const {message: messageApi} = App.useApp();
|
||||
const isEdit = !!initialValues?.id;
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const values = await form.validateFields();
|
||||
// 将字符串转换回布尔值
|
||||
const submitData = {
|
||||
...values,
|
||||
enabled: values.enabled === 'true'
|
||||
};
|
||||
|
||||
if (isEdit) {
|
||||
await updateProjectGroup({...submitData, id: initialValues?.id});
|
||||
message.success('更新成功');
|
||||
await updateProjectGroup({
|
||||
...values,
|
||||
id: initialValues.id,
|
||||
});
|
||||
} else {
|
||||
await createProjectGroup(submitData);
|
||||
message.success('创建成功');
|
||||
await createProjectGroup(values);
|
||||
}
|
||||
messageApi.success(`${isEdit ? '更新' : '创建'}成功`);
|
||||
form.resetFields();
|
||||
onSuccess();
|
||||
onCancel();
|
||||
form.resetFields();
|
||||
} catch (error) {
|
||||
message.error(isEdit ? '更新失败' : '创建失败');
|
||||
if (error instanceof Error) {
|
||||
messageApi.error(`${isEdit ? '更新' : '创建'}失败: ${error.message}`);
|
||||
} else {
|
||||
messageApi.error(`${isEdit ? '更新' : '创建'}失败`);
|
||||
}
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
form.resetFields();
|
||||
onCancel();
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={isEdit ? '编辑项目组' : '新建项目组'}
|
||||
open={visible}
|
||||
title={`${isEdit ? '编辑' : '新建'}项目组`}
|
||||
open={open}
|
||||
onCancel={() => {
|
||||
form.resetFields();
|
||||
onCancel();
|
||||
}}
|
||||
onOk={handleSubmit}
|
||||
onCancel={handleCancel}
|
||||
confirmLoading={loading}
|
||||
maskClosable={false}
|
||||
destroyOnClose
|
||||
>
|
||||
<Form
|
||||
form={form}
|
||||
layout="vertical"
|
||||
initialValues={{
|
||||
type: ProjectGroupTypeEnum.PROJECT,
|
||||
enabled: 'true',
|
||||
sort: 0
|
||||
enabled: true,
|
||||
sort: 0,
|
||||
...initialValues,
|
||||
}}
|
||||
>
|
||||
<Form.Item
|
||||
@ -89,55 +75,76 @@ const ProjectGroupModal: React.FC<ProjectGroupModalProps> = ({
|
||||
label="项目组编码"
|
||||
rules={[
|
||||
{required: true, message: '请输入项目组编码'},
|
||||
{pattern: /^[A-Za-z0-9_-]+$/, message: '项目组编码只能包含字母、数字、下划线和连字符'}
|
||||
{max: 50, message: '项目组编码不能超过50个字符'},
|
||||
]}
|
||||
tooltip="项目组的唯一标识,创建后不可修改"
|
||||
>
|
||||
<Input placeholder="请输入项目组编码" disabled={isEdit}/>
|
||||
<Input
|
||||
placeholder="请输入项目组编码"
|
||||
disabled={isEdit}
|
||||
maxLength={50}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="projectGroupName"
|
||||
label="项目组名称"
|
||||
rules={[{required: true, message: '请输入项目组名称'}]}
|
||||
rules={[
|
||||
{required: true, message: '请输入项目组名称'},
|
||||
{max: 50, message: '项目组名称不能超过50个字符'},
|
||||
]}
|
||||
tooltip="项目组的显示名称"
|
||||
>
|
||||
<Input placeholder="请输入项目组名称"/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="projectGroupDesc"
|
||||
label="项目组描述"
|
||||
>
|
||||
<Input.TextArea rows={4} placeholder="请输入项目组描述"/>
|
||||
<Input
|
||||
placeholder="请输入项目组名称"
|
||||
maxLength={50}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="type"
|
||||
label="项目组类型"
|
||||
rules={[{required: true, message: '请选择项目组类型'}]}
|
||||
tooltip="项目组的类型,创建后不可修改"
|
||||
>
|
||||
<Radio.Group>
|
||||
<Radio value={ProjectGroupTypeEnum.PRODUCT}>产品型</Radio>
|
||||
<Radio value={ProjectGroupTypeEnum.PROJECT}>项目型</Radio>
|
||||
</Radio.Group>
|
||||
<Select
|
||||
placeholder="请选择项目组类型"
|
||||
disabled={isEdit}
|
||||
>
|
||||
<Select.Option value={ProjectGroupTypeEnum.PRODUCT}>产品型</Select.Option>
|
||||
<Select.Option value={ProjectGroupTypeEnum.PROJECT}>项目型</Select.Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="projectGroupDesc"
|
||||
label="项目组描述"
|
||||
rules={[{max: 200, message: '项目组描述不能超过200个字符'}]}
|
||||
tooltip="项目组的详细描述信息"
|
||||
>
|
||||
<Input.TextArea
|
||||
placeholder="请输入项目组描述"
|
||||
maxLength={200}
|
||||
showCount
|
||||
rows={4}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="enabled"
|
||||
label="项目组状态"
|
||||
rules={[{required: true, message: '请选择项目组状态'}]}
|
||||
label="状态"
|
||||
valuePropName="checked"
|
||||
tooltip="是否启用该项目组"
|
||||
>
|
||||
<Radio.Group>
|
||||
<Radio value="true">启用</Radio>
|
||||
<Radio value="false">禁用</Radio>
|
||||
</Radio.Group>
|
||||
<Switch checkedChildren="启用" unCheckedChildren="禁用"/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="sort"
|
||||
label="排序"
|
||||
rules={[{required: true, message: '请输入排序值'}]}
|
||||
tooltip="数字越小越靠前"
|
||||
>
|
||||
<InputNumber min={0} placeholder="请输入排序值" style={{width: '100%'}}/>
|
||||
<InputNumber min={0} style={{width: '100%'}}/>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import React, {useState} from 'react';
|
||||
import {PageContainer} from '@ant-design/pro-layout';
|
||||
import {Button, message, Popconfirm, Space, Tag, Tooltip} from 'antd';
|
||||
import {Button, Space, Popconfirm, Tag, App} from 'antd';
|
||||
import {
|
||||
PlusOutlined,
|
||||
EditOutlined,
|
||||
@ -20,14 +20,15 @@ const ProjectGroupList: React.FC = () => {
|
||||
const [modalVisible, setModalVisible] = useState(false);
|
||||
const [currentProject, setCurrentProject] = useState<ProjectGroup>();
|
||||
const actionRef = React.useRef<ActionType>();
|
||||
const {message: messageApi} = App.useApp();
|
||||
|
||||
const handleDelete = async (id: number) => {
|
||||
try {
|
||||
await deleteProjectGroup(id);
|
||||
message.success('删除成功');
|
||||
messageApi.success('删除成功');
|
||||
actionRef.current?.reload();
|
||||
} catch (error) {
|
||||
message.error('删除失败');
|
||||
messageApi.error('删除失败');
|
||||
}
|
||||
};
|
||||
|
||||
@ -41,6 +42,17 @@ const ProjectGroupList: React.FC = () => {
|
||||
setModalVisible(true);
|
||||
};
|
||||
|
||||
const handleModalClose = () => {
|
||||
setModalVisible(false);
|
||||
setCurrentProject(undefined);
|
||||
};
|
||||
|
||||
const handleSuccess = () => {
|
||||
setModalVisible(false);
|
||||
setCurrentProject(undefined);
|
||||
actionRef.current?.reload();
|
||||
};
|
||||
|
||||
const columns: ProColumns<ProjectGroup>[] = [
|
||||
{
|
||||
title: '项目组编码',
|
||||
@ -97,12 +109,10 @@ const ProjectGroupList: React.FC = () => {
|
||||
dataIndex: 'environments',
|
||||
width: 100,
|
||||
render: (_, record) => (
|
||||
<Tooltip title="环境数量">
|
||||
<Space>
|
||||
<EnvironmentOutlined/>
|
||||
{record?.totalEnvironments || 0}
|
||||
</Space>
|
||||
</Tooltip>
|
||||
),
|
||||
},
|
||||
{
|
||||
@ -110,12 +120,10 @@ const ProjectGroupList: React.FC = () => {
|
||||
dataIndex: 'applications',
|
||||
width: 100,
|
||||
render: (_, record) => (
|
||||
<Tooltip title="项目数量">
|
||||
<Space>
|
||||
<TeamOutlined/>
|
||||
{record?.totalApplications || 0}
|
||||
</Space>
|
||||
</Tooltip>
|
||||
),
|
||||
},
|
||||
{
|
||||
@ -126,26 +134,20 @@ const ProjectGroupList: React.FC = () => {
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
width: 280,
|
||||
width: 180,
|
||||
key: 'action',
|
||||
valueType: 'option',
|
||||
fixed: 'right',
|
||||
align: 'center',
|
||||
render: (_, record) => [
|
||||
<Button
|
||||
key="edit"
|
||||
type="link"
|
||||
icon={<EditOutlined/>}
|
||||
onClick={() => handleEdit(record)}
|
||||
>
|
||||
<Space>
|
||||
<EditOutlined/>
|
||||
编辑
|
||||
</Button>,
|
||||
<Button
|
||||
key="bind"
|
||||
type="link"
|
||||
icon={<EnvironmentOutlined/>}
|
||||
>
|
||||
环境绑定
|
||||
</Space>
|
||||
</Button>,
|
||||
<Popconfirm
|
||||
key="delete"
|
||||
@ -156,9 +158,11 @@ const ProjectGroupList: React.FC = () => {
|
||||
<Button
|
||||
type="link"
|
||||
danger
|
||||
icon={<DeleteOutlined/>}
|
||||
>
|
||||
<Space>
|
||||
<DeleteOutlined/>
|
||||
删除
|
||||
</Space>
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
],
|
||||
@ -166,13 +170,42 @@ const ProjectGroupList: React.FC = () => {
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageContainer>
|
||||
<ProTable<ProjectGroup>
|
||||
columns={columns}
|
||||
actionRef={actionRef}
|
||||
scroll={{x: 'max-content'}}
|
||||
cardBordered
|
||||
rowKey="id"
|
||||
search={false}
|
||||
options={{
|
||||
setting: false,
|
||||
density: false,
|
||||
fullScreen: false,
|
||||
reload: false,
|
||||
}}
|
||||
toolbar={{
|
||||
actions: [
|
||||
<Button
|
||||
key="add"
|
||||
type="primary"
|
||||
onClick={handleAdd}
|
||||
icon={<PlusOutlined/>}
|
||||
>
|
||||
新建项目组
|
||||
</Button>
|
||||
],
|
||||
}}
|
||||
form={{
|
||||
syncToUrl: true,
|
||||
ignoreRules: false,
|
||||
}}
|
||||
pagination={{
|
||||
pageSize: 10,
|
||||
showQuickJumper: true,
|
||||
}}
|
||||
request={async (params) => {
|
||||
try {
|
||||
const queryParams: ProjectGroupQueryParams = {
|
||||
pageSize: params.pageSize,
|
||||
pageNum: params.current,
|
||||
@ -186,54 +219,26 @@ const ProjectGroupList: React.FC = () => {
|
||||
success: true,
|
||||
total: data.totalElements || 0,
|
||||
};
|
||||
} catch (error) {
|
||||
messageApi.error('获取项目组列表失败');
|
||||
return {
|
||||
data: [],
|
||||
success: false,
|
||||
total: 0,
|
||||
};
|
||||
}
|
||||
}}
|
||||
rowKey="id"
|
||||
search={{
|
||||
labelWidth: 'auto',
|
||||
span: {
|
||||
xs: 24,
|
||||
sm: 12,
|
||||
md: 8,
|
||||
lg: 6,
|
||||
xl: 6,
|
||||
xxl: 6,
|
||||
},
|
||||
}}
|
||||
options={{
|
||||
setting: {
|
||||
listsHeight: 400,
|
||||
},
|
||||
}}
|
||||
form={{
|
||||
syncToUrl: true,
|
||||
}}
|
||||
pagination={{
|
||||
pageSize: 10,
|
||||
showQuickJumper: true,
|
||||
}}
|
||||
dateFormatter="string"
|
||||
toolBarRender={() => [
|
||||
<Button
|
||||
key="add"
|
||||
type="primary"
|
||||
onClick={handleAdd}
|
||||
icon={<PlusOutlined/>}
|
||||
>
|
||||
新建项目组
|
||||
</Button>,
|
||||
]}
|
||||
/>
|
||||
|
||||
{modalVisible && (
|
||||
<ProjectGroupModal
|
||||
visible={modalVisible}
|
||||
onCancel={() => setModalVisible(false)}
|
||||
onSuccess={() => {
|
||||
setModalVisible(false);
|
||||
actionRef.current?.reload();
|
||||
}}
|
||||
open={modalVisible}
|
||||
onCancel={handleModalClose}
|
||||
onSuccess={handleSuccess}
|
||||
initialValues={currentProject}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user