重构消息通知弹窗
This commit is contained in:
parent
8f8b90abb2
commit
123b9054da
@ -14,6 +14,7 @@ import {
|
||||
applicationsConfig,
|
||||
notificationChannelTypesConfig,
|
||||
notificationChannelsConfig,
|
||||
notificationTemplatesConfig,
|
||||
usersConfig,
|
||||
rolesConfig,
|
||||
departmentsConfig
|
||||
@ -32,6 +33,7 @@ class DataSourceRegistryImpl extends BaseRegistry<DataSourceType, DataSourceConf
|
||||
this.register(DataSourceType.DOCKER_REGISTRIES, dockerRegistriesConfig);
|
||||
this.register(DataSourceType.NOTIFICATION_CHANNEL_TYPES, notificationChannelTypesConfig);
|
||||
this.register(DataSourceType.NOTIFICATION_CHANNELS, notificationChannelsConfig);
|
||||
this.register(DataSourceType.NOTIFICATION_TEMPLATES, notificationTemplatesConfig);
|
||||
this.register(DataSourceType.USERS, usersConfig);
|
||||
this.register(DataSourceType.ROLES, rolesConfig);
|
||||
this.register(DataSourceType.DEPARTMENTS, departmentsConfig);
|
||||
|
||||
@ -188,3 +188,16 @@ export const departmentsConfig: DataSourceConfig = {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 通知模板数据源
|
||||
*/
|
||||
export const notificationTemplatesConfig: DataSourceConfig = {
|
||||
url: '/api/v1/notification-template/list',
|
||||
transform: (data: any[]) => {
|
||||
return data.map((item: any) => ({
|
||||
label: `(${item.channelType})-${item.name}`,
|
||||
value: item.id
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -12,6 +12,7 @@ export enum DataSourceType {
|
||||
DOCKER_REGISTRIES = 'DOCKER_REGISTRIES',
|
||||
NOTIFICATION_CHANNEL_TYPES = 'NOTIFICATION_CHANNEL_TYPES',
|
||||
NOTIFICATION_CHANNELS = 'NOTIFICATION_CHANNELS',
|
||||
NOTIFICATION_TEMPLATES = 'NOTIFICATION_TEMPLATES',
|
||||
USERS = 'USERS',
|
||||
ROLES = 'ROLES',
|
||||
DEPARTMENTS = 'DEPARTMENTS',
|
||||
|
||||
@ -67,9 +67,13 @@ const DeploymentFormModal: React.FC<DeploymentFormModalProps> = ({
|
||||
},
|
||||
// 通知信息(使用环境配置的通知设置)
|
||||
notification: {
|
||||
required: environment.notificationConfig?.deployNotificationEnabled ? 'true' : 'false',
|
||||
channelId: environment.notificationConfig?.notificationChannelId?.toString() || '',
|
||||
channelName: environment.notificationConfig?.notificationChannelName || '',
|
||||
notificationChannelId: environment.notificationConfig?.notificationChannelId,
|
||||
deployNotificationEnabled: environment.notificationConfig?.deployNotificationEnabled,
|
||||
deployNotificationTemplateId: environment.notificationConfig?.deployNotificationTemplateId,
|
||||
buildNotificationEnabled: environment.notificationConfig?.buildNotificationEnabled,
|
||||
buildNotificationTemplateId: environment.notificationConfig?.buildNotificationTemplateId,
|
||||
buildFailureFileEnabled: environment.notificationConfig?.buildFailureFileEnabled,
|
||||
buildFailureNotificationTemplateId: environment.notificationConfig?.buildFailureNotificationTemplateId,
|
||||
},
|
||||
};
|
||||
}, [app, environment, teamId]); // 只在这些依赖变化时重新生成
|
||||
|
||||
@ -61,9 +61,11 @@ export interface ApplicationConfig {
|
||||
export interface NotificationConfig {
|
||||
notificationChannelId: number;
|
||||
deployNotificationEnabled: boolean;
|
||||
deployNotificationTemplateId: number;
|
||||
buildNotificationEnabled: boolean;
|
||||
buildNotificationTemplateId: number;
|
||||
buildFailureFileEnabled: boolean;
|
||||
notificationChannelName: string;
|
||||
buildFailureNotificationTemplateId: number;
|
||||
}
|
||||
|
||||
export interface DeployEnvironment {
|
||||
|
||||
@ -56,6 +56,7 @@ const formSchema = z.object({
|
||||
.regex(/^[a-zA-Z][a-zA-Z0-9_]*$/, '模板编码只能包含字母、数字和下划线,且以字母开头'),
|
||||
description: z.string().max(500, '描述不能超过500个字符').optional(),
|
||||
channelType: z.nativeEnum(NotificationChannelType),
|
||||
titleTemplate: z.string().min(1, '请输入标题模板'),
|
||||
contentTemplate: z.string().min(1, '请输入模板内容'),
|
||||
templateConfig: z.object({
|
||||
// 用于后端多态反序列化,必须存在
|
||||
@ -100,6 +101,7 @@ export const NotificationTemplateDialog: React.FC<NotificationTemplateDialogProp
|
||||
mode: 'onChange',
|
||||
defaultValues: {
|
||||
channelType: NotificationChannelType.WEWORK,
|
||||
titleTemplate: '',
|
||||
contentTemplate: '',
|
||||
},
|
||||
});
|
||||
@ -147,6 +149,7 @@ export const NotificationTemplateDialog: React.FC<NotificationTemplateDialogProp
|
||||
code: '',
|
||||
description: '',
|
||||
channelType: NotificationChannelType.WEWORK,
|
||||
titleTemplate: '',
|
||||
contentTemplate: '',
|
||||
templateConfig: TemplateConfigFormFactory.getDefaultConfig(NotificationChannelType.WEWORK),
|
||||
});
|
||||
@ -177,6 +180,7 @@ export const NotificationTemplateDialog: React.FC<NotificationTemplateDialogProp
|
||||
code: data.code,
|
||||
description: data.description || '',
|
||||
channelType: data.channelType,
|
||||
titleTemplate: data.titleTemplate || '',
|
||||
contentTemplate: data.contentTemplate,
|
||||
templateConfig: cfg,
|
||||
});
|
||||
@ -239,6 +243,7 @@ export const NotificationTemplateDialog: React.FC<NotificationTemplateDialogProp
|
||||
code: values.code,
|
||||
description: values.description,
|
||||
channelType: values.channelType,
|
||||
titleTemplate: values.titleTemplate,
|
||||
contentTemplate: values.contentTemplate,
|
||||
enabled: currentEnabled,
|
||||
templateConfig: templateConfig,
|
||||
@ -372,6 +377,21 @@ export const NotificationTemplateDialog: React.FC<NotificationTemplateDialogProp
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 标题模板 */}
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="titleTemplate">
|
||||
标题模板 <span className="text-destructive">*</span>
|
||||
</Label>
|
||||
<Input
|
||||
id="titleTemplate"
|
||||
placeholder="请输入标题模板(支持FreeMarker语法)"
|
||||
{...register('titleTemplate')}
|
||||
/>
|
||||
{errors.titleTemplate && (
|
||||
<p className="text-sm text-destructive">{errors.titleTemplate.message}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 模板配置 */}
|
||||
{watchedChannelType && (
|
||||
<div className="space-y-2">
|
||||
|
||||
@ -356,6 +356,7 @@ const NotificationTemplateList: React.FC = () => {
|
||||
<TableRow>
|
||||
<TableHead>模板名称</TableHead>
|
||||
<TableHead>模板编码</TableHead>
|
||||
<TableHead>标题模板</TableHead>
|
||||
<TableHead>渠道类型</TableHead>
|
||||
<TableHead>状态</TableHead>
|
||||
<TableHead>创建时间</TableHead>
|
||||
@ -365,13 +366,13 @@ const NotificationTemplateList: React.FC = () => {
|
||||
<TableBody>
|
||||
{loading ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={6} className="h-32 text-center">
|
||||
<TableCell colSpan={7} className="h-32 text-center">
|
||||
<Loader2 className="h-8 w-8 animate-spin mx-auto" />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : list.length === 0 ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={6} className="h-32 text-center text-muted-foreground">
|
||||
<TableCell colSpan={7} className="h-32 text-center text-muted-foreground">
|
||||
暂无数据
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
@ -380,6 +381,9 @@ const NotificationTemplateList: React.FC = () => {
|
||||
<TableRow key={template.id}>
|
||||
<TableCell className="font-medium">{template.name}</TableCell>
|
||||
<TableCell>{template.code}</TableCell>
|
||||
<TableCell className="max-w-xs truncate" title={template.titleTemplate}>
|
||||
{template.titleTemplate || '-'}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Badge variant="outline">
|
||||
{getChannelTypeLabel(template.channelType)}
|
||||
|
||||
@ -44,7 +44,7 @@ export const getTemplateById = async (id: number): Promise<NotificationTemplateD
|
||||
* 创建模板
|
||||
*/
|
||||
export const createTemplate = async (
|
||||
template: Pick<NotificationTemplateDTO, 'name' | 'code' | 'description' | 'channelType' | 'contentTemplate' | 'templateConfig'>
|
||||
template: Pick<NotificationTemplateDTO, 'name' | 'code' | 'description' | 'channelType' | 'titleTemplate' | 'contentTemplate' | 'templateConfig'>
|
||||
): Promise<NotificationTemplateDTO> => {
|
||||
// 确保 channelType 在 templateConfig 之前
|
||||
const payload = {
|
||||
@ -52,6 +52,7 @@ export const createTemplate = async (
|
||||
code: template.code,
|
||||
description: template.description,
|
||||
channelType: template.channelType,
|
||||
titleTemplate: template.titleTemplate,
|
||||
contentTemplate: template.contentTemplate,
|
||||
templateConfig: template.templateConfig,
|
||||
enabled: true, // 默认启用
|
||||
@ -64,7 +65,7 @@ export const createTemplate = async (
|
||||
*/
|
||||
export const updateTemplate = async (
|
||||
id: number,
|
||||
template: Pick<NotificationTemplateDTO, 'id' | 'name' | 'code' | 'description' | 'channelType' | 'contentTemplate' | 'templateConfig' | 'enabled'>
|
||||
template: Pick<NotificationTemplateDTO, 'id' | 'name' | 'code' | 'description' | 'channelType' | 'titleTemplate' | 'contentTemplate' | 'templateConfig' | 'enabled'>
|
||||
): Promise<NotificationTemplateDTO> => {
|
||||
// 确保字段顺序:channelType 必须在 templateConfig 之前
|
||||
const payload = {
|
||||
@ -73,6 +74,7 @@ export const updateTemplate = async (
|
||||
code: template.code,
|
||||
description: template.description,
|
||||
channelType: template.channelType,
|
||||
titleTemplate: template.titleTemplate,
|
||||
contentTemplate: template.contentTemplate,
|
||||
enabled: template.enabled,
|
||||
templateConfig: template.templateConfig,
|
||||
|
||||
@ -20,6 +20,9 @@ export interface NotificationTemplateDTO extends BaseResponse {
|
||||
/** 渠道类型 */
|
||||
channelType: NotificationChannelType;
|
||||
|
||||
/** 标题模板(FreeMarker格式) */
|
||||
titleTemplate: string;
|
||||
|
||||
/** 内容模板(FreeMarker格式) */
|
||||
contentTemplate: string;
|
||||
|
||||
|
||||
@ -71,21 +71,16 @@ export const NotificationNodeDefinition: ConfigurableNodeDefinition = {
|
||||
"x-dataSource": DataSourceType.NOTIFICATION_CHANNELS,
|
||||
"x-allow-variable": true
|
||||
},
|
||||
title: {
|
||||
type: "string",
|
||||
title: "通知标题",
|
||||
description: "通知消息的标题",
|
||||
default: "${notification.title}"
|
||||
},
|
||||
content: {
|
||||
type: "string",
|
||||
title: "通知内容",
|
||||
description: "通知消息的正文内容,支持变量表达式",
|
||||
format: "textarea",
|
||||
default: "${notification.context}"
|
||||
notificationTemplateId: {
|
||||
type: "number",
|
||||
title: "通知模板",
|
||||
description: "选择通知模板,或输入动态值",
|
||||
"x-dataSource": DataSourceType.NOTIFICATION_TEMPLATES,
|
||||
"x-allow-variable": true,
|
||||
"x-depends-on": "channelId"
|
||||
}
|
||||
},
|
||||
required: ["channelId", "title", "content"]
|
||||
required: ["channelId", "notificationTemplateId"]
|
||||
},
|
||||
outputs: defineNodeOutputs()
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user