From 48f84593310ed80ba72f35597297dca3eab135d8 Mon Sep 17 00:00:00 2001 From: dengqichen Date: Mon, 23 Dec 2024 15:12:36 +0800 Subject: [PATCH] 1 --- .../List/components/ApplicationModal.tsx | 123 +++++++ .../pages/Deploy/Application/List/index.tsx | 273 +++++++++++++++ .../pages/Deploy/Application/List/service.ts | 33 ++ .../pages/Deploy/Application/List/types.ts | 31 ++ .../List/components/EnvironmentModal.tsx | 118 +++++++ .../pages/Deploy/Environment/List/index.tsx | 317 ++++++++++++++++++ .../pages/Deploy/Environment/List/service.ts | 33 ++ .../pages/Deploy/Environment/List/types.ts | 31 ++ .../Project/List/components/ProjectModal.tsx | 113 +++++++ .../src/pages/Deploy/Project/List/index.tsx | 217 ++++++++++++ .../src/pages/Deploy/Project/List/service.ts | 41 +++ .../src/pages/Deploy/Project/List/types.ts | 33 ++ frontend/src/pages/Login/index.tsx | 1 - frontend/src/pages/System/Menu/service.ts | 274 +++++++-------- frontend/src/router/index.tsx | 44 ++- 15 files changed, 1532 insertions(+), 150 deletions(-) create mode 100644 frontend/src/pages/Deploy/Application/List/components/ApplicationModal.tsx create mode 100644 frontend/src/pages/Deploy/Application/List/index.tsx create mode 100644 frontend/src/pages/Deploy/Application/List/service.ts create mode 100644 frontend/src/pages/Deploy/Application/List/types.ts create mode 100644 frontend/src/pages/Deploy/Environment/List/components/EnvironmentModal.tsx create mode 100644 frontend/src/pages/Deploy/Environment/List/index.tsx create mode 100644 frontend/src/pages/Deploy/Environment/List/service.ts create mode 100644 frontend/src/pages/Deploy/Environment/List/types.ts create mode 100644 frontend/src/pages/Deploy/Project/List/components/ProjectModal.tsx create mode 100644 frontend/src/pages/Deploy/Project/List/index.tsx create mode 100644 frontend/src/pages/Deploy/Project/List/service.ts create mode 100644 frontend/src/pages/Deploy/Project/List/types.ts diff --git a/frontend/src/pages/Deploy/Application/List/components/ApplicationModal.tsx b/frontend/src/pages/Deploy/Application/List/components/ApplicationModal.tsx new file mode 100644 index 00000000..85657eb0 --- /dev/null +++ b/frontend/src/pages/Deploy/Application/List/components/ApplicationModal.tsx @@ -0,0 +1,123 @@ +import React, { useEffect } from 'react'; +import { Modal, Form, Input, InputNumber, Radio, message } from 'antd'; +import type { Application } from '../types'; +import { createApplication, updateApplication } from '../service'; + +interface ApplicationModalProps { + visible: boolean; + onCancel: () => void; + onSuccess: () => void; + initialValues?: Application; + projectId: number; +} + +const ApplicationModal: React.FC = ({ + visible, + onCancel, + onSuccess, + initialValues, + projectId, +}) => { + const [form] = Form.useForm(); + const isEdit = !!initialValues; + + useEffect(() => { + if (visible) { + if (initialValues) { + form.setFieldsValue(initialValues); + } else { + form.setFieldsValue({ projectId, appStatus: 'ENABLE', sort: 0 }); + } + } + }, [visible, initialValues, form, projectId]); + + const handleSubmit = async () => { + try { + const values = await form.validateFields(); + if (isEdit) { + await updateApplication({ ...values, id: initialValues.id }); + message.success('更新成功'); + } else { + await createApplication(values); + message.success('创建成功'); + } + onSuccess(); + onCancel(); + form.resetFields(); + } catch (error) { + message.error(isEdit ? '更新失败' : '创建失败'); + } + }; + + const handleCancel = () => { + form.resetFields(); + onCancel(); + }; + + return ( + +
+ + + + + + + + + + + + + + + 启用 + 禁用 + + + + + + + + +
+
+ ); +}; + +export default ApplicationModal; diff --git a/frontend/src/pages/Deploy/Application/List/index.tsx b/frontend/src/pages/Deploy/Application/List/index.tsx new file mode 100644 index 00000000..a372c87d --- /dev/null +++ b/frontend/src/pages/Deploy/Application/List/index.tsx @@ -0,0 +1,273 @@ +import React, { useState } from 'react'; +import { PageContainer } from '@ant-design/pro-layout'; +import { Card, Row, Col, Typography, Button, message, Popconfirm, Tag, Space, Tooltip } from 'antd'; +import { PlusOutlined, EditOutlined, DeleteOutlined, CodeOutlined, CloudUploadOutlined, ApiOutlined } from '@ant-design/icons'; +import { getApplicationList, deleteApplication } from './service'; +import type { Application } from './types'; +import ApplicationModal from './components/ApplicationModal'; + +const { Title, Text } = Typography; + +const ApplicationList: React.FC = () => { + const [applications, setApplications] = useState([]); + const [loading, setLoading] = useState(false); + const [modalVisible, setModalVisible] = useState(false); + const [currentApplication, setCurrentApplication] = useState(); + + // TODO: 这里需要从上下文或者URL参数获取projectId + const projectId = 1; + + const fetchApplications = async () => { + setLoading(true); + try { + const data = await getApplicationList(); + setApplications(data); + } catch (error) { + message.error('获取应用列表失败'); + } finally { + setLoading(false); + } + }; + + React.useEffect(() => { + fetchApplications(); + }, []); + + const handleDelete = async (id: number) => { + try { + await deleteApplication(id); + message.success('删除成功'); + fetchApplications(); + } catch (error) { + message.error('删除失败'); + } + }; + + const handleAdd = () => { + setCurrentApplication(undefined); + setModalVisible(true); + }; + + const handleEdit = (application: Application) => { + setCurrentApplication(application); + setModalVisible(true); + }; + + // 根据应用编码推测应用类型 + const getAppType = (appCode: string) => { + if (appCode.toLowerCase().includes('web')) return { icon: , name: '前端应用', color: '#1890ff' }; + if (appCode.toLowerCase().includes('api')) return { icon: , name: 'API服务', color: '#52c41a' }; + return { icon: , name: '后端服务', color: '#722ed1' }; + }; + + const ApplicationCard = ({ application }: { application: Application }) => { + const appType = getAppType(application.appCode); + + return ( + } + onClick={() => handleEdit(application)} + > + 编辑 + , + handleDelete(application.id)} + > + + , + ]} + > +
+
+ {appType.icon} + {appType.name} +
+ + + {application.appName} + <Tag + color={application.appStatus === 'ENABLE' ? '#52c41a' : '#d9d9d9'} + style={{ + borderRadius: '4px', + fontSize: '12px', + padding: '0 8px', + }} + > + {application.appStatus === 'ENABLE' ? '启用' : '禁用'} + </Tag> + + + + {application.appDesc || '暂无描述'} + + +
+
+ 应用编码 +
+ {application.appCode} +
+
+ +
+ 排序 +
+ {application.sort} +
+
+ + + +
+
+
+
+ ); + }; + + return ( + } + onClick={handleAdd} + style={{ + borderRadius: '6px', + boxShadow: '0 2px 0 rgba(0,0,0,0.045)', + }} + > + 新建应用 + + ], + }} + > +
+ + {applications.map(application => ( + + + + ))} + + +
+ +
新建应用
+
+
+ +
+
+ + setModalVisible(false)} + onSuccess={fetchApplications} + initialValues={currentApplication} + projectId={projectId} + /> +
+ ); +}; + +export default ApplicationList; diff --git a/frontend/src/pages/Deploy/Application/List/service.ts b/frontend/src/pages/Deploy/Application/List/service.ts new file mode 100644 index 00000000..04183e71 --- /dev/null +++ b/frontend/src/pages/Deploy/Application/List/service.ts @@ -0,0 +1,33 @@ +import request from '@/utils/request'; +import type { CreateApplicationRequest, UpdateApplicationRequest, Application, ApplicationQuery } from './types'; +import type { Page } from '@/types/base'; + +const BASE_URL = '/api/v1/applications'; + +// 创建应用 +export const createApplication = (data: CreateApplicationRequest) => + request.post(BASE_URL, data); + +// 更新应用 +export const updateApplication = (data: UpdateApplicationRequest) => + request.put(`${BASE_URL}/${data.id}`, data); + +// 删除应用 +export const deleteApplication = (id: number) => + request.delete(`${BASE_URL}/${id}`); + +// 获取应用详情 +export const getApplication = (id: number) => + request.get(`${BASE_URL}/${id}`); + +// 分页查询应用列表 +export const getApplicationPage = (params?: ApplicationQuery) => + request.get>(`${BASE_URL}/page`, { params }); + +// 获取所有应用列表 +export const getApplicationList = () => + request.get(BASE_URL); + +// 条件查询应用列表 +export const getApplicationListByCondition = (params?: ApplicationQuery) => + request.get(`${BASE_URL}/list`, { params }); diff --git a/frontend/src/pages/Deploy/Application/List/types.ts b/frontend/src/pages/Deploy/Application/List/types.ts new file mode 100644 index 00000000..a0fbbdd5 --- /dev/null +++ b/frontend/src/pages/Deploy/Application/List/types.ts @@ -0,0 +1,31 @@ +import type { BaseQuery } from '@/types/base'; + +export interface Application { + id: number; + projectId: number; + appCode: string; + appName: string; + appDesc?: string; + appStatus: 'ENABLE' | 'DISABLE'; + sort: number; +} + +export interface CreateApplicationRequest { + projectId: number; + appCode: string; + appName: string; + appDesc?: string; + appStatus: string; + sort: number; +} + +export interface UpdateApplicationRequest extends CreateApplicationRequest { + id: number; +} + +export interface ApplicationQuery extends BaseQuery { + projectId?: number; + appCode?: string; + appName?: string; + appStatus?: string; +} diff --git a/frontend/src/pages/Deploy/Environment/List/components/EnvironmentModal.tsx b/frontend/src/pages/Deploy/Environment/List/components/EnvironmentModal.tsx new file mode 100644 index 00000000..42fe7669 --- /dev/null +++ b/frontend/src/pages/Deploy/Environment/List/components/EnvironmentModal.tsx @@ -0,0 +1,118 @@ +import React, { useEffect } from 'react'; +import { Modal, Form, Input, InputNumber, message } from 'antd'; +import type { Environment } from '../types'; +import { createEnvironment, updateEnvironment } from '../service'; + +interface EnvironmentModalProps { + visible: boolean; + onCancel: () => void; + onSuccess: () => void; + initialValues?: Environment; + projectId: number; + tenantCode: string; +} + +const EnvironmentModal: React.FC = ({ + visible, + onCancel, + onSuccess, + initialValues, + projectId, + tenantCode, +}) => { + const [form] = Form.useForm(); + const isEdit = !!initialValues; + + useEffect(() => { + if (visible) { + if (initialValues) { + form.setFieldsValue(initialValues); + } else { + form.setFieldsValue({ projectId, tenantCode, sort: 0 }); + } + } + }, [visible, initialValues, form, projectId, tenantCode]); + + const handleSubmit = async () => { + try { + const values = await form.validateFields(); + if (isEdit) { + await updateEnvironment({ ...values, id: initialValues.id }); + message.success('更新成功'); + } else { + await createEnvironment(values); + message.success('创建成功'); + } + onSuccess(); + onCancel(); + form.resetFields(); + } catch (error) { + message.error(isEdit ? '更新失败' : '创建失败'); + } + }; + + const handleCancel = () => { + form.resetFields(); + onCancel(); + }; + + return ( + +
+ + + + + + + + + + + + + + + + + + + +
+
+ ); +}; + +export default EnvironmentModal; diff --git a/frontend/src/pages/Deploy/Environment/List/index.tsx b/frontend/src/pages/Deploy/Environment/List/index.tsx new file mode 100644 index 00000000..d21857ef --- /dev/null +++ b/frontend/src/pages/Deploy/Environment/List/index.tsx @@ -0,0 +1,317 @@ +import React, { useState } from 'react'; +import { PageContainer } from '@ant-design/pro-layout'; +import { Card, Row, Col, Typography, Button, message, Popconfirm, Tag, Space, Progress } from 'antd'; +import { + PlusOutlined, + EditOutlined, + DeleteOutlined, + CloudServerOutlined, + DatabaseOutlined, + ClusterOutlined, + DesktopOutlined +} from '@ant-design/icons'; +import { getEnvironmentList, deleteEnvironment } from './service'; +import type { Environment } from './types'; +import EnvironmentModal from './components/EnvironmentModal'; + +const { Title, Text } = Typography; + +const EnvironmentList: React.FC = () => { + const [environments, setEnvironments] = useState([]); + const [loading, setLoading] = useState(false); + const [modalVisible, setModalVisible] = useState(false); + const [currentEnvironment, setCurrentEnvironment] = useState(); + + // TODO: 这里需要从上下文或者URL参数获取projectId + const projectId = 1; + + const fetchEnvironments = async () => { + setLoading(true); + try { + const data = await getEnvironmentList(); + setEnvironments(data); + } catch (error) { + message.error('获取环境列表失败'); + } finally { + setLoading(false); + } + }; + + React.useEffect(() => { + fetchEnvironments(); + }, []); + + const handleDelete = async (id: number) => { + try { + await deleteEnvironment(id); + message.success('删除成功'); + fetchEnvironments(); + } catch (error) { + message.error('删除失败'); + } + }; + + const handleAdd = () => { + setCurrentEnvironment(undefined); + setModalVisible(true); + }; + + const handleEdit = (environment: Environment) => { + setCurrentEnvironment(environment); + setModalVisible(true); + }; + + // 根据环境编码获取环境信息 + const getEnvInfo = (envCode: string) => { + const envTypes = { + dev: { + icon: , + name: '开发环境', + color: '#1890ff', + usage: 45 + }, + test: { + icon: , + name: '测试环境', + color: '#52c41a', + usage: 65 + }, + staging: { + icon: , + name: '预发环境', + color: '#faad14', + usage: 85 + }, + prod: { + icon: , + name: '生产环境', + color: '#f5222d', + usage: 92 + } + }; + + const envType = Object.entries(envTypes).find(([key]) => + envCode.toLowerCase().includes(key) + ); + + return envType ? envType[1] : { + icon: , + name: '其他环境', + color: '#8c8c8c', + usage: 50 + }; + }; + + const EnvironmentCard = ({ environment }: { environment: Environment }) => { + const envInfo = getEnvInfo(environment.envCode); + + return ( + } + onClick={() => handleEdit(environment)} + > + 编辑 + , + handleDelete(environment.id)} + > + + , + ]} + > +
+ + {envInfo.icon} + {envInfo.name} + + + + {environment.envName} + <Tag + color={environment.envStatus === 'ENABLE' ? '#52c41a' : '#d9d9d9'} + style={{ + borderRadius: '4px', + fontSize: '12px', + padding: '0 8px', + }} + > + {environment.envStatus === 'ENABLE' ? '启用' : '禁用'} + </Tag> + + + + {environment.envDesc || '暂无描述'} + + +
+
+ 环境编码 +
+ {environment.envCode} +
+
+ +
+
+ 资源使用率 + {envInfo.usage}% +
+ +
+
+
+
+ ); + }; + + return ( + } + onClick={handleAdd} + style={{ + borderRadius: '6px', + boxShadow: '0 2px 0 rgba(0,0,0,0.045)', + }} + > + 新建环境 + + ], + }} + > +
+ + {environments.map(environment => ( + + + + ))} + + +
+ +
新建环境
+
+
+ +
+
+ + setModalVisible(false)} + onSuccess={fetchEnvironments} + initialValues={currentEnvironment} + projectId={projectId} + /> +
+ ); +}; + +export default EnvironmentList; diff --git a/frontend/src/pages/Deploy/Environment/List/service.ts b/frontend/src/pages/Deploy/Environment/List/service.ts new file mode 100644 index 00000000..28ed4172 --- /dev/null +++ b/frontend/src/pages/Deploy/Environment/List/service.ts @@ -0,0 +1,33 @@ +import request from '@/utils/request'; +import type { CreateEnvironmentRequest, UpdateEnvironmentRequest, Environment, EnvironmentQuery } from './types'; +import type { Page } from '@/types/base'; + +const BASE_URL = '/api/v1/environments'; + +// 创建环境 +export const createEnvironment = (data: CreateEnvironmentRequest) => + request.post(BASE_URL, 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 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) => + 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 new file mode 100644 index 00000000..0595108d --- /dev/null +++ b/frontend/src/pages/Deploy/Environment/List/types.ts @@ -0,0 +1,31 @@ +import type { BaseQuery } from '@/types/base'; + +export interface Environment { + id: number; + tenantCode: string; + projectId: number; + envCode: string; + envName: string; + envDesc?: string; + sort: number; +} + +export interface CreateEnvironmentRequest { + tenantCode: string; + projectId: number; + envCode: string; + envName: string; + envDesc?: string; + sort: number; +} + +export interface UpdateEnvironmentRequest extends CreateEnvironmentRequest { + id: number; +} + +export interface EnvironmentQuery extends BaseQuery { + tenantCode?: string; + projectId?: number; + envCode?: string; + envName?: string; +} diff --git a/frontend/src/pages/Deploy/Project/List/components/ProjectModal.tsx b/frontend/src/pages/Deploy/Project/List/components/ProjectModal.tsx new file mode 100644 index 00000000..21d27aa6 --- /dev/null +++ b/frontend/src/pages/Deploy/Project/List/components/ProjectModal.tsx @@ -0,0 +1,113 @@ +import React, { useEffect } from 'react'; +import { Modal, Form, Input, InputNumber, Radio, message } from 'antd'; +import type { Project } from '../types'; +import { createProject, updateProject } from '../service'; + +interface ProjectModalProps { + visible: boolean; + onCancel: () => void; + onSuccess: () => void; + initialValues?: Project; +} + +const ProjectModal: React.FC = ({ + visible, + onCancel, + onSuccess, + initialValues, +}) => { + const [form] = Form.useForm(); + const isEdit = !!initialValues; + + useEffect(() => { + if (visible && initialValues) { + form.setFieldsValue(initialValues); + } + }, [visible, initialValues, form]); + + const handleSubmit = async () => { + try { + const values = await form.validateFields(); + if (isEdit) { + await updateProject({ ...values, id: initialValues.id }); + message.success('更新成功'); + } else { + await createProject(values); + message.success('创建成功'); + } + onSuccess(); + onCancel(); + form.resetFields(); + } catch (error) { + message.error(isEdit ? '更新失败' : '创建失败'); + } + }; + + const handleCancel = () => { + form.resetFields(); + onCancel(); + }; + + return ( + +
+ + + + + + + + + + + + + + + 启用 + 禁用 + + + + + + +
+
+ ); +}; + +export default ProjectModal; diff --git a/frontend/src/pages/Deploy/Project/List/index.tsx b/frontend/src/pages/Deploy/Project/List/index.tsx new file mode 100644 index 00000000..065155eb --- /dev/null +++ b/frontend/src/pages/Deploy/Project/List/index.tsx @@ -0,0 +1,217 @@ +import React, { useEffect, useState } from 'react'; +import { PageContainer } from '@ant-design/pro-layout'; +import { Card, Row, Col, Typography, Button, message, Popconfirm, Tag } from 'antd'; +import { PlusOutlined, TeamOutlined, AppstoreOutlined, EditOutlined, DeleteOutlined } from '@ant-design/icons'; +import { getProjectList, deleteProject } from './service'; +import type { Project } from './types'; +import ProjectModal from './components/ProjectModal'; + +const { Title, Text } = Typography; + +const ProjectList: React.FC = () => { + const [projects, setProjects] = useState([]); + const [loading, setLoading] = useState(false); + const [modalVisible, setModalVisible] = useState(false); + const [currentProject, setCurrentProject] = useState(); + + const fetchProjects = async () => { + setLoading(true); + try { + const data = await getProjectList(); + setProjects(data); + } catch (error) { + message.error('获取项目列表失败'); + } finally { + setLoading(false); + } + }; + + useEffect(() => { + fetchProjects(); + }, []); + + const handleDelete = async (id: number) => { + try { + await deleteProject(id); + message.success('删除成功'); + fetchProjects(); + } catch (error) { + message.error('删除失败'); + } + }; + + const handleAdd = () => { + setCurrentProject(undefined); + setModalVisible(true); + }; + + const handleEdit = (project: Project) => { + setCurrentProject(project); + setModalVisible(true); + }; + + const ProjectCard = ({ project }: { project: Project }) => ( + } + onClick={() => handleEdit(project)} + > + 编辑 + , + handleDelete(project.id)} + > + + , + ]} + > +
+ + {project.projectName} + <Tag + color={project.projectStatus === 'ENABLE' ? '#52c41a' : '#d9d9d9'} + style={{ + marginLeft: '8px', + borderRadius: '4px', + fontSize: '12px', + padding: '0 8px', + }} + > + {project.projectStatus === 'ENABLE' ? '启用' : '禁用'} + </Tag> + + + {project.projectDesc || '暂无描述'} + +
+
+ 项目编码 +
+ {project.projectCode} +
+
+
+ 排序 +
+ {project.sort} +
+
+
+
+
+ ); + + return ( + } + onClick={handleAdd} + style={{ + borderRadius: '6px', + boxShadow: '0 2px 0 rgba(0,0,0,0.045)', + }} + > + 新建项目 + + ], + }} + > +
+ + {projects.map(project => ( + + + + ))} + + +
+ +
新建项目
+
+
+ +
+
+
+ ); +}; + +export default ProjectList; diff --git a/frontend/src/pages/Deploy/Project/List/service.ts b/frontend/src/pages/Deploy/Project/List/service.ts new file mode 100644 index 00000000..fbf1020f --- /dev/null +++ b/frontend/src/pages/Deploy/Project/List/service.ts @@ -0,0 +1,41 @@ +import request from '@/utils/request'; +import type { CreateProjectRequest, UpdateProjectRequest, Project, ProjectQueryParams } from './types'; +import type { Page } from '@/types/base'; + +const BASE_URL = '/api/v1/projects'; + +// 创建项目 +export const createProject = (data: CreateProjectRequest) => + request.post(BASE_URL, data); + +// 更新项目 +export const updateProject = (data: UpdateProjectRequest) => + request.put(`${BASE_URL}/${data.id}`, data); + +// 删除项目 +export const deleteProject = (id: number) => + request.delete(`${BASE_URL}/${id}`); + +// 获取项目详情 +export const getProject = (id: number) => + request.get(`${BASE_URL}/${id}`); + +// 分页查询项目列表 +export const getProjectPage = (params?: ProjectQueryParams) => + request.get>(`${BASE_URL}/page`, { params }); + +// 获取所有项目列表 +export const getProjectList = () => + request.get(BASE_URL); + +// 条件查询项目列表 +export const getProjectListByCondition = (params?: ProjectQueryParams) => + request.get(`${BASE_URL}/list`, { params }); + +// 批量处理项目 +export const batchProcessProject = (data: any) => + request.post(`${BASE_URL}/batch`, data); + +// 导出项目数据 +export const exportProject = () => + request.get(`${BASE_URL}/export`, { responseType: 'blob' }); diff --git a/frontend/src/pages/Deploy/Project/List/types.ts b/frontend/src/pages/Deploy/Project/List/types.ts new file mode 100644 index 00000000..33b6c5f7 --- /dev/null +++ b/frontend/src/pages/Deploy/Project/List/types.ts @@ -0,0 +1,33 @@ +import { BaseResponse, BaseRequest, BaseQuery } from '@/types/base'; + +// 项目基础信息 +export interface Project extends BaseResponse { + tenantId: number; + projectCode: string; + projectName: string; + projectDesc?: string; + projectStatus: string; + sort: number; +} + +// 创建项目请求参数 +export interface CreateProjectRequest extends BaseRequest { + tenantId: number; + projectCode: string; + projectName: string; + projectDesc?: string; + projectStatus: string; + sort: number; +} + +// 更新项目请求参数 +export interface UpdateProjectRequest extends CreateProjectRequest { + id: number; +} + +// 分页查询参数 +export interface ProjectQueryParams extends BaseQuery { + projectName?: string; + projectCode?: string; + projectStatus?: string; +} diff --git a/frontend/src/pages/Login/index.tsx b/frontend/src/pages/Login/index.tsx index f9f3ad61..a2f8f1d2 100644 --- a/frontend/src/pages/Login/index.tsx +++ b/frontend/src/pages/Login/index.tsx @@ -6,7 +6,6 @@ import {login} from './service'; import {getEnabledTenants} from '@/pages/System/Tenant/service'; import {setToken, setUserInfo, setMenus} from '../../store/userSlice'; import {getCurrentUserMenus} from '@/pages/System/Menu/service'; -import type { MenuResponse } from '@/pages/System/Menu/types'; import type { TenantResponse } from '@/pages/System/Tenant/types'; import styles from './index.module.css'; diff --git a/frontend/src/pages/System/Menu/service.ts b/frontend/src/pages/System/Menu/service.ts index eed74192..7f024d07 100644 --- a/frontend/src/pages/System/Menu/service.ts +++ b/frontend/src/pages/System/Menu/service.ts @@ -18,142 +18,142 @@ export const getCurrentUserMenus = async () => { const menus = await request.get(`${BASE_URL}/current`); console.log('Backend menus:', menus); - // 添加首页路由 - const dashboard: MenuResponse = { - id: 0, - createTime: new Date().toISOString(), - updateTime: new Date().toISOString(), - version: 0, - name: "首页", - path: "/dashboard", - component: "/Dashboard/index", - icon: "dashboard", - type: MenuTypeEnum.MENU, - parentId: 0, - sort: 0, - hidden: false, - enabled: true, - createBy: "system", - updateBy: "system" - }; - - // 添加工作流菜单 - const workflow: MenuResponse = { - id: -2, - createTime: new Date().toISOString(), - updateTime: new Date().toISOString(), - version: 0, - name: "工作流管理", - path: "/workflow", - icon: "apartment", - type: MenuTypeEnum.DIRECTORY, - parentId: 0, - sort: 2, - hidden: false, - enabled: true, - createBy: "system", - updateBy: "system", - children: [ - { - id: -21, - createTime: new Date().toISOString(), - updateTime: new Date().toISOString(), - version: 0, - name: "流程定义", - path: "/workflow/definition", - component: "/pages/Workflow/Definition/index", - icon: "apartment", - type: MenuTypeEnum.MENU, - parentId: -2, - sort: 0, - hidden: false, - enabled: true, - createBy: "system", - updateBy: "system" - }, - { - id: 1000, - createTime: new Date().toISOString(), - updateTime: new Date().toISOString(), - version: 0, - name: "流程实例", - path: "/workflow/instance", - component: "/Workflow/Instance/index", - icon: "instance", - type: MenuTypeEnum.DIRECTORY, - parentId: -2, - sort: 1, - hidden: false, - enabled: true, - createBy: "system", - updateBy: "system", - children: [ - { - id: 1001, - createTime: new Date().toISOString(), - updateTime: new Date().toISOString(), - version: 0, - name: "工作流实例", - path: "/workflow/instance", - component: "/Workflow/Instance/index", - icon: "instance", - type: MenuTypeEnum.MENU, - parentId: 1000, - sort: 1, - hidden: false - }, - { - id: 1002, - createTime: new Date().toISOString(), - updateTime: new Date().toISOString(), - version: 0, - name: "节点设计", - path: "/workflow/node-design", - component: "/Workflow/NodeDesign/index", - icon: "node-design", - type: MenuTypeEnum.MENU, - parentId: 1000, - sort: 2, - hidden: false - } - ] - }, - { - id: -23, - createTime: new Date().toISOString(), - updateTime: new Date().toISOString(), - version: 0, - name: "流程监控", - path: "/workflow/monitor", - component: "/pages/Workflow/Monitor/index", - icon: "dashboard", - type: MenuTypeEnum.MENU, - parentId: -2, - sort: 2, - hidden: false, - enabled: true, - createBy: "system", - updateBy: "system" - }, - { - id: -24, - createTime: new Date().toISOString(), - updateTime: new Date().toISOString(), - version: 0, - name: "日志流", - path: "/workflow/log-stream/:processInstanceId", - component: "/pages/LogStream/index", - icon: "file-text", - type: MenuTypeEnum.MENU, - parentId: -2, - sort: 3, - hidden: false, - enabled: true, - createBy: "system", - updateBy: "system" - } - ] - }; + // // 添加首页路由 + // const dashboard: MenuResponse = { + // id: 0, + // createTime: new Date().toISOString(), + // updateTime: new Date().toISOString(), + // version: 0, + // name: "首页", + // path: "/dashboard", + // component: "/Dashboard/index", + // icon: "dashboard", + // type: MenuTypeEnum.MENU, + // parentId: 0, + // sort: 0, + // hidden: false, + // enabled: true, + // createBy: "system", + // updateBy: "system" + // }; + // + // // 添加工作流菜单 + // const workflow: MenuResponse = { + // id: -2, + // createTime: new Date().toISOString(), + // updateTime: new Date().toISOString(), + // version: 0, + // name: "工作流管理", + // path: "/workflow", + // icon: "apartment", + // type: MenuTypeEnum.DIRECTORY, + // parentId: 0, + // sort: 2, + // hidden: false, + // enabled: true, + // createBy: "system", + // updateBy: "system", + // children: [ + // { + // id: -21, + // createTime: new Date().toISOString(), + // updateTime: new Date().toISOString(), + // version: 0, + // name: "流程定义", + // path: "/workflow/definition", + // component: "/pages/Workflow/Definition/index", + // icon: "apartment", + // type: MenuTypeEnum.MENU, + // parentId: -2, + // sort: 0, + // hidden: false, + // enabled: true, + // createBy: "system", + // updateBy: "system" + // }, + // { + // id: 1000, + // createTime: new Date().toISOString(), + // updateTime: new Date().toISOString(), + // version: 0, + // name: "流程实例", + // path: "/workflow/instance", + // component: "/Workflow/Instance/index", + // icon: "instance", + // type: MenuTypeEnum.DIRECTORY, + // parentId: -2, + // sort: 1, + // hidden: false, + // enabled: true, + // createBy: "system", + // updateBy: "system", + // children: [ + // { + // id: 1001, + // createTime: new Date().toISOString(), + // updateTime: new Date().toISOString(), + // version: 0, + // name: "工作流实例", + // path: "/workflow/instance", + // component: "/Workflow/Instance/index", + // icon: "instance", + // type: MenuTypeEnum.MENU, + // parentId: 1000, + // sort: 1, + // hidden: false + // }, + // { + // id: 1002, + // createTime: new Date().toISOString(), + // updateTime: new Date().toISOString(), + // version: 0, + // name: "节点设计", + // path: "/workflow/node-design", + // component: "/Workflow/NodeDesign/index", + // icon: "node-design", + // type: MenuTypeEnum.MENU, + // parentId: 1000, + // sort: 2, + // hidden: false + // } + // ] + // }, + // { + // id: -23, + // createTime: new Date().toISOString(), + // updateTime: new Date().toISOString(), + // version: 0, + // name: "流程监控", + // path: "/workflow/monitor", + // component: "/pages/Workflow/Monitor/index", + // icon: "dashboard", + // type: MenuTypeEnum.MENU, + // parentId: -2, + // sort: 2, + // hidden: false, + // enabled: true, + // createBy: "system", + // updateBy: "system" + // }, + // { + // id: -24, + // createTime: new Date().toISOString(), + // updateTime: new Date().toISOString(), + // version: 0, + // name: "日志流", + // path: "/workflow/log-stream/:processInstanceId", + // component: "/pages/LogStream/index", + // icon: "file-text", + // type: MenuTypeEnum.MENU, + // parentId: -2, + // sort: 3, + // hidden: false, + // enabled: true, + // createBy: "system", + // updateBy: "system" + // } + // ] + // }; // 添加X6测试菜单 // const x6Test: MenuResponse = { @@ -192,7 +192,7 @@ export const getCurrentUserMenus = async () => { }; const processedMenus = menus.map(processMenu); - const allMenus = [dashboard, workflow, ...processedMenus]; + const allMenus = [ ...processedMenus]; console.log('All menus:', allMenus); return allMenus; }; diff --git a/frontend/src/router/index.tsx b/frontend/src/router/index.tsx index 3a805c63..fb343e4f 100644 --- a/frontend/src/router/index.tsx +++ b/frontend/src/router/index.tsx @@ -1,24 +1,24 @@ -import {createBrowserRouter, Navigate} from 'react-router-dom'; -import {lazy, Suspense} from 'react'; -import {Spin} from 'antd'; +import { createBrowserRouter, Navigate } from 'react-router-dom'; +import { lazy, Suspense } from 'react'; +import { Spin } from 'antd'; import Login from '../pages/Login'; import BasicLayout from '../layouts/BasicLayout'; -import {useSelector} from 'react-redux'; -import {RootState} from '../store'; +import { useSelector } from 'react-redux'; +import { RootState } from '../store'; // 加中组件 const LoadingComponent = () => ( -
- +
+
); // 路由守卫 -const PrivateRoute = ({children}: { children: React.ReactNode }) => { +const PrivateRoute = ({ children }: { children: React.ReactNode }) => { const token = useSelector((state: RootState) => state.user.token); if (!token) { - return ; + return ; } return <>{children}; @@ -39,24 +39,27 @@ const WorkflowMonitor = lazy(() => import('../pages/Workflow/Monitor')); const LogStreamPage = lazy(() => import('../pages/LogStream')); const NodeDesign = lazy(() => import('../pages/Workflow/NodeDesign')); const NodeDesignForm = lazy(() => import('../pages/Workflow/NodeDesign/Design')); +const ProjectList = lazy(() => import('../pages/Deploy/Project/List')); +const ApplicationList = lazy(() => import('../pages/Deploy/Application/List')); +const EnvironmentList = lazy(() => import('../pages/Deploy/Environment/List')); // 创建路由 const router = createBrowserRouter([ { path: '/login', - element: + element: }, { path: '/', element: ( - + ), children: [ { path: '', - element: + element: }, { path: 'dashboard', @@ -66,6 +69,23 @@ const router = createBrowserRouter([ ) }, + { + path: 'deploy', + children: [ + { + path: 'project', + element: }> + }, + { + path: 'application', + element: }> + }, + { + path: 'environment', + element: }> + } + ] + }, { path: 'system', children: [