179 Commits

Author SHA1 Message Date
lyt
67e79a5786 update README.en.md. 2022-12-08 12:00:40 +08:00
lyt
a9e1b6770a publish inital version 2022-11-25 22:42:09 +08:00
lyt
161748a549 'admin-22.11.19:修复v2.3.0版本动态路由事件调用关闭当前tagsview、普通路由刷新界面参数丢失问题' 2022-11-19 00:09:19 +08:00
lyt
f9ce14431d 'admin-22.11.18:优化v2.3.0版本tagsview风格5兼容火狐' 2022-11-18 02:19:03 +08:00
lyt
3a8f31ab89 'admin-22.11.17:优化v2.3.0版本iframe右键菜单刷新问题' 2022-11-17 08:25:32 +08:00
lyt
f00fc0d2f1 'admin-22.11.17:优化v2.3.0版本iframe右键菜单刷新及loading' 2022-11-17 01:18:18 +08:00
lyt
32e2d6cf94 'admin-22.11.17:修复v2.3.0版本iframe右键菜单刷新' 2022-11-17 00:44:00 +08:00
5cb614c277 'admin-22.11.17:修复v2.3.0版本iframe右键菜单刷新'
Signed-off-by: lyt-Top <1105290566@qq.com>
2022-11-16 16:39:38 +00:00
lyt
cdf1b715df 'admin-22.11.16:发布v2.3.0,具体更新内容查看CHANGELOG.md' 2022-11-16 15:34:23 +08:00
lyt
aba9f3947d 'admin-22.07.20:降级vite@2.9.14,防止打包样式丢掉问题' 2022-07-20 23:15:32 +08:00
lyt
e08b4b3782 'admin-22.07.16:修复行内表单最后一个el-form-item位置下移问题' 2022-07-16 22:59:53 +08:00
lyt
356040806d 'admin-22.07.16:修复行内表单最后一个el-form-item位置下移问题' 2022-07-16 22:57:49 +08:00
lyt
f56cae3719 'admin-22.07.11:el-form表单最后一行样式,移动端lable左对齐' 2022-07-11 21:02:36 +08:00
b88fe3957f !32 开启后端控制路由找不到登录路由
Merge pull request !32 from 赵国辉/master
2022-07-11 12:49:53 +00:00
ccdce9b904 v2.2.0版本 src/router/route.ts 静态路由“布局界面”已删除,在“开启后端控制路由”时,未登录无法找到登录路由 2022-07-11 10:02:40 +08:00
134da2bc8c !30 修复多个权限同时校验的BUG
Merge pull request !30 from LostDeer/auth
2022-07-10 12:57:39 +00:00
4841a29c29 多个权限校验的BUG 2022-07-10 20:12:53 +08:00
lyt
e57dde4bab 'admin-22.07.10:发布v2.2.0版本,具体查看master分支根目录CHANGELOG.md' 2022-07-10 19:37:39 +08:00
fdf9cd4abe !29 修复tailwindcss无法正常工作
Merge pull request !29 from 山田/master
2022-07-03 06:23:56 +00:00
664e70de6c 修复tailwindcss无法正常工作 2022-06-17 10:32:20 +08:00
lyt
4259e9931f 'admin-22.06.07:函数deepClone对null的判断错误' 2022-06-07 22:30:57 +08:00
3a57a7f4ff !28 动态路由国际化匹配规则修复
Merge pull request !28 from tony星/dev
2022-06-07 14:28:46 +00:00
fab396b503 update src/utils/other.ts.
动态路由国际化判断修复,原正则表达式容易匹配到menu之类带en,zh的路由
2022-06-07 07:07:48 +00:00
lyt
41f6992630 'admin-22.06.06:添加大佬集成后端链接' 2022-06-06 22:08:16 +08:00
lyt
a402bd3c3a 'admin-22.06.04:优化开启TagViews缓存后登录到主页控制台js报错' 2022-06-04 19:49:30 +08:00
7d004ee948 !27 use Vue ref for dom
Merge pull request !27 from pauli/master
2022-06-04 11:23:46 +00:00
5a75f2626e fix: ref not id 2022-05-30 10:01:00 +08:00
lyt
21248a361e 'admin-22.05.29:优化最新版是否开启TagsView缓存的设置有严重Bug[#I59RXK](https://gitee.com/lyt-top/vue-next-admin/issues/I59RXK)' 2022-05-29 16:10:57 +08:00
lyt
6441700ae1 'admin-22.05.28:更新优化v2.1.1,更新日志查看根目录CHANGELOG.md' 2022-05-28 19:10:19 +08:00
f716918ef2 !26 dark loading遮罩优化
Merge pull request !26 from tony星/dev
2022-05-27 15:01:39 +00:00
abf9f1ae08 update src/theme/dark.scss.
dark loading遮罩优化
2022-05-25 09:42:27 +00:00
lyt
1cd056cb83 'admin-22.05.22:修复标记为需要缓存的tab页后,再次从左侧菜单打开,还是显示被缓存的页面内容(#I4UY3G),感谢@axcc1234、特别感谢群友@华仔' 2022-05-22 17:18:13 +08:00
lyt
2c8dbace6c 'admin-22.05.19:优化tagsView、路由参数演示界面' 2022-05-19 19:08:29 +08:00
lyt
77b7621e87 'admin-22.05.11:适配element-plusv2.2.0版本,优化注释' 2022-05-11 18:52:22 +08:00
lyt
d6fceb257c 'admin-22.05.07:优化深色模式等' 2022-05-07 20:25:52 +08:00
lyt
23a0c21f15 'admin-22.05.05:优化注释' 2022-05-05 20:00:52 +08:00
lyt
6eaad912f5 admin-22.05.01:重构路由,解决部分No match found for location with path xxx 2022-05-01 11:43:53 +08:00
lyt
faf372a8b9 'admin-22.04.30:退出登录报错问题' 2022-04-30 01:31:21 +08:00
lyt
d375051ec3 'admin-22.04.30:优化首屏加载loading问题' 2022-04-30 00:26:46 +08:00
lyt
c630f04194 'admin-22.04.29:优化滚动条问题及替换pinia后的bug' 2022-04-29 22:57:31 +08:00
lyt
f6302a8b40 'admin-22.04.29:修复tagsview标签页高亮等问题' 2022-04-29 13:19:54 +08:00
lyt
9991d931a2 'admin-22.04.28:wangeditor替换成v5,适配深色模式' 2022-04-28 19:39:44 +08:00
lyt
35fdb164e9 Merge branch 'develop' of https://gitee.com/lyt-top/vue-next-admin into develop 2022-04-27 19:30:10 +08:00
lyt
695884a1aa 'admin-22.04.27:修复全局修改组件大小失效了' 2022-04-27 19:29:31 +08:00
65c953a613 update src/components/iconSelector/index.vue. 2022-04-26 11:21:48 +00:00
lyt
4f36e46f7b 'admin-22.04.26:优化图标选择器回显问题' 2022-04-26 19:17:00 +08:00
lyt
ba4247febd 'admin-22.04.25:优化注释内容' 2022-04-25 19:05:12 +08:00
lyt
c942aec7f1 'admin-22.04.20:优化地址栏有参数退出登录,再次登录不跳之前界面问题' 2022-04-20 20:57:11 +08:00
lyt
c180c24769 'admin-22.04.20:修复开启Tagsview图标、router.push路径找不到时报错问题' 2022-04-20 20:34:30 +08:00
lyt
ec1e963358 'admin-22.04.19:优化菜单鼠标经过hover颜色不统一问题' 2022-04-19 22:01:54 +08:00
lyt
805f991791 'admin-22.04.19:修复打包错误问题' 2022-04-19 21:15:06 +08:00
lyt
d50627e0df admin-22.04.18:更新版本v2.1.0,更新内容查看根目录CHANGELOG.md 2022-04-19 20:31:05 +08:00
lyt
638fe523cb admin-22.04.18:更新版本v2.1.0,更新内容查看根目录CHANGELOG.md 2022-04-18 22:00:00 +08:00
lyt
6dcc2c78c1 admin-22.04.18:更新版本v2.1.0,更新内容查看根目录CHANGELOG.md 2022-04-18 19:14:38 +08:00
lyt
8c216f6e94 'admin-22.03.04:更新版本v2.0.2,更新内容查看根目录CHANGELOG.md' 2022-03-04 12:39:54 +08:00
lyt
49c5eaf1bc 'admin-22.02.25:更新版本v2.0.1,更新内容查看根目录CHANGELOG.md' 2022-02-25 23:19:12 +08:00
lyt
4408b04b0f 'admin-22.02.21:更新版本v2.0.0,更新内容查看根目录CHANGELOG.md' 2022-02-21 23:52:59 +08:00
lyt
920c705421 'admin-21.12.30:新增tagsview风格5' 2021-12-30 20:24:35 +08:00
98e75351b8 !17 修复 修改elementPlus 组件size无效BUG
Merge pull request !17 from P)/master
2021-12-23 11:50:33 +00:00
P)
8ae48b5c7c 修复 修改elementPlus 组件size无效BUG 2021-12-23 05:11:31 +00:00
lyt
616de6e06b 'admin-21.12.22:优化打包时警告问题' 2021-12-22 21:37:33 +08:00
e2db218ca8 update vite.config.ts. 2021-12-22 03:25:32 +00:00
6a6b95b725 'admin-21.12.21:更新优化内容请看CHANGELOG.md文件' 2021-12-21 13:49:55 +00:00
lyt
73db2121ff 'admin-21.12.21:更新优化内容请看CHANGELOG.md文件' 2021-12-21 21:35:01 +08:00
lyt
9051c31771 'admin-21.12.21:更新优化内容请看CHANGELOG.md文件' 2021-12-21 21:32:26 +08:00
lyt
18ef20b230 'admin-21.12.17:修复tagsView拖拽问题,感谢@简单爱、' 2021-12-17 20:19:11 +08:00
lyt
ec5911a987 'admin-21.12.16:优化登录页、添加开发文档连接' 2021-12-16 21:10:17 +08:00
lyt
646eac1c88 'admin-21.12.12:更新修复查看CHANGELOG.md文件' 2021-12-12 21:27:33 +08:00
94af6f8e1e !15 fixed typo
Merge pull request !15 from Rehmet/master
2021-12-12 04:39:01 +00:00
f9c604ac96 update README.md. fixed typo 2021-12-11 09:52:43 +00:00
lyt
4228b58434 'admin-21.12.04:修复更新诸多内容,具体查看CHANGELOG.md文件。改动大,谨慎更新!' 2021-12-04 13:32:14 +08:00
lyt
cc7520976e 'admin-21.10.31:优化login界面组件name值重复问题' 2021-10-31 22:49:53 +08:00
lyt
e3aa4d4816 'admin-21.10.30:修复演示错误,TagsView拖拽问题,感谢群友@小明画家、@HelloWord' 2021-10-30 21:11:40 +08:00
lyt
af13922492 'admin-21.10.18:优化tagsview问题' 2021-10-18 20:32:06 +08:00
lyt
6467f57cf2 'admin-21.10.18:优化tagsview问题' 2021-10-18 20:21:22 +08:00
lyt
04ca9b04c1 'admin-21.10.17:更新优化,具体查看根目录CHANGELOG.md' 2021-10-17 12:32:28 +08:00
lyt
3f4c08c119 'admin-21.09.25:修复横向导航菜单不高亮问题' 2021-09-25 19:11:58 +08:00
lyt
99431138c5 'admin-21.09.25:修改浏览器窗口大小改变时菜单水平折叠问题,感谢群友@三木' 2021-09-25 17:47:34 +08:00
lyt
fe70746902 'admin-21.09.25:更新修复诸多内容,请查看CHANGELOG.md' 2021-09-25 16:47:30 +08:00
lyt
cea507e688 'admin-21.09.11:优化深色模式' 2021-09-11 10:37:29 +08:00
15a3025928 update CHANGELOG.md. 2021-09-10 15:32:18 +00:00
lyt
4f9ddc6c26 'admin-21.09.10:新增功能,具体查看CHANGELOG.md' 2021-09-10 23:24:58 +08:00
f6ff10c4a9 'admin-21.08.29:修复优化诸多内容,请查看CHANGELOG.md文件' 2021-08-29 18:45:29 +08:00
8b1c80a8d4 'admin-21.08.22:修复优化诸多内容,请查看CHANGELOG.md文件' 2021-08-22 18:05:26 +08:00
a4da51c179 !14 修复固定header后没有回到顶部的bug,修复拉取项目后运行不起来的bug
Merge pull request !14 from wjs0509/master
2021-08-21 09:42:43 +00:00
12e6b2b4bc 修复用户管理分页的bug 2021-08-20 19:16:29 +08:00
25fa3536de 修复固定header后没有回到顶部的bug,修复拉取项目后运行不起来的bug 2021-08-20 18:58:52 +08:00
ea5dccebe6 'admin-21.08.16:优化布局配置菜单/顶栏功能' 2021-08-16 21:44:16 +08:00
e0960f9009 'admin-21.08.14优化修复菜单高亮、右键菜单全屏、国际化等' 2021-08-14 18:03:50 +08:00
c49736ae7d 'admin-21.08.07:更新README.md' 2021-08-07 19:43:33 +08:00
dfcf817d70 'admin-21.08.06:优化,具体查看根目录CHANGELOG.md文件' 2021-08-06 12:19:25 +08:00
71eb623cef 'admin-21.08.06:优化,具体查看根目录CHANGELOG.md文件' 2021-08-06 12:17:56 +08:00
28edd79b1c 'admin-21.08.01:更新修复请查看CHANGELOG.md文件或查看标签' 2021-08-01 18:30:30 +08:00
1dd6b653e7 处理图标页全屏后无法渲染的问题 2021-07-27 02:36:19 +00:00
92db3f6335 !12 处理图表页全屏后无法渲染的问题
Merge pull request !12 from MaxNull/dev-20210727
2021-07-27 02:33:54 +00:00
fd5d763de4 处理图标页全屏后无法渲染的问题 2021-07-27 10:13:16 +08:00
0050120d79 Merge branch 'master' of https://gitee.com/maxnull/vue-next-admin
# Conflicts:
#	src/layout/component/aside.vue
#	src/layout/component/header.vue
#	src/layout/navBars/tagsView/tagsView.vue
#	src/store/modules/tagsViewRoutes.ts
2021-07-27 08:51:34 +08:00
b86aca5838 'admin-21.07.26:处理全屏若干问题,pr!11,感谢群友@另一个前端' 2021-07-26 23:46:37 +08:00
a1be0533c4 '处理冲突,请勿拉新(请忽略此版本)' 2021-07-26 20:12:51 +08:00
6ca702d590 处理全屏若干问题 2021-07-26 17:31:26 +08:00
lyt
e3f3a9cc5e 'admin-21.07.26:修复tagsview右键菜单点击开启当前页全屏问题' 2021-07-26 17:00:04 +08:00
0e45548c06 'admin-21.07.25:新增数据可视化演示2、登录页新增扫码' 2021-07-25 01:28:32 +08:00
3e7ff893cd 'admin-21.07.16:优化数据可视化演示、tagsView移动端拖动问题' 2021-07-16 23:59:56 +08:00
lyt
7138d5a24d 'admin-21.07.16:发布v1.0.12版本,具体更新内容查看CHANGELOG.md' 2021-07-16 12:46:26 +08:00
lyt
804213a646 'admin-21.07.15:优化tagsView动态路由时的右键菜单' 2021-07-15 10:53:16 +08:00
d9e9779581 'admin-21.07.14:优化、修复诸多内容,具体查看根目录CHANGELOG.md文件' 2021-07-14 21:02:28 +08:00
f7275035e5 'admin-21.07.08:clipboard切换为vue-clipboard3(#10),@21030442-mao' 2021-07-08 20:52:48 +08:00
2303c4a462 !10 clipboard切换为vue-clipboard3
Merge pull request !10 from MaxNull/master
2021-07-08 12:42:04 +00:00
e4ad584724 Merge branch 'master' of https://gitee.com/lyt-top/vue-next-admin 2021-07-08 17:27:12 +08:00
6c28fb5211 clipboard切换为vue-clipboard3 2021-07-08 17:27:01 +08:00
lyt
ff9d32c2e9 'admin-21.07.08:修复登录页表单在移动端显示问题' 2021-07-08 16:45:10 +08:00
b3a6bb6a8e 'admin-21.07.07:优化内嵌iframe、外链,解决tagsView刷新问题' 2021-07-07 20:16:31 +08:00
lyt
ae27489247 'admin-21.07.07:更新最新依赖' 2021-07-07 17:15:11 +08:00
8ff747f011 'admin-21.07.04:修复图标选择器双向绑定回显、路由引入第三方icon样式表现不一致的问题,感谢群友@伯牙已遇钟子期、@借个微笑丶' 2021-07-04 01:09:37 +08:00
dc36d3b2a4 Merge pull request !9 from 真的不知所措/hotfix 2021-07-03 17:00:01 +00:00
e28760d921 修复路由引入第三方icon样式表现不一致的问题 2021-07-03 21:28:22 +08:00
543a31b6d1 'admin-07.03:修复图标选择器双向绑定回显问题,感谢群友@伯牙已遇钟子期' 2021-07-03 20:29:26 +08:00
03b85bc8b0 'admin-21.07.03:右键菜单文字换行问题' 2021-07-03 17:34:03 +08:00
fe537d0e36 'admin-21.07.02:系列组件优化,请查看CHANGELOG.md更新日志文件' 2021-07-02 21:52:32 +08:00
382454ca0e 'admin-21.07.01:新增滚动通知栏/公告组件及演示界面' 2021-07-01 22:33:57 +08:00
lyt
b2fd7d6010 'admin-21.06.29:更新最新依赖、各项优化,请查看CHANGELOG.md更新文件' 2021-06-29 17:20:40 +08:00
92904ffef3 'admin-21.06.24:修复使用拖动指令出现滚动条的问题' 2021-06-24 19:45:50 +08:00
lyt
0c2026d37e 'admin-21.06.24:新增拖动指令及其演示界面、优化登录页,锁屏界面' 2021-06-24 18:02:13 +08:00
lyt
54bbaa1946 'admin-21.06.23:优化去掉内嵌iframe、其它组件优化等' 2021-06-23 17:29:23 +08:00
8ab22b0a02 'admin-21.06.22:修复内嵌iframe不可使用的问题' 2021-06-22 22:03:59 +08:00
lyt
20e8cad9c7 'admin-21.06.22:@vite2.3.8降低成@vite2.3.7,防止elementPlus字体图标消失' 2021-06-22 10:08:21 +08:00
lyt
79f08c362c 'admin-21.06.21:优化时间工具类,感谢群友@我觉得还行' 2021-06-21 15:49:16 +08:00
2462f110ef 'admin-21.06.19:修复诸多问题,具体查看CHANGELOG.md文件1.04更新日志' 2021-06-20 11:46:51 +08:00
e21fd35b08 'admin-21.06.19:修复诸多问题,具体查看CHANGELOG.md文件1.04更新日志' 2021-06-19 17:53:23 +08:00
9a43e1d6c7 'admin-21.06.19:修复诸多问题,具体查看CHANGELOG.md文件1.04更新日志' 2021-06-19 17:49:42 +08:00
57f666a69e !6 新增 深克隆工具方便开发
Merge pull request !6 from kangert/master
2021-06-18 14:26:56 +08:00
6166da0875 Merge branch 'master' of gitee.com:kangert/vue-next-admin 2021-06-18 11:11:22 +08:00
72ffa2d6fd 新增 深克隆工具方便开发 2021-06-18 11:11:20 +08:00
fa34433aa1 !5 优化 类型定义提高编码体验
Merge pull request !5 from kangert/master
2021-06-17 12:43:17 +08:00
134b63848a 修复 不能将类型“string | undefined”分配给类型“string”的问题 2021-06-17 10:33:15 +08:00
793e740c32 优化 类型定义提高编码体验 2021-06-17 10:32:20 +08:00
1ade90a114 !4 优化 vuex模块自动导入
Merge pull request !4 from kangert/master
2021-06-16 21:51:16 +08:00
6a176cf78a 优化 vuex模块自动导入 2021-06-16 17:32:36 +08:00
lyt
2950720b21 'admin-21.06.03:删除G6思维导图界面' 2021-06-03 11:46:49 +08:00
lyt
1596de0cde 'admin-21.06.01:修复开启后端控制路由isRequestRoutes在非首页刷新页面后,回到首页的问题,感谢群友@伯牙已遇钟子期' 2021-06-02 17:16:41 +08:00
49c6356e35 'admin-21.06.01:修复菜单搜索中文不可以搜索的问题,感谢群友@逍遥天意' 2021-06-01 20:26:35 +08:00
2c1ffb7369 'admin-21.05.31:修复分栏、经典布局路由设置meta.isHide为true时报错问题,感谢群友@29、@芭芭拉' 2021-05-31 21:42:31 +08:00
lyt
80a88495fb 'admin-21.05.31:更正文件命名错误' 2021-05-31 16:10:57 +08:00
lyt
4b82473eb0 'admin-21.05.31:修复分栏布局路由设置meta.isHide为true时报错问题,感谢群友@29' 2021-05-31 16:06:53 +08:00
441a00ed7d 'admin-21.05.29:修复切换布局后面包屑显示异常问题' 2021-05-29 00:29:49 +08:00
lyt
03609aeda6 'admin-21.05.28:修复布局配置设置初始值不生效问题' 2021-05-28 17:23:26 +08:00
4cc95c4f8f 'admin-21.05.27:新增utils下的storage.js读取浏览器缓存新写法,建议使用' 2021-05-27 21:26:45 +08:00
dca87eea7e 'admin-21.05.27:新增utils下的storage.ts读取浏览器缓存新写法,建议使用' 2021-05-27 21:01:41 +08:00
lyt
1bdef58956 'admin-21.05.26:更新最新依赖' 2021-05-26 10:54:28 +08:00
806ef38eea 'admin-21.05.23:修复改变浏览器窗口时,部分布局配置失效问题' 2021-05-23 16:30:14 +08:00
lyt
e51acd0cc1 'admin-21.05.20:根据官方文档,增加eslint的Vue.js3.x的配置,感谢群友@λόγος' 2021-05-20 17:18:26 +08:00
lyt
899dc13024 'admin-21.05.20:修改端口防止端口与百度云冲突,感谢群友@烦。更新最新依赖' 2021-05-20 11:22:29 +08:00
lyt
2063732014 'admin-21.05.18:新增分栏布局分栏导航菜单可设置水平、垂直布局' 2021-05-18 15:42:45 +08:00
fc696e6d94 'admin-21.05.16:优化iframe、更新最新依赖、规范工具类命名' 2021-05-16 17:02:53 +08:00
lyt
be9df6766e 'admin-21.05.13:修复全局改变组件大小默认值报错问题' 2021-05-13 16:41:26 +08:00
lyt
679cac1c74 'admin-21.02.27:vue-next-admin项目第一次提交' 2021-05-13 12:01:44 +08:00
lyt
8b21955e28 'admin-21.02.27:vue-next-admin项目第一次提交' 2021-05-13 12:01:09 +08:00
lyt
051b622730 'admin-21.05.13:删除yarn.lock(会报错),添加package-lock.json等,感谢@mrmengj' 2021-05-13 10:44:11 +08:00
lyt
7e470e4223 'admin-21.05.12:优化全局切换组件size不生效、更新最新依赖等' 2021-05-12 11:32:31 +08:00
lyt
dde15be65c 'admin-21.05.08:优化锁屏界面' 2021-05-08 18:02:13 +08:00
lyt
daaafac7f8 'admin-21.05.08:修复粘贴复制的链接,登录后不跳转到对应的链接的问题' 2021-05-08 11:36:07 +08:00
lyt
7c673b9ae6 'admin-21.05.07:更新最新依赖' 2021-05-07 15:30:00 +08:00
37e3db50b6 'admin-21.05.04:修复ts的interface类型声明及引用报错问题' 2021-05-04 20:51:36 +08:00
6e99cdee7d 'admin-21.05.02:新增树形控件演示、部分逻辑优化' 2021-05-02 21:24:41 +08:00
lyt
a184ebf5d3 'admin-21.04.30:新增波浪效果指令及演示、窗格拆分器等' 2021-04-30 16:15:43 +08:00
lyt
6cf87bee90 'admin-21.04.29:更新最新依赖、新增大图预览及其它组件演示效果优化' 2021-04-29 17:42:01 +08:00
lyt
9863d4ed0d 'admin-21.04.29:新增分栏布局时分栏菜单背景渐变功能' 2021-04-29 11:57:04 +08:00
lyt
7016ef6723 'admin-21.04.29:新增分栏布局时分栏菜单背景渐变功能' 2021-04-29 11:49:01 +08:00
6517b843ae 'admin-21.04.28:修复themeConfig.ts设置默认布局不生效的问题' 2021-04-28 20:20:53 +08:00
lyt
b240c9d8e9 'admin-21.04.28:新增全局改变组件大小功能、拖拽布局演示等' 2021-04-28 17:02:32 +08:00
lyt
fa717eb5f2 'admin-21.04.27:更新最新依赖' 2021-04-27 16:45:37 +08:00
d1e656dc90 'admin-21.04.25:修复打包、切换布局刷新样式丢失问题' 2021-04-25 21:15:44 +08:00
lyt
827c2c6b04 'admin-21.04.23:修复后端控制路由报Invalid-arguments、刷新界面出现404、空白、报错等' 2021-04-23 16:18:17 +08:00
lyt
084e0f2c99 'admin-21.04.23:修复后端控制路由报Invalid-arguments、刷新界面出现404、空白、报错等' 2021-04-23 15:30:32 +08:00
1f4c902f40 'admin-21.04.21:更新最新依赖' 2021-04-21 20:54:47 +08:00
lyt
ea60dd9d93 'admin-21.04.19:修改tsconfig.json,防止开发环境编译器报红问题' 2021-04-19 13:03:36 +08:00
1fe01e62a4 'admin-21.04.18:更新最新依赖' 2021-04-18 16:45:25 +08:00
lyt
b1cf16aa8c 'admin-21.04.15:修改文案、链接等' 2021-04-15 14:16:09 +08:00
70f089acf7 'admin-21.04.14:更新最新依赖、添加eslint规则' 2021-04-14 23:00:38 +08:00
294 changed files with 17772 additions and 9317 deletions

5
.env
View File

@ -1,5 +0,0 @@
# port 端口号
VUE_APP_PORT = 9999
# open 运行 npm run dev 时自动打开浏览器
VUE_APP_OPEN = false

View File

@ -1,2 +0,0 @@
# 开发环境
VUE_APP_BASE_API = http://localhost:9999/

View File

@ -1,2 +0,0 @@
# 线上环境
VUE_APP_BASE_API = ''

View File

@ -1,22 +0,0 @@
module.exports = {
root: true,
env: {
node: true,
},
parserOptions: {
parser: '@babel/eslint-parser',
},
plugins: ['vue'],
extends: ['plugin:vue/essential', 'eslint:recommended'],
rules: {
// http://eslint.cn/docs/rules/
'vue/no-parsing-error': 'off',
'no-unused-vars': 'error',
'no-dupe-args': 'error',
'no-empty': 'off',
'no-extra-semi': 'off',
'no-constant-condition': 'off',
'no-console': 'error',
'vue/multi-word-component-names': 'off',
},
};

31
.gitignore vendored
View File

@ -1,22 +1,11 @@
node_modules
.DS_Store
/dist
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace
# 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?
# Local History for Visual Studio Code
.history/
frontend/LICENSE
*.zip

View File

@ -1,124 +0,0 @@
# <a href="https://gitee.com/lyt-top/vue-next-admin/tree/vue-prev-admin" target="_blank">vue-prev-admin 更新日志</a>
🎉🎉🔥 `vue-prev-admin` 基于 vue2.x + webpack + element ui适配手机、平板、pc 的后台开源免费模板库vue3.x 请切换 master 分支)
## 1.2.3
`2023.04.12`
- 🐞 修复 [#I6UW2I 关闭标签页后,分栏没有消失,需要手动点击首页才会消失](https://gitee.com/lyt-top/vue-next-admin/issues/I6UW2I),感谢[@小菜鸟儿](https://gitee.com/cainiaoer)
- 🎯 优化 `layout/navBars/breadcrumb` 文件夹名称改成 `layout/navBars/topBar` 更易理解(可全局替换),感谢群友@傲世盛唐
- 🎯 优化 `layout/navBars/topBar/user.vue` 组件,`UserNews` 点击消息图标触发范围,防止点击消息通知背景色时不触发 Popover 弹出框
## 1.2.2
`2023.02.23`
🚩🚩🚩 感谢 [驰骋工作流引擎-表单引擎-低代码开发平台](http://www.ccflow.org/) 赞助商的赞助。驰骋公司为社会提供流程引擎+表单引擎+低代码开发平台一体的开源软件解决方案,欢迎广大开发者前去体验!
- 🎉 新增 赞助商组件(`/src/layout/sponsors`[项目目录结构查看](https://lyt-top.gitee.io/vue-next-admin-doc-preview/config/)
- 🎯 优化 `/src/utils/storage``key` 编写成 `${__NEXT_NAME__}:${key}`,防止部署多套系统到同一域名不同目录时,变量共用的问题(`__NEXT_NAME__``package.json` 中的 `name`
## 1.2.1
`2022.12.12`
- 🐞 优化 版本升级提示
- 🐞 优化 深色模式
## 1.2.0
`2022.12.06`
- 🎉 新增 版本升级提示
- 🎉 新增 主题色修改
- 🎉 新增 深色模式
- 🐞 优化 外链界面 `/src/layout/routerView/link.vue`
- 🐞 修复 `菜单水平折叠` 刷新界面还原默认值问题
- 💔 移除 `vue.config.js` 打包加时间戳方法,因为打包报错了
## 1.1.1
`2022.11.17`
- 🐞 优化 [vue2 版本打包出来配置路由懒加载无效。](https://gitee.com/lyt-top/vue-next-admin/issues/I5RFQT),感谢[@林建生](https://gitee.com/ljsshuai)
## 1.1.0
`2022.06.12`
- 🐞 优化 部分界面演示图片不出来问题
- 🐞 修复 [vue-prev-admin 全屏模式下,滚动条无法滚到底 #I4S79C](https://gitee.com/lyt-top/vue-next-admin/issues/I4S79C),感谢[@qfvh](https://gitee.com/qfvh)
## 1.0.9
`2021.12.22`
- 🎯 优化 部署后每次都要强制刷新清浏览器缓存问题
- 🎉 新增 工具类百分比验证演示
- 🐞 修复 tag-view 标签右键会超出浏览器
## 1.0.8
`2021.12.16`
- 🌟 更新 依赖更新最新版本
- 🎯 优化 登录界面逻辑、权限管理逻辑
- 🎯 优化 同步 vue-next-admin-images 后端控制菜单模拟数据
- 🎯 优化 菜单格式(对象改数组)
- 🐞 修复 登录页手机端样式问题
## 1.0.7
`2021.11.27`
- 🌟 更新 依赖更新最新版本
- 🐞 修复 登录问题 [#I4GIKU](https://gitee.com/lyt-top/vue-next-admin/issues/I4GIKU)
## 1.0.6
`2021.11.08`
- 🌟 更新 依赖更新最新版本
- 🎯 优化 目录移动 `@/views/layout` 移动 `@/layout` (可全局替换)
- 🎯 优化 eslint 语法检测问题,`@babel/eslint-parser` 替换已废弃的 `babel-eslint`,出现报错,请尝试降级 eslint
- 🎯 优化 vuex 文件自动导入
## 1.0.5
`2021.06.30`
- 🌟 更新 依赖更新最新版本
- 🎉 新增 表单自适应演示界面
- 🎯 优化 去掉内嵌 iframe 内边距padding
## 1.0.4
`2021.06.19`
- 🌟 更新 依赖更新最新版本
- 🎉 新增 项目仓库地址
- 🎯 优化 移除 utils/storage.ts 下的旧写法(改动较大)
- 🐞 修复 鼠标移入顶部用户信息栏 开/关全屏 文字反向问题
## 1.0.3
`2021.06.02`
- 🌟 更新 依赖更新最新版本
- 💯 优化 动态加载后端返回路由路由(模拟数据)方法
## 1.0.2
`2021.06.01`
- 🐞 修复 菜单搜索中文不可以搜索的问题,感谢群友@逍遥天意
## 1.0.1
`2021.05.31`
- 🎉 新增 新增个人中心演示空白页
- 🎉 新增 更新日志文件 `CHANGELOG.md`,以后每次更新都会在这里显示对应内容
- 🌟 更新 依赖更新最新版本
- 🐞 修复 分栏布局路由设置 `meta.isHide``true` 时报错问题,感谢群友@29@芭芭拉

42
LICENSE
View File

@ -1,21 +1,21 @@
MIT License
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
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
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.
MIT License
Copyright (c) 2022 倔强嘴角留下一抹殇
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
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.

91
README.en.md Normal file

File diff suppressed because one or more lines are too long

169
README.md

File diff suppressed because one or more lines are too long

View File

@ -1,4 +0,0 @@
module.exports = {
presets: ['@vue/cli-plugin-babel/preset'],
plugins: ['@babel/plugin-proposal-optional-chaining'],
};

25
backend/.eslintrc.js Normal file
View File

@ -0,0 +1,25 @@
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 Normal file
View File

@ -0,0 +1,37 @@
# 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

4
backend/.prettierrc Normal file
View File

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

5
backend/nest-cli.json Normal file
View File

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

90
backend/package.json Normal file
View File

@ -0,0 +1,90 @@
{
"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

@ -0,0 +1,47 @@
/*
* @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

@ -0,0 +1,123 @@
/*
* @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

@ -0,0 +1,143 @@
/*
* @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

@ -0,0 +1,57 @@
/*
* @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

@ -0,0 +1,294 @@
/*
* @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

@ -0,0 +1,189 @@
/*
* @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

@ -0,0 +1,188 @@
/*
* @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

@ -0,0 +1,43 @@
/*
* @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

@ -0,0 +1,35 @@
/*
* @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

@ -0,0 +1,19 @@
/*
* @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

@ -0,0 +1,61 @@
/*
* @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

@ -0,0 +1,22 @@
/*
* @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

@ -0,0 +1,18 @@
/*
* @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

@ -0,0 +1,57 @@
/*
* @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

@ -0,0 +1,78 @@
/*
* @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

@ -0,0 +1,21 @@
/*
* @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

@ -0,0 +1,70 @@
/*
* @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

@ -0,0 +1,25 @@
/*
* @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

@ -0,0 +1,26 @@
/*
* @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

@ -0,0 +1,65 @@
/*
* @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

@ -0,0 +1,28 @@
/*
* @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

@ -0,0 +1,24 @@
/*
* @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

@ -0,0 +1,15 @@
/*
* @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

@ -0,0 +1,33 @@
/*
* @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

@ -0,0 +1,216 @@
/*
* @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

@ -0,0 +1,179 @@
/*
* @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

@ -0,0 +1,60 @@
/*
* @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

@ -0,0 +1,94 @@
/*
* @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

@ -0,0 +1,94 @@
/*
* @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

@ -0,0 +1,156 @@
/*
* @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

@ -0,0 +1,93 @@
/*
* @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

@ -0,0 +1,50 @@
/*
* @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
];
}

61
backend/src/app.module.ts Normal file
View File

@ -0,0 +1,61 @@
/*
* @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("*");
}
}

33
backend/src/main.ts Normal file
View File

@ -0,0 +1,33 @@
/*
* @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

@ -0,0 +1,24 @@
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

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

View File

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

21
backend/tsconfig.json Normal file
View File

@ -0,0 +1,21 @@
{
"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 Normal file
View File

@ -0,0 +1,9 @@
.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.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

View File

@ -1,3 +1,4 @@
*.sh
node_modules
lib
@ -5,7 +6,6 @@ lib
*.scss
*.woff
*.ttf
*.json
.vscode
.idea
dist

63
frontend/.eslintrc.js Normal file
View File

@ -0,0 +1,63 @@
module.exports = {
root: true,
env: {
browser: true,
es2021: true,
node: true,
},
parser: 'vue-eslint-parser',
parserOptions: {
ecmaVersion: 12,
parser: '@typescript-eslint/parser',
sourceType: 'module',
},
extends: ['plugin:vue/vue3-essential', 'plugin:vue/essential', 'eslint:recommended'],
plugins: ['vue', '@typescript-eslint'],
rules: {
// http://eslint.cn/docs/rules/
// https://eslint.vuejs.org/rules/
'@typescript-eslint/ban-ts-ignore': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-var-requires': 'off',
'@typescript-eslint/no-empty-function': 'off',
'@typescript-eslint/no-use-before-define': 'off',
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/ban-types': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'vue/custom-event-name-casing': 'off',
'vue/attributes-order': 'off',
'vue/one-component-per-file': 'off',
'vue/html-closing-bracket-newline': 'off',
'vue/max-attributes-per-line': 'off',
'vue/multiline-html-element-content-newline': 'off',
'vue/singleline-html-element-content-newline': 'off',
'vue/attribute-hyphenation': 'off',
'vue/html-self-closing': 'off',
'vue/no-multiple-template-root': 'off',
'vue/require-default-prop': 'off',
'vue/no-v-model-argument': 'off',
'vue/no-arrow-functions-in-watch': 'off',
'vue/no-template-key': 'off',
'vue/no-v-html': 'off',
'vue/comment-directive': 'off',
'vue/no-parsing-error': 'off',
'vue/no-deprecated-v-on-native-modifier': 'off',
'vue/multi-word-component-names': 'off',
'no-useless-escape': 'off',
'no-sparse-arrays': 'off',
'no-prototype-builtins': 'off',
'no-constant-condition': 'off',
'no-use-before-define': 'off',
'no-restricted-globals': 'off',
'no-restricted-syntax': 'off',
'generator-star-spacing': 'off',
'no-unreachable': 'off',
'no-multiple-template-root': 'off',
'no-unused-vars': 'error',
'no-v-model-argument': 'off',
'no-case-declarations': 'off',
'no-console': 'error',
},
};

25
frontend/.gitignore vendored Normal file
View File

@ -0,0 +1,25 @@
.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,7 +1,7 @@
<!DOCTYPE html>
<html lang="en">
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta
@ -12,19 +12,21 @@
name="description"
content="vue-next-admin基于 vue3 + CompositionAPI + typescript + vite + element plus适配手机、平板、pc 的后台开源免费管理系统模板vue-prev-admin基于 vue2 + element ui适配手机、平板、pc 的后台开源免费管理系统模板!"
/>
<link rel="icon" href="<%= BASE_URL %>favicon.ico" />
<title>vue-prev-admin</title>
<link rel="icon" href="/favicon.ico" />
<title>vue-next-admin</title>
</head>
<body>
<div id="app"></div>
<script type="text/javascript">
var _hmt = _hmt || [];
(function() {
(function () {
var hm = document.createElement('script');
hm.src = 'https://hm.baidu.com/hm.js?9d1e524198ede8205ac7c938c243344c';
hm.src = 'https://hm.baidu.com/hm.js?d9c8b87d10717013641458b300c552e4';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(hm, s);
})();
</script>
<script type="module" src="/src/main.ts"></script>
<script type="text/javascript" src="https://api.map.baidu.com/api?v=3.0&ak=wsijQt8sLXrCW71YesmispvYHitfG9gv&s=1"></script>
</body>
</html>

82
frontend/package.json Normal file
View File

@ -0,0 +1,82 @@
{
"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"
}
}

4
frontend/plugins.d.ts vendored Normal file
View File

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

BIN
frontend/public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

13
frontend/shim.d.ts vendored Normal file
View File

@ -0,0 +1,13 @@
/* 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;
}

6
frontend/source.d.ts vendored Normal file
View File

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

107
frontend/src/App.vue Normal file
View File

@ -0,0 +1,107 @@
<!--
* @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

@ -0,0 +1,23 @@
/*
* @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

@ -0,0 +1,34 @@
/*
* @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

@ -0,0 +1,51 @@
/*
* @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

@ -0,0 +1,52 @@
/*
* @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

@ -0,0 +1,51 @@
/*
* @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.

After

Width:  |  Height:  |  Size: 2.1 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.4 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 26 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 36 KiB

View File

@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 50" width="64" height="50">
<defs>
<image width="64" height="50" id="img1" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAAAyCAYAAADsg90UAAAAAXNSR0IB2cksfwAACI5JREFUeJy1WgtsW9UZdvNq0xU6WhZoCCVx7Gunbld1GSsMdYpGREhwr+2IMbQWqRoTk+iWPZi2wWhm1kwEmqV52feRJrbvvd6DiUnbQCAemzY2xh4tSB0wOqZIqAtLYzultIWEpt13btLq+sap/R8nRzpK4vh+5/++85///8851yEk1V+hX6B0tyE3OWwt0x789VR78AKlZ0KBz1oxBE3+BdUW9PNuQ7nJbk/BDQ/v4hi0x44z1R64jyrAVCjQ/fLNN8/ZoUWdgqGc57AFE6I86giH+QRw6XI9QE4RBz3iTAyVW3EyQdEDUmeIIhyeFNtMHO/o0B4e8qYASfVvLk2p4BKgdnioFCAvEQeddhnyFivOpOgvBaFXiAKcygQDrk927S/zjkae5xUA/X3YI/C5AFwHLrSPw+2+YYfCMugiCvDaCbGt0hc9uAXuP1OEAPACZQ+fAGguQ9nGgglx0OfsOAhq24lBcG/YYQa/kWLIz3XlCW4B6mKRCoCMEQdNu3VpnRVnMhBYA2L/LVCATCooXuNJSHXFzv58P+mMyyu5RQDAKNntdLnNjoN0+NMCM0D8901NDkGXuyBAseRZn3Ubagu3AFjTd1GXAdZdpCqurMgSIBS4BwRn8wgwje/d4pV6yz2adGQJyM/bo/ZyC+DSlWqApIiDHqtPyFdlCyBeD4Lv5xHgr5mAWO4ZjTRh9s8tlQDoryIbrOISoLYvvAIALxIHnIHn3GnFmfDfztLhH/IEv/vYd72xyKElJM/6Gdjj4fYCuNA3yYMa6ogdBwQfvIwAk+mguHZz3+MbMPsfLrEALD3v5RfAkH3UOID+Jqqwj2ULIDZeJvhF2He8I0NfX2ry8wI85VSy41LBzaUPsXT4L/Iy0OUbrTgpsXUlyI7lEOAsvGOboPRdKejK28shAPqpek1etxjHArxAiXCo/pAdB2QPLRQg8BLcv8ITi4h4bnaZBGD2+IsQQG0DCDUyv2LfjcHVQyB93iLAecz+bvY/T1x6YrnImwIkVakIAeSrAfIucdCz7qRcY8VJh8RrQDplWfvHxttaVvkGe6vg/h8tpwDoR926wpcOb9CkEhQ4Mbrqym4rTkrcydLhs5bU95Wwwwx+311m8qxPQ4CtvE6AdKjcLlCrQkNJXNXdnYUD0t+eF+AE1v76rV0/qvTEo/8kkkGJq/yFLIKhPsgtgEszl8E4cdAxPHdllgDBwGZW9k6Fguaa9A4PtMEwkrDe0chhCPAZ/E5dNn/kPiWqGe5mVSH1rJCdze2w4rDNDsi/gdn/FPtb0KTfUmeyQenvuE45WCLQd6unsc3fwKeAw0yHX+Zwu247DpbB3emQv3R7x7du8GgybdtrKGc39Ty23rQnqfKk5y9wCwD1XACZJg56xGUMZFVhx1taytjPBnWAfuihyT+3TEiAQwC1Mhzmqwrr9QhbBoeJg36A6Ou0Y/kGe2qQ+mhiGspHDcODTZfs0eRr8fkE0Z4xpyGt4RJgTnX1carq8Jw9dhzv6BB5k+VJyK9t6+y8dNK74bGHMSHK00Scc/CC7fwC6MotAjX6GuqTzsTBkosYW/f9YKUnIb1OFiAW/c4Ce5Lq18jLKKnu5xaApTWBHn0n8Nz6ixhw4zvJ5HXlvU2R3gWui02XizwhrEznbTW9vQ64nUEckBUut13EQOHzFFUA70jErBv+09xcYrXHqUXK8H9qIXXapSs1dm4FN1SF95DdzlD72LMNUt/15CMvXT65NfzIRtQOq5FCv7jAHkPppdqDZ3YvZFZgY8WEQN8dvvFxpB8hLvWT3T8uyWxckG9CEfX38fk0ahGgmboMMIk/2xg5UJKbYZ62sf9RbI7UPxGJzLgT0R2Y/RM0z1HO+QZ6Gl/3+dh2Wmdb6HQwUJc9IfI6fPc40Z5xCLeW2wvw8Pfoy0B5mfoMAuYLn+7oKE0FxZqp+YvWTHugw24PvvtLIjZLh83cAghzmxHqASb5qntz/4EvsfEy7UHroepvJoKtWdUcPPKrVGwsA6k2FuYTAPXAGoC8RfYCStflqRvv3VuB4Hc1SI9bBJjAZ1VWe+p1+TqBHpfG+M8Ksa0EgLacAnhiETNzIPjdbztLPIfPWrME0CSkQ+UfxDFmkQ5v5RPAYcYBckFTcDfUWc+hwZsw06tA+EiOi5RoDns66ctA/Qm3AE4tuhYgZ5Zl9uPR32165PtlINoMwjM5jtPfSvn9WTe/iEufw7PUW+U3a2NSKZ8CTU0s+DyzHAJgq9yuNDaywxNtkQuVWaTDBqs5SIdsQt4hjsXOCvmvzrDu6Fdnebo3Hj3u69xXngoFqkH09KL3ie2BB6y2XCsfZNt1apnOqsL7uY/KoB57k+SDJRUhIZlX2iD4UJ4b5ef+3dqabY9hlunUdPt0XXyIryqs1yKVAv3qbPFuKNNedaAR5FeD4NE8AqRTwZ1ZmxqnHv0EcI4Rxz3hNqSqxThevrEXqpJq31IJgOD3TN3+h0tYmmPpLt9bJex80W4SRBzmEF7kE8Bhbo5aONwutwCx6K4JUVyByu/JfOTn+6EFAiTNN1uoAgxyC1BvqFcAZLJo8pr8rlvuq8wExfpFUl+u/s7/dt6R9UKkS5ergHeaOP6Yc2SQLw7Mqa6SDznsHakvzLAyoeAPCyRvVoXpUGCb1ZZqRWFnhdQXPWcg3Jac5App7C2MImf/5Kb+nupJ0X8FSL1NEOACEyyHPQ9QbWA73CIEkBuEIqrCzUO9CsNB6esCqQ8pAqD/Oe33l9vs2SLQd6svuhJSWW6GeVptLFKGbMD9eltjZ2cjw5m4o60ChF4lCvBeJhjIuntAYF4N3KNEO9Lwgmo+F/D5mNt1cbr/2OeD916qx+HSB4gCsDdNdmXZM7dbjRJtYXeZd/EJ4DDX3Q4uARLSj604yO23UgVA2jRy2EO+OkNX/w+fZNm8pw5QbAAAAABJRU5ErkJggg=="/>
</defs>
<style>
tspan { white-space:pre }
</style>
<use id="Background" href="#img1" x="0" y="0" />
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@ -0,0 +1,30 @@
<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

@ -0,0 +1,31 @@
<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

@ -0,0 +1,36 @@
<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

@ -0,0 +1,149 @@
<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

@ -0,0 +1,88 @@
<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

@ -0,0 +1,252 @@
<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

@ -0,0 +1,195 @@
<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

@ -0,0 +1,73 @@
<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

@ -0,0 +1,68 @@
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

@ -2,54 +2,15 @@
export default {
router: {
home: 'home',
system: 'system',
systemMenu: 'systemMenu',
systemUser: 'systemUser',
limits: 'limits',
limitsFrontEnd: 'FrontEnd',
limitsFrontEndPage: 'FrontEndPage',
limitsFrontEndBtn: 'FrontEndBtn',
limitsBackEnd: 'BackEnd',
limitsBackEndEndPage: 'BackEndEndPage',
menu: 'menu',
menu1: 'menu1',
menu11: 'menu11',
menu12: 'menu12',
menu121: 'menu121',
menu122: 'menu122',
menu13: 'menu13',
menu2: 'menu2',
funIndex: 'function',
funTagsView: 'funTagsView',
funSignCanvas: 'Online signature',
funCountup: 'countup',
funEchartsTree: 'echartsTree',
funSelector: 'funSelector',
funWangEditor: 'wangEditor',
funCropper: 'cropper',
funMindMap: 'G6 MindMap',
funQrcode: 'qrcode',
funEchartsMap: 'EchartsMap',
funPrintJs: 'PrintJs',
funClipboard: 'Copy cut',
funScreenShort: 'screenCapture',
pagesIndex: 'pages',
pagesFiltering: 'Filtering',
pagesFilteringDetails: 'FilteringDetails',
pagesFilteringDetails1: 'FilteringDetails1',
pagesIocnfont: 'iconfont icon',
pagesElement: 'element icon',
pagesAwesome: 'awesome icon',
pagesCityLinkage: 'CityLinkage',
pagesFormAdapt: 'FormAdapt',
pagesListAdapt: 'ListAdapt',
pagesWaterfall: 'Waterfall',
pagesSteps: 'Steps',
chartIndex: 'chartIndex',
system: {
system: 'system',
menu: 'systemMenu',
role: 'systemRole',
user: 'systemUser',
dic: 'systemDic',
city: 'systemDept'
},
personal: 'personal',
tools: 'tools',
layoutLinkView: 'LinkView',
layoutIfameView: 'IfameView',
},
staticRoutes: {
signIn: 'signIn',
@ -64,10 +25,9 @@ export default {
title4: 'news',
title5: 'Full screen on',
title6: 'Full screen off',
dropdownLarge: 'large',
dropdownDefault: 'default',
dropdownMedium: 'medium',
dropdownSmall: 'small',
dropdownMini: 'mini',
dropdown1: 'home page',
dropdown2: 'Personal Center',
dropdown3: '404',
@ -84,7 +44,6 @@ export default {
logOutConfirm: 'determine',
logOutCancel: 'cancel',
logOutExit: 'Exiting',
logOutSuccess: 'Exit successfully!',
},
tagsView: {
refresh: 'refresh',
@ -92,6 +51,7 @@ export default {
closeOther: 'closeOther',
closeAll: 'closeAll',
fullscreen: 'fullscreen',
closeFullscreen: 'closeFullscreen',
},
notFound: {
foundTitle: 'Wrong address input, please re-enter the address~',
@ -106,16 +66,19 @@ export default {
layout: {
configTitle: 'Layout configuration',
oneTitle: 'Global Themes',
twoTitle: 'Menu / top bar',
twoTopTitle: 'top bar set up',
twoMenuTitle: 'Menu set up',
twoColumnsTitle: 'Columns set up',
twoTopBar: 'Top bar background',
twoMenuBar: 'Menu background',
twoColumnsMenuBar: 'Column menu background',
twoTopBarColor: 'Top bar default font color',
twoMenuBarColor: 'Menu default font color',
twoColumnsMenuBarColor: 'Default font color bar menu',
twoIsTopBarColorGradual: 'Top bar gradient',
twoMenuBar: 'Menu background',
twoMenuBarColor: 'Menu default font color',
twoIsMenuBarColorGradual: 'Menu gradient',
twoIsMenuBarColorHighlight: 'Menu font highlight',
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',
@ -131,6 +94,7 @@ export default {
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',
@ -147,18 +111,10 @@ export default {
sixClassic: 'Two',
sixTransverse: 'Three',
sixColumns: 'Four',
tipText: 'Click the button below to copy the layout configuration to `/src/store/modules/themeConfig.js` It has been modified in.',
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!',
},
upgrade: {
title: 'New version',
msg: 'The new version is available, please update it now! Dont worry, the update is fast!',
desc: 'Prompt: Update will restore the default configuration',
btnOne: 'Cruel refusal',
btnTwo: 'Update now',
btnTwoLoading: 'Updating',
},
};

View File

@ -2,54 +2,15 @@
export default {
router: {
home: '首页',
system: '系统设置',
systemMenu: '菜单管理',
systemUser: '用户管理',
limits: '权限管理',
limitsFrontEnd: '前端控制',
limitsFrontEndPage: '页面权限',
limitsFrontEndBtn: '按钮权限',
limitsBackEnd: '后端控制',
limitsBackEndEndPage: '页面权限',
menu: '菜单嵌套',
menu1: '菜单1',
menu11: '菜单11',
menu12: '菜单12',
menu121: '菜单121',
menu122: '菜单122',
menu13: '菜单13',
menu2: '菜单2',
funIndex: '功能',
funTagsView: 'tagsView 操作',
funSignCanvas: '在线签名',
funCountup: 'countup 数字滚动',
funEchartsTree: 'echartsTree 树图',
funSelector: '图标选择器',
funWangEditor: 'wangEditor 编辑器',
funCropper: 'cropper 图片裁剪',
funMindMap: 'G6 思维导图',
funQrcode: 'qrcode 二维码生成',
funEchartsMap: '地理坐标/地图',
funPrintJs: '页面打印',
funClipboard: '复制剪切',
funScreenShort: 'web端自定义截屏',
pagesIndex: '页面',
pagesFiltering: '过滤筛选组件',
pagesFilteringDetails: '过滤筛选组件详情',
pagesFilteringDetails1: '过滤筛选组件详情111',
pagesIocnfont: 'iconfont 字体图标',
pagesElement: 'element 字体图标',
pagesAwesome: 'awesome 字体图标',
pagesCityLinkage: '城市多级联动',
pagesFormAdapt: '表单自适应',
pagesListAdapt: '列表自适应',
pagesWaterfall: '瀑布屏',
pagesSteps: '步骤条',
chartIndex: '大数据图表',
system: {
system: '系统设置',
menu: '菜单管理',
role: '角色管理',
user: '用户管理',
dic: '字典管理',
city: '城市管理'
},
personal: '个人中心',
tools: '工具类集合',
layoutLinkView: '外链',
layoutIfameView: '内嵌 iframe',
},
staticRoutes: {
signIn: '登录',
@ -64,10 +25,9 @@ export default {
title4: '消息',
title5: '开全屏',
title6: '关全屏',
dropdownLarge: '大型',
dropdownDefault: '默认',
dropdownMedium: '中等',
dropdownSmall: '小型',
dropdownMini: '超小',
dropdown1: '首页',
dropdown2: '个人中心',
dropdown3: '404',
@ -84,7 +44,6 @@ export default {
logOutConfirm: '确定',
logOutCancel: '取消',
logOutExit: '退出中',
logOutSuccess: '安全退出成功!',
},
tagsView: {
refresh: '刷新',
@ -92,6 +51,7 @@ export default {
closeOther: '关闭其它',
closeAll: '全部关闭',
fullscreen: '当前页全屏',
closeFullscreen: '关闭全屏',
},
notFound: {
foundTitle: '地址输入错误,请重新输入地址~',
@ -106,16 +66,19 @@ export default {
layout: {
configTitle: '布局配置',
oneTitle: '全局主题',
twoTitle: '菜单 / 顶栏',
twoTopTitle: '顶栏设置',
twoMenuTitle: '菜单设置',
twoColumnsTitle: '分栏设置',
twoTopBar: '顶栏背景',
twoMenuBar: '菜单背景',
twoColumnsMenuBar: '分栏菜单背景',
twoTopBarColor: '顶栏默认字体颜色',
twoMenuBarColor: '菜单默认字体颜色',
twoColumnsMenuBarColor: '分栏菜单默认字体颜色',
twoIsTopBarColorGradual: '顶栏背景渐变',
twoMenuBar: '菜单背景',
twoMenuBarColor: '菜单默认字体颜色',
twoIsMenuBarColorGradual: '菜单背景渐变',
twoIsMenuBarColorHighlight: '菜单字体背景高亮',
twoColumnsMenuBar: '分栏菜单背景',
twoColumnsMenuBarColor: '分栏菜单默认字体颜色',
twoIsColumnsMenuBarColorGradual: '分栏菜单背景渐变',
twoIsColumnsMenuHoverPreload: '分栏菜单鼠标悬停预加载',
threeTitle: '界面设置',
threeIsCollapse: '菜单水平折叠',
threeIsUniqueOpened: '菜单手风琴',
@ -131,6 +94,7 @@ export default {
fourIsTagsviewIcon: '开启 Tagsview 图标',
fourIsCacheTagsView: '开启 TagsView 缓存',
fourIsSortableTagsView: '开启 TagsView 拖拽',
fourIsShareTagsView: '开启 TagsView 共用',
fourIsFooter: '开启 Footer',
fourIsGrayscale: '灰色模式',
fourIsInvert: '色弱模式',
@ -147,18 +111,10 @@ export default {
sixClassic: '经典',
sixTransverse: '横向',
sixColumns: '分栏',
tipText: '点击下方按钮,复制布局配置去 `src/store/modules/themeConfig.js` 中修改。',
tipText: '点击下方按钮,复制布局配置去 `src/stores/themeConfig.ts` 中修改。',
copyText: '一键复制配置',
resetText: '一键恢复默认',
copyTextSuccess: '复制成功!',
copyTextError: '复制失败!',
},
upgrade: {
title: '新版本升级',
msg: '新版本来啦,马上更新尝鲜吧!不用担心,更新很快的哦!',
desc: '提示:更新会还原默认配置',
btnOne: '残忍拒绝',
btnTwo: '马上更新',
btnTwoLoading: '更新中',
},
};

View File

@ -2,54 +2,16 @@
export default {
router: {
home: '首頁',
system: '系統設置',
systemMenu: '選單管理',
systemUser: '用戶管理',
limits: '許可權管理',
limitsFrontEnd: '前端控制',
limitsFrontEndPage: '頁面許可權',
limitsFrontEndBtn: '按鈕許可權',
limitsBackEnd: '後端控制',
limitsBackEndEndPage: '頁面許可權',
menu: '選單嵌套',
menu1: '選單1',
menu11: '選單11',
menu12: '選單12',
menu121: '選單121',
menu122: '選單122',
menu13: '選單13',
menu2: '選單2',
funIndex: '功能',
funTagsView: 'tagsView 操作',
funSignCanvas: '線上簽名',
funCountup: 'countup 數位滾動',
funEchartsTree: 'echartsTree 樹圖',
funSelector: '圖標選擇器',
funWangEditor: 'wangEditor 編輯器',
funCropper: 'cropper 圖片裁剪',
funMindMap: 'G6 心智圖',
funQrcode: 'qrcode 二維碼生成',
funEchartsMap: '地理座標/地圖',
funPrintJs: '頁面列印',
funClipboard: '複製剪切',
funScreenShort: '自定義截圖',
pagesIndex: '頁面',
pagesFiltering: '過濾篩選組件',
pagesFilteringDetails: '過濾篩選組件詳情',
pagesFilteringDetails1: '過濾篩選組件詳情111',
pagesIocnfont: 'iconfont 字體圖標',
pagesElement: 'element 字體圖標',
pagesAwesome: 'awesome 字體圖標',
pagesCityLinkage: '都市多級聯動',
pagesFormAdapt: '表單自我調整',
pagesListAdapt: '清單自我調整',
pagesWaterfall: '瀑布屏',
pagesSteps: '步驟條',
chartIndex: '大資料圖表',
system: {
system: '系統設置',
menu: '選單管理',
role: '角色管理',
user: '用戶管理',
dic: '字典管理',
city: '城市管理'
},
personal: '個人中心',
tools: '工具類集合',
layoutLinkView: '外鏈',
layoutIfameView: '内嵌 iframe',
},
staticRoutes: {
signIn: '登入',
@ -64,10 +26,9 @@ export default {
title4: '消息',
title5: '開全屏',
title6: '關全屏',
dropdownLarge: '大型',
dropdownDefault: '默認',
dropdownMedium: '中等',
dropdownSmall: '小型',
dropdownMini: '超小',
dropdown1: '首頁',
dropdown2: '個人中心',
dropdown3: '404',
@ -84,7 +45,6 @@ export default {
logOutConfirm: '確定',
logOutCancel: '取消',
logOutExit: '退出中',
logOutSuccess: '安全退出成功!',
},
tagsView: {
refresh: '重繪',
@ -92,6 +52,7 @@ export default {
closeOther: '關閉其它',
closeAll: '全部關閉',
fullscreen: '當前頁全屏',
closeFullscreen: '關閉全屏',
},
notFound: {
foundTitle: '地址輸入錯誤,請重新輸入地址~',
@ -106,16 +67,19 @@ export default {
layout: {
configTitle: '佈局配寘',
oneTitle: '全域主題',
twoTitle: '選單 / 頂欄',
twoTopTitle: '頂欄設定',
twoMenuTitle: '選單設定',
twoColumnsTitle: '分欄設定',
twoTopBar: '頂欄背景',
twoMenuBar: '選單背景',
twoColumnsMenuBar: '分欄選單背景',
twoTopBarColor: '頂欄默認字體顏色',
twoMenuBarColor: '選單默認字體顏色',
twoColumnsMenuBarColor: '分欄選單默認字體顏色',
twoIsTopBarColorGradual: '頂欄背景漸變',
twoMenuBar: '選單背景',
twoMenuBarColor: '選單默認字體顏色',
twoIsMenuBarColorGradual: '選單背景漸變',
twoIsMenuBarColorHighlight: '選單字體背景高亮',
twoColumnsMenuBar: '分欄選單背景',
twoColumnsMenuBarColor: '分欄選單默認字體顏色',
twoIsColumnsMenuBarColorGradual: '分欄選單背景漸變',
twoIsColumnsMenuHoverPreload: '分欄選單滑鼠懸停預加載',
threeTitle: '介面設定',
threeIsCollapse: '選單水准折疊',
threeIsUniqueOpened: '選單手風琴',
@ -131,6 +95,7 @@ export default {
fourIsTagsviewIcon: '開啟 Tagsview 圖標',
fourIsCacheTagsView: '開啟 TagsView 緩存',
fourIsSortableTagsView: '開啟 TagsView 拖拽',
fourIsShareTagsView: '開啟 TagsView 共用',
fourIsFooter: '開啟 Footer',
fourIsGrayscale: '灰色模式',
fourIsInvert: '色弱模式',
@ -147,18 +112,10 @@ export default {
sixClassic: '經典',
sixTransverse: '橫向',
sixColumns: '分欄',
tipText: '點擊下方按鈕,複製佈局配寘去`src/store/modules/themeConfig.js`中修改。',
tipText: '點擊下方按鈕,複製佈局配寘去`src/stores/themeConfig.ts`中修改。',
copyText: '一鍵複製配寘',
resetText: '一鍵恢復默認',
copyTextSuccess: '複製成功!',
copyTextError: '複製失敗!',
},
upgrade: {
title: '新版本陞級',
msg: '新版本來啦,馬上更新嘗鮮吧! 不用擔心,更新很快的哦!',
desc: '提示:更新會還原默認配寘',
btnOne: '殘忍拒絕',
btnTwo: '馬上更新',
btnTwoLoading: '更新中',
},
};

View File

@ -0,0 +1,13 @@
// 定义内容
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

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

View File

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

View File

@ -0,0 +1,29 @@
// 定义内容
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

@ -0,0 +1,28 @@
// 定义内容
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

@ -0,0 +1,28 @@
// 定义内容
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

@ -0,0 +1,165 @@
<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>

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