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

liuxun pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/submarine.git


The following commit(s) were added to refs/heads/master by this push:
     new b58428b  SUBMARINE-461. [WEB] Implement department page with Angular
b58428b is described below

commit b58428b3431a4bd9ec99938dae68f8e4bd4d0c56
Author: jasoonn <[email protected]>
AuthorDate: Sun Apr 12 15:10:10 2020 +0800

    SUBMARINE-461. [WEB] Implement department page with Angular
    
    ### What is this PR for?
    Implement frontend of department page in workbench with Angular.
    
    ### What type of PR is it?
    [Feature]
    
    ### Todos
    
    ### What is the Jira issue?
    https://issues.apache.org/jira/browse/SUBMARINE-461
    
    ### How should this be tested?
    https://travis-ci.org/github/apache/submarine/builds/673989361
    
    ### Screenshots (if appropriate)
    ![](https://i.imgur.com/RiVPkKr.gif)
    
    ### Questions:
    * Does the licenses files need update? No
    * Is there breaking changes for older versions? No
    * Does this needs documentation? No
    
    Author: jasoonn <[email protected]>
    
    Closes #260 from jasoonn/SUBMARINE-461 and squashes the following commits:
    
    56e66e8 [jasoonn] update
---
 .../apache/submarine/integration/departmentIT.java |  73 +++++++
 .../app/pages/workbench/data/data.component.scss   |   6 +-
 .../src/app/pages/workbench/job/job.component.scss |   6 +-
 .../manager/department/department.component.html   | 181 +++++++++++++++
 .../department/department.component.scss}          |  60 +++--
 .../manager/department/department.component.ts     | 242 +++++++++++++++++++++
 .../workbench/manager/manager-routing.module.ts    |   5 +
 .../pages/workbench/manager/manager.component.ts   |  16 ++
 .../app/pages/workbench/manager/manager.module.ts  |   3 +-
 .../src/app/pages/workbench/workbench.component.ts |   4 +
 10 files changed, 557 insertions(+), 39 deletions(-)

diff --git 
a/submarine-test/test-e2e/src/test/java/org/apache/submarine/integration/departmentIT.java
 
b/submarine-test/test-e2e/src/test/java/org/apache/submarine/integration/departmentIT.java
new file mode 100644
index 0000000..32764b4
--- /dev/null
+++ 
b/submarine-test/test-e2e/src/test/java/org/apache/submarine/integration/departmentIT.java
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+
+package org.apache.submarine.integration;
+
+import org.apache.submarine.AbstractSubmarineIT;
+import org.apache.submarine.WebDriverManager;
+import org.openqa.selenium.support.ui.ExpectedConditions;
+import org.openqa.selenium.support.ui.WebDriverWait;
+import org.openqa.selenium.By;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+import org.testng.Assert;
+
+public class departmentIT extends AbstractSubmarineIT{
+
+  public final static Logger LOG = LoggerFactory.getLogger(departmentIT.class);
+
+  @BeforeClass
+  public static void startUp(){
+    LOG.info("[Testcase]: departmentIT");
+    driver =  WebDriverManager.getWebDriver();
+  }
+
+  @AfterClass
+  public static void tearDown(){
+    driver.quit();
+  }
+
+  @Test
+  public void dataNavigation() throws Exception {
+    // Login
+    LOG.info("Login");
+    pollingWait(By.cssSelector("input[ng-reflect-name='userName']"), 
MAX_BROWSER_TIMEOUT_SEC).sendKeys("admin");
+    pollingWait(By.cssSelector("input[ng-reflect-name='password']"), 
MAX_BROWSER_TIMEOUT_SEC).sendKeys("admin");
+    clickAndWait(By.cssSelector("button[class='login-form-button ant-btn 
ant-btn-primary']"));
+    pollingWait(By.cssSelector("a[routerlink='/workbench/dashboard']"), 
MAX_BROWSER_TIMEOUT_SEC);
+
+    // Routing to department page
+    pollingWait(By.xpath("//span[contains(text(), \"Manager\")]"), 
MAX_BROWSER_TIMEOUT_SEC).click();
+    WebDriverWait wait = new WebDriverWait( driver, 60);
+    pollingWait(By.xpath("//a[@href='/workbench/manager/department']"), 
MAX_BROWSER_TIMEOUT_SEC).click();
+    
wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//span[@class='ant-breadcrumb-link
 ng-star-inserted']")));
+    Assert.assertEquals(driver.getCurrentUrl(), 
"http://localhost:8080/workbench/manager/department";);
+
+    // Test create new department
+    pollingWait(By.xpath("//button[@id='btnAddDepartment']"), 
MAX_BROWSER_TIMEOUT_SEC).click();
+    pollingWait(By.xpath("//input[@id='codeInput']"), 
MAX_BROWSER_TIMEOUT_SEC).sendKeys("e2e test code");
+    pollingWait(By.xpath("//input[@id='nameInput']"), 
MAX_BROWSER_TIMEOUT_SEC).sendKeys("e2e test name");
+    pollingWait(By.xpath("//button[@id='btnSubmit']"), 
MAX_BROWSER_TIMEOUT_SEC).click();
+
+    //TODO(jasoonn): Assert whether new project be created
+
+    
+  }
+}
diff --git 
a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/data/data.component.scss
 
b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/data/data.component.scss
index 6d4337c..f48cd26 100644
--- 
a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/data/data.component.scss
+++ 
b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/data/data.component.scss
@@ -24,9 +24,9 @@
  }
 
  .trainingDiv{
-    margin-top: 25px;
-    margin-left: 20px;
-    margin-right: 20px;
+    margin-top: 16px;
+    margin-left: 25px;
+    margin-right: 25px;
     padding: 10px;
     background-color:white;
   }
diff --git 
a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/job/job.component.scss
 
b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/job/job.component.scss
index 88ee4fc..64d6f27 100644
--- 
a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/job/job.component.scss
+++ 
b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/job/job.component.scss
@@ -24,9 +24,9 @@
  }
 
  #jobData{
-    margin-top: 25px;
-    margin-left: 20px;
-    margin-right: 20px;
+    margin-top: 16px;
+    margin-left: 25px;
+    margin-right: 25px;
     background-color:white;
     padding-left: 10px;
     padding-right: 10px;
diff --git 
a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/department/department.component.html
 
b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/department/department.component.html
new file mode 100644
index 0000000..2cb5571
--- /dev/null
+++ 
b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/department/department.component.html
@@ -0,0 +1,181 @@
+<!--
+  ~ 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.
+  -->
+
+
+<nz-card>
+    <div class="department-header">
+        <form nz-form [nzLayout]="'inline'" [formGroup]="dapartmentDictForm">
+          <nz-form-item>
+            <nz-form-label>Code</nz-form-label>
+            <nz-form-control>
+              <input nz-input formControlName="departmentCode" style="width: 
400px;" placeholder="Please enter department code"/>
+            </nz-form-control>
+          </nz-form-item>
+          <nz-form-item>
+            <nz-form-label>Name</nz-form-label>
+            <nz-form-control>
+              <input nz-input formControlName="departmentName" style="width: 
400px;" placeholder="Please enter department name"/>
+            </nz-form-control>
+          </nz-form-item>
+          <nz-form-item>
+            <nz-form-control>
+              <button nz-button nzType="primary" (click)="queryDepartment()">
+                <i nz-icon nzType="search"></i>
+                Query
+              </button>
+              <button nz-button id='btnAddDepartment' style="margin-left: 8px" 
(click)="isVisible=true;editMode = false;">
+                <i nz-icon nzType="plus"></i>
+                Add
+              </button>
+            </nz-form-control>
+          </nz-form-item>
+        </form>
+    </div>
+
+    <nz-table *ngIf="isLoading==false" #expandTable nzBordered 
[nzData]="isExpandTable ? listOfMapData : filterArr">
+      <thead>
+        <tr>
+          <th nzWidth="15%">Code</th>
+          <th nzWidth="10%">Name</th>
+          <th nzWidth="25%">Parent Deptartment</th>
+          <th nzWidth="25%">Description</th>
+          <th nzWidth="10%">Status</th>
+          <th>Action</th>
+        </tr>
+      </thead>
+      <tbody *ngIf="isExpandTable">
+        <ng-container *ngFor="let data of expandTable.data">
+          <ng-container *ngFor="let item of mapOfExpandedData[data.key]">
+            <tr *ngIf="(item.parent && item.parent.expand) || !item.parent">
+              <td
+                [nzIndentSize]="item.level * 20"
+                [nzShowExpand]="item.children!==none"
+                [(nzExpand)]="item.expand"
+                (nzExpandChange)="collapse(mapOfExpandedData[data.key], item, 
$event)"
+              >
+                {{ item.title }}
+              </td>
+              <td>{{ item.code }}</td>
+              <td>{{ showParent(item) }}</td>
+              <td>{{ item.description }}</td>
+              <td><nz-tag [nzColor]="item.status==='Deleted'? 'red' : 
'blue'">{{ item.status }}</nz-tag></td>
+              <td>
+                <button nz-button nzType="link" (click)="editDepartment(item)" 
style="padding-left: 2px;padding-right: 5px;">Edit</button>
+                |
+                <button *ngIf="item.status==='Available'" nz-button 
nzType="link" (click)="deleteDepartment(item)" style="padding-left: 
2px;padding-right: 5px;">Delete</button>
+                <button *ngIf="item.status==='Deleted'" nz-button 
nzType="link" (click)="restoreDepartment(item)" style="padding-left: 
2px;padding-right: 5px;">Restore</button>
+              </td>
+            </tr>
+          </ng-container>
+        </ng-container>
+      </tbody>
+
+      <tbody *ngIf="!isExpandTable">
+        <ng-container *ngFor="let item of expandTable.data">
+          <tr>
+            <td>{{ item.title }}</td>
+            <td>{{ item.code }}</td>
+            <td>{{ showParent(item) }}</td>
+            <td>{{ item.description }}</td>
+            <td><nz-tag [nzColor]="item.status==='Deleted'? 'red' : 'blue'">{{ 
item.status }}</nz-tag></td>
+            <td>
+              <button nz-button nzType="link" (click)="editDepartment(item)" 
style="padding-left: 2px;padding-right: 5px;">Edit</button>
+              |
+              <button *ngIf="item.status==='Available'" nz-button 
nzType="link" (click)="deleteDepartment(item)" style="padding-left: 
2px;padding-right: 5px;">Delete</button>
+              <button *ngIf="item.status==='Deleted'" nz-button nzType="link" 
(click)="restoreDepartment(item)" style="padding-left: 2px;padding-right: 
5px;">Restore</button>
+            </td>
+          </tr>
+        </ng-container>
+      </tbody>
+    </nz-table>
+    <nz-spin *ngIf="isLoading" [nzSpinning]="true" style="height: 50px;">
+    </nz-spin>
+</nz-card>
+
+
+<nz-drawer
+    [(nzVisible)]="isVisible"
+    nzClosable = "true"
+    nzTitle="Add Department"
+    (nzOnClose)="isVisible=false;"
+    [nzWidth]="700"
+>
+<form [formGroup]="newDepartmentForm">
+  <div>
+    <label class="form-label"><span class="red-star">* </span> Code:</label>
+    <input 
+      nz-input 
+      id='codeInput'
+      type="text" 
+      placeholder="Please enter department code" 
+      style="margin-top: 25px;width: 70%;" 
+      class="form-control" 
+      formControlName="code">
+  </div>
+  <div>
+    <label class="form-label"><span class="red-star">* </span> Name:</label>
+    <input 
+      nz-input 
+      id='nameInput'
+      type="text" 
+      placeholder="Please enter department name" 
+      style="margin-top: 25px;width: 70%;" 
+      class="form-control" 
+      formControlName="name"
+    >
+  </div>
+  <div>
+    <label class="form-label"> Parent:</label>
+    <nz-tree-select
+      style="width: 70%;margin-top: 25px;"
+      [nzExpandedKeys]="expandKeys"
+      [nzNodes]="listOfMapData"
+      nzShowSearch
+      nzPlaceHolder="Please select"
+      class="form-control" 
+      formControlName="parent"
+    >
+    </nz-tree-select>
+  </div>
+  <div>
+    <label class="form-label" > Sort:</label>
+    <nz-input-number class="form-control" formControlName="sort" 
style="margin-top: 25px;"></nz-input-number>
+  </div>
+  <div>
+    <label class="form-label" > Status:</label>
+    <nz-switch nzCheckedChildren="Available" nzUnCheckedChildren="Deleted" 
class="form-control" formControlName="status" style="margin-top: 
30px;"></nz-switch>
+  </div>
+  <div>
+    <label class="form-label">Description:</label>
+    <textarea rows="6" nz-input class="form-control" 
formControlName="description" style="width: 70%;margin-top: 25px;"></textarea>
+  </div>
+
+
+</form>
+<div class="footer">
+  <button type="button" (click)="isVisible = false;" class="ant-btn" 
style="margin-right: 8px;"><span>Cancel</span></button>
+  <button 
+    type="button" 
+    (click)="submitDepartment()" 
+    class="ant-btn ant-btn-primary" 
+    id='btnSubmit'
+    [disabled]="!(newDepartmentForm.get('name')).valid || 
!(newDepartmentForm.get('code').valid)"
+  ><span>Submit</span></button>
+</div>
+</nz-drawer>
diff --git 
a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/data/data.component.scss
 
b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/department/department.component.scss
similarity index 58%
copy from 
submarine-workbench/workbench-web-ng/src/app/pages/workbench/data/data.component.scss
copy to 
submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/department/department.component.scss
index 6d4337c..7d6295a 100644
--- 
a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/data/data.component.scss
+++ 
b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/department/department.component.scss
@@ -17,41 +17,37 @@
  * under the License.
  */
 
- #dataOuter{
-    background-color: white;
-    padding-left: 30px;
-    padding-top: 20px;
+ .department-header {
+    margin-bottom: 16px;
  }
 
- .trainingDiv{
-    margin-top: 25px;
-    margin-left: 20px;
-    margin-right: 20px;
-    padding: 10px;
-    background-color:white;
+ .footer {
+    position: absolute;
+    bottom: 0px;
+    width: 100%;
+    border-top: 1px solid rgb(232, 232, 232);
+    padding: 10px 16px;
+    text-align: right;
+    left: 0px;
+    background: #fff;
   }
-  
-  .horizontal_dotted_line{
-   border-top: 2px dotted whitesmoke;
-   color:transparent;
- } 
 
- .red-star {
-   margin-top: 20px;
-   color: red;
-}
+  input.ng-invalid.ng-touched {
+    border: 1px solid red;
+  }
 
-.form-label {
-   float:left;
-   width:200px;
-   text-align: right;
-   padding-right: 12px;
-   margin-top: 32px;
-   margin-left: 25%;
-   clear: left;
-   color: black;
-}
+  .form-label {
+    float:left;
+    width:20%;
+    text-align: right;
+    padding-right: 12px;
+    margin-top: 32px;
+    margin-left: 10px;
+    clear: left;
+    color: black;
+  }
 
-input.ng-invalid.ng-touched {
-   border: 1px solid red;
- }
+  .red-star {
+    margin-top: 20px;
+    color: red;
+  }
diff --git 
a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/department/department.component.ts
 
b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/department/department.component.ts
new file mode 100644
index 0000000..fe7753f
--- /dev/null
+++ 
b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/department/department.component.ts
@@ -0,0 +1,242 @@
+/*!
+ * 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.
+ */
+
+import { Component, OnInit } from '@angular/core';
+import { FormGroup, FormControl, Validators } from '@angular/forms';
+
+export interface TreeNodeInterface {
+  key: number;
+  title: string; //need to be title so that can be displayed in ng tree node
+  code: string;
+  description: string;
+  status: string;
+  level?: number;
+  expand?: boolean;
+  children?: TreeNodeInterface[];
+  parent?: TreeNodeInterface;
+}
+
+@Component({
+  selector: 'app-department',
+  templateUrl: './department.component.html',
+  styleUrls: ['./department.component.scss']
+})
+export class DepartmentComponent implements OnInit {
+  isLoading = true;
+  dapartmentDictForm: FormGroup;
+  newDepartmentForm: FormGroup;
+  editMode = false;
+  editNode: TreeNodeInterface;
+  isVisible = false;
+  isExpandTable = true;
+  filterArr: TreeNodeInterface[] = [
+    {
+      key: 120,
+      title: 'ABCD',
+      code: '123',
+      description: 'Company ABCD ',
+      status: 'Deleted'
+   }
+  ];
+
+  listOfMapData: TreeNodeInterface[] = [
+    {
+      key: 1,
+      title: 'A',
+      code: '123',
+      description: 'Company A',
+      status: 'Available',
+      children: [
+        {
+          key: 11,
+          title: 'AA',
+          code: '123',
+          description: 'Company AA',
+          status: 'Deleted'
+        },
+        {
+          key: 12,
+          title: 'AB',
+          code: '123',
+          description: 'Company AB',
+          status: 'Deleted',
+          children: [
+            {
+              key: 121,
+              title: 'ABC',
+              code: '123',
+              description: 'Company ABC',
+              status: 'Deleted',
+              children: [
+                {
+                  key: 120,
+                  title: 'ABCD',
+                  code: '123',
+                  description: 'Company ABCD ',
+                  status: 'Deleted'
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    },
+    {
+      key: 123,
+      title: 'E',
+      code: '999',
+      description: 'Company E',
+      status: 'Deleted'
+    }
+  ];
+
+  constructor() { }
+
+  //TODO(jasoonn): Load departments data
+  ngOnInit() {
+    this.dapartmentDictForm = new FormGroup({
+      'departmentName': new FormControl(''),
+      'departmentCode': new FormControl('')
+    });
+    this.newDepartmentForm = new FormGroup({
+      'code': new FormControl(null, Validators.required),
+      'name': new FormControl(null, Validators.required),
+      'parent': new FormControl(null),
+      'sort': new FormControl(0),
+      'status': new FormControl(null),
+      'description' : new FormControl(null)
+    });
+    setTimeout(() => {
+      this.isLoading = false;
+    }, 500);
+    this.listOfMapData.forEach(item => {
+      this.mapOfExpandedData[item.key] = this.convertTreeToList(item);
+    });
+
+    
+  }
+
+  queryDepartment(){
+    this.filterArr = [];
+    Object.keys(this.mapOfExpandedData).forEach(item => {
+      this.mapOfExpandedData[item].forEach(node => {
+        if 
(node.title.includes(this.dapartmentDictForm.get('departmentName').value) && 
node.code.includes(this.dapartmentDictForm.get('departmentCode').value)){
+          console.log('bingo', node)
+          this.filterArr.push(node);
+        }
+      });   
+    });
+    this.isExpandTable = false;
+    this.filterArr=[...this.filterArr];
+  }
+
+  //TODO(jasoonn): Create or edit department, need to communicate with db
+  submitDepartment(){
+    //Edit department
+    if (this.editMode === true){
+      this.editNode.title = this.newDepartmentForm.get('name').value;
+      this.editNode.code = this.newDepartmentForm.get('code').value;
+      this.editNode.description = 
this.newDepartmentForm.get('description').value;
+      this.editNode.status = this.newDepartmentForm.get('status').value ? 
'Available' : 'Deleted';
+    }
+    else{
+      console.log('createDepartment');
+    }
+    this.isVisible = false;
+  }
+
+  //TODO(jasoonn): Delete the department, need to comunicate with db
+  deleteDepartment(node: TreeNodeInterface){
+    node.status = 'Deleted';
+  }
+
+  //TODO(jasoonn): Restore the department, need to comunicate with db
+  restoreDepartment(node: TreeNodeInterface){
+    node.status = 'Available';
+  }
+
+
+  //TODO(jasoonn): Edit the department, need to comunicate with db, and 
reorder the list
+  editDepartment(node: TreeNodeInterface){
+    this.editNode = node;
+    this.editMode = true;
+    this.isVisible = true;
+    this.newDepartmentForm.get('code').setValue(node.code);
+    this.newDepartmentForm.get('name').setValue(node.title);
+    if (node.parent){
+      this.newDepartmentForm.get('parent').setValue(node.parent.key);
+    }
+    else {
+      this.newDepartmentForm.get('parent').setValue(null);
+    }
+    this.newDepartmentForm.get('status').setValue(node.status === 'Available' 
? true : false);
+    this.newDepartmentForm.get('description').setValue(node.description);
+  }
+
+  mapOfExpandedData: { [key: string]: TreeNodeInterface[] } = {};
+
+  collapse(array: TreeNodeInterface[], data: TreeNodeInterface, $event: 
boolean): void {
+    if ($event === false) {
+      if (data.children) {
+        data.children.forEach(d => {
+          const target = array.find(a => a.key === d.key)!;
+          target.expand = false;
+          if (target.key === data.key) return;
+          this.collapse(array, target, false);
+        });
+      } else {
+        return;
+      }
+    }
+  }
+
+  convertTreeToList(root: TreeNodeInterface): TreeNodeInterface[] {
+    const stack: TreeNodeInterface[] = [];
+    const array: TreeNodeInterface[] = [];
+    const hashMap = {};
+    stack.push({ ...root, level: 0, expand: false });
+
+    while (stack.length !== 0) {
+      const node = stack.pop()!;
+      this.visitNode(node, hashMap, array);
+      if (node.children) {
+        for (let i = node.children.length - 1; i >= 0; i--) {
+          stack.push({ ...node.children[i], level: node.level! + 1, expand: 
false, parent: node });
+        }
+      }
+    }
+
+    return array;
+  }
+
+  visitNode(node: TreeNodeInterface, hashMap: { [key: string]: boolean }, 
array: TreeNodeInterface[]): void {
+    if (!hashMap[node.key]) {
+      hashMap[node.key] = true;
+      array.push(node);
+    }
+  }
+
+  showParent(node: TreeNodeInterface){
+    if (node.parent) {
+      return node.parent.title;
+    }
+    return 'None'
+  }
+
+}
diff --git 
a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/manager-routing.module.ts
 
b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/manager-routing.module.ts
index e8128cd..d34a575 100644
--- 
a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/manager-routing.module.ts
+++ 
b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/manager-routing.module.ts
@@ -22,6 +22,7 @@ import { RouterModule, Routes } from '@angular/router';
 import { DataDictComponent } from 
'@submarine/pages/workbench/manager/data-dict/data-dict.component';
 import { ManagerComponent } from './manager.component';
 import { UserComponent } from './user/user.component';
+import { DepartmentComponent } from './department/department.component';
 
 const routes: Routes = [
   {
@@ -40,6 +41,10 @@ const routes: Routes = [
       {
         path: 'dataDict',
         component: DataDictComponent
+      },
+      {
+        path: 'department',
+        component: DepartmentComponent
       }
     ]
   }
diff --git 
a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/manager.component.ts
 
b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/manager.component.ts
index 440c3f3..4239164 100644
--- 
a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/manager.component.ts
+++ 
b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/manager.component.ts
@@ -67,6 +67,22 @@ export class ManagerComponent implements OnInit {
           title: 'Data Dict'
         }
       ]
+    },
+    department: {
+      title: 'department',
+      description: 'System Department Manager',
+      breadCrumb: [
+        {
+          title: 'Home',
+          routerLink: '/workbench/home'
+        },
+        {
+          title: 'manager'
+        },
+        {
+          title: 'department'
+        }
+      ]
     }
   };
   currentHeaderInfo: HeaderInfo;
diff --git 
a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/manager.module.ts
 
b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/manager.module.ts
index 4e1224e..8300149 100644
--- 
a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/manager.module.ts
+++ 
b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/manager.module.ts
@@ -31,9 +31,10 @@ import { ManagerComponent } from './manager.component';
 import { UserDrawerComponent } from './user-drawer/user-drawer.component';
 import { UserPasswordModalComponent } from 
'./user-password-modal/user-password-modal.component';
 import { UserComponent } from './user/user.component';
+import { DepartmentComponent } from './department/department.component';
 
 @NgModule({
-  declarations: [UserComponent, ManagerComponent, DataDictComponent, 
UserPasswordModalComponent, UserDrawerComponent, DataDictModalComponent, 
DataDictConfigModalComponent],
+  declarations: [UserComponent, ManagerComponent, DataDictComponent, 
UserPasswordModalComponent, UserDrawerComponent, DataDictModalComponent, 
DataDictConfigModalComponent, DepartmentComponent],
   imports: [CommonModule, ManagerRoutingModule, NgZorroAntdModule, 
ComponentsModule, FormsModule, ReactiveFormsModule]
 })
 export class ManagerModule {}
diff --git 
a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/workbench.component.ts
 
b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/workbench.component.ts
index 24bf571..fd452ab 100644
--- 
a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/workbench.component.ts
+++ 
b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/workbench.component.ts
@@ -85,6 +85,10 @@ export class WorkbenchComponent implements OnInit {
         {
           title: 'Data dict',
           routerLink: '/workbench/manager/dataDict'
+        },
+        {
+          title: 'Department',
+          routerLink: '/workbench/manager/department'
         }
       ]
     }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to