fix: improve test run menu and checklist ui (#25300)

This commit is contained in:
lyzno1 2025-09-06 22:54:36 +08:00 committed by GitHub
parent a91e59d544
commit 58cbd337b5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 44 additions and 74 deletions

View File

@ -119,8 +119,8 @@ const WorkflowChecklist = ({
<span className='grow truncate'> <span className='grow truncate'>
{node.title} {node.title}
</span> </span>
<div className='flex h-4 w-[50px] items-center justify-center gap-1 opacity-0 transition-opacity duration-200 group-hover:opacity-100'> <div className='flex h-4 w-[60px] shrink-0 items-center justify-center gap-1 opacity-0 transition-opacity duration-200 group-hover:opacity-100'>
<span className='text-xs font-medium leading-4 text-primary-600'> <span className='whitespace-nowrap text-xs font-medium leading-4 text-primary-600'>
{t('workflow.panel.goTo')} {t('workflow.panel.goTo')}
</span> </span>
<IconR className='h-3.5 w-3.5 text-primary-600' /> <IconR className='h-3.5 w-3.5 text-primary-600' />

View File

@ -16,8 +16,8 @@ import {
import { WorkflowRunningStatus } from '../types' import { WorkflowRunningStatus } from '../types'
import ViewHistory from './view-history' import ViewHistory from './view-history'
import Checklist from './checklist' import Checklist from './checklist'
import TestRunDropdown, { type TestRunDropdownRef } from './test-run-dropdown' import TestRunMenu, { type TestRunMenuRef } from './test-run-menu'
import type { TriggerOption } from './test-run-dropdown' import type { TriggerOption } from './test-run-menu'
import { useDynamicTestRunOptions } from '../hooks/use-dynamic-test-run-options' import { useDynamicTestRunOptions } from '../hooks/use-dynamic-test-run-options'
import cn from '@/utils/classnames' import cn from '@/utils/classnames'
import { import {
@ -36,12 +36,12 @@ const RunMode = memo(() => {
const workflowRunningData = useStore(s => s.workflowRunningData) const workflowRunningData = useStore(s => s.workflowRunningData)
const isRunning = workflowRunningData?.result.status === WorkflowRunningStatus.Running const isRunning = workflowRunningData?.result.status === WorkflowRunningStatus.Running
const dynamicOptions = useDynamicTestRunOptions() const dynamicOptions = useDynamicTestRunOptions()
const testRunDropdownRef = useRef<TestRunDropdownRef>(null) const testRunMenuRef = useRef<TestRunMenuRef>(null)
useEffect(() => { useEffect(() => {
// @ts-expect-error - Dynamic property for backward compatibility with keyboard shortcuts // @ts-expect-error - Dynamic property for backward compatibility with keyboard shortcuts
window._toggleTestRunDropdown = () => { window._toggleTestRunDropdown = () => {
testRunDropdownRef.current?.toggle() testRunMenuRef.current?.toggle()
} }
return () => { return () => {
// @ts-expect-error - Dynamic property cleanup // @ts-expect-error - Dynamic property cleanup
@ -89,8 +89,8 @@ const RunMode = memo(() => {
</div> </div>
) )
: ( : (
<TestRunDropdown <TestRunMenu
ref={testRunDropdownRef} ref={testRunMenuRef}
options={dynamicOptions} options={dynamicOptions}
onSelect={handleTriggerSelect} onSelect={handleTriggerSelect}
> >
@ -105,7 +105,7 @@ const RunMode = memo(() => {
{t('workflow.common.run')} {t('workflow.common.run')}
<ShortcutsName keys={['alt', 'r']} className="ml-1" textColor="secondary" /> <ShortcutsName keys={['alt', 'r']} className="ml-1" textColor="secondary" />
</div> </div>
</TestRunDropdown> </TestRunMenu>
) )
} }
{ {

View File

@ -22,17 +22,17 @@ export type TestRunOptions = {
runAll?: TriggerOption runAll?: TriggerOption
} }
type TestRunDropdownProps = { type TestRunMenuProps = {
options: TestRunOptions options: TestRunOptions
onSelect: (option: TriggerOption) => void onSelect: (option: TriggerOption) => void
children: React.ReactNode children: React.ReactNode
} }
export type TestRunDropdownRef = { export type TestRunMenuRef = {
toggle: () => void toggle: () => void
} }
const TestRunDropdown = forwardRef<TestRunDropdownRef, TestRunDropdownProps>(({ const TestRunMenu = forwardRef<TestRunMenuRef, TestRunMenuProps>(({
options, options,
onSelect, onSelect,
children, children,
@ -107,6 +107,6 @@ const TestRunDropdown = forwardRef<TestRunDropdownRef, TestRunDropdownProps>(({
) )
}) })
TestRunDropdown.displayName = 'TestRunDropdown' TestRunMenu.displayName = 'TestRunMenu'
export default TestRunDropdown export default TestRunMenu

View File

@ -3,14 +3,12 @@ import { useNodes } from 'reactflow'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { BlockEnum, type CommonNodeType } from '../types' import { BlockEnum, type CommonNodeType } from '../types'
import { getWorkflowEntryNode } from '../utils/workflow-entry' import { getWorkflowEntryNode } from '../utils/workflow-entry'
import type { TestRunOptions, TriggerOption } from '../header/test-run-dropdown' import type { TestRunOptions, TriggerOption } from '../header/test-run-menu'
import Home from '@/app/components/base/icons/src/vender/workflow/Home' import { TriggerAll } from '@/app/components/base/icons/src/vender/workflow'
import { Schedule, TriggerAll, WebhookLine } from '@/app/components/base/icons/src/vender/workflow' import BlockIcon from '../block-icon'
import AppIcon from '@/app/components/base/app-icon'
import { useStore } from '../store' import { useStore } from '../store'
import { canFindTool } from '@/utils' import { canFindTool } from '@/utils'
import { CollectionType } from '@/app/components/tools/types' import { useAllTriggerPlugins } from '@/service/use-triggers'
import useGetIcon from '@/app/components/plugins/install-plugin/base/use-get-icon'
export const useDynamicTestRunOptions = (): TestRunOptions => { export const useDynamicTestRunOptions = (): TestRunOptions => {
const { t } = useTranslation() const { t } = useTranslation()
@ -19,7 +17,7 @@ export const useDynamicTestRunOptions = (): TestRunOptions => {
const customTools = useStore(s => s.customTools) const customTools = useStore(s => s.customTools)
const workflowTools = useStore(s => s.workflowTools) const workflowTools = useStore(s => s.workflowTools)
const mcpTools = useStore(s => s.mcpTools) const mcpTools = useStore(s => s.mcpTools)
const { getIconUrl } = useGetIcon() const { data: triggerPlugins } = useAllTriggerPlugins()
return useMemo(() => { return useMemo(() => {
const allTriggers: TriggerOption[] = [] const allTriggers: TriggerOption[] = []
@ -36,9 +34,10 @@ export const useDynamicTestRunOptions = (): TestRunOptions => {
type: 'user_input', type: 'user_input',
name: nodeData.title || t('workflow.blocks.start'), name: nodeData.title || t('workflow.blocks.start'),
icon: ( icon: (
<div className="flex h-6 w-6 items-center justify-center rounded-lg border-[0.5px] border-white/2 bg-util-colors-blue-brand-blue-brand-500 text-white shadow-md"> <BlockIcon
<Home className="h-3.5 w-3.5" /> type={BlockEnum.Start}
</div> size='md'
/>
), ),
nodeId: node.id, nodeId: node.id,
enabled: true, enabled: true,
@ -50,9 +49,10 @@ export const useDynamicTestRunOptions = (): TestRunOptions => {
type: 'schedule', type: 'schedule',
name: nodeData.title || t('workflow.blocks.trigger-schedule'), name: nodeData.title || t('workflow.blocks.trigger-schedule'),
icon: ( icon: (
<div className="flex h-6 w-6 items-center justify-center rounded-lg border-[0.5px] border-white/2 bg-util-colors-violet-violet-500 text-white shadow-md"> <BlockIcon
<Schedule className="h-4.5 w-4.5" /> type={BlockEnum.TriggerSchedule}
</div> size='md'
/>
), ),
nodeId: node.id, nodeId: node.id,
enabled: true, enabled: true,
@ -64,61 +64,30 @@ export const useDynamicTestRunOptions = (): TestRunOptions => {
type: 'webhook', type: 'webhook',
name: nodeData.title || t('workflow.blocks.trigger-webhook'), name: nodeData.title || t('workflow.blocks.trigger-webhook'),
icon: ( icon: (
<div className="flex h-6 w-6 items-center justify-center rounded-lg border-[0.5px] border-white/2 bg-util-colors-blue-blue-500 text-white shadow-md"> <BlockIcon
<WebhookLine className="h-4.5 w-4.5" /> type={BlockEnum.TriggerWebhook}
</div> size='md'
/>
), ),
nodeId: node.id, nodeId: node.id,
enabled: true, enabled: true,
}) })
} }
else if (nodeData.type === BlockEnum.TriggerPlugin) { else if (nodeData.type === BlockEnum.TriggerPlugin) {
let icon
let toolIcon: string | any let toolIcon: string | any
// 按照 use-workflow-search.tsx 的模式获取工具图标
if (nodeData.provider_id) { if (nodeData.provider_id) {
let targetTools = workflowTools const targetTools = triggerPlugins || []
if (nodeData.provider_type === CollectionType.builtIn)
targetTools = buildInTools
else if (nodeData.provider_type === CollectionType.custom)
targetTools = customTools
else if (nodeData.provider_type === CollectionType.mcp)
targetTools = mcpTools
toolIcon = targetTools.find(toolWithProvider => canFindTool(toolWithProvider.id, nodeData.provider_id!))?.icon toolIcon = targetTools.find(toolWithProvider => canFindTool(toolWithProvider.id, nodeData.provider_id!))?.icon
} }
if (typeof toolIcon === 'string') { const icon = (
const iconUrl = toolIcon.startsWith('http') ? toolIcon : getIconUrl(toolIcon) <BlockIcon
icon = ( type={BlockEnum.TriggerPlugin}
<div size='md'
className="bg-util-colors-white-white-500 flex h-6 w-6 items-center justify-center rounded-lg border-[0.5px] border-white/2 text-white shadow-md" toolIcon={toolIcon}
style={{ />
backgroundImage: `url(${iconUrl})`, )
backgroundSize: 'cover',
backgroundPosition: 'center',
}}
/>
)
}
else if (toolIcon && typeof toolIcon === 'object' && 'content' in toolIcon) {
icon = (
<AppIcon
className="!h-6 !w-6 rounded-lg border-[0.5px] border-white/2 shadow-md"
size="tiny"
icon={toolIcon.content}
background={toolIcon.background}
/>
)
}
else {
icon = (
<div className="bg-util-colors-white-white-500 flex h-6 w-6 items-center justify-center rounded-lg border-[0.5px] border-white/2 text-white shadow-md">
<span className="text-xs font-medium text-text-tertiary">P</span>
</div>
)
}
allTriggers.push({ allTriggers.push({
id: node.id, id: node.id,
@ -139,9 +108,10 @@ export const useDynamicTestRunOptions = (): TestRunOptions => {
type: 'user_input', type: 'user_input',
name: (startNode.data as CommonNodeType)?.title || t('workflow.blocks.start'), name: (startNode.data as CommonNodeType)?.title || t('workflow.blocks.start'),
icon: ( icon: (
<div className="flex h-6 w-6 items-center justify-center rounded-lg border-[0.5px] border-white/2 bg-util-colors-blue-brand-blue-brand-500 text-white shadow-md"> <BlockIcon
<Home className="h-3.5 w-3.5" /> type={BlockEnum.Start}
</div> size='md'
/>
), ),
nodeId: startNode.id, nodeId: startNode.id,
enabled: true, enabled: true,
@ -166,5 +136,5 @@ export const useDynamicTestRunOptions = (): TestRunOptions => {
triggers: allTriggers, triggers: allTriggers,
runAll, runAll,
} }
}, [nodes, buildInTools, customTools, workflowTools, mcpTools, getIconUrl, t]) }, [nodes, buildInTools, customTools, workflowTools, mcpTools, triggerPlugins, t])
} }