From b2cee0063e60c7f17590667c52bca13992d47cdc Mon Sep 17 00:00:00 2001 From: joe Date: Sat, 21 Sep 2024 19:45:01 +0800 Subject: [PATCH] =?UTF-8?q?hotfix:=20=E4=BF=AE=E5=A4=8D=E4=B8=80=E7=B3=BB?= =?UTF-8?q?=E5=88=97=E9=97=AE=E9=A2=98=20(#24)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1、fix: 修复transition addItem导致的数据问题 2、fix: 修复code mode数据问题 3、fix: 修复mode切换问题 --- .../app/config-form/button-config/index.tsx | 21 +++++++- .../app/config-form/widget-config/index.tsx | 22 +++++++- .../app/config-form/workflow-config/index.tsx | 25 ++++++++- .../widgets/expression-input/index.tsx | 51 ++++++++++++------- .../widgets/transition-condition-editor.tsx | 20 +++++++- .../app/state-config-sheet/index.tsx | 4 ++ .../components/app/transition-sheet/index.tsx | 9 +--- web/apps/web/src/stores/app/use-app-state.tsx | 2 +- 8 files changed, 122 insertions(+), 32 deletions(-) diff --git a/web/apps/web/src/components/app/config-form/button-config/index.tsx b/web/apps/web/src/components/app/config-form/button-config/index.tsx index f9972757..3d0bf3ae 100644 --- a/web/apps/web/src/components/app/config-form/button-config/index.tsx +++ b/web/apps/web/src/components/app/config-form/button-config/index.tsx @@ -1,24 +1,41 @@ 'use client'; -import { TValues } from '@shellagent/form-engine'; +import { TValues, TFieldMode } from '@shellagent/form-engine'; +import { useCallback } from 'react'; import NodeForm from '@/components/app/node-form'; import { useAppState } from '@/stores/app/use-app-state'; import { buttonConfigSchema } from '@/stores/app/utils/schema'; +import { useAppStore } from '@/stores/app/app-provider'; interface ButtonConfigProps { values: TValues; + id: string; onChange: (values: TValues) => void; } -export const ButtonConfig = ({ values, onChange }: ButtonConfigProps) => { +export const ButtonConfig = ({ values, onChange, id }: ButtonConfigProps) => { + const { setFieldsModeMap, fieldsModeMap } = useAppStore(state => ({ + setFieldsModeMap: state.setFieldsModeMap, + fieldsModeMap: state.config?.fieldsModeMap, + })); const currentButtonId = useAppState(state => state.currentButtonId); + + const onModeChange = useCallback( + (name: string, mode: TFieldMode) => { + setFieldsModeMap({ id: `${id}.${currentButtonId}`, name, mode }); + }, + [currentButtonId, id, setFieldsModeMap], + ); + return ( ); }; diff --git a/web/apps/web/src/components/app/config-form/widget-config/index.tsx b/web/apps/web/src/components/app/config-form/widget-config/index.tsx index 433ec0f9..b4a61d9b 100644 --- a/web/apps/web/src/components/app/config-form/widget-config/index.tsx +++ b/web/apps/web/src/components/app/config-form/widget-config/index.tsx @@ -1,24 +1,35 @@ 'use client'; -import { TValues, getDefaultValueBySchema } from '@shellagent/form-engine'; +import { + TValues, + getDefaultValueBySchema, + TFieldMode, +} from '@shellagent/form-engine'; import { isEmpty, merge } from 'lodash-es'; import { useEffect, useMemo, useCallback } from 'react'; import NodeForm from '@/components/app/node-form'; import { getSchemaByWidget } from '@/stores/app/utils/get-widget-schema'; import { useWorkflowStore } from '@/stores/workflow/workflow-provider'; +import { useAppStore } from '@/stores/app/app-provider'; interface WidgetConfigProps { values: TValues | undefined; parent: string; + id: string; onChange: (values: TValues) => void; } export const WidgetConfig: React.FC = ({ values, parent, + id, onChange, }) => { + const { setFieldsModeMap, fieldsModeMap } = useAppStore(state => ({ + setFieldsModeMap: state.setFieldsModeMap, + fieldsModeMap: state.config?.fieldsModeMap, + })); const { loading, getWidgetSchema, widgetSchema } = useWorkflowStore( state => ({ loading: state.loading.getWidgetSchema, @@ -74,6 +85,13 @@ export const WidgetConfig: React.FC = ({ values, ]); + const onModeChange = useCallback( + (name: string, mode: TFieldMode) => { + setFieldsModeMap({ id: `${id}.${parent}`, name, mode }); + }, + [id, setFieldsModeMap, parent], + ); + if (!values) { return null; } @@ -85,6 +103,8 @@ export const WidgetConfig: React.FC = ({ parent={parent} onChange={handleOnChange} loading={loading?.[values.widget_class_name]} + onModeChange={onModeChange} + modeMap={fieldsModeMap?.[`${id}.${parent}`] || {}} /> ); }; diff --git a/web/apps/web/src/components/app/config-form/workflow-config/index.tsx b/web/apps/web/src/components/app/config-form/workflow-config/index.tsx index 3e255c49..f9cb22dd 100644 --- a/web/apps/web/src/components/app/config-form/workflow-config/index.tsx +++ b/web/apps/web/src/components/app/config-form/workflow-config/index.tsx @@ -1,7 +1,11 @@ 'use client'; import { NodeIdEnum } from '@shellagent/flow-engine'; -import { TValues, getDefaultValueBySchema } from '@shellagent/form-engine'; +import { + TValues, + getDefaultValueBySchema, + TFieldMode, +} from '@shellagent/form-engine'; import { merge } from 'lodash-es'; import { useEffect, useMemo, useCallback } from 'react'; @@ -12,11 +16,15 @@ import { useWorkflowStore } from '@/stores/workflow/workflow-provider'; interface WorkflowConfigProps { values: TValues | undefined; + parent: string; + id: string; onChange: (values: TValues) => void; } export const WorkflowConfig: React.FC = ({ values, + parent, + id, onChange, }) => { const { @@ -29,6 +37,11 @@ export const WorkflowConfig: React.FC = ({ loading: state.loading, })); + const { setFieldsModeMap, fieldsModeMap } = useAppStore(state => ({ + setFieldsModeMap: state.setFieldsModeMap, + fieldsModeMap: state.config?.fieldsModeMap, + })); + const options = useAppStore(state => state.flowList).map(flow => ({ label: flow.metadata?.name, value: `${flow.id}/latest`, @@ -75,6 +88,13 @@ export const WorkflowConfig: React.FC = ({ [defaultValues, onChange], ); + const onModeChange = useCallback( + (name: string, mode: TFieldMode) => { + setFieldsModeMap({ id: `${id}.${parent}`, name, mode }); + }, + [id, setFieldsModeMap, parent], + ); + if (!values) { return null; } @@ -82,10 +102,13 @@ export const WorkflowConfig: React.FC = ({ return ( ); }; diff --git a/web/apps/web/src/components/app/node-form/widgets/expression-input/index.tsx b/web/apps/web/src/components/app/node-form/widgets/expression-input/index.tsx index eb5d9393..66afa359 100644 --- a/web/apps/web/src/components/app/node-form/widgets/expression-input/index.tsx +++ b/web/apps/web/src/components/app/node-form/widgets/expression-input/index.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect } from 'react'; +import React, { useCallback, useEffect, useMemo } from 'react'; import { MentionsInput, OnChangeHandlerFunc, @@ -76,29 +76,44 @@ const ExpressionInput: React.FC = ({ return text; }; + // TODO 如果多个value相同,只能映射到最后一个label const v2l = (value: string) => { - return ( - flatOptions?.reduce((prev, item) => { - const { value, label } = item; - if (value && prev) { - prev = prev.replaceAll( - value, - `@[${label || DEFAULT_LABEL}](value:${value})`, - ); - } - return prev; - }, value) || value - ); + const valueToLabelMap = new Map(); + + // 构建 value 到 label 的映射 + flatOptions.forEach(item => { + const { value: itemValue, label } = item; + if (itemValue) { + valueToLabelMap.set(itemValue, label || DEFAULT_LABEL); + } + }); + + // 替换 value 为对应的 label + valueToLabelMap.forEach((label, itemValue) => { + const regex = new RegExp(`\\b${itemValue}\\b`, 'g'); + // 确保替换时没有重复替换 + value = value.replace(regex, match => { + // 检查是否已经被替换过 + const alreadyReplaced = new RegExp( + `@\\[${label}\\]\\(value:${itemValue}\\)`, + ).test(value); + return alreadyReplaced ? match : `@[${label}](value:${itemValue})`; + }); + }); + + return value; }; + const l2v = (label: string) => { const str = flatOptions?.reduce((prev, item) => { - const { value, label } = item; + const { value, label: itemLabel } = item; if (value && prev) { - prev = prev.replaceAll( - `@[${label || DEFAULT_LABEL}](value:${value})`, - value || '', + const regex = new RegExp( + `@\\[${itemLabel || DEFAULT_LABEL}\\]\\(value:${value}\\)`, + 'g', ); + prev = prev.replace(regex, value || ''); } return prev; }, label) || label; @@ -124,7 +139,7 @@ const ExpressionInput: React.FC = ({ onChange?.(e); }; - const newValue = value ? v2l(value) : value; + const newValue = useMemo(() => (value ? v2l(value) : value), [value]); const mentions = flatOptions?.reduce((memo, cur) => { diff --git a/web/apps/web/src/components/app/node-form/widgets/transition-condition-editor.tsx b/web/apps/web/src/components/app/node-form/widgets/transition-condition-editor.tsx index 7ddd6505..26a6af0d 100644 --- a/web/apps/web/src/components/app/node-form/widgets/transition-condition-editor.tsx +++ b/web/apps/web/src/components/app/node-form/widgets/transition-condition-editor.tsx @@ -5,9 +5,10 @@ import { ArrowRightIcon, } from '@heroicons/react/24/outline'; import { useReactFlowStore, NodeTypeEnum } from '@shellagent/flow-engine'; +import { TFieldMode } from '@shellagent/form-engine'; import { Button, Input, Select, IconButton, Drawer } from '@shellagent/ui'; import { produce } from 'immer'; -import { useRef, useState } from 'react'; +import { useRef, useState, useCallback } from 'react'; import { ICondition } from '@/components/app/edges'; import NodeForm from '@/components/app/node-form'; @@ -117,7 +118,11 @@ const TransitionConditionEditor = ({ setOpen: state.setTargetInputsSheetOpen, })); - const nodeData = useAppStore(state => state.nodeData); + const { nodeData, setFieldsModeMap, fieldsModeMap } = useAppStore(state => ({ + nodeData: state.nodeData, + setFieldsModeMap: state.setFieldsModeMap, + fieldsModeMap: state.config?.fieldsModeMap, + })); const handleOpen = (open: boolean, index: number) => { setOpen(open); @@ -167,6 +172,15 @@ const TransitionConditionEditor = ({ })); }; + const modeId = `${source}.condition.${index}`; + + const onModeChange = useCallback( + (name: string, mode: TFieldMode) => { + setFieldsModeMap({ id: modeId, name, mode }); + }, + [modeId, setFieldsModeMap], + ); + return (
{value?.map?.((condition, index) => ( @@ -204,6 +218,8 @@ const TransitionConditionEditor = ({ parent={`condition.${index}`} schema={schema} values={value?.[index]?.target_inputs} + onModeChange={onModeChange} + modeMap={fieldsModeMap?.[modeId] || {}} onChange={values => onChange( produce(value, draft => { diff --git a/web/apps/web/src/components/app/state-config-sheet/index.tsx b/web/apps/web/src/components/app/state-config-sheet/index.tsx index c8c1bb00..34e80e28 100644 --- a/web/apps/web/src/components/app/state-config-sheet/index.tsx +++ b/web/apps/web/src/components/app/state-config-sheet/index.tsx @@ -99,6 +99,7 @@ const StateConfigSheet: React.FC<{}> = () => { return { children: ( nodeFormRef.current?.setValue( @@ -119,7 +120,9 @@ const StateConfigSheet: React.FC<{}> = () => { return { children: ( @@ -140,6 +143,7 @@ const StateConfigSheet: React.FC<{}> = () => { return { children: ( = () => { id: generateUUID(), custom: true, event_key: generateUUID(), - type: EdgeDataTypeEnum.CHAT, + type: currentEdegData.type, source: newEdge.source, target: newEdge.target, conditions: [ diff --git a/web/apps/web/src/stores/app/use-app-state.tsx b/web/apps/web/src/stores/app/use-app-state.tsx index 1adec489..2c00bf51 100644 --- a/web/apps/web/src/stores/app/use-app-state.tsx +++ b/web/apps/web/src/stores/app/use-app-state.tsx @@ -63,7 +63,7 @@ const initialState: State = { id: '', custom: true, event_key: '', - type: EdgeDataTypeEnum.CHAT, + type: EdgeDataTypeEnum.ALWAYS, target: '', conditions: [], },