From 7e0c22e1acd2f9cd438ec7b8379656a51350ae05 Mon Sep 17 00:00:00 2001 From: AlexBob <5199840@qq.com> Date: Mon, 8 Jul 2024 16:07:29 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96ng-ui=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE=EF=BC=8C=E6=B7=BB=E5=8A=A0=E5=9F=BA=E7=A1=80=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E5=92=8C=E5=88=9D=E5=A7=8B=E4=BB=A3=E7=A0=81=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 本次提交包括以下内容: - 创建ng-ui项目目录和初始文件结构。 - 配置.vscode目录下的settings.json、extensions.json、launch.json和tasks.json文件,以优化开发环境和体验。 - 添加docker相关配置,包括Dockerfile和web.json文件,用于构建和配置nginx服务。 - 在projects/commons目录下初始化一个Angular库,并配置相应的tsconfig文件、ng-package.json和package.json文件。- 添加BrowserStorage.service.ts和BrowserStorageServer.service.ts文件,提供浏览器存储服务的实现。 - 创建commons.component.ts、commons.component.spec.ts和commons.service.ts文件,作为库的 --- ng-ui/angular.json | 3 + ng-ui/projects/web/src/app/app.component.html | 2 +- ng-ui/projects/web/src/app/app.component.scss | 6 + .../web/src/app/app.component.spec.ts | 29 ---- ng-ui/projects/web/src/app/app.component.ts | 28 +--- ng-ui/projects/web/src/app/app.config.ts | 34 +++- ng-ui/projects/web/src/app/app.routes.ts | 4 + .../src/app/home/groups/groups.component.css | 3 + .../src/app/home/groups/groups.component.html | 1 + .../src/app/home/groups/groups.component.ts | 9 + .../web/src/app/home/home-routing.module.ts | 11 +- .../projects/web/src/app/home/home.module.ts | 2 - .../src/app/home/index/index.component.html | 49 +++++- .../src/app/home/index/index.component.scss | 45 +++++ .../app/home/index/index.component.spec.ts | 23 --- .../web/src/app/home/index/index.component.ts | 86 +++++++++- .../src/app/home/menus/menu-form.component.ts | 155 ++++++++++++++++++ .../web/src/app/home/menus/menu.types.ts | 51 ++++++ .../src/app/home/menus/menus.component.html | 45 +++++ .../src/app/home/menus/menus.component.scss | 9 + .../web/src/app/home/menus/menus.component.ts | 93 +++++++++++ .../web/src/app/home/menus/menus.service.ts | 46 ++++++ .../src/app/home/users/users.component.css | 3 + .../web/src/app/home/users/users.component.ts | 9 + ng-ui/projects/web/src/core/auth.service.ts | 11 +- .../projects/web/src/core/loading.service.ts | 26 ++- ng-ui/projects/web/src/styles.scss | 5 + 27 files changed, 687 insertions(+), 101 deletions(-) delete mode 100644 ng-ui/projects/web/src/app/app.component.spec.ts create mode 100644 ng-ui/projects/web/src/app/home/groups/groups.component.css create mode 100644 ng-ui/projects/web/src/app/home/groups/groups.component.html create mode 100644 ng-ui/projects/web/src/app/home/groups/groups.component.ts delete mode 100644 ng-ui/projects/web/src/app/home/index/index.component.spec.ts create mode 100644 ng-ui/projects/web/src/app/home/menus/menu-form.component.ts create mode 100644 ng-ui/projects/web/src/app/home/menus/menu.types.ts create mode 100644 ng-ui/projects/web/src/app/home/menus/menus.component.html create mode 100644 ng-ui/projects/web/src/app/home/menus/menus.component.scss create mode 100644 ng-ui/projects/web/src/app/home/menus/menus.component.ts create mode 100644 ng-ui/projects/web/src/app/home/menus/menus.service.ts create mode 100644 ng-ui/projects/web/src/app/home/users/users.component.css create mode 100644 ng-ui/projects/web/src/app/home/users/users.component.ts diff --git a/ng-ui/angular.json b/ng-ui/angular.json index 210b74cb..31b930be 100644 --- a/ng-ui/angular.json +++ b/ng-ui/angular.json @@ -37,6 +37,9 @@ "styles": [ "node_modules/bootstrap/dist/css/bootstrap.css", "node_modules/bootstrap-icons/font/bootstrap-icons.css", + "node_modules/ng-zorro-antd/ng-zorro-antd.css", + "node_modules/ng-zorro-antd/ng-zorro-antd.dark.css", + "node_modules/ng-zorro-antd/ng-zorro-antd.compact.css", "projects/web/src/styles.scss" ], "scripts": ["node_modules/bootstrap/dist/js/bootstrap.bundle.js"], diff --git a/ng-ui/projects/web/src/app/app.component.html b/ng-ui/projects/web/src/app/app.component.html index 50322faa..7b7be9bc 100644 --- a/ng-ui/projects/web/src/app/app.component.html +++ b/ng-ui/projects/web/src/app/app.component.html @@ -1,4 +1,4 @@ -
+
diff --git a/ng-ui/projects/web/src/app/app.component.scss b/ng-ui/projects/web/src/app/app.component.scss index 46e6e8b1..d4900b62 100644 --- a/ng-ui/projects/web/src/app/app.component.scss +++ b/ng-ui/projects/web/src/app/app.component.scss @@ -2,3 +2,9 @@ min-height: 100%; min-width: 100%; } + +.layout { + min-height: 100%; + min-width: 100%; + display: block; +} diff --git a/ng-ui/projects/web/src/app/app.component.spec.ts b/ng-ui/projects/web/src/app/app.component.spec.ts deleted file mode 100644 index 21fd31a1..00000000 --- a/ng-ui/projects/web/src/app/app.component.spec.ts +++ /dev/null @@ -1,29 +0,0 @@ -import {TestBed} from '@angular/core/testing'; -import {AppComponent} from './app.component'; - -describe('AppComponent', () => { - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [AppComponent], - }).compileComponents(); - }); - - it('should create the app', () => { - const fixture = TestBed.createComponent(AppComponent); - const app = fixture.componentInstance; - expect(app).toBeTruthy(); - }); - - it(`should have the 'web' title`, () => { - const fixture = TestBed.createComponent(AppComponent); - const app = fixture.componentInstance; - expect(app.title).toEqual('web'); - }); - - it('should render title', () => { - const fixture = TestBed.createComponent(AppComponent); - fixture.detectChanges(); - const compiled = fixture.nativeElement as HTMLElement; - expect(compiled.querySelector('h1')?.textContent).toContain('Hello, web'); - }); -}); diff --git a/ng-ui/projects/web/src/app/app.component.ts b/ng-ui/projects/web/src/app/app.component.ts index 3bfc252b..4e0d7619 100644 --- a/ng-ui/projects/web/src/app/app.component.ts +++ b/ng-ui/projects/web/src/app/app.component.ts @@ -1,12 +1,11 @@ -import {AsyncPipe} from '@angular/common'; -import {Component} from '@angular/core'; -import {RouterOutlet} from '@angular/router'; -import {toSignal} from '@angular/core/rxjs-interop'; +import { AsyncPipe } from '@angular/common'; +import { Component } from '@angular/core'; +import { toSignal } from '@angular/core/rxjs-interop'; +import { RouterOutlet } from '@angular/router'; -import {NzBackTopModule} from 'ng-zorro-antd/back-top'; -import {NzSpinModule} from 'ng-zorro-antd/spin'; -import {debounceTime, distinctUntilChanged, tap} from 'rxjs'; -import {LoadingService} from '../core/loading.service'; +import { NzBackTopModule } from 'ng-zorro-antd/back-top'; +import { NzSpinModule } from 'ng-zorro-antd/spin'; +import { LoadingService } from '../core/loading.service'; @Component({ selector: 'app-root', @@ -16,18 +15,7 @@ import {LoadingService} from '../core/loading.service'; styleUrl: './app.component.scss', }) export class AppComponent { - loadingShow = toSignal( - this.loading.progress$.pipe( - debounceTime(500), - distinctUntilChanged(), - tap(res => console.log(`Loading show is: ${res}`)) - ), - { initialValue: false } - ); + loadingShow = toSignal(this.loading.progress$, { initialValue: true }); constructor(private loading: LoadingService) {} - - ngOnInit(): void { - this.loading.show(); - } } diff --git a/ng-ui/projects/web/src/app/app.config.ts b/ng-ui/projects/web/src/app/app.config.ts index 7d17e3e4..7fc76c3c 100644 --- a/ng-ui/projects/web/src/app/app.config.ts +++ b/ng-ui/projects/web/src/app/app.config.ts @@ -1,5 +1,11 @@ -import { ApplicationConfig, importProvidersFrom, provideExperimentalZonelessChangeDetection } from '@angular/core'; -import { provideRouter, TitleStrategy } from '@angular/router'; +import { + ApplicationConfig, + importProvidersFrom, + inject, + LOCALE_ID, + provideExperimentalZonelessChangeDetection, +} from '@angular/core'; +import { provideRouter, TitleStrategy, withComponentInputBinding } from '@angular/router'; import { routes } from './app.routes'; import { NzConfig, provideNzConfig } from 'ng-zorro-antd/core/config'; @@ -15,6 +21,7 @@ import { } from '@angular/common/http'; import { authTokenInterceptor, defaultInterceptor } from '../core/http.Interceptor'; import { BrowserStorageServerService, BrowserStorageService } from 'plate-commons'; +import { en_US, NZ_I18N, zh_CN } from 'ng-zorro-antd/i18n'; export const ngZorroConfig: NzConfig = { message: { @@ -23,7 +30,7 @@ export const ngZorroConfig: NzConfig = { nzAnimate: true, nzPauseOnHover: true, }, - notification: {nzTop: 240}, + notification: { nzTop: 240 }, }; export const appConfig: ApplicationConfig = { @@ -40,8 +47,23 @@ export const appConfig: ApplicationConfig = { headerName: 'X-XSRF-TOKEN', }) ), - {provide: TitleStrategy, useClass: PageTitleStrategy}, - {provide: BrowserStorageService, useClass: BrowserStorageServerService}, - provideExperimentalZonelessChangeDetection(), provideRouter(routes) + { provide: TitleStrategy, useClass: PageTitleStrategy }, + { provide: BrowserStorageService, useClass: BrowserStorageServerService }, + { + provide: NZ_I18N, + useFactory: () => { + const localId = inject(LOCALE_ID); + switch (localId) { + case 'en': + return en_US; + case 'zh': + return zh_CN; + default: + return zh_CN; + } + }, + }, + provideExperimentalZonelessChangeDetection(), + provideRouter(routes, withComponentInputBinding()), ], }; diff --git a/ng-ui/projects/web/src/app/app.routes.ts b/ng-ui/projects/web/src/app/app.routes.ts index b37fa824..e102a346 100644 --- a/ng-ui/projects/web/src/app/app.routes.ts +++ b/ng-ui/projects/web/src/app/app.routes.ts @@ -6,6 +6,10 @@ export const routes: Routes = [ path: 'auth', loadChildren: () => import('../core/security.module').then(m => m.SecurityModule), }, + { + path: 'home', + loadChildren: () => import('./home/home.module').then(m => m.HomeModule), + }, { path: '', pathMatch: 'full', redirectTo: 'auth' }, { path: '**', component: NotFoundComponent }, ]; diff --git a/ng-ui/projects/web/src/app/home/groups/groups.component.css b/ng-ui/projects/web/src/app/home/groups/groups.component.css new file mode 100644 index 00000000..5d4e87f3 --- /dev/null +++ b/ng-ui/projects/web/src/app/home/groups/groups.component.css @@ -0,0 +1,3 @@ +:host { + display: block; +} diff --git a/ng-ui/projects/web/src/app/home/groups/groups.component.html b/ng-ui/projects/web/src/app/home/groups/groups.component.html new file mode 100644 index 00000000..41ad0f66 --- /dev/null +++ b/ng-ui/projects/web/src/app/home/groups/groups.component.html @@ -0,0 +1 @@ +

groups works!

diff --git a/ng-ui/projects/web/src/app/home/groups/groups.component.ts b/ng-ui/projects/web/src/app/home/groups/groups.component.ts new file mode 100644 index 00000000..0da62242 --- /dev/null +++ b/ng-ui/projects/web/src/app/home/groups/groups.component.ts @@ -0,0 +1,9 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-groups', + templateUrl: './groups.component.html', + styleUrl: './groups.component.css', +}) +export class GroupsComponent { +} diff --git a/ng-ui/projects/web/src/app/home/home-routing.module.ts b/ng-ui/projects/web/src/app/home/home-routing.module.ts index be3c41e8..ca134617 100644 --- a/ng-ui/projects/web/src/app/home/home-routing.module.ts +++ b/ng-ui/projects/web/src/app/home/home-routing.module.ts @@ -1,10 +1,15 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; +import { IndexComponent } from './index/index.component'; -const routes: Routes = []; +const routes: Routes = [ + { path: 'index', component: IndexComponent, title: '首页' }, + { path: '', pathMatch: 'full', redirectTo: 'index' }, + { path: '**', component: IndexComponent }, +]; @NgModule({ imports: [RouterModule.forChild(routes)], - exports: [RouterModule] + exports: [RouterModule], }) -export class HomeRoutingModule { } +export class HomeRoutingModule {} diff --git a/ng-ui/projects/web/src/app/home/home.module.ts b/ng-ui/projects/web/src/app/home/home.module.ts index 05129f9b..8fc64b41 100644 --- a/ng-ui/projects/web/src/app/home/home.module.ts +++ b/ng-ui/projects/web/src/app/home/home.module.ts @@ -1,5 +1,4 @@ import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; import { HomeRoutingModule } from './home-routing.module'; @@ -7,7 +6,6 @@ import { HomeRoutingModule } from './home-routing.module'; @NgModule({ declarations: [], imports: [ - CommonModule, HomeRoutingModule ] }) diff --git a/ng-ui/projects/web/src/app/home/index/index.component.html b/ng-ui/projects/web/src/app/home/index/index.component.html index f1b86081..2a3e3dd9 100644 --- a/ng-ui/projects/web/src/app/home/index/index.component.html +++ b/ng-ui/projects/web/src/app/home/index/index.component.html @@ -1 +1,48 @@ -

index works!

+ + + + + + + + + + + + @for (breadcrumb of breadcrumbs; track breadcrumb) { + {{ breadcrumb.label }} + } + + +
+ +
+
+ Ant Design ©2024 Implement By Angular +
+
+
diff --git a/ng-ui/projects/web/src/app/home/index/index.component.scss b/ng-ui/projects/web/src/app/home/index/index.component.scss index e69de29b..1a841e0d 100644 --- a/ng-ui/projects/web/src/app/home/index/index.component.scss +++ b/ng-ui/projects/web/src/app/home/index/index.component.scss @@ -0,0 +1,45 @@ +:host { + min-width: 100%; + min-height: 100%; +} + +.layout { + min-height: 100vh; +} + +.nz-header { + padding: 0 !important; +} + +.logo { + width: 14rem; + color: white; + float: left; + font-size: 2rem; + justify-content: center; + align-items: center; + padding-left: 2rem; +} + +.header-menu { + line-height: 4rem; + margin-right: 1rem; +} + +.sider-menu { + min-height: 100%; + border-right: 0; +} + +.inner-layout { + padding: 0 1.2rem 1.2rem; +} + +nz-breadcrumb { + margin: 1rem 0; +} + +nz-content { + background: #fff; + padding: 1rem; +} diff --git a/ng-ui/projects/web/src/app/home/index/index.component.spec.ts b/ng-ui/projects/web/src/app/home/index/index.component.spec.ts deleted file mode 100644 index 322e0c8a..00000000 --- a/ng-ui/projects/web/src/app/home/index/index.component.spec.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { IndexComponent } from './index.component'; - -describe('IndexComponent', () => { - let component: IndexComponent; - let fixture: ComponentFixture; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [IndexComponent] - }) - .compileComponents(); - - fixture = TestBed.createComponent(IndexComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/ng-ui/projects/web/src/app/home/index/index.component.ts b/ng-ui/projects/web/src/app/home/index/index.component.ts index 55e258c5..e16002d6 100644 --- a/ng-ui/projects/web/src/app/home/index/index.component.ts +++ b/ng-ui/projects/web/src/app/home/index/index.component.ts @@ -1,12 +1,90 @@ -import { Component } from '@angular/core'; +import { Component, signal, Type } from '@angular/core'; +import { + ActivatedRoute, + NavigationEnd, + Params, + PRIMARY_OUTLET, + Resolve, + ResolveFn, + Router, + RouterModule, +} from '@angular/router'; +import { NzBreadCrumbModule } from 'ng-zorro-antd/breadcrumb'; +import { NzLayoutModule } from 'ng-zorro-antd/layout'; +import { distinctUntilChanged, filter, map } from 'rxjs'; +import { Menu } from '../menus/menu.types'; +import { MenusService } from '../menus/menus.service'; +import { CommonModule } from '@angular/common'; +import { NzMenuModule } from 'ng-zorro-antd/menu'; + +export interface Breadcrumb { + label: string | ResolveFn | Type>; + params: Params; + url: string; +} @Component({ - selector: 'app-index', + selector: 'home-index', standalone: true, - imports: [], + imports: [NzLayoutModule, NzBreadCrumbModule, RouterModule, CommonModule, NzMenuModule], templateUrl: './index.component.html', - styleUrl: './index.component.scss' + styleUrl: './index.component.scss', }) export class IndexComponent { + menus = signal([] as Menu[]); + breadcrumbs: Breadcrumb[] = []; + + constructor(private router: Router, private activatedRoute: ActivatedRoute, private menusService: MenusService) {} + + ngOnInit() { + this.initMenu(); + this.initBreadcrumb(); + } + + initMenu() { + const menuRequest: Menu = { + pcode: '0', + tenantCode: '0', + }; + this.menusService.getMeMenus(menuRequest); + } + + initBreadcrumb() { + this.breadcrumbs = this.getBreadcrumbs(this.activatedRoute.root); + this.router.events + .pipe( + filter(event => event instanceof NavigationEnd), + distinctUntilChanged(), + map(() => this.getBreadcrumbs(this.activatedRoute.root)) + ) + .subscribe(event => (this.breadcrumbs = event)); + } + + getBreadcrumbs(route: ActivatedRoute, url = '', breads: Breadcrumb[] = []): Breadcrumb[] { + const children: ActivatedRoute[] = route.children; + if (children.length === 0) { + return breads; + } + for (const child of children) { + // verify primary route + if (child.outlet !== PRIMARY_OUTLET) { + continue; + } + if (!child.snapshot.routeConfig?.title || !child.snapshot.url.length) { + return this.getBreadcrumbs(child, url, breads); + } + const title = child.snapshot.routeConfig?.title; + const routeURL: string = child.snapshot.url.map(segment => segment.path).join('/'); + url += `/${routeURL}`; + const breadcrumb: Breadcrumb = { + label: title ? title : 'title', + params: child.snapshot.params, + url, + }; + breads.push(breadcrumb); + return this.getBreadcrumbs(child, url, breads); + } + return breads; + } } diff --git a/ng-ui/projects/web/src/app/home/menus/menu-form.component.ts b/ng-ui/projects/web/src/app/home/menus/menu-form.component.ts new file mode 100644 index 00000000..f574df90 --- /dev/null +++ b/ng-ui/projects/web/src/app/home/menus/menu-form.component.ts @@ -0,0 +1,155 @@ +import { NgIf } from '@angular/common'; +import { Component, EventEmitter, Output } from '@angular/core'; +import { FormBuilder, FormGroup, FormsModule, Validators } from '@angular/forms'; +import { NzFormModule } from 'ng-zorro-antd/form'; +import { NzModalModule } from 'ng-zorro-antd/modal'; + +@Component({ + selector: 'menu-form', + standalone: true, + imports: [FormsModule, NzModalModule, NzFormModule, NgIf], + template: ` + +
+ +
+ + 编码 + + + + Please input your username! + The username is redundant! + + + + + 父级 + + + + Please input your username! + The username is redundant! + + + +
+
+ + 租户 + + + + Please input your username! + The username is redundant! + + + + + 类型 + + + + Please input your username! + The username is redundant! + + + +
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+ + + + +
+
...
+
+ `, + styles: [], +}) +export class MenuFormComponent { + isVisible = false; + + @Output() event = new EventEmitter<{ + btn: string; + status: number; + data: null; + }>(); + + menuForm: FormGroup; + + constructor(private formBuilder: FormBuilder) { + this.menuForm = this.formBuilder.group({ + id: [null], + code: [null], + pcode: [null], + tenantCode: [null], + type: [null, Validators.required], + authority: [null, Validators.required, Validators.minLength(6), Validators.maxLength(64)], + name: [null, Validators.required], + path: [null, Validators.required, Validators.pattern(/^(\/|(\/[a-zA-Z0-9_-]+)+)$/)], + sort: [null, Validators.required], + extend: [null], + permissions: [null], + icons: [null], + }); + } + + //表单提交 + onSubmit() { + console.log(this.menuForm.value); + } + + showModal(): void { + this.event.next({ btn: 'show', status: 0, data: null }); + this.isVisible = true; + } + + handleOk(): void { + this.event.next({ btn: 'ok', status: 200, data: null }); + this.isVisible = false; + } + + handleCancel(): void { + this.event.next({ btn: 'cancel', status: -1, data: null }); + this.isVisible = false; + } +} diff --git a/ng-ui/projects/web/src/app/home/menus/menu.types.ts b/ng-ui/projects/web/src/app/home/menus/menu.types.ts new file mode 100644 index 00000000..95c5ffc8 --- /dev/null +++ b/ng-ui/projects/web/src/app/home/menus/menu.types.ts @@ -0,0 +1,51 @@ + +export interface Menu { + id?: number; + code?: string; + pcode?: string; + tenantCode?: string; + type?: MenuType; + authority?: string; + name?: string; + path?: string; + sort?: number; + extend?: never; + creator?: UserAuditor; + updater?: UserAuditor; + createdTime?: Date; + updatedTime?: Date; + permissions?: Permission[]; + icons?: string; + children?: Menu[]; + level?: number; + expand?: boolean; + parent?: Menu; +} + +export interface UserAuditor { + code: string; + username: string; + name?: string; +} + +export enum MenuType { + FOLDER = 'FOLDER', + MENU = 'MENU', + LINK = 'LINK', + API = 'API', +} + +export enum HttpMethod { + GET = 'GET', + POST = 'POST', + PUT = 'PUT', + DELETE = 'DELETE', + ALL = 'ALL', +} + +export interface Permission { + method: HttpMethod; + name: string; + path: string; + authority: string; +} diff --git a/ng-ui/projects/web/src/app/home/menus/menus.component.html b/ng-ui/projects/web/src/app/home/menus/menus.component.html new file mode 100644 index 00000000..55de5a76 --- /dev/null +++ b/ng-ui/projects/web/src/app/home/menus/menus.component.html @@ -0,0 +1,45 @@ +
+
+ +
+
+ +
+
+ + + + 编码 + 排序 + 菜单名称 + 权限标识 + 菜单类型 + 组件路径 + 创建时间 + 操作 + + + + + + + + {{ item.code }} + + {{ item.sort }} + {{ item.name }} + {{ item.authority }} + {item.type, select, FOLDER {FOLDER} MENU {MENU} LINK {LINK} API {API} } + {{ item.path }} + {{ item.createdTime | date : 'short' }} + {{ item.path }} + + + + + + diff --git a/ng-ui/projects/web/src/app/home/menus/menus.component.scss b/ng-ui/projects/web/src/app/home/menus/menus.component.scss new file mode 100644 index 00000000..a7855c49 --- /dev/null +++ b/ng-ui/projects/web/src/app/home/menus/menus.component.scss @@ -0,0 +1,9 @@ +:host { + min-height: 100%; + background-image: linear-gradient( + rgba(0, 0, 255, 0.5), + rgba(255, 255, 0, 0.5) + ); + background-position: center; + background-repeat: no-repeat; +} diff --git a/ng-ui/projects/web/src/app/home/menus/menus.component.ts b/ng-ui/projects/web/src/app/home/menus/menus.component.ts new file mode 100644 index 00000000..a4a63d89 --- /dev/null +++ b/ng-ui/projects/web/src/app/home/menus/menus.component.ts @@ -0,0 +1,93 @@ +import { Component, OnDestroy, OnInit, signal, WritableSignal } from '@angular/core'; +import { MenusService } from './menus.service'; +import { Subject, takeUntil } from 'rxjs'; +import { Menu } from './menu.types'; +import { NzTableModule } from 'ng-zorro-antd/table'; +import { MenuFormComponent } from './menu-form.component'; +import { DatePipe, NgFor } from '@angular/common'; + +@Component({ + selector: 'app-menus', + standalone: true, + templateUrl: './menus.component.html', + styleUrls: ['./menus.component.scss'], + imports: [NzTableModule, MenuFormComponent, DatePipe, NgFor], +}) +export class MenusComponent implements OnInit, OnDestroy { + listMenus: WritableSignal = signal([]); + mapOfExpandedData: Record = {}; + + private _subject: Subject = new Subject(); + + constructor(private menusService: MenusService) {} + + collapse(array: Menu[], data: Menu, $event: boolean): void { + if (!$event) { + if (data.children) { + data.children.forEach(d => { + const target = array.find(a => a.code === d.code); + if (target) { + target.expand = false; + this.collapse(array, target, false); + } + }); + } else { + return; + } + } + } + + convertTreeToList(root: Menu): Menu[] { + const stack: Menu[] = []; + const array: Menu[] = []; + const hashMap = {}; + stack.push({ ...root, level: 0, expand: false }); + while (stack.length !== 0) { + const node = stack.pop(); + if (!node) { + continue; + } + this.visitNode(node, hashMap, array); + if (node.children) { + for (let i = node.children.length - 1; i >= 0; i--) { + stack.push({ + ...node.children[i], + level: node.level ? node.level + 1 : 1, + expand: false, + parent: node, + }); + } + } + } + + return array; + } + + visitNode(node: Menu, hashMap: Record, array: Menu[]): void { + if (!hashMap[node.code ? node.code : '0']) { + hashMap[node.code ? node.code : '0'] = true; + array.push(node); + } + } + + ngOnInit(): void { + const menuRequest: Menu = { + pcode: '0', + tenantCode: '0', + }; + this.menusService + .getMenus(menuRequest) + .pipe(takeUntil(this._subject)) + .subscribe(result => { + this.listMenus.set(result); + this.listMenus().forEach(item => { + this.mapOfExpandedData[item.code ? item.code : '0'] = this.convertTreeToList(item); + }); + }); + } + + ngOnDestroy(): void { + this._subject.next(); + this._subject.complete(); + } +} diff --git a/ng-ui/projects/web/src/app/home/menus/menus.service.ts b/ng-ui/projects/web/src/app/home/menus/menus.service.ts new file mode 100644 index 00000000..e81b41df --- /dev/null +++ b/ng-ui/projects/web/src/app/home/menus/menus.service.ts @@ -0,0 +1,46 @@ +import { Injectable } from '@angular/core'; +import { HttpClient, HttpParams } from '@angular/common/http'; +import { concatMap, delay, from, map, mergeMap, Observable, retry, toArray } from 'rxjs'; +import { Menu } from './menu.types'; + +@Injectable({ + providedIn: 'root', +}) +export class MenusService { + constructor(private http: HttpClient) { + } + + childrenMap = (items: Menu[]) => { + return from(items).pipe( + delay(100), + mergeMap(item => { + return this.getChildren({pcode: item.code}).pipe( + map(children => { + item.children = children; + return item; + }) + ); + }), + retry(3) + ); + }; + + getMenus(request: Menu): Observable { + const params = new HttpParams({fromObject: request as never}); + return this.http + .get('/menus/search', {params: params}) + .pipe(concatMap(this.childrenMap), toArray(), retry(3)); + } + + getMeMenus(request: Menu): Observable { + const params = new HttpParams({fromObject: request as never}); + return this.http + .get('/menus/me', {params: params}) + .pipe(concatMap(this.childrenMap), toArray(), retry(3)); + } + + getChildren(request: Menu): Observable { + const params = new HttpParams({fromObject: request as never}); + return this.http.get('/menus/me', {params: params}); + } +} diff --git a/ng-ui/projects/web/src/app/home/users/users.component.css b/ng-ui/projects/web/src/app/home/users/users.component.css new file mode 100644 index 00000000..5d4e87f3 --- /dev/null +++ b/ng-ui/projects/web/src/app/home/users/users.component.css @@ -0,0 +1,3 @@ +:host { + display: block; +} diff --git a/ng-ui/projects/web/src/app/home/users/users.component.ts b/ng-ui/projects/web/src/app/home/users/users.component.ts new file mode 100644 index 00000000..c2bb7334 --- /dev/null +++ b/ng-ui/projects/web/src/app/home/users/users.component.ts @@ -0,0 +1,9 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-users', + template: `

users works!

`, + styleUrl: './users.component.css', +}) +export class UsersComponent { +} diff --git a/ng-ui/projects/web/src/core/auth.service.ts b/ng-ui/projects/web/src/core/auth.service.ts index f3ff6bba..f2c7db71 100644 --- a/ng-ui/projects/web/src/core/auth.service.ts +++ b/ng-ui/projects/web/src/core/auth.service.ts @@ -1,7 +1,7 @@ -import {inject, Injectable} from '@angular/core'; -import {Subject} from 'rxjs'; -import {HttpErrorResponse} from '@angular/common/http'; -import {CanActivateChildFn, CanActivateFn, CanMatchFn, Router,} from '@angular/router'; +import { inject, Injectable } from '@angular/core'; +import { Subject } from 'rxjs'; +import { HttpErrorResponse } from '@angular/common/http'; +import { CanActivateChildFn, CanActivateFn, CanMatchFn, Router } from '@angular/router'; export const authGuard: | CanMatchFn @@ -15,7 +15,7 @@ export const authGuard: return router.parseUrl(auth.loginUrl); }; -@Injectable({providedIn: 'root'}) +@Injectable({ providedIn: 'root' }) export class AuthService { public readonly loginUrl = '/auth/login'; isLoggedIn = false; @@ -32,7 +32,6 @@ export class AuthService { } return this.token; } - login(token: string) { this.isLoggedIn = true; this.authenticatedSource.next(true); diff --git a/ng-ui/projects/web/src/core/loading.service.ts b/ng-ui/projects/web/src/core/loading.service.ts index 5bf68233..6086fa8f 100644 --- a/ng-ui/projects/web/src/core/loading.service.ts +++ b/ng-ui/projects/web/src/core/loading.service.ts @@ -1,12 +1,26 @@ -import {Injectable} from '@angular/core'; -import {Observable, Subject} from 'rxjs'; +import { Injectable } from '@angular/core'; +import { NavigationEnd, NavigationStart, Router } from '@angular/router'; +import { debounceTime, distinctUntilChanged, Observable, Subject, tap } from 'rxjs'; -@Injectable({providedIn: 'root'}) +@Injectable({ providedIn: 'root' }) export class LoadingService { - // Observable string sources private progressSource: Subject = new Subject(); - progress$: Observable = this.progressSource.asObservable(); - + progress$: Observable = this.progressSource.asObservable().pipe( + debounceTime(500), + distinctUntilChanged(), + tap(res => console.log(`Loading Spin show is: ${res}`)) + ); + constructor(public router: Router) { + this.progressSource.next(true); + router.events.subscribe(event => { + if (event instanceof NavigationStart) { + this.show(); + } + if (event instanceof NavigationEnd) { + this.hide(); + } + }); + } show(): void { this.progressSource.next(true); } diff --git a/ng-ui/projects/web/src/styles.scss b/ng-ui/projects/web/src/styles.scss index 90d4ee00..1774061c 100644 --- a/ng-ui/projects/web/src/styles.scss +++ b/ng-ui/projects/web/src/styles.scss @@ -1 +1,6 @@ /* You can add global styles to this file, and also import other style files */ +html, +body { + height: 100%; + width: 100%; +}