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)
This commit is contained in:
balibabu 2024-11-13 14:54:10 +08:00 committed by GitHub
parent 9fc092a911
commit 1fe9a2e6fd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 443 additions and 17 deletions

View File

@ -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<boolean>();
useEffect(() => {
prevOpenRef.current = visible;
}, [visible]);
const prevOpen = prevOpenRef.current;
useEffect(() => {
if (!visible && prevOpen) {
form.resetFields();
}
}, [form, prevOpen, visible]);
};

View File

@ -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',
};

View File

@ -0,0 +1,68 @@
import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons';
import { Button, Form, Input } from 'antd';
const BeginDynamicOptions = () => {
return (
<Form.List
name="options"
rules={[
{
validator: async (_, names) => {
if (!names || names.length < 1) {
return Promise.reject(new Error('At least 1 option'));
}
},
},
]}
>
{(fields, { add, remove }, { errors }) => (
<>
{fields.map((field, index) => (
<Form.Item
label={index === 0 ? 'Options' : ''}
required={false}
key={field.key}
>
<Form.Item
{...field}
validateTrigger={['onChange', 'onBlur']}
rules={[
{
required: true,
whitespace: true,
message: 'Please input option or delete this field.',
},
]}
noStyle
>
<Input
placeholder="option"
style={{ width: '90%', marginRight: 16 }}
/>
</Form.Item>
{fields.length > 1 ? (
<MinusCircleOutlined
className="dynamic-delete-button"
onClick={() => remove(field.name)}
/>
) : null}
</Form.Item>
))}
<Form.Item>
<Button
type="dashed"
onClick={() => add()}
icon={<PlusOutlined />}
block
>
Add field
</Button>
<Form.ErrorList errors={errors} />
</Form.Item>
</>
)}
</Form.List>
);
};
export default BeginDynamicOptions;

View File

@ -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<BeginQuery>();
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,
};
};

View File

@ -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 (
<Form
name="basic"
labelCol={{ span: 8 }}
wrapperCol={{ span: 16 }}
onValuesChange={onValuesChange}
autoComplete="off"
form={form}
<Form.Provider
onFormFinish={(name, { values }) => {
if (name === 'queryForm') {
ok(values as BeginQuery);
}
}}
>
<Form.Item<FieldType>
name={'prologue'}
label={t('setAnOpener')}
tooltip={t('setAnOpenerTip')}
initialValue={t('setAnOpenerInitial')}
<Form
name="basicForm"
onValuesChange={onValuesChange}
autoComplete="off"
form={form}
layout="vertical"
>
<Input.TextArea autoSize={{ minRows: 5 }} />
</Form.Item>
</Form>
<Form.Item<FieldType>
name={'prologue'}
label={t('setAnOpener')}
tooltip={t('setAnOpenerTip')}
initialValue={t('setAnOpenerInitial')}
>
<Input.TextArea autoSize={{ minRows: 5 }} />
</Form.Item>
{/* Create a hidden field to make Form instance record this */}
<Form.Item name="query" noStyle />
<Form.Item
label="Query List"
shouldUpdate={(prevValues, curValues) =>
prevValues.query !== curValues.query
}
>
{({ getFieldValue }) => {
const query: BeginQuery[] = getFieldValue('query') || [];
return (
<QueryTable
data={query}
showModal={showModal}
deleteRecord={handleDeleteRecord}
></QueryTable>
);
}}
</Form.Item>
<Button
htmlType="button"
style={{ margin: '0 8px' }}
onClick={() => showModal()}
block
>
Add +
</Button>
{visible && (
<ModalForm
visible={visible}
hideModal={hideModal}
initialValue={currentRecord}
onOk={ok}
otherThanCurrentQuery={otherThanCurrentQuery}
/>
)}
</Form>
</Form.Provider>
);
};

View File

@ -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<BeginQuery> & {
initialValue: BeginQuery;
otherThanCurrentQuery: BeginQuery[];
}) => {
const [form] = Form.useForm();
const options = useMemo(() => {
return Object.values(BeginQueryType).reduce<DefaultOptionType[]>(
(pre, cur) => {
return [
...pre,
{
label: cur,
value: cur,
},
];
},
[],
);
}, []);
useResetFormOnCloseModal({
form,
visible: visible,
});
useEffect(() => {
form.setFieldsValue(initialValue);
}, [form, initialValue]);
const onOk = () => {
form.submit();
};
return (
<Modal
title="Begin query"
open={visible}
onOk={onOk}
onCancel={hideModal}
centered
>
<Form form={form} layout="vertical" name="queryForm" autoComplete="false">
<Form.Item
name="type"
label="Type"
rules={[{ required: true }]}
initialValue={BeginQueryType.Line}
>
<Select options={options} />
</Form.Item>
<Form.Item
name="key"
label="Key"
rules={[
{ required: true },
() => ({
validator(_, value) {
if (
!value ||
!otherThanCurrentQuery.some((x) => x.key === value)
) {
return Promise.resolve();
}
return Promise.reject(new Error('The key cannot be repeated!'));
},
}),
]}
>
<Input />
</Form.Item>
<Form.Item name="name" label="Name" rules={[{ required: true }]}>
<Input />
</Form.Item>
<Form.Item
name="optional"
label={'Optional'}
valuePropName="checked"
initialValue={false}
>
<Switch />
</Form.Item>
<Form.Item
shouldUpdate={(prevValues, curValues) =>
prevValues.type !== curValues.type
}
>
{({ getFieldValue }) => {
const type: BeginQueryType = getFieldValue('type');
return (
type === BeginQueryType.Options && (
<BeginDynamicOptions></BeginDynamicOptions>
)
);
}}
</Form.Item>
</Form>
</Modal>
);
};

View File

@ -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<BeginQuery>['columns'] = [
{
title: 'Key',
dataIndex: 'key',
key: 'key',
ellipsis: {
showTitle: false,
},
render: (key) => (
<Tooltip placement="topLeft" title={key}>
{key}
</Tooltip>
),
},
{
title: 'Name',
dataIndex: 'name',
key: 'name',
ellipsis: {
showTitle: false,
},
render: (name) => (
<Tooltip placement="topLeft" title={name}>
{name}
</Tooltip>
),
},
{
title: 'Type',
dataIndex: 'type',
key: 'type',
},
{
title: 'Optional',
dataIndex: 'optional',
key: 'optional',
render: (optional) => (optional ? 'Yes' : 'No'),
},
{
title: 'Action',
key: 'action',
render: (_, record, idx) => (
<Space>
<EditOutlined onClick={() => showModal(idx, record)} />
<DeleteOutlined
className="cursor-pointer"
onClick={() => deleteRecord(idx)}
/>
</Space>
),
},
];
return (
<Table<BeginQuery> columns={columns} dataSource={data} pagination={false} />
);
};
export default QueryTable;

View File

@ -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)[];
}