Template
1
0
mirror of https://github.com/un-pany/v3-admin-vite.git synced 2025-04-21 03:19:19 +08:00

Compare commits

..

596 Commits
v3.3.0 ... main

Author SHA1 Message Date
pany
4c2e01e320 chore: release 5.0.0-beta.6 2025-04-18 19:31:30 +08:00
pany
ce9918b21a chore: update dependencies 2025-04-18 19:30:33 +08:00
pany
f314c04a59 chore: update dependencies 2025-03-27 16:26:01 +08:00
pany
8d3b688a5f docs: add MobVue introduction 2025-03-21 19:23:08 +08:00
pany
138def830f chore: update dependencies 2025-03-21 19:10:01 +08:00
pany
546d29c39b docs: fix typo 2025-03-08 15:46:30 +08:00
pany
cf24e82e53 chore: update dependencies 2025-03-05 20:22:19 +08:00
pany
5dcd7105c0 docs: fix typo 2025-03-01 11:12:30 +08:00
pany
7cca118cfd docs: add MobVue 2025-03-01 11:08:17 +08:00
pany
97d1e288a9 perf: 优化 useTheme 逻辑 2025-02-22 18:10:18 +08:00
pany
8d0c30b001 chore: release 5.0.0-beta.5 2025-02-21 17:20:36 +08:00
pany
64c1dbb5c4 chore: update dependencies 2025-02-21 17:19:07 +08:00
pany
93e5537332 docs: fix typo 2025-02-19 14:28:40 +08:00
pany
c2f5c8ee91 refactor: 减少路由守卫中的硬编码 2025-02-19 13:35:10 +08:00
pany
73fa762052 types: update vue-router type 2025-02-19 11:31:57 +08:00
pany
8d4588b029 fix: 删除冗余的 async 关键字 2025-02-18 16:28:41 +08:00
pany
a935696af0 docs: fix typo 2025-02-18 11:46:05 +08:00
pany
2b081e4eb4 types: 优化调用 isString 方法后对参数类型的推导 2025-02-17 18:19:01 +08:00
pany
9acc5f156e chore: 优化 UnoCSS 配置 2025-02-12 14:05:27 +08:00
pany
a4d38a4307 docs: 更新简介 2025-02-11 16:33:09 +08:00
pany
2dd0aa8575 docs: fix typo 2025-02-10 11:05:55 +08:00
pany
67abb3b2d9 chore: release 5.0.0-beta.4 2025-02-10 10:56:13 +08:00
pany
70b0889be9 fix: 更新生产和预发环境的接口地址 2025-02-10 10:55:13 +08:00
pany
050fc557a6 chore: release 5.0.0-beta.3 2025-02-08 15:46:50 +08:00
pany
879dd6b318 refactor: 重命名接口使其更加遵循 RESTful 风格 2025-02-08 15:21:39 +08:00
pany
d8d1ad2ab7 docs: fix typo 2025-02-08 14:01:33 +08:00
pany
95a8604a2f feat: 为 401 403 添加合适的 error message 2025-02-07 19:14:16 +08:00
pany
d82f3ec874 feat: 由 Apifox 提供在线 Mock 2025-02-07 17:23:07 +08:00
pany
c84b7bcca1 chore: upgrade pnpm and dependencies 2025-02-06 11:02:19 +08:00
pany
010dbcbfe0 chore: release 5.0.0-beta.2 2025-01-22 19:02:10 +08:00
pany
935493cce8 chore: 压缩内置图片 2025-01-22 18:58:01 +08:00
pany
95167dcfc0 chore: update dependencies 2025-01-22 18:39:19 +08:00
pany
fc345dc77b chore: update dependencies 2025-01-16 16:21:11 +08:00
pany
0a438082a6 chore: 更新 code-snippets 2024-12-31 18:14:09 +08:00
pany
6ad8d08ff4 perf: 优化 vite-svg-loader 的 svgo 配置 2024-12-24 20:25:14 +08:00
pany
d34b7c95d0 chore: release 5.0.0-beta.1 2024-12-20 20:35:42 +08:00
pany
c643b410ef perf: 优化预热常用文件功能 2024-12-13 19:38:19 +08:00
pany
5aa09b05db perf: 减少 tsc noEmit 配置硬编码 2024-12-13 19:01:50 +08:00
pany
1fa9d8c1f7 perf: 减少不必要的 dom 节点 2024-12-12 20:12:06 +08:00
pany
197b3be224 feat: 禁止选中 Sidebar 菜单文字 2024-12-12 11:49:44 +08:00
pany
ea34a957f0 perf: 优化 NavigationBar CSS 2024-12-10 19:20:32 +08:00
pany
a3ec9e2ad2 perf: 右侧设置按钮定位方式改为百分比 2024-12-10 17:36:58 +08:00
pany
75206acfe3 ci: 升级 node & pnpm 2024-12-10 17:05:44 +08:00
mowangjuanzi
272642ec97
fix: 修复登录页的主题切换按钮不能跟随配置 (#230)
Co-authored-by: pany <939630029@qq.com>
2024-12-10 10:24:27 +08:00
pany
63e3960465 perf: 更新 unocss 配置 2024-12-09 19:22:38 +08:00
pany
2b21a29f09 perf: 优化 unocss important 配置 2024-12-09 18:49:38 +08:00
pany
e05e94f2b0 perf: 提升 unocss 样式优先级 2024-12-09 18:33:59 +08:00
pany
170269e171 docs: 更新中文文档链接 2024-12-06 17:39:41 +08:00
zhengdongxiang
b02c8c6f60
fix: 修复TagsView居中样式问题 (#227)
Co-authored-by: mac <mac@macdeMacBook-Pro.local>
2024-12-06 13:44:16 +08:00
pany
f7d4ea147e chore: release 5.0.0-beta.0 2024-12-05 19:35:30 +08:00
pany
f331a2e655 docs: 更新依赖 & 更新付费服务详情链接 2024-12-05 19:34:25 +08:00
pany
c9859aa597
chore: 5.0.0-beta 2024-12-05 19:05:51 +08:00
pany
2431b21d26 Merge branch 'main' into 5.x 2024-12-05 18:59:32 +08:00
pany
45468c5d91 docs: 优化标签样式 2024-12-05 17:34:43 +08:00
pany
45d01eabea docs: 添加标签 2024-12-05 17:10:34 +08:00
pany
c43ee2bb30 docs: 5.0 英文自述 2024-12-05 16:24:10 +08:00
pany
ae8315586c docs: 添加发行版链接 2024-12-05 15:55:17 +08:00
pany
cd338a5f4a docs: 更换预览图 2024-12-05 15:31:38 +08:00
pany
956792ff40 docs: 更换预览图 2024-12-05 15:21:32 +08:00
pany
4d5e6a03b2 docs: 优化文档 2024-12-05 14:54:30 +08:00
pany
aeaaa87a22 docs: 完善文档 2024-12-05 14:44:02 +08:00
pany
f5c7788692 docs: 优化 5.0 中文自述 2024-12-05 13:42:51 +08:00
pany
1cef1684ef docs: 5.0 中文自述 2024-12-05 11:53:53 +08:00
pany
5e52ef5ad0 chore: 升级 unplugin-svg-component 0.12.1 2024-12-04 17:30:19 +08:00
pany
c35f0e7735 perf: 简化 router.push 参数 2024-12-03 19:46:49 +08:00
pany
ab306c4e2c perf: 移除路由守卫中的 next 参数 2024-12-03 19:43:14 +08:00
pany
2685efa759 perf: 将 jsdom 切换为 happy-dom 2024-12-03 19:01:09 +08:00
pany
b85354b8d3 test: 更新测试用例 2024-12-03 18:42:34 +08:00
pany
17ca9b58e4 perf: svg icon 进行 treeShaking 2024-12-03 17:17:34 +08:00
pany
5702a2f9e0 chore: 升级 unplugin-svg-component 2024-12-03 16:53:07 +08:00
pany
8d2f240381 chore: fix typo 2024-12-03 14:51:09 +08:00
liym
52a5cb8dc6
chore: fix typo (#222)
Co-authored-by: pany <939630029@qq.com>
2024-12-03 14:27:23 +08:00
pany
a3f6c45866 wip: 更新单测 2024-12-03 13:57:57 +08:00
pany
50d862dec6 chore: release v4.5.6 2024-12-03 13:54:07 +08:00
pany
d3fc20a53a fix: 修复 release v4.5.5 版本引起的 mobile + 非左侧布局模式时左侧导航菜单背景色异常的问题 2024-12-03 13:54:07 +08:00
pany
2d840ea675 chore: release v4.5.5 2024-12-03 13:54:07 +08:00
pany
1f531938a3 fix: 左侧导航菜单在高度不足时渲染不全的问题 2024-12-03 13:54:07 +08:00
pany
fb6b42d753 chore: 还原 @types/node 依赖 2024-12-03 13:33:05 +08:00
pany
9f8e1beafe chore: element plus 2.9.0 2024-12-03 11:53:09 +08:00
pany
d631d46ab2 docs: 优化 description 2024-12-02 19:21:56 +08:00
pany
631efeed5d docs: 优化权限指令和权限函数注释 2024-12-02 19:01:56 +08:00
pany
08ce7fd2f4 perf: 优化注释和插件命名 2024-12-02 18:41:44 +08:00
pany
cfbefb10ab perf: 精简代码 2024-12-02 18:37:13 +08:00
pany
de349901df docs: 优化注释 2024-12-02 18:29:41 +08:00
pany
0c23ef9f8c chore: 修改 directives.d.ts 文件名 2024-12-02 18:16:07 +08:00
pany
df3f209be9 perf: 移除冗余的文件 2024-12-02 17:17:48 +08:00
ClariS
ebb8e808d4
feat: 引入 unplugin-svg-component 插件代替 vite-plugin-svg-icons 插件 & elIcon 支持类型提示 (#220)
Co-authored-by: pany <panyang@mafengwo.com>
2024-12-02 17:07:21 +08:00
ClariS
503e2d8487
docs: 删除 global-components.d.ts 注释多余的括号 (#221) 2024-12-02 10:22:18 +08:00
pany
392927884b perf: 移除冗余的 xe-utils 插件 2024-11-29 19:28:31 +08:00
pany
51823f8c6a perf: 移除暂未大量使用的 vxe-table-plugin-element 插件 2024-11-29 18:37:12 +08:00
pany
ff5dcd66f1 perf: 将项目中所以得外部 css 资源放一起 2024-11-29 17:03:28 +08:00
pany
0142c8a94d chore: 更换 description & 移除自定义 changelogithub 2024-11-29 16:19:47 +08:00
pany
23f0c1b133 perf: 移除冗余的 element plus css 完整导入 2024-11-29 15:45:14 +08:00
pany
20cc0885d7 refactor: 自动按需导入 pinia api 2024-11-29 11:48:04 +08:00
pany
5338d12fd3 refactor: 自动按需导入 vue 和 vue-router api 2024-11-29 11:41:21 +08:00
pany
ea1fe99dfa perf: 将自动生成的 .d.ts 文件归纳到 auto 目录 2024-11-29 10:59:33 +08:00
pany
fd075bd63d feat: 引入自动按需导入组件 2024-11-28 19:34:54 +08:00
pany
6e43607f7e feat: 同步 element plus font-family 方案 2024-11-28 17:05:26 +08:00
pany
6fd7dfe7aa perf: 优化 demo 页面 2024-11-28 16:59:26 +08:00
pany
8e423954ce chore: 移除 @types/node 依赖,升级其他依赖 2024-11-28 15:53:08 +08:00
pany
6a39d67941 perf: 优化权限示例页面名称 2024-11-28 14:50:50 +08:00
pany
97030fa04b refactor: 优化权限示例页面 UI 2024-11-28 14:44:27 +08:00
pany
d3b09be774 perf: 省略 window 前缀 2024-11-28 13:39:44 +08:00
pany
05f8cc6404 docs: fix typo 2024-11-28 11:43:05 +08:00
pany
1bd695db17 perf: 保留一个 dashboard 占位图 2024-11-28 11:39:08 +08:00
pany
0fe9d6072b perf: dashboard 页面占位图自适应宽度 2024-11-28 11:32:28 +08:00
pany
86ec9766dd feat: 新增 dashboard 页面占位图 2024-11-28 11:18:02 +08:00
pany
0ac0719e21 feat: 收缩侧边栏后选中项父级背景高亮 2024-11-27 19:35:49 +08:00
pany
f46f9f5091 docs: 优化左侧导航菜单 title 2024-11-27 19:13:47 +08:00
pany
fa0eadd986 feat: 标签栏左右滚动按钮文字提示 2024-11-27 18:57:16 +08:00
pany
0cbb1ea998 revert: 撤销 svgo 配置 2024-11-27 17:41:00 +08:00
pany
eef5807040 docs: 优化路径别名注释 2024-11-27 17:14:54 +08:00
pany
d0cfcead4a refactor: 将 apis 目录剥离到 common 目录下 2024-11-27 17:06:50 +08:00
pany
5a94ec4c3b feat: 登录页验证码输入框开启遮眼动画 2024-11-27 16:58:34 +08:00
pany
0dc9d03237 refactor: 构建 login 微模块 2024-11-27 16:43:38 +08:00
pany
f892c5d730 refactor: 将示例页面的 route 收拢到 /demo 下 2024-11-27 15:23:28 +08:00
pany
cc6635a6d9 perf: 简化 error 目录名称 2024-11-27 14:12:44 +08:00
pany
9a02f69692 refactor: 将示例页面收拢到 demo 目录 2024-11-27 14:04:17 +08:00
pany
b0e93f6184 refactor: 重命名 plugins 目录下名称 2024-11-27 11:52:06 +08:00
pany
dc48bf4880 refactor: 合并 directives 目录到 plugins 目录 2024-11-27 11:17:38 +08:00
pany
f6668e471c revert: 撤销对 plugins 和 directives 的目录迁移 2024-11-27 10:55:57 +08:00
pany
f2146ae716 refactor: 迁移 utils 到 common 目录 2024-11-27 10:31:51 +08:00
pany
835d4c4636 refactor: 迁移 plugins 到 common 目录 2024-11-27 10:23:29 +08:00
pany
8cb796a290 refactor: 迁移 directives 到 common 目录 2024-11-27 10:21:06 +08:00
pany
1ce2353c1b refactor: 迁移 constants 到 common 目录 2024-11-27 10:19:19 +08:00
pany
77cfb43df6 refactor: 迁移 composables 到 common 目录 2024-11-27 10:17:30 +08:00
pany
a61ec9c71b refactor: 迁移 components 到 common 目录 2024-11-27 10:14:30 +08:00
pany
fd6b76f990 refactor: 迁移 assets 到 common 目录 2024-11-27 10:09:14 +08:00
pany
f197777060 perf: 简化 Sidebar 子组件名称 2024-11-26 19:07:35 +08:00
pany
9363308494 perf: 扁平化 plugins 目录 2024-11-26 18:25:05 +08:00
pany
24fd0a6253 perf: 扁平化 directives 目录 2024-11-26 18:20:52 +08:00
pany
cc65b1d6c7 perf: 将 layouts 配置文件迁移到 layouts 目录 2024-11-26 18:13:39 +08:00
pany
8a224cc946 perf: 将白名单配置文件迁移到 router 目录 2024-11-26 18:05:20 +08:00
pany
599d03a4cf perf: 移除没用到的 svg icon 2024-11-26 17:37:08 +08:00
pany
609c1b4b4b docs: route meta 2024-11-26 17:20:15 +08:00
pany
663a2c7173 perf: 移除左侧导航菜单 unique-opened 属性 2024-11-26 16:15:49 +08:00
pany
b01c8b29ef perf: 移除冗余的 alwaysShow 属性 2024-11-26 16:14:05 +08:00
pany
4bf0e7a998 refactor: 精简多级路由示例页面 2024-11-26 16:02:50 +08:00
pany
31b2a5ba82 refactor: 调整 router 模块结构 2024-11-26 14:45:42 +08:00
pany
c1290cdf28 refactor: 调整 layouts 目录结构 2024-11-26 11:42:24 +08:00
pany
df2821ebe9 refactor: 调整 http 模块目录结构 2024-11-26 11:29:20 +08:00
pany
ea098a6eaa refactor: 调整部分相对路径 2024-11-26 11:08:05 +08:00
pany
19b145597e refactor: 统一 vxe-table 页面代码风格 2024-11-26 10:49:16 +08:00
pany
5727b2245b ci: 修复 release.yml 配置错误 2024-11-25 19:37:02 +08:00
pany
4d518b06f4 chore: 更新 changelogithub 配置 2024-11-25 19:32:16 +08:00
pany
6f87caf262 refactor: 移除不必要的 _ 前缀 2024-11-25 19:02:36 +08:00
pany
1a2924861d docs: 新增 husky 配置注释 2024-11-25 17:52:26 +08:00
pany
74543de942 refactor: 统一采用 props.name 的形式访问 prop 2024-11-25 17:48:47 +08:00
pany
51e7cd95a6 refactor: 优先采用普通导出而不是默认导出 2024-11-25 17:42:52 +08:00
pany
0f5f16cf0f refactor: 统一代码风格 (if 语句 & 箭头函数) 2024-11-25 17:26:27 +08:00
pany
9b359203fc chore: 精简 preview 命令 2024-11-25 14:28:14 +08:00
pany
23ab6b7ab5 docs: 统一注释风格 (允许顶层变量采用 /** 内层变量采用默认的 //) 2024-11-25 14:21:18 +08:00
pany
0b517855b6 perf: 优化部分 composable 代码细节 2024-11-25 11:09:23 +08:00
pany
b5210eb452 docs: 更新 useRouteListener 注释风格 2024-11-25 10:56:06 +08:00
pany
921d4411d0 refactor: 重构 usePagination 代码细节 2024-11-25 10:53:34 +08:00
pany
592fa82650 refactor: 重构 useFullscreenLoading 代码细节 2024-11-25 10:40:24 +08:00
pany
f121c01227 perf: 优化 useFetchSelect 细节 2024-11-25 10:26:04 +08:00
pany
61997fd102 perf: 优化 ThemeSwitch 组件细节 2024-11-25 10:19:15 +08:00
pany
30b4754493 refactor: 重构 SearchMenu 组件结构细节 2024-11-25 10:10:24 +08:00
pany
ff4ae8ba5b refactor: 重构 SearchMenu 组件结构 2024-11-23 00:04:43 +08:00
pany
b18bf9d4b5 refactor: 重构 Notify 组件结构 2024-11-22 21:13:49 +08:00
pany
aada21b6c1 perf: 优化 pinia store 调用位置 2024-11-22 19:55:51 +08:00
pany
8dec01f011 feat: 通过 svgo 自动移除 svg fill 属性 2024-11-22 16:50:27 +08:00
pany
23f5dc9c14 refactor: 更改 svg icons 目录结构 2024-11-22 16:06:31 +08:00
pany
17b9ebb55c refactor: 压缩和重命名目录结构 2024-11-22 14:22:57 +08:00
pany
464218e72c refactor: store/modules 目录替换为 pinia/stores 2024-11-21 21:03:09 +08:00
pany
7a29374c2e refactor: hook 关键字替换为 composable 2024-11-21 20:41:48 +08:00
pany
f9b1f080b5 docs: 优化注释 2024-11-21 20:14:55 +08:00
pany
14fca80a90 perf: 优化环境变量 2024-11-21 20:01:28 +08:00
pany
0fe2d1fbcf chore: 默认关闭反向代理的 ws 配置 2024-11-21 19:54:16 +08:00
pany
a415ade4c2 refactor: 统一调用工具函数 2024-11-21 19:53:33 +08:00
pany
d29e5ba062 perf: 优化全局 ts 配置 2024-11-21 19:52:07 +08:00
pany
ea253ab1a9 docs: 优化 env 文件注释和命名 2024-11-21 19:50:20 +08:00
pany
b892bc0936 feat: 允许 JS 2024-11-21 14:44:19 +08:00
pany
9473a13c53 docs: fix typo 2024-11-21 12:41:24 +08:00
pany
5f611b9a13 fix: 修复 mobile + 非左侧布局模式时左侧导航菜单背景色异常的问题 2024-11-21 11:52:45 +08:00
pany
3155c086ca feat: 左侧导航菜单 title 过长自动省略 2024-11-21 11:28:37 +08:00
pany
b8033dc892 perf: 优化冗余的 css important 2024-11-21 11:03:40 +08:00
pany
526c2cf2c6 chore: 移除 pnpm shamefully-hoist 配置项 2024-11-20 20:29:23 +08:00
pany
c168ef7ab8 fix: 左侧导航菜单在高度不足时渲染不全的问题 2024-11-20 19:49:01 +08:00
pany
4f01de4e92 docs: 优化注释 2024-11-20 16:30:41 +08:00
pany
f2eba50938 docs: 更新项目中的部分外链 2024-11-20 15:40:24 +08:00
pany
4a0452d38e refactor: 重构 vite.config.ts 文件 2024-11-20 15:18:27 +08:00
pany
226cdd3d1f docs: 优化注释 2024-11-19 20:14:23 +08:00
pany
f3645cfdca chore: 精简 .gitignore 文件 2024-11-19 19:31:10 +08:00
pany
787143e1c8 chore: 更新 vitest 配置 2024-11-19 18:31:03 +08:00
pany
45bcc0545a chore: 精简 tsconfig 2024-11-19 18:18:03 +08:00
pany
84233e5ba6 ci: update node / pnpm 2024-11-19 16:38:23 +08:00
pany
c35e70ea77 chore: 精简 package.json 文件内容 2024-11-19 15:52:33 +08:00
pany
727a3a4b60 chore: remove prettier 2024-11-19 15:04:14 +08:00
pany
cefad64730 docs: 移除冗余的注释 2024-11-19 15:00:11 +08:00
pany
933b7f0cd9 docs: 精简 commit 类型 2024-11-19 14:45:40 +08:00
pany
80afc6e13f perf: 移除未用到的示例工具函数 2024-11-19 14:04:26 +08:00
pany
d47428ccc7 chore: pnpm eslint . --fix 2024-11-19 13:46:35 +08:00
pany
28ed4c0977 chore: pnpm eslint . --fix 2024-11-19 10:42:02 +08:00
pany
7f02e18d37 chore: pnpm eslint . --fix 2024-11-18 19:40:44 +08:00
pany
c8571a5f55 feat: 全新的 ESLint 配置 2024-11-18 14:25:35 +08:00
pany
92d0778992 chore: release v4.5.4 2024-11-18 11:31:09 +08:00
pany
43e359fffa chore: update dependencies 2024-11-18 10:54:49 +08:00
pany
4f4b4892b9 refactor: 重构标签栏右键菜单定位方式 2024-11-15 16:54:59 +08:00
pany
7748dc0fa4 chore: release v4.5.3 2024-11-14 20:10:29 +08:00
pany
394f6e16dc perf: api 优化, 将 className 优化为 classList 2024-11-14 20:07:24 +08:00
pany
869986bc50 refactor: 更新 pinia store 的调用方式 2024-11-14 17:33:12 +08:00
pany
ade5d806c7 refactor: 重写灰色模式和色弱模式挂载方式 2024-11-14 16:29:59 +08:00
pany
047909f661 style: 优化搜索组件样式细节 2024-11-13 17:51:34 +08:00
pany
03b2611786 style: 优化设置按钮 z-index 防止被局部 loading 遮挡 2024-11-13 16:55:45 +08:00
pany
738bd4a49f style: 禁用 el-tag 组件渐变动画 2024-11-13 16:27:27 +08:00
pany
19f7b6049e fix: 修复布局配置为混合模式时 header 组件 content 区域超出屏幕的问题 2024-11-13 16:08:35 +08:00
pany
e38525638d refactor: no keywords "any" and "unknown" 2024-11-12 20:10:09 +08:00
pany
b3d935cdb0 fix: 元素可以获得焦点时, 不应该采用 aria-hidden 属性对辅助技术隐藏 2024-11-12 15:58:59 +08:00
pany
396b8fac53 refactor: 移除 utils/index.ts 文件 2024-11-12 13:24:41 +08:00
pany
6af1ba94ff refactor: 重写 getCssVar、setCssVar 工具函数 2024-11-12 13:06:33 +08:00
pany
82bd4ee14a chore: release v4.5.2 2024-11-11 20:02:39 +08:00
pany
f83a72f097 style: 补充代码片段 code-snippets 中缺少的字段 2024-11-11 19:37:50 +08:00
pany
ac3f53f86b chore: 移除废弃的 CompConsumer 组件 2024-11-11 19:26:45 +08:00
pany
2108c04388 chore: husky 添加 tsc 检测, 达到更严格的代码提交校验 2024-11-11 19:22:31 +08:00
pany
6680e72645 refactor: 重写 formatDateTime 工具函数 2024-11-11 19:06:19 +08:00
pany
6f2fbf4a90 fix: 修复上一个 commit 带来的 tag 显示顺序问题 2024-11-08 20:23:56 +08:00
_island
13a53cc994
fix: 第一个缓存页面加载时不触发 onActivated 钩子 (#214)
Co-authored-by: pany <939630029@qq.com>
2024-11-08 19:40:42 +08:00
pany
ffb6cb3a11 chore: update dependencies 2024-11-04 19:06:48 +08:00
pany
3a18c38218 fix: html lang="zh-CN" 2024-11-04 17:55:12 +08:00
pany
bc0c8f4cc4 refactor: extract detect-ie.js 2024-11-04 17:37:43 +08:00
淳冉
8523ec6695
feat: 验证 IE, 并引导使用其它浏览器 (#215)
Co-authored-by: pany <939630029@qq.com>
2024-11-04 17:25:02 +08:00
pany
0bcb7886d5 chore: update dependencies 2024-10-19 10:52:19 +08:00
pany
9b1142c106 chore: update dependencies 2024-10-10 20:44:37 +08:00
pany
cd3fcd3b1f Merge branch 'main' of https://github.com/un-pany/v3-admin-vite 2024-09-27 20:43:27 +08:00
pany
9168c56359 chore: release v4.5.1 2024-09-27 20:42:36 +08:00
pany
3a9d344698 chore: release v4.5.1 2024-09-27 20:35:45 +08:00
pany
934eed8e7b chore: update release.yml 2024-09-27 20:29:08 +08:00
pany
256e48b0db chore: release v4.5.0 2024-09-27 16:12:02 +08:00
pany
bf8b5b99bb chore: configure release.yml 2024-09-27 16:10:12 +08:00
pany
85bbfb5adc chore: update node and pnpm 2024-09-27 14:10:47 +08:00
pany
356ba4723b chore: fixed sass 1.78.0 2024-09-27 14:07:24 +08:00
pany
377f9a5141 chore: fixed eslint 8.57.1 && update other dependencies 2024-09-27 14:02:37 +08:00
pany
0f140a61b2 chore: lock the version 2024-09-20 11:11:49 +08:00
pany
7cd21e6a87 chore: 项目默认使用工作区 typescript 版本 2024-09-19 19:57:37 +08:00
pany
8201db2f66 chore: 适配 typescript 5.6.x 2024-09-19 19:56:29 +08:00
pany
e9440ff996 chore: update other dependencies 2024-09-19 19:55:41 +08:00
pany
0a93a9f909 chore: fixed eslint 8.57.0 && update other dependencies 2024-08-24 13:55:59 +08:00
pany
372a63439b chore: fixed eslint 8.57.0 && update other dependencies 2024-08-10 10:28:09 +08:00
Femoon
7e0e868c48
style: fix the width of some icons when the sidebar was collapsed (#201)
Co-authored-by: pany <939630029@qq.com>
2024-07-24 11:09:03 +08:00
Valentine Miya
7bda678d7c
types: modify table type name (#199) 2024-07-22 11:40:25 +08:00
Juck Zhang
6893c70df0
chore: update ". vscode/settings.json" 2024-07-15 21:00:20 +08:00
pany
f97824ddf8 chore: fixed eslint 8.57.0 && update other dependencies 2024-07-15 20:27:36 +08:00
pany
ccdeaaeaba ci: update node pnpm version 2024-07-15 18:44:17 +08:00
pany
d77d7f7651 chore: update husky configuration 2024-07-15 18:28:55 +08:00
pany
7e9eaab9da chore: 替换 vxe-table 已废弃的 api 2024-05-28 10:23:52 +08:00
pany
3bc8ca91f6 chore: fixed eslint 8.57.0 && update other dependencies 2024-05-28 10:22:05 +08:00
pany
1c78445e44 docs: 更新进群方式 2024-05-24 10:24:31 +08:00
pany
4e6ed361c7 fix: 解决开启 "固定 Header" 时出现 1px 滚动条的问题 2024-05-21 10:10:15 +08:00
qppq54s
6f8755c47c
fix: 修复 iPhone 手机浏览器访问开发环境时可能出现白屏的问题 (#183) 2024-04-22 20:51:32 +08:00
pany
82b5bd81ef refactor: 完成多主题模块的重构 2024-03-29 21:14:11 +08:00
pany
5b531e75db wip: 重构多主题模块 2024-03-29 17:00:29 +08:00
pany
20f9ba8595 wip: 重构多主题模块 2024-03-28 21:34:24 +08:00
pany
246fca2692 fix: 修复 element plus table 右侧操作栏透明的问题 2024-03-26 09:39:32 +08:00
pany
7837bac010 style: 简化多主题样式 2024-03-25 13:48:08 +08:00
pany
7e22f8915f chore: update vite 2024-03-25 09:45:04 +08:00
pany
1186650df8 chore: update all dependencies 2024-03-23 13:44:02 +08:00
pany
952b327261 fix: 修复内容区全屏时顶部存在的间隙以及部分模态框不能显示的问题 2024-03-22 18:58:56 +08:00
pany
b7987218c0 fix: 非左侧模式时,Header 都是 fixed 布局 2024-03-22 11:04:37 +08:00
pany
f528bde687 style: 完善 vxe-table 多主题样式 2024-03-21 17:06:37 +08:00
pany
56f933d0ca chore: update all dependencies 2024-03-21 15:37:09 +08:00
nevlf
6a6c39395c
chore: 升级 vxe-table 并适配主题切换 (#177)
Co-authored-by: pany <939630029@qq.com>
2024-03-21 15:28:43 +08:00
pany
2fa3fc05fb style: add pixel units 2024-03-21 10:35:00 +08:00
nevlf
c4526f72b1
feat: 主题切换动画 (#176)
Co-authored-by: pany <panyang@mafengwo.com>
2024-03-21 10:00:14 +08:00
ClariS
bf17c67fda
fix: debugger 语句在开发环境下不生效 (#170)
Co-authored-by: pany <939630029@qq.com>
2024-03-18 17:31:27 +08:00
Aaron-zon
893ad57f7c
refactor: 将项目中 json 法深拷贝换成 lodash-es 的 cloneDeep (#174) 2024-03-11 09:20:19 +08:00
pany
5a2836c825 docs: 优化权限相关文档 2024-03-08 21:12:48 +08:00
pany
a01e68d381 chore: fixed vxe-table 4.4.1 && update other dependencies 2024-03-08 20:52:19 +08:00
dingzhen
f01b72f3c7
chore: 移除已经废弃的 vscode-typescript-vue-plugin 插件, 已被 volar 扩展取代 (#172) 2024-03-08 17:45:38 +08:00
pany
c5471b5fca refactor: 留用 404 alias 同时移除 ErrorPage 路由 && 简化 redirect 路由 path 2024-03-03 23:00:17 +08:00
pany
d716248736 style: 移动端时登录框宽度自适应 2024-03-03 22:24:23 +08:00
pany
c0a950b383 chore: remove useless code 2024-02-28 17:28:12 +08:00
pany
c6225b3cf7 style: simplify sidebar css 2024-02-28 17:04:25 +08:00
pany
d8ab55fe2a fix: 修复缩放侧边栏时 icon 轻微抖动问题 2024-02-27 17:23:24 +08:00
pany
58fc646116 chore: fixed vxe-table 4.4.1 && update other dependencies 2024-02-27 15:35:46 +08:00
cilliandevops
ecfd39bcc2
feat:add login box top owl animation effect (#162)
Co-authored-by: pany <939630029@qq.com>
Co-authored-by: pany <panyang@mafengwo.com>
2024-02-26 22:22:15 +08:00
pany
2498f239c0 refactor: rename the variable "whether to enable dynamic routing" from "async" to "dynamic" 2024-02-25 19:26:35 +08:00
Aaron-zon
9963b590fd
style: rename the variable "whether to enable dynamic routing" from "async" to "dynamic" 2024-02-25 19:12:51 +08:00
pany
01249a4f5a refactor: update "hooks/useFetchSelect" type 2024-02-25 12:52:55 +08:00
pany
2ad674b0b0 chore: remove ‘declare’ 2024-02-22 18:52:25 +08:00
pany
86b6562ab8 chore: 更新 vitest 插件 2024-02-22 14:29:19 +08:00
pany
34276b5855 chore: prettier 继承 .editorconfig 的缩进风格 2024-02-21 13:34:50 +08:00
pany
bf9d6eb7b6 chore: fixed vxe-table 4.4.1 && update other dependencies 2024-02-20 09:56:40 +08:00
pany
975bfd24dc docs: 错别字登陆更正为登录 2024-02-07 19:04:55 +08:00
pany
13165b7600 chore: v4.4.0 2024-02-07 16:24:53 +08:00
pany
e81668e293 chore: 切换在线 mock 接口地址 2024-02-07 10:38:12 +08:00
pany
a00e57a1e8 perf: 移除遗留的废弃逻辑 2024-02-07 10:32:53 +08:00
pany
b92a1a24fc refactor: 简化路由守卫逻辑 2024-02-06 20:22:07 +08:00
pany
160f389c60 perf: 移除冗余的方法 2024-02-06 17:21:16 +08:00
pany
8c7f4184a1 perf: 新版 vue 已修复热更新导致的页面白屏问题,项目移除相关的代码 2024-02-06 17:07:07 +08:00
pany
0f1afe1a94 fix: 修复禁用动态路由后用户名不显示的问题 2024-02-06 16:21:11 +08:00
pany
02dca40083 feat: add useLayoutMode 2024-02-06 15:25:16 +08:00
pany
0a4d896965 feat: add useDevice 2024-02-06 13:39:56 +08:00
pany
b2597f1ffd fix: 修复子路由设置 hidden: true 不生效的问题 2024-02-05 17:26:42 +08:00
pany
fff3eb9498 refactor: 采用 vue 3.4 新增的 defineModel 宏 2024-02-04 17:14:24 +08:00
pany
8fd01971fd chore: element-plus 2.5.5 2024-02-03 16:33:07 +08:00
pany
47e83913e1 refactor: 增强 element plus table 示例代码的通用性 2024-02-03 16:07:17 +08:00
pany
7f067b111b chore: 适配 element-plus 2.5.4 2024-02-02 19:43:01 +08:00
pany
f5339314b8 refactor: 重写部分 table 示例代码,使逻辑更加清晰 2024-01-22 22:38:11 +08:00
pany
2e1e72099a chore: 适配 element-plus 2.5.2 2024-01-19 19:37:28 +08:00
pany
7317e96e7b chore: fixed vxe-table 4.4.1 && update other dependencies 2024-01-19 19:36:24 +08:00
dingzhen
9510639b49
fix: 修复处于顶部模式时 el-menu 未自适应宽度去折叠的问题 (#153)
Co-authored-by: pany <939630029@qq.com>
2024-01-04 22:59:02 +08:00
pany
56f294d51c chore: fixed vxe-table 4.4.1 && update other dependencies 2024-01-04 22:49:14 +08:00
pany
d19b6da1d4 chore: 更新新版 VSCode codeActionsOnSave 配置 2024-01-04 21:03:36 +08:00
pany
4d3d4e1057 chore: update vue 3.4 2023-12-29 18:57:54 +08:00
pany
9fb0b9b51a chore: fixed vxe-table 4.4.1 && update other dependencies 2023-12-28 12:37:46 +08:00
pany
03928dfce5 types: 隐藏无须处理的 ts 报错 2023-12-12 20:45:02 +08:00
pany
aa98712984 chore: fixed vxe-table 4.4.1 && update other dependencies 2023-12-12 19:15:17 +08:00
pany
3f6a140e11 chore: 适配 vite 5 2023-12-12 19:01:25 +08:00
pany
1a1c8886d2 chore: fixed vxe-table 4.4.1 && update other dependencies 2023-12-08 13:06:16 +08:00
pany
b2eaa29b1e chore: v4.3.0 2023-12-02 15:24:15 +08:00
pany
f50ccba6d4 perf: 开启 vite5 的预热功能,提高初始页面加载速度 2023-12-02 15:19:42 +08:00
pany
3810f269bf chore: upgrade the vite version to 5.x 2023-12-02 14:40:55 +08:00
pany
16f2e1ad2f chore: fixed vxe-table 4.4.1 && update other dependencies 2023-11-08 10:06:03 +08:00
pany
536802ed6a chore: fixed vxe-table 4.4.1 && update other dependencies 2023-10-30 10:10:44 +08:00
pany
7783d577b0 chore: v4.2.4 2023-10-20 17:27:24 +08:00
pany
8d9e16ba79 chore: fixed vxe-table 4.4.1 && update other dependencies 2023-10-20 14:40:16 +08:00
pany
91350be394 style: 解决顶部模式下 navigation-bar 背景色遮挡了下方阴影的问题 2023-10-18 16:49:14 +08:00
pany
7307f4caa2 style: 优化 element-plus 2.4.1 版本下 el-menu--horizontal 样式 2023-10-18 15:48:54 +08:00
pany
a91b8848a9 docs: 新增三级及其以上路由缓存功能的文案提示 2023-10-18 14:42:26 +08:00
pany
9ddcf09dfd chore: update element-plus 2.4.1 2023-10-18 14:07:37 +08:00
pany
029e695be2 style: 优化 element-plus 2.4.0 版本表格暗黑模式下样式 2023-10-17 14:41:04 +08:00
pany
80462fd2fc chore: fixed vxe-table 4.4.1 && update other dependencies 2023-10-17 14:39:23 +08:00
pany
8006a2af55 chore: 移除废弃的 mock api 2023-10-08 14:43:14 +08:00
pany
c5e3684495 chore: fixed vxe-table 4.4.1 && update other dependencies 2023-10-07 18:18:47 +08:00
_island
5d1eeec01f
fix: sidebar scroll area is abnormal (#134)
* fix: sidebar scroll area is abnormal

* fix: display value

* fix: 防止顶部模式垂直滚动

* fix: 'div' should be 'block'

---------

Co-authored-by: pany <939630029@qq.com>
2023-10-07 16:29:43 +08:00
pany
fc9e188572 chore: update deploy.yml 2023-09-28 09:55:12 +08:00
pany
ca9674c4cd chore: fixed vxe-table 4.4.1 && update other dependencies 2023-09-28 09:52:03 +08:00
pany
a36c560810 docs: 完善 CompConsumer 组件注释 2023-09-22 17:34:52 +08:00
pany
d20023b355 chore: v4.2.3 2023-09-21 17:54:56 +08:00
pany
536803c3c1 style: 添加导入类型时遗漏的 type 标识 2023-09-21 17:54:08 +08:00
Chill Fish
bcb97a6c49
refactor: 使 router/permission 更易读 (#133) 2023-09-19 10:11:25 +08:00
pany
561261cf58 chore: fixed vxe-table 4.4.1 && update other dependencies 2023-09-15 10:49:20 +08:00
ClariS
8e83db5228
fix: 替换 %placeholder 后侧边栏 tip-line 样式丢失 (#132) 2023-09-07 17:12:31 +08:00
ClariS
574f47cb00
perf: 使用 %placeholder 代替 @mixin (#131) 2023-09-07 13:43:23 +08:00
pany
7bfacf333a feat: 新增页脚 Footer 组件 2023-09-05 18:03:30 +08:00
ClariS
6d6ee82379
fix: 左侧布局模式下取消固定 header 导致 header 底部阴影丢失 (#129) 2023-09-05 09:45:10 +08:00
ClariS
41c7ac1c9a
fix: app-scrollbar 设置高度 100% 不生效 (#128) 2023-09-04 17:26:42 +08:00
pany
f1918b4bb8 perf: 全局水印采用 fixed 布局,以兼容更多的细节场景 2023-09-01 23:01:52 +08:00
pany
2c760e077b chore: v4.2.2 2023-09-01 17:53:37 +08:00
pany
a02d150533 docs: 优化水印 hook 注释 2023-09-01 17:50:37 +08:00
ClariS
be7425d54d
perf: 优化水印 hook (#127) 2023-09-01 17:27:19 +08:00
pany
65530215e0 fix: 合并路由守卫中重复的钩子 2023-09-01 14:40:28 +08:00
pany
2e28d0db26 perf: 新增水印防御功能开关 & 对水印监听逻辑进行极致的性能优化 2023-09-01 11:52:57 +08:00
pany
9ebed9753d perf: 再次提升水印 hook 性能 2023-09-01 00:43:44 +08:00
pany
413305322d fix: 解决父容器相同时,后渲染的水印没有防御功能的问题 2023-08-31 22:13:36 +08:00
pany
f3e71e0795 perf: 优化水印 hook 性能 2023-08-31 18:28:24 +08:00
pany
d2952e0d16 feat: 水印功能 2023-08-31 17:28:05 +08:00
pany
cc87ab3ccc chore: 还原不必要的 route title,优化 hook 示例文案 2023-08-31 08:53:35 +08:00
ClariS
2ea0a80160
wip: 水印 hook 优化 (#126) 2023-08-31 08:46:58 +08:00
pany
2c7d5b2b76 wip: 水印 hook 优化 2023-08-30 18:26:49 +08:00
ClariS
e43edd4739
feat: 新增水印 hook (#125) 2023-08-30 15:32:34 +08:00
pany
33f0131c48
docs: Create FUNDING.yml 2023-08-30 12:52:44 +08:00
pany
46e9ea2f28 docs: 完善路由 title 以及文档链接 2023-08-29 19:12:56 +08:00
pany
6593db0d21 feat: 动态标题 2023-08-29 18:13:35 +08:00
ClariS
1ef54abc1e
fix: 优化 TagsView 后导致 tag 显示顺序错乱 (#124) 2023-08-29 09:05:23 +08:00
ClariS
07f321ecbb
perf: 优化 TagsView (#123) 2023-08-28 19:34:05 +08:00
pany
6b62edc323 perf: 优化 CompConsumer 代码 2023-08-28 19:12:35 +08:00
HavocZ
28f1c0718b
feat: 新增一种 keep-alive 缓存方案 (#119) 2023-08-28 17:34:28 +08:00
pany
a7e078930c fix: 解决 useRouteListener 代码中 onBeforeUnmount 用在 setup 外产生的告警 2023-08-28 17:05:59 +08:00
pany
15df444f20 chore: fixed vxe-table 4.4.1 && update other dependencies 2023-08-28 16:54:37 +08:00
pany
36f8c8acf2 docs: 优化一些文案 2023-08-28 16:52:17 +08:00
pany
a3dce0e0f2 feat: 新增 useRouteListener,系统统一采用该 hook 监听路由变化 2023-08-28 14:07:34 +08:00
ClariS
a267429e5b
docs: vite 配置选项 chunkSizeWarningLimit 注释优化 (#122) 2023-08-28 11:09:07 +08:00
pany
41973c2013 refactor: 优化路由监听器,使其支持移除单个回调函数 2023-08-25 19:01:31 +08:00
pany
5113d60565 fix: 应该在组件销毁前移除路由变化事件监听器 2023-08-25 15:58:38 +08:00
HavocZ
086e9a8c60
perf: 优化单独监听路由浪费渲染性能的问题 (#120) 2023-08-25 14:36:03 +08:00
ClariS
8c89dbd743
perf: 优化 useFullscreenLoading hook 示例 (#121) 2023-08-25 12:34:13 +08:00
pany
a486b8c18c chore: v4.2.1 2023-08-24 17:48:02 +08:00
pany
56bb2c2492 chore: el-config-provider 组件采用短横线方式 2023-08-24 16:27:58 +08:00
pany
adff68b7e8 docs: 优化 fixBlankPage 方法注释 2023-08-24 16:25:47 +08:00
pany
59a412ace3 perf: 优化 router-view 绑定 key 的方式 2023-08-24 16:24:59 +08:00
ClariS
28edb3775a
docs: 更新消息通知文案时间 (#117) 2023-08-24 16:15:20 +08:00
pany
db3de00fcf fix: 修复 <transition> 和 <keep-alive> 组合使用导致的页面空白 2023-08-24 14:08:43 +08:00
pany
86f11d42b8 docs: 优化分块策略和混淆器注释 2023-08-23 13:48:33 +08:00
pany
84885b7bc8 chore: 移除 terser 依赖 && 修复分块策略配置名称拼写错误 2023-08-23 12:04:31 +08:00
HavocZ
88033a823d
perf: 优化构建速度 (#112) 2023-08-23 11:59:22 +08:00
ClariS
2cd7b161e8
fix: 搜索菜单项路径为外链时无法跳转 (#111) 2023-08-22 16:06:49 +08:00
pany
ab6aa20695 chore: v4.2.0 2023-08-21 17:31:48 +08:00
pany
82c3409b7d fix: 解决项目配置的缓存项少于实际所需项数时导致的页面空白问题 2023-08-21 17:15:12 +08:00
pany
30ec43d490 chore: fixed vxe-table 4.4.1 && update other dependencies 2023-08-18 17:03:13 +08:00
pany
31288109c4 feat: 新增返回顶部功能 2023-08-18 09:11:48 +08:00
pany
f966b45ace style: 伪元素统一采用双冒号格式 2023-08-17 13:55:23 +08:00
pany
8eb8876f04 perf: 将可替换的 getCssVariableValue 取值代码替换为直接使用 css var 2023-08-17 11:34:05 +08:00
ClariS
8488bfdff2
fix: 侧边栏菜单高亮 bug (#108) 2023-08-16 14:48:50 +08:00
pany
4a27131c6d fix: 修复路由缺少 name 和带有动态参数时菜单搜索功能异常问题 2023-08-15 13:36:54 +08:00
ClariS
5f0e91c5ec
perf: 对搜索菜单组件进行优化 (#106) 2023-08-14 17:08:44 +08:00
ClariS
f12f33014e
fix: 修复搜索菜单列表滚动 BUG (#105) 2023-08-14 16:00:12 +08:00
pany
e32e3226d6 docs: Optimize README 2023-08-11 11:00:31 +08:00
pany
ee91984cf5 docs: adjust paragraph order 2023-08-11 09:34:15 +08:00
pany
9f486757b8 docs: update the README 2023-08-10 18:22:32 +08:00
ClariS
ac1b621667
feat: 新增菜单搜索功能 (#96) 2023-08-10 14:48:22 +08:00
ClariS
ad9ff59a40
feat: 支持三级及其以上路由的 keep-alive 缓存 (#93) 2023-08-07 15:14:15 +08:00
pany
45b0bea731 chore: 简化反向代理配置 2023-08-07 09:34:11 +08:00
pany
a47cea68a9 chore: fixed vxe-table 4.4.1 && update other dependencies 2023-08-07 09:26:01 +08:00
pany
df4ff20df8 chore: 更新 vite/client 配置 2023-07-24 13:41:08 +08:00
pany
307810d9c0 chore: v4.1.0 2023-07-21 17:40:17 +08:00
pany
2d561982f7 chore: 切换 mock api 服务 2023-07-21 15:50:23 +08:00
pany
1aacb0cd1e chore: 固定 vxe-table 4.4.1 && 升级其他所有依赖 2023-07-19 18:18:44 +08:00
pany
1ae44be0bc docs: 新增服务挂了友情提示 2023-07-19 18:15:37 +08:00
pany
6f381ffd54 feat: 新增顶部布局模式 2023-07-19 18:14:42 +08:00
pany
4571a12742 style: 优化顶部布局模式样式 2023-07-19 13:11:52 +08:00
pany
58cfdebcca wip: 顶部布局模式开发中 2023-07-19 09:05:55 +08:00
pany
310ab846fa fix: 修复顶部模式时 Logo 组件动态高度失效 2023-07-18 13:36:22 +08:00
pany
8a0b8c5b3a wip: 顶部布局模式开发中 2023-07-18 11:14:07 +08:00
pany
d5afb53c25 chore: vite 4.4.4 2023-07-14 17:56:09 +08:00
pany
bee0691d08 chore: 固定 vxe-table 4.4.1 && 升级 element-plus 2.3.8 和其他依赖 2023-07-14 14:49:05 +08:00
pany
3531250c51 fix: 纠正布局顶部模型时,标签栏右键菜单定位偏差 2023-07-12 13:34:28 +08:00
pany
444e04d19b chore: lint 2023-07-11 17:41:34 +08:00
pany
89a0bf43ea chore: 固定 vxe-table 4.4.1 && update other dependencies 2023-07-11 17:40:00 +08:00
pany
a4ab651282 chore: 项目发布 4.1.0-beta && 固定 vxe-table 4.4.1 && 固定 prettier 2.8.8 && update other dependencies 2023-07-07 15:30:18 +08:00
pany
9710c359af feat: 新增切换布局模式组件 2023-07-07 15:25:36 +08:00
pany
135cf8fc1e style: 优化混合模式下左侧菜单的颜色 2023-07-07 12:21:28 +08:00
pany
f9677d9a4a chore: 提示项目有破坏性更新 2023-07-06 14:10:14 +08:00
pany
025a69883f chore: 将 layout 重命名为 layouts 2023-07-06 13:14:44 +08:00
pany
442ae06c47 feat: 新增混合布局模式 2023-07-06 13:02:52 +08:00
pany
c3ad3c0ce1 fix: 缓存标签栏数据时,删除不必要的属性,防止 JSON.stringify 处理到循环引用 2023-06-30 18:05:30 +08:00
pany
e2ebc1da85 feat: 新增重置布局配置项功能 2023-06-30 18:03:19 +08:00
pany
f800a8d202 feat: 新增布局配置项缓存功能 2023-06-30 13:43:20 +08:00
pany
3fd5dbc412 feat: 新增标签栏缓存功能 2023-06-30 12:50:24 +08:00
pany
665bdbec7a feat: 路由白名单功能支持匹配路由 Name 2023-06-29 18:00:03 +08:00
pany
57d1df3f6e feat: 拓展全屏组件,支持内容区放大和内容区全屏两种模式 2023-06-29 12:43:01 +08:00
pany
83678f7370 chore: vxe-table 4.4.1 && update other dependencies 2023-06-27 13:56:32 +08:00
pany
4c0fcd0473 chore: v4.0.0 2023-06-21 12:38:16 +08:00
pany
85bf5a6948 perf: 优化 dark-blue 主题颜色 2023-06-21 12:37:19 +08:00
pany
29e0da32e2 chore: vxe-table 4.4.1 && update other dependencies 2023-06-21 12:28:58 +08:00
pany
8c6f1bd450 fix: 修复控制台告警 “Added non-passive event listener to a scroll-blocking 'wheel' event” 2023-06-21 12:24:54 +08:00
pany
ed1748a111 chore: node version 18.x 2023-06-21 12:02:50 +08:00
pany
9ff017e73a docs: 修改 app-loading.css 注释类型 2023-06-21 11:55:09 +08:00
pany
de4c43bda3 docs: 新增 npmrc 配置注释并添加到 eslint 忽略文件 2023-06-21 11:54:13 +08:00
pany
8f6062494f chore: 移除 vitest/global 配置 2023-06-21 11:52:12 +08:00
pany
996d6c8d53 chore: 移除按需引入相关代码 2023-06-21 11:48:17 +08:00
pany
d4f6f5b717 perf: 代码优化 layout/index 2023-06-21 11:47:16 +08:00
pany
07bde250ff perf: 代码优化 新增 global-components.d.ts 全局注册组件的类型文件 2023-06-21 10:28:04 +08:00
pany
75eadd2094 perf: 代码优化 统一将 props 运行时声明修改为类型声明 2023-06-20 18:40:18 +08:00
pany
d1c7480a4f perf: 代码优化 views/table 2023-06-20 09:13:47 +08:00
pany
d06985b8e6 perf: 代码优化 .editorconfig 2023-06-20 09:13:11 +08:00
pany
57c73cd9a8 chore: vxe-table 4.4.1 && update other dependencies 2023-06-17 15:35:27 +08:00
pany
a124cb2829 fix: 暂时固定 vxe-table 版本为 4.4.1,等待新版 vxe-table 颜色变量稳定后再适配多主题模式 2023-06-17 15:29:32 +08:00
pany
4d07d52b91 perf: 代码优化 views/permission 2023-06-17 14:34:39 +08:00
pany
be75b2b118 perf: 代码优化 views/login 2023-06-16 18:13:13 +08:00
pany
c265e64676 perf: 代码优化 views/dashboard 2023-06-16 18:12:53 +08:00
pany
e8d0f9ba9d perf: 代码优化 App.vue 2023-06-16 18:11:41 +08:00
pany
53694a35be fix: 启用 pnpm shamefully-hoist 配置,用以修复 vue 组件和 element plus 组件没有类型提示的问题 2023-06-16 13:21:01 +08:00
pany
1a6c2f174e chore: updates all dependencies 2023-06-15 13:48:57 +08:00
pany
e8faa26bd7 perf: 代码优化 layout/Sidebar 2023-06-15 13:42:58 +08:00
pany
8706398eb7 perf: 代码优化 layout/TagsView 2023-06-14 18:15:07 +08:00
pany
46747e7ce2 perf: 代码优化 layout/Settings 2023-06-14 18:14:47 +08:00
pany
9083700b3d perf: 代码优化 layout/NavigationBar 2023-06-14 18:14:11 +08:00
pany
710ff1cda7 perf: 代码优化 layout/Breadcrumb 2023-06-13 13:01:48 +08:00
pany
62e1a7a63d perf: 代码优化 layout/AppMain 2023-06-13 13:01:13 +08:00
pany
4dce9658ff perf: 代码优化 layout/hooks 2023-06-13 13:00:56 +08:00
pany
74f2f7b725 perf: 代码优化 layout/index 2023-06-13 13:00:35 +08:00
pany
3953269006 chore: updates all dependencies && pnpm 8.6.2 2023-06-12 18:22:08 +08:00
pany
44d7ec87c0 perf: 代码优化 LICENSE 2023-06-12 18:20:54 +08:00
pany
47fff7ee14 perf: 代码优化 tests/demo.test 2023-06-12 18:20:24 +08:00
pany
4caa0edf0a perf: 代码优化 prettier 2023-06-12 18:19:21 +08:00
pany
69492c863a chore: updates all dependencies 2023-06-08 18:30:16 +08:00
pany
1d2a4ad9b6 perf: 代码优化 utils/validate 和 utils/service 2023-06-08 13:39:06 +08:00
pddzl
b40df6c6ad
feat: 新增 service.ts 对二进制数据的处理逻辑 (#86) 2023-06-08 09:45:23 +08:00
pany
246f29e8c3 perf: 代码优化 utils/validate 2023-06-07 12:38:58 +08:00
pany
dfff053ae8 perf: 代码优化 utils/service 2023-06-06 18:15:29 +08:00
pany
4b779ed010 chore: updates all dependencies && unocss 0.53.0 2023-06-05 16:10:13 +08:00
pany
3feceb063b style: 优化滚动条交汇处 2023-06-05 11:42:59 +08:00
pany
b2ee4348b7 style: 优化多主题下滚动条样式 2023-06-05 11:36:03 +08:00
CHENZL
155c3e247b
feat: 优化滚动条样式 (#84) 2023-06-05 11:19:27 +08:00
pany
4c08e5f190 perf: 代码优化 utils/permission 和 directives/permission 2023-06-01 18:00:08 +08:00
pany
8520fd3df2 perf: 代码优化 utils/index 2023-06-01 16:09:58 +08:00
pany
dc117459ad chore: updates all dependencies 2023-06-01 09:28:55 +08:00
pany
b2da13b34a perf: 代码优化 router/index 2023-06-01 09:26:53 +08:00
pany
bf29e0e52a perf: 代码优化 utils/cache 2023-06-01 09:24:37 +08:00
pany
51916b6bc0 perf: 代码优化 store/modules/user 2023-05-31 18:23:47 +08:00
pany
4dd30c8233 chore: updates all dependencies && vxe-table 4.4.1 && pnpm 8.6.0 2023-05-30 18:08:09 +08:00
pany
836818aedd perf: 代码优化 store/modules/tags-view 2023-05-26 18:16:44 +08:00
pany
32fae72f4f perf: 代码优化 store/modules/settings 2023-05-26 13:04:12 +08:00
CHENZL
7020a864f6
feat: 补全深蓝模式下,primary 颜色各种状态 (#81) 2023-05-25 18:36:20 +08:00
pany
d3673e431e perf: 代码优化 统一 constants 目录下文件名风格 2023-05-25 09:22:10 +08:00
pany
d090c294ae perf: 代码优化 store/modules/permission 2023-05-24 18:25:49 +08:00
pany
5c09716fa5 perf: 代码优化 store/modules/app 2023-05-24 18:24:06 +08:00
pany
2d36652540 chore: updates all dependencies 2023-05-24 09:40:41 +08:00
pany
c759cc2c00 chore: updates all dependencies 2023-05-23 18:22:33 +08:00
pany
189d4a2c1c perf: 代码优化 public/app-loading 2023-05-23 18:15:08 +08:00
pany
8a24b6d219 perf: 代码优化 components/ThemeSwitch 2023-05-22 14:04:31 +08:00
pany
d22a44396d perf: 代码优化 components/Screenfull 2023-05-22 13:46:45 +08:00
pany
d50cba28af perf: 代码优化 components/Screenfull 和 hooks/useFullscreenLoading 2023-05-22 13:44:48 +08:00
pany
d66528f40a perf: 代码优化 components/Screenfull 2023-05-22 13:35:04 +08:00
pany
9c7fd02fb7 perf: 代码优化 components/Notify 2023-05-22 09:14:03 +08:00
pany
2a79acc59a perf: 代码优化 hooks/useFullscreenLoading 2023-05-21 16:02:02 +08:00
pany
17dc453350 perf: 代码优化 constants/cacheKey 2023-05-21 11:49:03 +08:00
pany
002bbdec8f perf: 代码优化 所有的 enum 类型命名 2023-05-21 10:47:42 +08:00
pany
e4d9673a2b perf: 代码优化 所有的 type 类型命名 2023-05-21 10:42:50 +08:00
pany
05712f4adc perf: 代码优化 所有的 interface 类型命名 2023-05-21 09:51:41 +08:00
pany
6cca254335 chore: updates all dependencies && element-plus 2.3.5 2023-05-20 21:13:22 +08:00
pany
63844a8f2e perf: 代码优化 hooks/useTheme 2023-05-19 21:02:20 +08:00
pany
ee2ee7e1da perf: 代码优化 hooks/usePagination 2023-05-19 20:47:52 +08:00
pany
9618a17ab5 perf: 代码优化 directives/permission 2023-05-19 20:02:02 +08:00
pany
94d96c95e7 chore: updates all dependencies 2023-05-19 18:38:51 +08:00
pany
245d679b8d chore: updates all dependencies && vue 3.3.4 2023-05-18 19:05:03 +08:00
pany
87f7e23d63 perf: 优化 app-loading 动画 2023-05-17 21:05:09 +08:00
pany
857b95043b perf: 优化路由过渡动画 2023-05-17 20:27:39 +08:00
pany
7cae618b50 chore: updates all dependencies 2023-05-17 20:24:05 +08:00
pany
9c1b59389c chore: updates all dependencies 2023-05-16 17:50:17 +08:00
pany
b69abd8e1a chore: updates all dependencies 2023-05-15 13:45:09 +08:00
pany
362ce6ed56 docs: 优化 element-plus.d.ts 文件注释 2023-05-12 18:59:39 +08:00
pany
bc7dae8435 chore: 引入 Element Plus Global Components 类型文件(解决 Vue Router 4.2.0 版本导致 Element Plus 组件没有类型提示的问题) && updates all dependencies && pnpm 8.5.0 2023-05-12 18:43:45 +08:00
pany
59ef19d9ea chore: 适配 vue 3.3 && updates all dependencies 2023-05-11 18:20:48 +08:00
pany
469cfa7fb5 style: 统一 props 命名风格 2023-05-10 18:15:18 +08:00
pany
388b596af6 chore: updates all dependencies 2023-05-09 10:26:44 +08:00
pany
eb9c7acde2 chore: updates all dependencies 2023-05-04 18:01:57 +08:00
pany
c822e29cb7 chore: updates all dependencies 2023-04-28 09:35:16 +08:00
pany
3bd28ad4b6 chore: 固定 vue-tsc 1.4.4 2023-04-27 18:16:36 +08:00
pany
ed7d27b360 chore: updates all dependencies 2023-04-27 17:44:57 +08:00
pany
2ec928434b docs: 修正权限函数报错提示文案 2023-04-26 16:51:11 +08:00
pany
a801a06edd chore: updates all dependencies && element-plus 2.3.4 2023-04-24 15:27:24 +08:00
pany
cb872ef049 chore: updates all dependencies 2023-04-23 09:49:00 +08:00
pany
4cb8080328 chore: updates all dependencies && vue-tsc 1.4.2 2023-04-22 10:27:59 +08:00
captain-hema
93b20e8dcb
feat: 深蓝主题下对 Element Plus 颜色的覆盖进行扩展 (#57) 2023-04-21 18:34:48 +08:00
pany
286d4388ae chore: updates all dependencies && vite 4.3.0 && pnpm 2023-04-20 17:21:58 +08:00
李成元
ae96836baa
feat: router 准备就绪后再挂载 (#71) 2023-04-14 14:39:19 +08:00
pany
55569f791c chore: updates all dependencies 2023-04-12 18:10:13 +08:00
pany
ace63efc35 docs: update readme.md 2023-04-12 18:07:07 +08:00
pany
79ec294070 chore: pnpm 8.x 2023-04-11 13:31:16 +08:00
pany
acd7e21b57 chore: v3.4.0-beta && 升级所有依赖 && 引入 @vue-macros/volar 2023-04-11 12:56:01 +08:00
pany
2be53ddfc2 fix: NODE_ENV=production is not supported in the .env file 2023-04-11 12:33:03 +08:00
pany
33ec2e6ec2 fix: 临时解决新版 vscode 或 ts 5.x 时找不到 unplugin-vue-define-options/vite 类型导致的报错 2023-04-11 12:30:58 +08:00
pany
b1855e80d1 ci: github workflows 2023-03-30 09:26:27 +08:00
pany
7ed338734c ci: github workflows 2023-03-30 09:19:54 +08:00
jiangyitao
7a8566244d
fix: 修复隐藏 TagsView 后,路由 keepAlive: true 无效的问题 (#66) 2023-03-30 09:13:00 +08:00
betterwusy
3b47cecfc9
refactor: useTheme 中使用 initTheme 方法显式执行初始化 (#65) 2023-03-28 17:48:39 +08:00
betterwusy
6d0ef20b0b
refactor: 使用 watchEffect 来收集主题相关的副作用 (#64) 2023-03-28 14:16:17 +08:00
pany
9e0bf64254 fix: 修复用 img src 显示 svg 图片时 src 地址指向对象的问题 2023-03-27 10:56:39 +08:00
pany
955b72eda7
chore: version 3.3.4 2023-03-23 15:31:22 +08:00
pany
9362cb704f feat: 'tag' outside the viewport is automatically moved to the viewable area 2023-03-23 11:27:07 +08:00
pany
87d19b0446 types: 统一导入类型的方式 2023-03-23 11:20:12 +08:00
_island
0e9eaafeea
feat: "tags-view" support wheel scroll (#62) 2023-03-17 16:16:52 +08:00
_island
9d473cc2ba
fix: menu styles (#61) 2023-03-16 17:42:32 +08:00
pany
c083e73829 chore: updates all dependencies 2023-03-16 16:22:28 +08:00
pany
036dae5661 chore: updates all dependencies 2023-03-09 14:23:19 +08:00
pany
e9ac44cd97 docs: 添加手摸手教程链接地址 2023-03-08 10:08:13 +08:00
pany
6b17fbd2b5 chore: updates all dependencies 2023-03-08 10:01:05 +08:00
pany
32101689bf fix: 修复角色数组为空时导致路由守卫无限循环的问题 2023-03-01 10:23:51 +08:00
pany
4a58eb8e03 types: 优化表格数据类型 2023-02-28 14:13:45 +08:00
pany
5d641ace1f refactor: 优化 useTheme 代码结构 2023-02-28 14:12:38 +08:00
pany
ebfa35d92f refactor: 更改 "欢迎 star 标语" 代码位置 2023-02-28 14:11:39 +08:00
pany
3dadc9c50f chore: updates all dependencies 2023-02-28 09:50:43 +08:00
pany
e798097d24 chore: updates all dependencies 2023-02-24 14:30:57 +08:00
pany
2df2d8512d chore: updates all dependencies 2023-02-22 16:14:35 +08:00
pany
15fa81f5fe feat: 新增 keep alive 缓存功能 2023-02-22 15:53:04 +08:00
pany
95b9691b20 chore: updates all dependencies 2023-02-20 14:51:34 +08:00
pany
b2bb559258 test: 添加单元测试示例 2023-02-16 17:36:47 +08:00
pany
8a2fa71e97 test: 集成 vitest 单元测试框架 2023-02-16 14:38:21 +08:00
pany
11a3a2669d chore: updates all dependencies 2023-02-16 10:41:23 +08:00
ClariS
18d70cfa51
types: 将类型和接口定义进行拆分 (#50)
* types: 将类型和接口定义进行拆分
2023-02-15 14:23:30 +08:00
captain-hema
d3d97928aa
fix: Fix folder naming errors (#49) 2023-02-15 10:50:40 +08:00
pany
3b032e62a7 chore: updates all dependencies 2023-02-13 09:19:22 +08:00
pany
42a9967952 types: 优化 useFullscreenLoading 的类型(闭包的泛型) 2023-02-08 11:31:56 +08:00
pany
e878399d9e feat: 新增两份用于 vscode 一键生成 vue3 代码的 .code-snippets 文件 2023-02-07 17:59:44 +08:00
pany
0215193efb chore: updates all dependencies 2023-02-07 11:38:46 +08:00
pany
8ac1158d61 chore: updates all dependencies 2023-02-02 13:38:08 +08:00
pany
61675f74ba feat: Axios 响应数据支持通过泛型推导 2023-02-02 11:31:17 +08:00
pany
f3a62504d6 perf: 优化 useFullscreenLoading 代码 2023-02-02 11:29:34 +08:00
pany
e270308f10 refactor: 重写 useFetchSelect 与 useFullscreenLoading 2023-02-01 18:09:44 +08:00
pany
a0058a174a chore: updates all dependencies 2023-02-01 10:28:18 +08:00
ClariS
f4fa12332d
style: 优化全屏 loading hook 命名 (#44) 2023-01-17 11:11:56 +08:00
ClariS
1161939616
feat: 新增 Select V2 示例 (#43) 2023-01-17 10:34:18 +08:00
pany
e43c93c8e9 docs: 添加赞助者列表 2023-01-10 17:47:31 +08:00
pany
da78d1b915 chore: updates all dependencies 2023-01-09 17:03:56 +08:00
ClariS
b9b917cf5e
feat: 封装全屏 loading hook (#41) 2023-01-09 16:39:38 +08:00
ClariS
9860c9a8e1
feat: 封装下拉框 hook (#40) 2023-01-05 17:15:18 +08:00
pany
93a84b5fec docs: 新增项目预览图 2022-12-30 17:55:09 +08:00
pany
ac714f261f chore: updates all dependencies 2022-12-30 17:19:41 +08:00
Defined
7feb1d515f
feat: 系统布局配置下增加灰色模式、色弱模式 (#39) 2022-12-08 18:06:01 +08:00
pany
9731b877b5 chore: updates all dependencies 2022-12-05 09:45:59 +08:00
pany
01780acc63 chore: updates all dependencies 2022-11-28 19:28:36 +08:00
pany
94205283a9 docs: 优化注释 2022-11-28 19:28:08 +08:00
pany
c06d65ff00 fix: 修复 el-scrollbar api 变动导致的获取滚动可视区宽度失败问题 2022-11-21 10:54:29 +08:00
pany
5a76ec4df3 chore: updates all dependencies 2022-11-21 10:52:09 +08:00
pany
53660d2f54 docs: "README" syntax error 2022-11-16 09:20:38 +08:00
pany
877ce5fefa chore: updates all dependencies 2022-11-16 09:15:54 +08:00
pany
08d275793b refactor: 重构全屏组件,使其更好地支持内容区全屏 2022-11-12 13:53:32 +08:00
Sunny-117
9557c5679e
feat: add full screen in area feature (#30) 2022-11-12 10:17:05 +08:00
pany
907dc21d55 chore: updates all dependencies 2022-11-07 17:47:09 +08:00
pany
8389a52af9 mod: 优化首页和项目外链 2022-11-07 17:46:28 +08:00
pany
dc108fdbff docs: 添加欢迎 star 标语 2022-11-07 17:44:48 +08:00
pany
675bd11fa9 fix: 修复表格分页组件在移动端页面超出页面的问题 2022-11-02 17:16:01 +08:00
pany
017ebf2f92 feat: 头像旁新增用户名显示 2022-11-02 16:57:43 +08:00
pany
4e2ffa38f5 feat: 添加消息通知图标隐藏功能 2022-10-31 15:35:51 +08:00
pany
d8f47cf91e chore: updates all dependencies 2022-10-31 11:46:37 +08:00
pany
3236230573 perf: 右侧面板按钮定位方式改为 fixed 2022-10-31 11:46:14 +08:00
pany
81d5ea1e61 feat: 新增消息通知列表空状态 2022-10-31 11:42:36 +08:00
Defined
0e0453eab0
feat: 消息通知列表 (#24) 2022-10-31 11:19:22 +08:00
pany
7f8fca14d8 wip: 消息通知 2022-10-28 18:05:16 +08:00
pany
fa55b36d3e style: 优化 table 示例样式 2022-10-28 14:27:35 +08:00
pany
da63f9e204 wip: 消息通知 2022-10-27 18:21:25 +08:00
pany
8140c25601 docs: 优化 2022-10-27 18:20:57 +08:00
264 changed files with 13575 additions and 7485 deletions

View File

@ -1,13 +1,24 @@
# 配置项文档https://editorconfig.org修改配置后重启编辑器
## 告知 EditorConfig 插件,当前即是根文件
root = true
## 适用全部文件
[*]
### 设置字符集
charset = utf-8
### 缩进风格 space | tab建议 space
indent_style = space
### 缩进的空格数
indent_size = 2
### 换行符类型 lf | cr | crlf一般都是设置为 lf
end_of_line = lf
### 是否在文件末尾插入空白行
insert_final_newline = true
### 是否删除一行中的前后空格
trim_trailing_whitespace = true
## 适用 .md 文件
[*.md]
insert_final_newline = false
trim_trailing_whitespace = false

7
.env Normal file
View File

@ -0,0 +1,7 @@
# 所有环境的环境变量(命名必须以 VITE_ 开头)
## 项目标题
VITE_APP_TITLE = V3 Admin Vite
## 路由模式 hash 或 html5
VITE_ROUTER_HISTORY = hash

View File

@ -1,13 +1,7 @@
# 请勿改动这一项,该项也不可以通过 import.meta.env.NODE_ENV 调用
NODE_ENV = development
# 开发环境的环境变量(命名必须以 VITE_ 开头)
# 下面是自定义的环境变量,可以修改(命名必须以 VITE_ 开头)
## 后端接口地址(如果解决跨域问题采用反向代理就只需写相对路径)
VITE_BASE_URL = /api/v1
# 后端接口公共路径(如果解决跨域问题采用反向代理就只需写公共路径)
VITE_BASE_API = '/api/v1'
# 路由模式 hash 或 html5
VITE_ROUTER_HISTORY = 'hash'
# 开发环境地址前缀(一般 '/''./' 都可以)
VITE_PUBLIC_PATH = '/'
## 开发环境域名和静态资源公共路径(一般 / 或 ./ 都可以)
VITE_PUBLIC_PATH = /

View File

@ -1,13 +1,7 @@
# 请勿改动这一项,该项也不可以通过 import.meta.env.NODE_ENV 调用
NODE_ENV = production
# 生产环境的环境变量(命名必须以 VITE_ 开头)
# 下面是自定义的环境变量,可以修改(命名必须以 VITE_ 开头)
## 后端接口地址(如果解决跨域问题采用 CORS 就需要写绝对路径)
VITE_BASE_URL = https://apifoxmock.com/m1/2930465-2145633-default/api/v1
# 后端接口公共路径(如果解决跨域问题采用 CORS 就需要写全路径)
VITE_BASE_API = 'https://mock.mengxuegu.com/mock/63218b5fb4c53348ed2bc212/api/v1'
# 路由模式 hash 或 html5
VITE_ROUTER_HISTORY = 'hash'
# 打包路径(就是网站前缀,例如部署到 https://un-pany.github.io/v3-admin-vite/ 域名下,就需要填写 /v3-admin-vite/
VITE_PUBLIC_PATH = '/v3-admin-vite/'
## 打包构建静态资源公共路径(例如部署到 https://un-pany.github.io/v3-admin-vite/ 域名下就需要填写 /v3-admin-vite/
VITE_PUBLIC_PATH = /v3-admin-vite/

View File

@ -1,13 +1,7 @@
# 请勿改动这一项,该项也不可以通过 import.meta.env.NODE_ENV 调用
NODE_ENV = production
# 预发布环境的环境变量(命名必须以 VITE_ 开头)
# 下面是自定义的环境变量,可以修改(命名必须以 VITE_ 开头)
## 后端接口地址(如果解决跨域问题采用 CORS 就需要写绝对路径)
VITE_BASE_URL = https://apifoxmock.com/m1/2930465-2145633-default/api/v1
# 后端接口公共路径(如果解决跨域问题采用 CORS 就需要写全路径)
VITE_BASE_API = 'https://mock.mengxuegu.com/mock/63218b5fb4c53348ed2bc212/api/v1'
# 路由模式 hash 或 html5
VITE_ROUTER_HISTORY = 'hash'
#打包路径(就是网站前缀,例如部署到 https://un-pany.github.io/v3-admin-vite/ 域名下,就需要填写 /v3-admin-vite/
VITE_PUBLIC_PATH = '/v3-admin-vite/'
## 打包构建静态资源公共路径(例如部署到 https://un-pany.github.io/ 域名下就需要填写 /
VITE_PUBLIC_PATH = /

View File

@ -1,7 +0,0 @@
# Eslint 会忽略的文件
.DS_Store
node_modules
dist
dist-ssr
*.local

View File

@ -1,83 +0,0 @@
module.exports = {
root: true,
env: {
browser: true,
node: true,
es6: true
},
globals: {
// script setup
defineProps: "readonly",
defineEmits: "readonly",
defineExpose: "readonly",
withDefaults: "readonly"
},
extends: [
"plugin:vue/vue3-essential",
"eslint:recommended",
"@vue/typescript/recommended",
"@vue/prettier",
"@vue/eslint-config-typescript"
// unplugin-auto-import 自动生成的文件
// "./types/.eslintrc-auto-import.json"
],
parser: "vue-eslint-parser",
parserOptions: {
parser: "@typescript-eslint/parser",
ecmaVersion: 2020,
sourceType: "module",
jsxPragma: "React",
ecmaFeatures: {
jsx: true,
tsx: true
}
},
rules: {
// TS
"@typescript-eslint/no-explicit-any": "off",
"no-debugger": "off",
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/ban-types": "off",
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/no-non-null-assertion": "off",
"@typescript-eslint/no-unused-vars": [
"error",
{
argsIgnorePattern: "^_",
varsIgnorePattern: "^_"
}
],
"no-unused-vars": [
"error",
{
argsIgnorePattern: "^_",
varsIgnorePattern: "^_"
}
],
// Vue
"vue/no-v-html": "off",
"vue/require-default-prop": "off",
"vue/require-explicit-emits": "off",
"vue/multi-word-component-names": "off",
"vue/html-self-closing": [
"error",
{
html: {
void: "always",
normal: "always",
component: "always"
},
svg: "always",
math: "always"
}
],
// Prettier
"prettier/prettier": [
"error",
{
endOfLine: "auto"
}
]
}
}

1
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1 @@
custom: https://github.com/un-pany/v3-admin-vite/issues/69

View File

@ -14,18 +14,18 @@ jobs:
with:
persist-credentials: false
- name: Setup Node.js 16.13.0
- name: Setup Node.js
uses: actions/setup-node@master
with:
node-version: "16.13.0"
node-version: 22.12.0
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: latest
version: 10.2.0
- name: Build
run: pnpm install && pnpm build:prod
run: pnpm install && pnpm build
- name: Deploy
uses: JamesIves/github-pages-deploy-action@releases/v3

27
.github/workflows/release.yml vendored Normal file
View File

@ -0,0 +1,27 @@
name: Release
permissions:
contents: write
on:
push:
tags:
- "v*"
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set node
uses: actions/setup-node@v4
with:
registry-url: https://registry.npmjs.org/
node-version: lts/*
- run: npx changelogithub
env:
GITHUB_TOKEN: ${{ secrets.V3_ADMIN_VITE }}

30
.gitignore vendored
View File

@ -1,34 +1,18 @@
# Git 会忽略的文件
.DS_Store
node_modules
# Common
dist
dist-ssr
node_modules
.eslintcache
vite.config.*.timestamp*
# MacOS
.DS_Store
# Local env files
*.local
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
# Editor directories and files
.vscode/*
!.vscode/extensions.json
!.vscode/settings.json
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
# Use the PNPM
# Use the pnpm
package-lock.json
yarn.lock

View File

@ -1,4 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
# 全局 ts 类型检查(此操作会增加 git commit 时长)
npx vue-tsc
# 执行 lint-staged 中配置的任务
npx lint-staged

5
.npmrc Normal file
View File

@ -0,0 +1,5 @@
# China mirror of npm
registry = https://registry.npmmirror.com
# 安装依赖时锁定版本号
save-exact = true

View File

@ -1,8 +0,0 @@
# Prettier 会忽略的文件
.DS_Store
node_modules
dist
dist-ssr
*.local
*.d.ts

View File

@ -1,10 +1,10 @@
{
"recommendations": [
"vue.volar",
"editorconfig.editorconfig",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"vue.vscode-typescript-vue-plugin",
"vue.volar",
"antfu.unocss"
"antfu.unocss",
"vitest.explorer",
"wiensss.region-highlighter"
]
}

15
.vscode/hook.code-snippets vendored Normal file
View File

@ -0,0 +1,15 @@
{
"Vue3 Composable 代码结构一键生成": {
"prefix": "Vue3 Composable",
"body": [
"const refName1 = ref<string>(\"这是一个响应式变量\")\n",
"export function useName() {",
"\tconst refName2 = ref<string>(\"这是一个响应式变量\")\n",
"\tconst fnName = () => {}\n",
"\treturn { refName1, refName2, fnName }",
"}",
"$1"
],
"description": "Vue3 Composable"
}
}

75
.vscode/settings.json vendored
View File

@ -1,30 +1,53 @@
{
"editor.tabSize": 2,
// Use workspace TypeScript version
"typescript.tsdk": "node_modules/typescript/lib",
// Disable the default formatter, use eslint instead
"prettier.enable": false,
"editor.formatOnSave": false,
// Auto fix
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
"source.fixAll.eslint": "explicit",
"source.organizeImports": "never"
},
"[vue]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[jsonc]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[html]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[css]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[scss]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
// Silent the stylistic rules in you IDE, but still auto fix them
"eslint.rules.customizations": [
{ "rule": "style/*", "severity": "off", "fixable": true },
{ "rule": "format/*", "severity": "off", "fixable": true },
{ "rule": "*-indent", "severity": "off", "fixable": true },
{ "rule": "*-spacing", "severity": "off", "fixable": true },
{ "rule": "*-spaces", "severity": "off", "fixable": true },
{ "rule": "*-order", "severity": "off", "fixable": true },
{ "rule": "*-dangle", "severity": "off", "fixable": true },
{ "rule": "*-newline", "severity": "off", "fixable": true },
{ "rule": "*quotes", "severity": "off", "fixable": true },
{ "rule": "*semi", "severity": "off", "fixable": true }
],
// Enable eslint for all supported languages
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact",
"vue",
"html",
"markdown",
"json",
"jsonc",
"yaml",
"toml",
"xml",
"gql",
"graphql",
"astro",
"svelte",
"css",
"less",
"scss",
"pcss",
"postcss"
]
}

16
.vscode/vue.code-snippets vendored Normal file
View File

@ -0,0 +1,16 @@
{
"Vue3 SFC 代码结构一键生成": {
"prefix": "Vue3 SFC",
"body": [
"<script lang=\"ts\" setup></script>\n",
"<template>",
"\t<div class=\"app-container\">",
"\t\t...",
"\t</div>",
"</template>\n",
"<style lang=\"scss\" scoped></style>",
"$1"
],
"description": "Vue3 SFC"
}
}

View File

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2022 pany <https://github.com/pany-ang>
Copyright (c) 2022-present pany <https://github.com/pany-ang>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

265
README.md
View File

@ -1,138 +1,227 @@
<div align="center">
<img alt="V3-Admin-Vite-Logo" width="120" height="120" src="./src/assets/layout/logo.png">
<img alt="logo" width="120" height="120" src="./src/common/assets/images/layouts/logo.png">
<h1>V3 Admin Vite</h1>
<span>English | <a href="./README.zh-CN.md">中文</a></span>
</div>
## ⚡ Introduction
[![github release](https://img.shields.io/github/v/release/un-pany/v3-admin-vite?style=flat)](https://github.com/un-pany/v3-admin-vite/releases)
[![github stars](https://img.shields.io/github/stars/un-pany/v3-admin-vite?style=flat)](https://github.com/un-pany/v3-admin-vite/stargazers)
[![gitee stars](https://gitee.com/un-pany/v3-admin-vite/badge/star.svg)](https://gitee.com/un-pany/v3-admin-vite/stargazers)
v3-admin-vite is a free and open source middle and background management system basic solution, based on mainstream framework such as Vue3, TypeScript, Element Plus, Pinia and Vite.
<b>English | <a href="./README.zh-CN.md">中文</a></b>
- Vue-Cli 5.x: [v3-admin](https://github.com/un-pany/v3-admin)
- Electron desktop: [v3-electron-vite](https://github.com/un-pany/v3-electron-vite)
## Introduction
## Feature
V3 Admin Vite is a well-crafted backend management system template, built with popular technologies such as Vue3, Vite, TypeScript, and Element Plus
- **Vue3**The latest Vue3 composition API using Vue3 + script setup
- **Element Plus**Vue3 version of Element UI
- **Pinia**: An alternative to Vuex in Vue3
- **Vite**Really fast
- **Vue Router**router
- **TypeScript**JavaScript With Syntax For Types
- **PNPM**Faster, disk space saving package management tool
- **Scss**Consistent with Element Plus
- **CSS variable**Mainly controls the layout and color of the item
- **ESlint**Code verification
- **Prettier** Code formatting
- **Axios**: Promise based HTTP client (encapsulated)
- **UnoCSS**: Real-time atomized CSS engine with high performance and flexibility
- **Annotation**Each configuration item is written with as detailed comments as possible
- **Mobile Compatible**: The layout is compatible with mobile page resolution
## Notifications
## Functions
> [!NOTE]
> Powered by love! All source code is free and open-source. If you find it helpful, feel free to give a star to support!
- **User management**: log in, log out of the demo
- **Authority management**: Built-in page permissions (dynamic routing), instruction permissions, permission functions
- **Multiple Environments**: Development, Staging, Production
- **Multiple themes**: Normal, Dark, Dark Blue, theme modes
- **Error page**: 403, 404
- **Dashboard**: Display different Dashboard pages according to different users
- **Other functions**SVG, Dynamic Sidebar, Dynamic Breadcrumb Navigation, Tabbed Navigation, Screenfull, Adaptive Shrink Sidebar
> [!IMPORTANT]
> Welcome to experience the brand-new version 5.0, currently in the beta stage. It will be a masterpiece!
## 📚 Document
> [!WARNING]
> Version 4.x will no longer be maintained unless there are critical bugs! [Click to switch to the 4.x branch](https://github.com/un-pany/v3-admin-vite/tree/4.x)
[Chinese documentation](https://juejin.cn/post/7089377403717287972)
> [!TIP]
> Paid services are officially launched! If you dont want to do it yourself but want to remove TS or other modules, try the lazy package! [Click to check it out](https://github.com/un-pany/v3-admin-vite/issues/225)
## Gitee repository
> [!TIP]
> If you have mobile web app needs, try the new open-source template. [MobVue](https://github.com/un-pany/mobvue)
[Gitee](https://gitee.com/un-pany/v3-admin-vite)
## Usage
## Online preview
<details>
<summary>Recommended Environment</summary>
| Location | account | Link |
| ------------ | ------------------- | ----------------------------------------------- |
| github-pages | `admin` or `editor` | [Link](https://un-pany.github.io/v3-admin-vite) |
<br>
## 🚀 Development
- Latest version of `Visual Studio Code`
- Install the recommended plugins in the `.vscode/extensions.json` file
- `node` 20.x or 22+
- `pnpm` 9.x or 10+
</details>
<details>
<summary>Local Development</summary>
<br>
```bash
# configure
1. installation of the recommended plugins in the .vscode directory
3. node version 16+
4. pnpm version 7.x
# clone
# Clone the project
git clone https://github.com/un-pany/v3-admin-vite.git
# enter the project directory
# Enter the project directory
cd v3-admin-vite
# install dependencies
# Install dependencies
pnpm i
# start the service
# Start the development server
pnpm dev
```
## ✔️ Preview
</details>
<details>
<summary>Build</summary>
<br>
```bash
# stage environment
pnpm preview:stage
# Build for the staging environment
pnpm build:staging
# prod environment
pnpm preview:prod
# Build for the production environment
pnpm build
```
## 📦️ Multi-environment packaging
</details>
<details>
<summary>Local Preview</summary>
<br>
```bash
# build the stage environment
pnpm build:stage
# build the prod environment
pnpm build:prod
# Execute the build command first to generate the dist directory, then run the preview command
pnpm preview
```
## 🔧 Code formatting check
</details>
<details>
<summary>Code Check</summary>
<br>
```bash
# Code linting and formatting
pnpm lint
# Unit tests
pnpm test
```
## Git commit specification reference
</details>
- `feat` add new functions
- `fix` Fix issues/bugs
- `perf` Optimize performance
- `style` Change the code style without affecting the running result
- `refactor` Re-factor code
- `revert` Undo changes
- `test` Test related, does not involve changes to business code
- `docs` Documentation and Annotation
- `chore` Updating dependencies/modifying scaffolding configuration, etc.
- `workflow` Work flow Improvements
- `ci` CICD
- `types` Type definition
- `wip` In development
<details>
<summary>Commit Guidelines</summary>
## 💕 Contributors
<br>
Thanks you to all the contributors!
`feat` New feature
`fix` Bug fix
`perf` Performance improvement
`refactor` Code refactoring
`docs` Documentation and comments
`types` Type-related changes
`test` Unit tests related
`ci` Continuous integration, workflows
`revert` Revert changes
`chore` Chores (update dependencies, modify configurations, etc)
</details>
## Links
**Online Preview**: [github-pages](https://un-pany.github.io/v3-admin-vite)
**Chinese Documentation**: [link](https://juejin.cn/post/7089377403717287972)
**Zero to Hero Tutorial**: [link](https://juejin.cn/column/7207659644487139387)
**Mobile Web App**: [mobvue](https://github.com/un-pany/mobvue)
**Electron Desktop Version**: [v3-electron-vite](https://github.com/un-pany/v3-electron-vite)
**Chinese Repository**: [gitee](https://gitee.com/un-pany/v3-admin-vite)
**Optional Group**: [check how to join](https://github.com/un-pany/v3-admin-vite/issues/191)
**Donations**: [buy a coffee for the author](https://github.com/un-pany/v3-admin-vite/issues/69)
**Releases & Changelog**: [releases](https://github.com/un-pany/v3-admin-vite/releases)
## Features
**Simplified structure**: No complex encapsulation, no complicated type gymnastics, just enough to meet the needs
**Detailed comments**: Every configuration item comes with as detailed comments as possible
**Latest dependencies**: Keeps all third-party dependencies up to date
**Consistency**: Unified code style, naming conventions, and comment style
## Built-in Features
**User Management**: Login, logout demonstration
**Permission Management**: Page-level permissions (dynamic routing), button-level permissions (permission directives, permission functions), route guards
**Multiple Environments**: Development, staging, and production environments
**Multiple Themes**: Normal, dark, and deep blue themes
**Multiple Layouts**: Left-side, top, and hybrid layouts
**Homepage**: Different dashboard pages for different users
**Error Pages**: 403, 404
**Mobile Compatibility**: Layouts compatible with mobile screen resolutions
**Others**: SVG sprite sheet, dynamic sidebar, dynamic breadcrumbs, tab navigation, content zoom and fullscreen, composable functions
## Tech Stack
**Vue3**: Vue3 + script setup with the latest Vue3 Composition API
**Element Plus**: The Vue3 version of Element UI
**Pinia**: The legendary Vuex5
**Vite**: Really fast
**Vue Router**: The routing system
**TypeScript**: A superset of JavaScript
**pnpm**: A faster, disk-space-saving package manager
**Scss**: Consistent with Element Plus
**CSS Variables**: Primarily controls layout and color in the project
**ESLint**: Code linting and formatting
**Axios**: Sends network requests
**UnoCSS**: A high-performance, flexible atomic CSS engine
## Project Preview Image
![preview](./src/common/assets/images/docs/preview.png)
## Contributors
A big thank you to all the contributors!
<a href="https://github.com/un-pany/v3-admin-vite/graphs/contributors">
<img src="https://contrib.rocks/image?repo=un-pany/v3-admin-vite" />
<img src="https://contrib.rocks/image?repo=un-pany/v3-admin-vite">
</a>
## Group
## License
QQ group1014374415 (left) && add me on WeChatInvite you to join WeChat group (right)
![qq.png](./src/assets/docs/qq.png)
![wechat.png](./src/assets/docs/wechat.png)
## 📄 License
[MIT](./LICENSE)
Copyright (c) 2022 [pany](https://github.com/pany-ang)
[MIT](./LICENSE) License © 2022-PRESENT [pany](https://github.com/pany-ang)

View File

@ -1,66 +1,55 @@
<div align="center">
<img alt="V3-Admin-Vite-Logo" width="120" height="120" src="./src/assets/layout/logo.png">
<img alt="logo" width="120" height="120" src="./src/common/assets/images/layouts/logo.png">
<h1>V3 Admin Vite</h1>
<span><a href="./README.md">English</a> | 中文</span>
</div>
## ⚡ 简介
[![github release](https://img.shields.io/github/v/release/un-pany/v3-admin-vite?style=flat)](https://github.com/un-pany/v3-admin-vite/releases)
[![github stars](https://img.shields.io/github/stars/un-pany/v3-admin-vite?style=flat)](https://github.com/un-pany/v3-admin-vite/stargazers)
[![gitee stars](https://gitee.com/un-pany/v3-admin-vite/badge/star.svg)](https://gitee.com/un-pany/v3-admin-vite/stargazers)
一个免费开源的中后台管理系统基础解决方案,基于 Vue3、TypeScript、Element Plus、Pinia 和 Vite 等主流技术.
<b><a href="./README.md">English</a> | 中文</b>
- Vue-Cli 5.x 版: [v3-admin](https://github.com/un-pany/v3-admin)
- Electron 桌面版: [v3-electron-vite](https://github.com/un-pany/v3-electron-vite)
## 简介
## 特性
V3 Admin Vite 是一个精心制作的后台管理系统模板,基于 Vue3、Vite、TypeScript、Element Plus 等主流技术
- **Vue3**:采用 Vue3 + script setup 最新的 Vue3 组合式 API
- **Element Plus**Element UI 的 Vue3 版本
- **Pinia**: 传说中的 Vuex5
- **Vite**:真的很快
- **Vue Router**:路由路由
- **TypeScript**JavaScript 语言的超集
- **PNPM**:更快速的,节省磁盘空间的包管理工具
- **Scss**:和 Element Plus 保持一致
- **CSS 变量**:主要控制项目的布局和颜色
- **ESlint**:代码校验
- **Prettier**:代码格式化
- **Axios**:发送网络请求(已封装好)
- **UnoCSS**:具有高性能且极具灵活性的即时原子化 CSS 引擎
- **注释**:各个配置项都写有尽可能详细的注释
- **兼容移动端**: 布局兼容移动端页面分辨率
## 通知
## 功能
> [!NOTE]
> 为爱发电!所有源码均免费开源,如果对你有帮助,欢迎点个 Star 支持一下!
- **用户管理**:登录、登出演示
- **权限管理**:内置页面权限(动态路由)、指令权限、权限函数、路由守卫
- **多环境**开发环境development、预发布环境staging、正式环境production
- **多主题**:内置普通、黑暗、深蓝三种主题模式
- **错误页面**: 403、404
- **Dashboard**:根据不同用户显示不同的 Dashboard 页面
- **其他内置功能**SVG、动态侧边栏、动态面包屑、标签页快捷导航、Screenfull 全屏、自适应收缩侧边栏
> [!IMPORTANT]
> 欢迎体验全新的 5.0 版本,目前正在 beta 阶段,它将是一次匠心之作!
## 📚 文档
> [!WARNING]
> 4.x 版本如果没有严重的 BUG 将不再维护![点击切换到 4.x 分支](https://github.com/un-pany/v3-admin-vite/tree/4.x)
[中文文档](https://juejin.cn/post/7089377403717287972)
> [!TIP]
> 正式推出付费服务,如果不想自己动手,但想移除 TS 或其他模块?试试懒人套餐![点击看看](https://github.com/un-pany/v3-admin-vite/issues/225)
## 国内仓库
> [!TIP]
> 如果你有移动端 H5 需求,试试新的开源模板。[MobVue](https://github.com/un-pany/mobvue)
[Gitee](https://gitee.com/un-pany/v3-admin-vite)
## 使用
## 在线预览
<details>
<summary>推荐环境</summary>
| 位置 | 账号 | 链接 |
| ------------ | --------------- | ----------------------------------------------- |
| github-pages | admin 或 editor | [链接](https://un-pany.github.io/v3-admin-vite) |
<br>
## 🚀 开发
- 新版 `Visual Studio Code`
- 安装 `.vscode/extensions.json` 文件中推荐的插件
- `node` 20.x 或 22+
- `pnpm` 9.x 或 10+
</details>
<details>
<summary>本地开发</summary>
<br>
```bash
# 配置
1. 一键安装 .vscode 目录中推荐的插件
3. node 版本 16+
4. pnpm 版本 7.x
# 克隆项目
git clone https://github.com/un-pany/v3-admin-vite.git
@ -74,65 +63,165 @@ pnpm i
pnpm dev
```
## ✔️ 预览
</details>
<details>
<summary>打包构建</summary>
<br>
```bash
# 预览预发布环境
pnpm preview:stage
# 打包构建预发布环境
pnpm build:staging
# 预览正式环境
pnpm preview:prod
# 打包构建生产环境
pnpm build
```
## 📦️ 多环境打包
</details>
<details>
<summary>本地预览</summary>
<br>
```bash
# 构建预发布环境
pnpm build:stage
# 构建正式环境
pnpm build:prod
# 先执行打包构建命令生成 dist 目录后再执行以下预览命令
pnpm preview
```
## 🔧 代码格式检查
</details>
<details>
<summary>代码检查</summary>
<br>
```bash
# 代码校验与格式化
pnpm lint
# 单元测试
pnpm test
```
## Git 提交规范参考
</details>
- `feat` 增加新的业务功能
- `fix` 修复业务问题/BUG
- `perf` 优化性能
- `style` 更改代码风格, 不影响运行结果
- `refactor` 重构代码
- `revert` 撤销更改
- `test` 测试相关, 不涉及业务代码的更改
- `docs` 文档和注释相关
- `chore` 更新依赖/修改脚手架配置等琐事
- `workflow` 工作流改进
- `ci` 持续集成相关
- `types` 类型定义文件更改
- `wip` 开发中
<details>
<summary>代码提交规范</summary>
## 💕 贡献者
<br>
感谢所有的贡献者!
`feat` 新功能
`fix` 修复错误
`perf` 性能优化
`refactor` 重构代码
`docs` 文档和注释
`types` 类型相关
`test` 单测相关
`ci` 持续集成、工作流
`revert` 撤销更改
`chore` 琐事(更新依赖、修改配置等)
</details>
## 链接
**在线预览**[github-pages](https://un-pany.github.io/v3-admin-vite)
**中文文档**[链接](https://juejin.cn/post/7445151895121543209)
**零基础教程**[链接](https://juejin.cn/column/7207659644487139387)
**移动端 H5**[mobvue](https://github.com/un-pany/mobvue)
**Electron 桌面版**[v3-electron-vite](https://github.com/un-pany/v3-electron-vite)
**国内仓库**[gitee](https://gitee.com/un-pany/v3-admin-vite)
**可有可无的群**[查看进群方式](https://github.com/un-pany/v3-admin-vite/issues/191)
**捐赠**[请作者喝咖啡](https://github.com/un-pany/v3-admin-vite/issues/69)
**发行版 & 更新日志**[releases](https://github.com/un-pany/v3-admin-vite/releases)
## 特性
**结构精简**:没有复杂的封装,没有复杂的类型体操,刚好够用
**详细的注释**:各个配置项都写有尽可能详细的注释
**最新的依赖**:及时更新所有三方依赖至最新版
**有一点规范**:代码风格统一、命名风格统一、注释风格统一
## 内置功能
**用户管理**:登录、登出演示
**权限管理**:页面级权限(动态路由)、按钮级权限(权限指令、权限函数)、路由守卫
**多环境**开发环境development、预发布环境staging、生产环境production
**多主题**:普通、黑暗、深蓝, 三种主题模式
**多布局**:左侧、顶部、混合, 三种布局模式
**首页**:根据不同用户显示不同的 Dashboard 页面
**错误页**403、404
**兼容移动端**:布局兼容移动端页面分辨率
**其他**SVG 雪碧图、动态侧边栏、动态面包屑、标签页快捷导航、内容区放大与全屏、组合式函数
## 技术栈
**Vue3**:采用 Vue3 + script setup 最新的 Vue3 组合式 API
**Element Plus**Element UI 的 Vue3 版本
**Pinia**:传说中的 Vuex5
**Vite**:真的很快
**Vue Router**:路由路由
**TypeScript**JavaScript 语言的超集
**pnpm**:更快速的,节省磁盘空间的包管理工具
**Scss**:和 Element Plus 保持一致
**CSS 变量**:主要控制项目的布局和颜色
**ESLint**:代码校验与格式化
**Axios**:发送网络请求(已封装好)
**UnoCSS**:具有高性能且极具灵活性的即时原子化 CSS 引擎
## 项目预览图
![preview](./src/common/assets/images/docs/preview.png)
## 贡献者
在此感谢所有的贡献者!
<a href="https://github.com/un-pany/v3-admin-vite/graphs/contributors">
<img src="https://contrib.rocks/image?repo=un-pany/v3-admin-vite" />
<img src="https://contrib.rocks/image?repo=un-pany/v3-admin-vite">
</a>
## 可有可无的群
## License
QQ 群1014374415&& 加我微信,拉你进微信群(右)
![qq.png](./src/assets/docs/qq.png)
![wechat.png](./src/assets/docs/wechat.png)
## 📄 License
[MIT](./LICENSE)
Copyright (c) 2022 [pany](https://github.com/pany-ang)
[MIT](./LICENSE) License © 2022-PRESENT [pany](https://github.com/pany-ang)

43
eslint.config.js Normal file
View File

@ -0,0 +1,43 @@
import antfu from "@antfu/eslint-config"
// 更多自定义配置可查阅仓库https://github.com/antfu/eslint-config
export default antfu(
{
// 使用外部格式化程序格式化 css、html、markdown 等文件
formatters: true,
// 启用样式规则
stylistic: {
// 缩进级别
indent: 2,
// 引号风格 'single' | 'double'
quotes: "double",
// 是否启用分号
semi: false
},
// 忽略文件
ignores: []
},
{
// 对所有文件都生效的规则
rules: {
// vue
"vue/block-order": ["error", { order: ["script", "template", "style"] }],
"vue/attributes-order": "off",
// ts
"ts/no-use-before-define": "off",
// node
"node/prefer-global/process": "off",
// style
"style/comma-dangle": ["error", "never"],
"style/brace-style": ["error", "1tbs"],
// regexp
"regexp/no-unused-capturing-group": "off",
// other
"no-console": "off",
"no-debugger": "off",
"symbol-description": "off",
"antfu/if-newline": "off",
"unicorn/no-instanceof-builtins": "off"
}
}
)

View File

@ -1,11 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" href="/favicon.ico" />
<link rel="stylesheet" href="/app-loading.css" />
<title>V3 Admin Vite</title>
<title>%VITE_APP_TITLE%</title>
<script src="/detect-ie.js" defer></script>
</head>
<body>
<div id="app">

View File

@ -1,101 +1,64 @@
{
"name": "v3-admin-vite",
"version": "3.3.0",
"description": "一个免费开源的中后台管理系统基础解决方案,基于 Vue3、TypeScript、Element Plus、Pinia 和 Vite 等主流技术.",
"author": {
"name": "pany",
"email": "939630029@qq.com",
"url": "https://github.com/pany-ang"
},
"repository": {
"type": "git",
"url": "https://github.com/un-pany/v3-admin-vite.git"
},
"type": "module",
"version": "5.0.0-beta.6",
"description": "A crafted admin template, built with Vue3, Vite, TypeScript, Element Plus, and more",
"author": "pany <939630029@qq.com> (https://github.com/pany-ang)",
"repository": "https://github.com/un-pany/v3-admin-vite",
"scripts": {
"dev": "vite",
"build:stage": "vue-tsc --noEmit && vite build --mode staging",
"build:prod": "vue-tsc --noEmit && vite build",
"preview:stage": "pnpm build:stage && vite preview",
"preview:prod": "pnpm build:prod && vite preview",
"lint:eslint": "eslint --cache --max-warnings 0 \"src/**/*.{vue,js,ts,tsx}\" --fix",
"lint:prettier": "prettier --write \"src/**/*.{js,ts,json,tsx,css,less,scss,vue,html,md}\"",
"lint": "pnpm lint:eslint && pnpm lint:prettier",
"prepare": "husky install"
"build:staging": "vue-tsc && vite build --mode staging",
"build": "vue-tsc && vite build",
"preview": "vite preview",
"lint": "eslint . --fix",
"prepare": "husky",
"test": "vitest"
},
"dependencies": {
"@element-plus/icons-vue": "^2.0.10",
"axios": "^1.1.3",
"dayjs": "^1.11.6",
"element-plus": "^2.2.19",
"js-cookie": "^3.0.1",
"lodash-es": "^4.17.21",
"normalize.css": "^8.0.1",
"nprogress": "^0.2.0",
"path-browserify": "^1.0.1",
"path-to-regexp": "^6.2.1",
"pinia": "^2.0.23",
"screenfull": "^6.0.2",
"vue": "^3.2.41",
"vue-router": "^4.1.6",
"vxe-table": "^4.3.5",
"vxe-table-plugin-element": "^3.0.6",
"xe-utils": "^3.5.7"
"@element-plus/icons-vue": "2.3.1",
"axios": "1.8.4",
"dayjs": "1.11.13",
"element-plus": "2.9.7",
"js-cookie": "3.0.5",
"lodash-es": "4.17.21",
"mitt": "3.0.1",
"normalize.css": "8.0.1",
"nprogress": "0.2.0",
"path-browserify": "1.0.1",
"path-to-regexp": "8.2.0",
"pinia": "3.0.2",
"screenfull": "6.0.2",
"vue": "3.5.13",
"vue-router": "4.5.0",
"vxe-table": "4.6.25"
},
"devDependencies": {
"@types/js-cookie": "^3.0.2",
"@types/lodash-es": "^4.17.6",
"@types/node": "^18.11.5",
"@types/nprogress": "^0.2.0",
"@types/path-browserify": "^1.0.0",
"@typescript-eslint/eslint-plugin": "^5.41.0",
"@typescript-eslint/parser": "^5.41.0",
"@vitejs/plugin-vue": "^3.1.2",
"@vitejs/plugin-vue-jsx": "^2.0.1",
"@vue/eslint-config-prettier": "^7.0.0",
"@vue/eslint-config-typescript": "^11.0.2",
"eslint": "^8.26.0",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-vue": "^9.6.0",
"husky": "^8.0.1",
"lint-staged": "^13.0.3",
"prettier": "^2.7.1",
"sass": "^1.55.0",
"terser": "^5.15.1",
"typescript": "^4.8.4",
"unocss": "^0.46.0",
"vite": "^3.1.8",
"vite-plugin-svg-icons": "^2.0.1",
"vite-svg-loader": "^3.6.0",
"vue-eslint-parser": "^9.1.0",
"vue-tsc": "^1.0.9"
"@antfu/eslint-config": "4.12.0",
"@types/js-cookie": "3.0.6",
"@types/lodash-es": "4.17.12",
"@types/node": "22.14.1",
"@types/nprogress": "0.2.3",
"@types/path-browserify": "1.0.3",
"@vitejs/plugin-vue": "5.2.3",
"@vitejs/plugin-vue-jsx": "4.1.2",
"@vue/test-utils": "2.4.6",
"eslint": "9.24.0",
"eslint-plugin-format": "1.0.1",
"happy-dom": "17.4.4",
"husky": "9.1.7",
"lint-staged": "15.5.1",
"sass": "1.78.0",
"typescript": "5.8.3",
"unocss": "66.1.0-beta.12",
"unplugin-auto-import": "19.1.2",
"unplugin-svg-component": "0.12.1",
"unplugin-vue-components": "28.5.0",
"vite": "6.3.2",
"vite-svg-loader": "5.1.0",
"vitest": "3.1.1",
"vue-tsc": "2.2.8"
},
"lint-staged": {
"*.{js,jsx,vue,ts,tsx}": [
"eslint --fix",
"prettier --write"
],
"*.{scss,less,css,html,md}": [
"prettier --write"
],
"package.json": [
"prettier --write"
],
"{!(package)*.json,.!(browserslist)*rc}": [
"prettier --write--parser json"
]
},
"keywords": [
"vue",
"vue3",
"admin",
"vue-admin",
"vue3-admin",
"vite",
"vite-admin",
"element-plus",
"element-plus-admin",
"ts",
"typescript"
],
"license": "MIT"
"*": "eslint --fix"
}
}

10399
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -1,21 +0,0 @@
/** 配置项文档https://prettier.io/docs/en/configuration.html */
module.exports = {
/** 每一行的宽度 */
printWidth: 120,
/** Tab 键的空格数 */
tabWidth: 2,
/** 在对象中的括号之间是否用空格来间隔 */
bracketSpacing: true,
/** 箭头函数的参数无论有几个,都要括号包裹 */
arrowParens: "always",
/** 换行符的使用 */
endOfLine: "auto",
/** 是否采用单引号 */
singleQuote: false,
/** 对象或者数组的最后一个元素后面不要加逗号 */
trailingComma: "none",
/** 是否加分号 */
semi: false,
/** 是否使用 Tab 格式化 */
useTabs: false
}

View File

@ -1,65 +1,45 @@
#app-loading,
#app-loading:before,
#app-loading:after {
border-radius: 50%;
width: 2.5em;
height: 2.5em;
-webkit-animation-fill-mode: both;
animation-fill-mode: both;
-webkit-animation: loadingAnimation 1.8s infinite ease-in-out;
animation: loadingAnimation 1.8s infinite ease-in-out;
}
/* 白屏阶段会执行的 CSS 加载动画 */
#app-loading {
color: #409eff;
font-size: 10px;
margin: 80px auto;
position: relative;
text-indent: -9999em;
-webkit-transform: translateZ(0);
-ms-transform: translateZ(0);
transform: translateZ(0);
-webkit-animation-delay: -0.16s;
animation-delay: -0.16s;
top: 0;
transform: translate(-50%, 0);
top: 45vh;
margin: 0 auto;
color: #409eff;
font-size: 12px;
}
#app-loading:before,
#app-loading:after {
#app-loading,
#app-loading::before,
#app-loading::after {
width: 2em;
height: 2em;
border-radius: 50%;
animation: 2s ease-in-out infinite app-loading-animation;
}
#app-loading::before,
#app-loading::after {
content: "";
position: absolute;
top: 0;
}
#app-loading:before {
left: -3.5em;
-webkit-animation-delay: -0.32s;
animation-delay: -0.32s;
#app-loading::before {
left: -4em;
animation-delay: -0.2s;
}
#app-loading:after {
left: 3.5em;
#app-loading::after {
left: 4em;
animation-delay: 0.2s;
}
@-webkit-keyframes loadingAnimation {
@keyframes app-loading-animation {
0%,
80%,
100% {
box-shadow: 0 2.5em 0 -1.3em;
box-shadow: 0 2em 0 -2em;
}
40% {
box-shadow: 0 2.5em 0 0;
}
}
@keyframes loadingAnimation {
0%,
80%,
100% {
box-shadow: 0 2.5em 0 -1.3em;
}
40% {
box-shadow: 0 2.5em 0 0;
box-shadow: 0 2em 0 0;
}
}

4
public/detect-ie.js Normal file
View File

@ -0,0 +1,4 @@
// Tip: Simple judgments may not fully cover
if (/MSIE\s|Trident\//.test(navigator.userAgent)) {
document.body.innerHTML = "<strong>Sorry, this browser is currently not supported. We recommend using the latest version of a modern browser. For example, Chrome/Firefox/Edge.</strong>"
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

View File

@ -1,17 +1,24 @@
<script lang="ts" setup>
import { useTheme } from "@/hooks/useTheme"
import zhCn from "element-plus/lib/locale/lang/zh-cn"
import { useGreyAndColorWeakness } from "@@/composables/useGreyAndColorWeakness"
import { usePany } from "@@/composables/usePany"
import { useTheme } from "@@/composables/useTheme"
import zhCn from "element-plus/es/locale/lang/zh-cn" // Element Plus
const { initTheme } = useTheme()
const { initGreyAndColorWeakness } = useGreyAndColorWeakness()
const { initStarNotification, initStoreNotification } = usePany()
/** 初始化主题 */
//
initTheme()
/** 将 Element Plus 的语言设置为中文 */
const locale = zhCn
//
initGreyAndColorWeakness()
//
initStarNotification()
initStoreNotification()
</script>
<template>
<ElConfigProvider :locale="locale">
<el-config-provider :locale="zhCn">
<router-view />
</ElConfigProvider>
</el-config-provider>
</template>

View File

@ -1,33 +0,0 @@
import { request } from "@/utils/service"
export interface ILoginData {
/** admin 或 editor */
username: "admin" | "editor"
/** 密码 */
password: string
/** 验证码 */
code: string
}
/** 获取登录验证码 */
export function getLoginCodeApi() {
return request({
url: "login/code",
method: "get"
})
}
/** 登录并返回 Token */
export function loginApi(data: ILoginData) {
return request({
url: "users/login",
method: "post",
data
})
}
/** 获取用户详情 */
export function getUserInfoApi() {
return request({
url: "users/info",
method: "get"
})
}

View File

@ -1,57 +0,0 @@
import { request } from "@/utils/service"
interface ICreateTableDataApi {
username: string
password: string
}
interface IUpdateTableDataApi {
id: string
username: string
password?: string
}
interface IGetTableDataApi {
/** 当前页码 */
currentPage: number
/** 查询条数 */
size: number
/** 查询参数 */
username?: string
phone?: string
}
/** 增 */
export function createTableDataApi(data: ICreateTableDataApi) {
return request({
url: "table",
method: "post",
data
})
}
/** 删 */
export function deleteTableDataApi(id: string) {
return request({
url: `table/${id}`,
method: "delete"
})
}
/** 改 */
export function updateTableDataApi(data: IUpdateTableDataApi) {
return request({
url: "table",
method: "put",
data
})
}
/** 查 */
export function getTableDataApi(params: IGetTableDataApi) {
return request({
url: "table",
method: "get",
params
})
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 373 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 407 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

View File

@ -0,0 +1,37 @@
import type * as Tables from "./type"
import { request } from "@/http/axios"
/** 增 */
export function createTableDataApi(data: Tables.CreateOrUpdateTableRequestData) {
return request({
url: "tables",
method: "post",
data
})
}
/** 删 */
export function deleteTableDataApi(id: number) {
return request({
url: `tables/${id}`,
method: "delete"
})
}
/** 改 */
export function updateTableDataApi(data: Tables.CreateOrUpdateTableRequestData) {
return request({
url: "tables",
method: "put",
data
})
}
/** 查 */
export function getTableDataApi(params: Tables.TableRequestData) {
return request<Tables.TableResponseData>({
url: "tables",
method: "get",
params
})
}

View File

@ -0,0 +1,31 @@
export interface CreateOrUpdateTableRequestData {
id?: number
username: string
password?: string
}
export interface TableRequestData {
/** 当前页码 */
currentPage: number
/** 查询条数 */
size: number
/** 查询参数:用户名 */
username?: string
/** 查询参数:手机号 */
phone?: string
}
export interface TableData {
createTime: string
email: string
id: number
phone: string
roles: string
status: boolean
username: string
}
export type TableResponseData = ApiResponseData<{
list: TableData[]
total: number
}>

View File

@ -0,0 +1,10 @@
import type * as Users from "./type"
import { request } from "@/http/axios"
/** 获取当前登录用户详情 */
export function getCurrentUserApi() {
return request<Users.CurrentUserResponseData>({
url: "users/me",
method: "get"
})
}

View File

@ -0,0 +1 @@
export type CurrentUserResponseData = ApiResponseData<{ username: string, roles: string[] }>

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 747 B

After

Width:  |  Height:  |  Size: 747 B

View File

Before

Width:  |  Height:  |  Size: 746 B

After

Width:  |  Height:  |  Size: 746 B

View File

@ -0,0 +1 @@
<svg width="15" height="15" aria-label="Arrow down" role="img"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.2"><path d="M7.5 3.5v8M10.5 8.5l-3 3-3-3"></path></g></svg>

After

Width:  |  Height:  |  Size: 223 B

View File

@ -0,0 +1 @@
<svg width="15" height="15" aria-label="Enter key" role="img"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.2"><path d="M12 3.53088v3c0 1-1 2-2 2H4M7 11.53088l-3-3 3-3"></path></g></svg>

After

Width:  |  Height:  |  Size: 241 B

View File

@ -0,0 +1 @@
<svg width="15" height="15" aria-label="Escape key" role="img"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.2"><path d="M13.6167 8.936c-.1065.3583-.6883.962-1.4875.962-.7993 0-1.653-.9165-1.653-2.1258v-.5678c0-1.2548.7896-2.1016 1.653-2.1016.8634 0 1.3601.4778 1.4875 1.0724M9 6c-.1352-.4735-.7506-.9219-1.46-.8972-.7092.0246-1.344.57-1.344 1.2166s.4198.8812 1.3445.9805C8.465 7.3992 8.968 7.9337 9 8.5c.032.5663-.454 1.398-1.4595 1.398C6.6593 9.898 6 9 5.963 8.4851m-1.4748.5368c-.2635.5941-.8099.876-1.5443.876s-1.7073-.6248-1.7073-2.204v-.4603c0-1.0416.721-2.131 1.7073-2.131.9864 0 1.6425 1.031 1.5443 2.2492h-2.956"></path></g></svg>

After

Width:  |  Height:  |  Size: 694 B

View File

@ -0,0 +1 @@
<svg width="15" height="15" aria-label="Arrow up" role="img"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.2"><path d="M7.5 11.5v-8M10.5 6.5l-3-3-3 3"></path></g></svg>

After

Width:  |  Height:  |  Size: 223 B

View File

@ -0,0 +1,11 @@
## 目录说明
- `common/assets/icons/preserve-color` 目录下存放带颜色的 svg icon
- `common/assets/icons` 目录存放的 svg icon 会被插件重写 `fill``stroke` 属性,使得图片自带的颜色丢失,从而继承父元素的颜色
## 使用说明
`common/assets/icons/preserve-color` 目录下需要添加 `preserve-color/` 前缀,像这样: `<SvgIcon name="preserve-color/name" />`
`common/assets/icons` 目录下则不需要,像这样: `<SvgIcon name="name" />`

View File

@ -0,0 +1 @@
<svg t="1691398959507" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2431" width="200" height="200"><path d="M862.609 816.955L726.44 680.785l-0.059-0.056a358.907 358.907 0 0 0 56.43-91.927c18.824-44.507 28.369-91.767 28.369-140.467 0-48.701-9.545-95.96-28.369-140.467-18.176-42.973-44.19-81.56-77.319-114.689-33.13-33.129-71.717-59.144-114.69-77.32-44.507-18.825-91.767-28.37-140.467-28.37-48.701 0-95.96 9.545-140.467 28.37-42.973 18.176-81.56 44.19-114.689 77.32-33.13 33.129-59.144 71.717-77.32 114.689-18.825 44.507-28.37 91.767-28.37 140.467 0 48.7 9.545 95.96 28.37 140.467 18.176 42.974 44.19 81.561 77.32 114.69 33.129 33.129 71.717 59.144 114.689 77.319 44.507 18.824 91.767 28.369 140.467 28.369 48.7 0 95.96-9.545 140.467-28.369 32.78-13.864 62.997-32.303 90.197-54.968 0.063 0.064 0.122 0.132 0.186 0.195l136.169 136.17c6.25 6.25 14.438 9.373 22.628 9.373 8.188 0 16.38-3.125 22.627-9.372 12.496-12.496 12.496-32.758 0-45.254z m-412.274-69.466c-79.907 0-155.031-31.118-211.534-87.62-56.503-56.503-87.62-131.627-87.62-211.534s31.117-155.031 87.62-211.534c56.502-56.503 131.626-87.62 211.534-87.62s155.031 31.117 211.534 87.62c56.502 56.502 87.62 131.626 87.62 211.534s-31.118 155.031-87.62 211.534c-56.503 56.502-131.627 87.62-211.534 87.62z" p-id="2432"></path></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 492 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

View File

@ -0,0 +1,90 @@
/**
* @description dark-blue 主题模式下的 Element Plus CSS 变量
* @description 在此查阅所有可自定义的变量https://github.com/element-plus/element-plus/blob/dev/packages/theme-chalk/src/common/var.scss
* @description 也可以打开浏览器控制台选择元素查看要覆盖的变量名
*/
/* 基础颜色 */
html.dark-blue {
/* color-primary */
--el-color-primary: #00bb99;
--el-color-primary-light-3: #00bb99b3;
--el-color-primary-light-5: #00bb9980;
--el-color-primary-light-7: #00bb994d;
--el-color-primary-light-8: #00bb9933;
--el-color-primary-light-9: #00bb991a;
--el-color-primary-dark-2: #00bb99;
/* color-success */
--el-color-success: #67c23a;
--el-color-success-light-3: #67c23ab3;
--el-color-success-light-5: #67c23a80;
--el-color-success-light-7: #67c23a4d;
--el-color-success-light-8: #67c23a33;
--el-color-success-light-9: #67c23a1a;
--el-color-success-dark-2: #67c23a;
/* color-warning */
--el-color-warning: #e6a23c;
--el-color-warning-light-3: #e6a23cb3;
--el-color-warning-light-5: #e6a23c80;
--el-color-warning-light-7: #e6a23c4d;
--el-color-warning-light-8: #e6a23c33;
--el-color-warning-light-9: #e6a23c1a;
--el-color-warning-dark-2: #e6a23c;
/* color-danger */
--el-color-danger: #f56c6c;
--el-color-danger-light-3: #f56c6cb3;
--el-color-danger-light-5: #f56c6c80;
--el-color-danger-light-7: #f56c6c4d;
--el-color-danger-light-8: #f56c6c33;
--el-color-danger-light-9: #f56c6c1a;
--el-color-danger-dark-2: #f56c6c;
/* color-error */
--el-color-error: #f56c6c;
--el-color-error-light-3: #f56c6cb3;
--el-color-error-light-5: #f56c6c80;
--el-color-error-light-7: #f56c6c4d;
--el-color-error-light-8: #f56c6c33;
--el-color-error-light-9: #f56c6c1a;
--el-color-error-dark-2: #f56c6c;
/* color-info */
--el-color-info: #909399;
--el-color-info-light-3: #909399b3;
--el-color-info-light-5: #90939980;
--el-color-info-light-7: #9093994d;
--el-color-info-light-8: #90939933;
--el-color-info-light-9: #9093991a;
--el-color-info-dark-2: #909399;
/* text-color */
--el-text-color-primary: #e5eaf3;
--el-text-color-regular: #cfd3dc;
--el-text-color-secondary: #a3a6ad;
--el-text-color-placeholder: #8d9095;
--el-text-color-disabled: #6c6e72;
/* border-color */
--el-border-color-darker: #003380;
--el-border-color-dark: #003380;
--el-border-color: #003380;
--el-border-color-light: #003380;
--el-border-color-lighter: #003380;
--el-border-color-extra-light: #003380;
/* fill-color */
--el-fill-color-darker: #002b6b;
--el-fill-color-dark: #002b6b;
--el-fill-color: #002b6b;
--el-fill-color-light: #002359;
--el-fill-color-lighter: #002359;
--el-fill-color-blank: #001b44;
--el-fill-color-extra-light: #001b44;
/* bg-color */
--el-bg-color-page: #001535;
--el-bg-color: #001b44;
--el-bg-color-overlay: #002359;
/* mask-color */
--el-mask-color: rgba(0, 0, 0, 0.5);
--el-mask-color-extra-light: rgba(0, 0, 0, 0.3);
}
/* button */
html.dark-blue .el-button {
--el-button-disabled-text-color: rgba(255, 255, 255, 0.5);
}

View File

@ -0,0 +1,20 @@
// 自定义 Element Plus 样式
// 卡片
.el-card {
background-color: var(--el-bg-color) !important;
}
// 分页
.el-pagination {
// 参考 Bootstrap 的响应式设计 WIDTH = 768
@media screen and (max-width: 768px) {
.el-pagination__total,
.el-pagination__sizes,
.el-pagination__jump,
.btn-prev,
.btn-next {
display: none;
}
}
}

View File

@ -2,10 +2,18 @@
@import "./variables.css";
// Transition
@import "./transition.scss";
// Element Plus
@import "./element-plus.css";
@import "./element-plus.scss";
// Vxe Table
@import "./vxe-table.css";
@import "./vxe-table.scss";
// 注册多主题
@import "./theme/register.scss";
// Mixins
@import "./mixins.scss";
// View Transition
@import "./view-transition.scss";
// 业务页面几乎都应该在根元素上挂载 class="app-container"以保持页面美观
.app-container {
@ -14,15 +22,26 @@
html {
height: 100%;
// 灰色模式
&.grey-mode {
filter: grayscale(1);
}
// 色弱模式
&.color-weakness {
filter: invert(0.8);
}
}
body {
height: 100%;
color: var(--v3-body-text-color);
background-color: var(--v3-body-bg-color);
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", Arial,
font-family:
Inter, "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", Arial,
sans-serif;
@extend %scrollbar;
}
#app {
@ -30,8 +49,8 @@ body {
}
*,
*:before,
*:after {
*::before,
*::after {
box-sizing: border-box;
}

View File

@ -0,0 +1,42 @@
// 清除浮动
%clearfix {
&::after {
content: "";
display: table;
clear: both;
}
}
// 美化原生滚动条
%scrollbar {
// 整个滚动条
&::-webkit-scrollbar {
width: 8px;
height: 8px;
}
// 滚动条上的滚动滑块
&::-webkit-scrollbar-thumb {
border-radius: 4px;
background-color: #90939955;
}
&::-webkit-scrollbar-thumb:hover {
background-color: #90939977;
}
&::-webkit-scrollbar-thumb:active {
background-color: #90939999;
}
// 当同时有垂直滚动条和水平滚动条时交汇的部分
&::-webkit-scrollbar-corner {
background-color: transparent;
}
}
// 文本溢出时显示省略号
%ellipsis {
// 隐藏溢出的文本
overflow: hidden;
// 防止文本换行
white-space: nowrap;
// 文本内容溢出容器时文本末尾显示省略号
text-overflow: ellipsis;
}

View File

@ -0,0 +1,29 @@
// Element Plus 相关
// 侧边栏的 item popper
.el-popper {
.el-menu {
background-color: var(--el-bg-color);
.el-menu-item {
background-color: var(--el-bg-color);
&.is-active,
&:hover {
background-color: var(--el-bg-color-overlay);
color: #ffffff;
}
}
.el-sub-menu__title {
background-color: var(--el-bg-color);
}
.el-sub-menu {
&.is-active {
> .el-sub-menu__title {
color: #ffffff;
}
}
}
}
.el-menu--horizontal {
border: none;
}
}

View File

@ -0,0 +1,4 @@
.#{$theme-name} {
@import "./layouts.scss";
@import "./element-plus.scss";
}

View File

@ -0,0 +1,34 @@
// Layout 相关
.app-wrapper {
// 侧边栏
.sidebar-container {
background-color: var(--el-bg-color);
.el-menu {
background-color: var(--el-bg-color);
.el-menu-item {
background-color: var(--el-bg-color);
&.is-active,
&:hover {
background-color: var(--el-bg-color-overlay);
color: #ffffff;
}
}
}
.el-sub-menu__title {
background-color: var(--el-bg-color);
}
.el-sub-menu {
&.is-active {
> .el-sub-menu__title {
color: #ffffff;
}
}
}
}
}
// 右侧设置面板
.handle-button {
background-color: lighten($theme-bg-color, 20%) !important;
}

View File

@ -0,0 +1,6 @@
// dark-blue 主题下的变量
// 主题名称
$theme-name: "dark-blue";
// 主题背景颜色
$theme-bg-color: #001b44;

View File

@ -1,3 +1,2 @@
@use "./element-plus.css";
@import "./variables.scss";
@import "../core/index.scss";

View File

@ -0,0 +1,6 @@
// dark 主题下的变量
// 主题名称
$theme-name: "dark";
// 主题背景颜色
$theme-bg-color: #141414;

View File

@ -1,4 +1,4 @@
// See https://staging-cn.vuejs.org/guide/built-ins/transition.html for detail
// https://cn.vuejs.org/guide/built-ins/transition
// fade-transform
.fade-transform-leave-active,
@ -14,12 +14,12 @@
transform: translateX(30px);
}
// sidebar-logo-fade
.sidebar-logo-fade-enter-active,
.sidebar-logo-fade-leave-active {
// layout-logo-fade
.layout-logo-fade-enter-active,
.layout-logo-fade-leave-active {
transition: opacity 1.5s;
}
.sidebar-logo-fade-enter-from,
.sidebar-logo-fade-leave-to {
.layout-logo-fade-enter-from,
.layout-logo-fade-leave-to {
opacity: 0;
}

View File

@ -0,0 +1,72 @@
/* 全局 CSS 变量,这种变量不仅可以在 CSS 和 SCSS 中使用,还可以导入到 JS 中使用 */
:root {
/* Body */
--v3-body-text-color: var(--el-text-color-primary);
--v3-body-bg-color: var(--el-bg-color-page);
/* Header 区域 = NavigationBar 组件 + TagsView 组件 */
--v3-header-height: calc(
var(--v3-navigationbar-height) + var(--v3-tagsview-height) + var(--v3-header-border-bottom-width)
);
--v3-header-bg-color: var(--el-bg-color);
--v3-header-box-shadow: var(--el-box-shadow-lighter);
--v3-header-border-bottom-width: 1px;
--v3-header-border-bottom: var(--v3-header-border-bottom-width) solid var(--el-fill-color);
/* NavigationBar 组件 */
--v3-navigationbar-height: 50px;
--v3-navigationbar-text-color: var(--el-text-color-regular);
/* Sidebar 组件(左侧模式全部生效、顶部模式全部不生效、混合模式非颜色部分生效) */
--v3-sidebar-width: 220px;
--v3-sidebar-hide-width: 58px;
--v3-sidebar-border-right: 1px solid var(--el-fill-color);
--v3-sidebar-menu-item-height: 60px;
--v3-sidebar-menu-tip-line-bg-color: var(--el-color-primary);
--v3-sidebar-menu-bg-color: #001428;
--v3-sidebar-menu-hover-bg-color: #409eff10;
--v3-sidebar-menu-text-color: #cfd3dc;
--v3-sidebar-menu-active-text-color: #ffffff;
/* TagsView 组件 */
--v3-tagsview-height: 34px;
--v3-tagsview-text-color: var(--el-text-color-regular);
--v3-tagsview-tag-active-text-color: #ffffff;
--v3-tagsview-tag-bg-color: var(--el-bg-color);
--v3-tagsview-tag-active-bg-color: var(--el-color-primary);
--v3-tagsview-tag-border-radius: 2px;
--v3-tagsview-tag-border-color: var(--el-border-color-lighter);
--v3-tagsview-tag-active-border-color: var(--el-color-primary);
--v3-tagsview-tag-icon-hover-bg-color: #00000030;
--v3-tagsview-tag-icon-hover-color: #ffffff;
--v3-tagsview-contextmenu-text-color: var(--el-text-color-regular);
--v3-tagsview-contextmenu-hover-text-color: var(--el-text-color-primary);
--v3-tagsview-contextmenu-bg-color: var(--el-bg-color-overlay);
--v3-tagsview-contextmenu-hover-bg-color: var(--el-fill-color);
--v3-tagsview-contextmenu-box-shadow: var(--el-box-shadow);
/* Hamburger 组件 */
--v3-hamburger-text-color: var(--el-text-color-primary);
/* RightPanel 组件 */
--v3-rightpanel-button-bg-color: #001428;
}
/* 内容区放大时,将不需要的组件隐藏 */
body.content-large {
/* Header 区域 = TagsView 组件 */
--v3-header-height: var(--v3-tagsview-height);
/* NavigationBar 组件 */
--v3-navigationbar-height: 0px;
/* Sidebar 组件 */
--v3-sidebar-width: 0px;
--v3-sidebar-hide-width: 0px;
}
/* 内容区全屏时,将不需要的组件隐藏 */
body.content-full {
/* Header 区域 */
--v3-header-height: 0px;
/* NavigationBar 组件 */
--v3-navigationbar-height: 0px;
/* Sidebar 组件 */
--v3-sidebar-width: 0px;
--v3-sidebar-hide-width: 0px;
/* TagsView 组件 */
--v3-tagsview-height: 0px;
}

View File

@ -0,0 +1,20 @@
// 控制切换主题时的动画效果只在较新的浏览器上生效例如 Chrome 111+
::view-transition-old(root) {
animation: none;
mix-blend-mode: normal;
}
::view-transition-new(root) {
animation: 0.5s ease-in clip-animation;
mix-blend-mode: normal;
}
@keyframes clip-animation {
from {
clip-path: circle(0px at var(--v3-theme-x) var(--v3-theme-y));
}
to {
clip-path: circle(var(--v3-theme-r) at var(--v3-theme-x) var(--v3-theme-y));
}
}

View File

@ -0,0 +1,97 @@
/**
* @description 所有主题模式下的 Vxe Table CSS 变量
* @description Element Plus CSS 变量来覆写 Vxe Table CSS 变量目的是使 Vxe Table 支持多主题模式且样式统一
* @description 在此查阅所有可自定义的变量https://github.com/x-extends/vxe-table/blob/master/styles/css-variable.scss
*/
:root {
/* color */
--vxe-font-color: var(--el-text-color-regular);
--vxe-primary-color: var(--el-color-primary);
--vxe-success-color: var(--el-color-success);
--vxe-info-color: var(--el-color-info);
--vxe-warning-color: var(--el-color-warning);
--vxe-danger-color: var(--el-color-danger);
--vxe-font-lighten-color: var(--el-text-color-primary);
--vxe-primary-lighten-color: var(--el-color-primary-light-3);
--vxe-success-lighten-color: var(--el-color-success-light-3);
--vxe-info-lighten-color: var(--el-color-info-light-3);
--vxe-warning-lighten-color: var(--el-color-warning-light-3);
--vxe-danger-lighten-color: var(--el-color-danger-light-3);
--vxe-font-darken-color: var(--el-text-color-secondary);
--vxe-primary-darken-color: var(--el-color-primary-dark-2);
--vxe-success-darken-color: var(--el-color-success-dark-2);
--vxe-info-darken-color: var(--el-color-info-dark-2);
--vxe-warning-darken-color: var(--el-color-warning-dark-2);
--vxe-danger-darken-color: var(--el-color-danger-dark-2);
--vxe-font-disabled-color: var(--el-text-color-disabled);
--vxe-primary-disabled-color: var(--el-color-primary-light-5);
--vxe-success-disabled-color: var(--el-color-success-light-5);
--vxe-info-disabled-color: var(--el-color-info-light-5);
--vxe-warning-disabled-color: var(--el-color-warning-light-5);
--vxe-danger-disabled-color: var(--el-color-danger-light-5);
/* input/radio/checkbox */
--vxe-input-border-color: var(--el-border-color);
--vxe-input-disabled-color: var(--el-text-color-disabled);
--vxe-input-disabled-background-color: var(--el-fill-color-light);
--vxe-input-placeholder-color: var(--el-text-color-placeholder);
/* popup */
--vxe-table-popup-border-color: var(--el-border-color);
/* table */
--vxe-table-header-font-color: var(--el-text-color-regular);
--vxe-table-footer-font-color: var(--el-text-color-regular);
--vxe-table-border-color: var(--el-border-color-lighter);
--vxe-table-header-background-color: var(--el-bg-color);
--vxe-table-body-background-color: var(--el-bg-color);
--vxe-table-footer-background-color: var(--el-bg-color);
--vxe-table-row-hover-background-color: var(--el-fill-color-light);
--vxe-table-row-current-background-color: var(--el-fill-color-light);
--vxe-table-row-hover-current-background-color: var(--el-fill-color-light);
--vxe-table-checkbox-range-background-color: var(--el-fill-color-light);
/* menu */
--vxe-table-menu-background-color: var(--el-bg-color-overlay);
/* loading */
--vxe-loading-color: var(--el-color-primary);
--vxe-loading-background-color: var(--el-mask-color);
/* validate */
--vxe-table-validate-error-color: var(--el-color-danger);
/* toolbar */
--vxe-toolbar-background-color: var(--el-bg-color);
--vxe-toolbar-custom-active-background-color: var(--el-bg-color-overlay);
--vxe-toolbar-panel-background-color: var(--el-bg-color-overlay);
/* pager */
--vxe-pager-background-color: var(--el-bg-color);
/* modal */
--vxe-modal-header-background-color: var(--el-bg-color);
--vxe-modal-body-background-color: var(--el-bg-color);
--vxe-modal-border-color: var(--el-border-color);
/* button */
--vxe-button-default-background-color: var(--el-bg-color-overlay);
/* input */
--vxe-input-background-color: var(--el-fill-color-blank);
--vxe-input-panel-background-color: var(--el-fill-color-blank);
/* form */
--vxe-form-background-color: var(--el-bg-color);
--vxe-form-validate-error-color: var(--el-color-danger);
/* select */
--vxe-select-option-hover-background-color: var(--el-bg-color-overlay);
--vxe-select-panel-background-color: var(--el-bg-color);
}

View File

@ -0,0 +1,38 @@
// 自定义 Vxe Table 样式
.vxe-grid {
// 表单
&--form-wrapper {
.vxe-form {
padding: 10px 20px;
margin-bottom: 20px;
}
}
// 工具栏
&--toolbar-wrapper {
.vxe-toolbar {
padding: 20px;
}
}
// 分页
&--pager-wrapper {
.vxe-pager {
height: 70px;
padding: 0 20px;
&--wrapper {
// 参考 Bootstrap 的响应式设计 WIDTH = 768
@media screen and (max-width: 768px) {
.vxe-pager--total,
.vxe-pager--sizes,
.vxe-pager--jump,
.vxe-pager--jump-prev,
.vxe-pager--jump-next {
display: none;
}
}
}
}
}
}

View File

@ -0,0 +1,60 @@
<script lang="ts" setup>
import type { NotifyItem } from "./type"
interface Props {
data: NotifyItem[]
}
const props = defineProps<Props>()
</script>
<template>
<el-empty v-if="props.data.length === 0" />
<el-card v-else v-for="(item, index) in props.data" :key="index" shadow="never" class="card-container">
<template #header>
<div class="card-header">
<div>
<span>
<span class="card-title">{{ item.title }}</span>
<el-tag v-if="item.extra" :type="item.status" effect="plain" size="small">{{ item.extra }}</el-tag>
</span>
<div class="card-time">
{{ item.datetime }}
</div>
</div>
<div v-if="item.avatar" class="card-avatar">
<img :src="item.avatar" width="34">
</div>
</div>
</template>
<div class="card-body">
{{ item.description ?? "No Data" }}
</div>
</el-card>
</template>
<style lang="scss" scoped>
.card-container {
margin-bottom: 10px;
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
.card-title {
font-weight: bold;
margin-right: 10px;
}
.card-time {
font-size: 12px;
color: var(--el-text-color-secondary);
}
.card-avatar {
display: flex;
align-items: center;
}
}
.card-body {
font-size: 12px;
}
}
</style>

View File

@ -0,0 +1,58 @@
import type { NotifyItem } from "./type"
export const notifyData: NotifyItem[] = [
{
avatar: "https://gw.alipayobjects.com/zos/rmsportal/OKJXDXrmkNshAMvwtvhu.png",
title: "V3 Admin Vite 上线啦",
datetime: "两年前",
description: "一个免费开源的中后台管理系统基础解决方案,基于 Vue3、TypeScript、Element Plus、Pinia 和 Vite 等主流技术"
},
{
avatar: "https://gw.alipayobjects.com/zos/rmsportal/OKJXDXrmkNshAMvwtvhu.png",
title: "V3 Admin 上线啦",
datetime: "三年前",
description: "一个中后台管理系统基础解决方案,基于 Vue3、TypeScript、Element Plus 和 Pinia"
}
]
export const messageData: NotifyItem[] = [
{
avatar: "https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png",
title: "来自楚门的世界",
description: "如果再也不能见到你,祝你早安、午安和晚安",
datetime: "1998-06-05"
},
{
avatar: "https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png",
title: "来自大话西游",
description: "如果非要在这份爱上加上一个期限,我希望是一万年",
datetime: "1995-02-04"
},
{
avatar: "https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png",
title: "来自龙猫",
description: "心存善意,定能途遇天使",
datetime: "1988-04-16"
}
]
export const todoData: NotifyItem[] = [
{
title: "任务名称",
description: "这家伙很懒,什么都没留下",
extra: "未开始",
status: "info"
},
{
title: "任务名称",
description: "这家伙很懒,什么都没留下",
extra: "进行中",
status: "primary"
},
{
title: "任务名称",
description: "这家伙很懒,什么都没留下",
extra: "已超时",
status: "danger"
}
]

View File

@ -0,0 +1,94 @@
<script lang="ts" setup>
import type { NotifyItem } from "./type"
import { Bell } from "@element-plus/icons-vue"
import { messageData, notifyData, todoData } from "./data"
import List from "./List.vue"
type TabName = "通知" | "消息" | "待办"
interface DataItem {
name: TabName
type: "primary" | "success" | "warning" | "danger" | "info"
list: NotifyItem[]
}
/** 角标当前值 */
const badgeValue = computed(() => data.value.reduce((sum, item) => sum + item.list.length, 0))
/** 角标最大值 */
const badgeMax = 99
/** 面板宽度 */
const popoverWidth = 350
/** 当前 Tab */
const activeName = ref<TabName>("通知")
/** 所有数据 */
const data = ref<DataItem[]>([
//
{
name: "通知",
type: "primary",
list: notifyData
},
//
{
name: "消息",
type: "danger",
list: messageData
},
//
{
name: "待办",
type: "warning",
list: todoData
}
])
function handleHistory() {
ElMessage.success(`跳转到${activeName.value}历史页面`)
}
</script>
<template>
<div class="notify">
<el-popover placement="bottom" :width="popoverWidth" trigger="click">
<template #reference>
<el-badge :value="badgeValue" :max="badgeMax" :hidden="badgeValue === 0">
<el-tooltip effect="dark" content="消息通知" placement="bottom">
<el-icon :size="20">
<Bell />
</el-icon>
</el-tooltip>
</el-badge>
</template>
<template #default>
<el-tabs v-model="activeName" class="demo-tabs" stretch>
<el-tab-pane v-for="(item, index) in data" :key="index" :name="item.name">
<template #label>
{{ item.name }}
<el-badge :value="item.list.length" :max="badgeMax" :type="item.type" />
</template>
<el-scrollbar height="400px">
<List :data="item.list" />
</el-scrollbar>
</el-tab-pane>
</el-tabs>
<div class="notify-history">
<el-button link @click="handleHistory">
查看{{ activeName }}历史
</el-button>
</div>
</template>
</el-popover>
</div>
</template>
<style lang="scss" scoped>
.notify-history {
text-align: center;
padding-top: 12px;
border-top: 1px solid var(--el-border-color);
}
</style>

View File

@ -0,0 +1,8 @@
export interface NotifyItem {
avatar?: string
title: string
datetime?: string
description?: string
status?: "primary" | "success" | "info" | "warning" | "danger"
extra?: string
}

View File

@ -0,0 +1,111 @@
<script lang="ts" setup>
import screenfull from "screenfull"
interface Props {
/** 全屏的元素,默认是 html */
element?: string
/** 打开全屏提示语 */
openTips?: string
/** 关闭全屏提示语 */
exitTips?: string
/** 是否只针对内容区 */
content?: boolean
}
const props = withDefaults(defineProps<Props>(), {
element: "html",
openTips: "全屏",
exitTips: "退出全屏",
content: false
})
const CONTENT_LARGE = "content-large"
const CONTENT_FULL = "content-full"
const classList = document.body.classList
// #region
const isEnabled = screenfull.isEnabled
const isFullscreen = ref<boolean>(false)
const fullscreenTips = computed(() => (isFullscreen.value ? props.exitTips : props.openTips))
const fullscreenSvgName = computed(() => (isFullscreen.value ? "fullscreen-exit" : "fullscreen"))
function handleFullscreenClick() {
const dom = document.querySelector(props.element) || undefined
isEnabled ? screenfull.toggle(dom) : ElMessage.warning("您的浏览器无法工作")
}
function handleFullscreenChange() {
isFullscreen.value = screenfull.isFullscreen
// 退 class
isFullscreen.value || classList.remove(CONTENT_LARGE, CONTENT_FULL)
}
watchEffect((onCleanup) => {
if (isEnabled) {
//
screenfull.on("change", handleFullscreenChange)
//
onCleanup(() => {
screenfull.off("change", handleFullscreenChange)
})
}
})
// #endregion
// #region
const isContentLarge = ref<boolean>(false)
const contentLargeTips = computed(() => (isContentLarge.value ? "内容区复原" : "内容区放大"))
const contentLargeSvgName = computed(() => (isContentLarge.value ? "fullscreen-exit" : "fullscreen"))
function handleContentLargeClick() {
isContentLarge.value = !isContentLarge.value
//
classList.toggle(CONTENT_LARGE, isContentLarge.value)
}
function handleContentFullClick() {
//
isContentLarge.value && handleContentLargeClick()
//
classList.add(CONTENT_FULL)
//
handleFullscreenClick()
}
// #endregion
</script>
<template>
<div>
<!-- 全屏 -->
<el-tooltip v-if="!props.content" effect="dark" :content="fullscreenTips" placement="bottom">
<SvgIcon :name="fullscreenSvgName" @click="handleFullscreenClick" class="svg-icon" />
</el-tooltip>
<!-- 内容区 -->
<el-dropdown v-else :disabled="isFullscreen">
<SvgIcon :name="contentLargeSvgName" class="svg-icon" />
<template #dropdown>
<el-dropdown-menu>
<!-- 内容区放大 -->
<el-dropdown-item @click="handleContentLargeClick">
{{ contentLargeTips }}
</el-dropdown-item>
<!-- 内容区全屏 -->
<el-dropdown-item @click="handleContentFullClick">
内容区全屏
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</template>
<style lang="scss" scoped>
.svg-icon {
font-size: 20px;
&:focus {
outline: none;
}
}
</style>

View File

@ -0,0 +1,54 @@
<script lang="ts" setup>
import { useDevice } from "@@/composables/useDevice"
interface Props {
total: number
}
const props = defineProps<Props>()
const { isMobile } = useDevice()
</script>
<template>
<div class="search-footer">
<template v-if="!isMobile">
<span class="search-footer-item">
<SvgIcon name="keyboard-enter" class="svg-icon" />
<span>确认</span>
</span>
<span class="search-footer-item">
<SvgIcon name="keyboard-up" class="svg-icon" />
<SvgIcon name="keyboard-down" class="svg-icon" />
<span>切换</span>
</span>
<span class="search-footer-item">
<SvgIcon name="keyboard-esc" class="svg-icon" />
<span>关闭</span>
</span>
</template>
<span class="search-footer-total"> {{ props.total }} </span>
</div>
</template>
<style lang="scss" scoped>
.search-footer {
display: flex;
color: var(--el-text-color-secondary);
font-size: 14px;
&-item {
display: flex;
align-items: center;
margin-right: 12px;
.svg-icon {
margin-right: 5px;
padding: 2px;
font-size: 20px;
background-color: var(--el-fill-color);
}
}
&-total {
margin: 0 0 0 auto;
}
}
</style>

View File

@ -0,0 +1,193 @@
<script lang="ts" setup>
import type { ElScrollbar } from "element-plus"
import type { RouteRecordNameGeneric, RouteRecordRaw } from "vue-router"
import { usePermissionStore } from "@/pinia/stores/permission"
import { useDevice } from "@@/composables/useDevice"
import { isExternal } from "@@/utils/validate"
import { cloneDeep, debounce } from "lodash-es"
import Footer from "./Footer.vue"
import Result from "./Result.vue"
/** 控制 modal 显隐 */
const modelValue = defineModel<boolean>({ required: true })
const router = useRouter()
const { isMobile } = useDevice()
const inputRef = ref<HTMLInputElement | null>(null)
const scrollbarRef = ref<InstanceType<typeof ElScrollbar> | null>(null)
const resultRef = ref<InstanceType<typeof Result> | null>(null)
const keyword = ref<string>("")
const result = shallowRef<RouteRecordRaw[]>([])
const activeRouteName = ref<RouteRecordNameGeneric | undefined>(undefined)
/** 是否按下了上键或下键(用于解决和 mouseenter 事件的冲突) */
const isPressUpOrDown = ref<boolean>(false)
/** 控制搜索对话框宽度 */
const modalWidth = computed(() => (isMobile.value ? "80vw" : "40vw"))
/** 树形菜单 */
const menus = computed(() => cloneDeep(usePermissionStore().routes))
/** 搜索(防抖) */
const handleSearch = debounce(() => {
const flatMenus = flatTree(menus.value)
const _keywords = keyword.value.toLocaleLowerCase().trim()
result.value = flatMenus.filter(menu => keyword.value ? menu.meta?.title?.toLocaleLowerCase().includes(_keywords) : false)
//
const length = result.value?.length
activeRouteName.value = length > 0 ? result.value[0].name : undefined
}, 500)
/** 将树形菜单扁平化为一维数组,用于菜单搜索 */
function flatTree(arr: RouteRecordRaw[], result: RouteRecordRaw[] = []) {
arr.forEach((item) => {
result.push(item)
item.children && flatTree(item.children, result)
})
return result
}
/** 关闭搜索对话框 */
function handleClose() {
modelValue.value = false
//
setTimeout(() => {
keyword.value = ""
result.value = []
}, 200)
}
/** 根据下标位置进行滚动 */
function scrollTo(index: number) {
if (!resultRef.value) return
const scrollTop = resultRef.value.getScrollTop(index)
// el-scrollbar
scrollbarRef.value?.setScrollTop(scrollTop)
}
/** 键盘上键 */
function handleUp() {
isPressUpOrDown.value = true
const { length } = result.value
if (length === 0) return
// name
const index = result.value.findIndex(item => item.name === activeRouteName.value)
//
if (index === 0) {
const bottomName = result.value[length - 1].name
// bottomName 1 name
if (activeRouteName.value === bottomName && length > 1) {
activeRouteName.value = result.value[length - 2].name
scrollTo(length - 2)
} else {
//
activeRouteName.value = bottomName
scrollTo(length - 1)
}
} else {
activeRouteName.value = result.value[index - 1].name
scrollTo(index - 1)
}
}
/** 键盘下键 */
function handleDown() {
isPressUpOrDown.value = true
const { length } = result.value
if (length === 0) return
// name name
const index = result.value.map(item => item.name).lastIndexOf(activeRouteName.value)
//
if (index === length - 1) {
const topName = result.value[0].name
// topName 1 name
if (activeRouteName.value === topName && length > 1) {
activeRouteName.value = result.value[1].name
scrollTo(1)
} else {
//
activeRouteName.value = topName
scrollTo(0)
}
} else {
activeRouteName.value = result.value[index + 1].name
scrollTo(index + 1)
}
}
/** 键盘回车键 */
function handleEnter() {
const { length } = result.value
if (length === 0) return
const name = activeRouteName.value
const path = result.value.find(item => item.name === name)?.path
if (path && isExternal(path)) return window.open(path, "_blank", "noopener, noreferrer")
if (!name) return ElMessage.warning("无法通过搜索进入该菜单,请为对应的路由设置唯一的 Name")
try {
router.push({ name })
} catch {
return ElMessage.warning("该菜单有必填的动态参数,无法通过搜索进入")
}
handleClose()
}
/** 释放上键或下键 */
function handleReleaseUpOrDown() {
isPressUpOrDown.value = false
}
</script>
<template>
<el-dialog
v-model="modelValue"
:before-close="handleClose"
:width="modalWidth"
top="5vh"
class="search-modal__private"
append-to-body
@opened="inputRef?.focus()"
@closed="inputRef?.blur()"
@keydown.up="handleUp"
@keydown.down="handleDown"
@keydown.enter="handleEnter"
@keyup.up.down="handleReleaseUpOrDown"
>
<el-input ref="inputRef" v-model="keyword" placeholder="搜索菜单" size="large" clearable @input="handleSearch">
<template #prefix>
<SvgIcon name="search" class="svg-icon" />
</template>
</el-input>
<el-empty v-if="result.length === 0" description="暂无搜索结果" :image-size="100" />
<template v-else>
<p>搜索结果</p>
<el-scrollbar ref="scrollbarRef" max-height="40vh" always>
<Result
ref="resultRef"
v-model="activeRouteName"
:data="result"
:is-press-up-or-down="isPressUpOrDown"
@click="handleEnter"
/>
</el-scrollbar>
</template>
<template #footer>
<Footer :total="result.length" />
</template>
</el-dialog>
</template>
<style lang="scss">
.search-modal__private {
.svg-icon {
font-size: 18px;
}
.el-dialog__header {
display: none;
}
.el-dialog__footer {
border-top: 1px solid var(--el-border-color);
padding-top: var(--el-dialog-padding-primary);
}
}
</style>

View File

@ -0,0 +1,115 @@
<script lang="ts" setup>
import type { RouteRecordNameGeneric, RouteRecordRaw } from "vue-router"
interface Props {
data: RouteRecordRaw[]
isPressUpOrDown: boolean
}
const props = defineProps<Props>()
/** 选中的菜单 */
const modelValue = defineModel<RouteRecordNameGeneric | undefined>({ required: true })
const instance = getCurrentInstance()
const scrollbarHeight = ref<number>(0)
/** 菜单的样式 */
function itemStyle(item: RouteRecordRaw) {
const flag = item.name === modelValue.value
return {
background: flag ? "var(--el-color-primary)" : "",
color: flag ? "#ffffff" : ""
}
}
/** 鼠标移入 */
function handleMouseenter(item: RouteRecordRaw) {
// mouseenter
if (props.isPressUpOrDown) return
modelValue.value = item.name
}
/** 计算滚动可视区高度 */
function getScrollbarHeight() {
// el-scrollbar max-height="40vh"
scrollbarHeight.value = Number((window.innerHeight * 0.4).toFixed(1))
}
/** 根据下标计算到顶部的距离 */
function getScrollTop(index: number) {
const currentInstance = instance?.proxy?.$refs[`resultItemRef${index}`] as HTMLDivElement[]
if (!currentInstance) return 0
const currentRef = currentInstance[0]
// 128 = result-item 56 + 56 = 112 margin8 + 8 = 16
const scrollTop = currentRef.offsetTop + 128
return scrollTop > scrollbarHeight.value ? scrollTop - scrollbarHeight.value : 0
}
//
onBeforeMount(() => {
window.addEventListener("resize", getScrollbarHeight)
})
//
onMounted(() => {
getScrollbarHeight()
})
//
onBeforeUnmount(() => {
window.removeEventListener("resize", getScrollbarHeight)
})
defineExpose({ getScrollTop })
</script>
<template>
<!-- 外层 div 不能删除是用来接收父组件 click 事件的 -->
<div>
<div
v-for="(item, index) in props.data"
:key="index"
:ref="`resultItemRef${index}`"
class="result-item"
:style="itemStyle(item)"
@mouseenter="handleMouseenter(item)"
>
<SvgIcon v-if="item.meta?.svgIcon" :name="item.meta.svgIcon" class="svg-icon" />
<component v-else-if="item.meta?.elIcon" :is="item.meta.elIcon" class="el-icon" />
<span class="result-item-title">
{{ item.meta?.title }}
</span>
<SvgIcon v-if="modelValue && modelValue === item.name" name="keyboard-enter" class="svg-icon" />
</div>
</div>
</template>
<style lang="scss" scoped>
@import "@@/assets/styles/mixins.scss";
.result-item {
display: flex;
align-items: center;
height: 56px;
padding: 0 15px;
margin-bottom: 8px;
border: 1px solid var(--el-border-color);
border-radius: 4px;
cursor: pointer;
.svg-icon {
min-width: 1em;
font-size: 18px;
}
.el-icon {
width: 1em;
font-size: 18px;
}
&-title {
flex: 1;
margin-left: 12px;
@extend %ellipsis;
}
}
</style>

View File

@ -0,0 +1,29 @@
<script lang="ts" setup>
import Modal from "./Modal.vue"
/** 控制 modal 显隐 */
const visible = ref<boolean>(false)
/** 打开 modal */
function handleOpen() {
visible.value = true
}
</script>
<template>
<div>
<el-tooltip effect="dark" content="搜索菜单" placement="bottom">
<SvgIcon name="search" @click="handleOpen" class="svg-icon" />
</el-tooltip>
<Modal v-model="visible" />
</div>
</template>
<style lang="scss" scoped>
.svg-icon {
font-size: 20px;
&:focus {
outline: none;
}
}
</style>

View File

@ -1,16 +1,12 @@
<script lang="ts" setup>
import { type ThemeName, useTheme } from "@/hooks/useTheme"
import { useTheme } from "@@/composables/useTheme"
import { MagicStick } from "@element-plus/icons-vue"
const { themeList, activeThemeName, setTheme } = useTheme()
const handleSetTheme = (name: ThemeName) => {
setTheme(name)
}
</script>
<template>
<el-dropdown trigger="click" @command="handleSetTheme">
<el-dropdown trigger="click">
<div>
<el-tooltip effect="dark" content="主题模式" placement="bottom">
<el-icon :size="20">
@ -24,7 +20,7 @@ const handleSetTheme = (name: ThemeName) => {
v-for="(theme, index) in themeList"
:key="index"
:disabled="activeThemeName === theme.name"
:command="theme.name"
@click="(e: MouseEvent) => setTheme(e, theme.name)"
>
<span>{{ theme.title }}</span>
</el-dropdown-item>

View File

@ -0,0 +1,12 @@
import { useAppStore } from "@/pinia/stores/app"
import { DeviceEnum } from "@@/constants/app-key"
const appStore = useAppStore()
const isMobile = computed(() => appStore.device === DeviceEnum.Mobile)
const isDesktop = computed(() => appStore.device === DeviceEnum.Desktop)
/** 设备类型 Composable */
export function useDevice() {
return { isMobile, isDesktop }
}

View File

@ -0,0 +1,42 @@
type OptionValue = string | number
/** Select 需要的数据格式 */
interface SelectOption {
value: OptionValue
label: string
disabled?: boolean
}
/** 接口响应格式 */
type ApiData = ApiResponseData<SelectOption[]>
/** 入参格式,暂时只需要传递 api 函数即可 */
interface FetchSelectProps {
api: () => Promise<ApiData>
}
/** 下拉选择器 Composable */
export function useFetchSelect(props: FetchSelectProps) {
const { api } = props
const loading = ref<boolean>(false)
const options = ref<SelectOption[]>([])
const value = ref<OptionValue>("")
// 调用接口获取数据
const loadData = () => {
loading.value = true
options.value = []
api().then((res) => {
options.value = res.data
}).finally(() => {
loading.value = false
})
}
onMounted(() => {
loadData()
})
return { loading, options, value }
}

View File

@ -0,0 +1,36 @@
import type { LoadingOptions } from "element-plus"
interface UseFullscreenLoading {
<T extends (...args: Parameters<T>) => ReturnType<T>>(
fn: T,
options?: LoadingOptions
): (...args: Parameters<T>) => Promise<ReturnType<T>>
}
interface LoadingInstance {
close: () => void
}
const DEFAULT_OPTIONS = {
lock: true,
text: "加载中..."
}
/**
* @name Composable
* @description fnLoading
* @param fn
* @param options LoadingOptions
* @returns Promise
*/
export const useFullscreenLoading: UseFullscreenLoading = (fn, options = {}) => {
let loadingInstance: LoadingInstance
return async (...args) => {
try {
loadingInstance = ElLoading.service({ ...DEFAULT_OPTIONS, ...options })
return await fn(...args)
} finally {
loadingInstance.close()
}
}
}

View File

@ -0,0 +1,20 @@
import { useSettingsStore } from "@/pinia/stores/settings"
const GREY_MODE = "grey-mode"
const COLOR_WEAKNESS = "color-weakness"
const classList = document.documentElement.classList
/** 初始化 */
function initGreyAndColorWeakness() {
const settingsStore = useSettingsStore()
watchEffect(() => {
classList.toggle(GREY_MODE, settingsStore.showGreyMode)
classList.toggle(COLOR_WEAKNESS, settingsStore.showColorWeakness)
})
}
/** 灰色模式和色弱模式 Composable */
export function useGreyAndColorWeakness() {
return { initGreyAndColorWeakness }
}

View File

@ -0,0 +1,17 @@
import { useSettingsStore } from "@/pinia/stores/settings"
import { LayoutModeEnum } from "@@/constants/app-key"
const settingsStore = useSettingsStore()
const isLeft = computed(() => settingsStore.layoutMode === LayoutModeEnum.Left)
const isTop = computed(() => settingsStore.layoutMode === LayoutModeEnum.Top)
const isLeftTop = computed(() => settingsStore.layoutMode === LayoutModeEnum.LeftTop)
function setLayoutMode(mode: LayoutModeEnum) {
settingsStore.layoutMode = mode
}
/** 布局模式 Composable */
export function useLayoutMode() {
return { isLeft, isTop, isLeftTop, setLayoutMode }
}

View File

@ -1,14 +1,4 @@
import { reactive } from "vue"
interface IDefaultPaginationData {
total: number
currentPage: number
pageSizes: number[]
pageSize: number
layout: string
}
interface IPaginationData {
interface PaginationData {
total?: number
currentPage?: number
pageSizes?: number[]
@ -17,7 +7,7 @@ interface IPaginationData {
}
/** 默认的分页参数 */
const defaultPaginationData: IDefaultPaginationData = {
const DEFAULT_PAGINATION_DATA = {
total: 0,
currentPage: 1,
pageSizes: [10, 20, 50],
@ -25,16 +15,15 @@ const defaultPaginationData: IDefaultPaginationData = {
layout: "total, sizes, prev, pager, next, jumper"
}
export const usePagination = (_paginationData: IPaginationData = {}) => {
/** 合并分页参数 */
const paginationData = reactive(Object.assign({ ...defaultPaginationData }, _paginationData))
/** 改变当前页码 */
/** 分页 Composable */
export function usePagination(initPaginationData: PaginationData = {}) {
// 合并分页参数
const paginationData = reactive({ ...DEFAULT_PAGINATION_DATA, ...initPaginationData })
// 改变当前页码
const handleCurrentChange = (value: number) => {
paginationData.currentPage = value
}
/** 改变页面大小 */
// 改变每页显示条数
const handleSizeChange = (value: number) => {
paginationData.pageSize = value
}

View File

@ -0,0 +1,42 @@
function initStarNotification() {
setTimeout(() => {
ElNotification({
title: "为爱发电!",
type: "success",
message: h(
"div",
null,
[
h("div", null, "所有源码均免费开源,如果对你有帮助,欢迎点个 Star 支持一下!"),
h("a", { style: "color: teal", target: "_blank", href: "https://github.com/un-pany/v3-admin-vite" }, "点击传送")
]
),
duration: 0,
position: "bottom-right"
})
}, 0)
}
function initStoreNotification() {
setTimeout(() => {
ElNotification({
title: "懒人服务?",
type: "warning",
message: h(
"div",
null,
[
h("div", null, "不想自己动手,但想移除 TS 或其他模块?也有懒人套餐!"),
h("a", { style: "color: teal", target: "_blank", href: "https://github.com/un-pany/v3-admin-vite/issues/225" }, "点击查看")
]
),
duration: 0,
position: "bottom-right"
})
}, 500)
}
/** 作者的小心思 */
export function usePany() {
return { initStarNotification, initStoreNotification }
}

View File

@ -0,0 +1,52 @@
import type { Handler } from "mitt"
import type { RouteLocationNormalizedGeneric } from "vue-router"
import mitt from "mitt"
/** 回调函数的类型 */
type Callback = (route: RouteLocationNormalizedGeneric) => void
const emitter = mitt()
const key = Symbol("ROUTE_CHANGE")
let latestRoute: RouteLocationNormalizedGeneric
/** 设置最新的路由信息,触发路由变化事件 */
export function setRouteChange(to: RouteLocationNormalizedGeneric) {
// 触发事件
emitter.emit(key, to)
// 缓存最新的路由信息
latestRoute = to
}
/**
* @name Composable
* @description 1. watch
* @description 2. 使
*/
export function useRouteListener() {
// 回调函数集合
const callbackList: Callback[] = []
// 监听路由变化(可以选择立即执行)
const listenerRouteChange = (callback: Callback, immediate = false) => {
// 缓存回调函数
callbackList.push(callback)
// 监听事件
emitter.on(key, callback as Handler)
// 可以选择立即执行一次回调函数
immediate && latestRoute && callback(latestRoute)
}
// 移除路由变化事件监听器
const removeRouteListener = (callback: Callback) => {
emitter.off(key, callback as Handler)
}
// 组件销毁前移除监听器
onBeforeUnmount(() => {
callbackList.forEach(removeRouteListener)
})
return { listenerRouteChange, removeRouteListener }
}

View File

@ -0,0 +1,75 @@
import { getActiveThemeName, setActiveThemeName } from "@@/utils/cache/local-storage"
import { setCssVar } from "@@/utils/css"
const DEFAULT_THEME_NAME = "normal"
type DefaultThemeName = typeof DEFAULT_THEME_NAME
/** 注册的主题名称, 其中 DefaultThemeName 是必填的 */
export type ThemeName = DefaultThemeName | "dark" | "dark-blue"
interface ThemeList {
title: string
name: ThemeName
}
/** 主题列表 */
const themeList: ThemeList[] = [
{
title: "默认",
name: DEFAULT_THEME_NAME
},
{
title: "黑暗",
name: "dark"
},
{
title: "深蓝",
name: "dark-blue"
}
]
/** 正在应用的主题名称 */
const activeThemeName = ref<ThemeName>(getActiveThemeName() || DEFAULT_THEME_NAME)
/** 设置主题 */
function setTheme({ clientX, clientY }: MouseEvent, value: ThemeName) {
const maxRadius = Math.hypot(
Math.max(clientX, window.innerWidth - clientX),
Math.max(clientY, window.innerHeight - clientY)
)
setCssVar("--v3-theme-x", `${clientX}px`)
setCssVar("--v3-theme-y", `${clientY}px`)
setCssVar("--v3-theme-r", `${maxRadius}px`)
const handler = () => {
activeThemeName.value = value
}
document.startViewTransition ? document.startViewTransition(handler) : handler()
}
/** 在 html 根元素上挂载 class */
function addHtmlClass(value: ThemeName) {
document.documentElement.classList.add(value)
}
/** 在 html 根元素上移除其他主题 class */
function removeHtmlClass(value: ThemeName) {
const otherThemeNameList = themeList.map(item => item.name).filter(name => name !== value)
document.documentElement.classList.remove(...otherThemeNameList)
}
/** 初始化 */
function initTheme() {
// watchEffect 来收集副作用
watchEffect(() => {
const value = activeThemeName.value
removeHtmlClass(value)
addHtmlClass(value)
setActiveThemeName(value)
})
}
/** 主题 Composable */
export function useTheme() {
return { themeList, activeThemeName, initTheme, setTheme }
}

View File

@ -0,0 +1,22 @@
/** 项目标题 */
const VITE_APP_TITLE = import.meta.env.VITE_APP_TITLE ?? "V3 Admin Vite"
/** 动态标题 */
const dynamicTitle = ref<string>("")
/** 设置标题 */
function setTitle(title?: string) {
dynamicTitle.value = title ? `${VITE_APP_TITLE} | ${title}` : VITE_APP_TITLE
}
// 监听标题变化
watch(dynamicTitle, (value, oldValue) => {
if (document && value !== oldValue) {
document.title = value
}
})
/** 标题 Composable */
export function useTitle() {
return { setTitle }
}

View File

@ -0,0 +1,233 @@
import type { Ref } from "vue"
import { debounce } from "lodash-es"
/** 默认配置 */
const DEFAULT_CONFIG = {
/** 防御(默认开启,能防御水印被删除或隐藏,但可能会有性能损耗) */
defense: true,
/** 文本颜色 */
color: "#c0c4cc",
/** 文本透明度 */
opacity: 0.5,
/** 文本字体大小 */
size: 16,
/** 文本字体 */
family: "serif",
/** 文本倾斜角度 */
angle: -20,
/** 一处水印所占宽度(数值越大水印密度越低) */
width: 300,
/** 一处水印所占高度(数值越大水印密度越低) */
height: 200
}
type DefaultConfig = typeof DEFAULT_CONFIG
interface Observer {
watermarkElMutationObserver?: MutationObserver
parentElMutationObserver?: MutationObserver
parentElResizeObserver?: ResizeObserver
}
/** body 元素 */
const bodyEl = ref<HTMLElement>(document.body)
/**
* @name Composable
* @description 1. body
* @description 2.
*/
export function useWatermark(parentEl: Ref<HTMLElement | null> = bodyEl) {
// 备份文本
let backupText: string
// 最终配置
let mergeConfig: DefaultConfig
// 水印元素
let watermarkEl: HTMLElement | null = null
// 观察器
const observer: Observer = {
watermarkElMutationObserver: undefined,
parentElMutationObserver: undefined,
parentElResizeObserver: undefined
}
// 设置水印
const setWatermark = (text: string, config: Partial<DefaultConfig> = {}) => {
if (!parentEl.value) return console.warn("请在 DOM 挂载完成后再调用 setWatermark 方法设置水印")
// 备份文本
backupText = text
// 合并配置
mergeConfig = { ...DEFAULT_CONFIG, ...config }
// 创建或更新水印元素
watermarkEl ? updateWatermarkEl() : createWatermarkEl()
// 监听水印元素和容器元素的变化
addElListener(parentEl.value)
}
// 创建水印元素
const createWatermarkEl = () => {
const isBody = parentEl.value!.tagName.toLowerCase() === bodyEl.value.tagName.toLowerCase()
const watermarkElPosition = isBody ? "fixed" : "absolute"
const parentElPosition = isBody ? "" : "relative"
watermarkEl = document.createElement("div")
watermarkEl.style.pointerEvents = "none"
watermarkEl.style.top = "0"
watermarkEl.style.left = "0"
watermarkEl.style.position = watermarkElPosition
watermarkEl.style.zIndex = "99999"
const { clientWidth, clientHeight } = parentEl.value!
updateWatermarkEl({ width: clientWidth, height: clientHeight })
// 设置水印容器为相对定位
parentEl.value!.style.position = parentElPosition
// 将水印元素添加到水印容器中
parentEl.value!.appendChild(watermarkEl)
}
// 更新水印元素
const updateWatermarkEl = (
options: Partial<{
width: number
height: number
}> = {}
) => {
if (!watermarkEl) return
backupText && (watermarkEl.style.background = `url(${createBase64()}) left top repeat`)
options.width && (watermarkEl.style.width = `${options.width}px`)
options.height && (watermarkEl.style.height = `${options.height}px`)
}
// 创建 base64 图片
const createBase64 = () => {
const { color, opacity, size, family, angle, width, height } = mergeConfig
const canvasEl = document.createElement("canvas")
canvasEl.width = width
canvasEl.height = height
const ctx = canvasEl.getContext("2d")
if (ctx) {
ctx.fillStyle = color
ctx.globalAlpha = opacity
ctx.font = `${size}px ${family}`
ctx.rotate((Math.PI / 180) * angle)
ctx.fillText(backupText, 0, height / 2)
}
return canvasEl.toDataURL()
}
// 清除水印
const clearWatermark = () => {
if (!parentEl.value || !watermarkEl) return
// 移除对水印元素和容器元素的监听
removeListener()
// 移除水印元素
try {
parentEl.value.removeChild(watermarkEl)
} catch {
// 比如在无防御情况下,用户打开控制台删除了这个元素
console.warn("水印元素已不存在,请重新创建")
} finally {
watermarkEl = null
}
}
// 刷新水印(防御时调用)
const updateWatermark = debounce(() => {
clearWatermark()
createWatermarkEl()
addElListener(parentEl.value!)
}, 100)
// 监听水印元素和容器元素的变化DOM 变化 & DOM 大小变化)
const addElListener = (targetNode: HTMLElement) => {
// 判断是否开启防御
if (mergeConfig.defense) {
// 防止重复添加监听
if (!observer.watermarkElMutationObserver && !observer.parentElMutationObserver) {
// 监听 DOM 变化
addMutationListener(targetNode)
}
} else {
// 无防御时不需要 mutation 监听
removeListener("mutation")
}
// 防止重复添加监听
if (!observer.parentElResizeObserver) {
// 监听 DOM 大小变化
addResizeListener(targetNode)
}
}
// 移除对水印元素和容器元素的监听,传参可指定要移除哪个监听,不传默认移除全部监听
const removeListener = (kind: "mutation" | "resize" | "all" = "all") => {
// 移除 mutation 监听
if (kind === "mutation" || kind === "all") {
observer.watermarkElMutationObserver?.disconnect()
observer.watermarkElMutationObserver = undefined
observer.parentElMutationObserver?.disconnect()
observer.parentElMutationObserver = undefined
}
// 移除 resize 监听
if (kind === "resize" || kind === "all") {
observer.parentElResizeObserver?.disconnect()
observer.parentElResizeObserver = undefined
}
}
// 监听 DOM 变化
const addMutationListener = (targetNode: HTMLElement) => {
// 当观察到变动时执行的回调
const mutationCallback = debounce((mutationList: MutationRecord[]) => {
// 水印的防御(防止用户手动删除水印元素或通过 CSS 隐藏水印)
mutationList.forEach(
debounce((mutation: MutationRecord) => {
switch (mutation.type) {
case "attributes":
mutation.target === watermarkEl && updateWatermark()
break
case "childList":
mutation.removedNodes.forEach((item) => {
item === watermarkEl && targetNode.appendChild(watermarkEl)
})
break
}
}, 100)
)
}, 100)
// 创建观察器实例并传入回调
observer.watermarkElMutationObserver = new MutationObserver(mutationCallback)
observer.parentElMutationObserver = new MutationObserver(mutationCallback)
// 以上述配置开始观察目标节点
observer.watermarkElMutationObserver.observe(watermarkEl!, {
// 观察目标节点属性是否变动,默认为 true
attributes: true,
// 观察目标子节点是否有添加或者删除,默认为 false
childList: false,
// 是否拓展到观察所有后代节点,默认为 false
subtree: false
})
observer.parentElMutationObserver.observe(targetNode, {
attributes: false,
childList: true,
subtree: false
})
}
// 监听 DOM 大小变化
const addResizeListener = (targetNode: HTMLElement) => {
// 当 targetNode 元素大小变化时去更新整个水印的大小
const resizeCallback = debounce(() => {
const { clientWidth, clientHeight } = targetNode
updateWatermarkEl({ width: clientWidth, height: clientHeight })
}, 500)
// 创建一个观察器实例并传入回调
observer.parentElResizeObserver = new ResizeObserver(resizeCallback)
// 开始观察目标节点
observer.parentElResizeObserver.observe(targetNode)
}
// 在组件卸载前移除水印以及各种监听
onBeforeUnmount(() => {
clearWatermark()
})
return { setWatermark, clearWatermark }
}

View File

@ -0,0 +1,22 @@
/** 设备类型 */
export enum DeviceEnum {
Mobile,
Desktop
}
/** 布局模式 */
export enum LayoutModeEnum {
Left = "left",
Top = "top",
LeftTop = "left-top"
}
/** 侧边栏打开状态常量 */
export const SIDEBAR_OPENED = "opened"
/** 侧边栏关闭状态常量 */
export const SIDEBAR_CLOSED = "closed"
export type SidebarOpened = typeof SIDEBAR_OPENED
export type SidebarClosed = typeof SIDEBAR_CLOSED

View File

@ -0,0 +1,11 @@
const SYSTEM_NAME = "v3-admin-vite"
/** 缓存数据时用到的 Key */
export class CacheKey {
static readonly TOKEN = `${SYSTEM_NAME}-token-key`
static readonly CONFIG_LAYOUT = `${SYSTEM_NAME}-config-layout-key`
static readonly SIDEBAR_STATUS = `${SYSTEM_NAME}-sidebar-status-key`
static readonly ACTIVE_THEME_NAME = `${SYSTEM_NAME}-active-theme-name-key`
static readonly VISITED_VIEWS = `${SYSTEM_NAME}-visited-views-key`
static readonly CACHED_VIEWS = `${SYSTEM_NAME}-cached-views-key`
}

16
src/common/utils/cache/cookies.ts vendored Normal file
View File

@ -0,0 +1,16 @@
// 统一处理 Cookie
import { CacheKey } from "@@/constants/cache-key"
import Cookies from "js-cookie"
export function getToken() {
return Cookies.get(CacheKey.TOKEN)
}
export function setToken(token: string) {
Cookies.set(CacheKey.TOKEN, token)
}
export function removeToken() {
Cookies.remove(CacheKey.TOKEN)
}

60
src/common/utils/cache/local-storage.ts vendored Normal file
View File

@ -0,0 +1,60 @@
// 统一处理 localStorage
import type { LayoutsConfig } from "@/layouts/config"
import type { TagView } from "@/pinia/stores/tags-view"
import type { ThemeName } from "@@/composables/useTheme"
import type { SidebarClosed, SidebarOpened } from "@@/constants/app-key"
import { CacheKey } from "@@/constants/cache-key"
// #region 系统布局配置
export function getLayoutsConfig() {
const json = localStorage.getItem(CacheKey.CONFIG_LAYOUT)
return json ? (JSON.parse(json) as LayoutsConfig) : null
}
export function setLayoutsConfig(settings: LayoutsConfig) {
localStorage.setItem(CacheKey.CONFIG_LAYOUT, JSON.stringify(settings))
}
export function removeLayoutsConfig() {
localStorage.removeItem(CacheKey.CONFIG_LAYOUT)
}
// #endregion
// #region 侧边栏状态
export function getSidebarStatus() {
return localStorage.getItem(CacheKey.SIDEBAR_STATUS)
}
export function setSidebarStatus(sidebarStatus: SidebarOpened | SidebarClosed) {
localStorage.setItem(CacheKey.SIDEBAR_STATUS, sidebarStatus)
}
// #endregion
// #region 正在应用的主题名称
export function getActiveThemeName() {
return localStorage.getItem(CacheKey.ACTIVE_THEME_NAME) as ThemeName | null
}
export function setActiveThemeName(themeName: ThemeName) {
localStorage.setItem(CacheKey.ACTIVE_THEME_NAME, themeName)
}
// #endregion
// #region 标签栏
export function getVisitedViews() {
const json = localStorage.getItem(CacheKey.VISITED_VIEWS)
return JSON.parse(json ?? "[]") as TagView[]
}
export function setVisitedViews(views: TagView[]) {
views.forEach((view) => {
// 删除不必要的属性,防止 JSON.stringify 处理到循环引用
delete view.matched
delete view.redirectedFrom
})
localStorage.setItem(CacheKey.VISITED_VIEWS, JSON.stringify(views))
}
export function getCachedViews() {
const json = localStorage.getItem(CacheKey.CACHED_VIEWS)
return JSON.parse(json ?? "[]") as string[]
}
export function setCachedViews(views: string[]) {
localStorage.setItem(CacheKey.CACHED_VIEWS, JSON.stringify(views))
}
// #endregion

18
src/common/utils/css.ts Normal file
View File

@ -0,0 +1,18 @@
/** 获取指定元素(默认全局)上的 CSS 变量的值 */
export function getCssVar(varName: string, element: HTMLElement = document.documentElement) {
if (!varName?.startsWith("--")) {
console.error("CSS 变量名应以 '--' 开头")
return ""
}
// 没有拿到值时,会返回空串
return getComputedStyle(element).getPropertyValue(varName)
}
/** 设置指定元素(默认全局)上的 CSS 变量的值 */
export function setCssVar(varName: string, value: string, element: HTMLElement = document.documentElement) {
if (!varName?.startsWith("--")) {
console.error("CSS 变量名应以 '--' 开头")
return
}
element.style.setProperty(varName, value)
}

View File

@ -0,0 +1,9 @@
import dayjs from "dayjs"
const INVALID_DATE = "N/A"
/** 格式化日期时间 */
export function formatDateTime(datetime: string | number | Date = "", template: string = "YYYY-MM-DD HH:mm:ss") {
const day = dayjs(datetime)
return day.isValid() ? day.format(template) : INVALID_DATE
}

View File

@ -0,0 +1,13 @@
import { useUserStore } from "@/pinia/stores/user"
import { isArray } from "@@/utils/validate"
/** 全局权限判断函数,和权限指令 v-permission 功能类似 */
export function checkPermission(permissionRoles: string[]): boolean {
if (isArray(permissionRoles) && permissionRoles.length > 0) {
const { roles } = useUserStore()
return roles.some(role => permissionRoles.includes(role))
} else {
console.error("参数必须是一个数组且长度大于 0参考checkPermission(['admin', 'editor'])")
return false
}
}

View File

@ -0,0 +1,15 @@
/** 判断是否为数组 */
export function isArray<T>(arg: T) {
return Array.isArray ? Array.isArray(arg) : Object.prototype.toString.call(arg) === "[object Array]"
}
/** 判断是否为字符串 */
export function isString(str: unknown) {
return typeof str === "string" || str instanceof String
}
/** 判断是否为外链 */
export function isExternal(path: string) {
const reg = /^(https?:|mailto:|tel:)/
return reg.test(path)
}

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