diff --git a/web-app/package.json b/web-app/package.json index eb686dc..e86f60d 100644 --- a/web-app/package.json +++ b/web-app/package.json @@ -22,6 +22,7 @@ "private": true, "dependencies": { "@angular/animations": "~12.2.0", + "@angular/cdk": "~12.2.0", "@angular/common": "~12.2.0", "@angular/compiler": "~12.2.0", "@angular/core": "~12.2.0", @@ -29,43 +30,38 @@ "@angular/platform-browser": "~12.2.0", "@angular/platform-browser-dynamic": "~12.2.0", "@angular/router": "~12.2.0", - "@angular/cdk": "~12.2.0", - "ng-alain": "^12.4.2", - "ng-zorro-antd": "^12.0.2", - "rxjs": "~6.6.0", - "tslib": "^2.3.0", - "zone.js": "~0.11.4", "@delon/abc": "^12.4.2", "@delon/acl": "^12.4.2", "@delon/auth": "^12.4.2", "@delon/cache": "^12.4.2", + "@delon/chart": "^12.4.2", "@delon/form": "^12.4.2", "@delon/mock": "^12.4.2", "@delon/theme": "^12.4.2", "@delon/util": "^12.4.2", - "@delon/chart": "^12.4.2", "ajv": "^8.6.2", "ajv-formats": "^2.1.1", - "screenfull": "^5.1.0" + "ng-alain": "^12.4.2", + "ng-zorro-antd": "^12.0.2", + "rxjs": "~6.6.0", + "screenfull": "^5.1.0", + "tslib": "^2.3.0", + "zone.js": "~0.11.4" }, "devDependencies": { "@angular-devkit/build-angular": "~12.2.13", - "@angular/cli": "~12.2.13", - "@angular/compiler-cli": "~12.2.0", - "@types/jasmine": "~3.8.0", - "@types/node": "^12.11.1", - "jasmine-core": "~3.8.0", - "karma": "~6.3.0", - "karma-chrome-launcher": "~3.1.0", - "karma-coverage": "~2.0.3", - "karma-jasmine": "~4.0.0", - "karma-jasmine-html-reporter": "~1.7.0", - "typescript": "~4.3.5", "@angular-eslint/builder": "~12.3.1", "@angular-eslint/eslint-plugin": "~12.3.1", "@angular-eslint/eslint-plugin-template": "~12.3.1", "@angular-eslint/schematics": "~12.3.1", "@angular-eslint/template-parser": "~12.3.1", + "@angular/cli": "~12.2.13", + "@angular/compiler-cli": "~12.2.0", + "@angular/language-service": "~12.2.0", + "@delon/testing": "^12.4.2", + "@ngx-formly/schematics": "^5.10.23", + "@types/jasmine": "~3.8.0", + "@types/node": "^12.11.1", "@typescript-eslint/eslint-plugin": "~4.29.2", "@typescript-eslint/parser": "~4.29.2", "eslint": "^7.32.0", @@ -74,19 +70,24 @@ "eslint-plugin-jsdoc": "~36.0.7", "eslint-plugin-prefer-arrow": "~1.2.3", "eslint-plugin-prettier": "^2.2.1", - "prettier": "^2.2.1", + "jasmine-core": "~3.8.0", + "karma": "~6.3.0", + "karma-chrome-launcher": "~3.1.0", + "karma-coverage": "~2.0.3", + "karma-jasmine": "~4.0.0", + "karma-jasmine-html-reporter": "~1.7.0", + "lint-staged": "^11.1.2", "ng-alain": "^12.4.2", "ng-alain-plugin-theme": "^12.0.0", + "prettier": "^2.2.1", "source-map-explorer": "^2.5.2", - "@angular/language-service": "~12.2.0", - "@delon/testing": "^12.4.2", - "lint-staged": "^11.1.2", "stylelint": "^13.13.1", "stylelint-config-prettier": "^8.0.2", "stylelint-config-rational-order": "^0.1.2", "stylelint-config-standard": "^22.0.0", "stylelint-declaration-block-no-ignored-properties": "^2.4.0", - "stylelint-order": "^4.1.0" + "stylelint-order": "^4.1.0", + "typescript": "~4.3.5" }, "lint-staged": { "(src)/**/*.{html,ts}": [ diff --git a/web-app/proxy.conf.js b/web-app/proxy.conf.js index 50258c5..ef28a11 100644 --- a/web-app/proxy.conf.js +++ b/web-app/proxy.conf.js @@ -9,8 +9,8 @@ module.exports = { /** * The following means that all requests are directed to the backend `https://localhost:9000/` */ - // '/': { - // target: 'https://localhost:9000/', + // '/apps/*': { + // target: 'https://localhost:8080', // secure: false, // Ignore invalid SSL certificates // changeOrigin: true // } diff --git a/web-app/src/app/app.module.ts b/web-app/src/app/app.module.ts index dd6860a..b20d469 100644 --- a/web-app/src/app/app.module.ts +++ b/web-app/src/app/app.module.ts @@ -51,7 +51,7 @@ import { HTTP_INTERCEPTORS } from '@angular/common/http'; import { DefaultInterceptor } from '@core'; import { SimpleInterceptor } from '@delon/auth'; const INTERCEPTOR_PROVIDES = [ - { provide: HTTP_INTERCEPTORS, useClass: SimpleInterceptor, multi: true}, + // { provide: HTTP_INTERCEPTORS, useClass: SimpleInterceptor, multi: true}, { provide: HTTP_INTERCEPTORS, useClass: DefaultInterceptor, multi: true} ]; // #endregion @@ -65,7 +65,7 @@ import { StartupService } from '@core'; export function StartupServiceFactory(startupService: StartupService): () => Observable { return () => startupService.load(); } -const APPINIT_PROVIDES = [ +const APP_INIT_PROVIDES = [ StartupService, { provide: APP_INITIALIZER, @@ -83,6 +83,7 @@ import { LayoutModule } from './layout/layout.module'; import { RoutesModule } from './routes/routes.module'; import { SharedModule } from './shared/shared.module'; import { STWidgetModule } from './shared/st-widget/st-widget.module'; +import { ReactiveFormsModule } from '@angular/forms'; @NgModule({ declarations: [ @@ -101,13 +102,14 @@ import { STWidgetModule } from './shared/st-widget/st-widget.module'; NzMessageModule, NzNotificationModule, ...FORM_MODULES, - ...GLOBAL_THIRD_MODULES + ...GLOBAL_THIRD_MODULES, + ReactiveFormsModule ], providers: [ ...LANG_PROVIDES, ...INTERCEPTOR_PROVIDES, ...I18NSERVICE_PROVIDES, - ...APPINIT_PROVIDES + ...APP_INIT_PROVIDES ], bootstrap: [AppComponent] }) diff --git a/web-app/src/app/core/i18n/i18n.service.ts b/web-app/src/app/core/i18n/i18n.service.ts index 06e29b7..827afc5 100644 --- a/web-app/src/app/core/i18n/i18n.service.ts +++ b/web-app/src/app/core/i18n/i18n.service.ts @@ -92,7 +92,7 @@ export class I18NService extends AlainI18nBaseService { } loadLangData(lang: string): Observable { - return this.http.get(`assets/tmp/i18n/${lang}.json`); + return this.http.get(`http://localhost:4200/assets/tmp/i18n/${lang}.json`); } use(lang: string, data: Record): void { diff --git a/web-app/src/app/core/index.ts b/web-app/src/app/core/index.ts index 8fdee38..907a1c2 100644 --- a/web-app/src/app/core/index.ts +++ b/web-app/src/app/core/index.ts @@ -1,4 +1,4 @@ export * from './i18n/i18n.service'; export * from './module-import-guard'; -export * from './net/default.interceptor'; +export * from './interceptor/default.interceptor'; export * from './startup/startup.service'; diff --git a/web-app/src/app/core/net/default.interceptor.ts b/web-app/src/app/core/interceptor/default.interceptor.ts similarity index 52% rename from web-app/src/app/core/net/default.interceptor.ts rename to web-app/src/app/core/interceptor/default.interceptor.ts index 362ab0b..a4fe3f7 100644 --- a/web-app/src/app/core/net/default.interceptor.ts +++ b/web-app/src/app/core/interceptor/default.interceptor.ts @@ -15,8 +15,9 @@ import { environment } from '@env/environment'; import { NzNotificationService } from 'ng-zorro-antd/notification'; import { BehaviorSubject, Observable, of, throwError } from 'rxjs'; import { catchError, filter, mergeMap, switchMap, take } from 'rxjs/operators'; +import {LocalStorageService} from "../../service/local-storage.service"; -const CODEMESSAGE: { [key: number]: string } = { +const CODE_MESSAGE: { [key: number]: string } = { 200: '服务器成功返回请求的数据。', 201: '新建或修改数据成功。', 202: '一个请求已经进入后台排队(异步任务)。', @@ -40,15 +41,10 @@ const CODEMESSAGE: { [key: number]: string } = { @Injectable() export class DefaultInterceptor implements HttpInterceptor { private refreshTokenEnabled = environment.api.refreshTokenEnabled; - private refreshTokenType: 're-request' | 'auth-refresh' = environment.api.refreshTokenType; private refreshToking = false; private refreshToken$: BehaviorSubject = new BehaviorSubject(null); - constructor(private injector: Injector) { - if (this.refreshTokenType === 'auth-refresh') { - this.buildAuthRefresh(); - } - } + constructor(private injector: Injector, private storageSvc: LocalStorageService) { } private get notification(): NzNotificationService { return this.injector.get(NzNotificationService); @@ -67,12 +63,11 @@ export class DefaultInterceptor implements HttpInterceptor { } private checkStatus(ev: HttpResponseBase): void { - if ((ev.status >= 200 && ev.status < 300) || ev.status === 401) { + if (ev.status >= 200 && ev.status < 500) { return; } - - const errortext = CODEMESSAGE[ev.status] || ev.statusText; - this.notification.error(`请求错误 ${ev.status}: ${ev.url}`, errortext); + const errorText = CODE_MESSAGE[ev.status] || ev.statusText; + this.notification.error(`抱歉服务器繁忙 ${ev.status}: ${ev.url}`, errorText); } /** @@ -109,6 +104,7 @@ export class DefaultInterceptor implements HttpInterceptor { this.refreshToking = false; this.refreshToken$.next(res); // 重新保存新 token + this.storageSvc.storageAuthorizationToken(res); this.tokenSrv.set(res); // 重新发起请求 return next.handle(this.reAttachToken(req)); @@ -124,138 +120,69 @@ export class DefaultInterceptor implements HttpInterceptor { /** * 重新附加新 Token 信息 * - * > 由于已经发起的请求,不会再走一遍 `@delon/auth` 因此需要结合业务情况重新附加新的 Token */ private reAttachToken(req: HttpRequest): HttpRequest { - // 以下示例是以 NG-ALAIN 默认使用 `SimpleInterceptor` - const token = this.tokenSrv.get()?.token; + let token = this.storageSvc.getAuthorizationToken(); return req.clone({ setHeaders: { - token: `Bearer ${token}` + 'Authorization': `Bearer ${token}` } }); } - // #endregion - - // #region 刷新Token方式二:使用 `@delon/auth` 的 `refresh` 接口 - - private buildAuthRefresh(): void { - if (!this.refreshTokenEnabled) { - return; - } - this.tokenSrv.refresh - .pipe( - filter(() => !this.refreshToking), - switchMap(res => { - console.log(res); - this.refreshToking = true; - return this.refreshTokenRequest(); - }) - ) - .subscribe( - res => { - // TODO: Mock expired value - res.expired = +new Date() + 1000 * 60 * 5; - this.refreshToking = false; - this.tokenSrv.set(res); - }, - () => this.toLogin() - ); - } - - // #endregion private toLogin(): void { this.notification.error(`未登录或登录已过期,请重新登录。`, ``); this.goTo(this.tokenSrv.login_url!); } - private handleData(ev: HttpResponseBase, req: HttpRequest, next: HttpHandler): Observable { - this.checkStatus(ev); - // 业务处理:一些通用操作 - switch (ev.status) { - case 200: - // 业务层级错误处理,以下是假定restful有一套统一输出格式(指不管成功与否都有相应的数据格式)情况下进行处理 - // 例如响应内容: - // 错误内容:{ status: 1, msg: '非法参数' } - // 正确内容:{ status: 0, response: { } } - // 则以下代码片断可直接适用 - // if (ev instanceof HttpResponse) { - // const body = ev.body; - // if (body && body.status !== 0) { - // this.injector.get(NzMessageService).error(body.msg); - // // 注意:这里如果继续抛出错误会被行254的 catchError 二次拦截,导致外部实现的 Pipe、subscribe 操作被中断,例如:this.http.get('/').subscribe() 不会触发 - // // 如果你希望外部实现,需要手动移除行254 - // return throwError({}); - // } else { - // // 忽略 Blob 文件体 - // if (ev.body instanceof Blob) { - // return of(ev); - // } - // // 重新修改 `body` 内容为 `response` 内容,对于绝大多数场景已经无须再关心业务状态码 - // return of(new HttpResponse(Object.assign(ev, { body: body.response }))); - // // 或者依然保持完整的格式 - // return of(ev); - // } - // } - break; - case 401: - if (this.refreshTokenEnabled && this.refreshTokenType === 're-request') { - return this.tryRefreshToken(ev, req, next); - } - this.toLogin(); - break; - case 403: - case 404: - case 500: - // this.goTo(`/exception/${ev.status}?url=${req.urlWithParams}`); - break; - default: - if (ev instanceof HttpErrorResponse) { - console.warn( - '未可知错误,大部分是由于后端不支持跨域CORS或无效配置引起,请参考 https://ng-alain.com/docs/server 解决跨域问题', - ev - ); - } - break; - } - if (ev instanceof HttpErrorResponse) { - return throwError(ev); - } else { - return of(ev); - } - } - - private getAdditionalHeaders(headers?: HttpHeaders): { [name: string]: string } { + private fillHeaders(headers?: HttpHeaders): { [name: string]: string } { const res: { [name: string]: string } = {}; const lang = this.injector.get(ALAIN_I18N_TOKEN).currentLang; if (!headers?.has('Accept-Language') && lang) { res['Accept-Language'] = lang; } - + let token = this.storageSvc.getAuthorizationToken(); + if (token !== null) { + res['Authorization'] = `Bearer ${token}`; + } return res; } intercept(req: HttpRequest, next: HttpHandler): Observable> { - // 统一加上服务端前缀 let url = req.url; if (!url.startsWith('https://') && !url.startsWith('http://')) { const { baseUrl } = environment.api; url = baseUrl + (baseUrl.endsWith('/') && url.startsWith('/') ? url.substring(1) : url); } - - const newReq = req.clone({ url, setHeaders: this.getAdditionalHeaders(req.headers) }); + const newReq = req.clone({ url, setHeaders: this.fillHeaders(req.headers) }); return next.handle(newReq).pipe( - mergeMap(ev => { - // 允许统一对请求错误处理 - if (ev instanceof HttpResponseBase) { - return this.handleData(ev, newReq, next); + mergeMap(httpEvent => { + if (httpEvent instanceof HttpResponseBase) { + // 处理token过期自动刷新 + switch (httpEvent.status) { + case 401: + if (this.refreshTokenEnabled) { + return this.tryRefreshToken(httpEvent, req, next); + } + this.toLogin(); + break; + case 403 | 404 | 500: + this.goTo(`/exception/${httpEvent.status}?url=${req.urlWithParams}`); + break; + default: + break; + } + return of(httpEvent); + } else { + return of(httpEvent); } - // 若一切都正常,则后续操作 - return of(ev); }), - catchError((err: HttpErrorResponse) => this.handleData(err, newReq, next)) + catchError((err: HttpErrorResponse) => { + this.checkStatus(err); + console.warn(`${err.status} == ${err.message}`) + return throwError(err); + }) ); } } diff --git a/web-app/src/app/core/startup/startup.service.ts b/web-app/src/app/core/startup/startup.service.ts index c9ace6a..bc9871d 100644 --- a/web-app/src/app/core/startup/startup.service.ts +++ b/web-app/src/app/core/startup/startup.service.ts @@ -12,7 +12,6 @@ import { NzIconService } from 'ng-zorro-antd/icon'; import { ICONS } from '../../../style-icons'; import { ICONS_AUTO } from '../../../style-icons-auto'; - /** * Used for application startup * Generally used to get the basic data of the application, like: Menu Data, User Data, etc. @@ -36,7 +35,7 @@ export class StartupService { private viaHttp(): Observable { const defaultLang = this.i18n.defaultLang; - return zip(this.i18n.loadLangData(defaultLang), this.httpClient.get('assets/tmp/app-data.json')).pipe( + return zip(this.i18n.loadLangData(defaultLang), this.httpClient.get('http://localhost:4200/assets/tmp/app-data.json')).pipe( catchError((res: NzSafeAny) => { console.warn(`StartupService.load: Network request failed`, res); setTimeout(() => this.router.navigateByUrl(`/exception/500`)); diff --git a/web-app/src/app/pojo/Message.ts b/web-app/src/app/pojo/Message.ts new file mode 100644 index 0000000..0e42366 --- /dev/null +++ b/web-app/src/app/pojo/Message.ts @@ -0,0 +1,5 @@ +export class Message { + data: any; + msg!: string; + code: number = 0; +} diff --git a/web-app/src/app/pojo/Monitor.ts b/web-app/src/app/pojo/Monitor.ts new file mode 100644 index 0000000..d58c767 --- /dev/null +++ b/web-app/src/app/pojo/Monitor.ts @@ -0,0 +1,13 @@ +export class Monitor { + id!: number; + name!: string; + app!: string; + host!: string; + intervals!: number; + status!: number; + description!: string; + creator!: string; + modifier!: string; + gmtCreate!: number; + gmtUpdate!: number; +} diff --git a/web-app/src/app/pojo/Param.ts b/web-app/src/app/pojo/Param.ts new file mode 100644 index 0000000..91ca677 --- /dev/null +++ b/web-app/src/app/pojo/Param.ts @@ -0,0 +1,6 @@ +export class Param { + id!: number; + field: string | undefined; + type: number | undefined; + value: string | undefined; +} diff --git a/web-app/src/app/pojo/ParamDefine.ts b/web-app/src/app/pojo/ParamDefine.ts new file mode 100644 index 0000000..fa89cb2 --- /dev/null +++ b/web-app/src/app/pojo/ParamDefine.ts @@ -0,0 +1,9 @@ +export class ParamDefine { + name!: string; + field!: string; + type!: string; + required: boolean | undefined; + range: string | undefined; + limit: number | undefined; + option: string | undefined; +} diff --git a/web-app/src/app/routes/monitor/monitor-detail/monitor-detail.component.html b/web-app/src/app/routes/monitor/monitor-detail/monitor-detail.component.html new file mode 100644 index 0000000..5303fb7 --- /dev/null +++ b/web-app/src/app/routes/monitor/monitor-detail/monitor-detail.component.html @@ -0,0 +1 @@ +

monitor-detail works!

diff --git a/web-app/src/app/routes/monitor/monitor-detail/monitor-detail.component.spec.ts b/web-app/src/app/routes/monitor/monitor-detail/monitor-detail.component.spec.ts new file mode 100644 index 0000000..cdaacfa --- /dev/null +++ b/web-app/src/app/routes/monitor/monitor-detail/monitor-detail.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MonitorDetailComponent } from './monitor-detail.component'; + +describe('MonitorDetailComponent', () => { + let component: MonitorDetailComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ MonitorDetailComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(MonitorDetailComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/web-app/src/app/routes/monitor/monitor-detail/monitor-detail.component.ts b/web-app/src/app/routes/monitor/monitor-detail/monitor-detail.component.ts new file mode 100644 index 0000000..c54cc29 --- /dev/null +++ b/web-app/src/app/routes/monitor/monitor-detail/monitor-detail.component.ts @@ -0,0 +1,16 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-monitor-detail', + templateUrl: './monitor-detail.component.html', + styles: [ + ] +}) +export class MonitorDetailComponent implements OnInit { + + constructor() { } + + ngOnInit(): void { + } + +} diff --git a/web-app/src/app/routes/monitor/monitor-edit/monitor-edit.component.html b/web-app/src/app/routes/monitor/monitor-edit/monitor-edit.component.html new file mode 100644 index 0000000..419e5e1 --- /dev/null +++ b/web-app/src/app/routes/monitor/monitor-edit/monitor-edit.component.html @@ -0,0 +1 @@ +

monitor-modify works!

diff --git a/web-app/src/app/routes/monitor/monitor-edit/monitor-edit.component.spec.ts b/web-app/src/app/routes/monitor/monitor-edit/monitor-edit.component.spec.ts new file mode 100644 index 0000000..624afe6 --- /dev/null +++ b/web-app/src/app/routes/monitor/monitor-edit/monitor-edit.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MonitorEditComponent } from './monitor-edit.component'; + +describe('MonitorModifyComponent', () => { + let component: MonitorEditComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ MonitorEditComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(MonitorEditComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/web-app/src/app/routes/monitor/monitor-edit/monitor-edit.component.ts b/web-app/src/app/routes/monitor/monitor-edit/monitor-edit.component.ts new file mode 100644 index 0000000..0e2151c --- /dev/null +++ b/web-app/src/app/routes/monitor/monitor-edit/monitor-edit.component.ts @@ -0,0 +1,16 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-monitor-modify', + templateUrl: './monitor-edit.component.html', + styles: [ + ] +}) +export class MonitorEditComponent implements OnInit { + + constructor() { } + + ngOnInit(): void { + } + +} diff --git a/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.html b/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.html new file mode 100644 index 0000000..b077d09 --- /dev/null +++ b/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.html @@ -0,0 +1,2 @@ + +

monitor-list works!

diff --git a/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.spec.ts b/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.spec.ts new file mode 100644 index 0000000..19e6e4d --- /dev/null +++ b/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MonitorListComponent } from './monitor-list.component'; + +describe('MonitorListComponent', () => { + let component: MonitorListComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ MonitorListComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(MonitorListComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.ts b/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.ts new file mode 100644 index 0000000..94c3810 --- /dev/null +++ b/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.ts @@ -0,0 +1,16 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-monitor-list', + templateUrl: './monitor-list.component.html', + styles: [ + ] +}) +export class MonitorListComponent implements OnInit { + + constructor() { } + + ngOnInit(): void { + } + +} diff --git a/web-app/src/app/routes/monitor/monitor-new/monitor-new.component.html b/web-app/src/app/routes/monitor/monitor-new/monitor-new.component.html new file mode 100644 index 0000000..2c613ad --- /dev/null +++ b/web-app/src/app/routes/monitor/monitor-new/monitor-new.component.html @@ -0,0 +1,136 @@ + + + + + + + + + + + 监控列表 + + + + + 新增 {{monitor.app}} 监控 + + + + +
+
+ + 监控Host + + + + + + 监控名称 + + + + + + + + + {{paramDefine.name}} + + + + + + {{paramDefine.name}} + + + + + + + + + + + + {{paramDefine.name}} + + + + + + {{paramDefine.name}} + + + + + + + + + + + 采集间隔 + + + + + + + 启动探测 + + + + + + + 描述备注 + + + + + + + +
+
+ + + +
+
+
+
diff --git a/web-app/src/app/routes/monitor/monitor-new/monitor-new.component.spec.ts b/web-app/src/app/routes/monitor/monitor-new/monitor-new.component.spec.ts new file mode 100644 index 0000000..047d38c --- /dev/null +++ b/web-app/src/app/routes/monitor/monitor-new/monitor-new.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MonitorNewComponent } from './monitor-new.component'; + +describe('MonitorAddComponent', () => { + let component: MonitorNewComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ MonitorNewComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(MonitorNewComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/web-app/src/app/routes/monitor/monitor-new/monitor-new.component.ts b/web-app/src/app/routes/monitor/monitor-new/monitor-new.component.ts new file mode 100644 index 0000000..69b80a4 --- /dev/null +++ b/web-app/src/app/routes/monitor/monitor-new/monitor-new.component.ts @@ -0,0 +1,77 @@ +import { Component, OnInit } from '@angular/core'; +import {ParamDefine} from "../../../pojo/ParamDefine"; +import {AppDefineService} from "../../../service/app-define.service"; +import {ActivatedRoute, ParamMap, Router} from "@angular/router"; +import {switchMap} from "rxjs/operators"; +import {FormBuilder, FormControl, FormGroup} from "@angular/forms"; +import {I18NService} from "@core"; +import {Param} from "../../../pojo/Param"; +import {Monitor} from "../../../pojo/Monitor"; +import {MonitorService} from "../../../service/monitor.service"; +import {NzNotificationService} from "ng-zorro-antd/notification"; + +@Component({ + selector: 'app-monitor-add', + templateUrl: './monitor-new.component.html', + styles: [ + ] +}) +export class MonitorNewComponent implements OnInit { + + paramDefines!: ParamDefine[]; + params!: Param[]; + monitor!: Monitor; + profileForm: FormGroup = new FormGroup({}); + detected: boolean = true; + passwordVisible!: boolean; + constructor(private appDefineSvc: AppDefineService, + private monitorSvc: MonitorService, + private route: ActivatedRoute, + private router: Router, + private notifySvc: NzNotificationService, + private i18n: I18NService, + private formBuilder: FormBuilder) { + this.monitor = new Monitor(); + } + + ngOnInit(): void { + const paramDefine$ = this.route.queryParamMap.pipe( + switchMap((paramMap: ParamMap) => { + this.monitor.app = paramMap.get("app") || ''; + return this.appDefineSvc.getAppParamsDefine(this.monitor.app); + }) + ).subscribe(message => { + if (message.code === 0) { + this.paramDefines = message.data; + this.params = []; + this.paramDefines.forEach(define => { + let param = new Param(); + param.field = define.field; + param.type = define.type === "number" ? 0 : 1; + this.params.push(param); + }) + } else { + console.warn(message.msg); + } + paramDefine$.unsubscribe(); + }); + } + + onSubmit() { + let addMonitor = { + "detected": this.detected, + "monitor": this.monitor, + "params": this.params + }; + this.monitorSvc.newMonitor(addMonitor) + .subscribe(message => { + if (message.code === 0) { + this.notifySvc.success("新增监控成功", ""); + this.router.navigateByUrl("/monitors") + } else { + this.notifySvc.error("新增监控失败", message.msg); + } + }) + } + +} diff --git a/web-app/src/app/routes/monitor/monitor-routing.module.ts b/web-app/src/app/routes/monitor/monitor-routing.module.ts index e133dc5..9fb67f9 100644 --- a/web-app/src/app/routes/monitor/monitor-routing.module.ts +++ b/web-app/src/app/routes/monitor/monitor-routing.module.ts @@ -1,7 +1,16 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; +import {MonitorListComponent} from "./monitor-list/monitor-list.component"; +import {MonitorNewComponent} from "./monitor-new/monitor-new.component"; +import {MonitorEditComponent} from "./monitor-edit/monitor-edit.component"; +import {MonitorDetailComponent} from "./monitor-detail/monitor-detail.component"; const routes: Routes = [ + { path: '', component: MonitorNewComponent }, + { path: 'new', component: MonitorNewComponent }, + { path: ':monitorId/edit', component: MonitorEditComponent }, + { path: ':monitorId', component: MonitorDetailComponent }, + { path: '**', component: MonitorListComponent } ]; @NgModule({ diff --git a/web-app/src/app/routes/monitor/monitor.module.ts b/web-app/src/app/routes/monitor/monitor.module.ts index cfa8b14..9820d79 100644 --- a/web-app/src/app/routes/monitor/monitor.module.ts +++ b/web-app/src/app/routes/monitor/monitor.module.ts @@ -1,14 +1,29 @@ import { NgModule, Type } from '@angular/core'; import { SharedModule } from '@shared'; import { MonitorRoutingModule } from './monitor-routing.module'; +import {MonitorNewComponent} from "./monitor-new/monitor-new.component"; +import {MonitorEditComponent} from "./monitor-edit/monitor-edit.component"; +import {MonitorListComponent} from "./monitor-list/monitor-list.component"; +import {MonitorDetailComponent} from "./monitor-detail/monitor-detail.component"; +import {NzBreadCrumbModule} from "ng-zorro-antd/breadcrumb"; +import {NzDividerModule} from "ng-zorro-antd/divider"; +import {NzSwitchModule} from "ng-zorro-antd/switch"; -const COMPONENTS: Type[] = []; +const COMPONENTS: Type[] = [ + MonitorNewComponent, + MonitorEditComponent, + MonitorListComponent, + MonitorDetailComponent +]; @NgModule({ - imports: [ - SharedModule, - MonitorRoutingModule - ], + imports: [ + SharedModule, + MonitorRoutingModule, + NzBreadCrumbModule, + NzDividerModule, + NzSwitchModule + ], declarations: COMPONENTS, }) export class MonitorModule { } diff --git a/web-app/src/app/routes/routes-routing.module.ts b/web-app/src/app/routes/routes-routing.module.ts index f204262..1a305c7 100644 --- a/web-app/src/app/routes/routes-routing.module.ts +++ b/web-app/src/app/routes/routes-routing.module.ts @@ -21,12 +21,11 @@ const routes: Routes = [ component: LayoutBasicComponent, canActivate: [SimpleGuard], children: [ - { path: '', redirectTo: 'dashboard', pathMatch: 'full' }, + // todo 根据路由自动生成面包屑 + { path: '', redirectTo: 'dashboard', pathMatch: 'full'}, { path: 'dashboard', component: DashboardComponent, data: { title: '仪表盘' } }, { path: 'exception', loadChildren: () => import('./exception/exception.module').then(m => m.ExceptionModule) }, - // 业务子模块 - // { path: 'widgets', loadChildren: () => import('./widgets/widgets.module').then(m => m.WidgetsModule) }, - { path: 'monitor', loadChildren: () => import('./monitor/monitor.module').then((m) => m.MonitorModule) },] + { path: 'monitors', loadChildren: () => import('./monitor/monitor.module').then((m) => m.MonitorModule) },] }, // 空白布局 // { diff --git a/web-app/src/app/service/app-define.service.spec.ts b/web-app/src/app/service/app-define.service.spec.ts new file mode 100644 index 0000000..78fa5c8 --- /dev/null +++ b/web-app/src/app/service/app-define.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { AppDefineService } from './app-define.service'; + +describe('AppDefineService', () => { + let service: AppDefineService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(AppDefineService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/web-app/src/app/service/app-define.service.ts b/web-app/src/app/service/app-define.service.ts new file mode 100644 index 0000000..0d229ba --- /dev/null +++ b/web-app/src/app/service/app-define.service.ts @@ -0,0 +1,22 @@ +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import {Message} from "../pojo/Message"; +import {Observable} from "rxjs"; + + +@Injectable({ + providedIn: 'root' +}) +export class AppDefineService { + + constructor(private http : HttpClient) { } + + public getAppParamsDefine(app: string | undefined | null) : Observable { + if (app === null || app === undefined) { + console.log("getAppParamsDefine app can not null"); + } + const paramDefineUri = `/apps/${app}/params`; + return this.http.get(paramDefineUri); + } + +} diff --git a/web-app/src/app/service/local-storage.service.spec.ts b/web-app/src/app/service/local-storage.service.spec.ts new file mode 100644 index 0000000..ba1dbd4 --- /dev/null +++ b/web-app/src/app/service/local-storage.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { LocalStorageService } from './local-storage.service'; + +describe('LocalStorageService', () => { + let service: LocalStorageService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(LocalStorageService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/web-app/src/app/service/local-storage.service.ts b/web-app/src/app/service/local-storage.service.ts new file mode 100644 index 0000000..3f49e05 --- /dev/null +++ b/web-app/src/app/service/local-storage.service.ts @@ -0,0 +1,29 @@ +import { Injectable } from '@angular/core'; + +const Authorization = 'Authorization'; + +@Injectable({ + providedIn: 'root' +}) +export class LocalStorageService { + + constructor() { } + + public putData(key: string, value: string) { + localStorage.setItem(key, value); + } + + public getData(key: string): string | null { + const data = localStorage.getItem(key); + return data === null ? null : data; + } + + public getAuthorizationToken(): string | null { + return this.getData(Authorization); + } + + public storageAuthorizationToken(token: string) { + return this.putData(Authorization, token); + } + +} diff --git a/web-app/src/app/service/monitor.service.spec.ts b/web-app/src/app/service/monitor.service.spec.ts new file mode 100644 index 0000000..19ed457 --- /dev/null +++ b/web-app/src/app/service/monitor.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { MonitorService } from './monitor.service'; + +describe('MonitorService', () => { + let service: MonitorService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(MonitorService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/web-app/src/app/service/monitor.service.ts b/web-app/src/app/service/monitor.service.ts new file mode 100644 index 0000000..2d75a54 --- /dev/null +++ b/web-app/src/app/service/monitor.service.ts @@ -0,0 +1,19 @@ +import { Injectable } from '@angular/core'; +import {HttpClient} from "@angular/common/http"; +import {Observable} from "rxjs"; +import {Message} from "../pojo/Message"; + +const monitor_uri = "/monitor"; + +@Injectable({ + providedIn: 'root' +}) +export class MonitorService { + + constructor(private http : HttpClient) { } + + public newMonitor(body: any) : Observable { + return this.http.post(monitor_uri, body); + } + +} diff --git a/web-app/src/app/service/my-service.service.spec.ts b/web-app/src/app/service/my-service.service.spec.ts new file mode 100644 index 0000000..05fd85b --- /dev/null +++ b/web-app/src/app/service/my-service.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { MyServiceService } from './my-service.service'; + +describe('MyServiceService', () => { + let service: MyServiceService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(MyServiceService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/web-app/src/app/service/my-service.service.ts b/web-app/src/app/service/my-service.service.ts new file mode 100644 index 0000000..683e3b3 --- /dev/null +++ b/web-app/src/app/service/my-service.service.ts @@ -0,0 +1,9 @@ +import { Injectable } from '@angular/core'; + +@Injectable({ + providedIn: 'root' +}) +export class MyServiceService { + + constructor() { } +} diff --git a/web-app/src/assets/tmp/app-data.json b/web-app/src/assets/tmp/app-data.json index cc3f325..b66ef59 100644 --- a/web-app/src/assets/tmp/app-data.json +++ b/web-app/src/assets/tmp/app-data.json @@ -19,7 +19,7 @@ "text": "仪表盘", "i18n": "menu.dashboard", "icon": "anticon-dashboard", - "link": "/dashboard/v1" + "link": "/dashboard" } ] }, @@ -35,18 +35,18 @@ "icon": "anticon-cloud", "children": [ { - "text": "仪表盘V1", - "link": "/dashboard/v1", + "text": "http", + "link": "/monitors", "i18n": "monitor.app.http" }, { - "text": "分析页", - "link": "/dashboard/analysis", + "text": "ping", + "link": "/monitors", "i18n": "monitor.app.ping" }, { - "text": "监控页", - "link": "/dashboard/monitor", + "text": "telnet", + "link": "/monitors", "i18n": "monitor.app.telnet" } ] @@ -58,17 +58,17 @@ "children": [ { "text": "Mysql", - "link": "/dashboard/v1", + "link": "/monitors", "i18n": "monitor.app.mysql" }, { "text": "Oracle", - "link": "/dashboard/analysis", + "link": "/monitors", "i18n": "monitor.app.oracle" }, { "text": "Redis", - "link": "/dashboard/monitor", + "link": "/monitors", "i18n": "monitor.app.redis" } ] diff --git a/web-app/src/environments/environment.prod.ts b/web-app/src/environments/environment.prod.ts index 327f2f0..ef7e136 100644 --- a/web-app/src/environments/environment.prod.ts +++ b/web-app/src/environments/environment.prod.ts @@ -5,7 +5,6 @@ export const environment = { useHash: true, api: { baseUrl: './', - refreshTokenEnabled: true, - refreshTokenType: 'auth-refresh' + refreshTokenEnabled: true } } as Environment; diff --git a/web-app/src/environments/environment.ts b/web-app/src/environments/environment.ts index 494c594..c3b74a9 100644 --- a/web-app/src/environments/environment.ts +++ b/web-app/src/environments/environment.ts @@ -5,17 +5,16 @@ import { DelonMockModule } from '@delon/mock'; import { Environment } from '@delon/theme'; -import * as MOCKDATA from '../../_mock'; +import * as MOCK_DATA from '../../_mock'; export const environment = { production: false, useHash: true, api: { - baseUrl: './', - refreshTokenEnabled: true, - refreshTokenType: 'auth-refresh' + baseUrl: 'http://localhost:8080/', + refreshTokenEnabled: true }, - modules: [DelonMockModule.forRoot({ data: MOCKDATA })] + modules: [DelonMockModule.forRoot({ data: MOCK_DATA })] } as Environment; /*