release: update 4.3.0
21
.dockerignore
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
node_modules
|
||||||
|
.DS_Store
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
*.local
|
||||||
|
.eslintcache
|
||||||
|
report.html
|
||||||
|
|
||||||
|
yarn.lock
|
||||||
|
npm-debug.log*
|
||||||
|
.pnpm-error.log*
|
||||||
|
.pnpm-debug.log
|
||||||
|
tests/**/coverage/
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.idea
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
tsconfig.tsbuildinfo
|
@ -1,6 +1,6 @@
|
|||||||
# 预发布也需要生产环境的行为
|
# 预发布也需要生产环境的行为
|
||||||
# https://cn.vitejs.dev/guide/env-and-mode.html#modes
|
# https://cn.vitejs.dev/guide/env-and-mode.html#modes
|
||||||
NODE_ENV=production
|
# NODE_ENV = development
|
||||||
|
|
||||||
VITE_PUBLIC_PATH = /
|
VITE_PUBLIC_PATH = /
|
||||||
|
|
||||||
|
1
.gitignore
vendored
@ -4,6 +4,7 @@ dist
|
|||||||
dist-ssr
|
dist-ssr
|
||||||
*.local
|
*.local
|
||||||
.eslintcache
|
.eslintcache
|
||||||
|
.stylelintcache
|
||||||
report.html
|
report.html
|
||||||
|
|
||||||
yarn.lock
|
yarn.lock
|
||||||
|
12
.vscode/vue3.0.code-snippets
vendored
@ -1,19 +1,19 @@
|
|||||||
{
|
{
|
||||||
"Vue3.0快速生成模板": {
|
"Vue3.0快速生成模板": {
|
||||||
|
"scope": "vue",
|
||||||
"prefix": "Vue3.0",
|
"prefix": "Vue3.0",
|
||||||
"body": [
|
"body": [
|
||||||
"<template>",
|
"<template>",
|
||||||
"\t<div>\n",
|
"\t<div>test</div>",
|
||||||
"\t</div>",
|
|
||||||
"</template>\n",
|
"</template>\n",
|
||||||
"<script lang='ts'>",
|
"<script lang='ts'>",
|
||||||
"export default {",
|
"export default {",
|
||||||
"\tsetup(){",
|
"\tsetup() {",
|
||||||
"\t\treturn{\n\n\t\t}",
|
"\t\treturn {}",
|
||||||
"\t},",
|
"\t}",
|
||||||
"}",
|
"}",
|
||||||
"</script>\n",
|
"</script>\n",
|
||||||
"<style scoped>\n",
|
"<style lang='scss' scoped>\n",
|
||||||
"</style>",
|
"</style>",
|
||||||
"$2"
|
"$2"
|
||||||
],
|
],
|
||||||
|
6
.vscode/vue3.2.code-snippets
vendored
@ -1,14 +1,14 @@
|
|||||||
{
|
{
|
||||||
"Vue3.2+快速生成模板": {
|
"Vue3.2+快速生成模板": {
|
||||||
|
"scope": "vue",
|
||||||
"prefix": "Vue3.2+",
|
"prefix": "Vue3.2+",
|
||||||
"body": [
|
"body": [
|
||||||
"<script setup lang='ts'>",
|
"<script setup lang='ts'>",
|
||||||
"</script>\n",
|
"</script>\n",
|
||||||
"<template>",
|
"<template>",
|
||||||
"\t<div>\n",
|
"\t<div>test</div>",
|
||||||
"\t</div>",
|
|
||||||
"</template>\n",
|
"</template>\n",
|
||||||
"<style scoped>\n",
|
"<style lang='scss' scoped>\n",
|
||||||
"</style>",
|
"</style>",
|
||||||
"$2"
|
"$2"
|
||||||
],
|
],
|
||||||
|
20
.vscode/vue3.3.code-snippets
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"Vue3.3+defineOptions快速生成模板": {
|
||||||
|
"scope": "vue",
|
||||||
|
"prefix": "Vue3.3+",
|
||||||
|
"body": [
|
||||||
|
"<script setup lang='ts'>",
|
||||||
|
"defineOptions({",
|
||||||
|
"\tname: ''",
|
||||||
|
"})",
|
||||||
|
"</script>\n",
|
||||||
|
"<template>",
|
||||||
|
"\t<div>test</div>",
|
||||||
|
"</template>\n",
|
||||||
|
"<style lang='scss' scoped>\n",
|
||||||
|
"</style>",
|
||||||
|
"$2"
|
||||||
|
],
|
||||||
|
"description": "Vue3.3+defineOptions快速生成模板"
|
||||||
|
}
|
||||||
|
}
|
20
Dockerfile
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
FROM node:16-alpine as build-stage
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
RUN corepack enable
|
||||||
|
RUN corepack prepare pnpm@7.32.1 --activate
|
||||||
|
|
||||||
|
RUN npm config set registry https://registry.npmmirror.com
|
||||||
|
|
||||||
|
COPY .npmrc package.json pnpm-lock.yaml ./
|
||||||
|
RUN pnpm install --frozen-lockfile
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
RUN pnpm build
|
||||||
|
|
||||||
|
FROM nginx:stable-alpine as production-stage
|
||||||
|
|
||||||
|
COPY --from=build-stage /app/dist /usr/share/nginx/html
|
||||||
|
EXPOSE 80
|
||||||
|
|
||||||
|
CMD ["nginx", "-g", "daemon off;"]
|
@ -15,39 +15,22 @@ The simplified version is based on the shelf extracted from [vue-pure-admin](htt
|
|||||||
|
|
||||||
## Docs
|
## Docs
|
||||||
|
|
||||||
- [Click me to view the domestic documentation site](https://yiming_chang.gitee.io/pure-admin-doc)
|
- [documentation site](https://yiming_chang.gitee.io/pure-admin-doc)
|
||||||
- [Click me to view foreign document site](https://pure-admin.github.io/pure-admin-doc)
|
|
||||||
|
|
||||||
## Preview
|
## Preview
|
||||||
|
|
||||||
- [Click me to view the preview station](https://pure-admin-thin.netlify.app/#/login)
|
- [Click me to view the preview station](https://pure-admin-thin.netlify.app/#/login)
|
||||||
|
|
||||||
## Usage
|
## Maintainer
|
||||||
|
|
||||||
### Installation dependencies
|
[xiaoxian521](https://github.com/xiaoxian521)
|
||||||
|
|
||||||
pnpm install
|
|
||||||
|
|
||||||
### Install a package
|
|
||||||
|
|
||||||
pnpm add packageName
|
|
||||||
|
|
||||||
### Uninstall a package
|
|
||||||
|
|
||||||
pnpm remove packageName
|
|
||||||
|
|
||||||
I think you should fork the project first to develop, so that you can pull the update synchronously when I update! ! !
|
|
||||||
|
|
||||||
## Supporting video tutorial
|
|
||||||
|
|
||||||
bilibili: https://www.bilibili.com/video/BV1534y1S7HV/
|
|
||||||
|
|
||||||
## ⚠️ Attention
|
## ⚠️ Attention
|
||||||
|
|
||||||
- The Lite version does not accept any issues and prs. If you have any questions, please go to the full version https://github.com/pure-admin/vue-pure-admin/issues/new/choose to mention, thank you! ! !
|
- The Lite version does not accept any issues and prs. If you have any questions, please go to the full version [issues](https://github.com/pure-admin/vue-pure-admin/issues/new/choose) to mention, thank you!
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
In principle, no fees and copyrights are charged, and you can use it with confidence, but if you need secondary open source, please contact the author for permission!
|
In principle, no fees and copyrights are charged, and it is commercially available, but if you need secondary open source (such as using this platform for secondary development and open source, the front-end code must be open source and free), please contact the author for permission! (Free, just take a record)
|
||||||
|
|
||||||
[MIT © 2020-present, pure-admin](./LICENSE)
|
[MIT © 2020-present, pure-admin](./LICENSE)
|
||||||
|
39
README.md
@ -10,58 +10,31 @@
|
|||||||
|
|
||||||
## 版本选择
|
## 版本选择
|
||||||
|
|
||||||
当前是非国际化版本哦,如果您需要国际化版本 [请点击](https://github.com/pure-admin/pure-admin-thin/tree/i18n)
|
当前是非国际化版本,如果您需要国际化版本 [请点击](https://github.com/pure-admin/pure-admin-thin/tree/i18n)
|
||||||
|
|
||||||
## 配套视频
|
## 配套视频
|
||||||
|
|
||||||
- [点我查看教程](https://www.bilibili.com/video/BV1kg411v7QT)
|
- [点我查看教程](https://www.bilibili.com/video/BV1kg411v7QT)
|
||||||
- [点我查看 UI 设计](https://www.bilibili.com/video/BV17g411T7rq)
|
- [点我查看 UI 设计](https://www.bilibili.com/video/BV17g411T7rq)
|
||||||
|
|
||||||
## 配套文档
|
## 配套保姆级文档
|
||||||
|
|
||||||
- [点我查看国内文档站](https://yiming_chang.gitee.io/pure-admin-doc)
|
- [查看文档](https://yiming_chang.gitee.io/pure-admin-doc)
|
||||||
- [点我查看国外文档站](https://pure-admin.github.io/pure-admin-doc)
|
|
||||||
|
|
||||||
## 预览
|
## 预览
|
||||||
|
|
||||||
- [点我查看预览站](https://pure-admin-thin.netlify.app/#/login)
|
- [查看预览](https://pure-admin-thin.netlify.app/#/login)
|
||||||
|
|
||||||
## 维护者
|
## 维护者
|
||||||
|
|
||||||
[xiaoxian521](https://github.com/xiaoxian521)
|
[xiaoxian521](https://github.com/xiaoxian521)
|
||||||
|
|
||||||
## 支持
|
|
||||||
|
|
||||||
如果你觉得这个项目对您有帮助,可以帮作者买一杯果汁 🍹 表示支持
|
|
||||||
|
|
||||||
<img src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f69bf13c5b854ed5b699807cafa0e3ce~tplv-k3u1fbpfcp-zoom-in-crop-mark:1304:0:0:0.awebp?" width="150px" height="150px" />
|
|
||||||
|
|
||||||
## `QQ` 交流群
|
|
||||||
|
|
||||||
[点击去加入](https://yiming_chang.gitee.io/pure-admin-doc/pages/support/#qq-%E4%BA%A4%E6%B5%81%E7%BE%A4)
|
|
||||||
|
|
||||||
## 用法
|
|
||||||
|
|
||||||
### 安装依赖
|
|
||||||
|
|
||||||
pnpm install
|
|
||||||
|
|
||||||
### 安装一个包
|
|
||||||
|
|
||||||
pnpm add 包名
|
|
||||||
|
|
||||||
### 卸载一个包
|
|
||||||
|
|
||||||
pnpm remove 包名
|
|
||||||
|
|
||||||
我认为你应该先 `fork` 项目去开发,以便我更新时您可以同步拉取更新!!!
|
|
||||||
|
|
||||||
## ⚠️ 注意
|
## ⚠️ 注意
|
||||||
|
|
||||||
- 精简版不接受任何 `issues` 和 `pr`,如果有问题请到完整版 [issues](https://github.com/pure-admin/vue-pure-admin/issues/new/choose) 去提,谢谢!!!
|
- 精简版不接受任何 `issues` 和 `pr`,如果有问题请到完整版 [issues](https://github.com/pure-admin/vue-pure-admin/issues/new/choose) 去提,谢谢!
|
||||||
|
|
||||||
## 许可证
|
## 许可证
|
||||||
|
|
||||||
原则上不收取任何费用及版权,可以放心使用,不过如需二次开源(比如用此平台二次开发并开源)请联系作者获取许可!
|
原则上不收取任何费用及版权,可商用,不过如需二次开源(比如用此平台二次开发并开源,要求前端代码必须开源免费)请联系作者获取许可!(免费,走个记录而已)
|
||||||
|
|
||||||
[MIT © 2020-present, pure-admin](./LICENSE)
|
[MIT © 2020-present, pure-admin](./LICENSE)
|
||||||
|
67
package.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "pure-admin-thin",
|
"name": "pure-admin-thin",
|
||||||
"version": "4.1.0",
|
"version": "4.3.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "NODE_OPTIONS=--max-old-space-size=4096 vite",
|
"dev": "NODE_OPTIONS=--max-old-space-size=4096 vite",
|
||||||
@ -13,10 +13,10 @@
|
|||||||
"typecheck": "tsc --noEmit && vue-tsc --noEmit --skipLibCheck",
|
"typecheck": "tsc --noEmit && vue-tsc --noEmit --skipLibCheck",
|
||||||
"svgo": "svgo -f src/assets/svg -o src/assets/svg",
|
"svgo": "svgo -f src/assets/svg -o src/assets/svg",
|
||||||
"cloc": "NODE_OPTIONS=--max-old-space-size=4096 cloc . --exclude-dir=node_modules --exclude-lang=YAML",
|
"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": "rimraf node_modules && rimraf .eslintcache && pnpm install",
|
||||||
"lint:eslint": "eslint --cache --max-warnings 0 \"{src,mock,build}/**/*.{vue,js,ts,tsx}\" --fix",
|
"lint:eslint": "eslint --cache --max-warnings 0 \"{src,mock,build}/**/*.{vue,js,ts,tsx}\" --fix",
|
||||||
"lint:prettier": "prettier --write \"src/**/*.{js,ts,json,tsx,css,scss,vue,html,md}\"",
|
"lint:prettier": "prettier --write \"src/**/*.{js,ts,json,tsx,css,scss,vue,html,md}\"",
|
||||||
"lint:stylelint": "stylelint --cache --fix \"**/*.{html,vue,css,scss}\" --cache --cache-location node_modules/.cache/stylelint/",
|
"lint:stylelint": "stylelint \"**/*.{html,vue,css,scss}\" --fix --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",
|
||||||
"lint": "pnpm lint:eslint && pnpm lint:prettier && pnpm lint:stylelint",
|
"lint": "pnpm lint:eslint && pnpm lint:prettier && pnpm lint:stylelint",
|
||||||
@ -30,69 +30,69 @@
|
|||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@pureadmin/descriptions": "^1.1.1",
|
"@pureadmin/descriptions": "^1.1.1",
|
||||||
"@pureadmin/table": "^2.1.0",
|
"@pureadmin/table": "^2.2.0",
|
||||||
"@pureadmin/utils": "^1.8.9",
|
"@pureadmin/utils": "^1.9.3",
|
||||||
"@vueuse/core": "^10.1.2",
|
"@vueuse/core": "^10.1.2",
|
||||||
"@vueuse/motion": "2.0.0-beta.12",
|
"@vueuse/motion": "^2.0.0",
|
||||||
"animate.css": "^4.1.1",
|
"animate.css": "^4.1.1",
|
||||||
"axios": "^1.4.0",
|
"axios": "^1.4.0",
|
||||||
"dayjs": "^1.11.7",
|
"dayjs": "^1.11.8",
|
||||||
"echarts": "^5.4.2",
|
"echarts": "^5.4.2",
|
||||||
"element-plus": "^2.3.4",
|
"element-plus": "^2.3.6",
|
||||||
"element-resize-detector": "^1.2.4",
|
"element-resize-detector": "^1.2.4",
|
||||||
"js-cookie": "^3.0.5",
|
"js-cookie": "^3.0.5",
|
||||||
"mitt": "^3.0.0",
|
"mitt": "^3.0.0",
|
||||||
"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.36",
|
"pinia": "^2.1.3",
|
||||||
"qs": "^6.11.1",
|
"qs": "^6.11.2",
|
||||||
"responsive-storage": "^2.2.0",
|
"responsive-storage": "^2.2.0",
|
||||||
"sortablejs": "^1.15.0",
|
"sortablejs": "^1.15.0",
|
||||||
"vue": "^3.3.1",
|
"vue": "^3.3.4",
|
||||||
"vue-router": "^4.1.6",
|
"vue-router": "^4.2.2",
|
||||||
"vue-types": "^5.0.2"
|
"vue-types": "^5.0.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@commitlint/cli": "^17.6.3",
|
"@commitlint/cli": "^17.6.5",
|
||||||
"@commitlint/config-conventional": "^17.6.3",
|
"@commitlint/config-conventional": "^17.6.5",
|
||||||
"@iconify-icons/ep": "^1.2.11",
|
"@iconify-icons/ep": "^1.2.11",
|
||||||
"@iconify-icons/ri": "^1.2.7",
|
"@iconify-icons/ri": "^1.2.8",
|
||||||
"@iconify/vue": "^4.1.1",
|
"@iconify/vue": "^4.1.1",
|
||||||
"@pureadmin/theme": "^3.0.0",
|
"@pureadmin/theme": "^3.0.0",
|
||||||
"@types/element-resize-detector": "1.1.3",
|
"@types/element-resize-detector": "1.1.3",
|
||||||
"@types/js-cookie": "^3.0.3",
|
"@types/js-cookie": "^3.0.3",
|
||||||
"@types/mockjs": "^1.0.7",
|
"@types/mockjs": "^1.0.7",
|
||||||
"@types/node": "^18.15.12",
|
"@types/node": "^20.2.5",
|
||||||
"@types/nprogress": "0.2.0",
|
"@types/nprogress": "0.2.0",
|
||||||
"@types/qs": "^6.9.7",
|
"@types/qs": "^6.9.7",
|
||||||
"@types/sortablejs": "^1.15.1",
|
"@types/sortablejs": "^1.15.1",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.59.5",
|
"@typescript-eslint/eslint-plugin": "^5.59.8",
|
||||||
"@typescript-eslint/parser": "^5.59.5",
|
"@typescript-eslint/parser": "^5.59.8",
|
||||||
"@vitejs/plugin-vue": "^4.2.2",
|
"@vitejs/plugin-vue": "^4.2.3",
|
||||||
"@vitejs/plugin-vue-jsx": "^3.0.1",
|
"@vitejs/plugin-vue-jsx": "^3.0.1",
|
||||||
"@vue/eslint-config-prettier": "^7.1.0",
|
"@vue/eslint-config-prettier": "^7.1.0",
|
||||||
"@vue/eslint-config-typescript": "^11.0.3",
|
"@vue/eslint-config-typescript": "^11.0.3",
|
||||||
"autoprefixer": "^10.4.14",
|
"autoprefixer": "^10.4.14",
|
||||||
"cloc": "^2.11.0",
|
"cloc": "^2.11.0",
|
||||||
"cssnano": "^6.0.1",
|
"cssnano": "^6.0.1",
|
||||||
"eslint": "^8.40.0",
|
"eslint": "^8.42.0",
|
||||||
"eslint-plugin-prettier": "^4.2.1",
|
"eslint-plugin-prettier": "^4.2.1",
|
||||||
"eslint-plugin-vue": "^9.12.0",
|
"eslint-plugin-vue": "^9.14.1",
|
||||||
"husky": "^8.0.3",
|
"husky": "^8.0.3",
|
||||||
"lint-staged": "^13.2.2",
|
"lint-staged": "^13.2.2",
|
||||||
"picocolors": "^1.0.0",
|
"picocolors": "^1.0.0",
|
||||||
"postcss": "^8.4.23",
|
"postcss": "^8.4.24",
|
||||||
"postcss-html": "^1.5.0",
|
"postcss-html": "^1.5.0",
|
||||||
"postcss-import": "^15.1.0",
|
"postcss-import": "^15.1.0",
|
||||||
"postcss-scss": "^4.0.6",
|
"postcss-scss": "^4.0.6",
|
||||||
"prettier": "^2.8.7",
|
"prettier": "^2.8.8",
|
||||||
"pretty-quick": "3.1.1",
|
"pretty-quick": "^3.1.3",
|
||||||
"rimraf": "^5.0.0",
|
"rimraf": "^5.0.1",
|
||||||
"rollup-plugin-visualizer": "^5.9.0",
|
"rollup-plugin-visualizer": "^5.9.0",
|
||||||
"sass": "^1.62.1",
|
"sass": "^1.62.1",
|
||||||
"sass-loader": "^13.2.2",
|
"sass-loader": "^13.3.1",
|
||||||
"stylelint": "^15.6.1",
|
"stylelint": "^15.6.3",
|
||||||
"stylelint-config-html": "^1.1.0",
|
"stylelint-config-html": "^1.1.0",
|
||||||
"stylelint-config-recess-order": "^4.0.0",
|
"stylelint-config-recess-order": "^4.0.0",
|
||||||
"stylelint-config-recommended": "^12.0.0",
|
"stylelint-config-recommended": "^12.0.0",
|
||||||
@ -105,16 +105,16 @@
|
|||||||
"stylelint-scss": "^5.0.0",
|
"stylelint-scss": "^5.0.0",
|
||||||
"svgo": "^3.0.2",
|
"svgo": "^3.0.2",
|
||||||
"tailwindcss": "^3.3.2",
|
"tailwindcss": "^3.3.2",
|
||||||
"terser": "^5.17.1",
|
"terser": "^5.17.7",
|
||||||
"typescript": "^5.0.4",
|
"typescript": "5.0.4",
|
||||||
"vite": "^4.3.5",
|
"vite": "^4.3.9",
|
||||||
"vite-plugin-cdn-import": "^0.3.5",
|
"vite-plugin-cdn-import": "^0.3.5",
|
||||||
"vite-plugin-compression": "^0.5.1",
|
"vite-plugin-compression": "^0.5.1",
|
||||||
"vite-plugin-mock": "^2.9.6",
|
"vite-plugin-mock": "^2.9.6",
|
||||||
"vite-plugin-remove-console": "^2.1.1",
|
"vite-plugin-remove-console": "^2.1.1",
|
||||||
"vite-svg-loader": "^4.0.0",
|
"vite-svg-loader": "^4.0.0",
|
||||||
"vue-eslint-parser": "^9.2.1",
|
"vue-eslint-parser": "^9.3.0",
|
||||||
"vue-tsc": "^1.6.4"
|
"vue-tsc": "^1.6.5"
|
||||||
},
|
},
|
||||||
"pnpm": {
|
"pnpm": {
|
||||||
"peerDependencyRules": {
|
"peerDependencyRules": {
|
||||||
@ -126,6 +126,7 @@
|
|||||||
},
|
},
|
||||||
"allowedDeprecatedVersions": {
|
"allowedDeprecatedVersions": {
|
||||||
"sourcemap-codec": "*",
|
"sourcemap-codec": "*",
|
||||||
|
"w3c-hr-time": "*",
|
||||||
"stable": "*"
|
"stable": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
2660
pnpm-lock.yaml
generated
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"Version": "4.1.0",
|
"Version": "4.3.0",
|
||||||
"Title": "PureAdmin",
|
"Title": "PureAdmin",
|
||||||
"FixedHeader": true,
|
"FixedHeader": true,
|
||||||
"HiddenSideBar": false,
|
"HiddenSideBar": false,
|
||||||
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
BIN
src/assets/user.jpg
Normal file
After Width: | Height: | Size: 3.6 KiB |
@ -12,6 +12,7 @@ import type {
|
|||||||
|
|
||||||
const dialogStore = ref<Array<DialogOptions>>([]);
|
const dialogStore = ref<Array<DialogOptions>>([]);
|
||||||
|
|
||||||
|
/** 打开弹框 */
|
||||||
const addDialog = (options: DialogOptions) => {
|
const addDialog = (options: DialogOptions) => {
|
||||||
const open = () =>
|
const open = () =>
|
||||||
dialogStore.value.push(Object.assign(options, { visible: true }));
|
dialogStore.value.push(Object.assign(options, { visible: true }));
|
||||||
@ -24,16 +25,40 @@ const addDialog = (options: DialogOptions) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** 关闭弹框 */
|
||||||
const closeDialog = (options: DialogOptions, index: number, args?: any) => {
|
const closeDialog = (options: DialogOptions, index: number, args?: any) => {
|
||||||
dialogStore.value.splice(index, 1);
|
dialogStore.value.splice(index, 1);
|
||||||
options.closeCallBack && options.closeCallBack({ options, index, args });
|
options.closeCallBack && options.closeCallBack({ options, index, args });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 更改弹框自身属性值
|
||||||
|
* @param value 属性值
|
||||||
|
* @param key 属性,默认`title`
|
||||||
|
* @param index 弹框索引(默认`0`,代表只有一个弹框,对于嵌套弹框要改哪个弹框的属性值就把该弹框索引赋给`index`)
|
||||||
|
*/
|
||||||
|
const updateDialog = (value: any, key = "title", index = 0) => {
|
||||||
|
dialogStore.value[index][key] = value;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 关闭所有弹框 */
|
||||||
const closeAllDialog = () => {
|
const closeAllDialog = () => {
|
||||||
dialogStore.value = [];
|
dialogStore.value = [];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** 千万别忘了在下面这三处引入并注册下,放心注册,不使用`addDialog`调用就不会被挂载
|
||||||
|
* https://github.com/pure-admin/vue-pure-admin/blob/main/src/App.vue#L4
|
||||||
|
* https://github.com/pure-admin/vue-pure-admin/blob/main/src/App.vue#L13
|
||||||
|
* https://github.com/pure-admin/vue-pure-admin/blob/main/src/App.vue#L18
|
||||||
|
*/
|
||||||
const ReDialog = withInstall(reDialog);
|
const ReDialog = withInstall(reDialog);
|
||||||
|
|
||||||
export type { EventType, ArgsType, DialogProps, ButtonProps, DialogOptions };
|
export type { EventType, ArgsType, DialogProps, ButtonProps, DialogOptions };
|
||||||
export { ReDialog, dialogStore, addDialog, closeDialog, closeAllDialog };
|
export {
|
||||||
|
ReDialog,
|
||||||
|
dialogStore,
|
||||||
|
addDialog,
|
||||||
|
closeDialog,
|
||||||
|
updateDialog,
|
||||||
|
closeAllDialog
|
||||||
|
};
|
||||||
|
@ -1,13 +1,17 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from "vue";
|
|
||||||
import { isFunction } from "@pureadmin/utils";
|
|
||||||
import {
|
import {
|
||||||
type DialogOptions,
|
closeDialog,
|
||||||
type ButtonProps,
|
|
||||||
type EventType,
|
|
||||||
dialogStore,
|
dialogStore,
|
||||||
closeDialog
|
type EventType,
|
||||||
|
type ButtonProps,
|
||||||
|
type DialogOptions
|
||||||
} from "./index";
|
} from "./index";
|
||||||
|
import { ref, computed } from "vue";
|
||||||
|
import { isFunction } from "@pureadmin/utils";
|
||||||
|
import Fullscreen from "@iconify-icons/ri/fullscreen-fill";
|
||||||
|
import ExitFullscreen from "@iconify-icons/ri/fullscreen-exit-fill";
|
||||||
|
|
||||||
|
const fullscreen = ref(false);
|
||||||
|
|
||||||
const footerButtons = computed(() => {
|
const footerButtons = computed(() => {
|
||||||
return (options: DialogOptions) => {
|
return (options: DialogOptions) => {
|
||||||
@ -47,11 +51,22 @@ const footerButtons = computed(() => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const fullscreenClass = computed(() => {
|
||||||
|
return [
|
||||||
|
"el-icon",
|
||||||
|
"el-dialog__close",
|
||||||
|
"-translate-x-2",
|
||||||
|
"cursor-pointer",
|
||||||
|
"hover:!text-[red]"
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
function eventsCallBack(
|
function eventsCallBack(
|
||||||
event: EventType,
|
event: EventType,
|
||||||
options: DialogOptions,
|
options: DialogOptions,
|
||||||
index: number
|
index: number
|
||||||
) {
|
) {
|
||||||
|
fullscreen.value = options?.fullscreen ?? false;
|
||||||
if (options?.[event] && isFunction(options?.[event])) {
|
if (options?.[event] && isFunction(options?.[event])) {
|
||||||
return options?.[event]({ options, index });
|
return options?.[event]({ options, index });
|
||||||
}
|
}
|
||||||
@ -69,25 +84,49 @@ function handleClose(
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<el-dialog
|
<el-dialog
|
||||||
|
class="pure-dialog"
|
||||||
v-for="(options, index) in dialogStore"
|
v-for="(options, index) in dialogStore"
|
||||||
:key="index"
|
:key="index"
|
||||||
v-bind="options"
|
v-bind="options"
|
||||||
v-model="options.visible"
|
v-model="options.visible"
|
||||||
@opened="eventsCallBack('open', options, index)"
|
:fullscreen="fullscreen ? true : options?.fullscreen ? true : false"
|
||||||
@close="handleClose(options, index)"
|
@close="handleClose(options, index)"
|
||||||
|
@opened="eventsCallBack('open', options, index)"
|
||||||
@openAutoFocus="eventsCallBack('openAutoFocus', options, index)"
|
@openAutoFocus="eventsCallBack('openAutoFocus', options, index)"
|
||||||
@closeAutoFocus="eventsCallBack('closeAutoFocus', options, index)"
|
@closeAutoFocus="eventsCallBack('closeAutoFocus', options, index)"
|
||||||
>
|
>
|
||||||
<!-- header -->
|
<!-- header -->
|
||||||
<template
|
<template
|
||||||
v-if="options?.headerRenderer"
|
v-if="options?.fullscreenIcon || options?.headerRenderer"
|
||||||
#header="{ close, titleId, titleClass }"
|
#header="{ close, titleId, titleClass }"
|
||||||
>
|
>
|
||||||
|
<div
|
||||||
|
v-if="options?.fullscreenIcon"
|
||||||
|
class="flex items-center justify-between"
|
||||||
|
>
|
||||||
|
<span :id="titleId" :class="titleClass">{{ options?.title }}</span>
|
||||||
|
<i
|
||||||
|
v-if="!options?.fullscreen"
|
||||||
|
:class="fullscreenClass"
|
||||||
|
@click="fullscreen = !fullscreen"
|
||||||
|
>
|
||||||
|
<IconifyIconOffline
|
||||||
|
class="pure-dialog-svg"
|
||||||
|
:icon="
|
||||||
|
options?.fullscreen
|
||||||
|
? ExitFullscreen
|
||||||
|
: fullscreen
|
||||||
|
? ExitFullscreen
|
||||||
|
: Fullscreen
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
<component
|
<component
|
||||||
|
v-else
|
||||||
:is="options?.headerRenderer({ close, titleId, titleClass })"
|
:is="options?.headerRenderer({ close, titleId, titleClass })"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<!-- default -->
|
|
||||||
<component
|
<component
|
||||||
v-bind="options?.props"
|
v-bind="options?.props"
|
||||||
:is="options.contentRenderer({ options, index })"
|
:is="options.contentRenderer({ options, index })"
|
||||||
|
@ -15,8 +15,10 @@ type DialogProps = {
|
|||||||
title?: string;
|
title?: string;
|
||||||
/** `Dialog` 的宽度,默认 `50%` */
|
/** `Dialog` 的宽度,默认 `50%` */
|
||||||
width?: string | number;
|
width?: string | number;
|
||||||
/** 是否为全屏 `Dialog`,默认 `false` */
|
/** 是否为全屏 `Dialog`(会一直处于全屏状态,除非弹框关闭),默认 `false`,`fullscreen` 和 `fullscreenIcon` 都传时只有 `fullscreen` 会生效 */
|
||||||
fullscreen?: boolean;
|
fullscreen?: boolean;
|
||||||
|
/** 是否显示全屏操作图标,默认 `false`,`fullscreen` 和 `fullscreenIcon` 都传时只有 `fullscreen` 会生效 */
|
||||||
|
fullscreenIcon?: boolean;
|
||||||
/** `Dialog CSS` 中的 `margin-top` 值,默认 `15vh` */
|
/** `Dialog CSS` 中的 `margin-top` 值,默认 `15vh` */
|
||||||
top?: string;
|
top?: string;
|
||||||
/** 是否需要遮罩层,默认 `true` */
|
/** 是否需要遮罩层,默认 `true` */
|
||||||
|
@ -200,9 +200,13 @@ export default defineComponent({
|
|||||||
|
|
||||||
return () => (
|
return () => (
|
||||||
<>
|
<>
|
||||||
<div {...attrs} class="w-[99/100] mt-6 p-2 bg-bg_color">
|
<div {...attrs} class="w-[99/100] mt-2 px-2 pb-2 bg-bg_color">
|
||||||
<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 truncate">{props.title}</p>
|
{slots?.title ? (
|
||||||
|
slots.title()
|
||||||
|
) : (
|
||||||
|
<p class="font-bold truncate">{props.title}</p>
|
||||||
|
)}
|
||||||
<div class="flex items-center justify-around">
|
<div class="flex items-center justify-around">
|
||||||
{slots?.buttons ? (
|
{slots?.buttons ? (
|
||||||
<div class="flex mr-4">{slots.buttons()}</div>
|
<div class="flex mr-4">{slots.buttons()}</div>
|
||||||
@ -245,6 +249,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
<el-popover
|
<el-popover
|
||||||
v-slots={reference}
|
v-slots={reference}
|
||||||
|
placement="bottom-start"
|
||||||
popper-style={{ padding: 0 }}
|
popper-style={{ padding: 0 }}
|
||||||
width="160"
|
width="160"
|
||||||
trigger="click"
|
trigger="click"
|
||||||
|
@ -15,6 +15,7 @@ const {
|
|||||||
onPanel,
|
onPanel,
|
||||||
pureApp,
|
pureApp,
|
||||||
username,
|
username,
|
||||||
|
userAvatar,
|
||||||
avatarsStyle,
|
avatarsStyle,
|
||||||
toggleSideBar
|
toggleSideBar
|
||||||
} = useNav();
|
} = useNav();
|
||||||
@ -46,10 +47,7 @@ const {
|
|||||||
<!-- 退出登录 -->
|
<!-- 退出登录 -->
|
||||||
<el-dropdown trigger="click">
|
<el-dropdown trigger="click">
|
||||||
<span class="el-dropdown-link navbar-bg-hover select-none">
|
<span class="el-dropdown-link navbar-bg-hover select-none">
|
||||||
<img
|
<img :src="userAvatar" :style="avatarsStyle" />
|
||||||
src="https://avatars.githubusercontent.com/u/44761321?v=4"
|
|
||||||
:style="avatarsStyle"
|
|
||||||
/>
|
|
||||||
<p v-if="username" class="dark:text-white">{{ username }}</p>
|
<p v-if="username" class="dark:text-white">{{ username }}</p>
|
||||||
</span>
|
</span>
|
||||||
<template #dropdown>
|
<template #dropdown>
|
||||||
|
@ -46,8 +46,9 @@ notices.value.map(v => (noticesNum.value += v.list.length));
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: 60px;
|
width: 40px;
|
||||||
height: 48px;
|
height: 48px;
|
||||||
|
margin-right: 10px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
.header-notice-icon {
|
.header-notice-icon {
|
||||||
|
@ -1,3 +1,17 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import ArrowUpLine from "@iconify-icons/ri/arrow-up-line";
|
||||||
|
import ArrowDownLine from "@iconify-icons/ri/arrow-down-line";
|
||||||
|
import { useNav } from "@/layout/hooks/useNav";
|
||||||
|
import mdiKeyboardEsc from "@/assets/svg/keyboard_esc.svg?component";
|
||||||
|
import enterOutlined from "@/assets/svg/enter_outlined.svg?component";
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<{ total: number }>(), {
|
||||||
|
total: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
const { device } = useNav();
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="search-footer text-[#333] dark:text-white">
|
<div class="search-footer text-[#333] dark:text-white">
|
||||||
<span class="search-footer-item">
|
<span class="search-footer-item">
|
||||||
@ -13,16 +27,15 @@
|
|||||||
<mdiKeyboardEsc class="icon" />
|
<mdiKeyboardEsc class="icon" />
|
||||||
关闭
|
关闭
|
||||||
</span>
|
</span>
|
||||||
|
<p
|
||||||
|
v-if="device !== 'mobile' && props.total > 0"
|
||||||
|
class="search-footer-total"
|
||||||
|
>
|
||||||
|
共{{ props.total }}项
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import ArrowUpLine from "@iconify-icons/ri/arrow-up-line";
|
|
||||||
import ArrowDownLine from "@iconify-icons/ri/arrow-down-line";
|
|
||||||
import mdiKeyboardEsc from "@/assets/svg/keyboard_esc.svg?component";
|
|
||||||
import enterOutlined from "@/assets/svg/enter_outlined.svg?component";
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.search-footer {
|
.search-footer {
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -40,5 +53,10 @@ import enterOutlined from "@/assets/svg/enter_outlined.svg?component";
|
|||||||
box-shadow: inset 0 -2px #cdcde6, inset 0 0 1px 1px #fff,
|
box-shadow: inset 0 -2px #cdcde6, inset 0 0 1px 1px #fff,
|
||||||
0 1px 2px 1px #1e235a66;
|
0 1px 2px 1px #1e235a66;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.search-footer-total {
|
||||||
|
position: absolute;
|
||||||
|
right: 20px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -7,7 +7,7 @@ import { useNav } from "@/layout/hooks/useNav";
|
|||||||
import { ref, computed, shallowRef } from "vue";
|
import { ref, computed, shallowRef } from "vue";
|
||||||
import { useDebounceFn, onKeyStroke } from "@vueuse/core";
|
import { useDebounceFn, onKeyStroke } from "@vueuse/core";
|
||||||
import { usePermissionStoreHook } from "@/store/modules/permission";
|
import { usePermissionStoreHook } from "@/store/modules/permission";
|
||||||
import Search from "@iconify-icons/ep/search";
|
import Search from "@iconify-icons/ri/search-line";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
/** 弹窗显隐 */
|
/** 弹窗显隐 */
|
||||||
@ -24,6 +24,8 @@ const props = withDefaults(defineProps<Props>(), {});
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const keyword = ref("");
|
const keyword = ref("");
|
||||||
|
const scrollbarRef = ref();
|
||||||
|
const resultRef = ref();
|
||||||
const activePath = ref("");
|
const activePath = ref("");
|
||||||
const inputRef = ref<HTMLInputElement | null>(null);
|
const inputRef = ref<HTMLInputElement | null>(null);
|
||||||
const resultOptions = shallowRef([]);
|
const resultOptions = shallowRef([]);
|
||||||
@ -82,6 +84,11 @@ function handleClose() {
|
|||||||
}, 200);
|
}, 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function scrollTo(index) {
|
||||||
|
const scrollTop = resultRef.value.handleScroll(index);
|
||||||
|
scrollbarRef.value.setScrollTop(scrollTop);
|
||||||
|
}
|
||||||
|
|
||||||
/** key up */
|
/** key up */
|
||||||
function handleUp() {
|
function handleUp() {
|
||||||
const { length } = resultOptions.value;
|
const { length } = resultOptions.value;
|
||||||
@ -91,8 +98,10 @@ function handleUp() {
|
|||||||
);
|
);
|
||||||
if (index === 0) {
|
if (index === 0) {
|
||||||
activePath.value = resultOptions.value[length - 1].path;
|
activePath.value = resultOptions.value[length - 1].path;
|
||||||
|
scrollTo(resultOptions.value.length - 1);
|
||||||
} else {
|
} else {
|
||||||
activePath.value = resultOptions.value[index - 1].path;
|
activePath.value = resultOptions.value[index - 1].path;
|
||||||
|
scrollTo(index - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,6 +117,7 @@ function handleDown() {
|
|||||||
} else {
|
} else {
|
||||||
activePath.value = resultOptions.value[index + 1].path;
|
activePath.value = resultOptions.value[index + 1].path;
|
||||||
}
|
}
|
||||||
|
scrollTo(index + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** key enter */
|
/** key enter */
|
||||||
@ -126,41 +136,55 @@ onKeyStroke("ArrowDown", handleDown);
|
|||||||
<template>
|
<template>
|
||||||
<el-dialog
|
<el-dialog
|
||||||
top="5vh"
|
top="5vh"
|
||||||
|
class="pure-search-dialog"
|
||||||
v-model="show"
|
v-model="show"
|
||||||
:width="device === 'mobile' ? '80vw' : '50vw'"
|
:show-close="false"
|
||||||
|
:width="device === 'mobile' ? '80vw' : '40vw'"
|
||||||
:before-close="handleClose"
|
:before-close="handleClose"
|
||||||
|
:style="{
|
||||||
|
borderRadius: '6px'
|
||||||
|
}"
|
||||||
@opened="inputRef.focus()"
|
@opened="inputRef.focus()"
|
||||||
@closed="inputRef.blur()"
|
@closed="inputRef.blur()"
|
||||||
>
|
>
|
||||||
<el-input
|
<el-input
|
||||||
ref="inputRef"
|
ref="inputRef"
|
||||||
|
size="large"
|
||||||
v-model="keyword"
|
v-model="keyword"
|
||||||
clearable
|
clearable
|
||||||
placeholder="请输入关键词搜索"
|
placeholder="搜索菜单"
|
||||||
@input="handleSearch"
|
@input="handleSearch"
|
||||||
>
|
>
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
<span class="el-input__icon">
|
<IconifyIconOffline
|
||||||
<IconifyIconOffline :icon="Search" />
|
:icon="Search"
|
||||||
</span>
|
class="text-primary w-[24px] h-[24px]"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
</el-input>
|
</el-input>
|
||||||
<div class="search-result-container">
|
<div class="search-result-container">
|
||||||
<el-empty v-if="resultOptions.length === 0" description="暂无搜索结果" />
|
<el-scrollbar ref="scrollbarRef" max-height="calc(90vh - 140px)">
|
||||||
<SearchResult
|
<el-empty
|
||||||
v-else
|
v-if="resultOptions.length === 0"
|
||||||
v-model:value="activePath"
|
description="暂无搜索结果"
|
||||||
:options="resultOptions"
|
/>
|
||||||
@click="handleEnter"
|
<SearchResult
|
||||||
/>
|
v-else
|
||||||
|
ref="resultRef"
|
||||||
|
v-model:value="activePath"
|
||||||
|
:options="resultOptions"
|
||||||
|
@click="handleEnter"
|
||||||
|
/>
|
||||||
|
</el-scrollbar>
|
||||||
</div>
|
</div>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<SearchFooter />
|
<SearchFooter :total="resultOptions.length" />
|
||||||
</template>
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.search-result-container {
|
.search-result-container {
|
||||||
margin-top: 20px;
|
margin-top: 12px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from "vue";
|
import { useResizeObserver } from "@vueuse/core";
|
||||||
import { useEpThemeStoreHook } from "@/store/modules/epTheme";
|
import { useEpThemeStoreHook } from "@/store/modules/epTheme";
|
||||||
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
||||||
|
import { ref, computed, getCurrentInstance, onMounted } from "vue";
|
||||||
import enterOutlined from "@/assets/svg/enter_outlined.svg?component";
|
import enterOutlined from "@/assets/svg/enter_outlined.svg?component";
|
||||||
import Bookmark2Line from "@iconify-icons/ri/bookmark-2-line";
|
import Bookmark2Line from "@iconify-icons/ri/bookmark-2-line";
|
||||||
|
|
||||||
@ -23,8 +24,11 @@ interface Emits {
|
|||||||
(e: "enter"): void;
|
(e: "enter"): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const resultRef = ref();
|
||||||
|
const innerHeight = ref();
|
||||||
const props = withDefaults(defineProps<Props>(), {});
|
const props = withDefaults(defineProps<Props>(), {});
|
||||||
const emit = defineEmits<Emits>();
|
const emit = defineEmits<Emits>();
|
||||||
|
const instance = getCurrentInstance()!;
|
||||||
|
|
||||||
const itemStyle = computed(() => {
|
const itemStyle = computed(() => {
|
||||||
return item => {
|
return item => {
|
||||||
@ -54,22 +58,46 @@ async function handleMouse(item) {
|
|||||||
function handleTo() {
|
function handleTo() {
|
||||||
emit("enter");
|
emit("enter");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function resizeResult() {
|
||||||
|
// el-scrollbar max-height="calc(90vh - 140px)"
|
||||||
|
innerHeight.value = window.innerHeight - window.innerHeight / 10 - 140;
|
||||||
|
}
|
||||||
|
|
||||||
|
useResizeObserver(resultRef, () => {
|
||||||
|
resizeResult();
|
||||||
|
});
|
||||||
|
|
||||||
|
function handleScroll(index: number) {
|
||||||
|
const curInstance = instance?.proxy?.$refs[`resultItemRef${index}`];
|
||||||
|
if (!curInstance) return 0;
|
||||||
|
const curRef = curInstance[0] as ElRef;
|
||||||
|
const scrollTop = curRef.offsetTop + 128; // 128 两个result-item(56px+56px=112px)高度加上下margin(8px+8px=16px)
|
||||||
|
return scrollTop > innerHeight.value ? scrollTop - innerHeight.value : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
resizeResult();
|
||||||
|
});
|
||||||
|
|
||||||
|
defineExpose({ handleScroll });
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="result">
|
<div ref="resultRef" class="result">
|
||||||
<template v-for="item in options" :key="item.path">
|
<div
|
||||||
<div
|
v-for="(item, index) in options"
|
||||||
class="result-item dark:bg-[#1d1d1d]"
|
:key="item.path"
|
||||||
:style="itemStyle(item)"
|
:ref="'resultItemRef' + index"
|
||||||
@click="handleTo"
|
class="result-item dark:bg-[#1d1d1d]"
|
||||||
@mouseenter="handleMouse(item)"
|
:style="itemStyle(item)"
|
||||||
>
|
@click="handleTo"
|
||||||
<component :is="useRenderIcon(item.meta?.icon ?? Bookmark2Line)" />
|
@mouseenter="handleMouse(item)"
|
||||||
<span class="result-item-title">{{ item.meta?.title }}</span>
|
>
|
||||||
<enterOutlined />
|
<component :is="useRenderIcon(item.meta?.icon ?? Bookmark2Line)" />
|
||||||
</div>
|
<span class="result-item-title">{{ item.meta?.title }}</span>
|
||||||
</template>
|
<enterOutlined />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -218,7 +218,6 @@ watch($storage, ({ layout }) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
onBeforeMount(() => {
|
onBeforeMount(() => {
|
||||||
dataThemeChange();
|
|
||||||
/* 初始化项目配置 */
|
/* 初始化项目配置 */
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
settings.greyVal &&
|
settings.greyVal &&
|
||||||
|
@ -19,6 +19,7 @@ const {
|
|||||||
onPanel,
|
onPanel,
|
||||||
menuSelect,
|
menuSelect,
|
||||||
username,
|
username,
|
||||||
|
userAvatar,
|
||||||
avatarsStyle
|
avatarsStyle
|
||||||
} = useNav();
|
} = useNav();
|
||||||
|
|
||||||
@ -66,10 +67,7 @@ watch(
|
|||||||
<!-- 退出登录 -->
|
<!-- 退出登录 -->
|
||||||
<el-dropdown trigger="click">
|
<el-dropdown trigger="click">
|
||||||
<span class="el-dropdown-link navbar-bg-hover">
|
<span class="el-dropdown-link navbar-bg-hover">
|
||||||
<img
|
<img :src="userAvatar" :style="avatarsStyle" />
|
||||||
src="https://avatars.githubusercontent.com/u/44761321?v=4"
|
|
||||||
:style="avatarsStyle"
|
|
||||||
/>
|
|
||||||
<p v-if="username" class="dark:text-white">{{ username }}</p>
|
<p v-if="username" class="dark:text-white">{{ username }}</p>
|
||||||
</span>
|
</span>
|
||||||
<template #dropdown>
|
<template #dropdown>
|
||||||
|
@ -7,7 +7,6 @@ const props = defineProps({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const { title } = useNav();
|
const { title } = useNav();
|
||||||
const topPath = getTopMenu().path;
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -18,7 +17,7 @@ const topPath = getTopMenu().path;
|
|||||||
key="props.collapse"
|
key="props.collapse"
|
||||||
:title="title"
|
:title="title"
|
||||||
class="sidebar-logo-link"
|
class="sidebar-logo-link"
|
||||||
:to="topPath"
|
:to="getTopMenu()?.path ?? '/'"
|
||||||
>
|
>
|
||||||
<img src="/logo.svg" alt="logo" />
|
<img src="/logo.svg" alt="logo" />
|
||||||
<span class="sidebar-title">{{ title }}</span>
|
<span class="sidebar-title">{{ title }}</span>
|
||||||
@ -28,7 +27,7 @@ const topPath = getTopMenu().path;
|
|||||||
key="expand"
|
key="expand"
|
||||||
:title="title"
|
:title="title"
|
||||||
class="sidebar-logo-link"
|
class="sidebar-logo-link"
|
||||||
:to="topPath"
|
:to="getTopMenu()?.path ?? '/'"
|
||||||
>
|
>
|
||||||
<img src="/logo.svg" alt="logo" />
|
<img src="/logo.svg" alt="logo" />
|
||||||
<span class="sidebar-title">{{ title }}</span>
|
<span class="sidebar-title">{{ title }}</span>
|
||||||
|
@ -22,6 +22,7 @@ const {
|
|||||||
menuSelect,
|
menuSelect,
|
||||||
resolvePath,
|
resolvePath,
|
||||||
username,
|
username,
|
||||||
|
userAvatar,
|
||||||
getDivStyle,
|
getDivStyle,
|
||||||
avatarsStyle
|
avatarsStyle
|
||||||
} = useNav();
|
} = useNav();
|
||||||
@ -97,10 +98,7 @@ watch(
|
|||||||
<!-- 退出登录 -->
|
<!-- 退出登录 -->
|
||||||
<el-dropdown trigger="click">
|
<el-dropdown trigger="click">
|
||||||
<span class="el-dropdown-link navbar-bg-hover select-none">
|
<span class="el-dropdown-link navbar-bg-hover select-none">
|
||||||
<img
|
<img :src="userAvatar" :style="avatarsStyle" />
|
||||||
src="https://avatars.githubusercontent.com/u/44761321?v=4"
|
|
||||||
:style="avatarsStyle"
|
|
||||||
/>
|
|
||||||
<p v-if="username" class="dark:text-white">{{ username }}</p>
|
<p v-if="username" class="dark:text-white">{{ username }}</p>
|
||||||
</span>
|
</span>
|
||||||
<template #dropdown>
|
<template #dropdown>
|
||||||
|
@ -48,7 +48,7 @@ const tabDom = ref();
|
|||||||
const containerDom = ref();
|
const containerDom = ref();
|
||||||
const scrollbarDom = ref();
|
const scrollbarDom = ref();
|
||||||
const isShowArrow = ref(false);
|
const isShowArrow = ref(false);
|
||||||
const topPath = getTopMenu().path;
|
const topPath = getTopMenu()?.path;
|
||||||
const { VITE_HIDE_HOME } = import.meta.env;
|
const { VITE_HIDE_HOME } = import.meta.env;
|
||||||
const { isFullscreen, toggle } = useFullscreen();
|
const { isFullscreen, toggle } = useFullscreen();
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ import { storeToRefs } from "pinia";
|
|||||||
import { getConfig } from "@/config";
|
import { getConfig } from "@/config";
|
||||||
import { emitter } from "@/utils/mitt";
|
import { emitter } from "@/utils/mitt";
|
||||||
import { routeMetaType } from "../types";
|
import { routeMetaType } from "../types";
|
||||||
|
import userAvatar from "@/assets/user.jpg";
|
||||||
import { getTopMenu } from "@/router/utils";
|
import { getTopMenu } from "@/router/utils";
|
||||||
import { useGlobal } from "@pureadmin/utils";
|
import { useGlobal } from "@pureadmin/utils";
|
||||||
import { computed, CSSProperties } from "vue";
|
import { computed, CSSProperties } from "vue";
|
||||||
@ -70,7 +71,7 @@ export function useNav() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function backTopMenu() {
|
function backTopMenu() {
|
||||||
router.push(getTopMenu().path);
|
router.push(getTopMenu()?.path);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onPanel() {
|
function onPanel() {
|
||||||
@ -150,6 +151,7 @@ export function useNav() {
|
|||||||
isCollapse,
|
isCollapse,
|
||||||
pureApp,
|
pureApp,
|
||||||
username,
|
username,
|
||||||
|
userAvatar,
|
||||||
avatarsStyle,
|
avatarsStyle,
|
||||||
tooltipEffect
|
tooltipEffect
|
||||||
};
|
};
|
||||||
|
@ -8,7 +8,15 @@ import { useLayout } from "./hooks/useLayout";
|
|||||||
import { useAppStoreHook } from "@/store/modules/app";
|
import { useAppStoreHook } from "@/store/modules/app";
|
||||||
import { useSettingStoreHook } from "@/store/modules/settings";
|
import { useSettingStoreHook } from "@/store/modules/settings";
|
||||||
import { deviceDetection, useDark, useGlobal } from "@pureadmin/utils";
|
import { deviceDetection, useDark, useGlobal } from "@pureadmin/utils";
|
||||||
import { h, reactive, computed, onMounted, defineComponent } from "vue";
|
import { useDataThemeChange } from "@/layout/hooks/useDataThemeChange";
|
||||||
|
import {
|
||||||
|
h,
|
||||||
|
reactive,
|
||||||
|
computed,
|
||||||
|
onMounted,
|
||||||
|
onBeforeMount,
|
||||||
|
defineComponent
|
||||||
|
} from "vue";
|
||||||
|
|
||||||
import navbar from "./components/navbar.vue";
|
import navbar from "./components/navbar.vue";
|
||||||
import tag from "./components/tag/index.vue";
|
import tag from "./components/tag/index.vue";
|
||||||
@ -88,11 +96,12 @@ emitter.on("resize", ({ detail }) => {
|
|||||||
toggle("desktop", false);
|
toggle("desktop", false);
|
||||||
isAutoCloseSidebar = false;
|
isAutoCloseSidebar = false;
|
||||||
}
|
}
|
||||||
} else if (width > 990) {
|
} else if (width > 990 && !set.sidebar.isClickCollapse) {
|
||||||
if (!set.sidebar.isClickCollapse) {
|
toggle("desktop", true);
|
||||||
toggle("desktop", true);
|
isAutoCloseSidebar = true;
|
||||||
isAutoCloseSidebar = true;
|
} else {
|
||||||
}
|
toggle("desktop", false);
|
||||||
|
isAutoCloseSidebar = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -102,6 +111,10 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
onBeforeMount(() => {
|
||||||
|
useDataThemeChange().dataThemeChange();
|
||||||
|
});
|
||||||
|
|
||||||
const layoutHeader = defineComponent({
|
const layoutHeader = defineComponent({
|
||||||
render() {
|
render() {
|
||||||
return h(
|
return h(
|
||||||
|
@ -22,7 +22,7 @@ import {
|
|||||||
formatFlatteningRoutes
|
formatFlatteningRoutes
|
||||||
} from "./utils";
|
} from "./utils";
|
||||||
import { buildHierarchyTree } from "@/utils/tree";
|
import { buildHierarchyTree } from "@/utils/tree";
|
||||||
import { isUrl, openLink, storageSession } from "@pureadmin/utils";
|
import { isUrl, openLink, storageSession, isAllEmpty } from "@pureadmin/utils";
|
||||||
|
|
||||||
import remainingRouter from "./modules/remaining";
|
import remainingRouter from "./modules/remaining";
|
||||||
|
|
||||||
@ -46,13 +46,13 @@ Object.keys(modules).forEach(key => {
|
|||||||
|
|
||||||
/** 导出处理后的静态路由(三级及以上的路由全部拍成二级) */
|
/** 导出处理后的静态路由(三级及以上的路由全部拍成二级) */
|
||||||
export const constantRoutes: Array<RouteRecordRaw> = formatTwoStageRoutes(
|
export const constantRoutes: Array<RouteRecordRaw> = formatTwoStageRoutes(
|
||||||
formatFlatteningRoutes(buildHierarchyTree(ascending(routes)))
|
formatFlatteningRoutes(buildHierarchyTree(ascending(routes.flat(Infinity))))
|
||||||
);
|
);
|
||||||
|
|
||||||
/** 用于渲染菜单,保持原始层级 */
|
/** 用于渲染菜单,保持原始层级 */
|
||||||
export const constantMenus: Array<RouteComponent> = ascending(routes).concat(
|
export const constantMenus: Array<RouteComponent> = ascending(
|
||||||
...remainingRouter
|
routes.flat(Infinity)
|
||||||
);
|
).concat(...remainingRouter);
|
||||||
|
|
||||||
/** 不参与菜单的路由 */
|
/** 不参与菜单的路由 */
|
||||||
export const remainingPaths = Object.keys(remainingRouter).map(v => {
|
export const remainingPaths = Object.keys(remainingRouter).map(v => {
|
||||||
@ -86,7 +86,9 @@ export function resetRouter() {
|
|||||||
if (name && router.hasRoute(name) && meta?.backstage) {
|
if (name && router.hasRoute(name) && meta?.backstage) {
|
||||||
router.removeRoute(name);
|
router.removeRoute(name);
|
||||||
router.options.routes = formatTwoStageRoutes(
|
router.options.routes = formatTwoStageRoutes(
|
||||||
formatFlatteningRoutes(buildHierarchyTree(ascending(routes)))
|
formatFlatteningRoutes(
|
||||||
|
buildHierarchyTree(ascending(routes.flat(Infinity)))
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -154,11 +156,22 @@ router.beforeEach((to: toRouteType, _from, next) => {
|
|||||||
getTopMenu(true);
|
getTopMenu(true);
|
||||||
// query、params模式路由传参数的标签页不在此处处理
|
// query、params模式路由传参数的标签页不在此处处理
|
||||||
if (route && route.meta?.title) {
|
if (route && route.meta?.title) {
|
||||||
useMultiTagsStoreHook().handleTags("push", {
|
if (isAllEmpty(route.parentId) && route.meta?.backstage) {
|
||||||
path: route.path,
|
// 此处为动态顶级路由(目录)
|
||||||
name: route.name,
|
const { path, name, meta } = route.children[0];
|
||||||
meta: route.meta
|
useMultiTagsStoreHook().handleTags("push", {
|
||||||
});
|
path,
|
||||||
|
name,
|
||||||
|
meta
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const { path, name, meta } = route;
|
||||||
|
useMultiTagsStoreHook().handleTags("push", {
|
||||||
|
path,
|
||||||
|
name,
|
||||||
|
meta
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
router.push(to.fullPath);
|
router.push(to.fullPath);
|
||||||
|
@ -79,6 +79,10 @@ html.dark {
|
|||||||
&:hover {
|
&:hover {
|
||||||
color: rgb(255 255 255 / 85%) !important;
|
color: rgb(255 255 255 / 85%) !important;
|
||||||
background-color: rgb(255 255 255 / 12%);
|
background-color: rgb(255 255 255 / 12%);
|
||||||
|
|
||||||
|
.pure-dialog-svg {
|
||||||
|
color: rgb(255 255 255 / 85%) !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -103,4 +107,35 @@ html.dark {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 自定义菜单搜索样式 */
|
||||||
|
.pure-search-dialog {
|
||||||
|
.el-dialog__footer {
|
||||||
|
box-shadow: 0 -1px 0 0 #555a64, 0 -3px 6px 0 rgb(69 98 155 / 12%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-footer {
|
||||||
|
.search-footer-item {
|
||||||
|
color: rgb(235 235 235 / 60%);
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ReSegmented 组件 */
|
||||||
|
.pure-segmented {
|
||||||
|
color: rgb(255 255 255 / 65%);
|
||||||
|
background-color: #000;
|
||||||
|
|
||||||
|
.pure-segmented-item-selected {
|
||||||
|
background-color: #1f1f1f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pure-segmented-item-disabled {
|
||||||
|
color: rgb(255 255 255 / 25%);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -69,6 +69,19 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.pure-dialog {
|
||||||
|
.pure-dialog-svg {
|
||||||
|
color: var(--el-color-info);
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-dialog__headerbtn {
|
||||||
|
top: 20px;
|
||||||
|
right: 14px;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* 全局覆盖element-plus的el-dialog、el-drawer、el-message-box、el-notification组件右上角关闭图标的样式,表现更鲜明 */
|
/* 全局覆盖element-plus的el-dialog、el-drawer、el-message-box、el-notification组件右上角关闭图标的样式,表现更鲜明 */
|
||||||
.el-dialog__headerbtn,
|
.el-dialog__headerbtn,
|
||||||
.el-message-box__headerbtn {
|
.el-message-box__headerbtn {
|
||||||
@ -94,6 +107,10 @@
|
|||||||
color: rgb(0 0 0 / 88%) !important;
|
color: rgb(0 0 0 / 88%) !important;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
background-color: rgb(0 0 0 / 6%);
|
background-color: rgb(0 0 0 / 6%);
|
||||||
|
|
||||||
|
.pure-dialog-svg {
|
||||||
|
color: rgb(0 0 0 / 88%) !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -131,3 +148,24 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 自定义菜单搜索样式 */
|
||||||
|
.pure-search-dialog {
|
||||||
|
.el-dialog__header {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-dialog__body {
|
||||||
|
padding-top: 12px;
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-input__inner {
|
||||||
|
font-size: 1.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-dialog__footer {
|
||||||
|
padding-bottom: 10px;
|
||||||
|
box-shadow: 0 -1px 0 0 #e0e3e8, 0 -3px 6px 0 rgb(69 98 155 / 12%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -63,7 +63,7 @@ class PureHttp {
|
|||||||
async (config: PureHttpRequestConfig): Promise<any> => {
|
async (config: PureHttpRequestConfig): Promise<any> => {
|
||||||
// 开启进度条动画
|
// 开启进度条动画
|
||||||
NProgress.start();
|
NProgress.start();
|
||||||
// 优先判断post/get等方法是否传入回掉,否则执行初始化设置等回掉
|
// 优先判断post/get等方法是否传入回调,否则执行初始化设置等回调
|
||||||
if (typeof config.beforeRequestCallback === "function") {
|
if (typeof config.beforeRequestCallback === "function") {
|
||||||
config.beforeRequestCallback(config);
|
config.beforeRequestCallback(config);
|
||||||
return config;
|
return config;
|
||||||
@ -123,7 +123,7 @@ class PureHttp {
|
|||||||
const $config = response.config;
|
const $config = response.config;
|
||||||
// 关闭进度条动画
|
// 关闭进度条动画
|
||||||
NProgress.done();
|
NProgress.done();
|
||||||
// 优先判断post/get等方法是否传入回掉,否则执行初始化设置等回掉
|
// 优先判断post/get等方法是否传入回调,否则执行初始化设置等回调
|
||||||
if (typeof $config.beforeResponseCallback === "function") {
|
if (typeof $config.beforeResponseCallback === "function") {
|
||||||
$config.beforeResponseCallback(response);
|
$config.beforeResponseCallback(response);
|
||||||
return response.data;
|
return response.data;
|
||||||
@ -159,7 +159,7 @@ class PureHttp {
|
|||||||
...axiosConfig
|
...axiosConfig
|
||||||
} as PureHttpRequestConfig;
|
} as PureHttpRequestConfig;
|
||||||
|
|
||||||
// 单独处理自定义请求/响应回掉
|
// 单独处理自定义请求/响应回调
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
PureHttp.axiosInstance
|
PureHttp.axiosInstance
|
||||||
.request(config)
|
.request(config)
|
||||||
|
@ -28,8 +28,7 @@
|
|||||||
"element-plus/global",
|
"element-plus/global",
|
||||||
"@pureadmin/table/volar",
|
"@pureadmin/table/volar",
|
||||||
"@pureadmin/descriptions/volar"
|
"@pureadmin/descriptions/volar"
|
||||||
],
|
]
|
||||||
"typeRoots": ["./types", "./node_modules/@types/"]
|
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"mock/*.ts",
|
"mock/*.ts",
|
||||||
|
4
types/index.d.ts
vendored
@ -41,6 +41,10 @@ type DeepPartial<T> = {
|
|||||||
[P in keyof T]?: DeepPartial<T[P]>;
|
[P in keyof T]?: DeepPartial<T[P]>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type Without<T, U> = { [P in Exclude<keyof T, keyof U>]?: never };
|
||||||
|
|
||||||
|
type Exclusive<T, U> = (Without<T, U> & U) | (Without<U, T> & T);
|
||||||
|
|
||||||
type TimeoutHandle = ReturnType<typeof setTimeout>;
|
type TimeoutHandle = ReturnType<typeof setTimeout>;
|
||||||
|
|
||||||
type IntervalHandle = ReturnType<typeof setInterval>;
|
type IntervalHandle = ReturnType<typeof setInterval>;
|
||||||
|