mirror of
https://github.com/soybeanjs/soybean-admin.git
synced 2025-12-25 05:50:18 +08:00
Compare commits
138 Commits
v0.9.9
...
thin-v0.10
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
685e0b5030 | ||
|
|
ead48f4502 | ||
|
|
305d95672a | ||
|
|
8a792c7d63 | ||
|
|
93ed5ad085 | ||
|
|
41f23386b2 | ||
|
|
c91644b829 | ||
|
|
073fd16bd7 | ||
|
|
f92ee770e0 | ||
|
|
1e6d52357e | ||
|
|
751ded44f3 | ||
|
|
8567f3e34e | ||
|
|
83f2514403 | ||
|
|
ad6ac7222c | ||
|
|
3ae1952624 | ||
|
|
3db549af40 | ||
|
|
94179ae552 | ||
|
|
7f35e87ed8 | ||
|
|
00da0009ef | ||
|
|
cffc30afa3 | ||
|
|
24cf1d9284 | ||
|
|
9296e6987d | ||
|
|
809fa85706 | ||
|
|
b3ae7605d3 | ||
|
|
864ec4737d | ||
|
|
854d0bcf20 | ||
|
|
458e387b68 | ||
|
|
56c770c49d | ||
|
|
946447394d | ||
|
|
44ba3273cb | ||
|
|
0f7b9d5e2b | ||
|
|
8a3f66db7b | ||
|
|
0eaa327d47 | ||
|
|
08e0cf5ad5 | ||
|
|
d7aea9d11c | ||
|
|
135ce77288 | ||
|
|
19141a73d2 | ||
|
|
9d1051b0bd | ||
|
|
c46a5920e5 | ||
|
|
43ac23f113 | ||
|
|
13f6cd8ef4 | ||
|
|
0e6d289128 | ||
|
|
bba68bff29 | ||
|
|
6e0cce4d49 | ||
|
|
d3ebe95076 | ||
|
|
cbda4a38a3 | ||
|
|
3318041b92 | ||
|
|
af53ec7625 | ||
|
|
de2829fde7 | ||
|
|
c1bee4046c | ||
|
|
473095b01b | ||
|
|
e6abf93457 | ||
|
|
882f281482 | ||
|
|
0b2f68ac04 | ||
|
|
2ca2b766f8 | ||
|
|
da611fb10b | ||
|
|
eb8e49e23c | ||
|
|
0907d38c06 | ||
|
|
2a9b725c6a | ||
|
|
8f24a94ed3 | ||
|
|
4eefc95baa | ||
|
|
1681c34a52 | ||
|
|
47ab0184b7 | ||
|
|
58591f660a | ||
|
|
3c7e1cf442 | ||
|
|
055d4cce33 | ||
|
|
a3dfe61a7b | ||
|
|
f9d47c081f | ||
|
|
ff5bf62989 | ||
|
|
1f6d079644 | ||
|
|
5c085a1986 | ||
|
|
9a23817473 | ||
|
|
56ea8937f6 | ||
|
|
4f51263501 | ||
|
|
bb2eab60f4 | ||
|
|
44e4c04811 | ||
|
|
b5839eab26 | ||
|
|
780ac75bf6 | ||
|
|
a252138594 | ||
|
|
270a055072 | ||
|
|
08e194efe9 | ||
|
|
5f6caab338 | ||
|
|
5aaa318142 | ||
|
|
cebbef680f | ||
|
|
0abde46ef4 | ||
|
|
f2b518ed26 | ||
|
|
c6207f35e1 | ||
|
|
ee8fa04814 | ||
|
|
7b746fa053 | ||
|
|
b7fea53107 | ||
|
|
f89f3e6a38 | ||
|
|
a0da2f6e16 | ||
|
|
3b5380e0d1 | ||
|
|
35276bfe41 | ||
|
|
215c1ecbd9 | ||
|
|
1698b21d7a | ||
|
|
ca1e66be47 | ||
|
|
22bf2823e8 | ||
|
|
32e98f1b3a | ||
|
|
c1c4335ce7 | ||
|
|
6c50662280 | ||
|
|
f3a1707b94 | ||
|
|
6ea755f2a8 | ||
|
|
a989b44a15 | ||
|
|
40f8587fd6 | ||
|
|
9f5638f16d | ||
|
|
9b19f96ff6 | ||
|
|
15da557892 | ||
|
|
2d23c9a2e6 | ||
|
|
ab49afd3db | ||
|
|
86a370fd69 | ||
|
|
36fc74ce07 | ||
|
|
8da8843fd0 | ||
|
|
f68285fbe5 | ||
|
|
85b8ef8f88 | ||
|
|
3d48aa8bbe | ||
|
|
a765da6e28 | ||
|
|
c57640acd0 | ||
|
|
c264216053 | ||
|
|
9d3c732993 | ||
|
|
34f023c4b1 | ||
|
|
5a4f842774 | ||
|
|
bae1767141 | ||
|
|
5957833a4f | ||
|
|
397092c21f | ||
|
|
f309003e67 | ||
|
|
eaf3678758 | ||
|
|
f2e82da7c8 | ||
|
|
211ae1f905 | ||
|
|
db629593c6 | ||
|
|
80d58cce2b | ||
|
|
a0f55aca69 | ||
|
|
d203a3586c | ||
|
|
f74a6424d0 | ||
|
|
209ef3d890 | ||
|
|
b549b32cbb | ||
|
|
54e2cb51cf | ||
|
|
42e6de395f |
10
.env
10
.env
@@ -10,11 +10,11 @@ VITE_APP_DESC=SoybeanAdmin是一个中后台管理系统模版
|
||||
VITE_AUTH_ROUTE_MODE=static
|
||||
|
||||
# 路由首页(根路由重定向), 用于static模式的权限路由,dynamic模式取决于后端返回的路由首页
|
||||
VITE_ROUTE_HOME_PATH=/dashboard/analysis
|
||||
VITE_ROUTE_HOME_PATH=/multi-menu/first/second
|
||||
|
||||
# iconify图标作为组件的前缀
|
||||
VITE_ICON_PREFFIX=icon
|
||||
VITE_ICON_PREFIX=icon
|
||||
|
||||
# 本地SVG图标作为组件的前缀, 请注意一定要包含 VITE_ICON_PREFFIX
|
||||
# 格式 {VITE_ICON_PREFFIX}-{本地图标集合名称}
|
||||
VITE_ICON_LOCAL_PREFFIX=icon-local
|
||||
# 本地SVG图标作为组件的前缀, 请注意一定要包含 VITE_ICON_PREFIX
|
||||
# 格式 {VITE_ICON_PREFIX}-{本地图标集合名称}
|
||||
VITE_ICON_LOCAL_PREFIX=icon-local
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
VITE_HTTP_PROXY=Y
|
||||
VITE_SOYBEAN_ROUTE_PLUGIN=Y
|
||||
|
||||
@@ -1,8 +1 @@
|
||||
VITE_VISUALIZER=N
|
||||
|
||||
VITE_COMPRESS=N
|
||||
|
||||
# gzip | brotliCompress | deflate | deflateRaw
|
||||
VITE_COMPRESS_TYPE=gzip
|
||||
|
||||
VITE_PWA=N
|
||||
VITE_PROD_MOCK=Y
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
!.env-config.ts
|
||||
components.d.ts
|
||||
router-page.d.ts
|
||||
*.svg
|
||||
|
||||
|
||||
@@ -10,7 +10,8 @@ module.exports = {
|
||||
{
|
||||
files: ['*.vue'],
|
||||
rules: {
|
||||
'no-undef': 'off' // use tsc to check the ts code of the vue
|
||||
'no-undef': 'off', // use tsc to check the ts code of the vue
|
||||
'vue/no-setup-props-destructure': 'off' // wait to fix this rule
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
16
.github/workflows/release.yml
vendored
16
.github/workflows/release.yml
vendored
@@ -1,27 +1,25 @@
|
||||
name: Release
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*.**"
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
|
||||
jobs:
|
||||
release:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16.x
|
||||
|
||||
- name: Create github releases
|
||||
run: npx changelogithub
|
||||
- run: npx githublogen
|
||||
env:
|
||||
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -34,3 +34,4 @@ stats.html
|
||||
/src/typings/components.d.ts
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
pnpm-lock.yaml
|
||||
|
||||
2
.npmrc
2
.npmrc
@@ -1,4 +1,2 @@
|
||||
registry=https://registry.npmmirror.com/
|
||||
shamefully-hoist=true
|
||||
strict-peer-dependencies=false
|
||||
auto-install-peers=true
|
||||
|
||||
14
.vscode/extensions.json
vendored
14
.vscode/extensions.json
vendored
@@ -1,27 +1,19 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"afzalsayed96.icones",
|
||||
"antfu.iconify",
|
||||
"antfu.unocss",
|
||||
"christian-kohler.path-intellisense",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"eamodio.gitlens",
|
||||
"editorconfig.editorconfig",
|
||||
"esbenp.prettier-vscode",
|
||||
"formulahendry.auto-close-tag",
|
||||
"formulahendry.auto-complete-tag",
|
||||
"formulahendry.auto-close-tag",
|
||||
"formulahendry.auto-rename-tag",
|
||||
"kisstkondoros.vscode-gutter-preview",
|
||||
"lokalise.i18n-ally",
|
||||
"mariusalchimavicius.json-to-ts",
|
||||
"mhutchie.git-graph",
|
||||
"mikestead.dotenv",
|
||||
"naumovs.color-highlight",
|
||||
"pkief.material-icon-theme",
|
||||
"sdras.vue-vscode-snippets",
|
||||
"streetsidesoftware.code-spell-checker",
|
||||
"vue.volar",
|
||||
"vue.vscode-typescript-vue-plugin",
|
||||
"whtouche.vscode-js-console-utils",
|
||||
"zhuangtongfa.material-theme"
|
||||
"vue.vscode-typescript-vue-plugin"
|
||||
]
|
||||
}
|
||||
|
||||
8
.vscode/launch.json
vendored
8
.vscode/launch.json
vendored
@@ -7,6 +7,14 @@
|
||||
"name": "Vue debugger",
|
||||
"url": "http://localhost:3200",
|
||||
"webRoot": "${workspaceFolder}"
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "TS debugger",
|
||||
"skipFiles": ["<node_internals>/**"],
|
||||
"runtimeArgs": ["--loader", "tsx"],
|
||||
"program": "${relativeFile}"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
121
.vscode/settings.json
vendored
121
.vscode/settings.json
vendored
@@ -1,75 +1,76 @@
|
||||
{
|
||||
"cSpell.ignorePaths": [
|
||||
"package.json",
|
||||
"package-lock.json",
|
||||
"yarn.lock",
|
||||
"pnpm-lock.yaml",
|
||||
"node_modules",
|
||||
"vscode-extension",
|
||||
".git/objects",
|
||||
".vscode",
|
||||
".vscode-insiders",
|
||||
"CHANGELOG.md",
|
||||
"dist",
|
||||
"public",
|
||||
"styles"
|
||||
],
|
||||
"cSpell.words": [
|
||||
"AMAP",
|
||||
"antdesign",
|
||||
"antv",
|
||||
"apacheecharts",
|
||||
"areaspline",
|
||||
"bmapgl",
|
||||
"colord",
|
||||
"echarts",
|
||||
"gitee",
|
||||
"gridicons",
|
||||
"iconify",
|
||||
"jsapi",
|
||||
"naiveui",
|
||||
"Popconfirm",
|
||||
"Posva",
|
||||
"Shenzhen",
|
||||
"Sider",
|
||||
"tauri",
|
||||
"unocss",
|
||||
"unplugin",
|
||||
"vditor",
|
||||
"VERCEL",
|
||||
"Vite",
|
||||
"vitejs",
|
||||
"vuedraggable",
|
||||
"vueuse",
|
||||
"wangeditor",
|
||||
"wechat",
|
||||
"xgplayer",
|
||||
"yanbowe",
|
||||
"ភាសាខ្មែរ"
|
||||
],
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": true
|
||||
},
|
||||
"editor.fontLigatures": true,
|
||||
"editor.formatOnSave": false,
|
||||
"editor.guides.bracketPairs": "active",
|
||||
"editor.quickSuggestions": {
|
||||
"strings": true
|
||||
},
|
||||
"editor.tabSize": 2,
|
||||
"eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact", "vue", "json"],
|
||||
"eslint.validate": ["json"],
|
||||
"files.associations": {
|
||||
"*.env.*": "dotenv"
|
||||
"*.env.*": "dotenv",
|
||||
"*.svg": "html"
|
||||
},
|
||||
"files.eol": "\n",
|
||||
"git.enableSmartCommit": true,
|
||||
"gutterpreview.paths": {
|
||||
"@": "/src",
|
||||
"~@": "/src"
|
||||
},
|
||||
"material-icon-theme.activeIconPack": "angular",
|
||||
"material-icon-theme.files.associations": {},
|
||||
"material-icon-theme.folders.associations": {
|
||||
"src-tauri": "src",
|
||||
"enum": "typescript",
|
||||
"enums": "typescript",
|
||||
"store": "context",
|
||||
"stores": "context",
|
||||
"composable": "hook",
|
||||
"composables": "hook",
|
||||
"directive": "tools",
|
||||
"directives": "tools",
|
||||
"business": "core",
|
||||
"request": "api",
|
||||
"adapter": "middleware"
|
||||
},
|
||||
"path-intellisense.mappings": {
|
||||
"@": "${workspaceFolder}/src",
|
||||
"~@": "${workspaceFolder}/src"
|
||||
},
|
||||
"terminal.integrated.cursorStyle": "line",
|
||||
"terminal.integrated.fontSize": 14,
|
||||
"terminal.integrated.fontWeight": 500,
|
||||
"terminal.integrated.tabs.enabled": true,
|
||||
"workbench.iconTheme": "material-icon-theme",
|
||||
"workbench.colorTheme": "One Dark Pro",
|
||||
"[html]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[json]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[jsonc]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[javascript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[javascriptreact]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[markdown]": {
|
||||
"editor.defaultFormatter": "yzhang.markdown-all-in-one"
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[typescriptreact]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[vue]": {
|
||||
"editor.defaultFormatter": "Vue.volar"
|
||||
"i18n-ally.displayLanguage": "zh-CN",
|
||||
"i18n-ally.enabledParsers": ["ts"],
|
||||
"i18n-ally.enabledFrameworks": ["vue"],
|
||||
"i18n-ally.editor.preferEditor": true,
|
||||
"i18n-ally.keystyle": "nested",
|
||||
"i18n-ally.localesPaths": ["src/locales/lang"],
|
||||
"material-icon-theme.activeIconPack": "vue",
|
||||
"[html][css][less][scss][sass][markdown][yaml][yml][jsonc]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.formatOnSave": true
|
||||
}
|
||||
}
|
||||
|
||||
1393
CHANGELOG.md
1393
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
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
|
||||
61
README.md
61
README.md
@@ -1,5 +1,5 @@
|
||||
<div align="center">
|
||||
<img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybean.svg" style="width: 160px;"/>
|
||||
<img src="./public/favicon.svg" style="width: 160px;"/>
|
||||
<h1>Soybean Admin</h1>
|
||||
</div>
|
||||
|
||||
@@ -19,22 +19,35 @@
|
||||
- **权限路由**:提供前端静态和后端动态两种路由模式,基于 mock 的动态路由能快速实现后端动态路由
|
||||
- **请求函数**:基于 axios 的完善的请求函数封装,提供 Promise 和 hooks 两种请求函数,加入请求结果数据转换的适配器
|
||||
|
||||
## SoybeanJS 工具库
|
||||
|
||||
- [@soybeanjs/cli](https://github.com/soybeanjs/cli): SoybeanJS 命令行工具,包含发布、git 和依赖等相关的实用命令
|
||||
- [@soybeanjs/changelog](https://github.com/soybeanjs/changelog): 根据 git tags 和 commits 生成 changelog [示例](./CHANGELOG.md)
|
||||
- [eslint-config-soybeanjs](https://github.com/soybeanjs/eslint-config): SoybeanJS 的 eslint 预设配置
|
||||
- [@soybeanjs/materials](https://github.com/soybeanjs/materials): SoybeanJS 的物料仓库
|
||||
- [@soybeanjs/vite-plugin-vue-page-route](https://github.com/soybeanjs/vite-plugin-vue-page-route): SoybeanAdmin 的路由插件
|
||||
|
||||
## 基于 SoybeanAdmin 二次开发的项目
|
||||
|
||||
- [electron-mock-admin](https://github.com/lixin59/electron-mock-api): 一个 Mock Api 管理系统,帮助前端开发伙伴快速实现接口的 mock。
|
||||
- [T-Shell](https://github.com/TheBlindM/T-Shell): 是一个可配置命令提示的终端模拟器和 SSH 客户端。
|
||||
|
||||
## 在线预览
|
||||
|
||||
- [Soybean Admin 预览地址](https://soybean.pro/)
|
||||
- [Soybean Admin 预览地址](https://admin.soybeanjs.cn/)
|
||||
|
||||
## 文档
|
||||
|
||||
- [项目文档预览地址](https://docs.soybean.pro)
|
||||
- [项目文档预览地址](https://admin-docs.soybeanjs.cn/)
|
||||
|
||||
## 代码仓库
|
||||
|
||||
- [github](https://github.com/honghuangdc/soybean-admin)
|
||||
- [tauri 版](https://github.com/honghuangdc/soybean-admin/tree/tauri)
|
||||
- [精简版](https://github.com/honghuangdc/soybean-admin/tree/thin)
|
||||
- [gitee](https://gitee.com/honghuangdc/soybean-admin)
|
||||
- [tauri 版](https://gitee.com/honghuangdc/soybean-admin/tree/tauri)
|
||||
- [精简版](https://gitee.com/honghuangdc/soybean-admin/tree/thin)
|
||||
| 仓库 | GitHub 地址 | gitee 镜像 | 预览 |
|
||||
| -------------- | ----------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | --------------------------------------------------------- |
|
||||
| soybean-admin | [GitHub](https://github.com/honghuangdc/soybean-admin) | [gitee](https://gitee.com/honghuangdc/soybean-admin) | [预览](https://admin.soybeanjs.cn/) |
|
||||
| tauri 版 | [tauri 版](https://github.com/honghuangdc/soybean-admin/tree/tauri) | [tauri 版](https://gitee.com/honghuangdc/soybean-admin/tree/tauri) | |
|
||||
| 精简版 | [精简版](https://github.com/honghuangdc/soybean-admin/tree/thin) | [精简版](https://gitee.com/honghuangdc/soybean-admin/tree/thin) | |
|
||||
| 集成 fast-crud | [集成 fast-crud](https://github.com/honghuangdc/soybean-admin/tree/fast-crud) | [集成 fast-crud](https://gitee.com/honghuangdc/soybean-admin/tree/fast-crud) | [预览](http://fast-crud.docmirror.cn/soybean/#/crud/demo) |
|
||||
|
||||
## 更新日志
|
||||
|
||||
@@ -54,13 +67,15 @@
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||
|
||||
@@ -68,6 +83,12 @@
|
||||
|
||||

|
||||
|
||||
<div align="center">
|
||||
<img style="width:380px;margin-right:18px;border:1px solid #dedede;" src="https://s2.loli.net/2023/06/07/A5Nonc9vI6pB1lr.png" />
|
||||
|
||||
<img style="width:380px;border:1px solid #dedede;" src="https://s2.loli.net/2023/06/07/VwBjqEhTke3OxXF.png" />
|
||||
</div>
|
||||
|
||||
## 安装使用
|
||||
|
||||
- 环境配置
|
||||
@@ -102,7 +123,8 @@ pnpm build
|
||||
- Docker 部署 Soybean
|
||||
|
||||
```bash
|
||||
docker run --name soybean -p 80:80 -d soybeanjs/soybean-admin:v0.9.6
|
||||
docker build -t soybean-admin-image -f docker/Dockerfile .
|
||||
docker run -d -p 80:80 soybean-admin-image
|
||||
```
|
||||
|
||||
- 访问 SoybeanAdmin
|
||||
@@ -117,10 +139,7 @@ docker run --name soybean -p 80:80 -d soybeanjs/soybean-admin:v0.9.6
|
||||
|
||||
项目已经内置 Angular 提交规范,直接执行 commit 命令即可生成符合 Angular 提交规范的 commit。
|
||||
|
||||
项目已用 simple-git-hooks 代替了 husky, 旧版本用了 husky,执行 pnpm soy init-git-hooks 进行初始化配置
|
||||
|
||||
## 基于 SoybeanAdmin 二次开发的项目
|
||||
[electron-mock-admin](https://github.com/lixin59/electron-mock-api): 一个 Mock Api 管理系统,帮助前端开发伙伴快速实现接口的mock。
|
||||
项目已用 simple-git-hooks 代替了 husky, 旧版本用了 husky,执行 pnpm soy init-simple-git-hooks 进行初始化配置
|
||||
|
||||
## 浏览器支持
|
||||
|
||||
@@ -138,16 +157,16 @@ docker run --name soybean -p 80:80 -d soybeanjs/soybean-admin:v0.9.6
|
||||
|
||||
## 交流
|
||||
|
||||
`Soybean Admin` 是完全开源免费的项目,在帮助开发者更方便地进行中大型管理系统开发,同时也提供微信和 QQ 交流群(人员已满),使用问题欢迎在群内提问。
|
||||
`Soybean Admin` 是完全开源免费的项目,在帮助开发者更方便地进行中大型管理系统开发,同时也提供微信和 QQ 交流群,使用问题欢迎在群内提问。
|
||||
|
||||
<div style="display:flex;">
|
||||
<div style="padding-right:24px;">
|
||||
<p>微信交流群</p>
|
||||
<img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybeanjs-wechat2.jpeg" style="width:200px" />
|
||||
<p>QQ交流群</p>
|
||||
<img src="https://i.loli.net/2021/11/24/1J6REWXiHomU2kM.jpg" style="width:200px" />
|
||||
</div>
|
||||
<div>
|
||||
<p>添加本人微信,欢迎来技术交流,业务咨询</p>
|
||||
<img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybeanjs.jpeg" style="width:180px" />
|
||||
<img src="https://s2.loli.net/2023/06/07/sVyCUFBvzQ9f5b7.jpg" style="width:200px" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -159,4 +178,4 @@ docker run --name soybean -p 80:80 -d soybeanjs/soybean-admin:v0.9.6
|
||||
|
||||
## License
|
||||
|
||||
[MIT © Soybean-2021](./LICENSE)
|
||||
本项目基于[MIT © Soybean-2021](./LICENSE) 协议,仅供参考学习,商用时请保留作者的版权信息,作者不对软件做担保和负责。
|
||||
|
||||
@@ -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 });
|
||||
};
|
||||
@@ -1,14 +0,0 @@
|
||||
import type { PluginOption } from 'vite';
|
||||
import { createHtmlPlugin } from 'vite-plugin-html';
|
||||
|
||||
export default (viteEnv: ImportMetaEnv): PluginOption[] => {
|
||||
return createHtmlPlugin({
|
||||
minify: true,
|
||||
inject: {
|
||||
data: {
|
||||
appName: viteEnv.VITE_APP_NAME,
|
||||
appTitle: viteEnv.VITE_APP_TITLE
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -2,30 +2,31 @@ 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 VueDevtools from 'vite-plugin-vue-devtools';
|
||||
import pageRoute from '@soybeanjs/vite-plugin-vue-page-route';
|
||||
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(), pageRoute()];
|
||||
const plugins = [
|
||||
vue({
|
||||
script: {
|
||||
defineModel: true
|
||||
}
|
||||
}),
|
||||
vueJsx(),
|
||||
VueDevtools(),
|
||||
...unplugin(viteEnv),
|
||||
unocss(),
|
||||
mock(viteEnv)
|
||||
];
|
||||
|
||||
if (viteEnv.VITE_VISUALIZER === 'Y') {
|
||||
plugins.push(visualizer as PluginOption);
|
||||
}
|
||||
if (viteEnv.VITE_COMPRESS === 'Y') {
|
||||
plugins.push(compress(viteEnv));
|
||||
}
|
||||
if (viteEnv.VITE_PWA === 'Y' || viteEnv.VITE_VERCEL === 'Y') {
|
||||
plugins.push(pwa());
|
||||
if (viteEnv.VITE_SOYBEAN_ROUTE_PLUGIN === 'Y') {
|
||||
plugins.push(pageRoute());
|
||||
}
|
||||
|
||||
return plugins;
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
import { viteMockServe } from 'vite-plugin-mock';
|
||||
|
||||
export default viteMockServe({
|
||||
mockPath: 'mock',
|
||||
injectCode: `
|
||||
import { setupMockServer } from '../mock';
|
||||
setupMockServer();
|
||||
`
|
||||
});
|
||||
export default (viteEnv: ImportMetaEnv) => {
|
||||
const prodMock = viteEnv.VITE_PROD_MOCK === 'Y';
|
||||
|
||||
return viteMockServe({
|
||||
mockPath: 'mock',
|
||||
prodEnabled: prodMock,
|
||||
injectCode: `
|
||||
import { setupMockServer } from '../mock';
|
||||
setupMockServer();
|
||||
`
|
||||
});
|
||||
};
|
||||
|
||||
@@ -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,4 +1,3 @@
|
||||
import VueMacros from 'unplugin-vue-macros/vite';
|
||||
import Icons from 'unplugin-icons/vite';
|
||||
import IconsResolver from 'unplugin-icons/resolver';
|
||||
import Components from 'unplugin-vue-components/vite';
|
||||
@@ -8,16 +7,15 @@ import { createSvgIconsPlugin } from 'vite-plugin-svg-icons';
|
||||
import { getSrcPath } from '../utils';
|
||||
|
||||
export default function unplugin(viteEnv: ImportMetaEnv) {
|
||||
const { VITE_ICON_PREFFIX, VITE_ICON_LOCAL_PREFFIX } = viteEnv;
|
||||
const { VITE_ICON_PREFIX, VITE_ICON_LOCAL_PREFIX } = viteEnv;
|
||||
|
||||
const srcPath = getSrcPath();
|
||||
const localIconPath = `${srcPath}/assets/svg-icon`;
|
||||
|
||||
/** 本地svg图标集合名称 */
|
||||
const collectionName = VITE_ICON_LOCAL_PREFFIX.replace(`${VITE_ICON_PREFFIX}-`, '');
|
||||
const collectionName = VITE_ICON_LOCAL_PREFIX.replace(`${VITE_ICON_PREFIX}-`, '');
|
||||
|
||||
return [
|
||||
VueMacros({}),
|
||||
Icons({
|
||||
compiler: 'vue3',
|
||||
customCollections: {
|
||||
@@ -33,12 +31,12 @@ export default function unplugin(viteEnv: ImportMetaEnv) {
|
||||
types: [{ from: 'vue-router', names: ['RouterLink', 'RouterView'] }],
|
||||
resolvers: [
|
||||
NaiveUiResolver(),
|
||||
IconsResolver({ customCollections: [collectionName], componentPrefix: VITE_ICON_PREFFIX })
|
||||
IconsResolver({ customCollections: [collectionName], componentPrefix: VITE_ICON_PREFIX })
|
||||
]
|
||||
}),
|
||||
createSvgIconsPlugin({
|
||||
iconDirs: [localIconPath],
|
||||
symbolId: `${VITE_ICON_LOCAL_PREFFIX}-[dir]-[name]`,
|
||||
symbolId: `${VITE_ICON_LOCAL_PREFIX}-[dir]-[name]`,
|
||||
inject: 'body-last',
|
||||
customDomId: '__SVG_ICON_LOCAL__'
|
||||
})
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
import { visualizer } from 'rollup-plugin-visualizer';
|
||||
|
||||
export default visualizer({
|
||||
gzipSize: true,
|
||||
brotliSize: true,
|
||||
open: true
|
||||
});
|
||||
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"types": {
|
||||
"feat": { "title": "🚀 Features" },
|
||||
"perf": { "title": "🔥 Performance" },
|
||||
"fix": { "title": "🩹 Fixes" },
|
||||
"refactor": { "title": "💅 Refactors" },
|
||||
"docs": { "title": "📖 Documentation" },
|
||||
"types": { "title": "🌊 Types" },
|
||||
"chore": { "title": "🏡 Chore" },
|
||||
"test": { "title": "🧪 Tests" },
|
||||
"style": { "title": "🎨 Styles" },
|
||||
"ci": { "title": "🤖 CI" }
|
||||
},
|
||||
"scopeMap": {},
|
||||
"titles": {
|
||||
"breakingChanges": "🚨 Breaking Changes"
|
||||
},
|
||||
"contributors": true,
|
||||
"capitalize": true,
|
||||
"group": true
|
||||
}
|
||||
@@ -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,10 +1,15 @@
|
||||
<!-- prettier-ignore -->
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-cmn-Hans">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="Expires" content="0" />
|
||||
<meta http-equiv="Pragma" content="no-cache" />
|
||||
<meta http-equiv="Cache-control" content="no-cache" />
|
||||
<meta http-equiv="Cache" content="no-cache" />
|
||||
<link rel="icon" href="/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title><%= appName %></title>
|
||||
<title>%VITE_APP_NAME%</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app">
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
135
package.json
135
package.json
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "soybean-admin",
|
||||
"version": "0.9.9",
|
||||
"version": "0.10.4",
|
||||
"description": "A fresh and elegant admin template, based on Vue3、Vite3、TypeScript、NaiveUI and UnoCSS. 一个基于Vue3、Vite3、TypeScript、NaiveUI and UnoCSS的清新优雅的中后台模版。",
|
||||
"author": {
|
||||
"name": "Soybean",
|
||||
"email": "honghuangdc@gmail.com",
|
||||
"url": "https://github.com/honghuangdc"
|
||||
"email": "soybeanjs@outlook.com",
|
||||
"url": "https://github.com/soybeanjs"
|
||||
},
|
||||
"license": "MIT",
|
||||
"homepage": "https://github.com/honghuangdc/soybean-admin",
|
||||
@@ -44,97 +44,66 @@
|
||||
"build:vercel": "cross-env VITE_HASH_ROUTE=Y VITE_VERCEL=Y vite build",
|
||||
"preview": "vite preview",
|
||||
"typecheck": "vue-tsc --noEmit --skipLibCheck",
|
||||
"lint": "eslint . --fix --ext .js,.jsx,.mjs,.json,.ts,.tsx,.vue",
|
||||
"format": "soy prettier-format",
|
||||
"lint": "eslint . --fix",
|
||||
"format": "soy prettier-write",
|
||||
"commit": "soy git-commit",
|
||||
"cleanup": "soy cleanup",
|
||||
"update-pkg": "soy update-pkg",
|
||||
"update-pkg": "soy ncu",
|
||||
"tsx": "tsx",
|
||||
"logo": "tsx ./scripts/logo.ts",
|
||||
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s",
|
||||
"release": "standard-version",
|
||||
"prepare": "soy init-git-hooks"
|
||||
"logo": "tsx ./scripts/logo.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@antv/data-set": "^0.11.8",
|
||||
"@antv/g2": "^4.2.9",
|
||||
"@better-scroll/core": "^2.5.0",
|
||||
"@soybeanjs/vue-admin-layout": "^1.1.1",
|
||||
"@soybeanjs/vue-admin-tab": "^1.0.5",
|
||||
"@soybeanjs/vue-materials": "^0.1.8",
|
||||
"@vueuse/core": "^9.13.0",
|
||||
"axios": "0.27.2",
|
||||
"clipboard": "^2.0.11",
|
||||
"colord": "^2.9.3",
|
||||
"crypto-js": "^4.1.1",
|
||||
"dayjs": "^1.11.7",
|
||||
"echarts": "^5.4.1",
|
||||
"form-data": "^4.0.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
"naive-ui": "2.34.3",
|
||||
"pinia": "^2.0.33",
|
||||
"print-js": "^1.6.0",
|
||||
"qs": "^6.11.1",
|
||||
"swiper": "^9.1.0",
|
||||
"ua-parser-js": "^1.0.34",
|
||||
"vditor": "^3.9.0",
|
||||
"vue": "3.2.47",
|
||||
"vue-i18n": "^9.2.2",
|
||||
"vue-router": "^4.1.6",
|
||||
"vuedraggable": "^4.1.0",
|
||||
"wangeditor": "^4.7.15",
|
||||
"xgplayer": "^2.32.2"
|
||||
"@better-scroll/core": "2.5.1",
|
||||
"@soybeanjs/vue-materials": "0.2.0",
|
||||
"@vueuse/core": "10.4.1",
|
||||
"axios": "1.5.0",
|
||||
"clipboard": "2.0.11",
|
||||
"colord": "2.9.3",
|
||||
"crypto-js": "4.1.1",
|
||||
"dayjs": "1.11.10",
|
||||
"form-data": "4.0.0",
|
||||
"lodash-es": "4.17.21",
|
||||
"naive-ui": "2.34.4",
|
||||
"pinia": "2.1.6",
|
||||
"qs": "6.11.2",
|
||||
"ua-parser-js": "1.0.36",
|
||||
"vue": "3.3.4",
|
||||
"vue-i18n": "9.4.1",
|
||||
"vue-router": "4.2.4",
|
||||
"xgplayer": "3.0.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@amap/amap-jsapi-types": "^0.0.13",
|
||||
"@iconify/json": "^2.2.33",
|
||||
"@iconify/vue": "^4.1.0",
|
||||
"@soybeanjs/cli": "^0.1.7",
|
||||
"@soybeanjs/vite-plugin-vue-page-route": "^0.0.5",
|
||||
"@types/bmapgl": "^0.0.5",
|
||||
"@types/crypto-js": "^4.1.1",
|
||||
"@types/node": "18.15.0",
|
||||
"@types/qs": "^6.9.7",
|
||||
"@types/ua-parser-js": "^0.7.36",
|
||||
"@unocss/preset-uno": "^0.50.4",
|
||||
"@unocss/transformer-directives": "^0.50.4",
|
||||
"@unocss/vite": "^0.50.4",
|
||||
"@vitejs/plugin-vue": "^4.0.0",
|
||||
"@vitejs/plugin-vue-jsx": "^3.0.0",
|
||||
"conventional-changelog": "^3.1.25",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^8.36.0",
|
||||
"eslint-config-soybeanjs": "^0.3.1",
|
||||
"lint-staged": "12.5.0",
|
||||
"mockjs": "^1.1.0",
|
||||
"rollup-plugin-visualizer": "^5.9.0",
|
||||
"sass": "^1.59.2",
|
||||
"simple-git-hooks": "^2.8.1",
|
||||
"standard-version": "^9.5.0",
|
||||
"tsx": "^3.12.4",
|
||||
"typescript": "4.9.5",
|
||||
"unplugin-icons": "^0.15.3",
|
||||
"unplugin-vue-components": "0.24.1",
|
||||
"unplugin-vue-macros": "1.6.4",
|
||||
"vite": "^4.1.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.14.4",
|
||||
"vite-plugin-svg-icons": "^2.0.1",
|
||||
"vue-tsc": "^1.2.0"
|
||||
"@iconify/json": "2.2.118",
|
||||
"@iconify/vue": "4.1.1",
|
||||
"@soybeanjs/cli": "0.7.1",
|
||||
"@soybeanjs/vite-plugin-vue-page-route": "0.0.10",
|
||||
"@types/crypto-js": "4.1.2",
|
||||
"@types/node": "20.6.3",
|
||||
"@types/qs": "6.9.8",
|
||||
"@types/ua-parser-js": "0.7.37",
|
||||
"@unocss/preset-uno": "0.56.0",
|
||||
"@unocss/transformer-directives": "0.56.0",
|
||||
"@unocss/vite": "0.56.0",
|
||||
"@vitejs/plugin-vue": "4.3.4",
|
||||
"@vitejs/plugin-vue-jsx": "3.0.2",
|
||||
"cross-env": "7.0.3",
|
||||
"eslint": "8.49.0",
|
||||
"eslint-config-soybeanjs": "0.5.6",
|
||||
"mockjs": "1.1.0",
|
||||
"sass": "1.67.0",
|
||||
"tsx": "3.12.10",
|
||||
"typescript": "5.2.2",
|
||||
"unplugin-icons": "0.17.0",
|
||||
"unplugin-vue-components": "0.25.2",
|
||||
"vite": "4.4.9",
|
||||
"vite-plugin-mock": "2.9.8",
|
||||
"vite-plugin-svg-icons": "2.0.1",
|
||||
"vite-plugin-vue-devtools": "1.0.0-rc.4",
|
||||
"vue-tsc": "1.8.13"
|
||||
},
|
||||
"pnpm": {
|
||||
"patchedDependencies": {
|
||||
"mockjs@1.1.0": "patches/mockjs@1.1.0.patch"
|
||||
}
|
||||
},
|
||||
"simple-git-hooks": {
|
||||
"commit-msg": "pnpm soy git-commit-verify",
|
||||
"pre-commit": "pnpm typecheck && pnpm lint-staged"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,jsx,mjs,json,ts,tsx,vue}": "eslint . --fix"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,56 +5,56 @@ index 35d5b9af3eff34324656879705dcb81470fc9697..3e6a52e0fbfdd39d3aaf1592ffd19ecd
|
||||
@@ -126,17 +126,17 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
/* 1 */
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
- /*
|
||||
|
||||
- /*
|
||||
+ /*
|
||||
## Handler
|
||||
|
||||
|
||||
处理数据模板。
|
||||
-
|
||||
-
|
||||
+
|
||||
* Handler.gen( template, name?, context? )
|
||||
|
||||
|
||||
入口方法。
|
||||
|
||||
|
||||
* Data Template Definition, DTD
|
||||
-
|
||||
-
|
||||
+
|
||||
处理数据模板定义。
|
||||
|
||||
|
||||
* Handler.array( options )
|
||||
@@ -146,7 +146,7 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
* Handler.string( options )
|
||||
* Handler.function( options )
|
||||
* Handler.regexp( options )
|
||||
-
|
||||
-
|
||||
+
|
||||
处理路径(相对和绝对)。
|
||||
|
||||
|
||||
* Handler.getValueByKeyPath( key, options )
|
||||
@@ -177,7 +177,7 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
|
||||
|
||||
Handle.gen(template, name, options)
|
||||
context
|
||||
- currentContext, templateCurrentContext,
|
||||
- currentContext, templateCurrentContext,
|
||||
+ currentContext, templateCurrentContext,
|
||||
path, templatePath
|
||||
root, templateRoot
|
||||
*/
|
||||
@@ -456,7 +456,7 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
phed = Handler.placeholder(ph, options.context.currentContext, options.context.templateCurrentContext, options)
|
||||
|
||||
|
||||
// 只有一个占位符,并且没有其他字符
|
||||
- if (placeholders.length === 1 && ph === result && typeof phed !== typeof result) { //
|
||||
- if (placeholders.length === 1 && ph === result && typeof phed !== typeof result) { //
|
||||
+ if (placeholders.length === 1 && ph === result && typeof phed !== typeof result) { //
|
||||
result = phed
|
||||
break
|
||||
|
||||
|
||||
@@ -627,7 +627,7 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
}
|
||||
// 引用的值已经计算好
|
||||
if (currentContext && (key in currentContext)) return currentContext[key]
|
||||
-
|
||||
-
|
||||
+
|
||||
// 尚未计算,递归引用数据模板中的属性
|
||||
if (templateCurrentContext &&
|
||||
@@ -63,41 +63,41 @@ index 35d5b9af3eff34324656879705dcb81470fc9697..3e6a52e0fbfdd39d3aaf1592ffd19ecd
|
||||
var tpl = Mock.heredoc(function() {
|
||||
/*!
|
||||
{{email}}{{age}}
|
||||
- <!-- Mock {
|
||||
- <!-- Mock {
|
||||
+ <!-- Mock {
|
||||
email: '@EMAIL',
|
||||
age: '@INT(1,100)'
|
||||
} -->
|
||||
*\/
|
||||
})
|
||||
-
|
||||
-
|
||||
+
|
||||
**相关阅读**
|
||||
* [Creating multiline strings in JavaScript](http://stackoverflow.com/questions/805107/creating-multiline-strings-in-javascript)、
|
||||
*/
|
||||
@@ -850,7 +850,7 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
解析数据模板(属性名部分)。
|
||||
|
||||
|
||||
* Parser.parse( name )
|
||||
-
|
||||
-
|
||||
+
|
||||
```json
|
||||
{
|
||||
parameters: [ name, inc, range, decimal ],
|
||||
@@ -922,7 +922,7 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
|
||||
|
||||
/*
|
||||
## Mock.Random
|
||||
-
|
||||
-
|
||||
+
|
||||
工具类,用于生成各种随机数据。
|
||||
*/
|
||||
|
||||
|
||||
@@ -1251,7 +1251,7 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
|
||||
|
||||
替代图片源
|
||||
http://fpoimg.com/
|
||||
- 参考自
|
||||
- 参考自
|
||||
+ 参考自
|
||||
http://rensanning.iteye.com/blog/1933310
|
||||
http://code.tutsplus.com/articles/the-top-8-placeholders-for-web-designers--net-19485
|
||||
@@ -106,16 +106,16 @@ index 35d5b9af3eff34324656879705dcb81470fc9697..3e6a52e0fbfdd39d3aaf1592ffd19ecd
|
||||
var bg_colour = Math.floor(Math.random() * 16777215).toString(16);
|
||||
bg_colour = "#" + ("000000" + bg_colour).slice(-6);
|
||||
document.bgColor = bg_colour;
|
||||
-
|
||||
-
|
||||
+
|
||||
http://martin.ankerl.com/2009/12/09/how-to-create-random-colors-programmatically/
|
||||
Creating random colors is actually more difficult than it seems. The randomness itself is easy, but aesthetically pleasing randomness is more difficult.
|
||||
https://github.com/devongovett/color-generator
|
||||
@@ -1561,7 +1561,7 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
|
||||
|
||||
http://tool.c7sky.com/webcolor
|
||||
网页设计常用色彩搭配表
|
||||
-
|
||||
-
|
||||
+
|
||||
https://github.com/One-com/one-color
|
||||
An OO-based JavaScript color parser/computation toolkit with support for RGB, HSV, HSL, CMYK, and alpha channels.
|
||||
@@ -124,7 +124,7 @@ index 35d5b9af3eff34324656879705dcb81470fc9697..3e6a52e0fbfdd39d3aaf1592ffd19ecd
|
||||
color += letters[Math.floor(Math.random() * 16)]
|
||||
}
|
||||
return color
|
||||
-
|
||||
-
|
||||
+
|
||||
// 随机生成一个无脑的颜色,格式为 '#RRGGBB'。
|
||||
// _brainlessColor()
|
||||
@@ -133,24 +133,24 @@ index 35d5b9af3eff34324656879705dcb81470fc9697..3e6a52e0fbfdd39d3aaf1592ffd19ecd
|
||||
}
|
||||
return result.join(' ')
|
||||
},
|
||||
- //
|
||||
- //
|
||||
+ //
|
||||
cparagraph: function(min, max) {
|
||||
var len = range(3, 7, min, max)
|
||||
var result = []
|
||||
@@ -2282,17 +2282,17 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
随机生成一个 URL。
|
||||
|
||||
|
||||
[URL 规范](http://www.w3.org/Addressing/URL/url-spec.txt)
|
||||
- http Hypertext Transfer Protocol
|
||||
- ftp File Transfer protocol
|
||||
- gopher The Gopher protocol
|
||||
- mailto Electronic mail address
|
||||
- mid Message identifiers for electronic mail
|
||||
- cid Content identifiers for MIME body part
|
||||
- news Usenet news
|
||||
- nntp Usenet news for local NNTP access only
|
||||
- prospero Access using the prospero protocols
|
||||
- http Hypertext Transfer Protocol
|
||||
- ftp File Transfer protocol
|
||||
- gopher The Gopher protocol
|
||||
- mailto Electronic mail address
|
||||
- mid Message identifiers for electronic mail
|
||||
- cid Content identifiers for MIME body part
|
||||
- news Usenet news
|
||||
- nntp Usenet news for local NNTP access only
|
||||
- prospero Access using the prospero protocols
|
||||
+ http Hypertext Transfer Protocol
|
||||
+ ftp File Transfer protocol
|
||||
+ gopher The Gopher protocol
|
||||
@@ -161,7 +161,7 @@ index 35d5b9af3eff34324656879705dcb81470fc9697..3e6a52e0fbfdd39d3aaf1592ffd19ecd
|
||||
+ nntp Usenet news for local NNTP access only
|
||||
+ prospero Access using the prospero protocols
|
||||
telnet rlogin tn3270 Reference to interactive sessions
|
||||
- wais Wide Area Information Servers
|
||||
- wais Wide Area Information Servers
|
||||
+ wais Wide Area Information Servers
|
||||
*/
|
||||
url: function(protocol, host) {
|
||||
@@ -170,10 +170,10 @@ index 35d5b9af3eff34324656879705dcb81470fc9697..3e6a52e0fbfdd39d3aaf1592ffd19ecd
|
||||
西南 重庆市 四川省 贵州省 云南省 西藏自治区
|
||||
西北 陕西省 甘肃省 青海省 宁夏回族自治区 新疆维吾尔自治区
|
||||
港澳台 香港特别行政区 澳门特别行政区 台湾省
|
||||
-
|
||||
-
|
||||
+
|
||||
**排序**
|
||||
-
|
||||
-
|
||||
+
|
||||
```js
|
||||
var map = {}
|
||||
@@ -182,25 +182,25 @@ index 35d5b9af3eff34324656879705dcb81470fc9697..3e6a52e0fbfdd39d3aaf1592ffd19ecd
|
||||
"0" / "1" / "2" / "3" / "4" / "5" / "6" / "7" / "8" / "9" /
|
||||
"a" / "b" / "c" / "d" / "e" / "f" /
|
||||
"A" / "B" / "C" / "D" / "E" / "F"
|
||||
-
|
||||
-
|
||||
+
|
||||
https://github.com/victorquinn/chancejs/blob/develop/chance.js#L1349
|
||||
*/
|
||||
guid: function() {
|
||||
@@ -6629,7 +6629,7 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
}
|
||||
|
||||
|
||||
function CaptureGroup(n) {
|
||||
- Group.call(this, "capture-group"), this.index = cgs[this.offset] || (cgs[this.offset] = index++),
|
||||
- Group.call(this, "capture-group"), this.index = cgs[this.offset] || (cgs[this.offset] = index++),
|
||||
+ Group.call(this, "capture-group"), this.index = cgs[this.offset] || (cgs[this.offset] = index++),
|
||||
this.body = n;
|
||||
}
|
||||
|
||||
|
||||
@@ -6711,7 +6711,7 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
}
|
||||
return r = l ? '"' + u(l) + '"' : "end of input", "Expected " + t + " but " + r + " found.";
|
||||
}
|
||||
- this.expected = n, this.found = l, this.offset = u, this.line = t, this.column = r,
|
||||
- this.expected = n, this.found = l, this.offset = u, this.line = t, this.column = r,
|
||||
+ this.expected = n, this.found = l, this.offset = u, this.line = t, this.column = r,
|
||||
this.name = "SyntaxError", this.message = e(n, l);
|
||||
}
|
||||
@@ -209,8 +209,8 @@ index 35d5b9af3eff34324656879705dcb81470fc9697..3e6a52e0fbfdd39d3aaf1592ffd19ecd
|
||||
function r(l) {
|
||||
function u(l, u, t) {
|
||||
var r, e;
|
||||
- for (r = u; t > r; r++) e = n.charAt(r), "\n" === e ? (l.seenCR || l.line++, l.column = 1,
|
||||
- l.seenCR = !1) : "\r" === e || "\u2028" === e || "\u2029" === e ? (l.line++, l.column = 1,
|
||||
- for (r = u; t > r; r++) e = n.charAt(r), "\n" === e ? (l.seenCR || l.line++, l.column = 1,
|
||||
- l.seenCR = !1) : "\r" === e || "\u2028" === e || "\u2029" === e ? (l.line++, l.column = 1,
|
||||
+ for (r = u; t > r; r++) e = n.charAt(r), "\n" === e ? (l.seenCR || l.line++, l.column = 1,
|
||||
+ l.seenCR = !1) : "\r" === e || "\u2028" === e || "\u2029" === e ? (l.line++, l.column = 1,
|
||||
l.seenCR = !0) : (l.column++, l.seenCR = !1);
|
||||
@@ -220,10 +220,10 @@ index 35d5b9af3eff34324656879705dcb81470fc9697..3e6a52e0fbfdd39d3aaf1592ffd19ecd
|
||||
}
|
||||
function c() {
|
||||
var l, u, t, r, o;
|
||||
- return l = qt, u = i(), null !== u ? (t = qt, 124 === n.charCodeAt(qt) ? (r = fl,
|
||||
- qt++) : (r = null, 0 === Wt && e(sl)), null !== r ? (o = c(), null !== o ? (r = [ r, o ],
|
||||
- t = r) : (qt = t, t = il)) : (qt = t, t = il), null === t && (t = al), null !== t ? (Lt = l,
|
||||
- u = hl(u, t), null === u ? (qt = l, l = u) : l = u) : (qt = l, l = il)) : (qt = l,
|
||||
- return l = qt, u = i(), null !== u ? (t = qt, 124 === n.charCodeAt(qt) ? (r = fl,
|
||||
- qt++) : (r = null, 0 === Wt && e(sl)), null !== r ? (o = c(), null !== o ? (r = [ r, o ],
|
||||
- t = r) : (qt = t, t = il)) : (qt = t, t = il), null === t && (t = al), null !== t ? (Lt = l,
|
||||
- u = hl(u, t), null === u ? (qt = l, l = u) : l = u) : (qt = l, l = il)) : (qt = l,
|
||||
+ return l = qt, u = i(), null !== u ? (t = qt, 124 === n.charCodeAt(qt) ? (r = fl,
|
||||
+ qt++) : (r = null, 0 === Wt && e(sl)), null !== r ? (o = c(), null !== o ? (r = [ r, o ],
|
||||
+ t = r) : (qt = t, t = il)) : (qt = t, t = il), null === t && (t = al), null !== t ? (Lt = l,
|
||||
@@ -232,13 +232,13 @@ index 35d5b9af3eff34324656879705dcb81470fc9697..3e6a52e0fbfdd39d3aaf1592ffd19ecd
|
||||
}
|
||||
function i() {
|
||||
var n, l, u, t, r;
|
||||
- if (n = qt, l = f(), null === l && (l = al), null !== l) if (u = qt, Wt++, t = d(),
|
||||
- if (n = qt, l = f(), null === l && (l = al), null !== l) if (u = qt, Wt++, t = d(),
|
||||
+ if (n = qt, l = f(), null === l && (l = al), null !== l) if (u = qt, Wt++, t = d(),
|
||||
Wt--, null === t ? u = al : (qt = u, u = il), null !== u) {
|
||||
- for (t = [], r = h(), null === r && (r = a()); null !== r; ) t.push(r), r = h(),
|
||||
- for (t = [], r = h(), null === r && (r = a()); null !== r; ) t.push(r), r = h(),
|
||||
+ for (t = [], r = h(), null === r && (r = a()); null !== r; ) t.push(r), r = h(),
|
||||
null === r && (r = a());
|
||||
- null !== t ? (r = s(), null === r && (r = al), null !== r ? (Lt = n, l = dl(l, t, r),
|
||||
- null !== t ? (r = s(), null === r && (r = al), null !== r ? (Lt = n, l = dl(l, t, r),
|
||||
+ null !== t ? (r = s(), null === r && (r = al), null !== r ? (Lt = n, l = dl(l, t, r),
|
||||
null === l ? (qt = n, n = l) : n = l) : (qt = n, n = il)) : (qt = n, n = il);
|
||||
} else qt = n, n = il; else qt = n, n = il;
|
||||
@@ -247,43 +247,43 @@ index 35d5b9af3eff34324656879705dcb81470fc9697..3e6a52e0fbfdd39d3aaf1592ffd19ecd
|
||||
}
|
||||
function f() {
|
||||
var l, u;
|
||||
- return l = qt, 94 === n.charCodeAt(qt) ? (u = pl, qt++) : (u = null, 0 === Wt && e(vl)),
|
||||
- return l = qt, 94 === n.charCodeAt(qt) ? (u = pl, qt++) : (u = null, 0 === Wt && e(vl)),
|
||||
+ return l = qt, 94 === n.charCodeAt(qt) ? (u = pl, qt++) : (u = null, 0 === Wt && e(vl)),
|
||||
null !== u && (Lt = l, u = wl()), null === u ? (qt = l, l = u) : l = u, l;
|
||||
}
|
||||
function s() {
|
||||
var l, u;
|
||||
- return l = qt, 36 === n.charCodeAt(qt) ? (u = Al, qt++) : (u = null, 0 === Wt && e(Cl)),
|
||||
- return l = qt, 36 === n.charCodeAt(qt) ? (u = Al, qt++) : (u = null, 0 === Wt && e(Cl)),
|
||||
+ return l = qt, 36 === n.charCodeAt(qt) ? (u = Al, qt++) : (u = null, 0 === Wt && e(Cl)),
|
||||
null !== u && (Lt = l, u = gl()), null === u ? (qt = l, l = u) : l = u, l;
|
||||
}
|
||||
function h() {
|
||||
var n, l, u;
|
||||
- return n = qt, l = a(), null !== l ? (u = d(), null !== u ? (Lt = n, l = bl(l, u),
|
||||
- return n = qt, l = a(), null !== l ? (u = d(), null !== u ? (Lt = n, l = bl(l, u),
|
||||
+ return n = qt, l = a(), null !== l ? (u = d(), null !== u ? (Lt = n, l = bl(l, u),
|
||||
null === l ? (qt = n, n = l) : n = l) : (qt = n, n = il)) : (qt = n, n = il), n;
|
||||
}
|
||||
function d() {
|
||||
var n, l, u;
|
||||
- return Wt++, n = qt, l = p(), null !== l ? (u = k(), null === u && (u = al), null !== u ? (Lt = n,
|
||||
- l = Tl(l, u), null === l ? (qt = n, n = l) : n = l) : (qt = n, n = il)) : (qt = n,
|
||||
- return Wt++, n = qt, l = p(), null !== l ? (u = k(), null === u && (u = al), null !== u ? (Lt = n,
|
||||
- l = Tl(l, u), null === l ? (qt = n, n = l) : n = l) : (qt = n, n = il)) : (qt = n,
|
||||
+ return Wt++, n = qt, l = p(), null !== l ? (u = k(), null === u && (u = al), null !== u ? (Lt = n,
|
||||
+ l = Tl(l, u), null === l ? (qt = n, n = l) : n = l) : (qt = n, n = il)) : (qt = n,
|
||||
n = il), Wt--, null === n && (l = null, 0 === Wt && e(kl)), n;
|
||||
}
|
||||
function p() {
|
||||
var n;
|
||||
- return n = v(), null === n && (n = w(), null === n && (n = A(), null === n && (n = C(),
|
||||
- return n = v(), null === n && (n = w(), null === n && (n = A(), null === n && (n = C(),
|
||||
+ return n = v(), null === n && (n = w(), null === n && (n = A(), null === n && (n = C(),
|
||||
null === n && (n = g(), null === n && (n = b()))))), n;
|
||||
}
|
||||
function v() {
|
||||
var l, u, t, r, o, c;
|
||||
- return l = qt, 123 === n.charCodeAt(qt) ? (u = xl, qt++) : (u = null, 0 === Wt && e(yl)),
|
||||
- null !== u ? (t = T(), null !== t ? (44 === n.charCodeAt(qt) ? (r = ml, qt++) : (r = null,
|
||||
- 0 === Wt && e(Rl)), null !== r ? (o = T(), null !== o ? (125 === n.charCodeAt(qt) ? (c = Fl,
|
||||
- qt++) : (c = null, 0 === Wt && e(Ql)), null !== c ? (Lt = l, u = Sl(t, o), null === u ? (qt = l,
|
||||
- l = u) : l = u) : (qt = l, l = il)) : (qt = l, l = il)) : (qt = l, l = il)) : (qt = l,
|
||||
- return l = qt, 123 === n.charCodeAt(qt) ? (u = xl, qt++) : (u = null, 0 === Wt && e(yl)),
|
||||
- null !== u ? (t = T(), null !== t ? (44 === n.charCodeAt(qt) ? (r = ml, qt++) : (r = null,
|
||||
- 0 === Wt && e(Rl)), null !== r ? (o = T(), null !== o ? (125 === n.charCodeAt(qt) ? (c = Fl,
|
||||
- qt++) : (c = null, 0 === Wt && e(Ql)), null !== c ? (Lt = l, u = Sl(t, o), null === u ? (qt = l,
|
||||
- l = u) : l = u) : (qt = l, l = il)) : (qt = l, l = il)) : (qt = l, l = il)) : (qt = l,
|
||||
+ return l = qt, 123 === n.charCodeAt(qt) ? (u = xl, qt++) : (u = null, 0 === Wt && e(yl)),
|
||||
+ null !== u ? (t = T(), null !== t ? (44 === n.charCodeAt(qt) ? (r = ml, qt++) : (r = null,
|
||||
+ 0 === Wt && e(Rl)), null !== r ? (o = T(), null !== o ? (125 === n.charCodeAt(qt) ? (c = Fl,
|
||||
@@ -293,9 +293,9 @@ index 35d5b9af3eff34324656879705dcb81470fc9697..3e6a52e0fbfdd39d3aaf1592ffd19ecd
|
||||
}
|
||||
function w() {
|
||||
var l, u, t, r;
|
||||
- return l = qt, 123 === n.charCodeAt(qt) ? (u = xl, qt++) : (u = null, 0 === Wt && e(yl)),
|
||||
- null !== u ? (t = T(), null !== t ? (n.substr(qt, 2) === Ul ? (r = Ul, qt += 2) : (r = null,
|
||||
- 0 === Wt && e(El)), null !== r ? (Lt = l, u = Gl(t), null === u ? (qt = l, l = u) : l = u) : (qt = l,
|
||||
- return l = qt, 123 === n.charCodeAt(qt) ? (u = xl, qt++) : (u = null, 0 === Wt && e(yl)),
|
||||
- null !== u ? (t = T(), null !== t ? (n.substr(qt, 2) === Ul ? (r = Ul, qt += 2) : (r = null,
|
||||
- 0 === Wt && e(El)), null !== r ? (Lt = l, u = Gl(t), null === u ? (qt = l, l = u) : l = u) : (qt = l,
|
||||
+ return l = qt, 123 === n.charCodeAt(qt) ? (u = xl, qt++) : (u = null, 0 === Wt && e(yl)),
|
||||
+ null !== u ? (t = T(), null !== t ? (n.substr(qt, 2) === Ul ? (r = Ul, qt += 2) : (r = null,
|
||||
+ 0 === Wt && e(El)), null !== r ? (Lt = l, u = Gl(t), null === u ? (qt = l, l = u) : l = u) : (qt = l,
|
||||
@@ -303,9 +303,9 @@ index 35d5b9af3eff34324656879705dcb81470fc9697..3e6a52e0fbfdd39d3aaf1592ffd19ecd
|
||||
}
|
||||
function A() {
|
||||
var l, u, t, r;
|
||||
- return l = qt, 123 === n.charCodeAt(qt) ? (u = xl, qt++) : (u = null, 0 === Wt && e(yl)),
|
||||
- null !== u ? (t = T(), null !== t ? (125 === n.charCodeAt(qt) ? (r = Fl, qt++) : (r = null,
|
||||
- 0 === Wt && e(Ql)), null !== r ? (Lt = l, u = Bl(t), null === u ? (qt = l, l = u) : l = u) : (qt = l,
|
||||
- return l = qt, 123 === n.charCodeAt(qt) ? (u = xl, qt++) : (u = null, 0 === Wt && e(yl)),
|
||||
- null !== u ? (t = T(), null !== t ? (125 === n.charCodeAt(qt) ? (r = Fl, qt++) : (r = null,
|
||||
- 0 === Wt && e(Ql)), null !== r ? (Lt = l, u = Bl(t), null === u ? (qt = l, l = u) : l = u) : (qt = l,
|
||||
+ return l = qt, 123 === n.charCodeAt(qt) ? (u = xl, qt++) : (u = null, 0 === Wt && e(yl)),
|
||||
+ null !== u ? (t = T(), null !== t ? (125 === n.charCodeAt(qt) ? (r = Fl, qt++) : (r = null,
|
||||
+ 0 === Wt && e(Ql)), null !== r ? (Lt = l, u = Bl(t), null === u ? (qt = l, l = u) : l = u) : (qt = l,
|
||||
@@ -313,45 +313,45 @@ index 35d5b9af3eff34324656879705dcb81470fc9697..3e6a52e0fbfdd39d3aaf1592ffd19ecd
|
||||
}
|
||||
function C() {
|
||||
var l, u;
|
||||
- return l = qt, 43 === n.charCodeAt(qt) ? (u = jl, qt++) : (u = null, 0 === Wt && e($l)),
|
||||
- return l = qt, 43 === n.charCodeAt(qt) ? (u = jl, qt++) : (u = null, 0 === Wt && e($l)),
|
||||
+ return l = qt, 43 === n.charCodeAt(qt) ? (u = jl, qt++) : (u = null, 0 === Wt && e($l)),
|
||||
null !== u && (Lt = l, u = ql()), null === u ? (qt = l, l = u) : l = u, l;
|
||||
}
|
||||
function g() {
|
||||
var l, u;
|
||||
- return l = qt, 42 === n.charCodeAt(qt) ? (u = Ll, qt++) : (u = null, 0 === Wt && e(Ml)),
|
||||
- return l = qt, 42 === n.charCodeAt(qt) ? (u = Ll, qt++) : (u = null, 0 === Wt && e(Ml)),
|
||||
+ return l = qt, 42 === n.charCodeAt(qt) ? (u = Ll, qt++) : (u = null, 0 === Wt && e(Ml)),
|
||||
null !== u && (Lt = l, u = Dl()), null === u ? (qt = l, l = u) : l = u, l;
|
||||
}
|
||||
function b() {
|
||||
var l, u;
|
||||
- return l = qt, 63 === n.charCodeAt(qt) ? (u = Hl, qt++) : (u = null, 0 === Wt && e(Ol)),
|
||||
- return l = qt, 63 === n.charCodeAt(qt) ? (u = Hl, qt++) : (u = null, 0 === Wt && e(Ol)),
|
||||
+ return l = qt, 63 === n.charCodeAt(qt) ? (u = Hl, qt++) : (u = null, 0 === Wt && e(Ol)),
|
||||
null !== u && (Lt = l, u = Wl()), null === u ? (qt = l, l = u) : l = u, l;
|
||||
}
|
||||
function k() {
|
||||
var l;
|
||||
- return 63 === n.charCodeAt(qt) ? (l = Hl, qt++) : (l = null, 0 === Wt && e(Ol)),
|
||||
- return 63 === n.charCodeAt(qt) ? (l = Hl, qt++) : (l = null, 0 === Wt && e(Ol)),
|
||||
+ return 63 === n.charCodeAt(qt) ? (l = Hl, qt++) : (l = null, 0 === Wt && e(Ol)),
|
||||
l;
|
||||
}
|
||||
function T() {
|
||||
var l, u, t;
|
||||
- if (l = qt, u = [], zl.test(n.charAt(qt)) ? (t = n.charAt(qt), qt++) : (t = null,
|
||||
- 0 === Wt && e(Il)), null !== t) for (;null !== t; ) u.push(t), zl.test(n.charAt(qt)) ? (t = n.charAt(qt),
|
||||
- if (l = qt, u = [], zl.test(n.charAt(qt)) ? (t = n.charAt(qt), qt++) : (t = null,
|
||||
- 0 === Wt && e(Il)), null !== t) for (;null !== t; ) u.push(t), zl.test(n.charAt(qt)) ? (t = n.charAt(qt),
|
||||
+ if (l = qt, u = [], zl.test(n.charAt(qt)) ? (t = n.charAt(qt), qt++) : (t = null,
|
||||
+ 0 === Wt && e(Il)), null !== t) for (;null !== t; ) u.push(t), zl.test(n.charAt(qt)) ? (t = n.charAt(qt),
|
||||
qt++) : (t = null, 0 === Wt && e(Il)); else u = il;
|
||||
- return null !== u && (Lt = l, u = Jl(u)), null === u ? (qt = l, l = u) : l = u,
|
||||
- return null !== u && (Lt = l, u = Jl(u)), null === u ? (qt = l, l = u) : l = u,
|
||||
+ return null !== u && (Lt = l, u = Jl(u)), null === u ? (qt = l, l = u) : l = u,
|
||||
l;
|
||||
}
|
||||
function x() {
|
||||
var l, u, t, r;
|
||||
- return l = qt, 40 === n.charCodeAt(qt) ? (u = Kl, qt++) : (u = null, 0 === Wt && e(Nl)),
|
||||
- null !== u ? (t = R(), null === t && (t = F(), null === t && (t = m(), null === t && (t = y()))),
|
||||
- null !== t ? (41 === n.charCodeAt(qt) ? (r = Pl, qt++) : (r = null, 0 === Wt && e(Vl)),
|
||||
- null !== r ? (Lt = l, u = Xl(t), null === u ? (qt = l, l = u) : l = u) : (qt = l,
|
||||
- return l = qt, 40 === n.charCodeAt(qt) ? (u = Kl, qt++) : (u = null, 0 === Wt && e(Nl)),
|
||||
- null !== u ? (t = R(), null === t && (t = F(), null === t && (t = m(), null === t && (t = y()))),
|
||||
- null !== t ? (41 === n.charCodeAt(qt) ? (r = Pl, qt++) : (r = null, 0 === Wt && e(Vl)),
|
||||
- null !== r ? (Lt = l, u = Xl(t), null === u ? (qt = l, l = u) : l = u) : (qt = l,
|
||||
+ return l = qt, 40 === n.charCodeAt(qt) ? (u = Kl, qt++) : (u = null, 0 === Wt && e(Nl)),
|
||||
+ null !== u ? (t = R(), null === t && (t = F(), null === t && (t = m(), null === t && (t = y()))),
|
||||
+ null !== t ? (41 === n.charCodeAt(qt) ? (r = Pl, qt++) : (r = null, 0 === Wt && e(Vl)),
|
||||
@@ -360,46 +360,46 @@ index 35d5b9af3eff34324656879705dcb81470fc9697..3e6a52e0fbfdd39d3aaf1592ffd19ecd
|
||||
}
|
||||
function y() {
|
||||
var n, l;
|
||||
- return n = qt, l = c(), null !== l && (Lt = n, l = Yl(l)), null === l ? (qt = n,
|
||||
- return n = qt, l = c(), null !== l && (Lt = n, l = Yl(l)), null === l ? (qt = n,
|
||||
+ return n = qt, l = c(), null !== l && (Lt = n, l = Yl(l)), null === l ? (qt = n,
|
||||
n = l) : n = l, n;
|
||||
}
|
||||
function m() {
|
||||
var l, u, t;
|
||||
- return l = qt, n.substr(qt, 2) === Zl ? (u = Zl, qt += 2) : (u = null, 0 === Wt && e(_l)),
|
||||
- null !== u ? (t = c(), null !== t ? (Lt = l, u = nu(t), null === u ? (qt = l, l = u) : l = u) : (qt = l,
|
||||
- return l = qt, n.substr(qt, 2) === Zl ? (u = Zl, qt += 2) : (u = null, 0 === Wt && e(_l)),
|
||||
- null !== u ? (t = c(), null !== t ? (Lt = l, u = nu(t), null === u ? (qt = l, l = u) : l = u) : (qt = l,
|
||||
+ return l = qt, n.substr(qt, 2) === Zl ? (u = Zl, qt += 2) : (u = null, 0 === Wt && e(_l)),
|
||||
+ null !== u ? (t = c(), null !== t ? (Lt = l, u = nu(t), null === u ? (qt = l, l = u) : l = u) : (qt = l,
|
||||
l = il)) : (qt = l, l = il), l;
|
||||
}
|
||||
function R() {
|
||||
var l, u, t;
|
||||
- return l = qt, n.substr(qt, 2) === lu ? (u = lu, qt += 2) : (u = null, 0 === Wt && e(uu)),
|
||||
- null !== u ? (t = c(), null !== t ? (Lt = l, u = tu(t), null === u ? (qt = l, l = u) : l = u) : (qt = l,
|
||||
- return l = qt, n.substr(qt, 2) === lu ? (u = lu, qt += 2) : (u = null, 0 === Wt && e(uu)),
|
||||
- null !== u ? (t = c(), null !== t ? (Lt = l, u = tu(t), null === u ? (qt = l, l = u) : l = u) : (qt = l,
|
||||
+ return l = qt, n.substr(qt, 2) === lu ? (u = lu, qt += 2) : (u = null, 0 === Wt && e(uu)),
|
||||
+ null !== u ? (t = c(), null !== t ? (Lt = l, u = tu(t), null === u ? (qt = l, l = u) : l = u) : (qt = l,
|
||||
l = il)) : (qt = l, l = il), l;
|
||||
}
|
||||
function F() {
|
||||
var l, u, t;
|
||||
- return l = qt, n.substr(qt, 2) === ru ? (u = ru, qt += 2) : (u = null, 0 === Wt && e(eu)),
|
||||
- null !== u ? (t = c(), null !== t ? (Lt = l, u = ou(t), null === u ? (qt = l, l = u) : l = u) : (qt = l,
|
||||
- return l = qt, n.substr(qt, 2) === ru ? (u = ru, qt += 2) : (u = null, 0 === Wt && e(eu)),
|
||||
- null !== u ? (t = c(), null !== t ? (Lt = l, u = ou(t), null === u ? (qt = l, l = u) : l = u) : (qt = l,
|
||||
+ return l = qt, n.substr(qt, 2) === ru ? (u = ru, qt += 2) : (u = null, 0 === Wt && e(eu)),
|
||||
+ null !== u ? (t = c(), null !== t ? (Lt = l, u = ou(t), null === u ? (qt = l, l = u) : l = u) : (qt = l,
|
||||
l = il)) : (qt = l, l = il), l;
|
||||
}
|
||||
function Q() {
|
||||
var l, u, t, r, o;
|
||||
- if (Wt++, l = qt, 91 === n.charCodeAt(qt) ? (u = iu, qt++) : (u = null, 0 === Wt && e(au)),
|
||||
- null !== u) if (94 === n.charCodeAt(qt) ? (t = pl, qt++) : (t = null, 0 === Wt && e(vl)),
|
||||
- if (Wt++, l = qt, 91 === n.charCodeAt(qt) ? (u = iu, qt++) : (u = null, 0 === Wt && e(au)),
|
||||
- null !== u) if (94 === n.charCodeAt(qt) ? (t = pl, qt++) : (t = null, 0 === Wt && e(vl)),
|
||||
+ if (Wt++, l = qt, 91 === n.charCodeAt(qt) ? (u = iu, qt++) : (u = null, 0 === Wt && e(au)),
|
||||
+ null !== u) if (94 === n.charCodeAt(qt) ? (t = pl, qt++) : (t = null, 0 === Wt && e(vl)),
|
||||
null === t && (t = al), null !== t) {
|
||||
- for (r = [], o = S(), null === o && (o = U()); null !== o; ) r.push(o), o = S(),
|
||||
- for (r = [], o = S(), null === o && (o = U()); null !== o; ) r.push(o), o = S(),
|
||||
+ for (r = [], o = S(), null === o && (o = U()); null !== o; ) r.push(o), o = S(),
|
||||
null === o && (o = U());
|
||||
- null !== r ? (93 === n.charCodeAt(qt) ? (o = fu, qt++) : (o = null, 0 === Wt && e(su)),
|
||||
- null !== o ? (Lt = l, u = hu(t, r), null === u ? (qt = l, l = u) : l = u) : (qt = l,
|
||||
- null !== r ? (93 === n.charCodeAt(qt) ? (o = fu, qt++) : (o = null, 0 === Wt && e(su)),
|
||||
- null !== o ? (Lt = l, u = hu(t, r), null === u ? (qt = l, l = u) : l = u) : (qt = l,
|
||||
+ null !== r ? (93 === n.charCodeAt(qt) ? (o = fu, qt++) : (o = null, 0 === Wt && e(su)),
|
||||
+ null !== o ? (Lt = l, u = hu(t, r), null === u ? (qt = l, l = u) : l = u) : (qt = l,
|
||||
l = il)) : (qt = l, l = il);
|
||||
@@ -408,9 +408,9 @@ index 35d5b9af3eff34324656879705dcb81470fc9697..3e6a52e0fbfdd39d3aaf1592ffd19ecd
|
||||
}
|
||||
function S() {
|
||||
var l, u, t, r;
|
||||
- return Wt++, l = qt, u = U(), null !== u ? (45 === n.charCodeAt(qt) ? (t = pu, qt++) : (t = null,
|
||||
- 0 === Wt && e(vu)), null !== t ? (r = U(), null !== r ? (Lt = l, u = wu(u, r), null === u ? (qt = l,
|
||||
- l = u) : l = u) : (qt = l, l = il)) : (qt = l, l = il)) : (qt = l, l = il), Wt--,
|
||||
- return Wt++, l = qt, u = U(), null !== u ? (45 === n.charCodeAt(qt) ? (t = pu, qt++) : (t = null,
|
||||
- 0 === Wt && e(vu)), null !== t ? (r = U(), null !== r ? (Lt = l, u = wu(u, r), null === u ? (qt = l,
|
||||
- l = u) : l = u) : (qt = l, l = il)) : (qt = l, l = il)) : (qt = l, l = il), Wt--,
|
||||
+ return Wt++, l = qt, u = U(), null !== u ? (45 === n.charCodeAt(qt) ? (t = pu, qt++) : (t = null,
|
||||
+ 0 === Wt && e(vu)), null !== t ? (r = U(), null !== r ? (Lt = l, u = wu(u, r), null === u ? (qt = l,
|
||||
+ l = u) : l = u) : (qt = l, l = il)) : (qt = l, l = il)) : (qt = l, l = il), Wt--,
|
||||
@@ -418,22 +418,22 @@ index 35d5b9af3eff34324656879705dcb81470fc9697..3e6a52e0fbfdd39d3aaf1592ffd19ecd
|
||||
}
|
||||
function U() {
|
||||
var n, l;
|
||||
- return Wt++, n = G(), null === n && (n = E()), Wt--, null === n && (l = null, 0 === Wt && e(Au)),
|
||||
- return Wt++, n = G(), null === n && (n = E()), Wt--, null === n && (l = null, 0 === Wt && e(Au)),
|
||||
+ return Wt++, n = G(), null === n && (n = E()), Wt--, null === n && (l = null, 0 === Wt && e(Au)),
|
||||
n;
|
||||
}
|
||||
function E() {
|
||||
var l, u;
|
||||
- return l = qt, Cu.test(n.charAt(qt)) ? (u = n.charAt(qt), qt++) : (u = null, 0 === Wt && e(gu)),
|
||||
- return l = qt, Cu.test(n.charAt(qt)) ? (u = n.charAt(qt), qt++) : (u = null, 0 === Wt && e(gu)),
|
||||
+ return l = qt, Cu.test(n.charAt(qt)) ? (u = n.charAt(qt), qt++) : (u = null, 0 === Wt && e(gu)),
|
||||
null !== u && (Lt = l, u = bu(u)), null === u ? (qt = l, l = u) : l = u, l;
|
||||
}
|
||||
function G() {
|
||||
var n;
|
||||
- return n = L(), null === n && (n = Y(), null === n && (n = H(), null === n && (n = O(),
|
||||
- null === n && (n = W(), null === n && (n = z(), null === n && (n = I(), null === n && (n = J(),
|
||||
- null === n && (n = K(), null === n && (n = N(), null === n && (n = P(), null === n && (n = V(),
|
||||
- null === n && (n = X(), null === n && (n = _(), null === n && (n = nl(), null === n && (n = ll(),
|
||||
- return n = L(), null === n && (n = Y(), null === n && (n = H(), null === n && (n = O(),
|
||||
- null === n && (n = W(), null === n && (n = z(), null === n && (n = I(), null === n && (n = J(),
|
||||
- null === n && (n = K(), null === n && (n = N(), null === n && (n = P(), null === n && (n = V(),
|
||||
- null === n && (n = X(), null === n && (n = _(), null === n && (n = nl(), null === n && (n = ll(),
|
||||
+ return n = L(), null === n && (n = Y(), null === n && (n = H(), null === n && (n = O(),
|
||||
+ null === n && (n = W(), null === n && (n = z(), null === n && (n = I(), null === n && (n = J(),
|
||||
+ null === n && (n = K(), null === n && (n = N(), null === n && (n = P(), null === n && (n = V(),
|
||||
@@ -445,25 +445,25 @@ index 35d5b9af3eff34324656879705dcb81470fc9697..3e6a52e0fbfdd39d3aaf1592ffd19ecd
|
||||
}
|
||||
function j() {
|
||||
var l, u;
|
||||
- return l = qt, 46 === n.charCodeAt(qt) ? (u = ku, qt++) : (u = null, 0 === Wt && e(Tu)),
|
||||
- return l = qt, 46 === n.charCodeAt(qt) ? (u = ku, qt++) : (u = null, 0 === Wt && e(Tu)),
|
||||
+ return l = qt, 46 === n.charCodeAt(qt) ? (u = ku, qt++) : (u = null, 0 === Wt && e(Tu)),
|
||||
null !== u && (Lt = l, u = xu()), null === u ? (qt = l, l = u) : l = u, l;
|
||||
}
|
||||
function $() {
|
||||
var l, u;
|
||||
- return Wt++, l = qt, mu.test(n.charAt(qt)) ? (u = n.charAt(qt), qt++) : (u = null,
|
||||
- 0 === Wt && e(Ru)), null !== u && (Lt = l, u = bu(u)), null === u ? (qt = l, l = u) : l = u,
|
||||
- return Wt++, l = qt, mu.test(n.charAt(qt)) ? (u = n.charAt(qt), qt++) : (u = null,
|
||||
- 0 === Wt && e(Ru)), null !== u && (Lt = l, u = bu(u)), null === u ? (qt = l, l = u) : l = u,
|
||||
+ return Wt++, l = qt, mu.test(n.charAt(qt)) ? (u = n.charAt(qt), qt++) : (u = null,
|
||||
+ 0 === Wt && e(Ru)), null !== u && (Lt = l, u = bu(u)), null === u ? (qt = l, l = u) : l = u,
|
||||
Wt--, null === l && (u = null, 0 === Wt && e(yu)), l;
|
||||
}
|
||||
function q() {
|
||||
var n;
|
||||
- return n = M(), null === n && (n = D(), null === n && (n = Y(), null === n && (n = H(),
|
||||
- null === n && (n = O(), null === n && (n = W(), null === n && (n = z(), null === n && (n = I(),
|
||||
- null === n && (n = J(), null === n && (n = K(), null === n && (n = N(), null === n && (n = P(),
|
||||
- null === n && (n = V(), null === n && (n = X(), null === n && (n = Z(), null === n && (n = _(),
|
||||
- null === n && (n = nl(), null === n && (n = ll(), null === n && (n = ul(), null === n && (n = tl()))))))))))))))))))),
|
||||
- return n = M(), null === n && (n = D(), null === n && (n = Y(), null === n && (n = H(),
|
||||
- null === n && (n = O(), null === n && (n = W(), null === n && (n = z(), null === n && (n = I(),
|
||||
- null === n && (n = J(), null === n && (n = K(), null === n && (n = N(), null === n && (n = P(),
|
||||
- null === n && (n = V(), null === n && (n = X(), null === n && (n = Z(), null === n && (n = _(),
|
||||
- null === n && (n = nl(), null === n && (n = ll(), null === n && (n = ul(), null === n && (n = tl()))))))))))))))))))),
|
||||
+ return n = M(), null === n && (n = D(), null === n && (n = Y(), null === n && (n = H(),
|
||||
+ null === n && (n = O(), null === n && (n = W(), null === n && (n = z(), null === n && (n = I(),
|
||||
+ null === n && (n = J(), null === n && (n = K(), null === n && (n = N(), null === n && (n = P(),
|
||||
@@ -473,93 +473,93 @@ index 35d5b9af3eff34324656879705dcb81470fc9697..3e6a52e0fbfdd39d3aaf1592ffd19ecd
|
||||
}
|
||||
function L() {
|
||||
var l, u;
|
||||
- return l = qt, n.substr(qt, 2) === Fu ? (u = Fu, qt += 2) : (u = null, 0 === Wt && e(Qu)),
|
||||
- return l = qt, n.substr(qt, 2) === Fu ? (u = Fu, qt += 2) : (u = null, 0 === Wt && e(Qu)),
|
||||
+ return l = qt, n.substr(qt, 2) === Fu ? (u = Fu, qt += 2) : (u = null, 0 === Wt && e(Qu)),
|
||||
null !== u && (Lt = l, u = Su()), null === u ? (qt = l, l = u) : l = u, l;
|
||||
}
|
||||
function M() {
|
||||
var l, u;
|
||||
- return l = qt, n.substr(qt, 2) === Fu ? (u = Fu, qt += 2) : (u = null, 0 === Wt && e(Qu)),
|
||||
- return l = qt, n.substr(qt, 2) === Fu ? (u = Fu, qt += 2) : (u = null, 0 === Wt && e(Qu)),
|
||||
+ return l = qt, n.substr(qt, 2) === Fu ? (u = Fu, qt += 2) : (u = null, 0 === Wt && e(Qu)),
|
||||
null !== u && (Lt = l, u = Uu()), null === u ? (qt = l, l = u) : l = u, l;
|
||||
}
|
||||
function D() {
|
||||
var l, u;
|
||||
- return l = qt, n.substr(qt, 2) === Eu ? (u = Eu, qt += 2) : (u = null, 0 === Wt && e(Gu)),
|
||||
- return l = qt, n.substr(qt, 2) === Eu ? (u = Eu, qt += 2) : (u = null, 0 === Wt && e(Gu)),
|
||||
+ return l = qt, n.substr(qt, 2) === Eu ? (u = Eu, qt += 2) : (u = null, 0 === Wt && e(Gu)),
|
||||
null !== u && (Lt = l, u = Bu()), null === u ? (qt = l, l = u) : l = u, l;
|
||||
}
|
||||
function H() {
|
||||
var l, u;
|
||||
- return l = qt, n.substr(qt, 2) === ju ? (u = ju, qt += 2) : (u = null, 0 === Wt && e($u)),
|
||||
- return l = qt, n.substr(qt, 2) === ju ? (u = ju, qt += 2) : (u = null, 0 === Wt && e($u)),
|
||||
+ return l = qt, n.substr(qt, 2) === ju ? (u = ju, qt += 2) : (u = null, 0 === Wt && e($u)),
|
||||
null !== u && (Lt = l, u = qu()), null === u ? (qt = l, l = u) : l = u, l;
|
||||
}
|
||||
function O() {
|
||||
var l, u;
|
||||
- return l = qt, n.substr(qt, 2) === Lu ? (u = Lu, qt += 2) : (u = null, 0 === Wt && e(Mu)),
|
||||
- return l = qt, n.substr(qt, 2) === Lu ? (u = Lu, qt += 2) : (u = null, 0 === Wt && e(Mu)),
|
||||
+ return l = qt, n.substr(qt, 2) === Lu ? (u = Lu, qt += 2) : (u = null, 0 === Wt && e(Mu)),
|
||||
null !== u && (Lt = l, u = Du()), null === u ? (qt = l, l = u) : l = u, l;
|
||||
}
|
||||
function W() {
|
||||
var l, u;
|
||||
- return l = qt, n.substr(qt, 2) === Hu ? (u = Hu, qt += 2) : (u = null, 0 === Wt && e(Ou)),
|
||||
- return l = qt, n.substr(qt, 2) === Hu ? (u = Hu, qt += 2) : (u = null, 0 === Wt && e(Ou)),
|
||||
+ return l = qt, n.substr(qt, 2) === Hu ? (u = Hu, qt += 2) : (u = null, 0 === Wt && e(Ou)),
|
||||
null !== u && (Lt = l, u = Wu()), null === u ? (qt = l, l = u) : l = u, l;
|
||||
}
|
||||
function z() {
|
||||
var l, u;
|
||||
- return l = qt, n.substr(qt, 2) === zu ? (u = zu, qt += 2) : (u = null, 0 === Wt && e(Iu)),
|
||||
- return l = qt, n.substr(qt, 2) === zu ? (u = zu, qt += 2) : (u = null, 0 === Wt && e(Iu)),
|
||||
+ return l = qt, n.substr(qt, 2) === zu ? (u = zu, qt += 2) : (u = null, 0 === Wt && e(Iu)),
|
||||
null !== u && (Lt = l, u = Ju()), null === u ? (qt = l, l = u) : l = u, l;
|
||||
}
|
||||
function I() {
|
||||
var l, u;
|
||||
- return l = qt, n.substr(qt, 2) === Ku ? (u = Ku, qt += 2) : (u = null, 0 === Wt && e(Nu)),
|
||||
- return l = qt, n.substr(qt, 2) === Ku ? (u = Ku, qt += 2) : (u = null, 0 === Wt && e(Nu)),
|
||||
+ return l = qt, n.substr(qt, 2) === Ku ? (u = Ku, qt += 2) : (u = null, 0 === Wt && e(Nu)),
|
||||
null !== u && (Lt = l, u = Pu()), null === u ? (qt = l, l = u) : l = u, l;
|
||||
}
|
||||
function J() {
|
||||
var l, u;
|
||||
- return l = qt, n.substr(qt, 2) === Vu ? (u = Vu, qt += 2) : (u = null, 0 === Wt && e(Xu)),
|
||||
- return l = qt, n.substr(qt, 2) === Vu ? (u = Vu, qt += 2) : (u = null, 0 === Wt && e(Xu)),
|
||||
+ return l = qt, n.substr(qt, 2) === Vu ? (u = Vu, qt += 2) : (u = null, 0 === Wt && e(Xu)),
|
||||
null !== u && (Lt = l, u = Yu()), null === u ? (qt = l, l = u) : l = u, l;
|
||||
}
|
||||
function K() {
|
||||
var l, u;
|
||||
- return l = qt, n.substr(qt, 2) === Zu ? (u = Zu, qt += 2) : (u = null, 0 === Wt && e(_u)),
|
||||
- return l = qt, n.substr(qt, 2) === Zu ? (u = Zu, qt += 2) : (u = null, 0 === Wt && e(_u)),
|
||||
+ return l = qt, n.substr(qt, 2) === Zu ? (u = Zu, qt += 2) : (u = null, 0 === Wt && e(_u)),
|
||||
null !== u && (Lt = l, u = nt()), null === u ? (qt = l, l = u) : l = u, l;
|
||||
}
|
||||
function N() {
|
||||
var l, u;
|
||||
- return l = qt, n.substr(qt, 2) === lt ? (u = lt, qt += 2) : (u = null, 0 === Wt && e(ut)),
|
||||
- return l = qt, n.substr(qt, 2) === lt ? (u = lt, qt += 2) : (u = null, 0 === Wt && e(ut)),
|
||||
+ return l = qt, n.substr(qt, 2) === lt ? (u = lt, qt += 2) : (u = null, 0 === Wt && e(ut)),
|
||||
null !== u && (Lt = l, u = tt()), null === u ? (qt = l, l = u) : l = u, l;
|
||||
}
|
||||
function P() {
|
||||
var l, u;
|
||||
- return l = qt, n.substr(qt, 2) === rt ? (u = rt, qt += 2) : (u = null, 0 === Wt && e(et)),
|
||||
- return l = qt, n.substr(qt, 2) === rt ? (u = rt, qt += 2) : (u = null, 0 === Wt && e(et)),
|
||||
+ return l = qt, n.substr(qt, 2) === rt ? (u = rt, qt += 2) : (u = null, 0 === Wt && e(et)),
|
||||
null !== u && (Lt = l, u = ot()), null === u ? (qt = l, l = u) : l = u, l;
|
||||
}
|
||||
function V() {
|
||||
var l, u;
|
||||
- return l = qt, n.substr(qt, 2) === ct ? (u = ct, qt += 2) : (u = null, 0 === Wt && e(it)),
|
||||
- return l = qt, n.substr(qt, 2) === ct ? (u = ct, qt += 2) : (u = null, 0 === Wt && e(it)),
|
||||
+ return l = qt, n.substr(qt, 2) === ct ? (u = ct, qt += 2) : (u = null, 0 === Wt && e(it)),
|
||||
null !== u && (Lt = l, u = at()), null === u ? (qt = l, l = u) : l = u, l;
|
||||
}
|
||||
function X() {
|
||||
var l, u;
|
||||
- return l = qt, n.substr(qt, 2) === ft ? (u = ft, qt += 2) : (u = null, 0 === Wt && e(st)),
|
||||
- return l = qt, n.substr(qt, 2) === ft ? (u = ft, qt += 2) : (u = null, 0 === Wt && e(st)),
|
||||
+ return l = qt, n.substr(qt, 2) === ft ? (u = ft, qt += 2) : (u = null, 0 === Wt && e(st)),
|
||||
null !== u && (Lt = l, u = ht()), null === u ? (qt = l, l = u) : l = u, l;
|
||||
}
|
||||
function Y() {
|
||||
var l, u, t;
|
||||
- return l = qt, n.substr(qt, 2) === dt ? (u = dt, qt += 2) : (u = null, 0 === Wt && e(pt)),
|
||||
- null !== u ? (n.length > qt ? (t = n.charAt(qt), qt++) : (t = null, 0 === Wt && e(vt)),
|
||||
- null !== t ? (Lt = l, u = wt(t), null === u ? (qt = l, l = u) : l = u) : (qt = l,
|
||||
- return l = qt, n.substr(qt, 2) === dt ? (u = dt, qt += 2) : (u = null, 0 === Wt && e(pt)),
|
||||
- null !== u ? (n.length > qt ? (t = n.charAt(qt), qt++) : (t = null, 0 === Wt && e(vt)),
|
||||
- null !== t ? (Lt = l, u = wt(t), null === u ? (qt = l, l = u) : l = u) : (qt = l,
|
||||
+ return l = qt, n.substr(qt, 2) === dt ? (u = dt, qt += 2) : (u = null, 0 === Wt && e(pt)),
|
||||
+ null !== u ? (n.length > qt ? (t = n.charAt(qt), qt++) : (t = null, 0 === Wt && e(vt)),
|
||||
+ null !== t ? (Lt = l, u = wt(t), null === u ? (qt = l, l = u) : l = u) : (qt = l,
|
||||
@@ -567,9 +567,9 @@ index 35d5b9af3eff34324656879705dcb81470fc9697..3e6a52e0fbfdd39d3aaf1592ffd19ecd
|
||||
}
|
||||
function Z() {
|
||||
var l, u, t;
|
||||
- return l = qt, 92 === n.charCodeAt(qt) ? (u = At, qt++) : (u = null, 0 === Wt && e(Ct)),
|
||||
- null !== u ? (gt.test(n.charAt(qt)) ? (t = n.charAt(qt), qt++) : (t = null, 0 === Wt && e(bt)),
|
||||
- null !== t ? (Lt = l, u = kt(t), null === u ? (qt = l, l = u) : l = u) : (qt = l,
|
||||
- return l = qt, 92 === n.charCodeAt(qt) ? (u = At, qt++) : (u = null, 0 === Wt && e(Ct)),
|
||||
- null !== u ? (gt.test(n.charAt(qt)) ? (t = n.charAt(qt), qt++) : (t = null, 0 === Wt && e(bt)),
|
||||
- null !== t ? (Lt = l, u = kt(t), null === u ? (qt = l, l = u) : l = u) : (qt = l,
|
||||
+ return l = qt, 92 === n.charCodeAt(qt) ? (u = At, qt++) : (u = null, 0 === Wt && e(Ct)),
|
||||
+ null !== u ? (gt.test(n.charAt(qt)) ? (t = n.charAt(qt), qt++) : (t = null, 0 === Wt && e(bt)),
|
||||
+ null !== t ? (Lt = l, u = kt(t), null === u ? (qt = l, l = u) : l = u) : (qt = l,
|
||||
@@ -577,15 +577,15 @@ index 35d5b9af3eff34324656879705dcb81470fc9697..3e6a52e0fbfdd39d3aaf1592ffd19ecd
|
||||
}
|
||||
function _() {
|
||||
var l, u, t, r;
|
||||
- if (l = qt, n.substr(qt, 2) === Tt ? (u = Tt, qt += 2) : (u = null, 0 === Wt && e(xt)),
|
||||
- if (l = qt, n.substr(qt, 2) === Tt ? (u = Tt, qt += 2) : (u = null, 0 === Wt && e(xt)),
|
||||
+ if (l = qt, n.substr(qt, 2) === Tt ? (u = Tt, qt += 2) : (u = null, 0 === Wt && e(xt)),
|
||||
null !== u) {
|
||||
- if (t = [], yt.test(n.charAt(qt)) ? (r = n.charAt(qt), qt++) : (r = null, 0 === Wt && e(mt)),
|
||||
- null !== r) for (;null !== r; ) t.push(r), yt.test(n.charAt(qt)) ? (r = n.charAt(qt),
|
||||
- if (t = [], yt.test(n.charAt(qt)) ? (r = n.charAt(qt), qt++) : (r = null, 0 === Wt && e(mt)),
|
||||
- null !== r) for (;null !== r; ) t.push(r), yt.test(n.charAt(qt)) ? (r = n.charAt(qt),
|
||||
+ if (t = [], yt.test(n.charAt(qt)) ? (r = n.charAt(qt), qt++) : (r = null, 0 === Wt && e(mt)),
|
||||
+ null !== r) for (;null !== r; ) t.push(r), yt.test(n.charAt(qt)) ? (r = n.charAt(qt),
|
||||
qt++) : (r = null, 0 === Wt && e(mt)); else t = il;
|
||||
- null !== t ? (Lt = l, u = Rt(t), null === u ? (qt = l, l = u) : l = u) : (qt = l,
|
||||
- null !== t ? (Lt = l, u = Rt(t), null === u ? (qt = l, l = u) : l = u) : (qt = l,
|
||||
+ null !== t ? (Lt = l, u = Rt(t), null === u ? (qt = l, l = u) : l = u) : (qt = l,
|
||||
l = il);
|
||||
} else qt = l, l = il;
|
||||
@@ -593,15 +593,15 @@ index 35d5b9af3eff34324656879705dcb81470fc9697..3e6a52e0fbfdd39d3aaf1592ffd19ecd
|
||||
}
|
||||
function nl() {
|
||||
var l, u, t, r;
|
||||
- if (l = qt, n.substr(qt, 2) === Ft ? (u = Ft, qt += 2) : (u = null, 0 === Wt && e(Qt)),
|
||||
- if (l = qt, n.substr(qt, 2) === Ft ? (u = Ft, qt += 2) : (u = null, 0 === Wt && e(Qt)),
|
||||
+ if (l = qt, n.substr(qt, 2) === Ft ? (u = Ft, qt += 2) : (u = null, 0 === Wt && e(Qt)),
|
||||
null !== u) {
|
||||
- if (t = [], St.test(n.charAt(qt)) ? (r = n.charAt(qt), qt++) : (r = null, 0 === Wt && e(Ut)),
|
||||
- null !== r) for (;null !== r; ) t.push(r), St.test(n.charAt(qt)) ? (r = n.charAt(qt),
|
||||
- if (t = [], St.test(n.charAt(qt)) ? (r = n.charAt(qt), qt++) : (r = null, 0 === Wt && e(Ut)),
|
||||
- null !== r) for (;null !== r; ) t.push(r), St.test(n.charAt(qt)) ? (r = n.charAt(qt),
|
||||
+ if (t = [], St.test(n.charAt(qt)) ? (r = n.charAt(qt), qt++) : (r = null, 0 === Wt && e(Ut)),
|
||||
+ null !== r) for (;null !== r; ) t.push(r), St.test(n.charAt(qt)) ? (r = n.charAt(qt),
|
||||
qt++) : (r = null, 0 === Wt && e(Ut)); else t = il;
|
||||
- null !== t ? (Lt = l, u = Et(t), null === u ? (qt = l, l = u) : l = u) : (qt = l,
|
||||
- null !== t ? (Lt = l, u = Et(t), null === u ? (qt = l, l = u) : l = u) : (qt = l,
|
||||
+ null !== t ? (Lt = l, u = Et(t), null === u ? (qt = l, l = u) : l = u) : (qt = l,
|
||||
l = il);
|
||||
} else qt = l, l = il;
|
||||
@@ -609,15 +609,15 @@ index 35d5b9af3eff34324656879705dcb81470fc9697..3e6a52e0fbfdd39d3aaf1592ffd19ecd
|
||||
}
|
||||
function ll() {
|
||||
var l, u, t, r;
|
||||
- if (l = qt, n.substr(qt, 2) === Gt ? (u = Gt, qt += 2) : (u = null, 0 === Wt && e(Bt)),
|
||||
- if (l = qt, n.substr(qt, 2) === Gt ? (u = Gt, qt += 2) : (u = null, 0 === Wt && e(Bt)),
|
||||
+ if (l = qt, n.substr(qt, 2) === Gt ? (u = Gt, qt += 2) : (u = null, 0 === Wt && e(Bt)),
|
||||
null !== u) {
|
||||
- if (t = [], St.test(n.charAt(qt)) ? (r = n.charAt(qt), qt++) : (r = null, 0 === Wt && e(Ut)),
|
||||
- null !== r) for (;null !== r; ) t.push(r), St.test(n.charAt(qt)) ? (r = n.charAt(qt),
|
||||
- if (t = [], St.test(n.charAt(qt)) ? (r = n.charAt(qt), qt++) : (r = null, 0 === Wt && e(Ut)),
|
||||
- null !== r) for (;null !== r; ) t.push(r), St.test(n.charAt(qt)) ? (r = n.charAt(qt),
|
||||
+ if (t = [], St.test(n.charAt(qt)) ? (r = n.charAt(qt), qt++) : (r = null, 0 === Wt && e(Ut)),
|
||||
+ null !== r) for (;null !== r; ) t.push(r), St.test(n.charAt(qt)) ? (r = n.charAt(qt),
|
||||
qt++) : (r = null, 0 === Wt && e(Ut)); else t = il;
|
||||
- null !== t ? (Lt = l, u = jt(t), null === u ? (qt = l, l = u) : l = u) : (qt = l,
|
||||
- null !== t ? (Lt = l, u = jt(t), null === u ? (qt = l, l = u) : l = u) : (qt = l,
|
||||
+ null !== t ? (Lt = l, u = jt(t), null === u ? (qt = l, l = u) : l = u) : (qt = l,
|
||||
l = il);
|
||||
} else qt = l, l = il;
|
||||
@@ -625,15 +625,15 @@ index 35d5b9af3eff34324656879705dcb81470fc9697..3e6a52e0fbfdd39d3aaf1592ffd19ecd
|
||||
}
|
||||
function ul() {
|
||||
var l, u;
|
||||
- return l = qt, n.substr(qt, 2) === Tt ? (u = Tt, qt += 2) : (u = null, 0 === Wt && e(xt)),
|
||||
- return l = qt, n.substr(qt, 2) === Tt ? (u = Tt, qt += 2) : (u = null, 0 === Wt && e(xt)),
|
||||
+ return l = qt, n.substr(qt, 2) === Tt ? (u = Tt, qt += 2) : (u = null, 0 === Wt && e(xt)),
|
||||
null !== u && (Lt = l, u = $t()), null === u ? (qt = l, l = u) : l = u, l;
|
||||
}
|
||||
function tl() {
|
||||
var l, u, t;
|
||||
- return l = qt, 92 === n.charCodeAt(qt) ? (u = At, qt++) : (u = null, 0 === Wt && e(Ct)),
|
||||
- null !== u ? (n.length > qt ? (t = n.charAt(qt), qt++) : (t = null, 0 === Wt && e(vt)),
|
||||
- null !== t ? (Lt = l, u = bu(t), null === u ? (qt = l, l = u) : l = u) : (qt = l,
|
||||
- return l = qt, 92 === n.charCodeAt(qt) ? (u = At, qt++) : (u = null, 0 === Wt && e(Ct)),
|
||||
- null !== u ? (n.length > qt ? (t = n.charAt(qt), qt++) : (t = null, 0 === Wt && e(vt)),
|
||||
- null !== t ? (Lt = l, u = bu(t), null === u ? (qt = l, l = u) : l = u) : (qt = l,
|
||||
+ return l = qt, 92 === n.charCodeAt(qt) ? (u = At, qt++) : (u = null, 0 === Wt && e(Ct)),
|
||||
+ null !== u ? (n.length > qt ? (t = n.charAt(qt), qt++) : (t = null, 0 === Wt && e(vt)),
|
||||
+ null !== t ? (Lt = l, u = bu(t), null === u ? (qt = l, l = u) : l = u) : (qt = l,
|
||||
@@ -644,7 +644,7 @@ index 35d5b9af3eff34324656879705dcb81470fc9697..3e6a52e0fbfdd39d3aaf1592ffd19ecd
|
||||
var Util = __webpack_require__(3)
|
||||
var Random = __webpack_require__(5)
|
||||
/*
|
||||
-
|
||||
-
|
||||
+
|
||||
*/
|
||||
var Handler = {
|
||||
@@ -653,21 +653,21 @@ index 35d5b9af3eff34324656879705dcb81470fc9697..3e6a52e0fbfdd39d3aaf1592ffd19ecd
|
||||
return Random.integer(min, max)
|
||||
},
|
||||
/*
|
||||
-
|
||||
-
|
||||
+
|
||||
*/
|
||||
charset: function(node, result, cache) {
|
||||
// node.invert
|
||||
@@ -7642,11 +7642,11 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
## valid(template, data)
|
||||
|
||||
|
||||
校验真实数据 data 是否与数据模板 template 匹配。
|
||||
-
|
||||
-
|
||||
+
|
||||
实现思路:
|
||||
1. 解析规则。
|
||||
先把数据模板 template 解析为更方便机器解析的 JSON-Schame
|
||||
- name 属性名
|
||||
- name 属性名
|
||||
+ name 属性名
|
||||
type 属性值类型
|
||||
template 属性值模板
|
||||
@@ -675,8 +675,8 @@ index 35d5b9af3eff34324656879705dcb81470fc9697..3e6a52e0fbfdd39d3aaf1592ffd19ecd
|
||||
@@ -7655,7 +7655,7 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
2. 递归验证规则。
|
||||
然后用 JSON-Schema 校验真实数据,校验项包括属性名、值类型、值、值生成规则。
|
||||
|
||||
- 提示信息
|
||||
|
||||
- 提示信息
|
||||
+ 提示信息
|
||||
https://github.com/fge/json-schema-validator/blob/master/src/main/resources/com/github/fge/jsonschema/validator/validation.properties
|
||||
[JSON-Schama validator](http://json-schema-validator.herokuapp.com/)
|
||||
@@ -685,25 +685,25 @@ index 35d5b9af3eff34324656879705dcb81470fc9697..3e6a52e0fbfdd39d3aaf1592ffd19ecd
|
||||
+step
|
||||
整数部分
|
||||
小数部分
|
||||
- boolean
|
||||
- string
|
||||
- boolean
|
||||
- string
|
||||
+ boolean
|
||||
+ string
|
||||
min-max
|
||||
count
|
||||
## properties
|
||||
@@ -7949,9 +7949,9 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
|
||||
|
||||
/*
|
||||
完善、友好的提示信息
|
||||
-
|
||||
-
|
||||
+
|
||||
Equal, not equal to, greater than, less than, greater than or equal to, less than or equal to
|
||||
- 路径 验证类型 描述
|
||||
- 路径 验证类型 描述
|
||||
+ 路径 验证类型 描述
|
||||
|
||||
|
||||
Expect path.name is less than or equal to expected, but path.name is actual.
|
||||
|
||||
|
||||
@@ -8264,7 +8264,7 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
Util.extend(MockXMLHttpRequest.prototype, {
|
||||
// https://xhr.spec.whatwg.org/#the-open()-method
|
||||
@@ -711,22 +711,22 @@ index 35d5b9af3eff34324656879705dcb81470fc9697..3e6a52e0fbfdd39d3aaf1592ffd19ecd
|
||||
- open: function(method, url, async, username, password) {
|
||||
+ open: function(method, url, async = true, username, password) {
|
||||
var that = this
|
||||
|
||||
|
||||
Util.extend(this.custom, {
|
||||
@@ -8310,6 +8310,8 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
var xhr = createNativeXMLHttpRequest()
|
||||
this.custom.xhr = xhr
|
||||
|
||||
|
||||
+ MockXMLHttpRequest.prototype.upload = xhr.upload
|
||||
+
|
||||
// 初始化所有事件,用于监听原生 XHR 对象的事件
|
||||
for (var i = 0; i < XHR_EVENTS.length; i++) {
|
||||
xhr.addEventListener(XHR_EVENTS[i], handle)
|
||||
@@ -8360,6 +8362,7 @@ return /******/ (function(modules) { // webpackBootstrap
|
||||
|
||||
|
||||
// 原生 XHR
|
||||
if (!this.match) {
|
||||
+ this.custom.xhr.responseType = this.responseType || ''
|
||||
this.custom.xhr.send(data)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
9711
pnpm-lock.yaml
generated
9711
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1 +1 @@
|
||||
<svg viewBox="0 0 160 160" xmlns="http://www.w3.org/2000/svg"><path d="M81.28 55.9c-.1-11.67-2.93-22.55-9.37-32.38-1-1.5-2.14-2.86-2.5-4.71a8.1 8.1 0 014-8.61 7.89 7.89 0 019.3 1.23 35.999 35.999 0 015.9 8.83 75.18 75.18 0 018.44 28.58 83.211 83.211 0 01-5.23 36.74 102.983 102.983 0 01-3 7.28 1.2 1.2 0 000 1.41c9.58 13.3 21.76 23 37.85 27.24a54.37 54.37 0 0019.68 1.57 7.72 7.72 0 018.36 6.9 7.903 7.903 0 01-6.7 9 64.744 64.744 0 01-23-1.33 77.68 77.68 0 01-36.93-19.88 93.628 93.628 0 01-11.91-13.71 2.18 2.18 0 00-2.3-1.06 72.744 72.744 0 00-27.38 7.55c-11.6 6-20.67 14.58-26.4 26.45a10.134 10.134 0 01-3.7 4.7 8 8 0 01-9.19-.7 7.86 7.86 0 01-2.36-9.28 60.324 60.324 0 018.72-14.52c12.2-15.43 28.21-24.59 47.32-28.57A85.085 85.085 0 0173.07 87c.524.015 1-.307 1.18-.8a76.06 76.06 0 006.53-22.3c.351-2.652.518-5.325.5-8z" fill="#1890ff"/><path d="M136.26 108.34a44.742 44.742 0 01-11.13-2.87 46.108 46.108 0 01-19.66-13.76 8 8 0 015.72-13.22 7.93 7.93 0 016.54 2.93 33.27 33.27 0 0018.87 10.75c1.546.155 3.058.553 4.48 1.18a8.08 8.08 0 013.84 9.21c-.92 3.52-4.13 5.81-8.66 5.78zm-80.6-75.02a7.61 7.61 0 016.64 5 49.139 49.139 0 013.64 17 46.33 46.33 0 01-2.46 17.28c-2 5.77-8.24 7.79-12.89 4.15a8.1 8.1 0 01-2.39-9 31.679 31.679 0 001.68-12.36 35.77 35.77 0 00-2.43-11c-2.1-5.45 1.75-11.07 8.21-11.07zm22.26 93.25a8 8 0 01-6.68 7.86 32.88 32.88 0 00-19.7 12.19 8.13 8.13 0 01-11.21 1.62 8 8 0 01-1.41-11.58A51.043 51.043 0 0154 123.81a45.842 45.842 0 0114-5.1c5.35-1.04 9.91 2.56 9.92 7.86z" fill="#1890ff"/></svg>
|
||||
<svg viewBox="0 0 160 160" xmlns="http://www.w3.org/2000/svg"><path d="M81.28 55.9c-.1-11.67-2.93-22.55-9.37-32.38-1-1.5-2.14-2.86-2.5-4.71a8.1 8.1 0 014-8.61 7.89 7.89 0 019.3 1.23 35.999 35.999 0 015.9 8.83 75.18 75.18 0 018.44 28.58 83.211 83.211 0 01-5.23 36.74 102.983 102.983 0 01-3 7.28 1.2 1.2 0 000 1.41c9.58 13.3 21.76 23 37.85 27.24a54.37 54.37 0 0019.68 1.57 7.72 7.72 0 018.36 6.9 7.903 7.903 0 01-6.7 9 64.744 64.744 0 01-23-1.33 77.68 77.68 0 01-36.93-19.88 93.628 93.628 0 01-11.91-13.71 2.18 2.18 0 00-2.3-1.06 72.744 72.744 0 00-27.38 7.55c-11.6 6-20.67 14.58-26.4 26.45a10.134 10.134 0 01-3.7 4.7 8 8 0 01-9.19-.7 7.86 7.86 0 01-2.36-9.28 60.324 60.324 0 018.72-14.52c12.2-15.43 28.21-24.59 47.32-28.57A85.085 85.085 0 0173.07 87c.524.015 1-.307 1.18-.8a76.06 76.06 0 006.53-22.3c.351-2.652.518-5.325.5-8z" fill="#646cff"/><path d="M136.26 108.34a44.742 44.742 0 01-11.13-2.87 46.108 46.108 0 01-19.66-13.76 8 8 0 015.72-13.22 7.93 7.93 0 016.54 2.93 33.27 33.27 0 0018.87 10.75c1.546.155 3.058.553 4.48 1.18a8.08 8.08 0 013.84 9.21c-.92 3.52-4.13 5.81-8.66 5.78zm-80.6-75.02a7.61 7.61 0 016.64 5 49.139 49.139 0 013.64 17 46.33 46.33 0 01-2.46 17.28c-2 5.77-8.24 7.79-12.89 4.15a8.1 8.1 0 01-2.39-9 31.679 31.679 0 001.68-12.36 35.77 35.77 0 00-2.43-11c-2.1-5.45 1.75-11.07 8.21-11.07zm22.26 93.25a8 8 0 01-6.68 7.86 32.88 32.88 0 00-19.7 12.19 8.13 8.13 0 01-11.21 1.62 8 8 0 01-1.41-11.58A51.043 51.043 0 0154 123.81a45.842 45.842 0 0114-5.1c5.35-1.04 9.91 2.56 9.92 7.86z" fill="#646cff"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
@@ -54,7 +54,7 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
placeholderClass: 'bg-white dark:bg-dark transition-background-color duration-300 ease-in-out',
|
||||
emptyDesc: '暂无数据',
|
||||
iconClass: 'text-320px text-primary',
|
||||
descClass: 'text-16px text-[#666]',
|
||||
descClass: 'text-16px text-#666',
|
||||
showNetworkReload: false
|
||||
});
|
||||
|
||||
|
||||
@@ -4,25 +4,23 @@
|
||||
<div class="w-56px h-56px my-36px">
|
||||
<div class="relative h-full animate-spin">
|
||||
<div
|
||||
v-for="(item, index) in lodingClasses"
|
||||
v-for="(item, index) in loadingClasses"
|
||||
:key="index"
|
||||
class="absolute w-16px h-16px bg-primary rounded-8px animate-pulse"
|
||||
:class="item"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
<h2 class="text-28px font-500 text-#646464">{{ title }}</h2>
|
||||
<h2 class="text-28px font-500 text-#646464">{{ $t('system.title') }}</h2>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useAppInfo } from '@/composables';
|
||||
import { localStg, getRgbOfColor } from '@/utils';
|
||||
import { sessionStg, getRgbOfColor } from '@/utils';
|
||||
import { $t } from '@/locales';
|
||||
import themeSettings from '@/settings/theme.json';
|
||||
|
||||
const { title } = useAppInfo();
|
||||
|
||||
const lodingClasses = [
|
||||
const loadingClasses = [
|
||||
'left-0 top-0',
|
||||
'left-0 bottom-0 animate-delay-500',
|
||||
'right-0 top-0 animate-delay-1000',
|
||||
@@ -31,7 +29,7 @@ const lodingClasses = [
|
||||
|
||||
function addThemeColorCssVars() {
|
||||
const defaultColor = themeSettings.themeColor;
|
||||
const themeColor = localStg.get('themeColor') || defaultColor;
|
||||
const themeColor = sessionStg.get('themeColor') || defaultColor;
|
||||
|
||||
const { r, g, b } = getRgbOfColor(themeColor);
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div
|
||||
class="dark:bg-[#18181c] dark:text-white dark:text-opacity-82 transition-all"
|
||||
:class="inverted ? 'bg-[#001428] text-white' : 'bg-white text-[#333639]'"
|
||||
class="dark:bg-dark dark:text-white dark:text-opacity-82 transition-all"
|
||||
:class="inverted ? 'bg-#001428 text-white' : 'bg-white text-#333639'"
|
||||
>
|
||||
<slot></slot>
|
||||
</div>
|
||||
|
||||
@@ -13,6 +13,8 @@ defineOptions({ name: 'DarkModeSwitch' });
|
||||
interface Props {
|
||||
/** 暗黑模式 */
|
||||
dark?: boolean;
|
||||
/** 自定义暗黑模式动画过渡 */
|
||||
customizeTransition?: boolean;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
@@ -34,9 +36,54 @@ const darkMode = computed({
|
||||
}
|
||||
});
|
||||
|
||||
function handleSwitch() {
|
||||
darkMode.value = !darkMode.value;
|
||||
async function handleSwitch(event: MouseEvent) {
|
||||
const x = event.clientX;
|
||||
const y = event.clientY;
|
||||
|
||||
if (!props.customizeTransition || !document.startViewTransition) {
|
||||
darkMode.value = !darkMode.value;
|
||||
return;
|
||||
}
|
||||
|
||||
const endRadius = Math.hypot(Math.max(x, innerWidth - x), Math.max(y, innerHeight - y));
|
||||
|
||||
const transition = document.startViewTransition(() => {
|
||||
darkMode.value = !darkMode.value;
|
||||
});
|
||||
|
||||
await transition.ready;
|
||||
|
||||
const clipPath = [`circle(0px at ${x}px ${y}px)`, `circle(${endRadius}px at ${x}px ${y}px)`];
|
||||
|
||||
document.documentElement.animate(
|
||||
{
|
||||
clipPath: darkMode.value ? clipPath : [...clipPath].reverse()
|
||||
},
|
||||
{
|
||||
duration: 300,
|
||||
easing: 'ease-in',
|
||||
pseudoElement: darkMode.value ? '::view-transition-new(root)' : '::view-transition-old(root)'
|
||||
}
|
||||
);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
<style>
|
||||
::view-transition-old(root),
|
||||
::view-transition-new(root) {
|
||||
animation: none;
|
||||
mix-blend-mode: normal;
|
||||
}
|
||||
::view-transition-old(root) {
|
||||
z-index: 9999;
|
||||
}
|
||||
::view-transition-new(root) {
|
||||
z-index: 1;
|
||||
}
|
||||
.dark::view-transition-old(root) {
|
||||
z-index: 1;
|
||||
}
|
||||
.dark::view-transition-new(root) {
|
||||
z-index: 9999;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="flex-col-center wh-full">
|
||||
<div class="text-400px text-primary">
|
||||
<div class="flex-col-center gap-24px min-h-520px wh-full overflow-hidden">
|
||||
<div class="flex text-400px text-primary">
|
||||
<icon-local-no-permission v-if="type === '403'" />
|
||||
<icon-local-not-found v-if="type === '404'" />
|
||||
<icon-local-service-error v-if="type === '500'" />
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
<div v-if="showTooltip">
|
||||
<n-tooltip :placement="placement" trigger="hover">
|
||||
<template #trigger>
|
||||
<div class="flex-center h-full cursor-pointer dark:hover:bg-[#333]" :class="contentClassName">
|
||||
<div class="flex-center h-full cursor-pointer dark:hover:bg-#333" :class="contentClassName">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
{{ tooltipContent }}
|
||||
</n-tooltip>
|
||||
</div>
|
||||
<div v-else class="flex-center cursor-pointer dark:hover:bg-[#333]" :class="contentClassName">
|
||||
<div v-else class="flex-center cursor-pointer dark:hover:bg-#333" :class="contentClassName">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
@@ -41,7 +41,7 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
const showTooltip = computed(() => Boolean(props.tooltipContent));
|
||||
|
||||
const contentClassName = computed(
|
||||
() => `${props.contentClass} ${props.inverted ? 'hover:bg-primary' : 'hover:bg-[#f6f6f6]'}`
|
||||
() => `${props.contentClass} ${props.inverted ? 'hover:bg-primary' : 'hover:bg-#f6f6f6'}`
|
||||
);
|
||||
</script>
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import { isNumber } from '@/utils';
|
||||
|
||||
defineOptions({ name: 'CountTo' });
|
||||
|
||||
type TansitionKey = keyof typeof TransitionPresets;
|
||||
type TransitionKey = keyof typeof TransitionPresets;
|
||||
|
||||
interface Props {
|
||||
/** 初始值 */
|
||||
@@ -32,7 +32,7 @@ interface Props {
|
||||
/** 使用缓冲动画函数 */
|
||||
useEasing?: boolean;
|
||||
/** 缓冲动画函数类型 */
|
||||
transition?: TansitionKey;
|
||||
transition?: TransitionKey;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
<template>
|
||||
<web-site-link label="github地址:" :link="link" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import WebSiteLink from './web-site-link.vue';
|
||||
|
||||
defineOptions({ name: 'GithubLink' });
|
||||
|
||||
interface Props {
|
||||
/** github链接 */
|
||||
link: string;
|
||||
}
|
||||
|
||||
defineProps<Props>();
|
||||
</script>
|
||||
<style scoped></style>
|
||||
@@ -1,77 +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">
|
||||
<span v-for="iconItem in iconsList" :key="iconItem" @click="handleChange(iconItem)">
|
||||
<svg-icon
|
||||
:icon="iconItem"
|
||||
class="border-1px border-[#d9d9d9] text-30px m-2px p-5px cursor-pointer"
|
||||
:class="{ 'border-primary': modelValue === iconItem }"
|
||||
/>
|
||||
</span>
|
||||
</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>
|
||||
@@ -37,13 +37,13 @@ const bindAttrs = computed<{ class: string; style: string }>(() => ({
|
||||
}));
|
||||
|
||||
const symbolId = computed(() => {
|
||||
const { VITE_ICON_LOCAL_PREFFIX: preffix } = import.meta.env;
|
||||
const { VITE_ICON_LOCAL_PREFIX: prefix } = import.meta.env;
|
||||
|
||||
const defaultLocalIcon = 'no-icon';
|
||||
|
||||
const icon = props.localIcon || defaultLocalIcon;
|
||||
|
||||
return `#${preffix}-${icon}`;
|
||||
return `#${prefix}-${icon}`;
|
||||
});
|
||||
|
||||
/** 渲染本地icon */
|
||||
|
||||
@@ -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,174 +0,0 @@
|
||||
import { nextTick, effectScope, onScopeDispose, 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?.clear();
|
||||
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 scope = effectScope();
|
||||
|
||||
scope.run(() => {
|
||||
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();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
watch(
|
||||
options,
|
||||
newValue => {
|
||||
update(newValue);
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
watch(
|
||||
() => theme.darkMode,
|
||||
() => {
|
||||
updateTheme();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
onScopeDispose(() => {
|
||||
destroy();
|
||||
scope.stop();
|
||||
});
|
||||
|
||||
return {
|
||||
domRef
|
||||
};
|
||||
}
|
||||
@@ -1,14 +1,34 @@
|
||||
import { effectScope, onScopeDispose, watch } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useEventListener } from '@vueuse/core';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useTabStore, useThemeStore } from '@/store';
|
||||
|
||||
/** 全局事件 */
|
||||
export function useGlobalEvents() {
|
||||
const theme = useThemeStore();
|
||||
const tab = useTabStore();
|
||||
const route = useRoute();
|
||||
const { locale, t } = useI18n();
|
||||
const scope = effectScope();
|
||||
|
||||
/** 页面离开时缓存多页签数据 */
|
||||
useEventListener(window, 'beforeunload', () => {
|
||||
theme.cacheThemeSettings();
|
||||
tab.cacheTabRoutes();
|
||||
});
|
||||
|
||||
scope.run(() => {
|
||||
// 国际化切换时更新浏览器标签文本
|
||||
watch(
|
||||
() => locale.value,
|
||||
() => {
|
||||
document.title = route.meta.i18nTitle ? t(route.meta.i18nTitle) : route.meta.title;
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
onScopeDispose(() => {
|
||||
scope.stop();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { h } from 'vue';
|
||||
import SvgIcon from '~/src/components/custom/svg-icon.vue';
|
||||
import SvgIcon from '@/components/custom/svg-icon.vue';
|
||||
|
||||
/**
|
||||
* 图标渲染
|
||||
@@ -48,7 +48,7 @@ export const useIconRender = () => {
|
||||
}
|
||||
|
||||
if (!icon && !localIcon) {
|
||||
window.console.warn('没有传递图标名称,请确保给icon或localIcon传递有效值!');
|
||||
throw Error('没有传递图标名称,请确保给icon或localIcon传递有效值!');
|
||||
}
|
||||
|
||||
return () => h(SvgIcon, { icon, localIcon, style });
|
||||
|
||||
@@ -2,5 +2,4 @@ export * from './system';
|
||||
export * from './router';
|
||||
export * from './layout';
|
||||
export * from './events';
|
||||
export * from './echarts';
|
||||
export * from './icon';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { computed } from 'vue';
|
||||
import { computed, watch } from 'vue';
|
||||
import { breakpointsTailwind, useBreakpoints } from '@vueuse/core';
|
||||
import { useAppStore, useThemeStore } from '@/store';
|
||||
|
||||
@@ -63,6 +63,16 @@ export function useBasicLayout() {
|
||||
return w;
|
||||
});
|
||||
|
||||
watch(
|
||||
isMobile,
|
||||
newValue => {
|
||||
if (newValue) {
|
||||
app.setSiderCollapse(true);
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
return {
|
||||
mode,
|
||||
isMobile,
|
||||
|
||||
@@ -2,26 +2,6 @@ import UAParser from 'ua-parser-js';
|
||||
import { useAuthStore } from '@/store';
|
||||
import { isArray, isString } from '@/utils';
|
||||
|
||||
interface AppInfo {
|
||||
/** 项目名称 */
|
||||
name: string;
|
||||
/** 项目标题 */
|
||||
title: string;
|
||||
/** 项目描述 */
|
||||
desc: string;
|
||||
}
|
||||
|
||||
/** 项目信息 */
|
||||
export function useAppInfo(): AppInfo {
|
||||
const { VITE_APP_NAME: name, VITE_APP_TITLE: title, VITE_APP_DESC: desc } = import.meta.env;
|
||||
|
||||
return {
|
||||
name,
|
||||
title,
|
||||
desc
|
||||
};
|
||||
}
|
||||
|
||||
/** 获取设备信息 */
|
||||
export function useDeviceInfo() {
|
||||
const parser = new UAParser();
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
export * from './service';
|
||||
export * from './regexp';
|
||||
export * from './map-sdk';
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
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';
|
||||
export const AMAP_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';
|
||||
|
||||
6
src/constants/_shared.ts
Normal file
6
src/constants/_shared.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export function transformObjectToOption<T extends object>(obj: T) {
|
||||
return Object.entries(obj).map(([value, label]) => ({
|
||||
value,
|
||||
label
|
||||
})) as Common.OptionWithKey<keyof T>[];
|
||||
}
|
||||
@@ -1,45 +1,17 @@
|
||||
import { $t } from '@/locales';
|
||||
import { transformObjectToOption } from './_shared';
|
||||
|
||||
export const loginModuleLabels: Record<UnionKey.LoginModule, string> = {
|
||||
'pwd-login': '账密登录',
|
||||
'code-login': '手机验证码登录',
|
||||
register: '注册',
|
||||
'reset-pwd': '重置密码',
|
||||
'bind-wechat': '微信绑定'
|
||||
'pwd-login': $t('page.login.pwdLogin.title'),
|
||||
'code-login': $t('page.login.codeLogin.title'),
|
||||
register: $t('page.login.register.title'),
|
||||
'reset-pwd': $t('page.login.resetPwd.title'),
|
||||
'bind-wechat': $t('page.login.bindWeChat.title')
|
||||
};
|
||||
|
||||
export const userRoleLabels: Record<Auth.RoleType, string> = {
|
||||
super: '超级管理员',
|
||||
admin: '管理员',
|
||||
user: '普通用户'
|
||||
super: $t('page.login.pwdLogin.superAdmin'),
|
||||
admin: $t('page.login.pwdLogin.admin'),
|
||||
user: $t('page.login.pwdLogin.user')
|
||||
};
|
||||
|
||||
export const userRoleOptions: Common.OptionWithKey<Auth.RoleType>[] = [
|
||||
{ value: 'super', label: userRoleLabels.super },
|
||||
{ value: 'admin', label: userRoleLabels.admin },
|
||||
{ value: 'user', label: userRoleLabels.user }
|
||||
];
|
||||
|
||||
/** 用户性别 */
|
||||
export const genderLabels: Record<UserManagement.GenderKey, string> = {
|
||||
0: '女',
|
||||
1: '男'
|
||||
};
|
||||
|
||||
export const genderOptions: Common.OptionWithKey<UserManagement.GenderKey>[] = [
|
||||
{ value: '0', label: genderLabels['0'] },
|
||||
{ value: '1', label: genderLabels['1'] }
|
||||
];
|
||||
|
||||
/** 用户状态 */
|
||||
export const userStatusLabels: Record<UserManagement.UserStatusKey, string> = {
|
||||
1: '启用',
|
||||
2: '禁用',
|
||||
3: '冻结',
|
||||
4: '软删除'
|
||||
};
|
||||
|
||||
export const userStatusOptions: Common.OptionWithKey<UserManagement.UserStatusKey>[] = [
|
||||
{ value: '1', label: userStatusLabels['1'] },
|
||||
{ value: '2', label: userStatusLabels['2'] },
|
||||
{ value: '3', label: userStatusLabels['3'] },
|
||||
{ value: '4', label: userStatusLabels['4'] }
|
||||
];
|
||||
export const userRoleOptions = transformObjectToOption(userRoleLabels);
|
||||
|
||||
@@ -1,81 +1,31 @@
|
||||
import { transformObjectToOption } from './_shared';
|
||||
|
||||
export const themeLayoutModeLabels: Record<UnionKey.ThemeLayoutMode, string> = {
|
||||
vertical: '左侧菜单模式',
|
||||
horizontal: '顶部菜单模式',
|
||||
'vertical-mix': '左侧菜单混合模式',
|
||||
'horizontal-mix': '顶部菜单混合模式'
|
||||
};
|
||||
|
||||
export const themeLayoutModeOptions: Common.OptionWithKey<UnionKey.ThemeLayoutMode>[] = [
|
||||
{
|
||||
value: 'vertical',
|
||||
label: themeLayoutModeLabels.vertical
|
||||
},
|
||||
{
|
||||
value: 'horizontal',
|
||||
label: themeLayoutModeLabels.horizontal
|
||||
},
|
||||
{
|
||||
value: 'vertical-mix',
|
||||
label: themeLayoutModeLabels['vertical-mix']
|
||||
},
|
||||
{
|
||||
value: 'horizontal-mix',
|
||||
label: themeLayoutModeLabels['horizontal-mix']
|
||||
}
|
||||
];
|
||||
export const themeLayoutModeOptions = transformObjectToOption(themeLayoutModeLabels);
|
||||
|
||||
export const themeScrollModeLabels: Record<UnionKey.ThemeScrollMode, string> = {
|
||||
wrapper: '外层滚动',
|
||||
content: '主体滚动'
|
||||
};
|
||||
|
||||
export const themeScrollModeOptions: Common.OptionWithKey<UnionKey.ThemeScrollMode>[] = [
|
||||
{
|
||||
value: 'wrapper',
|
||||
label: themeScrollModeLabels.wrapper
|
||||
},
|
||||
{
|
||||
value: 'content',
|
||||
label: themeScrollModeLabels.content
|
||||
}
|
||||
];
|
||||
export const themeScrollModeOptions = transformObjectToOption(themeScrollModeLabels);
|
||||
|
||||
export const themeTabModeLabels: Record<UnionKey.ThemeTabMode, string> = {
|
||||
chrome: '谷歌风格',
|
||||
button: '按钮风格'
|
||||
};
|
||||
|
||||
export const themeTabModeOptions: Common.OptionWithKey<UnionKey.ThemeTabMode>[] = [
|
||||
{
|
||||
value: 'chrome',
|
||||
label: themeTabModeLabels.chrome
|
||||
},
|
||||
{
|
||||
value: 'button',
|
||||
label: themeTabModeLabels.button
|
||||
}
|
||||
];
|
||||
export const themeTabModeOptions = transformObjectToOption(themeTabModeLabels);
|
||||
|
||||
export const themeHorizontalMenuPositionLabels: Record<UnionKey.ThemeHorizontalMenuPosition, string> = {
|
||||
'flex-start': '居左',
|
||||
center: '居中',
|
||||
'flex-end': '居右'
|
||||
};
|
||||
|
||||
export const themeHorizontalMenuPositionOptions: Common.OptionWithKey<UnionKey.ThemeHorizontalMenuPosition>[] = [
|
||||
{
|
||||
value: 'flex-start',
|
||||
label: themeHorizontalMenuPositionLabels['flex-start']
|
||||
},
|
||||
{
|
||||
value: 'center',
|
||||
label: themeHorizontalMenuPositionLabels.center
|
||||
},
|
||||
{
|
||||
value: 'flex-end',
|
||||
label: themeHorizontalMenuPositionLabels['flex-end']
|
||||
}
|
||||
];
|
||||
export const themeHorizontalMenuPositionOptions = transformObjectToOption(themeHorizontalMenuPositionLabels);
|
||||
|
||||
export const themeAnimateModeLabels: Record<UnionKey.ThemeAnimateMode, string> = {
|
||||
'zoom-fade': '渐变',
|
||||
@@ -85,30 +35,4 @@ export const themeAnimateModeLabels: Record<UnionKey.ThemeAnimateMode, string> =
|
||||
'fade-bottom': '底部消退',
|
||||
'fade-scale': '缩放消退'
|
||||
};
|
||||
|
||||
export const themeAnimateModeOptions: Common.OptionWithKey<UnionKey.ThemeAnimateMode>[] = [
|
||||
{
|
||||
value: 'zoom-fade',
|
||||
label: themeAnimateModeLabels['zoom-fade']
|
||||
},
|
||||
{
|
||||
value: 'zoom-out',
|
||||
label: themeAnimateModeLabels['zoom-out']
|
||||
},
|
||||
{
|
||||
value: 'fade-slide',
|
||||
label: themeAnimateModeLabels['fade-slide']
|
||||
},
|
||||
{
|
||||
value: 'fade',
|
||||
label: themeAnimateModeLabels.fade
|
||||
},
|
||||
{
|
||||
value: 'fade-bottom',
|
||||
label: themeAnimateModeLabels['fade-bottom']
|
||||
},
|
||||
{
|
||||
value: 'fade-scale',
|
||||
label: themeAnimateModeLabels['fade-scale']
|
||||
}
|
||||
];
|
||||
export const themeAnimateModeOptions = transformObjectToOption(themeAnimateModeLabels);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import useCountDown from './use-count-down';
|
||||
import useSmsCode from './use-sms-code';
|
||||
import useImageVerify from './use-image-verify';
|
||||
|
||||
export { useCountDown, useSmsCode, useImageVerify };
|
||||
export { useCountDown, useSmsCode };
|
||||
|
||||
180
src/hooks/business/use-hook-table.ts
Normal file
180
src/hooks/business/use-hook-table.ts
Normal file
@@ -0,0 +1,180 @@
|
||||
import { ref, reactive } from 'vue';
|
||||
import type { Ref } from 'vue';
|
||||
import type { PaginationProps, DataTableBaseColumn, DataTableSelectionColumn, DataTableExpandColumn } from 'naive-ui';
|
||||
import type { TableColumnGroup } from 'naive-ui/es/data-table/src/interface';
|
||||
import { useLoadingEmpty } from '../common';
|
||||
|
||||
/**
|
||||
* 接口请求函数
|
||||
*/
|
||||
type ApiFn<T = any, R = any> = (args: T) => Promise<Service.RequestResult<R>>;
|
||||
|
||||
/**
|
||||
* 接口请求函数的参数
|
||||
*/
|
||||
type GetApiFnParameters<T extends ApiFn, R = any> = T extends (args: infer P) => Promise<Service.RequestResult<R>>
|
||||
? P
|
||||
: never;
|
||||
|
||||
/**
|
||||
* 接口请求函数的返回值
|
||||
*/
|
||||
type GetApiFnReturnType<T extends ApiFn, P = any> = T extends (args: P) => Promise<Service.RequestResult<infer R>>
|
||||
? R
|
||||
: never;
|
||||
|
||||
/**
|
||||
* 表格接口请求后转换后的数据
|
||||
*/
|
||||
type Transformer<TableData, Response> = (response: Response) => {
|
||||
data: TableData[];
|
||||
pageNum: number;
|
||||
pageSize: number;
|
||||
total: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* 列表接口参数更新
|
||||
*/
|
||||
type ApiParamsUpdater<P, R> = (params: P) => R;
|
||||
|
||||
/**
|
||||
* 分页参数
|
||||
*/
|
||||
type PagePropsOfPagination = Pick<PaginationProps, 'page' | 'pageSize'>;
|
||||
|
||||
/**
|
||||
* 自定义的列 key
|
||||
*/
|
||||
type CustomColumnKey<K = never> = K | 'action';
|
||||
|
||||
/**
|
||||
* 表格的列
|
||||
*/
|
||||
type HookTableColumn<T = Record<string, unknown>> =
|
||||
| (Omit<TableColumnGroup<T>, 'key'> & { key: CustomColumnKey<keyof T> })
|
||||
| (Omit<DataTableBaseColumn<T>, 'key'> & { key: CustomColumnKey<keyof T> })
|
||||
| DataTableSelectionColumn<T>
|
||||
| DataTableExpandColumn<T>;
|
||||
|
||||
/**
|
||||
* 表格配置
|
||||
*/
|
||||
type HookTableConfig<TableData, Fn extends ApiFn> = {
|
||||
/**
|
||||
* 列表接口参数
|
||||
*/
|
||||
apiParams: GetApiFnParameters<Fn>;
|
||||
/**
|
||||
* 列表接口返回数据转换
|
||||
*/
|
||||
transformer: Transformer<TableData, GetApiFnReturnType<Fn>>;
|
||||
/**
|
||||
* 列表列
|
||||
*/
|
||||
columns: () => HookTableColumn<TableData>[];
|
||||
/**
|
||||
* 列表接口参数更新
|
||||
* @description 用于更新分页参数, 如果列表接口的参数不包含同名分页参数属性 `page` 和 `pageSize`, 需要通过此函数更新
|
||||
* @default p => p
|
||||
*/
|
||||
apiParamsUpdater?: ApiParamsUpdater<GetApiFnParameters<Fn> & Partial<PagePropsOfPagination>, GetApiFnParameters<Fn>>;
|
||||
/**
|
||||
* 列表分页参数
|
||||
*/
|
||||
pagination?: PaginationProps;
|
||||
/**
|
||||
* 是否立即请求
|
||||
* @default true
|
||||
*/
|
||||
immediate?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* 通用表格 hook
|
||||
* @param apiFn 接口请求函数
|
||||
* @param config 表格配置
|
||||
*/
|
||||
export default function useHookTable<TableData, Fn extends ApiFn>(apiFn: Fn, config: HookTableConfig<TableData, Fn>) {
|
||||
const { loading, startLoading, endLoading, empty, setEmpty } = useLoadingEmpty();
|
||||
|
||||
const { apiParams, transformer, apiParamsUpdater = p => p, immediate = true } = config;
|
||||
|
||||
const data: Ref<TableData[]> = ref([]);
|
||||
|
||||
function updateData(update: TableData[]) {
|
||||
data.value = update;
|
||||
}
|
||||
|
||||
const columns = ref(config.columns()) as Ref<HookTableColumn<TableData>[]>;
|
||||
|
||||
const requestParams = ref(apiParams) as Ref<HookTableConfig<TableData, Fn>['apiParams']>;
|
||||
|
||||
function updateRequestParamsByPagination(p: PagePropsOfPagination) {
|
||||
requestParams.value = apiParamsUpdater({ ...requestParams.value, ...p });
|
||||
}
|
||||
|
||||
const pagination = reactive({
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
showSizePicker: true,
|
||||
pageSizes: [10, 15, 20, 25, 30],
|
||||
onChange: (page: number) => {
|
||||
pagination.page = page;
|
||||
|
||||
updateRequestParamsByPagination({ page });
|
||||
getData();
|
||||
},
|
||||
onUpdatePageSize: (pageSize: number) => {
|
||||
pagination.pageSize = pageSize;
|
||||
pagination.page = 1;
|
||||
|
||||
updateRequestParamsByPagination({ pageSize });
|
||||
getData();
|
||||
},
|
||||
...config.pagination
|
||||
}) as PaginationProps;
|
||||
|
||||
function updatePagination(update: Partial<PaginationProps>) {
|
||||
Object.assign(pagination, update);
|
||||
|
||||
updateRequestParamsByPagination({ page: pagination.page, pageSize: pagination.pageSize });
|
||||
}
|
||||
|
||||
async function getData() {
|
||||
startLoading();
|
||||
|
||||
const { data: apiData, error } = await apiFn(requestParams.value);
|
||||
|
||||
if (!error && data) {
|
||||
const { data: tableData, pageNum, pageSize, total } = transformer(apiData);
|
||||
|
||||
updateData(tableData);
|
||||
|
||||
setEmpty(tableData.length === 0);
|
||||
|
||||
updatePagination({ page: pageNum, pageSize, itemCount: total });
|
||||
}
|
||||
|
||||
endLoading();
|
||||
}
|
||||
|
||||
function reloadColumns() {
|
||||
columns.value = config.columns();
|
||||
}
|
||||
|
||||
if (immediate) {
|
||||
getData();
|
||||
}
|
||||
|
||||
return {
|
||||
data,
|
||||
columns,
|
||||
loading,
|
||||
empty,
|
||||
pagination,
|
||||
getData,
|
||||
updatePagination,
|
||||
reloadColumns
|
||||
};
|
||||
}
|
||||
@@ -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,89 +0,0 @@
|
||||
import { ref, reactive } from 'vue';
|
||||
import type { Ref } from 'vue';
|
||||
import type { DataTableBaseColumn, DataTableSelectionColumn, DataTableExpandColumn, PaginationProps } from 'naive-ui';
|
||||
import type { MaybeComputedRef } from '@vueuse/core';
|
||||
import type { TableColumnGroup, InternalRowData } from 'naive-ui/es/data-table/src/interface';
|
||||
import { useLoadingEmpty } from '../common';
|
||||
|
||||
/**
|
||||
* 表格的列
|
||||
*/
|
||||
type DataTableColumn<T = InternalRowData> =
|
||||
| (Omit<TableColumnGroup<T>, 'key'> & { key: keyof T })
|
||||
| (Omit<DataTableBaseColumn<T>, 'key'> & { key: keyof T })
|
||||
| DataTableSelectionColumn<T>
|
||||
| DataTableExpandColumn<T>;
|
||||
|
||||
/**
|
||||
* 表格分页参数
|
||||
*/
|
||||
type TablePaginationParams = Pick<PaginationProps, 'page' | 'pageSize'>;
|
||||
|
||||
/**
|
||||
* 表格接口的请求参数
|
||||
*/
|
||||
type TableApiParams = Record<string, unknown> & TablePaginationParams;
|
||||
|
||||
/**
|
||||
* 表格接口的请求数据
|
||||
*/
|
||||
type TableApiData<T = InternalRowData> = {
|
||||
data: T[];
|
||||
pageNum: number;
|
||||
pageSize: number;
|
||||
total: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* 表格接口的请求函数
|
||||
*/
|
||||
type TableApiFn<P extends TableApiParams, T extends InternalRowData> = (
|
||||
params: P
|
||||
) => Promise<Service.SuccessResult<TableApiData<T>>>;
|
||||
|
||||
export function useNaiveTable<TableData extends InternalRowData, P extends TableApiParams>(
|
||||
apiFn: TableApiFn<P, TableData>,
|
||||
apiParams: P,
|
||||
columns: MaybeComputedRef<DataTableColumn<TableData>[]>
|
||||
) {
|
||||
const { loading, startLoading, endLoading, empty, setEmpty } = useLoadingEmpty();
|
||||
|
||||
const tableData: Ref<TableData[]> = ref([]);
|
||||
|
||||
async function getTableData(paginationParams?: TablePaginationParams) {
|
||||
startLoading();
|
||||
|
||||
const params = { ...apiParams, ...paginationParams };
|
||||
|
||||
const { data } = await apiFn(params);
|
||||
if (data) {
|
||||
tableData.value = data.data;
|
||||
|
||||
setEmpty(data.data.length === 0);
|
||||
}
|
||||
endLoading();
|
||||
}
|
||||
|
||||
const pagination: PaginationProps = reactive({
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
showSizePicker: true,
|
||||
pageSizes: [10, 15, 20, 25, 30],
|
||||
onChange: (page: number) => {
|
||||
pagination.page = page;
|
||||
},
|
||||
onUpdatePageSize: (pageSize: number) => {
|
||||
pagination.pageSize = pageSize;
|
||||
pagination.page = 1;
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
tableData,
|
||||
columns,
|
||||
loading,
|
||||
empty,
|
||||
pagination,
|
||||
start: getTableData
|
||||
};
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
<template>
|
||||
<admin-layout
|
||||
:mode="mode"
|
||||
:is-mobile="isMobile"
|
||||
:scroll-mode="theme.scrollMode"
|
||||
:scroll-el-id="app.scrollElId"
|
||||
:full-content="app.contentFull"
|
||||
@@ -15,6 +16,8 @@
|
||||
:sider-collapsed-width="siderCollapsedWidth"
|
||||
:footer-visible="theme.footer.visible"
|
||||
:fixed-footer="theme.footer.fixed"
|
||||
:right-footer="theme.footer.right"
|
||||
@click-mobile-sider-mask="app.setSiderCollapse(true)"
|
||||
>
|
||||
<template #header>
|
||||
<global-header v-bind="headerProps" />
|
||||
@@ -30,7 +33,7 @@
|
||||
<global-footer />
|
||||
</template>
|
||||
</admin-layout>
|
||||
<global-back-top />
|
||||
<n-back-top :key="theme.scrollMode" :listen-to="`#${app.scrollElId}`" class="z-100" />
|
||||
<setting-drawer />
|
||||
</template>
|
||||
|
||||
@@ -38,22 +41,22 @@
|
||||
import { AdminLayout } from '@soybeanjs/vue-materials';
|
||||
import { useAppStore, useThemeStore } from '@/store';
|
||||
import { useBasicLayout } from '@/composables';
|
||||
import {
|
||||
GlobalBackTop,
|
||||
GlobalContent,
|
||||
GlobalFooter,
|
||||
GlobalHeader,
|
||||
GlobalSider,
|
||||
GlobalTab,
|
||||
SettingDrawer
|
||||
} from '../common';
|
||||
import { GlobalContent, GlobalFooter, GlobalHeader, GlobalSider, GlobalTab, SettingDrawer } from '../common';
|
||||
|
||||
defineOptions({ name: 'BasicLayout' });
|
||||
|
||||
const app = useAppStore();
|
||||
const theme = useThemeStore();
|
||||
|
||||
const { mode, headerProps, siderVisible, siderWidth, siderCollapsedWidth } = useBasicLayout();
|
||||
const { mode, isMobile, headerProps, siderVisible, siderWidth, siderCollapsedWidth } = useBasicLayout();
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
<style lang="scss">
|
||||
#__SCROLL_EL_ID__ {
|
||||
@include scrollbar(8px, #e1e1e1);
|
||||
}
|
||||
|
||||
.dark #__SCROLL_EL_ID__ {
|
||||
@include scrollbar(8px, #555);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
<template>
|
||||
<n-back-top :show="show" class="z-1000" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue';
|
||||
import { useWindowScroll } from '@vueuse/core';
|
||||
|
||||
defineOptions({ name: 'GlobalBackTop' });
|
||||
|
||||
const { y: scrollY } = useWindowScroll();
|
||||
|
||||
const show = computed(() => scrollY.value > 180);
|
||||
</script>
|
||||
<style scoped></style>
|
||||
@@ -13,7 +13,7 @@
|
||||
v-if="app.reloadFlag"
|
||||
:key="route.fullPath"
|
||||
:class="{ 'p-16px': showPadding }"
|
||||
class="flex-grow bg-[#f6f9f8] dark:bg-[#101014] transition duration-300 ease-in-out"
|
||||
class="flex-grow bg-#f6f9f8 dark:bg-#101014 transition duration-300 ease-in-out"
|
||||
/>
|
||||
</keep-alive>
|
||||
</transition>
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
<template>
|
||||
<dark-mode-container class="flex-center h-full">
|
||||
<dark-mode-container class="flex-center h-full" :inverted="theme.footer.inverted">
|
||||
<span>Copyright ©2021 Soybean Admin</span>
|
||||
</dark-mode-container>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useThemeStore } from '@/store';
|
||||
|
||||
defineOptions({ name: 'GlobalFooter' });
|
||||
|
||||
const theme = useThemeStore();
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
|
||||
@@ -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>
|
||||
@@ -2,7 +2,7 @@
|
||||
<n-breadcrumb class="px-12px">
|
||||
<template v-for="breadcrumb in breadcrumbs" :key="breadcrumb.key">
|
||||
<n-breadcrumb-item>
|
||||
<n-dropdown v-if="breadcrumb.hasChildren" :options="breadcrumb.children" @select="dropdownSelect">
|
||||
<n-dropdown v-if="breadcrumb.hasChildren" :options="breadcrumb.options" @select="dropdownSelect">
|
||||
<span>
|
||||
<component
|
||||
:is="breadcrumb.icon"
|
||||
@@ -19,7 +19,9 @@
|
||||
class="inline-block align-text-bottom mr-4px text-16px"
|
||||
:class="{ 'text-#BBBBBB': theme.header.inverted }"
|
||||
/>
|
||||
<span :class="{ 'text-#BBBBBB': theme.header.inverted }">{{ breadcrumb.label }}</span>
|
||||
<span :class="{ 'text-#BBBBBB': theme.header.inverted }">
|
||||
{{ breadcrumb.label }}
|
||||
</span>
|
||||
</template>
|
||||
</n-breadcrumb-item>
|
||||
</template>
|
||||
@@ -33,6 +35,7 @@ import { routePath } from '@/router';
|
||||
import { useRouteStore, useThemeStore } from '@/store';
|
||||
import { useRouterPush } from '@/composables';
|
||||
import { getBreadcrumbByRouteKey } from '@/utils';
|
||||
import { $t } from '@/locales';
|
||||
|
||||
defineOptions({ name: 'GlobalBreadcrumb' });
|
||||
|
||||
@@ -42,7 +45,13 @@ const routeStore = useRouteStore();
|
||||
const { routerPush } = useRouterPush();
|
||||
|
||||
const breadcrumbs = computed(() =>
|
||||
getBreadcrumbByRouteKey(route.name as string, routeStore.menus as App.GlobalMenuOption[], routePath('root'))
|
||||
getBreadcrumbByRouteKey(route.name as string, routeStore.menus as App.GlobalMenuOption[], routePath('root')).map(
|
||||
item => ({
|
||||
...item,
|
||||
label: item.i18nTitle ? $t(item.i18nTitle) : item.label,
|
||||
options: item.options?.map(oItem => ({ ...oItem, label: oItem.i18nTitle ? $t(oItem.i18nTitle) : oItem.label }))
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
function dropdownSelect(key: string) {
|
||||
|
||||
@@ -20,6 +20,7 @@ import { useRoute } from 'vue-router';
|
||||
import type { MenuOption } from 'naive-ui';
|
||||
import { useRouteStore, useThemeStore } from '@/store';
|
||||
import { useRouterPush } from '@/composables';
|
||||
import { translateMenuLabel } from '@/utils';
|
||||
|
||||
defineOptions({ name: 'HeaderMenu' });
|
||||
|
||||
@@ -28,7 +29,7 @@ const routeStore = useRouteStore();
|
||||
const theme = useThemeStore();
|
||||
const { routerPush } = useRouterPush();
|
||||
|
||||
const menus = computed(() => routeStore.menus as App.GlobalMenuOption[]);
|
||||
const menus = computed(() => translateMenuLabel(routeStore.menus as App.GlobalMenuOption[]));
|
||||
const activeKey = computed(() => (route.meta?.activeMenu ? route.meta.activeMenu : route.name) as string);
|
||||
|
||||
function handleUpdateMenu(_key: string, item: MenuOption) {
|
||||
|
||||
@@ -1,21 +1,10 @@
|
||||
import MenuCollapse from './menu-collapse.vue';
|
||||
import GlobalBreadcrumb from './global-breadcrumb.vue';
|
||||
import HeaderMenu from './header-menu.vue';
|
||||
import GithubSite from './github-site.vue';
|
||||
import FullScreen from './full-screen.vue';
|
||||
import ThemeMode from './theme-mode.vue';
|
||||
import UserAvatar from './user-avatar.vue';
|
||||
import SystemMessage from './system-message.vue';
|
||||
import SettingButton from './setting-button.vue';
|
||||
import ToggleLang from './toggle-lang.vue';
|
||||
|
||||
export {
|
||||
MenuCollapse,
|
||||
GlobalBreadcrumb,
|
||||
HeaderMenu,
|
||||
GithubSite,
|
||||
FullScreen,
|
||||
ThemeMode,
|
||||
UserAvatar,
|
||||
SystemMessage,
|
||||
SettingButton
|
||||
};
|
||||
export { MenuCollapse, GlobalBreadcrumb, HeaderMenu, FullScreen, ThemeMode, UserAvatar, SettingButton, ToggleLang };
|
||||
|
||||
@@ -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 './message-list.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,6 +1,11 @@
|
||||
<template>
|
||||
<hover-container class="w-40px" :inverted="theme.header.inverted" tooltip-content="主题模式">
|
||||
<dark-mode-switch :dark="theme.darkMode" class="wh-full" @update:dark="theme.setDarkMode" />
|
||||
<dark-mode-switch
|
||||
:dark="theme.darkMode"
|
||||
:customize-transition="theme.isCustomizeDarkModeTransition"
|
||||
class="wh-full"
|
||||
@update:dark="theme.setDarkMode"
|
||||
/>
|
||||
</hover-container>
|
||||
</template>
|
||||
|
||||
|
||||
39
src/layouts/common/global-header/components/toggle-lang.vue
Normal file
39
src/layouts/common/global-header/components/toggle-lang.vue
Normal file
@@ -0,0 +1,39 @@
|
||||
<template>
|
||||
<hover-container class="w-40px h-full" :inverted="theme.header.inverted">
|
||||
<n-dropdown :options="options" trigger="hover" :value="language" @select="handleSelect">
|
||||
<icon-cil:language class="text-18px outline-transparent" />
|
||||
</n-dropdown>
|
||||
</hover-container>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useThemeStore } from '@/store';
|
||||
import { localStg } from '@/utils';
|
||||
|
||||
const theme = useThemeStore();
|
||||
const { locale } = useI18n();
|
||||
|
||||
const language = ref<I18nType.LangType>(localStg.get('lang') || 'zh-CN');
|
||||
const options = [
|
||||
{
|
||||
label: '中文',
|
||||
key: 'zh-CN'
|
||||
},
|
||||
{
|
||||
label: 'English',
|
||||
key: 'en'
|
||||
},
|
||||
{
|
||||
label: 'ភាសាខ្មែរ',
|
||||
key: 'km-KH'
|
||||
}
|
||||
];
|
||||
const handleSelect = (key: string) => {
|
||||
language.value = key as I18nType.LangType;
|
||||
locale.value = key;
|
||||
localStg.set('lang', key as I18nType.LangType);
|
||||
};
|
||||
</script>
|
||||
<style scoped></style>
|
||||
@@ -7,11 +7,9 @@
|
||||
</div>
|
||||
<header-menu v-else />
|
||||
<div class="flex justify-end h-full">
|
||||
<global-search />
|
||||
<github-site />
|
||||
<full-screen />
|
||||
<theme-mode />
|
||||
<system-message />
|
||||
<toggle-lang />
|
||||
<setting-button v-if="showButton" />
|
||||
<user-avatar />
|
||||
</div>
|
||||
@@ -22,17 +20,15 @@
|
||||
import { useThemeStore } from '@/store';
|
||||
import { useBasicLayout } from '@/composables';
|
||||
import GlobalLogo from '../global-logo/index.vue';
|
||||
import GlobalSearch from '../global-search/index.vue';
|
||||
import {
|
||||
FullScreen,
|
||||
GithubSite,
|
||||
GlobalBreadcrumb,
|
||||
HeaderMenu,
|
||||
MenuCollapse,
|
||||
SettingButton,
|
||||
SystemMessage,
|
||||
ThemeMode,
|
||||
UserAvatar
|
||||
UserAvatar,
|
||||
ToggleLang
|
||||
} from './components';
|
||||
|
||||
defineOptions({ name: 'GlobalHeader' });
|
||||
|
||||
@@ -1,19 +1,15 @@
|
||||
<template>
|
||||
<router-link :to="routeHomePath" class="flex-center w-full nowrap-hidden">
|
||||
<system-logo class="text-32px text-primary" />
|
||||
<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') }}
|
||||
<h2 v-show="showTitle" class="pl-8px text-16px font-bold text-primary transition duration-300 ease-in-out">
|
||||
{{ $t('system.title') }}
|
||||
</h2>
|
||||
</router-link>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { routePath } from '@/router';
|
||||
import { t, setLocale } from '@/locales';
|
||||
import { $t } from '@/locales';
|
||||
|
||||
defineOptions({ name: 'GlobalLogo' });
|
||||
|
||||
@@ -25,12 +21,6 @@ interface Props {
|
||||
defineProps<Props>();
|
||||
|
||||
const routeHomePath = routePath('root');
|
||||
|
||||
let flag = true;
|
||||
function toggleLocal() {
|
||||
flag = !flag;
|
||||
setLocale(flag ? 'en' : 'zh-CN');
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
import SearchModal from './search-modal.vue';
|
||||
|
||||
export { SearchModal };
|
||||
@@ -22,6 +22,9 @@ defineOptions({ name: 'SearchFooter' });
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.icon {
|
||||
box-shadow: inset 0 -2px #cdcde6, inset 0 0 1px 1px #fff, 0 1px 2px 1px #1e235a66;
|
||||
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 './search-result.vue';
|
||||
import SearchFooter from './search-footer.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,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>
|
||||
@@ -6,7 +6,7 @@
|
||||
>
|
||||
<component :is="icon" :class="[isMini ? 'text-16px' : 'text-20px']" />
|
||||
<p
|
||||
class="text-12px overflow-hidden transition-height duration-300 ease-in-out"
|
||||
class="w-full text-center ellipsis-text text-12px transition-height duration-300 ease-in-out"
|
||||
:class="[isMini ? 'h-0 pt-0' : 'h-24px pt-4px']"
|
||||
>
|
||||
{{ label }}
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import type { VNodeChild } from 'vue';
|
||||
import type { Component } from 'vue';
|
||||
import { useBoolean } from '@/hooks';
|
||||
|
||||
defineOptions({ name: 'MixMenuDetail' });
|
||||
@@ -30,7 +30,7 @@ interface Props {
|
||||
/** 当前激活状态的理由名称 */
|
||||
activeRouteName: string;
|
||||
/** 路由图标 */
|
||||
icon?: () => VNodeChild;
|
||||
icon?: Component;
|
||||
/** mini尺寸的路由 */
|
||||
isMini?: boolean;
|
||||
}
|
||||
|
||||
@@ -5,10 +5,11 @@
|
||||
>
|
||||
<dark-mode-container
|
||||
class="drawer-shadow absolute-lt flex-col-stretch h-full nowrap-hidden"
|
||||
:inverted="theme.sider.inverted"
|
||||
:style="{ width: showDrawer ? theme.sider.mixChildMenuWidth + 'px' : '0px' }"
|
||||
>
|
||||
<header class="header-height flex-y-center justify-between" :style="{ height: theme.header.height + 'px' }">
|
||||
<h2 class="text-primary pl-8px text-16px font-bold">{{ title }}</h2>
|
||||
<h2 class="text-primary pl-8px text-16px font-bold">{{ $t('system.title') }}</h2>
|
||||
<div class="px-8px text-16px text-gray-600 cursor-pointer" @click="app.toggleMixSiderFixed">
|
||||
<icon-mdi-pin-off v-if="app.mixSiderFixed" />
|
||||
<icon-mdi-pin v-else />
|
||||
@@ -20,6 +21,7 @@
|
||||
:options="menus"
|
||||
:expanded-keys="expandedKeys"
|
||||
:indent="18"
|
||||
:inverted="!theme.darkMode && theme.sider.inverted"
|
||||
@update:value="handleUpdateMenu"
|
||||
@update:expanded-keys="handleUpdateExpandedKeys"
|
||||
/>
|
||||
@@ -33,8 +35,9 @@ import { computed, ref, watch } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import type { MenuOption } from 'naive-ui';
|
||||
import { useAppStore, useThemeStore } from '@/store';
|
||||
import { useAppInfo, useRouterPush } from '@/composables';
|
||||
import { useRouterPush } from '@/composables';
|
||||
import { getActiveKeyPathsOfMenus } from '@/utils';
|
||||
import { $t } from '@/locales';
|
||||
|
||||
defineOptions({ name: 'MixMenuDrawer' });
|
||||
|
||||
@@ -51,7 +54,6 @@ const route = useRoute();
|
||||
const app = useAppStore();
|
||||
const theme = useThemeStore();
|
||||
const { routerPush } = useRouterPush();
|
||||
const { title } = useAppInfo();
|
||||
|
||||
const showDrawer = computed(() => (props.visible && props.menus.length) || app.mixSiderFixed);
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<dark-mode-container class="flex h-full" :inverted="theme.sider.inverted" @mouseleave="resetFirstDegreeMenus">
|
||||
<div class="flex-1 flex-col-stretch h-full">
|
||||
<div class="flex-1-hidden flex-col-stretch h-full">
|
||||
<global-logo :show-title="false" :style="{ height: theme.header.height + 'px' }" />
|
||||
<n-scrollbar class="flex-1-hidden">
|
||||
<mix-menu-detail
|
||||
@@ -26,7 +26,9 @@ import { useRoute } from 'vue-router';
|
||||
import { useAppStore, useRouteStore, useThemeStore } from '@/store';
|
||||
import { useRouterPush } from '@/composables';
|
||||
import { useBoolean } from '@/hooks';
|
||||
import { translateMenuLabel } from '@/utils';
|
||||
import { GlobalLogo } from '@/layouts/common';
|
||||
import { $t } from '@/locales';
|
||||
import { MixMenuCollapse, MixMenuDetail, MixMenuDrawer } from './components';
|
||||
|
||||
defineOptions({ name: 'VerticalMixSider' });
|
||||
@@ -45,13 +47,13 @@ function setActiveParentRouteName(routeName: string) {
|
||||
|
||||
const firstDegreeMenus = computed(() =>
|
||||
routeStore.menus.map(item => {
|
||||
const { routeName, label } = item;
|
||||
const { routeName, label, i18nTitle } = item;
|
||||
const icon = item?.icon;
|
||||
const hasChildren = Boolean(item.children && item.children.length);
|
||||
|
||||
return {
|
||||
routeName,
|
||||
label,
|
||||
label: i18nTitle ? $t(i18nTitle) : label,
|
||||
icon,
|
||||
hasChildren
|
||||
};
|
||||
@@ -88,7 +90,7 @@ const activeChildMenus = computed(() => {
|
||||
routeStore.menus.some(item => {
|
||||
const flag = item.routeName === activeParentRouteName.value && Boolean(item.children?.length);
|
||||
if (flag) {
|
||||
menus.push(...(item.children || []));
|
||||
menus.push(...translateMenuLabel((item.children || []) as App.GlobalMenuOption[]));
|
||||
}
|
||||
return flag;
|
||||
});
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
:options="menus"
|
||||
:expanded-keys="expandedKeys"
|
||||
:indent="18"
|
||||
:inverted="theme.sider.inverted"
|
||||
:inverted="!theme.darkMode && theme.sider.inverted"
|
||||
@update:value="handleUpdateMenu"
|
||||
@update:expanded-keys="handleUpdateExpandedKeys"
|
||||
/>
|
||||
@@ -21,7 +21,7 @@ import { useRoute } from 'vue-router';
|
||||
import type { MenuOption } from 'naive-ui';
|
||||
import { useAppStore, useRouteStore, useThemeStore } from '@/store';
|
||||
import { useRouterPush } from '@/composables';
|
||||
import { getActiveKeyPathsOfMenus } from '@/utils';
|
||||
import { getActiveKeyPathsOfMenus, translateMenuLabel } from '@/utils';
|
||||
|
||||
defineOptions({ name: 'VerticalMenu' });
|
||||
|
||||
@@ -31,7 +31,7 @@ const theme = useThemeStore();
|
||||
const routeStore = useRouteStore();
|
||||
const { routerPush } = useRouterPush();
|
||||
|
||||
const menus = computed(() => routeStore.menus as App.GlobalMenuOption[]);
|
||||
const menus = computed(() => translateMenuLabel(routeStore.menus as App.GlobalMenuOption[]));
|
||||
|
||||
const activeKey = computed(() => (route.meta?.activeMenu ? route.meta.activeMenu : route.name) as string);
|
||||
const expandedKeys = ref<string[]>([]);
|
||||
|
||||
@@ -6,26 +6,21 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useAppStore, useRouteStore } from '@/store';
|
||||
import { useRouteStore } from '@/store';
|
||||
import { useLoading } from '@/hooks';
|
||||
|
||||
defineOptions({ name: 'ReloadButton' });
|
||||
|
||||
const app = useAppStore();
|
||||
const routeStore = useRouteStore();
|
||||
const { reCacheRoute } = useRouteStore();
|
||||
const route = useRoute();
|
||||
const { loading, startLoading, endLoading } = useLoading();
|
||||
|
||||
function handleRefresh() {
|
||||
const isCached = routeStore.cacheRoutes.includes(String(route.name));
|
||||
if (isCached) {
|
||||
routeStore.removeCacheRoute(route.name as AuthRoute.AllRouteKey);
|
||||
}
|
||||
async function handleRefresh() {
|
||||
startLoading();
|
||||
app.reloadPage();
|
||||
|
||||
await reCacheRoute(route.name as AuthRoute.AllRouteKey);
|
||||
|
||||
setTimeout(() => {
|
||||
if (isCached) {
|
||||
routeStore.addCacheRoute(route.name as AuthRoute.AllRouteKey);
|
||||
}
|
||||
endLoading();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div ref="tabRef" class="flex h-full pr-18px" :class="[isChromeMode ? 'items-end' : 'items-center gap-12px']">
|
||||
<AdminTab
|
||||
<PageTab
|
||||
v-for="item in tab.tabs"
|
||||
:key="item.fullPath"
|
||||
:mode="theme.tab.mode"
|
||||
@@ -19,8 +19,8 @@
|
||||
class="inline-block align-text-bottom text-16px"
|
||||
/>
|
||||
</template>
|
||||
{{ item.meta.title }}
|
||||
</AdminTab>
|
||||
{{ item.meta.i18nTitle ? $t(item.meta.i18nTitle) : item.meta.title }}
|
||||
</PageTab>
|
||||
</div>
|
||||
<context-menu
|
||||
:visible="dropdown.visible"
|
||||
@@ -34,8 +34,9 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, nextTick, reactive, ref, watch } from 'vue';
|
||||
import { AdminTab } from '@soybeanjs/vue-materials';
|
||||
import { PageTab } from '@soybeanjs/vue-materials';
|
||||
import { useTabStore, useThemeStore } from '@/store';
|
||||
import { $t } from '@/locales';
|
||||
import { ContextMenu } from './components';
|
||||
|
||||
defineOptions({ name: 'TabDetail' });
|
||||
|
||||
@@ -5,6 +5,5 @@ import GlobalSider from './global-sider/index.vue';
|
||||
import GlobalContent from './global-content/index.vue';
|
||||
import GlobalFooter from './global-footer/index.vue';
|
||||
import GlobalLogo from './global-logo/index.vue';
|
||||
import GlobalBackTop from './global-back-top/index.vue';
|
||||
|
||||
export { SettingDrawer, GlobalHeader, GlobalTab, GlobalSider, GlobalContent, GlobalFooter, GlobalLogo, GlobalBackTop };
|
||||
export { SettingDrawer, GlobalHeader, GlobalTab, GlobalSider, GlobalContent, GlobalFooter, GlobalLogo };
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<n-divider title-placement="center">主题模式</n-divider>
|
||||
<n-divider title-placement="center">{{ $t('layout.settingDrawer.themeModeTitle') }}</n-divider>
|
||||
<n-space vertical size="large">
|
||||
<setting-menu label="深色主题">
|
||||
<setting-menu :label="$t('layout.settingDrawer.darkMode')">
|
||||
<n-switch :value="theme.darkMode" @update:value="theme.setDarkMode">
|
||||
<template #checked>
|
||||
<icon-mdi-white-balance-sunny class="text-14px text-white" />
|
||||
@@ -11,7 +11,7 @@
|
||||
</template>
|
||||
</n-switch>
|
||||
</setting-menu>
|
||||
<setting-menu label="跟随系统">
|
||||
<setting-menu :label="$t('layout.settingDrawer.followSystemTheme')">
|
||||
<n-switch :value="theme.followSystemTheme" @update:value="theme.setFollowSystemTheme">
|
||||
<template #checked>
|
||||
<icon-ic-baseline-do-not-disturb class="text-14px text-white" />
|
||||
@@ -21,17 +21,31 @@
|
||||
</template>
|
||||
</n-switch>
|
||||
</setting-menu>
|
||||
<setting-menu label="侧边栏深色主题">
|
||||
<setting-menu :label="$t('layout.settingDrawer.isCustomizeDarkModeTransition')">
|
||||
<n-switch :value="theme.isCustomizeDarkModeTransition" @update:value="theme.setIsCustomizeDarkModeTransition">
|
||||
<template #checked>
|
||||
<icon-ic-baseline-do-not-disturb class="text-14px text-white" />
|
||||
</template>
|
||||
<template #unchecked>
|
||||
<icon-ic-round-hdr-auto class="text-14px text-white" />
|
||||
</template>
|
||||
</n-switch>
|
||||
</setting-menu>
|
||||
<setting-menu :label="$t('layout.settingDrawer.sider.inverted')">
|
||||
<n-switch :value="theme.sider.inverted" @update:value="theme.setSiderInverted" />
|
||||
</setting-menu>
|
||||
<setting-menu label="头部深色主题">
|
||||
<setting-menu :label="$t('layout.settingDrawer.header.inverted')">
|
||||
<n-switch :value="theme.header.inverted" @update:value="theme.setHeaderInverted" />
|
||||
</setting-menu>
|
||||
<setting-menu :label="$t('layout.settingDrawer.footer.inverted')">
|
||||
<n-switch :value="theme.footer.inverted" @update:value="theme.setFooterInverted" />
|
||||
</setting-menu>
|
||||
</n-space>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useThemeStore } from '@/store';
|
||||
import { $t } from '@/locales';
|
||||
import SettingMenu from '../setting-menu/index.vue';
|
||||
|
||||
defineOptions({ name: 'DarkMode' });
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
<n-tooltip :placement="activeConfig.placement" trigger="hover">
|
||||
<template #trigger>
|
||||
<div class="layout-checkbox__shadow relative w-56px h-48px bg-white rounded-4px overflow-hidden">
|
||||
<div class="absolute-lt bg-[#273352]" :class="activeConfig.menuClass"></div>
|
||||
<div class="absolute-rb bg-[#f0f2f5]" :class="activeConfig.mainClass"></div>
|
||||
<div class="absolute-lt bg-#273352" :class="activeConfig.menuClass"></div>
|
||||
<div class="absolute-rb bg-#f0f2f5" :class="activeConfig.mainClass"></div>
|
||||
</div>
|
||||
</template>
|
||||
<span>{{ label }}</span>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<n-divider title-placement="center">布局模式</n-divider>
|
||||
<n-divider title-placement="center">{{ $t('layout.settingDrawer.layoutModelTitle') }}</n-divider>
|
||||
<n-space justify="space-around" :wrap="true" :size="24" class="px-12px">
|
||||
<layout-card
|
||||
v-for="item in theme.layout.modeList"
|
||||
@@ -43,6 +43,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useThemeStore } from '@/store';
|
||||
import { $t } from '@/locales';
|
||||
import { LayoutCard } from './components';
|
||||
|
||||
defineOptions({ name: 'LayoutMode' });
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<n-divider title-placement="center">界面功能</n-divider>
|
||||
<n-divider title-placement="center">{{ $t('layout.settingDrawer.pageFunctionsTitle') }}</n-divider>
|
||||
<n-space vertical size="large">
|
||||
<setting-menu label="滚动模式">
|
||||
<setting-menu :label="$t('layout.settingDrawer.scrollMode')">
|
||||
<n-select
|
||||
class="w-120px"
|
||||
size="small"
|
||||
@@ -10,10 +10,10 @@
|
||||
@update:value="theme.setScrollMode"
|
||||
/>
|
||||
</setting-menu>
|
||||
<setting-menu label="固定头部和多页签">
|
||||
<setting-menu :label="$t('layout.settingDrawer.fixedHeaderAndTab')">
|
||||
<n-switch :value="theme.fixedHeaderAndTab" @update:value="theme.setIsFixedHeaderAndTab" />
|
||||
</setting-menu>
|
||||
<setting-menu label="顶部菜单位置">
|
||||
<setting-menu :label="$t('layout.settingDrawer.menu.horizontalPosition')">
|
||||
<n-select
|
||||
class="w-120px"
|
||||
size="small"
|
||||
@@ -22,7 +22,7 @@
|
||||
@update:value="theme.setHorizontalMenuPosition"
|
||||
/>
|
||||
</setting-menu>
|
||||
<setting-menu label="头部高度">
|
||||
<setting-menu :label="$t('layout.settingDrawer.header.height')">
|
||||
<n-input-number
|
||||
class="w-120px"
|
||||
size="small"
|
||||
@@ -31,7 +31,7 @@
|
||||
@update:value="theme.setHeaderHeight"
|
||||
/>
|
||||
</setting-menu>
|
||||
<setting-menu label="多页签高度">
|
||||
<setting-menu :label="$t('layout.settingDrawer.tab.height')">
|
||||
<n-input-number
|
||||
class="w-120px"
|
||||
size="small"
|
||||
@@ -40,10 +40,10 @@
|
||||
@update:value="theme.setTabHeight"
|
||||
/>
|
||||
</setting-menu>
|
||||
<setting-menu label="多页签缓存">
|
||||
<setting-menu :label="$t('layout.settingDrawer.tab.isCache')">
|
||||
<n-switch :value="theme.tab.isCache" @update:value="theme.setTabIsCache" />
|
||||
</setting-menu>
|
||||
<setting-menu label="侧边栏展开宽度">
|
||||
<setting-menu :label="$t('layout.settingDrawer.sider.width')">
|
||||
<n-input-number
|
||||
class="w-120px"
|
||||
size="small"
|
||||
@@ -52,7 +52,7 @@
|
||||
@update:value="theme.setSiderWidth"
|
||||
/>
|
||||
</setting-menu>
|
||||
<setting-menu label="左侧混合侧边栏展开宽度">
|
||||
<setting-menu :label="$t('layout.settingDrawer.sider.mixWidth')">
|
||||
<n-input-number
|
||||
class="w-120px"
|
||||
size="small"
|
||||
@@ -61,17 +61,21 @@
|
||||
@update:value="theme.setMixSiderWidth"
|
||||
/>
|
||||
</setting-menu>
|
||||
<setting-menu label="固定底部">
|
||||
<setting-menu :label="$t('layout.settingDrawer.footer.visible')">
|
||||
<n-switch :value="theme.footer.visible" @update:value="theme.setFooterVisible" />
|
||||
</setting-menu>
|
||||
<setting-menu :label="$t('layout.settingDrawer.footer.fixed')">
|
||||
<n-switch :value="theme.footer.fixed" @update:value="theme.setFooterIsFixed" />
|
||||
</setting-menu>
|
||||
<setting-menu label="显示底部">
|
||||
<n-switch :value="theme.footer.visible" @update:value="theme.setFooterVisible" />
|
||||
<setting-menu :label="$t('layout.settingDrawer.footer.right')">
|
||||
<n-switch :value="theme.footer.right" @update:value="theme.setFooterIsRight" />
|
||||
</setting-menu>
|
||||
</n-space>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useThemeStore } from '@/store';
|
||||
import { $t } from '@/locales';
|
||||
import SettingMenu from '../setting-menu/index.vue';
|
||||
|
||||
defineOptions({ name: 'PageFunc' });
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
<template>
|
||||
<n-divider title-placement="center">界面显示</n-divider>
|
||||
<n-divider title-placement="center">{{ $t('layout.settingDrawer.pageViewTitle') }}</n-divider>
|
||||
<n-space vertical size="large">
|
||||
<setting-menu label="面包屑">
|
||||
<setting-menu :label="$t('layout.settingDrawer.header.crumb.visible')">
|
||||
<n-switch :value="theme.header.crumb.visible" @update:value="theme.setHeaderCrumbVisible" />
|
||||
</setting-menu>
|
||||
<setting-menu label="面包屑图标">
|
||||
<setting-menu :label="$t('layout.settingDrawer.header.crumb.icon')">
|
||||
<n-switch :value="theme.header.crumb.showIcon" @update:value="theme.setHeaderCrumbIconVisible" />
|
||||
</setting-menu>
|
||||
<setting-menu label="多页签">
|
||||
<setting-menu :label="$t('layout.settingDrawer.tab.visible')">
|
||||
<n-switch :value="theme.tab.visible" @update:value="theme.setTabVisible" />
|
||||
</setting-menu>
|
||||
<setting-menu label="多页签风格">
|
||||
<setting-menu :label="$t('layout.settingDrawer.tab.modeList.mode')">
|
||||
<n-select
|
||||
class="w-120px"
|
||||
size="small"
|
||||
@@ -19,10 +19,10 @@
|
||||
@update:value="theme.setTabMode"
|
||||
/>
|
||||
</setting-menu>
|
||||
<setting-menu label="页面切换动画">
|
||||
<setting-menu :label="$t('layout.settingDrawer.page.animate')">
|
||||
<n-switch :value="theme.page.animate" @update:value="theme.setPageIsAnimate" />
|
||||
</setting-menu>
|
||||
<setting-menu label="页面切换动画类型">
|
||||
<setting-menu :label="$t('layout.settingDrawer.page.animateMode')">
|
||||
<n-select
|
||||
class="w-120px"
|
||||
size="small"
|
||||
@@ -36,6 +36,7 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useThemeStore } from '@/store';
|
||||
import { $t } from '@/locales';
|
||||
import SettingMenu from '../setting-menu/index.vue';
|
||||
|
||||
defineOptions({ name: 'PageView' });
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<n-divider title-placement="center">系统主题</n-divider>
|
||||
<n-divider title-placement="center">{{ $t('layout.settingDrawer.systemThemeTitle') }}</n-divider>
|
||||
<n-grid :cols="8" :x-gap="8" :y-gap="12">
|
||||
<n-grid-item v-for="color in theme.themeColorList" :key="color" class="flex-x-center">
|
||||
<color-checkbox :color="color" :checked="color === theme.themeColor" @click="theme.setThemeColor(color)" />
|
||||
@@ -7,7 +7,9 @@
|
||||
</n-grid>
|
||||
<n-space :vertical="true" class="pt-12px">
|
||||
<n-color-picker :value="theme.themeColor" :show-alpha="false" @update-value="theme.setThemeColor" />
|
||||
<n-button :block="true" :type="otherColorBtnType" @click="openModal">更多颜色</n-button>
|
||||
<n-button :block="true" :type="otherColorBtnType" @click="openModal">
|
||||
{{ $t('layout.settingDrawer.systemTheme.moreColors') }}
|
||||
</n-button>
|
||||
</n-space>
|
||||
<color-modal :visible="visible" @close="closeModal" />
|
||||
</template>
|
||||
@@ -17,6 +19,7 @@ import { computed } from 'vue';
|
||||
import { isInTraditionColors } from '@/settings';
|
||||
import { useThemeStore } from '@/store';
|
||||
import { useBoolean } from '@/hooks';
|
||||
import { $t } from '@/locales';
|
||||
import { ColorCheckbox, ColorModal } from './components';
|
||||
|
||||
defineOptions({ name: 'ThemeColorSelect' });
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
<template>
|
||||
<n-divider title-placement="center">主题配置</n-divider>
|
||||
<n-divider title-placement="center">{{ $t('layout.settingDrawer.themeConfiguration.title') }}</n-divider>
|
||||
<textarea id="themeConfigCopyTarget" v-model="dataClipboardText" class="absolute opacity-0" />
|
||||
<n-space vertical>
|
||||
<div ref="copyRef" data-clipboard-target="#themeConfigCopyTarget">
|
||||
<n-button type="primary" :block="true">拷贝当前配置</n-button>
|
||||
<n-button type="primary" :block="true">{{ $t('layout.settingDrawer.themeConfiguration.copy') }}</n-button>
|
||||
</div>
|
||||
<n-button type="warning" :block="true" @click="handleResetConfig">重置当前配置</n-button>
|
||||
<n-button type="warning" :block="true" @click="handleResetConfig">
|
||||
{{ $t('layout.settingDrawer.themeConfiguration.reset') }}
|
||||
</n-button>
|
||||
</n-space>
|
||||
</template>
|
||||
|
||||
@@ -13,6 +15,7 @@
|
||||
import { onMounted, onUnmounted, ref, watch } from 'vue';
|
||||
import Clipboard from 'clipboard';
|
||||
import { useThemeStore } from '@/store';
|
||||
import { $t } from '@/locales';
|
||||
|
||||
defineOptions({ name: 'ThemeConfig' });
|
||||
|
||||
@@ -28,7 +31,7 @@ function getClipboardText() {
|
||||
|
||||
function handleResetConfig() {
|
||||
theme.resetThemeStore();
|
||||
window.$message?.success('已重置配置,请重新拷贝!');
|
||||
window.$message?.success($t('layout.settingDrawer.themeConfiguration.resetSuccess'));
|
||||
}
|
||||
|
||||
function clipboardEventListener() {
|
||||
@@ -36,9 +39,9 @@ function clipboardEventListener() {
|
||||
const copy = new Clipboard(copyRef.value);
|
||||
copy.on('success', () => {
|
||||
window.$dialog?.success({
|
||||
title: '操作成功',
|
||||
content: '复制成功,请替换 src/settings/theme.json的内容!',
|
||||
positiveText: '确定'
|
||||
title: $t('layout.settingDrawer.themeConfiguration.operateSuccess'),
|
||||
content: $t('layout.settingDrawer.themeConfiguration.copySuccess'),
|
||||
positiveText: $t('layout.settingDrawer.themeConfiguration.confirmCopy')
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<n-drawer :show="app.settingDrawerVisible" display-directive="show" :width="330" @mask-click="app.closeSettingDrawer">
|
||||
<n-drawer-content title="主题配置" :native-scrollbar="false">
|
||||
<n-drawer-content :title="$t('layout.settingDrawer.title')" :native-scrollbar="false">
|
||||
<dark-mode />
|
||||
<layout-mode />
|
||||
<theme-color-select />
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useAppStore } from '@/store';
|
||||
import { $t } from '@/locales';
|
||||
import { DarkMode, DrawerButton, LayoutMode, PageFunc, PageView, ThemeColorSelect, ThemeConfig } from './components';
|
||||
|
||||
defineOptions({ name: 'SettingDrawer' });
|
||||
|
||||
@@ -1,22 +1,34 @@
|
||||
import type { App } from 'vue';
|
||||
import { createI18n } from 'vue-i18n';
|
||||
import messages from './lang';
|
||||
import type { LocaleKey } from './lang';
|
||||
import type { TranslateOptions } from 'vue-i18n';
|
||||
import { localStg } from '@/utils/storage';
|
||||
import messages from './locale';
|
||||
|
||||
const i18n = createI18n({
|
||||
locale: 'zh-CN',
|
||||
locale: localStg.get('lang') || 'zh-CN',
|
||||
fallbackLocale: 'en',
|
||||
messages
|
||||
messages,
|
||||
legacy: false
|
||||
});
|
||||
|
||||
export function setupI18n(app: App) {
|
||||
app.use(i18n);
|
||||
}
|
||||
|
||||
export function t(key: string) {
|
||||
return i18n.global.t(key);
|
||||
interface T {
|
||||
(key: I18nType.I18nKey): string;
|
||||
(key: I18nType.I18nKey, plural: number, options?: TranslateOptions<I18nType.LangType>): string;
|
||||
(key: I18nType.I18nKey, defaultMsg: string, options?: TranslateOptions<I18nType.I18nKey>): string;
|
||||
(key: I18nType.I18nKey, list: unknown[], options?: TranslateOptions<I18nType.I18nKey>): string;
|
||||
(key: I18nType.I18nKey, list: unknown[], plural: number): string;
|
||||
(key: I18nType.I18nKey, list: unknown[], defaultMsg: string): string;
|
||||
(key: I18nType.I18nKey, named: Record<string, unknown>, options?: TranslateOptions<I18nType.LangType>): string;
|
||||
(key: I18nType.I18nKey, named: Record<string, unknown>, plural: number): string;
|
||||
(key: I18nType.I18nKey, named: Record<string, unknown>, defaultMsg: string): string;
|
||||
}
|
||||
|
||||
export function setLocale(locale: LocaleKey) {
|
||||
i18n.global.locale = locale;
|
||||
export const $t = i18n.global.t as T;
|
||||
|
||||
export function setLocale(locale: I18nType.LangType) {
|
||||
i18n.global.locale.value = locale;
|
||||
}
|
||||
|
||||
@@ -1,18 +1,216 @@
|
||||
import type { LocaleMessages } from 'vue-i18n';
|
||||
|
||||
const locale: LocaleMessages<I18nType.Schema> = {
|
||||
message: {
|
||||
system: {
|
||||
title: 'SoybeanAdmin'
|
||||
const locale: I18nType.Schema = {
|
||||
system: {
|
||||
title: 'SoybeanAdmin'
|
||||
},
|
||||
common: {
|
||||
add: 'Add',
|
||||
addSuccess: 'Add Success',
|
||||
edit: 'Edit',
|
||||
editSuccess: 'Edit Success',
|
||||
delete: 'Delete',
|
||||
deleteSuccess: 'Delete Success',
|
||||
batchDelete: 'Batch Delete',
|
||||
confirm: 'Confirm',
|
||||
cancel: 'Cancel',
|
||||
pleaseCheckValue: 'Please check the value is valid',
|
||||
action: 'Action'
|
||||
},
|
||||
routes: {
|
||||
dashboard: {
|
||||
_value: 'Dashboard',
|
||||
analysis: 'Analysis',
|
||||
workbench: 'Workbench'
|
||||
},
|
||||
routes: {
|
||||
dashboard: {
|
||||
dashboard: 'Dashboard',
|
||||
analysis: 'Analysis',
|
||||
workbench: 'Workbench'
|
||||
document: {
|
||||
_value: 'Document',
|
||||
vue: 'Vue Document',
|
||||
vite: 'Vite Document',
|
||||
naive: 'NaiveUI Document',
|
||||
project: 'Project Document',
|
||||
'project-link': 'Project Document(href)'
|
||||
},
|
||||
component: {
|
||||
_value: 'Component',
|
||||
button: 'Button',
|
||||
card: 'Card',
|
||||
table: 'Table'
|
||||
},
|
||||
plugin: {
|
||||
_value: 'Plugin',
|
||||
charts: {
|
||||
_value: 'Chart',
|
||||
echarts: 'ECharts',
|
||||
antv: 'AntV'
|
||||
},
|
||||
about: {
|
||||
about: 'About'
|
||||
copy: 'Copy',
|
||||
editor: {
|
||||
_value: 'Editor',
|
||||
quill: 'Quill',
|
||||
markdown: 'Markdown'
|
||||
},
|
||||
icon: 'Icon',
|
||||
map: 'Map',
|
||||
print: 'Print',
|
||||
swiper: 'Swiper',
|
||||
video: 'Video'
|
||||
},
|
||||
'auth-demo': {
|
||||
_value: 'Auth Demo',
|
||||
permission: 'Toggle Permission',
|
||||
super: 'Super Auth'
|
||||
},
|
||||
function: {
|
||||
_value: 'Function',
|
||||
tab: 'System Tab'
|
||||
},
|
||||
exception: {
|
||||
_value: 'Exception',
|
||||
403: '403',
|
||||
404: '404',
|
||||
500: '500'
|
||||
},
|
||||
'multi-menu': {
|
||||
_value: 'Multi Degree Menu',
|
||||
first: {
|
||||
_value: 'First Degree',
|
||||
second: 'Second Degree',
|
||||
'second-new': {
|
||||
_value: 'Second Degree With Children',
|
||||
third: 'Third Degree'
|
||||
}
|
||||
}
|
||||
},
|
||||
management: {
|
||||
_value: 'System Management',
|
||||
auth: 'Auth',
|
||||
role: 'Role',
|
||||
route: 'Route',
|
||||
user: 'User'
|
||||
},
|
||||
about: 'About'
|
||||
},
|
||||
layout: {
|
||||
settingDrawer: {
|
||||
title: 'Theme configuration',
|
||||
themeModeTitle: 'Theme mode',
|
||||
darkMode: 'Dark mode',
|
||||
layoutModelTitle: 'Layout mode',
|
||||
systemThemeTitle: 'System theme',
|
||||
pageFunctionsTitle: 'Page functions',
|
||||
pageViewTitle: 'Page view',
|
||||
followSystemTheme: 'Follow the system',
|
||||
isCustomizeDarkModeTransition: 'Custom dark theme animation transition',
|
||||
scrollMode: 'scrollMode',
|
||||
scrollModeList: {
|
||||
wrapper: 'Outer layer scroll',
|
||||
content: 'Main body scroll'
|
||||
},
|
||||
fixedHeaderAndTab: 'Fixed header and multiple tabs',
|
||||
header: {
|
||||
inverted: 'darkHead',
|
||||
height: 'Head Height',
|
||||
crumb: {
|
||||
visible: 'Crumb',
|
||||
icon: 'Crumb icon'
|
||||
}
|
||||
},
|
||||
tab: {
|
||||
visible: 'Multi-page tab',
|
||||
height: 'Multiple tab height',
|
||||
modeList: {
|
||||
mode: 'Multi-tab style',
|
||||
chrome: 'Google style',
|
||||
button: 'Button style'
|
||||
},
|
||||
isCache: 'Multiple tab caching'
|
||||
},
|
||||
sider: {
|
||||
inverted: 'Dark sidebar',
|
||||
width: 'Sidebar expanded width',
|
||||
mixWidth: 'Left hybrid sidebar expanded width'
|
||||
},
|
||||
menu: {
|
||||
horizontalPosition: 'Top menu position',
|
||||
horizontalPositionList: {
|
||||
flexStart: 'Right',
|
||||
center: 'center',
|
||||
flexEnd: 'Left'
|
||||
}
|
||||
},
|
||||
footer: {
|
||||
inverted: 'Dark bottom',
|
||||
visible: 'Show bottom',
|
||||
fixed: 'Fixed bottom',
|
||||
right: 'Bottom to the right'
|
||||
},
|
||||
page: {
|
||||
animate: 'switch animation',
|
||||
animateMode: 'switch animation type',
|
||||
animateModeList: {
|
||||
zoomFade: 'Gradual change',
|
||||
zoomOut: 'Flash',
|
||||
fadeSlide: 'Slide',
|
||||
fade: 'Fade away',
|
||||
fadeBottom: 'Bottom fade',
|
||||
fadeScale: 'Resizing fade away'
|
||||
}
|
||||
},
|
||||
systemTheme: {
|
||||
moreColors: 'More colors'
|
||||
},
|
||||
themeConfiguration: {
|
||||
title: 'Theme configuration',
|
||||
copy: 'Copy the current configuration',
|
||||
reset: 'Reset the current configuration',
|
||||
resetSuccess: 'The configuration has been reset, please copy it again!',
|
||||
operateSuccess: 'Successful operation',
|
||||
copySuccess: 'Copy success, please replace the content of src/settings/theme.json!',
|
||||
confirmCopy: 'Confirm'
|
||||
}
|
||||
}
|
||||
},
|
||||
page: {
|
||||
login: {
|
||||
common: {
|
||||
userNamePlaceholder: 'Please enter user name',
|
||||
phonePlaceholder: 'Please enter phone number',
|
||||
codePlaceholder: 'Please enter verification code',
|
||||
passwordPlaceholder: 'Please enter password',
|
||||
confirmPasswordPlaceholder: 'Please enter password again',
|
||||
codeLogin: 'Verification code login',
|
||||
confirm: 'Confirm',
|
||||
back: 'Back',
|
||||
validateSuccess: 'Verification passed',
|
||||
loginSuccess: 'Login success',
|
||||
welcomeBack: 'Welcome back, {userName}!'
|
||||
},
|
||||
pwdLogin: {
|
||||
title: 'Password Login',
|
||||
rememberMe: 'Remember me',
|
||||
forgetPassword: 'Forget password?',
|
||||
register: 'Register account',
|
||||
otherAccountLogin: 'Other Account Login',
|
||||
otherLoginMode: 'Other Login Mode',
|
||||
superAdmin: 'Super Administrator',
|
||||
admin: 'Administrator',
|
||||
user: 'Ordinary User'
|
||||
},
|
||||
codeLogin: {
|
||||
title: 'Verification Code Login',
|
||||
getCode: 'Get verification code',
|
||||
imageCodePlaceholder: 'Please enter image verification code'
|
||||
},
|
||||
register: {
|
||||
title: 'Register Account',
|
||||
agreement: 'I have read and agree to',
|
||||
protocol: '《User Agreement》',
|
||||
policy: '《Privacy Policy》'
|
||||
},
|
||||
resetPwd: {
|
||||
title: 'Reset Password'
|
||||
},
|
||||
bindWeChat: {
|
||||
title: 'Bind WeChat'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user