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

bzp2010 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 0273865d feat: add batch delete function for route (#2502)
0273865d is described below

commit 0273865d54eb60a5812c403a887398fe74d30a65
Author: 房森 <[email protected]>
AuthorDate: Tue Aug 9 10:23:16 2022 +0800

    feat: add batch delete function for route (#2502)
    
    Co-authored-by: Young <[email protected]>
    Co-authored-by: Qi Guo <[email protected]>
    Co-authored-by: Feng Han <[email protected]>
---
 .../integration/route/batch-delete-route.spec.js   | 165 +++++++++++++++++++++
 web/src/locales/en-US/component.ts                 |   1 +
 web/src/locales/tr-TR/component.ts                 |   1 +
 web/src/locales/zh-CN/component.ts                 |   1 +
 web/src/pages/Route/List.tsx                       |  32 +++-
 web/src/pages/Route/locales/en-US.ts               |   4 +
 web/src/pages/Route/locales/tr-TR.ts               |   4 +
 web/src/pages/Route/locales/zh-CN.ts               |   4 +
 web/src/pages/Route/service.ts                     |   2 +-
 9 files changed, 210 insertions(+), 4 deletions(-)

diff --git a/web/cypress/integration/route/batch-delete-route.spec.js 
b/web/cypress/integration/route/batch-delete-route.spec.js
new file mode 100644
index 00000000..3a5c603a
--- /dev/null
+++ b/web/cypress/integration/route/batch-delete-route.spec.js
@@ -0,0 +1,165 @@
+/*
+ * 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 */
+
+context('Create and Batch Deletion Routes', () => {
+  const timeout = 5000;
+
+  const selector = {
+    name: '#name',
+    description: '#desc',
+    hosts_0: '#hosts_0',
+    uris_0: '#uris_0',
+    labels_0_labelKey: '#labels_0_labelKey',
+    labels_0_labelValue: '#labels_0_labelValue',
+    nodes_0_host: '#submitNodes_0_host',
+    nodes_0_port: '#submitNodes_0_port',
+    nodes_0_weight: '#submitNodes_0_weight',
+    nameSearchInput: '#name',
+    pathSearchInput: '#uri',
+    drawerBody: '.ant-drawer-wrapper-body',
+    notification: '.ant-notification-notice-message',
+    notificationClose: '.anticon-close',
+  };
+
+  const data = {
+    host1: '11.11.11.11',
+    host2: '12.12.12.12',
+    port: '80',
+    weight: 1,
+    uris: '/get',
+    uris0: '/get0',
+    uris1: '/get1',
+    uris2: '/get2',
+    urisx: '/getx',
+    submitSuccess: 'Submit Successfully',
+    deleteRouteSuccess: 'Delete Route Successfully',
+    test: 'test',
+    test0: 'test0',
+    test1: 'test1',
+    test2: 'test2',
+    testx: 'testx',
+    desc0: 'desc0',
+    desc1: 'desc1',
+    desc2: 'desc2',
+    value0: 'value0',
+    label0_value0: 'label0:value0',
+  };
+
+  beforeEach(() => {
+    cy.login();
+  });
+
+  it('should successfully create 3 routes', function () {
+    cy.visit('/');
+    cy.contains('Route').click();
+    for (let i = 0; i < 3; i += 1) {
+      cy.contains('Create').click();
+      cy.contains('Next').click().click();
+      cy.get(selector.name).type(`test${i}`);
+      cy.get(selector.description).type(`desc${i}`);
+      cy.get(selector.hosts_0).type(data.host1);
+      cy.get(selector.uris_0).clear().type(`/get${i}`);
+
+      // config label
+      cy.contains('Manage').click();
+
+      // eslint-disable-next-line no-loop-func
+      cy.get(selector.drawerBody).within(() => {
+        cy.contains('button', 'Add')
+          .should('not.be.disabled')
+          .click()
+          .then(() => {
+            cy.get(selector.labels_0_labelKey).type(`label${i}`);
+            cy.get(selector.labels_0_labelValue).type(`value${i}`);
+            cy.contains('Confirm').click();
+          });
+      });
+
+      cy.contains('button', 'Next').should('not.be.disabled').click();
+      cy.get(selector.nodes_0_host).type(data.host2, {
+        timeout,
+      });
+      cy.get(selector.nodes_0_port).type(data.port);
+      cy.get(selector.nodes_0_weight).type(data.weight);
+      cy.contains('Next').click();
+      cy.contains('Next').click();
+      cy.contains('Submit').click();
+      cy.contains(data.submitSuccess);
+      cy.contains('Goto List').click();
+      cy.url().should('contains', 'routes/list');
+    }
+  });
+
+  it('should delete the route', function () {
+    cy.visit('/routes/list');
+    cy.contains(data.test0).get('[type="checkbox"]').check();
+    cy.contains(data.test2).get('[type="checkbox"]').check();
+    cy.contains('BatchDeletion Routes').should('be.visible').click({ timeout 
});
+    cy.get(selector.notification).should('contain', data.deleteRouteSuccess);
+    cy.get(selector.notificationClose).should('be.visible').click({
+      force: true,
+      multiple: true,
+    });
+  });
+
+  it('should batch delete the name of the route', function () {
+    cy.visit('/');
+    cy.contains('Route').click();
+    // full match
+    cy.get(selector.nameSearchInput).type(data.test0);
+    cy.contains('Search').click();
+    cy.contains(data.test1).should('not.exist');
+    cy.contains(data.test0).should('not.exist');
+    cy.contains(data.test2).should('not.exist');
+    // partial match
+    cy.get(selector.nameSearchInput).clear().type(data.test2);
+    cy.contains('Search').click();
+    cy.contains(data.test0).should('not.exist');
+    cy.contains(data.test1).should('not.exist');
+    cy.contains(data.test2).should('not.exist');
+    // no match
+    cy.get(selector.nameSearchInput).clear().type(data.testx);
+    cy.contains('Search').click();
+    cy.contains(data.test0).should('not.exist');
+    cy.contains(data.test1).should('not.exist');
+    cy.contains(data.test2).should('not.exist');
+  });
+
+  it('should batch delete the path of the route', function () {
+    cy.visit('/');
+    cy.contains('Route').click();
+    // full match
+    cy.get(selector.pathSearchInput).type(data.uris0);
+    cy.contains('Search').click();
+    cy.contains(data.uris1).should('not.exist');
+    cy.contains(data.uris0).should('not.exist');
+    cy.contains(data.uris2).should('not.exist');
+    // partial match
+    cy.get(selector.pathSearchInput).clear().type(data.uris2);
+    cy.contains('Search').click();
+    cy.contains(data.uris0).should('not.exist');
+    cy.contains(data.uris1).should('not.exist');
+    cy.contains(data.uris2).should('not.exist');
+    // no match
+    cy.get(selector.pathSearchInput).clear().type(data.urisx);
+    cy.contains('Search').click();
+    cy.contains(data.uris0).should('not.exist');
+    cy.contains(data.uris1).should('not.exist');
+    cy.contains(data.uris2).should('not.exist');
+  });
+});
diff --git a/web/src/locales/en-US/component.ts 
b/web/src/locales/en-US/component.ts
index 10db5d53..fb117214 100644
--- a/web/src/locales/en-US/component.ts
+++ b/web/src/locales/en-US/component.ts
@@ -53,6 +53,7 @@ export default {
   'component.global.notification.success.message.deleteSuccess': 'Deleted 
Successfully',
   'component.global.create.consumer.success': 'Create Consumer Successfully',
   'component.global.delete.consumer.success': 'Delete Consumer Successfully',
+  'component.global.delete.routes.success':'Delete Route Successfully',
   'component.global.edit.consumer.success': 'Edit Consumer Successfully',
 
   'component.global.steps.stepTitle.basicInformation': 'Basic Information',
diff --git a/web/src/locales/tr-TR/component.ts 
b/web/src/locales/tr-TR/component.ts
index f4df0435..031355cb 100644
--- a/web/src/locales/tr-TR/component.ts
+++ b/web/src/locales/tr-TR/component.ts
@@ -53,6 +53,7 @@ export default {
   'component.global.notification.success.message.deleteSuccess': 'Başarıyla 
silindi',
   'component.global.create.consumer.success': 'Başarıyla oluşturuldu',
   'component.global.delete.consumer.success': 'Başarıyla silindi',
+  'component.global.delete.routes.success':'Sil Yöneltmeler Başarılı',
   'component.global.edit.consumer.success': 'Başarıyla güncellendi',
 
   'component.global.steps.stepTitle.basicInformation': 'Temel Bilgiler',
diff --git a/web/src/locales/zh-CN/component.ts 
b/web/src/locales/zh-CN/component.ts
index 6131a42b..a7902e0c 100644
--- a/web/src/locales/zh-CN/component.ts
+++ b/web/src/locales/zh-CN/component.ts
@@ -60,6 +60,7 @@ export default {
   'component.global.updateTime': '更新时间',
   'component.global.create.consumer.success': '创建消费者成功',
   'component.global.delete.consumer.success': '删除消费者成功',
+  'component.global.delete.routes.success':'删除路由成功',
   'component.global.edit.consumer.success': '配置消费者成功',
 
   'component.global.popconfirm.title.delete': '确定删除该条记录吗?',
diff --git a/web/src/pages/Route/List.tsx b/web/src/pages/Route/List.tsx
index f4f36bd5..a897108d 100755
--- a/web/src/pages/Route/List.tsx
+++ b/web/src/pages/Route/List.tsx
@@ -31,6 +31,7 @@ import {
   Modal,
   Menu,
   Dropdown,
+  Table,
   Tooltip,
 } from 'antd';
 import { history, useIntl } from 'umi';
@@ -96,6 +97,8 @@ const Page: React.FC = () => {
       setSelectedRowKeys(currentSelectKeys);
     },
     preserveSelectedRowKeys: true,
+    selections: [Table.SELECTION_ALL, Table.SELECTION_INVERT, 
Table.SELECTION_NONE],
+    defaultSelectedRowKeys: [1],
   };
 
   const handleTableActionSuccessResponse = (msgTip: string) => {
@@ -306,7 +309,7 @@ const Page: React.FC = () => {
     {
       title: formatMessage({ id: 'component.global.name' }),
       dataIndex: 'name',
-      fixed: 'left',
+      width: 150,
     },
     {
       title: formatMessage({ id: 'component.global.id' }),
@@ -542,6 +545,31 @@ const Page: React.FC = () => {
         actionRef={ref}
         rowKey="id"
         columns={columns}
+        rowSelection={rowSelection}
+        tableAlertRender={() => (
+          <Space size={24}>
+            <span>
+            {formatMessage({ id: 'page.route.chosen' })} 
{selectedRowKeys.length} {formatMessage({ id: 'page.route.item' })}
+            </span>
+          </Space>
+        )}
+        tableAlertOptionRender={() => {
+          return (
+            <Space size={16}>
+              <Button
+               onClick={async () => {
+               await remove(selectedRowKeys).then(() => {
+                handleTableActionSuccessResponse(
+                  `${formatMessage({ id: 
'component.global.delete.routes.success' })}`,
+                );
+               });
+               ref.current?.reloadAndRest?.();
+             }}>
+             {formatMessage({ id: 'page.route.batchDeletion' })}
+             </Button>
+            </Space>
+          );
+        }}
         request={fetchList}
         pagination={{
           onChange: (page, pageSize?) => savePageList(page, pageSize),
@@ -559,9 +587,7 @@ const Page: React.FC = () => {
           </Button>,
           <ListToolbar />,
         ]}
-        rowSelection={rowSelection}
         footer={() => <ListFooter />}
-        tableAlertRender={false}
         scroll={{ x: 1300 }}
       />
       <DebugDrawView
diff --git a/web/src/pages/Route/locales/en-US.ts 
b/web/src/pages/Route/locales/en-US.ts
index 5fcfc284..3ba20111 100644
--- a/web/src/pages/Route/locales/en-US.ts
+++ b/web/src/pages/Route/locales/en-US.ts
@@ -43,6 +43,10 @@ export default {
   'page.route.import': 'Import',
   'page.route.createRoute': 'Create Route',
   'page.route.editRoute': 'Configure Route',
+  'page.route.batchDeletion': 'BatchDeletion Routes',
+  'page.route.unSelect': 'Unselect',
+  'page.route.item': 'items',
+  'page.route.chosen':'chosen',
 
   'page.route.input.placeholder.parameterNameHttpHeader': 'Request header 
name, for example: HOST',
   'page.route.input.placeholder.parameterNameRequestParameter': 'Parameter 
name, for example: id',
diff --git a/web/src/pages/Route/locales/tr-TR.ts 
b/web/src/pages/Route/locales/tr-TR.ts
index d14f3d00..6bd6e6a2 100644
--- a/web/src/pages/Route/locales/tr-TR.ts
+++ b/web/src/pages/Route/locales/tr-TR.ts
@@ -43,6 +43,10 @@ export default {
   'page.route.import': 'İçe aktar',
   'page.route.createRoute': 'Yeni yönlendirme oluştur',
   'page.route.editRoute': 'Yönlendirme düzenle',
+  'page.route.batchDeletion': 'Toplu yolu sil',
+  'page.route.unSelect': 'Seçilme',
+  'page.route.item': 'term',
+  'page.route.chosen':'Seçili',
 
   'page.route.input.placeholder.parameterNameHttpHeader': 'Request header adı 
örn :HOST',
   'page.route.input.placeholder.parameterNameRequestParameter': 'Parameter adı 
örn :userId',
diff --git a/web/src/pages/Route/locales/zh-CN.ts 
b/web/src/pages/Route/locales/zh-CN.ts
index dce4a763..3b5bc470 100644
--- a/web/src/pages/Route/locales/zh-CN.ts
+++ b/web/src/pages/Route/locales/zh-CN.ts
@@ -51,6 +51,10 @@ export default {
   'page.route.import': '导入',
   'page.route.createRoute': '创建路由',
   'page.route.editRoute': '编辑路由',
+  'page.route.batchDeletion': '批量删除路由',
+  'page.route.unSelect': '取消选择',
+  'page.route.item': '项',
+  'page.route.chosen':'已选择',
 
   // button
   'page.route.button.returnList': '返回路由列表',
diff --git a/web/src/pages/Route/service.ts b/web/src/pages/Route/service.ts
index ae317c5d..bba3681c 100644
--- a/web/src/pages/Route/service.ts
+++ b/web/src/pages/Route/service.ts
@@ -59,7 +59,7 @@ export const fetchList = ({ current = 1, pageSize = 10, 
...res }) => {
   });
 };
 
-export const remove = (rid: string) => request(`/routes/${rid}`, { method: 
'DELETE' });
+export const remove = (rid: string[]) => request(`/routes/${rid}`, { method: 
'DELETE' });
 
 export const checkUniqueName = (name = '', exclude = '') =>
   request('/notexist/routes', {

Reply via email to