From 1fe9a2e6fd8690bf702537bec72ae9a04c8715db Mon Sep 17 00:00:00 2001 From: balibabu Date: Wed, 13 Nov 2024 14:54:10 +0800 Subject: [PATCH] feat: Add input parameter to begin operator #3355 (#3375) ### What problem does this PR solve? feat: Add input parameter to begin operator #3355 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- web/src/hooks/logic-hooks.ts | 21 ++++ web/src/pages/flow/constant.tsx | 20 ++++ .../form/begin-form/begin-dynamic-options.tsx | 68 +++++++++++ web/src/pages/flow/form/begin-form/hooks.ts | 50 ++++++++ web/src/pages/flow/form/begin-form/index.tsx | 108 ++++++++++++++--- .../flow/form/begin-form/paramater-modal.tsx | 113 ++++++++++++++++++ .../flow/form/begin-form/query-table.tsx | 71 +++++++++++ web/src/pages/flow/interface.ts | 9 ++ 8 files changed, 443 insertions(+), 17 deletions(-) create mode 100644 web/src/pages/flow/form/begin-form/begin-dynamic-options.tsx create mode 100644 web/src/pages/flow/form/begin-form/hooks.ts create mode 100644 web/src/pages/flow/form/begin-form/paramater-modal.tsx create mode 100644 web/src/pages/flow/form/begin-form/query-table.tsx diff --git a/web/src/hooks/logic-hooks.ts b/web/src/hooks/logic-hooks.ts index 5ca6a895c..420897492 100644 --- a/web/src/hooks/logic-hooks.ts +++ b/web/src/hooks/logic-hooks.ts @@ -557,3 +557,24 @@ export const useHandleChunkMethodSelectChange = (form: FormInstance) => { return handleChange; }; + +// reset form fields when modal is form, closed +export const useResetFormOnCloseModal = ({ + form, + visible, +}: { + form: FormInstance; + visible?: boolean; +}) => { + const prevOpenRef = useRef(); + useEffect(() => { + prevOpenRef.current = visible; + }, [visible]); + const prevOpen = prevOpenRef.current; + + useEffect(() => { + if (!visible && prevOpen) { + form.resetFields(); + } + }, [form, prevOpen, visible]); +}; diff --git a/web/src/pages/flow/constant.tsx b/web/src/pages/flow/constant.tsx index d55b24a59..40d134cac 100644 --- a/web/src/pages/flow/constant.tsx +++ b/web/src/pages/flow/constant.tsx @@ -2859,3 +2859,23 @@ export const TuShareSrcOptions = [ 'jinrongjie', ]; export const CrawlerResultOptions = ['markdown', 'html', 'content']; + +export enum BeginQueryType { + Line = 'line', + Paragraph = 'paragraph', + Options = 'options', + File = 'file', + Integer = 'integer', + Boolean = 'boolean', + Url = 'url', +} + +export const BeginQueryTypeMap = { + [BeginQueryType.Line]: 'input', + [BeginQueryType.Paragraph]: 'textarea', + [BeginQueryType.Options]: 'select', + [BeginQueryType.File]: 'file', + [BeginQueryType.Integer]: 'inputnumber', + [BeginQueryType.Boolean]: 'switch', + [BeginQueryType.Url]: 'input', +}; diff --git a/web/src/pages/flow/form/begin-form/begin-dynamic-options.tsx b/web/src/pages/flow/form/begin-form/begin-dynamic-options.tsx new file mode 100644 index 000000000..b68a13d5b --- /dev/null +++ b/web/src/pages/flow/form/begin-form/begin-dynamic-options.tsx @@ -0,0 +1,68 @@ +import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons'; +import { Button, Form, Input } from 'antd'; + +const BeginDynamicOptions = () => { + return ( + { + if (!names || names.length < 1) { + return Promise.reject(new Error('At least 1 option')); + } + }, + }, + ]} + > + {(fields, { add, remove }, { errors }) => ( + <> + {fields.map((field, index) => ( + + + + + {fields.length > 1 ? ( + remove(field.name)} + /> + ) : null} + + ))} + + + + + + )} + + ); +}; + +export default BeginDynamicOptions; diff --git a/web/src/pages/flow/form/begin-form/hooks.ts b/web/src/pages/flow/form/begin-form/hooks.ts new file mode 100644 index 000000000..b045f5dc5 --- /dev/null +++ b/web/src/pages/flow/form/begin-form/hooks.ts @@ -0,0 +1,50 @@ +import { useSetModalState } from '@/hooks/common-hooks'; +import { useSetSelectedRecord } from '@/hooks/logic-hooks'; +import { useCallback, useMemo, useState } from 'react'; +import { BeginQuery, IOperatorForm } from '../../interface'; + +export const useEditQueryRecord = ({ form, onValuesChange }: IOperatorForm) => { + const { setRecord, currentRecord } = useSetSelectedRecord(); + const { visible, hideModal, showModal } = useSetModalState(); + const [index, setIndex] = useState(-1); + + const otherThanCurrentQuery = useMemo(() => { + const query: BeginQuery[] = form?.getFieldValue('query') || []; + return query.filter((item, idx) => idx !== index); + }, [form, index]); + + const handleEditRecord = useCallback( + (record: BeginQuery) => { + const query: BeginQuery[] = form?.getFieldValue('query') || []; + + const nextQuery: BeginQuery[] = + index > -1 ? query.toSpliced(index, 1, record) : [...query, record]; + + onValuesChange?.( + { query: nextQuery }, + { query: nextQuery, prologue: form?.getFieldValue('prologue') }, + ); + hideModal(); + }, + [form, hideModal, index, onValuesChange], + ); + + const handleShowModal = useCallback( + (idx?: number, record?: BeginQuery) => { + setIndex(idx ?? -1); + setRecord(record ?? ({} as BeginQuery)); + showModal(); + }, + [setRecord, showModal], + ); + + return { + ok: handleEditRecord, + currentRecord, + setRecord, + visible, + hideModal, + showModal: handleShowModal, + otherThanCurrentQuery, + }; +}; diff --git a/web/src/pages/flow/form/begin-form/index.tsx b/web/src/pages/flow/form/begin-form/index.tsx index dbb852707..00265f4cb 100644 --- a/web/src/pages/flow/form/begin-form/index.tsx +++ b/web/src/pages/flow/form/begin-form/index.tsx @@ -1,6 +1,10 @@ import { useTranslate } from '@/hooks/common-hooks'; -import { Form, Input } from 'antd'; -import { IOperatorForm } from '../../interface'; +import { Button, Form, Input } from 'antd'; +import { useCallback } from 'react'; +import { BeginQuery, IOperatorForm } from '../../interface'; +import { useEditQueryRecord } from './hooks'; +import { ModalForm } from './paramater-modal'; +import QueryTable from './query-table'; type FieldType = { prologue?: string; @@ -8,25 +12,95 @@ type FieldType = { const BeginForm = ({ onValuesChange, form }: IOperatorForm) => { const { t } = useTranslate('chat'); + const { + ok, + currentRecord, + visible, + hideModal, + showModal, + otherThanCurrentQuery, + } = useEditQueryRecord({ + form, + onValuesChange, + }); + + const handleDeleteRecord = useCallback( + (idx: number) => { + const query = form?.getFieldValue('query') || []; + const nextQuery = query.filter( + (item: BeginQuery, index: number) => index !== idx, + ); + onValuesChange?.( + { query: nextQuery }, + { query: nextQuery, prologue: form?.getFieldValue('prologue') }, + ); + }, + [form, onValuesChange], + ); return ( -
{ + if (name === 'queryForm') { + ok(values as BeginQuery); + } + }} > - - name={'prologue'} - label={t('setAnOpener')} - tooltip={t('setAnOpenerTip')} - initialValue={t('setAnOpenerInitial')} + - - - + + name={'prologue'} + label={t('setAnOpener')} + tooltip={t('setAnOpenerTip')} + initialValue={t('setAnOpenerInitial')} + > + + + {/* Create a hidden field to make Form instance record this */} + + + + prevValues.query !== curValues.query + } + > + {({ getFieldValue }) => { + const query: BeginQuery[] = getFieldValue('query') || []; + return ( + + ); + }} + + + + {visible && ( + + )} + + ); }; diff --git a/web/src/pages/flow/form/begin-form/paramater-modal.tsx b/web/src/pages/flow/form/begin-form/paramater-modal.tsx new file mode 100644 index 000000000..6fd3e6adb --- /dev/null +++ b/web/src/pages/flow/form/begin-form/paramater-modal.tsx @@ -0,0 +1,113 @@ +import { useResetFormOnCloseModal } from '@/hooks/logic-hooks'; +import { IModalProps } from '@/interfaces/common'; +import { Form, Input, Modal, Select, Switch } from 'antd'; +import { DefaultOptionType } from 'antd/es/select'; +import { useEffect, useMemo } from 'react'; +import { BeginQueryType } from '../../constant'; +import { BeginQuery } from '../../interface'; +import BeginDynamicOptions from './begin-dynamic-options'; + +export const ModalForm = ({ + visible, + initialValue, + hideModal, + otherThanCurrentQuery, +}: IModalProps & { + initialValue: BeginQuery; + otherThanCurrentQuery: BeginQuery[]; +}) => { + const [form] = Form.useForm(); + const options = useMemo(() => { + return Object.values(BeginQueryType).reduce( + (pre, cur) => { + return [ + ...pre, + { + label: cur, + value: cur, + }, + ]; + }, + [], + ); + }, []); + + useResetFormOnCloseModal({ + form, + visible: visible, + }); + + useEffect(() => { + form.setFieldsValue(initialValue); + }, [form, initialValue]); + + const onOk = () => { + form.submit(); + }; + + return ( + +
+ + + + + + + + + + + prevValues.type !== curValues.type + } + > + {({ getFieldValue }) => { + const type: BeginQueryType = getFieldValue('type'); + return ( + type === BeginQueryType.Options && ( + + ) + ); + }} + +
+
+ ); +}; diff --git a/web/src/pages/flow/form/begin-form/query-table.tsx b/web/src/pages/flow/form/begin-form/query-table.tsx new file mode 100644 index 000000000..a4df68f35 --- /dev/null +++ b/web/src/pages/flow/form/begin-form/query-table.tsx @@ -0,0 +1,71 @@ +import { DeleteOutlined, EditOutlined } from '@ant-design/icons'; +import type { TableProps } from 'antd'; +import { Space, Table, Tooltip } from 'antd'; +import { BeginQuery } from '../../interface'; + +interface IProps { + data: BeginQuery[]; + deleteRecord(index: number): void; + showModal(index: number, record: BeginQuery): void; +} + +const QueryTable = ({ data, deleteRecord, showModal }: IProps) => { + const columns: TableProps['columns'] = [ + { + title: 'Key', + dataIndex: 'key', + key: 'key', + ellipsis: { + showTitle: false, + }, + render: (key) => ( + + {key} + + ), + }, + { + title: 'Name', + dataIndex: 'name', + key: 'name', + ellipsis: { + showTitle: false, + }, + render: (name) => ( + + {name} + + ), + }, + { + title: 'Type', + dataIndex: 'type', + key: 'type', + }, + { + title: 'Optional', + dataIndex: 'optional', + key: 'optional', + render: (optional) => (optional ? 'Yes' : 'No'), + }, + { + title: 'Action', + key: 'action', + render: (_, record, idx) => ( + + showModal(idx, record)} /> + deleteRecord(idx)} + /> + + ), + }, + ]; + + return ( + columns={columns} dataSource={data} pagination={false} /> + ); +}; + +export default QueryTable; diff --git a/web/src/pages/flow/interface.ts b/web/src/pages/flow/interface.ts index 880b44f54..d0b80a24c 100644 --- a/web/src/pages/flow/interface.ts +++ b/web/src/pages/flow/interface.ts @@ -100,3 +100,12 @@ export type NodeData = { }; export type IPosition = { top: number; right: number; idx: number }; + +export interface BeginQuery { + key: string; + type: string; + value: string; + optional: boolean; + name: string; + options: (number | string | boolean)[]; +}