From 3b9a65bcc8384a433115f484252b25be35cf1431 Mon Sep 17 00:00:00 2001 From: dengqichen Date: Mon, 20 Jan 2025 13:33:18 +0800 Subject: [PATCH] 1 --- .../components/DeploymentConfigModal copy.tsx | 420 ++++++++++++++ .../List/components/DeploymentConfigModal.tsx | 532 +++++------------- .../pages/Deploy/Deployment/List/service.ts | 7 +- 3 files changed, 556 insertions(+), 403 deletions(-) create mode 100644 frontend/src/pages/Deploy/Deployment/List/components/DeploymentConfigModal copy.tsx diff --git a/frontend/src/pages/Deploy/Deployment/List/components/DeploymentConfigModal copy.tsx b/frontend/src/pages/Deploy/Deployment/List/components/DeploymentConfigModal copy.tsx new file mode 100644 index 00000000..f140317b --- /dev/null +++ b/frontend/src/pages/Deploy/Deployment/List/components/DeploymentConfigModal copy.tsx @@ -0,0 +1,420 @@ +import React, { useState, useEffect } from 'react'; +import {Modal, Button, message} from 'antd'; +import { FullscreenOutlined, FullscreenExitOutlined } from '@ant-design/icons'; +import type {DeploymentConfig} from '../types'; +import './styles.less'; +import {FormButtonGroup, FormItem, Select, Submit, FormGrid, Input, ArrayTable} from '@formily/antd-v5' +import {createForm, Field, FieldDataSource, onFormInit} from '@formily/core' +import {createSchemaField, FormProvider, ISchema} from '@formily/react' +import {action} from '@formily/reactive' +import request from '@/utils/request'; +import Editor from '@/components/Editor'; + +// 定义 ScriptEditor 组件 +const ScriptEditor: React.FC = (props) => { + const [isFullscreen, setIsFullscreen] = useState(false); + + useEffect(() => { + const handleEscKey = (event: KeyboardEvent) => { + if (event.key === 'Escape' && isFullscreen) { + setIsFullscreen(false); + } + }; + + document.addEventListener('keydown', handleEscKey); + return () => { + document.removeEventListener('keydown', handleEscKey); + }; + }, [isFullscreen]); + + const toggleFullscreen = () => { + setIsFullscreen(!isFullscreen); + }; + + const editorStyle = isFullscreen ? { + position: 'fixed', + top: 0, + left: 0, + right: 0, + bottom: 0, + zIndex: 1000, + backgroundColor: '#1e1e1e' + } : {}; + + return ( +
+ +
+ +
+
+ ); +}; + +interface DeploymentConfigModalProps { + open: boolean; + onCancel: () => void; + onSuccess: () => void; + initialValues?: DeploymentConfig; + envId: number; +} + +// 定义字段映射接口 +interface FieldMapping { + label?: string; + value?: string; +} + +const DeploymentConfigModal: React.FC = ({ + open, + onCancel, + onSuccess, + initialValues, + envId, + }) => { + + // 通用的异步数据源加载方法 + const useAsyncDataSource = (url: string | null, mapping: FieldMapping = {}) => (field: Field) => { + if (!url) { + field.dataSource = []; + return; + } + + const { label = 'name', value = 'id' } = mapping; + field.loading = true; + request.get(url) + .then(action.bound?.((response) => { + field.dataSource = response.map((item: any) => ({ + label: item[label], + value: item[value] + })); + field.loading = false; + })) + .catch(action.bound?.((error) => { + console.error(`Failed to load data from ${url}:`, error); + field.dataSource = []; + field.loading = false; + })); + }; + + const SchemaField = createSchemaField({ + components: { + Select, + FormItem, + FormGrid, + Input, + ArrayTable, + ScriptEditor + }, + scope: { + useAsyncDataSource + } + }) + + // 创建表单实例 + const form = createForm() + + const schema: ISchema = { + type: 'object', + properties: { + jenkinsConfig: { + type: 'void', + 'x-component': 'FormGrid', + 'x-component-props': { + maxColumns: 3, + minColumns: 3, + columnGap: 24 + }, + properties: { + externalSystemId: { + type: 'string', + title: 'Jenkins系统', + required: true, + 'x-decorator': 'FormItem', + 'x-decorator-props': { + labelCol: 24, + wrapperCol: 24, + layout: 'vertical', + colon: false, + labelAlign: 'left' + }, + 'x-component': 'Select', + 'x-component-props': { + style: { + width: '100%' + }, + placeholder: '请选择三方系统', + allowClear: true + }, + 'x-reactions': ["{{useAsyncDataSource('/api/v1/external-system/list?type=JENKINS', { label: 'name' })}}"], + }, + viewId: { + type: 'string', + title: 'Jenkins视图', + required: true, + 'x-decorator': 'FormItem', + 'x-decorator-props': { + labelCol: 24, + wrapperCol: 24, + layout: 'vertical', + colon: false, + labelAlign: 'left' + }, + 'x-component': 'Select', + 'x-component-props': { + style: { + width: '100%' + }, + placeholder: '请选择Jenkins视图', + disabled: '{{!$form.values.externalSystemId}}', + allowClear: true + }, + 'x-reactions': { + dependencies: ['externalSystemId'], + fulfill: { + state: { + value: undefined + }, + run: '{{useAsyncDataSource($deps[0] ? `/api/v1/jenkins-view/list?externalSystemId=${$deps[0]}` : null, { label: "viewName" })($self)}}' + } + } + }, + jobId: { + type: 'string', + title: 'Jenkins作业', + required: true, + 'x-decorator': 'FormItem', + 'x-decorator-props': { + labelCol: 24, + wrapperCol: 24, + layout: 'vertical', + colon: false, + labelAlign: 'left' + }, + 'x-component': 'Select', + 'x-component-props': { + style: { + width: '100%' + }, + placeholder: '请选择Jenkins作业', + disabled: '{{!$form.values.viewId}}', + allowClear: true + }, + 'x-reactions': { + dependencies: ['externalSystemId', 'viewId'], + fulfill: { + state: { + value: undefined + }, + run: '{{useAsyncDataSource(($deps[0] && $deps[1]) ? `/api/v1/jenkins-job/list?externalSystemId=${$deps[0]}&viewId=${$deps[1]}` : null, { label: "jobName" })($self)}}' + } + } + }, + } + }, + envConfig: { + type: 'void', + 'x-component': 'FormGrid', + 'x-component-props': { + maxColumns: 1, + minColumns: 1 + }, + properties: { + envs: { + type: 'array', + title: '环境变量', + 'x-decorator': 'FormItem', + 'x-decorator-props': { + labelCol: 24, + wrapperCol: 24, + layout: 'vertical', + colon: false, + labelAlign: 'left' + }, + 'x-component': 'ArrayTable', + 'x-component-props': { + pagination: { pageSize: 10 }, + scroll: { x: '100%' }, + style: { + minHeight: '160px', + maxHeight: '240px', + overflow: 'auto' + } + }, + items: { + type: 'object', + properties: { + key: { + type: 'void', + 'x-component': 'ArrayTable.Column', + 'x-component-props': { title: '键', width: '40%' }, + properties: { + key: { + type: 'string', + 'x-decorator': 'FormItem', + 'x-component': 'Input', + 'x-validator': [ + { + required: true, + message: '请输入环境变量名' + }, + { + pattern: '^[a-zA-Z][a-zA-Z0-9_]*$', + message: '只能包含字母、数字和下划线,且必须以字母开头' + } + ], + 'x-component-props': { + placeholder: '请输入环境变量名', + allowClear: true + } + } + } + }, + value: { + type: 'void', + 'x-component': 'ArrayTable.Column', + 'x-component-props': { title: '值', width: '40%' }, + properties: { + value: { + type: 'string', + 'x-decorator': 'FormItem', + 'x-component': 'Input', + 'x-validator': { + required: true, + message: '请输入环境变量值' + }, + 'x-component-props': { + placeholder: '请输入环境变量值', + allowClear: true + } + } + } + }, + column3: { + type: 'void', + 'x-component': 'ArrayTable.Column', + 'x-component-props': { + title: '操作', + dataIndex: 'operations', + width: '20%', + }, + properties: { + remove: { + type: 'void', + 'x-component': 'ArrayTable.Remove' + }, + moveUp: { + type: 'void', + 'x-component': 'ArrayTable.MoveUp' + }, + moveDown: { + type: 'void', + 'x-component': 'ArrayTable.MoveDown' + } + } + } + } + }, + properties: { + add: { + type: 'void', + title: '添加环境变量', + 'x-component': 'ArrayTable.Addition' + } + } + }, + script: { + type: 'string', + title: '脚本内容', + 'x-decorator': 'FormItem', + 'x-decorator-props': { + labelCol: 24, + wrapperCol: 24, + layout: 'vertical', + colon: false, + labelAlign: 'left', + style: { + marginBottom: '16px' + } + }, + 'x-component': 'ScriptEditor', + 'x-component-props': { + language: 'shell', + theme: 'vs-dark', + options: { + minimap: { + enabled: true, + scale: 2, + showSlider: "mouseover", + renderCharacters: false + }, + scrollBeyondLastLine: false, + fontSize: 14, + lineNumbers: 'on', + automaticLayout: true, + tabSize: 2 + } + } + } + } + } + } + }; + + const handleSubmit = async () => { + try { + const values = await form.submit() + console.log('表单提交的值:', values) + console.log('Schema:', schema) + console.log('FORMILY:', JSON.stringify(schema, null, 2)) + onSuccess?.() + form.reset() + } catch (e) { + console.error('表单提交出错:', e) + message.error('提交失败,请检查表单数据是否正确') + } + } + + return ( + + 取消 + , + + ]} + bodyStyle={{ + padding: '24px', + maxHeight: '80vh', + overflow: 'auto' + }} + > + + + + + ); +}; + +export default DeploymentConfigModal; \ No newline at end of file diff --git a/frontend/src/pages/Deploy/Deployment/List/components/DeploymentConfigModal.tsx b/frontend/src/pages/Deploy/Deployment/List/components/DeploymentConfigModal.tsx index f140317b..48fe3a34 100644 --- a/frontend/src/pages/Deploy/Deployment/List/components/DeploymentConfigModal.tsx +++ b/frontend/src/pages/Deploy/Deployment/List/components/DeploymentConfigModal.tsx @@ -1,419 +1,147 @@ -import React, { useState, useEffect } from 'react'; -import {Modal, Button, message} from 'antd'; -import { FullscreenOutlined, FullscreenExitOutlined } from '@ant-design/icons'; -import type {DeploymentConfig} from '../types'; -import './styles.less'; -import {FormButtonGroup, FormItem, Select, Submit, FormGrid, Input, ArrayTable} from '@formily/antd-v5' -import {createForm, Field, FieldDataSource, onFormInit} from '@formily/core' -import {createSchemaField, FormProvider, ISchema} from '@formily/react' -import {action} from '@formily/reactive' -import request from '@/utils/request'; -import Editor from '@/components/Editor'; +import React, {useEffect, useState} from 'react'; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogFooter, +} from "@/components/ui/dialog"; +import {Button} from "@/components/ui/button"; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import {useForm} from "react-hook-form"; +import {getApplicationList} from '../../../Application/List/service'; +import {getExternalSystemList} from '../service'; +import type {ExternalSystem} from '@/pages/Deploy/External/types'; -// 定义 ScriptEditor 组件 -const ScriptEditor: React.FC = (props) => { - const [isFullscreen, setIsFullscreen] = useState(false); - - useEffect(() => { - const handleEscKey = (event: KeyboardEvent) => { - if (event.key === 'Escape' && isFullscreen) { - setIsFullscreen(false); - } - }; - - document.addEventListener('keydown', handleEscKey); - return () => { - document.removeEventListener('keydown', handleEscKey); - }; - }, [isFullscreen]); - - const toggleFullscreen = () => { - setIsFullscreen(!isFullscreen); - }; - - const editorStyle = isFullscreen ? { - position: 'fixed', - top: 0, - left: 0, - right: 0, - bottom: 0, - zIndex: 1000, - backgroundColor: '#1e1e1e' - } : {}; - - return ( -
- -
- -
-
- ); -}; +interface Application { + id: number; + appName: string; +} interface DeploymentConfigModalProps { open: boolean; onCancel: () => void; onSuccess: () => void; - initialValues?: DeploymentConfig; - envId: number; } -// 定义字段映射接口 -interface FieldMapping { - label?: string; - value?: string; -} - -const DeploymentConfigModal: React.FC = ({ - open, - onCancel, - onSuccess, - initialValues, - envId, - }) => { - - // 通用的异步数据源加载方法 - const useAsyncDataSource = (url: string | null, mapping: FieldMapping = {}) => (field: Field) => { - if (!url) { - field.dataSource = []; - return; +const DeploymentConfigModal: React.FC = ({open, onCancel, onSuccess}) => { + const [applications, setApplications] = useState([]); + const [externalSystems, setExternalSystems] = useState([]); + const form = useForm({ + defaultValues: { + applicationId: undefined, + externalSystemId: undefined, } + }); - const { label = 'name', value = 'id' } = mapping; - field.loading = true; - request.get(url) - .then(action.bound?.((response) => { - field.dataSource = response.map((item: any) => ({ - label: item[label], - value: item[value] - })); - field.loading = false; - })) - .catch(action.bound?.((error) => { - console.error(`Failed to load data from ${url}:`, error); - field.dataSource = []; - field.loading = false; - })); - }; - - const SchemaField = createSchemaField({ - components: { - Select, - FormItem, - FormGrid, - Input, - ArrayTable, - ScriptEditor - }, - scope: { - useAsyncDataSource + useEffect(() => { + if (open) { + // 获取应用列表 + getApplicationList().then(data => { + setApplications(data); + }); + // 获取外部系统列表 + getExternalSystemList('JENKINS').then(data => { + setExternalSystems(data); + }); } - }) + }, [open]); - // 创建表单实例 - const form = createForm() - - const schema: ISchema = { - type: 'object', - properties: { - jenkinsConfig: { - type: 'void', - 'x-component': 'FormGrid', - 'x-component-props': { - maxColumns: 3, - minColumns: 3, - columnGap: 24 - }, - properties: { - externalSystemId: { - type: 'string', - title: 'Jenkins系统', - required: true, - 'x-decorator': 'FormItem', - 'x-decorator-props': { - labelCol: 24, - wrapperCol: 24, - layout: 'vertical', - colon: false, - labelAlign: 'left' - }, - 'x-component': 'Select', - 'x-component-props': { - style: { - width: '100%' - }, - placeholder: '请选择三方系统', - allowClear: true - }, - 'x-reactions': ["{{useAsyncDataSource('/api/v1/external-system/list?type=JENKINS', { label: 'name' })}}"], - }, - viewId: { - type: 'string', - title: 'Jenkins视图', - required: true, - 'x-decorator': 'FormItem', - 'x-decorator-props': { - labelCol: 24, - wrapperCol: 24, - layout: 'vertical', - colon: false, - labelAlign: 'left' - }, - 'x-component': 'Select', - 'x-component-props': { - style: { - width: '100%' - }, - placeholder: '请选择Jenkins视图', - disabled: '{{!$form.values.externalSystemId}}', - allowClear: true - }, - 'x-reactions': { - dependencies: ['externalSystemId'], - fulfill: { - state: { - value: undefined - }, - run: '{{useAsyncDataSource($deps[0] ? `/api/v1/jenkins-view/list?externalSystemId=${$deps[0]}` : null, { label: "viewName" })($self)}}' - } - } - }, - jobId: { - type: 'string', - title: 'Jenkins作业', - required: true, - 'x-decorator': 'FormItem', - 'x-decorator-props': { - labelCol: 24, - wrapperCol: 24, - layout: 'vertical', - colon: false, - labelAlign: 'left' - }, - 'x-component': 'Select', - 'x-component-props': { - style: { - width: '100%' - }, - placeholder: '请选择Jenkins作业', - disabled: '{{!$form.values.viewId}}', - allowClear: true - }, - 'x-reactions': { - dependencies: ['externalSystemId', 'viewId'], - fulfill: { - state: { - value: undefined - }, - run: '{{useAsyncDataSource(($deps[0] && $deps[1]) ? `/api/v1/jenkins-job/list?externalSystemId=${$deps[0]}&viewId=${$deps[1]}` : null, { label: "jobName" })($self)}}' - } - } - }, - } - }, - envConfig: { - type: 'void', - 'x-component': 'FormGrid', - 'x-component-props': { - maxColumns: 1, - minColumns: 1 - }, - properties: { - envs: { - type: 'array', - title: '环境变量', - 'x-decorator': 'FormItem', - 'x-decorator-props': { - labelCol: 24, - wrapperCol: 24, - layout: 'vertical', - colon: false, - labelAlign: 'left' - }, - 'x-component': 'ArrayTable', - 'x-component-props': { - pagination: { pageSize: 10 }, - scroll: { x: '100%' }, - style: { - minHeight: '160px', - maxHeight: '240px', - overflow: 'auto' - } - }, - items: { - type: 'object', - properties: { - key: { - type: 'void', - 'x-component': 'ArrayTable.Column', - 'x-component-props': { title: '键', width: '40%' }, - properties: { - key: { - type: 'string', - 'x-decorator': 'FormItem', - 'x-component': 'Input', - 'x-validator': [ - { - required: true, - message: '请输入环境变量名' - }, - { - pattern: '^[a-zA-Z][a-zA-Z0-9_]*$', - message: '只能包含字母、数字和下划线,且必须以字母开头' - } - ], - 'x-component-props': { - placeholder: '请输入环境变量名', - allowClear: true - } - } - } - }, - value: { - type: 'void', - 'x-component': 'ArrayTable.Column', - 'x-component-props': { title: '值', width: '40%' }, - properties: { - value: { - type: 'string', - 'x-decorator': 'FormItem', - 'x-component': 'Input', - 'x-validator': { - required: true, - message: '请输入环境变量值' - }, - 'x-component-props': { - placeholder: '请输入环境变量值', - allowClear: true - } - } - } - }, - column3: { - type: 'void', - 'x-component': 'ArrayTable.Column', - 'x-component-props': { - title: '操作', - dataIndex: 'operations', - width: '20%', - }, - properties: { - remove: { - type: 'void', - 'x-component': 'ArrayTable.Remove' - }, - moveUp: { - type: 'void', - 'x-component': 'ArrayTable.MoveUp' - }, - moveDown: { - type: 'void', - 'x-component': 'ArrayTable.MoveDown' - } - } - } - } - }, - properties: { - add: { - type: 'void', - title: '添加环境变量', - 'x-component': 'ArrayTable.Addition' - } - } - }, - script: { - type: 'string', - title: '脚本内容', - 'x-decorator': 'FormItem', - 'x-decorator-props': { - labelCol: 24, - wrapperCol: 24, - layout: 'vertical', - colon: false, - labelAlign: 'left', - style: { - marginBottom: '16px' - } - }, - 'x-component': 'ScriptEditor', - 'x-component-props': { - language: 'shell', - theme: 'vs-dark', - options: { - minimap: { - enabled: true, - scale: 2, - showSlider: "mouseover", - renderCharacters: false - }, - scrollBeyondLastLine: false, - fontSize: 14, - lineNumbers: 'on', - automaticLayout: true, - tabSize: 2 - } - } - } - } - } - } - }; - - const handleSubmit = async () => { - try { - const values = await form.submit() - console.log('表单提交的值:', values) - console.log('Schema:', schema) - console.log('FORMILY:', JSON.stringify(schema, null, 2)) - onSuccess?.() - form.reset() - } catch (e) { - console.error('表单提交出错:', e) - message.error('提交失败,请检查表单数据是否正确') - } - } + const handleSubmit = form.handleSubmit((values) => { + console.log('表单提交的值:', values); + onSuccess?.(); + }); return ( - - 取消 - , - - ]} - bodyStyle={{ - padding: '24px', - maxHeight: '80vh', - overflow: 'auto' - }} - > - - - - + !open && onCancel()}> + + + 部署配置 + +
+ +
+ ( + + 应用选择 + + + + )} + /> + ( + + 三方系统 + + + + )} + /> +
+
+ + + + + +
+
); }; diff --git a/frontend/src/pages/Deploy/Deployment/List/service.ts b/frontend/src/pages/Deploy/Deployment/List/service.ts index 79dffcbf..b6139d20 100644 --- a/frontend/src/pages/Deploy/Deployment/List/service.ts +++ b/frontend/src/pages/Deploy/Deployment/List/service.ts @@ -1,6 +1,7 @@ import request from '@/utils/request'; import type {DeploymentConfig, CreateDeploymentConfigRequest, UpdateDeploymentConfigRequest, DeploymentConfigQueryParams, DeployConfigTemplate} from './types'; import type {Page} from '@/types/base'; +import type {ExternalSystem} from '@/pages/Deploy/External/types'; const BASE_URL = '/api/v1/deploy-app-config'; @@ -30,4 +31,8 @@ export const getDeploymentConfigsByEnv = (environmentId: number) => // 获取部署配置模板列表 export const getDeployConfigTemplates = () => - request.get(`${BASE_URL}/defined`); \ No newline at end of file + request.get(`${BASE_URL}/defined`); + +// 获取外部系统列表 +export const getExternalSystemList = (type: string) => + request.get('/api/v1/external-system/list', {params: {type}}); \ No newline at end of file