Compare commits

...

128 Commits

Author SHA1 Message Date
xiaoxian521
ad6ced45cc release: update 3.5.0 2022-09-10 21:44:17 +08:00
xiaoxian521
3d96b9acd4 chore: update 2022-09-09 00:02:15 +08:00
xiaoxian521
594f9b98ab feat: 添加cssnano,打包时压缩css体积 2022-09-08 17:23:48 +08:00
xiaoxian521
392105e820 chore: use pnpm shell-emulator instead of cross-env 2022-09-07 21:59:03 +08:00
xiaoxian521
f629663641 chore: update 2022-09-07 16:55:49 +08:00
RealityBoy
3683bd46a4 refactor: use tailwindcss replace unocss (#342)
* refactor: use `tailwindcss` replace `unocss`

* fix: update
2022-09-07 15:07:01 +08:00
Yalin
4dfde1bea6 docs:注释文案修正 (#344) 2022-09-07 14:09:54 +08:00
xiaoxian521
4e506555ad fix: token过期,刷新死循环 2022-09-06 23:22:06 +08:00
xiaoxian521
45f912c2f5 chore: update 2022-09-05 22:21:10 +08:00
xiaoxian521
d44b35937b chore: update 2022-09-04 12:34:43 +08:00
xiaoxian521
594abd7372 chore: update 2022-09-03 13:56:30 +08:00
xiaoxian521
869964ea7e chore: update 2022-08-31 11:37:18 +08:00
xiaoxian521
4c26318126 docs: update 2022-08-31 11:19:27 +08:00
xiaoxian521
ab3468b758 chore: update 2022-08-31 11:01:36 +08:00
xiaoxian521
1dbd29735e docs: update 2022-08-31 01:39:09 +08:00
xiaoxian521
2def54c775 workflow: add gitee Deploy 2022-08-31 00:40:03 +08:00
xiaoxian521
67e8532d37 chore: update 2022-08-29 17:14:25 +08:00
一万
fb2c016ebe feat: add el-table-infinite-scroll demo (#335) 2022-08-24 23:25:00 +08:00
xiaoxian521
3056527701 perf: 重置路由时,清空缓存页面 2022-08-24 16:08:05 +08:00
xiaoxian521
cef79dfca0 chore: window.process定义在index.html中,防止低版本浏览器报错 2022-08-24 09:46:19 +08:00
xiaoxian521
769410efdf chore: 开启vscode括号对指南 2022-08-23 20:47:35 +08:00
xiaoxian521
c7dbb07858 chore: update unocss 2022-08-23 17:59:38 +08:00
xiaoxian521
cc92b3e51e style: reset.scss 2022-08-23 16:58:52 +08:00
xiaoxian521
88bba7555b release: update 3.4.6 2022-08-23 10:31:44 +08:00
xiaoxian521
30c682bd24 fix: 修复iframe加载失败 2022-08-23 10:24:40 +08:00
SampsonYe(叶飞)
3baffa11e4 fix: 修复动态路由子级长度为0时抛出异常 (#333) 2022-08-23 10:10:49 +08:00
xiaoxian521
1ed598c5f2 fix: process is not defined in path 2022-08-23 09:55:23 +08:00
xiaoxian521
185d779c8d release: update 3.4.5 2022-08-22 20:21:53 +08:00
xiaoxian521
241fc618b8 fix: 修复本地响应式存储对象设置问题 2022-08-22 20:17:50 +08:00
xiaoxian521
3a0b2e04d4 release: update 3.4.0 2022-08-22 17:21:18 +08:00
xiaoxian521
cec867297d chore: update 2022-08-22 16:16:17 +08:00
RealityBoy
cbe539c727 Refactor/tags (#332)
* refactor: tags

* chore: update

* chore: update

* chore: update

* chore: update
2022-08-22 15:49:29 +08:00
xiaoxian521
7c84d9eb70 perf: router 2022-08-22 10:19:10 +08:00
xiaoxian521
5a7187d22f perf: 移动端优化 2022-08-21 10:49:27 +08:00
xiaoxian521
a7e8249ae6 chore: update 2022-08-21 10:26:09 +08:00
xiaoxian521
d81ab81fb0 chore: update 2022-08-21 07:31:59 +08:00
xiaoxian521
846e7350fb chore: update 2022-08-19 23:01:45 +08:00
xiaoxian521
102f27fe30 chore: update element-plus@2.2.14 and perf menu 2022-08-19 21:41:49 +08:00
newives
0e76bd4aac perf: 清空缓存并返回登录页时添加重置路由 (#329)
Co-authored-by: xiaoxian521 <1923740402@qq.com>
2022-08-19 15:56:39 +08:00
xiaoxian521
a74a9e2489 release: update 3.3.5 2022-08-19 15:29:06 +08:00
xiaoxian521
28e58e4b4e types: 添加完整路由配置表类型声明 2022-08-19 15:03:52 +08:00
xiaoxian521
a8e830f51c chore: update 2022-08-19 09:57:35 +08:00
xiaoxian521
0493c402a7 chore: update 2022-08-19 09:47:13 +08:00
xiaoxian521
ca10d104c0 fix: 当没有icon时,垂直导航菜单折叠文字被隐藏 2022-08-19 09:44:58 +08:00
xiaoxian521
035ac47b39 chore: update 2022-08-18 18:36:36 +08:00
xiaoxian521
a983575b6d perf: 优化接口类型 2022-08-18 18:35:09 +08:00
xiaoxian521
d6a329a63c refactor: 重构重置路由功能 2022-08-18 18:34:53 +08:00
xiaoxian521
5ac646444f chore: update 2022-08-18 15:35:50 +08:00
xiaoxian521
b36eab2141 docs: update 2022-08-18 00:20:04 +08:00
xiaoxian521
3c44909f47 chore: update 2022-08-17 22:36:21 +08:00
xiaoxian521
b827301d0a chore: update 2022-08-17 20:45:18 +08:00
xiaoxian521
e0c8781bcc feat: 添加无Layout的空白页面示例 2022-08-17 19:39:54 +08:00
xiaoxian521
8314df9faf chore: update 2022-08-17 14:02:06 +08:00
xiaoxian521
dffe8834a1 fix: unocss exclude 2022-08-17 11:09:17 +08:00
xiaoxian521
8d5be25093 chore: update 2022-08-17 10:25:39 +08:00
xiaoxian521
7878c108e1 chore: update 2022-08-16 22:29:08 +08:00
xiaoxian521
f27787d560 chore: update 2022-08-15 15:53:27 +08:00
xiaoxian521
acaeb0cb42 chore: update 2022-08-15 13:01:08 +08:00
xiaoxian521
9e8907ce21 chore: update 2022-08-15 11:59:27 +08:00
xiaoxian521
3879b99176 fix: [重构主题后的一些问题](https://github.com/xiaoxian521/vue-pure-admin/issues/319) 2022-08-14 23:09:54 +08:00
xiaoxian521
bcbc110883 chore: update 2022-08-11 22:23:41 +08:00
xiaoxian521
243c8f71a7 chore: update 2022-08-11 20:32:55 +08:00
xiaoxian521
5f1466b574 feat: login page add i18n 2022-08-11 13:10:37 +08:00
xiaoxian521
26b85b5c92 fix: svg icon 2022-07-31 19:17:46 +08:00
RealityBoy
76f6a9df89 Merge pull request #314 from xiaoxian521/perf/login
perf: login
2022-07-27 21:26:11 +08:00
xiaoxian521
0d4fe313b4 fix: update 2022-07-27 21:10:36 +08:00
xiaoxian521
c7955bcc19 style: fix 2022-07-27 16:37:06 +08:00
xiaoxian521
fe0f0c4d9e chore: update 2022-07-27 15:23:46 +08:00
lrl
114d5427cc perf: login 2022-07-27 15:08:44 +08:00
RealityBoy
d824c99489 Refactor/themes (#311)
* refactor: theme
2022-07-26 13:16:44 +08:00
一万
708ce43e00 fix: vite build error: out of memory (#310) 2022-07-22 11:10:20 +08:00
xiaoxian521
271b953081 fix: 兼容存放目录以中文命名,但是我们真心不推荐中文命名 2022-07-18 16:09:14 +08:00
xiaoxian521
af71024f42 chore: update 2022-07-18 16:07:03 +08:00
xiaoxian521
a73610672f fix: update 2022-07-18 13:50:32 +08:00
xiaoxian521
4c6acc91ab fix: 修复路由showlinkfalse的异步路由,刷新后不显示 2022-07-18 13:48:59 +08:00
xiaoxian521
b2dd962161 chore: update @pureadmin/utils 2022-07-18 11:14:12 +08:00
xiaoxian521
b52ab8cb0a chore: update vite-plugin-remove-console 2022-07-18 11:11:44 +08:00
hb0730
d87f7e1948 fix(login): use a unified title (#305) 2022-07-13 09:25:01 +08:00
xiaoxian521
60e3519f93 docs: update 2022-07-10 23:26:15 +08:00
xiaoxian521
ef80912137 chore: update vite-plugin-remove-console latest 2022-07-05 17:33:14 +08:00
xiaoxian521
2e0b574733 docs: update 2022-07-04 19:55:15 +08:00
xiaoxian521
e1eb658697 docs: update 2022-07-04 19:47:00 +08:00
xiaoxian521
5a23bd02a4 fix: update @pureadmin/utils 2022-07-02 12:11:09 +08:00
xiaoxian521
af04611d6c feat: add @pureadmin/utils 2022-06-28 11:45:32 +08:00
xiaoxian521
384d5e9598 chore: update @pureadmin/table 2022-06-26 14:00:56 +08:00
xiaoxian521
1848c43f0c fix: update 2022-06-25 16:18:21 +08:00
xiaoxian521
d2cd29a838 feat: add cloc 2022-06-25 12:24:44 +08:00
xiaoxian521
91576bd7dd chore: update @pureadmin/descriptions 2022-06-24 14:12:56 +08:00
xiaoxian521
1b2eb2481e feat: use @pureadmin/descriptions replace el-descriptions 2022-06-24 11:49:39 +08:00
xiaoxian521
e3c240f139 style: fix 2022-06-22 22:45:19 +08:00
xiaoxian521
d0cfc6614e chore: update vite-plugin-remove-console 2022-06-22 21:36:48 +08:00
xiaoxian521
007bb0124f chore: update responsive-storage 2022-06-22 21:30:40 +08:00
xiaoxian521
9fa1d9d2ea perf: use @pureadmin/table replace el-table 2022-06-22 20:16:33 +08:00
xiaoxian521
1dfc67802a feat: add @pureadmin/table 2022-06-22 18:36:08 +08:00
xiaoxian521
bae84cd1ba fix: update 2022-06-20 16:23:36 +08:00
xiaoxian521
3f3301a4c2 chore: add Vue.volar 2022-06-20 13:14:39 +08:00
一万
4af50acf88 feat: export execl demo (#288) 2022-06-03 23:41:47 +08:00
xiaoxian521
61880bc07b feat: add FUNDING.yml 2022-06-03 09:11:36 +08:00
一万
804f1aea9b fix: reset tags when logout (#286) 2022-05-28 23:10:26 +08:00
fwindpeak
636201df80 fix: correct typos 2022-05-28 10:10:10 +08:00
xiaoxian521
4157c7bccc fix: update 2022-05-27 23:05:40 +08:00
xiaoxian521
0d05a2a6e3 workflow: gh-pages is no longer deployed 2022-05-27 23:04:07 +08:00
xiaoxian521
a53cbc2b87 chore: update the latest version of element-plus 2022-05-27 17:21:18 +08:00
xiaoxian521
870f064598 perf: pdf demo 2022-05-26 22:35:35 +08:00
一万
2a15298cf8 feat: add pdf preview demo (#282)
* feat: add pdf preview demo
2022-05-26 21:29:26 +08:00
一万
bf8a7d98ac fix: theme (#281)
* fix: theme
2022-05-25 22:23:20 +08:00
xiaoxian521
e9602d23cb perf: update 2022-05-22 14:39:02 +08:00
xiaoxian521
747e2b9c1c feat: use unplugin-vue-define-options add setup name 2022-05-21 12:29:54 +08:00
duyin
dca722cb29 fix: proxy 2022-05-19 22:52:11 +08:00
一万
1f8e50f482 feat: withInstall (#275) 2022-05-19 12:31:42 +08:00
xiaoxian521
b6ed8b40d1 fix: 兼容路由title为空的情况 2022-05-19 12:03:39 +08:00
xiaoxian521
348916e567 feat: add Virtual List demo 2022-05-19 01:16:08 +08:00
xiaoxian521
6ea020c8d2 chore: update @pureadmin/theme 2022-05-18 15:48:55 +08:00
xiaoxian521
8f5981a313 perf: icon 2022-05-18 12:27:49 +08:00
xiaoxian521
378feceb5f perf: tag demo 2022-05-18 10:29:26 +08:00
xiaoxian521
c65940f5ef perf: fix ReTable 2022-05-17 17:56:12 +08:00
Simler Li
6627d4724e fix: 修改防抖demo的文字说明 (#270) 2022-05-17 16:15:59 +08:00
xiaoxian521
9ca764b2a6 fix: 修复直接通过浏览器地址栏打开演示环境标签详情页,tag未加载的情况 2022-05-17 00:52:07 +08:00
xiaoxian521
f4c419d44a fix: update 2022-05-16 20:27:55 +08:00
ryiot
4f165b9391 fix:修复在苹果自带桌面浏览器 safari 中无法输入验证码 (#268) 2022-05-15 20:07:23 +08:00
xiaoxian521
25f70fd51b perf: standard code format in the layout folder 2022-05-14 11:01:43 +08:00
xiaoxian521
58f6be4d02 Merge branch 'main' of github.com:xiaoxian521/vue-pure-admin 2022-05-14 10:53:46 +08:00
xiaoxian521
64326c4fb5 perf: standard code format in the views folder 2022-05-14 10:53:06 +08:00
啝裳
16cce26b67 Rename select.vue to Select.vue 2022-05-14 10:03:06 +08:00
啝裳
6587e6eec0 Rename Filpper.tsx to filpper.tsx 2022-05-14 10:02:24 +08:00
xiaoxian521
1cf82eb7a4 perf: standard code format 2022-05-14 09:59:02 +08:00
xiaoxian521
2214ce7911 fix: update 2022-05-14 09:56:07 +08:00
xiaoxian521
7f10ac7679 fix: update 2022-05-11 16:34:54 +08:00
263 changed files with 7997 additions and 9232 deletions

View File

@@ -1,4 +1,10 @@
public public
dist dist
*.d.ts *.d.ts
package.json package.json
.eslintrc.js
.prettierrc.js
commitlint.config.js
postcss.config.js
tailwind.config.js
stylelint.config.js

View File

@@ -49,6 +49,29 @@ module.exports = {
jsx: true jsx: true
} }
}, },
overrides: [
{
files: ["*.ts", "*.vue"],
rules: {
"no-undef": "off"
}
},
{
files: ["*.vue"],
parser: "vue-eslint-parser",
parserOptions: {
parser: "@typescript-eslint/parser",
extraFileExtensions: [".vue"],
ecmaVersion: "latest",
ecmaFeatures: {
jsx: true
}
},
rules: {
"no-undef": "off"
}
}
],
rules: { rules: {
"vue/no-v-html": "off", "vue/no-v-html": "off",
"vue/require-default-prop": "off", "vue/require-default-prop": "off",
@@ -94,4 +117,4 @@ module.exports = {
} }
] ]
} }
}; }

39
.github/workflows/gitee.yml vendored Normal file
View File

@@ -0,0 +1,39 @@
name: Build and Deploy
permissions:
contents: write
on:
push:
branches:
- gitee
jobs:
deploy:
concurrency: ci-${{ github.ref }}
runs-on: ubuntu-latest
steps:
- name: Checkout 🛎️
uses: actions/checkout@v3
- name: Setup node
uses: actions/setup-node@v2
with:
node-version: "16"
registry-url: https://registry.npmjs.com/
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: latest
- name: Deploy 🔧
run: |
pnpm install
sed -i "s#VITE_PUBLIC_PATH = /#VITE_PUBLIC_PATH = /vue-pure-admin/#g" $(pwd)/.env.production
pnpm build
cd dist
touch README.md .nojekyll
- name: Deploy 🚀
uses: JamesIves/github-pages-deploy-action@v4
with:
folder: dist
clean: true

View File

@@ -60,6 +60,7 @@ jobs:
run: | run: |
pnpm install pnpm install
pnpm lint pnpm lint
pnpm typecheck
env: env:
VALIDATE_ALL_CODEBASE: false VALIDATE_ALL_CODEBASE: false
DEFAULT_BRANCH: main DEFAULT_BRANCH: main

View File

@@ -1,34 +0,0 @@
name: preview
on:
push:
branches:
- master
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [12.x]
steps:
- name: Checkout
uses: actions/checkout@v1
- name: Setup node
uses: actions/setup-node@v2
with:
node-version: "16"
registry-url: https://registry.npmjs.com/
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: latest
- name: run deploy.sh
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: pnpm install && pnpm deploy

2
.gitignore vendored
View File

@@ -4,6 +4,7 @@ dist
dist-ssr dist-ssr
*.local *.local
.eslintcache .eslintcache
report.html
yarn.lock yarn.lock
npm-debug.log* npm-debug.log*
@@ -17,3 +18,4 @@ tests/**/coverage/
*.ntvs* *.ntvs*
*.njsproj *.njsproj
*.sln *.sln
tsconfig.tsbuildinfo

View File

@@ -2,4 +2,4 @@ ports:
- port: 3344 - port: 3344
onOpen: open-preview onOpen: open-preview
tasks: tasks:
- init: npm install && npm run serve - init: pnpm install && pnpm serve

View File

@@ -1,6 +1,6 @@
module.exports = { module.exports = {
"*.{js,jsx,ts,tsx}": ["eslint --fix", "prettier --write"], "*.{js,jsx,ts,tsx}": ["eslint --fix", "prettier --write"],
"{!(package)*.json,.!(browserslist)*rc}": ["prettier --write--parser json"], "{!(package)*.json}": ["prettier --write--parser json"],
"package.json": ["prettier --write"], "package.json": ["prettier --write"],
"*.vue": ["eslint --fix", "prettier --write", "stylelint --fix"], "*.vue": ["eslint --fix", "prettier --write", "stylelint --fix"],
"*.{vue,css,scss,postcss,less}": ["stylelint --fix", "prettier --write"], "*.{vue,css,scss,postcss,less}": ["stylelint --fix", "prettier --write"],

3
.npmrc Normal file
View File

@@ -0,0 +1,3 @@
shamefully-hoist=true
strict-peer-dependencies=false
shell-emulator=true

View File

@@ -3,13 +3,13 @@
"vscode-icons-team.vscode-icons", "vscode-icons-team.vscode-icons",
"davidanson.vscode-markdownlint", "davidanson.vscode-markdownlint",
"stylelint.vscode-stylelint", "stylelint.vscode-stylelint",
"bradlc.vscode-tailwindcss",
"dbaeumer.vscode-eslint", "dbaeumer.vscode-eslint",
"esbenp.prettier-vscode", "esbenp.prettier-vscode",
"johnsoncodehk.volar",
"lokalise.i18n-ally", "lokalise.i18n-ally",
"mikestead.dotenv", "mikestead.dotenv",
"eamodio.gitlens", "eamodio.gitlens",
"antfu.iconify", "antfu.iconify",
"antfu.unocss" "Vue.volar"
] ]
} }

View File

@@ -6,6 +6,7 @@
}, },
"editor.tabSize": 2, "editor.tabSize": 2,
"editor.formatOnPaste": true, "editor.formatOnPaste": true,
"editor.guides.bracketPairs": "active",
"files.autoSave": "afterDelay", "files.autoSave": "afterDelay",
"git.confirmSync": false, "git.confirmSync": false,
"workbench.startupEditor": "newUntitledFile", "workbench.startupEditor": "newUntitledFile",
@@ -33,5 +34,6 @@
"i18n-ally.enabledParsers": ["yaml", "js"], "i18n-ally.enabledParsers": ["yaml", "js"],
"i18n-ally.sourceLanguage": "en", "i18n-ally.sourceLanguage": "en",
"i18n-ally.displayLanguage": "zh-CN", "i18n-ally.displayLanguage": "zh-CN",
"i18n-ally.enabledFrameworks": ["vue"] "i18n-ally.enabledFrameworks": ["vue"],
"iconify.excludes": ["el"]
} }

View File

@@ -1,3 +1,76 @@
# 3.5.0 (2022-9-10)
### 🎫 Feat
- Add `cssnano` to compress the size of `css` when packaging
- Add `element-plus` seamless scrolling `Table` page demo
- Open `vscode` bracket pair guide
### ✔️ refactor
- Replace `unocss` with `tailwindcss`, add `tailwindcss` [documentation](https://xiaoxian521.github.io/pure-admin-doc/pages/39156f/)
### 🐞 Bug fixes
- `token` expires, refresh the infinite loop
### 🍏 Perf
- When resetting the route, clear the cached page
# 3.4.6 (2022-8-23)
### 🐞 Bug fixes
- `process` is not defined in path
- Fixed an error when dynamic routing `children` is an empty array
- Fixed `iframe` loading failure
# 3.4.5 (2022-8-22)
### 🐞 Bug fixes
- Fix local responsive storage object setting issue
# 3.4.0 (2022-8-22)
### 🍏 Perf
- Optimized routing
- Optimized for mobile compatibility
- Optimized routing parameters (`query`, `params` way to refresh the page does not need to open the tab page cache, the parameters can be retained on the `url` and `tab page`)
# 3.3.5 (2022-8-19)
### 🎫 Feat
- Secondary encapsulation of `Table` of `element-plus` into [@pureadmin/table](https://github.com/xiaoxian521/pure-admin-table), providing flexible configuration items and integrating into the platform
- Secondary encapsulation of `Descriptions` of `element-plus` into [@pureadmin/descriptions](https://github.com/xiaoxian521/pure-admin-descriptions), providing flexible configuration items and integrating into the platform
- Centralize most of the tools and hooks of the platform to [@pureadmin/utils](https://pure-admin-utils-docs.vercel.app/), and delete the code concentrated in this library to reduce the size of the platform
- Add [unplugin-vue-define-options](https://www.npmjs.com/package/unplugin-vue-define-options) plugin, the page can directly write `defineOptions({name: custom name})`
- Add project files, language analysis tool [cloc](https://www.npmjs.com/package/cloc)
- Added landing page internationalization
- Add full routing configuration table type declaration
- Add virtual listing page demo
- Add `PDF` preview page demo
- Added export `execl` page demo
- Added blank page demo without `Layout`
### ✔️ refactor
- Refactored the theme color to adapt to `element-plus` dark mode (also solved the problem that the same element `css` in `3.3.0` and earlier versions was overwritten many times, resulting in poor style debugging)
- Refactored route reset function
### 🍏 Perf
- The compatible project storage directory is named in Chinese, but we really do not recommend Chinese naming, because a library may not escape the Chinese path, causing the project to crash
- Optimized interface type
### 🐞 Bug fixes
- Fixed async routes with `showlink` set to `false`, not showing after refresh
- Fixed vertical navigation menu text being hidden after collapse when there is no `icon`
# 3.3.0 (2022-5-11) # 3.3.0 (2022-5-11)
### 🎫 Feat ### 🎫 Feat

View File

@@ -1,3 +1,76 @@
# 3.5.0 (2022-9-10)
### 🎫 Feat
- Add `cssnano` to compress the size of `css` when packaging
- Add `element-plus` seamless scrolling `Table` page demo
- Open `vscode` bracket pair guide
### ✔️ refactor
- Replace `unocss` with `tailwindcss`, add `tailwindcss` [documentation](https://xiaoxian521.github.io/pure-admin-doc/pages/39156f/)
### 🐞 Bug fixes
- `token` expires, refresh the infinite loop
### 🍏 Perf
- When resetting the route, clear the cached page
# 3.4.6 (2022-8-23)
### 🐞 Bug fixes
- `process` is not defined in path
- Fixed an error when dynamic routing `children` is an empty array
- Fixed `iframe` loading failure
# 3.4.5 (2022-8-22)
### 🐞 Bug fixes
- Fix local responsive storage object setting issue
# 3.4.0 (2022-8-22)
### 🍏 Perf
- Optimized routing
- Optimized for mobile compatibility
- Optimized routing parameters (`query`, `params` way to refresh the page does not need to open the tab page cache, the parameters can be retained on the `url` and `tab page`)
# 3.3.5 (2022-8-19)
### 🎫 Feat
- Secondary encapsulation of `Table` of `element-plus` into [@pureadmin/table](https://github.com/xiaoxian521/pure-admin-table), providing flexible configuration items and integrating into the platform
- Secondary encapsulation of `Descriptions` of `element-plus` into [@pureadmin/descriptions](https://github.com/xiaoxian521/pure-admin-descriptions), providing flexible configuration items and integrating into the platform
- Centralize most of the tools and hooks of the platform to [@pureadmin/utils](https://pure-admin-utils-docs.vercel.app/), and delete the code concentrated in this library to reduce the size of the platform
- Add [unplugin-vue-define-options](https://www.npmjs.com/package/unplugin-vue-define-options) plugin, the page can directly write `defineOptions({name: custom name})`
- Add project files, language analysis tool [cloc](https://www.npmjs.com/package/cloc)
- Added landing page internationalization
- Add full routing configuration table type declaration
- Add virtual listing page demo
- Add `PDF` preview page demo
- Added export `execl` page demo
- Added blank page demo without `Layout`
### ✔️ refactor
- Refactored the theme color to adapt to `element-plus` dark mode (also solved the problem that the same element `css` in `3.3.0` and earlier versions was overwritten many times, resulting in poor style debugging)
- Refactored route reset function
### 🍏 Perf
- The compatible project storage directory is named in Chinese, but we really do not recommend Chinese naming, because a library may not escape the Chinese path, causing the project to crash
- Optimized interface type
### 🐞 Bug fixes
- Fixed async routes with `showlink` set to `false`, not showing after refresh
- Fixed vertical navigation menu text being hidden after collapse when there is no `icon`
# 3.3.0 (2022-5-11) # 3.3.0 (2022-5-11)
### 🎫 Feat ### 🎫 Feat

View File

@@ -1,3 +1,76 @@
# 3.5.0 (2022-9-10)
### 🎫 Feat
- 添加 `cssnano` ,打包时压缩 `css` 体积
- 添加 `element-plus` 无缝滚动 `Table` 页面 demo
- 开启 `vscode` 括号对指南
### ✔️ refactor
- 使用 `tailwindcss` 替换 `unocss`,新增 `tailwindcss` [使用文档](http://yiming_chang.gitee.io/pure-admin-doc/pages/39156f/)
### 🐞 Bug fixes
- `token` 过期,刷新死循环
### 🍏 Perf
- 重置路由时,清空缓存页面
# 3.4.6 (2022-8-23)
### 🐞 Bug fixes
- `process` is not defined in path
- 修复动态路由`children`为空数组时报错
- 修复`iframe`加载失败
# 3.4.5 (2022-8-22)
### 🐞 Bug fixes
- 修复本地响应式存储对象设置问题
# 3.4.0 (2022-8-22)
### 🍏 Perf
- 优化路由
- 优化移动端兼容性
- 优化路由传参(`query``params` 方式刷新页面不需要再开启标签页缓存也能保留参数在`url``标签页`上)
# 3.3.5 (2022-8-19)
### 🎫 Feat
-`element-plus``Table` 二次封装到[@pureadmin/table](https://github.com/xiaoxian521/pure-admin-table),提供灵活的配置项并集成到平台里
-`element-plus``Descriptions` 二次封装到[@pureadmin/descriptions](https://github.com/xiaoxian521/pure-admin-descriptions),提供灵活的配置项并集成到平台里
- 将平台的大部分工具以及 hooks 都集中到[@pureadmin/utils](https://pure-admin-utils-docs.vercel.app/),并删除集中到这个库里的代码,减少平台体积
- 添加[unplugin-vue-define-options](https://www.npmjs.com/package/unplugin-vue-define-options)插件,页面可直接写 `defineOptions({name: 自定义名称})`
- 添加项目文件、语言分析工具 [cloc](https://www.npmjs.com/package/cloc)
- 添加登录页国际化
- 添加完整路由配置表类型声明
- 添加虚拟列表页面 demo
- 添加 `PDF` 预览页面 demo
- 添加导出 `execl` 页面 demo
- 添加无 `Layout` 的空白页面 demo
### ✔️ refactor
- 重构主题色,适配 `element-plus` 暗黑模式(同时也解决了 `3.3.0` 及更低版本中同样的元素 `css` 被多次覆盖,导致样式不好调试的问题)
- 重构路由重置功能
### 🍏 Perf
- 兼容项目存放目录以中文命名,但我们真心不推荐中文命名,因为可能某个库没有对中文路径做转义处理,导致项目奔溃
- 优化接口类型
### 🐞 Bug fixes
- 修复路由 `showlink``false` 的异步路由,刷新后不显示
- 修复当没有 `icon` 时,垂直导航菜单折叠后文字被隐藏
# 3.3.0 (2022-5-11) # 3.3.0 (2022-5-11)
### 🎫 Feat ### 🎫 Feat
@@ -21,7 +94,7 @@
### ✔️ refactor ### ✔️ refactor
- 重构登页,更偏向实际业务场景 - 重构登页,更偏向实际业务场景
- 使用`unocss`替换`windicss``unocss`开发环境下性能更好,没有内存泄露,而且`api`使用上兼容`windicss` - 使用`unocss`替换`windicss``unocss`开发环境下性能更好,没有内存泄露,而且`api`使用上兼容`windicss`
### 🍏 Perf ### 🍏 Perf

View File

@@ -17,7 +17,9 @@ vue-pure-admin is a free and open source middle and back-end template. Using the
## Docs ## Docs
- [Click Watch Docs](https://pure-admin-doc.vercel.app) - [Click me to view the domestic documentation site](http://yiming_chang.gitee.io/pure-admin-doc)
- [Click me to view foreign document site 1](https://xiaoxian521.github.io/pure-admin-doc)
- [Click me to view foreign document site 2](https://pure-admin-doc.vercel.app)
## Thin ## Thin
@@ -29,12 +31,23 @@ vue-pure-admin is a free and open source middle and back-end template. Using the
## Preview ## Preview
- [vue-pure-admin](https://vue-pure-admin.vercel.app) - [Click me to view the domestic preview station](http://yiming_chang.gitee.io/vue-pure-admin)
- [Click me to view foreign preview site 1](https://xiaoxian521.github.io/vue-pure-admin)
- [Click me to view foreign preview station 2](https://vue-pure-admin.vercel.app)
- PC
<p align="center"> <p align="center">
<img alt="PureAdmin Logo" width="100%" src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b4857fc7eb7d4c0f8deeefc644c1f7dd~tplv-k3u1fbpfcp-watermark.awebp?"> <img alt="PureAdmin Logo" width="100%" src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d45c15ccbe674fe291a4faa528d11eda~tplv-k3u1fbpfcp-zoom-in-crop-mark:3024:0:0:0.awebp?">
<img alt="PureAdmin Logo" width="100%" src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/549c3184697f4d268a78c9833e5ec2ea~tplv-k3u1fbpfcp-watermark.awebp?"> </p>
<img alt="PureAdmin Logo" width="100%" src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/381fc957fac143db9f06efdd389d88a3~tplv-k3u1fbpfcp-watermark.awebp?">
- DarkMode
<p align="center">
<img alt="PureAdmin Logo" width="100%" src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/10a351f0d9c94b90ba3b408a786b9ede~tplv-k3u1fbpfcp-zoom-in-crop-mark:3024:0:0:0.awebp?">
</p>
- Mobile
<p align="center">
<img alt="PureAdmin Logo" width="100%" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3061c7b92f6d4cb4bcdf227d966ac696~tplv-k3u1fbpfcp-zoom-in-crop-mark:3024:0:0:0.awebp?">
</p> </p>
### Use Gitpod ### Use Gitpod
@@ -141,6 +154,8 @@ Thank you very much for your support, I believe the project will get better and
| xueyuheng | taolei1990 | hang-kim | madwolfcrazy | limuen | BenLakes | | xueyuheng | taolei1990 | hang-kim | madwolfcrazy | limuen | BenLakes |
| :--------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------: | | :--------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------: |
| <a href="https://github.com/xueyuheng"><img src="https://avatars.githubusercontent.com/u/48202935?v=4" width="60px" height="60px" /></a> | <a href="https://github.com/taolei1990"><img src="https://avatars.githubusercontent.com/u/23173640?v=4" width="60px" height="60px" /></a> | <a href="https://github.com/hang-kim"><img src="https://avatars.githubusercontent.com/u/52914259?v=4" width="60px" height="60px" /></a> | <a href="https://github.com/madwolfcrazy"><img src="https://avatars.githubusercontent.com/u/223671?v=4" width="60px" height="60px" /></a> | <a href="https://github.com/limuen"><img src="https://avatars.githubusercontent.com/u/31790606?v=4" width="60px" height="60px" /></a> | <a href="https://github.com/BenLakes"><img src="https://avatars.githubusercontent.com/u/15206046?v=4" width="60px" height="60px" /></a> | | <a href="https://github.com/xueyuheng"><img src="https://avatars.githubusercontent.com/u/48202935?v=4" width="60px" height="60px" /></a> | <a href="https://github.com/taolei1990"><img src="https://avatars.githubusercontent.com/u/23173640?v=4" width="60px" height="60px" /></a> | <a href="https://github.com/hang-kim"><img src="https://avatars.githubusercontent.com/u/52914259?v=4" width="60px" height="60px" /></a> | <a href="https://github.com/madwolfcrazy"><img src="https://avatars.githubusercontent.com/u/223671?v=4" width="60px" height="60px" /></a> | <a href="https://github.com/limuen"><img src="https://avatars.githubusercontent.com/u/31790606?v=4" width="60px" height="60px" /></a> | <a href="https://github.com/BenLakes"><img src="https://avatars.githubusercontent.com/u/15206046?v=4" width="60px" height="60px" /></a> |
| mollerzhu | TLovers | cnyyk | | | |
| <a href="https://github.com/mollerzhu"><img src="https://avatars.githubusercontent.com/u/49627902?v=4" width="60px" height="60px" /></a> | <a href="https://github.com/TLovers"><img src="https://avatars.githubusercontent.com/u/26561694?v=4" width="60px" height="60px" /></a> | <a href="https://github.com/cnyyk"><img src="https://avatars.githubusercontent.com/u/275233?v=4" width="60px" height="60px" /></a> | | | |
## Contributors ## Contributors

View File

@@ -17,7 +17,9 @@ vue-pure-admin 是一个免费开源的中后台模版。使用了最新的`vue3
## 配套文档 ## 配套文档
- [点我查看文档](https://pure-admin-doc.vercel.app) - [点我查看国内文档](http://yiming_chang.gitee.io/pure-admin-doc)
- [点我查看国外文档站 1](https://xiaoxian521.github.io/pure-admin-doc)
- [点我查看国外文档站 2](https://pure-admin-doc.vercel.app)
## 精简版 ## 精简版
@@ -29,12 +31,23 @@ vue-pure-admin 是一个免费开源的中后台模版。使用了最新的`vue3
## 预览 ## 预览
- [vue-pure-admin](https://vue-pure-admin.vercel.app) - [点我查看国内预览站](http://yiming_chang.gitee.io/vue-pure-admin)
- [点我查看国外预览站 1](https://xiaoxian521.github.io/vue-pure-admin)
- [点我查看国外预览站 2](https://vue-pure-admin.vercel.app)
- PC 端
<p align="center"> <p align="center">
<img alt="PureAdmin Logo" width="100%" src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b4857fc7eb7d4c0f8deeefc644c1f7dd~tplv-k3u1fbpfcp-watermark.awebp?"> <img alt="PureAdmin Logo" width="100%" src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d45c15ccbe674fe291a4faa528d11eda~tplv-k3u1fbpfcp-zoom-in-crop-mark:3024:0:0:0.awebp?">
<img alt="PureAdmin Logo" width="100%" src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/549c3184697f4d268a78c9833e5ec2ea~tplv-k3u1fbpfcp-watermark.awebp?"> </p>
<img alt="PureAdmin Logo" width="100%" src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/381fc957fac143db9f06efdd389d88a3~tplv-k3u1fbpfcp-watermark.awebp?">
- 暗黑模式
<p align="center">
<img alt="PureAdmin Logo" width="100%" src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/10a351f0d9c94b90ba3b408a786b9ede~tplv-k3u1fbpfcp-zoom-in-crop-mark:3024:0:0:0.awebp?">
</p>
- 移动端
<p align="center">
<img alt="PureAdmin Logo" width="100%" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3061c7b92f6d4cb4bcdf227d966ac696~tplv-k3u1fbpfcp-zoom-in-crop-mark:3024:0:0:0.awebp?">
</p> </p>
### 使用 Gitpod ### 使用 Gitpod
@@ -130,9 +143,9 @@ pnpm build
## QQ 交流群 ## QQ 交流群
群里严禁`黄``赌``毒``vpn`等违法行为! 一群已满,下面是二群,群里严禁`黄``赌``毒``vpn`等违法行为!
<img src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f0697596aec84661b724f6eebdf8db17~tplv-k3u1fbpfcp-watermark.awebp?" width="150px" height="225px" /> <img src="http://yiming_chang.gitee.io/pure-admin-doc/img/support/qq.png" width="150px" height="225px" />
## 许可证 ## 许可证
@@ -144,9 +157,11 @@ pnpm build
非常感谢你们的支持,相信项目会越来越好 :heart: 非常感谢你们的支持,相信项目会越来越好 :heart:
| xueyuheng | taolei1990 | hang-kim | madwolfcrazy | limuen | BenLakes | mollerzhu | | xueyuheng | taolei1990 | hang-kim | madwolfcrazy | limuen | BenLakes |
| :--------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------: | | :--------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------: |
| <a href="https://github.com/xueyuheng"><img src="https://avatars.githubusercontent.com/u/48202935?v=4" width="60px" height="60px" /></a> | <a href="https://github.com/taolei1990"><img src="https://avatars.githubusercontent.com/u/23173640?v=4" width="60px" height="60px" /></a> | <a href="https://github.com/hang-kim"><img src="https://avatars.githubusercontent.com/u/52914259?v=4" width="60px" height="60px" /></a> | <a href="https://github.com/madwolfcrazy"><img src="https://avatars.githubusercontent.com/u/223671?v=4" width="60px" height="60px" /></a> | <a href="https://github.com/limuen"><img src="https://avatars.githubusercontent.com/u/31790606?v=4" width="60px" height="60px" /></a> | <a href="https://github.com/BenLakes"><img src="https://avatars.githubusercontent.com/u/15206046?v=4" width="60px" height="60px" /></a> | <a href="https://github.com/mollerzhu"><img src="https://avatars.githubusercontent.com/u/49627902?v=4" width="60px" height="60px" /></a> | | <a href="https://github.com/xueyuheng"><img src="https://avatars.githubusercontent.com/u/48202935?v=4" width="60px" height="60px" /></a> | <a href="https://github.com/taolei1990"><img src="https://avatars.githubusercontent.com/u/23173640?v=4" width="60px" height="60px" /></a> | <a href="https://github.com/hang-kim"><img src="https://avatars.githubusercontent.com/u/52914259?v=4" width="60px" height="60px" /></a> | <a href="https://github.com/madwolfcrazy"><img src="https://avatars.githubusercontent.com/u/223671?v=4" width="60px" height="60px" /></a> | <a href="https://github.com/limuen"><img src="https://avatars.githubusercontent.com/u/31790606?v=4" width="60px" height="60px" /></a> | <a href="https://github.com/BenLakes"><img src="https://avatars.githubusercontent.com/u/15206046?v=4" width="60px" height="60px" /></a> |
| mollerzhu | TLovers | cnyyk | | | |
| <a href="https://github.com/mollerzhu"><img src="https://avatars.githubusercontent.com/u/49627902?v=4" width="60px" height="60px" /></a> | <a href="https://github.com/TLovers"><img src="https://avatars.githubusercontent.com/u/26561694?v=4" width="60px" height="60px" /></a> | <a href="https://github.com/cnyyk"><img src="https://avatars.githubusercontent.com/u/275233?v=4" width="60px" height="60px" /></a> | | | |
## 贡献者 ## 贡献者

View File

@@ -30,7 +30,7 @@ const warpperEnv = (envConf: Recordable): ViteEnv => {
// 跨域代理重写 // 跨域代理重写
const regExps = (value: string, reg: string): string => { const regExps = (value: string, reg: string): string => {
return value.replace(new RegExp(reg, "g"), ""); return value.replace(new RegExp(`^${reg}`, "g"), "");
}; };
// 环境变量 // 环境变量

View File

@@ -1,45 +1,10 @@
import { readdir, stat } from "fs";
import type { Plugin } from "vite"; import type { Plugin } from "vite";
import dayjs, { Dayjs } from "dayjs"; import dayjs, { Dayjs } from "dayjs";
import { sum } from "lodash-unified";
import duration from "dayjs/plugin/duration"; import duration from "dayjs/plugin/duration";
import { green, blue, bold } from "picocolors"; import { green, blue, bold } from "picocolors";
import { getPackageSize } from "@pureadmin/utils";
dayjs.extend(duration); dayjs.extend(duration);
const staticPath = "dist";
const fileListTotal: number[] = [];
const recursiveDirectory = (folder: string, callback: Function): void => {
readdir(folder, (err, files: string[]) => {
if (err) throw err;
let count = 0;
const checkEnd = () => {
++count == files.length && callback();
};
files.forEach((item: string) => {
stat(folder + "/" + item, async (err, stats) => {
if (err) throw err;
if (stats.isFile()) {
fileListTotal.push(stats.size);
checkEnd();
} else if (stats.isDirectory()) {
recursiveDirectory(`${staticPath}/${item}/`, checkEnd);
}
});
});
files.length === 0 && callback();
});
};
const formatBytes = (a: number, b?: number): string => {
if (0 == a) return "0 Bytes";
const c = 1024,
d = b || 2,
e = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"],
f = Math.floor(Math.log(a) / Math.log(c));
return parseFloat((a / Math.pow(c, f)).toFixed(d)) + " " + e[f];
};
export function viteBuildInfo(): Plugin { export function viteBuildInfo(): Plugin {
let config: { command: string }; let config: { command: string };
let startTime: Dayjs; let startTime: Dayjs;
@@ -66,18 +31,18 @@ export function viteBuildInfo(): Plugin {
closeBundle() { closeBundle() {
if (config.command === "build") { if (config.command === "build") {
endTime = dayjs(new Date()); endTime = dayjs(new Date());
recursiveDirectory(staticPath, () => { getPackageSize({
console.log( callback: (size: string) => {
bold( console.log(
green( bold(
`恭喜打包完成🎉(总用时${dayjs green(
.duration(endTime.diff(startTime)) `🎉恭喜打包完成(总用时${dayjs
.format("mm分ss秒")},打包后的大小为${formatBytes( .duration(endTime.diff(startTime))
sum(fileListTotal) .format("mm分ss秒")},打包后的大小为${size}`
)}` )
) )
) );
); }
}); });
} }
} }

View File

@@ -1,5 +1,4 @@
import { resolve } from "path"; import { resolve } from "path";
import Unocss from "unocss/vite";
import vue from "@vitejs/plugin-vue"; import vue from "@vitejs/plugin-vue";
import { viteBuildInfo } from "./info"; import { viteBuildInfo } from "./info";
import svgLoader from "vite-svg-loader"; import svgLoader from "vite-svg-loader";
@@ -11,6 +10,7 @@ import { visualizer } from "rollup-plugin-visualizer";
import removeConsole from "vite-plugin-remove-console"; import removeConsole from "vite-plugin-remove-console";
import themePreprocessorPlugin from "@pureadmin/theme"; import themePreprocessorPlugin from "@pureadmin/theme";
import { genScssMultipleScopeVars } from "/@/layout/theme"; import { genScssMultipleScopeVars } from "/@/layout/theme";
import DefineOptions from "unplugin-vue-define-options/vite";
export function getPluginsList(command, VITE_LEGACY) { export function getPluginsList(command, VITE_LEGACY) {
const prodMock = true; const prodMock = true;
@@ -25,9 +25,9 @@ export function getPluginsList(command, VITE_LEGACY) {
}), }),
// jsx、tsx语法支持 // jsx、tsx语法支持
vueJsx(), vueJsx(),
Unocss(), DefineOptions(),
// 线上环境删除console // 线上环境删除console
removeConsole(), removeConsole({ external: ["src/assets/iconfont/iconfont.js"] }),
viteBuildInfo(), viteBuildInfo(),
// 自定义主题 // 自定义主题
themePreprocessorPlugin({ themePreprocessorPlugin({

View File

@@ -1,30 +0,0 @@
#!/usr/bin/env sh
# Replace packaging path
sed -i "s#VITE_PUBLIC_PATH = /#VITE_PUBLIC_PATH = /vue-pure-admin/#g" $(pwd)/.env.production
# Make sure the script throws the error encountered
set -e
pnpm build
cd dist
touch README.md .nojekyll
# deploy to github
if [ -z "$GITHUB_TOKEN" ]; then
msg='deploy'
githubUrl=git@github.com:xiaoxian521/vue-pure-admin.git
else
msg='ci: Automatic deployment from github actions'
githubUrl=https://xiaoxian521:${GITHUB_TOKEN}@github.com/xiaoxian521/vue-pure-admin.git
git config --global user.name "xiaoxian521"
git config --global user.email "1923740402@qq.com"
fi
git init
git add -A
git commit -m "${msg}"
# Push to github gh-pages branch
git push -f $githubUrl master:gh-pages
cd -
rm -rf dist

View File

@@ -2,10 +2,14 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" /> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="renderer" content="webkit" />
<meta
name="viewport"
content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=0"
/>
<title>vue-pure-admin</title> <title>vue-pure-admin</title>
<script src="/sortable.min.js"></script> <link rel="icon" href="/favicon.ico" />
<script> <script>
window.process = {}; window.process = {};
</script> </script>

View File

@@ -21,6 +21,7 @@ buttons:
menus: menus:
hshome: Home hshome: Home
hslogin: Login hslogin: Login
hsempty: Empty Page
hssysManagement: System Manage hssysManagement: System Manage
hsUser: User Manage hsUser: User Manage
hsDict: Dict Manage hsDict: Dict Manage
@@ -82,5 +83,49 @@ menus:
hsQrcode: Qrcode hsQrcode: Qrcode
hsCascader: Area Cascader hsCascader: Area Cascader
hsSwiper: Swiper Plugin hsSwiper: Swiper Plugin
hsVirtualList: Virtual List
hsPdf: PDF Preview
hsExecl: Export Excel
hsInfiniteScroll: Table Infinite Scroll
status: status:
hsLoad: Loading... hsLoad: Loading...
login:
username: Username
password: Password
verifyCode: VerifyCode
remember: Remember Password
sure: Sure Password
forget: Forget Password?
login: Login
thirdLogin: Third Login
phoneLogin: Phone Login
qRCodeLogin: QRCode Login
register: Register
weChatLogin: WeChat Login
alipayLogin: Alipay Login
qqLogin: QQ Login
weiboLogin: Weibo Login
phone: Phone
smsVerifyCode: SMS VerifyCode
back: Back
test: Mock Test
tip: After scanning the code, click "Confirm" to complete the login
definite: Definite
loginSuccess: Login Success
registerSuccess: Regist Success
tickPrivacy: Please tick Privacy Policy
readAccept: I have read it carefully and accept
privacyPolicy: Privacy Policy
getVerifyCode: Get VerifyCode
info: Seconds
usernameReg: Please enter username
passwordReg: Please enter password
verifyCodeReg: Please enter verify code
verifyCodeCorrectReg: Please enter correct verify code
verifyCodeSixReg: Please enter a 6-digit verify code
phoneReg: Please enter the phone
phoneCorrectReg: Please enter the correct phone number format
passwordRuleReg: The password format should be any combination of 8-18 digits
passwordSureReg: Please enter confirm password
passwordDifferentReg: The two passwords do not match!
passwordUpdateReg: Password has been updated

View File

@@ -3,7 +3,7 @@ buttons:
hsfullscreen: 全屏 hsfullscreen: 全屏
hsexitfullscreen: 退出全屏 hsexitfullscreen: 退出全屏
hsrefreshRoute: 刷新路由 hsrefreshRoute: 刷新路由
hslogin: hslogin:
hsadd: 新增 hsadd: 新增
hsmark: 标记/取消 hsmark: 标记/取消
hssave: 保存 hssave: 保存
@@ -20,7 +20,8 @@ buttons:
hscloseAllTabs: 关闭全部标签页 hscloseAllTabs: 关闭全部标签页
menus: menus:
hshome: 首页 hshome: 首页
hslogin: hslogin:
hsempty: 无Layout页
hssysManagement: 系统管理 hssysManagement: 系统管理
hsUser: 用户管理 hsUser: 用户管理
hsDict: 字典管理 hsDict: 字典管理
@@ -82,5 +83,49 @@ menus:
hsQrcode: 二维码 hsQrcode: 二维码
hsCascader: 区域级联选择器 hsCascader: 区域级联选择器
hsSwiper: Swiper插件 hsSwiper: Swiper插件
hsVirtualList: 虚拟列表
hsPdf: PDF预览
hsExecl: 导出Excel
hsInfiniteScroll: 表格无限滚动
status: status:
hsLoad: 加载中... hsLoad: 加载中...
login:
username: 账号
password: 密码
verifyCode: 验证码
remember: 记住密码
sure: 确认密码
forget: 忘记密码?
login: 登录
thirdLogin: 第三方登录
phoneLogin: 手机登录
qRCodeLogin: 二维码登录
register: 注册
weChatLogin: 微信登录
alipayLogin: 支付宝登录
qqLogin: QQ登录
weiboLogin: 微博登录
phone: 手机号码
smsVerifyCode: 短信验证码
back: 返回
test: 模拟测试
tip: 扫码后点击"确认",即可完成登录
definite: 确定
loginSuccess: 登录成功
registerSuccess: 注册成功
tickPrivacy: 请勾选隐私政策
readAccept: 我已仔细阅读并接受
privacyPolicy: 《隐私政策》
getVerifyCode: 获取验证码
info: 秒后重新获取
usernameReg: 请输入账号
passwordReg: 请输入密码
verifyCodeReg: 请输入验证码
verifyCodeCorrectReg: 请输入正确的验证码
verifyCodeSixReg: 请输入6位数字验证码
phoneReg: 请输入手机号码
phoneCorrectReg: 请输入正确的手机号码格式
passwordRuleReg: 密码格式应为8-18位数字、字母、符号的任意两种组合
passwordSureReg: 请输入确认密码
passwordDifferentReg: 两次密码不一致!
passwordUpdateReg: 修改密码成功

View File

@@ -4,7 +4,6 @@ import { MockMethod } from "vite-plugin-mock";
// http://mockjs.com/examples.html#Object // http://mockjs.com/examples.html#Object
const systemRouter = { const systemRouter = {
path: "/system", path: "/system",
redirect: "/system/user/index",
meta: { meta: {
icon: "setting", icon: "setting",
title: "menus.hssysManagement", title: "menus.hssysManagement",
@@ -13,7 +12,7 @@ const systemRouter = {
children: [ children: [
{ {
path: "/system/user/index", path: "/system/user/index",
name: "user", name: "User",
meta: { meta: {
icon: "flUser", icon: "flUser",
title: "menus.hsUser" title: "menus.hsUser"
@@ -21,7 +20,7 @@ const systemRouter = {
}, },
{ {
path: "/system/role/index", path: "/system/role/index",
name: "role", name: "Role",
meta: { meta: {
icon: "role", icon: "role",
title: "menus.hsRole" title: "menus.hsRole"
@@ -29,7 +28,7 @@ const systemRouter = {
}, },
{ {
path: "/system/dept/index", path: "/system/dept/index",
name: "dept", name: "Dept",
meta: { meta: {
icon: "dept", icon: "dept",
title: "menus.hsDept" title: "menus.hsDept"
@@ -38,7 +37,7 @@ const systemRouter = {
{ {
path: "/system/dict", path: "/system/dict",
component: "/system/dict/index", component: "/system/dict/index",
name: "dict", name: "Dict",
meta: { meta: {
icon: "dict", icon: "dict",
title: "menus.hsDict", title: "menus.hsDict",
@@ -50,7 +49,6 @@ const systemRouter = {
const permissionRouter = { const permissionRouter = {
path: "/permission", path: "/permission",
redirect: "/permission/page/index",
meta: { meta: {
title: "menus.permission", title: "menus.permission",
icon: "lollipop", icon: "lollipop",
@@ -59,14 +57,14 @@ const permissionRouter = {
children: [ children: [
{ {
path: "/permission/page/index", path: "/permission/page/index",
name: "permissionPage", name: "PermissionPage",
meta: { meta: {
title: "menus.permissionPage" title: "menus.permissionPage"
} }
}, },
{ {
path: "/permission/button/index", path: "/permission/button/index",
name: "permissionButton", name: "PermissionButton",
meta: { meta: {
title: "menus.permissionButton", title: "menus.permissionButton",
authority: [] authority: []
@@ -77,7 +75,6 @@ const permissionRouter = {
const frameRouter = { const frameRouter = {
path: "/iframe", path: "/iframe",
redirect: "/iframe/pure",
meta: { meta: {
icon: "monitor", icon: "monitor",
title: "menus.hsExternalPage", title: "menus.hsExternalPage",
@@ -86,22 +83,22 @@ const frameRouter = {
children: [ children: [
{ {
path: "/iframe/pure", path: "/iframe/pure",
name: "reFramePure", name: "FramePure",
meta: { meta: {
title: "menus.hsPureDocument", title: "menus.hsPureDocument",
frameSrc: "https://pure-admin-doc.vercel.app" frameSrc: "http://yiming_chang.gitee.io/pure-admin-doc"
} }
}, },
{ {
path: "/external", path: "/external",
name: "https://pure-admin-doc.vercel.app", name: "http://yiming_chang.gitee.io/pure-admin-doc",
meta: { meta: {
title: "menus.externalLink" title: "menus.externalLink"
} }
}, },
{ {
path: "/iframe/ep", path: "/iframe/ep",
name: "reFrameEp", name: "FrameEp",
meta: { meta: {
title: "menus.hsEpDocument", title: "menus.hsEpDocument",
frameSrc: "https://element-plus.org/zh-CN/" frameSrc: "https://element-plus.org/zh-CN/"
@@ -112,7 +109,6 @@ const frameRouter = {
const tabsRouter = { const tabsRouter = {
path: "/tabs", path: "/tabs",
redirect: "/tabs/index",
meta: { meta: {
icon: "IF-team-icontabs", icon: "IF-team-icontabs",
title: "menus.hstabs", title: "menus.hstabs",
@@ -121,19 +117,25 @@ const tabsRouter = {
children: [ children: [
{ {
path: "/tabs/index", path: "/tabs/index",
name: "reTabs", name: "Tabs",
meta: { meta: {
title: "menus.hstabs" title: "menus.hstabs"
} }
}, },
{ {
path: "/tabs/detail", path: "/tabs/query-detail",
name: "tabDetail", name: "TabQueryDetail",
meta: { meta: {
title: "", // 不在menu菜单中显示
showLink: false, showLink: false
dynamicLevel: 3, }
refreshRedirect: "/tabs/index" },
{
path: "/tabs/params-detail/:id",
component: "params-detail",
name: "TabParamsDetail",
meta: {
showLink: false
} }
} }
] ]

View File

@@ -1,19 +1,20 @@
{ {
"name": "vue-pure-admin", "name": "vue-pure-admin",
"version": "3.2.0", "version": "3.5.0",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "cross-env --max_old_space_size=4096 vite", "dev": "NODE_OPTIONS=--max-old-space-size=4096 vite",
"serve": "pnpm dev", "serve": "pnpm dev",
"build": "rimraf dist && cross-env vite build", "build": "rimraf dist && NODE_OPTIONS=--max-old-space-size=8192 vite build",
"build:staging": "rimraf dist && cross-env vite build --mode staging", "build:staging": "rimraf dist && vite build --mode staging",
"report": "rimraf dist && cross-env vite build", "report": "rimraf dist && vite build",
"deploy": "bash deploy.sh",
"preview": "vite preview", "preview": "vite preview",
"preview:build": "pnpm build && vite preview", "preview:build": "pnpm build && vite preview",
"typecheck": "tsc --noEmit && vue-tsc --noEmit --skipLibCheck",
"cloc": "NODE_OPTIONS=--max-old-space-size=4096 cloc . --exclude-dir=node_modules --exclude-lang=YAML",
"clean:cache": "rm -rf node_modules && rm -rf .eslintcache && pnpm install", "clean:cache": "rm -rf node_modules && rm -rf .eslintcache && pnpm install",
"lint:eslint": "eslint --cache --max-warnings 0 \"{src,mock}/**/*.{vue,ts,tsx}\" --fix", "lint:eslint": "eslint --cache --max-warnings 0 \"{src,mock,build}/**/*.{vue,js,ts,tsx}\" --fix",
"lint:prettier": "prettier --write \"src/**/*.{js,json,tsx,css,less,scss,vue,html,md}\"", "lint:prettier": "prettier --write \"src/**/*.{js,ts,json,tsx,css,less,scss,vue,html,md}\"",
"lint:stylelint": "stylelint --cache --fix \"**/*.{vue,css,scss,postcss,less}\" --cache --cache-location node_modules/.cache/stylelint/", "lint:stylelint": "stylelint --cache --fix \"**/*.{vue,css,scss,postcss,less}\" --cache --cache-location node_modules/.cache/stylelint/",
"lint:lint-staged": "lint-staged -c ./.husky/lintstagedrc.js", "lint:lint-staged": "lint-staged -c ./.husky/lintstagedrc.js",
"lint:pretty": "pretty-quick --staged", "lint:pretty": "pretty-quick --staged",
@@ -29,23 +30,26 @@
"dependencies": { "dependencies": {
"@amap/amap-jsapi-loader": "^1.0.1", "@amap/amap-jsapi-loader": "^1.0.1",
"@ctrl/tinycolor": "^3.4.1", "@ctrl/tinycolor": "^3.4.1",
"@logicflow/core": "^1.1.15", "@logicflow/core": "^1.1.24",
"@logicflow/extension": "^1.1.15", "@logicflow/extension": "^1.1.24",
"@pureadmin/components": "^1.0.6", "@pureadmin/components": "^1.1.0",
"@vueuse/core": "^8.4.2", "@pureadmin/descriptions": "^1.1.0",
"@pureadmin/table": "^1.2.0",
"@pureadmin/utils": "^0.1.1",
"@vueuse/core": "^9.1.1",
"@vueuse/motion": "^2.0.0-beta.12", "@vueuse/motion": "^2.0.0-beta.12",
"@vueuse/shared": "^8.4.2", "@vueuse/shared": "^9.1.1",
"@wangeditor/editor": "^5.0.1", "@wangeditor/editor": "^5.1.14",
"@wangeditor/editor-for-vue": "^5.1.10", "@wangeditor/editor-for-vue": "^5.1.12",
"animate.css": "^4.1.1", "animate.css": "^4.1.1",
"axios": "^0.27.1", "axios": "^0.27.2",
"china-area-data": "^5.0.1", "china-area-data": "^5.0.1",
"cropperjs": "^1.5.12", "cropperjs": "^1.5.12",
"css-color-function": "^1.3.3", "dayjs": "^1.11.4",
"dayjs": "^1.11.2",
"driver.js": "^0.9.8", "driver.js": "^0.9.8",
"echarts": "^5.3.2", "echarts": "^5.3.3",
"element-plus": "2.1.11", "el-table-infinite-scroll": "^3.0.1",
"element-plus": "^2.2.16",
"element-resize-detector": "^1.2.3", "element-resize-detector": "^1.2.3",
"js-cookie": "^3.0.1", "js-cookie": "^3.0.1",
"jsbarcode": "^3.11.5", "jsbarcode": "^3.11.5",
@@ -56,39 +60,43 @@
"mockjs": "^1.1.0", "mockjs": "^1.1.0",
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
"path": "^0.12.7", "path": "^0.12.7",
"pinia": "^2.0.14", "pinia": "^2.0.21",
"qrcode": "^1.5.0", "qrcode": "^1.5.1",
"qs": "^6.10.1", "qs": "^6.11.0",
"resize-observer-polyfill": "^1.5.1", "resize-observer-polyfill": "^1.5.1",
"responsive-storage": "^1.0.11", "responsive-storage": "^2.1.0",
"rgb-hex": "^4.0.0", "sortablejs": "^1.15.0",
"swiper": "^8.1.4", "swiper": "^8.3.2",
"v-contextmenu": "3.0.0", "v-contextmenu": "3.0.0",
"vue": "^3.2.33", "vue": "^3.2.39",
"vue-form-create2": "^1.2.8", "vue-form-create2": "^1.2.8",
"vue-i18n": "^9.2.0-beta.35", "vue-i18n": "^9.2.2",
"vue-json-pretty": "^2.0.2", "vue-json-pretty": "^2.1.1",
"vue-router": "^4.0.15", "vue-pdf-embed": "^1.1.4",
"vue-types": "^4.1.1", "vue-router": "^4.1.5",
"vuedraggable": "4.1.0", "vue-types": "^4.2.1",
"vxe-table": "^4.2.3", "vue-virtual-scroller": "^2.0.0-alpha.1",
"xe-utils": "^3.5.4", "vuedraggable": "^4.1.0",
"xgplayer": "^2.31.6" "vxe-table": "^4.3.2",
"xe-utils": "^3.5.6",
"xgplayer": "^2.31.7",
"xlsx": "^0.18.5"
}, },
"devDependencies": { "devDependencies": {
"@commitlint/cli": "13.1.0", "@commitlint/cli": "13.1.0",
"@commitlint/config-conventional": "13.1.0", "@commitlint/config-conventional": "13.1.0",
"@iconify-icons/carbon": "^1.2.4", "@faker-js/faker": "^7.4.0",
"@iconify-icons/ep": "^1.2.4", "@iconify-icons/carbon": "^1.2.8",
"@iconify-icons/fa": "^1.2.2", "@iconify-icons/ep": "^1.2.7",
"@iconify-icons/fa-solid": "^1.2.2", "@iconify-icons/fa": "^1.2.3",
"@iconify-icons/fluent": "^1.2.5", "@iconify-icons/fa-solid": "^1.2.3",
"@iconify-icons/fluent": "^1.2.16",
"@iconify-icons/mdi": "^1.2.8", "@iconify-icons/mdi": "^1.2.8",
"@iconify-icons/ri": "^1.2.1", "@iconify-icons/ri": "^1.2.3",
"@iconify-icons/uil": "^1.2.1", "@iconify-icons/uil": "^1.2.2",
"@iconify/vue": "^3.2.1", "@iconify/vue": "^3.2.1",
"@intlify/vite-plugin-vue-i18n": "^3.4.0", "@intlify/vite-plugin-vue-i18n": "^6.0.1",
"@pureadmin/theme": "^1.1.0", "@pureadmin/theme": "^2.4.0",
"@types/element-resize-detector": "1.1.3", "@types/element-resize-detector": "1.1.3",
"@types/js-cookie": "^3.0.1", "@types/js-cookie": "^3.0.1",
"@types/lodash": "^4.14.180", "@types/lodash": "^4.14.180",
@@ -98,15 +106,18 @@
"@types/nprogress": "0.2.0", "@types/nprogress": "0.2.0",
"@types/qrcode": "^1.4.2", "@types/qrcode": "^1.4.2",
"@types/qs": "^6.9.7", "@types/qs": "^6.9.7",
"@types/sortablejs": "^1.13.0",
"@typescript-eslint/eslint-plugin": "^5.10.2", "@typescript-eslint/eslint-plugin": "^5.10.2",
"@typescript-eslint/parser": "^5.10.2", "@typescript-eslint/parser": "^5.10.2",
"@vitejs/plugin-legacy": "^1.8.2", "@vitejs/plugin-legacy": "^2.1.0",
"@vitejs/plugin-vue": "^2.3.2", "@vitejs/plugin-vue": "^3.1.0",
"@vitejs/plugin-vue-jsx": "^1.3.10", "@vitejs/plugin-vue-jsx": "^2.0.1",
"@vue/eslint-config-prettier": "^7.0.0", "@vue/eslint-config-prettier": "^7.0.0",
"@vue/eslint-config-typescript": "^10.0.0", "@vue/eslint-config-typescript": "^10.0.0",
"autoprefixer": "^10.4.5", "@vue/runtime-core": "^3.2.39",
"cross-env": "7.0.3", "autoprefixer": "^10.4.8",
"cloc": "^2.10.0",
"cssnano": "^5.1.13",
"eslint": "^8.8.0", "eslint": "^8.8.0",
"eslint-plugin-prettier": "^4.0.0", "eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-vue": "^8.4.1", "eslint-plugin-vue": "^8.4.1",
@@ -114,29 +125,40 @@
"husky": "^7.0.4", "husky": "^7.0.4",
"lint-staged": "11.1.2", "lint-staged": "11.1.2",
"picocolors": "^1.0.0", "picocolors": "^1.0.0",
"postcss": "^8.4.6", "postcss": "^8.4.16",
"postcss-html": "^1.3.0", "postcss-html": "^1.5.0",
"postcss-import": "14.0.0", "postcss-import": "^15.0.0",
"postcss-scss": "^4.0.3", "postcss-scss": "^4.0.4",
"prettier": "^2.5.1", "prettier": "^2.5.1",
"pretty-quick": "3.1.1", "pretty-quick": "3.1.1",
"rimraf": "3.0.2", "rimraf": "3.0.2",
"rollup": "^2.70.1", "rollup-plugin-visualizer": "^5.8.1",
"rollup-plugin-visualizer": "^5.6.0", "sass": "^1.53.0",
"sass": "^1.51.0", "sass-loader": "^13.0.2",
"stylelint": "^14.3.0", "stylelint": "^14.3.0",
"stylelint-config-html": "^1.0.0", "stylelint-config-html": "^1.0.0",
"stylelint-config-prettier": "^9.0.3", "stylelint-config-prettier": "^9.0.3",
"stylelint-config-recommended": "^6.0.0", "stylelint-config-recommended": "^6.0.0",
"stylelint-config-standard": "^24.0.0", "stylelint-config-standard": "^24.0.0",
"stylelint-order": "^5.0.0", "stylelint-order": "^5.0.0",
"typescript": "^4.6.3", "tailwindcss": "^3.1.8",
"unocss": "^0.33.2", "terser": "^5.15.0",
"vite": "^2.9.6", "typescript": "^4.7.4",
"unplugin-vue-define-options": "0.7.3",
"vite": "^3.1.0",
"vite-plugin-mock": "^2.9.6", "vite-plugin-mock": "^2.9.6",
"vite-plugin-remove-console": "^0.0.7", "vite-plugin-remove-console": "^1.1.0",
"vite-svg-loader": "^3.3.0", "vite-svg-loader": "^3.4.0",
"vue-eslint-parser": "^8.2.0" "vue-eslint-parser": "^8.2.0",
"vue-tsc": "^0.40.7"
},
"pnpm": {
"peerDependencyRules": {
"ignoreMissing": [
"rollup",
"webpack"
]
}
}, },
"repository": "git@github.com:xiaoxian521/vue-pure-admin.git", "repository": "git@github.com:xiaoxian521/vue-pure-admin.git",
"author": "xiaoxian521", "author": "xiaoxian521",

4216
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,3 +1,8 @@
module.exports = { module.exports = {
plugins: [require("autoprefixer"), require("postcss-import")] plugins: {
"postcss-import": {},
tailwindcss: {},
autoprefixer: {},
...(process.env.NODE_ENV === "production" ? { cssnano: {} } : {})
}
}; };

View File

@@ -1,5 +1,5 @@
{ {
"Version": "3.2.0", "Version": "3.5.0",
"Title": "PureAdmin", "Title": "PureAdmin",
"FixedHeader": true, "FixedHeader": true,
"HiddenSideBar": false, "HiddenSideBar": false,

2607
public/sortable.min.js vendored

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +1,15 @@
import { http } from "../utils/http"; import { http } from "../utils/http";
interface postType extends Promise<any> { type Result = {
data?: object; data?: {
/** 列表数据 */
list: Array<any>;
};
code?: number; code?: number;
msg?: string; msg?: string;
} };
// 卡片列表 // 卡片列表
export const getCardList = (data?: object): postType => { export const getCardList = (data?: object) => {
return http.request("post", "/getCardList", { data }); return http.request<Result>("post", "/getCardList", { data });
}; };

View File

@@ -1,6 +1,11 @@
import { http } from "../utils/http"; import { http } from "../utils/http";
type Result = {
code: number;
info: Array<any>;
};
// 地图数据 // 地图数据
export const mapJson = (params?: object) => { export const mapJson = (params?: object) => {
return http.request("get", "/getMapInfo", { params }); return http.request<Result>("get", "/getMapInfo", { params });
}; };

View File

@@ -1,5 +1,10 @@
import { http } from "../utils/http"; import { http } from "../utils/http";
export const getAsyncRoutes = (params?: object) => { type Result = {
return http.request("get", "/getAsyncRoutes", { params }); code: number;
info: Array<any>;
};
export const getAsyncRoutes = (params?: object) => {
return http.request<Result>("get", "/getAsyncRoutes", { params });
}; };

View File

@@ -1,22 +1,27 @@
import { http } from "../utils/http"; import { http } from "../utils/http";
interface ResponseType extends Promise<any> { type Result = {
data?: object; data?: {
/** 列表数据 */
list: Array<any>;
/** 总数 */
total: number;
};
code?: number; code?: number;
msg?: string; msg?: string;
} };
// 获取用户管理列表 // 获取用户管理列表
export const getUserList = (data?: object): ResponseType => { export const getUserList = (data?: object) => {
return http.request("post", "/user", { data }); return http.request<Result>("post", "/user", { data });
}; };
// 获取角色管理列表 // 获取角色管理列表
export const getRoleList = (data?: object): ResponseType => { export const getRoleList = (data?: object) => {
return http.request("post", "/role", { data }); return http.request<Result>("post", "/role", { data });
}; };
// 获取部门管理列表 // 获取部门管理列表
export const getDeptList = (data?: object): ResponseType => { export const getDeptList = (data?: object) => {
return http.request("post", "/dept", { data }); return http.request<Result>("post", "/dept", { data });
}; };

View File

@@ -1,14 +1,14 @@
import { http } from "../utils/http"; import { http } from "../utils/http";
interface userType extends Promise<any> { type Result = {
svg?: string; svg?: string;
code?: number; code?: number;
info?: object; info?: object;
} };
// 获取验证码 // 获取验证码
export const getVerify = (): userType => { export const getVerify = () => {
return http.request("get", "/captcha"); return http.request<Result>("get", "/captcha");
}; };
// 登录 // 登录

1
src/assets/svg/back.svg Normal file
View File

@@ -0,0 +1 @@
<svg width="32" height="32" viewBox="0 0 48 48"><path fill="#2F88FF" fill-rule="evenodd" stroke="#000" stroke-linejoin="round" stroke-width="4" d="M44 40.8361C39.1069 34.8632 34.7617 31.4739 30.9644 30.6682C27.1671 29.8625 23.5517 29.7408 20.1182 30.303V41L4 23.5453L20.1182 7V17.167C26.4667 17.2172 31.8638 19.4948 36.3095 24C40.7553 28.5052 43.3187 34.1172 44 40.8361Z" clip-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 399 B

View File

@@ -1,10 +1,7 @@
import { App } from "vue";
import reBarcode from "./src/index.vue"; import reBarcode from "./src/index.vue";
import { withInstall } from "@pureadmin/utils";
export const ReBarcode = Object.assign(reBarcode, { /** 条形码组件 */
install(app: App) { export const ReBarcode = withInstall(reBarcode);
app.component(reBarcode.name, reBarcode);
}
});
export default ReBarcode; export default ReBarcode;

View File

@@ -1,13 +1,11 @@
<script lang="ts">
export default {
name: "ReBarcode"
};
</script>
<script setup lang="ts"> <script setup lang="ts">
import JsBarcode from "jsbarcode"; import JsBarcode from "jsbarcode";
import { ref, onMounted } from "vue"; import { ref, onMounted } from "vue";
defineOptions({
name: "ReBarcode"
});
const props = defineProps({ const props = defineProps({
tag: { tag: {
type: String, type: String,

View File

@@ -1,10 +0,0 @@
import { App } from "vue";
import reCard from "./src/index.vue";
export const ReCard = Object.assign(reCard, {
install(app: App) {
app.component(reCard.name, reCard);
}
});
export default ReCard;

View File

@@ -1,36 +0,0 @@
import { App } from "vue";
import reBar from "./src/Bar.vue";
import reGithub from "./src/Github.vue";
import reInfinite from "./src/Infinite.vue";
import reLine from "./src/Line.vue";
import rePie from "./src/Pie.vue";
export const ReBar = Object.assign(reBar, {
install(app: App) {
app.component(reBar.name, reBar);
}
});
export const ReGithub = Object.assign(reGithub, {
install(app: App) {
app.component(reGithub.name, reGithub);
}
});
export const ReInfinite = Object.assign(reInfinite, {
install(app: App) {
app.component(reInfinite.name, reInfinite);
}
});
export const ReLine = Object.assign(reLine, {
install(app: App) {
app.component(reLine.name, reLine);
}
});
export const RePie = Object.assign(rePie, {
install(app: App) {
app.component(rePie.name, rePie);
}
});

View File

@@ -1,96 +0,0 @@
<script lang="ts">
export default {
name: "Bar"
};
</script>
<script setup lang="ts">
import { ECharts } from "echarts";
import echarts from "/@/plugins/echarts";
import { onBeforeMount, onMounted, nextTick } from "vue";
import { useEventListener, tryOnUnmounted, useTimeoutFn } from "@vueuse/core";
let echartInstance: ECharts;
const props = defineProps({
index: {
type: Number,
default: 0
}
});
function initechartInstance() {
const echartDom = document.querySelector(".bar" + props.index);
if (!echartDom) return;
// @ts-ignore
echartInstance = echarts.init(echartDom);
echartInstance.clear(); //清除旧画布 重新渲染
echartInstance.setOption({
tooltip: {
trigger: "axis",
axisPointer: {
type: "shadow"
}
},
grid: {
bottom: "20%",
height: "68%",
containLabel: true
},
xAxis: [
{
type: "category",
axisTick: {
alignWithLabel: true
},
axisLabel: {
interval: 0
// width: "70",
// overflow: "truncate"
},
data: ["open_issues", "forks", "watchers", "star"]
}
],
yAxis: [
{
type: "value"
}
],
series: [
{
name: "GitHub信息",
type: "bar",
data: [3, 204, 1079, 1079]
}
]
});
}
onBeforeMount(() => {
nextTick(() => {
initechartInstance();
});
});
onMounted(() => {
nextTick(() => {
useEventListener("resize", () => {
if (!echartInstance) return;
useTimeoutFn(() => {
echartInstance.resize();
}, 180);
});
});
});
tryOnUnmounted(() => {
if (!echartInstance) return;
echartInstance.dispose();
echartInstance = null;
});
</script>
<template>
<div :class="'bar' + props.index" style="width: 100%; height: 35vh" />
</template>

View File

@@ -1,87 +0,0 @@
<script setup lang="ts">
import { ref } from "vue";
const lists = ref([
{ type: "", label: "善良" },
{ type: "success", label: "好学" },
{ type: "info", label: "幽默" },
{ type: "danger", label: "旅游" },
{ type: "warning", label: "追剧" }
]);
</script>
<template>
<el-descriptions class="margin-top" direction="vertical" :column="3" border>
<el-descriptions-item>
<template #label>
<el-icon>
<IconifyIconOffline icon="user" />
</el-icon>
用户名
</template>
xiaoxian
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<el-icon>
<IconifyIconOffline icon="iphone" />
</el-icon>
手机号
</template>
123456789
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<el-icon>
<IconifyIconOffline icon="location" />
</el-icon>
居住地
</template>
上海
</el-descriptions-item>
</el-descriptions>
<el-descriptions class="margin-top" direction="vertical" :column="2" border>
<el-descriptions-item>
<template #label>
<el-icon>
<IconifyIconOffline icon="tickets" />
</el-icon>
标签
</template>
<el-tag
v-for="item in lists"
:key="item.label"
:type="item.type"
size="small"
effect="dark"
>
{{ item.label }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<el-icon>
<IconifyIconOffline icon="office-building" />
</el-icon>
联系地址
</template>
上海市徐汇区
</el-descriptions-item>
</el-descriptions>
<el-descriptions class="margin-top" direction="vertical" :column="1" border>
<el-descriptions-item>
<template #label>
<el-icon>
<IconifyIconOffline icon="notebook" />
</el-icon>
留言
</template>
好好学习天天向上
</el-descriptions-item>
</el-descriptions>
</template>
<style scoped>
.el-tag {
margin-right: 10px !important;
}
</style>

View File

@@ -1,84 +0,0 @@
<script lang="ts">
export default {
name: "Line"
};
</script>
<script setup lang="ts">
import { ECharts } from "echarts";
import echarts from "/@/plugins/echarts";
import { onBeforeMount, onMounted, nextTick } from "vue";
import { useEventListener, tryOnUnmounted, useTimeoutFn } from "@vueuse/core";
let echartInstance: ECharts;
const props = defineProps({
index: {
type: Number,
default: 0
}
});
function initechartInstance() {
const echartDom = document.querySelector(".line" + props.index);
if (!echartDom) return;
// @ts-ignore
echartInstance = echarts.init(echartDom);
echartInstance.clear(); //清除旧画布 重新渲染
echartInstance.setOption({
grid: {
bottom: "20%",
height: "68%",
containLabel: true
},
tooltip: {
trigger: "item"
},
xAxis: {
type: "category",
axisLabel: {
interval: 0
},
data: ["open_issues", "forks", "watchers", "star"]
},
yAxis: {
type: "value"
},
series: [
{
data: [3, 204, 1079, 1079],
type: "line",
areaStyle: {}
}
]
});
}
onBeforeMount(() => {
nextTick(() => {
initechartInstance();
});
});
onMounted(() => {
nextTick(() => {
useEventListener("resize", () => {
if (!echartInstance) return;
useTimeoutFn(() => {
echartInstance.resize();
}, 180);
});
});
});
tryOnUnmounted(() => {
if (!echartInstance) return;
echartInstance.dispose();
echartInstance = null;
});
</script>
<template>
<div :class="'line' + props.index" style="width: 100%; height: 35vh" />
</template>

View File

@@ -1,87 +0,0 @@
<script lang="ts">
export default {
name: "Pie"
};
</script>
<script setup lang="ts">
import { ECharts } from "echarts";
import echarts from "/@/plugins/echarts";
import { onBeforeMount, onMounted, nextTick } from "vue";
import { useEventListener, tryOnUnmounted, useTimeoutFn } from "@vueuse/core";
let echartInstance: ECharts;
const props = defineProps({
index: {
type: Number,
default: 0
}
});
function initechartInstance() {
const echartDom = document.querySelector(".pie" + props.index);
if (!echartDom) return;
// @ts-ignore
echartInstance = echarts.init(echartDom);
echartInstance.clear(); //清除旧画布 重新渲染
echartInstance.setOption({
tooltip: {
trigger: "item"
},
legend: {
orient: "vertical",
right: true
},
series: [
{
name: "Github信息",
type: "pie",
radius: "60%",
center: ["40%", "50%"],
data: [
{ value: 1079, name: "watchers" },
{ value: 1079, name: "star" },
{ value: 204, name: "forks" },
{ value: 3, name: "open_issues" }
],
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: "rgba(0, 0, 0, 0.5)"
}
}
}
]
});
}
onBeforeMount(() => {
nextTick(() => {
initechartInstance();
});
});
onMounted(() => {
nextTick(() => {
useEventListener("resize", () => {
if (!echartInstance) return;
useTimeoutFn(() => {
echartInstance.resize();
}, 180);
});
});
});
tryOnUnmounted(() => {
if (!echartInstance) return;
echartInstance.dispose();
echartInstance = null;
});
</script>
<template>
<div :class="'pie' + props.index" style="width: 100%; height: 35vh" />
</template>

View File

@@ -1,15 +1,11 @@
import { App } from "vue";
import reNormalCountTo from "./src/normal"; import reNormalCountTo from "./src/normal";
import reboundCountTo from "./src/rebound"; import reboundCountTo from "./src/rebound";
import { withInstall } from "@pureadmin/utils";
export const ReNormalCountTo = Object.assign(reNormalCountTo, { /** 普通数字动画组件 */
install(app: App) { const ReNormalCountTo = withInstall(reNormalCountTo);
app.component(reNormalCountTo.name, reNormalCountTo);
}
});
export const ReboundCountTo = Object.assign(reboundCountTo, { /** 回弹式数字动画组件 */
install(app: App) { const ReboundCountTo = withInstall(reboundCountTo);
app.component(reboundCountTo.name, reboundCountTo);
} export { ReNormalCountTo, ReboundCountTo };
});

View File

@@ -7,10 +7,10 @@ import {
unref unref
} from "vue"; } from "vue";
import { countToProps } from "./props"; import { countToProps } from "./props";
import { isNumber } from "/@/utils/is"; import { isNumber } from "@pureadmin/utils";
export default defineComponent({ export default defineComponent({
name: "Normal", name: "ReNormalCountTo",
props: countToProps, props: countToProps,
emits: ["mounted", "callback"], emits: ["mounted", "callback"],
setup(props, { emit }) { setup(props, { emit }) {

View File

@@ -4,15 +4,15 @@ import {
ref, ref,
unref, unref,
onBeforeMount, onBeforeMount,
onBeforeUnmount, onBeforeUnmount
getCurrentInstance
} from "vue"; } from "vue";
import { reboundProps } from "./props"; import { reboundProps } from "./props";
export default defineComponent({ export default defineComponent({
name: "Rebound", name: "ReboundCountTo",
props: reboundProps, props: reboundProps,
setup(props) { setup(props) {
const ulRef = ref();
const timer = ref(null); const timer = ref(null);
onBeforeMount(() => { onBeforeMount(() => {
@@ -23,8 +23,7 @@ export default defineComponent({
// Safari浏览器的兼容代码 // Safari浏览器的兼容代码
isSafari && isSafari &&
(timer.value = setTimeout(() => { (timer.value = setTimeout(() => {
// @ts-ignore ulRef.value.setAttribute(
getCurrentInstance().refs["ul"].setAttribute(
"style", "style",
` `
animation: none; animation: none;
@@ -42,10 +41,9 @@ export default defineComponent({
<> <>
<div <div
class="scroll-num" class="scroll-num"
// @ts-ignore
style={{ "--i": props.i, "--delay": props.delay }} style={{ "--i": props.i, "--delay": props.delay }}
> >
<ul ref="ul" style={{ fontSize: "32px" }}> <ul ref="ulRef" style={{ fontSize: "32px" }}>
<li>0</li> <li>0</li>
<li>1</li> <li>1</li>
<li>2</li> <li>2</li>

View File

@@ -1,10 +1,7 @@
import { App } from "vue";
import reCropper from "./src"; import reCropper from "./src";
import { withInstall } from "@pureadmin/utils";
export const ReCropper = Object.assign(reCropper, { /** 图片裁剪组件 */
install(app: App) { export const ReCropper = withInstall(reCropper);
app.component(reCropper.name, reCropper);
}
});
export default ReCropper; export default ReCropper;

View File

@@ -9,7 +9,7 @@ import {
PropType PropType
} from "vue"; } from "vue";
import { templateRef } from "@vueuse/core"; import { templateRef } from "@vueuse/core";
import { useAttrs } from "/@/utils/useAttrs"; import { useAttrs } from "@pureadmin/utils";
import Cropper from "cropperjs"; import Cropper from "cropperjs";
import "cropperjs/dist/cropper.css"; import "cropperjs/dist/cropper.css";
@@ -74,7 +74,7 @@ const props = {
}; };
export default defineComponent({ export default defineComponent({
name: "Cropper", name: "ReCropper",
props, props,
setup(props) { setup(props) {
const cropper: any = ref<Nullable<Cropper>>(null); const cropper: any = ref<Nullable<Cropper>>(null);

View File

@@ -20,7 +20,7 @@ export interface attrsType {
*/ */
export function useRenderFlicker(attrs?: attrsType): Component { export function useRenderFlicker(attrs?: attrsType): Component {
return defineComponent({ return defineComponent({
name: "Flicker", name: "ReFlicker",
render() { render() {
return h( return h(
"div", "div",

View File

@@ -1,10 +1,7 @@
import { App } from "vue";
import reFlop from "./src/index.vue"; import reFlop from "./src/index.vue";
import { withInstall } from "@pureadmin/utils";
export const ReFlop = Object.assign(reFlop, { /** 时间翻牌组件 */
install(app: App) { export const ReFlop = withInstall(reFlop);
app.component(reFlop.name, reFlop);
}
});
export default ReFlop; export default ReFlop;

View File

@@ -15,7 +15,7 @@ const props = {
}; };
export default defineComponent({ export default defineComponent({
name: "Filpper", name: "ReFlop",
props, props,
setup(props) { setup(props) {
// eslint-disable-next-line vue/no-setup-props-destructure // eslint-disable-next-line vue/no-setup-props-destructure

View File

@@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, unref, nextTick, onUnmounted } from "vue"; import { ref, unref, nextTick, onUnmounted } from "vue";
import { templateRef } from "@vueuse/core"; import { templateRef } from "@vueuse/core";
import flippers from "./Filpper"; import flippers from "./filpper";
let timer = ref(null); let timer = ref(null);
let flipObjs = ref([]); let flipObjs = ref([]);

View File

@@ -1,22 +1,17 @@
import { App } from "vue";
import control from "./src/Control.vue"; import control from "./src/Control.vue";
import nodePanel from "./src/NodePanel.vue"; import nodePanel from "./src/NodePanel.vue";
import dataDialog from "./src/DataDialog.vue"; import dataDialog from "./src/DataDialog.vue";
import { withInstall } from "@pureadmin/utils";
export const Control = Object.assign(control, { /** LogicFlow流程图-控制面板 */
install(app: App) { const Control = withInstall(control);
app.component(control.name, control);
}
});
export const NodePanel = Object.assign(nodePanel, { /** LogicFlow流程图-拖拽面板 */
install(app: App) { const NodePanel = withInstall(nodePanel);
app.component(nodePanel.name, nodePanel);
}
});
export const DataDialog = Object.assign(dataDialog, { /** LogicFlow流程图-查看数据 */
install(app: App) { const DataDialog = withInstall(dataDialog);
app.component(dataDialog.name, dataDialog);
} export { Control, NodePanel, DataDialog };
});
// LogicFlow流程图文档http://logic-flow.org/

View File

@@ -102,6 +102,7 @@ onMounted(() => {
v-for="(item, key) in titleLists" v-for="(item, key) in titleLists"
:key="key" :key="key"
:title="item.text" :title="item.text"
class="dark:text-bg_color"
@mouseenter.prevent="onEnter(key)" @mouseenter.prevent="onEnter(key)"
@mouseleave.prevent="focusIndex = -1" @mouseleave.prevent="focusIndex = -1"
> >

View File

@@ -35,7 +35,7 @@ const nodeDragNode = item => {
<!-- 左侧bpmn元素选择器 --> <!-- 左侧bpmn元素选择器 -->
<div class="node-panel"> <div class="node-panel">
<div <div
class="node-item" class="node-item dark:text-bg_color"
v-for="item in props.nodeList" v-for="item in props.nodeList"
:key="item.text" :key="item.text"
@mousedown="nodeDragNode(item)" @mousedown="nodeDragNode(item)"

View File

@@ -1,16 +1,15 @@
import iconifyIconOffline from "./src/iconifyIconOffline"; import iconifyIconOffline from "./src/iconifyIconOffline";
import iconifyIconOnline from "./src/iconifyIconOnline"; import iconifyIconOnline from "./src/iconifyIconOnline";
import iconSelect from "./src/Select.vue";
import fontIcon from "./src/iconfont"; import fontIcon from "./src/iconfont";
import iconSelect from "./src/select.vue";
export const IconifyIconOffline = iconifyIconOffline; /** 离线图标组件 */
export const IconifyIconOnline = iconifyIconOnline; const IconifyIconOffline = iconifyIconOffline;
export const FontIcon = fontIcon; /** 在线图标组件 */
export const IconSelect = iconSelect; const IconifyIconOnline = iconifyIconOnline;
/** 图标选择器组件 */
const IconSelect = iconSelect;
/** iconfont组件 */
const FontIcon = fontIcon;
export default { export { IconifyIconOffline, IconifyIconOnline, IconSelect, FontIcon };
IconifyIconOffline,
IconifyIconOnline,
FontIcon,
IconSelect
};

View File

@@ -4,6 +4,10 @@ import { ref, computed, CSSProperties, toRef, watch } from "vue";
import { IconJson } from "/@/components/ReIcon/data"; import { IconJson } from "/@/components/ReIcon/data";
type ParameterCSSProperties = (item?: string) => CSSProperties | undefined; type ParameterCSSProperties = (item?: string) => CSSProperties | undefined;
defineOptions({
name: "IconSelect"
});
const props = defineProps({ const props = defineProps({
modelValue: { modelValue: {
require: false, require: false,
@@ -114,7 +118,7 @@ watch(
</script> </script>
<template> <template>
<div class="selector w-350px"> <div class="selector w-[350px]">
<el-input v-model="inputValue" disabled> <el-input v-model="inputValue" disabled>
<template #append> <template #append>
<el-popover <el-popover
@@ -128,7 +132,7 @@ watch(
> >
<template #reference> <template #reference>
<div <div
class="w-40px h-32px cursor-pointer flex justify-center items-center" class="w-[40px] h-[32px] cursor-pointer flex justify-center items-center"
@click="visible = !visible" @click="visible = !visible"
> >
<IconifyIconOnline :icon="currentActiveType + icon" /> <IconifyIconOnline :icon="currentActiveType + icon" />
@@ -157,7 +161,7 @@ watch(
v-for="(item, key) in pageList" v-for="(item, key) in pageList"
:key="key" :key="key"
:title="item" :title="item"
class="icon-item p-2 w-1/10 cursor-pointer mr-2 mt-1 flex justify-center items-center border border-solid" class="icon-item p-2 w-[1/10] cursor-pointer mr-2 mt-1 flex justify-center items-center border border-solid"
:style="iconItemStyle(item)" :style="iconItemStyle(item)"
@click="onChangeIcon(item)" @click="onChangeIcon(item)"
> >

View File

@@ -1,14 +1,14 @@
import { iconType } from "./types"; import { iconType } from "./types";
import { h, defineComponent, Component } from "vue"; import { h, defineComponent, Component } from "vue";
import { IconifyIconOffline, FontIcon } from "../index"; import { IconifyIconOnline, IconifyIconOffline, FontIcon } from "../index";
/** /**
* 支持fontawesome4、5+、iconfont、remixicon、element-plus的icons、自定义svg * 支持fontawesome4、5+、iconfont、remixicon、element-plus的icons、自定义svg
* @param icon 必传 string 图标 * @param icon 必传 图标
* @param attrs 可选 iconType 属性 * @param attrs 可选 iconType 属性
* @returns Component * @returns Component
*/ */
export function useRenderIcon(icon: string, attrs?: iconType): Component { export function useRenderIcon(icon: any, attrs?: iconType): Component {
// iconfont // iconfont
const ifReg = /^IF-/; const ifReg = /^IF-/;
// typeof icon === "function" 属于SVG // typeof icon === "function" 属于SVG
@@ -30,14 +30,16 @@ export function useRenderIcon(icon: string, attrs?: iconType): Component {
}); });
} }
}); });
} else if (typeof icon === "function") { } else if (typeof icon === "function" || typeof icon?.render === "function") {
// svg // svg
return icon; return icon;
} else { } else {
return defineComponent({ return defineComponent({
name: "Icon", name: "Icon",
render() { render() {
return h(IconifyIconOffline, { const IconifyIcon =
attrs && attrs["online"] ? IconifyIconOnline : IconifyIconOffline;
return h(IconifyIcon, {
icon: icon, icon: icon,
...attrs ...attrs
}); });

View File

@@ -2,7 +2,7 @@ import { h, defineComponent } from "vue";
// 封装iconfont组件默认`font-class`引用模式,支持`unicode`引用、`font-class`引用、`symbol`引用 https://www.iconfont.cn/help/detail?spm=a313x.7781069.1998910419.20&helptype=code // 封装iconfont组件默认`font-class`引用模式,支持`unicode`引用、`font-class`引用、`symbol`引用 https://www.iconfont.cn/help/detail?spm=a313x.7781069.1998910419.20&helptype=code
export default defineComponent({ export default defineComponent({
name: "fontIcon", name: "FontIcon",
props: { props: {
icon: { icon: {
type: String, type: String,

View File

@@ -87,6 +87,8 @@ import Ppt from "@iconify-icons/ri/file-ppt-2-line";
import TerminalWindowLine from "@iconify-icons/ri/terminal-window-line"; import TerminalWindowLine from "@iconify-icons/ri/terminal-window-line";
import User from "@iconify-icons/ri/user-3-fill"; import User from "@iconify-icons/ri/user-3-fill";
import Lock from "@iconify-icons/ri/lock-fill"; import Lock from "@iconify-icons/ri/lock-fill";
import MenuUnfold from "@iconify-icons/ri/menu-unfold-fill";
import MenuFold from "@iconify-icons/ri/menu-fold-fill";
addIcon("arrow-right-s-line", ArrowRightSLine); addIcon("arrow-right-s-line", ArrowRightSLine);
addIcon("arrow-left-s-line", ArrowLeftSLine); addIcon("arrow-left-s-line", ArrowLeftSLine);
addIcon("logout-circle-r-line", LogoutCircleRLine); addIcon("logout-circle-r-line", LogoutCircleRLine);
@@ -112,6 +114,8 @@ addIcon("ppt", Ppt);
addIcon("terminal-window-line", TerminalWindowLine); addIcon("terminal-window-line", TerminalWindowLine);
addIcon("user", User); addIcon("user", User);
addIcon("lock", Lock); addIcon("lock", Lock);
addIcon("menu-unfold", MenuUnfold);
addIcon("menu-fold", MenuFold);
// Font Awesome 4 // Font Awesome 4
import FaUser from "@iconify-icons/fa/user"; import FaUser from "@iconify-icons/fa/user";
@@ -147,7 +151,7 @@ addIcon("location-company", LocationCompany);
// Iconify Icon在Vue里离线使用用于内网环境https://docs.iconify.design/icon-components/vue/offline.html // Iconify Icon在Vue里离线使用用于内网环境https://docs.iconify.design/icon-components/vue/offline.html
export default defineComponent({ export default defineComponent({
name: "IconifyIcon", name: "IconifyIconOffline",
components: { IconifyIcon }, components: { IconifyIcon },
props: { props: {
icon: { icon: {
@@ -161,6 +165,9 @@ export default defineComponent({
IconifyIcon, IconifyIcon,
{ {
icon: `${this.icon}`, icon: `${this.icon}`,
style: attrs?.style
? Object.assign(attrs.style, { outline: "none" })
: { outline: "none" },
...attrs ...attrs
}, },
{ {

View File

@@ -1,9 +1,9 @@
import { h, defineComponent } from "vue"; import { h, defineComponent } from "vue";
import { Icon as IconifyIcon } from "@iconify/vue"; import { Icon as IconifyIcon } from "@iconify/vue";
// Iconify Icon在Vue里在线使用用于外网环境 https://docs.iconify.design/icon-components/vue/offline.html // Iconify Icon在Vue里在线使用用于外网环境
export default defineComponent({ export default defineComponent({
name: "IconifyIcon", name: "IconifyIconOnline",
components: { IconifyIcon }, components: { IconifyIcon },
props: { props: {
icon: { icon: {
@@ -17,6 +17,9 @@ export default defineComponent({
IconifyIcon, IconifyIcon,
{ {
icon: `${this.icon}`, icon: `${this.icon}`,
style: attrs?.style
? Object.assign(attrs.style, { outline: "none" })
: { outline: "none" },
...attrs ...attrs
}, },
{ {

View File

@@ -11,7 +11,9 @@ export interface iconType {
horizontalAlign?: boolean; horizontalAlign?: boolean;
verticalAlign?: boolean; verticalAlign?: boolean;
align?: string; align?: string;
online?: boolean;
onLoad?: Function; onLoad?: Function;
includes?: Function;
// all icon // all icon
style?: object; style?: object;

View File

@@ -1,12 +1,7 @@
import { App } from "vue";
import reImageVerify from "./src/index.vue"; import reImageVerify from "./src/index.vue";
import { withInstall } from "@pureadmin/utils";
export const ReImageVerify = Object.assign(reImageVerify, { /** 图形验证码组件 */
install(app: App) { export const ReImageVerify = withInstall(reImageVerify);
app.component(reImageVerify.name, reImageVerify);
}
});
export default { export default ReImageVerify;
ReImageVerify
};

View File

@@ -1,13 +1,11 @@
<script lang="ts">
export default {
name: "ReImageVerify"
};
</script>
<script setup lang="ts"> <script setup lang="ts">
import { watch } from "vue"; import { watch } from "vue";
import { useImageVerify } from "./hooks"; import { useImageVerify } from "./hooks";
defineOptions({
name: "ReImageVerify"
});
interface Props { interface Props {
code?: string; code?: string;
} }

View File

@@ -1,12 +1,7 @@
import { App } from "vue";
import amap from "./src/Amap.vue"; import amap from "./src/Amap.vue";
import { withInstall } from "@pureadmin/utils";
export const Amap = Object.assign(amap, { /** 高德地图组件 */
install(app: App) { export const Amap = withInstall(amap);
app.component(amap.name, amap);
}
});
export default { export default Amap;
Amap
};

View File

@@ -1,8 +1,8 @@
<script setup lang="ts"> <script setup lang="ts">
import AMapLoader from "@amap/amap-jsapi-loader";
import { reactive, getCurrentInstance, onBeforeMount, onUnmounted } from "vue"; import { reactive, getCurrentInstance, onBeforeMount, onUnmounted } from "vue";
import { deviceDetection } from "@pureadmin/utils";
import AMapLoader from "@amap/amap-jsapi-loader";
import { mapJson } from "/@/api/mock"; import { mapJson } from "/@/api/mock";
import { deviceDetection } from "/@/utils/deviceDetection";
import car from "/@/assets/car.png"; import car from "/@/assets/car.png";
export interface MapConfigureInter { export interface MapConfigureInter {
@@ -15,9 +15,9 @@ export interface MapConfigureInter {
plugin?: Fn; plugin?: Fn;
} }
type resultType = { defineOptions({
info: Array<undefined>; name: "Amap"
}; });
let MarkerCluster; let MarkerCluster;
let map: MapConfigureInter; let map: MapConfigureInter;
@@ -92,8 +92,8 @@ onBeforeMount(() => {
// 获取模拟车辆信息 // 获取模拟车辆信息
mapJson() mapJson()
.then((res: resultType) => { .then(({ info }) => {
let points: object = res.info.map((v: any) => { let points: object = info.map(v => {
return { return {
lnglat: [v.lng, v.lat], lnglat: [v.lng, v.lat],
...v ...v

View File

@@ -1,10 +1,7 @@
import { App } from "vue";
import reQrcode from "./src/index"; import reQrcode from "./src/index";
import { withInstall } from "@pureadmin/utils";
export const ReQrcode = Object.assign(reQrcode, { /** 二维码组件 */
install(app: App) { export const ReQrcode = withInstall(reQrcode);
app.component(reQrcode.name, reQrcode);
}
});
export default ReQrcode; export default ReQrcode;

View File

@@ -8,8 +8,8 @@ import {
defineComponent defineComponent
} from "vue"; } from "vue";
import "./index.scss"; import "./index.scss";
import { isString } from "/@/utils/is";
import { cloneDeep } from "lodash-unified"; import { cloneDeep } from "lodash-unified";
import { isString } from "@pureadmin/utils";
import { propTypes } from "/@/utils/propTypes"; import { propTypes } from "/@/utils/propTypes";
import { IconifyIconOffline } from "../../ReIcon"; import { IconifyIconOffline } from "../../ReIcon";
import QRCode, { QRCodeRenderersOptions } from "qrcode"; import QRCode, { QRCodeRenderersOptions } from "qrcode";
@@ -78,7 +78,7 @@ export default defineComponent({
const _width: number = await getOriginWidth(unref(renderText), options); const _width: number = await getOriginWidth(unref(renderText), options);
options.scale = options.scale =
props.width === 0 ? undefined : (props.width / _width) * 4; props.width === 0 ? undefined : (props.width / _width) * 4;
const canvasRef: HTMLCanvasElement = await toCanvas( const canvasRef: any = await toCanvas(
unref(wrapRef) as HTMLCanvasElement, unref(wrapRef) as HTMLCanvasElement,
unref(renderText), unref(renderText),
options options
@@ -246,7 +246,7 @@ export default defineComponent({
> >
<div class="absolute top-[50%] left-[50%] font-bold"> <div class="absolute top-[50%] left-[50%] font-bold">
<IconifyIconOffline <IconifyIconOffline
class="cursor-pointer outline-none" class="cursor-pointer"
icon="refresh-right" icon="refresh-right"
width="30" width="30"
color="var(--el-color-primary)" color="var(--el-color-primary)"

View File

@@ -1,10 +1,7 @@
import { App } from "vue";
import reSeamlessScroll from "./src/index.vue"; import reSeamlessScroll from "./src/index.vue";
import { withInstall } from "@pureadmin/utils";
export const ReSeamlessScroll = Object.assign(reSeamlessScroll, { /** 无缝滚动组件 */
install(app: App) { export const ReSeamlessScroll = withInstall(reSeamlessScroll);
app.component(reSeamlessScroll.name, reSeamlessScroll);
}
});
export default ReSeamlessScroll; export default ReSeamlessScroll;

View File

@@ -11,6 +11,10 @@ import * as utilsMethods from "./utils";
const { animationFrame, copyObj } = utilsMethods; const { animationFrame, copyObj } = utilsMethods;
animationFrame(); animationFrame();
defineOptions({
name: "ReSeamlessScroll"
});
const props = defineProps({ const props = defineProps({
data: { data: {
type: Array as PropType<unknown> type: Array as PropType<unknown>
@@ -102,7 +106,7 @@ let defaultOption = computed(() => {
}); });
let options = computed(() => { let options = computed(() => {
// @ts-ignore // @ts-expect-error
return copyObj({}, unref(defaultOption), classOption); return copyObj({}, unref(defaultOption), classOption);
}); });
@@ -162,8 +166,8 @@ let autoPlay = computed(() => {
}); });
let scrollSwitch = computed(() => { let scrollSwitch = computed(() => {
// 从 props 解构出来的 属性 不再具有应性. // 从 props 解构出来的 属性 不再具有应性.
return props.data.length >= unref(options).limitMoveNum; return (props.data as any).length >= unref(options).limitMoveNum;
}); });
let hoverStopSwitch = computed(() => { let hoverStopSwitch = computed(() => {
@@ -424,7 +428,7 @@ function scrollInitMove() {
if (timer) clearTimeout(timer); if (timer) clearTimeout(timer);
copyHtml.value = unref(slotList).innerHTML; copyHtml.value = unref(slotList).innerHTML;
setTimeout(() => { setTimeout(() => {
realBoxHeight.value = unref(realBox).offsetHeight; realBoxHeight.value = unref(realBox)?.offsetHeight;
scrollMove(); scrollMove();
}, 0); }, 0);
} else { } else {
@@ -450,7 +454,6 @@ function scrollStopMove() {
// 鼠标滚轮事件 // 鼠标滚轮事件
function wheel(e) { function wheel(e) {
e.preventDefault();
if ( if (
unref(options).direction === "left" || unref(options).direction === "left" ||
unref(options).direction === "right" unref(options).direction === "right"
@@ -513,10 +516,10 @@ defineExpose({
:style="pos" :style="pos"
@mouseenter="enter" @mouseenter="enter"
@mouseleave="leave" @mouseleave="leave"
@touchstart="touchStart" @touchstart.passive="touchStart"
@touchmove="touchMove" @touchmove.passive="touchMove"
@touchend="touchEnd" @touchend="touchEnd"
@mousewheel="wheel" @mousewheel.passive="wheel"
> >
<div :ref="'slotList' + classOption['key']" :style="float"> <div :ref="'slotList' + classOption['key']" :style="float">
<slot /> <slot />

View File

@@ -1,10 +1,7 @@
import { App } from "vue";
import reSelector from "./src"; import reSelector from "./src";
import { withInstall } from "@pureadmin/utils";
export const ReSelector = Object.assign(reSelector, { /** 选择器组件 */
install(app: App) { export const ReSelector = withInstall(reSelector);
app.component(reSelector.name, reSelector);
}
});
export default ReSelector; export default ReSelector;

View File

@@ -6,7 +6,7 @@ import {
getCurrentInstance, getCurrentInstance,
unref unref
} from "vue"; } from "vue";
import { addClass, removeClass, toggleClass } from "/@/utils/operate"; import { addClass, removeClass, toggleClass } from "@pureadmin/utils";
import "./index.css"; import "./index.css";
const stayClass = "stay"; //鼠标点击 const stayClass = "stay"; //鼠标点击
@@ -50,7 +50,7 @@ const props = {
}; };
export default defineComponent({ export default defineComponent({
name: "Selector", name: "ReSelector",
props, props,
emits: ["selectedVal"], emits: ["selectedVal"],
setup(props, { emit }) { setup(props, { emit }) {
@@ -254,23 +254,32 @@ export default defineComponent({
}); });
addClass( addClass(
instance.refs["hsdiv" + props.HsKey + item[0]], instance.refs["hsdiv" + props.HsKey + item[0]] as Element,
activeClass, activeClass,
stayClass stayClass
); );
addClass(instance.refs["hstd" + props.HsKey + item[0]], bothLeftSides);
addClass( addClass(
instance.refs["hsdiv" + props.HsKey + item[1]], instance.refs["hstd" + props.HsKey + item[0]] as Element,
bothLeftSides
);
addClass(
instance.refs["hsdiv" + props.HsKey + item[1]] as Element,
activeClass, activeClass,
stayClass stayClass
); );
addClass(instance.refs["hstd" + props.HsKey + item[1]], bothRightSides); addClass(
instance.refs["hstd" + props.HsKey + item[1]] as Element,
bothRightSides
);
while (item[1] >= item[0]) { while (item[1] >= item[0]) {
addClass(instance.refs["hstd" + props.HsKey + item[0]], inRange); addClass(
instance.refs["hstd" + props.HsKey + item[0]] as Element,
inRange
);
item[0]++; item[0]++;
} }
}; };

View File

@@ -8,8 +8,9 @@ export interface ContextProps {
split: string; split: string;
} }
/** 切割面板组件 */
export default defineComponent({ export default defineComponent({
name: "splitPane", name: "SplitPane",
components: { resizer }, components: { resizer },
props: { props: {
splitSet: { splitSet: {

View File

@@ -2,7 +2,7 @@ import { computed, unref, defineComponent } from "vue";
import "./resizer.css"; import "./resizer.css";
export default defineComponent({ export default defineComponent({
name: "resizer", name: "Resizer",
props: { props: {
split: { split: {
type: String, type: String,

View File

@@ -1,16 +0,0 @@
## 一款基于`element-plus`表格封装的`table-crud`组件库,采用`tsx`语法编写,通过一些灵活的配置项即可实现增删改查
### wait todo
- 搜索组件
- 表格组件
- 弹框组件
- form 组件
### completed
- 操作栏组件
目前只完成了操作栏组件,后续有时间慢慢完善对应组件,因为作者比较忙,暂无具体计划完成时间,忘谅解,当然非常欢迎 pr等这些组件全部完成后我会单独抽离成`npm`包的形式发布。
注意:该组件库为了快速成型,内部依赖了`unocss`

View File

@@ -1,8 +1,5 @@
import { App } from "vue"; import tableProBar from "./src/bar";
import epTableProBar from "./src/bar"; import { withInstall } from "@pureadmin/utils";
export const EpTableProBar = Object.assign(epTableProBar, { /** table-crud组件 */
install(app: App) { export const TableProBar = withInstall(tableProBar);
app.component(epTableProBar.name, epTableProBar);
}
});

View File

@@ -1,7 +1,6 @@
import { emitter } from "/@/utils/mitt";
import { IconifyIconOffline } from "../../ReIcon";
import { defineComponent, ref, computed, PropType } from "vue"; import { defineComponent, ref, computed, PropType } from "vue";
import { useEpThemeStoreHook } from "/@/store/modules/epTheme"; import { useEpThemeStoreHook } from "/@/store/modules/epTheme";
import { IconifyIconOffline } from "../../ReIcon";
export const loadingSvg = ` export const loadingSvg = `
<path class="path" d=" <path class="path" d="
@@ -44,13 +43,12 @@ const props = {
}; };
export default defineComponent({ export default defineComponent({
name: "epTableProBar", name: "TableProBar",
props, props,
emits: ["refresh"], emits: ["refresh"],
setup(props, { emit, slots, attrs }) { setup(props, { emit, slots, attrs }) {
const buttonRef = ref(); const buttonRef = ref();
const checkList = ref([]); const checkList = ref([]);
const currentWidth = ref(0);
const size = ref("default"); const size = ref("default");
const isExpandAll = ref(true); const isExpandAll = ref(true);
@@ -59,7 +57,7 @@ export default defineComponent({
return { return {
background: background:
s === size.value ? useEpThemeStoreHook().epThemeColor : "", s === size.value ? useEpThemeStoreHook().epThemeColor : "",
color: s === size.value ? "#f4f4f5" : "#000" color: s === size.value ? "#fff" : "var(--el-text-color-primary)"
}; };
}; };
}); });
@@ -78,12 +76,6 @@ export default defineComponent({
}); });
} }
// 监听容器
emitter.on("resize", ({ detail }) => {
const { width } = detail;
currentWidth.value = width;
});
const dropdown = { const dropdown = {
dropdown: () => ( dropdown: () => (
<el-dropdown-menu class="translation"> <el-dropdown-menu class="translation">
@@ -112,10 +104,10 @@ export default defineComponent({
const reference = { const reference = {
reference: () => ( reference: () => (
<IconifyIconOffline <IconifyIconOffline
class="cursor-pointer outline-none" class="cursor-pointer"
icon="setting" icon="setting"
width="16" width="16"
color="#606266" color="text_color_regular"
onMouseover={e => (buttonRef.value = e.currentTarget)} onMouseover={e => (buttonRef.value = e.currentTarget)}
/> />
) )
@@ -125,15 +117,13 @@ export default defineComponent({
<> <>
<div <div
{...attrs} {...attrs}
class="w-99/100 mt-6 p-2 bg-white" class="w-[99/100] mt-6 p-2 bg-white dark:bg-dark"
v-loading={props.loading} v-loading={props.loading}
element-loading-svg={loadingSvg} element-loading-svg={loadingSvg}
element-loading-svg-view-box="-10, -10, 50, 50" element-loading-svg-view-box="-10, -10, 50, 50"
> >
<div class="flex justify-between w-full h-60px p-4"> <div class="flex justify-between w-full h-[60px] p-4">
<p class="font-bold"> <p class="font-bold truncate">{props.title}</p>
{currentWidth.value > 390 ? props.title : "列表"}
</p>
<div class="flex items-center justify-around"> <div class="flex items-center justify-around">
<div class="flex mr-4">{slots?.buttons()}</div> <div class="flex mr-4">{slots?.buttons()}</div>
{props.tableRef?.size ? ( {props.tableRef?.size ? (
@@ -144,10 +134,10 @@ export default defineComponent({
placement="top" placement="top"
> >
<IconifyIconOffline <IconifyIconOffline
class="cursor-pointer outline-none" class="cursor-pointer"
icon={isExpandAll.value ? "unExpand" : "expand"} icon={isExpandAll.value ? "unExpand" : "expand"}
width="16" width="16"
color="#606266" color="text_color_regular"
onClick={() => onExpand()} onClick={() => onExpand()}
/> />
</el-tooltip> </el-tooltip>
@@ -156,10 +146,10 @@ export default defineComponent({
) : undefined} ) : undefined}
<el-tooltip effect="dark" content="刷新" placement="top"> <el-tooltip effect="dark" content="刷新" placement="top">
<IconifyIconOffline <IconifyIconOffline
class="cursor-pointer outline-none" class="cursor-pointer"
icon="refresh-right" icon="refresh-right"
width="16" width="16"
color="#606266" color="text_color_regular"
onClick={() => emit("refresh")} onClick={() => emit("refresh")}
/> />
</el-tooltip> </el-tooltip>
@@ -168,10 +158,10 @@ export default defineComponent({
<el-tooltip effect="dark" content="密度" placement="top"> <el-tooltip effect="dark" content="密度" placement="top">
<el-dropdown v-slots={dropdown} trigger="click"> <el-dropdown v-slots={dropdown} trigger="click">
<IconifyIconOffline <IconifyIconOffline
class="cursor-pointer outline-none" class="cursor-pointer"
icon="density" icon="density"
width="16" width="16"
color="#606266" color="text_color_regular"
/> />
</el-dropdown> </el-dropdown>
</el-tooltip> </el-tooltip>

View File

@@ -1,5 +1,5 @@
// 参考https://www.npmjs.com/package/element-tree-line (主要是替换需要通过函数传参的方式去注册组件并添加更好的类型支持并移除this.$scopedSlots在3.x中,将所有this.$scopedSlots替换为this.$slots) // 参考https://www.npmjs.com/package/element-tree-line (主要是替换需要通过函数传参的方式去注册组件并添加更好的类型支持并移除this.$scopedSlots在3.x中,将所有this.$scopedSlots替换为this.$slots)
import { isFunction } from "/@/utils/is"; import { isFunction } from "@pureadmin/utils";
import { h, defineComponent } from "vue"; import { h, defineComponent } from "vue";
import type { PropType } from "vue"; import type { PropType } from "vue";
import "./index.scss"; import "./index.scss";
@@ -9,8 +9,9 @@ import type {
TreeNodeData TreeNodeData
} from "element-plus/es/components/tree-v2/src/types"; } from "element-plus/es/components/tree-v2/src/types";
/** 树形连接线组件 */
export default defineComponent({ export default defineComponent({
name: "el-tree-line", name: "ReTreeLine",
props: { props: {
node: { node: {
type: Object as PropType<TreeNode>, type: Object as PropType<TreeNode>,

View File

@@ -1,7 +1,7 @@
import { Directive } from "vue"; import { Directive, type DirectiveBinding, type VNode } from "vue";
import type { DirectiveBinding, VNode } from "vue";
import elementResizeDetectorMaker from "element-resize-detector"; import elementResizeDetectorMaker from "element-resize-detector";
import type { Erd } from "element-resize-detector"; import type { Erd } from "element-resize-detector";
import { optimizeFps } from "@pureadmin/utils";
import { emitter } from "/@/utils/mitt"; import { emitter } from "/@/utils/mitt";
const erd: Erd = elementResizeDetectorMaker({ const erd: Erd = elementResizeDetectorMaker({
@@ -14,7 +14,9 @@ export const resize: Directive = {
const width = elem.offsetWidth; const width = elem.offsetWidth;
const height = elem.offsetHeight; const height = elem.offsetHeight;
if (binding?.instance) { if (binding?.instance) {
emitter.emit("resize", { detail: { width, height } }); optimizeFps(() => {
emitter.emit("resize", { detail: { width, height } });
})();
} else { } else {
vnode.el.dispatchEvent( vnode.el.dispatchEvent(
new CustomEvent("resize", { detail: { width, height } }) new CustomEvent("resize", { detail: { width, height } })

View File

@@ -1,23 +1,18 @@
<script setup lang="ts"> <script setup lang="ts">
import { import { useGlobal } from "@pureadmin/utils";
h,
ref,
computed,
Transition,
defineComponent,
getCurrentInstance
} from "vue";
import backTop from "/@/assets/svg/back_top.svg?component"; import backTop from "/@/assets/svg/back_top.svg?component";
import { h, computed, Transition, defineComponent } from "vue";
import { usePermissionStoreHook } from "/@/store/modules/permission"; import { usePermissionStoreHook } from "/@/store/modules/permission";
const props = defineProps({ const props = defineProps({
fixedHeader: Boolean fixedHeader: Boolean
}); });
const keepAlive: Boolean = ref(
getCurrentInstance().appContext.config.globalProperties.$config?.KeepAlive const { $storage, $config } = useGlobal<GlobalPropertiesApi>();
);
const instance = const keepAlive = computed(() => {
getCurrentInstance().appContext.app.config.globalProperties.$storage; return $config?.KeepAlive;
});
const transitions = computed(() => { const transitions = computed(() => {
return route => { return route => {
@@ -26,11 +21,11 @@ const transitions = computed(() => {
}); });
const hideTabs = computed(() => { const hideTabs = computed(() => {
return instance?.configure.hideTabs; return $storage?.configure.hideTabs;
}); });
const layout = computed(() => { const layout = computed(() => {
return instance?.layout.layout === "vertical"; return $storage?.layout.layout === "vertical";
}); });
const getSectionStyle = computed(() => { const getSectionStyle = computed(() => {

View File

@@ -1,69 +1,51 @@
<script setup lang="ts"> <script setup lang="ts">
import { useI18n } from "vue-i18n";
import { useNav } from "../hooks/nav";
import { useRoute } from "vue-router";
import Search from "./search/index.vue"; import Search from "./search/index.vue";
import Notice from "./notice/index.vue"; import Notice from "./notice/index.vue";
import mixNav from "./sidebar/mixNav.vue"; import mixNav from "./sidebar/mixNav.vue";
import avatars from "/@/assets/avatars.jpg"; import avatars from "/@/assets/avatars.jpg";
import Hamburger from "./sidebar/hamBurger.vue"; import { useNav } from "/@/layout/hooks/useNav";
import { watch, getCurrentInstance } from "vue";
import Breadcrumb from "./sidebar/breadCrumb.vue"; import Breadcrumb from "./sidebar/breadCrumb.vue";
import { deviceDetection } from "/@/utils/deviceDetection"; import { deviceDetection } from "@pureadmin/utils";
import topCollapse from "./sidebar/topCollapse.vue";
import screenfull from "../components/screenfull/index.vue"; import screenfull from "../components/screenfull/index.vue";
import { useTranslationLang } from "../hooks/useTranslationLang";
import globalization from "/@/assets/svg/globalization.svg?component"; import globalization from "/@/assets/svg/globalization.svg?component";
const route = useRoute();
const { locale, t } = useI18n();
const instance =
getCurrentInstance().appContext.config.globalProperties.$storage;
const { const {
layout,
device,
logout, logout,
onPanel, onPanel,
changeTitle,
toggleSideBar,
pureApp, pureApp,
username, username,
avatarsStyle, avatarsStyle,
toggleSideBar,
getDropdownItemStyle, getDropdownItemStyle,
changeWangeditorLanguage getDropdownItemClass
} = useNav(); } = useNav();
watch( const { t, locale, translationCh, translationEn } = useTranslationLang();
() => locale.value,
() => {
changeTitle(route.meta);
locale.value === "en"
? changeWangeditorLanguage(locale.value)
: changeWangeditorLanguage("zh-CN");
}
);
function translationCh() {
instance.locale = { locale: "zh" };
locale.value = "zh";
}
function translationEn() {
instance.locale = { locale: "en" };
locale.value = "en";
}
</script> </script>
<template> <template>
<div class="navbar"> <div
<Hamburger class="navbar bg-[#fff] shadow-sm shadow-[rgba(0, 21, 41, 0.08)] dark:shadow-[#0d0d0d]"
v-if="pureApp.layout !== 'mix'" >
:is-active="pureApp.sidebar.opened" <topCollapse
v-if="device === 'mobile'"
class="hamburger-container" class="hamburger-container"
:is-active="pureApp.sidebar.opened"
@toggleClick="toggleSideBar" @toggleClick="toggleSideBar"
/> />
<Breadcrumb v-if="pureApp.layout !== 'mix'" class="breadcrumb-container" /> <Breadcrumb
v-if="layout !== 'mix' && device !== 'mobile'"
class="breadcrumb-container"
/>
<mixNav v-if="pureApp.layout === 'mix'" /> <mixNav v-if="layout === 'mix'" />
<div v-if="pureApp.layout === 'vertical'" class="vertical-header-right"> <div v-if="layout === 'vertical'" class="vertical-header-right">
<!-- 菜单搜索 --> <!-- 菜单搜索 -->
<Search /> <Search />
<!-- 通知 --> <!-- 通知 -->
@@ -72,34 +54,41 @@ function translationEn() {
<screenfull id="header-screenfull" v-show="!deviceDetection()" /> <screenfull id="header-screenfull" v-show="!deviceDetection()" />
<!-- 国际化 --> <!-- 国际化 -->
<el-dropdown id="header-translation" trigger="click"> <el-dropdown id="header-translation" trigger="click">
<globalization /> <globalization
class="navbar-bg-hover w-[40px] h-[48px] p-[11px] cursor-pointer outline-none"
/>
<template #dropdown> <template #dropdown>
<el-dropdown-menu class="translation"> <el-dropdown-menu class="translation">
<el-dropdown-item <el-dropdown-item
:style="getDropdownItemStyle(locale, 'zh')" :style="getDropdownItemStyle(locale, 'zh')"
:class="['dark:!text-white', getDropdownItemClass(locale, 'zh')]"
@click="translationCh" @click="translationCh"
><IconifyIconOffline >
<IconifyIconOffline
class="check-zh" class="check-zh"
v-show="locale === 'zh'" v-show="locale === 'zh'"
icon="check" icon="check"
/>简体中文</el-dropdown-item />
> 简体中文
</el-dropdown-item>
<el-dropdown-item <el-dropdown-item
:style="getDropdownItemStyle(locale, 'en')" :style="getDropdownItemStyle(locale, 'en')"
:class="['dark:!text-white', getDropdownItemClass(locale, 'en')]"
@click="translationEn" @click="translationEn"
> >
<span class="check-en" v-show="locale === 'en'"> <span class="check-en" v-show="locale === 'en'">
<IconifyIconOffline icon="check" /> </span <IconifyIconOffline icon="check" />
>English </span>
English
</el-dropdown-item> </el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
</template> </template>
</el-dropdown> </el-dropdown>
<!-- 退出登 --> <!-- 退出登 -->
<el-dropdown trigger="click"> <el-dropdown trigger="click">
<span class="el-dropdown-link"> <span class="el-dropdown-link navbar-bg-hover">
<img v-if="avatars" :src="avatars" :style="avatarsStyle" /> <img v-if="avatars" :src="avatars" :style="avatarsStyle" />
<p v-if="username">{{ username }}</p> <p v-if="username" class="dark:text-white">{{ username }}</p>
</span> </span>
<template #dropdown> <template #dropdown>
<el-dropdown-menu class="logout"> <el-dropdown-menu class="logout">
@@ -107,13 +96,14 @@ function translationEn() {
<IconifyIconOffline <IconifyIconOffline
icon="logout-circle-r-line" icon="logout-circle-r-line"
style="margin: 5px" style="margin: 5px"
/>{{ t("buttons.hsLoginOut") }}</el-dropdown-item />
> {{ t("buttons.hsLoginOut") }}
</el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
</template> </template>
</el-dropdown> </el-dropdown>
<span <span
class="el-icon-setting" class="el-icon-setting navbar-bg-hover"
:title="t('buttons.hssystemSet')" :title="t('buttons.hssystemSet')"
@click="onPanel" @click="onPanel"
> >
@@ -128,16 +118,12 @@ function translationEn() {
width: 100%; width: 100%;
height: 48px; height: 48px;
overflow: hidden; overflow: hidden;
background: #fff;
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
.hamburger-container { .hamburger-container {
line-height: 48px; line-height: 48px;
height: 100%; height: 100%;
float: left; float: left;
cursor: pointer; cursor: pointer;
transition: background 0.3s;
-webkit-tap-highlight-color: transparent;
} }
.vertical-header-right { .vertical-header-right {
@@ -148,31 +134,6 @@ function translationEn() {
color: #000000d9; color: #000000d9;
justify-content: flex-end; justify-content: flex-end;
:deep(.dropdown-badge) {
&:hover {
background: #f6f6f6;
}
}
.screen-full {
cursor: pointer;
&:hover {
background: #f6f6f6;
}
}
.globalization {
height: 48px;
width: 40px;
padding: 11px;
cursor: pointer;
&:hover {
background: #f6f6f6;
}
}
.el-dropdown-link { .el-dropdown-link {
height: 48px; height: 48px;
padding: 10px; padding: 10px;
@@ -182,10 +143,6 @@ function translationEn() {
cursor: pointer; cursor: pointer;
color: #000000d9; color: #000000d9;
&:hover {
background: #f6f6f6;
}
p { p {
font-size: 14px; font-size: 14px;
} }
@@ -204,15 +161,12 @@ function translationEn() {
display: flex; display: flex;
cursor: pointer; cursor: pointer;
align-items: center; align-items: center;
&:hover {
background: #f6f6f6;
}
} }
} }
.breadcrumb-container { .breadcrumb-container {
float: left; float: left;
margin-left: 16px;
} }
} }

View File

@@ -15,14 +15,13 @@ notices.value.forEach(notice => {
}); });
function tabClick() { function tabClick() {
// @ts-expect-error (dropdownDom as any).value.handleOpen();
dropdownDom.value.handleOpen();
} }
</script> </script>
<template> <template>
<el-dropdown ref="dropdownDom" trigger="click" placement="bottom-end"> <el-dropdown ref="dropdownDom" trigger="click" placement="bottom-end">
<span class="dropdown-badge"> <span class="dropdown-badge navbar-bg-hover select-none">
<el-badge :value="noticesNum" :max="99"> <el-badge :value="noticesNum" :max="99">
<span class="header-notice-icon"> <span class="header-notice-icon">
<IconifyIconOffline icon="bell" /> <IconifyIconOffline icon="bell" />

View File

@@ -44,7 +44,9 @@ function hoverDescription(event, description) {
</script> </script>
<template> <template>
<div class="notice-container"> <div
class="notice-container border-b-[1px] border-solid border-[#f0f0f0] dark:border-[#303030]"
>
<el-avatar <el-avatar
v-if="props.noticeItem.avatar" v-if="props.noticeItem.avatar"
:size="30" :size="30"
@@ -52,7 +54,7 @@ function hoverDescription(event, description) {
class="notice-container-avatar" class="notice-container-avatar"
/> />
<div class="notice-container-text"> <div class="notice-container-text">
<div class="notice-text-title"> <div class="notice-text-title text-[#000000d9] dark:text-white">
<el-tooltip <el-tooltip
popper-class="notice-title-popper" popper-class="notice-title-popper"
:disabled="!titleTooltip" :disabled="!titleTooltip"
@@ -72,7 +74,8 @@ function hoverDescription(event, description) {
:type="props.noticeItem?.status" :type="props.noticeItem?.status"
size="small" size="small"
class="notice-title-extra" class="notice-title-extra"
>{{ props.noticeItem?.extra }} >
{{ props.noticeItem?.extra }}
</el-tag> </el-tag>
</div> </div>
@@ -90,7 +93,7 @@ function hoverDescription(event, description) {
{{ props.noticeItem.description }} {{ props.noticeItem.description }}
</div> </div>
</el-tooltip> </el-tooltip>
<div class="notice-text-datetime"> <div class="notice-text-datetime text-[#00000073] dark:text-white">
{{ props.noticeItem.datetime }} {{ props.noticeItem.datetime }}
</div> </div>
</div> </div>
@@ -108,7 +111,7 @@ function hoverDescription(event, description) {
align-items: flex-start; align-items: flex-start;
justify-content: space-between; justify-content: space-between;
padding: 12px 0; padding: 12px 0;
border-bottom: 1px solid #f0f0f0; // border-bottom: 1px solid #f0f0f0;
.notice-container-avatar { .notice-container-avatar {
margin-right: 16px; margin-right: 16px;
@@ -127,7 +130,6 @@ function hoverDescription(event, description) {
font-weight: 400; font-weight: 400;
font-size: 14px; font-size: 14px;
line-height: 1.5715; line-height: 1.5715;
color: rgba(0, 0, 0, 0.85);
cursor: pointer; cursor: pointer;
.notice-title-content { .notice-title-content {
@@ -149,7 +151,6 @@ function hoverDescription(event, description) {
.notice-text-datetime { .notice-text-datetime {
font-size: 12px; font-size: 12px;
line-height: 1.5715; line-height: 1.5715;
color: rgba(0, 0, 0, 0.45);
} }
.notice-text-description { .notice-text-description {

View File

@@ -5,7 +5,7 @@ import { emitter } from "/@/utils/mitt";
let show = ref<Boolean>(false); let show = ref<Boolean>(false);
const target = ref(null); const target = ref(null);
onClickOutside(target, event => { onClickOutside(target, (event: any) => {
if (event.clientX > target.value.offsetLeft) return; if (event.clientX > target.value.offsetLeft) return;
show.value = false; show.value = false;
}); });
@@ -18,15 +18,17 @@ emitter.on("openPanel", () => {
<template> <template>
<div :class="{ show: show }" class="right-panel-container"> <div :class="{ show: show }" class="right-panel-container">
<div class="right-panel-background" /> <div class="right-panel-background" />
<div ref="target" class="right-panel"> <div ref="target" class="right-panel bg-white dark:bg-dark">
<div class="right-panel-items"> <div class="right-panel-items">
<div class="project-configuration"> <div class="project-configuration">
<h3>项目配置</h3> <h3 class="dark:text-white">项目配置</h3>
<el-icon title="关闭配置" class="el-icon-close" @click="show = !show"> <el-icon title="关闭配置" class="el-icon-close" @click="show = !show">
<IconifyIconOffline icon="close" /> <IconifyIconOffline icon="close" />
</el-icon> </el-icon>
</div> </div>
<div style="border-bottom: 1px solid #dcdfe6" /> <div
class="border-b-[1px] border-solid border-[#dcdfe6] dark:border-[#303030]"
/>
<slot /> <slot />
</div> </div>
</div> </div>
@@ -62,7 +64,7 @@ emitter.on("openPanel", () => {
box-shadow: 0 0 15px 0 rgba(0, 0, 0, 0.05); box-shadow: 0 0 15px 0 rgba(0, 0, 0, 0.05);
transition: all 0.25s cubic-bezier(0.7, 0.3, 0.1, 1); transition: all 0.25s cubic-bezier(0.7, 0.3, 0.1, 1);
transform: translate(100%); transform: translate(100%);
background: #fff; // background: #fff;
z-index: 40000; z-index: 40000;
} }

View File

@@ -1,13 +1,16 @@
<script setup lang="ts"> <script setup lang="ts">
import { useFullscreen } from "@vueuse/core";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import { useFullscreen } from "@vueuse/core";
const { t } = useI18n(); const { t } = useI18n();
const { isFullscreen, toggle } = useFullscreen(); const { isFullscreen, toggle } = useFullscreen();
</script> </script>
<template> <template>
<div class="screen-full" @click="toggle"> <div
class="screen-full w-[36px] h-[48px] flex-ac cursor-pointer navbar-bg-hover"
@click="toggle"
>
<FontIcon <FontIcon
:title=" :title="
isFullscreen ? t('buttons.hsexitfullscreen') : t('buttons.hsfullscreen') isFullscreen ? t('buttons.hsexitfullscreen') : t('buttons.hsfullscreen')
@@ -16,13 +19,3 @@ const { isFullscreen, toggle } = useFullscreen();
/> />
</div> </div>
</template> </template>
<style lang="scss" scoped>
.screen-full {
width: 36px;
height: 48px;
display: flex;
align-items: center;
justify-content: space-around;
}
</style>

View File

@@ -1,5 +1,5 @@
<template> <template>
<div class="search-footer"> <div class="search-footer text-[#333] dark:text-white">
<span class="search-footer-item"> <span class="search-footer-item">
<enterOutlined class="icon" /> <enterOutlined class="icon" />
确认 确认
@@ -23,7 +23,6 @@ import mdiKeyboardEsc from "/@/assets/svg/mdi_keyboard_esc.svg?component";
<style lang="scss" scoped> <style lang="scss" scoped>
.search-footer { .search-footer {
display: flex; display: flex;
color: #333;
.search-footer-item { .search-footer-item {
display: flex; display: flex;

View File

@@ -2,8 +2,9 @@
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
import SearchResult from "./SearchResult.vue"; import SearchResult from "./SearchResult.vue";
import SearchFooter from "./SearchFooter.vue"; import SearchFooter from "./SearchFooter.vue";
import { deleteChildren } from "/@/utils/tree"; import { useNav } from "/@/layout/hooks/useNav";
import { transformI18n } from "/@/plugins/i18n"; import { transformI18n } from "/@/plugins/i18n";
import { deleteChildren } from "@pureadmin/utils";
import { useDebounceFn, onKeyStroke } from "@vueuse/core"; import { useDebounceFn, onKeyStroke } from "@vueuse/core";
import { ref, watch, computed, nextTick, shallowRef } from "vue"; import { ref, watch, computed, nextTick, shallowRef } from "vue";
import { usePermissionStoreHook } from "/@/store/modules/permission"; import { usePermissionStoreHook } from "/@/store/modules/permission";
@@ -17,6 +18,7 @@ interface Emits {
(e: "update:value", val: boolean): void; (e: "update:value", val: boolean): void;
} }
const { device } = useNav();
const emit = defineEmits<Emits>(); const emit = defineEmits<Emits>();
const props = withDefaults(defineProps<Props>(), {}); const props = withDefaults(defineProps<Props>(), {});
const router = useRouter(); const router = useRouter();
@@ -130,7 +132,12 @@ onKeyStroke("ArrowDown", handleDown);
</script> </script>
<template> <template>
<el-dialog top="5vh" v-model="show" :before-close="handleClose"> <el-dialog
top="5vh"
:width="device === 'mobile' ? '80vw' : '50vw'"
v-model="show"
:before-close="handleClose"
>
<el-input <el-input
ref="inputRef" ref="inputRef"
v-model="keyword" v-model="keyword"

View File

@@ -1,24 +1,3 @@
<template>
<div class="result">
<template v-for="item in options" :key="item.path">
<div
class="result-item"
:style="{
background:
item?.path === active ? useEpThemeStoreHook().epThemeColor : '',
color: item.path === active ? '#fff' : ''
}"
@click="handleTo"
@mouseenter="handleMouse(item)"
>
<component :is="useRenderIcon(item.meta?.icon ?? 'bookmark-2-line')" />
<span class="result-item-title">{{ t(item.meta?.title) }}</span>
<enterOutlined />
</div>
</template>
</div>
</template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed } from "vue"; import { computed } from "vue";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
@@ -49,6 +28,17 @@ interface Emits {
const props = withDefaults(defineProps<Props>(), {}); const props = withDefaults(defineProps<Props>(), {});
const emit = defineEmits<Emits>(); const emit = defineEmits<Emits>();
const itemStyle = computed(() => {
return item => {
return {
background:
item?.path === active.value ? useEpThemeStoreHook().epThemeColor : "",
color: item.path === active.value ? "#fff" : "",
fontSize: item.path === active.value ? "16px" : "14px"
};
};
});
const active = computed({ const active = computed({
get() { get() {
return props.value; return props.value;
@@ -67,6 +57,24 @@ function handleTo() {
emit("enter"); emit("enter");
} }
</script> </script>
<template>
<div class="result">
<template v-for="item in options" :key="item.path">
<div
class="result-item dark:bg-[#1d1d1d]"
:style="itemStyle(item)"
@click="handleTo"
@mouseenter="handleMouse(item)"
>
<component :is="useRenderIcon(item.meta?.icon ?? 'bookmark-2-line')" />
<span class="result-item-title">{{ t(item.meta?.title) }}</span>
<enterOutlined />
</div>
</template>
</div>
</template>
<style lang="scss" scoped> <style lang="scss" scoped>
.result { .result {
padding-bottom: 12px; padding-bottom: 12px;
@@ -78,8 +86,9 @@ function handleTo() {
margin-top: 8px; margin-top: 8px;
padding: 14px; padding: 14px;
border-radius: 4px; border-radius: 4px;
background: #e5e7eb;
cursor: pointer; cursor: pointer;
border: 0.1px solid #ccc;
transition: all 0.3s;
&-title { &-title {
display: flex; display: flex;

View File

@@ -1,6 +1,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import { SearchModal } from "./components"; import { SearchModal } from "./components";
import useBoolean from "../../hooks/useBoolean"; import { useBoolean } from "../../hooks/useBoolean";
const { bool: show, toggle } = useBoolean(); const { bool: show, toggle } = useBoolean();
function handleSearch() { function handleSearch() {
toggle(); toggle();
@@ -8,23 +8,11 @@ function handleSearch() {
</script> </script>
<template> <template>
<div class="search-container" @click="handleSearch"> <div
class="search-container w-[40px] h-[48px] flex-c cursor-pointer navbar-bg-hover"
@click="handleSearch"
>
<IconifyIconOffline icon="search" /> <IconifyIconOffline icon="search" />
</div> </div>
<SearchModal v-model:value="show" /> <SearchModal v-model:value="show" />
</template> </template>
<style lang="scss" scoped>
.search-container {
display: flex;
align-items: center;
justify-content: center;
height: 48px;
width: 40px;
cursor: pointer;
&:hover {
background: #f6f6f6;
}
}
</style>

View File

@@ -1,75 +1,57 @@
<script setup lang="ts"> <script setup lang="ts">
import { import {
reactive,
ref, ref,
unref, unref,
watch, watch,
reactive,
computed, computed,
nextTick, nextTick,
useCssModule, useCssModule
getCurrentInstance
} from "vue"; } from "vue";
import { find } from "lodash-unified";
import { getConfig } from "/@/config"; import { getConfig } from "/@/config";
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
import panel from "../panel/index.vue"; import panel from "../panel/index.vue";
import { emitter } from "/@/utils/mitt"; import { emitter } from "/@/utils/mitt";
import { resetRouter } from "/@/router";
import { templateRef } from "@vueuse/core"; import { templateRef } from "@vueuse/core";
import { debounce } from "/@/utils/debounce"; import { routerArrays } from "/@/layout/types";
import { themeColorsType } from "../../types"; import { useNav } from "/@/layout/hooks/useNav";
import { useAppStoreHook } from "/@/store/modules/app"; import { useAppStoreHook } from "/@/store/modules/app";
import { shadeBgColor } from "../../theme/element-plus";
import { useEpThemeStoreHook } from "/@/store/modules/epTheme";
import { storageLocal, storageSession } from "/@/utils/storage";
import { useMultiTagsStoreHook } from "/@/store/modules/multiTags"; import { useMultiTagsStoreHook } from "/@/store/modules/multiTags";
import { createNewStyle, writeNewStyle } from "../../theme/element-plus"; import { useDataThemeChange } from "/@/layout/hooks/useDataThemeChange";
import {
useDark,
debounce,
useGlobal,
storageLocal,
storageSession
} from "@pureadmin/utils";
import { toggleTheme } from "@pureadmin/theme/dist/browser-utils"; import { toggleTheme } from "@pureadmin/theme/dist/browser-utils";
import dayIcon from "/@/assets/svg/day.svg?component"; import dayIcon from "/@/assets/svg/day.svg?component";
import darkIcon from "/@/assets/svg/dark.svg?component"; import darkIcon from "/@/assets/svg/dark.svg?component";
const router = useRouter(); const router = useRouter();
const { device } = useNav();
const { isDark } = useDark();
const { isSelect } = useCssModule(); const { isSelect } = useCssModule();
const body = document.documentElement as HTMLElement; const { $storage } = useGlobal<GlobalPropertiesApi>();
const instance =
getCurrentInstance().appContext.app.config.globalProperties.$storage;
const instanceConfig =
getCurrentInstance().appContext.app.config.globalProperties.$config;
let themeColors = ref<Array<themeColorsType>>([
// 道奇蓝(默认)
{ color: "#1b2a47", themeColor: "default" },
// 亮白色
{ color: "#ffffff", themeColor: "light" },
// 猩红色
{ color: "#f5222d", themeColor: "dusk" },
// 橙红色
{ color: "#fa541c", themeColor: "volcano" },
// 金色
{ color: "#fadb14", themeColor: "yellow" },
// 绿宝石
{ color: "#13c2c2", themeColor: "mingQing" },
// 酸橙绿
{ color: "#52c41a", themeColor: "auroraGreen" },
// 深粉色
{ color: "#eb2f96", themeColor: "pink" },
// 深紫罗兰色
{ color: "#722ed1", themeColor: "saucePurple" }
]);
const mixRef = templateRef<HTMLElement | null>("mixRef", null);
const verticalRef = templateRef<HTMLElement | null>("verticalRef", null); const verticalRef = templateRef<HTMLElement | null>("verticalRef", null);
const horizontalRef = templateRef<HTMLElement | null>("horizontalRef", null); const horizontalRef = templateRef<HTMLElement | null>("horizontalRef", null);
const mixRef = templateRef<HTMLElement | null>("mixRef", null);
let layoutTheme = const {
ref(storageLocal.getItem("responsive-layout")) || body,
ref({ dataTheme,
layout: instanceConfig?.Layout ?? "vertical", layoutTheme,
theme: instanceConfig?.Theme ?? "default" themeColors,
}); dataThemeChange,
setEpThemeColor,
setLayoutThemeColor
} = useDataThemeChange();
// body添加layout属性作用于src/style/sidebar.scss /* body添加layout属性作用于src/style/sidebar.scss */
if (unref(layoutTheme)) { if (unref(layoutTheme)) {
let layout = unref(layoutTheme).layout; let layout = unref(layoutTheme).layout;
let theme = unref(layoutTheme).theme; let theme = unref(layoutTheme).theme;
@@ -79,20 +61,18 @@ if (unref(layoutTheme)) {
setLayoutModel(layout); setLayoutModel(layout);
} }
// 默认灵动模式 /** 默认灵动模式 */
const markValue = ref(instance.configure?.showModel ?? "smart"); const markValue = ref($storage.configure?.showModel ?? "smart");
const logoVal = ref(instance.configure?.showLogo ?? true); const logoVal = ref($storage.configure?.showLogo ?? true);
const epThemeColor = ref(useEpThemeStoreHook().getEpThemeColor);
const settings = reactive({ const settings = reactive({
greyVal: instance.configure.grey, greyVal: $storage.configure.grey,
weakVal: instance.configure.weak, weakVal: $storage.configure.weak,
tabsVal: instance.configure.hideTabs, tabsVal: $storage.configure.hideTabs,
showLogo: instance.configure.showLogo, showLogo: $storage.configure.showLogo,
showModel: instance.configure.showModel, showModel: $storage.configure.showModel,
multiTagsCache: instance.configure.multiTagsCache multiTagsCache: $storage.configure.multiTagsCache
}); });
const getThemeColorStyle = computed(() => { const getThemeColorStyle = computed(() => {
@@ -101,10 +81,17 @@ const getThemeColorStyle = computed(() => {
}; };
}); });
/** 当网页为暗黑模式时不显示亮白色切换选项 */
const showThemeColors = computed(() => {
return themeColor => {
return themeColor === "light" && isDark.value ? false : true;
};
});
function storageConfigureChange<T>(key: string, val: T): void { function storageConfigureChange<T>(key: string, val: T): void {
const storageConfigure = instance.configure; const storageConfigure = $storage.configure;
storageConfigure[key] = val; storageConfigure[key] = val;
instance.configure = storageConfigure; $storage.configure = storageConfigure;
} }
function toggleClass(flag: boolean, clsName: string, target?: HTMLElement) { function toggleClass(flag: boolean, clsName: string, target?: HTMLElement) {
@@ -114,13 +101,13 @@ function toggleClass(flag: boolean, clsName: string, target?: HTMLElement) {
targetEl.className = flag ? `${className} ${clsName} ` : className; targetEl.className = flag ? `${className} ${clsName} ` : className;
} }
// 灰色模式设置 /** 灰色模式设置 */
const greyChange = (value): void => { const greyChange = (value): void => {
toggleClass(settings.greyVal, "html-grey", document.querySelector("html")); toggleClass(settings.greyVal, "html-grey", document.querySelector("html"));
storageConfigureChange("grey", value); storageConfigureChange("grey", value);
}; };
// 色弱模式设置 /** 色弱模式设置 */
const weekChange = (value): void => { const weekChange = (value): void => {
toggleClass( toggleClass(
settings.weakVal, settings.weakVal,
@@ -133,7 +120,7 @@ const weekChange = (value): void => {
const tagsChange = () => { const tagsChange = () => {
let showVal = settings.tabsVal; let showVal = settings.tabsVal;
storageConfigureChange("hideTabs", showVal); storageConfigureChange("hideTabs", showVal);
emitter.emit("tagViewsChange", showVal); emitter.emit("tagViewsChange", showVal as unknown as string);
}; };
const multiTagsCacheChange = () => { const multiTagsCacheChange = () => {
@@ -142,27 +129,19 @@ const multiTagsCacheChange = () => {
useMultiTagsStoreHook().multiTagsCacheChange(multiTagsCache); useMultiTagsStoreHook().multiTagsCacheChange(multiTagsCache);
}; };
// 清空缓存并返回登录页 /** 清空缓存并返回登录页 */
function onReset() { function onReset() {
router.push("/login"); router.push("/login");
const { Grey, Weak, MultiTagsCache, EpThemeColor, Layout } = getConfig(); const { Grey, Weak, MultiTagsCache, EpThemeColor, Layout } = getConfig();
useAppStoreHook().setLayout(Layout); useAppStoreHook().setLayout(Layout);
useEpThemeStoreHook().setEpThemeColor(EpThemeColor); setEpThemeColor(EpThemeColor);
useMultiTagsStoreHook().multiTagsCacheChange(MultiTagsCache); useMultiTagsStoreHook().multiTagsCacheChange(MultiTagsCache);
toggleClass(Grey, "html-grey", document.querySelector("html")); toggleClass(Grey, "html-grey", document.querySelector("html"));
toggleClass(Weak, "html-weakness", document.querySelector("html")); toggleClass(Weak, "html-weakness", document.querySelector("html"));
useMultiTagsStoreHook().handleTags("equal", [ useMultiTagsStoreHook().handleTags("equal", [...routerArrays]);
{
path: "/welcome",
parentPath: "/",
meta: {
title: "menus.hshome",
icon: "home-filled"
}
}
]);
storageLocal.clear(); storageLocal.clear();
storageSession.clear(); storageSession.clear();
resetRouter();
} }
function onChange(label) { function onChange(label) {
@@ -170,7 +149,7 @@ function onChange(label) {
emitter.emit("tagViewsShowModel", label); emitter.emit("tagViewsShowModel", label);
} }
// 侧边栏Logo /** 侧边栏Logo */
function logoChange() { function logoChange() {
unref(logoVal) unref(logoVal)
? storageConfigureChange("showLogo", true) ? storageConfigureChange("showLogo", true)
@@ -184,8 +163,8 @@ function setFalse(Doms): any {
}); });
} }
watch(instance, ({ layout }) => { watch($storage, ({ layout }) => {
// 设置wangeditorV5主题色 /* 设置wangeditorV5主题色 */
body.style.setProperty("--w-e-toolbar-active-color", layout["epThemeColor"]); body.style.setProperty("--w-e-toolbar-active-color", layout["epThemeColor"]);
switch (layout["layout"]) { switch (layout["layout"]) {
case "vertical": case "vertical":
@@ -206,7 +185,7 @@ watch(instance, ({ layout }) => {
} }
}); });
// 主题色 激活选择项 /** 主题色 激活选择项 */
const getThemeColor = computed(() => { const getThemeColor = computed(() => {
return current => { return current => {
if ( if (
@@ -225,83 +204,27 @@ const getThemeColor = computed(() => {
}; };
}); });
// 设置导航模式 /** 设置导航模式 */
function setLayoutModel(layout: string) { function setLayoutModel(layout: string) {
layoutTheme.value.layout = layout; layoutTheme.value.layout = layout;
window.document.body.setAttribute("layout", layout); window.document.body.setAttribute("layout", layout);
instance.layout = { $storage.layout = {
layout, layout,
theme: layoutTheme.value.theme, theme: layoutTheme.value.theme,
darkMode: instance.layout.darkMode, darkMode: $storage.layout?.darkMode,
sidebarStatus: instance.layout.sidebarStatus, sidebarStatus: $storage.layout?.sidebarStatus,
epThemeColor: instance.layout.epThemeColor epThemeColor: $storage.layout?.epThemeColor
}; };
useAppStoreHook().setLayout(layout); useAppStoreHook().setLayout(layout);
} }
// 存放夜间主题切换前的导航主题色 /* 初始化项目配置 */
let tempLayoutThemeColor;
// 设置导航主题色
function setLayoutThemeColor(theme: string) {
tempLayoutThemeColor = instance.layout.theme;
layoutTheme.value.theme = theme;
toggleTheme({
scopeName: `layout-theme-${theme}`
});
instance.layout = {
layout: useAppStoreHook().layout,
theme,
darkMode: dataTheme.value,
sidebarStatus: instance.layout.sidebarStatus,
epThemeColor: instance.layout.epThemeColor
};
if (theme === "default" || theme === "light") {
setEpThemeColor(getConfig().EpThemeColor);
} else {
const colors = find(themeColors.value, { themeColor: theme });
setEpThemeColor(colors.color);
}
}
// 设置ep主题色
const setEpThemeColor = (color: string) => {
// @ts-expect-error
writeNewStyle(createNewStyle(color));
useEpThemeStoreHook().setEpThemeColor(color);
body.style.setProperty("--el-color-primary-active", shadeBgColor(color));
};
let dataTheme = ref<boolean>(instance.layout.darkMode);
// 日间、夜间主题切换
function dataThemeChange() {
if (dataTheme.value) {
body.setAttribute("data-theme", "dark");
setLayoutThemeColor("light");
} else {
body.setAttribute("data-theme", "");
tempLayoutThemeColor && setLayoutThemeColor(tempLayoutThemeColor);
instance.layout = {
layout: useAppStoreHook().layout,
theme: instance.layout.theme,
darkMode: dataTheme.value,
sidebarStatus: instance.layout.sidebarStatus,
epThemeColor: instance.layout.epThemeColor
};
}
}
//初始化项目配置
nextTick(() => { nextTick(() => {
settings.greyVal && settings.greyVal &&
document.querySelector("html")?.setAttribute("class", "html-grey"); document.querySelector("html")?.setAttribute("class", "html-grey");
settings.weakVal && settings.weakVal &&
document.querySelector("html")?.setAttribute("class", "html-weakness"); document.querySelector("html")?.setAttribute("class", "html-weakness");
settings.tabsVal && tagsChange(); settings.tabsVal && tagsChange();
// @ts-expect-error
writeNewStyle(createNewStyle(epThemeColor.value));
dataThemeChange(); dataThemeChange();
}); });
</script> </script>
@@ -331,7 +254,12 @@ nextTick(() => {
</li> </li>
</el-tooltip> </el-tooltip>
<el-tooltip class="item" content="顶部模式" placement="bottom"> <el-tooltip
v-if="device !== 'mobile'"
class="item"
content="顶部模式"
placement="bottom"
>
<li <li
:class="layoutTheme.layout === 'horizontal' ? $style.isSelect : ''" :class="layoutTheme.layout === 'horizontal' ? $style.isSelect : ''"
ref="horizontalRef" ref="horizontalRef"
@@ -342,7 +270,12 @@ nextTick(() => {
</li> </li>
</el-tooltip> </el-tooltip>
<el-tooltip class="item" content="混合模式" placement="bottom"> <el-tooltip
v-if="device !== 'mobile'"
class="item"
content="混合模式"
placement="bottom"
>
<li <li
:class="layoutTheme.layout === 'mix' ? $style.isSelect : ''" :class="layoutTheme.layout === 'mix' ? $style.isSelect : ''"
ref="mixRef" ref="mixRef"
@@ -354,11 +287,12 @@ nextTick(() => {
</el-tooltip> </el-tooltip>
</ul> </ul>
<el-divider v-show="!dataTheme">主题色</el-divider> <el-divider>主题色</el-divider>
<ul class="theme-color" v-show="!dataTheme"> <ul class="theme-color">
<li <li
v-for="(item, index) in themeColors" v-for="(item, index) in themeColors"
:key="index" :key="index"
v-show="showThemeColors(item.themeColor)"
:style="getThemeColorStyle(item.color)" :style="getThemeColorStyle(item.color)"
@click="setLayoutThemeColor(item.themeColor)" @click="setLayoutThemeColor(item.themeColor)"
> >
@@ -374,8 +308,8 @@ nextTick(() => {
<el-divider>界面显示</el-divider> <el-divider>界面显示</el-divider>
<ul class="setting"> <ul class="setting">
<li v-show="!dataTheme"> <li>
<span>灰色模式</span> <span class="dark:text-white">灰色模式</span>
<el-switch <el-switch
v-model="settings.greyVal" v-model="settings.greyVal"
inline-prompt inline-prompt
@@ -385,8 +319,8 @@ nextTick(() => {
@change="greyChange" @change="greyChange"
/> />
</li> </li>
<li v-show="!dataTheme"> <li>
<span>色弱模式</span> <span class="dark:text-white">色弱模式</span>
<el-switch <el-switch
v-model="settings.weakVal" v-model="settings.weakVal"
inline-prompt inline-prompt
@@ -397,7 +331,7 @@ nextTick(() => {
/> />
</li> </li>
<li> <li>
<span>隐藏标签页</span> <span class="dark:text-white">隐藏标签页</span>
<el-switch <el-switch
v-model="settings.tabsVal" v-model="settings.tabsVal"
inline-prompt inline-prompt
@@ -408,7 +342,7 @@ nextTick(() => {
/> />
</li> </li>
<li> <li>
<span>侧边栏Logo</span> <span class="dark:text-white">侧边栏Logo</span>
<el-switch <el-switch
v-model="logoVal" v-model="logoVal"
inline-prompt inline-prompt
@@ -421,7 +355,7 @@ nextTick(() => {
/> />
</li> </li>
<li> <li>
<span>标签页持久化</span> <span class="dark:text-white">标签页持久化</span>
<el-switch <el-switch
v-model="settings.multiTagsCache" v-model="settings.multiTagsCache"
inline-prompt inline-prompt
@@ -433,7 +367,7 @@ nextTick(() => {
</li> </li>
<li> <li>
<span>标签风格</span> <span class="dark:text-white">标签风格</span>
<el-radio-group v-model="markValue" size="small" @change="onChange"> <el-radio-group v-model="markValue" size="small" @change="onChange">
<el-radio label="card">卡片</el-radio> <el-radio label="card">卡片</el-radio>
<el-radio label="smart">灵动</el-radio> <el-radio label="smart">灵动</el-radio>
@@ -453,8 +387,8 @@ nextTick(() => {
height="15" height="15"
style="margin-right: 4px" style="margin-right: 4px"
/> />
清空缓存并返回登录页</el-button 清空缓存并返回登录页
> </el-button>
</panel> </panel>
</template> </template>

View File

@@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, watch } from "vue";
import { isEqual } from "lodash-unified"; import { isEqual } from "lodash-unified";
import { transformI18n } from "/@/plugins/i18n"; import { transformI18n } from "/@/plugins/i18n";
import { ref, watch, onMounted, toRaw } from "vue";
import { getParentPaths, findRouteByPath } from "/@/router/utils"; import { getParentPaths, findRouteByPath } from "/@/router/utils";
import { useMultiTagsStoreHook } from "/@/store/modules/multiTags"; import { useMultiTagsStoreHook } from "/@/store/modules/multiTags";
import { useRoute, useRouter, RouteLocationMatched } from "vue-router"; import { useRoute, useRouter, RouteLocationMatched } from "vue-router";
@@ -9,24 +9,29 @@ import { useRoute, useRouter, RouteLocationMatched } from "vue-router";
const route = useRoute(); const route = useRoute();
const levelList = ref([]); const levelList = ref([]);
const router = useRouter(); const router = useRouter();
const routes = router.options.routes; const routes: any = router.options.routes;
const multiTags = useMultiTagsStoreHook().multiTags; const multiTags: any = useMultiTagsStoreHook().multiTags;
const isDashboard = (route: RouteLocationMatched): boolean | string => { const isDashboard = (route: RouteLocationMatched): boolean | string => {
const name = route && (route.name as string); const name = route && (route.name as string);
if (!name) { if (!name) return false;
return false; return name.trim().toLocaleLowerCase() === "Welcome".toLocaleLowerCase();
}
return name.trim().toLocaleLowerCase() === "welcome".toLocaleLowerCase();
}; };
const getBreadcrumb = (): void => { const getBreadcrumb = (): void => {
// 当前路由信息 // 当前路由信息
let currentRoute; let currentRoute;
if (Object.keys(route.query).length > 0) { if (Object.keys(route.query).length > 0) {
multiTags.forEach(item => { multiTags.forEach(item => {
if (isEqual(route.query, item?.query)) { if (isEqual(route.query, item?.query)) {
currentRoute = item; currentRoute = toRaw(item);
}
});
} else if (Object.keys(route.params).length > 0) {
multiTags.forEach(item => {
if (isEqual(route.params, item?.params)) {
currentRoute = toRaw(item);
} }
}); });
} else { } else {
@@ -38,29 +43,12 @@ const getBreadcrumb = (): void => {
let matched = []; let matched = [];
// 获取每个父级路径对应的路由信息 // 获取每个父级路径对应的路由信息
parentRoutes.forEach(path => { parentRoutes.forEach(path => {
if (path !== "/") { if (path !== "/") matched.push(findRouteByPath(path, routes));
matched.push(findRouteByPath(path, routes));
}
}); });
if (router.currentRoute.value.meta?.refreshRedirect) {
matched.unshift(
findRouteByPath(
router.currentRoute.value.meta.refreshRedirect as string,
routes
)
);
} else {
// 过滤与子级相同标题的父级路由
matched = matched.filter(item => {
return !item.redirect || (item.redirect && item.children.length !== 1);
});
}
if (currentRoute?.path !== "/welcome") {
matched.push(currentRoute);
}
const first = matched[0]; if (currentRoute?.path !== "/welcome") matched.push(currentRoute);
if (!isDashboard(first)) {
if (!isDashboard(matched[0])) {
matched = [ matched = [
{ {
path: "/welcome", path: "/welcome",
@@ -70,59 +58,51 @@ const getBreadcrumb = (): void => {
].concat(matched); ].concat(matched);
} }
matched.forEach((item, index) => {
if (currentRoute?.query || currentRoute?.params) return;
if (item?.children) {
item.children.forEach(v => {
if (v?.meta?.title === item?.meta?.title) {
matched.splice(index, 1);
}
});
}
});
levelList.value = matched.filter( levelList.value = matched.filter(
item => item?.meta && item?.meta.title !== false item => item?.meta && item?.meta.title !== false
); );
}; };
getBreadcrumb(); const handleLink = (item: RouteLocationMatched): void => {
const { redirect, path } = item;
if (redirect) {
router.push(redirect as any);
} else {
router.push(path);
}
};
onMounted(() => {
getBreadcrumb();
});
watch( watch(
() => route.path, () => route.path,
() => getBreadcrumb() () => {
); getBreadcrumb();
watch(
() => route.query,
() => getBreadcrumb()
);
const handleLink = (item: RouteLocationMatched): any => {
const { redirect, path } = item;
if (redirect) {
router.push(redirect.toString());
return;
} }
router.push(path); );
};
</script> </script>
<template> <template>
<el-breadcrumb class="app-breadcrumb" separator="/"> <el-breadcrumb class="!leading-[50px] select-none" separator="/">
<transition-group appear name="breadcrumb"> <transition-group appear name="breadcrumb">
<el-breadcrumb-item v-for="(item, index) in levelList" :key="item.path"> <el-breadcrumb-item v-for="item in levelList" :key="item.path">
<span <a @click.prevent="handleLink(item)">
v-if="item.redirect === 'noRedirect' || index == levelList.length - 1"
class="no-redirect"
>{{ transformI18n(item.meta.title) }}</span
>
<a v-else @click.prevent="handleLink(item)">
{{ transformI18n(item.meta.title) }} {{ transformI18n(item.meta.title) }}
</a> </a>
</el-breadcrumb-item> </el-breadcrumb-item>
</transition-group> </transition-group>
</el-breadcrumb> </el-breadcrumb>
</template> </template>
<style lang="scss" scoped>
.app-breadcrumb.el-breadcrumb {
display: inline-block;
font-size: 14px;
line-height: 50px;
.no-redirect {
color: #97a8be;
cursor: text;
}
}
</style>

View File

@@ -1,63 +0,0 @@
<script setup lang="ts">
import { ref } from "vue";
import { useEpThemeStoreHook } from "/@/store/modules/epTheme";
export interface Props {
isActive: boolean;
}
const props = withDefaults(defineProps<Props>(), {
isActive: false
});
const fillColor = ref<string>("");
const emit = defineEmits<{
(e: "toggleClick"): void;
}>();
const toggleClick = () => {
emit("toggleClick");
};
</script>
<template>
<div
:class="classes.container"
:title="props.isActive ? '点击折叠' : '点击展开'"
@click="toggleClick"
@mouseenter="fillColor = useEpThemeStoreHook().epThemeColor"
@mouseleave="fillColor = ''"
>
<svg
:fill="fillColor"
:class="['hamburger', props.isActive ? 'is-active' : '']"
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
width="64"
height="64"
>
<path
d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM142.4 642.1L298.7 519a8.84 8.84 0 0 0 0-13.9L142.4 381.9c-5.8-4.6-14.4-.5-14.4 6.9v246.3a8.9 8.9 0 0 0 14.4 7z"
/>
</svg>
</div>
</template>
<style module="classes" scoped>
.container {
padding: 0 15px;
}
</style>
<style scoped>
.hamburger {
display: inline-block;
vertical-align: middle;
width: 20px;
height: 20px;
}
.is-active {
transform: rotate(180deg);
}
</style>

View File

@@ -1,74 +1,39 @@
<script setup lang="ts"> <script setup lang="ts">
import { useI18n } from "vue-i18n"; import { ref, watch } from "vue";
import { useNav } from "../../hooks/nav";
import Search from "../search/index.vue"; import Search from "../search/index.vue";
import Notice from "../notice/index.vue"; import Notice from "../notice/index.vue";
import { templateRef } from "@vueuse/core";
import SidebarItem from "./sidebarItem.vue"; import SidebarItem from "./sidebarItem.vue";
import avatars from "/@/assets/avatars.jpg"; import avatars from "/@/assets/avatars.jpg";
import { useNav } from "/@/layout/hooks/useNav";
import screenfull from "../screenfull/index.vue"; import screenfull from "../screenfull/index.vue";
import { useRoute, useRouter } from "vue-router"; import { deviceDetection } from "@pureadmin/utils";
import { deviceDetection } from "/@/utils/deviceDetection"; import { useTranslationLang } from "../../hooks/useTranslationLang";
import { watch, nextTick, onMounted, getCurrentInstance } from "vue";
import { usePermissionStoreHook } from "/@/store/modules/permission"; import { usePermissionStoreHook } from "/@/store/modules/permission";
import globalization from "/@/assets/svg/globalization.svg?component"; import globalization from "/@/assets/svg/globalization.svg?component";
const route = useRoute(); const menuRef = ref();
const { locale, t } = useI18n();
const routers = useRouter().options.routes;
const menuRef = templateRef<ElRef | null>("menu", null);
const instance =
getCurrentInstance().appContext.config.globalProperties.$storage;
const title =
getCurrentInstance().appContext.config.globalProperties.$config?.Title;
const { t, route, locale, translationCh, translationEn } =
useTranslationLang(menuRef);
const { const {
title,
routers,
logout, logout,
backHome, backHome,
onPanel, onPanel,
changeTitle,
handleResize,
menuSelect, menuSelect,
username, username,
avatarsStyle, avatarsStyle,
getDropdownItemStyle, getDropdownItemStyle,
changeWangeditorLanguage getDropdownItemClass
} = useNav(); } = useNav();
onMounted(() => {
nextTick(() => {
handleResize(menuRef.value);
});
});
watch(
() => locale.value,
() => {
changeTitle(route.meta);
locale.value === "en"
? changeWangeditorLanguage(locale.value)
: changeWangeditorLanguage("zh-CN");
}
);
watch( watch(
() => route.path, () => route.path,
() => { () => {
menuSelect(route.path, routers); menuSelect(route.path, routers);
} }
); );
function translationCh() {
instance.locale = { locale: "zh" };
locale.value = "zh";
handleResize(menuRef.value);
}
function translationEn() {
instance.locale = { locale: "en" };
locale.value = "en";
handleResize(menuRef.value);
}
</script> </script>
<template> <template>
@@ -78,11 +43,11 @@ function translationEn() {
<h4>{{ title }}</h4> <h4>{{ title }}</h4>
</div> </div>
<el-menu <el-menu
ref="menu"
class="horizontal-header-menu"
mode="horizontal"
:default-active="route.path"
router router
ref="menuRef"
mode="horizontal"
class="horizontal-header-menu"
:default-active="route.path"
@select="indexPath => menuSelect(indexPath, routers)" @select="indexPath => menuSelect(indexPath, routers)"
> >
<sidebar-item <sidebar-item
@@ -101,33 +66,39 @@ function translationEn() {
<screenfull id="header-screenfull" v-show="!deviceDetection()" /> <screenfull id="header-screenfull" v-show="!deviceDetection()" />
<!-- 国际化 --> <!-- 国际化 -->
<el-dropdown id="header-translation" trigger="click"> <el-dropdown id="header-translation" trigger="click">
<globalization /> <globalization
class="navbar-bg-hover w-[40px] h-[48px] p-[11px] cursor-pointer outline-none"
/>
<template #dropdown> <template #dropdown>
<el-dropdown-menu class="translation"> <el-dropdown-menu class="translation">
<el-dropdown-item <el-dropdown-item
:style="getDropdownItemStyle(locale, 'zh')" :style="getDropdownItemStyle(locale, 'zh')"
:class="['dark:!text-white', getDropdownItemClass(locale, 'zh')]"
@click="translationCh" @click="translationCh"
> >
<span class="check-zh" v-show="locale === 'zh'"> <span class="check-zh" v-show="locale === 'zh'">
<IconifyIconOffline icon="check" /> </span <IconifyIconOffline icon="check" />
>简体中文 </span>
简体中文
</el-dropdown-item> </el-dropdown-item>
<el-dropdown-item <el-dropdown-item
:style="getDropdownItemStyle(locale, 'en')" :style="getDropdownItemStyle(locale, 'en')"
:class="['dark:!text-white', getDropdownItemClass(locale, 'en')]"
@click="translationEn" @click="translationEn"
> >
<span class="check-en" v-show="locale === 'en'"> <span class="check-en" v-show="locale === 'en'">
<IconifyIconOffline icon="check" /> </span <IconifyIconOffline icon="check" />
>English</el-dropdown-item </span>
> English
</el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
</template> </template>
</el-dropdown> </el-dropdown>
<!-- 退出登 --> <!-- 退出登 -->
<el-dropdown trigger="click"> <el-dropdown trigger="click">
<span class="el-dropdown-link"> <span class="el-dropdown-link navbar-bg-hover">
<img v-if="avatars" :src="avatars" :style="avatarsStyle" /> <img v-if="avatars" :src="avatars" :style="avatarsStyle" />
<p v-if="username">{{ username }}</p> <p v-if="username" class="dark:text-white">{{ username }}</p>
</span> </span>
<template #dropdown> <template #dropdown>
<el-dropdown-menu class="logout"> <el-dropdown-menu class="logout">
@@ -136,13 +107,13 @@ function translationEn() {
icon="logout-circle-r-line" icon="logout-circle-r-line"
style="margin: 5px" style="margin: 5px"
/> />
{{ t("buttons.hsLoginOut") }}</el-dropdown-item {{ t("buttons.hsLoginOut") }}
> </el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
</template> </template>
</el-dropdown> </el-dropdown>
<span <span
class="el-icon-setting" class="el-icon-setting navbar-bg-hover"
:title="t('buttons.hssystemSet')" :title="t('buttons.hssystemSet')"
@click="onPanel" @click="onPanel"
> >

View File

@@ -0,0 +1,47 @@
<script setup lang="ts">
import { useDark } from "@pureadmin/utils";
interface Props {
isActive: boolean;
}
const props = withDefaults(defineProps<Props>(), {
isActive: false
});
const { isDark } = useDark();
const emit = defineEmits<{
(e: "toggleClick"): void;
}>();
const toggleClick = () => {
emit("toggleClick");
};
</script>
<template>
<div class="container">
<el-tooltip
placement="right"
:effect="isDark ? 'dark' : 'light'"
:content="props.isActive ? '点击折叠' : '点击展开'"
>
<IconifyIconOffline
:icon="props.isActive ? 'menu-fold' : 'menu-unfold'"
class="cursor-pointer inline-block align-middle text-primary hover:text-primary dark:hover:!text-white w-[16px] h-[16px] ml-4 mb-1"
@click="toggleClick"
/>
</el-tooltip>
</div>
</template>
<style lang="scss" scoped>
.container {
position: absolute;
bottom: 0;
width: 100%;
height: 40px;
line-height: 40px;
box-shadow: 0 0 6px -2px var(--el-color-primary);
}
</style>

View File

@@ -1,11 +1,11 @@
<script setup lang="ts"> <script setup lang="ts">
import { getCurrentInstance } from "vue"; import { useNav } from "/@/layout/hooks/useNav";
const props = defineProps({ const props = defineProps({
collapse: Boolean collapse: Boolean
}); });
const title = const { title } = useNav();
getCurrentInstance().appContext.config.globalProperties.$config?.Title;
</script> </script>
<template> <template>

View File

@@ -1,48 +1,39 @@
<script setup lang="ts"> <script setup lang="ts">
import { useI18n } from "vue-i18n";
import Search from "../search/index.vue"; import Search from "../search/index.vue";
import Notice from "../notice/index.vue"; import Notice from "../notice/index.vue";
import { useNav } from "../../hooks/nav";
import { templateRef } from "@vueuse/core";
import avatars from "/@/assets/avatars.jpg"; import avatars from "/@/assets/avatars.jpg";
import { useNav } from "/@/layout/hooks/useNav";
import { transformI18n } from "/@/plugins/i18n"; import { transformI18n } from "/@/plugins/i18n";
import screenfull from "../screenfull/index.vue"; import screenfull from "../screenfull/index.vue";
import { useRoute, useRouter } from "vue-router"; import { deviceDetection } from "@pureadmin/utils";
import { deviceDetection } from "/@/utils/deviceDetection"; import { ref, toRaw, watch, onMounted } from "vue";
import { useRenderIcon } from "/@/components/ReIcon/src/hooks"; import { useRenderIcon } from "/@/components/ReIcon/src/hooks";
import { useEpThemeStoreHook } from "/@/store/modules/epTheme";
import { getParentPaths, findRouteByPath } from "/@/router/utils"; import { getParentPaths, findRouteByPath } from "/@/router/utils";
import { useTranslationLang } from "../../hooks/useTranslationLang";
import { usePermissionStoreHook } from "/@/store/modules/permission"; import { usePermissionStoreHook } from "/@/store/modules/permission";
import globalization from "/@/assets/svg/globalization.svg?component"; import globalization from "/@/assets/svg/globalization.svg?component";
import { ref, watch, nextTick, onMounted, getCurrentInstance } from "vue";
const route = useRoute(); const menuRef = ref();
const { locale, t } = useI18n(); let defaultActive = ref(null);
const routers = useRouter().options.routes;
const menuRef = templateRef<ElRef | null>("menu", null);
const instance =
getCurrentInstance().appContext.config.globalProperties.$storage;
const { t, route, locale, translationCh, translationEn } =
useTranslationLang(menuRef);
const { const {
device,
routers,
logout, logout,
onPanel, onPanel,
changeTitle,
toggleSideBar,
handleResize,
menuSelect, menuSelect,
resolvePath, resolvePath,
pureApp,
username, username,
avatarsStyle, avatarsStyle,
getDropdownItemStyle, getDropdownItemStyle,
changeWangeditorLanguage getDropdownItemClass
} = useNav(); } = useNav();
let defaultActive = ref(null);
function getDefaultActive(routePath) { function getDefaultActive(routePath) {
const wholeMenus = usePermissionStoreHook().wholeMenus; const wholeMenus = usePermissionStoreHook().wholeMenus;
// 当前路由的父级路径 /** 当前路由的父级路径 */
const parentRoutes = getParentPaths(routePath, wholeMenus)[0]; const parentRoutes = getParentPaths(routePath, wholeMenus)[0];
defaultActive.value = findRouteByPath( defaultActive.value = findRouteByPath(
parentRoutes, parentRoutes,
@@ -52,70 +43,24 @@ function getDefaultActive(routePath) {
onMounted(() => { onMounted(() => {
getDefaultActive(route.path); getDefaultActive(route.path);
nextTick(() => {
handleResize(menuRef.value);
});
}); });
watch(
() => locale.value,
() => {
changeTitle(route.meta);
locale.value === "en"
? changeWangeditorLanguage(locale.value)
: changeWangeditorLanguage("zh-CN");
}
);
watch( watch(
() => route.path, () => route.path,
() => { () => {
getDefaultActive(route.path); getDefaultActive(route.path);
} }
); );
function translationCh() {
instance.locale = { locale: "zh" };
locale.value = "zh";
handleResize(menuRef.value);
}
function translationEn() {
instance.locale = { locale: "en" };
locale.value = "en";
handleResize(menuRef.value);
}
</script> </script>
<template> <template>
<div class="horizontal-header"> <div v-if="device !== 'mobile'" class="horizontal-header">
<div
:class="classes.container"
:title="pureApp.sidebar.opened ? '点击折叠' : '点击展开'"
@click="toggleSideBar"
>
<svg
:fill="useEpThemeStoreHook().fill"
:class="[
'hamburger',
pureApp.sidebar.opened ? 'is-active-hamburger' : ''
]"
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
width="64"
height="64"
>
<path
d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM142.4 642.1L298.7 519a8.84 8.84 0 0 0 0-13.9L142.4 381.9c-5.8-4.6-14.4-.5-14.4 6.9v246.3a8.9 8.9 0 0 0 14.4 7z"
/>
</svg>
</div>
<el-menu <el-menu
ref="menu"
class="horizontal-header-menu"
mode="horizontal"
:default-active="defaultActive"
router router
ref="menuRef"
mode="horizontal"
class="horizontal-header-menu"
:default-active="defaultActive"
@select="indexPath => menuSelect(indexPath, routers)" @select="indexPath => menuSelect(indexPath, routers)"
> >
<el-menu-item <el-menu-item
@@ -124,10 +69,15 @@ function translationEn() {
:index="resolvePath(route) || route.redirect" :index="resolvePath(route) || route.redirect"
> >
<template #title> <template #title>
<div v-show="route.meta.icon" :class="['el-icon', route.meta.icon]"> <div
<component :is="useRenderIcon(route.meta && route.meta.icon)" /> v-if="toRaw(route.meta.icon)"
:class="['sub-menu-icon', route.meta.icon]"
>
<component
:is="useRenderIcon(route.meta && toRaw(route.meta.icon))"
/>
</div> </div>
<span>{{ transformI18n(route.meta.title) }}</span> <span class="select-none">{{ transformI18n(route.meta.title) }}</span>
<FontIcon <FontIcon
v-if="route.meta.extraIcon" v-if="route.meta.extraIcon"
width="30px" width="30px"
@@ -148,31 +98,39 @@ function translationEn() {
<screenfull id="header-screenfull" v-show="!deviceDetection()" /> <screenfull id="header-screenfull" v-show="!deviceDetection()" />
<!-- 国际化 --> <!-- 国际化 -->
<el-dropdown id="header-translation" trigger="click"> <el-dropdown id="header-translation" trigger="click">
<globalization /> <globalization
class="navbar-bg-hover w-[40px] h-[48px] p-[11px] cursor-pointer outline-none"
/>
<template #dropdown> <template #dropdown>
<el-dropdown-menu class="translation"> <el-dropdown-menu class="translation">
<el-dropdown-item <el-dropdown-item
:style="getDropdownItemStyle(locale, 'zh')" :style="getDropdownItemStyle(locale, 'zh')"
:class="['dark:!text-white', getDropdownItemClass(locale, 'zh')]"
@click="translationCh" @click="translationCh"
><span class="check-zh" v-show="locale === 'zh'"
><IconifyIconOffline icon="check" /></span
>简体中文</el-dropdown-item
> >
<span class="check-zh" v-show="locale === 'zh'">
<IconifyIconOffline icon="check" />
</span>
简体中文
</el-dropdown-item>
<el-dropdown-item <el-dropdown-item
:style="getDropdownItemStyle(locale, 'en')" :style="getDropdownItemStyle(locale, 'en')"
:class="['dark:!text-white', getDropdownItemClass(locale, 'en')]"
@click="translationEn" @click="translationEn"
><span class="check-en" v-show="locale === 'en'"
><IconifyIconOffline icon="check" /></span
>English</el-dropdown-item
> >
<span class="check-en" v-show="locale === 'en'">
<IconifyIconOffline icon="check" />
</span>
English
</el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
</template> </template>
</el-dropdown> </el-dropdown>
<!-- 退出登 --> <!-- 退出登 -->
<el-dropdown trigger="click"> <el-dropdown trigger="click">
<span class="el-dropdown-link"> <span class="el-dropdown-link navbar-bg-hover">
<img v-if="avatars" :src="avatars" :style="avatarsStyle" /> <img v-if="avatars" :src="avatars" :style="avatarsStyle" />
<p v-if="username">{{ username }}</p> <p v-if="username" class="dark:text-white">{{ username }}</p>
</span> </span>
<template #dropdown> <template #dropdown>
<el-dropdown-menu class="logout"> <el-dropdown-menu class="logout">
@@ -181,13 +139,13 @@ function translationEn() {
icon="logout-circle-r-line" icon="logout-circle-r-line"
style="margin: 5px" style="margin: 5px"
/> />
{{ t("buttons.hsLoginOut") }}</el-dropdown-item {{ t("buttons.hsLoginOut") }}
> </el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
</template> </template>
</el-dropdown> </el-dropdown>
<span <span
class="el-icon-setting" class="el-icon-setting navbar-bg-hover"
:title="t('buttons.hssystemSet')" :title="t('buttons.hssystemSet')"
@click="onPanel" @click="onPanel"
> >
@@ -197,26 +155,7 @@ function translationEn() {
</div> </div>
</template> </template>
<style module="classes" scoped>
.container {
padding: 0 15px;
}
</style>
<style lang="scss" scoped> <style lang="scss" scoped>
.hamburger {
width: 20px;
height: 20px;
&:hover {
cursor: pointer;
}
}
.is-active-hamburger {
transform: rotate(180deg);
}
.translation { .translation {
::v-deep(.el-dropdown-menu__item) { ::v-deep(.el-dropdown-menu__item) {
padding: 5px 40px; padding: 5px 40px;

View File

@@ -1,14 +1,12 @@
<script setup lang="ts"> <script setup lang="ts">
import path from "path"; import path from "path";
import { useNav } from "../../hooks/nav";
import { childrenType } from "../../types"; import { childrenType } from "../../types";
import { useNav } from "/@/layout/hooks/useNav";
import { transformI18n } from "/@/plugins/i18n"; import { transformI18n } from "/@/plugins/i18n";
import { useAppStoreHook } from "/@/store/modules/app";
import { useRenderIcon } from "/@/components/ReIcon/src/hooks"; import { useRenderIcon } from "/@/components/ReIcon/src/hooks";
import { ref, PropType, nextTick, computed, CSSProperties } from "vue"; import { ref, toRaw, PropType, nextTick, computed, CSSProperties } from "vue";
const { pureApp } = useNav(); const { layout, isCollapse } = useNav();
const menuMode = ["vertical", "mix"].includes(pureApp.layout);
const props = defineProps({ const props = defineProps({
item: { item: {
@@ -25,7 +23,7 @@ const props = defineProps({
}); });
const getExtraIconStyle = computed((): CSSProperties => { const getExtraIconStyle = computed((): CSSProperties => {
if (useAppStoreHook().getSidebarStatus) { if (!isCollapse.value) {
return { return {
position: "absolute", position: "absolute",
right: "10px" right: "10px"
@@ -46,7 +44,7 @@ const getNoDropdownStyle = computed((): CSSProperties => {
const getDivStyle = computed((): CSSProperties => { const getDivStyle = computed((): CSSProperties => {
return { return {
width: pureApp.sidebar.opened ? "" : "100%", width: !isCollapse.value ? "" : "100%",
display: "flex", display: "flex",
alignItems: "center", alignItems: "center",
justifyContent: "space-between", justifyContent: "space-between",
@@ -54,9 +52,8 @@ const getDivStyle = computed((): CSSProperties => {
}; };
}); });
const getMenuTextStyle = computed((): CSSProperties => { const getMenuTextStyle = computed(() => {
return { return {
width: pureApp.sidebar.opened ? "125px" : "",
overflow: "hidden", overflow: "hidden",
textOverflow: "ellipsis", textOverflow: "ellipsis",
outline: "none" outline: "none"
@@ -65,14 +62,14 @@ const getMenuTextStyle = computed((): CSSProperties => {
const getSubTextStyle = computed((): CSSProperties => { const getSubTextStyle = computed((): CSSProperties => {
return { return {
width: pureApp.sidebar.opened ? "125px" : "", width: !isCollapse.value ? "210px" : "",
display: "inline-block", display: "inline-block",
overflow: "hidden", overflow: "hidden",
textOverflow: "ellipsis" textOverflow: "ellipsis"
}; };
}); });
const getSpanStyle = computed((): CSSProperties => { const getSpanStyle = computed(() => {
return { return {
overflow: "hidden", overflow: "hidden",
textOverflow: "ellipsis" textOverflow: "ellipsis"
@@ -148,21 +145,31 @@ function resolvePath(routePath) {
:class="{ 'submenu-title-noDropdown': !isNest }" :class="{ 'submenu-title-noDropdown': !isNest }"
:style="getNoDropdownStyle" :style="getNoDropdownStyle"
> >
<div class="sub-menu-icon" v-show="props.item.meta.icon"> <div class="sub-menu-icon" v-if="toRaw(props.item.meta.icon)">
<component <component
:is=" :is="
useRenderIcon( useRenderIcon(
onlyOneChild.meta.icon || toRaw(onlyOneChild.meta.icon) ||
(props.item.meta && props.item.meta.icon) (props.item.meta && toRaw(props.item.meta.icon))
) )
" "
/> />
</div> </div>
<div <div
v-if=" v-if="
!pureApp.sidebar.opened && isCollapse &&
pureApp.layout === 'mix' && layout === 'vertical' &&
props.item?.pathList?.length === 2 props.item?.pathList?.length === 1
"
:style="getDivStyle"
>
<span :style="getMenuTextStyle">
{{ transformI18n(onlyOneChild.meta.title) }}
</span>
</div>
<div
v-if="
isCollapse && layout === 'mix' && props.item?.pathList?.length === 2
" "
:style="getDivStyle" :style="getDivStyle"
> >
@@ -172,9 +179,9 @@ function resolvePath(routePath) {
</div> </div>
<template #title> <template #title>
<div :style="getDivStyle"> <div :style="getDivStyle">
<span v-if="!menuMode">{{ <span v-if="layout === 'horizontal'">
transformI18n(onlyOneChild.meta.title) {{ transformI18n(onlyOneChild.meta.title) }}
}}</span> </span>
<el-tooltip <el-tooltip
v-else v-else
placement="top" placement="top"
@@ -205,24 +212,21 @@ function resolvePath(routePath) {
</el-menu-item> </el-menu-item>
</template> </template>
<el-sub-menu <el-sub-menu v-else ref="subMenu" :index="resolvePath(props.item.path)">
v-else
ref="subMenu"
:index="resolvePath(props.item.path)"
popper-append-to-body
>
<template #title> <template #title>
<div v-show="props.item.meta.icon" class="sub-menu-icon"> <div v-if="toRaw(props.item.meta.icon)" class="sub-menu-icon">
<component <component
:is="useRenderIcon(props.item.meta && props.item.meta.icon)" :is="useRenderIcon(props.item.meta && toRaw(props.item.meta.icon))"
/> />
</div> </div>
<span v-if="!menuMode">{{ transformI18n(props.item.meta.title) }}</span> <span v-if="layout === 'horizontal'">
{{ transformI18n(props.item.meta.title) }}
</span>
<el-tooltip <el-tooltip
v-else v-else
placement="top" placement="top"
:offset="-10" :offset="-10"
:disabled="!pureApp.sidebar.opened || !props.item.showTooltip" :disabled="!isCollapse || !props.item.showTooltip"
> >
<template #content> <template #content>
{{ transformI18n(props.item.meta.title) }} {{ transformI18n(props.item.meta.title) }}

View File

@@ -0,0 +1,30 @@
<script setup lang="ts">
interface Props {
isActive: boolean;
}
const props = withDefaults(defineProps<Props>(), {
isActive: false
});
const emit = defineEmits<{
(e: "toggleClick"): void;
}>();
const toggleClick = () => {
emit("toggleClick");
};
</script>
<template>
<div
class="px-3 mr-1 navbar-bg-hover"
:title="props.isActive ? '点击折叠' : '点击展开'"
@click="toggleClick"
>
<IconifyIconOffline
:icon="props.isActive ? 'menu-fold' : 'menu-unfold'"
class="inline-block align-middle hover:text-primary dark:hover:!text-white"
/>
</div>
</template>

View File

@@ -1,26 +1,28 @@
<script setup lang="ts"> <script setup lang="ts">
import Logo from "./logo.vue"; import Logo from "./logo.vue";
import { useRoute } from "vue-router";
import { emitter } from "/@/utils/mitt"; import { emitter } from "/@/utils/mitt";
import { useNav } from "../../hooks/nav";
import SidebarItem from "./sidebarItem.vue"; import SidebarItem from "./sidebarItem.vue";
import { storageLocal } from "/@/utils/storage"; import leftCollapse from "./leftCollapse.vue";
import { useRoute, useRouter } from "vue-router"; import type { StorageConfigs } from "/#/index";
import { useNav } from "/@/layout/hooks/useNav";
import { storageLocal } from "@pureadmin/utils";
import { ref, computed, watch, onBeforeMount } from "vue"; import { ref, computed, watch, onBeforeMount } from "vue";
import { findRouteByPath, getParentPaths } from "/@/router/utils"; import { findRouteByPath, getParentPaths } from "/@/router/utils";
import { usePermissionStoreHook } from "/@/store/modules/permission"; import { usePermissionStoreHook } from "/@/store/modules/permission";
const route = useRoute(); const route = useRoute();
const routers = useRouter().options.routes;
const showLogo = ref( const showLogo = ref(
storageLocal.getItem("responsive-configure")?.showLogo ?? true storageLocal.getItem<StorageConfigs>("responsive-configure")?.showLogo ?? true
); );
const { pureApp, isCollapse, menuSelect } = useNav(); const { routers, device, pureApp, isCollapse, menuSelect, toggleSideBar } =
useNav();
let subMenuData = ref([]); let subMenuData = ref([]);
const menuData = computed(() => { const menuData = computed(() => {
return pureApp.layout === "mix" return pureApp.layout === "mix" && device.value !== "mobile"
? subMenuData.value ? subMenuData.value
: usePermissionStoreHook().wholeMenus; : usePermissionStoreHook().wholeMenus;
}); });
@@ -59,25 +61,33 @@ watch(
<template> <template>
<div :class="['sidebar-container', showLogo ? 'has-logo' : '']"> <div :class="['sidebar-container', showLogo ? 'has-logo' : '']">
<Logo v-if="showLogo" :collapse="isCollapse" /> <Logo v-if="showLogo" :collapse="isCollapse" />
<el-scrollbar wrap-class="scrollbar-wrapper"> <el-scrollbar
wrap-class="scrollbar-wrapper"
:class="[device === 'mobile' ? 'mobile' : 'pc']"
>
<el-menu <el-menu
:default-active="route.path"
:collapse="isCollapse"
unique-opened
router router
:collapse-transition="false" unique-opened
mode="vertical" mode="vertical"
class="outer-most" class="outer-most select-none"
:collapse="isCollapse"
:default-active="route.path"
:collapse-transition="false"
@select="indexPath => menuSelect(indexPath, routers)" @select="indexPath => menuSelect(indexPath, routers)"
> >
<sidebar-item <sidebar-item
v-for="routes in menuData" v-for="routes in menuData"
:key="routes.path" :key="routes.path"
:item="routes" :item="routes"
class="outer-most"
:base-path="routes.path" :base-path="routes.path"
class="outer-most select-none"
/> />
</el-menu> </el-menu>
</el-scrollbar> </el-scrollbar>
<leftCollapse
v-if="device !== 'mobile'"
:is-active="pureApp.sidebar.opened"
@toggleClick="toggleSideBar"
/>
</div> </div>
</template> </template>

View File

@@ -43,7 +43,7 @@
font-size: 14px; font-size: 14px;
display: flex; display: flex;
align-items: center; align-items: center;
color: var(--el-text-color-regular); color: var(--el-text-color-primary);
background: #fff; background: #fff;
position: relative; position: relative;
box-shadow: 0 0 1px #888; box-shadow: 0 0 1px #888;
@@ -92,7 +92,7 @@
a { a {
text-decoration: none; text-decoration: none;
color: #666; color: var(--el-text-color-primary);
padding: 0 4px; padding: 0 4px;
} }
@@ -144,7 +144,7 @@
list-style-type: none; list-style-type: none;
padding: 5px 0; padding: 5px 0;
border-radius: 4px; border-radius: 4px;
color: #000000d9; color: var(--el-text-color-primary);
font-weight: normal; font-weight: normal;
font-size: 13px; font-size: 13px;
white-space: nowrap; white-space: nowrap;
@@ -160,7 +160,7 @@
align-items: center; align-items: center;
&:hover { &:hover {
background: var(--el-color-primary-light-9); // background: var(--el-color-primary-light-9);
color: var(--el-color-primary); color: var(--el-color-primary);
} }
@@ -173,8 +173,6 @@
} }
.el-dropdown-menu { .el-dropdown-menu {
padding: 0;
li { li {
width: 100%; width: 100%;
margin: 0; margin: 0;
@@ -207,7 +205,7 @@
} }
.scroll-item.is-active { .scroll-item.is-active {
background-color: var(--el-color-primary-light-9); // background-color: var(--el-color-primary-light-9);
position: relative; position: relative;
color: #fff; color: #fff;
@@ -220,7 +218,7 @@
} }
a { a {
color: var(--el-color-primary); color: var(--el-color-primary) !important;
} }
} }
@@ -228,7 +226,7 @@
.arrow-right { .arrow-right {
width: 40px; width: 40px;
height: 38px; height: 38px;
color: #00000073; color: var(--el-text-color-primary);
position: relative; position: relative;
svg { svg {

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