├── .gitignore
├── LICENSE
├── README.md
├── docker-compose.yml
├── mongo
├── Dockerfile
├── dump
│ └── vitetest
│ │ ├── Img.bson
│ │ ├── Img.metadata.json
│ │ ├── Menu.bson
│ │ ├── Menu.metadata.json
│ │ ├── Pet.bson
│ │ ├── Pet.metadata.json
│ │ ├── Role.bson
│ │ ├── Role.metadata.json
│ │ ├── User.bson
│ │ └── User.metadata.json
├── export.sh
└── import.sh
├── nginx
└── conf.d
│ └── docker_nginx.conf
├── preview
├── dashboard.png
├── logo.png
└── theme.png
├── server
├── .dockerignore
├── .eslintrc.js
├── .gitignore
├── .prettierrc
├── Dockerfile
├── README.md
├── nest-cli.json
├── package-lock.json
├── package.json
├── public
│ ├── files
│ │ ├── 内网_1688694163233.bat
│ │ ├── 外网_1688694081394.bat
│ │ └── 退费核实平台需完善内容0630_1688694316010.docx
│ ├── imgs
│ │ ├── clearButtonBg_1620282302613.png
│ │ ├── close_1620279997443.png
│ │ ├── defaultPanel_1620278623571.png
│ │ ├── defaultPanel_1620282310246.png
│ │ ├── dialogTitle_1620282152240.png
│ │ ├── ico-clear_1620282187944.png
│ │ ├── imgAdd_1620280226115.png
│ │ ├── loading_1620280035600.gif
│ │ ├── logout_1620280008925.png
│ │ ├── modifyPassword_1620441325045.png
│ │ ├── next_1620282322191.png
│ │ ├── openPanel_1620280058549.png
│ │ ├── prev_1620282558482.png
│ │ ├── stretchRight_1620280171414.jpg
│ │ ├── tip2_1620280048557.png
│ │ ├── tip_1620280019728.png
│ │ └── top_1620280078350.jpg
│ └── index.html
├── src
│ ├── app.controller.spec.ts
│ ├── app.controller.ts
│ ├── app.module.ts
│ ├── app.service.ts
│ ├── config.ts
│ ├── helper
│ │ └── base.controller.ts
│ ├── main.ts
│ ├── modules
│ │ ├── file
│ │ │ ├── file.controller.ts
│ │ │ ├── file.interface.ts
│ │ │ ├── file.model.ts
│ │ │ ├── file.module.ts
│ │ │ └── file.service.ts
│ │ ├── img
│ │ │ ├── img.controller.ts
│ │ │ ├── img.interface.ts
│ │ │ ├── img.model.ts
│ │ │ ├── img.module.ts
│ │ │ └── img.service.ts
│ │ ├── pet
│ │ │ ├── pet.controller.ts
│ │ │ ├── pet.interface.ts
│ │ │ ├── pet.model.ts
│ │ │ ├── pet.module.ts
│ │ │ └── pet.service.ts
│ │ └── sys
│ │ │ ├── auth
│ │ │ ├── auth module.ts
│ │ │ ├── auth module.ts1
│ │ │ ├── auth.controller.ts
│ │ │ ├── auth.service.ts
│ │ │ └── passport
│ │ │ │ ├── http.strategy.ts
│ │ │ │ └── jwt.strategy.ts
│ │ │ ├── menu
│ │ │ ├── menu.controller.ts
│ │ │ ├── menu.interface.ts
│ │ │ ├── menu.model.ts
│ │ │ ├── menu.module.ts
│ │ │ └── menu.service.ts
│ │ │ ├── role
│ │ │ ├── role.controller.ts
│ │ │ ├── role.interface.ts
│ │ │ ├── role.model.ts
│ │ │ ├── role.module.ts
│ │ │ └── role.service.ts
│ │ │ └── user
│ │ │ ├── user.controller.ts
│ │ │ ├── user.interface.ts
│ │ │ ├── user.model.ts
│ │ │ ├── user.module.ts
│ │ │ └── user.service.ts
│ └── util
│ │ └── util.ts
├── test
│ ├── app.e2e-spec.ts
│ └── jest-e2e.json
├── tsconfig.build.json
└── tsconfig.json
├── web
├── .env.dev
├── .env.pro
├── .gitignore
├── .vscode
│ └── extensions.json
├── README.md
├── env.d.ts
├── index.html
├── mock
│ ├── _createProductionServer.ts
│ └── user
│ │ └── index.ts
├── package-lock.json
├── package.json
├── pnpm-lock.yaml
├── public
│ ├── favicon.ico
│ └── logo.png
├── src
│ ├── App.vue
│ ├── api
│ │ └── system
│ │ │ ├── menu.ts
│ │ │ ├── role.ts
│ │ │ └── user.ts
│ ├── assets
│ │ ├── imgs
│ │ │ ├── avatar.jpg
│ │ │ └── logo.png
│ │ └── svgs
│ │ │ ├── 403.svg
│ │ │ ├── 404.svg
│ │ │ ├── 500.svg
│ │ │ ├── add.svg
│ │ │ ├── delete.svg
│ │ │ ├── edit.svg
│ │ │ ├── exit-fullscreen.svg
│ │ │ ├── expend.svg
│ │ │ ├── full-screen.svg
│ │ │ ├── icon.svg
│ │ │ ├── lang-select.svg
│ │ │ ├── login-bg.svg
│ │ │ ├── login-box-bg.svg
│ │ │ ├── message.svg
│ │ │ ├── money.svg
│ │ │ ├── nav-left.svg
│ │ │ ├── nav-top.svg
│ │ │ ├── password-edit.svg
│ │ │ ├── peoples.svg
│ │ │ ├── put-away2.svg
│ │ │ ├── right.svg
│ │ │ ├── role-select.svg
│ │ │ ├── search1.svg
│ │ │ ├── shopping.svg
│ │ │ ├── theme-center.svg
│ │ │ ├── triangle-next.svg
│ │ │ ├── triangle-prev.svg
│ │ │ └── unfold.svg
│ ├── common
│ │ └── index.ts
│ ├── components
│ │ ├── Breadcrumb
│ │ │ └── index.vue
│ │ ├── Dialog
│ │ │ └── index.vue
│ │ ├── Echarts
│ │ │ ├── getEcharts.ts
│ │ │ └── index.vue
│ │ ├── Editor
│ │ │ └── index.vue
│ │ ├── Form
│ │ │ ├── componentMap.ts
│ │ │ ├── helper.ts
│ │ │ ├── index.vue
│ │ │ ├── useForm.ts
│ │ │ ├── useRenderCheckbox.tsx
│ │ │ ├── useRenderRadio.tsx
│ │ │ └── useRenderSelect.tsx
│ │ ├── Hamburger
│ │ │ └── index.vue
│ │ ├── HeaderSearch
│ │ │ ├── fuse.js
│ │ │ └── index.vue
│ │ ├── ScreenFull
│ │ │ └── index.vue
│ │ ├── SearchFields
│ │ │ └── index.vue
│ │ ├── SvgIcon
│ │ │ ├── SvgIcon.vue
│ │ │ └── index.ts
│ │ ├── Table
│ │ │ ├── helper.ts
│ │ │ └── index.vue
│ │ ├── Upload
│ │ │ └── index.vue
│ │ ├── UserAvatar
│ │ │ └── index.vue
│ │ └── __tests__
│ │ │ └── HelloWorld.spec.ts
│ ├── config.ts
│ ├── directives
│ │ ├── index.ts
│ │ └── waves
│ │ │ ├── index.ts
│ │ │ ├── waves.css
│ │ │ └── waves.ts
│ ├── i18n
│ │ ├── index.ts
│ │ └── langs
│ │ │ └── zh-CN.ts
│ ├── layout
│ │ ├── Dynamic.vue
│ │ ├── ExternalLink.vue
│ │ ├── components
│ │ │ ├── AppMain.vue
│ │ │ ├── Navbar.vue
│ │ │ ├── Settings
│ │ │ │ ├── LayoutSetting.vue
│ │ │ │ ├── MainSetting.vue
│ │ │ │ ├── ModifyPasswordDialog.vue
│ │ │ │ └── index.vue
│ │ │ ├── rightMenu
│ │ │ │ └── index.vue
│ │ │ ├── sidebar
│ │ │ │ ├── AppLink.vue
│ │ │ │ ├── Item.vue
│ │ │ │ ├── Logo.vue
│ │ │ │ ├── SidebarItem.vue
│ │ │ │ └── index.vue
│ │ │ ├── sidebarH
│ │ │ │ ├── AppLink.vue
│ │ │ │ ├── Item.vue
│ │ │ │ ├── Logo.vue
│ │ │ │ ├── SidebarItem.vue
│ │ │ │ └── index.vue
│ │ │ └── tagsView
│ │ │ │ ├── ScrollPane.vue
│ │ │ │ └── index.vue
│ │ └── index.vue
│ ├── main.ts
│ ├── permission.ts
│ ├── plugins
│ │ ├── element.ts
│ │ └── nProgress.ts
│ ├── router
│ │ └── index.ts
│ ├── store
│ │ ├── index.ts
│ │ └── modules
│ │ │ ├── app.ts
│ │ │ ├── app1.ts
│ │ │ ├── counter.ts
│ │ │ ├── permission.ts
│ │ │ ├── tagsView.ts
│ │ │ └── user.ts
│ ├── styles
│ │ ├── elementHack.scss
│ │ ├── global.scss
│ │ ├── main.scss
│ │ ├── sidebar.scss
│ │ ├── tagsView.scss
│ │ ├── transition.scss
│ │ └── var.css
│ ├── utils
│ │ ├── auth.ts
│ │ ├── deepClone.ts
│ │ ├── fullScreen.ts
│ │ ├── index.ts
│ │ ├── is.ts
│ │ ├── request.ts
│ │ ├── resize.ts
│ │ ├── slot.ts
│ │ ├── storage.ts
│ │ ├── themeSet.ts
│ │ ├── title.ts
│ │ └── validator.ts
│ └── views
│ │ ├── ZjView.vue
│ │ ├── dashboard
│ │ ├── echartOptions.ts
│ │ └── index.vue
│ │ ├── form
│ │ └── demo.vue
│ │ ├── icons
│ │ ├── element-icons.ts
│ │ ├── index.vue
│ │ └── svg-icons.ts
│ │ ├── login
│ │ ├── 404.vue
│ │ ├── NoRoutes.vue
│ │ ├── components
│ │ │ ├── LoginForm.vue
│ │ │ └── index.ts
│ │ └── index.vue
│ │ ├── product
│ │ ├── components
│ │ │ └── AddDialog.vue
│ │ └── list.vue
│ │ ├── redirect
│ │ └── index.vue
│ │ └── system
│ │ ├── menu
│ │ ├── components
│ │ │ └── IconDialog.vue
│ │ └── index.vue
│ │ ├── role
│ │ ├── components
│ │ │ ├── AddDialog.vue
│ │ │ └── PermissionDialog.vue
│ │ └── index.vue
│ │ └── user
│ │ ├── components
│ │ └── AddDialog.vue
│ │ └── index.vue
├── tsconfig.json
├── types
│ ├── app.d.ts
│ ├── form.d.ts
│ ├── global.d.ts
│ ├── grid.d.ts
│ ├── router.d.ts
│ ├── searchField.d.ts
│ ├── table.d.ts
│ └── vue.d.ts
└── vite.config.ts
└── 备注.txt
/.gitignore:
--------------------------------------------------------------------------------
1 | # compiled output
2 | /dist
3 | /node_modules
4 |
5 | # Logs
6 | logs
7 | *.log
8 | npm-debug.log*
9 | yarn-debug.log*
10 | yarn-error.log*
11 | lerna-debug.log*
12 |
13 | # OS
14 | .DS_Store
15 |
16 | # Tests
17 | /coverage
18 | /.nyc_output
19 |
20 | # IDEs and editors
21 | /.idea
22 | .project
23 | .classpath
24 | .c9/
25 | *.launch
26 | .settings/
27 | *.sublime-workspace
28 |
29 | # IDE - VSCode
30 | .vscode/*
31 | !.vscode/settings.json
32 | !.vscode/tasks.json
33 | !.vscode/launch.json
34 | !.vscode/extensions.json
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 bigjbigj
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 | 
3 |
4 | # 说明:
5 | - 全栈web开发解决方案,已集成docker并搭配代码生成脚手架工具,方便开发人员快速应用部署,高效开发
6 | - 框架已提供完备的UI交互,以及用户、角色、菜单等基础功能,小而精,无冗余,开箱即用
7 | - 具体配置及应用请移步[帮助文档](https://bigjbigj.gitee.io/zj-admin-preview/)~
8 |
9 | ## 技术栈
10 | - 前端: vue3+ts+vite+elementplus
11 | - 后端: nestjs+mongoose
12 | - 数据库: mongodb
13 | - node版本: 14及以上
14 |
15 | # 安装:
16 | - 可直接在docker中部署
17 | ```javascript
18 | docker-compose up
19 | ```
20 | - 单独运行前后端代码(进入server或web文件夹中)
21 | ```javascript
22 | npm install
23 | npm run dev
24 | ```
25 | ps:由于无mock都是真接口,需要导入基础数据。docker需要在容器中自行导入,自行配置的mongodb可执行mongo\import.sh导入
26 |
27 | # 脚手架使用
28 | ```javascript
29 | npm install zhangjincli -g
30 | ```
31 |
32 | ## 指令说明
33 | - zj --help 查看所有指令
34 | - zj init templates初始化模板
35 | - zj init models初始化测试数据
36 | - zj config 参考npm的指令增改查脚手架的配置文件
37 | - zj show logs 查看log
38 | - zj compile ./models 编译指定目录,只编译前端vue文件
39 | - zj compilefb ./models 编译指定目录,前后端都编译
40 | - zj compile ./models -v3 指令中添加-v3,则输出为vue3版本
41 |
42 | ### PS
43 | - 前端vue+element,后端nestjs
44 | - .json为前端数据模型,_b.json为后端数据模型
45 | - 前端编译vue版本通过设置-v2或--vue2指定为vue2版本,-v3或--vue3指定为vue3版本
46 | - 数据模型只支持json格式,.json文件为前端模型,_b.json为后端数据模型,zj init models可以生成测试数据
47 | - 支持递归编译
48 | - 生成的后端代码,直接放到server\src\modules目录中,需要手动在app.module.ts中引入模块,需要配置swagger则自行在main.ts中添加
49 | - 生成的前端代码,直接放到web\src\views目录中,在运行起来的项目页面中,系统管理->菜单管理中添加相应路由即可,别忘了去角色管理配置一下菜单权限
50 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '2.0'
2 | services:
3 | mongo:
4 | image: mongo
5 | # container_name: zj-server
6 | # build: ./mongo
7 | restart: always
8 | environment:
9 | - MONGO_DATA_DIR=/data/db
10 | - MONGO_LOG_DIR=/data/logs
11 | volumes:
12 | - ./mongo/dump:/data/db
13 | # volumes:
14 | # - ./mongo:/usr/mongo
15 | ports:
16 | - 27017:27017
17 | # command:
18 | # /usr/mongo/import.sh
19 | mongo-express:
20 | image: mongo-express
21 | restart: always
22 | ports:
23 | - 8081:8081
24 | nginx:
25 | restart: always
26 | image: nginx
27 | ports:
28 | - 8092:80
29 | volumes:
30 | - ./nginx/conf.d/:/etc/nginx/conf.d
31 | - ./web/dist:/var/www/html/
32 | nestjs:
33 | container_name: zj-server
34 | #构建容器
35 | build: ./server
36 | #直接从git拉去
37 | # build: git@github.com:su37josephxia/docker_ci.git#:backend
38 | # 需要链接本地代码时
39 | # volumes:
40 | # - ./server/dist:/usr/src/app/dist
41 | # - ./server:/usr/src/app
42 | ports:
43 | - 3000:3000
--------------------------------------------------------------------------------
/mongo/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM mongo
2 | WORKDIR /usr/mongo
3 | ADD . /usr/mongo
4 | EXPOSE 27017
5 | # RUN mongorestore -d zj-server ./dump/zj-server
6 | # 在docker里一直报错,docker exec -it 027 /bin/bash进到伪终端去还原数据,不需要-h了
7 | RUN mongorestore -h mongo:27017 -d zj-server ./dump/zj-server
8 |
--------------------------------------------------------------------------------
/mongo/dump/vitetest/Img.bson:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhangjinzhangjin/zj-admin/a8dd261f416b778221acccb25d5633fde85d3f89/mongo/dump/vitetest/Img.bson
--------------------------------------------------------------------------------
/mongo/dump/vitetest/Img.metadata.json:
--------------------------------------------------------------------------------
1 | {"options":{},"indexes":[{"v":{"$numberInt":"2"},"key":{"_id":{"$numberInt":"1"}},"name":"_id_","ns":"vitetest.Img"},{"v":{"$numberInt":"2"},"unique":true,"key":{"imgname":{"$numberInt":"1"}},"name":"imgname_1","ns":"vitetest.Img","background":true}],"uuid":"d44628500b80402f8488382a5440c80f"}
--------------------------------------------------------------------------------
/mongo/dump/vitetest/Menu.bson:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhangjinzhangjin/zj-admin/a8dd261f416b778221acccb25d5633fde85d3f89/mongo/dump/vitetest/Menu.bson
--------------------------------------------------------------------------------
/mongo/dump/vitetest/Menu.metadata.json:
--------------------------------------------------------------------------------
1 | {"options":{},"indexes":[{"v":{"$numberInt":"2"},"key":{"_id":{"$numberInt":"1"}},"name":"_id_","ns":"vitetest.Menu"},{"v":{"$numberInt":"2"},"unique":true,"key":{"menuKey":{"$numberInt":"1"}},"name":"menuKey_1","ns":"vitetest.Menu","background":true}],"uuid":"772358fcfb6348d0b43fca4a172d66b0"}
--------------------------------------------------------------------------------
/mongo/dump/vitetest/Pet.bson:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhangjinzhangjin/zj-admin/a8dd261f416b778221acccb25d5633fde85d3f89/mongo/dump/vitetest/Pet.bson
--------------------------------------------------------------------------------
/mongo/dump/vitetest/Pet.metadata.json:
--------------------------------------------------------------------------------
1 | {"options":{},"indexes":[{"v":{"$numberInt":"2"},"key":{"_id":{"$numberInt":"1"}},"name":"_id_","ns":"vitetest.Pet"},{"v":{"$numberInt":"2"},"unique":true,"key":{"code":{"$numberInt":"1"}},"name":"code_1","ns":"vitetest.Pet","background":true}],"uuid":"6226cd5f4c4d4b9ca788efeed52ef918"}
--------------------------------------------------------------------------------
/mongo/dump/vitetest/Role.bson:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhangjinzhangjin/zj-admin/a8dd261f416b778221acccb25d5633fde85d3f89/mongo/dump/vitetest/Role.bson
--------------------------------------------------------------------------------
/mongo/dump/vitetest/Role.metadata.json:
--------------------------------------------------------------------------------
1 | {"options":{},"indexes":[{"v":{"$numberInt":"2"},"key":{"_id":{"$numberInt":"1"}},"name":"_id_","ns":"vitetest.Role"},{"v":{"$numberInt":"2"},"unique":true,"key":{"code":{"$numberInt":"1"}},"name":"code_1","ns":"vitetest.Role","background":true}],"uuid":"e6f7f74ae9044cf287dfcc321eb0f697"}
--------------------------------------------------------------------------------
/mongo/dump/vitetest/User.bson:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhangjinzhangjin/zj-admin/a8dd261f416b778221acccb25d5633fde85d3f89/mongo/dump/vitetest/User.bson
--------------------------------------------------------------------------------
/mongo/dump/vitetest/User.metadata.json:
--------------------------------------------------------------------------------
1 | {"options":{},"indexes":[{"v":{"$numberInt":"2"},"key":{"_id":{"$numberInt":"1"}},"name":"_id_","ns":"vitetest.User"},{"v":{"$numberInt":"2"},"unique":true,"key":{"username":{"$numberInt":"1"}},"name":"username_1","ns":"vitetest.User","background":true}],"uuid":"1b7eada3a25348c29de0fc530cdf9982"}
--------------------------------------------------------------------------------
/mongo/export.sh:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 | mongodump -d vitetest -o ./dump
3 | # mongodump -d nestjs -o e:/data/dump
--------------------------------------------------------------------------------
/mongo/import.sh:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 | mongorestore -d vitetest ./dump/vitetest
3 | # mongorestore -h mongo:27017 -d zj-server ./dump/zj-server
--------------------------------------------------------------------------------
/nginx/conf.d/docker_nginx.conf:
--------------------------------------------------------------------------------
1 | server {
2 | listen 80;
3 | # server_name www.josephxia.com;
4 | location / {
5 | root /var/www/html;
6 | index index.html index.htm;
7 | try_files $uri $uri/ /index.html$args;
8 | }
9 |
10 | location ~ \.(gif|jpg|png)$ {
11 | root /static;
12 | index index.html index.htm;
13 | }
14 |
15 |
16 | # location /api {
17 | # proxy_pass http://app-pm2:3000;
18 | # proxy_redirect off;
19 | # proxy_set_header Host $host;
20 | # proxy_set_header X-Real-IP $remote_addr;
21 | # proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
22 | # }
23 |
24 |
25 |
26 | # location = / {
27 | # rewrite ^(.*) https://www.josephxia.com/$1 permanent;
28 | # }
29 | }
30 | # server {
31 | # listen 443;
32 | # server_name localhost;
33 | # ssl on;
34 | # root html;
35 | # index index.html index.htm;
36 | # ssl_certificate conf.d/cert/www.josephxia.com.pem;
37 | # ssl_certificate_key conf.d/cert/www.josephxia.com.key;
38 | # ssl_session_timeout 5m;
39 | # ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
40 | # ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
41 | # ssl_prefer_server_ciphers on;
42 | # location / {
43 | # root /var/www/html;
44 | # index index.html index.htm;
45 | # }
46 | # }
--------------------------------------------------------------------------------
/preview/dashboard.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhangjinzhangjin/zj-admin/a8dd261f416b778221acccb25d5633fde85d3f89/preview/dashboard.png
--------------------------------------------------------------------------------
/preview/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhangjinzhangjin/zj-admin/a8dd261f416b778221acccb25d5633fde85d3f89/preview/logo.png
--------------------------------------------------------------------------------
/preview/theme.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhangjinzhangjin/zj-admin/a8dd261f416b778221acccb25d5633fde85d3f89/preview/theme.png
--------------------------------------------------------------------------------
/server/.dockerignore:
--------------------------------------------------------------------------------
1 | node_modules
--------------------------------------------------------------------------------
/server/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | parser: '@typescript-eslint/parser',
3 | parserOptions: {
4 | project: 'tsconfig.json',
5 | sourceType: 'module',
6 | },
7 | plugins: ['@typescript-eslint/eslint-plugin'],
8 | extends: [
9 | 'plugin:@typescript-eslint/recommended',
10 | 'prettier/@typescript-eslint',
11 | 'plugin:prettier/recommended',
12 | ],
13 | root: true,
14 | env: {
15 | node: true,
16 | jest: true,
17 | },
18 | ignorePatterns: ['.eslintrc.js'],
19 | rules: {
20 | '@typescript-eslint/interface-name-prefix': 'off',
21 | '@typescript-eslint/explicit-function-return-type': 'off',
22 | '@typescript-eslint/explicit-module-boundary-types': 'off',
23 | '@typescript-eslint/no-explicit-any': 'off',
24 | },
25 | };
26 |
--------------------------------------------------------------------------------
/server/.gitignore:
--------------------------------------------------------------------------------
1 | # compiled output
2 | /dist
3 | /node_modules
4 |
5 | # Logs
6 | logs
7 | *.log
8 | npm-debug.log*
9 | yarn-debug.log*
10 | yarn-error.log*
11 | lerna-debug.log*
12 |
13 | # OS
14 | .DS_Store
15 |
16 | # Tests
17 | /coverage
18 | /.nyc_output
19 |
20 | # IDEs and editors
21 | /.idea
22 | .project
23 | .classpath
24 | .c9/
25 | *.launch
26 | .settings/
27 | *.sublime-workspace
28 |
29 | # IDE - VSCode
30 | .vscode/*
31 | !.vscode/settings.json
32 | !.vscode/tasks.json
33 | !.vscode/launch.json
34 | !.vscode/extensions.json
--------------------------------------------------------------------------------
/server/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "trailingComma": "all"
4 | }
--------------------------------------------------------------------------------
/server/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:14 AS development
2 | WORKDIR /usr/src/app
3 | ADD . /usr/src/app
4 | RUN npm config set registry https://registry.npm.taobao.org/
5 | RUN npm install --only=dev
6 | RUN npm install -g @nestjs/cli
7 | RUN npm install
8 | RUN npm install @types/estree@latest
9 | EXPOSE 3000
10 | #pm2在docker中使用命令为pm2-docker
11 | # CMD ["pm2-runtime", "start", "--json", "process.json"]
12 | # CMD ["pm2-runtime", "start", "process.yml"]
13 | CMD ["npm", "run", "dev"]
14 | # CMD ["npm", "start"]
--------------------------------------------------------------------------------
/server/nest-cli.json:
--------------------------------------------------------------------------------
1 | {
2 | "collection": "@nestjs/schematics",
3 | "sourceRoot": "src"
4 | }
5 |
--------------------------------------------------------------------------------
/server/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nesttest",
3 | "version": "0.0.1",
4 | "description": "",
5 | "author": "",
6 | "private": true,
7 | "license": "UNLICENSED",
8 | "scripts": {
9 | "prebuild": "rimraf dist",
10 | "build": "nest build",
11 | "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
12 | "start": "nest start",
13 | "dev": "nest start --watch",
14 | "start:dev": "nest start --watch",
15 | "start:debug": "nest start --debug --watch",
16 | "start:prod": "node dist/main",
17 | "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
18 | "test": "jest",
19 | "test:watch": "jest --watch",
20 | "test:cov": "jest --coverage",
21 | "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
22 | "test:e2e": "jest --config ./test/jest-e2e.json"
23 | },
24 | "dependencies": {
25 | "@nestjs/common": "^7.5.1",
26 | "@nestjs/core": "^7.5.1",
27 | "@nestjs/jwt": "^7.2.0",
28 | "@nestjs/mongoose": "^7.2.4",
29 | "@nestjs/passport": "^7.1.5",
30 | "@nestjs/platform-express": "^7.5.1",
31 | "@nestjs/swagger": "^4.8.0",
32 | "@types/passport-jwt": "^3.0.5",
33 | "md5": "^2.3.0",
34 | "moment": "^2.29.1",
35 | "mongoose": "^5.12.4",
36 | "passport": "^0.4.1",
37 | "passport-http-bearer": "^1.0.1",
38 | "passport-jwt": "^4.0.0",
39 | "reflect-metadata": "^0.1.13",
40 | "rimraf": "^3.0.2",
41 | "rxjs": "^6.6.3",
42 | "swagger-ui-express": "^4.1.6"
43 | },
44 | "devDependencies": {
45 | "@nestjs/cli": "^7.5.1",
46 | "@nestjs/schematics": "^7.1.3",
47 | "@nestjs/testing": "^7.5.1",
48 | "@types/express": "^4.17.8",
49 | "@types/jest": "^26.0.15",
50 | "@types/node": "^14.14.6",
51 | "@types/supertest": "^2.0.10",
52 | "@typescript-eslint/eslint-plugin": "^4.6.1",
53 | "@typescript-eslint/parser": "^4.6.1",
54 | "eslint": "^7.12.1",
55 | "eslint-config-prettier": "7.1.0",
56 | "eslint-plugin-prettier": "^3.1.4",
57 | "jest": "^26.6.3",
58 | "prettier": "^2.1.2",
59 | "supertest": "^6.0.0",
60 | "ts-jest": "^26.4.3",
61 | "ts-loader": "^8.0.8",
62 | "ts-node": "^9.0.0",
63 | "tsconfig-paths": "^3.9.0",
64 | "typescript": "^4.0.5"
65 | },
66 | "jest": {
67 | "moduleFileExtensions": [
68 | "js",
69 | "json",
70 | "ts"
71 | ],
72 | "rootDir": "src",
73 | "testRegex": ".*\\.spec\\.ts$",
74 | "transform": {
75 | "^.+\\.(t|j)s$": "ts-jest"
76 | },
77 | "collectCoverageFrom": [
78 | "**/*.(t|j)s"
79 | ],
80 | "coverageDirectory": "../coverage",
81 | "testEnvironment": "node"
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/server/public/files/内网_1688694163233.bat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhangjinzhangjin/zj-admin/a8dd261f416b778221acccb25d5633fde85d3f89/server/public/files/内网_1688694163233.bat
--------------------------------------------------------------------------------
/server/public/files/外网_1688694081394.bat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhangjinzhangjin/zj-admin/a8dd261f416b778221acccb25d5633fde85d3f89/server/public/files/外网_1688694081394.bat
--------------------------------------------------------------------------------
/server/public/files/退费核实平台需完善内容0630_1688694316010.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhangjinzhangjin/zj-admin/a8dd261f416b778221acccb25d5633fde85d3f89/server/public/files/退费核实平台需完善内容0630_1688694316010.docx
--------------------------------------------------------------------------------
/server/public/imgs/clearButtonBg_1620282302613.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhangjinzhangjin/zj-admin/a8dd261f416b778221acccb25d5633fde85d3f89/server/public/imgs/clearButtonBg_1620282302613.png
--------------------------------------------------------------------------------
/server/public/imgs/close_1620279997443.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhangjinzhangjin/zj-admin/a8dd261f416b778221acccb25d5633fde85d3f89/server/public/imgs/close_1620279997443.png
--------------------------------------------------------------------------------
/server/public/imgs/defaultPanel_1620278623571.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhangjinzhangjin/zj-admin/a8dd261f416b778221acccb25d5633fde85d3f89/server/public/imgs/defaultPanel_1620278623571.png
--------------------------------------------------------------------------------
/server/public/imgs/defaultPanel_1620282310246.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhangjinzhangjin/zj-admin/a8dd261f416b778221acccb25d5633fde85d3f89/server/public/imgs/defaultPanel_1620282310246.png
--------------------------------------------------------------------------------
/server/public/imgs/dialogTitle_1620282152240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhangjinzhangjin/zj-admin/a8dd261f416b778221acccb25d5633fde85d3f89/server/public/imgs/dialogTitle_1620282152240.png
--------------------------------------------------------------------------------
/server/public/imgs/ico-clear_1620282187944.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhangjinzhangjin/zj-admin/a8dd261f416b778221acccb25d5633fde85d3f89/server/public/imgs/ico-clear_1620282187944.png
--------------------------------------------------------------------------------
/server/public/imgs/imgAdd_1620280226115.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhangjinzhangjin/zj-admin/a8dd261f416b778221acccb25d5633fde85d3f89/server/public/imgs/imgAdd_1620280226115.png
--------------------------------------------------------------------------------
/server/public/imgs/loading_1620280035600.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhangjinzhangjin/zj-admin/a8dd261f416b778221acccb25d5633fde85d3f89/server/public/imgs/loading_1620280035600.gif
--------------------------------------------------------------------------------
/server/public/imgs/logout_1620280008925.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhangjinzhangjin/zj-admin/a8dd261f416b778221acccb25d5633fde85d3f89/server/public/imgs/logout_1620280008925.png
--------------------------------------------------------------------------------
/server/public/imgs/modifyPassword_1620441325045.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhangjinzhangjin/zj-admin/a8dd261f416b778221acccb25d5633fde85d3f89/server/public/imgs/modifyPassword_1620441325045.png
--------------------------------------------------------------------------------
/server/public/imgs/next_1620282322191.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhangjinzhangjin/zj-admin/a8dd261f416b778221acccb25d5633fde85d3f89/server/public/imgs/next_1620282322191.png
--------------------------------------------------------------------------------
/server/public/imgs/openPanel_1620280058549.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhangjinzhangjin/zj-admin/a8dd261f416b778221acccb25d5633fde85d3f89/server/public/imgs/openPanel_1620280058549.png
--------------------------------------------------------------------------------
/server/public/imgs/prev_1620282558482.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhangjinzhangjin/zj-admin/a8dd261f416b778221acccb25d5633fde85d3f89/server/public/imgs/prev_1620282558482.png
--------------------------------------------------------------------------------
/server/public/imgs/stretchRight_1620280171414.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhangjinzhangjin/zj-admin/a8dd261f416b778221acccb25d5633fde85d3f89/server/public/imgs/stretchRight_1620280171414.jpg
--------------------------------------------------------------------------------
/server/public/imgs/tip2_1620280048557.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhangjinzhangjin/zj-admin/a8dd261f416b778221acccb25d5633fde85d3f89/server/public/imgs/tip2_1620280048557.png
--------------------------------------------------------------------------------
/server/public/imgs/tip_1620280019728.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhangjinzhangjin/zj-admin/a8dd261f416b778221acccb25d5633fde85d3f89/server/public/imgs/tip_1620280019728.png
--------------------------------------------------------------------------------
/server/public/imgs/top_1620280078350.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhangjinzhangjin/zj-admin/a8dd261f416b778221acccb25d5633fde85d3f89/server/public/imgs/top_1620280078350.jpg
--------------------------------------------------------------------------------
/server/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Document
8 |
9 |
10 | zj test
11 |
12 |
--------------------------------------------------------------------------------
/server/src/app.controller.spec.ts:
--------------------------------------------------------------------------------
1 | import { Test, TestingModule } from '@nestjs/testing';
2 | import { AppController } from './app.controller';
3 | import { AppService } from './app.service';
4 |
5 | describe('AppController', () => {
6 | let appController: AppController;
7 |
8 | beforeEach(async () => {
9 | const app: TestingModule = await Test.createTestingModule({
10 | controllers: [AppController],
11 | providers: [AppService],
12 | }).compile();
13 |
14 | appController = app.get(AppController);
15 | });
16 |
17 | describe('root', () => {
18 | it('should return "Hello World!"', () => {
19 | expect(appController.getHello()).toBe('Hello World!');
20 | });
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/server/src/app.controller.ts:
--------------------------------------------------------------------------------
1 | // import { Controller, Get } from '@nestjs/common';
2 | // import { AppService } from './app.service';
3 |
4 | // @Controller()
5 | // export class AppController {
6 | // constructor(private readonly appService: AppService) {}
7 | // @Get()
8 | // getHello(): string {
9 | // return this.appService.getHello();
10 | // }
11 | // @Get("/aaa")
12 | // aaa(): string {
13 | // return "aaa";
14 | // }
15 | // }
16 | import { UserController } from "./modules/sys/user/user.controller";
17 | // import { RoleController } from "./controllers/role.controller";
18 | // export default [UserController, RoleController];
19 | export default [UserController];
20 |
--------------------------------------------------------------------------------
/server/src/app.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from "@nestjs/common";
2 | import { RoleModule } from "./modules/sys/role/role.module";
3 | import { UserModule } from "./modules/sys/user/user.module";
4 | import { AuthModule } from "./modules/sys/auth/auth module";
5 | import { MenuModule } from "./modules/sys/menu/menu.module"
6 | import { ImgModule } from "./modules/img/img.module"
7 | import { PetModule } from "./modules/pet/pet.module"
8 | import { FileModule } from "./modules/file/file.module"
9 | import { MongooseModule } from "@nestjs/mongoose";
10 | // import { AppController } from "./app.controller";
11 | // import { AppService } from "./app.service";
12 | import config from "./config"
13 | @Module({
14 | imports: [
15 | AuthModule,
16 | RoleModule,
17 | UserModule,
18 | MenuModule,
19 | ImgModule,
20 | PetModule,
21 | FileModule,
22 | MongooseModule.forRoot(config.mongoUrl, {useNewUrlParser: true, useCreateIndex: true, useFindAndModify: false, poolSize: 5}),
23 | ], // 这里可以直接导入module
24 | // controllers: [AppController],
25 | // providers: [AppService],
26 | })
27 | export class AppModule {}
28 |
--------------------------------------------------------------------------------
/server/src/app.service.ts:
--------------------------------------------------------------------------------
1 | // import { Injectable } from '@nestjs/common';
2 |
3 | // @Injectable()
4 | // export class AppService {
5 | // getHello(): string {
6 | // return 'Hello World!';
7 | // }
8 | // }
9 | import { UserService } from "./modules/sys/user/user.service";
10 | // import { RoleService } from "./services/role.service";
11 | // export default [UserService, RoleService];
12 | export default [UserService];
--------------------------------------------------------------------------------
/server/src/config.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | HasSalt: ':zj@666!', // 密码盐
3 | mongoUrl: 'mongodb://localhost:27017/vitetest',
4 | };
5 |
--------------------------------------------------------------------------------
/server/src/helper/base.controller.ts:
--------------------------------------------------------------------------------
1 | export default class BaseController {
2 | success(response, data) {
3 | response.send({
4 | code: 200,
5 | data,
6 | });
7 | }
8 | message(response, message) {
9 | response.send({
10 | code: 200,
11 | message,
12 | });
13 | }
14 | error(response, message, code = -1, errors = {}) {
15 | response.send({
16 | code,
17 | message,
18 | errors,
19 | });
20 | }
21 | }
--------------------------------------------------------------------------------
/server/src/main.ts:
--------------------------------------------------------------------------------
1 | import { NestFactory } from "@nestjs/core";
2 | import { AppModule } from "./app.module";
3 | import { NestExpressApplication } from "@nestjs/platform-express";
4 | import { DocumentBuilder, SwaggerModule } from "@nestjs/swagger";
5 | import { UserModule } from './modules/sys/user/user.module';
6 | import { RoleModule } from './modules/sys/role/role.module';
7 | import { AuthModule } from './modules/sys/auth/auth module'
8 | import { PetModule } from './modules/pet/pet.module'
9 | const path = require("path");
10 | async function bootstrap() {
11 | // const app = await NestFactory.create(AppModule);
12 | const app = await NestFactory.create(AppModule);
13 | // app.useStaticAssets("public");
14 | app.useStaticAssets(path.join(__dirname, "..", "public"), {
15 | prefix: "/static/", //设置虚拟路径
16 | });
17 | // 配置swagger
18 | const authApiOptions = new DocumentBuilder()
19 | .setTitle("Auth API Doc")
20 | .setDescription("Auth API Info")
21 | .setVersion("1.0")
22 | .addBearerAuth()
23 | .addTag("auth") // match tags in controllers
24 | .build();
25 | const AuthApiDocument = SwaggerModule.createDocument(app, authApiOptions, {
26 | include: [AuthModule],
27 | });
28 | SwaggerModule.setup("swagger/auth", app, AuthApiDocument);
29 | const userApiOptions = new DocumentBuilder()
30 | .setTitle("User API Doc")
31 | .setDescription("User API Info")
32 | .setVersion("1.0")
33 | .addBearerAuth()
34 | .addTag("/user") // match tags in controllers
35 | .build();
36 | const userApiDocument = SwaggerModule.createDocument(app, userApiOptions, {
37 | include: [UserModule],
38 | });
39 | SwaggerModule.setup("swagger/user", app, userApiDocument);
40 | const roleApiOptions = new DocumentBuilder()
41 | .setTitle("Role API Doc")
42 | .setDescription("Role API Info")
43 | .setVersion("1.0")
44 | .addBearerAuth()
45 | .addTag("role") // match tags in controllers
46 | .build();
47 | const roleApiDocument = SwaggerModule.createDocument(app, roleApiOptions, {
48 | include: [RoleModule],
49 | });
50 | SwaggerModule.setup("swagger/role", app, roleApiDocument);
51 | const petApiOptions = new DocumentBuilder()
52 | .setTitle("Pet API Doc")
53 | .setDescription("Pet API Info")
54 | .setVersion("1.0")
55 | .addBearerAuth()
56 | .addTag("pet") // match tags in controllers
57 | .build();
58 | const petApiDocument = SwaggerModule.createDocument(app, petApiOptions, {
59 | include: [PetModule],
60 | });
61 | SwaggerModule.setup("swagger/pet", app, petApiDocument);
62 | app.enableCors();
63 | await app.listen(3000);
64 | console.log("service on port 3000");
65 | }
66 | bootstrap();
67 |
--------------------------------------------------------------------------------
/server/src/modules/file/file.controller.ts:
--------------------------------------------------------------------------------
1 | import BaseController from '../../helper/base.controller';
2 | import {
3 | Controller,
4 | Get,
5 | Post,
6 | Query,
7 | Response,
8 | UseInterceptors,
9 | UploadedFile,
10 | } from '@nestjs/common';
11 | import { FileService } from './file.service';
12 | import { AuthGuard } from '@nestjs/passport';
13 | import { UseGuards } from '@nestjs/common';
14 | import { FileInterceptor } from '@nestjs/platform-express';
15 | import { ApiTags } from '@nestjs/swagger';
16 | import * as path from 'path';
17 | import * as fs from 'fs';
18 | const targetDir = path.resolve('public/files');
19 | const res = fs.existsSync(targetDir);
20 | if (!res) {
21 | // 没有目录就创建一个
22 | fs.mkdirSync(targetDir);
23 | }
24 | @UseGuards(AuthGuard())
25 | @ApiTags('/file')
26 | @Controller('file')
27 | export class FileController extends BaseController {
28 | constructor(private readonly appService: FileService) {
29 | super();
30 | }
31 | @Get('/getOneById') // 获取一个
32 | async findOne(@Response() response, @Query() payload) {
33 | const res = await this.appService.findOne(payload);
34 | return this.success(response, res);
35 | }
36 | @Get('/all') // 获取全部
37 | async findAll(@Response() response, @Query() payload) {
38 | const res = await this.appService.findAll(payload);
39 | return this.success(response, res);
40 | }
41 | @Get('/downloadById') // 下载一个
42 | async download(@Response() response, @Query() payload) {
43 | const res = await this.appService.findOne(payload);
44 | const targetPath = targetDir + res.url.replace('/static/files', '')
45 | // const readStream = fs.createReadStream(targetPath);
46 | // response.writeHead(200, {
47 | // 'Content-Type': 'application/force-download',
48 | // 'Content-Disposition': 'attachment; filename=' + res.name,
49 | // });
50 | // readStream.pipe(response);
51 | response.download(targetPath)
52 | }
53 | @Post('/upload')
54 | @UseInterceptors(FileInterceptor('file'))
55 | async uploadFile(@UploadedFile() file, @Response() response) {
56 | const filename = file.originalname;
57 | const extname = path.extname(filename).toLowerCase();
58 | const realFilename = filename.replace(extname, '');
59 | const timeStr = Date.now();
60 | const target = path.join(targetDir, `${realFilename}_${timeStr}${extname}`);
61 | const serverUrl = `/static/files/${realFilename}_${timeStr}${extname}`;
62 | const res = (await this.appService.upload(
63 | target,
64 | file.buffer,
65 | serverUrl,
66 | filename,
67 | )) as any;
68 | if (res) {
69 | this.success(response, {
70 | serverUrl,
71 | uid: res._id,
72 | name: res.name,
73 | });
74 | } else {
75 | this.error(response, '文件上传失败');
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/server/src/modules/file/file.interface.ts:
--------------------------------------------------------------------------------
1 | import { ApiPropertyOptional } from '@nestjs/swagger';
2 | import { Document } from 'mongoose';
3 | export interface FileInterface extends Document{
4 | url: string,
5 | name: string,
6 | }
7 | export class FileDto {
8 | @ApiPropertyOptional({
9 | description: '地址',
10 | })
11 | readonly url?: string;
12 | @ApiPropertyOptional({
13 | description: '文件名',
14 | })
15 | readonly name?: string;
16 | }
--------------------------------------------------------------------------------
/server/src/modules/file/file.model.ts:
--------------------------------------------------------------------------------
1 | import * as mongoose from 'mongoose';
2 | export const FileModel = new mongoose.Schema(
3 | {
4 | url: {
5 | type: String,
6 | unique: true,
7 | required: true,
8 | },
9 | name: {
10 | type: String,
11 | },
12 | },
13 | {
14 | versionKey: false,
15 | timestamps: {
16 | currentTime: () => Date.now() + 8 * 60 * 60 * 1000,
17 | },
18 | },
19 | );
20 |
--------------------------------------------------------------------------------
/server/src/modules/file/file.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from "@nestjs/common";
2 | import { FileService } from "./file.service";
3 | import { FileController } from "./file.controller";
4 | import { PassportModule } from "@nestjs/passport";
5 | import { MongooseModule } from "@nestjs/mongoose";
6 | import { FileModel } from "./file.model";
7 | @Module({
8 | imports: [
9 | PassportModule.register({ defaultStrategy: "jwt" }),
10 | MongooseModule.forFeature([
11 | { name: "File", schema: FileModel, collection: "File" },
12 | ]),
13 | ],
14 | controllers: [FileController],
15 | providers: [FileService],
16 | exports: [FileService],
17 | })
18 | export class FileModule {}
19 |
--------------------------------------------------------------------------------
/server/src/modules/file/file.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from "@nestjs/common";
2 | import { FileInterface } from "./file.interface";
3 | import { Model } from "mongoose";
4 | import { InjectModel } from "@nestjs/mongoose";
5 | import { filterSearch, formatTime } from "../../util/util";
6 | import * as fs from "fs"
7 | @Injectable()
8 | export class FileService {
9 | constructor(
10 | @InjectModel("File") private readonly FileModel: Model
11 | ) {}
12 | async findOne(payload) {
13 | const { id } = payload;
14 | return await this.FileModel.findOne({ _id: id });
15 | }
16 | async findAll(payload) {
17 | let { pageNo = "1", pageSize = "10", orderName = "updatedAt" , orderDir = "desc" } = payload;
18 | let res = [];
19 | let total = 0;
20 | let skip = (Number(pageNo) - 1) * Number(pageSize);
21 | const params = filterSearch(payload);
22 | if(!orderName){
23 | orderName = "updatedAt"
24 | }
25 | const sortObj = {
26 | [orderName]: orderDir === "desc"? -1: 1
27 | }
28 | res = await this.FileModel.find(params)
29 | .skip(skip)
30 | .limit(Number(pageSize))
31 | .sort(sortObj)
32 | .exec();
33 | total = await this.FileModel.countDocuments()
34 | let data = res.map((e) => {
35 | const jsonObject = Object.assign({}, e._doc);
36 | jsonObject.createdAt = formatTime(e.createdAt);
37 | return jsonObject;
38 | });
39 | return {
40 | total: total,
41 | list: data,
42 | pageSize: Number(pageSize),
43 | pageNo: Number(pageNo),
44 | };
45 | }
46 | async upload(path, fileBuffer, url, filename){
47 | return new Promise((resolve, reject) => {
48 | const writeStream = fs.createWriteStream(path);
49 | writeStream.write(fileBuffer)
50 | writeStream.on('error', (err) => {
51 | resolve(false)
52 | });
53 | writeStream.on('finish', async () => {
54 | const payload = {
55 | name: filename,
56 | url,
57 | }
58 | const res = await this.FileModel.create(payload);
59 | resolve(res)
60 | });
61 | writeStream.end();
62 | })
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/server/src/modules/img/img.interface.ts:
--------------------------------------------------------------------------------
1 | import { ApiPropertyOptional } from '@nestjs/swagger';
2 | import { Document } from 'mongoose';
3 | export interface ImgInterface extends Document{
4 | imgname: string,
5 | file: string,
6 | }
7 | export class ImgDto {
8 | @ApiPropertyOptional({
9 | description: '图片名称',
10 | })
11 | readonly imgname: string;
12 | @ApiPropertyOptional({
13 | description: '图片文件',
14 | })
15 | readonly file: string;
16 | }
--------------------------------------------------------------------------------
/server/src/modules/img/img.model.ts:
--------------------------------------------------------------------------------
1 | import * as mongoose from "mongoose";
2 | export const ImgModel = new mongoose.Schema(
3 | {
4 | imgname: { type: String, unique: true, required: true },
5 | file: { type: String, required: true },
6 | },
7 | {
8 | versionKey: false,
9 | timestamps: true,
10 | }
11 | );
12 |
--------------------------------------------------------------------------------
/server/src/modules/img/img.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from "@nestjs/common";
2 | import { ImgService } from "./img.service";
3 | import { ImgController } from "./img.controller";
4 | import { PassportModule } from "@nestjs/passport";
5 | import { MongooseModule } from "@nestjs/mongoose";
6 | import { ImgModel } from "./img.model";
7 | @Module({
8 | imports: [
9 | // PassportModule.register({defaultStrategy: 'bearer'})
10 | // 指定strategy, 不用再AuthGuard里特别指定
11 | PassportModule.register({ defaultStrategy: "jwt" }),
12 | // TypeOrmModule.forFeature([User, Platform, Role]),
13 | MongooseModule.forFeature([
14 | { name: "Img", schema: ImgModel, collection: "Img" },
15 | ]),
16 | ],
17 | controllers: [ImgController],
18 | providers: [ImgService],
19 | exports: [ImgService],
20 | })
21 | export class ImgModule {}
22 |
--------------------------------------------------------------------------------
/server/src/modules/img/img.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from "@nestjs/common";
2 | import { ImgInterface } from "./img.interface";
3 | import { Model } from "mongoose";
4 | import { InjectModel } from "@nestjs/mongoose";
5 | import { filterSearch, formatTime } from "../../util/util";
6 | import * as fs from "fs"
7 | @Injectable()
8 | export class ImgService {
9 | constructor(
10 | @InjectModel("Img") private readonly ImgModel: Model
11 | ) {}
12 | async findAll(payload) {
13 | const { pageNo = "1", pageSize = "10" } = payload;
14 | let res = [];
15 | let total = 0;
16 | let skip = (Number(pageNo) - 1) * Number(pageSize);
17 | const params = filterSearch(payload);
18 | res = await this.ImgModel.find(params)
19 | .skip(skip)
20 | .limit(Number(pageSize))
21 | .sort({ updatedAt: -1 })
22 | .exec();
23 | // total = await this.UserModel.count(params).exec();
24 | total = await this.ImgModel.countDocuments()
25 | return {
26 | total: total,
27 | list: res,
28 | pageSize: Number(pageSize),
29 | pageNo: Number(pageNo),
30 | };
31 | }
32 | async getImgById(id: string): Promise {
33 | return await this.ImgModel.findOne({ _id: id });
34 | }
35 | async findOneByImgName(imgname: string): Promise {
36 | const res = await this.ImgModel.findOne({ imgname });
37 | return res;
38 | }
39 | async createOne(body: ImgInterface) {
40 | return await this.ImgModel.create(body);
41 | }
42 | async deleteById(id: string) {
43 | return await this.ImgModel.deleteOne({ _id: id });
44 | }
45 | async update(id: string, payload: ImgInterface): Promise{
46 | return await this.ImgModel.findByIdAndUpdate(id, payload);
47 | }
48 | async upload(fileName, fileBuffer){
49 | return new Promise((resolve, reject) => {
50 | const writeStream = fs.createWriteStream(fileName);
51 | writeStream.write(fileBuffer)
52 | writeStream.on('error', (err) => {
53 | resolve(false)
54 | });
55 | writeStream.on('finish', () => {
56 | resolve(true)
57 | });
58 | writeStream.end();
59 | })
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/server/src/modules/pet/pet.interface.ts:
--------------------------------------------------------------------------------
1 | import { ApiPropertyOptional } from '@nestjs/swagger';
2 | import { Document } from 'mongoose';
3 | export interface PetInterface extends Document{
4 | code: string,
5 | name: string,
6 | sex: string,
7 | desp: string,
8 | }
9 | export class PetDto {
10 | @ApiPropertyOptional({
11 | description: '编号',
12 | })
13 | readonly code?: string;
14 | @ApiPropertyOptional({
15 | description: '名称',
16 | })
17 | readonly name?: string;
18 | @ApiPropertyOptional({
19 | description: '性别',
20 | })
21 | readonly sex?: string;
22 | @ApiPropertyOptional({
23 | description: '备注',
24 | })
25 | readonly desp?: string;
26 | }
--------------------------------------------------------------------------------
/server/src/modules/pet/pet.model.ts:
--------------------------------------------------------------------------------
1 | import * as mongoose from "mongoose";
2 | const obj = {
3 | code: {
4 | type: String,
5 | required: true,
6 | },
7 | name: {
8 | type: String,
9 | },
10 | sex: {
11 | type: String,
12 | default: -1,
13 | },
14 | desp: {
15 | type: String,
16 | },
17 | };
18 | const params = {
19 | versionKey: false,
20 | timestamps: true,
21 | };
22 | export const PetModel = new mongoose.Schema(obj, params);
23 |
--------------------------------------------------------------------------------
/server/src/modules/pet/pet.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from "@nestjs/common";
2 | import { PetService } from "./pet.service";
3 | import { PetController } from "./pet.controller";
4 | import { PassportModule } from "@nestjs/passport";
5 | import { MongooseModule } from "@nestjs/mongoose";
6 | import { PetModel } from "./pet.model";
7 | @Module({
8 | imports: [
9 | PassportModule.register({ defaultStrategy: "jwt" }),
10 | MongooseModule.forFeature([
11 | { name: "Pet", schema: PetModel, collection: "Pet" },
12 | ]),
13 | ],
14 | controllers: [PetController],
15 | providers: [PetService],
16 | exports: [PetService],
17 | })
18 | export class PetModule {}
19 |
--------------------------------------------------------------------------------
/server/src/modules/pet/pet.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from "@nestjs/common";
2 | import { PetInterface } from "./pet.interface";
3 | import { Model } from "mongoose";
4 | import { InjectModel } from "@nestjs/mongoose";
5 | import { filterSearch, formatTime } from "../../util/util";
6 | @Injectable()
7 | export class PetService {
8 | constructor(
9 | @InjectModel("Pet") private readonly PetModel: Model
10 | ) {}
11 | async findAll(payload) {
12 | let { pageNo = "1", pageSize = "10", orderName = "updatedAt" , orderDir = "desc" } = payload;
13 | let res = [];
14 | let total = 0;
15 | let skip = (Number(pageNo) - 1) * Number(pageSize);
16 | const params = filterSearch(payload);
17 | if(!orderName){
18 | orderName = "updatedAt"
19 | }
20 | const sortObj = {
21 | [orderName]: orderDir === "desc"? -1: 1
22 | }
23 | res = await this.PetModel.find(params)
24 | .skip(skip)
25 | .limit(Number(pageSize))
26 | .sort(sortObj)
27 | .exec();
28 | total = await this.PetModel.countDocuments()
29 | let data = res.map((e) => {
30 | const jsonObject = Object.assign({}, e._doc);
31 | jsonObject.createdAt = formatTime(e.createdAt);
32 | return jsonObject;
33 | });
34 | return {
35 | total: total,
36 | list: data,
37 | pageSize: Number(pageSize),
38 | pageNo: Number(pageNo),
39 | };
40 | }
41 | async getById(id: string): Promise {
42 | return await this.PetModel.findOne({ _id: id });
43 | }
44 | async getByName(name: string): Promise {
45 | const res = await this.PetModel.findOne({ name });
46 | return res;
47 | }
48 | async createOne(body: PetInterface) {
49 | return await this.PetModel.create(body);
50 | }
51 | async deleteById(id: string) {
52 | return await this.PetModel.deleteOne({ _id: id });
53 | }
54 | async deleteMany(ids: string) {
55 | const payload:string[] = ids.split(",") || [];
56 | return await this.PetModel.deleteMany({_id: {$in: payload}})
57 | }
58 | async update(id: string, payload: PetInterface): Promise{
59 | return await this.PetModel.findByIdAndUpdate(id, payload);
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/server/src/modules/sys/auth/auth module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { UserModule } from '../user/user.module';
3 | import { AuthService } from './auth.service';
4 | // import { HttpStrategy } from './passport/http.strategy';
5 | import { JwtModule } from '@nestjs/jwt';
6 | import { AuthController } from './auth.controller';
7 | import { JwtStrategy } from './passport/jwt.strategy';
8 | @Module({
9 | imports: [
10 | JwtModule.register({
11 | // secretOrPrivateKey: "zjToken",
12 | secret: 'zjToken',
13 | signOptions: {
14 | // expiresIn: 3600,
15 | },
16 | }),
17 | UserModule,
18 | ],
19 | providers: [AuthService, JwtStrategy], //HttpStrategy
20 | controllers: [AuthController],
21 | })
22 | export class AuthModule {}
23 |
--------------------------------------------------------------------------------
/server/src/modules/sys/auth/auth module.ts1:
--------------------------------------------------------------------------------
1 | import { Module } from "@nestjs/common";
2 | import { UserModule } from "../user/user.module";
3 | import { AuthService } from "./auth.service";
4 | import { HttpStrategy } from "./passport/http.strategy";
5 | @Module({
6 | imports: [UserModule],
7 | providers: [AuthService, HttpStrategy],
8 | })
9 | export class AuthModule {}
10 |
--------------------------------------------------------------------------------
/server/src/modules/sys/auth/auth.controller.ts:
--------------------------------------------------------------------------------
1 | import { Controller, Post, Body, Response } from '@nestjs/common';
2 | import { ApiBody, ApiQuery, ApiTags } from '@nestjs/swagger';
3 | import { UserService } from '../user/user.service';
4 | import { UserInterface, UserDto } from '../user/user.interface';
5 | import { AuthService } from './auth.service';
6 | import BaseController from '../../../helper/base.controller';
7 | @ApiTags('auth')
8 | @Controller('auth')
9 | export class AuthController extends BaseController {
10 | constructor(
11 | private readonly authService: AuthService,
12 | private readonly userService: UserService,
13 | ) {
14 | super();
15 | }
16 | // 默认密码admin, 000000
17 | // 传入name及password取得jwt token
18 | @Post('/login')
19 | @ApiBody({
20 | type: UserDto,
21 | description: '用户信息',
22 | })
23 | async getToken(
24 | @Body('username') username: string,
25 | @Body('password') password: string,
26 | @Response() response,
27 | ) {
28 | const res = await this.authService.createToken(username, password);
29 | return this.success(response, res);
30 | }
31 | @Post('/regist') // 创建用户
32 | @ApiBody({
33 | type: UserDto,
34 | description: '用户名',
35 | })
36 | async regist(@Body() body: UserInterface, @Response() response) {
37 | const { username } = body;
38 | const targetUser = await this.userService.findOneByUserName(username);
39 | if (targetUser) {
40 | return this.error(response, '用户名已存在');
41 | }
42 | const res = await this.userService.createOne(body);
43 | if (res._id) {
44 | return this.message(response, '添加新用户成功');
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/server/src/modules/sys/auth/auth.service.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Injectable,
3 | UnauthorizedException,
4 | HttpException,
5 | } from "@nestjs/common";
6 | import { UserService } from "../user/user.service";
7 | import { JwtService } from "@nestjs/jwt";
8 | import { UserInterface } from "../user/user.interface";
9 | import * as md5 from "md5";
10 | import config from "../../../config"
11 | @Injectable()
12 | export class AuthService {
13 | constructor(
14 | private readonly usersService: UserService,
15 | private readonly jwtService: JwtService
16 | ) {}
17 | async createToken(name: string, password: string) {
18 | const user: UserInterface | null = await this.usersService.findOneByUserName(
19 | name
20 | );
21 | if (!user) {
22 | throw new UnauthorizedException("用户名不存在。");
23 | }
24 | if (user.password !== md5(password + config.HasSalt)) {
25 | throw new UnauthorizedException("密码不对。");
26 | }
27 | const expiration = 60 * 60 * 24 * 7;
28 | const accessToken = this.jwtService.sign(
29 | { id: user._id },
30 | {
31 | // jwt只加密id
32 | expiresIn: expiration,
33 | }
34 | );
35 | return {
36 | expiration,
37 | accessToken,
38 | };
39 | }
40 | async validateUser(payload) {
41 | return await this.usersService.getUserById(payload.id); // 把整个user对象拿回来,会自动放入到req中
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/server/src/modules/sys/auth/passport/http.strategy.ts:
--------------------------------------------------------------------------------
1 | import { Injectable, UnauthorizedException } from '@nestjs/common';
2 | import { AuthService } from '../auth.service';
3 | import { PassportStrategy } from '@nestjs/passport';
4 | import { Strategy } from 'passport-http-bearer';
5 | @Injectable()
6 | // 要继承@nest/passport下的PassportStrategy并传入passport
7 | // 本列是以http bearer为例
8 | export class HttpStrategy extends PassportStrategy(Strategy) {
9 | constructor(private readonly authService: AuthService) {
10 | super();
11 | }
12 | async validate(token: string) {
13 | const user = await this.authService.validateUser(token);
14 | if (!user) {
15 | return new UnauthorizedException();
16 | }
17 | return user;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/server/src/modules/sys/auth/passport/jwt.strategy.ts:
--------------------------------------------------------------------------------
1 | import { ExtractJwt, Strategy } from 'passport-jwt';
2 | import { AuthService } from '../auth.service';
3 | import { Injectable, UnauthorizedException } from '@nestjs/common';
4 | import { PassportStrategy } from '@nestjs/passport';
5 | @Injectable()
6 | export class JwtStrategy extends PassportStrategy(Strategy) {
7 | constructor(private readonly authService: AuthService) {
8 | super({
9 | // 这里没有intellisense可以用,下面这一段是说
10 | // 要从header取得bearer token
11 | jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
12 | // 这里的key就是要跟create token时的key一样
13 | secretOrKey: 'zjToken',
14 | });
15 | }
16 | // Passport会自动verify jwt,如果key不正确,或是相关信息不正确,如issuer
17 | async validate(payload) {
18 | const user = await this.authService.validateUser(payload);
19 | if (!user) throw new UnauthorizedException();
20 | return user;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/server/src/modules/sys/menu/menu.interface.ts:
--------------------------------------------------------------------------------
1 | import { ApiPropertyOptional } from '@nestjs/swagger';
2 | import { Document } from 'mongoose';
3 | export interface MenuInterface extends Document {
4 | label: string;
5 | code: string;
6 | pid: string;
7 | path: string;
8 | desc?: string;
9 | sort?: number;
10 | }
11 | export class MenuDto {
12 | @ApiPropertyOptional({
13 | description: '菜单名',
14 | })
15 | readonly label?: string;
16 | @ApiPropertyOptional({
17 | description: '菜单编码',
18 | })
19 | readonly code: string;
20 | @ApiPropertyOptional({
21 | description: '父菜单编码',
22 | })
23 | readonly parent: string;
24 | @ApiPropertyOptional({
25 | description: '菜单路由',
26 | })
27 | readonly path: string;
28 | @ApiPropertyOptional({
29 | description: '描述',
30 | })
31 | readonly desc: string;
32 | }
33 |
--------------------------------------------------------------------------------
/server/src/modules/sys/menu/menu.model.ts:
--------------------------------------------------------------------------------
1 | import * as mongoose from 'mongoose';
2 | export const MenuModel = new mongoose.Schema(
3 | {
4 | menuKey: { type: String, required: true, unique: true },
5 | menuType: { type: String, required: true },
6 | label: { type: String, required: true },
7 | desp: { type: String },
8 | path: { type: String, required: true },
9 | name: { type: String },
10 | aliveName: { type: String, default: '' },
11 | pid: { type: String },
12 | pname: { type: String, default: '' },
13 | component: { type: String },
14 | dynamicPage: { type: String },
15 | linkAddress: { type: String },
16 | icon: { type: String },
17 | sort: { type: Number },
18 | },
19 | {
20 | versionKey: false,
21 | timestamps: {
22 | currentTime: () => Date.now() + 8 * 60 * 60 * 1000,
23 | },
24 | },
25 | );
26 |
--------------------------------------------------------------------------------
/server/src/modules/sys/menu/menu.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from "@nestjs/common";
2 | import { MenuService } from "./menu.service";
3 | import { MenuController } from "./menu.controller";
4 | import { PassportModule } from "@nestjs/passport";
5 | import { MongooseModule } from "@nestjs/mongoose";
6 | import { MenuModel } from "./menu.model";
7 | @Module({
8 | imports: [
9 | PassportModule.register({ defaultStrategy: "jwt" }),
10 | MongooseModule.forFeature([
11 | { name: "Menu", schema: MenuModel, collection: "Menu" },
12 | ]),
13 | ],
14 | controllers: [MenuController],
15 | providers: [MenuService],
16 | exports: [MenuService]
17 | })
18 | export class MenuModule {}
19 |
--------------------------------------------------------------------------------
/server/src/modules/sys/menu/menu.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@nestjs/common';
2 | import { MenuInterface } from './menu.interface';
3 | import { Model } from 'mongoose';
4 | import { InjectModel } from '@nestjs/mongoose';
5 | import { getTreeData } from '../../../util/util';
6 | @Injectable()
7 | export class MenuService {
8 | constructor(
9 | @InjectModel('Menu') private readonly MenuModel: Model,
10 | ) {}
11 | async create(payload) {
12 | return await this.MenuModel.create(payload);
13 | }
14 | async findList(): Promise {
15 | // 获取一维数组
16 | const menus = await this.MenuModel.find({}).sort({ sort: 1 }); // 按最新更新时间排序
17 | return menus;
18 | }
19 | async findAll(): Promise {
20 | // 获取菜单树
21 | const menus = await this.MenuModel.find({}).sort({ sort: 1 }); // 按最新更新时间排序
22 | const nodes = getTreeData(menus);
23 | return nodes;
24 | }
25 | async getMenuById(id: string): Promise {
26 | return await this.MenuModel.findOne({ _id: id });
27 | }
28 | async findOneByMenuKey(menuKey: string): Promise {
29 | return await this.MenuModel.findOne({ menuKey });
30 | }
31 | async update(id: string, payload: MenuInterface): Promise {
32 | return await this.MenuModel.findByIdAndUpdate(id, payload);
33 | }
34 | async deleteById(id: string) {
35 | const res = await this.MenuModel.deleteOne({ _id: id });
36 | const children = await this.findChildrenById(id);
37 | const ids = children.map((item) => item._id);
38 | return await this.MenuModel.deleteMany({ _id: { $in: ids } });
39 | }
40 | async findChildrenById(id: string) {
41 | return await this.MenuModel.find({ pid: id }).sort({ sort: 1 });
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/server/src/modules/sys/role/role.interface.ts:
--------------------------------------------------------------------------------
1 | import { ApiPropertyOptional } from "@nestjs/swagger";
2 | import { Document } from 'mongoose';
3 | export interface RoleInterface extends Document{
4 | label: string;
5 | code: string;
6 | desc?: string;
7 | routes: Array