三方系统密码加密

This commit is contained in:
dengqichen 2025-11-12 09:53:10 +08:00
parent 5cc4958eb0
commit 5a924f7aa5
7 changed files with 96 additions and 51 deletions

View File

@ -183,6 +183,40 @@ const SelectSeparator = React.forwardRef<
))
SelectSeparator.displayName = SelectPrimitive.Separator.displayName
// ClearableSelect - 支持清空功能的Select包装组件
interface ClearableSelectProps {
value?: string | number | null;
onValueChange?: (value: string | null) => void;
placeholder?: string;
disabled?: boolean;
children: React.ReactNode;
defaultValue?: string;
open?: boolean;
onOpenChange?: (open: boolean) => void;
}
const ClearableSelect = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Root>,
ClearableSelectProps
>(({ value, onValueChange, children, ...props }, ref) => {
const hasValue = value !== undefined && value !== null && value !== '';
const selectProps = hasValue ? { value: value.toString() } : {};
return (
<Select
key={hasValue ? value : 'empty'}
{...selectProps}
onValueChange={(val) => {
onValueChange?.(val || null);
}}
{...props}
>
{children}
</Select>
);
});
ClearableSelect.displayName = "ClearableSelect";
export {
Select,
SelectGroup,
@ -194,4 +228,5 @@ export {
SelectSeparator,
SelectScrollUpButton,
SelectScrollDownButton,
ClearableSelect,
}

View File

@ -104,7 +104,7 @@ export const EnvironmentTabs: React.FC<EnvironmentTabsProps> = React.memo(({
</CardContent>
</Card>
) : (
<div className="grid grid-cols-6 gap-3">
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4 gap-4">
{env.applications.map((app) => (
<ApplicationCard
key={app.teamApplicationId}

View File

@ -20,8 +20,8 @@ const LoadingState = () => (
{/* 卡片骨架 */}
<div className="space-y-4">
<div className="h-12 w-full bg-slate-100 rounded animate-pulse" />
<div className="grid grid-cols-6 gap-3">
{[1, 2, 3, 4, 5, 6].map((i) => (
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4 gap-4">
{[1, 2, 3, 4].map((i) => (
<div key={i} className="h-48 bg-slate-100 rounded-lg animate-pulse" />
))}
</div>

View File

@ -10,6 +10,7 @@ import {
DialogHeader,
DialogTitle,
DialogFooter,
DialogBody,
} from "@/components/ui/dialog";
import {Button} from "@/components/ui/button";
import {
@ -142,8 +143,8 @@ const ApplicationModal: React.FC<ApplicationModalProps> = ({
<DialogTitle>{isEdit ? '编辑' : '新建'}</DialogTitle>
</DialogHeader>
<Form {...form}>
<form className="space-y-6">
<div className="px-6">
<form className="flex flex-col flex-1 min-h-0">
<DialogBody>
<div className="space-y-4">
<div className="grid grid-cols-3 gap-4">
<FormField
@ -293,8 +294,8 @@ const ApplicationModal: React.FC<ApplicationModalProps> = ({
/>
</div>
</div>
<DialogFooter className="px-6 py-4 border-t mt-0">
</DialogBody>
<DialogFooter>
<Button type="button" variant="outline" onClick={onCancel}>
</Button>

View File

@ -8,6 +8,7 @@ import {
DialogHeader,
DialogTitle,
DialogBody,
DialogFooter,
DialogLoading,
} from '@/components/ui/dialog';
import { Button } from '@/components/ui/button';
@ -266,7 +267,7 @@ const NotificationChannelDialog: React.FC<NotificationChannelDialogProps> = ({
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="max-w-2xl max-h-[90vh] overflow-y-auto">
<DialogContent className="max-w-2xl">
<DialogHeader>
<DialogTitle>
{mode === 'edit' ? '编辑' : '创建'}
@ -277,7 +278,7 @@ const NotificationChannelDialog: React.FC<NotificationChannelDialogProps> = ({
{loading ? (
<DialogLoading />
) : (
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
<form onSubmit={handleSubmit(onSubmit)} id="notification-channel-form" className="space-y-4">
{/* 渠道名称 */}
<div className="space-y-2">
<Label htmlFor="name">
@ -622,20 +623,21 @@ const NotificationChannelDialog: React.FC<NotificationChannelDialogProps> = ({
<p className="text-sm text-destructive">{errors.description.message}</p>
)}
</div>
{/* Submit buttons at the bottom of form */}
<div className="flex justify-end gap-2 pt-4 border-t">
<Button variant="outline" type="button" onClick={() => onOpenChange(false)}>
</Button>
<Button type="submit" disabled={saving}>
{saving && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
{mode === 'edit' ? '保存' : '创建'}
</Button>
</div>
</form>
)}
</DialogBody>
{!loading && (
<DialogFooter>
<Button variant="outline" type="button" onClick={() => onOpenChange(false)}>
</Button>
<Button type="submit" form="notification-channel-form" disabled={saving}>
{saving && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
{mode === 'edit' ? '保存' : '创建'}
</Button>
</DialogFooter>
)}
</DialogContent>
</Dialog>
);

View File

@ -22,7 +22,7 @@ import {
FormMessage,
} from '@/components/ui/form';
import {
Select,
ClearableSelect,
SelectContent,
SelectItem,
SelectTrigger,
@ -51,9 +51,12 @@ const formSchema = z
})
.refine(
(data) => {
// 如果启用了部署通知或构建通知,则通知渠道必填
// 如果启用了任意通知类型,则通知渠道必填
if (
(data.notificationConfig?.deployNotificationEnabled ||data.notificationConfig?.buildNotificationEnabled) && !data.notificationConfig?.notificationChannelId
(data.notificationConfig?.deployNotificationEnabled ||
data.notificationConfig?.buildNotificationEnabled ||
data.notificationConfig?.buildFailureFileEnabled) &&
!data.notificationConfig?.notificationChannelId
) {
return false;
}
@ -138,6 +141,7 @@ export const NotificationConfigDialog: React.FC<
notificationChannelId: undefined,
deployNotificationEnabled: false,
buildNotificationEnabled: false,
buildFailureFileEnabled: false,
},
});
}
@ -171,6 +175,9 @@ export const NotificationConfigDialog: React.FC<
}, [open, teamId, environmentId]);
const handleSubmit = async (data: FormData) => {
console.log('表单提交数据:', data);
console.log('当前配置:', currentConfig);
if (!configId || !currentConfig) {
toast({
title: '错误',
@ -182,34 +189,31 @@ export const NotificationConfigDialog: React.FC<
setSubmitting(true);
try {
// 提交完整的环境配置,只更新通知部分
const payload: any = {
// 构建通知配置对象
let notificationConfig = undefined;
if (data.notificationConfig?.notificationChannelId) {
notificationConfig = {
id: data.notificationConfig.id,
notificationChannelId: data.notificationConfig.notificationChannelId,
deployNotificationEnabled: data.notificationConfig.deployNotificationEnabled ?? false,
buildNotificationEnabled: data.notificationConfig.buildNotificationEnabled ?? false,
buildFailureFileEnabled: data.notificationConfig.buildFailureFileEnabled ?? false,
};
}
// 构建完整的提交数据(保留原有配置,只更新通知部分)
const payload = {
teamId: currentConfig.teamId,
environmentId: currentConfig.environmentId,
approvalRequired: currentConfig.approvalRequired,
approverUserIds: currentConfig.approverUserIds || [],
requireCodeReview: currentConfig.requireCodeReview,
remark: currentConfig.remark,
// 更新通知配置
notificationConfig:
data.notificationConfig &&
(data.notificationConfig.deployNotificationEnabled ||
data.notificationConfig.buildNotificationEnabled ||
data.notificationConfig.buildFailureFileEnabled)
? {
id: data.notificationConfig.id,
notificationChannelId:
data.notificationConfig.notificationChannelId,
deployNotificationEnabled:
data.notificationConfig.deployNotificationEnabled,
buildNotificationEnabled:
data.notificationConfig.buildNotificationEnabled,
buildFailureFileEnabled:
data.notificationConfig.buildFailureFileEnabled,
}
: undefined,
notificationConfig,
};
console.log('最终提交的 payload:', payload);
await updateTeamEnvironmentConfig(configId, payload);
toast({
@ -257,10 +261,10 @@ export const NotificationConfigDialog: React.FC<
render={({ field }) => (
<FormItem>
<FormLabel></FormLabel>
<Select
value={field.value?.toString() || ''}
<ClearableSelect
value={field.value}
onValueChange={(value) =>
field.onChange(value ? Number(value) : undefined)
field.onChange(value ? Number(value) : null)
}
disabled={loadingChannels}
>
@ -268,7 +272,9 @@ export const NotificationConfigDialog: React.FC<
<SelectTrigger
clearable
hasValue={!!field.value}
onClear={() => field.onChange(undefined)}
onClear={() => {
field.onChange(null);
}}
>
<SelectValue
placeholder={
@ -289,7 +295,7 @@ export const NotificationConfigDialog: React.FC<
</SelectItem>
))}
</SelectContent>
</Select>
</ClearableSelect>
<FormMessage />
<div className="text-sm text-muted-foreground">
@ -342,7 +348,7 @@ export const NotificationConfigDialog: React.FC<
)}
/>
{/* 构建通知 */}
{/* 失败日志 */}
<FormField
control={form.control}
name="notificationConfig.buildFailureFileEnabled"

View File

@ -5,6 +5,7 @@ import {
DialogHeader,
DialogTitle,
DialogFooter,
DialogBody,
} from '@/components/ui/dialog';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
@ -199,7 +200,7 @@ const EditModal: React.FC<EditModalProps> = ({ visible, onClose, onSuccess, reco
<DialogHeader>
<DialogTitle>{isEdit ? '编辑流程' : '新建流程'}</DialogTitle>
</DialogHeader>
<div className="grid gap-6 px-6 py-4">
<DialogBody className="grid gap-6">
{/* 流程分类 */}
<div className="space-y-2">
<Label htmlFor="categoryId">
@ -325,7 +326,7 @@ const EditModal: React.FC<EditModalProps> = ({ visible, onClose, onSuccess, reco
/>
</div>
</div>
</DialogBody>
<DialogFooter>
<Button variant="outline" onClick={handleClose} disabled={submitting}>