增加审批组件
This commit is contained in:
parent
ab4ea6e367
commit
164a39bd42
@ -9,6 +9,7 @@ import { gitRepositoriesConfig } from './presets/git';
|
|||||||
import { dockerRegistriesConfig } from './presets/docker';
|
import { dockerRegistriesConfig } from './presets/docker';
|
||||||
import { notificationChannelTypesConfig, notificationChannelsConfig } from './presets/notification';
|
import { notificationChannelTypesConfig, notificationChannelsConfig } from './presets/notification';
|
||||||
import { usersConfig, rolesConfig, departmentsConfig } from './presets/user';
|
import { usersConfig, rolesConfig, departmentsConfig } from './presets/user';
|
||||||
|
import { environmentsConfig, projectGroupsConfig, applicationsConfig } from './presets/deploy';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 数据源配置注册表
|
* 数据源配置注册表
|
||||||
@ -22,7 +23,10 @@ export const DATA_SOURCE_REGISTRY: DataSourceRegistry = {
|
|||||||
[DataSourceType.NOTIFICATION_CHANNELS]: notificationChannelsConfig,
|
[DataSourceType.NOTIFICATION_CHANNELS]: notificationChannelsConfig,
|
||||||
[DataSourceType.USERS]: usersConfig,
|
[DataSourceType.USERS]: usersConfig,
|
||||||
[DataSourceType.ROLES]: rolesConfig,
|
[DataSourceType.ROLES]: rolesConfig,
|
||||||
[DataSourceType.DEPARTMENTS]: departmentsConfig
|
[DataSourceType.DEPARTMENTS]: departmentsConfig,
|
||||||
|
[DataSourceType.ENVIRONMENTS]: environmentsConfig,
|
||||||
|
[DataSourceType.PROJECT_GROUPS]: projectGroupsConfig,
|
||||||
|
[DataSourceType.APPLICATIONS]: applicationsConfig
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
55
frontend/src/domain/dataSource/presets/deploy.ts
Normal file
55
frontend/src/domain/dataSource/presets/deploy.ts
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
/**
|
||||||
|
* 部署相关数据源
|
||||||
|
*/
|
||||||
|
import type { DataSourceConfig } from '../types';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 环境列表数据源
|
||||||
|
*/
|
||||||
|
export const environmentsConfig: DataSourceConfig = {
|
||||||
|
url: '/api/v1/environments/list',
|
||||||
|
params: { enabled: true },
|
||||||
|
transform: (data: any[]) => {
|
||||||
|
return data.map((item: any) => ({
|
||||||
|
label: item.name,
|
||||||
|
value: item.id,
|
||||||
|
code: item.code,
|
||||||
|
type: item.type,
|
||||||
|
description: item.description
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 项目组列表数据源
|
||||||
|
*/
|
||||||
|
export const projectGroupsConfig: DataSourceConfig = {
|
||||||
|
url: '/api/v1/project-groups/list',
|
||||||
|
params: { enabled: true },
|
||||||
|
transform: (data: any[]) => {
|
||||||
|
return data.map((item: any) => ({
|
||||||
|
label: item.name,
|
||||||
|
value: item.id,
|
||||||
|
code: item.code,
|
||||||
|
description: item.description
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 应用列表数据源
|
||||||
|
*/
|
||||||
|
export const applicationsConfig: DataSourceConfig = {
|
||||||
|
url: '/api/v1/applications/list',
|
||||||
|
params: { enabled: true },
|
||||||
|
transform: (data: any[]) => {
|
||||||
|
return data.map((item: any) => ({
|
||||||
|
label: item.name,
|
||||||
|
value: item.id,
|
||||||
|
code: item.code,
|
||||||
|
projectGroupId: item.projectGroupId,
|
||||||
|
description: item.description
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@ -14,7 +14,10 @@ export enum DataSourceType {
|
|||||||
NOTIFICATION_CHANNELS = 'NOTIFICATION_CHANNELS',
|
NOTIFICATION_CHANNELS = 'NOTIFICATION_CHANNELS',
|
||||||
USERS = 'USERS',
|
USERS = 'USERS',
|
||||||
ROLES = 'ROLES',
|
ROLES = 'ROLES',
|
||||||
DEPARTMENTS = 'DEPARTMENTS'
|
DEPARTMENTS = 'DEPARTMENTS',
|
||||||
|
ENVIRONMENTS = 'ENVIRONMENTS',
|
||||||
|
PROJECT_GROUPS = 'PROJECT_GROUPS',
|
||||||
|
APPLICATIONS = 'APPLICATIONS'
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import React, { useState, useEffect } from 'react';
|
|||||||
import { useNavigate, useParams } from 'react-router-dom';
|
import { useNavigate, useParams } from 'react-router-dom';
|
||||||
import { Card, CardContent } from '@/components/ui/card';
|
import { Card, CardContent } from '@/components/ui/card';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { useToast } from '@/components/ui/use-toast';
|
||||||
import { FormDesigner } from '@/components/FormDesigner';
|
import { FormDesigner } from '@/components/FormDesigner';
|
||||||
import type { FormSchema } from '@/components/FormDesigner';
|
import type { FormSchema } from '@/components/FormDesigner';
|
||||||
import { ArrowLeft, Workflow } from 'lucide-react';
|
import { ArrowLeft, Workflow } from 'lucide-react';
|
||||||
@ -14,6 +15,7 @@ import type { FormDefinitionRequest } from './types';
|
|||||||
const FormDesignerPage: React.FC = () => {
|
const FormDesignerPage: React.FC = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { id } = useParams<{ id: string }>();
|
const { id } = useParams<{ id: string }>();
|
||||||
|
const { toast } = useToast();
|
||||||
const [formSchema, setFormSchema] = useState<FormSchema | null>(null);
|
const [formSchema, setFormSchema] = useState<FormSchema | null>(null);
|
||||||
const [formDefinition, setFormDefinition] = useState<any>(null);
|
const [formDefinition, setFormDefinition] = useState<any>(null);
|
||||||
|
|
||||||
@ -37,7 +39,11 @@ const FormDesignerPage: React.FC = () => {
|
|||||||
// 保存表单(只更新 schema,不修改基本信息)
|
// 保存表单(只更新 schema,不修改基本信息)
|
||||||
const handleSave = async (schema: FormSchema) => {
|
const handleSave = async (schema: FormSchema) => {
|
||||||
if (!id || !formDefinition) {
|
if (!id || !formDefinition) {
|
||||||
alert('表单信息加载失败');
|
toast({
|
||||||
|
variant: "destructive",
|
||||||
|
title: "保存失败",
|
||||||
|
description: "表单信息加载失败,请刷新页面后重试",
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,14 +55,22 @@ const FormDesignerPage: React.FC = () => {
|
|||||||
isTemplate: formDefinition.isTemplate,
|
isTemplate: formDefinition.isTemplate,
|
||||||
schema,
|
schema,
|
||||||
status: formDefinition.status || 'DRAFT',
|
status: formDefinition.status || 'DRAFT',
|
||||||
|
version: formDefinition.version, // 乐观锁版本号
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await updateDefinition(Number(id), request);
|
await updateDefinition(Number(id), request);
|
||||||
alert('保存成功');
|
toast({
|
||||||
|
title: "保存成功",
|
||||||
|
description: `表单 "${formDefinition.name}" 已保存`,
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('保存表单失败:', error);
|
console.error('保存表单失败:', error);
|
||||||
alert('保存失败');
|
toast({
|
||||||
|
variant: "destructive",
|
||||||
|
title: "保存失败",
|
||||||
|
description: error instanceof Error ? error.message : '未知错误,请稍后重试',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import { Label } from '@/components/ui/label';
|
|||||||
import { Textarea } from '@/components/ui/textarea';
|
import { Textarea } from '@/components/ui/textarea';
|
||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||||
import { Switch } from '@/components/ui/switch';
|
import { Switch } from '@/components/ui/switch';
|
||||||
|
import { useToast } from '@/components/ui/use-toast';
|
||||||
import { Loader2, FileText, Tag, Folder, AlignLeft, Copy } from 'lucide-react';
|
import { Loader2, FileText, Tag, Folder, AlignLeft, Copy } from 'lucide-react';
|
||||||
import { getEnabledCategories } from '../../Category/service';
|
import { getEnabledCategories } from '../../Category/service';
|
||||||
import { updateDefinition } from '../service';
|
import { updateDefinition } from '../service';
|
||||||
@ -27,6 +28,7 @@ interface EditBasicInfoModalProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const EditBasicInfoModal: React.FC<EditBasicInfoModalProps> = ({ visible, record, onClose, onSuccess }) => {
|
const EditBasicInfoModal: React.FC<EditBasicInfoModalProps> = ({ visible, record, onClose, onSuccess }) => {
|
||||||
|
const { toast } = useToast();
|
||||||
const [submitting, setSubmitting] = useState(false);
|
const [submitting, setSubmitting] = useState(false);
|
||||||
const [categories, setCategories] = useState<FormCategoryResponse[]>([]);
|
const [categories, setCategories] = useState<FormCategoryResponse[]>([]);
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
@ -70,15 +72,27 @@ const EditBasicInfoModal: React.FC<EditBasicInfoModalProps> = ({ visible, record
|
|||||||
if (!record) return;
|
if (!record) return;
|
||||||
|
|
||||||
if (!formData.name.trim()) {
|
if (!formData.name.trim()) {
|
||||||
alert('请输入表单名称');
|
toast({
|
||||||
|
variant: "destructive",
|
||||||
|
title: "验证失败",
|
||||||
|
description: "请输入表单名称",
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!formData.key.trim()) {
|
if (!formData.key.trim()) {
|
||||||
alert('请输入表单标识');
|
toast({
|
||||||
|
variant: "destructive",
|
||||||
|
title: "验证失败",
|
||||||
|
description: "请输入表单标识",
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!/^[a-zA-Z0-9-]+$/.test(formData.key)) {
|
if (!/^[a-zA-Z0-9-]+$/.test(formData.key)) {
|
||||||
alert('表单标识只能包含英文字母、数字和中划线');
|
toast({
|
||||||
|
variant: "destructive",
|
||||||
|
title: "验证失败",
|
||||||
|
description: "表单标识只能包含英文字母、数字和中划线",
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,15 +106,23 @@ const EditBasicInfoModal: React.FC<EditBasicInfoModalProps> = ({ visible, record
|
|||||||
schema: record.schema,
|
schema: record.schema,
|
||||||
status: record.status,
|
status: record.status,
|
||||||
isTemplate: formData.isTemplate,
|
isTemplate: formData.isTemplate,
|
||||||
|
version: record.version, // 乐观锁版本号
|
||||||
};
|
};
|
||||||
|
|
||||||
await updateDefinition(record.id, request);
|
await updateDefinition(record.id, request);
|
||||||
alert('更新成功');
|
toast({
|
||||||
|
title: "更新成功",
|
||||||
|
description: `表单 "${formData.name}" 的基本信息已更新`,
|
||||||
|
});
|
||||||
onSuccess();
|
onSuccess();
|
||||||
onClose();
|
onClose();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('更新表单失败:', error);
|
console.error('更新表单失败:', error);
|
||||||
alert('更新表单失败: ' + (error as Error).message);
|
toast({
|
||||||
|
variant: "destructive",
|
||||||
|
title: "更新失败",
|
||||||
|
description: error instanceof Error ? error.message : '未知错误,请稍后重试',
|
||||||
|
});
|
||||||
} finally {
|
} finally {
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { BaseQuery } from '@/types/base';
|
import { BaseQuery, BaseResponse } from '@/types/base';
|
||||||
import type { FormSchema } from '@/components/FormDesigner';
|
import type { FormSchema } from '@/components/FormDesigner';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -19,8 +19,7 @@ export interface FormDefinitionQuery extends BaseQuery {
|
|||||||
/**
|
/**
|
||||||
* 表单定义响应
|
* 表单定义响应
|
||||||
*/
|
*/
|
||||||
export interface FormDefinitionResponse {
|
export interface FormDefinitionResponse extends BaseResponse {
|
||||||
id: number;
|
|
||||||
name: string;
|
name: string;
|
||||||
key: string;
|
key: string;
|
||||||
formVersion: number;
|
formVersion: number;
|
||||||
@ -30,10 +29,6 @@ export interface FormDefinitionResponse {
|
|||||||
tags?: string[];
|
tags?: string[];
|
||||||
status: FormDefinitionStatus;
|
status: FormDefinitionStatus;
|
||||||
isTemplate: boolean;
|
isTemplate: boolean;
|
||||||
createBy?: string;
|
|
||||||
createTime?: string;
|
|
||||||
updateBy?: string;
|
|
||||||
updateTime?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -48,5 +43,6 @@ export interface FormDefinitionRequest {
|
|||||||
tags?: string[];
|
tags?: string[];
|
||||||
status?: FormDefinitionStatus;
|
status?: FormDefinitionStatus;
|
||||||
isTemplate?: boolean;
|
isTemplate?: boolean;
|
||||||
|
version?: number; // 乐观锁版本号(更新时必传)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user