From ff6bdc1bed13db4dfcb1b7f3b95298783e4a2462 Mon Sep 17 00:00:00 2001 From: zhaoying Date: Mon, 19 Jan 2026 14:49:48 +0800 Subject: [PATCH] feat(web): nodeProperties's ui update --- web/src/assets/images/workflow/deleteBg.svg | 21 ++ .../assets/images/workflow/deleteBg_hover.svg | 22 ++ .../assets/images/workflow/delete_cycle.svg | 18 + web/src/assets/images/workflow/recall.svg | 18 + .../assets/images/workflow/recall_hover.svg | 18 + web/src/components/Header/index.tsx | 2 +- web/src/components/RbModal/index.css | 6 +- web/src/components/RbModal/index.tsx | 1 + web/src/components/SearchInput/index.tsx | 5 +- web/src/i18n/en.ts | 11 +- web/src/i18n/zh.ts | 12 +- web/src/styles/antdThemeConfig.ts | 10 + .../components/ConfigHeader.tsx | 14 +- .../components/Chat/VariableConfigModal.tsx | 13 +- .../Workflow/components/Editor/index.tsx | 52 ++- .../Editor/plugin/Jinja2HighlightPlugin.tsx | 28 +- .../Properties/AssignmentList/index.tsx | 216 ++++++------ .../components/Properties/CaseList/index.tsx | 177 +++++----- .../Properties/CategoryList/index.tsx | 19 +- .../Properties/ConditionList/index.tsx | 196 ++++++----- .../Properties/CycleVarsList/index.tsx | 130 +++---- .../Properties/GroupVariableList/index.tsx | 33 +- .../HttpRequest/AuthConfigModal.tsx | 7 +- .../Properties/HttpRequest/EditableTable.tsx | 46 ++- .../Properties/HttpRequest/index.tsx | 55 ++- .../Properties/Knowledge/Knowledge.tsx | 98 +++--- .../Knowledge/KnowledgeConfigModal.tsx | 1 + .../Knowledge/KnowledgeGlobalConfigModal.tsx | 1 + .../Knowledge/KnowledgeListModal.tsx | 1 + .../Properties/MappingList/index.tsx | 81 +++-- .../Properties/MemoryConfig/index.tsx | 3 +- .../components/Properties/MessageEditor.tsx | 34 +- .../Properties/ParamsList/ParamEditModal.tsx | 1 + .../Properties/ParamsList/index.tsx | 70 ++-- .../components/Properties/RbSlider.tsx | 55 +++ .../Properties/ToolConfig/index.tsx | 11 +- .../{ => VariableList}/VariableEditModal.tsx | 13 +- .../Properties/VariableList/index.tsx | 108 ++++++ .../Properties/VariableList/types.ts | 23 ++ .../components/Properties/VariableSelect.tsx | 3 +- .../Workflow/components/Properties/index.tsx | 316 +++++++----------- .../Properties/properties.module.css | 90 +++++ web/src/views/Workflow/constant.ts | 19 +- web/src/views/Workflow/types.ts | 23 +- 44 files changed, 1267 insertions(+), 814 deletions(-) create mode 100644 web/src/assets/images/workflow/deleteBg.svg create mode 100644 web/src/assets/images/workflow/deleteBg_hover.svg create mode 100644 web/src/assets/images/workflow/delete_cycle.svg create mode 100644 web/src/assets/images/workflow/recall.svg create mode 100644 web/src/assets/images/workflow/recall_hover.svg create mode 100644 web/src/views/Workflow/components/Properties/RbSlider.tsx rename web/src/views/Workflow/components/Properties/{ => VariableList}/VariableEditModal.tsx (94%) create mode 100644 web/src/views/Workflow/components/Properties/VariableList/index.tsx create mode 100644 web/src/views/Workflow/components/Properties/VariableList/types.ts create mode 100644 web/src/views/Workflow/components/Properties/properties.module.css diff --git a/web/src/assets/images/workflow/deleteBg.svg b/web/src/assets/images/workflow/deleteBg.svg new file mode 100644 index 00000000..f3827fef --- /dev/null +++ b/web/src/assets/images/workflow/deleteBg.svg @@ -0,0 +1,21 @@ + + + 编组 33 + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/web/src/assets/images/workflow/deleteBg_hover.svg b/web/src/assets/images/workflow/deleteBg_hover.svg new file mode 100644 index 00000000..9e92cf75 --- /dev/null +++ b/web/src/assets/images/workflow/deleteBg_hover.svg @@ -0,0 +1,22 @@ + + + 编组 33 + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/web/src/assets/images/workflow/delete_cycle.svg b/web/src/assets/images/workflow/delete_cycle.svg new file mode 100644 index 00000000..0d85650d --- /dev/null +++ b/web/src/assets/images/workflow/delete_cycle.svg @@ -0,0 +1,18 @@ + + + 编组 33 + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/web/src/assets/images/workflow/recall.svg b/web/src/assets/images/workflow/recall.svg new file mode 100644 index 00000000..756f9060 --- /dev/null +++ b/web/src/assets/images/workflow/recall.svg @@ -0,0 +1,18 @@ + + + 召回 + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/web/src/assets/images/workflow/recall_hover.svg b/web/src/assets/images/workflow/recall_hover.svg new file mode 100644 index 00000000..a2e949a0 --- /dev/null +++ b/web/src/assets/images/workflow/recall_hover.svg @@ -0,0 +1,18 @@ + + + 召回 + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/web/src/components/Header/index.tsx b/web/src/components/Header/index.tsx index 9aeeab6b..fac432f5 100644 --- a/web/src/components/Header/index.tsx +++ b/web/src/components/Header/index.tsx @@ -54,7 +54,7 @@ const AppHeader: FC<{source?: 'space' | 'manage';}> = ({source = 'manage'}) => { key: '1', label: (<>
{user.username}
-
{user.email}
+
{user.email}
), }, { diff --git a/web/src/components/RbModal/index.css b/web/src/components/RbModal/index.css index d4aa4388..8dabc4ab 100644 --- a/web/src/components/RbModal/index.css +++ b/web/src/components/RbModal/index.css @@ -1,3 +1,5 @@ -.rb-modal .ant-modal-header { - margin-bottom: 24px; +.rb-modal .ant-modal-footer .ant-btn { + height: 32px !important; + padding: 0 15px !important; + font-size: 14px !important; } \ No newline at end of file diff --git a/web/src/components/RbModal/index.tsx b/web/src/components/RbModal/index.tsx index 7a1b8711..e6607e98 100644 --- a/web/src/components/RbModal/index.tsx +++ b/web/src/components/RbModal/index.tsx @@ -9,6 +9,7 @@ import { type FC } from 'react' import { Modal, type ModalProps } from 'antd' import { useTranslation } from 'react-i18next' +import './index.css' const RbModal: FC = ({ onOk, onCancel, diff --git a/web/src/components/SearchInput/index.tsx b/web/src/components/SearchInput/index.tsx index 7c0b9c5e..7bcb2c76 100644 --- a/web/src/components/SearchInput/index.tsx +++ b/web/src/components/SearchInput/index.tsx @@ -1,5 +1,5 @@ import { useState, type FC, useCallback, useRef } from 'react'; -import { Input } from 'antd'; +import { Input, type InputProps } from 'antd'; import { useTranslation } from 'react-i18next'; import searchIcon from '@/assets/images/search.svg' @@ -11,6 +11,7 @@ interface SearchInputProps { defaultValue?: string; style?: Record; className?: string; + size?: InputProps['size'] } const SearchInput: FC = ({ @@ -79,7 +80,7 @@ const SearchInput: FC = ({ return ( } + prefix={search} placeholder={placeholder || t('user.searchPlaceholder')} value={value} onChange={handleChange} diff --git a/web/src/i18n/en.ts b/web/src/i18n/en.ts index c46dbb91..1341bf55 100644 --- a/web/src/i18n/en.ts +++ b/web/src/i18n/en.ts @@ -417,7 +417,8 @@ export const en = { refresh: 'Refresh', return: 'Return', statusEnabled: 'Available', - statusDisabled: 'Unavailable' + statusDisabled: 'Unavailable', + remove: 'Remove', }, model: { searchPlaceholder: 'search model…', @@ -1798,9 +1799,11 @@ Memory Bear: After the rebellion, regional warlordism intensified for several re temperature: 'Temperature', max_tokens: 'Max Tokens', context: 'Context', + contextPlaceholder: '{x} Set Variable', memory: 'Memory', enable_window: 'Memory Window', inner: 'Built-in', + messagesPlaceholder: 'Write prompts here, type "{" to insert variables, type "insert" to insert', }, start: { variables: 'Input Fields', @@ -1811,7 +1814,6 @@ Memory Bear: After the rebellion, regional warlordism intensified for several re array: 'Dropdown Options', object: 'Object', - addVariable: 'Add Variable', editVariable: 'Edit Variable', variableType: 'Variable Type', variableName: 'Variable Name', @@ -1835,6 +1837,7 @@ Memory Bear: After the rebellion, regional warlordism intensified for several re 'parameter-extractor': { model_id: 'Model', text: 'Input Variable', + textPlaceholder: '{x} Set Variable', params: 'Extract Parameters', prompt: 'Instruction', @@ -1855,6 +1858,8 @@ Memory Bear: After the rebellion, regional warlordism intensified for several re 'array[number]': 'Array[Number]', 'array[boolean]': 'Array[Boolean]', 'array[object]': 'Array[Object]', + addParams: 'Add Extract Variable', + promptPlaceholder: 'Write prompts here, type "{" to insert variables, type "insert" to insert', }, 'var-aggregator': { group: 'Aggregation Group', @@ -1924,6 +1929,7 @@ Memory Bear: After the rebellion, regional warlordism intensified for several re loop: { cycle_vars: 'Loop Variables', condition: 'Loop Termination Condition', + addCondition: 'Add Condition', max_loop: 'Maximum Loop Count', }, assigner: { @@ -1960,6 +1966,7 @@ Memory Bear: After the rebellion, regional warlordism intensified for several re type: 'Type', value: 'Value', addCase: 'Add Condition', + addVariable: 'Add Variables', }, clear: 'Clear', diff --git a/web/src/i18n/zh.ts b/web/src/i18n/zh.ts index 028202d1..b6834fea 100644 --- a/web/src/i18n/zh.ts +++ b/web/src/i18n/zh.ts @@ -965,7 +965,8 @@ export const zh = { refresh: '刷新', return: '返回', statusEnabled: '可用', - statusDisabled: '不可用' + statusDisabled: '不可用', + remove: '删除', }, product: { applicationManagement: '应用管理', @@ -1891,9 +1892,11 @@ export const zh = { temperature: '温度', max_tokens: '最大令牌数', context: '上下文', + contextPlaceholder: '{x} 设置变量', memory: '记忆', enable_window: '记忆窗口', inner: '内置', + messagesPlaceholder: '在此处编写提示,输入“{”插入变量,输入“insert”插入', }, start: { variables: '输入字段', @@ -1904,7 +1907,6 @@ export const zh = { array: '下拉选项', object: '对象', - addVariable: '添加变量', editVariable: '编辑变量', variableType: '变量类型', variableName: '变量名称', @@ -1924,10 +1926,12 @@ export const zh = { query: '查询变量', knowledge_retrieval: '知识库', recallConfig: '召回测试', + addKnowledge: '添加知识库' }, 'parameter-extractor': { model_id: '模型', text: '输入变量', + textPlaceholder: '{x} 设置变量', params: '提取参数', prompt: '指令', @@ -1948,6 +1952,8 @@ export const zh = { 'array[number]': 'Array[Number]', 'array[boolean]': 'Array[Boolean]', 'array[object]': 'Array[Object]', + addParams: '添加提取变量', + promptPlaceholder: '在此处编写提示,输入“{”插入变量,输入“insert”插入', }, 'var-aggregator': { group: '聚合分组', @@ -2017,6 +2023,7 @@ export const zh = { loop: { cycle_vars: '循环变量', condition: '循环终止条件', + addCondition: '添加条件', max_loop: '最大循环次数', }, assigner: { @@ -2053,6 +2060,7 @@ export const zh = { type: '类型', value: '值', addCase: '添加条件', + addVariable: '添加变量', }, clear: '清空', diff --git a/web/src/styles/antdThemeConfig.ts b/web/src/styles/antdThemeConfig.ts index e5d6d88e..db1166fb 100644 --- a/web/src/styles/antdThemeConfig.ts +++ b/web/src/styles/antdThemeConfig.ts @@ -21,6 +21,8 @@ export const lightTheme: ThemeConfig = { colorBorderSecondary: '#DFE4ED', // colorBgContainer: '#FBFDFF', colorError: '#FF5D34', + sizeSM: 12, + fontSizeSM: 12, }, components: { Layout: { @@ -86,6 +88,7 @@ export const lightTheme: ThemeConfig = { rowSelectedBg: '#E9F1FF', rowSelectedHoverBg: '#F0F3F8', cellPaddingBlock: 8, + cellFontSizeSM: 12, // cellPaddingInline: 24, selectionColumnWidth: 48, @@ -95,6 +98,13 @@ export const lightTheme: ThemeConfig = { lastItemColor: '#212332', linkColor: '#5B6167', linkHoverColor: '#212332', + }, + Input: { + inputFontSizeSM: 12, + controlHeightSM: 26 + }, + Select: { + lineHeightSM: 26 } } }; \ No newline at end of file diff --git a/web/src/views/ApplicationConfig/components/ConfigHeader.tsx b/web/src/views/ApplicationConfig/components/ConfigHeader.tsx index 37a56c80..1837b696 100644 --- a/web/src/views/ApplicationConfig/components/ConfigHeader.tsx +++ b/web/src/views/ApplicationConfig/components/ConfigHeader.tsx @@ -1,6 +1,6 @@ -import { type FC, useEffect, useRef } from 'react'; +import { type FC, useRef } from 'react'; import { useNavigate, useParams } from 'react-router-dom'; -import { Layout, Tabs, Dropdown, Button } from 'antd'; +import { Layout, Tabs, Dropdown, Button, Flex } from 'antd'; import type { MenuProps } from 'antd'; import { useTranslation } from 'react-i18next'; import styles from '../index.module.css' @@ -141,10 +141,12 @@ const ConfigHeader: FC = ({ {/* */} - :
- - {t('application.returnToApplicationList')} -
+ : +
+ + {t('application.returnToApplicationList')} +
+
} void; - variables: StartVariableItem[] + refresh: (values: Variable[]) => void; + variables: Variable[] } const VariableConfigModal = forwardRef(({ @@ -15,9 +16,9 @@ const VariableConfigModal = forwardRef { const { t } = useTranslation(); const [visible, setVisible] = useState(false); - const [form] = Form.useForm<{variables: StartVariableItem[]}>(); + const [form] = Form.useForm<{variables: Variable[]}>(); const [loading, setLoading] = useState(false) - const [initialValues, setInitialValues] = useState([]) + const [initialValues, setInitialValues] = useState([]) // 封装取消方法,添加关闭弹窗逻辑 const handleClose = () => { @@ -26,7 +27,7 @@ const VariableConfigModal = forwardRef { + const handleOpen = (values: Variable[]) => { setVisible(true); form.setFieldsValue({variables: values}) setInitialValues([...values]) diff --git a/web/src/views/Workflow/components/Editor/index.tsx b/web/src/views/Workflow/components/Editor/index.tsx index 9a387eec..ba2e3a41 100644 --- a/web/src/views/Workflow/components/Editor/index.tsx +++ b/web/src/views/Workflow/components/Editor/index.tsx @@ -1,4 +1,4 @@ -import { type FC, useState, useEffect } from 'react'; +import { type FC, useState, useEffect, useMemo } from 'react'; import { LexicalComposer } from '@lexical/react/LexicalComposer'; import { RichTextPlugin } from '@lexical/react/LexicalRichTextPlugin'; import { ContentEditable } from '@lexical/react/LexicalContentEditable'; @@ -25,7 +25,11 @@ interface LexicalEditorProps { options: Suggestion[]; variant?: 'outlined' | 'borderless'; height?: number; + fontSize?: number; + lineHeight?: number; enableJinja2?: boolean; + size?: 'default' | 'small'; + type?: 'input' | 'textarea' } const theme = { @@ -51,8 +55,9 @@ const Editor: FC =({ onChange, options, variant = 'borderless', - height = 60, enableJinja2 = false, + size = 'default', + type = 'textarea' }) => { const [_count, setCount] = useState(0); @@ -94,12 +99,9 @@ const Editor: FC =({ display: flex; } .line-numbers { - background-color: #f8f9fa; - border-right: 1px solid #e1e4e8; - color: #656d76; font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace; font-size: 12px; - line-height: 20px; + line-height: 16px; padding: 4px 8px; text-align: right; user-select: none; @@ -142,6 +144,21 @@ const Editor: FC =({ console.error(error); }, }; + const minheight = useMemo(() => { + if (type === 'input') { + return `${size === 'small' ? 26 : 30}px` + } + return `${size === 'small' ? 60 : 120}px` + }, [type, size]) + const fontSize = useMemo(() => { + return `${size === 'small' ? 12 : 14}px` + }, [size]) + const lineHeight = useMemo(() => { + return `${size === 'small' ? 16 : 20}px` + }, [size]) + const placeHolderMinheight = useMemo(() => { + return `${size === 'small' ? 16 : 30}px` + }, [type, size]) return ( @@ -152,7 +169,7 @@ const Editor: FC =({
1
@@ -160,12 +177,12 @@ const Editor: FC =({ @@ -173,14 +190,14 @@ const Editor: FC =({ ) : ( ) @@ -188,12 +205,13 @@ const Editor: FC =({ placeholder={
diff --git a/web/src/views/Workflow/components/Editor/plugin/Jinja2HighlightPlugin.tsx b/web/src/views/Workflow/components/Editor/plugin/Jinja2HighlightPlugin.tsx index 498f78d7..4c75fc58 100644 --- a/web/src/views/Workflow/components/Editor/plugin/Jinja2HighlightPlugin.tsx +++ b/web/src/views/Workflow/components/Editor/plugin/Jinja2HighlightPlugin.tsx @@ -9,12 +9,21 @@ const Jinja2HighlightPlugin = () => { return editor.registerNodeTransform(TextNode, (textNode: TextNode) => { const text = textNode.getTextContent(); - if (containsJinja2Patterns(text)) { - const parent = textNode.getParent(); - if (!parent) return; + // Skip if node already has styling (prevent infinite recursion) + if (textNode.getStyle()) return; + + // Skip if no Jinja2 patterns found + if (!containsJinja2Patterns(text)) return; + + const parent = textNode.getParent(); + if (!parent) return; - const tokens = tokenizeJinja2(text); - const newNodes = tokens.map(token => { + const tokens = tokenizeJinja2(text); + + // Skip if no meaningful tokenization (only one text token) + if (tokens.length <= 1 || (tokens.length === 1 && tokens[0].type === 'text')) return; + + const newNodes = tokens.map(token => { const newNode = $createTextNode(token.text); switch (token.type) { @@ -30,16 +39,16 @@ const Jinja2HighlightPlugin = () => { newNode.setStyle('color: #008000'); break; case 'brace-0': - newNode.setStyle('color: #d73a49; font-family: monospace; font-weight: bold;'); + newNode.setStyle('color: #155EEF; font-family: monospace; font-weight: bold;'); break; case 'brace-1': - newNode.setStyle('color: #0366d6; font-family: monospace; font-weight: bold;'); + newNode.setStyle('color: #369F21; font-family: monospace; font-weight: bold;'); break; case 'brace-2': - newNode.setStyle('color: #28a745; font-family: monospace; font-weight: bold;'); + newNode.setStyle('color: #FF5D34; font-family: monospace; font-weight: bold;'); break; case 'brace-3': - newNode.setStyle('color: #6f42c1; font-family: monospace; font-weight: bold;'); + newNode.setStyle('color: #5B6167; font-family: monospace; font-weight: bold;'); break; case 'expression-0': case 'expression-1': @@ -77,7 +86,6 @@ const Jinja2HighlightPlugin = () => { newNodes[i - 1].insertAfter(newNodes[i]); } } - } }); }, [editor]); diff --git a/web/src/views/Workflow/components/Properties/AssignmentList/index.tsx b/web/src/views/Workflow/components/Properties/AssignmentList/index.tsx index 2ee4f51e..29f8f7a1 100644 --- a/web/src/views/Workflow/components/Properties/AssignmentList/index.tsx +++ b/web/src/views/Workflow/components/Properties/AssignmentList/index.tsx @@ -1,7 +1,6 @@ import { type FC } from 'react' import { useTranslation } from 'react-i18next'; -import { Form, Input, Row, Col, Select, InputNumber, Radio } from 'antd' -import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons'; +import { Form, Input, Select, InputNumber, Radio, Button, Space } from 'antd' import type { Suggestion } from '../../Editor/plugin/AutocompletePlugin' import VariableSelect from '../VariableSelect' @@ -9,6 +8,7 @@ interface AssignmentListProps { value?: Array<{ variable_selector: string; operation: string[]; value: string;}>; parentName: string; options: Suggestion[]; + size?: 'small' | 'middle' } const operationsObj = { @@ -31,6 +31,7 @@ const operationsObj = { const AssignmentList: FC = ({ parentName, options = [], + size = 'small' }) => { const { t } = useTranslation(); const form = Form.useFormInstance(); @@ -39,109 +40,126 @@ const AssignmentList: FC = ({ {(fields, { add, remove }) => ( <> -
- {t(`workflow.config.assigner.${parentName}`)} - add({ operation: 'cover'})} /> -
- {fields.map(({ key, name, ...restField }) => { - const variableSelector = form.getFieldValue([parentName, name, 'variable_selector']); - const selectedOption = options.find(option => `{{${option.value}}}` === variableSelector); - const dataType = selectedOption?.dataType; - const operationOptions = dataType === 'number' ? operationsObj.number : operationsObj.default; - - return ( -
- - - - vo.nodeData.type === 'loop' || vo.value.includes('conv.') || (vo.nodeData.type === 'iteration' && (vo.label === 'item' || vo.label === 'index')))} - popupMatchSelectWidth={false} - onChange={() => { - form.setFieldValue([parentName, name, 'operation'], undefined); - form.setFieldValue([parentName, name, 'value'], undefined); - }} - /> - - - - - ({ + ...op, + label: t(op.label) + }))} + popupMatchSelectWidth={false} + onChange={() => { + form.setFieldValue([parentName, name, 'value'], undefined); + }} + size={size} + className="rb:w-24!" + /> + +
+ + {(form) => { + const operation = form.getFieldValue([parentName, name, 'operation']); + if (operation === 'clear') return null; + + return ( + + {dataType === 'number' && operation === 'cover' + ? vo.dataType === dataType) : options} + popupMatchSelectWidth={false} + size={size} + /> + : dataType === 'number' + ? form.setFieldValue([name, 'value'], value)} + size={size} + /> + : operation === 'assign' + ? <> + {dataType === 'boolean' + ? + True + False + + : + } + + : vo.dataType === dataType) : options} + popupMatchSelectWidth={false} + size={size} + /> + } + + ); + }} + +
+
remove(name)} + >
+
+ ) + })} + )} diff --git a/web/src/views/Workflow/components/Properties/CaseList/index.tsx b/web/src/views/Workflow/components/Properties/CaseList/index.tsx index c475034e..8caa601e 100644 --- a/web/src/views/Workflow/components/Properties/CaseList/index.tsx +++ b/web/src/views/Workflow/components/Properties/CaseList/index.tsx @@ -1,8 +1,7 @@ import { type FC } from 'react' import clsx from 'clsx' import { useTranslation } from 'react-i18next'; -import { Form, Button, Select, Space, Row, Col, Divider, InputNumber, Radio, type SelectProps } from 'antd' -import { DeleteOutlined } from '@ant-design/icons'; +import { Form, Button, Select, Space, Divider, InputNumber, Radio, type SelectProps } from 'antd' import type { Suggestion } from '../../Editor/plugin/AutocompletePlugin' import VariableSelect from '../VariableSelect' @@ -247,37 +246,40 @@ const CaseList: FC = ({ {(conditionFields, { add: addCondition, remove: removeCondition }) => { const logicalOperator = form.getFieldValue(name)?.[caseIndex]?.logical_operator || 'and' return ( -
-
- - {caseIndex === 0 ? 'IF' : 'ELIF'}
- {caseFields.length > 1 && {`CASE ${caseIndex + 1}`}} -
+
+
+
+ {caseIndex === 0 ? 'IF' : 'ELIF'} + {caseFields.length > 1 && ({`CASE ${caseIndex + 1}`})} +
- {caseFields.length > 1 && handleRemoveCase(removeCase, caseField.name, caseIndex)} - />} + {caseFields.length > 1 && + + }
- {conditionFields?.length > 1 && - <> -
-
+ {conditionFields?.length > 1 &&
+
+
- +
- - } +
+
} {conditionFields.map((conditionField, conditionIndex) => { const cases = form.getFieldValue(name) || []; const currentCase = cases[caseIndex] || {}; @@ -290,91 +292,86 @@ const CaseList: FC = ({ const operatorList = operatorsObj[leftFieldType || 'default'] || operatorsObj.default || []; const inputType = leftFieldType === 'number' ? currentExpression.input_type : undefined; return ( -
-
- - - - handleLeftFieldChange(caseIndex, conditionIndex, val)} - /> - - - - - ({ + ...vo, + label: t(String(vo?.label || '')) + }))} + size="small" + popupMatchSelectWidth={false} + placeholder={t('common.pleaseSelect')} + className="rb:bg-white! rb:w-22!" + /> + +
- {!hideRightField && <> + {!hideRightField &&
{leftFieldType === 'number' - ? - - - handleInputTypeChange(caseIndex, conditionIndex)} + className="rb:w-18!" + /> + + + + {inputType === 'Variable' + ? + vo.dataType === 'number')} + allowClear={false} popupMatchSelectWidth={false} variant="borderless" - onChange={() => handleInputTypeChange(caseIndex, conditionIndex)} + size="small" /> - - - - - {inputType === 'Variable' - ? - vo.dataType === 'number')} - allowClear={false} - popupMatchSelectWidth={false} + : form.setFieldValue([name, caseIndex, 'expressions', conditionIndex, 'right'], value)} /> - : form.setFieldValue([name, caseIndex, 'expressions', conditionIndex, 'right'], value)} - /> - } - - - + } + +
: {leftFieldType === 'boolean' ? True False - : + : } } - } +
}
+
removeCondition(conditionField.name)} + >
) })} @@ -388,6 +385,8 @@ const CaseList: FC = ({
= ({ parentName, selectedNode, graphRe
@@ -179,8 +178,10 @@ const CategoryList: FC = ({ parentName, selectedNode, graphRe diff --git a/web/src/views/Workflow/components/Properties/ConditionList/index.tsx b/web/src/views/Workflow/components/Properties/ConditionList/index.tsx index dfdda990..e1b698b3 100644 --- a/web/src/views/Workflow/components/Properties/ConditionList/index.tsx +++ b/web/src/views/Workflow/components/Properties/ConditionList/index.tsx @@ -1,11 +1,10 @@ import { type FC } from 'react' +import clsx from 'clsx' import { useTranslation } from 'react-i18next'; -import { Form, Button, Select, Row, Col, InputNumber, Radio, Input, type SelectProps } from 'antd' -import { DeleteOutlined } from '@ant-design/icons'; +import { Form, Button, Select, InputNumber, Radio, Input, Divider, type SelectProps } from 'antd' import type { Suggestion } from '../../Editor/plugin/AutocompletePlugin' import VariableSelect from '../VariableSelect' -import Editor from '../../Editor' interface Case { logical_operator: 'and' | 'or'; @@ -84,52 +83,64 @@ const ConditionList: FC = ({ return ( <> - {(fields, { add, remove }) => ( -
+ {(fields, { add, remove }) => { + const logicalOperator = form.getFieldValue([parentName, 'logical_operator']); + return (
- {fields.map((field, index) => { - const expressions = form.getFieldValue([parentName, 'expressions']) || []; - const currentExpression = expressions[index] || {}; - const currentOperator = currentExpression.operator; - const hideRightField = currentOperator === 'empty' || currentOperator === 'not_empty'; - const leftFieldValue = currentExpression.left; - const leftFieldOption = options.find(option => `{{${option.value}}}` === leftFieldValue); - const leftFieldType = leftFieldOption?.dataType; - const operatorList = operatorsObj[leftFieldType || 'default'] || operatorsObj.default || []; - const inputType = leftFieldType === 'number' ? currentExpression.input_type : undefined; - const logicalOperator = form.getFieldValue([parentName, 'logical_operator']); - - return ( -
- {index > 0 && (<> -
-
- - - -
- )} - -
- - +
+
+ {t('workflow.config.loop.condition')} +
+ + +
+ {fields?.length > 1 &&
+
+
+ + + +
+
+
} + {fields.map((field, index) => { + const expressions = form.getFieldValue([parentName, 'expressions']) || []; + const currentExpression = expressions[index] || {}; + const currentOperator = currentExpression.operator; + const hideRightField = currentOperator === 'empty' || currentOperator === 'not_empty'; + const leftFieldValue = currentExpression.left; + const leftFieldOption = options.find(option => `{{${option.value}}}` === leftFieldValue); + const leftFieldType = leftFieldOption?.dataType; + const operatorList = operatorsObj[leftFieldType || 'default'] || operatorsObj.default || []; + const inputType = leftFieldType === 'number' ? currentExpression.input_type : undefined; + + return ( +
+
+
- vo.value.includes('sys.') || - vo.value.includes('conv.') || + options={options.filter(vo => + vo.value.includes('sys.') || + vo.value.includes('conv.') || vo.nodeData.type === 'loop' || (vo.nodeData.cycle && vo.nodeData.cycle === selectedNode?.id) )} size="small" allowClear={false} popupMatchSelectWidth={false} + placeholder={t('common.pleaseSelect')} onChange={(val) => handleLeftFieldChange(index, val)} /> - - - + + + : form.setFieldValue([parentName, 'expressions', index, 'right'], value)} + /> } - +
+ : + {leftFieldType === 'boolean' + ? + True + False + + : + } + } - } - - +
} +
+
remove(field.name)} + >
-
- ) - })} + ) + })}
- - -
- )} + ) + }}
) diff --git a/web/src/views/Workflow/components/Properties/CycleVarsList/index.tsx b/web/src/views/Workflow/components/Properties/CycleVarsList/index.tsx index 4d436af0..13e00ea2 100644 --- a/web/src/views/Workflow/components/Properties/CycleVarsList/index.tsx +++ b/web/src/views/Workflow/components/Properties/CycleVarsList/index.tsx @@ -1,7 +1,6 @@ import { type FC } from 'react' import { useTranslation } from 'react-i18next'; -import { Form, Select, Row, Col, Input } from 'antd' -import { DeleteOutlined, PlusOutlined } from '@ant-design/icons'; +import { Form, Select, Input, Button } from 'antd' import VariableSelect from '../VariableSelect' import type { Suggestion } from '../../Editor/plugin/AutocompletePlugin' @@ -20,6 +19,7 @@ interface CycleVarsListProps { parentName: string; selectedNode?: any; graphRef?: any; + size?: 'small' | 'middle' } const types = [ @@ -37,7 +37,8 @@ const CycleVarsList: FC = ({ options, parentName, selectedNode, - graphRef + graphRef, + size = 'middle' }) => { const { t } = useTranslation(); const form = Form.useFormInstance(); @@ -78,62 +79,56 @@ const CycleVarsList: FC = ({ const availableOptions = getChildNodeVariables(); return ( -
- - - {(fields, { add, remove }) => ( - <> -
- 循环变量 - add({ name: '', type: 'string', input_type: 'constant', value: '' })} /> -
- {fields.map(({ key, name, ...field }, index) => { - const currentInputType = value?.[index]?.input_type; - - return ( -
- - - - - - - - - { - // 重置 value 字段 - form.setFieldValue([parentName, index, 'value'], undefined); - }} - /> - - - - remove(name)} + + {(fields, { add, remove }) => ( + <> +
+ {t('workflow.config.loop.cycle_vars')} + +
+ {fields.map(({ key, name }, index) => { + const currentInputType = value?.[index]?.input_type; + + return ( +
+
+
+ + + + + { + form.setFieldValue([parentName, index, 'value'], undefined); + }} + className="rb:w-18!" + /> + +
{currentInputType === 'variable' ? ( @@ -145,22 +140,29 @@ const CycleVarsList: FC = ({ return option.dataType === currentType })} + variant="borderless" + size="small" /> ) : ( )}
- ) - })} - - )} - -
+
remove(name)} + >
+
+ ) + })} + + )} +
) } diff --git a/web/src/views/Workflow/components/Properties/GroupVariableList/index.tsx b/web/src/views/Workflow/components/Properties/GroupVariableList/index.tsx index 61cdd7b0..06ea9e86 100644 --- a/web/src/views/Workflow/components/Properties/GroupVariableList/index.tsx +++ b/web/src/views/Workflow/components/Properties/GroupVariableList/index.tsx @@ -1,7 +1,7 @@ import { type FC } from 'react' import { useTranslation } from 'react-i18next'; import { Form, Input, Button, Row, Col } from 'antd' -import { MinusCircleOutlined } from '@ant-design/icons'; + import type { Suggestion } from '../../Editor/plugin/AutocompletePlugin' import VariableSelect from '../VariableSelect' @@ -9,13 +9,15 @@ interface GroupVariableListProps { value?: Array<{ key: string; value: string[]; }>; name: string; options: Suggestion[]; - isCanAdd: boolean + isCanAdd: boolean; + size: 'small' | 'middle' } const GroupVariableList: FC = ({ name, options = [], - isCanAdd = false + isCanAdd = false, + size = "middle" }) => { const { t } = useTranslation(); const form = Form.useFormInstance(); @@ -54,6 +56,7 @@ const GroupVariableList: FC = ({ placeholder={t('common.pleaseSelect')} options={filteredOptions} mode="multiple" + size={size} />
@@ -76,11 +79,15 @@ const GroupVariableList: FC = ({ ]} noStyle > - {isCanAdd ? : t('workflow.config.var-aggregator.variable')} + {isCanAdd ? : t('workflow.config.var-aggregator.variable')} + {isCanAdd && - remove(name)} /> +
remove(name)} + >
} @@ -104,16 +111,22 @@ const GroupVariableList: FC = ({ })() } mode="multiple" + size={size} /> ) })} - {isCanAdd && - - } + + {isCanAdd && } )} diff --git a/web/src/views/Workflow/components/Properties/HttpRequest/AuthConfigModal.tsx b/web/src/views/Workflow/components/Properties/HttpRequest/AuthConfigModal.tsx index 71cacbab..f77e9c06 100644 --- a/web/src/views/Workflow/components/Properties/HttpRequest/AuthConfigModal.tsx +++ b/web/src/views/Workflow/components/Properties/HttpRequest/AuthConfigModal.tsx @@ -93,6 +93,7 @@ const AuthConfigModal = forwardRef(({ initialValues={{ auth: 'none' }} + size="middle" > (({ ]} > (({ { required: true, message: t('common.pleaseEnter') } ]} > - + } (({ { required: true, message: t('common.pleaseEnter') } ]} > - + } diff --git a/web/src/views/Workflow/components/Properties/HttpRequest/EditableTable.tsx b/web/src/views/Workflow/components/Properties/HttpRequest/EditableTable.tsx index e0912c0a..671ae074 100644 --- a/web/src/views/Workflow/components/Properties/HttpRequest/EditableTable.tsx +++ b/web/src/views/Workflow/components/Properties/HttpRequest/EditableTable.tsx @@ -1,7 +1,6 @@ -import { useMemo } from 'react'; import { useTranslation } from 'react-i18next' import { Button, Select, Table, Form, type TableProps } from 'antd'; -import { PlusOutlined, DeleteOutlined } from '@ant-design/icons'; +import { PlusOutlined } from '@ant-design/icons'; import type { Suggestion } from '../../Editor/plugin/AutocompletePlugin'; import Empty from '@/components/Empty'; import VariableSelect from '../VariableSelect'; @@ -19,6 +18,7 @@ interface EditableTableProps { options?: Suggestion[]; typeOptions?: { value: string, label: string }[] filterBooleanType?: boolean; + size?: "small" } const EditableTable: React.FC = ({ @@ -26,7 +26,8 @@ const EditableTable: React.FC = ({ title, options = [], typeOptions = [], - filterBooleanType = false + filterBooleanType = false, + size = 'small' }) => { const { t } = useTranslation(); @@ -38,21 +39,24 @@ const EditableTable: React.FC = ({ const getColumns = (remove: (index: number) => void): TableProps['columns'] => { const hasType = typeOptions.length > 0; - const baseWidth = hasType ? '35%' : '45%'; + const cellClassName="rb:p-1!" + const contentClassName ="rb:w-[108px]! rb:text-[12px]!" return [ { title: t('workflow.config.name'), dataIndex: 'name', - width: baseWidth, + className: cellClassName, render: (_: any, __: TableRow, index: number) => ( ) @@ -61,18 +65,20 @@ const EditableTable: React.FC = ({ title: t('workflow.config.type'), dataIndex: 'type', width: '20%', + className: cellClassName, render: (_: any, __: TableRow, index: number) => ( {(form) => ( - vo.dataType === 'string' || vo.dataType === 'number')} variant="outlined" /> + vo.dataType === 'string' || vo.dataType === 'number')} + variant="outlined" + type="input" + size="small" + /> - + vo.dataType === 'string' || vo.dataType === 'number')} /> - + vo.dataType === 'string' || vo.dataType === 'number')} /> - + - - - - - - - - - remove(name)} /> - - - ))} - - - + + {fields.map(({ key, name, ...restField }) => ( +
+ + + + + + +
remove(name)} + >
+
+ ))} )} + ) }; diff --git a/web/src/views/Workflow/components/Properties/MemoryConfig/index.tsx b/web/src/views/Workflow/components/Properties/MemoryConfig/index.tsx index 0668ca9a..c994df62 100644 --- a/web/src/views/Workflow/components/Properties/MemoryConfig/index.tsx +++ b/web/src/views/Workflow/components/Properties/MemoryConfig/index.tsx @@ -30,7 +30,7 @@ const MemoryConfig: FC<{ options: Suggestion[]; parentName: string; }> = ({ return ( <> {values?.memory?.enable && <> -
+
{t('workflow.config.llm.memory')} {t('workflow.config.llm.inner')}
@@ -40,6 +40,7 @@ const MemoryConfig: FC<{ options: Suggestion[]; parentName: string; }> = ({ isArray={false} parentName={[parentName, 'messages']} options={options} + size="small" /> diff --git a/web/src/views/Workflow/components/Properties/MessageEditor.tsx b/web/src/views/Workflow/components/Properties/MessageEditor.tsx index 939bda01..fa1ffb80 100644 --- a/web/src/views/Workflow/components/Properties/MessageEditor.tsx +++ b/web/src/views/Workflow/components/Properties/MessageEditor.tsx @@ -1,13 +1,14 @@ import { type FC, useMemo } from 'react'; +import clsx from 'clsx' import { useTranslation } from 'react-i18next' import { Input, Form, Space, Button, Row, Col, Select, type FormListOperation } from 'antd'; -import { MinusCircleOutlined } from '@ant-design/icons'; import Editor from '../Editor' import type { Suggestion } from '../Editor/plugin/AutocompletePlugin' interface MessageEditor { options: Suggestion[]; - title?: string + title?: string; + titleVariant?: 'outlined' | 'borderless'; isArray?: boolean; parentName?: string | string[]; label?: string; @@ -15,6 +16,7 @@ interface MessageEditor { value?: string; enableJinja2?: boolean; onChange?: (value?: string) => void; + size?: 'small' | 'default' } const roleOptions = [ // { label: 'SYSTEM', value: 'SYSTEM' }, @@ -23,11 +25,13 @@ const roleOptions = [ ] const MessageEditor: FC = ({ title, + titleVariant = 'outlined', isArray = true, parentName = 'messages', placeholder, options, enableJinja2 = false, + size = 'default' }) => { const { t } = useTranslation() const form = Form.useFormInstance(); @@ -74,14 +78,16 @@ const MessageEditor: FC = ({ if (!isArray) { return ( - + - {title ?? t('workflow.answerDesc')} +
{title ?? t('workflow.answerDesc')}
- +
); @@ -90,7 +96,7 @@ const MessageEditor: FC = ({ return ( {(fields, { add, remove }) => ( - + {fields.map(({ key, name, ...restField }) => { const fieldValue = Array.isArray(parentName) ? parentName.reduce((obj, key) => obj?.[key], values) @@ -99,16 +105,17 @@ const MessageEditor: FC = ({ const currentRole = (fieldValue?.[name]?.role || 'USER').toUpperCase(); return ( - + {currentRole === 'SYSTEM' ? ( - + ) : ( ({ value: vo, label: vo }))} placeholder={t('common.pleaseSelect')} /> + ? = ({ if (selectedNode?.data?.type === 'start' && key === 'variables' && config.type === 'define') { return ( -
-
-
- {t(`workflow.config.${selectedNode?.data?.type}.${key}`)} -
- -
- - - {Array.isArray(config.defaultValue) && config.defaultValue?.map((vo, index) => -
- {vo.name}·{vo.description} - -
- {vo.required && {t('workflow.config.start.required')}} - {vo.type} -
- -
handleEditVariable(index, vo)} - >
-
handleDeleteVariable(index, vo)} - >
-
-
- )} - - {config.sys?.map((vo, index) => -
-
- sys.{vo.name} -
- {vo.type} -
- )} -
-
+ + + ) } @@ -1143,23 +1068,12 @@ const Properties: FC = ({ key={key} options={contextVariableList.filter(variable => variable.nodeData?.type !== 'knowledge-retrieval')} parentName={key} + placeholder={t(config.placeholder || 'common.pleaseSelect')} + size="small" />
) } - if (selectedNode?.data?.type === 'end' && key === 'output') { - return ( - - variable.nodeData?.type !== 'knowledge-retrieval')} - /> - - ) - } - if (config.type === 'define') { return null } @@ -1184,6 +1098,8 @@ const Properties: FC = ({ parentName={key} enableJinja2={config.enableJinja2 as boolean} options={getFilteredVariableList(selectedNode?.data?.type, key)} + titleVariant={config.titleVariant} + size="small" /> ) @@ -1206,9 +1122,9 @@ const Properties: FC = ({ name={key} options={getFilteredVariableList(selectedNode?.data?.type, key)} isCanAdd={!!(values as any)?.group} + size="small" /> - ) } if (config.type === 'caseList') { @@ -1226,9 +1142,7 @@ const Properties: FC = ({ if (config.type === 'mappingList') { return ( - + @@ -1238,6 +1152,7 @@ const Properties: FC = ({ return ( @@ -1276,87 +1191,14 @@ const Properties: FC = ({ ) } - - return ( - - {config.type === 'input' - ? - : config.type === 'textarea' - ? - : config.type === 'select' - ? + : config.type === 'textarea' + ? + : config.type === 'select' + ?