Merge branch 'main' into gitee

This commit is contained in:
xiaoxian521 2024-05-06 16:08:45 +08:00
commit b617ce650a
89 changed files with 1617 additions and 1152 deletions

View File

@ -32,7 +32,7 @@ body:
label: 验证 (Verify) label: 验证 (Verify)
description: 在提交问题之前,请确保您执行以下操作 (Before submitting an issue, please ensure you do the following) description: 在提交问题之前,请确保您执行以下操作 (Before submitting an issue, please ensure you do the following)
options: 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 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)) - 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 required: true

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

@ -0,0 +1,42 @@
name: Build and Deploy
permissions:
contents: write
on:
push:
branches:
- main
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: 8.6.10
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

@ -379,7 +379,7 @@ Totally `ESM` version
### ✔️ refactor ### ✔️ refactor
- completely removed `lodash` and its related libraries - 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 ### 🎫 Feat
@ -399,7 +399,7 @@ Totally `ESM` version
### ✔️ 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 🐮 - 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 ### 🎫 Feat
@ -511,7 +511,7 @@ Totally `ESM` version
### 🎫 Feat ### 🎫 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 - 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) - 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 - 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
@ -612,7 +612,7 @@ Totally `ESM` version
### ✔️ 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 ### 🐞 Bug fixes

View File

@ -379,7 +379,7 @@ Totally `ESM` version
### ✔️ refactor ### ✔️ refactor
- completely removed `lodash` and its related libraries - 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 ### 🎫 Feat
@ -399,7 +399,7 @@ Totally `ESM` version
### ✔️ 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 🐮 - 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 ### 🎫 Feat
@ -511,7 +511,7 @@ Totally `ESM` version
### 🎫 Feat ### 🎫 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 - 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) - 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 - 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
@ -612,7 +612,7 @@ Totally `ESM` version
### ✔️ 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 ### 🐞 Bug fixes

View File

@ -378,7 +378,7 @@
### ✔️ refactor ### ✔️ refactor
- 完全移除了 `lodash` 和其相关库 - 完全移除了 `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 ### 🎫 Feat
@ -398,7 +398,7 @@
### ✔️ refactor ### ✔️ refactor
- 完全移除了 `vxe-table`,移除后,完整版整体打包大小减少 `1.82MB`,首启动时长基本和精简版持平 🐮 - 完全移除了 `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 ### 🎫 Feat
@ -510,7 +510,7 @@
### 🎫 Feat ### 🎫 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) 示例保持一致 - 为 [@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` 即可解决 - 优化菜单,添加 `MenuArrowIconNoTransition` 全局配置,在 `public/platform-config.json` 中配置即可,对于出现左侧菜单模式,菜单展开卡顿的可设置 `MenuArrowIconNoTransition: true` 即可解决
@ -611,7 +611,7 @@
### ✔️ 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 ### 🐞 Bug fixes

View File

@ -30,12 +30,12 @@ The simplified version is based on the shelf extracted from [vue-pure-admin](htt
## Nanny-level documents ## 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) [Click me to view @pureadmin/utils documentation](https://pure-admin-utils.netlify.app)
## Quality service, software outsourcing, sponsorship support ## 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 ## Tauri
@ -47,7 +47,7 @@ The simplified version is based on the shelf extracted from [vue-pure-admin](htt
## Preview ## Preview
[preview station](https://yiming_chang.gitee.io/vue-pure-admin) [preview station](https://pure-admin.github.io/vue-pure-admin)
`PC` `PC`

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) [点我查看 @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` 版本 ## `Tauri` 版本
@ -48,7 +48,7 @@
## 预览 ## 预览
[点我查看预览](https://yiming_chang.gitee.io/vue-pure-admin) [点我查看预览](https://pure-admin.github.io/vue-pure-admin)
`PC` `PC`

View File

@ -7,7 +7,7 @@ import boxen, { type Options as BoxenOptions } from "boxen";
dayjs.extend(duration); dayjs.extend(duration);
const welcomeMessage = gradientString("cyan", "magenta").multiline( 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 = { const boxenOptions: BoxenOptions = {

View File

@ -182,7 +182,12 @@ menus:
pureChildMenuOverflow: Child Menu Overflow Show Tooltip Text pureChildMenuOverflow: Child Menu Overflow Show Tooltip Text
status: status:
pureLoad: Loading... pureLoad: Loading...
pureMessage: Message
pureNotify: Notify
pureTodo: Todo
pureNoMessage: No Message pureNoMessage: No Message
pureNoNotify: No Notify
pureNoTodo: No Todo
login: login:
pureUsername: Username pureUsername: Username
purePassword: Password purePassword: Password

View File

@ -182,7 +182,12 @@ menus:
pureChildMenuOverflow: 菜单超出显示 Tooltip 文字提示 pureChildMenuOverflow: 菜单超出显示 Tooltip 文字提示
status: status:
pureLoad: 加载中... pureLoad: 加载中...
pureMessage: 消息
pureNotify: 通知
pureTodo: 待办
pureNoMessage: 暂无消息 pureNoMessage: 暂无消息
pureNoNotify: 暂无通知
pureNoTodo: 暂无待办
login: login:
pureUsername: 账号 pureUsername: 账号
purePassword: 密码 purePassword: 密码

View File

@ -154,7 +154,7 @@ const frameRouter = {
children: [ children: [
{ {
path: "/external", path: "/external",
name: "https://yiming_chang.gitee.io/pure-admin-doc", name: "https://pure-admin.github.io/pure-admin-doc",
meta: { meta: {
title: "menus.pureExternalLink", title: "menus.pureExternalLink",
roles: ["admin", "common"] roles: ["admin", "common"]

View File

@ -430,7 +430,7 @@ export default defineFakeRoute([
id: 102, id: 102,
menuType: 2, menuType: 2,
title: "menus.pureExternalLink", title: "menus.pureExternalLink",
name: "https://yiming_chang.gitee.io/pure-admin-doc", name: "https://pure-admin.github.io/pure-admin-doc",
path: "/external", path: "/external",
component: "", component: "",
rank: null, rank: null,

View File

@ -56,7 +56,7 @@
"@pureadmin/table": "^3.1.2", "@pureadmin/table": "^3.1.2",
"@pureadmin/utils": "^2.4.7", "@pureadmin/utils": "^2.4.7",
"@vue-flow/background": "^1.3.0", "@vue-flow/background": "^1.3.0",
"@vue-flow/core": "^1.33.5", "@vue-flow/core": "^1.33.6",
"@vueuse/core": "^10.9.0", "@vueuse/core": "^10.9.0",
"@vueuse/motion": "^2.1.0", "@vueuse/motion": "^2.1.0",
"@wangeditor/editor": "^5.1.23", "@wangeditor/editor": "^5.1.23",
@ -66,10 +66,10 @@
"axios": "^1.6.8", "axios": "^1.6.8",
"china-area-data": "^5.0.1", "china-area-data": "^5.0.1",
"cropperjs": "^1.6.2", "cropperjs": "^1.6.2",
"dayjs": "^1.11.10", "dayjs": "^1.11.11",
"echarts": "^5.5.0", "echarts": "^5.5.0",
"el-table-infinite-scroll": "^3.0.3", "el-table-infinite-scroll": "^3.0.3",
"element-plus": "^2.7.1", "element-plus": "2.7.1",
"intro.js": "^7.2.0", "intro.js": "^7.2.0",
"js-cookie": "^3.0.5", "js-cookie": "^3.0.5",
"jsbarcode": "^3.11.6", "jsbarcode": "^3.11.6",
@ -80,7 +80,7 @@
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
"path": "^0.12.7", "path": "^0.12.7",
"pinia": "^2.1.7", "pinia": "^2.1.7",
"pinyin-pro": "^3.20.2", "pinyin-pro": "^3.20.3",
"plus-pro-components": "^0.0.11", "plus-pro-components": "^0.0.11",
"qrcode": "^1.5.3", "qrcode": "^1.5.3",
"qs": "^6.12.1", "qs": "^6.12.1",
@ -91,21 +91,21 @@
"v-contextmenu": "^3.2.0", "v-contextmenu": "^3.2.0",
"v3-infinite-loading": "^1.3.1", "v3-infinite-loading": "^1.3.1",
"version-rocket": "^1.7.1", "version-rocket": "^1.7.1",
"vue": "^3.4.25", "vue": "^3.4.26",
"vue-i18n": "^9.13.1", "vue-i18n": "^9.13.1",
"vue-json-pretty": "^2.4.0", "vue-json-pretty": "^2.4.0",
"vue-pdf-embed": "^2.0.3", "vue-pdf-embed": "^2.0.3",
"vue-router": "^4.3.2", "vue-router": "^4.3.2",
"vue-tippy": "^6.4.1", "vue-tippy": "^6.4.1",
"vue-types": "^5.1.1", "vue-types": "^5.1.2",
"vue-virtual-scroller": "2.0.0-beta.8", "vue-virtual-scroller": "2.0.0-beta.8",
"vue-waterfall-plugin-next": "^2.4.3", "vue-waterfall-plugin-next": "^2.4.3",
"vue3-danmaku": "^1.6.0", "vue3-danmaku": "^1.6.0",
"vue3-puzzle-vcode": "^1.1.7", "vue3-puzzle-vcode": "^1.1.7",
"vuedraggable": "^4.1.0", "vuedraggable": "^4.1.0",
"vxe-table": "^4.6.3", "vxe-table": "^4.6.8",
"wavesurfer.js": "^7.7.11", "wavesurfer.js": "^7.7.12",
"xgplayer": "^3.0.16", "xgplayer": "^3.0.17",
"xlsx": "^0.18.5" "xlsx": "^0.18.5"
}, },
"devDependencies": { "devDependencies": {
@ -123,18 +123,18 @@
"@types/gradient-string": "^1.1.6", "@types/gradient-string": "^1.1.6",
"@types/intro.js": "^5.1.5", "@types/intro.js": "^5.1.5",
"@types/js-cookie": "^3.0.6", "@types/js-cookie": "^3.0.6",
"@types/node": "^20.12.7", "@types/node": "^20.12.8",
"@types/nprogress": "^0.2.3", "@types/nprogress": "^0.2.3",
"@types/qrcode": "^1.5.5", "@types/qrcode": "^1.5.5",
"@types/qs": "^6.9.15", "@types/qs": "^6.9.15",
"@types/sortablejs": "^1.15.8", "@types/sortablejs": "^1.15.8",
"@typescript-eslint/eslint-plugin": "^7.7.1", "@typescript-eslint/eslint-plugin": "^7.8.0",
"@typescript-eslint/parser": "^7.7.1", "@typescript-eslint/parser": "^7.8.0",
"@vitejs/plugin-vue": "^5.0.4", "@vitejs/plugin-vue": "^5.0.4",
"@vitejs/plugin-vue-jsx": "^3.1.0", "@vitejs/plugin-vue-jsx": "^3.1.0",
"autoprefixer": "^10.4.19", "autoprefixer": "^10.4.19",
"boxen": "^7.1.1", "boxen": "^7.1.1",
"cssnano": "^7.0.0", "cssnano": "^7.0.1",
"dagre": "^0.8.5", "dagre": "^0.8.5",
"eslint": "^9.1.1", "eslint": "^9.1.1",
"eslint-config-prettier": "^9.1.0", "eslint-config-prettier": "^9.1.0",
@ -151,8 +151,8 @@
"prettier": "^3.2.5", "prettier": "^3.2.5",
"rimraf": "^5.0.5", "rimraf": "^5.0.5",
"rollup-plugin-visualizer": "^5.12.0", "rollup-plugin-visualizer": "^5.12.0",
"sass": "^1.75.0", "sass": "^1.76.0",
"stylelint": "^16.4.0", "stylelint": "^16.5.0",
"stylelint-config-recess-order": "^5.0.1", "stylelint-config-recess-order": "^5.0.1",
"stylelint-config-recommended-vue": "^1.5.0", "stylelint-config-recommended-vue": "^1.5.0",
"stylelint-config-standard-scss": "^13.1.0", "stylelint-config-standard-scss": "^13.1.0",
@ -160,7 +160,7 @@
"svgo": "^3.2.0", "svgo": "^3.2.0",
"tailwindcss": "^3.4.3", "tailwindcss": "^3.4.3",
"typescript": "^5.4.5", "typescript": "^5.4.5",
"vite": "^5.2.10", "vite": "^5.2.11",
"vite-plugin-cdn-import": "^0.3.5", "vite-plugin-cdn-import": "^0.3.5",
"vite-plugin-compression": "^0.5.1", "vite-plugin-compression": "^0.5.1",
"vite-plugin-fake-server": "^2.1.1", "vite-plugin-fake-server": "^2.1.1",

1280
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

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

View File

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

View File

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

View File

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

View File

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

View File

@ -4,7 +4,7 @@ import { IconifyIconOnline, IconifyIconOffline, FontIcon } from "../index";
/** /**
* `iconfont` `svg` `iconify` * `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 icon
* @param attrs iconType * @param attrs iconType
* @returns Component * @returns Component

View File

@ -18,11 +18,11 @@ import {
getKeyList getKeyList
} from "@pureadmin/utils"; } from "@pureadmin/utils";
import DragIcon from "./svg/drag.svg?component"; import DragIcon from "@/assets/table-bar/drag.svg?component";
import ExpandIcon from "./svg/expand.svg?component"; import ExpandIcon from "@/assets/table-bar/expand.svg?component";
import RefreshIcon from "./svg/refresh.svg?component"; import RefreshIcon from "@/assets/table-bar/refresh.svg?component";
import SettingIcon from "./svg/settings.svg?component"; import SettingIcon from "@/assets/table-bar/settings.svg?component";
import CollapseIcon from "./svg/collapse.svg?component"; import CollapseIcon from "@/assets/table-bar/collapse.svg?component";
const props = { const props = {
/** 头部最左边的标题 */ /** 头部最左边的标题 */

View File

@ -502,7 +502,7 @@ defineExpose({
</script> </script>
<template> <template>
<div :ref="'wrap' + props.classOption['key']"> <div :ref="'wrap' + classOption['key']">
<div <div
v-if="navigation" v-if="navigation"
:style="leftSwitch" :style="leftSwitch"
@ -520,7 +520,7 @@ defineExpose({
<slot name="right-switch" /> <slot name="right-switch" />
</div> </div>
<div <div
:ref="'realBox' + props.classOption['key']" :ref="'realBox' + classOption['key']"
:style="pos" :style="pos"
@mouseenter="enter" @mouseenter="enter"
@mouseleave="leave" @mouseleave="leave"
@ -529,7 +529,7 @@ defineExpose({
@touchend="touchEnd" @touchend="touchEnd"
@mousewheel.passive="wheel" @mousewheel.passive="wheel"
> >
<div :ref="'slotList' + props.classOption['key']" :style="float"> <div :ref="'slotList' + classOption['key']" :style="float">
<slot /> <slot />
</div> </div>
<div :style="float" v-html="copyHtml" /> <div :style="float" v-html="copyHtml" />

View File

@ -6,7 +6,7 @@ export interface OptionsType {
label?: string | (() => VNode | Component); label?: string | (() => VNode | Component);
/** /**
* @description `useRenderIcon` * @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; icon?: string | Component;
/** 图标属性、样式配置 */ /** 图标属性、样式配置 */

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

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

View File

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

View File

@ -1,13 +1,14 @@
<script setup lang="ts"> <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 { useNav } from "@/layout/hooks/useNav";
import FullScreen from "./sidebar/fullScreen.vue"; import LaySearch from "../lay-search/index.vue";
import Breadcrumb from "./sidebar/breadCrumb.vue"; import LayNotice from "../lay-notice/index.vue";
import topCollapse from "./sidebar/topCollapse.vue"; import LayNavMix from "../lay-sidebar/NavMix.vue";
import { useTranslationLang } from "../hooks/useTranslationLang"; import { useTranslationLang } from "@/layout/hooks/useTranslationLang";
import globalization from "@/assets/svg/globalization.svg?component"; 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 AccountSettingsIcon from "@iconify-icons/ri/user-settings-line";
import LogoutCircleRLine from "@iconify-icons/ri/logout-circle-r-line"; import LogoutCircleRLine from "@iconify-icons/ri/logout-circle-r-line";
import Setting from "@iconify-icons/ri/settings-3-line"; import Setting from "@iconify-icons/ri/settings-3-line";
@ -33,26 +34,26 @@ const { t, locale, translationCh, translationEn } = useTranslationLang();
<template> <template>
<div class="navbar bg-[#fff] shadow-sm shadow-[rgba(0,21,41,0.08)]"> <div class="navbar bg-[#fff] shadow-sm shadow-[rgba(0,21,41,0.08)]">
<topCollapse <LaySidebarTopCollapse
v-if="device === 'mobile'" v-if="device === 'mobile'"
class="hamburger-container" class="hamburger-container"
:is-active="pureApp.sidebar.opened" :is-active="pureApp.sidebar.opened"
@toggleClick="toggleSideBar" @toggleClick="toggleSideBar"
/> />
<Breadcrumb <LaySidebarBreadCrumb
v-if="layout !== 'mix' && device !== 'mobile'" v-if="layout !== 'mix' && device !== 'mobile'"
class="breadcrumb-container" class="breadcrumb-container"
/> />
<mixNav v-if="layout === 'mix'" /> <LayNavMix v-if="layout === 'mix'" />
<div v-if="layout === 'vertical'" class="vertical-header-right"> <div v-if="layout === 'vertical'" class="vertical-header-right">
<!-- 菜单搜索 --> <!-- 菜单搜索 -->
<Search id="header-search" /> <LaySearch id="header-search" />
<!-- 国际化 --> <!-- 国际化 -->
<el-dropdown id="header-translation" trigger="click"> <el-dropdown id="header-translation" trigger="click">
<globalization <GlobalizationIcon
class="navbar-bg-hover w-[40px] h-[48px] p-[11px] cursor-pointer outline-none" class="navbar-bg-hover w-[40px] h-[48px] p-[11px] cursor-pointer outline-none"
/> />
<template #dropdown> <template #dropdown>
@ -83,9 +84,9 @@ const { t, locale, translationCh, translationEn } = useTranslationLang();
</template> </template>
</el-dropdown> </el-dropdown>
<!-- 全屏 --> <!-- 全屏 -->
<FullScreen id="full-screen" /> <LaySidebarFullScreen id="full-screen" />
<!-- 消息通知 --> <!-- 消息通知 -->
<Notice id="header-notice" /> <LayNotice id="header-notice" />
<!-- 退出登录 --> <!-- 退出登录 -->
<el-dropdown trigger="click"> <el-dropdown trigger="click">
<span class="el-dropdown-link navbar-bg-hover select-none"> <span class="el-dropdown-link navbar-bg-hover select-none">

View File

@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { ListItem } from "./data"; import { ListItem } from "../data";
import { ref, PropType, nextTick } from "vue"; import { ref, PropType, nextTick } from "vue";
import { useNav } from "@/layout/hooks/useNav"; import { useNav } from "@/layout/hooks/useNav";
import { deviceDetection } from "@pureadmin/utils"; import { deviceDetection } from "@pureadmin/utils";
@ -52,9 +52,9 @@ function hoverDescription(event, description) {
class="notice-container border-b-[1px] border-solid border-[#f0f0f0] dark:border-[#303030]" class="notice-container border-b-[1px] border-solid border-[#f0f0f0] dark:border-[#303030]"
> >
<el-avatar <el-avatar
v-if="props.noticeItem.avatar" v-if="noticeItem.avatar"
:size="30" :size="30"
:src="props.noticeItem.avatar" :src="noticeItem.avatar"
class="notice-container-avatar" class="notice-container-avatar"
/> />
<div class="notice-container-text"> <div class="notice-container-text">
@ -63,7 +63,7 @@ function hoverDescription(event, description) {
popper-class="notice-title-popper" popper-class="notice-title-popper"
:effect="tooltipEffect" :effect="tooltipEffect"
:disabled="!titleTooltip" :disabled="!titleTooltip"
:content="props.noticeItem.title" :content="noticeItem.title"
placement="top-start" placement="top-start"
:enterable="!isMobile" :enterable="!isMobile"
> >
@ -72,16 +72,16 @@ function hoverDescription(event, description) {
class="notice-title-content" class="notice-title-content"
@mouseover="hoverTitle" @mouseover="hoverTitle"
> >
{{ props.noticeItem.title }} {{ noticeItem.title }}
</div> </div>
</el-tooltip> </el-tooltip>
<el-tag <el-tag
v-if="props.noticeItem?.extra" v-if="noticeItem?.extra"
:type="props.noticeItem?.status" :type="noticeItem?.status"
size="small" size="small"
class="notice-title-extra" class="notice-title-extra"
> >
{{ props.noticeItem?.extra }} {{ noticeItem?.extra }}
</el-tag> </el-tag>
</div> </div>
@ -89,19 +89,19 @@ function hoverDescription(event, description) {
popper-class="notice-title-popper" popper-class="notice-title-popper"
:effect="tooltipEffect" :effect="tooltipEffect"
:disabled="!descriptionTooltip" :disabled="!descriptionTooltip"
:content="props.noticeItem.description" :content="noticeItem.description"
placement="top-start" placement="top-start"
> >
<div <div
ref="descriptionRef" ref="descriptionRef"
class="notice-text-description" class="notice-text-description"
@mouseover="hoverDescription($event, props.noticeItem.description)" @mouseover="hoverDescription($event, noticeItem.description)"
> >
{{ props.noticeItem.description }} {{ noticeItem.description }}
</div> </div>
</el-tooltip> </el-tooltip>
<div class="notice-text-datetime text-[#00000073] dark:text-white"> <div class="notice-text-datetime text-[#00000073] dark:text-white">
{{ props.noticeItem.datetime }} {{ noticeItem.datetime }}
</div> </div>
</div> </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,24 +1,36 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref } from "vue";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import { ref, computed } from "vue";
import { noticesData } from "./data"; import { noticesData } from "./data";
import NoticeList from "./noticeList.vue"; import NoticeList from "./components/NoticeList.vue";
import Bell from "@iconify-icons/ep/bell"; import BellIcon from "@iconify-icons/ep/bell";
const { t } = useI18n(); const { t } = useI18n();
const noticesNum = ref(0); const noticesNum = ref(0);
const notices = ref(noticesData); 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)); notices.value.map(v => (noticesNum.value += v.list.length));
const getLabel = computed(
() => item =>
t(item.name) + (item.list.length > 0 ? `(${item.list.length})` : "")
);
</script> </script>
<template> <template>
<el-dropdown trigger="click" placement="bottom-end"> <el-dropdown trigger="click" placement="bottom-end">
<span class="dropdown-badge navbar-bg-hover select-none"> <span
<el-badge :value="noticesNum" :max="99"> :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"> <span class="header-notice-icon">
<IconifyIconOffline :icon="Bell" /> <IconifyIconOffline :icon="BellIcon" />
</span> </span>
</el-badge> </el-badge>
</span> </span>
@ -37,13 +49,10 @@ notices.value.map(v => (noticesNum.value += v.list.length));
/> />
<span v-else> <span v-else>
<template v-for="item in notices" :key="item.key"> <template v-for="item in notices" :key="item.key">
<el-tab-pane <el-tab-pane :label="getLabel(item)" :name="`${item.key}`">
:label="`${item.name}(${item.list.length})`"
:name="`${item.key}`"
>
<el-scrollbar max-height="330px"> <el-scrollbar max-height="330px">
<div class="noticeList-container"> <div class="noticeList-container">
<NoticeList :list="item.list" /> <NoticeList :list="item.list" :emptyText="item.emptyText" />
</div> </div>
</el-scrollbar> </el-scrollbar>
</el-tab-pane> </el-tab-pane>
@ -62,7 +71,6 @@ notices.value.map(v => (noticesNum.value += v.list.length));
justify-content: center; justify-content: center;
width: 40px; width: 40px;
height: 48px; height: 48px;
margin-right: 10px;
cursor: pointer; cursor: pointer;
.header-notice-icon { .header-notice-icon {

View File

@ -4,7 +4,7 @@ import { emitter } from "@/utils/mitt";
import { onClickOutside } from "@vueuse/core"; import { onClickOutside } from "@vueuse/core";
import { ref, computed, onMounted, onBeforeUnmount } from "vue"; import { ref, computed, onMounted, onBeforeUnmount } from "vue";
import { useDataThemeChange } from "@/layout/hooks/useDataThemeChange"; 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 target = ref(null);
const show = ref<Boolean>(false); const show = ref<Boolean>(false);
@ -68,7 +68,7 @@ onBeforeUnmount(() => {
class="dark:text-white" class="dark:text-white"
width="18px" width="18px"
height="18px" height="18px"
:icon="Close" :icon="CloseIcon"
@click="show = !show" @click="show = !show"
/> />
</span> </span>

View File

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

View File

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

View File

@ -13,7 +13,7 @@ import { ref, computed, shallowRef, watch } from "vue";
import { useDebounceFn, onKeyStroke } from "@vueuse/core"; import { useDebounceFn, onKeyStroke } from "@vueuse/core";
import { usePermissionStoreHook } from "@/store/modules/permission"; import { usePermissionStoreHook } from "@/store/modules/permission";
import { cloneDeep, isAllEmpty, storageLocal } from "@pureadmin/utils"; 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 { interface Props {
/** 弹窗显隐 */ /** 弹窗显隐 */
@ -298,7 +298,7 @@ onKeyStroke("ArrowDown", handleDown);
> >
<template #prefix> <template #prefix>
<IconifyIconOffline <IconifyIconOffline
:icon="Search" :icon="SearchIcon"
class="text-primary w-[24px] h-[24px]" class="text-primary w-[24px] h-[24px]"
/> />
</template> </template>

View File

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

View File

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

View File

@ -10,8 +10,8 @@ import {
onBeforeMount onBeforeMount
} from "vue"; } from "vue";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import panel from "../panel/index.vue";
import { emitter } from "@/utils/mitt"; import { emitter } from "@/utils/mitt";
import LayPanel from "../lay-panel/index.vue";
import { useNav } from "@/layout/hooks/useNav"; import { useNav } from "@/layout/hooks/useNav";
import { useAppStoreHook } from "@/store/modules/app"; import { useAppStoreHook } from "@/store/modules/app";
import { toggleTheme } from "@pureadmin/theme/dist/browser-utils"; import { toggleTheme } from "@pureadmin/theme/dist/browser-utils";
@ -23,9 +23,9 @@ import { useDark, useGlobal, debounce, isNumber } from "@pureadmin/utils";
import Check from "@iconify-icons/ep/check"; import Check from "@iconify-icons/ep/check";
import LeftArrow from "@iconify-icons/ri/arrow-left-s-line"; import LeftArrow from "@iconify-icons/ri/arrow-left-s-line";
import RightArrow from "@iconify-icons/ri/arrow-right-s-line"; import RightArrow from "@iconify-icons/ri/arrow-right-s-line";
import dayIcon from "@/assets/svg/day.svg?component"; import DayIcon from "@/assets/svg/day.svg?component";
import darkIcon from "@/assets/svg/dark.svg?component"; import DarkIcon from "@/assets/svg/dark.svg?component";
import systemIcon from "@/assets/svg/system.svg?component"; import SystemIcon from "@/assets/svg/system.svg?component";
const { t } = useI18n(); const { t } = useI18n();
const { device } = useNav(); const { device } = useNav();
@ -199,21 +199,21 @@ const themeOptions = computed<Array<OptionsType>>(() => {
return [ return [
{ {
label: t("panel.pureOverallStyleLight"), label: t("panel.pureOverallStyleLight"),
icon: dayIcon, icon: DayIcon,
theme: "light", theme: "light",
tip: t("panel.pureOverallStyleLightTip"), tip: t("panel.pureOverallStyleLightTip"),
iconAttrs: { fill: isDark.value ? "#fff" : "#000" } iconAttrs: { fill: isDark.value ? "#fff" : "#000" }
}, },
{ {
label: t("panel.pureOverallStyleDark"), label: t("panel.pureOverallStyleDark"),
icon: darkIcon, icon: DarkIcon,
theme: "dark", theme: "dark",
tip: t("panel.pureOverallStyleDarkTip"), tip: t("panel.pureOverallStyleDarkTip"),
iconAttrs: { fill: isDark.value ? "#fff" : "#000" } iconAttrs: { fill: isDark.value ? "#fff" : "#000" }
}, },
{ {
label: t("panel.pureOverallStyleSystem"), label: t("panel.pureOverallStyleSystem"),
icon: systemIcon, icon: SystemIcon,
theme: "system", theme: "system",
tip: t("panel.pureOverallStyleSystemTip"), tip: t("panel.pureOverallStyleSystemTip"),
iconAttrs: { fill: isDark.value ? "#fff" : "#000" } iconAttrs: { fill: isDark.value ? "#fff" : "#000" }
@ -313,7 +313,7 @@ onUnmounted(() => removeMatchMedia);
</script> </script>
<template> <template>
<panel> <LayPanel>
<div class="p-5"> <div class="p-5">
<p :class="pClass">{{ t("panel.pureOverallStyle") }}</p> <p :class="pClass">{{ t("panel.pureOverallStyle") }}</p>
<Segmented <Segmented
@ -517,7 +517,7 @@ onUnmounted(() => removeMatchMedia);
</li> </li>
</ul> </ul>
</div> </div>
</panel> </LayPanel>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -1,14 +1,15 @@
<script setup lang="ts"> <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 { isAllEmpty } from "@pureadmin/utils";
import { ref, nextTick, computed } from "vue"; import { ref, nextTick, computed } from "vue";
import { useNav } from "@/layout/hooks/useNav"; 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 { useTranslationLang } from "../../hooks/useTranslationLang";
import { usePermissionStoreHook } from "@/store/modules/permission"; import { usePermissionStoreHook } from "@/store/modules/permission";
import globalization from "@/assets/svg/globalization.svg?component"; import 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 AccountSettingsIcon from "@iconify-icons/ri/user-settings-line";
import LogoutCircleRLine from "@iconify-icons/ri/logout-circle-r-line"; import LogoutCircleRLine from "@iconify-icons/ri/logout-circle-r-line";
import Setting from "@iconify-icons/ri/settings-3-line"; import Setting from "@iconify-icons/ri/settings-3-line";
@ -21,11 +22,11 @@ const { t, route, locale, translationCh, translationEn } =
const { const {
title, title,
logout, logout,
backTopMenu,
onPanel, onPanel,
getLogo, getLogo,
username, username,
userAvatar, userAvatar,
backTopMenu,
avatarsStyle, avatarsStyle,
toAccountSettings, toAccountSettings,
getDropdownItemStyle, getDropdownItemStyle,
@ -58,7 +59,7 @@ nextTick(() => {
class="horizontal-header-menu" class="horizontal-header-menu"
:default-active="defaultActive" :default-active="defaultActive"
> >
<sidebar-item <LaySidebarItem
v-for="route in usePermissionStoreHook().wholeMenus" v-for="route in usePermissionStoreHook().wholeMenus"
:key="route.path" :key="route.path"
:item="route" :item="route"
@ -67,10 +68,10 @@ nextTick(() => {
</el-menu> </el-menu>
<div class="horizontal-header-right"> <div class="horizontal-header-right">
<!-- 菜单搜索 --> <!-- 菜单搜索 -->
<Search id="header-search" /> <LaySearch id="header-search" />
<!-- 国际化 --> <!-- 国际化 -->
<el-dropdown id="header-translation" trigger="click"> <el-dropdown id="header-translation" trigger="click">
<globalization <GlobalizationIcon
class="navbar-bg-hover w-[40px] h-[48px] p-[11px] cursor-pointer outline-none" class="navbar-bg-hover w-[40px] h-[48px] p-[11px] cursor-pointer outline-none"
/> />
<template #dropdown> <template #dropdown>
@ -99,9 +100,9 @@ nextTick(() => {
</template> </template>
</el-dropdown> </el-dropdown>
<!-- 全屏 --> <!-- 全屏 -->
<FullScreen id="full-screen" /> <LaySidebarFullScreen id="full-screen" />
<!-- 消息通知 --> <!-- 消息通知 -->
<Notice id="header-notice" /> <LayNotice id="header-notice" />
<!-- 退出登录 --> <!-- 退出登录 -->
<el-dropdown trigger="click"> <el-dropdown trigger="click">
<span class="el-dropdown-link navbar-bg-hover"> <span class="el-dropdown-link navbar-bg-hover">

View File

@ -1,17 +1,18 @@
<script setup lang="ts"> <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 { isAllEmpty } from "@pureadmin/utils";
import { useNav } from "@/layout/hooks/useNav"; import { useNav } from "@/layout/hooks/useNav";
import { transformI18n } from "@/plugins/i18n"; 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 { ref, toRaw, watch, onMounted, nextTick } from "vue";
import { useRenderIcon } from "@/components/ReIcon/src/hooks"; import { useRenderIcon } from "@/components/ReIcon/src/hooks";
import { getParentPaths, findRouteByPath } from "@/router/utils"; import { getParentPaths, findRouteByPath } from "@/router/utils";
import { useTranslationLang } from "../../hooks/useTranslationLang"; import { useTranslationLang } from "../../hooks/useTranslationLang";
import { usePermissionStoreHook } from "@/store/modules/permission"; import { usePermissionStoreHook } from "@/store/modules/permission";
import globalization from "@/assets/svg/globalization.svg?component"; import 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 AccountSettingsIcon from "@iconify-icons/ri/user-settings-line";
import LogoutCircleRLine from "@iconify-icons/ri/logout-circle-r-line"; import LogoutCircleRLine from "@iconify-icons/ri/logout-circle-r-line";
import Setting from "@iconify-icons/ri/settings-3-line"; import Setting from "@iconify-icons/ri/settings-3-line";
@ -93,17 +94,17 @@ watch(
<span class="select-none"> <span class="select-none">
{{ transformI18n(route.meta.title) }} {{ transformI18n(route.meta.title) }}
</span> </span>
<extraIcon :extraIcon="route.meta.extraIcon" /> <LaySidebarExtraIcon :extraIcon="route.meta.extraIcon" />
</div> </div>
</template> </template>
</el-menu-item> </el-menu-item>
</el-menu> </el-menu>
<div class="horizontal-header-right"> <div class="horizontal-header-right">
<!-- 菜单搜索 --> <!-- 菜单搜索 -->
<Search id="header-search" /> <LaySearch id="header-search" />
<!-- 国际化 --> <!-- 国际化 -->
<el-dropdown id="header-translation" trigger="click"> <el-dropdown id="header-translation" trigger="click">
<globalization <GlobalizationIcon
class="navbar-bg-hover w-[40px] h-[48px] p-[11px] cursor-pointer outline-none" class="navbar-bg-hover w-[40px] h-[48px] p-[11px] cursor-pointer outline-none"
/> />
<template #dropdown> <template #dropdown>
@ -132,9 +133,9 @@ watch(
</template> </template>
</el-dropdown> </el-dropdown>
<!-- 全屏 --> <!-- 全屏 -->
<FullScreen id="full-screen" /> <LaySidebarFullScreen id="full-screen" />
<!-- 消息通知 --> <!-- 消息通知 -->
<Notice id="header-notice" /> <LayNotice id="header-notice" />
<!-- 退出登录 --> <!-- 退出登录 -->
<el-dropdown trigger="click"> <el-dropdown trigger="click">
<span class="el-dropdown-link navbar-bg-hover select-none"> <span class="el-dropdown-link navbar-bg-hover select-none">

View File

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

View File

@ -36,7 +36,7 @@ const toggleClick = () => {
<template> <template>
<div <div
v-tippy="{ v-tippy="{
content: props.isActive content: isActive
? t('buttons.pureClickCollapse') ? t('buttons.pureClickCollapse')
: t('buttons.pureClickExpand'), : t('buttons.pureClickExpand'),
theme: tooltipEffect, theme: tooltipEffect,
@ -49,7 +49,7 @@ const toggleClick = () => {
<IconifyIconOffline <IconifyIconOffline
:icon="ArrowLeft" :icon="ArrowLeft"
:class="[iconClass, themeColor === 'light' ? '' : 'text-primary']" :class="[iconClass, themeColor === 'light' ? '' : 'text-primary']"
:style="{ transform: props.isActive ? 'none' : 'rotateY(180deg)' }" :style="{ transform: isActive ? 'none' : 'rotateY(180deg)' }"
/> />
</div> </div>
</template> </template>

View File

@ -11,9 +11,9 @@ const props = defineProps({
</script> </script>
<template> <template>
<div v-if="props.extraIcon" class="flex justify-center items-center"> <div v-if="extraIcon" class="flex justify-center items-center">
<component <component
:is="useRenderIcon(toRaw(props.extraIcon))" :is="useRenderIcon(toRaw(extraIcon))"
class="w-[30px] h-[30px]" class="w-[30px] h-[30px]"
/> />
</div> </div>

View File

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

View File

@ -46,7 +46,7 @@ const toggleClick = () => {
<div class="left-collapse"> <div class="left-collapse">
<IconifyIconOffline <IconifyIconOffline
v-tippy="{ v-tippy="{
content: props.isActive content: isActive
? t('buttons.pureClickCollapse') ? t('buttons.pureClickCollapse')
: t('buttons.pureClickExpand'), : t('buttons.pureClickExpand'),
theme: tooltipEffect, theme: tooltipEffect,
@ -55,7 +55,7 @@ const toggleClick = () => {
}" }"
:icon="MenuFold" :icon="MenuFold"
:class="[iconClass, themeColor === 'light' ? '' : 'text-primary']" :class="[iconClass, themeColor === 'light' ? '' : 'text-primary']"
:style="{ transform: props.isActive ? 'none' : 'rotateY(180deg)' }" :style="{ transform: isActive ? 'none' : 'rotateY(180deg)' }"
@click="toggleClick" @click="toggleClick"
/> />
</div> </div>

View File

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

View File

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

View File

@ -26,14 +26,12 @@ const toggleClick = () => {
<div <div
class="px-3 mr-1 navbar-bg-hover" class="px-3 mr-1 navbar-bg-hover"
:title=" :title="
props.isActive isActive ? t('buttons.pureClickCollapse') : t('buttons.pureClickExpand')
? t('buttons.pureClickCollapse')
: t('buttons.pureClickExpand')
" "
@click="toggleClick" @click="toggleClick"
> >
<IconifyIconOffline <IconifyIconOffline
:icon="props.isActive ? MenuFold : MenuUnfold" :icon="isActive ? MenuFold : MenuUnfold"
class="inline-block align-middle hover:text-primary dark:hover:!text-white" class="inline-block align-middle hover:text-primary dark:hover:!text-white"
/> />
</div> </div>

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,26 +0,0 @@
<script setup lang="ts">
import { PropType } from "vue";
import { ListItem } from "./data";
import { useI18n } from "vue-i18n";
import NoticeItem from "./noticeItem.vue";
const props = defineProps({
list: {
type: Array as PropType<Array<ListItem>>,
default: () => []
}
});
const { t } = useI18n();
</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="t('status.pureNoMessage')" />
</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"; import { ref, unref, watch, onMounted, nextTick } from "vue";
defineOptions({ defineOptions({
name: "FrameView" name: "LayFrame"
}); });
const props = defineProps<{ const props = defineProps<{

View File

@ -24,13 +24,13 @@ import {
useResizeObserver useResizeObserver
} from "@pureadmin/utils"; } from "@pureadmin/utils";
import navbar from "./components/navbar.vue"; import LayTag from "./components/lay-tag/index.vue";
import tag from "./components/tag/index.vue"; import LayNavbar from "./components/lay-navbar/index.vue";
import appMain from "./components/appMain.vue"; import LayContent from "./components/lay-content/index.vue";
import setting from "./components/setting/index.vue"; import LaySetting from "./components/lay-setting/index.vue";
import Vertical from "./components/sidebar/vertical.vue"; import NavVertical from "./components/lay-sidebar/NavVertical.vue";
import Horizontal from "./components/sidebar/horizontal.vue"; import NavHorizontal from "./components/lay-sidebar/NavHorizontal.vue";
import backTop from "@/assets/svg/back_top.svg?component"; import BackTopIcon from "@/assets/svg/back_top.svg?component";
const { t } = useI18n(); const { t } = useI18n();
const appWrapperRef = ref(); const appWrapperRef = ref();
@ -126,7 +126,8 @@ onBeforeMount(() => {
useDataThemeChange().dataThemeChange($storage.layout?.overallStyle); useDataThemeChange().dataThemeChange($storage.layout?.overallStyle);
}); });
const layoutHeader = defineComponent({ const LayHeader = defineComponent({
name: "LayHeader",
render() { render() {
return h( return h(
"div", "div",
@ -144,12 +145,12 @@ const layoutHeader = defineComponent({
default: () => [ default: () => [
!pureSetting.hiddenSideBar && !pureSetting.hiddenSideBar &&
(layout.value.includes("vertical") || layout.value.includes("mix")) (layout.value.includes("vertical") || layout.value.includes("mix"))
? h(navbar) ? h(LayNavbar)
: null, : null,
!pureSetting.hiddenSideBar && layout.value.includes("horizontal") !pureSetting.hiddenSideBar && layout.value.includes("horizontal")
? h(Horizontal) ? h(NavHorizontal)
: null, : null,
h(tag) h(LayTag)
] ]
} }
); );
@ -168,7 +169,7 @@ const layoutHeader = defineComponent({
class="app-mask" class="app-mask"
@click="useAppStoreHook().toggleSideBar()" @click="useAppStoreHook().toggleSideBar()"
/> />
<Vertical <NavVertical
v-show=" v-show="
!pureSetting.hiddenSideBar && !pureSetting.hiddenSideBar &&
(layout.includes('vertical') || layout.includes('mix')) (layout.includes('vertical') || layout.includes('mix'))
@ -181,24 +182,24 @@ const layoutHeader = defineComponent({
]" ]"
> >
<div v-if="set.fixedHeader"> <div v-if="set.fixedHeader">
<layout-header /> <LayHeader />
<!-- 主体内容 --> <!-- 主体内容 -->
<app-main :fixed-header="set.fixedHeader" /> <LayContent :fixed-header="set.fixedHeader" />
</div> </div>
<el-scrollbar v-else> <el-scrollbar v-else>
<el-backtop <el-backtop
:title="t('buttons.pureBackTop')" :title="t('buttons.pureBackTop')"
target=".main-container .el-scrollbar__wrap" target=".main-container .el-scrollbar__wrap"
> >
<backTop /> <BackTopIcon />
</el-backtop> </el-backtop>
<layout-header /> <LayHeader />
<!-- 主体内容 --> <!-- 主体内容 -->
<app-main :fixed-header="set.fixedHeader" /> <LayContent :fixed-header="set.fixedHeader" />
</el-scrollbar> </el-scrollbar>
</div> </div>
<!-- 系统设置 --> <!-- 系统设置 -->
<setting /> <LaySetting />
</div> </div>
</template> </template>

View File

@ -20,11 +20,12 @@ import {
// 可选组件 // 可选组件
Icon, Icon,
Column, Column,
Grid,
Pager,
Select,
// Colgroup, // Colgroup,
// Grid,
// Tooltip, // Tooltip,
// Toolbar, // Toolbar,
// Pager,
// Form, // Form,
// FormItem, // FormItem,
// FormGather, // FormGather,
@ -35,7 +36,6 @@ import {
// RadioButton, // RadioButton,
// Switch, // Switch,
// Input, // Input,
// Select,
// Optgroup, // Optgroup,
// Option, // Option,
// Textarea, // Textarea,
@ -76,11 +76,12 @@ export function useVxeTable(app: App) {
// 可选组件 // 可选组件
.use(Icon) .use(Icon)
.use(Column) .use(Column)
.use(Grid)
.use(Pager)
.use(Select)
// .use(Colgroup) // .use(Colgroup)
// .use(Grid)
// .use(Tooltip) // .use(Tooltip)
// .use(Toolbar) // .use(Toolbar)
// .use(Pager)
// .use(Form) // .use(Form)
// .use(FormItem) // .use(FormItem)
// .use(FormGather) // .use(FormGather)
@ -91,7 +92,6 @@ export function useVxeTable(app: App) {
// .use(RadioButton) // .use(RadioButton)
// .use(Switch) // .use(Switch)
// .use(Input) // .use(Input)
// .use(Select)
// .use(Optgroup) // .use(Optgroup)
// .use(Option) // .use(Option)
// .use(Textarea) // .use(Textarea)

View File

@ -1,6 +1,6 @@
import { $t } from "@/plugins/i18n"; import { $t } from "@/plugins/i18n";
import { board } from "@/router/enums"; import { board } from "@/router/enums";
const IFrame = () => import("@/layout/frameView.vue"); const IFrame = () => import("@/layout/frame.vue");
export default { export default {
path: "/board", path: "/board",

View File

@ -1,6 +1,6 @@
import { $t } from "@/plugins/i18n"; import { $t } from "@/plugins/i18n";
import { formdesign } from "@/router/enums"; import { formdesign } from "@/router/enums";
const IFrame = () => import("@/layout/frameView.vue"); const IFrame = () => import("@/layout/frame.vue");
export default { export default {
path: "/form-design", path: "/form-design",

View File

@ -1,6 +1,6 @@
import { $t } from "@/plugins/i18n"; import { $t } from "@/plugins/i18n";
import { mind } from "@/router/enums"; import { mind } from "@/router/enums";
const IFrame = () => import("@/layout/frameView.vue"); const IFrame = () => import("@/layout/frame.vue");
export default { export default {
path: "/mind-map", path: "/mind-map",

View File

@ -1,5 +1,5 @@
import { ppt } from "@/router/enums"; import { ppt } from "@/router/enums";
const IFrame = () => import("@/layout/frameView.vue"); const IFrame = () => import("@/layout/frame.vue");
export default { export default {
path: "/ppt", path: "/ppt",

View File

@ -22,7 +22,7 @@ import { userKey, type DataInfo } from "@/utils/auth";
import { type menuType, routerArrays } from "@/layout/types"; import { type menuType, routerArrays } from "@/layout/types";
import { useMultiTagsStoreHook } from "@/store/modules/multiTags"; import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
import { usePermissionStoreHook } from "@/store/modules/permission"; import { usePermissionStoreHook } from "@/store/modules/permission";
const IFrame = () => import("@/layout/frameView.vue"); const IFrame = () => import("@/layout/frame.vue");
// https://cn.vitejs.dev/guide/features.html#glob-import // https://cn.vitejs.dev/guide/features.html#glob-import
const modulesRoutes = import.meta.glob("/src/views/**/*.{vue,tsx}"); const modulesRoutes = import.meta.glob("/src/views/**/*.{vue,tsx}");

View File

@ -82,10 +82,7 @@ export function useColumns() {
className: "pure-version", className: "pure-version",
cellRenderer: () => { cellRenderer: () => {
return ( return (
<a <a href="https://pure-admin.github.io/pure-admin-doc" target="_blank">
href="https://yiming_chang.gitee.io/pure-admin-doc"
target="_blank"
>
<span style="color: var(--el-color-primary)"></span> <span style="color: var(--el-color-primary)"></span>
</a> </a>
); );
@ -97,10 +94,7 @@ export function useColumns() {
className: "pure-version", className: "pure-version",
cellRenderer: () => { cellRenderer: () => {
return ( return (
<a <a href="https://pure-admin.github.io/vue-pure-admin" target="_blank">
href="https://yiming_chang.gitee.io/vue-pure-admin"
target="_blank"
>
<span style="color: var(--el-color-primary)"></span> <span style="color: var(--el-color-primary)"></span>
</a> </a>
); );

View File

@ -8,8 +8,8 @@ import Preferences from "./components/Preferences.vue";
import SecurityLog from "./components/SecurityLog.vue"; import SecurityLog from "./components/SecurityLog.vue";
import { useGlobal, deviceDetection } from "@pureadmin/utils"; import { useGlobal, deviceDetection } from "@pureadmin/utils";
import AccountManagement from "./components/AccountManagement.vue"; import AccountManagement from "./components/AccountManagement.vue";
import TopCollapse from "@/layout/components/sidebar/topCollapse.vue";
import { useDataThemeChange } from "@/layout/hooks/useDataThemeChange"; import { useDataThemeChange } from "@/layout/hooks/useDataThemeChange";
import LaySidebarTopCollapse from "@/layout/components/lay-sidebar/components/SidebarTopCollapse.vue";
import leftLine from "@iconify-icons/ri/arrow-left-s-line"; import leftLine from "@iconify-icons/ri/arrow-left-s-line";
import ProfileIcon from "@iconify-icons/ri/user-3-line"; import ProfileIcon from "@iconify-icons/ri/user-3-line";
@ -115,7 +115,7 @@ getMine().then(res => {
</el-menu> </el-menu>
</el-aside> </el-aside>
<el-main> <el-main>
<TopCollapse <LaySidebarTopCollapse
v-if="deviceDetection()" v-if="deviceDetection()"
class="px-0" class="px-0"
:is-active="isOpen" :is-active="isOpen"

View File

@ -23,7 +23,7 @@ const { isDisabled, text } = useVerifyCode();
const onLogin = async (formEl: FormInstance | undefined) => { const onLogin = async (formEl: FormInstance | undefined) => {
loading.value = true; loading.value = true;
if (!formEl) return; if (!formEl) return;
await formEl.validate((valid, fields) => { await formEl.validate(valid => {
if (valid) { if (valid) {
// //
setTimeout(() => { setTimeout(() => {
@ -34,7 +34,6 @@ const onLogin = async (formEl: FormInstance | undefined) => {
}, 2000); }, 2000);
} else { } else {
loading.value = false; loading.value = false;
return fields;
} }
}); });
}; };

View File

@ -45,7 +45,7 @@ const repeatPasswordRule = [
const onUpdate = async (formEl: FormInstance | undefined) => { const onUpdate = async (formEl: FormInstance | undefined) => {
loading.value = true; loading.value = true;
if (!formEl) return; if (!formEl) return;
await formEl.validate((valid, fields) => { await formEl.validate(valid => {
if (valid) { if (valid) {
if (checked.value) { if (checked.value) {
// //
@ -63,7 +63,6 @@ const onUpdate = async (formEl: FormInstance | undefined) => {
} }
} else { } else {
loading.value = false; loading.value = false;
return fields;
} }
}); });
}; };

View File

@ -42,7 +42,7 @@ const repeatPasswordRule = [
const onUpdate = async (formEl: FormInstance | undefined) => { const onUpdate = async (formEl: FormInstance | undefined) => {
loading.value = true; loading.value = true;
if (!formEl) return; if (!formEl) return;
await formEl.validate((valid, fields) => { await formEl.validate(valid => {
if (valid) { if (valid) {
// //
setTimeout(() => { setTimeout(() => {
@ -53,7 +53,6 @@ const onUpdate = async (formEl: FormInstance | undefined) => {
}, 2000); }, 2000);
} else { } else {
loading.value = false; loading.value = false;
return fields;
} }
}); });
}; };

View File

@ -64,7 +64,7 @@ const ruleForm = reactive({
const onLogin = async (formEl: FormInstance | undefined) => { const onLogin = async (formEl: FormInstance | undefined) => {
if (!formEl) return; if (!formEl) return;
await formEl.validate((valid, fields) => { await formEl.validate(valid => {
if (valid) { if (valid) {
loading.value = true; loading.value = true;
useUserStoreHook() useUserStoreHook()
@ -86,8 +86,6 @@ const onLogin = async (formEl: FormInstance | undefined) => {
} }
}) })
.finally(() => (loading.value = false)); .finally(() => (loading.value = false));
} else {
return fields;
} }
}); });
}; };

View File

@ -82,12 +82,7 @@ const dataList = ref([
<template> <template>
<div> <div>
<el-scrollbar> <el-scrollbar>
<PureDescriptions <PureDescriptions border :data="data" :columns="columns" :column="5" />
border
:data="props.data"
:columns="columns"
:column="5"
/>
</el-scrollbar> </el-scrollbar>
<el-tabs :modelValue="'responseBody'" type="border-card" class="mt-4"> <el-tabs :modelValue="'responseBody'" type="border-card" class="mt-4">
<el-tab-pane <el-tab-pane

View File

@ -8,7 +8,7 @@
| `name` | 路由名称(必须唯一并且和当前路由`component`字段对应的页面里用`defineOptions`包起来的`name`保持一致) | | `name` | 路由名称(必须唯一并且和当前路由`component`字段对应的页面里用`defineOptions`包起来的`name`保持一致) |
| `path` | 路由路径 | | `path` | 路由路径 |
| `component` | 组件路径(传`component`组件路径,那么`path`可以随便写,如果不传,`component`组件路径会跟`path`保持一致) | | `component` | 组件路径(传`component`组件路径,那么`path`可以随便写,如果不传,`component`组件路径会跟`path`保持一致) |
| `rank` | 菜单排序(平台规定只有`home`路由的`rank`才能为`0`,所以后端在返回`rank`的时候需要从非`0`开始 [点击查看更多](https://yiming_chang.gitee.io/pure-admin-doc/pages/routerMenu/#%E8%8F%9C%E5%8D%95%E6%8E%92%E5%BA%8F-rank) | | `rank` | 菜单排序(平台规定只有`home`路由的`rank`才能为`0`,所以后端在返回`rank`的时候需要从非`0`开始 [点击查看更多](https://pure-admin.github.io/pure-admin-doc/pages/routerMenu/#%E8%8F%9C%E5%8D%95%E6%8E%92%E5%BA%8F-rank) |
| `redirect` | 路由重定向 | | `redirect` | 路由重定向 |
| `icon` | 菜单图标 | | `icon` | 菜单图标 |
| `extraIcon` | 右侧图标 | | `extraIcon` | 右侧图标 |
@ -22,5 +22,5 @@
| `hiddenTag` | 标签页(当前菜单名称或自定义信息禁止添加到标签页) | | `hiddenTag` | 标签页(当前菜单名称或自定义信息禁止添加到标签页) |
| `fixedTag` | 固定标签页(当前菜单名称是否固定显示在标签页且不可关闭) | | `fixedTag` | 固定标签页(当前菜单名称是否固定显示在标签页且不可关闭) |
| `showLink` | 菜单(是否显示该菜单) | | `showLink` | 菜单(是否显示该菜单) |
| `showParent` | 父级菜单(是否显示父级菜单 [点击查看更多](https://yiming_chang.gitee.io/pure-admin-doc/pages/routerMenu/#%E7%AC%AC%E4%B8%80%E7%A7%8D-%E8%AF%A5%E6%A8%A1%E5%BC%8F%E9%92%88%E5%AF%B9%E7%88%B6%E7%BA%A7%E8%8F%9C%E5%8D%95%E4%B8%8B%E5%8F%AA%E6%9C%89%E4%B8%80%E4%B8%AA%E5%AD%90%E8%8F%9C%E5%8D%95%E7%9A%84%E6%83%85%E5%86%B5-%E5%9C%A8%E5%AD%90%E8%8F%9C%E5%8D%95%E7%9A%84-meta-%E5%B1%9E%E6%80%A7%E4%B8%AD%E5%8A%A0%E4%B8%8A-showparent-true-%E5%8D%B3%E5%8F%AF) | | `showParent` | 父级菜单(是否显示父级菜单 [点击查看更多](https://pure-admin.github.io/pure-admin-doc/pages/routerMenu/#%E7%AC%AC%E4%B8%80%E7%A7%8D-%E8%AF%A5%E6%A8%A1%E5%BC%8F%E9%92%88%E5%AF%B9%E7%88%B6%E7%BA%A7%E8%8F%9C%E5%8D%95%E4%B8%8B%E5%8F%AA%E6%9C%89%E4%B8%80%E4%B8%AA%E5%AD%90%E8%8F%9C%E5%8D%95%E7%9A%84%E6%83%85%E5%86%B5-%E5%9C%A8%E5%AD%90%E8%8F%9C%E5%8D%95%E7%9A%84-meta-%E5%B1%9E%E6%80%A7%E4%B8%AD%E5%8A%A0%E4%B8%8A-showparent-true-%E5%8D%B3%E5%8F%AF) |

View File

@ -17,7 +17,7 @@ interface Tree {
children?: Tree[]; children?: Tree[];
} }
const props = defineProps({ defineProps({
treeLoading: Boolean, treeLoading: Boolean,
treeData: Array treeData: Array
}); });
@ -95,7 +95,7 @@ defineExpose({ onTreeReset });
<template> <template>
<div <div
v-loading="props.treeLoading" v-loading="treeLoading"
class="h-full bg-bg_color overflow-auto" class="h-full bg-bg_color overflow-auto"
:style="{ minHeight: `calc(100vh - 141px)` }" :style="{ minHeight: `calc(100vh - 141px)` }"
> >
@ -153,7 +153,7 @@ defineExpose({ onTreeReset });
<el-divider /> <el-divider />
<el-tree <el-tree
ref="treeRef" ref="treeRef"
:data="props.treeData" :data="treeData"
node-key="id" node-key="id"
size="small" size="small"
:props="defaultProps" :props="defaultProps"

View File

@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { tableDataMore } from "./data"; import { tableDataMore } from "./data";
const props = withDefaults( withDefaults(
defineProps<{ defineProps<{
height?: string; height?: string;
}>(), }>(),
@ -62,12 +62,12 @@ function handleClick(row) {
<template> <template>
<pure-table <pure-table
:data=" :data="
props.height height
? tableDataMore.concat(tableDataMore).concat(tableDataMore) ? tableDataMore.concat(tableDataMore).concat(tableDataMore)
: tableDataMore : tableDataMore
" "
:columns="columns" :columns="columns"
:height="props.height" :height="height"
> >
<template #operation="{ row }"> <template #operation="{ row }">
<el-button link type="primary" size="small" @click="handleClick(row)"> <el-button link type="primary" size="small" @click="handleClick(row)">

View File

@ -30,7 +30,7 @@ const columns: TableColumnList = [
<template #video> <template #video>
<video width="180" height="180" controls> <video width="180" height="180" controls>
<source <source
src="https://yiming_chang.gitee.io/pure-admin-doc/video/url.mov" src="https://pure-admin.github.io/pure-admin-doc/video/url.mov"
type="video/mp4" type="video/mp4"
/> />
</video> </video>

View File

@ -1,5 +1,6 @@
import List from "./list.vue"; import List from "./list.vue";
import TreeList from "./treeList.vue"; import TreeList from "./treeList.vue";
import PageList from "./pageList.vue";
const rendContent = (val: string) => const rendContent = (val: string) =>
`代码位置src/views/table/virtual/${val}.vue`; `代码位置src/views/table/virtual/${val}.vue`;
@ -8,13 +9,19 @@ export const list = [
{ {
key: "list", key: "list",
content: rendContent("list"), content: rendContent("list"),
title: "虚拟表", title: "虚拟",
component: List component: List
}, },
{ {
key: "treeList", key: "treeList",
content: rendContent("treeList"), content: rendContent("treeList"),
title: "虚拟树", title: "虚拟树形表格",
component: TreeList component: TreeList
},
{
key: "pageList",
content: rendContent("pageList"),
title: "分页表格",
component: PageList
} }
]; ];

View File

@ -1,9 +1,20 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from "vue"; import { ref } from "vue";
import { VxeTableBar } from "@/components/ReVxeTableBar";
const vxeTableRef = ref();
const loading = ref(true);
const tableData = ref([]); const tableData = ref([]);
setTimeout(() => { const columns = [
{ type: "seq", field: "seq", title: "序号", width: 100 },
{ field: "name", title: "名称", sortable: true },
{ field: "role", title: "角色" },
{ field: "sex", title: "性别" }
];
async function onSearch() {
loading.value = true;
// //
const mockList = []; const mockList = [];
for (let index = 0; index < 500; index++) { for (let index = 0; index < 500; index++) {
@ -15,21 +26,33 @@ setTimeout(() => {
}); });
} }
tableData.value = mockList; tableData.value = mockList;
}, 100); setTimeout(() => {
loading.value = false;
}, 500);
}
onSearch();
</script> </script>
<template> <template>
<vxe-table <VxeTableBar
border :vxeTableRef="vxeTableRef"
:columns="columns"
title="虚拟表格"
@refresh="onSearch"
>
<template v-slot="{ size, dynamicColumns }">
<vxe-grid
ref="vxeTableRef"
v-loading="loading"
show-overflow show-overflow
height="500" height="500"
:size="size"
:row-config="{ isHover: true }" :row-config="{ isHover: true }"
:data="tableData"
:scroll-y="{ enabled: true }" :scroll-y="{ enabled: true }"
> :columns="dynamicColumns"
<vxe-column type="seq" title="序号" width="100" /> :data="tableData"
<vxe-column field="name" title="名称" sortable /> />
<vxe-column field="role" title="角色" /> </template>
<vxe-column field="sex" title="性别" /> </VxeTableBar>
</vxe-table>
</template> </template>

View File

@ -0,0 +1,86 @@
<script lang="ts" setup>
import { ref, reactive, computed } from "vue";
import { VxeTableBar } from "@/components/ReVxeTableBar";
const vxeTableRef = ref();
const loading = ref(true);
const tableData = ref([]);
const getTableHeight = computed(() => {
return (size: string) => {
switch (size) {
case "medium":
return 531;
case "small":
return 482;
case "mini":
return 433;
}
};
});
const pagerConfig = reactive({
total: 0,
currentPage: 1,
pageSize: 10,
pageSizes: [5, 10, 15, 20]
});
const columns = [
{ type: "seq", field: "seq", title: "序号", width: 100 },
{ field: "name", title: "名称", sortable: true },
{ field: "role", title: "角色" },
{ field: "sex", title: "性别" }
];
async function onSearch() {
loading.value = true;
//
const mockList = [];
for (let index = 0; index < 10; index++) {
mockList.push({
id: index,
name: "Test" + index,
role: "Developer",
sex: "男"
});
}
pagerConfig.total = 20;
tableData.value = mockList;
setTimeout(() => {
loading.value = false;
}, 500);
}
const handlePageChange = ({ currentPage, pageSize }) => {
pagerConfig.currentPage = currentPage;
pagerConfig.pageSize = pageSize;
onSearch();
};
onSearch();
</script>
<template>
<VxeTableBar
:vxeTableRef="vxeTableRef"
:columns="columns"
title="分页表格"
@refresh="onSearch"
>
<template v-slot="{ size, dynamicColumns }">
<vxe-grid
ref="vxeTableRef"
v-loading="loading"
show-overflow
:height="getTableHeight(size)"
:size="size"
:column-config="{ resizable: true }"
:columns="dynamicColumns"
:pagerConfig="pagerConfig"
:data="tableData"
@page-change="handlePageChange"
/>
</template>
</VxeTableBar>
</template>

View File

@ -1,56 +1,51 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from "vue"; import { ref } from "vue";
import treeList from "./tree.json"; import treeList from "./tree.json";
import { VxeTableBar } from "@/components/ReVxeTableBar";
const tableRef = ref(); const vxeTableRef = ref();
const loading = ref(false); const loading = ref(true);
const tableData = ref([]); const tableData = ref([]);
const loadList = () => { const columns = [
{ type: "seq", field: "seq", title: "序号", width: 200, treeNode: true },
{ field: "id", title: "Id" },
{ field: "name", title: "地点" }
];
async function onSearch() {
loading.value = true; loading.value = true;
setTimeout(() => {
tableData.value = treeList; tableData.value = treeList;
setTimeout(() => {
loading.value = false; loading.value = false;
}, 200); }, 500);
}; }
const expandAllEvent = () => { onSearch();
const $table = tableRef.value;
if ($table) {
$table.setAllTreeExpand(true);
}
};
const claseExpandEvent = () => {
const $table = tableRef.value;
if ($table) {
$table.clearTreeExpand();
}
};
loadList();
</script> </script>
<template> <template>
<div> <VxeTableBar
<div class="mb-4"> tree
<el-button @click="expandAllEvent">展开所有</el-button> title="虚拟树形表格"
<el-button @click="claseExpandEvent">收起所有</el-button> :isExpandAll="false"
</div> :vxeTableRef="vxeTableRef"
:columns="columns"
<vxe-table @refresh="onSearch"
ref="tableRef" >
<template v-slot="{ size, dynamicColumns }">
<vxe-grid
ref="vxeTableRef"
v-loading="loading"
show-overflow show-overflow
height="500" height="500"
:loading="loading" :size="size"
:tree-config="{ transform: true }" :tree-config="{ transform: true }"
:scroll-y="{ enabled: true, gt: 20 }" :scroll-y="{ enabled: true, gt: 20 }"
:columns="dynamicColumns"
:data="tableData" :data="tableData"
> />
<vxe-column type="seq" title="序号" width="200" tree-node /> </template>
<vxe-column field="id" title="Id" /> </VxeTableBar>
<vxe-column field="name" title="地点" />
</vxe-table>
</div>
</template> </template>

6
types/global.d.ts vendored
View File

@ -61,7 +61,7 @@ declare global {
/** /**
* *
* @see {@link https://yiming_chang.gitee.io/pure-admin-doc/pages/config/#%E5%85%B7%E4%BD%93%E9%85%8D%E7%BD%AE} * @see {@link https://pure-admin.github.io/pure-admin-doc/pages/config/#%E5%85%B7%E4%BD%93%E9%85%8D%E7%BD%AE}
*/ */
interface ViteEnv { interface ViteEnv {
VITE_PORT: number; VITE_PORT: number;
@ -79,7 +79,7 @@ declare global {
/** /**
* `public/platform-config.json` * `public/platform-config.json`
* @see {@link https://yiming_chang.gitee.io/pure-admin-doc/pages/config/#platform-config-json} * @see {@link https://pure-admin.github.io/pure-admin-doc/pages/config/#platform-config-json}
*/ */
interface PlatformConfigs { interface PlatformConfigs {
Version?: string; Version?: string;
@ -120,7 +120,7 @@ declare global {
/** /**
* `PlatformConfigs` * `PlatformConfigs`
* @see {@link https://yiming_chang.gitee.io/pure-admin-doc/pages/config/#platform-config-json} * @see {@link https://pure-admin.github.io/pure-admin-doc/pages/config/#platform-config-json}
*/ */
interface StorageConfigs { interface StorageConfigs {
version?: string; version?: string;