fix(web): workflow editor bug

This commit is contained in:
zhaoying
2026-03-25 15:40:12 +08:00
parent 49364802c2
commit edd115582f
7 changed files with 80 additions and 74 deletions

View File

@@ -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>

View File

@@ -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>

View File

@@ -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;
}; };

View File

@@ -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')}

View File

@@ -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')}

View File

@@ -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>}

View File

@@ -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变量支持