Skip to content

Commit 89c4e26

Browse files
authored
[elsa] refactor(NodeState): unify intelligentForm and manualCheck node states (#191)
1 parent 3f9e4b6 commit 89c4e26

File tree

15 files changed

+414
-157
lines changed

15 files changed

+414
-157
lines changed

framework/elsa/fit-elsa-react/src/components/intelligentForm/Consts.js

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,43 @@
55
*--------------------------------------------------------------------------------------------*/
66

77
import {RENDER_TYPE} from '@/common/Consts.js';
8+
import {v4 as uuidv4} from 'uuid';
9+
import {DATA_TYPES, FROM_TYPE} from '@/common/Consts.js';
810

9-
export const RENDER_OPTIONS_TYPE = new Set([RENDER_TYPE.RADIO, RENDER_TYPE.SELECT, RENDER_TYPE.CHECK_BOX]);
11+
export const RENDER_OPTIONS_TYPE = new Set([RENDER_TYPE.RADIO, RENDER_TYPE.SELECT, RENDER_TYPE.CHECK_BOX]);
12+
13+
export const FORM_TYPE = {
14+
ORCHESTRATION: 'orchestration',
15+
MANUAL: 'manual',
16+
};
17+
18+
export const ORCHESTRATION_INIT_ENTITY = {
19+
inputParams: [{
20+
id: uuidv4(),
21+
name: 'data',
22+
type: DATA_TYPES.OBJECT,
23+
from: FROM_TYPE.EXPAND,
24+
value: [],
25+
}, {
26+
id: uuidv4(),
27+
name: 'schema',
28+
type: DATA_TYPES.OBJECT,
29+
from: FROM_TYPE.INPUT,
30+
value: {
31+
parameters: [],
32+
},
33+
}],
34+
outputParams: [{
35+
id: uuidv4(),
36+
name: 'output',
37+
type: DATA_TYPES.OBJECT,
38+
value: [],
39+
}],
40+
};
41+
42+
export const MANUAL_INIT_ENTITY = {
43+
inputParams: [],
44+
outputParams: [],
45+
};
46+
47+
export const ORCHESTRATION_TASK_ID = 'a910a3d38a4549eda1112beee008419d';

framework/elsa/fit-elsa-react/src/components/intelligentForm/IntelligentFormWrapper.jsx

Lines changed: 25 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,13 @@
44
* Licensed under the MIT License. See License.txt in the project root for license information.
55
*--------------------------------------------------------------------------------------------*/
66

7-
import {useLayoutEffect, useState} from 'react';
8-
import {Button, Collapse} from 'antd';
9-
import {DeleteOutlined, PlusOutlined} from '@ant-design/icons';
10-
import {useConfigContext, useDispatch, useShapeContext} from '@/components/DefaultRoot.jsx';
11-
import {v4 as uuidv4} from 'uuid';
7+
import {Radio} from 'antd';
8+
import {useDispatch} from '@/components/DefaultRoot.jsx';
129
import PropTypes from 'prop-types';
10+
import SimpleFormWrapper from '@/components/intelligentForm/SimpleFormWrapper.jsx';
11+
import ManualCheckFormWrapper from '@/components/manualCheck/ManualCheckFormWrapper.jsx';
1312
import {useTranslation} from 'react-i18next';
14-
import {InvokeOutput} from '@/components/common/InvokeOutput.jsx';
15-
import {IntelligentInputFormItem} from '@/components/intelligentForm/IntelligentInputFormItem.jsx';
16-
import {JadeCollapse} from '@/components/common/JadeCollapse.jsx';
17-
18-
const {Panel} = Collapse;
13+
import {FORM_TYPE} from '@/components/intelligentForm/Consts.js';
1914

2015
IntelligentFormWrapper.propTypes = {
2116
data: PropTypes.object.isRequired,
@@ -32,111 +27,34 @@ IntelligentFormWrapper.propTypes = {
3227
export default function IntelligentFormWrapper({data, shapeStatus}) {
3328
const {t} = useTranslation();
3429
const dispatch = useDispatch();
35-
const shape = useShapeContext();
36-
const isConfig = useConfigContext();
37-
const items = data.converter.entity.inputParams.find(
38-
(item) => item.name === 'schema'
39-
)?.value.parameters;
40-
const output = data.converter.entity.outputParams.find((item) => item.name === 'output');
41-
42-
// items中所有初始都为打开状态
43-
const [openItems, setOpenItems] = useState(() => {
44-
return isConfig ? items.map(item => item.id) : [];
45-
});
46-
47-
// 智能表单节点使用JadeInput,但输出的地方外层需要套一个output,在此处把output注册
48-
useLayoutEffect(() => {
49-
shape.page.registerObservable({
50-
nodeId: shape.id,
51-
observableId: output.id,
52-
value: output.name,
53-
type: output.type,
54-
parentId: undefined,
55-
});
56-
}, []);
5730

58-
const outputDataConvert = (items) => {
59-
const outputData = JSON.parse(JSON.stringify(data.converter.entity.outputParams)); // 手动深度拷贝
60-
const outputItem = outputData.find((item) => item.name === 'output');
61-
if (outputItem) {
62-
outputItem.value = [...items]; // 创建新数组
63-
}
64-
return outputData;
65-
};
66-
67-
// 添加新元素到 items 数组中,并将其 key 添加到当前展开的面板数组中
68-
const addItem = () => {
69-
// 智能表单节点入参最大数量为30
70-
if (items.length < 30) {
71-
const newItemId = 'input_' + uuidv4();
72-
if (isConfig) {
73-
setOpenItems([...openItems, newItemId]); // 将新元素 key 添加到 openItems 数组中
74-
}
75-
dispatch({type: 'addParam', id: newItemId});
76-
}
31+
const changeFormType = (e) => {
32+
dispatch({type: 'changeFormType', value: e.target.value});
7733
};
7834

79-
const renderAddInputIcon = () => {
80-
return (<>
81-
<Button disabled={shapeStatus.disabled}
82-
type='text'
83-
className='icon-button jade-start-add-icon'
84-
onClick={addItem}>
85-
<PlusOutlined/>
86-
</Button>
87-
</>);
88-
};
89-
90-
const renderDeleteIcon = (item) => {
91-
return (<>
92-
<Button
93-
disabled={shapeStatus.disabled}
94-
type='text'
95-
className='icon-button start-node-delete-icon-button'
96-
onClick={() => handleDelete(item.id)}>
97-
<DeleteOutlined/>
98-
</Button>
99-
</>);
100-
};
101-
102-
const handleDelete = (itemId) => {
103-
const updatedOpenItems = openItems.filter((key) => key !== itemId);
104-
setOpenItems(updatedOpenItems);
105-
dispatch({type: 'deleteParam', id: itemId});
35+
const getFormTypeOptions = () => {
36+
return [{label: t('orchestration'), value: FORM_TYPE.ORCHESTRATION}, {label: t('manual'), value: FORM_TYPE.MANUAL}];
10637
};
10738

10839
return (<>
10940
<div>
110-
<div style={{display: 'flex', alignItems: 'center', marginBottom: '8px', paddingLeft: '8px', paddingRight: '4px', height: '32px'}}>
111-
<div className='jade-panel-header-font'>{t('formItem')}</div>
112-
{renderAddInputIcon()}
41+
<div style={{fontSize: '16px', fontFamily: 'Huawei Sans', fontWeight: 400, display: 'flex', paddingBottom: '8px'}}>
42+
<span>{t('type')}</span>
43+
<Radio.Group
44+
value={data.formType}
45+
onChange={changeFormType}
46+
options={getFormTypeOptions()}
47+
buttonStyle="solid"
48+
style={{fontFamily: 'Huawei Sans', paddingLeft: '8px'}}
49+
/>
11350
</div>
114-
<JadeCollapse
115-
activeKey={openItems}
116-
onChange={(keys) => setOpenItems(keys)}
117-
style={{backgroundColor: 'transparent'}}>
118-
{
119-
items.map((item) => (
120-
<Panel
121-
key={item.id}
122-
header={
123-
<div className="panel-header">
124-
<span className="jade-panel-header-font">{item.name}</span> {/* 显示Name值的元素 */}
125-
{renderDeleteIcon(item)}
126-
</div>
127-
}
128-
className="jade-panel"
129-
style={{marginBottom: 8, borderRadius: '8px', width: '100%'}}
130-
forceRender
131-
>
132-
<div className={'jade-custom-panel-content'}>
133-
<IntelligentInputFormItem item={item} items={items} shapeStatus={shapeStatus} output={output}/>
134-
</div>
135-
</Panel>
136-
))
137-
}
138-
</JadeCollapse>
139-
<InvokeOutput outputData={outputDataConvert(items)} isObservableTree={false}/>
51+
{
52+
data.formType === FORM_TYPE.ORCHESTRATION ? (
53+
<SimpleFormWrapper data={data} shapeStatus={shapeStatus}/>
54+
) : (
55+
<ManualCheckFormWrapper data={data} shapeStatus={shapeStatus}/>
56+
)
57+
}
14058
</div>
14159
</>);
14260
}
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
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 {useLayoutEffect, useState} from 'react';
8+
import {Button, Collapse} from 'antd';
9+
import {DeleteOutlined, PlusOutlined} from '@ant-design/icons';
10+
import {useConfigContext, useDispatch, useShapeContext} from '@/components/DefaultRoot.jsx';
11+
import {v4 as uuidv4} from 'uuid';
12+
import PropTypes from 'prop-types';
13+
import {useTranslation} from 'react-i18next';
14+
import {InvokeOutput} from '@/components/common/InvokeOutput.jsx';
15+
import {IntelligentInputFormItem} from '@/components/intelligentForm/IntelligentInputFormItem.jsx';
16+
import {JadeCollapse} from '@/components/common/JadeCollapse.jsx';
17+
18+
const {Panel} = Collapse;
19+
20+
SimpleFormWrapper.propTypes = {
21+
data: PropTypes.object.isRequired,
22+
shapeStatus: PropTypes.object,
23+
};
24+
25+
/**
26+
* 智能表单Wrapper
27+
*
28+
* @param data 数据.
29+
* @param shapeStatus 图形状态.
30+
* @returns {JSX.Element} 智能表单Wrapper的DOM
31+
*/
32+
export default function SimpleFormWrapper({data, shapeStatus}) {
33+
const {t} = useTranslation();
34+
const dispatch = useDispatch();
35+
const shape = useShapeContext();
36+
const isConfig = useConfigContext();
37+
const items = data.converter.entity.inputParams.find(
38+
(item) => item.name === 'schema'
39+
)?.value.parameters;
40+
const output = data.converter.entity.outputParams.find((item) => item.name === 'output');
41+
42+
// items中所有初始都为打开状态
43+
const [openItems, setOpenItems] = useState(() => {
44+
return isConfig ? items.map(item => item.id) : [];
45+
});
46+
47+
// 智能表单节点使用JadeInput,但输出的地方外层需要套一个output,在此处把output注册
48+
useLayoutEffect(() => {
49+
shape.page.registerObservable({
50+
nodeId: shape.id,
51+
observableId: output.id,
52+
value: output.name,
53+
type: output.type,
54+
parentId: undefined,
55+
});
56+
}, []);
57+
58+
const outputDataConvert = (items) => {
59+
const outputData = JSON.parse(JSON.stringify(data.converter.entity.outputParams)); // 手动深度拷贝
60+
const outputItem = outputData.find((item) => item.name === 'output');
61+
if (outputItem) {
62+
outputItem.value = [...items]; // 创建新数组
63+
}
64+
return outputData;
65+
};
66+
67+
// 添加新元素到 items 数组中,并将其 key 添加到当前展开的面板数组中
68+
const addItem = () => {
69+
// 智能表单节点入参最大数量为30
70+
if (items.length < 30) {
71+
const newItemId = 'input_' + uuidv4();
72+
if (isConfig) {
73+
setOpenItems([...openItems, newItemId]); // 将新元素 key 添加到 openItems 数组中
74+
}
75+
dispatch({type: 'addParam', id: newItemId});
76+
}
77+
};
78+
79+
const renderAddInputIcon = () => {
80+
return (<>
81+
<Button disabled={shapeStatus.disabled}
82+
type='text'
83+
className='icon-button jade-start-add-icon'
84+
onClick={addItem}>
85+
<PlusOutlined/>
86+
</Button>
87+
</>);
88+
};
89+
90+
const renderDeleteIcon = (item) => {
91+
return (<>
92+
<Button
93+
disabled={shapeStatus.disabled}
94+
type='text'
95+
className='icon-button start-node-delete-icon-button'
96+
onClick={() => handleDelete(item.id)}>
97+
<DeleteOutlined/>
98+
</Button>
99+
</>);
100+
};
101+
102+
const handleDelete = (itemId) => {
103+
const updatedOpenItems = openItems.filter((key) => key !== itemId);
104+
setOpenItems(updatedOpenItems);
105+
dispatch({type: 'deleteParam', id: itemId});
106+
};
107+
108+
return (<>
109+
<div>
110+
<div style={{display: 'flex', alignItems: 'center', marginBottom: '8px', paddingLeft: '8px', paddingRight: '4px', height: '32px'}}>
111+
<div className='jade-panel-header-font'>{t('formItem')}</div>
112+
{renderAddInputIcon()}
113+
</div>
114+
<JadeCollapse
115+
activeKey={openItems}
116+
onChange={(keys) => setOpenItems(keys)}
117+
style={{backgroundColor: 'transparent'}}>
118+
{
119+
items.map((item) => (
120+
<Panel
121+
key={item.id}
122+
header={
123+
<div className="panel-header">
124+
<span className="jade-panel-header-font">{item.name}</span> {/* 显示Name值的元素 */}
125+
{renderDeleteIcon(item)}
126+
</div>
127+
}
128+
className="jade-panel"
129+
style={{marginBottom: 8, borderRadius: '8px', width: '100%'}}
130+
forceRender
131+
>
132+
<div className={'jade-custom-panel-content'}>
133+
<IntelligentInputFormItem item={item} items={items} shapeStatus={shapeStatus} output={output}/>
134+
</div>
135+
</Panel>
136+
))
137+
}
138+
</JadeCollapse>
139+
<InvokeOutput outputData={outputDataConvert(items)} isObservableTree={false}/>
140+
</div>
141+
</>);
142+
}

0 commit comments

Comments
 (0)