parent
2f48b1e542
commit
e80cd5fe41
@ -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 |
||||
@ -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" |
||||
} |
||||
} |
||||
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
|
After Width: | Height: | Size: 68 KiB |
|
After Width: | Height: | Size: 76 KiB |
|
After Width: | Height: | Size: 68 KiB |
|
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.
|
||||
|
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…
Reference in new issue