This commit is contained in:
dengqichen 2024-12-27 14:04:57 +08:00
parent fe6de5720a
commit f8a9243ecd
3 changed files with 100 additions and 89 deletions

View File

@ -73,15 +73,21 @@ const DeploymentConfigModal: React.FC<DeploymentConfigModalProps> = ({
if (!open) return; if (!open) return;
if (initialValues) { if (initialValues) {
// 设置基础字段
form.setFieldsValue({ form.setFieldsValue({
applicationId: initialValues.applicationId, applicationId: initialValues.applicationId,
enabled: initialValues.enabled enabled: initialValues.enabled,
...initialValues.buildVariables // 设置所有构建变量到表单中
}); });
const template = templates.find(t => t.buildType === initialValues.buildType && t.languageType === initialValues.languageType); const template = templates.find(t => t.buildType === initialValues.buildType && t.languageType === initialValues.languageType);
if (template) { if (template) {
setSelectedTemplate(template); setSelectedTemplate(template);
setBuildVariables(initialValues.buildVariables || {}); // 只保存编辑器字段的值到 buildVariables
const editorValues = Object.entries(initialValues.buildVariables || {})
.filter(([key]) => template.buildVariablesSchema?.properties?.[key]?.editorConfig)
.reduce((acc, [key, value]) => ({...acc, [key]: value}), {});
setBuildVariables(editorValues);
} }
} else { } else {
form.resetFields(); form.resetFields();
@ -100,17 +106,30 @@ const DeploymentConfigModal: React.FC<DeploymentConfigModalProps> = ({
if (template) { if (template) {
setSelectedTemplate(template); setSelectedTemplate(template);
setBuildVariables({}); setBuildVariables({});
// 重置除了 applicationId 和 enabled 之外的所有字段
const currentValues = form.getFieldsValue();
form.setFieldsValue({
applicationId: currentValues.applicationId,
enabled: currentValues.enabled
});
} }
} }
}, [applications, templates]); }, [applications, templates, form]);
const handleFormValuesChange = (changedValues: Record<string, any>) => {
setBuildVariables(prev => ({
...prev,
...changedValues
}));
};
const handleSubmit = async () => { const handleSubmit = async () => {
try { try {
setLoading(true); setLoading(true);
const values = await form.validateFields(); const formValues = await form.validateFields();
// 获取选中的应用信息 // 获取选中的应用信息
const selectedApp = applications.find(app => app.id === values.applicationId); const selectedApp = applications.find(app => app.id === formValues.applicationId);
if (!selectedApp) { if (!selectedApp) {
throw new Error('请选择应用'); throw new Error('请选择应用');
} }
@ -121,14 +140,20 @@ const DeploymentConfigModal: React.FC<DeploymentConfigModalProps> = ({
throw new Error('未找到匹配配置模板'); throw new Error('未找到匹配配置模板');
} }
// 从 formValues 中提取构建变量(排除 applicationId 和 enabled
const { applicationId, enabled, ...formBuildVariables } = formValues;
// 构建提交数据 // 构建提交数据
const submitData: CreateDeploymentConfigRequest = { const submitData: CreateDeploymentConfigRequest = {
environmentId: envId, environmentId: envId,
applicationId: values.applicationId, applicationId,
buildType: template.buildType, buildType: template.buildType,
languageType: selectedApp.language, languageType: selectedApp.language,
buildVariables, buildVariables: {
enabled: values.enabled ...formBuildVariables, // 表单中的值
...buildVariables // 编辑器中的值
} as Record<string, any>, // 使用类型断言确保类型兼容
enabled
}; };
if (isEdit) { if (isEdit) {
@ -217,34 +242,57 @@ const DeploymentConfigModal: React.FC<DeploymentConfigModalProps> = ({
{/* 动态构建配置 */} {/* 动态构建配置 */}
{selectedTemplate?.buildVariablesSchema?.properties && ( {selectedTemplate?.buildVariablesSchema?.properties && (
<> <>
{/* 按照原始顺序渲染所有字段 */} {/* 使用 BetaSchemaForm 渲染普通字段 */}
{Object.entries(selectedTemplate.buildVariablesSchema.properties).map(([key, property]) => { <BetaSchemaForm
// 如果是编辑器字段 layoutType="Embed"
if (property.editorConfig) { columns={convertJsonSchemaToColumns({
type: 'object',
properties: Object.fromEntries(
Object.entries(selectedTemplate.buildVariablesSchema.properties)
.filter(([_, prop]) => !prop.editorConfig)
.map(([key, prop]) => [key, {
...prop,
type: prop.type || 'string' // 确保有 type 字段
}])
),
required: selectedTemplate.buildVariablesSchema.required || []
})}
initialValues={buildVariables}
onValuesChange={(_, values) => {
setBuildVariables(prev => ({
...prev,
...values
}));
}}
/>
{/* 渲染编辑器字段 */}
{Object.entries(selectedTemplate.buildVariablesSchema.properties)
.filter(([_, prop]) => prop.editorConfig)
.map(([key, property]) => {
const isFullscreen = fullscreenEditor?.key === key; const isFullscreen = fullscreenEditor?.key === key;
const editorConfig = property.editorConfig;
const editor = ( const editor = (
<Editor <Editor
height={isFullscreen ? "calc(100vh - 55px)" : "300px"} height={isFullscreen ? "calc(100vh - 55px)" : "300px"}
defaultLanguage={editorConfig.language || 'shell'} defaultLanguage={property.editorConfig.language || 'shell'}
theme={editorConfig.theme || 'vs-dark'} theme={property.editorConfig.theme || 'vs-dark'}
value={buildVariables[key] || property.default || ''} value={buildVariables[key] || property.default || ''}
onChange={(value) => handleEditorChange(key, value)} onChange={(value) => handleEditorChange(key, value)}
options={{ options={{
minimap: { enabled: isFullscreen ? true : (editorConfig.minimap ?? false) }, minimap: { enabled: isFullscreen ? true : (property.editorConfig.minimap ?? false) },
fontSize: editorConfig.fontSize || 14, fontSize: property.editorConfig.fontSize || 14,
lineNumbers: editorConfig.lineNumbers ? 'on' : 'off', lineNumbers: property.editorConfig.lineNumbers ? 'on' : 'off',
wordWrap: editorConfig.wordWrap ? 'on' : 'off', wordWrap: property.editorConfig.wordWrap ? 'on' : 'off',
tabSize: editorConfig.tabSize || 2, tabSize: property.editorConfig.tabSize || 2,
scrollBeyondLastLine: false, scrollBeyondLastLine: false,
automaticLayout: true, automaticLayout: true,
folding: editorConfig.folding ?? true, folding: property.editorConfig.folding ?? true,
autoClosingBrackets: 'always', autoClosingBrackets: 'always',
autoClosingQuotes: 'always', autoClosingQuotes: 'always',
formatOnPaste: true, formatOnPaste: true,
formatOnType: true, formatOnType: true,
suggestOnTriggerCharacters: editorConfig.autoComplete ?? true, suggestOnTriggerCharacters: property.editorConfig.autoComplete ?? true,
renderWhitespace: 'boundary', renderWhitespace: 'boundary',
}} }}
/> />
@ -298,48 +346,7 @@ const DeploymentConfigModal: React.FC<DeploymentConfigModalProps> = ({
{editor} {editor}
</Form.Item> </Form.Item>
); );
} })}
// 如果是普通字段,使用 BetaSchemaForm
return (
<BetaSchemaForm
key={key}
layoutType="Embed"
columns={convertJsonSchemaToColumns({
type: 'object',
properties: {
[key]: {
type: property.type || 'string',
title: property.title,
description: property.description,
default: property.default,
enum: property.enum,
enumNames: property.enumNames,
format: property.format,
minimum: property.minimum,
maximum: property.maximum,
minLength: property.minLength,
maxLength: property.maxLength,
pattern: property.pattern,
items: property.items && {
type: property.items.type || 'string',
enum: property.items.enum,
enumNames: property.items.enumNames
}
}
},
required: selectedTemplate.buildVariablesSchema.required?.includes(key) ? [key] : []
})}
initialValues={{
[key]: buildVariables[key]
}}
onValuesChange={(_, values) => setBuildVariables(prev => ({
...prev,
...values
}))}
/>
);
})}
</> </>
)} )}

View File

@ -20,7 +20,7 @@ const CodePreviewModal: React.FC<{
language?: string; language?: string;
}> = ({open, onCancel, code, language = 'shell'}) => { }> = ({open, onCancel, code, language = 'shell'}) => {
const [isFullscreen, setIsFullscreen] = useState(false); const [isFullscreen, setIsFullscreen] = useState(false);
// 当模态<E6A8A1><E68081>打开或关闭时重置全屏状态 // 当模态<E6A8A1><E68081>打开或关闭时重置全屏状态
useEffect(() => { useEffect(() => {
setIsFullscreen(false); setIsFullscreen(false);
@ -33,7 +33,7 @@ const CodePreviewModal: React.FC<{
<Button <Button
type="text" type="text"
icon={<FullscreenOutlined />} icon={<FullscreenOutlined/>}
onClick={() => setIsFullscreen(!isFullscreen)} onClick={() => setIsFullscreen(!isFullscreen)}
/> />
</Space> </Space>
@ -51,17 +51,17 @@ const CodePreviewModal: React.FC<{
} : undefined} } : undefined}
wrapClassName={isFullscreen ? 'fullscreen-modal' : undefined} wrapClassName={isFullscreen ? 'fullscreen-modal' : undefined}
styles={{ styles={{
body: { padding: 0 } body: {padding: 0}
}} }}
destroyOnClose destroyOnClose
> >
<Editor <Editor
height={isFullscreen ? "calc(100vh - 55px)" : "500px"} height={isFullscreen ? "calc(100vh - 100px)" : "500px"}
defaultLanguage={language} defaultLanguage={language}
value={code} value={code}
options={{ options={{
readOnly: true, readOnly: true,
minimap: { enabled: true }, minimap: {enabled: true},
scrollBeyondLastLine: false, scrollBeyondLastLine: false,
automaticLayout: true, automaticLayout: true,
contextmenu: true, contextmenu: true,
@ -78,7 +78,7 @@ const DeploymentConfigList: React.FC = () => {
const [currentConfig, setCurrentConfig] = useState<DeploymentConfig>(); const [currentConfig, setCurrentConfig] = useState<DeploymentConfig>();
const [templates, setTemplates] = useState<DeployConfigTemplate[]>([]); const [templates, setTemplates] = useState<DeployConfigTemplate[]>([]);
const [codePreviewVisible, setCodePreviewVisible] = useState(false); const [codePreviewVisible, setCodePreviewVisible] = useState(false);
const [previewCode, setPreviewCode] = useState<{code: string; language?: string}>({code: ''}); const [previewCode, setPreviewCode] = useState<{ code: string; language?: string }>({code: ''});
const actionRef = React.useRef<ActionType>(); const actionRef = React.useRef<ActionType>();
// 获取环境列表 // 获取环境列表
@ -158,7 +158,7 @@ const DeploymentConfigList: React.FC = () => {
// 根据模板获取构建配置的子列 // 根据模板获取构建配置的子列
const getBuildConfigColumns = () => { const getBuildConfigColumns = () => {
const buildConfigColumns: ProColumns<DeploymentConfig>[] = []; const buildConfigColumns: ProColumns<DeploymentConfig>[] = [];
// 获取所有可能的字段和它们的标题 // 获取所有可能的字段和它们的标题
const fieldInfo = new Map<string, { title: string }>(); const fieldInfo = new Map<string, { title: string }>();
templates.forEach(template => { templates.forEach(template => {
@ -182,11 +182,11 @@ const DeploymentConfigList: React.FC = () => {
ellipsis: true, ellipsis: true,
render: (_, record) => { render: (_, record) => {
// 找到对应的模板 // 找到对应的模板
const template = templates.find(t => const template = templates.find(t =>
t.buildType === record.buildType && t.buildType === record.buildType &&
t.languageType === record.languageType t.languageType === record.languageType
); );
if (!template?.buildVariablesSchema?.properties?.[field]) { if (!template?.buildVariablesSchema?.properties?.[field]) {
return '-'; return '-';
} }
@ -201,9 +201,9 @@ const DeploymentConfigList: React.FC = () => {
return ( return (
<Space> <Space>
<Tooltip title="点击查看完整内容"> <Tooltip title="点击查看完整内容">
<Button <Button
type="link" type="link"
icon={<EyeOutlined />} icon={<EyeOutlined/>}
onClick={() => handleCodePreview( onClick={() => handleCodePreview(
value, value,
fieldProperties.editorConfig?.language fieldProperties.editorConfig?.language
@ -234,13 +234,6 @@ const DeploymentConfigList: React.FC = () => {
}; };
const columns: ProColumns<DeploymentConfig>[] = [ const columns: ProColumns<DeploymentConfig>[] = [
{
title: '应用名称',
dataIndex: ['application', 'appName'],
width: 150,
ellipsis: true,
fixed: 'left',
},
{ {
title: '应用编码', title: '应用编码',
dataIndex: ['application', 'appCode'], dataIndex: ['application', 'appCode'],
@ -248,6 +241,13 @@ const DeploymentConfigList: React.FC = () => {
copyable: true, copyable: true,
ellipsis: true, ellipsis: true,
}, },
{
title: '应用名称',
dataIndex: ['application', 'appName'],
width: 150,
ellipsis: true,
fixed: 'left',
},
{ {
title: '构建配置', title: '构建配置',
children: getBuildConfigColumns() children: getBuildConfigColumns()
@ -274,7 +274,7 @@ const DeploymentConfigList: React.FC = () => {
onClick={() => handleEdit(record)} onClick={() => handleEdit(record)}
> >
<Space> <Space>
<EditOutlined /> <EditOutlined/>
</Space> </Space>
</Button>, </Button>,
@ -289,7 +289,7 @@ const DeploymentConfigList: React.FC = () => {
danger danger
> >
<Space> <Space>
<DeleteOutlined /> <DeleteOutlined/>
</Space> </Space>
</Button> </Button>
@ -301,7 +301,7 @@ const DeploymentConfigList: React.FC = () => {
return ( return (
<PageContainer <PageContainer
header={{ header={{
title: '部署配置', title: '部署配置管理',
extra: [ extra: [
<Select <Select
key="env-select" key="env-select"

View File

@ -1,6 +1,6 @@
import React, {useState} from 'react'; import React, {useState} from 'react';
import {PageContainer} from '@ant-design/pro-layout'; import {PageContainer} from '@ant-design/pro-layout';
import {Button, Space, Popconfirm, Tag, App} from 'antd'; import {Button, Space, Popconfirm, Tag, App, Select} from 'antd';
import {PlusOutlined, EditOutlined, DeleteOutlined} from '@ant-design/icons'; import {PlusOutlined, EditOutlined, DeleteOutlined} from '@ant-design/icons';
import {getEnvironmentPage, deleteEnvironment} from './service'; import {getEnvironmentPage, deleteEnvironment} from './service';
import type {Environment, EnvironmentQueryParams} from './types'; import type {Environment, EnvironmentQueryParams} from './types';
@ -166,7 +166,11 @@ const EnvironmentList: React.FC = () => {
]; ];
return ( return (
<PageContainer> <PageContainer
header={{
title: '环境管理'
}}
>
<ProTable<Environment> <ProTable<Environment>
columns={columns} columns={columns}
actionRef={actionRef} actionRef={actionRef}