40 Commits

Author SHA1 Message Date
lyt
999429ce13 'admin-23.04.12:同步master分支v2.4.33版本内容' 2023-04-12 11:51:49 +08:00
lyt
1d11c00f91 'admin-23.03.26:同步master分支v2.4.32版本内容' 2023-03-26 19:23:01 +08:00
lyt
a67f379a4d 'admin-23.03.10:同步master分支v2.4.31版本内容' 2023-03-10 23:17:26 +08:00
lyt
edaeb4bc2f 'admin-23.02.27:优化分栏布局' 2023-02-27 23:28:43 +08:00
lyt
d1c47e6e22 'admin-23.02.23:同步master分支v2.4.3版本内容,感谢赞助商.驰骋工作流引擎-表单引擎-低代码开发平台' 2023-02-23 00:13:35 +08:00
lyt
646b228125 'admin-22.12.10:同步master分支v2.4.21版本内容,具体查看masterCHANGELOG.md' 2022-12-12 13:22:41 +08:00
lyt
8a5919c0c4 'admin-22.12.10:同步master分支v2.4.2版本内容,具体查看masterCHANGELOG.md' 2022-12-10 10:22:15 +08:00
lyt
c35077eef3 'admin-22.12.01:同步master分支v2.4.1版本内容,具体查看masterCHANGELOG.md' 2022-12-01 00:55:43 +08:00
lyt
5f5b0e9c6e 'admin-22.11.19:修复v2.3.0版本动态路由事件调用关闭当前tagsview、普通路由刷新界面参数丢失问题' 2022-11-19 00:15:32 +08:00
lyt
b7b08dfb4e 'admin-22.11.18:优化v2.3.0版本tagsview风格5兼容火狐' 2022-11-18 02:24:04 +08:00
lyt
18da902d2c 'admin-22.11.17:优化v2.3.0版本iframe右键菜单刷新问题' 2022-11-17 08:28:37 +08:00
lyt
a3bacd66f7 'admin-22.11.17:优化v2.3.0版本iframe右键菜单刷新及loading' 2022-11-17 01:28:38 +08:00
lyt
f782d437cd 'admin-22.11.17:修复v2.3.0版本iframe右键菜单刷新' 2022-11-17 00:52:19 +08:00
76297872c8 'admin-22.11.17:修复v2.3.0版本iframe右键菜单刷新'
Signed-off-by: lyt-Top <1105290566@qq.com>
2022-11-16 16:41:32 +00:00
lyt
1fa0d67637 'admin-22.11.16:同步master分支v2.3.0版本内容' 2022-11-16 17:57:38 +08:00
lyt
1c6f5b7d42 'admin-22.07.20:降级vite@2.9.14,防止打包样式丢掉问题' 2022-07-20 23:25:28 +08:00
lyt
56a64b1382 'admin-22.07.16:修复行内表单最后一个el-form-item位置下移问题' 2022-07-16 22:51:20 +08:00
lyt
9bc002602b 'admin-22.07.11:同步master分支v2.2.0版本' 2022-07-11 21:20:11 +08:00
5316fcb159 update README.md. 2022-06-07 14:37:13 +00:00
lyt
db69523bf0 'admin-22.06.07:函数deepClone对null的判断错误' 2022-06-07 22:32:32 +08:00
lyt
f1b2623ad3 'admin-22.06.06:添加大佬集成后端链接' 2022-06-06 22:22:16 +08:00
lyt
1c9758eb27 Merge branch 'vue-next-admin-template' of https://gitee.com/lyt-top/vue-next-admin into vue-next-admin-template 2022-06-05 21:57:42 +08:00
lyt
7f67399013 'admin-22.06.05:去国际化问题' 2022-06-05 21:57:22 +08:00
e809490703 update package.json. 2022-06-05 13:40:07 +00:00
lyt
187be009fa 'admin-22.06.05:同步master分支v2.1.1版本内容,具体查看master/CHANGELOG.md' 2022-06-05 16:19:18 +08:00
06dd89be46 !21 修复组件切换大小问题
Merge pull request !21 from 小李/vue-next-admin-template
2022-03-07 11:46:02 +00:00
7cfccaa255 修复组件切换大小问题 2022-03-07 10:26:56 +08:00
lyt
79482ddbea 'admin-22.03.04:同步master分支v2.0.2版本内容,具体查看master的CHANGELOG.md' 2022-03-04 18:26:27 +08:00
lyt
2ec65911ee admin-21.12.23:修复#I4NS3Q 2021-12-23 20:30:26 +08:00
lyt
5a50d3c681 'admin-21.12.22:优化打包时警告问题' 2021-12-22 21:39:41 +08:00
c1b4cf75ee update vite.config.ts. 2021-12-22 03:26:02 +00:00
2409dc2c3c 'admin-21.12.21:更新优化内容请看CHANGELOG.md文件' 2021-12-21 13:53:21 +00:00
lyt
a4c436a227 'admin-21.12.21:更新优化内容请看CHANGELOG.md文件' 2021-12-21 21:52:20 +08:00
lyt
234bd07f45 'admin-21.12.17:修复tagsView拖拽问题,感谢@简单爱、' 2021-12-17 20:28:22 +08:00
lyt
348933c5cf 'admin-21.12.12:更新修复查看CHANGELOG.md文件' 2021-12-12 20:36:00 +08:00
lyt
cc6aa94f53 'admin-21.12.12:更新修复查看CHANGELOG.md文件' 2021-12-12 20:28:25 +08:00
lyt
1ef8ca8757 'admin-21.12.07:修复router/index.ts中hasRoles未同步master,感谢@小刘' 2021-12-07 20:23:17 +08:00
lyt
6de2a537c3 'admin-21.12.04:同步master分支v1.2.0版本内容,具体查看masterCHANGELOG.md' 2021-12-04 15:11:30 +08:00
lyt
5fdef5fc69 'admin-21.10.18:优化tagsview问题' 2021-10-18 20:31:11 +08:00
lyt
4bca8db62b 'admin-21.10.17:基础模板ts(不带国际化)第一次提交' 2021-10-17 20:02:57 +08:00
265 changed files with 14250 additions and 13390 deletions

11
.env Normal file
View File

@ -0,0 +1,11 @@
# port 端口号
VITE_PORT = 8888
# open 运行 npm run dev 时自动打开浏览器
VITE_OPEN = false
# 打包是否开启 cdn源文件 utils/build.ts可自行修改
VITE_OPEN_CDN = false
# public path 配置线上环境路径(打包)、本地通过 http-server 访问时,请置空即可
VITE_PUBLIC_PATH =

5
.env.development Normal file
View File

@ -0,0 +1,5 @@
# 本地环境
ENV = development
# 本地环境接口地址
VITE_API_URL = http://localhost:8888/

5
.env.production Normal file
View File

@ -0,0 +1,5 @@
# 线上环境
ENV = production
# 线上环境接口地址
VITE_API_URL = https://lyt-top.gitee.io/vue-next-admin-preview/

View File

@ -13,9 +13,18 @@ module.exports = {
},
extends: ['plugin:vue/vue3-essential', 'plugin:vue/essential', 'eslint:recommended'],
plugins: ['vue', '@typescript-eslint'],
overrides: [
{
files: ['*.ts', '*.tsx', '*.vue'],
rules: {
'no-undef': 'off',
},
},
],
rules: {
// http://eslint.cn/docs/rules/
// https://eslint.vuejs.org/rules/
// https://typescript-eslint.io/rules/no-unused-vars/
'@typescript-eslint/ban-ts-ignore': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/no-explicit-any': 'off',
@ -26,6 +35,9 @@ module.exports = {
'@typescript-eslint/ban-types': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-redeclare': 'error',
'@typescript-eslint/no-non-null-asserted-optional-chain': 'off',
'@typescript-eslint/no-unused-vars': [2],
'vue/custom-event-name-casing': 'off',
'vue/attributes-order': 'off',
'vue/one-component-per-file': 'off',
@ -59,5 +71,6 @@ module.exports = {
'no-v-model-argument': 'off',
'no-case-declarations': 'off',
'no-console': 'error',
'no-redeclare': 'off',
},
};

32
.gitignore vendored
View File

@ -1,11 +1,23 @@
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace
.DS_Store
node_modules
/dist
# Local History for Visual Studio Code
.history/
frontend/LICENSE
*.zip
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

94
CHANGELOG.md Normal file
View File

@ -0,0 +1,94 @@
# <a href="https://gitee.com/lyt-top/vue-next-admin" target="_blank">vue-next-admin-template不带国际化 更新日志</a>
🎉🎉🔥 `vue-next-admin-template` 基于 vue-next-admin-v2.4.33 版本) vue3.x 、Typescript、vite、Element plus 等适配手机、平板、pc 的后台开源免费模板库vue2.x 请切换 vue-prev-admin 分支)
## 2.4.33
`2023.04.12`
- 🎉 同步 master 分支 v2.4.33 版本内容,具体查看 [master CHANGELOG.md](https://gitee.com/lyt-top/vue-next-admin/blob/master/CHANGELOG.md)
## 2.4.32
`2023.03.26`
- 🎉 同步 master 分支 v2.4.32 版本内容,具体查看 [master CHANGELOG.md](https://gitee.com/lyt-top/vue-next-admin/blob/master/CHANGELOG.md)
## 2.4.31
`2023.03.10`
- 🎉 同步 master 分支 v2.4.31 版本内容,具体查看 [master CHANGELOG.md](https://gitee.com/lyt-top/vue-next-admin/blob/master/CHANGELOG.md)
## 2.4.3
`2023.02.22`
🚩🚩🚩 感谢 [驰骋工作流引擎-表单引擎-低代码开发平台](http://www.ccflow.org/) 赞助商的赞助。驰骋公司为社会提供流程引擎+表单引擎+低代码开发平台一体的开源软件解决方案,欢迎广大开发者前去体验!
- 🎉 同步 master 分支 v2.4.3 版本内容,具体查看 [master CHANGELOG.md](https://gitee.com/lyt-top/vue-next-admin/blob/master/CHANGELOG.md)
## 2.4.21
`2022.12.12`
- 🎉 同步 master 分支 v2.4.21 版本内容,具体查看 [master CHANGELOG.md](https://gitee.com/lyt-top/vue-next-admin/blob/master/CHANGELOG.md)
## 2.4.2
`2022.12.10`
- 🎉 同步 master 分支 v2.4.2 版本内容,具体查看 [master CHANGELOG.md](https://gitee.com/lyt-top/vue-next-admin/blob/master/CHANGELOG.md)
## 2.4.1
`2022.11.30`
- 🎉 同步 master 分支 v2.4.1 版本内容,具体查看 [master CHANGELOG.md](https://gitee.com/lyt-top/vue-next-admin/blob/master/CHANGELOG.md)
## 2.3.0
`2022.11.16`
- 🎉 同步 master 分支 v2.3.0 版本内容,具体查看 [master CHANGELOG.md](https://gitee.com/lyt-top/vue-next-admin/blob/master/CHANGELOG.md)
## 2.2.0
`2022.07.11`
- 🎉 同步 master 分支 v2.2.0 版本内容,具体查看 [master CHANGELOG.md](https://gitee.com/lyt-top/vue-next-admin/blob/master/CHANGELOG.md)
## 2.1.1
- 🎉 同步 master 分支 v2.1.1 版本内容,具体查看 [master CHANGELOG.md](https://gitee.com/lyt-top/vue-next-admin/blob/master/CHANGELOG.md)
## 2.0.2
- 🎉 同步 master 分支 v2.0.2 版本内容,具体查看 master CHANGELOG.md
## 0.2.2
`2021.12.21`
- 🎉 同步 master 分支 v1.2.2 版本内容,具体查看 master CHANGELOG.md
## 0.2.1
`2021.12.12`
- 🌟 更新 依赖更新最新版本
- 🐞 修复 浏览器标题问题
- 🐞 修复 element plus svg 图标引入
- 🐞 修复 默认显示英文问题,改成默认显示中文
## 0.2.0
`2021.12.04`
- 🎉 同步 master 分支 v1.2.0 版本内容,具体查看 master CHANGELOG.md
## 0.1.0
`2021.10.17`
- 🎉 新增 vue-next-admin-template 基础版本(不带国际化),切换 `vue-next-admin-template` 分支

View File

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2022 倔强嘴角留下一抹殇
Copyright (c) 2021 lyt-Top
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
SOFTWARE.

File diff suppressed because one or more lines are too long

166
README.md

File diff suppressed because one or more lines are too long

View File

@ -1,25 +0,0 @@
module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
project: 'tsconfig.json',
tsconfigRootDir : __dirname,
sourceType: 'module',
},
plugins: ['@typescript-eslint/eslint-plugin'],
extends: [
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
],
root: true,
env: {
node: true,
jest: true,
},
ignorePatterns: ['.eslintrc.js'],
rules: {
'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-explicit-any': 'off',
},
};

37
backend/.gitignore vendored
View File

@ -1,37 +0,0 @@
# compiled output
/dist
/node_modules
# Logs
logs
*.log
npm-debug.log*
pnpm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
package-lock.json
# OS
.DS_Store
# Tests
/coverage
/.nyc_output
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.lock

View File

@ -1,4 +0,0 @@
{
"singleQuote": true,
"trailingComma": "all"
}

View File

@ -1,5 +0,0 @@
{
"$schema": "https://json.schemastore.org/nest-cli",
"collection": "@nestjs/schematics",
"sourceRoot": "src"
}

View File

@ -1,90 +0,0 @@
{
"name": "tsplatform",
"version": "1.1.0",
"description": "nestjs backend",
"author": "SweetHoney",
"private": true,
"license": "UNLICENSED",
"scripts": {
"prebuild": "rimraf dist",
"build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "nest start",
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json"
},
"dependencies": {
"@fastify/static": "^6.5.0",
"@nestjs/common": "^9.0.0",
"@nestjs/config": "^2.2.0",
"@nestjs/core": "^9.0.0",
"@nestjs/jwt": "^9.0.0",
"@nestjs/platform-express": "^9.0.0",
"@nestjs/platform-fastify": "^9.2.0",
"@nestjs/swagger": "^6.1.3",
"@nestjs/typeorm": "^9.0.1",
"class-transformer": "^0.5.1",
"class-validator": "^0.13.2",
"cookie-parser": "^1.4.6",
"csurf": "^1.11.0",
"express-rate-limit": "^6.7.0",
"fastify-swagger": "^5.2.0",
"fs": "^0.0.1-security",
"helmet": "^6.0.0",
"mysql2": "^2.3.3",
"passport-jwt": "^4.0.0",
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2",
"rxjs": "^7.2.0",
"ts-md5": "^1.3.1",
"typeorm": "^0.3.10"
},
"devDependencies": {
"@nestjs/cli": "^9.0.0",
"@nestjs/schematics": "^9.0.0",
"@nestjs/testing": "^9.0.0",
"@types/express": "^4.17.13",
"@types/jest": "28.1.8",
"@types/node": "^16.0.0",
"@types/passport-jwt": "^3.0.7",
"@types/supertest": "^2.0.11",
"@typescript-eslint/eslint-plugin": "^5.0.0",
"@typescript-eslint/parser": "^5.0.0",
"eslint": "^8.0.1",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
"jest": "28.1.3",
"prettier": "^2.3.2",
"source-map-support": "^0.5.20",
"supertest": "^6.1.3",
"ts-jest": "28.0.8",
"ts-loader": "^9.2.3",
"ts-node": "^10.0.0",
"tsconfig-paths": "4.1.0",
"typescript": "^4.7.4"
},
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "src",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node"
}
}

View File

@ -1,47 +0,0 @@
/*
* @Author: '490912587@qq.com' '490912587@qq.com'
* @Date: 2022-11-17 10:59:16
* @LastEditors: '490912587@qq.com' '490912587@qq.com'
* @LastEditTime: 2022-11-24 10:58:50
* @FilePath: \tsplatform\backend\src\Config\Index.ts
* @Description: 系统全局配置
*/
/**
* 系统配置
*/
export const AppConfig = {
debug: false,
port: 3000,
jwt: {
secret: "4vRk^ga52xVP$B2vYK$%r8a8hctLgbU9",
expiresIn: "60000s"
},
md5Key: `4vRk^ga52xVP$B2vYK$%r8a8hctLgbU9`
}
/**
* 数据库配置
*/
export const DataBaseConfig = {
DataBase: {
type: 'mysql',
host: '127.0.0.1',
port: 3306,
database: 'sweet_honey',
username: 'root',
password: 'root',
//是否自动迁移同步
synchronize: true,
},
DataBase1: {
type: 'mysql',
host: '127.0.0.1',
port: 3306,
database: 'test1',
username: 'root',
password: 'root',
//是否自动迁移同步
synchronize: true,
}
}

View File

@ -1,123 +0,0 @@
/*
* @Author: '490912587@qq.com' '490912587@qq.com'
* @Date: 2022-11-17 17:06:21
* @LastEditors: '490912587@qq.com' '490912587@qq.com'
* @LastEditTime: 2022-11-24 11:11:11
* @FilePath: \tsplatform\backend\src\Controller\AuthController.ts
* @Description: 系统授权控制器
*/
import { Body, Controller, Get, Post, Request, Req, UseGuards } from '@nestjs/common';
import { RestfulReturn } from 'src/Expand/RestfulReturn';
import {
ApiBearerAuth,
ApiOperation,
ApiTags,
} from '@nestjs/swagger';
import { UserService } from 'src/Service/UserService';
import { RestfulHttpCodeEnum } from 'src/Enum/Global';
import { JwtService } from '@nestjs/jwt';
import { LoginDto } from 'src/EntiyDto/LoginDto';
import { AppConfig } from 'src/Config/Index';
import { RoleService } from 'src/Service/RoleService';
import { MenuService } from 'src/Service/MenuService';
@ApiBearerAuth()
@ApiTags('系统授权管理')
@Controller('/auth')
export class AuthController {
constructor(
public readonly _restful: RestfulReturn,
public readonly _service: UserService,
public readonly _roleService: RoleService,
public readonly _menuService: MenuService,
public readonly _jwt: JwtService
) { }
@ApiOperation({
summary: '获取账号登录验证码',
description: '获取账号登录验证码'
})
@Get('/captcha')
async captcha() {
return await this._restful.toJson(
RestfulHttpCodeEnum.SUCCESS, ""
);
}
@ApiOperation({
summary: '账号密码登录',
description: '账号密码登录'
})
@Post('/login')
async login(@Body() dto: LoginDto) {
const result = await this._service.repository.findOne({ where: { UserName: dto.UserName, Password: dto.Password } });
if (result && result.Id > 0) {
const token = await this._jwt.signAsync({ uid: result.Id }, {
secret: AppConfig.jwt.secret,
expiresIn: AppConfig.jwt.expiresIn,
});
return await this._restful.toJson(
RestfulHttpCodeEnum.SUCCESS, { userinfo: result, token: token }
);
} else {
return await this._restful.toJson(
RestfulHttpCodeEnum.FAIL, "账号密码有误!"
);
}
}
@ApiOperation({
summary: '根据token获取用户信息',
description: '根据token获取用户信息'
})
@Get('/getUserInfo')
async getUserInfo(@Req() request: Request) {
try {
const token = request.headers["authorization"];
const jwtResult = await this._jwt.verifyAsync(token, { secret: AppConfig.jwt.secret, ignoreExpiration: false })
const uid = jwtResult.uid;
const result = await this._service.getById(uid);
const menuRole = await this._roleService.getMenusByRoleId(result.RoleId);
const menuIds: number[] = [];
for await (const info of menuRole) {
menuIds.push(info.MenuId);
}
const menus = await this._menuService.repository.createQueryBuilder().whereInIds(menuIds).getMany()
const permissions: string[] = [];
for await (const info of menus) {
permissions.push(info.Permission);
}
return await this._restful.toJson(
RestfulHttpCodeEnum.SUCCESS, { userInfo: result, permission: permissions }
);
}
catch (ex) {
return await this._restful.toJson(
RestfulHttpCodeEnum.FAIL, ex
);
}
}
@ApiOperation({
summary: '根据token退出登录',
description: '根据token退出登录'
})
@Get('/loginOut')
async loginOut(@Req() request: Request) {
try {
const token = request.headers["authorization"];
const jwtResult = await this._jwt.verifyAsync(token, { secret: AppConfig.jwt.secret, ignoreExpiration: false })
const uid = jwtResult.uid;
const result = await this._service.getById(uid);
return await this._restful.toJson(
RestfulHttpCodeEnum.SUCCESS, result
);
}
catch (ex) {
return await this._restful.toJson(
RestfulHttpCodeEnum.FAIL, ex
);
}
}
}

View File

@ -1,143 +0,0 @@
/*
* @Author: '490912587@qq.com' '490912587@qq.com'
* @Date: 2022-11-17 17:05:40
* @LastEditors: '490912587@qq.com' '490912587@qq.com'
* @LastEditTime: 2022-11-24 11:11:20
* @FilePath: \tsplatform\backend\src\Controller\RoleController.ts
* @Description: 省市区控制器
*/
import { Body, Controller, Get, Post, Query } from '@nestjs/common';
import { RestfulReturn } from 'src/Expand/RestfulReturn';
import {
ApiBearerAuth,
ApiOperation,
ApiTags,
} from '@nestjs/swagger';
import { IdDto, IdsDto, ListDto, PageDto } from 'src/EntiyDto/CommonDto';
import { RestfulHttpCodeEnum } from 'src/Enum/Global';
import { CityService } from 'src/Service/CityService';
import { SysCity } from 'src/Entity/SysCity';
import { Equal, Not } from 'typeorm';
@ApiBearerAuth()
@ApiTags('省市区管理')
@Controller('/city')
export class CityController {
constructor(
public readonly restful: RestfulReturn,
public readonly service: CityService
) {
}
//#region 基础控制器
@ApiOperation({
summary: '分页查询省市区列表',
description: '分页查询省市区列表'
})
@Get('/getPage')
async getPage(@Query() dto: PageDto) {
const result = await this.service.getPage(dto.Page, dto.PageSize, dto.Keywords, dto.OrderBy);
return await this.restful.toJson(
RestfulHttpCodeEnum.SUCCESS,
{
total: result[1],
page: 1,
pageSize: 10,
rows: result[0],
}
);
}
@ApiOperation({
summary: '查询省市区列表',
description: '查询省市区列表'
})
@Get('/getList')
async getList(@Query() dto: ListDto) {
const result = await this.service.getList(dto.Keywords, dto.OrderBy);
return await this.restful.toJson(
RestfulHttpCodeEnum.SUCCESS, result
);
}
@ApiOperation({
summary: '根据ID查询省市区',
description: '根据ID查询省市区'
})
@Get('/getById')
async getById(@Query() dto: IdDto) {
const result = await this.service.getById(dto.Id);
return await this.restful.toJson(
RestfulHttpCodeEnum.SUCCESS, result
);
}
@ApiOperation({
summary: '根据ID数组查询省市区',
description: '根据ID数组查询省市区'
})
@Get('/getByIds')
async getByIds(@Query() dto: IdsDto) {
const result = await this.service.getByIds(dto.Ids);
return await this.restful.toJson(
RestfulHttpCodeEnum.SUCCESS, result
);
}
@ApiOperation({
summary: '新增省市区',
description: '新增省市区'
})
@Post('/add')
async add(@Body() model: SysCity) {
const isRepeat = await this.service.repository.createQueryBuilder()
.orWhere({ RegionName: Equal(model.RegionName.trim()), })
.orWhere({ RegionCode: Equal(model.RegionCode.trim()) }).getCount();
if (isRepeat > 0) {
return await this.restful.toJson(
RestfulHttpCodeEnum.FAIL, "地区名称或者行政编号重复!"
);
} else {
const result = await this.service.save(new SysCity());
return await this.restful.toJson(
RestfulHttpCodeEnum.SUCCESS, result
);
}
}
@ApiOperation({
summary: '修改省市区',
description: '修改省市区'
})
@Post('/edit')
async edit(@Body() model: SysCity) {
const isRepeat = await this.service.repository.createQueryBuilder()
.where({ Id: Not(model.Id) })
.andWhere([{ RegionName: Equal(model.RegionName.trim()) }, { RegionCode: Equal(model.RegionCode.trim()) }])
.getCount();
if (isRepeat > 0) {
return await this.restful.toJson(
RestfulHttpCodeEnum.FAIL, "地区名称或者行政编号重复!"
);
} else {
const result = await this.service.save(new SysCity());
return await this.restful.toJson(
RestfulHttpCodeEnum.SUCCESS, result
);
}
}
@ApiOperation({
summary: '假删除省市区',
description: '假删除省市区'
})
@Post('/del')
async del(@Body() dto: IdDto) {
const result = await this.service.destroy(dto.Id);
return await this.restful.toJson(
RestfulHttpCodeEnum.SUCCESS, result
);
}
//#endregion
}

View File

@ -1,57 +0,0 @@
/*
* @Author: '490912587@qq.com' '490912587@qq.com'
* @Date: 2022-11-17 17:05:41
* @LastEditors: '490912587@qq.com' '490912587@qq.com'
* @LastEditTime: 2022-11-24 10:11:01
* @FilePath: \tsplatform\backend\src\Controller\UserController.ts
* @Description: 系统用户控制器
*/
import { Controller, Get } from '@nestjs/common';
import { RestfulReturn } from 'src/Expand/RestfulReturn';
import {
ApiBearerAuth,
ApiOperation,
ApiTags,
} from '@nestjs/swagger';
import { RestfulHttpCodeEnum } from 'src/Enum/Global';
import { CityService } from 'src/Service/CityService';
import { RoleService } from 'src/Service/RoleService';
import { MenuService } from 'src/Service/MenuService';
import { UserService } from 'src/Service/UserService';
@ApiBearerAuth()
@ApiTags('系统管理')
@Controller('/')
export class IndexController {
constructor(
public readonly restful: RestfulReturn,
public readonly role_service: RoleService,
public readonly menu_service: MenuService,
public readonly user_service: UserService,
public readonly city_service: CityService,
) {
}
@ApiOperation({
summary: '数据初始化',
description: '数据初始化'
})
@Get('/seed')
async seedData() {
//初始化账号数据
const userCount = await this.user_service.seedData();
//初始化菜单数据
const menuCount = await this.menu_service.seedData();
//初始化角色数据
const roleCounts = await this.role_service.seedData();
//初始化省市区数据
const cityCount = await this.city_service.seedData();
return await this.restful.toJson(
RestfulHttpCodeEnum.SUCCESS,
`一共Seed${userCount}条记录!\r\n` +
`一共Seed${menuCount}条记录!\r\n` +
`一共Seed${roleCounts[0]}条记录!\r\n` +
`一共Seed${roleCounts[0]}条记录!\r\n` +
`一共Seed${cityCount}条记录!`
);
}
}

View File

@ -1,294 +0,0 @@
/*
* @Author: '490912587@qq.com' '490912587@qq.com'
* @Date: 2022-11-17 09:19:20
* @LastEditors: '490912587@qq.com' '490912587@qq.com'
* @LastEditTime: 2022-11-24 00:23:12
* @FilePath: \tsplatform\backend\src\Controller\MenuController.ts
* @Description: 系统菜单控制器
*/
import { Body, Controller, Get, HttpException, HttpStatus, Post, Query } from '@nestjs/common';
import { RestfulReturn } from 'src/Expand/RestfulReturn';
import {
ApiBearerAuth,
ApiOperation,
ApiProperty,
ApiTags,
} from '@nestjs/swagger';
import { SysMenu } from 'src/Entity/SysMenu';
import { plainToClass } from 'class-transformer';
import { IdDto, IdsDto, IdStatusDto, ListDto, PageDto, RoleIdDto } from 'src/EntiyDto/CommonDto';
import { MenuService } from 'src/Service/MenuService';
import { RestfulHttpCodeEnum } from 'src/Enum/Global';
import { MenuDto } from 'src/EntiyDto/MenuDto';
import { RouteMenu } from 'src/EntiyDto/RouteMenu';
import { Equal, In, Not } from 'typeorm';
import { RoleService } from 'src/Service/RoleService';
@Controller('/menu')
@ApiBearerAuth()
@ApiTags('系统菜单管理')
export class MenuController {
constructor(
public readonly restful: RestfulReturn,
public readonly service: MenuService,
public readonly _roleService: RoleService,
) {
}
@ApiOperation({
summary: '获取所有菜单Tree菜单列表使用',
description: '获取所有菜单Tree'
})
@Get('/getMenuTree')
async getMenuTree() {
const result = await this.MenuTree(0);
return await this.restful.toJson(
RestfulHttpCodeEnum.SUCCESS, result
);
}
@ApiOperation({
summary: '根据角色ID获取用户授权的菜单Tree前端router使用',
description: '根据角色ID获取用户授权的菜单Tree'
})
@Get('/getAuthMenuTree')
async getAuthMenuTree(@Query() dto: RoleIdDto) {
if (dto.RoleId > 0) {
const result = await this.RouteTree(0, dto.RoleId);
return await this.restful.toJson(
RestfulHttpCodeEnum.SUCCESS, result
);
} else {
throw new HttpException("角色Id不能为空", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
//递归获取菜单Tree
private async MenuTree(parentId: number): Promise<SysMenu[]> {
const result = await this.service.repository.createQueryBuilder()
.where({ ParentId: parentId })
.orderBy("sort", "ASC")
.getMany();
const treeList: SysMenu[] = new Array<SysMenu>;
for await (let info of result) {
const children = await this.MenuTree(info.Id);
const node: any = {
Id: info.Id,
ParentId: info.ParentId,
Type: info.Type,
Title: info.Title,
Path: info.Path,
Permission: info.Permission,
Component: info.Component,
Icon: info.Icon,
Name: info.Name,
Redirect: info.Redirect,
IsLink: info.IsLink,
IsHide: info.IsHide,
IsKeepAlive: info.IsKeepAlive,
IsAffix: info.IsAffix,
IsIframe: info.IsIframe,
Children: [],
Sort: info.Sort,
};
if (children.length > 0) {
node.Children = children;
}
treeList.push(node);
}
return treeList;
}
//递归获取前端路由Tree
private async RouteTree(parentId: number, RoleId: number): Promise<RouteMenu[]> {
const menuRole = await this._roleService.getMenusByRoleId(RoleId);
const menuIds: number[] = [];
for await (const info of menuRole) {
menuIds.push(info.MenuId);
}
const result = await this.service.repository.createQueryBuilder()
.where({ ParentId: parentId, Id: In(menuIds) })
.orderBy("sort", "ASC")
.getMany();
const treeList: RouteMenu[] = new Array<RouteMenu>;
for await (let info of result) {
const children = await this.RouteTree(info.Id, RoleId);
const node: RouteMenu = {
name: info.Name,
path: info.Path,
component: info.Component,
redirect: info.Redirect,
meta: {
title: info.Title,
icon: info.Icon,
isLink: info.IsLink,
isHide: info.IsHide,
isKeepAlive: info.IsKeepAlive,
isAffix: info.IsAffix,
isIframe: info.IsIframe,
permission: info.Permission,
},
children: [],
};
if (children.length > 0) {
for await (let item of children) {
node.children.push({
name: item.name,
path: item.path,
component: item.component,
redirect: item.redirect,
meta: {
title: item.meta.title,
icon: item.meta.icon,
isLink: item.meta.isLink,
isHide: item.meta.isHide,
isKeepAlive: item.meta.isKeepAlive,
isAffix: item.meta.isAffix,
isIframe: item.meta.isIframe,
permission: item.meta.permission,
},
children: [],
});
}
}
treeList.push(node);
}
return treeList;
}
//#region 基础控制器
@ApiOperation({
summary: '分页查询菜单列表',
description: '分页查询菜单列表'
})
@ApiProperty()
@Get('/getPage')
async getPage(@Query() dto: PageDto) {
const result = await this.service.getPage(dto.Page, dto.PageSize, dto.Keywords, dto.OrderBy);
return await this.restful.toJson(
RestfulHttpCodeEnum.SUCCESS,
{
total: result[1],
page: 1,
pageSize: 10,
rows: result[0],
}
);
}
@ApiOperation({
summary: '查询菜单列表',
description: '查询菜单列表'
})
@Get('/getList')
async getList(@Query() dto: ListDto) {
const result = await this.service.getList(dto.Keywords, dto.OrderBy);
return await this.restful.toJson(
RestfulHttpCodeEnum.SUCCESS, result
);
}
@ApiOperation({
summary: '根据ID查询菜单',
description: '根据ID查询菜单'
})
@Get('/getById')
async getById(@Query() dto: IdDto) {
const result = await this.service.getById(dto.Id);
return await this.restful.toJson(
RestfulHttpCodeEnum.SUCCESS, result
);
}
@ApiOperation({
summary: '根据ID数组查询菜单',
description: '根据ID数组查询菜单'
})
@Get('/getByIds')
async getByIds(@Query() dto: IdsDto) {
const result = await this.service.getByIds(dto.Ids);
return await this.restful.toJson(
RestfulHttpCodeEnum.SUCCESS, result
);
}
@ApiOperation({
summary: '新增菜单',
description: '新增菜单'
})
@Post('/add')
async add(@Body() dto: MenuDto) {
const model = plainToClass(SysMenu, dto);
const isRepeat = await this.service.repository.createQueryBuilder()
.orWhere({ Name: Equal(model.Name.trim()), })
.orWhere({ Permission: Equal(model.Permission.trim()) }).getCount();
if (isRepeat > 0) {
return await this.restful.toJson(
RestfulHttpCodeEnum.FAIL, "路由名称或者权限标识重复!"
);
} else {
const result = await this.service.save(model);
return await this.restful.toJson(
RestfulHttpCodeEnum.SUCCESS, result
);
}
}
@ApiOperation({
summary: '修改菜单',
description: '修改菜单'
})
@Post('/edit')
async edit(@Body() dto: MenuDto) {
const model = plainToClass(SysMenu, dto);
const isRepeat = await this.service.repository.createQueryBuilder()
.where({ Id: Not(model.Id) })
.andWhere([{ Name: Equal(model.Name.trim()) }, { Permission: Equal(model.Permission.trim()) }])
.getCount();
if (isRepeat > 0) {
return await this.restful.toJson(
RestfulHttpCodeEnum.FAIL, "路由名称或者权限标识重复!"
);
} else {
const result = await this.service.save(model);
return await this.restful.toJson(
RestfulHttpCodeEnum.SUCCESS, result
);
}
}
@ApiOperation({
summary: '假删除菜单',
description: '假删除菜单'
})
@Post('/del')
async del(@Body() dto: IdDto) {
const result = await this.service.destroy(dto.Id);
return await this.restful.toJson(
RestfulHttpCodeEnum.SUCCESS, result
);
}
@ApiOperation({
summary: '修改菜单状态',
description: '修改菜单状态'
})
@Post('/setStatus')
async setStatus(@Body() dto: IdStatusDto) {
const result = await this.service.repository.createQueryBuilder()
.update()
.set({
Status: dto.Status
})
.where("Id = :Id", { Id: dto.Id })
.execute();
if (result.affected > 0) {
return await this.restful.toJson(
RestfulHttpCodeEnum.SUCCESS, result
);
} else {
return await this.restful.toJson(
RestfulHttpCodeEnum.FAIL, result
);
}
}
//#endregion
}

View File

@ -1,189 +0,0 @@
/*
* @Author: '490912587@qq.com' '490912587@qq.com'
* @Date: 2022-11-17 17:05:40
* @LastEditors: '490912587@qq.com' '490912587@qq.com'
* @LastEditTime: 2022-11-23 22:32:29
* @FilePath: \tsplatform\backend\src\Controller\RoleController.ts
* @Description: 系统角色控制器
*/
import { Body, Controller, Get, Post, Query } from '@nestjs/common';
import { RestfulReturn } from 'src/Expand/RestfulReturn';
import {
ApiBearerAuth,
ApiOperation,
ApiTags,
} from '@nestjs/swagger';
import { RoleService } from 'src/Service/RoleService';
import { IdDto, IdsDto, IdStatusDto, ListDto, PageDto } from 'src/EntiyDto/CommonDto';
import { RestfulHttpCodeEnum } from 'src/Enum/Global';
import { SysRole } from 'src/Entity/SysRole';
import { RoleDto } from 'src/EntiyDto/RoleDto';
import { plainToClass } from 'class-transformer';
import { Not } from 'typeorm';
@ApiBearerAuth()
@ApiTags('系统角色管理')
@Controller('/role')
export class RoleController {
constructor(
public readonly restful: RestfulReturn,
public readonly service: RoleService
) {
}
//#region 基础控制器
@ApiOperation({
summary: '分页查询角色列表',
description: '分页查询角色列表'
})
@Get('/getPage')
async getPage(@Query() dto: PageDto) {
const result = await this.service.getPage(dto.Page, dto.PageSize, dto.Keywords, dto.OrderBy);
return await this.restful.toJson(
RestfulHttpCodeEnum.SUCCESS,
{
total: result[1],
page: 1,
pageSize: 10,
rows: result[0],
}
);
}
@ApiOperation({
summary: '查询角色列表',
description: '查询角色列表'
})
@Get('/getList')
async getList(@Query() dto: ListDto) {
const result = await this.service.getList(dto.Keywords, dto.OrderBy);
return await this.restful.toJson(
RestfulHttpCodeEnum.SUCCESS, result
);
}
@ApiOperation({
summary: '根据ID查询角色',
description: '根据ID查询角色'
})
@Get('/getById')
async getById(@Query() dto: IdDto) {
const result = await this.service.getById(dto.Id);
return await this.restful.toJson(
RestfulHttpCodeEnum.SUCCESS, result
);
}
@ApiOperation({
summary: '根据ID数组查询角色',
description: '根据ID数组查询角色'
})
@Get('/getByIds')
async getByIds(@Query() dto: IdsDto) {
const result = await this.service.getByIds(dto.Ids);
return await this.restful.toJson(
RestfulHttpCodeEnum.SUCCESS, result
);
}
@ApiOperation({
summary: '新增角色',
description: '新增角色'
})
@Post('/add')
async add(@Body() dto: RoleDto) {
const model = plainToClass(SysRole, dto);
const isRepeat = await this.service.repository.count({
where: { Name: model.Name.trim() }
});
if (isRepeat > 0) {
return await this.restful.toJson(
RestfulHttpCodeEnum.FAIL, "角色名称重复!"
);
} else {
const result = await this.service.save(model);
if (result) {
return await this.restful.toJson(
RestfulHttpCodeEnum.SUCCESS, result
);
} else {
return await this.restful.toJson(
RestfulHttpCodeEnum.FAIL, result
);
}
}
}
@ApiOperation({
summary: '修改角色',
description: '修改角色'
})
@Post('/edit')
async edit(@Body() dto: RoleDto) {
const model = plainToClass(SysRole, dto);
const isRepeat = await this.service.repository.count({
where: { Id: Not(model.Id), Name: model.Name.trim() }
});
if (isRepeat > 0) {
return await this.restful.toJson(
RestfulHttpCodeEnum.FAIL, "角色名称重复!"
);
} else {
const result = await this.service.save(model);
if (result) {
return await this.restful.toJson(
RestfulHttpCodeEnum.SUCCESS, result
);
} else {
return await this.restful.toJson(
RestfulHttpCodeEnum.FAIL, result
);
}
}
}
@ApiOperation({
summary: '删除角色',
description: '删除角色'
})
@Post('/del')
async del(@Body() dto: IdDto) {
const result = await this.service.destroy(dto.Id);
if (result) {
return await this.restful.toJson(
RestfulHttpCodeEnum.SUCCESS, result
);
} else {
return await this.restful.toJson(
RestfulHttpCodeEnum.FAIL, result
);
}
}
@ApiOperation({
summary: '修改角色状态',
description: '修改角色状态'
})
@Post('/setStatus')
async setStatus(@Body() dto: IdStatusDto) {
const result = await this.service.repository.createQueryBuilder()
.update()
.set({
Status: dto.Status
})
.where("Id = :Id", { Id: dto.Id })
.execute();
if (result.affected > 0) {
return await this.restful.toJson(
RestfulHttpCodeEnum.SUCCESS, result
);
} else {
return await this.restful.toJson(
RestfulHttpCodeEnum.FAIL, result
);
}
}
//#endregion
}

View File

@ -1,188 +0,0 @@
/*
* @Author: '490912587@qq.com' '490912587@qq.com'
* @Date: 2022-11-17 17:05:41
* @LastEditors: '490912587@qq.com' '490912587@qq.com'
* @LastEditTime: 2022-11-23 22:49:42
* @FilePath: \tsplatform\backend\src\Controller\UserController.ts
* @Description: 系统用户控制器
*/
import { Body, Controller, Get, Post, Query } from '@nestjs/common';
import { RestfulReturn } from 'src/Expand/RestfulReturn';
import {
ApiBearerAuth,
ApiOperation,
ApiTags,
} from '@nestjs/swagger';
import { UserService } from 'src/Service/UserService';
import { SysUser } from 'src/Entity/SysUser';
import { IdDto, IdsDto, IdStatusDto, ListDto, PageDto } from 'src/EntiyDto/CommonDto';
import { RestfulHttpCodeEnum } from 'src/Enum/Global';
import { plainToClass } from 'class-transformer';
import { AppConfig } from 'src/Config/Index';
import { Md5 } from 'ts-md5';
import { UserDto } from 'src/EntiyDto/UserDto';
import { Equal, Not } from 'typeorm';
@ApiBearerAuth()
@ApiTags('系统用户管理')
@Controller('/user')
export class UserController {
constructor(
public readonly restful: RestfulReturn,
public readonly service: UserService
) {
}
//#region 基础控制器
@ApiOperation({
summary: '分页查询用户列表',
description: '分页查询用户列表'
})
@Get('/getPage')
async getPage(@Query() dto: PageDto) {
const result = await this.service.getPage(dto.Page, dto.PageSize, dto.Keywords, dto.OrderBy);
return await this.restful.toJson(
RestfulHttpCodeEnum.SUCCESS,
{
total: result[1],
page: 1,
pageSize: 10,
rows: result[0],
}
);
}
@ApiOperation({
summary: '查询用户列表',
description: '查询用户列表'
})
@Get('/getList')
async getList(@Query() dto: ListDto) {
const result = await this.service.getList(dto.Keywords, dto.OrderBy);
return await this.restful.toJson(
RestfulHttpCodeEnum.SUCCESS, result
);
}
@ApiOperation({
summary: '根据ID查询用户',
description: '根据ID查询用户'
})
@Get('/getById')
async getById(@Query() dto: IdDto) {
const result = await this.service.getById(dto.Id);
return await this.restful.toJson(
RestfulHttpCodeEnum.SUCCESS, result
);
}
@ApiOperation({
summary: '根据ID数组查询用户',
description: '根据ID数组查询用户'
})
@Get('/getByIds')
async getByIds(@Query() dto: IdsDto) {
const result = await this.service.getByIds(dto.Ids);
return await this.restful.toJson(
RestfulHttpCodeEnum.SUCCESS, result
);
}
@ApiOperation({
summary: '新增用户',
description: '新增用户'
})
@Post('/add')
async add(@Body() dto: UserDto) {
const model = plainToClass(SysUser, dto);
const isRepeat = await this.service.repository.createQueryBuilder()
.orWhere({ UserName: Equal(model.UserName.trim()), })
.orWhere({ Phone: Equal(model.Phone.trim()) })
.orWhere({ Email: Equal(model.Email.trim()) }).getCount();
if (isRepeat > 0) {
return await this.restful.toJson(
RestfulHttpCodeEnum.FAIL, "账号/手机号/邮箱重复!"
);
} else {
//给新用户设置个默认密码888888用户登录后可以自行修改密码
model.Password = Md5.hashStr(Md5.hashStr("888888") + AppConfig.md5Key);
const result = await this.service.save(model);
if (result) {
return await this.restful.toJson(
RestfulHttpCodeEnum.SUCCESS, result
);
} else {
return await this.restful.toJson(
RestfulHttpCodeEnum.FAIL, result
);
}
}
}
@ApiOperation({
summary: '修改用户',
description: '修改用户'
})
@Post('/edit')
async edit(@Body() dto: UserDto) {
const model = plainToClass(SysUser, dto);
const isRepeat = await this.service.repository.createQueryBuilder()
.where({ Id: Not(model.Id) })
.andWhere([{ UserName: Equal(model.UserName.trim()) }, { Phone: Equal(model.Phone.trim()) }, { Email: Equal(model.Email.trim()) }])
.getCount();
if (isRepeat > 0) {
return await this.restful.toJson(
RestfulHttpCodeEnum.FAIL, "账号/手机号/邮箱重复!"
);
} else {
//给新用户设置个默认密码888888用户登录后可以自行修改密码
model.Password = Md5.hashStr(Md5.hashStr("888888") + AppConfig.md5Key);
const result = await this.service.save(model);
if (result) {
return await this.restful.toJson(
RestfulHttpCodeEnum.SUCCESS, result
);
} else {
return await this.restful.toJson(
RestfulHttpCodeEnum.FAIL, result
);
}
}
}
@ApiOperation({
summary: '假删除用户',
description: '假删除用户'
})
@Post('/del')
async del(@Body() dto: IdDto) {
const result = await this.service.destroy(dto.Id);
return await this.restful.toJson(
RestfulHttpCodeEnum.SUCCESS, result
);
}
@ApiOperation({
summary: '修改用户状态',
description: '修改用户状态'
})
@Post('/setStatus')
async setStatus(@Body() dto: IdStatusDto) {
const result = await this.service.repository.createQueryBuilder()
.update()
.set({
Status: dto.Status
})
.where("Id = :Id", { Id: dto.Id })
.execute();
if (result.affected > 0) {
return await this.restful.toJson(
RestfulHttpCodeEnum.SUCCESS, result
);
} else {
return await this.restful.toJson(
RestfulHttpCodeEnum.FAIL, result
);
}
}
//#endregion
}

View File

@ -1,43 +0,0 @@
/*
* @Author: '490912587@qq.com' '490912587@qq.com'
* @Date: 2022-11-17 11:26:13
* @LastEditors: '490912587@qq.com' '490912587@qq.com'
* @LastEditTime: 2022-11-22 21:57:27
* @FilePath: \tsplatform\backend\src\Entity\SysBase.ts
* @Description: 系统基类实体模型
*/
import { Entity, Column, PrimaryColumn, PrimaryGeneratedColumn, CreateDateColumn, UpdateDateColumn, VersionColumn } from 'typeorm';
@Entity()
export class SysBase {
@PrimaryGeneratedColumn()
@PrimaryColumn({ comment: "ID" })
Id: number;
@Column({ comment: "状态", default: 0, nullable: true })
Status: number;
@Column({ comment: "商户/站点ID", default: 0, nullable: true })
MerchantId: number;
//自动为实体插入日期
@CreateDateColumn()
CreateTime: Date;
//每次调用实体管理器或存储库的save时自动更新实体日期
@UpdateDateColumn()
UpdateTime: Date;
@Column({ comment: "操作人ID", default: 0, nullable: true })
OperatorId: number;
@Column({ comment: "修改人ID", default: 0, nullable: true })
UpdateUserId: number;
@Column({ comment: "是否删除", default: false, nullable: true })
IsDelete: boolean;
//每次调用实体管理器或存储库的save时自动增长实体版本
@VersionColumn()
Version: number;
}

View File

@ -1,35 +0,0 @@
/*
* @Author: '490912587@qq.com' '490912587@qq.com'
* @Date: 2022-11-17 13:12:19
* @LastEditors: '490912587@qq.com' '490912587@qq.com'
* @LastEditTime: 2022-11-18 14:18:50
* @FilePath: \tsplatform\backend\src\Entity\SysCity.ts
* @Description: 系统城市实体模型
*/
import { Entity, Column, PrimaryColumn, PrimaryGeneratedColumn } from 'typeorm';
@Entity()
export class SysCity {
@PrimaryGeneratedColumn()
@PrimaryColumn({ comment: "ID" })
Id: number;
@Column({ comment: "地区编号" })
RegionId: string;
@Column({ comment: "地区名称" })
RegionName: string;
@Column({ comment: "地区简称", nullable: true })
RegionShortName: string;
@Column({ comment: "地区行政编号", nullable: true })
RegionCode: string;
@Column({ comment: "地区父级ID" })
RegionParentId: string;
@Column({ comment: "地区级别" })
RegionLevel: number;
}

View File

@ -1,19 +0,0 @@
/*
* @Author: '490912587@qq.com' '490912587@qq.com'
* @Date: 2022-11-17 13:10:02
* @LastEditors: '490912587@qq.com' '490912587@qq.com'
* @LastEditTime: 2022-11-22 15:08:00
* @FilePath: \tsplatform\backend\src\Entity\SysDictionaries.ts
* @Description: 系统字典实体模型
*/
import { Entity, Column } from 'typeorm';
import { SysBase } from 'src/Entity/SysBase';
@Entity()
export class SysDictionaries extends SysBase {
@Column({ comment: "字典名称" })
Name: string;
@Column({ comment: "备注", nullable: true })
Remark: string;
}

View File

@ -1,61 +0,0 @@
/*
* @Author: '490912587@qq.com' '490912587@qq.com'
* @Date: 2022-11-17 11:21:40
* @LastEditors: '490912587@qq.com' '490912587@qq.com'
* @LastEditTime: 2022-11-23 21:52:49
* @FilePath: \tsplatform\backend\src\Entity\SysMenu.ts
* @Description: 系统菜单实体模型
*/
import { Entity, Column } from 'typeorm';
import { SysBase } from 'src/Entity/SysBase';
import { MenuEnum } from 'src/Enum/Global';
@Entity()
export class SysMenu extends SysBase {
@Column({ comment: "父级ID", default: 0 })
ParentId: number;
@Column({ comment: "菜单类型", default: 0 })
Type: MenuEnum;
@Column({ comment: "路由名称", unique: true })
Name: string;
@Column({ comment: "路由地址", nullable: true })
Path: string;
@Column({ comment: "组件名称", nullable: true })
Component: string;
@Column({ comment: "重定向路径", nullable: true })
Redirect: string;
@Column({ comment: "菜单名称" })
Title: string;
@Column({ comment: "是否外链,开启外链条件,`1、isLink: 链接地址不为空 2、isIframe:false`", nullable: true })
IsLink: string;
@Column({ comment: "是否隐藏此路由" })
IsHide: boolean;
@Column({ comment: "是否缓存组件状态" })
IsKeepAlive: boolean;
@Column({ comment: "是否固定在 tagsView 栏上" })
IsAffix: boolean;
@Column({ comment: "是否内嵌窗口,开启条件,`1、isIframe:true 2、isLink链接地址不为空`" })
IsIframe: boolean;
@Column({ comment: "路由权限标识", unique: true })
Permission: string;
@Column({ comment: "菜单图标", nullable: true })
Icon: string;
@Column({ comment: "排序", nullable: true, default: 0 })
Sort: number;
Children: SysMenu[];
}

View File

@ -1,22 +0,0 @@
/*
* @Author: '490912587@qq.com' '490912587@qq.com'
* @Date: 2022-11-17 13:08:01
* @LastEditors: '490912587@qq.com' '490912587@qq.com'
* @LastEditTime: 2022-11-23 16:46:20
* @FilePath: \tsplatform\backend\src\Entity\SysRole.ts
* @Description: 系统角色实体模型
*/
import { Entity, Column } from 'typeorm';
import { SysBase } from 'src/Entity/SysBase';
import { SysRoleMenu } from 'src/Entity/SysRoleMenu';
@Entity()
export class SysRole extends SysBase {
@Column({ comment: "角色名称", unique: true })
Name: string;
@Column({ comment: "备注", nullable: true })
Remark: string;
SysRoleMenu: SysRoleMenu[];
}

View File

@ -1,18 +0,0 @@
/*
* @Author: '490912587@qq.com' '490912587@qq.com'
* @Date: 2022-11-17 13:54:50
* @LastEditors: '490912587@qq.com' '490912587@qq.com'
* @LastEditTime: 2022-11-23 17:32:38
* @FilePath: \tsplatform\backend\src\Entity\SysRoleMenu.ts
* @Description: 系统角色菜单映射实体模型
*/
import { Entity, Column, ManyToOne } from 'typeorm';
import { SysBase } from 'src/Entity/SysBase';
@Entity()
export class SysRoleMenu extends SysBase {
@Column({ comment: "菜单Id", default: 0 })
MenuId: number;
@Column({ comment: "角色Id", default: 0 })
RoleId: number;
}

View File

@ -1,57 +0,0 @@
/*
* @Author: '490912587@qq.com' '490912587@qq.com'
* @Date: 2022-11-17 13:55:35
* @LastEditors: '490912587@qq.com' '490912587@qq.com'
* @LastEditTime: 2022-11-23 23:13:45
* @FilePath: \tsplatform\backend\src\Entity\SysUser.ts
* @Description: 系统用户映射实体模型
*/
import { Entity, Column, Index } from 'typeorm';
import { SysBase } from 'src/Entity/SysBase';
import { SexEnum, UserTypeEnum } from 'src/Enum/Global';
@Entity()
export class SysUser extends SysBase {
@Column({ comment: "账号类型", default: 1 })
UserType: UserTypeEnum;
@Column({ comment: "账号", unique: true })
UserName: string;
@Column({ comment: "密码" })
Password: string;
@Column({ comment: "昵称", nullable: true })
NickName: string;
@Column({ comment: "头像", nullable: true })
Avatar: string;
@Column({ comment: "出生日期", nullable: true })
Birthday: string;
@Column({ comment: "性别", nullable: true, default: 0 })
Sex: SexEnum;
@Column({ comment: "邮箱", unique: true })
Email: string;
@Column({ comment: "手机号码", unique: true })
Phone: string;
@Column({ comment: "真实姓名", nullable: true })
RealName: string;
@Column({ comment: "身份证号", nullable: true })
IdCard: string;
@Column({ comment: "个性签名", nullable: true })
Signature: string;
@Column({ comment: "个人简介", nullable: true })
Introduction: string;
@Column({ comment: "备注", nullable: true })
Remark: string;
@Column({ comment: "角色Id", default: 0 })
RoleId: number;
}

View File

@ -1,78 +0,0 @@
/*
* @Author: '490912587@qq.com' '490912587@qq.com'
* @Date: 2022-11-18 09:02:28
* @LastEditors: '490912587@qq.com' '490912587@qq.com'
* @LastEditTime: 2022-11-24 00:20:22
* @FilePath: \tsplatform\backend\src\Expand\CommonDto.ts
* @Description: 公共DTO服务
*/
import { Injectable } from "@nestjs/common";
import { ApiProperty } from "@nestjs/swagger";
import { IsArray, IsNotEmpty, IsNumber, IsNumberString } from "class-validator";
//分页查询DTO
@Injectable()
export class PageDto {
@IsNotEmpty()
@IsNumberString()
@ApiProperty({ name: "Page", required: true })
Page: number
@IsNotEmpty()
@IsNumberString()
@ApiProperty({ name: "PageSize", required: true })
PageSize: number
@ApiProperty({ name: "Keywords", required: false })
Keywords: string
@ApiProperty({ name: "OrderBy", required: false })
OrderBy: string
}
//列表查询DTO
@Injectable()
export class ListDto {
@ApiProperty({ name: "Keywords", required: false })
Keywords: string
@ApiProperty({ name: "OrderBy", required: false })
OrderBy: string
}
//ids DTO
@Injectable()
export class IdsDto {
@IsNotEmpty()
@IsArray()
@ApiProperty({ name: "Ids", required: true })
Ids: number[]
}
//id DTO
@Injectable()
export class IdDto {
@IsNotEmpty()
@IsNumber()
@ApiProperty({ name: "Id", required: true })
Id: number
}
@Injectable()
export class RoleIdDto {
@ApiProperty({ name: "RoleId" })
RoleId: number;
}
//Id Status DTO
@Injectable()
export class IdStatusDto {
@IsNotEmpty()
@IsNumber()
@ApiProperty({ name: "Id", required: true })
Id: number
@IsNotEmpty()
@IsNumber()
@ApiProperty({ name: "Status", required: true })
Status: number
}

View File

@ -1,21 +0,0 @@
/*
* @Author: '490912587@qq.com' '490912587@qq.com'
* @Date: 2022-11-18 17:35:08
* @LastEditors: '490912587@qq.com' '490912587@qq.com'
* @LastEditTime: 2022-11-23 12:12:42
* @FilePath: \tsplatform\backend\src\EntiyDto\Login.ts
* @Description: DTO类
*/
import { ApiProperty } from "@nestjs/swagger";
import { IsNotEmpty, IsString } from "class-validator";
export class LoginDto {
@IsNotEmpty()
@IsString()
@ApiProperty({ name: "UserName", required: true })
UserName: string;
@IsNotEmpty()
@IsString()
@ApiProperty({ name: "Password", required: true })
Password: string;
}

View File

@ -1,70 +0,0 @@
/*
* @Author: '490912587@qq.com' '490912587@qq.com'
* @Date: 2022-11-18 17:35:08
* @LastEditors: '490912587@qq.com' '490912587@qq.com'
* @LastEditTime: 2022-11-24 00:14:13
* @FilePath: \tsplatform\backend\src\EntiyDto\MenuDto.ts
* @Description: SysMenu DTO类
*/
import { ApiProperty } from "@nestjs/swagger";
import { IsBoolean, IsNotEmpty, IsNumber, IsString } from "class-validator";
import { MenuEnum } from "src/Enum/Global";
export class MenuDto {
@ApiProperty({ name: "ParentId" })
ParentId: number;
@IsNotEmpty()
@IsNumber()
@ApiProperty({ name: "Type", required: true })
Type: MenuEnum;
@ApiProperty({ name: "Name", required: false })
Name: string;
@ApiProperty({ name: "Path", required: false })
Path: string;
@ApiProperty({ name: "Component", required: false })
Component: string;
@ApiProperty({ name: "Redirect", required: false })
Redirect: string;
@IsNotEmpty()
@IsString()
@ApiProperty({ name: "Title", required: true })
Title: string;
@ApiProperty({ name: "IsLink", required: false })
IsLink: string;
@IsNotEmpty()
@IsBoolean()
@ApiProperty({ name: "IsHide", required: true })
IsHide: boolean;
@IsNotEmpty()
@IsBoolean()
@ApiProperty({ name: "IsKeepAlive", required: true })
IsKeepAlive: boolean;
@IsNotEmpty()
@IsBoolean()
@ApiProperty({ name: "IsAffix", required: true })
IsAffix: boolean;
@IsNotEmpty()
@IsBoolean()
@ApiProperty({ name: "IsIframe", required: true })
IsIframe: boolean;
@IsNotEmpty()
@IsString()
@ApiProperty({ name: "Permission", required: true })
Permission: string;
@ApiProperty({ name: "Icon", required: false })
Icon: string;
}

View File

@ -1,25 +0,0 @@
/*
* @Author: '490912587@qq.com' '490912587@qq.com'
* @Date: 2022-11-22 16:16:08
* @LastEditors: '490912587@qq.com' '490912587@qq.com'
* @LastEditTime: 2022-11-23 12:12:56
* @FilePath: \backend\src\EntiyDto\RoleDto.ts
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import { ApiProperty } from "@nestjs/swagger";
import { IsArray, IsNotEmpty } from "class-validator";
export class RoleDto {
@IsNotEmpty()
@ApiProperty({ name: "Name", required: true })
Name: string;
@ApiProperty({ name: "Remark" })
Remark: string;
@IsNotEmpty()
@IsArray()
@ApiProperty({ name: "SysRoleMenu", required: true })
SysRoleMenu: number[];
}

View File

@ -1,26 +0,0 @@
/*
* @Author: '490912587@qq.com' '490912587@qq.com'
* @Date: 2022-11-18 17:35:08
* @LastEditors: '490912587@qq.com' '490912587@qq.com'
* @LastEditTime: 2022-11-23 12:13:13
* @FilePath: \tsplatform\backend\src\EntiyDto\RouteMenu.ts
* @Description: RouteMenu类 菜单树查询不需要dto映射
*/
export class RouteMenu {
name: string;
path: string;
component: string;
redirect: string;
meta: Meta;
children: RouteMenu[];
}
export class Meta {
title: string;
isLink: string;
isHide: boolean;
isKeepAlive: boolean;
isAffix: boolean;
isIframe: boolean;
permission: string;
icon: string;
}

View File

@ -1,65 +0,0 @@
/*
* @Author: '490912587@qq.com' '490912587@qq.com'
* @Date: 2022-11-22 16:16:19
* @LastEditors: '490912587@qq.com' '490912587@qq.com'
* @LastEditTime: 2022-11-23 21:32:25
* @FilePath: \backend\src\EntiyDto\UserDto.ts
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import { ApiProperty } from "@nestjs/swagger";
import { IsNotEmpty, IsNumber, IsNumberString } from "class-validator";
export class UserDto {
@IsNotEmpty()
@IsNumber()
@ApiProperty({ name: "UserType", required: true })
UserType: number;
@IsNotEmpty()
@ApiProperty({ name: "Name", required: true })
UserName: string;
@ApiProperty({ name: "Password" })
Password: string;
@ApiProperty({ name: "NickName" })
NickName: string;
@ApiProperty({ name: "Avatar" })
Avatar: string;
@ApiProperty({ name: "Birthday" })
Birthday: string;
@IsNotEmpty()
@IsNumber()
@ApiProperty({ name: "Sex" })
Sex: number;
@ApiProperty({ name: "Email", required: true })
Email: string;
@IsNotEmpty()
@IsNumberString()
@ApiProperty({ name: "Phone", required: true })
Phone: string;
@ApiProperty({ name: "RealName" })
RealName: string;
@ApiProperty({ name: "IdCard" })
IdCard: string;
@ApiProperty({ name: "Signature" })
Signature: string;
@ApiProperty({ name: "Introduction" })
Introduction: string;
@ApiProperty({ name: "Remark" })
Remark: string;
@ApiProperty({ name: "RoleId", required: true })
RoleId: number;
}

View File

@ -1,28 +0,0 @@
/*
* @Author: '490912587@qq.com' '490912587@qq.com'
* @Date: 2022-11-17 13:25:43
* @LastEditors: '490912587@qq.com' '490912587@qq.com'
* @LastEditTime: 2022-11-18 09:15:04
* @FilePath: \tsplatform\backend\src\Enum\Global.ts
* @Description: 全局枚举类
*/
export enum RestfulHttpCodeEnum {
SUCCESS = 200,
FAIL = 300,
NODATA = 201,
}
export enum UserTypeEnum {
Admin = 0,
None = 1,
SuperAdmin = 999,
}
export enum SexEnum {
Man = 0,
WoMan = 1
}
export enum MenuEnum {
Directory = 0,
Menu = 1,
Btn = 2
}

View File

@ -1,24 +0,0 @@
/*
* @Author: '490912587@qq.com' '490912587@qq.com'
* @Date: 2022-11-17 09:19:20
* @LastEditors: '490912587@qq.com' '490912587@qq.com'
* @LastEditTime: 2022-11-18 09:10:15
* @FilePath: \tsplatform\backend\src\Expand\RestfulReturn.ts
* @Description: RestfulAPI全局响应结构
*/
import { Injectable } from "@nestjs/common";
import { RestfulHttpCodeEnum } from "src/Enum/Global";
@Injectable()
export class RestfulReturn {
//HttpStatus=200时的状态码规则
toJson(code: RestfulHttpCodeEnum, data: any) {
return {
code: code,
msg: code === RestfulHttpCodeEnum.SUCCESS ? "操作成功!" :
code === RestfulHttpCodeEnum.FAIL ? "操作失败!" :
code === RestfulHttpCodeEnum.NODATA ? "数据为空!" : "未知错误!",
data: data,
timespan: new Date().getTime()
}
}
}

View File

@ -1,15 +0,0 @@
/*
* @Author: '490912587@qq.com' '490912587@qq.com'
* @Date: 2022-11-18 09:02:28
* @LastEditors: '490912587@qq.com' '490912587@qq.com'
* @LastEditTime: 2022-11-24 10:13:53
* @FilePath: \tsplatform\backend\src\Logging\LoggerPrint.ts
* @Description: 全局日志打印服务
*/
import { Injectable, Logger } from "@nestjs/common";
@Injectable()
export class LoggerPrint {
constructor(private readonly logger: Logger) {
}
}

View File

@ -1,33 +0,0 @@
/*
* @Author: '490912587@qq.com' '490912587@qq.com'
* @Date: 2022-11-21 11:57:30
* @LastEditors: '490912587@qq.com' '490912587@qq.com'
* @LastEditTime: 2022-11-22 10:41:09
* @FilePath: \tsplatform\backend\src\Middleware\JwtHandle.ts
* @Description: 授权中间件
*/
import { HttpException, HttpStatus, Injectable, NestMiddleware } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { NextFunction } from 'express';
import { AppConfig } from 'src/Config/Index';
@Injectable()
export class JwtHandle implements NestMiddleware {
constructor(
public readonly _jwt: JwtService
) { }
async use(req: Request, res: Response, next: NextFunction) {
try {
const token = req.headers["authorization"];
if (!token) {
throw new HttpException("Token指令牌不存在", HttpStatus.UNAUTHORIZED);
} else {
await this._jwt.verifyAsync(token, { secret: AppConfig.jwt.secret, ignoreExpiration: false })
next();
}
}
catch (ex) {
throw new HttpException(ex.message, HttpStatus.UNAUTHORIZED);
}
}
}

File diff suppressed because one or more lines are too long

View File

@ -1,216 +0,0 @@
/*
* @Author: '490912587@qq.com' '490912587@qq.com'
* @Date: 2022-11-24 09:22:50
* @LastEditors: '490912587@qq.com' '490912587@qq.com'
* @LastEditTime: 2022-11-24 09:58:45
* @FilePath: \backend\src\SeedData\Menu.ts
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
export const menus = [{
"Id": 8,
"Status": 0,
"MerchantId": 0,
"CreateTime": "2022-11-23T07:53:21.453Z",
"UpdateTime": "2022-11-24T01:29:41.000Z",
"OperatorId": 0,
"UpdateUserId": 0,
"IsDelete": false,
"Version": 5,
"ParentId": 2,
"Type": 1,
"Name": "system.city",
"Path": "/system/city",
"Component": "/system/city/index",
"Redirect": "",
"Title": "message.router.system.city",
"IsLink": "",
"IsHide": false,
"IsKeepAlive": true,
"IsAffix": false,
"IsIframe": false,
"Permission": "system.city",
"Icon": "ele-OfficeBuilding",
"Sort": 101
},
{
"Id": 7,
"Status": 0,
"MerchantId": 0,
"CreateTime": "2022-11-23T07:50:37.192Z",
"UpdateTime": "2022-11-24T01:21:19.762Z",
"OperatorId": 0,
"UpdateUserId": 0,
"IsDelete": false,
"Version": 4,
"ParentId": 2,
"Type": 1,
"Name": "system.dic",
"Path": "/system/dic",
"Component": "/system/dic/index",
"Redirect": "",
"Title": "message.router.system.dic",
"IsLink": "",
"IsHide": false,
"IsKeepAlive": true,
"IsAffix": false,
"IsIframe": false,
"Permission": "system.dic",
"Icon": "iconfont icon-zhongyingwen1",
"Sort": 100
},
{
"Id": 6,
"Status": 0,
"MerchantId": 0,
"CreateTime": "2022-11-22T13:59:37.445Z",
"UpdateTime": "2022-11-23T05:56:53.726Z",
"OperatorId": 0,
"UpdateUserId": 0,
"IsDelete": false,
"Version": 0,
"ParentId": 0,
"Type": 0,
"Name": "personal",
"Path": "/personal",
"Component": "personal/index",
"Redirect": null,
"Title": "message.router.personal",
"IsLink": "",
"IsHide": true,
"IsKeepAlive": false,
"IsAffix": false,
"IsIframe": false,
"Permission": "personal",
"Icon": "iconfont icon-gerenzhongxin",
"Sort": 6
},
{
"Id": 5,
"Status": 0,
"MerchantId": 0,
"CreateTime": "2022-11-22T13:59:37.445Z",
"UpdateTime": "2022-11-23T08:05:06.779Z",
"OperatorId": 0,
"UpdateUserId": 0,
"IsDelete": false,
"Version": 0,
"ParentId": 2,
"Type": 1,
"Name": "system.role",
"Path": "/system/role",
"Component": "system/role/index",
"Redirect": null,
"Title": "message.router.system.role",
"IsLink": "",
"IsHide": false,
"IsKeepAlive": true,
"IsAffix": false,
"IsIframe": false,
"Permission": "system.role",
"Icon": "fa fa-street-view",
"Sort": 5
},
{
"Id": 4,
"Status": 0,
"MerchantId": 0,
"CreateTime": "2022-11-22T13:59:37.445Z",
"UpdateTime": "2022-11-23T08:05:03.689Z",
"OperatorId": 0,
"UpdateUserId": 0,
"IsDelete": false,
"Version": 0,
"ParentId": 2,
"Type": 1,
"Name": "system.user",
"Path": "/system/user",
"Component": "system/user/index",
"Redirect": null,
"Title": "message.router.system.user",
"IsLink": null,
"IsHide": false,
"IsKeepAlive": true,
"IsAffix": false,
"IsIframe": false,
"Permission": "system.user",
"Icon": "iconfont icon-icon-",
"Sort": 4
},
{
"Id": 3,
"Status": 0,
"MerchantId": 0,
"CreateTime": "2022-11-22T13:59:37.445Z",
"UpdateTime": "2022-11-23T08:05:00.650Z",
"OperatorId": 0,
"UpdateUserId": 0,
"IsDelete": false,
"Version": 0,
"ParentId": 2,
"Type": 1,
"Name": "system.menu",
"Path": "/system/menu",
"Component": "system/menu/index",
"Redirect": null,
"Title": "message.router.system.menu",
"IsLink": "",
"IsHide": false,
"IsKeepAlive": true,
"IsAffix": false,
"IsIframe": false,
"Permission": "system.menu",
"Icon": "iconfont icon-caidan",
"Sort": 3
},
{
"Id": 2,
"Status": 0,
"MerchantId": 0,
"CreateTime": "2022-11-22T13:59:37.445Z",
"UpdateTime": "2022-11-23T08:05:16.188Z",
"OperatorId": 0,
"UpdateUserId": 0,
"IsDelete": false,
"Version": 0,
"ParentId": 0,
"Type": 0,
"Name": "system",
"Path": "/system",
"Component": "layout/routerView/parent",
"Redirect": "/system/menu",
"Title": "message.router.system.system",
"IsLink": "",
"IsHide": false,
"IsKeepAlive": true,
"IsAffix": false,
"IsIframe": false,
"Permission": "system",
"Icon": "iconfont icon-xitongshezhi",
"Sort": 2
},
{
"Id": 1,
"Status": 0,
"MerchantId": 0,
"CreateTime": "2022-11-22T13:59:37.445Z",
"UpdateTime": "2022-11-22T13:59:37.471Z",
"OperatorId": 0,
"UpdateUserId": 0,
"IsDelete": false,
"Version": 0,
"ParentId": 0,
"Type": 0,
"Name": "home",
"Path": "/home",
"Component": "home/index",
"Redirect": "",
"Title": "message.router.home",
"IsLink": "",
"IsHide": false,
"IsKeepAlive": true,
"IsAffix": true,
"IsIframe": false,
"Permission": "home",
"Icon": "iconfont icon-shouye",
"Sort": 1
}];

View File

@ -1,179 +0,0 @@
/*
* @Author: '490912587@qq.com' '490912587@qq.com'
* @Date: 2022-11-24 09:23:07
* @LastEditors: '490912587@qq.com' '490912587@qq.com'
* @LastEditTime: 2022-11-24 10:04:17
* @FilePath: \backend\src\SeedData\Role.ts
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
export const roles = [
{
"Id": 1,
"Status": 0,
"MerchantId": 0,
"CreateTime": "2022-11-22T13:59:37.567Z",
"UpdateTime": "2022-11-24T01:35:03.000Z",
"OperatorId": 0,
"UpdateUserId": 0,
"IsDelete": false,
"Version": 4,
"Name": "超级管理员",
"Remark": null
},
{
"Id": 2,
"Status": 0,
"MerchantId": 0,
"CreateTime": "2022-11-22T13:59:37.567Z",
"UpdateTime": "2022-11-24T01:36:45.000Z",
"OperatorId": 0,
"UpdateUserId": 0,
"IsDelete": false,
"Version": 3,
"Name": "普通用户",
"Remark": null
}
];
export const roleMenus = [{
"Id": 1,
"Status": 0,
"MerchantId": 0,
"CreateTime": "2022-11-24T01:35:03.437Z",
"UpdateTime": "2022-11-24T01:35:03.437Z",
"OperatorId": 0,
"UpdateUserId": 0,
"IsDelete": false,
"Version": 1,
"MenuId": 1,
"RoleId": 1
},
{
"Id": 2,
"Status": 0,
"MerchantId": 0,
"CreateTime": "2022-11-24T01:35:03.437Z",
"UpdateTime": "2022-11-24T01:35:03.437Z",
"OperatorId": 0,
"UpdateUserId": 0,
"IsDelete": false,
"Version": 1,
"MenuId": 2,
"RoleId": 1
},
{
"Id": 3,
"Status": 0,
"MerchantId": 0,
"CreateTime": "2022-11-24T01:35:03.437Z",
"UpdateTime": "2022-11-24T01:35:03.437Z",
"OperatorId": 0,
"UpdateUserId": 0,
"IsDelete": false,
"Version": 1,
"MenuId": 3,
"RoleId": 1
},
{
"Id": 4,
"Status": 0,
"MerchantId": 0,
"CreateTime": "2022-11-24T01:35:03.437Z",
"UpdateTime": "2022-11-24T01:35:03.437Z",
"OperatorId": 0,
"UpdateUserId": 0,
"IsDelete": false,
"Version": 1,
"MenuId": 4,
"RoleId": 1
},
{
"Id": 5,
"Status": 0,
"MerchantId": 0,
"CreateTime": "2022-11-24T01:35:03.437Z",
"UpdateTime": "2022-11-24T01:35:03.437Z",
"OperatorId": 0,
"UpdateUserId": 0,
"IsDelete": false,
"Version": 1,
"MenuId": 5,
"RoleId": 1
},
{
"Id": 6,
"Status": 0,
"MerchantId": 0,
"CreateTime": "2022-11-24T01:35:03.437Z",
"UpdateTime": "2022-11-24T01:35:03.437Z",
"OperatorId": 0,
"UpdateUserId": 0,
"IsDelete": false,
"Version": 1,
"MenuId": 6,
"RoleId": 1
},
{
"Id": 7,
"Status": 0,
"MerchantId": 0,
"CreateTime": "2022-11-24T01:35:03.437Z",
"UpdateTime": "2022-11-24T01:35:03.437Z",
"OperatorId": 0,
"UpdateUserId": 0,
"IsDelete": false,
"Version": 1,
"MenuId": 7,
"RoleId": 1
},
{
"Id": 8,
"Status": 0,
"MerchantId": 0,
"CreateTime": "2022-11-24T01:35:03.437Z",
"UpdateTime": "2022-11-24T01:35:03.437Z",
"OperatorId": 0,
"UpdateUserId": 0,
"IsDelete": false,
"Version": 1,
"MenuId": 8,
"RoleId": 1
},
{
"Id": 9,
"Status": 0,
"MerchantId": 0,
"CreateTime": "2022-11-24T01:36:45.486Z",
"UpdateTime": "2022-11-24T01:36:45.486Z",
"OperatorId": 0,
"UpdateUserId": 0,
"IsDelete": false,
"Version": 1,
"MenuId": 1,
"RoleId": 2
},
{
"Id": 10,
"Status": 0,
"MerchantId": 0,
"CreateTime": "2022-11-24T01:36:45.486Z",
"UpdateTime": "2022-11-24T01:36:45.486Z",
"OperatorId": 0,
"UpdateUserId": 0,
"IsDelete": false,
"Version": 1,
"MenuId": 2,
"RoleId": 2
},
{
"Id": 11,
"Status": 0,
"MerchantId": 0,
"CreateTime": "2022-11-24T01:36:45.486Z",
"UpdateTime": "2022-11-24T01:36:45.486Z",
"OperatorId": 0,
"UpdateUserId": 0,
"IsDelete": false,
"Version": 1,
"MenuId": 4,
"RoleId": 2
}];

View File

@ -1,60 +0,0 @@
/*
* @Author: '490912587@qq.com' '490912587@qq.com'
* @Date: 2022-11-24 09:22:58
* @LastEditors: '490912587@qq.com' '490912587@qq.com'
* @LastEditTime: 2022-11-24 10:04:55
* @FilePath: \backend\src\SeedData\User.ts
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
export const users = [{
"Id": 1,
"Status": 0,
"MerchantId": 0,
"CreateTime": "2022-11-22T13:59:37.496Z",
"UpdateTime": "2022-11-24T01:21:53.423Z",
"OperatorId": 0,
"UpdateUserId": 0,
"IsDelete": false,
"Version": 0,
"UserType": 999,
"UserName": "admin",
"Password": "1a23c5066d05474304c03342e57850d9",
"NickName": "甜蜜蜜",
"Avatar": "https://img2.baidu.com/it/u=1978192862,2048448374&fm=253&fmt=auto&app=138&f=JPEG?w=504&h=500",
"Birthday": null,
"Sex": 0,
"Email": "490912587@qq.com",
"Phone": "13000000000",
"RealName": null,
"IdCard": null,
"Signature": null,
"Introduction": null,
"Remark": null,
"RoleId": 1
},
{
"Id": 2,
"Status": 0,
"MerchantId": 0,
"CreateTime": "2022-11-23T08:45:37.321Z",
"UpdateTime": "2022-11-24T01:39:33.000Z",
"OperatorId": 0,
"UpdateUserId": 0,
"IsDelete": false,
"Version": 4,
"UserType": 0,
"UserName": "test",
"Password": "7b0ea1aa91470cd870111415a94b475b",
"NickName": "甜蜜蜜的小号",
"Avatar": "https://img2.baidu.com/it/u=1978192862,2048448374&fm=253&fmt=auto&app=138&f=JPEG?w=504&h=500",
"Birthday": null,
"Sex": 1,
"Email": "1844045442@qq.com",
"Phone": "15000000000",
"RealName": null,
"IdCard": null,
"Signature": null,
"Introduction": null,
"Remark": null,
"RoleId": 2
}];

View File

@ -1,94 +0,0 @@
/*
* @Author: '490912587@qq.com' '490912587@qq.com'
* @Date: 2022-11-17 17:08:52
* @LastEditors: '490912587@qq.com' '490912587@qq.com'
* @LastEditTime: 2022-11-24 11:12:25
* @FilePath: \tsplatform\backend\src\Service\UserService.ts
* @Description: 省市区服务
*/
import { Injectable, Logger } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { DeleteResult, In, InsertResult, Like, Repository } from 'typeorm';
import { LoggerPrint } from 'src/Logging/LoggerPrint';
import { SysCity } from 'src/Entity/SysCity';
import { citys } from 'src/SeedData/City';
@Injectable()
export class CityService {
private readonly tableName: string = "省市区";
constructor(
public readonly loggerPrint: LoggerPrint,
public readonly logger: Logger,
@InjectRepository(SysCity)
public readonly repository: Repository<SysCity>
) {
}
//#region 基础控制器
// 初始化数据
async seedData(): Promise<number> {
let result: InsertResult;
await this.repository.queryRunner?.startTransaction();
try {
await this.repository.clear();
result = await this.repository.createQueryBuilder().insert().values(citys).execute();
await this.repository.queryRunner?.commitTransaction();
await this.repository.createQueryBuilder().useTransaction(true);
return result.identifiers.length;
} catch (err) {
//如果遇到错误,可以回滚事务
await this.repository.queryRunner?.rollbackTransaction();
this.logger.warn(err);
return 0;
}
}
async getPage(
page: number = 1,
pageSize: number = 10,
keywords: string = "",
orderBy: string = ""
): Promise<[SysCity[], number]> {
const result = await this.repository.findAndCount({
where: [{ RegionName: Like("%" + keywords + "%") }, { RegionShortName: Like("%" + keywords + "%") }],
order: {
Id: "ASC"
},
skip: page - 1,
take: pageSize
})
return result;
}
async getList(
keywords: string = "",
orderBy: string = "",
): Promise<SysCity[]> {
const result = await this.repository.find({
where: [{ RegionName: Like("%" + keywords + "%") }, { RegionShortName: Like("%" + keywords + "%") }],
order: {
Id: "ASC"
},
})
return result;
}
async getById(id: number): Promise<SysCity> {
const result = await this.repository.findOne({ where: { Id: id } });
return result;
}
async getByIds(ids: number[]): Promise<SysCity[]> {
const result = await this.repository.find({
where: { Id: In(ids) },
})
return result;
}
async save(model: SysCity): Promise<SysCity> {
const result = await this.repository.save(model);
return result;
}
async destroy(id: number): Promise<DeleteResult> {
const result = await this.repository.delete(id);
return result;
}
//#endregion
}

View File

@ -1,94 +0,0 @@
/*
* @Author: '490912587@qq.com' '490912587@qq.com'
* @Date: 2022-11-17 17:08:52
* @LastEditors: '490912587@qq.com' '490912587@qq.com'
* @LastEditSysMenuime: 2022-11-18 11:31:43
* @FilePath: \tsplatform\backend\src\Service\MenuService.ts
* @Description: 系统菜单服务
*/
import { Injectable, Logger } from '@nestjs/common';
import { SysMenu } from 'src/Entity/SysMenu';
import { InjectRepository } from '@nestjs/typeorm';
import { DeleteResult, In, InsertResult, Like, Repository } from 'typeorm';
import { LoggerPrint } from 'src/Logging/LoggerPrint';
import { menus } from 'src/SeedData/Menu';
@Injectable()
export class MenuService {
private readonly tableName: string = "系统菜单";
constructor(
public readonly loggerPrint: LoggerPrint,
public readonly logger: Logger,
@InjectRepository(SysMenu)
public readonly repository: Repository<SysMenu>
) {
}
//#region 基础控制器
// 初始化数据
async seedData(): Promise<number> {
let result: InsertResult;
await this.repository.queryRunner?.startTransaction();
try {
await this.repository.clear();
result = await this.repository.createQueryBuilder().insert().values(menus).execute();
await this.repository.queryRunner?.commitTransaction();
await this.repository.createQueryBuilder().useTransaction(true);
return result.identifiers.length;
} catch (err) {
//如果遇到错误,可以回滚事务
await this.repository.queryRunner?.rollbackTransaction();
this.logger.warn(err);
return 0;
}
}
async getPage(
page: number = 1,
pageSize: number = 10,
keywords: string = "",
orderBy: string = ""
): Promise<[SysMenu[], number]> {
const result = await this.repository.findAndCount({
where: [{ Title: Like("%" + keywords + "%") }, { Name: Like("%" + keywords + "%") }],
order: {
Id: "DESC"
},
skip: page - 1,
take: pageSize
})
return result;
}
async getList(
keywords: string = "",
orderBy: string = ""
): Promise<SysMenu[]> {
const result = await this.repository.find({
where: [{ Name: Like("%" + keywords + "%") }],
order: {
Id: "DESC"
},
})
return result;
}
async getById(id: number): Promise<SysMenu> {
const result = await this.repository.findOne({ where: { Id: id } });
return result;
}
async getByIds(ids: number[]): Promise<SysMenu[]> {
const result = await this.repository.find({
where: { Id: In(ids) },
})
return result;
}
async save(model: SysMenu): Promise<SysMenu> {
const result = await this.repository.save(model);
return result;
}
async destroy(id: number): Promise<DeleteResult> {
const result = await this.repository.delete(id);
return result;
}
//#endregion
}

View File

@ -1,156 +0,0 @@
/*
* @Author: '490912587@qq.com' '490912587@qq.com'
* @Date: 2022-11-17 17:08:52
* @LastEditors: '490912587@qq.com' '490912587@qq.com'
* @LastEditTime: 2022-11-24 10:15:40
* @FilePath: \tsplatform\backend\src\Service\RoleService.ts
* @Description: 系统角色服务
*/
import { Injectable, Logger } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { DeleteResult, In, InsertResult, Like, Repository } from 'typeorm';
import { SysRole } from 'src/Entity/SysRole';
import { LoggerPrint } from 'src/Logging/LoggerPrint';
import { SysRoleMenu } from 'src/Entity/SysRoleMenu';
import { roleMenus, roles } from 'src/SeedData/Role';
// where: { Name: keywords },//and查询
// where: [{ Name: keywords }],//or查询
// where: { Name: Like("%out #%") },//like查询
// where: { Id: In([0,1]) },//in查询
// where: { Name: Like("%out #%") },//like查询
@Injectable()
export class RoleService {
private readonly tableName: string = "系统角色";
constructor(
public readonly loggerPrint: LoggerPrint,
public readonly logger: Logger,
@InjectRepository(SysRole)
public readonly repository: Repository<SysRole>,
@InjectRepository(SysRoleMenu)
public readonly roleMenuRepository: Repository<SysRoleMenu>,
) {
}
//根据角色Id获得路由菜单
async getMenusByRoleId(id: number): Promise<SysRoleMenu[]> {
//根据角色Id获得角色菜单映射数据
const result = await this.roleMenuRepository.find({
where: { RoleId: id }
});
return result;
}
//#region 基础控制器
// 初始化数据
async seedData(): Promise<number[]> {
let result: InsertResult;
let result1: InsertResult;
await this.repository.queryRunner?.startTransaction();
try {
await this.repository.clear();
result = await this.repository.createQueryBuilder().insert().values(roles).execute();
result1 = await this.roleMenuRepository.createQueryBuilder().insert().values(roleMenus).execute();
await this.repository.queryRunner?.commitTransaction();
await this.repository.createQueryBuilder().useTransaction(true);
return [result.identifiers.length, result1.identifiers.length]
} catch (err) {
//如果遇到错误,可以回滚事务
await this.repository.queryRunner?.rollbackTransaction();
this.logger.warn(err);
return [0, 0];
}
}
async getPage(
page: number = 1,
pageSize: number = 10,
keywords: string = "",
orderBy: string = ""
): Promise<[SysRole[], number]> {
const result = await this.repository.findAndCount({
where: [{ Name: Like("%" + keywords + "%") }],
order: {
Id: "ASC"
},
skip: page - 1,
take: pageSize
})
for await (let item of result[0]) {
const roleMenuResult = await this.roleMenuRepository.find({
where: { RoleId: item.Id },
})
item.SysRoleMenu = roleMenuResult;
}
return result;
}
async getList(
keywords: string = "",
orderBy: string = ""
): Promise<SysRole[]> {
const result = await this.repository.find({
where: [{ Name: Like("%" + keywords + "%") }],
order: {
Id: "ASC"
},
})
for await (let item of result) {
const roleMenuResult = await this.roleMenuRepository.find({
where: { RoleId: item.Id },
})
item.SysRoleMenu = roleMenuResult;
}
return result;
}
async getById(id: number): Promise<SysRole> {
const result = await this.repository.findOne({ where: { Id: id } });
return result;
}
async getByIds(ids: number[]): Promise<SysRole[]> {
const result = await this.repository.find({
where: { Id: In(ids) }
})
return result;
}
async save(model: SysRole): Promise<boolean> {
await this.repository.queryRunner?.startTransaction();
try {
//如果是编辑先删除主子表后,修改主表,新增子表
if (model.Id > 0) {
await this.repository.createQueryBuilder().update().set({ Name: model.Name, Remark: model.Remark, Status: model.Status }).where("Id = :Id", { Id: model.Id }).execute();
await this.roleMenuRepository.delete({ RoleId: model.Id });
for await (let item of model.SysRoleMenu) {
item.RoleId = model.Id
}
} else {
const result = await this.repository.createQueryBuilder().insert().values(model).execute();
for await (let item of model.SysRoleMenu) {
item.RoleId = result.identifiers[0].Id
}
}
await this.roleMenuRepository.createQueryBuilder().insert().values(model.SysRoleMenu).execute();
await this.repository.queryRunner?.commitTransaction();
await this.repository.createQueryBuilder().useTransaction(true);
return true;
} catch (err) {
await this.repository.queryRunner?.rollbackTransaction();
this.logger.warn(err);
return false;
}
}
async destroy(id: number): Promise<boolean> {
await this.repository.queryRunner?.startTransaction();
try {
//删除主子表
await this.repository.delete({ Id: id });
await this.roleMenuRepository.delete({ RoleId: id });
await this.repository.queryRunner?.commitTransaction();
await this.repository.createQueryBuilder().useTransaction(true);
return true;
} catch (err) {
await this.repository.queryRunner?.rollbackTransaction();
this.logger.warn(err);
return false;
}
}
//#endregion
}

View File

@ -1,93 +0,0 @@
/*
* @Author: '490912587@qq.com' '490912587@qq.com'
* @Date: 2022-11-17 17:08:52
* @LastEditors: '490912587@qq.com' '490912587@qq.com'
* @LastEditTime: 2022-11-24 10:15:57
* @FilePath: \tsplatform\backend\src\Service\UserService.ts
* @Description: 系统用户服务
*/
import { Injectable, Logger } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { DeleteResult, In, InsertResult, Like, Repository } from 'typeorm';
import { LoggerPrint } from 'src/Logging/LoggerPrint';
import { SysUser } from 'src/Entity/SysUser';
import { users } from 'src/SeedData/User';
@Injectable()
export class UserService {
private readonly tableName: string = "系统用户";
constructor(
public readonly loggerPrint: LoggerPrint,
public readonly logger: Logger,
@InjectRepository(SysUser)
public readonly repository: Repository<SysUser>
) {
}
//#region 基础控制器
// 初始化数据
async seedData(): Promise<number> {
let result: InsertResult;
await this.repository.queryRunner?.startTransaction();
try {
await this.repository.clear();
result = await this.repository.createQueryBuilder().insert().values(users).execute();
await this.repository.queryRunner?.commitTransaction();
await this.repository.createQueryBuilder().useTransaction(true);
return result.identifiers.length;
} catch (err) {
//如果遇到错误,可以回滚事务
await this.repository.queryRunner?.rollbackTransaction();
this.logger.warn(err);
return 0;
}
}
async getPage(
page: number = 1,
pageSize: number = 10,
keywords: string = "",
orderBy: string = ""
): Promise<[SysUser[], number]> {
const result = await this.repository.findAndCount({
where: [{ Phone: Like("%" + keywords + "%") }, { UserName: Like("%" + keywords + "%") }, { NickName: Like("%" + keywords + "%") }],
order: {
Id: "ASC"
},
skip: page - 1,
take: pageSize
})
return result;
}
async getList(
keywords: string = "",
orderBy: string = ""
): Promise<SysUser[]> {
const result = await this.repository.find({
where: [{ Phone: Like("%" + keywords + "%") }, { UserName: Like("%" + keywords + "%") }, { NickName: Like("%" + keywords + "%") }],
order: {
Id: "ASC"
},
})
return result;
}
async getById(id: number): Promise<SysUser> {
const result = await this.repository.findOne({ where: { Id: id } });
return result;
}
async getByIds(ids: number[]): Promise<SysUser[]> {
const result = await this.repository.find({
where: { Id: In(ids) },
})
return result;
}
async save(model: SysUser): Promise<SysUser> {
const result = await this.repository.save(model);
return result;
}
async destroy(id: number): Promise<DeleteResult> {
const result = await this.repository.delete(id);
return result;
}
//#endregion
}

View File

@ -1,50 +0,0 @@
/*
* @Author: '490912587@qq.com' '490912587@qq.com'
* @Date: 2022-11-17 11:44:48
* @LastEditors: '490912587@qq.com' '490912587@qq.com'
* @LastEditTime: 2022-11-23 17:51:54
* @FilePath: \tsplatform\backend\src\Utils\LoadModules.ts
* @Description: 全局加载模块
*/
import { Logger } from '@nestjs/common';
import { RestfulReturn } from 'src/Expand/RestfulReturn';
import { LoggerPrint } from 'src/Logging/LoggerPrint';
import { JwtService } from '@nestjs/jwt';
import { SysMenu } from 'src/Entity/SysMenu';
import { SysCity } from 'src/Entity/SysCity';
import { SysUser } from 'src/Entity/SysUser';
import { SysRole } from 'src/Entity/SysRole';
import { SysRoleMenu } from 'src/Entity/SysRoleMenu';
import { IndexController } from 'src/Controller/IndexController';
import { MenuController } from 'src/Controller/MenuController';
import { AuthController } from 'src/Controller/AuthController';
import { UserController } from 'src/Controller/UserController';
import { RoleController } from 'src/Controller/RoleController';
import { CityController } from 'src/Controller/CityController';
import { MenuService } from 'src/Service/MenuService';
import { UserService } from 'src/Service/UserService';
import { RoleService } from 'src/Service/RoleService';
import { Repository } from 'typeorm';
import { CityService } from 'src/Service/CityService';
//自动导入Entity
export function LoadPlatFormEntity() {
return [SysCity, SysMenu, SysUser, SysRole, SysRoleMenu];
}
//自动导入Controller
export function LoadController() {
return [IndexController, CityController, MenuController, UserController, AuthController, RoleController,];
}
//自动导入Service服务
export function LoadService() {
return [
//特殊服务提供者
RestfulReturn, Logger, LoggerPrint, Repository,JwtService,
//Service服务提供者
MenuService, UserService, RoleService, CityService
];
}

View File

@ -1,61 +0,0 @@
/*
* @Author: '490912587@qq.com' '490912587@qq.com'
* @Date: 2022-11-17 09:19:20
* @LastEditors: '490912587@qq.com' '490912587@qq.com'
* @LastEditTime: 2022-11-24 10:16:52
* @FilePath: \tsplatform\backend\src\app.module.ts
* @Description: 全局模块入口
*/
import { MiddlewareConsumer, Module, NestModule, RequestMethod } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { TypeOrmModule } from '@nestjs/typeorm';
import { LoadController, LoadPlatFormEntity, LoadService } from 'src/Utils/LoadModules';
import { AppConfig, DataBaseConfig } from 'src/Config/Index';
import { JwtHandle } from 'src/Middleware/JwtHandle';
@Module({
imports: [
TypeOrmModule.forFeature(LoadPlatFormEntity()),
TypeOrmModule.forRootAsync({
useFactory: () => ({
debug: false,
//重试连接数据库的次数
retryAttempts: 10,
//两次重试连接的间隔(ms)
retryDelay: 3000,
//自动加载实体
autoLoadEntities: true,
type: 'mysql',
host: DataBaseConfig.DataBase.host,
port: DataBaseConfig.DataBase.port,
username: DataBaseConfig.DataBase.username,
password: DataBaseConfig.DataBase.password,
database: DataBaseConfig.DataBase.database,
entities: LoadPlatFormEntity(),
logging: AppConfig.debug,
synchronize: AppConfig.debug,
})
}),
ConfigModule.forRoot({
isGlobal: true,
envFilePath: ['.env', '.env.development', '.env.production'],
}),
],
controllers: LoadController(),
providers: LoadService()
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(JwtHandle)
.exclude(
{ path: 'auth/login', method: RequestMethod.POST },
{ path: 'auth/getUserInfo', method: RequestMethod.GET },
{ path: '/seed', method: RequestMethod.GET },
)
.forRoutes("*");
}
}

View File

@ -1,33 +0,0 @@
/*
* @Author: '490912587@qq.com' '490912587@qq.com'
* @Date: 2022-11-17 09:19:20
* @LastEditors: '490912587@qq.com' '490912587@qq.com'
* @LastEditTime: 2022-11-24 11:17:23
* @FilePath: \tsplatform\backend\src\main.ts
* @Description: 主程序入口
*/
import { NestFactory } from '@nestjs/core';
import { AppModule } from 'src/app.module';
import { NestExpressApplication } from '@nestjs/platform-express';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { AppConfig } from 'src/Config/Index';
import { ValidationPipe } from '@nestjs/common';
async function bootstrap() {
const app = await NestFactory.create<NestExpressApplication>(AppModule, { logger: ['error', 'warn', "log", "debug", "verbose"], cors: true });
//全局Dto参数校验管道
app.useGlobalPipes(new ValidationPipe());
const config = new DocumentBuilder()
//基础文档里添加安全定义
.addBearerAuth()
.setTitle('甜蜜蜜TS开发总平台')
.setDescription('甜蜜蜜TS开发总平台')
.setContact("甜蜜蜜", "https://gitee.com/tmm-top/vue-next-admin-ts", "490912587@qq.com")
.setVersion('1.0')
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('/api', app, document);
await app.listen(AppConfig.port, '0.0.0.0');
}
bootstrap();

View File

@ -1,24 +0,0 @@
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import * as request from 'supertest';
import { AppModule } from './../src/app.module';
describe('AppController (e2e)', () => {
let app: INestApplication;
beforeEach(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
});
it('/ (GET)', () => {
return request(app.getHttpServer())
.get('/')
.expect(200)
.expect('Hello World!');
});
});

View File

@ -1,9 +0,0 @@
{
"moduleFileExtensions": ["js", "json", "ts"],
"rootDir": ".",
"testEnvironment": "node",
"testRegex": ".e2e-spec.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
}
}

View File

@ -1,4 +0,0 @@
{
"extends": "./tsconfig.json",
"exclude": ["node_modules", "test", "dist", "**/*spec.ts"],
}

View File

@ -1,21 +0,0 @@
{
"compilerOptions": {
"module": "commonjs",
"declaration": true,
"removeComments": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"target": "es2022",
"sourceMap": true,
"outDir": "./dist",
"baseUrl": "./",
"incremental": true,
"skipLibCheck": true,
"strictNullChecks": false,
"noImplicitAny": false,
"strictBindCallApply": false,
"forceConsistentCasingInFileNames": false,
"noFallthroughCasesInSwitch": false
}
}

9
doc/.gitignore vendored
View File

@ -1,9 +0,0 @@
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace
# Local History for Visual Studio Code
.history/

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

25
frontend/.gitignore vendored
View File

@ -1,25 +0,0 @@
.DS_Store
node_modules
/dist
# local env files
.env
.env.development
.env.production
*.lock
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
package-lock.json
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

View File

@ -1,82 +0,0 @@
{
"name": "tsplatform",
"version": "1.1.0",
"description": "vue3 vite next admin template",
"author": "SweetHoney",
"license": "MIT",
"scripts": {
"dev": "vite --force",
"build": "vite build",
"lint-fix": "eslint --fix --ext .js --ext .jsx --ext .vue src/"
},
"dependencies": {
"@element-plus/icons-vue": "^2.0.10",
"@wangeditor/editor-for-vue": "^5.1.11",
"axios": "^1.1.3",
"countup.js": "^2.3.2",
"cropperjs": "^1.5.12",
"echarts": "^5.4.0",
"echarts-gl": "^2.0.9",
"echarts-wordcloud": "^2.0.0",
"element-plus": "^2.2.21",
"js-cookie": "^3.0.1",
"jsplumb": "^2.15.6",
"mitt": "^3.0.0",
"nprogress": "^0.2.0",
"pinia": "^2.0.23",
"print-js": "^1.6.0",
"qrcodejs2-fixes": "^0.0.2",
"screenfull": "^6.0.2",
"sortablejs": "^1.15.0",
"splitpanes": "^3.1.5",
"ts-md5": "^1.3.1",
"vue": "^3.2.45",
"vue-clipboard3": "^2.0.0",
"vue-grid-layout": "^3.0.0-beta1",
"vue-i18n": "^9.2.2",
"vue-router": "^4.1.6"
},
"devDependencies": {
"@types/node": "^18.11.9",
"@types/nprogress": "^0.2.0",
"@types/sortablejs": "^1.15.0",
"@typescript-eslint/eslint-plugin": "^5.43.0",
"@typescript-eslint/parser": "^5.43.0",
"@vitejs/plugin-vue": "^3.2.0",
"@vue/compiler-sfc": "^3.2.45",
"dotenv": "^16.0.3",
"eslint": "^8.27.0",
"eslint-plugin-vue": "^9.7.0",
"prettier": "^2.7.1",
"sass": "^1.56.1",
"sass-loader": "^13.2.0",
"typescript": "^4.9.3",
"vite": "^3.2.4",
"vue-eslint-parser": "^9.1.0"
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead"
],
"bugs": {
"url": "https://gitee.com/lyt-top/vue-next-admin/issues"
},
"engines": {
"node": ">=12.0.0",
"npm": ">= 6.0.0"
},
"keywords": [
"vue",
"vue3",
"vuejs/vue-next",
"element-ui",
"element-plus",
"vue-next-admin",
"next-admin"
],
"repository": {
"type": "git",
"url": "https://gitee.com/lyt-top/vue-next-admin.git"
}
}

View File

@ -1,4 +0,0 @@
declare module 'vue-grid-layout';
declare module 'qrcodejs2-fixes';
declare module 'splitpanes';
declare module 'js-cookie';

13
frontend/shim.d.ts vendored
View File

@ -1,13 +0,0 @@
/* eslint-disable */
// 声明文件,*.vue 后缀的文件交给 vue 模块来处理
declare module '*.vue' {
import type { DefineComponent } from 'vue';
const component: DefineComponent<{}, {}, any>;
export default component;
}
// 声明文件,定义全局变量。其它 app.config.globalProperties.xxx使用 getCurrentInstance() 来获取
interface Window {
nextLoading: boolean;
}

View File

@ -1,6 +0,0 @@
declare module '*.json';
declare module '*.png';
declare module '*.jpg';
declare module '*.scss';
declare module '*.ts';
declare module '*.js';

View File

@ -1,107 +0,0 @@
<!--
* @Author: '490912587@qq.com' '490912587@qq.com'
* @Date: 2022-11-17 09:19:20
* @LastEditors: '490912587@qq.com' '490912587@qq.com'
* @LastEditTime: 2022-11-23 17:25:54
* @FilePath: \frontend\src\App.vue
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
-->
<template>
<el-config-provider :size="getGlobalComponentSize" :locale="getGlobalI18n">
<router-view v-show="themeConfig.lockScreenTime > 1" />
<LockScreen v-if="themeConfig.isLockScreen" />
<Setings ref="setingsRef" v-show="themeConfig.lockScreenTime > 1" />
<CloseFull v-if="!themeConfig.isLockScreen" />
</el-config-provider>
</template>
<script lang="ts">
import { defineAsyncComponent, computed, ref, onBeforeMount, onMounted, onUnmounted, nextTick, defineComponent, watch } from 'vue';
import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n';
import { storeToRefs } from 'pinia';
import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
import { useThemeConfig } from '/@/stores/themeConfig';
import other from '/@/utils/other';
import { Local, Session } from '/@/utils/storage';
import mittBus from '/@/utils/mitt';
import setIntroduction from '/@/utils/setIconfont';
export default defineComponent({
name: 'app',
components: {
LockScreen: defineAsyncComponent(() => import('/@/layout/lockScreen/index.vue')),
Setings: defineAsyncComponent(() => import('/@/layout/navBars/breadcrumb/setings.vue')),
CloseFull: defineAsyncComponent(() => import('/@/layout/navBars/breadcrumb/closeFull.vue')),
},
setup() {
const { messages, locale } = useI18n();
const setingsRef = ref();
const route = useRoute();
const stores = useTagsViewRoutes();
const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig);
// 获取全局组件大小
const getGlobalComponentSize = computed(() => {
return other.globalComponentSize();
});
// 获取全局 i18n
const getGlobalI18n = computed(() => {
return messages.value[locale.value];
});
// 设置初始化,防止刷新时恢复默认
onBeforeMount(() => {
// 设置批量第三方 icon 图标
setIntroduction.cssCdn();
// 设置批量第三方 js
setIntroduction.jsCdn();
});
// 页面加载时
onMounted(() => {
nextTick(() => {
// 监听布局配置弹窗点击打开
mittBus.on('openSetingsDrawer', () => {
setingsRef.value.openDrawer();
});
// 获取缓存中的布局配置
if (Local.get('themeConfig')) {
storesThemeConfig.setThemeConfig(Local.get('themeConfig'));
document.documentElement.style.cssText = Local.get('themeConfigStyle');
}
// 获取缓存中的全屏配置
if (Session.get('isTagsViewCurrenFull')) {
stores.setCurrenFullscreen(Session.get('isTagsViewCurrenFull'));
}
});
});
// 页面销毁时,关闭监听布局配置/i18n监听
onUnmounted(() => {
mittBus.off('openSetingsDrawer', () => {});
});
// 监听路由的变化,设置网站标题
watch(
() => route.path,
() => {
other.useTitle();
},
{
deep: true,
}
);
return {
themeConfig,
setingsRef,
getGlobalComponentSize,
getGlobalI18n,
};
},
});
</script>
<style>
.searchBox {
display: flex;
flex-direction: row;
justify-content: flex-start;
}
</style>

View File

@ -1,23 +0,0 @@
/*
* @Author: '490912587@qq.com' '490912587@qq.com'
* @Date: 2022-11-17 09:19:20
* @LastEditors: '490912587@qq.com' '490912587@qq.com'
* @LastEditTime: 2022-11-24 09:43:23
* @FilePath: \tsplatform\frontend\src\api\menu\index.ts
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import request from '/@/utils/request';
export function getPage(params: object) {
return request({
url: '/city/getPage',
method: 'get',
params
});
}
export function getList(params: object) {
return request({
url: '/city/getList',
method: 'get',
params
});
}

View File

@ -1,34 +0,0 @@
/*
* @Author: '490912587@qq.com' '490912587@qq.com'
* @Date: 2022-11-17 09:19:20
* @LastEditors: '490912587@qq.com' '490912587@qq.com'
* @LastEditTime: 2022-11-23 17:01:13
* @FilePath: \tsplatform\frontend\src\api\login\index.ts
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import request from '/@/utils/request';
/**
* 登录api接口集合
* @method signIn 用户登录
* @method signOut 用户退出登录
*/
export function signIn(params: object) {
return request({
url: '/auth/login',
method: 'post',
data: params,
});
}
export function getUserInfo() {
return request({
url: '/auth/getUserInfo',
method: 'get'
});
}
export function signOut() {
return request({
url: '/auth/loginOut',
method: 'get'
});
}

View File

@ -1,51 +0,0 @@
/*
* @Author: '490912587@qq.com' '490912587@qq.com'
* @Date: 2022-11-17 09:19:20
* @LastEditors: '490912587@qq.com' '490912587@qq.com'
* @LastEditTime: 2022-11-24 00:24:11
* @FilePath: \tsplatform\frontend\src\api\menu\index.ts
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import request from '/@/utils/request';
export function getAuthMenu(params: object) {
return request({
url: '/menu/getAuthMenuTree',
method: 'get',
params,
});
}
export function getMenuTree(params: object) {
return request({
url: '/menu/getMenuTree',
method: 'get',
params,
});
}
export function add(params: object) {
return request({
url: '/menu/add',
method: 'post',
data: params
});
}
export function edit(params: object) {
return request({
url: '/menu/edit',
method: 'post',
data: params
});
}
export function del(params: object) {
return request({
url: '/menu/del',
method: 'post',
data: params
});
}
export function setStatus(params: object) {
return request({
url: '/menu/setStatus',
method: 'post',
data: params
});
}

View File

@ -1,52 +0,0 @@
/*
* @Author: '490912587@qq.com' '490912587@qq.com'
* @Date: 2022-11-17 09:19:20
* @LastEditors: '490912587@qq.com' '490912587@qq.com'
* @LastEditTime: 2022-11-23 21:35:46
* @FilePath: \tsplatform\frontend\src\api\menu\index.ts
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import request from '/@/utils/request';
export function getPage(params: object) {
return request({
url: '/role/getPage',
method: 'get',
params
});
}
export function getList(params: object) {
return request({
url: '/role/getList',
method: 'get',
params
});
}
export function add(params: object) {
return request({
url: '/role/add',
method: 'post',
data: params
});
}
export function edit(params: object) {
return request({
url: '/role/edit',
method: 'post',
data: params
});
}
export function del(params: object) {
return request({
url: '/role/del',
method: 'post',
data: params
});
}
export function setStatus(params: object) {
return request({
url: '/role/setStatus',
method: 'post',
data: params
});
}

View File

@ -1,51 +0,0 @@
/*
* @Author: '490912587@qq.com' '490912587@qq.com'
* @Date: 2022-11-17 09:19:20
* @LastEditors: '490912587@qq.com' '490912587@qq.com'
* @LastEditTime: 2022-11-23 21:35:53
* @FilePath: \tsplatform\frontend\src\api\menu\index.ts
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import request from '/@/utils/request';
export function getPage(params: object) {
return request({
url: '/user/getPage',
method: 'get',
params
});
}
export function getList(params: object) {
return request({
url: '/role/getList',
method: 'get',
params
});
}
export function add(params: object) {
return request({
url: '/user/add',
method: 'post',
data: params
});
}
export function edit(params: object) {
return request({
url: '/user/edit',
method: 'post',
data: params
});
}
export function del(params: object) {
return request({
url: '/user/del',
method: 'post',
data: params
});
}
export function setStatus(params: object) {
return request({
url: '/user/setStatus',
method: 'post',
data: params
});
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -1,30 +0,0 @@
<template>
<slot v-if="getUserAuthBtnList" />
</template>
<script lang="ts">
import { computed, defineComponent } from 'vue';
import { storeToRefs } from 'pinia';
import { useUserInfo } from '/@/stores/userInfo';
export default defineComponent({
name: 'auth',
props: {
value: {
type: String,
default: () => '',
},
},
setup(props) {
const stores = useUserInfo();
const { userInfos } = storeToRefs(stores);
// 获取 vuex 中的用户权限
const getUserAuthBtnList = computed(() => {
return userInfos.value.authBtnList.some((v: string) => v === props.value);
});
return {
getUserAuthBtnList,
};
},
});
</script>

View File

@ -1,31 +0,0 @@
<template>
<slot v-if="getUserAuthBtnList" />
</template>
<script lang="ts">
import { computed, defineComponent } from 'vue';
import { storeToRefs } from 'pinia';
import { useUserInfo } from '/@/stores/userInfo';
import { judementSameArr } from '/@/utils/arrayOperation';
export default defineComponent({
name: 'authAll',
props: {
value: {
type: Array,
default: () => [],
},
},
setup(props) {
const stores = useUserInfo();
const { userInfos } = storeToRefs(stores);
// 获取 pinia 中的用户权限
const getUserAuthBtnList = computed(() => {
return judementSameArr(props.value, userInfos.value.authBtnList);
});
return {
getUserAuthBtnList,
};
},
});
</script>

View File

@ -1,36 +0,0 @@
<template>
<slot v-if="getUserAuthBtnList" />
</template>
<script lang="ts">
import { computed, defineComponent } from 'vue';
import { storeToRefs } from 'pinia';
import { useUserInfo } from '/@/stores/userInfo';
export default defineComponent({
name: 'auths',
props: {
value: {
type: Array,
default: () => [],
},
},
setup(props) {
const stores = useUserInfo();
const { userInfos } = storeToRefs(stores);
// 获取 vuex 中的用户权限
const getUserAuthBtnList = computed(() => {
let flag = false;
userInfos.value.authBtnList.map((val: string) => {
props.value.map((v) => {
if (val === v) flag = true;
});
});
return flag;
});
return {
getUserAuthBtnList,
};
},
});
</script>

View File

@ -1,149 +0,0 @@
<template>
<div>
<el-dialog title="更换头像" v-model="isShowDialog" width="769px">
<div class="cropper-warp">
<div class="cropper-warp-left">
<img :src="cropperImg" class="cropper-warp-left-img" />
</div>
<div class="cropper-warp-right">
<div class="cropper-warp-right-title">预览</div>
<div class="cropper-warp-right-item">
<div class="cropper-warp-right-value">
<img :src="cropperImgBase64" class="cropper-warp-right-value-img" />
</div>
<div class="cropper-warp-right-label">100 x 100</div>
</div>
<div class="cropper-warp-right-item">
<div class="cropper-warp-right-value">
<img :src="cropperImgBase64" class="cropper-warp-right-value-img cropper-size" />
</div>
<div class="cropper-warp-right-label">50 x 50</div>
</div>
</div>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel" size="default"> </el-button>
<el-button type="primary" @click="onSubmit" size="default"> </el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang="ts">
import { reactive, toRefs, nextTick, defineComponent } from 'vue';
import Cropper from 'cropperjs';
import 'cropperjs/dist/cropper.css';
export default defineComponent({
name: 'cropperIndex',
setup() {
const state = reactive({
isShowDialog: false,
cropperImg: '',
cropperImgBase64: '',
cropper: null,
});
// 打开弹窗
const openDialog = (imgs: any) => {
state.cropperImg = imgs;
state.isShowDialog = true;
nextTick(() => {
initCropper();
});
};
// 关闭弹窗
const closeDialog = () => {
state.isShowDialog = false;
};
// 取消
const onCancel = () => {
closeDialog();
};
// 更换
const onSubmit = () => {
// state.cropperImgBase64 = state.cropper.getCroppedCanvas().toDataURL('image/jpeg');
};
// 初始化cropperjs图片裁剪
const initCropper = () => {
const letImg: any = document.querySelector('.cropper-warp-left-img');
(<any>state.cropper) = new Cropper(letImg, {
viewMode: 1,
dragMode: 'none',
initialAspectRatio: 1,
aspectRatio: 1,
preview: '.before',
background: false,
autoCropArea: 0.6,
zoomOnWheel: false,
crop: () => {
state.cropperImgBase64 = (<any>state.cropper).getCroppedCanvas().toDataURL('image/jpeg');
},
});
};
return {
openDialog,
closeDialog,
onCancel,
onSubmit,
initCropper,
...toRefs(state),
};
},
});
</script>
<style scoped lang="scss">
.cropper-warp {
display: flex;
.cropper-warp-left {
position: relative;
display: inline-block;
height: 350px;
flex: 1;
border: 1px solid var(--el-border-color);
background: var(--el-color-white);
overflow: hidden;
background-repeat: no-repeat;
cursor: move;
border-radius: var(--el-border-radius-base);
.cropper-warp-left-img {
width: 100%;
height: 100%;
}
}
.cropper-warp-right {
width: 150px;
height: 350px;
.cropper-warp-right-title {
text-align: center;
height: 20px;
line-height: 20px;
}
.cropper-warp-right-item {
margin: 15px 0;
.cropper-warp-right-value {
display: flex;
.cropper-warp-right-value-img {
width: 100px;
height: 100px;
border-radius: var(--el-border-radius-circle);
margin: auto;
}
.cropper-size {
width: 50px;
height: 50px;
}
}
.cropper-warp-right-label {
text-align: center;
font-size: 12px;
color: var(--el-text-color-primary);
height: 30px;
line-height: 30px;
}
}
}
}
</style>

View File

@ -1,88 +0,0 @@
<template>
<div class="editor-container">
<Toolbar :editor="editorRef" :mode="mode" />
<Editor :mode="mode" :defaultConfig="editorConfig" :style="{ height }" v-model="editorVal" @onCreated="handleCreated" @onChange="handleChange" />
</div>
</template>
<script lang="ts">
// https://www.wangeditor.com/v5/for-frame.html#vue3
import '@wangeditor/editor/dist/css/style.css';
import { defineComponent, reactive, toRefs, shallowRef, watch, onBeforeUnmount } from 'vue';
import { Toolbar, Editor } from '@wangeditor/editor-for-vue';
export default defineComponent({
name: 'wngEditor',
components: { Toolbar, Editor },
props: {
// 是否禁用
disable: {
type: Boolean,
default: () => true,
},
// 内容框默认 placeholder
placeholder: {
type: String,
default: () => '请输入内容...',
},
// 双向绑定:双向绑定值,字段名为固定,改了之后将不生效
// 参考https://v3.cn.vuejs.org/guide/migration/v-model.html#%E8%BF%81%E7%A7%BB%E7%AD%96%E7%95%A5
modelValue: String,
// https://www.wangeditor.com/v5/getting-started.html#mode-%E6%A8%A1%E5%BC%8F
// 模式,可选 <default|simple>,默认 default
mode: {
type: String,
default: () => 'default',
},
// 高度
height: {
type: String,
default: () => '310px',
},
},
setup(props, { emit }) {
const editorRef = shallowRef();
const state = reactive({
editorConfig: {
placeholder: props.placeholder,
},
editorVal: props.modelValue,
});
// 编辑器回调函数
const handleCreated = (editor: any) => {
editorRef.value = editor;
};
// 编辑器内容改变时
const handleChange = (editor: any) => {
// console.log(editor.getText());
// console.log(editor.getHtml());
emit('update:modelValue', editor.getHtml());
};
// 监听是否禁用改变
// https://gitee.com/lyt-top/vue-next-admin/issues/I4LM7I
watch(
() => props.disable,
(bool) => {
const editor = editorRef.value;
if (editor == null) return;
bool ? editor.disable() : editor.enable();
},
{
deep: true,
}
);
// 页面销毁时
onBeforeUnmount(() => {
const editor = editorRef.value;
if (editor == null) return;
editor.destroy();
});
return {
editorRef,
handleCreated,
handleChange,
...toRefs(state),
};
},
});
</script>

View File

@ -1,252 +0,0 @@
<template>
<div class="icon-selector w100 h100">
<el-popover
placement="bottom"
:width="fontIconWidth"
trigger="click"
transition="el-zoom-in-top"
popper-class="icon-selector-popper"
@show="onPopoverShow"
>
<template #reference>
<el-input
v-model="fontIconSearch"
:placeholder="fontIconPlaceholder"
:clearable="clearable"
:disabled="disabled"
:size="size"
ref="inputWidthRef"
@clear="onClearFontIcon"
@focus="onIconFocus"
@blur="onIconBlur"
>
<template #prepend>
<SvgIcon
:name="fontIconPrefix === '' ? prepend : fontIconPrefix"
class="font14"
v-if="fontIconPrefix === '' ? prepend?.indexOf('ele-') > -1 : fontIconPrefix?.indexOf('ele-') > -1"
/>
<i v-else :class="fontIconPrefix === '' ? prepend : fontIconPrefix" class="font14"></i>
</template>
</el-input>
</template>
<template #default>
<div class="icon-selector-warp">
<div class="icon-selector-warp-title flex">
<div class="flex-auto">{{ title }}</div>
<div class="icon-selector-warp-title-tab" v-if="type === 'all'">
<span :class="{ 'span-active': fontIconType === 'ali' }" @click="onIconChange('ali')" class="ml10" title="iconfont 图标">ali</span>
<span :class="{ 'span-active': fontIconType === 'ele' }" @click="onIconChange('ele')" class="ml10" title="elementPlus 图标">ele</span>
<span :class="{ 'span-active': fontIconType === 'awe' }" @click="onIconChange('awe')" class="ml10" title="fontawesome 图标">awe</span>
</div>
</div>
<div class="icon-selector-warp-row">
<el-scrollbar ref="selectorScrollbarRef">
<el-row :gutter="10" v-if="fontIconSheetsFilterList.length > 0">
<el-col :xs="6" :sm="4" :md="4" :lg="4" :xl="4" @click="onColClick(v)" v-for="(v, k) in fontIconSheetsFilterList" :key="k">
<div class="icon-selector-warp-item" :class="{ 'icon-selector-active': fontIconPrefix === v }">
<div class="flex-margin">
<div class="icon-selector-warp-item-value">
<SvgIcon :name="v" />
</div>
</div>
</div>
</el-col>
</el-row>
<el-empty :image-size="100" v-if="fontIconSheetsFilterList.length <= 0" :description="emptyDescription"></el-empty>
</el-scrollbar>
</div>
</div>
</template>
</el-popover>
</div>
</template>
<script lang="ts">
import { ref, toRefs, reactive, onMounted, nextTick, computed, watch, defineComponent } from 'vue';
import initIconfont from '/@/utils/getStyleSheets';
export default defineComponent({
name: 'iconSelector',
emits: ['update:modelValue', 'get', 'clear'],
props: {
// 输入框前置内容
prepend: {
type: String,
default: () => 'ele-Pointer',
},
// 输入框占位文本
placeholder: {
type: String,
default: () => '请输入内容搜索图标或者选择图标',
},
// 输入框占位文本
size: {
type: String,
default: () => 'default',
},
// 弹窗标题
title: {
type: String,
default: () => '请选择图标',
},
// icon 图标类型
type: {
type: String,
default: () => 'ele',
},
// 禁用
disabled: {
type: Boolean,
default: () => false,
},
// 是否可清空
clearable: {
type: Boolean,
default: () => true,
},
// 自定义空状态描述文字
emptyDescription: {
type: String,
default: () => '无相关图标',
},
// 双向绑定值,默认为 modelValue
// 参考https://v3.cn.vuejs.org/guide/migration/v-model.html#%E8%BF%81%E7%A7%BB%E7%AD%96%E7%95%A5
// 参考https://v3.cn.vuejs.org/guide/component-custom-events.html#%E5%A4%9A%E4%B8%AA-v-model-%E7%BB%91%E5%AE%9A
modelValue: String,
},
setup(props, { emit }) {
const inputWidthRef = ref();
const selectorScrollbarRef = ref();
const state = reactive({
fontIconPrefix: '',
fontIconWidth: 0,
fontIconSearch: '',
fontIconTabsIndex: 0,
fontIconSheetsList: [],
fontIconPlaceholder: '',
fontIconType: 'ali',
fontIconShow: true,
});
// 处理 input 获取焦点时modelValue 有值时,改变 input 的 placeholder 值
const onIconFocus = () => {
if (!props.modelValue) return false;
state.fontIconSearch = '';
state.fontIconPlaceholder = props.modelValue;
};
// 处理 input 失去焦点时,为空将清空 input 值,为点击选中图标时,将取原先值
const onIconBlur = () => {
setTimeout(() => {
const icon = state.fontIconSheetsList.filter((icon: string) => icon === state.fontIconSearch);
if (icon.length <= 0) state.fontIconSearch = '';
}, 300);
};
// 处理 icon 双向绑定数值回显
const initModeValueEcho = () => {
if (props.modelValue === '') return ((<string | undefined>state.fontIconPlaceholder) = props.placeholder);
(<string | undefined>state.fontIconPlaceholder) = props.modelValue;
(<string | undefined>state.fontIconPrefix) = props.modelValue;
};
// 处理 icon type 类型为 all 时,类型 ali、ele、awe 回显问题
const initFontIconTypeEcho = () => {
if ((<any>props.modelValue)?.indexOf('iconfont') > -1) onIconChange('ali');
else if ((<any>props.modelValue)?.indexOf('ele-') > -1) onIconChange('ele');
else if ((<any>props.modelValue)?.indexOf('fa') > -1) onIconChange('awe');
else onIconChange('ali');
};
// 图标搜索及图标数据显示
const fontIconSheetsFilterList = computed(() => {
if (!state.fontIconSearch) return state.fontIconSheetsList;
let search = state.fontIconSearch.trim().toLowerCase();
return state.fontIconSheetsList.filter((item: any) => {
if (item.toLowerCase().indexOf(search) !== -1) return item;
});
});
// 获取 input 的宽度
const getInputWidth = () => {
nextTick(() => {
state.fontIconWidth = inputWidthRef.value.$el.offsetWidth;
});
};
// 监听页面宽度改变
const initResize = () => {
window.addEventListener('resize', () => {
getInputWidth();
});
};
// 初始化数据
const initFontIconData = async (type: string) => {
state.fontIconSheetsList = [];
if (type === 'ali') {
await initIconfont.ali().then((res: any) => {
// 阿里字体图标使用 `iconfont xxx`
state.fontIconSheetsList = res.map((i: string) => `iconfont ${i}`);
});
} else if (type === 'ele') {
await initIconfont.ele().then((res: any) => {
state.fontIconSheetsList = res;
});
} else if (type === 'awe') {
await initIconfont.awe().then((res: any) => {
// fontawesome字体图标使用 `fa xxx`
state.fontIconSheetsList = res.map((i: string) => `fa ${i}`);
});
}
// 初始化 input 的 placeholder
// 参考单项数据流https://cn.vuejs.org/v2/guide/components-props.html?#%E5%8D%95%E5%90%91%E6%95%B0%E6%8D%AE%E6%B5%81
state.fontIconPlaceholder = props.placeholder;
// 初始化双向绑定回显
initModeValueEcho();
};
// 图标点击切换
const onIconChange = (type: string) => {
state.fontIconType = type;
initFontIconData(type);
};
// 获取当前点击的 icon 图标
const onColClick = (v: any) => {
state.fontIconPlaceholder = v;
state.fontIconPrefix = v;
emit('get', state.fontIconPrefix);
emit('update:modelValue', state.fontIconPrefix);
};
// 清空当前点击的 icon 图标
const onClearFontIcon = () => {
state.fontIconPrefix = '';
emit('clear', state.fontIconPrefix);
emit('update:modelValue', state.fontIconPrefix);
};
// 监听 Popover 打开,用于双向绑定值回显
const onPopoverShow = () => {
initModeValueEcho();
initFontIconTypeEcho();
};
// 页面加载时
onMounted(() => {
initModeValueEcho();
initResize();
getInputWidth();
});
// 监听双向绑定 modelValue 的变化
watch(
() => props.modelValue,
() => {
initModeValueEcho();
}
);
return {
inputWidthRef,
selectorScrollbarRef,
fontIconSheetsFilterList,
onColClick,
onIconChange,
onClearFontIcon,
onIconFocus,
onIconBlur,
onPopoverShow,
...toRefs(state),
};
},
});
</script>

View File

@ -1,195 +0,0 @@
<template>
<div class="notice-bar" :style="{ background, height: `${height}px` }" v-show="!isMode">
<div class="notice-bar-warp" :style="{ color, fontSize: `${size}px` }">
<i v-if="leftIcon" class="notice-bar-warp-left-icon" :class="leftIcon"></i>
<div class="notice-bar-warp-text-box" ref="noticeBarWarpRef">
<div class="notice-bar-warp-text" ref="noticeBarTextRef" v-if="!scrollable">{{ text }}</div>
<div class="notice-bar-warp-slot" v-else><slot /></div>
</div>
<SvgIcon :name="rightIcon" v-if="rightIcon" class="notice-bar-warp-right-icon" @click="onRightIconClick" />
</div>
</div>
</template>
<script lang="ts">
import { toRefs, reactive, defineComponent, ref, onMounted, nextTick } from 'vue';
export default defineComponent({
name: 'noticeBar',
props: {
// 通知栏模式,可选值为 closeable link
mode: {
type: String,
default: () => '',
},
// 通知文本内容
text: {
type: String,
default: () => '',
},
// 通知文本颜色
color: {
type: String,
default: () => 'var(--el-color-warning)',
},
// 通知背景色
background: {
type: String,
default: () => 'var(--el-color-warning-light-9)',
},
// 字体大小单位px
size: {
type: [Number, String],
default: () => 14,
},
// 通知栏高度单位px
height: {
type: Number,
default: () => 40,
},
// 动画延迟时间 (s)
delay: {
type: Number,
default: () => 1,
},
// 滚动速率 (px/s)
speed: {
type: Number,
default: () => 100,
},
// 是否开启垂直滚动
scrollable: {
type: Boolean,
default: () => false,
},
// 自定义左侧图标
leftIcon: {
type: String,
default: () => '',
},
// 自定义右侧图标
rightIcon: {
type: String,
default: () => '',
},
},
setup(props, { emit }) {
const noticeBarWarpRef = ref();
const noticeBarTextRef = ref();
const state = reactive({
order: 1,
oneTime: 0,
twoTime: 0,
warpOWidth: 0,
textOWidth: 0,
isMode: false,
});
// 初始化 animation 各项参数
const initAnimation = () => {
nextTick(() => {
state.warpOWidth = noticeBarWarpRef.value.offsetWidth;
state.textOWidth = noticeBarTextRef.value.offsetWidth;
document.styleSheets[0].insertRule(`@keyframes oneAnimation {0% {left: 0px;} 100% {left: -${state.textOWidth}px;}}`);
document.styleSheets[0].insertRule(`@keyframes twoAnimation {0% {left: ${state.warpOWidth}px;} 100% {left: -${state.textOWidth}px;}}`);
computeAnimationTime();
setTimeout(() => {
changeAnimation();
}, props.delay * 1000);
});
};
// 计算 animation 滚动时长
const computeAnimationTime = () => {
state.oneTime = state.textOWidth / props.speed;
state.twoTime = (state.textOWidth + state.warpOWidth) / props.speed;
};
// 改变 animation 动画调用
const changeAnimation = () => {
if (state.order === 1) {
noticeBarTextRef.value.style.cssText = `animation: oneAnimation ${state.oneTime}s linear; opactity: 1;}`;
state.order = 2;
} else {
noticeBarTextRef.value.style.cssText = `animation: twoAnimation ${state.twoTime}s linear infinite; opacity: 1;`;
}
};
// 监听 animation 动画的结束
const listenerAnimationend = () => {
noticeBarTextRef.value.addEventListener(
'animationend',
() => {
changeAnimation();
},
false
);
};
// 右侧 icon 图标点击
const onRightIconClick = () => {
if (!props.mode) return false;
if (props.mode === 'closeable') {
state.isMode = true;
emit('close');
} else if (props.mode === 'link') {
emit('link');
}
};
// 页面加载时
onMounted(() => {
if (props.scrollable) return false;
initAnimation();
listenerAnimationend();
});
return {
noticeBarWarpRef,
noticeBarTextRef,
onRightIconClick,
...toRefs(state),
};
},
});
</script>
<style scoped lang="scss">
.notice-bar {
padding: 0 15px;
width: 100%;
border-radius: 4px;
.notice-bar-warp {
display: flex;
align-items: center;
width: 100%;
height: inherit;
.notice-bar-warp-text-box {
flex: 1;
height: inherit;
display: flex;
align-items: center;
overflow: hidden;
position: relative;
.notice-bar-warp-text {
white-space: nowrap;
position: absolute;
left: 0;
}
.notice-bar-warp-slot {
width: 100%;
white-space: nowrap;
:deep(.el-carousel__item) {
display: flex;
align-items: center;
}
}
}
.notice-bar-warp-left-icon {
width: 24px;
font-size: inherit !important;
}
.notice-bar-warp-right-icon {
width: 24px;
text-align: right;
font-size: inherit !important;
&:hover {
cursor: pointer;
}
}
}
}
</style>

View File

@ -1,73 +0,0 @@
<template>
<i v-if="isShowIconSvg" class="el-icon" :style="setIconSvgStyle">
<component :is="getIconName" />
</i>
<div v-else-if="isShowIconImg" :style="setIconImgOutStyle">
<img :src="getIconName" :style="setIconSvgInsStyle" />
</div>
<i v-else :class="getIconName" :style="setIconSvgStyle" />
</template>
<script lang="ts">
import { computed, defineComponent } from 'vue';
export default defineComponent({
name: 'svgIcon',
props: {
// svg 图标组件名字
name: {
type: String,
},
// svg 大小
size: {
type: Number,
default: () => 14,
},
// svg 颜色
color: {
type: String,
},
},
setup(props) {
// 在线链接、本地引入地址前缀
const linesString = ['https', 'http', '/src', '/assets', import.meta.env.VITE_PUBLIC_PATH];
// 获取 icon 图标名称
const getIconName = computed(() => {
return props?.name;
});
// 用于判断 element plus 自带 svg 图标的显示、隐藏
const isShowIconSvg = computed(() => {
return props?.name?.startsWith('ele-');
});
// 用于判断在线链接、本地引入等图标显示、隐藏
const isShowIconImg = computed(() => {
return linesString.find((str) => props.name?.startsWith(str));
});
// 设置图标样式
const setIconSvgStyle = computed(() => {
return `font-size: ${props.size}px;color: ${props.color};`;
});
// 设置图片样式
const setIconImgOutStyle = computed(() => {
return `width: ${props.size}px;height: ${props.size}px;display: inline-block;overflow: hidden;`;
});
// 设置图片样式
// https://gitee.com/lyt-top/vue-next-admin/issues/I59ND0
const setIconSvgInsStyle = computed(() => {
const filterStyle: string[] = [];
const compatibles: string[] = ['-webkit', '-ms', '-o', '-moz'];
compatibles.forEach((j) => filterStyle.push(`${j}-filter: drop-shadow(${props.color} 30px 0);`));
return `width: ${props.size}px;height: ${props.size}px;position: relative;left: -${props.size}px;${filterStyle.join('')}`;
});
return {
getIconName,
isShowIconSvg,
isShowIconImg,
setIconSvgStyle,
setIconImgOutStyle,
setIconSvgInsStyle,
};
},
});
</script>

View File

@ -1,68 +0,0 @@
import { createI18n } from 'vue-i18n';
import pinia from '/@/stores/index';
import { storeToRefs } from 'pinia';
import { useThemeConfig } from '/@/stores/themeConfig';
import zhcnLocale from 'element-plus/lib/locale/lang/zh-cn';
import enLocale from 'element-plus/lib/locale/lang/en';
import zhtwLocale from 'element-plus/lib/locale/lang/zh-tw';
import nextZhcn from '/@/i18n/lang/zh-cn';
import nextEn from '/@/i18n/lang/en';
import nextZhtw from '/@/i18n/lang/zh-tw';
import pagesLoginZhcn from '/@/i18n/pages/login/zh-cn';
import pagesLoginEn from '/@/i18n/pages/login/en';
import pagesLoginZhtw from '/@/i18n/pages/login/zh-tw';
import pagesFormI18nZhcn from '/@/i18n/pages/formI18n/zh-cn';
import pagesFormI18nEn from '/@/i18n/pages/formI18n/en';
import pagesFormI18nZhtw from '/@/i18n/pages/formI18n/zh-tw';
// 定义语言国际化内容
/**
* 说明:
* /src/i18n/lang 下的 ts 为框架的国际化内容
* /src/i18n/pages 下的 ts 为各界面的国际化内容
*/
const messages = {
[zhcnLocale.name]: {
...zhcnLocale,
message: {
...nextZhcn,
...pagesLoginZhcn,
...pagesFormI18nZhcn,
},
},
[enLocale.name]: {
...enLocale,
message: {
...nextEn,
...pagesLoginEn,
...pagesFormI18nEn,
},
},
[zhtwLocale.name]: {
...zhtwLocale,
message: {
...nextZhtw,
...pagesLoginZhtw,
...pagesFormI18nZhtw,
},
},
};
// 读取 pinia 默认语言
const stores = useThemeConfig(pinia);
const { themeConfig } = storeToRefs(stores);
// 导出语言国际化
// https://vue-i18n.intlify.dev/guide/essentials/fallback.html#explicit-fallback-with-one-locale
export const i18n = createI18n({
legacy: false,
silentTranslationWarn: true,
missingWarn: false,
silentFallbackWarn: true,
fallbackWarn: false,
locale: themeConfig.value.globalI18n,
fallbackLocale: zhcnLocale.name,
messages,
});

View File

@ -1,120 +0,0 @@
// 定义内容
export default {
router: {
home: 'home',
system: {
system: 'system',
menu: 'systemMenu',
role: 'systemRole',
user: 'systemUser',
dic: 'systemDic',
city: 'systemDept'
},
personal: 'personal',
},
staticRoutes: {
signIn: 'signIn',
notFound: 'notFound',
noPower: 'noPower',
},
user: {
title0: 'Component size',
title1: 'Language switching',
title2: 'Menu search',
title3: 'Layout configuration',
title4: 'news',
title5: 'Full screen on',
title6: 'Full screen off',
dropdownLarge: 'large',
dropdownDefault: 'default',
dropdownSmall: 'small',
dropdown1: 'home page',
dropdown2: 'Personal Center',
dropdown3: '404',
dropdown4: '401',
dropdown5: 'Log out',
dropdown6: 'Code warehouse',
searchPlaceholder: 'Menu search: support Chinese, routing path',
newTitle: 'notice',
newBtn: 'All read',
newGo: 'Go to the notification center',
newDesc: 'No notice',
logOutTitle: 'Tips',
logOutMessage: 'This operation will log out. Do you want to continue?',
logOutConfirm: 'determine',
logOutCancel: 'cancel',
logOutExit: 'Exiting',
},
tagsView: {
refresh: 'refresh',
close: 'close',
closeOther: 'closeOther',
closeAll: 'closeAll',
fullscreen: 'fullscreen',
closeFullscreen: 'closeFullscreen',
},
notFound: {
foundTitle: 'Wrong address input, please re-enter the address~',
foundMsg: 'You can check the web address first, and then re-enter or give us feedback.',
foundBtn: 'Back to home page',
},
noAccess: {
accessTitle: 'You are not authorized to operate~',
accessMsg: 'Contact information: add QQ group discussion 665452019',
accessBtn: 'Reauthorization',
},
layout: {
configTitle: 'Layout configuration',
oneTitle: 'Global Themes',
twoTopTitle: 'top bar set up',
twoMenuTitle: 'Menu set up',
twoColumnsTitle: 'Columns set up',
twoTopBar: 'Top bar background',
twoTopBarColor: 'Top bar default font color',
twoIsTopBarColorGradual: 'Top bar gradient',
twoMenuBar: 'Menu background',
twoMenuBarColor: 'Menu default font color',
twoIsMenuBarColorGradual: 'Menu gradient',
twoColumnsMenuBar: 'Column menu background',
twoColumnsMenuBarColor: 'Default font color bar menu',
twoIsColumnsMenuBarColorGradual: 'Column gradient',
twoIsColumnsMenuHoverPreload: 'Column Menu Hover Preload',
threeTitle: 'Interface settings',
threeIsCollapse: 'Menu horizontal collapse',
threeIsUniqueOpened: 'Menu accordion',
threeIsFixedHeader: 'Fixed header',
threeIsClassicSplitMenu: 'Classic layout split menu',
threeIsLockScreen: 'Open the lock screen',
threeLockScreenTime: 'screen locking(s/s)',
fourTitle: 'Interface display',
fourIsShowLogo: 'Sidebar logo',
fourIsBreadcrumb: 'Open breadcrumb',
fourIsBreadcrumbIcon: 'Open breadcrumb icon',
fourIsTagsview: 'Open tagsview',
fourIsTagsviewIcon: 'Open tagsview Icon',
fourIsCacheTagsView: 'Enable tagsview cache',
fourIsSortableTagsView: 'Enable tagsview drag',
fourIsShareTagsView: 'Enable tagsview sharing',
fourIsFooter: 'Open footer',
fourIsGrayscale: 'Grey model',
fourIsInvert: 'Color weak mode',
fourIsDark: 'Dark Mode',
fourIsWartermark: 'Turn on watermark',
fourWartermarkText: 'Watermark copy',
fiveTitle: 'Other settings',
fiveTagsStyle: 'Tagsview style',
fiveAnimation: 'page animation',
fiveColumnsAsideStyle: 'Column style',
fiveColumnsAsideLayout: 'Column layout',
sixTitle: 'Layout switch',
sixDefaults: 'One',
sixClassic: 'Two',
sixTransverse: 'Three',
sixColumns: 'Four',
tipText: 'Click the button below to copy the layout configuration to `/src/stores/themeConfig.ts` It has been modified in.',
copyText: 'replication configuration',
resetText: 'restore default',
copyTextSuccess: 'Copy succeeded!',
copyTextError: 'Copy failed!',
},
};

View File

@ -1,120 +0,0 @@
// 定义内容
export default {
router: {
home: '首页',
system: {
system: '系统设置',
menu: '菜单管理',
role: '角色管理',
user: '用户管理',
dic: '字典管理',
city: '城市管理'
},
personal: '个人中心',
},
staticRoutes: {
signIn: '登录',
notFound: '找不到此页面',
noPower: '没有权限',
},
user: {
title0: '组件大小',
title1: '语言切换',
title2: '菜单搜索',
title3: '布局配置',
title4: '消息',
title5: '开全屏',
title6: '关全屏',
dropdownLarge: '大型',
dropdownDefault: '默认',
dropdownSmall: '小型',
dropdown1: '首页',
dropdown2: '个人中心',
dropdown3: '404',
dropdown4: '401',
dropdown5: '退出登录',
dropdown6: '代码仓库',
searchPlaceholder: '菜单搜索:支持中文、路由路径',
newTitle: '通知',
newBtn: '全部已读',
newGo: '前往通知中心',
newDesc: '暂无通知',
logOutTitle: '提示',
logOutMessage: '此操作将退出登录, 是否继续?',
logOutConfirm: '确定',
logOutCancel: '取消',
logOutExit: '退出中',
},
tagsView: {
refresh: '刷新',
close: '关闭',
closeOther: '关闭其它',
closeAll: '全部关闭',
fullscreen: '当前页全屏',
closeFullscreen: '关闭全屏',
},
notFound: {
foundTitle: '地址输入错误,请重新输入地址~',
foundMsg: '您可以先检查网址,然后重新输入或给我们反馈问题。',
foundBtn: '返回首页',
},
noAccess: {
accessTitle: '您未被授权,没有操作权限~',
accessMsg: '联系方式加QQ群探讨 665452019',
accessBtn: '重新授权',
},
layout: {
configTitle: '布局配置',
oneTitle: '全局主题',
twoTopTitle: '顶栏设置',
twoMenuTitle: '菜单设置',
twoColumnsTitle: '分栏设置',
twoTopBar: '顶栏背景',
twoTopBarColor: '顶栏默认字体颜色',
twoIsTopBarColorGradual: '顶栏背景渐变',
twoMenuBar: '菜单背景',
twoMenuBarColor: '菜单默认字体颜色',
twoIsMenuBarColorGradual: '菜单背景渐变',
twoColumnsMenuBar: '分栏菜单背景',
twoColumnsMenuBarColor: '分栏菜单默认字体颜色',
twoIsColumnsMenuBarColorGradual: '分栏菜单背景渐变',
twoIsColumnsMenuHoverPreload: '分栏菜单鼠标悬停预加载',
threeTitle: '界面设置',
threeIsCollapse: '菜单水平折叠',
threeIsUniqueOpened: '菜单手风琴',
threeIsFixedHeader: '固定 Header',
threeIsClassicSplitMenu: '经典布局分割菜单',
threeIsLockScreen: '开启锁屏',
threeLockScreenTime: '自动锁屏(s/秒)',
fourTitle: '界面显示',
fourIsShowLogo: '侧边栏 Logo',
fourIsBreadcrumb: '开启 Breadcrumb',
fourIsBreadcrumbIcon: '开启 Breadcrumb 图标',
fourIsTagsview: '开启 Tagsview',
fourIsTagsviewIcon: '开启 Tagsview 图标',
fourIsCacheTagsView: '开启 TagsView 缓存',
fourIsSortableTagsView: '开启 TagsView 拖拽',
fourIsShareTagsView: '开启 TagsView 共用',
fourIsFooter: '开启 Footer',
fourIsGrayscale: '灰色模式',
fourIsInvert: '色弱模式',
fourIsDark: '深色模式',
fourIsWartermark: '开启水印',
fourWartermarkText: '水印文案',
fiveTitle: '其它设置',
fiveTagsStyle: 'Tagsview 风格',
fiveAnimation: '主页面切换动画',
fiveColumnsAsideStyle: '分栏高亮风格',
fiveColumnsAsideLayout: '分栏布局风格',
sixTitle: '布局切换',
sixDefaults: '默认',
sixClassic: '经典',
sixTransverse: '横向',
sixColumns: '分栏',
tipText: '点击下方按钮,复制布局配置去 `src/stores/themeConfig.ts` 中修改。',
copyText: '一键复制配置',
resetText: '一键恢复默认',
copyTextSuccess: '复制成功!',
copyTextError: '复制失败!',
},
};

View File

@ -1,121 +0,0 @@
// 定义内容
export default {
router: {
home: '首頁',
system: {
system: '系統設置',
menu: '選單管理',
role: '角色管理',
user: '用戶管理',
dic: '字典管理',
city: '城市管理'
},
personal: '個人中心',
},
staticRoutes: {
signIn: '登入',
notFound: '找不到此頁面',
noPower: '沒有許可權',
},
user: {
title0: '組件大小',
title1: '語言切換',
title2: '選單蒐索',
title3: '佈局配寘',
title4: '消息',
title5: '開全屏',
title6: '關全屏',
dropdownLarge: '大型',
dropdownDefault: '默認',
dropdownSmall: '小型',
dropdown1: '首頁',
dropdown2: '個人中心',
dropdown3: '404',
dropdown4: '401',
dropdown5: '登出',
dropdown6: '程式碼倉庫',
searchPlaceholder: '選單蒐索:支援中文、路由路徑',
newTitle: '通知',
newBtn: '全部已讀',
newGo: '前往通知中心',
newDesc: '暫無通知',
logOutTitle: '提示',
logOutMessage: '此操作將登出,是否繼續?',
logOutConfirm: '確定',
logOutCancel: '取消',
logOutExit: '退出中',
},
tagsView: {
refresh: '重繪',
close: '關閉',
closeOther: '關閉其它',
closeAll: '全部關閉',
fullscreen: '當前頁全屏',
closeFullscreen: '關閉全屏',
},
notFound: {
foundTitle: '地址輸入錯誤,請重新輸入地址~',
foundMsg: '您可以先檢查網址,然後重新輸入或給我們迴響問題。',
foundBtn: '返回首頁',
},
noAccess: {
accessTitle: '您未被授權,沒有操作許可權~',
accessMsg: '聯繫方式加QQ群探討665452019',
accessBtn: '重新授權',
},
layout: {
configTitle: '佈局配寘',
oneTitle: '全域主題',
twoTopTitle: '頂欄設定',
twoMenuTitle: '選單設定',
twoColumnsTitle: '分欄設定',
twoTopBar: '頂欄背景',
twoTopBarColor: '頂欄默認字體顏色',
twoIsTopBarColorGradual: '頂欄背景漸變',
twoMenuBar: '選單背景',
twoMenuBarColor: '選單默認字體顏色',
twoIsMenuBarColorGradual: '選單背景漸變',
twoColumnsMenuBar: '分欄選單背景',
twoColumnsMenuBarColor: '分欄選單默認字體顏色',
twoIsColumnsMenuBarColorGradual: '分欄選單背景漸變',
twoIsColumnsMenuHoverPreload: '分欄選單滑鼠懸停預加載',
threeTitle: '介面設定',
threeIsCollapse: '選單水准折疊',
threeIsUniqueOpened: '選單手風琴',
threeIsFixedHeader: '固定 Header',
threeIsClassicSplitMenu: '經典佈局分割選單',
threeIsLockScreen: '開啟鎖屏',
threeLockScreenTime: '自動鎖屏(s/秒)',
fourTitle: '介面顯示',
fourIsShowLogo: '側邊欄 Logo',
fourIsBreadcrumb: '開啟 Breadcrumb',
fourIsBreadcrumbIcon: '開啟 Breadcrumb 圖標',
fourIsTagsview: '開啟 Tagsview',
fourIsTagsviewIcon: '開啟 Tagsview 圖標',
fourIsCacheTagsView: '開啟 TagsView 緩存',
fourIsSortableTagsView: '開啟 TagsView 拖拽',
fourIsShareTagsView: '開啟 TagsView 共用',
fourIsFooter: '開啟 Footer',
fourIsGrayscale: '灰色模式',
fourIsInvert: '色弱模式',
fourIsDark: '深色模式',
fourIsWartermark: '開啟浮水印',
fourWartermarkText: '浮水印文案',
fiveTitle: '其它設定',
fiveTagsStyle: 'Tagsview 風格',
fiveAnimation: '主頁面切換動畫',
fiveColumnsAsideStyle: '分欄高亮風格',
fiveColumnsAsideLayout: '分欄佈局風格',
sixTitle: '佈局切換',
sixDefaults: '默認',
sixClassic: '經典',
sixTransverse: '橫向',
sixColumns: '分欄',
tipText: '點擊下方按鈕,複製佈局配寘去`src/stores/themeConfig.ts`中修改。',
copyText: '一鍵複製配寘',
resetText: '一鍵恢復默認',
copyTextSuccess: '複製成功!',
copyTextError: '複製失敗!',
},
};

View File

@ -1,13 +0,0 @@
// 定义内容
export default {
formI18nLabel: {
name: 'name',
email: 'email',
autograph: 'autograph',
},
formI18nPlaceholder: {
name: 'Please enter your name',
email: 'Please enter the users Department',
autograph: 'Please enter the login account name',
},
};

View File

@ -1,13 +0,0 @@
// 定义内容
export default {
formI18nLabel: {
name: '姓名',
email: '用户归属部门',
autograph: '登陆账户名',
},
formI18nPlaceholder: {
name: '请输入姓名',
email: '请输入用户归属部门',
autograph: '请输入登陆账户名',
},
};

View File

@ -1,13 +0,0 @@
// 定义内容
export default {
formI18nLabel: {
name: '姓名',
email: '用戶歸屬部門',
autograph: '登入帳戶名',
},
formI18nPlaceholder: {
name: '請輸入姓名',
email: '請輸入用戶歸屬部門',
autograph: '請輸入登入帳戶名',
},
};

View File

@ -1,29 +0,0 @@
// 定义内容
export default {
label: {
one1: 'User name login',
two2: 'Mobile number',
},
link: {
one3: 'Third party login',
two4: 'Links',
},
account: {
accountPlaceholder1: 'The user name admin or not is common',
accountPlaceholder2: 'Password: 123456',
accountPlaceholder3: 'Please enter the verification code',
accountBtnText: 'Sign in',
},
mobile: {
placeholder1: 'Please input mobile phone number',
placeholder2: 'Please enter the verification code',
codeText: 'Get code',
btnText: 'Sign in',
msgText:
'Warm tip: it is recommended to use Google, Microsoft edge, version 79.0.1072.62 and above browsers, and 360 browser, please use speed mode',
},
scan: {
text: 'Open the mobile phone to scan and quickly log in / register',
},
signInText: 'welcome back!',
};

View File

@ -1,28 +0,0 @@
// 定义内容
export default {
label: {
one1: '用户名登录',
two2: '手机号登录',
},
link: {
one3: '第三方登录',
two4: '友情链接',
},
account: {
accountPlaceholder1: '用户名 admin 或不输均为 common',
accountPlaceholder2: '密码123456',
accountPlaceholder3: '请输入验证码',
accountBtnText: '登 录',
},
mobile: {
placeholder1: '请输入手机号',
placeholder2: '请输入验证码',
codeText: '获取验证码',
btnText: '登 录',
msgText: '* 温馨提示建议使用谷歌、Microsoft Edge版本 79.0.1072.62 及以上浏览器360浏览器请使用极速模式',
},
scan: {
text: '打开手机扫一扫,快速登录/注册',
},
signInText: '欢迎回来!',
};

View File

@ -1,28 +0,0 @@
// 定义内容
export default {
label: {
one1: '用戶名登入',
two2: '手機號登入',
},
link: {
one3: '協力廠商登入',
two4: '友情連結',
},
account: {
accountPlaceholder1: '用戶名admin或不輸均為common',
accountPlaceholder2: '密碼123456',
accountPlaceholder3: '請輸入驗證碼',
accountBtnText: '登入',
},
mobile: {
placeholder1: '請輸入手機號',
placeholder2: '請輸入驗證碼',
codeText: '獲取驗證碼',
btnText: '登入',
msgText: '* 溫馨提示建議使用穀歌、Microsoft Edge版本79.0.1072.62及以上瀏覽器360瀏覽器請使用極速模式',
},
scan: {
text: '打開手機掃一掃,快速登錄/注册',
},
signInText: '歡迎回來!',
};

View File

@ -1,165 +0,0 @@
<template>
<div class="h100" v-show="!isTagsViewCurrenFull">
<el-aside class="layout-aside" :class="setCollapseStyle">
<Logo v-if="setShowLogo" />
<el-scrollbar class="flex-auto" ref="layoutAsideScrollbarRef" @mouseenter="onAsideEnterLeave(true)" @mouseleave="onAsideEnterLeave(false)">
<Vertical :menuList="menuList" />
</el-scrollbar>
</el-aside>
</div>
</template>
<script lang="ts">
import { defineAsyncComponent, toRefs, reactive, computed, watch, onBeforeMount, defineComponent, ref } from 'vue';
import { storeToRefs } from 'pinia';
import pinia from '/@/stores/index';
import { useRoutesList } from '/@/stores/routesList';
import { useThemeConfig } from '/@/stores/themeConfig';
import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
import mittBus from '/@/utils/mitt';
export default defineComponent({
name: 'layoutAside',
components: {
Logo: defineAsyncComponent(() => import('/@/layout/logo/index.vue')),
Vertical: defineAsyncComponent(() => import('/@/layout/navMenu/vertical.vue')),
},
setup() {
const layoutAsideScrollbarRef = ref();
const stores = useRoutesList();
const storesThemeConfig = useThemeConfig();
const storesTagsViewRoutes = useTagsViewRoutes();
const { routesList } = storeToRefs(stores);
const { themeConfig } = storeToRefs(storesThemeConfig);
const { isTagsViewCurrenFull } = storeToRefs(storesTagsViewRoutes);
const state = reactive({
menuList: [],
clientWidth: 0,
});
// 设置菜单展开/收起时的宽度
const setCollapseStyle = computed(() => {
const { layout, isCollapse, menuBar } = themeConfig.value;
const asideBrTheme = ['#FFFFFF', '#FFF', '#fff', '#ffffff'];
const asideBrColor = asideBrTheme.includes(menuBar) ? 'layout-el-aside-br-color' : '';
// 判断是否是手机端
if (state.clientWidth <= 1000) {
if (isCollapse) {
document.body.setAttribute('class', 'el-popup-parent--hidden');
const asideEle = document.querySelector('.layout-container') as HTMLElement;
const modeDivs = document.createElement('div');
modeDivs.setAttribute('class', 'layout-aside-mobile-mode');
asideEle.appendChild(modeDivs);
modeDivs.addEventListener('click', closeLayoutAsideMobileMode);
return [asideBrColor, 'layout-aside-mobile', 'layout-aside-mobile-open'];
} else {
// 关闭弹窗
closeLayoutAsideMobileMode();
return [asideBrColor, 'layout-aside-mobile', 'layout-aside-mobile-close'];
}
} else {
if (layout === 'columns') {
// 分栏布局,菜单收起时宽度给 1px
if (isCollapse) return [asideBrColor, 'layout-aside-pc-1'];
else return [asideBrColor, 'layout-aside-pc-220'];
} else {
// 其它布局给 64px
if (isCollapse) return [asideBrColor, 'layout-aside-pc-64'];
else return [asideBrColor, 'layout-aside-pc-220'];
}
}
});
// 关闭移动端蒙版
const closeLayoutAsideMobileMode = () => {
const el = document.querySelector('.layout-aside-mobile-mode');
el?.setAttribute('style', 'animation: error-img-two 0.3s');
setTimeout(() => {
el?.parentNode?.removeChild(el);
}, 300);
const clientWidth = document.body.clientWidth;
if (clientWidth < 1000) themeConfig.value.isCollapse = false;
document.body.setAttribute('class', '');
};
// 设置显示/隐藏 logo
const setShowLogo = computed(() => {
let { layout, isShowLogo } = themeConfig.value;
return (isShowLogo && layout === 'defaults') || (isShowLogo && layout === 'columns');
});
// 设置/过滤路由(非静态路由/是否显示在菜单中)
const setFilterRoutes = () => {
if (themeConfig.value.layout === 'columns') return false;
(state.menuList as any) = filterRoutesFun(routesList.value);
};
// 路由过滤递归函数
const filterRoutesFun = (arr: Array<string>) => {
return arr
.filter((item: any) => !item.meta.isHide)
.map((item: any) => {
item = Object.assign({}, item);
if (item.children) item.children = filterRoutesFun(item.children);
return item;
});
};
// 设置菜单导航是否固定(移动端)
const initMenuFixed = (clientWidth: number) => {
state.clientWidth = clientWidth;
};
// 鼠标移入、移出
const onAsideEnterLeave = (bool: Boolean) => {
let { layout } = themeConfig.value;
if (layout !== 'columns') return false;
if (!bool) mittBus.emit('restoreDefault');
stores.setColumnsMenuHover(bool);
};
// 监听 themeConfig 配置文件的变化,更新菜单 el-scrollbar 的高度
watch(themeConfig.value, (val) => {
if (val.isShowLogoChange !== val.isShowLogo) {
if (layoutAsideScrollbarRef.value) layoutAsideScrollbarRef.value.update();
}
});
// 监听vuex值的变化动态赋值给菜单中
watch(
pinia.state,
(val) => {
let { layout, isClassicSplitMenu } = val.themeConfig.themeConfig;
if (layout === 'classic' && isClassicSplitMenu) return false;
setFilterRoutes();
},
{
deep: true,
}
);
// 页面加载前
onBeforeMount(() => {
initMenuFixed(document.body.clientWidth);
setFilterRoutes();
// 此界面不需要取消监听(mittBus.off('setSendColumnsChildren))
// 因为切换布局时有的监听需要使用,取消了监听,某些操作将不生效
mittBus.on('setSendColumnsChildren', (res: any) => {
state.menuList = res.children;
});
mittBus.on('setSendClassicChildren', (res: any) => {
let { layout, isClassicSplitMenu } = themeConfig.value;
if (layout === 'classic' && isClassicSplitMenu) {
state.menuList = [];
state.menuList = res.children;
}
});
mittBus.on('getBreadcrumbIndexSetFilterRoutes', () => {
setFilterRoutes();
});
mittBus.on('layoutMobileResize', (res: any) => {
initMenuFixed(res.clientWidth);
closeLayoutAsideMobileMode();
});
});
return {
layoutAsideScrollbarRef,
setCollapseStyle,
setShowLogo,
isTagsViewCurrenFull,
onAsideEnterLeave,
...toRefs(state),
};
},
});
</script>

View File

@ -1,296 +0,0 @@
<template>
<div class="layout-columns-aside">
<el-scrollbar>
<ul @mouseleave="onColumnsAsideMenuMouseleave()">
<li
v-for="(v, k) in columnsAsideList"
:key="k"
@click="onColumnsAsideMenuClick(v, k)"
@mouseenter="onColumnsAsideMenuMouseenter(v, k)"
:ref="
(el) => {
if (el) columnsAsideOffsetTopRefs[k] = el;
}
"
:class="{ 'layout-columns-active': liIndex === k, 'layout-columns-hover': liHoverIndex === k }"
:title="$t(v.meta.title)"
>
<div :class="themeConfig.columnsAsideLayout" v-if="!v.meta.isLink || (v.meta.isLink && v.meta.isIframe)">
<SvgIcon :name="v.meta.icon" />
<div class="columns-vertical-title font12">
{{
$t(v.meta.title) && $t(v.meta.title).length >= 4
? $t(v.meta.title).substr(0, themeConfig.columnsAsideLayout === 'columns-vertical' ? 4 : 3)
: $t(v.meta.title)
}}
</div>
</div>
<div :class="themeConfig.columnsAsideLayout" v-else>
<a :href="v.meta.isLink" target="_blank">
<SvgIcon :name="v.meta.icon" />
<div class="columns-vertical-title font12">
{{
$t(v.meta.title) && $t(v.meta.title).length >= 4
? $t(v.meta.title).substr(0, themeConfig.columnsAsideLayout === 'columns-vertical' ? 4 : 3)
: $t(v.meta.title)
}}
</div>
</a>
</div>
</li>
<div ref="columnsAsideActiveRef" :class="themeConfig.columnsAsideStyle"></div>
</ul>
</el-scrollbar>
</div>
</template>
<script lang="ts">
import { reactive, toRefs, ref, onMounted, nextTick, watch, onUnmounted, defineComponent } from 'vue';
import { useRoute, useRouter, onBeforeRouteUpdate, RouteRecordRaw } from 'vue-router';
import { storeToRefs } from 'pinia';
import pinia from '/@/stores/index';
import { useRoutesList } from '/@/stores/routesList';
import { useThemeConfig } from '/@/stores/themeConfig';
import mittBus from '/@/utils/mitt';
// 定义接口来定义对象的类型
interface ColumnsAsideState {
columnsAsideList: any[];
liIndex: number;
liOldIndex: null | number;
liHoverIndex: null | number;
liOldPath: null | string;
difference: number;
routeSplit: string[];
}
export default defineComponent({
name: 'layoutColumnsAside',
setup() {
const columnsAsideOffsetTopRefs: any = ref([]);
const columnsAsideActiveRef = ref();
const stores = useRoutesList();
const storesThemeConfig = useThemeConfig();
const { routesList, isColumnsMenuHover, isColumnsNavHover } = storeToRefs(stores);
const { themeConfig } = storeToRefs(storesThemeConfig);
const route = useRoute();
const router = useRouter();
const state = reactive<ColumnsAsideState>({
columnsAsideList: [],
liIndex: 0,
liOldIndex: null,
liHoverIndex: null,
liOldPath: null,
difference: 0,
routeSplit: [],
});
// 设置菜单高亮位置移动
const setColumnsAsideMove = (k: number) => {
state.liIndex = k;
columnsAsideActiveRef.value.style.top = `${columnsAsideOffsetTopRefs.value[k].offsetTop + state.difference}px`;
};
// 菜单高亮点击事件
const onColumnsAsideMenuClick = (v: Object, k: number) => {
setColumnsAsideMove(k);
let { path, redirect } = v as any;
if (redirect) router.push(redirect);
else router.push(path);
};
// 鼠标移入时,显示当前的子级菜单
const onColumnsAsideMenuMouseenter = (v: RouteRecordRaw, k: number) => {
if (!themeConfig.value.isColumnsMenuHoverPreload) return false;
let { path } = v;
state.liOldPath = path;
state.liOldIndex = k;
state.liHoverIndex = k;
mittBus.emit('setSendColumnsChildren', setSendChildren(path));
stores.setColumnsMenuHover(false);
stores.setColumnsNavHover(true);
};
// 鼠标移走时,显示原来的子级菜单
const onColumnsAsideMenuMouseleave = async () => {
await stores.setColumnsNavHover(false);
// 添加延时器,防止拿到的 store.state.routesList 值不是最新的
setTimeout(() => {
if (!isColumnsMenuHover && !isColumnsNavHover) mittBus.emit('restoreDefault');
}, 100);
};
// 设置高亮动态位置
const onColumnsAsideDown = (k: number) => {
nextTick(() => {
setColumnsAsideMove(k);
});
};
// 设置/过滤路由(非静态路由/是否显示在菜单中)
const setFilterRoutes = () => {
state.columnsAsideList = filterRoutesFun(routesList.value);
const resData: any = setSendChildren(route.path);
if (Object.keys(resData).length <= 0) return false;
onColumnsAsideDown(resData.item[0].k);
mittBus.emit('setSendColumnsChildren', resData);
};
// 传送当前子级数据到菜单中
const setSendChildren = (path: string) => {
const currentPathSplit = path.split('/');
let currentData: any = {};
state.columnsAsideList.map((v: any, k: number) => {
if (v.path === `/${currentPathSplit[1]}`) {
v['k'] = k;
currentData['item'] = [{ ...v }];
currentData['children'] = [{ ...v }];
if (v.children) currentData['children'] = v.children;
}
});
return currentData;
};
// 路由过滤递归函数
const filterRoutesFun = (arr: Array<string>) => {
return arr
.filter((item: any) => !item.meta.isHide)
.map((item: any) => {
item = Object.assign({}, item);
if (item.children) item.children = filterRoutesFun(item.children);
return item;
});
};
// tagsView 点击时,根据路由查找下标 columnsAsideList实现左侧菜单高亮
const setColumnsMenuHighlight = (path: string) => {
state.routeSplit = path.split('/');
state.routeSplit.shift();
const routeFirst = `/${state.routeSplit[0]}`;
const currentSplitRoute = state.columnsAsideList.find((v: any) => v.path === routeFirst);
if (!currentSplitRoute) return false;
// 延迟拿值,防止取不到
setTimeout(() => {
onColumnsAsideDown((<any>currentSplitRoute).k);
}, 0);
};
// 监听布局配置信息的变化,动态增加菜单高亮位置移动像素
watch(
pinia.state,
(val) => {
val.themeConfig.themeConfig.columnsAsideStyle === 'columnsRound' ? (state.difference = 3) : (state.difference = 0);
if (!val.routesList.isColumnsMenuHover && !val.routesList.isColumnsNavHover) {
state.liHoverIndex = null;
mittBus.emit('setSendColumnsChildren', setSendChildren(route.path));
} else {
state.liHoverIndex = state.liOldIndex;
if (!state.liOldPath) return false;
mittBus.emit('setSendColumnsChildren', setSendChildren(state.liOldPath));
}
},
{
deep: true,
}
);
// 页面加载时
onMounted(() => {
setFilterRoutes();
// 销毁变量,防止鼠标再次移入时,保留了上次的记录
mittBus.on('restoreDefault', () => {
state.liOldIndex = null;
state.liOldPath = null;
});
});
// 页面卸载时
onUnmounted(() => {
mittBus.off('restoreDefault', () => {});
});
// 路由更新时
onBeforeRouteUpdate((to) => {
setColumnsMenuHighlight(to.path);
mittBus.emit('setSendColumnsChildren', setSendChildren(to.path));
});
return {
themeConfig,
columnsAsideOffsetTopRefs,
columnsAsideActiveRef,
onColumnsAsideDown,
onColumnsAsideMenuClick,
onColumnsAsideMenuMouseenter,
onColumnsAsideMenuMouseleave,
...toRefs(state),
};
},
});
</script>
<style scoped lang="scss">
.layout-columns-aside {
width: 70px;
height: 100%;
background: var(--next-bg-columnsMenuBar);
ul {
position: relative;
.layout-columns-active {
color: var(--next-bg-columnsMenuBarColor) !important;
transition: 0.3s ease-in-out;
}
.layout-columns-hover {
color: var(--el-color-primary);
a {
color: var(--el-color-primary);
}
}
li {
color: var(--next-bg-columnsMenuBarColor);
width: 100%;
height: 50px;
text-align: center;
display: flex;
cursor: pointer;
position: relative;
z-index: 1;
&:hover {
@extend .layout-columns-hover;
}
.columns-vertical {
margin: auto;
.columns-vertical-title {
padding-top: 1px;
}
}
.columns-horizontal {
display: flex;
height: 50px;
width: 100%;
align-items: center;
padding: 0 5px;
i {
margin-right: 3px;
}
a {
display: flex;
.columns-horizontal-title {
padding-top: 1px;
}
}
}
a {
text-decoration: none;
color: var(--next-bg-columnsMenuBarColor);
}
}
.columns-round {
background: var(--el-color-primary);
color: var(--el-color-white);
position: absolute;
left: 50%;
top: 2px;
height: 44px;
width: 65px;
transform: translateX(-50%);
z-index: 0;
transition: 0.3s ease-in-out;
border-radius: 5px;
}
.columns-card {
@extend .columns-round;
top: 0;
height: 50px;
width: 100%;
border-radius: 0;
}
}
}
</style>

View File

@ -1,25 +0,0 @@
<template>
<el-header class="layout-header" v-show="!isTagsViewCurrenFull">
<NavBarsIndex />
</el-header>
</template>
<script lang="ts">
import { defineAsyncComponent, defineComponent } from 'vue';
import { storeToRefs } from 'pinia';
import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
export default defineComponent({
name: 'layoutHeader',
components: {
NavBarsIndex: defineAsyncComponent(() => import('/@/layout/navBars/index.vue')),
},
setup() {
const storesTagsViewRoutes = useTagsViewRoutes();
const { isTagsViewCurrenFull } = storeToRefs(storesTagsViewRoutes);
return {
isTagsViewCurrenFull,
};
},
});
</script>

View File

@ -1,59 +0,0 @@
<template>
<el-main class="layout-main" :style="isFixedHeader ? `height: calc(100% - ${setMainHeight})` : `minHeight: calc(100% - ${setMainHeight})`">
<el-scrollbar ref="layoutMainScrollbarRef" class="layout-main-scroll" wrap-class="layout-main-scroll" view-class="layout-main-scroll">
<LayoutParentView />
<LayoutFooter v-if="isFooter" />
</el-scrollbar>
<el-backtop target=".layout-backtop .el-scrollbar__wrap" />
</el-main>
</template>
<script lang="ts">
import { defineAsyncComponent, defineComponent, onMounted, computed, ref } from 'vue';
import { useRoute } from 'vue-router';
import { storeToRefs } from 'pinia';
import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
import { useThemeConfig } from '/@/stores/themeConfig';
import { NextLoading } from '/@/utils/loading';
export default defineComponent({
name: 'layoutMain',
components: {
LayoutParentView: defineAsyncComponent(() => import('/@/layout/routerView/parent.vue')),
LayoutFooter: defineAsyncComponent(() => import('/@/layout/footer/index.vue')),
},
setup() {
const layoutMainScrollbarRef = ref('');
const route = useRoute();
const storesTagsViewRoutes = useTagsViewRoutes();
const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig);
const { isTagsViewCurrenFull } = storeToRefs(storesTagsViewRoutes);
// 设置 footer 显示/隐藏
const isFooter = computed(() => {
return themeConfig.value.isFooter && !route.meta.isIframe;
});
// 设置 header 固定
const isFixedHeader = computed(() => {
return themeConfig.value.isFixedHeader;
});
// 设置主内容区的高度
const setMainHeight = computed(() => {
if (isTagsViewCurrenFull.value) return '0px';
const { isTagsview, layout } = themeConfig.value;
if (isTagsview && layout !== 'classic') return '85px';
else return '51px';
});
// 页面加载前
onMounted(() => {
NextLoading.done(600);
});
return {
layoutMainScrollbarRef,
isFooter,
isFixedHeader,
setMainHeight,
};
},
});
</script>

View File

@ -1,38 +0,0 @@
<!--
* @Author: '490912587@qq.com' '490912587@qq.com'
* @Date: 2022-11-17 09:19:20
* @LastEditors: '490912587@qq.com' '490912587@qq.com'
* @LastEditTime: 2022-11-22 13:43:03
* @FilePath: \frontend\src\layout\footer\index.vue
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
-->
<template>
<div class="layout-footer pb15">
<div class="layout-footer-warp">
<div>TsPlatformMade by 490912587@qq.com with </div>
<div class="mt5">甜蜜蜜版权所有</div>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
name: 'layoutFooter',
});
</script>
<style scoped lang="scss">
.layout-footer {
width: 100%;
display: flex;
&-warp {
margin: auto;
color: var(--el-text-color-secondary);
text-align: center;
animation: error-num 0.3s ease;
}
}
</style>

View File

@ -1,54 +0,0 @@
<template>
<component :is="themeConfig.layout" />
</template>
<script lang="ts">
import { onBeforeMount, onUnmounted, defineComponent, defineAsyncComponent } from 'vue';
import { storeToRefs } from 'pinia';
import { useThemeConfig } from '/@/stores/themeConfig';
import { Local } from '/@/utils/storage';
import mittBus from '/@/utils/mitt';
export default defineComponent({
name: 'layout',
components: {
defaults: defineAsyncComponent(() => import('/@/layout/main/defaults.vue')),
classic: defineAsyncComponent(() => import('/@/layout/main/classic.vue')),
transverse: defineAsyncComponent(() => import('/@/layout/main/transverse.vue')),
columns: defineAsyncComponent(() => import('/@/layout/main/columns.vue')),
},
setup() {
const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig);
// 窗口大小改变时(适配移动端)
const onLayoutResize = () => {
if (!Local.get('oldLayout')) Local.set('oldLayout', themeConfig.value.layout);
const clientWidth = document.body.clientWidth;
if (clientWidth < 1000) {
themeConfig.value.isCollapse = false;
mittBus.emit('layoutMobileResize', {
layout: 'defaults',
clientWidth,
});
} else {
mittBus.emit('layoutMobileResize', {
layout: Local.get('oldLayout') ? Local.get('oldLayout') : themeConfig.value.layout,
clientWidth,
});
}
};
// 页面加载前
onBeforeMount(() => {
onLayoutResize();
window.addEventListener('resize', onLayoutResize);
});
// 页面卸载时
onUnmounted(() => {
window.removeEventListener('resize', onLayoutResize);
});
return {
themeConfig,
};
},
});
</script>

View File

@ -1,77 +0,0 @@
<template>
<el-container class="layout-container flex-center">
<LayoutHeader />
<el-container class="layout-mian-height-50">
<LayoutAside />
<div class="flex-center layout-backtop">
<LayoutTagsView v-if="isTagsview" />
<LayoutMain ref="layoutMainRef" />
</div>
</el-container>
</el-container>
</template>
<script lang="ts">
import { defineAsyncComponent, defineComponent, computed, ref, watch, nextTick, onMounted } from 'vue';
import { useRoute } from 'vue-router';
import { storeToRefs } from 'pinia';
import { useThemeConfig } from '/@/stores/themeConfig';
export default defineComponent({
name: 'layoutClassic',
components: {
LayoutAside: defineAsyncComponent(() => import('/@/layout/component/aside.vue')),
LayoutHeader: defineAsyncComponent(() => import('/@/layout/component/header.vue')),
LayoutMain: defineAsyncComponent(() => import('/@/layout/component/main.vue')),
LayoutTagsView: defineAsyncComponent(() => import('/@/layout/navBars/tagsView/tagsView.vue')),
},
setup() {
const layoutMainRef = ref<any>('');
const route = useRoute();
const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig);
// 判断是否显示 tasgview
const isTagsview = computed(() => {
return themeConfig.value.isTagsview;
});
// 重置滚动条高度,更新子级 scrollbar
const updateScrollbar = () => {
layoutMainRef.value.layoutMainScrollbarRef.update();
};
// 重置滚动条高度,由于组件是异步引入的
const initScrollBarHeight = () => {
nextTick(() => {
setTimeout(() => {
updateScrollbar();
layoutMainRef.value.layoutMainScrollbarRef.wrap$.scrollTop = 0;
}, 500);
});
};
// 监听路由的变化,切换界面时,滚动条置顶
watch(
() => route.path,
() => {
initScrollBarHeight();
}
);
// 监听 themeConfig 配置文件的变化,更新菜单 el-scrollbar 的高度
watch(
themeConfig,
() => {
updateScrollbar();
},
{
deep: true,
}
);
// 页面加载时
onMounted(() => {
initScrollBarHeight();
});
return {
layoutMainRef,
isTagsview,
};
},
});
</script>

View File

@ -1,78 +0,0 @@
<template>
<el-container class="layout-container">
<ColumnsAside />
<el-container class="layout-columns-warp layout-container-view h100">
<LayoutAside />
<el-scrollbar ref="layoutScrollbarRef" class="layout-backtop">
<LayoutHeader />
<LayoutMain ref="layoutMainRef" />
</el-scrollbar>
</el-container>
</el-container>
</template>
<script lang="ts">
import { defineAsyncComponent, watch, defineComponent, onMounted, nextTick, ref } from 'vue';
import { useRoute } from 'vue-router';
import { storeToRefs } from 'pinia';
import { useThemeConfig } from '/@/stores/themeConfig';
export default defineComponent({
name: 'layoutColumns',
components: {
LayoutAside: defineAsyncComponent(() => import('/@/layout/component/aside.vue')),
LayoutHeader: defineAsyncComponent(() => import('/@/layout/component/header.vue')),
LayoutMain: defineAsyncComponent(() => import('/@/layout/component/main.vue')),
ColumnsAside: defineAsyncComponent(() => import('/@/layout/component/columnsAside.vue')),
},
setup() {
const layoutScrollbarRef = ref<any>('');
const layoutMainRef = ref<any>('');
const route = useRoute();
const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig);
// 重置滚动条高度
const updateScrollbar = () => {
// 更新父级 scrollbar
layoutScrollbarRef.value.update();
// 更新子级 scrollbar
layoutMainRef.value.layoutMainScrollbarRef.update();
};
// 重置滚动条高度,由于组件是异步引入的
const initScrollBarHeight = () => {
nextTick(() => {
setTimeout(() => {
updateScrollbar();
layoutScrollbarRef.value.wrap$.scrollTop = 0;
layoutMainRef.value.layoutMainScrollbarRef.wrap$.scrollTop = 0;
}, 500);
});
};
// 监听路由的变化,切换界面时,滚动条置顶
watch(
() => route.path,
() => {
initScrollBarHeight();
}
);
// 监听 themeConfig 配置文件的变化,更新菜单 el-scrollbar 的高度
watch(
themeConfig,
() => {
updateScrollbar();
},
{
deep: true,
}
);
// 页面加载时
onMounted(() => {
initScrollBarHeight();
});
return {
layoutScrollbarRef,
layoutMainRef,
};
},
});
</script>

View File

@ -1,78 +0,0 @@
<template>
<el-container class="layout-container">
<LayoutAside />
<el-container class="layout-container-view h100">
<el-scrollbar ref="layoutScrollbarRef" class="layout-backtop">
<LayoutHeader />
<LayoutMain ref="layoutMainRef" />
</el-scrollbar>
</el-container>
</el-container>
</template>
<script lang="ts">
import { defineAsyncComponent, watch, defineComponent, onMounted, nextTick, ref } from 'vue';
import { useRoute } from 'vue-router';
import { storeToRefs } from 'pinia';
import { useThemeConfig } from '/@/stores/themeConfig';
import { NextLoading } from '/@/utils/loading';
export default defineComponent({
name: 'layoutDefaults',
components: {
LayoutAside: defineAsyncComponent(() => import('/@/layout/component/aside.vue')),
LayoutHeader: defineAsyncComponent(() => import('/@/layout/component/header.vue')),
LayoutMain: defineAsyncComponent(() => import('/@/layout/component/main.vue')),
},
setup() {
const layoutScrollbarRef = ref<any>('');
const layoutMainRef = ref<any>('');
const route = useRoute();
const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig);
// 重置滚动条高度
const updateScrollbar = () => {
// 更新父级 scrollbar
layoutScrollbarRef.value.update();
// 更新子级 scrollbar
layoutMainRef.value.layoutMainScrollbarRef.update();
};
// 重置滚动条高度,由于组件是异步引入的
const initScrollBarHeight = () => {
nextTick(() => {
setTimeout(() => {
updateScrollbar();
layoutScrollbarRef.value.wrap$.scrollTop = 0;
layoutMainRef.value.layoutMainScrollbarRef.wrap$.scrollTop = 0;
}, 500);
});
};
// 监听路由的变化,切换界面时,滚动条置顶
watch(
() => route.path,
() => {
initScrollBarHeight();
}
);
// 监听 themeConfig 配置文件的变化,更新菜单 el-scrollbar 的高度
watch(
themeConfig,
() => {
updateScrollbar();
},
{
deep: true,
}
);
// 页面加载时
onMounted(() => {
initScrollBarHeight();
NextLoading.done(600);
});
return {
layoutScrollbarRef,
layoutMainRef,
};
},
});
</script>

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