diff --git a/frontend/src/pages/Deploy/Application/List/index.tsx b/frontend/src/pages/Deploy/Application/List/index.tsx index e3f626c4..a6498bbe 100644 --- a/frontend/src/pages/Deploy/Application/List/index.tsx +++ b/frontend/src/pages/Deploy/Application/List/index.tsx @@ -240,36 +240,11 @@ const ApplicationList: React.FC = () => { ]; return ( - - 当前项目组: - ({ - label: ( - - {project.projectGroupName} - - {getProjectTypeInfo(project.type).label} - - - ), - value: project.id, - }))} - /> - - ), - }} - > + <> columns={columns} actionRef={actionRef} - scroll={{x: 1300}} + scroll={{x: 'max-content'}} cardBordered request={async (params) => { if (!selectedProjectGroupId) { @@ -343,7 +318,7 @@ const ApplicationList: React.FC = () => { projectGroupId={selectedProjectGroupId} /> )} - + > ); }; diff --git a/frontend/src/pages/Deploy/Environment/List/components/EnvironmentModal.tsx b/frontend/src/pages/Deploy/Environment/List/components/EnvironmentModal.tsx index e8fe9aff..0716466f 100644 --- a/frontend/src/pages/Deploy/Environment/List/components/EnvironmentModal.tsx +++ b/frontend/src/pages/Deploy/Environment/List/components/EnvironmentModal.tsx @@ -1,8 +1,9 @@ -import React, {useEffect, useState} from 'react'; -import {Modal, Form, Input, Select, message} from 'antd'; +import React, {useEffect} from 'react'; +import {Modal, Form, Input, InputNumber, Select, message} 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; @@ -12,105 +13,133 @@ interface EnvironmentModalProps { } const EnvironmentModal: React.FC = ({ - visible, - onCancel, - onSuccess, - initialValues, - }) => { + visible, + onCancel, + onSuccess, + initialValues, +}) => { const [form] = Form.useForm(); - const [loading, setLoading] = useState(false); - + const isEdit = !!initialValues?.id; useEffect(() => { - if (visible) { - form.setFieldsValue(initialValues || {}); + if (visible && initialValues) { + form.setFieldsValue(initialValues); } - }, [visible, initialValues]); + }, [visible, initialValues, form]); - const handleOk = async () => { + const handleSubmit = async () => { try { const values = await form.validateFields(); - setLoading(true); - if (initialValues?.id) { + if (isEdit) { await updateEnvironment({ ...values, id: initialValues.id, }); - message.success('更新成功'); } else { await createEnvironment(values); - message.success('创建成功'); } + message.success(`${isEdit ? '更新' : '创建'}成功`); + form.resetFields(); onSuccess(); - onCancel(); } catch (error) { - console.error('Submit failed:', error); - } finally { - setLoading(false); + message.error(`${isEdit ? '更新' : '创建'}失败`); } }; return ( { + form.resetFields(); + onCancel(); + }} + onOk={handleSubmit} width={600} > - - - - + + + + - + + + + + + {Object.values(BuildTypeEnum).map((type) => { + const typeInfo = getBuildTypeInfo(type); + return ( + + + {typeInfo.icon} + {typeInfo.label} + + + ); + })} + + + + + + {Object.values(DeployTypeEnum).map((type) => { + const typeInfo = getDeployTypeInfo(type); + return ( + + + {typeInfo.icon} + {typeInfo.label} + + + ); + })} + - + diff --git a/frontend/src/pages/Deploy/Environment/List/index.tsx b/frontend/src/pages/Deploy/Environment/List/index.tsx index bfadb5bd..63fe3698 100644 --- a/frontend/src/pages/Deploy/Environment/List/index.tsx +++ b/frontend/src/pages/Deploy/Environment/List/index.tsx @@ -1,18 +1,19 @@ import React, {useState} from 'react'; import {PageContainer} from '@ant-design/pro-layout'; -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 {Button, message, Popconfirm, Space, Tag} from 'antd'; +import {PlusOutlined, EditOutlined, DeleteOutlined} from '@ant-design/icons'; +import {getEnvironmentPage, deleteEnvironment} from './service'; +import type {Environment, EnvironmentQueryParams} from './types'; +import {BuildTypeEnum, DeployTypeEnum} from './types'; +import {getBuildTypeInfo, getDeployTypeInfo} from './utils'; import EnvironmentModal from './components/EnvironmentModal'; import {ProTable} from '@ant-design/pro-components'; -import type {ProColumns} from '@ant-design/pro-components'; +import type {ProColumns, ActionType} from '@ant-design/pro-components'; const EnvironmentList: React.FC = () => { const [modalVisible, setModalVisible] = useState(false); const [currentEnvironment, setCurrentEnvironment] = useState(); - const actionRef = React.useRef(); - const [form] = Form.useForm(); + const actionRef = React.useRef(); const handleDelete = async (id: number) => { try { @@ -53,21 +54,57 @@ const EnvironmentList: React.FC = () => { title: '环境描述', dataIndex: 'envDesc', ellipsis: true, + width: '30%', }, { - title: '构建类型', + title: '构建方式', dataIndex: 'buildType', - width: 100, - render: (buildType) => ( - - {buildType} - - ), + width: 180, + render: (buildType) => { + if (!buildType) return '-'; + const typeInfo = getBuildTypeInfo(buildType as BuildTypeEnum); + return ( + + {typeInfo.icon} + {typeInfo.label} + + ); + }, + filters: [ + {text: 'Jenkins构建', value: BuildTypeEnum.JENKINS}, + {text: 'GitLab Runner构建', value: BuildTypeEnum.GITLAB_RUNNER}, + {text: 'GitHub Action构建', value: BuildTypeEnum.GITHUB_ACTION}, + ], + filterMode: 'menu', + filtered: false, + }, + { + title: '部署方式', + dataIndex: 'deployType', + width: 180, + render: (deployType) => { + if (!deployType) return '-'; + const typeInfo = getDeployTypeInfo(deployType as DeployTypeEnum); + return ( + + {typeInfo.icon} + {typeInfo.label} + + ); + }, + filters: [ + {text: 'Kubernetes集群部署', value: DeployTypeEnum.K8S}, + {text: 'Docker容器部署', value: DeployTypeEnum.DOCKER}, + {text: '虚拟机部署', value: DeployTypeEnum.VM}, + ], + filterMode: 'menu', + filtered: false, }, { title: '排序', dataIndex: 'sort', width: 80, + align: 'center', sorter: true, }, { @@ -76,30 +113,31 @@ const EnvironmentList: React.FC = () => { key: 'action', valueType: 'option', fixed: 'right', - render: (_, record) => ( - + align: 'center', + render: (_, record) => [ + } + onClick={() => handleEdit(record)} + > + 编辑 + , + handleDelete(record.id)} + > } - onClick={() => handleEdit(record)} + danger + icon={} > - 编辑 + 删除 - handleDelete(record.id)} - > - } - > - 删除 - - - - ), + + ], }, ]; @@ -108,13 +146,18 @@ const EnvironmentList: React.FC = () => { columns={columns} actionRef={actionRef} + scroll={{x: 'max-content'}} cardBordered request={async (params) => { - const {current, pageSize} = params; - const data = await getEnvironmentPage({ - current, - pageSize, - }); + const queryParams: EnvironmentQueryParams = { + pageSize: params.pageSize, + pageNum: params.current, + envCode: params.envCode as string, + envName: params.envName as string, + buildType: params.buildType as BuildTypeEnum, + deployType: params.deployType as DeployTypeEnum, + }; + const data = await getEnvironmentPage(queryParams); return { data: data.content || [], success: true, diff --git a/frontend/src/pages/Deploy/Environment/List/service.ts b/frontend/src/pages/Deploy/Environment/List/service.ts index 28ed4172..364f321a 100644 --- a/frontend/src/pages/Deploy/Environment/List/service.ts +++ b/frontend/src/pages/Deploy/Environment/List/service.ts @@ -1,33 +1,33 @@ -import request from '@/utils/request'; -import type { CreateEnvironmentRequest, UpdateEnvironmentRequest, Environment, EnvironmentQuery } from './types'; -import type { Page } from '@/types/base'; +import type {Environment, CreateEnvironmentRequest, UpdateEnvironmentRequest, EnvironmentQueryParams} from './types'; +import type {Page} from '@/types/base'; +import request from "@/utils/request"; const BASE_URL = '/api/v1/environments'; +// 获取环境分页列表 +export const getEnvironmentPage = (params: EnvironmentQueryParams) => + request.get>(`${BASE_URL}/page`, {params}); + // 创建环境 -export const createEnvironment = (data: CreateEnvironmentRequest) => - request.post(BASE_URL, data); +export const createEnvironment = (data: CreateEnvironmentRequest) => + request.post(BASE_URL, data); // 更新环境 -export const updateEnvironment = (data: UpdateEnvironmentRequest) => - request.put(`${BASE_URL}/${data.id}`, data); +export const updateEnvironment = (data: UpdateEnvironmentRequest) => + request.put(`${BASE_URL}/${data.id}`, data); // 删除环境 -export const deleteEnvironment = (id: number) => - request.delete(`${BASE_URL}/${id}`); +export const deleteEnvironment = (id: number) => + request.delete(`${BASE_URL}/${id}`); // 获取环境详情 export const getEnvironment = (id: number) => request.get(`${BASE_URL}/${id}`); -// 分页查询环境列表 -export const getEnvironmentPage = (params?: EnvironmentQuery) => - request.get>(`${BASE_URL}/page`, { params }); - // 获取所有环境列表 export const getEnvironmentList = () => request.get(BASE_URL); // 条件查询环境列表 -export const getEnvironmentListByCondition = (params?: EnvironmentQuery) => +export const getEnvironmentListByCondition = (params?: EnvironmentQueryParams) => request.get(`${BASE_URL}/list`, { params }); diff --git a/frontend/src/pages/Deploy/Environment/List/types.ts b/frontend/src/pages/Deploy/Environment/List/types.ts index 3cfe42a0..89f1aa6e 100644 --- a/frontend/src/pages/Deploy/Environment/List/types.ts +++ b/frontend/src/pages/Deploy/Environment/List/types.ts @@ -1,32 +1,50 @@ -import type { BaseQuery } from '@/types/base'; +import {BaseResponse, BaseRequest, BaseQuery} from '@/types/base'; - -export interface Environment { - id: number; - envCode: string; - envName: string; - envDesc?: string; - sort: number; +// 构建方式枚举 +export enum BuildTypeEnum { + JENKINS = 'JENKINS', + GITLAB_RUNNER = 'GITLAB_RUNNER', + GITHUB_ACTION = 'GITHUB_ACTION' } -export interface CreateEnvironmentRequest { - projectId: number; - envCode: string; - envName: string; - envDesc?: string; - sort: number; +// 部署方式枚举 +export enum DeployTypeEnum { + K8S = 'K8S', + DOCKER = 'DOCKER', + VM = 'VM' } -export interface UpdateEnvironmentRequest { - id: number; - envCode: string; - envName: string; - envDesc?: string; - sort: number; +// 环境基础信息 +export interface Environment extends BaseResponse { + tenantCode: string; + envCode: string; + envName: string; + envDesc?: string; + buildType: BuildTypeEnum; + deployType: DeployTypeEnum; + sort: number; } -export interface EnvironmentQuery extends BaseQuery { - projectId?: number; - envCode?: string; - envName?: string; +// 创建环境请求参数 +export interface CreateEnvironmentRequest extends BaseRequest { + tenantCode: string; + envCode: string; + envName: string; + envDesc?: string; + buildType: BuildTypeEnum; + deployType: DeployTypeEnum; + sort: number; +} + +// 更新环境请求参数 +export interface UpdateEnvironmentRequest extends CreateEnvironmentRequest { + id: number; +} + +// 分页查询参数 +export interface EnvironmentQueryParams extends BaseQuery { + envCode?: string; + envName?: string; + buildType?: BuildTypeEnum; + deployType?: DeployTypeEnum; } diff --git a/frontend/src/pages/Deploy/Environment/List/utils.tsx b/frontend/src/pages/Deploy/Environment/List/utils.tsx new file mode 100644 index 00000000..9bf76781 --- /dev/null +++ b/frontend/src/pages/Deploy/Environment/List/utils.tsx @@ -0,0 +1,76 @@ +import React from 'react'; +import { + GithubOutlined, + GitlabOutlined, + CloudServerOutlined, + CloudOutlined, + DesktopOutlined, + ApiOutlined, +} from '@ant-design/icons'; +import {BuildTypeEnum, DeployTypeEnum} from './types'; + +interface TypeInfo { + label: string; + color: string; + icon: React.ReactNode; +} + +// 获取构建方式信息 +export const getBuildTypeInfo = (type: BuildTypeEnum): TypeInfo => { + switch (type) { + case BuildTypeEnum.JENKINS: + return { + label: 'Jenkins构建', + color: '#D24939', + icon: , + }; + case BuildTypeEnum.GITLAB_RUNNER: + return { + label: 'GitLab Runner构建', + color: '#FC6D26', + icon: , + }; + case BuildTypeEnum.GITHUB_ACTION: + return { + label: 'GitHub Action构建', + color: '#2088FF', + icon: , + }; + default: + return { + label: type || '未知', + color: '#666666', + icon: , + }; + } +}; + +// 获取部署方式信息 +export const getDeployTypeInfo = (type: DeployTypeEnum): TypeInfo => { + switch (type) { + case DeployTypeEnum.K8S: + return { + label: 'Kubernetes集群部署', + color: '#326CE5', + icon: , + }; + case DeployTypeEnum.DOCKER: + return { + label: 'Docker容器部署', + color: '#2496ED', + icon: , + }; + case DeployTypeEnum.VM: + return { + label: '虚拟机部署', + color: '#F7B93E', + icon: , + }; + default: + return { + label: type || '未知', + color: '#666666', + icon: , + }; + } +}; \ No newline at end of file diff --git a/frontend/src/pages/Deploy/ProjectGroup/List/index.tsx b/frontend/src/pages/Deploy/ProjectGroup/List/index.tsx index 716273f9..d354b16e 100644 --- a/frontend/src/pages/Deploy/ProjectGroup/List/index.tsx +++ b/frontend/src/pages/Deploy/ProjectGroup/List/index.tsx @@ -100,7 +100,7 @@ const ProjectGroupList: React.FC = () => { - {record.environments?.length || 0} + {record?.totalEnvironments || 0} ), @@ -126,35 +126,42 @@ const ProjectGroupList: React.FC = () => { }, { title: '操作', - width: 180, + width: 280, key: 'action', valueType: 'option', fixed: 'right', align: 'center', - render: (_, record) => ( - + render: (_, record) => [ + } + onClick={() => handleEdit(record)} + > + 编辑 + , + } + > + 环境绑定 + , + handleDelete(record.id)} + > } - onClick={() => handleEdit(record)} + danger + icon={} > - 编辑 + 删除 - handleDelete(record.id)} - > - } - > - 删除 - - - - ), + + ], }, ]; @@ -163,6 +170,7 @@ const ProjectGroupList: React.FC = () => { columns={columns} actionRef={actionRef} + scroll={{ x: 'max-content' }} cardBordered request={async (params) => { const queryParams: ProjectGroupQueryParams = { diff --git a/frontend/src/pages/Deploy/ProjectGroup/List/types.ts b/frontend/src/pages/Deploy/ProjectGroup/List/types.ts index 062850d7..eaa99549 100644 --- a/frontend/src/pages/Deploy/ProjectGroup/List/types.ts +++ b/frontend/src/pages/Deploy/ProjectGroup/List/types.ts @@ -13,6 +13,7 @@ export interface ProjectGroup extends BaseResponse { projectGroupName: string; projectGroupDesc?: string; enabled: boolean; + totalEnvironments: number; totalApplications: number; sort: number; }