### What problem does this PR solve? feat: add MessageForm #918 ### Type of change - [x] New Feature (non-breaking change which adds functionality)tags/v0.8.0
| @@ -552,6 +552,10 @@ The above is the content you need to summarize.`, | |||
| description: 'Description', | |||
| examples: 'Examples', | |||
| to: 'To', | |||
| message: 'Messages', | |||
| messagePlaceholder: 'message', | |||
| messageMsg: 'Please input message or delete this field.', | |||
| addField: 'Add field', | |||
| }, | |||
| footer: { | |||
| profile: 'All rights reserved @ React', | |||
| @@ -513,6 +513,10 @@ export default { | |||
| description: '描述', | |||
| examples: '範例', | |||
| to: '到', | |||
| message: '訊息', | |||
| messagePlaceholder: '訊息', | |||
| messageMsg: '請輸入訊息或刪除此欄位。', | |||
| addField: '新增字段', | |||
| }, | |||
| footer: { | |||
| profile: '“保留所有權利 @ react”', | |||
| @@ -531,6 +531,10 @@ export default { | |||
| description: '描述', | |||
| examples: '示例', | |||
| to: '到', | |||
| message: '消息', | |||
| messagePlaceholder: '消息', | |||
| messageMsg: '请输入消息或删除此字段。', | |||
| addField: '新增字段', | |||
| }, | |||
| footer: { | |||
| profile: 'All rights reserved @ React', | |||
| @@ -1,6 +1,7 @@ | |||
| import { | |||
| DatabaseOutlined, | |||
| MergeCellsOutlined, | |||
| MessageOutlined, | |||
| RocketOutlined, | |||
| SendOutlined, | |||
| SlidersOutlined, | |||
| @@ -12,6 +13,7 @@ export enum Operator { | |||
| Generate = 'Generate', | |||
| Answer = 'Answer', | |||
| Categorize = 'Categorize', | |||
| Message = 'Message', | |||
| } | |||
| export const operatorIconMap = { | |||
| @@ -20,6 +22,7 @@ export const operatorIconMap = { | |||
| [Operator.Answer]: SendOutlined, | |||
| [Operator.Begin]: SlidersOutlined, | |||
| [Operator.Categorize]: DatabaseOutlined, | |||
| [Operator.Message]: MessageOutlined, | |||
| }; | |||
| export const operatorMap = { | |||
| @@ -30,6 +33,7 @@ export const operatorMap = { | |||
| [Operator.Answer]: { description: 'Answer description' }, | |||
| [Operator.Begin]: { description: 'Begin description' }, | |||
| [Operator.Categorize]: { description: 'Categorize description' }, | |||
| [Operator.Message]: { description: 'Message description' }, | |||
| }; | |||
| export const componentMenuList = [ | |||
| @@ -49,6 +53,10 @@ export const componentMenuList = [ | |||
| name: Operator.Categorize, | |||
| description: operatorMap[Operator.Categorize].description, | |||
| }, | |||
| { | |||
| name: Operator.Message, | |||
| description: operatorMap[Operator.Message].description, | |||
| }, | |||
| ]; | |||
| export const initialRetrievalValues = { | |||
| @@ -106,6 +114,7 @@ export const RestrictedUpstreamMap = { | |||
| [Operator.Answer]: [], | |||
| [Operator.Retrieval]: [], | |||
| [Operator.Generate]: [], | |||
| [Operator.Message]: [], | |||
| }; | |||
| export const NodeMap = { | |||
| @@ -114,4 +123,5 @@ export const NodeMap = { | |||
| [Operator.Retrieval]: 'ragNode', | |||
| [Operator.Generate]: 'ragNode', | |||
| [Operator.Answer]: 'ragNode', | |||
| [Operator.Message]: 'ragNode', | |||
| }; | |||
| @@ -8,6 +8,7 @@ import CategorizeForm from '../categorize-form'; | |||
| import { Operator } from '../constant'; | |||
| import GenerateForm from '../generate-form'; | |||
| import { useHandleFormValuesChange } from '../hooks'; | |||
| import MessageForm from '../message-form'; | |||
| import RetrievalForm from '../retrieval-form'; | |||
| interface IProps { | |||
| @@ -20,6 +21,7 @@ const FormMap = { | |||
| [Operator.Generate]: GenerateForm, | |||
| [Operator.Answer]: AnswerForm, | |||
| [Operator.Categorize]: CategorizeForm, | |||
| [Operator.Message]: MessageForm, | |||
| }; | |||
| const FlowDrawer = ({ | |||
| @@ -0,0 +1,16 @@ | |||
| .dynamicDeleteButton { | |||
| position: relative; | |||
| top: 4px; | |||
| margin: 0 8px; | |||
| color: #999; | |||
| font-size: 24px; | |||
| cursor: pointer; | |||
| transition: all 0.3s; | |||
| &:hover { | |||
| color: #777; | |||
| } | |||
| &[disabled] { | |||
| cursor: not-allowed; | |||
| opacity: 0.5; | |||
| } | |||
| } | |||
| @@ -1,44 +1,85 @@ | |||
| import { useTranslate } from '@/hooks/commonHooks'; | |||
| import type { FormProps } from 'antd'; | |||
| import { Form, Input } from 'antd'; | |||
| import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons'; | |||
| import { Button, Form, Input } from 'antd'; | |||
| import { IOperatorForm } from '../interface'; | |||
| type FieldType = { | |||
| prologue?: string; | |||
| }; | |||
| import styles from './index.less'; | |||
| const onFinish: FormProps<FieldType>['onFinish'] = (values) => { | |||
| console.log('Success:', values); | |||
| const formItemLayout = { | |||
| labelCol: { | |||
| sm: { span: 6 }, | |||
| }, | |||
| wrapperCol: { | |||
| sm: { span: 18 }, | |||
| }, | |||
| }; | |||
| const onFinishFailed: FormProps<FieldType>['onFinishFailed'] = (errorInfo) => { | |||
| console.log('Failed:', errorInfo); | |||
| const formItemLayoutWithOutLabel = { | |||
| wrapperCol: { | |||
| sm: { span: 18, offset: 6 }, | |||
| }, | |||
| }; | |||
| const MessageForm = ({ onValuesChange, form }: IOperatorForm) => { | |||
| const { t } = useTranslate('chat'); | |||
| const { t } = useTranslate('flow'); | |||
| return ( | |||
| <Form | |||
| name="basic" | |||
| labelCol={{ span: 8 }} | |||
| wrapperCol={{ span: 16 }} | |||
| style={{ maxWidth: 600 }} | |||
| {...formItemLayoutWithOutLabel} | |||
| initialValues={{ remember: true }} | |||
| onFinish={onFinish} | |||
| onFinishFailed={onFinishFailed} | |||
| onValuesChange={onValuesChange} | |||
| autoComplete="off" | |||
| form={form} | |||
| > | |||
| <Form.Item<FieldType> | |||
| name={'prologue'} | |||
| label={t('setAnOpener')} | |||
| tooltip={t('setAnOpenerTip')} | |||
| initialValue={t('setAnOpenerInitial')} | |||
| > | |||
| <Input.TextArea autoSize={{ minRows: 5 }} /> | |||
| </Form.Item> | |||
| <Form.List name="messages"> | |||
| {(fields, { add, remove }, {}) => ( | |||
| <> | |||
| {fields.map((field, index) => ( | |||
| <Form.Item | |||
| {...(index === 0 ? formItemLayout : formItemLayoutWithOutLabel)} | |||
| label={index === 0 ? t('message') : ''} | |||
| required={false} | |||
| key={field.key} | |||
| > | |||
| <Form.Item | |||
| {...field} | |||
| validateTrigger={['onChange', 'onBlur']} | |||
| rules={[ | |||
| { | |||
| required: true, | |||
| whitespace: true, | |||
| message: t('messageMsg'), | |||
| }, | |||
| ]} | |||
| noStyle | |||
| > | |||
| <Input | |||
| placeholder={t('messagePlaceholder')} | |||
| style={{ width: '80%' }} | |||
| /> | |||
| </Form.Item> | |||
| {fields.length > 1 ? ( | |||
| <MinusCircleOutlined | |||
| className={styles.dynamicDeleteButton} | |||
| onClick={() => remove(field.name)} | |||
| /> | |||
| ) : null} | |||
| </Form.Item> | |||
| ))} | |||
| <Form.Item> | |||
| <Button | |||
| type="dashed" | |||
| onClick={() => add()} | |||
| style={{ width: '80%' }} | |||
| icon={<PlusOutlined />} | |||
| > | |||
| {t('addField')} | |||
| </Button> | |||
| </Form.Item> | |||
| </> | |||
| )} | |||
| </Form.List> | |||
| </Form> | |||
| ); | |||
| }; | |||