feat(web): add http-request、jinja-render node
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
import { forwardRef, useEffect, useImperativeHandle, useState } from 'react';
|
||||
import { Form, Select, Input } from 'antd';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import type { AuthConfigModalRef, HttpRequestConfigForm } from './types'
|
||||
import RbModal from '@/components/RbModal'
|
||||
|
||||
const FormItem = Form.Item;
|
||||
|
||||
interface AuthConfigModalProps {
|
||||
refresh: (values: HttpRequestConfigForm['auth']) => void;
|
||||
}
|
||||
|
||||
const AuthConfigModal = forwardRef<AuthConfigModalRef, AuthConfigModalProps>(({
|
||||
refresh,
|
||||
}, ref) => {
|
||||
const { t } = useTranslation();
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [form] = Form.useForm<HttpRequestConfigForm['auth']>();
|
||||
|
||||
const values = Form.useWatch<HttpRequestConfigForm['auth']>([], form);
|
||||
|
||||
// 封装取消方法,添加关闭弹窗逻辑
|
||||
const handleClose = () => {
|
||||
setVisible(false);
|
||||
form.resetFields();
|
||||
};
|
||||
|
||||
const handleOpen = (data?: HttpRequestConfigForm['auth']) => {
|
||||
if (data) {
|
||||
form.setFieldsValue({
|
||||
auth: !data.auth_type || data.auth_type === 'none' ? 'none' : 'api_key',
|
||||
auth_type: !data.auth_type || data.auth_type === 'none' ? undefined : data.auth_type,
|
||||
header: data.header,
|
||||
api_key: data.api_key
|
||||
})
|
||||
}
|
||||
setVisible(true);
|
||||
};
|
||||
// 封装保存方法,添加提交逻辑
|
||||
const handleSave = () => {
|
||||
form
|
||||
.validateFields()
|
||||
.then(() => {
|
||||
const { auth, auth_type, ...rest } = values ?? {}
|
||||
refresh({
|
||||
auth_type: auth === 'none' ? 'none' : auth_type,
|
||||
...rest
|
||||
})
|
||||
handleClose()
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log('err', err)
|
||||
});
|
||||
}
|
||||
|
||||
// 暴露给父组件的方法
|
||||
useImperativeHandle(ref, () => ({
|
||||
handleOpen,
|
||||
handleClose
|
||||
}));
|
||||
|
||||
useEffect(() => {
|
||||
if (values?.auth === 'api_key') {
|
||||
form.setFieldValue('auth_type', 'basic')
|
||||
} else {
|
||||
form.setFieldsValue({
|
||||
auth_type: undefined,
|
||||
header: undefined,
|
||||
api_key: undefined
|
||||
})
|
||||
}
|
||||
}, [values?.auth])
|
||||
|
||||
|
||||
return (
|
||||
<RbModal
|
||||
title={t('workflow.config.http-request.auth')}
|
||||
open={visible}
|
||||
onCancel={handleClose}
|
||||
okText={t('common.save')}
|
||||
onOk={handleSave}
|
||||
>
|
||||
<Form
|
||||
form={form}
|
||||
layout="vertical"
|
||||
initialValues={{
|
||||
auth: 'none'
|
||||
}}
|
||||
>
|
||||
<FormItem
|
||||
name="auth"
|
||||
label={t('workflow.config.http-request.authType')}
|
||||
>
|
||||
<Select
|
||||
options={[
|
||||
{ value: 'none', label: t('workflow.config.http-request.none') },
|
||||
{ value: 'api_key', label: t('workflow.config.http-request.apiKey') },
|
||||
]}
|
||||
/>
|
||||
</FormItem>
|
||||
{values?.auth !== 'none' && <>
|
||||
<FormItem
|
||||
name="auth_type"
|
||||
label={t('workflow.config.http-request.authType')}
|
||||
>
|
||||
<Select
|
||||
options={[
|
||||
{ value: 'basic', label: t('workflow.config.http-request.basic') },
|
||||
{ value: 'bearer', label: t('workflow.config.http-request.bearer') },
|
||||
{ value: 'custom', label: t('workflow.config.http-request.custom') },
|
||||
]}
|
||||
/>
|
||||
</FormItem>
|
||||
{values?.auth_type === 'custom' &&
|
||||
<FormItem
|
||||
name="header"
|
||||
label={t('workflow.config.http-request.header')}
|
||||
rules={[
|
||||
{ required: true, message: t('common.pleaseEnter') }
|
||||
]}
|
||||
>
|
||||
<Input placeholder={t('common.pleaseEnter')} />
|
||||
</FormItem>
|
||||
}
|
||||
<FormItem
|
||||
name="api_key"
|
||||
label={t('workflow.config.http-request.api_key')}
|
||||
rules={[
|
||||
{ required: true, message: t('common.pleaseEnter') }
|
||||
]}
|
||||
>
|
||||
<Input placeholder={t('common.pleaseEnter')} />
|
||||
</FormItem>
|
||||
</>}
|
||||
</Form>
|
||||
</RbModal>
|
||||
);
|
||||
});
|
||||
|
||||
export default AuthConfigModal;
|
||||
@@ -0,0 +1,232 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Button, Select, Table } from 'antd';
|
||||
import { PlusOutlined, DeleteOutlined } from '@ant-design/icons';
|
||||
import Editor from '../../Editor';
|
||||
import type { Suggestion } from '../../Editor/plugin/AutocompletePlugin';
|
||||
import Empty from '@/components/Empty';
|
||||
import VariableSelect from '../VariableSelect';
|
||||
|
||||
export interface TableRow {
|
||||
key: string;
|
||||
name: string;
|
||||
value: string;
|
||||
type?: string;
|
||||
}
|
||||
|
||||
interface EditableTableProps {
|
||||
title?: string;
|
||||
value?: Record<string, string> | TableRow[];
|
||||
onChange?: (value: TableRow[]) => void;
|
||||
options?: Suggestion[];
|
||||
typeOptions?: {value: string, label: string}[]
|
||||
}
|
||||
|
||||
const EditableTable: React.FC<EditableTableProps> = ({
|
||||
title,
|
||||
value,
|
||||
onChange,
|
||||
options = [],
|
||||
typeOptions = []
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const [rows, setRows] = useState<TableRow[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
console.log('EditableTable value', value)
|
||||
if (Array.isArray(value)) {
|
||||
setRows([...value])
|
||||
} else if (value && Object.keys(value).length > 0) {
|
||||
// Only update if rows are empty or significantly different
|
||||
const valueEntries = Object.entries(value)
|
||||
if (rows.length === 0 || rows.length !== valueEntries.length) {
|
||||
setRows(valueEntries.map(([key, val], index) => {
|
||||
console.log('val', val)
|
||||
return {
|
||||
key: index.toString(),
|
||||
name: key || '',
|
||||
value: val || '',
|
||||
type: typeOptions.length > 0 ? typeOptions[0].value : undefined
|
||||
}
|
||||
}))
|
||||
}
|
||||
} else {
|
||||
setRows([])
|
||||
}
|
||||
}, [JSON.stringify(value), typeOptions.length])
|
||||
|
||||
const handleChange = (key: string, field: 'name' | 'value' | 'type', val: string) => {
|
||||
const newRows = [...rows.map(row =>
|
||||
row.key === key ? { ...row, [field]: val } : row
|
||||
)];
|
||||
|
||||
setRows(newRows);
|
||||
onChange?.(newRows);
|
||||
};
|
||||
|
||||
const handleAdd = () => {
|
||||
const newKey = Date.now().toString();
|
||||
if (typeOptions.length) {
|
||||
setRows([...rows, { key: newKey, name: '', value: '', type: typeOptions[0].value }]);
|
||||
} else {
|
||||
setRows([...rows, { key: newKey, name: '', value: '' }]);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDelete = (key: string, index: number) => {
|
||||
console.log('index', index)
|
||||
|
||||
if (rows.length === 1) {
|
||||
setRows([]);
|
||||
onChange?.([]);
|
||||
} else {
|
||||
const newRows = rows.filter(row => row.key !== key);
|
||||
setRows(newRows);
|
||||
onChange?.(newRows);
|
||||
}
|
||||
};
|
||||
|
||||
const columns = typeOptions?.length > 0 ? [
|
||||
{
|
||||
title: t('workflow.config.name'),
|
||||
dataIndex: 'name',
|
||||
width: '45%',
|
||||
render: (text: string, record: TableRow) => (
|
||||
<Editor
|
||||
options={options}
|
||||
value={text}
|
||||
height={32}
|
||||
variant="outlined"
|
||||
onChange={(value) => handleChange(record.key, 'name', value)}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t('workflow.config.type'),
|
||||
dataIndex: 'type',
|
||||
width: '20%',
|
||||
render: (text: string, record: TableRow) => (
|
||||
<Select
|
||||
value={text}
|
||||
options={typeOptions}
|
||||
onChange={(value) => {
|
||||
console.log('value record', value)
|
||||
handleChange(record.key, 'type', value)
|
||||
}}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t('workflow.config.value'),
|
||||
dataIndex: 'value',
|
||||
width: '45%',
|
||||
render: (text: string, record: TableRow) => {
|
||||
if (record.type === 'file') {
|
||||
|
||||
return (
|
||||
<VariableSelect
|
||||
options={options}
|
||||
value={text}
|
||||
onChange={(value) => {
|
||||
console.log('value record', value)
|
||||
handleChange(record.key, 'value', value)
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<Editor
|
||||
options={options}
|
||||
value={text}
|
||||
height={32}
|
||||
variant="outlined"
|
||||
onChange={(value) => {
|
||||
console.log('value record', value)
|
||||
handleChange(record.key, 'value', value)
|
||||
}}
|
||||
/>
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '',
|
||||
width: '10%',
|
||||
render: (_: any, record: TableRow, index: number) => (
|
||||
<Button
|
||||
type="text"
|
||||
icon={<DeleteOutlined />}
|
||||
onClick={() => handleDelete(record.key, index)}
|
||||
/>
|
||||
),
|
||||
},
|
||||
] : [
|
||||
{
|
||||
title: '键',
|
||||
dataIndex: 'name',
|
||||
width: '45%',
|
||||
render: (text: string, record: TableRow) => (
|
||||
<Editor
|
||||
options={options}
|
||||
value={text}
|
||||
height={32}
|
||||
variant="outlined"
|
||||
onChange={(value) => handleChange(record.key, 'name', value)}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '值',
|
||||
dataIndex: 'value',
|
||||
width: '45%',
|
||||
render: (text: string, record: TableRow) => (
|
||||
<Editor
|
||||
options={options}
|
||||
value={text}
|
||||
height={32}
|
||||
variant="outlined"
|
||||
onChange={(value) => handleChange(record.key, 'value', value)}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '',
|
||||
width: '10%',
|
||||
render: (_: any, record: TableRow, index: number) => (
|
||||
<Button
|
||||
type="text"
|
||||
icon={<DeleteOutlined />}
|
||||
onClick={() => handleDelete(record.key, index)}
|
||||
/>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="rb:mb-4">
|
||||
{title && <div className="rb:flex rb:items-center rb:mb-2 rb:justify-between">
|
||||
<div className="rb:font-medium">{title}</div>
|
||||
<Button
|
||||
type="text"
|
||||
icon={<PlusOutlined />}
|
||||
onClick={handleAdd}
|
||||
size="small"
|
||||
/>
|
||||
</div>}
|
||||
<Table
|
||||
columns={columns}
|
||||
dataSource={rows}
|
||||
pagination={false}
|
||||
size="small"
|
||||
locale={{ emptyText: <Empty size={88} /> }}
|
||||
scroll={{ x: 'max-content' }}
|
||||
/>
|
||||
{!title &&
|
||||
<Button type="dashed" onClick={handleAdd} block className='rb:mt-1'>
|
||||
+{t('common.add')}
|
||||
</Button>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default EditableTable;
|
||||
@@ -0,0 +1,272 @@
|
||||
import { type FC, useEffect, useRef } from "react";
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Form, Row, Col, Select, Button, Divider, InputNumber, Switch, Input, Slider } from 'antd'
|
||||
import Editor from '../../Editor'
|
||||
import type { Suggestion } from '../../Editor/plugin/AutocompletePlugin'
|
||||
import AuthConfigModal from './AuthConfigModal'
|
||||
import type { AuthConfigModalRef, HttpRequestConfigForm } from './types'
|
||||
import VariableSelect from "../VariableSelect";
|
||||
import MessageEditor from '../MessageEditor'
|
||||
import EditableTable, { type TableRow } from './EditableTable'
|
||||
|
||||
const HttpRequest: FC<{ options: Suggestion[]; }> = ({
|
||||
options,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const form = Form.useFormInstance();
|
||||
const values = Form.useWatch([], form) || {}
|
||||
const authConfigModalRef = useRef<AuthConfigModalRef>(null)
|
||||
|
||||
const handleChangeAuth = () => {
|
||||
authConfigModalRef.current?.handleOpen(values?.auth)
|
||||
}
|
||||
const handleRefresh = (auth: HttpRequestConfigForm['auth']) => {
|
||||
console.log('handleRefresh', auth)
|
||||
form.setFieldsValue({ auth: {...auth} })
|
||||
}
|
||||
|
||||
const handleChangeBodyContentType = (contentType: string) => {
|
||||
const currentValues = form.getFieldsValue()
|
||||
form.setFieldsValue({
|
||||
body: {
|
||||
...currentValues?.body,
|
||||
content_type: contentType,
|
||||
data: undefined
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const updateObjectList = (data: TableRow[], key: string) => {
|
||||
let obj: Record<string, string> = {}
|
||||
if (data.length) {
|
||||
data.forEach(vo => {
|
||||
obj[vo.name] = vo.value
|
||||
})
|
||||
}
|
||||
|
||||
form.setFieldValue(key, obj)
|
||||
}
|
||||
|
||||
console.log('HttpRequest', values)
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="rb:flex rb:items-center rb:justify-between rb:mb-4">
|
||||
<div>API</div>
|
||||
<Button onClick={handleChangeAuth}>{t('workflow.config.http-request.auth')}</Button>
|
||||
</div>
|
||||
<Row gutter={16}>
|
||||
<Col span={8}>
|
||||
<Form.Item name="method">
|
||||
<Select
|
||||
options={[
|
||||
{ label: 'GET', value: 'GET' },
|
||||
{ label: 'POST', value: 'POST' },
|
||||
{ label: 'HEAD', value: 'HEAD' },
|
||||
{ label: 'PATCH', value: 'PATCH' },
|
||||
{ label: 'PUT', value: 'PUT' },
|
||||
{ label: 'DELETE', value: 'DELETE' },
|
||||
]}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={16}>
|
||||
<Form.Item name="url">
|
||||
<Editor options={options} variant="outlined" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
<Form.Item name="auth" hidden>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item name="headers">
|
||||
<EditableTable
|
||||
title="HEADERS"
|
||||
options={options}
|
||||
onChange={(headers) => updateObjectList(headers, 'headers')}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item name="params">
|
||||
<EditableTable
|
||||
title="PARAMS"
|
||||
options={options}
|
||||
onChange={(params) => updateObjectList(params, 'params')}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label="BODY">
|
||||
<Form.Item name={['body', 'content_type']}>
|
||||
<Select
|
||||
placeholder={t('common.pleaseSelect')}
|
||||
onChange={handleChangeBodyContentType}
|
||||
options={[
|
||||
{ label: 'none', value: 'none' },
|
||||
{ label: 'form-data', value: 'form-data' },
|
||||
{ label: 'x-www-form-urlencoded', value: 'x-www-form-urlencoded' },
|
||||
{ label: 'JSON', value: 'json' },
|
||||
{ label: 'raw', value: 'raw' },
|
||||
{ label: 'binary', value: 'binary' },
|
||||
]}
|
||||
/>
|
||||
</Form.Item>
|
||||
{values?.body?.content_type === 'form-data' &&
|
||||
<Form.Item name={['body', 'data']} noStyle>
|
||||
<EditableTable
|
||||
options={options}
|
||||
onChange={(data) => {
|
||||
form.setFieldsValue({
|
||||
body: {
|
||||
...form.getFieldValue('body'),
|
||||
data
|
||||
}
|
||||
})
|
||||
}}
|
||||
typeOptions={[
|
||||
{ label: 'text', value: 'text' },
|
||||
{ label: 'file', value: 'file' }
|
||||
]}
|
||||
/>
|
||||
</Form.Item>
|
||||
}
|
||||
{values?.body?.content_type === 'x-www-form-urlencoded' &&
|
||||
<Form.Item name={['body', 'data']} noStyle>
|
||||
<EditableTable
|
||||
options={options}
|
||||
onChange={(data) => {
|
||||
const currentBody = form.getFieldValue('body') || {}
|
||||
form.setFieldsValue({
|
||||
body: { ...currentBody, data }
|
||||
})
|
||||
}}
|
||||
/>
|
||||
</Form.Item>
|
||||
}
|
||||
{values?.body?.content_type === 'json' &&
|
||||
<Form.Item name={['body', 'data']}>
|
||||
<MessageEditor
|
||||
options={options}
|
||||
isArray={false}
|
||||
title="JSON"
|
||||
/>
|
||||
</Form.Item>
|
||||
}
|
||||
{values?.body?.content_type === 'raw' &&
|
||||
<Form.Item name={['body', 'data']}>
|
||||
<MessageEditor
|
||||
options={options}
|
||||
isArray={false}
|
||||
title="RAW TEXT"
|
||||
/>
|
||||
</Form.Item>
|
||||
}
|
||||
{values?.body?.content_type === 'binary' &&
|
||||
<Form.Item name={['body', 'data']}>
|
||||
<VariableSelect
|
||||
options={options}
|
||||
/>
|
||||
</Form.Item>
|
||||
}
|
||||
</Form.Item>
|
||||
<Divider />
|
||||
<Form.Item layout="horizontal" name="verify_ssl" label={t('workflow.config.http-request.verify_ssl')}>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
|
||||
<Divider />
|
||||
<div>{t('workflow.config.http-request.timeouts')}</div>
|
||||
<Form.Item
|
||||
name={['timeouts', 'connect_timeout']}
|
||||
label={t('workflow.config.http-request.connect_timeout')}
|
||||
>
|
||||
<InputNumber placeholder={t('common.pleaseEnter')} className="rb:w-full!" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name={['timeouts', 'read_timeout']}
|
||||
label={t('workflow.config.http-request.read_timeout')}
|
||||
>
|
||||
<InputNumber placeholder={t('common.pleaseEnter')} className="rb:w-full!" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name={['timeouts', 'write_timeout']}
|
||||
label={t('workflow.config.http-request.write_timeout')}
|
||||
>
|
||||
<InputNumber placeholder={t('common.pleaseEnter')} className="rb:w-full!" />
|
||||
</Form.Item>
|
||||
|
||||
<Divider />
|
||||
<Form.Item name={['retry', 'enable']} valuePropName="checked" layout="horizontal" label={t('workflow.config.http-request.retry')}>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
{(values?.retry?.enable || typeof values?.retry?.max_attempts === 'number' || typeof values?.retry?.retry_interval === 'number') &&
|
||||
<>
|
||||
<Form.Item
|
||||
name={['retry', 'max_attempts']}
|
||||
label={t('workflow.config.http-request.max_attempts')}
|
||||
>
|
||||
<InputNumber placeholder={t('common.pleaseEnter')} className="rb:w-full!" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name={['retry', 'retry_interval']}
|
||||
label={t('workflow.config.http-request.retry_interval')}
|
||||
>
|
||||
<InputNumber placeholder={t('common.pleaseEnter')} className="rb:w-full!" />
|
||||
</Form.Item>
|
||||
</>
|
||||
}
|
||||
|
||||
<Divider />
|
||||
<Form.Item layout="horizontal" name={['error_handle', 'method']} label={t('workflow.config.http-request.error_handle')}>
|
||||
<Select
|
||||
placeholder={t('common.pleaseSelect')}
|
||||
options={[
|
||||
{ value: 'none', label: t('workflow.config.http-request.none') },
|
||||
{ value: 'default', label: t('workflow.config.http-request.default') },
|
||||
{ value: 'branch', label: t('workflow.config.http-request.branch') },
|
||||
]}
|
||||
/>
|
||||
</Form.Item>
|
||||
{values?.error_handle?.method === 'default' &&
|
||||
<>
|
||||
<Form.Item
|
||||
name={['error_handle', 'body']}
|
||||
label="body"
|
||||
>
|
||||
<Input placeholder={t('common.pleaseEnter')} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name={['error_handle', 'status_code']}
|
||||
label="status_code"
|
||||
>
|
||||
<InputNumber placeholder={t('common.pleaseEnter')} className="rb:w-full!" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name={['error_handle', 'headers']}
|
||||
label="headers"
|
||||
rules={[
|
||||
{
|
||||
validator: (_, value) => {
|
||||
if (!value) return Promise.resolve();
|
||||
try {
|
||||
JSON.parse(value);
|
||||
return Promise.resolve();
|
||||
} catch {
|
||||
return Promise.reject(new Error('Please enter valid JSON format'));
|
||||
}
|
||||
}
|
||||
}
|
||||
]}
|
||||
>
|
||||
<Input.TextArea placeholder={t('common.pleaseEnter')} />
|
||||
</Form.Item>
|
||||
</>
|
||||
}
|
||||
|
||||
<AuthConfigModal
|
||||
ref={authConfigModalRef}
|
||||
refresh={handleRefresh}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
export default HttpRequest;
|
||||
@@ -0,0 +1,44 @@
|
||||
export interface HttpRequestConfigForm {
|
||||
method?: string;
|
||||
url?: string;
|
||||
auth?: {
|
||||
auth?: string;
|
||||
auth_type?: string;
|
||||
header?: string;
|
||||
api_key?: string;
|
||||
};
|
||||
headers?: {
|
||||
[key: string]: string;
|
||||
};
|
||||
params?: {
|
||||
[key: string]: string;
|
||||
};
|
||||
body?: {
|
||||
content_type?: string;
|
||||
data: string | Record<string, string>;
|
||||
};
|
||||
verify_ssl?: boolean;
|
||||
timeouts?: {
|
||||
connect_timeout: number;
|
||||
read_timeout: number;
|
||||
write_timeout: number;
|
||||
};
|
||||
retry?: {
|
||||
max_attempts: number;
|
||||
retry_interval: number;
|
||||
};
|
||||
error_handle?: {
|
||||
method: string;
|
||||
default: {
|
||||
body: string;
|
||||
status_code: number;
|
||||
headers: {
|
||||
[key: string]: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export interface AuthConfigModalRef {
|
||||
handleOpen: (vo?: HttpRequestConfigForm['auth']) => void;
|
||||
}
|
||||
Reference in New Issue
Block a user