三方系统密码加密
This commit is contained in:
parent
a974b6fea4
commit
3f061e996e
@ -1,6 +1,6 @@
|
|||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import * as SelectPrimitive from "@radix-ui/react-select"
|
import * as SelectPrimitive from "@radix-ui/react-select"
|
||||||
import { Check, ChevronDown, ChevronUp } from "lucide-react"
|
import { Check, ChevronDown, ChevronUp, X } from "lucide-react"
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
@ -10,10 +10,35 @@ const SelectGroup = SelectPrimitive.Group
|
|||||||
|
|
||||||
const SelectValue = SelectPrimitive.Value
|
const SelectValue = SelectPrimitive.Value
|
||||||
|
|
||||||
|
interface SelectTriggerProps extends React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger> {
|
||||||
|
clearable?: boolean;
|
||||||
|
onClear?: () => void;
|
||||||
|
hasValue?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
const SelectTrigger = React.forwardRef<
|
const SelectTrigger = React.forwardRef<
|
||||||
React.ElementRef<typeof SelectPrimitive.Trigger>,
|
React.ElementRef<typeof SelectPrimitive.Trigger>,
|
||||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
|
SelectTriggerProps
|
||||||
>(({ className, children, ...props }, ref) => (
|
>(({ className, children, clearable, onClear, hasValue, ...props }, ref) => {
|
||||||
|
const clearButtonRef = React.useRef<HTMLSpanElement>(null);
|
||||||
|
|
||||||
|
const handleClear = (e: React.MouseEvent | React.PointerEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
onClear?.();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleTriggerPointerDown = (e: React.PointerEvent<HTMLButtonElement>) => {
|
||||||
|
// 如果点击的是清空按钮或其子元素,阻止触发器的默认行为
|
||||||
|
if (clearButtonRef.current && (e.target === clearButtonRef.current || clearButtonRef.current.contains(e.target as Node))) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
props.onPointerDown?.(e);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
<SelectPrimitive.Trigger
|
<SelectPrimitive.Trigger
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
@ -21,13 +46,28 @@ const SelectTrigger = React.forwardRef<
|
|||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
onPointerDown={handleTriggerPointerDown}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
{clearable && hasValue && (
|
||||||
|
<span
|
||||||
|
ref={clearButtonRef}
|
||||||
|
role="button"
|
||||||
|
onClick={handleClear}
|
||||||
|
className="flex items-center justify-center hover:bg-accent rounded-sm p-0.5 transition-colors cursor-pointer z-10"
|
||||||
|
aria-label="清空选择"
|
||||||
|
>
|
||||||
|
<X className="h-3.5 w-3.5 opacity-50 hover:opacity-100" />
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
<SelectPrimitive.Icon asChild>
|
<SelectPrimitive.Icon asChild>
|
||||||
<ChevronDown className="h-4 w-4 opacity-50" />
|
<ChevronDown className="h-4 w-4 opacity-50" />
|
||||||
</SelectPrimitive.Icon>
|
</SelectPrimitive.Icon>
|
||||||
|
</div>
|
||||||
</SelectPrimitive.Trigger>
|
</SelectPrimitive.Trigger>
|
||||||
))
|
);
|
||||||
|
})
|
||||||
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName
|
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName
|
||||||
|
|
||||||
const SelectScrollUpButton = React.forwardRef<
|
const SelectScrollUpButton = React.forwardRef<
|
||||||
|
|||||||
@ -7,7 +7,6 @@ import {
|
|||||||
Package,
|
Package,
|
||||||
GitBranch,
|
GitBranch,
|
||||||
Rocket,
|
Rocket,
|
||||||
Server,
|
|
||||||
CheckCircle2,
|
CheckCircle2,
|
||||||
XCircle,
|
XCircle,
|
||||||
TrendingUp,
|
TrendingUp,
|
||||||
@ -85,39 +84,6 @@ export const ApplicationCard: React.FC<ApplicationCardProps> = ({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 工作流 */}
|
|
||||||
<div className="flex items-center gap-1.5">
|
|
||||||
<Rocket className="h-3 w-3 shrink-0" />
|
|
||||||
{app.workflowDefinitionName ? (
|
|
||||||
<span className="truncate">{app.workflowDefinitionName}</span>
|
|
||||||
) : (
|
|
||||||
<Skeleton className="h-3 w-20" />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Jenkins */}
|
|
||||||
{app.deploySystemName ? (
|
|
||||||
<TooltipProvider>
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<div className="flex items-center gap-1.5 cursor-default">
|
|
||||||
<Server className="h-3 w-3 shrink-0" />
|
|
||||||
<span className="truncate">{app.deploySystemName}</span>
|
|
||||||
</div>
|
|
||||||
</TooltipTrigger>
|
|
||||||
{app.deployJob && (
|
|
||||||
<TooltipContent>
|
|
||||||
<p>{app.deploySystemName} ({app.deployJob})</p>
|
|
||||||
</TooltipContent>
|
|
||||||
)}
|
|
||||||
</Tooltip>
|
|
||||||
</TooltipProvider>
|
|
||||||
) : (
|
|
||||||
<div className="flex items-center gap-1.5">
|
|
||||||
<Server className="h-3 w-3 shrink-0" />
|
|
||||||
<Skeleton className="h-3 w-24" />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 部署统计信息 */}
|
{/* 部署统计信息 */}
|
||||||
|
|||||||
@ -50,7 +50,6 @@ import {
|
|||||||
getTeamEnvironmentConfig,
|
getTeamEnvironmentConfig,
|
||||||
createTeamEnvironmentConfig,
|
createTeamEnvironmentConfig,
|
||||||
updateTeamEnvironmentConfig,
|
updateTeamEnvironmentConfig,
|
||||||
getNotificationChannels,
|
|
||||||
} from '../service';
|
} from '../service';
|
||||||
|
|
||||||
interface User {
|
interface User {
|
||||||
@ -64,23 +63,9 @@ const formSchema = z.object({
|
|||||||
environmentId: z.number().min(1, '请选择环境'),
|
environmentId: z.number().min(1, '请选择环境'),
|
||||||
approvalRequired: z.boolean().default(false),
|
approvalRequired: z.boolean().default(false),
|
||||||
approverUserIds: z.array(z.number()).default([]),
|
approverUserIds: z.array(z.number()).default([]),
|
||||||
notificationChannelId: z.number().nullish(),
|
|
||||||
notificationEnabled: z.boolean().default(false),
|
|
||||||
requireCodeReview: z.boolean().default(false),
|
requireCodeReview: z.boolean().default(false),
|
||||||
remark: z.string().max(100, '备注最多100个字符').optional(),
|
remark: z.string().max(100, '备注最多100个字符').optional(),
|
||||||
}).refine(
|
}).refine(
|
||||||
(data) => {
|
|
||||||
// 如果启用了通知,则通知渠道必填
|
|
||||||
if (data.notificationEnabled && !data.notificationChannelId) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
{
|
|
||||||
message: '启用通知时必须选择通知渠道',
|
|
||||||
path: ['notificationChannelId'],
|
|
||||||
}
|
|
||||||
).refine(
|
|
||||||
(data) => {
|
(data) => {
|
||||||
// 如果需要审批,则审批人必填
|
// 如果需要审批,则审批人必填
|
||||||
if (data.approvalRequired && (!data.approverUserIds || data.approverUserIds.length === 0)) {
|
if (data.approvalRequired && (!data.approverUserIds || data.approverUserIds.length === 0)) {
|
||||||
@ -96,12 +81,6 @@ const formSchema = z.object({
|
|||||||
|
|
||||||
type FormData = z.infer<typeof formSchema>;
|
type FormData = z.infer<typeof formSchema>;
|
||||||
|
|
||||||
interface NotificationChannel {
|
|
||||||
id: number;
|
|
||||||
name: string;
|
|
||||||
type: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface TeamEnvironmentConfigDialogProps{
|
interface TeamEnvironmentConfigDialogProps{
|
||||||
open: boolean;
|
open: boolean;
|
||||||
onOpenChange: (open: boolean) => void;
|
onOpenChange: (open: boolean) => void;
|
||||||
@ -115,8 +94,6 @@ interface TeamEnvironmentConfigDialogProps {
|
|||||||
initialData?: {
|
initialData?: {
|
||||||
environmentId: number;
|
environmentId: number;
|
||||||
workflowDefinitionId?: number;
|
workflowDefinitionId?: number;
|
||||||
notificationChannelId?: number;
|
|
||||||
notificationEnabled?: boolean;
|
|
||||||
requireCodeReview?: boolean;
|
requireCodeReview?: boolean;
|
||||||
remark?: string;
|
remark?: string;
|
||||||
};
|
};
|
||||||
@ -138,10 +115,6 @@ export const TeamEnvironmentConfigDialog: React.FC<
|
|||||||
const [submitting, setSubmitting] = useState(false);
|
const [submitting, setSubmitting] = useState(false);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [configId, setConfigId] = useState<number | null>(null);
|
const [configId, setConfigId] = useState<number | null>(null);
|
||||||
const [notificationChannels, setNotificationChannels] = useState<
|
|
||||||
NotificationChannel[]
|
|
||||||
>([]);
|
|
||||||
const [loadingChannels, setLoadingChannels] = useState(false);
|
|
||||||
|
|
||||||
const form = useForm<FormData>({
|
const form = useForm<FormData>({
|
||||||
resolver: zodResolver(formSchema),
|
resolver: zodResolver(formSchema),
|
||||||
@ -149,8 +122,6 @@ export const TeamEnvironmentConfigDialog: React.FC<
|
|||||||
environmentId: initialData?.environmentId || editEnvironmentId || 0,
|
environmentId: initialData?.environmentId || editEnvironmentId || 0,
|
||||||
approvalRequired: false,
|
approvalRequired: false,
|
||||||
approverUserIds: [],
|
approverUserIds: [],
|
||||||
notificationChannelId: initialData?.notificationChannelId,
|
|
||||||
notificationEnabled: initialData?.notificationEnabled || false,
|
|
||||||
requireCodeReview: initialData?.requireCodeReview || false,
|
requireCodeReview: initialData?.requireCodeReview || false,
|
||||||
remark: initialData?.remark || '',
|
remark: initialData?.remark || '',
|
||||||
},
|
},
|
||||||
@ -159,7 +130,6 @@ export const TeamEnvironmentConfigDialog: React.FC<
|
|||||||
// 对话框打开时初始化
|
// 对话框打开时初始化
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (open) {
|
if (open) {
|
||||||
loadNotificationChannels();
|
|
||||||
|
|
||||||
if (editEnvironmentId) {
|
if (editEnvironmentId) {
|
||||||
// 编辑模式或绑定新环境:设置环境ID并加载配置
|
// 编辑模式或绑定新环境:设置环境ID并加载配置
|
||||||
@ -172,8 +142,6 @@ export const TeamEnvironmentConfigDialog: React.FC<
|
|||||||
environmentId: initialData.environmentId,
|
environmentId: initialData.environmentId,
|
||||||
approvalRequired: false,
|
approvalRequired: false,
|
||||||
approverUserIds: [],
|
approverUserIds: [],
|
||||||
notificationChannelId: initialData.notificationChannelId,
|
|
||||||
notificationEnabled: initialData.notificationEnabled || false,
|
|
||||||
requireCodeReview: initialData.requireCodeReview || false,
|
requireCodeReview: initialData.requireCodeReview || false,
|
||||||
remark: initialData.remark || '',
|
remark: initialData.remark || '',
|
||||||
});
|
});
|
||||||
@ -184,8 +152,6 @@ export const TeamEnvironmentConfigDialog: React.FC<
|
|||||||
environmentId: 0,
|
environmentId: 0,
|
||||||
approvalRequired: false,
|
approvalRequired: false,
|
||||||
approverUserIds: [],
|
approverUserIds: [],
|
||||||
notificationChannelId: undefined,
|
|
||||||
notificationEnabled: false,
|
|
||||||
requireCodeReview: false,
|
requireCodeReview: false,
|
||||||
remark: '',
|
remark: '',
|
||||||
});
|
});
|
||||||
@ -206,8 +172,6 @@ export const TeamEnvironmentConfigDialog: React.FC<
|
|||||||
environmentId: config.environmentId,
|
environmentId: config.environmentId,
|
||||||
approvalRequired: config.approvalRequired || false,
|
approvalRequired: config.approvalRequired || false,
|
||||||
approverUserIds: config.approverUserIds || [],
|
approverUserIds: config.approverUserIds || [],
|
||||||
notificationChannelId: config.notificationChannelId,
|
|
||||||
notificationEnabled: config.notificationEnabled || false,
|
|
||||||
requireCodeReview: config.requireCodeReview || false,
|
requireCodeReview: config.requireCodeReview || false,
|
||||||
remark: config.remark || '',
|
remark: config.remark || '',
|
||||||
});
|
});
|
||||||
@ -220,8 +184,6 @@ export const TeamEnvironmentConfigDialog: React.FC<
|
|||||||
environmentId: editEnvironmentId,
|
environmentId: editEnvironmentId,
|
||||||
approvalRequired: false,
|
approvalRequired: false,
|
||||||
approverUserIds: [],
|
approverUserIds: [],
|
||||||
notificationChannelId: undefined,
|
|
||||||
notificationEnabled: false,
|
|
||||||
requireCodeReview: false,
|
requireCodeReview: false,
|
||||||
remark: '',
|
remark: '',
|
||||||
});
|
});
|
||||||
@ -237,22 +199,6 @@ export const TeamEnvironmentConfigDialog: React.FC<
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const loadNotificationChannels = async () => {
|
|
||||||
setLoadingChannels(true);
|
|
||||||
try {
|
|
||||||
const channels = await getNotificationChannels();
|
|
||||||
setNotificationChannels(channels);
|
|
||||||
} catch (error) {
|
|
||||||
toast({
|
|
||||||
title: '加载失败',
|
|
||||||
description: '无法加载通知渠道列表',
|
|
||||||
variant: 'destructive',
|
|
||||||
});
|
|
||||||
} finally {
|
|
||||||
setLoadingChannels(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSubmit = async (data: FormData) => {
|
const handleSubmit = async (data: FormData) => {
|
||||||
console.log('表单提交开始', data);
|
console.log('表单提交开始', data);
|
||||||
setSubmitting(true);
|
setSubmitting(true);
|
||||||
@ -263,9 +209,6 @@ export const TeamEnvironmentConfigDialog: React.FC<
|
|||||||
approvalRequired: data.approvalRequired,
|
approvalRequired: data.approvalRequired,
|
||||||
// 如果不需要审批,清空审批人列表
|
// 如果不需要审批,清空审批人列表
|
||||||
approverUserIds: data.approvalRequired ? data.approverUserIds : [],
|
approverUserIds: data.approvalRequired ? data.approverUserIds : [],
|
||||||
// 如果未启用通知,清空通知渠道
|
|
||||||
notificationChannelId: data.notificationEnabled ? data.notificationChannelId : undefined,
|
|
||||||
notificationEnabled: data.notificationEnabled,
|
|
||||||
requireCodeReview: data.requireCodeReview,
|
requireCodeReview: data.requireCodeReview,
|
||||||
remark: data.remark,
|
remark: data.remark,
|
||||||
};
|
};
|
||||||
@ -493,77 +436,6 @@ export const TeamEnvironmentConfigDialog: React.FC<
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* 启用通知 */}
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="notificationEnabled"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem className="flex flex-row items-center justify-between rounded-lg border p-4">
|
|
||||||
<div className="space-y-0.5">
|
|
||||||
<FormLabel className="text-base">启用通知</FormLabel>
|
|
||||||
<div className="text-sm text-muted-foreground">
|
|
||||||
部署状态变更时发送通知
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<FormControl>
|
|
||||||
<Switch
|
|
||||||
checked={field.value}
|
|
||||||
onCheckedChange={(checked) => {
|
|
||||||
field.onChange(checked);
|
|
||||||
// 取消通知时自动清空通知渠道
|
|
||||||
if (!checked) {
|
|
||||||
form.setValue('notificationChannelId', undefined);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* 通知渠道选择 - 仅在启用通知时显示 */}
|
|
||||||
{form.watch('notificationEnabled') && (
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="notificationChannelId"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>通知渠道 *</FormLabel>
|
|
||||||
<Select
|
|
||||||
value={field.value?.toString() || ''}
|
|
||||||
onValueChange={(value) =>
|
|
||||||
field.onChange(value ? Number(value) : undefined)
|
|
||||||
}
|
|
||||||
disabled={loadingChannels}
|
|
||||||
>
|
|
||||||
<FormControl>
|
|
||||||
<SelectTrigger>
|
|
||||||
<SelectValue
|
|
||||||
placeholder={
|
|
||||||
loadingChannels
|
|
||||||
? '加载中...'
|
|
||||||
: '请选择通知渠道'
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</SelectTrigger>
|
|
||||||
</FormControl>
|
|
||||||
<SelectContent>
|
|
||||||
{notificationChannels.map((channel) => (
|
|
||||||
<SelectItem
|
|
||||||
key={channel.id}
|
|
||||||
value={channel.id.toString()}
|
|
||||||
>
|
|
||||||
{channel.name} ({channel.type})
|
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* 需要代码审查 */}
|
{/* 需要代码审查 */}
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
|
|||||||
@ -20,12 +20,13 @@ import { ConfirmDialog } from '@/components/ui/confirm-dialog';
|
|||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Badge } from '@/components/ui/badge';
|
import { Badge } from '@/components/ui/badge';
|
||||||
import { useToast } from '@/components/ui/use-toast';
|
import { useToast } from '@/components/ui/use-toast';
|
||||||
import { Settings, AppWindow, Trash2, Plus, Loader2 } from 'lucide-react';
|
import { Settings, AppWindow, Trash2, Plus, Loader2, Bell } from 'lucide-react';
|
||||||
import type { Environment } from '@/pages/Deploy/Environment/List/types';
|
import type { Environment } from '@/pages/Deploy/Environment/List/types';
|
||||||
import type { TeamEnvironmentConfig } from '../types';
|
import type { TeamEnvironmentConfig } from '../types';
|
||||||
import { getTeamEnvironmentConfigs, deleteTeamEnvironmentConfig } from '../service';
|
import { getTeamEnvironmentConfigs, deleteTeamEnvironmentConfig } from '../service';
|
||||||
import { TeamEnvironmentConfigDialog } from './TeamEnvironmentConfigDialog';
|
import { TeamEnvironmentConfigDialog } from './TeamEnvironmentConfigDialog';
|
||||||
import { TeamApplicationManageDialog } from './TeamApplicationManageDialog';
|
import { TeamApplicationManageDialog } from './TeamApplicationManageDialog';
|
||||||
|
import { NotificationConfigDialog } from './NotificationConfigDialog';
|
||||||
|
|
||||||
interface User {
|
interface User {
|
||||||
id: number;
|
id: number;
|
||||||
@ -69,8 +70,13 @@ export const TeamEnvironmentManageDialog: React.FC<
|
|||||||
// 应用管理对话框状态
|
// 应用管理对话框状态
|
||||||
const [appManageDialogOpen, setAppManageDialogOpen] = useState(false);
|
const [appManageDialogOpen, setAppManageDialogOpen] = useState(false);
|
||||||
const [selectedEnvironmentId, setSelectedEnvironmentId] = useState<
|
const [selectedEnvironmentId, setSelectedEnvironmentId] = useState<
|
||||||
number | undefined
|
number | null
|
||||||
>();
|
>(null);
|
||||||
|
|
||||||
|
// 通知配置对话框状态
|
||||||
|
const [notificationDialogOpen, setNotificationDialogOpen] = useState(false);
|
||||||
|
const [notificationEnvironmentId, setNotificationEnvironmentId] = useState<number | null>(null);
|
||||||
|
const [notificationEnvironmentName, setNotificationEnvironmentName] = useState<string>('');
|
||||||
|
|
||||||
// 删除确认对话框状态
|
// 删除确认对话框状态
|
||||||
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
||||||
@ -123,6 +129,12 @@ export const TeamEnvironmentManageDialog: React.FC<
|
|||||||
setAppManageDialogOpen(true);
|
setAppManageDialogOpen(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleOpenNotificationDialog = (config: TeamEnvironmentConfig) => {
|
||||||
|
setNotificationEnvironmentId(config.environmentId);
|
||||||
|
setNotificationEnvironmentName(config.environmentName || '');
|
||||||
|
setNotificationDialogOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
const handleOpenDeleteDialog = (config: TeamEnvironmentConfig) => {
|
const handleOpenDeleteDialog = (config: TeamEnvironmentConfig) => {
|
||||||
setDeletingConfig(config);
|
setDeletingConfig(config);
|
||||||
setDeleteDialogOpen(true);
|
setDeleteDialogOpen(true);
|
||||||
@ -202,15 +214,30 @@ export const TeamEnvironmentManageDialog: React.FC<
|
|||||||
)}
|
)}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
{config.notificationEnabled ? (
|
{config.notificationConfig ? (
|
||||||
<Badge variant="outline">
|
<div className="flex flex-col gap-1">
|
||||||
{config.notificationChannelName || '已启用'}
|
{config.notificationConfig.deployNotificationEnabled && (
|
||||||
|
<Badge variant="outline" className="text-xs">
|
||||||
|
部署通知
|
||||||
</Badge>
|
</Badge>
|
||||||
) : (
|
)}
|
||||||
|
{config.notificationConfig.buildNotificationEnabled && (
|
||||||
|
<Badge variant="outline" className="text-xs">
|
||||||
|
构建通知
|
||||||
|
</Badge>
|
||||||
|
)}
|
||||||
|
{!config.notificationConfig.deployNotificationEnabled &&
|
||||||
|
!config.notificationConfig.buildNotificationEnabled && (
|
||||||
<span className="text-muted-foreground text-sm">
|
<span className="text-muted-foreground text-sm">
|
||||||
未启用
|
未启用
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<span className="text-muted-foreground text-sm">
|
||||||
|
未配置
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<span className="text-sm">
|
<span className="text-sm">
|
||||||
@ -247,6 +274,14 @@ export const TeamEnvironmentManageDialog: React.FC<
|
|||||||
<Settings className="h-4 w-4 mr-1" />
|
<Settings className="h-4 w-4 mr-1" />
|
||||||
配置
|
配置
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => handleOpenNotificationDialog(config)}
|
||||||
|
>
|
||||||
|
<Bell className="h-4 w-4 mr-1" />
|
||||||
|
通知
|
||||||
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="sm"
|
size="sm"
|
||||||
@ -337,6 +372,18 @@ export const TeamEnvironmentManageDialog: React.FC<
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* 通知配置对话框 */}
|
||||||
|
{notificationEnvironmentId && (
|
||||||
|
<NotificationConfigDialog
|
||||||
|
open={notificationDialogOpen}
|
||||||
|
onOpenChange={setNotificationDialogOpen}
|
||||||
|
teamId={teamId}
|
||||||
|
environmentId={notificationEnvironmentId}
|
||||||
|
environmentName={notificationEnvironmentName}
|
||||||
|
onSuccess={handleConfigSuccess}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* 删除确认对话框 */}
|
{/* 删除确认对话框 */}
|
||||||
<ConfirmDialog
|
<ConfirmDialog
|
||||||
open={deleteDialogOpen}
|
open={deleteDialogOpen}
|
||||||
|
|||||||
@ -156,16 +156,7 @@ export const getTeamEnvironmentConfig = (teamId: number, environmentId: number)
|
|||||||
/**
|
/**
|
||||||
* 创建团队环境配置
|
* 创建团队环境配置
|
||||||
*/
|
*/
|
||||||
export const createTeamEnvironmentConfig = (data: {
|
export const createTeamEnvironmentConfig = (data: TeamEnvironmentConfigRequest) =>
|
||||||
teamId: number;
|
|
||||||
environmentId: number;
|
|
||||||
approvalRequired?: boolean;
|
|
||||||
approverUserIds?: number[];
|
|
||||||
notificationChannelId?: number;
|
|
||||||
notificationEnabled?: boolean;
|
|
||||||
requireCodeReview?: boolean;
|
|
||||||
remark?: string;
|
|
||||||
}) =>
|
|
||||||
request.post('/api/v1/team-environment-config', data);
|
request.post('/api/v1/team-environment-config', data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -173,16 +164,7 @@ export const createTeamEnvironmentConfig = (data: {
|
|||||||
*/
|
*/
|
||||||
export const updateTeamEnvironmentConfig = (
|
export const updateTeamEnvironmentConfig = (
|
||||||
id: number,
|
id: number,
|
||||||
data: {
|
data: TeamEnvironmentConfigRequest
|
||||||
teamId: number;
|
|
||||||
environmentId: number;
|
|
||||||
approvalRequired?: boolean;
|
|
||||||
approverUserIds?: number[];
|
|
||||||
notificationChannelId?: number;
|
|
||||||
notificationEnabled?: boolean;
|
|
||||||
requireCodeReview?: boolean;
|
|
||||||
remark?: string;
|
|
||||||
}
|
|
||||||
) =>
|
) =>
|
||||||
request.put(`/api/v1/team-environment-config/${id}`, data);
|
request.put(`/api/v1/team-environment-config/${id}`, data);
|
||||||
|
|
||||||
|
|||||||
@ -45,6 +45,19 @@ export interface TeamRequest {
|
|||||||
|
|
||||||
// ==================== 团队环境配置相关 ====================
|
// ==================== 团队环境配置相关 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通知配置
|
||||||
|
*/
|
||||||
|
export interface NotificationConfig {
|
||||||
|
id?: number;
|
||||||
|
teamId?: number;
|
||||||
|
environmentId?: number;
|
||||||
|
notificationChannelId?: number;
|
||||||
|
deployNotificationEnabled?: boolean;
|
||||||
|
buildNotificationEnabled?: boolean;
|
||||||
|
notificationChannelName?: string; // 关联数据
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 团队环境配置响应
|
* 团队环境配置响应
|
||||||
*/
|
*/
|
||||||
@ -53,13 +66,12 @@ export interface TeamEnvironmentConfig extends BaseResponse {
|
|||||||
environmentId: number;
|
environmentId: number;
|
||||||
approvalRequired?: boolean;
|
approvalRequired?: boolean;
|
||||||
approverUserIds?: number[];
|
approverUserIds?: number[];
|
||||||
notificationChannelId?: number;
|
|
||||||
notificationEnabled?: boolean;
|
|
||||||
requireCodeReview?: boolean;
|
requireCodeReview?: boolean;
|
||||||
remark?: string;
|
remark?: string;
|
||||||
|
// 嵌套的通知配置
|
||||||
|
notificationConfig?: NotificationConfig;
|
||||||
// 关联数据
|
// 关联数据
|
||||||
environmentName?: string;
|
environmentName?: string;
|
||||||
notificationChannelName?: string;
|
|
||||||
applicationCount?: number;
|
applicationCount?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,10 +83,16 @@ export interface TeamEnvironmentConfigRequest {
|
|||||||
environmentId: number;
|
environmentId: number;
|
||||||
approvalRequired?: boolean;
|
approvalRequired?: boolean;
|
||||||
approverUserIds?: number[];
|
approverUserIds?: number[];
|
||||||
notificationChannelId?: number;
|
|
||||||
notificationEnabled?: boolean;
|
|
||||||
requireCodeReview?: boolean;
|
requireCodeReview?: boolean;
|
||||||
remark?: string;
|
remark?: string;
|
||||||
|
// 嵌套的通知配置
|
||||||
|
notificationConfig?: {
|
||||||
|
id?: number;
|
||||||
|
notificationChannelId?: number;
|
||||||
|
deployNotificationEnabled?: boolean;
|
||||||
|
buildNotificationEnabled?: boolean;
|
||||||
|
buildFailureFileEnabled?: boolean;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== 团队应用关联相关 ====================
|
// ==================== 团队应用关联相关 ====================
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user