mirror of
				https://github.com/pure-admin/vue-pure-admin.git
				synced 2025-11-03 13:44:47 +08:00 
			
		
		
		
	Merge branch 'main' into gitee
This commit is contained in:
		
						commit
						bbf64e1ff0
					
				
							
								
								
									
										1
									
								
								.prettierignore
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.prettierignore
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
src/views/system/menu/README.md
 | 
			
		||||
@ -1,3 +1,96 @@
 | 
			
		||||
# 5.0.0 (2024-02-26)
 | 
			
		||||
 | 
			
		||||
Totally `ESM` version
 | 
			
		||||
 | 
			
		||||
### ✔️refactor
 | 
			
		||||
 | 
			
		||||
- Upgrade `vite` to `v5` version, specify `node` version `>18.18.0`, `pnpm` version `>=8.6.10`
 | 
			
		||||
- Use [vite-plugin-fake-server](https://www.npmjs.com/package/vite-plugin-fake-server) to replace [vite-plugin-mock](https://www.npmjs.com/package/vite-plugin-mock), use [@faker-js/faker](https://www.npmjs.com/package/@faker-js/faker) to replace [mockjs](https://www.npmjs.com/package/mockjs)
 | 
			
		||||
- Rename `tailwind.config.js` to `tailwind.config.ts` and update its file to `esm` syntax
 | 
			
		||||
- Updated `.prettierrc.js` file to `esm` syntax
 | 
			
		||||
- Updated `postcss.config.js` file to `esm` syntax
 | 
			
		||||
- Updated `commitlint.config.js` file to `esm` syntax
 | 
			
		||||
- Use `eslint.config.js` to replace `.eslintrc.js` and follow `esm` syntax
 | 
			
		||||
- Upgrade `stylelint` to `16` version and follow `esm` syntax
 | 
			
		||||
- All `search` search icons are uniformly replaced with `@iconify-icons/ri/search-line` which is more commonly used and put into the global offline icon
 | 
			
		||||
- Removed the filter effect of `iframe` in dark mode
 | 
			
		||||
- Bring a more beautiful and refined homepage
 | 
			
		||||
- Cleaner and neater pop-up panel on the right side of project configuration
 | 
			
		||||
- Restructure the About page to make it more compact and key information more prominent
 | 
			
		||||
 | 
			
		||||
### 🎫Feat
 | 
			
		||||
 | 
			
		||||
- Add system management-menu management
 | 
			
		||||
- Improve system management-user management
 | 
			
		||||
- Embedded `iframe` pages support setting `keepAlive` to maintain page status
 | 
			
		||||
- Optimized navigation, the pop-up menu is adaptive and scrollable beyond the content area
 | 
			
		||||
- Added file upload example
 | 
			
		||||
- Added overall style adaptive operating system light, dark, and automatic theme functions
 | 
			
		||||
- Add footer
 | 
			
		||||
- Supports multi-tab pages to open systems that have already been logged in without logging in again and adds an internal login-free function (users can choose the number of days without login)
 | 
			
		||||
- Terminal command line that brings a high-level feel
 | 
			
		||||
- Add audio visualization function example
 | 
			
		||||
- Added video frame interception - `WebAssembly` version, supports `MP4`, `MOV`, `AVI`, `WebM`, `MKV` and other mainstream formats
 | 
			
		||||
- Added methods to block keyboard `F12`, browser default right-click menu, page element selection, and picture default draggability
 | 
			
		||||
- The secondary package `localforage` supports setting expiration time and provides complete type prompts
 | 
			
		||||
- Add `AnimateCss` selector component `ReAnimateSelector`
 | 
			
		||||
- Added `ReText` component, supports automatic omission and display of `Tooltip` function, supports multi-line omission, high reusability
 | 
			
		||||
- Add an art drawing board function, which can be used to draw some design idea architecture diagrams, for example
 | 
			
		||||
- New component - optional button example
 | 
			
		||||
- Add common button examples
 | 
			
		||||
- Added color picker component example
 | 
			
		||||
- Add date picker component example
 | 
			
		||||
- Added datetime picker example
 | 
			
		||||
- Added time selector example
 | 
			
		||||
- Added statistics component example
 | 
			
		||||
- Add label component example
 | 
			
		||||
- Added accordion panel component example
 | 
			
		||||
- Add progress bar component example
 | 
			
		||||
- Upgrade `Swiper 11`
 | 
			
		||||
- Add [vite-plugin-router-warn](https://www.npmjs.com/package/vite-plugin-router-warn) plug-in to eliminate unnecessary `vue-router` dynamic routing warning`No match found for location with path`
 | 
			
		||||
 | 
			
		||||
### 🐞 Bug fixes
 | 
			
		||||
 | 
			
		||||
- Fixed the problem that in `query` routing parameter passing mode, two `router` jumps will be triggered when clicking the tab page to switch operations.
 | 
			
		||||
- Fixed an issue in card tab mode, when passing parameters through the `query` route, the `card-active` attribute still exists after leaving the active tab, resulting in the font color not changing when the mouse `hover`
 | 
			
		||||
- Fixed the error in reading and parsing the same name in the `src/layout/components/appMain.vue` file
 | 
			
		||||
- Fixed the issue where the height of the embedded page `frameView` does not adapt after hiding the tab page.
 | 
			
		||||
- Fixed the problem of invalid routing `meta.transition.name` configuration
 | 
			
		||||
- Fixed the problem that the right-click tab page panel cannot be closed when clicking on the `iframe` page and the right-click tab page panel is blocked when on the `iframe` page
 | 
			
		||||
- Fixed the problem of missing parameters when clicking on breadcrumbs to jump to the page in routing `query` and `params` modes
 | 
			
		||||
 | 
			
		||||
### 🍏Perf
 | 
			
		||||
 | 
			
		||||
- Optimize theme color
 | 
			
		||||
- Tabs can be slid left or right according to the sliding force
 | 
			
		||||
- The interface naming rules are unified into `kebab-case` string naming method
 | 
			
		||||
- The `label` of `el-form` is consistent with the global `label` style
 | 
			
		||||
- `VITE_PUBLIC_PATH` defaults to `/`, which is more friendly to `VITE_ROUTER_HISTORY` in `h5` mode
 | 
			
		||||
- Optimize the `transformI18n` function and support unlimited nesting levels for internationalization (of course, the platform still recommends that the fewer nesting levels, the better)
 | 
			
		||||
- When initializing the page, load `pinia` first and then `router`, which is compatible with more usage scenarios.
 | 
			
		||||
- Optimize the judgment logic of request whitelist
 | 
			
		||||
- The navigation style of the left menu has been adjusted to optimize the different display methods on PC and mobile when there is no logo.
 | 
			
		||||
- Upgrade code specification style related libraries to the latest
 | 
			
		||||
- Optimize login page `loading` judgment
 | 
			
		||||
- Optimize the `IconSelect` icon selector component to improve user experience
 | 
			
		||||
- Optimize the segmented controller component and add `v-model` support
 | 
			
		||||
- Optimize the method of obtaining platform `logo`
 | 
			
		||||
- Upgraded `@pureadmin/theme`, bringing more friendly `esm` support
 | 
			
		||||
- Optimize some functions in the `build/info.ts` file to make it friendly and support `esm`
 | 
			
		||||
- Optimize the column setting pop-up box of the `PureTableBar` component, set the maximum height, and scroll beyond it
 | 
			
		||||
- Optimize the functional pop-up component `ReDialog` to retain the closing animation
 | 
			
		||||
- Test the Chinese path and delete the `sass-loader` dependency
 | 
			
		||||
- The packaged code is changed to the browser that natively supports [ES2015](https://caniuse.com/es6) by default
 | 
			
		||||
- Remove the `stylelint` plug-in dependency that will be automatically installed
 | 
			
		||||
- Enhance the way `useRenderIcon` uses local `svg`
 | 
			
		||||
- Optimize the style of the expand and collapse buttons in the lower left corner of the left menu under the bright white theme color scheme
 | 
			
		||||
- Optimize all `description` contents of `el-empty`. Add `el-empty` when the icon selector content is empty
 | 
			
		||||
- The `tooltip` theme after the left menu is collapsed is consistent with the overall menu
 | 
			
		||||
- Update `svgo` command to `svgo -f . -r` (compress all `SVG` files in the current directory)
 | 
			
		||||
- Optimize project construction related functions
 | 
			
		||||
- Enhanced `ReTypeit` component to support slots and all `typeit` configuration items
 | 
			
		||||
- Optimize internationalization-related processing logic and add cache during initialization to avoid unnecessary performance consumption
 | 
			
		||||
 | 
			
		||||
# 4.5.0 (2023-06-26)
 | 
			
		||||
 | 
			
		||||
### ✔️ refactor
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										93
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										93
									
								
								CHANGELOG.md
									
									
									
									
									
								
							@ -1,3 +1,96 @@
 | 
			
		||||
# 5.0.0 (2024-02-26)
 | 
			
		||||
 | 
			
		||||
Totally `ESM` version
 | 
			
		||||
 | 
			
		||||
### ✔️refactor
 | 
			
		||||
 | 
			
		||||
- Upgrade `vite` to `v5` version, specify `node` version `>18.18.0`, `pnpm` version `>=8.6.10`
 | 
			
		||||
- Use [vite-plugin-fake-server](https://www.npmjs.com/package/vite-plugin-fake-server) to replace [vite-plugin-mock](https://www.npmjs.com/package/vite-plugin-mock), use [@faker-js/faker](https://www.npmjs.com/package/@faker-js/faker) to replace [mockjs](https://www.npmjs.com/package/mockjs)
 | 
			
		||||
- Rename `tailwind.config.js` to `tailwind.config.ts` and update its file to `esm` syntax
 | 
			
		||||
- Updated `.prettierrc.js` file to `esm` syntax
 | 
			
		||||
- Updated `postcss.config.js` file to `esm` syntax
 | 
			
		||||
- Updated `commitlint.config.js` file to `esm` syntax
 | 
			
		||||
- Use `eslint.config.js` to replace `.eslintrc.js` and follow `esm` syntax
 | 
			
		||||
- Upgrade `stylelint` to `16` version and follow `esm` syntax
 | 
			
		||||
- All `search` search icons are uniformly replaced with `@iconify-icons/ri/search-line` which is more commonly used and put into the global offline icon
 | 
			
		||||
- Removed the filter effect of `iframe` in dark mode
 | 
			
		||||
- Bring a more beautiful and refined homepage
 | 
			
		||||
- Cleaner and neater pop-up panel on the right side of project configuration
 | 
			
		||||
- Restructure the About page to make it more compact and key information more prominent
 | 
			
		||||
 | 
			
		||||
### 🎫Feat
 | 
			
		||||
 | 
			
		||||
- Add system management-menu management
 | 
			
		||||
- Improve system management-user management
 | 
			
		||||
- Embedded `iframe` pages support setting `keepAlive` to maintain page status
 | 
			
		||||
- Optimized navigation, the pop-up menu is adaptive and scrollable beyond the content area
 | 
			
		||||
- Added file upload example
 | 
			
		||||
- Added overall style adaptive operating system light, dark, and automatic theme functions
 | 
			
		||||
- Add footer
 | 
			
		||||
- Supports multi-tab pages to open systems that have already been logged in without logging in again and adds an internal login-free function (users can choose the number of days without login)
 | 
			
		||||
- Terminal command line that brings a high-level feel
 | 
			
		||||
- Add audio visualization function example
 | 
			
		||||
- Added video frame interception - `WebAssembly` version, supports `MP4`, `MOV`, `AVI`, `WebM`, `MKV` and other mainstream formats
 | 
			
		||||
- Added methods to block keyboard `F12`, browser default right-click menu, page element selection, and picture default draggability
 | 
			
		||||
- The secondary package `localforage` supports setting expiration time and provides complete type prompts
 | 
			
		||||
- Add `AnimateCss` selector component `ReAnimateSelector`
 | 
			
		||||
- Added `ReText` component, supports automatic omission and display of `Tooltip` function, supports multi-line omission, high reusability
 | 
			
		||||
- Add an art drawing board function, which can be used to draw some design idea architecture diagrams, for example
 | 
			
		||||
- New component - optional button example
 | 
			
		||||
- Add common button examples
 | 
			
		||||
- Added color picker component example
 | 
			
		||||
- Add date picker component example
 | 
			
		||||
- Added datetime picker example
 | 
			
		||||
- Added time selector example
 | 
			
		||||
- Added statistics component example
 | 
			
		||||
- Add label component example
 | 
			
		||||
- Added accordion panel component example
 | 
			
		||||
- Add progress bar component example
 | 
			
		||||
- Upgrade `Swiper 11`
 | 
			
		||||
- Add [vite-plugin-router-warn](https://www.npmjs.com/package/vite-plugin-router-warn) plug-in to eliminate unnecessary `vue-router` dynamic routing warning`No match found for location with path`
 | 
			
		||||
 | 
			
		||||
### 🐞 Bug fixes
 | 
			
		||||
 | 
			
		||||
- Fixed the problem that in `query` routing parameter passing mode, two `router` jumps will be triggered when clicking the tab page to switch operations.
 | 
			
		||||
- Fixed an issue in card tab mode, when passing parameters through the `query` route, the `card-active` attribute still exists after leaving the active tab, resulting in the font color not changing when the mouse `hover`
 | 
			
		||||
- Fixed the error in reading and parsing the same name in the `src/layout/components/appMain.vue` file
 | 
			
		||||
- Fixed the issue where the height of the embedded page `frameView` does not adapt after hiding the tab page.
 | 
			
		||||
- Fixed the problem of invalid routing `meta.transition.name` configuration
 | 
			
		||||
- Fixed the problem that the right-click tab page panel cannot be closed when clicking on the `iframe` page and the right-click tab page panel is blocked when on the `iframe` page
 | 
			
		||||
- Fixed the problem of missing parameters when clicking on breadcrumbs to jump to the page in routing `query` and `params` modes
 | 
			
		||||
 | 
			
		||||
### 🍏Perf
 | 
			
		||||
 | 
			
		||||
- Optimize theme color
 | 
			
		||||
- Tabs can be slid left or right according to the sliding force
 | 
			
		||||
- The interface naming rules are unified into `kebab-case` string naming method
 | 
			
		||||
- The `label` of `el-form` is consistent with the global `label` style
 | 
			
		||||
- `VITE_PUBLIC_PATH` defaults to `/`, which is more friendly to `VITE_ROUTER_HISTORY` in `h5` mode
 | 
			
		||||
- Optimize the `transformI18n` function and support unlimited nesting levels for internationalization (of course, the platform still recommends that the fewer nesting levels, the better)
 | 
			
		||||
- When initializing the page, load `pinia` first and then `router`, which is compatible with more usage scenarios.
 | 
			
		||||
- Optimize the judgment logic of request whitelist
 | 
			
		||||
- The navigation style of the left menu has been adjusted to optimize the different display methods on PC and mobile when there is no logo.
 | 
			
		||||
- Upgrade code specification style related libraries to the latest
 | 
			
		||||
- Optimize login page `loading` judgment
 | 
			
		||||
- Optimize the `IconSelect` icon selector component to improve user experience
 | 
			
		||||
- Optimize the segmented controller component and add `v-model` support
 | 
			
		||||
- Optimize the method of obtaining platform `logo`
 | 
			
		||||
- Upgraded `@pureadmin/theme`, bringing more friendly `esm` support
 | 
			
		||||
- Optimize some functions in the `build/info.ts` file to make it friendly and support `esm`
 | 
			
		||||
- Optimize the column setting pop-up box of the `PureTableBar` component, set the maximum height, and scroll beyond it
 | 
			
		||||
- Optimize the functional pop-up component `ReDialog` to retain the closing animation
 | 
			
		||||
- Test the Chinese path and delete the `sass-loader` dependency
 | 
			
		||||
- The packaged code is changed to the browser that natively supports [ES2015](https://caniuse.com/es6) by default
 | 
			
		||||
- Remove the `stylelint` plug-in dependency that will be automatically installed
 | 
			
		||||
- Enhance the way `useRenderIcon` uses local `svg`
 | 
			
		||||
- Optimize the style of the expand and collapse buttons in the lower left corner of the left menu under the bright white theme color scheme
 | 
			
		||||
- Optimize all `description` contents of `el-empty`. Add `el-empty` when the icon selector content is empty
 | 
			
		||||
- The `tooltip` theme after the left menu is collapsed is consistent with the overall menu
 | 
			
		||||
- Update `svgo` command to `svgo -f . -r` (compress all `SVG` files in the current directory)
 | 
			
		||||
- Optimize project construction related functions
 | 
			
		||||
- Enhanced `ReTypeit` component to support slots and all `typeit` configuration items
 | 
			
		||||
- Optimize internationalization-related processing logic and add cache during initialization to avoid unnecessary performance consumption
 | 
			
		||||
 | 
			
		||||
# 4.5.0 (2023-06-26)
 | 
			
		||||
 | 
			
		||||
### ✔️ refactor
 | 
			
		||||
 | 
			
		||||
@ -1,3 +1,96 @@
 | 
			
		||||
# 5.0.0 (2024-02-26)
 | 
			
		||||
 | 
			
		||||
全面`ESM`版本
 | 
			
		||||
 | 
			
		||||
### ✔️ refactor
 | 
			
		||||
 | 
			
		||||
- 升级`vite`至`v5`版本,规定`node`版本`>18.18.0`,`pnpm`版本`>=8.6.10`
 | 
			
		||||
- 使用 [vite-plugin-fake-server](https://www.npmjs.com/package/vite-plugin-fake-server) 替换 [vite-plugin-mock](https://www.npmjs.com/package/vite-plugin-mock),使用 [@faker-js/faker](https://www.npmjs.com/package/@faker-js/faker) 替换 [mockjs](https://www.npmjs.com/package/mockjs)
 | 
			
		||||
- 重命名`tailwind.config.js`为`tailwind.config.ts`并更新其文件为`esm`语法
 | 
			
		||||
- 更新`.prettierrc.js`文件为`esm`语法
 | 
			
		||||
- 更新`postcss.config.js`文件为`esm`语法
 | 
			
		||||
- 更新`commitlint.config.js`文件为`esm`语法
 | 
			
		||||
- 使用`eslint.config.js`替换`.eslintrc.js`并遵循`esm`语法
 | 
			
		||||
- 升级`stylelint`至`16`版本并遵循`esm`语法
 | 
			
		||||
- 所有`search`搜索图标统一替换为`@iconify-icons/ri/search-line`它比较常用将其放入全局离线图标中
 | 
			
		||||
- 移除`iframe`在暗模式下的滤镜效果
 | 
			
		||||
- 带来更美观精致的首页
 | 
			
		||||
- 更干净整洁的项目配置右侧弹出面板
 | 
			
		||||
- 重构关于页面,整体更紧致,关键信息更突出
 | 
			
		||||
 | 
			
		||||
### 🎫 Feat
 | 
			
		||||
 | 
			
		||||
- 添加系统管理-菜单管理
 | 
			
		||||
- 完善系统管理-用户管理
 | 
			
		||||
- 内嵌`iframe`页支持设置`keepAlive`,保持页面状态
 | 
			
		||||
- 优化导航,弹出的菜单超出内容区自适应且可滚动
 | 
			
		||||
- 添加文件上传示例
 | 
			
		||||
- 添加整体风格自适应操作系统浅色、深色、自动主题功能
 | 
			
		||||
- 添加页脚
 | 
			
		||||
- 支持多标签页打开已经登录的系统后无需再登录并添加内免登录功能(用户可选择免登录的天数)
 | 
			
		||||
- 带来高级感的终端命令行
 | 
			
		||||
- 添加音频可视化功能示例
 | 
			
		||||
- 添加视频帧截取-`WebAssembly`版,支持`MP4`、`MOV`、`AVI`、`WebM`、`MKV`等主流格式
 | 
			
		||||
- 添加阻止键盘`F12`、浏览器默认右键菜单、页面元素选中、图片默认可拖动方法
 | 
			
		||||
- 二次封装`localforage`支持设置过期时间,提供完整的类型提示
 | 
			
		||||
- 添加`AnimateCss`选择器组件`ReAnimateSelector`
 | 
			
		||||
- 添加`ReText`组件,支持自动省略显示`Tooltip`功能, 支持多行省略, 高可复用性
 | 
			
		||||
- 添加艺术画板功能,比如可以用来绘制一些设计思想架构图
 | 
			
		||||
- 新增组件-可选按钮示例
 | 
			
		||||
- 添加常用按钮示例
 | 
			
		||||
- 添加颜色选择器组件示例
 | 
			
		||||
- 添加日期选择器组件示例
 | 
			
		||||
- 添加日期时间选择器示例
 | 
			
		||||
- 添加时间选择(器)示例
 | 
			
		||||
- 添加统计组件示例
 | 
			
		||||
- 添加标签组件示例
 | 
			
		||||
- 添加折叠面板组件示例
 | 
			
		||||
- 添加进度条组件示例
 | 
			
		||||
- 升级`Swiper 11`
 | 
			
		||||
- 添加 [vite-plugin-router-warn](https://www.npmjs.com/package/vite-plugin-router-warn) 插件,根治非必要的`vue-router`动态路由警告`No match found for location with path`
 | 
			
		||||
 | 
			
		||||
### 🐞 Bug fixes
 | 
			
		||||
 | 
			
		||||
- 修复`query`路由传参模式下,点击标签页进行切换操作时会触发两次`router`跳转问题
 | 
			
		||||
- 修复卡片标签页模式下,通过`query`路由传参时,离开激活的标签后仍存在`card-active`属性,导致鼠标`hover`时字体颜色未改变
 | 
			
		||||
- 修复`src/layout/components/appMain.vue`文件中同名词读取解析错误
 | 
			
		||||
- 修复内嵌页面`frameView`在隐藏标签页后高度没有自适应
 | 
			
		||||
- 修复路由`meta.transition.name`配置无效的问题
 | 
			
		||||
- 修复点击`iframe`页面无法关闭右键标签页面板以及在`iframe`页面时右键标签页面板被遮挡的问题
 | 
			
		||||
- 修复在路由`query`、`params`模式下点击面包屑跳转页面少参问题
 | 
			
		||||
 | 
			
		||||
### 🍏 Perf
 | 
			
		||||
 | 
			
		||||
- 优化主题色
 | 
			
		||||
- 标签页可按滑动力度进行左右滑动
 | 
			
		||||
- 接口命名规则统一为`kebab-case`串式命名法
 | 
			
		||||
- `el-form`的`label`和全局的`label`样式保持一致
 | 
			
		||||
- `VITE_PUBLIC_PATH`默认还原为 `/` 对`VITE_ROUTER_HISTORY`为`h5`模式更友好
 | 
			
		||||
- 优化`transformI18n`函数,国际化支持无限嵌套级别(当然平台还是推荐嵌套层级越少越好)
 | 
			
		||||
- 页面初始化时先加载`pinia`再加载`router`,兼容更多使用场景
 | 
			
		||||
- 优化请求白名单的判断逻辑
 | 
			
		||||
- 左侧菜单导航样式调整,优化有无`logo`时`pc`端和移动端不同的展示方式
 | 
			
		||||
- 升级代码规范风格相关库至最新
 | 
			
		||||
- 优化登录页`loading`判断
 | 
			
		||||
- 优化`IconSelect`图标选择器组件,提升用户体验
 | 
			
		||||
- 优化分段控制器组件,添加`v-model`支持
 | 
			
		||||
- 优化平台`logo`获取方式
 | 
			
		||||
- 升级`@pureadmin/theme`,带来了更友好的`esm`支持
 | 
			
		||||
- 优化`build/info.ts`文件中的一些函数,使其友好支持`esm`
 | 
			
		||||
- 优化`PureTableBar`组件的列设置弹出框,设置最大高度,超出可滚动
 | 
			
		||||
- 优化函数式弹框组件`ReDialog`保留关闭动画
 | 
			
		||||
- 对中文路径做测试,删除`sass-loader`依赖
 | 
			
		||||
- 打包后的代码改为默认原生支持 [ES2015](https://caniuse.com/es6) 的浏览器
 | 
			
		||||
- 删除会自动安装的`stylelint`插件依赖
 | 
			
		||||
- 增强`useRenderIcon`使用本地`svg`的方式
 | 
			
		||||
- 优化左侧菜单最左下角的展开、折叠按钮在亮白主题配色下的样式
 | 
			
		||||
- 优化所有`el-empty`的`description`内容。图标选择器内容为空时加上`el-empty`
 | 
			
		||||
- 左侧菜单折叠后的`tooltip`主题与整体菜单保持统一
 | 
			
		||||
- 更新`svgo`命令为`svgo -f . -r`(压缩当前目录下的所有`SVG`文件)
 | 
			
		||||
- 优化项目构建相关函数
 | 
			
		||||
- 增强`ReTypeit`组件,支持插槽以及所有`typeit`配置项
 | 
			
		||||
- 优化国际化相关处理逻辑,初始化时添加缓存以避免不必要的性能消耗
 | 
			
		||||
 | 
			
		||||
# 4.5.0 (2023-06-26)
 | 
			
		||||
 | 
			
		||||
### ✔️ refactor
 | 
			
		||||
 | 
			
		||||
@ -29,6 +29,7 @@ menus:
 | 
			
		||||
  hssysManagement: System Manage
 | 
			
		||||
  hsUser: User Manage
 | 
			
		||||
  hsRole: Role Manage
 | 
			
		||||
  hsSystemMenu: Menu Manage
 | 
			
		||||
  hsDept: Dept Manage
 | 
			
		||||
  hseditor: Editor
 | 
			
		||||
  hsabnormal: Abnormal Page
 | 
			
		||||
 | 
			
		||||
@ -29,6 +29,7 @@ menus:
 | 
			
		||||
  hssysManagement: 系统管理
 | 
			
		||||
  hsUser: 用户管理
 | 
			
		||||
  hsRole: 角色管理
 | 
			
		||||
  hsSystemMenu: 菜单管理
 | 
			
		||||
  hsDept: 部门管理
 | 
			
		||||
  hseditor: 编辑器
 | 
			
		||||
  hsabnormal: 异常页面
 | 
			
		||||
 | 
			
		||||
@ -11,7 +11,7 @@ import { system, permission, frame, tabs } from "@/router/enums";
 | 
			
		||||
const systemRouter = {
 | 
			
		||||
  path: "/system",
 | 
			
		||||
  meta: {
 | 
			
		||||
    icon: "setting",
 | 
			
		||||
    icon: "ri:settings-3-line",
 | 
			
		||||
    title: "menus.hssysManagement",
 | 
			
		||||
    rank: system
 | 
			
		||||
  },
 | 
			
		||||
@ -20,7 +20,7 @@ const systemRouter = {
 | 
			
		||||
      path: "/system/user/index",
 | 
			
		||||
      name: "SystemUser",
 | 
			
		||||
      meta: {
 | 
			
		||||
        icon: "flUser",
 | 
			
		||||
        icon: "ri:admin-line",
 | 
			
		||||
        title: "menus.hsUser",
 | 
			
		||||
        roles: ["admin"]
 | 
			
		||||
      }
 | 
			
		||||
@ -29,16 +29,25 @@ const systemRouter = {
 | 
			
		||||
      path: "/system/role/index",
 | 
			
		||||
      name: "SystemRole",
 | 
			
		||||
      meta: {
 | 
			
		||||
        icon: "role",
 | 
			
		||||
        icon: "ri:admin-fill",
 | 
			
		||||
        title: "menus.hsRole",
 | 
			
		||||
        roles: ["admin"]
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      path: "/system/menu/index",
 | 
			
		||||
      name: "SystemMenu",
 | 
			
		||||
      meta: {
 | 
			
		||||
        icon: "ep:menu",
 | 
			
		||||
        title: "menus.hsSystemMenu",
 | 
			
		||||
        roles: ["admin"]
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      path: "/system/dept/index",
 | 
			
		||||
      name: "SystemDept",
 | 
			
		||||
      meta: {
 | 
			
		||||
        icon: "dept",
 | 
			
		||||
        icon: "ri:git-branch-line",
 | 
			
		||||
        title: "menus.hsDept",
 | 
			
		||||
        roles: ["admin"]
 | 
			
		||||
      }
 | 
			
		||||
@ -50,7 +59,7 @@ const permissionRouter = {
 | 
			
		||||
  path: "/permission",
 | 
			
		||||
  meta: {
 | 
			
		||||
    title: "menus.permission",
 | 
			
		||||
    icon: "lollipop",
 | 
			
		||||
    icon: "ep:lollipop",
 | 
			
		||||
    rank: permission
 | 
			
		||||
  },
 | 
			
		||||
  children: [
 | 
			
		||||
@ -68,7 +77,11 @@ const permissionRouter = {
 | 
			
		||||
      meta: {
 | 
			
		||||
        title: "menus.permissionButton",
 | 
			
		||||
        roles: ["admin", "common"],
 | 
			
		||||
        auths: ["btn_add", "btn_edit", "btn_delete"]
 | 
			
		||||
        auths: [
 | 
			
		||||
          "permission:btn:add",
 | 
			
		||||
          "permission:btn:edit",
 | 
			
		||||
          "permission:btn:delete"
 | 
			
		||||
        ]
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  ]
 | 
			
		||||
@ -77,7 +90,7 @@ const permissionRouter = {
 | 
			
		||||
const frameRouter = {
 | 
			
		||||
  path: "/iframe",
 | 
			
		||||
  meta: {
 | 
			
		||||
    icon: "monitor",
 | 
			
		||||
    icon: "ep:monitor",
 | 
			
		||||
    title: "menus.hsExternalPage",
 | 
			
		||||
    rank: frame
 | 
			
		||||
  },
 | 
			
		||||
@ -180,7 +193,7 @@ const frameRouter = {
 | 
			
		||||
const tabsRouter = {
 | 
			
		||||
  path: "/tabs",
 | 
			
		||||
  meta: {
 | 
			
		||||
    icon: "tag",
 | 
			
		||||
    icon: "ri:bookmark-2-line",
 | 
			
		||||
    title: "menus.hstabs",
 | 
			
		||||
    rank: tabs
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										614
									
								
								mock/system.ts
									
									
									
									
									
								
							
							
						
						
									
										614
									
								
								mock/system.ts
									
									
									
									
									
								
							@ -140,6 +140,620 @@ export default defineFakeRoute([
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  // 菜单管理
 | 
			
		||||
  {
 | 
			
		||||
    url: "/menu",
 | 
			
		||||
    method: "post",
 | 
			
		||||
    response: () => {
 | 
			
		||||
      return {
 | 
			
		||||
        success: true,
 | 
			
		||||
        data: [
 | 
			
		||||
          // 外部页面
 | 
			
		||||
          {
 | 
			
		||||
            parentId: 0,
 | 
			
		||||
            id: 100,
 | 
			
		||||
            menuType: 0, // 菜单类型(0代表菜单、1代表iframe、2代表外链、3代表按钮)
 | 
			
		||||
            title: "menus.hsExternalPage",
 | 
			
		||||
            name: "PureIframe",
 | 
			
		||||
            path: "/iframe",
 | 
			
		||||
            component: "",
 | 
			
		||||
            rank: 7,
 | 
			
		||||
            redirect: "",
 | 
			
		||||
            icon: "ep:monitor",
 | 
			
		||||
            extraIcon: "",
 | 
			
		||||
            enterTransition: "",
 | 
			
		||||
            leaveTransition: "",
 | 
			
		||||
            activePath: "",
 | 
			
		||||
            auths: "",
 | 
			
		||||
            frameSrc: "",
 | 
			
		||||
            frameLoading: true,
 | 
			
		||||
            keepAlive: false,
 | 
			
		||||
            hiddenTag: false,
 | 
			
		||||
            showLink: true,
 | 
			
		||||
            showParent: false
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            parentId: 100,
 | 
			
		||||
            id: 101,
 | 
			
		||||
            menuType: 0,
 | 
			
		||||
            title: "menus.hsExternalDoc",
 | 
			
		||||
            name: "PureIframeExternal",
 | 
			
		||||
            path: "/iframe/external",
 | 
			
		||||
            component: "",
 | 
			
		||||
            rank: null,
 | 
			
		||||
            redirect: "",
 | 
			
		||||
            icon: "",
 | 
			
		||||
            extraIcon: "",
 | 
			
		||||
            enterTransition: "",
 | 
			
		||||
            leaveTransition: "",
 | 
			
		||||
            activePath: "",
 | 
			
		||||
            auths: "",
 | 
			
		||||
            frameSrc: "",
 | 
			
		||||
            frameLoading: true,
 | 
			
		||||
            keepAlive: false,
 | 
			
		||||
            hiddenTag: false,
 | 
			
		||||
            showLink: true,
 | 
			
		||||
            showParent: false
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            parentId: 101,
 | 
			
		||||
            id: 102,
 | 
			
		||||
            menuType: 2,
 | 
			
		||||
            title: "menus.externalLink",
 | 
			
		||||
            name: "https://yiming_chang.gitee.io/pure-admin-doc",
 | 
			
		||||
            path: "/external",
 | 
			
		||||
            component: "",
 | 
			
		||||
            rank: null,
 | 
			
		||||
            redirect: "",
 | 
			
		||||
            icon: "",
 | 
			
		||||
            extraIcon: "",
 | 
			
		||||
            enterTransition: "",
 | 
			
		||||
            leaveTransition: "",
 | 
			
		||||
            activePath: "",
 | 
			
		||||
            auths: "",
 | 
			
		||||
            frameSrc: "",
 | 
			
		||||
            frameLoading: true,
 | 
			
		||||
            keepAlive: false,
 | 
			
		||||
            hiddenTag: false,
 | 
			
		||||
            showLink: true,
 | 
			
		||||
            showParent: false
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            parentId: 101,
 | 
			
		||||
            id: 103,
 | 
			
		||||
            menuType: 2,
 | 
			
		||||
            title: "menus.pureutilsLink",
 | 
			
		||||
            name: "https://pure-admin-utils.netlify.app/",
 | 
			
		||||
            path: "/pureutilsLink",
 | 
			
		||||
            component: "",
 | 
			
		||||
            rank: null,
 | 
			
		||||
            redirect: "",
 | 
			
		||||
            icon: "",
 | 
			
		||||
            extraIcon: "",
 | 
			
		||||
            enterTransition: "",
 | 
			
		||||
            leaveTransition: "",
 | 
			
		||||
            activePath: "",
 | 
			
		||||
            auths: "",
 | 
			
		||||
            frameSrc: "",
 | 
			
		||||
            frameLoading: true,
 | 
			
		||||
            keepAlive: false,
 | 
			
		||||
            hiddenTag: false,
 | 
			
		||||
            showLink: true,
 | 
			
		||||
            showParent: false
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            parentId: 100,
 | 
			
		||||
            id: 104,
 | 
			
		||||
            menuType: 1,
 | 
			
		||||
            title: "menus.hsEmbeddedDoc",
 | 
			
		||||
            name: "PureIframeEmbedded",
 | 
			
		||||
            path: "/iframe/embedded",
 | 
			
		||||
            component: "",
 | 
			
		||||
            rank: null,
 | 
			
		||||
            redirect: "",
 | 
			
		||||
            icon: "",
 | 
			
		||||
            extraIcon: "",
 | 
			
		||||
            enterTransition: "",
 | 
			
		||||
            leaveTransition: "",
 | 
			
		||||
            activePath: "",
 | 
			
		||||
            auths: "",
 | 
			
		||||
            frameSrc: "",
 | 
			
		||||
            frameLoading: true,
 | 
			
		||||
            keepAlive: false,
 | 
			
		||||
            hiddenTag: false,
 | 
			
		||||
            showLink: true,
 | 
			
		||||
            showParent: false
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            parentId: 104,
 | 
			
		||||
            id: 105,
 | 
			
		||||
            menuType: 1,
 | 
			
		||||
            title: "menus.hsEpDocument",
 | 
			
		||||
            name: "FrameEp",
 | 
			
		||||
            path: "/iframe/ep",
 | 
			
		||||
            component: "",
 | 
			
		||||
            rank: null,
 | 
			
		||||
            redirect: "",
 | 
			
		||||
            icon: "",
 | 
			
		||||
            extraIcon: "",
 | 
			
		||||
            enterTransition: "",
 | 
			
		||||
            leaveTransition: "",
 | 
			
		||||
            activePath: "",
 | 
			
		||||
            auths: "",
 | 
			
		||||
            frameSrc: "https://element-plus.org/zh-CN/",
 | 
			
		||||
            frameLoading: true,
 | 
			
		||||
            keepAlive: true,
 | 
			
		||||
            hiddenTag: false,
 | 
			
		||||
            showLink: true,
 | 
			
		||||
            showParent: false
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            parentId: 104,
 | 
			
		||||
            id: 106,
 | 
			
		||||
            menuType: 1,
 | 
			
		||||
            title: "menus.hsTailwindcssDocument",
 | 
			
		||||
            name: "FrameTailwindcss",
 | 
			
		||||
            path: "/iframe/tailwindcss",
 | 
			
		||||
            component: "",
 | 
			
		||||
            rank: null,
 | 
			
		||||
            redirect: "",
 | 
			
		||||
            icon: "",
 | 
			
		||||
            extraIcon: "",
 | 
			
		||||
            enterTransition: "",
 | 
			
		||||
            leaveTransition: "",
 | 
			
		||||
            activePath: "",
 | 
			
		||||
            auths: "",
 | 
			
		||||
            frameSrc: "https://tailwindcss.com/docs/installation",
 | 
			
		||||
            frameLoading: true,
 | 
			
		||||
            keepAlive: true,
 | 
			
		||||
            hiddenTag: false,
 | 
			
		||||
            showLink: true,
 | 
			
		||||
            showParent: false
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            parentId: 104,
 | 
			
		||||
            id: 107,
 | 
			
		||||
            menuType: 1,
 | 
			
		||||
            title: "menus.hsVueDocument",
 | 
			
		||||
            name: "FrameVue",
 | 
			
		||||
            path: "/iframe/vue3",
 | 
			
		||||
            component: "",
 | 
			
		||||
            rank: null,
 | 
			
		||||
            redirect: "",
 | 
			
		||||
            icon: "",
 | 
			
		||||
            extraIcon: "",
 | 
			
		||||
            enterTransition: "",
 | 
			
		||||
            leaveTransition: "",
 | 
			
		||||
            activePath: "",
 | 
			
		||||
            auths: "",
 | 
			
		||||
            frameSrc: "https://cn.vuejs.org/",
 | 
			
		||||
            frameLoading: true,
 | 
			
		||||
            keepAlive: true,
 | 
			
		||||
            hiddenTag: false,
 | 
			
		||||
            showLink: true,
 | 
			
		||||
            showParent: false
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            parentId: 104,
 | 
			
		||||
            id: 108,
 | 
			
		||||
            menuType: 1,
 | 
			
		||||
            title: "menus.hsViteDocument",
 | 
			
		||||
            name: "FrameVite",
 | 
			
		||||
            path: "/iframe/vite",
 | 
			
		||||
            component: "",
 | 
			
		||||
            rank: null,
 | 
			
		||||
            redirect: "",
 | 
			
		||||
            icon: "",
 | 
			
		||||
            extraIcon: "",
 | 
			
		||||
            enterTransition: "",
 | 
			
		||||
            leaveTransition: "",
 | 
			
		||||
            activePath: "",
 | 
			
		||||
            auths: "",
 | 
			
		||||
            frameSrc: "https://cn.vitejs.dev/",
 | 
			
		||||
            frameLoading: true,
 | 
			
		||||
            keepAlive: true,
 | 
			
		||||
            hiddenTag: false,
 | 
			
		||||
            showLink: true,
 | 
			
		||||
            showParent: false
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            parentId: 104,
 | 
			
		||||
            id: 109,
 | 
			
		||||
            menuType: 1,
 | 
			
		||||
            title: "menus.hsPiniaDocument",
 | 
			
		||||
            name: "FramePinia",
 | 
			
		||||
            path: "/iframe/pinia",
 | 
			
		||||
            component: "",
 | 
			
		||||
            rank: null,
 | 
			
		||||
            redirect: "",
 | 
			
		||||
            icon: "",
 | 
			
		||||
            extraIcon: "",
 | 
			
		||||
            enterTransition: "",
 | 
			
		||||
            leaveTransition: "",
 | 
			
		||||
            activePath: "",
 | 
			
		||||
            auths: "",
 | 
			
		||||
            frameSrc: "https://pinia.vuejs.org/zh/index.html",
 | 
			
		||||
            frameLoading: true,
 | 
			
		||||
            keepAlive: true,
 | 
			
		||||
            hiddenTag: false,
 | 
			
		||||
            showLink: true,
 | 
			
		||||
            showParent: false
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            parentId: 104,
 | 
			
		||||
            id: 110,
 | 
			
		||||
            menuType: 1,
 | 
			
		||||
            title: "menus.hsRouterDocument",
 | 
			
		||||
            name: "FrameRouter",
 | 
			
		||||
            path: "/iframe/vue-router",
 | 
			
		||||
            component: "",
 | 
			
		||||
            rank: null,
 | 
			
		||||
            redirect: "",
 | 
			
		||||
            icon: "",
 | 
			
		||||
            extraIcon: "",
 | 
			
		||||
            enterTransition: "",
 | 
			
		||||
            leaveTransition: "",
 | 
			
		||||
            activePath: "",
 | 
			
		||||
            auths: "",
 | 
			
		||||
            frameSrc: "https://router.vuejs.org/zh/",
 | 
			
		||||
            frameLoading: true,
 | 
			
		||||
            keepAlive: true,
 | 
			
		||||
            hiddenTag: false,
 | 
			
		||||
            showLink: true,
 | 
			
		||||
            showParent: false
 | 
			
		||||
          },
 | 
			
		||||
          // 权限管理
 | 
			
		||||
          {
 | 
			
		||||
            parentId: 0,
 | 
			
		||||
            id: 200,
 | 
			
		||||
            menuType: 0,
 | 
			
		||||
            title: "menus.permission",
 | 
			
		||||
            name: "PurePermission",
 | 
			
		||||
            path: "/permission",
 | 
			
		||||
            component: "",
 | 
			
		||||
            rank: 9,
 | 
			
		||||
            redirect: "",
 | 
			
		||||
            icon: "ep:lollipop",
 | 
			
		||||
            extraIcon: "",
 | 
			
		||||
            enterTransition: "",
 | 
			
		||||
            leaveTransition: "",
 | 
			
		||||
            activePath: "",
 | 
			
		||||
            auths: "",
 | 
			
		||||
            frameSrc: "",
 | 
			
		||||
            frameLoading: true,
 | 
			
		||||
            keepAlive: false,
 | 
			
		||||
            hiddenTag: false,
 | 
			
		||||
            showLink: true,
 | 
			
		||||
            showParent: false
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            parentId: 200,
 | 
			
		||||
            id: 201,
 | 
			
		||||
            menuType: 0,
 | 
			
		||||
            title: "menus.permissionPage",
 | 
			
		||||
            name: "PermissionPage",
 | 
			
		||||
            path: "/permission/page/index",
 | 
			
		||||
            component: "",
 | 
			
		||||
            rank: null,
 | 
			
		||||
            redirect: "",
 | 
			
		||||
            icon: "",
 | 
			
		||||
            extraIcon: "",
 | 
			
		||||
            enterTransition: "",
 | 
			
		||||
            leaveTransition: "",
 | 
			
		||||
            activePath: "",
 | 
			
		||||
            auths: "",
 | 
			
		||||
            frameSrc: "",
 | 
			
		||||
            frameLoading: true,
 | 
			
		||||
            keepAlive: false,
 | 
			
		||||
            hiddenTag: false,
 | 
			
		||||
            showLink: true,
 | 
			
		||||
            showParent: false
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            parentId: 200,
 | 
			
		||||
            id: 202,
 | 
			
		||||
            menuType: 0,
 | 
			
		||||
            title: "menus.permissionButton",
 | 
			
		||||
            name: "PermissionButton",
 | 
			
		||||
            path: "/permission/button/index",
 | 
			
		||||
            component: "",
 | 
			
		||||
            rank: null,
 | 
			
		||||
            redirect: "",
 | 
			
		||||
            icon: "",
 | 
			
		||||
            extraIcon: "",
 | 
			
		||||
            enterTransition: "",
 | 
			
		||||
            leaveTransition: "",
 | 
			
		||||
            activePath: "",
 | 
			
		||||
            auths: "",
 | 
			
		||||
            frameSrc: "",
 | 
			
		||||
            frameLoading: true,
 | 
			
		||||
            keepAlive: false,
 | 
			
		||||
            hiddenTag: false,
 | 
			
		||||
            showLink: true,
 | 
			
		||||
            showParent: false
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            parentId: 202,
 | 
			
		||||
            id: 203,
 | 
			
		||||
            menuType: 3,
 | 
			
		||||
            title: "添加",
 | 
			
		||||
            name: "",
 | 
			
		||||
            path: "",
 | 
			
		||||
            component: "",
 | 
			
		||||
            rank: null,
 | 
			
		||||
            redirect: "",
 | 
			
		||||
            icon: "",
 | 
			
		||||
            extraIcon: "",
 | 
			
		||||
            enterTransition: "",
 | 
			
		||||
            leaveTransition: "",
 | 
			
		||||
            activePath: "",
 | 
			
		||||
            auths: "permission:btn:add",
 | 
			
		||||
            frameSrc: "",
 | 
			
		||||
            frameLoading: true,
 | 
			
		||||
            keepAlive: false,
 | 
			
		||||
            hiddenTag: false,
 | 
			
		||||
            showLink: true,
 | 
			
		||||
            showParent: false
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            parentId: 202,
 | 
			
		||||
            id: 204,
 | 
			
		||||
            menuType: 3,
 | 
			
		||||
            title: "修改",
 | 
			
		||||
            name: "",
 | 
			
		||||
            path: "",
 | 
			
		||||
            component: "",
 | 
			
		||||
            rank: null,
 | 
			
		||||
            redirect: "",
 | 
			
		||||
            icon: "",
 | 
			
		||||
            extraIcon: "",
 | 
			
		||||
            enterTransition: "",
 | 
			
		||||
            leaveTransition: "",
 | 
			
		||||
            activePath: "",
 | 
			
		||||
            auths: "permission:btn:edit",
 | 
			
		||||
            frameSrc: "",
 | 
			
		||||
            frameLoading: true,
 | 
			
		||||
            keepAlive: false,
 | 
			
		||||
            hiddenTag: false,
 | 
			
		||||
            showLink: true,
 | 
			
		||||
            showParent: false
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            parentId: 202,
 | 
			
		||||
            id: 205,
 | 
			
		||||
            menuType: 3,
 | 
			
		||||
            title: "删除",
 | 
			
		||||
            name: "",
 | 
			
		||||
            path: "",
 | 
			
		||||
            component: "",
 | 
			
		||||
            rank: null,
 | 
			
		||||
            redirect: "",
 | 
			
		||||
            icon: "",
 | 
			
		||||
            extraIcon: "",
 | 
			
		||||
            enterTransition: "",
 | 
			
		||||
            leaveTransition: "",
 | 
			
		||||
            activePath: "",
 | 
			
		||||
            auths: "permission:btn:delete",
 | 
			
		||||
            frameSrc: "",
 | 
			
		||||
            frameLoading: true,
 | 
			
		||||
            keepAlive: false,
 | 
			
		||||
            hiddenTag: false,
 | 
			
		||||
            showLink: true,
 | 
			
		||||
            showParent: false
 | 
			
		||||
          },
 | 
			
		||||
          // 系统管理
 | 
			
		||||
          {
 | 
			
		||||
            parentId: 0,
 | 
			
		||||
            id: 300,
 | 
			
		||||
            menuType: 0,
 | 
			
		||||
            title: "menus.hssysManagement",
 | 
			
		||||
            name: "PureSystem",
 | 
			
		||||
            path: "/system",
 | 
			
		||||
            component: "",
 | 
			
		||||
            rank: 10,
 | 
			
		||||
            redirect: "",
 | 
			
		||||
            icon: "ri:settings-3-line",
 | 
			
		||||
            extraIcon: "",
 | 
			
		||||
            enterTransition: "",
 | 
			
		||||
            leaveTransition: "",
 | 
			
		||||
            activePath: "",
 | 
			
		||||
            auths: "",
 | 
			
		||||
            frameSrc: "",
 | 
			
		||||
            frameLoading: true,
 | 
			
		||||
            keepAlive: false,
 | 
			
		||||
            hiddenTag: false,
 | 
			
		||||
            showLink: true,
 | 
			
		||||
            showParent: false
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            parentId: 300,
 | 
			
		||||
            id: 301,
 | 
			
		||||
            menuType: 0,
 | 
			
		||||
            title: "menus.hsUser",
 | 
			
		||||
            name: "SystemUser",
 | 
			
		||||
            path: "/system/user/index",
 | 
			
		||||
            component: "",
 | 
			
		||||
            rank: null,
 | 
			
		||||
            redirect: "",
 | 
			
		||||
            icon: "ri:admin-line",
 | 
			
		||||
            extraIcon: "",
 | 
			
		||||
            enterTransition: "",
 | 
			
		||||
            leaveTransition: "",
 | 
			
		||||
            activePath: "",
 | 
			
		||||
            auths: "",
 | 
			
		||||
            frameSrc: "",
 | 
			
		||||
            frameLoading: true,
 | 
			
		||||
            keepAlive: false,
 | 
			
		||||
            hiddenTag: false,
 | 
			
		||||
            showLink: true,
 | 
			
		||||
            showParent: false
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            parentId: 300,
 | 
			
		||||
            id: 302,
 | 
			
		||||
            menuType: 0,
 | 
			
		||||
            title: "menus.hsRole",
 | 
			
		||||
            name: "SystemRole",
 | 
			
		||||
            path: "/system/role/index",
 | 
			
		||||
            component: "",
 | 
			
		||||
            rank: null,
 | 
			
		||||
            redirect: "",
 | 
			
		||||
            icon: "ri:admin-fill",
 | 
			
		||||
            extraIcon: "",
 | 
			
		||||
            enterTransition: "",
 | 
			
		||||
            leaveTransition: "",
 | 
			
		||||
            activePath: "",
 | 
			
		||||
            auths: "",
 | 
			
		||||
            frameSrc: "",
 | 
			
		||||
            frameLoading: true,
 | 
			
		||||
            keepAlive: false,
 | 
			
		||||
            hiddenTag: false,
 | 
			
		||||
            showLink: true,
 | 
			
		||||
            showParent: false
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            parentId: 300,
 | 
			
		||||
            id: 303,
 | 
			
		||||
            menuType: 0,
 | 
			
		||||
            title: "menus.hsSystemMenu",
 | 
			
		||||
            name: "SystemMenu",
 | 
			
		||||
            path: "/system/menu/index",
 | 
			
		||||
            component: "",
 | 
			
		||||
            rank: null,
 | 
			
		||||
            redirect: "",
 | 
			
		||||
            icon: "ep:menu",
 | 
			
		||||
            extraIcon: "",
 | 
			
		||||
            enterTransition: "",
 | 
			
		||||
            leaveTransition: "",
 | 
			
		||||
            activePath: "",
 | 
			
		||||
            auths: "",
 | 
			
		||||
            frameSrc: "",
 | 
			
		||||
            frameLoading: true,
 | 
			
		||||
            keepAlive: false,
 | 
			
		||||
            hiddenTag: false,
 | 
			
		||||
            showLink: true,
 | 
			
		||||
            showParent: false
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            parentId: 300,
 | 
			
		||||
            id: 304,
 | 
			
		||||
            menuType: 0,
 | 
			
		||||
            title: "menus.hsDept",
 | 
			
		||||
            name: "SystemDept",
 | 
			
		||||
            path: "/system/dept/index",
 | 
			
		||||
            component: "",
 | 
			
		||||
            rank: null,
 | 
			
		||||
            redirect: "",
 | 
			
		||||
            icon: "ri:git-branch-line",
 | 
			
		||||
            extraIcon: "",
 | 
			
		||||
            enterTransition: "",
 | 
			
		||||
            leaveTransition: "",
 | 
			
		||||
            activePath: "",
 | 
			
		||||
            auths: "",
 | 
			
		||||
            frameSrc: "",
 | 
			
		||||
            frameLoading: true,
 | 
			
		||||
            keepAlive: false,
 | 
			
		||||
            hiddenTag: false,
 | 
			
		||||
            showLink: true,
 | 
			
		||||
            showParent: false
 | 
			
		||||
          },
 | 
			
		||||
          // 标签页操作
 | 
			
		||||
          {
 | 
			
		||||
            parentId: 0,
 | 
			
		||||
            id: 400,
 | 
			
		||||
            menuType: 0,
 | 
			
		||||
            title: "menus.hstabs",
 | 
			
		||||
            name: "PureTabs",
 | 
			
		||||
            path: "/tabs",
 | 
			
		||||
            component: "",
 | 
			
		||||
            rank: 11,
 | 
			
		||||
            redirect: "",
 | 
			
		||||
            icon: "ri:bookmark-2-line",
 | 
			
		||||
            extraIcon: "",
 | 
			
		||||
            enterTransition: "",
 | 
			
		||||
            leaveTransition: "",
 | 
			
		||||
            activePath: "",
 | 
			
		||||
            auths: "",
 | 
			
		||||
            frameSrc: "",
 | 
			
		||||
            frameLoading: true,
 | 
			
		||||
            keepAlive: false,
 | 
			
		||||
            hiddenTag: false,
 | 
			
		||||
            showLink: true,
 | 
			
		||||
            showParent: false
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            parentId: 400,
 | 
			
		||||
            id: 401,
 | 
			
		||||
            menuType: 0,
 | 
			
		||||
            title: "menus.hstabs",
 | 
			
		||||
            name: "Tabs",
 | 
			
		||||
            path: "/tabs/index",
 | 
			
		||||
            component: "",
 | 
			
		||||
            rank: null,
 | 
			
		||||
            redirect: "",
 | 
			
		||||
            icon: "",
 | 
			
		||||
            extraIcon: "",
 | 
			
		||||
            enterTransition: "",
 | 
			
		||||
            leaveTransition: "",
 | 
			
		||||
            activePath: "",
 | 
			
		||||
            auths: "",
 | 
			
		||||
            frameSrc: "",
 | 
			
		||||
            frameLoading: true,
 | 
			
		||||
            keepAlive: false,
 | 
			
		||||
            hiddenTag: false,
 | 
			
		||||
            showLink: true,
 | 
			
		||||
            showParent: false
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            parentId: 400,
 | 
			
		||||
            id: 402,
 | 
			
		||||
            menuType: 0,
 | 
			
		||||
            title: "query传参模式",
 | 
			
		||||
            name: "TabQueryDetail",
 | 
			
		||||
            path: "/tabs/query-detail",
 | 
			
		||||
            component: "",
 | 
			
		||||
            rank: null,
 | 
			
		||||
            redirect: "",
 | 
			
		||||
            icon: "",
 | 
			
		||||
            extraIcon: "",
 | 
			
		||||
            enterTransition: "",
 | 
			
		||||
            leaveTransition: "",
 | 
			
		||||
            activePath: "/tabs/index",
 | 
			
		||||
            auths: "",
 | 
			
		||||
            frameSrc: "",
 | 
			
		||||
            frameLoading: true,
 | 
			
		||||
            keepAlive: false,
 | 
			
		||||
            hiddenTag: false,
 | 
			
		||||
            showLink: false,
 | 
			
		||||
            showParent: false
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            parentId: 400,
 | 
			
		||||
            id: 403,
 | 
			
		||||
            menuType: 0,
 | 
			
		||||
            title: "params传参模式",
 | 
			
		||||
            name: "TabParamsDetail",
 | 
			
		||||
            path: "/tabs/params-detail/:id",
 | 
			
		||||
            component: "params-detail",
 | 
			
		||||
            rank: null,
 | 
			
		||||
            redirect: "",
 | 
			
		||||
            icon: "",
 | 
			
		||||
            extraIcon: "",
 | 
			
		||||
            enterTransition: "",
 | 
			
		||||
            leaveTransition: "",
 | 
			
		||||
            activePath: "/tabs/index",
 | 
			
		||||
            auths: "",
 | 
			
		||||
            frameSrc: "",
 | 
			
		||||
            frameLoading: true,
 | 
			
		||||
            keepAlive: false,
 | 
			
		||||
            hiddenTag: false,
 | 
			
		||||
            showLink: false,
 | 
			
		||||
            showParent: false
 | 
			
		||||
          }
 | 
			
		||||
        ]
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  // 部门管理
 | 
			
		||||
  {
 | 
			
		||||
    url: "/dept",
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										76
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										76
									
								
								package.json
									
									
									
									
									
								
							@ -1,6 +1,6 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "vue-pure-admin",
 | 
			
		||||
  "version": "4.5.0",
 | 
			
		||||
  "version": "5.0.0",
 | 
			
		||||
  "private": true,
 | 
			
		||||
  "type": "module",
 | 
			
		||||
  "scripts": {
 | 
			
		||||
@ -19,7 +19,7 @@
 | 
			
		||||
    "lint:prettier": "prettier --write  \"src/**/*.{js,ts,json,tsx,css,scss,vue,html,md}\"",
 | 
			
		||||
    "lint:stylelint": "stylelint --cache --fix \"**/*.{html,vue,css,scss}\" --cache-location node_modules/.cache/stylelint/",
 | 
			
		||||
    "lint": "pnpm lint:eslint && pnpm lint:prettier && pnpm lint:stylelint",
 | 
			
		||||
    "prepare": "husky install",
 | 
			
		||||
    "prepare": "husky",
 | 
			
		||||
    "preinstall": "npx only-allow pnpm"
 | 
			
		||||
  },
 | 
			
		||||
  "keywords": [
 | 
			
		||||
@ -50,13 +50,13 @@
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "@amap/amap-jsapi-loader": "^1.0.1",
 | 
			
		||||
    "@howdyjs/mouse-menu": "2.0.9",
 | 
			
		||||
    "@logicflow/core": "^1.2.18",
 | 
			
		||||
    "@logicflow/extension": "^1.2.19",
 | 
			
		||||
    "@logicflow/core": "^1.2.22",
 | 
			
		||||
    "@logicflow/extension": "^1.2.22",
 | 
			
		||||
    "@pureadmin/descriptions": "^1.2.0",
 | 
			
		||||
    "@pureadmin/table": "^3.0.2",
 | 
			
		||||
    "@pureadmin/utils": "^2.4.4",
 | 
			
		||||
    "@vueuse/core": "^10.7.2",
 | 
			
		||||
    "@vueuse/motion": "^2.0.0",
 | 
			
		||||
    "@vueuse/core": "^10.8.0",
 | 
			
		||||
    "@vueuse/motion": "^2.1.0",
 | 
			
		||||
    "@wangeditor/editor": "^5.1.23",
 | 
			
		||||
    "@wangeditor/editor-for-vue": "^5.1.12",
 | 
			
		||||
    "@zxcvbn-ts/core": "^3.0.4",
 | 
			
		||||
@ -65,9 +65,9 @@
 | 
			
		||||
    "china-area-data": "^5.0.1",
 | 
			
		||||
    "cropperjs": "^1.6.1",
 | 
			
		||||
    "dayjs": "^1.11.10",
 | 
			
		||||
    "echarts": "^5.4.3",
 | 
			
		||||
    "echarts": "^5.5.0",
 | 
			
		||||
    "el-table-infinite-scroll": "^3.0.3",
 | 
			
		||||
    "element-plus": "^2.5.3",
 | 
			
		||||
    "element-plus": "^2.5.6",
 | 
			
		||||
    "intro.js": "^7.2.0",
 | 
			
		||||
    "js-cookie": "^3.0.5",
 | 
			
		||||
    "jsbarcode": "^3.11.6",
 | 
			
		||||
@ -77,37 +77,37 @@
 | 
			
		||||
    "nprogress": "^0.2.0",
 | 
			
		||||
    "path": "^0.12.7",
 | 
			
		||||
    "pinia": "^2.1.7",
 | 
			
		||||
    "pinyin-pro": "^3.19.3",
 | 
			
		||||
    "pinyin-pro": "^3.19.6",
 | 
			
		||||
    "qrcode": "^1.5.3",
 | 
			
		||||
    "qs": "^6.11.2",
 | 
			
		||||
    "responsive-storage": "^2.2.0",
 | 
			
		||||
    "sortablejs": "^1.15.2",
 | 
			
		||||
    "swiper": "^11.0.5",
 | 
			
		||||
    "swiper": "^11.0.6",
 | 
			
		||||
    "typeit": "8.7.1",
 | 
			
		||||
    "v-contextmenu": "3.0.0",
 | 
			
		||||
    "v-contextmenu": "^3.2.0",
 | 
			
		||||
    "v3-infinite-loading": "^1.3.1",
 | 
			
		||||
    "version-rocket": "^1.7.1",
 | 
			
		||||
    "vue": "3.4.14",
 | 
			
		||||
    "vue-i18n": "^9.9.0",
 | 
			
		||||
    "vue-i18n": "^9.9.1",
 | 
			
		||||
    "vue-json-pretty": "^2.3.0",
 | 
			
		||||
    "vue-pdf-embed": "1.2.1",
 | 
			
		||||
    "vue-router": "^4.2.5",
 | 
			
		||||
    "vue-router": "^4.3.0",
 | 
			
		||||
    "vue-tippy": "^6.4.1",
 | 
			
		||||
    "vue-types": "^5.1.1",
 | 
			
		||||
    "vue-virtual-scroller": "2.0.0-beta.8",
 | 
			
		||||
    "vue-waterfall-plugin-next": "^2.3.1",
 | 
			
		||||
    "vue3-danmaku": "^1.6.0",
 | 
			
		||||
    "vuedraggable": "^4.1.0",
 | 
			
		||||
    "wavesurfer.js": "^7.7.1",
 | 
			
		||||
    "xgplayer": "^3.0.11",
 | 
			
		||||
    "wavesurfer.js": "^7.7.3",
 | 
			
		||||
    "xgplayer": "^3.0.13",
 | 
			
		||||
    "xlsx": "^0.18.5"
 | 
			
		||||
  },
 | 
			
		||||
  "devDependencies": {
 | 
			
		||||
    "@commitlint/cli": "^18.6.0",
 | 
			
		||||
    "@commitlint/config-conventional": "^18.6.0",
 | 
			
		||||
    "@commitlint/types": "^18.6.0",
 | 
			
		||||
    "@eslint/js": "^8.56.0",
 | 
			
		||||
    "@faker-js/faker": "^8.4.0",
 | 
			
		||||
    "@commitlint/cli": "^18.6.1",
 | 
			
		||||
    "@commitlint/config-conventional": "^18.6.2",
 | 
			
		||||
    "@commitlint/types": "^18.6.1",
 | 
			
		||||
    "@eslint/js": "^8.57.0",
 | 
			
		||||
    "@faker-js/faker": "^8.4.1",
 | 
			
		||||
    "@iconify-icons/ep": "^1.2.12",
 | 
			
		||||
    "@iconify-icons/ri": "^1.2.10",
 | 
			
		||||
    "@iconify/vue": "^4.1.1",
 | 
			
		||||
@ -116,44 +116,44 @@
 | 
			
		||||
    "@types/gradient-string": "^1.1.5",
 | 
			
		||||
    "@types/intro.js": "^5.1.5",
 | 
			
		||||
    "@types/js-cookie": "^3.0.6",
 | 
			
		||||
    "@types/node": "^20.11.7",
 | 
			
		||||
    "@types/node": "^20.11.20",
 | 
			
		||||
    "@types/nprogress": "^0.2.3",
 | 
			
		||||
    "@types/qrcode": "^1.5.5",
 | 
			
		||||
    "@types/qs": "^6.9.11",
 | 
			
		||||
    "@types/sortablejs": "^1.15.7",
 | 
			
		||||
    "@typescript-eslint/eslint-plugin": "^6.19.1",
 | 
			
		||||
    "@typescript-eslint/parser": "^6.19.1",
 | 
			
		||||
    "@vitejs/plugin-vue": "^5.0.3",
 | 
			
		||||
    "@types/sortablejs": "^1.15.8",
 | 
			
		||||
    "@typescript-eslint/eslint-plugin": "^7.0.2",
 | 
			
		||||
    "@typescript-eslint/parser": "^7.0.2",
 | 
			
		||||
    "@vitejs/plugin-vue": "^5.0.4",
 | 
			
		||||
    "@vitejs/plugin-vue-jsx": "^3.1.0",
 | 
			
		||||
    "autoprefixer": "^10.4.17",
 | 
			
		||||
    "boxen": "^7.1.1",
 | 
			
		||||
    "cloc": "^2.11.0",
 | 
			
		||||
    "cssnano": "^6.0.3",
 | 
			
		||||
    "eslint": "^8.56.0",
 | 
			
		||||
    "cssnano": "^6.0.5",
 | 
			
		||||
    "eslint": "^8.57.0",
 | 
			
		||||
    "eslint-config-prettier": "^9.1.0",
 | 
			
		||||
    "eslint-define-config": "^2.1.0",
 | 
			
		||||
    "eslint-plugin-prettier": "^5.1.3",
 | 
			
		||||
    "eslint-plugin-vue": "^9.20.1",
 | 
			
		||||
    "eslint-plugin-vue": "^9.22.0",
 | 
			
		||||
    "gradient-string": "^2.0.2",
 | 
			
		||||
    "husky": "^8.0.3",
 | 
			
		||||
    "lint-staged": "^15.2.0",
 | 
			
		||||
    "postcss": "^8.4.33",
 | 
			
		||||
    "husky": "^9.0.11",
 | 
			
		||||
    "lint-staged": "^15.2.2",
 | 
			
		||||
    "postcss": "^8.4.35",
 | 
			
		||||
    "postcss-html": "^1.6.0",
 | 
			
		||||
    "postcss-import": "^15.1.0",
 | 
			
		||||
    "postcss-import": "^16.0.1",
 | 
			
		||||
    "postcss-scss": "^4.0.9",
 | 
			
		||||
    "prettier": "^3.2.4",
 | 
			
		||||
    "prettier": "^3.2.5",
 | 
			
		||||
    "rimraf": "^5.0.5",
 | 
			
		||||
    "rollup-plugin-visualizer": "^5.12.0",
 | 
			
		||||
    "sass": "^1.70.0",
 | 
			
		||||
    "stylelint": "^16.2.0",
 | 
			
		||||
    "stylelint-config-recess-order": "^4.4.0",
 | 
			
		||||
    "sass": "^1.71.1",
 | 
			
		||||
    "stylelint": "^16.2.1",
 | 
			
		||||
    "stylelint-config-recess-order": "^4.6.0",
 | 
			
		||||
    "stylelint-config-recommended-vue": "^1.5.0",
 | 
			
		||||
    "stylelint-config-standard-scss": "^12.0.0",
 | 
			
		||||
    "stylelint-config-standard-scss": "^13.0.0",
 | 
			
		||||
    "stylelint-prettier": "^5.0.0",
 | 
			
		||||
    "svgo": "^3.2.0",
 | 
			
		||||
    "tailwindcss": "^3.4.1",
 | 
			
		||||
    "typescript": "^5.3.3",
 | 
			
		||||
    "vite": "^5.0.12",
 | 
			
		||||
    "vite": "^5.1.4",
 | 
			
		||||
    "vite-plugin-cdn-import": "^0.3.5",
 | 
			
		||||
    "vite-plugin-compression": "^0.5.1",
 | 
			
		||||
    "vite-plugin-fake-server": "^2.1.1",
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										2008
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2008
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -1,5 +1,5 @@
 | 
			
		||||
{
 | 
			
		||||
  "Version": "4.5.0",
 | 
			
		||||
  "Version": "5.0.0",
 | 
			
		||||
  "Title": "PureAdmin",
 | 
			
		||||
  "FixedHeader": true,
 | 
			
		||||
  "HiddenSideBar": false,
 | 
			
		||||
@ -13,7 +13,7 @@
 | 
			
		||||
  "Grey": false,
 | 
			
		||||
  "Weak": false,
 | 
			
		||||
  "HideTabs": false,
 | 
			
		||||
  "HideFooter": true,
 | 
			
		||||
  "HideFooter": false,
 | 
			
		||||
  "SidebarStatus": true,
 | 
			
		||||
  "EpThemeColor": "#409EFF",
 | 
			
		||||
  "ShowLogo": true,
 | 
			
		||||
 | 
			
		||||
@ -43,3 +43,8 @@ export const getRoleList = (data?: object) => {
 | 
			
		||||
export const getDeptList = (data?: object) => {
 | 
			
		||||
  return http.request<Result>("post", "/dept", { data });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/** 获取菜单管理列表 */
 | 
			
		||||
export const getMenuList = (data?: object) => {
 | 
			
		||||
  return http.request<Result>("post", "/menu", { data });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| 
		 Before Width: | Height: | Size: 8.2 KiB After Width: | Height: | Size: 6.7 KiB  | 
@ -1,12 +1,19 @@
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { ref, computed } from "vue";
 | 
			
		||||
import { animates } from "./animate";
 | 
			
		||||
import { ref, computed, toRef } from "vue";
 | 
			
		||||
import { cloneDeep } from "@pureadmin/utils";
 | 
			
		||||
 | 
			
		||||
defineOptions({
 | 
			
		||||
  name: "ReAnimateSelector"
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const props = defineProps({
 | 
			
		||||
  placeholder: {
 | 
			
		||||
    type: String,
 | 
			
		||||
    default: "请选择动画"
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const inputValue = defineModel({ type: String });
 | 
			
		||||
 | 
			
		||||
const searchVal = ref();
 | 
			
		||||
@ -74,7 +81,7 @@ function onMouseleave() {
 | 
			
		||||
  <el-select
 | 
			
		||||
    clearable
 | 
			
		||||
    filterable
 | 
			
		||||
    placeholder="请选择动画"
 | 
			
		||||
    :placeholder="props.placeholder"
 | 
			
		||||
    popper-class="pure-animate-popper"
 | 
			
		||||
    :model-value="inputValue"
 | 
			
		||||
    :filter-method="filterMethod"
 | 
			
		||||
 | 
			
		||||
@ -90,7 +90,7 @@ function handleClose(
 | 
			
		||||
    v-model="options.visible"
 | 
			
		||||
    class="pure-dialog"
 | 
			
		||||
    :fullscreen="fullscreen ? true : options?.fullscreen ? true : false"
 | 
			
		||||
    @close="handleClose(options, index)"
 | 
			
		||||
    @closed="handleClose(options, index)"
 | 
			
		||||
    @opened="eventsCallBack('open', options, index)"
 | 
			
		||||
    @openAutoFocus="eventsCallBack('openAutoFocus', options, index)"
 | 
			
		||||
    @closeAutoFocus="eventsCallBack('closeAutoFocus', options, index)"
 | 
			
		||||
 | 
			
		||||
@ -3,7 +3,7 @@ import type { CSSProperties, VNode, Component } from "vue";
 | 
			
		||||
type DoneFn = (cancel?: boolean) => void;
 | 
			
		||||
type EventType = "open" | "close" | "openAutoFocus" | "closeAutoFocus";
 | 
			
		||||
type ArgsType = {
 | 
			
		||||
  /** `cancel` 点击取消按钮、`sure` 点击确定按钮、`close` 点击右上角关闭按钮或者空白页 */
 | 
			
		||||
  /** `cancel` 点击取消按钮、`sure` 点击确定按钮、`close` 点击右上角关闭按钮或空白页或按下了esc键 */
 | 
			
		||||
  command: "cancel" | "sure" | "close";
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -157,7 +157,7 @@ interface DialogOptions extends DialogProps {
 | 
			
		||||
    options: DialogOptions;
 | 
			
		||||
    index: number;
 | 
			
		||||
  }) => void;
 | 
			
		||||
  /** `Dialog` 关闭后的回调(只有点击右上角关闭按钮或者空白页关闭页面时才会触发) */
 | 
			
		||||
  /** `Dialog` 关闭后的回调(只有点击右上角关闭按钮或空白页或按下了esc键关闭页面时才会触发) */
 | 
			
		||||
  close?: ({
 | 
			
		||||
    options,
 | 
			
		||||
    index
 | 
			
		||||
@ -165,7 +165,7 @@ interface DialogOptions extends DialogProps {
 | 
			
		||||
    options: DialogOptions;
 | 
			
		||||
    index: number;
 | 
			
		||||
  }) => void;
 | 
			
		||||
  /** `Dialog` 关闭后的回调。 `args` 返回的 `command` 值解析:`cancel` 点击取消按钮、`sure` 点击确定按钮、`close` 点击右上角关闭按钮或者空白页  */
 | 
			
		||||
  /** `Dialog` 关闭后的回调。 `args` 返回的 `command` 值解析:`cancel` 点击取消按钮、`sure` 点击确定按钮、`close` 点击右上角关闭按钮或空白页或按下了esc键  */
 | 
			
		||||
  closeCallBack?: ({
 | 
			
		||||
    options,
 | 
			
		||||
    index,
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,7 @@
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { IconJson } from "@/components/ReIcon/data";
 | 
			
		||||
import { cloneDeep, isAllEmpty } from "@pureadmin/utils";
 | 
			
		||||
import { ref, computed, CSSProperties, toRef, watch } from "vue";
 | 
			
		||||
import { ref, computed, CSSProperties, watch } from "vue";
 | 
			
		||||
import Search from "@iconify-icons/ri/search-eye-line";
 | 
			
		||||
 | 
			
		||||
type ParameterCSSProperties = (item?: string) => CSSProperties | undefined;
 | 
			
		||||
 | 
			
		||||
@ -30,7 +30,7 @@ import Table from "@iconify-icons/ri/table-line";
 | 
			
		||||
import Info from "@iconify-icons/ri/file-info-line";
 | 
			
		||||
import Artboard from "@iconify-icons/ri/artboard-line";
 | 
			
		||||
addIcon("ubuntuFill", UbuntuFill);
 | 
			
		||||
addIcon("menu", Menu);
 | 
			
		||||
addIcon("ep:menu", Menu);
 | 
			
		||||
addIcon("edit", Edit);
 | 
			
		||||
addIcon("informationLine", InformationLine);
 | 
			
		||||
addIcon("setUp", SetUp);
 | 
			
		||||
@ -42,14 +42,14 @@ addIcon("listCheck", ListCheck);
 | 
			
		||||
addIcon("histogram", Histogram);
 | 
			
		||||
addIcon("ppt", Ppt);
 | 
			
		||||
addIcon("checkboxCircleLine", CheckboxCircleLine);
 | 
			
		||||
addIcon("flUser", FlUser);
 | 
			
		||||
addIcon("role", Role);
 | 
			
		||||
addIcon("setting", Setting);
 | 
			
		||||
addIcon("dept", Dept);
 | 
			
		||||
addIcon("ri:admin-line", FlUser);
 | 
			
		||||
addIcon("ri:admin-fill", Role);
 | 
			
		||||
addIcon("ri:settings-3-line", Setting);
 | 
			
		||||
addIcon("ri:git-branch-line", Dept);
 | 
			
		||||
addIcon("search", Search);
 | 
			
		||||
addIcon("lollipop", Lollipop);
 | 
			
		||||
addIcon("monitor", Monitor);
 | 
			
		||||
addIcon("tag", Tag);
 | 
			
		||||
addIcon("ep:lollipop", Lollipop);
 | 
			
		||||
addIcon("ep:monitor", Monitor);
 | 
			
		||||
addIcon("ri:bookmark-2-line", Tag);
 | 
			
		||||
addIcon("table", Table);
 | 
			
		||||
addIcon("info", Info);
 | 
			
		||||
addIcon("artboard", Artboard);
 | 
			
		||||
 | 
			
		||||
@ -29,6 +29,10 @@ const props = {
 | 
			
		||||
  columns: {
 | 
			
		||||
    type: Array as PropType<TableColumnList>,
 | 
			
		||||
    default: () => []
 | 
			
		||||
  },
 | 
			
		||||
  isExpandAll: {
 | 
			
		||||
    type: Boolean,
 | 
			
		||||
    default: true
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -38,10 +42,10 @@ export default defineComponent({
 | 
			
		||||
  emits: ["refresh"],
 | 
			
		||||
  setup(props, { emit, slots, attrs }) {
 | 
			
		||||
    const size = ref("default");
 | 
			
		||||
    const isExpandAll = ref(true);
 | 
			
		||||
    const loading = ref(false);
 | 
			
		||||
    const checkAll = ref(true);
 | 
			
		||||
    const isIndeterminate = ref(false);
 | 
			
		||||
    const isExpandAll = ref(props.isExpandAll);
 | 
			
		||||
    const filterColumns = cloneDeep(props?.columns).filter(column =>
 | 
			
		||||
      isBoolean(column?.hide)
 | 
			
		||||
        ? !column.hide
 | 
			
		||||
 | 
			
		||||
@ -12,7 +12,7 @@ export interface OptionsType {
 | 
			
		||||
  /** 图标属性、样式配置 */
 | 
			
		||||
  iconAttrs?: iconType;
 | 
			
		||||
  /** 值 */
 | 
			
		||||
  value?: string | number;
 | 
			
		||||
  value?: any;
 | 
			
		||||
  /** 是否禁用 */
 | 
			
		||||
  disabled?: boolean;
 | 
			
		||||
  /** `tooltip` 提示 */
 | 
			
		||||
 | 
			
		||||
@ -1,44 +1,8 @@
 | 
			
		||||
import { h, defineComponent } from "vue";
 | 
			
		||||
import TypeIt from "typeit";
 | 
			
		||||
import typeIt from "./src/index";
 | 
			
		||||
import type { TypeItOptions } from "typeit";
 | 
			
		||||
 | 
			
		||||
// 打字机效果组件(只是简单的封装下,更多配置项参考 https://www.typeitjs.com/docs/vanilla/usage#options)
 | 
			
		||||
export default defineComponent({
 | 
			
		||||
  name: "TypeIt",
 | 
			
		||||
  props: {
 | 
			
		||||
    /** 打字速度,以每一步之间的毫秒数为单位,默认`200` */
 | 
			
		||||
    speed: {
 | 
			
		||||
      type: Number,
 | 
			
		||||
      default: 200
 | 
			
		||||
    },
 | 
			
		||||
    values: {
 | 
			
		||||
      type: Array,
 | 
			
		||||
      defalut: []
 | 
			
		||||
    },
 | 
			
		||||
    className: {
 | 
			
		||||
      type: String,
 | 
			
		||||
      default: "type-it"
 | 
			
		||||
    },
 | 
			
		||||
    cursor: {
 | 
			
		||||
      type: Boolean,
 | 
			
		||||
      default: true
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  render() {
 | 
			
		||||
    return h(
 | 
			
		||||
      "span",
 | 
			
		||||
      {
 | 
			
		||||
        class: this.className
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        default: () => []
 | 
			
		||||
      }
 | 
			
		||||
    );
 | 
			
		||||
  },
 | 
			
		||||
  mounted() {
 | 
			
		||||
    new TypeIt(`.${this.className}`, {
 | 
			
		||||
      strings: this.values,
 | 
			
		||||
      speed: this.speed,
 | 
			
		||||
      cursor: this.cursor
 | 
			
		||||
    }).go();
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
const TypeIt = typeIt;
 | 
			
		||||
 | 
			
		||||
export { TypeIt, TypeItOptions };
 | 
			
		||||
 | 
			
		||||
export default TypeIt;
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										56
									
								
								src/components/ReTypeit/src/index.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								src/components/ReTypeit/src/index.tsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,56 @@
 | 
			
		||||
import type { El } from "typeit/dist/types";
 | 
			
		||||
import TypeIt, { type TypeItOptions } from "typeit";
 | 
			
		||||
import { ref, defineComponent, onMounted, type PropType } from "vue";
 | 
			
		||||
 | 
			
		||||
// 打字机效果组件(配置项详情请查阅 https://www.typeitjs.com/docs/vanilla/usage#options)
 | 
			
		||||
export default defineComponent({
 | 
			
		||||
  name: "TypeIt",
 | 
			
		||||
  props: {
 | 
			
		||||
    options: {
 | 
			
		||||
      type: Object as PropType<TypeItOptions>,
 | 
			
		||||
      default: () => ({}) as TypeItOptions
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  setup(props, { slots, expose }) {
 | 
			
		||||
    /**
 | 
			
		||||
     * 输出错误信息
 | 
			
		||||
     * @param message 错误信息
 | 
			
		||||
     */
 | 
			
		||||
    function throwError(message: string) {
 | 
			
		||||
      throw new TypeError(message);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取浏览器默认语言
 | 
			
		||||
     */
 | 
			
		||||
    function getBrowserLanguage() {
 | 
			
		||||
      return navigator.language;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const typedItRef = ref<Element | null>(null);
 | 
			
		||||
 | 
			
		||||
    onMounted(() => {
 | 
			
		||||
      const $typed = typedItRef.value!.querySelector(".type-it") as El;
 | 
			
		||||
 | 
			
		||||
      if (!$typed) {
 | 
			
		||||
        const errorMsg =
 | 
			
		||||
          getBrowserLanguage() === "zh-CN"
 | 
			
		||||
            ? "请确保有且只有一个具有class属性为 'type-it' 的元素"
 | 
			
		||||
            : "Please make sure that there is only one element with a Class attribute with 'type-it'";
 | 
			
		||||
        throwError(errorMsg);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      const typeIt = new TypeIt($typed, props.options).go();
 | 
			
		||||
 | 
			
		||||
      expose({
 | 
			
		||||
        typeIt
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    return () => (
 | 
			
		||||
      <div ref={typedItRef}>
 | 
			
		||||
        {slots.default?.() ?? <span class="type-it"></span>}
 | 
			
		||||
      </div>
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
@ -5,14 +5,16 @@ const TITLE = getConfig("Title");
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
  <footer class="layout-footer">
 | 
			
		||||
    MIT © 2020-PRESENT
 | 
			
		||||
  <footer
 | 
			
		||||
    class="layout-footer text-[rgba(0,0,0,0.6)] dark:text-[rgba(220,220,242,0.8)]"
 | 
			
		||||
  >
 | 
			
		||||
    Copyright © 2020-present
 | 
			
		||||
    <a
 | 
			
		||||
      class="ml-1 hover:text-primary"
 | 
			
		||||
      class="hover:text-primary"
 | 
			
		||||
      href="https://github.com/pure-admin"
 | 
			
		||||
      target="_blank"
 | 
			
		||||
    >
 | 
			
		||||
      {{ TITLE }}
 | 
			
		||||
       {{ TITLE }}
 | 
			
		||||
    </a>
 | 
			
		||||
  </footer>
 | 
			
		||||
</template>
 | 
			
		||||
@ -24,6 +26,6 @@ const TITLE = getConfig("Title");
 | 
			
		||||
  justify-content: center;
 | 
			
		||||
  width: 100%;
 | 
			
		||||
  padding: 0 0 8px;
 | 
			
		||||
  color: #c0c4cc;
 | 
			
		||||
  font-size: 14px;
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,7 @@ export interface ListItem {
 | 
			
		||||
  datetime: string;
 | 
			
		||||
  type: string;
 | 
			
		||||
  description: string;
 | 
			
		||||
  status?: "" | "success" | "warning" | "info" | "danger";
 | 
			
		||||
  status?: "primary" | "success" | "warning" | "info" | "danger";
 | 
			
		||||
  extra?: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -115,7 +115,7 @@ defineExpose({ handleScroll });
 | 
			
		||||
    cursor: pointer;
 | 
			
		||||
    border: 0.1px solid #ccc;
 | 
			
		||||
    border-radius: 4px;
 | 
			
		||||
    transition: all 0.3s;
 | 
			
		||||
    transition: font-size 0.16s;
 | 
			
		||||
 | 
			
		||||
    &-title {
 | 
			
		||||
      display: flex;
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,8 @@
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { ref, computed } from "vue";
 | 
			
		||||
import { computed } from "vue";
 | 
			
		||||
import { useGlobal } from "@pureadmin/utils";
 | 
			
		||||
import { useNav } from "@/layout/hooks/useNav";
 | 
			
		||||
 | 
			
		||||
import MenuFold from "@iconify-icons/ri/menu-fold-fill";
 | 
			
		||||
 | 
			
		||||
interface Props {
 | 
			
		||||
@ -12,7 +13,6 @@ const props = withDefaults(defineProps<Props>(), {
 | 
			
		||||
  isActive: false
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const visible = ref(false);
 | 
			
		||||
const { tooltipEffect } = useNav();
 | 
			
		||||
 | 
			
		||||
const iconClass = computed(() => {
 | 
			
		||||
@ -42,21 +42,18 @@ const toggleClick = () => {
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="collapse-container">
 | 
			
		||||
    <el-tooltip
 | 
			
		||||
      placement="right"
 | 
			
		||||
      :visible="visible"
 | 
			
		||||
      :effect="tooltipEffect"
 | 
			
		||||
      :content="props.isActive ? '点击折叠' : '点击展开'"
 | 
			
		||||
    >
 | 
			
		||||
      <IconifyIconOffline
 | 
			
		||||
        :icon="MenuFold"
 | 
			
		||||
        :class="[iconClass, themeColor === 'light' ? '' : 'text-primary']"
 | 
			
		||||
        :style="{ transform: props.isActive ? 'none' : 'rotateY(180deg)' }"
 | 
			
		||||
        @click="toggleClick"
 | 
			
		||||
        @mouseenter="visible = true"
 | 
			
		||||
        @mouseleave="visible = false"
 | 
			
		||||
      />
 | 
			
		||||
    </el-tooltip>
 | 
			
		||||
    <IconifyIconOffline
 | 
			
		||||
      v-tippy="{
 | 
			
		||||
        content: props.isActive ? '点击折叠' : '点击展开',
 | 
			
		||||
        theme: tooltipEffect,
 | 
			
		||||
        hideOnClick: 'toggle',
 | 
			
		||||
        placement: 'right'
 | 
			
		||||
      }"
 | 
			
		||||
      :icon="MenuFold"
 | 
			
		||||
      :class="[iconClass, themeColor === 'light' ? '' : 'text-primary']"
 | 
			
		||||
      :style="{ transform: props.isActive ? 'none' : 'rotateY(180deg)' }"
 | 
			
		||||
      @click="toggleClick"
 | 
			
		||||
    />
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -3,7 +3,6 @@ import path from "path";
 | 
			
		||||
import { getConfig } from "@/config";
 | 
			
		||||
import { menuType } from "../../types";
 | 
			
		||||
import extraIcon from "./extraIcon.vue";
 | 
			
		||||
import { useDark } from "@pureadmin/utils";
 | 
			
		||||
import { ReText } from "@/components/ReText";
 | 
			
		||||
import { useNav } from "@/layout/hooks/useNav";
 | 
			
		||||
import { transformI18n } from "@/plugins/i18n";
 | 
			
		||||
@ -16,7 +15,6 @@ import ArrowLeft from "@iconify-icons/ep/arrow-left-bold";
 | 
			
		||||
import ArrowRight from "@iconify-icons/ep/arrow-right-bold";
 | 
			
		||||
 | 
			
		||||
const { layout, isCollapse, tooltipEffect, getDivStyle } = useNav();
 | 
			
		||||
const { isDark } = useDark();
 | 
			
		||||
 | 
			
		||||
const props = defineProps({
 | 
			
		||||
  item: {
 | 
			
		||||
@ -143,7 +141,7 @@ function resolvePath(routePath) {
 | 
			
		||||
        <ReText
 | 
			
		||||
          :tippyProps="{
 | 
			
		||||
            offset: [0, -10],
 | 
			
		||||
            theme: !isDark ? tooltipEffect : undefined
 | 
			
		||||
            theme: tooltipEffect
 | 
			
		||||
          }"
 | 
			
		||||
          class="!text-inherit"
 | 
			
		||||
        >
 | 
			
		||||
@ -181,7 +179,7 @@ function resolvePath(routePath) {
 | 
			
		||||
        "
 | 
			
		||||
        :tippyProps="{
 | 
			
		||||
          offset: [0, -10],
 | 
			
		||||
          theme: !isDark ? tooltipEffect : undefined
 | 
			
		||||
          theme: tooltipEffect
 | 
			
		||||
        }"
 | 
			
		||||
        :class="{
 | 
			
		||||
          '!text-inherit': true,
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
// 完整版菜单比较多,将 rank 抽离出来,在此方便维护
 | 
			
		||||
 | 
			
		||||
const home = 0, // 平台规定只有 home 路由的 rank 才能为 0 ,所以后端在返回 rank 的时候需要从 1 开始哦
 | 
			
		||||
const home = 0, // 平台规定只有 home 路由的 rank 才能为 0 ,所以后端在返回 rank 的时候需要从非 0 开始
 | 
			
		||||
  components = 1,
 | 
			
		||||
  able = 2,
 | 
			
		||||
  table = 3,
 | 
			
		||||
 | 
			
		||||
@ -5,7 +5,7 @@ export default {
 | 
			
		||||
  path: "/components",
 | 
			
		||||
  redirect: "/components/dialog",
 | 
			
		||||
  meta: {
 | 
			
		||||
    icon: "menu",
 | 
			
		||||
    icon: "ep:menu",
 | 
			
		||||
    title: $t("menus.hscomponents"),
 | 
			
		||||
    rank: components
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
@ -44,15 +44,23 @@
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.pure-dialog {
 | 
			
		||||
  .el-dialog__header.show-close {
 | 
			
		||||
    padding-right: 16px;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .el-dialog__headerbtn {
 | 
			
		||||
    top: 16px;
 | 
			
		||||
    right: 12px;
 | 
			
		||||
    width: 24px;
 | 
			
		||||
    height: 24px;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .pure-dialog-svg {
 | 
			
		||||
    color: var(--el-color-info);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .el-dialog__headerbtn {
 | 
			
		||||
    top: 20px;
 | 
			
		||||
    right: 14px;
 | 
			
		||||
    width: 24px;
 | 
			
		||||
    height: 24px;
 | 
			
		||||
  .el-dialog__footer {
 | 
			
		||||
    padding-top: 0;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -148,17 +156,14 @@
 | 
			
		||||
    display: none;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .el-dialog__body {
 | 
			
		||||
    padding-top: 12px;
 | 
			
		||||
    padding-bottom: 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .el-input__inner {
 | 
			
		||||
    font-size: 1.2em;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .el-dialog__footer {
 | 
			
		||||
    padding-bottom: 10px;
 | 
			
		||||
    width: calc(100% + 32px);
 | 
			
		||||
    padding: 10px 20px;
 | 
			
		||||
    margin: auto -16px -16px;
 | 
			
		||||
    box-shadow:
 | 
			
		||||
      0 -1px 0 0 #e0e3e8,
 | 
			
		||||
      0 -3px 6px 0 rgb(69 98 155 / 12%);
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,7 @@
 | 
			
		||||
.wave {
 | 
			
		||||
  position: fixed;
 | 
			
		||||
  height: 100%;
 | 
			
		||||
  width: 80%;
 | 
			
		||||
  left: 0;
 | 
			
		||||
  bottom: 0;
 | 
			
		||||
  z-index: -1;
 | 
			
		||||
 | 
			
		||||
@ -1,9 +1,13 @@
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import TypeIt from "@/components/ReTypeit";
 | 
			
		||||
import { TypeIt, type TypeItOptions } from "@/components/ReTypeit";
 | 
			
		||||
 | 
			
		||||
defineOptions({
 | 
			
		||||
  name: "Typeit"
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const options: TypeItOptions = {
 | 
			
		||||
  strings: ["test1", "test2", "test3"]
 | 
			
		||||
};
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
@ -13,6 +17,6 @@ defineOptions({
 | 
			
		||||
        <span class="font-medium"> 打字机组件 </span>
 | 
			
		||||
      </div>
 | 
			
		||||
    </template>
 | 
			
		||||
    <TypeIt :values="['test1', 'test2', 'test3']" />
 | 
			
		||||
    <TypeIt :options="options" />
 | 
			
		||||
  </el-card>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
@ -6,10 +6,10 @@ defineOptions({
 | 
			
		||||
  name: "AnimateCss"
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const icon = ref("");
 | 
			
		||||
const animate = ref("");
 | 
			
		||||
 | 
			
		||||
watch(icon, () => {
 | 
			
		||||
  console.log("icon", icon.value);
 | 
			
		||||
watch(animate, () => {
 | 
			
		||||
  console.log("animate", animate.value);
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
@ -29,6 +29,6 @@ watch(icon, () => {
 | 
			
		||||
        </span>
 | 
			
		||||
      </div>
 | 
			
		||||
    </template>
 | 
			
		||||
    <ReAnimateSelector v-model="icon" class="!w-[200px]" />
 | 
			
		||||
    <ReAnimateSelector v-model="animate" class="!w-[200px]" />
 | 
			
		||||
  </el-card>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
@ -203,7 +203,7 @@ function onCloseCallBackClick() {
 | 
			
		||||
      } else if (args?.command === "sure") {
 | 
			
		||||
        text = "您点击了确定按钮";
 | 
			
		||||
      } else {
 | 
			
		||||
        text = "您点击了右上角关闭按钮或者空白页";
 | 
			
		||||
        text = "您点击了右上角关闭按钮或空白页或按下了esc键";
 | 
			
		||||
      }
 | 
			
		||||
      message(text);
 | 
			
		||||
    },
 | 
			
		||||
@ -301,7 +301,9 @@ function onFormOneClick() {
 | 
			
		||||
      } else if (args?.command === "sure") {
 | 
			
		||||
        message(`您点击了确定按钮,当前表单数据为 ${text}`);
 | 
			
		||||
      } else {
 | 
			
		||||
        message(`您点击了右上角关闭按钮或者空白页,当前表单数据为 ${text}`);
 | 
			
		||||
        message(
 | 
			
		||||
          `您点击了右上角关闭按钮或空白页或按下了esc键,当前表单数据为 ${text}`
 | 
			
		||||
        );
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
@ -12,8 +12,8 @@ const checked2 = ref(false);
 | 
			
		||||
const baseTag = ref("dark");
 | 
			
		||||
const tagList = ref([
 | 
			
		||||
  {
 | 
			
		||||
    type: "",
 | 
			
		||||
    text: "Default"
 | 
			
		||||
    type: "primary",
 | 
			
		||||
    text: "Primary"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    type: "success",
 | 
			
		||||
 | 
			
		||||
@ -166,7 +166,9 @@ watch(loginDay, value => {
 | 
			
		||||
          <avatar class="avatar" />
 | 
			
		||||
          <Motion>
 | 
			
		||||
            <h2 class="outline-none">
 | 
			
		||||
              <TypeIt :values="[title]" :cursor="false" :speed="150" />
 | 
			
		||||
              <TypeIt
 | 
			
		||||
                :options="{ strings: [title], cursor: false, speed: 100 }"
 | 
			
		||||
              />
 | 
			
		||||
            </h2>
 | 
			
		||||
          </Motion>
 | 
			
		||||
 | 
			
		||||
@ -321,6 +323,18 @@ watch(loginDay, value => {
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div
 | 
			
		||||
      class="w-full flex-c absolute bottom-3 text-sm text-[rgba(0,0,0,0.6)] dark:text-[rgba(220,220,242,0.8)]"
 | 
			
		||||
    >
 | 
			
		||||
      Copyright © 2020-present
 | 
			
		||||
      <a
 | 
			
		||||
        class="hover:text-primary"
 | 
			
		||||
        href="https://github.com/pure-admin"
 | 
			
		||||
        target="_blank"
 | 
			
		||||
      >
 | 
			
		||||
         {{ title }}
 | 
			
		||||
      </a>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -15,19 +15,26 @@ defineOptions({
 | 
			
		||||
        <div class="card-header">组件方式判断权限</div>
 | 
			
		||||
      </template>
 | 
			
		||||
      <el-space wrap>
 | 
			
		||||
        <Auth value="btn_add">
 | 
			
		||||
        <Auth value="permission:btn:add">
 | 
			
		||||
          <el-button plain type="warning">
 | 
			
		||||
            拥有code:'btn_add' 权限可见
 | 
			
		||||
            拥有code:'permission:btn:add' 权限可见
 | 
			
		||||
          </el-button>
 | 
			
		||||
        </Auth>
 | 
			
		||||
        <Auth :value="['btn_edit']">
 | 
			
		||||
        <Auth :value="['permission:btn:edit']">
 | 
			
		||||
          <el-button plain type="primary">
 | 
			
		||||
            拥有code:['btn_edit'] 权限可见
 | 
			
		||||
            拥有code:['permission:btn:edit'] 权限可见
 | 
			
		||||
          </el-button>
 | 
			
		||||
        </Auth>
 | 
			
		||||
        <Auth :value="['btn_add', 'btn_edit', 'btn_delete']">
 | 
			
		||||
        <Auth
 | 
			
		||||
          :value="[
 | 
			
		||||
            'permission:btn:add',
 | 
			
		||||
            'permission:btn:edit',
 | 
			
		||||
            'permission:btn:delete'
 | 
			
		||||
          ]"
 | 
			
		||||
        >
 | 
			
		||||
          <el-button plain type="danger">
 | 
			
		||||
            拥有code:['btn_add', 'btn_edit', 'btn_delete'] 权限可见
 | 
			
		||||
            拥有code:['permission:btn:add', 'permission:btn:edit',
 | 
			
		||||
            'permission:btn:delete'] 权限可见
 | 
			
		||||
          </el-button>
 | 
			
		||||
        </Auth>
 | 
			
		||||
      </el-space>
 | 
			
		||||
@ -38,18 +45,25 @@ defineOptions({
 | 
			
		||||
        <div class="card-header">函数方式判断权限</div>
 | 
			
		||||
      </template>
 | 
			
		||||
      <el-space wrap>
 | 
			
		||||
        <el-button v-if="hasAuth('btn_add')" plain type="warning">
 | 
			
		||||
          拥有code:'btn_add' 权限可见
 | 
			
		||||
        <el-button v-if="hasAuth('permission:btn:add')" plain type="warning">
 | 
			
		||||
          拥有code:'permission:btn:add' 权限可见
 | 
			
		||||
        </el-button>
 | 
			
		||||
        <el-button v-if="hasAuth(['btn_edit'])" plain type="primary">
 | 
			
		||||
          拥有code:['btn_edit'] 权限可见
 | 
			
		||||
        <el-button v-if="hasAuth(['permission:btn:edit'])" plain type="primary">
 | 
			
		||||
          拥有code:['permission:btn:edit'] 权限可见
 | 
			
		||||
        </el-button>
 | 
			
		||||
        <el-button
 | 
			
		||||
          v-if="hasAuth(['btn_add', 'btn_edit', 'btn_delete'])"
 | 
			
		||||
          v-if="
 | 
			
		||||
            hasAuth([
 | 
			
		||||
              'permission:btn:add',
 | 
			
		||||
              'permission:btn:edit',
 | 
			
		||||
              'permission:btn:delete'
 | 
			
		||||
            ])
 | 
			
		||||
          "
 | 
			
		||||
          plain
 | 
			
		||||
          type="danger"
 | 
			
		||||
        >
 | 
			
		||||
          拥有code:['btn_add', 'btn_edit', 'btn_delete'] 权限可见
 | 
			
		||||
          拥有code:['permission:btn:add', 'permission:btn:edit',
 | 
			
		||||
          'permission:btn:delete'] 权限可见
 | 
			
		||||
        </el-button>
 | 
			
		||||
      </el-space>
 | 
			
		||||
    </el-card>
 | 
			
		||||
@ -61,18 +75,23 @@ defineOptions({
 | 
			
		||||
        </div>
 | 
			
		||||
      </template>
 | 
			
		||||
      <el-space wrap>
 | 
			
		||||
        <el-button v-auth="'btn_add'" plain type="warning">
 | 
			
		||||
          拥有code:'btn_add' 权限可见
 | 
			
		||||
        <el-button v-auth="'permission:btn:add'" plain type="warning">
 | 
			
		||||
          拥有code:'permission:btn:add' 权限可见
 | 
			
		||||
        </el-button>
 | 
			
		||||
        <el-button v-auth="['btn_edit']" plain type="primary">
 | 
			
		||||
          拥有code:['btn_edit'] 权限可见
 | 
			
		||||
        <el-button v-auth="['permission:btn:edit']" plain type="primary">
 | 
			
		||||
          拥有code:['permission:btn:edit'] 权限可见
 | 
			
		||||
        </el-button>
 | 
			
		||||
        <el-button
 | 
			
		||||
          v-auth="['btn_add', 'btn_edit', 'btn_delete']"
 | 
			
		||||
          v-auth="[
 | 
			
		||||
            'permission:btn:add',
 | 
			
		||||
            'permission:btn:edit',
 | 
			
		||||
            'permission:btn:delete'
 | 
			
		||||
          ]"
 | 
			
		||||
          plain
 | 
			
		||||
          type="danger"
 | 
			
		||||
        >
 | 
			
		||||
          拥有code:['btn_add', 'btn_edit', 'btn_delete'] 权限可见
 | 
			
		||||
          拥有code:['permission:btn:add', 'permission:btn:edit',
 | 
			
		||||
          'permission:btn:delete'] 权限可见
 | 
			
		||||
        </el-button>
 | 
			
		||||
      </el-space>
 | 
			
		||||
    </el-card>
 | 
			
		||||
 | 
			
		||||
@ -95,7 +95,10 @@ const tableData = [
 | 
			
		||||
      :columns="columns"
 | 
			
		||||
    >
 | 
			
		||||
      <template #tag="{ row }">
 | 
			
		||||
        <el-tag :type="row.tag === 'Home' ? '' : 'success'" disable-transitions>
 | 
			
		||||
        <el-tag
 | 
			
		||||
          :type="row.tag === 'Home' ? null : 'success'"
 | 
			
		||||
          disable-transitions
 | 
			
		||||
        >
 | 
			
		||||
          {{ row.tag }}
 | 
			
		||||
        </el-tag>
 | 
			
		||||
      </template>
 | 
			
		||||
 | 
			
		||||
@ -89,7 +89,7 @@ const {
 | 
			
		||||
        <pure-table
 | 
			
		||||
          ref="tableRef"
 | 
			
		||||
          adaptive
 | 
			
		||||
          :adaptiveConfig="{ offsetBottom: 32 }"
 | 
			
		||||
          :adaptiveConfig="{ offsetBottom: 45 }"
 | 
			
		||||
          align-whole="center"
 | 
			
		||||
          row-key="id"
 | 
			
		||||
          showOverflowTooltip
 | 
			
		||||
@ -111,20 +111,20 @@ const {
 | 
			
		||||
              link
 | 
			
		||||
              type="primary"
 | 
			
		||||
              :size="size"
 | 
			
		||||
              :icon="useRenderIcon(AddFill)"
 | 
			
		||||
              @click="openDialog('新增', { parentId: row.id } as any)"
 | 
			
		||||
              :icon="useRenderIcon(EditPen)"
 | 
			
		||||
              @click="openDialog('修改', row)"
 | 
			
		||||
            >
 | 
			
		||||
              新增
 | 
			
		||||
              修改
 | 
			
		||||
            </el-button>
 | 
			
		||||
            <el-button
 | 
			
		||||
              class="reset-margin"
 | 
			
		||||
              link
 | 
			
		||||
              type="primary"
 | 
			
		||||
              :size="size"
 | 
			
		||||
              :icon="useRenderIcon(EditPen)"
 | 
			
		||||
              @click="openDialog('修改', row)"
 | 
			
		||||
              :icon="useRenderIcon(AddFill)"
 | 
			
		||||
              @click="openDialog('新增', { parentId: row.id } as any)"
 | 
			
		||||
            >
 | 
			
		||||
              修改
 | 
			
		||||
              新增
 | 
			
		||||
            </el-button>
 | 
			
		||||
            <el-popconfirm
 | 
			
		||||
              :title="`是否确认删除部门名称为${row.name}的这条数据`"
 | 
			
		||||
@ -150,6 +150,14 @@ const {
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
:deep(.el-table__inner-wrapper::before) {
 | 
			
		||||
  height: 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.main-content {
 | 
			
		||||
  margin: 24px 24px 0 !important;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.search-form {
 | 
			
		||||
  :deep(.el-form-item) {
 | 
			
		||||
    margin-bottom: 12px;
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										27
									
								
								src/views/system/menu/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/views/system/menu/README.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,27 @@
 | 
			
		||||
<!-- 初版,持续完善中 -->
 | 
			
		||||
 | 
			
		||||
## 字段含义
 | 
			
		||||
 | 
			
		||||
| 字段              | 说明                                                         |
 | 
			
		||||
| :---------------- | :----------------------------------------------------------- |
 | 
			
		||||
| `menuType`        | 菜单类型(`0`代表菜单、`1`代表`iframe`、`2`代表外链、`3`代表按钮) |
 | 
			
		||||
| `parentId`        |                                                              |
 | 
			
		||||
| `title`           | 菜单名称(兼容国际化、非国际化,如果用国际化的写法就必须在根目录的`locales`文件夹下对应添加) |
 | 
			
		||||
| `name`            | 路由名称(必须唯一并且和当前路由`component`字段对应的页面里用`defineOptions`包起来的`name`保持一致) |
 | 
			
		||||
| `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)) |
 | 
			
		||||
| `redirect`        | 路由重定向                                                   |
 | 
			
		||||
| `icon`            | 菜单图标                                                     |
 | 
			
		||||
| `extraIcon`       | 右侧图标                                                     |
 | 
			
		||||
| `enterTransition` | 进场动画(页面加载动画)                                     |
 | 
			
		||||
| `leaveTransition` | 离场动画(页面加载动画)                                     |
 | 
			
		||||
| `activePath`      | 菜单激活(将某个菜单激活,主要用于通过`query`或`params`传参的路由,当它们通过配置`showLink: false`后不在菜单中显示,就不会有任何菜单高亮,而通过设置`activePath`指定激活菜单即可获得高亮,`activePath`为指定激活菜单的`path`) |
 | 
			
		||||
| `auths`           | 权限标识(按钮级别权限设置)                                 |
 | 
			
		||||
| `frameSrc`        | 链接地址(需要内嵌的`iframe`链接地址)                       |
 | 
			
		||||
| `frameLoading`    | 加载动画(内嵌的`iframe`页面是否开启首次加载动画)           |
 | 
			
		||||
| `keepAlive`       | 缓存页面(是否缓存该路由页面,开启后会保存该页面的整体状态,刷新后会清空状态) |
 | 
			
		||||
| `hiddenTag`       | 标签页(当前菜单名称或自定义信息禁止添加到标签页)           |
 | 
			
		||||
| `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)) |
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										326
									
								
								src/views/system/menu/form.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										326
									
								
								src/views/system/menu/form.vue
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,326 @@
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { ref } from "vue";
 | 
			
		||||
import ReCol from "@/components/ReCol";
 | 
			
		||||
import { formRules } from "./utils/rule";
 | 
			
		||||
import { FormProps } from "./utils/types";
 | 
			
		||||
import { transformI18n } from "@/plugins/i18n";
 | 
			
		||||
import { IconSelect } from "@/components/ReIcon";
 | 
			
		||||
import Segmented from "@/components/ReSegmented";
 | 
			
		||||
import ReAnimateSelector from "@/components/ReAnimateSelector";
 | 
			
		||||
import {
 | 
			
		||||
  menuTypeOptions,
 | 
			
		||||
  showLinkOptions,
 | 
			
		||||
  keepAliveOptions,
 | 
			
		||||
  hiddenTagOptions,
 | 
			
		||||
  showParentOptions,
 | 
			
		||||
  frameLoadingOptions
 | 
			
		||||
} from "./utils/enums";
 | 
			
		||||
 | 
			
		||||
const props = withDefaults(defineProps<FormProps>(), {
 | 
			
		||||
  formInline: () => ({
 | 
			
		||||
    menuType: 0,
 | 
			
		||||
    higherMenuOptions: [],
 | 
			
		||||
    parentId: 0,
 | 
			
		||||
    title: "",
 | 
			
		||||
    name: "",
 | 
			
		||||
    path: "",
 | 
			
		||||
    component: "",
 | 
			
		||||
    rank: 99,
 | 
			
		||||
    redirect: " ",
 | 
			
		||||
    icon: "",
 | 
			
		||||
    extraIcon: "",
 | 
			
		||||
    enterTransition: "",
 | 
			
		||||
    leaveTransition: "",
 | 
			
		||||
    activePath: "",
 | 
			
		||||
    auths: "",
 | 
			
		||||
    frameSrc: "",
 | 
			
		||||
    frameLoading: true,
 | 
			
		||||
    keepAlive: false,
 | 
			
		||||
    hiddenTag: false,
 | 
			
		||||
    showLink: true,
 | 
			
		||||
    showParent: false
 | 
			
		||||
  })
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const ruleFormRef = ref();
 | 
			
		||||
const newFormInline = ref(props.formInline);
 | 
			
		||||
 | 
			
		||||
function getRef() {
 | 
			
		||||
  return ruleFormRef.value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
defineExpose({ getRef });
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
  <el-form
 | 
			
		||||
    ref="ruleFormRef"
 | 
			
		||||
    :model="newFormInline"
 | 
			
		||||
    :rules="formRules"
 | 
			
		||||
    label-width="82px"
 | 
			
		||||
  >
 | 
			
		||||
    <el-row :gutter="30">
 | 
			
		||||
      <re-col>
 | 
			
		||||
        <el-form-item label="菜单类型">
 | 
			
		||||
          <Segmented
 | 
			
		||||
            v-model="newFormInline.menuType"
 | 
			
		||||
            :options="menuTypeOptions"
 | 
			
		||||
          />
 | 
			
		||||
        </el-form-item>
 | 
			
		||||
      </re-col>
 | 
			
		||||
 | 
			
		||||
      <re-col>
 | 
			
		||||
        <el-form-item label="上级菜单">
 | 
			
		||||
          <el-cascader
 | 
			
		||||
            v-model="newFormInline.parentId"
 | 
			
		||||
            class="w-full"
 | 
			
		||||
            :options="newFormInline.higherMenuOptions"
 | 
			
		||||
            :props="{
 | 
			
		||||
              value: 'id',
 | 
			
		||||
              label: 'title',
 | 
			
		||||
              emitPath: false,
 | 
			
		||||
              checkStrictly: true
 | 
			
		||||
            }"
 | 
			
		||||
            clearable
 | 
			
		||||
            filterable
 | 
			
		||||
            placeholder="请选择上级菜单"
 | 
			
		||||
          >
 | 
			
		||||
            <template #default="{ node, data }">
 | 
			
		||||
              <span>{{ transformI18n(data.title) }}</span>
 | 
			
		||||
              <span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
 | 
			
		||||
            </template>
 | 
			
		||||
          </el-cascader>
 | 
			
		||||
        </el-form-item>
 | 
			
		||||
      </re-col>
 | 
			
		||||
 | 
			
		||||
      <re-col :value="12" :xs="24" :sm="24">
 | 
			
		||||
        <el-form-item label="菜单名称" prop="title">
 | 
			
		||||
          <el-input
 | 
			
		||||
            v-model="newFormInline.title"
 | 
			
		||||
            clearable
 | 
			
		||||
            placeholder="请输入菜单名称"
 | 
			
		||||
          />
 | 
			
		||||
        </el-form-item>
 | 
			
		||||
      </re-col>
 | 
			
		||||
      <re-col v-if="newFormInline.menuType !== 3" :value="12" :xs="24" :sm="24">
 | 
			
		||||
        <el-form-item label="路由名称" prop="name">
 | 
			
		||||
          <el-input
 | 
			
		||||
            v-model="newFormInline.name"
 | 
			
		||||
            clearable
 | 
			
		||||
            placeholder="请输入路由名称"
 | 
			
		||||
          />
 | 
			
		||||
        </el-form-item>
 | 
			
		||||
      </re-col>
 | 
			
		||||
 | 
			
		||||
      <re-col v-if="newFormInline.menuType !== 3" :value="12" :xs="24" :sm="24">
 | 
			
		||||
        <el-form-item label="路由路径" prop="path">
 | 
			
		||||
          <el-input
 | 
			
		||||
            v-model="newFormInline.path"
 | 
			
		||||
            clearable
 | 
			
		||||
            placeholder="请输入路由路径"
 | 
			
		||||
          />
 | 
			
		||||
        </el-form-item>
 | 
			
		||||
      </re-col>
 | 
			
		||||
      <re-col
 | 
			
		||||
        v-show="newFormInline.menuType === 0"
 | 
			
		||||
        :value="12"
 | 
			
		||||
        :xs="24"
 | 
			
		||||
        :sm="24"
 | 
			
		||||
      >
 | 
			
		||||
        <el-form-item label="组件路径">
 | 
			
		||||
          <el-input
 | 
			
		||||
            v-model="newFormInline.component"
 | 
			
		||||
            clearable
 | 
			
		||||
            placeholder="请输入组件路径"
 | 
			
		||||
          />
 | 
			
		||||
        </el-form-item>
 | 
			
		||||
      </re-col>
 | 
			
		||||
 | 
			
		||||
      <re-col :value="12" :xs="24" :sm="24">
 | 
			
		||||
        <el-form-item label="菜单排序">
 | 
			
		||||
          <el-input-number
 | 
			
		||||
            v-model="newFormInline.rank"
 | 
			
		||||
            class="!w-full"
 | 
			
		||||
            :min="1"
 | 
			
		||||
            :max="9999"
 | 
			
		||||
            controls-position="right"
 | 
			
		||||
          />
 | 
			
		||||
        </el-form-item>
 | 
			
		||||
      </re-col>
 | 
			
		||||
      <re-col
 | 
			
		||||
        v-show="newFormInline.menuType === 0"
 | 
			
		||||
        :value="12"
 | 
			
		||||
        :xs="24"
 | 
			
		||||
        :sm="24"
 | 
			
		||||
      >
 | 
			
		||||
        <el-form-item label="路由重定向">
 | 
			
		||||
          <el-input
 | 
			
		||||
            v-model="newFormInline.redirect"
 | 
			
		||||
            clearable
 | 
			
		||||
            placeholder="请输入默认跳转地址"
 | 
			
		||||
          />
 | 
			
		||||
        </el-form-item>
 | 
			
		||||
      </re-col>
 | 
			
		||||
 | 
			
		||||
      <re-col
 | 
			
		||||
        v-show="newFormInline.menuType !== 3"
 | 
			
		||||
        :value="12"
 | 
			
		||||
        :xs="24"
 | 
			
		||||
        :sm="24"
 | 
			
		||||
      >
 | 
			
		||||
        <el-form-item label="菜单图标">
 | 
			
		||||
          <IconSelect v-model="newFormInline.icon" class="w-full" />
 | 
			
		||||
        </el-form-item>
 | 
			
		||||
      </re-col>
 | 
			
		||||
      <re-col
 | 
			
		||||
        v-show="newFormInline.menuType !== 3"
 | 
			
		||||
        :value="12"
 | 
			
		||||
        :xs="24"
 | 
			
		||||
        :sm="24"
 | 
			
		||||
      >
 | 
			
		||||
        <el-form-item label="右侧图标">
 | 
			
		||||
          <el-input
 | 
			
		||||
            v-model="newFormInline.extraIcon"
 | 
			
		||||
            clearable
 | 
			
		||||
            placeholder="菜单名称右侧的额外图标"
 | 
			
		||||
          />
 | 
			
		||||
        </el-form-item>
 | 
			
		||||
      </re-col>
 | 
			
		||||
 | 
			
		||||
      <re-col v-show="newFormInline.menuType < 2" :value="12" :xs="24" :sm="24">
 | 
			
		||||
        <el-form-item label="进场动画">
 | 
			
		||||
          <ReAnimateSelector
 | 
			
		||||
            v-model="newFormInline.enterTransition"
 | 
			
		||||
            placeholder="请选择页面进场加载动画"
 | 
			
		||||
          />
 | 
			
		||||
        </el-form-item>
 | 
			
		||||
      </re-col>
 | 
			
		||||
      <re-col v-show="newFormInline.menuType < 2" :value="12" :xs="24" :sm="24">
 | 
			
		||||
        <el-form-item label="离场动画">
 | 
			
		||||
          <ReAnimateSelector
 | 
			
		||||
            v-model="newFormInline.leaveTransition"
 | 
			
		||||
            placeholder="请选择页面离场加载动画"
 | 
			
		||||
          />
 | 
			
		||||
        </el-form-item>
 | 
			
		||||
      </re-col>
 | 
			
		||||
 | 
			
		||||
      <re-col
 | 
			
		||||
        v-show="newFormInline.menuType === 0"
 | 
			
		||||
        :value="12"
 | 
			
		||||
        :xs="24"
 | 
			
		||||
        :sm="24"
 | 
			
		||||
      >
 | 
			
		||||
        <el-form-item label="菜单激活">
 | 
			
		||||
          <el-input
 | 
			
		||||
            v-model="newFormInline.activePath"
 | 
			
		||||
            clearable
 | 
			
		||||
            placeholder="请输入需要激活的菜单"
 | 
			
		||||
          />
 | 
			
		||||
        </el-form-item>
 | 
			
		||||
      </re-col>
 | 
			
		||||
      <re-col v-if="newFormInline.menuType === 3" :value="12" :xs="24" :sm="24">
 | 
			
		||||
        <!-- 按钮级别权限设置 -->
 | 
			
		||||
        <el-form-item label="权限标识" prop="auths">
 | 
			
		||||
          <el-input
 | 
			
		||||
            v-model="newFormInline.auths"
 | 
			
		||||
            clearable
 | 
			
		||||
            placeholder="请输入权限标识"
 | 
			
		||||
          />
 | 
			
		||||
        </el-form-item>
 | 
			
		||||
      </re-col>
 | 
			
		||||
 | 
			
		||||
      <re-col
 | 
			
		||||
        v-show="newFormInline.menuType === 1"
 | 
			
		||||
        :value="12"
 | 
			
		||||
        :xs="24"
 | 
			
		||||
        :sm="24"
 | 
			
		||||
      >
 | 
			
		||||
        <!-- iframe -->
 | 
			
		||||
        <el-form-item label="链接地址">
 | 
			
		||||
          <el-input
 | 
			
		||||
            v-model="newFormInline.frameSrc"
 | 
			
		||||
            clearable
 | 
			
		||||
            placeholder="请输入 iframe 链接地址"
 | 
			
		||||
          />
 | 
			
		||||
        </el-form-item>
 | 
			
		||||
      </re-col>
 | 
			
		||||
      <re-col v-if="newFormInline.menuType === 1" :value="12" :xs="24" :sm="24">
 | 
			
		||||
        <el-form-item label="加载动画">
 | 
			
		||||
          <Segmented
 | 
			
		||||
            :modelValue="newFormInline.frameLoading ? 0 : 1"
 | 
			
		||||
            :options="frameLoadingOptions"
 | 
			
		||||
            @change="
 | 
			
		||||
              ({ option: { value } }) => {
 | 
			
		||||
                newFormInline.frameLoading = value;
 | 
			
		||||
              }
 | 
			
		||||
            "
 | 
			
		||||
          />
 | 
			
		||||
        </el-form-item>
 | 
			
		||||
      </re-col>
 | 
			
		||||
 | 
			
		||||
      <re-col v-show="newFormInline.menuType < 2" :value="12" :xs="24" :sm="24">
 | 
			
		||||
        <el-form-item label="缓存页面">
 | 
			
		||||
          <Segmented
 | 
			
		||||
            :modelValue="newFormInline.keepAlive ? 0 : 1"
 | 
			
		||||
            :options="keepAliveOptions"
 | 
			
		||||
            @change="
 | 
			
		||||
              ({ option: { value } }) => {
 | 
			
		||||
                newFormInline.keepAlive = value;
 | 
			
		||||
              }
 | 
			
		||||
            "
 | 
			
		||||
          />
 | 
			
		||||
        </el-form-item>
 | 
			
		||||
      </re-col>
 | 
			
		||||
      <re-col v-show="newFormInline.menuType < 2" :value="12" :xs="24" :sm="24">
 | 
			
		||||
        <el-form-item label="标签页">
 | 
			
		||||
          <Segmented
 | 
			
		||||
            :modelValue="newFormInline.hiddenTag ? 1 : 0"
 | 
			
		||||
            :options="hiddenTagOptions"
 | 
			
		||||
            @change="
 | 
			
		||||
              ({ option: { value } }) => {
 | 
			
		||||
                newFormInline.hiddenTag = value;
 | 
			
		||||
              }
 | 
			
		||||
            "
 | 
			
		||||
          />
 | 
			
		||||
        </el-form-item>
 | 
			
		||||
      </re-col>
 | 
			
		||||
 | 
			
		||||
      <re-col
 | 
			
		||||
        v-show="newFormInline.menuType !== 3"
 | 
			
		||||
        :value="12"
 | 
			
		||||
        :xs="24"
 | 
			
		||||
        :sm="24"
 | 
			
		||||
      >
 | 
			
		||||
        <el-form-item label="菜单">
 | 
			
		||||
          <Segmented
 | 
			
		||||
            :modelValue="newFormInline.showLink ? 0 : 1"
 | 
			
		||||
            :options="showLinkOptions"
 | 
			
		||||
            @change="
 | 
			
		||||
              ({ option: { value } }) => {
 | 
			
		||||
                newFormInline.showLink = value;
 | 
			
		||||
              }
 | 
			
		||||
            "
 | 
			
		||||
          />
 | 
			
		||||
        </el-form-item>
 | 
			
		||||
      </re-col>
 | 
			
		||||
      <re-col
 | 
			
		||||
        v-show="newFormInline.menuType !== 3"
 | 
			
		||||
        :value="8"
 | 
			
		||||
        :xs="24"
 | 
			
		||||
        :sm="24"
 | 
			
		||||
      >
 | 
			
		||||
        <el-form-item label="父级菜单">
 | 
			
		||||
          <Segmented
 | 
			
		||||
            :modelValue="newFormInline.showParent ? 0 : 1"
 | 
			
		||||
            :options="showParentOptions"
 | 
			
		||||
            @change="
 | 
			
		||||
              ({ option: { value } }) => {
 | 
			
		||||
                newFormInline.showParent = value;
 | 
			
		||||
              }
 | 
			
		||||
            "
 | 
			
		||||
          />
 | 
			
		||||
        </el-form-item>
 | 
			
		||||
      </re-col>
 | 
			
		||||
    </el-row>
 | 
			
		||||
  </el-form>
 | 
			
		||||
</template>
 | 
			
		||||
							
								
								
									
										157
									
								
								src/views/system/menu/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										157
									
								
								src/views/system/menu/index.vue
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,157 @@
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { ref } from "vue";
 | 
			
		||||
import { useMenu } from "./utils/hook";
 | 
			
		||||
import { transformI18n } from "@/plugins/i18n";
 | 
			
		||||
import { PureTableBar } from "@/components/RePureTableBar";
 | 
			
		||||
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
 | 
			
		||||
 | 
			
		||||
import Delete from "@iconify-icons/ep/delete";
 | 
			
		||||
import EditPen from "@iconify-icons/ep/edit-pen";
 | 
			
		||||
import Refresh from "@iconify-icons/ep/refresh";
 | 
			
		||||
import AddFill from "@iconify-icons/ri/add-circle-line";
 | 
			
		||||
 | 
			
		||||
defineOptions({
 | 
			
		||||
  name: "SystemMenu"
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const formRef = ref();
 | 
			
		||||
const tableRef = ref();
 | 
			
		||||
const {
 | 
			
		||||
  form,
 | 
			
		||||
  loading,
 | 
			
		||||
  columns,
 | 
			
		||||
  dataList,
 | 
			
		||||
  onSearch,
 | 
			
		||||
  resetForm,
 | 
			
		||||
  openDialog,
 | 
			
		||||
  handleDelete,
 | 
			
		||||
  handleSelectionChange
 | 
			
		||||
} = useMenu();
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="main">
 | 
			
		||||
    <el-form
 | 
			
		||||
      ref="formRef"
 | 
			
		||||
      :inline="true"
 | 
			
		||||
      :model="form"
 | 
			
		||||
      class="search-form bg-bg_color w-[99/100] pl-8 pt-[12px]"
 | 
			
		||||
    >
 | 
			
		||||
      <el-form-item label="菜单名称:" prop="title">
 | 
			
		||||
        <el-input
 | 
			
		||||
          v-model="form.title"
 | 
			
		||||
          placeholder="请输入菜单名称"
 | 
			
		||||
          clearable
 | 
			
		||||
          class="!w-[180px]"
 | 
			
		||||
        />
 | 
			
		||||
      </el-form-item>
 | 
			
		||||
      <el-form-item>
 | 
			
		||||
        <el-button
 | 
			
		||||
          type="primary"
 | 
			
		||||
          :icon="useRenderIcon('search')"
 | 
			
		||||
          :loading="loading"
 | 
			
		||||
          @click="onSearch"
 | 
			
		||||
        >
 | 
			
		||||
          搜索
 | 
			
		||||
        </el-button>
 | 
			
		||||
        <el-button :icon="useRenderIcon(Refresh)" @click="resetForm(formRef)">
 | 
			
		||||
          重置
 | 
			
		||||
        </el-button>
 | 
			
		||||
      </el-form-item>
 | 
			
		||||
    </el-form>
 | 
			
		||||
 | 
			
		||||
    <PureTableBar
 | 
			
		||||
      title="菜单管理(初版,持续完善中)"
 | 
			
		||||
      :columns="columns"
 | 
			
		||||
      :isExpandAll="false"
 | 
			
		||||
      :tableRef="tableRef?.getTableRef()"
 | 
			
		||||
      @refresh="onSearch"
 | 
			
		||||
    >
 | 
			
		||||
      <template #buttons>
 | 
			
		||||
        <el-button
 | 
			
		||||
          type="primary"
 | 
			
		||||
          :icon="useRenderIcon(AddFill)"
 | 
			
		||||
          @click="openDialog()"
 | 
			
		||||
        >
 | 
			
		||||
          新增菜单
 | 
			
		||||
        </el-button>
 | 
			
		||||
      </template>
 | 
			
		||||
      <template v-slot="{ size, dynamicColumns }">
 | 
			
		||||
        <pure-table
 | 
			
		||||
          ref="tableRef"
 | 
			
		||||
          adaptive
 | 
			
		||||
          :adaptiveConfig="{ offsetBottom: 45 }"
 | 
			
		||||
          align-whole="center"
 | 
			
		||||
          row-key="id"
 | 
			
		||||
          showOverflowTooltip
 | 
			
		||||
          table-layout="auto"
 | 
			
		||||
          :loading="loading"
 | 
			
		||||
          :size="size"
 | 
			
		||||
          :data="dataList"
 | 
			
		||||
          :columns="dynamicColumns"
 | 
			
		||||
          :header-cell-style="{
 | 
			
		||||
            background: 'var(--el-fill-color-light)',
 | 
			
		||||
            color: 'var(--el-text-color-primary)'
 | 
			
		||||
          }"
 | 
			
		||||
          @selection-change="handleSelectionChange"
 | 
			
		||||
        >
 | 
			
		||||
          <template #operation="{ row }">
 | 
			
		||||
            <el-button
 | 
			
		||||
              class="reset-margin"
 | 
			
		||||
              link
 | 
			
		||||
              type="primary"
 | 
			
		||||
              :size="size"
 | 
			
		||||
              :icon="useRenderIcon(EditPen)"
 | 
			
		||||
              @click="openDialog('修改', row)"
 | 
			
		||||
            >
 | 
			
		||||
              修改
 | 
			
		||||
            </el-button>
 | 
			
		||||
            <el-button
 | 
			
		||||
              v-show="row.menuType !== 3"
 | 
			
		||||
              class="reset-margin"
 | 
			
		||||
              link
 | 
			
		||||
              type="primary"
 | 
			
		||||
              :size="size"
 | 
			
		||||
              :icon="useRenderIcon(AddFill)"
 | 
			
		||||
              @click="openDialog('新增', { parentId: row.id } as any)"
 | 
			
		||||
            >
 | 
			
		||||
              新增
 | 
			
		||||
            </el-button>
 | 
			
		||||
            <el-popconfirm
 | 
			
		||||
              :title="`是否确认删除菜单名称为${transformI18n(row.title)}的这条数据${row?.children?.length > 0 ? '。注意下级菜单也会一并删除,请谨慎操作' : ''}`"
 | 
			
		||||
              @confirm="handleDelete(row)"
 | 
			
		||||
            >
 | 
			
		||||
              <template #reference>
 | 
			
		||||
                <el-button
 | 
			
		||||
                  class="reset-margin"
 | 
			
		||||
                  link
 | 
			
		||||
                  type="primary"
 | 
			
		||||
                  :size="size"
 | 
			
		||||
                  :icon="useRenderIcon(Delete)"
 | 
			
		||||
                >
 | 
			
		||||
                  删除
 | 
			
		||||
                </el-button>
 | 
			
		||||
              </template>
 | 
			
		||||
            </el-popconfirm>
 | 
			
		||||
          </template>
 | 
			
		||||
        </pure-table>
 | 
			
		||||
      </template>
 | 
			
		||||
    </PureTableBar>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
:deep(.el-table__inner-wrapper::before) {
 | 
			
		||||
  height: 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.main-content {
 | 
			
		||||
  margin: 24px 24px 0 !important;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.search-form {
 | 
			
		||||
  :deep(.el-form-item) {
 | 
			
		||||
    margin-bottom: 12px;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
							
								
								
									
										94
									
								
								src/views/system/menu/utils/enums.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								src/views/system/menu/utils/enums.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,94 @@
 | 
			
		||||
import type { OptionsType } from "@/components/ReSegmented";
 | 
			
		||||
 | 
			
		||||
const menuTypeOptions: Array<OptionsType> = [
 | 
			
		||||
  {
 | 
			
		||||
    label: "菜单",
 | 
			
		||||
    value: 0
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    label: "iframe",
 | 
			
		||||
    value: 1
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    label: "外链",
 | 
			
		||||
    value: 2
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    label: "按钮",
 | 
			
		||||
    value: 3
 | 
			
		||||
  }
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
const showLinkOptions: Array<OptionsType> = [
 | 
			
		||||
  {
 | 
			
		||||
    label: "显示",
 | 
			
		||||
    tip: "会在菜单中显示",
 | 
			
		||||
    value: true
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    label: "隐藏",
 | 
			
		||||
    tip: "不会在菜单中显示",
 | 
			
		||||
    value: false
 | 
			
		||||
  }
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
const keepAliveOptions: Array<OptionsType> = [
 | 
			
		||||
  {
 | 
			
		||||
    label: "缓存",
 | 
			
		||||
    tip: "会保存该页面的整体状态,刷新后会清空状态",
 | 
			
		||||
    value: true
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    label: "不缓存",
 | 
			
		||||
    tip: "不会保存该页面的整体状态",
 | 
			
		||||
    value: false
 | 
			
		||||
  }
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
const hiddenTagOptions: Array<OptionsType> = [
 | 
			
		||||
  {
 | 
			
		||||
    label: "允许",
 | 
			
		||||
    tip: "当前菜单名称或自定义信息允许添加到标签页",
 | 
			
		||||
    value: false
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    label: "禁止",
 | 
			
		||||
    tip: "当前菜单名称或自定义信息禁止添加到标签页",
 | 
			
		||||
    value: true
 | 
			
		||||
  }
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
const showParentOptions: Array<OptionsType> = [
 | 
			
		||||
  {
 | 
			
		||||
    label: "显示",
 | 
			
		||||
    tip: "会显示父级菜单",
 | 
			
		||||
    value: true
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    label: "隐藏",
 | 
			
		||||
    tip: "不会显示父级菜单",
 | 
			
		||||
    value: false
 | 
			
		||||
  }
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
const frameLoadingOptions: Array<OptionsType> = [
 | 
			
		||||
  {
 | 
			
		||||
    label: "开启",
 | 
			
		||||
    tip: "有首次加载动画",
 | 
			
		||||
    value: true
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    label: "关闭",
 | 
			
		||||
    tip: "无首次加载动画",
 | 
			
		||||
    value: false
 | 
			
		||||
  }
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
export {
 | 
			
		||||
  menuTypeOptions,
 | 
			
		||||
  showLinkOptions,
 | 
			
		||||
  keepAliveOptions,
 | 
			
		||||
  hiddenTagOptions,
 | 
			
		||||
  showParentOptions,
 | 
			
		||||
  frameLoadingOptions
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										223
									
								
								src/views/system/menu/utils/hook.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										223
									
								
								src/views/system/menu/utils/hook.tsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,223 @@
 | 
			
		||||
import editForm from "../form.vue";
 | 
			
		||||
import { handleTree } from "@/utils/tree";
 | 
			
		||||
import { message } from "@/utils/message";
 | 
			
		||||
import { getMenuList } from "@/api/system";
 | 
			
		||||
import { transformI18n } from "@/plugins/i18n";
 | 
			
		||||
import { addDialog } from "@/components/ReDialog";
 | 
			
		||||
import { reactive, ref, onMounted, h } from "vue";
 | 
			
		||||
import type { FormItemProps } from "../utils/types";
 | 
			
		||||
import { cloneDeep, isAllEmpty } from "@pureadmin/utils";
 | 
			
		||||
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
 | 
			
		||||
 | 
			
		||||
export function useMenu() {
 | 
			
		||||
  const form = reactive({
 | 
			
		||||
    title: ""
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  const formRef = ref();
 | 
			
		||||
  const dataList = ref([]);
 | 
			
		||||
  const loading = ref(true);
 | 
			
		||||
 | 
			
		||||
  const getMenuType = (type, text = false) => {
 | 
			
		||||
    switch (type) {
 | 
			
		||||
      case 0:
 | 
			
		||||
        return text ? "菜单" : "primary";
 | 
			
		||||
      case 1:
 | 
			
		||||
        return text ? "iframe" : "warning";
 | 
			
		||||
      case 2:
 | 
			
		||||
        return text ? "外链" : "danger";
 | 
			
		||||
      case 3:
 | 
			
		||||
        return text ? "按钮" : "info";
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const columns: TableColumnList = [
 | 
			
		||||
    {
 | 
			
		||||
      label: "菜单名称",
 | 
			
		||||
      prop: "title",
 | 
			
		||||
      align: "left",
 | 
			
		||||
      cellRenderer: ({ row }) => (
 | 
			
		||||
        <>
 | 
			
		||||
          <span class="inline-block mr-1">
 | 
			
		||||
            {h(useRenderIcon(row.icon), {
 | 
			
		||||
              style: { paddingTop: "1px" }
 | 
			
		||||
            })}
 | 
			
		||||
          </span>
 | 
			
		||||
          <span>{transformI18n(row.title)}</span>
 | 
			
		||||
        </>
 | 
			
		||||
      )
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      label: "菜单类型",
 | 
			
		||||
      prop: "menuType",
 | 
			
		||||
      width: 100,
 | 
			
		||||
      cellRenderer: ({ row, props }) => (
 | 
			
		||||
        <el-tag
 | 
			
		||||
          size={props.size}
 | 
			
		||||
          type={getMenuType(row.menuType)}
 | 
			
		||||
          effect="plain"
 | 
			
		||||
        >
 | 
			
		||||
          {getMenuType(row.menuType, true)}
 | 
			
		||||
        </el-tag>
 | 
			
		||||
      )
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      label: "路由路径",
 | 
			
		||||
      prop: "path"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      label: "组件路径",
 | 
			
		||||
      prop: "component",
 | 
			
		||||
      formatter: ({ path, component }) =>
 | 
			
		||||
        isAllEmpty(component) ? path : component
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      label: "权限标识",
 | 
			
		||||
      prop: "auths"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      label: "排序",
 | 
			
		||||
      prop: "rank",
 | 
			
		||||
      width: 100
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      label: "隐藏",
 | 
			
		||||
      prop: "showLink",
 | 
			
		||||
      formatter: ({ showLink }) => (showLink ? "否" : "是"),
 | 
			
		||||
      width: 100
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      label: "操作",
 | 
			
		||||
      fixed: "right",
 | 
			
		||||
      width: 210,
 | 
			
		||||
      slot: "operation"
 | 
			
		||||
    }
 | 
			
		||||
  ];
 | 
			
		||||
 | 
			
		||||
  function handleSelectionChange(val) {
 | 
			
		||||
    console.log("handleSelectionChange", val);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function resetForm(formEl) {
 | 
			
		||||
    if (!formEl) return;
 | 
			
		||||
    formEl.resetFields();
 | 
			
		||||
    onSearch();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async function onSearch() {
 | 
			
		||||
    loading.value = true;
 | 
			
		||||
    const { data } = await getMenuList(); // 这里是返回一维数组结构,前端自行处理成树结构,返回格式要求:唯一id加父节点parentId,parentId取父节点id
 | 
			
		||||
    let newData = data;
 | 
			
		||||
    if (!isAllEmpty(form.title)) {
 | 
			
		||||
      // 前端搜索菜单名称
 | 
			
		||||
      newData = newData.filter(item =>
 | 
			
		||||
        transformI18n(item.title).includes(form.title)
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
    dataList.value = handleTree(newData); // 处理成树结构
 | 
			
		||||
    setTimeout(() => {
 | 
			
		||||
      loading.value = false;
 | 
			
		||||
    }, 500);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function formatHigherMenuOptions(treeList) {
 | 
			
		||||
    if (!treeList || !treeList.length) return;
 | 
			
		||||
    const newTreeList = [];
 | 
			
		||||
    for (let i = 0; i < treeList.length; i++) {
 | 
			
		||||
      treeList[i].title = transformI18n(treeList[i].title);
 | 
			
		||||
      formatHigherMenuOptions(treeList[i].children);
 | 
			
		||||
      newTreeList.push(treeList[i]);
 | 
			
		||||
    }
 | 
			
		||||
    return newTreeList;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function openDialog(title = "新增", row?: FormItemProps) {
 | 
			
		||||
    addDialog({
 | 
			
		||||
      title: `${title}菜单`,
 | 
			
		||||
      props: {
 | 
			
		||||
        formInline: {
 | 
			
		||||
          menuType: row?.menuType ?? 0,
 | 
			
		||||
          higherMenuOptions: formatHigherMenuOptions(cloneDeep(dataList.value)),
 | 
			
		||||
          parentId: row?.parentId ?? 0,
 | 
			
		||||
          title: row?.title ?? "",
 | 
			
		||||
          name: row?.name ?? "",
 | 
			
		||||
          path: row?.path ?? "",
 | 
			
		||||
          component: row?.component ?? "",
 | 
			
		||||
          rank: row?.rank ?? 99,
 | 
			
		||||
          redirect: row?.redirect ?? "",
 | 
			
		||||
          icon: row?.icon ?? "",
 | 
			
		||||
          extraIcon: row?.extraIcon ?? "",
 | 
			
		||||
          enterTransition: row?.enterTransition ?? "",
 | 
			
		||||
          leaveTransition: row?.leaveTransition ?? "",
 | 
			
		||||
          activePath: row?.activePath ?? "",
 | 
			
		||||
          auths: row?.auths ?? "",
 | 
			
		||||
          frameSrc: row?.frameSrc ?? "",
 | 
			
		||||
          frameLoading: row?.frameLoading ?? true,
 | 
			
		||||
          keepAlive: row?.keepAlive ?? false,
 | 
			
		||||
          hiddenTag: row?.hiddenTag ?? false,
 | 
			
		||||
          showLink: row?.showLink ?? true,
 | 
			
		||||
          showParent: row?.showParent ?? false
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      width: "45%",
 | 
			
		||||
      draggable: true,
 | 
			
		||||
      fullscreenIcon: true,
 | 
			
		||||
      closeOnClickModal: false,
 | 
			
		||||
      contentRenderer: () => h(editForm, { ref: formRef }),
 | 
			
		||||
      beforeSure: (done, { options }) => {
 | 
			
		||||
        const FormRef = formRef.value.getRef();
 | 
			
		||||
        const curData = options.props.formInline as FormItemProps;
 | 
			
		||||
        function chores() {
 | 
			
		||||
          message(
 | 
			
		||||
            `您${title}了菜单名称为${transformI18n(curData.title)}的这条数据`,
 | 
			
		||||
            {
 | 
			
		||||
              type: "success"
 | 
			
		||||
            }
 | 
			
		||||
          );
 | 
			
		||||
          done(); // 关闭弹框
 | 
			
		||||
          onSearch(); // 刷新表格数据
 | 
			
		||||
        }
 | 
			
		||||
        FormRef.validate(valid => {
 | 
			
		||||
          if (valid) {
 | 
			
		||||
            console.log("curData", curData);
 | 
			
		||||
            // 表单规则校验通过
 | 
			
		||||
            if (title === "新增") {
 | 
			
		||||
              // 实际开发先调用新增接口,再进行下面操作
 | 
			
		||||
              chores();
 | 
			
		||||
            } else {
 | 
			
		||||
              // 实际开发先调用修改接口,再进行下面操作
 | 
			
		||||
              chores();
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function handleDelete(row) {
 | 
			
		||||
    message(`您删除了菜单名称为${transformI18n(row.title)}的这条数据`, {
 | 
			
		||||
      type: "success"
 | 
			
		||||
    });
 | 
			
		||||
    onSearch();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  onMounted(() => {
 | 
			
		||||
    onSearch();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  return {
 | 
			
		||||
    form,
 | 
			
		||||
    loading,
 | 
			
		||||
    columns,
 | 
			
		||||
    dataList,
 | 
			
		||||
    /** 搜索 */
 | 
			
		||||
    onSearch,
 | 
			
		||||
    /** 重置 */
 | 
			
		||||
    resetForm,
 | 
			
		||||
    /** 新增、修改菜单 */
 | 
			
		||||
    openDialog,
 | 
			
		||||
    /** 删除菜单 */
 | 
			
		||||
    handleDelete,
 | 
			
		||||
    handleSelectionChange
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										10
									
								
								src/views/system/menu/utils/rule.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/views/system/menu/utils/rule.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,10 @@
 | 
			
		||||
import { reactive } from "vue";
 | 
			
		||||
import type { FormRules } from "element-plus";
 | 
			
		||||
 | 
			
		||||
/** 自定义表单规则校验 */
 | 
			
		||||
export const formRules = reactive(<FormRules>{
 | 
			
		||||
  title: [{ required: true, message: "菜单名称为必填项", trigger: "blur" }],
 | 
			
		||||
  name: [{ required: true, message: "路由名称为必填项", trigger: "blur" }],
 | 
			
		||||
  path: [{ required: true, message: "路由路径为必填项", trigger: "blur" }],
 | 
			
		||||
  auths: [{ required: true, message: "权限标识为必填项", trigger: "blur" }]
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										29
									
								
								src/views/system/menu/utils/types.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								src/views/system/menu/utils/types.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,29 @@
 | 
			
		||||
interface FormItemProps {
 | 
			
		||||
  /** 菜单类型(0代表菜单、1代表iframe、2代表外链、3代表按钮)*/
 | 
			
		||||
  menuType: number;
 | 
			
		||||
  higherMenuOptions: Record<string, unknown>[];
 | 
			
		||||
  parentId: number;
 | 
			
		||||
  title: string;
 | 
			
		||||
  name: string;
 | 
			
		||||
  path: string;
 | 
			
		||||
  component: string;
 | 
			
		||||
  rank: number;
 | 
			
		||||
  redirect: string;
 | 
			
		||||
  icon: string;
 | 
			
		||||
  extraIcon: string;
 | 
			
		||||
  enterTransition: string;
 | 
			
		||||
  leaveTransition: string;
 | 
			
		||||
  activePath: string;
 | 
			
		||||
  auths: string;
 | 
			
		||||
  frameSrc: string;
 | 
			
		||||
  frameLoading: boolean;
 | 
			
		||||
  keepAlive: boolean;
 | 
			
		||||
  hiddenTag: boolean;
 | 
			
		||||
  showLink: boolean;
 | 
			
		||||
  showParent: boolean;
 | 
			
		||||
}
 | 
			
		||||
interface FormProps {
 | 
			
		||||
  formInline: FormItemProps;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type { FormItemProps, FormProps };
 | 
			
		||||
@ -108,6 +108,7 @@ const {
 | 
			
		||||
          :loading="loading"
 | 
			
		||||
          :size="size"
 | 
			
		||||
          adaptive
 | 
			
		||||
          :adaptiveConfig="{ offsetBottom: 108 }"
 | 
			
		||||
          :data="dataList"
 | 
			
		||||
          :columns="dynamicColumns"
 | 
			
		||||
          :pagination="pagination"
 | 
			
		||||
@ -206,6 +207,10 @@ const {
 | 
			
		||||
  margin: 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.main-content {
 | 
			
		||||
  margin: 24px 24px 0 !important;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.search-form {
 | 
			
		||||
  :deep(.el-form-item) {
 | 
			
		||||
    margin-bottom: 12px;
 | 
			
		||||
 | 
			
		||||
@ -150,6 +150,7 @@ const {
 | 
			
		||||
            ref="tableRef"
 | 
			
		||||
            row-key="id"
 | 
			
		||||
            adaptive
 | 
			
		||||
            :adaptiveConfig="{ offsetBottom: 108 }"
 | 
			
		||||
            align-whole="center"
 | 
			
		||||
            table-layout="auto"
 | 
			
		||||
            :loading="loading"
 | 
			
		||||
@ -260,6 +261,10 @@ const {
 | 
			
		||||
  outline: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.main-content {
 | 
			
		||||
  margin: 24px 24px 0 !important;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.search-form {
 | 
			
		||||
  :deep(.el-form-item) {
 | 
			
		||||
    margin-bottom: 12px;
 | 
			
		||||
 | 
			
		||||
@ -96,7 +96,7 @@ defineExpose({ onTreeReset });
 | 
			
		||||
  <div
 | 
			
		||||
    v-loading="props.treeLoading"
 | 
			
		||||
    class="h-full bg-bg_color overflow-auto"
 | 
			
		||||
    :style="{ minHeight: `calc(100vh - 133px)` }"
 | 
			
		||||
    :style="{ minHeight: `calc(100vh - 145px)` }"
 | 
			
		||||
  >
 | 
			
		||||
    <div class="flex items-center h-[34px]">
 | 
			
		||||
      <el-input
 | 
			
		||||
 | 
			
		||||
@ -104,7 +104,7 @@ export function useUser(tableRef: Ref, treeRef: Ref) {
 | 
			
		||||
      cellRenderer: ({ row, props }) => (
 | 
			
		||||
        <el-tag
 | 
			
		||||
          size={props.size}
 | 
			
		||||
          type={row.sex === 1 ? "danger" : ""}
 | 
			
		||||
          type={row.sex === 1 ? "danger" : null}
 | 
			
		||||
          effect="plain"
 | 
			
		||||
        >
 | 
			
		||||
          {row.sex === 1 ? "女" : "男"}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user