From c17d27c98778a9fb70bda083f949c4e56120ed1e Mon Sep 17 00:00:00 2001 From: Fu Diwei Date: Thu, 4 Sep 2025 19:38:04 +0800 Subject: [PATCH] feat(ui): NotificationProviderPicker --- .../provider/ACMEDns01ProviderSelect.tsx | 9 +- .../provider/AccessProviderPicker.tsx | 182 ++++++++-------- .../provider/AccessProviderSelect.tsx | 34 +-- .../components/provider/CAProviderSelect.tsx | 9 +- .../provider/DeploymentProviderPicker.tsx | 184 ++++++++-------- .../provider/DeploymentProviderSelect.tsx | 9 +- .../provider/NotificationProviderPicker.tsx | 127 +++++++++++ .../provider/NotificationProviderSelect.tsx | 9 +- ui/src/components/provider/_shared.ts | 42 ++++ .../forms/BizDeployNodeConfigDrawer.tsx | 2 +- .../forms/BizDeployNodeConfigForm.tsx | 7 +- .../forms/BizNotifyNodeConfigDrawer.tsx | 7 +- .../forms/BizNotifyNodeConfigForm.tsx | 203 ++++++++---------- ui/src/i18n/locales/en/nls.provider.json | 3 +- .../i18n/locales/en/nls.workflow.nodes.json | 7 +- ui/src/i18n/locales/zh/nls.provider.json | 3 +- .../i18n/locales/zh/nls.workflow.nodes.json | 1 + 17 files changed, 489 insertions(+), 349 deletions(-) create mode 100644 ui/src/components/provider/NotificationProviderPicker.tsx create mode 100644 ui/src/components/provider/_shared.ts diff --git a/ui/src/components/provider/ACMEDns01ProviderSelect.tsx b/ui/src/components/provider/ACMEDns01ProviderSelect.tsx index 1861a847b..4d137e484 100644 --- a/ui/src/components/provider/ACMEDns01ProviderSelect.tsx +++ b/ui/src/components/provider/ACMEDns01ProviderSelect.tsx @@ -1,13 +1,12 @@ import { useMemo } from "react"; import { useTranslation } from "react-i18next"; -import { Avatar, Select, type SelectProps, Typography, theme } from "antd"; +import { Avatar, Select, Typography, theme } from "antd"; import { type ACMEDns01Provider, acmeDns01ProvidersMap } from "@/domain/provider"; -export interface ACMEDns01ProviderSelectProps - extends Omit { - onFilter?: (value: string, option: ACMEDns01Provider) => boolean; -} +import { type SharedSelectProps } from "./_shared"; + +export interface ACMEDns01ProviderSelectProps extends SharedSelectProps {} const ACMEDns01ProviderSelect = ({ onFilter, ...props }: ACMEDns01ProviderSelectProps) => { const { t } = useTranslation(); diff --git a/ui/src/components/provider/AccessProviderPicker.tsx b/ui/src/components/provider/AccessProviderPicker.tsx index a4d1c109f..3b7f1c5aa 100644 --- a/ui/src/components/provider/AccessProviderPicker.tsx +++ b/ui/src/components/provider/AccessProviderPicker.tsx @@ -1,37 +1,31 @@ import { useMemo, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; -import { useMount, useSize } from "ahooks"; +import { useMount } from "ahooks"; import { Avatar, Card, Empty, Input, type InputRef, Tag, Tooltip, Typography } from "antd"; import Show from "@/components/Show"; import { ACCESS_USAGES, type AccessProvider, type AccessUsageType, accessProvidersMap } from "@/domain/provider"; import { mergeCls } from "@/utils/css"; -export interface AccessProviderPickerProps { - className?: string; - style?: React.CSSProperties; - autoFocus?: boolean; - gap?: number | "small" | "middle" | "large"; - placeholder?: string; +import { type SharedPickerProps, usePickerWrapperCols } from "./_shared"; + +export interface AccessProviderPickerProps extends SharedPickerProps { showOptionTags?: boolean | { [key in AccessUsageType]?: boolean }; - onFilter?: (value: string, option: AccessProvider) => boolean; - onSelect?: (value: string) => void; } -const AccessProviderPicker = ({ className, style, autoFocus, gap = "middle", placeholder, showOptionTags, onFilter, onSelect }: AccessProviderPickerProps) => { +const AccessProviderPicker = ({ + className, + style, + autoFocus, + gap = "middle", + placeholder, + showOptionTags, + showSearch = true, + onFilter, + onSelect, +}: AccessProviderPickerProps) => { const { t } = useTranslation(); - const wrapperRef = useRef(null); - const wrapperSize = useSize(wrapperRef); - - const [keyword, setKeyword] = useState(); - const keywordInputRef = useRef(null); - useMount(() => { - if (autoFocus) { - setTimeout(() => keywordInputRef.current?.focus(), 1); - } - }); - const showOptionTagForDNS = useMemo(() => { return typeof showOptionTags === "object" ? !!showOptionTags?.[ACCESS_USAGES.DNS] : !!showOptionTags; }, [showOptionTags]); @@ -48,6 +42,16 @@ const AccessProviderPicker = ({ className, style, autoFocus, gap = "middle", pla return showOptionTagForDNS || showOptionTagForHosting || showOptionTagForCA || showOptionTagForNotification; }, [showOptionTagForDNS, showOptionTagForHosting, showOptionTagForCA, showOptionTagForNotification]); + const { wrapperElRef, cols } = usePickerWrapperCols(showOptionTagAnyhow ? 240 : 200); + + const [keyword, setKeyword] = useState(); + const keywordInputRef = useRef(null); + useMount(() => { + if (autoFocus) { + setTimeout(() => keywordInputRef.current?.focus(), 1); + } + }); + const providers = useMemo(() => { return Array.from(accessProvidersMap.values()) .filter((provider) => { @@ -66,86 +70,80 @@ const AccessProviderPicker = ({ className, style, autoFocus, gap = "middle", pla return true; }); }, [onFilter, keyword]); - const providerCols = useMemo(() => { - if (!wrapperSize) { - return 1; - } - - const cols = Math.floor(wrapperSize.width / (showOptionTagAnyhow ? 240 : 200)); - return Math.min(9, Math.max(1, cols)); - }, [wrapperSize, showOptionTagAnyhow]); const handleProviderTypeSelect = (value: string) => { onSelect?.(value); }; return ( -
- setKeyword(e.target.value.trim())} /> +
+ +
+ setKeyword(e.target.value.trim())} /> +
+
-
- 0} fallback={}> -
- {providers.map((provider) => { - return ( -
- { - if (provider.builtin) { - return; - } + 0} fallback={}> +
+ {providers.map((provider) => { + return ( +
+ { + if (provider.builtin) { + return; + } - handleProviderTypeSelect(provider.type); - }} - > -
-
- } shape="square" size={32} /> -
-
-
- - {t(provider.name) || "\u00A0"} - -
- -
- - {t("access.props.provider.builtin")} - - - {t("access.props.provider.usage.dns")} - - - {t("access.props.provider.usage.hosting")} - - - {t("access.props.provider.usage.ca")} - - - {t("access.props.provider.usage.notification")} - -
-
-
+ handleProviderTypeSelect(provider.type); + }} + > +
+
+ } shape="square" size={32} />
- -
- ); - })} -
- -
+
+
+ + {t(provider.name) || "\u00A0"} + +
+ +
+ + {t("access.props.provider.builtin")} + + + {t("access.props.provider.usage.dns")} + + + {t("access.props.provider.usage.hosting")} + + + {t("access.props.provider.usage.ca")} + + + {t("access.props.provider.usage.notification")} + +
+
+
+
+
+
+ ); + })} +
+
); }; diff --git a/ui/src/components/provider/AccessProviderSelect.tsx b/ui/src/components/provider/AccessProviderSelect.tsx index 7b18c9c96..a207144ab 100644 --- a/ui/src/components/provider/AccessProviderSelect.tsx +++ b/ui/src/components/provider/AccessProviderSelect.tsx @@ -1,14 +1,14 @@ import { useMemo } from "react"; import { useTranslation } from "react-i18next"; -import { Avatar, Select, type SelectProps, Tag, Typography, theme } from "antd"; +import { Avatar, Select, Tag, Typography, theme } from "antd"; import Show from "@/components/Show"; import { ACCESS_USAGES, type AccessProvider, type AccessUsageType, accessProvidersMap } from "@/domain/provider"; -export interface AccessProviderSelectProps - extends Omit { +import { type SharedSelectProps } from "./_shared"; + +export interface AccessProviderSelectProps extends SharedSelectProps { showOptionTags?: boolean | { [key in AccessUsageType]?: boolean }; - onFilter?: (value: string, option: AccessProvider) => boolean; } const AccessProviderSelect = ({ showOptionTags, onFilter, ...props }: AccessProviderSelectProps = { showOptionTags: true }) => { @@ -16,6 +16,19 @@ const AccessProviderSelect = ({ showOptionTags, onFilter, ...props }: AccessProv const { token: themeToken } = theme.useToken(); + const showOptionTagForDNS = useMemo(() => { + return typeof showOptionTags === "object" ? !!showOptionTags?.[ACCESS_USAGES.DNS] : !!showOptionTags; + }, [showOptionTags]); + const showOptionTagForHosting = useMemo(() => { + return typeof showOptionTags === "object" ? !!showOptionTags?.[ACCESS_USAGES.HOSTING] : !!showOptionTags; + }, [showOptionTags]); + const showOptionTagForCA = useMemo(() => { + return typeof showOptionTags === "object" ? !!showOptionTags?.[ACCESS_USAGES.CA] : !!showOptionTags; + }, [showOptionTags]); + const showOptionTagForNotification = useMemo(() => { + return typeof showOptionTags === "object" ? !!showOptionTags?.[ACCESS_USAGES.NOTIFICATION] : !!showOptionTags; + }, [showOptionTags]); + const options = useMemo>(() => { return Array.from(accessProvidersMap.values()) .filter((provider) => { @@ -34,19 +47,6 @@ const AccessProviderSelect = ({ showOptionTags, onFilter, ...props }: AccessProv })); }, [onFilter]); - const showOptionTagForDNS = useMemo(() => { - return typeof showOptionTags === "object" ? !!showOptionTags?.[ACCESS_USAGES.DNS] : !!showOptionTags; - }, [showOptionTags]); - const showOptionTagForHosting = useMemo(() => { - return typeof showOptionTags === "object" ? !!showOptionTags?.[ACCESS_USAGES.HOSTING] : !!showOptionTags; - }, [showOptionTags]); - const showOptionTagForCA = useMemo(() => { - return typeof showOptionTags === "object" ? !!showOptionTags?.[ACCESS_USAGES.CA] : !!showOptionTags; - }, [showOptionTags]); - const showOptionTagForNotification = useMemo(() => { - return typeof showOptionTags === "object" ? !!showOptionTags?.[ACCESS_USAGES.NOTIFICATION] : !!showOptionTags; - }, [showOptionTags]); - const renderOption = (key: string) => { const provider = accessProvidersMap.get(key) ?? ({ type: "", name: "", icon: "", usages: [] } as unknown as AccessProvider); return ( diff --git a/ui/src/components/provider/CAProviderSelect.tsx b/ui/src/components/provider/CAProviderSelect.tsx index ab89bd065..a8106a9c7 100644 --- a/ui/src/components/provider/CAProviderSelect.tsx +++ b/ui/src/components/provider/CAProviderSelect.tsx @@ -1,13 +1,12 @@ import { useMemo } from "react"; import { useTranslation } from "react-i18next"; -import { Avatar, Select, type SelectProps, Typography, theme } from "antd"; +import { Avatar, Select, Typography, theme } from "antd"; import { type CAProvider, caProvidersMap } from "@/domain/provider"; -export interface CAProviderSelectProps - extends Omit { - onFilter?: (value: string, option: CAProvider) => boolean; -} +import { type SharedSelectProps } from "./_shared"; + +export interface CAProviderSelectProps extends SharedSelectProps {} const CAProviderSelect = ({ onFilter, ...props }: CAProviderSelectProps) => { const { t } = useTranslation(); diff --git a/ui/src/components/provider/DeploymentProviderPicker.tsx b/ui/src/components/provider/DeploymentProviderPicker.tsx index bf3c2a7e6..68dbf767a 100644 --- a/ui/src/components/provider/DeploymentProviderPicker.tsx +++ b/ui/src/components/provider/DeploymentProviderPicker.tsx @@ -1,6 +1,6 @@ import { useMemo, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; -import { useMount, useSize } from "ahooks"; +import { useMount } from "ahooks"; import { Avatar, Card, Checkbox, Empty, Flex, Input, type InputRef, Tabs, Tooltip, Typography } from "antd"; import Show from "@/components/Show"; @@ -9,24 +9,26 @@ import { useZustandShallowSelector } from "@/hooks"; import { useAccessesStore } from "@/stores/access"; import { mergeCls } from "@/utils/css"; -export interface DeploymentProviderPickerProps { - className?: string; - style?: React.CSSProperties; - autoFocus?: boolean; - gap?: number | "small" | "middle" | "large"; - placeholder?: string; - onFilter?: (value: string, option: DeploymentProvider) => boolean; - onSelect?: (value: string) => void; -} +import { type SharedPickerProps, usePickerWrapperCols } from "./_shared"; -const DeploymentProviderPicker = ({ className, style, autoFocus, gap = "middle", placeholder, onFilter, onSelect }: DeploymentProviderPickerProps) => { +export interface DeploymentProviderPickerProps extends SharedPickerProps {} + +const DeploymentProviderPicker = ({ + className, + style, + autoFocus, + gap = "middle", + placeholder, + showSearch = true, + onFilter, + onSelect, +}: DeploymentProviderPickerProps) => { const { t } = useTranslation(); const { accesses, fetchAccesses } = useAccessesStore(useZustandShallowSelector(["accesses", "fetchAccesses"])); useMount(() => fetchAccesses(false)); - const wrapperRef = useRef(null); - const wrapperSize = useSize(wrapperRef); + const { wrapperElRef, cols } = usePickerWrapperCols(320); const [isAvailableOnly, setIsAvailableOnly] = useState(true); @@ -72,99 +74,91 @@ const DeploymentProviderPicker = ({ className, style, autoFocus, gap = "middle", return true; }); }, [onFilter, accesses, isAvailableOnly, category, keyword]); - const providerCols = useMemo(() => { - if (!wrapperSize) { - return 1; - } - - const cols = Math.floor(wrapperSize.width / 320); - return Math.min(9, Math.max(1, cols)); - }, [wrapperSize]); const handleProviderTypeSelect = (value: string) => { onSelect?.(value); }; return ( -
- setKeyword(e.target.value.trim())} /> +
+ +
+ setKeyword(e.target.value.trim())} /> +
+
-
- - setIsAvailableOnly(!isAvailableOnly)}> - {t("provider.text.show_available_hosting_provider_only")} - - -
+ + setIsAvailableOnly(!isAvailableOnly)}> + {t("provider.text.show_available_provider_only")} + + -
- - ({ - key: key, - label: t(`provider.category.${key}`), - }))} - size="small" - tabBarStyle={{ marginLeft: "-1rem" }} - tabPosition="left" - onChange={(key) => setCategory(key)} - /> + + ({ + key: key, + label: t(`provider.category.${key}`), + }))} + size="small" + tabBarStyle={{ marginLeft: "-1rem" }} + tabPosition="left" + onChange={(key) => setCategory(key)} + /> -
- 0} fallback={}> -
- {providers.map((provider) => { - return ( -
- { - handleProviderTypeSelect(provider.type); - }} - > -
- } shape="square" size={28} /> -
-
- - {t(provider.name) || "\u00A0"} - -
+
+ 0} fallback={}> +
+ {providers.map((provider) => { + return ( +
+ { + handleProviderTypeSelect(provider.type); + }} + > +
+ } shape="square" size={28} /> +
+
+ + {t(provider.name) || "\u00A0"} +
- -
- ); - })} -
- -
- -
+
+ +
+ ); + })} +
+ +
+
); }; diff --git a/ui/src/components/provider/DeploymentProviderSelect.tsx b/ui/src/components/provider/DeploymentProviderSelect.tsx index ffe3d1d3a..cd6f5b6d5 100644 --- a/ui/src/components/provider/DeploymentProviderSelect.tsx +++ b/ui/src/components/provider/DeploymentProviderSelect.tsx @@ -1,13 +1,12 @@ import { useMemo } from "react"; import { useTranslation } from "react-i18next"; -import { Avatar, Select, type SelectProps, Typography, theme } from "antd"; +import { Avatar, Select, Typography, theme } from "antd"; import { type DeploymentProvider, deploymentProvidersMap } from "@/domain/provider"; -export interface DeploymentProviderSelectProps - extends Omit { - onFilter?: (value: string, option: DeploymentProvider) => boolean; -} +import { type SharedSelectProps } from "./_shared"; + +export interface DeploymentProviderSelectProps extends SharedSelectProps {} const DeploymentProviderSelect = ({ onFilter, ...props }: DeploymentProviderSelectProps) => { const { t } = useTranslation(); diff --git a/ui/src/components/provider/NotificationProviderPicker.tsx b/ui/src/components/provider/NotificationProviderPicker.tsx new file mode 100644 index 000000000..c7d4310be --- /dev/null +++ b/ui/src/components/provider/NotificationProviderPicker.tsx @@ -0,0 +1,127 @@ +import { useMemo, useRef, useState } from "react"; +import { useTranslation } from "react-i18next"; +import { useMount } from "ahooks"; +import { Avatar, Card, Checkbox, Empty, Flex, Input, type InputRef, Tooltip, Typography } from "antd"; + +import Show from "@/components/Show"; +import { type NotificationProvider, notificationProvidersMap } from "@/domain/provider"; +import { useZustandShallowSelector } from "@/hooks"; +import { useAccessesStore } from "@/stores/access"; +import { mergeCls } from "@/utils/css"; + +import { type SharedPickerProps, usePickerWrapperCols } from "./_shared"; + +export interface NotificationProviderPickerProps extends SharedPickerProps {} + +const NotificationProviderPicker = ({ + className, + style, + autoFocus, + gap = "middle", + placeholder, + showSearch = true, + onFilter, + onSelect, +}: NotificationProviderPickerProps) => { + const { t } = useTranslation(); + + const { accesses, fetchAccesses } = useAccessesStore(useZustandShallowSelector(["accesses", "fetchAccesses"])); + useMount(() => fetchAccesses(false)); + + const { wrapperElRef, cols } = usePickerWrapperCols(320); + + const [isAvailableOnly, setIsAvailableOnly] = useState(true); + + const [keyword, setKeyword] = useState(); + const keywordInputRef = useRef(null); + useMount(() => { + if (autoFocus) { + setTimeout(() => keywordInputRef.current?.focus(), 1); + } + }); + + const providers = useMemo(() => { + return Array.from(notificationProvidersMap.values()) + .filter((provider) => { + if (onFilter) { + return onFilter(provider.type, provider); + } + + return true; + }) + .filter((provider) => { + if (isAvailableOnly) { + return accesses.some((access) => access.provider === provider.provider); + } + + return true; + }) + .filter((provider) => { + if (keyword) { + const value = keyword.toLowerCase(); + return provider.type.toLowerCase().includes(value) || t(provider.name).toLowerCase().includes(value); + } + + return true; + }); + }, [onFilter, accesses, isAvailableOnly, keyword]); + + const handleProviderTypeSelect = (value: string) => { + onSelect?.(value); + }; + + return ( +
+ +
+ setKeyword(e.target.value.trim())} /> +
+
+ + + setIsAvailableOnly(!isAvailableOnly)}> + {t("provider.text.show_available_provider_only")} + + + + 0} fallback={}> +
+ {providers.map((provider) => { + return ( +
+ { + handleProviderTypeSelect(provider.type); + }} + > +
+ } shape="square" size={28} /> +
+
+ + {t(provider.name) || "\u00A0"} + +
+
+
+
+
+ ); + })} +
+
+
+ ); +}; + +export default NotificationProviderPicker; diff --git a/ui/src/components/provider/NotificationProviderSelect.tsx b/ui/src/components/provider/NotificationProviderSelect.tsx index 71b9aee08..f7f86ad16 100644 --- a/ui/src/components/provider/NotificationProviderSelect.tsx +++ b/ui/src/components/provider/NotificationProviderSelect.tsx @@ -1,13 +1,12 @@ import { useMemo } from "react"; import { useTranslation } from "react-i18next"; -import { Avatar, Select, type SelectProps, Typography, theme } from "antd"; +import { Avatar, Select, Typography, theme } from "antd"; import { type NotificationProvider, notificationProvidersMap } from "@/domain/provider"; -export interface NotificationProviderSelectProps - extends Omit { - onFilter?: (value: string, option: NotificationProvider) => boolean; -} +import { type SharedSelectProps } from "./_shared"; + +export interface NotificationProviderSelectProps extends SharedSelectProps {} const NotificationProviderSelect = ({ onFilter, ...props }: NotificationProviderSelectProps) => { const { t } = useTranslation(); diff --git a/ui/src/components/provider/_shared.ts b/ui/src/components/provider/_shared.ts new file mode 100644 index 000000000..3c8f32482 --- /dev/null +++ b/ui/src/components/provider/_shared.ts @@ -0,0 +1,42 @@ +import { useMemo, useRef } from "react"; +import { useSize } from "ahooks"; +import { type SelectProps } from "antd"; + +type Provider = { type: string; name: string }; + +export interface SharedSelectProps + extends Omit { + className?: string; + style?: React.CSSProperties; + onFilter?: (value: string, option: T) => boolean; +} + +export interface SharedPickerProps { + className?: string; + style?: React.CSSProperties; + autoFocus?: boolean; + gap?: number | "small" | "middle" | "large"; + placeholder?: string; + showSearch?: boolean; + onFilter?: (value: string, option: T) => boolean; + onSelect?: (value: string) => void; +} + +export const usePickerWrapperCols = (width: number) => { + const wrapperElRef = useRef(null); + const wrapperSize = useSize(wrapperElRef); + + const cols = useMemo(() => { + if (!wrapperSize) { + return 1; + } + + const cols = Math.floor(wrapperSize.width / width); + return Math.min(9, Math.max(1, cols)); + }, [wrapperSize, width]); + + return { + wrapperElRef, + cols, + }; +}; diff --git a/ui/src/components/workflow/designer/forms/BizDeployNodeConfigDrawer.tsx b/ui/src/components/workflow/designer/forms/BizDeployNodeConfigDrawer.tsx index bab398d4c..2058f9769 100644 --- a/ui/src/components/workflow/designer/forms/BizDeployNodeConfigDrawer.tsx +++ b/ui/src/components/workflow/designer/forms/BizDeployNodeConfigDrawer.tsx @@ -28,7 +28,7 @@ const BizDeployNodeConfigDrawer = ({ node, ...props }: BizDeployNodeConfigDrawer return ( { // 切换部署目标时重置表单,避免其他部署目标的配置字段影响当前部署目标 if (initialValues?.provider === value) { - formInst.resetFields(); + formInst.resetFields(["providerConfig"]); } else { const oldValues = formInst.getFieldsValue(); const newValues: Record = {}; @@ -537,10 +537,7 @@ const BizDeployNodeConfigForm = ({ node, ...props }: BizDeployNodeConfigFormProp showSearch onFilter={(_, option) => { if (option.reserve) return false; - if (fieldProvider) return deploymentProvidersMap.get(fieldProvider)?.provider === option.provider; - - const provider = accessProvidersMap.get(option.provider); - return !!provider?.usages?.includes(ACCESS_USAGES.HOSTING); + return deploymentProvidersMap.get(fieldProvider)?.provider === option.provider; }} /> diff --git a/ui/src/components/workflow/designer/forms/BizNotifyNodeConfigDrawer.tsx b/ui/src/components/workflow/designer/forms/BizNotifyNodeConfigDrawer.tsx index f5b3e247f..15429d28f 100644 --- a/ui/src/components/workflow/designer/forms/BizNotifyNodeConfigDrawer.tsx +++ b/ui/src/components/workflow/designer/forms/BizNotifyNodeConfigDrawer.tsx @@ -23,11 +23,12 @@ const BizNotifyNodeConfigDrawer = ({ node, ...props }: BizNotifyNodeConfigDrawer const [formInst] = Form.useForm(); + const fieldProvider = Form.useWatch("provider", { form: formInst, preserve: true }); + return ( { - // 通常情况下每个授权信息只对应一个消息通知提供商,此时无需显示消息通知提供商字段; - // 如果对应多个,则显示。 - if (fieldProviderAccessId) { - const access = accesses.find((e) => e.id === fieldProviderAccessId); - const providers = Array.from(notificationProvidersMap.values()).filter((e) => e.provider === access?.provider); - setShowProvider(providers.length > 1); - } else { - setShowProvider(false); - } - }, [accesses, fieldProviderAccessId]); - - const handleProviderSelect = (value: string) => { - // 切换消息通知提供商时联动授权信息 - if (initialValues?.provider === value) { - formInst.setFieldValue("providerAccessId", initialValues?.providerAccessId); - } else { - if (notificationProvidersMap.get(fieldProvider)?.provider !== notificationProvidersMap.get(value)?.provider) { - formInst.setFieldValue("providerAccessId", void 0); - } - } + const handleProviderPick = (value: string) => { + formInst.setFieldValue("provider", value); }; - const handleProviderAccessSelect = (value: string) => { - // 切换授权信息时联动消息通知提供商 - const access = accesses.find((access) => access.id === value); - const provider = Array.from(notificationProvidersMap.values()).find((provider) => provider.provider === access?.provider); - if (fieldProvider !== provider?.type) { - formInst.setFieldValue("provider", provider?.type); + const handleProviderSelect = (value?: string | undefined) => { + // 切换通知渠道时重置表单,避免其他通知渠道的配置字段影响当前通知渠道 + if (initialValues?.provider === value) { + formInst.resetFields(["providerConfig"]); + } else { + if (notificationProvidersMap.get(fieldProvider)?.provider !== notificationProvidersMap.get(value!)?.provider) { + formInst.setFieldValue("providerAccessId", void 0); + } } }; return (
-
- - - + + + - - - -
+
+
+ + + -
- - - {t("workflow_node.notify.form_anchor.channel.title")} - - + + + +
-
diff --git a/ui/src/i18n/locales/en/nls.provider.json b/ui/src/i18n/locales/en/nls.provider.json index 0607dbbfa..6cf80f38a 100644 --- a/ui/src/i18n/locales/en/nls.provider.json +++ b/ui/src/i18n/locales/en/nls.provider.json @@ -194,6 +194,7 @@ "provider.category.nas": "NAS", "provider.category.other": "Other", + "provider.text.nodata": "No providers available", "provider.text.default_ca_provider": "(Default) Follow global settings", - "provider.text.show_available_hosting_provider_only": "Only display available providers (with added credentials)" + "provider.text.show_available_provider_only": "Only display available providers (with added credentials)" } diff --git a/ui/src/i18n/locales/en/nls.workflow.nodes.json b/ui/src/i18n/locales/en/nls.workflow.nodes.json index 6971b368c..bbef59cf8 100644 --- a/ui/src/i18n/locales/en/nls.workflow.nodes.json +++ b/ui/src/i18n/locales/en/nls.workflow.nodes.json @@ -147,9 +147,9 @@ "workflow_node.deploy.form.certificate_output_node_id.label": "Certificate to deploy", "workflow_node.deploy.form.certificate_output_node_id.placeholder": "Please select certificate to deploy", "workflow_node.deploy.form.certificate_output_node_id.help": "Notes: The certificate to be deployed comes from the previous nodes of application or upload.", - "workflow_node.deploy.form.provider.label": "Deploy target", - "workflow_node.deploy.form.provider.placeholder": "Please select deploy target", - "workflow_node.deploy.form.provider.search.placeholder": "Search deploy target ...", + "workflow_node.deploy.form.provider.label": "Deployment target", + "workflow_node.deploy.form.provider.placeholder": "Please select deployment target", + "workflow_node.deploy.form.provider.search.placeholder": "Search deployment target ...", "workflow_node.deploy.form.provider_access.label": "Hosting provider credential", "workflow_node.deploy.form.provider_access.placeholder": "Please select an credential of Hosting provider", "workflow_node.deploy.form.provider_access.tooltip": "Used to invoke API during deployment.", @@ -997,6 +997,7 @@ "workflow_node.notify.form.message.placeholder": "Please enter message", "workflow_node.notify.form.provider.label": "Notification channel", "workflow_node.notify.form.provider.placeholder": "Please select notification channel", + "workflow_node.notify.form.provider.search.placeholder": "Search notification channel ...", "workflow_node.notify.form.provider_access.label": "Notification provider credential", "workflow_node.notify.form.provider_access.placeholder": "Please select an credential of notification provider", "workflow_node.notify.form.provider_access.button": "Create", diff --git a/ui/src/i18n/locales/zh/nls.provider.json b/ui/src/i18n/locales/zh/nls.provider.json index 2f1b64297..f92dcbf0c 100644 --- a/ui/src/i18n/locales/zh/nls.provider.json +++ b/ui/src/i18n/locales/zh/nls.provider.json @@ -194,6 +194,7 @@ "provider.category.nas": "NAS", "provider.category.other": "其他", + "provider.text.nodata": "暂无提供商", "provider.text.default_ca_provider": "(默认)不指定,跟随全局设置", - "provider.text.show_available_hosting_provider_only": "仅列出可用的提供商(即已添加过授权凭据的)" + "provider.text.show_available_provider_only": "仅列出可用的提供商(即已添加过授权凭据的)" } diff --git a/ui/src/i18n/locales/zh/nls.workflow.nodes.json b/ui/src/i18n/locales/zh/nls.workflow.nodes.json index a83826c03..dba294320 100644 --- a/ui/src/i18n/locales/zh/nls.workflow.nodes.json +++ b/ui/src/i18n/locales/zh/nls.workflow.nodes.json @@ -995,6 +995,7 @@ "workflow_node.notify.form.message.placeholder": "请输入通知内容", "workflow_node.notify.form.provider.label": "通知渠道", "workflow_node.notify.form.provider.placeholder": "请选择通知渠道", + "workflow_node.notify.form.provider.search.placeholder": "搜索通知渠道……", "workflow_node.notify.form.provider_access.label": "通知渠道授权", "workflow_node.notify.form.provider_access.placeholder": "请选择通知渠道授权", "workflow_node.notify.form.provider_access.button": "新建",