1
0
mirror of https://github.com/zclzone/vue-naive-admin.git synced 2025-12-29 04:20:22 +08:00

364 Commits

Author SHA1 Message Date
zclzone
921e0d18e9 mod: update settings.json 2023-10-07 12:57:37 +08:00
zclzone
3e259877c6 chore: update deps 2023-10-06 22:41:16 +08:00
zclzone
a431a2cf85 fix(other): loginInfo.username绑定错误
ISSUES CLOSED: #55
2023-10-06 22:39:39 +08:00
zclzone
c7c8691164 Merge branch 'main' of https://gitee.com/zclzone/vue-naive-admin 2023-09-26 22:53:50 +08:00
张传龙
1d246d1cd6 fix: 描述 2023-09-26 16:54:12 +08:00
张传龙
1854d2cec4 style: 移除不再需要的样式 2023-09-21 09:25:03 +08:00
zclzone
478d8fccd1 feat: 首页icon 2023-09-18 22:46:49 +08:00
zclzone
0accfe3a4a feat: 调整首页echarts图 2023-09-17 12:25:12 +08:00
zclzone
4bd50e2f9e refactor: 重构首页 2023-09-14 22:54:50 +08:00
zclzone
72857b3862 Merge branch 'main' of https://github.com/zclzone/vue-naive-admin 2023-09-09 17:51:19 +08:00
zclzone
a18ffce56c style: 修改图标 2023-09-09 17:50:55 +08:00
Ronnie Zhang
a2222c4dc0 docs: Update README.EN.md 2023-09-09 17:43:05 +08:00
Ronnie Zhang
a357ac36ee Update README.md 2023-09-09 17:42:11 +08:00
Ronnie Zhang
5e1acd7b8e Update README.md 2023-09-06 11:01:40 +08:00
Ronnie Zhang
8bbd04e668 docs: Update README.md 2023-09-06 11:00:59 +08:00
Ronnie Zhang
0c5c3df645 docs: Update README.md 2023-09-06 10:59:34 +08:00
Ronnie Zhang
ed577cd41a docs: Update README.md 2023-09-01 21:14:52 +08:00
Ronnie Zhang
456ad78dd2 Merge pull request #48 from xtazhangzp/main 2023-09-01 20:59:38 +08:00
v_zhangzp
ba20e7a96b fix: 修改时间方法Date.now() 2023-08-31 17:03:07 +08:00
张传龙
3ed1aafa80 chore: update deps 2023-08-28 11:08:06 +08:00
张传龙
5cf1212847 build: 锁定不能升级的依赖包 2023-08-28 10:51:32 +08:00
张传龙
81e0bb7b78 feat: md-editor暗黑主题调整 2023-08-25 10:52:04 +08:00
张传龙
f3125ddec8 chore: update deps 2023-08-25 10:42:18 +08:00
张传龙
3b3cb7ba34 docs: update readme 2023-08-17 18:50:46 +08:00
张传龙
d7c1063102 feat: 更新logo 2023-08-17 18:30:24 +08:00
张传龙
a5aa8a353f build: 新增一键启动脚本,F5直接启动项目 2023-08-15 10:49:44 +08:00
张传龙
cbb6ca4f6b chore: update deps 2023-08-10 16:12:40 +08:00
Ronnie Zhang
2ece015dae docs: update readme 2023-08-05 16:40:26 +08:00
zclzone
a80f83a011 mod: merge commit from github 2023-08-04 22:14:12 +08:00
zclzone
65d4d3848d fix(deps): 修复自定义指令引起的热更新失效问题
ISSUES CLOSED: #45
2023-08-04 22:07:20 +08:00
zclzone
53830256f4 fix: size type 2023-08-04 21:53:55 +08:00
张传龙
22c59c208f feat: add unocss preset rem to px 2023-08-03 17:20:21 +08:00
张传龙
b43f87035b chore: rollback vite-plugin-mock version 2023-07-28 01:10:01 +08:00
张传龙
20eee94630 chore: update unocss config 2023-07-28 00:56:14 +08:00
张传龙
c504ad065c chore: update deps 2023-07-27 14:25:07 +08:00
张传龙
0e764ac748 feat: add message notification 2023-07-21 16:01:35 +08:00
张传龙
8f15e1c655 fix: 多余的reloadPage 2023-07-13 14:46:41 +08:00
张传龙
d702a6703b fix: keepAlive 2023-07-11 15:07:37 +08:00
张传龙
d49af8b574 docs: update readme 2023-07-07 15:33:20 +08:00
张传龙
a036602d3e Merge branch 'main' of https://github.com/zclzone/vue-naive-admin 2023-07-07 15:09:04 +08:00
张传龙
eb173dc38e docs: update readme 2023-07-07 15:05:25 +08:00
zclzone
e5f1ee25c3 mod: update copyright 2023-07-01 10:41:25 +08:00
张传龙
fe11e18197 Merge branch 'main' of https://github.com/zclzone/vue-naive-admin 2023-06-27 17:18:24 +08:00
张传龙
0f9fb9f1c9 Merge branch 'main' of https://gitee.com/zclzone/vue-naive-admin 2023-06-27 17:16:56 +08:00
张传龙
50f96b99c7 style: sort package.json 2023-06-27 17:16:45 +08:00
Ronnie Zhang
2eb936bcac Merge pull request #43 from mizhexiaoxiao/main 2023-06-26 20:46:00 +08:00
mizhexiaoxiao
b50731881c fix: 在中文输入法输入字母按enter键会触发查询 2023-06-26 10:46:43 +08:00
zclzone
d4a5cffd81 mod: update copyright 2023-06-23 18:45:58 +08:00
zclzone
70eab22f65 style: 修改注释 2023-06-21 22:06:40 +08:00
zclzone
0247f3ebfa refactor: 简化proxy配置 2023-06-21 21:53:33 +08:00
张传龙
a610c1c6d0 docs: update readme 2023-06-19 09:06:15 +08:00
Ronnie Zhang
5a9c0fb584 Merge pull request #41 from szluyu99/main 2023-06-19 09:01:24 +08:00
szluyu99
cad72b3b73 docs: README 新增使用该项目的开源项目 2023-06-18 21:56:09 +08:00
zclzone
9edd0e5ad6 style: 暗黑模式细节 2023-06-17 10:43:03 +08:00
zclzone
ae6db3ed3c feat: tag标签增加图标展示 2023-06-17 10:42:33 +08:00
张传龙
dcab55055c fix(utils): 修复\\$message后于接口请求定义问题
ISSUES CLOSED: #39
2023-06-15 10:26:03 +08:00
张传龙
5aa4d3d5ae Merge branch 'main' of https://gitee.com/zclzone/vue-naive-admin 2023-06-03 16:45:11 +08:00
张传龙
eea9fc79f7 style: lint fix 2023-06-03 16:38:45 +08:00
张传龙
526792e22f chore: update .prettierrc 2023-06-03 16:34:50 +08:00
张传龙
855202962c chore: update path-intellisense settings 2023-06-03 16:34:01 +08:00
zclzone
74a58fafb9 docs: update README 2023-05-24 21:18:18 +08:00
zclzone
ad451dae1e mod: merge 2023-05-13 23:12:49 +08:00
zclzone
35ed004b2e chore: 移除plugin unplugin-vue-define-options(vue官方已支持) 2023-05-13 17:25:44 +08:00
zclzone
fcdd31c935 chore: update deps 2023-05-13 17:24:43 +08:00
张传龙
386d9ec27a style: lint fix 2023-05-08 14:15:03 +08:00
张传龙
e84dd01365 feat: add @unocss/eslint-config 2023-05-08 14:13:59 +08:00
zclzone
6da56ec881 Merge branch 'main' of https://github.com/zclzone/vue-naive-admin 2023-05-07 22:25:09 +08:00
zclzone
4e9e3469b0 chore: update deps 2023-05-07 22:24:46 +08:00
zclzone
3b86597eff style: lint fix 2023-05-07 22:20:57 +08:00
zclzone
ebffe52c7c style: lint fix 2023-05-07 22:05:13 +08:00
zclzone
6d863e1a63 chore: eslint -> @zclzone/eslint-config 2023-05-07 22:00:25 +08:00
Ronnie Zhang
b2e1c2d22c Merge pull request #35 from liaocp666/patch-1
docs: 修正文档中的错别字
2023-05-03 20:25:42 +08:00
Kent Liao
2e283c3b19 修正错别字 2023-04-28 10:58:00 +08:00
zclzone
6300758fd0 fix: 更新失效图片链接 2023-04-23 22:35:33 +08:00
zclzone
b5717f6b8d build: gh-pages -> vercel 2023-04-23 22:18:00 +08:00
张传龙
ff11cdf73f build: remove vercel env 2023-04-23 14:46:12 +08:00
张传龙
9a01e42915 build: add vercel.json 2023-04-23 14:38:50 +08:00
zclzone
7c74578afc build: 触发构建 2023-04-20 21:56:53 +08:00
zclzone
a6a8002c59 build: 触发构建 2023-04-20 21:21:12 +08:00
zclzone
daf747348e chore: 添加vercel环境 2023-04-20 20:56:21 +08:00
张传龙
60b5ce1817 fix: redirect 2023-04-17 14:14:10 +08:00
张传龙
4c75be67f2 feat: 图片上传 2023-04-17 13:36:14 +08:00
张传龙
be1c875a72 Merge branch 'main' of https://gitee.com/zclzone/vue-naive-admin 2023-04-17 13:24:48 +08:00
张传龙
329a6e29cb chore: update jsconfig.json 2023-04-17 13:18:44 +08:00
zclzone
af907932fb perf: 优化ScrollX 2023-04-16 21:41:27 +08:00
张传龙
681b3144d1 feat: 增加表格导出功能 2023-04-13 12:13:32 +08:00
张传龙
8304970a59 chore: update deps 2023-04-13 08:20:45 +08:00
zclzone
5cef8e4a01 fix: 暗黑模式细节 2023-03-12 21:25:14 +08:00
张传龙
0b50e1dbee Merge branch 'main' of https://github.com/zclzone/vue-naive-admin 2023-03-08 11:01:00 +08:00
张传龙
88a93c4e57 chore: update deps 2023-03-08 10:58:08 +08:00
zclzone
38ae35ee95 fix: 文件名称 2023-02-12 12:26:47 +08:00
zclzone
c7471a66db docs: 默认中文README 2023-02-12 12:25:49 +08:00
zclzone
a4531be904 fix: 修复暗色模式下部分显示问题 2023-02-12 12:10:26 +08:00
张传龙
c58605de54 refactor: 重构暗黑模式 2023-01-30 15:37:10 +08:00
zclzone
c3dc0b4b2c chore: update deps 2022-12-31 18:17:36 +08:00
zclzone
2d3e9988ec chore: update deps 2022-12-31 18:07:05 +08:00
zclzone
9548a0bfc8 feat: 集成简易暗黑模式 2022-12-18 19:19:42 +08:00
张传龙
dda778fdde mod: update 2022-12-12 09:32:50 +08:00
张传龙
98e3f13185 fix: 示例组件name 2022-12-09 14:17:21 +08:00
张传龙
7e79c51630 feat: 全局中文设定 2022-12-08 15:08:27 +08:00
zclzone
181aed4897 chore: update deps 2022-11-28 12:13:02 +08:00
zclzone
c626d2b785 feat: 当前标签页始终显示在视野内 2022-11-27 20:04:11 +08:00
zclzone
ed79e81b13 perf: 切换tab时自动展开对应的菜单 2022-11-25 09:16:01 +08:00
zclzone
264119a142 fix: $message 2022-11-24 21:05:42 +08:00
zclzone
4c1c77821f feat: 增加多级菜单示例 2022-11-24 18:16:37 +08:00
zclzone
67b11f04fc fix: fullPath 2022-11-14 18:29:41 +08:00
zclzone
649fe1d4e8 fix: route path 2022-11-12 19:09:24 +08:00
张传龙
c3192423c6 mod: remove 2022-10-31 15:23:40 +08:00
张传龙
911fc74305 chore: update deps 2022-10-31 14:09:07 +08:00
张传龙
2fcfd6b4d1 docs: add docs 2022-10-28 15:03:04 +08:00
张传龙
bac868d071 docs: add docs 2022-10-27 17:15:34 +08:00
张传龙
ea5460488a docs: add docs 2022-10-26 17:23:08 +08:00
张传龙
61d42ead21 chore: update deps 2022-10-21 17:39:01 +08:00
张传龙
a98555beb1 chore: update deps 2022-10-19 17:59:09 +08:00
张传龙
820eb516ce docs: update readme 2022-10-16 16:46:56 +08:00
张传龙
6cd0dc1eff chore: update deps 2022-10-09 17:56:23 +08:00
张传龙
5dcb2958a1 chore: update deps 2022-10-04 17:07:34 +08:00
张传龙
c25476278b fix: rich text editor keep alive 2022-09-29 16:53:54 +08:00
张传龙
99ddb4fe70 feat: rich text editor 2022-09-29 08:55:26 +08:00
张传龙
82c47ffc72 chore: update deps 2022-09-28 16:44:01 +08:00
张传龙
1f1678800f style: md editor 2022-09-28 16:43:44 +08:00
张传龙
efdd89cd50 feat: back top 2022-09-26 17:40:41 +08:00
张传龙
100b91a118 style: reset.css 2022-09-25 17:14:01 +08:00
张传龙
26b71f0ec6 perf: unocss demo 2022-09-25 17:13:35 +08:00
张传龙
92e7ada37b fix: toLogin 2022-09-24 15:40:34 +08:00
张传龙
2d879d0592 refactor: http interceptors 2022-09-24 15:27:26 +08:00
张传龙
8806a6cb43 fix: keep-alive key 2022-09-24 14:44:59 +08:00
张传龙
85a04fd06d fix: logout 2022-09-21 17:14:41 +08:00
张传龙
4a5b8dd005 fix: fix 2022-09-21 17:13:39 +08:00
张传龙
2f7da255e5 style: naive theme 2022-09-18 20:18:55 +08:00
张传龙
6664ae8f7b refactor: folders 2022-09-18 20:05:40 +08:00
张传龙
bdbe9b8483 refactor: dynamic routes 2022-09-18 15:10:22 +08:00
张传龙
30211e14ea chore: update deps 2022-09-18 15:08:06 +08:00
张传龙
e7b1896d9e chore: update deps 2022-09-15 21:44:58 +08:00
张传龙
a5c1046e67 mod: remove 2022-09-14 09:14:37 +08:00
张传龙
31670cd671 mod: base demo 2022-09-11 17:08:36 +08:00
张传龙
b0e3a94e12 style: remove extra code 2022-09-10 16:00:39 +08:00
张传龙
2b2a324a62 refactor: simplify noNeedToken judge 2022-09-10 15:56:53 +08:00
张传龙
40483e09e6 refactor: keep alive 2022-09-09 09:53:49 +08:00
张传龙
a5a3472486 refactor: setupExtend replace with defineOptions 2022-09-08 15:08:28 +08:00
张传龙
fd1752693a chore: update deps 2022-09-08 15:06:23 +08:00
张传龙
2f3a83758a mod: reload page 2022-09-08 09:00:28 +08:00
张传龙
738212c84b Merge branch 'main' of https://github.com/zclzone/vue-naive-admin 2022-09-07 16:13:50 +08:00
张传龙
a4f3e16007 style: update icon and title 2022-09-07 16:12:38 +08:00
Ronnie Zhang
5b2d1c68dd Merge pull request #26 from haichao0817/main
style: change 'loging' to 'loading'
2022-09-07 14:56:17 +08:00
wukang
7b8b50322c change 'loging' to 'loading' 2022-09-07 11:01:02 +08:00
张传龙
bb171866b6 refactor: routes and file 2022-09-04 12:18:47 +08:00
张传龙
f1bc9edbac refactor: request error tip 2022-09-04 11:35:30 +08:00
张传龙
3a38adc71e fix: mock api error 2022-09-03 22:43:57 +08:00
Ronnie Zhang
b760cc34dd Merge pull request #25 from zclzone/feature/crud-table
Feature/crud table
2022-09-03 22:34:49 +08:00
张传龙
b59e47b5dd feat: finish curd table 2022-09-03 22:28:37 +08:00
张传龙
d1dd58215d wip: crud table 2022-09-03 17:33:20 +08:00
张传龙
661aed1a94 style: add annotation 2022-09-03 17:32:30 +08:00
张传龙
f2e2fc6819 wip: crud table 2022-09-01 14:53:18 +08:00
张传龙
9ea8ffd7fd wip: crud table 2022-08-31 10:16:38 +08:00
张传龙
af983d16b9 wip: commonPage 2022-08-29 10:08:18 +08:00
张传龙
079761b6fd feat: add page components 2022-08-28 19:37:23 +08:00
张传龙
841bab0d63 release: release v1.0.0 2022-08-27 14:51:08 +08:00
张传龙
453148fc8d build: update commitlint 2022-08-27 14:50:34 +08:00
张传龙
7ec078bd7a Revert "mod: gh-pages gzip"
This reverts commit dd0bc3e6e8.
2022-08-27 14:47:18 +08:00
张传龙
dd0bc3e6e8 mod: gh-pages gzip 2022-08-27 14:36:21 +08:00
张传龙
8c665c727b style: format 2022-08-27 14:26:14 +08:00
张传龙
da98aa1c7d docs: update readme 2022-08-27 14:23:12 +08:00
张传龙
51b47ea722 chore: upgrade to vite3 2022-08-27 14:22:09 +08:00
张传龙
220a7800f7 refactor: refactor 2022-08-27 14:09:32 +08:00
张传龙
230e3a72d9 chore: update deps 2022-08-27 12:03:58 +08:00
张传龙
0cefadc2a5 refactor: custom icon 2022-08-27 11:46:34 +08:00
张传龙
2f1b747243 feat: add compress plugin 2022-08-27 11:04:07 +08:00
张传龙
296d5ea6f0 perf: png replace with webp 2022-08-27 10:58:22 +08:00
张传龙
3a415703d4 perf: table demo 2022-08-27 10:36:07 +08:00
张传龙
006f730457 perf: table demo 2022-08-26 22:48:03 +08:00
张传龙
606c5a2df0 docs: update readme 2022-08-25 18:26:29 +08:00
张传龙
30c375cc1d fix(components): fix tags contenxtmenu
ISSUES CLOSED: #23
2022-08-18 10:56:28 +08:00
张传龙
ddf14053da chore: update deps 2022-08-18 09:42:08 +08:00
张传龙
38edbcb68a mod: update mock data 2022-08-18 09:39:09 +08:00
Ronnie Zhang
3e54a82abb Merge pull request #22 from amplest/main
fix: get请求无法接收参数
2022-08-12 22:59:43 +08:00
Xiongxing
df6225a752 fix: get请求无法接收参数 2022-08-12 21:10:39 +08:00
张传龙
63c1f2f132 refactor: routes sort 2022-08-08 15:44:16 +08:00
张传龙
0bb2a904e7 refactor: adjust routes 2022-08-08 15:36:43 +08:00
张传龙
ef3aaa5be5 refactor: refactor async routes 2022-08-07 22:25:28 +08:00
张传龙
869a68812c chore: adjust commitlink config 2022-08-04 18:04:08 +08:00
张传龙
fd0032e0e9 docs: update readme 2022-08-02 09:34:25 +08:00
张传龙
b53d7daaa1 docs: update readme 2022-08-02 09:31:17 +08:00
张传龙
856bdfd0ee docs: update readme 2022-07-31 18:08:13 +08:00
张传龙
9f9884759c refactor: simplify permission-guard 2022-07-30 22:11:53 +08:00
张传龙
7dad43d003 docs: update readme 2022-07-29 16:48:58 +08:00
张传龙
7762e02b31 refactor: refactor api usage 2022-07-25 18:36:22 +08:00
张传龙
e5768fa1e3 style: modify login page 2022-07-23 22:17:23 +08:00
张传龙
7ee613d8cf style: modify footer 2022-07-23 22:10:01 +08:00
张传龙
80a5b7f053 style: modify custom scrollbar 2022-07-21 17:47:11 +08:00
张传龙
eb160731da feat: login page compatible mobile 2022-07-20 18:26:38 +08:00
张传龙
789231a7f4 style: format 2022-07-20 09:13:07 +08:00
张传龙
6ea6e1c267 chore: update settings.json 2022-07-20 09:10:40 +08:00
张传龙
d971e7e4ba fix: fix incorrent judgment of isHash 2022-07-19 16:29:07 +08:00
张传龙
215998dc66 perf: optimize login page 2022-07-17 20:37:44 +08:00
张传龙
40f9ac1a6b fix: fix incorrent usage of vue router 2022-07-17 14:54:45 +08:00
张传龙
6ec5588ed4 chore: setup lint-staged 2022-07-15 14:48:40 +08:00
张传龙
380e5768c4 chore: update settings.json 2022-07-15 14:06:57 +08:00
张传龙
5856f601fa style: use unocss rewrite css 2022-07-14 18:05:47 +08:00
张传龙
d10b8f0e96 chore(prettier): update prettier config 2022-07-14 18:04:00 +08:00
张传龙
3860cf9ebb style: simplify unocss test page 2022-07-14 16:40:25 +08:00
张传龙
94b46d9bf6 chore(unocss): update unocss config 2022-07-14 16:39:24 +08:00
张传龙
4df7d44bf1 docs: modify annotation 2022-07-13 22:58:09 +08:00
张传龙
42b8aca37b docs(readme): update readme 2022-07-11 16:07:07 +08:00
张传龙
0c96d0e937 docs(readme): update readme 2022-07-11 12:29:48 +08:00
张传龙
b540f5599f fix: fix incorrect text 2022-07-11 12:28:33 +08:00
张传龙
18b8a81640 fix(other): disabled unplugin generate .d.ts 2022-07-10 22:24:23 +08:00
张传龙
06b3afc2de build(deps): update unplugin deps 2022-07-10 22:20:21 +08:00
92376
3088773ebe fix: modify exit full screen icon
* !1 退出全屏 icon
* 退出全屏 icon
2022-07-10 12:55:45 +00:00
张传龙
83b42bf6b8 chore(projects): add husky and commitlint 2022-07-10 14:02:02 +08:00
张传龙
fd08d25ccf perf: optimize ScrooX component. 2022-07-09 15:03:39 +08:00
张传龙
76c3f0b64c perf: optimize ScrooX component. 2022-07-09 14:38:38 +08:00
张传龙
a1db8273f5 chore: update unocss dependencies. 2022-07-08 22:35:55 +08:00
张传龙
f5ab04112f chore: update settings.json 2022-07-08 17:43:57 +08:00
张传龙
805b2e066f docs: update readme 2022-07-06 10:10:23 +08:00
Ronnie Zhang
6979b245a9 Merge pull request #19 from sean3112/main
微调一下demo
2022-07-05 20:15:27 +08:00
Sean Huang
dff8862c75 feat: Add response code 400.
fix: Change the parameter naming of the get method to params.
2022-07-05 18:35:05 +08:00
Sean Huang
1da5e8d573 Merge remote-tracking branch 'origin/main' 2022-07-05 18:12:01 +08:00
张传龙
7f97dd2f5a style: update prettier format rules 2022-07-03 14:52:49 +08:00
Sean Huang
1f69f07100 Merge remote-tracking branch 'origin/main' 2022-07-03 00:15:38 +08:00
张传龙
f97beeb54b perf: add remember me feature 2022-07-02 00:03:34 +08:00
张传龙
57bc68e7b0 refactor: simplify wrapper storage 2022-07-01 23:27:05 +08:00
Ronnie Zhang
90aa54d4a4 Merge pull request #18 from sean3112/patch-1
Breakpoints issues, depends on 'vite-plugin-vue-setup-extend-plus' instead of 'vite-plugin-vue-setup-extend'
2022-07-01 15:24:32 +08:00
Sean Huang
7564f115d6 Breakpoints issues
Solved the problem that the breakpoint is not in the source code location when debugging.
2022-07-01 15:07:01 +08:00
Sean Huang
8d3753a80e Update package.json
Breakpoints are not in the source code location during debugging
2022-07-01 12:58:43 +08:00
Sean Huang
a816028560 调试时,断点不在源码位置处,更新插件依赖vite-plugin-vue-setup-extend为vite-plugin-vue-setup-extend-plus即可。 2022-07-01 12:38:38 +08:00
张传龙
acde2c1004 feat: Breadcrumb add Icon 2022-06-30 18:29:26 +08:00
张传龙
cb5dd34e17 refactor: simplify mock setup 2022-06-26 18:42:07 +08:00
张传龙
73c82520ca mod: use unocss rewrite the demo page 2022-06-26 18:25:14 +08:00
张传龙
e465ee50bf mod: use unocss rewrite the 404 page 2022-06-26 15:39:44 +08:00
张传龙
2be3f095aa mod: delete extra code 2022-06-26 15:37:57 +08:00
张传龙
26ecafffdc docs: update readme 2022-06-26 15:26:52 +08:00
张传龙
7150d93394 docs: update readme 2022-06-26 15:09:00 +08:00
张传龙
6e26769679 docs: update readme 2022-06-25 14:45:50 +08:00
张传龙
b7ce7912a7 revert: 简化构建步骤,撤销app.config.js功能 2022-06-25 14:45:23 +08:00
张传龙
1fa9d4d472 fix: 修复common文件夹大小写问题 2022-06-25 13:48:54 +08:00
张传龙
fa11b1bc64 fix: 修复common文件夹大小写问题 2022-06-25 13:47:30 +08:00
张传龙
4bf8916fdc style: update settings.json 2022-06-24 15:12:16 +08:00
张传龙
8496c08646 docs: update readme 2022-06-23 19:30:22 +08:00
张传龙
065868a40b fix: 修复高度溢出问题 2022-06-22 20:36:53 +08:00
张传龙
dd4cd871ba style: 样式调整 2022-06-19 19:41:49 +08:00
张传龙
a2b84d35f7 mod: 修改文件夹名大小写 2022-06-19 19:18:12 +08:00
张传龙
e128dfabc7 refactor: Refactor Naive UI AppProvider 2022-06-19 17:11:38 +08:00
张传龙
21c1d6d3aa feat: 添加白屏loading效果 2022-06-19 16:27:44 +08:00
张传龙
3990d4da80 feat: 集成unplugin-auto-import自动引入 2022-06-19 15:22:01 +08:00
张传龙
ac9ccbadf0 mod: 使用unocss重写首页样式 2022-06-19 15:02:58 +08:00
张传龙
8ae4046285 mod: 修改友好提示 2022-06-19 14:50:50 +08:00
张传龙
f88b4f52a1 refactor: 重构图标使用方式,集成自定应图标 2022-06-19 13:35:36 +08:00
张传龙
00ba77c15e chore: 依赖更新 2022-06-18 22:07:50 +08:00
张传龙
39a80926bf perf: 全局样式调整 2022-06-18 22:00:58 +08:00
张传龙
16957a96b7 mod: 使用unocss重写登录页 2022-06-18 19:14:50 +08:00
张传龙
ef33b28492 mod: 细节调整 2022-06-17 23:05:57 +08:00
张传龙
ae43ffb94f refactor: 重构异常处理 2022-06-17 22:44:32 +08:00
张传龙
f0b6ce7d20 chore: update unocss.config.js 2022-06-17 17:48:25 +08:00
张传龙
c3354afa6c docs: update readme 2022-06-15 12:50:49 +08:00
张传龙
08ef914528 chore: update settings.json 2022-06-12 11:37:33 +08:00
张传龙
d86ee26ad6 chore: update naive ui dependencies. 2022-06-11 22:15:15 +08:00
张传龙
c8495f7a5f style: 主题相关调整 2022-06-11 22:14:23 +08:00
张传龙
0636ac4716 perf: 优化多标签滚动 2022-06-11 20:17:30 +08:00
张传龙
67d966e096 refactor: 简化unocss集成 2022-06-11 16:55:36 +08:00
张传龙
b5ac614943 docs: update readme 2022-06-05 18:15:19 +08:00
张传龙
9151b2d297 feat: 菜单自定义排序 2022-06-03 22:42:33 +08:00
张传龙
84f8431134 mod: 删除非必要代码 2022-06-03 19:50:10 +08:00
张传龙
fdc49f6dcc refactor: 重构axios封装 2022-06-03 19:49:38 +08:00
张传龙
d2b88a8300 mod: 调整import顺序 2022-05-25 19:53:57 +08:00
张传龙
ffc042167a fear: 集成Naive UI Notification组件 2022-05-24 15:13:11 +08:00
张传龙
85f9c91d6e mod: 移除非必要代码 2022-05-23 18:05:18 +08:00
张传龙
21391b202f feat: 头部增加github源码网站 2022-05-22 20:50:32 +08:00
张传龙
36ddb23db6 docs: update README 2022-05-21 17:22:35 +08:00
张传龙
f3c391c031 fix: 修改配置路由写法以修复热更新问题 2022-05-20 18:33:13 +08:00
张传龙
df378f784b chore: 依赖更新 2022-05-20 18:31:21 +08:00
张传龙
2154267615 mod: 修改mock数据 2022-05-19 15:52:22 +08:00
张传龙
3203a9a459 chore: update vite.config.js 2022-05-19 15:40:02 +08:00
张传龙
5ce2150706 fix: 修复vite热重启后proxy失效问题 2022-05-19 12:02:01 +08:00
张传龙
5bd380037c mod: 示例代码修改 2022-05-18 20:40:31 +08:00
张传龙
e63e9f5cf2 chore: update settings.json 2022-05-17 11:01:23 +08:00
张传龙
74c244cf37 chore: update extensions 2022-05-17 11:01:12 +08:00
张传龙
5cd85cf72d mod: 修改工具方法 2022-05-16 18:29:32 +08:00
张传龙
96d88a97f1 chore: 依赖更新 2022-05-16 18:27:56 +08:00
张传龙
00c32a950a chore: 依赖更新 2022-05-15 18:51:57 +08:00
张传龙
bfd048d40a mod: 调整示例页面 2022-05-13 15:09:41 +08:00
张传龙
1254a199d7 refactor: 重构sidebar 2022-05-13 14:52:11 +08:00
张传龙
1190d08a87 refactor: 规范文件夹结构 2022-05-12 19:50:21 +08:00
张传龙
958589edd0 refactor: 重构header 2022-05-11 14:54:12 +08:00
张传龙
2338ded165 feat: 集成全屏功能 2022-05-10 17:18:04 +08:00
张传龙
4d6a58bfc8 feat: 集成vueUse 2022-05-10 17:17:49 +08:00
张传龙
f15e21b0a0 feat: 集成菜单栏伸缩功能 2022-05-08 11:59:01 +08:00
张传龙
f88820b727 style: 调整404页面 2022-05-07 23:01:30 +08:00
张传龙
598d256be4 style: lint fix 2022-05-06 22:36:22 +08:00
张传龙
5b51cfb4f1 chore: update extensions 2022-05-06 22:35:33 +08:00
张传龙
45c2e3aebe chore: 简化eslint配置 2022-05-06 22:34:55 +08:00
张传龙
c2249d531f chore: 依赖更新 2022-05-05 21:13:45 +08:00
张传龙
44c6b420d0 chore: 依赖更新 2022-05-04 19:49:55 +08:00
张传龙
117a46a251 chore: update extensions 2022-05-04 18:03:41 +08:00
张传龙
d8569a4eb1 refactor: 简化文件夹结构 2022-05-03 22:37:51 +08:00
张传龙
c268b3c75d mod: 修改table示例的写法 2022-05-03 22:28:51 +08:00
张传龙
21e0d86fcd refactor: app-main 弃用naive ui虚拟滚动条,改成自定义滚动条 2022-05-02 17:39:31 +08:00
张传龙
76bd414941 mod: 调整多标签滑动写法 2022-05-01 18:16:41 +08:00
张传龙
894b87426a fix: 修复标签栏溢出换行问题 2022-04-30 18:39:42 +08:00
张传龙
f9c2362cd8 mod: 规范化调整.vue文件命名 2022-04-29 20:40:29 +08:00
张传龙
d922dcc224 docs: update readme 2022-04-26 19:59:59 +08:00
张传龙
7c8a17bbb2 fix: 修复递归判断单个子路由错误问题 2022-04-25 09:56:23 +08:00
张传龙
321e19a3a5 fix: 修复isHidden路由未正确判断问题 2022-04-25 09:51:02 +08:00
张传龙
bf2d45416f feat: 单个菜单时替换父菜单处理 2022-04-23 22:34:12 +08:00
张传龙
cf1b83d3f1 feat: 集成多标签右键菜单 2022-04-23 19:23:12 +08:00
张传龙
bf63fb5ab7 mod: 修改侧边菜单渲染菜单使用封装的renderIcon方法 2022-04-23 19:10:55 +08:00
张传龙
a6f86ee315 feat: 封装renderIcon方法 2022-04-23 19:09:06 +08:00
张传龙
b2cf78b36d mod: 删除测试代码 2022-04-22 08:58:43 +08:00
张传龙
c9c0c35343 revert: 撤销router-view页面组件多根节点支持 2022-04-21 22:45:10 +08:00
张传龙
3c46d2c159 feat: 集成重新加载页面功能 2022-04-21 22:35:26 +08:00
张传龙
585bf4a4c4 fix: 修复router-view页面组件因transition导致使用多个根节点告警问题 2022-04-20 18:03:23 +08:00
张传龙
2bd85e6e60 feat: 集成rollup打包分析插件 2022-04-19 21:50:19 +08:00
张传龙
967ae1c483 style: 调整layout布局 2022-04-18 22:01:30 +08:00
张传龙
238bceb500 fix: 修复http拦截强制覆盖Authorization问题 2022-04-17 21:16:30 +08:00
张传龙
c2145c0ddb mod: 修改路由name 2022-04-17 16:33:12 +08:00
张传龙
d759c9b9ae docs: update readme 2022-04-16 22:54:03 +08:00
张传龙
3fdba613d3 docs: update readme 2022-04-15 22:19:09 +08:00
张传龙
40d5106c6b release: v0.3.2 2022-04-15 22:16:10 +08:00
张传龙
094a9dcb3b fix: 修复二次登录后标签页会多出登录标签问题 2022-04-14 15:30:34 +08:00
张传龙
db5089d92e chore: update jsconfig.json 2022-04-13 17:24:27 +08:00
张传龙
a41ccad2d0 style: 修改naive ui主题颜色配置 2022-04-12 17:18:21 +08:00
张传龙
8973e39566 feat: 多标签增加sessionStorage缓存 2022-04-11 22:13:17 +08:00
张传龙
8c1191ece2 mod: 文件名模块修改 2022-04-11 22:12:00 +08:00
张传龙
b3aa8147b1 refactor: 重构主题色配置,多标签配置化处理 2022-04-10 23:20:28 +08:00
张传龙
0d240f083a feat: 集成tags多标签功能 2022-04-10 21:41:06 +08:00
张传龙
c180cf54a8 Merge branch 'main' of https://github.com/zclzone/vue-naive-admin 2022-04-10 14:08:03 +08:00
张传龙
2541706ac3 docs: update readme 2022-04-10 14:06:06 +08:00
Ronnie Zhang
44b935e8f6 Create LICENSE 2022-04-10 12:57:19 +08:00
张传龙
ea1ce0601a ci: 修改 github actions 配置文件 2022-04-10 00:03:28 +08:00
张传龙
2989ecf126 ci: 修改 github actions 配置文件 2022-04-09 23:43:38 +08:00
张传龙
ce94bf38d1 docs: update readme 2022-04-09 23:38:43 +08:00
张传龙
13bc185926 ci: 修改 github actions 配置文件 2022-04-09 23:36:19 +08:00
张传龙
51cfd3e2eb ci: 修改 github actions 配置文件 2022-04-09 23:29:54 +08:00
张传龙
c22cb3b35c ci: 修改 github actions 配置文件 2022-04-09 23:14:02 +08:00
张传龙
621a2304e7 ci: 修改 github actions 配置文件 2022-04-09 23:03:09 +08:00
张传龙
729337cdc5 ci: 修改 github actions 配置文件 2022-04-09 22:59:05 +08:00
张传龙
4ef58b612f ci: 修改 github actions 配置文件 2022-04-09 22:43:58 +08:00
张传龙
ba5d32244f ci: 修改 github actions 配置文件 2022-04-09 22:42:25 +08:00
张传龙
efc2a194a3 ci: 修改 github actions 配置文件 2022-04-09 22:37:44 +08:00
张传龙
6160c2e664 chore: 修改github actions配置文件 2022-04-09 22:33:13 +08:00
张传龙
fbd1e9a38a ci: 集成github actions自动发布github pages 2022-04-09 22:30:21 +08:00
张传龙
17928cbc57 chore: 集成github pages发布环境 2022-04-09 22:29:11 +08:00
张传龙
e7fc403c77 style: 删除无用注释代码 2022-04-09 19:32:15 +08:00
张传龙
3f7ed95fdb style: 增加vscode插件推荐列表 2022-04-09 19:05:58 +08:00
张传龙
7478f193f9 Merge branch 'main' of https://github.com/zclzone/vue-naive-admin 2022-04-08 19:39:35 +08:00
张传龙
ba49d94bf4 docs: 完善插件使用注释 2022-04-08 19:35:22 +08:00
Ronnie Zhang
ea9851ccd3 Merge pull request #7 from liulinboyi/fix/dialog
fix: $dialog对话框可以异步
2022-04-08 18:34:23 +08:00
张传龙
ec55f33655 feat: 配合unplugin-icons集成iconify图标解决方案 2022-04-08 17:21:48 +08:00
liulinboyi
7a85c714cb fix: $dialog对话框可以异步 2022-04-08 16:32:33 +08:00
张传龙
7b90d7f8de style: 修改html代码风格 2022-04-08 11:25:11 +08:00
张传龙
16f580c96d mod: 修改外链中文档地址 2022-04-05 17:40:32 +08:00
张传龙
f1329a46e4 docs: update readme 2022-04-05 17:35:05 +08:00
张传龙
ef6df57dc5 style: 修改404图片 2022-04-05 11:33:35 +08:00
张传龙
95e5cd7134 style: 修改favicon 2022-04-05 11:33:18 +08:00
张传龙
9db7aa50a1 fix: 修复更新Naive UI版本后菜单高亮样式失效问题 2022-04-05 00:37:30 +08:00
张传龙
a9997984d5 refactor: 重构登录页UI 2022-04-05 00:36:24 +08:00
张传龙
acb47a17b4 refactor: 规范化调整.vue文件结构及命名 2022-04-03 19:45:39 +08:00
张传龙
9c5f4eaa3d chore: 依赖更新 2022-04-02 09:39:16 +08:00
张传龙
361fb52345 mod: 修改配置,打包默认不生成CNAME文件 2022-04-01 17:59:04 +08:00
张传龙
5993e8d7d0 refactor: 全局规范化调整文件夹和文件命名(代码无改动) 2022-04-01 17:41:07 +08:00
张传龙
8648f16ed8 mod: remove router.isReady 2022-04-01 16:39:26 +08:00
张传龙
33aaadba60 fix: 修复挂载路由时使用$loadingBar出错问题 2022-03-29 09:28:36 +08:00
张传龙
437d87f19e mod: 调整setupApp相关写法 2022-03-28 18:31:32 +08:00
张传龙
dfcc8c2158 mod: 修改失效图片链接 2022-03-26 17:50:29 +08:00
182 changed files with 10319 additions and 3791 deletions

45
.cz-config.js Normal file
View File

@@ -0,0 +1,45 @@
module.exports = {
types: [
{ value: 'feat', name:'feat: 新增功能' },
{ value: 'fix', name:'fix: 修复bug' },
{ value: 'docs', name:'docs: 文档变更' },
{ value: 'style', name:'style: 代码格式(不影响功能,例如空格、分号等格式修正)' },
{ value: 'refactor', name:'refactor: 代码重构(不包括 bug 修复、功能新增)' },
{ value: 'perf', name:'perf: 性能优化' },
{ value: 'test', name:'test: 添加、修改测试用例' },
{ value: 'build', name:'build: 构建流程、外部依赖变更(如升级 npm 包、修改 脚手架 配置等)' },
{ value: 'ci', name:'ci: 修改 CI 配置、脚本' },
{ value: 'chore', name:'chore: 对构建过程或辅助工具和库的更改(不影响源文件、测试用例)' },
{ value: 'revert', name:'revert: 回滚 commit' },
{ value: 'wip', name:'wip: 开发中' },
{ value: 'mod', name:'mod: 不确定分类的修改' },
{ value: 'release', name:'release: 发布' },
],
scopes: [
['custom', '自定义'],
['projects', '项目搭建'],
['components', '组件相关'],
['utils', 'utils 相关'],
['styles', '样式相关'],
['deps', '项目依赖'],
['other', '其他修改'],
].map(([value, description]) => {
return {
value,
name: `${value.padEnd(30)} (${description})`
}
}),
messages: {
type: '确保本次提交遵循 Angular 规范!选择你要提交的类型:\n',
scope: '选择一个 scope可选',
customScope: '请输入自定义的 scope',
subject: '填写简短精炼的变更描述:',
body: '填写更加详细的变更描述(可选)。使用 "|" 换行:',
breaking: '列举非兼容性重大的变更(可选):',
footer: '列举出所有变更的 Issues Closed可选。 例如: #31, #34',
confirmCommit: '确认提交?'
},
allowBreakingChanges: ['feat', 'fix'],
subjectLimit: 100,
breaklineChar: '|'
}

5
.env
View File

@@ -1,6 +1,3 @@
VITE_APP_TITLE = 'Vue Naive Admin'
VITE_TITLE = 'Vue Naive Admin'
VITE_PORT = 3100
# 打包时自动生成CNAME文件用于配置github pages自定义域名如不需要可注释或者直接删除
VITE_APP_GLOB_CNAME = 'template.qszone.com'

View File

@@ -2,13 +2,10 @@
VITE_PUBLIC_PATH = '/'
# 是否启用MOCK
VITE_APP_USE_MOCK = true
VITE_USE_MOCK = true
# proxy
VITE_PROXY = [["/api","http://localhost:8080"],["/api-test","localhost:8080"]]
# 是否启用MOCK
VITE_USE_PROXY = true
# base api
VITE_APP_GLOB_BASE_API = '/api'
# test base api
VITE_APP_GLOB_BASE_API_TEST = '/api-test'
VITE_BASE_API = '/api'

13
.env.github Normal file
View File

@@ -0,0 +1,13 @@
# 自定义域名CNAME
# VITE_CNAME = 'template.qszone.com'
# 资源公共路径,需要以 /开头和结尾
VITE_PUBLIC_PATH = '/vue-naive-admin/'
VITE_USE_HASH = true
# 是否启用MOCK
VITE_USE_MOCK = true
# base api
VITE_BASE_API = '/api'

View File

@@ -2,10 +2,13 @@
VITE_PUBLIC_PATH = '/'
# 是否启用MOCK
VITE_APP_USE_MOCK = true
VITE_USE_MOCK = true
# base api
VITE_APP_GLOB_BASE_API = '/api'
VITE_BASE_API = '/api'
# test base api
VITE_APP_GLOB_BASE_API_TEST = '/api-test'
# 是否启用压缩
VITE_USE_COMPRESS = true
# 压缩类型
VITE_COMPRESS_TYPE = gzip

View File

@@ -1,11 +0,0 @@
# 资源公共路径,需要以 /开头和结尾
VITE_PUBLIC_PATH = '/'
# 是否启用MOCK
VITE_APP_USE_MOCK = false
# base api
VITE_APP_GLOB_BASE_API = 'http://localhost:8080/api'
# test base api
VITE_APP_GLOB_BASE_API_TEST = 'http://localhost:8080/api-test'

View File

@@ -1,10 +1,7 @@
VITE_PUBLIC_PATH = '/'
# 是否启用MOCK
VITE_APP_USE_MOCK = true
VITE_USE_MOCK = true
# base api
VITE_APP_GLOB_BASE_API = '/api'
# test base api
VITE_APP_GLOB_BASE_API_TEST = '/api-test'
VITE_BASE_API = '/api'

View File

@@ -0,0 +1,62 @@
{
"globals": {
"$loadingBar": true,
"$message": true,
"defineOptions": true,
"$dialog": true,
"$notification": true,
"EffectScope": true,
"computed": true,
"createApp": true,
"customRef": true,
"defineAsyncComponent": true,
"defineComponent": true,
"effectScope": true,
"getCurrentInstance": true,
"getCurrentScope": true,
"h": true,
"inject": true,
"isProxy": true,
"isReactive": true,
"isReadonly": true,
"isRef": true,
"markRaw": true,
"nextTick": true,
"onActivated": true,
"onBeforeMount": true,
"onBeforeUnmount": true,
"onBeforeUpdate": true,
"onDeactivated": true,
"onErrorCaptured": true,
"onMounted": true,
"onRenderTracked": true,
"onRenderTriggered": true,
"onScopeDispose": true,
"onServerPrefetch": true,
"onUnmounted": true,
"onUpdated": true,
"provide": true,
"reactive": true,
"readonly": true,
"ref": true,
"resolveComponent": true,
"shallowReactive": true,
"shallowReadonly": true,
"shallowRef": true,
"toRaw": true,
"toRef": true,
"toRefs": true,
"triggerRef": true,
"unref": true,
"useAttrs": true,
"useCssModule": true,
"useCssVars": true,
"useRoute": true,
"useRouter": true,
"useSlots": true,
"watch": true,
"watchEffect": true,
"watchPostEffect": true,
"watchSyncEffect": true
}
}

View File

@@ -1,3 +1,4 @@
node_modules
dist
public
package.json

View File

@@ -1,26 +0,0 @@
// * https://zhuanlan.zhihu.com/p/388703150
module.exports = {
root: true,
env: {
browser: true, // browser global variables
node: true,
es2021: true, // adds all ECMAScript 2021 globals and automatically sets the ecmaVersion parser option to 12.
},
parserOptions: {
ecmaVersion: 2020,
},
parser: 'vue-eslint-parser',
extends: ['plugin:vue/vue3-recommended', 'plugin:prettier/recommended'],
plugins: ['prettier'],
rules: {
'prettier/prettier': 'error',
'vue/valid-template-root': 'off',
'vue/no-multiple-template-root': 'off',
'vue/multi-word-component-names': [
'error',
{
ignores: ['index', '401', '404'],
},
],
},
}

1
.gitignore vendored
View File

@@ -1,3 +1,4 @@
node_modules
dist
*.local
stats.html

36
.husky/_/husky.sh Normal file
View File

@@ -0,0 +1,36 @@
#!/usr/bin/env sh
if [ -z "$husky_skip_init" ]; then
debug () {
if [ "$HUSKY_DEBUG" = "1" ]; then
echo "husky (debug) - $1"
fi
}
readonly hook_name="$(basename -- "$0")"
debug "starting $hook_name..."
if [ "$HUSKY" = "0" ]; then
debug "HUSKY env variable is set to 0, skipping hook"
exit 0
fi
if [ -f ~/.huskyrc ]; then
debug "sourcing ~/.huskyrc"
. ~/.huskyrc
fi
readonly husky_skip_init=1
export husky_skip_init
sh -e "$0" "$@"
exitCode="$?"
if [ $exitCode != 0 ]; then
echo "husky - $hook_name hook exited with code $exitCode (error)"
fi
if [ $exitCode = 127 ]; then
echo "husky - command not found in PATH=$PATH"
fi
exit $exitCode
fi

4
.husky/commit-msg Normal file
View File

@@ -0,0 +1,4 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npx --no-install commitlint --edit "$1"

4
.husky/pre-commit Normal file
View File

@@ -0,0 +1,4 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npm run lint:staged

7
.prettierrc.json Normal file
View File

@@ -0,0 +1,7 @@
{
"printWidth": 100,
"singleQuote": true,
"semi": false,
"endOfLine": "lf",
"htmlWhitespaceSensitivity": "ignore"
}

View File

@@ -1,11 +1,9 @@
{
"recommendations": [
"dbaeumer.vscode-eslint",
"johnsoncodehk.volar",
"hollowtree.vue-snippets",
"esbenp.prettier-vscode",
"vue.volar",
"antfu.iconify",
"mikestead.dotenv",
"wayou.vscode-todo-highlight",
"aaron-bond.better-comments"
"sdras.vue-vscode-snippets",
"cipchk.cssrem",
]
}

14
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,14 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "一键启动",
"type": "node",
"request": "launch",
"runtimeExecutable": "npm",
"runtimeArgs": ["run-script", "dev"],
"console": "integratedTerminal",
"skipFiles": ["<node_internals>/**"]
}
]
}

34
.vscode/settings.json vendored
View File

@@ -1,26 +1,30 @@
{
"files.eol": "\n",
"path-intellisense.mappings": {
"@/": "${workspaceRoot}/src"
},
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"[jsonc]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[html]": {
"editor.defaultFormatter": "vscode.html-language-features"
},
"prettier.printWidth": 120,
"prettier.singleQuote": true,
"prettier.semi": false,
"prettier.endOfLine": "lf",
"files.eol": "\n",
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
"editor.formatOnSave": false
},
"[css]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
"[typescript]": {
"editor.formatOnSave": false
},
"[typescriptreact]": {
"editor.formatOnSave": false
},
"[vue]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
"editor.formatOnSave": false
},
"eslint.validate": ["javascript", "javascriptreact", "typescript", "vue"],
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
"eslint.validate": ["javascript", "javascriptreact", "typescript"]
"files.associations": {
"*.env.*": "dotenv",
"*.css": "postcss"
}
}

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022 Ronnie Zhang
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.

207
README.EN.md Normal file
View File

@@ -0,0 +1,207 @@
<p align="center">
<a href="https://github.com/zclzone/vue-naive-admin">
<img alt="Vue Naive Admin Logo" width="200" src="./src/assets/images/logo.png">
</a>
</p>
<p align="center">
<a href="./LICENSE"><img allt="MIT License" src="https://badgen.net/github/license/zclzone/vue-naive-admin"/></a>
</p>
<p align='center'>
<b>英文</b> |
<a href="https://github.com/zclzone/vue-naive-admin/blob/main/README.md">中文</a>
</p>
### Introduction
[Vue Naive Admin](https://github.com/zclzone/vue-naive-admin) is a **completely open source free and commercially allowed ** admin templateBased on the latest technology stack of front-end such as `Vue3、Vite3、Pinia、Unocss and Naive UI`. Compared with other more popular backend management templates, this project is more concise, lightweight, fresh style, very low learning costs, ideal for small and medium-sized projects or personal projects.
### Features
- 🍒 Integrated [Naive UI](https://www.naiveui.com)recommended by Evan You.
- 🍑 Integrated login, logout and permission verification.
- 🍐 Integrated multi-environment configuration, dev, test, production and github pages environments.
- 🍎 Integrated `eslint + prettier`.
- 🍌 Integrated `husky + commitlint`.
- 🍉 Integrated `Mock`.
- 🍍 Integrated `pinia`lightweight, simple and easy to use alternative to vuex.
- 📦 Integrated `unplugin` auto import.
- 🤹 Integrated `iconify` iconsupport custom svg icons.
- 🍇 Integrated `unocss`.
### Preview
[https://template.qszone.com](https://template.qszone.com)
[https://base.isme.top](https://base.isme.top)
### Docs
[Vue Naive Admin Docs](https://zclzone.github.io/vue-naive-admin-docs)
### Getting Started
```shell
# Recommended setup git autocrlf 为 false
git config --global core.autocrlf false
# Clone Project
git clone https://github.com/zclzone/vue-naive-admin.git
cd vue-naive-admin
# Install dependencies(Recommended use pnpm: https://pnpm.io/zh/installation)
npm i -g pnpm # Installed and can be ignored
pnpm i # or npm i
# Start
pnpm dev
```
### Build and Release
```shell
# Test Environment
pnpm build:test
# Github Environment
pnpm build:github
# Prod Environment
pnpm build
```
### Other
```shell
# eslint check
pnpm lint
# eslint check and fix
pnpm lint:fix
# PreviewNeed to build first
pnpm preview
# Commithusky+commitlint
pnpm cz
```
### Directory description
```
Vue Naive Admin
|-- .github // github相关如推送github仓库后自动部署gh pages
|-- .husky // git commit钩子
|-- .vscode // vscode编辑器相关
| |-- extensions.json // 插件推荐
| |-- settings.json // 项目级别的vscode配置优先级大于全局vscode配置
|-- build // 构建相关配置
| |-- constant.js // 构建相关的常量
| |-- utils.js // 构建相关的工具方法
| |-- config
| | |-- define.js // 注入全局常量启动或打包后将添加到window中
| | |-- proxy.js // 代理配置
| |-- plugin
| | |-- html.js // vite-plugin-html插件用于注入变量或者html标签
| | |-- mock.js // vite-plugin-mock插件处理mock
| | |-- unplugin.js // unplugin相关插件包含DefineOptions和自动导入
| |-- script // 打包完成后执行的一些node脚本不重要
| |-- build-cname.js // 自动生成cname
|-- mock // mock
| |-- utils.js // mock请求需要用到的工具方法
| |-- api // mock接口
|-- public // 公共资源文件夹下的文件会在打包后会直接加到dist根目录下
|-- settings // 项目配置
| |-- proxy-config.js // 代理配置文件
| |-- theme.json // 主题配置项,主题色等
|-- src
| |-- api // 公共api
| |-- assets // 静态资源
| | |-- images // 图片
| | |-- svg // svg图标
| |-- components // 全局组件
| | |-- common // 公共组件
| | |-- icon // icon相关组件
| | |-- page // 页面组件
| | |-- query-bar // 查询选项
| | |-- table // 封装的表格组件
| |-- composables // 封装的组合式函数
| |-- layout // 布局相关组件
| | |-- components
| | |-- AppMain.vue // 主体内容
| | |-- header // 头部
| | |-- sidebar // 侧边菜单栏
| | |-- tags // 多页签栏
| |-- router // 路由
| | |-- guard // 路由守卫
| | |-- routes // 路由列表
| |-- store // 状态管理pinia
| | |-- modules // 模块
| | |-- app // 管理页面重新加载、折叠菜单栏和keepAlive等
| | |-- permission // 权限相关,管理权限菜单
| | |-- tags // 管理多页签
| | |-- user // 用户模块,管理用户信息、登录登出
| |-- styles // 样式
| |-- utils // 封装的工具方法
| | |-- auth // 权限相关如token、跳转登录页等
| | |-- common // 通用
| | |-- http // 封装axios
| | |-- storage // 封装localStorage和sessionStorage
| |-- views // 页面
| | |-- demo // 示例
| | |-- error-page // 错误页
| | |-- login // 登录页
| | |-- workbench // 首页
| |-- App.vue
| |-- main.js
|-- .cz-config.js // git提交配置
|-- .editorconfig // 编辑器配置
|-- .env // 环境文件,所有环境都会载入
|-- .env.development // 开发环境文件
|-- .env.production // 生产环境文件
|-- .env.test // 测试环境文件
|-- .eslintignore // eslint忽略
|-- .eslintrc.js // eslint配置
|-- .gitignore // git忽略
|-- .prettierignore // prettier格式化忽略
|-- commitlint.config.js // commitlint规范配置
|-- index.html
|-- jsconfig.json // js配置
|-- LICENSE // 协议
|-- package.json // 依赖描述文件
|-- pnpm-lock.yaml // 依赖锁定文件
|-- prettier.config.js // prettier格式化配置
|-- README.md // 项目描述文档(英文)
|-- README.zh-CN.md // 项目描述文档(中文)
|-- unocss.config.js // unocss配置
|-- vite.config.js // vite配置
```
### TS version: Qs Admin
#### source code
- github: [https://github.com/zclzone/qs-admin](https://github.com/zclzone/qs-admin)
- gitee: [https://gitee.com/zclzone/qs-admin-ts](https://gitee.com/zclzone/qs-admin-ts)
#### preview
- [https://admin.qszone.com](https://admin.qszone.com)
- [https://zclzone.github.io/qs-admin](https://zclzone.github.io/qs-admin)
### Open source projects that use this project:
- [gin-vue-blog](https://github.com/szluyu99/gin-vue-blog): A full-stack blog project in Golang, the frontend of the blog backend is based on vue-naive-admin and integrates with a real backend service, implementing features such as backend-controlled routing.
### Communication group & About the author
<a href="https://blog.qszone.com/about/">
<img src="https://assets.qszone.com/images/about.png" style="max-width: 400px" />
</a>

224
README.md
View File

@@ -1,37 +1,55 @@
# VUE NAIVE ADMIN
<p align="center">
<a href="https://github.com/zclzone/vue-naive-admin">
<img alt="Vue Naive Admin Logo" width="200" src="./src/assets/images/logo.png">
</a>
</p>
<p align="center">
<a href="./LICENSE"><img alt="MIT License" src="https://badgen.net/github/license/zclzone/vue-naive-admin"/></a>
</p>
## 简介
<p align='center'>
<b>中文</b> |
<a href="https://github.com/zclzone/vue-naive-admin/blob/main/README.EN.md">English</a>
</p>
Vue Naive Admin一个基于 Vue3.0、Vite、Naive UI 的轻量级后台管理模板,没有集成 TypeScript没有集成国际化没有集成复杂的主题配置上手成本非常低对新手极其友好。不过麻雀虽小五脏俱全权限、Mock、菜单、axios 封装、pinia、项目配置、样式配置、环境配置以及一些经常用的基础组件封装等等这些该有的都有参考多个 vue3 后台管理模板后以最简洁优雅的方式实现,非常适用于中小型项目或者个人项目。
### 简介
## 为什么要开发这个模板
[Vue Naive Admin](https://github.com/zclzone/vue-naive-admin) 是一个 **开源免费且允许商用** 的后台管理模板,基于 `Vue3、Vite4、Pinia、Unocss 和 Naive UI` 等前端最新技术栈。相较于其他比较流行的后台管理模板,此项目更加简洁、轻量,风格清新,上手成本非常低,非常适合中小型项目或者个人项目。
1. Vue3 和 Vite 已经趋于成熟,学习 vite 和 vue3 非常有必要,通过开发模板进行学习是一个很好的方式,事实也证明我确实从中获益良多
2. 目前主流的 Vue3+Vite 后台管理模板都相对复杂,甚至感觉有点花里胡哨(没有贬低的意思,大部分的架构设计都很优秀,只是觉得集成了太多不实用的东西)
3. 自己搭的模板开发起来才最顺手。本人很反感拿别人的模板直接上手开发,如果非要拿别人的模板开发也会尽量先吃透再用,不吃透就没有代码的掌控感和安全感
### 功能
## 功能
- 🍒 集成 [Naive UI](https://www.naiveui.com)
- 🍑 集成登陆、注销及权限验证
- 🍐 集成多环境配置dev、测试、生产环境
- 🍎 集成 `eslint + prettier`,代码约束和格式化统一
- 🍌 集成 `husky + commitlint`,代码提交规范化
- 🍉 集成 `mock` 接口服务dev 环境和发布环境都支持,可动态配置是否启用 mock 服务,不启用时不会加载 mock 包,减少打包体积
- 🍍 集成 `pinia`vuex 的替代方案,轻量、简单、易用
- 📦 集成 `unplugin` 插件,自动导入,解放双手,开发效率直接起飞
- 🤹 集成 `iconify` 图标,支持自定义 svg 图标, 优雅使用icon
- 🍇 集成 `unocss`antfu 开源的原子 css 解决方案,非常轻量
- 🍒 集成 Naive UI尤大推荐的 UI 组件库很香https://www.naiveui.com
- 🍑 集成登陆、注销及权限验证(暂只支持角色页面权限,后续考虑添加按钮权限)
- 🍐 集成多环境配置dev、测试、预发布和生产
- 🍎 集成 eslint + prettier代码约束和格式化统一
- 🍉 集成 Mock 接口服务dev 环境和发布环境都支持,可动态配置是否启用 mock 服务,不启用时不会加载 mock 包,减少打包体积
- 🍇 集成 unocssantfu 大神开源的原子化 css 解决方案,非常轻量,目前我是自己写 scss 样式搭配着 unocss 使用的,很香
- 🍍 集成 piniaVuex 的替代方案,轻量、简单、易用,很香
- 🍏 集成 axios支持多 axios 实例,支持线上环境免重新打包修改 baseURL
- 🍌 二次封装全局 Dialog、Message、LoadingBar 组件
- 🍋 二次封装 localStorage 和 sessionStorage支持设置过期时间
### 预览
## 预览
[https://template.qszone.com](https://template.qszone.com)
[template.qszone.com](https://template.qszone.com)
[https://template.isme.top](https://template.isme.top)
## 文档
[https://base.isme.top](https://base.isme.top)
[羽雀文档Vue Naive Admin](https://www.yuque.com/qszone/vue-naive-admin)
### 文档
## 构建步骤
项目文档: [Vue Naive Admin Docs](https://zclzone.github.io/vue-naive-admin-docs)
从0到1搭建后台: [从0到1带你搭建Vite+Vue3+Pinia+Naive UI后台](https://juejin.cn/column/7093180796424421384)
如何安装pnpm: [安装pnpm](docs/安装pnpm.md)
如何使用图标: [使用图标](docs/使用图标.md)
如何使用unocss: [保熟的UnoCSS使用指北优雅使用antfu大佬的原子化CSS](https://juejin.cn/post/7142466784971456548)
### 快速开始
```shell
# 推荐配置git autocrlf 为 false本项目规范使用lf换行符此配置是为防止git自动将源文件转换为crlf
@@ -45,51 +63,161 @@ git clone https://github.com/zclzone/vue-naive-admin.git
cd vue-naive-admin
# 安装依赖(建议使用pnpm: https://pnpm.io/zh/installation)
npm i -g pnpm # 装了可忽略
pnpm i # 或者 npm i
# 启动
npm run dev
pnpm dev
```
## 发布
### 构建发布
```shell
# 构建测试环境
npm run build:test
pnpm build:test
# 构建预发布环境
npm run build:staging
# 构建github pages环境
pnpm build:github
# 构建生产环境
npm run build
pnpm build
```
## 其他指令
### 其他指令
```shell
# eslint代码格式检查
npm run lint
pnpm lint
# 代码检查并修复
npm run lint:fix
pnpm lint:fix
# 预览发布包效果(需先执行构建指令)
npm run preview
pnpm preview
# 提交代码husky+commitlint
pnpm cz
```
## Git 提交规范
- `feat` 增加新功能
- `fix` 修复问题/BUG
- `style` 代码风格相关无影响运行结果的
- `perf` 优化/性能提升
- `refactor` 重构
- `revert` 撤销修改
- `test` 测试相关
- `docs` 文档/注释
- `chore` 依赖更新/脚手架配置修改等
- `workflow` 工作流改进
- `ci` 持续集成
- `types` 类型定义文件更改
- `wip` 开发中
- `mod` 不确定分类的修改
### 目录说明
```
Vue Naive Admin
|-- .github // github相关如推送github仓库后自动部署gh pages
|-- .husky // git commit钩子
|-- .vscode // vscode编辑器相关
| |-- extensions.json // 插件推荐
| |-- settings.json // 项目级别的vscode配置优先级大于全局vscode配置
|-- build // 构建相关配置
| |-- constant.js // 构建相关的常量
| |-- utils.js // 构建相关的工具方法
| |-- config
| | |-- define.js // 注入全局常量启动或打包后将添加到window中
| | |-- proxy.js // 代理配置
| |-- plugin
| | |-- html.js // vite-plugin-html插件用于注入变量或者html标签
| | |-- mock.js // vite-plugin-mock插件处理mock
| | |-- unplugin.js // unplugin相关插件包含DefineOptions和自动导入
| |-- script // 打包完成后执行的一些node脚本不重要
| |-- build-cname.js // 自动生成cname
|-- mock // mock
| |-- utils.js // mock请求需要用到的工具方法
| |-- api // mock接口
|-- public // 公共资源文件夹下的文件会在打包后会直接加到dist根目录下
|-- settings // 项目配置
| |-- proxy-config.js // 代理配置文件
| |-- theme.json // 主题配置项,主题色等
|-- src
| |-- api // 公共api
| |-- assets // 静态资源
| | |-- images // 图片
| | |-- svg // svg图标
| |-- components // 全局组件
| | |-- common // 公共组件
| | |-- icon // icon相关组件
| | |-- page // 页面组件
| | |-- query-bar // 查询选项
| | |-- table // 封装的表格组件
| |-- composables // 封装的组合式函数
| |-- layout // 布局相关组件
| | |-- components
| | |-- AppMain.vue // 主体内容
| | |-- header // 头部
| | |-- sidebar // 侧边菜单栏
| | |-- tags // 多页签栏
| |-- router // 路由
| | |-- guard // 路由守卫
| | |-- routes // 路由列表
| |-- store // 状态管理pinia
| | |-- modules // 模块
| | |-- app // 管理页面重新加载、折叠菜单栏和keepAlive等
| | |-- permission // 权限相关,管理权限菜单
| | |-- tags // 管理多页签
| | |-- user // 用户模块,管理用户信息、登录登出
| |-- styles // 样式
| |-- utils // 封装的工具方法
| | |-- auth // 权限相关如token、跳转登录页等
| | |-- common // 通用
| | |-- http // 封装axios
| | |-- storage // 封装localStorage和sessionStorage
| |-- views // 页面
| | |-- demo // 示例
| | |-- error-page // 错误页
| | |-- login // 登录页
| | |-- workbench // 首页
| |-- App.vue
| |-- main.js
|-- .cz-config.js // git提交配置
|-- .editorconfig // 编辑器配置
|-- .env // 环境文件,所有环境都会载入
|-- .env.development // 开发环境文件
|-- .env.production // 生产环境文件
|-- .env.test // 测试环境文件
|-- .eslintignore // eslint忽略
|-- .eslintrc.js // eslint配置
|-- .gitignore // git忽略
|-- .prettierignore // prettier格式化忽略
|-- commitlint.config.js // commitlint规范配置
|-- index.html
|-- jsconfig.json // js配置
|-- LICENSE // 协议
|-- package.json // 依赖描述文件
|-- pnpm-lock.yaml // 依赖锁定文件
|-- prettier.config.js // prettier格式化配置
|-- README.md // 项目描述文档(英文)
|-- README.zh-CN.md // 项目描述文档(中文)
|-- unocss.config.js // unocss配置
|-- vite.config.js // vite配置
```
### TS 版本: Qs Admin
#### 源码
- github: [https://github.com/zclzone/qs-admin](https://github.com/zclzone/qs-admin)
- gitee: [https://gitee.com/zclzone/qs-admin-ts](https://gitee.com/zclzone/qs-admin-ts)
#### 预览
- [https://admin.qszone.com](https://admin.qszone.com)
- [https://zclzone.github.io/qs-admin](https://zclzone.github.io/qs-admin)
### 使用该项目的开源项目
- [gin-vue-blog](https://github.com/szluyu99/gin-vue-blog): Golang 全栈博客项目, 博客后台的前端基于 vue-naive-admin对接真实后端服务实现了后端控制路由等特性。
### 入群交流 & 关于作者
<a href="https://blog.qszone.com/about/">
<img src="https://assets.qszone.com/images/about.png" style="max-width: 400px" />
</a>
### ☕ 赞助我
> 开源不易,请作者喝杯咖啡吧
<p>
<img src="https://assets.qszone.com/images/zhifu_weixin.jpg" style="width: 220px" />
<img src="https://assets.qszone.com/images/zhifu_zhifubao.jpg" style="width: 220px" />
</p>

View File

@@ -1,3 +1,33 @@
export const GLOB_CONFIG_FILE_NAME = 'app.config.js'
export const GLOB_CONFIG_NAME = '__APP__GLOB__CONF__'
export const OUTPUT_DIR = 'dist'
export const PROXY_CONFIG = {
/**
* @desc 替换匹配值
* @请求路径 http://localhost:3100/api/user
* @转发路径 http://localhost:8080/user
*/
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
rewrite: (path) => path.replace(new RegExp('^/api'), ''),
},
/**
* @desc 不替换匹配值
* @请求路径 http://localhost:3100/api/v2/user
* @转发路径 http://localhost:8080/api/v2/user
*/
'/api/v2': {
target: 'http://localhost:8080',
changeOrigin: true,
},
/**
* @desc 替换部分匹配值
* @请求路径 http://localhost:3100/api/v3/user
* @转发路径 http://localhost:8080/user
*/
'/api/v3': {
target: 'http://localhost:8080',
changeOrigin: true,
rewrite: (path) => path.replace(new RegExp('^/api'), ''),
},
}

15
build/plugin/html.js Normal file
View File

@@ -0,0 +1,15 @@
import { createHtmlPlugin } from 'vite-plugin-html'
export function configHtmlPlugin(viteEnv, isBuild) {
const { VITE_TITLE } = viteEnv
const htmlPlugin = createHtmlPlugin({
minify: isBuild,
inject: {
data: {
title: VITE_TITLE,
},
},
})
return htmlPlugin
}

40
build/plugin/index.js Normal file
View File

@@ -0,0 +1,40 @@
import vue from '@vitejs/plugin-vue'
/**
* * unocss插件原子css
* https://github.com/antfu/unocss
*/
import Unocss from 'unocss/vite'
// rollup打包分析插件
import visualizer from 'rollup-plugin-visualizer'
// 压缩
import viteCompression from 'vite-plugin-compression'
import { configHtmlPlugin } from './html'
import { configMockPlugin } from './mock'
import unplugin from './unplugin'
export function createVitePlugins(viteEnv, isBuild) {
const plugins = [vue(), ...unplugin, configHtmlPlugin(viteEnv, isBuild), Unocss()]
if (viteEnv?.VITE_USE_MOCK) {
plugins.push(configMockPlugin(isBuild))
}
if (viteEnv.VITE_USE_COMPRESS) {
plugins.push(viteCompression({ algorithm: viteEnv.VITE_COMPRESS_TYPE || 'gzip' }))
}
if (isBuild) {
plugins.push(
visualizer({
open: true,
gzipSize: true,
brotliSize: true,
})
)
}
return plugins
}

View File

@@ -2,12 +2,11 @@ import { viteMockServe } from 'vite-plugin-mock'
export function configMockPlugin(isBuild) {
return viteMockServe({
ignore: /^\_/,
mockPath: 'mock',
mockPath: 'mock/api',
localEnabled: !isBuild,
prodEnabled: isBuild,
injectCode: `
import { setupProdMockServer } from '../mock/_createProdServer';
import { setupProdMockServer } from '../mock';
setupProdMockServer();
`,
})

46
build/plugin/unplugin.js Normal file
View File

@@ -0,0 +1,46 @@
import { resolve } from 'path'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { NaiveUiResolver } from 'unplugin-vue-components/resolvers'
import { FileSystemIconLoader } from 'unplugin-icons/loaders'
import IconsResolver from 'unplugin-icons/resolver'
/**
* * unplugin-icons插件自动引入iconify图标
* usage: https://github.com/antfu/unplugin-icons
* 图标库: https://icones.js.org/
*/
import Icons from 'unplugin-icons/vite'
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
import { getSrcPath } from '../utils'
const customIconPath = resolve(getSrcPath(), 'assets/svg')
export default [
AutoImport({
imports: ['vue', 'vue-router'],
dts: false,
}),
Icons({
compiler: 'vue3',
customCollections: {
custom: FileSystemIconLoader(customIconPath),
},
scale: 1,
defaultClass: 'inline-block',
}),
Components({
resolvers: [
NaiveUiResolver(),
IconsResolver({ customCollections: ['custom'], componentPrefix: 'icon' }),
],
dts: false,
}),
createSvgIconsPlugin({
iconDirs: [customIconPath],
symbolId: 'icon-custom-[dir]-[name]',
inject: 'body-last',
customDomId: '__CUSTOM_SVG_ICON__',
}),
]

View File

@@ -1,13 +1,14 @@
import { resolve } from 'path'
import chalk from 'chalk'
import { writeFileSync } from 'fs-extra'
import { OUTPUT_DIR } from '../constant'
import { getEnvConfig, getRootPath } from '../utils'
export function runBuildCNAME() {
const { VITE_APP_GLOB_CNAME } = getEnvConfig()
if (!VITE_APP_GLOB_CNAME) return
const { VITE_CNAME } = getEnvConfig()
if (!VITE_CNAME) return
try {
writeFileSync(getRootPath(`${OUTPUT_DIR}/CNAME`), VITE_APP_GLOB_CNAME)
writeFileSync(resolve(getRootPath(), `${OUTPUT_DIR}/CNAME`), VITE_CNAME)
} catch (error) {
console.log(chalk.red('CNAME file failed to package:\n' + error))
}

View File

@@ -1,29 +0,0 @@
import { GLOB_CONFIG_FILE_NAME, GLOB_CONFIG_NAME, OUTPUT_DIR } from '../constant'
import fs, { writeFileSync } from 'fs-extra'
import chalk from 'chalk'
import { getEnvConfig, getRootPath } from '../utils'
function createConfig(option) {
const { config, configName, configFileName } = option
try {
const windowConf = `window.${configName}`
const configStr = `${windowConf}=${JSON.stringify(config)};
Object.freeze(${windowConf});
Object.defineProperty(window, "${configName}", {
configurable: false,
writable: false,
});
`.replace(/\s/g, '')
fs.mkdirp(getRootPath(OUTPUT_DIR))
writeFileSync(getRootPath(`${OUTPUT_DIR}/${configFileName}`), configStr)
} catch (error) {
console.log(chalk.red('configuration file configuration file failed to package:\n' + error))
}
}
export function runBuildConfig() {
const config = getEnvConfig()
const configName = GLOB_CONFIG_NAME
const configFileName = GLOB_CONFIG_FILE_NAME
createConfig({ config, configName, configFileName })
}

View File

@@ -1,10 +1,8 @@
import chalk from 'chalk'
import { runBuildConfig } from './build-config'
import { runBuildCNAME } from './build-cname'
export const runBuild = async () => {
try {
runBuildConfig()
runBuildCNAME()
console.log(`${chalk.cyan('build successfully!')}`)
} catch (error) {

View File

@@ -2,33 +2,36 @@ import fs from 'fs'
import path from 'path'
import dotenv from 'dotenv'
export function wrapperEnv(envOptions) {
if (!envOptions) return {}
const ret = {}
/**
* * 项目根路径
* @description 结尾不带/
*/
export function getRootPath() {
return path.resolve(process.cwd())
}
for (const key in envOptions) {
let val = envOptions[key]
if (['true', 'false'].includes(val)) {
val = val === 'true'
/**
* * 项目src路径
* @param srcName src目录名称(默认: "src")
* @description 结尾不带斜杠
*/
export function getSrcPath(srcName = 'src') {
return path.resolve(getRootPath(), srcName)
}
export function convertEnv(envOptions) {
const result = {}
if (!envOptions) return result
for (const envKey in envOptions) {
let envVal = envOptions[envKey]
if (['true', 'false'].includes(envVal)) envVal = envVal === 'true'
if (['VITE_PORT'].includes(envKey)) envVal = +envVal
result[envKey] = envVal
}
if (['VITE_PORT'].includes(key)) {
val = +val
}
if (key === 'VITE_PROXY' && val) {
try {
val = JSON.parse(val.replace(/'/g, '"'))
} catch (error) {
val = ''
}
}
ret[key] = val
if (typeof key === 'string') {
process.env[key] = val
} else if (typeof key === 'object') {
process.env[key] = JSON.stringify(val)
}
}
return ret
return result
}
/**
@@ -45,7 +48,7 @@ function getConfFiles() {
return ['.env', '.env.local', '.env.production']
}
export function getEnvConfig(match = 'VITE_APP_GLOB_', confFiles = getConfFiles()) {
export function getEnvConfig(match = 'VITE_', confFiles = getConfFiles()) {
let envConfig = {}
confFiles.forEach((item) => {
try {
@@ -65,7 +68,3 @@ export function getEnvConfig(match = 'VITE_APP_GLOB_', confFiles = getConfFiles(
})
return envConfig
}
export function getRootPath(...dir) {
return path.resolve(process.cwd(), ...dir)
}

View File

@@ -1,32 +0,0 @@
import html from 'vite-plugin-html'
import { version } from '../../../package.json'
import { GLOB_CONFIG_FILE_NAME } from '../../constant'
export function configHtmlPlugin(viteEnv, isBuild) {
const { VITE_APP_TITLE, VITE_PUBLIC_PATH } = viteEnv
const path = VITE_PUBLIC_PATH.endsWith('/') ? VITE_PUBLIC_PATH : `${VITE_PUBLIC_PATH}/`
const getAppConfigSrc = () => {
return `${path}${GLOB_CONFIG_FILE_NAME}?v=${version}-${new Date().getTime()}`
}
const htmlPlugin = html({
minify: isBuild,
inject: {
data: {
title: VITE_APP_TITLE,
},
tags: isBuild
? [
{
tag: 'script',
attrs: {
src: getAppConfigSrc(),
},
},
]
: [],
},
})
return htmlPlugin
}

View File

@@ -1,26 +0,0 @@
import vue from '@vitejs/plugin-vue'
import Components from 'unplugin-vue-components/vite'
import { NaiveUiResolver } from 'unplugin-vue-components/resolvers'
import VueSetupExtend from 'vite-plugin-vue-setup-extend'
import { unocss } from './unocss'
import { configHtmlPlugin } from './html'
import { configMockPlugin } from './mock'
export function createVitePlugins(viteEnv, isBuild) {
const plugins = [
vue(),
Components({
resolvers: [NaiveUiResolver()],
}),
VueSetupExtend(),
unocss(),
configHtmlPlugin(viteEnv, isBuild),
]
viteEnv?.VITE_APP_USE_MOCK && plugins.push(configMockPlugin(isBuild))
return plugins
}

View File

@@ -1,9 +0,0 @@
import Unocss from 'unocss/vite'
import { presetUno, presetAttributify, presetIcons } from 'unocss'
// https://github.com/antfu/unocss
export function unocss() {
return Unocss({
presets: [presetUno(), presetAttributify(), presetIcons()],
})
}

View File

@@ -1,18 +0,0 @@
const httpsRE = /^https:\/\//
export function createProxy(list = []) {
const ret = {}
for (const [prefix, target] of list) {
const isHttps = httpsRE.test(target)
// https://github.com/http-party/node-http-proxy#options
ret[prefix] = {
target: target,
changeOrigin: true,
ws: true,
rewrite: (path) => path.replace(new RegExp(`^${prefix}`), ''),
// https is require secure=false
...(isHttps ? { secure: false } : {}),
}
}
return ret
}

26
commitlint.config.js Normal file
View File

@@ -0,0 +1,26 @@
module.exports = {
ignores: [(commit) => commit.includes('init')],
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [
2,
'always',
[
'feat',
'fix',
'docs',
'style',
'refactor',
'perf',
'test',
'build',
'ci',
'chore',
'revert',
'wip',
'mod',
'release',
],
],
},
}

3
docs/使用unocss.md Normal file
View File

@@ -0,0 +1,3 @@
推荐阅读作者在掘金的文章:
[保熟的UnoCSS使用指北优雅使用antfu大佬的原子化CSS](https://juejin.cn/post/7142466784971456548)

40
docs/使用图标.md Normal file
View File

@@ -0,0 +1,40 @@
## 使用 iconify 图标
首先去图标库地址:[icones](https://icones.js.org/) 找合适的图标
### 1. 结合 unocss 使用
```html
<i i-carbon-sun />
<i class="i-carbon-sun" />
```
### 2. 结合插件 unplugin-icons 自定义标签使用
`<icon-[iconify图标名称]`
```html
<icon-ant-design:fullscreen-exit-outlined />
<icon-ant-design:fullscreen-outlined />
```
这种方式还支持自定义 svg 图标,本项目自定义 svg 图标固定放在 src/assets/svg 下
`<icon-custom-[svg图标文件名]`
```
<icon-custom-logo />
```
具体配置参看 build/plugin/unplugin.js
### 3. 结合 Naive UI 的 NIcon 组件封装使用
```html
<!-- iconify图标 -->
<TheIcon icon="material-symbols:delete-outline" />
<!-- 自定义svg图标 -->
<TheIcon icon="logo" type="custom" />
```
封装组件参看 src/components/icon

32
docs/安装pnpm.md Normal file
View File

@@ -0,0 +1,32 @@
## 安装pnpm
### 使用Corepack安装推荐
从 v16.13 开始Node.js 发布了 Corepack 来管理包管理器。 这是一项实验性功能,需要通过运行如下脚本来启用它:
```
npx corepack enable // 可能需要管理员权限
```
这将自动在您的系统上安装 pnpm。 但是,它可能不是最新版本的 pnpm。 若要升级,请检查[最新的 pnpm 版本](https://github.com/pnpm/pnpm/releases/latest) 并运行,如 7.14.0
```
corepack prepare pnpm@7.14.0 --activate
```
如果是 Node.js v16.17 或者更新的版本,可以直接安装最新版本的 pnpm
```
corepack prepare pnpm@latest --activate
```
### 使用npm安装
```
npm i -g pnpm
```
更新,卸了重新装
```
npm uninstall -g pnpm
npm i -g pnpm
```

View File

@@ -1,22 +1,35 @@
<!DOCTYPE html>
<html lang="cn">
<head>
<head>
<meta charset="UTF-8" />
<meta http-equiv="Expires" content="0" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Cache-control" content="no-cache" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" href="/favicon.ico" />
<title>
<%= title %>
</title>
</head>
<link rel="icon" href="/favicon.png" />
<link rel="stylesheet" href="/resource/loading.css" />
<body>
<div id="app"></div>
<title><%= title %></title>
</head>
<body>
<div id="app">
<!-- 白屏时的loading效果 -->
<div class="loading-container">
<img src="/resource/logo.png" alt="logo" height="128" />
<div class="loading-spin__container">
<div class="loading-spin">
<div class="left-0 top-0 loading-spin-item"></div>
<div class="left-0 bottom-0 loading-spin-item loading-delay-500"></div>
<div class="right-0 top-0 loading-spin-item loading-delay-1000"></div>
<div class="right-0 bottom-0 loading-spin-item loading-delay-1500"></div>
</div>
</div>
<div class="loading-title"><%= title %></div>
</div>
<script src="/resource/loading.js"></script>
</div>
<script type="module" src="/src/main.js"></script>
</body>
</body>
</html>

View File

@@ -1,9 +1,14 @@
{
"compilerOptions": {
"target": "ESNext",
"baseUrl": "./",
"moduleResolution": "node",
"paths": {
"~/*": ["./*"],
"@/*": ["src/*"]
}
},
"jsx": "preserve",
"allowJs": true
},
"exclude": ["node_modules", "dist"]
}

View File

@@ -1,14 +0,0 @@
import { createProdMockServer } from 'vite-plugin-mock/es/createProdMockServer'
const modules = import.meta.globEager('./**/*.js')
const mockModules = []
Object.keys(modules).forEach((key) => {
if (key.includes('/_')) {
return
}
mockModules.push(...modules[key].default)
})
export function setupProdMockServer() {
createProdMockServer(mockModules)
}

View File

@@ -1,4 +1,4 @@
import { resolveToken } from '../_utils'
import { resolveToken } from '../utils'
const token = {
admin: 'admin',

5
mock/api/index.js Normal file
View File

@@ -0,0 +1,5 @@
import auth from './auth'
import user from './user'
import post from './post'
export default [...auth, ...user, ...post]

138
mock/api/post.js Normal file
View File

@@ -0,0 +1,138 @@
const posts = [
{
title: '使用纯css优雅配置移动端rem布局',
author: '大脸怪',
category: 'Css',
description: '通常配置rem布局会使用js进行处理比如750的设计稿会这样...',
content: '通常配置rem布局会使用js进行处理比如750的设计稿会这样',
isRecommend: true,
isPublish: true,
createDate: '2021-11-04T04:03:36.000Z',
updateDate: '2021-11-04T04:03:36.000Z',
},
{
title: 'Vue2&Vue3项目风格指南',
author: 'Ronnie',
category: 'Vue',
description: '总结的Vue2和Vue3的项目风格',
content: '### 1. 命名风格\n\n> 文件夹如果是由多个单词组成,应该始终是横线连接 ',
isRecommend: true,
isPublish: true,
createDate: '2021-10-25T08:57:47.000Z',
updateDate: '2022-02-28T04:02:39.000Z',
},
{
title: '如何优雅的给图片添加水印',
author: '大脸怪',
category: 'JavaScript',
description: '优雅的给图片添加水印',
content: '我之前写过一篇文章记录了一次上传图片的优化史',
isRecommend: true,
isPublish: true,
createDate: '2021-06-24T18:46:19.000Z',
updateDate: '2021-09-23T07:51:22.000Z',
},
{
title: '前端缓存的理解',
author: '大脸怪',
category: 'Http',
description: '谈谈前端缓存的理解',
content:
'> 背景\n\n公司有个vue-cli3移动端web项目发版更新后发现部分用户手机在钉钉内置浏览器打开出现了缓存',
isRecommend: true,
isPublish: true,
createDate: '2021-06-10T18:51:19.000Z',
updateDate: '2021-09-17T09:33:24.000Z',
},
{
title: 'Promise的五个静态方法',
author: '大脸怪',
category: 'JavaScript',
description: '简单介绍下在 Promise 类中有5 种静态方法及它们的使用场景',
content:
'## 1. Promise.all\n\n并行执行多个 promise并等待所有 promise 都准备就绪。再对它们进行处理。',
isRecommend: true,
isPublish: true,
createDate: '2021-02-22T22:37:06.000Z',
updateDate: '2021-09-17T09:33:24.000Z',
},
]
export default [
{
url: '/api/posts',
method: 'get',
response: (data = {}) => {
const { title, pageNo, pageSize } = data.query
let pageData = []
let total = 60
const filterData = posts.filter(
(item) => item.title.includes(title) || (!title && title !== 0)
)
if (filterData.length) {
if (pageSize) {
while (pageData.length < pageSize) {
pageData.push(filterData[Math.round(Math.random() * (filterData.length - 1))])
}
} else {
pageData = filterData
}
pageData = pageData.map((item, index) => ({
id: pageSize * (pageNo - 1) + index + 1,
...item,
}))
} else {
total = 0
}
return {
code: 0,
message: 'ok',
data: {
pageData,
total,
pageNo,
pageSize,
},
}
},
},
{
url: '/api/post',
method: 'post',
response: ({ body }) => {
return {
code: 0,
message: 'ok',
data: body,
}
},
},
{
url: '/api/post/:id',
method: 'put',
response: ({ query, body }) => {
return {
code: 0,
message: 'ok',
data: {
id: query.id,
body,
},
}
},
},
{
url: '/api/post/:id',
method: 'delete',
response: ({ query }) => {
return {
code: 0,
message: 'ok',
data: {
id: query.id,
},
}
},
},
]

View File

@@ -1,24 +1,24 @@
import { resolveToken } from '../_utils'
import { resolveToken } from '../utils'
const users = {
admin: {
id: 1,
name: '大脸怪(admin)',
avatar: 'https://gitee.com/zclzone/res/raw/master/qs-zone/blob/img/lADPDiQ3QDTwsz3NAarNAaw_428_426.jpg',
avatar: 'https://assets.qszone.com/images/avatar.jpg',
email: 'Ronnie@123.com',
role: ['admin'],
},
editor: {
id: 2,
name: '大脸怪(editor)',
avatar: 'https://gitee.com/zclzone/res/raw/master/qs-zone/blob/img/lADPDiQ3QDTwsz3NAarNAaw_428_426.jpg',
avatar: 'https://assets.qszone.com/images/avatar.jpg',
email: 'Ronnie@123.com',
role: ['editor'],
},
guest: {
id: 3,
name: '访客(guest)',
avatar: 'https://gitee.com/zclzone/res/raw/master/qs-zone/blob/img/lADPDiQ3QDTwsz3NAarNAaw_428_426.jpg',
avatar: 'https://assets.qszone.com/images/avatar.jpg',
role: [],
},
}

6
mock/index.js Normal file
View File

@@ -0,0 +1,6 @@
import { createProdMockServer } from 'vite-plugin-mock/es/createProdMockServer'
import api from './api'
export function setupProdMockServer() {
createProdMockServer(api)
}

File diff suppressed because one or more lines are too long

View File

@@ -1,48 +1,81 @@
{
"name": "vue-naive-admin",
"version": "0.0.1",
"version": "1.0.0",
"scripts": {
"build": "vite build",
"build:github": "vite build --mode github && esno ./build/script",
"build:test": "vite build --mode test",
"cz": "cz",
"dev": "vite",
"lint": "eslint --ext .js,.vue .",
"lint:fix": "eslint --fix --ext .js,.vue .",
"build": "vite build && esno ./build/script",
"build:test": "vite build --mode test && esno ./build/script",
"build:staging": "vite build --mode staging && esno ./build/script",
"lint:staged": "lint-staged",
"prepare": "husky install",
"preview": "vite preview"
},
"lint-staged": {
"*.{js,vue}": [
"eslint --ext .js,.vue ."
]
},
"config": {
"commitizen": {
"path": "node_modules/cz-customizable"
}
},
"eslintConfig": {
"extends": [
"@zclzone",
"@unocss",
".eslint-global-variables.json"
]
},
"dependencies": {
"@vicons/fa": "^0.11.0",
"axios": "^0.21.4",
"dayjs": "^1.10.7",
"@unocss/eslint-config": "^0.55.7",
"@vueuse/core": "^10.4.1",
"@wangeditor/editor": "^5.1.23",
"@wangeditor/editor-for-vue": "5.1.12",
"axios": "^1.5.1",
"dayjs": "^1.11.10",
"echarts": "^5.4.3",
"lodash-es": "^4.17.21",
"md-editor-v3": "^1.10.2",
"md-editor-v3": "^4.7.0",
"mockjs": "^1.1.0",
"pinia": "^2.0.11",
"vue": "^3.2.30",
"vue-router": "^4.0.12"
"pinia": "^2.1.6",
"vite": "^4.4.11",
"vue": "3.3.4",
"vue-echarts": "^6.6.1",
"vue-router": "^4.2.5",
"xlsx": "^0.18.5"
},
"devDependencies": {
"@unocss/preset-attributify": "^0.16.4",
"@unocss/preset-icons": "^0.16.4",
"@unocss/preset-uno": "^0.16.4",
"@vitejs/plugin-vue": "^1.10.2",
"@vue/compiler-sfc": "^3.2.30",
"chalk": "^5.0.0",
"dotenv": "^10.0.0",
"eslint": "^8.6.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-vue": "^8.2.0",
"esno": "^0.13.0",
"fs-extra": "^10.0.0",
"naive-ui": "^2.25.2",
"prettier": "^2.5.1",
"sass": "^1.38.1",
"unocss": "^0.16.4",
"unplugin-vue-components": "^0.17.18",
"vite": "^2.8.0",
"vite-plugin-html": "^2.1.1",
"vite-plugin-mock": "^2.9.6",
"vite-plugin-vue-setup-extend": "^0.3.0"
"@commitlint/cli": "^17.7.2",
"@commitlint/config-conventional": "^17.7.0",
"@iconify/json": "^2.2.125",
"@iconify/vue": "^4.1.1",
"@unocss/preset-rem-to-px": "^0.55.7",
"@vitejs/plugin-vue": "^4.4.0",
"@vue/compiler-sfc": "^3.3.4",
"@zclzone/eslint-config": "^0.0.4",
"chalk": "^5.3.0",
"commitizen": "^4.3.0",
"cz-conventional-changelog": "^3.3.0",
"cz-customizable": "^7.0.0",
"dotenv": "^16.3.1",
"esno": "^0.17.0",
"fs-extra": "^11.1.1",
"husky": "^8.0.3",
"lint-staged": "^14.0.1",
"naive-ui": "^2.35.0",
"rollup-plugin-visualizer": "^5.9.2",
"sass": "^1.69.0",
"unocss": "0.55.3",
"unplugin-auto-import": "^0.16.6",
"unplugin-icons": "^0.16.6",
"unplugin-vue-components": "^0.25.2",
"vite-plugin-compression": "^0.5.1",
"vite-plugin-html": "^3.2.0",
"vite-plugin-mock": "2.9.6",
"vite-plugin-svg-icons": "^2.0.1"
}
}

6817
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +0,0 @@
module.exports = {
printWidth: 120,
singleQuote: true,
semi: false,
endOfLine: 'lf',
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

BIN
public/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

1
public/favicon.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 448 512" data-v-fba6e5d0=""><path d="M400 32H48C21.5 32 0 53.5 0 80v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V80c0-26.5-21.5-48-48-48zm-92.2 312.9c-63.4 0-85.4-28.6-97.1-64.1c-16.3-51-21.5-84.3-63-84.3c-22.4 0-45.1 16.1-45.1 61.2c0 35.2 18 57.2 43.3 57.2c28.6 0 47.6-21.3 47.6-21.3l11.7 31.9s-19.8 19.4-61.2 19.4c-51.3 0-79.9-30.1-79.9-85.8c0-57.9 28.6-92 82.5-92c73.5 0 80.8 41.4 100.8 101.9c8.8 26.8 24.2 46.2 61.2 46.2c24.9 0 38.1-5.5 38.1-19.1c0-19.9-21.8-22-49.9-28.6c-30.4-7.3-42.5-23.1-42.5-48c0-40 32.3-52.4 65.2-52.4c37.4 0 60.1 13.6 63 46.6l-36.7 4.4c-1.5-15.8-11-22.4-28.6-22.4c-16.1 0-26 7.3-26 19.8c0 11 4.8 17.6 20.9 21.3c32.7 7.1 71.8 12 71.8 57.5c.1 36.7-30.7 50.6-76.1 50.6z" fill="#316c72"></path></svg>

After

Width:  |  Height:  |  Size: 825 B

View File

@@ -0,0 +1,85 @@
.loading-container {
position: fixed;
left: 0;
top: 0;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
}
.loading-spin__container {
width: 56px;
height: 56px;
margin: 36px 0;
}
.loading-spin {
position: relative;
height: 100%;
animation: loadingSpin 1s linear infinite;
}
.left-0 {
left: 0;
}
.right-0 {
right: 0;
}
.top-0 {
top: 0;
}
.bottom-0 {
bottom: 0;
}
.loading-spin-item {
position: absolute;
height: 16px;
width: 16px;
background-color: var(--primary-color);
border-radius: 8px;
-webkit-animation: loadingPulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
animation: loadingPulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
@keyframes loadingSpin {
from {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
to {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
@keyframes loadingPulse {
0%, 100% {
opacity: 1;
}
50% {
opacity: .5;
}
}
.loading-delay-500 {
-webkit-animation-delay: 500ms;
animation-delay: 500ms;
}
.loading-delay-1000 {
-webkit-animation-delay: 1000ms;
animation-delay: 1000ms;
}
.loading-delay-1500 {
-webkit-animation-delay: 1500ms;
animation-delay: 1500ms;
}
.loading-title {
font-size: 28px;
font-weight: 500;
color: #6a6a6a;
}

View File

@@ -0,0 +1,9 @@
function addThemeColorCssVars() {
const key = '__THEME_COLOR__'
const defaultColor = '#316c72'
const themeColor = window.localStorage.getItem(key) || defaultColor
const cssVars = `--primary-color: ${themeColor}`
document.documentElement.style.cssText = cssVars
}
addThemeColorCssVars()

BIN
public/resource/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

1
settings/index.js Normal file
View File

@@ -0,0 +1 @@
export * from './theme.json'

37
settings/theme.json Normal file
View File

@@ -0,0 +1,37 @@
{
"header": {
"height": 60
},
"tags": {
"visible": true,
"height": 50
},
"naiveThemeOverrides": {
"common": {
"primaryColor": "#316C72FF",
"primaryColorHover": "#316C72E3",
"primaryColorPressed": "#2B4C59FF",
"primaryColorSuppl": "#316C72E3",
"infoColor": "#2080F0FF",
"infoColorHover": "#4098FCFF",
"infoColorPressed": "#1060C9FF",
"infoColorSuppl": "#4098FCFF",
"successColor": "#18A058FF",
"successColorHover": "#36AD6AFF",
"successColorPressed": "#0C7A43FF",
"successColorSuppl": "#36AD6AFF",
"warningColor": "#F0A020FF",
"warningColorHover": "#FCB040FF",
"warningColorPressed": "#C97C10FF",
"warningColorSuppl": "#FCB040FF",
"errorColor": "#D03050FF",
"errorColorHover": "#DE576DFF",
"errorColorPressed": "#AB1F3FFF",
"errorColorSuppl": "#DE576DFF"
}
}
}

View File

@@ -1,20 +1,11 @@
<script setup>
import AppProvider from '@/components/AppProvider/index.vue'
</script>
<template>
<app-provider>
<AppProvider>
<router-view v-slot="{ Component }">
<component :is="Component" />
</router-view>
</app-provider>
</AppProvider>
</template>
<style lang="scss">
#app {
height: 100%;
.n-config-provider {
height: inherit;
}
}
</style>
<script setup>
import AppProvider from '@/components/common/AppProvider.vue'
</script>

View File

@@ -1,16 +0,0 @@
import { defAxios as request } from '@/utils/http'
export const login = (data) => {
return request({
url: '/auth/login',
method: 'post',
data,
})
}
export const refreshToken = () => {
return request({
url: '/auth/refreshToken',
method: 'post',
})
}

6
src/api/index.js Normal file
View File

@@ -0,0 +1,6 @@
import { request } from '@/utils'
export default {
getUser: () => request.get('/user'),
refreshToken: () => request.post('/auth/refreshToken', null, { noNeedTip: true }),
}

View File

@@ -1,39 +0,0 @@
import { defAxios as request } from '@/utils/http'
export function getPosts(data = {}) {
return request({
url: '/posts',
method: 'get',
data,
})
}
export function getPostById({ id }) {
return request({
url: `/post/${id}`,
method: 'get',
})
}
export function savePost(id, data = {}) {
if (id) {
return request({
url: `/post/${id}`,
method: 'put',
data,
})
}
return request({
url: '/post',
method: 'post',
data,
})
}
export function deletePost(id) {
return request({
url: `/post/${id}`,
method: 'delete',
})
}

View File

@@ -1,38 +0,0 @@
import { defAxios as request } from '@/utils/http'
export function getUsers(data = {}) {
return request({
url: '/users',
method: 'get',
data,
})
}
export function getUser(id) {
if (id) {
return request({
url: `/user/${id}`,
method: 'get',
})
}
return request({
url: '/user',
method: 'get',
})
}
export function saveUser(data = {}, id) {
if (id) {
return request({
url: '/user',
method: 'put',
data,
})
}
return request({
url: `/user/${id}`,
method: 'put',
data,
})
}

BIN
src/assets/images/404.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

BIN
src/assets/images/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

View File

@@ -1,52 +0,0 @@
<script setup>
import { isNullOrUndef } from '@/utils/is'
import { useDialog } from 'naive-ui'
const NDialog = useDialog()
class Dialog {
success(title, option) {
this.showDialog('success', { title, ...option })
}
warning(title, option) {
this.showDialog('warning', { title, ...option })
}
error(title, option) {
this.showDialog('error', { title, ...option })
}
showDialog(type = 'success', option) {
if (isNullOrUndef(option.title)) {
// ! 没有title的情况
option.showIcon = false
}
NDialog[type]({
positiveText: 'OK',
closable: false,
...option,
})
}
confirm(option = {}) {
this.showDialog(option.type || 'error', {
positiveText: '确定',
negativeText: '取消',
onPositiveClick: option.confirm,
onNegativeClick: option.cancel,
onMaskClick: option.cancel,
...option,
})
}
}
window['$dialog'] = new Dialog()
Object.freeze(window.$dialog)
Object.defineProperty(window, '$dialog', {
configurable: false,
writable: false,
})
</script>
<template></template>

View File

@@ -1,10 +0,0 @@
<script setup>
import { useLoadingBar } from 'naive-ui'
window['$loadingBar'] = useLoadingBar()
Object.defineProperty(window, '$loadingBar', {
configurable: false,
writable: false,
})
</script>
<template></template>

View File

@@ -1,72 +0,0 @@
<script setup>
import { useMessage } from 'naive-ui'
const NMessage = useMessage()
let loadingMessage = null
class Message {
/**
* 规则:
* * loading message只显示一个新的message会替换正在显示的loading message
* * loading message不会自动清除除非被替换成非loading message非loading message默认2秒后自动清除
*/
removeMessage(message, duration = 2000) {
setTimeout(() => {
if (message) {
message.destroy()
message = null
}
}, duration)
}
showMessage(type, content, option = {}) {
if (loadingMessage && loadingMessage.type === 'loading') {
// 如果存在则替换正在显示的loading message
loadingMessage.type = type
loadingMessage.content = content
if (type !== 'loading') {
// 非loading message需设置自动清除
this.removeMessage(loadingMessage, option.duration)
}
} else {
// 不存在正在显示的loading则新建一个message,如果新建的message是loading message则将message赋值存储下来
let message = NMessage[type](content, option)
if (type === 'loading') {
loadingMessage = message
}
}
}
loading(content) {
this.showMessage('loading', content, { duration: 0 })
}
success(content, option = {}) {
this.showMessage('success', content, option)
}
error(content, option = {}) {
this.showMessage('error', content, option)
}
info(content, option = {}) {
this.showMessage('info', content, option)
}
warning(content, option = {}) {
this.showMessage('warning', content, option)
}
}
window['$message'] = new Message()
Object.defineProperty(window, '$message', {
configurable: false,
writable: false,
})
</script>
<template></template>

View File

@@ -1,23 +0,0 @@
<script setup>
import MessageContent from './MessageContent.vue'
import DialogContent from './DialogContent.vue'
import LoadingBar from './LoadingBar.vue'
import { useAppStore } from '@/store/modules/app'
const appStore = useAppStore()
</script>
<template>
<n-config-provider :theme-overrides="appStore.themeOverrides">
<n-loading-bar-provider>
<loading-bar />
<n-dialog-provider>
<dialog-content />
<n-message-provider>
<message-content />
<slot></slot>
</n-message-provider>
</n-dialog-provider>
</n-loading-bar-provider>
</n-config-provider>
</template>

View File

@@ -0,0 +1,23 @@
<template>
<footer f-c-c flex-col text-14 color="#6a6a6a">
<p>
Copyright © 2022-present
<a
href="https://github.com/zclzone"
target="__blank"
hover="decoration-underline color-primary"
>
Ronnie Zhang
</a>
</p>
<p>
<a
href="http://beian.miit.gov.cn/"
target="__blank"
hover="decoration-underline color-primary"
>
赣ICP备2020015008号-1
</a>
</p>
</footer>
</template>

View File

@@ -0,0 +1,30 @@
<template>
<n-config-provider
wh-full
:locale="zhCN"
:date-locale="dateZhCN"
:theme="appStore.isDark ? darkTheme : undefined"
:theme-overrides="naiveThemeOverrides"
>
<slot />
</n-config-provider>
</template>
<script setup>
import { zhCN, dateZhCN, darkTheme } from 'naive-ui'
import { useCssVar } from '@vueuse/core'
import { kebabCase } from 'lodash-es'
import { naiveThemeOverrides } from '~/settings'
import { useAppStore } from '@/store'
const appStore = useAppStore()
function setupCssVar() {
const common = naiveThemeOverrides.common
for (const key in common) {
useCssVar(`--${kebabCase(key)}`, document.documentElement).value = common[key] || ''
if (key === 'primaryColor') window.localStorage.setItem('__THEME_COLOR__', common[key] || '')
}
}
setupCssVar()
</script>

View File

@@ -0,0 +1,162 @@
<template>
<div ref="wrapper" class="wrapper" @mousewheel.prevent="handleMouseWheel">
<template v-if="showArrow && isOverflow">
<div class="left dark:bg-dark!" @click="handleMouseWheel({ wheelDelta: 120 })">
<icon-ic:baseline-keyboard-arrow-left />
</div>
<div class="right dark:bg-dark!" @click="handleMouseWheel({ wheelDelta: -120 })">
<icon-ic:baseline-keyboard-arrow-right />
</div>
</template>
<div
ref="content"
class="content"
:class="{ overflow: isOverflow && showArrow }"
:style="{
transform: `translateX(${translateX}px)`,
}"
>
<slot />
</div>
</div>
</template>
<script setup>
import { debounce, useResize } from '@/utils'
defineProps({
showArrow: {
type: Boolean,
default: true,
},
})
const translateX = ref(0)
const content = ref(null)
const wrapper = ref(null)
const isOverflow = ref(false)
const refreshIsOverflow = debounce(() => {
const wrapperWidth = wrapper.value?.offsetWidth
const contentWidth = content.value?.offsetWidth
isOverflow.value = contentWidth > wrapperWidth
resetTranslateX(wrapperWidth, contentWidth)
}, 200)
function handleMouseWheel(e) {
const { wheelDelta } = e
const wrapperWidth = wrapper.value?.offsetWidth
const contentWidth = content.value?.offsetWidth
/**
* @wheelDelta 平行滚动的值 >0 右移 <0: 左移
* @translateX 内容translateX的值
* @wrapperWidth 容器的宽度
* @contentWidth 内容的宽度
*/
if (wheelDelta < 0) {
if (wrapperWidth > contentWidth && translateX.value < -10) return
if (wrapperWidth <= contentWidth && contentWidth + translateX.value - wrapperWidth < -10) return
}
if (wheelDelta > 0 && translateX.value > 10) {
return
}
translateX.value += wheelDelta
resetTranslateX(wrapperWidth, contentWidth)
}
const resetTranslateX = debounce(function (wrapperWidth, contentWidth) {
if (!isOverflow.value) {
translateX.value = 0
} else if (-translateX.value > contentWidth - wrapperWidth) {
translateX.value = wrapperWidth - contentWidth
} else if (translateX.value > 0) {
translateX.value = 0
}
}, 200)
const observers = ref([])
onMounted(() => {
refreshIsOverflow()
observers.value.push(useResize(document.body, refreshIsOverflow))
observers.value.push(useResize(content.value, refreshIsOverflow))
})
onBeforeUnmount(() => {
observers.value.forEach((item) => {
item?.disconnect()
})
})
function handleScroll(x, width) {
const wrapperWidth = wrapper.value?.offsetWidth
const contentWidth = content.value?.offsetWidth
if (contentWidth <= wrapperWidth) return
// 当 x 小于可视范围的最小值时
if (x < -translateX.value + 150) {
translateX.value = -(x - 150)
resetTranslateX(wrapperWidth, contentWidth)
}
// 当 x 大于可视范围的最大值时
if (x + width > -translateX.value + wrapperWidth) {
translateX.value = wrapperWidth - (x + width)
resetTranslateX(wrapperWidth, contentWidth)
}
}
defineExpose({
handleScroll,
})
</script>
<style lang="scss" scoped>
.wrapper {
display: flex;
background-color: #fff;
z-index: 9;
overflow: hidden;
position: relative;
.content {
padding: 0 10px;
display: flex;
align-items: center;
flex-wrap: nowrap;
transition: transform 0.5s;
&.overflow {
padding-left: 30px;
padding-right: 30px;
}
}
.left,
.right {
background-color: #fff;
position: absolute;
top: 0;
bottom: 0;
margin: auto;
width: 20px;
height: 35px;
display: flex;
align-items: center;
justify-content: center;
font-size: 18px;
border: 1px solid #e0e0e6;
border-radius: 2px;
z-index: 2;
cursor: pointer;
}
.left {
left: 0;
}
.right {
right: 0;
}
}
</style>

View File

@@ -0,0 +1,22 @@
<script setup>
/** 自定义图标 */
const props = defineProps({
/** 图标名称(assets/svg下的文件名) */
icon: {
type: String,
required: true,
},
size: {
type: Number,
default: 14,
},
color: {
type: String,
default: undefined,
},
})
</script>
<template>
<TheIcon type="custom" v-bind="props" />
</template>

View File

@@ -0,0 +1,24 @@
<script setup>
const props = defineProps({
icon: {
type: String,
required: true,
},
prefix: {
type: String,
default: 'icon-custom',
},
color: {
type: String,
default: 'currentColor',
},
})
const symbolId = computed(() => `#${props.prefix}-${props.icon}`)
</script>
<template>
<svg aria-hidden="true" width="1em" height="1em">
<use :xlink:href="symbolId" :fill="color" />
</svg>
</template>

View File

@@ -0,0 +1,33 @@
<script setup>
import { renderIcon, renderCustomIcon } from '@/utils'
const props = defineProps({
icon: {
type: String,
required: true,
},
size: {
type: Number,
default: 14,
},
color: {
type: String,
default: undefined,
},
/** iconify | custom */
type: {
type: String,
default: 'iconify',
},
})
const iconCom = computed(() =>
props.type === 'iconify'
? renderIcon(props.icon, { size: props.size, color: props.color })
: renderCustomIcon(props.icon, { size: props.size, color: props.color })
)
</script>
<template>
<component :is="iconCom" />
</template>

View File

@@ -0,0 +1,18 @@
<template>
<transition name="fade-slide" mode="out-in" appear>
<section class="cus-scroll-y wh-full flex-col bg-[#f5f6fb] p-15 dark:bg-hex-121212">
<slot />
<AppFooter v-if="showFooter" mt-15 />
<n-back-top :bottom="20" />
</section>
</transition>
</template>
<script setup>
defineProps({
showFooter: {
type: Boolean,
default: false,
},
})
</script>

View File

@@ -0,0 +1,33 @@
<template>
<AppPage :show-footer="showFooter">
<header v-if="showHeader" mb-15 min-h-45 flex items-center justify-between px-15>
<slot v-if="$slots.header" name="header" />
<template v-else>
<h2 text-22 font-normal text-hex-333 dark:text-hex-ccc>{{ title || route.meta?.title }}</h2>
<slot name="action" />
</template>
</header>
<n-card flex-1 rounded-10>
<slot />
</n-card>
</AppPage>
</template>
<script setup>
defineProps({
showFooter: {
type: Boolean,
default: false,
},
showHeader: {
type: Boolean,
default: true,
},
title: {
type: String,
default: undefined,
},
})
const route = useRoute()
</script>

View File

@@ -0,0 +1,27 @@
<template>
<div
bg="#fafafc"
min-h-60
flex
items-start
justify-between
b-1
rounded-8
p-15
bc-ccc
dark:bg-black
>
<n-space wrap :size="[35, 15]">
<slot />
</n-space>
<div flex-shrink-0>
<n-button secondary type="primary" @click="emit('reset')">重置</n-button>
<n-button ml-20 type="primary" @click="emit('search')">搜索</n-button>
</div>
</div>
</template>
<script setup>
const emit = defineEmits(['search', 'reset'])
</script>

View File

@@ -0,0 +1,34 @@
<template>
<div flex items-center>
<label
v-if="!isNullOrWhitespace(label)"
w-80
flex-shrink-0
:style="{ width: labelWidth + 'px' }"
>
{{ label }}
</label>
<div :style="{ width: contentWidth + 'px' }" flex-shrink-0>
<slot />
</div>
</div>
</template>
<script setup>
import { isNullOrWhitespace } from '@/utils'
defineProps({
label: {
type: String,
default: '',
},
labelWidth: {
type: Number,
default: 80,
},
contentWidth: {
type: Number,
default: 220,
},
})
</script>

View File

@@ -0,0 +1,55 @@
<template>
<n-modal
v-model:show="show"
:style="{ width }"
preset="card"
:title="title"
size="huge"
:bordered="false"
>
<slot />
<template v-if="showFooter" #footer>
<footer flex justify-end>
<slot name="footer">
<n-button @click="show = false">取消</n-button>
<n-button :loading="loading" ml-20 type="primary" @click="emit('onSave')">保存</n-button>
</slot>
</footer>
</template>
</n-modal>
</template>
<script setup>
const props = defineProps({
width: {
type: String,
default: '600px',
},
title: {
type: String,
default: '',
},
showFooter: {
type: Boolean,
default: true,
},
visible: {
type: Boolean,
required: true,
},
loading: {
type: Boolean,
default: false,
},
})
const emit = defineEmits(['update:visible', 'onSave'])
const show = computed({
get() {
return props.visible
},
set(v) {
emit('update:visible', v)
},
})
</script>

View File

@@ -0,0 +1,149 @@
<template>
<QueryBar v-if="$slots.queryBar" mb-30 @search="handleSearch" @reset="handleReset">
<slot name="queryBar" />
</QueryBar>
<n-data-table
:remote="remote"
:loading="loading"
:scroll-x="scrollX"
:columns="columns"
:data="tableData"
:row-key="(row) => row[rowKey]"
:pagination="isPagination ? pagination : false"
@update:checked-row-keys="onChecked"
@update:page="onPageChange"
/>
</template>
<script setup>
import { utils, writeFile } from 'xlsx'
const props = defineProps({
/**
* @remote true: 后端分页 false 前端分页
*/
remote: {
type: Boolean,
default: true,
},
/**
* @remote 是否分页
*/
isPagination: {
type: Boolean,
default: true,
},
scrollX: {
type: Number,
default: 1200,
},
rowKey: {
type: String,
default: 'id',
},
columns: {
type: Array,
required: true,
},
/** queryBar中的参数 */
queryItems: {
type: Object,
default() {
return {}
},
},
/** 补充参数(可选) */
extraParams: {
type: Object,
default() {
return {}
},
},
/**
* ! 约定接口入参出参
* * 分页模式需约定分页接口入参
* @pageSize 分页参数一页展示多少条默认10
* @pageNo 分页参数页码默认1
* * 需约定接口出参
* @pageData 分页模式必须,非分页模式如果没有pageData则取上一层data
* @total 分页模式必须非分页模式如果没有total则取上一层data.length
*/
getData: {
type: Function,
required: true,
},
})
const emit = defineEmits(['update:queryItems', 'onChecked', 'onDataChange'])
const loading = ref(false)
const initQuery = { ...props.queryItems }
const tableData = ref([])
const pagination = reactive({ page: 1, pageSize: 10 })
async function handleQuery() {
try {
loading.value = true
let paginationParams = {}
// 如果非分页模式或者使用前端分页,则无需传分页参数
if (props.isPagination && props.remote) {
paginationParams = { pageNo: pagination.page, pageSize: pagination.pageSize }
}
const { data } = await props.getData({
...props.queryItems,
...props.extraParams,
...paginationParams,
})
tableData.value = data?.pageData || data
pagination.itemCount = data.total ?? data.length
} catch (error) {
tableData.value = []
pagination.itemCount = 0
} finally {
emit('onDataChange', tableData.value)
loading.value = false
}
}
function handleSearch() {
pagination.page = 1
handleQuery()
}
async function handleReset() {
const queryItems = { ...props.queryItems }
for (const key in queryItems) {
queryItems[key] = ''
}
emit('update:queryItems', { ...queryItems, ...initQuery })
await nextTick()
pagination.page = 1
handleQuery()
}
function onPageChange(currentPage) {
pagination.page = currentPage
if (props.remote) {
handleQuery()
}
}
function onChecked(rowKeys) {
if (props.columns.some((item) => item.type === 'selection')) {
emit('onChecked', rowKeys)
}
}
function handleExport(columns = props.columns, data = tableData.value) {
if (!data?.length) return $message.warning('没有数据')
const columnsData = columns.filter((item) => !!item.title && !item.hideInExcel)
const thKeys = columnsData.map((item) => item.key)
const thData = columnsData.map((item) => item.title)
const trData = data.map((item) => thKeys.map((key) => item[key]))
const sheet = utils.aoa_to_sheet([thData, ...trData])
const workBook = utils.book_new()
utils.book_append_sheet(workBook, sheet, '数据报表')
writeFile(workBook, '数据报表.xlsx')
}
defineExpose({
handleSearch,
handleReset,
handleExport,
})
</script>

1
src/composables/index.js Normal file
View File

@@ -0,0 +1 @@
export { default as useCRUD } from './useCRUD'

103
src/composables/useCRUD.js Normal file
View File

@@ -0,0 +1,103 @@
import { isNullOrWhitespace } from '@/utils'
const ACTIONS = {
view: '查看',
edit: '编辑',
add: '新增',
}
export default function ({ name, initForm = {}, doCreate, doDelete, doUpdate, refresh }) {
const modalVisible = ref(false)
const modalAction = ref('')
const modalTitle = computed(() => ACTIONS[modalAction.value] + name)
const modalLoading = ref(false)
const modalFormRef = ref(null)
const modalForm = ref({ ...initForm })
/** 新增 */
function handleAdd() {
modalAction.value = 'add'
modalVisible.value = true
modalForm.value = { ...initForm }
}
/** 修改 */
function handleEdit(row) {
modalAction.value = 'edit'
modalVisible.value = true
modalForm.value = { ...row }
}
/** 查看 */
function handleView(row) {
modalAction.value = 'view'
modalVisible.value = true
modalForm.value = { ...row }
}
/** 保存 */
function handleSave() {
if (!['edit', 'add'].includes(modalAction.value)) {
modalVisible.value = false
return
}
modalFormRef.value?.validate(async (err) => {
if (err) return
const actions = {
add: {
api: () => doCreate(modalForm.value),
cb: () => $message.success('新增成功'),
},
edit: {
api: () => doUpdate(modalForm.value),
cb: () => $message.success('编辑成功'),
},
}
const action = actions[modalAction.value]
try {
modalLoading.value = true
const data = await action.api()
action.cb()
modalLoading.value = modalVisible.value = false
data && refresh(data)
} catch (error) {
modalLoading.value = false
}
})
}
/** 删除 */
function handleDelete(id, confirmOptions) {
if (isNullOrWhitespace(id)) return
$dialog.confirm({
content: '确定删除?',
async confirm() {
try {
modalLoading.value = true
const data = await doDelete(id)
$message.success('删除成功')
modalLoading.value = false
refresh(data)
} catch (error) {
modalLoading.value = false
}
},
...confirmOptions,
})
}
return {
modalVisible,
modalAction,
modalTitle,
modalLoading,
handleAdd,
handleDelete,
handleEdit,
handleView,
handleSave,
modalForm,
modalFormRef,
}
}

View File

@@ -1,19 +1,16 @@
<template>
<router-view v-slot="{ Component, route }">
<transition name="fade-slide" mode="out-in" appear>
<keep-alive :include="keepAliveRouteNames">
<component :is="Component" :key="route.path" />
</keep-alive>
</transition>
<KeepAlive :include="keepAliveNames">
<component :is="Component" v-if="!tagStore.reloading" :key="route.fullPath" />
</KeepAlive>
</router-view>
</template>
<script setup>
import { computed } from 'vue'
import { useRouter } from 'vue-router'
const router = useRouter()
const allRoutes = router.getRoutes()
const keepAliveRouteNames = computed(() => {
return allRoutes.filter((route) => route.meta?.keepAlive).map((route) => route.name)
import { useTagsStore } from '@/store'
const tagStore = useTagsStore()
const keepAliveNames = computed(() => {
return tagStore.tags.filter((item) => item.keepAlive).map((item) => item.name)
})
</script>

View File

@@ -1,18 +0,0 @@
<script setup>
import { useRouter } from 'vue-router'
const router = useRouter()
const { currentRoute } = router
function handleBreadClick(path) {
if (path === currentRoute.value.path) return
router.push(path)
}
</script>
<template>
<n-breadcrumb>
<n-breadcrumb-item v-for="item in currentRoute.matched" :key="item.path" @click="handleBreadClick(item.path)">
{{ item.meta.title }}
</n-breadcrumb-item>
</n-breadcrumb>
</template>

View File

@@ -1,96 +0,0 @@
<script setup>
import { useUserStore } from '@/store/modules/user'
import { useRouter } from 'vue-router'
import { resetRouter } from '@/router'
import { usePermissionStore } from '@/store/modules/permission'
import { NOT_FOUND_ROUTE } from '@/router/routes'
const userStore = useUserStore()
const router = useRouter()
const options = [
{
label: '切换角色',
key: 'switchRole',
},
{
label: '退出登录',
key: 'logout',
},
]
function handleSelect(key) {
if (key === 'logout') {
logout()
} else if (key === 'switchRole') {
switchRole()
}
}
function logout() {
userStore.logout()
$message.success('已退出登录')
router.push({ path: '/login' })
}
function switchRole() {
const permissionStore = usePermissionStore()
const users = [
{
id: 1,
name: '大脸怪(admin)',
avatar: 'https://gitee.com/zclzone/res/raw/master/qs-zone/blob/img/lADPDiQ3QDTwsz3NAarNAaw_428_426.jpg',
email: 'Ronnie@123.com',
role: ['admin'],
},
{
id: 2,
name: '大脸怪(editor)',
avatar: 'https://gitee.com/zclzone/res/raw/master/qs-zone/blob/img/lADPDiQ3QDTwsz3NAarNAaw_428_426.jpg',
email: 'Ronnie@123.com',
role: ['editor'],
},
{
id: 3,
name: '访客(guest)',
avatar: 'https://gitee.com/zclzone/res/raw/master/qs-zone/blob/img/lADPDiQ3QDTwsz3NAarNAaw_428_426.jpg',
role: [],
},
]
const switchUser = users[+userStore.userId % users.length]
resetRouter()
userStore.setUserInfo(switchUser)
const accessRoutes = permissionStore.generateRoutes(switchUser.role)
accessRoutes.forEach((route) => {
!router.hasRoute(route.name) && router.addRoute(route)
})
router.addRoute(NOT_FOUND_ROUTE)
$message.success(`${switchUser.name}`)
}
</script>
<template>
<n-dropdown :options="options" @select="handleSelect">
<div class="avatar">
<img :src="userStore.avatar" />
<span>{{ userStore.name }}</span>
</div>
</n-dropdown>
</template>
<style lang="scss" scoped>
.avatar {
display: flex;
align-items: center;
cursor: pointer;
img {
width: 100%;
width: 25px;
height: 25px;
border-radius: 50%;
margin-right: 10px;
}
}
</style>

View File

@@ -0,0 +1,30 @@
<template>
<n-breadcrumb>
<n-breadcrumb-item
v-for="item in route.matched.filter((item) => !!item.meta?.title)"
:key="item.path"
@click="handleBreadClick(item.path)"
>
<component :is="getIcon(item.meta)" />
{{ item.meta.title }}
</n-breadcrumb-item>
</n-breadcrumb>
</template>
<script setup>
import { renderCustomIcon, renderIcon } from '@/utils'
const router = useRouter()
const route = useRoute()
function handleBreadClick(path) {
if (path === route.path) return
router.push(path)
}
function getIcon(meta) {
if (meta?.customIcon) return renderCustomIcon(meta.customIcon, { size: 18 })
if (meta?.icon) return renderIcon(meta.icon, { size: 18 })
return null
}
</script>

View File

@@ -0,0 +1,12 @@
<template>
<n-icon mr20 size="18" style="cursor: pointer" @click="toggle">
<icon-ant-design:fullscreen-exit-outlined v-if="isFullscreen" />
<icon-ant-design:fullscreen-outlined v-else />
</n-icon>
</template>
<script setup>
import { useFullscreen } from '@vueuse/core'
const { isFullscreen, toggle } = useFullscreen()
</script>

View File

@@ -0,0 +1,11 @@
<template>
<n-icon mr-20 size="18" style="cursor: pointer" @click="handleLinkClick">
<icon-mdi:github />
</n-icon>
</template>
<script setup>
function handleLinkClick() {
window.open('https://github.com/zclzone/vue-naive-admin')
}
</script>

View File

@@ -0,0 +1,12 @@
<template>
<n-icon size="20" cursor-pointer @click="appStore.switchCollapsed">
<icon-mdi:format-indent-increase v-if="appStore.collapsed" />
<icon-mdi:format-indent-decrease v-else />
</n-icon>
</template>
<script setup>
import { useAppStore } from '@/store'
const appStore = useAppStore()
</script>

View File

@@ -0,0 +1,82 @@
<template>
<n-popover trigger="click" placement="bottom" @update:show="handlePopoverShow">
<template #trigger>
<n-badge :value="count" mr-20 cursor-pointer>
<n-icon size="18" color-black dark="color-hex-fff">
<icon-material-symbols:notifications-outline />
</n-icon>
</n-badge>
</template>
<n-tabs v-model:value="activeTab" type="line" justify-content="space-around" animated>
<n-tab-pane
v-for="tab in tabs"
:key="tab.name"
:name="tab.name"
:tab="tab.title + `(${tab.messages.length})`"
>
<ul class="cus-scroll-y max-h-200 w-220">
<li
v-for="(item, index) in tab.messages"
:key="index"
class="flex-col py-12"
border-t="1 solid gray-200"
:style="index > 0 ? '' : 'border: none;'"
>
<span mb-4 text-ellipsis>{{ item.content }}</span>
<span text-hex-bbb>{{ item.time }}</span>
</li>
</ul>
</n-tab-pane>
</n-tabs>
</n-popover>
</template>
<script setup>
import { formatDateTime } from '@/utils'
const activeTab = ref('')
const tabs = [
{
name: 'zan',
title: '点赞',
messages: [
{ content: '你的文章《XX》收到一条点赞', time: formatDateTime() },
{ content: '你的文章《YY》收到一条点赞', time: formatDateTime() },
{ content: '你的文章《AA》收到一条点赞', time: formatDateTime() },
{ content: '你的文章《BB》收到一条点赞', time: formatDateTime() },
{ content: '你的文章《CC》收到一条点赞', time: formatDateTime() },
{ content: '你的文章《DD》收到一条点赞', time: formatDateTime() },
],
},
{
name: 'star',
title: '关注',
messages: [
{ content: '张三 关注了你', time: formatDateTime() },
{ content: '王五 关注了你', time: formatDateTime() },
],
},
{
name: 'comment',
title: '评论',
messages: [
{ content: '张三 评论了你的文章《XX》"学到了"', time: formatDateTime() },
{ content: '李四 评论了你的文章《YY》"不如Vue"', time: formatDateTime() },
],
},
]
const count = ref(tabs.map((item) => item.messages).flat().length)
watch(activeTab, (v) => {
if (count === 0) return
const tabIndex = tabs.findIndex((item) => item.name === v)
count.value -= tabs[tabIndex].messages.length
if (count.value < 0) count.value = 0
})
function handlePopoverShow(show) {
if (show && !activeTab.value) {
activeTab.value = tabs[0]?.name
}
}
</script>

View File

@@ -0,0 +1,18 @@
<script setup>
import { useAppStore } from '@/store'
import { useDark, useToggle } from '@vueuse/core'
const appStore = useAppStore()
const isDark = useDark()
const toggleDark = () => {
appStore.toggleDark()
useToggle(isDark)()
}
</script>
<template>
<n-icon mr-20 cursor-pointer size="18" @click="toggleDark">
<icon-mdi-moon-waning-crescent v-if="isDark" />
<icon-mdi-white-balance-sunny v-else />
</n-icon>
</template>

View File

@@ -0,0 +1,37 @@
<template>
<n-dropdown :options="options" @select="handleSelect">
<div flex cursor-pointer items-center>
<img :src="userStore.avatar" mr10 h-35 w-35 rounded-full />
<span>{{ userStore.name }}</span>
</div>
</n-dropdown>
</template>
<script setup>
import { useUserStore } from '@/store'
import { renderIcon } from '@/utils'
const userStore = useUserStore()
const options = [
{
label: '退出登录',
key: 'logout',
icon: renderIcon('mdi:exit-to-app', { size: '14px' }),
},
]
function handleSelect(key) {
if (key === 'logout') {
$dialog.confirm({
title: '提示',
type: 'info',
content: '确认退出?',
confirm() {
userStore.logout()
$message.success('已退出登录')
},
})
}
}
</script>

View File

@@ -1,21 +1,23 @@
<script setup>
import BreadCrumb from './BreadCrumb.vue'
import HeaderAction from './HeaderAction.vue'
</script>
<template>
<header class="header">
<bread-crumb />
<header-action />
</header>
<div flex items-center>
<MenuCollapse />
<BreadCrumb ml-15 hidden sm:block />
</div>
<div ml-auto flex items-center>
<MessageNotification />
<ThemeMode />
<GithubSite />
<FullScreen />
<UserAvatar />
</div>
</template>
<style lang="scss" scoped>
.header {
padding: 0 24px;
height: 100%;
display: flex;
justify-content: space-between;
align-items: center;
}
</style>
<script setup>
import BreadCrumb from './components/BreadCrumb.vue'
import MenuCollapse from './components/MenuCollapse.vue'
import FullScreen from './components/FullScreen.vue'
import UserAvatar from './components/UserAvatar.vue'
import GithubSite from './components/GithubSite.vue'
import ThemeMode from './components/ThemeMode.vue'
import MessageNotification from './components/MessageNotification.vue'
</script>

View File

@@ -1,31 +0,0 @@
<script setup>
import { LastfmSquare } from '@vicons/fa'
const title = import.meta.env.VITE_APP_TITLE
</script>
<template>
<div class="logo">
<n-icon size="36" color="#316c72">
<lastfm-square />
</n-icon>
<router-link to="/">
<n-gradient-text type="primary">{{ title }}</n-gradient-text>
</router-link>
</div>
</template>
<style lang="scss" scoped>
.logo {
height: 64px;
display: flex;
align-items: center;
justify-content: center;
a {
margin-left: 5px;
.n-gradient-text {
font-size: 14px;
font-weight: bold;
}
}
}
</style>

View File

@@ -1,137 +0,0 @@
<script setup>
import { useRouter } from 'vue-router'
import { computed, h } from 'vue'
import { usePermissionStore } from '@/store/modules/permission'
import { NIcon } from 'naive-ui'
import { ListAlt, CircleRegular } from '@vicons/fa'
import { isExternal } from '@/utils/is'
const router = useRouter()
const permissionStore = usePermissionStore()
const { currentRoute } = router
const menuOptions = computed(() => {
return generateOptions(permissionStore.routes, '')
})
function resolvePath(basePath, path) {
if (isExternal(path)) return path
return (
'/' +
[basePath, path]
.filter((path) => !!path && path !== '/')
.map((path) => path.replace(/(^\/)|(\/$)/g, ''))
.join('/')
)
}
function renderIcon(icon, props = { size: 12 }) {
return () => h(NIcon, { ...props }, { default: () => h(icon) })
}
function isSingleRoute(route) {
let isSingle = true
let curRoute = route
while (curRoute.children && curRoute.children.length) {
if (curRoute.children.length > 1) {
isSingle = false
break
}
if (curRoute.children.length === 1) {
curRoute = curRoute.children[0]
}
}
return isSingle
}
function generateOptions(routes, basePath) {
let options = []
routes.forEach((route) => {
if (route.name && !route.isHidden) {
let curOption = {
label: (route.meta && route.meta.title) || route.name,
key: route.name,
path: resolvePath(basePath, route.path),
}
if (route.children && route.children.length) {
curOption.icon = renderIcon(route.meta?.icon || ListAlt, { size: 16 })
curOption.children = generateOptions(route.children, resolvePath(basePath, route.path))
} else {
curOption.icon = (route.meta?.icon && renderIcon(route.meta?.icon)) || renderIcon(CircleRegular, { size: 8 })
}
options.push(curOption)
}
})
return options
}
function handleMenuSelect(key, item) {
if (isExternal(item.path)) {
window.open(item.path)
} else {
router.push(item.path)
}
// 通过path重定向
// router.push({
// path: '/redirect',
// query: { redirect: item.path },
// })
}
</script>
<template>
<n-menu
class="side-menu"
accordion
:indent="12"
:root-indent="12"
:options="menuOptions"
:value="(currentRoute.meta && currentRoute.meta.activeMenu) || currentRoute.name"
@update:value="handleMenuSelect"
/>
</template>
<style lang="scss">
.n-menu {
margin-top: 10px;
padding-left: 10px;
.n-menu-item {
margin-top: 0;
position: relative;
&::before {
left: 0;
right: 0;
border-radius: 0;
background-color: unset !important;
}
&:hover,
&.n-menu-item--selected {
border-radius: 0 !important;
&::before {
border-right: 3px solid $primaryColor;
background-color: #16243a;
background: linear-gradient(90deg, rgba(255, 255, 255, 0) 0%, rgba($primaryColor, 0.3) 100%);
}
}
}
.n-menu-item-content-header {
font-size: 14px;
font-weight: bold;
}
.n-submenu-children {
.n-menu-item-content-header {
font-size: 14px;
font-weight: normal;
position: relative;
overflow: visible !important;
}
}
}
</style>

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