You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by ju...@apache.org on 2020/06/01 10:35:53 UTC
[incubator-apisix-dashboard] branch next updated: Fix routes (#229)
This is an automated email from the ASF dual-hosted git repository.
juzhiyuan pushed a commit to branch next
in repository https://gitbox.apache.org/repos/asf/incubator-apisix-dashboard.git
The following commit(s) were added to refs/heads/next by this push:
new 512b22c Fix routes (#229)
512b22c is described below
commit 512b22c7fdbd3f021c07fcbede3707acc8dad3ba
Author: 琚致远 <ju...@apache.org>
AuthorDate: Mon Jun 1 18:35:47 2020 +0800
Fix routes (#229)
* feat: refactor page1
* codes clean
* codes clean
* codes clean
---
.../Routes/components/Step1/MatchingRulesView.tsx | 147 +++++++++
src/pages/Routes/components/Step1/MetaView.tsx | 32 ++
.../Routes/components/Step1/RequestConfigView.tsx | 149 +++++++++
src/pages/Routes/components/Step1/index.tsx | 343 +--------------------
src/pages/Routes/constants.ts | 1 +
src/pages/Routes/typing.d.ts | 3 +
6 files changed, 342 insertions(+), 333 deletions(-)
diff --git a/src/pages/Routes/components/Step1/MatchingRulesView.tsx b/src/pages/Routes/components/Step1/MatchingRulesView.tsx
new file mode 100644
index 0000000..ae87d6c
--- /dev/null
+++ b/src/pages/Routes/components/Step1/MatchingRulesView.tsx
@@ -0,0 +1,147 @@
+import React, { useState } from 'react';
+import { Button, Table, Modal, Form, Select, Input, Space } from 'antd';
+
+import PanelSection from '../PanelSection';
+
+interface Props extends RouteModule.Data {}
+
+const MatchingRulesView: React.FC<Props> = ({ data, onChange }) => {
+ const { advancedMatchingRules } = data.step1Data;
+
+ const [visible, setVisible] = useState(false);
+ const [modalForm] = Form.useForm();
+
+ const { Option } = Select;
+
+ const onOk = () => {
+ modalForm.validateFields().then((value) => {
+ onChange({
+ advancedMatchingRules: advancedMatchingRules.concat({
+ ...(value as RouteModule.MatchingRule),
+ key: Math.random().toString(36).slice(2),
+ }),
+ });
+ modalForm.resetFields();
+ setVisible(false);
+ });
+ };
+
+ const handleEdit = (record: RouteModule.MatchingRule) => {
+ setVisible(true);
+ modalForm.setFieldsValue(record);
+ };
+
+ const handleRemove = (key: string) => {
+ const filteredAdvancedMatchingRules = advancedMatchingRules.filter((item) => item.key !== key);
+ onChange({ advancedMatchingRules: filteredAdvancedMatchingRules });
+ };
+
+ const columns = [
+ {
+ title: '参数位置',
+ dataIndex: 'paramsLocation',
+ key: 'paramsLocation',
+ },
+ {
+ title: '参数名称',
+ dataIndex: 'paramsName',
+ key: 'paramsName',
+ },
+ {
+ title: '运算符',
+ dataIndex: 'paramsExpresstion',
+ key: 'paramsExpresstion',
+ },
+ {
+ title: '参数值',
+ dataIndex: 'paramsValue',
+ key: 'paramsValue',
+ },
+ {
+ title: '操作',
+ key: 'action',
+ render: (_: any, record: RouteModule.MatchingRule) => (
+ <Space size="middle">
+ <a onClick={() => handleEdit(record)}>编辑</a>
+ <a onClick={() => handleRemove(record.key)}>移除</a>
+ </Space>
+ ),
+ },
+ ];
+
+ const renderModal = () => {
+ return (
+ <Modal
+ title="新增"
+ centered
+ visible={visible}
+ onOk={onOk}
+ onCancel={() => {
+ setVisible(false);
+ modalForm.resetFields();
+ }}
+ destroyOnClose
+ >
+ <Form form={modalForm} labelCol={{ span: 4 }} wrapperCol={{ span: 20 }}>
+ <Form.Item
+ label="参数位置"
+ name="paramsLocation"
+ rules={[{ required: true, message: '请选择参数位置' }]}
+ >
+ <Select>
+ <Option value="header">header</Option>
+ <Option value="query">query</Option>
+ <Option value="params">params</Option>
+ <Option value="cookie">cookie</Option>
+ </Select>
+ </Form.Item>
+ <Form.Item
+ label="参数名称"
+ name="paramsName"
+ rules={[{ required: true, message: '请输入参数名称' }]}
+ >
+ <Input />
+ </Form.Item>
+ <Form.Item
+ label="运算符"
+ name="paramsExpresstion"
+ rules={[{ required: true, message: '请选择运算符' }]}
+ >
+ <Select>
+ <Option value="==">等于</Option>
+ <Option value="~=">不等于</Option>
+ <Option value=">">大于</Option>
+ <Option value="<">小于</Option>
+ <Option value="~~">正则匹配</Option>
+ </Select>
+ </Form.Item>
+ <Form.Item
+ label="值"
+ name="paramsValue"
+ rules={[{ required: true, message: '请输入参数值' }]}
+ >
+ <Input />
+ </Form.Item>
+ </Form>
+ </Modal>
+ );
+ };
+
+ return (
+ <PanelSection title="高级路由匹配条件">
+ <Button
+ onClick={() => setVisible(true)}
+ type="primary"
+ style={{
+ marginBottom: 16,
+ }}
+ >
+ 增加
+ </Button>
+ <Table key="table" bordered dataSource={advancedMatchingRules} columns={columns} />
+ {renderModal()}
+ </PanelSection>
+ );
+};
+
+export default MatchingRulesView;
diff --git a/src/pages/Routes/components/Step1/MetaView.tsx b/src/pages/Routes/components/Step1/MetaView.tsx
new file mode 100644
index 0000000..7a74b2b
--- /dev/null
+++ b/src/pages/Routes/components/Step1/MetaView.tsx
@@ -0,0 +1,32 @@
+import React from 'react';
+import Form, { FormInstance } from 'antd/es/form';
+import { Input } from 'antd';
+
+import PanelSection from '../PanelSection';
+import { formItemLayout } from '.';
+import styles from '../../Create.less';
+
+interface Props {
+ form: FormInstance;
+}
+
+const MetaView: React.FC<Props> = ({ form }) => {
+ return (
+ <PanelSection title="名称及其描述">
+ <Form {...formItemLayout} form={form} layout="horizontal" className={styles.stepForm}>
+ <Form.Item
+ label="API 名称"
+ name="name"
+ rules={[{ required: true, message: '请输入 API 名称' }]}
+ >
+ <Input placeholder="请输入 API 名称" />
+ </Form.Item>
+ <Form.Item label="描述" name="desc">
+ <Input.TextArea placeholder="请输入描述" />
+ </Form.Item>
+ </Form>
+ </PanelSection>
+ );
+};
+
+export default MetaView;
diff --git a/src/pages/Routes/components/Step1/RequestConfigView.tsx b/src/pages/Routes/components/Step1/RequestConfigView.tsx
new file mode 100644
index 0000000..359d44a
--- /dev/null
+++ b/src/pages/Routes/components/Step1/RequestConfigView.tsx
@@ -0,0 +1,149 @@
+import React, { useState } from 'react';
+import Form, { FormInstance } from 'antd/es/form';
+import { Row, Checkbox, Button, Col, Input, Space } from 'antd';
+import { CheckboxValueType } from 'antd/lib/checkbox/Group';
+import { CheckboxChangeEvent } from 'antd/lib/checkbox';
+
+import PanelSection from '../PanelSection';
+import { formItemLayout } from '.';
+import styles from '../../Create.less';
+import { httpMethodOptionList } from '@/pages/Routes/constants';
+
+interface Props extends RouteModule.Data {
+ form: FormInstance;
+}
+
+const RequestConfigView: React.FC<Props> = ({ data, form, onChange }) => {
+ const { paths, hosts } = data.step1Data;
+ const [protocolValueList, setProtocolValueList] = useState<RouteModule.RequestProtocol[]>([
+ 'HTTP',
+ 'HTTPS',
+ ]);
+ const protocolList = ['HTTP', 'HTTPS', 'WebSocket'];
+ const [httpMethodList, setHttpMethodList] = useState({
+ checkedList: httpMethodOptionList,
+ indeterminate: false,
+ checkAll: true,
+ });
+
+ const onProtocolChange = (e: CheckboxValueType[]) => {
+ if (!e.includes('HTTP') && !e.includes('HTTPS')) return;
+ setProtocolValueList(e as RouteModule.RequestProtocol[]);
+ };
+ const onMethodsChange = (checkedList: CheckboxValueType[]) => {
+ setHttpMethodList({
+ checkedList: checkedList as RouteModule.HttpMethod[],
+ indeterminate: !!checkedList.length && checkedList.length < httpMethodOptionList.length,
+ checkAll: checkedList.length === httpMethodOptionList.length,
+ });
+ };
+ const onCheckAllChange = (e: CheckboxChangeEvent) => {
+ setHttpMethodList({
+ checkedList: e.target.checked ? httpMethodOptionList : [],
+ indeterminate: false,
+ checkAll: e.target.checked,
+ });
+ };
+
+ const renderHosts = () =>
+ hosts.map((item, index) => (
+ <Row key={`${item + index}`} style={{ marginBottom: '10px' }} gutter={[16, 16]}>
+ <Col span={16}>
+ <Input placeholder="HOST" />
+ </Col>
+ <Col span={4}>
+ <Space>
+ {hosts.length > 1 && (
+ <Button
+ type="primary"
+ danger
+ onClick={() => {
+ onChange({ hosts: hosts.filter((_, _index) => _index !== index) });
+ }}
+ >
+ 删除
+ </Button>
+ )}
+ </Space>
+ </Col>
+ </Row>
+ ));
+
+ const renderPaths = () =>
+ paths.map((item, index) => (
+ <Row key={`${item + index}`} style={{ marginBottom: '10px' }} gutter={[16, 16]}>
+ <Col span={16}>
+ <Input placeholder="请输入 Path" />
+ </Col>
+ <Col span={4}>
+ <Space>
+ <Button
+ type="primary"
+ danger
+ onClick={() => {
+ onChange({ paths: paths.filter((_, _index) => _index !== index) });
+ }}
+ >
+ 删除
+ </Button>
+ </Space>
+ </Col>
+ </Row>
+ ));
+
+ const addPath = () => {
+ onChange({
+ paths: paths.concat(['']),
+ });
+ };
+
+ return (
+ <PanelSection title="请求基础定义">
+ <Form {...formItemLayout} form={form} layout="horizontal" className={styles.stepForm}>
+ <Form.Item label="协议" name="protocol" rules={[{ required: true, message: '请勾选协议' }]}>
+ <Row>
+ <Checkbox.Group
+ options={protocolList}
+ value={protocolValueList}
+ onChange={onProtocolChange}
+ />
+ </Row>
+ </Form.Item>
+ {/* TODO: name */}
+ <Form.Item label="HOST" rules={[{ required: true, message: '请输入 HOST' }]}>
+ {renderHosts()}
+ <Button type="primary" onClick={() => onChange({ hosts: hosts.concat('') })}>
+ 增加
+ </Button>
+ </Form.Item>
+ {/* TODO: name */}
+ <Form.Item label="PATH">
+ {renderPaths()}
+ <Button onClick={addPath} type="primary">
+ 增加
+ </Button>
+ </Form.Item>
+ <Form.Item
+ label="HTTP Methods"
+ name="httpMethods"
+ rules={[{ required: true, message: '请勾选 HTTP Methods' }]}
+ >
+ <Checkbox
+ indeterminate={httpMethodList.indeterminate}
+ onChange={onCheckAllChange}
+ checked={httpMethodList.checkAll}
+ >
+ ANY
+ </Checkbox>
+ <Checkbox.Group
+ options={httpMethodOptionList}
+ value={httpMethodList.checkedList}
+ onChange={onMethodsChange}
+ />
+ </Form.Item>
+ </Form>
+ </PanelSection>
+ );
+};
+
+export default RequestConfigView;
diff --git a/src/pages/Routes/components/Step1/index.tsx b/src/pages/Routes/components/Step1/index.tsx
index aab22e4..bb9b854 100644
--- a/src/pages/Routes/components/Step1/index.tsx
+++ b/src/pages/Routes/components/Step1/index.tsx
@@ -1,17 +1,11 @@
-import React, { useState } from 'react';
-import { Form, Button, Input, Checkbox, Row, Col, Table, Space, Modal, Select } from 'antd';
-import { CheckboxChangeEvent } from 'antd/lib/checkbox';
-import { CheckboxValueType } from 'antd/lib/checkbox/Group';
-import styles from '../../Create.less';
-import PanelSection from '../PanelSection';
+import React from 'react';
+import { Form } from 'antd';
-const { TextArea } = Input;
-const { Option } = Select;
+import MetaView from './MetaView';
+import RequestConfigView from './RequestConfigView';
+import MatchingRulesView from './MatchingRulesView';
-type HttpMethod = 'GET' | 'HEAD' | 'POST' | 'PUT' | 'DELETE' | 'OPTIONS' | 'PATCH';
-type RequestProtocol = 'HTTPS' | 'HTTP';
-
-const formItemLayout = {
+export const formItemLayout = {
labelCol: {
span: 6,
},
@@ -20,330 +14,13 @@ const formItemLayout = {
},
};
-const DEFAULT_MODAL_DATA: RouteModule.MatchingRule = {
- paramsLocation: 'query',
- paramsName: '',
- paramsExpresstion: '==',
- paramsValue: '',
- key: '',
-};
-
-const Step1: React.FC<RouteModule.Data> = ({ data, onChange }) => {
- const { step1Data } = data;
- const { hosts, paths, advancedMatchingRules } = step1Data;
+const Step1: React.FC<RouteModule.Data> = (props) => {
const [form] = Form.useForm();
- const [modalVisible, setModalVisible] = useState(false);
- const [editModalData, setEditModalData] = useState(DEFAULT_MODAL_DATA);
- const [protocolValueList, setProtocolValueList] = useState<RequestProtocol[]>(['HTTP', 'HTTPS']);
- const protocolList = ['HTTP', 'HTTPS', 'WebSocket'];
- const httpMethodOptionList = ['GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'OPTIONS', 'PATCH'];
- const [httpMethodList, setHttpMethodList] = useState({
- checkedList: httpMethodOptionList,
- indeterminate: false,
- checkAll: true,
- });
-
- const handleAdd = () => {
- setModalVisible(true);
- };
-
- const handleEdit = (record: RouteModule.MatchingRule) => {
- setEditModalData(record);
- requestAnimationFrame(() => {
- setModalVisible(true);
- });
- };
-
- const handleRemove = (key: string) => {
- const filteredAdvancedMatchingRules = advancedMatchingRules.filter((item) => item.key !== key);
- onChange({ advancedMatchingRules: filteredAdvancedMatchingRules });
- };
-
- const columns = [
- {
- title: '参数位置',
- dataIndex: 'paramsLocation',
- key: 'paramsLocation',
- },
- {
- title: '参数名称',
- dataIndex: 'paramsName',
- key: 'paramsName',
- },
- {
- title: '运算符',
- dataIndex: 'paramsExpresstion',
- key: 'paramsExpresstion',
- },
- {
- title: '参数值',
- dataIndex: 'paramsValue',
- key: 'paramsValue',
- },
- {
- title: '操作',
- key: 'action',
- render: (_: any, record: RouteModule.MatchingRule) => (
- <Space size="middle">
- <a onClick={() => handleEdit(record)}>编辑</a>
- <a onClick={() => handleRemove(record.key)}>移除</a>
- </Space>
- ),
- },
- ];
-
- const addHost = () => {
- onChange({
- hosts: hosts.concat(''),
- });
- };
-
- const renderHosts = () =>
- hosts.map((item, index) => (
- <Row key={`${item + index}`} style={{ marginBottom: '10px' }} gutter={[16, 16]}>
- <Col span={16}>
- <Input placeholder="HOST" />
- </Col>
- <Col span={4}>
- <Space>
- {hosts.length > 1 && (
- <Button
- type="primary"
- danger
- onClick={() => {
- onChange({ hosts: hosts.filter((_, _index) => _index !== index) });
- }}
- >
- 删除
- </Button>
- )}
- </Space>
- </Col>
- </Row>
- ));
-
- const renderPaths = () =>
- paths.map((item, index) => (
- <Row key={`${item + index}`} style={{ marginBottom: '10px' }} gutter={[16, 16]}>
- <Col span={16}>
- <Input placeholder="请输入 Path" />
- </Col>
- <Col span={4}>
- <Space>
- <Button
- type="primary"
- danger
- onClick={() => {
- onChange({ paths: paths.filter((_, _index) => _index !== index) });
- }}
- >
- 删除
- </Button>
- </Space>
- </Col>
- </Row>
- ));
-
- const addPath = () => {
- onChange({
- paths: paths.concat(['']),
- });
- };
-
- const renderMeta = () => (
- <>
- <PanelSection title="名称及其描述">
- <Form {...formItemLayout} form={form} layout="horizontal" className={styles.stepForm}>
- <Form.Item
- label="API 名称"
- name="name"
- rules={[{ required: true, message: '请输入 API 名称' }]}
- >
- <Input placeholder="请输入 API 名称" />
- </Form.Item>
- <Form.Item label="描述" name="desc">
- <TextArea placeholder="请输入描述" />
- </Form.Item>
- </Form>
- </PanelSection>
- </>
- );
-
- const renderBaseRequestConfig = () => {
- const onProtocolChange = (e: CheckboxValueType[]) => {
- if (!e.includes('HTTP') && !e.includes('HTTPS')) return;
- setProtocolValueList(e as RequestProtocol[]);
- };
- const onMethodsChange = (checkedList: CheckboxValueType[]) => {
- setHttpMethodList({
- checkedList: checkedList as HttpMethod[],
- indeterminate: !!checkedList.length && checkedList.length < httpMethodOptionList.length,
- checkAll: checkedList.length === httpMethodOptionList.length,
- });
- };
- const onCheckAllChange = (e: CheckboxChangeEvent) => {
- setHttpMethodList({
- checkedList: e.target.checked ? httpMethodOptionList : [],
- indeterminate: false,
- checkAll: e.target.checked,
- });
- };
- return (
- <PanelSection title="请求基础定义">
- <Form {...formItemLayout} form={form} layout="horizontal" className={styles.stepForm}>
- <Form.Item
- label="协议"
- name="protocol"
- rules={[{ required: true, message: '请勾选协议' }]}
- >
- <Row>
- <Checkbox.Group
- options={protocolList}
- value={protocolValueList}
- onChange={onProtocolChange}
- />
- </Row>
- </Form.Item>
- {/* TODO: name */}
- <Form.Item label="HOST" rules={[{ required: true, message: '请输入 HOST' }]}>
- {renderHosts()}
- <Button type="primary" onClick={addHost}>
- 增加
- </Button>
- </Form.Item>
- {/* TODO: name */}
- <Form.Item label="PATH">
- {renderPaths()}
- <Button onClick={addPath} type="primary">
- 增加
- </Button>
- </Form.Item>
- <Form.Item
- label="HTTP Methods"
- name="httpMethods"
- rules={[{ required: true, message: '请勾选 HTTP Methods' }]}
- >
- <Checkbox
- indeterminate={httpMethodList.indeterminate}
- onChange={onCheckAllChange}
- checked={httpMethodList.checkAll}
- >
- ANY
- </Checkbox>
- <Checkbox.Group
- options={httpMethodOptionList}
- value={httpMethodList.checkedList}
- onChange={onMethodsChange}
- />
- </Form.Item>
- </Form>
- </PanelSection>
- );
- };
-
- const [modalForm] = Form.useForm();
- const handleOk = () => {
- modalForm.validateFields().then((value) => {
- onChange({
- advancedMatchingRules: advancedMatchingRules.concat({
- ...(value as RouteModule.MatchingRule),
- key: Math.random().toString(36).slice(2),
- }),
- });
- setModalVisible(false);
- });
- };
-
- const handleCancel = () => {
- setModalVisible(false);
- };
-
- const handleClose = () => {
- // TODO: Data not updated in a timely manner
- setEditModalData(DEFAULT_MODAL_DATA);
- modalForm.resetFields();
- };
-
- const renderAdvancedMatchingRules = () => (
- <>
- <PanelSection title="高级路由匹配条件">
- <div>
- <Button
- onClick={handleAdd}
- type="primary"
- style={{
- marginBottom: 16,
- }}
- >
- 新增
- </Button>
- <Table key="table" bordered dataSource={advancedMatchingRules} columns={columns} />
- </div>
- </PanelSection>
- </>
- );
-
return (
<>
- <Modal
- title="新增"
- centered
- visible={modalVisible}
- onOk={handleOk}
- onCancel={handleCancel}
- afterClose={handleClose}
- destroyOnClose
- >
- <Form
- form={modalForm}
- labelCol={{ span: 4 }}
- wrapperCol={{ span: 20 }}
- initialValues={editModalData}
- >
- <Form.Item
- label="参数位置"
- name="paramsLocation"
- rules={[{ required: true, message: '请选择参数位置' }]}
- >
- <Select>
- <Option value="header">header</Option>
- <Option value="query">query</Option>
- <Option value="params">params</Option>
- <Option value="cookie">cookie</Option>
- </Select>
- </Form.Item>
- <Form.Item
- label="参数名称"
- name="paramsName"
- rules={[{ required: true, message: '请输入参数名称' }]}
- >
- <Input />
- </Form.Item>
- <Form.Item
- label="运算符"
- name="paramsExpresstion"
- rules={[{ required: true, message: '请选择运算符' }]}
- >
- <Select>
- <Option value="==">等于</Option>
- <Option value="~=">不等于</Option>
- <Option value=">">大于</Option>
- <Option value="<">小于</Option>
- <Option value="~~">正则匹配</Option>
- </Select>
- </Form.Item>
- <Form.Item
- label="值"
- name="paramsValue"
- rules={[{ required: true, message: '请输入参数值' }]}
- >
- <Input />
- </Form.Item>
- </Form>
- </Modal>
- {renderMeta()}
- {renderBaseRequestConfig()}
- {renderAdvancedMatchingRules()}
+ <MetaView form={form} />
+ <RequestConfigView form={form} {...props} />
+ <MatchingRulesView {...props} />
</>
);
};
diff --git a/src/pages/Routes/constants.ts b/src/pages/Routes/constants.ts
new file mode 100644
index 0000000..15c2f18
--- /dev/null
+++ b/src/pages/Routes/constants.ts
@@ -0,0 +1 @@
+export const httpMethodOptionList = ['GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'OPTIONS', 'PATCH'];
diff --git a/src/pages/Routes/typing.d.ts b/src/pages/Routes/typing.d.ts
index 51fe10d..b3023d1 100644
--- a/src/pages/Routes/typing.d.ts
+++ b/src/pages/Routes/typing.d.ts
@@ -31,6 +31,9 @@ declare namespace RouteModule {
onChange(data: T): void;
}
+ type HttpMethod = 'GET' | 'HEAD' | 'POST' | 'PUT' | 'DELETE' | 'OPTIONS' | 'PATCH';
+ type RequestProtocol = 'HTTPS' | 'HTTP';
+
type backendAddressItemProps = {
host: '';
port: number;