Skip to content

Commit f76ae46

Browse files
authored
[elsa] add text concatenate node (#300)
1 parent 3cbc375 commit f76ae46

File tree

13 files changed

+644
-4
lines changed

13 files changed

+644
-4
lines changed

framework/elsa/fit-elsa-react/src/App.jsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ function App({i18n}) {
6868
llmModelEndpoint: '',
6969
},
7070
});
71+
configs.push({
72+
node: 'textConcatenateNodeState'
73+
});
7174

7275
JadeFlow.edit({
7376
div: stage,
@@ -241,6 +244,7 @@ function App({i18n}) {
241244
<Button onClick={() => window.agent.createNodeByPosition('textExtractionNodeState', {x:100, y:100}, {uniqueName : ''})}>创建文本提取节点</Button>
242245
<Button onClick={() => window.agent.createNodeByPosition('queryOptimizationNodeState', {x:100, y:100}, {uniqueName : ''})}>创建问题优化节点</Button>
243246
<Button onClick={() => window.agent.createNodeByPosition('codeNodeState', {x:100, y:100}, {uniqueName : ''})}>创建code节点</Button>
247+
<Button onClick={() => window.agent.createNodeByPosition('textConcatenateNodeState', {x:100, y:100}, {uniqueName : ''})}>创建文本拼接节点</Button>
244248
<Button onClick={() => {
245249
const nodeTypes = ['endNodeEnd',
246250
'retrievalNodeState',
Lines changed: 14 additions & 0 deletions
Loading
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved.
3+
* This file is a part of the ModelEngine Project.
4+
* Licensed under the MIT License. See License.txt in the project root for license information.
5+
*--------------------------------------------------------------------------------------------*/
6+
7+
import {Button, Col, Collapse, Form, Popover, Row} from 'antd';
8+
import {useFormContext, useShapeContext} from '@/components/DefaultRoot.jsx';
9+
import PropTypes from 'prop-types';
10+
import {Trans, useTranslation} from 'react-i18next';
11+
import React, {useState} from 'react';
12+
import FullScreenIcon from '../asserts/icon-full-screen.svg?react';
13+
import {JadeCollapse} from '@/components/common/JadeCollapse.jsx';
14+
import {PromptDrawer} from "@/components/common/prompt/PromptDrawer.jsx";
15+
import TextArea from "antd/es/input/TextArea.js";
16+
import {QuestionCircleOutlined} from "@ant-design/icons";
17+
import * as Proptypes from "prop-types";
18+
19+
const {Panel} = Collapse;
20+
21+
/**
22+
* 文本模板组件。
23+
*
24+
* @param name 表单名称
25+
* @param title 标题文本
26+
* @param placeHolder 输入框占位符文本
27+
* @param template 文字模板对象
28+
* @param labelName 标签名称
29+
* @param onChange 内容变化时的回调函数
30+
* @param disabled 是否禁用输入框
31+
* @param maxLength 输入内容的最大长度限制
32+
* @returns {Element} 返回文本模板组件的JSX元素
33+
* @constructor
34+
*/
35+
const _TemplatePanel = ({name, title, placeHolder, template, labelName, onChange, disabled, maxLength}) => {
36+
const shape = useShapeContext();
37+
const {t} = useTranslation();
38+
const form = useFormContext();
39+
40+
const [promptOpen, setPromptOpen] = useState(false);
41+
42+
const promptContent = (<div className={'jade-font-size'} style={{lineHeight: '1.2'}}>
43+
<Trans i18nKey='promptPopover' components={{p: <p/>}}/>
44+
</div>);
45+
46+
const _onChange = (templateText) => {
47+
onChange(templateText);
48+
form.setFieldsValue({[name]: templateText});
49+
};
50+
51+
/**
52+
* 失焦时才设置值,对于必填项.若为空,则不设置
53+
*
54+
* @param e event事件
55+
*/
56+
const changeOnBlur = (e) => {
57+
if (template.value !== e.target.value) {
58+
_onChange(e.target.value);
59+
}
60+
};
61+
62+
return (<JadeCollapse defaultActiveKey={['templatePanel']}>
63+
<Panel
64+
key="templatePanel"
65+
header={<>
66+
<div className={'required-after'} style={{display: 'flex', alignItems: 'center', width: '100%'}}>
67+
<span className='jade-second-title'>{title}</span>
68+
{promptContent && <Popover
69+
content={[promptContent]}
70+
align={{offset: [0, 3]}}
71+
overlayClassName={'jade-custom-popover'}
72+
>
73+
<QuestionCircleOutlined className='jade-panel-header-popover-content'/>
74+
</Popover>}
75+
<div className={'prompt-title-buttons'}>
76+
<Button
77+
disabled={disabled}
78+
type='text'
79+
className='icon-button'
80+
style={{height: '100%'}}
81+
onClick={() => setPromptOpen(true)}>
82+
<FullScreenIcon/>
83+
</Button>
84+
</div>
85+
</div>
86+
</>}
87+
className="jade-panel"
88+
>
89+
<div className="jade-custom-panel-content">
90+
<Row gutter={16}>
91+
<Col span={24}>
92+
<div className={'prompt-container'}>
93+
<Form.Item
94+
className='jade-form-item'
95+
name={name}
96+
rules={[{required: true, message: t('paramCannotBeEmpty')}]}
97+
initialValue={template.value}
98+
validateTrigger='onBlur'
99+
>
100+
<TextArea
101+
disabled={disabled}
102+
maxLength={2000}
103+
className='jade-textarea-input jade-font-size'
104+
onBlur={(e) => changeOnBlur(e)}
105+
placeholder={placeHolder}
106+
/>
107+
</Form.Item>
108+
<PromptDrawer
109+
value={template.value}
110+
name={name}
111+
title={title}
112+
rules={[{required: true, message: t('paramCannotBeEmpty')}]}
113+
placeHolder={placeHolder}
114+
container={shape.page.graph.div.parentElement}
115+
open={promptOpen}
116+
onClose={() => setPromptOpen(false)}
117+
onConfirm={(v) => _onChange(v)}
118+
labelName={labelName}
119+
maxLength={maxLength}
120+
/>
121+
</div>
122+
</Col>
123+
</Row>
124+
</div>
125+
</Panel>
126+
</JadeCollapse>);
127+
};
128+
129+
_TemplatePanel.propTypes = {
130+
name: PropTypes.string.isRequired,
131+
title: PropTypes.string.isRequired,
132+
placeHolder: PropTypes.string.isRequired,
133+
template: PropTypes.object.isRequired,
134+
labelName: PropTypes.string.isRequired,
135+
onChange: Proptypes.func.isRequired,
136+
disabled: PropTypes.bool,
137+
maxLength: PropTypes.number,
138+
};
139+
140+
const areEqual = (prevProps, nextProps) => {
141+
return prevProps.template === nextProps.template;
142+
};
143+
144+
export const TemplatePanel = React.memo(_TemplatePanel, areEqual);

framework/elsa/fit-elsa-react/src/components/common/prompt/Prompt.jsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import './prompt.css';
1111
import PropTypes from 'prop-types';
1212
import {useFormContext, useShapeContext} from '@/components/DefaultRoot.jsx';
1313
import {PromptDrawer} from '@/components/common/prompt/PromptDrawer.jsx';
14+
import {useTranslation} from "react-i18next";
1415

1516
const {TextArea} = Input;
1617

@@ -39,6 +40,7 @@ export const Prompt = (
3940
}) => {
4041
const shape = useShapeContext();
4142
const form = useFormContext();
43+
const {t} = useTranslation();
4244
const [promptContent, setPromptContent] = useState(prompt.value);
4345

4446
const _onChange = (promptText) => {
@@ -120,7 +122,9 @@ export const Prompt = (
120122
onClose={() => setOpen(false)}
121123
onConfirm={(v) => _onChange(v)}
122124
allowAIGenerate={allowAIGenerate}
123-
onAIGenerate={onAIGenerate}/>
125+
onAIGenerate={onAIGenerate}
126+
labelName={t('promptName')}
127+
/>
124128
</div>
125129
</Col>
126130
</Row>

framework/elsa/fit-elsa-react/src/components/common/prompt/PromptDrawer.jsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ const _PromptDrawer = (
4747
allowAIGenerate,
4848
onAIGenerate,
4949
maxLength = 2000,
50+
labelName
5051
}) => {
5152
const {t} = useTranslation();
5253
const form = useFormContext();
@@ -106,7 +107,7 @@ const _PromptDrawer = (
106107
className='jade-form-item'
107108
label={
108109
<div style={{display: 'flex', alignItems: 'center', width: '100%'}}>
109-
<span>{t('promptName')}</span>
110+
<span>{labelName}</span>
110111
{allowAIGenerate && <div className='jade-prompt-drawer-btn' onClick={onAIGenerate((promptText) => {
111112
if (promptText === '') {
112113
return;
@@ -154,6 +155,7 @@ _PromptDrawer.propTypes = {
154155
allowAIGenerate: PropTypes.bool,
155156
onAIGenerate: PropTypes.func,
156157
maxLength: PropTypes.number,
158+
labelName: PropTypes.string,
157159
};
158160

159161
const areEqual = (prevProps, nextProps) => {

0 commit comments

Comments
 (0)