Compare commits

..

55 Commits

Author SHA1 Message Date
xiaoxian521
613d20fc1b release: update 5.6.0 2024-05-14 09:55:45 +08:00
xiaoxian521
8934499349 chore(deps): update 2024-05-14 09:32:40 +08:00
xiaoxian521
bb86962186 chore: 升级pnpmv9版本,相关兼容处理 2024-05-13 22:45:19 +08:00
xiaoxian521
30f598dfa5 chore: update 2024-05-09 21:43:20 +08:00
xiaoxian521
de00537fdd chore(deps): update 2024-05-09 10:37:19 +08:00
Fifteen
5238ee7ad9 perf: 优化ReSegmented组件 (#1137) 2024-05-08 17:41:04 +08:00
一万
3ca9a7b5bc fix: 修复点击外链会跳转两次的问题 (#1133) 2024-05-08 15:59:32 +08:00
xiaoxian521
fae4924102 release: update 5.5.0 2024-05-07 09:09:35 +08:00
xiaoxian521
295240f2a3 chore(deps): update 2024-05-06 16:21:14 +08:00
xiaoxian521
1d9f1cf465 docs: 更换文档站和完整版预览站地址 2024-05-03 00:00:35 +08:00
xiaoxian521
a56061ac81 chore: 更新依赖,相关兼容处理 2024-05-02 22:39:18 +08:00
xiaoxian521
d24f334a81 ci: add pages.yml 2024-05-02 21:45:57 +08:00
xiaoming
cc7726e1c2 feat: 新组件ReVxeTableBar搭配vxe-table使用 (#1087) 2024-05-02 21:34:16 +08:00
xiaoming
b8159a0d73 refactor: 重构layout文件命名规范,更易读 (#1110) 2024-04-30 22:27:54 +08:00
一万
2f9bc7e187 fix: 修复函数式弹窗点击取消按钮,延时关闭无效问题 (#1112) 2024-04-26 15:20:54 +08:00
Nexpro
d6f487a48c chore: revise typo (#1103) 2024-04-26 10:59:48 +08:00
xiaoxian521
75dc3b6216 chore(deps): update 2024-04-25 11:40:17 +08:00
xiaoxian521
4be7db4944 chore: update 2024-04-24 16:25:12 +08:00
xiaoxian521
884cf92640 chore(deps): update 2024-04-24 11:19:51 +08:00
苗大
fb0c092a2c style: update 2024-04-23 11:35:11 +08:00
xiaoxian521
1ce0038c13 chore: update 2024-04-23 10:26:26 +08:00
xiaoxian521
88d804df89 chore: update 2024-04-22 13:09:32 +08:00
xiaoxian521
efe0c83e4f chore: 升级依赖,相关兼容处理 2024-04-22 12:27:53 +08:00
shuTwT
ada3e80c7e perf: 优化账号设置-头像上传功能 2024-04-19 10:52:39 +08:00
xiaoxian521
d203222acf release: update 5.4.0 2024-04-18 15:34:36 +08:00
xiaoxian521
07ad01ebee style: update 2024-04-18 13:47:13 +08:00
xiaoxian521
ed71fe708d chore: update 2024-04-18 12:20:08 +08:00
xiaoxian521
29b06c5285 chore(deps): update 2024-04-17 18:37:45 +08:00
fangcy
9ad00d97ee fix: 修复调用刷新token接口时,refreshToken参数为空的问题 2024-04-17 18:17:39 +08:00
xiaoming
a402d33b89 perf: 框架核心代码国际化全量覆盖(仅需处理业务代码国际化即可) (#1082) 2024-04-17 15:41:37 +08:00
xiaoxian521
d1d2bcaa11 feat: 添加全局配置MaxTagsLevel属性,可设置打开标签页最大数 2024-04-16 19:34:57 +08:00
xiaoxian521
abd042afe8 types: 优src/utils/http文件中postget工具函数的类型 2024-04-16 14:01:36 +08:00
xiaoxian521
cd42dbf188 chore: 移除非必要的cloc工具依赖包,它使用GPL开源许可证具有争议性 2024-04-16 11:59:11 +08:00
xiaoxian521
4cbf9056b1 perf: 简化store文件中导出和导入用法 2024-04-16 11:49:15 +08:00
xiaoxian521
5d3a9dff46 fix: 修复账户设置页面,刷新后字体颜色未生效的问题 2024-04-15 17:52:51 +08:00
xiaoxian521
dbb05f4166 fix: 修复三级菜单向左折叠后图标显示样式问题 2024-04-15 16:25:01 +08:00
xiaoxian521
ea36e260e2 fix: 修复当一个页面存在多个RePureTableBar组件,列无法正常拖动问题 2024-04-15 12:52:01 +08:00
xiaoxian521
a5b57908ec perf: 优化请求白名单逻辑,兼容更多场景 2024-04-15 11:40:15 +08:00
xiaoxian521
699b174f95 perf: 优化获取顶级菜单逻辑 2024-04-15 11:15:57 +08:00
xiaoxian521
0934c97413 chore: 升级eslint9,相关兼容处理 2024-04-13 11:21:06 +08:00
xiaoxian521
0dc979eec0 chore(deps): update 2024-04-13 09:37:18 +08:00
xiaoxian521
7ed0e8f89f docs: 更新特别代码贡献名单 2024-04-12 21:34:45 +08:00
EchoByte
332678ba88 feat: 添加账户设置功能页面且兼容移动端 (#1022) 2024-04-12 16:20:33 +08:00
xiaoming
296db3789b feat: 路由添加可配置的fixedTag属性,作用为当前菜单名称是否固定显示在标签页且不可关闭 (#1047) 2024-04-09 17:07:05 +08:00
xiaoxian521
c0a1b918aa feat: pure-table添加headerSlot自定义表头插槽用法示例 2024-04-06 12:25:13 +08:00
xiaoxian521
bf874276f3 chore(deps): update 2024-04-06 10:44:08 +08:00
hb0730
f120467fc8 feat: 函数式弹框ReDialog添加Popconfirm气泡确认框 (#1046) 2024-04-04 22:14:24 +08:00
xiaoxian521
8d2ef1f644 perf: 优化登录接口 2024-04-04 15:23:02 +08:00
hb0730
fcd0fc9d65 workflow: update node-version (#1051) 2024-04-03 12:23:16 +08:00
xiaoxian521
d685a18b9e feat: 添加思维导图示例 2024-03-31 14:38:15 +08:00
hb0730
e0f52fc379 feat: 分段控制器组件添加全局禁用属性disabled (#1043)
* feat: segmented component global disabled
2024-03-31 12:37:10 +08:00
xiaoxian521
53e3e601fb chore: 升级依赖,相关兼容处理 2024-03-30 23:45:56 +08:00
xiaoxian521
adc82644cb chore(deps): update 2024-03-29 20:17:51 +08:00
xiaoxian521
ee89a864db chore(deps): update 2024-03-29 10:11:06 +08:00
Fifteen
75fad304e7 chore: revise typo (#1039) 2024-03-29 10:06:35 +08:00
178 changed files with 10151 additions and 6993 deletions

View File

@@ -1,13 +0,0 @@
public
dist
*.d.ts
/src/assets
package.json
eslint.config.js
.prettierrc.js
commitlint.config.js
postcss.config.js
tailwind.config.ts
stylelint.config.js
src/components/ReSplitPane/iconfont
src/components/ReFlowChart/src/assets/iconfont

View File

@@ -32,7 +32,7 @@ body:
label: 验证 (Verify)
description: 在提交问题之前,请确保您执行以下操作 (Before submitting an issue, please ensure you do the following)
options:
- label: 是否仔细阅读过 [文档](https://yiming_chang.gitee.io/pure-admin-doc/) (Have you read [documentation](https://yiming_chang.gitee.io/pure-admin-doc/) carefully)
- label: 是否仔细阅读过 [文档](https://pure-admin.github.io/pure-admin-doc/) (Have you read [documentation](https://pure-admin.github.io/pure-admin-doc/) carefully)
required: true
- label: 检查是否存在相同或类似的问题 [issues](https://github.com/pure-admin/vue-pure-admin/issues) (Check for the same or similar [issues](https://github.com/pure-admin/vue-pure-admin/issues))
required: true

View File

@@ -13,18 +13,18 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Install Node.js
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: 18
node-version: 20
- uses: pnpm/action-setup@v2
- uses: pnpm/action-setup@v3
name: Install pnpm
id: pnpm-install
with:
version: 8.6.10
version: 9
run_install: false
- name: Get pnpm store directory
@@ -33,7 +33,7 @@ jobs:
run: |
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
- uses: actions/cache@v3
- uses: actions/cache@v4
name: Setup pnpm cache
with:
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}

42
.github/workflows/pages.yml vendored Normal file
View File

@@ -0,0 +1,42 @@
name: Build and Deploy
permissions:
contents: write
on:
push:
branches:
- pages
jobs:
deploy:
concurrency: ci-${{ github.ref }}
runs-on: ubuntu-latest
steps:
- name: Checkout 🛎️
uses: actions/checkout@v4
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: 20
registry-url: https://registry.npmjs.com/
- uses: pnpm/action-setup@v3
name: Install pnpm
id: pnpm-install
with:
version: 9
run_install: false
- name: Deploy 🔧
run: |
pnpm install --no-frozen-lockfile
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

@@ -3,7 +3,7 @@
"prettier --cache --ignore-unknown --write",
"eslint --cache --fix"
],
"{!(package)*.json,*.code-snippets,.!({browserslist,nvm})*rc}": [
"{!(package)*.json,*.code-snippets,.!({browserslist,npm,nvm})*rc}": [
"prettier --cache --write--parser json"
],
"package.json": ["prettier --cache --write"],

5
.npmrc
View File

@@ -1,3 +1,4 @@
shell-emulator=true
shamefully-hoist=true
strict-peer-dependencies=false
shell-emulator=true
enable-pre-post-scripts=false
strict-peer-dependencies=false

2
.nvmrc
View File

@@ -1 +1 @@
v20.11.1
v20.13.1

View File

@@ -1,6 +1,76 @@
# 5.6.0 (2024-05-14)
### ✔️ Refactor
- Upgrade `pnpm` to `v9` version, requiring `pnpm` version `>=9`
### 🐞 Bug fixes
- Fixed the issue where clicking on an external link would jump twice
### 🍏 Perf
- Optimize `ReSegmented` component
# 5.5.0 (2024-05-07)
### 📄 Docs
The addresses of the document site and full version preview site have been changed!
- The latest document site address: https://pure-admin.github.io/pure-admin-doc
- The latest full version preview site address: https://pure-admin.github.io/vue-pure-admin
### ✔️ Refactor
- Reconstruct the `layout` file naming convention to make it more readable
### 🎫 Feat
- Add new component `ReVxeTableBar` to be used with `vxe-table`
### 🐞 Bug fixes
- Fixed the issue where the background color is white when `FixedHeader` is set to `false` in dark mode
- Fixed the problem of delayed closing of functional pop-up window `ReDialog` when clicking the cancel button
### 🍏 Perf
- Optimize account settings-avatar upload function
# 5.4.0 (2024-04-18)
### 🎫 Feat
- Add global disabled attribute `disabled` to `ReSegmented` component
- Added mind map examples
- Functional pop-up box `ReDialog` adds `Popconfirm` bubble confirmation box
- `pure-table` adds `headerSlot` custom header slot usage example
- The route adds a configurable `fixedTag` attribute, which determines whether the current menu name is fixedly displayed on the tab and cannot be closed.
- Added account setting function page and compatible with mobile terminals
- Upgrade `eslint9` and related compatibility processing
- Added global configuration `MaxTagsLevel` property to set the maximum number of open tabs
- Full coverage of the internationalization of the core code of the framework (only the internationalization of the business code needs to be processed)
### 🐞 Bug fixes
- Fixed the issue where columns cannot be dragged normally when there are multiple `RePureTableBar` components on a page
- Fixed the problem of icon display style after the third-level menu is folded to the left
- Fixed the issue where the font color does not take effect after refreshing the account settings page
- Fixed the issue where the `refreshToken` parameter is empty when calling the refresh `token` interface
### 🍏 Perf
- Optimize login interface
- Optimize the logic of obtaining the top menu
- Optimize request whitelist logic and be compatible with more scenarios
- Simplified export and import usage in `store` files
- Removed unnecessary `cloc` tool dependency package, which uses the `GPL` open source license and is controversial
- Optimize the types of `post` and `get` utility functions in `src/utils/http` files
# 5.3.0 (2024-03-28)
### ✔️ refactor
### ✔️ Refactor
- Reconstruct internationalized file naming conventions and demo pages with code location hints
@@ -13,7 +83,7 @@
# 5.2.0 (2024-03-22)
### ✔️ refactor
### ✔️ Refactor
- Place the full screen button at the top to make it visible and easy to operate
@@ -49,7 +119,7 @@
# 5.1.0 (2024-03-02)
### ✔️ refactor
### ✔️ Refactor
- Reconstruct the tab page `UI` to make it more convenient to click the close button
@@ -71,7 +141,7 @@
Totally `ESM` version
### ✔️ refactor
### ✔️ Refactor
- Upgrade `vite` to `v5` version, specify `node` version `>18.18.0`, `pnpm` version `>=8.6.10`
- Use [vite-plugin-fake-server](https://www.npmjs.com/package/vite-plugin-fake-server) to replace [vite-plugin-mock](https://www.npmjs.com/package/vite-plugin-mock), use [@faker-js/faker](https://www.npmjs.com/package/@faker-js/faker) to replace [mockjs](https://www.npmjs.com/package/mockjs)
@@ -162,7 +232,7 @@ Totally `ESM` version
# 4.5.0 (2023-06-26)
### ✔️ refactor
### ✔️ Refactor
- Refactor image crop `ReCropper` component, add more useful functions
@@ -275,7 +345,7 @@ Totally `ESM` version
[View 4.0.0 version optimization details](https://github.com/pure-admin/vue-pure-admin/issues/428#issuecomment-1422191158)
### ✔️ refactor
### ✔️ Refactor
- Use `css` pseudo-class `before` to refactor the activation background of the menu, similar to [ant.design-menu](https://ant.design/components/menu-cn#components-menu-demo-inline-collapsed)
@@ -346,10 +416,10 @@ Totally `ESM` version
# 3.9.5 (2022-12-13)
### ✔️ refactor
### ✔️ Refactor
- completely removed `lodash` and its related libraries
[Click here to see Why Removed? How to integrate it yourself? ](https://yiming_chang.gitee.io/pure-admin-doc/pages/FAQ/#%E5%B9%B3%E5%8F%B0%E5%9C%A8-v3-9-5-%E7 %89%88%E6%9C%AC%E5%AE%8C%E5%85%A8%E7%A7%BB%E9%99%A4%E4%BA%86-lodash-%E5%92%8C% E5%85%B6%E7%9B%B8%E5%85%B3%E5%BA%93-%E4%B8%BA%E4%BB%80%E4%B9%88%E7%A7%BB%E9 %99%A4-%E5%A6%82%E4%BD%95%E8%87%AA%E8%A1%8C%E9%9B%86%E6%88%90)
[Click here to see Why Removed? How to integrate it yourself? ](https://pure-admin.github.io/pure-admin-doc/pages/FAQ/#%E5%B9%B3%E5%8F%B0%E5%9C%A8-v3-9-5-%E7 %89%88%E6%9C%AC%E5%AE%8C%E5%85%A8%E7%A7%BB%E9%99%A4%E4%BA%86-lodash-%E5%92%8C% E5%85%B6%E7%9B%B8%E5%85%B3%E5%BA%93-%E4%B8%BA%E4%BB%80%E4%B9%88%E7%A7%BB%E9 %99%A4-%E5%A6%82%E4%BD%95%E8%87%AA%E8%A1%8C%E9%9B%86%E6%88%90)
### 🎫 Feat
@@ -366,10 +436,10 @@ Totally `ESM` version
# 3.9.4 (2022-12-05)
### ✔️ refactor
### ✔️ Refactor
- Completely removed `vxe-table`, after removal, the overall package size of the full version is reduced by `1.82MB`, and the initial startup time is basically the same as the lite version 🐮
[Click here to see Why Removed? How to integrate it yourself?](https://yiming_chang.gitee.io/pure-admin-doc/pages/FAQ/#%E5%B9%B3%E5%8F%B0%E5%9C%A8-v3-9-4-%E7%89%88%E6%9C%AC%E5%AE%8C%E5%85%A8%E7%A7%BB%E9%99%A4%E4%BA%86-vxe-table-%E4%B8%BA%E4%BB%80%E4%B9%88%E7%A7%BB%E9%99%A4-%E5%A6%82%E4%BD%95%E8%87%AA%E8%A1%8C%E9%9B%86%E6%88%90)
[Click here to see Why Removed? How to integrate it yourself?](https://pure-admin.github.io/pure-admin-doc/pages/FAQ/#%E5%B9%B3%E5%8F%B0%E5%9C%A8-v3-9-4-%E7%89%88%E6%9C%AC%E5%AE%8C%E5%85%A8%E7%A7%BB%E9%99%A4%E4%BA%86-vxe-table-%E4%B8%BA%E4%BB%80%E4%B9%88%E7%A7%BB%E9%99%A4-%E5%A6%82%E4%BD%95%E8%87%AA%E8%A1%8C%E9%9B%86%E6%88%90)
### 🎫 Feat
@@ -475,13 +545,13 @@ Totally `ESM` version
# 3.7.0 (2022-11-21)
### ✔️ refactor
### ✔️ Refactor
- Replace `driver.js` with `intro.js`
### 🎫 Feat
- Add front-end single sign-on, test address https://yiming_chang.gitee.io/vue-pure-admin/#/pure-table/index?username=sso&roles=admin&accessToken=eyJhbGciOiJIUzUxMiJ9.admin
- Add front-end single sign-on, test address https://pure-admin.github.io/vue-pure-admin/#/pure-table/index?username=sso&roles=admin&accessToken=eyJhbGciOiJIUzUxMiJ9.admin
- Add more examples for [@pureadmin/table](https://github.com/pure-admin/pure-admin-table) and `element-plus` [table](https://element-plus.org /zh-CN/component/table.html) example remains the same
- Rich watermark function page (supports customizing various colors, shadows, text, additional attributes, setting undeletable watermarks and setting watermarks for specified elements)
- Optimize the menu, add `MenuArrowIconNoTransition` global configuration, configure it in `public/platform-config.json`, for the left menu mode, the menu expansion can be set `MenuArrowIconNoTransition: true` to solve
@@ -526,7 +596,7 @@ Totally `ESM` version
# 3.6.2 (2022-10-27)
### ✔️ refactor
### ✔️ Refactor
- Replace `/@/` alias with `@/` alias
@@ -554,7 +624,7 @@ Totally `ESM` version
- Add typewriter component `demo`
- Added `json` editor `demo`
### ✔️ refactor
### ✔️ Refactor
- Refactor the permission module, adopt the most commonly used `RBAC` (Role-Based Access List): role-based permission control (User -> Role -> Permission), and update the page permission and button permission `demo` example, button Permissions support three operation modes (judging permissions in component mode, judging permissions in function mode, and judging permissions in instruction mode)
@@ -580,9 +650,9 @@ Totally `ESM` version
- Add `element-plus` seamless scrolling `Table` page demo
- Open `vscode` bracket pair guide
### ✔️ refactor
### ✔️ Refactor
- Replace `unocss` with `tailwindcss`, add `tailwindcss` [documentation](https://yiming_chang.gitee.io/pure-admin-doc/pages/tailwindcss/)
- Replace `unocss` with `tailwindcss`, add `tailwindcss` [documentation](https://pure-admin.github.io/pure-admin-doc/pages/tailwindcss/)
### 🐞 Bug fixes
@@ -630,7 +700,7 @@ Totally `ESM` version
- Added export `excel` page demo
- Added blank page demo without `Layout`
### ✔️ refactor
### ✔️ 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
@@ -666,7 +736,7 @@ Totally `ESM` version
- Added pre-release packaging mode
- Add [hooks] to close a tag (https://github.com/pure-admin/vue-pure-admin/commit/5e8723a031923e79f507e5a17151d3bd88a51523)
### ✔️ refactor
### ✔️ Refactor
- Refactored the landing page to be more inclined to the actual business scenario
- Use `unocss` instead of `windicss`, `unocss` has better performance in development environment, no memory leaks, and `api` is compatible with `windicss`
@@ -745,7 +815,7 @@ Totally `ESM` version
- Added `WindiCSS` support
- Add online environment remove console plugin `vite-plugin-remove-console`
### ✔️ refactor
### ✔️ Refactor
- Replace `@element-plus/icons-vue` with `@iconify-icons/ep`

View File

@@ -1,6 +1,76 @@
# 5.6.0 (2024-05-14)
### ✔️ Refactor
- Upgrade `pnpm` to `v9` version, requiring `pnpm` version `>=9`
### 🐞 Bug fixes
- Fixed the issue where clicking on an external link would jump twice
### 🍏 Perf
- Optimize `ReSegmented` component
# 5.5.0 (2024-05-07)
### 📄 Docs
The addresses of the document site and full version preview site have been changed!
- The latest document site address: https://pure-admin.github.io/pure-admin-doc
- The latest full version preview site address: https://pure-admin.github.io/vue-pure-admin
### ✔️ Refactor
- Reconstruct the `layout` file naming convention to make it more readable
### 🎫 Feat
- Add new component `ReVxeTableBar` to be used with `vxe-table`
### 🐞 Bug fixes
- Fixed the issue where the background color is white when `FixedHeader` is set to `false` in dark mode
- Fixed the problem of delayed closing of functional pop-up window `ReDialog` when clicking the cancel button
### 🍏 Perf
- Optimize account settings-avatar upload function
# 5.4.0 (2024-04-18)
### 🎫 Feat
- Add global disabled attribute `disabled` to `ReSegmented` component
- Added mind map examples
- Functional pop-up box `ReDialog` adds `Popconfirm` bubble confirmation box
- `pure-table` adds `headerSlot` custom header slot usage example
- The route adds a configurable `fixedTag` attribute, which determines whether the current menu name is fixedly displayed on the tab and cannot be closed.
- Added account setting function page and compatible with mobile terminals
- Upgrade `eslint9` and related compatibility processing
- Added global configuration `MaxTagsLevel` property to set the maximum number of open tabs
- Full coverage of the internationalization of the core code of the framework (only the internationalization of the business code needs to be processed)
### 🐞 Bug fixes
- Fixed the issue where columns cannot be dragged normally when there are multiple `RePureTableBar` components on a page
- Fixed the problem of icon display style after the third-level menu is folded to the left
- Fixed the issue where the font color does not take effect after refreshing the account settings page
- Fixed the issue where the `refreshToken` parameter is empty when calling the refresh `token` interface
### 🍏 Perf
- Optimize login interface
- Optimize the logic of obtaining the top menu
- Optimize request whitelist logic and be compatible with more scenarios
- Simplified export and import usage in `store` files
- Removed unnecessary `cloc` tool dependency package, which uses the `GPL` open source license and is controversial
- Optimize the types of `post` and `get` utility functions in `src/utils/http` files
# 5.3.0 (2024-03-28)
### ✔️ refactor
### ✔️ Refactor
- Reconstruct internationalized file naming conventions and demo pages with code location hints
@@ -13,7 +83,7 @@
# 5.2.0 (2024-03-22)
### ✔️ refactor
### ✔️ Refactor
- Place the full screen button at the top to make it visible and easy to operate
@@ -49,7 +119,7 @@
# 5.1.0 (2024-03-02)
### ✔️ refactor
### ✔️ Refactor
- Reconstruct the tab page `UI` to make it more convenient to click the close button
@@ -71,7 +141,7 @@
Totally `ESM` version
### ✔️ refactor
### ✔️ Refactor
- Upgrade `vite` to `v5` version, specify `node` version `>18.18.0`, `pnpm` version `>=8.6.10`
- Use [vite-plugin-fake-server](https://www.npmjs.com/package/vite-plugin-fake-server) to replace [vite-plugin-mock](https://www.npmjs.com/package/vite-plugin-mock), use [@faker-js/faker](https://www.npmjs.com/package/@faker-js/faker) to replace [mockjs](https://www.npmjs.com/package/mockjs)
@@ -162,7 +232,7 @@ Totally `ESM` version
# 4.5.0 (2023-06-26)
### ✔️ refactor
### ✔️ Refactor
- Refactor image crop `ReCropper` component, add more useful functions
@@ -275,7 +345,7 @@ Totally `ESM` version
[View 4.0.0 version optimization details](https://github.com/pure-admin/vue-pure-admin/issues/428#issuecomment-1422191158)
### ✔️ refactor
### ✔️ Refactor
- Use `css` pseudo-class `before` to refactor the activation background of the menu, similar to [ant.design-menu](https://ant.design/components/menu-cn#components-menu-demo-inline-collapsed)
@@ -346,10 +416,10 @@ Totally `ESM` version
# 3.9.5 (2022-12-13)
### ✔️ refactor
### ✔️ Refactor
- completely removed `lodash` and its related libraries
[Click here to see Why Removed? How to integrate it yourself? ](https://yiming_chang.gitee.io/pure-admin-doc/pages/FAQ/#%E5%B9%B3%E5%8F%B0%E5%9C%A8-v3-9-5-%E7 %89%88%E6%9C%AC%E5%AE%8C%E5%85%A8%E7%A7%BB%E9%99%A4%E4%BA%86-lodash-%E5%92%8C% E5%85%B6%E7%9B%B8%E5%85%B3%E5%BA%93-%E4%B8%BA%E4%BB%80%E4%B9%88%E7%A7%BB%E9 %99%A4-%E5%A6%82%E4%BD%95%E8%87%AA%E8%A1%8C%E9%9B%86%E6%88%90)
[Click here to see Why Removed? How to integrate it yourself? ](https://pure-admin.github.io/pure-admin-doc/pages/FAQ/#%E5%B9%B3%E5%8F%B0%E5%9C%A8-v3-9-5-%E7 %89%88%E6%9C%AC%E5%AE%8C%E5%85%A8%E7%A7%BB%E9%99%A4%E4%BA%86-lodash-%E5%92%8C% E5%85%B6%E7%9B%B8%E5%85%B3%E5%BA%93-%E4%B8%BA%E4%BB%80%E4%B9%88%E7%A7%BB%E9 %99%A4-%E5%A6%82%E4%BD%95%E8%87%AA%E8%A1%8C%E9%9B%86%E6%88%90)
### 🎫 Feat
@@ -366,10 +436,10 @@ Totally `ESM` version
# 3.9.4 (2022-12-05)
### ✔️ refactor
### ✔️ Refactor
- Completely removed `vxe-table`, after removal, the overall package size of the full version is reduced by `1.82MB`, and the initial startup time is basically the same as the lite version 🐮
[Click here to see Why Removed? How to integrate it yourself?](https://yiming_chang.gitee.io/pure-admin-doc/pages/FAQ/#%E5%B9%B3%E5%8F%B0%E5%9C%A8-v3-9-4-%E7%89%88%E6%9C%AC%E5%AE%8C%E5%85%A8%E7%A7%BB%E9%99%A4%E4%BA%86-vxe-table-%E4%B8%BA%E4%BB%80%E4%B9%88%E7%A7%BB%E9%99%A4-%E5%A6%82%E4%BD%95%E8%87%AA%E8%A1%8C%E9%9B%86%E6%88%90)
[Click here to see Why Removed? How to integrate it yourself?](https://pure-admin.github.io/pure-admin-doc/pages/FAQ/#%E5%B9%B3%E5%8F%B0%E5%9C%A8-v3-9-4-%E7%89%88%E6%9C%AC%E5%AE%8C%E5%85%A8%E7%A7%BB%E9%99%A4%E4%BA%86-vxe-table-%E4%B8%BA%E4%BB%80%E4%B9%88%E7%A7%BB%E9%99%A4-%E5%A6%82%E4%BD%95%E8%87%AA%E8%A1%8C%E9%9B%86%E6%88%90)
### 🎫 Feat
@@ -475,13 +545,13 @@ Totally `ESM` version
# 3.7.0 (2022-11-21)
### ✔️ refactor
### ✔️ Refactor
- Replace `driver.js` with `intro.js`
### 🎫 Feat
- Add front-end single sign-on, test address https://yiming_chang.gitee.io/vue-pure-admin/#/pure-table/index?username=sso&roles=admin&accessToken=eyJhbGciOiJIUzUxMiJ9.admin
- Add front-end single sign-on, test address https://pure-admin.github.io/vue-pure-admin/#/pure-table/index?username=sso&roles=admin&accessToken=eyJhbGciOiJIUzUxMiJ9.admin
- Add more examples for [@pureadmin/table](https://github.com/pure-admin/pure-admin-table) and `element-plus` [table](https://element-plus.org /zh-CN/component/table.html) example remains the same
- Rich watermark function page (supports customizing various colors, shadows, text, additional attributes, setting undeletable watermarks and setting watermarks for specified elements)
- Optimize the menu, add `MenuArrowIconNoTransition` global configuration, configure it in `public/platform-config.json`, for the left menu mode, the menu expansion can be set `MenuArrowIconNoTransition: true` to solve
@@ -526,7 +596,7 @@ Totally `ESM` version
# 3.6.2 (2022-10-27)
### ✔️ refactor
### ✔️ Refactor
- Replace `/@/` alias with `@/` alias
@@ -554,7 +624,7 @@ Totally `ESM` version
- Add typewriter component `demo`
- Added `json` editor `demo`
### ✔️ refactor
### ✔️ Refactor
- Refactor the permission module, adopt the most commonly used `RBAC` (Role-Based Access List): role-based permission control (User -> Role -> Permission), and update the page permission and button permission `demo` example, button Permissions support three operation modes (judging permissions in component mode, judging permissions in function mode, and judging permissions in instruction mode)
@@ -580,9 +650,9 @@ Totally `ESM` version
- Add `element-plus` seamless scrolling `Table` page demo
- Open `vscode` bracket pair guide
### ✔️ refactor
### ✔️ Refactor
- Replace `unocss` with `tailwindcss`, add `tailwindcss` [documentation](https://yiming_chang.gitee.io/pure-admin-doc/pages/tailwindcss/)
- Replace `unocss` with `tailwindcss`, add `tailwindcss` [documentation](https://pure-admin.github.io/pure-admin-doc/pages/tailwindcss/)
### 🐞 Bug fixes
@@ -630,7 +700,7 @@ Totally `ESM` version
- Added export `excel` page demo
- Added blank page demo without `Layout`
### ✔️ refactor
### ✔️ 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
@@ -666,7 +736,7 @@ Totally `ESM` version
- Added pre-release packaging mode
- Add [hooks] to close a tag (https://github.com/pure-admin/vue-pure-admin/commit/5e8723a031923e79f507e5a17151d3bd88a51523)
### ✔️ refactor
### ✔️ Refactor
- Refactored the landing page to be more inclined to the actual business scenario
- Use `unocss` instead of `windicss`, `unocss` has better performance in development environment, no memory leaks, and `api` is compatible with `windicss`
@@ -745,7 +815,7 @@ Totally `ESM` version
- Added `WindiCSS` support
- Add online environment remove console plugin `vite-plugin-remove-console`
### ✔️ refactor
### ✔️ Refactor
- Replace `@element-plus/icons-vue` with `@iconify-icons/ep`

View File

@@ -1,6 +1,76 @@
# 5.6.0 (2024-05-14)
### ✔️ Refactor
- 升级`pnpm``v9`版本,规定`pnpm`版本`>=9`
### 🐞 Bug fixes
- 修复点击外链会跳转两次的问题
### 🍏 Perf
- 优化`ReSegmented`组件
# 5.5.0 (2024-05-07)
### 📄 Docs
文档站和完整版预览站地址更换!
- 最新文档站地址https://pure-admin.github.io/pure-admin-doc
- 最新完整版预览站地址https://pure-admin.github.io/vue-pure-admin
### ✔️ Refactor
- 重构`layout`文件命名规范,更易读
### 🎫 Feat
- 添加新组件`ReVxeTableBar`搭配`vxe-table`使用
### 🐞 Bug fixes
- 修复深色模式下设置`FixedHeader``false`时,背景色为白色的问题
- 修复函数式弹窗`ReDialog`点击取消按钮,延时关闭无效问题
### 🍏 Perf
- 优化账号设置-头像上传功能
# 5.4.0 (2024-04-18)
### 🎫 Feat
- 分段控制器组件添加全局禁用属性`disabled`
- 添加思维导图示例
- 函数式弹框`ReDialog`添加`Popconfirm`气泡确认框
- `pure-table`添加`headerSlot`自定义表头插槽用法示例
- 路由添加可配置的`fixedTag`属性,作用为当前菜单名称是否固定显示在标签页且不可关闭
- 添加账户设置功能页面且兼容移动端
- 升级`eslint9`,相关兼容处理
- 添加全局配置`MaxTagsLevel`属性,可设置打开标签页最大数
- 框架核心代码国际化全量覆盖(仅需处理业务代码国际化即可)
### 🐞 Bug fixes
- 修复当一个页面存在多个`RePureTableBar`组件,列无法正常拖动问题
- 修复三级菜单向左折叠后图标显示样式问题
- 修复账户设置页面,刷新后字体颜色未生效的问题
- 修复调用刷新`token`接口时,`refreshToken`参数为空的问题
### 🍏 Perf
- 优化登录接口
- 优化获取顶级菜单逻辑
- 优化请求白名单逻辑,兼容更多场景
- 简化`store`文件中导出和导入用法
- 移除非必要的`cloc`工具依赖包,它使用`GPL`开源许可证具有争议性
- 优化`src/utils/http`文件中`post``get`工具函数的类型
# 5.3.0 (2024-03-28)
### ✔️ refactor
### ✔️ Refactor
- 重构国际化文件命名规范以及演示页加上代码位置提示
@@ -13,7 +83,7 @@
# 5.2.0 (2024-03-22)
### ✔️ refactor
### ✔️ Refactor
- 将全屏按钮置于顶部,使其显眼且易于操作
@@ -41,7 +111,7 @@
- 修复`windwos`下点击注册页面会出现滚动条问题
- 修复`windows`下页面切换,内容区会出现滚动条问题
- 修复`pure-table`带状态表格在深色整体风格下状态样式消失的问题
- 修复项目配置中开启灰色模式和深色整体风格,刷新页面整体风格异常
- 修复系统配置中开启灰色模式和深色整体风格,刷新页面整体风格异常
### 🍏 Perf
@@ -49,7 +119,7 @@
# 5.1.0 (2024-03-02)
### ✔️ refactor
### ✔️ Refactor
- 重构标签页`UI`,点击关闭按钮更方便
@@ -71,7 +141,7 @@
全面`ESM`版本
### ✔️ refactor
### ✔️ Refactor
- 升级`vite``v5`版本,规定`node`版本`>18.18.0``pnpm`版本`>=8.6.10`
- 使用 [vite-plugin-fake-server](https://www.npmjs.com/package/vite-plugin-fake-server) 替换 [vite-plugin-mock](https://www.npmjs.com/package/vite-plugin-mock),使用 [@faker-js/faker](https://www.npmjs.com/package/@faker-js/faker) 替换 [mockjs](https://www.npmjs.com/package/mockjs)
@@ -84,7 +154,7 @@
- 所有`search`搜索图标统一替换为`@iconify-icons/ri/search-line`它比较常用将其放入全局离线图标中
- 移除`iframe`在暗模式下的滤镜效果
- 带来更美观精致的首页
- 更干净整洁的项目配置右侧弹出面板
- 更干净整洁的系统配置右侧弹出面板
- 重构关于页面,整体更紧致,关键信息更突出
### 🎫 Feat
@@ -162,7 +232,7 @@
# 4.5.0 (2023-06-26)
### ✔️ refactor
### ✔️ Refactor
- 重构图片裁剪 `ReCropper` 组件,添加更多实用功能
@@ -274,7 +344,7 @@
[查看 4.0.0 版本优化细节](https://github.com/pure-admin/vue-pure-admin/issues/428#issuecomment-1422191158)
### ✔️ refactor
### ✔️ Refactor
- 采用 `css` 伪类 `before` 写法重构菜单的激活背景,类似于 [ant.design-menu](https://ant.design/components/menu-cn#components-menu-demo-inline-collapsed)
@@ -345,10 +415,10 @@
# 3.9.5 (2022-12-13)
### ✔️ refactor
### ✔️ Refactor
- 完全移除了 `lodash` 和其相关库
[点击此处查看为什么移除?如何自行集成?](https://yiming_chang.gitee.io/pure-admin-doc/pages/FAQ/#%E5%B9%B3%E5%8F%B0%E5%9C%A8-v3-9-5-%E7%89%88%E6%9C%AC%E5%AE%8C%E5%85%A8%E7%A7%BB%E9%99%A4%E4%BA%86-lodash-%E5%92%8C%E5%85%B6%E7%9B%B8%E5%85%B3%E5%BA%93-%E4%B8%BA%E4%BB%80%E4%B9%88%E7%A7%BB%E9%99%A4-%E5%A6%82%E4%BD%95%E8%87%AA%E8%A1%8C%E9%9B%86%E6%88%90)
[点击此处查看为什么移除?如何自行集成?](https://pure-admin.github.io/pure-admin-doc/pages/FAQ/#%E5%B9%B3%E5%8F%B0%E5%9C%A8-v3-9-5-%E7%89%88%E6%9C%AC%E5%AE%8C%E5%85%A8%E7%A7%BB%E9%99%A4%E4%BA%86-lodash-%E5%92%8C%E5%85%B6%E7%9B%B8%E5%85%B3%E5%BA%93-%E4%B8%BA%E4%BB%80%E4%B9%88%E7%A7%BB%E9%99%A4-%E5%A6%82%E4%BD%95%E8%87%AA%E8%A1%8C%E9%9B%86%E6%88%90)
### 🎫 Feat
@@ -365,10 +435,10 @@
# 3.9.4 (2022-12-05)
### ✔️ refactor
### ✔️ Refactor
- 完全移除了 `vxe-table`,移除后,完整版整体打包大小减少 `1.82MB`,首启动时长基本和精简版持平 🐮
[点击此处查看为什么移除?如何自行集成?](https://yiming_chang.gitee.io/pure-admin-doc/pages/FAQ/#%E5%B9%B3%E5%8F%B0%E5%9C%A8-v3-9-4-%E7%89%88%E6%9C%AC%E5%AE%8C%E5%85%A8%E7%A7%BB%E9%99%A4%E4%BA%86-vxe-table-%E4%B8%BA%E4%BB%80%E4%B9%88%E7%A7%BB%E9%99%A4-%E5%A6%82%E4%BD%95%E8%87%AA%E8%A1%8C%E9%9B%86%E6%88%90)
[点击此处查看为什么移除?如何自行集成?](https://pure-admin.github.io/pure-admin-doc/pages/FAQ/#%E5%B9%B3%E5%8F%B0%E5%9C%A8-v3-9-4-%E7%89%88%E6%9C%AC%E5%AE%8C%E5%85%A8%E7%A7%BB%E9%99%A4%E4%BA%86-vxe-table-%E4%B8%BA%E4%BB%80%E4%B9%88%E7%A7%BB%E9%99%A4-%E5%A6%82%E4%BD%95%E8%87%AA%E8%A1%8C%E9%9B%86%E6%88%90)
### 🎫 Feat
@@ -474,13 +544,13 @@
# 3.7.0 (2022-11-21)
### ✔️ refactor
### ✔️ Refactor
- 使用 `intro.js` 替换 `driver.js`
### 🎫 Feat
- 添加前端单点登录,测试地址 https://yiming_chang.gitee.io/vue-pure-admin/#/pure-table/index?username=sso&roles=admin&accessToken=eyJhbGciOiJIUzUxMiJ9.admin
- 添加前端单点登录,测试地址 https://pure-admin.github.io/vue-pure-admin/#/pure-table/index?username=sso&roles=admin&accessToken=eyJhbGciOiJIUzUxMiJ9.admin
- 为 [@pureadmin/table](https://github.com/pure-admin/pure-admin-table) 添加更多的示例和 `element-plus` 的 [table](https://element-plus.org/zh-CN/component/table.html) 示例保持一致
- 丰富水印功能页面(支持自定义各种颜色、阴影、文字、额外属性、设置不可删除水印以及给指定元素设置水印)
- 优化菜单,添加 `MenuArrowIconNoTransition` 全局配置,在 `public/platform-config.json` 中配置即可,对于出现左侧菜单模式,菜单展开卡顿的可设置 `MenuArrowIconNoTransition: true` 即可解决
@@ -525,7 +595,7 @@
# 3.6.2 (2022-10-27)
### ✔️ refactor
### ✔️ Refactor
- 使用`@/`别名替换`/@/`别名
@@ -553,7 +623,7 @@
- 添加打字机组件`demo`
- 添加`json`编辑器`demo`
### ✔️ refactor
### ✔️ Refactor
- 重构权限模块,采用目前最常用的`RBAC`Role-Based Access List: 基于角色的权限控制( 用户 -> 角色 -> 权限 ),并更新页面权限和按钮权限`demo`示例,按钮权限支持三种操作模式(组件方式判断权限、函数方式判断权限、指令方式判断权限)
@@ -579,9 +649,9 @@
- 添加 `element-plus` 无缝滚动 `Table` 页面 demo
- 开启 `vscode` 括号对指南
### ✔️ refactor
### ✔️ Refactor
- 使用 `tailwindcss` 替换 `unocss`,新增 `tailwindcss` [使用文档](https://yiming_chang.gitee.io/pure-admin-doc/pages/tailwindcss/)
- 使用 `tailwindcss` 替换 `unocss`,新增 `tailwindcss` [使用文档](https://pure-admin.github.io/pure-admin-doc/pages/tailwindcss/)
### 🐞 Bug fixes
@@ -629,7 +699,7 @@
- 添加导出 `excel` 页面 demo
- 添加无 `Layout` 的空白页面 demo
### ✔️ refactor
### ✔️ Refactor
- 重构主题色,适配 `element-plus` 暗黑模式(同时也解决了 `3.3.0` 及更低版本中同样的元素 `css` 被多次覆盖,导致样式不好调试的问题)
- 重构路由重置功能
@@ -665,7 +735,7 @@
- 添加预发布打包模式
- 添加关闭某个标签的[hooks](https://github.com/pure-admin/vue-pure-admin/commit/5e8723a031923e79f507e5a17151d3bd88a51523)
### ✔️ refactor
### ✔️ Refactor
- 重构登录页,更偏向实际业务场景
- 使用`unocss`替换`windicss``unocss`开发环境下性能更好,没有内存泄露,而且`api`使用上兼容`windicss`
@@ -744,7 +814,7 @@
- 添加 `WindiCSS` 支持
- 添加线上环境删 console 插件`vite-plugin-remove-console`
### ✔️ refactor
### ✔️ Refactor
- 使用`@iconify-icons/ep`替换`@element-plus/icons-vue`

View File

@@ -1,8 +1,8 @@
FROM node:18-alpine as build-stage
FROM node:20-alpine as build-stage
WORKDIR /app
RUN corepack enable
RUN corepack prepare pnpm@8.6.10 --activate
RUN corepack prepare pnpm@latest --activate
RUN npm config set registry https://registry.npmmirror.com

View File

@@ -30,12 +30,12 @@ The simplified version is based on the shelf extracted from [vue-pure-admin](htt
## Nanny-level documents
[Click me to view vue-pure-admin documentation](https://yiming_chang.gitee.io/pure-admin-doc)
[Click me to view vue-pure-admin documentation](https://pure-admin.github.io/pure-admin-doc)
[Click me to view @pureadmin/utils documentation](https://pure-admin-utils.netlify.app)
## Quality service, software outsourcing, sponsorship support
[Click me for details](https://yiming_chang.gitee.io/pure-admin-doc/pages/service/)
[Click me for details](https://pure-admin.github.io/pure-admin-doc/pages/service/)
## Tauri
@@ -47,7 +47,7 @@ The simplified version is based on the shelf extracted from [vue-pure-admin](htt
## Preview
[preview station](https://yiming_chang.gitee.io/vue-pure-admin)
[preview station](https://pure-admin.github.io/vue-pure-admin)
`PC`
@@ -176,13 +176,14 @@ Thank you very much for your in-depth understanding of the source code and your
| **Contributor** | **SpecificCode** |
| :---------------------------------------------: | :------------------------------------------------------------------------------: |
| [hb0730](https://github.com/hb0730) | [代码](https://github.com/pure-admin/vue-pure-admin/commits?author=hb0730) |
| [o-cc](https://github.com/o-cc) | [代码](https://github.com/pure-admin/vue-pure-admin/commits?author=o-cc) |
| [yj-liuzepeng](https://github.com/yj-liuzepeng) | [代码](https://github.com/pure-admin/vue-pure-admin/commits?author=yj-liuzepeng) |
| [skyline523](https://github.com/skyline523) | [代码](https://github.com/pure-admin/vue-pure-admin/commits?author=skyline523) |
| [shark-lajiao](https://github.com/shark-lajiao) | [代码](https://github.com/pure-admin/vue-pure-admin/commits?author=shark-lajiao) |
| [WitMiao](https://github.com/WitMiao) | [代码](https://github.com/pure-admin/vue-pure-admin/commits?author=WitMiao) |
| [QFifteen](https://github.com/QFifteen) | [代码](https://github.com/pure-admin/vue-pure-admin/commits?author=QFifteen) |
| [hb0730](https://github.com/hb0730) | [code](https://github.com/pure-admin/vue-pure-admin/commits?author=hb0730) |
| [o-cc](https://github.com/o-cc) | [code](https://github.com/pure-admin/vue-pure-admin/commits?author=o-cc) |
| [yj-liuzepeng](https://github.com/yj-liuzepeng) | [code](https://github.com/pure-admin/vue-pure-admin/commits?author=yj-liuzepeng) |
| [skyline523](https://github.com/skyline523) | [code](https://github.com/pure-admin/vue-pure-admin/commits?author=skyline523) |
| [shark-lajiao](https://github.com/shark-lajiao) | [code](https://github.com/pure-admin/vue-pure-admin/commits?author=shark-lajiao) |
| [WitMiao](https://github.com/WitMiao) | [code](https://github.com/pure-admin/vue-pure-admin/commits?author=WitMiao) |
| [QFifteen](https://github.com/QFifteen) | [code](https://github.com/pure-admin/vue-pure-admin/commits?author=QFifteen) |
| [edgexie](https://github.com/edgexie) | [code](https://github.com/pure-admin/vue-pure-admin/commits?author=edgexie) |
## Git Contribution submission specification

View File

@@ -31,12 +31,12 @@
## 配套保姆级文档
[点我查看 vue-pure-admin 文档](https://yiming_chang.gitee.io/pure-admin-doc)
[点我查看 vue-pure-admin 文档](https://pure-admin.github.io/pure-admin-doc)
[点我查看 @pureadmin/utils 文档](https://pure-admin-utils.netlify.app)
## 优质服务、软件外包、赞助支持
[点我查看详情](https://yiming_chang.gitee.io/pure-admin-doc/pages/service/)
[点我查看详情](https://pure-admin.github.io/pure-admin-doc/pages/service/)
## `Tauri` 版本
@@ -48,7 +48,7 @@
## 预览
[点我查看预览](https://yiming_chang.gitee.io/vue-pure-admin)
[点我查看预览](https://pure-admin.github.io/vue-pure-admin)
`PC`
@@ -184,6 +184,7 @@ docker run -dp 8080:80 --name pure-admin vue-pure-admin
| [shark-lajiao](https://github.com/shark-lajiao) | [代码](https://github.com/pure-admin/vue-pure-admin/commits?author=shark-lajiao) |
| [WitMiao](https://github.com/WitMiao) | [代码](https://github.com/pure-admin/vue-pure-admin/commits?author=WitMiao) |
| [QFifteen](https://github.com/QFifteen) | [代码](https://github.com/pure-admin/vue-pure-admin/commits?author=QFifteen) |
| [edgexie](https://github.com/edgexie) | [代码](https://github.com/pure-admin/vue-pure-admin/commits?author=edgexie) |
## `Git` 贡献提交规范

View File

@@ -7,7 +7,7 @@ import boxen, { type Options as BoxenOptions } from "boxen";
dayjs.extend(duration);
const welcomeMessage = gradientString("cyan", "magenta").multiline(
`您好! 欢迎使用 pure-admin 开源项目\n我们为您精心准备了下面两个贴心的保姆级文档\nhttps://yiming_chang.gitee.io/pure-admin-doc\nhttps://pure-admin-utils.netlify.app`
`您好! 欢迎使用 pure-admin 开源项目\n我们为您精心准备了下面两个贴心的保姆级文档\nhttps://pure-admin.github.io/pure-admin-doc\nhttps://pure-admin-utils.netlify.app`
);
const boxenOptions: BoxenOptions = {

View File

@@ -36,8 +36,6 @@ const include = [
"vue-json-pretty",
"@logicflow/core",
"@pureadmin/utils",
"@vue-office/docx",
"@vue-office/excel",
"@wangeditor/editor",
"responsive-storage",
"plus-pro-components",

View File

@@ -24,8 +24,7 @@ export function getPluginsList(
// jsx、tsx语法支持
vueJsx(),
VueI18nPlugin({
runtimeOnly: true,
compositionOnly: true,
jitCompilation: false,
include: [pathResolve("../locales/**")]
}),
viteBuildInfo(),

View File

@@ -48,7 +48,7 @@ const __APP_INFO__ = {
};
/** 处理环境变量 */
const warpperEnv = (envConf: Recordable): ViteEnv => {
const wrapperEnv = (envConf: Recordable): ViteEnv => {
// 默认值
const ret: ViteEnv = {
VITE_PORT: 8848,
@@ -107,4 +107,4 @@ const getPackageSize = options => {
});
};
export { root, pathResolve, alias, __APP_INFO__, warpperEnv, getPackageSize };
export { root, pathResolve, alias, __APP_INFO__, wrapperEnv, getPackageSize };

View File

@@ -10,7 +10,14 @@ import pluginTypeScript from "@typescript-eslint/eslint-plugin";
export default defineFlatConfig([
{
...js.configs.recommended,
ignores: ["src/assets/**", "src/**/iconfont/**"],
ignores: [
"**/.*",
"dist/*",
"*.d.ts",
"public/*",
"src/assets/**",
"src/**/iconfont/**"
],
languageOptions: {
globals: {
// index.d.ts

View File

@@ -1,7 +1,8 @@
buttons:
pureAccountSettings: Account
pureLoginOut: LoginOut
pureLogin: Login
pureSystemSet: Open ProjectConfig
pureOpenSystemSet: Open System Configs
pureReload: Reload
pureCloseCurrentTab: Close CurrentTab
pureCloseLeftTabs: Close LeftTabs
@@ -10,6 +11,54 @@ buttons:
pureCloseAllTabs: Close AllTabs
pureContentFullScreen: Content FullScreen
pureContentExitFullScreen: Content ExitFullScreen
pureClickCollapse: Collapse
pureClickExpand: Expand
pureConfirm: Confirm
pureSwitch: Switch
pureClose: Close
pureBackTop: BackTop
pureOpenText: Open
pureCloseText: Close
search:
pureTotal: Total
pureHistory: History
pureCollect: Collect
pureDragSort: Drag Sort
pureEmpty: Empty
purePlaceholder: Search Menu
panel:
pureSystemSet: System Configs
pureCloseSystemSet: Close System Configs
pureClearCacheAndToLogin: Clear cache and return to login page
pureClearCache: Clear Cache
pureOverallStyle: Overall Style
pureOverallStyleLight: Light
pureOverallStyleLightTip: Set sail freshly and light up the comfortable work interface
pureOverallStyleDark: Dark
pureOverallStyleDarkTip: Moonlight Overture, indulge in the tranquility and elegance of the night
pureOverallStyleSystem: Auto
pureOverallStyleSystemTip: Synchronize time, the interface naturally responds to morning and dusk
pureThemeColor: Theme Color
pureLayoutModel: Layout Model
pureVerticalTip: The menu on the left is familiar and friendly
pureHorizontalTip: Top menu, concise overview
pureMixTip: Mixed menu, flexible
pureStretch: Stretch Page
pureStretchFixed: Fixed
pureStretchFixedTip: Compact pages make it easy to find the information you need
pureStretchCustom: Custom
pureStretchCustomTip: Minimum 1280, maximum 1600
pureTagsStyle: Tags Style
pureTagsStyleSmart: Smart
pureTagsStyleSmartTip: Smart tags add fun and brilliance
pureTagsStyleCard: Card
pureTagsStyleCardTip: Card tags for efficient browsing
pureInterfaceDisplay: Interface Display
pureGreyModel: Grey Model
pureWeakModel: Weak Model
pureHiddenTags: Hidden Tags
pureHiddenFooter: Hidden Footer
pureMultiTagsCache: MultiTags Cache
menus:
pureHome: Home
pureLogin: Login
@@ -108,7 +157,7 @@ menus:
pureTimeline: Time Line
pureLineTree: LineTree
pureList: List Page
pureListCard: Card List Page
pureCardList: Card List Page
pureDebounce: Debounce & Throttle
pureFormDesign: Form Design
pureBarcode: Barcode
@@ -117,8 +166,6 @@ menus:
pureSwiper: Swiper Plugin
pureVirtualList: Virtual List
purePdf: PDF Preview
pureWord: Word Preview
pureExcels: Excel Preview
pureExcel: Export Excel
pureInfiniteScroll: Table Infinite Scroll
pureSensitive: Sensitive Filter
@@ -130,10 +177,17 @@ menus:
pureTableEdit: Edit Usage
pureVxeTable: Virtual Usage
pureBoard: Paint Board
pureMindMap: Mind Map
pureMenuOverflow: Menu Overflow Show Tooltip Text
pureChildMenuOverflow: Child Menu Overflow Show Tooltip Text
status:
pureLoad: Loading...
pureMessage: Message
pureNotify: Notify
pureTodo: Todo
pureNoMessage: No Message
pureNoNotify: No Notify
pureNoTodo: No Todo
login:
pureUsername: Username
purePassword: Password
@@ -158,6 +212,7 @@ login:
pureTip: After scanning the code, click "Confirm" to complete the login
pureDefinite: Definite
pureLoginSuccess: Login Success
pureLoginFail: Login Fail
pureRegisterSuccess: Regist Success
pureTickPrivacy: Please tick Privacy Policy
pureReadAccept: I have read it carefully and accept
@@ -174,4 +229,4 @@ login:
purePassWordRuleReg: The password format should be any combination of 8-18 digits
purePassWordSureReg: Please enter confirm password
purePassWordDifferentReg: The two passwords do not match!
purePassWordUpdateReg: Password has been updated
purePassWordUpdateReg: Password has been updated

View File

@@ -1,7 +1,8 @@
buttons:
pureAccountSettings: 账户设置
pureLoginOut: 退出系统
pureLogin: 登录
pureSystemSet: 打开项目配置
pureOpenSystemSet: 打开系统配置
pureReload: 重新加载
pureCloseCurrentTab: 关闭当前标签页
pureCloseLeftTabs: 关闭左侧标签页
@@ -10,6 +11,54 @@ buttons:
pureCloseAllTabs: 关闭全部标签页
pureContentFullScreen: 内容区全屏
pureContentExitFullScreen: 内容区退出全屏
pureClickCollapse: 点击折叠
pureClickExpand: 点击展开
pureConfirm: 确认
pureSwitch: 切换
pureClose: 关闭
pureBackTop: 回到顶部
pureOpenText:
pureCloseText:
search:
pureTotal:
pureHistory: 搜索历史
pureCollect: 收藏
pureDragSort: (可拖拽排序)
pureEmpty: 暂无搜索结果
purePlaceholder: 搜索菜单(支持拼音搜索)
panel:
pureSystemSet: 系统配置
pureCloseSystemSet: 关闭配置
pureClearCacheAndToLogin: 清空缓存并返回登录页
pureClearCache: 清空缓存
pureOverallStyle: 整体风格
pureOverallStyleLight: 浅色
pureOverallStyleLightTip: 清新启航,点亮舒适的工作界面
pureOverallStyleDark: 深色
pureOverallStyleDarkTip: 月光序曲,沉醉于夜的静谧雅致
pureOverallStyleSystem: 自动
pureOverallStyleSystemTip: 同步时光,界面随晨昏自然呼应
pureThemeColor: 主题色
pureLayoutModel: 导航模式
pureVerticalTip: 左侧菜单,亲切熟悉
pureHorizontalTip: 顶部菜单,简洁概览
pureMixTip: 混合菜单,灵活多变
pureStretch: 页宽
pureStretchFixed: 固定
pureStretchFixedTip: 紧凑页面,轻松找到所需信息
pureStretchCustom: 自定义
pureStretchCustomTip: 最小1280、最大1600
pureTagsStyle: 页签风格
pureTagsStyleSmart: 灵动
pureTagsStyleSmartTip: 灵动标签,添趣生辉
pureTagsStyleCard: 卡片
pureTagsStyleCardTip: 卡片标签,高效浏览
pureInterfaceDisplay: 界面显示
pureGreyModel: 灰色模式
pureWeakModel: 色弱模式
pureHiddenTags: 隐藏标签页
pureHiddenFooter: 隐藏页脚
pureMultiTagsCache: 页签持久化
menus:
pureHome: 首页
pureLogin: 登录
@@ -108,7 +157,7 @@ menus:
pureTimeline: 时间线
pureLineTree: 树形连接线
pureList: 列表页面
pureListCard: 卡片列表页
pureCardList: 卡片列表页
pureDebounce: 防抖节流
pureFormDesign: 表单设计器
pureBarcode: 条形码
@@ -117,8 +166,6 @@ menus:
pureSwiper: Swiper插件
pureVirtualList: 虚拟列表
purePdf: PDF预览
pureWord: Word预览
pureExcels: Excel预览
pureExcel: 导出Excel
pureInfiniteScroll: 表格无限滚动
pureSensitive: 敏感词过滤
@@ -130,10 +177,17 @@ menus:
pureTableEdit: 可编辑用法
pureVxeTable: 虚拟滚动
pureBoard: 艺术画板
pureMindMap: 思维导图
pureMenuOverflow: 目录超出显示 Tooltip 文字提示
pureChildMenuOverflow: 菜单超出显示 Tooltip 文字提示
status:
pureLoad: 加载中...
pureMessage: 消息
pureNotify: 通知
pureTodo: 待办
pureNoMessage: 暂无消息
pureNoNotify: 暂无通知
pureNoTodo: 暂无待办
login:
pureUsername: 账号
purePassword: 密码
@@ -158,6 +212,7 @@ login:
pureTip: 扫码后点击"确认",即可完成登录
pureDefinite: 确定
pureLoginSuccess: 登录成功
pureLoginFail: 登录失败
pureRegisterSuccess: 注册成功
pureTickPrivacy: 请勾选隐私政策
pureReadAccept: 我已仔细阅读并接受

View File

@@ -146,30 +146,6 @@ const frameRouter = {
rank: frame
},
children: [
{
path: "/iframe/external",
meta: {
title: "menus.pureExternalDoc"
},
children: [
{
path: "/external",
name: "https://yiming_chang.gitee.io/pure-admin-doc",
meta: {
title: "menus.pureExternalLink",
roles: ["admin", "common"]
}
},
{
path: "/pureUtilsLink",
name: "https://pure-admin-utils.netlify.app/",
meta: {
title: "menus.pureUtilsLink",
roles: ["admin", "common"]
}
}
]
},
{
path: "/iframe/embedded",
meta: {
@@ -257,6 +233,30 @@ const frameRouter = {
}
}
]
},
{
path: "/iframe/external",
meta: {
title: "menus.pureExternalDoc"
},
children: [
{
path: "/external",
name: "https://pure-admin.github.io/pure-admin-doc",
meta: {
title: "menus.pureExternalLink",
roles: ["admin", "common"]
}
},
{
path: "/pureUtilsLink",
name: "https://pure-admin-utils.netlify.app/",
meta: {
title: "menus.pureUtilsLink",
roles: ["admin", "common"]
}
}
]
}
]
};

View File

@@ -10,7 +10,9 @@ export default defineFakeRoute([
return {
success: true,
data: {
avatar: "https://avatars.githubusercontent.com/u/44761321",
username: "admin",
nickname: "小铭",
// 一个用户可能有多个角色
roles: ["admin"],
accessToken: "eyJhbGciOiJIUzUxMiJ9.admin",
@@ -22,8 +24,9 @@ export default defineFakeRoute([
return {
success: true,
data: {
avatar: "https://avatars.githubusercontent.com/u/52823142",
username: "common",
// 一个用户可能有多个角色
nickname: "小林",
roles: ["common"],
accessToken: "eyJhbGciOiJIUzUxMiJ9.common",
refreshToken: "eyJhbGciOiJIUzUxMiJ9.commonRefresh",

59
mock/mine.ts Normal file
View File

@@ -0,0 +1,59 @@
import { defineFakeRoute } from "vite-plugin-fake-server/client";
import { faker } from "@faker-js/faker/locale/zh_CN";
export default defineFakeRoute([
// 账户设置-个人信息
{
url: "/mine",
method: "get",
response: () => {
return {
success: true,
data: {
avatar: "https://avatars.githubusercontent.com/u/44761321",
username: "admin",
nickname: "小铭",
email: "pureadmin@163.com",
phone: "15888886789",
description: "一个热爱开源的前端工程师"
}
};
}
},
// 账户设置-个人安全日志
{
url: "/mine-logs",
method: "get",
response: () => {
let list = [
{
id: 1,
ip: faker.internet.ipv4(),
address: "中国河南省信阳市",
system: "macOS",
browser: "Chrome",
summary: "账户登录", // 详情
operatingTime: new Date() // 时间
},
{
id: 2,
ip: faker.internet.ipv4(),
address: "中国广东省深圳市",
system: "Windows",
browser: "Firefox",
summary: "绑定了手机号码",
operatingTime: new Date().setDate(new Date().getDate() - 1)
}
];
return {
success: true,
data: {
list,
total: list.length, // 总条目数
pageSize: 10, // 每页显示条目个数
currentPage: 1 // 当前页数
}
};
}
}
]);

View File

@@ -9,9 +9,9 @@ export default defineFakeRoute([
response: ({ body }) => {
let list = [
{
username: "admin",
nickname: "admin",
avatar: "https://avatars.githubusercontent.com/u/44761321",
username: "admin",
nickname: "小铭",
phone: "15888886789",
email: faker.internet.email(),
sex: 0,
@@ -27,9 +27,9 @@ export default defineFakeRoute([
createTime: 1605456000000
},
{
username: "common",
nickname: "common",
avatar: "https://avatars.githubusercontent.com/u/52823142",
username: "common",
nickname: "小林",
phone: "18288882345",
email: faker.internet.email(),
sex: 1,
@@ -397,6 +397,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@@ -420,6 +421,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@@ -428,7 +430,7 @@ export default defineFakeRoute([
id: 102,
menuType: 2,
title: "menus.pureExternalLink",
name: "https://yiming_chang.gitee.io/pure-admin-doc",
name: "https://pure-admin.github.io/pure-admin-doc",
path: "/external",
component: "",
rank: null,
@@ -443,6 +445,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@@ -466,6 +469,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@@ -489,6 +493,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@@ -512,6 +517,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: true,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@@ -535,6 +541,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: true,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@@ -558,6 +565,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: true,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@@ -581,6 +589,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: true,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@@ -604,6 +613,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: true,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@@ -627,6 +637,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: true,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@@ -651,6 +662,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@@ -674,6 +686,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@@ -697,6 +710,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@@ -720,6 +734,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@@ -743,6 +758,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@@ -766,6 +782,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@@ -790,6 +807,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@@ -813,6 +831,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@@ -836,6 +855,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@@ -859,6 +879,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@@ -882,6 +903,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@@ -906,6 +928,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@@ -929,6 +952,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@@ -952,6 +976,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@@ -975,6 +1000,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@@ -998,6 +1024,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@@ -1022,6 +1049,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@@ -1045,6 +1073,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@@ -1068,6 +1097,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: false,
showParent: false
},
@@ -1091,6 +1121,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: false,
showParent: false
}
@@ -1472,6 +1503,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@@ -1495,6 +1527,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@@ -1518,6 +1551,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@@ -1541,6 +1575,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
},
@@ -1564,6 +1599,7 @@ export default defineFakeRoute([
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: false
}

View File

@@ -1,6 +1,6 @@
{
"name": "vue-pure-admin",
"version": "5.3.0",
"version": "5.6.0",
"private": true,
"type": "module",
"scripts": {
@@ -13,7 +13,6 @@
"preview:build": "pnpm build && vite preview",
"typecheck": "tsc --noEmit && vue-tsc --noEmit --skipLibCheck",
"svgo": "svgo -f . -r",
"cloc": "NODE_OPTIONS=--max-old-space-size=4096 cloc . --exclude-dir=node_modules --exclude-lang=YAML",
"clean:cache": "rimraf .eslintcache && rimraf pnpm-lock.yaml && rimraf node_modules && pnpm store prune && pnpm install",
"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}\"",
@@ -50,16 +49,14 @@
"dependencies": {
"@amap/amap-jsapi-loader": "^1.0.1",
"@howdyjs/mouse-menu": "^2.1.3",
"@infectoone/vue-ganttastic": "^2.3.1",
"@logicflow/core": "^1.2.23",
"@logicflow/extension": "^1.2.23",
"@infectoone/vue-ganttastic": "^2.3.2",
"@logicflow/core": "^1.2.27",
"@logicflow/extension": "^1.2.27",
"@pureadmin/descriptions": "^1.2.1",
"@pureadmin/table": "^3.1.2",
"@pureadmin/utils": "^2.4.7",
"@vue-flow/background": "^1.3.0",
"@vue-flow/core": "^1.33.5",
"@vue-office/docx": "^1.6.0",
"@vue-office/excel": "^1.7.2",
"@vue-flow/core": "^1.33.7",
"@vueuse/core": "^10.9.0",
"@vueuse/motion": "^2.1.0",
"@wangeditor/editor": "^5.1.23",
@@ -68,11 +65,11 @@
"animate.css": "^4.1.1",
"axios": "^1.6.8",
"china-area-data": "^5.0.1",
"cropperjs": "^1.6.1",
"dayjs": "^1.11.10",
"cropperjs": "^1.6.2",
"dayjs": "^1.11.11",
"echarts": "^5.5.0",
"el-table-infinite-scroll": "^3.0.3",
"element-plus": "^2.6.2",
"element-plus": "^2.7.3",
"intro.js": "^7.2.0",
"js-cookie": "^3.0.5",
"jsbarcode": "^3.11.6",
@@ -83,89 +80,88 @@
"nprogress": "^0.2.0",
"path": "^0.12.7",
"pinia": "^2.1.7",
"pinyin-pro": "^3.19.6",
"plus-pro-components": "^0.0.5",
"pinyin-pro": "^3.20.4",
"plus-pro-components": "^0.1.3",
"qrcode": "^1.5.3",
"qs": "^6.12.0",
"qs": "^6.12.1",
"responsive-storage": "^2.2.0",
"sortablejs": "^1.15.2",
"swiper": "^11.0.7",
"swiper": "^11.1.2",
"typeit": "^8.8.3",
"v-contextmenu": "^3.2.0",
"v3-infinite-loading": "^1.3.1",
"version-rocket": "^1.7.1",
"vue": "^3.4.21",
"vue-i18n": "^9.10.2",
"vue": "^3.4.27",
"vue-i18n": "^9.13.1",
"vue-json-pretty": "^2.4.0",
"vue-pdf-embed": "^2.0.3",
"vue-router": "^4.3.0",
"vue-router": "^4.3.2",
"vue-tippy": "^6.4.1",
"vue-types": "^5.1.1",
"vue-types": "^5.1.2",
"vue-virtual-scroller": "2.0.0-beta.8",
"vue-waterfall-plugin-next": "^2.4.3",
"vue3-danmaku": "^1.6.0",
"vue3-puzzle-vcode": "^1.1.7",
"vuedraggable": "^4.1.0",
"vxe-table": "^4.5.21",
"wavesurfer.js": "^7.7.5",
"xgplayer": "^3.0.14",
"vxe-table": "^4.6.12",
"wavesurfer.js": "^7.7.14",
"xgplayer": "^3.0.17",
"xlsx": "^0.18.5"
},
"devDependencies": {
"@commitlint/cli": "^19.2.1",
"@commitlint/config-conventional": "^19.1.0",
"@commitlint/cli": "^19.3.0",
"@commitlint/config-conventional": "^19.2.2",
"@commitlint/types": "^19.0.3",
"@eslint/js": "^8.57.0",
"@eslint/js": "^9.2.0",
"@faker-js/faker": "^8.4.1",
"@iconify-icons/ep": "^1.2.12",
"@iconify-icons/ri": "^1.2.10",
"@iconify/vue": "^4.1.1",
"@intlify/unplugin-vue-i18n": "^2.0.0",
"@iconify/vue": "^4.1.2",
"@intlify/unplugin-vue-i18n": "^4.0.0",
"@pureadmin/theme": "^3.2.0",
"@types/dagre": "^0.7.52",
"@types/gradient-string": "^1.1.5",
"@types/gradient-string": "^1.1.6",
"@types/intro.js": "^5.1.5",
"@types/js-cookie": "^3.0.6",
"@types/node": "^20.11.30",
"@types/node": "^20.12.11",
"@types/nprogress": "^0.2.3",
"@types/qrcode": "^1.5.5",
"@types/qs": "^6.9.14",
"@types/qs": "^6.9.15",
"@types/sortablejs": "^1.15.8",
"@typescript-eslint/eslint-plugin": "^7.4.0",
"@typescript-eslint/parser": "^7.4.0",
"@typescript-eslint/eslint-plugin": "^7.9.0",
"@typescript-eslint/parser": "^7.9.0",
"@vitejs/plugin-vue": "^5.0.4",
"@vitejs/plugin-vue-jsx": "^3.1.0",
"autoprefixer": "^10.4.19",
"boxen": "^7.1.1",
"cloc": "^2.11.0",
"cssnano": "^6.1.2",
"cssnano": "^7.0.1",
"dagre": "^0.8.5",
"eslint": "^8.57.0",
"eslint": "^9.2.0",
"eslint-config-prettier": "^9.1.0",
"eslint-define-config": "^2.1.0",
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-vue": "^9.24.0",
"eslint-plugin-vue": "^9.26.0",
"gradient-string": "^2.0.2",
"husky": "^9.0.11",
"lint-staged": "^15.2.2",
"postcss": "^8.4.38",
"postcss-html": "^1.6.0",
"postcss-html": "^1.7.0",
"postcss-import": "^16.1.0",
"postcss-scss": "^4.0.9",
"prettier": "^3.2.5",
"rimraf": "^5.0.5",
"rimraf": "^5.0.7",
"rollup-plugin-visualizer": "^5.12.0",
"sass": "^1.72.0",
"stylelint": "^16.3.1",
"stylelint-config-recess-order": "^5.0.0",
"sass": "^1.77.1",
"stylelint": "^16.5.0",
"stylelint-config-recess-order": "^5.0.1",
"stylelint-config-recommended-vue": "^1.5.0",
"stylelint-config-standard-scss": "^13.0.0",
"stylelint-config-standard-scss": "^13.1.0",
"stylelint-prettier": "^5.0.0",
"svgo": "^3.2.0",
"tailwindcss": "^3.4.1",
"typescript": "^5.4.3",
"vite": "^5.2.6",
"vite-plugin-cdn-import": "^0.3.5",
"svgo": "^3.3.2",
"tailwindcss": "^3.4.3",
"typescript": "^5.4.5",
"vite": "^5.2.11",
"vite-plugin-cdn-import": "^1.0.1",
"vite-plugin-compression": "^0.5.1",
"vite-plugin-fake-server": "^2.1.1",
"vite-plugin-remove-console": "^2.2.0",
@@ -176,9 +172,8 @@
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0",
"pnpm": ">=8.6.10"
"pnpm": ">=9"
},
"packageManager": "pnpm@8.6.10",
"pnpm": {
"allowedDeprecatedVersions": {
"sourcemap-codec": "*",
@@ -186,6 +181,11 @@
"w3c-hr-time": "*",
"stable": "*",
"abab": "*"
},
"peerDependencyRules": {
"allowedVersions": {
"eslint": "9"
}
}
}
}

13149
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -10,10 +10,10 @@ import { defineComponent } from "vue";
import { checkVersion } from "version-rocket";
import { ElConfigProvider } from "element-plus";
import { ReDialog } from "@/components/ReDialog";
import en from "element-plus/dist/locale/en.mjs";
import zhCn from "element-plus/dist/locale/zh-cn.mjs";
import plusEn from "plus-pro-components/locale/en.mjs";
import plusZhCn from "plus-pro-components/locale/zh-cn.mjs";
import en from "element-plus/es/locale/lang/en";
import zhCn from "element-plus/es/locale/lang/zh-cn";
import plusEn from "plus-pro-components/es/locale/lang/en";
import plusZhCn from "plus-pro-components/es/locale/lang/zh-cn";
export default defineComponent({
name: "app",

View File

@@ -3,9 +3,13 @@ import { http } from "@/utils/http";
export type UserResult = {
success: boolean;
data: {
/** 头像 */
avatar: string;
/** 用户名 */
username: string;
/** 当前登陆用户的角色 */
/** 昵称 */
nickname: string;
/** 当前登录用户的角色 */
roles: Array<string>;
/** `token` */
accessToken: string;
@@ -28,12 +32,56 @@ export type RefreshTokenResult = {
};
};
export type UserInfo = {
/** 头像 */
avatar: string;
/** 用户名 */
username: string;
/** 昵称 */
nickname: string;
/** 邮箱 */
email: string;
/** 联系电话 */
phone: string;
/** 简介 */
description: string;
};
export type UserInfoResult = {
success: boolean;
data: UserInfo;
};
type ResultTable = {
success: boolean;
data?: {
/** 列表数据 */
list: Array<any>;
/** 总条目数 */
total?: number;
/** 每页显示条目个数 */
pageSize?: number;
/** 当前页数 */
currentPage?: number;
};
};
/** 登录 */
export const getLogin = (data?: object) => {
return http.request<UserResult>("post", "/login", { data });
};
/** 刷新token */
/** 刷新`token` */
export const refreshTokenApi = (data?: object) => {
return http.request<RefreshTokenResult>("post", "/refresh-token", { data });
};
/** 账户设置-个人信息 */
export const getMine = (data?: object) => {
return http.request<UserInfoResult>("get", "/mine", { data });
};
/** 账户设置-个人安全日志 */
export const getMineLogs = (data?: object) => {
return http.request<ResultTable>("get", "/mine-logs", { data });
};

View File

Before

Width:  |  Height:  |  Size: 439 B

After

Width:  |  Height:  |  Size: 439 B

View File

Before

Width:  |  Height:  |  Size: 392 B

After

Width:  |  Height:  |  Size: 392 B

View File

Before

Width:  |  Height:  |  Size: 161 B

After

Width:  |  Height:  |  Size: 161 B

View File

Before

Width:  |  Height:  |  Size: 235 B

After

Width:  |  Height:  |  Size: 235 B

View File

Before

Width:  |  Height:  |  Size: 840 B

After

Width:  |  Height:  |  Size: 840 B

View File

@@ -7,7 +7,7 @@ defineOptions({
name: "ReAnimateSelector"
});
const props = defineProps({
defineProps({
placeholder: {
type: String,
default: "请选择动画"
@@ -81,7 +81,7 @@ function onMouseleave() {
<el-select
clearable
filterable
:placeholder="props.placeholder"
:placeholder="placeholder"
popper-class="pure-animate-popper"
:model-value="inputValue"
:filter-method="filterMethod"

View File

@@ -61,7 +61,7 @@ export default defineComponent({
state.rAF = requestAnimationFrame(count);
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars,no-unused-vars
// eslint-disable-next-line @typescript-eslint/no-unused-vars
function pauseResume() {
if (state.paused) {
resume();
@@ -83,7 +83,7 @@ export default defineComponent({
requestAnimationFrame(count);
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars,no-unused-vars
// eslint-disable-next-line @typescript-eslint/no-unused-vars
function reset() {
state.startTime = null;
cancelAnimationFrame(state.rAF);

View File

@@ -0,0 +1,7 @@
import reCropperPreview from "./src/index.vue";
import { withInstall } from "@pureadmin/utils";
/** 图片裁剪预览组件 */
export const ReCropperPreview = withInstall(reCropperPreview);
export default ReCropperPreview;

View File

@@ -3,7 +3,11 @@ import { ref } from "vue";
import ReCropper from "@/components/ReCropper";
import { formatBytes } from "@pureadmin/utils";
const props = defineProps({
defineOptions({
name: "ReCropperPreview"
});
defineProps({
imgSrc: String
});
@@ -40,7 +44,7 @@ defineExpose({ hidePopover });
<div class="w-[18vw]">
<ReCropper
ref="refCropper"
:src="props.imgSrc"
:src="imgSrc"
circled
@cropper="onCropper"
@readied="showPopover = true"

View File

@@ -29,9 +29,11 @@ const addDialog = (options: DialogOptions) => {
const closeDialog = (options: DialogOptions, index: number, args?: any) => {
dialogStore.value[index].visible = false;
options.closeCallBack && options.closeCallBack({ options, index, args });
const closeDelay = options?.closeDelay ?? 200;
useTimeoutFn(() => {
dialogStore.value.splice(index, 1);
}, 200);
}, closeDelay);
};
/**

View File

@@ -11,6 +11,10 @@ import { isFunction } from "@pureadmin/utils";
import Fullscreen from "@iconify-icons/ri/fullscreen-fill";
import ExitFullscreen from "@iconify-icons/ri/fullscreen-exit-fill";
defineOptions({
name: "ReDialog"
});
const fullscreen = ref(false);
const footerButtons = computed(() => {
@@ -37,6 +41,7 @@ const footerButtons = computed(() => {
type: "primary",
text: true,
bg: true,
popconfirm: options?.popconfirm,
btnClick: ({ dialog: { options, index } }) => {
const done = () =>
closeDialog(options, index, { command: "sure" });
@@ -149,19 +154,34 @@ function handleClose(
<component :is="options?.footerRenderer({ options, index })" />
</template>
<span v-else>
<el-button
v-for="(btn, key) in footerButtons(options)"
:key="key"
v-bind="btn"
@click="
btn.btnClick({
dialog: { options, index },
button: { btn, index: key }
})
"
>
{{ btn?.label }}
</el-button>
<template v-for="(btn, key) in footerButtons(options)" :key="key">
<el-popconfirm
v-if="btn.popconfirm"
v-bind="btn.popconfirm"
@confirm="
btn.btnClick({
dialog: { options, index },
button: { btn, index: key }
})
"
>
<template #reference>
<el-button v-bind="btn">{{ btn?.label }}</el-button>
</template>
</el-popconfirm>
<el-button
v-else
v-bind="btn"
@click="
btn.btnClick({
dialog: { options, index },
button: { btn, index: key }
})
"
>
{{ btn?.label }}
</el-button>
</template>
</span>
</template>
</el-dialog>

View File

@@ -11,6 +11,13 @@ type ArgsType = {
/** `cancel` 点击取消按钮、`sure` 点击确定按钮、`close` 点击右上角关闭按钮或空白页或按下了esc键 */
command: "cancel" | "sure" | "close";
};
type ButtonType =
| "primary"
| "success"
| "warning"
| "danger"
| "info"
| "text";
/** https://element-plus.org/zh-CN/component/dialog.html#attributes */
type DialogProps = {
@@ -58,6 +65,34 @@ type DialogProps = {
destroyOnClose?: boolean;
};
//element-plus.org/zh-CN/component/popconfirm.html#attributes
type Popconfirm = {
/** 标题 */
title?: string;
/** 确认按钮文字 */
confirmButtonText?: string;
/** 取消按钮文字 */
cancelButtonText?: string;
/** 确认按钮类型,默认 `primary` */
confirmButtonType?: ButtonType;
/** 取消按钮类型,默认 `text` */
cancelButtonType?: ButtonType;
/** 自定义图标,默认 `QuestionFilled` */
icon?: string | Component;
/** `Icon` 颜色,默认 `#f90` */
iconColor?: string;
/** 是否隐藏 `Icon`,默认 `false` */
hideIcon?: boolean;
/** 关闭时的延迟,默认 `200` */
hideAfter?: number;
/** 是否将 `popover` 的下拉列表插入至 `body` 元素,默认 `true` */
teleported?: boolean;
/** 当 `popover` 组件长时间不触发且 `persistent` 属性设置为 `false` 时, `popover` 将会被删除,默认 `false` */
persistent?: boolean;
/** 弹层宽度,最小宽度 `150px`,默认 `150` */
width?: string | number;
};
type BtnClickDialog = {
options?: DialogOptions;
index?: number;
@@ -86,6 +121,8 @@ type ButtonProps = {
round?: boolean;
/** 是否为圆形按钮,默认 `false` */
circle?: boolean;
/** 确认按钮的 `Popconfirm` 气泡确认框相关配置 */
popconfirm?: Popconfirm;
/** 是否为加载中状态,默认 `false` */
loading?: boolean;
/** 自定义加载中状态图标组件 */
@@ -123,6 +160,8 @@ interface DialogOptions extends DialogProps {
props?: any;
/** 是否隐藏 `Dialog` 按钮操作区的内容 */
hideFooter?: boolean;
/** 确认按钮的 `Popconfirm` 气泡确认框相关配置 */
popconfirm?: Popconfirm;
/**
* @description 自定义对话框标题的内容渲染器
* @see {@link https://element-plus.org/zh-CN/component/dialog.html#%E8%87%AA%E5%AE%9A%E4%B9%89%E5%A4%B4%E9%83%A8}

View File

@@ -2,6 +2,10 @@
import flippers from "./filpper";
import { ref, unref, nextTick, onUnmounted } from "vue";
defineOptions({
name: "ReFlop"
});
const timer = ref(null);
const flipObjs = ref([]);

View File

@@ -2,7 +2,7 @@
import VueJsonPretty from "vue-json-pretty";
import "vue-json-pretty/lib/styles.css";
const props = defineProps({
defineProps({
graphData: Object
});
</script>
@@ -12,6 +12,6 @@ const props = defineProps({
:path="'res'"
:deep="3"
:showLength="true"
:data="props.graphData"
:data="graphData"
/>
</template>

View File

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

View File

@@ -4,7 +4,7 @@ import { IconifyIconOnline, IconifyIconOffline, FontIcon } from "../index";
/**
* 支持 `iconfont`、自定义 `svg` 以及 `iconify` 中所有的图标
* @see 点击查看文档图标篇 {@link https://yiming_chang.gitee.io/pure-admin-doc/pages/icon/}
* @see 点击查看文档图标篇 {@link https://pure-admin.github.io/pure-admin-doc/pages/icon/}
* @param icon 必传 图标
* @param attrs 可选 iconType 属性
* @returns Component

View File

@@ -31,6 +31,7 @@ import Links from "@iconify-icons/ri/links-fill";
import Search from "@iconify-icons/ri/search-line";
import FlUser from "@iconify-icons/ri/admin-line";
import Setting from "@iconify-icons/ri/settings-3-line";
import MindMap from "@iconify-icons/ri/mind-map";
import BarChart from "@iconify-icons/ri/bar-chart-horizontal-line";
import LoginLog from "@iconify-icons/ri/window-line";
import Artboard from "@iconify-icons/ri/artboard-line";
@@ -54,6 +55,7 @@ addIcon("ri:table-line", Table);
addIcon("ri:search-line", Search);
addIcon("ri:admin-line", FlUser);
addIcon("ri:settings-3-line", Setting);
addIcon("ri:mind-map", MindMap);
addIcon("ri:bar-chart-horizontal-line", BarChart);
addIcon("ri:window-line", LoginLog);
addIcon("ri:file-search-line", SystemLog);

View File

@@ -1,7 +1,15 @@
import Sortable from "sortablejs";
import { transformI18n } from "@/plugins/i18n";
import { useEpThemeStoreHook } from "@/store/modules/epTheme";
import { defineComponent, ref, computed, type PropType, nextTick } from "vue";
import {
type PropType,
ref,
unref,
computed,
nextTick,
defineComponent,
getCurrentInstance
} from "vue";
import {
delay,
cloneDeep,
@@ -10,11 +18,11 @@ import {
getKeyList
} from "@pureadmin/utils";
import DragIcon from "./svg/drag.svg?component";
import ExpandIcon from "./svg/expand.svg?component";
import RefreshIcon from "./svg/refresh.svg?component";
import SettingIcon from "./svg/settings.svg?component";
import CollapseIcon from "./svg/collapse.svg?component";
import DragIcon from "@/assets/table-bar/drag.svg?component";
import ExpandIcon from "@/assets/table-bar/expand.svg?component";
import RefreshIcon from "@/assets/table-bar/refresh.svg?component";
import SettingIcon from "@/assets/table-bar/settings.svg?component";
import CollapseIcon from "@/assets/table-bar/collapse.svg?component";
const props = {
/** 头部最左边的标题 */
@@ -34,6 +42,10 @@ const props = {
isExpandAll: {
type: Boolean,
default: true
},
tableKey: {
type: [String, Number] as PropType<string | number>,
default: "0"
}
};
@@ -46,6 +58,7 @@ export default defineComponent({
const loading = ref(false);
const checkAll = ref(true);
const isIndeterminate = ref(false);
const instance = getCurrentInstance()!;
const isExpandAll = ref(props.isExpandAll);
const filterColumns = cloneDeep(props?.columns).filter(column =>
isBoolean(column?.hide)
@@ -170,9 +183,9 @@ export default defineComponent({
const rowDrop = (event: { preventDefault: () => void }) => {
event.preventDefault();
nextTick(() => {
const wrapper: HTMLElement = document.querySelector(
".el-checkbox-group>div"
);
const wrapper: HTMLElement = (
instance?.proxy?.$refs[`GroupRef${unref(props.tableKey)}`] as any
).$el.firstElementChild;
Sortable.create(wrapper, {
animation: 300,
handle: ".drag-btn",
@@ -299,6 +312,7 @@ export default defineComponent({
<div class="pt-[6px] pl-[11px]">
<el-scrollbar max-height="36vh">
<el-checkbox-group
ref={`GroupRef${unref(props.tableKey)}`}
modelValue={checkedColumns.value}
onChange={value => handleCheckedColumnsChange(value)}
>

View File

@@ -307,7 +307,7 @@ function touchMove(e) {
function touchEnd() {
if (!unref(canTouchScroll)) return;
// eslint-disable-next-line prefer-const
let timer: any;
const direction = unref(options).direction;
delay.value = 50;
@@ -502,7 +502,7 @@ defineExpose({
</script>
<template>
<div :ref="'wrap' + props.classOption['key']">
<div :ref="'wrap' + classOption['key']">
<div
v-if="navigation"
:style="leftSwitch"
@@ -520,7 +520,7 @@ defineExpose({
<slot name="right-switch" />
</div>
<div
:ref="'realBox' + props.classOption['key']"
:ref="'realBox' + classOption['key']"
:style="pos"
@mouseenter="enter"
@mouseleave="leave"
@@ -529,7 +529,7 @@ defineExpose({
@touchend="touchEnd"
@mousewheel.passive="wheel"
>
<div :ref="'slotList' + props.classOption['key']" :style="float">
<div :ref="'slotList' + classOption['key']" :style="float">
<slot />
</div>
<div :style="float" v-html="copyHtml" />

View File

@@ -60,14 +60,12 @@ export function copyObj() {
copyIsArray,
clone,
i = 1,
// eslint-disable-next-line prefer-rest-params
target = arguments[0] || {}, // 使用||运算符排除隐式强制类型转换为false的数据类型
deep = false,
// eslint-disable-next-line prefer-const
len = arguments.length;
if (typeof target === "boolean") {
deep = target;
// eslint-disable-next-line prefer-rest-params
target = arguments[1] || {};
i++;
}
@@ -80,7 +78,7 @@ export function copyObj() {
}
for (; i < len; i++) {
//所以如果源对象中数据类型为Undefined或Null那么就会跳过本次循环接着循环下一个源对象
// eslint-disable-next-line prefer-rest-params
if ((options = arguments[i]) != null) {
// 如果遇到源对象的数据类型为Boolean, Number for in循环会被跳过不执行for in循环// src用于判断target对象是否存在name属性
for (name in options) {

View File

@@ -34,9 +34,19 @@ const props = {
type: Boolean,
default: false
},
/** 控件尺寸 */
/** 控件尺寸 */
size: {
type: String as PropType<"small" | "default" | "large">
},
/** 是否全局禁用,默认 `false` */
disabled: {
type: Boolean,
default: false
},
/** 当内容发生变化时,设置 `resize` 可使其自适应容器位置 */
resize: {
type: Boolean,
default: false
}
};
@@ -57,7 +67,7 @@ export default defineComponent({
: ref(0);
function handleChange({ option, index }, event: Event) {
if (option.disabled) return;
if (props.disabled || option.disabled) return;
event.preventDefault();
isNumber(props.modelValue)
? emit("update:modelValue", index)
@@ -67,6 +77,7 @@ export default defineComponent({
}
function handleMouseenter({ option, index }, event: Event) {
if (props.disabled) return;
event.preventDefault();
curMouseActive.value = index;
if (option.disabled || curIndex.value === index) {
@@ -79,6 +90,7 @@ export default defineComponent({
}
function handleMouseleave(_, event: Event) {
if (props.disabled) return;
event.preventDefault();
curMouseActive.value = -1;
}
@@ -101,7 +113,7 @@ export default defineComponent({
});
}
props.block && handleResizeInit();
(props.block || props.resize) && handleResizeInit();
watch(
() => curIndex.value,
@@ -115,7 +127,9 @@ export default defineComponent({
}
);
watch(() => props.size, handleResizeInit);
watch(() => props.size, handleResizeInit, {
immediate: true
});
const rendLabel = () => {
return props.options.map((option, index) => {
@@ -124,14 +138,16 @@ export default defineComponent({
ref={`labelRef${index}`}
class={[
"pure-segmented-item",
option?.disabled && "pure-segmented-item-disabled"
(props.disabled || option?.disabled) &&
"pure-segmented-item-disabled"
]}
style={{
background:
curMouseActive.value === index ? segmentedItembg.value : "",
color:
!option.disabled &&
(curIndex.value === index || curMouseActive.value === index)
color: props.disabled
? null
: !option.disabled &&
(curIndex.value === index || curMouseActive.value === index)
? isDark.value
? "rgba(255, 255, 255, 0.85)"
: "rgba(0,0,0,.88)"

View File

@@ -6,7 +6,7 @@ export interface OptionsType {
label?: string | (() => VNode | Component);
/**
* @description 图标,采用平台内置的 `useRenderIcon` 函数渲染
* @see {@link 用法参考 https://yiming_chang.gitee.io/pure-admin-doc/pages/icon/#%E9%80%9A%E7%94%A8%E5%9B%BE%E6%A0%87-userendericon-hooks }
* @see {@link 用法参考 https://pure-admin.github.io/pure-admin-doc/pages/icon/#%E9%80%9A%E7%94%A8%E5%9B%BE%E6%A0%87-userendericon-hooks }
*/
icon?: string | Component;
/** 图标属性、样式配置 */

View File

@@ -44,7 +44,6 @@
(t.style.width = 0),
(t.style.height = 0),
(t.style.overflow = "hidden"),
// eslint-disable-next-line no-self-assign
(t = t),
(e = document.body).firstChild ? m(t, e.firstChild) : e.appendChild(t));
}),

View File

@@ -2,6 +2,10 @@
import { h, onMounted, ref, useSlots } from "vue";
import { type TippyOptions, useTippy } from "vue-tippy";
defineOptions({
name: "ReText"
});
const props = defineProps({
// 行数
lineClamp: {

View File

@@ -0,0 +1,5 @@
import vxeTableBar from "./src/bar";
import { withInstall } from "@pureadmin/utils";
/** 配合 `vxe-table` 实现快速便捷的表格操作 */
export const VxeTableBar = withInstall(vxeTableBar);

View File

@@ -0,0 +1,362 @@
import Sortable from "sortablejs";
import { transformI18n } from "@/plugins/i18n";
import { useEpThemeStoreHook } from "@/store/modules/epTheme";
import { delay, cloneDeep, getKeyList } from "@pureadmin/utils";
import {
type PropType,
ref,
unref,
computed,
nextTick,
defineComponent,
getCurrentInstance
} from "vue";
import DragIcon from "@/assets/table-bar/drag.svg?component";
import ExpandIcon from "@/assets/table-bar/expand.svg?component";
import RefreshIcon from "@/assets/table-bar/refresh.svg?component";
import SettingIcon from "@/assets/table-bar/settings.svg?component";
import CollapseIcon from "@/assets/table-bar/collapse.svg?component";
const props = {
/** 头部最左边的标题 */
title: {
type: String,
default: "列表"
},
vxeTableRef: {
type: Object as PropType<any>
},
/** 需要展示的列 */
columns: {
type: Array as PropType<any>,
default: () => []
},
/** 是否为树列表 */
tree: {
type: Boolean,
default: false
},
isExpandAll: {
type: Boolean,
default: true
},
tableKey: {
type: [String, Number] as PropType<string | number>,
default: "0"
}
};
export default defineComponent({
name: "VxeTableBar",
props,
emits: ["refresh"],
setup(props, { emit, slots, attrs }) {
const size = ref("small");
const loading = ref(false);
const checkAll = ref(true);
const isIndeterminate = ref(false);
const instance = getCurrentInstance()!;
const isExpandAll = ref(props.isExpandAll);
let checkColumnList = getKeyList(cloneDeep(props?.columns), "title");
const checkedColumns = ref(getKeyList(cloneDeep(props?.columns), "title"));
const dynamicColumns = ref(cloneDeep(props?.columns));
const getDropdownItemStyle = computed(() => {
return s => {
return {
background:
s === size.value ? useEpThemeStoreHook().epThemeColor : "",
color: s === size.value ? "#fff" : "var(--el-text-color-primary)"
};
};
});
const iconClass = computed(() => {
return [
"text-black",
"dark:text-white",
"duration-100",
"hover:!text-primary",
"cursor-pointer",
"outline-none"
];
});
const topClass = computed(() => {
return [
"flex",
"justify-between",
"pt-[3px]",
"px-[11px]",
"border-b-[1px]",
"border-solid",
"border-[#dcdfe6]",
"dark:border-[#303030]"
];
});
function onReFresh() {
loading.value = true;
emit("refresh");
delay(500).then(() => (loading.value = false));
}
function onExpand() {
isExpandAll.value = !isExpandAll.value;
isExpandAll.value
? props.vxeTableRef.setAllTreeExpand(true)
: props.vxeTableRef.clearTreeExpand();
props.vxeTableRef.refreshColumn();
}
function reloadColumn() {
const curCheckedColumns = cloneDeep(dynamicColumns.value).filter(item =>
checkedColumns.value.includes(item.title)
);
props.vxeTableRef.reloadColumn(curCheckedColumns);
}
function handleCheckAllChange(val: boolean) {
checkedColumns.value = val ? checkColumnList : [];
isIndeterminate.value = false;
reloadColumn();
}
function handleCheckedColumnsChange(value: string[]) {
checkedColumns.value = value;
const checkedCount = value.length;
checkAll.value = checkedCount === checkColumnList.length;
isIndeterminate.value =
checkedCount > 0 && checkedCount < checkColumnList.length;
}
async function onReset() {
checkAll.value = true;
isIndeterminate.value = false;
dynamicColumns.value = cloneDeep(props?.columns);
checkColumnList = [];
checkColumnList = await getKeyList(cloneDeep(props?.columns), "title");
checkedColumns.value = getKeyList(cloneDeep(props?.columns), "title");
props.vxeTableRef.refreshColumn();
}
function changeSize(curSize: string) {
size.value = curSize;
props.vxeTableRef.refreshColumn();
}
const dropdown = {
dropdown: () => (
<el-dropdown-menu class="translation">
<el-dropdown-item
style={getDropdownItemStyle.value("medium")}
onClick={() => changeSize("medium")}
>
</el-dropdown-item>
<el-dropdown-item
style={getDropdownItemStyle.value("small")}
onClick={() => changeSize("small")}
>
</el-dropdown-item>
<el-dropdown-item
style={getDropdownItemStyle.value("mini")}
onClick={() => changeSize("mini")}
>
</el-dropdown-item>
</el-dropdown-menu>
)
};
/** 列展示拖拽排序 */
const rowDrop = (event: { preventDefault: () => void }) => {
event.preventDefault();
nextTick(() => {
const wrapper: HTMLElement = (
instance?.proxy?.$refs[`VxeGroupRef${unref(props.tableKey)}`] as any
).$el.firstElementChild;
Sortable.create(wrapper, {
animation: 300,
handle: ".drag-btn",
onEnd: ({ newIndex, oldIndex, item }) => {
const targetThElem = item;
const wrapperElem = targetThElem.parentNode as HTMLElement;
const oldColumn = dynamicColumns.value[oldIndex];
const newColumn = dynamicColumns.value[newIndex];
if (oldColumn?.fixed || newColumn?.fixed) {
// 当前列存在fixed属性 则不可拖拽
const oldThElem = wrapperElem.children[oldIndex] as HTMLElement;
if (newIndex > oldIndex) {
wrapperElem.insertBefore(targetThElem, oldThElem);
} else {
wrapperElem.insertBefore(
targetThElem,
oldThElem ? oldThElem.nextElementSibling : oldThElem
);
}
return;
}
const currentRow = dynamicColumns.value.splice(oldIndex, 1)[0];
dynamicColumns.value.splice(newIndex, 0, currentRow);
reloadColumn();
}
});
});
};
const isFixedColumn = (title: string) => {
return dynamicColumns.value.filter(
item => transformI18n(item.title) === transformI18n(title)
)[0].fixed
? true
: false;
};
const rendTippyProps = (content: string) => {
// https://vue-tippy.netlify.app/props
return {
content,
offset: [0, 18],
duration: [300, 0],
followCursor: true,
hideOnClick: "toggle"
};
};
const reference = {
reference: () => (
<SettingIcon
class={["w-[16px]", iconClass.value]}
v-tippy={rendTippyProps("列设置")}
/>
)
};
return () => (
<>
<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">
{slots?.title ? (
slots.title()
) : (
<p class="font-bold truncate">{props.title}</p>
)}
<div class="flex items-center justify-around">
{slots?.buttons ? (
<div class="flex mr-4">{slots.buttons()}</div>
) : null}
{props.tree ? (
<>
<ExpandIcon
class={["w-[16px]", iconClass.value]}
style={{
transform: isExpandAll.value ? "none" : "rotate(-90deg)"
}}
v-tippy={rendTippyProps(
isExpandAll.value ? "折叠" : "展开"
)}
onClick={() => onExpand()}
/>
<el-divider direction="vertical" />
</>
) : null}
<RefreshIcon
class={[
"w-[16px]",
iconClass.value,
loading.value ? "animate-spin" : ""
]}
v-tippy={rendTippyProps("刷新")}
onClick={() => onReFresh()}
/>
<el-divider direction="vertical" />
<el-dropdown
v-slots={dropdown}
trigger="click"
v-tippy={rendTippyProps("密度")}
>
<CollapseIcon class={["w-[16px]", iconClass.value]} />
</el-dropdown>
<el-divider direction="vertical" />
<el-popover
v-slots={reference}
placement="bottom-start"
popper-style={{ padding: 0 }}
width="200"
trigger="click"
>
<div class={[topClass.value]}>
<el-checkbox
class="!-mr-1"
label="列展示"
v-model={checkAll.value}
indeterminate={isIndeterminate.value}
onChange={value => handleCheckAllChange(value)}
/>
<el-button type="primary" link onClick={() => onReset()}>
</el-button>
</div>
<div class="pt-[6px] pl-[11px]">
<el-scrollbar max-height="36vh">
<el-checkbox-group
ref={`VxeGroupRef${unref(props.tableKey)}`}
modelValue={checkedColumns.value}
onChange={value => handleCheckedColumnsChange(value)}
>
<el-space
direction="vertical"
alignment="flex-start"
size={0}
>
{checkColumnList.map((item, index) => {
return (
<div class="flex items-center">
<DragIcon
class={[
"drag-btn w-[16px] mr-2",
isFixedColumn(item)
? "!cursor-no-drop"
: "!cursor-grab"
]}
onMouseenter={(event: {
preventDefault: () => void;
}) => rowDrop(event)}
/>
<el-checkbox
key={index}
label={item}
value={item}
onChange={reloadColumn}
>
<span
title={transformI18n(item)}
class="inline-block w-[120px] truncate hover:text-text_color_primary"
>
{transformI18n(item)}
</span>
</el-checkbox>
</div>
);
})}
</el-space>
</el-checkbox-group>
</el-scrollbar>
</div>
</el-popover>
</div>
</div>
{slots.default({
size: size.value,
dynamicColumns: dynamicColumns.value
})}
</div>
</>
);
}
});

View File

@@ -35,7 +35,7 @@ export const getPlatformConfig = async (app: App): Promise<undefined> => {
})
.then(({ data: config }) => {
let $config = app.config.globalProperties.$config;
// 自动注入项目配置
// 自动注入系统配置
if (app && $config && typeof config === "object") {
$config = Object.assign($config, config);
app.config.globalProperties.$config = $config;

View File

@@ -1,8 +1,9 @@
<script setup lang="ts">
import Footer from "./footer/index.vue";
import { useI18n } from "vue-i18n";
import LayFrame from "../lay-frame/index.vue";
import LayFooter from "../lay-footer/index.vue";
import { useGlobal, isNumber } from "@pureadmin/utils";
import KeepAliveFrame from "./keepAliveFrame/index.vue";
import backTop from "@/assets/svg/back_top.svg?component";
import BackTopIcon from "@/assets/svg/back_top.svg?component";
import { h, computed, Transition, defineComponent } from "vue";
import { usePermissionStoreHook } from "@/store/modules/permission";
@@ -10,6 +11,7 @@ const props = defineProps({
fixedHeader: Boolean
});
const { t } = useI18n();
const { $storage, $config } = useGlobal<GlobalPropertiesApi>();
const isKeepAlive = computed(() => {
@@ -97,15 +99,15 @@ const transitionMain = defineComponent({
<template>
<section
:class="[props.fixedHeader ? 'app-main' : 'app-main-nofixed-header']"
:class="[fixedHeader ? 'app-main' : 'app-main-nofixed-header']"
:style="getSectionStyle"
>
<router-view>
<template #default="{ Component, route }">
<KeepAliveFrame :currComp="Component" :currRoute="route">
<LayFrame :currComp="Component" :currRoute="route">
<template #default="{ Comp, fullPath, frameInfo }">
<el-scrollbar
v-if="props.fixedHeader"
v-if="fixedHeader"
:wrap-style="{
display: 'flex',
'flex-wrap': 'wrap',
@@ -121,10 +123,10 @@ const transitionMain = defineComponent({
}"
>
<el-backtop
title="回到顶部"
:title="t('buttons.pureBackTop')"
target=".app-main .el-scrollbar__wrap"
>
<backTop />
<BackTopIcon />
</el-backtop>
<div class="grow">
<transitionMain :route="route">
@@ -148,7 +150,7 @@ const transitionMain = defineComponent({
/>
</transitionMain>
</div>
<Footer v-if="!hideFooter" />
<LayFooter v-if="!hideFooter" />
</el-scrollbar>
<div v-else class="grow">
<transitionMain :route="route">
@@ -173,12 +175,12 @@ const transitionMain = defineComponent({
</transitionMain>
</div>
</template>
</KeepAliveFrame>
</LayFrame>
</template>
</router-view>
<!-- 页脚 -->
<Footer v-if="!hideFooter && !props.fixedHeader" />
<LayFooter v-if="!hideFooter && !fixedHeader" />
</section>
</template>

View File

@@ -1,9 +1,9 @@
<script setup lang="ts">
import { getConfig } from "@/config";
import { useMultiFrame } from "@/layout/hooks/useMultiFrame";
import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
import { type Component, shallowRef, watch, computed } from "vue";
import { type RouteRecordRaw, RouteLocationNormalizedLoaded } from "vue-router";
import { useMultiFrame } from "@/layout/components/keepAliveFrame/useMultiFrame";
const props = defineProps<{
currRoute: RouteLocationNormalizedLoaded;
@@ -20,7 +20,7 @@ const keep = computed(() => {
!!props.currRoute.meta?.frameSrc
);
});
// frameView
// LayFrame
const normalComp = computed(() => !keep.value && props.currComp);
watch(useMultiTagsStoreHook().multiTags, (tags: any) => {
@@ -65,7 +65,7 @@ watch(
</script>
<template>
<template v-for="[fullPath, Comp] in compList" :key="fullPath">
<div v-show="fullPath === props.currRoute.fullPath" class="w-full h-full">
<div v-show="fullPath === currRoute.fullPath" class="w-full h-full">
<slot
:fullPath="fullPath"
:Comp="Comp"
@@ -74,6 +74,6 @@ watch(
</div>
</template>
<div v-show="!keep" class="w-full h-full">
<slot :Comp="normalComp" :fullPath="props.currRoute.fullPath" frameInfo />
<slot :Comp="normalComp" :fullPath="currRoute.fullPath" frameInfo />
</div>
</template>

View File

@@ -1,16 +1,19 @@
<script setup lang="ts">
import Search from "./search/index.vue";
import Notice from "./notice/index.vue";
import mixNav from "./sidebar/mixNav.vue";
import { useNav } from "@/layout/hooks/useNav";
import FullScreen from "./sidebar/fullScreen.vue";
import Breadcrumb from "./sidebar/breadCrumb.vue";
import topCollapse from "./sidebar/topCollapse.vue";
import { useTranslationLang } from "../hooks/useTranslationLang";
import globalization from "@/assets/svg/globalization.svg?component";
import LaySearch from "../lay-search/index.vue";
import LayNotice from "../lay-notice/index.vue";
import LayNavMix from "../lay-sidebar/NavMix.vue";
import { useTranslationLang } from "@/layout/hooks/useTranslationLang";
import LaySidebarFullScreen from "../lay-sidebar/components/SidebarFullScreen.vue";
import LaySidebarBreadCrumb from "../lay-sidebar/components/SidebarBreadCrumb.vue";
import LaySidebarTopCollapse from "../lay-sidebar/components/SidebarTopCollapse.vue";
import GlobalizationIcon from "@/assets/svg/globalization.svg?component";
import AccountSettingsIcon from "@iconify-icons/ri/user-settings-line";
import LogoutCircleRLine from "@iconify-icons/ri/logout-circle-r-line";
import Setting from "@iconify-icons/ri/settings-3-line";
import Check from "@iconify-icons/ep/check";
const {
layout,
device,
@@ -21,6 +24,7 @@ const {
userAvatar,
avatarsStyle,
toggleSideBar,
toAccountSettings,
getDropdownItemStyle,
getDropdownItemClass
} = useNav();
@@ -30,26 +34,26 @@ const { t, locale, translationCh, translationEn } = useTranslationLang();
<template>
<div class="navbar bg-[#fff] shadow-sm shadow-[rgba(0,21,41,0.08)]">
<topCollapse
<LaySidebarTopCollapse
v-if="device === 'mobile'"
class="hamburger-container"
:is-active="pureApp.sidebar.opened"
@toggleClick="toggleSideBar"
/>
<Breadcrumb
<LaySidebarBreadCrumb
v-if="layout !== 'mix' && device !== 'mobile'"
class="breadcrumb-container"
/>
<mixNav v-if="layout === 'mix'" />
<LayNavMix v-if="layout === 'mix'" />
<div v-if="layout === 'vertical'" class="vertical-header-right">
<!-- 菜单搜索 -->
<Search id="header-search" />
<LaySearch id="header-search" />
<!-- 国际化 -->
<el-dropdown id="header-translation" trigger="click">
<globalization
<GlobalizationIcon
class="navbar-bg-hover w-[40px] h-[48px] p-[11px] cursor-pointer outline-none"
/>
<template #dropdown>
@@ -80,9 +84,9 @@ const { t, locale, translationCh, translationEn } = useTranslationLang();
</template>
</el-dropdown>
<!-- 全屏 -->
<FullScreen id="full-screen" />
<LaySidebarFullScreen id="full-screen" />
<!-- 消息通知 -->
<Notice id="header-notice" />
<LayNotice id="header-notice" />
<!-- 退出登录 -->
<el-dropdown trigger="click">
<span class="el-dropdown-link navbar-bg-hover select-none">
@@ -91,6 +95,13 @@ const { t, locale, translationCh, translationEn } = useTranslationLang();
</span>
<template #dropdown>
<el-dropdown-menu class="logout">
<el-dropdown-item @click="toAccountSettings">
<IconifyIconOffline
:icon="AccountSettingsIcon"
style="margin: 5px"
/>
{{ t("buttons.pureAccountSettings") }}
</el-dropdown-item>
<el-dropdown-item @click="logout">
<IconifyIconOffline
:icon="LogoutCircleRLine"
@@ -103,7 +114,7 @@ const { t, locale, translationCh, translationEn } = useTranslationLang();
</el-dropdown>
<span
class="set-icon navbar-bg-hover"
:title="t('buttons.pureSystemSet')"
:title="t('buttons.pureOpenSystemSet')"
@click="onPanel"
>
<IconifyIconOffline :icon="Setting" />
@@ -177,7 +188,7 @@ const { t, locale, translationCh, translationEn } = useTranslationLang();
}
.logout {
max-width: 120px;
width: 120px;
::v-deep(.el-dropdown-menu__item) {
display: inline-flex;

View File

@@ -1,10 +1,10 @@
<script setup lang="ts">
import { ListItem } from "./data";
import { ListItem } from "../data";
import { ref, PropType, nextTick } from "vue";
import { useNav } from "@/layout/hooks/useNav";
import { deviceDetection } from "@pureadmin/utils";
const props = defineProps({
defineProps({
noticeItem: {
type: Object as PropType<ListItem>,
default: () => {}
@@ -52,9 +52,9 @@ function hoverDescription(event, description) {
class="notice-container border-b-[1px] border-solid border-[#f0f0f0] dark:border-[#303030]"
>
<el-avatar
v-if="props.noticeItem.avatar"
v-if="noticeItem.avatar"
:size="30"
:src="props.noticeItem.avatar"
:src="noticeItem.avatar"
class="notice-container-avatar"
/>
<div class="notice-container-text">
@@ -63,7 +63,7 @@ function hoverDescription(event, description) {
popper-class="notice-title-popper"
:effect="tooltipEffect"
:disabled="!titleTooltip"
:content="props.noticeItem.title"
:content="noticeItem.title"
placement="top-start"
:enterable="!isMobile"
>
@@ -72,16 +72,16 @@ function hoverDescription(event, description) {
class="notice-title-content"
@mouseover="hoverTitle"
>
{{ props.noticeItem.title }}
{{ noticeItem.title }}
</div>
</el-tooltip>
<el-tag
v-if="props.noticeItem?.extra"
:type="props.noticeItem?.status"
v-if="noticeItem?.extra"
:type="noticeItem?.status"
size="small"
class="notice-title-extra"
>
{{ props.noticeItem?.extra }}
{{ noticeItem?.extra }}
</el-tag>
</div>
@@ -89,19 +89,19 @@ function hoverDescription(event, description) {
popper-class="notice-title-popper"
:effect="tooltipEffect"
:disabled="!descriptionTooltip"
:content="props.noticeItem.description"
:content="noticeItem.description"
placement="top-start"
>
<div
ref="descriptionRef"
class="notice-text-description"
@mouseover="hoverDescription($event, props.noticeItem.description)"
@mouseover="hoverDescription($event, noticeItem.description)"
>
{{ props.noticeItem.description }}
{{ noticeItem.description }}
</div>
</el-tooltip>
<div class="notice-text-datetime text-[#00000073] dark:text-white">
{{ props.noticeItem.datetime }}
{{ noticeItem.datetime }}
</div>
</div>
</div>

View File

@@ -0,0 +1,24 @@
<script setup lang="ts">
import { PropType } from "vue";
import { ListItem } from "../data";
import NoticeItem from "./NoticeItem.vue";
import { transformI18n } from "@/plugins/i18n";
defineProps({
list: {
type: Array as PropType<Array<ListItem>>,
default: () => []
},
emptyText: {
type: String,
default: ""
}
});
</script>
<template>
<div v-if="list.length">
<NoticeItem v-for="(item, index) in list" :key="index" :noticeItem="item" />
</div>
<el-empty v-else :description="transformI18n(emptyText)" />
</template>

View File

@@ -0,0 +1,99 @@
import { $t } from "@/plugins/i18n";
export interface ListItem {
avatar: string;
title: string;
datetime: string;
type: string;
description: string;
status?: "primary" | "success" | "warning" | "info" | "danger";
extra?: string;
}
export interface TabItem {
key: string;
name: string;
list: ListItem[];
emptyText: string;
}
export const noticesData: TabItem[] = [
{
key: "1",
name: $t("status.pureNotify"),
list: [],
emptyText: $t("status.pureNoNotify")
},
{
key: "2",
name: $t("status.pureMessage"),
list: [
{
avatar: "https://xiaoxian521.github.io/hyperlink/svg/smile1.svg",
title: "小铭 评论了你",
description: "诚在于心,信在于行,诚信在于心行合一。",
datetime: "今天",
type: "2"
},
{
avatar: "https://xiaoxian521.github.io/hyperlink/svg/smile2.svg",
title: "李白 回复了你",
description: "长风破浪会有时,直挂云帆济沧海。",
datetime: "昨天",
type: "2"
},
{
avatar: "https://xiaoxian521.github.io/hyperlink/svg/smile5.svg",
title: "标题",
description:
"请将鼠标移动到此处以便测试超长的消息在此处将如何处理。本例中设置的描述最大行数为2超过2行的描述内容将被省略并且可以通过tooltip查看完整内容",
datetime: "时间",
type: "2"
}
],
emptyText: $t("status.pureNoMessage")
},
{
key: "3",
name: $t("status.pureTodo"),
list: [
{
avatar: "",
title: "第三方紧急代码变更",
description:
"小林提交于 2024-05-10需在 2024-05-11 前完成代码变更任务",
datetime: "",
extra: "马上到期",
status: "danger",
type: "3"
},
{
avatar: "",
title: "版本发布",
description: "指派小铭于 2024-06-18 前完成更新并发布",
datetime: "",
extra: "已耗时 8 天",
status: "warning",
type: "3"
},
{
avatar: "",
title: "新功能开发",
description: "开发多租户管理",
datetime: "",
extra: "进行中",
type: "3"
},
{
avatar: "",
title: "任务名称",
description: "任务需要在 2030-10-30 10:00 前启动",
datetime: "",
extra: "未开始",
status: "info",
type: "3"
}
],
emptyText: $t("status.pureNoTodo")
}
];

View File

@@ -1,22 +1,36 @@
<script setup lang="ts">
import { ref } from "vue";
import { useI18n } from "vue-i18n";
import { ref, computed } from "vue";
import { noticesData } from "./data";
import NoticeList from "./noticeList.vue";
import Bell from "@iconify-icons/ep/bell";
import NoticeList from "./components/NoticeList.vue";
import BellIcon from "@iconify-icons/ep/bell";
const { t } = useI18n();
const noticesNum = ref(0);
const notices = ref(noticesData);
const activeKey = ref(noticesData[0].key);
const activeKey = ref(noticesData[0]?.key);
notices.value.map(v => (noticesNum.value += v.list.length));
const getLabel = computed(
() => item =>
t(item.name) + (item.list.length > 0 ? `(${item.list.length})` : "")
);
</script>
<template>
<el-dropdown trigger="click" placement="bottom-end">
<span class="dropdown-badge navbar-bg-hover select-none">
<el-badge :value="noticesNum" :max="99">
<span
:class="[
'dropdown-badge',
'navbar-bg-hover',
'select-none',
Number(noticesNum) !== 0 && 'mr-[10px]'
]"
>
<el-badge :value="Number(noticesNum) === 0 ? '' : noticesNum" :max="99">
<span class="header-notice-icon">
<IconifyIconOffline :icon="Bell" />
<IconifyIconOffline :icon="BellIcon" />
</span>
</el-badge>
</span>
@@ -30,18 +44,15 @@ notices.value.map(v => (noticesNum.value += v.list.length));
>
<el-empty
v-if="notices.length === 0"
description="暂无消息"
:description="t('status.pureNoMessage')"
:image-size="60"
/>
<span v-else>
<template v-for="item in notices" :key="item.key">
<el-tab-pane
:label="`${item.name}(${item.list.length})`"
:name="`${item.key}`"
>
<el-tab-pane :label="getLabel(item)" :name="`${item.key}`">
<el-scrollbar max-height="330px">
<div class="noticeList-container">
<NoticeList :list="item.list" />
<NoticeList :list="item.list" :emptyText="item.emptyText" />
</div>
</el-scrollbar>
</el-tab-pane>
@@ -60,7 +71,6 @@ notices.value.map(v => (noticesNum.value += v.list.length));
justify-content: center;
width: 40px;
height: 48px;
margin-right: 10px;
cursor: pointer;
.header-notice-icon {

View File

@@ -1,9 +1,10 @@
<script setup lang="ts">
import { useI18n } from "vue-i18n";
import { emitter } from "@/utils/mitt";
import { onClickOutside } from "@vueuse/core";
import { ref, computed, onMounted, onBeforeUnmount } from "vue";
import { useDataThemeChange } from "@/layout/hooks/useDataThemeChange";
import Close from "@iconify-icons/ep/close";
import CloseIcon from "@iconify-icons/ep/close";
const target = ref(null);
const show = ref<Boolean>(false);
@@ -25,6 +26,7 @@ const iconClass = computed(() => {
];
});
const { t } = useI18n();
const { onReset } = useDataThemeChange();
onClickOutside(target, (event: any) => {
@@ -51,10 +53,12 @@ onBeforeUnmount(() => {
<div
class="project-configuration border-b-[1px] border-solid border-[var(--pure-border-color)]"
>
<h4 class="dark:text-white">项目配置</h4>
<h4 class="dark:text-white">
{{ t("panel.pureSystemSet") }}
</h4>
<span
v-tippy="{
content: '关闭配置',
content: t('panel.pureCloseSystemSet'),
placement: 'bottom-start',
zIndex: 41000
}"
@@ -64,7 +68,7 @@ onBeforeUnmount(() => {
class="dark:text-white"
width="18px"
height="18px"
:icon="Close"
:icon="CloseIcon"
@click="show = !show"
/>
</span>
@@ -78,7 +82,7 @@ onBeforeUnmount(() => {
>
<el-button
v-tippy="{
content: '清空缓存并返回登录页',
content: t('panel.pureClearCacheAndToLogin'),
placement: 'left-start',
zIndex: 41000
}"
@@ -87,7 +91,7 @@ onBeforeUnmount(() => {
bg
@click="onReset"
>
清空缓存
{{ t("panel.pureClearCache") }}
</el-button>
</div>
</div>

View File

@@ -1,37 +1,36 @@
<script setup lang="ts">
import { useI18n } from "vue-i18n";
import { useNav } from "@/layout/hooks/useNav";
import mdiKeyboardEsc from "@/assets/svg/keyboard_esc.svg?component";
import enterOutlined from "@/assets/svg/enter_outlined.svg?component";
import MdiKeyboardEsc from "@/assets/svg/keyboard_esc.svg?component";
import EnterOutlined from "@/assets/svg/enter_outlined.svg?component";
import ArrowUpLine from "@iconify-icons/ri/arrow-up-line";
import ArrowDownLine from "@iconify-icons/ri/arrow-down-line";
const props = withDefaults(defineProps<{ total: number }>(), {
withDefaults(defineProps<{ total: number }>(), {
total: 0
});
const { t } = useI18n();
const { device } = useNav();
</script>
<template>
<div class="search-footer text-[#333] dark:text-white">
<span class="search-footer-item">
<enterOutlined class="icon" />
确认
<EnterOutlined class="icon" />
{{ t("buttons.pureConfirm") }}
</span>
<span class="search-footer-item">
<IconifyIconOffline :icon="ArrowUpLine" class="icon" />
<IconifyIconOffline :icon="ArrowDownLine" class="icon" />
切换
{{ t("buttons.pureSwitch") }}
</span>
<span class="search-footer-item">
<mdiKeyboardEsc class="icon" />
关闭
<MdiKeyboardEsc class="icon" />
{{ t("buttons.pureClose") }}
</span>
<p
v-if="device !== 'mobile' && props.total > 0"
class="search-footer-total"
>
{{ props.total }}
<p v-if="device !== 'mobile' && total > 0" class="search-footer-total">
{{ `${t("search.pureTotal")} ${total}` }}
</p>
</div>
</template>

View File

@@ -1,5 +1,6 @@
<script setup lang="ts">
import Sortable from "sortablejs";
import { useI18n } from "vue-i18n";
import SearchHistoryItem from "./SearchHistoryItem.vue";
import type { optionsItem, dragItem, Props } from "../types";
import { useEpThemeStoreHook } from "@/store/modules/epTheme";
@@ -19,6 +20,7 @@ const innerHeight = ref();
/** 判断是否停止鼠标移入事件处理 */
const stopMouseEvent = ref(false);
const { t } = useI18n();
const emit = defineEmits<Emits>();
const instance = getCurrentInstance()!;
const props = withDefaults(defineProps<Props>(), {});
@@ -141,7 +143,9 @@ defineExpose({ handleScroll });
<template>
<div ref="historyRef" class="history">
<template v-if="historyList.length">
<div :style="titleStyle">搜索历史</div>
<div :style="titleStyle">
{{ t("search.pureHistory") }}
</div>
<div
v-for="(item, index) in historyList"
:key="item.path"
@@ -160,7 +164,9 @@ defineExpose({ handleScroll });
</template>
<template v-if="collectList.length">
<div :style="titleStyle">
收藏{{ collectList.length > 1 ? "(可拖拽排序)" : "" }}
{{
`${t("search.pureCollect")}${collectList.length > 1 ? t("search.pureDragSort") : ""}`
}}
</div>
<div class="collect-container">
<div

View File

@@ -2,8 +2,8 @@
import type { optionsItem } from "../types";
import { transformI18n } from "@/plugins/i18n";
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
import Star from "@iconify-icons/ep/star";
import Close from "@iconify-icons/ep/close";
import StarIcon from "@iconify-icons/ep/star";
import CloseIcon from "@iconify-icons/ep/close";
interface Props {
item: optionsItem;
@@ -33,12 +33,12 @@ function handleDelete(item) {
</span>
<IconifyIconOffline
v-show="item.type === 'history'"
:icon="Star"
:icon="StarIcon"
class="w-[18px] h-[18px] mr-2 hover:text-[#d7d5d4]"
@click.stop="handleCollect(item)"
/>
<IconifyIconOffline
:icon="Close"
:icon="CloseIcon"
class="w-[18px] h-[18px] hover:text-[#d7d5d4] cursor-pointer"
@click.stop="handleDelete(item)"
/>

View File

@@ -6,14 +6,14 @@ import { useRouter } from "vue-router";
import SearchResult from "./SearchResult.vue";
import SearchFooter from "./SearchFooter.vue";
import { useNav } from "@/layout/hooks/useNav";
import { transformI18n } from "@/plugins/i18n";
import SearchHistory from "./SearchHistory.vue";
import { transformI18n, $t } from "@/plugins/i18n";
import type { optionsItem, dragItem } from "../types";
import { ref, computed, shallowRef, watch } from "vue";
import { useDebounceFn, onKeyStroke } from "@vueuse/core";
import { usePermissionStoreHook } from "@/store/modules/permission";
import { cloneDeep, isAllEmpty, storageLocal } from "@pureadmin/utils";
import Search from "@iconify-icons/ri/search-line";
import SearchIcon from "@iconify-icons/ri/search-line";
interface Props {
/** 弹窗显隐 */
@@ -29,7 +29,7 @@ const emit = defineEmits<Emits>();
const props = withDefaults(defineProps<Props>(), {});
const router = useRouter();
const { locale } = useI18n();
const { t, locale } = useI18n();
const HISTORY_TYPE = "history";
const COLLECT_TYPE = "collect";
@@ -293,19 +293,19 @@ onKeyStroke("ArrowDown", handleDown);
v-model="keyword"
size="large"
clearable
placeholder="搜索菜单(中文模式下支持拼音搜索)"
:placeholder="t('search.purePlaceholder')"
@input="handleSearch"
>
<template #prefix>
<IconifyIconOffline
:icon="Search"
:icon="SearchIcon"
class="text-primary w-[24px] h-[24px]"
/>
</template>
</el-input>
<div class="search-content">
<el-scrollbar ref="scrollbarRef" max-height="calc(90vh - 140px)">
<el-empty v-if="showEmpty" description="暂无搜索结果" />
<el-empty v-if="showEmpty" :description="t('search.pureEmpty')" />
<SearchHistory
v-if="showSearchHistory"
ref="historyRef"

View File

@@ -5,7 +5,7 @@ import { useResizeObserver } from "@pureadmin/utils";
import { useEpThemeStoreHook } from "@/store/modules/epTheme";
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";
interface Emits {
(e: "update:value", val: string): void;
@@ -84,7 +84,7 @@ defineExpose({ handleScroll });
<span class="result-item-title">
{{ transformI18n(item.meta?.title) }}
</span>
<enterOutlined />
<EnterOutlined />
</div>
</div>
</template>

View File

@@ -1,6 +1,6 @@
<script setup lang="ts">
import { SearchModal } from "./components";
import { useBoolean } from "../../hooks/useBoolean";
import SearchModal from "./components/SearchModal.vue";
const { bool: show, toggle } = useBoolean();
function handleSearch() {

View File

@@ -9,8 +9,9 @@ import {
onUnmounted,
onBeforeMount
} from "vue";
import panel from "../panel/index.vue";
import { useI18n } from "vue-i18n";
import { emitter } from "@/utils/mitt";
import LayPanel from "../lay-panel/index.vue";
import { useNav } from "@/layout/hooks/useNav";
import { useAppStoreHook } from "@/store/modules/app";
import { toggleTheme } from "@pureadmin/theme/dist/browser-utils";
@@ -22,10 +23,11 @@ import { useDark, useGlobal, debounce, isNumber } from "@pureadmin/utils";
import Check from "@iconify-icons/ep/check";
import LeftArrow from "@iconify-icons/ri/arrow-left-s-line";
import RightArrow from "@iconify-icons/ri/arrow-right-s-line";
import dayIcon from "@/assets/svg/day.svg?component";
import darkIcon from "@/assets/svg/dark.svg?component";
import systemIcon from "@/assets/svg/system.svg?component";
import DayIcon from "@/assets/svg/day.svg?component";
import DarkIcon from "@/assets/svg/dark.svg?component";
import SystemIcon from "@/assets/svg/system.svg?component";
const { t } = useI18n();
const { device } = useNav();
const { isDark } = useDark();
const { $storage } = useGlobal<GlobalPropertiesApi>();
@@ -145,18 +147,20 @@ function setFalse(Doms): any {
}
/** 页宽 */
const stretchTypeOptions: Array<OptionsType> = [
{
label: "固定",
tip: "紧凑页面,轻松找到所需信息",
value: "fixed"
},
{
label: "自定义",
tip: "最小1280、最大1600",
value: "custom"
}
];
const stretchTypeOptions = computed<Array<OptionsType>>(() => {
return [
{
label: t("panel.pureStretchFixed"),
tip: t("panel.pureStretchFixedTip"),
value: "fixed"
},
{
label: t("panel.pureStretchCustom"),
tip: t("panel.pureStretchCustomTip"),
value: "custom"
}
];
});
const setStretch = value => {
settings.stretch = value;
@@ -194,41 +198,43 @@ const pClass = computed(() => {
const themeOptions = computed<Array<OptionsType>>(() => {
return [
{
label: "浅色",
icon: dayIcon,
label: t("panel.pureOverallStyleLight"),
icon: DayIcon,
theme: "light",
tip: "清新启航,点亮舒适的工作界面",
tip: t("panel.pureOverallStyleLightTip"),
iconAttrs: { fill: isDark.value ? "#fff" : "#000" }
},
{
label: "深色",
icon: darkIcon,
label: t("panel.pureOverallStyleDark"),
icon: DarkIcon,
theme: "dark",
tip: "月光序曲,沉醉于夜的静谧雅致",
tip: t("panel.pureOverallStyleDarkTip"),
iconAttrs: { fill: isDark.value ? "#fff" : "#000" }
},
{
label: "自动",
icon: systemIcon,
label: t("panel.pureOverallStyleSystem"),
icon: SystemIcon,
theme: "system",
tip: "同步时光,界面随晨昏自然呼应",
tip: t("panel.pureOverallStyleSystemTip"),
iconAttrs: { fill: isDark.value ? "#fff" : "#000" }
}
];
});
const markOptions: Array<OptionsType> = [
{
label: "灵动",
tip: "灵动标签,添趣生辉",
value: "smart"
},
{
label: "卡片",
tip: "卡片标签,高效浏览",
value: "card"
}
];
const markOptions = computed<Array<OptionsType>>(() => {
return [
{
label: t("panel.pureTagsStyleSmart"),
tip: t("panel.pureTagsStyleSmartTip"),
value: "smart"
},
{
label: t("panel.pureTagsStyleCard"),
tip: t("panel.pureTagsStyleCardTip"),
value: "card"
}
];
});
/** 设置导航模式 */
function setLayoutModel(layout: string) {
@@ -291,7 +297,7 @@ function watchSystemThemeChange() {
}
onBeforeMount(() => {
/* 初始化项目配置 */
/* 初始化系统配置 */
nextTick(() => {
watchSystemThemeChange();
settings.greyVal &&
@@ -307,10 +313,11 @@ onUnmounted(() => removeMatchMedia);
</script>
<template>
<panel>
<LayPanel>
<div class="p-5">
<p :class="pClass">整体风格</p>
<p :class="pClass">{{ t("panel.pureOverallStyle") }}</p>
<Segmented
resize
class="select-none"
:modelValue="overallStyle === 'system' ? 2 : dataTheme ? 1 : 0"
:options="themeOptions"
@@ -326,7 +333,7 @@ onUnmounted(() => removeMatchMedia);
"
/>
<p :class="['mt-5', pClass]">主题色</p>
<p :class="['mt-5', pClass]">{{ t("panel.pureThemeColor") }}</p>
<ul class="theme-color">
<li
v-for="(item, index) in themeColors"
@@ -345,12 +352,12 @@ onUnmounted(() => removeMatchMedia);
</li>
</ul>
<p :class="['mt-5', pClass]">导航模式</p>
<p :class="['mt-5', pClass]">{{ t("panel.pureLayoutModel") }}</p>
<ul class="pure-theme">
<li
ref="verticalRef"
v-tippy="{
content: '左侧菜单,亲切熟悉',
content: t('panel.pureVerticalTip'),
zIndex: 41000
}"
:class="layoutTheme.layout === 'vertical' ? 'is-select' : ''"
@@ -363,7 +370,7 @@ onUnmounted(() => removeMatchMedia);
v-if="device !== 'mobile'"
ref="horizontalRef"
v-tippy="{
content: '顶部菜单,简洁概览',
content: t('panel.pureHorizontalTip'),
zIndex: 41000
}"
:class="layoutTheme.layout === 'horizontal' ? 'is-select' : ''"
@@ -376,7 +383,7 @@ onUnmounted(() => removeMatchMedia);
v-if="device !== 'mobile'"
ref="mixRef"
v-tippy="{
content: '混合菜单,灵活多变',
content: t('panel.pureMixTip'),
zIndex: 41000
}"
:class="layoutTheme.layout === 'mix' ? 'is-select' : ''"
@@ -388,8 +395,9 @@ onUnmounted(() => removeMatchMedia);
</ul>
<span v-if="useAppStoreHook().getViewportWidth > 1280">
<p :class="['mt-5', pClass]">页宽</p>
<p :class="['mt-5', pClass]">{{ t("panel.pureStretch") }}</p>
<Segmented
resize
class="mb-2 select-none"
:modelValue="isNumber(settings.stretch) ? 1 : 0"
:options="stretchTypeOptions"
@@ -430,53 +438,56 @@ onUnmounted(() => removeMatchMedia);
</button>
</span>
<p :class="['mt-4', pClass]">页签风格</p>
<p :class="['mt-4', pClass]">{{ t("panel.pureTagsStyle") }}</p>
<Segmented
resize
class="select-none"
:modelValue="markValue === 'smart' ? 0 : 1"
:options="markOptions"
@change="onChange"
/>
<p class="mt-5 font-medium text-sm dark:text-white">界面显示</p>
<p class="mt-5 font-medium text-sm dark:text-white">
{{ t("panel.pureInterfaceDisplay") }}
</p>
<ul class="setting">
<li>
<span class="dark:text-white">灰色模式</span>
<span class="dark:text-white">{{ t("panel.pureGreyModel") }}</span>
<el-switch
v-model="settings.greyVal"
inline-prompt
active-text=""
inactive-text=""
:active-text="t('buttons.pureOpenText')"
:inactive-text="t('buttons.pureCloseText')"
@change="greyChange"
/>
</li>
<li>
<span class="dark:text-white">色弱模式</span>
<span class="dark:text-white">{{ t("panel.pureWeakModel") }}</span>
<el-switch
v-model="settings.weakVal"
inline-prompt
active-text=""
inactive-text=""
:active-text="t('buttons.pureOpenText')"
:inactive-text="t('buttons.pureCloseText')"
@change="weekChange"
/>
</li>
<li>
<span class="dark:text-white">隐藏标签页</span>
<span class="dark:text-white">{{ t("panel.pureHiddenTags") }}</span>
<el-switch
v-model="settings.tabsVal"
inline-prompt
active-text=""
inactive-text=""
:active-text="t('buttons.pureOpenText')"
:inactive-text="t('buttons.pureCloseText')"
@change="tagsChange"
/>
</li>
<li>
<span class="dark:text-white">隐藏页脚</span>
<span class="dark:text-white">{{ t("panel.pureHiddenFooter") }}</span>
<el-switch
v-model="settings.hideFooter"
inline-prompt
active-text=""
inactive-text=""
:active-text="t('buttons.pureOpenText')"
:inactive-text="t('buttons.pureCloseText')"
@change="hideFooterChange"
/>
</li>
@@ -487,24 +498,26 @@ onUnmounted(() => removeMatchMedia);
inline-prompt
:active-value="true"
:inactive-value="false"
active-text=""
inactive-text=""
:active-text="t('buttons.pureOpenText')"
:inactive-text="t('buttons.pureCloseText')"
@change="logoChange"
/>
</li>
<li>
<span class="dark:text-white">页签持久化</span>
<span class="dark:text-white">
{{ t("panel.pureMultiTagsCache") }}
</span>
<el-switch
v-model="settings.multiTagsCache"
inline-prompt
active-text=""
inactive-text=""
:active-text="t('buttons.pureOpenText')"
:inactive-text="t('buttons.pureCloseText')"
@change="multiTagsCacheChange"
/>
</li>
</ul>
</div>
</panel>
</LayPanel>
</template>
<style lang="scss" scoped>

View File

@@ -1,14 +1,16 @@
<script setup lang="ts">
import Search from "../search/index.vue";
import Notice from "../notice/index.vue";
import FullScreen from "./fullScreen.vue";
import SidebarItem from "./sidebarItem.vue";
import { isAllEmpty } from "@pureadmin/utils";
import { ref, nextTick, computed } from "vue";
import { useNav } from "@/layout/hooks/useNav";
import LaySearch from "../lay-search/index.vue";
import LayNotice from "../lay-notice/index.vue";
import { useTranslationLang } from "../../hooks/useTranslationLang";
import { usePermissionStoreHook } from "@/store/modules/permission";
import globalization from "@/assets/svg/globalization.svg?component";
import LaySidebarItem from "../lay-sidebar/components/SidebarItem.vue";
import LaySidebarFullScreen from "../lay-sidebar/components/SidebarFullScreen.vue";
import GlobalizationIcon from "@/assets/svg/globalization.svg?component";
import AccountSettingsIcon from "@iconify-icons/ri/user-settings-line";
import LogoutCircleRLine from "@iconify-icons/ri/logout-circle-r-line";
import Setting from "@iconify-icons/ri/settings-3-line";
import Check from "@iconify-icons/ep/check";
@@ -20,12 +22,13 @@ const { t, route, locale, translationCh, translationEn } =
const {
title,
logout,
backTopMenu,
onPanel,
getLogo,
username,
userAvatar,
backTopMenu,
avatarsStyle,
toAccountSettings,
getDropdownItemStyle,
getDropdownItemClass
} = useNav();
@@ -50,13 +53,12 @@ nextTick(() => {
</div>
<el-menu
ref="menuRef"
router
mode="horizontal"
popper-class="pure-scrollbar"
class="horizontal-header-menu"
:default-active="defaultActive"
>
<sidebar-item
<LaySidebarItem
v-for="route in usePermissionStoreHook().wholeMenus"
:key="route.path"
:item="route"
@@ -65,10 +67,10 @@ nextTick(() => {
</el-menu>
<div class="horizontal-header-right">
<!-- 菜单搜索 -->
<Search id="header-search" />
<LaySearch id="header-search" />
<!-- 国际化 -->
<el-dropdown id="header-translation" trigger="click">
<globalization
<GlobalizationIcon
class="navbar-bg-hover w-[40px] h-[48px] p-[11px] cursor-pointer outline-none"
/>
<template #dropdown>
@@ -97,9 +99,9 @@ nextTick(() => {
</template>
</el-dropdown>
<!-- 全屏 -->
<FullScreen id="full-screen" />
<LaySidebarFullScreen id="full-screen" />
<!-- 消息通知 -->
<Notice id="header-notice" />
<LayNotice id="header-notice" />
<!-- 退出登录 -->
<el-dropdown trigger="click">
<span class="el-dropdown-link navbar-bg-hover">
@@ -107,6 +109,13 @@ nextTick(() => {
<p v-if="username" class="dark:text-white">{{ username }}</p>
</span>
<template #dropdown>
<el-dropdown-item @click="toAccountSettings">
<IconifyIconOffline
:icon="AccountSettingsIcon"
style="margin: 5px"
/>
{{ t("buttons.pureAccountSettings") }}
</el-dropdown-item>
<el-dropdown-menu class="logout">
<el-dropdown-item @click="logout">
<IconifyIconOffline
@@ -120,7 +129,7 @@ nextTick(() => {
</el-dropdown>
<span
class="set-icon navbar-bg-hover"
:title="t('buttons.pureSystemSet')"
:title="t('buttons.pureOpenSystemSet')"
@click="onPanel"
>
<IconifyIconOffline :icon="Setting" />
@@ -151,7 +160,7 @@ nextTick(() => {
}
.logout {
max-width: 120px;
width: 120px;
::v-deep(.el-dropdown-menu__item) {
display: inline-flex;

View File

@@ -1,17 +1,19 @@
<script setup lang="ts">
import extraIcon from "./extraIcon.vue";
import Search from "../search/index.vue";
import Notice from "../notice/index.vue";
import FullScreen from "./fullScreen.vue";
import { isAllEmpty } from "@pureadmin/utils";
import { useNav } from "@/layout/hooks/useNav";
import { transformI18n } from "@/plugins/i18n";
import LaySearch from "../lay-search/index.vue";
import LayNotice from "../lay-notice/index.vue";
import { ref, toRaw, watch, onMounted, nextTick } from "vue";
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
import { getParentPaths, findRouteByPath } from "@/router/utils";
import { useTranslationLang } from "../../hooks/useTranslationLang";
import { usePermissionStoreHook } from "@/store/modules/permission";
import globalization from "@/assets/svg/globalization.svg?component";
import LaySidebarExtraIcon from "../lay-sidebar/components/SidebarExtraIcon.vue";
import LaySidebarFullScreen from "../lay-sidebar/components/SidebarFullScreen.vue";
import GlobalizationIcon from "@/assets/svg/globalization.svg?component";
import AccountSettingsIcon from "@iconify-icons/ri/user-settings-line";
import LogoutCircleRLine from "@iconify-icons/ri/logout-circle-r-line";
import Setting from "@iconify-icons/ri/settings-3-line";
import Check from "@iconify-icons/ep/check";
@@ -30,6 +32,7 @@ const {
userAvatar,
getDivStyle,
avatarsStyle,
toAccountSettings,
getDropdownItemStyle,
getDropdownItemClass
} = useNav();
@@ -91,17 +94,17 @@ watch(
<span class="select-none">
{{ transformI18n(route.meta.title) }}
</span>
<extraIcon :extraIcon="route.meta.extraIcon" />
<LaySidebarExtraIcon :extraIcon="route.meta.extraIcon" />
</div>
</template>
</el-menu-item>
</el-menu>
<div class="horizontal-header-right">
<!-- 菜单搜索 -->
<Search id="header-search" />
<LaySearch id="header-search" />
<!-- 国际化 -->
<el-dropdown id="header-translation" trigger="click">
<globalization
<GlobalizationIcon
class="navbar-bg-hover w-[40px] h-[48px] p-[11px] cursor-pointer outline-none"
/>
<template #dropdown>
@@ -130,9 +133,9 @@ watch(
</template>
</el-dropdown>
<!-- 全屏 -->
<FullScreen id="full-screen" />
<LaySidebarFullScreen id="full-screen" />
<!-- 消息通知 -->
<Notice id="header-notice" />
<LayNotice id="header-notice" />
<!-- 退出登录 -->
<el-dropdown trigger="click">
<span class="el-dropdown-link navbar-bg-hover select-none">
@@ -140,6 +143,13 @@ watch(
<p v-if="username" class="dark:text-white">{{ username }}</p>
</span>
<template #dropdown>
<el-dropdown-item @click="toAccountSettings">
<IconifyIconOffline
:icon="AccountSettingsIcon"
style="margin: 5px"
/>
{{ t("buttons.pureAccountSettings") }}
</el-dropdown-item>
<el-dropdown-menu class="logout">
<el-dropdown-item @click="logout">
<IconifyIconOffline
@@ -153,7 +163,7 @@ watch(
</el-dropdown>
<span
class="set-icon navbar-bg-hover"
:title="t('buttons.pureSystemSet')"
:title="t('buttons.pureOpenSystemSet')"
@click="onPanel"
>
<IconifyIconOffline :icon="Setting" />
@@ -184,7 +194,7 @@ watch(
}
.logout {
max-width: 120px;
width: 120px;
::v-deep(.el-dropdown-menu__item) {
display: inline-flex;

View File

@@ -1,16 +1,16 @@
<script setup lang="ts">
import Logo from "./logo.vue";
import { useRoute } from "vue-router";
import { emitter } from "@/utils/mitt";
import SidebarItem from "./sidebarItem.vue";
import LeftCollapse from "./leftCollapse.vue";
import { useNav } from "@/layout/hooks/useNav";
import CenterCollapse from "./centerCollapse.vue";
import { responsiveStorageNameSpace } from "@/config";
import { storageLocal, isAllEmpty } from "@pureadmin/utils";
import { findRouteByPath, getParentPaths } from "@/router/utils";
import { usePermissionStoreHook } from "@/store/modules/permission";
import { ref, computed, watch, onMounted, onBeforeUnmount } from "vue";
import LaySidebarLogo from "../lay-sidebar/components/SidebarLogo.vue";
import LaySidebarItem from "../lay-sidebar/components/SidebarItem.vue";
import LaySidebarLeftCollapse from "../lay-sidebar/components/SidebarLeftCollapse.vue";
import LaySidebarCenterCollapse from "../lay-sidebar/components/SidebarCenterCollapse.vue";
const route = useRoute();
const isShow = ref(false);
@@ -93,13 +93,12 @@ onBeforeUnmount(() => {
@mouseenter.prevent="isShow = true"
@mouseleave.prevent="isShow = false"
>
<Logo v-if="showLogo" :collapse="isCollapse" />
<LaySidebarLogo v-if="showLogo" :collapse="isCollapse" />
<el-scrollbar
wrap-class="scrollbar-wrapper"
:class="[device === 'mobile' ? 'mobile' : 'pc']"
>
<el-menu
router
unique-opened
mode="vertical"
popper-class="pure-scrollbar"
@@ -109,7 +108,7 @@ onBeforeUnmount(() => {
:popper-effect="tooltipEffect"
:default-active="defaultActive"
>
<sidebar-item
<LaySidebarItem
v-for="routes in menuData"
:key="routes.path"
:item="routes"
@@ -118,12 +117,12 @@ onBeforeUnmount(() => {
/>
</el-menu>
</el-scrollbar>
<CenterCollapse
<LaySidebarCenterCollapse
v-if="device !== 'mobile' && (isShow || isCollapse)"
:is-active="pureApp.sidebar.opened"
@toggleClick="toggleSideBar"
/>
<LeftCollapse
<LaySidebarLeftCollapse
v-if="device !== 'mobile'"
:is-active="pureApp.sidebar.opened"
@toggleClick="toggleSideBar"

View File

@@ -1,5 +1,6 @@
<script setup lang="ts">
import { computed } from "vue";
import { useI18n } from "vue-i18n";
import { useGlobal } from "@pureadmin/utils";
import { useNav } from "@/layout/hooks/useNav";
@@ -9,10 +10,11 @@ interface Props {
isActive: boolean;
}
const props = withDefaults(defineProps<Props>(), {
withDefaults(defineProps<Props>(), {
isActive: false
});
const { t } = useI18n();
const { tooltipEffect } = useNav();
const iconClass = computed(() => {
@@ -34,7 +36,9 @@ const toggleClick = () => {
<template>
<div
v-tippy="{
content: props.isActive ? '点击折叠' : '点击展开',
content: isActive
? t('buttons.pureClickCollapse')
: t('buttons.pureClickExpand'),
theme: tooltipEffect,
hideOnClick: 'toggle',
placement: 'right'
@@ -45,7 +49,7 @@ const toggleClick = () => {
<IconifyIconOffline
:icon="ArrowLeft"
:class="[iconClass, themeColor === 'light' ? '' : 'text-primary']"
:style="{ transform: props.isActive ? 'none' : 'rotateY(180deg)' }"
:style="{ transform: isActive ? 'none' : 'rotateY(180deg)' }"
/>
</div>
</template>

View File

@@ -2,7 +2,7 @@
import { toRaw } from "vue";
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
const props = defineProps({
defineProps({
extraIcon: {
type: String,
default: ""
@@ -11,9 +11,9 @@ const props = defineProps({
</script>
<template>
<div v-if="props.extraIcon" class="flex justify-center items-center">
<div v-if="extraIcon" class="flex justify-center items-center">
<component
:is="useRenderIcon(toRaw(props.extraIcon))"
:is="useRenderIcon(toRaw(extraIcon))"
class="w-[30px] h-[30px]"
/>
</div>

View File

@@ -1,12 +1,12 @@
<script setup lang="ts">
import path from "path";
import { getConfig } from "@/config";
import LinkItem from "./linkItem.vue";
import { menuType } from "../../types";
import extraIcon from "./extraIcon.vue";
import { menuType } from "@/layout/types";
import { ReText } from "@/components/ReText";
import { useNav } from "@/layout/hooks/useNav";
import { transformI18n } from "@/plugins/i18n";
import SidebarLinkItem from "./SidebarLinkItem.vue";
import SidebarExtraIcon from "./SidebarExtraIcon.vue";
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
import {
type PropType,
@@ -106,9 +106,9 @@ function resolvePath(routePath) {
</script>
<template>
<link-item
<SidebarLinkItem
v-if="
hasOneShowingChild(props.item.children, props.item) &&
hasOneShowingChild(item.children, item) &&
(!onlyOneChild.children || onlyOneChild.noShowingChildren)
"
:to="item"
@@ -120,7 +120,7 @@ function resolvePath(routePath) {
v-bind="attrs"
>
<div
v-if="toRaw(props.item.meta.icon)"
v-if="toRaw(item.meta.icon)"
class="sub-menu-icon"
:style="getSubMenuIconStyle"
>
@@ -128,24 +128,24 @@ function resolvePath(routePath) {
:is="
useRenderIcon(
toRaw(onlyOneChild.meta.icon) ||
(props.item.meta && toRaw(props.item.meta.icon))
(item.meta && toRaw(item.meta.icon))
)
"
/>
</div>
<el-text
v-if="
(!props.item?.meta.icon &&
(!item?.meta.icon &&
isCollapse &&
layout === 'vertical' &&
props.item?.pathList?.length === 1) ||
item?.pathList?.length === 1) ||
(!onlyOneChild.meta.icon &&
isCollapse &&
layout === 'mix' &&
props.item?.pathList?.length === 2)
item?.pathList?.length === 2)
"
truncated
class="!px-4 !text-inherit"
class="!w-full !px-4 !text-inherit"
>
{{ transformI18n(onlyOneChild.meta.title) }}
</el-text>
@@ -157,61 +157,62 @@ function resolvePath(routePath) {
offset: [0, -10],
theme: tooltipEffect
}"
class="!text-inherit"
class="!w-full !text-inherit"
>
{{ transformI18n(onlyOneChild.meta.title) }}
</ReText>
<extraIcon :extraIcon="onlyOneChild.meta.extraIcon" />
<SidebarExtraIcon :extraIcon="onlyOneChild.meta.extraIcon" />
</div>
</template>
</el-menu-item>
</link-item>
</SidebarLinkItem>
<el-sub-menu
v-else
ref="subMenu"
teleported
:index="resolvePath(props.item.path)"
:index="resolvePath(item.path)"
v-bind="expandCloseIcon"
>
<template #title>
<div
v-if="toRaw(props.item.meta.icon)"
v-if="toRaw(item.meta.icon)"
:style="getSubMenuIconStyle"
class="sub-menu-icon"
>
<component
:is="useRenderIcon(props.item.meta && toRaw(props.item.meta.icon))"
/>
<component :is="useRenderIcon(item.meta && toRaw(item.meta.icon))" />
</div>
<ReText
v-if="
!(
layout === 'vertical' &&
isCollapse &&
toRaw(props.item.meta.icon) &&
props.item.parentId === null
)
layout === 'mix' && toRaw(item.meta.icon)
? !isCollapse || item?.pathList?.length !== 2
: !(
layout === 'vertical' &&
isCollapse &&
toRaw(item.meta.icon) &&
item.parentId === null
)
"
:tippyProps="{
offset: [0, -10],
theme: tooltipEffect
}"
:class="{
'!w-full': true,
'!text-inherit': true,
'!px-4':
layout !== 'horizontal' &&
isCollapse &&
!toRaw(props.item.meta.icon) &&
props.item.parentId === null
!toRaw(item.meta.icon) &&
item.parentId === null
}"
>
{{ transformI18n(props.item.meta.title) }}
{{ transformI18n(item.meta.title) }}
</ReText>
<extraIcon v-if="!isCollapse" :extraIcon="props.item.meta.extraIcon" />
<SidebarExtraIcon v-if="!isCollapse" :extraIcon="item.meta.extraIcon" />
</template>
<sidebar-item
v-for="child in props.item.children"
v-for="child in item.children"
:key="child.path"
:is-nest="true"
:item="child"

View File

@@ -1,5 +1,6 @@
<script setup lang="ts">
import { computed } from "vue";
import { useI18n } from "vue-i18n";
import { useGlobal } from "@pureadmin/utils";
import { useNav } from "@/layout/hooks/useNav";
@@ -9,10 +10,11 @@ interface Props {
isActive: boolean;
}
const props = withDefaults(defineProps<Props>(), {
withDefaults(defineProps<Props>(), {
isActive: false
});
const { t } = useI18n();
const { tooltipEffect } = useNav();
const iconClass = computed(() => {
@@ -44,14 +46,16 @@ const toggleClick = () => {
<div class="left-collapse">
<IconifyIconOffline
v-tippy="{
content: props.isActive ? '点击折叠' : '点击展开',
content: isActive
? t('buttons.pureClickCollapse')
: t('buttons.pureClickExpand'),
theme: tooltipEffect,
hideOnClick: 'toggle',
placement: 'right'
}"
:icon="MenuFold"
:class="[iconClass, themeColor === 'light' ? '' : 'text-primary']"
:style="{ transform: props.isActive ? 'none' : 'rotateY(180deg)' }"
:style="{ transform: isActive ? 'none' : 'rotateY(180deg)' }"
@click="toggleClick"
/>
</div>

View File

@@ -3,10 +3,6 @@ import { computed } from "vue";
import { isUrl } from "@pureadmin/utils";
import { menuType } from "@/layout/types";
defineOptions({
name: "LinkItem"
});
const props = defineProps<{
to: menuType;
}>();

View File

@@ -2,7 +2,7 @@
import { getTopMenu } from "@/router/utils";
import { useNav } from "@/layout/hooks/useNav";
const props = defineProps({
defineProps({
collapse: Boolean
});
@@ -10,11 +10,11 @@ const { title, getLogo } = useNav();
</script>
<template>
<div class="sidebar-logo-container" :class="{ collapses: props.collapse }">
<div class="sidebar-logo-container" :class="{ collapses: collapse }">
<transition name="sidebarLogoFade">
<router-link
v-if="props.collapse"
key="props.collapse"
v-if="collapse"
key="collapse"
:title="title"
class="sidebar-logo-link"
:to="getTopMenu()?.path ?? '/'"

View File

@@ -1,4 +1,5 @@
<script setup lang="ts">
import { useI18n } from "vue-i18n";
import MenuFold from "@iconify-icons/ri/menu-fold-fill";
import MenuUnfold from "@iconify-icons/ri/menu-unfold-fill";
@@ -6,10 +7,12 @@ interface Props {
isActive: boolean;
}
const props = withDefaults(defineProps<Props>(), {
withDefaults(defineProps<Props>(), {
isActive: false
});
const { t } = useI18n();
const emit = defineEmits<{
(e: "toggleClick"): void;
}>();
@@ -22,11 +25,13 @@ const toggleClick = () => {
<template>
<div
class="px-3 mr-1 navbar-bg-hover"
:title="props.isActive ? '点击折叠' : '点击展开'"
:title="
isActive ? t('buttons.pureClickCollapse') : t('buttons.pureClickExpand')
"
@click="toggleClick"
>
<IconifyIconOffline
:icon="props.isActive ? MenuFold : MenuUnfold"
:icon="isActive ? MenuFold : MenuUnfold"
class="inline-block align-middle hover:text-primary dark:hover:!text-white"
/>
</div>

View File

@@ -90,6 +90,10 @@
padding: 0 12px;
}
}
.fixed-tag {
padding: 0 12px;
}
}
}

View File

@@ -8,6 +8,7 @@ import { onClickOutside } from "@vueuse/core";
import { handleAliveRoute, getTopMenu } from "@/router/utils";
import { useSettingStoreHook } from "@/store/modules/settings";
import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
import { usePermissionStoreHook } from "@/store/modules/permission";
import { ref, watch, unref, toRaw, nextTick, onBeforeUnmount } from "vue";
import {
delay,
@@ -59,6 +60,10 @@ const contextmenuRef = ref();
const isShowArrow = ref(false);
const topPath = getTopMenu()?.path;
const { VITE_HIDE_HOME } = import.meta.env;
const fixedTags = [
...routerArrays,
...usePermissionStoreHook().flatteningRoutes.filter(v => v?.meta?.fixedTag)
];
const dynamicTagView = async () => {
await nextTick();
@@ -228,10 +233,13 @@ function deleteDynamicTag(obj: any, current: any, tag?: string) {
other?: boolean
): void => {
if (other) {
useMultiTagsStoreHook().handleTags("equal", [
VITE_HIDE_HOME === "false" ? routerArrays[0] : toRaw(getTopMenu()),
obj
]);
useMultiTagsStoreHook().handleTags(
"equal",
[
VITE_HIDE_HOME === "false" ? fixedTags : toRaw(getTopMenu()),
obj
].flat()
);
} else {
useMultiTagsStoreHook().handleTags("splice", "", {
startIndex,
@@ -244,7 +252,7 @@ function deleteDynamicTag(obj: any, current: any, tag?: string) {
if (tag === "other") {
spliceRoute(1, 1, true);
} else if (tag === "left") {
spliceRoute(1, valueIndex - 1);
spliceRoute(fixedTags.length, valueIndex - 1, true);
} else if (tag === "right") {
spliceRoute(valueIndex + 1, multiTags.value.length);
} else {
@@ -321,10 +329,11 @@ function onClickDrop(key, item, selectRoute?: RouteConfigs) {
case 5:
//
useMultiTagsStoreHook().handleTags("splice", "", {
startIndex: 1,
startIndex: fixedTags.length,
length: multiTags.value.length
});
router.push(topPath);
// router.push(fixedTags[fixedTags.length - 1]?.path);
handleAliveRoute(route as ToRouteType);
break;
case 6:
@@ -363,10 +372,14 @@ function showMenus(value: boolean) {
});
}
function disabledMenus(value: boolean) {
function disabledMenus(value: boolean, fixedTag = false) {
Array.of(1, 2, 3, 4, 5).forEach(v => {
tagsViews[v].disabled = value;
});
if (fixedTag) {
tagsViews[2].show = false;
tagsViews[2].disabled = true;
}
}
/** 检查当前右键的菜单两边是否存在别的菜单,如果左侧的菜单是顶级菜单,则不显示关闭左侧标签页,如果右侧没有菜单,则不显示关闭右侧标签页 */
@@ -383,6 +396,13 @@ function showMenuModel(
} else {
currentIndex = allRoute.findIndex(v => isEqual(v.query, query));
}
function fixedTagDisabled() {
if (allRoute[currentIndex]?.meta?.fixedTag) {
Array.of(1, 2, 3, 4, 5).forEach(v => {
tagsViews[v].disabled = true;
});
}
}
showMenus(true);
@@ -401,6 +421,7 @@ function showMenuModel(
tagsViews[v].disabled = false;
});
tagsViews[2].disabled = true;
fixedTagDisabled();
} else if (currentIndex === 1 && routeLength === 2) {
disabledMenus(false);
//
@@ -408,6 +429,7 @@ function showMenuModel(
tagsViews[v].show = false;
tagsViews[v].disabled = true;
});
fixedTagDisabled();
} else if (routeLength - 1 === currentIndex && currentIndex !== 0) {
//
tagsViews[3].show = false;
@@ -415,29 +437,31 @@ function showMenuModel(
tagsViews[v].disabled = false;
});
tagsViews[3].disabled = true;
if (allRoute[currentIndex - 1]?.meta?.fixedTag) {
tagsViews[2].show = false;
tagsViews[2].disabled = true;
}
fixedTagDisabled();
} else if (currentIndex === 0 || currentPath === `/redirect${topPath}`) {
//
disabledMenus(true);
} else {
disabledMenus(false);
disabledMenus(false, allRoute[currentIndex - 1]?.meta?.fixedTag);
fixedTagDisabled();
}
}
function openMenu(tag, e) {
closeMenu();
if (tag.path === topPath) {
//
if (tag.path === topPath || tag?.meta?.fixedTag) {
// fixedTag
showMenus(false);
tagsViews[0].show = true;
} else if (route.path !== tag.path && route.name !== tag.name) {
//
tagsViews[0].show = false;
showMenuModel(tag.path, tag.query);
} else if (
// eslint-disable-next-line no-dupe-else-if
multiTags.value.length === 2 &&
route.path !== tag.path
) {
} else if (multiTags.value.length === 2 && route.path !== tag.path) {
showMenus(true);
//
tagsViews[4].show = false;
@@ -485,7 +509,6 @@ function tagOnClick(item) {
} else {
router.push({ path });
}
// showMenuModel(item?.path, item?.query);
}
onClickOutside(contextmenuRef, closeMenu, {
@@ -549,7 +572,11 @@ onBeforeUnmount(() => {
v-for="(item, index) in multiTags"
:ref="'dynamic' + index"
:key="index"
:class="['scroll-item is-closable', linkIsActive(item)]"
:class="[
'scroll-item is-closable',
linkIsActive(item),
!isAllEmpty(item?.meta?.fixedTag) && 'fixed-tag'
]"
@contextmenu.prevent="openMenu(item, $event)"
@mouseenter.prevent="onMouseenter(index)"
@mouseleave.prevent="onMouseleave(index)"
@@ -562,8 +589,10 @@ onBeforeUnmount(() => {
</span>
<span
v-if="
iconIsActive(item, index) ||
(index === activeIndex && index !== 0)
isAllEmpty(item?.meta?.fixedTag)
? iconIsActive(item, index) ||
(index === activeIndex && index !== 0)
: false
"
class="el-icon-close"
@click.stop="deleteMenu(item)"

View File

@@ -1,146 +0,0 @@
export interface ListItem {
avatar: string;
title: string;
datetime: string;
type: string;
description: string;
status?: "primary" | "success" | "warning" | "info" | "danger";
extra?: string;
}
export interface TabItem {
key: string;
name: string;
list: ListItem[];
}
export const noticesData: TabItem[] = [
{
key: "1",
name: "通知",
list: [
{
avatar:
"https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png",
title: "你收到了 12 份新周报",
datetime: "一年前",
description: "",
type: "1"
},
{
avatar:
"https://gw.alipayobjects.com/zos/rmsportal/OKJXDXrmkNshAMvwtvhu.png",
title: "你推荐的 前端高手 已通过第三轮面试",
datetime: "一年前",
description: "",
type: "1"
},
{
avatar:
"https://gw.alipayobjects.com/zos/rmsportal/kISTdvpyTAhtGxpovNWd.png",
title: "这种模板可以区分多种通知类型",
datetime: "一年前",
description: "",
type: "1"
},
{
avatar:
"https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png",
title:
"展示标题内容超过一行后的处理方式如果内容超过1行将自动截断并支持tooltip显示完整标题。",
datetime: "一年前",
description: "",
type: "1"
},
{
avatar:
"https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png",
title: "左侧图标用于区分不同的类型",
datetime: "一年前",
description: "",
type: "1"
},
{
avatar:
"https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png",
title: "左侧图标用于区分不同的类型",
datetime: "一年前",
description: "",
type: "1"
}
]
},
{
key: "2",
name: "消息",
list: [
{
avatar:
"https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg",
title: "李白 评论了你",
description: "长风破浪会有时,直挂云帆济沧海",
datetime: "一年前",
type: "2"
},
{
avatar:
"https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg",
title: "李白 回复了你",
description: "行路难,行路难,多歧路,今安在。",
datetime: "一年前",
type: "2"
},
{
avatar:
"https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg",
title: "标题",
description:
"请将鼠标移动到此处以便测试超长的消息在此处将如何处理。本例中设置的描述最大行数为2超过2行的描述内容将被省略并且可以通过tooltip查看完整内容",
datetime: "一年前",
type: "2"
}
]
},
{
key: "3",
name: "待办",
list: [
{
avatar: "",
title: "任务名称",
description: "任务需要在 2022-11-16 20:00 前启动",
datetime: "",
extra: "未开始",
status: "info",
type: "3"
},
{
avatar: "",
title: "第三方紧急代码变更",
description:
"一拳提交于 2022-11-16需在 2022-11-18 前完成代码变更任务",
datetime: "",
extra: "马上到期",
status: "danger",
type: "3"
},
{
avatar: "",
title: "信息安全考试",
description: "指派小仙于 2022-12-12 前完成更新并发布",
datetime: "",
extra: "已耗时 8 天",
status: "warning",
type: "3"
},
{
avatar: "",
title: "vue-pure-admin 版本发布",
description: "vue-pure-admin 版本发布",
datetime: "",
extra: "进行中",
type: "3"
}
]
}
];

View File

@@ -1,23 +0,0 @@
<script setup lang="ts">
import { PropType } from "vue";
import { ListItem } from "./data";
import NoticeItem from "./noticeItem.vue";
const props = defineProps({
list: {
type: Array as PropType<Array<ListItem>>,
default: () => []
}
});
</script>
<template>
<div v-if="props.list.length">
<NoticeItem
v-for="(item, index) in props.list"
:key="index"
:noticeItem="item"
/>
</div>
<el-empty v-else description="暂无消息" />
</template>

View File

@@ -1,3 +0,0 @@
import SearchModal from "./SearchModal.vue";
export { SearchModal };

View File

@@ -4,7 +4,7 @@ import { useRoute } from "vue-router";
import { ref, unref, watch, onMounted, nextTick } from "vue";
defineOptions({
name: "FrameView"
name: "LayFrame"
});
const props = defineProps<{

View File

@@ -2,22 +2,23 @@ import { storeToRefs } from "pinia";
import { getConfig } from "@/config";
import { useRouter } from "vue-router";
import { emitter } from "@/utils/mitt";
import userAvatar from "@/assets/user.jpg";
import Avatar from "@/assets/user.jpg";
import { getTopMenu } from "@/router/utils";
import { useFullscreen } from "@vueuse/core";
import { useGlobal } from "@pureadmin/utils";
import type { routeMetaType } from "../types";
import { transformI18n } from "@/plugins/i18n";
import { router, remainingPaths } from "@/router";
import { computed, type CSSProperties } from "vue";
import { useAppStoreHook } from "@/store/modules/app";
import { useUserStoreHook } from "@/store/modules/user";
import { useGlobal, isAllEmpty } from "@pureadmin/utils";
import { useEpThemeStoreHook } from "@/store/modules/epTheme";
import { usePermissionStoreHook } from "@/store/modules/permission";
import ExitFullscreen from "@iconify-icons/ri/fullscreen-exit-fill";
import Fullscreen from "@iconify-icons/ri/fullscreen-fill";
const errorInfo = "当前路由配置不正确,请检查配置";
const errorInfo =
"The current routing configuration is incorrect, please check the configuration";
export function useNav() {
const pureApp = useAppStoreHook();
@@ -37,9 +38,18 @@ export function useNav() {
};
});
/** 用户名 */
/** 头像(如果头像为空则使用 src/assets/user.jpg */
const userAvatar = computed(() => {
return isAllEmpty(useUserStoreHook()?.avatar)
? Avatar
: useUserStoreHook()?.avatar;
});
/** 昵称(如果昵称为空则显示用户名) */
const username = computed(() => {
return useUserStoreHook()?.username;
return isAllEmpty(useUserStoreHook()?.nickname)
? useUserStoreHook()?.username
: useUserStoreHook()?.nickname;
});
/** 设置国际化选中后的样式 */
@@ -99,6 +109,10 @@ export function useNav() {
emitter.emit("openPanel");
}
function toAccountSettings() {
router.push({ name: "AccountSettings" });
}
function toggleSideBar() {
pureApp.toggleSideBar();
}
@@ -159,6 +173,7 @@ export function useNav() {
userAvatar,
avatarsStyle,
tooltipEffect,
toAccountSettings,
getDropdownItemStyle,
getDropdownItemClass
};

View File

@@ -3,6 +3,7 @@ import "animate.css";
// 引入 src/components/ReIcon/src/offlineIcon.ts 文件中所有使用addIcon添加过的本地图标
import "@/components/ReIcon/src/offlineIcon";
import { setType } from "./types";
import { useI18n } from "vue-i18n";
import { useLayout } from "./hooks/useLayout";
import { useAppStoreHook } from "@/store/modules/app";
import { useSettingStoreHook } from "@/store/modules/settings";
@@ -23,14 +24,15 @@ import {
useResizeObserver
} from "@pureadmin/utils";
import navbar from "./components/navbar.vue";
import tag from "./components/tag/index.vue";
import appMain from "./components/appMain.vue";
import setting from "./components/setting/index.vue";
import Vertical from "./components/sidebar/vertical.vue";
import Horizontal from "./components/sidebar/horizontal.vue";
import backTop from "@/assets/svg/back_top.svg?component";
import LayTag from "./components/lay-tag/index.vue";
import LayNavbar from "./components/lay-navbar/index.vue";
import LayContent from "./components/lay-content/index.vue";
import LaySetting from "./components/lay-setting/index.vue";
import NavVertical from "./components/lay-sidebar/NavVertical.vue";
import NavHorizontal from "./components/lay-sidebar/NavHorizontal.vue";
import BackTopIcon from "@/assets/svg/back_top.svg?component";
const { t } = useI18n();
const appWrapperRef = ref();
const { isDark } = useDark();
const { layout } = useLayout();
@@ -124,7 +126,8 @@ onBeforeMount(() => {
useDataThemeChange().dataThemeChange($storage.layout?.overallStyle);
});
const layoutHeader = defineComponent({
const LayHeader = defineComponent({
name: "LayHeader",
render() {
return h(
"div",
@@ -142,12 +145,12 @@ const layoutHeader = defineComponent({
default: () => [
!pureSetting.hiddenSideBar &&
(layout.value.includes("vertical") || layout.value.includes("mix"))
? h(navbar)
? h(LayNavbar)
: null,
!pureSetting.hiddenSideBar && layout.value.includes("horizontal")
? h(Horizontal)
? h(NavHorizontal)
: null,
h(tag)
h(LayTag)
]
}
);
@@ -166,7 +169,7 @@ const layoutHeader = defineComponent({
class="app-mask"
@click="useAppStoreHook().toggleSideBar()"
/>
<Vertical
<NavVertical
v-show="
!pureSetting.hiddenSideBar &&
(layout.includes('vertical') || layout.includes('mix'))
@@ -179,24 +182,24 @@ const layoutHeader = defineComponent({
]"
>
<div v-if="set.fixedHeader">
<layout-header />
<LayHeader />
<!-- 主体内容 -->
<app-main :fixed-header="set.fixedHeader" />
<LayContent :fixed-header="set.fixedHeader" />
</div>
<el-scrollbar v-else>
<el-backtop
title="回到顶部"
:title="t('buttons.pureBackTop')"
target=".main-container .el-scrollbar__wrap"
>
<backTop />
<BackTopIcon />
</el-backtop>
<layout-header />
<LayHeader />
<!-- 主体内容 -->
<app-main :fixed-header="set.fixedHeader" />
<LayContent :fixed-header="set.fixedHeader" />
</el-scrollbar>
</div>
<!-- 系统设置 -->
<setting />
<LaySetting />
</div>
</template>

View File

@@ -5,8 +5,8 @@ import { responsiveStorageNameSpace } from "@/config";
import { storageLocal, isObject } from "@pureadmin/utils";
// element-plus国际化
import enLocale from "element-plus/dist/locale/en.mjs";
import zhLocale from "element-plus/dist/locale/zh-cn.mjs";
import enLocale from "element-plus/es/locale/lang/en";
import zhLocale from "element-plus/es/locale/lang/zh-cn";
const siphonI18n = (function () {
// 仅初始化一次国际化配置

View File

@@ -16,14 +16,16 @@ import {
// Export,
// Keyboard,
// Validator,
Custom,
// 可选组件
Icon,
Column,
Grid,
Pager,
Select,
// Colgroup,
// Grid,
// Tooltip,
// Toolbar,
// Pager,
// Form,
// FormItem,
// FormGather,
@@ -34,7 +36,6 @@ import {
// RadioButton,
// Switch,
// Input,
// Select,
// Optgroup,
// Option,
// Textarea,
@@ -64,21 +65,23 @@ VXETable.config({
export function useVxeTable(app: App) {
// 表格功能
// app.use(Filter)
// .use(Edit)
// .use(Menu)
// .use(Export)
// .use(Keyboard)
// .use(Validator)
// 可选组件
app
// .use(Filter)
// .use(Edit)
// .use(Menu)
// .use(Export)
// .use(Keyboard)
// .use(Validator)
.use(Custom)
// 可选组件
.use(Icon)
.use(Column)
.use(Grid)
.use(Pager)
.use(Select)
// .use(Colgroup)
// .use(Grid)
// .use(Tooltip)
// .use(Toolbar)
// .use(Pager)
// .use(Form)
// .use(FormItem)
// .use(FormGather)
@@ -89,7 +92,6 @@ export function useVxeTable(app: App) {
// .use(RadioButton)
// .use(Switch)
// .use(Input)
// .use(Select)
// .use(Optgroup)
// .use(Option)
// .use(Textarea)

View File

@@ -22,8 +22,9 @@ const home = 0, // 平台规定只有 home 路由的 rank 才能为 0 ,所以
formdesign = 19,
board = 20,
ppt = 21,
guide = 22,
menuoverflow = 23;
mind = 22,
guide = 23,
menuoverflow = 24;
export {
home,
@@ -48,6 +49,7 @@ export {
formdesign,
board,
ppt,
mind,
guide,
menuoverflow
};

View File

@@ -95,22 +95,6 @@ export default {
}
}
},
{
path: "/able/word",
name: "Word",
component: () => import("@/views/able/word.vue"),
meta: {
title: $t("menus.pureWord")
}
},
{
path: "/able/excels",
name: "Excels",
component: () => import("@/views/able/excels.vue"),
meta: {
title: $t("menus.pureExcels")
}
},
{
path: "/able/pdf",
name: "Pdf",

View File

@@ -1,6 +1,6 @@
import { $t } from "@/plugins/i18n";
import { board } from "@/router/enums";
const IFrame = () => import("@/layout/frameView.vue");
const IFrame = () => import("@/layout/frame.vue");
export default {
path: "/board",
@@ -17,6 +17,7 @@ export default {
component: IFrame,
meta: {
title: $t("menus.pureBoard"),
keepAlive: true,
frameSrc: "https://songlh.top/paint-board/"
}
}

View File

@@ -1,6 +1,6 @@
import { $t } from "@/plugins/i18n";
import { formdesign } from "@/router/enums";
const IFrame = () => import("@/layout/frameView.vue");
const IFrame = () => import("@/layout/frame.vue");
export default {
path: "/form-design",
@@ -17,6 +17,7 @@ export default {
component: IFrame,
meta: {
title: $t("menus.pureFormDesign"),
keepAlive: true,
frameSrc:
"https://haixin-fang.github.io/vue-form-design/playground/index.html",
frameLoading: false

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