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;