1
This commit is contained in:
parent
278ada41eb
commit
90e9ab004c
43
frontend/src/pages/Deploy/Environment/List/index.less
Normal file
43
frontend/src/pages/Deploy/Environment/List/index.less
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
.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,7 +1,7 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { PageContainer } from '@ant-design/pro-layout';
|
import { PageContainer } from '@ant-design/pro-layout';
|
||||||
import { Card, Row, Col, Typography, Button, message, Popconfirm, Space, Tag, Tooltip } from 'antd';
|
import { Card, Row, Col, Typography, Button, message, Empty, Tooltip, Tag } from 'antd';
|
||||||
import { PlusOutlined, EditOutlined, DeleteOutlined, RocketOutlined } from '@ant-design/icons';
|
import { PlusOutlined, RocketOutlined, CloudServerOutlined, SettingOutlined, DeleteOutlined, EditOutlined } from '@ant-design/icons';
|
||||||
import { getEnvironmentList, deleteEnvironment } from './service';
|
import { getEnvironmentList, deleteEnvironment } from './service';
|
||||||
import type { Environment } from './types';
|
import type { Environment } from './types';
|
||||||
import { BuildTypeEnum, DeployTypeEnum } from './types';
|
import { BuildTypeEnum, DeployTypeEnum } from './types';
|
||||||
@ -14,16 +14,19 @@ const buildTypeMap = {
|
|||||||
[BuildTypeEnum.JENKINS]: {
|
[BuildTypeEnum.JENKINS]: {
|
||||||
label: 'Jenkins构建',
|
label: 'Jenkins构建',
|
||||||
color: '#1890ff',
|
color: '#1890ff',
|
||||||
|
icon: <RocketOutlined />,
|
||||||
description: '使用Jenkins进行自动化构建和部署'
|
description: '使用Jenkins进行自动化构建和部署'
|
||||||
},
|
},
|
||||||
[BuildTypeEnum.GITLAB_RUNNER]: {
|
[BuildTypeEnum.GITLAB_RUNNER]: {
|
||||||
label: 'GitLab Runner构建',
|
label: 'GitLab Runner构建',
|
||||||
color: '#722ed1',
|
color: '#722ed1',
|
||||||
|
icon: <CloudServerOutlined />,
|
||||||
description: '使用GitLab Runner进行CI/CD流程'
|
description: '使用GitLab Runner进行CI/CD流程'
|
||||||
},
|
},
|
||||||
[BuildTypeEnum.GITHUB_ACTION]: {
|
[BuildTypeEnum.GITHUB_ACTION]: {
|
||||||
label: 'GitHub Action构建',
|
label: 'GitHub Action构建',
|
||||||
color: '#52c41a',
|
color: '#52c41a',
|
||||||
|
icon: <SettingOutlined />,
|
||||||
description: '使用GitHub Action进行自动化工作流'
|
description: '使用GitHub Action进行自动化工作流'
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -32,16 +35,19 @@ const deployTypeMap = {
|
|||||||
[DeployTypeEnum.K8S]: {
|
[DeployTypeEnum.K8S]: {
|
||||||
label: 'Kubernetes集群部署',
|
label: 'Kubernetes集群部署',
|
||||||
color: '#1890ff',
|
color: '#1890ff',
|
||||||
|
icon: <CloudServerOutlined />,
|
||||||
description: '部署到Kubernetes容器编排平台'
|
description: '部署到Kubernetes容器编排平台'
|
||||||
},
|
},
|
||||||
[DeployTypeEnum.DOCKER]: {
|
[DeployTypeEnum.DOCKER]: {
|
||||||
label: 'Docker容器部署',
|
label: 'Docker容器部署',
|
||||||
color: '#13c2c2',
|
color: '#13c2c2',
|
||||||
|
icon: <RocketOutlined />,
|
||||||
description: '部署为Docker独立容器'
|
description: '部署为Docker独立容器'
|
||||||
},
|
},
|
||||||
[DeployTypeEnum.VM]: {
|
[DeployTypeEnum.VM]: {
|
||||||
label: '虚拟机部署',
|
label: '虚拟机部署',
|
||||||
color: '#faad14',
|
color: '#faad14',
|
||||||
|
icon: <SettingOutlined />,
|
||||||
description: '部署到传统虚拟机环境'
|
description: '部署到传统虚拟机环境'
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -97,65 +103,46 @@ const EnvironmentList: React.FC = () => {
|
|||||||
hoverable
|
hoverable
|
||||||
className="environment-card"
|
className="environment-card"
|
||||||
style={{
|
style={{
|
||||||
height: '100%',
|
borderRadius: '12px',
|
||||||
borderRadius: '16px',
|
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
border: 'none',
|
|
||||||
transition: 'all 0.3s',
|
|
||||||
boxShadow: '0 1px 2px 0 rgba(0,0,0,0.03), 0 1px 6px -1px rgba(0,0,0,0.02), 0 2px 4px 0 rgba(0,0,0,0.02)',
|
|
||||||
}}
|
|
||||||
bodyStyle={{
|
|
||||||
padding: '24px',
|
|
||||||
height: '100%',
|
height: '100%',
|
||||||
display: 'flex',
|
transition: 'all 0.3s ease',
|
||||||
flexDirection: 'column',
|
|
||||||
background: `linear-gradient(145deg, #ffffff 0%, ${buildType.color}08 100%)`,
|
|
||||||
}}
|
}}
|
||||||
actions={[
|
actions={[
|
||||||
<Tooltip title="编辑环境配置">
|
<Tooltip title="编辑环境">
|
||||||
<Button
|
<Button
|
||||||
type="text"
|
type="text"
|
||||||
key="edit"
|
icon={<EditOutlined />}
|
||||||
icon={<EditOutlined style={{ color: buildType.color }} />}
|
|
||||||
onClick={() => handleEdit(environment)}
|
onClick={() => handleEdit(environment)}
|
||||||
>
|
>
|
||||||
编辑
|
编辑
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>,
|
</Tooltip>,
|
||||||
<Popconfirm
|
<Tooltip title="删除环境">
|
||||||
key="delete"
|
|
||||||
title="确定要删除该环境吗?"
|
|
||||||
description="删除后将无法恢复,请谨慎操作"
|
|
||||||
onConfirm={() => handleDelete(environment.id)}
|
|
||||||
>
|
|
||||||
<Button
|
<Button
|
||||||
type="text"
|
type="text"
|
||||||
danger
|
danger
|
||||||
icon={<DeleteOutlined />}
|
icon={<DeleteOutlined />}
|
||||||
|
onClick={() => handleDelete(environment.id)}
|
||||||
>
|
>
|
||||||
删除
|
删除
|
||||||
</Button>
|
</Button>
|
||||||
</Popconfirm>,
|
</Tooltip>
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<div style={{ flex: 1 }}>
|
<div style={{ position: 'relative', padding: '16px' }}>
|
||||||
<div style={{
|
<div style={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'flex-start',
|
alignItems: 'center',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
marginBottom: '16px'
|
marginBottom: '16px'
|
||||||
}}>
|
}}>
|
||||||
<Title level={4} style={{
|
<Title level={4} style={{ margin: 0 }}>
|
||||||
margin: 0,
|
|
||||||
color: '#1f1f1f',
|
|
||||||
}}>
|
|
||||||
{environment.envName}
|
{environment.envName}
|
||||||
</Title>
|
</Title>
|
||||||
<RocketOutlined style={{
|
<Tag color={buildType.color} style={{ borderRadius: '12px', padding: '0 12px' }}>
|
||||||
fontSize: '24px',
|
{buildType.icon} {buildType.label}
|
||||||
color: buildType.color,
|
</Tag>
|
||||||
opacity: 0.8
|
|
||||||
}} />
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Text
|
<Text
|
||||||
@ -175,71 +162,45 @@ const EnvironmentList: React.FC = () => {
|
|||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<div style={{
|
<div style={{
|
||||||
display: 'flex',
|
background: '#f5f5f5',
|
||||||
flexDirection: 'column',
|
borderRadius: '8px',
|
||||||
gap: '16px',
|
padding: '16px',
|
||||||
padding: '20px',
|
marginBottom: '16px'
|
||||||
background: '#fff',
|
|
||||||
borderRadius: '12px',
|
|
||||||
boxShadow: '0 2px 8px rgba(0,0,0,0.04)',
|
|
||||||
}}>
|
}}>
|
||||||
<div>
|
<Text type="secondary">环境编码</Text>
|
||||||
<Text type="secondary" style={{ fontSize: '13px', display: 'block', marginBottom: '8px' }}>环境编码</Text>
|
<div style={{
|
||||||
<div style={{
|
fontFamily: 'monaco, monospace',
|
||||||
color: '#262626',
|
fontSize: '14px',
|
||||||
fontFamily: 'monaco, monospace',
|
color: '#262626',
|
||||||
fontSize: '14px',
|
marginTop: '8px',
|
||||||
padding: '8px 12px',
|
padding: '8px',
|
||||||
background: '#f5f5f5',
|
background: '#fff',
|
||||||
borderRadius: '6px',
|
borderRadius: '4px',
|
||||||
letterSpacing: '0.5px',
|
border: '1px solid #f0f0f0'
|
||||||
}}>
|
}}>
|
||||||
{environment.envCode}
|
{environment.envCode}
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<Space direction="vertical" size="middle" style={{ width: '100%' }}>
|
<div style={{
|
||||||
<div>
|
display: 'flex',
|
||||||
<Text type="secondary" style={{ fontSize: '13px', display: 'block', marginBottom: '8px' }}>构建方式</Text>
|
justifyContent: 'space-between',
|
||||||
<Tooltip title={buildType.description}>
|
alignItems: 'center',
|
||||||
<Tag
|
marginTop: '16px'
|
||||||
color={buildType.color}
|
}}>
|
||||||
style={{
|
<Tooltip title={deployType.description}>
|
||||||
padding: '4px 12px',
|
<Tag
|
||||||
borderRadius: '4px',
|
color={deployType.color}
|
||||||
cursor: 'help'
|
style={{
|
||||||
}}
|
padding: '4px 12px',
|
||||||
>
|
borderRadius: '12px',
|
||||||
{buildType.label}
|
cursor: 'help'
|
||||||
</Tag>
|
}}
|
||||||
</Tooltip>
|
>
|
||||||
</div>
|
{deployType.icon} {deployType.label}
|
||||||
<div>
|
</Tag>
|
||||||
<Text type="secondary" style={{ fontSize: '13px', display: 'block', marginBottom: '8px' }}>部署方式</Text>
|
</Tooltip>
|
||||||
<Tooltip title={deployType.description}>
|
<Text type="secondary">排序: {environment.sort}</Text>
|
||||||
<Tag
|
|
||||||
color={deployType.color}
|
|
||||||
style={{
|
|
||||||
padding: '4px 12px',
|
|
||||||
borderRadius: '4px',
|
|
||||||
cursor: 'help'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{deployType.label}
|
|
||||||
</Tag>
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<Text type="secondary" style={{ fontSize: '13px', display: 'block', marginBottom: '8px' }}>排序</Text>
|
|
||||||
<div style={{
|
|
||||||
color: '#262626',
|
|
||||||
fontSize: '14px',
|
|
||||||
padding: '4px 0',
|
|
||||||
}}>
|
|
||||||
{environment.sort}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Space>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
@ -258,9 +219,10 @@ const EnvironmentList: React.FC = () => {
|
|||||||
onClick={handleAdd}
|
onClick={handleAdd}
|
||||||
style={{
|
style={{
|
||||||
borderRadius: '8px',
|
borderRadius: '8px',
|
||||||
boxShadow: '0 2px 0 rgba(0,0,0,0.045)',
|
|
||||||
height: '40px',
|
|
||||||
padding: '0 24px',
|
padding: '0 24px',
|
||||||
|
height: '40px',
|
||||||
|
boxShadow: '0 2px 0 rgba(0,0,0,0.045)',
|
||||||
|
transition: 'all 0.3s ease',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
新建环境
|
新建环境
|
||||||
@ -270,47 +232,39 @@ const EnvironmentList: React.FC = () => {
|
|||||||
>
|
>
|
||||||
<div style={{ padding: '24px' }}>
|
<div style={{ padding: '24px' }}>
|
||||||
<Row gutter={[24, 24]}>
|
<Row gutter={[24, 24]}>
|
||||||
{environments.map(environment => (
|
{environments.length > 0 ? (
|
||||||
<Col xs={24} sm={12} md={8} lg={6} key={environment.id}>
|
environments.map((environment) => (
|
||||||
<EnvironmentCard environment={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>
|
</Col>
|
||||||
))}
|
)}
|
||||||
<Col xs={24} sm={12} md={8} lg={6}>
|
|
||||||
<Card
|
|
||||||
hoverable
|
|
||||||
onClick={handleAdd}
|
|
||||||
style={{
|
|
||||||
height: '100%',
|
|
||||||
borderRadius: '16px',
|
|
||||||
border: '1px dashed #d9d9d9',
|
|
||||||
background: '#fafafa',
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
minHeight: '380px',
|
|
||||||
transition: 'all 0.3s',
|
|
||||||
}}
|
|
||||||
bodyStyle={{
|
|
||||||
width: '100%',
|
|
||||||
height: '100%',
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div style={{ textAlign: 'center' }}>
|
|
||||||
<PlusOutlined style={{ fontSize: '28px', color: '#8c8c8c' }} />
|
|
||||||
<div style={{ marginTop: '12px', color: '#8c8c8c', fontSize: '16px' }}>新建环境</div>
|
|
||||||
</div>
|
|
||||||
</Card>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
</Row>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<EnvironmentModal
|
<EnvironmentModal
|
||||||
visible={modalVisible}
|
visible={modalVisible}
|
||||||
onCancel={() => setModalVisible(false)}
|
onCancel={() => setModalVisible(false)}
|
||||||
onSuccess={fetchEnvironments}
|
onSuccess={() => {
|
||||||
|
setModalVisible(false);
|
||||||
|
fetchEnvironments();
|
||||||
|
}}
|
||||||
initialValues={currentEnvironment}
|
initialValues={currentEnvironment}
|
||||||
/>
|
/>
|
||||||
</PageContainer>
|
</PageContainer>
|
||||||
|
|||||||
@ -5,14 +5,14 @@ import type { Environment } from '../../../Environment/List/types';
|
|||||||
import { createProjectGroup, updateProjectGroup } from '../service';
|
import { createProjectGroup, updateProjectGroup } from '../service';
|
||||||
import { getEnvironmentList } from '../../../Environment/List/service';
|
import { getEnvironmentList } from '../../../Environment/List/service';
|
||||||
|
|
||||||
interface ProjectModalProps {
|
interface ProjectGroupModalProps {
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
onSuccess: () => void;
|
onSuccess: () => void;
|
||||||
initialValues?: ProjectGroup;
|
initialValues?: ProjectGroup;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ProjectModal: React.FC<ProjectModalProps> = ({
|
const ProjectGroupModal: React.FC<ProjectGroupModalProps> = ({
|
||||||
visible,
|
visible,
|
||||||
onCancel,
|
onCancel,
|
||||||
onSuccess,
|
onSuccess,
|
||||||
@ -83,6 +83,7 @@ const ProjectModal: React.FC<ProjectModalProps> = ({
|
|||||||
onOk={handleSubmit}
|
onOk={handleSubmit}
|
||||||
onCancel={handleCancel}
|
onCancel={handleCancel}
|
||||||
destroyOnClose
|
destroyOnClose
|
||||||
|
width={560}
|
||||||
>
|
>
|
||||||
<Form
|
<Form
|
||||||
form={form}
|
form={form}
|
||||||
@ -155,4 +156,4 @@ const ProjectModal: React.FC<ProjectModalProps> = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ProjectModal;
|
export default ProjectGroupModal;
|
||||||
171
frontend/src/pages/Deploy/ProjectGroup/List/index.less
Normal file
171
frontend/src/pages/Deploy/ProjectGroup/List/index.less
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
.project-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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-space {
|
||||||
|
.anticon {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.environment-section {
|
||||||
|
.section-header {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.environment-list {
|
||||||
|
.environment-item {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 16px;
|
||||||
|
border: 1px solid #f0f0f0;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: translateX(4px);
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||||
|
border-color: #d9d9d9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.env-header {
|
||||||
|
.env-title {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.env-tags {
|
||||||
|
display: flex;
|
||||||
|
gap: 6px;
|
||||||
|
|
||||||
|
.env-tag {
|
||||||
|
margin: 0;
|
||||||
|
padding: 4px 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
.anticon {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.env-meta {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
|
||||||
|
.env-code {
|
||||||
|
font-family: 'monaco, monospace';
|
||||||
|
color: #666;
|
||||||
|
background: #f5f5f5;
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.env-desc {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #8c8c8c;
|
||||||
|
max-width: 200px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
cursor: help;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.env-footer {
|
||||||
|
margin-top: 12px;
|
||||||
|
padding-top: 12px;
|
||||||
|
border-top: 1px dashed #f0f0f0;
|
||||||
|
|
||||||
|
.anticon {
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-section {
|
||||||
|
background: #f5f5f5;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 16px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-content {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid #f0f0f0;
|
||||||
|
padding: 8px;
|
||||||
|
|
||||||
|
&.code {
|
||||||
|
font-family: 'monaco, monospace';
|
||||||
|
font-size: 14px;
|
||||||
|
color: #262626;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,19 +1,11 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { PageContainer } from '@ant-design/pro-layout';
|
import { PageContainer } from '@ant-design/pro-layout';
|
||||||
import { Card, Row, Col, Typography, Button, message, Popconfirm, Space, Tag, Input, Select, Tooltip, Statistic, Divider } from 'antd';
|
import { Card, Row, Col, Typography, Button, message, Input, Select, Empty, Tooltip, Tag, Space } from 'antd';
|
||||||
import {
|
import { PlusOutlined, SearchOutlined, RocketOutlined, TeamOutlined, EnvironmentOutlined, EditOutlined, DeleteOutlined, ClockCircleOutlined, CloudServerOutlined, SettingOutlined } from '@ant-design/icons';
|
||||||
PlusOutlined,
|
|
||||||
EditOutlined,
|
|
||||||
DeleteOutlined,
|
|
||||||
TeamOutlined,
|
|
||||||
SearchOutlined,
|
|
||||||
EnvironmentOutlined,
|
|
||||||
ClockCircleOutlined,
|
|
||||||
RocketOutlined
|
|
||||||
} from '@ant-design/icons';
|
|
||||||
import { getProjectGroupList, deleteProjectGroup } from './service';
|
import { getProjectGroupList, deleteProjectGroup } from './service';
|
||||||
import type { ProjectGroup } from './types';
|
import type { ProjectGroup } from './types';
|
||||||
import ProjectModal from './components/ProjectModal';
|
import ProjectGroupModal from './components/ProjectGroupModal';
|
||||||
|
import { BuildTypeEnum, DeployTypeEnum } from '../../Environment/List/types';
|
||||||
|
|
||||||
const { Title, Text } = Typography;
|
const { Title, Text } = Typography;
|
||||||
const { Search } = Input;
|
const { Search } = Input;
|
||||||
@ -91,204 +83,221 @@ const ProjectList: React.FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const filteredProjects = projects.filter(project => {
|
const filteredProjects = projects.filter(project => {
|
||||||
const matchesSearch = searchText ? (
|
const matchesSearch = searchText ?
|
||||||
project.projectGroupName.toLowerCase().includes(searchText.toLowerCase()) ||
|
project.projectGroupName.toLowerCase().includes(searchText.toLowerCase()) ||
|
||||||
project.projectGroupCode.toLowerCase().includes(searchText.toLowerCase()) ||
|
project.projectGroupCode.toLowerCase().includes(searchText.toLowerCase()) ||
|
||||||
project.projectGroupDesc?.toLowerCase().includes(searchText.toLowerCase())
|
project.projectGroupDesc?.toLowerCase().includes(searchText.toLowerCase())
|
||||||
) : true;
|
: true;
|
||||||
|
|
||||||
const matchesType = projectType === 'ALL' ? true :
|
const matchesType = projectType === 'ALL' ? true : getProjectTypeInfo(project.projectGroupCode).type === projectType;
|
||||||
getProjectTypeInfo(project.projectGroupCode).type === projectType;
|
|
||||||
|
|
||||||
return matchesSearch && matchesType;
|
return matchesSearch && matchesType;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const buildTypeMap = {
|
||||||
|
[BuildTypeEnum.JENKINS]: {
|
||||||
|
label: 'Jenkins构建',
|
||||||
|
color: '#1890ff',
|
||||||
|
icon: <RocketOutlined />,
|
||||||
|
},
|
||||||
|
[BuildTypeEnum.GITLAB_RUNNER]: {
|
||||||
|
label: 'GitLab Runner构建',
|
||||||
|
color: '#722ed1',
|
||||||
|
icon: <CloudServerOutlined />,
|
||||||
|
},
|
||||||
|
[BuildTypeEnum.GITHUB_ACTION]: {
|
||||||
|
label: 'GitHub Action构建',
|
||||||
|
color: '#52c41a',
|
||||||
|
icon: <SettingOutlined />,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const deployTypeMap = {
|
||||||
|
[DeployTypeEnum.K8S]: {
|
||||||
|
label: 'K8S',
|
||||||
|
color: '#1890ff',
|
||||||
|
icon: <CloudServerOutlined />,
|
||||||
|
},
|
||||||
|
[DeployTypeEnum.DOCKER]: {
|
||||||
|
label: 'Docker',
|
||||||
|
color: '#13c2c2',
|
||||||
|
icon: <RocketOutlined />,
|
||||||
|
},
|
||||||
|
[DeployTypeEnum.VM]: {
|
||||||
|
label: 'VM',
|
||||||
|
color: '#faad14',
|
||||||
|
icon: <SettingOutlined />,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
const ProjectCard = ({ project }: { project: ProjectGroup }) => {
|
const ProjectCard = ({ project }: { project: ProjectGroup }) => {
|
||||||
const typeInfo = getProjectTypeInfo(project.projectGroupCode);
|
const typeInfo = getProjectTypeInfo(project.projectGroupCode);
|
||||||
const Icon = typeInfo.icon;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
hoverable
|
hoverable
|
||||||
className="project-card"
|
className="project-card"
|
||||||
style={{
|
style={{
|
||||||
height: '100%',
|
borderRadius: '12px',
|
||||||
borderRadius: '16px',
|
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
border: 'none',
|
|
||||||
transition: 'all 0.3s',
|
|
||||||
boxShadow: '0 1px 2px 0 rgba(0,0,0,0.03), 0 1px 6px -1px rgba(0,0,0,0.02), 0 2px 4px 0 rgba(0,0,0,0.02)',
|
|
||||||
'&:hover': {
|
|
||||||
transform: 'translateY(-2px)',
|
|
||||||
boxShadow: '0 4px 12px rgba(0,0,0,0.1)',
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
bodyStyle={{
|
|
||||||
padding: '24px',
|
|
||||||
height: '100%',
|
height: '100%',
|
||||||
display: 'flex',
|
transition: 'all 0.3s ease',
|
||||||
flexDirection: 'column',
|
|
||||||
background: `linear-gradient(145deg, #ffffff 0%, ${typeInfo.color}08 100%)`,
|
|
||||||
}}
|
}}
|
||||||
|
actions={[
|
||||||
|
<Tooltip title="编辑项目组">
|
||||||
|
<Button
|
||||||
|
type="text"
|
||||||
|
icon={<EditOutlined />}
|
||||||
|
onClick={() => handleEdit(project)}
|
||||||
|
>
|
||||||
|
编辑
|
||||||
|
</Button>
|
||||||
|
</Tooltip>,
|
||||||
|
<Tooltip title="删除项目组">
|
||||||
|
<Button
|
||||||
|
type="text"
|
||||||
|
danger
|
||||||
|
icon={<DeleteOutlined />}
|
||||||
|
onClick={() => handleDelete(project.id)}
|
||||||
|
>
|
||||||
|
删除
|
||||||
|
</Button>
|
||||||
|
</Tooltip>,
|
||||||
|
<Tooltip title="管理成员">
|
||||||
|
<Button
|
||||||
|
type="text"
|
||||||
|
icon={<TeamOutlined />}
|
||||||
|
>
|
||||||
|
成员
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
]}
|
||||||
>
|
>
|
||||||
<div style={{ flex: 1 }}>
|
<div style={{ position: 'relative', padding: '16px' }}>
|
||||||
<div style={{
|
<div style={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'flex-start',
|
alignItems: 'center',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
marginBottom: '16px',
|
marginBottom: '16px'
|
||||||
position: 'relative'
|
|
||||||
}}>
|
}}>
|
||||||
<div style={{ flex: 1 }}>
|
<Title level={4} style={{ margin: 0 }}>
|
||||||
<Title level={4} style={{
|
{project.projectGroupName}
|
||||||
margin: 0,
|
</Title>
|
||||||
color: '#1f1f1f',
|
<Tooltip title={typeInfo.description}>
|
||||||
display: 'flex',
|
<Tag color={typeInfo.color} style={{ borderRadius: '12px', padding: '0 12px' }}>
|
||||||
alignItems: 'center',
|
{typeInfo.icon} {typeInfo.label}
|
||||||
gap: '8px'
|
</Tag>
|
||||||
}}>
|
</Tooltip>
|
||||||
{project.projectGroupName}
|
</div>
|
||||||
<Tag
|
|
||||||
color={typeInfo.color}
|
<Text
|
||||||
style={{
|
type="secondary"
|
||||||
margin: 0,
|
style={{
|
||||||
padding: '0 8px',
|
display: 'block',
|
||||||
borderRadius: '4px',
|
marginBottom: '24px',
|
||||||
fontSize: '12px'
|
height: '40px',
|
||||||
}}
|
overflow: 'hidden',
|
||||||
>
|
textOverflow: 'ellipsis',
|
||||||
{typeInfo.label}
|
WebkitLineClamp: 2,
|
||||||
</Tag>
|
WebkitBoxOrient: 'vertical',
|
||||||
</Title>
|
display: '-webkit-box',
|
||||||
<Text
|
}}
|
||||||
type="secondary"
|
>
|
||||||
style={{
|
{project.projectGroupDesc || '暂无描述'}
|
||||||
display: 'block',
|
</Text>
|
||||||
marginTop: '8px',
|
|
||||||
height: '40px',
|
<div className="info-section">
|
||||||
overflow: 'hidden',
|
<div className="section-header">
|
||||||
textOverflow: 'ellipsis',
|
<Text type="secondary">项目组编码</Text>
|
||||||
WebkitLineClamp: 2,
|
|
||||||
WebkitBoxOrient: 'vertical',
|
|
||||||
display: '-webkit-box',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{project.projectGroupDesc || '暂无描述'}
|
|
||||||
</Text>
|
|
||||||
</div>
|
</div>
|
||||||
<div style={{
|
<div className="section-content code">
|
||||||
color: typeInfo.color,
|
{project.projectGroupCode}
|
||||||
fontSize: '24px',
|
|
||||||
opacity: 0.8,
|
|
||||||
marginLeft: '16px'
|
|
||||||
}}>
|
|
||||||
{Icon}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{project.environments && project.environments.length > 0 && (
|
||||||
|
<div className="info-section environment-section">
|
||||||
|
<div className="section-header">
|
||||||
|
<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
|
||||||
|
<Text type="secondary">关联环境</Text>
|
||||||
|
<Tag color="blue" style={{ margin: 0, borderRadius: '10px' }}>
|
||||||
|
{project.environments.length} 个环境
|
||||||
|
</Tag>
|
||||||
|
</div>
|
||||||
|
<Tooltip title="查看更多">
|
||||||
|
<Button type="link" size="small" style={{ padding: 0 }}>
|
||||||
|
管理环境
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
<div className="environment-list">
|
||||||
|
{project.environments.map(env => {
|
||||||
|
const buildType = buildTypeMap[env.buildType];
|
||||||
|
const deployType = deployTypeMap[env.deployType];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div key={env.id} className="environment-item">
|
||||||
|
<div className="env-header">
|
||||||
|
<div className="env-title">
|
||||||
|
<Text strong style={{ fontSize: '15px' }}>{env.envName}</Text>
|
||||||
|
<div className="env-tags">
|
||||||
|
<Tooltip title={`构建方式:${buildType.label}`}>
|
||||||
|
<Tag color={buildType.color} className="env-tag">
|
||||||
|
{buildType.icon}
|
||||||
|
</Tag>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip title={`部署方式:${deployType.label}`}>
|
||||||
|
<Tag color={deployType.color} className="env-tag">
|
||||||
|
{deployType.icon}
|
||||||
|
</Tag>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="env-meta">
|
||||||
|
<Tooltip title="环境编码">
|
||||||
|
<Tag className="env-code">
|
||||||
|
{env.envCode}
|
||||||
|
</Tag>
|
||||||
|
</Tooltip>
|
||||||
|
{env.envDesc && (
|
||||||
|
<Tooltip title={env.envDesc}>
|
||||||
|
<Text type="secondary" className="env-desc">
|
||||||
|
{env.envDesc}
|
||||||
|
</Text>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="env-footer">
|
||||||
|
<Space size={16}>
|
||||||
|
<Text type="secondary" style={{ fontSize: '12px' }}>
|
||||||
|
<ClockCircleOutlined style={{ marginRight: '4px' }} />
|
||||||
|
最近部署:2小时前
|
||||||
|
</Text>
|
||||||
|
<Text type="secondary" style={{ fontSize: '12px' }}>
|
||||||
|
<RocketOutlined style={{ marginRight: '4px' }} />
|
||||||
|
部署次数:12次
|
||||||
|
</Text>
|
||||||
|
</Space>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div style={{
|
<div style={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
justifyContent: 'space-between',
|
||||||
gap: '16px',
|
alignItems: 'center',
|
||||||
padding: '20px',
|
marginTop: '16px'
|
||||||
background: '#fff',
|
|
||||||
borderRadius: '12px',
|
|
||||||
boxShadow: '0 2px 8px rgba(0,0,0,0.04)',
|
|
||||||
}}>
|
}}>
|
||||||
<div>
|
<Space>
|
||||||
<Text type="secondary" style={{ fontSize: '13px', display: 'block', marginBottom: '8px' }}>项目组编码</Text>
|
<ClockCircleOutlined style={{ color: typeInfo.color }} />
|
||||||
<div style={{
|
<Text type="secondary">2小时前</Text>
|
||||||
color: '#262626',
|
|
||||||
fontFamily: 'monaco, monospace',
|
|
||||||
fontSize: '14px',
|
|
||||||
padding: '8px 12px',
|
|
||||||
background: '#f5f5f5',
|
|
||||||
borderRadius: '6px',
|
|
||||||
letterSpacing: '0.5px',
|
|
||||||
}}>
|
|
||||||
{project.projectGroupCode}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Row gutter={16}>
|
|
||||||
<Col span={12}>
|
|
||||||
<Statistic
|
|
||||||
title={<Text type="secondary" style={{ fontSize: '13px' }}>环境数量</Text>}
|
|
||||||
value={2}
|
|
||||||
prefix={<EnvironmentOutlined />}
|
|
||||||
valueStyle={{ fontSize: '16px', color: typeInfo.color }}
|
|
||||||
/>
|
|
||||||
</Col>
|
|
||||||
<Col span={12}>
|
|
||||||
<Statistic
|
|
||||||
title={<Text type="secondary" style={{ fontSize: '13px' }}>成员数量</Text>}
|
|
||||||
value={5}
|
|
||||||
prefix={<TeamOutlined />}
|
|
||||||
valueStyle={{ fontSize: '16px', color: typeInfo.color }}
|
|
||||||
/>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<Text type="secondary" style={{ fontSize: '13px', display: 'block', marginBottom: '8px' }}>最近活动</Text>
|
|
||||||
<div style={{
|
|
||||||
color: '#262626',
|
|
||||||
fontSize: '14px',
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
gap: '8px'
|
|
||||||
}}>
|
|
||||||
<ClockCircleOutlined style={{ color: typeInfo.color }} />
|
|
||||||
<span>2小时前</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<Text type="secondary" style={{ fontSize: '13px', display: 'block', marginBottom: '8px' }}>排序</Text>
|
|
||||||
<div style={{
|
|
||||||
color: '#262626',
|
|
||||||
fontSize: '14px',
|
|
||||||
padding: '4px 0',
|
|
||||||
}}>
|
|
||||||
{project.sort}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Divider style={{ margin: '8px 0' }} />
|
|
||||||
|
|
||||||
<Space size="middle" style={{ justifyContent: 'center' }}>
|
|
||||||
<Button
|
|
||||||
type="primary"
|
|
||||||
icon={<EditOutlined />}
|
|
||||||
onClick={() => handleEdit(project)}
|
|
||||||
style={{
|
|
||||||
borderRadius: '6px',
|
|
||||||
background: typeInfo.color,
|
|
||||||
border: 'none'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
编辑
|
|
||||||
</Button>
|
|
||||||
<Popconfirm
|
|
||||||
title="确定要删除该项目组吗?"
|
|
||||||
description="删除后将无法恢复,请谨慎操作"
|
|
||||||
onConfirm={() => handleDelete(project.id)}
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
danger
|
|
||||||
icon={<DeleteOutlined />}
|
|
||||||
style={{ borderRadius: '6px' }}
|
|
||||||
>
|
|
||||||
删除
|
|
||||||
</Button>
|
|
||||||
</Popconfirm>
|
|
||||||
<Button
|
|
||||||
icon={<TeamOutlined />}
|
|
||||||
style={{ borderRadius: '6px' }}
|
|
||||||
>
|
|
||||||
成员
|
|
||||||
</Button>
|
|
||||||
</Space>
|
</Space>
|
||||||
|
<Text type="secondary">排序: {project.sort}</Text>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
@ -307,9 +316,10 @@ const ProjectList: React.FC = () => {
|
|||||||
onClick={handleAdd}
|
onClick={handleAdd}
|
||||||
style={{
|
style={{
|
||||||
borderRadius: '8px',
|
borderRadius: '8px',
|
||||||
boxShadow: '0 2px 0 rgba(0,0,0,0.045)',
|
|
||||||
height: '40px',
|
|
||||||
padding: '0 24px',
|
padding: '0 24px',
|
||||||
|
height: '40px',
|
||||||
|
boxShadow: '0 2px 0 rgba(0,0,0,0.045)',
|
||||||
|
transition: 'all 0.3s ease',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
新建项目组
|
新建项目组
|
||||||
@ -350,47 +360,39 @@ const ProjectList: React.FC = () => {
|
|||||||
|
|
||||||
<div style={{ padding: '0 24px 24px' }}>
|
<div style={{ padding: '0 24px 24px' }}>
|
||||||
<Row gutter={[24, 24]}>
|
<Row gutter={[24, 24]}>
|
||||||
{filteredProjects.map(project => (
|
{filteredProjects.length > 0 ? (
|
||||||
<Col xs={24} sm={12} md={8} lg={6} key={project.id}>
|
filteredProjects.map((project) => (
|
||||||
<ProjectCard project={project} />
|
<Col key={project.id} xs={24} sm={12} md={8} lg={6}>
|
||||||
|
<ProjectCard project={project} />
|
||||||
|
</Col>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<Col span={24}>
|
||||||
|
<Empty
|
||||||
|
image={Empty.PRESENTED_IMAGE_SIMPLE}
|
||||||
|
description={searchText ? "未找到匹配的项目组" : "暂无项目组数据"}
|
||||||
|
style={{
|
||||||
|
background: '#fff',
|
||||||
|
padding: '40px',
|
||||||
|
borderRadius: '8px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button type="primary" onClick={handleAdd}>
|
||||||
|
立即创建
|
||||||
|
</Button>
|
||||||
|
</Empty>
|
||||||
</Col>
|
</Col>
|
||||||
))}
|
)}
|
||||||
<Col xs={24} sm={12} md={8} lg={6}>
|
|
||||||
<Card
|
|
||||||
hoverable
|
|
||||||
onClick={handleAdd}
|
|
||||||
style={{
|
|
||||||
height: '100%',
|
|
||||||
borderRadius: '16px',
|
|
||||||
border: '1px dashed #d9d9d9',
|
|
||||||
background: '#fafafa',
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
minHeight: '380px',
|
|
||||||
transition: 'all 0.3s',
|
|
||||||
}}
|
|
||||||
bodyStyle={{
|
|
||||||
width: '100%',
|
|
||||||
height: '100%',
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div style={{ textAlign: 'center' }}>
|
|
||||||
<PlusOutlined style={{ fontSize: '28px', color: '#8c8c8c' }} />
|
|
||||||
<div style={{ marginTop: '12px', color: '#8c8c8c', fontSize: '16px' }}>新建项目组</div>
|
|
||||||
</div>
|
|
||||||
</Card>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
</Row>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ProjectModal
|
<ProjectGroupModal
|
||||||
visible={modalVisible}
|
visible={modalVisible}
|
||||||
onCancel={() => setModalVisible(false)}
|
onCancel={() => setModalVisible(false)}
|
||||||
onSuccess={fetchProjects}
|
onSuccess={() => {
|
||||||
|
setModalVisible(false);
|
||||||
|
fetchProjects();
|
||||||
|
}}
|
||||||
initialValues={currentProject}
|
initialValues={currentProject}
|
||||||
/>
|
/>
|
||||||
</PageContainer>
|
</PageContainer>
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { BaseResponse, BaseRequest, BaseQuery } from '@/types/base';
|
import { BaseResponse, BaseRequest, BaseQuery } from '@/types/base';
|
||||||
|
import { Environment } from '../../Environment/List/types';
|
||||||
|
|
||||||
// 项目基础信息
|
// 项目基础信息
|
||||||
export interface ProjectGroup extends BaseResponse {
|
export interface ProjectGroup extends BaseResponse {
|
||||||
@ -6,7 +7,8 @@ export interface ProjectGroup extends BaseResponse {
|
|||||||
projectGroupCode: string;
|
projectGroupCode: string;
|
||||||
projectGroupName: string;
|
projectGroupName: string;
|
||||||
projectGroupDesc?: string;
|
projectGroupDesc?: string;
|
||||||
projectGroupStatus: string;
|
projectGroupStatus: 'ENABLE' | 'DISABLE';
|
||||||
|
environments: Environment[];
|
||||||
sort: number;
|
sort: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user