You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@submarine.apache.org by li...@apache.org on 2019/11/11 01:29:33 UTC
[submarine] branch master updated: SUBMARINE-257. Submarine web
user manager page with angular
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 4e7571f SUBMARINE-257. Submarine web user manager page with angular
4e7571f is described below
commit 4e7571f164274a5e3f78f183d011768e8536fd1f
Author: lleohao <ll...@hotmail.com>
AuthorDate: Sat Nov 9 16:03:00 2019 +0800
SUBMARINE-257. Submarine web user manager page with angular
### What is this PR for?
Submarine web, add user manager page with angular
### What type of PR is it?
[Feature]
### What is the Jira issue?
https://issues.apache.org/jira/browse/SUBMARINE-257
### How should this be tested?
https://travis-ci.org/lleohao/hadoop-submarine/builds/609567077
### Screenshots (if appropriate)
1. user list
![image](https://user-images.githubusercontent.com/3677382/68528827-cc5b3900-0332-11ea-9094-081ec7e12c8c.png)
2. add user
![image](https://user-images.githubusercontent.com/3677382/68528836-e1d06300-0332-11ea-93f5-61ee42a2a53c.png)
3. view user detail
![image](https://user-images.githubusercontent.com/3677382/68528857-088e9980-0333-11ea-9747-fc992451983e.png)
4. edit user
![image](https://user-images.githubusercontent.com/3677382/68528859-0debe400-0333-11ea-8d23-25e0535a2277.png)
5. change user password
![image](https://user-images.githubusercontent.com/3677382/68528851-fca2d780-0332-11ea-82fc-b2d42e8f5eb9.png)
6. delete user
![image](https://user-images.githubusercontent.com/3677382/68528848-f7de2380-0332-11ea-939c-b4d9a751158e.png)
7. Log out
![image](https://user-images.githubusercontent.com/3677382/68528854-02002200-0333-11ea-9269-bdfea5664c9a.png)
### Questions:
* Does the licenses files need update? No
* Is there breaking changes for older versions? No
* Does this needs documentation? No
Author: lleohao <ll...@hotmail.com>
Closes #89 from lleohao/SUBMARINE-257 and squashes the following commits:
66e7c44 [lleohao] Merge branch 'master' into SUBMARINE-257
d6ddf35 [lleohao] [SUBMARINE-257] ci: fix build ng error
af4dff5 [lleohao] [SUBMARINE-257] test: fix e2e test case
79663bb [lleohao] [SUBMARINE-257] ci: add unit test
b977f89 [lleohao] [SUBMARINE-257] feat: add manager user feature
3d7055e [lleohao] [SUBMARINE-257] feat: add page header and logout feature
028b399 [lleohao] [SUBMARINE-257] feat: add sidebar and logo
20b96b6 [lleohao] [SUBMARINE-257] feat: init workbench module
---
.gitignore | 5 +
.travis.yml | 25 ++-
.../org/apache/submarine/rest/LoginRestApi.java | 10 +-
submarine-workbench/workbench-web-ng/angular.json | 5 +-
.../protractor-ci.conf.js} | 22 +-
.../workbench-web-ng/e2e/src/app.e2e-spec.ts | 4 +-
.../workbench-web-ng/e2e/src/app.po.ts | 2 +-
submarine-workbench/workbench-web-ng/karma.conf.js | 6 +
submarine-workbench/workbench-web-ng/package.json | 2 +
submarine-workbench/workbench-web-ng/pom.xml | 1 +
.../workbench-web-ng/src/app/app-routing.module.ts | 6 +-
.../workbench-web-ng/src/app/app.component.html | 52 -----
.../workbench-web-ng/src/app/app.component.scss | 77 -------
.../workbench-web-ng/src/app/app.component.spec.ts | 16 +-
.../workbench-web-ng/src/app/app.module.ts | 2 +-
.../components.module.ts} | 13 +-
.../page-layout/page-layout.component.html} | 18 +-
.../page-layout/page-layout.component.scss} | 4 +
.../page-layout/page-layout.component.ts} | 20 +-
.../public-api.ts => interfaces/action.ts} | 6 +-
.../app/interfaces/base-entity.ts} | 20 +-
.../src/app/interfaces/permission.ts | 31 ++-
.../src/app/interfaces/public-api.ts | 4 +-
.../workbench-web-ng/src/app/interfaces/rest.ts | 31 +++
.../workbench-web-ng/src/app/interfaces/role.ts | 25 ++-
.../app/interfaces/sys-dept-select.ts} | 20 +-
.../app/interfaces/sys-dict-item.ts} | 24 +-
.../src/app/interfaces/{rest.ts => sys-user.ts} | 21 +-
.../src/app/interfaces/{user.ts => user-info.ts} | 41 +++-
.../src/app/pages/user/login/login.component.ts | 11 +-
.../src/app/pages/user/user.component.ts | 1 +
.../manager/data-dict/data-dict.component.html} | 2 +-
.../manager/data-dict/data-dict.component.scss} | 0
.../manager/data-dict/data-dict.component.ts} | 14 +-
.../manager/manager-routing.module.ts | 7 +-
.../{ => workbench}/manager/manager.component.html | 9 +-
.../{ => workbench}/manager/manager.component.scss | 0
.../{ => workbench}/manager/manager.component.ts | 30 ++-
.../{ => workbench}/manager/manager.module.ts | 10 +-
.../manager/user-drawer/user-drawer.component.html | 206 +++++++++++++++++
.../user-drawer/user-drawer.component.scss} | 22 +-
.../manager/user-drawer/user-drawer.component.ts | 243 +++++++++++++++++++++
.../user-password-modal.component.html | 62 ++++++
.../user-password-modal.component.scss} | 0
.../user-password-modal.component.ts | 83 +++++++
.../workbench/manager/user/user.component.html | 131 +++++++++++
.../manager/user/user.component.scss | 11 +
.../pages/workbench/manager/user/user.component.ts | 172 +++++++++++++++
.../workbench-routing.module.ts} | 17 +-
.../app/pages/workbench/workbench.component.html | 82 +++++++
.../workbench/workbench.component.scss} | 38 ++--
.../src/app/pages/workbench/workbench.component.ts | 88 ++++++++
.../workbench.module.ts} | 13 +-
.../src/app/services/auth.service.ts | 56 +++--
.../src/app/services/base-api.service.ts | 35 +++
.../{base-api.service.ts => department.service.ts} | 48 ++--
.../src/app/services/public-api.ts | 5 +
.../src/app/services/system-utils.service.ts | 95 ++++++++
.../src/app/services/user.service.ts | 139 ++++++++++++
.../workbench-web-ng/src/assets/logo.png | Bin 0 -> 16547 bytes
submarine-workbench/workbench-web-ng/src/main.ts | 3 +-
.../workbench-web-ng/src/polyfills.ts | 3 +-
submarine-workbench/workbench-web-ng/src/test.ts | 10 +-
63 files changed, 1798 insertions(+), 361 deletions(-)
diff --git a/.gitignore b/.gitignore
index a514d00..521d78c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -67,4 +67,9 @@ submarine-workbench/workbench-web/.env.*.local
submarine-workbench/workbench-web/npm-debug.log*
submarine-workbench/workbench-web/yarn-debug.log*
submarine-workbench/workbench-web/yarn-error.log*
+submarine-workbench/workbench-web-ng/node
+submarine-workbench/workbench-web-ng/node_modules
+submarine-workbench/workbench-web-ng/dist
+submarine-workbench/workbench-web-ng/package-lock.json
+submarine-workbench/workbench-web-ng/npm-debug.log*
submarine-test/e2e/Driver
diff --git a/.travis.yml b/.travis.yml
index 313743e..1b25f56 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -22,8 +22,10 @@ cache:
apt: true
directories:
- ${HOME}/.m2
- - submarine-workbench/workbench-web/node
- - submarine-workbench/workbench-web/node_modules
+ - submarine-workbench/workbench-web/node
+ - submarine-workbench/workbench-web/node_modules
+ - submarine-workbench/workbench-web-ng/node
+ - submarine-workbench/workbench-web-ng/node_modules
addons:
apt:
@@ -139,6 +141,25 @@ matrix:
dist: xenial
env: NAME="Test submarine distribution" PROFILE="-Phadoop-2.9" BUILD_FLAG="clean package install -DskipTests" TEST_FLAG="test -DskipRat -am" MODULES="" TEST_MODULES="" TEST_PROJECTS=""
+ # Test submarine web-ng
+ - language: node_js
+ node_js:
+ - "10"
+ before_install:
+ - cd submarine-workbench/workbench-web-ng
+ before_script:
+ npm install
+ addons:
+ apt:
+ sources:
+ - google-chrome
+ packages:
+ - google-chrome-stable
+ script:
+ - npm run test -- --no-watch --no-progress --browsers=ChromeHeadlessCI
+ - npm run e2e -- --protractor-config=e2e/protractor-ci.conf.js
+ env: NAME="Build workbench-web-ng"
+
install:
- mvn --version
- echo "[$NAME] > mvn $BUILD_FLAG $MODULES $PROFILE -B"
diff --git a/submarine-workbench/workbench-server/src/main/java/org/apache/submarine/rest/LoginRestApi.java b/submarine-workbench/workbench-server/src/main/java/org/apache/submarine/rest/LoginRestApi.java
index df21f29..f0b89c7 100644
--- a/submarine-workbench/workbench-server/src/main/java/org/apache/submarine/rest/LoginRestApi.java
+++ b/submarine-workbench/workbench-server/src/main/java/org/apache/submarine/rest/LoginRestApi.java
@@ -59,11 +59,12 @@ public class LoginRestApi {
try (SqlSession sqlSession = MyBatisUtil.getSqlSession()) {
SysUserMapper sysUserMapper = sqlSession.getMapper(SysUserMapper.class);
sysUser = sysUserMapper.login(mapParams);
+
+ sysUser.setToken("mock_token");
} catch (Exception e) {
LOG.error(e.getMessage(), e);
return new JsonResponse.Builder<>(Response.Status.OK).success(false).build();
}
- sysUser.setToken("mock_token");
return new JsonResponse.Builder<SysUser>(Response.Status.OK).success(true).result(sysUser).build();
}
@@ -76,4 +77,11 @@ public class LoginRestApi {
return new JsonResponse.Builder<String>(Response.Status.OK).success(true).result(data).build();
}
+
+ @POST
+ @Path("/logout")
+ @SubmarineApi
+ public Response logout() {
+ return new JsonResponse.Builder<Boolean>(Response.Status.OK).success(true).result(true).build();
+ }
}
diff --git a/submarine-workbench/workbench-web-ng/angular.json b/submarine-workbench/workbench-web-ng/angular.json
index c47d29d..b8ea29d 100644
--- a/submarine-workbench/workbench-web-ng/angular.json
+++ b/submarine-workbench/workbench-web-ng/angular.json
@@ -101,7 +101,8 @@
"styles": [
"src/styles.scss"
],
- "scripts": []
+ "scripts": [],
+ "codeCoverage": true
}
},
"lint": {
@@ -133,4 +134,4 @@
}
},
"defaultProject": "workbench-web-ng"
-}
\ No newline at end of file
+}
diff --git a/submarine-workbench/workbench-web-ng/src/app/pages/manager/user/user.component.ts b/submarine-workbench/workbench-web-ng/e2e/protractor-ci.conf.js
similarity index 70%
rename from submarine-workbench/workbench-web-ng/src/app/pages/manager/user/user.component.ts
rename to submarine-workbench/workbench-web-ng/e2e/protractor-ci.conf.js
index 8c3d582..11abcc4 100644
--- a/submarine-workbench/workbench-web-ng/src/app/pages/manager/user/user.component.ts
+++ b/submarine-workbench/workbench-web-ng/e2e/protractor-ci.conf.js
@@ -17,15 +17,17 @@
* under the License.
*/
-import { Component, OnInit } from '@angular/core';
+// @ts-check
+// Protractor configuration file, see link for more information
+// https://github.com/angular/protractor/blob/master/lib/config.ts
-@Component({
- selector: 'submarine-manager-user',
- templateUrl: './user.component.html',
- styleUrls: ['./user.component.scss']
-})
-export class UserComponent implements OnInit {
- constructor() {}
+const config = require('./protractor.conf').config;
- ngOnInit() {}
-}
+config.capabilities = {
+ browserName: 'chrome',
+ chromeOptions: {
+ args: ['--headless', '--no-sandbox']
+ }
+};
+
+exports.config = config;
diff --git a/submarine-workbench/workbench-web-ng/e2e/src/app.e2e-spec.ts b/submarine-workbench/workbench-web-ng/e2e/src/app.e2e-spec.ts
index 5b0d8f6..d91a449 100644
--- a/submarine-workbench/workbench-web-ng/e2e/src/app.e2e-spec.ts
+++ b/submarine-workbench/workbench-web-ng/e2e/src/app.e2e-spec.ts
@@ -27,9 +27,9 @@ describe('workspace-project App', () => {
page = new AppPage();
});
- it('should display welcome message', () => {
+ it('should display submarine', () => {
page.navigateTo();
- expect(page.getTitleText()).toEqual('workbench-web-ng app is running!');
+ expect(page.getTitleText()).toEqual('Submarine');
});
afterEach(async () => {
diff --git a/submarine-workbench/workbench-web-ng/e2e/src/app.po.ts b/submarine-workbench/workbench-web-ng/e2e/src/app.po.ts
index 493318b..62e9fb6 100644
--- a/submarine-workbench/workbench-web-ng/e2e/src/app.po.ts
+++ b/submarine-workbench/workbench-web-ng/e2e/src/app.po.ts
@@ -25,6 +25,6 @@ export class AppPage {
}
getTitleText() {
- return element(by.css('app-root .content span')).getText() as Promise<string>;
+ return element(by.css('submarine-root submarine-user .title')).getText() as Promise<string>;
}
}
diff --git a/submarine-workbench/workbench-web-ng/karma.conf.js b/submarine-workbench/workbench-web-ng/karma.conf.js
index 00f2741..bfa1d61 100644
--- a/submarine-workbench/workbench-web-ng/karma.conf.js
+++ b/submarine-workbench/workbench-web-ng/karma.conf.js
@@ -45,6 +45,12 @@ module.exports = function (config) {
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
+ customLaunchers: {
+ ChromeHeadlessCI: {
+ base: 'ChromeHeadless',
+ flags: ['--no-sandbox']
+ }
+ },
singleRun: false,
restartOnFileChange: true
});
diff --git a/submarine-workbench/workbench-web-ng/package.json b/submarine-workbench/workbench-web-ng/package.json
index c2deab2..8fc2e31 100644
--- a/submarine-workbench/workbench-web-ng/package.json
+++ b/submarine-workbench/workbench-web-ng/package.json
@@ -19,6 +19,8 @@
"@angular/platform-browser": "~8.2.9",
"@angular/platform-browser-dynamic": "~8.2.9",
"@angular/router": "~8.2.9",
+ "date-fns": "^2.6.0",
+ "lodash": "^4.17.15",
"md5": "^2.2.1",
"ng-zorro-antd": "8.1.2",
"rxjs": "~6.4.0",
diff --git a/submarine-workbench/workbench-web-ng/pom.xml b/submarine-workbench/workbench-web-ng/pom.xml
index 081d2d7..f74883d 100644
--- a/submarine-workbench/workbench-web-ng/pom.xml
+++ b/submarine-workbench/workbench-web-ng/pom.xml
@@ -48,6 +48,7 @@
</properties>
<build>
+ <defaultGoal>compile</defaultGoal>
<plugins>
<plugin>
<groupId>org.apache.rat</groupId>
diff --git a/submarine-workbench/workbench-web-ng/src/app/app-routing.module.ts b/submarine-workbench/workbench-web-ng/src/app/app-routing.module.ts
index 6cb4725..6ca7ea4 100644
--- a/submarine-workbench/workbench-web-ng/src/app/app-routing.module.ts
+++ b/submarine-workbench/workbench-web-ng/src/app/app-routing.module.ts
@@ -25,12 +25,12 @@ const routes: Routes = [
{
path: '',
pathMatch: 'full',
- redirectTo: '/manager/user'
+ redirectTo: 'workbench'
},
{
- path: 'manager',
+ path: 'workbench',
canActivate: [AuthGuard],
- loadChildren: () => import('./pages/manager/manager.module').then(m => m.ManagerModule)
+ loadChildren: () => import('./pages/workbench/workbench.module').then(m => m.WorkbenchModule)
},
{
path: 'user',
diff --git a/submarine-workbench/workbench-web-ng/src/app/app.component.html b/submarine-workbench/workbench-web-ng/src/app/app.component.html
index 6c447c0..970329b 100644
--- a/submarine-workbench/workbench-web-ng/src/app/app.component.html
+++ b/submarine-workbench/workbench-web-ng/src/app/app.component.html
@@ -19,56 +19,4 @@
<nz-layout class="app-layout">
<router-outlet></router-outlet>
-<!-- <nz-sider class="menu-sidebar"-->
-<!-- nzCollapsible-->
-<!-- nzWidth="256px"-->
-<!-- nzBreakpoint="md"-->
-<!-- [(nzCollapsed)]="isCollapsed"-->
-<!-- [nzTrigger]="null">-->
-<!-- <div class="sidebar-logo">-->
-<!-- <a href="https://ng.ant.design/" target="_blank">-->
-<!-- <img src="https://ng.ant.design/assets/img/logo.svg" alt="logo">-->
-<!-- <h1>Ant Design Of Angular</h1>-->
-<!-- </a>-->
-<!-- </div>-->
-<!-- <ul nz-menu nzTheme="dark" nzMode="inline" [nzInlineCollapsed]="isCollapsed">-->
-<!-- <li nz-submenu nzOpen nzTitle="Dashboard" nzIcon="dashboard">-->
-<!-- <ul>-->
-<!-- <li nz-menu-item nzMatchRouter>-->
-<!-- <a routerLink="/welcome">Welcome</a>-->
-<!-- </li>-->
-<!-- <li nz-menu-item nzMatchRouter>-->
-<!-- <a>Monitor</a>-->
-<!-- </li>-->
-<!-- <li nz-menu-item nzMatchRouter>-->
-<!-- <a>Workplace</a>-->
-<!-- </li>-->
-<!-- </ul>-->
-<!-- </li>-->
-<!-- <li nz-submenu nzOpen nzTitle="Form" nzIcon="form">-->
-<!-- <ul>-->
-<!-- <li nz-menu-item nzMatchRouter>-->
-<!-- <a>Basic Form</a>-->
-<!-- </li>-->
-<!-- </ul>-->
-<!-- </li>-->
-<!-- </ul>-->
-<!-- </nz-sider>-->
-<!-- <nz-layout>-->
-<!-- <nz-header>-->
-<!-- <div class="app-header">-->
-<!-- <span class="header-trigger" (click)="isCollapsed = !isCollapsed">-->
-<!-- <i class="trigger"-->
-<!-- nz-icon-->
-<!-- [nzType]="isCollapsed ? 'menu-unfold' : 'menu-fold'"-->
-<!-- ></i>-->
-<!-- </span>-->
-<!-- </div>-->
-<!-- </nz-header>-->
-<!-- <nz-content>-->
-<!-- <div class="inner-content">-->
-<!-- <router-outlet></router-outlet>-->
-<!-- </div>-->
-<!-- </nz-content>-->
-<!-- </nz-layout>-->
</nz-layout>
diff --git a/submarine-workbench/workbench-web-ng/src/app/app.component.scss b/submarine-workbench/workbench-web-ng/src/app/app.component.scss
index 67bafe6..a3b6485 100644
--- a/submarine-workbench/workbench-web-ng/src/app/app.component.scss
+++ b/submarine-workbench/workbench-web-ng/src/app/app.component.scss
@@ -17,83 +17,6 @@
* under the License.
*/
-:host {
- display: flex;
- text-rendering: optimizeLegibility;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
-}
-
.app-layout {
height: 100vh;
}
-
-.menu-sidebar {
- position: relative;
- z-index: 10;
- min-height: 100vh;
- box-shadow: 2px 0 6px rgba(0,21,41,.35);
-}
-
-.header-trigger {
- height: 64px;
- padding: 20px 24px;
- font-size: 20px;
- cursor: pointer;
- transition: all .3s,padding 0s;
-}
-
-.trigger:hover {
- color: #1890ff;
-}
-
-.sidebar-logo {
- position: relative;
- height: 64px;
- padding-left: 24px;
- overflow: hidden;
- line-height: 64px;
- background: #001529;
- transition: all .3s;
-}
-
-.sidebar-logo img {
- display: inline-block;
- height: 32px;
- width: 32px;
- vertical-align: middle;
-}
-
-.sidebar-logo h1 {
- display: inline-block;
- margin: 0 0 0 20px;
- color: #fff;
- font-weight: 600;
- font-size: 14px;
- font-family: Avenir,Helvetica Neue,Arial,Helvetica,sans-serif;
- vertical-align: middle;
-}
-
-nz-header {
- padding: 0;
- width: 100%;
- z-index: 2;
-}
-
-.app-header {
- position: relative;
- height: 64px;
- padding: 0;
- background: #fff;
- box-shadow: 0 1px 4px rgba(0,21,41,.08);
-}
-
-nz-content {
- margin: 24px;
-}
-
-.inner-content {
- padding: 24px;
- background: #fff;
- height: 100%;
-}
diff --git a/submarine-workbench/workbench-web-ng/src/app/app.component.spec.ts b/submarine-workbench/workbench-web-ng/src/app/app.component.spec.ts
index e59a500..c26eb91 100644
--- a/submarine-workbench/workbench-web-ng/src/app/app.component.spec.ts
+++ b/submarine-workbench/workbench-web-ng/src/app/app.component.spec.ts
@@ -19,12 +19,13 @@
import { async, TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
+import { NgZorroAntdModule } from 'ng-zorro-antd';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
- imports: [RouterTestingModule],
+ imports: [RouterTestingModule, NgZorroAntdModule],
declarations: [AppComponent]
}).compileComponents();
}));
@@ -34,17 +35,4 @@ describe('AppComponent', () => {
const app = fixture.debugElement.componentInstance;
expect(app).toBeTruthy();
});
-
- it(`should have as title 'workbench-web-ng'`, () => {
- const fixture = TestBed.createComponent(AppComponent);
- const app = fixture.debugElement.componentInstance;
- expect(app.title).toEqual('workbench-web-ng');
- });
-
- it('should render title', () => {
- const fixture = TestBed.createComponent(AppComponent);
- fixture.detectChanges();
- const compiled = fixture.debugElement.nativeElement;
- expect(compiled.querySelector('.content span').textContent).toContain('workbench-web-ng app is running!');
- });
});
diff --git a/submarine-workbench/workbench-web-ng/src/app/app.module.ts b/submarine-workbench/workbench-web-ng/src/app/app.module.ts
index 59c9cfd..64acdb4 100644
--- a/submarine-workbench/workbench-web-ng/src/app/app.module.ts
+++ b/submarine-workbench/workbench-web-ng/src/app/app.module.ts
@@ -25,7 +25,7 @@ import { HttpClientModule } from '@angular/common/http';
import zh from '@angular/common/locales/zh';
import { FormsModule } from '@angular/forms';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
-import { LocalStorageService } from '@submarine/services/local-storage.service';
+import { LocalStorageService } from '@submarine/services';
import { zh_CN, NgZorroAntdModule, NZ_I18N } from 'ng-zorro-antd';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
diff --git a/submarine-workbench/workbench-web-ng/src/app/pages/manager/manager.module.ts b/submarine-workbench/workbench-web-ng/src/app/components/components.module.ts
similarity index 74%
copy from submarine-workbench/workbench-web-ng/src/app/pages/manager/manager.module.ts
copy to submarine-workbench/workbench-web-ng/src/app/components/components.module.ts
index cfabafe..bfbd54c 100644
--- a/submarine-workbench/workbench-web-ng/src/app/pages/manager/manager.module.ts
+++ b/submarine-workbench/workbench-web-ng/src/app/components/components.module.ts
@@ -19,13 +19,14 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
+import { RouterModule } from '@angular/router';
import { NgZorroAntdModule } from 'ng-zorro-antd';
-import { ManagerRoutingModule } from './manager-routing.module';
-import { ManagerComponent } from './manager.component';
-import { UserComponent } from './user/user.component';
+import { PageLayoutComponent } from './page-layout/page-layout.component';
@NgModule({
- declarations: [UserComponent, ManagerComponent],
- imports: [CommonModule, ManagerRoutingModule, NgZorroAntdModule]
+ declarations: [PageLayoutComponent],
+ imports: [CommonModule, RouterModule, NgZorroAntdModule],
+ exports: [PageLayoutComponent]
})
-export class ManagerModule {}
+export class ComponentsModule {
+}
diff --git a/submarine-workbench/workbench-web-ng/src/app/pages/manager/user/user.component.html b/submarine-workbench/workbench-web-ng/src/app/components/page-layout/page-layout.component.html
similarity index 62%
copy from submarine-workbench/workbench-web-ng/src/app/pages/manager/user/user.component.html
copy to submarine-workbench/workbench-web-ng/src/app/components/page-layout/page-layout.component.html
index c5412cf..bf9287d 100644
--- a/submarine-workbench/workbench-web-ng/src/app/pages/manager/user/user.component.html
+++ b/submarine-workbench/workbench-web-ng/src/app/components/page-layout/page-layout.component.html
@@ -17,4 +17,20 @@
~ under the License.
-->
-<p>user works!</p>
+<div [ngStyle]="!title ? {margin: '-24px -24px 0'} : null">
+ <nz-page-header [nzTitle]="title|titlecase" *ngIf="breadCrumb">
+ <nz-breadcrumb nz-page-header-breadcrumb>
+ <nz-breadcrumb-item *ngFor="let item of breadCrumb">
+ {{item|titlecase}}
+ </nz-breadcrumb-item>
+ </nz-breadcrumb>
+ <nz-page-header-content *ngIf="description">
+ <p>{{description}}</p>
+ </nz-page-header-content>
+ </nz-page-header>
+
+ <div class="content">
+ <ng-content></ng-content>
+ </div>
+</div>
+
diff --git a/submarine-workbench/workbench-web-ng/src/app/pages/manager/manager.component.scss b/submarine-workbench/workbench-web-ng/src/app/components/page-layout/page-layout.component.scss
similarity index 94%
copy from submarine-workbench/workbench-web-ng/src/app/pages/manager/manager.component.scss
copy to submarine-workbench/workbench-web-ng/src/app/components/page-layout/page-layout.component.scss
index 510f082..ecea51c 100644
--- a/submarine-workbench/workbench-web-ng/src/app/pages/manager/manager.component.scss
+++ b/submarine-workbench/workbench-web-ng/src/app/components/page-layout/page-layout.component.scss
@@ -17,3 +17,7 @@
* under the License.
*/
+nz-page-header {
+ margin: -24px -24px 16px;
+}
+
diff --git a/submarine-workbench/workbench-web-ng/src/app/pages/manager/manager.component.ts b/submarine-workbench/workbench-web-ng/src/app/components/page-layout/page-layout.component.ts
similarity index 68%
copy from submarine-workbench/workbench-web-ng/src/app/pages/manager/manager.component.ts
copy to submarine-workbench/workbench-web-ng/src/app/components/page-layout/page-layout.component.ts
index efa6503..77a8a28 100644
--- a/submarine-workbench/workbench-web-ng/src/app/pages/manager/manager.component.ts
+++ b/submarine-workbench/workbench-web-ng/src/app/components/page-layout/page-layout.component.ts
@@ -17,15 +17,21 @@
* under the License.
*/
-import { Component, OnInit } from '@angular/core';
+import { Component, Input, OnInit } from '@angular/core';
@Component({
- selector: 'submarine-manager',
- templateUrl: './manager.component.html',
- styleUrls: ['./manager.component.scss']
+ selector: 'submarine-page-layout',
+ templateUrl: './page-layout.component.html',
+ styleUrls: ['./page-layout.component.scss']
})
-export class ManagerComponent implements OnInit {
- constructor() {}
+export class PageLayoutComponent implements OnInit {
+ @Input() title: string;
+ @Input() description: string;
+ @Input() breadCrumb: string[];
- ngOnInit() {}
+ constructor() {
+ }
+
+ ngOnInit() {
+ }
}
diff --git a/submarine-workbench/workbench-web-ng/src/app/services/public-api.ts b/submarine-workbench/workbench-web-ng/src/app/interfaces/action.ts
similarity index 89%
copy from submarine-workbench/workbench-web-ng/src/app/services/public-api.ts
copy to submarine-workbench/workbench-web-ng/src/app/interfaces/action.ts
index a853cff..525ed78 100644
--- a/submarine-workbench/workbench-web-ng/src/app/services/public-api.ts
+++ b/submarine-workbench/workbench-web-ng/src/app/interfaces/action.ts
@@ -17,4 +17,8 @@
* under the License.
*/
-export * from './auth.service';
+export interface Action {
+ action: string;
+ defaultCheck: boolean;
+ describe: string;
+}
diff --git a/submarine-workbench/workbench-web-ng/e2e/src/app.po.ts b/submarine-workbench/workbench-web-ng/src/app/interfaces/base-entity.ts
similarity index 71%
copy from submarine-workbench/workbench-web-ng/e2e/src/app.po.ts
copy to submarine-workbench/workbench-web-ng/src/app/interfaces/base-entity.ts
index 493318b..00dbaa3 100644
--- a/submarine-workbench/workbench-web-ng/e2e/src/app.po.ts
+++ b/submarine-workbench/workbench-web-ng/src/app/interfaces/base-entity.ts
@@ -17,14 +17,18 @@
* under the License.
*/
-import { browser, by, element } from 'protractor';
+export class BaseEntity {
+ id: string;
+ createBy: string;
+ createTime: number;
+ updateBy: string;
+ updateTime: number;
-export class AppPage {
- navigateTo() {
- return browser.get(browser.baseUrl) as Promise<any>;
- }
-
- getTitleText() {
- return element(by.css('app-root .content span')).getText() as Promise<string>;
+ constructor(data: BaseEntity) {
+ this.id = data.id;
+ this.createBy = data.createBy;
+ this.createTime = data.createTime;
+ this.updateBy = data.updateBy;
+ this.updateTime = data.updateTime;
}
}
diff --git a/submarine-workbench/workbench-web-ng/src/app/interfaces/permission.ts b/submarine-workbench/workbench-web-ng/src/app/interfaces/permission.ts
index fffef9e..349649a 100644
--- a/submarine-workbench/workbench-web-ng/src/app/interfaces/permission.ts
+++ b/submarine-workbench/workbench-web-ng/src/app/interfaces/permission.ts
@@ -17,19 +17,30 @@
* under the License.
*/
-export interface PermissionActionEntitySet {
+import { Action } from './action';
+
+export interface ActionEntitySet {
action: string;
- defaultChecked: boolean;
+ defaultCheck: boolean;
describe: string;
}
-export type PermissionAction = PermissionActionEntitySet;
-
export class Permission {
- permissionId = '';
- permissionName = '';
- roleId = '';
- actionList = null;
- actionEntitySet: PermissionActionEntitySet[] = [];
- actions: PermissionAction[] = [];
+ roleId: string;
+ permissionId: string;
+ permissionName: string;
+ dataAccess?: any;
+ actionList?: any;
+ actions: Action[];
+ actionEntitySet: ActionEntitySet[];
+
+ constructor(permission: Permission) {
+ this.roleId = permission.roleId;
+ this.permissionId = permission.permissionId;
+ this.permissionName = permission.permissionName;
+ this.dataAccess = permission.dataAccess;
+ this.actionList = permission.actionList;
+ this.actions = permission.actions;
+ this.actionEntitySet = permission.actionEntitySet;
+ }
}
diff --git a/submarine-workbench/workbench-web-ng/src/app/interfaces/public-api.ts b/submarine-workbench/workbench-web-ng/src/app/interfaces/public-api.ts
index 8958522..4123a93 100644
--- a/submarine-workbench/workbench-web-ng/src/app/interfaces/public-api.ts
+++ b/submarine-workbench/workbench-web-ng/src/app/interfaces/public-api.ts
@@ -19,5 +19,7 @@
export * from './permission';
export * from './role';
-export * from './user';
+export * from './user-info';
export * from './rest';
+export * from './action';
+export * from './sys-user';
diff --git a/submarine-workbench/workbench-web-ng/src/app/interfaces/rest.ts b/submarine-workbench/workbench-web-ng/src/app/interfaces/rest.ts
index dcf39fc..61f5d46 100644
--- a/submarine-workbench/workbench-web-ng/src/app/interfaces/rest.ts
+++ b/submarine-workbench/workbench-web-ng/src/app/interfaces/rest.ts
@@ -17,10 +17,41 @@
* under the License.
*/
+/**
+ * REST api abstract interface
+ *
+ * @example
+ * ```typescript
+ * const res = Rest<{userName: string, password: string}>;
+ *
+ * // res.result.userName is string
+ * // res.result.password is string
+ * ```
+ */
export interface Rest<T> {
+ /**
+ * request result
+ */
result: T;
+ /**
+ * request http status code
+ */
code: number;
message: string;
status: string;
success: boolean;
}
+
+/**
+ * Array result
+ */
+export interface ListResult<T> {
+ /**
+ * result list
+ */
+ records: T[];
+ /**
+ * result list length
+ */
+ total: number;
+}
diff --git a/submarine-workbench/workbench-web-ng/src/app/interfaces/role.ts b/submarine-workbench/workbench-web-ng/src/app/interfaces/role.ts
index f3f8669..1464300 100644
--- a/submarine-workbench/workbench-web-ng/src/app/interfaces/role.ts
+++ b/submarine-workbench/workbench-web-ng/src/app/interfaces/role.ts
@@ -19,13 +19,24 @@
import { Permission } from './permission';
-export type RoleId = string;
-
export class Role {
- createTime: number = -1;
- creatorId = '';
- describe = '';
- id = '';
- name = '';
+ id: string;
+ name: string;
+ describe: string;
+ status: number;
+ creatorId: string;
+ createTime: number;
+ deleted: number;
permissions: Permission[];
+
+ constructor(role: Role) {
+ this.id = role.id;
+ this.name = role.name;
+ this.describe = role.describe;
+ this.status = role.status;
+ this.creatorId = role.creatorId;
+ this.createTime = role.createTime;
+ this.deleted = role.deleted;
+ this.permissions = role.permissions.map(permission => new Permission(permission));
+ }
}
diff --git a/submarine-workbench/workbench-web-ng/e2e/src/app.po.ts b/submarine-workbench/workbench-web-ng/src/app/interfaces/sys-dept-select.ts
similarity index 69%
copy from submarine-workbench/workbench-web-ng/e2e/src/app.po.ts
copy to submarine-workbench/workbench-web-ng/src/app/interfaces/sys-dept-select.ts
index 493318b..5929e7b 100644
--- a/submarine-workbench/workbench-web-ng/e2e/src/app.po.ts
+++ b/submarine-workbench/workbench-web-ng/src/app/interfaces/sys-dept-select.ts
@@ -17,14 +17,18 @@
* under the License.
*/
-import { browser, by, element } from 'protractor';
+export class SysDeptSelect {
+ key: string;
+ value: string;
+ title: string;
+ disabled: boolean;
+ children: SysDeptSelect[];
-export class AppPage {
- navigateTo() {
- return browser.get(browser.baseUrl) as Promise<any>;
- }
-
- getTitleText() {
- return element(by.css('app-root .content span')).getText() as Promise<string>;
+ constructor(data: SysDeptSelect) {
+ this.key = data.key;
+ this.value = data.value;
+ this.title = data.title;
+ this.disabled = data.disabled;
+ this.children = data.children.map(item => new SysDeptSelect(item));
}
}
diff --git a/submarine-workbench/workbench-web-ng/e2e/src/app.po.ts b/submarine-workbench/workbench-web-ng/src/app/interfaces/sys-dict-item.ts
similarity index 61%
copy from submarine-workbench/workbench-web-ng/e2e/src/app.po.ts
copy to submarine-workbench/workbench-web-ng/src/app/interfaces/sys-dict-item.ts
index 493318b..dbe56d1 100644
--- a/submarine-workbench/workbench-web-ng/e2e/src/app.po.ts
+++ b/submarine-workbench/workbench-web-ng/src/app/interfaces/sys-dict-item.ts
@@ -17,14 +17,24 @@
* under the License.
*/
-import { browser, by, element } from 'protractor';
+import { BaseEntity } from '@submarine/interfaces/base-entity';
-export class AppPage {
- navigateTo() {
- return browser.get(browser.baseUrl) as Promise<any>;
- }
+export class SysDictItem extends BaseEntity {
+ dictCode: string;
+ itemCode: string;
+ itemName: string;
+ description: string;
+ sortOrder: number;
+ deleted: number;
+
+ constructor(data: SysDictItem) {
+ super(data);
- getTitleText() {
- return element(by.css('app-root .content span')).getText() as Promise<string>;
+ this.dictCode = data.dictCode;
+ this.itemCode = data.itemCode;
+ this.itemName = data.itemName;
+ this.description = data.description;
+ this.sortOrder = data.sortOrder;
+ this.deleted = data.deleted;
}
}
diff --git a/submarine-workbench/workbench-web-ng/src/app/interfaces/rest.ts b/submarine-workbench/workbench-web-ng/src/app/interfaces/sys-user.ts
similarity index 71%
copy from submarine-workbench/workbench-web-ng/src/app/interfaces/rest.ts
copy to submarine-workbench/workbench-web-ng/src/app/interfaces/sys-user.ts
index dcf39fc..18571e7 100644
--- a/submarine-workbench/workbench-web-ng/src/app/interfaces/rest.ts
+++ b/submarine-workbench/workbench-web-ng/src/app/interfaces/sys-user.ts
@@ -17,10 +17,21 @@
* under the License.
*/
-export interface Rest<T> {
- result: T;
- code: number;
- message: string;
+import { BaseEntity } from './base-entity';
+
+export interface SysUser extends BaseEntity {
+ userName: string;
+ realName: string;
+ password: string;
+ avatar: string;
+ sex: string;
status: string;
- success: boolean;
+ phone: string;
+ email: string;
+ deptCode: string;
+ deptName: string;
+ roleCode: string;
+ birthday: number;
+ deleted: number;
+ token: string;
}
diff --git a/submarine-workbench/workbench-web-ng/src/app/interfaces/user.ts b/submarine-workbench/workbench-web-ng/src/app/interfaces/user-info.ts
similarity index 50%
copy from submarine-workbench/workbench-web-ng/src/app/interfaces/user.ts
copy to submarine-workbench/workbench-web-ng/src/app/interfaces/user-info.ts
index c68c424..c159497 100644
--- a/submarine-workbench/workbench-web-ng/src/app/interfaces/user.ts
+++ b/submarine-workbench/workbench-web-ng/src/app/interfaces/user-info.ts
@@ -19,13 +19,38 @@
import { Role } from './role';
-export class User {
- avatar = '';
- id = '';
- name = '';
- telephone = '';
- username = '';
+export class UserInfo {
+ id: string;
+ name: string;
+ username: string;
+ password: string;
+ avatar: string;
+ status: number;
+ telephone: string;
+ lastLoginIp: string;
+ lastLoginTime: number;
+ creatorId: string;
+ createTime: number;
+ merchantCode: string;
+ deleted: number;
+ roleId: string;
role: Role;
- roleId: number;
- token: string;
+
+ constructor(res: UserInfo) {
+ this.id = res.id;
+ this.name = res.name;
+ this.username = res.username;
+ this.password = res.password;
+ this.avatar = res.avatar;
+ this.status = res.status;
+ this.telephone = res.telephone;
+ this.lastLoginIp = res.lastLoginIp;
+ this.lastLoginTime = res.lastLoginTime;
+ this.creatorId = res.creatorId;
+ this.createTime = res.createTime;
+ this.merchantCode = res.merchantCode;
+ this.deleted = res.deleted;
+ this.roleId = res.roleId;
+ this.role = new Role(res.role);
+ }
}
diff --git a/submarine-workbench/workbench-web-ng/src/app/pages/user/login/login.component.ts b/submarine-workbench/workbench-web-ng/src/app/pages/user/login/login.component.ts
index 9190cea..2158604 100644
--- a/submarine-workbench/workbench-web-ng/src/app/pages/user/login/login.component.ts
+++ b/submarine-workbench/workbench-web-ng/src/app/pages/user/login/login.component.ts
@@ -20,8 +20,8 @@
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Router } from '@angular/router';
+import { AuthService } from '@submarine/services';
import { NzNotificationService } from 'ng-zorro-antd';
-import { AuthService } from '../../../services';
@Component({
selector: 'submarine-login',
@@ -38,7 +38,7 @@ export class LoginComponent implements OnInit {
private router: Router
) {
if (this.authService.isLoggedIn) {
- this.router.navigate(['/manager/user']);
+ this.router.navigate(['/workbench']);
}
}
@@ -55,7 +55,6 @@ export class LoginComponent implements OnInit {
this.loginSuccess();
},
error => {
- console.log(error);
this.requestFailed(error);
}
);
@@ -71,11 +70,7 @@ export class LoginComponent implements OnInit {
}
loginSuccess() {
- this.router.navigate(['/manager/user']);
-
- setTimeout(() => {
- this.nzNotificationService.success('Welcome', 'Welcome back');
- }, 1000);
+ this.router.navigate(['/workbench']);
}
requestFailed(error: Error) {
diff --git a/submarine-workbench/workbench-web-ng/src/app/pages/user/user.component.ts b/submarine-workbench/workbench-web-ng/src/app/pages/user/user.component.ts
index b38a643..c4f6328 100644
--- a/submarine-workbench/workbench-web-ng/src/app/pages/user/user.component.ts
+++ b/submarine-workbench/workbench-web-ng/src/app/pages/user/user.component.ts
@@ -18,6 +18,7 @@
*/
import { Component, OnInit } from '@angular/core';
+import { UserService } from '@submarine/services';
@Component({
selector: 'submarine-user',
diff --git a/submarine-workbench/workbench-web-ng/src/app/pages/manager/user/user.component.html b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/data-dict/data-dict.component.html
similarity index 97%
rename from submarine-workbench/workbench-web-ng/src/app/pages/manager/user/user.component.html
rename to submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/data-dict/data-dict.component.html
index c5412cf..5f8f81f 100644
--- a/submarine-workbench/workbench-web-ng/src/app/pages/manager/user/user.component.html
+++ b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/data-dict/data-dict.component.html
@@ -17,4 +17,4 @@
~ under the License.
-->
-<p>user works!</p>
+<p>data-dict works!</p>
diff --git a/submarine-workbench/workbench-web-ng/src/app/pages/manager/user/user.component.scss b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/data-dict/data-dict.component.scss
similarity index 100%
copy from submarine-workbench/workbench-web-ng/src/app/pages/manager/user/user.component.scss
copy to submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/data-dict/data-dict.component.scss
diff --git a/submarine-workbench/workbench-web-ng/src/app/pages/manager/manager.component.ts b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/data-dict/data-dict.component.ts
similarity index 80%
copy from submarine-workbench/workbench-web-ng/src/app/pages/manager/manager.component.ts
copy to submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/data-dict/data-dict.component.ts
index efa6503..3dde811 100644
--- a/submarine-workbench/workbench-web-ng/src/app/pages/manager/manager.component.ts
+++ b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/data-dict/data-dict.component.ts
@@ -20,12 +20,14 @@
import { Component, OnInit } from '@angular/core';
@Component({
- selector: 'submarine-manager',
- templateUrl: './manager.component.html',
- styleUrls: ['./manager.component.scss']
+ selector: 'submarine-data-dict',
+ templateUrl: './data-dict.component.html',
+ styleUrls: ['./data-dict.component.scss']
})
-export class ManagerComponent implements OnInit {
- constructor() {}
+export class DataDictComponent implements OnInit {
+ constructor() {
+ }
- ngOnInit() {}
+ ngOnInit() {
+ }
}
diff --git a/submarine-workbench/workbench-web-ng/src/app/pages/manager/manager-routing.module.ts b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/manager-routing.module.ts
similarity index 86%
copy from submarine-workbench/workbench-web-ng/src/app/pages/manager/manager-routing.module.ts
copy to submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/manager-routing.module.ts
index 97c775f..52757dc 100644
--- a/submarine-workbench/workbench-web-ng/src/app/pages/manager/manager-routing.module.ts
+++ b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/manager-routing.module.ts
@@ -19,6 +19,7 @@
import { NgModule } from '@angular/core';
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';
@@ -30,11 +31,15 @@ const routes: Routes = [
{
path: '',
pathMatch: 'full',
- redirectTo: '/manager/user'
+ redirectTo: 'user'
},
{
path: 'user',
component: UserComponent
+ },
+ {
+ path: 'data-dict',
+ component: DataDictComponent
}
]
}
diff --git a/submarine-workbench/workbench-web-ng/src/app/pages/manager/manager.component.html b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/manager.component.html
similarity index 79%
rename from submarine-workbench/workbench-web-ng/src/app/pages/manager/manager.component.html
rename to submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/manager.component.html
index 4f806ff..598d7db 100644
--- a/submarine-workbench/workbench-web-ng/src/app/pages/manager/manager.component.html
+++ b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/manager.component.html
@@ -17,5 +17,10 @@
~ under the License.
-->
-<p>manager works!</p>
-<router-outlet></router-outlet>
+<submarine-page-layout
+ [title]="currentHeaderInfo?.title"
+ [breadCrumb]="currentHeaderInfo?.breadCrumb"
+ [description]="currentHeaderInfo?.description"
+>
+ <router-outlet></router-outlet>
+</submarine-page-layout>
diff --git a/submarine-workbench/workbench-web-ng/src/app/pages/manager/manager.component.scss b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/manager.component.scss
similarity index 100%
rename from submarine-workbench/workbench-web-ng/src/app/pages/manager/manager.component.scss
rename to submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/manager.component.scss
diff --git a/submarine-workbench/workbench-web-ng/src/app/pages/manager/manager.component.ts b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/manager.component.ts
similarity index 50%
rename from submarine-workbench/workbench-web-ng/src/app/pages/manager/manager.component.ts
rename to submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/manager.component.ts
index efa6503..15f42c5 100644
--- a/submarine-workbench/workbench-web-ng/src/app/pages/manager/manager.component.ts
+++ b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/manager.component.ts
@@ -17,15 +17,41 @@
* under the License.
*/
+import { Location, LocationStrategy, PathLocationStrategy } from '@angular/common';
import { Component, OnInit } from '@angular/core';
+import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
+import _ from 'lodash';
+
+interface HeaderInfo {
+ title: string;
+ description: string;
+ breadCrumb: string[];
+}
@Component({
selector: 'submarine-manager',
templateUrl: './manager.component.html',
- styleUrls: ['./manager.component.scss']
+ styleUrls: ['./manager.component.scss'],
+ providers: [Location, { provide: LocationStrategy, useClass: PathLocationStrategy }]
})
export class ManagerComponent implements OnInit {
- constructor() {}
+ private headerInfo: { [key: string]: HeaderInfo } = {
+ user: {
+ title: 'user',
+ description: 'You can check the user, delete the user, lock and unlock the user, etc.',
+ breadCrumb: ['manager', 'user']
+ }
+ };
+ currentHeaderInfo: HeaderInfo;
+
+ constructor(private route: ActivatedRoute, private location: Location, private router: Router) {
+ this.router.events.subscribe(event => {
+ if (event instanceof NavigationEnd) {
+ const lastMatch = _.last(event.urlAfterRedirects.split('/'));
+ this.currentHeaderInfo = this.headerInfo[lastMatch];
+ }
+ });
+ }
ngOnInit() {}
}
diff --git a/submarine-workbench/workbench-web-ng/src/app/pages/manager/manager.module.ts b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/manager.module.ts
similarity index 68%
copy from submarine-workbench/workbench-web-ng/src/app/pages/manager/manager.module.ts
copy to submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/manager.module.ts
index cfabafe..94048f5 100644
--- a/submarine-workbench/workbench-web-ng/src/app/pages/manager/manager.module.ts
+++ b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/manager.module.ts
@@ -19,13 +19,19 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
+import { FormsModule, ReactiveFormsModule } from '@angular/forms';
+import { ComponentsModule } from '@submarine/components/components.module';
import { NgZorroAntdModule } from 'ng-zorro-antd';
+
+import { DataDictComponent } from './data-dict/data-dict.component';
import { ManagerRoutingModule } from './manager-routing.module';
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';
@NgModule({
- declarations: [UserComponent, ManagerComponent],
- imports: [CommonModule, ManagerRoutingModule, NgZorroAntdModule]
+ declarations: [UserComponent, ManagerComponent, DataDictComponent, UserPasswordModalComponent, UserDrawerComponent],
+ imports: [CommonModule, ManagerRoutingModule, NgZorroAntdModule, ComponentsModule, FormsModule, ReactiveFormsModule]
})
export class ManagerModule {}
diff --git a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/user-drawer/user-drawer.component.html b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/user-drawer/user-drawer.component.html
new file mode 100644
index 0000000..9ae9b64
--- /dev/null
+++ b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/user-drawer/user-drawer.component.html
@@ -0,0 +1,206 @@
+<!--
+ ~ 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-drawer
+ [nzTitle]="title"
+ [nzMaskClosable]="false"
+ [nzWidth]="720"
+ [nzVisible]="visible"
+ (nzOnClose)="onClose()"
+ [nzBodyStyle]="{ height: 'calc(100% - 55px)', overflow: 'auto', 'padding-bottom': '53px' }"
+>
+ <form nz-form [formGroup]="form" nzLayout="horizontal">
+ <!-- Account Name-->
+ <nz-form-item>
+ <nz-form-label [nzSpan]="labelSpan" nzRequired>
+ Account Name
+ </nz-form-label>
+ <nz-form-control [nzSpan]="controlSpan" nzHasFeedback [nzErrorTip]="usernameErrorTpl">
+ <input
+ type="text"
+ nz-input
+ formControlName="userName"
+ placeholder="User's account name"
+ [readOnly]="sysUser?.id"
+ />
+
+ <ng-template #usernameErrorTpl let-control>
+ <ng-container *ngIf="control.hasError('required')">
+ Please input account's name!
+ </ng-container>
+ <ng-container *ngIf="control.hasError('duplicated')">
+ Account name already exist!
+ </ng-container>
+ <ng-container *ngIf="control.hasError('error')">
+ Error
+ </ng-container>
+ </ng-template>
+ </nz-form-control>
+ </nz-form-item>
+
+ <!-- Password -->
+ <nz-form-item *ngIf="!sysUser?.id">
+ <nz-form-label [nzSpan]="labelSpan" nzRequired>
+ Password
+ </nz-form-label>
+ <nz-form-control [nzSpan]="controlSpan" [nzErrorTip]="passwordErrorTpl">
+ <input
+ type="password"
+ nz-input
+ formControlName="password"
+ (ngModelChange)="validateConfirmPassword()"
+ placeholder="Users's account password"
+ />
+
+ <ng-template #passwordErrorTpl let-control>
+ <ng-container *ngIf="control.hasError('pattern')">
+ The password consists of 8 digits, uppercase and lowercase letters and special symbols!
+ </ng-container>
+ <ng-container *ngIf="control.hasError('required')">
+ Pleas input account's password!
+ </ng-container>
+ </ng-template>
+ </nz-form-control>
+ </nz-form-item>
+
+ <!-- Confirm -->
+ <nz-form-item *ngIf="!sysUser?.id">
+ <nz-form-label [nzSpan]="labelSpan" nzRequired>
+ Confirm
+ </nz-form-label>
+ <nz-form-control [nzSpan]="controlSpan" [nzErrorTip]="confirmErrorTpl">
+ <input type="password" nz-input formControlName="confirm" placeholder="Please confirm password"/>
+
+ <ng-template #confirmErrorTpl let-control>
+ <ng-container *ngIf="control.hasError('required')">
+ Please confirm your password!
+ </ng-container>
+ <ng-container *ngIf="control.hasError('confirm')">
+ Password is inconsistent!
+ </ng-container>
+ </ng-template>
+ </nz-form-control>
+ </nz-form-item>
+
+ <!-- Real Name -->
+ <nz-form-item>
+ <nz-form-label [nzSpan]="labelSpan" nzRequired>
+ Real Name
+ </nz-form-label>
+ <nz-form-control [nzSpan]="controlSpan" nzErrorTip="Please input account's real name">
+ <input type="text" nz-input formControlName="realName" placeholder="User's real name" [readOnly]="readonly"/>
+ </nz-form-control>
+ </nz-form-item>
+
+ <!-- Department -->
+ <nz-form-item>
+ <nz-form-label [nzSpan]="labelSpan">
+ Department
+ </nz-form-label>
+ <nz-form-control [nzSpan]="controlSpan">
+ <nz-tree-select
+ nzAllowClear
+ nzDefaultExpandAll
+ nzShowSearch
+ nzPlaceHolder="Please select"
+ formControlName="deptCode"
+ [nzNodes]="sysDeptTreeList"
+ ></nz-tree-select>
+ </nz-form-control>
+ </nz-form-item>
+
+ <!-- Birthday -->
+ <nz-form-item>
+ <nz-form-label [nzSpan]="labelSpan">
+ Birthday
+ </nz-form-label>
+ <nz-form-control [nzSpan]="controlSpan">
+ <nz-date-picker formControlName="birthday"></nz-date-picker>
+ </nz-form-control>
+ </nz-form-item>
+
+ <!-- Sex -->
+ <nz-form-item>
+ <nz-form-label [nzSpan]="labelSpan">
+ Sex
+ </nz-form-label>
+ <nz-form-control [nzSpan]="controlSpan">
+ <nz-select formControlName="sex" nzPlaceHolder="Please Select">
+ <nz-option [nzValue]="item.itemCode" [nzLabel]="item.itemName" *ngFor="let item of sexDictSelect"></nz-option>
+ </nz-select>
+ </nz-form-control>
+ </nz-form-item>
+
+ <!-- email -->
+ <nz-form-item>
+ <nz-form-label [nzSpan]="labelSpan">
+ email
+ </nz-form-label>
+ <nz-form-control [nzSpan]="controlSpan" [nzErrorTip]="emailErrorTip">
+ <input type="email" nz-input formControlName="email" [readOnly]="readonly"/>
+
+ <ng-template #emailErrorTip let-control>
+ <ng-container *ngIf="control.hasError('email')">
+ Email is incorrect!
+ </ng-container>
+ <ng-container *ngIf="control.hasError('duplicated')">
+ Email name already exist!
+ </ng-container>
+ </ng-template>
+ </nz-form-control>
+ </nz-form-item>
+
+ <!-- Phone -->
+ <nz-form-item>
+ <nz-form-label [nzSpan]="labelSpan">
+ Phone
+ </nz-form-label>
+ <nz-form-control [nzSpan]="controlSpan" [nzErrorTip]="phoneErrorTip">
+ <input type="text" nz-input formControlName="phone" [readOnly]="readonly"/>
+
+ <ng-template #phoneErrorTip let-control>
+ <ng-container *ngIf="control.hasError('duplicated')">
+ Phone already exist!
+ </ng-container>
+ </ng-template>
+ </nz-form-control>
+ </nz-form-item>
+
+ <!-- Status -->
+ <nz-form-item>
+ <nz-form-label [nzSpan]="labelSpan">
+ Status
+ </nz-form-label>
+ <nz-form-control [nzSpan]="controlSpan">
+ <nz-select formControlName="status" nzPlaceHolder="Please Select">
+ <nz-option
+ [nzValue]="item.itemCode"
+ [nzLabel]="item.itemName"
+ *ngFor="let item of statusDictSelect"
+ ></nz-option>
+ </nz-select>
+ </nz-form-control>
+ </nz-form-item>
+ </form>
+
+ <div class="footer" *ngIf="!readonly">
+ <button type="button" (click)="onClose()" class="ant-btn" style="margin-right: 8px;"><span>Cancel</span></button>
+ <button type="button" (click)="onSubmit()" class="ant-btn ant-btn-primary"><span>Submit</span></button>
+ </div>
+</nz-drawer>
diff --git a/submarine-workbench/workbench-web-ng/src/app/interfaces/user.ts b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/user-drawer/user-drawer.component.scss
similarity index 81%
rename from submarine-workbench/workbench-web-ng/src/app/interfaces/user.ts
rename to submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/user-drawer/user-drawer.component.scss
index c68c424..7750c7a 100644
--- a/submarine-workbench/workbench-web-ng/src/app/interfaces/user.ts
+++ b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/user-drawer/user-drawer.component.scss
@@ -1,4 +1,4 @@
-/*
+/*!
* 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
@@ -17,15 +17,13 @@
* under the License.
*/
-import { Role } from './role';
-
-export class User {
- avatar = '';
- id = '';
- name = '';
- telephone = '';
- username = '';
- role: Role;
- roleId: number;
- token: string;
+.footer {
+ position: absolute;
+ bottom: 0;
+ width: 100%;
+ border-top: 1px solid rgb(232, 232, 232);
+ padding: 10px 16px;
+ text-align: right;
+ left: 0;
+ background: #fff;
}
diff --git a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/user-drawer/user-drawer.component.ts b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/user-drawer/user-drawer.component.ts
new file mode 100644
index 0000000..a480af4
--- /dev/null
+++ b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/user-drawer/user-drawer.component.ts
@@ -0,0 +1,243 @@
+/*
+ * 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, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core';
+import { FormBuilder, FormControl, FormGroup, ValidationErrors, Validators } from '@angular/forms';
+import { SysUser } from '@submarine/interfaces';
+import { SysDeptSelect } from '@submarine/interfaces/sys-dept-select';
+import { SysDictItem } from '@submarine/interfaces/sys-dict-item';
+import { SystemUtilsService, SysDictCode } from '@submarine/services';
+import { format } from 'date-fns';
+import { zip, Observable, Observer } from 'rxjs';
+import { filter, map, startWith, take } from 'rxjs/operators';
+
+@Component({
+ selector: 'submarine-user-drawer',
+ templateUrl: './user-drawer.component.html',
+ styleUrls: ['./user-drawer.component.scss']
+})
+export class UserDrawerComponent implements OnInit, OnChanges {
+ @Input() visible: boolean;
+ @Input() readonly: boolean;
+ @Input() sysUser: SysUser;
+ @Input() sysDeptTreeList: SysDeptSelect[];
+ @Output() readonly close: EventEmitter<any> = new EventEmitter<any>();
+ @Output() readonly submit: EventEmitter<Partial<SysUser>> = new EventEmitter<Partial<SysUser>>();
+
+ form: FormGroup;
+ labelSpan = 5;
+ controlSpan = 14;
+ sexDictSelect: SysDictItem[] = [];
+ statusDictSelect: SysDictItem[] = [];
+ title = 'Add';
+
+ constructor(private fb: FormBuilder, private systemUtilsService: SystemUtilsService) {
+ }
+
+ ngOnInit() {
+ this.form = this.fb.group(
+ {
+ userName: new FormControl('',
+ {
+ updateOn: 'blur',
+ validators: [Validators.required],
+ asyncValidators: [this.userNameValidator]
+ }
+ ),
+ password: ['', [Validators.required, Validators.pattern(/^(?=.*[a-zA-Z])(?=.*\d)(?=.*[~!@#$%^&*()_+`\-={}:";'<>?,./]).{8,}$/)]],
+ confirm: ['', this.confirmValidator],
+ realName: ['', Validators.required],
+ deptCode: [],
+ sex: [],
+ status: [],
+ birthday: [],
+ email: new FormControl('', {
+ updateOn: 'blur',
+ validators: [Validators.email],
+ asyncValidators: [this.emailValidator]
+ }),
+ phone: new FormControl('', {
+ updateOn: 'blur',
+ asyncValidators: [this.phoneValidator]
+ })
+ }
+ );
+
+ zip(
+ this.systemUtilsService.fetchSysDictByCode(SysDictCode.USER_SEX),
+ this.systemUtilsService.fetchSysDictByCode(SysDictCode.USER_STATUS)
+ )
+ .pipe(map(([item1, item2]) => [item1.records, item2.records]))
+ .subscribe(([sexDictSelect, statusDictSelect]) => {
+ this.sexDictSelect = sexDictSelect;
+ this.statusDictSelect = statusDictSelect;
+ });
+ }
+
+ ngOnChanges(changes: SimpleChanges): void {
+ if (!changes.visible) {
+ return;
+ }
+
+ if (changes.visible.currentValue) {
+ const sysUser = this.sysUser;
+ const readOnly = this.readonly;
+
+ this.form.reset({
+ userName: sysUser ? sysUser.userName : '',
+ password: sysUser ? sysUser.password : '',
+ confirm: sysUser ? sysUser.password : '',
+ realName: sysUser ? sysUser.realName : '',
+ deptCode: {
+ value: sysUser ? sysUser.deptCode : '',
+ disabled: readOnly
+ },
+ sex: {
+ value: sysUser ? sysUser.sex : '',
+ disabled: readOnly
+ },
+ status: {
+ value: sysUser ? sysUser.status : '',
+ disabled: readOnly
+ },
+ birthday: {
+ value: (sysUser && sysUser.birthday) ? new Date(sysUser.birthday) : '',
+ disabled: readOnly
+ },
+ email: sysUser ? sysUser.email : '',
+ phone: sysUser ? sysUser.phone : ''
+ });
+ this.title = this.generateTitle();
+ }
+ }
+
+ onClose() {
+ this.close.emit();
+ }
+
+ onSubmit() {
+ for (const key in this.form.controls) {
+ this.form.controls[key].markAsDirty();
+ this.form.controls[key].updateValueAndValidity();
+ }
+
+ this.form.statusChanges.pipe(
+ startWith(this.form.status),
+ filter(status => status !== 'PENDING'),
+ take(1),
+ map(status => status === 'VALID')
+ ).subscribe(valid => {
+ if (valid) {
+ const sysUser = this.sysUser ? this.sysUser : {};
+ const formData = { ...sysUser, ...this.form.value };
+
+ delete formData.confirm;
+ delete formData['sex@dict'];
+ delete formData['status@dict'];
+
+ if (formData.birthday) {
+ formData.birthday = format(formData.birthday, 'yyyy-MM-dd HH:mm:ss');
+ }
+
+ this.submit.emit(formData);
+ }
+ });
+ }
+
+ generateTitle(): string {
+ if (this.readonly) {
+ return 'Details';
+ } else if (this.sysUser && this.sysUser.id) {
+ return 'Edit';
+ } else {
+ return 'Add';
+ }
+ }
+
+ confirmValidator = (control: FormControl): { [s: string]: boolean } => {
+ if (!control.value) {
+ return { error: true, required: true };
+ } else if (control.value !== this.form.controls.password.value) {
+ return { error: true, confirm: true };
+ }
+
+ return {};
+ };
+
+ validateConfirmPassword = () => {
+ setTimeout(() => {
+ this.form.controls.confirm.updateValueAndValidity();
+ });
+ };
+
+ userNameValidator = (control: FormControl) =>
+ new Observable((observer: Observer<ValidationErrors | null>) => {
+ this.systemUtilsService
+ .duplicateCheckUsername(control.value, this.sysUser && this.sysUser.id)
+ .subscribe(status => {
+ if (status) {
+ observer.next(null);
+ } else {
+ observer.next({ error: true, duplicated: true });
+ }
+
+ observer.complete();
+ });
+ });
+
+ emailValidator = (control: FormControl) =>
+ new Observable((observer: Observer<ValidationErrors | null>) => {
+ if (!control.value) {
+ observer.next(null);
+ return observer.complete();
+ }
+
+ this.systemUtilsService
+ .duplicateCheckUserEmail(control.value, this.sysUser && this.sysUser.id)
+ .subscribe(status => {
+ if (status) {
+ observer.next(null);
+ } else {
+ observer.next({ error: true, duplicated: true });
+ }
+
+ observer.complete();
+ });
+ });
+
+ phoneValidator = (control: FormControl) =>
+ new Observable((observer: Observer<ValidationErrors | null>) => {
+ if (!control.value) {
+ observer.next(null);
+ return observer.complete();
+ }
+
+ this.systemUtilsService
+ .duplicateCheckUserPhone(control.value, this.sysUser && this.sysUser.id)
+ .subscribe(status => {
+ if (status) {
+ observer.next(null);
+ } else {
+ observer.next({ error: true, duplicated: true });
+ }
+
+ observer.complete();
+ });
+ });
+}
diff --git a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/user-password-modal/user-password-modal.component.html b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/user-password-modal/user-password-modal.component.html
new file mode 100644
index 0000000..9ae1ea9
--- /dev/null
+++ b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/user-password-modal/user-password-modal.component.html
@@ -0,0 +1,62 @@
+<!--
+ ~ 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-modal [nzVisible]="visible" nzTitle="Reset Password" nzCancelText="Close" nzOkText="Ok" (nzOnCancel)="hideModal()"
+ (nzOnOk)="submitForm()">
+ <form nz-form nzLayout="horizontal" [formGroup]="form">
+ <nz-form-item>
+ <nz-form-label>
+ Account Name
+ </nz-form-label>
+ <nz-form-control>
+ <input type="text" nz-input readonly formControlName="accountName"/>
+ </nz-form-control>
+ </nz-form-item>
+ <nz-form-item>
+ <nz-form-label nzRequired>
+ Password
+ </nz-form-label>
+ <nz-form-control nzHasFeedback nzErrorTip="Please input new password">
+ <input
+ nz-input
+ type="password"
+ formControlName="password"
+ (ngModelChange)="validateConfirmPassword()"
+ />
+ </nz-form-control>
+ </nz-form-item>
+ <nz-form-item>
+ <nz-form-label nzRequired>
+ Confirm Password
+ </nz-form-label>
+ <nz-form-control nzHasFeedback [nzErrorTip]="passwordErrorTpl">
+ <input type="password" nz-input formControlName="confirm"/>
+
+ <ng-template #passwordErrorTpl let-control>
+ <ng-container *ngIf="control.hasError('required')">
+ Please confirm your password!
+ </ng-container>
+ <ng-container *ngIf="control.hasError('confirm')">
+ Password is inconsistent!
+ </ng-container>
+ </ng-template>
+ </nz-form-control>
+ </nz-form-item>
+ </form>
+</nz-modal>
diff --git a/submarine-workbench/workbench-web-ng/src/app/pages/manager/user/user.component.scss b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/user-password-modal/user-password-modal.component.scss
similarity index 100%
copy from submarine-workbench/workbench-web-ng/src/app/pages/manager/user/user.component.scss
copy to submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/user-password-modal/user-password-modal.component.scss
diff --git a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/user-password-modal/user-password-modal.component.ts b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/user-password-modal/user-password-modal.component.ts
new file mode 100644
index 0000000..2478ff6
--- /dev/null
+++ b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/user-password-modal/user-password-modal.component.ts
@@ -0,0 +1,83 @@
+/*
+ * 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, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core';
+import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms';
+
+@Component({
+ selector: 'submarine-user-password-modal',
+ templateUrl: './user-password-modal.component.html',
+ styleUrls: ['./user-password-modal.component.scss']
+})
+export class UserPasswordModalComponent implements OnChanges {
+ @Input() accountName: string;
+ @Input() visible: boolean;
+ @Output() readonly close: EventEmitter<any> = new EventEmitter();
+ @Output() readonly ok: EventEmitter<string> = new EventEmitter();
+ form: FormGroup;
+
+ constructor(private fb: FormBuilder) {
+ this.form = this.fb.group({
+ accountName: [''],
+ password: ['', Validators.required],
+ confirm: ['', this.confirmValidator]
+ });
+ }
+
+ ngOnChanges(changes: SimpleChanges) {
+ this.form.reset({
+ accountName: this.accountName,
+ password: '',
+ confirm: ''
+ });
+ }
+
+ confirmValidator = (control: FormControl): { [s: string]: boolean } => {
+ if (!control.value) {
+ return { error: true, required: true };
+ } else if (control.value !== this.form.controls.password.value) {
+ return { error: true, confirm: true };
+ }
+
+ return {};
+ };
+
+ validateConfirmPassword = () => {
+ setTimeout(() => {
+ this.form.controls.confirm.updateValueAndValidity();
+ });
+ };
+
+ hideModal() {
+ this.close.emit();
+ }
+
+ submitForm() {
+ for (const key in this.form.controls) {
+ this.form.controls[key].markAsDirty();
+ this.form.controls[key].updateValueAndValidity();
+ }
+
+ if (!this.form.valid) {
+ return;
+ }
+
+ this.ok.emit(this.form.value.password);
+ }
+}
diff --git a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/user/user.component.html b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/user/user.component.html
new file mode 100644
index 0000000..d13467a
--- /dev/null
+++ b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/user/user.component.html
@@ -0,0 +1,131 @@
+<!--
+ ~ 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="user-table-operate">
+ <form nz-form [nzLayout]="'inline'" [formGroup]="form">
+ <nz-form-item>
+ <nz-form-label>Department</nz-form-label>
+ <nz-form-control>
+ <nz-tree-select
+ style="width: 171px"
+ nzAllowClear
+ nzDefaultExpandAll
+ nzShowSearch
+ nzPlaceHolder="Please select"
+ formControlName="deptCode"
+ [nzNodes]="sysDeptTreeList"
+ ></nz-tree-select>
+ </nz-form-control>
+ </nz-form-item>
+ <nz-form-item>
+ <nz-form-label>Account Name</nz-form-label>
+ <nz-form-control>
+ <input nz-input formControlName="accountName"/>
+ </nz-form-control>
+ </nz-form-item>
+ <nz-form-item>
+ <nz-form-label>Email</nz-form-label>
+ <nz-form-control>
+ <input nz-input formControlName="email"/>
+ </nz-form-control>
+ </nz-form-item>
+ <nz-form-item>
+ <nz-form-control>
+ <button nz-button nzType="primary" (click)="queryUserList()">
+ <i nz-icon nzType="search"></i>
+ Query
+ </button>
+ <button nz-button style="margin-left: 8px" (click)="onShowUserDrawer()">
+ <i nz-icon nzType="plus"></i>
+ Add User
+ </button>
+ </nz-form-control>
+ </nz-form-item>
+ </form>
+ </div>
+
+ <nz-table #table [nzData]="userList" [nzScroll]="{ x: '1100px' }" nzNoResult="No result" nzBordered>
+ <thead>
+ <tr>
+ <th>Account Name</th>
+ <th>Real Name</th>
+ <th>Department</th>
+ <th>Role</th>
+ <th>Status</th>
+ <th>Sex</th>
+ <th>Email</th>
+ <th nzWidth="120px">Create Time</th>
+ <th nzWidth="120px" nzRight="0px">Action</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr *ngFor="let data of table.data">
+ <td>{{ data.userName }}</td>
+ <td>{{ data.realName }}</td>
+ <td>{{ data.deptName }}</td>
+ <td>{{ data.roleName }}</td>
+ <td>{{ data['status@dict'] }}</td>
+ <td>{{ data['sex@dict'] }}</td>
+ <td>{{ data.email }}</td>
+ <td>{{ data.createTime }}</td>
+ <td class="td-action" nzRight="0px">
+ <a (click)="onShowUserDrawer(data)">Edit</a>
+ <a nz-dropdown [nzDropdownMenu]="more">
+ More
+ <i nz-icon nzType="down"></i>
+ </a>
+ <nz-dropdown-menu #more="nzDropdownMenu">
+ <ul nz-menu nzSelectable>
+ <li nz-menu-item (click)="onShowUserDrawer(data, true)">Details</li>
+ <li nz-menu-item (click)="onShowResetPasswordModal(data)">Password</li>
+ <li
+ nz-menu-item
+ nz-popconfirm
+ nzPlacement="left"
+ nzTitle="Confirm to delete?"
+ nzCancelText="Cancel"
+ nzOkText="Ok"
+ (nzOnConfirm)="onDeleteUser(data)"
+ >
+ Delete
+ </li>
+ </ul>
+ </nz-dropdown-menu>
+ </td>
+ </tr>
+ </tbody>
+ </nz-table>
+</nz-card>
+
+<submarine-user-password-modal
+ [visible]="resetPasswordModalVisible"
+ [accountName]="currentSysUser?.userName"
+ (close)="onHideResetPasswordModal()"
+ (ok)="onChangePassword($event)"
+></submarine-user-password-modal>
+
+<submarine-user-drawer
+ [visible]="userDrawerVisible"
+ [readonly]="userDrawerReadonly"
+ [sysDeptTreeList]="sysDeptTreeList"
+ [sysUser]="currentSysUser"
+ (close)="onCloseUserDrawer()"
+ (submit)="onSubmitUserDrawer($event)"
+></submarine-user-drawer>
diff --git a/submarine-workbench/workbench-web-ng/src/app/pages/manager/user/user.component.scss b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/user/user.component.scss
similarity index 86%
rename from submarine-workbench/workbench-web-ng/src/app/pages/manager/user/user.component.scss
rename to submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/user/user.component.scss
index 510f082..937c4c8 100644
--- a/submarine-workbench/workbench-web-ng/src/app/pages/manager/user/user.component.scss
+++ b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/user/user.component.scss
@@ -17,3 +17,14 @@
* under the License.
*/
+.td-action a + a {
+ margin-left: 8px;
+}
+
+.user-table-operate {
+ margin-bottom: 16px;
+}
+
+nz-table td {
+ word-break: break-all;
+}
diff --git a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/user/user.component.ts b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/user/user.component.ts
new file mode 100644
index 0000000..3c544e0
--- /dev/null
+++ b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/manager/user/user.component.ts
@@ -0,0 +1,172 @@
+/*
+ * 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 { FormBuilder, FormGroup } from '@angular/forms';
+import { SysUser } from '@submarine/interfaces';
+import { SysDeptSelect } from '@submarine/interfaces/sys-dept-select';
+import { DepartmentService, UserService } from '@submarine/services';
+import { NzMessageService } from 'ng-zorro-antd';
+
+@Component({
+ selector: 'submarine-manager-user',
+ templateUrl: './user.component.html',
+ styleUrls: ['./user.component.scss']
+})
+export class UserComponent implements OnInit {
+ column: string = 'createdTime';
+ order: string = 'desc';
+ field = [
+ 'id',
+ 'userName',
+ 'realName',
+ 'deptName',
+ 'roleCode',
+ 'status@dict',
+ 'sex@dict',
+ 'email',
+ 'createTime',
+ 'action'
+ ];
+ accountName: string = '';
+ email: string = '';
+ deptCode: string = '';
+ pageNo: number = 1;
+ pageSize: number = 10;
+ userList: SysUser[] = [];
+ total: number = 0;
+ sysDeptTreeList: SysDeptSelect[] = [];
+ form: FormGroup;
+ resetPasswordModalVisible: boolean = false;
+ currentSysUser: SysUser;
+ userDrawerVisible: boolean = false;
+ private userDrawerReadonly: boolean = false;
+
+ constructor(
+ private userService: UserService,
+ private deptService: DepartmentService,
+ private fb: FormBuilder,
+ private nzMessageService: NzMessageService
+ ) {
+ }
+
+ ngOnInit() {
+ this.fetchUserList();
+
+ this.deptService.fetchSysDeptSelect().subscribe(list => {
+ this.sysDeptTreeList = list;
+ });
+
+ this.form = this.fb.group({
+ deptCode: [this.deptCode],
+ accountName: [this.accountName],
+ email: [this.email]
+ });
+ }
+
+ queryUserList() {
+ const { deptCode, accountName, email } = this.form.getRawValue();
+ this.deptCode = deptCode;
+ this.accountName = accountName;
+ this.email = email;
+
+ this.fetchUserList();
+ }
+
+ fetchUserList() {
+ this.userService
+ .fetchUserList({
+ column: this.column,
+ order: this.order,
+ field: this.field.join(','),
+ accountName: this.accountName,
+ email: this.email,
+ deptCode: this.deptCode ? this.deptCode : '',
+ pageNo: '' + this.pageNo,
+ pageSize: '' + this.pageSize
+ })
+ .subscribe(({ records, total }) => {
+ this.total = total;
+ this.userList = records;
+ });
+ }
+
+ onShowResetPasswordModal(data: SysUser) {
+ this.currentSysUser = data;
+ this.resetPasswordModalVisible = true;
+ }
+
+ onHideResetPasswordModal() {
+ this.currentSysUser = null;
+ this.resetPasswordModalVisible = false;
+ }
+
+ onChangePassword(password: string) {
+ const { id } = this.currentSysUser;
+
+ this.resetPasswordModalVisible = false;
+
+ this.userService.changePassword(id, password).subscribe(() => {
+ this.nzMessageService.success('Change password success!');
+ }, () => {
+ this.nzMessageService.error('Change password error');
+ });
+ }
+
+ onShowUserDrawer(sysUser?: SysUser, readOnly = false) {
+ this.currentSysUser = sysUser;
+ this.userDrawerReadonly = readOnly;
+ this.userDrawerVisible = true;
+ }
+
+ onCloseUserDrawer() {
+ this.userDrawerVisible = false;
+ }
+
+ onSubmitUserDrawer(formData: Partial<SysUser>) {
+ this.userDrawerVisible = false;
+
+ if (formData.id) {
+ this.userService.updateUser(formData).subscribe(() => {
+ this.nzMessageService.success('Update user success!');
+ this.queryUserList();
+ }, err => {
+ this.nzMessageService.error(err.message);
+ });
+ } else {
+ this.userService.createUser(formData).subscribe(() => {
+ this.nzMessageService.success('Add user success!');
+ this.queryUserList();
+ }, err => {
+ this.nzMessageService.error(err.message);
+ });
+ }
+ }
+
+ onDeleteUser(data: SysUser) {
+ this.userService.deleteUser(data.id).subscribe(
+ () => {
+ this.nzMessageService.success('Delete user success!');
+ this.fetchUserList();
+ }, err => {
+ this.nzMessageService.success(err.message);
+ }
+ );
+ }
+}
diff --git a/submarine-workbench/workbench-web-ng/src/app/pages/manager/manager-routing.module.ts b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/workbench-routing.module.ts
similarity index 75%
rename from submarine-workbench/workbench-web-ng/src/app/pages/manager/manager-routing.module.ts
rename to submarine-workbench/workbench-web-ng/src/app/pages/workbench/workbench-routing.module.ts
index 97c775f..3c23d98 100644
--- a/submarine-workbench/workbench-web-ng/src/app/pages/manager/manager-routing.module.ts
+++ b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/workbench-routing.module.ts
@@ -19,29 +19,28 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
-import { ManagerComponent } from './manager.component';
-import { UserComponent } from './user/user.component';
+import { WorkbenchComponent } from '@submarine/pages/workbench/workbench.component';
const routes: Routes = [
{
path: '',
- component: ManagerComponent,
+ component: WorkbenchComponent,
children: [
{
path: '',
pathMatch: 'full',
- redirectTo: '/manager/user'
+ redirectTo: 'manager'
},
{
- path: 'user',
- component: UserComponent
+ path: 'manager',
+ loadChildren: () => import('./manager/manager.module').then(m => m.ManagerModule)
}
]
}
];
@NgModule({
- imports: [RouterModule.forChild(routes)],
- exports: [RouterModule]
+ imports: [RouterModule.forChild(routes)]
})
-export class ManagerRoutingModule {}
+export class WorkbenchRoutingModule {
+}
diff --git a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/workbench.component.html b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/workbench.component.html
new file mode 100644
index 0000000..f5b21c1
--- /dev/null
+++ b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/workbench.component.html
@@ -0,0 +1,82 @@
+<!--
+ ~ 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-sider
+ class="menu-sidebar"
+ nzCollapsible
+ nzWidth="256px"
+ nzBreakpoint="md"
+ [(nzCollapsed)]="isCollapsed"
+ [nzTrigger]="null"
+>
+ <div class="sidebar-logo">
+ <a routerLink="/workbench/dashboard" target="_blank">
+ <img src="/assets/logo.png" alt="logo"/>
+ <h1>Submarine</h1>
+ </a>
+ </div>
+ <ul nz-menu nzTheme="dark" nzMode="inline" [nzInlineCollapsed]="isCollapsed">
+ <ng-container *ngFor="let menu of menus">
+ <li *ngIf="menu.children" nz-submenu nzOpen [nzTitle]="menu.title" [nzIcon]="menu.iconType">
+ <ul>
+ <li nz-menu-item nzMatchRouter *ngFor="let subItem of menu.children">
+ <a [routerLink]="subItem.routerLink">{{ subItem.title }}</a>
+ </li>
+ </ul>
+ </li>
+ <li *ngIf="!menu.children" nz-menu-item nz-tooltip nzPlacement="right" [nzTitle]="menu.title" nzMatchRouter>
+ <i nz-icon [nzType]="menu.iconType"></i>
+ <span>
+ <a [routerLink]="menu.routerLink">{{ menu.title }}</a>
+ </span>
+ </li>
+ </ng-container>
+ </ul>
+</nz-sider>
+<nz-layout [ngClass]="isCollapsed ? 'close' : ''">
+ <nz-header>
+ <div class="app-header">
+ <div class="header-trigger" (click)="isCollapsed = !isCollapsed">
+ <i class="trigger" nz-icon [nzType]="isCollapsed ? 'menu-unfold' : 'menu-fold'"></i>
+ </div>
+
+ <div class="header-operation">
+ <div nz-dropdown *ngIf="userInfo$ | async as userInfo" [nzDropdownMenu]="userInfoMenu"
+ class="operation-user-info">
+ <nz-avatar nzIcon="user" nzSize="small"></nz-avatar>
+ {{userInfo.name}}
+ </div>
+ <nz-dropdown-menu #userInfoMenu="nzDropdownMenu">
+ <ul nz-menu>
+ <li nz-menu-item nzDisabled>
+ <i nz-icon nzType="setting"></i>UserInfo setting
+ </li>
+ <li nz-menu-divider></li>
+ <li nz-menu-item (click)="logout()">
+ <i nz-icon nzType="logout"></i>Sign out
+ </li>
+ </ul>
+ </nz-dropdown-menu>
+ </div>
+ </div>
+ </nz-header>
+ <nz-content>
+ <router-outlet></router-outlet>
+ </nz-content>
+</nz-layout>
diff --git a/submarine-workbench/workbench-web-ng/src/app/app.component.scss b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/workbench.component.scss
similarity index 78%
copy from submarine-workbench/workbench-web-ng/src/app/app.component.scss
copy to submarine-workbench/workbench-web-ng/src/app/pages/workbench/workbench.component.scss
index 67bafe6..486c21f 100644
--- a/submarine-workbench/workbench-web-ng/src/app/app.component.scss
+++ b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/workbench.component.scss
@@ -24,23 +24,26 @@
-moz-osx-font-smoothing: grayscale;
}
-.app-layout {
- height: 100vh;
-}
-
.menu-sidebar {
position: relative;
z-index: 10;
min-height: 100vh;
- box-shadow: 2px 0 6px rgba(0,21,41,.35);
+ box-shadow: 2px 0 6px rgba(0, 21, 41, .35);
}
.header-trigger {
height: 64px;
- padding: 20px 24px;
font-size: 20px;
cursor: pointer;
- transition: all .3s,padding 0s;
+ transition: all .3s, padding 0s;
+}
+
+.operation-user-info {
+ color: #333;
+}
+
+nz-avatar {
+ margin-right: 8px;
}
.trigger:hover {
@@ -69,8 +72,8 @@
margin: 0 0 0 20px;
color: #fff;
font-weight: 600;
- font-size: 14px;
- font-family: Avenir,Helvetica Neue,Arial,Helvetica,sans-serif;
+ font-size: 20px;
+ font-family: Avenir, Helvetica Neue, Arial, Helvetica, sans-serif;
vertical-align: middle;
}
@@ -83,17 +86,22 @@ nz-header {
.app-header {
position: relative;
height: 64px;
- padding: 0;
+ padding: 0 24px;
background: #fff;
- box-shadow: 0 1px 4px rgba(0,21,41,.08);
+ box-shadow: 0 1px 4px rgba(0, 21, 41, .08);
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
}
nz-content {
margin: 24px;
}
-.inner-content {
- padding: 24px;
- background: #fff;
- height: 100%;
+nz-layout {
+ max-width: calc(100% - 256px);
+
+ &.close {
+ max-width: calc(100% - 80px);
+ }
}
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
new file mode 100644
index 0000000..62732e2
--- /dev/null
+++ b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/workbench.component.ts
@@ -0,0 +1,88 @@
+/*
+ * 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 { Router } from '@angular/router';
+import { UserInfo } from '@submarine/interfaces';
+import { AuthService, UserService } from '@submarine/services';
+import { NzNotificationService } from 'ng-zorro-antd';
+import { Observable } from 'rxjs';
+import { tap } from 'rxjs/operators';
+
+interface SidebarMenu {
+ title: string;
+ iconType: string;
+ routerLink?: string;
+ children?: Array<{
+ title: string;
+ routerLink?: string;
+ }>;
+}
+
+@Component({
+ selector: 'submarine-workbench',
+ templateUrl: './workbench.component.html',
+ styleUrls: ['./workbench.component.scss']
+})
+export class WorkbenchComponent implements OnInit {
+ isCollapsed: boolean = false;
+ menus: SidebarMenu[] = [
+ {
+ title: 'Manager',
+ iconType: 'setting',
+ children: [
+ {
+ title: 'User',
+ routerLink: '/workbench/manager/user'
+ },
+ {
+ title: 'Data dict',
+ routerLink: '/workbench/manager/data-dict'
+ }
+ ]
+ }
+ ];
+ userInfo$: Observable<UserInfo>;
+
+ constructor(
+ private router: Router,
+ private authService: AuthService,
+ private userService: UserService,
+ private nzNotificationService: NzNotificationService
+ ) {
+ }
+
+ ngOnInit() {
+ if (this.authService.isLoggedIn) {
+ this.userInfo$ = this.userService.fetchUserInfo().pipe(
+ tap(userInfo => {
+ this.nzNotificationService.success('Welcome', `Welcome back, ${userInfo.name}`);
+ })
+ );
+ }
+ }
+
+ logout() {
+ this.authService.logout().subscribe(isLogout => {
+ if (isLogout) {
+ this.router.navigate(['/user/login']);
+ }
+ });
+ }
+}
diff --git a/submarine-workbench/workbench-web-ng/src/app/pages/manager/manager.module.ts b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/workbench.module.ts
similarity index 73%
rename from submarine-workbench/workbench-web-ng/src/app/pages/manager/manager.module.ts
rename to submarine-workbench/workbench-web-ng/src/app/pages/workbench/workbench.module.ts
index cfabafe..c49faca 100644
--- a/submarine-workbench/workbench-web-ng/src/app/pages/manager/manager.module.ts
+++ b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/workbench.module.ts
@@ -19,13 +19,14 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
+import { RouterModule } from '@angular/router';
+import { WorkbenchRoutingModule } from '@submarine/pages/workbench/workbench-routing.module';
import { NgZorroAntdModule } from 'ng-zorro-antd';
-import { ManagerRoutingModule } from './manager-routing.module';
-import { ManagerComponent } from './manager.component';
-import { UserComponent } from './user/user.component';
+import { WorkbenchComponent } from './workbench.component';
@NgModule({
- declarations: [UserComponent, ManagerComponent],
- imports: [CommonModule, ManagerRoutingModule, NgZorroAntdModule]
+ declarations: [WorkbenchComponent],
+ imports: [CommonModule, WorkbenchRoutingModule, NgZorroAntdModule, RouterModule]
})
-export class ManagerModule {}
+export class WorkbenchModule {
+}
diff --git a/submarine-workbench/workbench-web-ng/src/app/services/auth.service.ts b/submarine-workbench/workbench-web-ng/src/app/services/auth.service.ts
index 1f3865a..099ec2f 100644
--- a/submarine-workbench/workbench-web-ng/src/app/services/auth.service.ts
+++ b/submarine-workbench/workbench-web-ng/src/app/services/auth.service.ts
@@ -19,12 +19,12 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
-import { Rest, User } from '@submarine/interfaces';
-import { BaseApiService } from '@submarine/services/base-api.service';
-import { LocalStorageService } from '@submarine/services/local-storage.service';
+import { Rest, SysUser } from '@submarine/interfaces';
import * as md5 from 'md5';
-import { Observable } from 'rxjs';
-import { map } from 'rxjs/operators';
+import { of, Observable } from 'rxjs';
+import { map, switchMap } from 'rxjs/operators';
+import { BaseApiService } from './base-api.service';
+import { LocalStorageService } from './local-storage.service';
@Injectable({
providedIn: 'root'
@@ -45,26 +45,36 @@ export class AuthService {
this.isLoggedIn = !!authToken;
}
- login(userForm: { userName: string; password: string }): Observable<boolean> {
- return this.httpClient
- .post<Rest<User>>(this.baseApi.getRestApi('/auth/login'), {
- username: userForm.userName,
- password: md5(userForm.password)
- })
- .pipe(
- map(res => {
- if (res.success) {
- this.isLoggedIn = true;
- this.localStorageService.set(this.authTokenKey, res.result.token);
- }
+ login(userForm: { userName: string; password: string }): Observable<SysUser> {
+ const apiUrl = this.baseApi.getRestApi('/auth/login');
+ const params = {
+ username: userForm.userName,
+ password: md5(userForm.password)
+ };
- return res.success;
- })
- );
+ return this.httpClient.post<Rest<SysUser>>(apiUrl, params).pipe(
+ switchMap(res => {
+ if (res.success) {
+ this.isLoggedIn = true;
+ this.localStorageService.set(this.authTokenKey, res.result.token);
+ return of(res.result);
+ } else {
+ throw this.baseApi.createRequestError(res.message, res.code, apiUrl, 'post', params);
+ }
+ })
+ );
}
- logout(): void {
- this.isLoggedIn = false;
- this.localStorageService.remove(this.authTokenKey);
+ logout() {
+ return this.httpClient.post<Rest<boolean>>(this.baseApi.getRestApi('/auth/logout'), {}).pipe(
+ map(res => {
+ if (res.result) {
+ this.isLoggedIn = false;
+ this.localStorageService.remove(this.authTokenKey);
+ }
+
+ return res.result;
+ })
+ );
}
}
diff --git a/submarine-workbench/workbench-web-ng/src/app/services/base-api.service.ts b/submarine-workbench/workbench-web-ng/src/app/services/base-api.service.ts
index 076ab1f..86b2710 100644
--- a/submarine-workbench/workbench-web-ng/src/app/services/base-api.service.ts
+++ b/submarine-workbench/workbench-web-ng/src/app/services/base-api.service.ts
@@ -18,6 +18,37 @@
*/
import { Injectable } from '@angular/core';
+import { environment } from '@submarine/environment';
+
+class HttpError extends Error {
+ code: number;
+ url: string;
+ method: string;
+ params: object;
+
+ constructor(message: string, code: number, url?: string, method?: string, params?: object) {
+ super(message);
+
+ this.code = code;
+ this.url = url;
+ this.method = method;
+ this.params = params;
+
+ if (!environment.production) {
+ this.logError();
+ }
+ }
+
+ logError() {
+ console.group('Http error');
+ console.log('error message: ', this.message);
+ console.log('error code: ', this.code);
+ console.log('-------------------------------');
+ console.log('url: ', this.url);
+ console.log('method: ', this.method);
+ console.log('params: ', this.params);
+ }
+}
@Injectable({
providedIn: 'root'
@@ -52,6 +83,10 @@ export class BaseApiService {
return `${this.getRestApiBase()}${str}`;
}
+ createRequestError(message: string, code: number, url?: string, method?: string, params?: any) {
+ return new HttpError(message, code, url, method, params);
+ }
+
private skipTrailingSlash(path) {
return path.replace(/\/$/, '');
}
diff --git a/submarine-workbench/workbench-web-ng/src/app/services/base-api.service.ts b/submarine-workbench/workbench-web-ng/src/app/services/department.service.ts
similarity index 52%
copy from submarine-workbench/workbench-web-ng/src/app/services/base-api.service.ts
copy to submarine-workbench/workbench-web-ng/src/app/services/department.service.ts
index 076ab1f..0872420 100644
--- a/submarine-workbench/workbench-web-ng/src/app/services/base-api.service.ts
+++ b/submarine-workbench/workbench-web-ng/src/app/services/department.service.ts
@@ -17,42 +17,32 @@
* under the License.
*/
+import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
+import { Rest } from '@submarine/interfaces';
+import { SysDeptSelect } from '@submarine/interfaces/sys-dept-select';
+import { of, Observable } from 'rxjs';
+import { switchMap } from 'rxjs/operators';
+import { BaseApiService } from './base-api.service';
@Injectable({
providedIn: 'root'
})
-export class BaseApiService {
- baseApi: string;
+export class DepartmentService {
- getPort() {
- let port = Number(location.port);
- if (!port) {
- port = 80;
- if (location.protocol === 'https:') {
- port = 443;
- }
- }
- return port;
+ constructor(private baseApi: BaseApiService, private httpClient: HttpClient) {
}
- getBase() {
- return `${location.protocol}//${location.hostname}:${this.getPort()}`;
- }
-
- getRestApiBase() {
- if (!this.baseApi) {
- this.baseApi = this.skipTrailingSlash(this.getBase()) + '/api';
- }
-
- return this.baseApi;
- }
-
- getRestApi(str: string): string {
- return `${this.getRestApiBase()}${str}`;
- }
-
- private skipTrailingSlash(path) {
- return path.replace(/\/$/, '');
+ fetchSysDeptSelect(): Observable<SysDeptSelect[]> {
+ const apiUrl = this.baseApi.getRestApi('/sys/dept/queryIdTree');
+ return this.httpClient.get<Rest<SysDeptSelect[]>>(apiUrl).pipe(
+ switchMap(res => {
+ if (res.success) {
+ return of(res.result);
+ } else {
+ throw this.baseApi.createRequestError(res.message, res.code, apiUrl, 'get');
+ }
+ })
+ );
}
}
diff --git a/submarine-workbench/workbench-web-ng/src/app/services/public-api.ts b/submarine-workbench/workbench-web-ng/src/app/services/public-api.ts
index a853cff..4bc5376 100644
--- a/submarine-workbench/workbench-web-ng/src/app/services/public-api.ts
+++ b/submarine-workbench/workbench-web-ng/src/app/services/public-api.ts
@@ -18,3 +18,8 @@
*/
export * from './auth.service';
+export * from './base-api.service';
+export * from './department.service';
+export * from './local-storage.service';
+export * from './system-utils.service';
+export * from './user.service';
diff --git a/submarine-workbench/workbench-web-ng/src/app/services/system-utils.service.ts b/submarine-workbench/workbench-web-ng/src/app/services/system-utils.service.ts
new file mode 100644
index 0000000..6046968
--- /dev/null
+++ b/submarine-workbench/workbench-web-ng/src/app/services/system-utils.service.ts
@@ -0,0 +1,95 @@
+/*
+ * 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 { HttpClient } from '@angular/common/http';
+import { Injectable } from '@angular/core';
+import { ListResult, Rest } from '@submarine/interfaces';
+import { SysDictItem } from '@submarine/interfaces/sys-dict-item';
+import { BaseApiService } from './base-api.service';
+import { of, Observable } from 'rxjs';
+import { switchMap } from 'rxjs/operators';
+
+export enum SysDictCode {
+ 'USER_SEX' = 'SYS_USER_SEX',
+ 'USER_STATUS' = 'SYS_USER_STATUS'
+}
+
+@Injectable({
+ providedIn: 'root'
+})
+export class SystemUtilsService {
+ dictItemCache: { [s: string]: ListResult<any> } = {};
+
+ constructor(private httpClient: HttpClient, private baseApi: BaseApiService) {
+ }
+
+ fetchSysDictByCode(code: SysDictCode): Observable<ListResult<SysDictItem>> {
+ if (this.dictItemCache[code]) {
+ return of(this.dictItemCache[code]);
+ }
+
+ const apiUrl = `${this.baseApi.getRestApi('/sys/dictItem/getDictItems')}/${code}`;
+
+ return this.httpClient.get<Rest<ListResult<SysDictItem>>>(apiUrl).pipe(
+ switchMap(res => {
+ if (res.success) {
+ this.dictItemCache[code] = res.result;
+ return of(res.result);
+ } else {
+ throw this.baseApi.createRequestError(res.message, res.code, apiUrl, 'get', code);
+ }
+ })
+ );
+ }
+
+ duplicateCheckUsername(userName: string, userId?: string) {
+ return this.duplicateCheck('sys_user', 'user_name', userName, userId);
+ }
+
+ duplicateCheckUserEmail(email: string, userId?: string) {
+ return this.duplicateCheck('sys_user', 'email', email, userId);
+ }
+
+ duplicateCheckUserPhone(phone: string, userId?: string) {
+ return this.duplicateCheck('sys_user', 'phone', phone, userId);
+ }
+
+ private duplicateCheck(
+ tableName: string,
+ fieldName: string,
+ fieldVal: string,
+ dataId?: string
+ ): Observable<boolean> {
+ const apiUrl = this.baseApi.getRestApi('/sys/duplicateCheck');
+ const params = {
+ tableName,
+ fieldName,
+ fieldVal,
+ dataId: dataId
+ };
+
+ return this.httpClient.get<Rest<string>>(apiUrl, {
+ params
+ }).pipe(
+ switchMap(res => {
+ return of(res.success);
+ })
+ );
+ }
+}
diff --git a/submarine-workbench/workbench-web-ng/src/app/services/user.service.ts b/submarine-workbench/workbench-web-ng/src/app/services/user.service.ts
new file mode 100644
index 0000000..65f914d
--- /dev/null
+++ b/submarine-workbench/workbench-web-ng/src/app/services/user.service.ts
@@ -0,0 +1,139 @@
+/*
+ * 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 { HttpClient } from '@angular/common/http';
+import { Injectable } from '@angular/core';
+import { ListResult, Rest, SysUser, UserInfo } from '@submarine/interfaces';
+import * as md5 from 'md5';
+import { of, Observable } from 'rxjs';
+import { switchMap } from 'rxjs/operators';
+import { BaseApiService } from './base-api.service';
+
+interface UserListQueryParams {
+ accountName: string;
+ email: string;
+ deptCode: string;
+ column: string;
+ order: string;
+ field: string;
+ pageNo: string;
+ pageSize: string;
+}
+
+@Injectable({
+ providedIn: 'root'
+})
+export class UserService {
+ private userInfo: UserInfo;
+
+ constructor(private httpClient: HttpClient, private baseApi: BaseApiService) {
+ }
+
+ fetchUserInfo(): Observable<UserInfo> {
+ const apiUrl = this.baseApi.getRestApi('/sys/user/info');
+ return this.httpClient.get<Rest<UserInfo>>(apiUrl).pipe(
+ switchMap(res => {
+ if (res.success) {
+ this.userInfo = new UserInfo(res.result);
+ return of(this.userInfo);
+ } else {
+ throw this.baseApi.createRequestError(res.message, res.code, apiUrl, 'get');
+ }
+ })
+ );
+ }
+
+ fetchUserList(queryParams: Partial<UserListQueryParams>): Observable<ListResult<SysUser>> {
+ const apiUrl = this.baseApi.getRestApi('/sys/user/list');
+ return this.httpClient.get<Rest<ListResult<SysUser>>>(apiUrl, {
+ params: queryParams
+ }).pipe(
+ switchMap(res => {
+ if (res.success) {
+ return of(res.result);
+ } else {
+ throw this.baseApi.createRequestError(res.message, res.code, apiUrl, 'get', queryParams);
+ }
+ })
+ );
+ }
+
+ changePassword(id: string, password: string): Observable<boolean> {
+ const apiUrl = this.baseApi.getRestApi('/sys/user/changePassword');
+
+ return this.httpClient.put<Rest<any>>(apiUrl, {
+ id,
+ password: md5(password)
+ }).pipe(
+ switchMap(res => {
+ if (res.success) {
+ return of(true);
+ } else {
+ throw this.baseApi.createRequestError(res.message, res.code, apiUrl, 'put', { id, password });
+ }
+ })
+ );
+ }
+
+ createUser(sysUser: Partial<SysUser>): Observable<SysUser> {
+ const apiUrl = this.baseApi.getRestApi('/sys/user/add');
+
+ return this.httpClient.post<Rest<SysUser>>(apiUrl, sysUser).pipe(
+ switchMap(res => {
+ if (res.success) {
+ return of(res.result);
+ } else {
+ throw this.baseApi.createRequestError(res.message, res.code, apiUrl, 'post', sysUser);
+ }
+ })
+ );
+ }
+
+ updateUser(sysUser: Partial<SysUser>): Observable<SysUser> {
+ const apiUrl = this.baseApi.getRestApi('/sys/user/edit');
+
+ return this.httpClient.put<Rest<SysUser>>(apiUrl, sysUser).pipe(
+ switchMap(res => {
+ if (res.success) {
+ return of(res.result);
+ } else {
+ throw this.baseApi.createRequestError(res.message, res.code, apiUrl, 'put', sysUser);
+ }
+ })
+ );
+ }
+
+ deleteUser(id: string): Observable<boolean> {
+ const apiUrl = this.baseApi.getRestApi(`/sys/user/delete`);
+
+ return this.httpClient.delete<Rest<any>>(apiUrl, {
+ params: {
+ id
+ }
+ }).pipe(
+ switchMap(res => {
+ if (res.success) {
+ return of(true);
+ } else {
+ throw this.baseApi.createRequestError(res.message, res.code, apiUrl, 'delete', id);
+ }
+ })
+ );
+ }
+}
diff --git a/submarine-workbench/workbench-web-ng/src/assets/logo.png b/submarine-workbench/workbench-web-ng/src/assets/logo.png
new file mode 100644
index 0000000..ff55ec2
Binary files /dev/null and b/submarine-workbench/workbench-web-ng/src/assets/logo.png differ
diff --git a/submarine-workbench/workbench-web-ng/src/main.ts b/submarine-workbench/workbench-web-ng/src/main.ts
index 6274974..4613957 100644
--- a/submarine-workbench/workbench-web-ng/src/main.ts
+++ b/submarine-workbench/workbench-web-ng/src/main.ts
@@ -27,5 +27,6 @@ if (environment.production) {
enableProdMode();
}
-platformBrowserDynamic().bootstrapModule(AppModule)
+platformBrowserDynamic()
+ .bootstrapModule(AppModule)
.catch(err => console.error(err));
diff --git a/submarine-workbench/workbench-web-ng/src/polyfills.ts b/submarine-workbench/workbench-web-ng/src/polyfills.ts
index 15f6f9c..7f618f4 100644
--- a/submarine-workbench/workbench-web-ng/src/polyfills.ts
+++ b/submarine-workbench/workbench-web-ng/src/polyfills.ts
@@ -74,8 +74,7 @@
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
-import 'zone.js/dist/zone'; // Included with Angular CLI.
-
+import 'zone.js/dist/zone'; // Included with Angular CLI.
/***************************************************************************************************
* APPLICATION IMPORTS
diff --git a/submarine-workbench/workbench-web-ng/src/test.ts b/submarine-workbench/workbench-web-ng/src/test.ts
index b90b1d8..d9617db 100644
--- a/submarine-workbench/workbench-web-ng/src/test.ts
+++ b/submarine-workbench/workbench-web-ng/src/test.ts
@@ -21,18 +21,12 @@
import 'zone.js/dist/zone-testing';
import { getTestBed } from '@angular/core/testing';
-import {
- BrowserDynamicTestingModule,
- platformBrowserDynamicTesting
-} from '@angular/platform-browser-dynamic/testing';
+import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing';
declare const require: any;
// First, initialize the Angular testing environment.
-getTestBed().initTestEnvironment(
- BrowserDynamicTestingModule,
- platformBrowserDynamicTesting()
-);
+getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting());
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@submarine.apache.org
For additional commands, e-mail: dev-help@submarine.apache.org