Merge pull request #148 from SuanmoSuanyangTechnology/feature/ui_zy
Feature/UI zy
This commit is contained in:
21
web/src/assets/images/workflow/deleteBg.svg
Normal file
21
web/src/assets/images/workflow/deleteBg.svg
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>编组 33</title>
|
||||
<g id="工作流" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="工作流--流程控制-条件分支" transform="translate(-1752, -352)" stroke="#5B6167">
|
||||
<g id="编组-37" transform="translate(1480, 63)">
|
||||
<g id="编组-35" transform="translate(8, 195)">
|
||||
<g id="编组-33" transform="translate(264, 94)">
|
||||
<g id="编组-32" transform="translate(3, 3.5)">
|
||||
<line x1="-1.63757896e-14" y1="2" x2="10" y2="2" id="路径-29"></line>
|
||||
<polyline id="路径-30" stroke-linejoin="round" points="3 1.99990611 3 0 7 0 7 2"></polyline>
|
||||
<path d="M1.5,2.01228712 L1.5,8 C1.5,8.55228475 1.94771525,9 2.5,9 L7.5,9 C8.05228475,9 8.5,8.55228475 8.5,8 L8.5,2 L8.5,2" id="路径-31" stroke-linejoin="round"></path>
|
||||
<line x1="4" y1="4.00683364" x2="4" y2="7.00683364" id="路径-32"></line>
|
||||
<line x1="6" y1="4.00683364" x2="6" y2="7.00683364" id="路径-32"></line>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
22
web/src/assets/images/workflow/deleteBg_hover.svg
Normal file
22
web/src/assets/images/workflow/deleteBg_hover.svg
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>编组 33</title>
|
||||
<g id="工作流" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="工作流--流程控制-条件分支" transform="translate(-1752, -420)">
|
||||
<g id="编组-37" transform="translate(1480, 63)">
|
||||
<g id="编组-35" transform="translate(8, 195)">
|
||||
<g id="编组-33" transform="translate(264, 162)">
|
||||
<rect id="矩形" fill="#FF5D34" opacity="0.116987" x="0" y="0" width="16" height="16" rx="4"></rect>
|
||||
<g id="编组-32" transform="translate(3, 3.5)" stroke="#FF5D34">
|
||||
<line x1="-1.63757896e-14" y1="2" x2="10" y2="2" id="路径-29"></line>
|
||||
<polyline id="路径-30" stroke-linejoin="round" points="3 1.99990611 3 0 7 0 7 2"></polyline>
|
||||
<path d="M1.5,2.01228712 L1.5,8 C1.5,8.55228475 1.94771525,9 2.5,9 L7.5,9 C8.05228475,9 8.5,8.55228475 8.5,8 L8.5,2 L8.5,2" id="路径-31" stroke-linejoin="round"></path>
|
||||
<line x1="4" y1="4.00683364" x2="4" y2="7.00683364" id="路径-32"></line>
|
||||
<line x1="6" y1="4.00683364" x2="6" y2="7.00683364" id="路径-32"></line>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
18
web/src/assets/images/workflow/delete_cycle.svg
Normal file
18
web/src/assets/images/workflow/delete_cycle.svg
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>编组 33</title>
|
||||
<g id="工作流" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="工作流--AI与认知处理-大语言模型" transform="translate(-1736, -662)" stroke="#212332" stroke-width="1.1">
|
||||
<g id="编组-34" transform="translate(1472, 64)">
|
||||
<g id="编组-3备份-10" transform="translate(12, 409)">
|
||||
<g id="选择备份" transform="translate(0, 177)">
|
||||
<g id="编组-33" transform="translate(252, 12)">
|
||||
<circle id="椭圆形" cx="8" cy="8" r="6.45"></circle>
|
||||
<line x1="6" y1="8" x2="10" y2="8" id="路径-10" stroke-linecap="round" stroke-linejoin="round"></line>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
18
web/src/assets/images/workflow/recall.svg
Normal file
18
web/src/assets/images/workflow/recall.svg
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="14px" height="14px" viewBox="0 0 14 14" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>召回</title>
|
||||
<g id="工作流" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="工作流--AI与认知处理-知识检索" transform="translate(-1684, -330)" stroke="#212332">
|
||||
<g id="节点属性" transform="translate(1472, 64)">
|
||||
<g id="编组-30" transform="translate(12, 264)">
|
||||
<g id="编组-28" transform="translate(196, 0)">
|
||||
<g id="召回" transform="translate(4, 2)">
|
||||
<path d="M3.00336574,8.78243131 C3.31276784,9.47513403 3.79697125,10.0726443 4.39990015,10.5188863 M5.34710411,11.051994 C5.85707009,11.2602342 6.41513255,11.375 7,11.375 C7.46865477,11.375 7.92009851,11.3013108 8.34337671,11.1648869 M9.11238544,10.8321699 C9.85595277,10.4214229 10.4672402,9.80055221 10.8661626,9.04964308 M11.1846924,8.28028469 C11.3084287,7.87534253 11.375,7.44544554 11.375,7 C11.375,4.58375422 9.41624578,2.625 7,2.625 C5.30981329,2.625 3.84348335,3.58344477 3.11486142,4.98648308" id="形状"></path>
|
||||
<polyline id="路径-10" stroke-linejoin="round" points="2.48490579 2.81937431 2.86401413 5.38855621 5.4725833 4.82767632"></polyline>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
18
web/src/assets/images/workflow/recall_hover.svg
Normal file
18
web/src/assets/images/workflow/recall_hover.svg
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="14px" height="14px" viewBox="0 0 14 14" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>召回</title>
|
||||
<g id="工作流" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="工作流--AI与认知处理-知识检索" transform="translate(-1684, -330)" stroke="#155EEF">
|
||||
<g id="节点属性" transform="translate(1472, 64)">
|
||||
<g id="编组-30" transform="translate(12, 264)">
|
||||
<g id="编组-28" transform="translate(196, 0)">
|
||||
<g id="召回" transform="translate(4, 2)">
|
||||
<path d="M3.00336574,8.78243131 C3.31276784,9.47513403 3.79697125,10.0726443 4.39990015,10.5188863 M5.34710411,11.051994 C5.85707009,11.2602342 6.41513255,11.375 7,11.375 C7.46865477,11.375 7.92009851,11.3013108 8.34337671,11.1648869 M9.11238544,10.8321699 C9.85595277,10.4214229 10.4672402,9.80055221 10.8661626,9.04964308 M11.1846924,8.28028469 C11.3084287,7.87534253 11.375,7.44544554 11.375,7 C11.375,4.58375422 9.41624578,2.625 7,2.625 C5.30981329,2.625 3.84348335,3.58344477 3.11486142,4.98648308" id="形状"></path>
|
||||
<polyline id="路径-10" stroke-linejoin="round" points="2.48490579 2.81937431 2.86401413 5.38855621 5.4725833 4.82767632"></polyline>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
12
web/src/components/FormItem/DescWrapper.tsx
Normal file
12
web/src/components/FormItem/DescWrapper.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
import clsx from "clsx";
|
||||
import type { FC, ReactNode } from "react";
|
||||
|
||||
const DescWrapper: FC<{desc: string | ReactNode, className?: string}> = ({desc, className}) => {
|
||||
return (
|
||||
<div className={clsx(className, "rb:text-[12px] rb:text-[#5B6167] rb:font-regular rb:leading-4 ")}>
|
||||
{desc}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default DescWrapper
|
||||
13
web/src/components/FormItem/LabelWrapper.tsx
Normal file
13
web/src/components/FormItem/LabelWrapper.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import clsx from "clsx";
|
||||
import type { FC, ReactNode } from "react";
|
||||
|
||||
const LabelWrapper: FC<{ title: string | ReactNode, className?: string; children?: ReactNode}> = ({title, className, children}) => {
|
||||
return (
|
||||
<div className={clsx(className)}>
|
||||
<div className="rb:text-[14px] rb:font-medium rb:leading-5">{title}</div>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default LabelWrapper
|
||||
45
web/src/components/FormItem/SwitchFormItem.tsx
Normal file
45
web/src/components/FormItem/SwitchFormItem.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
import { Switch, Form, ConfigProvider } from "antd";
|
||||
import useSize from 'antd/lib/config-provider/hooks/useSize'
|
||||
import type { FC, ReactNode } from "react";
|
||||
import { useContext } from "react";
|
||||
|
||||
import LabelWrapper from './LabelWrapper'
|
||||
import DescWrapper from './DescWrapper'
|
||||
|
||||
interface SwitchFormItemProps {
|
||||
title: string | ReactNode;
|
||||
desc?: string | ReactNode;
|
||||
name: string | string[];
|
||||
size?: 'small' | 'default'
|
||||
className?: string;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
const SwitchFormItem: FC<SwitchFormItemProps> = ({
|
||||
title,
|
||||
desc,
|
||||
name,
|
||||
size = 'default',
|
||||
className,
|
||||
disabled
|
||||
}) => {
|
||||
const componentSize = useSize()
|
||||
console.log('componentSize', componentSize)
|
||||
|
||||
return (
|
||||
<div className={`${className} rb:flex rb:items-center rb:justify-between`}>
|
||||
<LabelWrapper title={title}>
|
||||
{desc && <DescWrapper desc={desc} className="rb:mt-2" />}
|
||||
</LabelWrapper>
|
||||
<Form.Item
|
||||
name={name}
|
||||
valuePropName="checked"
|
||||
className="rb:mb-0!"
|
||||
>
|
||||
<Switch disabled={disabled} size={size} />
|
||||
</Form.Item>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default SwitchFormItem
|
||||
@@ -54,7 +54,7 @@ const AppHeader: FC<{source?: 'space' | 'manage';}> = ({source = 'manage'}) => {
|
||||
key: '1',
|
||||
label: (<>
|
||||
<div>{user.username}</div>
|
||||
<div className="rb:text-[12px] rb:text-[#5B6167] rb:mt-[8px]">{user.email}</div>
|
||||
<div className="rb:text-[12px] rb:text-[#5B6167] rb:mt-2">{user.email}</div>
|
||||
</>),
|
||||
},
|
||||
{
|
||||
|
||||
@@ -150,9 +150,19 @@ const RbMarkdown: FC<RbMarkdownProps> = ({
|
||||
)
|
||||
}
|
||||
|
||||
// 处理键盘快捷键
|
||||
const handleKeyDown = (e: React.KeyboardEvent) => {
|
||||
if ((e.ctrlKey || e.metaKey) && e.key === 'c') {
|
||||
const selection = window.getSelection()
|
||||
if (selection && selection.toString()) {
|
||||
navigator.clipboard.writeText(selection.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 预览模式
|
||||
return (
|
||||
<div className="rb:relative">
|
||||
<div className="rb:relative" onKeyDown={handleKeyDown} tabIndex={0}>
|
||||
<style>{`
|
||||
.html-comment {
|
||||
color: #999;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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<ModalProps> = ({
|
||||
onOk,
|
||||
onCancel,
|
||||
|
||||
@@ -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<string, string | number>;
|
||||
className?: string;
|
||||
size?: InputProps['size']
|
||||
}
|
||||
|
||||
const SearchInput: FC<SearchInputProps> = ({
|
||||
@@ -79,7 +80,7 @@ const SearchInput: FC<SearchInputProps> = ({
|
||||
return (
|
||||
<Input
|
||||
allowClear
|
||||
prefix={<img src={searchIcon} alt="search" className="rb:w-[16px] rb:h-[16px] rb:mr-[4px]" />}
|
||||
prefix={<img src={searchIcon} alt="search" className="rb:w-4 rb:h-4 rb:mr-1" />}
|
||||
placeholder={placeholder || t('user.searchPlaceholder')}
|
||||
value={value}
|
||||
onChange={handleChange}
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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: '清空',
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -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<ConfigHeaderProps> = ({
|
||||
{/* <Button type="primary">{t('workflow.export')}</Button> */}
|
||||
<img src={logoutIcon} className="rb:w-4 rb:h-4 rb:cursor-pointer" onClick={goToApplication} />
|
||||
</div>
|
||||
: <div className="rb:h-8 rb:flex rb:items-center rb:justify-end rb:text-[12px] rb:text-[#5B6167] rb:font-regular rb:cursor-pointer" onClick={goToApplication}>
|
||||
<img src={logoutIcon} className="rb:mr-2 rb:w-4 rb:h-4" />
|
||||
{t('application.returnToApplicationList')}
|
||||
</div>
|
||||
: <Flex justify="flex-end">
|
||||
<div className="rb:h-8 rb:flex rb:items-center rb:text-[12px] rb:text-[#5B6167] rb:font-regular rb:cursor-pointer" onClick={goToApplication}>
|
||||
<img src={logoutIcon} className="rb:mr-2 rb:w-4 rb:h-4" />
|
||||
{t('application.returnToApplicationList')}
|
||||
</div>
|
||||
</Flex>
|
||||
}
|
||||
</Header>
|
||||
<ApplicationModal
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Row, Col, Form, Slider, Button, Alert, message, Switch, Space } from 'antd';
|
||||
import { Row, Col, Form, Slider, Button, Alert, message, Space } from 'antd';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import RbCard from '@/components/RbCard/Card';
|
||||
@@ -9,6 +9,7 @@ import type { ConfigForm } from './types'
|
||||
import CustomSelect from '@/components/CustomSelect';
|
||||
import { getModelListUrl } from '@/api/models'
|
||||
import Tag from '@/components/Tag'
|
||||
import SwitchFormItem from '@/components/FormItem/SwitchFormItem'
|
||||
|
||||
const configList = [
|
||||
{
|
||||
@@ -158,23 +159,17 @@ const EmotionEngine: React.FC = () => {
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="rb:flex rb:items-center rb:justify-between rb:mb-6">
|
||||
<div>
|
||||
<span className="rb:text-[14px] rb:font-medium rb:leading-5">{t(`emotionEngine.${config.key}`)}</span>
|
||||
<SwitchFormItem
|
||||
title={t(`emotionEngine.${config.key}`)}
|
||||
name={config.key}
|
||||
desc={<>
|
||||
{config.hasSubTitle && <div className="rb:mt-1 rb:text-[12px] rb:text-[#5B6167] rb:font-regular rb:leading-4">{t(`emotionEngine.${config.key}_subTitle`)}</div>}
|
||||
<div className="rb:mt-1 rb:text-[12px] rb:text-[#5B6167] rb:font-regular rb:leading-4">{t(`emotionEngine.${config.key}_desc`)}</div>
|
||||
</div>
|
||||
<Form.Item
|
||||
name={config.key}
|
||||
valuePropName="checked"
|
||||
className="rb:ml-2 rb:mb-0!"
|
||||
>
|
||||
<Switch
|
||||
disabled={!values?.emotion_enabled && config.key !== 'emotion_enabled'} />
|
||||
</Form.Item>
|
||||
</div>
|
||||
</>}
|
||||
className="rb:mb-6"
|
||||
disabled={!values?.emotion_enabled && config.key !== 'emotion_enabled'}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
<Row gutter={16} className="rb:mt-3">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Row, Col, Form, Slider, Button, Space, message, Switch } from 'antd';
|
||||
import { Row, Col, Form, Slider, Button, Space, message } from 'antd';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import RbCard from '@/components/RbCard/Card';
|
||||
@@ -7,6 +7,7 @@ import strategyImpactSimulator from '@/assets/images/memory/strategyImpactSimula
|
||||
import LineChart from './components/LineChart'
|
||||
import { getMemoryForgetConfig, updateMemoryForgetConfig } from '@/api/memory'
|
||||
import type { ConfigForm } from './types'
|
||||
import SwitchFormItem from '@/components/FormItem/SwitchFormItem'
|
||||
|
||||
const configList = [
|
||||
{
|
||||
@@ -155,26 +156,12 @@ const ForgettingEngine: React.FC = () => {
|
||||
{configList.map(config => {
|
||||
if (config.type === 'button') {
|
||||
return (
|
||||
<div key={config.key} className="rb:mb-2">
|
||||
<div className="rb:flex rb:items-center rb:justify-between">
|
||||
<div>
|
||||
<span className="rb:text-[14px] rb:font-medium rb:leading-5">{t(`forgettingEngine.${config.key}`)}</span>
|
||||
</div>
|
||||
<Form.Item
|
||||
name={config.name}
|
||||
valuePropName="checked"
|
||||
className="rb:ml-2 rb:mb-0!"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
</div>
|
||||
<div className="rb:flex rb:text-[12px] rb:items-center rb:justify-between rb:text-[#5B6167] rb:leading-5">
|
||||
<Space size={4}>
|
||||
{config.range && <span>{t(`forgettingEngine.range`)}: {config.range?.join('-')}</span>}
|
||||
{config.type && <span>{t(`forgettingEngine.type`)}: {config.type}</span>}
|
||||
</Space>
|
||||
</div>
|
||||
</div>
|
||||
<SwitchFormItem
|
||||
title={t(`forgettingEngine.${config.key}`)}
|
||||
name={config.name}
|
||||
desc={config.type && <span>{t(`forgettingEngine.type`)}: {config.type}</span>}
|
||||
className="rb:mb-2"
|
||||
/>
|
||||
)
|
||||
}
|
||||
return (
|
||||
@@ -191,8 +178,6 @@ const ForgettingEngine: React.FC = () => {
|
||||
>
|
||||
{config.type === 'decimal'
|
||||
? <Slider tooltip={{ open: false }} max={config.range?.[1] || 1} min={config.range?.[0] || 0} step={config.step ?? 0.01} style={{ margin: '0' }} />
|
||||
: config.type === 'button'
|
||||
? <Switch />
|
||||
: null
|
||||
}
|
||||
</Form.Item>
|
||||
|
||||
@@ -11,6 +11,7 @@ import { getModelList } from '@/api/models';
|
||||
import type { Model } from '@/views/ModelManagement/types'
|
||||
import { configList } from './constant'
|
||||
import Result from './components/Result'
|
||||
import SwitchFormItem from '@/components/FormItem/SwitchFormItem'
|
||||
|
||||
const keys = [
|
||||
// 'example',
|
||||
@@ -173,25 +174,18 @@ const MemoryExtractionEngine: FC = () => {
|
||||
}
|
||||
)}
|
||||
>
|
||||
<div className="rb:text-[16px] rb:font-medium rb:leading-[22px]">{t(`memoryExtractionEngine.${vo.title}`)}</div>
|
||||
<div className="rb:text-[16px] rb:font-medium rb:leading-5.5">{t(`memoryExtractionEngine.${vo.title}`)}</div>
|
||||
<div className="rb:mt-1 rb:text-[12px] rb:text-[#5B6167] rb:font-regular rb:leading-4">{t(`memoryExtractionEngine.${vo.title}SubTitle`)}</div>
|
||||
|
||||
{vo.list.map(config => (
|
||||
<div key={config.label}>
|
||||
{config.control === 'button' &&
|
||||
<div className="rb:flex rb:items-center rb:justify-between rb:mt-6">
|
||||
<div>
|
||||
<span className="rb:text-[14px] rb:font-medium rb:leading-5">-{t(`memoryExtractionEngine.${config.label}`)}</span>
|
||||
<ConfigDesc config={config} className="rb:ml-2" />
|
||||
</div>
|
||||
<Form.Item
|
||||
name={config.variableName}
|
||||
valuePropName="checked"
|
||||
className="rb:ml-2 rb:mb-0!"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
</div>
|
||||
<SwitchFormItem
|
||||
title={<>-{t(`memoryExtractionEngine.${config.label}`)}</>}
|
||||
name={config.variableName}
|
||||
desc={<ConfigDesc config={config} className="rb:ml-2" />}
|
||||
className="rb:mt-6"
|
||||
/>
|
||||
}
|
||||
{config.control === 'select' &&
|
||||
<>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Row, Col, Form, App, Button, Switch, Space, Select } from 'antd';
|
||||
import { Row, Col, Form, App, Button, Space, Select } from 'antd';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
@@ -11,6 +11,7 @@ import CustomSelect from '@/components/CustomSelect';
|
||||
import { getModelListUrl } from '@/api/models'
|
||||
import Tag from '@/components/Tag'
|
||||
import { useI18n } from '@/store/locale';
|
||||
import SwitchFormItem from '@/components/FormItem/SwitchFormItem'
|
||||
|
||||
const configList = [
|
||||
// 启用反思引擎
|
||||
@@ -219,21 +220,16 @@ const SelfReflectionEngine: React.FC = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="rb:flex rb:items-center rb:justify-between rb:mb-6">
|
||||
<div>
|
||||
<span className="rb:text-[14px] rb:font-medium rb:leading-5">{t(`reflectionEngine.${config.key}`)}</span>
|
||||
<SwitchFormItem
|
||||
title={t(`reflectionEngine.${config.key}`)}
|
||||
name={config.key}
|
||||
desc={<>
|
||||
{(config as any).hasSubTitle && <div className="rb:mt-1 rb:text-[12px] rb:text-[#5B6167] rb:font-regular rb:leading-4">{t(`reflectionEngine.${config.key}_subTitle`)}</div>}
|
||||
<div className="rb:mt-1 rb:text-[12px] rb:text-[#5B6167] rb:font-regular rb:leading-4">{t(`reflectionEngine.${config.key}_desc`)}</div>
|
||||
</div>
|
||||
<Form.Item
|
||||
name={config.key}
|
||||
valuePropName="checked"
|
||||
className="rb:ml-2 rb:mb-0!"
|
||||
>
|
||||
<Switch
|
||||
disabled={!values?.reflection_enabled && config.key !== 'reflection_enabled'} />
|
||||
</Form.Item>
|
||||
</div>
|
||||
</>}
|
||||
className="rb:mb-6"
|
||||
disabled={!values?.reflection_enabled && config.key !== 'reflection_enabled'}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
<Row gutter={16} className="rb:mt-3">
|
||||
|
||||
@@ -2,12 +2,13 @@ import { forwardRef, useImperativeHandle, useState } from 'react';
|
||||
import { Form, Input, InputNumber, Checkbox } from 'antd';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import type { StartVariableItem, VariableConfigModalRef } from '../../types'
|
||||
import type { VariableConfigModalRef } from '../../types'
|
||||
import type { Variable } from '../Properties/VariableList/types'
|
||||
import RbModal from '@/components/RbModal'
|
||||
|
||||
interface VariableEditModalProps {
|
||||
refresh: (values: StartVariableItem[]) => void;
|
||||
variables: StartVariableItem[]
|
||||
refresh: (values: Variable[]) => void;
|
||||
variables: Variable[]
|
||||
}
|
||||
|
||||
const VariableConfigModal = forwardRef<VariableConfigModalRef, VariableEditModalProps>(({
|
||||
@@ -15,9 +16,9 @@ const VariableConfigModal = forwardRef<VariableConfigModalRef, VariableEditModal
|
||||
}, ref) => {
|
||||
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<StartVariableItem[]>([])
|
||||
const [initialValues, setInitialValues] = useState<Variable[]>([])
|
||||
|
||||
// 封装取消方法,添加关闭弹窗逻辑
|
||||
const handleClose = () => {
|
||||
@@ -26,7 +27,7 @@ const VariableConfigModal = forwardRef<VariableConfigModalRef, VariableEditModal
|
||||
setLoading(false)
|
||||
};
|
||||
|
||||
const handleOpen = (values: StartVariableItem[]) => {
|
||||
const handleOpen = (values: Variable[]) => {
|
||||
setVisible(true);
|
||||
form.setFieldsValue({variables: values})
|
||||
setInitialValues([...values])
|
||||
|
||||
@@ -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<LexicalEditorProps> =({
|
||||
onChange,
|
||||
options,
|
||||
variant = 'borderless',
|
||||
height = 60,
|
||||
enableJinja2 = false,
|
||||
size = 'default',
|
||||
type = 'textarea'
|
||||
}) => {
|
||||
|
||||
const [_count, setCount] = useState(0);
|
||||
@@ -94,12 +99,9 @@ const Editor: FC<LexicalEditorProps> =({
|
||||
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<LexicalEditorProps> =({
|
||||
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 (
|
||||
<LexicalComposer initialConfig={initialConfig}>
|
||||
@@ -152,7 +169,7 @@ const Editor: FC<LexicalEditorProps> =({
|
||||
<div className="editor-with-line-numbers" style={{
|
||||
border: variant === 'borderless' ? 'none' : '1px solid #DFE4ED',
|
||||
borderRadius: '6px',
|
||||
minHeight: `${height}px`,
|
||||
minHeight: minheight,
|
||||
}}>
|
||||
<div className="line-numbers">
|
||||
<div>1</div>
|
||||
@@ -160,12 +177,12 @@ const Editor: FC<LexicalEditorProps> =({
|
||||
<ContentEditable
|
||||
className="editor-content-with-numbers"
|
||||
style={{
|
||||
minHeight: `${height}px`,
|
||||
padding: '4px 11px',
|
||||
minHeight: minheight,
|
||||
padding: '4px 0',
|
||||
outline: 'none',
|
||||
resize: 'none',
|
||||
fontSize: '14px',
|
||||
lineHeight: '20px',
|
||||
fontSize: fontSize,
|
||||
lineHeight: lineHeight,
|
||||
border: 'none',
|
||||
}}
|
||||
/>
|
||||
@@ -173,14 +190,14 @@ const Editor: FC<LexicalEditorProps> =({
|
||||
) : (
|
||||
<ContentEditable
|
||||
style={{
|
||||
minHeight: `${height}px`,
|
||||
minHeight: minheight,
|
||||
padding: variant === 'borderless' ? '0' : '4px 11px',
|
||||
border: variant === 'borderless' ? 'none' : '1px solid #DFE4ED',
|
||||
borderRadius: '6px',
|
||||
outline: 'none',
|
||||
resize: 'none',
|
||||
fontSize: '14px',
|
||||
lineHeight: '20px',
|
||||
fontSize: fontSize,
|
||||
lineHeight: lineHeight,
|
||||
}}
|
||||
/>
|
||||
)
|
||||
@@ -188,12 +205,13 @@ const Editor: FC<LexicalEditorProps> =({
|
||||
placeholder={
|
||||
<div
|
||||
style={{
|
||||
minHeight: placeHolderMinheight,
|
||||
position: 'absolute',
|
||||
top: variant === 'borderless' ? '0' : '6px',
|
||||
left: enableJinja2 ? '59px' : (variant === 'borderless' ? '0' : '11px'),
|
||||
color: '#5B6167',
|
||||
fontSize: '14px',
|
||||
lineHeight: '20px',
|
||||
color: '#A8A9AA',
|
||||
fontSize: fontSize,
|
||||
lineHeight: placeHolderMinheight,
|
||||
pointerEvents: 'none',
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -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]);
|
||||
|
||||
|
||||
@@ -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<AssignmentListProps> = ({
|
||||
parentName,
|
||||
options = [],
|
||||
size = 'small'
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const form = Form.useFormInstance();
|
||||
@@ -39,109 +40,126 @@ const AssignmentList: FC<AssignmentListProps> = ({
|
||||
<Form.List name={parentName}>
|
||||
{(fields, { add, remove }) => (
|
||||
<>
|
||||
<div className="rb:flex rb:justify-between">
|
||||
{t(`workflow.config.assigner.${parentName}`)}
|
||||
<PlusOutlined onClick={() => add({ operation: 'cover'})} />
|
||||
</div>
|
||||
{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 (
|
||||
<div key={key} className="rb:mb-4">
|
||||
<Row gutter={12} className="rb:mb-2!">
|
||||
<Col span={14}>
|
||||
<Form.Item
|
||||
{...restField}
|
||||
name={[name, 'variable_selector']}
|
||||
noStyle
|
||||
>
|
||||
<VariableSelect
|
||||
placeholder={t('common.pleaseSelect')}
|
||||
options={options.filter(vo => 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);
|
||||
}}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Form.Item
|
||||
{...restField}
|
||||
name={[name, 'operation']}
|
||||
noStyle
|
||||
>
|
||||
<Select
|
||||
placeholder={t('common.pleaseSelect')}
|
||||
options={operationOptions.map(op => ({
|
||||
...op,
|
||||
label: t(op.label)
|
||||
}))}
|
||||
popupMatchSelectWidth={false}
|
||||
onChange={() => {
|
||||
form.setFieldValue([parentName, name, 'value'], undefined);
|
||||
}}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={2} className="rb:flex! rb:items-center rb:justify-end">
|
||||
<MinusCircleOutlined onClick={() => remove(name)} />
|
||||
</Col>
|
||||
</Row>
|
||||
<div className="rb:flex rb:items-center rb:justify-between rb:mb-2.5">
|
||||
<div className="rb:text-[12px] rb:leading-4.5 rb:font-medium">
|
||||
{t(`workflow.config.assigner.${parentName}`)}
|
||||
</div>
|
||||
|
||||
<Form.Item shouldUpdate noStyle>
|
||||
{(form) => {
|
||||
const operation = form.getFieldValue([parentName, name, 'operation']);
|
||||
if (operation === 'clear') return null;
|
||||
|
||||
return (
|
||||
<Button
|
||||
onClick={() => add({ operation: 'cover' })}
|
||||
className="rb:py-0! rb:px-1! rb:text-[12px]!"
|
||||
size="small"
|
||||
>
|
||||
+ {t('workflow.config.addVariable')}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<Space size={10} direction="vertical" className="rb:w-full!">
|
||||
{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 (
|
||||
<div key={key} className="rb:flex rb:items-start">
|
||||
<div className="rb:flex-1">
|
||||
<div className="rb:flex rb:gap-1 rb:mb-1">
|
||||
<Form.Item
|
||||
{...restField}
|
||||
name={[name, 'value']}
|
||||
name={[name, 'variable_selector']}
|
||||
noStyle
|
||||
>
|
||||
{dataType === 'number' && operation === 'cover'
|
||||
? <VariableSelect
|
||||
placeholder={t('common.pleaseSelect')}
|
||||
options={dataType ? options.filter(vo => vo.dataType === dataType) : options}
|
||||
popupMatchSelectWidth={false}
|
||||
/>
|
||||
: dataType === 'number'
|
||||
? <InputNumber
|
||||
placeholder={t('common.pleaseEnter')}
|
||||
className="rb:w-full!"
|
||||
onChange={(value) => form.setFieldValue([name, 'value'], value)}
|
||||
/>
|
||||
: operation === 'assign'
|
||||
? <>
|
||||
{dataType === 'boolean'
|
||||
? <Radio.Group block>
|
||||
<Radio.Button value={true}>True</Radio.Button>
|
||||
<Radio.Button value={false}>False</Radio.Button>
|
||||
</Radio.Group>
|
||||
: <Input.TextArea
|
||||
placeholder={t('common.pleaseEnter')}
|
||||
rows={3}
|
||||
/>
|
||||
}
|
||||
</>
|
||||
: <VariableSelect
|
||||
placeholder={t('common.pleaseSelect')}
|
||||
options={dataType ? options.filter(vo => vo.dataType === dataType) : options}
|
||||
popupMatchSelectWidth={false}
|
||||
/>
|
||||
}
|
||||
<VariableSelect
|
||||
placeholder={t('common.pleaseSelect')}
|
||||
options={options.filter(vo => 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);
|
||||
}}
|
||||
size={size}
|
||||
className="rb:w-39!"
|
||||
/>
|
||||
</Form.Item>
|
||||
);
|
||||
}}
|
||||
</Form.Item>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
<Form.Item
|
||||
{...restField}
|
||||
name={[name, 'operation']}
|
||||
noStyle
|
||||
>
|
||||
<Select
|
||||
placeholder={t('common.pleaseSelect')}
|
||||
options={operationOptions.map(op => ({
|
||||
...op,
|
||||
label: t(op.label)
|
||||
}))}
|
||||
popupMatchSelectWidth={false}
|
||||
onChange={() => {
|
||||
form.setFieldValue([parentName, name, 'value'], undefined);
|
||||
}}
|
||||
size={size}
|
||||
className="rb:w-24!"
|
||||
/>
|
||||
</Form.Item>
|
||||
</div>
|
||||
<Form.Item shouldUpdate noStyle>
|
||||
{(form) => {
|
||||
const operation = form.getFieldValue([parentName, name, 'operation']);
|
||||
if (operation === 'clear') return null;
|
||||
|
||||
return (
|
||||
<Form.Item
|
||||
{...restField}
|
||||
name={[name, 'value']}
|
||||
noStyle
|
||||
>
|
||||
{dataType === 'number' && operation === 'cover'
|
||||
? <VariableSelect
|
||||
placeholder={t('common.pleaseSelect')}
|
||||
options={dataType ? options.filter(vo => vo.dataType === dataType) : options}
|
||||
popupMatchSelectWidth={false}
|
||||
size={size}
|
||||
/>
|
||||
: dataType === 'number'
|
||||
? <InputNumber
|
||||
placeholder={t('common.pleaseEnter')}
|
||||
className="rb:w-full!"
|
||||
onChange={(value) => form.setFieldValue([name, 'value'], value)}
|
||||
size={size}
|
||||
/>
|
||||
: operation === 'assign'
|
||||
? <>
|
||||
{dataType === 'boolean'
|
||||
? <Radio.Group block size={size}>
|
||||
<Radio.Button value={true}>True</Radio.Button>
|
||||
<Radio.Button value={false}>False</Radio.Button>
|
||||
</Radio.Group>
|
||||
: <Input.TextArea
|
||||
placeholder={t('common.pleaseEnter')}
|
||||
rows={3}
|
||||
/>
|
||||
}
|
||||
</>
|
||||
: <VariableSelect
|
||||
placeholder={t('common.pleaseSelect')}
|
||||
options={dataType ? options.filter(vo => vo.dataType === dataType) : options}
|
||||
popupMatchSelectWidth={false}
|
||||
size={size}
|
||||
/>
|
||||
}
|
||||
</Form.Item>
|
||||
);
|
||||
}}
|
||||
</Form.Item>
|
||||
</div>
|
||||
<div
|
||||
className="rb:ml-1 rb:size-4 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/workflow/deleteBg.svg')] rb:hover:bg-[url('@/assets/images/workflow/deleteBg_hover.svg')]"
|
||||
onClick={() => remove(name)}
|
||||
></div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</Space>
|
||||
</>
|
||||
)}
|
||||
</Form.List>
|
||||
|
||||
@@ -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<CaseListProps> = ({
|
||||
{(conditionFields, { add: addCondition, remove: removeCondition }) => {
|
||||
const logicalOperator = form.getFieldValue(name)?.[caseIndex]?.logical_operator || 'and'
|
||||
return (
|
||||
<div className={clsx("rb:relative rb:mb-4 rb:border rb:border-gray-200 rb:rounded rb:p-3 rb:pl-5")}>
|
||||
<div className="rb:flex rb:items-center rb:justify-between rb:mb-3">
|
||||
<span className="rb:font-medium">
|
||||
{caseIndex === 0 ? 'IF' : 'ELIF'}<br/>
|
||||
{caseFields.length > 1 && <span className="rb:text-[10px] rb:text-[#5B6167]">{`CASE ${caseIndex + 1}`}</span>}
|
||||
</span>
|
||||
<div className={clsx("rb:relative")}>
|
||||
<div className="rb:flex rb:items-center rb:justify-between rb:mb-2">
|
||||
<div className="rb:text-[12px] rb:leading-4.5">
|
||||
<span className="rb:font-medium ">{caseIndex === 0 ? 'IF' : 'ELIF'}</span>
|
||||
{caseFields.length > 1 && <span className="rb:text-[10px] rb:text-[#5B6167]"> ({`CASE ${caseIndex + 1}`})</span>}
|
||||
</div>
|
||||
|
||||
<Space>
|
||||
<Button
|
||||
type="dashed"
|
||||
onClick={() => addCondition({})}
|
||||
className="rb:py-0! rb:px-1! rb:text-[12px]!"
|
||||
size="small"
|
||||
>
|
||||
+ {t('workflow.config.addCase')}
|
||||
</Button>
|
||||
{caseFields.length > 1 && <DeleteOutlined
|
||||
className="rb:text-[12px]"
|
||||
onClick={() => handleRemoveCase(removeCase, caseField.name, caseIndex)}
|
||||
/>}
|
||||
{caseFields.length > 1 &&
|
||||
<Button
|
||||
className="rb:py-0! rb:px-1! rb:text-[12px]!"
|
||||
onClick={() => handleRemoveCase(removeCase, caseField.name, caseIndex)}
|
||||
>
|
||||
{t('common.remove')}
|
||||
</Button>
|
||||
}
|
||||
</Space>
|
||||
</div>
|
||||
{conditionFields?.length > 1 &&
|
||||
<>
|
||||
<div className="rb:absolute rb:w-3 rb:left-2 rb:top-15 rb:bottom-6 rb:z-10 rb:border rb:border-[#DFE4ED] rb:rounded-l-md rb:border-r-0"></div>
|
||||
<div className="rb:absolute rb:z-10 rb:left-0 rb:top-[50%] rb:transform-[translateY(-50%)]]">
|
||||
{conditionFields?.length > 1 && <div className="rb:absolute rb:top-8 rb:bottom-4 rb:w-8.5 rb:h-[calc(100%-32px)]">
|
||||
<div className="rb:absolute rb:w-2.5 rb:h-[calc(50%-30px)] rb:left-5 rb:top-4 rb:z-10 rb:border-l rb:border-t rb:border-[#DFE4ED] rb:rounded-tl-[10px] rb:border-r-0"></div>
|
||||
<div className="rb:absolute rb:z-10 rb:left-0 rb:top-[calc(50%-13px)]">
|
||||
<Form.Item name={[caseField.name, 'logical_operator']} noStyle >
|
||||
<Button size="small" className="rb:cursor-pointer" onClick={() => handleChangeLogicalOperator(caseIndex)}>{logicalOperator}</Button>
|
||||
<Button size="small" className="rb:text-[12px]! rb:py-px! rb:px-1! rb:w-8.5! rb:h-5!" onClick={() => handleChangeLogicalOperator(caseIndex)}>{logicalOperator}</Button>
|
||||
</Form.Item>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
<div className="rb:absolute rb:w-2.5 rb:h-[calc(50%-30px)] rb:left-5 rb:bottom-4 rb:z-10 rb:border-l rb:border-b rb:border-[#DFE4ED] rb:rounded-bl-[10px] rb:border-r-0"></div>
|
||||
</div>}
|
||||
{conditionFields.map((conditionField, conditionIndex) => {
|
||||
const cases = form.getFieldValue(name) || [];
|
||||
const currentCase = cases[caseIndex] || {};
|
||||
@@ -290,91 +292,86 @@ const CaseList: FC<CaseListProps> = ({
|
||||
const operatorList = operatorsObj[leftFieldType || 'default'] || operatorsObj.default || [];
|
||||
const inputType = leftFieldType === 'number' ? currentExpression.input_type : undefined;
|
||||
return (
|
||||
<div key={conditionField.key} className={clsx({
|
||||
"rb:mb-3": conditionIndex !== conditionFields.length - 1
|
||||
})}>
|
||||
<div className="rb:border rb:border-[#DFE4ED] rb:rounded-md rb:px-2 rb:py-1.5 rb:bg-white">
|
||||
<Row gutter={12} className="rb:mb-1">
|
||||
<Col span={14}>
|
||||
<Form.Item name={[conditionField.name, 'left']} noStyle>
|
||||
<VariableSelect
|
||||
placeholder={t('common.pleaseSelect')}
|
||||
options={options}
|
||||
size="small"
|
||||
allowClear={false}
|
||||
popupMatchSelectWidth={false}
|
||||
onChange={(val) => handleLeftFieldChange(caseIndex, conditionIndex, val)}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Form.Item name={[conditionField.name, 'operator']} noStyle>
|
||||
<Select
|
||||
options={operatorList.map(vo => ({
|
||||
...vo,
|
||||
label: t(String(vo?.label || ''))
|
||||
}))}
|
||||
size="small"
|
||||
popupMatchSelectWidth={false}
|
||||
placeholder={t('common.pleaseSelect')}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={2}>
|
||||
<DeleteOutlined
|
||||
className="rb:text-[12px]"
|
||||
onClick={() => removeCondition(conditionField.name)}
|
||||
<div key={conditionField.key} className="rb:flex rb:items-start rb:ml-9.5 rb:mb-4">
|
||||
<div className="rb:flex-1 rb:bg-[#F6F8FC] rb:border rb:border-[#DFE4ED] rb:rounded-md">
|
||||
<div className={clsx("rb:flex rb:gap-1 rb:p-1", {
|
||||
'rb:border-b rb:border-b-[#DFE4ED]': !hideRightField
|
||||
})}>
|
||||
<Form.Item name={[conditionField.name, 'left']} noStyle>
|
||||
<VariableSelect
|
||||
placeholder={t('common.pleaseSelect')}
|
||||
options={options}
|
||||
size="small"
|
||||
allowClear={false}
|
||||
popupMatchSelectWidth={false}
|
||||
onChange={(val) => handleLeftFieldChange(caseIndex, conditionIndex, val)}
|
||||
className="rb:bg-white! rb:w-29.5!"
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form.Item>
|
||||
<Form.Item name={[conditionField.name, 'operator']} noStyle>
|
||||
<Select
|
||||
options={operatorList.map(vo => ({
|
||||
...vo,
|
||||
label: t(String(vo?.label || ''))
|
||||
}))}
|
||||
size="small"
|
||||
popupMatchSelectWidth={false}
|
||||
placeholder={t('common.pleaseSelect')}
|
||||
className="rb:bg-white! rb:w-22!"
|
||||
/>
|
||||
</Form.Item>
|
||||
</div>
|
||||
|
||||
{!hideRightField && <>
|
||||
{!hideRightField && <div className="rb:p-1">
|
||||
{leftFieldType === 'number'
|
||||
? <Row>
|
||||
<Col span={12}>
|
||||
<Form.Item name={[conditionField.name, 'input_type']} noStyle>
|
||||
<Select
|
||||
? <div className="rb:flex rb:items-center">
|
||||
<Form.Item name={[conditionField.name, 'input_type']} noStyle>
|
||||
<Select
|
||||
placeholder={t('common.pleaseSelect')}
|
||||
options={[{ value: 'Variable', label: 'Variable' }, { value: 'Constant', label: 'Constant' }]}
|
||||
popupMatchSelectWidth={false}
|
||||
variant="borderless"
|
||||
onChange={() => handleInputTypeChange(caseIndex, conditionIndex)}
|
||||
className="rb:w-18!"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Divider type="vertical" />
|
||||
<Form.Item name={[conditionField.name, 'right']} noStyle>
|
||||
{inputType === 'Variable'
|
||||
?
|
||||
<VariableSelect
|
||||
placeholder={t('common.pleaseSelect')}
|
||||
options={[{ value: 'Variable', label: 'Variable' }, { value: 'Constant', label: 'Constant' }]}
|
||||
options={options.filter(vo => vo.dataType === 'number')}
|
||||
allowClear={false}
|
||||
popupMatchSelectWidth={false}
|
||||
variant="borderless"
|
||||
onChange={() => handleInputTypeChange(caseIndex, conditionIndex)}
|
||||
size="small"
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item name={[conditionField.name, 'right']} noStyle>
|
||||
{inputType === 'Variable'
|
||||
?
|
||||
<VariableSelect
|
||||
placeholder={t('common.pleaseSelect')}
|
||||
options={options.filter(vo => vo.dataType === 'number')}
|
||||
allowClear={false}
|
||||
popupMatchSelectWidth={false}
|
||||
: <InputNumber
|
||||
placeholder={t('common.pleaseEnter')}
|
||||
variant="borderless"
|
||||
className="rb:w-full!"
|
||||
onChange={(value) => form.setFieldValue([name, caseIndex, 'expressions', conditionIndex, 'right'], value)}
|
||||
/>
|
||||
: <InputNumber
|
||||
placeholder={t('common.pleaseEnter')}
|
||||
variant="borderless"
|
||||
className="rb:w-full!"
|
||||
onChange={(value) => form.setFieldValue([name, caseIndex, 'expressions', conditionIndex, 'right'], value)}
|
||||
/>
|
||||
}
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
}
|
||||
</Form.Item>
|
||||
</div>
|
||||
: <Form.Item name={[conditionField.name, 'right']} noStyle>
|
||||
{leftFieldType === 'boolean'
|
||||
? <Radio.Group block>
|
||||
<Radio.Button value={true}>True</Radio.Button>
|
||||
<Radio.Button value={false}>False</Radio.Button>
|
||||
</Radio.Group>
|
||||
: <Editor options={options} />
|
||||
: <Editor options={options} size="small" type="input" />
|
||||
}
|
||||
</Form.Item>
|
||||
}
|
||||
</>}
|
||||
</div>}
|
||||
</div>
|
||||
<div
|
||||
className="rb:ml-1 rb:size-4 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/workflow/deleteBg.svg')] rb:hover:bg-[url('@/assets/images/workflow/deleteBg_hover.svg')]"
|
||||
onClick={() => removeCondition(conditionField.name)}
|
||||
></div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
@@ -388,6 +385,8 @@ const CaseList: FC<CaseListProps> = ({
|
||||
<Button
|
||||
type="dashed"
|
||||
block
|
||||
size="middle"
|
||||
className="rb:text-[12px]!"
|
||||
onClick={() => handleAddCase(addCase)}
|
||||
>
|
||||
+ ELIF
|
||||
@@ -395,9 +394,9 @@ const CaseList: FC<CaseListProps> = ({
|
||||
</>
|
||||
)}
|
||||
</Form.List>
|
||||
<Divider />
|
||||
<div className="rb:font-medium">ELSE</div>
|
||||
<div className="rb:text-[12px] rb:text-[#5B6167] ">{t('workflow.config.if-else.else_desc')}</div>
|
||||
|
||||
<div className="rb:font-medium rb:text-[12px] rb:mt-4 rb:leading-4.5">ELSE</div>
|
||||
<div className="rb:text-[12px] rb:text-[#5B6167] rb:mt-2 rb:leading-4.5">{t('workflow.config.if-else.else_desc')}</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { type FC } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Button, Form, Space } from 'antd';
|
||||
import { DeleteOutlined } from '@ant-design/icons';
|
||||
import { Graph, Node } from '@antv/x6';
|
||||
|
||||
import Editor from '../../Editor';
|
||||
import type { Suggestion } from '../../Editor/plugin/AutocompletePlugin'
|
||||
|
||||
@@ -151,17 +151,15 @@ const CategoryList: FC<CategoryListProps> = ({ parentName, selectedNode, graphRe
|
||||
const contentLength = (currentItem.class_name || '').length;
|
||||
|
||||
return (
|
||||
<div key={key} className="rb:border rb:border-[#DFE4ED] rb:rounded-md rb:p-3 rb:bg-[#F8F9FB]">
|
||||
<div key={key} className="rb:border rb:border-[#DFE4ED] rb:rounded-md rb:p-2 rb:bg-[#F8F9FB]">
|
||||
<div className="rb:flex rb:items-center rb:justify-between rb:mb-2">
|
||||
<div>{t('workflow.config.question-classifier.class_name')} {index + 1}</div>
|
||||
<div className="rb:text-[12px] rb:font-medium rb:py-1 rb:leading-2">{t('workflow.config.question-classifier.class_name')} {index + 1}</div>
|
||||
<div className="rb:flex rb:items-center rb:gap-1">
|
||||
<span className="rb:text-xs rb:text-gray-500">{contentLength}</span>
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
icon={<DeleteOutlined />}
|
||||
<div
|
||||
className="rb:ml-1 rb:size-4 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/workflow/deleteBg.svg')] rb:hover:bg-[url('@/assets/images/workflow/deleteBg_hover.svg')]"
|
||||
onClick={() => handleRemoveCategory(remove, name, index)}
|
||||
/>
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
<Form.Item
|
||||
@@ -172,6 +170,7 @@ const CategoryList: FC<CategoryListProps> = ({ parentName, selectedNode, graphRe
|
||||
<Editor
|
||||
placeholder={t('common.pleaseEnter')}
|
||||
options={options}
|
||||
size="small"
|
||||
/>
|
||||
</Form.Item>
|
||||
</div>
|
||||
@@ -179,8 +178,10 @@ const CategoryList: FC<CategoryListProps> = ({ parentName, selectedNode, graphRe
|
||||
|
||||
<Button
|
||||
type="dashed"
|
||||
size="middle"
|
||||
block
|
||||
onClick={() => handleAddCategory(add)}
|
||||
className="rb:w-full"
|
||||
className="rb:text-[12px]!"
|
||||
>
|
||||
+ {t('workflow.config.question-classifier.addClassName')}
|
||||
</Button>
|
||||
|
||||
@@ -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<CaseListProps> = ({
|
||||
return (
|
||||
<>
|
||||
<Form.List name={[parentName, 'expressions']}>
|
||||
{(fields, { add, remove }) => (
|
||||
<div>
|
||||
{(fields, { add, remove }) => {
|
||||
const logicalOperator = form.getFieldValue([parentName, 'logical_operator']);
|
||||
return (
|
||||
<div className="rb:relative">
|
||||
{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 (
|
||||
<div key={field.key} className="rb:mb-3">
|
||||
{index > 0 && (<>
|
||||
<div className="rb:absolute rb:w-3 rb:left-2 rb:top-3.75 rb:bottom-3.75 rb:z-10 rb:border rb:border-[#DFE4ED] rb:rounded-l-md rb:border-r-0"></div>
|
||||
<div className="rb:absolute rb:z-10 rb:left-0 rb:top-[50%] rb:transform-[translateY(-50%)]]">
|
||||
<Form.Item name={[parentName, 'logical_operator']} noStyle >
|
||||
<Button size="small" className="rb:cursor-pointer" onClick={handleChangeLogicalOperator}>{logicalOperator}</Button>
|
||||
</Form.Item>
|
||||
</div>
|
||||
</>)}
|
||||
|
||||
<div className="rb:border rb:border-[#DFE4ED] rb:rounded-md rb:p-3 rb:bg-white rb:ml-6">
|
||||
<Row gutter={8} align="middle">
|
||||
<Col span={14}>
|
||||
<div className="rb:flex rb:items-center rb:justify-between rb:mb-2">
|
||||
<div className="rb:text-[12px] rb:font-medium rb:leading-4.5">
|
||||
{t('workflow.config.loop.condition')}
|
||||
</div>
|
||||
|
||||
<Button
|
||||
onClick={() => add({})}
|
||||
className="rb:py-0! rb:px-1! rb:text-[12px]!"
|
||||
size="small"
|
||||
>
|
||||
+ {t('workflow.config.loop.addCondition')}
|
||||
</Button>
|
||||
</div>
|
||||
{fields?.length > 1 && <div className="rb:absolute rb:top-8 rb:bottom-4 rb:w-8.5 rb:h-[calc(100%-32px)]">
|
||||
<div className="rb:absolute rb:w-2.5 rb:h-[calc(50%-30px)] rb:left-5 rb:top-4 rb:z-10 rb:border-l rb:border-t rb:border-[#DFE4ED] rb:rounded-tl-[10px] rb:border-r-0"></div>
|
||||
<div className="rb:absolute rb:z-10 rb:left-0 rb:top-[calc(50%-13px)]">
|
||||
<Form.Item name={[parentName, 'logical_operator']} noStyle >
|
||||
<Button size="small" className="rb:text-[12px]! rb:py-px! rb:px-1! rb:w-8.5! rb:h-5!" onClick={handleChangeLogicalOperator}>{logicalOperator}</Button>
|
||||
</Form.Item>
|
||||
</div>
|
||||
<div className="rb:absolute rb:w-2.5 rb:h-[calc(50%-30px)] rb:left-5 rb:bottom-4 rb:z-10 rb:border-l rb:border-b rb:border-[#DFE4ED] rb:rounded-bl-[10px] rb:border-r-0"></div>
|
||||
</div>}
|
||||
{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 (
|
||||
<div key={field.key} className="rb:flex rb:items-start rb:ml-9.5 rb:mb-4">
|
||||
<div className="rb:flex-1 rb:bg-[#F6F8FC] rb:border rb:border-[#DFE4ED] rb:rounded-md">
|
||||
<div className={clsx("rb:flex rb:gap-1 rb:p-1", {
|
||||
'rb:border-b rb:border-b-[#DFE4ED]': !hideRightField
|
||||
})}>
|
||||
<Form.Item name={[field.name, 'left']} noStyle>
|
||||
<VariableSelect
|
||||
options={options.filter(vo =>
|
||||
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.Item>
|
||||
</Col>
|
||||
|
||||
<Col span={8}>
|
||||
<Form.Item name={[field.name, 'operator']} noStyle>
|
||||
<Select
|
||||
options={operatorList.map(vo => ({
|
||||
@@ -138,84 +149,67 @@ const ConditionList: FC<CaseListProps> = ({
|
||||
}))}
|
||||
size="small"
|
||||
popupMatchSelectWidth={false}
|
||||
placeholder={t('common.pleaseSelect')}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={2}>
|
||||
<DeleteOutlined
|
||||
className="rb:text-gray-400 rb:cursor-pointer rb:hover:text-red-500"
|
||||
onClick={() => remove(field.name)}
|
||||
/>
|
||||
</Col>
|
||||
</div>
|
||||
|
||||
{!hideRightField && <>
|
||||
{!hideRightField && <div className="rb:p-1">
|
||||
{leftFieldType === 'number'
|
||||
? <Col span={24}><Row>
|
||||
<Col span={12}>
|
||||
<Form.Item name={[field.name, 'input_type']} noStyle>
|
||||
<Select
|
||||
? <div className="rb:flex rb:items-center">
|
||||
<Form.Item name={[field.name, 'input_type']} noStyle>
|
||||
<Select
|
||||
placeholder={t('common.pleaseSelect')}
|
||||
options={[{ value: 'Variable', label: 'Variable' }, { value: 'Constant', label: 'Constant' }]}
|
||||
popupMatchSelectWidth={false}
|
||||
variant="borderless"
|
||||
className="rb:w-full!"
|
||||
onChange={() => handleInputTypeChange(index)}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Divider type="vertical" />
|
||||
<Form.Item name={[field.name, 'right']} noStyle>
|
||||
{inputType === 'Variable'
|
||||
?
|
||||
<VariableSelect
|
||||
placeholder={t('common.pleaseSelect')}
|
||||
options={[{ value: 'Variable', label: 'Variable' }, { value: 'Constant', label: 'Constant' }]}
|
||||
options={options.filter(vo => vo.dataType === 'number')}
|
||||
allowClear={false}
|
||||
popupMatchSelectWidth={false}
|
||||
variant="borderless"
|
||||
className="rb:w-full!"
|
||||
onChange={() => handleInputTypeChange(index)}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item name={[field.name, 'right']} noStyle>
|
||||
{inputType === 'Variable'
|
||||
?
|
||||
<VariableSelect
|
||||
placeholder={t('common.pleaseSelect')}
|
||||
options={options.filter(vo => vo.dataType === 'number')}
|
||||
allowClear={false}
|
||||
popupMatchSelectWidth={false}
|
||||
variant="borderless"
|
||||
className="rb:w-full!"
|
||||
/>
|
||||
: <InputNumber
|
||||
placeholder={t('common.pleaseEnter')}
|
||||
variant="borderless"
|
||||
className="rb:w-full!"
|
||||
onChange={(value) => form.setFieldValue([parentName, 'expressions', index, 'right'], value)}
|
||||
/>
|
||||
}
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row></Col>
|
||||
: <Col span={24}>
|
||||
<Form.Item name={[field.name, 'right']} noStyle>
|
||||
{leftFieldType === 'boolean'
|
||||
? <Radio.Group block>
|
||||
<Radio.Button value={true}>True</Radio.Button>
|
||||
<Radio.Button value={false}>False</Radio.Button>
|
||||
</Radio.Group>
|
||||
: <Input placeholder={t('common.pleaseEnter')} />
|
||||
: <InputNumber
|
||||
placeholder={t('common.pleaseEnter')}
|
||||
variant="borderless"
|
||||
className="rb:w-full!"
|
||||
onChange={(value) => form.setFieldValue([parentName, 'expressions', index, 'right'], value)}
|
||||
/>
|
||||
}
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</div>
|
||||
: <Form.Item name={[field.name, 'right']} noStyle>
|
||||
{leftFieldType === 'boolean'
|
||||
? <Radio.Group block>
|
||||
<Radio.Button value={true}>True</Radio.Button>
|
||||
<Radio.Button value={false}>False</Radio.Button>
|
||||
</Radio.Group>
|
||||
: <Input placeholder={t('common.pleaseEnter')} />
|
||||
}
|
||||
</Form.Item>
|
||||
}
|
||||
</>}
|
||||
|
||||
</Row>
|
||||
</div>}
|
||||
</div>
|
||||
<div
|
||||
className="rb:ml-1 rb:size-4 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/workflow/deleteBg.svg')] rb:hover:bg-[url('@/assets/images/workflow/deleteBg_hover.svg')]"
|
||||
onClick={() => remove(field.name)}
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
|
||||
<Button
|
||||
type="dashed"
|
||||
onClick={() => add({ left: '', operator: '', right: '' })}
|
||||
className="rb:w-full rb:ml-6 rb:mt-2"
|
||||
icon={<span>+</span>}
|
||||
>
|
||||
添加条件
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
)
|
||||
}}
|
||||
</Form.List>
|
||||
</>
|
||||
)
|
||||
|
||||
@@ -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<CycleVarsListProps> = ({
|
||||
options,
|
||||
parentName,
|
||||
selectedNode,
|
||||
graphRef
|
||||
graphRef,
|
||||
size = 'middle'
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const form = Form.useFormInstance();
|
||||
@@ -78,62 +79,56 @@ const CycleVarsList: FC<CycleVarsListProps> = ({
|
||||
const availableOptions = getChildNodeVariables();
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
||||
<Form.List name={parentName}>
|
||||
{(fields, { add, remove }) => (
|
||||
<>
|
||||
<div className="rb:flex rb:items-center rb:justify-between rb:mb-3">
|
||||
<span className="rb:text-sm rb:font-medium">循环变量</span>
|
||||
<PlusOutlined className="rb:text-gray-400 rb:cursor-pointer rb:hover:text-blue-500" onClick={() => add({ name: '', type: 'string', input_type: 'constant', value: '' })} />
|
||||
</div>
|
||||
{fields.map(({ key, name, ...field }, index) => {
|
||||
const currentInputType = value?.[index]?.input_type;
|
||||
|
||||
return (
|
||||
<div key={key} className="rb:mb-3 rb:border rb:border-[#DFE4ED] rb:rounded-md rb:p-3 rb:bg-white">
|
||||
<Row gutter={8} align="middle" className="rb:mb-2">
|
||||
<Col span={8}>
|
||||
<Form.Item name={[name, 'name']} noStyle>
|
||||
<Input size="small" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={6}>
|
||||
<Form.Item name={[name, 'type']} noStyle>
|
||||
<Select
|
||||
options={types.map(key => ({
|
||||
value: key,
|
||||
label: t(`workflow.config.parameter-extractor.${key}`),
|
||||
}))}
|
||||
size="small"
|
||||
popupMatchSelectWidth={false}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Form.Item name={[name, 'input_type']} noStyle>
|
||||
<Select
|
||||
placeholder="Constant"
|
||||
options={[
|
||||
{ label: 'Constant', value: 'constant' },
|
||||
{ label: 'Variable', value: 'variable' }
|
||||
]}
|
||||
size="small"
|
||||
popupMatchSelectWidth={false}
|
||||
onChange={() => {
|
||||
// 重置 value 字段
|
||||
form.setFieldValue([parentName, index, 'value'], undefined);
|
||||
}}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={2}>
|
||||
<DeleteOutlined
|
||||
className="rb:text-gray-400 rb:cursor-pointer rb:hover:text-red-500"
|
||||
onClick={() => remove(name)}
|
||||
<Form.List name={parentName}>
|
||||
{(fields, { add, remove }) => (
|
||||
<>
|
||||
<div className="rb:flex rb:items-center rb:justify-between rb:mb-3">
|
||||
<span className="rb:text-[12px] rb:font-medium">{t('workflow.config.loop.cycle_vars')}</span>
|
||||
<Button
|
||||
onClick={() => add({ name: '', type: 'string', input_type: 'constant', value: '' })}
|
||||
className="rb:py-0! rb:px-1! rb:text-[12px]!"
|
||||
size="small"
|
||||
>
|
||||
+ {t('workflow.config.addVariable')}
|
||||
</Button>
|
||||
</div>
|
||||
{fields.map(({ key, name }, index) => {
|
||||
const currentInputType = value?.[index]?.input_type;
|
||||
|
||||
return (
|
||||
<div key={key} className="rb:flex rb:items-start rb:mb-2">
|
||||
<div className="rb:flex-1 rb:bg-[#F6F8FC] rb:border rb:border-[#DFE4ED] rb:rounded-md">
|
||||
<div className="rb:flex rb:gap-1 rb:p-1 rb:border-b rb:border-b-[#DFE4ED]">
|
||||
<Form.Item name={[name, 'name']} noStyle>
|
||||
<Input size={size} className="rb:w-23!" placeholder={t('common.pleaseEnter')} />
|
||||
</Form.Item>
|
||||
<Form.Item name={[name, 'type']} noStyle>
|
||||
<Select
|
||||
options={types.map(key => ({
|
||||
value: key,
|
||||
label: t(`workflow.config.parameter-extractor.${key}`),
|
||||
}))}
|
||||
size={size}
|
||||
popupMatchSelectWidth={false}
|
||||
className="rb:w-18.5!"
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form.Item>
|
||||
<Form.Item name={[name, 'input_type']} noStyle>
|
||||
<Select
|
||||
placeholder="Constant"
|
||||
options={[
|
||||
{ label: 'Constant', value: 'constant' },
|
||||
{ label: 'Variable', value: 'variable' }
|
||||
]}
|
||||
size={size}
|
||||
popupMatchSelectWidth={false}
|
||||
onChange={() => {
|
||||
form.setFieldValue([parentName, index, 'value'], undefined);
|
||||
}}
|
||||
className="rb:w-18!"
|
||||
/>
|
||||
</Form.Item>
|
||||
</div>
|
||||
|
||||
<Form.Item name={[name, 'value']} noStyle>
|
||||
{currentInputType === 'variable' ? (
|
||||
@@ -145,22 +140,29 @@ const CycleVarsList: FC<CycleVarsListProps> = ({
|
||||
|
||||
return option.dataType === currentType
|
||||
})}
|
||||
variant="borderless"
|
||||
size="small"
|
||||
/>
|
||||
) : (
|
||||
<Input.TextArea
|
||||
placeholder={t('common.pleaseEnter')}
|
||||
rows={3}
|
||||
className="rb:w-full"
|
||||
variant="borderless"
|
||||
/>
|
||||
)}
|
||||
</Form.Item>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</>
|
||||
)}
|
||||
</Form.List>
|
||||
</div>
|
||||
<div
|
||||
className="rb:ml-1 rb:size-4 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/workflow/deleteBg.svg')] rb:hover:bg-[url('@/assets/images/workflow/deleteBg_hover.svg')]"
|
||||
onClick={() => remove(name)}
|
||||
></div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</>
|
||||
)}
|
||||
</Form.List>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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<GroupVariableListProps> = ({
|
||||
name,
|
||||
options = [],
|
||||
isCanAdd = false
|
||||
isCanAdd = false,
|
||||
size = "middle"
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const form = Form.useFormInstance();
|
||||
@@ -54,6 +56,7 @@ const GroupVariableList: FC<GroupVariableListProps> = ({
|
||||
placeholder={t('common.pleaseSelect')}
|
||||
options={filteredOptions}
|
||||
mode="multiple"
|
||||
size={size}
|
||||
/>
|
||||
</Form.Item>
|
||||
</div>
|
||||
@@ -76,11 +79,15 @@ const GroupVariableList: FC<GroupVariableListProps> = ({
|
||||
]}
|
||||
noStyle
|
||||
>
|
||||
{isCanAdd ? <Input placeholder={t('common.pleaseEnter')} /> : t('workflow.config.var-aggregator.variable')}
|
||||
{isCanAdd ? <Input placeholder={t('common.pleaseEnter')} size={size} /> : t('workflow.config.var-aggregator.variable')}
|
||||
</Form.Item>
|
||||
</Col>
|
||||
|
||||
{isCanAdd && <Col span={12} className="rb:flex! rb:items-center rb:justify-end">
|
||||
<MinusCircleOutlined onClick={() => remove(name)} />
|
||||
<div
|
||||
className="rb:ml-1 rb:size-4 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/workflow/deleteBg.svg')] rb:hover:bg-[url('@/assets/images/workflow/deleteBg_hover.svg')]"
|
||||
onClick={() => remove(name)}
|
||||
></div>
|
||||
</Col>}
|
||||
</Row>
|
||||
|
||||
@@ -104,16 +111,22 @@ const GroupVariableList: FC<GroupVariableListProps> = ({
|
||||
})()
|
||||
}
|
||||
mode="multiple"
|
||||
size={size}
|
||||
/>
|
||||
</Form.Item>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
{isCanAdd && <Form.Item noStyle>
|
||||
<Button type="dashed" onClick={() => add({ key: `Group${fields.length + 1}` })} block>
|
||||
+ {t('workflow.config.var-aggregator.addGroup')}
|
||||
</Button>
|
||||
</Form.Item>}
|
||||
|
||||
{isCanAdd && <Button
|
||||
type="dashed"
|
||||
block
|
||||
size="middle"
|
||||
className="rb:text-[12px]!"
|
||||
onClick={() => add({ key: `Group${fields.length + 1}` })}
|
||||
>
|
||||
+ {t('workflow.config.var-aggregator.addGroup')}
|
||||
</Button>}
|
||||
</>
|
||||
)}
|
||||
</Form.List>
|
||||
|
||||
@@ -93,6 +93,7 @@ const AuthConfigModal = forwardRef<AuthConfigModalRef, AuthConfigModalProps>(({
|
||||
initialValues={{
|
||||
auth: 'none'
|
||||
}}
|
||||
size="middle"
|
||||
>
|
||||
<FormItem
|
||||
name="auth"
|
||||
@@ -102,6 +103,7 @@ const AuthConfigModal = forwardRef<AuthConfigModalRef, AuthConfigModalProps>(({
|
||||
]}
|
||||
>
|
||||
<Select
|
||||
size="middle"
|
||||
options={[
|
||||
{ value: 'none', label: t('workflow.config.http-request.none') },
|
||||
{ value: 'api_key', label: t('workflow.config.http-request.apiKey') },
|
||||
@@ -117,6 +119,7 @@ const AuthConfigModal = forwardRef<AuthConfigModalRef, AuthConfigModalProps>(({
|
||||
]}
|
||||
>
|
||||
<Select
|
||||
size="middle"
|
||||
options={[
|
||||
{ value: 'basic', label: t('workflow.config.http-request.basic') },
|
||||
{ value: 'bearer', label: t('workflow.config.http-request.bearer') },
|
||||
@@ -132,7 +135,7 @@ const AuthConfigModal = forwardRef<AuthConfigModalRef, AuthConfigModalProps>(({
|
||||
{ required: true, message: t('common.pleaseEnter') }
|
||||
]}
|
||||
>
|
||||
<Input placeholder={t('common.pleaseEnter')} />
|
||||
<Input size="middle" placeholder={t('common.pleaseEnter')} />
|
||||
</FormItem>
|
||||
}
|
||||
<FormItem
|
||||
@@ -142,7 +145,7 @@ const AuthConfigModal = forwardRef<AuthConfigModalRef, AuthConfigModalProps>(({
|
||||
{ required: true, message: t('common.pleaseEnter') }
|
||||
]}
|
||||
>
|
||||
<Input placeholder={t('common.pleaseEnter')} />
|
||||
<Input size="middle" placeholder={t('common.pleaseEnter')} />
|
||||
</FormItem>
|
||||
</>}
|
||||
</Form>
|
||||
|
||||
@@ -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<EditableTableProps> = ({
|
||||
@@ -26,7 +26,8 @@ const EditableTable: React.FC<EditableTableProps> = ({
|
||||
title,
|
||||
options = [],
|
||||
typeOptions = [],
|
||||
filterBooleanType = false
|
||||
filterBooleanType = false,
|
||||
size = 'small'
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -38,21 +39,24 @@ const EditableTable: React.FC<EditableTableProps> = ({
|
||||
|
||||
const getColumns = (remove: (index: number) => void): TableProps<TableRow>['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) => (
|
||||
<Form.Item name={[index, 'name']} noStyle>
|
||||
<VariableSelect
|
||||
placeholder={t('common.pleaseSelect')}
|
||||
size="small"
|
||||
// size="small"
|
||||
options={options}
|
||||
filterBooleanType={filterBooleanType}
|
||||
popupMatchSelectWidth={false}
|
||||
className={contentClassName}
|
||||
size={size}
|
||||
/>
|
||||
</Form.Item>
|
||||
)
|
||||
@@ -61,18 +65,20 @@ const EditableTable: React.FC<EditableTableProps> = ({
|
||||
title: t('workflow.config.type'),
|
||||
dataIndex: 'type',
|
||||
width: '20%',
|
||||
className: cellClassName,
|
||||
render: (_: any, __: TableRow, index: number) => (
|
||||
<Form.Item shouldUpdate noStyle>
|
||||
{(form) => (
|
||||
<Form.Item name={[index, 'type']} noStyle>
|
||||
<Select
|
||||
placeholder={t('common.pleaseSelect')}
|
||||
size="small"
|
||||
// size="small"
|
||||
options={typeOptions}
|
||||
popupMatchSelectWidth={false}
|
||||
onChange={() => {
|
||||
form.setFieldValue([...Array.isArray(parentName) ? parentName : [parentName], index, 'value'], undefined);
|
||||
}}
|
||||
size={size}
|
||||
/>
|
||||
</Form.Item>
|
||||
)}
|
||||
@@ -82,7 +88,7 @@ const EditableTable: React.FC<EditableTableProps> = ({
|
||||
{
|
||||
title: t('workflow.config.value'),
|
||||
dataIndex: 'value',
|
||||
width: baseWidth,
|
||||
className: cellClassName,
|
||||
render: (_: any, __: TableRow, index: number) => (
|
||||
<Form.Item
|
||||
shouldUpdate={(prevValues, currentValues) => {
|
||||
@@ -102,10 +108,12 @@ const EditableTable: React.FC<EditableTableProps> = ({
|
||||
<Form.Item name={[index, 'value']} noStyle>
|
||||
<VariableSelect
|
||||
placeholder={t('common.pleaseSelect')}
|
||||
size="small"
|
||||
// size="small"
|
||||
options={filteredOptions}
|
||||
filterBooleanType={filterBooleanType}
|
||||
popupMatchSelectWidth={false}
|
||||
className={contentClassName}
|
||||
size={size}
|
||||
/>
|
||||
</Form.Item>
|
||||
);
|
||||
@@ -116,9 +124,12 @@ const EditableTable: React.FC<EditableTableProps> = ({
|
||||
{
|
||||
title: '',
|
||||
dataIndex: 'actions',
|
||||
width: '10%',
|
||||
className: cellClassName,
|
||||
render: (_: any, __: TableRow, index: number) => (
|
||||
<Button type="text" icon={<DeleteOutlined />} onClick={() => remove(index)} />
|
||||
<div
|
||||
className="rb:ml-1 rb:size-4 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/workflow/deleteBg.svg')] rb:hover:bg-[url('@/assets/images/workflow/deleteBg_hover.svg')]"
|
||||
onClick={() => remove(index)}
|
||||
></div>
|
||||
)
|
||||
}
|
||||
];
|
||||
@@ -129,13 +140,11 @@ const EditableTable: React.FC<EditableTableProps> = ({
|
||||
<Form.List name={parentName}>
|
||||
{(fields, { add, remove }) => {
|
||||
const AddButton = ({ block = false }: { block?: boolean }) => (
|
||||
<Button
|
||||
type={block ? "dashed" : "text"}
|
||||
<Button
|
||||
icon={block ? undefined : <PlusOutlined />}
|
||||
onClick={() => add(createNewRow())}
|
||||
size="small"
|
||||
block={block}
|
||||
className={block ? "rb:mt-1" : ""}
|
||||
className={block ? "rb:mt-1 rb:text-[12px]! rb:bg-transparent!" : "rb:text-[12px]!"}
|
||||
>
|
||||
{block && `+${t('common.add')}`}
|
||||
</Button>
|
||||
@@ -145,8 +154,8 @@ const EditableTable: React.FC<EditableTableProps> = ({
|
||||
<>
|
||||
{title && (
|
||||
<div className="rb:flex rb:items-center rb:mb-2 rb:justify-between">
|
||||
<div className="rb:font-medium">{title}</div>
|
||||
<AddButton />
|
||||
<div className="rb:font-medium rb:text-[12px] rb:leading-4.5">{title}</div>
|
||||
<AddButton block={true} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -161,8 +170,9 @@ const EditableTable: React.FC<EditableTableProps> = ({
|
||||
columns={getColumns(remove)}
|
||||
pagination={false}
|
||||
size="small"
|
||||
rowClassName="rb:p-0! rb:bg-[#F6F8FC]!"
|
||||
locale={{ emptyText: <Empty size={88} /> }}
|
||||
scroll={{ x: 'max-content' }}
|
||||
style={{ width: '274px' }}
|
||||
/>
|
||||
|
||||
{!title && <AddButton block />}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { type FC, useRef } from "react";
|
||||
import { type FC, useRef, useState } from "react";
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Form, Row, Col, Select, Button, Divider, InputNumber, Switch, Input } from 'antd'
|
||||
import { CaretDownOutlined, CaretRightOutlined, SettingOutlined } from '@ant-design/icons';
|
||||
import Editor from '../../Editor'
|
||||
import type { Suggestion } from '../../Editor/plugin/AutocompletePlugin'
|
||||
import AuthConfigModal from './AuthConfigModal'
|
||||
@@ -65,15 +66,23 @@ const HttpRequest: FC<{ options: Suggestion[]; selectedNode?: any; graphRef?: an
|
||||
}
|
||||
}
|
||||
|
||||
console.log('HttpRequest', values)
|
||||
const [collapsed, setCollapsed] = useState(true)
|
||||
const handleToggle = () => {
|
||||
setCollapsed((prev: boolean) => !prev)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="rb:flex rb:items-center rb:justify-between rb:mb-4">
|
||||
<div>API</div>
|
||||
<Button onClick={handleChangeAuth}>{t('workflow.config.http-request.auth')}</Button>
|
||||
<div className="rb:flex rb:items-center rb:justify-between rb:mb-1">
|
||||
<div className="rb:font-medium rb:text-[12px] rb:leading-4.5">API</div>
|
||||
<Button onClick={handleChangeAuth}
|
||||
size="small"
|
||||
type="text"
|
||||
icon={<SettingOutlined />}
|
||||
className="rb:mt-1 rb:text-[12px]!"
|
||||
>{t('workflow.config.http-request.auth')}: {!values?.auth?.auth_type || values?.auth?.auth_type === 'none' ? t('workflow.config.http-request.none') : t('workflow.config.http-request.apiKey')}</Button>
|
||||
</div>
|
||||
<Row gutter={16}>
|
||||
<Row gutter={4}>
|
||||
<Col span={8}>
|
||||
<Form.Item name="method">
|
||||
<Select
|
||||
@@ -85,35 +94,43 @@ const HttpRequest: FC<{ options: Suggestion[]; selectedNode?: any; graphRef?: an
|
||||
{ label: 'PUT', value: 'PUT' },
|
||||
{ label: 'DELETE', value: 'DELETE' },
|
||||
]}
|
||||
className="rb:bg-transparent!"
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={16}>
|
||||
<Form.Item name="url">
|
||||
<Editor options={options.filter(vo => vo.dataType === 'string' || vo.dataType === 'number')} variant="outlined" />
|
||||
<Editor
|
||||
options={options.filter(vo => vo.dataType === 'string' || vo.dataType === 'number')}
|
||||
variant="outlined"
|
||||
type="input"
|
||||
size="small"
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
<Form.Item name="auth" hidden>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item name="headers">
|
||||
<Form.Item name="headers" noStyle>
|
||||
<EditableTable
|
||||
size="small"
|
||||
parentName="headers"
|
||||
title="HEADERS"
|
||||
options={options.filter(vo => vo.dataType === 'string' || vo.dataType === 'number')}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item name="params">
|
||||
<Form.Item name="params" noStyle>
|
||||
<EditableTable
|
||||
size="small"
|
||||
parentName="params"
|
||||
title="PARAMS"
|
||||
options={options.filter(vo => vo.dataType === 'string' || vo.dataType === 'number')}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label="BODY">
|
||||
<Form.Item label="BODY" className="rb:mb-0!">
|
||||
<Form.Item name={['body', 'content_type']}>
|
||||
<Select
|
||||
placeholder={t('common.pleaseSelect')}
|
||||
@@ -131,6 +148,7 @@ const HttpRequest: FC<{ options: Suggestion[]; selectedNode?: any; graphRef?: an
|
||||
{values?.body?.content_type === 'form-data' &&
|
||||
<Form.Item name={['body', 'data']} noStyle>
|
||||
<EditableTable
|
||||
size="small"
|
||||
parentName={['body', 'data']}
|
||||
options={options.filter(vo => vo.dataType === 'string' || vo.dataType === 'number')}
|
||||
typeOptions={[
|
||||
@@ -143,6 +161,7 @@ const HttpRequest: FC<{ options: Suggestion[]; selectedNode?: any; graphRef?: an
|
||||
{values?.body?.content_type === 'x-www-form-urlencoded' &&
|
||||
<Form.Item name={['body', 'data']} noStyle>
|
||||
<EditableTable
|
||||
size="small"
|
||||
parentName={['body', 'data']}
|
||||
options={options.filter(vo => vo.dataType === 'string' || vo.dataType === 'number')}
|
||||
filterBooleanType={true}
|
||||
@@ -150,7 +169,7 @@ const HttpRequest: FC<{ options: Suggestion[]; selectedNode?: any; graphRef?: an
|
||||
</Form.Item>
|
||||
}
|
||||
{values?.body?.content_type === 'json' &&
|
||||
<Form.Item name={['body', 'data']}>
|
||||
<Form.Item name={['body', 'data']} noStyle>
|
||||
<MessageEditor
|
||||
key="json"
|
||||
parentName={['body', 'data']}
|
||||
@@ -161,7 +180,7 @@ const HttpRequest: FC<{ options: Suggestion[]; selectedNode?: any; graphRef?: an
|
||||
</Form.Item>
|
||||
}
|
||||
{values?.body?.content_type === 'raw' &&
|
||||
<Form.Item name={['body', 'data']}>
|
||||
<Form.Item name={['body', 'data']} noStyle>
|
||||
<MessageEditor
|
||||
key="raw"
|
||||
parentName={['body', 'data']}
|
||||
@@ -172,7 +191,7 @@ const HttpRequest: FC<{ options: Suggestion[]; selectedNode?: any; graphRef?: an
|
||||
</Form.Item>
|
||||
}
|
||||
{values?.body?.content_type === 'binary' &&
|
||||
<Form.Item name={['body', 'data']}>
|
||||
<Form.Item name={['body', 'data']} noStyle>
|
||||
<VariableSelect
|
||||
placeholder={t('common.pleaseSelect')}
|
||||
options={options.filter(vo => vo.dataType.includes('file'))}
|
||||
@@ -182,15 +201,19 @@ const HttpRequest: FC<{ options: Suggestion[]; selectedNode?: any; graphRef?: an
|
||||
}
|
||||
</Form.Item>
|
||||
<Divider />
|
||||
<Form.Item layout="horizontal" name="verify_ssl" label={t('workflow.config.http-request.verify_ssl')}>
|
||||
<Form.Item layout="horizontal" name="verify_ssl" label={t('workflow.config.http-request.verify_ssl')} className="rb:mb-0!">
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
|
||||
<Divider />
|
||||
<div>{t('workflow.config.http-request.timeouts')}</div>
|
||||
<div className="rb:font-medium rb:text-[12px] rb:leading-4.5 rb:mb-2.5 rb:cursor-pointer" onClick={handleToggle}>
|
||||
{t('workflow.config.http-request.timeouts')}
|
||||
{collapsed ? <CaretRightOutlined /> : <CaretDownOutlined />}
|
||||
</div>
|
||||
<Form.Item
|
||||
name={['timeouts', 'connect_timeout']}
|
||||
label={t('workflow.config.http-request.connect_timeout')}
|
||||
hidden={collapsed}
|
||||
>
|
||||
<InputNumber
|
||||
placeholder={t('common.pleaseEnter')}
|
||||
@@ -201,6 +224,7 @@ const HttpRequest: FC<{ options: Suggestion[]; selectedNode?: any; graphRef?: an
|
||||
<Form.Item
|
||||
name={['timeouts', 'read_timeout']}
|
||||
label={t('workflow.config.http-request.read_timeout')}
|
||||
hidden={collapsed}
|
||||
>
|
||||
<InputNumber
|
||||
placeholder={t('common.pleaseEnter')}
|
||||
@@ -211,6 +235,7 @@ const HttpRequest: FC<{ options: Suggestion[]; selectedNode?: any; graphRef?: an
|
||||
<Form.Item
|
||||
name={['timeouts', 'write_timeout']}
|
||||
label={t('workflow.config.http-request.write_timeout')}
|
||||
hidden={collapsed}
|
||||
>
|
||||
<InputNumber
|
||||
placeholder={t('common.pleaseEnter')}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { type FC, useRef, useState, useEffect } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Space, Button, List } from 'antd'
|
||||
import { Space, Button } from 'antd'
|
||||
import knowledgeEmpty from '@/assets/images/application/knowledgeEmpty.svg'
|
||||
import type {
|
||||
KnowledgeConfigForm,
|
||||
@@ -113,49 +113,65 @@ const Knowledge: FC<{value?: KnowledgeConfig; onChange?: (config: KnowledgeConfi
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<div className="rb:flex rb:justify-between rb:items-center">
|
||||
<div>{t('application.knowledgeBaseAssociation')}</div>
|
||||
<div className="rb:flex rb:items-center rb:justify-between rb:mb-2">
|
||||
<div className="rb:text-[12px] rb:font-medium rb:leading-4.5">
|
||||
{t('application.knowledgeBaseAssociation')}
|
||||
</div>
|
||||
|
||||
<Space>
|
||||
<Button style={{ padding: '0 8px', height: '24px' }} onClick={handleKnowledgeConfig}>{t('workflow.config.knowledge-retrieval.recallConfig')}</Button>
|
||||
<Button style={{ padding: '0 8px', height: '24px' }} onClick={handleAddKnowledge}>+</Button>
|
||||
</Space>
|
||||
<Button
|
||||
onClick={handleKnowledgeConfig}
|
||||
className="rb:py-0! rb:px-1! rb:text-[12px]! rb:group rb:gap-0.5!"
|
||||
size="small"
|
||||
>
|
||||
<div
|
||||
className="rb:size-3.5 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/workflow/recall.svg')] rb:group-hover:bg-[url('@/assets/images/workflow/recall_hover.svg')]"
|
||||
></div>
|
||||
{t('workflow.config.knowledge-retrieval.recallConfig')}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{knowledgeList.length === 0
|
||||
? <Empty url={knowledgeEmpty} size={88} subTitle={t('application.knowledgeEmpty')} />
|
||||
:
|
||||
<List
|
||||
grid={{ gutter: 12, column: 1 }}
|
||||
dataSource={knowledgeList}
|
||||
renderItem={(item) => {
|
||||
if (!item.id) return null
|
||||
return (
|
||||
<List.Item>
|
||||
<div key={item.id} className="rb:flex rb:items-center rb:justify-between rb:p-[12px_16px] rb:bg-[#FBFDFF] rb:border rb:border-[#DFE4ED] rb:rounded-lg">
|
||||
<div className="rb:font-medium rb:leading-4">
|
||||
{item.name}
|
||||
<Tag color={item.status === 1 ? 'success' : item.status === 0 ? 'default' : 'error'} className="rb:ml-2">
|
||||
{item.status === 1 ? t('common.enable') : item.status === 0 ? t('common.disabled') : t('common.deleted')}
|
||||
</Tag>
|
||||
<div className="rb:mt-1 rb:text-[12px] rb:text-[#5B6167] rb:font-regular rb:leading-5">{t('application.contains', {include_count: item.doc_num})}</div>
|
||||
</div>
|
||||
<Space size={12}>
|
||||
<div
|
||||
className="rb:w-6 rb:h-6 rb:cursor-pointer rb:bg-[url('@/assets/images/editBorder.svg')] rb:hover:bg-[url('@/assets/images/editBg.svg')]"
|
||||
onClick={() => handleEditKnowledge(item)}
|
||||
></div>
|
||||
<div
|
||||
className="rb:w-6 rb:h-6 rb:cursor-pointer rb:bg-[url('@/assets/images/deleteBorder.svg')] rb:hover:bg-[url('@/assets/images/deleteBg.svg')]"
|
||||
onClick={() => handleDeleteKnowledge(item.id)}
|
||||
></div>
|
||||
</Space>
|
||||
</div>
|
||||
</List.Item>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
}
|
||||
<Space size={10} direction="vertical" className="rb:w-full!">
|
||||
<Button
|
||||
type="dashed"
|
||||
block
|
||||
size="middle"
|
||||
className="rb:text-[12px]!"
|
||||
onClick={handleAddKnowledge}
|
||||
>
|
||||
+ {t('workflow.config.knowledge-retrieval.addKnowledge')}
|
||||
</Button>
|
||||
|
||||
{knowledgeList.length === 0
|
||||
? <Empty url={knowledgeEmpty} size={88} subTitle={t('application.knowledgeEmpty')} />
|
||||
: knowledgeList.map(item => {
|
||||
if (!item.id) return null
|
||||
return (
|
||||
<div key={item.id} className="rb:text-[12px] rb:flex rb:items-center rb:justify-between rb:py-2 rb:px-2.5 rb:bg-[#F6F8FC] rb:border rb:border-[#DFE4ED] rb:rounded-lg">
|
||||
<div className="">
|
||||
<span className="rb:font-medium rb:leading-4">{item.name}</span>
|
||||
<Tag
|
||||
color={item.status === 1 ? 'success' : item.status === 0 ? 'default' : 'error'}
|
||||
className="rb:ml-1 rb:py-0! rb:px-1! rb:text-[12px] rb:leading-3.5!"
|
||||
>
|
||||
{item.status === 1 ? t('common.enable') : item.status === 0 ? t('common.disabled') : t('common.deleted')}
|
||||
</Tag>
|
||||
<div className="rb:mt-1 rb:text-[12px] rb:text-[#5B6167] rb:font-regular rb:leading-5">{t('application.contains', { include_count: item.doc_num })}</div>
|
||||
</div>
|
||||
<Space size={12}>
|
||||
<div
|
||||
className="rb:size-4 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/edit.svg')] rb:hover:bg-[url('@/assets/images/edit_hover.svg')]"
|
||||
onClick={() => handleEditKnowledge(item)}
|
||||
></div>
|
||||
<div
|
||||
className="rb:size-4 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/delete.svg')] rb:hover:bg-[url('@/assets/images/delete_hover.svg')]"
|
||||
onClick={() => handleDeleteKnowledge(item.id)}
|
||||
></div>
|
||||
</Space>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
}
|
||||
</Space>
|
||||
{/* 全局设置 */}
|
||||
<KnowledgeGlobalConfigModal
|
||||
data={editConfig}
|
||||
|
||||
@@ -83,6 +83,7 @@ const KnowledgeConfigModal = forwardRef<KnowledgeConfigModalRef, KnowledgeConfig
|
||||
<Form
|
||||
form={form}
|
||||
layout="vertical"
|
||||
size="middle"
|
||||
>
|
||||
{data && (
|
||||
<div className="rb:mb-6 rb:flex rb:items-center rb:justify-between rb:border rb:rounded-lg rb:p-[17px_16px] rb:cursor-pointer rb:bg-[#F0F3F8] rb:border-[#DFE4ED] rb:text-[#212332]">
|
||||
|
||||
@@ -70,6 +70,7 @@ const KnowledgeGlobalConfigModal = forwardRef<KnowledgeGlobalConfigModalRef, Kno
|
||||
<Form
|
||||
form={form}
|
||||
layout="vertical"
|
||||
size="middle"
|
||||
>
|
||||
<div className="rb:text-[#5B6167] rb:mb-6">{t('application.globalConfigDesc')}</div>
|
||||
|
||||
|
||||
@@ -117,6 +117,7 @@ const KnowledgeListModal = forwardRef<KnowledgeModalRef, KnowledgeModalProps>(({
|
||||
placeholder={t('knowledgeBase.searchPlaceholder')}
|
||||
onSearch={handleSearch}
|
||||
style={{ width: '100%' }}
|
||||
size="middle"
|
||||
/>
|
||||
{filterList.length === 0
|
||||
? <Empty />
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { MinusCircleOutlined } from '@ant-design/icons';
|
||||
import { Button, Form, Input, Space, Row, Col } from 'antd';
|
||||
import { Button, Form, Input, Divider } from 'antd';
|
||||
import type { Suggestion } from '../../Editor/plugin/AutocompletePlugin'
|
||||
import VariableSelect from '../VariableSelect'
|
||||
|
||||
@@ -16,43 +15,55 @@ const MappingList: React.FC<MappingListProps> = ({ name, options }) => {
|
||||
<Form.List name={name}>
|
||||
{(fields, { add, remove }) => (
|
||||
<>
|
||||
{fields.map(({ key, name, ...restField }) => (
|
||||
<Row key={key} gutter={12} className="rb:mb-2">
|
||||
<Col span={10}>
|
||||
<Form.Item
|
||||
{...restField}
|
||||
name={[name, 'name']}
|
||||
noStyle
|
||||
>
|
||||
<Input placeholder={t('common.pleaseEnter')} data-field-type="mapping-name" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
{...restField}
|
||||
name={[name, 'value']}
|
||||
noStyle
|
||||
>
|
||||
<VariableSelect
|
||||
placeholder={t('common.pleaseSelect')}
|
||||
options={options}
|
||||
popupMatchSelectWidth={false}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={2}>
|
||||
<MinusCircleOutlined onClick={() => remove(name)} />
|
||||
</Col>
|
||||
</Row>
|
||||
))}
|
||||
<Form.Item>
|
||||
<Button type="dashed" onClick={() => add()} block>
|
||||
+ {t('common.add')}
|
||||
<div className="rb:flex rb:items-center rb:justify-between rb:mb-2">
|
||||
<div className="rb:text-[12px] rb:font-medium rb:leading-4.5">
|
||||
{t('workflow.config.jinja-render.mapping')}
|
||||
</div>
|
||||
|
||||
<Button
|
||||
onClick={() => add()}
|
||||
className="rb:py-0! rb:px-1! rb:text-[12px]!"
|
||||
size="small"
|
||||
>
|
||||
+ {t('workflow.config.addVariable')}
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</div>
|
||||
{fields.map(({ key, name, ...restField }) => (
|
||||
<div key={key} className="rb:flex rb:items-center rb:gap-1 rb:mb-2">
|
||||
<Form.Item
|
||||
{...restField}
|
||||
name={[name, 'name']}
|
||||
noStyle
|
||||
>
|
||||
<Input
|
||||
placeholder={t('common.pleaseEnter')}
|
||||
size="small"
|
||||
className="rb:w-24!"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
{...restField}
|
||||
name={[name, 'value']}
|
||||
noStyle
|
||||
>
|
||||
<VariableSelect
|
||||
placeholder={t('common.pleaseSelect')}
|
||||
options={options}
|
||||
popupMatchSelectWidth={false}
|
||||
size="small"
|
||||
className="rb:w-39!"
|
||||
/>
|
||||
</Form.Item>
|
||||
<div
|
||||
className="rb:ml-1 rb:size-4 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/workflow/deleteBg.svg')] rb:hover:bg-[url('@/assets/images/workflow/deleteBg_hover.svg')]"
|
||||
onClick={() => remove(name)}
|
||||
></div>
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</Form.List>
|
||||
<Divider />
|
||||
</>
|
||||
)
|
||||
};
|
||||
|
||||
@@ -30,7 +30,7 @@ const MemoryConfig: FC<{ options: Suggestion[]; parentName: string; }> = ({
|
||||
return (
|
||||
<>
|
||||
{values?.memory?.enable && <>
|
||||
<div className="rb:flex rb:items-center rb:justify-between rb:py-1.5 rb:px-2 rb:bg-[#F6F8FC] rb:rounded-md rb:mb-2">
|
||||
<div className="rb:flex rb:items-center rb:justify-between rb:py-1.5 rb:px-2 rb:text-[12px] rb:bg-[#F6F8FC] rb:rounded-md rb:mb-2">
|
||||
{t('workflow.config.llm.memory')}
|
||||
<span>{t('workflow.config.llm.inner')}</span>
|
||||
</div>
|
||||
@@ -40,6 +40,7 @@ const MemoryConfig: FC<{ options: Suggestion[]; parentName: string; }> = ({
|
||||
isArray={false}
|
||||
parentName={[parentName, 'messages']}
|
||||
options={options}
|
||||
size="small"
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
|
||||
@@ -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<MessageEditor> = ({
|
||||
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<MessageEditor> = ({
|
||||
|
||||
if (!isArray) {
|
||||
return (
|
||||
<Space size={12} direction="vertical" className="rb:w-full rb:border rb:border-[#DFE4ED] rb:rounded-md rb:px-2 rb:py-1.5 rb:bg-white" data-editor-type={parentName === 'template' ? 'template' : undefined}>
|
||||
<Space size={8} direction="vertical" className="rb:w-full rb:border rb:border-[#DFE4ED] rb:rounded-md rb:px-2 rb:py-1.5" data-editor-type={parentName === 'template' ? 'template' : undefined}>
|
||||
<Row>
|
||||
<Col span={12}>
|
||||
{title ?? t('workflow.answerDesc')}
|
||||
<div className={clsx("rb:text-[12px] rb:font-medium rb:py-1 rb:leading-2", {
|
||||
'rb:bg-[#F6F8FC] rb:border rb:border-[#DFE4ED] rb:rounded-sm rb:px-2': titleVariant === 'outlined'
|
||||
})}>{title ?? t('workflow.answerDesc')}</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<Form.Item name={parentName} noStyle>
|
||||
<Editor enableJinja2={enableJinja2} placeholder={placeholder} options={processedOptions} />
|
||||
<Editor size={size} enableJinja2={enableJinja2} placeholder={placeholder} options={processedOptions} />
|
||||
</Form.Item>
|
||||
</Space>
|
||||
);
|
||||
@@ -90,7 +96,7 @@ const MessageEditor: FC<MessageEditor> = ({
|
||||
return (
|
||||
<Form.List name={parentName}>
|
||||
{(fields, { add, remove }) => (
|
||||
<Space size={12} direction="vertical" className="rb:w-full">
|
||||
<Space size={8} direction="vertical" className="rb:w-full">
|
||||
{fields.map(({ key, name, ...restField }) => {
|
||||
const fieldValue = Array.isArray(parentName)
|
||||
? parentName.reduce((obj, key) => obj?.[key], values)
|
||||
@@ -99,16 +105,17 @@ const MessageEditor: FC<MessageEditor> = ({
|
||||
const currentRole = (fieldValue?.[name]?.role || 'USER').toUpperCase();
|
||||
|
||||
return (
|
||||
<Space key={key} size={12} direction="vertical" className="rb:w-full rb:border rb:border-[#DFE4ED] rb:rounded-md rb:px-2 rb:py-1.5 rb:bg-white">
|
||||
<Space key={key} size={12} direction="vertical" className="rb:w-full rb:border rb:border-[#DFE4ED] rb:rounded-md rb:p-2">
|
||||
<Row>
|
||||
<Col span={12}>
|
||||
<Form.Item {...restField} name={[name, 'role']} noStyle>
|
||||
{currentRole === 'SYSTEM' ? (
|
||||
<Input disabled />
|
||||
<Input disabled className="rb:font-medium!" />
|
||||
) : (
|
||||
<Select
|
||||
options={roleOptions}
|
||||
disabled={currentRole === 'SYSTEM'}
|
||||
className="rb:font-medium!"
|
||||
/>
|
||||
)}
|
||||
</Form.Item>
|
||||
@@ -116,20 +123,23 @@ const MessageEditor: FC<MessageEditor> = ({
|
||||
{currentRole !== 'SYSTEM' && (
|
||||
<Col span={12}>
|
||||
<div className="rb:h-full rb:flex rb:justify-end rb:items-center">
|
||||
<MinusCircleOutlined onClick={() => remove(name)} />
|
||||
<div
|
||||
className="rb:size-4 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/workflow/delete_cycle.svg')]"
|
||||
onClick={() => remove(name)}
|
||||
></div>
|
||||
</div>
|
||||
</Col>
|
||||
)}
|
||||
</Row>
|
||||
<Form.Item {...restField} name={[name, 'content']} noStyle>
|
||||
<Editor enableJinja2={enableJinja2} placeholder={placeholder} options={processedOptions} />
|
||||
<Editor size={size} enableJinja2={enableJinja2} placeholder={placeholder} options={processedOptions} />
|
||||
</Form.Item>
|
||||
</Space>
|
||||
);
|
||||
})}
|
||||
<Form.Item noStyle>
|
||||
<Button type="dashed" onClick={() => handleAdd(add)} block>
|
||||
+{t('workflow.addMessage')}
|
||||
<Button type="dashed" size="middle" className="rb:text-[12px]!" onClick={() => handleAdd(add)} block>
|
||||
+ {t('workflow.addMessage')}
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Space>
|
||||
|
||||
@@ -74,6 +74,7 @@ const ParamEditModal = forwardRef<ParamEditModalRef, ParamEditModalProps>(({
|
||||
<Form
|
||||
form={form}
|
||||
layout="vertical"
|
||||
size="middle"
|
||||
scrollToFirstError={{ behavior: 'instant', block: 'end', focus: true }}
|
||||
>
|
||||
<FormItem
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { type FC, useRef } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Button, Space, List } from 'antd'
|
||||
import Empty from '@/components/Empty'
|
||||
import { Button, Space } from 'antd'
|
||||
|
||||
|
||||
import type { ParamItem, ParamEditModalRef } from './types'
|
||||
import ParamEditModal from './ParamEditModal'
|
||||
@@ -41,47 +41,37 @@ const ParamsList: FC<ParamsListProps> = ({
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<div className="rb:flex rb:justify-between rb:items-center">
|
||||
<div>{label}</div>
|
||||
|
||||
<Space>
|
||||
<Button style={{ padding: '0 8px', height: '24px' }} onClick={handleAdd}>+</Button>
|
||||
</Space>
|
||||
<div className="rb:leading-4.25 rb:text-[12px] rb:font-medium rb:mb-2">
|
||||
{label}
|
||||
</div>
|
||||
|
||||
{value?.length === 0
|
||||
? <Empty size={88} />
|
||||
:
|
||||
<List
|
||||
grid={{ gutter: 12, column: 1 }}
|
||||
dataSource={value}
|
||||
renderItem={(item, index) => (
|
||||
<List.Item>
|
||||
<div key={index} className="rb:group rb:relative rb:p-[12px_16px] rb:bg-[#FBFDFF] rb:cursor-pointer rb:border rb:border-[#DFE4ED] rb:rounded-lg">
|
||||
<div className="rb:flex rb:items-center rb:justify-between">
|
||||
<div className="rb:leading-4">
|
||||
<span className="rb:font-medium">{item.name}</span>
|
||||
<span className="rb:text-[12px] rb:text-[#5B6167] rb:font-regular"> ({t(`workflow.config.parameter-extractor.${item.type}`)})</span>
|
||||
</div>
|
||||
<span className="rb:block rb:group-hover:hidden rb:text-[12px] rb:text-[#5B6167] rb:font-regular">{item.required ? t('workflow.config.parameter-extractor.required') : ''}</span>
|
||||
<Space size={10} direction="vertical" className="rb:w-full!">
|
||||
<Button type="dashed" block size="middle" className="rb:text-[12px]!" onClick={handleAdd}>+ {t('workflow.config.parameter-extractor.addParams')}</Button>
|
||||
|
||||
</div>
|
||||
<div className="rb:mt-1 rb:text-[12px] rb:text-[#5B6167] rb:font-regular rb:leading-5 rb:wrap-break-word rb:line-clamp-1">{item.desc}</div>
|
||||
<Space size={12} className="rb:hidden! rb:group-hover:flex! rb:absolute rb:right-4 rb:top-[50%] rb:transform-[translateY(-50%)] rb:bg-white">
|
||||
<div
|
||||
className="rb:size-5 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/editBorder.svg')] rb:hover:bg-[url('@/assets/images/editBg.svg')]"
|
||||
onClick={() => handleEdit(index)}
|
||||
></div>
|
||||
<div
|
||||
className="rb:size-5 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/deleteBorder.svg')] rb:hover:bg-[url('@/assets/images/deleteBg.svg')]"
|
||||
onClick={() => handleDelete(index)}
|
||||
></div>
|
||||
</Space>
|
||||
</div>
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
}
|
||||
{value?.map((item, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="rb:cursor-pointer rb:group rb:py-2 rb:pl-2.5 rb:pr-2 rb:text-[12px] rb:flex rb:items-center rb:justify-between rb:bg-[#F6F8FC] rb:border rb:border-[#DFE4ED] rb:rounded-md"
|
||||
>
|
||||
<div>
|
||||
<span className="rb:font-medium">{item.name}</span>
|
||||
<span className="rb:text-[12px] rb:text-[#5B6167] rb:font-regular"> ({t(`workflow.config.parameter-extractor.${item.type}`)}) {item.required ? t('workflow.config.parameter-extractor.required') : ''}</span>
|
||||
<div className="rb:text-[12px] rb:text-[#5B6167] rb:font-regular rb:leading-4.25 rb:mt-0.5">{item.desc}</div>
|
||||
</div>
|
||||
|
||||
<Space size={8}>
|
||||
<div
|
||||
className="rb:size-4 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/edit.svg')] rb:hover:bg-[url('@/assets/images/edit_hover.svg')]"
|
||||
onClick={() => handleEdit(index)}
|
||||
></div>
|
||||
<div
|
||||
className="rb:size-4 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/delete.svg')] rb:hover:bg-[url('@/assets/images/delete_hover.svg')]"
|
||||
onClick={() => handleDelete(index)}
|
||||
></div>
|
||||
</Space>
|
||||
</div>
|
||||
))}
|
||||
</Space>
|
||||
|
||||
<ParamEditModal
|
||||
ref={paramEditModalRef}
|
||||
|
||||
55
web/src/views/Workflow/components/Properties/RbSlider.tsx
Normal file
55
web/src/views/Workflow/components/Properties/RbSlider.tsx
Normal file
@@ -0,0 +1,55 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import type { InputNumberProps, SliderSingleProps } from 'antd';
|
||||
import { Col, InputNumber, Row, Slider } from 'antd';
|
||||
|
||||
const RbSlider: React.FC<SliderSingleProps> = ({
|
||||
value,
|
||||
onChange,
|
||||
min,
|
||||
max,
|
||||
step = 0.01,
|
||||
...props
|
||||
}) => {
|
||||
const [curValue, setCurValue] = useState<number | undefined>(0)
|
||||
useEffect(() => {
|
||||
setCurValue(value)
|
||||
}, [value])
|
||||
const handleSliderChange = (newValue: number) => {
|
||||
onChange && onChange(newValue);
|
||||
};
|
||||
|
||||
const handleInputChange: InputNumberProps['onChange'] = (newValue) => {
|
||||
onChange && onChange(newValue as number);
|
||||
};
|
||||
|
||||
return (
|
||||
<Row gutter={12}>
|
||||
<Col span={16}>
|
||||
<Slider
|
||||
{...props}
|
||||
min={min}
|
||||
max={max}
|
||||
step={step as number}
|
||||
value={curValue}
|
||||
className="rb:my-0! rb:ml-2.5!"
|
||||
classNames={{
|
||||
rail: 'rb:h-[6px]!',
|
||||
track: 'rb:h-[6px]!'
|
||||
}}
|
||||
onChange={handleSliderChange}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<InputNumber
|
||||
min={min}
|
||||
max={max}
|
||||
step={step as number}
|
||||
value={curValue}
|
||||
onChange={handleInputChange}
|
||||
className="rb:w-full!"
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
};
|
||||
export default RbSlider;
|
||||
@@ -171,6 +171,7 @@ const ToolConfig: FC<{ options: Suggestion[]; }> = ({
|
||||
label={t('workflow.config.tool.tool_id')}
|
||||
>
|
||||
<Cascader
|
||||
placeholder={t('common.pleaseSelect')}
|
||||
options={optionList}
|
||||
loadData={loadData}
|
||||
onChange={handleChange}
|
||||
@@ -187,28 +188,30 @@ const ToolConfig: FC<{ options: Suggestion[]; }> = ({
|
||||
label={parameter.name}
|
||||
extra={parameter.type === 'boolean' ? undefined : parameter.description}
|
||||
rules={[
|
||||
{ required: parameter.required, message: t('workflow.config.tool.required') }
|
||||
{ required: parameter.required, message: t('common.pleaseEnter') }
|
||||
]}
|
||||
layout={parameter.type === 'boolean' ? 'horizontal' : 'vertical'}
|
||||
className={parameter.type === 'boolean' ? 'rb:mb-0!' : ''}
|
||||
>
|
||||
{parameter.type === 'string' && parameter.enum && parameter.enum.length > 0
|
||||
? <Select options={parameter.enum.map(vo => ({ value: vo, label: vo }))} placeholder={t('common.pleaseSelect')} />
|
||||
? <Select size="small" options={parameter.enum.map(vo => ({ value: vo, label: vo }))} placeholder={t('common.pleaseSelect')} />
|
||||
: parameter.type === 'boolean'
|
||||
? <Switch />
|
||||
? <Switch size="small" />
|
||||
: parameter.type === 'integer' || parameter.type === 'number'
|
||||
? <InputNumber
|
||||
min={parameter.minimum}
|
||||
max={parameter.maximum}
|
||||
step={parameter.type === 'integer' ? 1 : 0.01}
|
||||
placeholder={t('common.pleaseEnter')}
|
||||
className="rb:w-full!"
|
||||
className="rb:w-full!"
|
||||
size="small"
|
||||
onChange={(value) => form.setFieldValue(['tool_parameters', parameter.name], value)}
|
||||
/>
|
||||
: <Editor
|
||||
height={32}
|
||||
variant="outlined"
|
||||
options={options}
|
||||
size="small"
|
||||
placeholder={t('common.pleaseEnter')}
|
||||
/>
|
||||
}
|
||||
|
||||
@@ -2,14 +2,14 @@ import { forwardRef, useImperativeHandle, useState } from 'react';
|
||||
import { Form, Input, Select, InputNumber, Checkbox, Tag } from 'antd';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import type { StartVariableItem, VariableEditModalRef } from '../../types'
|
||||
import type { Variable, VariableEditModalRef } from './types'
|
||||
import RbModal from '@/components/RbModal'
|
||||
import SortableList from '@/components/SortableList'
|
||||
|
||||
const FormItem = Form.Item;
|
||||
|
||||
interface VariableEditModalProps {
|
||||
refresh: (values: StartVariableItem) => void;
|
||||
refresh: (values: Variable) => void;
|
||||
}
|
||||
|
||||
const types = [
|
||||
@@ -36,9 +36,9 @@ const VariableEditModal = forwardRef<VariableEditModalRef, VariableEditModalProp
|
||||
}, ref) => {
|
||||
const { t } = useTranslation();
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [form] = Form.useForm<StartVariableItem>();
|
||||
const [form] = Form.useForm<Variable>();
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [editVo, setEditVo] = useState<StartVariableItem | null>(null)
|
||||
const [editVo, setEditVo] = useState<Variable | null>(null)
|
||||
|
||||
const values = Form.useWatch([], form);
|
||||
|
||||
@@ -50,7 +50,7 @@ const VariableEditModal = forwardRef<VariableEditModalRef, VariableEditModalProp
|
||||
setEditVo(null)
|
||||
};
|
||||
|
||||
const handleOpen = (variable?: StartVariableItem) => {
|
||||
const handleOpen = (variable?: Variable) => {
|
||||
setVisible(true);
|
||||
if (variable) {
|
||||
setEditVo(variable || null)
|
||||
@@ -85,7 +85,7 @@ const VariableEditModal = forwardRef<VariableEditModalRef, VariableEditModalProp
|
||||
|
||||
return (
|
||||
<RbModal
|
||||
title={editVo ? t('workflow.config.start.editVariable') : t('workflow.config.start.addVariable')}
|
||||
title={editVo ? t('workflow.config.start.editVariable') : t('workflow.config.addVariable')}
|
||||
open={visible}
|
||||
onCancel={handleClose}
|
||||
okText={t('common.save')}
|
||||
@@ -96,6 +96,7 @@ const VariableEditModal = forwardRef<VariableEditModalRef, VariableEditModalProp
|
||||
form={form}
|
||||
layout="vertical"
|
||||
initialValues={initialValues}
|
||||
size="middle"
|
||||
scrollToFirstError={{ behavior: 'instant', block: 'end', focus: true }}
|
||||
>
|
||||
{/* 变量类型 */}
|
||||
@@ -0,0 +1,108 @@
|
||||
import { type FC, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Node } from '@antv/x6';
|
||||
import { Space, Button, Divider, App } from 'antd'
|
||||
import type { Variable, VariableEditModalRef } from './types'
|
||||
import type { NodeConfig } from '../../../types'
|
||||
import VariableEditModal from './VariableEditModal'
|
||||
|
||||
interface VariableListProps {
|
||||
selectedNode?: Node | null;
|
||||
config: NodeConfig;
|
||||
value?: Variable[];
|
||||
parentName: string;
|
||||
onChange?: (value: Variable[]) => void;
|
||||
}
|
||||
const VariableList: FC<VariableListProps> = ({
|
||||
value = [],
|
||||
onChange,
|
||||
selectedNode,
|
||||
config,
|
||||
parentName
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const { modal } = App.useApp()
|
||||
const variableModalRef = useRef<VariableEditModalRef>(null)
|
||||
const [editIndex, setEditIndex] = useState<number | null>(null)
|
||||
|
||||
const handleAddVariable = () => {
|
||||
setEditIndex(null)
|
||||
variableModalRef.current?.handleOpen()
|
||||
}
|
||||
const handleEditVariable = (index: number, vo: Variable) => {
|
||||
variableModalRef.current?.handleOpen(vo)
|
||||
setEditIndex(index)
|
||||
}
|
||||
const handleRefreshVariable = (variable: Variable) => {
|
||||
if (!selectedNode) return
|
||||
|
||||
if (editIndex !== null) {
|
||||
const list = [...value]
|
||||
list[editIndex] = variable
|
||||
onChange?.(list)
|
||||
} else {
|
||||
console.log('VariableList', value, variable)
|
||||
onChange?.([...value, variable])
|
||||
}
|
||||
}
|
||||
const handleDeleteVariable = (index: number, vo: Variable, e: React.MouseEvent) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
if (!selectedNode) return
|
||||
|
||||
modal.confirm({
|
||||
title: t('common.confirmDeleteDesc', { name: vo.name }),
|
||||
okText: t('common.delete'),
|
||||
cancelText: t('common.cancel'),
|
||||
okType: 'danger',
|
||||
onOk: () => {
|
||||
const list = [...value]
|
||||
list.splice(index, 1)
|
||||
onChange?.([...list])
|
||||
}
|
||||
})
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<Space size={10} direction="vertical" className="rb:w-full">
|
||||
<div className="rb:leading-4.25 rb:text-[12px] rb:font-medium">
|
||||
{t(`workflow.config.${selectedNode?.data?.type}.${parentName}`)}
|
||||
</div>
|
||||
<Button type="dashed" block size="middle" className="rb:text-[12px]!" onClick={handleAddVariable}>+ {t('workflow.config.addVariable')}</Button>
|
||||
{Array.isArray(value) && value?.map((vo, index) =>
|
||||
<div
|
||||
key={`${vo.name}}-${index}`}
|
||||
className="rb:cursor-pointer rb:group rb:py-2 rb:pl-2.5 rb:pr-2 rb:text-[12px] rb:flex rb:items-center rb:justify-between rb:bg-[#F6F8FC] rb:border rb:border-[#DFE4ED] rb:rounded-md"
|
||||
onClick={() => handleEditVariable(index, vo)}
|
||||
>
|
||||
<span className="rb:font-medium">{vo.name}·{vo.description}</span>
|
||||
|
||||
<Space size={8}>
|
||||
{vo.required && <span className="rb:py-px rb:px-2 rb:bg-[#FBFDFF] rb:border rb:border-[#DFE4ED] rb:rounded-sm">{t('workflow.config.start.required')}</span>}
|
||||
<span className="rb:py-px rb:px-2 rb:bg-[#FBFDFF] rb:border rb:border-[#DFE4ED] rb:rounded-sm">{vo.type}</span>
|
||||
<div
|
||||
className="rb:size-3 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/close.svg')] rb:hover:bg-[url('@/assets/images/close_hover.svg')]"
|
||||
onClick={(e) => handleDeleteVariable(index, vo, e)}
|
||||
></div>
|
||||
</Space>
|
||||
</div>
|
||||
)}
|
||||
|
||||
</Space>
|
||||
<Divider size="small" />
|
||||
<Space size={10} direction="vertical" className="rb:w-full">
|
||||
{config.sys?.map((vo, index) =>
|
||||
<div key={index} className="rb:py-2 rb:pl-2.5 rb:pr-2 rb:text-[12px] rb:flex rb:items-center rb:justify-between rb:bg-[#F6F8FC] rb:border rb:border-[#DFE4ED] rb:rounded-md">
|
||||
<span className="rb:font-medium">sys.{vo.name}</span>
|
||||
<span className="rb:py-px rb:px-2 rb:bg-[#FBFDFF] rb:border rb:border-[#DFE4ED] rb:rounded-sm">{vo.type}</span>
|
||||
</div>
|
||||
)}
|
||||
</Space>
|
||||
<VariableEditModal
|
||||
ref={variableModalRef}
|
||||
refresh={handleRefreshVariable}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default VariableList
|
||||
@@ -0,0 +1,23 @@
|
||||
export interface Variable {
|
||||
name: string;
|
||||
type: string;
|
||||
required: boolean;
|
||||
description: string;
|
||||
max_length?: number;
|
||||
default?: string;
|
||||
readonly?: boolean;
|
||||
defaultValue?: any;
|
||||
value?: any;
|
||||
}
|
||||
export interface VariableEditModalRef {
|
||||
handleOpen: (values?: Variable) => void;
|
||||
}
|
||||
|
||||
export interface ApiExtensionModalData {
|
||||
name: string;
|
||||
apiEndpoint: string;
|
||||
apiKey: string;
|
||||
}
|
||||
export interface ApiExtensionModalRef {
|
||||
handleOpen: () => void;
|
||||
}
|
||||
@@ -10,6 +10,7 @@ interface VariableSelectProps extends SelectProps {
|
||||
onChange?: (value: string) => void;
|
||||
allowClear?: boolean;
|
||||
filterBooleanType?: boolean;
|
||||
size?: 'small' | 'middle' | 'large'
|
||||
}
|
||||
|
||||
const VariableSelect: FC<VariableSelectProps> = ({
|
||||
@@ -18,7 +19,7 @@ const VariableSelect: FC<VariableSelectProps> = ({
|
||||
value,
|
||||
allowClear = true,
|
||||
onChange,
|
||||
size,
|
||||
size = 'middle',
|
||||
filterBooleanType = false,
|
||||
...resetPorps
|
||||
}) => {
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { type FC, useEffect, useState, useRef, useMemo } from "react";
|
||||
import clsx from 'clsx'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Graph, Node } from '@antv/x6';
|
||||
import { Form, Input, Button, Select, InputNumber, Slider, Space, Divider, App, Switch } from 'antd'
|
||||
import { Form, Input, Select, InputNumber, Switch } from 'antd'
|
||||
|
||||
import type { NodeConfig, NodeProperties, StartVariableItem, VariableEditModalRef, ChatVariable } from '../../types'
|
||||
import type { NodeConfig, NodeProperties, ChatVariable } from '../../types'
|
||||
import Empty from '@/components/Empty';
|
||||
import emptyIcon from '@/assets/images/workflow/empty.png'
|
||||
import CustomSelect from "@/components/CustomSelect";
|
||||
import VariableEditModal from './VariableEditModal';
|
||||
import MessageEditor from './MessageEditor'
|
||||
import Knowledge from './Knowledge/Knowledge';
|
||||
import type { Suggestion } from '../Editor/plugin/AutocompletePlugin'
|
||||
@@ -23,7 +23,11 @@ import CycleVarsList from './CycleVarsList'
|
||||
import AssignmentList from './AssignmentList'
|
||||
import ToolConfig from './ToolConfig'
|
||||
import MemoryConfig from './MemoryConfig'
|
||||
import VariableList from './VariableList'
|
||||
// import { calculateVariableList } from './utils/variableListCalculator'
|
||||
import styles from './properties.module.css'
|
||||
import Editor from "../Editor";
|
||||
import RbSlider from './RbSlider'
|
||||
|
||||
interface PropertiesProps {
|
||||
selectedNode?: Node | null;
|
||||
@@ -42,12 +46,9 @@ const Properties: FC<PropertiesProps> = ({
|
||||
chatVariables
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const { modal } = App.useApp()
|
||||
const [form] = Form.useForm<NodeConfig>();
|
||||
const [configs, setConfigs] = useState<Record<string,NodeConfig>>({} as Record<string,NodeConfig>)
|
||||
const values = Form.useWatch([], form);
|
||||
const variableModalRef = useRef<VariableEditModalRef>(null)
|
||||
const [editIndex, setEditIndex] = useState<number | null>(null)
|
||||
const [graphUpdateTrigger, setGraphUpdateTrigger] = useState(0)
|
||||
const prevMappingNamesRef = useRef<string[]>([])
|
||||
const prevTemplateVarsRef = useRef<string[]>([])
|
||||
@@ -243,49 +244,6 @@ const Properties: FC<PropertiesProps> = ({
|
||||
}
|
||||
}, [values, selectedNode, form])
|
||||
|
||||
const handleAddVariable = () => {
|
||||
setEditIndex(null)
|
||||
variableModalRef.current?.handleOpen()
|
||||
}
|
||||
const handleEditVariable = (index: number, vo: StartVariableItem) => {
|
||||
variableModalRef.current?.handleOpen(vo)
|
||||
setEditIndex(index)
|
||||
}
|
||||
const handleRefreshVariable = (value: StartVariableItem) => {
|
||||
if (!selectedNode) return
|
||||
|
||||
if (editIndex !== null) {
|
||||
const defaultValue = selectedNode.data.config.variables.defaultValue ?? []
|
||||
defaultValue[editIndex] = value
|
||||
selectedNode.data.config.variables.defaultValue = [...defaultValue]
|
||||
} else {
|
||||
const defaultValue = selectedNode.data.config.variables.defaultValue ?? []
|
||||
selectedNode.data.config.variables.defaultValue = [...defaultValue, value]
|
||||
}
|
||||
selectedNode?.setData({ ...selectedNode.data})
|
||||
|
||||
setConfigs({ ...selectedNode.data.config })
|
||||
}
|
||||
const handleDeleteVariable = (index: number, vo: StartVariableItem) => {
|
||||
if (!selectedNode) return
|
||||
|
||||
modal.confirm({
|
||||
title: t('common.confirmDeleteDesc', { name: vo.name }),
|
||||
okText: t('common.delete'),
|
||||
cancelText: t('common.cancel'),
|
||||
okType: 'danger',
|
||||
onOk: () => {
|
||||
const defaultValue = selectedNode.data.config.variables.defaultValue ?? []
|
||||
defaultValue.splice(index, 1)
|
||||
selectedNode.data.config.variables.defaultValue = [...defaultValue]
|
||||
|
||||
selectedNode?.setData({ ...selectedNode.data })
|
||||
|
||||
setConfigs({ ...selectedNode.data.config })
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const variableList = useMemo(() => {
|
||||
if (!selectedNode || !graphRef?.current) return [];
|
||||
|
||||
@@ -586,7 +544,7 @@ const Properties: FC<PropertiesProps> = ({
|
||||
break
|
||||
case 'question-classifier':
|
||||
const classNameKey = `${dataNodeId}_class_name`;
|
||||
const outputKey = `${dataNodeId}_output`;
|
||||
// const outputKey = `${dataNodeId}_output`;
|
||||
if (!addedKeys.has(classNameKey)) {
|
||||
addedKeys.add(classNameKey);
|
||||
variableList.push({
|
||||
@@ -1039,11 +997,11 @@ const Properties: FC<PropertiesProps> = ({
|
||||
console.log('variableList', variableList)
|
||||
|
||||
return (
|
||||
<div className="rb:w-75 rb:fixed rb:right-0 rb:top-16 rb:bottom-0 rb:p-3">
|
||||
<div className="rb:font-medium rb:leading-5 rb:mb-3">{t('workflow.nodeProperties')}</div>
|
||||
<div className={clsx("rb:w-75 rb:fixed rb:right-0 rb:top-16 rb:bottom-0 rb:p-3 rb:pb-6", styles.properties)}>
|
||||
<div className="rb:font-medium rb:leading-5 rb:pb-3 rb:mb-3 rb:border-b rb:border-b-[#DFE4ED]">{t('workflow.nodeProperties')}</div>
|
||||
{!selectedNode
|
||||
? <Empty url={emptyIcon} size={140} className="rb:h-full rb:mx-15" title={t('workflow.empty')} />
|
||||
: <Form form={form} layout="vertical" className="rb:h-[calc(100%-20px)] rb:overflow-y-auto">
|
||||
: <Form form={form} size="small" layout="vertical" className="rb:h-[calc(100%-20px)] rb:overflow-x-hidden rb:overflow-y-auto">
|
||||
<Form.Item name="name" label={t('workflow.nodeName')}>
|
||||
<Input
|
||||
placeholder={t('common.pleaseEnter')}
|
||||
@@ -1073,46 +1031,13 @@ const Properties: FC<PropertiesProps> = ({
|
||||
|
||||
if (selectedNode?.data?.type === 'start' && key === 'variables' && config.type === 'define') {
|
||||
return (
|
||||
<div key={key}>
|
||||
<div className="rb:flex rb:items-center rb:justify-between rb:mb-2.75">
|
||||
<div className="rb:leading-5">
|
||||
{t(`workflow.config.${selectedNode?.data?.type}.${key}`)}
|
||||
</div>
|
||||
<Button style={{padding: '0 8px', height: '24px'}} onClick={handleAddVariable}>+{t('application.addVariables')}</Button>
|
||||
</div>
|
||||
|
||||
<Space size={4} direction="vertical" className="rb:w-full">
|
||||
{Array.isArray(config.defaultValue) && config.defaultValue?.map((vo, index) =>
|
||||
<div key={`${vo.name}}-${index}`} className="rb:p-[4px_8px] rb:text-[12px] rb:text-[#5B6167] rb:flex rb:items-center rb:justify-between rb:border rb:border-[#DFE4ED] rb:rounded-md rb:group rb:cursor-pointer">
|
||||
<span>{vo.name}·{vo.description}</span>
|
||||
|
||||
<div className="rb:group-hover:hidden rb:flex rb:items-center rb:gap-1">
|
||||
{vo.required && <span>{t('workflow.config.start.required')}</span>}
|
||||
{vo.type}
|
||||
</div>
|
||||
<Space className="rb:hidden! rb:group-hover:flex!">
|
||||
<div
|
||||
className="rb:w-4.5 rb:h-4.5 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/editBorder.svg')] rb:hover:bg-[url('@/assets/images/editBg.svg')]"
|
||||
onClick={() => handleEditVariable(index, vo)}
|
||||
></div>
|
||||
<div
|
||||
className="rb:w-4.5 rb:h-4.5 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/deleteBorder.svg')] rb:hover:bg-[url('@/assets/images/deleteBg.svg')]"
|
||||
onClick={() => handleDeleteVariable(index, vo)}
|
||||
></div>
|
||||
</Space>
|
||||
</div>
|
||||
)}
|
||||
<Divider size="small" />
|
||||
{config.sys?.map((vo, index) =>
|
||||
<div key={index} className="rb:p-[4px_8px] rb:text-[12px] rb:text-[#5B6167] rb:flex rb:items-center rb:justify-between rb:border rb:border-[#DFE4ED] rb:rounded-md">
|
||||
<div>
|
||||
<span>sys.{vo.name}</span>
|
||||
</div>
|
||||
{vo.type}
|
||||
</div>
|
||||
)}
|
||||
</Space>
|
||||
</div>
|
||||
<Form.Item key={key} name={key}>
|
||||
<VariableList
|
||||
parentName={key}
|
||||
selectedNode={selectedNode}
|
||||
config={config}
|
||||
/>
|
||||
</Form.Item>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1143,23 +1068,12 @@ const Properties: FC<PropertiesProps> = ({
|
||||
key={key}
|
||||
options={contextVariableList.filter(variable => variable.nodeData?.type !== 'knowledge-retrieval')}
|
||||
parentName={key}
|
||||
placeholder={t(config.placeholder || 'common.pleaseSelect')}
|
||||
size="small"
|
||||
/>
|
||||
</Form.Item>
|
||||
)
|
||||
}
|
||||
if (selectedNode?.data?.type === 'end' && key === 'output') {
|
||||
return (
|
||||
<Form.Item key={key} name={key}>
|
||||
<MessageEditor
|
||||
key={key}
|
||||
isArray={false}
|
||||
parentName={key}
|
||||
options={variableList.filter(variable => variable.nodeData?.type !== 'knowledge-retrieval')}
|
||||
/>
|
||||
</Form.Item>
|
||||
)
|
||||
}
|
||||
|
||||
if (config.type === 'define') {
|
||||
return null
|
||||
}
|
||||
@@ -1184,6 +1098,8 @@ const Properties: FC<PropertiesProps> = ({
|
||||
parentName={key}
|
||||
enableJinja2={config.enableJinja2 as boolean}
|
||||
options={getFilteredVariableList(selectedNode?.data?.type, key)}
|
||||
titleVariant={config.titleVariant}
|
||||
size="small"
|
||||
/>
|
||||
</Form.Item>
|
||||
)
|
||||
@@ -1206,9 +1122,9 @@ const Properties: FC<PropertiesProps> = ({
|
||||
name={key}
|
||||
options={getFilteredVariableList(selectedNode?.data?.type, key)}
|
||||
isCanAdd={!!(values as any)?.group}
|
||||
size="small"
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
)
|
||||
}
|
||||
if (config.type === 'caseList') {
|
||||
@@ -1226,9 +1142,7 @@ const Properties: FC<PropertiesProps> = ({
|
||||
|
||||
if (config.type === 'mappingList') {
|
||||
return (
|
||||
<Form.Item key={key} name={key}
|
||||
label={t(`workflow.config.${selectedNode?.data?.type}.${key}`)}
|
||||
>
|
||||
<Form.Item key={key} name={key} noStyle>
|
||||
<MappingList name={key} options={getFilteredVariableList(selectedNode?.data?.type, key)} />
|
||||
</Form.Item>
|
||||
|
||||
@@ -1238,6 +1152,7 @@ const Properties: FC<PropertiesProps> = ({
|
||||
return (
|
||||
<Form.Item key={key} name={key}>
|
||||
<CycleVarsList
|
||||
size="small"
|
||||
parentName={key}
|
||||
options={getFilteredVariableList(selectedNode?.data?.type, key)}
|
||||
/>
|
||||
@@ -1276,87 +1191,14 @@ const Properties: FC<PropertiesProps> = ({
|
||||
</Form.Item>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Form.Item
|
||||
key={key}
|
||||
name={key}
|
||||
label={t(`workflow.config.${selectedNode?.data?.type}.${key}`)}
|
||||
layout={config.type === 'switch' ? 'horizontal' : 'vertical'}
|
||||
>
|
||||
{config.type === 'input'
|
||||
? <Input placeholder={t('common.pleaseEnter')} />
|
||||
: config.type === 'textarea'
|
||||
? <Input.TextArea placeholder={t('common.pleaseEnter')} />
|
||||
: config.type === 'select'
|
||||
? <Select
|
||||
options={config.needTranslation ? (config.options || []).map(vo => ({ ...vo, label: t(vo.label) })) : config.options}
|
||||
placeholder={t('common.pleaseSelect')}
|
||||
/>
|
||||
: config.type === 'inputNumber'
|
||||
? <InputNumber
|
||||
placeholder={t('common.pleaseEnter')}
|
||||
className="rb:w-full!"
|
||||
onChange={(value) => form.setFieldValue(key, value)}
|
||||
/>
|
||||
: config.type === 'slider'
|
||||
? <Slider min={config.min} max={config.max} step={config.step} />
|
||||
: config.type === 'customSelect'
|
||||
? <CustomSelect
|
||||
placeholder={t('common.pleaseSelect')}
|
||||
url={config.url as string}
|
||||
params={config.params}
|
||||
hasAll={false}
|
||||
valueKey={config.valueKey}
|
||||
labelKey={config.labelKey}
|
||||
/>
|
||||
: config.type === 'variableList'
|
||||
? <VariableSelect
|
||||
placeholder={t('common.pleaseSelect')}
|
||||
options={(() => {
|
||||
const baseVariableList = getFilteredVariableList(selectedNode?.data?.type, key);
|
||||
// Apply filtering if specified in config
|
||||
if (config.filterNodeTypes || config.filterVariableNames) {
|
||||
return baseVariableList.filter(variable => {
|
||||
const nodeTypeMatch = !config.filterNodeTypes ||
|
||||
(Array.isArray(config.filterNodeTypes) && config.filterNodeTypes.includes(variable.nodeData?.type));
|
||||
const variableNameMatch = !config.filterVariableNames ||
|
||||
(Array.isArray(config.filterVariableNames) && config.filterVariableNames.includes(variable.label));
|
||||
return nodeTypeMatch || variableNameMatch;
|
||||
});
|
||||
}
|
||||
// Filter child nodes for iteration output
|
||||
if (config.filterChildNodes && selectedNode) {
|
||||
const graph = graphRef.current;
|
||||
if (!graph) return [];
|
||||
|
||||
const nodes = graph.getNodes();
|
||||
|
||||
// Find child nodes whose cycle field equals parent node's ID
|
||||
const childNodes = nodes.filter(node => {
|
||||
const nodeData = node.getData();
|
||||
return nodeData?.cycle === selectedNode.id;
|
||||
});
|
||||
|
||||
return baseVariableList.filter(variable =>
|
||||
childNodes.some(node => node.id === variable.nodeData?.id) || selectedNode?.data?.type === 'iteration' && key === 'output' && variable.value.includes('sys.')
|
||||
);
|
||||
}
|
||||
return baseVariableList;
|
||||
})()
|
||||
}
|
||||
/>
|
||||
: config.type === 'switch'
|
||||
? <Switch onChange={key === 'group' ? () => { form.setFieldValue('group_variables', []) } : undefined} />
|
||||
: config.type === 'categoryList'
|
||||
? <CategoryList
|
||||
parentName={key}
|
||||
selectedNode={selectedNode}
|
||||
graphRef={graphRef}
|
||||
options={getFilteredVariableList(selectedNode?.data?.type, key)}
|
||||
/>
|
||||
: config.type === 'conditionList'
|
||||
? <ConditionList
|
||||
if (config.type === 'conditionList') {
|
||||
return (
|
||||
<Form.Item
|
||||
key={key}
|
||||
name={key}
|
||||
noStyle
|
||||
>
|
||||
<ConditionList
|
||||
parentName={key}
|
||||
options={(() => {
|
||||
const cycleVars = values?.cycle_vars || [];
|
||||
@@ -1375,6 +1217,93 @@ const Properties: FC<PropertiesProps> = ({
|
||||
graphRef={graphRef}
|
||||
addBtnText={t('workflow.config.addCase')}
|
||||
/>
|
||||
</Form.Item>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Form.Item
|
||||
key={key}
|
||||
name={key}
|
||||
label={key === 'parallel_count' ? <span className="rb:text-[10px] rb:text-[#5B6167] rb:leading-3.5 rb:-mb-1!">{t(`workflow.config.${selectedNode?.data?.type}.${key}`)}</span> : t(`workflow.config.${selectedNode?.data?.type}.${key}`)}
|
||||
layout={config.type === 'switch' ? 'horizontal' : 'vertical'}
|
||||
className={key === 'parallel_count' ? 'rb:-mt-3! rb:leading-3.5!' : ''}
|
||||
>
|
||||
{config.type === 'input'
|
||||
? <Input placeholder={t('common.pleaseEnter')} />
|
||||
: config.type === 'textarea'
|
||||
? <Input.TextArea placeholder={t('common.pleaseEnter')} />
|
||||
: config.type === 'select'
|
||||
? <Select
|
||||
options={config.needTranslation ? (config.options || []).map(vo => ({ ...vo, label: t(vo.label) })) : config.options}
|
||||
placeholder={t('common.pleaseSelect')}
|
||||
/>
|
||||
: config.type === 'inputNumber'
|
||||
? <InputNumber
|
||||
placeholder={t('common.pleaseEnter')}
|
||||
className="rb:w-full!"
|
||||
onChange={(value) => form.setFieldValue(key, value)}
|
||||
/>
|
||||
: config.type === 'slider'
|
||||
? <RbSlider min={config.min} max={config.max} step={config.step} />
|
||||
: config.type === 'customSelect'
|
||||
? <CustomSelect
|
||||
placeholder={t('common.pleaseSelect')}
|
||||
url={config.url as string}
|
||||
params={config.params}
|
||||
hasAll={false}
|
||||
valueKey={config.valueKey}
|
||||
labelKey={config.labelKey}
|
||||
size="small"
|
||||
/>
|
||||
: config.type === 'variableList'
|
||||
? <VariableSelect
|
||||
placeholder={t(config.placeholder || 'common.pleaseSelect')}
|
||||
options={(() => {
|
||||
const baseVariableList = getFilteredVariableList(selectedNode?.data?.type, key);
|
||||
// Apply filtering if specified in config
|
||||
if (config.filterNodeTypes || config.filterVariableNames) {
|
||||
return baseVariableList.filter(variable => {
|
||||
const nodeTypeMatch = !config.filterNodeTypes ||
|
||||
(Array.isArray(config.filterNodeTypes) && config.filterNodeTypes.includes(variable.nodeData?.type));
|
||||
const variableNameMatch = !config.filterVariableNames ||
|
||||
(Array.isArray(config.filterVariableNames) && config.filterVariableNames.includes(variable.label));
|
||||
return nodeTypeMatch || variableNameMatch;
|
||||
});
|
||||
}
|
||||
// Filter child nodes for iteration output
|
||||
if (config.filterChildNodes && selectedNode) {
|
||||
const graph = graphRef.current;
|
||||
if (!graph) return [];
|
||||
|
||||
const nodes = graph.getNodes();
|
||||
|
||||
// Find child nodes whose cycle field equals parent node's ID
|
||||
const childNodes = nodes.filter(node => {
|
||||
const nodeData = node.getData();
|
||||
return nodeData?.cycle === selectedNode.id;
|
||||
});
|
||||
|
||||
return baseVariableList.filter(variable =>
|
||||
childNodes.some(node => node.id === variable.nodeData?.id) || selectedNode?.data?.type === 'iteration' && key === 'output' && variable.value.includes('sys.')
|
||||
);
|
||||
}
|
||||
return baseVariableList;
|
||||
})()
|
||||
}
|
||||
size="small"
|
||||
/>
|
||||
: config.type === 'switch'
|
||||
? <Switch onChange={key === 'group' ? () => { form.setFieldValue('group_variables', []) } : undefined} />
|
||||
: config.type === 'categoryList'
|
||||
? <CategoryList
|
||||
parentName={key}
|
||||
selectedNode={selectedNode}
|
||||
graphRef={graphRef}
|
||||
options={getFilteredVariableList(selectedNode?.data?.type, key)}
|
||||
/>
|
||||
: config.type === 'editor'
|
||||
? <Editor options={variableList} variant="outlined" size="small" />
|
||||
: null
|
||||
}
|
||||
</Form.Item>
|
||||
@@ -1383,11 +1312,6 @@ const Properties: FC<PropertiesProps> = ({
|
||||
}
|
||||
</Form>
|
||||
}
|
||||
|
||||
<VariableEditModal
|
||||
ref={variableModalRef}
|
||||
refresh={handleRefreshVariable}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
.properties :global(.ant-input-outlined),
|
||||
.properties :global(.ant-input-number-outlined),
|
||||
.properties :global(.ant-select-outlined:not(.ant-select-customize-input) .ant-select-selector),
|
||||
.properties :global(.ant-input-number-outlined),
|
||||
.properties :global(.ant-input-number-outlined .ant-input-number-handler-wrap) {
|
||||
background-color: transparent;
|
||||
}
|
||||
.properties :global(.ant-input-outlined.ant-input-disabled),
|
||||
.properties :global(.ant-input-outlined[disabled]) {
|
||||
background-color: #F6F8FC;
|
||||
}
|
||||
.properties :global(.ant-select-single.ant-select-sm){
|
||||
height: 28px;
|
||||
}
|
||||
.properties :global(.ant-table-wrapper .ant-table-thead>tr>th),
|
||||
.properties :global(.ant-table-wrapper .ant-table-thead>tr>td),
|
||||
.properties :global(.ant-table-wrapper .ant-table) {
|
||||
background-color: #F6F8FC;
|
||||
}
|
||||
.properties :global(.ant-table-wrapper .ant-table),
|
||||
.properties :global(.ant-table-container),
|
||||
.properties :global(.ant-table-wrapper table) {
|
||||
border-radius: 6px;
|
||||
}
|
||||
.properties :global(.ant-table-wrapper .ant-table-container table>thead>tr:first-child>*:first-child) {
|
||||
border-start-start-radius: 6px;
|
||||
}
|
||||
.properties :global(.ant-table-wrapper .ant-table-container table>thead>tr:first-child>*:last-child) {
|
||||
border-start-end-radius: 6px;
|
||||
}
|
||||
.properties :global(.ant-table-row:last-child .ant-table-cell:first-child) {
|
||||
border-bottom-left-radius: 6px;
|
||||
}
|
||||
.properties :global(.ant-table-row:last-child .ant-table-cell:last-child) {
|
||||
border-bottom-right-radius: 6px;
|
||||
}
|
||||
.properties :global(.ant-table-wrapper .ant-table) {
|
||||
background: transparent;
|
||||
}
|
||||
.properties :global(.ant-table-wrapper .ant-table-container) {
|
||||
border-start-start-radius: 6px;
|
||||
border-start-end-radius: 6px;
|
||||
}
|
||||
.properties :global(.ant-table-container) {
|
||||
/* border-left: none;
|
||||
border-top: none;
|
||||
border-bottom: none; */
|
||||
border: none;
|
||||
}
|
||||
.properties :global(.ant-table-wrapper .ant-table-tbody>tr.ant-table-placeholder:hover>th),
|
||||
.properties :global(.ant-table-wrapper .ant-table-tbody>tr.ant-table-placeholder:hover>td),
|
||||
.properties :global(.ant-table-wrapper .ant-table-tbody>tr.ant-table-placeholder),
|
||||
.properties :global(.ant-table-wrapper .ant-table) {
|
||||
background-color: #F6F8FC;
|
||||
}
|
||||
.properties :global(.ant-form-item-horizontal.ant-form-item .ant-form-item-control-input-content:has(> .ant-switch:only-child, > .ant-rate:only-child)) {
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
}
|
||||
.properties :global(.ant-divider-horizontal.ant-divider-sm) {
|
||||
margin-block: 16px;
|
||||
}
|
||||
.properties :global(.ant-form-item) {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.properties :global(.ant-form-item .ant-form-item-label>label) {
|
||||
font-weight: 500;
|
||||
font-size: 12px;
|
||||
}
|
||||
.properties :global(.ant-select-single.ant-select-sm .ant-select-selector),
|
||||
.properties :global(.ant-select-dropdown .ant-select-item),.properties :global(.ant-input-number-sm) {
|
||||
font-size: 12px;
|
||||
}
|
||||
.properties :global(.ant-input-number-out-of-range .ant-input-number-input-wrap input) {
|
||||
color: #212332;
|
||||
}
|
||||
.properties :global(.ant-slider-horizontal .ant-slider-step) {
|
||||
height: 6px;
|
||||
}
|
||||
.properties :global(.ant-select-single.ant-select-sm:not(.ant-select-customize-input) .ant-select-selector) {
|
||||
padding: 0 4px 0 6px ;
|
||||
}
|
||||
.properties :global(.ant-select-single.ant-select-sm:not(.ant-select-customize-input).ant-select-show-arrow .ant-select-selection-item),
|
||||
.properties :global(.ant-select-single.ant-select-sm:not(.ant-select-customize-input).ant-select-show-arrow .ant-select-selection-placeholder) {
|
||||
padding-right: 10px;
|
||||
}
|
||||
.properties :global(.ant-select .ant-select-arrow) {
|
||||
font-size: 10px;
|
||||
inset-inline-end: 6px;
|
||||
}
|
||||
@@ -90,7 +90,7 @@ export const nodeLibrary: NodeLibrary[] = [
|
||||
type: "end", icon: endIcon,
|
||||
config: {
|
||||
output: {
|
||||
type: 'define'
|
||||
type: 'editor'
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -125,6 +125,7 @@ export const nodeLibrary: NodeLibrary[] = [
|
||||
},
|
||||
context: {
|
||||
type: 'variableList',
|
||||
placeholder: 'workflow.config.llm.contextPlaceholder'
|
||||
},
|
||||
messages: {
|
||||
type: 'define',
|
||||
@@ -134,7 +135,8 @@ export const nodeLibrary: NodeLibrary[] = [
|
||||
content: undefined,
|
||||
readonly: true
|
||||
},
|
||||
]
|
||||
],
|
||||
placeholder: 'workflow.config.llm.messagesPlaceholder'
|
||||
},
|
||||
memory: {
|
||||
type: 'memoryConfig',
|
||||
@@ -170,7 +172,8 @@ export const nodeLibrary: NodeLibrary[] = [
|
||||
},
|
||||
text: {
|
||||
type: 'variableList',
|
||||
filterLoopIterationVars: true
|
||||
filterLoopIterationVars: true,
|
||||
placeholder: 'workflow.config.parameter-extractor.textPlaceholder'
|
||||
},
|
||||
params: {
|
||||
type: 'paramList',
|
||||
@@ -178,6 +181,8 @@ export const nodeLibrary: NodeLibrary[] = [
|
||||
prompt: {
|
||||
type: 'messageEditor',
|
||||
isArray: false,
|
||||
titleVariant: 'borderless',
|
||||
placeholder: 'workflow.config.parameter-extractor.promptPlaceholder'
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -189,7 +194,7 @@ export const nodeLibrary: NodeLibrary[] = [
|
||||
{ type: "memory-read", icon: memoryReadIcon,
|
||||
config: {
|
||||
message: {
|
||||
type: 'messageEditor',
|
||||
type: 'editor',
|
||||
isArray: false
|
||||
},
|
||||
config_id: {
|
||||
@@ -212,7 +217,7 @@ export const nodeLibrary: NodeLibrary[] = [
|
||||
{ type: "memory-write", icon: memoryWriteIcon,
|
||||
config: {
|
||||
message: {
|
||||
type: 'messageEditor',
|
||||
type: 'editor',
|
||||
isArray: false
|
||||
},
|
||||
config_id: {
|
||||
@@ -270,7 +275,8 @@ export const nodeLibrary: NodeLibrary[] = [
|
||||
},
|
||||
user_supplement_prompt: {
|
||||
type: 'messageEditor',
|
||||
isArray: false
|
||||
isArray: false,
|
||||
titleVariant: 'borderless'
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -436,6 +442,7 @@ export const nodeLibrary: NodeLibrary[] = [
|
||||
type: 'messageEditor',
|
||||
isArray: false,
|
||||
enableJinja2: true,
|
||||
titleVariant: 'borderless',
|
||||
defaultValue: "{{arg1}}"
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
|
||||
import { Graph } from '@antv/x6';
|
||||
import type { KnowledgeConfig } from './components/Properties/Knowledge/types'
|
||||
import type { Variable } from './components/Properties/VariableList/types'
|
||||
export interface NodeConfig {
|
||||
type: 'input' | 'textarea' | 'select' | 'inputNumber' | 'slider' | 'customSelect' | 'define' | 'knowledge' | 'variableList' | string;
|
||||
placeholder?: string;
|
||||
titleVariant?: 'outlined' | 'borderless';
|
||||
options?: { label: string; value: string }[];
|
||||
|
||||
max?: number;
|
||||
@@ -14,7 +17,7 @@ export interface NodeConfig {
|
||||
valueKey?: string;
|
||||
labelKey?: string;
|
||||
|
||||
defaultValue?: any | StartVariableItem[];
|
||||
defaultValue?: any;
|
||||
|
||||
sys?: Array<{
|
||||
name: string;
|
||||
@@ -37,6 +40,7 @@ export interface NodeProperties {
|
||||
id?: string;
|
||||
config?: Record<string, NodeConfig>;
|
||||
hidden?: boolean;
|
||||
cycle?: string;
|
||||
}
|
||||
|
||||
export interface NodeLibrary {
|
||||
@@ -87,27 +91,12 @@ export interface WorkflowConfig {
|
||||
updated_at: number;
|
||||
}
|
||||
|
||||
export interface VariableEditModalRef {
|
||||
handleOpen: (values?: StartVariableItem) => void;
|
||||
}
|
||||
export interface StartVariableItem {
|
||||
name: string;
|
||||
type: string;
|
||||
required: boolean;
|
||||
description: string;
|
||||
max_length?: number;
|
||||
default?: string;
|
||||
readonly?: boolean;
|
||||
defaultValue?: any;
|
||||
value?: any;
|
||||
}
|
||||
|
||||
export interface ChatRef {
|
||||
handleOpen: () => void;
|
||||
}
|
||||
export type GraphRef = React.MutableRefObject<Graph | undefined>
|
||||
export interface VariableConfigModalRef {
|
||||
handleOpen: (values: StartVariableItem[]) => void;
|
||||
handleOpen: (values: Variable[]) => void;
|
||||
}
|
||||
|
||||
export interface ChatVariable {
|
||||
|
||||
Reference in New Issue
Block a user