初始化项目

master
胡锐 3 months ago
parent 2f48b1e542
commit e80cd5fe41
  1. 16
      .editorconfig
  2. 8
      .gitignore
  3. 141
      angular.json
  4. 12938
      package-lock.json
  5. 46
      package.json
  6. 56
      server.ts
  7. 1
      src/app/app.component.html
  8. 0
      src/app/app.component.less
  9. 13
      src/app/app.component.ts
  10. 11
      src/app/app.config.server.ts
  11. 31
      src/app/app.config.ts
  12. 42
      src/app/app.routes.ts
  13. 12
      src/app/data/common/button.namespace.ts
  14. 13
      src/app/data/common/dictionary.namespace.ts
  15. 5
      src/app/data/login/localStorage.namespace.ts
  16. 5
      src/app/data/login/login.namespace.ts
  17. 9
      src/app/data/menu/menu.namespace.ts
  18. 25
      src/app/model/discount.interface.ts
  19. 12
      src/app/pages/account/account.module.ts
  20. 8
      src/app/pages/account/account.routes.ts
  21. 136
      src/app/pages/account/sys-account/sys-account.component.html
  22. 15
      src/app/pages/account/sys-account/sys-account.component.less
  23. 322
      src/app/pages/account/sys-account/sys-account.component.ts
  24. 101
      src/app/pages/account/sys-role/sys-role.component.html
  25. 12
      src/app/pages/account/sys-role/sys-role.component.less
  26. 291
      src/app/pages/account/sys-role/sys-role.component.ts
  27. 10
      src/app/pages/body/body.module.ts
  28. 103
      src/app/pages/body/index/index.component.html
  29. 69
      src/app/pages/body/index/index.component.less
  30. 246
      src/app/pages/body/index/index.component.ts
  31. 12
      src/app/pages/body/menu/menu.module.ts
  32. 9
      src/app/pages/body/tab/tab.component.html
  33. 34
      src/app/pages/body/tab/tab.component.less
  34. 116
      src/app/pages/body/tab/tab.component.ts
  35. 176
      src/app/pages/cms/cms-list/cms-list.component.html
  36. 12
      src/app/pages/cms/cms-list/cms-list.component.less
  37. 333
      src/app/pages/cms/cms-list/cms-list.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. 44
      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. 10
      src/app/pages/index/index.module.ts
  53. 6
      src/app/pages/index/index.routes.ts
  54. 5
      src/app/pages/index/index/index.component.html
  55. 0
      src/app/pages/index/index/index.component.less
  56. 21
      src/app/pages/index/index/index.component.ts
  57. 12
      src/app/pages/login/login.module.ts
  58. 6
      src/app/pages/login/login.routes.ts
  59. 65
      src/app/pages/login/login/login.component.html
  60. 78
      src/app/pages/login/login/login.component.less
  61. 126
      src/app/pages/login/login/login.component.ts
  62. 106
      src/app/pages/system/menu/menu.component.html
  63. 6
      src/app/pages/system/menu/menu.component.less
  64. 253
      src/app/pages/system/menu/menu.component.ts
  65. 12
      src/app/pages/system/system.module.ts
  66. 8
      src/app/pages/system/system.routes.ts
  67. 38
      src/app/pipes/common/button.pipe.ts
  68. 19
      src/app/pipes/common/date-minutes-diff.pipe.ts
  69. 39
      src/app/pipes/common/dictionary.pipe.ts
  70. 127
      src/app/services/account/sys-account.service.ts
  71. 72
      src/app/services/cms/cms.service.ts
  72. 104
      src/app/services/common/common.service.ts
  73. 47
      src/app/services/common/validators.service.ts
  74. 38
      src/app/services/login/login.service.ts
  75. 120
      src/app/services/menu/menu.service.ts
  76. 73
      src/app/services/role/role.service.ts
  77. 88
      src/app/utils/Interceptor.service.ts
  78. 139
      src/app/utils/dateUtils.service.ts
  79. 28
      src/app/utils/initGuard.service.ts
  80. 32
      src/app/utils/localStorage.service.ts
  81. 13
      src/app/utils/mathUtils.service.ts
  82. 24
      src/app/utils/objectData.service.ts
  83. 63
      src/app/utils/routeReuseStrategy.ts
  84. 65
      src/app/utils/validators.service.ts
  85. 0
      src/assets/.gitkeep
  86. 13266
      src/assets/data/region.json
  87. BIN
      src/assets/login/1.png
  88. BIN
      src/assets/login/2.png
  89. BIN
      src/assets/login/3.png
  90. BIN
      src/assets/logo.jpg
  91. 5
      src/environments/environment.dev.ts
  92. 5
      src/environments/environment.prod.ts
  93. 18
      src/environments/environment.ts
  94. BIN
      src/favicon.ico
  95. 13
      src/index.html
  96. 7
      src/main.server.ts
  97. 6
      src/main.ts
  98. 15
      src/styles.less
  99. 18
      tsconfig.app.json
  100. 33
      tsconfig.json
  101. Some files were not shown because too many files have changed in this diff Show More

@ -0,0 +1,16 @@
# 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
[*.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,141 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"oa-web": {
"projectType": "application",
"schematics": {
"@schematics/angular:component": {
"style": "less"
}
},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:application",
"options": {
"outputPath": "dist/oa-web",
"index": "src/index.html",
"browser": "src/main.ts",
"polyfills": [
"zone.js"
],
"tsConfig": "tsconfig.app.json",
"inlineStyleLanguage": "less",
"assets": [
"src/favicon.ico",
"src/assets",
{
"glob": "**/*",
"input": "./node_modules/@ant-design/icons-angular/src/inline-svg/",
"output": "/assets/"
}
],
"styles": [
"src/styles.less"
],
"scripts": [],
"server": "src/main.server.ts",
"prerender": true,
"ssr": {
"entry": "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": "oa-web:build:production"
},
"development": {
"buildTarget": "oa-web:build:development"
}
},
"defaultConfiguration": "development"
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"buildTarget": "oa-web:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"polyfills": [
"zone.js",
"zone.js/testing"
],
"tsConfig": "tsconfig.spec.json",
"inlineStyleLanguage": "less",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.less"
],
"scripts": []
}
}
}
}
},
"cli": {
"analytics": "a81abb26-556d-44f1-a3d7-6e52625f6794"
}
}

12938
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -0,0 +1,46 @@
{
"name": "oa-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:hai-oil-web": "node dist/hai-oil-web/server/server.mjs"
},
"private": true,
"dependencies": {
"@angular/animations": "^17.0.0",
"@angular/common": "^17.0.0",
"@angular/compiler": "^17.0.0",
"@angular/core": "^17.0.0",
"@angular/forms": "^17.0.0",
"@angular/platform-browser": "^17.0.0",
"@angular/platform-browser-dynamic": "^17.0.0",
"@angular/platform-server": "^17.0.0",
"@angular/router": "^17.0.0",
"@angular/ssr": "^17.0.7",
"express": "^4.18.2",
"ng-zorro-antd": "^17.1.0",
"qqmap": "^1.0.1",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"zone.js": "~0.14.2"
},
"devDependencies": {
"@angular-devkit/build-angular": "^17.0.7",
"@angular/cli": "^17.0.7",
"@angular/compiler-cli": "^17.0.0",
"@types/express": "^4.17.17",
"@types/jasmine": "~5.1.0",
"@types/node": "^18.18.0",
"jasmine-core": "~5.1.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.2.2"
}
}

@ -0,0 +1,56 @@
import { APP_BASE_HREF } from '@angular/common';
import { CommonEngine } from '@angular/ssr';
import express from 'express';
import { fileURLToPath } from 'node:url';
import { dirname, join, resolve } from 'node:path';
import bootstrap from './src/main.server';
// The Express app is exported so that it can be used by serverless Functions.
export function app(): express.Express {
const server = express();
const serverDistFolder = dirname(fileURLToPath(import.meta.url));
const browserDistFolder = resolve(serverDistFolder, '../browser');
const indexHtml = join(serverDistFolder, 'index.server.html');
const commonEngine = new CommonEngine();
server.set('view engine', 'html');
server.set('views', browserDistFolder);
// Example Express Rest API endpoints
// server.get('/api/**', (req, res) => { });
// Serve static files from /browser
server.get('*.*', express.static(browserDistFolder, {
maxAge: '1y'
}));
// All regular routes use the Angular engine
server.get('*', (req, res, next) => {
const { protocol, originalUrl, baseUrl, headers } = req;
commonEngine
.render({
bootstrap,
documentFilePath: indexHtml,
url: `${protocol}://${headers.host}${originalUrl}`,
publicPath: browserDistFolder,
providers: [{ provide: APP_BASE_HREF, useValue: baseUrl }],
})
.then((html) => res.send(html))
.catch((err) => next(err));
});
return server;
}
function run(): void {
const port = process.env['PORT'] || 4000;
// Start up the Node server
const server = app();
server.listen(port, () => {
console.log(`Node Express server listening on http://localhost:${port}`);
});
}
run();

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

@ -0,0 +1,13 @@
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterOutlet } from '@angular/router';
@Component({
selector: 'app-root',
standalone: true,
imports: [CommonModule, RouterOutlet],
templateUrl: './app.component.html',
styleUrl: './app.component.less'
})
export class AppComponent {
}

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

@ -0,0 +1,31 @@
import { ApplicationConfig, importProvidersFrom } from '@angular/core';
import {provideRouter, RouteReuseStrategy} from '@angular/router';
import { routes } from './app.routes';
import { provideClientHydration } 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 {HTTP_INTERCEPTORS, HttpClientModule} from '@angular/common/http';
import {BrowserAnimationsModule, provideAnimations} from '@angular/platform-browser/animations';
import {InitGuardService} from "./utils/initGuard.service";
import {InterceptorService} from "./utils/Interceptor.service";
import {AiRouteReuseStrategy} from "./utils/routeReuseStrategy";
registerLocaleData(zh);
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes),
provideClientHydration(),
provideNzI18n(zh_CN),
importProvidersFrom(FormsModule),
importProvidersFrom(HttpClientModule),
provideAnimations(),
BrowserAnimationsModule,
InitGuardService, // 校验登录
{ provide: HTTP_INTERCEPTORS, useClass: InterceptorService, multi: true},
/* { provide: RouteReuseStrategy, useClass: AiRouteReuseStrategy }*/
]
};

@ -0,0 +1,42 @@
import { Routes } from '@angular/router';
import {IndexComponent} from "./pages/body/index/index.component";
import {InitGuardService} from "./utils/initGuard.service";
import {Error404Component} from "./pages/error/error404/error404.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: 'account',
loadChildren: () => import('./pages/account/account.routes').then(m => m.ACCOUNT_ROUTES),
canActivate: [InitGuardService]
},
{
path: 'system',
loadChildren: () => import('./pages/system/system.routes').then(m => m.SYSTEM_ROUTES),
canActivate: [InitGuardService]
},
]
},
{
path: 'error',
loadChildren: () => import('./pages/error/error.routes').then(m => m.ERROR_ROUTES),
},
{
path: '**', component: Error404Component
},
];

@ -0,0 +1,12 @@
export interface Button {
menuName: string;
menuType: string;
menuUrl: string;
menuUrlImg: string;
menuPSid: number;
menuSort: string;
menuDesc: string;
}
export let buttonData: Button[] = [];

@ -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,5 @@
export const backgroundImage = [
"./assets/login/1.png",
"./assets/login/2.png",
"./assets/login/3.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,25 @@
export interface DiscountData {
id: number;
name: string;
img: string;
type: number;
condition: string;
price: string;
percentage: string;
effectiveDay: number;
effectiveNum: number;
createTime: string;
updateTime: string;
salesEndTime: string;
}
export interface NodesDiscountGoods {
id: number;
discountId: number;
specsId: string;
specsName: string;
createTime: string;
goodsName: string;
SpecsName: string;
salePrice: string;
}

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

@ -0,0 +1,8 @@
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,136 @@
<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="30px">账户状态</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.status | dictionary: 'SEC_USER_STATUS'}}</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,322 @@
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/role/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: [''],
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,101 @@
<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"
[nzCheckStrictly]="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,291 @@
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/role/role.service";
import {MenuService} from "../../../services/menu/menu.service";
@Component({
selector: 'app-sys-role',
standalone: true,
imports: [
NzTableComponent,
NzTheadComponent,
NzDividerComponent,
NzTbodyComponent,
NzFormDirective,
NzRowDirective,
ReactiveFormsModule,
NzColDirective,
NzButtonComponent,
NzIconDirective,
NzInputDirective,
NzFormModule,
NzFlexDirective,
NgForOf,
DatePipe,
NzThMeasureDirective,
NzInputNumberComponent,
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 = [];
@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) => {
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) => {
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,103 @@
<nz-layout>
<!-- start 头部 start-->
<nz-header>
<div nz-row>
<!-- logo -->
<div class="logo" nz-col nzSpan="4">
<!--<h2>OA审批</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)="showUpdatePwd()">修改密码</li>
<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">
<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>
<nz-modal [(nzVisible)]="updatePwdModal"
[nzWidth]="500"
nzTitle="修改密码"
[nzFooter]="null"
(nzOnCancel)="closeUpdatePwd()">
<ng-container *nzModalContent>
<form nz-form [formGroup]="updatePwdForm">
<nz-form-item>
<nz-form-label [nzSm]="6" [nzXs]="24" nzRequired>旧密码</nz-form-label>
<nz-form-control [nzSm]="14" [nzXs]="24">
<input [type]="'password'" nz-input formControlName="oldPassword" placeholder="请输入旧密码" />
</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">
<input [type]="'password'" nz-input formControlName="newPassword" placeholder="请输入新密码" />
</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]="errorTpl">
<input [type]="'password'" nz-input formControlName="confirmPassword" placeholder="确认新密码" />
<ng-template #errorTpl let-control>
@if (control.errors?.['confirm']) {
两次密码输入不一致!
}
</ng-template>
</nz-form-control>
</nz-form-item>
<div nz-flex style="margin-top: 15px" [nzJustify]="'center'">
<button nz-button nzType="primary" (click)="submitUpdatePwd()" class="submit-btn" style="width: 150px;">确定</button>
</div>
</form>
</ng-container>
</nz-modal>

@ -0,0 +1,69 @@
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 {
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;
}
}

@ -0,0 +1,246 @@
import {Component, OnInit} from '@angular/core';
import {Router, RouterLink, RouterOutlet} from "@angular/router";
import {
NzContentComponent,
NzFooterComponent,
NzHeaderComponent,
NzLayoutComponent,
NzSiderComponent
} from "ng-zorro-antd/layout";
import {NzBreadCrumbComponent, NzBreadCrumbItemComponent} from "ng-zorro-antd/breadcrumb";
import {NzColDirective, NzRowDirective} from "ng-zorro-antd/grid";
import {NzMenuDirective, NzMenuGroupComponent, NzMenuItemComponent, NzSubMenuComponent} from "ng-zorro-antd/menu";
import {NgClass, NgForOf, NgIf} from "@angular/common";
import {NzIconDirective} from "ng-zorro-antd/icon";
import {NzTabComponent, NzTabSetComponent} from "ng-zorro-antd/tabs";
import {TabComponent} from "../tab/tab.component";
import {BrowserStorageService} from "../../../utils/localStorage.service";
import {DATA, INIT_FLAG, LOGIN_DATA, USER_TOKEN} from "../../../data/login/localStorage.namespace";
import {NzDropDownDirective, NzDropdownMenuComponent} from "ng-zorro-antd/dropdown";
import {NzMessageService} from "ng-zorro-antd/message";
import {Dictionary, dictionaryData} from "../../../data/common/dictionary.namespace";
import {CommonService} from "../../../services/common/common.service";
import {LoginService} from "../../../services/login/login.service";
import {NzConfigService} from "ng-zorro-antd/core/config";
import {buttonData} from "../../../data/common/button.namespace";
import {SysAccountService} from "../../../services/account/sys-account.service";
import {
AbstractControl,
FormGroup,
FormsModule,
NonNullableFormBuilder,
ReactiveFormsModule, ValidationErrors, ValidatorFn,
Validators
} from "@angular/forms";
import {NzButtonComponent} from "ng-zorro-antd/button";
import {NzDatePickerComponent} from "ng-zorro-antd/date-picker";
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 {NzModalModule} from "ng-zorro-antd/modal";
import {NzOptionComponent, NzSelectComponent} from "ng-zorro-antd/select";
import {NzUploadComponent} from "ng-zorro-antd/upload";
import {ValidatorsService} from "../../../utils/validators.service";
@Component({
selector: 'app-index',
standalone: true,
imports: [
RouterOutlet,
NzLayoutComponent,
NzHeaderComponent,
NzBreadCrumbComponent,
NzBreadCrumbItemComponent,
NzContentComponent,
NzFooterComponent,
NzRowDirective,
NzColDirective,
NzMenuItemComponent,
NzMenuDirective,
RouterLink,
NzSubMenuComponent,
NgForOf,
NgClass,
NzIconDirective,
NzTabComponent,
NzTabSetComponent,
TabComponent,
NzSiderComponent,
NzMenuGroupComponent,
NzDropDownDirective,
NzDropdownMenuComponent,
FormsModule,
NgIf,
NzButtonComponent,
NzDatePickerComponent,
NzFlexDirective,
NzFormControlComponent,
NzFormDirective,
NzFormItemComponent,
NzFormLabelComponent,
NzInputDirective,
NzOptionComponent,
NzSelectComponent,
NzUploadComponent,
ReactiveFormsModule,
NzModalModule,
],
templateUrl: './index.component.html',
styleUrl: './index.component.less',
})
export class IndexComponent implements OnInit {
// 菜单数据
menuData: any = [{
'menuName' : '首页',
'selected' : true,
}];
userInfo: any;
// 左侧菜单栏数据
leftMenuData: any = [];
// 侧边菜单展示开关
isCollapse = true;
// 当前顶级菜单
currentParentMenu: any = {};
updatePwdModal = false;
updatePwdForm: FormGroup;
ngOnInit(): void {
// 缓存数据字典
this.commonService.queryDictionary('','',(data: any) => {
dictionaryData.length = 0;
for (let item of data['return_data']) {
dictionaryData.push(
{
codeType: item.codeType,
codeValue: item.codeValue,
codeName: item.codeName,
codeDesc: item.codeDesc,
sortId: item.sortId,
ext1: item.ext1,
ext2: item.ext2,
ext3: item.ext3
}
);
}
});
}
constructor(
private commonService: CommonService,
private storage: BrowserStorageService,
private nzConfigService: NzConfigService,
private message: NzMessageService,
private fb: NonNullableFormBuilder,
private sysAccountService: SysAccountService,
private router: Router, // 路由
private login: LoginService
) {
// 缓存角色按钮权限
for (let item of this.storage.get(DATA)['button']) {
buttonData.push(
{
menuName: item.menuName,
menuType: item.menuType,
menuUrl: item.menuUrl,
menuUrlImg: item.menuUrlImg,
menuPSid: item.menuPSid,
menuSort: item.menuSort,
menuDesc: item.menuDesc,
}
);
}
this.updatePwdForm = this.fb.group({
userId: [ this.storage.get(DATA)['account']['id']],
oldPassword: ['', [Validators.required, ValidatorsService.minLength(6), ValidatorsService.maxLength(16)]],
newPassword: ['', [Validators.required, ValidatorsService.minLength(6), ValidatorsService.maxLength(16)]],
confirmPassword: ['', [Validators.required, this.confirmationValidator]],
});
this.menuData = this.menuData.concat(this.storage.get(DATA)['menuTree']);
this.userInfo = this.storage.get(DATA)['account'];
}
/**
*
* @param control
*/
confirmationValidator: ValidatorFn = (control: AbstractControl): { [s: string]: boolean } => {
if (!control.value) {
return { required: true };
} else if (control.value !== this.updatePwdForm.controls['newPassword'].value) {
return { confirm: true, error: true };
}
return {};
};
// 选择操作
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 showUpdatePwd() {
this.updatePwdModal = true;
}
/**
*
*/
public submitUpdatePwd() {
if (this.updatePwdForm.valid) {
this.sysAccountService.updatePwd(this.updatePwdForm.value, (data: any) => {
if (data['return_code'] == '000000') {
this.message.success("修改成功,请重新登录");
// 登出
this.loginOut();
} else {
this.message.create('error', data['return_msg']);
}
});
} else {
Object.values(this.updatePwdForm.controls).forEach(control => {
if (control.invalid) {
control.markAsDirty();
control.updateValueAndValidity({ onlySelf: true });
}
});
}
}
/**
*
*/
public closeUpdatePwd() {
this.updatePwdModal = false;
}
// 退出登录
public loginOut(): void {
this.login.loginOut( (data: any) => {
if (data['return_code'] === '000000') {
this.storage.remove(LOGIN_DATA);
this.storage.remove(DATA);
this.storage.remove(USER_TOKEN);
this.storage.remove(INIT_FLAG);
this.router.navigateByUrl('/login').then();
} else {
this.message.error(data['return_msg']);
}
});
}
}

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

@ -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(100vh - 114px);
overflow: auto;
}

@ -0,0 +1,116 @@
import { Component } from '@angular/core';
import {NgForOf} from "@angular/common";
import {NzTabComponent, NzTabSetComponent} from "ng-zorro-antd/tabs";
import {ActivationEnd, Router, RouterOutlet} from "@angular/router";
import {filter} from "rxjs";
import {NzCardComponent} from "ng-zorro-antd/card";
@Component({
selector: 'app-tab',
standalone: true,
imports: [
NgForOf,
NzTabComponent,
NzTabSetComponent,
RouterOutlet,
NzCardComponent
],
templateUrl: './tab.component.html',
styleUrl: './tab.component.less'
})
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,176 @@
<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>
<nz-select formControlName="type" nzAllowClear [nzPlaceHolder]="'请选择'">
<nz-option *ngFor="let item of cmsTypeArray" nzValue="{{item.codeValue}}" nzLabel="{{item.codeName}}"></nz-option>
</nz-select>
</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 formControlName="status" nzAllowClear [nzPlaceHolder]="'请选择'">
<nz-option *ngFor="let item of cmsStatusArray" nzValue="{{item.codeValue}}" nzLabel="{{item.codeName}}"></nz-option>
</nz-select>
</nz-form-control>
</nz-form-item>
</div>
<div nz-col [nzSpan]="6">
<nz-form-item>
<nz-form-label></nz-form-label>
<nz-form-control>
<button nz-button style="margin-right: 8px" [nzType]="'primary'" (click)="searchFormSubmit()">查询</button>
<button nz-button style="margin-right: 8px" (click)="searchFormReset()">重置</button>
<button nz-button style="margin-right: 8px" (click)="showEditCmsModal(null)" [nzType]="'primary'" >新增轮播图</button>
</nz-form-control>
</nz-form-item>
</div>
</div>
</form>
<nz-table #basicTable
[nzScroll]="{ x: '1500' }"
[nzBordered]="true"
[nzFrontPagination]="false"
[nzShowQuickJumper]="true"
[nzShowTotal]="totalTemplate"
[(nzPageIndex)]="tablePageNum"
(nzPageIndexChange)="queryData()"
nzShowSizeChanger
(nzPageSizeChange)="this.tablePageSize = $event;queryData()"
[nzPageSizeOptions]="[ 10, 20, 30, 50, 100 ]"
[nzTotal]="tableData.total"
[nzData]="tableData.list" >
<thead>
<tr>
<th nzWidth="100px">轮播类型</th>
<th nzWidth="100px">跳转类型</th>
<th nzWidth="100px">跳转地址</th>
<th nzWidth="100px">图片</th>
<th nzWidth="100px">状态</th>
<th nzWidth="100px">定时上线时间</th>
<th nzWidth="100px">定时下线时间</th>
<th nzWidth="100px">创建时间</th>
<th nzRight nzWidth="70px">操作</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let data of basicTable.data">
<td>{{data.type | dictionary : 'CMS_TYPE'}}</td>
<td>
<span *ngIf="data.jumpType == 1">内链</span>
<span *ngIf="data.jumpType == 2">外链</span>
</td>
<td>{{data.jumpUrl}}</td>
<td><img nz-image width="100px" height="100px" nzSrc="{{IMAGE_URL + data.imgUrl}}" alt="" style="object-fit: contain;"></td>
<td>{{data.status | dictionary : 'CMS_STATUS'}}</td>
<td>
<span *ngIf="data.startTime == null">未设置</span>
<span *ngIf="data.startTime != null">{{data.startTime | date : 'yyyy-MM-dd HH:mm'}}</span>
</td>
<td>
<span *ngIf="data.endTime == null">未设置</span>
<span *ngIf="data.endTime != null">{{data.endTime | date : 'yyyy-MM-dd HH:mm'}}</span>
</td>
<td>{{data.createTime | date : 'yyyy-MM-dd HH:mm:ss'}}</td>
<td nzRight>
<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 != 2"><a (click)="showEditCmsModal(data)">修改</a></li>
<li nz-menu-item *ngIf="data.status != 2"><a (click)="online(data)">上线</a></li>
<li nz-menu-item *ngIf="data.status == 2"><a (click)="offline(data.id)">下线</a></li>
<li nz-menu-item *ngIf="data.status != 2"><a (click)="delCms(data.id)">删除</a></li>
</ul>
</nz-dropdown-menu></td>
</tr>
</tbody>
<ng-template #totalTemplate let-total>总计 {{ total }} 条</ng-template>
</nz-table>
<nz-modal [(nzVisible)]="editCmsModal"
[nzWidth]="500"
nzTitle="{{this.editCmsForm.controls['id'].value==null?'新增':'修改'}}"
[nzFooter]="null"
(nzOnCancel)="closeEditModal()">
<ng-container *nzModalContent>
<form nz-form [formGroup]="editCmsForm">
<nz-form-item>
<nz-form-label [nzSm]="6" [nzXs]="24" nzRequired>轮播图类型</nz-form-label>
<nz-form-control [nzSm]="14" [nzXs]="24">
<nz-select formControlName="type" [nzPlaceHolder]="'请选择'">
<nz-option *ngFor="let item of cmsTypeArray" 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>轮播图图片</nz-form-label>
<nz-form-control [nzSm]="14" [nzXs]="24">
<nz-upload
class="logo-uploader"
nzAction="{{BASE_URL}}fileUpload/uploadfile"
nzName="avatar"
nzListType="picture-card"
[nzShowUploadList]="false"
[nzBeforeUpload]="beforeUpload"
(nzChange)="handleChange($event)"
>
<ng-container *ngIf="!logoUrl">
<i class="upload-icon" nz-icon [nzType]="loading ? 'loading' : 'plus'"></i>
<div class="ant-upload-text">上传</div>
</ng-container>
<img *ngIf="logoUrl" [src]="logoUrl" style="width: 100%" />
</nz-upload>
</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">
<nz-select formControlName="jumpType" nzAllowClear [nzPlaceHolder]="'请选择'">
<nz-option nzValue="1" nzLabel="内链"></nz-option>
<nz-option nzValue="2" nzLabel="外链"></nz-option>
</nz-select>
</nz-form-control>
</nz-form-item>
<nz-form-item *ngIf="editCmsForm.controls['jumpType'].value != null">
<nz-form-label [nzSm]="6" [nzXs]="24">跳转地址</nz-form-label>
<nz-form-control [nzSm]="14" [nzXs]="24">
<input nz-input formControlName="jumpUrl" placeholder="请输入" />
</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">
<nz-range-picker formControlName="startTime" [nzShowTime]="{ nzFormat: 'HH:mm' }" [nzFormat]="'yyyy/MM/dd HH:mm'" ></nz-range-picker>
</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">
<nz-range-picker formControlName="endTime" [nzShowTime]="{ nzFormat: 'HH:mm' }" [nzFormat]="'yyyy/MM/dd HH:mm'" ></nz-range-picker>
</nz-form-control>
</nz-form-item>
<div nz-flex style="margin-top: 15px" [nzJustify]="'center'">
<button nz-button nzType="primary" (click)="submitData()" class="submit-btn">保存</button>
</div>
</form>
</ng-container>
</nz-modal>

@ -0,0 +1,12 @@
nz-input-number {
width: 100%;
}
nz-date-picker {
width: 100%;
}
.submit-btn {
width: 150px;
}
nz-range-picker {
width: 100%;
}

@ -0,0 +1,333 @@
import { Component } from '@angular/core';
import {DatePipe, NgForOf, NgIf} from "@angular/common";
import {DictionaryPipe} from "../../../pipes/common/dictionary.pipe";
import {FormGroup, FormsModule, NonNullableFormBuilder, ReactiveFormsModule, Validators} from "@angular/forms";
import {NzButtonComponent} from "ng-zorro-antd/button";
import {
NzCellFixedDirective,
NzTableCellDirective,
NzTableComponent, NzTableModule,
NzTbodyComponent, NzTheadComponent,
NzThMeasureDirective, NzTrDirective
} from "ng-zorro-antd/table";
import {NzColDirective, NzRowDirective} from "ng-zorro-antd/grid";
import {NzDatePickerComponent} from "ng-zorro-antd/date-picker";
import {NzFlexDirective} from "ng-zorro-antd/flex";
import {NzFormControlComponent, NzFormDirective, NzFormItemComponent, NzFormLabelComponent} from "ng-zorro-antd/form";
import {NzInputNumberComponent, NzInputNumberModule} from "ng-zorro-antd/input-number";
import {NzModalComponent, NzModalModule, NzModalService} from "ng-zorro-antd/modal";
import {NzOptionComponent, NzSelectComponent} from "ng-zorro-antd/select";
import {NzMessageService} from "ng-zorro-antd/message";
import {BrowserStorageService} from "../../../utils/localStorage.service";
import {CommonService} from "../../../services/common/common.service";
import {DATA} from "../../../data/login/localStorage.namespace";
import {NzDividerComponent} from "ng-zorro-antd/divider";
import {NzDropDownADirective, NzDropDownDirective, NzDropdownMenuComponent} from "ng-zorro-antd/dropdown";
import {NzIconDirective} from "ng-zorro-antd/icon";
import {NzInputDirective} from "ng-zorro-antd/input";
import {NzMenuDirective, NzMenuItemComponent} from "ng-zorro-antd/menu";
import {NzDescriptionsModule} from "ng-zorro-antd/descriptions";
import {NzUploadComponent, NzUploadFile} from "ng-zorro-antd/upload";
import {NzAvatarModule} from "ng-zorro-antd/avatar";
import {NzImageModule} from "ng-zorro-antd/image";
import {NzSwitchComponent} from "ng-zorro-antd/switch";
import {environment} from "../../../../environments/environment";
import {CmsService} from "../../../services/cms/cms.service";
import {Observable, Observer} from "rxjs";
@Component({
selector: 'app-cms-list',
standalone: true,
imports: [
DatePipe,
DictionaryPipe,
FormsModule,
NgForOf,
NgIf,
NzButtonComponent,
NzCellFixedDirective,
NzColDirective,
NzDividerComponent,
NzDropDownADirective,
NzDropDownDirective,
NzDropdownMenuComponent,
NzFormControlComponent,
NzFormDirective,
NzFormItemComponent,
NzFormLabelComponent,
NzIconDirective,
NzInputDirective,
NzMenuDirective,
NzMenuItemComponent,
NzOptionComponent,
NzRowDirective,
NzSelectComponent,
NzTableCellDirective,
NzTableComponent,
NzTbodyComponent,
NzThMeasureDirective,
NzTheadComponent,
NzTrDirective,
ReactiveFormsModule,
NzTableModule,
NzModalModule,
NzFlexDirective,
NzDescriptionsModule,
NzUploadComponent,
NzAvatarModule,
NzImageModule,
NzSwitchComponent,
NzDatePickerComponent,
NzInputNumberModule
],
templateUrl: './cms-list.component.html',
styleUrl: './cms-list.component.less'
})
export class CmsListComponent {
BASE_URL = environment.baseUrl;
IMAGE_URL = environment.imageUrl;
loading = false;
logoUrl?: string;
// 表单页数
tablePageNum = 1;
// 每页数量
tablePageSize = 10;
// 表单数据
tableData: any = {
total: 0,
list: [],
};
accountType = null;
// 搜索表单
searchForm: FormGroup;
// 编辑表单
editCmsForm: FormGroup;
editCmsModal = false;
// 轮播图类型
cmsTypeArray = new DictionaryPipe().getDictionaryList('CMS_TYPE');
// 轮播图状态
cmsStatusArray = new DictionaryPipe().getDictionaryList('CMS_STATUS');
constructor(private fb: NonNullableFormBuilder,
private message: NzMessageService,
private storage: BrowserStorageService,
private cmsService: CmsService,
private commonService: CommonService,
private modal: NzModalService) {
this.accountType = this.storage.get(DATA)['account']['objectType'];
// 初始化搜索框
this.searchForm = this.fb.group({
type: [null],
status: [null],
});
this.editCmsForm = this.fb.group({
id: [''],
type: ['', [Validators.required]],
imgUrl: ['', [Validators.required]],
jumpType: [''],
jumpUrl: [''],
startTime: [''],
endTime: [''],
});
// 查询数据
this.queryData();
}
/**
*
*/
queryData() {
this.searchForm.value.pageNum = this.tablePageNum;
this.searchForm.value.pageSize = this.tablePageSize;
this.searchForm.value.time = new Date().getTime();
this.cmsService.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();
}
/**
*
*/
delCms(id: number) {
this.modal.confirm({
nzTitle: '提示',
nzContent: '确定删除吗?',
nzOnOk: () =>
this.cmsService.delete({id: id}, (data: any) => {
if (data['return_code'] == '000000') {
// 刷新数据
this.queryData();
this.message.create('success', '操作成功');
} else {
this.message.create('error', data['return_msg']);
}
})
});
}
/**
* 线
*/
offline(id: number) {
this.modal.confirm({
nzTitle: '提示',
nzContent: '确定轮播图下线吗?',
nzOnOk: () =>
this.cmsService.offline({id: id}, (data: any) => {
if (data['return_code'] == '000000') {
// 刷新数据
this.queryData();
this.message.create('success', '操作成功');
} else {
this.message.create('error', data['return_msg']);
}
})
});
}
/**
* 线
*/
online(data: any) {
this.modal.confirm({
nzTitle: '提示',
nzContent: data.startTime!=null?'已设置自动上线时间,确定需要提前上线吗?':'确定立即上线吗?',
nzOnOk: () =>
this.cmsService.online({id: data.id}, (data: any) => {
if (data['return_code'] == '000000') {
// 刷新数据
this.queryData();
this.message.create('success', '操作成功');
} else {
this.message.create('error', data['return_msg']);
}
})
});
}
/**
*
*/
showEditCmsModal(data: any) {
if (data != null) {
this.logoUrl = this.IMAGE_URL + data['imgUrl'];
data['type'] = ''+data['type'];
data['jumpType'] = ''+data['jumpType'];
}
this.editCmsForm.patchValue(data);
this.editCmsModal = true;
}
/**
*
*/
submitData() {
if (this.editCmsForm.valid) {
if (this.editCmsForm.value.startTime != null && this.editCmsForm.value.startTime != '') {
console.log(this.editCmsForm.value.startTime);
let startTime = new Date(this.editCmsForm.value.startTime);
startTime.setSeconds(0,0);
console.log(startTime.getTime())
this.editCmsForm.controls['startTime'].setValue(startTime.getTime());
}
if (this.editCmsForm.value.endTime != null && this.editCmsForm.value.endTime != '') {
let endTime = new Date(this.editCmsForm.value.endTime);
endTime.setSeconds(0,0);
this.editCmsForm.controls['endTime'].setValue(endTime.getTime());
}
this.cmsService.edit(this.editCmsForm.value, (data: any) => {
if (data['return_code'] == '000000') {
this.message.create('success', '操作成功');
// 刷新数据
this.queryData();
// 关闭弹窗
this.closeEditModal();
// 表单重置
this.editCmsForm.reset();
} else {
this.message.create('error', data['return_msg']);
}
});
} else {
Object.values(this.editCmsForm.controls).forEach(control => {
if (control.invalid) {
control.markAsDirty();
control.updateValueAndValidity({ onlySelf: true });
}
});
}
}
/**
*
*/
closeEditModal() {
this.editCmsForm.reset();
this.logoUrl = '';
this.editCmsModal = false;
}
private getBase64(img: File, callback: (img: string) => void): void {
const reader = new FileReader();
reader.addEventListener('load', () => callback(reader.result!.toString()));
reader.readAsDataURL(img);
}
handleChange(info: { file: NzUploadFile }): void {
switch (info.file.status) {
case 'uploading':
this.loading = true;
break;
case 'done':
// Get this url from response in real world.
this.getBase64(info.file!.originFileObj!, (img: string) => {
this.loading = false;
this.logoUrl = img;
});
// 不是自建站,拼接绝对路径访问链接
this.editCmsForm.patchValue({ imgUrl: info.file.response['return_data'][0]});
break;
case 'error':
this.message.error('网络错误!');
this.loading = false;
break;
}
}
beforeUpload = (file: NzUploadFile, _fileList: NzUploadFile[]) => {
return new Observable((observer: Observer<boolean>) => {
const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png';
if (!isJpgOrPng) {
this.message.error('只能上传JPG文件!');
observer.complete();
return;
}
const isLt2M = file.size! / 1024 / 1024 < 2;
if (!isLt2M) {
this.message.error('图片必须小于2MB!');
observer.complete();
return;
}
observer.next(isJpgOrPng && isLt2M);
observer.complete();
});
};
}

@ -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,44 @@
import { Component } from '@angular/core';
import {NzResultModule} from "ng-zorro-antd/result";
import {NzButtonComponent} from "ng-zorro-antd/button";
import {BrowserStorageService} from "../../../utils/localStorage.service";
import {NzMessageService} from "ng-zorro-antd/message";
import {Router} from "@angular/router";
import {LoginService} from "../../../services/login/login.service";
import {DATA, INIT_FLAG, LOGIN_DATA, USER_TOKEN} from "../../../data/login/localStorage.namespace";
@Component({
selector: 'app-error401',
standalone: true,
imports: [
NzResultModule,
NzButtonComponent
],
templateUrl: './error401.component.html',
styleUrl: './error401.component.less'
})
export class Error401Component {
constructor(
private storage: BrowserStorageService,
private message: NzMessageService,
private router: Router, // 路由
private login: LoginService
) {}
// 退出登录
public loginOut(): void {
this.login.loginOut( (data: any) => {
if (data['return_code'] === '000000') {
this.storage.remove(LOGIN_DATA);
this.storage.remove(DATA);
this.storage.remove(USER_TOKEN);
this.storage.remove(INIT_FLAG);
this.router.navigateByUrl('/login').then();
} 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,10 @@
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,5 @@
<nz-result
[nzIcon]="'smile-twotone'"
nzTitle="欢迎使用"
>
</nz-result>

@ -0,0 +1,21 @@
import { Component } from '@angular/core';
import {NzTabComponent, NzTabSetComponent} from "ng-zorro-antd/tabs";
import {NgForOf, NgOptimizedImage} from "@angular/common";
import {NzResultComponent} from "ng-zorro-antd/result";
@Component({
selector: 'app-index',
standalone: true,
imports: [
NzTabSetComponent,
NzTabComponent,
NgForOf,
NgOptimizedImage,
NzResultComponent
],
templateUrl: './index.component.html',
styleUrl: './index.component.less'
})
export class IndexComponent {
}

@ -0,0 +1,12 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { NzSliderModule } from 'ng-zorro-antd/slider';
@NgModule({
declarations: [],
imports: [
CommonModule,
NzSliderModule,
]
})
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,65 @@
<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">
<input type="password" nzSize="large" nz-input formControlName="password" placeholder="密码" />
</nz-input-group>
</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,78 @@
.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: 450px;
margin-top: 100px;
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%;
}

@ -0,0 +1,126 @@
import {Component} 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 {
FormControl,
FormGroup,
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 {BrowserStorageService} from "../../../utils/localStorage.service";
import {LoginService} from "../../../services/login/login.service";
import {dictionaryData} from "../../../data/common/dictionary.namespace";
import {buttonData} from "../../../data/common/button.namespace";
@Component({
selector: 'app-login',
standalone: true,
templateUrl: './login.component.html',
host: {ngSkipHydration: 'true'},
imports: [
NzCarouselComponent,
NzCarouselContentDirective,
NgForOf,
NgOptimizedImage,
NzInputGroupComponent,
NzFormItemComponent,
ReactiveFormsModule,
NzFormControlComponent,
NzColDirective,
NzButtonComponent,
NzCheckboxComponent,
NzInputDirective,
NzRowDirective,
NzFormDirective,
NzSliderComponent,
],
styleUrl: './login.component.less'
})
export class LoginComponent {
constructor(
private fb: NonNullableFormBuilder,
private message: NzMessageService,
private router: Router, // 路由
private storage: BrowserStorageService,
private login: LoginService
) {
this.validateForm.patchValue(this.storage.get(LOGIN_DATA));
}
// 背景图片
backgroundImage = backgroundImage;
// 验证随机数
randomNumber = MathUtils.generateRandomNumber(100);
// 表单
validateForm: FormGroup<{
loginName: FormControl<string>;
password: FormControl<string>;
slider: FormControl<string>;
remember: FormControl<boolean>;
}> = this.fb.group({
loginName: ['', [Validators.required]],
password: ['', [Validators.required]],
slider: ['', [Validators.required]],
remember: [true]
});
// 登录
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.set(LOGIN_DATA, this.validateForm.value);
this.storage.set(DATA, data['return_data']['object']);
this.storage.set(USER_TOKEN, data['return_data']['token']);
this.storage.set(INIT_FLAG, true);
this.router.navigateByUrl('/admin/index');
} else {
this.message.error(data['return_msg']);
}
});
this.randomNumber = MathUtils.generateRandomNumber(100)
} else {
Object.values(this.validateForm.controls).forEach(control => {
if (control.invalid) {
// 表单脏检查
control.markAsDirty();
control.updateValueAndValidity({ onlySelf: true });
}
});
}
}
}

@ -0,0 +1,106 @@
<div nz-row [nzGutter]="16">
<div nz-col nzSpan="12">
<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>
</div>
<div nz-col nzSpan="12">
<nz-card nzTitle="菜单详情" [nzExtra]="extraTemplate" [nzBodyStyle]="{ padding: '0px'}">
<nz-descriptions nzBordered
nzLayout="vertical"
[nzSize]="'middle'"
[nzColumn]="{ xxl: 2, xl: 2, lg: 2, md: 2, sm: 2, xs: 1 }"
>
<nz-descriptions-item nzTitle="父级菜单">{{menuDetail?.parentMenu?menuDetail.parentMenu.menuName:'无'}}</nz-descriptions-item>
<nz-descriptions-item nzTitle="菜单名称">{{menuDetail.menuName?menuDetail.menuName:'无'}}</nz-descriptions-item>
<nz-descriptions-item nzTitle="菜单类型">{{menuDetail.menuType | dictionary: '菜单类型'}}</nz-descriptions-item>
<nz-descriptions-item nzTitle="菜单URL">{{menuDetail.menuUrl?menuDetail.menuUrl:'无'}}</nz-descriptions-item>
<nz-descriptions-item nzTitle="菜单图标">{{menuDetail.menuUrlImg?menuDetail.menuUrlImg:'无'}}</nz-descriptions-item>
<nz-descriptions-item nzTitle="菜单描述">{{menuDetail.menuDesc?menuDetail.menuDesc:'无'}}</nz-descriptions-item>
</nz-descriptions>
</nz-card>
<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>
</div>
</div>
<nz-modal [(nzVisible)]="editMenuVisible" nzTitle="{{editMenuTitle}}" [nzFooter]="null" (nzOnCancel)="closeEditMenu()">
<ng-container *nzModalContent>
<form nz-form [formGroup]="editMenuForm" (ngSubmit)="submitEditMenuForm()">
<nz-form-item>
<nz-form-label [nzSm]="6" [nzXs]="24">父级菜单</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>
<div nz-flex [nzJustify]="'center'">
<button nz-button nzType="primary" class="submit-btn">保存</button>
</div>
</form>
</ng-container>
</nz-modal>

@ -0,0 +1,6 @@
.submit-btn {
width: 150px;
}
nz-input-number {
width: 100%;
}

@ -0,0 +1,253 @@
import {Component} from '@angular/core';
import {NzTreeComponent} from "ng-zorro-antd/tree";
import {NgForOf, NgIf} from "@angular/common";
import {NzIconDirective} from "ng-zorro-antd/icon";
import {NzDropdownMenuComponent} from "ng-zorro-antd/dropdown";
import {
NzTreeNodeCheckboxComponent,
NzTreeNodeComponent,
NzTreeNodeOptionComponent,
NzTreeViewComponent
} from "ng-zorro-antd/tree-view";
import {NzCardComponent} from "ng-zorro-antd/card";
import {NzColDirective, NzRowDirective} from "ng-zorro-antd/grid";
import {NzButtonComponent} from "ng-zorro-antd/button";
import {NzDescriptionsComponent, NzDescriptionsItemComponent} from "ng-zorro-antd/descriptions";
import {NzFormDirective, NzFormLabelComponent, NzFormModule} from "ng-zorro-antd/form";
import {FormGroup, NonNullableFormBuilder, ReactiveFormsModule, Validators} from "@angular/forms";
import {NzInputDirective, NzInputGroupComponent} from "ng-zorro-antd/input";
import {NzModalModule, NzModalService} from 'ng-zorro-antd/modal';
import {NzFlexDirective} from "ng-zorro-antd/flex";
import {NzOptionComponent, NzSelectComponent} from "ng-zorro-antd/select";
import {NzInputNumberComponent} from "ng-zorro-antd/input-number";
import {NzMessageService} from "ng-zorro-antd/message";
import {NzSpinComponent} from "ng-zorro-antd/spin";
import {NzTreeSelectComponent} from "ng-zorro-antd/tree-select";
import {DictionaryPipe} from "../../../pipes/common/dictionary.pipe";
import {Dictionary} from "../../../data/common/dictionary.namespace";
import {MenuService} from "../../../services/menu/menu.service";
@Component({
selector: 'app-menu',
standalone: true,
imports: [
NzTreeComponent,
NgIf,
NzIconDirective,
NzDropdownMenuComponent,
NzTreeViewComponent,
NzTreeNodeComponent,
NzTreeNodeCheckboxComponent,
NzTreeNodeOptionComponent,
NzCardComponent,
NzRowDirective,
NzColDirective,
NzButtonComponent,
NzModalModule,
NzFormDirective,
ReactiveFormsModule,
NzFormLabelComponent,
NzInputDirective,
NzDescriptionsItemComponent,
NzDescriptionsComponent,
NzInputGroupComponent,
NzFormModule,
NzFlexDirective,
NzSelectComponent,
NzOptionComponent,
NgForOf,
NzInputNumberComponent,
NzSpinComponent,
NzTreeSelectComponent,
DictionaryPipe
],
templateUrl: './menu.component.html',
styleUrl: './menu.component.less'
})
export class MenuComponent {
// 菜单数据
menuData: any = [];
// 菜单树形数据
menuTree: any[] = [];
// 菜单详情
menuDetail: any = {};
// 编辑菜单弹出框
editMenuVisible = false;
// 编辑菜单表单
editMenuForm: FormGroup;
// 编辑菜单标题
editMenuTitle = '';
// 树视图展示状态
treeView= false;
menuTypeArray:Dictionary[] = [];
constructor(private menuService: MenuService,
private fb: NonNullableFormBuilder,
private modal: NzModalService,
private message: NzMessageService) {
// 菜单类型
this.menuTypeArray = new DictionaryPipe().getDictionaryList('MENU_TYPE');
// 获取菜单数据
this.queryMenuTree();
this.editMenuForm = this.fb.group({
id: [],
menuName: ['', [Validators.required]],
menuType: ['1', [Validators.required]],
menuUrl: ['', [Validators.required]],
menuPSid: [null],
menuUrlImg: [''],
menuSort: ['', [Validators.required]],
menuDesc: [''],
});
}
/**
*
*/
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;
}
}
/**
*
* @param edit true false
*/
openEditMenu(edit: boolean) {
// 初始化表单
this.editMenuTitle = '添加菜单';
this.editMenuForm.reset();
let menu = {
menuPSid: '',
menuType: '1'
};
if (edit == false && this.menuDetail?.id == null) {
this.modal.warning({
nzTitle: '提示',
nzContent: '请点击需要修改的菜单',
});
return;
}
if (this.menuDetail != null) {
if (edit) {
// 增加
menu = {
menuPSid: ""+this.menuDetail.id,
menuType: '1'
};
} else {
// 修改
this.editMenuTitle = '修改菜单';
menu = this.menuDetail;
menu['menuPSid'] = ""+menu['menuPSid']
menu['menuType'] = ""+menu['menuType']
}
}
this.editMenuForm.patchValue(menu);
this.editMenuVisible = true;
}
/**
*
*/
submitEditMenuForm() {
if (this.editMenuForm.valid) {
this.treeView = false;
console.log(this.editMenuForm.controls['menuPSid'].value);
if (this.editMenuForm.controls['menuPSid'].value == 'undefined') {
this.editMenuForm.controls['menuPSid'].setValue(null);
}
console.log(this.editMenuForm.value);
this.menuService.editMenu(this.editMenuForm.value, (data: any) => {
if (data['return_code'] == '000000') {
// 刷新数据
this.queryMenuTree();
this.message.create('success', '操作成功');
// 关闭弹窗
this.closeEditMenu();
} else {
this.message.create('error', data['return_msg']);
}
});
} else {
Object.values(this.editMenuForm.controls).forEach(control => {
if (control.invalid) {
control.markAsDirty();
control.updateValueAndValidity({ onlySelf: true });
}
});
}
}
/**
*
*/
closeEditMenu() {
this.editMenuVisible = false;
}
/**
*
*/
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,8 @@
import { Routes } from '@angular/router';
import {MenuComponent} from "./menu/menu.component";
import {CmsListComponent} from "../cms/cms-list/cms-list.component";
export const SYSTEM_ROUTES: Routes = [
{ path: 'menu', component: MenuComponent},
{ path: 'cms-list', component: CmsListComponent}
];

@ -0,0 +1,38 @@
import { Pipe, PipeTransform } from '@angular/core';
import {Button, buttonData} from "../../data/common/button.namespace";
@Pipe({
name: 'button',
standalone: true
})
export class ButtonPipe implements PipeTransform {
data: Button[] = buttonData;
/**
*
* @param buttonCode
*/
isButton(buttonCode: string): boolean {
// 读取缓存数据
return this.data.find(o => o.menuUrl == buttonCode) != null;
}
/**
*
* @param buttonCode
*/
getButton(buttonCode: string): Button | any {
// 读取缓存数据
return this.data.find(o => o.menuUrl == buttonCode);
}
transform(codeType: string): string {
const obj = this.getButton(codeType);
if (obj == null) {
return '未知';
} else {
return obj.menuName;
}
}
}

@ -0,0 +1,19 @@
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'dateMinutesDiff',
standalone: true
})
/**
*
*/
export class DateMinutesDiffPipe implements PipeTransform {
transform(newTime: number | Date, olbTime: number | Date): number {
console.log('123');
var diffTimeStamp = new Date().getTime() - new Date(olbTime).getTime();
return parseInt(String(diffTimeStamp / (1000 * 60)));
}
}

@ -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,127 @@
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 updatePwd(param: any, callBack:any) {
param.time = new Date().getTime();
this.http.post(environment.baseUrl + 'secUser/updatePwd', param).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,72 @@
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 CmsService {
constructor(private http: HttpClient) { }
/**
*
* @param params
* @param callBack
*/
public edit(params: any, callBack:any) {
params.time = new Date().getTime();
this.http.post(environment.baseUrl + 'cms/edit',params).subscribe(data => {
callBack(data);
});
}
/**
* 线
* @param params
* @param callBack
*/
public online(params: any, callBack:any) {
params.time = new Date().getTime();
this.http.post(environment.baseUrl + 'cms/online',params).subscribe(data => {
callBack(data);
});
}
/**
* 线
* @param params
* @param callBack
*/
public offline(params: any, callBack:any) {
params.time = new Date().getTime();
this.http.post(environment.baseUrl + 'cms/offline',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 + 'cms/delete',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 + 'cms/queryList?'+ObjectData.objectByString(params)).subscribe(data => {
callBack(data);
});
}
}

@ -0,0 +1,104 @@
import { Injectable } from '@angular/core';
import {HttpClient} from "@angular/common/http";
import {environment} from "../../../environments/environment";
import {ObjectData} from "../../utils/objectData.service";
import {NzUploadFile} from "ng-zorro-antd/upload";
@Injectable({
providedIn: 'root'
})
export class CommonService {
constructor(private http: HttpClient) { }
/**
*
*
* @param codeType
* @param codeValue
* @param callBack
*/
public queryDictionary(codeType: string, codeValue: string, callBack:any) {
const param = {
codeType: codeType,
codeValue: codeValue
};
this.http.get(environment.baseUrl + 'common/queryDictionary?'+ObjectData.objectByString(param)).subscribe(data => {
callBack(data);
});
}
/**
*
*
* @param regionId id
* @param callBack
*/
public getRegion(regionId: string, callBack:any) {
this.http.get(environment.baseUrl + 'common/getRegion?regionId=' + regionId).subscribe(data => {
callBack(data);
});
}
/**
*
*
* @param imgData
*/
public imgList(imgData: NzUploadFile[]): string {
if (imgData[0]['response'] != null) {
return imgData[0]['response']['return_data'][0];
} else {
return imgData[0].name;
}
}
/**
*
*
* @param imgArray
*/
public postImg(imgArray: any): any {
const array = [];
for (const i of imgArray) {
if (i['response'] != null) {
array.push(i['response']['return_data'][0]);
} else {
array.push(i.name);
}
}
return array.join(',');
}
/**
*
*
* @param imgUrl
*/
public stitchImg(imgUrl: string): any {
const imgArray = [];
for (const i of imgUrl.split(',')) {
imgArray.push(
{
uid: 1,
name: i,
status: 'done',
url: environment.imageUrl + i
});
}
console.log(imgArray);
return imgArray;
}
// 判断字符串是否为空
public whetherStringIsNull(s: string): boolean {
if (s != null && s !== '') {
return false;
} else {
return true;
}
}
}

@ -0,0 +1,47 @@
// current locale is key of the MyErrorsOptions
import {AbstractControl, ValidatorFn, Validators} from "@angular/forms";
import {NzSafeAny} from "ng-zorro-antd/core/types";
export type MyErrorsOptions = { 'zh-cn': string; en: string } & Record<string, NzSafeAny>;
export type MyValidationErrors = Record<string, MyErrorsOptions>;
export class MyValidators extends Validators {
static override minLength(minLength: number): ValidatorFn {
return (control: AbstractControl): MyValidationErrors | null => {
if (Validators.minLength(minLength)(control) === null) {
return null;
}
return { minlength: { 'zh-cn': `最小长度为 ${minLength}`, en: `MinLength is ${minLength}` } };
};
}
static override maxLength(maxLength: number): ValidatorFn {
return (control: AbstractControl): MyValidationErrors | null => {
if (Validators.maxLength(maxLength)(control) === null) {
return null;
}
return { maxlength: { 'zh-cn': `最大长度为 ${maxLength}`, en: `MaxLength is ${maxLength}` } };
};
}
static mobile(control: AbstractControl): MyValidationErrors | null {
const value = control.value;
if (isEmptyInputValue(value)) {
return null;
}
return isMobile(value)
? null
: { mobile: { 'zh-cn': `手机号码格式不正确`, en: `Mobile phone number is not valid` } };
}
}
function isEmptyInputValue(value: NzSafeAny): boolean {
return value == null || value.length === 0;
}
function isMobile(value: string): boolean {
return typeof value === 'string' && /(^1\d{10}$)/.test(value);
}

@ -0,0 +1,38 @@
import { Injectable } from '@angular/core';
import {HttpClient} from "@angular/common/http";
import {environment} from "../../../environments/environment";
@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 + 'secUser/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 + 'secUser/loginOut', null).subscribe(data => {
callBack(data);
});
}
}

@ -0,0 +1,120 @@
import { Injectable } from '@angular/core';
import {HttpClient} from "@angular/common/http";
import {environment} from "../../../environments/environment";
import {ObjectData} from "../../utils/objectData.service";
import {DATA} from "../../data/login/localStorage.namespace";
@Injectable({
providedIn: 'root'
})
export class MenuService {
constructor(private http: HttpClient) { }
/**
*
* @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 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 menuId
* @param callBack
*/
public queryMenuDetail(menuId: number, callBack:any) {
let params = {
menuId: menuId,
time: new Date().getTime()
};
this.http.get(environment.baseUrl + 'secMenu/queryDetail?' + ObjectData.objectByString(params)).subscribe(data => {
callBack(data);
});
}
/**
*
* @param roleId
* @param callBack
*/
public queryRoleMenuTree(roleId: string, callBack:any) {
let paramStr = '?dt='+new Date().getTime();
if (roleId != null && roleId != '') {
paramStr = '&roleId=' + roleId;
}
this.http.get(environment.baseUrl + 'secMenu/queryRoleMenuTree'+paramStr).subscribe(data => {
callBack(data);
});
}
/**
*
* @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 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);
});
}
/**
*
* @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);
});
}
}

@ -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,88 @@
import {Injectable} from '@angular/core';
import {
HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpResponse, HttpHeaders
} from '@angular/common/http';
import {catchError, finalize} from 'rxjs/operators';
import {mergeMap} from 'rxjs/operators';
import {Observable} from 'rxjs';
import {USER_TOKEN} from "../data/login/localStorage.namespace";
import {BrowserStorageService} from "./localStorage.service";
import {Router} from "@angular/router";
@Injectable()
export class InterceptorService implements HttpInterceptor {
constructor(
private router: Router, // 路由
private storage: BrowserStorageService,
) {
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
let secureReq: HttpRequest<any>;
let modifiedHeaders: HttpHeaders;
const token = this.storage.get(USER_TOKEN);
if (token != null) {
modifiedHeaders =
req.headers.append('Cache-Control', 'no-cache')
.append('Pragma', 'no-cache')
.append('Authorization', token);
}
secureReq = req.clone({
url: req.url,
// @ts-ignore
headers: modifiedHeaders
});
// @ts-ignore
return next.handle(secureReq)
.pipe(
catchError((res: HttpResponse<any>) => {
let msg = '';
switch (res.status) {
case 401:
msg = '身份验证过期,请重新进入页面';
break;
case 200:
msg = '身份验证过期,请重新进入页面';
break;
case 404:
msg = '找不到地址';
break;
case 403:
msg = '业务错误';
break;
case 500:
msg = '服务器发生错误,请重试';
break;
}
this.jumpError(msg , res.status);
return Observable.create(res);
}),
finalize(() => {
}),
mergeMap(
// 有响应时成功;忽略其他事件
(event: any) => {
return Observable.create((observer: { next: (arg0: any) => any; }) => observer.next(event));
}),
);
}
jumpError(message: string , status: number) {
if (status == 401) {
this.router.navigateByUrl('/error/401').then(r => console.log("返回内容" ,r));
}
if (status == 403) {
this.router.navigateByUrl('/error/403').then(r => console.log("返回内容" ,r));
}
if (status == 404) {
this.router.navigateByUrl('/error/404').then(r => console.log("返回内容" ,r));
}
if (status == 500) {
this.router.navigateByUrl('/error/500').then(r => console.log("返回内容" ,r));
}
}
}

@ -0,0 +1,139 @@
/**
*
*/
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 startTime
* @param endTime
*/
public static getDateSelectType(startTime: Date, endTime: Date): number {
const type = 0; // 未知
for (let type of DateSelectType) {
let date = this.getDate(new Date(), type.value);
if (startTime.toLocaleDateString() == new Date(date.timeS).toLocaleDateString()
&& endTime.toLocaleDateString() == new Date(date.timeE).toLocaleDateString()) {
return type.value;
}
}
return type;
}
}

@ -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 {BrowserStorageService} from "./localStorage.service";
@Injectable()
export class InitGuardService implements CanActivate {
constructor(
private storage: BrowserStorageService,
private router: Router,
) { }
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
const init = !!this.storage.get(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,32 @@
import { Inject, Injectable, InjectionToken } from '@angular/core';
export const BROWSER_STORAGE = new InjectionToken<Storage>('Browser Storage', {
providedIn: 'root',
factory: () => localStorage
});
@Injectable({
providedIn: 'root'
})
export class BrowserStorageService {
constructor(@Inject(BROWSER_STORAGE) public storage: Storage) {}
get(key: string) {
// @ts-ignore
return JSON.parse(this.storage.getItem(key)) as T;
}
set(key: string, value: any) {
if (!value && value === undefined) { return; }
const arr = JSON.stringify(value);
this.storage.setItem(key, arr);
}
remove(key: string) {
this.storage.removeItem(key);
}
clear() {
this.storage.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);
}
}

@ -0,0 +1,65 @@
import {AbstractControl, ValidationErrors, ValidatorFn, Validators} from '@angular/forms';
import {NzSafeAny} from 'ng-zorro-antd/core/types';
export type validatorsErrorsOptions = { 'zh-cn': string; en: string } & Record<string, NzSafeAny>;
export type validatorsErrors = Record<string, validatorsErrorsOptions>;
export class ValidatorsService extends Validators {
/**
*
* @param minLength
*/
static override minLength(minLength: number): ValidatorFn {
return (control: AbstractControl): validatorsErrors | null => {
if (Validators.minLength(minLength)(control) === null) {
return null;
}
return { minlength: { 'zh-cn': `最小长度为 ${minLength}`, en: `MinLength is ${minLength}` } };
};
}
/**
*
* @param maxLength
*/
static override maxLength(maxLength: number): ValidatorFn {
return (control: AbstractControl): validatorsErrors | null => {
if (Validators.maxLength(maxLength)(control) === null) {
return null;
}
return { maxlength: { 'zh-cn': `最大长度为 ${maxLength}`, en: `MaxLength is ${maxLength}` } };
};
}
/**
*
* @param control
*/
static mobile(control: AbstractControl): validatorsErrors | null {
const value = control.value;
if (isEmptyInputValue(value)) {
return null;
}
return isMobile(value) ? null : { mobile: { 'zh-cn': `手机号码格式不正确`, en: `Mobile phone number is not valid` } };
}
static isMobile(value: string): boolean {
return typeof value === 'string' && /^[1](([3][0-9])|([4][0,1,4-9])|([5][0-3,5-9])|([6][2,5,6,7])|([7][0-8])|([8][0-9])|([9][0-3,5-9]))[0-9]{8}$/.test(value);
}
}
function isEmptyInputValue(value: NzSafeAny): boolean {
return value == null || value.length === 0;
}
function isMobile(value: string): boolean {
return typeof value === 'string' && /^[1](([3][0-9])|([4][0,1,4-9])|([5][0-3,5-9])|([6][2,5,6,7])|([7][0-8])|([8][0-9])|([9][0-3,5-9]))[0-9]{8}$/.test(value);
}

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

@ -0,0 +1,5 @@
export const environment = {
production: false,
baseUrl: 'https://test-oil.dctpay.com/brest/', // 测试环境服务器地址(请求数据地址)
imageUrl: 'https://test-oil.dctpay.com/filesystem/',
};

@ -0,0 +1,5 @@
export const environment = {
production: true,
baseUrl: 'https://oil.dctpay.com/brest/', // 测试环境服务器地址(请求数据地址)
imageUrl: 'https://oil.dctpay.com/filesystem/',
};

@ -0,0 +1,18 @@
// 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: 'http://localhost:9850/server/', // 测试环境服务器地址(请求数据地址)
imageUrl: 'https://oa.dctpay.com/filesystem/',
};
/*
* 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.

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>OA审批</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,15 @@
// Custom Theming for NG-ZORRO
// For more information: https://ng.ant.design/docs/customize-theme/en
@import "../node_modules/ng-zorro-antd/ng-zorro-antd.less";
body, html {
margin: 0;
padding: 0;
height: 100%;
overflow: hidden;
}
// Override less variables to here
// View all variables: https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/components/style/themes/default.less
// @primary-color: #1890ff;
/* You can add global styles to this file, and also import other style files */

@ -0,0 +1,18 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/app",
"types": [
"node"
]
},
"files": [
"src/main.ts",
"src/main.server.ts",
"server.ts"
],
"include": [
"src/**/*.d.ts"
]
}

@ -0,0 +1,33 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"compileOnSave": false,
"compilerOptions": {
"outDir": "./dist/out-tsc",
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"skipLibCheck": true,
"esModuleInterop": true,
"sourceMap": true,
"declaration": false,
"experimentalDecorators": true,
"moduleResolution": "node",
"importHelpers": true,
"target": "ES2022",
"module": "ES2022",
"useDefineForClassFields": false,
"lib": [
"ES2022",
"dom"
]
},
"angularCompilerOptions": {
"enableI18nLegacyMessageIdFormat": false,
"strictInjectionParameters": true,
"strictInputAccessModifiers": true,
"strictTemplates": true
}
}

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

Loading…
Cancel
Save