You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by su...@apache.org on 2021/03/21 01:00:37 UTC

[apisix-dashboard] branch master updated: feat: save list page's meta status (#1584)

This is an automated email from the ASF dual-hosted git repository.

sunyi pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix-dashboard.git


The following commit(s) were added to refs/heads/master by this push:
     new 3da7370  feat: save list page's meta status (#1584)
3da7370 is described below

commit 3da737017121632a47a28bcd3460a10f6f309250
Author: guoqqqi <72...@users.noreply.github.com>
AuthorDate: Sun Mar 21 09:00:28 2021 +0800

    feat: save list page's meta status (#1584)
---
 web/cypress/fixtures/selector.json                 |  9 +-
 .../plugin/create-edit-delete-plugin.spec.js       |  6 +-
 .../service/create-edit-delete-service.spec.js     |  7 +-
 .../service/save-paginator-status.spec.js          | 98 ++++++++++++++++++++++
 web/src/pages/Consumer/List.tsx                    | 18 +++-
 web/src/pages/Plugin/List.tsx                      | 16 ++++
 web/src/pages/PluginTemplate/List.tsx              | 16 ++++
 web/src/pages/Route/List.tsx                       | 20 ++++-
 web/src/pages/SSL/List.tsx                         | 18 +++-
 web/src/pages/Service/List.tsx                     | 18 +++-
 web/src/pages/Upstream/List.tsx                    | 18 +++-
 11 files changed, 232 insertions(+), 12 deletions(-)

diff --git a/web/cypress/fixtures/selector.json b/web/cypress/fixtures/selector.json
index 15de3f7..121adf1 100644
--- a/web/cypress/fixtures/selector.json
+++ b/web/cypress/fixtures/selector.json
@@ -71,5 +71,12 @@
   "customSelector": "[title=Custom]",
   "notificationClose": ".anticon-close",
   "redirectURIInput": "#redirectURI",
-  "redirectCodeSelector": "#ret_code"
+  "redirectCodeSelector": "#ret_code",
+
+  "paginationOptions": ".ant-pagination-options",
+  "fiftyPerPage": "[title=\"50 / page\"]",
+  "twentyPerPage": "[title=\"20 / page\"]",
+  "pageList": ".ant-table-pagination-right",
+  "pageTwo": ".ant-pagination-item-2",
+  "pageTwoActived": ".ant-pagination-item-2.ant-pagination-item-active"
 }
diff --git a/web/cypress/integration/plugin/create-edit-delete-plugin.spec.js b/web/cypress/integration/plugin/create-edit-delete-plugin.spec.js
index dcde1b4..136ab49 100644
--- a/web/cypress/integration/plugin/create-edit-delete-plugin.spec.js
+++ b/web/cypress/integration/plugin/create-edit-delete-plugin.spec.js
@@ -52,8 +52,12 @@ context('Create and Delete Plugin List', () => {
 
   it('should delete plugin list', function () {
     cy.visit('/plugin/list');
-
     cy.get(this.domSelector.refresh).click();
+    cy.get(this.domSelector.paginationOptions).click();
+    cy.contains('50 / page').should('be.visible').click();
+    cy.get(this.domSelector.fiftyPerPage).should('exist');
+    cy.location('href').should('include', 'pageSize=50');
+
     cy.get(this.domSelector.deleteButton, { timeout }).should('exist').each(function ($el) {
       cy.wrap($el).click().click({ timeout });
       cy.contains('button', 'Confirm').click({ force: true });
diff --git a/web/cypress/integration/service/create-edit-delete-service.spec.js b/web/cypress/integration/service/create-edit-delete-service.spec.js
index e7e3507..83f5641 100644
--- a/web/cypress/integration/service/create-edit-delete-service.spec.js
+++ b/web/cypress/integration/service/create-edit-delete-service.spec.js
@@ -16,7 +16,7 @@
  */
 /* eslint-disable no-undef */
 
-context('create and delete service ', () => {
+context('Create and Delete Service ', () => {
   beforeEach(() => {
     cy.login();
 
@@ -50,7 +50,7 @@ context('create and delete service ', () => {
     cy.get(this.domSelector.drawer).should('be.visible');
 
     cy.get(this.domSelector.codemirrorScroll).within(() => {
-      cy.contains('upstream').should("exist");
+      cy.contains('upstream').should('exist');
       cy.contains(this.data.serviceName).should('exist');
     });
   });
@@ -65,7 +65,7 @@ context('create and delete service ', () => {
 
     // Confirm whether the created data is saved.
     cy.get(this.domSelector.nodes_0_host).should('value', this.data.ip1);
-    cy.get(this.domSelector.description).should('value', this.data.description)
+    cy.get(this.domSelector.description).should('value', this.data.description);
     cy.get(this.domSelector.name).clear().type(this.data.serviceName2);
     cy.get(this.domSelector.description).clear().type(this.data.description2);
     cy.get(this.domSelector.nodes_0_host).click();
@@ -101,4 +101,3 @@ context('create and delete service ', () => {
     cy.get(this.domSelector.notification).should('contain', this.data.deleteServiceSuccess);
   });
 });
-
diff --git a/web/cypress/integration/service/save-paginator-status.spec.js b/web/cypress/integration/service/save-paginator-status.spec.js
new file mode 100644
index 0000000..895768b
--- /dev/null
+++ b/web/cypress/integration/service/save-paginator-status.spec.js
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/* eslint-disable no-undef */
+import defaultSettings from '../../../config/defaultSettings';
+
+context('Save Paginator Status', () => {
+  const timeout = 300;
+  const token = localStorage.getItem('token');
+  const { SERVE_ENV = 'dev' } = Cypress.env();
+
+  beforeEach(() => {
+    cy.login();
+
+    cy.fixture('selector.json').as('domSelector');
+    cy.fixture('data.json').as('data');
+  });
+
+  it('should create 11 test services', function () {
+    cy.visit('/');
+    cy.contains('Service').click();
+
+    for (let i = 0; i <= 10; i++) {
+      cy.request({
+        method: 'POST',
+        url: `${defaultSettings.serveUrlMap[SERVE_ENV]}/apisix/admin/services`,
+        headers: {
+          Authorization: token,
+        },
+        body: {
+          upstream: {
+            nodes: {"39.97.63.215:80": 1},
+            timeout: {connect: 6, read: 6, send: 6},
+            type: 'roundrobin',
+            pass_host: 'pass',
+          },
+          enable_websocket: true,
+          name: `${this.data.serviceName}${i}`,
+        }
+      }).then((res) => {
+        expect(res.body.code).to.equal(0);
+      });
+    }
+    cy.get(this.domSelector.pageList).should('be.visible');
+  });
+
+  it('should save paginator\' status', function () {
+    cy.visit('/');
+    cy.contains('Service').click();
+
+    // Test page status
+    cy.get(this.domSelector.pageList).should('be.visible');
+    cy.get(this.domSelector.pageTwo).click();
+    cy.get(this.domSelector.pageTwoActived).should('exist');
+    cy.location('href').should('include', 'page=2');
+
+    cy.reload();
+    cy.get(this.domSelector.pageTwoActived).should('exist');
+    cy.location('href').should('include', 'page=2');
+
+    // Test pageSize status
+    cy.get(this.domSelector.paginationOptions).click();
+    cy.contains('20 / page').should('be.visible').click();
+    cy.get(this.domSelector.twentyPerPage).should('exist');
+    cy.location('href').should('include', 'pageSize=20');
+
+    cy.reload();
+    cy.get(this.domSelector.twentyPerPage).should('exist');
+    cy.location('href').should('include', 'pageSize=20');
+  });
+
+  it('should delete test service', function () {
+    cy.visit('/service/list?page=1&pageSize=20');
+    cy.reload();
+    cy.contains('Service List').should('be.visible');
+    cy.get(this.domSelector.deleteButton, { timeout: 300 })
+      .should('exist')
+      .each(function ($el) {
+        cy.wrap($el).click().click({ timeout });
+        cy.contains('button', 'Confirm').click({ force: true });
+        cy.get(this.domSelector.notification).should('contain', this.data.deleteServiceSuccess);
+        cy.get(this.domSelector.notificationCloseIcon).click().should('not.exist');
+      });
+  });
+});
diff --git a/web/src/pages/Consumer/List.tsx b/web/src/pages/Consumer/List.tsx
index 26214d0..0957383 100644
--- a/web/src/pages/Consumer/List.tsx
+++ b/web/src/pages/Consumer/List.tsx
@@ -14,13 +14,14 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import React, { useRef, useState } from 'react';
+import React, { useEffect, useRef, useState } from 'react';
 import { PageContainer } from '@ant-design/pro-layout';
 import ProTable from '@ant-design/pro-table';
 import type { ProColumns, ActionType } from '@ant-design/pro-table';
 import { Popconfirm, Button, notification } from 'antd';
 import { history, useIntl } from 'umi';
 import { PlusOutlined } from '@ant-design/icons';
+import querystring from 'query-string'
 
 import { timestampToLocaleString } from '@/helpers';
 import { RawDataEditor } from '@/components/RawDataEditor';
@@ -32,6 +33,16 @@ const Page: React.FC = () => {
   const { formatMessage } = useIntl();
   const [rawDataEditorVisible, setRawDataEditorVisible] = useState(false);
   const [rawData, setRawData] = useState({});
+  const [paginationConfig, setPaginationConfig] = useState({ pageSize: 10, current: 1 });
+
+  const savePageList = (page = 1, pageSize = 10) => {
+    history.replace(`/consumer/list?page=${page}&pageSize=${pageSize}`);
+  };
+
+  useEffect(() => {
+    const { page = 1, pageSize = 10 } = querystring.parse(window.location.search);
+    setPaginationConfig({ pageSize: Number(pageSize), current: Number(page) });
+  }, [window.location.search]);
 
   const columns: ProColumns<ConsumerModule.ResEntity>[] = [
     {
@@ -100,6 +111,11 @@ const Page: React.FC = () => {
         columns={columns}
         rowKey="id"
         request={fetchList}
+        pagination={{
+          onChange: (page, pageSize?) => savePageList(page, pageSize),
+          pageSize: paginationConfig.pageSize,
+          current: paginationConfig.current,
+        }}
         search={{
           searchText: formatMessage({ id: 'component.global.search' }),
           resetText: formatMessage({ id: 'component.global.reset' }),
diff --git a/web/src/pages/Plugin/List.tsx b/web/src/pages/Plugin/List.tsx
index 31e4fc0..b8917c8 100644
--- a/web/src/pages/Plugin/List.tsx
+++ b/web/src/pages/Plugin/List.tsx
@@ -22,6 +22,7 @@ import type { ActionType, ProColumns } from '@ant-design/pro-table';
 import { Button, Popconfirm, Space, notification } from 'antd';
 import { PlusOutlined } from '@ant-design/icons';
 import { omit } from 'lodash';
+import querystring from 'query-string'
 
 import PluginDetail from '@/components/Plugin/PluginDetail';
 
@@ -34,6 +35,11 @@ const Page: React.FC = () => {
   const [initialData, setInitialData] = useState({});
   const [pluginList, setPluginList] = useState<PluginComponent.Meta[]>([]);
   const [name, setName] = useState('');
+  const [paginationConfig, setPaginationConfig] = useState({ pageSize: 10, current: 1 });
+
+  const savePageList = (page = 1, pageSize = 10) => {
+    history.replace(`/plugin/list?page=${page}&pageSize=${pageSize}`);
+  };
 
   useEffect(() => {
     fetchPluginList().then(setPluginList);
@@ -51,6 +57,11 @@ const Page: React.FC = () => {
     }
   }, [name]);
 
+  useEffect(() => {
+    const { page = 1, pageSize = 10 } = querystring.parse(window.location.search);
+    setPaginationConfig({ pageSize: Number(pageSize), current: Number(page) });
+  }, [window.location.search]);
+
   const columns: ProColumns<PluginModule.TransformedPlugin>[] = [
     {
       title: formatMessage({ id: 'component.global.name' }),
@@ -142,6 +153,11 @@ const Page: React.FC = () => {
         search={false}
         columns={columns}
         request={fetchList}
+        pagination={{
+          onChange: (page, pageSize?) => savePageList(page, pageSize),
+          pageSize: paginationConfig.pageSize,
+          current: paginationConfig.current,
+        }}
         toolBarRender={() => [
           <Button type="primary" onClick={() => history.push('/plugin/market')}>
             <PlusOutlined />
diff --git a/web/src/pages/PluginTemplate/List.tsx b/web/src/pages/PluginTemplate/List.tsx
index 6e0fa45..69f92d1 100644
--- a/web/src/pages/PluginTemplate/List.tsx
+++ b/web/src/pages/PluginTemplate/List.tsx
@@ -21,14 +21,25 @@ import ProTable from '@ant-design/pro-table';
 import type { ActionType, ProColumns } from '@ant-design/pro-table';
 import { Button, notification, Popconfirm, Select, Space, Tag } from 'antd';
 import { PlusOutlined } from '@ant-design/icons';
+import querystring from 'query-string'
 
 import { fetchList, remove, fetchLabelList } from './service';
 
 const Page: React.FC = () => {
   const ref = useRef<ActionType>();
   const [labelList, setLabelList] = useState<LabelList>({});
+  const [paginationConfig, setPaginationConfig] = useState({ pageSize: 10, current: 1 });
   const { formatMessage } = useIntl();
 
+  const savePageList = (page = 1, pageSize = 10) => {
+    history.replace(`/plugin-template/list?page=${page}&pageSize=${pageSize}`);
+  };
+
+  useEffect(() => {
+    const { page = 1, pageSize = 10 } = querystring.parse(window.location.search);
+    setPaginationConfig({ pageSize: Number(pageSize), current: Number(page) });
+  }, [window.location.search]);
+
   useEffect(() => {
     fetchLabelList().then(setLabelList);
   }, []);
@@ -142,6 +153,11 @@ const Page: React.FC = () => {
         rowKey="id"
         columns={columns}
         request={fetchList}
+        pagination={{
+          onChange: (page, pageSize?) => savePageList(page, pageSize),
+          pageSize: paginationConfig.pageSize,
+          current: paginationConfig.current,
+        }}
         search={{
           searchText: formatMessage({ id: 'component.global.search' }),
           resetText: formatMessage({ id: 'component.global.reset' }),
diff --git a/web/src/pages/Route/List.tsx b/web/src/pages/Route/List.tsx
index dcc8e3d..c799bf2 100644
--- a/web/src/pages/Route/List.tsx
+++ b/web/src/pages/Route/List.tsx
@@ -37,6 +37,7 @@ import { js_beautify } from 'js-beautify';
 import yaml from 'js-yaml';
 import moment from 'moment';
 import { saveAs } from 'file-saver';
+import querystring from 'query-string'
 
 import { timestampToLocaleString } from '@/helpers';
 import type { RcFile } from 'antd/lib/upload';
@@ -75,11 +76,21 @@ const Page: React.FC = () => {
   const [showImportModal, setShowImportModal] = useState(false);
   const [rawDataEditorVisible, setRawDataEditorVisible] = useState(false);
   const [rawData, setRawData] = useState({});
+  const [paginationConfig, setPaginationConfig] = useState({ pageSize: 10, current: 1 });
+
+  const savePageList = (page = 1, pageSize = 10) => {
+    history.replace(`/routes/list?page=${page}&pageSize=${pageSize}`);
+  };
 
   useEffect(() => {
     fetchLabelList().then(setLabelList);
   }, []);
 
+  useEffect(() => {
+    const { page = 1, pageSize = 10 } = querystring.parse(window.location.search);
+    setPaginationConfig({ pageSize: Number(pageSize), current: Number(page) });
+  }, [window.location.search]);
+
   const rowSelection = {
     selectedRowKeys,
     onChange: (currentSelectKeys: string[]) => {
@@ -317,8 +328,8 @@ const Page: React.FC = () => {
           {record.status ? (
             <Tag color="green">{formatMessage({ id: 'page.route.published' })}</Tag>
           ) : (
-              <Tag color="red">{formatMessage({ id: 'page.route.unpublished' })}</Tag>
-            )}
+            <Tag color="red">{formatMessage({ id: 'page.route.unpublished' })}</Tag>
+          )}
         </>
       ),
       renderFormItem: (_, { type }) => {
@@ -421,6 +432,11 @@ const Page: React.FC = () => {
         rowKey="id"
         columns={columns}
         request={fetchList}
+        pagination={{
+          onChange: (page, pageSize?) => savePageList(page, pageSize),
+          pageSize: paginationConfig.pageSize,
+          current: paginationConfig.current,
+        }}
         search={{
           searchText: formatMessage({ id: 'component.global.search' }),
           resetText: formatMessage({ id: 'component.global.reset' }),
diff --git a/web/src/pages/SSL/List.tsx b/web/src/pages/SSL/List.tsx
index f66cfa0..56e5737 100644
--- a/web/src/pages/SSL/List.tsx
+++ b/web/src/pages/SSL/List.tsx
@@ -14,13 +14,14 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import React, { useRef } from 'react';
+import React, { useEffect, useRef, useState } from 'react';
 import { PageHeaderWrapper } from '@ant-design/pro-layout';
 import ProTable from '@ant-design/pro-table';
 import type { ProColumns, ActionType } from '@ant-design/pro-table';
 import { Button, Popconfirm, notification, Tag } from 'antd';
 import { useIntl, history } from 'umi';
 import { PlusOutlined } from '@ant-design/icons';
+import querystring from 'query-string'
 
 import { fetchList, remove as removeSSL } from '@/pages/SSL/service';
 import { timestampToLocaleString } from '@/helpers';
@@ -28,6 +29,16 @@ import { timestampToLocaleString } from '@/helpers';
 const Page: React.FC = () => {
   const tableRef = useRef<ActionType>();
   const { formatMessage } = useIntl();
+  const [paginationConfig, setPaginationConfig] = useState({ pageSize: 10, current: 1 });
+
+  const savePageList = (page = 1, pageSize = 10) => {
+    history.replace(`/ssl/list?page=${page}&pageSize=${pageSize}`);
+  };
+
+  useEffect(() => {
+    const { page = 1, pageSize = 10 } = querystring.parse(window.location.search);
+    setPaginationConfig({ pageSize: Number(pageSize), current: Number(page) });
+  }, [window.location.search]);
 
   const columns: ProColumns<SSLModule.ResponseBody>[] = [
     {
@@ -103,6 +114,11 @@ const Page: React.FC = () => {
         columns={columns}
         actionRef={tableRef}
         request={fetchList}
+        pagination={{
+          onChange: (page, pageSize?) => savePageList(page, pageSize),
+          pageSize: paginationConfig.pageSize,
+          current: paginationConfig.current,
+        }}
         search={{
           searchText: formatMessage({ id: 'component.global.search' }),
           resetText: formatMessage({ id: 'component.global.reset' }),
diff --git a/web/src/pages/Service/List.tsx b/web/src/pages/Service/List.tsx
index 4917fba..467be78 100644
--- a/web/src/pages/Service/List.tsx
+++ b/web/src/pages/Service/List.tsx
@@ -14,13 +14,14 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import React, { useRef, useState } from 'react';
+import React, { useEffect, useRef, useState } from 'react';
 import { history, useIntl } from 'umi';
 import { PageHeaderWrapper } from '@ant-design/pro-layout';
 import ProTable from '@ant-design/pro-table';
 import type { ActionType, ProColumns } from '@ant-design/pro-table';
 import { PlusOutlined } from '@ant-design/icons';
 import { Button, notification, Popconfirm, Space } from 'antd';
+import querystring from 'query-string'
 
 import { RawDataEditor } from '@/components/RawDataEditor';
 import { fetchList, remove } from './service';
@@ -30,6 +31,16 @@ const Page: React.FC = () => {
   const { formatMessage } = useIntl();
   const [rawDataEditorVisible, setRawDataEditorVisible] = useState(false);
   const [rawData, setRawData] = useState({});
+  const [paginationConfig, setPaginationConfig] = useState({ pageSize: 10, current: 1 });
+
+  const savePageList = (page = 1, pageSize = 10) => {
+    history.replace(`/service/list?page=${page}&pageSize=${pageSize}`);
+  };
+
+  useEffect(() => {
+    const { page = 1, pageSize = 10 } = querystring.parse(window.location.search);
+    setPaginationConfig({ pageSize: Number(pageSize), current: Number(page) });
+  }, [window.location.search]);
 
   const columns: ProColumns<ServiceModule.ResponseBody>[] = [
     {
@@ -98,6 +109,11 @@ const Page: React.FC = () => {
         rowKey="id"
         columns={columns}
         request={fetchList}
+        pagination={{
+          onChange: (page, pageSize?) => savePageList(page, pageSize),
+          pageSize: paginationConfig.pageSize,
+          current: paginationConfig.current,
+        }}
         search={{
           searchText: formatMessage({ id: 'component.global.search' }),
           resetText: formatMessage({ id: 'component.global.reset' }),
diff --git a/web/src/pages/Upstream/List.tsx b/web/src/pages/Upstream/List.tsx
index 5158681..ce5d6e7 100644
--- a/web/src/pages/Upstream/List.tsx
+++ b/web/src/pages/Upstream/List.tsx
@@ -14,13 +14,14 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import React, { useRef, useState } from 'react';
+import React, { useEffect, useRef, useState } from 'react';
 import { PageContainer } from '@ant-design/pro-layout';
 import ProTable from '@ant-design/pro-table';
 import type { ProColumns, ActionType } from '@ant-design/pro-table';
 import { Popconfirm, Button, notification } from 'antd';
 import { history, useIntl } from 'umi';
 import { PlusOutlined } from '@ant-design/icons';
+import querystring from 'query-string'
 
 import { RawDataEditor } from '@/components/RawDataEditor';
 import { timestampToLocaleString } from '@/helpers';
@@ -31,9 +32,19 @@ const Page: React.FC = () => {
   const ref = useRef<ActionType>();
   const [rawDataEditorVisible, setRawDataEditorVisible] = useState(false);
   const [rawData, setRawData] = useState({});
+  const [paginationConfig, setPaginationConfig] = useState({ pageSize: 10, current: 1 });
 
   const { formatMessage } = useIntl();
 
+  const savePageList = (page = 1, pageSize = 10) => {
+    history.replace(`/upstream/list?page=${page}&pageSize=${pageSize}`);
+  };
+
+  useEffect(() => {
+    const { page = 1, pageSize = 10 } = querystring.parse(window.location.search);
+    setPaginationConfig({ pageSize: Number(pageSize), current: Number(page) });
+  }, [window.location.search]);
+
   const columns: ProColumns<UpstreamModule.ResponseBody>[] = [
     {
       title: formatMessage({ id: 'page.upstream.list.name' }),
@@ -104,6 +115,11 @@ const Page: React.FC = () => {
         columns={columns}
         rowKey="id"
         request={fetchList}
+        pagination={{
+          onChange: (page, pageSize?) => savePageList(page, pageSize),
+          pageSize: paginationConfig.pageSize,
+          current: paginationConfig.current,
+        }}
         search={{
           searchText: formatMessage({ id: 'component.global.search' }),
           resetText: formatMessage({ id: 'component.global.reset' }),