fix(web): workflow editor bug
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
* @Author: ZhaoYing
|
* @Author: ZhaoYing
|
||||||
* @Date: 2026-02-03 16:34:12
|
* @Date: 2026-02-03 16:34:12
|
||||||
* @Last Modified by: ZhaoYing
|
* @Last Modified by: ZhaoYing
|
||||||
* @Last Modified time: 2026-03-25 11:16:04
|
* @Last Modified time: 2026-03-25 15:38:13
|
||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* Application Management Page
|
* Application Management Page
|
||||||
@@ -199,8 +199,8 @@ const ApplicationManagement: React.FC = () => {
|
|||||||
footer={
|
footer={
|
||||||
item.is_shared
|
item.is_shared
|
||||||
? <Flex justify="space-between" gap={12}>
|
? <Flex justify="space-between" gap={12}>
|
||||||
<RbButton type="primary" ghost block onClick={() => handleEdit(item)}>{t('common.view')}</RbButton>
|
<RbButton block onClick={() => handleEdit(item)}>{t('common.view')}</RbButton>
|
||||||
{item.share_permission === 'editable' && <RbButton type="primary" className="rb:w-[calc(100%-46px)]" onClick={() => handleCopy(item)}>{t('common.copy')}</RbButton>}
|
{item.share_permission === 'editable' && <RbButton type="primary" ghost className="rb:w-[calc(100%-46px)]" onClick={() => handleCopy(item)}>{t('common.copy')}</RbButton>}
|
||||||
</Flex>
|
</Flex>
|
||||||
: <Flex justify="space-between" gap={12}>
|
: <Flex justify="space-between" gap={12}>
|
||||||
<RbButton danger className="rb:w-22.25" onClick={() => handleDelete(item)}>{t('common.delete')}</RbButton>
|
<RbButton danger className="rb:w-22.25" onClick={() => handleDelete(item)}>{t('common.delete')}</RbButton>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* @Author: ZhaoYing
|
* @Author: ZhaoYing
|
||||||
* @Date: 2026-02-03 17:48:59
|
* @Date: 2026-02-03 17:48:59
|
||||||
* @Last Modified by: ZhaoYing
|
* @Last Modified by: ZhaoYing
|
||||||
* @Last Modified time: 2026-03-20 18:49:51
|
* @Last Modified time: 2026-03-25 15:33:38
|
||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* Space Management Page
|
* Space Management Page
|
||||||
@@ -93,7 +93,7 @@ const SpaceManagement: React.FC = () => {
|
|||||||
</RbCard>
|
</RbCard>
|
||||||
</List.Item>
|
</List.Item>
|
||||||
)}
|
)}
|
||||||
className="rb:h-[calc(100vh-148px)] rb:overflow-y-auto rb:overflow-x-hidden"
|
className="rb:h-[calc(100vh-124px)] rb:overflow-y-auto rb:overflow-x-hidden"
|
||||||
/>
|
/>
|
||||||
</BodyWrapper>
|
</BodyWrapper>
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ const InitialValuePlugin: React.FC<InitialValuePluginProps> = ({ value, options
|
|||||||
const prevValueRef = useRef<string>('');
|
const prevValueRef = useRef<string>('');
|
||||||
const prevEnableLineNumbersRef = useRef<boolean>(enableLineNumbers);
|
const prevEnableLineNumbersRef = useRef<boolean>(enableLineNumbers);
|
||||||
const isUserInputRef = useRef(false);
|
const isUserInputRef = useRef(false);
|
||||||
|
const optionsRef = useRef(options);
|
||||||
|
optionsRef.current = options;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const removeListener = editor.registerUpdateListener(({ editorState, tags }) => {
|
const removeListener = editor.registerUpdateListener(({ editorState, tags }) => {
|
||||||
@@ -41,80 +43,84 @@ const InitialValuePlugin: React.FC<InitialValuePluginProps> = ({ value, options
|
|||||||
isUserInputRef.current = false;
|
isUserInputRef.current = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// Update refs BEFORE editor.update to prevent re-entry
|
||||||
|
prevValueRef.current = value;
|
||||||
|
prevEnableLineNumbersRef.current = enableLineNumbers;
|
||||||
|
isUserInputRef.current = false;
|
||||||
|
|
||||||
queueMicrotask(() => {
|
queueMicrotask(() => {
|
||||||
editor.update(() => {
|
editor.update(() => {
|
||||||
const root = $getRoot();
|
const root = $getRoot();
|
||||||
root.clear();
|
root.clear();
|
||||||
|
|
||||||
const parts = value.split(/(\{\{[^}]+\}\})/);
|
const parts = value.split(/(\{\{[^}]+\}\})/);
|
||||||
|
|
||||||
if (enableLineNumbers) {
|
if (enableLineNumbers) {
|
||||||
// Handle newlines properly in Jinja2 mode
|
const lines = value.split('\n');
|
||||||
const lines = value.split('\n');
|
lines.forEach((line) => {
|
||||||
lines.forEach((line) => {
|
const paragraph = $createParagraphNode();
|
||||||
|
paragraph.append($createTextNode(line));
|
||||||
|
root.append(paragraph);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
const paragraph = $createParagraphNode();
|
const paragraph = $createParagraphNode();
|
||||||
paragraph.append($createTextNode(line));
|
parts.forEach(part => {
|
||||||
root.append(paragraph);
|
const match = part.match(/^\{\{([^.]+)\.([^}]+)\}\}$/);
|
||||||
});
|
const contextMatch = part.match(/^\{\{context\}\}$/);
|
||||||
} else {
|
const conversationMatch = part.match(/^\{\{conv\.([^}]+)\}\}$/);
|
||||||
const paragraph = $createParagraphNode();
|
|
||||||
parts.forEach(part => {
|
|
||||||
const match = part.match(/^\{\{([^.]+)\.([^}]+)\}\}$/);
|
|
||||||
const contextMatch = part.match(/^\{\{context\}\}$/);
|
|
||||||
const conversationMatch = part.match(/^\{\{conv\.([^}]+)\}\}$/);
|
|
||||||
|
|
||||||
if (contextMatch) {
|
if (contextMatch) {
|
||||||
const contextSuggestion = options.find(s => s.isContext && s.label === 'context');
|
const contextSuggestion = optionsRef.current.find(s => s.isContext && s.label === 'context');
|
||||||
if (contextSuggestion) {
|
if (contextSuggestion) {
|
||||||
paragraph.append($createVariableNode(contextSuggestion));
|
paragraph.append($createVariableNode(contextSuggestion));
|
||||||
} else {
|
} else {
|
||||||
paragraph.append($createTextNode(part));
|
paragraph.append($createTextNode(part));
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (conversationMatch) {
|
|
||||||
const [_, variableName] = conversationMatch;
|
|
||||||
const conversationSuggestion = options.find(s =>
|
|
||||||
s.group === 'CONVERSATION' && s.label === variableName
|
|
||||||
);
|
|
||||||
if (conversationSuggestion) {
|
|
||||||
paragraph.append($createVariableNode(conversationSuggestion));
|
|
||||||
} else {
|
|
||||||
paragraph.append($createTextNode(part));
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (match) {
|
|
||||||
const [_, nodeId, label] = match;
|
|
||||||
|
|
||||||
const suggestion = options.find(s => {
|
|
||||||
if (nodeId === 'sys') {
|
|
||||||
return s.nodeData.type === 'start' && s.label === `sys.${label}`
|
|
||||||
}
|
}
|
||||||
return s.nodeData.id === nodeId && s.label === label
|
return
|
||||||
});
|
}
|
||||||
|
|
||||||
if (suggestion) {
|
if (conversationMatch) {
|
||||||
paragraph.append($createVariableNode(suggestion));
|
const [_, variableName] = conversationMatch;
|
||||||
} else {
|
const conversationSuggestion = optionsRef.current.find(s =>
|
||||||
|
s.group === 'CONVERSATION' && s.label === variableName
|
||||||
|
);
|
||||||
|
if (conversationSuggestion) {
|
||||||
|
paragraph.append($createVariableNode(conversationSuggestion));
|
||||||
|
} else {
|
||||||
|
paragraph.append($createTextNode(part));
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (match) {
|
||||||
|
const [_, nodeId, label] = match;
|
||||||
|
|
||||||
|
const suggestion = optionsRef.current.find(s => {
|
||||||
|
if (nodeId === 'sys') {
|
||||||
|
return s.nodeData.type === 'start' && s.label === `sys.${label}`
|
||||||
|
}
|
||||||
|
return s.nodeData.id === nodeId && s.label === label
|
||||||
|
});
|
||||||
|
|
||||||
|
if (suggestion) {
|
||||||
|
paragraph.append($createVariableNode(suggestion));
|
||||||
|
} else {
|
||||||
|
paragraph.append($createTextNode(part));
|
||||||
|
}
|
||||||
|
} else if (part) {
|
||||||
paragraph.append($createTextNode(part));
|
paragraph.append($createTextNode(part));
|
||||||
}
|
}
|
||||||
} else if (part) {
|
});
|
||||||
paragraph.append($createTextNode(part));
|
root.append(paragraph);
|
||||||
}
|
}
|
||||||
});
|
}, { tag: 'programmatic' });
|
||||||
root.append(paragraph);
|
|
||||||
}
|
|
||||||
}, { discrete: true, tag: 'programmatic' });
|
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
prevValueRef.current = value;
|
||||||
|
prevEnableLineNumbersRef.current = enableLineNumbers;
|
||||||
|
isUserInputRef.current = false;
|
||||||
}
|
}
|
||||||
|
}, [value, editor, enableLineNumbers]);
|
||||||
prevValueRef.current = value;
|
|
||||||
prevEnableLineNumbersRef.current = enableLineNumbers;
|
|
||||||
isUserInputRef.current = false;
|
|
||||||
}, [value, options, editor, enableLineNumbers]);
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* @Author: ZhaoYing
|
* @Author: ZhaoYing
|
||||||
* @Date: 2026-02-09 18:24:53
|
* @Date: 2026-02-09 18:24:53
|
||||||
* @Last Modified by: ZhaoYing
|
* @Last Modified by: ZhaoYing
|
||||||
* @Last Modified time: 2026-03-24 15:00:46
|
* @Last Modified time: 2026-03-25 15:23:45
|
||||||
*/
|
*/
|
||||||
import { type FC } from 'react'
|
import { type FC } from 'react'
|
||||||
import clsx from 'clsx'
|
import clsx from 'clsx'
|
||||||
@@ -321,7 +321,7 @@ const CaseList: FC<CaseListProps> = ({
|
|||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Divider type="vertical" />
|
<Divider type="vertical" />
|
||||||
<Form.Item name={[conditionField.name, 'right']} noStyle>
|
<Form.Item name={[conditionField.name, 'right']} noStyle>
|
||||||
{inputType === 'Variable'
|
{inputType === 'variable'
|
||||||
? <VariableSelect
|
? <VariableSelect
|
||||||
placeholder={t('common.pleaseSelect')}
|
placeholder={t('common.pleaseSelect')}
|
||||||
options={options.filter(vo => vo.dataType === 'number')}
|
options={options.filter(vo => vo.dataType === 'number')}
|
||||||
|
|||||||
@@ -195,7 +195,7 @@ const ConditionList: FC<CaseListProps> = ({
|
|||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Divider type="vertical" />
|
<Divider type="vertical" />
|
||||||
<Form.Item name={[field.name, 'right']} noStyle>
|
<Form.Item name={[field.name, 'right']} noStyle>
|
||||||
{inputType === 'Variable'
|
{inputType === 'variable'
|
||||||
? (
|
? (
|
||||||
<VariableSelect
|
<VariableSelect
|
||||||
placeholder={t('common.pleaseSelect')}
|
placeholder={t('common.pleaseSelect')}
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ const VariableList: FC<VariableListProps> = ({
|
|||||||
className="rb:cursor-pointer rb:group rb:py-2! rb:pl-2.5! rb:pr-2! rb:text-[12px] rb:bg-[#F6F6F6] rb-border rb:rounded-lg"
|
className="rb:cursor-pointer rb:group rb:py-2! rb:pl-2.5! rb:pr-2! rb:text-[12px] rb:bg-[#F6F6F6] rb-border rb:rounded-lg"
|
||||||
onClick={() => handleEditVariable(index, vo)}
|
onClick={() => handleEditVariable(index, vo)}
|
||||||
>
|
>
|
||||||
<span className="rb:font-medium">{vo.name}·{vo.description}</span>
|
<span className="rb:font-medium rb:flex-1">{vo.name}·{vo.description}</span>
|
||||||
|
|
||||||
<Space size={8}>
|
<Space size={8}>
|
||||||
{vo.required && <span className="rb:py-px rb:px-2 rb:bg-white rb-border rb:rounded-sm">{t('workflow.config.start.required')}</span>}
|
{vo.required && <span className="rb:py-px rb:px-2 rb:bg-white rb-border rb:rounded-sm">{t('workflow.config.start.required')}</span>}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* @Author: ZhaoYing
|
* @Author: ZhaoYing
|
||||||
* @Date: 2026-02-03 15:39:59
|
* @Date: 2026-02-03 15:39:59
|
||||||
* @Last Modified by: ZhaoYing
|
* @Last Modified by: ZhaoYing
|
||||||
* @Last Modified time: 2026-03-24 16:33:32
|
* @Last Modified time: 2026-03-25 15:08:02
|
||||||
*/
|
*/
|
||||||
import { type FC, useEffect, useState, useMemo } from "react";
|
import { type FC, useEffect, useState, useMemo } from "react";
|
||||||
import clsx from 'clsx'
|
import clsx from 'clsx'
|
||||||
@@ -454,7 +454,7 @@ const Properties: FC<PropertiesProps> = ({
|
|||||||
className="rb:h-full! rb:hover:shadow-none!"
|
className="rb:h-full! rb:hover:shadow-none!"
|
||||||
bodyClassName={clsx('rb:overflow-y-auto! rb:h-[calc(100vh-131px)]! rb:px-3! rb:pt-0! rb:pb-3!')}
|
bodyClassName={clsx('rb:overflow-y-auto! rb:h-[calc(100vh-131px)]! rb:px-3! rb:pt-0! rb:pb-3!')}
|
||||||
>
|
>
|
||||||
<Form form={form} size="small" layout="vertical">
|
<Form key={selectedNode?.getData()?.id} form={form} size="small" layout="vertical">
|
||||||
<Form.Item name="name" label={t('workflow.nodeName')}>
|
<Form.Item name="name" label={t('workflow.nodeName')}>
|
||||||
<Input
|
<Input
|
||||||
placeholder={t('common.pleaseEnter')}
|
placeholder={t('common.pleaseEnter')}
|
||||||
@@ -525,7 +525,7 @@ const Properties: FC<PropertiesProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (key === 'model_id' && selectedNode?.data?.type === 'llm') {
|
if (key === 'model_id' && selectedNode?.data?.type === 'llm') {
|
||||||
return <ModelConfig />
|
return <ModelConfig key={key} />
|
||||||
}
|
}
|
||||||
if (selectedNode?.data?.type === 'llm' && key === 'messages' && config.type === 'define') {
|
if (selectedNode?.data?.type === 'llm' && key === 'messages' && config.type === 'define') {
|
||||||
// 为llm节点且isArray=true时添加context变量支持
|
// 为llm节点且isArray=true时添加context变量支持
|
||||||
|
|||||||
Reference in New Issue
Block a user