mirror of
https://github.com/soybeanjs/soybean-admin.git
synced 2025-09-30 23:26:41 +08:00
build(projects): update branch thin from main [更新thin分支]
This commit is contained in:
parent
971915948b
commit
e43ede0f67
2
.env
2
.env
@ -10,7 +10,7 @@ VITE_APP_DESC=SoybeanAdmin是一个中后台管理系统模版
|
||||
VITE_AUTH_ROUTE_MODE=dynamic
|
||||
|
||||
# 路由首页(根路由重定向), 用于static模式的权限路由,dynamic模式取决于后端返回的路由首页
|
||||
VITE_ROUTE_HOME_PATH=/dashboard/analysis
|
||||
VITE_ROUTE_HOME_PATH=/multi-menu/first/second
|
||||
|
||||
# iconify图标作为组件的前缀
|
||||
VITE_ICON_PREFFIX=icon
|
||||
|
@ -1,6 +0,0 @@
|
||||
VITE_VISUALIZER=N
|
||||
|
||||
VITE_COMPRESS=N
|
||||
|
||||
# gzip | brotliCompress | deflate | deflateRaw
|
||||
VITE_COMPRESS_TYPE=gzip
|
@ -1,4 +0,0 @@
|
||||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
npx --no-install commitlint --edit "$1"
|
@ -1,4 +0,0 @@
|
||||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
pnpm lint && pnpm typecheck
|
223
CHANGELOG.md
223
CHANGELOG.md
@ -1,223 +0,0 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
||||
|
||||
### [0.9.7](https://github.com/honghuangdc/soybean-admin/compare/v0.9.6...v0.9.7) (2022-11-07)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **projects:** 全局搜索菜单及消息通知适配移动端 ([97e2ffd](https://github.com/honghuangdc/soybean-admin/commit/97e2ffddf4ac047133dc016a91ac07556e562d29))
|
||||
* **projects:** 实现用户管理页面 ([472f93b](https://github.com/honghuangdc/soybean-admin/commit/472f93bfc111e8ca94adef823b8cc12e4f8cd2c6))
|
||||
* **projects:** 适配移动端,修复Tab关闭图标的bug ([296b154](https://github.com/honghuangdc/soybean-admin/commit/296b154be5dfe410b3cfca9afaeeaf9c47de3e0c)), closes [#87](https://github.com/honghuangdc/soybean-admin/issues/87) [#106](https://github.com/honghuangdc/soybean-admin/issues/106) [#109](https://github.com/honghuangdc/soybean-admin/issues/109) [#111](https://github.com/honghuangdc/soybean-admin/issues/111)
|
||||
* **projects:** 添加请求适配adapter层应用的示例页面 ([8d11a6a](https://github.com/honghuangdc/soybean-admin/commit/8d11a6affcfa37344011a6aaf3d6e005546f0e61))
|
||||
* **projects:** 添加生产的主题配置缓存 ([718c362](https://github.com/honghuangdc/soybean-admin/commit/718c36263e451a39bca6da6c33657a09515ffbcc))
|
||||
* **projects:** 添加系统管理的页面 ([c33b5eb](https://github.com/honghuangdc/soybean-admin/commit/c33b5ebfefbb3ae507141bd2e9414231fd1512d4))
|
||||
* **projects:** 添加组件名称,调整vue文件里面的类型声明位置 ([f64bc91](https://github.com/honghuangdc/soybean-admin/commit/f64bc91ce285c7a9806ed0f6ae970d9b598fd0cb))
|
||||
* **projects:** 添加provide、inject上下文示例 ([a444731](https://github.com/honghuangdc/soybean-admin/commit/a444731e9eef43022930c3550dcfc058e70a2941))
|
||||
* **projects:** 系统消息组件代码优化 ([9518372](https://github.com/honghuangdc/soybean-admin/commit/9518372fe0431d4e08a5f40d1b2982691fbb4107))
|
||||
* **projects:** 增加返回顶部功能 ([894b0f1](https://github.com/honghuangdc/soybean-admin/commit/894b0f1c182a36ad1774a8144bf50dd4e0b62a46))
|
||||
* **projects:** 增加系统消息组件 ([afa0134](https://github.com/honghuangdc/soybean-admin/commit/afa0134fdd63c253e102bc129e275d16ca25508e))
|
||||
* **projects:** add constant route page without login status[添加未登录可访问的固定路由示例页面] ([78efd77](https://github.com/honghuangdc/soybean-admin/commit/78efd7793a241811065caf56edf7e68aea58bc8c))
|
||||
* **projects:** add pinia setup syntax example: setup-store[添加setup syntax的pinia示例setup-store] ([82c4b09](https://github.com/honghuangdc/soybean-admin/commit/82c4b09b9411390f97c2d10bb211c66ed9656b63))
|
||||
* **projects:** import i18n [引入i18n] ([b632b7f](https://github.com/honghuangdc/soybean-admin/commit/b632b7ffed5c6d6ec15c23c8cce030bf669c554f))
|
||||
* **projects:** new router system [新的路由系统] ([c7b6a3f](https://github.com/honghuangdc/soybean-admin/commit/c7b6a3fbecd1ba051833e4e47b75a06935f212c8))
|
||||
* **projects:** refactor icon system, unify icon usage [重构图标系统,统一图标用法] ([811f820](https://github.com/honghuangdc/soybean-admin/commit/811f820644053606e50624c2f184f9669f3eff7e))
|
||||
* **projects:** support constant route without login status[支持未登录状态下访问自定义的固定路由] ([a539112](https://github.com/honghuangdc/soybean-admin/commit/a539112a0f53183ee073d4eb9034ef48209fe30c))
|
||||
* **projects:** useNaiveTable函数:类型部分 ([02992dc](https://github.com/honghuangdc/soybean-admin/commit/02992dc02d105cbfcebbea397438c68db1fa8177))
|
||||
* **tabs:** 多页签增加关闭所有 ([8237adb](https://github.com/honghuangdc/soybean-admin/commit/8237adb9c0b187911df37d6d99fd84718bc3ea8f))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **deps:** decrease @types/node version to fix TS type error [降低@types/node版本修复TS的类型错误] ([149d22a](https://github.com/honghuangdc/soybean-admin/commit/149d22a4a491ca5fc6c52375046e9f1cb86ee76d))
|
||||
* **projects:** 修复多个后端服务时的本地代理 ([2aba58c](https://github.com/honghuangdc/soybean-admin/commit/2aba58c973e5d0ea975443a8b22c9d94283d4fb9))
|
||||
* **projects:** 修复构建后mockjs对xhr的影响问题 ([7757285](https://github.com/honghuangdc/soybean-admin/commit/77572855c3f7161697f42e6da36771c15707f0ab))
|
||||
* **projects:** 修复图标的TS类型 ([dbd6760](https://github.com/honghuangdc/soybean-admin/commit/dbd676095b42aaebc783d5c89478306a453195a5))
|
||||
* **projects:** 修复eslint规则 ([d7f5bf3](https://github.com/honghuangdc/soybean-admin/commit/d7f5bf3373e7884b8dc2c696a2c36e9cf27ad64b))
|
||||
* **projects:** 修复import.meta.env的TS类型 ([1994262](https://github.com/honghuangdc/soybean-admin/commit/19942625d58e673126db5249488555de71d18457))
|
||||
* **projects:** 修复tab不显示路由首页的问题 ([a792bb5](https://github.com/honghuangdc/soybean-admin/commit/a792bb5cb3c388ba3b93e17bab8f42d23cd5df4a))
|
||||
* **projects:** 修复TS类型问题 ([16dce9a](https://github.com/honghuangdc/soybean-admin/commit/16dce9a4ce4d3aa822d70f6e5199eb9c86e33ad9))
|
||||
* **projects:** add iconify json ([8a1ec93](https://github.com/honghuangdc/soybean-admin/commit/8a1ec938e7a26728919024e9f5b7b0af2b270aba))
|
||||
* **svg-icon:** 自定义图标在Dropdown组件下hover状态无法显示图标 ([0523f08](https://github.com/honghuangdc/soybean-admin/commit/0523f0838246041bfc09130e21369bd777f63682))
|
||||
* **utils:** 修复iconifyRender ([c37d0ac](https://github.com/honghuangdc/soybean-admin/commit/c37d0ac7887a3451b8558fc4aa6c05ed3b0ef74f))
|
||||
|
||||
### [0.9.6](https://github.com/honghuangdc/soybean-admin/compare/v0.9.5...v0.9.6) (2022-06-15)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **projects:** 本地svg动态渲染图标 ([c3c975e](https://github.com/honghuangdc/soybean-admin/commit/c3c975ee1142987b7ded0107bf91d0080d5651fe)), closes [#61](https://github.com/honghuangdc/soybean-admin/issues/61)
|
||||
* **projects:** 上下结构,菜单支持横向滚动 ([808051b](https://github.com/honghuangdc/soybean-admin/commit/808051b29dd682e1cbcf0e211774efb9cc12713a))
|
||||
* **projects:** 新增Antv G2图表示例 ([2d64a2e](https://github.com/honghuangdc/soybean-admin/commit/2d64a2e57c8d83c8d06f210eeefef8f31b3abeb9))
|
||||
* **projects:** 增加设置当前Tab页签名称功能 ([487213b](https://github.com/honghuangdc/soybean-admin/commit/487213b64853765e2bd186474e4607572624a33e))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **projects:** 设置tab标题导致meta属性丢失 ([efcfa57](https://github.com/honghuangdc/soybean-admin/commit/efcfa576d52a7eab644f3b4c65af153442887fab))
|
||||
* **projects:** 修复顶部菜单的位置失效问题 ([4ee0d94](https://github.com/honghuangdc/soybean-admin/commit/4ee0d94f1bde83c788fc0dcb084402359c04fb1b))
|
||||
|
||||
### [0.9.5](https://github.com/honghuangdc/soybean-admin/compare/v0.9.4...v0.9.5) (2022-06-06)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **projects:** 支持同一路由根据不同query和hash同时显示不同Tab ([4122685](https://github.com/honghuangdc/soybean-admin/commit/4122685803f8a0a485682d16cec74e27945adc47)), closes [#64](https://github.com/honghuangdc/soybean-admin/issues/64)
|
||||
* **projects:** 动态路由根路由重定向只需取决于后端返回的路由首页 ([434ab1c](https://github.com/honghuangdc/soybean-admin/commit/434ab1c560b260f8a19895405eb1d3c3313052d7))
|
||||
* **projects:** 补充更多的ECharts示例 ([c776249](https://github.com/honghuangdc/soybean-admin/commit/c7762490def77695bedf179ffc63e3e95d15e14d))
|
||||
* **projects:** 添加百度地图、升级依赖 ([39854a4](https://github.com/honghuangdc/soybean-admin/commit/39854a492b9cce71e0c7ed52af9985cb4abd6a97))
|
||||
* **projects:** 添加插件页面:图表 ([0a46ea0](https://github.com/honghuangdc/soybean-admin/commit/0a46ea08443f6b879434e925d440cf07e9494fcb))
|
||||
* **projects:** 添加自动跟随系统主题设置 ([ba07b69](https://github.com/honghuangdc/soybean-admin/commit/ba07b695dd9dc5d3f8ebf57d0f2e69d624994962))
|
||||
* **projects:** 添加antv g2图表示例 ([44b022a](https://github.com/honghuangdc/soybean-admin/commit/44b022aefd7dbb4c34886814cf04767450dec026))
|
||||
* **projects:** 引入echarts替换antvG2plot ([e7ad086](https://github.com/honghuangdc/soybean-admin/commit/e7ad08685e8ac52a8906fc94e656192275f9764c))
|
||||
* **route:** 路由meta新增activeMenu属性 ([ebd16a4](https://github.com/honghuangdc/soybean-admin/commit/ebd16a4d1ab1a95a27838a2d4f20cc1d1e7309ae))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **projects:** 修复@antv/g2生产环境报错 ([4558c24](https://github.com/honghuangdc/soybean-admin/commit/4558c24d1c1e1faa3326650fc16e6baf384509ac))
|
||||
* **projects:** 修复插件不存在的错误提示 ([7165282](https://github.com/honghuangdc/soybean-admin/commit/716528206e9f63e873607d0afd59d83f6984e3fe))
|
||||
* **projects:** 修复权限切换路由数据未更新的问题 ([60f9125](https://github.com/honghuangdc/soybean-admin/commit/60f912508b0e685957fb22ef0ed1f83272847263))
|
||||
* **projects:** 修复页面切换时导致的溢出滚动条 ([e023306](https://github.com/honghuangdc/soybean-admin/commit/e0233061d3bca236b4c4bb462ce00f7ca186b9fa))
|
||||
* **route:** 当为左侧混合菜单时activeMenu无效情况 ([3e4f9e2](https://github.com/honghuangdc/soybean-admin/commit/3e4f9e282442073447c5c24c33d65bc6130978ee))
|
||||
|
||||
### [0.9.4](https://github.com/honghuangdc/soybean-admin/compare/v0.9.3...v0.9.4) (2022-04-28)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **layouts:** 添加侧边栏/头部的反转模式来增加对比度 ([861c8b9](https://github.com/honghuangdc/soybean-admin/commit/861c8b9852e0097a1f6b79ac2c10d19add123bde))
|
||||
* **layouts:** 添加侧边栏/头部的反转模式来增加对比度 ([3c8dd77](https://github.com/honghuangdc/soybean-admin/commit/3c8dd772f89d2b656a42c4f7164e581acdb2b1a5))
|
||||
* **projects:** 插件方式按需引入naiveUI ([6bed9ea](https://github.com/honghuangdc/soybean-admin/commit/6bed9ead38af6d58f6cd9e520db848ae5cbfa4db))
|
||||
* **projects:** 登录页背景图片位置适配移动端 ([24010d0](https://github.com/honghuangdc/soybean-admin/commit/24010d05fb1ff51cb5e5d94ffe310206a9638711))
|
||||
* **projects:** 登录页面适配移动端 ([ec0776e](https://github.com/honghuangdc/soybean-admin/commit/ec0776e268cd3d1031e9ecd794abce271a675793))
|
||||
* **projects:** 权限完善及权限示例页面 ([807448a](https://github.com/honghuangdc/soybean-admin/commit/807448aec5b041535fe4fbac90eca1138b2f439c))
|
||||
* **projects:** 添加请求适配器的请求示例 ([bed4292](https://github.com/honghuangdc/soybean-admin/commit/bed4292ed380e77ac428ab057abc42eceb72af53))
|
||||
* **projects:** 新增静态路由 ([ca2dfa6](https://github.com/honghuangdc/soybean-admin/commit/ca2dfa6185aa7a4e58184bcfef2a1246a52f88fd))
|
||||
* **projects:** 引入unocss替换windicss ([c9d3e5a](https://github.com/honghuangdc/soybean-admin/commit/c9d3e5a3fdf59179dcfc122ab8369c492ea7832e))
|
||||
* **projects:** HTML lang 修改为 zh-cmn-Hans ([b9c5c34](https://github.com/honghuangdc/soybean-admin/commit/b9c5c349790b1e83a7acd1f2c53a86c9221944ff))
|
||||
* **projects:** HTML lang 修改为 zh-cmn-Hans ([dbeb595](https://github.com/honghuangdc/soybean-admin/commit/dbeb595c0b9fc11e7d166a7684af37cc971f1a11))
|
||||
* **projects:** mock添加权限过滤 ([7f4350a](https://github.com/honghuangdc/soybean-admin/commit/7f4350aeb673dab59192584177a897aacebe4b28))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **projects:** 去除从环境文件引入端口号导致的错误 ([2d6d179](https://github.com/honghuangdc/soybean-admin/commit/2d6d179d669ea71cca3fe97ac840e4856bff4051))
|
||||
* **projects:** 全局搜索弹窗弹出时动画闪屏问题 ([bb1bbf2](https://github.com/honghuangdc/soybean-admin/commit/bb1bbf272438f4ed440735118c6a9ec04c7d109f))
|
||||
* **projects:** 添加.npmrc修复无法获取自动引入的全局组件声明类型 ([e8488e4](https://github.com/honghuangdc/soybean-admin/commit/e8488e4d5237e5e03ec07ff07d03115389d5b1ef))
|
||||
* **projects:** 添加获取路由组件文件未找到时的错误提示 ([219f87f](https://github.com/honghuangdc/soybean-admin/commit/219f87f46758f328f26697f66d8583f49c0d41de))
|
||||
* **projects:** 修复获取vite环境变量的方式 ([46e1ae7](https://github.com/honghuangdc/soybean-admin/commit/46e1ae7825b2b204ce3cdd63b3c64f39bff096d0))
|
||||
* **projects:** 修复路由守卫的动态路由逻辑 ([e6c26fc](https://github.com/honghuangdc/soybean-admin/commit/e6c26fcb4ae085f9fd7d7eb9183ddba020d0b5da))
|
||||
* **projects:** 修复样式 ([e899914](https://github.com/honghuangdc/soybean-admin/commit/e8999144266761b3b701442975c3c00251240d53))
|
||||
* **projects:** 修复在新版vite下环境变量获取不到的问题 ([3fb13ca](https://github.com/honghuangdc/soybean-admin/commit/3fb13ca9e710549d2ddeb774fe08fabd27d5ae11))
|
||||
* **projects:** 修复vite alias ([cd7ca8f](https://github.com/honghuangdc/soybean-admin/commit/cd7ca8f4c77ac8c753b753ba698a9573d6c37bf9))
|
||||
|
||||
### [0.9.3](https://github.com/honghuangdc/soybean-admin/compare/v0.9.2...v0.9.3) (2022-03-12)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **components:** svgIcon,添加type,调整size方案 ([ce4e039](https://github.com/honghuangdc/soybean-admin/commit/ce4e039f48001b47a2933e807f5410a9573781b9))
|
||||
* **projects:** 引入soybean-admin-tab、去除vite-plugin-svg-icons,用unplugin-icons实现自定义svg的iconify写法、代码优化 ([a1a57a1](https://github.com/honghuangdc/soybean-admin/commit/a1a57a185ce5004888ca4e1611973665ee46980b))
|
||||
* **projects:** 新增子菜单图标和多页签图标 ([f5c56c3](https://github.com/honghuangdc/soybean-admin/commit/f5c56c355ce41157b20ed0a10272a28e6d8b2b49))
|
||||
* **projects:** 新增自定义svg图标动态渲染 ([f83c7b5](https://github.com/honghuangdc/soybean-admin/commit/f83c7b59b893ab6e210188e92c4177b3d01392ce))
|
||||
* **projects:** 添加naiveUI按需引入 ([a810ef8](https://github.com/honghuangdc/soybean-admin/commit/a810ef85b19e4b74f3ddb3c69d17c050e556ee90))
|
||||
* **projects:** 添加SvgIcon,配置vite plugin ([378d55a](https://github.com/honghuangdc/soybean-admin/commit/378d55ac0e11cdf115ce3cb8e281d60f7fc4ff7a))
|
||||
* **projects:** 添加全局组件自动引入注册 ([f5a043b](https://github.com/honghuangdc/soybean-admin/commit/f5a043b11a403927828ae922bdae411a4e5ae3c6))
|
||||
* **projects:** 添加网络代理 ([094dca9](https://github.com/honghuangdc/soybean-admin/commit/094dca961f608404352ac360f44496423d88dae8))
|
||||
* **projects:** 重构项目的TS类型架构,去除interface文件夹 ([8191490](https://github.com/honghuangdc/soybean-admin/commit/8191490f39fc011096edd77c3156eb4fe33d4e1c))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **components:** 修复组件LoadingEmptyWrapper适应暗黑模式 ([811b15e](https://github.com/honghuangdc/soybean-admin/commit/811b15e672c9d69e9c5789eb11ab2db7bd729f37))
|
||||
* **components:** 组件LoadingEmptyWrapper添加背景颜色动画过渡 ([7add5c2](https://github.com/honghuangdc/soybean-admin/commit/7add5c2edfcabadb77084179d464b849d880d5e6))
|
||||
* **projects:** 修复 BASE_URL 没有生效的问题 ([72d7dcf](https://github.com/honghuangdc/soybean-admin/commit/72d7dcfa5ee8dc6f3601f4d65c6aca9ad2cc5d5c))
|
||||
* **projects:** 修复页面切换动画开关不生效 ([9d4ed61](https://github.com/honghuangdc/soybean-admin/commit/9d4ed617fb80095e521d8063718283459711118f))
|
||||
* **projects:** 修复页面切换动画无变化 ([c4546bd](https://github.com/honghuangdc/soybean-admin/commit/c4546bdfa303f1e89c0d7ddd46b54e4ec5170096))
|
||||
|
||||
### [0.9.2](https://github.com/honghuangdc/soybean-admin/compare/v0.9.1...v0.9.2) (2022-02-11)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **projects:** 迁移全局搜索菜单功能 ([554d7fd](https://github.com/honghuangdc/soybean-admin/commit/554d7fd6114b9cf6df571c3cb02f4cb0cc6dcfd4))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **components:** 修复Tab在移动端设备无法点击的问题 ([2c9660f](https://github.com/honghuangdc/soybean-admin/commit/2c9660fdbf9a84e980db0aff5cd0aed0f75963ca))
|
||||
* **projects:** 修复分析页和工作台的布局问题 ([e93b94c](https://github.com/honghuangdc/soybean-admin/commit/e93b94cb2435a130bb1d94a703328af342cd24c9))
|
||||
* **projects:** 修复项目配置拷贝功能 ([a7a269d](https://github.com/honghuangdc/soybean-admin/commit/a7a269d6a61ccd25883e6bb69639d39e0260587d))
|
||||
* **projects:** vite配置修复 ([facc00e](https://github.com/honghuangdc/soybean-admin/commit/facc00e8b4998dc8bd338e3b63a652b4bfe2ed3e))
|
||||
|
||||
### [0.9.1](https://github.com/honghuangdc/soybean-admin/compare/v0.1.3...v0.9.1) (2022-01-23)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **projects:** 新版重构完成 ([68b4230](https://github.com/honghuangdc/soybean-admin/commit/68b42304d5964246775c7a82dcc1406c5db7a4e4))
|
||||
|
||||
### [0.1.3](https://github.com/honghuangdc/soybean-admin/compare/v0.1.2...v0.1.3) (2022-01-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **projects:** 修复未登录时会调用获取用户路由的接口 ([21bab1f](https://github.com/honghuangdc/soybean-admin/commit/21bab1f7c30611fe59dc91c7a73050ccb49a4658))
|
||||
* **projects:** 修复路由守卫的动态路由逻辑 ([b61b0ce](https://github.com/honghuangdc/soybean-admin/commit/b61b0ce25fdcbaf29ca64cbcc467e12faa947625))
|
||||
|
||||
### [0.1.2](https://github.com/honghuangdc/soybean-admin/compare/v0.1.1...v0.1.2) (2022-01-21)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **projects:** 添加缓存主题色 ([3709297](https://github.com/honghuangdc/soybean-admin/commit/37092974d37b2e661d4cbf9d27c89b5e99119cd7))
|
||||
* **projects:** 添加页面缓存、记录在tab中的缓存页面的滚动条位置 ([1d63a83](https://github.com/honghuangdc/soybean-admin/commit/1d63a838226df4f48e7f2a15b5a05d4b496d3c69))
|
||||
|
||||
### [0.1.1](https://github.com/honghuangdc/soybean-admin/compare/v0.0.5...v0.1.1) (2022-01-20)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **projects:** theme store完成 ([bf020a8](https://github.com/honghuangdc/soybean-admin/commit/bf020a82580e6b1fbda1cc1e0bd6176770434884))
|
||||
* **projects:** 主题配置抽屉: 迁移其他功能 ([6d132c5](https://github.com/honghuangdc/soybean-admin/commit/6d132c59770e925cfc61217dcefa5b4d937604df))
|
||||
* **projects:** 主题配置抽屉:迁移暗黑模式、布局模式、添加颜色选择面板 ([912bfdf](https://github.com/honghuangdc/soybean-admin/commit/912bfdf4390ab624d3f8e343be88e8c1cf7ab5b6))
|
||||
* **projects:** 创建自定义布局组件SoybeanLayout ([0653fb1](https://github.com/honghuangdc/soybean-admin/commit/0653fb144fe9d49f24ef4fe6e4a58de6de342b78))
|
||||
* **projects:** 初始化加载效果:应用主题颜色 ([035fa11](https://github.com/honghuangdc/soybean-admin/commit/035fa114c9fd638cf467e6a73a8e4c558f503deb))
|
||||
* **projects:** 图标选择器增加扩展树形 ([041012b](https://github.com/honghuangdc/soybean-admin/commit/041012b3ee04d960c1e38895839225613f7af377))
|
||||
* **projects:** 增加Icon选择器组件 ([9472b51](https://github.com/honghuangdc/soybean-admin/commit/9472b51811f419e9139de81c73f2c71d170700c2))
|
||||
* **projects:** 增加全局搜索菜单功能 ([b9ce691](https://github.com/honghuangdc/soybean-admin/commit/b9ce69130b12712013228326f883e2d973e4e46a))
|
||||
* **projects:** 增加项目文档外链 ([1901a0b](https://github.com/honghuangdc/soybean-admin/commit/1901a0bfb7bfa516dfda552675397ddec96b8d4b))
|
||||
* **projects:** 多级路由的所有子路由转换成二级路由 ([85b55bb](https://github.com/honghuangdc/soybean-admin/commit/85b55bb37a0a06e2645b96ed81aefe463127121a))
|
||||
* **projects:** 引入mockjs ([9bc682d](https://github.com/honghuangdc/soybean-admin/commit/9bc682dae878c084e38a0e2c9a4a2de171023c48))
|
||||
* **projects:** 新增BasicLayout布局 ([006467a](https://github.com/honghuangdc/soybean-admin/commit/006467a0626f427da3f516d90c15bf1e1eef0e55))
|
||||
* **projects:** 添加cryptojs,对本地缓存数据进行加密 ([7a0648d](https://github.com/honghuangdc/soybean-admin/commit/7a0648dba55a98f61f4d81696307d86c82a1d34d))
|
||||
* **projects:** 添加NaiveProvider组件 ([c804b21](https://github.com/honghuangdc/soybean-admin/commit/c804b21ceb92133c6ea7cc64c87521cc164e40ce))
|
||||
* **projects:** 添加侧边菜单 ([e25afe2](https://github.com/honghuangdc/soybean-admin/commit/e25afe2fadfe86b9330ee02190a4e40b8321714c))
|
||||
* **projects:** 添加头部折叠按钮 ([a090d39](https://github.com/honghuangdc/soybean-admin/commit/a090d398fc071e246b92d0da80883cf5cbedba0e))
|
||||
* **projects:** 添加常用组件、composables函数 ([230a50a](https://github.com/honghuangdc/soybean-admin/commit/230a50a4cf4d2ebb62b19d6324234243cf6b2f0d))
|
||||
* **projects:** 添加抽屉 ([10e4d81](https://github.com/honghuangdc/soybean-admin/commit/10e4d81bd6a0b35d8cfb4f7a1e981f8ef6ab87cc))
|
||||
* **projects:** 添加表格页面示例 ([51c744c](https://github.com/honghuangdc/soybean-admin/commit/51c744c8e2c8ed9691e92e35b6a88582f22c30d8))
|
||||
* **projects:** 添加路由跳转浏览器新标签 ([987cef3](https://github.com/honghuangdc/soybean-admin/commit/987cef336338987f2e6f0d5aba8f6d4602b297ca))
|
||||
* **projects:** 登录页面开始迁移 ([f5a36a0](https://github.com/honghuangdc/soybean-admin/commit/f5a36a05cb626ec62115283f1d2c534b2a787bdd))
|
||||
* **projects:** 细节完善 ([cc290ac](https://github.com/honghuangdc/soybean-admin/commit/cc290accc29282e9ba655356e2695b6ca4b23605))
|
||||
* **projects:** 细节完善、迁移页面 ([ce531ce](https://github.com/honghuangdc/soybean-admin/commit/ce531ce5dda0b4a1024aa6bd3d68835b59760d57))
|
||||
* **projects:** 菜单搜索增加大小写转换 ([2907868](https://github.com/honghuangdc/soybean-admin/commit/29078689b0652cf4ae852c93d8601a157579adcc))
|
||||
* **projects:** 请求拦截器添加刷新token ([839b82b](https://github.com/honghuangdc/soybean-admin/commit/839b82ba8b052b02e24bcfe6da54160609a4fd4b))
|
||||
* **projects:** 路由页面跳转权限完成 ([0d2a562](https://github.com/honghuangdc/soybean-admin/commit/0d2a5629e89c73a32d6c79f04b51543e1513e006))
|
||||
* **projects:** 迁移多页签 ([28efbdb](https://github.com/honghuangdc/soybean-admin/commit/28efbdbc70733d22011a0eee084d35711429d188))
|
||||
* **projects:** 迁移登录完成 ([b93b80c](https://github.com/honghuangdc/soybean-admin/commit/b93b80cb4b35268dfb6a09517a2494af24748dac))
|
||||
* **projects:** 集成naiveUI主题配置,将css vars添加至html ([2c19684](https://github.com/honghuangdc/soybean-admin/commit/2c196841bd8527d7acccefe6a7545e0a49d532f7))
|
||||
* **projects:** 面包屑 ([09c7658](https://github.com/honghuangdc/soybean-admin/commit/09c7658c21c7dda461dbb528e85b638b5a7dfacd))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **deps:** 降低vite版本 ([c9c5ca9](https://github.com/honghuangdc/soybean-admin/commit/c9c5ca9989eddb084f2706155473123c5dcfc334))
|
||||
* **projects:** 修复redirect-not-found子路由 ([5bfb819](https://github.com/honghuangdc/soybean-admin/commit/5bfb8199b463d9ca6430577b5c493c0b78967aa9))
|
||||
* **projects:** 修复vertical-mix布局、重构初始化的loading ([579e074](https://github.com/honghuangdc/soybean-admin/commit/579e07400e1b9a52934ed808a37c8579a41e8e74))
|
||||
* **projects:** 修复网络请求错误空信息的提示 ([ff9216b](https://github.com/honghuangdc/soybean-admin/commit/ff9216b621aaef0a8203386fa1c3ca5477a2edea))
|
||||
* **projects:** 修复面包屑数据 ([28b5d22](https://github.com/honghuangdc/soybean-admin/commit/28b5d224010a28669ad3a1919fc49f6e2dc808cd))
|
||||
* **projects:** 去除Layout组件冗余代码 ([0e783bc](https://github.com/honghuangdc/soybean-admin/commit/0e783bcf7be0b3a083fe950adfb0afc72b510f97))
|
||||
* **projects:** 请求相关细节修复 ([2ad1ad3](https://github.com/honghuangdc/soybean-admin/commit/2ad1ad32b8410d84902a33d825032c282ca6df86))
|
16
Makefile
16
Makefile
@ -1,16 +0,0 @@
|
||||
ImageTag ?=v0.9.6
|
||||
SoybeanAdminImg ?= soybeanjs/soybean-admin:$(ImageTag)
|
||||
|
||||
VERSION=$(shell git rev-parse --short HEAD)
|
||||
|
||||
soybean-admin: soybean-admin-build soybean-admin-push
|
||||
|
||||
soybean-admin-build:
|
||||
docker build --build-arg version=$(VERSION) -t ${SoybeanAdminImg} -f docker/Dockerfile .
|
||||
|
||||
soybean-admin-push:
|
||||
docker push ${SoybeanAdminImg}
|
||||
|
||||
# run tauri app:
|
||||
run:
|
||||
pnpm tauri dev
|
@ -1,8 +0,0 @@
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
/** 项目构建时间 */
|
||||
const PROJECT_BUILD_TIME = JSON.stringify(dayjs().format('YYYY-MM-DD HH:mm:ss'));
|
||||
|
||||
export const viteDefine = {
|
||||
PROJECT_BUILD_TIME
|
||||
};
|
@ -1,2 +1 @@
|
||||
export * from './define';
|
||||
export * from './proxy';
|
||||
|
@ -1,6 +0,0 @@
|
||||
import ViteCompression from 'vite-plugin-compression';
|
||||
|
||||
export default (viteEnv: ImportMetaEnv) => {
|
||||
const { VITE_COMPRESS_TYPE = 'gzip' } = viteEnv;
|
||||
return ViteCompression({ algorithm: VITE_COMPRESS_TYPE });
|
||||
};
|
@ -2,38 +2,17 @@ import type { PluginOption } from 'vite';
|
||||
import vue from '@vitejs/plugin-vue';
|
||||
import vueJsx from '@vitejs/plugin-vue-jsx';
|
||||
import unocss from '@unocss/vite';
|
||||
import progress from 'vite-plugin-progress';
|
||||
import routerPage from '@soybeanjs/router-page';
|
||||
import html from './html';
|
||||
import unplugin from './unplugin';
|
||||
import mock from './mock';
|
||||
import visualizer from './visualizer';
|
||||
import compress from './compress';
|
||||
import pwa from './pwa';
|
||||
|
||||
/**
|
||||
* vite插件
|
||||
* @param viteEnv - 环境变量配置
|
||||
*/
|
||||
export function setupVitePlugins(viteEnv: ImportMetaEnv): (PluginOption | PluginOption[])[] {
|
||||
const plugins = [
|
||||
vue(),
|
||||
vueJsx(),
|
||||
html(viteEnv),
|
||||
...unplugin(viteEnv),
|
||||
unocss(),
|
||||
mock,
|
||||
progress(),
|
||||
routerPage(),
|
||||
pwa()
|
||||
];
|
||||
|
||||
if (viteEnv.VITE_VISUALIZER === 'Y') {
|
||||
plugins.push(visualizer as PluginOption);
|
||||
}
|
||||
if (viteEnv.VITE_COMPRESS === 'Y') {
|
||||
plugins.push(compress(viteEnv));
|
||||
}
|
||||
const plugins = [vue(), vueJsx(), html(viteEnv), ...unplugin(viteEnv), unocss(), mock, routerPage()];
|
||||
|
||||
return plugins;
|
||||
}
|
||||
|
@ -1,31 +0,0 @@
|
||||
import { VitePWA } from 'vite-plugin-pwa';
|
||||
|
||||
export default function setupVitePwa() {
|
||||
return VitePWA({
|
||||
registerType: 'autoUpdate',
|
||||
includeAssets: ['favicon.ico'],
|
||||
manifest: {
|
||||
name: 'SoybeanAdmin',
|
||||
short_name: 'SoybeanAdmin',
|
||||
theme_color: '#fff',
|
||||
icons: [
|
||||
{
|
||||
src: '/logo.png',
|
||||
sizes: '192x192',
|
||||
type: 'image/png'
|
||||
},
|
||||
{
|
||||
src: '/logo.png',
|
||||
sizes: '512x512',
|
||||
type: 'image/png'
|
||||
},
|
||||
{
|
||||
src: '/logo.png',
|
||||
sizes: '512x512',
|
||||
type: 'image/png',
|
||||
purpose: 'any maskable'
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
import { visualizer } from 'rollup-plugin-visualizer';
|
||||
|
||||
export default visualizer({
|
||||
gzipSize: true,
|
||||
brotliSize: true,
|
||||
open: true
|
||||
});
|
@ -1,99 +0,0 @@
|
||||
/** @type {import('cz-git').UserConfig} */
|
||||
module.exports = {
|
||||
rules: {
|
||||
'body-leading-blank': [1, 'always'],
|
||||
'body-max-line-length': [2, 'always', 100],
|
||||
'footer-leading-blank': [1, 'always'],
|
||||
'footer-max-line-length': [2, 'always', 100],
|
||||
'header-max-length': [2, 'always', 100],
|
||||
'subject-case': [2, 'never', ['sentence-case', 'start-case', 'pascal-case', 'upper-case']],
|
||||
'subject-empty': [2, 'never'],
|
||||
'subject-full-stop': [2, 'never', '.'],
|
||||
'type-case': [2, 'always', 'lower-case'],
|
||||
'type-empty': [2, 'never'],
|
||||
'type-enum': [
|
||||
2,
|
||||
'always',
|
||||
['build', 'chore', 'ci', 'docs', 'feat', 'fix', 'perf', 'refactor', 'revert', 'style', 'test']
|
||||
]
|
||||
},
|
||||
prompt: {
|
||||
alias: { fd: 'docs: fix typos' },
|
||||
messages: {
|
||||
type: '选择你要提交的类型 :',
|
||||
scope: '选择一个提交范围(可选):',
|
||||
customScope: '请输入自定义的提交范围 :',
|
||||
subject: '填写简短精炼的变更描述 :\n',
|
||||
body: '填写更加详细的变更描述(可选)。使用 "|" 换行 :\n',
|
||||
breaking: '列举非兼容性重大的变更(可选)。使用 "|" 换行 :\n',
|
||||
footerPrefixsSelect: '选择关联issue前缀(可选):',
|
||||
customFooterPrefixs: '输入自定义issue前缀 :',
|
||||
footer: '列举关联issue (可选) 例如: #31, #I3244 :\n',
|
||||
confirmCommit: '是否提交或修改commit ?'
|
||||
},
|
||||
types: [
|
||||
{ value: 'init', name: 'init: 初始化' },
|
||||
{ value: 'feat', name: 'feat: 新增功能' },
|
||||
{ value: 'fix', name: 'fix: 修复缺陷' },
|
||||
{ value: 'docs', name: 'docs: 文档更新' },
|
||||
{ value: 'style', name: 'style: 代码格式' },
|
||||
{ value: 'refactor', name: 'refactor: 代码重构' },
|
||||
{ value: 'perf', name: 'perf: 性能提升' },
|
||||
{ value: 'test', name: 'test: 测试相关' },
|
||||
{ value: 'build', name: 'build: 构建相关' },
|
||||
{ value: 'ci', name: 'ci: 持续集成' },
|
||||
{ value: 'revert', name: 'revert: 回退代码' },
|
||||
{ value: 'chore', name: 'chore: 其他修改' }
|
||||
],
|
||||
useEmoji: false,
|
||||
emojiAlign: 'center',
|
||||
themeColorCode: '',
|
||||
scopes: [
|
||||
['projects', '项目搭建'],
|
||||
['components', '组件相关'],
|
||||
['hooks', 'hook 相关'],
|
||||
['utils', 'utils 相关'],
|
||||
['types', 'ts类型相关'],
|
||||
['styles', '样式相关'],
|
||||
['deps', '项目依赖'],
|
||||
['auth', '对 auth 修改'],
|
||||
['release', '版本发布'],
|
||||
['other', '其他修改']
|
||||
].map(([value, description]) => {
|
||||
return {
|
||||
value,
|
||||
name: `${value.padEnd(30)} (${description})`
|
||||
};
|
||||
}),
|
||||
allowCustomScopes: true,
|
||||
allowEmptyScopes: true,
|
||||
customScopesAlign: 'bottom',
|
||||
customScopesAlias: 'custom',
|
||||
emptyScopesAlias: 'empty',
|
||||
upperCaseSubject: false,
|
||||
markBreakingChangeMode: false,
|
||||
allowBreakingChanges: ['feat', 'fix'],
|
||||
breaklineNumber: 100,
|
||||
breaklineChar: '|',
|
||||
skipQuestions: [],
|
||||
issuePrefixs: [
|
||||
// 如果使用 gitee 作为开发管理
|
||||
{ value: 'link', name: 'link: 链接 ISSUES 进行中' },
|
||||
{ value: 'closed', name: 'closed: 标记 ISSUES 已完成' }
|
||||
],
|
||||
customIssuePrefixsAlign: 'top',
|
||||
emptyIssuePrefixsAlias: 'skip',
|
||||
customIssuePrefixsAlias: 'custom',
|
||||
allowCustomIssuePrefixs: true,
|
||||
allowEmptyIssuePrefixs: true,
|
||||
confirmColorize: true,
|
||||
maxHeaderLength: Infinity,
|
||||
maxSubjectLength: Infinity,
|
||||
minSubjectLength: 0,
|
||||
scopeOverrides: undefined,
|
||||
defaultBody: '',
|
||||
defaultIssues: '',
|
||||
defaultScope: '',
|
||||
defaultSubject: ''
|
||||
}
|
||||
};
|
@ -1,32 +0,0 @@
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
.npmrc
|
||||
.cache
|
||||
|
||||
tests/server/static
|
||||
tests/server/static/upload
|
||||
|
||||
.local
|
||||
# local env files
|
||||
.env.local
|
||||
.env.*.local
|
||||
.eslintcache
|
||||
|
||||
# Log files
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
# .vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
yarn.lock
|
||||
pnpm-lock.yaml
|
||||
/vite-profile.cpuprofile
|
@ -1,24 +0,0 @@
|
||||
FROM node:16.17.0 as builder
|
||||
|
||||
ENV WORKDIR=/soybean-admin
|
||||
|
||||
WORKDIR $WORKDIR
|
||||
|
||||
COPY ./ $WORKDIR/
|
||||
|
||||
ARG version
|
||||
ENV COMMITID=$version
|
||||
|
||||
RUN npm i -g pnpm
|
||||
|
||||
RUN pnpm install
|
||||
RUN pnpm build
|
||||
|
||||
FROM nginx:alpine as prod
|
||||
|
||||
RUN mkdir /soybean
|
||||
|
||||
COPY --from=builder /soybean-admin/dist /soybean-admin
|
||||
COPY --from=builder /soybean-admin/docker/nginx.conf /etc/nginx/nginx.conf
|
||||
|
||||
EXPOSE 80
|
@ -1,54 +0,0 @@
|
||||
user nginx;
|
||||
worker_processes 1;
|
||||
error_log /var/log/nginx/error.log warn;
|
||||
pid /var/run/nginx.pid;
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||
access_log /var/log/nginx/access.log main;
|
||||
sendfile on;
|
||||
keepalive_timeout 65;
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
|
||||
location / {
|
||||
# 不缓存html,防止程序更新后缓存继续生效
|
||||
if ($request_filename ~* .*\.(?:htm|html)$) {
|
||||
add_header Cache-Control "private, no-store, no-cache, must-revalidate, proxy-revalidate";
|
||||
access_log on;
|
||||
}
|
||||
root /soybean-admin/;
|
||||
index index.html index.htm;
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
# location /soybean/soybean-webserver/v1 {
|
||||
# proxy_set_header Host $host;
|
||||
# proxy_set_header X-Real-IP $remote_addr;
|
||||
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
# proxy_set_header REMOTE-HOST $remote_addr;
|
||||
|
||||
# # 后台接口地址
|
||||
# proxy_pass http://192.168.1.99:30597/v1;
|
||||
# proxy_redirect default;
|
||||
# add_header Access-Control-Allow-Origin *;
|
||||
# add_header Access-Control-Allow-Headers X-Requested-With;
|
||||
# add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
|
||||
# }
|
||||
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
location = /50x.html {
|
||||
root /usr/share/nginx/html;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
import auth from './auth';
|
||||
import route from './route';
|
||||
import management from './management';
|
||||
|
||||
export default [...auth, ...route, ...management];
|
||||
export default [...auth, ...route];
|
||||
|
@ -1,33 +0,0 @@
|
||||
import { mock } from 'mockjs';
|
||||
import type { MockMethod } from 'vite-plugin-mock';
|
||||
|
||||
const apis: MockMethod[] = [
|
||||
{
|
||||
url: '/mock/getAllUserList',
|
||||
method: 'post',
|
||||
response: (): Service.MockServiceResult<ApiUserManagement.User[]> => {
|
||||
const data = mock({
|
||||
'list|1000': [
|
||||
{
|
||||
id: '@id',
|
||||
userName: '@cname',
|
||||
'age|18-56': 56,
|
||||
'gender|1': ['0', '1', null],
|
||||
phone:
|
||||
/^[1](([3][0-9])|([4][01456789])|([5][012356789])|([6][2567])|([7][0-8])|([8][0-9])|([9][012356789]))[0-9]{8}$/,
|
||||
'email|1': ['@email("qq.com")', null],
|
||||
'userStatus|1': ['1', '2', '3', '4', null]
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
return {
|
||||
code: 200,
|
||||
message: 'ok',
|
||||
data: data.list
|
||||
};
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
export default apis;
|
@ -8,7 +8,7 @@ const apis: MockMethod[] = [
|
||||
response: (options: Service.MockOption): Service.MockServiceResult => {
|
||||
const { userId = undefined } = options.body;
|
||||
|
||||
const routeHomeName: AuthRoute.LastDegreeRouteKey = 'dashboard_analysis';
|
||||
const routeHomeName: AuthRoute.LastDegreeRouteKey = 'multi-menu_first_second';
|
||||
|
||||
const role = userModel.find(item => item.userId === userId)?.userRole || 'user';
|
||||
|
||||
|
@ -1,396 +1,5 @@
|
||||
export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
super: [
|
||||
{
|
||||
name: 'dashboard',
|
||||
path: '/dashboard',
|
||||
component: 'basic',
|
||||
children: [
|
||||
{
|
||||
name: 'dashboard_analysis',
|
||||
path: '/dashboard/analysis',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '分析页',
|
||||
requiresAuth: true,
|
||||
icon: 'icon-park-outline:analysis'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'dashboard_workbench',
|
||||
path: '/dashboard/workbench',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '工作台',
|
||||
requiresAuth: true,
|
||||
icon: 'icon-park-outline:workbench'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '仪表盘',
|
||||
icon: 'mdi:monitor-dashboard',
|
||||
order: 1
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'document',
|
||||
path: '/document',
|
||||
component: 'basic',
|
||||
children: [
|
||||
{
|
||||
name: 'document_vue',
|
||||
path: '/document/vue',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'vue文档',
|
||||
requiresAuth: true,
|
||||
icon: 'logos:vue'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'document_vite',
|
||||
path: '/document/vite',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'vite文档',
|
||||
requiresAuth: true,
|
||||
icon: 'logos:vitejs'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'document_naive',
|
||||
path: '/document/naive',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'naive文档',
|
||||
requiresAuth: true,
|
||||
icon: 'logos:naiveui'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'document_project',
|
||||
path: '/document/project',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '项目文档',
|
||||
requiresAuth: true,
|
||||
localIcon: 'logo'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'document_project-link',
|
||||
path: '/document/project-link',
|
||||
meta: {
|
||||
title: '项目文档(外链)',
|
||||
requiresAuth: true,
|
||||
localIcon: 'logo',
|
||||
href: 'https://docs.soybean.pro/'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '文档',
|
||||
icon: 'mdi:file-document-multiple-outline',
|
||||
order: 2
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'component',
|
||||
path: '/component',
|
||||
component: 'basic',
|
||||
children: [
|
||||
{
|
||||
name: 'component_button',
|
||||
path: '/component/button',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '按钮',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:button-cursor'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'component_card',
|
||||
path: '/component/card',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '卡片',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:card-outline'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'component_table',
|
||||
path: '/component/table',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '表格',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:table-large'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '组件示例',
|
||||
icon: 'cib:app-store',
|
||||
order: 3
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'plugin',
|
||||
path: '/plugin',
|
||||
component: 'basic',
|
||||
children: [
|
||||
{
|
||||
name: 'plugin_charts',
|
||||
path: '/plugin/charts',
|
||||
component: 'multi',
|
||||
children: [
|
||||
{
|
||||
name: 'plugin_charts_echarts',
|
||||
path: '/plugin/charts/echarts',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'ECharts',
|
||||
requiresAuth: true,
|
||||
icon: 'simple-icons:apacheecharts'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'plugin_charts_antv',
|
||||
path: '/plugin/charts/antv',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'AntV',
|
||||
requiresAuth: true,
|
||||
icon: 'simple-icons:antdesign'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '图表',
|
||||
icon: 'mdi:chart-areaspline'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'plugin_map',
|
||||
path: '/plugin/map',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '地图',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:map'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'plugin_video',
|
||||
path: '/plugin/video',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '视频',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:video'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'plugin_editor',
|
||||
path: '/plugin/editor',
|
||||
component: 'multi',
|
||||
children: [
|
||||
{
|
||||
name: 'plugin_editor_quill',
|
||||
path: '/plugin/editor/quill',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '富文本编辑器',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:file-document-edit-outline'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'plugin_editor_markdown',
|
||||
path: '/plugin/editor/markdown',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'markdown编辑器',
|
||||
requiresAuth: true,
|
||||
icon: 'ri:markdown-line'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '编辑器',
|
||||
icon: 'icon-park-outline:editor'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'plugin_swiper',
|
||||
path: '/plugin/swiper',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'Swiper插件',
|
||||
requiresAuth: true,
|
||||
icon: 'simple-icons:swiper'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'plugin_copy',
|
||||
path: '/plugin/copy',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '剪贴板',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:clipboard-outline'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'plugin_icon',
|
||||
path: '/plugin/icon',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '图标',
|
||||
requiresAuth: true,
|
||||
localIcon: 'custom-icon'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'plugin_print',
|
||||
path: '/plugin/print',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '打印',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:printer'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '插件示例',
|
||||
icon: 'clarity:plugin-line',
|
||||
order: 4
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'auth-demo',
|
||||
path: '/auth-demo',
|
||||
component: 'basic',
|
||||
children: [
|
||||
{
|
||||
name: 'auth-demo_permission',
|
||||
path: '/auth-demo/permission',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '权限切换',
|
||||
requiresAuth: true,
|
||||
icon: 'ic:round-construction'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'auth-demo_super',
|
||||
path: '/auth-demo/super',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '超级管理员可见',
|
||||
requiresAuth: true,
|
||||
icon: 'ic:round-supervisor-account'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '权限示例',
|
||||
icon: 'ic:baseline-security',
|
||||
order: 5
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'function',
|
||||
path: '/function',
|
||||
component: 'basic',
|
||||
children: [
|
||||
{
|
||||
name: 'function_tab',
|
||||
path: '/function/tab',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'Tab',
|
||||
requiresAuth: true,
|
||||
icon: 'ic:round-tab'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'function_tab-detail',
|
||||
path: '/function/tab-detail',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'Tab Detail',
|
||||
requiresAuth: true,
|
||||
hide: true,
|
||||
activeMenu: 'function_tab',
|
||||
icon: 'ic:round-tab'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'function_tab-multi-detail',
|
||||
path: '/function/tab-multi-detail',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'Tab Multi Detail',
|
||||
requiresAuth: true,
|
||||
hide: true,
|
||||
multiTab: true,
|
||||
activeMenu: 'function_tab',
|
||||
icon: 'ic:round-tab'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '功能',
|
||||
icon: 'icon-park-outline:all-application',
|
||||
order: 6
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'exception',
|
||||
path: '/exception',
|
||||
component: 'basic',
|
||||
children: [
|
||||
{
|
||||
name: 'exception_403',
|
||||
path: '/exception/403',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '异常页403',
|
||||
requiresAuth: true,
|
||||
icon: 'ic:baseline-block'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'exception_404',
|
||||
path: '/exception/404',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '异常页404',
|
||||
requiresAuth: true,
|
||||
icon: 'ic:baseline-web-asset-off'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'exception_500',
|
||||
path: '/exception/500',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '异常页500',
|
||||
requiresAuth: true,
|
||||
icon: 'ic:baseline-wifi-off'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '异常页',
|
||||
icon: 'ant-design:exception-outlined',
|
||||
order: 7
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'multi-menu',
|
||||
path: '/multi-menu',
|
||||
@ -442,456 +51,11 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
meta: {
|
||||
title: '多级菜单',
|
||||
icon: 'carbon:menu',
|
||||
order: 8
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'management',
|
||||
path: '/management',
|
||||
component: 'basic',
|
||||
children: [
|
||||
{
|
||||
name: 'management_auth',
|
||||
path: '/management/auth',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '权限管理',
|
||||
requiresAuth: true,
|
||||
icon: 'ic:baseline-security'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'management_role',
|
||||
path: '/management/role',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '角色管理',
|
||||
requiresAuth: true,
|
||||
icon: 'carbon:user-role'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'management_user',
|
||||
path: '/management/user',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '用户管理',
|
||||
requiresAuth: true,
|
||||
icon: 'ic:round-manage-accounts'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'management_route',
|
||||
path: '/management/route',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '路由管理',
|
||||
requiresAuth: true,
|
||||
icon: 'material-symbols:route'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '系统管理',
|
||||
icon: 'carbon:cloud-service-management',
|
||||
order: 9
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'about',
|
||||
path: '/about',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '关于',
|
||||
requiresAuth: true,
|
||||
singleLayout: 'basic',
|
||||
icon: 'fluent:book-information-24-regular',
|
||||
order: 10
|
||||
order: 1
|
||||
}
|
||||
}
|
||||
],
|
||||
admin: [
|
||||
{
|
||||
name: 'dashboard',
|
||||
path: '/dashboard',
|
||||
component: 'basic',
|
||||
children: [
|
||||
{
|
||||
name: 'dashboard_analysis',
|
||||
path: '/dashboard/analysis',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '分析页',
|
||||
requiresAuth: true,
|
||||
icon: 'icon-park-outline:analysis'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'dashboard_workbench',
|
||||
path: '/dashboard/workbench',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '工作台',
|
||||
requiresAuth: true,
|
||||
icon: 'icon-park-outline:workbench'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '仪表盘',
|
||||
icon: 'mdi:monitor-dashboard',
|
||||
order: 1
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'document',
|
||||
path: '/document',
|
||||
component: 'basic',
|
||||
children: [
|
||||
{
|
||||
name: 'document_vue',
|
||||
path: '/document/vue',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'vue文档',
|
||||
requiresAuth: true,
|
||||
icon: 'logos:vue'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'document_vite',
|
||||
path: '/document/vite',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'vite文档',
|
||||
requiresAuth: true,
|
||||
icon: 'logos:vitejs'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'document_naive',
|
||||
path: '/document/naive',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'naive文档',
|
||||
requiresAuth: true,
|
||||
icon: 'logos:naiveui'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'document_project',
|
||||
path: '/document/project',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '项目文档',
|
||||
requiresAuth: true,
|
||||
localIcon: 'logo'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'document_project-link',
|
||||
path: '/document/project-link',
|
||||
meta: {
|
||||
title: '项目文档(外链)',
|
||||
requiresAuth: true,
|
||||
localIcon: 'logo',
|
||||
href: 'https://docs.soybean.pro/'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '文档',
|
||||
icon: 'mdi:file-document-multiple-outline',
|
||||
order: 2
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'component',
|
||||
path: '/component',
|
||||
component: 'basic',
|
||||
children: [
|
||||
{
|
||||
name: 'component_button',
|
||||
path: '/component/button',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '按钮',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:button-cursor'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'component_card',
|
||||
path: '/component/card',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '卡片',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:card-outline'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'component_table',
|
||||
path: '/component/table',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '表格',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:table-large'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '组件示例',
|
||||
icon: 'cib:app-store',
|
||||
order: 3
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'plugin',
|
||||
path: '/plugin',
|
||||
component: 'basic',
|
||||
children: [
|
||||
{
|
||||
name: 'plugin_charts',
|
||||
path: '/plugin/charts',
|
||||
component: 'multi',
|
||||
children: [
|
||||
{
|
||||
name: 'plugin_charts_echarts',
|
||||
path: '/plugin/charts/echarts',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'ECharts',
|
||||
requiresAuth: true,
|
||||
icon: 'simple-icons:apacheecharts'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'plugin_charts_antv',
|
||||
path: '/plugin/charts/antv',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'AntV',
|
||||
requiresAuth: true,
|
||||
icon: 'simple-icons:antdesign'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '图表',
|
||||
icon: 'mdi:chart-areaspline'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'plugin_map',
|
||||
path: '/plugin/map',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '地图',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:map'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'plugin_video',
|
||||
path: '/plugin/video',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '视频',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:video'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'plugin_editor',
|
||||
path: '/plugin/editor',
|
||||
component: 'multi',
|
||||
children: [
|
||||
{
|
||||
name: 'plugin_editor_quill',
|
||||
path: '/plugin/editor/quill',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '富文本编辑器',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:file-document-edit-outline'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'plugin_editor_markdown',
|
||||
path: '/plugin/editor/markdown',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'markdown编辑器',
|
||||
requiresAuth: true,
|
||||
icon: 'ri:markdown-line'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '编辑器',
|
||||
icon: 'icon-park-outline:editor'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'plugin_swiper',
|
||||
path: '/plugin/swiper',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'Swiper插件',
|
||||
requiresAuth: true,
|
||||
icon: 'simple-icons:swiper'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'plugin_copy',
|
||||
path: '/plugin/copy',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '剪贴板',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:clipboard-outline'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'plugin_icon',
|
||||
path: '/plugin/icon',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '图标',
|
||||
requiresAuth: true,
|
||||
localIcon: 'custom-icon'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'plugin_print',
|
||||
path: '/plugin/print',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '打印',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:printer'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '插件示例',
|
||||
icon: 'clarity:plugin-line',
|
||||
order: 4
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'auth-demo',
|
||||
path: '/auth-demo',
|
||||
component: 'basic',
|
||||
children: [
|
||||
{
|
||||
name: 'auth-demo_permission',
|
||||
path: '/auth-demo/permission',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '权限切换',
|
||||
requiresAuth: true,
|
||||
icon: 'ic:round-construction'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '权限示例',
|
||||
icon: 'ic:baseline-security',
|
||||
order: 5
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'function',
|
||||
path: '/function',
|
||||
component: 'basic',
|
||||
children: [
|
||||
{
|
||||
name: 'function_tab',
|
||||
path: '/function/tab',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'Tab',
|
||||
requiresAuth: true,
|
||||
icon: 'ic:round-tab'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'function_tab-detail',
|
||||
path: '/function/tab-detail',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'Tab Detail',
|
||||
requiresAuth: true,
|
||||
hide: true,
|
||||
activeMenu: 'function_tab',
|
||||
icon: 'ic:round-tab'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'function_tab-multi-detail',
|
||||
path: '/function/tab-multi-detail',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'Tab Multi Detail',
|
||||
requiresAuth: true,
|
||||
hide: true,
|
||||
multiTab: true,
|
||||
activeMenu: 'function_tab',
|
||||
icon: 'ic:round-tab'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '功能',
|
||||
icon: 'icon-park-outline:all-application',
|
||||
order: 6
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'exception',
|
||||
path: '/exception',
|
||||
component: 'basic',
|
||||
children: [
|
||||
{
|
||||
name: 'exception_403',
|
||||
path: '/exception/403',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '异常页403',
|
||||
requiresAuth: true,
|
||||
icon: 'ic:baseline-block'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'exception_404',
|
||||
path: '/exception/404',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '异常页404',
|
||||
requiresAuth: true,
|
||||
icon: 'ic:baseline-web-asset-off'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'exception_500',
|
||||
path: '/exception/500',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '异常页500',
|
||||
requiresAuth: true,
|
||||
icon: 'ic:baseline-wifi-off'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '异常页',
|
||||
icon: 'ant-design:exception-outlined',
|
||||
order: 7
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'multi-menu',
|
||||
path: '/multi-menu',
|
||||
@ -945,117 +109,9 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
icon: 'carbon:menu',
|
||||
order: 8
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'management',
|
||||
path: '/management',
|
||||
component: 'basic',
|
||||
children: [
|
||||
{
|
||||
name: 'management_auth',
|
||||
path: '/management/auth',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '权限管理',
|
||||
requiresAuth: true,
|
||||
icon: 'ic:baseline-security'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'management_role',
|
||||
path: '/management/role',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '角色管理',
|
||||
requiresAuth: true,
|
||||
icon: 'carbon:user-role'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'management_user',
|
||||
path: '/management/user',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '用户管理',
|
||||
requiresAuth: true,
|
||||
icon: 'ic:round-manage-accounts'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'management_route',
|
||||
path: '/management/route',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '路由管理',
|
||||
requiresAuth: true,
|
||||
icon: 'material-symbols:route'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '系统管理',
|
||||
icon: 'carbon:cloud-service-management',
|
||||
order: 9
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'about',
|
||||
path: '/about',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '关于',
|
||||
requiresAuth: true,
|
||||
singleLayout: 'basic',
|
||||
icon: 'fluent:book-information-24-regular',
|
||||
order: 10
|
||||
}
|
||||
}
|
||||
],
|
||||
user: [
|
||||
{
|
||||
name: 'dashboard',
|
||||
path: '/dashboard',
|
||||
component: 'basic',
|
||||
children: [
|
||||
{
|
||||
name: 'dashboard_analysis',
|
||||
path: '/dashboard/analysis',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '分析页',
|
||||
requiresAuth: true,
|
||||
icon: 'icon-park-outline:analysis'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '仪表盘',
|
||||
icon: 'mdi:monitor-dashboard',
|
||||
order: 1
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'auth-demo',
|
||||
path: '/auth-demo',
|
||||
component: 'basic',
|
||||
children: [
|
||||
{
|
||||
name: 'auth-demo_permission',
|
||||
path: '/auth-demo/permission',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '权限切换',
|
||||
requiresAuth: true,
|
||||
icon: 'ic:round-construction'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '权限示例',
|
||||
icon: 'ic:baseline-security',
|
||||
order: 5
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'multi-menu',
|
||||
path: '/multi-menu',
|
||||
@ -1109,18 +165,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
icon: 'carbon:menu',
|
||||
order: 7
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'about',
|
||||
path: '/about',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '关于',
|
||||
requiresAuth: true,
|
||||
singleLayout: 'basic',
|
||||
icon: 'fluent:book-information-24-regular',
|
||||
order: 8
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
41
package.json
41
package.json
@ -41,22 +41,11 @@
|
||||
"build": "npm run typecheck && cross-env VITE_SERVICE_ENV=prod vite build",
|
||||
"build:dev": "npm run typecheck && cross-env VITE_SERVICE_ENV=dev vite build",
|
||||
"build:test": "npm run typecheck && cross-env VITE_SERVICE_ENV=test vite build",
|
||||
"build:vercel": "cross-env VITE_HASH_ROUTE=Y VITE_VERCEL=Y vite build",
|
||||
"preview": "vite preview",
|
||||
"typecheck": "vue-tsc --noEmit --skipLibCheck",
|
||||
"lint": "eslint . --fix",
|
||||
"commit": "czg",
|
||||
"cz": "czg",
|
||||
"esno": "esno",
|
||||
"cleanup": "esno ./scripts/cleanup.ts",
|
||||
"update-pkg": "ncu --deep -u",
|
||||
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s",
|
||||
"release": "standard-version",
|
||||
"prepare": "husky install"
|
||||
"lint": "eslint . --fix"
|
||||
},
|
||||
"dependencies": {
|
||||
"@antv/data-set": "^0.11.8",
|
||||
"@antv/g2": "^4.2.8",
|
||||
"@better-scroll/core": "^2.5.0",
|
||||
"@soybeanjs/vue-admin-layout": "^1.1.1",
|
||||
"@soybeanjs/vue-admin-tab": "^1.0.5",
|
||||
@ -66,29 +55,19 @@
|
||||
"colord": "^2.9.3",
|
||||
"crypto-js": "^4.1.1",
|
||||
"dayjs": "^1.11.6",
|
||||
"echarts": "^5.4.0",
|
||||
"form-data": "^4.0.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
"naive-ui": "2.33.5",
|
||||
"pinia": "^2.0.23",
|
||||
"print-js": "^1.6.0",
|
||||
"qs": "^6.11.0",
|
||||
"swiper": "^8.4.4",
|
||||
"ua-parser-js": "^1.0.32",
|
||||
"vditor": "^3.8.18",
|
||||
"vue": "3.2.45",
|
||||
"vue-i18n": "^9.2.2",
|
||||
"vue-router": "^4.1.6",
|
||||
"vuedraggable": "^4.1.0",
|
||||
"wangeditor": "^4.7.15",
|
||||
"xgplayer": "^2.32.2"
|
||||
"vue-router": "^4.1.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@amap/amap-jsapi-types": "^0.0.10",
|
||||
"@iconify/json": "^2.1.139",
|
||||
"@iconify/vue": "^4.0.0",
|
||||
"@soybeanjs/router-page": "1.0.3",
|
||||
"@types/bmapgl": "^0.0.5",
|
||||
"@types/crypto-js": "^4.1.1",
|
||||
"@types/node": "18.11.9",
|
||||
"@types/qs": "^6.9.7",
|
||||
@ -97,35 +76,21 @@
|
||||
"@unocss/vite": "^0.46.5",
|
||||
"@vitejs/plugin-vue": "^3.2.0",
|
||||
"@vitejs/plugin-vue-jsx": "^2.1.1",
|
||||
"commitlint": "^17.2.0",
|
||||
"conventional-changelog": "^3.1.25",
|
||||
"cross-env": "^7.0.3",
|
||||
"cz-git": "^1.3.12",
|
||||
"czg": "^1.3.12",
|
||||
"eslint": "^8.27.0",
|
||||
"eslint-config-soybeanjs-vue": "^0.1.2",
|
||||
"esno": "^0.16.3",
|
||||
"husky": "^8.0.2",
|
||||
"mockjs": "^1.1.0",
|
||||
"npm-check-updates": "^16.4.1",
|
||||
"rimraf": "^3.0.2",
|
||||
"rollup-plugin-visualizer": "^5.8.3",
|
||||
"sass": "^1.56.1",
|
||||
"standard-version": "^9.5.0",
|
||||
"typescript": "4.8.4",
|
||||
"unplugin-icons": "^0.14.13",
|
||||
"unplugin-vue-components": "0.22.9",
|
||||
"unplugin-vue-macros": "^0.16.3",
|
||||
"utility-types": "^3.10.0",
|
||||
"vite": "^3.2.4",
|
||||
"vite-plugin-compression": "^0.5.1",
|
||||
"vite-plugin-html": "^3.2.0",
|
||||
"vite-plugin-mock": "^2.9.6",
|
||||
"vite-plugin-progress": "^0.0.6",
|
||||
"vite-plugin-pwa": "^0.13.3",
|
||||
"vite-plugin-svg-icons": "^2.0.1",
|
||||
"vue-tsc": "^1.0.9",
|
||||
"zx": "^7.1.1"
|
||||
"vue-tsc": "^1.0.9"
|
||||
},
|
||||
"pnpm": {
|
||||
"patchedDependencies": {
|
||||
|
5285
pnpm-lock.yaml
5285
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
@ -1,4 +0,0 @@
|
||||
#!/usr/bin/env zx
|
||||
import { $ } from 'zx';
|
||||
|
||||
$`pnpm rimraf node_modules dist pnpm-lock.yaml`;
|
@ -1,116 +0,0 @@
|
||||
<template>
|
||||
<span>{{ value }}</span>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { computed, onMounted, ref, watch, watchEffect } from 'vue';
|
||||
import { TransitionPresets, useTransition } from '@vueuse/core';
|
||||
import { isNumber } from '@/utils';
|
||||
|
||||
defineOptions({ name: 'CountTo' });
|
||||
|
||||
type TansitionKey = keyof typeof TransitionPresets;
|
||||
|
||||
interface Props {
|
||||
/** 初始值 */
|
||||
startValue?: number;
|
||||
/** 结束值 */
|
||||
endValue?: number;
|
||||
/** 动画时长 */
|
||||
duration?: number;
|
||||
/** 自动动画 */
|
||||
autoplay?: boolean;
|
||||
/** 进制 */
|
||||
decimals?: number;
|
||||
/** 前缀 */
|
||||
prefix?: string;
|
||||
/** 后缀 */
|
||||
suffix?: string;
|
||||
/** 分割符号 */
|
||||
separator?: string;
|
||||
/** 小数点 */
|
||||
decimal?: string;
|
||||
/** 使用缓冲动画函数 */
|
||||
useEasing?: boolean;
|
||||
/** 缓冲动画函数类型 */
|
||||
transition?: TansitionKey;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
startValue: 0,
|
||||
endValue: 2021,
|
||||
duration: 1500,
|
||||
autoplay: true,
|
||||
decimals: 0,
|
||||
prefix: '',
|
||||
suffix: '',
|
||||
separator: ',',
|
||||
decimal: '.',
|
||||
useEasing: true,
|
||||
transition: 'linear'
|
||||
});
|
||||
|
||||
interface Emits {
|
||||
(e: 'on-started'): void;
|
||||
(e: 'on-finished'): void;
|
||||
}
|
||||
|
||||
const emit = defineEmits<Emits>();
|
||||
|
||||
const source = ref(props.startValue);
|
||||
let outputValue = useTransition(source);
|
||||
const value = computed(() => formatNumber(outputValue.value));
|
||||
const disabled = ref(false);
|
||||
|
||||
function run() {
|
||||
outputValue = useTransition(source, {
|
||||
disabled,
|
||||
duration: props.duration,
|
||||
onStarted: () => emit('on-started'),
|
||||
onFinished: () => emit('on-finished'),
|
||||
...(props.useEasing ? { transition: TransitionPresets[props.transition] } : {})
|
||||
});
|
||||
}
|
||||
|
||||
function start() {
|
||||
run();
|
||||
source.value = props.endValue;
|
||||
}
|
||||
|
||||
function formatNumber(num: number | string) {
|
||||
if (num !== 0 && !num) {
|
||||
return '';
|
||||
}
|
||||
const { decimals, decimal, separator, suffix, prefix } = props;
|
||||
let number = Number(num).toFixed(decimals);
|
||||
number = String(number);
|
||||
|
||||
const x = number.split('.');
|
||||
let x1 = x[0];
|
||||
const x2 = x.length > 1 ? decimal + x[1] : '';
|
||||
const rgx = /(\d+)(\d{3})/;
|
||||
if (separator && !isNumber(separator)) {
|
||||
while (rgx.test(x1)) {
|
||||
x1 = x1.replace(rgx, `$1${separator}$2`);
|
||||
}
|
||||
}
|
||||
return prefix + x1 + x2 + suffix;
|
||||
}
|
||||
|
||||
watch([() => props.startValue, () => props.endValue], () => {
|
||||
if (props.autoplay) {
|
||||
start();
|
||||
}
|
||||
});
|
||||
|
||||
watchEffect(() => {
|
||||
source.value = props.startValue;
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
if (props.autoplay) {
|
||||
start();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
@ -1,17 +0,0 @@
|
||||
<template>
|
||||
<web-site-link label="github地址:" :link="link" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import WebSiteLink from './WebSiteLink.vue';
|
||||
|
||||
defineOptions({ name: 'GithubLink' });
|
||||
|
||||
interface Props {
|
||||
/** github链接 */
|
||||
link: string;
|
||||
}
|
||||
|
||||
defineProps<Props>();
|
||||
</script>
|
||||
<style scoped></style>
|
@ -1,78 +0,0 @@
|
||||
<template>
|
||||
<n-popover placement="bottom-end" trigger="click">
|
||||
<template #trigger>
|
||||
<n-input v-model:value="modelValue" readonly placeholder="点击选择图标">
|
||||
<template #suffix>
|
||||
<svg-icon :icon="selectedIcon" class="text-30px p-5px" />
|
||||
</template>
|
||||
</n-input>
|
||||
</template>
|
||||
<template #header>
|
||||
<n-input v-model:value="searchValue" placeholder="搜索图标"></n-input>
|
||||
</template>
|
||||
<div v-if="iconsList.length > 0" class="grid grid-cols-9 h-auto overflow-auto">
|
||||
<template v-for="iconItem in iconsList" :key="iconItem">
|
||||
<svg-icon
|
||||
:icon="iconItem"
|
||||
class="border-1px border-[#d9d9d9] text-30px m-2px p-5px"
|
||||
:class="{ 'border-primary': modelValue === iconItem }"
|
||||
@click="handleChange(iconItem)"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
<n-empty v-else class="w-306px" description="你什么也找不到" />
|
||||
</n-popover>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
defineOptions({ name: 'IconSelect' });
|
||||
|
||||
interface Props {
|
||||
/** 选中的图标 */
|
||||
value: string;
|
||||
/** 图标列表 */
|
||||
icons: string[];
|
||||
/** 未选中图标 */
|
||||
emptyIcon?: string;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
emptyIcon: 'mdi:apps'
|
||||
});
|
||||
|
||||
interface Emits {
|
||||
(e: 'update:value', val: string): void;
|
||||
}
|
||||
|
||||
const emit = defineEmits<Emits>();
|
||||
|
||||
const modelValue = computed({
|
||||
get() {
|
||||
return props.value;
|
||||
},
|
||||
set(val: string) {
|
||||
emit('update:value', val);
|
||||
}
|
||||
});
|
||||
|
||||
const selectedIcon = computed(() => modelValue.value || props.emptyIcon);
|
||||
|
||||
const searchValue = ref('');
|
||||
|
||||
const iconsList = computed(() => props.icons.filter(v => v.includes(searchValue.value)));
|
||||
|
||||
function handleChange(iconItem: string) {
|
||||
modelValue.value = iconItem;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.n-input-wrapper) {
|
||||
padding-right: 0;
|
||||
}
|
||||
:deep(.n-input__suffix) {
|
||||
border: 1px solid #d9d9d9;
|
||||
}
|
||||
</style>
|
@ -1,42 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<canvas ref="domRef" width="152" height="40" class="cursor-pointer" @click="getImgCode"></canvas>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { watch } from 'vue';
|
||||
import { useImageVerify } from '@/hooks';
|
||||
|
||||
defineOptions({ name: 'ImageVerify' });
|
||||
|
||||
interface Props {
|
||||
code?: string;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
code: ''
|
||||
});
|
||||
|
||||
interface Emits {
|
||||
(e: 'update:code', code: string): void;
|
||||
}
|
||||
|
||||
const emit = defineEmits<Emits>();
|
||||
|
||||
const { domRef, imgCode, setImgCode, getImgCode } = useImageVerify();
|
||||
|
||||
watch(
|
||||
() => props.code,
|
||||
newValue => {
|
||||
setImgCode(newValue);
|
||||
}
|
||||
);
|
||||
watch(imgCode, newValue => {
|
||||
emit('update:code', newValue);
|
||||
});
|
||||
|
||||
defineExpose({ getImgCode });
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
@ -1,23 +0,0 @@
|
||||
<template>
|
||||
<p>
|
||||
<span>{{ label }}</span>
|
||||
<a class="text-blue-500" :href="link" target="_blank">
|
||||
{{ link }}
|
||||
</a>
|
||||
</p>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
defineOptions({ name: 'WebSiteLink' });
|
||||
|
||||
interface Props {
|
||||
/** 网址名称 */
|
||||
label: string;
|
||||
/** 网址链接 */
|
||||
link: string;
|
||||
}
|
||||
|
||||
defineProps<Props>();
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
@ -1,167 +0,0 @@
|
||||
import { nextTick, onUnmounted, ref, watch } from 'vue';
|
||||
import type { ComputedRef, Ref } from 'vue';
|
||||
import * as echarts from 'echarts/core';
|
||||
import { BarChart, GaugeChart, LineChart, PictorialBarChart, PieChart, RadarChart, ScatterChart } from 'echarts/charts';
|
||||
import type {
|
||||
BarSeriesOption,
|
||||
GaugeSeriesOption,
|
||||
LineSeriesOption,
|
||||
PictorialBarSeriesOption,
|
||||
PieSeriesOption,
|
||||
RadarSeriesOption,
|
||||
ScatterSeriesOption
|
||||
} from 'echarts/charts';
|
||||
import {
|
||||
DatasetComponent,
|
||||
GridComponent,
|
||||
LegendComponent,
|
||||
TitleComponent,
|
||||
ToolboxComponent,
|
||||
TooltipComponent,
|
||||
TransformComponent
|
||||
} from 'echarts/components';
|
||||
import type {
|
||||
DatasetComponentOption,
|
||||
GridComponentOption,
|
||||
LegendComponentOption,
|
||||
TitleComponentOption,
|
||||
ToolboxComponentOption,
|
||||
TooltipComponentOption
|
||||
} from 'echarts/components';
|
||||
import { LabelLayout, UniversalTransition } from 'echarts/features';
|
||||
import { CanvasRenderer } from 'echarts/renderers';
|
||||
import { useElementSize } from '@vueuse/core';
|
||||
import { useThemeStore } from '@/store';
|
||||
|
||||
export type ECOption = echarts.ComposeOption<
|
||||
| BarSeriesOption
|
||||
| LineSeriesOption
|
||||
| PieSeriesOption
|
||||
| ScatterSeriesOption
|
||||
| PictorialBarSeriesOption
|
||||
| RadarSeriesOption
|
||||
| GaugeSeriesOption
|
||||
| TitleComponentOption
|
||||
| LegendComponentOption
|
||||
| TooltipComponentOption
|
||||
| GridComponentOption
|
||||
| ToolboxComponentOption
|
||||
| DatasetComponentOption
|
||||
>;
|
||||
|
||||
echarts.use([
|
||||
TitleComponent,
|
||||
LegendComponent,
|
||||
TooltipComponent,
|
||||
GridComponent,
|
||||
DatasetComponent,
|
||||
TransformComponent,
|
||||
ToolboxComponent,
|
||||
BarChart,
|
||||
LineChart,
|
||||
PieChart,
|
||||
ScatterChart,
|
||||
PictorialBarChart,
|
||||
RadarChart,
|
||||
GaugeChart,
|
||||
LabelLayout,
|
||||
UniversalTransition,
|
||||
CanvasRenderer
|
||||
]);
|
||||
|
||||
/**
|
||||
* Echarts hooks函数
|
||||
* @param options - 图表配置
|
||||
* @param renderFun - 图表渲染函数(例如:图表监听函数)
|
||||
* @description 按需引入图表组件,没注册的组件需要先引入
|
||||
*/
|
||||
export function useEcharts(
|
||||
options: Ref<ECOption> | ComputedRef<ECOption>,
|
||||
renderFun?: (chartInstance: echarts.ECharts) => void
|
||||
) {
|
||||
const theme = useThemeStore();
|
||||
|
||||
const domRef = ref<HTMLElement>();
|
||||
|
||||
const initialSize = { width: 0, height: 0 };
|
||||
const { width, height } = useElementSize(domRef, initialSize);
|
||||
|
||||
let chart: echarts.ECharts | null = null;
|
||||
|
||||
function canRender() {
|
||||
return initialSize.width > 0 && initialSize.height > 0;
|
||||
}
|
||||
|
||||
function isRendered() {
|
||||
return Boolean(domRef.value && chart);
|
||||
}
|
||||
|
||||
function update(updateOptions: ECOption) {
|
||||
if (isRendered()) {
|
||||
chart!.setOption({ ...updateOptions, backgroundColor: 'transparent' });
|
||||
}
|
||||
}
|
||||
|
||||
async function render() {
|
||||
if (domRef.value) {
|
||||
const chartTheme = theme.darkMode ? 'dark' : 'light';
|
||||
await nextTick();
|
||||
chart = echarts.init(domRef.value, chartTheme);
|
||||
if (renderFun) {
|
||||
renderFun(chart);
|
||||
}
|
||||
update(options.value);
|
||||
}
|
||||
}
|
||||
|
||||
function resize() {
|
||||
chart?.resize();
|
||||
}
|
||||
|
||||
function destroy() {
|
||||
chart?.dispose();
|
||||
}
|
||||
|
||||
function updateTheme() {
|
||||
destroy();
|
||||
render();
|
||||
}
|
||||
|
||||
const stopSizeWatch = watch([width, height], ([newWidth, newHeight]) => {
|
||||
initialSize.width = newWidth;
|
||||
initialSize.height = newHeight;
|
||||
if (newWidth === 0 && newHeight === 0) {
|
||||
// 节点被删除 将chart置为空
|
||||
chart = null;
|
||||
}
|
||||
if (canRender()) {
|
||||
if (!isRendered()) {
|
||||
render();
|
||||
} else {
|
||||
resize();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const stopOptionWatch = watch(options, newValue => {
|
||||
update(newValue);
|
||||
});
|
||||
|
||||
const stopDarkModeWatch = watch(
|
||||
() => theme.darkMode,
|
||||
() => {
|
||||
updateTheme();
|
||||
}
|
||||
);
|
||||
|
||||
onUnmounted(() => {
|
||||
destroy();
|
||||
stopSizeWatch();
|
||||
stopOptionWatch();
|
||||
stopDarkModeWatch();
|
||||
});
|
||||
|
||||
return {
|
||||
domRef
|
||||
};
|
||||
}
|
@ -2,5 +2,4 @@ export * from './system';
|
||||
export * from './router';
|
||||
export * from './layout';
|
||||
export * from './events';
|
||||
export * from './echarts';
|
||||
export * from './icon';
|
||||
|
@ -1,3 +1,2 @@
|
||||
export * from './service';
|
||||
export * from './regexp';
|
||||
export * from './map-sdk';
|
||||
|
@ -1,8 +0,0 @@
|
||||
/** 百度地图sdk地址 */
|
||||
export const BAIDU_MAP_SDK_URL = `https://api.map.baidu.com/getscript?v=3.0&ak=KSezYymXPth1DIGILRX3oYN9PxbOQQmU&services=&t=20210201100830&s=1`;
|
||||
|
||||
/** 高德地图sdk地址 */
|
||||
export const GAODE_MAP_SDK_URL = 'https://webapi.amap.com/maps?v=2.0&key=e7bd02bd504062087e6563daf4d6721d';
|
||||
|
||||
/** 腾讯地图sdk地址 */
|
||||
export const TENCENT_MAP_SDK_URL = 'https://map.qq.com/api/gljs?v=1.exp&key=A6DBZ-KXPLW-JKSRY-ONZF4-CPHY3-K6BL7';
|
@ -1,25 +0,0 @@
|
||||
/** 用户性别 */
|
||||
export const genderLabels: Record<UserManagement.GenderKey, string> = {
|
||||
0: '女',
|
||||
1: '男'
|
||||
};
|
||||
|
||||
export const genderOptions: { value: UserManagement.GenderKey; label: string }[] = [
|
||||
{ value: '0', label: genderLabels['0'] },
|
||||
{ value: '1', label: genderLabels['1'] }
|
||||
];
|
||||
|
||||
/** 用户状态 */
|
||||
export const userStatusLabels: Record<UserManagement.UserStatusKey, string> = {
|
||||
1: '启用',
|
||||
2: '禁用',
|
||||
3: '冻结',
|
||||
4: '软删除'
|
||||
};
|
||||
|
||||
export const userStatusOptions: { value: UserManagement.UserStatusKey; label: string }[] = [
|
||||
{ value: '1', label: userStatusLabels['1'] },
|
||||
{ value: '2', label: userStatusLabels['2'] },
|
||||
{ value: '3', label: userStatusLabels['3'] },
|
||||
{ value: '4', label: userStatusLabels['4'] }
|
||||
];
|
@ -1 +0,0 @@
|
||||
export * from './business';
|
@ -1,5 +1,4 @@
|
||||
import useCountDown from './useCountDown';
|
||||
import useSmsCode from './useSmsCode';
|
||||
import useImageVerify from './useImageVerify';
|
||||
|
||||
export { useCountDown, useSmsCode, useImageVerify };
|
||||
export { useCountDown, useSmsCode };
|
||||
|
@ -1,87 +0,0 @@
|
||||
import { onMounted, ref } from 'vue';
|
||||
|
||||
/**
|
||||
* 绘制图形验证码
|
||||
* @param width - 图形宽度
|
||||
* @param height - 图形高度
|
||||
*/
|
||||
export default function useImageVerify(width = 152, height = 40) {
|
||||
const domRef = ref<HTMLCanvasElement>();
|
||||
const imgCode = ref('');
|
||||
|
||||
function setImgCode(code: string) {
|
||||
imgCode.value = code;
|
||||
}
|
||||
|
||||
function getImgCode() {
|
||||
if (!domRef.value) return;
|
||||
imgCode.value = draw(domRef.value, width, height);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getImgCode();
|
||||
});
|
||||
|
||||
return {
|
||||
domRef,
|
||||
imgCode,
|
||||
setImgCode,
|
||||
getImgCode
|
||||
};
|
||||
}
|
||||
|
||||
function randomNum(min: number, max: number) {
|
||||
const num = Math.floor(Math.random() * (max - min) + min);
|
||||
return num;
|
||||
}
|
||||
|
||||
function randomColor(min: number, max: number) {
|
||||
const r = randomNum(min, max);
|
||||
const g = randomNum(min, max);
|
||||
const b = randomNum(min, max);
|
||||
return `rgb(${r},${g},${b})`;
|
||||
}
|
||||
|
||||
function draw(dom: HTMLCanvasElement, width: number, height: number) {
|
||||
let imgCode = '';
|
||||
|
||||
const NUMBER_STRING = '0123456789';
|
||||
|
||||
const ctx = dom.getContext('2d');
|
||||
if (!ctx) return imgCode;
|
||||
|
||||
ctx.fillStyle = randomColor(180, 230);
|
||||
ctx.fillRect(0, 0, width, height);
|
||||
|
||||
for (let i = 0; i < 4; i += 1) {
|
||||
const text = NUMBER_STRING[randomNum(0, NUMBER_STRING.length)];
|
||||
imgCode += text;
|
||||
const fontSize = randomNum(18, 41);
|
||||
const deg = randomNum(-30, 30);
|
||||
ctx.font = `${fontSize}px Simhei`;
|
||||
ctx.textBaseline = 'top';
|
||||
ctx.fillStyle = randomColor(80, 150);
|
||||
ctx.save();
|
||||
ctx.translate(30 * i + 23, 15);
|
||||
ctx.rotate((deg * Math.PI) / 180);
|
||||
ctx.fillText(text, -15 + 5, -15);
|
||||
ctx.restore();
|
||||
}
|
||||
for (let i = 0; i < 5; i += 1) {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(randomNum(0, width), randomNum(0, height));
|
||||
ctx.lineTo(randomNum(0, width), randomNum(0, height));
|
||||
ctx.strokeStyle = randomColor(180, 230);
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
}
|
||||
for (let i = 0; i < 41; i += 1) {
|
||||
ctx.beginPath();
|
||||
ctx.arc(randomNum(0, width), randomNum(0, height), 1, 0, 2 * Math.PI);
|
||||
ctx.closePath();
|
||||
ctx.fillStyle = randomColor(150, 200);
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
return imgCode;
|
||||
}
|
@ -1 +0,0 @@
|
||||
export function useTable() {}
|
@ -1,23 +0,0 @@
|
||||
<template>
|
||||
<hover-container
|
||||
tooltip-content="github"
|
||||
class="w-40px h-full"
|
||||
:inverted="theme.header.inverted"
|
||||
@click="handleClickLink"
|
||||
>
|
||||
<icon-mdi-github class="text-20px" />
|
||||
</hover-container>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useThemeStore } from '@/store';
|
||||
|
||||
defineOptions({ name: 'GithubSite' });
|
||||
|
||||
const theme = useThemeStore();
|
||||
function handleClickLink() {
|
||||
window.open('https://github.com/honghuangdc/soybean-admin', '_blank');
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
@ -1,57 +0,0 @@
|
||||
<template>
|
||||
<n-scrollbar class="max-h-360px">
|
||||
<n-list>
|
||||
<n-list-item
|
||||
v-for="(item, index) in list"
|
||||
:key="item.id"
|
||||
class="hover:bg-[#f6f6f6] dark:hover:bg-dark cursor-pointer"
|
||||
@click="handleRead(index)"
|
||||
>
|
||||
<n-thing class="px-15px" :class="{ 'opacity-30': item.isRead }">
|
||||
<template #avatar>
|
||||
<n-avatar v-if="item.avatar" :src="item.avatar" />
|
||||
<svg-icon v-else class="text-34px text-primary" :icon="item.icon" :local-icon="item.svgIcon" />
|
||||
</template>
|
||||
<template #header>
|
||||
<n-ellipsis :line-clamp="1">
|
||||
{{ item.title }}
|
||||
<template #tooltip>
|
||||
{{ item.title }}
|
||||
</template>
|
||||
</n-ellipsis>
|
||||
</template>
|
||||
<template v-if="item.tagTitle" #header-extra>
|
||||
<n-tag v-bind="item.tagProps" size="small">{{ item.tagTitle }}</n-tag>
|
||||
</template>
|
||||
<template #description>
|
||||
<n-ellipsis v-if="item.description" :line-clamp="2">
|
||||
{{ item.description }}
|
||||
</n-ellipsis>
|
||||
<p>{{ item.date }}</p>
|
||||
</template>
|
||||
</n-thing>
|
||||
</n-list-item>
|
||||
</n-list>
|
||||
</n-scrollbar>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
defineOptions({ name: 'MessageList' });
|
||||
|
||||
interface Props {
|
||||
list?: App.MessageList[];
|
||||
}
|
||||
|
||||
withDefaults(defineProps<Props>(), {
|
||||
list: () => []
|
||||
});
|
||||
|
||||
interface Emits {
|
||||
(e: 'read', val: number): void;
|
||||
}
|
||||
|
||||
const emit = defineEmits<Emits>();
|
||||
|
||||
function handleRead(index: number) {
|
||||
emit('read', index);
|
||||
}
|
||||
</script>
|
@ -1,217 +0,0 @@
|
||||
<template>
|
||||
<n-popover class="!p-0" trigger="click" placement="bottom">
|
||||
<template #trigger>
|
||||
<hover-container tooltip-content="消息通知" :inverted="theme.header.inverted" class="relative w-40px h-full">
|
||||
<icon-clarity:notification-line class="text-18px" />
|
||||
<n-badge
|
||||
:value="count"
|
||||
:max="99"
|
||||
:class="[count < 10 ? '-right-2px' : '-right-10px']"
|
||||
class="absolute top-10px"
|
||||
/>
|
||||
</hover-container>
|
||||
</template>
|
||||
<n-tabs
|
||||
v-model:value="currentTab"
|
||||
:class="[isMobile ? 'w-276px' : 'w-360px']"
|
||||
type="line"
|
||||
justify-content="space-evenly"
|
||||
>
|
||||
<n-tab-pane v-for="(item, index) in tabData" :key="item.key" :name="index">
|
||||
<template #tab>
|
||||
<div class="flex-x-center items-center" :class="[isMobile ? 'w-92px' : 'w-120px']">
|
||||
<span class="mr-5px">{{ item.name }}</span>
|
||||
<n-badge
|
||||
v-bind="item.badgeProps"
|
||||
:value="item.list.filter(message => !message.isRead).length"
|
||||
:max="99"
|
||||
show-zero
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<loading-empty-wrapper
|
||||
class="h-360px"
|
||||
:loading="loading"
|
||||
:empty="item.list.length === 0"
|
||||
placeholder-class="bg-$n-color transition-background-color duration-300 ease-in-out"
|
||||
>
|
||||
<message-list :list="item.list" @read="handleRead" />
|
||||
</loading-empty-wrapper>
|
||||
</n-tab-pane>
|
||||
</n-tabs>
|
||||
<div v-if="showAction" class="flex border-t border-$n-divider-color cursor-pointer">
|
||||
<div class="flex-1 text-center py-10px" @click="handleClear">清空</div>
|
||||
<div class="flex-1 text-center py-10px border-l border-$n-divider-color" @click="handleAllRead">全部已读</div>
|
||||
<div class="flex-1 text-center py-10px border-l border-$n-divider-color" @click="handleLoadMore">查看更多</div>
|
||||
</div>
|
||||
</n-popover>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref } from 'vue';
|
||||
import { useThemeStore } from '@/store';
|
||||
import { useBasicLayout } from '@/composables';
|
||||
import { useBoolean } from '@/hooks';
|
||||
import MessageList from './MessageList.vue';
|
||||
|
||||
defineOptions({ name: 'SystemMessage' });
|
||||
|
||||
const theme = useThemeStore();
|
||||
const { isMobile } = useBasicLayout();
|
||||
const { bool: loading, setBool: setLoading } = useBoolean();
|
||||
|
||||
const currentTab = ref(0);
|
||||
|
||||
const tabData = ref<App.MessageTab[]>([
|
||||
{
|
||||
key: 1,
|
||||
name: '通知',
|
||||
badgeProps: { type: 'warning' },
|
||||
list: [
|
||||
{ id: 1, icon: 'ri:message-3-line', title: '你收到了5条新消息', date: '2022-06-17' },
|
||||
{ id: 4, icon: 'ri:message-3-line', title: 'Soybean Admin 1.0.0 版本正在筹备中', date: '2022-06-17' },
|
||||
{ id: 2, icon: 'ri:message-3-line', title: 'Soybean Admin 0.9.6 版本发布了', date: '2022-06-16' },
|
||||
{ id: 3, icon: 'ri:message-3-line', title: 'Soybean Admin 0.9.5 版本发布了', date: '2022-06-07' },
|
||||
{
|
||||
id: 5,
|
||||
icon: 'ri:message-3-line',
|
||||
title: '测试超长标题测试超长标题测试超长标题测试超长标题测试超长标题测试超长标题测试超长标题测试超长标题',
|
||||
date: '2022-06-17'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
key: 2,
|
||||
name: '消息',
|
||||
badgeProps: { type: 'error' },
|
||||
list: [
|
||||
{
|
||||
id: 1,
|
||||
title: '项目动态',
|
||||
svgIcon: 'avatar',
|
||||
description: 'Soybean 刚才把工作台页面随便写了一些,凑合能看了!',
|
||||
date: '2021-11-07 22:45:32'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: '项目动态',
|
||||
svgIcon: 'avatar',
|
||||
description: 'Soybean 正在忙于为soybean-admin写项目说明文档!',
|
||||
date: '2021-11-03 20:33:31'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: '项目动态',
|
||||
svgIcon: 'avatar',
|
||||
description: 'Soybean 准备为soybean-admin 1.0的发布做充分的准备工作!',
|
||||
date: '2021-10-31 22:43:12'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: '项目动态',
|
||||
svgIcon: 'avatar',
|
||||
description: '@yanbowe 向soybean-admin提交了一个bug,多标签栏不会自适应。',
|
||||
date: '2021-10-27 10:24:54'
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
title: '项目动态',
|
||||
svgIcon: 'avatar',
|
||||
description: 'Soybean 在2021年5月28日创建了开源项目soybean-admin!',
|
||||
date: '2021-05-28 22:22:22'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
key: 3,
|
||||
name: '待办',
|
||||
badgeProps: { type: 'info' },
|
||||
list: [
|
||||
{
|
||||
id: 1,
|
||||
icon: 'ri:calendar-todo-line',
|
||||
title: '缓存主题配置',
|
||||
description: '任务正在计划中',
|
||||
date: '2022-06-17',
|
||||
tagTitle: '未开始',
|
||||
tagProps: { type: 'default' }
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
icon: 'ri:calendar-todo-line',
|
||||
title: '添加锁屏组件、全局Iframe组件',
|
||||
description: '任务正在计划中',
|
||||
date: '2022-06-17',
|
||||
tagTitle: '未开始',
|
||||
tagProps: { type: 'default' }
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
icon: 'ri:calendar-todo-line',
|
||||
title: '示例页面完善',
|
||||
description: '任务正在计划中',
|
||||
date: '2022-06-17',
|
||||
tagTitle: '未开始',
|
||||
tagProps: { type: 'default' }
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
icon: 'ri:calendar-todo-line',
|
||||
title: '表单、表格示例',
|
||||
description: '任务正在计划中',
|
||||
date: '2022-06-17',
|
||||
tagTitle: '未开始',
|
||||
tagProps: { type: 'default' }
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
icon: 'ri:calendar-todo-line',
|
||||
title: '性能优化(优化递归函数)',
|
||||
description: '任务正在计划中',
|
||||
date: '2022-06-17',
|
||||
tagTitle: '未开始',
|
||||
tagProps: { type: 'default' }
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
icon: 'ri:calendar-todo-line',
|
||||
title: '精简版(新分支thin)',
|
||||
description: '任务正在计划中',
|
||||
date: '2022-06-17',
|
||||
tagTitle: '未开始',
|
||||
tagProps: { type: 'default' }
|
||||
}
|
||||
]
|
||||
}
|
||||
]);
|
||||
|
||||
const count = computed(() => {
|
||||
return tabData.value.reduce((acc, cur) => {
|
||||
return acc + cur.list.filter(item => !item.isRead).length;
|
||||
}, 0);
|
||||
});
|
||||
|
||||
const showAction = computed(() => tabData.value[currentTab.value].list.length > 0);
|
||||
|
||||
function handleRead(index: number) {
|
||||
tabData.value[currentTab.value].list[index].isRead = true;
|
||||
}
|
||||
|
||||
function handleAllRead() {
|
||||
tabData.value[currentTab.value].list.forEach(item => Object.assign(item, { isRead: true }));
|
||||
}
|
||||
|
||||
function handleClear() {
|
||||
tabData.value[currentTab.value].list = [];
|
||||
}
|
||||
|
||||
function handleLoadMore() {
|
||||
const { list } = tabData.value[currentTab.value];
|
||||
setLoading(true);
|
||||
setTimeout(() => {
|
||||
list.push(...tabData.value[currentTab.value].list);
|
||||
setLoading(false);
|
||||
}, 1000);
|
||||
}
|
||||
</script>
|
||||
<style scoped></style>
|
@ -1,21 +1,9 @@
|
||||
import MenuCollapse from './MenuCollapse.vue';
|
||||
import GlobalBreadcrumb from './GlobalBreadcrumb.vue';
|
||||
import HeaderMenu from './HeaderMenu.vue';
|
||||
import GithubSite from './GithubSite.vue';
|
||||
import FullScreen from './FullScreen.vue';
|
||||
import ThemeMode from './ThemeMode.vue';
|
||||
import UserAvatar from './UserAvatar.vue';
|
||||
import SystemMessage from './SystemMessage.vue';
|
||||
import SettingButton from './SettingButton.vue';
|
||||
|
||||
export {
|
||||
MenuCollapse,
|
||||
GlobalBreadcrumb,
|
||||
HeaderMenu,
|
||||
GithubSite,
|
||||
FullScreen,
|
||||
ThemeMode,
|
||||
UserAvatar,
|
||||
SystemMessage,
|
||||
SettingButton
|
||||
};
|
||||
export { MenuCollapse, GlobalBreadcrumb, HeaderMenu, FullScreen, ThemeMode, UserAvatar, SettingButton };
|
||||
|
@ -7,11 +7,8 @@
|
||||
</div>
|
||||
<header-menu v-else />
|
||||
<div class="flex justify-end h-full">
|
||||
<global-search />
|
||||
<github-site />
|
||||
<full-screen />
|
||||
<theme-mode />
|
||||
<system-message />
|
||||
<setting-button v-if="showButton" />
|
||||
<user-avatar />
|
||||
</div>
|
||||
@ -22,15 +19,12 @@
|
||||
import { useThemeStore } from '@/store';
|
||||
import { useBasicLayout } from '@/composables';
|
||||
import GlobalLogo from '../GlobalLogo/index.vue';
|
||||
import GlobalSearch from '../GlobalSearch/index.vue';
|
||||
import {
|
||||
FullScreen,
|
||||
GithubSite,
|
||||
GlobalBreadcrumb,
|
||||
HeaderMenu,
|
||||
MenuCollapse,
|
||||
SettingButton,
|
||||
SystemMessage,
|
||||
ThemeMode,
|
||||
UserAvatar
|
||||
} from './components';
|
||||
|
@ -4,16 +4,15 @@
|
||||
<h2
|
||||
v-show="showTitle"
|
||||
class="pl-8px text-16px font-bold text-primary transition duration-300 ease-in-out"
|
||||
@click="toggleLocal"
|
||||
>
|
||||
{{ t('message.system.title') }}
|
||||
{{ title }}
|
||||
</h2>
|
||||
</router-link>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { routePath } from '@/router';
|
||||
import { t, setLocale } from '@/locales';
|
||||
import { useAppInfo } from '@/composables'
|
||||
|
||||
defineOptions({ name: 'GlobalLogo' });
|
||||
|
||||
@ -26,11 +25,8 @@ defineProps<Props>();
|
||||
|
||||
const routeHomePath = routePath('root');
|
||||
|
||||
let flag = true;
|
||||
function toggleLocal() {
|
||||
flag = !flag;
|
||||
setLocale(flag ? 'en' : 'zh-CN');
|
||||
}
|
||||
const { title } = useAppInfo()
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
|
@ -1,27 +0,0 @@
|
||||
<template>
|
||||
<div class="px-24px h-44px flex-y-center">
|
||||
<span class="mr-14px flex-y-center">
|
||||
<icon-mdi-keyboard-return class="icon text-20px p-2px mr-6px" />
|
||||
<span>确认</span>
|
||||
</span>
|
||||
<span class="mr-14px flex-y-center">
|
||||
<icon-mdi-arrow-up-thin class="icon text-20px p-2px mr-5px" />
|
||||
<icon-mdi-arrow-down-thin class="icon text-20px p-2px mr-6px" />
|
||||
<span>切换</span>
|
||||
</span>
|
||||
<span class="flex-y-center">
|
||||
<icon-mdi-keyboard-esc class="icon text-20px p-2px mr-6px" />
|
||||
<span>关闭</span>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
defineOptions({ name: 'SearchFooter' });
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.icon {
|
||||
box-shadow: inset 0 -2px #cdcde6, inset 0 0 1px 1px #fff, 0 1px 2px 1px #1e235a66;
|
||||
}
|
||||
</style>
|
@ -1,147 +0,0 @@
|
||||
<template>
|
||||
<n-modal
|
||||
v-model:show="show"
|
||||
:segmented="{ footer: 'soft' }"
|
||||
:closable="false"
|
||||
preset="card"
|
||||
footer-style="padding: 0; margin: 0"
|
||||
class="fixed left-0 right-0"
|
||||
:class="[isMobile ? 'wh-full top-0px rounded-0' : 'w-630px top-50px']"
|
||||
@after-leave="handleClose"
|
||||
>
|
||||
<n-input-group>
|
||||
<n-input ref="inputRef" v-model:value="keyword" clearable placeholder="请输入关键词搜索" @input="handleSearch">
|
||||
<template #prefix>
|
||||
<icon-uil-search class="text-15px text-[#c2c2c2]" />
|
||||
</template>
|
||||
</n-input>
|
||||
<n-button v-if="isMobile" type="primary" ghost @click="handleClose">取消</n-button>
|
||||
</n-input-group>
|
||||
|
||||
<div class="mt-20px">
|
||||
<n-empty v-if="resultOptions.length === 0" description="暂无搜索结果" />
|
||||
<search-result v-else v-model:value="activePath" :options="resultOptions" @enter="handleEnter" />
|
||||
</div>
|
||||
<template #footer>
|
||||
<search-footer v-if="!isMobile" />
|
||||
</template>
|
||||
</n-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, nextTick, ref, shallowRef, watch } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { onKeyStroke, useDebounceFn } from '@vueuse/core';
|
||||
import { useRouteStore } from '@/store';
|
||||
import { useBasicLayout } from '@/composables';
|
||||
import SearchResult from './SearchResult.vue';
|
||||
import SearchFooter from './SearchFooter.vue';
|
||||
|
||||
defineOptions({ name: 'SearchModal' });
|
||||
|
||||
interface Props {
|
||||
/** 弹窗显隐 */
|
||||
value: boolean;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
|
||||
interface Emits {
|
||||
(e: 'update:value', val: boolean): void;
|
||||
}
|
||||
|
||||
const emit = defineEmits<Emits>();
|
||||
|
||||
const { isMobile } = useBasicLayout();
|
||||
const router = useRouter();
|
||||
const routeStore = useRouteStore();
|
||||
|
||||
const keyword = ref('');
|
||||
const activePath = ref('');
|
||||
const resultOptions = shallowRef<AuthRoute.Route[]>([]);
|
||||
const inputRef = ref<HTMLInputElement>();
|
||||
|
||||
const handleSearch = useDebounceFn(search, 300);
|
||||
|
||||
const show = computed({
|
||||
get() {
|
||||
return props.value;
|
||||
},
|
||||
set(val: boolean) {
|
||||
emit('update:value', val);
|
||||
}
|
||||
});
|
||||
|
||||
watch(show, async val => {
|
||||
if (val) {
|
||||
/** 自动聚焦 */
|
||||
await nextTick();
|
||||
inputRef.value?.focus();
|
||||
}
|
||||
});
|
||||
|
||||
/** 查询 */
|
||||
function search() {
|
||||
resultOptions.value = routeStore.searchMenus.filter(
|
||||
menu => keyword.value && menu.meta?.title.toLocaleLowerCase().includes(keyword.value.toLocaleLowerCase().trim())
|
||||
);
|
||||
if (resultOptions.value?.length > 0) {
|
||||
activePath.value = resultOptions.value[0].path;
|
||||
} else {
|
||||
activePath.value = '';
|
||||
}
|
||||
}
|
||||
|
||||
function handleClose() {
|
||||
show.value = false;
|
||||
/** 延时处理防止用户看到某些操作 */
|
||||
setTimeout(() => {
|
||||
resultOptions.value = [];
|
||||
keyword.value = '';
|
||||
}, 200);
|
||||
}
|
||||
|
||||
/** key up */
|
||||
function handleUp() {
|
||||
const { length } = resultOptions.value;
|
||||
if (length === 0) return;
|
||||
const index = resultOptions.value.findIndex(item => item.path === activePath.value);
|
||||
if (index === 0) {
|
||||
activePath.value = resultOptions.value[length - 1].path;
|
||||
} else {
|
||||
activePath.value = resultOptions.value[index - 1].path;
|
||||
}
|
||||
}
|
||||
|
||||
/** key down */
|
||||
function handleDown() {
|
||||
const { length } = resultOptions.value;
|
||||
if (length === 0) return;
|
||||
const index = resultOptions.value.findIndex(item => item.path === activePath.value);
|
||||
if (index + 1 === length) {
|
||||
activePath.value = resultOptions.value[0].path;
|
||||
} else {
|
||||
activePath.value = resultOptions.value[index + 1].path;
|
||||
}
|
||||
}
|
||||
|
||||
/** key enter */
|
||||
function handleEnter() {
|
||||
const { length } = resultOptions.value;
|
||||
if (length === 0 || activePath.value === '') return;
|
||||
const routeItem = resultOptions.value.find(item => item.path === activePath.value);
|
||||
if (routeItem?.meta?.href) {
|
||||
window.open(activePath.value, '__blank');
|
||||
} else {
|
||||
router.push(activePath.value);
|
||||
handleClose();
|
||||
}
|
||||
}
|
||||
|
||||
onKeyStroke('Escape', handleClose);
|
||||
onKeyStroke('Enter', handleEnter);
|
||||
onKeyStroke('ArrowUp', handleUp);
|
||||
onKeyStroke('ArrowDown', handleDown);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
@ -1,64 +0,0 @@
|
||||
<template>
|
||||
<n-scrollbar>
|
||||
<div class="pb-12px">
|
||||
<template v-for="item in options" :key="item.path">
|
||||
<div
|
||||
class="bg-[#e5e7eb] dark:bg-dark h-56px mt-8px px-14px rounded-4px cursor-pointer flex-y-center justify-between"
|
||||
:style="{
|
||||
background: item.path === active ? theme.themeColor : '',
|
||||
color: item.path === active ? '#fff' : ''
|
||||
}"
|
||||
@click="handleTo"
|
||||
@mouseenter="handleMouse(item)"
|
||||
>
|
||||
<svg-icon :icon="item.meta.icon" :local-icon="item.meta.localIcon" />
|
||||
<span class="flex-1 ml-5px">{{ item.meta?.title }}</span>
|
||||
<icon-ant-design-enter-outlined class="icon text-20px p-2px mr-3px" />
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</n-scrollbar>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue';
|
||||
import { useThemeStore } from '@/store';
|
||||
|
||||
defineOptions({ name: 'SearchResult' });
|
||||
|
||||
interface Props {
|
||||
value: string;
|
||||
options: AuthRoute.Route[];
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
|
||||
interface Emits {
|
||||
(e: 'update:value', val: string): void;
|
||||
(e: 'enter'): void;
|
||||
}
|
||||
|
||||
const emit = defineEmits<Emits>();
|
||||
|
||||
const theme = useThemeStore();
|
||||
|
||||
const active = computed({
|
||||
get() {
|
||||
return props.value;
|
||||
},
|
||||
set(val: string) {
|
||||
emit('update:value', val);
|
||||
}
|
||||
});
|
||||
|
||||
/** 鼠标移入 */
|
||||
async function handleMouse(item: AuthRoute.Route) {
|
||||
active.value = item.path;
|
||||
}
|
||||
|
||||
function handleTo() {
|
||||
emit('enter');
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
@ -1,3 +0,0 @@
|
||||
import SearchModal from './SearchModal.vue';
|
||||
|
||||
export { SearchModal };
|
@ -1,30 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<hover-container
|
||||
class="w-40px h-full"
|
||||
tooltip-content="搜索"
|
||||
:inverted="theme.header.inverted"
|
||||
@click="handleSearch"
|
||||
>
|
||||
<icon-uil-search class="text-20px" />
|
||||
</hover-container>
|
||||
<search-modal v-model:value="show" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useThemeStore } from '@/store';
|
||||
import { useBoolean } from '@/hooks';
|
||||
import { SearchModal } from './components';
|
||||
|
||||
defineOptions({ name: 'GlobalSearch' });
|
||||
|
||||
const { bool: show, toggle } = useBoolean();
|
||||
const theme = useThemeStore();
|
||||
|
||||
function handleSearch() {
|
||||
toggle();
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
@ -1,22 +0,0 @@
|
||||
import type { App } from 'vue';
|
||||
import { createI18n } from 'vue-i18n';
|
||||
import messages from './lang';
|
||||
import type { LocaleKey } from './lang';
|
||||
|
||||
const i18n = createI18n({
|
||||
locale: 'zh-CN',
|
||||
fallbackLocale: 'en',
|
||||
messages
|
||||
});
|
||||
|
||||
export function setupI18n(app: App) {
|
||||
app.use(i18n);
|
||||
}
|
||||
|
||||
export function t(key: string) {
|
||||
return i18n.global.t(key);
|
||||
}
|
||||
|
||||
export function setLocale(locale: LocaleKey) {
|
||||
i18n.global.locale = locale;
|
||||
}
|
@ -1 +0,0 @@
|
||||
export * from './i18n';
|
@ -1,21 +0,0 @@
|
||||
import type { LocaleMessages } from 'vue-i18n';
|
||||
|
||||
const locale: LocaleMessages<I18nType.Schema> = {
|
||||
message: {
|
||||
system: {
|
||||
title: 'SoybeanAdmin'
|
||||
},
|
||||
routes: {
|
||||
dashboard: {
|
||||
dashboard: 'Dashboard',
|
||||
analysis: 'Analysis',
|
||||
workbench: 'Workbench'
|
||||
},
|
||||
about: {
|
||||
about: 'About'
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default locale;
|
@ -1,11 +0,0 @@
|
||||
import zhCN from './zh-cn';
|
||||
import en from './en';
|
||||
|
||||
const locales = {
|
||||
'zh-CN': zhCN,
|
||||
en
|
||||
};
|
||||
|
||||
export type LocaleKey = keyof typeof locales;
|
||||
|
||||
export default locales;
|
@ -1,21 +0,0 @@
|
||||
import type { LocaleMessages } from 'vue-i18n';
|
||||
|
||||
const locale: LocaleMessages<I18nType.Schema> = {
|
||||
message: {
|
||||
system: {
|
||||
title: 'Soybean管理系统'
|
||||
},
|
||||
routes: {
|
||||
dashboard: {
|
||||
dashboard: '仪表盘',
|
||||
analysis: '分析页',
|
||||
workbench: '工作台'
|
||||
},
|
||||
about: {
|
||||
about: '关于'
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default locale;
|
@ -5,7 +5,6 @@ import { setupDirectives } from './directives';
|
||||
import { setupRouter } from './router';
|
||||
import { setupAssets } from './plugins';
|
||||
import { setupStore } from './store';
|
||||
import { setupI18n } from './locales';
|
||||
|
||||
async function setupApp() {
|
||||
// import assets: js、css
|
||||
@ -27,7 +26,6 @@ async function setupApp() {
|
||||
// vue router
|
||||
await setupRouter(app);
|
||||
|
||||
setupI18n(app);
|
||||
|
||||
// mount app
|
||||
app.mount('#app');
|
||||
|
@ -1,7 +1,4 @@
|
||||
import 'uno.css';
|
||||
import 'swiper/css';
|
||||
import 'swiper/css/navigation';
|
||||
import 'swiper/css/pagination';
|
||||
import 'virtual:svg-icons-register';
|
||||
import '../styles/css/global.css';
|
||||
|
||||
|
@ -1,15 +0,0 @@
|
||||
const about: AuthRoute.Route = {
|
||||
name: 'about',
|
||||
path: '/about',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '关于',
|
||||
requiresAuth: true,
|
||||
singleLayout: 'basic',
|
||||
permissions: ['super', 'admin', 'user'],
|
||||
icon: 'fluent:book-information-24-regular',
|
||||
order: 10
|
||||
}
|
||||
};
|
||||
|
||||
export default about;
|
@ -1,35 +0,0 @@
|
||||
const authDemo: AuthRoute.Route = {
|
||||
name: 'auth-demo',
|
||||
path: '/auth-demo',
|
||||
component: 'basic',
|
||||
children: [
|
||||
{
|
||||
name: 'auth-demo_permission',
|
||||
path: '/auth-demo/permission',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '权限切换',
|
||||
requiresAuth: true,
|
||||
icon: 'ic:round-construction'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'auth-demo_super',
|
||||
path: '/auth-demo/super',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '超级管理员可见',
|
||||
requiresAuth: true,
|
||||
permissions: ['super'],
|
||||
icon: 'ic:round-supervisor-account'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '权限示例',
|
||||
icon: 'ic:baseline-security',
|
||||
order: 5
|
||||
}
|
||||
};
|
||||
|
||||
export default authDemo;
|
@ -1,44 +0,0 @@
|
||||
const component: AuthRoute.Route = {
|
||||
name: 'component',
|
||||
path: '/component',
|
||||
component: 'basic',
|
||||
children: [
|
||||
{
|
||||
name: 'component_button',
|
||||
path: '/component/button',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '按钮',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:button-cursor'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'component_card',
|
||||
path: '/component/card',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '卡片',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:card-outline'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'component_table',
|
||||
path: '/component/table',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '表格',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:table-large'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '组件示例',
|
||||
icon: 'cib:app-store',
|
||||
order: 3
|
||||
}
|
||||
};
|
||||
|
||||
export default component;
|
@ -1,34 +0,0 @@
|
||||
const dashboard: AuthRoute.Route = {
|
||||
name: 'dashboard',
|
||||
path: '/dashboard',
|
||||
component: 'basic',
|
||||
children: [
|
||||
{
|
||||
name: 'dashboard_analysis',
|
||||
path: '/dashboard/analysis',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '分析页',
|
||||
requiresAuth: true,
|
||||
icon: 'icon-park-outline:analysis'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'dashboard_workbench',
|
||||
path: '/dashboard/workbench',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '工作台',
|
||||
requiresAuth: true,
|
||||
icon: 'icon-park-outline:workbench'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '仪表盘',
|
||||
icon: 'mdi:monitor-dashboard',
|
||||
order: 1
|
||||
}
|
||||
};
|
||||
|
||||
export default dashboard;
|
@ -1,64 +0,0 @@
|
||||
const document: AuthRoute.Route = {
|
||||
name: 'document',
|
||||
path: '/document',
|
||||
component: 'basic',
|
||||
children: [
|
||||
{
|
||||
name: 'document_vue',
|
||||
path: '/document/vue',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'vue文档',
|
||||
requiresAuth: true,
|
||||
icon: 'logos:vue'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'document_vite',
|
||||
path: '/document/vite',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'vite文档',
|
||||
requiresAuth: true,
|
||||
icon: 'logos:vitejs'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'document_naive',
|
||||
path: '/document/naive',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'naive文档',
|
||||
requiresAuth: true,
|
||||
icon: 'logos:naiveui'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'document_project',
|
||||
path: '/document/project',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '项目文档',
|
||||
requiresAuth: true,
|
||||
localIcon: 'logo'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'document_project-link',
|
||||
path: '/document/project-link',
|
||||
meta: {
|
||||
title: '项目文档(外链)',
|
||||
requiresAuth: true,
|
||||
localIcon: 'logo',
|
||||
href: 'https://docs.soybean.pro/'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '文档',
|
||||
icon: 'mdi:file-document-multiple-outline',
|
||||
order: 2
|
||||
}
|
||||
};
|
||||
|
||||
export default document;
|
@ -1,44 +0,0 @@
|
||||
const exception: AuthRoute.Route = {
|
||||
name: 'exception',
|
||||
path: '/exception',
|
||||
component: 'basic',
|
||||
children: [
|
||||
{
|
||||
name: 'exception_403',
|
||||
path: '/exception/403',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '异常页403',
|
||||
requiresAuth: true,
|
||||
icon: 'ic:baseline-block'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'exception_404',
|
||||
path: '/exception/404',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '异常页404',
|
||||
requiresAuth: true,
|
||||
icon: 'ic:baseline-web-asset-off'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'exception_500',
|
||||
path: '/exception/500',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '异常页500',
|
||||
requiresAuth: true,
|
||||
icon: 'ic:baseline-wifi-off'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '异常页',
|
||||
icon: 'ant-design:exception-outlined',
|
||||
order: 7
|
||||
}
|
||||
};
|
||||
|
||||
export default exception;
|
@ -1,49 +0,0 @@
|
||||
const functionRoute: AuthRoute.Route = {
|
||||
name: 'function',
|
||||
path: '/function',
|
||||
component: 'basic',
|
||||
children: [
|
||||
{
|
||||
name: 'function_tab',
|
||||
path: '/function/tab',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'Tab',
|
||||
requiresAuth: true,
|
||||
icon: 'ic:round-tab'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'function_tab-detail',
|
||||
path: '/function/tab-detail',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'Tab Detail',
|
||||
requiresAuth: true,
|
||||
hide: true,
|
||||
activeMenu: 'function_tab',
|
||||
icon: 'ic:round-tab'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'function_tab-multi-detail',
|
||||
path: '/function/tab-multi-detail',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'Tab Multi Detail',
|
||||
requiresAuth: true,
|
||||
hide: true,
|
||||
multiTab: true,
|
||||
activeMenu: 'function_tab',
|
||||
icon: 'ic:round-tab'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '功能',
|
||||
icon: 'icon-park-outline:all-application',
|
||||
order: 6
|
||||
}
|
||||
};
|
||||
|
||||
export default functionRoute;
|
@ -1,54 +0,0 @@
|
||||
const management: AuthRoute.Route = {
|
||||
name: 'management',
|
||||
path: '/management',
|
||||
component: 'basic',
|
||||
children: [
|
||||
{
|
||||
name: 'management_auth',
|
||||
path: '/management/auth',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '权限管理',
|
||||
requiresAuth: true,
|
||||
icon: 'ic:baseline-security'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'management_role',
|
||||
path: '/management/role',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '角色管理',
|
||||
requiresAuth: true,
|
||||
icon: 'carbon:user-role'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'management_user',
|
||||
path: '/management/user',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '用户管理',
|
||||
requiresAuth: true,
|
||||
icon: 'ic:round-manage-accounts'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'management_route',
|
||||
path: '/management/route',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '路由管理',
|
||||
requiresAuth: true,
|
||||
icon: 'material-symbols:route'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '系统管理',
|
||||
icon: 'carbon:cloud-service-management',
|
||||
order: 9
|
||||
}
|
||||
};
|
||||
|
||||
export default management;
|
@ -1,136 +0,0 @@
|
||||
const plugin: AuthRoute.Route = {
|
||||
name: 'plugin',
|
||||
path: '/plugin',
|
||||
component: 'basic',
|
||||
children: [
|
||||
{
|
||||
name: 'plugin_charts',
|
||||
path: '/plugin/charts',
|
||||
component: 'multi',
|
||||
children: [
|
||||
{
|
||||
name: 'plugin_charts_echarts',
|
||||
path: '/plugin/charts/echarts',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'ECharts',
|
||||
requiresAuth: true,
|
||||
icon: 'simple-icons:apacheecharts'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'plugin_charts_antv',
|
||||
path: '/plugin/charts/antv',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'AntV',
|
||||
requiresAuth: true,
|
||||
icon: 'simple-icons:antdesign'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '图表',
|
||||
icon: 'mdi:chart-areaspline'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'plugin_map',
|
||||
path: '/plugin/map',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '地图',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:map'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'plugin_video',
|
||||
path: '/plugin/video',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '视频',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:video'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'plugin_editor',
|
||||
path: '/plugin/editor',
|
||||
component: 'multi',
|
||||
children: [
|
||||
{
|
||||
name: 'plugin_editor_quill',
|
||||
path: '/plugin/editor/quill',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '富文本编辑器',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:file-document-edit-outline'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'plugin_editor_markdown',
|
||||
path: '/plugin/editor/markdown',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'markdown编辑器',
|
||||
requiresAuth: true,
|
||||
icon: 'ri:markdown-line'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '编辑器',
|
||||
icon: 'icon-park-outline:editor'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'plugin_swiper',
|
||||
path: '/plugin/swiper',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'Swiper插件',
|
||||
requiresAuth: true,
|
||||
icon: 'simple-icons:swiper'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'plugin_copy',
|
||||
path: '/plugin/copy',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '剪贴板',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:clipboard-outline'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'plugin_icon',
|
||||
path: '/plugin/icon',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '图标',
|
||||
requiresAuth: true,
|
||||
localIcon: 'custom-icon'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'plugin_print',
|
||||
path: '/plugin/print',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '打印',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:printer'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '插件示例',
|
||||
icon: 'clarity:plugin-line',
|
||||
order: 4
|
||||
}
|
||||
};
|
||||
|
||||
export default plugin;
|
@ -1,2 +1 @@
|
||||
export * from './auth';
|
||||
export * from './management';
|
||||
|
@ -1,13 +0,0 @@
|
||||
export function adapterOfFetchUserList(data: ApiUserManagement.User[] | null): UserManagement.User[] {
|
||||
if (!data) return [];
|
||||
|
||||
return data.map((item, index) => {
|
||||
const user: UserManagement.User = {
|
||||
index: index + 1,
|
||||
key: item.id,
|
||||
...item
|
||||
};
|
||||
|
||||
return user;
|
||||
});
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
import { adapter } from '@/utils';
|
||||
import { mockRequest } from '../request';
|
||||
import { adapterOfFetchUserList } from './management.adapter';
|
||||
|
||||
/** 获取用户列表 */
|
||||
export const fetchUserList = async () => {
|
||||
const data = await mockRequest.post<ApiUserManagement.User[] | null>('/getAllUserList');
|
||||
return adapter(adapterOfFetchUserList, data);
|
||||
};
|
29
src/typings/api.d.ts
vendored
29
src/typings/api.d.ts
vendored
@ -21,32 +21,3 @@ declare namespace ApiRoute {
|
||||
home: AuthRoute.AllRouteKey;
|
||||
}
|
||||
}
|
||||
|
||||
declare namespace ApiUserManagement {
|
||||
interface User {
|
||||
/** 用户id */
|
||||
id: string;
|
||||
/** 用户名 */
|
||||
userName: string | null;
|
||||
/** 用户年龄 */
|
||||
age: number | null;
|
||||
/**
|
||||
* 用户性别
|
||||
* - 0: 女
|
||||
* - 1: 男
|
||||
*/
|
||||
gender: '0' | '1' | null;
|
||||
/** 用户手机号码 */
|
||||
phone: string;
|
||||
/** 用户邮箱 */
|
||||
email: string | null;
|
||||
/**
|
||||
* 用户状态
|
||||
* - 1: 启用
|
||||
* - 2: 禁用
|
||||
* - 3: 冻结
|
||||
* - 4: 软删除
|
||||
*/
|
||||
userStatus: '1' | '2' | '3' | '4' | null;
|
||||
}
|
||||
}
|
||||
|
25
src/typings/business.d.ts
vendored
25
src/typings/business.d.ts
vendored
@ -19,28 +19,3 @@ declare namespace Auth {
|
||||
userRole: RoleType;
|
||||
}
|
||||
}
|
||||
|
||||
declare namespace UserManagement {
|
||||
interface User extends ApiUserManagement.User {
|
||||
/** 序号 */
|
||||
index: number;
|
||||
/** 表格的key(id) */
|
||||
key: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户性别
|
||||
* - 0: 女
|
||||
* - 1: 男
|
||||
*/
|
||||
type GenderKey = NonNullable<User['gender']>;
|
||||
|
||||
/**
|
||||
* 用户状态
|
||||
* - 1: 启用
|
||||
* - 2: 禁用
|
||||
* - 3: 冻结
|
||||
* - 4: 软删除
|
||||
*/
|
||||
type UserStatusKey = NonNullable<User['userStatus']>;
|
||||
}
|
||||
|
3
src/typings/global.d.ts
vendored
3
src/typings/global.d.ts
vendored
@ -13,6 +13,3 @@ declare namespace Common {
|
||||
*/
|
||||
type StrategyAction = [boolean, () => void];
|
||||
}
|
||||
|
||||
/** 构建时间 */
|
||||
declare const PROJECT_BUILD_TIME: string;
|
||||
|
9
src/typings/package.d.ts
vendored
9
src/typings/package.d.ts
vendored
@ -1,9 +0,0 @@
|
||||
/// <reference types="@amap/amap-jsapi-types" />
|
||||
/// <reference types="bmapgl" />
|
||||
|
||||
declare namespace BMap {
|
||||
class Map extends BMapGL.Map {}
|
||||
class Point extends BMapGL.Point {}
|
||||
}
|
||||
|
||||
declare const TMap: any;
|
47
src/typings/router-page.d.ts
vendored
47
src/typings/router-page.d.ts
vendored
@ -10,58 +10,15 @@ declare namespace RouterPage {
|
||||
| '403'
|
||||
| '404'
|
||||
| '500'
|
||||
| 'about'
|
||||
| 'auth-demo'
|
||||
| 'auth-demo_permission'
|
||||
| 'auth-demo_super'
|
||||
| 'component'
|
||||
| 'component_button'
|
||||
| 'component_card'
|
||||
| 'component_table'
|
||||
| 'constant-page'
|
||||
| 'dashboard'
|
||||
| 'dashboard_analysis'
|
||||
| 'dashboard_workbench'
|
||||
| 'document'
|
||||
| 'document_naive'
|
||||
| 'document_project'
|
||||
| 'document_project-link'
|
||||
| 'document_vite'
|
||||
| 'document_vue'
|
||||
| 'exception'
|
||||
| 'exception_403'
|
||||
| 'exception_404'
|
||||
| 'exception_500'
|
||||
| 'function'
|
||||
| 'function_tab'
|
||||
| 'function_tab-detail'
|
||||
| 'function_tab-multi-detail'
|
||||
| 'login'
|
||||
| 'management'
|
||||
| 'management_auth'
|
||||
| 'management_role'
|
||||
| 'management_route'
|
||||
| 'management_user'
|
||||
| 'multi-menu'
|
||||
| 'multi-menu_first'
|
||||
| 'multi-menu_first_second'
|
||||
| 'multi-menu_first_second-new'
|
||||
| 'multi-menu_first_second-new_third'
|
||||
| 'not-found'
|
||||
| 'plugin'
|
||||
| 'plugin_charts'
|
||||
| 'plugin_charts_antv'
|
||||
| 'plugin_charts_echarts'
|
||||
| 'plugin_copy'
|
||||
| 'plugin_editor'
|
||||
| 'plugin_editor_markdown'
|
||||
| 'plugin_editor_quill'
|
||||
| 'plugin_icon'
|
||||
| 'plugin_map'
|
||||
| 'plugin_print'
|
||||
| 'plugin_swiper'
|
||||
| 'plugin_video';
|
||||
| 'not-found';
|
||||
|
||||
/** 最后一级路由(该级路有对应的vue文件) */
|
||||
type LastDegreeRouteKey = Extract<RouteKey, '403' | '404' | '500' | 'about' | 'auth-demo_permission' | 'auth-demo_super' | 'component_button' | 'component_card' | 'component_table' | 'constant-page' | 'dashboard_analysis' | 'dashboard_workbench' | 'document_naive' | 'document_project' | 'document_project-link' | 'document_vite' | 'document_vue' | 'exception_403' | 'exception_404' | 'exception_500' | 'function_tab' | 'function_tab-detail' | 'function_tab-multi-detail' | 'login' | 'management_auth' | 'management_role' | 'management_route' | 'management_user' | 'multi-menu_first_second' | 'multi-menu_first_second-new_third' | 'not-found' | 'plugin_charts_antv' | 'plugin_charts_echarts' | 'plugin_copy' | 'plugin_editor_markdown' | 'plugin_editor_quill' | 'plugin_icon' | 'plugin_map' | 'plugin_print' | 'plugin_swiper' | 'plugin_video'>
|
||||
type LastDegreeRouteKey = Extract<RouteKey, '403' | '404' | '500' | 'constant-page' | 'login' | 'multi-menu_first_second' | 'multi-menu_first_second-new_third' | 'not-found'>
|
||||
}
|
||||
|
@ -1,19 +0,0 @@
|
||||
<template>
|
||||
<n-card title="开发环境依赖" :bordered="false" size="small" class="rounded-16px shadow-sm">
|
||||
<n-descriptions label-placement="left" bordered size="small">
|
||||
<n-descriptions-item v-for="item in devDependencies" :key="item.name" :label="item.name">
|
||||
{{ item.version }}
|
||||
</n-descriptions-item>
|
||||
</n-descriptions>
|
||||
</n-card>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { pkgJson } from './model';
|
||||
|
||||
defineOptions({ name: 'DevDependency' });
|
||||
|
||||
const { devDependencies } = pkgJson;
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
@ -1,19 +0,0 @@
|
||||
<template>
|
||||
<n-card title="生产环境依赖" :bordered="false" size="small" class="rounded-16px shadow-sm">
|
||||
<n-descriptions label-placement="left" bordered size="small">
|
||||
<n-descriptions-item v-for="item in dependencies" :key="item.name" :label="item.name">
|
||||
{{ item.version }}
|
||||
</n-descriptions-item>
|
||||
</n-descriptions>
|
||||
</n-card>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { pkgJson } from './model';
|
||||
|
||||
defineOptions({ name: 'ProDependency' });
|
||||
|
||||
const { dependencies } = pkgJson;
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
@ -1,29 +0,0 @@
|
||||
<template>
|
||||
<n-card title="项目信息" :bordered="false" size="small" class="rounded-16px shadow-sm">
|
||||
<n-descriptions label-placement="left" bordered size="small" :column="2">
|
||||
<n-descriptions-item label="版本">
|
||||
<n-tag type="primary">{{ version }}</n-tag>
|
||||
</n-descriptions-item>
|
||||
<n-descriptions-item label="最后编译时间">
|
||||
<n-tag type="primary">{{ latestBuildTime }}</n-tag>
|
||||
</n-descriptions-item>
|
||||
<n-descriptions-item label="Github地址">
|
||||
<a class="text-primary" href="https://github.com/honghuangdc/soybean-admin" target="_blank">Github地址</a>
|
||||
</n-descriptions-item>
|
||||
<n-descriptions-item label="预览地址">
|
||||
<a class="text-primary" href="https://soybean.pro" target="_blank">预览地址</a>
|
||||
</n-descriptions-item>
|
||||
</n-descriptions>
|
||||
</n-card>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { pkgJson } from './model';
|
||||
|
||||
defineOptions({ name: 'ProjectInfo' });
|
||||
|
||||
const { version } = pkgJson;
|
||||
const latestBuildTime = PROJECT_BUILD_TIME;
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
@ -1,14 +0,0 @@
|
||||
<template>
|
||||
<n-card title="关于" :bordered="false" size="large" class="rounded-16px shadow-sm">
|
||||
<p class="leading-24px">
|
||||
Soybean Admin 是一个基于 Vue3、Vite、Naive UI、TypeScript
|
||||
的中后台解决方案,它使用了最新的前端技术栈,并提炼了典型的业务模型,页面,包括二次封装组件、动态菜单、权限校验、粒子化权限控制等功能,它可以帮助你快速搭建企业级中后台项目,相信不管是从新技术使用还是其他方面,都能帮助到你。
|
||||
</p>
|
||||
</n-card>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
defineOptions({ name: 'ProjectIntroduction' });
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
@ -1,6 +0,0 @@
|
||||
import ProjectIntroduction from './ProjectIntroduction.vue';
|
||||
import ProjectInfo from './ProjectInfo.vue';
|
||||
import ProDependency from './ProDependency.vue';
|
||||
import DevDependency from './DevDependency.vue';
|
||||
|
||||
export { ProjectIntroduction, ProjectInfo, ProDependency, DevDependency };
|
@ -1,39 +0,0 @@
|
||||
import pkg from '~/package.json';
|
||||
|
||||
/** npm依赖包版本信息 */
|
||||
export interface PkgVersionInfo {
|
||||
name: string;
|
||||
version: string;
|
||||
}
|
||||
|
||||
interface Package {
|
||||
name: string;
|
||||
version: string;
|
||||
dependencies: Record<string, string>;
|
||||
devDependencies: Record<string, string>;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
interface PkgJson {
|
||||
name: string;
|
||||
version: string;
|
||||
dependencies: PkgVersionInfo[];
|
||||
devDependencies: PkgVersionInfo[];
|
||||
}
|
||||
|
||||
const pkgWithType = pkg as Package;
|
||||
|
||||
function transformVersionData(tuple: [string, string]): PkgVersionInfo {
|
||||
const [name, version] = tuple;
|
||||
return {
|
||||
name,
|
||||
version
|
||||
};
|
||||
}
|
||||
|
||||
export const pkgJson: PkgJson = {
|
||||
name: pkgWithType.name,
|
||||
version: pkgWithType.version,
|
||||
dependencies: Object.entries(pkgWithType.dependencies).map(item => transformVersionData(item)),
|
||||
devDependencies: Object.entries(pkgWithType.devDependencies).map(item => transformVersionData(item))
|
||||
};
|
@ -1,14 +0,0 @@
|
||||
<template>
|
||||
<n-space :vertical="true" :size="16">
|
||||
<project-introduction />
|
||||
<project-info />
|
||||
<pro-dependency />
|
||||
<dev-dependency />
|
||||
</n-space>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { DevDependency, ProDependency, ProjectInfo, ProjectIntroduction } from './components';
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
@ -1,66 +0,0 @@
|
||||
<template>
|
||||
<div class="h-full">
|
||||
<n-card title="权限切换" class="h-full shadow-sm rounded-16px">
|
||||
<div class="pb-12px">
|
||||
<n-gradient-text type="primary" :size="20">当前用户的权限:{{ auth.userInfo.userRole }}</n-gradient-text>
|
||||
</div>
|
||||
<n-select
|
||||
:value="auth.userInfo.userRole"
|
||||
class="w-120px"
|
||||
size="small"
|
||||
:options="options"
|
||||
@update:value="auth.updateUserRole"
|
||||
/>
|
||||
<div class="py-12px">
|
||||
<n-gradient-text type="primary" :size="20">权限指令 v-permission</n-gradient-text>
|
||||
</div>
|
||||
<div>
|
||||
<n-button v-permission="'super'" class="mr-12px">super可见</n-button>
|
||||
<n-button v-permission="'admin'" class="mr-12px">admin可见</n-button>
|
||||
<n-button v-permission="['admin', 'user']">admin和test可见</n-button>
|
||||
</div>
|
||||
<div class="py-12px">
|
||||
<n-gradient-text type="primary" :size="20">权限函数 hasPermission</n-gradient-text>
|
||||
</div>
|
||||
<n-space>
|
||||
<n-button v-if="hasPermission('super')">super可见</n-button>
|
||||
<n-button v-if="hasPermission('admin')">admin可见</n-button>
|
||||
<n-button v-if="hasPermission(['admin', 'user'])">admin和user可见</n-button>
|
||||
</n-space>
|
||||
</n-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { watch } from 'vue';
|
||||
import type { SelectOption } from 'naive-ui';
|
||||
import { EnumUserRole } from '@/enum';
|
||||
import { useAppStore, useAuthStore } from '@/store';
|
||||
import { usePermission } from '@/composables';
|
||||
|
||||
const app = useAppStore();
|
||||
const auth = useAuthStore();
|
||||
const { hasPermission } = usePermission();
|
||||
|
||||
interface RoleList {
|
||||
label: string;
|
||||
value: keyof typeof EnumUserRole;
|
||||
}
|
||||
|
||||
const roleList: RoleList[] = [
|
||||
{ label: EnumUserRole.super, value: 'super' },
|
||||
{ label: EnumUserRole.admin, value: 'admin' },
|
||||
{ label: EnumUserRole.user, value: 'user' }
|
||||
];
|
||||
|
||||
const options: SelectOption[] = roleList as unknown as SelectOption[];
|
||||
|
||||
watch(
|
||||
() => auth.userInfo.userRole,
|
||||
async () => {
|
||||
app.reloadPage();
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
@ -1,9 +0,0 @@
|
||||
<template>
|
||||
<div class="h-full">
|
||||
<n-card title="当前页面只有super才能看到" class="h-full shadow-sm rounded-16px"> </n-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<style scoped></style>
|
@ -1,575 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<n-card title="按钮" class="h-full shadow-sm rounded-16px">
|
||||
<n-grid cols="s:1 m:2" responsive="screen" :x-gap="16" :y-gap="16">
|
||||
<n-grid-item v-for="item in buttonExample" :key="item.id">
|
||||
<n-card :title="item.label" class="min-h-180px">
|
||||
<p v-if="item.desc" class="pb-16px">{{ item.desc }}</p>
|
||||
<n-space>
|
||||
<n-button
|
||||
v-for="button in item.buttons"
|
||||
:key="button.id"
|
||||
v-bind="button.props"
|
||||
:style="`--icon-margin: ${button.props.circle ? 0 : 6}px`"
|
||||
>
|
||||
<template v-if="button.icon" #icon>
|
||||
<svg-icon :icon="button.icon" />
|
||||
</template>
|
||||
{{ button.label }}
|
||||
</n-button>
|
||||
</n-space>
|
||||
</n-card>
|
||||
</n-grid-item>
|
||||
<n-grid-item class="h-180px">
|
||||
<n-card title="加载中" class="h-full">
|
||||
<p class="pb-16px">按钮有加载状态。</p>
|
||||
<n-space>
|
||||
<n-button :loading="loading" type="primary" @click="startLoading">开始加载</n-button>
|
||||
<n-button @click="endLoading">取消加载</n-button>
|
||||
</n-space>
|
||||
</n-card>
|
||||
</n-grid-item>
|
||||
</n-grid>
|
||||
</n-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { ButtonProps } from 'naive-ui';
|
||||
import { useLoading } from '@/hooks';
|
||||
|
||||
interface ButtonDetail {
|
||||
id: number;
|
||||
props: ButtonProps & { href?: string; target?: string };
|
||||
label?: string;
|
||||
icon?: string;
|
||||
}
|
||||
|
||||
interface ButtonExample {
|
||||
id: number;
|
||||
label: string;
|
||||
buttons: ButtonDetail[];
|
||||
desc?: string;
|
||||
}
|
||||
|
||||
const { loading, startLoading, endLoading } = useLoading();
|
||||
|
||||
const buttonExample: ButtonExample[] = [
|
||||
{
|
||||
id: 0,
|
||||
label: '基础',
|
||||
buttons: [
|
||||
{
|
||||
id: 0,
|
||||
props: {},
|
||||
label: 'Default'
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
props: { type: 'tertiary' },
|
||||
label: 'Tertiary'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
props: { type: 'primary' },
|
||||
label: 'Primary'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
props: { type: 'info' },
|
||||
label: 'Info'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
props: { type: 'success' },
|
||||
label: 'Success'
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
props: { type: 'warning' },
|
||||
label: 'Warning'
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
props: { type: 'error' },
|
||||
label: 'Error'
|
||||
}
|
||||
],
|
||||
desc: '按钮的 type 分别为 default、primary、info、success、warning 和 error。'
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
label: '次要按钮',
|
||||
buttons: [
|
||||
{
|
||||
id: 0,
|
||||
props: { strong: true, secondary: true },
|
||||
label: 'Default'
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
props: { strong: true, secondary: true, type: 'tertiary' },
|
||||
label: 'Tertiary'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
props: { strong: true, secondary: true, type: 'primary' },
|
||||
label: 'Primary'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
props: { strong: true, secondary: true, type: 'info' },
|
||||
label: 'Info'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
props: { strong: true, secondary: true, type: 'success' },
|
||||
label: 'Success'
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
props: { strong: true, secondary: true, type: 'warning' },
|
||||
label: 'Warning'
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
props: { strong: true, secondary: true, type: 'error' },
|
||||
label: 'Error'
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
props: { strong: true, secondary: true, round: true },
|
||||
label: 'Default'
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
props: { strong: true, secondary: true, round: true, type: 'tertiary' },
|
||||
label: 'Tertiary'
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
props: { strong: true, secondary: true, round: true, type: 'primary' },
|
||||
label: 'Primary'
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
props: { strong: true, secondary: true, round: true, type: 'info' },
|
||||
label: 'Info'
|
||||
},
|
||||
{
|
||||
id: 11,
|
||||
props: { strong: true, secondary: true, round: true, type: 'success' },
|
||||
label: 'Success'
|
||||
},
|
||||
{
|
||||
id: 12,
|
||||
props: { strong: true, secondary: true, round: true, type: 'warning' },
|
||||
label: 'Warning'
|
||||
},
|
||||
{
|
||||
id: 13,
|
||||
props: { strong: true, secondary: true, round: true, type: 'error' },
|
||||
label: 'Error'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
label: '次次要按钮',
|
||||
buttons: [
|
||||
{
|
||||
id: 0,
|
||||
props: { tertiary: true },
|
||||
label: 'Default'
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
props: { tertiary: true, type: 'primary' },
|
||||
label: 'Primary'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
props: { tertiary: true, type: 'info' },
|
||||
label: 'Info'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
props: { tertiary: true, type: 'success' },
|
||||
label: 'Success'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
props: { tertiary: true, type: 'warning' },
|
||||
label: 'Warning'
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
props: { tertiary: true, type: 'error' },
|
||||
label: 'Error'
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
props: { tertiary: true, round: true },
|
||||
label: 'Default'
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
props: { tertiary: true, round: true, type: 'primary' },
|
||||
label: 'Primary'
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
props: { tertiary: true, round: true, type: 'info' },
|
||||
label: 'Info'
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
props: { tertiary: true, round: true, type: 'success' },
|
||||
label: 'Success'
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
props: { tertiary: true, round: true, type: 'warning' },
|
||||
label: 'Warning'
|
||||
},
|
||||
{
|
||||
id: 11,
|
||||
props: { tertiary: true, round: true, type: 'error' },
|
||||
label: 'Error'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
label: '次次次要按钮',
|
||||
buttons: [
|
||||
{
|
||||
id: 0,
|
||||
props: { quaternary: true },
|
||||
label: 'Default'
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
props: { quaternary: true, type: 'primary' },
|
||||
label: 'Primary'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
props: { quaternary: true, type: 'info' },
|
||||
label: 'Info'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
props: { quaternary: true, type: 'success' },
|
||||
label: 'Success'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
props: { quaternary: true, type: 'warning' },
|
||||
label: 'Warning'
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
props: { quaternary: true, type: 'error' },
|
||||
label: 'Error'
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
props: { quaternary: true, round: true },
|
||||
label: 'Default'
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
props: { quaternary: true, round: true, type: 'primary' },
|
||||
label: 'Primary'
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
props: { quaternary: true, round: true, type: 'info' },
|
||||
label: 'Info'
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
props: { quaternary: true, round: true, type: 'success' },
|
||||
label: 'Success'
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
props: { quaternary: true, round: true, type: 'warning' },
|
||||
label: 'Warning'
|
||||
},
|
||||
{
|
||||
id: 11,
|
||||
props: { quaternary: true, round: true, type: 'error' },
|
||||
label: 'Error'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
label: '虚线按钮',
|
||||
buttons: [
|
||||
{
|
||||
id: 0,
|
||||
props: { dashed: true },
|
||||
label: 'Default'
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
props: { dashed: true, type: 'tertiary' },
|
||||
label: 'Tertiary'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
props: { dashed: true, type: 'primary' },
|
||||
label: 'Primary'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
props: { dashed: true, type: 'info' },
|
||||
label: 'Info'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
props: { dashed: true, type: 'success' },
|
||||
label: 'Success'
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
props: { dashed: true, type: 'warning' },
|
||||
label: 'Warning'
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
props: { dashed: true, type: 'error' },
|
||||
label: 'Error'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
label: '尺寸',
|
||||
buttons: [
|
||||
{
|
||||
id: 0,
|
||||
props: { size: 'tiny', strong: true },
|
||||
label: '小小'
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
props: { size: 'small', strong: true },
|
||||
label: '小'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
props: { size: 'medium', strong: true },
|
||||
label: '不小'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
props: { size: 'large', strong: true },
|
||||
label: '不不小'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
label: '文本按钮',
|
||||
buttons: [
|
||||
{
|
||||
id: 0,
|
||||
props: { text: true },
|
||||
label: '那车头依然吐着烟',
|
||||
icon: 'mdi:train'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
label: '自定义标签按钮',
|
||||
buttons: [
|
||||
{
|
||||
id: 0,
|
||||
props: {
|
||||
text: true,
|
||||
tag: 'a',
|
||||
href: 'https://github.com/honghuangdc/soybean-admin',
|
||||
target: '_blank',
|
||||
type: 'primary'
|
||||
},
|
||||
label: 'soybean-admin'
|
||||
}
|
||||
],
|
||||
desc: '你可以把按钮渲染成不同的标签,比如 a标签 。'
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
label: '按钮禁用',
|
||||
buttons: [
|
||||
{
|
||||
id: 0,
|
||||
props: {
|
||||
disabled: true
|
||||
},
|
||||
label: '不许点'
|
||||
}
|
||||
],
|
||||
desc: '按钮可以被禁用'
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
label: '图标按钮',
|
||||
buttons: [
|
||||
{
|
||||
id: 0,
|
||||
props: {
|
||||
secondary: true,
|
||||
strong: true
|
||||
},
|
||||
label: '+100元',
|
||||
icon: 'mdi:cash-100'
|
||||
},
|
||||
{
|
||||
id: 0,
|
||||
props: {
|
||||
iconPlacement: 'right',
|
||||
secondary: true,
|
||||
strong: true
|
||||
},
|
||||
label: '+100元',
|
||||
icon: 'mdi:cash-100'
|
||||
}
|
||||
],
|
||||
desc: '在按钮上使用图标。'
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
label: '不同形状按钮',
|
||||
buttons: [
|
||||
{
|
||||
id: 0,
|
||||
props: {
|
||||
circle: true
|
||||
},
|
||||
icon: 'mdi:cash-100'
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
props: {
|
||||
round: true
|
||||
},
|
||||
label: '圆角'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
props: {},
|
||||
label: '方'
|
||||
}
|
||||
],
|
||||
desc: '按钮拥有不同的形状。'
|
||||
},
|
||||
{
|
||||
id: 11,
|
||||
label: '透明背景按钮',
|
||||
buttons: [
|
||||
{
|
||||
id: 0,
|
||||
props: { ghost: true },
|
||||
label: 'Default'
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
props: { ghost: true, type: 'tertiary' },
|
||||
label: 'Tertiary'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
props: { ghost: true, type: 'primary' },
|
||||
label: 'Primary'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
props: { ghost: true, type: 'info' },
|
||||
label: 'Info'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
props: { ghost: true, type: 'success' },
|
||||
label: 'Success'
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
props: { ghost: true, type: 'warning' },
|
||||
label: 'Warning'
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
props: { ghost: true, type: 'error' },
|
||||
label: 'Error'
|
||||
}
|
||||
],
|
||||
desc: 'Ghost 按钮有透明的背景。'
|
||||
},
|
||||
{
|
||||
id: 12,
|
||||
label: '自定义颜色',
|
||||
buttons: [
|
||||
{
|
||||
id: 0,
|
||||
props: {
|
||||
color: '#8a2be2'
|
||||
},
|
||||
label: '#8a2be2',
|
||||
icon: 'ic:baseline-color-lens'
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
props: {
|
||||
color: '#ff69b4'
|
||||
},
|
||||
label: '#ff69b4',
|
||||
icon: 'ic:baseline-color-lens'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
props: {
|
||||
color: '#8a2be2',
|
||||
ghost: true
|
||||
},
|
||||
label: '#8a2be2',
|
||||
icon: 'ic:baseline-color-lens'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
props: {
|
||||
color: '#ff69b4',
|
||||
ghost: true
|
||||
},
|
||||
label: '#ff69b4',
|
||||
icon: 'ic:baseline-color-lens'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
props: {
|
||||
color: '#8a2be2',
|
||||
text: true
|
||||
},
|
||||
label: '#8a2be2',
|
||||
icon: 'ic:baseline-color-lens'
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
props: {
|
||||
color: '#ff69b4',
|
||||
text: true
|
||||
},
|
||||
label: '#ff69b4',
|
||||
icon: 'ic:baseline-color-lens'
|
||||
}
|
||||
],
|
||||
desc: '这两个颜色看起来像毒蘑菇。'
|
||||
}
|
||||
];
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
@ -1,42 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<n-card title="卡片" class="h-full shadow-sm rounded-16px">
|
||||
<n-space :vertical="true">
|
||||
<n-card title="基本用法">
|
||||
<p class="pb-16px">基础卡片</p>
|
||||
<n-card title="卡片">卡片内容</n-card>
|
||||
</n-card>
|
||||
<n-card title="尺寸">
|
||||
<p class="pb-16px">卡片有 small、medium、large、huge 尺寸。</p>
|
||||
<n-space vertical>
|
||||
<n-card title="小卡片" size="small">卡片内容</n-card>
|
||||
<n-card title="中卡片" size="medium">卡片内容</n-card>
|
||||
<n-card title="大卡片" size="large">卡片内容</n-card>
|
||||
<n-card title="超大卡片" size="huge">卡片内容</n-card>
|
||||
</n-space>
|
||||
</n-card>
|
||||
<n-card title="文本按钮">
|
||||
<p class="pb-16px">
|
||||
content 和 footer 可以被 hard 或 soft 分段,action 可以被分段。分段分割线会在区域的上方出现。
|
||||
</p>
|
||||
<n-card
|
||||
title="卡片分段示例"
|
||||
:segmented="{
|
||||
content: true,
|
||||
footer: 'soft'
|
||||
}"
|
||||
>
|
||||
<template #header-extra>#header-extra</template>
|
||||
卡片内容
|
||||
<template #footer>#footer</template>
|
||||
<template #action>#action</template>
|
||||
</n-card>
|
||||
</n-card>
|
||||
</n-space>
|
||||
</n-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<style scoped></style>
|
@ -1,107 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<n-card title="表格" class="h-full shadow-sm rounded-16px">
|
||||
<n-space :vertical="true">
|
||||
<n-space>
|
||||
<n-button @click="getDataSource">有数据</n-button>
|
||||
<n-button @click="getEmptyDataSource">空数据</n-button>
|
||||
</n-space>
|
||||
<loading-empty-wrapper class="h-480px" :loading="loading" :empty="empty">
|
||||
<n-data-table :columns="columns" :data="dataSource" :flex-height="true" class="h-480px" />
|
||||
</loading-empty-wrapper>
|
||||
</n-space>
|
||||
</n-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="tsx">
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { NSpace, NButton, NPopconfirm } from 'naive-ui';
|
||||
import type { DataTableColumn } from 'naive-ui';
|
||||
import { useLoadingEmpty } from '@/hooks';
|
||||
import { getRandomInteger } from '@/utils';
|
||||
|
||||
interface DataSource {
|
||||
name: string;
|
||||
age: number;
|
||||
address: string;
|
||||
}
|
||||
|
||||
const { loading, startLoading, endLoading, empty, setEmpty } = useLoadingEmpty();
|
||||
|
||||
const columns: DataTableColumn[] = [
|
||||
{
|
||||
title: 'Name',
|
||||
key: 'name',
|
||||
align: 'center'
|
||||
},
|
||||
{
|
||||
title: 'Age',
|
||||
key: 'age',
|
||||
align: 'center'
|
||||
},
|
||||
{
|
||||
title: 'Address',
|
||||
key: 'address',
|
||||
align: 'center'
|
||||
},
|
||||
{
|
||||
key: 'action',
|
||||
title: 'Action',
|
||||
align: 'center',
|
||||
render: () => {
|
||||
return (
|
||||
<NSpace justify={'center'}>
|
||||
<NButton size={'small'} onClick={() => {}}>
|
||||
编辑
|
||||
</NButton>
|
||||
<NPopconfirm onPositiveClick={() => {}}>
|
||||
{{
|
||||
default: () => '确认删除',
|
||||
trigger: () => <NButton size={'small'}>删除</NButton>
|
||||
}}
|
||||
</NPopconfirm>
|
||||
</NSpace>
|
||||
);
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
const dataSource = ref<DataSource[]>([]);
|
||||
|
||||
function createDataSource(): DataSource[] {
|
||||
return Array(100)
|
||||
.fill(1)
|
||||
.map((_item, index) => {
|
||||
return {
|
||||
name: `Name${index}`,
|
||||
age: getRandomInteger(30, 20),
|
||||
address: '中国'
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function getDataSource() {
|
||||
startLoading();
|
||||
setTimeout(() => {
|
||||
dataSource.value = createDataSource();
|
||||
endLoading();
|
||||
setEmpty(!dataSource.value.length);
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
function getEmptyDataSource() {
|
||||
startLoading();
|
||||
setTimeout(() => {
|
||||
dataSource.value = [];
|
||||
endLoading();
|
||||
setEmpty(!dataSource.value.length);
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getDataSource();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
@ -1,136 +0,0 @@
|
||||
<template>
|
||||
<n-grid :x-gap="16" :y-gap="16" :item-responsive="true">
|
||||
<n-grid-item span="0:24 640:24 1024:8">
|
||||
<n-card title="时间线" :bordered="false" class="rounded-16px shadow-sm">
|
||||
<n-timeline>
|
||||
<n-timeline-item v-for="item in timelines" :key="item.type" v-bind="item" />
|
||||
</n-timeline>
|
||||
</n-card>
|
||||
</n-grid-item>
|
||||
<n-grid-item span="0:24 640:24 1024:16">
|
||||
<n-card title="表格" :bordered="false" class="rounded-16px shadow-sm">
|
||||
<n-data-table size="small" :columns="columns" :data="tableData" />
|
||||
</n-card>
|
||||
</n-grid-item>
|
||||
</n-grid>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { h } from 'vue';
|
||||
import { NTag } from 'naive-ui';
|
||||
|
||||
defineOptions({ name: 'DashboardAnalysisBottomPart' });
|
||||
|
||||
interface TimelineData {
|
||||
type: 'default' | 'info' | 'success' | 'warning' | 'error';
|
||||
title: string;
|
||||
content: string;
|
||||
time: string;
|
||||
}
|
||||
|
||||
interface TableData {
|
||||
key: number;
|
||||
name: string;
|
||||
age: number;
|
||||
address: string;
|
||||
tags: string[];
|
||||
}
|
||||
|
||||
const timelines: TimelineData[] = [
|
||||
{ type: 'default', title: '啊', content: '', time: '2021-10-10 20:46' },
|
||||
{ type: 'success', title: '成功', content: '哪里成功', time: '2021-10-10 20:46' },
|
||||
{ type: 'error', title: '错误', content: '哪里错误', time: '2021-10-10 20:46' },
|
||||
{ type: 'warning', title: '警告', content: '哪里警告', time: '2021-10-10 20:46' },
|
||||
{ type: 'info', title: '信息', content: '是的', time: '2021-10-10 20:46' }
|
||||
];
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: 'Name',
|
||||
key: 'name'
|
||||
},
|
||||
{
|
||||
title: 'Age',
|
||||
key: 'age'
|
||||
},
|
||||
{
|
||||
title: 'Address',
|
||||
key: 'address'
|
||||
},
|
||||
{
|
||||
title: 'Tags',
|
||||
key: 'tags',
|
||||
render(row: TableData) {
|
||||
const tags = row.tags.map(tagKey => {
|
||||
return h(
|
||||
NTag,
|
||||
{
|
||||
style: {
|
||||
marginRight: '6px'
|
||||
},
|
||||
type: 'info'
|
||||
},
|
||||
{
|
||||
default: () => tagKey
|
||||
}
|
||||
);
|
||||
});
|
||||
return tags;
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
const tableData: TableData[] = [
|
||||
{
|
||||
key: 0,
|
||||
name: 'John Brown',
|
||||
age: 32,
|
||||
address: 'New York No. 1 Lake Park',
|
||||
tags: ['nice', 'developer']
|
||||
},
|
||||
{
|
||||
key: 1,
|
||||
name: 'Jim Green',
|
||||
age: 42,
|
||||
address: 'London No. 1 Lake Park',
|
||||
tags: ['wow']
|
||||
},
|
||||
{
|
||||
key: 2,
|
||||
name: 'Joe Black',
|
||||
age: 32,
|
||||
address: 'Sidney No. 1 Lake Park',
|
||||
tags: ['cool', 'teacher']
|
||||
},
|
||||
{
|
||||
key: 3,
|
||||
name: 'Soybean',
|
||||
age: 25,
|
||||
address: 'China Shenzhen',
|
||||
tags: ['handsome', 'programmer']
|
||||
},
|
||||
{
|
||||
key: 4,
|
||||
name: 'John Brown',
|
||||
age: 32,
|
||||
address: 'New York No. 1 Lake Park',
|
||||
tags: ['nice', 'developer']
|
||||
},
|
||||
{
|
||||
key: 5,
|
||||
name: 'Jim Green',
|
||||
age: 42,
|
||||
address: 'London No. 1 Lake Park',
|
||||
tags: ['wow']
|
||||
},
|
||||
{
|
||||
key: 6,
|
||||
name: 'Joe Black',
|
||||
age: 32,
|
||||
address: 'Sidney No. 1 Lake Park',
|
||||
tags: ['cool', 'teacher']
|
||||
}
|
||||
];
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
@ -1,25 +0,0 @@
|
||||
<template>
|
||||
<div class="p-16px rounded-16px text-white" :style="{ backgroundImage: gradientStyle }">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
|
||||
interface Props {
|
||||
/** 渐变开始的颜色 */
|
||||
startColor?: string;
|
||||
/** 渐变结束的颜色 */
|
||||
endColor?: string;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
startColor: '#56cdf3',
|
||||
endColor: '#719de3'
|
||||
});
|
||||
|
||||
const gradientStyle = computed(() => `linear-gradient(to bottom right, ${props.startColor}, ${props.endColor})`);
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
@ -1,3 +0,0 @@
|
||||
import GradientBg from './GradientBg.vue';
|
||||
|
||||
export { GradientBg };
|
@ -1,70 +0,0 @@
|
||||
<template>
|
||||
<n-grid cols="s:1 m:2 l:4" responsive="screen" :x-gap="16" :y-gap="16">
|
||||
<n-grid-item v-for="item in cardData" :key="item.id">
|
||||
<gradient-bg class="h-100px" :start-color="item.colors[0]" :end-color="item.colors[1]">
|
||||
<h3 class="text-16px">{{ item.title }}</h3>
|
||||
<div class="flex justify-between pt-12px">
|
||||
<svg-icon :icon="item.icon" class="text-32px" />
|
||||
<count-to
|
||||
:prefix="item.unit"
|
||||
:start-value="1"
|
||||
:end-value="item.value"
|
||||
class="text-30px text-white dark:text-dark"
|
||||
/>
|
||||
</div>
|
||||
</gradient-bg>
|
||||
</n-grid-item>
|
||||
</n-grid>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { GradientBg } from './components';
|
||||
|
||||
defineOptions({ name: 'DashboardAnalysisDataCard' });
|
||||
|
||||
interface CardData {
|
||||
id: string;
|
||||
title: string;
|
||||
value: number;
|
||||
unit: string;
|
||||
colors: [string, string];
|
||||
icon: string;
|
||||
}
|
||||
|
||||
const cardData: CardData[] = [
|
||||
{
|
||||
id: 'visit',
|
||||
title: '访问量',
|
||||
value: 1000000,
|
||||
unit: '',
|
||||
colors: ['#ec4786', '#b955a4'],
|
||||
icon: 'ant-design:bar-chart-outlined'
|
||||
},
|
||||
{
|
||||
id: 'amount',
|
||||
title: '成交额',
|
||||
value: 234567.89,
|
||||
unit: '$',
|
||||
colors: ['#865ec0', '#5144b4'],
|
||||
icon: 'ant-design:money-collect-outlined'
|
||||
},
|
||||
{
|
||||
id: 'download',
|
||||
title: '下载数',
|
||||
value: 666666,
|
||||
unit: '',
|
||||
colors: ['#56cdf3', '#719de3'],
|
||||
icon: 'carbon:document-download'
|
||||
},
|
||||
{
|
||||
id: 'trade',
|
||||
title: '成交数',
|
||||
value: 999999,
|
||||
unit: '',
|
||||
colors: ['#fcbc25', '#f68057'],
|
||||
icon: 'ant-design:trademark-circle-outlined'
|
||||
}
|
||||
];
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
@ -1,184 +0,0 @@
|
||||
<template>
|
||||
<n-grid :x-gap="16" :y-gap="16" :item-responsive="true">
|
||||
<n-grid-item span="0:24 640:24 1024:16">
|
||||
<n-card :bordered="false" class="rounded-16px shadow-sm">
|
||||
<div class="flex w-full h-360px">
|
||||
<div class="w-200px h-full py-12px">
|
||||
<h3 class="text-16px font-bold">Dashboard</h3>
|
||||
<p class="text-[#aaa]">Overview Of Lasted Month</p>
|
||||
<h3 class="pt-36px text-24px font-bold">
|
||||
<count-to prefix="$" :start-value="0" :end-value="7754" />
|
||||
</h3>
|
||||
<p class="text-[#aaa]">Current Month Earnings</p>
|
||||
<h3 class="pt-36px text-24px font-bold">
|
||||
<count-to :start-value="0" :end-value="1234" />
|
||||
</h3>
|
||||
<p class="text-[#aaa]">Current Month Sales</p>
|
||||
<n-button class="mt-24px" type="primary">Last Month Summary</n-button>
|
||||
</div>
|
||||
<div class="flex-1-hidden h-full">
|
||||
<div ref="lineRef" class="wh-full"></div>
|
||||
</div>
|
||||
</div>
|
||||
</n-card>
|
||||
</n-grid-item>
|
||||
<n-grid-item span="0:24 640:24 1024:8">
|
||||
<n-card :bordered="false" class="rounded-16px shadow-sm">
|
||||
<div ref="pieRef" class="w-full h-360px"></div>
|
||||
</n-card>
|
||||
</n-grid-item>
|
||||
</n-grid>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import type { Ref } from 'vue';
|
||||
import { type ECOption, useEcharts } from '@/composables';
|
||||
|
||||
defineOptions({ name: 'DashboardAnalysisTopCard' });
|
||||
|
||||
const lineOptions = ref<ECOption>({
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'cross',
|
||||
label: {
|
||||
backgroundColor: '#6a7985'
|
||||
}
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
data: ['下载量', '注册数']
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: ['06:00', '08:00', '10:00', '12:00', '14:00', '16:00', '18:00', '20:00', '22:00', '24:00']
|
||||
}
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value'
|
||||
}
|
||||
],
|
||||
series: [
|
||||
{
|
||||
color: '#8e9dff',
|
||||
name: '下载量',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
stack: 'Total',
|
||||
areaStyle: {
|
||||
color: {
|
||||
type: 'linear',
|
||||
x: 0,
|
||||
y: 0,
|
||||
x2: 0,
|
||||
y2: 1,
|
||||
colorStops: [
|
||||
{
|
||||
offset: 0.25,
|
||||
color: '#8e9dff'
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: '#fff'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
emphasis: {
|
||||
focus: 'series'
|
||||
},
|
||||
data: [4623, 6145, 6268, 6411, 1890, 4251, 2978, 3880, 3606, 4311]
|
||||
},
|
||||
{
|
||||
color: '#26deca',
|
||||
name: '注册数',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
stack: 'Total',
|
||||
areaStyle: {
|
||||
color: {
|
||||
type: 'linear',
|
||||
x: 0,
|
||||
y: 0,
|
||||
x2: 0,
|
||||
y2: 1,
|
||||
colorStops: [
|
||||
{
|
||||
offset: 0.25,
|
||||
color: '#26deca'
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: '#fff'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
emphasis: {
|
||||
focus: 'series'
|
||||
},
|
||||
data: [2208, 2016, 2916, 4512, 8281, 2008, 1963, 2367, 2956, 678]
|
||||
}
|
||||
]
|
||||
}) as Ref<ECOption>;
|
||||
const { domRef: lineRef } = useEcharts(lineOptions);
|
||||
|
||||
const pieOptions = ref<ECOption>({
|
||||
tooltip: {
|
||||
trigger: 'item'
|
||||
},
|
||||
legend: {
|
||||
bottom: '1%',
|
||||
left: 'center',
|
||||
itemStyle: {
|
||||
borderWidth: 0
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
color: ['#5da8ff', '#8e9dff', '#fedc69', '#26deca'],
|
||||
name: '时间安排',
|
||||
type: 'pie',
|
||||
radius: ['45%', '75%'],
|
||||
avoidLabelOverlap: false,
|
||||
itemStyle: {
|
||||
borderRadius: 10,
|
||||
borderColor: '#fff',
|
||||
borderWidth: 1
|
||||
},
|
||||
label: {
|
||||
show: false,
|
||||
position: 'center'
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
fontSize: '12'
|
||||
}
|
||||
},
|
||||
labelLine: {
|
||||
show: false
|
||||
},
|
||||
data: [
|
||||
{ value: 20, name: '学习' },
|
||||
{ value: 10, name: '娱乐' },
|
||||
{ value: 30, name: '工作' },
|
||||
{ value: 40, name: '休息' }
|
||||
]
|
||||
}
|
||||
]
|
||||
}) as Ref<ECOption>;
|
||||
const { domRef: pieRef } = useEcharts(pieOptions);
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
@ -1,5 +0,0 @@
|
||||
import TopChart from './TopChart/index.vue';
|
||||
import DataCard from './DataCard/index.vue';
|
||||
import BottomPart from './BottomPart/index.vue';
|
||||
|
||||
export { TopChart, DataCard, BottomPart };
|
@ -1,13 +0,0 @@
|
||||
<template>
|
||||
<n-space :vertical="true" :size="16">
|
||||
<top-chart />
|
||||
<data-card />
|
||||
<bottom-part />
|
||||
</n-space>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { BottomPart, DataCard, TopChart } from './components';
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
@ -1,50 +0,0 @@
|
||||
<template>
|
||||
<n-card :bordered="false" class="rounded-16px shadow-sm">
|
||||
<div class="flex-y-center justify-between">
|
||||
<div class="flex-y-center">
|
||||
<icon-local-avatar class="text-70px" />
|
||||
<div class="pl-12px">
|
||||
<h3 class="text-18px font-semibold">早安,{{ auth.userInfo.userName }}, 今天又是充满活力的一天!</h3>
|
||||
<p class="leading-30px text-[#999]">今日多云转晴,20℃ - 25℃!</p>
|
||||
</div>
|
||||
</div>
|
||||
<n-space :size="24" :wrap="false">
|
||||
<n-statistic v-for="item in statisticData" :key="item.id" class="whitespace-nowrap" v-bind="item"></n-statistic>
|
||||
</n-space>
|
||||
</div>
|
||||
</n-card>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useAuthStore } from '@/store';
|
||||
|
||||
defineOptions({ name: 'DashboardWorkbenchHeader' });
|
||||
|
||||
const auth = useAuthStore();
|
||||
|
||||
interface StatisticData {
|
||||
id: number;
|
||||
label: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
const statisticData: StatisticData[] = [
|
||||
{
|
||||
id: 0,
|
||||
label: '项目数',
|
||||
value: '25'
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
label: '待办',
|
||||
value: '4/16'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
label: '消息',
|
||||
value: '12'
|
||||
}
|
||||
];
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
@ -1,25 +0,0 @@
|
||||
<template>
|
||||
<div
|
||||
class="flex-col-center p-12px border-1px border-[#efeff5] dark:border-[#ffffff17] rounded-4px hover:shadow-sm cursor-pointer"
|
||||
>
|
||||
<svg-icon :icon="icon" :style="{ color: iconColor }" class="text-30px" />
|
||||
<p class="py-8px text-16px">{{ label }}</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
defineOptions({ name: 'DashboardWorkbenchMainShortcutsCard' });
|
||||
|
||||
interface Props {
|
||||
/** 快捷操作名称 */
|
||||
label: string;
|
||||
/** 图标 */
|
||||
icon: string;
|
||||
/** 图标颜色 */
|
||||
iconColor: string;
|
||||
}
|
||||
|
||||
defineProps<Props>();
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
@ -1,42 +0,0 @@
|
||||
<template>
|
||||
<div
|
||||
class="p-4px border-1px border-[#efeff5] dark:border-[#ffffff17] rounded-4px hover:shadow-sm cursor-pointer"
|
||||
@click="handleOpenSite"
|
||||
>
|
||||
<header class="flex-y-center">
|
||||
<svg-icon :icon="icon" :style="{ color: iconColor }" class="text-30px" />
|
||||
<h3 class="pl-12px text-18px font-semibold">{{ name }}</h3>
|
||||
</header>
|
||||
<p class="py-8px h-56px text-[#999]">{{ description }}</p>
|
||||
<div class="flex justify-end">
|
||||
<span>{{ author }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
defineOptions({ name: 'DashboardWorkbenchMainTechnologyCard' });
|
||||
|
||||
interface Props {
|
||||
/** 技术名称 */
|
||||
name: string;
|
||||
/** 技术描述 */
|
||||
description: string;
|
||||
/** 技术作者 */
|
||||
author: string;
|
||||
/** 技术官网 */
|
||||
site: string;
|
||||
/** 技术图标 */
|
||||
icon: string;
|
||||
/** 图标颜色 */
|
||||
iconColor?: string;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
|
||||
function handleOpenSite() {
|
||||
window.open(props.site, '_blank');
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
@ -1,4 +0,0 @@
|
||||
import TechnologyCard from './TechnologyCard.vue';
|
||||
import ShortcutsCard from './ShortcutsCard.vue';
|
||||
|
||||
export { TechnologyCard, ShortcutsCard };
|
@ -1,144 +0,0 @@
|
||||
<template>
|
||||
<n-grid :item-responsive="true" :x-gap="16" :y-gap="16">
|
||||
<n-grid-item span="0:24 640:24 1024:16">
|
||||
<n-space :vertical="true" :size="16">
|
||||
<n-card title="项目主要技术栈" :bordered="false" size="small" class="shadow-sm rounded-16px">
|
||||
<template #header-extra>
|
||||
<a class="text-primary" href="javascript:;">更多技术栈</a>
|
||||
</template>
|
||||
<n-grid :item-responsive="true" responsive="screen" cols="m:2 l:3" :x-gap="8" :y-gap="8">
|
||||
<n-grid-item v-for="item in technology" :key="item.id">
|
||||
<technology-card v-bind="item" />
|
||||
</n-grid-item>
|
||||
</n-grid>
|
||||
</n-card>
|
||||
<n-card title="动态" :bordered="false" size="small" class="shadow-sm rounded-16px">
|
||||
<template #header-extra>
|
||||
<a class="text-primary" href="javascript:;">更多动态</a>
|
||||
</template>
|
||||
<n-list>
|
||||
<n-list-item v-for="item in activity" :key="item.id">
|
||||
<template #prefix>
|
||||
<icon-local-avatar class="text-48px" />
|
||||
</template>
|
||||
<n-thing :title="item.content" :description="item.time" />
|
||||
</n-list-item>
|
||||
</n-list>
|
||||
</n-card>
|
||||
</n-space>
|
||||
</n-grid-item>
|
||||
<n-grid-item span="0:24 640:24 1024:8">
|
||||
<n-space :vertical="true" :size="16">
|
||||
<n-card title="快捷操作" :bordered="false" size="small" class="shadow-sm rounded-16px">
|
||||
<n-grid :item-responsive="true" responsive="screen" cols="m:2 l:3" :x-gap="8" :y-gap="8">
|
||||
<n-grid-item v-for="item in shortcuts" :key="item.id">
|
||||
<shortcuts-card v-bind="item" />
|
||||
</n-grid-item>
|
||||
</n-grid>
|
||||
</n-card>
|
||||
<n-card title="创意" :bordered="false" size="small" class="shadow-sm rounded-16px">
|
||||
<icon-local-banner class="text-400px text-primary" />
|
||||
</n-card>
|
||||
</n-space>
|
||||
</n-grid-item>
|
||||
</n-grid>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ShortcutsCard, TechnologyCard } from './components';
|
||||
|
||||
defineOptions({ name: 'DashboardWorkbenchMain' });
|
||||
|
||||
interface Technology {
|
||||
id: number;
|
||||
name: string;
|
||||
description: string;
|
||||
author: string;
|
||||
site: string;
|
||||
icon: string;
|
||||
iconColor?: string;
|
||||
}
|
||||
|
||||
const technology: Technology[] = [
|
||||
{
|
||||
id: 0,
|
||||
name: 'Vue',
|
||||
description: '一套用于构建用户界面的渐进式框架',
|
||||
author: '尤雨溪 - Evan You',
|
||||
site: 'https://v3.cn.vuejs.org/',
|
||||
icon: 'logos:vue'
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
name: 'TypeScript',
|
||||
description: 'JavaScript类型的超集,它可以编译成纯JavaScript',
|
||||
author: '微软 - Microsoft',
|
||||
site: 'https://www.typescriptlang.org/',
|
||||
icon: 'logos:typescript-icon'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'Vite',
|
||||
description: '下一代前端开发与构建工具',
|
||||
author: '尤雨溪 - Evan You',
|
||||
site: 'https://vitejs.cn/',
|
||||
icon: 'logos:vitejs'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: 'NaiveUI',
|
||||
description: '一个 Vue 3 组件库',
|
||||
author: '图森未来 - TuSimple',
|
||||
site: 'https://www.naiveui.com/zh-CN/os-theme',
|
||||
icon: 'logos:naiveui'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: 'UnoCSS',
|
||||
description: '下一代实用优先的CSS框架',
|
||||
author: 'Anthony Fu',
|
||||
site: 'https://uno.antfu.me/?s=',
|
||||
icon: 'logos:unocss'
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: 'Pinia',
|
||||
description: 'vue状态管理框架,支持vue2、vue3',
|
||||
author: 'Posva',
|
||||
site: 'https://pinia.esm.dev/',
|
||||
icon: 'noto:pineapple'
|
||||
}
|
||||
];
|
||||
|
||||
interface Activity {
|
||||
id: number;
|
||||
content: string;
|
||||
time: string;
|
||||
}
|
||||
|
||||
const activity: Activity[] = [
|
||||
{ id: 4, content: 'Soybean 刚才把工作台页面随便写了一些,凑合能看了!', time: '2021-11-07 22:45:32' },
|
||||
{ id: 3, content: 'Soybean 正在忙于为soybean-admin写项目说明文档!', time: '2021-11-03 20:33:31' },
|
||||
{ id: 2, content: 'Soybean 准备为soybean-admin 1.0的发布做充分的准备工作!', time: '2021-10-31 22:43:12' },
|
||||
{ id: 1, content: '@yanbowe 向soybean-admin提交了一个bug,多标签栏不会自适应。', time: '2021-10-27 10:24:54' },
|
||||
{ id: 0, content: 'Soybean 在2021年5月28日创建了开源项目soybean-admin!', time: '2021-05-28 22:22:22' }
|
||||
];
|
||||
|
||||
interface Shortcuts {
|
||||
id: number;
|
||||
label: string;
|
||||
icon: string;
|
||||
iconColor: string;
|
||||
}
|
||||
|
||||
const shortcuts: Shortcuts[] = [
|
||||
{ id: 0, label: '主控台', icon: 'mdi:desktop-mac-dashboard', iconColor: '#409eff' },
|
||||
{ id: 1, label: '系统管理', icon: 'ic:outline-settings', iconColor: '#7238d1' },
|
||||
{ id: 2, label: '权限管理', icon: 'mdi:family-tree', iconColor: '#f56c6c' },
|
||||
{ id: 3, label: '组件', icon: 'fluent:app-store-24-filled', iconColor: '#19a2f1' },
|
||||
{ id: 4, label: '表格', icon: 'mdi:table-large', iconColor: '#fab251' },
|
||||
{ id: 5, label: '图表', icon: 'mdi:chart-areaspline', iconColor: '#8aca6b' }
|
||||
];
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
@ -1,4 +0,0 @@
|
||||
import WorkbenchHeader from './WorkbenchHeader/index.vue';
|
||||
import WorkbenchMain from './WorkbenchMain/index.vue';
|
||||
|
||||
export { WorkbenchHeader, WorkbenchMain };
|
@ -1,12 +0,0 @@
|
||||
<template>
|
||||
<n-space :vertical="true" :size="16">
|
||||
<workbench-header />
|
||||
<workbench-main />
|
||||
</n-space>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { WorkbenchHeader, WorkbenchMain } from './components';
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
@ -1,13 +0,0 @@
|
||||
<template>
|
||||
<div class="h-full">
|
||||
<iframe class="wh-full" :src="src"></iframe>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
|
||||
const src = ref('https://www.naiveui.com/zh-CN/os-theme/docs/introduction');
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
@ -1,7 +0,0 @@
|
||||
<template>
|
||||
<div></div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<style scoped></style>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user