main
袁野 4 weeks ago
commit 22252db5d4
  1. BIN
      .DS_Store
  2. 17
      .editorconfig
  3. 8
      .gitignore
  4. 4
      .vscode/extensions.json
  5. 20
      .vscode/launch.json
  6. 42
      .vscode/tasks.json
  7. 59
      README.md
  8. 145
      angular.json
  9. 13934
      package-lock.json
  10. 44
      package.json
  11. BIN
      public/favicon.ico
  12. 1
      src/app/app.component.html
  13. 0
      src/app/app.component.scss
  14. 14
      src/app/app.component.ts
  15. 14
      src/app/app.config.server.ts
  16. 27
      src/app/app.config.ts
  17. 8
      src/app/app.routes.server.ts
  18. 49
      src/app/app.routes.ts
  19. 13
      src/app/data/common/dictionary.namespace.ts
  20. 5
      src/app/data/login/localStorage.namespace.ts
  21. 6
      src/app/data/login/login.namespace.ts
  22. 9
      src/app/data/menu/menu.namespace.ts
  23. 12
      src/app/pages/account/account.module.ts
  24. 9
      src/app/pages/account/account.routes.ts
  25. 133
      src/app/pages/account/sys-account/sys-account.component.html
  26. 15
      src/app/pages/account/sys-account/sys-account.component.less
  27. 324
      src/app/pages/account/sys-account/sys-account.component.ts
  28. 100
      src/app/pages/account/sys-role/sys-role.component.html
  29. 12
      src/app/pages/account/sys-role/sys-role.component.less
  30. 293
      src/app/pages/account/sys-role/sys-role.component.ts
  31. 10
      src/app/pages/body/body.module.ts
  32. 68
      src/app/pages/body/index/index.component.html
  33. 96
      src/app/pages/body/index/index.component.scss
  34. 116
      src/app/pages/body/index/index.component.ts
  35. 9
      src/app/pages/body/tab/tab.component.html
  36. 34
      src/app/pages/body/tab/tab.component.scss
  37. 119
      src/app/pages/body/tab/tab.component.ts
  38. 12
      src/app/pages/error/error.module.ts
  39. 15
      src/app/pages/error/error.routes.ts
  40. 5
      src/app/pages/error/error401/error401.component.html
  41. 0
      src/app/pages/error/error401/error401.component.less
  42. 45
      src/app/pages/error/error401/error401.component.ts
  43. 5
      src/app/pages/error/error403/error403.component.html
  44. 0
      src/app/pages/error/error403/error403.component.less
  45. 29
      src/app/pages/error/error403/error403.component.ts
  46. 5
      src/app/pages/error/error404/error404.component.html
  47. 0
      src/app/pages/error/error404/error404.component.less
  48. 27
      src/app/pages/error/error404/error404.component.ts
  49. 5
      src/app/pages/error/error500/error500.component.html
  50. 0
      src/app/pages/error/error500/error500.component.less
  51. 27
      src/app/pages/error/error500/error500.component.ts
  52. 12
      src/app/pages/index/index.module.ts
  53. 6
      src/app/pages/index/index.routes.ts
  54. 0
      src/app/pages/index/index/index.component.html
  55. 0
      src/app/pages/index/index/index.component.scss
  56. 13
      src/app/pages/index/index/index.component.ts
  57. 10
      src/app/pages/login/login.module.ts
  58. 6
      src/app/pages/login/login.routes.ts
  59. 78
      src/app/pages/login/login/login.component.html
  60. 80
      src/app/pages/login/login/login.component.less
  61. 113
      src/app/pages/login/login/login.component.ts
  62. 30
      src/app/pages/student/student-item/student-item.component.html
  63. 33
      src/app/pages/student/student-item/student-item.component.scss
  64. 35
      src/app/pages/student/student-item/student-item.component.ts
  65. 12
      src/app/pages/student/student.module.ts
  66. 7
      src/app/pages/student/student.routes.ts
  67. 102
      src/app/pages/system/menu/menu.component.html
  68. 0
      src/app/pages/system/menu/menu.component.scss
  69. 206
      src/app/pages/system/menu/menu.component.ts
  70. 12
      src/app/pages/system/system.module.ts
  71. 7
      src/app/pages/system/system.routes.ts
  72. 16
      src/app/pipes/common/data-utils.pipe.ts
  73. 39
      src/app/pipes/common/dictionary.pipe.ts
  74. 115
      src/app/services/account/sys-account.service.ts
  75. 25
      src/app/services/commons/dictionary.service.ts
  76. 14
      src/app/services/commons/icon.service.ts
  77. 52
      src/app/services/login.service.ts
  78. 87
      src/app/services/system/menu.service.ts
  79. 73
      src/app/services/system/role.service.ts
  80. 35
      src/app/utils/Interceptor.service.ts
  81. 141
      src/app/utils/dateUtils.service.ts
  82. 28
      src/app/utils/initGuard.service.ts
  83. 41
      src/app/utils/localStorage.service.ts
  84. 13
      src/app/utils/mathUtils.service.ts
  85. 24
      src/app/utils/objectData.service.ts
  86. 63
      src/app/utils/routeReuseStrategy.ts
  87. 0
      src/assets/.gitkeep
  88. BIN
      src/assets/home/home.png
  89. BIN
      src/assets/login/1.png
  90. BIN
      src/assets/login/2.png
  91. BIN
      src/assets/login/3.png
  92. BIN
      src/assets/login/4.png
  93. 7
      src/environments/environment.dev.ts
  94. 7
      src/environments/environment.prod.ts
  95. 19
      src/environments/environment.ts
  96. 13
      src/index.html
  97. 7
      src/main.server.ts
  98. 6
      src/main.ts
  99. 66
      src/server.ts
  100. 1
      src/styles.scss
  101. Some files were not shown because too many files have changed in this diff Show More

BIN
.DS_Store vendored

Binary file not shown.

@ -0,0 +1,17 @@
# Editor configuration, see https://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.ts]
quote_type = single
ij_typescript_use_double_quotes = false
[*.md]
max_line_length = off
trim_trailing_whitespace = false

8
.gitignore vendored

@ -0,0 +1,8 @@
.git/
.idea/
node_modules/
dist/
.angular/

@ -0,0 +1,4 @@
{
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846
"recommendations": ["angular.ng-template"]
}

@ -0,0 +1,20 @@
{
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "ng serve",
"type": "chrome",
"request": "launch",
"preLaunchTask": "npm: start",
"url": "http://localhost:4200/"
},
{
"name": "ng test",
"type": "chrome",
"request": "launch",
"preLaunchTask": "npm: test",
"url": "http://localhost:9876/debug.html"
}
]
}

42
.vscode/tasks.json vendored

@ -0,0 +1,42 @@
{
// For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558
"version": "2.0.0",
"tasks": [
{
"type": "npm",
"script": "start",
"isBackground": true,
"problemMatcher": {
"owner": "typescript",
"pattern": "$tsc",
"background": {
"activeOnStart": true,
"beginsPattern": {
"regexp": "(.*?)"
},
"endsPattern": {
"regexp": "bundle generation complete"
}
}
}
},
{
"type": "npm",
"script": "test",
"isBackground": true,
"problemMatcher": {
"owner": "typescript",
"pattern": "$tsc",
"background": {
"activeOnStart": true,
"beginsPattern": {
"regexp": "(.*?)"
},
"endsPattern": {
"regexp": "bundle generation complete"
}
}
}
}
]
}

@ -0,0 +1,59 @@
# CgWeb
This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 19.2.0.
## Development server
To start a local development server, run:
```bash
ng serve
```
Once the server is running, open your browser and navigate to `http://localhost:4200/`. The application will automatically reload whenever you modify any of the source files.
## Code scaffolding
Angular CLI includes powerful code scaffolding tools. To generate a new component, run:
```bash
ng generate component component-name
```
For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run:
```bash
ng generate --help
```
## Building
To build the project run:
```bash
ng build
```
This will compile your project and store the build artifacts in the `dist/` directory. By default, the production build optimizes your application for performance and speed.
## Running unit tests
To execute unit tests with the [Karma](https://karma-runner.github.io) test runner, use the following command:
```bash
ng test
```
## Running end-to-end tests
For end-to-end (e2e) testing, run:
```bash
ng e2e
```
Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs.
## Additional Resources
For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.

@ -0,0 +1,145 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"cg-web": {
"projectType": "application",
"schematics": {
"@schematics/angular:component": {
"style": "scss"
}
},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:application",
"options": {
"outputPath": "dist/cg-web",
"index": "src/index.html",
"browser": "src/main.ts",
"polyfills": [
"zone.js"
],
"tsConfig": "tsconfig.app.json",
"inlineStyleLanguage": "scss",
"assets": [
"src/favicon.ico",
"src/assets",
{
"glob": "**/*",
"input": "public"
},
{
"glob": "**/*",
"input": "./node_modules/@ant-design/icons-angular/src/inline-svg/",
"output": "/assets/"
}
],
"styles": [
"src/theme.less",
"src/styles.scss"
],
"scripts": [],
"server": "src/main.server.ts",
"outputMode": "server",
"ssr": {
"entry": "src/server.ts"
}
},
"configurations": {
"prod": {
"budgets": [
{
"type": "initial",
"maximumWarning": "500kb",
"maximumError": "2mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
}
],
"outputHashing": "all",
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
]
},
"dev": {
"budgets": [
{
"type": "initial",
"maximumWarning": "500kb",
"maximumError": "2mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
}
],
"outputHashing": "all",
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.dev.ts"
}
]
},
"development": {
"optimization": false,
"extractLicenses": false,
"sourceMap": true
}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": {
"buildTarget": "cg-web:build:production"
},
"development": {
"buildTarget": "cg-web:build:development"
}
},
"defaultConfiguration": "development"
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n"
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"polyfills": [
"zone.js",
"zone.js/testing"
],
"tsConfig": "tsconfig.spec.json",
"inlineStyleLanguage": "scss",
"assets": [
{
"glob": "**/*",
"input": "public"
}
],
"styles": [
"src/styles.scss"
],
"scripts": []
}
}
}
}
},
"cli": {
"analytics": "d2267a73-5d6b-4696-b602-6326a9fc0d26"
}
}

13934
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -0,0 +1,44 @@
{
"name": "cg-web",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"watch": "ng build --watch --configuration development",
"test": "ng test",
"serve:ssr:cg-web": "node dist/cg-web/server/server.mjs"
},
"private": true,
"dependencies": {
"@angular/common": "^19.2.0",
"@angular/compiler": "^19.2.0",
"@angular/core": "^19.2.0",
"@angular/forms": "^19.2.0",
"@angular/platform-browser": "^19.2.0",
"@angular/platform-browser-dynamic": "^19.2.0",
"@angular/platform-server": "^19.2.0",
"@angular/router": "^19.2.0",
"@angular/ssr": "^19.2.0",
"express": "^4.18.2",
"ng-zorro-antd": "^19.1.0",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"zone.js": "~0.15.0"
},
"devDependencies": {
"@angular-devkit/build-angular": "^19.2.0",
"@angular/cli": "^19.2.0",
"@angular/compiler-cli": "^19.2.0",
"@types/express": "^4.17.17",
"@types/jasmine": "~5.1.0",
"@types/node": "^18.18.0",
"jasmine-core": "~5.6.0",
"karma": "~6.4.0",
"karma-chrome-launcher": "~3.2.0",
"karma-coverage": "~2.2.0",
"karma-jasmine": "~5.1.0",
"karma-jasmine-html-reporter": "~2.1.0",
"typescript": "~5.7.2"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

@ -0,0 +1 @@
<router-outlet></router-outlet>

@ -0,0 +1,14 @@
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
@Component({
selector: 'app-root',
standalone: true,
imports: [RouterOutlet],
templateUrl: './app.component.html',
styleUrl: './app.component.scss'
})
export class AppComponent {
title = '超冠体育';
}

@ -0,0 +1,14 @@
import { mergeApplicationConfig, ApplicationConfig } from '@angular/core';
import { provideServerRendering } from '@angular/platform-server';
import { provideServerRouting } from '@angular/ssr';
import { appConfig } from './app.config';
import { serverRoutes } from './app.routes.server';
const serverConfig: ApplicationConfig = {
providers: [
provideServerRendering(),
provideServerRouting(serverRoutes)
]
};
export const config = mergeApplicationConfig(appConfig, serverConfig);

@ -0,0 +1,27 @@
import { ApplicationConfig, provideZoneChangeDetection, importProvidersFrom } from '@angular/core';
import {provideRouter} from '@angular/router';
import { routes } from './app.routes';
import { provideClientHydration, withEventReplay } from '@angular/platform-browser';
import { zh_CN, provideNzI18n } from 'ng-zorro-antd/i18n';
import { registerLocaleData } from '@angular/common';
import zh from '@angular/common/locales/zh';
import { FormsModule } from '@angular/forms';
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
import { provideHttpClient, withInterceptors} from '@angular/common/http';
import {InterceptorService} from "./utils/Interceptor.service";
registerLocaleData(zh);
export const appConfig: ApplicationConfig = {
providers: [
provideZoneChangeDetection({ eventCoalescing: true }),
provideRouter(routes),
provideClientHydration(withEventReplay()),
provideNzI18n(zh_CN),
importProvidersFrom(FormsModule),
provideAnimationsAsync(),
provideHttpClient( withInterceptors([InterceptorService]),),
]
};

@ -0,0 +1,8 @@
import { RenderMode, ServerRoute } from '@angular/ssr';
export const serverRoutes: ServerRoute[] = [
{
path: '**',
renderMode: RenderMode.Prerender
}
];

@ -0,0 +1,49 @@
import { Routes } from '@angular/router';
import {Error404Component} from './pages/error/error404/error404.component';
import {InitGuardService} from './utils/initGuard.service';
import {IndexComponent} from './pages/body/index/index.component';
export const routes: Routes = [
{path: '', pathMatch: 'full', redirectTo: 'admin/index'},
{
path: 'login',
loadChildren: () => import('./pages/login/login.routes').then(m => m.LOGIN_ROUTES)
},
{
path: 'index',
loadChildren: () => import('./pages/login/login.routes').then(m => m.LOGIN_ROUTES)
},
{
path: 'admin', component: IndexComponent,
children: [
{
path: 'index',
loadChildren: () => import('./pages/index/index.routes').then(m => m.INDEX_ROUTES),
canActivate: [InitGuardService]
},
{
path: 'system',
loadChildren: () => import('./pages/system/system.routes').then(m => m.SYSTEM_ROUTES),
canActivate: [InitGuardService]
},
{
path: 'account',
loadChildren: () => import('./pages/account/account.routes').then(m => m.ACCOUNT_ROUTES),
canActivate: [InitGuardService]
},
{
path: 'student',
loadChildren: () => import('./pages/student/student.routes').then(m => m.STUDENT_ROUTES),
canActivate: [InitGuardService]
},
]
},
{
path: 'error',
loadChildren: () => import('./pages/error/error.routes').then(m => m.ERROR_ROUTES),
},
{
path: '**', component: Error404Component
},
];

@ -0,0 +1,13 @@
export interface Dictionary {
codeType: string;
codeValue: string;
codeName: string;
codeDesc: string;
sortId: number;
ext1: string;
ext2: string;
ext3: string;
}
export let dictionaryData: Dictionary[] = [];

@ -0,0 +1,5 @@
export const USER_TOKEN = 'userToken';
export const INIT_FLAG = 'initFlag';
export const DATA = 'DATA';
export const LOGIN_DATA = 'loginData';

@ -0,0 +1,6 @@
export const backgroundImage = [
"./assets/login/1.png",
"./assets/login/2.png",
"./assets/login/3.png",
"./assets/login/4.png"];

@ -0,0 +1,9 @@
export const menuData = [
{title: "首页" , selected: true} ,
{title: "商户管理" , selected: false} ,
{title: "数据交易" , selected: false} ,
{title: "产品中心" , selected: false} ,
{title: "账号管理" , selected: false} ,
{title: "营销工具" , selected: false}
];

@ -0,0 +1,12 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
@NgModule({
declarations: [],
imports: [
CommonModule
]
})
export class AccountModule { }

@ -0,0 +1,9 @@
import { Routes } from '@angular/router';
import {SysAccountComponent} from "./sys-account/sys-account.component";
import {SysRoleComponent} from "./sys-role/sys-role.component";
export const ACCOUNT_ROUTES: Routes = [
{ path: 'sys_account', component: SysAccountComponent},
{ path: 'sys_role', component: SysRoleComponent},
];

@ -0,0 +1,133 @@
<form nz-form [formGroup]="searchForm" class="search_form" [nzLayout]="'vertical'">
<div nz-row [nzGutter]="24">
<div nz-col [nzSpan]="6">
<nz-form-item>
<nz-form-label>用户名</nz-form-label>
<nz-form-control>
<input nzSize="large" nz-input formControlName="userName" placeholder="请输入" />
</nz-form-control>
</nz-form-item>
</div>
<div nz-col [nzSpan]="6">
<nz-form-item>
<nz-form-label>登录账户</nz-form-label>
<nz-form-control>
<input nzSize="large" nz-input formControlName="loginName" placeholder="请输入"/>
</nz-form-control>
</nz-form-item>
</div>
<div nz-col [nzSpan]="6">
<nz-form-item>
<nz-form-label>联系方式</nz-form-label>
<nz-form-control>
<input nzSize="large" nz-input formControlName="telephone" placeholder="请输入"/>
</nz-form-control>
</nz-form-item>
</div>
<div nz-col [nzSpan]="6">
<nz-form-item>
<nz-form-label>账户状态</nz-form-label>
<nz-form-control>
<nz-select nzSize="large" formControlName="status" nzAllowClear [nzPlaceHolder]="'请选择'">
<nz-option *ngFor="let item of accountStatus" nzValue="{{item.codeValue}}" nzLabel="{{item.codeName}}"></nz-option>
</nz-select>
</nz-form-control>
</nz-form-item>
</div>
<div nz-col [nzSpan]="6">
<button nz-button [nzType]="'primary'" (click)="searchFormSubmit()">查询</button>
<button nz-button (click)="searchFormReset()">重置</button>
<button nz-button [nzType]="'primary'" (click)="showEditAccount(true, null)">创建</button>
</div>
</div>
</form>
<nz-table #basicTable
[nzScroll]="{ x: '1100px' }"
[nzBordered]="true"
[nzFrontPagination]="false"
[nzShowQuickJumper]="true"
[nzShowTotal]="totalTemplate"
[(nzPageIndex)]="tablePageNum"
(nzPageIndexChange)="queryData()"
[nzTotal]="tableData.total"
[nzData]="tableData.list" >
<thead>
<tr>
<th nzWidth="80px">用户名</th>
<th nzWidth="50px">登录账户</th>
<th nzWidth="80px">联系方式</th>
<th nzWidth="50px">创建时间</th>
<th nzWidth="50px">更新时间</th>
<th nzRight nzWidth="50px">操作</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let data of basicTable.data">
<td>{{data.userName}}</td>
<td>{{data.loginName}}</td>
<td>{{data.telephone}}</td>
<td>{{data.createTime | date: 'yyyy-MM-dd HH:mm'}}</td>
<td>{{data.updateTime | date: 'yyyy-MM-dd HH:mm'}}</td>
<td nzRight>
<a (click)="showEditAccount(false, data)">修改</a>
<nz-divider nzType="vertical"></nz-divider>
<a nz-dropdown [nzDropdownMenu]="menu">
更多
<span nz-icon nzType="down"></span>
</a>
<nz-dropdown-menu #menu="nzDropdownMenu">
<ul nz-menu nzSelectable>
<li nz-menu-item *ngIf="data.status == 1"><a (click)="showDisable(data.id)">禁用账户</a></li>
<li nz-menu-item *ngIf="data.status == 2"><a (click)="showRestore(data.id)">恢复账户</a></li>
<li nz-menu-item><a (click)="showResetPwd(data.id)">密码重置</a></li>
<li nz-menu-item nzDanger (click)="showDelData(data.id)">删除</li>
</ul>
</nz-dropdown-menu>
</td>
</tr>
</tbody>
<ng-template #totalTemplate let-total>总计 {{ total }} 条</ng-template>
</nz-table>
<nz-modal [(nzVisible)]="editAccountVisible" nzTitle="{{editAccountTitle}}" [nzFooter]="null" (nzOnCancel)="closeEditAccount()">
<ng-container *nzModalContent>
<form nz-form [formGroup]="editAccountForm" (ngSubmit)="submitEditAccountForm()">
<nz-form-item>
<nz-form-label [nzSm]="6" [nzXs]="24" nzRequired>用户名</nz-form-label>
<nz-form-control [nzSm]="14" [nzXs]="24" nzErrorTip="用户名填写有误!">
<input nzSize="large" nz-input formControlName="userName"/>
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label [nzSm]="6" [nzXs]="24" nzRequired>登录账户</nz-form-label>
<nz-form-control [nzSm]="14" [nzXs]="24" nzErrorTip="登录账户填写有误!">
<input nzSize="large" nz-input formControlName="loginName"/>
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label [nzSm]="6" [nzXs]="24">联系方式</nz-form-label>
<nz-form-control [nzSm]="14" [nzXs]="24">
<input nzSize="large" nz-input formControlName="telephone"/>
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label [nzSm]="6" [nzXs]="24" nzRequired>角色权限</nz-form-label>
<nz-form-control [nzSm]="14" [nzXs]="24" nzErrorTip="角色权限选择有误!">
<nz-select nzSize="large" nzShowSearch formControlName="roleId">
<nz-option *ngFor="let item of roleData" nzValue="{{item.id}}" nzLabel="{{item.roleName}}"></nz-option>
</nz-select>
</nz-form-control>
</nz-form-item>
<div nz-flex [nzJustify]="'center'">
<button nz-button nzType="primary" class="submit-btn">保存</button>
</div>
</form>
</ng-container>
</nz-modal>

@ -0,0 +1,15 @@
[nz-form-label] {
overflow: visible;
}
button {
margin-right: 8px;
}
.search-area {
text-align: left;
}
.submit-btn {
width: 150px;
}
.search_form {
margin-bottom: 15px;
}

@ -0,0 +1,324 @@
import { Component } from '@angular/core';
import {FormGroup, NonNullableFormBuilder, ReactiveFormsModule, Validators} from "@angular/forms";
import {NzMessageService} from "ng-zorro-antd/message";
import {NzModalComponent, NzModalModule, NzModalService} from "ng-zorro-antd/modal";
import {DatePipe, NgForOf, NgIf} from "@angular/common";
import {NzButtonComponent} from "ng-zorro-antd/button";
import {NzColDirective, NzRowDirective} from "ng-zorro-antd/grid";
import {NzDividerComponent} from "ng-zorro-antd/divider";
import {NzFlexDirective} from "ng-zorro-antd/flex";
import {NzFormControlComponent, NzFormDirective, NzFormItemComponent, NzFormLabelComponent} from "ng-zorro-antd/form";
import {NzInputDirective} from "ng-zorro-antd/input";
import {
NzTableCellDirective,
NzTableComponent, NzTableModule,
NzTbodyComponent,
NzTheadComponent,
NzThMeasureDirective, NzTrDirective
} from "ng-zorro-antd/table";
import {NzTreeComponent} from "ng-zorro-antd/tree";
import {NzOptionComponent, NzSelectComponent} from "ng-zorro-antd/select";
import {NzDropDownModule} from "ng-zorro-antd/dropdown";
import {NzIconDirective} from "ng-zorro-antd/icon";
import {DictionaryPipe} from "../../../pipes/common/dictionary.pipe";
import {Dictionary} from "../../../data/common/dictionary.namespace";
import {SysAccountService} from "../../../services/account/sys-account.service";
import {RoleService} from '../../../services/system/role.service';
@Component({
selector: 'app-sys-account',
standalone: true,
imports: [
DatePipe,
NgForOf,
NgIf,
NzButtonComponent,
NzColDirective,
NzDividerComponent,
NzFlexDirective,
NzFormControlComponent,
NzFormDirective,
NzFormItemComponent,
NzFormLabelComponent,
NzInputDirective,
NzModalComponent,
NzRowDirective,
NzTableCellDirective,
NzTableComponent,
NzTbodyComponent,
NzThMeasureDirective,
NzTheadComponent,
NzTrDirective,
NzTreeComponent,
ReactiveFormsModule,
NzTableModule,
NzModalModule,
NzSelectComponent,
NzOptionComponent,
NzDropDownModule,
NzIconDirective,
DictionaryPipe,
],
templateUrl: './sys-account.component.html',
styleUrl: './sys-account.component.less'
})
export class SysAccountComponent {
// 表单页数
tablePageNum = 1;
// 表单数据
tableData: any = {
total: 0,
list: [],
};
accountStatus: Dictionary[] = []
// 角色数据
roleData: any = [];
// 搜索表单
searchForm: FormGroup;
// 编辑账户弹出框
editAccountVisible = false;
// 编辑账户表单
editAccountForm: FormGroup;
// 编辑账户标题
editAccountTitle = '';
constructor(private fb: NonNullableFormBuilder,
private sysAccountService: SysAccountService,
private roleService: RoleService,
private message: NzMessageService,
private modal: NzModalService) {
// 初始化搜索框
this.searchForm = this.fb.group({
userName: [''],
loginName: [''],
telephone: [''],
status: [''],
});
// 初始化账户表单
this.editAccountForm = this.fb.group({
id: [''],
userName: ['', [Validators.required]],
loginName: ['', [Validators.required]],
telephone: [''],
platformCode: [''],
roleId: ['', [Validators.required]],
});
this.accountStatus = new DictionaryPipe().getDictionaryList("SEC_USER_STATUS");
this.queryAllRole();
this.queryData();
}
/**
*
*/
queryAllRole() {
this.roleService.queryAllRole((data: any) => {
if (data['return_code'] == '000000') {
this.roleData = data['return_data'];
}
});
}
/**
*
*/
queryData() {
this.searchForm.value.pageNum = this.tablePageNum;
this.searchForm.value.pageSize = 10;
this.searchForm.value.objectType = 1;
this.searchForm.value.time = new Date().getTime();
this.sysAccountService.queryList(this.searchForm.value, (data: any) => {
if (data['return_code'] == '000000') {
this.tableData = data['return_data'];
}
});
}
/**
*
*/
searchFormSubmit(): void {
this.tablePageNum = 1;
this.queryData();
}
/**
*
*/
searchFormReset(): void {
this.searchForm.reset();
}
/**
*
* @param edit true: false
* @param data
*/
showEditAccount(edit: boolean, data: any) {
if (edit) {
this.editAccountTitle = '创建账户';
} else {
this.editAccountTitle = '修改账户';
data['roleId'] = ""+data['roleId']
this.editAccountForm.patchValue(data);
}
this.editAccountVisible = true;
}
/**
*
*/
submitEditAccountForm() {
if (this.editAccountForm.valid) {
this.sysAccountService.editUser(this.editAccountForm.value, (data: any) => {
if (data['return_code'] == '000000') {
// 刷新数据
this.queryData();
this.message.create('success', '操作成功');
// 关闭弹窗
this.closeEditAccount();
} else {
this.message.create('error', data['return_msg']);
}
});
} else {
Object.values(this.editAccountForm.controls).forEach(control => {
if (control.invalid) {
control.markAsDirty();
control.updateValueAndValidity({ onlySelf: true });
}
});
}
}
/**
*
*/
closeEditAccount() {
this.editAccountForm.reset();
this.editAccountVisible = false;
}
/**
*
*/
showRestore(dataId: number) {
this.modal.confirm({
nzTitle: '提示',
nzContent: '确定恢复账户状态?',
nzOnOk: () => this.restore(dataId)
});
}
/**
*
*/
restore(dataId: number) {
const param = {
userId: dataId
}
this.sysAccountService.restore(param, (data: any) => {
if (data['return_code'] == '000000') {
// 刷新数据
this.queryData();
this.message.success("操作成功");
} else {
this.message.create('error', data['return_msg']);
}
});
}
/**
*
*/
showDisable(dataId: number) {
this.modal.confirm({
nzTitle: '提示',
nzContent: '确定禁用账户吗?禁用后无法登录!',
nzOnOk: () => this.disable(dataId)
});
}
/**
*
*/
disable(dataId: number) {
const param = {
userId: dataId
}
this.sysAccountService.disable(param, (data: any) => {
if (data['return_code'] == '000000') {
// 刷新数据
this.queryData();
this.message.success("操作成功");
} else {
this.message.create('error', data['return_msg']);
}
});
}
/**
*
*/
showResetPwd(dataId: number) {
this.modal.confirm({
nzTitle: '提示',
nzContent: '确定重置账户登录密码吗?',
nzOnOk: () => this.resetPwd(dataId)
});
}
/**
*
*/
resetPwd(dataId: number) {
const param = {
userId: dataId
}
this.sysAccountService.resetPwd(param, (data: any) => {
if (data['return_code'] == '000000') {
// 刷新数据
this.queryData();
this.modal.success({
nzTitle: '提示',
nzContent: '密码重置成功,新密码:123456'
});
} else {
this.message.create('error', data['return_msg']);
}
});
}
/**
*
*/
showDelData(dataId: number) {
this.modal.confirm({
nzTitle: '提示',
nzContent: '确定删除该账户数据?',
nzOnOk: () => this.delData(dataId)
});
}
/**
*
*/
delData(dataId: number) {
const param = {
userId: dataId
}
this.sysAccountService.delete(param, (data: any) => {
if (data['return_code'] == '000000') {
// 刷新数据
this.queryData();
this.message.create('success', '操作成功');
} else {
this.message.create('error', data['return_msg']);
}
});
}
}

@ -0,0 +1,100 @@
<form nz-form [formGroup]="searchForm" [nzLayout]="'vertical'">
<div nz-row [nzGutter]="24">
<div nz-col [nzSpan]="4">
<nz-form-item>
<nz-form-label>角色名称</nz-form-label>
<nz-form-control>
<input nzSize="large" nz-input formControlName="roleName" placeholder="请输入" />
</nz-form-control>
</nz-form-item>
</div>
<div nz-col [nzSpan]="6" class="search-area">
<button nz-button [nzType]="'primary'" (click)="searchFormSubmit()">查询</button>
<button nz-button (click)="searchFormReset()">重置</button>
<button nz-button [nzType]="'primary'" (click)="showEditRole(true, null)">创建</button>
</div>
</div>
</form>
<nz-table #basicTable
[nzBordered]="true"
[nzFrontPagination]="false"
[nzShowQuickJumper]="true"
[nzShowTotal]="totalTemplate"
[(nzPageIndex)]="tablePageNum"
(nzPageIndexChange)="queryData()"
[nzTotal]="tableData.total"
[nzData]="tableData.list" >
<thead>
<tr>
<th nzWidth="80px">角色名称</th>
<th nzWidth="100px">角色描述</th>
<th nzWidth="50px">创建时间</th>
<th nzWidth="50px">更新时间</th>
<th nzWidth="70px">操作</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let data of basicTable.data">
<td>{{data.roleName}}</td>
<td>{{data.roleDesc}}</td>
<td>{{data.createTime | date: 'yyyy-MM-dd HH:mm'}}</td>
<td>{{data.updateTime | date: 'yyyy-MM-dd HH:mm'}}</td>
<td>
<a (click)="showEditRole(false, data)">修改</a>
<nz-divider nzType="vertical"></nz-divider>
<a (click)="showAssignRoleMenu(data.id)">分配菜单</a>
<nz-divider nzType="vertical"></nz-divider>
<a (click)="showDelData(data.id)">删除</a>
</td>
</tr>
</tbody>
<ng-template #totalTemplate let-total>总计 {{ total }} 条</ng-template>
</nz-table>
<nz-modal [(nzVisible)]="editRoleVisible" nzTitle="{{editRoleTitle}}" [nzFooter]="null" (nzOnCancel)="closeEditRole()">
<ng-container *nzModalContent>
<form nz-form [formGroup]="editRoleForm" (ngSubmit)="submitEditRoleForm()">
<nz-form-item>
<nz-form-label [nzSm]="6" [nzXs]="24" nzRequired>角色名称</nz-form-label>
<nz-form-control [nzSm]="14" [nzXs]="24" nzErrorTip="角色名称填写有误!">
<input nzSize="large" nz-input formControlName="roleName"/>
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label [nzSm]="6" [nzXs]="24" nzRequired>角色描述</nz-form-label>
<nz-form-control [nzSm]="14" [nzXs]="24" nzErrorTip="角色描述填写有误!">
<input nzSize="large" nz-input formControlName="roleDesc"/>
</nz-form-control>
</nz-form-item>
<div nz-flex [nzJustify]="'center'">
<button nz-button nzType="primary" class="submit-btn">保存</button>
</div>
</form>
</ng-container>
</nz-modal>
<nz-modal [(nzVisible)]="assignRoleMenuVisible" nzTitle="角色菜单权限" [nzFooter]="null" (nzOnCancel)="closeAssignRoleMenu()">
<ng-container *nzModalContent>
<nz-tree
#nzTreeComponent
*ngIf="menuTree.length"
[nzData]="menuTree"
[nzCheckedKeys]="menuTreeCheckedKeys"
[nzShowLine]="true"
[nzExpandAll]="true"
[nzMultiple]="false"
[nzCheckable]="true"
nzVirtualHeight="500px"
></nz-tree>
<nz-divider nzOrientation="right"></nz-divider>
<div nz-flex [nzJustify]="'center'">
<button nz-button nzType="primary" class="submit-btn" (click)="submitAssignRoleMenu()">保存</button>
</div>
</ng-container>
</nz-modal>

@ -0,0 +1,12 @@
[nz-form-label] {
overflow: visible;
}
button {
margin-left: 8px;
}
.submit-btn {
width: 150px;
}
.search-area {
margin-top: 30px;
}

@ -0,0 +1,293 @@
import {Component, ViewChild} from '@angular/core';
import {
NzTableComponent,
NzTableModule,
NzTbodyComponent,
NzTheadComponent,
NzThMeasureDirective
} from "ng-zorro-antd/table";
import {NzDividerComponent} from "ng-zorro-antd/divider";
import {NzFormDirective, NzFormModule} from "ng-zorro-antd/form";
import {NzColDirective, NzRowDirective} from "ng-zorro-antd/grid";
import {
FormGroup,
NonNullableFormBuilder,
ReactiveFormsModule, Validators,
} from "@angular/forms";
import {NzButtonComponent} from "ng-zorro-antd/button";
import {NzIconDirective} from "ng-zorro-antd/icon";
import {NzInputDirective} from "ng-zorro-antd/input";
import {NzFlexDirective} from "ng-zorro-antd/flex";
import {DatePipe, NgForOf, NgIf} from "@angular/common";
import {NzModalComponent, NzModalModule, NzModalService} from "ng-zorro-antd/modal";
import {NzMessageService} from "ng-zorro-antd/message";
import {NzInputNumberComponent} from "ng-zorro-antd/input-number";
import {NzOptionComponent, NzSelectComponent} from "ng-zorro-antd/select";
import {NzTreeComponent, NzTreeNodeOptions} from "ng-zorro-antd/tree";
import {RoleService} from '../../../services/system/role.service';
import {MenuService} from '../../../services/system/menu.service';
@Component({
selector: 'app-sys-role',
standalone: true,
imports: [
NzTableComponent,
NzTheadComponent,
NzDividerComponent,
NzTbodyComponent,
NzFormDirective,
NzRowDirective,
ReactiveFormsModule,
NzColDirective,
NzButtonComponent,
NzInputDirective,
NzFormModule,
NzFlexDirective,
NgForOf,
DatePipe,
NzThMeasureDirective,
NzModalComponent,
NzOptionComponent,
NzSelectComponent,
NzModalModule,
NgIf,
NzTreeComponent,
NzTableModule
],
templateUrl: './sys-role.component.html',
styleUrl: './sys-role.component.less'
})
export class SysRoleComponent {
// 表单页数
tablePageNum = 1;
// 表单数据
tableData: any = {
total: 0,
list: [],
};
// 搜索表单
searchForm: FormGroup;
// 编辑角色弹出框
editRoleVisible = false;
// 编辑角色表单
editRoleForm: FormGroup;
// 编辑角色标题
editRoleTitle = '';
// 分配角色菜单弹出框
assignRoleMenuVisible = false;
// 当前需要分配权限角色
currentAssignRoleId: any = null;
// 菜单树
menuTree: NzTreeNodeOptions[] = [];
// 菜单已勾选数据
menuTreeCheckedKeys: any = [];
@ViewChild('nzTreeComponent', { static: false }) nzTreeComponent!: NzTreeComponent;
constructor(private fb: NonNullableFormBuilder,
private roleService: RoleService,
private menuService: MenuService,
private message: NzMessageService,
private modal: NzModalService) {
// 初始化搜索框
this.searchForm = this.fb.group({
roleName: [''],
});
// 初始化表单
this.editRoleForm = this.fb.group({
id: [''],
roleName: ['', [Validators.required]],
roleDesc: [''],
});
this.menuService.queryMenuTree((data: any) => {
console.log("树数据" , data['return_data']);
this.menuTree = data['return_data'];
});
this.queryData();
}
/**
*
*/
queryData() {
this.searchForm.value.pageNum = this.tablePageNum;
this.searchForm.value.pageSize = 10;
this.searchForm.value.time = new Date().getTime();
this.roleService.queryList(this.searchForm.value, (data: any) => {
if (data['return_code'] == '000000') {
this.tableData = data['return_data'];
}
});
}
/**
*
*/
searchFormSubmit(): void {
this.tablePageNum = 1;
this.queryData();
}
/**
*
*/
searchFormReset(): void {
this.searchForm.reset();
}
/**
*
* @param edit ture false
* @param data
*/
showEditRole(edit: boolean, data: any) {
if (edit) {
this.editRoleTitle = '创建角色';
} else {
this.editRoleTitle = '修改角色';
this.editRoleForm.patchValue(data);
}
this.editRoleVisible = true;
}
/**
*
* @param edit
*/
submitEditRoleForm() {
if (this.editRoleForm.valid) {
this.roleService.editRole(this.editRoleForm.value, (data: any) => {
if (data['return_code'] == '000000') {
// 刷新数据
this.queryData();
this.message.create('success', '操作成功');
// 关闭弹窗
this.closeEditRole();
} else {
this.message.create('error', data['return_msg']);
}
});
} else {
Object.values(this.editRoleForm.controls).forEach(control => {
if (control.invalid) {
control.markAsDirty();
control.updateValueAndValidity({ onlySelf: true });
}
});
}
}
/**
*
*/
closeEditRole() {
this.editRoleForm.reset();
this.editRoleVisible = false;
}
/**
*
*/
showDelData(dataId: number) {
this.modal.confirm({
nzTitle: '提示',
nzContent: '确定删除数据?',
nzOnOk: () => this.delData(dataId)
});
}
/**
*
*/
delData(dataId: number) {
const param = {
id: dataId
}
this.roleService.delRole(param, (data: any) => {
if (data['return_code'] == '000000') {
// 刷新数据
this.queryData();
this.message.create('success', '操作成功');
} else {
this.message.create('error', data['return_msg']);
}
});
}
/**
*
* @param roleId
*/
showAssignRoleMenu(roleId: number) {
this.currentAssignRoleId = roleId;
this.menuService.queryRoleMenuArray(roleId, (data: any) => {
console.log("角色权限菜单" , data['return_data']);
console.log("树" , this.menuTree);
this.menuTreeCheckedKeys = data['return_data'];
});
this.assignRoleMenuVisible = true;
}
/**
*
* @param data
* @param merIds
*/
recursionData(data: any, merIds: any): any {
for (let node of data.children) {
merIds.push(node.key)
if (node.children != null) {
this.recursionData(node,merIds);
}
}
}
/**
*
*/
submitAssignRoleMenu() {
let menuIds: any = [];
// 已勾选的菜单
const checkedNodeList = this.nzTreeComponent.getCheckedNodeList();
for (let node of checkedNodeList) {
menuIds.push(node.origin.key);
if (node.origin.children != null) {
menuIds.concat(this.recursionData(node.origin, menuIds))
}
};
// 半选状态的菜单
const halfCheckedNodeList = this.nzTreeComponent.getHalfCheckedNodeList();
for (let node of halfCheckedNodeList) {
menuIds.push(node.key);
}
const param= {
roleId: this.currentAssignRoleId,
menuIds: menuIds
};
this.menuService.assignMenu(param, (data: any) => {
if (data['return_code'] == '000000') {
this.message.create('success', '操作成功');
this.closeAssignRoleMenu();
} else {
this.message.create('error', data['return_msg']);
}
});
}
/**
*
*/
closeAssignRoleMenu() {
this.menuTreeCheckedKeys = [];
this.assignRoleMenuVisible = false;
}
}

@ -0,0 +1,10 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
@NgModule({
declarations: [],
imports: [
CommonModule
]
})
export class BodyModule { }

@ -0,0 +1,68 @@
<nz-layout>
<!-- start 头部 start-->
<nz-header>
<div nz-row>
<!-- logo -->
<div class="logo" nz-col nzSpan="4">
<h2>超冠体育管理平台</h2>
</div>
<!-- logo -->
<div class="menu" nz-col nzSpan="16">
<ul>
<li *ngFor="let item of menuData" [ngClass]="{'li-selected' : item.selected}" (click)="isSelected(item)">{{item.menuName}}</li>
</ul>
</div>
<div class="user" nz-col nzSpan="4">
<div>
<span nz-dropdown [nzDropdownMenu]="menu">{{userInfo['userName']}}</span>
<nz-dropdown-menu #menu="nzDropdownMenu">
<ul nz-menu nzSelectable>
<li nz-menu-item (click)="loginOut()">退出登录</li>
</ul>
</nz-dropdown-menu>
</div>
</div>
</div>
</nz-header>
<!-- end 头部 end -->
<nz-layout>
<!-- start 侧边 start-->
<nz-sider nzCollapsible [(nzCollapsed)]="isCollapse" [nzTrigger]="null" [nzCollapsedWidth]="0" >
<div class="side_menu" style="height: 50px;">
<span
class="trigger"
nz-icon
[nzType]="isCollapse ? 'menu-unfold' : 'menu-fold'"
(click)="isCollapse = !isCollapse"
></span>
</div>
<!-- start 菜单内容 end-->
<div>
<ul nz-menu nzMode="inline">
<li nz-submenu nzOpen *ngFor="let item of leftMenuData" nzTitle="{{item.menuName}}" nzIcon="{{item.menuUrlImg}}" >
<ul>
<li nz-menu-item nzMatchRouter *ngFor="let childMenu of item['childMenuList']">
<a [routerLink]="[currentParentMenu['menuUrl'], childMenu['menuUrl']]" [queryParams]="{title: childMenu['menuName']}">{{childMenu['menuName']}}</a>
</li>
</ul>
</li>
</ul>
</div>
<!-- start 菜单内容 end -->
</nz-sider>
<!-- end 侧边 end -->
<!-- start 主体 start-->
<nz-content>
<app-tab></app-tab>
</nz-content>
<!-- end 主体 end -->
</nz-layout>
</nz-layout>

@ -0,0 +1,96 @@
nz-header {
background: #181f47;
padding: 0 26px;
.logo {
h2 {
color: white;
}
}
.menu {
width: 52%;
display: flex;
justify-content: center;
ul {
li {
height: 56px;
float: left;
color: #fff;
opacity: .65;
font-size: 14px;
padding: 0 20px;
cursor: pointer;
}
.li-selected {
opacity: 1;
border-bottom: 2px solid #1890ff;
}
li:hover {
opacity: 1;
border-bottom: 2px solid #1890ff;
}
list-style-type: none; /* 移除列表的标记,如果需要的话 */
padding: 0; /* 移除padding,如果需要的话 */
}
}
}
nz-sider {
background: #fff;
box-shadow: 2px 0 8px rgba(0,0,0,.15);
height: calc(100vh - 64px);;
.trigger {
margin-top: 16px;
font-size: 18px;
line-height: 50px;
padding: 0 24px;
cursor: pointer;
transition: color 0.3s;
}
.trigger:hover {
color: #1890ff;
}
}
nz-content {
padding: 0 0 0 4px;
}
.user {
div {
cursor: pointer;
float: right;
margin-right: 30px;
color: white;
white-space: nowrap;
text-align: left;
overflow: hidden;
text-overflow: ellipsis;
}
}
.file-downLoad {
width: 300px;
}
.button-up {
font-size: 16px;
padding: 10px;
top: 48px;
left: 22px;
z-index: 999;
border-radius: 5px;
position:absolute;
height: 80px;
width: 280px;
background-color: rgba(0, 0, 0, 0.7);
}
.to_top {
z-index: 999;
top: 40px;
position:absolute;
width: 0;
height: 0;
border-bottom: 8px solid rgba(0, 0, 0, 0.7);
border-left: 8px solid transparent;
border-right: 8px solid transparent;
}

@ -0,0 +1,116 @@
import {
AbstractControl, FormControl,
FormGroup,
NonNullableFormBuilder, ReactiveFormsModule,
ValidatorFn,
Validators
} from "@angular/forms";
import {Component, inject} from '@angular/core';
import {DATA, INIT_FLAG, LOGIN_DATA, USER_TOKEN} from '../../../data/login/localStorage.namespace';
import {LoginService} from '../../../services/login.service';
import {NzConfigService} from 'ng-zorro-antd/core/config';
import {LocalStorageService} from '../../../utils/localStorage.service';
import {Router, RouterLink} from '@angular/router';
import {NzMessageService} from 'ng-zorro-antd/message';
import {NzContentComponent, NzHeaderComponent, NzLayoutComponent, NzSiderComponent} from 'ng-zorro-antd/layout';
import {NzColDirective, NzRowDirective} from 'ng-zorro-antd/grid';
import {NgClass, NgForOf} from '@angular/common';
import {NzDropDownDirective, NzDropdownMenuComponent} from 'ng-zorro-antd/dropdown';
import {NzMenuDirective, NzMenuItemComponent, NzSubMenuComponent} from 'ng-zorro-antd/menu';
import {NzIconDirective} from 'ng-zorro-antd/icon';
import {TabComponent} from '../tab/tab.component';
@Component({
selector: 'app-index',
standalone: true,
imports: [
NzLayoutComponent,
NzHeaderComponent,
NzRowDirective,
NzColDirective,
NgClass,
NgForOf,
NzDropDownDirective,
NzMenuDirective,
NzSiderComponent,
NzIconDirective,
NzSubMenuComponent,
NzMenuItemComponent,
RouterLink,
NzContentComponent,
TabComponent,
NzDropdownMenuComponent
],
templateUrl: './index.component.html',
styleUrl: './index.component.scss'
})
export class IndexComponent {
// 菜单数据
menuData: any = [{
'menuName' : '首页',
'selected' : true,
}];
userInfo: any;
// 左侧菜单栏数据
leftMenuData: any = [];
// 侧边菜单展示开关
isCollapse = true;
// 当前顶级菜单
currentParentMenu: any = {};
private login = inject(LoginService);
constructor(
private message: NzMessageService ,
private storage: LocalStorageService ,
private router: Router, // 路由
) {
// @ts-ignore
this.menuData = this.menuData.concat(this.storage.getItem(DATA)['menuTree']);
// @ts-ignore
this.userInfo = this.storage.getItem(DATA)['account'];
}
// 选择操作
isSelected(item: any) {
this.menuData.map((data: any) => {
data.selected = data.menuName === item.menuName;
});
if (item.menuName === '首页') {
// 激活首页路由
this.router.navigateByUrl('/admin/index').finally();
}
this.currentParentMenu = item;
this.leftMenuData = item['childMenuList'];
this.isCollapse = item['menuName'] === '首页';
}
// 退出登录
public loginOut(): void {
this.login.loginOut( (data: any) => {
if (data['return_code'] === '000000') {
this.storage.removeItem(LOGIN_DATA);
this.storage.removeItem(DATA);
this.storage.removeItem(USER_TOKEN);
this.storage.removeItem(INIT_FLAG);
location.reload();
this.router.navigateByUrl('/login').then();
} else {
this.message.error(data['return_msg']);
}
});
}
}

@ -0,0 +1,9 @@
<nz-tabset [(nzSelectedIndex)]="activatedMenuIndex" nzType="editable-card" nzHideAdd (nzClose)="closeTab($event)">
<nz-tab *ngFor="let tab of tabs; let i = index" [nzClosable]="i > 0" (nzSelect)="toggleTab(tab.path)" [nzTitle]="tab.title"></nz-tab>
</nz-tabset>
<nz-card [nzBordered]="false" [nzBodyStyle]="{ padding: '12px'}">
<router-outlet></router-outlet>
</nz-card>

@ -0,0 +1,34 @@
:host {
background: white;
overflow: hidden;
display: block;
height: 100%;
}
.card-container ::ng-deep p {
margin: 0;
}
.card-container ::ng-deep > .ant-tabs-card .ant-tabs-content {
margin-top: -16px;
}
.card-container ::ng-deep > .ant-tabs-card .ant-tabs-content > .ant-tabs-tabpane {
background: #f5f5f5;
padding: 16px;
}
.card-container ::ng-deep > .ant-tabs-card > .ant-tabs-nav::before {
display: none;
}
.card-container ::ng-deep > .ant-tabs-card .ant-tabs-tab {
border-color: transparent;
background: transparent;
}
.card-container ::ng-deep > .ant-tabs-card .ant-tabs-tab-active {
border-color: #f5f5f5;
background: #f5f5f5;
}
nz-card {
height: calc(98vh - 114px);
overflow: auto;
}

@ -0,0 +1,119 @@
import { Component } from '@angular/core';
import {ActivationEnd, Router, RouterOutlet} from "@angular/router";
import {filter} from "rxjs";
import {NzTabComponent, NzTabSetComponent} from 'ng-zorro-antd/tabs';
import {NzCardComponent} from 'ng-zorro-antd/card';
import {NgForOf} from '@angular/common';
@Component({
selector: 'app-tab',
standalone: true,
imports: [
NzTabSetComponent,
NzTabComponent,
NzCardComponent,
RouterOutlet,
NgForOf
],
templateUrl: './tab.component.html',
styleUrl: './tab.component.scss'
})
export class TabComponent {
// 当前打开的Tab页
activatedMenuIndex = -1;
// 存放已打开的Tab页信息
tabs: { path: string, title: string }[] = [{ path: '/admin/index', title: '首页' }];
constructor(private router: Router) {
// 激活首页路由
this.router.navigateByUrl('/admin/index').finally();
// 监听路由事件,只订阅 ActivationEnd 事件
this.router.events.pipe(filter(e => e instanceof ActivationEnd))
.subscribe((e) => {
const thisEvt = <ActivationEnd>e;
if (thisEvt.snapshot.routeConfig?.loadChildren) {
// 当前激活的路由
// const activatedRoutePath = thisEvt.snapshot.routeConfig?.path;
const activatedRoutePath = this.router.url;
const routeData = thisEvt.snapshot.queryParams;
let menuTitle = '新标签页';
if(routeData) {
menuTitle = routeData['title'];
}
// 该路由是否已激活,激活过则直接打开
let isExist = false;
this.tabs.every((t, i) => {
if(activatedRoutePath === t.path) {
this.activatedMenuIndex = i;
isExist = true;
return false;
}
return true;
});
// 指定路由不在tabs中存在(未激活或激活后关闭)
if(!isExist) {
this.activeMenu(activatedRoutePath, menuTitle);
}
}
});
}
// 点击菜单激活指定路由并保存tab页签
activeMenu(menuPath: string | undefined, menuTitle: string): void {
if(!menuPath) return;
let menuIndex = -1;
this.tabs.every((t, i) => {
if(menuPath === t.path) {
menuIndex = i;
return false;
}
return true;
});
if(menuIndex === -1) {
this.tabs.push({path: menuPath, title: menuTitle});
menuIndex = this.tabs.length - 1;
this.activatedMenuIndex = menuIndex;
}
}
// 激活路由
activeRoute(path: string): void {
this.router.navigateByUrl(path).finally();
}
// 切换tab,激活对应路由
toggleTab(path: string): void {
this.activeRoute(path);
}
// tab页签关闭,从缓存中删除对应信息
closeTab(tabsObj: any): void {
if (1 === this.tabs.length) return;
const path = this.tabs[tabsObj['index']]['path'];
let selectedIndex = -1;
this.tabs.every((t, i) => {
if(t.path === path) {
selectedIndex = i;
return false;
}
return true;
});
this.tabs.splice(selectedIndex, 1);
if(selectedIndex === this.activatedMenuIndex) {
let prevIndex = this.activatedMenuIndex - 1;
this.activatedMenuIndex = prevIndex > 0 ? prevIndex : 0;
this.activeRoute(this.tabs[this.activatedMenuIndex].path);
}else if (this.activatedMenuIndex > selectedIndex) {
this.activatedMenuIndex -= 1;
}
}
}

@ -0,0 +1,12 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
@NgModule({
declarations: [],
imports: [
CommonModule
]
})
export class ErrorModule { }

@ -0,0 +1,15 @@
import { Routes } from '@angular/router';
import {Error403Component} from "./error403/error403.component";
import {Error404Component} from "./error404/error404.component";
import {Error500Component} from "./error500/error500.component";
import {Error401Component} from "./error401/error401.component";
export const ERROR_ROUTES: Routes = [
{ path: '401', component: Error401Component },
{ path: '403', component: Error403Component },
{ path: '404', component: Error404Component },
{ path: '500', component: Error500Component },
];

@ -0,0 +1,5 @@
<nz-result nzStatus="warning" nzTitle="当前登录信息已过期,请重新登录!">
<div nz-result-extra>
<button nz-button nzType="primary" (click)="loginOut()">重新登录</button>
</div>
</nz-result>

@ -0,0 +1,45 @@
import { Component } from '@angular/core';
import {NzResultModule} from "ng-zorro-antd/result";
import {NzButtonComponent} from "ng-zorro-antd/button";
import {NzMessageService} from "ng-zorro-antd/message";
import {Router} from "@angular/router";
import {DATA, INIT_FLAG, LOGIN_DATA, USER_TOKEN} from "../../../data/login/localStorage.namespace";
import {LocalStorageService} from '../../../utils/localStorage.service';
import {LoginService} from '../../../services/login.service';
@Component({
selector: 'app-error401',
standalone: true,
imports: [
NzResultModule,
NzButtonComponent
],
templateUrl: './error401.component.html',
styleUrl: './error401.component.less'
})
export class Error401Component {
constructor(
private storage: LocalStorageService,
private message: NzMessageService,
private router: Router, // 路由
private login: LoginService
) {}
// 退出登录
public loginOut(): void {
this.login.loginOut( (data: any) => {
if (data['return_code'] === '000000') {
this.storage.removeItem(LOGIN_DATA);
this.storage.removeItem(DATA);
this.storage.removeItem(USER_TOKEN);
this.storage.removeItem(INIT_FLAG);
this.router.navigate(['/login']).then(r => console.log(r));
} else {
this.message.error(data['return_msg']);
}
});
}
}

@ -0,0 +1,5 @@
<nz-result nzStatus="403" nzTitle="403" nzSubTitle="抱歉,您无权访问此页面。">
<div nz-result-extra>
<button nz-button nzType="primary" (click)="getHome()">返回首页</button>
</div>
</nz-result>

@ -0,0 +1,29 @@
import { Component } from '@angular/core';
import {NzResultModule} from "ng-zorro-antd/result";
import {NzButtonComponent} from "ng-zorro-antd/button";
import {Router} from "@angular/router";
@Component({
selector: 'app-error403',
standalone: true,
imports: [
NzResultModule,
NzButtonComponent
],
templateUrl: './error403.component.html',
styleUrl: './error403.component.less'
})
export class Error403Component {
constructor(
private router: Router, // 路由
) {}
// 退出登录
public getHome(): void {
this.router.navigateByUrl('/admin/index').then(r => console.log("返回内容" ,r));
}
}

@ -0,0 +1,5 @@
<nz-result nzStatus="404" nzTitle="404" nzSubTitle="很抱歉,您访问的页面不存在。">
<div nz-result-extra>
<button nz-button nzType="primary" (click)="getHome()">返回首页</button>
</div>
</nz-result>

@ -0,0 +1,27 @@
import { Component } from '@angular/core';
import {Router} from "@angular/router";
import {NzResultModule} from "ng-zorro-antd/result";
import {NzButtonComponent} from "ng-zorro-antd/button";
@Component({
selector: 'app-error404',
standalone: true,
imports: [
NzResultModule,
NzButtonComponent
],
templateUrl: './error404.component.html',
styleUrl: './error404.component.less'
})
export class Error404Component {
constructor(
private router: Router, // 路由
) {}
// 退出登录
public getHome(): void {
this.router.navigateByUrl('/admin/index').then(r => console.log("返回内容" ,r));
}
}

@ -0,0 +1,5 @@
<nz-result nzStatus="500" nzTitle="500" nzSubTitle="很抱歉,服务器上出现错误。">
<div nz-result-extra>
<button nz-button nzType="primary" (click)="getHome()">返回首页</button>
</div>
</nz-result>

@ -0,0 +1,27 @@
import { Component } from '@angular/core';
import {NzResultModule} from "ng-zorro-antd/result";
import {NzButtonComponent} from "ng-zorro-antd/button";
import {Router} from "@angular/router";
@Component({
selector: 'app-error500',
standalone: true,
imports: [
NzResultModule,
NzButtonComponent
],
templateUrl: './error500.component.html',
styleUrl: './error500.component.less'
})
export class Error500Component {
constructor(
private router: Router, // 路由
) {}
// 退出登录
public getHome(): void {
this.router.navigateByUrl('/admin/index').then(r => console.log("返回内容" ,r));
}
}

@ -0,0 +1,12 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
@NgModule({
declarations: [],
imports: [
CommonModule
]
})
export class IndexModule { }

@ -0,0 +1,6 @@
import { Routes } from '@angular/router';
import {IndexComponent} from "./index/index.component";
export const INDEX_ROUTES: Routes = [
{ path: '', component: IndexComponent}
];

@ -0,0 +1,13 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-index',
imports: [],
templateUrl: './index.component.html',
standalone: true,
styleUrl: './index.component.scss'
})
export class IndexComponent {
isCollapsed = false;
}

@ -0,0 +1,10 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
@NgModule({
declarations: [],
imports: [
CommonModule,
]
})
export class LoginModule { }

@ -0,0 +1,6 @@
import { Routes } from '@angular/router';
import {LoginComponent} from "./login/login.component";
export const LOGIN_ROUTES: Routes = [
{ path: '', component: LoginComponent}
];

@ -0,0 +1,78 @@
<div class="bg">
<!-- 登录框-->
<div class="login-box">
<!-- 广告位-->
<div class="login-box-size box-left">
<nz-carousel [nzAutoPlay] = true [nzDots] = false [nzEffect]="'effect'">
<div nz-carousel-content *ngFor="let index of backgroundImage">
<img ngSrc="{{index}}" alt="" priority={true} fill="">
</div>
</nz-carousel>
</div>
<!-- 登录-->
<div class="login-box-size box-center">
<h1>超冠体育 后台登录</h1>
<div>
<form nz-form [formGroup]="validateForm" class="login-form" (ngSubmit)="submitForm()">
<nz-form-item>
<nz-form-control nzErrorTip="请输入您的用户名!">
<nz-input-group nzPrefixIcon="user">
<input type="text" nz-input formControlName="loginName" nzSize="large" placeholder="用户名" />
</nz-input-group>
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-control nzErrorTip="请输入您的密码!">
<nz-input-group nzPrefixIcon="lock" [nzSuffix]="suffixTemplate">
<input
[type]="passwordVisible ? 'text' : 'password'"
nz-input
nzSize="large"
placeholder="密码"
formControlName="password"
/>
</nz-input-group>
<ng-template #suffixTemplate>
<span
nz-icon
[nzType]="passwordVisible ? 'eye-invisible' : 'eye'"
(click)="passwordVisible = !passwordVisible"
></span>
</ng-template>
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-control nzErrorTip="请滑动验证!">
<nz-input-group >
<nz-row>
<nz-col nzSpan="18">
<nz-slider formControlName="slider" [nzDisabled]="false"></nz-slider>
</nz-col>
<div nz-col nzSpan="6">
请滑动至
<span class="take_care">{{randomNumber}}</span>
</div>
</nz-row>
</nz-input-group>
</nz-form-control>
</nz-form-item>
<div nz-row class="login-form-margin">
<div nz-col [nzSpan]="24">
<label nz-checkbox formControlName="remember">
<span>记住密码</span>
</label>
</div>
</div>
<button nz-button nzSize="large" class="login-form-button login-form-margin" [nzType]="'primary'">登录</button>
</form>
</div>
</div>
</div>
<!-- 底部 -->
<div class="footer">
渝ICP备19012384号-1
</div>
</div>

@ -0,0 +1,80 @@
.bg {
//背景图
width: 100%;
height: 100%;
background: linear-gradient(126deg, #0f75ff, #2cd8d4);
overflow: hidden;
//登录框
.login-box {
width: 1000px;
height: 660px;
background: white;
margin: 200px auto 0;
border-radius: 10px;
//盒子大小
.login-box-size {
width: 50%;
height: 660px;
float: left;
border-radius: 10px;
//盒子中图片
[nz-carousel-content] {
text-align: center;
height: 660px;
color: #fff;
overflow: hidden;
}
h1 {
margin-top: 70px;
color: #0f75ff;
font-weight: bold;
letter-spacing: 4px;
}
}
.box-left {
background: #f2f5fa;
}
.box-center {
text-align: center;
}
}
.footer {
text-align: center;
width: 100%;
height: 50px;
line-height: 50px;
position: fixed;
bottom: 0;
background: white;
}
}
.login-form {
margin-top: 50px;
margin-left: 40px;
max-width: 400px;
.take_care {
color: red;
font-size: 20px;
}
input {
height: 32px;
}
nz-form-control {
margin-top: 10px;
}
}
.login-form-margin {
margin-bottom: 16px;
float: right;
}
.login-form-button {
margin-top: 20px;
width: 100%;
}
i {
cursor: pointer;
}

@ -0,0 +1,113 @@
import {Component, inject} from '@angular/core';
import {NzCarouselComponent, NzCarouselContentDirective} from "ng-zorro-antd/carousel";
import {NgForOf, NgOptimizedImage} from "@angular/common";
import {NzFormControlComponent, NzFormDirective, NzFormItemComponent} from "ng-zorro-antd/form";
import {
NonNullableFormBuilder,
ReactiveFormsModule,
Validators
} from "@angular/forms";
import {NzInputDirective, NzInputGroupComponent} from "ng-zorro-antd/input";
import {NzColDirective, NzRowDirective} from "ng-zorro-antd/grid";
import {NzButtonComponent} from "ng-zorro-antd/button";
import {NzCheckboxComponent} from "ng-zorro-antd/checkbox";
import {backgroundImage} from "../../../data/login/login.namespace";
import {NzSliderComponent} from "ng-zorro-antd/slider";
import {MathUtils} from "../../../utils/mathUtils.service";
import {NzMessageService} from "ng-zorro-antd/message";
import {DATA, INIT_FLAG, LOGIN_DATA, USER_TOKEN} from "../../../data/login/localStorage.namespace";
import {Router} from "@angular/router";
import { LocalStorageService} from "../../../utils/localStorage.service";
import {NzIconDirective} from "ng-zorro-antd/icon";
import {LoginService} from '../../../services/login.service';
@Component({
selector: 'app-login',
standalone: true,
templateUrl: './login.component.html',
host: {ngSkipHydration: 'true'},
imports: [
NzCarouselComponent,
NzCarouselContentDirective,
NgOptimizedImage,
NzFormDirective,
ReactiveFormsModule,
NzFormItemComponent,
NzFormControlComponent,
NzInputGroupComponent,
NzInputDirective,
NzIconDirective,
NzRowDirective,
NzColDirective,
NzSliderComponent,
NzCheckboxComponent,
NzButtonComponent,
NgForOf
],
styleUrl: './login.component.less'
})
export class LoginComponent {
private fb = inject(NonNullableFormBuilder);
private login = inject(LoginService);
constructor(
private message: NzMessageService ,
private storage: LocalStorageService ,
private router: Router, // 路由
) {}
validateForm = this.fb.group({
loginName: this.fb.control('', [Validators.required]),
password: this.fb.control('', [Validators.required]),
slider: [0, [Validators.required]],
remember: this.fb.control(true)
});
// 背景图片
backgroundImage = backgroundImage;
passwordVisible = false;
// 验证随机数
randomNumber = MathUtils.generateRandomNumber(100);
// 表单
submitForm(): void {
if (this.validateForm.valid) {
if (Number(this.validateForm.value.slider) !== this.randomNumber) {
this.message.warning('验证不通过!')
this.randomNumber = MathUtils.generateRandomNumber(100)
return;
}
// 发起登录
this.login.userLogin(this.validateForm.value , (data: any) => {
if (data['return_code'] === '000000') {
this.storage.setItem(LOGIN_DATA, this.validateForm.value);
this.storage.setItem(DATA, data['return_data']['object']);
this.storage.setItem(USER_TOKEN, data['return_data']['token']);
this.storage.setItem(INIT_FLAG, true);
this.router.navigateByUrl('/admin/index');
} else {
this.message.error(data['return_msg']);
}
});
this.randomNumber = MathUtils.generateRandomNumber(100)
console.log('submit', this.validateForm.value);
} else {
Object.values(this.validateForm.controls).forEach(control => {
if (control.invalid) {
control.markAsDirty();
control.updateValueAndValidity({onlySelf: true});
}
});
}
}
}

@ -0,0 +1,30 @@
<form nz-form [formGroup]="validateForm" class="ant-advanced-search-form">
<div nz-row [nzGutter]="24">
@for (control of controlArray; track control) {
<div nz-col [nzSpan]="8" [hidden]="!control.show">
<nz-form-item>
<nz-form-label [nzFor]="'field' + control.index">Field {{ control.index }}</nz-form-label>
<nz-form-control>
<input
nz-input
placeholder="placeholder"
[formControlName]="'field' + control.index"
[attr.id]="'field' + control.index"
/>
</nz-form-control>
</nz-form-item>
</div>
}
</div>
<div nz-row>
<div nz-col [nzSpan]="24" class="search-area">
<button nz-button [nzType]="'primary'">Search</button>
<button nz-button (click)="resetForm()">Clear</button>
<a class="collapse" (click)="toggleCollapse()">
Collapse
<nz-icon [nzType]="isCollapse ? 'down' : 'up'" />
</a>
</div>
</div>
</form>
<div class="search-result-list">Search Result List</div>

@ -0,0 +1,33 @@
.ant-advanced-search-form {
padding: 24px;
background: #fbfbfb;
border: 1px solid #d9d9d9;
border-radius: 6px;
}
.search-result-list {
margin-top: 16px;
border: 1px dashed #e9e9e9;
border-radius: 6px;
background-color: #fafafa;
min-height: 200px;
text-align: center;
padding-top: 80px;
}
[nz-form-label] {
overflow: visible;
}
button {
margin-left: 8px;
}
.collapse {
margin-left: 8px;
font-size: 12px;
}
.search-area {
text-align: right;
}

@ -0,0 +1,35 @@
import {Component, inject} from '@angular/core';
import {FormControl, FormRecord, NonNullableFormBuilder, ReactiveFormsModule, Validators} from '@angular/forms';
import {NzColDirective, NzRowDirective} from 'ng-zorro-antd/grid';
import {NzInputDirective} from 'ng-zorro-antd/input';
import {NzFormDirective} from 'ng-zorro-antd/form';
import {NzButtonComponent} from 'ng-zorro-antd/button';
import {NzIconDirective} from 'ng-zorro-antd/icon';
@Component({
selector: 'app-student-item',
imports: [
ReactiveFormsModule,
NzRowDirective,
NzColDirective,
NzInputDirective,
NzFormDirective,
NzButtonComponent
],
templateUrl: './student-item.component.html',
standalone: true,
styleUrl: './student-item.component.scss'
})
export class StudentItemComponent {
private fb = inject(NonNullableFormBuilder);
validateForm = this.fb.group({
name: this.fb.control('', [Validators.required]),
});
constructor() { }
resetForm(): void {
this.validateForm.reset();
}
}

@ -0,0 +1,12 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
@NgModule({
declarations: [],
imports: [
CommonModule
]
})
export class StudentModule { }

@ -0,0 +1,7 @@
import { Routes } from '@angular/router';
import {StudentItemComponent} from './student-item/student-item.component';
export const STUDENT_ROUTES: Routes = [
{ path: 'student-item', component: StudentItemComponent},
];

@ -0,0 +1,102 @@
<nz-list nzGrid>
<div nz-row [nzGutter]="16">
<div nz-col [nzSpan]="12">
<nz-list-item>
<nz-card [nzTitle]="'系统菜单'">
<nz-spin [nzSpinning]="!treeView"></nz-spin>
<nz-tree
*ngIf="menuTree.length > 0 && treeView"
[nzData]="menuTree"
[nzShowLine]="true"
[nzExpandAll]="true"
(nzClick)="clickNode($event)"
[nzMultiple]="false"
></nz-tree>
</nz-card>
</nz-list-item>
</div>
<div nz-col [nzSpan]="12">
<nz-list-item>
<nz-card [nzTitle]="'菜单详情'">
<nz-descriptions [nzExtra]="extraTemplate" nzBordered>
<nz-descriptions-item [nzSpan]="3" nzTitle="父级菜单">{{menuDetail?.parentMenu?menuDetail.parentMenu.menuName:'无'}}</nz-descriptions-item>
<nz-descriptions-item [nzSpan]="3" nzTitle="菜单名称">{{menuDetail.menuName?menuDetail.menuName:'无'}}</nz-descriptions-item>
<nz-descriptions-item [nzSpan]="3" nzTitle="菜单类型">{{menuDetail.menuType == 1 ? '菜单' : '按钮'}}</nz-descriptions-item>
<nz-descriptions-item [nzSpan]="3" nzTitle="菜单URL">{{menuDetail.menuUrl?menuDetail.menuUrl:'无'}}</nz-descriptions-item>
<nz-descriptions-item [nzSpan]="3" nzTitle="菜单图标">{{menuDetail.menuUrlImg?menuDetail.menuUrlImg:'无'}}</nz-descriptions-item>
<nz-descriptions-item [nzSpan]="3" nzTitle="菜单描述">{{menuDetail.menuDesc?menuDetail.menuDesc:'无'}}</nz-descriptions-item>
<ng-template #extraTemplate>
<div nz-row [nzGutter]="12">
<div nz-col nzSpan="8"><button nz-button nzType="primary" (click)="openEditMenu(true)">添加</button></div>
<div nz-col nzSpan="8"><button nz-button nzType="primary" (click)="openEditMenu(false)">修改</button></div>
<div nz-col nzSpan="8"><button nz-button nzType="primary" (click)="showDelMenu()" nzDanger>删除</button></div>
</div>
</ng-template>
</nz-descriptions>
</nz-card>
</nz-list-item>
</div>
</div>
</nz-list>
<nz-modal [(nzVisible)]="isVisible" nzTitle="编辑菜单" (nzOnCancel)="isVisible = false" (nzOnOk)="handleOk()">
<ng-container *nzModalContent>
<form nz-form [formGroup]="validateForm">
<nz-form-item>
<nz-form-label [nzSm]="6" [nzXs]="24" nzFor="menuPSid">父级菜单</nz-form-label>
<nz-form-control [nzSm]="14" [nzXs]="24">
<nz-tree-select
formControlName="menuPSid"
[nzNodes]="menuTree"
nzShowSearch
></nz-tree-select>
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label [nzSm]="6" [nzXs]="24" nzRequired>菜单名称</nz-form-label>
<nz-form-control [nzSm]="14" [nzXs]="24" nzErrorTip="菜单名称填写有误!">
<input nz-input formControlName="menuName"/>
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label [nzSm]="6" [nzXs]="24" nzRequired>菜单类型</nz-form-label>
<nz-form-control [nzSm]="14" [nzXs]="24" nzErrorTip="菜单类型选择有误!">
<nz-select formControlName="menuType" nzShowSearch>
<nz-option *ngFor="let item of menuTypeArray" nzValue="{{item.codeValue}}" nzLabel="{{item.codeName}}"></nz-option>
</nz-select>
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label [nzSm]="6" [nzXs]="24" nzRequired>菜单URL</nz-form-label>
<nz-form-control [nzSm]="14" [nzXs]="24" nzErrorTip="菜单URL填写有误!">
<input nz-input formControlName="menuUrl"/>
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label [nzSm]="6" [nzXs]="24" nzRequired>菜单顺序</nz-form-label>
<nz-form-control [nzSm]="14" [nzXs]="24" nzErrorTip="菜单顺序填写有误!">
<nz-input-number [nzMin]="1" [nzMax]="999" [nzStep]="1" formControlName="menuSort"></nz-input-number>
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label [nzSm]="6" [nzXs]="24">图标URL</nz-form-label>
<nz-form-control [nzSm]="14" [nzXs]="24">
<input nz-input formControlName="menuUrlImg"/>
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label [nzSm]="6" [nzXs]="24">菜单描述</nz-form-label>
<nz-form-control [nzSm]="14" [nzXs]="24">
<input nz-input formControlName="menuDesc"/>
</nz-form-control>
</nz-form-item>
</form>
</ng-container>
</nz-modal>

@ -0,0 +1,206 @@
import {Component, inject} from '@angular/core';
import {NzListModule} from 'ng-zorro-antd/list';
import {NzGridModule} from 'ng-zorro-antd/grid';
import {NzCardModule} from 'ng-zorro-antd/card';
import {DictionaryService} from '../../../services/commons/dictionary.service';
import {MenuService} from '../../../services/system/menu.service';
import {NzSpinComponent} from 'ng-zorro-antd/spin';
import {NzTreeComponent} from 'ng-zorro-antd/tree';
import {NgForOf, NgIf} from '@angular/common';
import {NzDescriptionsComponent, NzDescriptionsItemComponent} from 'ng-zorro-antd/descriptions';
import {NzButtonComponent} from 'ng-zorro-antd/button';
import {NonNullableFormBuilder, ReactiveFormsModule, Validators} from '@angular/forms';
import {NzModalComponent, NzModalContentDirective, NzModalModule, NzModalService} from 'ng-zorro-antd/modal';
import {NzFormControlComponent, NzFormDirective, NzFormLabelComponent, NzFormModule} from 'ng-zorro-antd/form';
import {NzInputDirective} from 'ng-zorro-antd/input';
import {NzTreeSelectComponent} from 'ng-zorro-antd/tree-select';
import {NzOptionComponent, NzSelectComponent} from 'ng-zorro-antd/select';
import {NzInputNumberComponent} from 'ng-zorro-antd/input-number';
import {NzMessageService} from 'ng-zorro-antd/message';
import {NzNotificationService} from 'ng-zorro-antd/notification';
@Component({
selector: 'app-menu',
imports: [
NzCardModule,
NzGridModule,
NzFormModule,
NzModalModule,
NzListModule,
NzSpinComponent,
NzTreeComponent,
NgIf,
NzDescriptionsComponent,
NzDescriptionsItemComponent, NzButtonComponent, NzModalComponent, NzModalContentDirective, NzFormDirective, ReactiveFormsModule, NzInputDirective, NzFormControlComponent, NzFormLabelComponent, NzTreeSelectComponent, NzSelectComponent, NzOptionComponent, NzInputNumberComponent, NgForOf],
templateUrl: './menu.component.html',
standalone: true,
styleUrl: './menu.component.scss'
})
export class MenuComponent {
private fb = inject(NonNullableFormBuilder);
private readonly notification = inject(NzNotificationService);
// 菜单类型
menuTypeArray: any = [];
// 菜单数据
menuData: any = [];
// 菜单树形数据
menuTree: any[] = [];
// 菜单详情
menuDetail: any = {};
// 编辑菜单弹出框
isVisible = false;
// 树视图展示状态
treeView= false;
constructor(
private dictionary: DictionaryService,
private modal: NzModalService,
private message: NzMessageService ,
private menuService: MenuService
) {
// 获取菜单类型
this.dictionary.getDictionary('MENU_TYPE ', (data: any) => {
this.menuTypeArray = data['return_data'];
});
this.queryMenuTree();
}
validateForm = this.fb.group({
menuName: this.fb.control('', [Validators.required]),
menuType: this.fb.control('', [Validators.required]),
menuUrl: this.fb.control('', [Validators.required]),
menuSort: this.fb.control('', [Validators.required]),
menuPSid: this.fb.control(null),
menuUrlImg: this.fb.control(''),
menuDesc: this.fb.control(''),
id: this.fb.control(null)
});
// 获取菜单树数据
queryMenuTree() {
this.menuService.queryMenuTree((data: any) => {
this.menuTree = data['return_data'];
this.treeView = true;
this.queryMenuData();
});
}
/**
*
*/
queryMenuData() {
this.menuService.queryMenuList((data: any) => {
this.menuData = data['return_data'];
});
}
/**
*
* @param nodeData
*/
clickNode(nodeData: any) {
const menuId = nodeData['keys'][0];
if (menuId != null) {
const menuObj = this.menuData.find((o: any) => o.id == menuId);
if (menuObj != null && menuObj.menuPSid != null) {
// 获取父级菜单
menuObj['parentMenu'] = this.menuData.find((o: any) => o.id == menuObj.menuPSid);
}
this.menuDetail = menuObj;
}
}
// 编辑菜单
openEditMenu(isEdit: boolean): void {
if (isEdit) {
this.validateForm.reset();
const menu:{} = {
menuPSid: ""+this.menuDetail.id,
menuType: '1'
};
this.validateForm.patchValue(menu);
} else {
// 判断是否点击需要修改菜单
if ( this.menuDetail?.id == null) {
this.notification.create(
'warning',
'修改提示',
'请点击需要修改的菜单!',
);
return;
}
this.menuDetail.menuPSid = String(this.menuDetail.menuPSid);
this.menuDetail.menuType = String(this.menuDetail.menuType);
this.validateForm.patchValue(this.menuDetail);
}
this.isVisible = true;
}
// 提交菜单
handleOk(): void {
if (this.validateForm.valid) {
this.treeView = false;
console.log(this.validateForm.controls['menuPSid'].value);
if (this.validateForm.controls['menuPSid'].value == 'undefined') {
this.validateForm.controls['menuPSid'].setValue(null);
}
console.log(this.validateForm.value);
this.menuService.editMenu(this.validateForm.value, (data: any) => {
if (data['return_code'] == '000000') {
// 刷新数据
this.queryMenuTree();
this.message.create('success', '操作成功');
// 关闭弹窗
this.isVisible = false;
} else {
this.message.create('error', data['return_msg']);
}
});
} else {
Object.values(this.validateForm.controls).forEach(control => {
if (control.invalid) {
control.markAsDirty();
control.updateValueAndValidity({ onlySelf: true });
}
});
}
}
/**
*
*/
showDelMenu() {
this.modal.confirm({
nzTitle: '提示',
nzContent: '确实删除数据?',
nzOnOk: () => this.delMenu()
});
}
/**
*
*/
delMenu() {
const param = {
menuId: this.menuDetail.id
}
this.treeView = false;
this.menuService.delMenu(param, (data: any) => {
if (data['return_code'] == '000000') {
// 刷新数据
this.queryMenuTree();
this.message.create('success', '操作成功');
} else {
this.treeView = true;
this.message.create('error', data['return_msg']);
}
});
}
}

@ -0,0 +1,12 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
@NgModule({
declarations: [],
imports: [
CommonModule
]
})
export class SystemModule { }

@ -0,0 +1,7 @@
import { Routes } from '@angular/router';
import {MenuComponent} from './menu/menu.component';
export const SYSTEM_ROUTES: Routes = [
{ path: 'menu', component: MenuComponent},
];

@ -0,0 +1,16 @@
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'dataUtils',
standalone: true
})
export class DataUtilsPipe implements PipeTransform {
transform(data: any, length: number): any {
if (data.length == 0) {
return [];
}
return data.slice(0 , length);
}
}

@ -0,0 +1,39 @@
import { Pipe, PipeTransform } from '@angular/core';
import {dictionaryData, Dictionary} from "../../data/common/dictionary.namespace";
@Pipe({
name: 'dictionary',
standalone: true
})
export class DictionaryPipe implements PipeTransform {
data: Dictionary[] = dictionaryData;
/**
*
* @param codeType
*/
getDictionaryList(codeType: string): any {
return this.data.filter(o => o.codeType == codeType);
}
/**
*
* @param codeType
* @param codeValue
*/
getDictionary(codeType: string, codeValue: string): any {
// 读取缓存数据
return this.data.find(o => o.codeType == codeType && o.codeValue == codeValue);
}
transform(codeValue: string,codeType: string): string {
const obj = this.getDictionary(codeType, codeValue);
if (obj == null) {
return '未知';
} else {
return obj.codeName;
}
}
}

@ -0,0 +1,115 @@
import { Injectable } from '@angular/core';
import {HttpClient} from "@angular/common/http";
import {environment} from "../../../environments/environment";
import {ObjectData} from "../../utils/objectData.service";
@Injectable({
providedIn: 'root'
})
export class SysAccountService {
constructor(private http: HttpClient) { }
/**
*
*
* @param params
* @param callBack
*/
public editUser(params: any, callBack:any) {
params.time = new Date().getTime();
this.http.post(environment.baseUrl + 'secUser/editUser', params).subscribe(data => {
callBack(data);
});
}
/**
*
* @param params
* @param callBack
*/
public delete(params: any, callBack:any) {
params.time = new Date().getTime();
this.http.post(environment.baseUrl + 'secUser/delete', params).subscribe(data => {
callBack(data);
});
}
/**
*
* @param params
* @param callBack
*/
public restore(params: any, callBack:any) {
params.time = new Date().getTime();
this.http.post(environment.baseUrl + 'secUser/restore', params).subscribe(data => {
callBack(data);
});
}
/**
*
* @param params
* @param callBack
*/
public disable(params: any, callBack:any) {
params.time = new Date().getTime();
this.http.post(environment.baseUrl + 'secUser/disable', params).subscribe(data => {
callBack(data);
});
}
/**
*
* @param userId
* @param callBack
*/
public resetPwd(param: any, callBack:any) {
param.time = new Date().getTime();
this.http.post(environment.baseUrl + 'secUser/resetPwd', param).subscribe(data => {
callBack(data);
});
}
/**
*
* @param userId
* @param callBack
*/
public queryDetail(userId: number, callBack:any) {
let params = {
userId: userId,
time: new Date().getTime()
};
this.http.get(environment.baseUrl + 'secUser/queryDetail?'+ObjectData.objectByString(params)).subscribe(data => {
callBack(data);
});
}
/**
*
* @param params
* @param callBack
*/
public queryList(params: any, callBack:any) {
params.time = new Date().getTime();
this.http.get(environment.baseUrl + 'secUser/queryList?'+ObjectData.objectByString(params)).subscribe(data => {
callBack(data);
});
}
/**
*
* @param params
* @param callBack
*/
public queryLoginLogList(params: any, callBack:any) {
params.time = new Date().getTime();
this.http.get(environment.baseUrl + 'secUser/queryLoginLogList?'+ObjectData.objectByString(params)).subscribe(data => {
callBack(data);
});
}
}

@ -0,0 +1,25 @@
import { Injectable } from '@angular/core';
import {HttpClient} from '@angular/common/http';
import {ObjectData} from '../../utils/objectData.service';
import {environment} from '../../../environments/environment';
@Injectable({
providedIn: 'root'
})
export class DictionaryService {
constructor(private http: HttpClient) {}
/**
*
*
* @param codeType
* @param callback
*/
public getDictionary(codeType: string , callback: any): any {
this.http.get(environment.baseUrl + '/common/queryDictionary?codeType=' + codeType).subscribe( data => {
callback(data);
});
}
}

@ -0,0 +1,14 @@
import { Injectable } from '@angular/core';
import {NzIconService} from "ng-zorro-antd/icon";
@Injectable({
providedIn: 'root'
})
export class IconService {
constructor(private iconService: NzIconService) {
this.iconService.fetchFromIconfont({
scriptUrl: 'https://at.alicdn.com/t/c/font_4647823_ys5j7ogeas.js'
});
}
}

@ -0,0 +1,52 @@
import { Injectable } from '@angular/core';
import {environment} from '../../environments/environment';
import {HttpClient} from '@angular/common/http';
@Injectable({
providedIn: 'root'
})
export class LoginService {
constructor(private http: HttpClient) {}
/**
* @Author Sum1Dream
* @Name userLogin
* @Description // 登录
* @Date 15:52 2024/4/18
* @Param params
* @Param callBack
*/
public userLogin(params: object, callBack:any) {
this.http.post(environment.baseUrl + 'login/login', params).subscribe(data => {
callBack(data);
});
}
/**
* @Author Sum1Dream
* @Name loginOut
* @Description // 退出登录
* @Date 15:52 2024/4/18
* @Param callBack
*/
public loginOut(callBack:any) {
this.http.post(environment.baseUrl + 'login/loginOut', null).subscribe(data => {
callBack(data);
});
}
/**
* @Author Sum1Dream
* @Name sendUserPass
* @Description // 修改用户密码
* @Date 15:52 2024/4/18
* @Param params
* @Param callBack
*/
public sendUserPass(params: object, callBack:any) {
this.http.post(environment.baseUrl + 'secUser/sendUserPass', params).subscribe(data => {
callBack(data);
});
}
}

@ -0,0 +1,87 @@
import { Injectable } from '@angular/core';
import {HttpClient} from '@angular/common/http';
import {environment} from '../../../environments/environment';
import {ObjectData} from '../../utils/objectData.service';
@Injectable({
providedIn: 'root'
})
export class MenuService {
constructor(private http: HttpClient) {}
/**
*
* @param roleId
* @param callBack
*/
public queryMenuTree(callBack:any) {
this.http.post(environment.baseUrl + 'secMenu/queryMenuTree?dt='+new Date().getTime(), null).subscribe(data => {
callBack(data);
});
}
/**
*
* @param roleId
* @param callBack
*/
public queryMenuList(callBack:any) {
let params = {
time: new Date().getTime()
};
this.http.post(environment.baseUrl + 'secMenu/queryMenuList?' + ObjectData.objectByString(params), null).subscribe(data => {
callBack(data);
});
}
/**
*
* @param params
* @param callBack
*/
public assignMenu(params: any, callBack:any) {
params.time = new Date().getTime();
this.http.post(environment.baseUrl + 'secMenu/assignMenu', params).subscribe(data => {
callBack(data);
});
}
/**
*
* @param params
* @param callBack
*/
public delMenu(params: any, callBack:any) {
params.time = new Date().getTime();
this.http.post(environment.baseUrl + 'secMenu/delMenu', params).subscribe(data => {
callBack(data);
});
}
/**
*
* @param params
* @param callBack
*/
public editMenu(params: any, callBack:any) {
params.time = new Date().getTime();
this.http.post(environment.baseUrl + 'secMenu/editMenu', params).subscribe(data => {
callBack(data);
});
}
/**
*
* @param roleId
* @param callBack
*/
public queryRoleMenuArray(roleId: number, callBack:any) {
const param = {
roleId: roleId,
t: new Date().getTime() // 时间戳避免缓存
}
this.http.get(environment.baseUrl + 'secMenu/queryRoleMenuArray?' + ObjectData.objectByString(param)).subscribe(data => {
callBack(data);
});
}
}

@ -0,0 +1,73 @@
import { Injectable } from '@angular/core';
import {HttpClient} from "@angular/common/http";
import {environment} from "../../../environments/environment";
import {ObjectData} from "../../utils/objectData.service";
@Injectable({
providedIn: 'root'
})
export class RoleService {
constructor(private http: HttpClient) { }
/**
*
* @param params
* @param callBack
*/
public editRole(params: any, callBack:any) {
params.time = new Date().getTime();
this.http.post(environment.baseUrl + 'secRole/editRole', params).subscribe(data => {
callBack(data);
});
}
/**
*
* @param params
* @param callBack
*/
public delRole(params: any, callBack:any) {
params.time = new Date().getTime();
this.http.post(environment.baseUrl + 'secRole/delRole', params).subscribe(data => {
callBack(data);
});
}
/**
*
* @param roleId
* @param callBack
*/
public queryDetail(roleId: number, callBack:any) {
this.http.get(environment.baseUrl + 'secRole/queryDetail?roleId='+roleId).subscribe(data => {
callBack(data);
});
}
/**
*
* @param params
* @param callBack
*/
public queryList(params: any, callBack:any) {
params.time = new Date().getTime();
this.http.get(environment.baseUrl + 'secRole/queryList?'+ObjectData.objectByString(params)).subscribe(data => {
callBack(data);
});
}
/**
*
* @param callBack
*/
public queryAllRole(callBack:any) {
let params = {
time: new Date().getTime()
};
this.http.get(environment.baseUrl + 'secRole/queryAllRole?'+ObjectData.objectByString(params)).subscribe(data => {
callBack(data);
});
}
}

@ -0,0 +1,35 @@
import {HttpRequest, HttpHandlerFn, HttpEvent, HttpErrorResponse} from '@angular/common/http';
import {catchError, Observable, throwError} from 'rxjs';
import {Router} from '@angular/router';
import {inject} from '@angular/core';
import {USER_TOKEN} from '../data/login/localStorage.namespace';
import {LocalStorageService} from './localStorage.service';
export function InterceptorService(req: HttpRequest<unknown>, next: HttpHandlerFn): Observable<HttpEvent<unknown>> {
const router = inject(Router);
const storage = inject(LocalStorageService);
const headers = req.headers.set('Authorization', String(storage.getItem(USER_TOKEN)));
const newReq = req.clone({headers});
return next(newReq).pipe(
catchError((error: HttpErrorResponse) => {
// 响应错误处理
if (error.status === 401) {
router.navigate(['/error/401']);
}
if (error.status == 403) {
router.navigate(['/error/403']);
}
if (error.status == 404) {
router.navigate(['/error/404']);
}
if (error.status == 500) {
router.navigate(['/error/500']);
}
return throwError(() => error);
})
);
}

@ -0,0 +1,141 @@
/**
*
*/
export const DateSelectType: any = [
{ value: 1, name: '今日'},
{ value: 2, name: '昨日'},
{ value: 3, name: '本周'},
{ value: 4, name: '上周'},
{ value: 5, name: '本月'},
{ value: 6, name: '上月'},
];
/**
*
*/
export class DateUtils {
/**
*
* @param currentDate
* @param type 1 2 3 4 5 6
*/
public static getDate(currentDate: Date, type: number): any {
const returnObj = {
timeS: 0,
timeE: 0,
}
if (type === 1) {
currentDate.setHours(0);
currentDate.setMinutes(0);
currentDate.setSeconds(0);
returnObj.timeS = currentDate.getTime();
currentDate.setHours(23);
currentDate.setMinutes(59);
currentDate.setSeconds(59);
returnObj.timeE = currentDate.getTime();
} else if (type === 2) {
currentDate.setDate(currentDate.getDate() - 1)
currentDate.setHours(0);
currentDate.setMinutes(0);
currentDate.setSeconds(0);
returnObj.timeS = currentDate.getTime();
currentDate.setHours(23);
currentDate.setMinutes(59);
currentDate.setSeconds(59);
returnObj.timeE = currentDate.getTime();
} else if (type === 3) {
const dayOfWeek = currentDate.getDay();
const dayOffset = dayOfWeek === 0 ? -6 : 1 - dayOfWeek;
const start = new Date(currentDate);
start.setDate(start.getDate() + dayOffset)
start.setHours(0);
start.setMinutes(0);
start.setSeconds(0);
returnObj.timeS = start.getTime();
const end = new Date(currentDate);
end.setDate(end.getDate() + 6)
end.setHours(23);
end.setMinutes(59);
end.setSeconds(59);
returnObj.timeE = end.getTime();
} else if (type === 4) {
const startOfThisWeek = new Date(currentDate.getFullYear(),currentDate.getMonth(),currentDate.getDate() - currentDate.getDay() + 1);
const endOfLastWeek = new Date(startOfThisWeek.getTime() - 1);
const startOfLastWeek = new Date((endOfLastWeek.getTime() + 1) - (7 * 24 * 60 * 60 * 1000));
startOfLastWeek .setHours(0);
startOfLastWeek .setMinutes(0);
startOfLastWeek .setSeconds(0);
returnObj.timeS = startOfLastWeek .getTime();
endOfLastWeek.setHours(23);
endOfLastWeek.setMinutes(59);
endOfLastWeek.setSeconds(59);
returnObj.timeE = endOfLastWeek.getTime();
} else if (type === 5) {
var daysInMonth = new Date(currentDate.getFullYear(), currentDate.getMonth()+1, 0).getDate();
const start = new Date(currentDate);
start.setDate(1)
start.setHours(0);
start.setMinutes(0);
start.setSeconds(0);
returnObj.timeS = start.getTime();
const end = new Date(currentDate);
end.setDate(daysInMonth)
end.setHours(23);
end.setMinutes(59);
end.setSeconds(59);
returnObj.timeE = end.getTime();
} else if (type === 6) {
var daysInMonth = new Date(currentDate.getFullYear(), currentDate.getMonth(), 0).getDate();
const start = new Date(currentDate);
start.setMonth(currentDate.getMonth() - 1);
start.setDate(1)
start.setHours(0);
start.setMinutes(0);
start.setSeconds(0);
returnObj.timeS = start.getTime();
const end = new Date(currentDate);
end.setMonth(currentDate.getMonth() - 1);
end.setDate(daysInMonth)
end.setHours(23);
end.setMinutes(59);
end.setSeconds(59);
returnObj.timeE = end.getTime();
}
return returnObj;
}
/**
*
* @param currentDate
* @param type 1 2:结束时间
*/
public static getDateHMS(currentDate: Date , type: number): any {
if (type == 1) {
currentDate.setHours(0);
currentDate.setMinutes(0);
currentDate.setSeconds(0);
}
if (type == 2) {
currentDate.setHours(23);
currentDate.setMinutes(59);
currentDate.setSeconds(59);
}
return currentDate;
}
}

@ -0,0 +1,28 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router';
import {CanActivate} from "@angular/router";
import {INIT_FLAG} from "../data/login/localStorage.namespace";
import {LocalStorageService} from './localStorage.service';
@Injectable({ providedIn: 'root' })
export class InitGuardService implements CanActivate {
constructor(
private storage: LocalStorageService,
private router: Router,
) { }
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
const init = !!this.storage.getItem(INIT_FLAG);
if (state.url.includes('login') && init) {
this.router.navigateByUrl('/admin/index').then();
return false;
}
if (!state.url.includes('login') && !init) {
this.router.navigateByUrl('/login').then();
return false;
}
return true;
}
}

@ -0,0 +1,41 @@
// storage.service.ts
import {Inject, Injectable, PLATFORM_ID} from '@angular/core';
import {isPlatformBrowser} from '@angular/common';
@Injectable({ providedIn: 'root' })
export class LocalStorageService {
constructor(@Inject(PLATFORM_ID) private platformId: Object) {}
getItem(key: string): string | null {
if (isPlatformBrowser(this.platformId)) {
const data = localStorage.getItem(key);
try {
return data ? JSON.parse(data) : null;
} catch (e) {
console.error('LocalStorage解析异常:', e);
return null;
}
}
return null; // 服务端返回默认值
}
setItem<T>(key: string, value: T): void {
try {
const data = JSON.stringify(value);
localStorage.setItem(key, data);
} catch (e) {
console.error('LocalStorage存储失败:', e);
}
}
removeItem(key: string): void {
localStorage.removeItem(key);
}
clear(): void {
localStorage.clear();
}
}

@ -0,0 +1,13 @@
export class MathUtils{
/**
* @Author Sum1Dream
* @Name
* @Description // 获取1-100随机数
* @Date 11:13 2024/4/9
* @Param number
*/
static generateRandomNumber(number: number): number {
return Math.floor(Math.random() * number) + 1;
}
}

@ -0,0 +1,24 @@
export class ObjectData {
/**
* @Author Sum1Dream
* @Name getWhereCondition
* @Description // 将对象转换成GET请求参数 key1=value1&key2=value2
* @Date 14:02 2024/4/9
* @Param object
*/
static objectByString(object: any): string {
let str = '';
for (const i in object) {
if (object[i] != null && object[i] !== '') {
if (str === '') {
str = i + '=' + object[i];
} else {
str += '&' + i + '=' + object[i];
}
}
}
return str;
}
}

@ -0,0 +1,63 @@
import {ActivatedRouteSnapshot, DetachedRouteHandle, RouteReuseStrategy} from '@angular/router';
export class AiRouteReuseStrategy implements RouteReuseStrategy {
/**
*
**/
public static routeSnapshots: { [key: string]: DetachedRouteHandle } = {};
/**
*
*
**/
shouldDetach(route: ActivatedRouteSnapshot): boolean {
return true;
}
/**
* url为key保存路由key也可以使用其他属性
**/
store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
const url = this.getFullRouteUrl(route);
AiRouteReuseStrategy.routeSnapshots[url] = handle;
}
/**
*
**/
shouldAttach(route: ActivatedRouteSnapshot): boolean {
const url = this.getFullRouteUrl(route);
return !!AiRouteReuseStrategy.routeSnapshots[url];
}
/**
* null
**/
retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle | null {
const url = this.getFullRouteUrl(route);
return route.routeConfig ? AiRouteReuseStrategy.routeSnapshots[url] : null;
}
/**
*
**/
shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
return future.routeConfig === curr.routeConfig &&
JSON.stringify(future.params) === JSON.stringify(curr.params);
}
private getFullRouteUrl(route: ActivatedRouteSnapshot): string {
return this.getFullRouteUrlPaths(route).filter(Boolean).join('/').replace(/\//g, '_');
}
private getFullRouteUrlPaths(route: ActivatedRouteSnapshot): string[] {
const paths = this.getRouteUrlPaths(route);
return route.parent
? [...this.getFullRouteUrlPaths(route.parent), ...paths]
: paths;
}
private getRouteUrlPaths(route: ActivatedRouteSnapshot): string[] {
return route.url.map(urlSegment => urlSegment.path);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 928 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 953 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 836 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 913 KiB

@ -0,0 +1,7 @@
export const environment = {
production: true,
baseUrl: 'https://phgcs.dctpay.com/brest/', // 测试环境服务器地址(请求数据地址)
orderUrl: 'https://phgcs.dctpay.com/order/', // 测试环境服务器地址(请求数据地址)
userUrl: 'https://phgcs.dctpay.com/user/', // 测试环境服务器地址(请求数据地址)
imageUrl: 'https://phg-test.obs.cn-southwest-2.myhuaweicloud.com/',
};

@ -0,0 +1,7 @@
export const environment = {
production: true,
baseUrl: 'https://phg.dctpay.com/brest/', // 测试环境服务器地址(请求数据地址)
orderUrl: 'https://phg.dctpay.com/order/', // 测试环境服务器地址(请求数据地址)
userUrl: 'https://phg.dctpay.com/user/', // 测试环境服务器地址(请求数据地址)
imageUrl: 'https://phg.obs.cn-southwest-2.myhuaweicloud.com/',
};

@ -0,0 +1,19 @@
// This file can be replaced during build by using the `fileReplacements` array.
// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
// The list of file replacements can be found in `angular.json`.
export const environment = {
production: false,
// baseUrl: 'https://phg.dctpay.com/brest/', // 测试环境服务器地址(请求数据地址)
baseUrl: 'http://localhost:9702/brest/', // 测试环境服务器地址(请求数据地址)
};
/*
* For easier debugging in development mode, you can import the following file
* to ignore zone related error stack fram es such as `zone.run`, `zoneDelegate.invokeTask`.
*
* This import should be commented out in production mode because it will have a negative impact
* on performance if an error is thrown.
*/
// import 'zone.js/dist/zone-error'; // Included with Angular CLI.

@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>超冠体育</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<app-root></app-root>
</body>
</html>

@ -0,0 +1,7 @@
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { config } from './app/app.config.server';
const bootstrap = () => bootstrapApplication(AppComponent, config);
export default bootstrap;

@ -0,0 +1,6 @@
import { bootstrapApplication } from '@angular/platform-browser';
import { appConfig } from './app/app.config';
import { AppComponent } from './app/app.component';
bootstrapApplication(AppComponent, appConfig)
.catch((err) => console.error(err));

@ -0,0 +1,66 @@
import {
AngularNodeAppEngine,
createNodeRequestHandler,
isMainModule,
writeResponseToNodeResponse,
} from '@angular/ssr/node';
import express from 'express';
import { dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
const serverDistFolder = dirname(fileURLToPath(import.meta.url));
const browserDistFolder = resolve(serverDistFolder, '../browser');
const app = express();
const angularApp = new AngularNodeAppEngine();
/**
* Example Express Rest API endpoints can be defined here.
* Uncomment and define endpoints as necessary.
*
* Example:
* ```ts
* app.get('/api/**', (req, res) => {
* // Handle API request
* });
* ```
*/
/**
* Serve static files from /browser
*/
app.use(
express.static(browserDistFolder, {
maxAge: '1y',
index: false,
redirect: false,
}),
);
/**
* Handle all other requests by rendering the Angular application.
*/
app.use('/**', (req, res, next) => {
angularApp
.handle(req)
.then((response) =>
response ? writeResponseToNodeResponse(response, res) : next(),
)
.catch(next);
});
/**
* Start the server if this module is the main entry point.
* The server listens on the port defined by the `PORT` environment variable, or defaults to 4000.
*/
if (isMainModule(import.meta.url)) {
const port = process.env['PORT'] || 4000;
app.listen(port, () => {
console.log(`Node Express server listening on http://localhost:${port}`);
});
}
/**
* Request handler used by the Angular CLI (for dev-server and during build) or Firebase Cloud Functions.
*/
export const reqHandler = createNodeRequestHandler(app);

@ -0,0 +1 @@
/* You can add global styles to this file, and also import other style files */

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save