1
This commit is contained in:
parent
201d9ca054
commit
6dfd7b914f
@ -11,20 +11,6 @@ interface EnvironmentModalProps {
|
||||
initialValues?: Environment;
|
||||
}
|
||||
|
||||
const {Option} = Select;
|
||||
|
||||
const buildTypeOptions = [
|
||||
{label: 'Jenkins构建', value: BuildTypeEnum.JENKINS},
|
||||
{label: 'GitLab Runner构建', value: BuildTypeEnum.GITLAB_RUNNER},
|
||||
{label: 'GitHub Action构建', value: BuildTypeEnum.GITHUB_ACTION},
|
||||
];
|
||||
|
||||
const deployTypeOptions = [
|
||||
{label: 'Kubernetes集群部署', value: DeployTypeEnum.K8S},
|
||||
{label: 'Docker容器部署', value: DeployTypeEnum.DOCKER},
|
||||
{label: '虚拟机部署', value: DeployTypeEnum.VM},
|
||||
];
|
||||
|
||||
const EnvironmentModal: React.FC<EnvironmentModalProps> = ({
|
||||
visible,
|
||||
onCancel,
|
||||
@ -105,18 +91,6 @@ const EnvironmentModal: React.FC<EnvironmentModalProps> = ({
|
||||
<Input placeholder="请输入环境编码"/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="buildType"
|
||||
label="构建方式"
|
||||
rules={[{required: true, message: '请选择构建方式'}]}
|
||||
>
|
||||
<Select
|
||||
placeholder="请选择构建方式"
|
||||
options={buildTypeOptions}
|
||||
style={{width: '100%'}}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="envDesc"
|
||||
label="环境描述"
|
||||
|
||||
@ -1,43 +0,0 @@
|
||||
.environment-card {
|
||||
&:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.ant-card-actions {
|
||||
background: #fafafa;
|
||||
border-top: 1px solid #f0f0f0;
|
||||
|
||||
li {
|
||||
margin: 12px 0;
|
||||
|
||||
&:not(:last-child) {
|
||||
border-right: 1px solid #f0f0f0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-card-body {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.ant-tag {
|
||||
border: none;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-typography {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.ant-btn {
|
||||
&:hover {
|
||||
transform: scale(1.05);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,64 +1,24 @@
|
||||
import React, {useState, useEffect} from 'react';
|
||||
import React, {useState} from 'react';
|
||||
import {PageContainer} from '@ant-design/pro-layout';
|
||||
import {Card, Row, Col, Typography, Button, message, Empty, Tooltip, Tag} from 'antd';
|
||||
import {PlusOutlined, RocketOutlined, CloudServerOutlined, SettingOutlined, DeleteOutlined, EditOutlined} from '@ant-design/icons';
|
||||
import {getEnvironmentList, deleteEnvironment} from './service';
|
||||
import {Button, message, Popconfirm, Space, Tag, Form, Input, Row, Col} from 'antd';
|
||||
import {PlusOutlined, DeleteOutlined, EditOutlined, SearchOutlined, ReloadOutlined} from '@ant-design/icons';
|
||||
import {getEnvironmentList, deleteEnvironment, getEnvironmentPage} from './service';
|
||||
import type {Environment} from './types';
|
||||
import {BuildTypeEnum, DeployTypeEnum} from './types';
|
||||
import EnvironmentModal from './components/EnvironmentModal';
|
||||
|
||||
const {Title, Text} = Typography;
|
||||
|
||||
// 构建方式和部署方式的显示映射
|
||||
const buildTypeMap = {
|
||||
[BuildTypeEnum.JENKINS]: {
|
||||
label: 'Jenkins构建',
|
||||
color: '#1890ff',
|
||||
icon: <RocketOutlined/>,
|
||||
description: '使用Jenkins进行自动化构建和部署'
|
||||
},
|
||||
[BuildTypeEnum.GITLAB_RUNNER]: {
|
||||
label: 'GitLab Runner构建',
|
||||
color: '#722ed1',
|
||||
icon: <CloudServerOutlined/>,
|
||||
description: '使用GitLab Runner进行CI/CD流程'
|
||||
},
|
||||
[BuildTypeEnum.GITHUB_ACTION]: {
|
||||
label: 'GitHub Action构建',
|
||||
color: '#52c41a',
|
||||
icon: <SettingOutlined/>,
|
||||
description: '使用GitHub Action进行自动化工作流'
|
||||
},
|
||||
};
|
||||
|
||||
import { ProTable } from '@ant-design/pro-components';
|
||||
import type { ProColumns } from '@ant-design/pro-components';
|
||||
|
||||
const EnvironmentList: React.FC = () => {
|
||||
const [environments, setEnvironments] = useState<Environment[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [modalVisible, setModalVisible] = useState(false);
|
||||
const [currentEnvironment, setCurrentEnvironment] = useState<Environment>();
|
||||
|
||||
const fetchEnvironments = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const data = await getEnvironmentList();
|
||||
setEnvironments(data);
|
||||
} catch (error) {
|
||||
message.error('获取环境列表失败');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchEnvironments();
|
||||
}, []);
|
||||
const actionRef = React.useRef();
|
||||
const [form] = Form.useForm();
|
||||
|
||||
const handleDelete = async (id: number) => {
|
||||
try {
|
||||
await deleteEnvironment(id);
|
||||
message.success('删除成功');
|
||||
fetchEnvironments();
|
||||
actionRef.current?.reload();
|
||||
} catch (error) {
|
||||
message.error('删除失败');
|
||||
}
|
||||
@ -74,157 +34,141 @@ const EnvironmentList: React.FC = () => {
|
||||
setModalVisible(true);
|
||||
};
|
||||
|
||||
const EnvironmentCard = ({environment}: { environment: Environment }) => {
|
||||
const buildType = buildTypeMap[environment.buildType];
|
||||
return (
|
||||
<Card
|
||||
hoverable
|
||||
className="environment-card"
|
||||
style={{
|
||||
borderRadius: '12px',
|
||||
overflow: 'hidden',
|
||||
height: '100%',
|
||||
transition: 'all 0.3s ease',
|
||||
}}
|
||||
actions={[
|
||||
<Tooltip title="编辑环境">
|
||||
const columns: ProColumns<Environment>[] = [
|
||||
{
|
||||
title: '环境编码',
|
||||
dataIndex: 'envCode',
|
||||
width: 120,
|
||||
copyable: true,
|
||||
ellipsis: true,
|
||||
fixed: 'left',
|
||||
},
|
||||
{
|
||||
title: '环境名称',
|
||||
dataIndex: 'envName',
|
||||
width: 150,
|
||||
ellipsis: true,
|
||||
},
|
||||
{
|
||||
title: '环境描述',
|
||||
dataIndex: 'envDesc',
|
||||
ellipsis: true,
|
||||
},
|
||||
{
|
||||
title: '构建类型',
|
||||
dataIndex: 'buildType',
|
||||
width: 100,
|
||||
render: (buildType) => (
|
||||
<Tag color={buildType === 'JENKINS' ? 'blue' : 'green'}>
|
||||
{buildType}
|
||||
</Tag>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '排序',
|
||||
dataIndex: 'sort',
|
||||
width: 80,
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
width: 180,
|
||||
key: 'action',
|
||||
valueType: 'option',
|
||||
fixed: 'right',
|
||||
render: (_, record) => (
|
||||
<Space>
|
||||
<Button
|
||||
type="link"
|
||||
icon={<EditOutlined/>}
|
||||
onClick={() => handleEdit(record)}
|
||||
>
|
||||
编辑
|
||||
</Button>
|
||||
<Popconfirm
|
||||
title="确定要删除该环境吗?"
|
||||
description="删除后将无法恢复,请谨慎操作"
|
||||
onConfirm={() => handleDelete(record.id)}
|
||||
>
|
||||
<Button
|
||||
type="text"
|
||||
icon={<EditOutlined/>}
|
||||
onClick={() => handleEdit(environment)}
|
||||
>
|
||||
编辑
|
||||
</Button>
|
||||
</Tooltip>,
|
||||
<Tooltip title="删除环境">
|
||||
<Button
|
||||
type="text"
|
||||
type="link"
|
||||
danger
|
||||
icon={<DeleteOutlined/>}
|
||||
onClick={() => handleDelete(environment.id)}
|
||||
>
|
||||
删除
|
||||
</Button>
|
||||
</Tooltip>
|
||||
]}
|
||||
>
|
||||
<div style={{position: 'relative', padding: '16px'}}>
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
marginBottom: '16px'
|
||||
}}>
|
||||
<Title level={4} style={{margin: 0}}>
|
||||
{environment.envName}
|
||||
</Title>
|
||||
<Tag color={buildType.color} style={{borderRadius: '12px', padding: '0 12px'}}>
|
||||
{buildType.icon} {buildType.label}
|
||||
</Tag>
|
||||
</div>
|
||||
|
||||
<Text
|
||||
type="secondary"
|
||||
style={{
|
||||
display: 'block',
|
||||
marginBottom: '24px',
|
||||
height: '40px',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
WebkitLineClamp: 2,
|
||||
WebkitBoxOrient: 'vertical',
|
||||
display: '-webkit-box',
|
||||
}}
|
||||
>
|
||||
{environment.envDesc || '暂无描述'}
|
||||
</Text>
|
||||
|
||||
<div style={{
|
||||
background: '#f5f5f5',
|
||||
borderRadius: '8px',
|
||||
padding: '16px',
|
||||
marginBottom: '16px'
|
||||
}}>
|
||||
<Text type="secondary">环境编码</Text>
|
||||
<div style={{
|
||||
fontFamily: 'monaco, monospace',
|
||||
fontSize: '14px',
|
||||
color: '#262626',
|
||||
marginTop: '8px',
|
||||
padding: '8px',
|
||||
background: '#fff',
|
||||
borderRadius: '4px',
|
||||
border: '1px solid #f0f0f0'
|
||||
}}>
|
||||
{environment.envCode}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
</Popconfirm>
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<PageContainer
|
||||
header={{
|
||||
title: '环境管理',
|
||||
extra: [
|
||||
<Button
|
||||
key="add"
|
||||
type="primary"
|
||||
icon={<PlusOutlined/>}
|
||||
onClick={handleAdd}
|
||||
style={{
|
||||
borderRadius: '8px',
|
||||
padding: '0 24px',
|
||||
height: '40px',
|
||||
boxShadow: '0 2px 0 rgba(0,0,0,0.045)',
|
||||
transition: 'all 0.3s ease',
|
||||
}}
|
||||
>
|
||||
新建环境
|
||||
</Button>
|
||||
],
|
||||
}}
|
||||
>
|
||||
<div style={{padding: '24px'}}>
|
||||
<Row gutter={[24, 24]}>
|
||||
{environments.length > 0 ? (
|
||||
environments.map((environment) => (
|
||||
<Col key={environment.id} xs={24} sm={12} md={8} lg={6}>
|
||||
<EnvironmentCard environment={environment}/>
|
||||
</Col>
|
||||
))
|
||||
) : (
|
||||
<Col span={24}>
|
||||
<Empty
|
||||
image={Empty.PRESENTED_IMAGE_SIMPLE}
|
||||
description="暂无环境数据"
|
||||
style={{
|
||||
background: '#fff',
|
||||
padding: '40px',
|
||||
borderRadius: '8px',
|
||||
}}
|
||||
>
|
||||
<Button type="primary" onClick={handleAdd}>
|
||||
立即创建
|
||||
</Button>
|
||||
</Empty>
|
||||
</Col>
|
||||
)}
|
||||
</Row>
|
||||
</div>
|
||||
|
||||
<EnvironmentModal
|
||||
visible={modalVisible}
|
||||
onCancel={() => setModalVisible(false)}
|
||||
onSuccess={() => {
|
||||
setModalVisible(false);
|
||||
fetchEnvironments();
|
||||
}}
|
||||
initialValues={currentEnvironment}
|
||||
/>
|
||||
</PageContainer>
|
||||
<>
|
||||
<ProTable<Environment>
|
||||
columns={columns}
|
||||
actionRef={actionRef}
|
||||
cardBordered
|
||||
request={async (params) => {
|
||||
const { current, pageSize } = params;
|
||||
const data = await getEnvironmentPage({
|
||||
current,
|
||||
pageSize,
|
||||
});
|
||||
return {
|
||||
data: data.content || [],
|
||||
success: true,
|
||||
total: data.totalElements || 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>,
|
||||
]}
|
||||
/>
|
||||
|
||||
<EnvironmentModal
|
||||
visible={modalVisible}
|
||||
onCancel={() => setModalVisible(false)}
|
||||
onSuccess={() => {
|
||||
setModalVisible(false);
|
||||
actionRef.current?.reload();
|
||||
}}
|
||||
initialValues={currentEnvironment}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -1,16 +1,5 @@
|
||||
import type { BaseQuery } from '@/types/base';
|
||||
|
||||
export enum BuildTypeEnum {
|
||||
JENKINS = 'JENKINS',
|
||||
GITLAB_RUNNER = 'GITLAB_RUNNER',
|
||||
GITHUB_ACTION = 'GITHUB_ACTION'
|
||||
}
|
||||
|
||||
export enum DeployTypeEnum {
|
||||
K8S = 'K8S',
|
||||
DOCKER = 'DOCKER',
|
||||
VM = 'VM'
|
||||
}
|
||||
|
||||
export interface Environment {
|
||||
id: number;
|
||||
@ -18,8 +7,6 @@ export interface Environment {
|
||||
envName: string;
|
||||
envDesc?: string;
|
||||
sort: number;
|
||||
buildType: BuildTypeEnum;
|
||||
deployType: DeployTypeEnum;
|
||||
}
|
||||
|
||||
export interface CreateEnvironmentRequest {
|
||||
@ -28,8 +15,6 @@ export interface CreateEnvironmentRequest {
|
||||
envName: string;
|
||||
envDesc?: string;
|
||||
sort: number;
|
||||
buildType: BuildTypeEnum;
|
||||
deployType: DeployTypeEnum;
|
||||
}
|
||||
|
||||
export interface UpdateEnvironmentRequest {
|
||||
@ -38,8 +23,6 @@ export interface UpdateEnvironmentRequest {
|
||||
envName: string;
|
||||
envDesc?: string;
|
||||
sort: number;
|
||||
buildType: BuildTypeEnum;
|
||||
deployType: DeployTypeEnum;
|
||||
}
|
||||
|
||||
export interface EnvironmentQuery extends BaseQuery {
|
||||
|
||||
@ -13,6 +13,7 @@ import {
|
||||
import {getProjectGroupList, deleteProjectGroup} from './service';
|
||||
import type {ProjectGroup} from './types';
|
||||
import ProjectGroupModal from './components/ProjectGroupModal';
|
||||
import { ProjectGroupTypeEnum } from './types';
|
||||
|
||||
const {Title, Text} = Typography;
|
||||
const {Search} = Input;
|
||||
@ -61,34 +62,34 @@ const ProjectList: React.FC = () => {
|
||||
setModalVisible(true);
|
||||
};
|
||||
|
||||
const getProjectTypeInfo = (type: string) => {
|
||||
switch (type) {
|
||||
case 'PRODUCT':
|
||||
return {
|
||||
type: 'PRODUCT',
|
||||
label: '产品项目组',
|
||||
color: '#1890ff',
|
||||
icon: <RocketOutlined/>,
|
||||
description: '产品相关的项目组'
|
||||
};
|
||||
case 'PLATFORM':
|
||||
return {
|
||||
type: 'PLATFORM',
|
||||
label: '平台项目组',
|
||||
color: '#52c41a',
|
||||
icon: <EnvironmentOutlined/>,
|
||||
description: '平台相关的项目组'
|
||||
};
|
||||
default:
|
||||
return {
|
||||
type: 'BUSINESS',
|
||||
label: '业务项目组',
|
||||
color: '#722ed1',
|
||||
icon: <TeamOutlined/>,
|
||||
description: '业务相关的项目组'
|
||||
};
|
||||
}
|
||||
};
|
||||
const getProjectTypeInfo = (type: ProjectGroupTypeEnum) => {
|
||||
switch (type) {
|
||||
case ProjectGroupTypeEnum.PRODUCT:
|
||||
return {
|
||||
type: ProjectGroupTypeEnum.PRODUCT,
|
||||
label: '产品型',
|
||||
color: '#1890ff',
|
||||
icon: <RocketOutlined/>,
|
||||
description: '产品型相关的项目组'
|
||||
};
|
||||
case ProjectGroupTypeEnum.PROJECT:
|
||||
return {
|
||||
type: ProjectGroupTypeEnum.PROJECT,
|
||||
label: '项目型',
|
||||
color: '#52c41a',
|
||||
icon: <EnvironmentOutlined/>,
|
||||
description: '项目型相关的项目组'
|
||||
};
|
||||
default:
|
||||
return {
|
||||
type: ProjectGroupTypeEnum.PROJECT,
|
||||
label: '项目型',
|
||||
color: '#722ed1',
|
||||
icon: <TeamOutlined/>,
|
||||
description: '项目型相关的项目组'
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const filteredProjects = projects.filter(project => {
|
||||
const matchesSearch = searchText ? (
|
||||
@ -147,7 +148,7 @@ const ProjectList: React.FC = () => {
|
||||
alignItems: 'center',
|
||||
gap: '8px'
|
||||
}}>
|
||||
{projectGroup.projectGroupName}
|
||||
{projectGroup.projectGroupName}({projectGroup.projectGroupCode})
|
||||
<Tag
|
||||
color={typeInfo.color}
|
||||
style={{
|
||||
@ -195,21 +196,6 @@ const ProjectList: React.FC = () => {
|
||||
borderRadius: '12px',
|
||||
boxShadow: '0 2px 8px rgba(0,0,0,0.04)',
|
||||
}}>
|
||||
<div>
|
||||
<Text type="secondary" style={{fontSize: '13px', display: 'block', marginBottom: '8px'}}>项目组编码</Text>
|
||||
<div style={{
|
||||
color: '#262626',
|
||||
fontFamily: 'monaco, monospace',
|
||||
fontSize: '14px',
|
||||
padding: '8px 12px',
|
||||
background: '#f5f5f5',
|
||||
borderRadius: '6px',
|
||||
letterSpacing: '0.5px',
|
||||
}}>
|
||||
{projectGroup.projectGroupCode}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Row gutter={16}>
|
||||
<Col span={8}>
|
||||
<Statistic
|
||||
@ -319,11 +305,10 @@ const ProjectList: React.FC = () => {
|
||||
value={projectType}
|
||||
onChange={setProjectType}
|
||||
options={[
|
||||
{ label: '全部类型', value: 'ALL' },
|
||||
{ label: '产品项目组', value: 'PRODUCT' },
|
||||
{ label: '平台项目组', value: 'PLATFORM' },
|
||||
{ label: '业务项目组', value: 'BUSINESS' }
|
||||
]}
|
||||
{ label: '全部类型', value: 'ALL' },
|
||||
{ label: '产品项目组', value: ProjectGroupTypeEnum.PRODUCT },
|
||||
{ label: '项目项目组', value: ProjectGroupTypeEnum.PROJECT }
|
||||
]}
|
||||
size="large"
|
||||
/>
|
||||
</Col>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user