### 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
| description: 'Description', | description: 'Description', | ||||
| examples: 'Examples', | examples: 'Examples', | ||||
| to: 'To', | to: 'To', | ||||
| message: 'Messages', | |||||
| messagePlaceholder: 'message', | |||||
| messageMsg: 'Please input message or delete this field.', | |||||
| addField: 'Add field', | |||||
| }, | }, | ||||
| footer: { | footer: { | ||||
| profile: 'All rights reserved @ React', | profile: 'All rights reserved @ React', |
| description: '描述', | description: '描述', | ||||
| examples: '範例', | examples: '範例', | ||||
| to: '到', | to: '到', | ||||
| message: '訊息', | |||||
| messagePlaceholder: '訊息', | |||||
| messageMsg: '請輸入訊息或刪除此欄位。', | |||||
| addField: '新增字段', | |||||
| }, | }, | ||||
| footer: { | footer: { | ||||
| profile: '“保留所有權利 @ react”', | profile: '“保留所有權利 @ react”', |
| description: '描述', | description: '描述', | ||||
| examples: '示例', | examples: '示例', | ||||
| to: '到', | to: '到', | ||||
| message: '消息', | |||||
| messagePlaceholder: '消息', | |||||
| messageMsg: '请输入消息或删除此字段。', | |||||
| addField: '新增字段', | |||||
| }, | }, | ||||
| footer: { | footer: { | ||||
| profile: 'All rights reserved @ React', | profile: 'All rights reserved @ React', |
| import { | import { | ||||
| DatabaseOutlined, | DatabaseOutlined, | ||||
| MergeCellsOutlined, | MergeCellsOutlined, | ||||
| MessageOutlined, | |||||
| RocketOutlined, | RocketOutlined, | ||||
| SendOutlined, | SendOutlined, | ||||
| SlidersOutlined, | SlidersOutlined, | ||||
| Generate = 'Generate', | Generate = 'Generate', | ||||
| Answer = 'Answer', | Answer = 'Answer', | ||||
| Categorize = 'Categorize', | Categorize = 'Categorize', | ||||
| Message = 'Message', | |||||
| } | } | ||||
| export const operatorIconMap = { | export const operatorIconMap = { | ||||
| [Operator.Answer]: SendOutlined, | [Operator.Answer]: SendOutlined, | ||||
| [Operator.Begin]: SlidersOutlined, | [Operator.Begin]: SlidersOutlined, | ||||
| [Operator.Categorize]: DatabaseOutlined, | [Operator.Categorize]: DatabaseOutlined, | ||||
| [Operator.Message]: MessageOutlined, | |||||
| }; | }; | ||||
| export const operatorMap = { | export const operatorMap = { | ||||
| [Operator.Answer]: { description: 'Answer description' }, | [Operator.Answer]: { description: 'Answer description' }, | ||||
| [Operator.Begin]: { description: 'Begin description' }, | [Operator.Begin]: { description: 'Begin description' }, | ||||
| [Operator.Categorize]: { description: 'Categorize description' }, | [Operator.Categorize]: { description: 'Categorize description' }, | ||||
| [Operator.Message]: { description: 'Message description' }, | |||||
| }; | }; | ||||
| export const componentMenuList = [ | export const componentMenuList = [ | ||||
| name: Operator.Categorize, | name: Operator.Categorize, | ||||
| description: operatorMap[Operator.Categorize].description, | description: operatorMap[Operator.Categorize].description, | ||||
| }, | }, | ||||
| { | |||||
| name: Operator.Message, | |||||
| description: operatorMap[Operator.Message].description, | |||||
| }, | |||||
| ]; | ]; | ||||
| export const initialRetrievalValues = { | export const initialRetrievalValues = { | ||||
| [Operator.Answer]: [], | [Operator.Answer]: [], | ||||
| [Operator.Retrieval]: [], | [Operator.Retrieval]: [], | ||||
| [Operator.Generate]: [], | [Operator.Generate]: [], | ||||
| [Operator.Message]: [], | |||||
| }; | }; | ||||
| export const NodeMap = { | export const NodeMap = { | ||||
| [Operator.Retrieval]: 'ragNode', | [Operator.Retrieval]: 'ragNode', | ||||
| [Operator.Generate]: 'ragNode', | [Operator.Generate]: 'ragNode', | ||||
| [Operator.Answer]: 'ragNode', | [Operator.Answer]: 'ragNode', | ||||
| [Operator.Message]: 'ragNode', | |||||
| }; | }; |
| import { Operator } from '../constant'; | import { Operator } from '../constant'; | ||||
| import GenerateForm from '../generate-form'; | import GenerateForm from '../generate-form'; | ||||
| import { useHandleFormValuesChange } from '../hooks'; | import { useHandleFormValuesChange } from '../hooks'; | ||||
| import MessageForm from '../message-form'; | |||||
| import RetrievalForm from '../retrieval-form'; | import RetrievalForm from '../retrieval-form'; | ||||
| interface IProps { | interface IProps { | ||||
| [Operator.Generate]: GenerateForm, | [Operator.Generate]: GenerateForm, | ||||
| [Operator.Answer]: AnswerForm, | [Operator.Answer]: AnswerForm, | ||||
| [Operator.Categorize]: CategorizeForm, | [Operator.Categorize]: CategorizeForm, | ||||
| [Operator.Message]: MessageForm, | |||||
| }; | }; | ||||
| const FlowDrawer = ({ | const FlowDrawer = ({ |
| .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; | |||||
| } | |||||
| } |
| import { useTranslate } from '@/hooks/commonHooks'; | 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'; | 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 MessageForm = ({ onValuesChange, form }: IOperatorForm) => { | ||||
| const { t } = useTranslate('chat'); | |||||
| const { t } = useTranslate('flow'); | |||||
| return ( | return ( | ||||
| <Form | <Form | ||||
| name="basic" | name="basic" | ||||
| labelCol={{ span: 8 }} | |||||
| wrapperCol={{ span: 16 }} | |||||
| style={{ maxWidth: 600 }} | |||||
| {...formItemLayoutWithOutLabel} | |||||
| initialValues={{ remember: true }} | initialValues={{ remember: true }} | ||||
| onFinish={onFinish} | |||||
| onFinishFailed={onFinishFailed} | |||||
| onValuesChange={onValuesChange} | onValuesChange={onValuesChange} | ||||
| autoComplete="off" | autoComplete="off" | ||||
| form={form} | 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> | </Form> | ||||
| ); | ); | ||||
| }; | }; |