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: `
+
+
+ ...
+
+ `,
+ 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