三方系统密码加密
This commit is contained in:
parent
5cc4958eb0
commit
5a924f7aa5
@ -183,6 +183,40 @@ const SelectSeparator = React.forwardRef<
|
|||||||
))
|
))
|
||||||
SelectSeparator.displayName = SelectPrimitive.Separator.displayName
|
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 {
|
export {
|
||||||
Select,
|
Select,
|
||||||
SelectGroup,
|
SelectGroup,
|
||||||
@ -194,4 +228,5 @@ export {
|
|||||||
SelectSeparator,
|
SelectSeparator,
|
||||||
SelectScrollUpButton,
|
SelectScrollUpButton,
|
||||||
SelectScrollDownButton,
|
SelectScrollDownButton,
|
||||||
|
ClearableSelect,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -104,7 +104,7 @@ export const EnvironmentTabs: React.FC<EnvironmentTabsProps> = React.memo(({
|
|||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</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) => (
|
{env.applications.map((app) => (
|
||||||
<ApplicationCard
|
<ApplicationCard
|
||||||
key={app.teamApplicationId}
|
key={app.teamApplicationId}
|
||||||
|
|||||||
@ -20,8 +20,8 @@ const LoadingState = () => (
|
|||||||
{/* 卡片骨架 */}
|
{/* 卡片骨架 */}
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="h-12 w-full bg-slate-100 rounded animate-pulse" />
|
<div className="h-12 w-full bg-slate-100 rounded animate-pulse" />
|
||||||
<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">
|
||||||
{[1, 2, 3, 4, 5, 6].map((i) => (
|
{[1, 2, 3, 4].map((i) => (
|
||||||
<div key={i} className="h-48 bg-slate-100 rounded-lg animate-pulse" />
|
<div key={i} className="h-48 bg-slate-100 rounded-lg animate-pulse" />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import {
|
|||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
DialogFooter,
|
DialogFooter,
|
||||||
|
DialogBody,
|
||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
import {Button} from "@/components/ui/button";
|
import {Button} from "@/components/ui/button";
|
||||||
import {
|
import {
|
||||||
@ -142,8 +143,8 @@ const ApplicationModal: React.FC<ApplicationModalProps> = ({
|
|||||||
<DialogTitle>{isEdit ? '编辑' : '新建'}应用</DialogTitle>
|
<DialogTitle>{isEdit ? '编辑' : '新建'}应用</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form className="space-y-6">
|
<form className="flex flex-col flex-1 min-h-0">
|
||||||
<div className="px-6">
|
<DialogBody>
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="grid grid-cols-3 gap-4">
|
<div className="grid grid-cols-3 gap-4">
|
||||||
<FormField
|
<FormField
|
||||||
@ -293,8 +294,8 @@ const ApplicationModal: React.FC<ApplicationModalProps> = ({
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</DialogBody>
|
||||||
<DialogFooter className="px-6 py-4 border-t mt-0">
|
<DialogFooter>
|
||||||
<Button type="button" variant="outline" onClick={onCancel}>
|
<Button type="button" variant="outline" onClick={onCancel}>
|
||||||
取消
|
取消
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import {
|
|||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
DialogBody,
|
DialogBody,
|
||||||
|
DialogFooter,
|
||||||
DialogLoading,
|
DialogLoading,
|
||||||
} from '@/components/ui/dialog';
|
} from '@/components/ui/dialog';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
@ -266,7 +267,7 @@ const NotificationChannelDialog: React.FC<NotificationChannelDialogProps> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||||
<DialogContent className="max-w-2xl max-h-[90vh] overflow-y-auto">
|
<DialogContent className="max-w-2xl">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>
|
<DialogTitle>
|
||||||
{mode === 'edit' ? '编辑' : '创建'}通知渠道
|
{mode === 'edit' ? '编辑' : '创建'}通知渠道
|
||||||
@ -277,7 +278,7 @@ const NotificationChannelDialog: React.FC<NotificationChannelDialogProps> = ({
|
|||||||
{loading ? (
|
{loading ? (
|
||||||
<DialogLoading />
|
<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">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="name">
|
<Label htmlFor="name">
|
||||||
@ -622,20 +623,21 @@ const NotificationChannelDialog: React.FC<NotificationChannelDialogProps> = ({
|
|||||||
<p className="text-sm text-destructive">{errors.description.message}</p>
|
<p className="text-sm text-destructive">{errors.description.message}</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</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>
|
</form>
|
||||||
)}
|
)}
|
||||||
</DialogBody>
|
</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>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -22,7 +22,7 @@ import {
|
|||||||
FormMessage,
|
FormMessage,
|
||||||
} from '@/components/ui/form';
|
} from '@/components/ui/form';
|
||||||
import {
|
import {
|
||||||
Select,
|
ClearableSelect,
|
||||||
SelectContent,
|
SelectContent,
|
||||||
SelectItem,
|
SelectItem,
|
||||||
SelectTrigger,
|
SelectTrigger,
|
||||||
@ -51,9 +51,12 @@ const formSchema = z
|
|||||||
})
|
})
|
||||||
.refine(
|
.refine(
|
||||||
(data) => {
|
(data) => {
|
||||||
// 如果启用了部署通知或构建通知,则通知渠道必填
|
// 如果启用了任意通知类型,则通知渠道必填
|
||||||
if (
|
if (
|
||||||
(data.notificationConfig?.deployNotificationEnabled ||data.notificationConfig?.buildNotificationEnabled) && !data.notificationConfig?.notificationChannelId
|
(data.notificationConfig?.deployNotificationEnabled ||
|
||||||
|
data.notificationConfig?.buildNotificationEnabled ||
|
||||||
|
data.notificationConfig?.buildFailureFileEnabled) &&
|
||||||
|
!data.notificationConfig?.notificationChannelId
|
||||||
) {
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -138,6 +141,7 @@ export const NotificationConfigDialog: React.FC<
|
|||||||
notificationChannelId: undefined,
|
notificationChannelId: undefined,
|
||||||
deployNotificationEnabled: false,
|
deployNotificationEnabled: false,
|
||||||
buildNotificationEnabled: false,
|
buildNotificationEnabled: false,
|
||||||
|
buildFailureFileEnabled: false,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -171,6 +175,9 @@ export const NotificationConfigDialog: React.FC<
|
|||||||
}, [open, teamId, environmentId]);
|
}, [open, teamId, environmentId]);
|
||||||
|
|
||||||
const handleSubmit = async (data: FormData) => {
|
const handleSubmit = async (data: FormData) => {
|
||||||
|
console.log('表单提交数据:', data);
|
||||||
|
console.log('当前配置:', currentConfig);
|
||||||
|
|
||||||
if (!configId || !currentConfig) {
|
if (!configId || !currentConfig) {
|
||||||
toast({
|
toast({
|
||||||
title: '错误',
|
title: '错误',
|
||||||
@ -182,34 +189,31 @@ export const NotificationConfigDialog: React.FC<
|
|||||||
|
|
||||||
setSubmitting(true);
|
setSubmitting(true);
|
||||||
try {
|
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,
|
teamId: currentConfig.teamId,
|
||||||
environmentId: currentConfig.environmentId,
|
environmentId: currentConfig.environmentId,
|
||||||
approvalRequired: currentConfig.approvalRequired,
|
approvalRequired: currentConfig.approvalRequired,
|
||||||
approverUserIds: currentConfig.approverUserIds || [],
|
approverUserIds: currentConfig.approverUserIds || [],
|
||||||
requireCodeReview: currentConfig.requireCodeReview,
|
requireCodeReview: currentConfig.requireCodeReview,
|
||||||
remark: currentConfig.remark,
|
remark: currentConfig.remark,
|
||||||
// 更新通知配置
|
notificationConfig,
|
||||||
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,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
console.log('最终提交的 payload:', payload);
|
||||||
|
|
||||||
await updateTeamEnvironmentConfig(configId, payload);
|
await updateTeamEnvironmentConfig(configId, payload);
|
||||||
|
|
||||||
toast({
|
toast({
|
||||||
@ -257,10 +261,10 @@ export const NotificationConfigDialog: React.FC<
|
|||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>通知渠道</FormLabel>
|
<FormLabel>通知渠道</FormLabel>
|
||||||
<Select
|
<ClearableSelect
|
||||||
value={field.value?.toString() || ''}
|
value={field.value}
|
||||||
onValueChange={(value) =>
|
onValueChange={(value) =>
|
||||||
field.onChange(value ? Number(value) : undefined)
|
field.onChange(value ? Number(value) : null)
|
||||||
}
|
}
|
||||||
disabled={loadingChannels}
|
disabled={loadingChannels}
|
||||||
>
|
>
|
||||||
@ -268,7 +272,9 @@ export const NotificationConfigDialog: React.FC<
|
|||||||
<SelectTrigger
|
<SelectTrigger
|
||||||
clearable
|
clearable
|
||||||
hasValue={!!field.value}
|
hasValue={!!field.value}
|
||||||
onClear={() => field.onChange(undefined)}
|
onClear={() => {
|
||||||
|
field.onChange(null);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<SelectValue
|
<SelectValue
|
||||||
placeholder={
|
placeholder={
|
||||||
@ -289,7 +295,7 @@ export const NotificationConfigDialog: React.FC<
|
|||||||
</SelectItem>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</ClearableSelect>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
<div className="text-sm text-muted-foreground">
|
<div className="text-sm text-muted-foreground">
|
||||||
选择通知渠道后,启用下方的通知类型
|
选择通知渠道后,启用下方的通知类型
|
||||||
@ -342,7 +348,7 @@ export const NotificationConfigDialog: React.FC<
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* 构建通知 */}
|
{/* 失败日志 */}
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="notificationConfig.buildFailureFileEnabled"
|
name="notificationConfig.buildFailureFileEnabled"
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import {
|
|||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
DialogFooter,
|
DialogFooter,
|
||||||
|
DialogBody,
|
||||||
} from '@/components/ui/dialog';
|
} from '@/components/ui/dialog';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
@ -199,7 +200,7 @@ const EditModal: React.FC<EditModalProps> = ({ visible, onClose, onSuccess, reco
|
|||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>{isEdit ? '编辑流程' : '新建流程'}</DialogTitle>
|
<DialogTitle>{isEdit ? '编辑流程' : '新建流程'}</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<div className="grid gap-6 px-6 py-4">
|
<DialogBody className="grid gap-6">
|
||||||
{/* 流程分类 */}
|
{/* 流程分类 */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="categoryId">
|
<Label htmlFor="categoryId">
|
||||||
@ -325,7 +326,7 @@ const EditModal: React.FC<EditModalProps> = ({ visible, onClose, onSuccess, reco
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</DialogBody>
|
||||||
|
|
||||||
<DialogFooter>
|
<DialogFooter>
|
||||||
<Button variant="outline" onClick={handleClose} disabled={submitting}>
|
<Button variant="outline" onClick={handleClose} disabled={submitting}>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user