三方系统密码加密
This commit is contained in:
parent
5cc4958eb0
commit
5a924f7aa5
@ -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,
|
||||
}
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
);
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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}>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user