feat: enhance email configuration and body input components with support for dynamic node variables

This commit is contained in:
twwu 2026-01-21 10:30:48 +08:00
parent c5721184e9
commit 2b28074f4c
7 changed files with 58 additions and 13 deletions

View File

@ -1,4 +1,5 @@
import type { ValueSelector } from '../../workflow/types'
import { uniqBy } from 'es-toolkit/compat'
import { SupportUploadFileTypes } from '../../workflow/types'
export const CONTEXT_PLACEHOLDER_TEXT = '{{#context#}}'
@ -57,7 +58,8 @@ export const getInputVars = (text: string): ValueSelector[] => {
return valueSelector
})
return inputVars
const uniqueInputVars = uniqBy(inputVars, item => item.join('.'))
return uniqueInputVars
}
return []
}

View File

@ -1,4 +1,8 @@
import type { EmailConfig } from '../../types'
import type {
Node,
NodeOutPutVar,
} from '@/app/components/workflow/types'
import { RiBugLine, RiCloseLine } from '@remixicon/react'
import { noop } from 'es-toolkit/compat'
import { memo, useCallback, useState } from 'react'
@ -20,6 +24,8 @@ type EmailConfigureModalProps = {
onClose: () => void
onConfirm: (data: EmailConfig) => void
config?: EmailConfig
nodesOutputVars?: NodeOutPutVar[]
availableNodes?: Node[]
}
const EmailConfigureModal = ({
@ -27,6 +33,8 @@ const EmailConfigureModal = ({
onClose,
onConfirm,
config,
nodesOutputVars = [],
availableNodes = [],
}: EmailConfigureModalProps) => {
const { t } = useTranslation()
const email = useAppContextWithSelector(s => s.userProfile.email)
@ -110,6 +118,8 @@ const EmailConfigureModal = ({
<MailBodyInput
value={body}
onChange={setBody}
nodesOutputVars={nodesOutputVars}
availableNodes={availableNodes}
/>
</div>
<div>

View File

@ -1,18 +1,30 @@
import type {
Node,
NodeOutPutVar,
} from '@/app/components/workflow/types'
import { useTranslation } from 'react-i18next'
import PromptEditor from '@/app/components/base/prompt-editor'
import Placeholder from '@/app/components/workflow/nodes/tool/components/mixed-variable-text-input/placeholder'
import { BlockEnum } from '@/app/components/workflow/types'
import { cn } from '@/utils/classnames'
type MailBodyInputProps = {
readOnly?: boolean
nodesOutputVars?: NodeOutPutVar[]
availableNodes?: Node[]
value?: string
onChange?: (text: string) => void
}
const MailBodyInput = ({
readOnly = false,
nodesOutputVars,
availableNodes = [],
value = '',
onChange,
}: MailBodyInputProps) => {
const { t } = useTranslation()
return (
<PromptEditor
wrapperClassName={cn(
@ -27,6 +39,23 @@ const MailBodyInput = ({
show: true,
selectable: true,
}}
workflowVariableBlock={{
show: true,
variables: nodesOutputVars || [],
workflowNodesMap: availableNodes.reduce((acc, node) => {
acc[node.id] = {
title: node.data.title,
type: node.data.type,
}
if (node.data.type === BlockEnum.Start) {
acc.sys = {
title: t('blocks.start', { ns: 'workflow' }),
type: BlockEnum.Start,
}
}
return acc
}, {} as Record<string, Pick<Node['data'], 'title' | 'type'>>),
}}
placeholder={<Placeholder hideBadge />}
onChange={onChange}
/>

View File

@ -110,6 +110,7 @@ const DeliveryMethodItem: FC<DeliveryMethodItemProps> = ({
<Tooltip
popupContent={emailSenderTooltipContent}
asChild={false}
needsDelay={false}
>
<ActionButton onClick={() => setShowTestEmailModal(true)}>
<RiSendPlane2Line className="h-4 w-4" />
@ -118,6 +119,7 @@ const DeliveryMethodItem: FC<DeliveryMethodItemProps> = ({
<Tooltip
popupContent={t('common.configure', { ns: 'workflow' })}
asChild={false}
needsDelay={false}
>
<ActionButton onClick={() => setShowEmailModal(true)}>
<RiEqualizer2Line className="h-4 w-4" />
@ -129,6 +131,7 @@ const DeliveryMethodItem: FC<DeliveryMethodItemProps> = ({
<Tooltip
popupContent={t('operation.remove', { ns: 'common' })}
asChild={false}
needsDelay={false}
>
<div
onMouseEnter={() => setIsHovering(true)}
@ -168,6 +171,8 @@ const DeliveryMethodItem: FC<DeliveryMethodItemProps> = ({
<EmailConfigureModal
isShow={showEmailModal}
config={method.config as EmailConfig}
nodesOutputVars={nodesOutputVars}
availableNodes={availableNodes}
onClose={() => setShowEmailModal(false)}
onConfirm={(data) => {
handleConfigChange(data)

View File

@ -117,6 +117,15 @@ const MethodSelector: FC<MethodSelectorProps> = ({
id: uuid4(),
type: DeliveryMethodType.Email,
enabled: false,
config: {
body: '{{#url#}}',
recipients: {
whole_workspace: false,
items: [],
},
subject: '',
debug_mode: false,
},
})
}}
>

View File

@ -86,7 +86,7 @@ const EmailSenderModal = ({
const accounts = members?.accounts || []
const generatedInputs = useMemo(() => {
const valueSelectors = doGetInputVars(formContent || '')
const valueSelectors = doGetInputVars((formContent || '') + (config?.body || ''))
const variables = unionBy(valueSelectors, item => item.join('.')).map((item) => {
const varInfo = getNodeInfoById(availableNodes, item[0])?.data
@ -120,7 +120,7 @@ const EmailSenderModal = ({
}
})
return varInputs
}, [availableNodes, formContent, nodesOutputVars])
}, [availableNodes, config?.body, formContent, nodesOutputVars])
const [inputs, setInputs] = useState<Record<string, any>>({})
const [collapsed, setCollapsed] = useState(true)

View File

@ -3395,16 +3395,6 @@
"count": 5
}
},
"app/components/workflow/nodes/human-input/components/delivery-method/email-configure-modal.tsx": {
"ts/no-explicit-any": {
"count": 1
}
},
"app/components/workflow/nodes/human-input/components/delivery-method/method-item.tsx": {
"ts/no-explicit-any": {
"count": 1
}
},
"app/components/workflow/nodes/human-input/components/delivery-method/recipient/email-input.tsx": {
"ts/no-explicit-any": {
"count": 2