NewLife/NewLife.QuickVue

完成项目搭建,完成page、form等公用组件基础功能,初步实现快速开发
zk authored at 2023-04-11 16:26:22
2b2f120
Tree
1 Parent(s) b53470f
Summary: 172 changed files with 24778 additions and 19 deletions.
Added +8 -0
Added +5 -0
Added +5 -0
Added +18 -0
Added +77 -0
Added +39 -0
Added +451 -0
Added +32 -0
Modified +2 -2
Added +7716 -0
Added +90 -0
Added +6 -0
Added +0 -0
Modified +10 -17
Added +40 -0
Added +70 -0
Added +34 -0
Added +101 -0
Added +0 -0
Added +19 -0
Added +1 -0
Added +1 -0
Added +0 -0
Added +9 -0
Added +26 -0
Added +27 -0
Added +32 -0
Added +26 -0
Added +143 -0
Added +101 -0
Added +23 -0
Added +32 -0
Added +34 -0
Added +107 -0
Added +50 -0
Added +9 -0
Added +241 -0
Added +84 -0
Added +191 -0
Added +88 -0
Added +64 -0
Added +139 -0
Added +44 -0
Added +26 -0
Added +33 -0
Added +63 -0
Added +328 -0
Added +109 -0
Added +27 -0
Added +40 -0
Added +178 -0
Added +18 -0
Added +30 -0
Added +82 -0
Added +68 -0
Added +192 -0
Added +192 -0
Added +192 -0
Added +13 -0
Added +13 -0
Added +13 -0
Added +29 -0
Added +28 -0
Added +28 -0
Added +165 -0
Added +281 -0
Added +18 -0
Added +65 -0
Added +25 -0
Added +50 -0
Added +352 -0
Added +77 -0
Added +71 -0
Added +71 -0
Added +71 -0
Added +58 -0
Added +146 -0
Added +53 -0
Added +107 -0
Added +124 -0
Added +824 -0
Added +258 -0
Added +107 -0
Added +35 -0
Added +138 -0
Added +726 -0
Added +159 -0
Added +49 -0
Added +102 -0
Added +101 -0
Added +93 -0
Added +108 -0
Added +108 -0
Added +151 -0
Added +26 -0
Added +22 -0
Added +0 -0
Added +21 -0
Added +16 -0
Added +52 -0
Added +253 -0
Added +153 -0
Added +137 -0
Added +1230 -0
Added +8 -0
Added +35 -0
Added +16 -0
Added +26 -0
Added +23 -0
Added +157 -0
Added +50 -0
Added +324 -0
Added +147 -0
Added +249 -0
Added +333 -0
Added +31 -0
Added +8 -0
Added +51 -0
Added +94 -0
Added +10 -0
Added +25 -0
Added +12 -0
Added +45 -0
Added +31 -0
Added +23 -0
Added +15 -0
Added +59 -0
Added +74 -0
Added +13 -0
Added +15 -0
Added +16 -0
Added +56 -0
Added +11 -0
Added +56 -0
Added +36 -0
Added +27 -0
Added +3 -0
Added +101 -0
Added +13 -0
Added +111 -0
Added +59 -0
Added +38 -0
Added +91 -0
Added +332 -0
Added +65 -0
Added +38 -0
Added +66 -0
Added +137 -0
Added +101 -0
Added +44 -0
Added +8 -0
Added +289 -0
Added +81 -0
Added +48 -0
Added +64 -0
Added +63 -0
Added +370 -0
Added +47 -0
Added +91 -0
Added +91 -0
Added +633 -0
Added +181 -0
Added +70 -0
Added +63 -0
Added +254 -0
Added +51 -0
Added +8 -0
Added +373 -0
Added +66 -0
Added +11 -0
Added +75 -0
Added +66 -0
Added +8 -0
diff --git a/.env b/.env
new file mode 100644
index 0000000..dacb1a5
--- /dev/null
+++ b/.env
@@ -0,0 +1,8 @@
+# port 端口号
+VITE_PORT = 8888
+
+# open 运行 npm run dev 时自动打开浏览器
+VITE_OPEN = false
+
+# public path 配置线上环境路径(打包)、本地通过 http-server 访问时,请置空即可
+VITE_PUBLIC_PATH = /vue-next-admin-preview/
\ No newline at end of file
Added +5 -0
diff --git a/.env.development b/.env.development
new file mode 100644
index 0000000..f36b7f1
--- /dev/null
+++ b/.env.development
@@ -0,0 +1,5 @@
+# 本地环境
+ENV = development
+
+# 本地环境接口地址
+VITE_API_URL = https://cube3.newlifex.com
\ No newline at end of file
Added +5 -0
diff --git a/.env.production b/.env.production
new file mode 100644
index 0000000..478a841
--- /dev/null
+++ b/.env.production
@@ -0,0 +1,5 @@
+# 线上环境
+ENV = production
+
+# 线上环境接口地址
+VITE_API_URL = https://lyt-top.gitee.io/vue-next-admin-preview/
\ No newline at end of file
Added +18 -0
diff --git a/.eslintignore b/.eslintignore
new file mode 100644
index 0000000..cfc877d
--- /dev/null
+++ b/.eslintignore
@@ -0,0 +1,18 @@
+
+*.sh
+node_modules
+lib
+*.md
+*.scss
+*.woff
+*.ttf
+.vscode
+.idea
+dist
+mock
+public
+bin
+build
+config
+index.html
+src/assets
\ No newline at end of file
Added +77 -0
diff --git a/.eslintrc.js b/.eslintrc.js
new file mode 100644
index 0000000..0a42a49
--- /dev/null
+++ b/.eslintrc.js
@@ -0,0 +1,77 @@
+module.exports = {
+	root: true,
+	env: {
+		browser: true,
+		es2021: true,
+		node: true,
+	},
+	parser: 'vue-eslint-parser',
+	parserOptions: {
+		ecmaVersion: 12,
+		parser: '@typescript-eslint/parser',
+		sourceType: 'module',
+	},
+	extends: ['plugin:vue/vue3-essential', 'plugin:vue/essential', 'eslint:recommended'],
+	plugins: ['vue', '@typescript-eslint'],
+	overrides: [
+		{
+			files: ['*.ts', '*.tsx', '*.vue'],
+			rules: {
+				'no-undef': 'off',
+			},
+		},
+	],
+	rules: {
+		// http://eslint.cn/docs/rules/
+		// https://eslint.vuejs.org/rules/
+		// https://typescript-eslint.io/rules/no-unused-vars/
+		'@typescript-eslint/ban-ts-ignore': 'off',
+		'@typescript-eslint/explicit-function-return-type': 'off',
+		'@typescript-eslint/no-explicit-any': 'off',
+		'@typescript-eslint/no-var-requires': 'off',
+		'@typescript-eslint/no-empty-function': 'off',
+		'@typescript-eslint/no-use-before-define': 'off',
+		'@typescript-eslint/ban-ts-comment': 'off',
+		'@typescript-eslint/ban-types': 'off',
+		'@typescript-eslint/no-non-null-assertion': 'off',
+		'@typescript-eslint/explicit-module-boundary-types': 'off',
+		'@typescript-eslint/no-redeclare': 'error',
+		'@typescript-eslint/no-non-null-asserted-optional-chain': 'off',
+		'@typescript-eslint/no-unused-vars': [2],
+		'vue/custom-event-name-casing': 'off',
+		'vue/attributes-order': 'off',
+		'vue/one-component-per-file': 'off',
+		'vue/html-closing-bracket-newline': 'off',
+		'vue/max-attributes-per-line': 'off',
+		'vue/multiline-html-element-content-newline': 'off',
+		'vue/singleline-html-element-content-newline': 'off',
+		'vue/attribute-hyphenation': 'off',
+		'vue/html-self-closing': 'off',
+		'vue/no-multiple-template-root': 'off',
+		'vue/require-default-prop': 'off',
+		'vue/no-v-model-argument': 'off',
+		'vue/no-arrow-functions-in-watch': 'off',
+		'vue/no-template-key': 'off',
+		'vue/no-v-html': 'off',
+		'vue/comment-directive': 'off',
+		'vue/no-parsing-error': 'off',
+		'vue/no-deprecated-v-on-native-modifier': 'off',
+		'vue/multi-word-component-names': 'off',
+		'vue/no-v-for-template-key': 'off',
+		'no-useless-escape': 'off',
+		'no-sparse-arrays': 'off',
+		'no-prototype-builtins': 'off',
+		'no-constant-condition': 'off',
+		'no-use-before-define': 'off',
+		'no-restricted-globals': 'off',
+		'no-restricted-syntax': 'off',
+		'generator-star-spacing': 'off',
+		'no-unreachable': 'off',
+		'no-multiple-template-root': 'off',
+		'no-unused-vars': 'error',
+		'no-v-model-argument': 'off',
+		'no-case-declarations': 'off',
+		'no-console': 'error',
+		'no-redeclare': 'off',
+	},
+};
Added +39 -0
diff --git a/.prettierrc.js b/.prettierrc.js
new file mode 100644
index 0000000..cff490a
--- /dev/null
+++ b/.prettierrc.js
@@ -0,0 +1,39 @@
+module.exports = {
+	// 一行最多多少个字符
+	printWidth: 150,
+	// 指定每个缩进级别的空格数
+	tabWidth: 2,
+	// 使用制表符而不是空格缩进行
+	useTabs: true,
+	// 在语句末尾打印分号
+	semi: true,
+	// 使用单引号而不是双引号
+	singleQuote: true,
+	// 更改引用对象属性的时间 可选值"<as-needed|consistent|preserve>"
+	quoteProps: 'as-needed',
+	// 在JSX中使用单引号而不是双引号
+	jsxSingleQuote: false,
+	// 多行时尽可能打印尾随逗号。(例如,单行数组永远不会出现逗号结尾。) 可选值"<none|es5|all>",默认none
+	trailingComma: 'es5',
+	// 在对象文字中的括号之间打印空格
+	bracketSpacing: true,
+	// jsx 标签的反尖括号需要换行
+	jsxBracketSameLine: false,
+	// 在单独的箭头函数参数周围包括括号 always:(x) => x \ avoid:x => x
+	arrowParens: 'always',
+	// 这两个选项可用于格式化以给定字符偏移量(分别包括和不包括)开始和结束的代码
+	rangeStart: 0,
+	rangeEnd: Infinity,
+	// 指定要使用的解析器,不需要写文件开头的 @prettier
+	requirePragma: false,
+	// 不需要自动在文件开头插入 @prettier
+	insertPragma: false,
+	// 使用默认的折行标准 always\never\preserve
+	proseWrap: 'preserve',
+	// 指定HTML文件的全局空格敏感度 css\strict\ignore
+	htmlWhitespaceSensitivity: 'css',
+	// Vue文件脚本和样式标签缩进
+	vueIndentScriptAndStyle: false,
+	// 换行符使用 lf 结尾是 可选值"<auto|lf|crlf|cr>"
+	endOfLine: 'lf',
+};
Added +451 -0
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..c7e3969
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,451 @@
+# <a href="https://gitee.com/lyt-top/vue-next-admin" target="_blank">vue-next-admin 更新日志</a>
+
+🎉🎉🔥 `vue-next-admin` 基于 vue3.x 、Typescript、vite、Element plus 等,适配手机、平板、pc 的后台开源免费模板库(vue2.x 请切换 vue-prev-admin 分支)
+
+## 2.4.3
+
+`2023.02.22`
+
+🚩🚩🚩 感谢 [驰骋工作流引擎-表单引擎-低代码开发平台](http://www.ccflow.org/) 赞助商的赞助。驰骋公司为社会提供流程引擎+表单引擎+低代码开发平台一体的开源软件解决方案,欢迎广大开发者前去体验!
+
+- 🌟 更新 依赖更新最新版本
+- 🎉 新增 赞助商组件(`/src/layout/sponsors`),[项目目录结构查看](https://lyt-top.gitee.io/vue-next-admin-doc-preview/config/)
+- 🐞 修复 [过滤筛选组件展开点击不了](https://gitee.com/lyt-top/vue-next-admin/issues/I688WG)
+- 🐞 修复 [设置锁屏时间时直接白屏了不能恢复,除非删除主题配置才会重新加载](https://gitee.com/lyt-top/vue-next-admin/issues/I6AF8P),感谢[@baizunxian](https://gitee.com/xb_xiaobai)
+- 🐞 修复 `分栏布局` 地址栏输入不存在的路由报错问题
+- 🎨 合并 [!44 tagsViewName 正则匹配错误,匹配到含 en 单词](https://gitee.com/lyt-top/vue-next-admin/pulls/44/files),感谢[@tony 星](https://gitee.com/tony_tong_xin)
+- 🎨 合并 [!45 fix 地址栏出现 false 问题](https://gitee.com/lyt-top/vue-next-admin/pulls/45),感谢[@随心](https://gitee.com/jiangqiang1996)
+- 🎯 优化 `/src/utils/storage` 下 `key` 编写成 `${__NEXT_NAME__}:${key}`,防止部署多套系统到同一域名不同目录时,变量共用的问题(`__NEXT_NAME__`为 `package.json` 中的 `name`)
+- 🎯 优化 watermark 单词拼写错误
+
+## 2.4.21
+
+`2022.12.12`
+
+- 🌟 更新 依赖更新最新版本
+- 🎉 新增 菜单背景高亮颜色可自定义,通过 `布局配置 -> 菜单设置 -> 菜单高亮背景色` 进行设置
+- 🐞 修复 `分栏布局` 二级导航菜单内容多时,无法滚动问题,感谢群友@静雨轩主人
+- 🐞 修复 [!42 修复 工作流无法添加新节点问题](https://gitee.com/lyt-top/vue-next-admin/pulls/42),感谢[@beta](https://gitee.com/beta_dz)
+- 🎯 优化 `/make/tableDemo` 表头很多时,无法滚动问题,感谢群友@糊涂涂涂
+
+## 2.4.2
+
+`2022.12.09`
+
+- 🌟 更新 依赖更新最新版本
+- 🎉 新增 国际化自动导入文件功能,只需在 `/src/i18n/pages` 下新建文件夹定义即可
+- 🎉 新增 `/make/tableDemo` 中 [搜索框展开,收缩功能,高级筛选组件 有计划做吗](https://gitee.com/lyt-top/vue-next-admin/issues/I6511L)
+- 🐞 修复 [!40 开启 TagsView 缓存后,刷新后所有的路由都变成组件缓存了](https://gitee.com/lyt-top/vue-next-admin/pulls/40),感谢[@mrjimin](https://gitee.com/mrjimin)
+- 🐞 修复 [!41 修复 get 请求传递嵌套对象或数组时无法正常编码问题](https://gitee.com/lyt-top/vue-next-admin/pulls/41),感谢[@随心](https://gitee.com/jiangqiang1996)
+- 🐞 修复 组件 wangEditor 回显值的问题
+- 🐞 修复 `/fun/echartsMap`(地理坐标/地图)、`visualizingDemo2`(数据可视化演示 2) 演示报错问题
+- 🎯 优化 版本升级提示
+- 🎯 优化 无权限登录时增加提示信息,[BUG:因前端加载路由(initFrontEndControlRoutes)中当前用户角色为一个陌生角色, 导致 router.beforeEach 会死循环 浏览器崩溃](https://gitee.com/lyt-top/vue-next-admin/issues/I64HVO),感谢[@canroc](https://gitee.com/canroc)、[@随心](https://gitee.com/jiangqiang1996)
+- 🌈 重构 `/views/system` 新增修改组件合并。[可以把新增修改组件合并成一个吧](https://gitee.com/lyt-top/vue-next-admin/issues/I64WES)
+- 🌈 重构 图标选择器,[图标选择器没办法筛选,只能筛选 ali 的](https://gitee.com/lyt-top/vue-next-admin/issues/I64HZD),感谢[@随心](https://gitee.com/jiangqiang1996)
+
+## 2.4.1
+
+`2022.11.30`
+
+- 🎉 新增 版本升级提示
+- 🐞 修复 [先打开 F12 再登录进去,然后改变浏览器大小 js 报错](https://gitee.com/lyt-top/vue-next-admin/issues/I63ZZT),感谢[@Quber](https://gitee.com/quber)
+
+## 2.4.0
+
+`2022.11.29`
+
+⚡⚡⚡ 此版为破坏性更新,应群友建议 `script lang="ts"` 改 `script lang="ts" setup 语法糖`。
+
+- 🌟 更新 依赖更新最新版本
+- 🎉 新增 表格封装演示,路径:`组件封装 -> 表格封装演示`
+- 🎉 新增 master 分支 script lang="ts" 改成 script lang="ts" setup 语法糖,将同步基础分支
+- 🐞 修复 [v2.3.0 版本报错问题处理](https://gitee.com/lyt-top/vue-next-admin/issues/I623RP)
+- 🐞 修复 [el-backtop 滚动高度不触发(固定了 header)](https://gitee.com/lyt-top/vue-next-admin/issues/I63N0D),感谢[@dejavuuuuu](https://gitee.com/zc19951010)
+- 🎯 优化 完善 ts 类型,删除根目录 `plugins.d.ts、shim.d.ts、source.d.ts`,移入到 `/src/types/global.d.ts`
+- 🎯 优化 代码 `watch` 移动到 `生命周期钩子` 最后,文字注释等
+
+## 2.3.0
+
+`2022.11.16`
+
+- 🌟 更新 依赖更新最新版本
+- 🎉 新增 新版登录页
+- 🎉 新增 tagsview 鼠标中键 `关闭当前 tagsview`
+- 🎉 新增 `分栏菜单鼠标悬停预加载`。[分栏模式如何去掉鼠标悬浮父级菜单,分栏菜单自动加载的功能啊](https://gitee.com/lyt-top/vue-next-admin/issues/I5RUY7)。操作路径:`布局配置 -> 分栏设置`
+- 🐞 修复 [vue-i18n](https://vue-i18n.intlify.dev/api/general.html#createi18n) 报错,[!39 修复 i18n 兼容性问题](https://toscode.gitee.com/lyt-top/vue-next-admin/pulls/39/files),感谢[@随心](https://toscode.gitee.com/jiangqiang1996)
+- 🐞 修复 顶栏搜索功能点击蒙蔽弹窗不关闭
+- 🐞 修复 [!38 fix: bug refreshRouterViewKey 值为 null 导致路由缓存第一次无效](https://toscode.gitee.com/lyt-top/vue-next-admin/pulls/38/files),感谢[@P)](https://toscode.gitee.com/foxp8y)
+- 🐞 修复 `路由参数 -> 普通路由/动态路由` 国际化演示时,`tagsView` 和 `浏览器标题` 显示异常。[演示中:路由参数界面 -> 动态路由,国际化显示时面包屑、浏览器标题有 bug](https://gitee.com/lyt-top/vue-next-admin/issues/I5JRJG)
+- 🐞 修复 `路由参数 -> 普通路由/动态路由` 动态设置 `tagsViewName` 时,`tagsView 右键菜单刷新` 功能失效(也就是路由后面有参数时,query、params)。[普通或动态路由新建页面后点击 tagview 刷新无效](https://gitee.com/lyt-top/vue-next-admin/issues/I5K3YO),感谢[@dejavuuuuu](https://gitee.com/zc19951010)
+- 🐞 修复 [表单(el-form)中,字体图标偏移问题](https://gitee.com/lyt-top/vue-next-admin/issues/I5K1PM)
+- 🐞 修复 路由 `router.addRoute` 时,一直提示 `No match found for location with path 'xxx'`
+- 🎯 优化 全局 `getCurrentInstance` 替换成 [`provide/inject`](https://cn.vuejs.org/api/application.html#app-provide) 或通过 `ref` 处理
+- 🎯 优化 引入组件方式 `(import xxx from xxx)` 改成 `defineAsyncComponent(() => import(xxx))`
+- 🎯 优化 页面高度 100% 问题,重写布局配置 `界面设置 -> 固定 Header` 多余的 `el-scrollbar` 逻辑、重写各界面需 `计算属性 computed` 设置动态高度问题(改为 css `flex` 设置自适应高度,具体查看文档:[设置可视区高度 100%](https://lyt-top.gitee.io/vue-next-admin-doc-preview/config/otherIssues/#%E8%AE%BE%E7%BD%AE%E5%8F%AF%E8%A7%86%E5%8C%BA%E9%AB%98%E5%BA%A6-100)。[!31 修复页面样式无法通过百分比设置的问题](https://toscode.gitee.com/lyt-top/vue-next-admin/pulls/31),感谢[@LostDeer](https://toscode.gitee.com/lyt-top/vue-next-admin/pulls/31/files)。`(改动较大,删除多余代码)`
+- 🎯 优化 [wangeditor](https://www.wangeditor.com/) 组件,`@wangeditor/editor-for-vue`。可自行修改,组件位置:`/src/components/editor`。相关 Issues:[wangeditor 编辑器多个菜单不能回弹](https://gitee.com/lyt-top/vue-next-admin/issues/I5M5H7)
+- 🌈 重构 外链、内嵌 iframe 逻辑 + 美化,iframe 支持缓存
+
+## 2.2.0
+
+`2022.07.10`
+
+⚡⚡⚡ [/sec/stores/userInfo.ts](https://gitee.com/lyt-top/vue-next-admin/blob/master/src/stores/userInfo.ts) 下添加了 `getApiUserInfo` 接口模拟数据 `setTimeout` 为 3 秒
+
+- 🌟 更新 依赖更新最新版本
+- 🐞 修复 [主界面重新授权按钮点击卡死不跳转登录界面#I5C3JS](https://gitee.com/lyt-top/vue-next-admin/issues/I5C3JS),感谢[@Hero-Typ](https://gitee.com/tian_yu_peng)
+- 🐞 修复 编译警告[#I5CVSB](https://gitee.com/lyt-top/vue-next-admin/issues/I5CVSB),全局替换成 `:deep(attr)`,感谢[@Linvas](https://gitee.com/linvas)。参考文档:[vue3 sfc-style](https://v3.cn.vuejs.org/api/sfc-style.html#style-scoped)。`node_modules\print-js\dist\print.js` 需 `print-js` 作者适配或去除 `package.json` 中的 `"print-js": "^1.6.0"`
+- 🐞 修复 [vue-next-admin-template-js 版本前端控制路由:userInfo.js 请求用户信息接口报错,加载不到路由 可以写个定时器模拟一下接口 一样的报错#I5F1HP](https://gitee.com/lyt-top/vue-next-admin/issues/I5F1HP),感谢[@白开水](https://gitee.com/libin951223)
+
+## 2.1.1
+
+`2022.05.27`
+
+- 🌟 更新 依赖更新最新版本
+- 🎯 优化 深色模式下,`<el-button text></el-button>` 时,`:active` 样式
+- 🎯 优化 [页面缓存在刷新之后失效 #I58U75](https://gitee.com/lyt-top/vue-next-admin/issues/I58U75)),感谢[@ls0428](https://gitee.com/ls0428)
+- 🎯 优化 [SvgIcon 对下载的 Svg 图像设置颜色无效 #I59ND0](https://gitee.com/lyt-top/vue-next-admin/issues/I59ND0)),感谢[@elus_z](https://gitee.com/elus_z)
+- 🎯 优化 `/src/utils/toolsValidate.ts` 工具类
+- 🐞 修复 [布局切换,TagsView 显示的 tab 会多一个出来 #I58WGM](https://gitee.com/lyt-top/vue-next-admin/issues/I58WGM),感谢[@lg_boy](https://gitee.com/lg_boy)
+- 🐞 修复 [如果设置顶部面包屑导航开启图标 isBreadcrumbIcon=true 后,样式有点问题 如果不开启就是正常的 #I58VB8](https://gitee.com/lyt-top/vue-next-admin/issues/I58VB8)
+- 🐞 修复 地址栏路由地址输入错误时,返回首页后,再次输入路由地址错误时,不跳转 404 问题
+- 🐞 修复 [2.1.0 版本的图标选择组件多次点击后功能失效 #I590TH](https://gitee.com/lyt-top/vue-next-admin/issues/I590TH),感谢[@quber](https://gitee.com/quber)
+
+## 2.1.0
+
+`2022.04.18`
+
+⚡⚡⚡ 此版本为破环性更新,优化内容如下:(谨慎更新!谨慎更新!!谨慎更新!!!)。因为 `vuex` 替换成 `pinia`
+
+- 🌟 更新 依赖更新最新版本
+- 🎯 优化 部分界面图片不显示问题(更换 gitee 在线图片地址源)
+- 🎯 优化 各界面方法引入与逻辑之间添加一行空行,方便区分内容
+- 🎯 优化 图标选择器 [#I4YAHB](https://gitee.com/lyt-top/vue-next-admin/issues/I4YAHB),感谢[@真有你的](https://gitee.com/sunliusen)
+- 🎯 优化 图标选择器 icon type 类型为 all 时,类型 ali、ele、awe 回显问题
+- 🎯 优化 去掉开发环境 i18n 控制台警告,页面代码:[i18n/index.ts](https://gitee.com/lyt-top/vue-next-admin/blob/master/src/i18n/index.ts)
+- 🎯 优化 `NextLoading.start()` 方法,防止第一次进入界面时出现短暂空白
+- 🎯 优化 地址栏有参数退出登录,再次登录不跳之前界面问题 `src/layout/navBars/breadcrumb/user.vue`
+- 🎯 优化 `SvgIcon` 组件,防止 `开启 Tagsview 图标` 时,`tagsView 右键菜单关闭` 报错问题,工作流不可连线、全屏时关闭按钮消失问题
+- 🎯 优化 [如果 url 中有中文等特殊字符,第一次切换该 tab 时 keep-alive 失效#I55JS7](https://gitee.com/lyt-top/vue-next-admin/issues/I55JS7),感谢[yuyong1566](https://gitee.com/yuyong1566)
+- 🎯 优化 [wangEditor](https://www.wangeditor.com/) 更新到 v5,[vue3 版本线上示例中 wangeditor 富文本编辑器 demo 实例,无法换行#I5565B](https://gitee.com/lyt-top/vue-next-admin/issues/I5565B),感谢@[jenchih](https://gitee.com/jenchih)
+- 🎯 优化 [在关闭 tagview 时,高度刷新时会会变化,出现滚动条](https://gitee.com/lyt-top/vue-next-admin/issues/I55FHM),感谢[张松](https://gitee.com/zs310071113)
+- 🎯 优化 [路由参数](https://lyt-top.gitee.io/vue-next-admin-preview/#/params/common)演示
+- 🎉 新增 [vuex](https://vuex.vuejs.org/) 替换成 [pinia](https://pinia.vuejs.org/getting-started.html)
+- 🎉 新增 tagsView 支持自定义 tagsView 名称(文章详情时有用),前往体验:[路由参数/普通路由](https://lyt-top.gitee.io/vue-next-admin-preview/#/params/common)。新增 tagsView 支持自定义名称国际化,感谢[@q7but](https://gitee.com/q7but)、[!22 add 添加自定义 tagVIewName 拓展,支持国际化](https://gitee.com/lyt-top/vue-next-admin/pulls/22/files)、感谢[@tony_tong_xin](https://gitee.com/tony_tong_xin)
+- 🐞 修复 适配 `"element-plus": "^2.1.9",2.2.0` 版本
+- 🐞 修复 [导航栏横向布局后,一级菜单显示问题#I4Z3M3](https://gitee.com/lyt-top/vue-next-admin/issues/I4Z3M3)
+- 🐞 修复 横向布局三级及以上导航菜单高亮、导航高度不统一问题
+- 🐞 修复 分栏模式下,选中的菜单是 primary 样式,鼠标移入字也变成 primary 色了,感谢群友@孤夜-流殇
+- 🐞 修复 [vuex 里面改了颜色 但是不生效 #I4WFMA](https://gitee.com/lyt-top/vue-next-admin/issues/I4WFMA)
+- 🐞 修复 全局主题 primary 清空颜色后报错,[#I4X0LG](https://gitee.com/lyt-top/vue-next-admin/issues/I4X0LG),感谢[面向 BUG 编程](https://gitee.com/fhtfy)
+- 🐞 修复 [.eslintrc.js 文件 rules 标签名错误 #I53IPK](https://gitee.com/lyt-top/vue-next-admin/issues/I53IPK),感谢[yuyong1566](https://gitee.com/yuyong1566)
+- 🐞 修复 `开启 Tagsview 图标` 时,`tagsView 右键菜单关闭` 报错问题
+- 🐞 修复 `router.push` 路径找不到时报错问题,`404、401 界面` 已移入到 `main` 主布局里(之前全屏)
+- 🐞 修复 [全局修改组件大小失效了](https://gitee.com/lyt-top/vue-next-admin/issues/I551RP),感谢[lg_boy](https://gitee.com/lg_boy)
+- 🐞 修复 [修改一下配置时,需要每次都清理 `window.localStorage` 浏览器永久缓存,配置才会生效,问题解决#I567R1](https://gitee.com/lyt-top/vue-next-admin/issues/I567R1),感谢[@lanbao123](https://gitee.com/lanbao123)
+- 🐞 修复 [标记为需要缓存的 tab 页后,再次从左侧菜单打开,还是显示被缓存的页面内容#I4UY3G](https://gitee.com/lyt-top/vue-next-admin/issues/I4UY3G),感谢@axcc1234、特别感谢群友@华仔
+- 🌈 重构 路由(`/src/router/index.ts`)解决 No match found for location with path "xxx"(前端控制,后端控制未解决) 问题
+
+## 2.0.2
+
+`2022.03.04`
+
+- 🌟 更新 依赖更新最新版本
+- 🎯 优化 Alert 提示添加边框
+- 🎯 优化 功能 / 数字滚动 演示界面
+- 🐞 修复 全局主题按钮颜色 :active 问题
+- 🐞 修复 Dropdown 下拉菜单样式问题
+- 🐞 修复 SvgIcon 图标组件动态切换时报警告问题,[SvgIcon 改变 name 时可能导致图像不显示](https://gitee.com/lyt-top/vue-next-admin/issues/I4VGE0),感谢@axcc1234
+
+## 2.0.1
+
+`2022.02.25`
+
+- 🌟 更新 依赖更新最新版本
+- 🎯 优化 svgIcon 图标组件
+- 🎯 优化 vite.config.ts 打包,感谢群友@YourObjec
+- 🐞 修复 tagViews 开启图标不显示问题(风格 5),感谢群友@坏人
+- 🐞 修复 [Element Plus 1.2.0-beta.6 以后的版本 el-table 在移动端无法左右滑动](https://gitee.com/lyt-top/vue-next-admin/issues/I4UPTP),感谢@YGDada
+
+## 2.0.0
+
+`2022.02.21`
+
+⚡⚡⚡ 此版本为破环性更新,优化内容如下:(谨慎更新!谨慎更新!!谨慎更新!!!)。演示界面建议直接覆盖文件。如需使用之前版本,请前往[gitee 发行版](https://gitee.com/lyt-top/vue-next-admin/releases) 进行对应版本下载。基础版会基于 `master` 分支进行修改
+
+- 🌟 更新 依赖更新最新版本
+- 🌟 更新 登录页、首页
+- 💔 移除 vue-web-screen-shot
+- 💔 移除 城市多级联动,完整 json 数据请去 [vue-next-admin-images/menu](https://gitee.com/lyt-top/vue-next-admin-images/tree/master/menu) 仓库查看
+- 💔 移除 功能/echartsTree 树图
+- 💔 移除 其它设置/Tagsview 风格 2、Tagsview 风格 3
+- 💔 移除 功能/验证器
+- 🚧 调整 src/api 编写方式
+- 🚧 调整 自定义封装公用组件演示,更好的维护
+- 🎉 新增 Volar 支持,vs code 配置参考 [Vue Language Features (Volar)](https://lyt-top.gitee.io/vue-next-admin-doc-preview/home/vscode/)
+- 🎉 新增 `SvgIcon` 支持本地 svg 图标使用
+- 🎉 新增 表单表格验证演示
+- 🎯 优化 全局主题(移除 success、info、warning、danger)
+- 🎯 优化 工作流(开源)
+- 🎯 优化 element plus svg 图标,`elementXXX` 改成 `ele-XXX`
+- 🌈 重构 深色模式
+- 🌹 合并 [处理 parent 的 h100 由于外层有 min-height 导致失效的问题](https://gitee.com/lyt-top/vue-next-admin/pulls/20),感谢@MaxNull、@21030442-mao
+- 🐞 修复 element plus 升级 `^1.3.0-beta.5` 后 组件 size 大小问题(大改:涉及布局、演示界面)
+- 🐞 修复 vs code 使用 Vue Language Features (Volar) 插件 代码报红问题(可以把公用的 ts 类型定义封装起来公用)
+
+## 1.2.2
+
+`2021.12.21`
+
+- 🌟 更新 依赖更新最新版本
+- 🎯 优化 iframes 滚动条问题
+- 🎯 优化 部署后每次都要强制刷新清浏览器缓存问题
+- 🎉 新增 工具类百分比验证演示
+- 🐞 修复 [tag-view 标签右键会超出浏览器 #I4KN78](https://gitee.com/lyt-top/vue-next-admin/issues/I4KN78)
+
+## 1.2.1
+
+`2021.12.12`
+
+- 🌟 更新 依赖更新最新版本
+- 🎯 优化 cropper 裁剪时卡顿问题 [#I4M2VQ](https://gitee.com/lyt-top/vue-next-admin/issues/I4M2VQ)
+- 🎯 优化 Wangeditor 富文本编辑器的问题 [#I4LPC1](https://gitee.com/lyt-top/vue-next-admin/issues/I4LPC1)、[#I4LM7I](https://gitee.com/lyt-top/vue-next-admin/issues/I4LM7I)
+- 🐞 修复 浏览器标题问题
+- 🐞 修复 element plus svg 图标引入
+- 🐞 修复 工作流不可以拖线连接问题
+
+## 1.2.0
+
+`2021.11.28`
+
+- 🌟 更新 依赖更新最新版本
+- 🎯 优化 深色模式
+- 🎯 优化 `/@/utils` 文件夹,合并删除单一内容
+- 🎯 优化 系统设置:菜单管理(新增、修改)、角色管理(新增菜单权限)、用户管理、部门管理、字典管理
+- 🎯 优化 登录界面逻辑、权限管理逻辑
+- 🎯 优化 同步 [vue-next-admin-images](https://gitee.com/lyt-top/vue-next-admin-images/tree/master/menu) 后端控制菜单模拟数据
+- 🎉 新增 适配 Font Icon 向 SVG Icon 迁移(改动大,"element-plus": "^1.2.0-beta.4" 谨慎更新)
+- 🐞 修复 热更新问题,感谢@甜蜜蜜
+- 🐞 修复 页面/element 字体图标演示
+- 🐞 修复 功能/图标选择器演示,新增高级功能 [issues #I4GJXQ](https://gitee.com/lyt-top/vue-next-admin/issues/I4GJXQ)
+
+## 1.1.2
+
+`2021.10.17`
+
+- 🌟 更新 依赖更新最新版本
+- 🐞 修复 开启全屏时,刷新界面被还原成未全屏的状态
+- 🎯 优化 tagsView 右键菜单关闭逻辑
+- 🎯 优化 wangeditor 富文本编辑器(增加双向绑定)
+- 🎉 新增 工作流(暂不开源)
+- 🎉 新增 基础版 ts(不带国际化),切换 `vue-next-admin-template` 分支
+
+## 1.1.1
+
+`2021.09.25`
+
+- 🌟 更新 依赖更新最新版本(`"element-plus": "^1.1.0-beta.13"` 版本运行错误,`^1.1.0-beta.16`修复横向菜单卡死问题)
+- 🐞 修复 Dialog 弹窗位置错误、Drawer 抽屉内边距、el-menu 菜单收起时背景色问题
+- 🎯 优化 锁屏界面自动锁屏(s/秒)必须设置至少 1 秒
+- 🎉 新增 分栏布局,鼠标移入当前项时,显示当前项菜单内容
+- 🎉 新增 工作流(未完成)
+
+## 1.1.0
+
+`2021.09.10`
+
+- 🌟 更新 依赖更新最新版本
+- 🎯 优化 小屏模式下登录页二维码遮挡标题问题
+- 🎉 新增 图片验证器
+- 🎉 新增 动态复杂表单
+- 🎉 新增 工作流(未完成)
+- 🎉 新增 深色主题(伪深色,样式变动大,谨慎更新)
+
+## 1.0.18
+
+`2021.08.29`
+
+- 🌟 更新 依赖更新最新版本
+- 🎯 优化 权限组件去掉顶级 div(`/src/components/auth`)
+- 🎉 新增 布局配置添加恢复默认按钮
+- 🐞 修复 升级 <a href="https://element-plus.gitee.io/#/zh-CN/component/changelog" target="_blank">element plus 1.1.0-beta.7</a>后项目无法启动、el-menu 菜单
+- 🐞 修复 表格固定列时的层级、设置了相对定位时,遮挡左侧导航菜单问题
+
+## 1.0.17
+
+`2021.08.22`
+
+- 🌟 更新 依赖更新最新版本
+- 🎯 优化 去除设置布局切换,重置主题样式(initSetLayoutChange),切换布局需手动设置样式,设置的样式自动同步各布局
+- 🎯 优化 Dropdown 下拉菜单用户账号靠边时换行问题
+- 🎯 优化 左侧导航菜单,共用菜单树,防止 `布局配置` 设置 `菜单 / 顶栏` 时,样式丢失等问题
+- 🐞 修复 固定 header 后没有回到顶部的 bug,拉取项目后运行不起来的 bug。<a href="https://gitee.com/lyt-top/vue-next-admin/pulls/14" target="_blank">!14</a>,感谢<a href="https://gitee.com/wjs0509" target="_blank">@wjs0509</a>
+- 🐞 修复 tagView 右键全屏后,浏览器窗口大小发生任何变化都会导致左边菜单显示出来,并且可点击打开对应页面。<a href="https://gitee.com/lyt-top/vue-next-admin/issues/I46E6T" target="_blank">I46E6T</a>
+- 🐞 修复 默认设置 `菜单 / 顶栏` 样式不生效问题(/@/src/store/modules/themeConfig.ts)
+
+## 1.0.16
+
+`2021.08.14`
+
+- 🌟 更新 依赖更新最新版本
+- 🎯 优化 菜单高亮(详情且详情设置了 meta.isHide 时,顶级菜单高亮),感谢群友@YourObject
+- 🎯 优化 详情路径写法:如父级(/pages/filtering),那么详情为(/pages/filtering/details?id=1)。这样写可实现(详情时,父级菜单高亮),否则写成(/pages/filteringDetails?id=1)顶级菜单将不会高亮。可参考:`页面/过滤筛选组件`,点击当前图片进行测试
+- 🎯 优化 tagsView 右键菜单全屏时,打开的界面高度问题
+- 🎯 优化 图表批量 resize 问题
+- 🐞 修复 菜单收起时(设置全局主题:primary 且有二级菜单时),文字高亮颜色不对
+- 🐞 修复 国际化 <a href="https://gitee.com/lyt-top/vue-next-admin/issues/I43NPE" target="_blank">#I43NPE</a>。可参考:`页面/过滤筛选组件`,点击顶部语言切换,进行底部分页国际化查看
+
+## 1.0.15
+
+`2021.08.06`
+
+- 🌟 更新 依赖更新最新版本
+- 🎯 优化 tagsView 右键菜单点击时的字段名(id 已修改成 contextMenuClickId)与路由中返回的 id 名冲突问题,感谢群友@伯牙已遇钟子期
+- 🎉 新增 多个 form 表单验证界面演示
+
+## 1.0.14
+
+`2021.07.29`
+
+- 🌟 更新 依赖更新最新版本(vue、vuex、vue-router),出现问题,请手动降级。版本查看:<a href="https://www.npmjs.com/" target="_blank">vnpm</a>
+- 🎯 优化 数据可视化图表演示加载卡顿问题、优化有图表的演示界面
+- 🎯 优化 路由参数演示界面
+- 🎯 优化 tagsView 操作演示界面,由于存在相同路由多标签,必须要传全部参数值(query 或者 params)
+- 🎉 新增 开启 TagsView 共用,开启时:(多个路由菜单共用一个详情组件(参数为后点击的覆盖前面点击的),tagsView 中只会出现一个(不支持同时出现多个 tagsView 标签))。关闭时:(多个路由菜单共用一个详情组件,参数不同,会同时出现多个 tagsView 标签)
+- 🐞 修复 tagsView 共用(单标签)时,右键菜单功能点击,参数不对的问题(第 2n+个参数未覆盖第一个参数值)
+- 🐞 修复 多 tagsView 标签(参数不同)、单个 tagsView 标签公用(参数不同)所带来的刷新功能、横向自动滚动等问题
+- 🐞 修复 处理全屏若干问题,<a href="https://gitee.com/lyt-top/vue-next-admin/pulls/12" target="_blank">pr!12</a>,感谢群友@另一个前端
+
+## 1.0.13
+
+`2021.07.25`
+
+- 🌟 更新 依赖更新最新版本
+- 🎉 新增 数据可视化演示界面(/visualizingDemo1、/visualizingDemo2)
+- 🎉 新增 登录页扫码登录
+
+## 1.0.12
+
+`2021.07.16`
+
+- 🌟 更新 依赖更新最新版本
+- 🎉 新增 数据可视化演示空界面(待完善)
+- 🎯 优化 tagsView 动态路由(xxx/:id/:name)时的右键菜单刷新、关闭其它时参数丢失问题(2021.07.15 优化)
+- 🐞 修复 路由带参数时,复制路径到登录页,跳转后参数消失的问题
+- 🐞 修复 设置多个外链,点击后,页面内容停留在上一个内容(内容未改变)、国际化处理、打开新窗口 sessionStorage 共享等
+
+## 1.0.11
+
+`2021.07.14`
+
+- 🌟 更新 依赖更新最新版本
+- 🎉 新增 路由参数、图片懒加载界面演示
+- ⚠️ 警告 Form 表单 `binding value must be a string or number`,解决:加上 `label-position="top"` 不报警告(等待官方修复)
+- 🎯 优化 锁屏界面动画效果、首页图表显示
+- 🎯 优化 tagsView 右键菜单 `关闭` 功能逻辑
+- 🐞 修复 开启 TagsView 拖拽报错及小于 `1000px` 时自动设置禁止拖拽(<a href="https://gitee.com/lyt-top/vue-next-admin/issues/I3ZRRI" target="_blank">#I3ZRRI</a>)
+- 🐞 修复 `iframe 内嵌、外链` 高度问题,使用 computed 进行计算
+- 🐞 修复 默认布局开启 `侧边栏 Logo` 与关闭 `菜单水平折叠`,切换到横向布局时,菜单看不见的问题
+- 🐞 修复 切换不同布局时,再去开启 `经典布局分割菜单` 功能不生效问题
+- 🐞 修复 浏览器窗口标题中/英文切换不实时生效的问题
+- 🐞 修复 切换布局时,某些功能不可以使用。部分界面不需要取消事件监听(proxy.mittBus.off('xxx'))
+- 🐞 修复 动态路由带参数,router-link 跳转问题(<a href="hhttps://gitee.com/lyt-top/vue-next-admin/issues/I3YX6G" target="_blank">#I3YX6G</a>)
+- 🐞 修复 横向菜单有二级菜单时,点击子级菜单不高亮问题
+- 🐞 修复 功能 tagsView 操作演示不生效
+
+## 1.0.10
+
+`2021.07.07`
+
+- 🌟 更新 依赖更新最新版本(字体图标无问题)
+- 🎯 优化 内嵌 iframe、外链,解决 tagsView 刷新问题
+
+## 1.0.9
+
+`2021.07.02`
+
+- 🌟 更新 依赖更新最新版本
+- 🎯 优化 图标选择器设置宽度、v-model 等问题
+- 🎯 优化 滚动通知栏在手机上的体验
+- 🎯 优化 系统管理/新增菜单(编辑菜单),使用 `图标选择器` 进行模拟
+- 🎯 优化 字体图标(自动载入) 逻辑
+- 🐞 修复 screenfull 全屏时,按键盘 esc 键图标不改变问题,感谢群友@伯牙已遇钟子期
+
+## 1.0.8
+
+`2021.06.29`
+
+- 🌟 更新 依赖更新最新版本
+- 🎉 新增 表单中英文切换演示
+- 🎯 优化 登录页查看密码 icon 图标
+- 🎯 优化 图标选择器
+- 🎯 优化 拖动指令
+- 🐞 修复 form 表单在页面小于 576px 时的排版问题
+
+## 1.0.7
+
+`2021.06.24`
+
+- 🌟 更新 依赖更新最新版本
+- 🎉 新增 拖动指令及其演示界面
+- 🎯 优化 锁屏界面,解锁提示
+- 🎯 优化 登录页在手机上显示的效果
+
+## 1.0.6
+
+`2021.06.23`
+
+- 🎯 优化 去掉内嵌 iframe 内边距(padding)
+- 🎯 优化 城市多级联动组件
+- 🎯 优化 Tree 树形控件改成表格组件
+- 🐞 修复 Cascader 级联选择器高度问题
+
+## 1.0.5
+
+`2021.06.22`
+
+- 🌟 更新 vite 降级为@vite2.3.7,降级方法 `cnpm install vite@2.3.7`,防止 element plus 字体图标消失
+- 🐞 修复 开启后端控制路由(isRequestRoutes = true)时,内嵌 iframe、外链不可使用的问题
+
+## 1.0.4
+
+`2021.06.19`
+
+- 🌟 更新 依赖更新最新版本("vite": "^2.3.7")热更新无问题
+- 🎉 新增 深克隆工具,方便开发,感谢<a href="https://gitee.com/kangert" target="_blank">@kangert</a>(<a href="https://gitee.com/lyt-top/vue-next-admin/pulls/6" target="_blank">#6</a>)
+- 🎯 优化 vuex 模块自动导入。感谢<a href="https://gitee.com/kangert" target="_blank">@kangert</a>(<a href="https://gitee.com/lyt-top/vue-next-admin/pulls/4" target="_blank">#4</a>),感谢群友@web 小学生-第五君
+- 🎯 优化 类型定义提高编码体验,修复不能将类型“string | undefined”分配给类型“string”的问题。感谢<a href="https://gitee.com/kangert" target="_blank">@kangert</a>(<a href="https://gitee.com/lyt-top/vue-next-admin/pulls/5" target="_blank">#5</a>)
+- 🎯 优化 `layout` 文件夹移动到与 `views` 文件夹同级(改动较大,`/@/views/layout` 变成 `/@/layout`)
+- 🎯 优化 页面有 `console.log` 时 `eslint` 不生效问题
+- 🎯 优化 页面、ts 中 `any` 类型问题(改动较大)
+- 🎯 优化 登录页在手机上显示的效果
+- 🎯 优化 多行注释信息,鼠标放到方法名即可查看,更加直观的知道方法参数等。引入方法时需去掉以 `.ts` 结尾的后缀(改动较大)
+- 🎯 优化 移除 `utils/storage.ts` 下的旧写法(改动较大)
+- 🎯 优化 拆分 `router` 下内容,路由、前端、后端控制分开写,方便理解
+- 🐞 修复 鼠标移入顶部用户信息栏 `开/关全屏` 文字反向问题
+- 🐞 修复 热更新时,NextLoading(界面 loading) 不消失问题 `window.nextLoading === undefined`
+- 🐞 修复 vuex 中不可以使用 `/@/api/xxx` 下的接口调用问题
+
+## 1.0.3
+
+`2021.06.02`
+
+- ❄️ 删除 G6 思维导图界面
+- 🌟 更新 手动更新 vue、vue-router、vuex 到最近最多人使用的版本,出现不可预测的问题请降低版本。版本查看:<a href="https://www.npmjs.com/package/vue" target="_blank">vue 版本查看</a>
+- 🐞 修复 开启后端控制路由 `isRequestRoutes` 在非首页刷新页面后,回到首页的问题,感谢群友@伯牙已遇钟子期
+
+## 1.0.2
+
+`2021.06.01`
+
+- 🌟 更新 依赖更新最新版本
+- 🐞 修复 菜单搜索中文不可以搜索的问题,感谢群友@逍遥天意
+
+## 1.0.1
+
+`2021.05.31`
+
+- 🎉 新增 更新日志文件 `CHANGELOG.md`,以后每次更新都会在这里显示对应内容
+- 🌟 更新 依赖更新最新版本
+- 🐞 修复 分栏、经典布局路由设置 `meta.isHide` 为 `true` 时报错问题,感谢群友@29、@芭芭拉
+- 🐞 修复 经典布局点击 `tagsView` 左侧菜单数据不变问题
Added +32 -0
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..dc8466c
--- /dev/null
+++ b/index.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+	<head>
+		<meta charset="utf-8" />
+		<meta http-equiv="X-UA-Compatible" content="IE=edge" />
+		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
+		<meta
+			name="keywords"
+			content="vue-next-admin,vue-prev-admin,vue-admin-wonderful,后台管理系统一站式平台模板,希望可以帮你完成快速开发。vue2.x,vue2.0,vue2,vue3,vue3.x,vue3.0,CompositionAPI,typescript,element plus,element,plus,admin,wonderful,wonderful-next,vue-next-admin,vite,vite-admin,快速,高效,后台模板,后台系统,管理系统"
+		/>
+		<meta
+			name="description"
+			content="vue-next-admin,基于 vue3 + CompositionAPI + typescript + vite + element plus,适配手机、平板、pc 的后台开源免费管理系统模板!vue-prev-admin,基于 vue2 +  element ui,适配手机、平板、pc 的后台开源免费管理系统模板!"
+		/>
+		<link rel="icon" href="/favicon.ico" />
+		<title>vue-next-admin</title>
+	</head>
+	<body>
+		<div id="app"></div>
+		<script type="text/javascript">
+			var _hmt = _hmt || [];
+			(function () {
+				var hm = document.createElement('script');
+				hm.src = 'https://hm.baidu.com/hm.js?d9c8b87d10717013641458b300c552e4';
+				var s = document.getElementsByTagName('script')[0];
+				s.parentNode.insertBefore(hm, s);
+			})();
+		</script>
+		<script type="module" src="/src/main.ts"></script>
+		<script type="text/javascript" src="https://api.map.baidu.com/api?v=3.0&ak=wsijQt8sLXrCW71YesmispvYHitfG9gv&s=1"></script>
+	</body>
+</html>
Modified +2 -2
diff --git a/LICENSE b/LICENSE
index 4e647d5..6f6a7ea 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
 MIT License
 
-Copyright (c) 2023 方泽坤
+Copyright (c) 2021 lyt-Top
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
@@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
+SOFTWARE.
\ No newline at end of file
Added +7716 -0
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..aafc793
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,7716 @@
+{
+	"name": "vue-next-admin",
+	"version": "2.4.3",
+	"lockfileVersion": 2,
+	"requires": true,
+	"packages": {
+		"": {
+			"name": "vue-next-admin",
+			"version": "2.4.3",
+			"license": "MIT",
+			"dependencies": {
+				"@element-plus/icons-vue": "^2.0.10",
+				"@form-create/element-ui": "^3.1.18",
+				"@wangeditor/editor": "^5.1.23",
+				"@wangeditor/editor-for-vue": "^5.1.12",
+				"axios": "^1.3.3",
+				"countup.js": "^2.4.2",
+				"cropperjs": "^1.5.13",
+				"echarts": "^5.4.1",
+				"echarts-gl": "^2.0.9",
+				"echarts-wordcloud": "^2.1.0",
+				"element-plus": "^2.2.32",
+				"js-cookie": "^3.0.1",
+				"js-table2excel": "^1.0.3",
+				"jsplumb": "^2.15.6",
+				"mitt": "^3.0.0",
+				"nprogress": "^0.2.0",
+				"pinia": "^2.0.32",
+				"print-js": "^1.6.0",
+				"qrcodejs2-fixes": "^0.0.2",
+				"qs": "^6.11.0",
+				"screenfull": "^6.0.2",
+				"sortablejs": "^1.15.0",
+				"splitpanes": "^3.1.5",
+				"ts-enum-util": "^4.0.2",
+				"vue": "^3.2.47",
+				"vue-clipboard3": "^2.0.0",
+				"vue-grid-layout": "^3.0.0-beta1",
+				"vue-i18n": "^9.2.2",
+				"vue-router": "^4.1.6",
+				"vuedraggable": "^4.1.0"
+			},
+			"devDependencies": {
+				"@iconify/vue": "^4.1.0",
+				"@types/node": "^18.14.0",
+				"@types/nprogress": "^0.2.0",
+				"@types/sortablejs": "^1.15.0",
+				"@typescript-eslint/eslint-plugin": "^5.53.0",
+				"@typescript-eslint/parser": "^5.53.0",
+				"@vitejs/plugin-vue": "^4.0.0",
+				"@vue/compiler-sfc": "^3.2.47",
+				"autoprefixer": "^10.4.13",
+				"eslint": "^8.34.0",
+				"eslint-plugin-vue": "^9.9.0",
+				"postcss": "^8.4.21",
+				"prettier": "^2.8.4",
+				"sass": "^1.58.3",
+				"tailwindcss": "^3.2.7",
+				"typescript": "^4.9.5",
+				"vite": "^4.1.4",
+				"vite-plugin-vue-setup-extend": "^0.4.0",
+				"vue-eslint-parser": "^9.1.0"
+			},
+			"engines": {
+				"node": ">=16.0.0",
+				"npm": ">= 7.0.0"
+			}
+		},
+		"node_modules/@babel/parser": {
+			"version": "7.21.1",
+			"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.1.tgz",
+			"integrity": "sha512-JzhBFpkuhBNYUY7qs+wTzNmyCWUHEaAFpQQD2YfU1rPL38/L43Wvid0fFkiOCnHvsGncRZgEPyGnltABLcVDTg==",
+			"bin": {
+				"parser": "bin/babel-parser.js"
+			},
+			"engines": {
+				"node": ">=6.0.0"
+			}
+		},
+		"node_modules/@babel/runtime": {
+			"version": "7.21.0",
+			"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.0.tgz",
+			"integrity": "sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==",
+			"dependencies": {
+				"regenerator-runtime": "^0.13.11"
+			},
+			"engines": {
+				"node": ">=6.9.0"
+			}
+		},
+		"node_modules/@ctrl/tinycolor": {
+			"version": "3.6.0",
+			"resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.6.0.tgz",
+			"integrity": "sha512-/Z3l6pXthq0JvMYdUFyX9j0MaCltlIn6mfh9jLyQwg5aPKxkyNa0PTHtU1AlFXLNk55ZuAeJRcpvq+tmLfKmaQ==",
+			"engines": {
+				"node": ">=10"
+			}
+		},
+		"node_modules/@element-plus/icons-vue": {
+			"version": "2.0.10",
+			"resolved": "https://registry.npmjs.org/@element-plus/icons-vue/-/icons-vue-2.0.10.tgz",
+			"integrity": "sha512-ygEZ1mwPjcPo/OulhzLE7mtDrQBWI8vZzEWSNB2W/RNCRjoQGwbaK4N8lV4rid7Ts4qvySU3njMN7YCiSlSaTQ==",
+			"peerDependencies": {
+				"vue": "^3.2.0"
+			}
+		},
+		"node_modules/@esbuild/android-arm": {
+			"version": "0.16.17",
+			"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.16.17.tgz",
+			"integrity": "sha512-N9x1CMXVhtWEAMS7pNNONyA14f71VPQN9Cnavj1XQh6T7bskqiLLrSca4O0Vr8Wdcga943eThxnVp3JLnBMYtw==",
+			"cpu": [
+				"arm"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"android"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/android-arm64": {
+			"version": "0.16.17",
+			"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.16.17.tgz",
+			"integrity": "sha512-MIGl6p5sc3RDTLLkYL1MyL8BMRN4tLMRCn+yRJJmEDvYZ2M7tmAf80hx1kbNEUX2KJ50RRtxZ4JHLvCfuB6kBg==",
+			"cpu": [
+				"arm64"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"android"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/android-x64": {
+			"version": "0.16.17",
+			"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.16.17.tgz",
+			"integrity": "sha512-a3kTv3m0Ghh4z1DaFEuEDfz3OLONKuFvI4Xqczqx4BqLyuFaFkuaG4j2MtA6fuWEFeC5x9IvqnX7drmRq/fyAQ==",
+			"cpu": [
+				"x64"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"android"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/darwin-arm64": {
+			"version": "0.16.17",
+			"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.16.17.tgz",
+			"integrity": "sha512-/2agbUEfmxWHi9ARTX6OQ/KgXnOWfsNlTeLcoV7HSuSTv63E4DqtAc+2XqGw1KHxKMHGZgbVCZge7HXWX9Vn+w==",
+			"cpu": [
+				"arm64"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"darwin"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/darwin-x64": {
+			"version": "0.16.17",
+			"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.16.17.tgz",
+			"integrity": "sha512-2By45OBHulkd9Svy5IOCZt376Aa2oOkiE9QWUK9fe6Tb+WDr8hXL3dpqi+DeLiMed8tVXspzsTAvd0jUl96wmg==",
+			"cpu": [
+				"x64"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"darwin"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/freebsd-arm64": {
+			"version": "0.16.17",
+			"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.16.17.tgz",
+			"integrity": "sha512-mt+cxZe1tVx489VTb4mBAOo2aKSnJ33L9fr25JXpqQqzbUIw/yzIzi+NHwAXK2qYV1lEFp4OoVeThGjUbmWmdw==",
+			"cpu": [
+				"arm64"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"freebsd"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/freebsd-x64": {
+			"version": "0.16.17",
+			"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.16.17.tgz",
+			"integrity": "sha512-8ScTdNJl5idAKjH8zGAsN7RuWcyHG3BAvMNpKOBaqqR7EbUhhVHOqXRdL7oZvz8WNHL2pr5+eIT5c65kA6NHug==",
+			"cpu": [
+				"x64"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"freebsd"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/linux-arm": {
+			"version": "0.16.17",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.16.17.tgz",
+			"integrity": "sha512-iihzrWbD4gIT7j3caMzKb/RsFFHCwqqbrbH9SqUSRrdXkXaygSZCZg1FybsZz57Ju7N/SHEgPyaR0LZ8Zbe9gQ==",
+			"cpu": [
+				"arm"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/linux-arm64": {
+			"version": "0.16.17",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.16.17.tgz",
+			"integrity": "sha512-7S8gJnSlqKGVJunnMCrXHU9Q8Q/tQIxk/xL8BqAP64wchPCTzuM6W3Ra8cIa1HIflAvDnNOt2jaL17vaW+1V0g==",
+			"cpu": [
+				"arm64"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/linux-ia32": {
+			"version": "0.16.17",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.16.17.tgz",
+			"integrity": "sha512-kiX69+wcPAdgl3Lonh1VI7MBr16nktEvOfViszBSxygRQqSpzv7BffMKRPMFwzeJGPxcio0pdD3kYQGpqQ2SSg==",
+			"cpu": [
+				"ia32"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/linux-loong64": {
+			"version": "0.16.17",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.16.17.tgz",
+			"integrity": "sha512-dTzNnQwembNDhd654cA4QhbS9uDdXC3TKqMJjgOWsC0yNCbpzfWoXdZvp0mY7HU6nzk5E0zpRGGx3qoQg8T2DQ==",
+			"cpu": [
+				"loong64"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/linux-mips64el": {
+			"version": "0.16.17",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.16.17.tgz",
+			"integrity": "sha512-ezbDkp2nDl0PfIUn0CsQ30kxfcLTlcx4Foz2kYv8qdC6ia2oX5Q3E/8m6lq84Dj/6b0FrkgD582fJMIfHhJfSw==",
+			"cpu": [
+				"mips64el"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/linux-ppc64": {
+			"version": "0.16.17",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.16.17.tgz",
+			"integrity": "sha512-dzS678gYD1lJsW73zrFhDApLVdM3cUF2MvAa1D8K8KtcSKdLBPP4zZSLy6LFZ0jYqQdQ29bjAHJDgz0rVbLB3g==",
+			"cpu": [
+				"ppc64"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/linux-riscv64": {
+			"version": "0.16.17",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.16.17.tgz",
+			"integrity": "sha512-ylNlVsxuFjZK8DQtNUwiMskh6nT0vI7kYl/4fZgV1llP5d6+HIeL/vmmm3jpuoo8+NuXjQVZxmKuhDApK0/cKw==",
+			"cpu": [
+				"riscv64"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/linux-s390x": {
+			"version": "0.16.17",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.16.17.tgz",
+			"integrity": "sha512-gzy7nUTO4UA4oZ2wAMXPNBGTzZFP7mss3aKR2hH+/4UUkCOyqmjXiKpzGrY2TlEUhbbejzXVKKGazYcQTZWA/w==",
+			"cpu": [
+				"s390x"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/linux-x64": {
+			"version": "0.16.17",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.16.17.tgz",
+			"integrity": "sha512-mdPjPxfnmoqhgpiEArqi4egmBAMYvaObgn4poorpUaqmvzzbvqbowRllQ+ZgzGVMGKaPkqUmPDOOFQRUFDmeUw==",
+			"cpu": [
+				"x64"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/netbsd-x64": {
+			"version": "0.16.17",
+			"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.16.17.tgz",
+			"integrity": "sha512-/PzmzD/zyAeTUsduZa32bn0ORug+Jd1EGGAUJvqfeixoEISYpGnAezN6lnJoskauoai0Jrs+XSyvDhppCPoKOA==",
+			"cpu": [
+				"x64"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"netbsd"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/openbsd-x64": {
+			"version": "0.16.17",
+			"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.16.17.tgz",
+			"integrity": "sha512-2yaWJhvxGEz2RiftSk0UObqJa/b+rIAjnODJgv2GbGGpRwAfpgzyrg1WLK8rqA24mfZa9GvpjLcBBg8JHkoodg==",
+			"cpu": [
+				"x64"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"openbsd"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/sunos-x64": {
+			"version": "0.16.17",
+			"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.16.17.tgz",
+			"integrity": "sha512-xtVUiev38tN0R3g8VhRfN7Zl42YCJvyBhRKw1RJjwE1d2emWTVToPLNEQj/5Qxc6lVFATDiy6LjVHYhIPrLxzw==",
+			"cpu": [
+				"x64"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"sunos"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/win32-arm64": {
+			"version": "0.16.17",
+			"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.16.17.tgz",
+			"integrity": "sha512-ga8+JqBDHY4b6fQAmOgtJJue36scANy4l/rL97W+0wYmijhxKetzZdKOJI7olaBaMhWt8Pac2McJdZLxXWUEQw==",
+			"cpu": [
+				"arm64"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"win32"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/win32-ia32": {
+			"version": "0.16.17",
+			"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.16.17.tgz",
+			"integrity": "sha512-WnsKaf46uSSF/sZhwnqE4L/F89AYNMiD4YtEcYekBt9Q7nj0DiId2XH2Ng2PHM54qi5oPrQ8luuzGszqi/veig==",
+			"cpu": [
+				"ia32"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"win32"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/win32-x64": {
+			"version": "0.16.17",
+			"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.16.17.tgz",
+			"integrity": "sha512-y+EHuSchhL7FjHgvQL/0fnnFmO4T1bhvWANX6gcnqTjtnKWbTvUMCpGnv2+t+31d7RzyEAYAd4u2fnIhHL6N/Q==",
+			"cpu": [
+				"x64"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"win32"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@eslint/eslintrc": {
+			"version": "1.4.1",
+			"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz",
+			"integrity": "sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==",
+			"dev": true,
+			"dependencies": {
+				"ajv": "^6.12.4",
+				"debug": "^4.3.2",
+				"espree": "^9.4.0",
+				"globals": "^13.19.0",
+				"ignore": "^5.2.0",
+				"import-fresh": "^3.2.1",
+				"js-yaml": "^4.1.0",
+				"minimatch": "^3.1.2",
+				"strip-json-comments": "^3.1.1"
+			},
+			"engines": {
+				"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+			},
+			"funding": {
+				"url": "https://opencollective.com/eslint"
+			}
+		},
+		"node_modules/@floating-ui/core": {
+			"version": "1.2.1",
+			"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.2.1.tgz",
+			"integrity": "sha512-LSqwPZkK3rYfD7GKoIeExXOyYx6Q1O4iqZWwIehDNuv3Dv425FIAE8PRwtAx1imEolFTHgBEcoFHm9MDnYgPCg=="
+		},
+		"node_modules/@floating-ui/dom": {
+			"version": "1.2.1",
+			"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.2.1.tgz",
+			"integrity": "sha512-Rt45SmRiV8eU+xXSB9t0uMYiQ/ZWGE/jumse2o3i5RGlyvcbqOF4q+1qBnzLE2kZ5JGhq0iMkcGXUKbFe7MpTA==",
+			"dependencies": {
+				"@floating-ui/core": "^1.2.1"
+			}
+		},
+		"node_modules/@form-create/component-elm-checkbox": {
+			"version": "3.1.15",
+			"resolved": "https://registry.npmmirror.com/@form-create/component-elm-checkbox/-/component-elm-checkbox-3.1.15.tgz",
+			"integrity": "sha512-bAl3k0p76wwMX0OxeR8KAEiUl2RP1Jl1kAqbjD762EcSXGgaXgP94v9ag1JhUoiwZkSKWl6IF+e/utjEonRSFw==",
+			"dependencies": {
+				"@form-create/utils": "^3.1.15"
+			}
+		},
+		"node_modules/@form-create/component-elm-frame": {
+			"version": "3.1.15",
+			"resolved": "https://registry.npmmirror.com/@form-create/component-elm-frame/-/component-elm-frame-3.1.15.tgz",
+			"integrity": "sha512-78WbMpMLTYwTo3QP7Fa+N2VF/u1vmRuwDmiobtBtVg9EE7m6//bQV96ibnLNXX27MD41gIg+o1GUJtx/qxZ8Ew==",
+			"dependencies": {
+				"@form-create/utils": "^3.1.15"
+			}
+		},
+		"node_modules/@form-create/component-elm-group": {
+			"version": "3.1.15",
+			"resolved": "https://registry.npmmirror.com/@form-create/component-elm-group/-/component-elm-group-3.1.15.tgz",
+			"integrity": "sha512-V/oVxHf9rHqqMHRV0XRVED9EYFCVZ8Tv/EbbIN/4rORruHtzdQOH8I0QHQ9T4vZO9Q4eblApzcJb8Y7bbHX23Q==",
+			"dependencies": {
+				"@form-create/utils": "^3.1.15"
+			}
+		},
+		"node_modules/@form-create/component-elm-radio": {
+			"version": "3.1.15",
+			"resolved": "https://registry.npmmirror.com/@form-create/component-elm-radio/-/component-elm-radio-3.1.15.tgz",
+			"integrity": "sha512-qAf1VlhrUvMlgzkz6BYLRn0UOLahkTI/cbzt8nkp5PL1oGoSXp5xqIOtHMjbnGEkEXW57kjRKI5Q7UjzIhYmvQ==",
+			"dependencies": {
+				"@form-create/utils": "^3.1.15"
+			}
+		},
+		"node_modules/@form-create/component-elm-select": {
+			"version": "3.1.15",
+			"resolved": "https://registry.npmmirror.com/@form-create/component-elm-select/-/component-elm-select-3.1.15.tgz",
+			"integrity": "sha512-ZnvPn/TGqgFDed7bKabjRvlwlQ8RYq5WCG9Iy63d2igC8577tv5QTI2rNvfGfxxH/254MPwgNlA94JukxzEOTw==",
+			"dependencies": {
+				"@form-create/utils": "^3.1.15"
+			}
+		},
+		"node_modules/@form-create/component-elm-tree": {
+			"version": "3.1.15",
+			"resolved": "https://registry.npmmirror.com/@form-create/component-elm-tree/-/component-elm-tree-3.1.15.tgz",
+			"integrity": "sha512-4VPN406A8Mvannn8P/2DvStqDYFfDHZ+ILG/0JndOyfe+GdYdSA5SwD3LfC4zD6AhpGrkOJXbd4YYXlBUtzt8g==",
+			"dependencies": {
+				"@form-create/utils": "^3.1.15"
+			}
+		},
+		"node_modules/@form-create/component-elm-upload": {
+			"version": "3.1.18",
+			"resolved": "https://registry.npmmirror.com/@form-create/component-elm-upload/-/component-elm-upload-3.1.18.tgz",
+			"integrity": "sha512-+jHZ1vfNusEqKSvpLpFcKbF0lE7S+YuWtAturzW2O5K+qYDw6SmGyB3/XeYqVXN9rfne0rqMbyYw6CGglVe7nw==",
+			"dependencies": {
+				"@form-create/utils": "^3.1.15"
+			}
+		},
+		"node_modules/@form-create/component-subform": {
+			"version": "3.1.5",
+			"resolved": "https://registry.npmmirror.com/@form-create/component-subform/-/component-subform-3.1.5.tgz",
+			"integrity": "sha512-JHNEFGuwpnjGvCJ0I0GCqPL5al0qXoN4ymnRBpm+oL+6MMo5bz1kUyoqMX1MutuC96gHTqpeqc67hssi8g2mIw=="
+		},
+		"node_modules/@form-create/core": {
+			"version": "3.1.18",
+			"resolved": "https://registry.npmmirror.com/@form-create/core/-/core-3.1.18.tgz",
+			"integrity": "sha512-kFu+fH88KvdkJoMPuLaBOG/4jNv+YPJbJHzvF4HZLn9XEtJgO9flef5WcvMyz12moI6uNGJ4Hs8+sdkXBJtkMw==",
+			"dependencies": {
+				"@form-create/utils": "^3.1.15"
+			},
+			"peerDependencies": {
+				"vue": "^3.1.0"
+			}
+		},
+		"node_modules/@form-create/element-ui": {
+			"version": "3.1.18",
+			"resolved": "https://registry.npmmirror.com/@form-create/element-ui/-/element-ui-3.1.18.tgz",
+			"integrity": "sha512-FstZrrfUBm9cRipqi6BW3cTPShQsgDWaHXYA5Uj/3fQ5y5EuoSRkNnlgqEoAjwUsQznL4tMC4BzqxoAHsP9zlg==",
+			"dependencies": {
+				"@form-create/component-elm-checkbox": "^3.1.15",
+				"@form-create/component-elm-frame": "^3.1.15",
+				"@form-create/component-elm-group": "^3.1.15",
+				"@form-create/component-elm-radio": "^3.1.15",
+				"@form-create/component-elm-select": "^3.1.15",
+				"@form-create/component-elm-tree": "^3.1.15",
+				"@form-create/component-elm-upload": "^3.1.18",
+				"@form-create/component-subform": "^3.1.5",
+				"@form-create/core": "^3.1.18",
+				"@form-create/utils": "^3.1.15"
+			},
+			"peerDependencies": {
+				"vue": "^3.1.0"
+			}
+		},
+		"node_modules/@form-create/utils": {
+			"version": "3.1.15",
+			"resolved": "https://registry.npmmirror.com/@form-create/utils/-/utils-3.1.15.tgz",
+			"integrity": "sha512-tP6Z/c2XC6OYrI8D9/XWvJc2h6apsyMFTy051sY+tCcxppqyR7dBEEXmgfWOrAr980N7k10g27kwJ9TdVn+bfw=="
+		},
+		"node_modules/@humanwhocodes/config-array": {
+			"version": "0.11.8",
+			"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz",
+			"integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==",
+			"dev": true,
+			"dependencies": {
+				"@humanwhocodes/object-schema": "^1.2.1",
+				"debug": "^4.1.1",
+				"minimatch": "^3.0.5"
+			},
+			"engines": {
+				"node": ">=10.10.0"
+			}
+		},
+		"node_modules/@humanwhocodes/module-importer": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+			"integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+			"dev": true,
+			"engines": {
+				"node": ">=12.22"
+			},
+			"funding": {
+				"type": "github",
+				"url": "https://github.com/sponsors/nzakas"
+			}
+		},
+		"node_modules/@humanwhocodes/object-schema": {
+			"version": "1.2.1",
+			"resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz",
+			"integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
+			"dev": true
+		},
+		"node_modules/@iconify/types": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmmirror.com/@iconify/types/-/types-2.0.0.tgz",
+			"integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==",
+			"dev": true
+		},
+		"node_modules/@iconify/vue": {
+			"version": "4.1.0",
+			"resolved": "https://registry.npmmirror.com/@iconify/vue/-/vue-4.1.0.tgz",
+			"integrity": "sha512-rBQVxNoSDooqgWkQg2MqkIHkH/huNuvXGqui5wijc1zLnU7TKzbBHW9VGmbnV4asNTmIHmqV4Nvt0M2rZ/9nHA==",
+			"dev": true,
+			"dependencies": {
+				"@iconify/types": "^2.0.0"
+			},
+			"peerDependencies": {
+				"vue": ">=3"
+			}
+		},
+		"node_modules/@interactjs/actions": {
+			"version": "1.10.17",
+			"resolved": "https://registry.npmjs.org/@interactjs/actions/-/actions-1.10.17.tgz",
+			"integrity": "sha512-wyB1ZqpaZy5gmz6VDqK9KWh98xKnFgL7VyLvxHODFi9V0IYX4HJAAOBlhtfze0D1R1f1cY+gqPDK+dLaHMlE+w==",
+			"optionalDependencies": {
+				"@interactjs/interact": "1.10.17"
+			},
+			"peerDependencies": {
+				"@interactjs/core": "1.10.17",
+				"@interactjs/utils": "1.10.17"
+			}
+		},
+		"node_modules/@interactjs/auto-scroll": {
+			"version": "1.10.17",
+			"resolved": "https://registry.npmjs.org/@interactjs/auto-scroll/-/auto-scroll-1.10.17.tgz",
+			"integrity": "sha512-IQcW7N3xOaoL8RnAGOGMk0Y2gue7L4S3BT6Id4VBBu8so163DtLiZVW6jXu9rKVntzbluaAeqNZlfAVyu3kIWg==",
+			"optionalDependencies": {
+				"@interactjs/interact": "1.10.17"
+			},
+			"peerDependencies": {
+				"@interactjs/utils": "1.10.17"
+			}
+		},
+		"node_modules/@interactjs/auto-start": {
+			"version": "1.10.17",
+			"resolved": "https://registry.npmjs.org/@interactjs/auto-start/-/auto-start-1.10.17.tgz",
+			"integrity": "sha512-qYVxhAbYnwxjD/NLEegUoAST7WASJ4VmWNjsyWRx/js5Op+I4E2zteARIeZGgrutcGIXMCcQzhCMgE3PjOpbpw==",
+			"optionalDependencies": {
+				"@interactjs/interact": "1.10.17"
+			},
+			"peerDependencies": {
+				"@interactjs/core": "1.10.17",
+				"@interactjs/utils": "1.10.17"
+			}
+		},
+		"node_modules/@interactjs/core": {
+			"version": "1.10.17",
+			"resolved": "https://registry.npmjs.org/@interactjs/core/-/core-1.10.17.tgz",
+			"integrity": "sha512-rL9w+83HDRuXub8Ezqs+97CYLl/ne7bLT/sAeduUWaxYhsW9iOqBoob9JnkkCZOaOsYizWI1EWy0+fNc5ibtLQ==",
+			"peerDependencies": {
+				"@interactjs/utils": "1.10.17"
+			}
+		},
+		"node_modules/@interactjs/dev-tools": {
+			"version": "1.10.17",
+			"resolved": "https://registry.npmjs.org/@interactjs/dev-tools/-/dev-tools-1.10.17.tgz",
+			"integrity": "sha512-Oi9nEw3FfSwkNmW+V0WwdHqvzEkVHc24mH1v5EjRn60sqgrGLK9nTQ+NSuqcnUY8GxC3TkyuxnsOodxiadIRmA==",
+			"optionalDependencies": {
+				"@interactjs/interact": "1.10.17"
+			},
+			"peerDependencies": {
+				"@interactjs/modifiers": "1.10.17",
+				"@interactjs/utils": "1.10.17"
+			}
+		},
+		"node_modules/@interactjs/inertia": {
+			"version": "1.10.17",
+			"resolved": "https://registry.npmjs.org/@interactjs/inertia/-/inertia-1.10.17.tgz",
+			"integrity": "sha512-41vbYUjZIDCKt2/yhmjPrEW5+0uoL/hldFsll9pkvnLhmm12Xk0VXOlmR2zXKAmsTK3fJlKMyBYUX92qHLkyVQ==",
+			"dependencies": {
+				"@interactjs/offset": "1.10.17"
+			},
+			"optionalDependencies": {
+				"@interactjs/interact": "1.10.17"
+			},
+			"peerDependencies": {
+				"@interactjs/core": "1.10.17",
+				"@interactjs/modifiers": "1.10.17",
+				"@interactjs/utils": "1.10.17"
+			}
+		},
+		"node_modules/@interactjs/interact": {
+			"version": "1.10.17",
+			"resolved": "https://registry.npmjs.org/@interactjs/interact/-/interact-1.10.17.tgz",
+			"integrity": "sha512-NyKsf8EFudvdahBjPz1Gt5QnynVwa/2LUfBc2/w8QOnOBiyzUm0HLloJSaB8a50QbQkSWN243/Lgpd8GTMQvuQ==",
+			"dependencies": {
+				"@interactjs/core": "1.10.17",
+				"@interactjs/types": "1.10.17",
+				"@interactjs/utils": "1.10.17"
+			}
+		},
+		"node_modules/@interactjs/interactjs": {
+			"version": "1.10.17",
+			"resolved": "https://registry.npmjs.org/@interactjs/interactjs/-/interactjs-1.10.17.tgz",
+			"integrity": "sha512-hHmiukARbZhiM12zNKx0yQlFVl4C+NMeYNAYh6Mf9U3ZziQ47C+JEW8Gr7Zr/MxfNZyPu5nLKCpVQjh/JvBO9g==",
+			"dependencies": {
+				"@interactjs/actions": "1.10.17",
+				"@interactjs/auto-scroll": "1.10.17",
+				"@interactjs/auto-start": "1.10.17",
+				"@interactjs/core": "1.10.17",
+				"@interactjs/dev-tools": "1.10.17",
+				"@interactjs/inertia": "1.10.17",
+				"@interactjs/interact": "1.10.17",
+				"@interactjs/modifiers": "1.10.17",
+				"@interactjs/offset": "1.10.17",
+				"@interactjs/pointer-events": "1.10.17",
+				"@interactjs/reflow": "1.10.17",
+				"@interactjs/utils": "1.10.17"
+			}
+		},
+		"node_modules/@interactjs/modifiers": {
+			"version": "1.10.17",
+			"resolved": "https://registry.npmjs.org/@interactjs/modifiers/-/modifiers-1.10.17.tgz",
+			"integrity": "sha512-Dxw8kv9VBIxnhNvQncR6CKAGMzKXczLvuAUIdSPFYtyerX/XiDulJUqhR+jVKNp/WjF1DvdBxWo0kGGLbM84LQ==",
+			"dependencies": {
+				"@interactjs/snappers": "1.10.17"
+			},
+			"optionalDependencies": {
+				"@interactjs/interact": "1.10.17"
+			},
+			"peerDependencies": {
+				"@interactjs/core": "1.10.17",
+				"@interactjs/utils": "1.10.17"
+			}
+		},
+		"node_modules/@interactjs/offset": {
+			"version": "1.10.17",
+			"resolved": "https://registry.npmjs.org/@interactjs/offset/-/offset-1.10.17.tgz",
+			"integrity": "sha512-wWBnIQWgLrmJNTBbd/FdxHxAJjiXl/5ND8Jbw2DuP9gIGDxhFSdEt62Fgqimn9ICb8v8ycvSLObEmcvJF/8hQQ==",
+			"optionalDependencies": {
+				"@interactjs/interact": "1.10.17"
+			},
+			"peerDependencies": {
+				"@interactjs/core": "1.10.17",
+				"@interactjs/utils": "1.10.17"
+			}
+		},
+		"node_modules/@interactjs/pointer-events": {
+			"version": "1.10.17",
+			"resolved": "https://registry.npmjs.org/@interactjs/pointer-events/-/pointer-events-1.10.17.tgz",
+			"integrity": "sha512-VsfluouEKb8QRGyH6jQATCW+QdAd/3dkENS7rj2m+EcVUhz2Ob5mpMRopjALi4pwltMowqTfuJ4LtwMSX2G29A==",
+			"optionalDependencies": {
+				"@interactjs/interact": "1.10.17"
+			},
+			"peerDependencies": {
+				"@interactjs/core": "1.10.17",
+				"@interactjs/utils": "1.10.17"
+			}
+		},
+		"node_modules/@interactjs/reflow": {
+			"version": "1.10.17",
+			"resolved": "https://registry.npmjs.org/@interactjs/reflow/-/reflow-1.10.17.tgz",
+			"integrity": "sha512-ncpWP5k93FRQptEhjzPZsbuRRajd4rkW17lDavCrEjrDi/LHnYekWGqZTaFzfJ80n1x8xUm9ujDjxCTylNqEIA==",
+			"optionalDependencies": {
+				"@interactjs/interact": "1.10.17"
+			},
+			"peerDependencies": {
+				"@interactjs/core": "1.10.17",
+				"@interactjs/utils": "1.10.17"
+			}
+		},
+		"node_modules/@interactjs/snappers": {
+			"version": "1.10.17",
+			"resolved": "https://registry.npmjs.org/@interactjs/snappers/-/snappers-1.10.17.tgz",
+			"integrity": "sha512-m753DGsNOts797e3zDT6wqELoc+BlpIC1w+TyMyISRxU6n1RlS8Q6LHBGgwAgV79LHLaahv/a5haFF9H1VG0FQ==",
+			"optionalDependencies": {
+				"@interactjs/interact": "1.10.17"
+			},
+			"peerDependencies": {
+				"@interactjs/utils": "1.10.17"
+			}
+		},
+		"node_modules/@interactjs/types": {
+			"version": "1.10.17",
+			"resolved": "https://registry.npmjs.org/@interactjs/types/-/types-1.10.17.tgz",
+			"integrity": "sha512-X2JpoM7xUw0p9Me0tMaI0HNfcF/Hd07ZZlzpnpEMpGerUZOLoyeThrV9P+CrBHxZrluWJrigJbcdqXliFd0YMA=="
+		},
+		"node_modules/@interactjs/utils": {
+			"version": "1.10.17",
+			"resolved": "https://registry.npmjs.org/@interactjs/utils/-/utils-1.10.17.tgz",
+			"integrity": "sha512-sZAW08CkqgvqRjUIaLRjScjObcCzN9D75yekLA21EClYAZIhi4A+GEt2z/WqOCOksTaEPLYmQyhkpXcboc0LhQ=="
+		},
+		"node_modules/@intlify/core-base": {
+			"version": "9.2.2",
+			"resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.2.2.tgz",
+			"integrity": "sha512-JjUpQtNfn+joMbrXvpR4hTF8iJQ2sEFzzK3KIESOx+f+uwIjgw20igOyaIdhfsVVBCds8ZM64MoeNSx+PHQMkA==",
+			"dependencies": {
+				"@intlify/devtools-if": "9.2.2",
+				"@intlify/message-compiler": "9.2.2",
+				"@intlify/shared": "9.2.2",
+				"@intlify/vue-devtools": "9.2.2"
+			},
+			"engines": {
+				"node": ">= 14"
+			}
+		},
+		"node_modules/@intlify/devtools-if": {
+			"version": "9.2.2",
+			"resolved": "https://registry.npmjs.org/@intlify/devtools-if/-/devtools-if-9.2.2.tgz",
+			"integrity": "sha512-4ttr/FNO29w+kBbU7HZ/U0Lzuh2cRDhP8UlWOtV9ERcjHzuyXVZmjyleESK6eVP60tGC9QtQW9yZE+JeRhDHkg==",
+			"dependencies": {
+				"@intlify/shared": "9.2.2"
+			},
+			"engines": {
+				"node": ">= 14"
+			}
+		},
+		"node_modules/@intlify/message-compiler": {
+			"version": "9.2.2",
+			"resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.2.2.tgz",
+			"integrity": "sha512-IUrQW7byAKN2fMBe8z6sK6riG1pue95e5jfokn8hA5Q3Bqy4MBJ5lJAofUsawQJYHeoPJ7svMDyBaVJ4d0GTtA==",
+			"dependencies": {
+				"@intlify/shared": "9.2.2",
+				"source-map": "0.6.1"
+			},
+			"engines": {
+				"node": ">= 14"
+			}
+		},
+		"node_modules/@intlify/shared": {
+			"version": "9.2.2",
+			"resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.2.2.tgz",
+			"integrity": "sha512-wRwTpsslgZS5HNyM7uDQYZtxnbI12aGiBZURX3BTR9RFIKKRWpllTsgzHWvj3HKm3Y2Sh5LPC1r0PDCKEhVn9Q==",
+			"engines": {
+				"node": ">= 14"
+			}
+		},
+		"node_modules/@intlify/vue-devtools": {
+			"version": "9.2.2",
+			"resolved": "https://registry.npmjs.org/@intlify/vue-devtools/-/vue-devtools-9.2.2.tgz",
+			"integrity": "sha512-+dUyqyCHWHb/UcvY1MlIpO87munedm3Gn6E9WWYdWrMuYLcoIoOEVDWSS8xSwtlPU+kA+MEQTP6Q1iI/ocusJg==",
+			"dependencies": {
+				"@intlify/core-base": "9.2.2",
+				"@intlify/shared": "9.2.2"
+			},
+			"engines": {
+				"node": ">= 14"
+			}
+		},
+		"node_modules/@nodelib/fs.scandir": {
+			"version": "2.1.5",
+			"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+			"integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+			"dev": true,
+			"dependencies": {
+				"@nodelib/fs.stat": "2.0.5",
+				"run-parallel": "^1.1.9"
+			},
+			"engines": {
+				"node": ">= 8"
+			}
+		},
+		"node_modules/@nodelib/fs.stat": {
+			"version": "2.0.5",
+			"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+			"integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+			"dev": true,
+			"engines": {
+				"node": ">= 8"
+			}
+		},
+		"node_modules/@nodelib/fs.walk": {
+			"version": "1.2.8",
+			"resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+			"integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+			"dev": true,
+			"dependencies": {
+				"@nodelib/fs.scandir": "2.1.5",
+				"fastq": "^1.6.0"
+			},
+			"engines": {
+				"node": ">= 8"
+			}
+		},
+		"node_modules/@popperjs/core": {
+			"name": "@sxzz/popperjs-es",
+			"version": "2.11.7",
+			"resolved": "https://registry.npmjs.org/@sxzz/popperjs-es/-/popperjs-es-2.11.7.tgz",
+			"integrity": "sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ==",
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/popperjs"
+			}
+		},
+		"node_modules/@transloadit/prettier-bytes": {
+			"version": "0.0.7",
+			"resolved": "https://registry.npmjs.org/@transloadit/prettier-bytes/-/prettier-bytes-0.0.7.tgz",
+			"integrity": "sha512-VeJbUb0wEKbcwaSlj5n+LscBl9IPgLPkHVGBkh00cztv6X4L/TJXK58LzFuBKX7/GAfiGhIwH67YTLTlzvIzBA=="
+		},
+		"node_modules/@types/event-emitter": {
+			"version": "0.3.3",
+			"resolved": "https://registry.npmjs.org/@types/event-emitter/-/event-emitter-0.3.3.tgz",
+			"integrity": "sha512-UfnOK1pIxO7P+EgPRZXD9jMpimd8QEFcEZ5R67R1UhGbv4zghU5+NE7U8M8G9H5Jc8FI51rqDWQs6FtUfq2e/Q=="
+		},
+		"node_modules/@types/json-schema": {
+			"version": "7.0.11",
+			"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
+			"integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==",
+			"dev": true
+		},
+		"node_modules/@types/lodash": {
+			"version": "4.14.191",
+			"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.191.tgz",
+			"integrity": "sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ=="
+		},
+		"node_modules/@types/lodash-es": {
+			"version": "4.17.6",
+			"resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.6.tgz",
+			"integrity": "sha512-R+zTeVUKDdfoRxpAryaQNRKk3105Rrgx2CFRClIgRGaqDTdjsm8h6IYA8ir584W3ePzkZfst5xIgDwYrlh9HLg==",
+			"dependencies": {
+				"@types/lodash": "*"
+			}
+		},
+		"node_modules/@types/node": {
+			"version": "18.14.0",
+			"resolved": "https://registry.npmjs.org/@types/node/-/node-18.14.0.tgz",
+			"integrity": "sha512-5EWrvLmglK+imbCJY0+INViFWUHg1AHel1sq4ZVSfdcNqGy9Edv3UB9IIzzg+xPaUcAgZYcfVs2fBcwDeZzU0A==",
+			"dev": true
+		},
+		"node_modules/@types/nprogress": {
+			"version": "0.2.0",
+			"resolved": "https://registry.npmjs.org/@types/nprogress/-/nprogress-0.2.0.tgz",
+			"integrity": "sha512-1cYJrqq9GezNFPsWTZpFut/d4CjpZqA0vhqDUPFWYKF1oIyBz5qnoYMzR+0C/T96t3ebLAC1SSnwrVOm5/j74A==",
+			"dev": true
+		},
+		"node_modules/@types/semver": {
+			"version": "7.3.13",
+			"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz",
+			"integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==",
+			"dev": true
+		},
+		"node_modules/@types/sortablejs": {
+			"version": "1.15.0",
+			"resolved": "https://registry.npmjs.org/@types/sortablejs/-/sortablejs-1.15.0.tgz",
+			"integrity": "sha512-qrhtM7M41EhH4tZQTNw2/RJkxllBx3reiJpTbgWCM2Dx0U1sZ6LwKp9lfNln9uqE26ZMKUaPEYaD4rzvOWYtZw==",
+			"dev": true
+		},
+		"node_modules/@types/web-bluetooth": {
+			"version": "0.0.16",
+			"resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.16.tgz",
+			"integrity": "sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ=="
+		},
+		"node_modules/@typescript-eslint/eslint-plugin": {
+			"version": "5.53.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.53.0.tgz",
+			"integrity": "sha512-alFpFWNucPLdUOySmXCJpzr6HKC3bu7XooShWM+3w/EL6J2HIoB2PFxpLnq4JauWVk6DiVeNKzQlFEaE+X9sGw==",
+			"dev": true,
+			"dependencies": {
+				"@typescript-eslint/scope-manager": "5.53.0",
+				"@typescript-eslint/type-utils": "5.53.0",
+				"@typescript-eslint/utils": "5.53.0",
+				"debug": "^4.3.4",
+				"grapheme-splitter": "^1.0.4",
+				"ignore": "^5.2.0",
+				"natural-compare-lite": "^1.4.0",
+				"regexpp": "^3.2.0",
+				"semver": "^7.3.7",
+				"tsutils": "^3.21.0"
+			},
+			"engines": {
+				"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			},
+			"peerDependencies": {
+				"@typescript-eslint/parser": "^5.0.0",
+				"eslint": "^6.0.0 || ^7.0.0 || ^8.0.0"
+			},
+			"peerDependenciesMeta": {
+				"typescript": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/@typescript-eslint/parser": {
+			"version": "5.53.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.53.0.tgz",
+			"integrity": "sha512-MKBw9i0DLYlmdOb3Oq/526+al20AJZpANdT6Ct9ffxcV8nKCHz63t/S0IhlTFNsBIHJv+GY5SFJ0XfqVeydQrQ==",
+			"dev": true,
+			"dependencies": {
+				"@typescript-eslint/scope-manager": "5.53.0",
+				"@typescript-eslint/types": "5.53.0",
+				"@typescript-eslint/typescript-estree": "5.53.0",
+				"debug": "^4.3.4"
+			},
+			"engines": {
+				"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			},
+			"peerDependencies": {
+				"eslint": "^6.0.0 || ^7.0.0 || ^8.0.0"
+			},
+			"peerDependenciesMeta": {
+				"typescript": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/@typescript-eslint/scope-manager": {
+			"version": "5.53.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.53.0.tgz",
+			"integrity": "sha512-Opy3dqNsp/9kBBeCPhkCNR7fmdSQqA+47r21hr9a14Bx0xnkElEQmhoHga+VoaoQ6uDHjDKmQPIYcUcKJifS7w==",
+			"dev": true,
+			"dependencies": {
+				"@typescript-eslint/types": "5.53.0",
+				"@typescript-eslint/visitor-keys": "5.53.0"
+			},
+			"engines": {
+				"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			}
+		},
+		"node_modules/@typescript-eslint/type-utils": {
+			"version": "5.53.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.53.0.tgz",
+			"integrity": "sha512-HO2hh0fmtqNLzTAme/KnND5uFNwbsdYhCZghK2SoxGp3Ifn2emv+hi0PBUjzzSh0dstUIFqOj3bp0AwQlK4OWw==",
+			"dev": true,
+			"dependencies": {
+				"@typescript-eslint/typescript-estree": "5.53.0",
+				"@typescript-eslint/utils": "5.53.0",
+				"debug": "^4.3.4",
+				"tsutils": "^3.21.0"
+			},
+			"engines": {
+				"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			},
+			"peerDependencies": {
+				"eslint": "*"
+			},
+			"peerDependenciesMeta": {
+				"typescript": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/@typescript-eslint/types": {
+			"version": "5.53.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.53.0.tgz",
+			"integrity": "sha512-5kcDL9ZUIP756K6+QOAfPkigJmCPHcLN7Zjdz76lQWWDdzfOhZDTj1irs6gPBKiXx5/6O3L0+AvupAut3z7D2A==",
+			"dev": true,
+			"engines": {
+				"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			}
+		},
+		"node_modules/@typescript-eslint/typescript-estree": {
+			"version": "5.53.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.53.0.tgz",
+			"integrity": "sha512-eKmipH7QyScpHSkhbptBBYh9v8FxtngLquq292YTEQ1pxVs39yFBlLC1xeIZcPPz1RWGqb7YgERJRGkjw8ZV7w==",
+			"dev": true,
+			"dependencies": {
+				"@typescript-eslint/types": "5.53.0",
+				"@typescript-eslint/visitor-keys": "5.53.0",
+				"debug": "^4.3.4",
+				"globby": "^11.1.0",
+				"is-glob": "^4.0.3",
+				"semver": "^7.3.7",
+				"tsutils": "^3.21.0"
+			},
+			"engines": {
+				"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			},
+			"peerDependenciesMeta": {
+				"typescript": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/@typescript-eslint/utils": {
+			"version": "5.53.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.53.0.tgz",
+			"integrity": "sha512-VUOOtPv27UNWLxFwQK/8+7kvxVC+hPHNsJjzlJyotlaHjLSIgOCKj9I0DBUjwOOA64qjBwx5afAPjksqOxMO0g==",
+			"dev": true,
+			"dependencies": {
+				"@types/json-schema": "^7.0.9",
+				"@types/semver": "^7.3.12",
+				"@typescript-eslint/scope-manager": "5.53.0",
+				"@typescript-eslint/types": "5.53.0",
+				"@typescript-eslint/typescript-estree": "5.53.0",
+				"eslint-scope": "^5.1.1",
+				"eslint-utils": "^3.0.0",
+				"semver": "^7.3.7"
+			},
+			"engines": {
+				"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			},
+			"peerDependencies": {
+				"eslint": "^6.0.0 || ^7.0.0 || ^8.0.0"
+			}
+		},
+		"node_modules/@typescript-eslint/visitor-keys": {
+			"version": "5.53.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.53.0.tgz",
+			"integrity": "sha512-JqNLnX3leaHFZEN0gCh81sIvgrp/2GOACZNgO4+Tkf64u51kTpAyWFOY8XHx8XuXr3N2C9zgPPHtcpMg6z1g0w==",
+			"dev": true,
+			"dependencies": {
+				"@typescript-eslint/types": "5.53.0",
+				"eslint-visitor-keys": "^3.3.0"
+			},
+			"engines": {
+				"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			}
+		},
+		"node_modules/@uppy/companion-client": {
+			"version": "2.2.2",
+			"resolved": "https://registry.npmjs.org/@uppy/companion-client/-/companion-client-2.2.2.tgz",
+			"integrity": "sha512-5mTp2iq97/mYSisMaBtFRry6PTgZA6SIL7LePteOV5x0/DxKfrZW3DEiQERJmYpHzy7k8johpm2gHnEKto56Og==",
+			"dependencies": {
+				"@uppy/utils": "^4.1.2",
+				"namespace-emitter": "^2.0.1"
+			}
+		},
+		"node_modules/@uppy/core": {
+			"version": "2.3.4",
+			"resolved": "https://registry.npmjs.org/@uppy/core/-/core-2.3.4.tgz",
+			"integrity": "sha512-iWAqppC8FD8mMVqewavCz+TNaet6HPXitmGXpGGREGrakZ4FeuWytVdrelydzTdXx6vVKkOmI2FLztGg73sENQ==",
+			"dependencies": {
+				"@transloadit/prettier-bytes": "0.0.7",
+				"@uppy/store-default": "^2.1.1",
+				"@uppy/utils": "^4.1.3",
+				"lodash.throttle": "^4.1.1",
+				"mime-match": "^1.0.2",
+				"namespace-emitter": "^2.0.1",
+				"nanoid": "^3.1.25",
+				"preact": "^10.5.13"
+			}
+		},
+		"node_modules/@uppy/store-default": {
+			"version": "2.1.1",
+			"resolved": "https://registry.npmjs.org/@uppy/store-default/-/store-default-2.1.1.tgz",
+			"integrity": "sha512-xnpTxvot2SeAwGwbvmJ899ASk5tYXhmZzD/aCFsXePh/v8rNvR2pKlcQUH7cF/y4baUGq3FHO/daKCok/mpKqQ=="
+		},
+		"node_modules/@uppy/utils": {
+			"version": "4.1.3",
+			"resolved": "https://registry.npmjs.org/@uppy/utils/-/utils-4.1.3.tgz",
+			"integrity": "sha512-nTuMvwWYobnJcytDO3t+D6IkVq/Qs4Xv3vyoEZ+Iaf8gegZP+rEyoaFT2CK5XLRMienPyqRqNbIfRuFaOWSIFw==",
+			"dependencies": {
+				"lodash.throttle": "^4.1.1"
+			}
+		},
+		"node_modules/@uppy/xhr-upload": {
+			"version": "2.1.3",
+			"resolved": "https://registry.npmjs.org/@uppy/xhr-upload/-/xhr-upload-2.1.3.tgz",
+			"integrity": "sha512-YWOQ6myBVPs+mhNjfdWsQyMRWUlrDLMoaG7nvf/G6Y3GKZf8AyjFDjvvJ49XWQ+DaZOftGkHmF1uh/DBeGivJQ==",
+			"dependencies": {
+				"@uppy/companion-client": "^2.2.2",
+				"@uppy/utils": "^4.1.2",
+				"nanoid": "^3.1.25"
+			},
+			"peerDependencies": {
+				"@uppy/core": "^2.3.3"
+			}
+		},
+		"node_modules/@vitejs/plugin-vue": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.0.0.tgz",
+			"integrity": "sha512-e0X4jErIxAB5oLtDqbHvHpJe/uWNkdpYV83AOG2xo2tEVSzCzewgJMtREZM30wXnM5ls90hxiOtAuVU6H5JgbA==",
+			"dev": true,
+			"engines": {
+				"node": "^14.18.0 || >=16.0.0"
+			},
+			"peerDependencies": {
+				"vite": "^4.0.0",
+				"vue": "^3.2.25"
+			}
+		},
+		"node_modules/@vue/compiler-core": {
+			"version": "3.2.47",
+			"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.47.tgz",
+			"integrity": "sha512-p4D7FDnQb7+YJmO2iPEv0SQNeNzcbHdGByJDsT4lynf63AFkOTFN07HsiRSvjGo0QrxR/o3d0hUyNCUnBU2Tig==",
+			"dependencies": {
+				"@babel/parser": "^7.16.4",
+				"@vue/shared": "3.2.47",
+				"estree-walker": "^2.0.2",
+				"source-map": "^0.6.1"
+			}
+		},
+		"node_modules/@vue/compiler-dom": {
+			"version": "3.2.47",
+			"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.47.tgz",
+			"integrity": "sha512-dBBnEHEPoftUiS03a4ggEig74J2YBZ2UIeyfpcRM2tavgMWo4bsEfgCGsu+uJIL/vax9S+JztH8NmQerUo7shQ==",
+			"dependencies": {
+				"@vue/compiler-core": "3.2.47",
+				"@vue/shared": "3.2.47"
+			}
+		},
+		"node_modules/@vue/compiler-sfc": {
+			"version": "3.2.47",
+			"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.47.tgz",
+			"integrity": "sha512-rog05W+2IFfxjMcFw10tM9+f7i/+FFpZJJ5XHX72NP9eC2uRD+42M3pYcQqDXVYoj74kHMSEdQ/WmCjt8JFksQ==",
+			"dependencies": {
+				"@babel/parser": "^7.16.4",
+				"@vue/compiler-core": "3.2.47",
+				"@vue/compiler-dom": "3.2.47",
+				"@vue/compiler-ssr": "3.2.47",
+				"@vue/reactivity-transform": "3.2.47",
+				"@vue/shared": "3.2.47",
+				"estree-walker": "^2.0.2",
+				"magic-string": "^0.25.7",
+				"postcss": "^8.1.10",
+				"source-map": "^0.6.1"
+			}
+		},
+		"node_modules/@vue/compiler-ssr": {
+			"version": "3.2.47",
+			"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.47.tgz",
+			"integrity": "sha512-wVXC+gszhulcMD8wpxMsqSOpvDZ6xKXSVWkf50Guf/S+28hTAXPDYRTbLQ3EDkOP5Xz/+SY37YiwDquKbJOgZw==",
+			"dependencies": {
+				"@vue/compiler-dom": "3.2.47",
+				"@vue/shared": "3.2.47"
+			}
+		},
+		"node_modules/@vue/devtools-api": {
+			"version": "6.5.0",
+			"resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.5.0.tgz",
+			"integrity": "sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q=="
+		},
+		"node_modules/@vue/reactivity": {
+			"version": "3.2.47",
+			"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.47.tgz",
+			"integrity": "sha512-7khqQ/75oyyg+N/e+iwV6lpy1f5wq759NdlS1fpAhFXa8VeAIKGgk2E/C4VF59lx5b+Ezs5fpp/5WsRYXQiKxQ==",
+			"dependencies": {
+				"@vue/shared": "3.2.47"
+			}
+		},
+		"node_modules/@vue/reactivity-transform": {
+			"version": "3.2.47",
+			"resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.47.tgz",
+			"integrity": "sha512-m8lGXw8rdnPVVIdIFhf0LeQ/ixyHkH5plYuS83yop5n7ggVJU+z5v0zecwEnX7fa7HNLBhh2qngJJkxpwEEmYA==",
+			"dependencies": {
+				"@babel/parser": "^7.16.4",
+				"@vue/compiler-core": "3.2.47",
+				"@vue/shared": "3.2.47",
+				"estree-walker": "^2.0.2",
+				"magic-string": "^0.25.7"
+			}
+		},
+		"node_modules/@vue/runtime-core": {
+			"version": "3.2.47",
+			"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.47.tgz",
+			"integrity": "sha512-RZxbLQIRB/K0ev0K9FXhNbBzT32H9iRtYbaXb0ZIz2usLms/D55dJR2t6cIEUn6vyhS3ALNvNthI+Q95C+NOpA==",
+			"dependencies": {
+				"@vue/reactivity": "3.2.47",
+				"@vue/shared": "3.2.47"
+			}
+		},
+		"node_modules/@vue/runtime-dom": {
+			"version": "3.2.47",
+			"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.47.tgz",
+			"integrity": "sha512-ArXrFTjS6TsDei4qwNvgrdmHtD930KgSKGhS5M+j8QxXrDJYLqYw4RRcDy1bz1m1wMmb6j+zGLifdVHtkXA7gA==",
+			"dependencies": {
+				"@vue/runtime-core": "3.2.47",
+				"@vue/shared": "3.2.47",
+				"csstype": "^2.6.8"
+			}
+		},
+		"node_modules/@vue/server-renderer": {
+			"version": "3.2.47",
+			"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.47.tgz",
+			"integrity": "sha512-dN9gc1i8EvmP9RCzvneONXsKfBRgqFeFZLurmHOveL7oH6HiFXJw5OGu294n1nHc/HMgTy6LulU/tv5/A7f/LA==",
+			"dependencies": {
+				"@vue/compiler-ssr": "3.2.47",
+				"@vue/shared": "3.2.47"
+			},
+			"peerDependencies": {
+				"vue": "3.2.47"
+			}
+		},
+		"node_modules/@vue/shared": {
+			"version": "3.2.47",
+			"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.47.tgz",
+			"integrity": "sha512-BHGyyGN3Q97EZx0taMQ+OLNuZcW3d37ZEVmEAyeoA9ERdGvm9Irc/0Fua8SNyOtV1w6BS4q25wbMzJujO9HIfQ=="
+		},
+		"node_modules/@vueuse/core": {
+			"version": "9.13.0",
+			"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-9.13.0.tgz",
+			"integrity": "sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==",
+			"dependencies": {
+				"@types/web-bluetooth": "^0.0.16",
+				"@vueuse/metadata": "9.13.0",
+				"@vueuse/shared": "9.13.0",
+				"vue-demi": "*"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/antfu"
+			}
+		},
+		"node_modules/@vueuse/core/node_modules/vue-demi": {
+			"version": "0.13.11",
+			"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.13.11.tgz",
+			"integrity": "sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==",
+			"hasInstallScript": true,
+			"bin": {
+				"vue-demi-fix": "bin/vue-demi-fix.js",
+				"vue-demi-switch": "bin/vue-demi-switch.js"
+			},
+			"engines": {
+				"node": ">=12"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/antfu"
+			},
+			"peerDependencies": {
+				"@vue/composition-api": "^1.0.0-rc.1",
+				"vue": "^3.0.0-0 || ^2.6.0"
+			},
+			"peerDependenciesMeta": {
+				"@vue/composition-api": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/@vueuse/metadata": {
+			"version": "9.13.0",
+			"resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-9.13.0.tgz",
+			"integrity": "sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==",
+			"funding": {
+				"url": "https://github.com/sponsors/antfu"
+			}
+		},
+		"node_modules/@vueuse/shared": {
+			"version": "9.13.0",
+			"resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-9.13.0.tgz",
+			"integrity": "sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==",
+			"dependencies": {
+				"vue-demi": "*"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/antfu"
+			}
+		},
+		"node_modules/@vueuse/shared/node_modules/vue-demi": {
+			"version": "0.13.11",
+			"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.13.11.tgz",
+			"integrity": "sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==",
+			"hasInstallScript": true,
+			"bin": {
+				"vue-demi-fix": "bin/vue-demi-fix.js",
+				"vue-demi-switch": "bin/vue-demi-switch.js"
+			},
+			"engines": {
+				"node": ">=12"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/antfu"
+			},
+			"peerDependencies": {
+				"@vue/composition-api": "^1.0.0-rc.1",
+				"vue": "^3.0.0-0 || ^2.6.0"
+			},
+			"peerDependenciesMeta": {
+				"@vue/composition-api": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/@wangeditor/basic-modules": {
+			"version": "1.1.7",
+			"resolved": "https://registry.npmjs.org/@wangeditor/basic-modules/-/basic-modules-1.1.7.tgz",
+			"integrity": "sha512-cY9CPkLJaqF05STqfpZKWG4LpxTMeGSIIF1fHvfm/mz+JXatCagjdkbxdikOuKYlxDdeqvOeBmsUBItufDLXZg==",
+			"dependencies": {
+				"is-url": "^1.2.4"
+			},
+			"peerDependencies": {
+				"@wangeditor/core": "1.x",
+				"dom7": "^3.0.0",
+				"lodash.throttle": "^4.1.1",
+				"nanoid": "^3.2.0",
+				"slate": "^0.72.0",
+				"snabbdom": "^3.1.0"
+			}
+		},
+		"node_modules/@wangeditor/code-highlight": {
+			"version": "1.0.3",
+			"resolved": "https://registry.npmjs.org/@wangeditor/code-highlight/-/code-highlight-1.0.3.tgz",
+			"integrity": "sha512-iazHwO14XpCuIWJNTQTikqUhGKyqj+dUNWJ9288Oym9M2xMVHvnsOmDU2sgUDWVy+pOLojReMPgXCsvvNlOOhw==",
+			"dependencies": {
+				"prismjs": "^1.23.0"
+			},
+			"peerDependencies": {
+				"@wangeditor/core": "1.x",
+				"dom7": "^3.0.0",
+				"slate": "^0.72.0",
+				"snabbdom": "^3.1.0"
+			}
+		},
+		"node_modules/@wangeditor/core": {
+			"version": "1.1.19",
+			"resolved": "https://registry.npmjs.org/@wangeditor/core/-/core-1.1.19.tgz",
+			"integrity": "sha512-KevkB47+7GhVszyYF2pKGKtCSj/YzmClsD03C3zTt+9SR2XWT5T0e3yQqg8baZpcMvkjs1D8Dv4fk8ok/UaS2Q==",
+			"dependencies": {
+				"@types/event-emitter": "^0.3.3",
+				"event-emitter": "^0.3.5",
+				"html-void-elements": "^2.0.0",
+				"i18next": "^20.4.0",
+				"scroll-into-view-if-needed": "^2.2.28",
+				"slate-history": "^0.66.0"
+			},
+			"peerDependencies": {
+				"@uppy/core": "^2.1.1",
+				"@uppy/xhr-upload": "^2.0.3",
+				"dom7": "^3.0.0",
+				"is-hotkey": "^0.2.0",
+				"lodash.camelcase": "^4.3.0",
+				"lodash.clonedeep": "^4.5.0",
+				"lodash.debounce": "^4.0.8",
+				"lodash.foreach": "^4.5.0",
+				"lodash.isequal": "^4.5.0",
+				"lodash.throttle": "^4.1.1",
+				"lodash.toarray": "^4.4.0",
+				"nanoid": "^3.2.0",
+				"slate": "^0.72.0",
+				"snabbdom": "^3.1.0"
+			}
+		},
+		"node_modules/@wangeditor/editor": {
+			"version": "5.1.23",
+			"resolved": "https://registry.npmjs.org/@wangeditor/editor/-/editor-5.1.23.tgz",
+			"integrity": "sha512-0RxfeVTuK1tktUaPROnCoFfaHVJpRAIE2zdS0mpP+vq1axVQpLjM8+fCvKzqYIkH0Pg+C+44hJpe3VVroSkEuQ==",
+			"dependencies": {
+				"@uppy/core": "^2.1.1",
+				"@uppy/xhr-upload": "^2.0.3",
+				"@wangeditor/basic-modules": "^1.1.7",
+				"@wangeditor/code-highlight": "^1.0.3",
+				"@wangeditor/core": "^1.1.19",
+				"@wangeditor/list-module": "^1.0.5",
+				"@wangeditor/table-module": "^1.1.4",
+				"@wangeditor/upload-image-module": "^1.0.2",
+				"@wangeditor/video-module": "^1.1.4",
+				"dom7": "^3.0.0",
+				"is-hotkey": "^0.2.0",
+				"lodash.camelcase": "^4.3.0",
+				"lodash.clonedeep": "^4.5.0",
+				"lodash.debounce": "^4.0.8",
+				"lodash.foreach": "^4.5.0",
+				"lodash.isequal": "^4.5.0",
+				"lodash.throttle": "^4.1.1",
+				"lodash.toarray": "^4.4.0",
+				"nanoid": "^3.2.0",
+				"slate": "^0.72.0",
+				"snabbdom": "^3.1.0"
+			}
+		},
+		"node_modules/@wangeditor/editor-for-vue": {
+			"version": "5.1.12",
+			"resolved": "https://registry.npmjs.org/@wangeditor/editor-for-vue/-/editor-for-vue-5.1.12.tgz",
+			"integrity": "sha512-0Ds3D8I+xnpNWezAeO7HmPRgTfUxHLMd9JKcIw+QzvSmhC5xUHbpCcLU+KLmeBKTR/zffnS5GQo6qi3GhTMJWQ==",
+			"peerDependencies": {
+				"@wangeditor/editor": ">=5.1.0",
+				"vue": "^3.0.5"
+			}
+		},
+		"node_modules/@wangeditor/list-module": {
+			"version": "1.0.5",
+			"resolved": "https://registry.npmjs.org/@wangeditor/list-module/-/list-module-1.0.5.tgz",
+			"integrity": "sha512-uDuYTP6DVhcYf7mF1pTlmNn5jOb4QtcVhYwSSAkyg09zqxI1qBqsfUnveeDeDqIuptSJhkh81cyxi+MF8sEPOQ==",
+			"peerDependencies": {
+				"@wangeditor/core": "1.x",
+				"dom7": "^3.0.0",
+				"slate": "^0.72.0",
+				"snabbdom": "^3.1.0"
+			}
+		},
+		"node_modules/@wangeditor/table-module": {
+			"version": "1.1.4",
+			"resolved": "https://registry.npmjs.org/@wangeditor/table-module/-/table-module-1.1.4.tgz",
+			"integrity": "sha512-5saanU9xuEocxaemGdNi9t8MCDSucnykEC6jtuiT72kt+/Hhh4nERYx1J20OPsTCCdVr7hIyQenFD1iSRkIQ6w==",
+			"peerDependencies": {
+				"@wangeditor/core": "1.x",
+				"dom7": "^3.0.0",
+				"lodash.isequal": "^4.5.0",
+				"lodash.throttle": "^4.1.1",
+				"nanoid": "^3.2.0",
+				"slate": "^0.72.0",
+				"snabbdom": "^3.1.0"
+			}
+		},
+		"node_modules/@wangeditor/upload-image-module": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/@wangeditor/upload-image-module/-/upload-image-module-1.0.2.tgz",
+			"integrity": "sha512-z81lk/v71OwPDYeQDxj6cVr81aDP90aFuywb8nPD6eQeECtOymrqRODjpO6VGvCVxVck8nUxBHtbxKtjgcwyiA==",
+			"peerDependencies": {
+				"@uppy/core": "^2.0.3",
+				"@uppy/xhr-upload": "^2.0.3",
+				"@wangeditor/basic-modules": "1.x",
+				"@wangeditor/core": "1.x",
+				"dom7": "^3.0.0",
+				"lodash.foreach": "^4.5.0",
+				"slate": "^0.72.0",
+				"snabbdom": "^3.1.0"
+			}
+		},
+		"node_modules/@wangeditor/video-module": {
+			"version": "1.1.4",
+			"resolved": "https://registry.npmjs.org/@wangeditor/video-module/-/video-module-1.1.4.tgz",
+			"integrity": "sha512-ZdodDPqKQrgx3IwWu4ZiQmXI8EXZ3hm2/fM6E3t5dB8tCaIGWQZhmqd6P5knfkRAd3z2+YRSRbxOGfoRSp/rLg==",
+			"peerDependencies": {
+				"@uppy/core": "^2.1.4",
+				"@uppy/xhr-upload": "^2.0.7",
+				"@wangeditor/core": "1.x",
+				"dom7": "^3.0.0",
+				"nanoid": "^3.2.0",
+				"slate": "^0.72.0",
+				"snabbdom": "^3.1.0"
+			}
+		},
+		"node_modules/acorn": {
+			"version": "8.8.2",
+			"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz",
+			"integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==",
+			"dev": true,
+			"bin": {
+				"acorn": "bin/acorn"
+			},
+			"engines": {
+				"node": ">=0.4.0"
+			}
+		},
+		"node_modules/acorn-jsx": {
+			"version": "5.3.2",
+			"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+			"integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+			"dev": true,
+			"peerDependencies": {
+				"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+			}
+		},
+		"node_modules/acorn-node": {
+			"version": "1.8.2",
+			"resolved": "https://registry.npmmirror.com/acorn-node/-/acorn-node-1.8.2.tgz",
+			"integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==",
+			"dev": true,
+			"dependencies": {
+				"acorn": "^7.0.0",
+				"acorn-walk": "^7.0.0",
+				"xtend": "^4.0.2"
+			}
+		},
+		"node_modules/acorn-node/node_modules/acorn": {
+			"version": "7.4.1",
+			"resolved": "https://registry.npmmirror.com/acorn/-/acorn-7.4.1.tgz",
+			"integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
+			"dev": true,
+			"bin": {
+				"acorn": "bin/acorn"
+			},
+			"engines": {
+				"node": ">=0.4.0"
+			}
+		},
+		"node_modules/acorn-walk": {
+			"version": "7.2.0",
+			"resolved": "https://registry.npmmirror.com/acorn-walk/-/acorn-walk-7.2.0.tgz",
+			"integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==",
+			"dev": true,
+			"engines": {
+				"node": ">=0.4.0"
+			}
+		},
+		"node_modules/ajv": {
+			"version": "6.12.6",
+			"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+			"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+			"dev": true,
+			"dependencies": {
+				"fast-deep-equal": "^3.1.1",
+				"fast-json-stable-stringify": "^2.0.0",
+				"json-schema-traverse": "^0.4.1",
+				"uri-js": "^4.2.2"
+			},
+			"funding": {
+				"type": "github",
+				"url": "https://github.com/sponsors/epoberezkin"
+			}
+		},
+		"node_modules/ansi-regex": {
+			"version": "5.0.1",
+			"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+			"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+			"dev": true,
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/ansi-styles": {
+			"version": "4.3.0",
+			"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+			"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+			"dev": true,
+			"dependencies": {
+				"color-convert": "^2.0.1"
+			},
+			"engines": {
+				"node": ">=8"
+			},
+			"funding": {
+				"url": "https://github.com/chalk/ansi-styles?sponsor=1"
+			}
+		},
+		"node_modules/anymatch": {
+			"version": "3.1.3",
+			"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+			"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+			"dev": true,
+			"dependencies": {
+				"normalize-path": "^3.0.0",
+				"picomatch": "^2.0.4"
+			},
+			"engines": {
+				"node": ">= 8"
+			}
+		},
+		"node_modules/arg": {
+			"version": "5.0.2",
+			"resolved": "https://registry.npmmirror.com/arg/-/arg-5.0.2.tgz",
+			"integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
+			"dev": true
+		},
+		"node_modules/argparse": {
+			"version": "2.0.1",
+			"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+			"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+			"dev": true
+		},
+		"node_modules/array-union": {
+			"version": "2.1.0",
+			"resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
+			"integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
+			"dev": true,
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/async-validator": {
+			"version": "4.2.5",
+			"resolved": "https://registry.npmjs.org/async-validator/-/async-validator-4.2.5.tgz",
+			"integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg=="
+		},
+		"node_modules/asynckit": {
+			"version": "0.4.0",
+			"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+			"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
+		},
+		"node_modules/autoprefixer": {
+			"version": "10.4.13",
+			"resolved": "https://registry.npmmirror.com/autoprefixer/-/autoprefixer-10.4.13.tgz",
+			"integrity": "sha512-49vKpMqcZYsJjwotvt4+h/BCjJVnhGwcLpDt5xkcaOG3eLrG/HUYLagrihYsQ+qrIBgIzX1Rw7a6L8I/ZA1Atg==",
+			"dev": true,
+			"dependencies": {
+				"browserslist": "^4.21.4",
+				"caniuse-lite": "^1.0.30001426",
+				"fraction.js": "^4.2.0",
+				"normalize-range": "^0.1.2",
+				"picocolors": "^1.0.0",
+				"postcss-value-parser": "^4.2.0"
+			},
+			"bin": {
+				"autoprefixer": "bin/autoprefixer"
+			},
+			"engines": {
+				"node": "^10 || ^12 || >=14"
+			},
+			"peerDependencies": {
+				"postcss": "^8.1.0"
+			}
+		},
+		"node_modules/axios": {
+			"version": "1.3.3",
+			"resolved": "https://registry.npmjs.org/axios/-/axios-1.3.3.tgz",
+			"integrity": "sha512-eYq77dYIFS77AQlhzEL937yUBSepBfPIe8FcgEDN35vMNZKMrs81pgnyrQpwfy4NF4b4XWX1Zgx7yX+25w8QJA==",
+			"dependencies": {
+				"follow-redirects": "^1.15.0",
+				"form-data": "^4.0.0",
+				"proxy-from-env": "^1.1.0"
+			}
+		},
+		"node_modules/balanced-match": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+			"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+			"dev": true
+		},
+		"node_modules/batch-processor": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/batch-processor/-/batch-processor-1.0.0.tgz",
+			"integrity": "sha512-xoLQD8gmmR32MeuBHgH0Tzd5PuSZx71ZsbhVxOCRbgktZEPe4SQy7s9Z50uPp0F/f7iw2XmkHN2xkgbMfckMDA=="
+		},
+		"node_modules/binary-extensions": {
+			"version": "2.2.0",
+			"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
+			"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
+			"dev": true,
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/boolbase": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
+			"integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
+			"dev": true
+		},
+		"node_modules/brace-expansion": {
+			"version": "1.1.11",
+			"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+			"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+			"dev": true,
+			"dependencies": {
+				"balanced-match": "^1.0.0",
+				"concat-map": "0.0.1"
+			}
+		},
+		"node_modules/braces": {
+			"version": "3.0.2",
+			"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+			"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+			"dev": true,
+			"dependencies": {
+				"fill-range": "^7.0.1"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/browserslist": {
+			"version": "4.21.5",
+			"resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.21.5.tgz",
+			"integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==",
+			"dev": true,
+			"dependencies": {
+				"caniuse-lite": "^1.0.30001449",
+				"electron-to-chromium": "^1.4.284",
+				"node-releases": "^2.0.8",
+				"update-browserslist-db": "^1.0.10"
+			},
+			"bin": {
+				"browserslist": "cli.js"
+			},
+			"engines": {
+				"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+			}
+		},
+		"node_modules/call-bind": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
+			"integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
+			"dependencies": {
+				"function-bind": "^1.1.1",
+				"get-intrinsic": "^1.0.2"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/ljharb"
+			}
+		},
+		"node_modules/callsites": {
+			"version": "3.1.0",
+			"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+			"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+			"dev": true,
+			"engines": {
+				"node": ">=6"
+			}
+		},
+		"node_modules/camelcase-css": {
+			"version": "2.0.1",
+			"resolved": "https://registry.npmmirror.com/camelcase-css/-/camelcase-css-2.0.1.tgz",
+			"integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==",
+			"dev": true,
+			"engines": {
+				"node": ">= 6"
+			}
+		},
+		"node_modules/caniuse-lite": {
+			"version": "1.0.30001458",
+			"resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001458.tgz",
+			"integrity": "sha512-lQ1VlUUq5q9ro9X+5gOEyH7i3vm+AYVT1WDCVB69XOZ17KZRhnZ9J0Sqz7wTHQaLBJccNCHq8/Ww5LlOIZbB0w==",
+			"dev": true
+		},
+		"node_modules/chalk": {
+			"version": "4.1.2",
+			"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+			"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+			"dev": true,
+			"dependencies": {
+				"ansi-styles": "^4.1.0",
+				"supports-color": "^7.1.0"
+			},
+			"engines": {
+				"node": ">=10"
+			},
+			"funding": {
+				"url": "https://github.com/chalk/chalk?sponsor=1"
+			}
+		},
+		"node_modules/chokidar": {
+			"version": "3.5.3",
+			"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
+			"integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
+			"dev": true,
+			"funding": [
+				{
+					"type": "individual",
+					"url": "https://paulmillr.com/funding/"
+				}
+			],
+			"dependencies": {
+				"anymatch": "~3.1.2",
+				"braces": "~3.0.2",
+				"glob-parent": "~5.1.2",
+				"is-binary-path": "~2.1.0",
+				"is-glob": "~4.0.1",
+				"normalize-path": "~3.0.0",
+				"readdirp": "~3.6.0"
+			},
+			"engines": {
+				"node": ">= 8.10.0"
+			},
+			"optionalDependencies": {
+				"fsevents": "~2.3.2"
+			}
+		},
+		"node_modules/chokidar/node_modules/glob-parent": {
+			"version": "5.1.2",
+			"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+			"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+			"dev": true,
+			"dependencies": {
+				"is-glob": "^4.0.1"
+			},
+			"engines": {
+				"node": ">= 6"
+			}
+		},
+		"node_modules/claygl": {
+			"version": "1.3.0",
+			"resolved": "https://registry.npmjs.org/claygl/-/claygl-1.3.0.tgz",
+			"integrity": "sha512-+gGtJjT6SSHD2l2yC3MCubW/sCV40tZuSs5opdtn79vFSGUgp/lH139RNEQ6Jy078/L0aV8odCw8RSrUcMfLaQ=="
+		},
+		"node_modules/clipboard": {
+			"version": "2.0.11",
+			"resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.11.tgz",
+			"integrity": "sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==",
+			"dependencies": {
+				"good-listener": "^1.2.2",
+				"select": "^1.1.2",
+				"tiny-emitter": "^2.0.0"
+			}
+		},
+		"node_modules/color-convert": {
+			"version": "2.0.1",
+			"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+			"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+			"dev": true,
+			"dependencies": {
+				"color-name": "~1.1.4"
+			},
+			"engines": {
+				"node": ">=7.0.0"
+			}
+		},
+		"node_modules/color-name": {
+			"version": "1.1.4",
+			"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+			"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+			"dev": true
+		},
+		"node_modules/combined-stream": {
+			"version": "1.0.8",
+			"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+			"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+			"dependencies": {
+				"delayed-stream": "~1.0.0"
+			},
+			"engines": {
+				"node": ">= 0.8"
+			}
+		},
+		"node_modules/compute-scroll-into-view": {
+			"version": "1.0.20",
+			"resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-1.0.20.tgz",
+			"integrity": "sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg=="
+		},
+		"node_modules/concat-map": {
+			"version": "0.0.1",
+			"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+			"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+			"dev": true
+		},
+		"node_modules/countup.js": {
+			"version": "2.4.2",
+			"resolved": "https://registry.npmjs.org/countup.js/-/countup.js-2.4.2.tgz",
+			"integrity": "sha512-EExCcu5rd7ffBj65B3CVNuS1HddN1Y4WuTfJEuocJXwZlNnlXZQ4sD9M/Cq32ZS0zR38F9vGMCw/iFcVImrNbw=="
+		},
+		"node_modules/cropperjs": {
+			"version": "1.5.13",
+			"resolved": "https://registry.npmjs.org/cropperjs/-/cropperjs-1.5.13.tgz",
+			"integrity": "sha512-by7jKAo73y5/Do0K6sxdTKHgndY0NMjG2bEdgeJxycbcmHuCiMXqw8sxy5C5Y5WTOTcDGmbT7Sr5CgKOXR06OA=="
+		},
+		"node_modules/cross-spawn": {
+			"version": "7.0.3",
+			"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
+			"integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+			"dev": true,
+			"dependencies": {
+				"path-key": "^3.1.0",
+				"shebang-command": "^2.0.0",
+				"which": "^2.0.1"
+			},
+			"engines": {
+				"node": ">= 8"
+			}
+		},
+		"node_modules/cssesc": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
+			"integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
+			"dev": true,
+			"bin": {
+				"cssesc": "bin/cssesc"
+			},
+			"engines": {
+				"node": ">=4"
+			}
+		},
+		"node_modules/csstype": {
+			"version": "2.6.21",
+			"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz",
+			"integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w=="
+		},
+		"node_modules/d": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
+			"integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
+			"dependencies": {
+				"es5-ext": "^0.10.50",
+				"type": "^1.0.1"
+			}
+		},
+		"node_modules/dayjs": {
+			"version": "1.11.7",
+			"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.7.tgz",
+			"integrity": "sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ=="
+		},
+		"node_modules/debug": {
+			"version": "4.3.4",
+			"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+			"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+			"dev": true,
+			"dependencies": {
+				"ms": "2.1.2"
+			},
+			"engines": {
+				"node": ">=6.0"
+			},
+			"peerDependenciesMeta": {
+				"supports-color": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/deep-is": {
+			"version": "0.1.4",
+			"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+			"integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+			"dev": true
+		},
+		"node_modules/defined": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmmirror.com/defined/-/defined-1.0.1.tgz",
+			"integrity": "sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==",
+			"dev": true
+		},
+		"node_modules/delayed-stream": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+			"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+			"engines": {
+				"node": ">=0.4.0"
+			}
+		},
+		"node_modules/delegate": {
+			"version": "3.2.0",
+			"resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz",
+			"integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw=="
+		},
+		"node_modules/detective": {
+			"version": "5.2.1",
+			"resolved": "https://registry.npmmirror.com/detective/-/detective-5.2.1.tgz",
+			"integrity": "sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw==",
+			"dev": true,
+			"dependencies": {
+				"acorn-node": "^1.8.2",
+				"defined": "^1.0.0",
+				"minimist": "^1.2.6"
+			},
+			"bin": {
+				"detective": "bin/detective.js"
+			},
+			"engines": {
+				"node": ">=0.8.0"
+			}
+		},
+		"node_modules/didyoumean": {
+			"version": "1.2.2",
+			"resolved": "https://registry.npmmirror.com/didyoumean/-/didyoumean-1.2.2.tgz",
+			"integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==",
+			"dev": true
+		},
+		"node_modules/dir-glob": {
+			"version": "3.0.1",
+			"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
+			"integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
+			"dev": true,
+			"dependencies": {
+				"path-type": "^4.0.0"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/dlv": {
+			"version": "1.1.3",
+			"resolved": "https://registry.npmmirror.com/dlv/-/dlv-1.1.3.tgz",
+			"integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
+			"dev": true
+		},
+		"node_modules/doctrine": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+			"integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+			"dev": true,
+			"dependencies": {
+				"esutils": "^2.0.2"
+			},
+			"engines": {
+				"node": ">=6.0.0"
+			}
+		},
+		"node_modules/dom7": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/dom7/-/dom7-3.0.0.tgz",
+			"integrity": "sha512-oNlcUdHsC4zb7Msx7JN3K0Nro1dzJ48knvBOnDPKJ2GV9wl1i5vydJZUSyOfrkKFDZEud/jBsTk92S/VGSAe/g==",
+			"dependencies": {
+				"ssr-window": "^3.0.0-alpha.1"
+			}
+		},
+		"node_modules/echarts": {
+			"version": "5.4.1",
+			"resolved": "https://registry.npmjs.org/echarts/-/echarts-5.4.1.tgz",
+			"integrity": "sha512-9ltS3M2JB0w2EhcYjCdmtrJ+6haZcW6acBolMGIuf01Hql1yrIV01L1aRj7jsaaIULJslEP9Z3vKlEmnJaWJVQ==",
+			"dependencies": {
+				"tslib": "2.3.0",
+				"zrender": "5.4.1"
+			}
+		},
+		"node_modules/echarts-gl": {
+			"version": "2.0.9",
+			"resolved": "https://registry.npmjs.org/echarts-gl/-/echarts-gl-2.0.9.tgz",
+			"integrity": "sha512-oKeMdkkkpJGWOzjgZUsF41DOh6cMsyrGGXimbjK2l6Xeq/dBQu4ShG2w2Dzrs/1bD27b2pLTGSaUzouY191gzA==",
+			"dependencies": {
+				"claygl": "^1.2.1",
+				"zrender": "^5.1.1"
+			},
+			"peerDependencies": {
+				"echarts": "^5.1.2"
+			}
+		},
+		"node_modules/echarts-wordcloud": {
+			"version": "2.1.0",
+			"resolved": "https://registry.npmjs.org/echarts-wordcloud/-/echarts-wordcloud-2.1.0.tgz",
+			"integrity": "sha512-Kt1JmbcROgb+3IMI48KZECK2AP5lG6bSsOEs+AsuwaWJxQom31RTNd6NFYI01E/YaI1PFZeueaupjlmzSQasjQ==",
+			"peerDependencies": {
+				"echarts": "^5.0.1"
+			}
+		},
+		"node_modules/electron-to-chromium": {
+			"version": "1.4.313",
+			"resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.4.313.tgz",
+			"integrity": "sha512-QckB9OVqr2oybjIrbMI99uF+b9+iTja5weFe0ePbqLb5BHqXOJUO1SG6kDj/1WtWPRIBr51N153AEq8m7HuIaA==",
+			"dev": true
+		},
+		"node_modules/element-plus": {
+			"version": "2.2.32",
+			"resolved": "https://registry.npmjs.org/element-plus/-/element-plus-2.2.32.tgz",
+			"integrity": "sha512-DTJMhYOy6MApbmh6z/95hPTK5WrBiNHGzV4IN+uEkup1WoimQ+Qyt8RxKdTe/X1LWEJ8YgWv/Cl8P4ocrt5z5g==",
+			"dependencies": {
+				"@ctrl/tinycolor": "^3.4.1",
+				"@element-plus/icons-vue": "^2.0.6",
+				"@floating-ui/dom": "^1.0.1",
+				"@popperjs/core": "npm:@sxzz/popperjs-es@^2.11.7",
+				"@types/lodash": "^4.14.182",
+				"@types/lodash-es": "^4.17.6",
+				"@vueuse/core": "^9.1.0",
+				"async-validator": "^4.2.5",
+				"dayjs": "^1.11.3",
+				"escape-html": "^1.0.3",
+				"lodash": "^4.17.21",
+				"lodash-es": "^4.17.21",
+				"lodash-unified": "^1.0.2",
+				"memoize-one": "^6.0.0",
+				"normalize-wheel-es": "^1.2.0"
+			},
+			"peerDependencies": {
+				"vue": "^3.2.0"
+			}
+		},
+		"node_modules/element-resize-detector": {
+			"version": "1.2.4",
+			"resolved": "https://registry.npmjs.org/element-resize-detector/-/element-resize-detector-1.2.4.tgz",
+			"integrity": "sha512-Fl5Ftk6WwXE0wqCgNoseKWndjzZlDCwuPTcoVZfCP9R3EHQF8qUtr3YUPNETegRBOKqQKPW3n4kiIWngGi8tKg==",
+			"dependencies": {
+				"batch-processor": "1.0.0"
+			}
+		},
+		"node_modules/es5-ext": {
+			"version": "0.10.62",
+			"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz",
+			"integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==",
+			"hasInstallScript": true,
+			"dependencies": {
+				"es6-iterator": "^2.0.3",
+				"es6-symbol": "^3.1.3",
+				"next-tick": "^1.1.0"
+			},
+			"engines": {
+				"node": ">=0.10"
+			}
+		},
+		"node_modules/es6-iterator": {
+			"version": "2.0.3",
+			"resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
+			"integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==",
+			"dependencies": {
+				"d": "1",
+				"es5-ext": "^0.10.35",
+				"es6-symbol": "^3.1.1"
+			}
+		},
+		"node_modules/es6-symbol": {
+			"version": "3.1.3",
+			"resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz",
+			"integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==",
+			"dependencies": {
+				"d": "^1.0.1",
+				"ext": "^1.1.2"
+			}
+		},
+		"node_modules/esbuild": {
+			"version": "0.16.17",
+			"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.16.17.tgz",
+			"integrity": "sha512-G8LEkV0XzDMNwXKgM0Jwu3nY3lSTwSGY6XbxM9cr9+s0T/qSV1q1JVPBGzm3dcjhCic9+emZDmMffkwgPeOeLg==",
+			"dev": true,
+			"hasInstallScript": true,
+			"bin": {
+				"esbuild": "bin/esbuild"
+			},
+			"engines": {
+				"node": ">=12"
+			},
+			"optionalDependencies": {
+				"@esbuild/android-arm": "0.16.17",
+				"@esbuild/android-arm64": "0.16.17",
+				"@esbuild/android-x64": "0.16.17",
+				"@esbuild/darwin-arm64": "0.16.17",
+				"@esbuild/darwin-x64": "0.16.17",
+				"@esbuild/freebsd-arm64": "0.16.17",
+				"@esbuild/freebsd-x64": "0.16.17",
+				"@esbuild/linux-arm": "0.16.17",
+				"@esbuild/linux-arm64": "0.16.17",
+				"@esbuild/linux-ia32": "0.16.17",
+				"@esbuild/linux-loong64": "0.16.17",
+				"@esbuild/linux-mips64el": "0.16.17",
+				"@esbuild/linux-ppc64": "0.16.17",
+				"@esbuild/linux-riscv64": "0.16.17",
+				"@esbuild/linux-s390x": "0.16.17",
+				"@esbuild/linux-x64": "0.16.17",
+				"@esbuild/netbsd-x64": "0.16.17",
+				"@esbuild/openbsd-x64": "0.16.17",
+				"@esbuild/sunos-x64": "0.16.17",
+				"@esbuild/win32-arm64": "0.16.17",
+				"@esbuild/win32-ia32": "0.16.17",
+				"@esbuild/win32-x64": "0.16.17"
+			}
+		},
+		"node_modules/escalade": {
+			"version": "3.1.1",
+			"resolved": "https://registry.npmmirror.com/escalade/-/escalade-3.1.1.tgz",
+			"integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
+			"dev": true,
+			"engines": {
+				"node": ">=6"
+			}
+		},
+		"node_modules/escape-html": {
+			"version": "1.0.3",
+			"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+			"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
+		},
+		"node_modules/escape-string-regexp": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+			"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+			"dev": true,
+			"engines": {
+				"node": ">=10"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/eslint": {
+			"version": "8.34.0",
+			"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.34.0.tgz",
+			"integrity": "sha512-1Z8iFsucw+7kSqXNZVslXS8Ioa4u2KM7GPwuKtkTFAqZ/cHMcEaR+1+Br0wLlot49cNxIiZk5wp8EAbPcYZxTg==",
+			"dev": true,
+			"dependencies": {
+				"@eslint/eslintrc": "^1.4.1",
+				"@humanwhocodes/config-array": "^0.11.8",
+				"@humanwhocodes/module-importer": "^1.0.1",
+				"@nodelib/fs.walk": "^1.2.8",
+				"ajv": "^6.10.0",
+				"chalk": "^4.0.0",
+				"cross-spawn": "^7.0.2",
+				"debug": "^4.3.2",
+				"doctrine": "^3.0.0",
+				"escape-string-regexp": "^4.0.0",
+				"eslint-scope": "^7.1.1",
+				"eslint-utils": "^3.0.0",
+				"eslint-visitor-keys": "^3.3.0",
+				"espree": "^9.4.0",
+				"esquery": "^1.4.0",
+				"esutils": "^2.0.2",
+				"fast-deep-equal": "^3.1.3",
+				"file-entry-cache": "^6.0.1",
+				"find-up": "^5.0.0",
+				"glob-parent": "^6.0.2",
+				"globals": "^13.19.0",
+				"grapheme-splitter": "^1.0.4",
+				"ignore": "^5.2.0",
+				"import-fresh": "^3.0.0",
+				"imurmurhash": "^0.1.4",
+				"is-glob": "^4.0.0",
+				"is-path-inside": "^3.0.3",
+				"js-sdsl": "^4.1.4",
+				"js-yaml": "^4.1.0",
+				"json-stable-stringify-without-jsonify": "^1.0.1",
+				"levn": "^0.4.1",
+				"lodash.merge": "^4.6.2",
+				"minimatch": "^3.1.2",
+				"natural-compare": "^1.4.0",
+				"optionator": "^0.9.1",
+				"regexpp": "^3.2.0",
+				"strip-ansi": "^6.0.1",
+				"strip-json-comments": "^3.1.0",
+				"text-table": "^0.2.0"
+			},
+			"bin": {
+				"eslint": "bin/eslint.js"
+			},
+			"engines": {
+				"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+			},
+			"funding": {
+				"url": "https://opencollective.com/eslint"
+			}
+		},
+		"node_modules/eslint-plugin-vue": {
+			"version": "9.9.0",
+			"resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.9.0.tgz",
+			"integrity": "sha512-YbubS7eK0J7DCf0U2LxvVP7LMfs6rC6UltihIgval3azO3gyDwEGVgsCMe1TmDiEkl6GdMKfRpaME6QxIYtzDQ==",
+			"dev": true,
+			"dependencies": {
+				"eslint-utils": "^3.0.0",
+				"natural-compare": "^1.4.0",
+				"nth-check": "^2.0.1",
+				"postcss-selector-parser": "^6.0.9",
+				"semver": "^7.3.5",
+				"vue-eslint-parser": "^9.0.1",
+				"xml-name-validator": "^4.0.0"
+			},
+			"engines": {
+				"node": "^14.17.0 || >=16.0.0"
+			},
+			"peerDependencies": {
+				"eslint": "^6.2.0 || ^7.0.0 || ^8.0.0"
+			}
+		},
+		"node_modules/eslint-scope": {
+			"version": "5.1.1",
+			"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
+			"integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
+			"dev": true,
+			"dependencies": {
+				"esrecurse": "^4.3.0",
+				"estraverse": "^4.1.1"
+			},
+			"engines": {
+				"node": ">=8.0.0"
+			}
+		},
+		"node_modules/eslint-utils": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz",
+			"integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==",
+			"dev": true,
+			"dependencies": {
+				"eslint-visitor-keys": "^2.0.0"
+			},
+			"engines": {
+				"node": "^10.0.0 || ^12.0.0 || >= 14.0.0"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/mysticatea"
+			},
+			"peerDependencies": {
+				"eslint": ">=5"
+			}
+		},
+		"node_modules/eslint-utils/node_modules/eslint-visitor-keys": {
+			"version": "2.1.0",
+			"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
+			"integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
+			"dev": true,
+			"engines": {
+				"node": ">=10"
+			}
+		},
+		"node_modules/eslint-visitor-keys": {
+			"version": "3.3.0",
+			"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz",
+			"integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==",
+			"dev": true,
+			"engines": {
+				"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+			}
+		},
+		"node_modules/eslint/node_modules/eslint-scope": {
+			"version": "7.1.1",
+			"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz",
+			"integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==",
+			"dev": true,
+			"dependencies": {
+				"esrecurse": "^4.3.0",
+				"estraverse": "^5.2.0"
+			},
+			"engines": {
+				"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+			}
+		},
+		"node_modules/eslint/node_modules/estraverse": {
+			"version": "5.3.0",
+			"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+			"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+			"dev": true,
+			"engines": {
+				"node": ">=4.0"
+			}
+		},
+		"node_modules/espree": {
+			"version": "9.4.1",
+			"resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz",
+			"integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==",
+			"dev": true,
+			"dependencies": {
+				"acorn": "^8.8.0",
+				"acorn-jsx": "^5.3.2",
+				"eslint-visitor-keys": "^3.3.0"
+			},
+			"engines": {
+				"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+			},
+			"funding": {
+				"url": "https://opencollective.com/eslint"
+			}
+		},
+		"node_modules/esquery": {
+			"version": "1.4.2",
+			"resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.2.tgz",
+			"integrity": "sha512-JVSoLdTlTDkmjFmab7H/9SL9qGSyjElT3myyKp7krqjVFQCDLmj1QFaCLRFBszBKI0XVZaiiXvuPIX3ZwHe1Ng==",
+			"dev": true,
+			"dependencies": {
+				"estraverse": "^5.1.0"
+			},
+			"engines": {
+				"node": ">=0.10"
+			}
+		},
+		"node_modules/esquery/node_modules/estraverse": {
+			"version": "5.3.0",
+			"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+			"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+			"dev": true,
+			"engines": {
+				"node": ">=4.0"
+			}
+		},
+		"node_modules/esrecurse": {
+			"version": "4.3.0",
+			"resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+			"integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+			"dev": true,
+			"dependencies": {
+				"estraverse": "^5.2.0"
+			},
+			"engines": {
+				"node": ">=4.0"
+			}
+		},
+		"node_modules/esrecurse/node_modules/estraverse": {
+			"version": "5.3.0",
+			"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+			"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+			"dev": true,
+			"engines": {
+				"node": ">=4.0"
+			}
+		},
+		"node_modules/estraverse": {
+			"version": "4.3.0",
+			"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
+			"integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+			"dev": true,
+			"engines": {
+				"node": ">=4.0"
+			}
+		},
+		"node_modules/estree-walker": {
+			"version": "2.0.2",
+			"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
+			"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
+		},
+		"node_modules/esutils": {
+			"version": "2.0.3",
+			"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+			"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+			"dev": true,
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/event-emitter": {
+			"version": "0.3.5",
+			"resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz",
+			"integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==",
+			"dependencies": {
+				"d": "1",
+				"es5-ext": "~0.10.14"
+			}
+		},
+		"node_modules/ext": {
+			"version": "1.7.0",
+			"resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz",
+			"integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==",
+			"dependencies": {
+				"type": "^2.7.2"
+			}
+		},
+		"node_modules/ext/node_modules/type": {
+			"version": "2.7.2",
+			"resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz",
+			"integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw=="
+		},
+		"node_modules/fast-deep-equal": {
+			"version": "3.1.3",
+			"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+			"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+			"dev": true
+		},
+		"node_modules/fast-glob": {
+			"version": "3.2.12",
+			"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz",
+			"integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==",
+			"dev": true,
+			"dependencies": {
+				"@nodelib/fs.stat": "^2.0.2",
+				"@nodelib/fs.walk": "^1.2.3",
+				"glob-parent": "^5.1.2",
+				"merge2": "^1.3.0",
+				"micromatch": "^4.0.4"
+			},
+			"engines": {
+				"node": ">=8.6.0"
+			}
+		},
+		"node_modules/fast-glob/node_modules/glob-parent": {
+			"version": "5.1.2",
+			"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+			"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+			"dev": true,
+			"dependencies": {
+				"is-glob": "^4.0.1"
+			},
+			"engines": {
+				"node": ">= 6"
+			}
+		},
+		"node_modules/fast-json-stable-stringify": {
+			"version": "2.1.0",
+			"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+			"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+			"dev": true
+		},
+		"node_modules/fast-levenshtein": {
+			"version": "2.0.6",
+			"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+			"integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+			"dev": true
+		},
+		"node_modules/fastq": {
+			"version": "1.15.0",
+			"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz",
+			"integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==",
+			"dev": true,
+			"dependencies": {
+				"reusify": "^1.0.4"
+			}
+		},
+		"node_modules/file-entry-cache": {
+			"version": "6.0.1",
+			"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
+			"integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
+			"dev": true,
+			"dependencies": {
+				"flat-cache": "^3.0.4"
+			},
+			"engines": {
+				"node": "^10.12.0 || >=12.0.0"
+			}
+		},
+		"node_modules/fill-range": {
+			"version": "7.0.1",
+			"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+			"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+			"dev": true,
+			"dependencies": {
+				"to-regex-range": "^5.0.1"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/find-up": {
+			"version": "5.0.0",
+			"resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+			"integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+			"dev": true,
+			"dependencies": {
+				"locate-path": "^6.0.0",
+				"path-exists": "^4.0.0"
+			},
+			"engines": {
+				"node": ">=10"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/flat-cache": {
+			"version": "3.0.4",
+			"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz",
+			"integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==",
+			"dev": true,
+			"dependencies": {
+				"flatted": "^3.1.0",
+				"rimraf": "^3.0.2"
+			},
+			"engines": {
+				"node": "^10.12.0 || >=12.0.0"
+			}
+		},
+		"node_modules/flatted": {
+			"version": "3.2.7",
+			"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz",
+			"integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==",
+			"dev": true
+		},
+		"node_modules/follow-redirects": {
+			"version": "1.15.2",
+			"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
+			"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
+			"funding": [
+				{
+					"type": "individual",
+					"url": "https://github.com/sponsors/RubenVerborgh"
+				}
+			],
+			"engines": {
+				"node": ">=4.0"
+			},
+			"peerDependenciesMeta": {
+				"debug": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/form-data": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+			"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+			"dependencies": {
+				"asynckit": "^0.4.0",
+				"combined-stream": "^1.0.8",
+				"mime-types": "^2.1.12"
+			},
+			"engines": {
+				"node": ">= 6"
+			}
+		},
+		"node_modules/fraction.js": {
+			"version": "4.2.0",
+			"resolved": "https://registry.npmmirror.com/fraction.js/-/fraction.js-4.2.0.tgz",
+			"integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==",
+			"dev": true,
+			"engines": {
+				"node": "*"
+			}
+		},
+		"node_modules/fs.realpath": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+			"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+			"dev": true
+		},
+		"node_modules/fsevents": {
+			"version": "2.3.2",
+			"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+			"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+			"dev": true,
+			"hasInstallScript": true,
+			"optional": true,
+			"os": [
+				"darwin"
+			],
+			"engines": {
+				"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+			}
+		},
+		"node_modules/function-bind": {
+			"version": "1.1.1",
+			"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+			"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
+		},
+		"node_modules/get-intrinsic": {
+			"version": "1.2.0",
+			"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz",
+			"integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==",
+			"dependencies": {
+				"function-bind": "^1.1.1",
+				"has": "^1.0.3",
+				"has-symbols": "^1.0.3"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/ljharb"
+			}
+		},
+		"node_modules/glob": {
+			"version": "7.2.3",
+			"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+			"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+			"dev": true,
+			"dependencies": {
+				"fs.realpath": "^1.0.0",
+				"inflight": "^1.0.4",
+				"inherits": "2",
+				"minimatch": "^3.1.1",
+				"once": "^1.3.0",
+				"path-is-absolute": "^1.0.0"
+			},
+			"engines": {
+				"node": "*"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/isaacs"
+			}
+		},
+		"node_modules/glob-parent": {
+			"version": "6.0.2",
+			"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+			"integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+			"dev": true,
+			"dependencies": {
+				"is-glob": "^4.0.3"
+			},
+			"engines": {
+				"node": ">=10.13.0"
+			}
+		},
+		"node_modules/globals": {
+			"version": "13.20.0",
+			"resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz",
+			"integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==",
+			"dev": true,
+			"dependencies": {
+				"type-fest": "^0.20.2"
+			},
+			"engines": {
+				"node": ">=8"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/globby": {
+			"version": "11.1.0",
+			"resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
+			"integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
+			"dev": true,
+			"dependencies": {
+				"array-union": "^2.1.0",
+				"dir-glob": "^3.0.1",
+				"fast-glob": "^3.2.9",
+				"ignore": "^5.2.0",
+				"merge2": "^1.4.1",
+				"slash": "^3.0.0"
+			},
+			"engines": {
+				"node": ">=10"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/good-listener": {
+			"version": "1.2.2",
+			"resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz",
+			"integrity": "sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw==",
+			"dependencies": {
+				"delegate": "^3.1.2"
+			}
+		},
+		"node_modules/grapheme-splitter": {
+			"version": "1.0.4",
+			"resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz",
+			"integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==",
+			"dev": true
+		},
+		"node_modules/has": {
+			"version": "1.0.3",
+			"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+			"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+			"dependencies": {
+				"function-bind": "^1.1.1"
+			},
+			"engines": {
+				"node": ">= 0.4.0"
+			}
+		},
+		"node_modules/has-flag": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+			"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+			"dev": true,
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/has-symbols": {
+			"version": "1.0.3",
+			"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
+			"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
+			"engines": {
+				"node": ">= 0.4"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/ljharb"
+			}
+		},
+		"node_modules/html-void-elements": {
+			"version": "2.0.1",
+			"resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-2.0.1.tgz",
+			"integrity": "sha512-0quDb7s97CfemeJAnW9wC0hw78MtW7NU3hqtCD75g2vFlDLt36llsYD7uB7SUzojLMP24N5IatXf7ylGXiGG9A==",
+			"funding": {
+				"type": "github",
+				"url": "https://github.com/sponsors/wooorm"
+			}
+		},
+		"node_modules/i18next": {
+			"version": "20.6.1",
+			"resolved": "https://registry.npmjs.org/i18next/-/i18next-20.6.1.tgz",
+			"integrity": "sha512-yCMYTMEJ9ihCwEQQ3phLo7I/Pwycf8uAx+sRHwwk5U9Aui/IZYgQRyMqXafQOw5QQ7DM1Z+WyEXWIqSuJHhG2A==",
+			"dependencies": {
+				"@babel/runtime": "^7.12.0"
+			}
+		},
+		"node_modules/ignore": {
+			"version": "5.2.4",
+			"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
+			"integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==",
+			"dev": true,
+			"engines": {
+				"node": ">= 4"
+			}
+		},
+		"node_modules/immer": {
+			"version": "9.0.19",
+			"resolved": "https://registry.npmjs.org/immer/-/immer-9.0.19.tgz",
+			"integrity": "sha512-eY+Y0qcsB4TZKwgQzLaE/lqYMlKhv5J9dyd2RhhtGhNo2njPXDqU9XPfcNfa3MIDsdtZt5KlkIsirlo4dHsWdQ==",
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/immer"
+			}
+		},
+		"node_modules/immutable": {
+			"version": "4.2.4",
+			"resolved": "https://registry.npmjs.org/immutable/-/immutable-4.2.4.tgz",
+			"integrity": "sha512-WDxL3Hheb1JkRN3sQkyujNlL/xRjAo3rJtaU5xeufUauG66JdMr32bLj4gF+vWl84DIA3Zxw7tiAjneYzRRw+w==",
+			"dev": true
+		},
+		"node_modules/import-fresh": {
+			"version": "3.3.0",
+			"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
+			"integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+			"dev": true,
+			"dependencies": {
+				"parent-module": "^1.0.0",
+				"resolve-from": "^4.0.0"
+			},
+			"engines": {
+				"node": ">=6"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/imurmurhash": {
+			"version": "0.1.4",
+			"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+			"integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+			"dev": true,
+			"engines": {
+				"node": ">=0.8.19"
+			}
+		},
+		"node_modules/inflight": {
+			"version": "1.0.6",
+			"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+			"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+			"dev": true,
+			"dependencies": {
+				"once": "^1.3.0",
+				"wrappy": "1"
+			}
+		},
+		"node_modules/inherits": {
+			"version": "2.0.4",
+			"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+			"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+			"dev": true
+		},
+		"node_modules/is-binary-path": {
+			"version": "2.1.0",
+			"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+			"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+			"dev": true,
+			"dependencies": {
+				"binary-extensions": "^2.0.0"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/is-core-module": {
+			"version": "2.11.0",
+			"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz",
+			"integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==",
+			"dev": true,
+			"dependencies": {
+				"has": "^1.0.3"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/ljharb"
+			}
+		},
+		"node_modules/is-extglob": {
+			"version": "2.1.1",
+			"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+			"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+			"dev": true,
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/is-glob": {
+			"version": "4.0.3",
+			"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+			"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+			"dev": true,
+			"dependencies": {
+				"is-extglob": "^2.1.1"
+			},
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/is-hotkey": {
+			"version": "0.2.0",
+			"resolved": "https://registry.npmjs.org/is-hotkey/-/is-hotkey-0.2.0.tgz",
+			"integrity": "sha512-UknnZK4RakDmTgz4PI1wIph5yxSs/mvChWs9ifnlXsKuXgWmOkY/hAE0H/k2MIqH0RlRye0i1oC07MCRSD28Mw=="
+		},
+		"node_modules/is-number": {
+			"version": "7.0.0",
+			"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+			"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+			"dev": true,
+			"engines": {
+				"node": ">=0.12.0"
+			}
+		},
+		"node_modules/is-path-inside": {
+			"version": "3.0.3",
+			"resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
+			"integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
+			"dev": true,
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/is-plain-object": {
+			"version": "5.0.0",
+			"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
+			"integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==",
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/is-url": {
+			"version": "1.2.4",
+			"resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz",
+			"integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww=="
+		},
+		"node_modules/isexe": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+			"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+			"dev": true
+		},
+		"node_modules/js-cookie": {
+			"version": "3.0.1",
+			"resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.1.tgz",
+			"integrity": "sha512-+0rgsUXZu4ncpPxRL+lNEptWMOWl9etvPHc/koSRp6MPwpRYAhmk0dUG00J4bxVV3r9uUzfo24wW0knS07SKSw==",
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/js-sdsl": {
+			"version": "4.3.0",
+			"resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz",
+			"integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==",
+			"dev": true,
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/js-sdsl"
+			}
+		},
+		"node_modules/js-table2excel": {
+			"version": "1.0.3",
+			"resolved": "https://registry.npmjs.org/js-table2excel/-/js-table2excel-1.0.3.tgz",
+			"integrity": "sha512-ivzOdgYqOD3zqzJZfW0Nm35P9BWffxD0Unwr+2QBeEawV7hhRY9RHBVNcvO6A9PhGkMyqPVL/u4/NeufaTTTXw=="
+		},
+		"node_modules/js-yaml": {
+			"version": "4.1.0",
+			"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+			"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+			"dev": true,
+			"dependencies": {
+				"argparse": "^2.0.1"
+			},
+			"bin": {
+				"js-yaml": "bin/js-yaml.js"
+			}
+		},
+		"node_modules/json-schema-traverse": {
+			"version": "0.4.1",
+			"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+			"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+			"dev": true
+		},
+		"node_modules/json-stable-stringify-without-jsonify": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+			"integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+			"dev": true
+		},
+		"node_modules/jsplumb": {
+			"version": "2.15.6",
+			"resolved": "https://registry.npmjs.org/jsplumb/-/jsplumb-2.15.6.tgz",
+			"integrity": "sha512-sIpbpz5eMVM+vV+MQzFCidlaa1RsknrQs6LOTKYDjYUDdTAi2AN2bFi94TxB33TifcIsRNV1jebcaxg0tCoPzg=="
+		},
+		"node_modules/levn": {
+			"version": "0.4.1",
+			"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+			"integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+			"dev": true,
+			"dependencies": {
+				"prelude-ls": "^1.2.1",
+				"type-check": "~0.4.0"
+			},
+			"engines": {
+				"node": ">= 0.8.0"
+			}
+		},
+		"node_modules/lilconfig": {
+			"version": "2.0.6",
+			"resolved": "https://registry.npmmirror.com/lilconfig/-/lilconfig-2.0.6.tgz",
+			"integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==",
+			"dev": true,
+			"engines": {
+				"node": ">=10"
+			}
+		},
+		"node_modules/locate-path": {
+			"version": "6.0.0",
+			"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+			"integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+			"dev": true,
+			"dependencies": {
+				"p-locate": "^5.0.0"
+			},
+			"engines": {
+				"node": ">=10"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/lodash": {
+			"version": "4.17.21",
+			"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+			"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
+		},
+		"node_modules/lodash-es": {
+			"version": "4.17.21",
+			"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
+			"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
+		},
+		"node_modules/lodash-unified": {
+			"version": "1.0.3",
+			"resolved": "https://registry.npmjs.org/lodash-unified/-/lodash-unified-1.0.3.tgz",
+			"integrity": "sha512-WK9qSozxXOD7ZJQlpSqOT+om2ZfcT4yO+03FuzAHD0wF6S0l0090LRPDx3vhTTLZ8cFKpBn+IOcVXK6qOcIlfQ==",
+			"peerDependencies": {
+				"@types/lodash-es": "*",
+				"lodash": "*",
+				"lodash-es": "*"
+			}
+		},
+		"node_modules/lodash.camelcase": {
+			"version": "4.3.0",
+			"resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
+			"integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA=="
+		},
+		"node_modules/lodash.clonedeep": {
+			"version": "4.5.0",
+			"resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
+			"integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ=="
+		},
+		"node_modules/lodash.debounce": {
+			"version": "4.0.8",
+			"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
+			"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="
+		},
+		"node_modules/lodash.foreach": {
+			"version": "4.5.0",
+			"resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz",
+			"integrity": "sha512-aEXTF4d+m05rVOAUG3z4vZZ4xVexLKZGF0lIxuHZ1Hplpk/3B6Z1+/ICICYRLm7c41Z2xiejbkCkJoTlypoXhQ=="
+		},
+		"node_modules/lodash.isequal": {
+			"version": "4.5.0",
+			"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
+			"integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ=="
+		},
+		"node_modules/lodash.merge": {
+			"version": "4.6.2",
+			"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+			"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+			"dev": true
+		},
+		"node_modules/lodash.throttle": {
+			"version": "4.1.1",
+			"resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
+			"integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ=="
+		},
+		"node_modules/lodash.toarray": {
+			"version": "4.4.0",
+			"resolved": "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz",
+			"integrity": "sha512-QyffEA3i5dma5q2490+SgCvDN0pXLmRGSyAANuVi0HQ01Pkfr9fuoKQW8wm1wGBnJITs/mS7wQvS6VshUEBFCw=="
+		},
+		"node_modules/lru-cache": {
+			"version": "6.0.0",
+			"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+			"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+			"dev": true,
+			"dependencies": {
+				"yallist": "^4.0.0"
+			},
+			"engines": {
+				"node": ">=10"
+			}
+		},
+		"node_modules/magic-string": {
+			"version": "0.25.9",
+			"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz",
+			"integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==",
+			"dependencies": {
+				"sourcemap-codec": "^1.4.8"
+			}
+		},
+		"node_modules/memoize-one": {
+			"version": "6.0.0",
+			"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz",
+			"integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw=="
+		},
+		"node_modules/merge2": {
+			"version": "1.4.1",
+			"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+			"integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+			"dev": true,
+			"engines": {
+				"node": ">= 8"
+			}
+		},
+		"node_modules/micromatch": {
+			"version": "4.0.5",
+			"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
+			"integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
+			"dev": true,
+			"dependencies": {
+				"braces": "^3.0.2",
+				"picomatch": "^2.3.1"
+			},
+			"engines": {
+				"node": ">=8.6"
+			}
+		},
+		"node_modules/mime-db": {
+			"version": "1.52.0",
+			"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+			"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+			"engines": {
+				"node": ">= 0.6"
+			}
+		},
+		"node_modules/mime-match": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/mime-match/-/mime-match-1.0.2.tgz",
+			"integrity": "sha512-VXp/ugGDVh3eCLOBCiHZMYWQaTNUHv2IJrut+yXA6+JbLPXHglHwfS/5A5L0ll+jkCY7fIzRJcH6OIunF+c6Cg==",
+			"dependencies": {
+				"wildcard": "^1.1.0"
+			}
+		},
+		"node_modules/mime-types": {
+			"version": "2.1.35",
+			"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+			"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+			"dependencies": {
+				"mime-db": "1.52.0"
+			},
+			"engines": {
+				"node": ">= 0.6"
+			}
+		},
+		"node_modules/minimatch": {
+			"version": "3.1.2",
+			"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+			"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+			"dev": true,
+			"dependencies": {
+				"brace-expansion": "^1.1.7"
+			},
+			"engines": {
+				"node": "*"
+			}
+		},
+		"node_modules/minimist": {
+			"version": "1.2.8",
+			"resolved": "https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz",
+			"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+			"dev": true
+		},
+		"node_modules/mitt": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.0.tgz",
+			"integrity": "sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ=="
+		},
+		"node_modules/ms": {
+			"version": "2.1.2",
+			"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+			"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+			"dev": true
+		},
+		"node_modules/namespace-emitter": {
+			"version": "2.0.1",
+			"resolved": "https://registry.npmjs.org/namespace-emitter/-/namespace-emitter-2.0.1.tgz",
+			"integrity": "sha512-N/sMKHniSDJBjfrkbS/tpkPj4RAbvW3mr8UAzvlMHyun93XEm83IAvhWtJVHo+RHn/oO8Job5YN4b+wRjSVp5g=="
+		},
+		"node_modules/nanoid": {
+			"version": "3.3.4",
+			"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
+			"integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
+			"bin": {
+				"nanoid": "bin/nanoid.cjs"
+			},
+			"engines": {
+				"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+			}
+		},
+		"node_modules/natural-compare": {
+			"version": "1.4.0",
+			"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+			"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+			"dev": true
+		},
+		"node_modules/natural-compare-lite": {
+			"version": "1.4.0",
+			"resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz",
+			"integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==",
+			"dev": true
+		},
+		"node_modules/next-tick": {
+			"version": "1.1.0",
+			"resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz",
+			"integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ=="
+		},
+		"node_modules/node-releases": {
+			"version": "2.0.10",
+			"resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.10.tgz",
+			"integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==",
+			"dev": true
+		},
+		"node_modules/normalize-path": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+			"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+			"dev": true,
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/normalize-range": {
+			"version": "0.1.2",
+			"resolved": "https://registry.npmmirror.com/normalize-range/-/normalize-range-0.1.2.tgz",
+			"integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==",
+			"dev": true,
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/normalize-wheel-es": {
+			"version": "1.2.0",
+			"resolved": "https://registry.npmjs.org/normalize-wheel-es/-/normalize-wheel-es-1.2.0.tgz",
+			"integrity": "sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw=="
+		},
+		"node_modules/nprogress": {
+			"version": "0.2.0",
+			"resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz",
+			"integrity": "sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA=="
+		},
+		"node_modules/nth-check": {
+			"version": "2.1.1",
+			"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
+			"integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
+			"dev": true,
+			"dependencies": {
+				"boolbase": "^1.0.0"
+			},
+			"funding": {
+				"url": "https://github.com/fb55/nth-check?sponsor=1"
+			}
+		},
+		"node_modules/object-hash": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmmirror.com/object-hash/-/object-hash-3.0.0.tgz",
+			"integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
+			"dev": true,
+			"engines": {
+				"node": ">= 6"
+			}
+		},
+		"node_modules/object-inspect": {
+			"version": "1.12.3",
+			"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz",
+			"integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==",
+			"funding": {
+				"url": "https://github.com/sponsors/ljharb"
+			}
+		},
+		"node_modules/once": {
+			"version": "1.4.0",
+			"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+			"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+			"dev": true,
+			"dependencies": {
+				"wrappy": "1"
+			}
+		},
+		"node_modules/optionator": {
+			"version": "0.9.1",
+			"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
+			"integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==",
+			"dev": true,
+			"dependencies": {
+				"deep-is": "^0.1.3",
+				"fast-levenshtein": "^2.0.6",
+				"levn": "^0.4.1",
+				"prelude-ls": "^1.2.1",
+				"type-check": "^0.4.0",
+				"word-wrap": "^1.2.3"
+			},
+			"engines": {
+				"node": ">= 0.8.0"
+			}
+		},
+		"node_modules/p-limit": {
+			"version": "3.1.0",
+			"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+			"integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+			"dev": true,
+			"dependencies": {
+				"yocto-queue": "^0.1.0"
+			},
+			"engines": {
+				"node": ">=10"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/p-locate": {
+			"version": "5.0.0",
+			"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+			"integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+			"dev": true,
+			"dependencies": {
+				"p-limit": "^3.0.2"
+			},
+			"engines": {
+				"node": ">=10"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/parent-module": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+			"integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+			"dev": true,
+			"dependencies": {
+				"callsites": "^3.0.0"
+			},
+			"engines": {
+				"node": ">=6"
+			}
+		},
+		"node_modules/path-exists": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+			"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+			"dev": true,
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/path-is-absolute": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+			"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+			"dev": true,
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/path-key": {
+			"version": "3.1.1",
+			"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+			"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+			"dev": true,
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/path-parse": {
+			"version": "1.0.7",
+			"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+			"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+			"dev": true
+		},
+		"node_modules/path-type": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
+			"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+			"dev": true,
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/picocolors": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
+			"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
+		},
+		"node_modules/picomatch": {
+			"version": "2.3.1",
+			"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+			"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+			"dev": true,
+			"engines": {
+				"node": ">=8.6"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/jonschlinkert"
+			}
+		},
+		"node_modules/pify": {
+			"version": "2.3.0",
+			"resolved": "https://registry.npmmirror.com/pify/-/pify-2.3.0.tgz",
+			"integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
+			"dev": true,
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/pinia": {
+			"version": "2.0.32",
+			"resolved": "https://registry.npmjs.org/pinia/-/pinia-2.0.32.tgz",
+			"integrity": "sha512-8Tw4OrpCSJ028UUyp0gYPP/wyjigLoEceuO/x1G+FlHVf73337e5vLm4uDmrRIoBG1hvaed/eSHnrCFjOc4nkA==",
+			"dependencies": {
+				"@vue/devtools-api": "^6.5.0",
+				"vue-demi": "*"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/posva"
+			},
+			"peerDependencies": {
+				"@vue/composition-api": "^1.4.0",
+				"typescript": ">=4.4.4",
+				"vue": "^2.6.14 || ^3.2.0"
+			},
+			"peerDependenciesMeta": {
+				"@vue/composition-api": {
+					"optional": true
+				},
+				"typescript": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/pinia/node_modules/vue-demi": {
+			"version": "0.13.11",
+			"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.13.11.tgz",
+			"integrity": "sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==",
+			"hasInstallScript": true,
+			"bin": {
+				"vue-demi-fix": "bin/vue-demi-fix.js",
+				"vue-demi-switch": "bin/vue-demi-switch.js"
+			},
+			"engines": {
+				"node": ">=12"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/antfu"
+			},
+			"peerDependencies": {
+				"@vue/composition-api": "^1.0.0-rc.1",
+				"vue": "^3.0.0-0 || ^2.6.0"
+			},
+			"peerDependenciesMeta": {
+				"@vue/composition-api": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/postcss": {
+			"version": "8.4.21",
+			"resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.4.21.tgz",
+			"integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==",
+			"dependencies": {
+				"nanoid": "^3.3.4",
+				"picocolors": "^1.0.0",
+				"source-map-js": "^1.0.2"
+			},
+			"engines": {
+				"node": "^10 || ^12 || >=14"
+			}
+		},
+		"node_modules/postcss-import": {
+			"version": "14.1.0",
+			"resolved": "https://registry.npmmirror.com/postcss-import/-/postcss-import-14.1.0.tgz",
+			"integrity": "sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==",
+			"dev": true,
+			"dependencies": {
+				"postcss-value-parser": "^4.0.0",
+				"read-cache": "^1.0.0",
+				"resolve": "^1.1.7"
+			},
+			"engines": {
+				"node": ">=10.0.0"
+			},
+			"peerDependencies": {
+				"postcss": "^8.0.0"
+			}
+		},
+		"node_modules/postcss-js": {
+			"version": "4.0.1",
+			"resolved": "https://registry.npmmirror.com/postcss-js/-/postcss-js-4.0.1.tgz",
+			"integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==",
+			"dev": true,
+			"dependencies": {
+				"camelcase-css": "^2.0.1"
+			},
+			"engines": {
+				"node": "^12 || ^14 || >= 16"
+			},
+			"peerDependencies": {
+				"postcss": "^8.4.21"
+			}
+		},
+		"node_modules/postcss-load-config": {
+			"version": "3.1.4",
+			"resolved": "https://registry.npmmirror.com/postcss-load-config/-/postcss-load-config-3.1.4.tgz",
+			"integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==",
+			"dev": true,
+			"dependencies": {
+				"lilconfig": "^2.0.5",
+				"yaml": "^1.10.2"
+			},
+			"engines": {
+				"node": ">= 10"
+			},
+			"peerDependencies": {
+				"postcss": ">=8.0.9",
+				"ts-node": ">=9.0.0"
+			},
+			"peerDependenciesMeta": {
+				"postcss": {
+					"optional": true
+				},
+				"ts-node": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/postcss-nested": {
+			"version": "6.0.0",
+			"resolved": "https://registry.npmmirror.com/postcss-nested/-/postcss-nested-6.0.0.tgz",
+			"integrity": "sha512-0DkamqrPcmkBDsLn+vQDIrtkSbNkv5AD/M322ySo9kqFkCIYklym2xEmWkwo+Y3/qZo34tzEPNUw4y7yMCdv5w==",
+			"dev": true,
+			"dependencies": {
+				"postcss-selector-parser": "^6.0.10"
+			},
+			"engines": {
+				"node": ">=12.0"
+			},
+			"peerDependencies": {
+				"postcss": "^8.2.14"
+			}
+		},
+		"node_modules/postcss-selector-parser": {
+			"version": "6.0.11",
+			"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz",
+			"integrity": "sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g==",
+			"dev": true,
+			"dependencies": {
+				"cssesc": "^3.0.0",
+				"util-deprecate": "^1.0.2"
+			},
+			"engines": {
+				"node": ">=4"
+			}
+		},
+		"node_modules/postcss-value-parser": {
+			"version": "4.2.0",
+			"resolved": "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
+			"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
+			"dev": true
+		},
+		"node_modules/preact": {
+			"version": "10.12.1",
+			"resolved": "https://registry.npmjs.org/preact/-/preact-10.12.1.tgz",
+			"integrity": "sha512-l8386ixSsBdbreOAkqtrwqHwdvR35ID8c3rKPa8lCWuO86dBi32QWHV4vfsZK1utLLFMvw+Z5Ad4XLkZzchscg==",
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/preact"
+			}
+		},
+		"node_modules/prelude-ls": {
+			"version": "1.2.1",
+			"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+			"integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+			"dev": true,
+			"engines": {
+				"node": ">= 0.8.0"
+			}
+		},
+		"node_modules/prettier": {
+			"version": "2.8.4",
+			"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.4.tgz",
+			"integrity": "sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==",
+			"dev": true,
+			"bin": {
+				"prettier": "bin-prettier.js"
+			},
+			"engines": {
+				"node": ">=10.13.0"
+			},
+			"funding": {
+				"url": "https://github.com/prettier/prettier?sponsor=1"
+			}
+		},
+		"node_modules/print-js": {
+			"version": "1.6.0",
+			"resolved": "https://registry.npmjs.org/print-js/-/print-js-1.6.0.tgz",
+			"integrity": "sha512-BfnOIzSKbqGRtO4o0rnj/K3681BSd2QUrsIZy/+WdCIugjIswjmx3lDEZpXB2ruGf9d4b3YNINri81+J0FsBWg=="
+		},
+		"node_modules/prismjs": {
+			"version": "1.29.0",
+			"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz",
+			"integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==",
+			"engines": {
+				"node": ">=6"
+			}
+		},
+		"node_modules/proxy-from-env": {
+			"version": "1.1.0",
+			"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+			"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
+		},
+		"node_modules/punycode": {
+			"version": "2.3.0",
+			"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz",
+			"integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==",
+			"dev": true,
+			"engines": {
+				"node": ">=6"
+			}
+		},
+		"node_modules/qrcodejs2-fixes": {
+			"version": "0.0.2",
+			"resolved": "https://registry.npmjs.org/qrcodejs2-fixes/-/qrcodejs2-fixes-0.0.2.tgz",
+			"integrity": "sha512-wMUXYMOixAEJlLnjk5MbLiFaz0gQObWYm/TIFWB5+j7sTY5gPyr09Cx1EpcLYbsgfFdN3wHjrKAhZofTuCBGhg=="
+		},
+		"node_modules/qs": {
+			"version": "6.11.0",
+			"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
+			"integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
+			"dependencies": {
+				"side-channel": "^1.0.4"
+			},
+			"engines": {
+				"node": ">=0.6"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/ljharb"
+			}
+		},
+		"node_modules/queue-microtask": {
+			"version": "1.2.3",
+			"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+			"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+			"dev": true,
+			"funding": [
+				{
+					"type": "github",
+					"url": "https://github.com/sponsors/feross"
+				},
+				{
+					"type": "patreon",
+					"url": "https://www.patreon.com/feross"
+				},
+				{
+					"type": "consulting",
+					"url": "https://feross.org/support"
+				}
+			]
+		},
+		"node_modules/quick-lru": {
+			"version": "5.1.1",
+			"resolved": "https://registry.npmmirror.com/quick-lru/-/quick-lru-5.1.1.tgz",
+			"integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==",
+			"dev": true,
+			"engines": {
+				"node": ">=10"
+			}
+		},
+		"node_modules/read-cache": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmmirror.com/read-cache/-/read-cache-1.0.0.tgz",
+			"integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==",
+			"dev": true,
+			"dependencies": {
+				"pify": "^2.3.0"
+			}
+		},
+		"node_modules/readdirp": {
+			"version": "3.6.0",
+			"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+			"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+			"dev": true,
+			"dependencies": {
+				"picomatch": "^2.2.1"
+			},
+			"engines": {
+				"node": ">=8.10.0"
+			}
+		},
+		"node_modules/regenerator-runtime": {
+			"version": "0.13.11",
+			"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
+			"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg=="
+		},
+		"node_modules/regexpp": {
+			"version": "3.2.0",
+			"resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz",
+			"integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==",
+			"dev": true,
+			"engines": {
+				"node": ">=8"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/mysticatea"
+			}
+		},
+		"node_modules/resolve": {
+			"version": "1.22.1",
+			"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
+			"integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
+			"dev": true,
+			"dependencies": {
+				"is-core-module": "^2.9.0",
+				"path-parse": "^1.0.7",
+				"supports-preserve-symlinks-flag": "^1.0.0"
+			},
+			"bin": {
+				"resolve": "bin/resolve"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/ljharb"
+			}
+		},
+		"node_modules/resolve-from": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+			"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+			"dev": true,
+			"engines": {
+				"node": ">=4"
+			}
+		},
+		"node_modules/reusify": {
+			"version": "1.0.4",
+			"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
+			"integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+			"dev": true,
+			"engines": {
+				"iojs": ">=1.0.0",
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/rimraf": {
+			"version": "3.0.2",
+			"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+			"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+			"dev": true,
+			"dependencies": {
+				"glob": "^7.1.3"
+			},
+			"bin": {
+				"rimraf": "bin.js"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/isaacs"
+			}
+		},
+		"node_modules/rollup": {
+			"version": "3.17.2",
+			"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.17.2.tgz",
+			"integrity": "sha512-qMNZdlQPCkWodrAZ3qnJtvCAl4vpQ8q77uEujVCCbC/6CLB7Lcmvjq7HyiOSnf4fxTT9XgsE36oLHJBH49xjqA==",
+			"dev": true,
+			"bin": {
+				"rollup": "dist/bin/rollup"
+			},
+			"engines": {
+				"node": ">=14.18.0",
+				"npm": ">=8.0.0"
+			},
+			"optionalDependencies": {
+				"fsevents": "~2.3.2"
+			}
+		},
+		"node_modules/run-parallel": {
+			"version": "1.2.0",
+			"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+			"integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+			"dev": true,
+			"funding": [
+				{
+					"type": "github",
+					"url": "https://github.com/sponsors/feross"
+				},
+				{
+					"type": "patreon",
+					"url": "https://www.patreon.com/feross"
+				},
+				{
+					"type": "consulting",
+					"url": "https://feross.org/support"
+				}
+			],
+			"dependencies": {
+				"queue-microtask": "^1.2.2"
+			}
+		},
+		"node_modules/sass": {
+			"version": "1.58.3",
+			"resolved": "https://registry.npmjs.org/sass/-/sass-1.58.3.tgz",
+			"integrity": "sha512-Q7RaEtYf6BflYrQ+buPudKR26/lH+10EmO9bBqbmPh/KeLqv8bjpTNqxe71ocONqXq+jYiCbpPUmQMS+JJPk4A==",
+			"dev": true,
+			"dependencies": {
+				"chokidar": ">=3.0.0 <4.0.0",
+				"immutable": "^4.0.0",
+				"source-map-js": ">=0.6.2 <2.0.0"
+			},
+			"bin": {
+				"sass": "sass.js"
+			},
+			"engines": {
+				"node": ">=12.0.0"
+			}
+		},
+		"node_modules/screenfull": {
+			"version": "6.0.2",
+			"resolved": "https://registry.npmjs.org/screenfull/-/screenfull-6.0.2.tgz",
+			"integrity": "sha512-AQdy8s4WhNvUZ6P8F6PB21tSPIYKniic+Ogx0AacBMjKP1GUHN2E9URxQHtCusiwxudnCKkdy4GrHXPPJSkCCw==",
+			"engines": {
+				"node": "^14.13.1 || >=16.0.0"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/scroll-into-view-if-needed": {
+			"version": "2.2.31",
+			"resolved": "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.31.tgz",
+			"integrity": "sha512-dGCXy99wZQivjmjIqihaBQNjryrz5rueJY7eHfTdyWEiR4ttYpsajb14rn9s5d4DY4EcY6+4+U/maARBXJedkA==",
+			"dependencies": {
+				"compute-scroll-into-view": "^1.0.20"
+			}
+		},
+		"node_modules/select": {
+			"version": "1.1.2",
+			"resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz",
+			"integrity": "sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA=="
+		},
+		"node_modules/semver": {
+			"version": "7.3.8",
+			"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
+			"integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
+			"dev": true,
+			"dependencies": {
+				"lru-cache": "^6.0.0"
+			},
+			"bin": {
+				"semver": "bin/semver.js"
+			},
+			"engines": {
+				"node": ">=10"
+			}
+		},
+		"node_modules/shebang-command": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+			"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+			"dev": true,
+			"dependencies": {
+				"shebang-regex": "^3.0.0"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/shebang-regex": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+			"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+			"dev": true,
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/side-channel": {
+			"version": "1.0.4",
+			"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
+			"integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
+			"dependencies": {
+				"call-bind": "^1.0.0",
+				"get-intrinsic": "^1.0.2",
+				"object-inspect": "^1.9.0"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/ljharb"
+			}
+		},
+		"node_modules/slash": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+			"integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+			"dev": true,
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/slate": {
+			"version": "0.72.8",
+			"resolved": "https://registry.npmjs.org/slate/-/slate-0.72.8.tgz",
+			"integrity": "sha512-/nJwTswQgnRurpK+bGJFH1oM7naD5qDmHd89JyiKNT2oOKD8marW0QSBtuFnwEbL5aGCS8AmrhXQgNOsn4osAw==",
+			"dependencies": {
+				"immer": "^9.0.6",
+				"is-plain-object": "^5.0.0",
+				"tiny-warning": "^1.0.3"
+			}
+		},
+		"node_modules/slate-history": {
+			"version": "0.66.0",
+			"resolved": "https://registry.npmjs.org/slate-history/-/slate-history-0.66.0.tgz",
+			"integrity": "sha512-6MWpxGQZiMvSINlCbMW43E2YBSVMCMCIwQfBzGssjWw4kb0qfvj0pIdblWNRQZD0hR6WHP+dHHgGSeVdMWzfng==",
+			"dependencies": {
+				"is-plain-object": "^5.0.0"
+			},
+			"peerDependencies": {
+				"slate": ">=0.65.3"
+			}
+		},
+		"node_modules/snabbdom": {
+			"version": "3.5.1",
+			"resolved": "https://registry.npmjs.org/snabbdom/-/snabbdom-3.5.1.tgz",
+			"integrity": "sha512-wHMNIOjkm/YNE5EM3RCbr/+DVgPg6AqQAX1eOxO46zYNvCXjKP5Y865tqQj3EXnaMBjkxmQA5jFuDpDK/dbfiA==",
+			"engines": {
+				"node": ">=8.3.0"
+			}
+		},
+		"node_modules/sortablejs": {
+			"version": "1.15.0",
+			"resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.15.0.tgz",
+			"integrity": "sha512-bv9qgVMjUMf89wAvM6AxVvS/4MX3sPeN0+agqShejLU5z5GX4C75ow1O2e5k4L6XItUyAK3gH6AxSbXrOM5e8w=="
+		},
+		"node_modules/source-map": {
+			"version": "0.6.1",
+			"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+			"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/source-map-js": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
+			"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/sourcemap-codec": {
+			"version": "1.4.8",
+			"resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
+			"integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
+			"deprecated": "Please use @jridgewell/sourcemap-codec instead"
+		},
+		"node_modules/splitpanes": {
+			"version": "3.1.5",
+			"resolved": "https://registry.npmjs.org/splitpanes/-/splitpanes-3.1.5.tgz",
+			"integrity": "sha512-r3Mq2ITFQ5a2VXLOy4/Sb2Ptp7OfEO8YIbhVJqJXoFc9hc5nTXXkCvtVDjIGbvC0vdE7tse+xTM9BMjsszP6bw==",
+			"funding": {
+				"url": "https://github.com/sponsors/antoniandre"
+			}
+		},
+		"node_modules/ssr-window": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/ssr-window/-/ssr-window-3.0.0.tgz",
+			"integrity": "sha512-q+8UfWDg9Itrg0yWK7oe5p/XRCJpJF9OBtXfOPgSJl+u3Xd5KI328RUEvUqSMVM9CiQUEf1QdBzJMkYGErj9QA=="
+		},
+		"node_modules/strip-ansi": {
+			"version": "6.0.1",
+			"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+			"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+			"dev": true,
+			"dependencies": {
+				"ansi-regex": "^5.0.1"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/strip-json-comments": {
+			"version": "3.1.1",
+			"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+			"integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+			"dev": true,
+			"engines": {
+				"node": ">=8"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/supports-color": {
+			"version": "7.2.0",
+			"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+			"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+			"dev": true,
+			"dependencies": {
+				"has-flag": "^4.0.0"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/supports-preserve-symlinks-flag": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+			"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+			"dev": true,
+			"engines": {
+				"node": ">= 0.4"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/ljharb"
+			}
+		},
+		"node_modules/tailwindcss": {
+			"version": "3.2.7",
+			"resolved": "https://registry.npmmirror.com/tailwindcss/-/tailwindcss-3.2.7.tgz",
+			"integrity": "sha512-B6DLqJzc21x7wntlH/GsZwEXTBttVSl1FtCzC8WP4oBc/NKef7kaax5jeihkkCEWc831/5NDJ9gRNDK6NEioQQ==",
+			"dev": true,
+			"dependencies": {
+				"arg": "^5.0.2",
+				"chokidar": "^3.5.3",
+				"color-name": "^1.1.4",
+				"detective": "^5.2.1",
+				"didyoumean": "^1.2.2",
+				"dlv": "^1.1.3",
+				"fast-glob": "^3.2.12",
+				"glob-parent": "^6.0.2",
+				"is-glob": "^4.0.3",
+				"lilconfig": "^2.0.6",
+				"micromatch": "^4.0.5",
+				"normalize-path": "^3.0.0",
+				"object-hash": "^3.0.0",
+				"picocolors": "^1.0.0",
+				"postcss": "^8.0.9",
+				"postcss-import": "^14.1.0",
+				"postcss-js": "^4.0.0",
+				"postcss-load-config": "^3.1.4",
+				"postcss-nested": "6.0.0",
+				"postcss-selector-parser": "^6.0.11",
+				"postcss-value-parser": "^4.2.0",
+				"quick-lru": "^5.1.1",
+				"resolve": "^1.22.1"
+			},
+			"bin": {
+				"tailwind": "lib/cli.js",
+				"tailwindcss": "lib/cli.js"
+			},
+			"engines": {
+				"node": ">=12.13.0"
+			},
+			"peerDependencies": {
+				"postcss": "^8.0.9"
+			}
+		},
+		"node_modules/text-table": {
+			"version": "0.2.0",
+			"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+			"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
+			"dev": true
+		},
+		"node_modules/tiny-emitter": {
+			"version": "2.1.0",
+			"resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
+			"integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q=="
+		},
+		"node_modules/tiny-warning": {
+			"version": "1.0.3",
+			"resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz",
+			"integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA=="
+		},
+		"node_modules/to-regex-range": {
+			"version": "5.0.1",
+			"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+			"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+			"dev": true,
+			"dependencies": {
+				"is-number": "^7.0.0"
+			},
+			"engines": {
+				"node": ">=8.0"
+			}
+		},
+		"node_modules/ts-enum-util": {
+			"version": "4.0.2",
+			"resolved": "https://registry.npmmirror.com/ts-enum-util/-/ts-enum-util-4.0.2.tgz",
+			"integrity": "sha512-BB5qjvHYgYgOB/CaoA1Cy/B2QNnZ+nVBrJ15VV/AXGWx+AO83k5wgeLOJvkSLoKKavvH/M8Wj4ZbgROjsuYwzw=="
+		},
+		"node_modules/tslib": {
+			"version": "2.3.0",
+			"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz",
+			"integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg=="
+		},
+		"node_modules/tsutils": {
+			"version": "3.21.0",
+			"resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz",
+			"integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==",
+			"dev": true,
+			"dependencies": {
+				"tslib": "^1.8.1"
+			},
+			"engines": {
+				"node": ">= 6"
+			},
+			"peerDependencies": {
+				"typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta"
+			}
+		},
+		"node_modules/tsutils/node_modules/tslib": {
+			"version": "1.14.1",
+			"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+			"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+			"dev": true
+		},
+		"node_modules/type": {
+			"version": "1.2.0",
+			"resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
+			"integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg=="
+		},
+		"node_modules/type-check": {
+			"version": "0.4.0",
+			"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+			"integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+			"dev": true,
+			"dependencies": {
+				"prelude-ls": "^1.2.1"
+			},
+			"engines": {
+				"node": ">= 0.8.0"
+			}
+		},
+		"node_modules/type-fest": {
+			"version": "0.20.2",
+			"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+			"integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+			"dev": true,
+			"engines": {
+				"node": ">=10"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/typescript": {
+			"version": "4.9.5",
+			"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
+			"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
+			"devOptional": true,
+			"bin": {
+				"tsc": "bin/tsc",
+				"tsserver": "bin/tsserver"
+			},
+			"engines": {
+				"node": ">=4.2.0"
+			}
+		},
+		"node_modules/update-browserslist-db": {
+			"version": "1.0.10",
+			"resolved": "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz",
+			"integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==",
+			"dev": true,
+			"dependencies": {
+				"escalade": "^3.1.1",
+				"picocolors": "^1.0.0"
+			},
+			"bin": {
+				"browserslist-lint": "cli.js"
+			},
+			"peerDependencies": {
+				"browserslist": ">= 4.21.0"
+			}
+		},
+		"node_modules/uri-js": {
+			"version": "4.4.1",
+			"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+			"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+			"dev": true,
+			"dependencies": {
+				"punycode": "^2.1.0"
+			}
+		},
+		"node_modules/util-deprecate": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+			"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+			"dev": true
+		},
+		"node_modules/vite": {
+			"version": "4.1.4",
+			"resolved": "https://registry.npmjs.org/vite/-/vite-4.1.4.tgz",
+			"integrity": "sha512-3knk/HsbSTKEin43zHu7jTwYWv81f8kgAL99G5NWBcA1LKvtvcVAC4JjBH1arBunO9kQka+1oGbrMKOjk4ZrBg==",
+			"dev": true,
+			"dependencies": {
+				"esbuild": "^0.16.14",
+				"postcss": "^8.4.21",
+				"resolve": "^1.22.1",
+				"rollup": "^3.10.0"
+			},
+			"bin": {
+				"vite": "bin/vite.js"
+			},
+			"engines": {
+				"node": "^14.18.0 || >=16.0.0"
+			},
+			"optionalDependencies": {
+				"fsevents": "~2.3.2"
+			},
+			"peerDependencies": {
+				"@types/node": ">= 14",
+				"less": "*",
+				"sass": "*",
+				"stylus": "*",
+				"sugarss": "*",
+				"terser": "^5.4.0"
+			},
+			"peerDependenciesMeta": {
+				"@types/node": {
+					"optional": true
+				},
+				"less": {
+					"optional": true
+				},
+				"sass": {
+					"optional": true
+				},
+				"stylus": {
+					"optional": true
+				},
+				"sugarss": {
+					"optional": true
+				},
+				"terser": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/vite-plugin-vue-setup-extend": {
+			"version": "0.4.0",
+			"resolved": "https://registry.npmjs.org/vite-plugin-vue-setup-extend/-/vite-plugin-vue-setup-extend-0.4.0.tgz",
+			"integrity": "sha512-WMbjPCui75fboFoUTHhdbXzu4Y/bJMv5N9QT9a7do3wNMNHHqrk+Tn2jrSJU0LS5fGl/EG+FEDBYVUeWIkDqXQ==",
+			"dev": true,
+			"dependencies": {
+				"@vue/compiler-sfc": "^3.2.29",
+				"magic-string": "^0.25.7"
+			},
+			"peerDependencies": {
+				"vite": ">=2.0.0"
+			}
+		},
+		"node_modules/vue": {
+			"version": "3.2.47",
+			"resolved": "https://registry.npmjs.org/vue/-/vue-3.2.47.tgz",
+			"integrity": "sha512-60188y/9Dc9WVrAZeUVSDxRQOZ+z+y5nO2ts9jWXSTkMvayiWxCWOWtBQoYjLeccfXkiiPZWAHcV+WTPhkqJHQ==",
+			"dependencies": {
+				"@vue/compiler-dom": "3.2.47",
+				"@vue/compiler-sfc": "3.2.47",
+				"@vue/runtime-dom": "3.2.47",
+				"@vue/server-renderer": "3.2.47",
+				"@vue/shared": "3.2.47"
+			}
+		},
+		"node_modules/vue-clipboard3": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/vue-clipboard3/-/vue-clipboard3-2.0.0.tgz",
+			"integrity": "sha512-Q9S7dzWGax7LN5iiSPcu/K1GGm2gcBBlYwmMsUc5/16N6w90cbKow3FnPmPs95sungns4yvd9/+JhbAznECS2A==",
+			"dependencies": {
+				"clipboard": "^2.0.6"
+			}
+		},
+		"node_modules/vue-eslint-parser": {
+			"version": "9.1.0",
+			"resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.1.0.tgz",
+			"integrity": "sha512-NGn/iQy8/Wb7RrRa4aRkokyCZfOUWk19OP5HP6JEozQFX5AoS/t+Z0ZN7FY4LlmWc4FNI922V7cvX28zctN8dQ==",
+			"dev": true,
+			"dependencies": {
+				"debug": "^4.3.4",
+				"eslint-scope": "^7.1.1",
+				"eslint-visitor-keys": "^3.3.0",
+				"espree": "^9.3.1",
+				"esquery": "^1.4.0",
+				"lodash": "^4.17.21",
+				"semver": "^7.3.6"
+			},
+			"engines": {
+				"node": "^14.17.0 || >=16.0.0"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/mysticatea"
+			},
+			"peerDependencies": {
+				"eslint": ">=6.0.0"
+			}
+		},
+		"node_modules/vue-eslint-parser/node_modules/eslint-scope": {
+			"version": "7.1.1",
+			"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz",
+			"integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==",
+			"dev": true,
+			"dependencies": {
+				"esrecurse": "^4.3.0",
+				"estraverse": "^5.2.0"
+			},
+			"engines": {
+				"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+			}
+		},
+		"node_modules/vue-eslint-parser/node_modules/estraverse": {
+			"version": "5.3.0",
+			"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+			"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+			"dev": true,
+			"engines": {
+				"node": ">=4.0"
+			}
+		},
+		"node_modules/vue-grid-layout": {
+			"version": "3.0.0-beta1",
+			"resolved": "https://registry.npmjs.org/vue-grid-layout/-/vue-grid-layout-3.0.0-beta1.tgz",
+			"integrity": "sha512-MsW0yfYNtnAO/uDhfZvkP6effxSJxvhAFbIL37x6Rn3vW9xf0WHVefKaSbQMLpSq3mXnR6ut0pg2Cd5lqIIZzg==",
+			"dependencies": {
+				"@interactjs/actions": "^1.10.2",
+				"@interactjs/auto-start": "^1.10.2",
+				"@interactjs/dev-tools": "^1.10.2",
+				"@interactjs/interactjs": "^1.10.2",
+				"@interactjs/modifiers": "^1.10.2",
+				"element-resize-detector": "^1.2.1",
+				"mitt": "^2.1.0"
+			}
+		},
+		"node_modules/vue-grid-layout/node_modules/mitt": {
+			"version": "2.1.0",
+			"resolved": "https://registry.npmjs.org/mitt/-/mitt-2.1.0.tgz",
+			"integrity": "sha512-ILj2TpLiysu2wkBbWjAmww7TkZb65aiQO+DkVdUTBpBXq+MHYiETENkKFMtsJZX1Lf4pe4QOrTSjIfUwN5lRdg=="
+		},
+		"node_modules/vue-i18n": {
+			"version": "9.2.2",
+			"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.2.2.tgz",
+			"integrity": "sha512-yswpwtj89rTBhegUAv9Mu37LNznyu3NpyLQmozF3i1hYOhwpG8RjcjIFIIfnu+2MDZJGSZPXaKWvnQA71Yv9TQ==",
+			"dependencies": {
+				"@intlify/core-base": "9.2.2",
+				"@intlify/shared": "9.2.2",
+				"@intlify/vue-devtools": "9.2.2",
+				"@vue/devtools-api": "^6.2.1"
+			},
+			"engines": {
+				"node": ">= 14"
+			},
+			"peerDependencies": {
+				"vue": "^3.0.0"
+			}
+		},
+		"node_modules/vue-router": {
+			"version": "4.1.6",
+			"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.1.6.tgz",
+			"integrity": "sha512-DYWYwsG6xNPmLq/FmZn8Ip+qrhFEzA14EI12MsMgVxvHFDYvlr4NXpVF5hrRH1wVcDP8fGi5F4rxuJSl8/r+EQ==",
+			"dependencies": {
+				"@vue/devtools-api": "^6.4.5"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/posva"
+			},
+			"peerDependencies": {
+				"vue": "^3.2.0"
+			}
+		},
+		"node_modules/vuedraggable": {
+			"version": "4.1.0",
+			"resolved": "https://registry.npmmirror.com/vuedraggable/-/vuedraggable-4.1.0.tgz",
+			"integrity": "sha512-FU5HCWBmsf20GpP3eudURW3WdWTKIbEIQxh9/8GE806hydR9qZqRRxRE3RjqX7PkuLuMQG/A7n3cfj9rCEchww==",
+			"dependencies": {
+				"sortablejs": "1.14.0"
+			},
+			"peerDependencies": {
+				"vue": "^3.0.1"
+			}
+		},
+		"node_modules/vuedraggable/node_modules/sortablejs": {
+			"version": "1.14.0",
+			"resolved": "https://registry.npmmirror.com/sortablejs/-/sortablejs-1.14.0.tgz",
+			"integrity": "sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w=="
+		},
+		"node_modules/which": {
+			"version": "2.0.2",
+			"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+			"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+			"dev": true,
+			"dependencies": {
+				"isexe": "^2.0.0"
+			},
+			"bin": {
+				"node-which": "bin/node-which"
+			},
+			"engines": {
+				"node": ">= 8"
+			}
+		},
+		"node_modules/wildcard": {
+			"version": "1.1.2",
+			"resolved": "https://registry.npmjs.org/wildcard/-/wildcard-1.1.2.tgz",
+			"integrity": "sha512-DXukZJxpHA8LuotRwL0pP1+rS6CS7FF2qStDDE1C7DDg2rLud2PXRMuEDYIPhgEezwnlHNL4c+N6MfMTjCGTng=="
+		},
+		"node_modules/word-wrap": {
+			"version": "1.2.3",
+			"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
+			"integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
+			"dev": true,
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/wrappy": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+			"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+			"dev": true
+		},
+		"node_modules/xml-name-validator": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz",
+			"integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==",
+			"dev": true,
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/xtend": {
+			"version": "4.0.2",
+			"resolved": "https://registry.npmmirror.com/xtend/-/xtend-4.0.2.tgz",
+			"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
+			"dev": true,
+			"engines": {
+				"node": ">=0.4"
+			}
+		},
+		"node_modules/yallist": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+			"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+			"dev": true
+		},
+		"node_modules/yaml": {
+			"version": "1.10.2",
+			"resolved": "https://registry.npmmirror.com/yaml/-/yaml-1.10.2.tgz",
+			"integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
+			"dev": true,
+			"engines": {
+				"node": ">= 6"
+			}
+		},
+		"node_modules/yocto-queue": {
+			"version": "0.1.0",
+			"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+			"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+			"dev": true,
+			"engines": {
+				"node": ">=10"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/zrender": {
+			"version": "5.4.1",
+			"resolved": "https://registry.npmjs.org/zrender/-/zrender-5.4.1.tgz",
+			"integrity": "sha512-M4Z05BHWtajY2241EmMPHglDQAJ1UyHQcYsxDNzD9XLSkPDqMq4bB28v9Pb4mvHnVQ0GxyTklZ/69xCFP6RXBA==",
+			"dependencies": {
+				"tslib": "2.3.0"
+			}
+		}
+	},
+	"dependencies": {
+		"@babel/parser": {
+			"version": "7.21.1",
+			"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.1.tgz",
+			"integrity": "sha512-JzhBFpkuhBNYUY7qs+wTzNmyCWUHEaAFpQQD2YfU1rPL38/L43Wvid0fFkiOCnHvsGncRZgEPyGnltABLcVDTg=="
+		},
+		"@babel/runtime": {
+			"version": "7.21.0",
+			"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.0.tgz",
+			"integrity": "sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==",
+			"requires": {
+				"regenerator-runtime": "^0.13.11"
+			}
+		},
+		"@ctrl/tinycolor": {
+			"version": "3.6.0",
+			"resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.6.0.tgz",
+			"integrity": "sha512-/Z3l6pXthq0JvMYdUFyX9j0MaCltlIn6mfh9jLyQwg5aPKxkyNa0PTHtU1AlFXLNk55ZuAeJRcpvq+tmLfKmaQ=="
+		},
+		"@element-plus/icons-vue": {
+			"version": "2.0.10",
+			"resolved": "https://registry.npmjs.org/@element-plus/icons-vue/-/icons-vue-2.0.10.tgz",
+			"integrity": "sha512-ygEZ1mwPjcPo/OulhzLE7mtDrQBWI8vZzEWSNB2W/RNCRjoQGwbaK4N8lV4rid7Ts4qvySU3njMN7YCiSlSaTQ==",
+			"requires": {}
+		},
+		"@esbuild/android-arm": {
+			"version": "0.16.17",
+			"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.16.17.tgz",
+			"integrity": "sha512-N9x1CMXVhtWEAMS7pNNONyA14f71VPQN9Cnavj1XQh6T7bskqiLLrSca4O0Vr8Wdcga943eThxnVp3JLnBMYtw==",
+			"dev": true,
+			"optional": true
+		},
+		"@esbuild/android-arm64": {
+			"version": "0.16.17",
+			"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.16.17.tgz",
+			"integrity": "sha512-MIGl6p5sc3RDTLLkYL1MyL8BMRN4tLMRCn+yRJJmEDvYZ2M7tmAf80hx1kbNEUX2KJ50RRtxZ4JHLvCfuB6kBg==",
+			"dev": true,
+			"optional": true
+		},
+		"@esbuild/android-x64": {
+			"version": "0.16.17",
+			"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.16.17.tgz",
+			"integrity": "sha512-a3kTv3m0Ghh4z1DaFEuEDfz3OLONKuFvI4Xqczqx4BqLyuFaFkuaG4j2MtA6fuWEFeC5x9IvqnX7drmRq/fyAQ==",
+			"dev": true,
+			"optional": true
+		},
+		"@esbuild/darwin-arm64": {
+			"version": "0.16.17",
+			"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.16.17.tgz",
+			"integrity": "sha512-/2agbUEfmxWHi9ARTX6OQ/KgXnOWfsNlTeLcoV7HSuSTv63E4DqtAc+2XqGw1KHxKMHGZgbVCZge7HXWX9Vn+w==",
+			"dev": true,
+			"optional": true
+		},
+		"@esbuild/darwin-x64": {
+			"version": "0.16.17",
+			"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.16.17.tgz",
+			"integrity": "sha512-2By45OBHulkd9Svy5IOCZt376Aa2oOkiE9QWUK9fe6Tb+WDr8hXL3dpqi+DeLiMed8tVXspzsTAvd0jUl96wmg==",
+			"dev": true,
+			"optional": true
+		},
+		"@esbuild/freebsd-arm64": {
+			"version": "0.16.17",
+			"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.16.17.tgz",
+			"integrity": "sha512-mt+cxZe1tVx489VTb4mBAOo2aKSnJ33L9fr25JXpqQqzbUIw/yzIzi+NHwAXK2qYV1lEFp4OoVeThGjUbmWmdw==",
+			"dev": true,
+			"optional": true
+		},
+		"@esbuild/freebsd-x64": {
+			"version": "0.16.17",
+			"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.16.17.tgz",
+			"integrity": "sha512-8ScTdNJl5idAKjH8zGAsN7RuWcyHG3BAvMNpKOBaqqR7EbUhhVHOqXRdL7oZvz8WNHL2pr5+eIT5c65kA6NHug==",
+			"dev": true,
+			"optional": true
+		},
+		"@esbuild/linux-arm": {
+			"version": "0.16.17",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.16.17.tgz",
+			"integrity": "sha512-iihzrWbD4gIT7j3caMzKb/RsFFHCwqqbrbH9SqUSRrdXkXaygSZCZg1FybsZz57Ju7N/SHEgPyaR0LZ8Zbe9gQ==",
+			"dev": true,
+			"optional": true
+		},
+		"@esbuild/linux-arm64": {
+			"version": "0.16.17",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.16.17.tgz",
+			"integrity": "sha512-7S8gJnSlqKGVJunnMCrXHU9Q8Q/tQIxk/xL8BqAP64wchPCTzuM6W3Ra8cIa1HIflAvDnNOt2jaL17vaW+1V0g==",
+			"dev": true,
+			"optional": true
+		},
+		"@esbuild/linux-ia32": {
+			"version": "0.16.17",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.16.17.tgz",
+			"integrity": "sha512-kiX69+wcPAdgl3Lonh1VI7MBr16nktEvOfViszBSxygRQqSpzv7BffMKRPMFwzeJGPxcio0pdD3kYQGpqQ2SSg==",
+			"dev": true,
+			"optional": true
+		},
+		"@esbuild/linux-loong64": {
+			"version": "0.16.17",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.16.17.tgz",
+			"integrity": "sha512-dTzNnQwembNDhd654cA4QhbS9uDdXC3TKqMJjgOWsC0yNCbpzfWoXdZvp0mY7HU6nzk5E0zpRGGx3qoQg8T2DQ==",
+			"dev": true,
+			"optional": true
+		},
+		"@esbuild/linux-mips64el": {
+			"version": "0.16.17",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.16.17.tgz",
+			"integrity": "sha512-ezbDkp2nDl0PfIUn0CsQ30kxfcLTlcx4Foz2kYv8qdC6ia2oX5Q3E/8m6lq84Dj/6b0FrkgD582fJMIfHhJfSw==",
+			"dev": true,
+			"optional": true
+		},
+		"@esbuild/linux-ppc64": {
+			"version": "0.16.17",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.16.17.tgz",
+			"integrity": "sha512-dzS678gYD1lJsW73zrFhDApLVdM3cUF2MvAa1D8K8KtcSKdLBPP4zZSLy6LFZ0jYqQdQ29bjAHJDgz0rVbLB3g==",
+			"dev": true,
+			"optional": true
+		},
+		"@esbuild/linux-riscv64": {
+			"version": "0.16.17",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.16.17.tgz",
+			"integrity": "sha512-ylNlVsxuFjZK8DQtNUwiMskh6nT0vI7kYl/4fZgV1llP5d6+HIeL/vmmm3jpuoo8+NuXjQVZxmKuhDApK0/cKw==",
+			"dev": true,
+			"optional": true
+		},
+		"@esbuild/linux-s390x": {
+			"version": "0.16.17",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.16.17.tgz",
+			"integrity": "sha512-gzy7nUTO4UA4oZ2wAMXPNBGTzZFP7mss3aKR2hH+/4UUkCOyqmjXiKpzGrY2TlEUhbbejzXVKKGazYcQTZWA/w==",
+			"dev": true,
+			"optional": true
+		},
+		"@esbuild/linux-x64": {
+			"version": "0.16.17",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.16.17.tgz",
+			"integrity": "sha512-mdPjPxfnmoqhgpiEArqi4egmBAMYvaObgn4poorpUaqmvzzbvqbowRllQ+ZgzGVMGKaPkqUmPDOOFQRUFDmeUw==",
+			"dev": true,
+			"optional": true
+		},
+		"@esbuild/netbsd-x64": {
+			"version": "0.16.17",
+			"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.16.17.tgz",
+			"integrity": "sha512-/PzmzD/zyAeTUsduZa32bn0ORug+Jd1EGGAUJvqfeixoEISYpGnAezN6lnJoskauoai0Jrs+XSyvDhppCPoKOA==",
+			"dev": true,
+			"optional": true
+		},
+		"@esbuild/openbsd-x64": {
+			"version": "0.16.17",
+			"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.16.17.tgz",
+			"integrity": "sha512-2yaWJhvxGEz2RiftSk0UObqJa/b+rIAjnODJgv2GbGGpRwAfpgzyrg1WLK8rqA24mfZa9GvpjLcBBg8JHkoodg==",
+			"dev": true,
+			"optional": true
+		},
+		"@esbuild/sunos-x64": {
+			"version": "0.16.17",
+			"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.16.17.tgz",
+			"integrity": "sha512-xtVUiev38tN0R3g8VhRfN7Zl42YCJvyBhRKw1RJjwE1d2emWTVToPLNEQj/5Qxc6lVFATDiy6LjVHYhIPrLxzw==",
+			"dev": true,
+			"optional": true
+		},
+		"@esbuild/win32-arm64": {
+			"version": "0.16.17",
+			"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.16.17.tgz",
+			"integrity": "sha512-ga8+JqBDHY4b6fQAmOgtJJue36scANy4l/rL97W+0wYmijhxKetzZdKOJI7olaBaMhWt8Pac2McJdZLxXWUEQw==",
+			"dev": true,
+			"optional": true
+		},
+		"@esbuild/win32-ia32": {
+			"version": "0.16.17",
+			"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.16.17.tgz",
+			"integrity": "sha512-WnsKaf46uSSF/sZhwnqE4L/F89AYNMiD4YtEcYekBt9Q7nj0DiId2XH2Ng2PHM54qi5oPrQ8luuzGszqi/veig==",
+			"dev": true,
+			"optional": true
+		},
+		"@esbuild/win32-x64": {
+			"version": "0.16.17",
+			"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.16.17.tgz",
+			"integrity": "sha512-y+EHuSchhL7FjHgvQL/0fnnFmO4T1bhvWANX6gcnqTjtnKWbTvUMCpGnv2+t+31d7RzyEAYAd4u2fnIhHL6N/Q==",
+			"dev": true,
+			"optional": true
+		},
+		"@eslint/eslintrc": {
+			"version": "1.4.1",
+			"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz",
+			"integrity": "sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==",
+			"dev": true,
+			"requires": {
+				"ajv": "^6.12.4",
+				"debug": "^4.3.2",
+				"espree": "^9.4.0",
+				"globals": "^13.19.0",
+				"ignore": "^5.2.0",
+				"import-fresh": "^3.2.1",
+				"js-yaml": "^4.1.0",
+				"minimatch": "^3.1.2",
+				"strip-json-comments": "^3.1.1"
+			}
+		},
+		"@floating-ui/core": {
+			"version": "1.2.1",
+			"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.2.1.tgz",
+			"integrity": "sha512-LSqwPZkK3rYfD7GKoIeExXOyYx6Q1O4iqZWwIehDNuv3Dv425FIAE8PRwtAx1imEolFTHgBEcoFHm9MDnYgPCg=="
+		},
+		"@floating-ui/dom": {
+			"version": "1.2.1",
+			"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.2.1.tgz",
+			"integrity": "sha512-Rt45SmRiV8eU+xXSB9t0uMYiQ/ZWGE/jumse2o3i5RGlyvcbqOF4q+1qBnzLE2kZ5JGhq0iMkcGXUKbFe7MpTA==",
+			"requires": {
+				"@floating-ui/core": "^1.2.1"
+			}
+		},
+		"@form-create/component-elm-checkbox": {
+			"version": "3.1.15",
+			"resolved": "https://registry.npmmirror.com/@form-create/component-elm-checkbox/-/component-elm-checkbox-3.1.15.tgz",
+			"integrity": "sha512-bAl3k0p76wwMX0OxeR8KAEiUl2RP1Jl1kAqbjD762EcSXGgaXgP94v9ag1JhUoiwZkSKWl6IF+e/utjEonRSFw==",
+			"requires": {
+				"@form-create/utils": "^3.1.15"
+			}
+		},
+		"@form-create/component-elm-frame": {
+			"version": "3.1.15",
+			"resolved": "https://registry.npmmirror.com/@form-create/component-elm-frame/-/component-elm-frame-3.1.15.tgz",
+			"integrity": "sha512-78WbMpMLTYwTo3QP7Fa+N2VF/u1vmRuwDmiobtBtVg9EE7m6//bQV96ibnLNXX27MD41gIg+o1GUJtx/qxZ8Ew==",
+			"requires": {
+				"@form-create/utils": "^3.1.15"
+			}
+		},
+		"@form-create/component-elm-group": {
+			"version": "3.1.15",
+			"resolved": "https://registry.npmmirror.com/@form-create/component-elm-group/-/component-elm-group-3.1.15.tgz",
+			"integrity": "sha512-V/oVxHf9rHqqMHRV0XRVED9EYFCVZ8Tv/EbbIN/4rORruHtzdQOH8I0QHQ9T4vZO9Q4eblApzcJb8Y7bbHX23Q==",
+			"requires": {
+				"@form-create/utils": "^3.1.15"
+			}
+		},
+		"@form-create/component-elm-radio": {
+			"version": "3.1.15",
+			"resolved": "https://registry.npmmirror.com/@form-create/component-elm-radio/-/component-elm-radio-3.1.15.tgz",
+			"integrity": "sha512-qAf1VlhrUvMlgzkz6BYLRn0UOLahkTI/cbzt8nkp5PL1oGoSXp5xqIOtHMjbnGEkEXW57kjRKI5Q7UjzIhYmvQ==",
+			"requires": {
+				"@form-create/utils": "^3.1.15"
+			}
+		},
+		"@form-create/component-elm-select": {
+			"version": "3.1.15",
+			"resolved": "https://registry.npmmirror.com/@form-create/component-elm-select/-/component-elm-select-3.1.15.tgz",
+			"integrity": "sha512-ZnvPn/TGqgFDed7bKabjRvlwlQ8RYq5WCG9Iy63d2igC8577tv5QTI2rNvfGfxxH/254MPwgNlA94JukxzEOTw==",
+			"requires": {
+				"@form-create/utils": "^3.1.15"
+			}
+		},
+		"@form-create/component-elm-tree": {
+			"version": "3.1.15",
+			"resolved": "https://registry.npmmirror.com/@form-create/component-elm-tree/-/component-elm-tree-3.1.15.tgz",
+			"integrity": "sha512-4VPN406A8Mvannn8P/2DvStqDYFfDHZ+ILG/0JndOyfe+GdYdSA5SwD3LfC4zD6AhpGrkOJXbd4YYXlBUtzt8g==",
+			"requires": {
+				"@form-create/utils": "^3.1.15"
+			}
+		},
+		"@form-create/component-elm-upload": {
+			"version": "3.1.18",
+			"resolved": "https://registry.npmmirror.com/@form-create/component-elm-upload/-/component-elm-upload-3.1.18.tgz",
+			"integrity": "sha512-+jHZ1vfNusEqKSvpLpFcKbF0lE7S+YuWtAturzW2O5K+qYDw6SmGyB3/XeYqVXN9rfne0rqMbyYw6CGglVe7nw==",
+			"requires": {
+				"@form-create/utils": "^3.1.15"
+			}
+		},
+		"@form-create/component-subform": {
+			"version": "3.1.5",
+			"resolved": "https://registry.npmmirror.com/@form-create/component-subform/-/component-subform-3.1.5.tgz",
+			"integrity": "sha512-JHNEFGuwpnjGvCJ0I0GCqPL5al0qXoN4ymnRBpm+oL+6MMo5bz1kUyoqMX1MutuC96gHTqpeqc67hssi8g2mIw=="
+		},
+		"@form-create/core": {
+			"version": "3.1.18",
+			"resolved": "https://registry.npmmirror.com/@form-create/core/-/core-3.1.18.tgz",
+			"integrity": "sha512-kFu+fH88KvdkJoMPuLaBOG/4jNv+YPJbJHzvF4HZLn9XEtJgO9flef5WcvMyz12moI6uNGJ4Hs8+sdkXBJtkMw==",
+			"requires": {
+				"@form-create/utils": "^3.1.15"
+			}
+		},
+		"@form-create/element-ui": {
+			"version": "3.1.18",
+			"resolved": "https://registry.npmmirror.com/@form-create/element-ui/-/element-ui-3.1.18.tgz",
+			"integrity": "sha512-FstZrrfUBm9cRipqi6BW3cTPShQsgDWaHXYA5Uj/3fQ5y5EuoSRkNnlgqEoAjwUsQznL4tMC4BzqxoAHsP9zlg==",
+			"requires": {
+				"@form-create/component-elm-checkbox": "^3.1.15",
+				"@form-create/component-elm-frame": "^3.1.15",
+				"@form-create/component-elm-group": "^3.1.15",
+				"@form-create/component-elm-radio": "^3.1.15",
+				"@form-create/component-elm-select": "^3.1.15",
+				"@form-create/component-elm-tree": "^3.1.15",
+				"@form-create/component-elm-upload": "^3.1.18",
+				"@form-create/component-subform": "^3.1.5",
+				"@form-create/core": "^3.1.18",
+				"@form-create/utils": "^3.1.15"
+			}
+		},
+		"@form-create/utils": {
+			"version": "3.1.15",
+			"resolved": "https://registry.npmmirror.com/@form-create/utils/-/utils-3.1.15.tgz",
+			"integrity": "sha512-tP6Z/c2XC6OYrI8D9/XWvJc2h6apsyMFTy051sY+tCcxppqyR7dBEEXmgfWOrAr980N7k10g27kwJ9TdVn+bfw=="
+		},
+		"@humanwhocodes/config-array": {
+			"version": "0.11.8",
+			"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz",
+			"integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==",
+			"dev": true,
+			"requires": {
+				"@humanwhocodes/object-schema": "^1.2.1",
+				"debug": "^4.1.1",
+				"minimatch": "^3.0.5"
+			}
+		},
+		"@humanwhocodes/module-importer": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+			"integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+			"dev": true
+		},
+		"@humanwhocodes/object-schema": {
+			"version": "1.2.1",
+			"resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz",
+			"integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
+			"dev": true
+		},
+		"@iconify/types": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmmirror.com/@iconify/types/-/types-2.0.0.tgz",
+			"integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==",
+			"dev": true
+		},
+		"@iconify/vue": {
+			"version": "4.1.0",
+			"resolved": "https://registry.npmmirror.com/@iconify/vue/-/vue-4.1.0.tgz",
+			"integrity": "sha512-rBQVxNoSDooqgWkQg2MqkIHkH/huNuvXGqui5wijc1zLnU7TKzbBHW9VGmbnV4asNTmIHmqV4Nvt0M2rZ/9nHA==",
+			"dev": true,
+			"requires": {
+				"@iconify/types": "^2.0.0"
+			}
+		},
+		"@interactjs/actions": {
+			"version": "1.10.17",
+			"resolved": "https://registry.npmjs.org/@interactjs/actions/-/actions-1.10.17.tgz",
+			"integrity": "sha512-wyB1ZqpaZy5gmz6VDqK9KWh98xKnFgL7VyLvxHODFi9V0IYX4HJAAOBlhtfze0D1R1f1cY+gqPDK+dLaHMlE+w==",
+			"requires": {
+				"@interactjs/interact": "1.10.17"
+			}
+		},
+		"@interactjs/auto-scroll": {
+			"version": "1.10.17",
+			"resolved": "https://registry.npmjs.org/@interactjs/auto-scroll/-/auto-scroll-1.10.17.tgz",
+			"integrity": "sha512-IQcW7N3xOaoL8RnAGOGMk0Y2gue7L4S3BT6Id4VBBu8so163DtLiZVW6jXu9rKVntzbluaAeqNZlfAVyu3kIWg==",
+			"requires": {
+				"@interactjs/interact": "1.10.17"
+			}
+		},
+		"@interactjs/auto-start": {
+			"version": "1.10.17",
+			"resolved": "https://registry.npmjs.org/@interactjs/auto-start/-/auto-start-1.10.17.tgz",
+			"integrity": "sha512-qYVxhAbYnwxjD/NLEegUoAST7WASJ4VmWNjsyWRx/js5Op+I4E2zteARIeZGgrutcGIXMCcQzhCMgE3PjOpbpw==",
+			"requires": {
+				"@interactjs/interact": "1.10.17"
+			}
+		},
+		"@interactjs/core": {
+			"version": "1.10.17",
+			"resolved": "https://registry.npmjs.org/@interactjs/core/-/core-1.10.17.tgz",
+			"integrity": "sha512-rL9w+83HDRuXub8Ezqs+97CYLl/ne7bLT/sAeduUWaxYhsW9iOqBoob9JnkkCZOaOsYizWI1EWy0+fNc5ibtLQ==",
+			"requires": {}
+		},
+		"@interactjs/dev-tools": {
+			"version": "1.10.17",
+			"resolved": "https://registry.npmjs.org/@interactjs/dev-tools/-/dev-tools-1.10.17.tgz",
+			"integrity": "sha512-Oi9nEw3FfSwkNmW+V0WwdHqvzEkVHc24mH1v5EjRn60sqgrGLK9nTQ+NSuqcnUY8GxC3TkyuxnsOodxiadIRmA==",
+			"requires": {
+				"@interactjs/interact": "1.10.17"
+			}
+		},
+		"@interactjs/inertia": {
+			"version": "1.10.17",
+			"resolved": "https://registry.npmjs.org/@interactjs/inertia/-/inertia-1.10.17.tgz",
+			"integrity": "sha512-41vbYUjZIDCKt2/yhmjPrEW5+0uoL/hldFsll9pkvnLhmm12Xk0VXOlmR2zXKAmsTK3fJlKMyBYUX92qHLkyVQ==",
+			"requires": {
+				"@interactjs/interact": "1.10.17",
+				"@interactjs/offset": "1.10.17"
+			}
+		},
+		"@interactjs/interact": {
+			"version": "1.10.17",
+			"resolved": "https://registry.npmjs.org/@interactjs/interact/-/interact-1.10.17.tgz",
+			"integrity": "sha512-NyKsf8EFudvdahBjPz1Gt5QnynVwa/2LUfBc2/w8QOnOBiyzUm0HLloJSaB8a50QbQkSWN243/Lgpd8GTMQvuQ==",
+			"requires": {
+				"@interactjs/core": "1.10.17",
+				"@interactjs/types": "1.10.17",
+				"@interactjs/utils": "1.10.17"
+			}
+		},
+		"@interactjs/interactjs": {
+			"version": "1.10.17",
+			"resolved": "https://registry.npmjs.org/@interactjs/interactjs/-/interactjs-1.10.17.tgz",
+			"integrity": "sha512-hHmiukARbZhiM12zNKx0yQlFVl4C+NMeYNAYh6Mf9U3ZziQ47C+JEW8Gr7Zr/MxfNZyPu5nLKCpVQjh/JvBO9g==",
+			"requires": {
+				"@interactjs/actions": "1.10.17",
+				"@interactjs/auto-scroll": "1.10.17",
+				"@interactjs/auto-start": "1.10.17",
+				"@interactjs/core": "1.10.17",
+				"@interactjs/dev-tools": "1.10.17",
+				"@interactjs/inertia": "1.10.17",
+				"@interactjs/interact": "1.10.17",
+				"@interactjs/modifiers": "1.10.17",
+				"@interactjs/offset": "1.10.17",
+				"@interactjs/pointer-events": "1.10.17",
+				"@interactjs/reflow": "1.10.17",
+				"@interactjs/utils": "1.10.17"
+			}
+		},
+		"@interactjs/modifiers": {
+			"version": "1.10.17",
+			"resolved": "https://registry.npmjs.org/@interactjs/modifiers/-/modifiers-1.10.17.tgz",
+			"integrity": "sha512-Dxw8kv9VBIxnhNvQncR6CKAGMzKXczLvuAUIdSPFYtyerX/XiDulJUqhR+jVKNp/WjF1DvdBxWo0kGGLbM84LQ==",
+			"requires": {
+				"@interactjs/interact": "1.10.17",
+				"@interactjs/snappers": "1.10.17"
+			}
+		},
+		"@interactjs/offset": {
+			"version": "1.10.17",
+			"resolved": "https://registry.npmjs.org/@interactjs/offset/-/offset-1.10.17.tgz",
+			"integrity": "sha512-wWBnIQWgLrmJNTBbd/FdxHxAJjiXl/5ND8Jbw2DuP9gIGDxhFSdEt62Fgqimn9ICb8v8ycvSLObEmcvJF/8hQQ==",
+			"requires": {
+				"@interactjs/interact": "1.10.17"
+			}
+		},
+		"@interactjs/pointer-events": {
+			"version": "1.10.17",
+			"resolved": "https://registry.npmjs.org/@interactjs/pointer-events/-/pointer-events-1.10.17.tgz",
+			"integrity": "sha512-VsfluouEKb8QRGyH6jQATCW+QdAd/3dkENS7rj2m+EcVUhz2Ob5mpMRopjALi4pwltMowqTfuJ4LtwMSX2G29A==",
+			"requires": {
+				"@interactjs/interact": "1.10.17"
+			}
+		},
+		"@interactjs/reflow": {
+			"version": "1.10.17",
+			"resolved": "https://registry.npmjs.org/@interactjs/reflow/-/reflow-1.10.17.tgz",
+			"integrity": "sha512-ncpWP5k93FRQptEhjzPZsbuRRajd4rkW17lDavCrEjrDi/LHnYekWGqZTaFzfJ80n1x8xUm9ujDjxCTylNqEIA==",
+			"requires": {
+				"@interactjs/interact": "1.10.17"
+			}
+		},
+		"@interactjs/snappers": {
+			"version": "1.10.17",
+			"resolved": "https://registry.npmjs.org/@interactjs/snappers/-/snappers-1.10.17.tgz",
+			"integrity": "sha512-m753DGsNOts797e3zDT6wqELoc+BlpIC1w+TyMyISRxU6n1RlS8Q6LHBGgwAgV79LHLaahv/a5haFF9H1VG0FQ==",
+			"requires": {
+				"@interactjs/interact": "1.10.17"
+			}
+		},
+		"@interactjs/types": {
+			"version": "1.10.17",
+			"resolved": "https://registry.npmjs.org/@interactjs/types/-/types-1.10.17.tgz",
+			"integrity": "sha512-X2JpoM7xUw0p9Me0tMaI0HNfcF/Hd07ZZlzpnpEMpGerUZOLoyeThrV9P+CrBHxZrluWJrigJbcdqXliFd0YMA=="
+		},
+		"@interactjs/utils": {
+			"version": "1.10.17",
+			"resolved": "https://registry.npmjs.org/@interactjs/utils/-/utils-1.10.17.tgz",
+			"integrity": "sha512-sZAW08CkqgvqRjUIaLRjScjObcCzN9D75yekLA21EClYAZIhi4A+GEt2z/WqOCOksTaEPLYmQyhkpXcboc0LhQ=="
+		},
+		"@intlify/core-base": {
+			"version": "9.2.2",
+			"resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.2.2.tgz",
+			"integrity": "sha512-JjUpQtNfn+joMbrXvpR4hTF8iJQ2sEFzzK3KIESOx+f+uwIjgw20igOyaIdhfsVVBCds8ZM64MoeNSx+PHQMkA==",
+			"requires": {
+				"@intlify/devtools-if": "9.2.2",
+				"@intlify/message-compiler": "9.2.2",
+				"@intlify/shared": "9.2.2",
+				"@intlify/vue-devtools": "9.2.2"
+			}
+		},
+		"@intlify/devtools-if": {
+			"version": "9.2.2",
+			"resolved": "https://registry.npmjs.org/@intlify/devtools-if/-/devtools-if-9.2.2.tgz",
+			"integrity": "sha512-4ttr/FNO29w+kBbU7HZ/U0Lzuh2cRDhP8UlWOtV9ERcjHzuyXVZmjyleESK6eVP60tGC9QtQW9yZE+JeRhDHkg==",
+			"requires": {
+				"@intlify/shared": "9.2.2"
+			}
+		},
+		"@intlify/message-compiler": {
+			"version": "9.2.2",
+			"resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.2.2.tgz",
+			"integrity": "sha512-IUrQW7byAKN2fMBe8z6sK6riG1pue95e5jfokn8hA5Q3Bqy4MBJ5lJAofUsawQJYHeoPJ7svMDyBaVJ4d0GTtA==",
+			"requires": {
+				"@intlify/shared": "9.2.2",
+				"source-map": "0.6.1"
+			}
+		},
+		"@intlify/shared": {
+			"version": "9.2.2",
+			"resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.2.2.tgz",
+			"integrity": "sha512-wRwTpsslgZS5HNyM7uDQYZtxnbI12aGiBZURX3BTR9RFIKKRWpllTsgzHWvj3HKm3Y2Sh5LPC1r0PDCKEhVn9Q=="
+		},
+		"@intlify/vue-devtools": {
+			"version": "9.2.2",
+			"resolved": "https://registry.npmjs.org/@intlify/vue-devtools/-/vue-devtools-9.2.2.tgz",
+			"integrity": "sha512-+dUyqyCHWHb/UcvY1MlIpO87munedm3Gn6E9WWYdWrMuYLcoIoOEVDWSS8xSwtlPU+kA+MEQTP6Q1iI/ocusJg==",
+			"requires": {
+				"@intlify/core-base": "9.2.2",
+				"@intlify/shared": "9.2.2"
+			}
+		},
+		"@nodelib/fs.scandir": {
+			"version": "2.1.5",
+			"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+			"integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+			"dev": true,
+			"requires": {
+				"@nodelib/fs.stat": "2.0.5",
+				"run-parallel": "^1.1.9"
+			}
+		},
+		"@nodelib/fs.stat": {
+			"version": "2.0.5",
+			"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+			"integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+			"dev": true
+		},
+		"@nodelib/fs.walk": {
+			"version": "1.2.8",
+			"resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+			"integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+			"dev": true,
+			"requires": {
+				"@nodelib/fs.scandir": "2.1.5",
+				"fastq": "^1.6.0"
+			}
+		},
+		"@popperjs/core": {
+			"version": "npm:@sxzz/popperjs-es@2.11.7",
+			"resolved": "https://registry.npmjs.org/@sxzz/popperjs-es/-/popperjs-es-2.11.7.tgz",
+			"integrity": "sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ=="
+		},
+		"@transloadit/prettier-bytes": {
+			"version": "0.0.7",
+			"resolved": "https://registry.npmjs.org/@transloadit/prettier-bytes/-/prettier-bytes-0.0.7.tgz",
+			"integrity": "sha512-VeJbUb0wEKbcwaSlj5n+LscBl9IPgLPkHVGBkh00cztv6X4L/TJXK58LzFuBKX7/GAfiGhIwH67YTLTlzvIzBA=="
+		},
+		"@types/event-emitter": {
+			"version": "0.3.3",
+			"resolved": "https://registry.npmjs.org/@types/event-emitter/-/event-emitter-0.3.3.tgz",
+			"integrity": "sha512-UfnOK1pIxO7P+EgPRZXD9jMpimd8QEFcEZ5R67R1UhGbv4zghU5+NE7U8M8G9H5Jc8FI51rqDWQs6FtUfq2e/Q=="
+		},
+		"@types/json-schema": {
+			"version": "7.0.11",
+			"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
+			"integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==",
+			"dev": true
+		},
+		"@types/lodash": {
+			"version": "4.14.191",
+			"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.191.tgz",
+			"integrity": "sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ=="
+		},
+		"@types/lodash-es": {
+			"version": "4.17.6",
+			"resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.6.tgz",
+			"integrity": "sha512-R+zTeVUKDdfoRxpAryaQNRKk3105Rrgx2CFRClIgRGaqDTdjsm8h6IYA8ir584W3ePzkZfst5xIgDwYrlh9HLg==",
+			"requires": {
+				"@types/lodash": "*"
+			}
+		},
+		"@types/node": {
+			"version": "18.14.0",
+			"resolved": "https://registry.npmjs.org/@types/node/-/node-18.14.0.tgz",
+			"integrity": "sha512-5EWrvLmglK+imbCJY0+INViFWUHg1AHel1sq4ZVSfdcNqGy9Edv3UB9IIzzg+xPaUcAgZYcfVs2fBcwDeZzU0A==",
+			"dev": true
+		},
+		"@types/nprogress": {
+			"version": "0.2.0",
+			"resolved": "https://registry.npmjs.org/@types/nprogress/-/nprogress-0.2.0.tgz",
+			"integrity": "sha512-1cYJrqq9GezNFPsWTZpFut/d4CjpZqA0vhqDUPFWYKF1oIyBz5qnoYMzR+0C/T96t3ebLAC1SSnwrVOm5/j74A==",
+			"dev": true
+		},
+		"@types/semver": {
+			"version": "7.3.13",
+			"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz",
+			"integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==",
+			"dev": true
+		},
+		"@types/sortablejs": {
+			"version": "1.15.0",
+			"resolved": "https://registry.npmjs.org/@types/sortablejs/-/sortablejs-1.15.0.tgz",
+			"integrity": "sha512-qrhtM7M41EhH4tZQTNw2/RJkxllBx3reiJpTbgWCM2Dx0U1sZ6LwKp9lfNln9uqE26ZMKUaPEYaD4rzvOWYtZw==",
+			"dev": true
+		},
+		"@types/web-bluetooth": {
+			"version": "0.0.16",
+			"resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.16.tgz",
+			"integrity": "sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ=="
+		},
+		"@typescript-eslint/eslint-plugin": {
+			"version": "5.53.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.53.0.tgz",
+			"integrity": "sha512-alFpFWNucPLdUOySmXCJpzr6HKC3bu7XooShWM+3w/EL6J2HIoB2PFxpLnq4JauWVk6DiVeNKzQlFEaE+X9sGw==",
+			"dev": true,
+			"requires": {
+				"@typescript-eslint/scope-manager": "5.53.0",
+				"@typescript-eslint/type-utils": "5.53.0",
+				"@typescript-eslint/utils": "5.53.0",
+				"debug": "^4.3.4",
+				"grapheme-splitter": "^1.0.4",
+				"ignore": "^5.2.0",
+				"natural-compare-lite": "^1.4.0",
+				"regexpp": "^3.2.0",
+				"semver": "^7.3.7",
+				"tsutils": "^3.21.0"
+			}
+		},
+		"@typescript-eslint/parser": {
+			"version": "5.53.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.53.0.tgz",
+			"integrity": "sha512-MKBw9i0DLYlmdOb3Oq/526+al20AJZpANdT6Ct9ffxcV8nKCHz63t/S0IhlTFNsBIHJv+GY5SFJ0XfqVeydQrQ==",
+			"dev": true,
+			"requires": {
+				"@typescript-eslint/scope-manager": "5.53.0",
+				"@typescript-eslint/types": "5.53.0",
+				"@typescript-eslint/typescript-estree": "5.53.0",
+				"debug": "^4.3.4"
+			}
+		},
+		"@typescript-eslint/scope-manager": {
+			"version": "5.53.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.53.0.tgz",
+			"integrity": "sha512-Opy3dqNsp/9kBBeCPhkCNR7fmdSQqA+47r21hr9a14Bx0xnkElEQmhoHga+VoaoQ6uDHjDKmQPIYcUcKJifS7w==",
+			"dev": true,
+			"requires": {
+				"@typescript-eslint/types": "5.53.0",
+				"@typescript-eslint/visitor-keys": "5.53.0"
+			}
+		},
+		"@typescript-eslint/type-utils": {
+			"version": "5.53.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.53.0.tgz",
+			"integrity": "sha512-HO2hh0fmtqNLzTAme/KnND5uFNwbsdYhCZghK2SoxGp3Ifn2emv+hi0PBUjzzSh0dstUIFqOj3bp0AwQlK4OWw==",
+			"dev": true,
+			"requires": {
+				"@typescript-eslint/typescript-estree": "5.53.0",
+				"@typescript-eslint/utils": "5.53.0",
+				"debug": "^4.3.4",
+				"tsutils": "^3.21.0"
+			}
+		},
+		"@typescript-eslint/types": {
+			"version": "5.53.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.53.0.tgz",
+			"integrity": "sha512-5kcDL9ZUIP756K6+QOAfPkigJmCPHcLN7Zjdz76lQWWDdzfOhZDTj1irs6gPBKiXx5/6O3L0+AvupAut3z7D2A==",
+			"dev": true
+		},
+		"@typescript-eslint/typescript-estree": {
+			"version": "5.53.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.53.0.tgz",
+			"integrity": "sha512-eKmipH7QyScpHSkhbptBBYh9v8FxtngLquq292YTEQ1pxVs39yFBlLC1xeIZcPPz1RWGqb7YgERJRGkjw8ZV7w==",
+			"dev": true,
+			"requires": {
+				"@typescript-eslint/types": "5.53.0",
+				"@typescript-eslint/visitor-keys": "5.53.0",
+				"debug": "^4.3.4",
+				"globby": "^11.1.0",
+				"is-glob": "^4.0.3",
+				"semver": "^7.3.7",
+				"tsutils": "^3.21.0"
+			}
+		},
+		"@typescript-eslint/utils": {
+			"version": "5.53.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.53.0.tgz",
+			"integrity": "sha512-VUOOtPv27UNWLxFwQK/8+7kvxVC+hPHNsJjzlJyotlaHjLSIgOCKj9I0DBUjwOOA64qjBwx5afAPjksqOxMO0g==",
+			"dev": true,
+			"requires": {
+				"@types/json-schema": "^7.0.9",
+				"@types/semver": "^7.3.12",
+				"@typescript-eslint/scope-manager": "5.53.0",
+				"@typescript-eslint/types": "5.53.0",
+				"@typescript-eslint/typescript-estree": "5.53.0",
+				"eslint-scope": "^5.1.1",
+				"eslint-utils": "^3.0.0",
+				"semver": "^7.3.7"
+			}
+		},
+		"@typescript-eslint/visitor-keys": {
+			"version": "5.53.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.53.0.tgz",
+			"integrity": "sha512-JqNLnX3leaHFZEN0gCh81sIvgrp/2GOACZNgO4+Tkf64u51kTpAyWFOY8XHx8XuXr3N2C9zgPPHtcpMg6z1g0w==",
+			"dev": true,
+			"requires": {
+				"@typescript-eslint/types": "5.53.0",
+				"eslint-visitor-keys": "^3.3.0"
+			}
+		},
+		"@uppy/companion-client": {
+			"version": "2.2.2",
+			"resolved": "https://registry.npmjs.org/@uppy/companion-client/-/companion-client-2.2.2.tgz",
+			"integrity": "sha512-5mTp2iq97/mYSisMaBtFRry6PTgZA6SIL7LePteOV5x0/DxKfrZW3DEiQERJmYpHzy7k8johpm2gHnEKto56Og==",
+			"requires": {
+				"@uppy/utils": "^4.1.2",
+				"namespace-emitter": "^2.0.1"
+			}
+		},
+		"@uppy/core": {
+			"version": "2.3.4",
+			"resolved": "https://registry.npmjs.org/@uppy/core/-/core-2.3.4.tgz",
+			"integrity": "sha512-iWAqppC8FD8mMVqewavCz+TNaet6HPXitmGXpGGREGrakZ4FeuWytVdrelydzTdXx6vVKkOmI2FLztGg73sENQ==",
+			"requires": {
+				"@transloadit/prettier-bytes": "0.0.7",
+				"@uppy/store-default": "^2.1.1",
+				"@uppy/utils": "^4.1.3",
+				"lodash.throttle": "^4.1.1",
+				"mime-match": "^1.0.2",
+				"namespace-emitter": "^2.0.1",
+				"nanoid": "^3.1.25",
+				"preact": "^10.5.13"
+			}
+		},
+		"@uppy/store-default": {
+			"version": "2.1.1",
+			"resolved": "https://registry.npmjs.org/@uppy/store-default/-/store-default-2.1.1.tgz",
+			"integrity": "sha512-xnpTxvot2SeAwGwbvmJ899ASk5tYXhmZzD/aCFsXePh/v8rNvR2pKlcQUH7cF/y4baUGq3FHO/daKCok/mpKqQ=="
+		},
+		"@uppy/utils": {
+			"version": "4.1.3",
+			"resolved": "https://registry.npmjs.org/@uppy/utils/-/utils-4.1.3.tgz",
+			"integrity": "sha512-nTuMvwWYobnJcytDO3t+D6IkVq/Qs4Xv3vyoEZ+Iaf8gegZP+rEyoaFT2CK5XLRMienPyqRqNbIfRuFaOWSIFw==",
+			"requires": {
+				"lodash.throttle": "^4.1.1"
+			}
+		},
+		"@uppy/xhr-upload": {
+			"version": "2.1.3",
+			"resolved": "https://registry.npmjs.org/@uppy/xhr-upload/-/xhr-upload-2.1.3.tgz",
+			"integrity": "sha512-YWOQ6myBVPs+mhNjfdWsQyMRWUlrDLMoaG7nvf/G6Y3GKZf8AyjFDjvvJ49XWQ+DaZOftGkHmF1uh/DBeGivJQ==",
+			"requires": {
+				"@uppy/companion-client": "^2.2.2",
+				"@uppy/utils": "^4.1.2",
+				"nanoid": "^3.1.25"
+			}
+		},
+		"@vitejs/plugin-vue": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.0.0.tgz",
+			"integrity": "sha512-e0X4jErIxAB5oLtDqbHvHpJe/uWNkdpYV83AOG2xo2tEVSzCzewgJMtREZM30wXnM5ls90hxiOtAuVU6H5JgbA==",
+			"dev": true,
+			"requires": {}
+		},
+		"@vue/compiler-core": {
+			"version": "3.2.47",
+			"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.47.tgz",
+			"integrity": "sha512-p4D7FDnQb7+YJmO2iPEv0SQNeNzcbHdGByJDsT4lynf63AFkOTFN07HsiRSvjGo0QrxR/o3d0hUyNCUnBU2Tig==",
+			"requires": {
+				"@babel/parser": "^7.16.4",
+				"@vue/shared": "3.2.47",
+				"estree-walker": "^2.0.2",
+				"source-map": "^0.6.1"
+			}
+		},
+		"@vue/compiler-dom": {
+			"version": "3.2.47",
+			"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.47.tgz",
+			"integrity": "sha512-dBBnEHEPoftUiS03a4ggEig74J2YBZ2UIeyfpcRM2tavgMWo4bsEfgCGsu+uJIL/vax9S+JztH8NmQerUo7shQ==",
+			"requires": {
+				"@vue/compiler-core": "3.2.47",
+				"@vue/shared": "3.2.47"
+			}
+		},
+		"@vue/compiler-sfc": {
+			"version": "3.2.47",
+			"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.47.tgz",
+			"integrity": "sha512-rog05W+2IFfxjMcFw10tM9+f7i/+FFpZJJ5XHX72NP9eC2uRD+42M3pYcQqDXVYoj74kHMSEdQ/WmCjt8JFksQ==",
+			"requires": {
+				"@babel/parser": "^7.16.4",
+				"@vue/compiler-core": "3.2.47",
+				"@vue/compiler-dom": "3.2.47",
+				"@vue/compiler-ssr": "3.2.47",
+				"@vue/reactivity-transform": "3.2.47",
+				"@vue/shared": "3.2.47",
+				"estree-walker": "^2.0.2",
+				"magic-string": "^0.25.7",
+				"postcss": "^8.1.10",
+				"source-map": "^0.6.1"
+			}
+		},
+		"@vue/compiler-ssr": {
+			"version": "3.2.47",
+			"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.47.tgz",
+			"integrity": "sha512-wVXC+gszhulcMD8wpxMsqSOpvDZ6xKXSVWkf50Guf/S+28hTAXPDYRTbLQ3EDkOP5Xz/+SY37YiwDquKbJOgZw==",
+			"requires": {
+				"@vue/compiler-dom": "3.2.47",
+				"@vue/shared": "3.2.47"
+			}
+		},
+		"@vue/devtools-api": {
+			"version": "6.5.0",
+			"resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.5.0.tgz",
+			"integrity": "sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q=="
+		},
+		"@vue/reactivity": {
+			"version": "3.2.47",
+			"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.47.tgz",
+			"integrity": "sha512-7khqQ/75oyyg+N/e+iwV6lpy1f5wq759NdlS1fpAhFXa8VeAIKGgk2E/C4VF59lx5b+Ezs5fpp/5WsRYXQiKxQ==",
+			"requires": {
+				"@vue/shared": "3.2.47"
+			}
+		},
+		"@vue/reactivity-transform": {
+			"version": "3.2.47",
+			"resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.47.tgz",
+			"integrity": "sha512-m8lGXw8rdnPVVIdIFhf0LeQ/ixyHkH5plYuS83yop5n7ggVJU+z5v0zecwEnX7fa7HNLBhh2qngJJkxpwEEmYA==",
+			"requires": {
+				"@babel/parser": "^7.16.4",
+				"@vue/compiler-core": "3.2.47",
+				"@vue/shared": "3.2.47",
+				"estree-walker": "^2.0.2",
+				"magic-string": "^0.25.7"
+			}
+		},
+		"@vue/runtime-core": {
+			"version": "3.2.47",
+			"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.47.tgz",
+			"integrity": "sha512-RZxbLQIRB/K0ev0K9FXhNbBzT32H9iRtYbaXb0ZIz2usLms/D55dJR2t6cIEUn6vyhS3ALNvNthI+Q95C+NOpA==",
+			"requires": {
+				"@vue/reactivity": "3.2.47",
+				"@vue/shared": "3.2.47"
+			}
+		},
+		"@vue/runtime-dom": {
+			"version": "3.2.47",
+			"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.47.tgz",
+			"integrity": "sha512-ArXrFTjS6TsDei4qwNvgrdmHtD930KgSKGhS5M+j8QxXrDJYLqYw4RRcDy1bz1m1wMmb6j+zGLifdVHtkXA7gA==",
+			"requires": {
+				"@vue/runtime-core": "3.2.47",
+				"@vue/shared": "3.2.47",
+				"csstype": "^2.6.8"
+			}
+		},
+		"@vue/server-renderer": {
+			"version": "3.2.47",
+			"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.47.tgz",
+			"integrity": "sha512-dN9gc1i8EvmP9RCzvneONXsKfBRgqFeFZLurmHOveL7oH6HiFXJw5OGu294n1nHc/HMgTy6LulU/tv5/A7f/LA==",
+			"requires": {
+				"@vue/compiler-ssr": "3.2.47",
+				"@vue/shared": "3.2.47"
+			}
+		},
+		"@vue/shared": {
+			"version": "3.2.47",
+			"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.47.tgz",
+			"integrity": "sha512-BHGyyGN3Q97EZx0taMQ+OLNuZcW3d37ZEVmEAyeoA9ERdGvm9Irc/0Fua8SNyOtV1w6BS4q25wbMzJujO9HIfQ=="
+		},
+		"@vueuse/core": {
+			"version": "9.13.0",
+			"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-9.13.0.tgz",
+			"integrity": "sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==",
+			"requires": {
+				"@types/web-bluetooth": "^0.0.16",
+				"@vueuse/metadata": "9.13.0",
+				"@vueuse/shared": "9.13.0",
+				"vue-demi": "*"
+			},
+			"dependencies": {
+				"vue-demi": {
+					"version": "0.13.11",
+					"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.13.11.tgz",
+					"integrity": "sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==",
+					"requires": {}
+				}
+			}
+		},
+		"@vueuse/metadata": {
+			"version": "9.13.0",
+			"resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-9.13.0.tgz",
+			"integrity": "sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ=="
+		},
+		"@vueuse/shared": {
+			"version": "9.13.0",
+			"resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-9.13.0.tgz",
+			"integrity": "sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==",
+			"requires": {
+				"vue-demi": "*"
+			},
+			"dependencies": {
+				"vue-demi": {
+					"version": "0.13.11",
+					"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.13.11.tgz",
+					"integrity": "sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==",
+					"requires": {}
+				}
+			}
+		},
+		"@wangeditor/basic-modules": {
+			"version": "1.1.7",
+			"resolved": "https://registry.npmjs.org/@wangeditor/basic-modules/-/basic-modules-1.1.7.tgz",
+			"integrity": "sha512-cY9CPkLJaqF05STqfpZKWG4LpxTMeGSIIF1fHvfm/mz+JXatCagjdkbxdikOuKYlxDdeqvOeBmsUBItufDLXZg==",
+			"requires": {
+				"is-url": "^1.2.4"
+			}
+		},
+		"@wangeditor/code-highlight": {
+			"version": "1.0.3",
+			"resolved": "https://registry.npmjs.org/@wangeditor/code-highlight/-/code-highlight-1.0.3.tgz",
+			"integrity": "sha512-iazHwO14XpCuIWJNTQTikqUhGKyqj+dUNWJ9288Oym9M2xMVHvnsOmDU2sgUDWVy+pOLojReMPgXCsvvNlOOhw==",
+			"requires": {
+				"prismjs": "^1.23.0"
+			}
+		},
+		"@wangeditor/core": {
+			"version": "1.1.19",
+			"resolved": "https://registry.npmjs.org/@wangeditor/core/-/core-1.1.19.tgz",
+			"integrity": "sha512-KevkB47+7GhVszyYF2pKGKtCSj/YzmClsD03C3zTt+9SR2XWT5T0e3yQqg8baZpcMvkjs1D8Dv4fk8ok/UaS2Q==",
+			"requires": {
+				"@types/event-emitter": "^0.3.3",
+				"event-emitter": "^0.3.5",
+				"html-void-elements": "^2.0.0",
+				"i18next": "^20.4.0",
+				"scroll-into-view-if-needed": "^2.2.28",
+				"slate-history": "^0.66.0"
+			}
+		},
+		"@wangeditor/editor": {
+			"version": "5.1.23",
+			"resolved": "https://registry.npmjs.org/@wangeditor/editor/-/editor-5.1.23.tgz",
+			"integrity": "sha512-0RxfeVTuK1tktUaPROnCoFfaHVJpRAIE2zdS0mpP+vq1axVQpLjM8+fCvKzqYIkH0Pg+C+44hJpe3VVroSkEuQ==",
+			"requires": {
+				"@uppy/core": "^2.1.1",
+				"@uppy/xhr-upload": "^2.0.3",
+				"@wangeditor/basic-modules": "^1.1.7",
+				"@wangeditor/code-highlight": "^1.0.3",
+				"@wangeditor/core": "^1.1.19",
+				"@wangeditor/list-module": "^1.0.5",
+				"@wangeditor/table-module": "^1.1.4",
+				"@wangeditor/upload-image-module": "^1.0.2",
+				"@wangeditor/video-module": "^1.1.4",
+				"dom7": "^3.0.0",
+				"is-hotkey": "^0.2.0",
+				"lodash.camelcase": "^4.3.0",
+				"lodash.clonedeep": "^4.5.0",
+				"lodash.debounce": "^4.0.8",
+				"lodash.foreach": "^4.5.0",
+				"lodash.isequal": "^4.5.0",
+				"lodash.throttle": "^4.1.1",
+				"lodash.toarray": "^4.4.0",
+				"nanoid": "^3.2.0",
+				"slate": "^0.72.0",
+				"snabbdom": "^3.1.0"
+			}
+		},
+		"@wangeditor/editor-for-vue": {
+			"version": "5.1.12",
+			"resolved": "https://registry.npmjs.org/@wangeditor/editor-for-vue/-/editor-for-vue-5.1.12.tgz",
+			"integrity": "sha512-0Ds3D8I+xnpNWezAeO7HmPRgTfUxHLMd9JKcIw+QzvSmhC5xUHbpCcLU+KLmeBKTR/zffnS5GQo6qi3GhTMJWQ==",
+			"requires": {}
+		},
+		"@wangeditor/list-module": {
+			"version": "1.0.5",
+			"resolved": "https://registry.npmjs.org/@wangeditor/list-module/-/list-module-1.0.5.tgz",
+			"integrity": "sha512-uDuYTP6DVhcYf7mF1pTlmNn5jOb4QtcVhYwSSAkyg09zqxI1qBqsfUnveeDeDqIuptSJhkh81cyxi+MF8sEPOQ==",
+			"requires": {}
+		},
+		"@wangeditor/table-module": {
+			"version": "1.1.4",
+			"resolved": "https://registry.npmjs.org/@wangeditor/table-module/-/table-module-1.1.4.tgz",
+			"integrity": "sha512-5saanU9xuEocxaemGdNi9t8MCDSucnykEC6jtuiT72kt+/Hhh4nERYx1J20OPsTCCdVr7hIyQenFD1iSRkIQ6w==",
+			"requires": {}
+		},
+		"@wangeditor/upload-image-module": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/@wangeditor/upload-image-module/-/upload-image-module-1.0.2.tgz",
+			"integrity": "sha512-z81lk/v71OwPDYeQDxj6cVr81aDP90aFuywb8nPD6eQeECtOymrqRODjpO6VGvCVxVck8nUxBHtbxKtjgcwyiA==",
+			"requires": {}
+		},
+		"@wangeditor/video-module": {
+			"version": "1.1.4",
+			"resolved": "https://registry.npmjs.org/@wangeditor/video-module/-/video-module-1.1.4.tgz",
+			"integrity": "sha512-ZdodDPqKQrgx3IwWu4ZiQmXI8EXZ3hm2/fM6E3t5dB8tCaIGWQZhmqd6P5knfkRAd3z2+YRSRbxOGfoRSp/rLg==",
+			"requires": {}
+		},
+		"acorn": {
+			"version": "8.8.2",
+			"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz",
+			"integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==",
+			"dev": true
+		},
+		"acorn-jsx": {
+			"version": "5.3.2",
+			"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+			"integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+			"dev": true,
+			"requires": {}
+		},
+		"acorn-node": {
+			"version": "1.8.2",
+			"resolved": "https://registry.npmmirror.com/acorn-node/-/acorn-node-1.8.2.tgz",
+			"integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==",
+			"dev": true,
+			"requires": {
+				"acorn": "^7.0.0",
+				"acorn-walk": "^7.0.0",
+				"xtend": "^4.0.2"
+			},
+			"dependencies": {
+				"acorn": {
+					"version": "7.4.1",
+					"resolved": "https://registry.npmmirror.com/acorn/-/acorn-7.4.1.tgz",
+					"integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
+					"dev": true
+				}
+			}
+		},
+		"acorn-walk": {
+			"version": "7.2.0",
+			"resolved": "https://registry.npmmirror.com/acorn-walk/-/acorn-walk-7.2.0.tgz",
+			"integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==",
+			"dev": true
+		},
+		"ajv": {
+			"version": "6.12.6",
+			"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+			"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+			"dev": true,
+			"requires": {
+				"fast-deep-equal": "^3.1.1",
+				"fast-json-stable-stringify": "^2.0.0",
+				"json-schema-traverse": "^0.4.1",
+				"uri-js": "^4.2.2"
+			}
+		},
+		"ansi-regex": {
+			"version": "5.0.1",
+			"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+			"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+			"dev": true
+		},
+		"ansi-styles": {
+			"version": "4.3.0",
+			"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+			"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+			"dev": true,
+			"requires": {
+				"color-convert": "^2.0.1"
+			}
+		},
+		"anymatch": {
+			"version": "3.1.3",
+			"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+			"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+			"dev": true,
+			"requires": {
+				"normalize-path": "^3.0.0",
+				"picomatch": "^2.0.4"
+			}
+		},
+		"arg": {
+			"version": "5.0.2",
+			"resolved": "https://registry.npmmirror.com/arg/-/arg-5.0.2.tgz",
+			"integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
+			"dev": true
+		},
+		"argparse": {
+			"version": "2.0.1",
+			"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+			"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+			"dev": true
+		},
+		"array-union": {
+			"version": "2.1.0",
+			"resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
+			"integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
+			"dev": true
+		},
+		"async-validator": {
+			"version": "4.2.5",
+			"resolved": "https://registry.npmjs.org/async-validator/-/async-validator-4.2.5.tgz",
+			"integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg=="
+		},
+		"asynckit": {
+			"version": "0.4.0",
+			"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+			"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
+		},
+		"autoprefixer": {
+			"version": "10.4.13",
+			"resolved": "https://registry.npmmirror.com/autoprefixer/-/autoprefixer-10.4.13.tgz",
+			"integrity": "sha512-49vKpMqcZYsJjwotvt4+h/BCjJVnhGwcLpDt5xkcaOG3eLrG/HUYLagrihYsQ+qrIBgIzX1Rw7a6L8I/ZA1Atg==",
+			"dev": true,
+			"requires": {
+				"browserslist": "^4.21.4",
+				"caniuse-lite": "^1.0.30001426",
+				"fraction.js": "^4.2.0",
+				"normalize-range": "^0.1.2",
+				"picocolors": "^1.0.0",
+				"postcss-value-parser": "^4.2.0"
+			}
+		},
+		"axios": {
+			"version": "1.3.3",
+			"resolved": "https://registry.npmjs.org/axios/-/axios-1.3.3.tgz",
+			"integrity": "sha512-eYq77dYIFS77AQlhzEL937yUBSepBfPIe8FcgEDN35vMNZKMrs81pgnyrQpwfy4NF4b4XWX1Zgx7yX+25w8QJA==",
+			"requires": {
+				"follow-redirects": "^1.15.0",
+				"form-data": "^4.0.0",
+				"proxy-from-env": "^1.1.0"
+			}
+		},
+		"balanced-match": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+			"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+			"dev": true
+		},
+		"batch-processor": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/batch-processor/-/batch-processor-1.0.0.tgz",
+			"integrity": "sha512-xoLQD8gmmR32MeuBHgH0Tzd5PuSZx71ZsbhVxOCRbgktZEPe4SQy7s9Z50uPp0F/f7iw2XmkHN2xkgbMfckMDA=="
+		},
+		"binary-extensions": {
+			"version": "2.2.0",
+			"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
+			"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
+			"dev": true
+		},
+		"boolbase": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
+			"integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
+			"dev": true
+		},
+		"brace-expansion": {
+			"version": "1.1.11",
+			"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+			"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+			"dev": true,
+			"requires": {
+				"balanced-match": "^1.0.0",
+				"concat-map": "0.0.1"
+			}
+		},
+		"braces": {
+			"version": "3.0.2",
+			"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+			"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+			"dev": true,
+			"requires": {
+				"fill-range": "^7.0.1"
+			}
+		},
+		"browserslist": {
+			"version": "4.21.5",
+			"resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.21.5.tgz",
+			"integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==",
+			"dev": true,
+			"requires": {
+				"caniuse-lite": "^1.0.30001449",
+				"electron-to-chromium": "^1.4.284",
+				"node-releases": "^2.0.8",
+				"update-browserslist-db": "^1.0.10"
+			}
+		},
+		"call-bind": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
+			"integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
+			"requires": {
+				"function-bind": "^1.1.1",
+				"get-intrinsic": "^1.0.2"
+			}
+		},
+		"callsites": {
+			"version": "3.1.0",
+			"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+			"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+			"dev": true
+		},
+		"camelcase-css": {
+			"version": "2.0.1",
+			"resolved": "https://registry.npmmirror.com/camelcase-css/-/camelcase-css-2.0.1.tgz",
+			"integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==",
+			"dev": true
+		},
+		"caniuse-lite": {
+			"version": "1.0.30001458",
+			"resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001458.tgz",
+			"integrity": "sha512-lQ1VlUUq5q9ro9X+5gOEyH7i3vm+AYVT1WDCVB69XOZ17KZRhnZ9J0Sqz7wTHQaLBJccNCHq8/Ww5LlOIZbB0w==",
+			"dev": true
+		},
+		"chalk": {
+			"version": "4.1.2",
+			"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+			"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+			"dev": true,
+			"requires": {
+				"ansi-styles": "^4.1.0",
+				"supports-color": "^7.1.0"
+			}
+		},
+		"chokidar": {
+			"version": "3.5.3",
+			"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
+			"integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
+			"dev": true,
+			"requires": {
+				"anymatch": "~3.1.2",
+				"braces": "~3.0.2",
+				"fsevents": "~2.3.2",
+				"glob-parent": "~5.1.2",
+				"is-binary-path": "~2.1.0",
+				"is-glob": "~4.0.1",
+				"normalize-path": "~3.0.0",
+				"readdirp": "~3.6.0"
+			},
+			"dependencies": {
+				"glob-parent": {
+					"version": "5.1.2",
+					"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+					"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+					"dev": true,
+					"requires": {
+						"is-glob": "^4.0.1"
+					}
+				}
+			}
+		},
+		"claygl": {
+			"version": "1.3.0",
+			"resolved": "https://registry.npmjs.org/claygl/-/claygl-1.3.0.tgz",
+			"integrity": "sha512-+gGtJjT6SSHD2l2yC3MCubW/sCV40tZuSs5opdtn79vFSGUgp/lH139RNEQ6Jy078/L0aV8odCw8RSrUcMfLaQ=="
+		},
+		"clipboard": {
+			"version": "2.0.11",
+			"resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.11.tgz",
+			"integrity": "sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==",
+			"requires": {
+				"good-listener": "^1.2.2",
+				"select": "^1.1.2",
+				"tiny-emitter": "^2.0.0"
+			}
+		},
+		"color-convert": {
+			"version": "2.0.1",
+			"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+			"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+			"dev": true,
+			"requires": {
+				"color-name": "~1.1.4"
+			}
+		},
+		"color-name": {
+			"version": "1.1.4",
+			"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+			"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+			"dev": true
+		},
+		"combined-stream": {
+			"version": "1.0.8",
+			"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+			"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+			"requires": {
+				"delayed-stream": "~1.0.0"
+			}
+		},
+		"compute-scroll-into-view": {
+			"version": "1.0.20",
+			"resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-1.0.20.tgz",
+			"integrity": "sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg=="
+		},
+		"concat-map": {
+			"version": "0.0.1",
+			"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+			"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+			"dev": true
+		},
+		"countup.js": {
+			"version": "2.4.2",
+			"resolved": "https://registry.npmjs.org/countup.js/-/countup.js-2.4.2.tgz",
+			"integrity": "sha512-EExCcu5rd7ffBj65B3CVNuS1HddN1Y4WuTfJEuocJXwZlNnlXZQ4sD9M/Cq32ZS0zR38F9vGMCw/iFcVImrNbw=="
+		},
+		"cropperjs": {
+			"version": "1.5.13",
+			"resolved": "https://registry.npmjs.org/cropperjs/-/cropperjs-1.5.13.tgz",
+			"integrity": "sha512-by7jKAo73y5/Do0K6sxdTKHgndY0NMjG2bEdgeJxycbcmHuCiMXqw8sxy5C5Y5WTOTcDGmbT7Sr5CgKOXR06OA=="
+		},
+		"cross-spawn": {
+			"version": "7.0.3",
+			"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
+			"integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+			"dev": true,
+			"requires": {
+				"path-key": "^3.1.0",
+				"shebang-command": "^2.0.0",
+				"which": "^2.0.1"
+			}
+		},
+		"cssesc": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
+			"integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
+			"dev": true
+		},
+		"csstype": {
+			"version": "2.6.21",
+			"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz",
+			"integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w=="
+		},
+		"d": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
+			"integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
+			"requires": {
+				"es5-ext": "^0.10.50",
+				"type": "^1.0.1"
+			}
+		},
+		"dayjs": {
+			"version": "1.11.7",
+			"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.7.tgz",
+			"integrity": "sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ=="
+		},
+		"debug": {
+			"version": "4.3.4",
+			"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+			"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+			"dev": true,
+			"requires": {
+				"ms": "2.1.2"
+			}
+		},
+		"deep-is": {
+			"version": "0.1.4",
+			"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+			"integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+			"dev": true
+		},
+		"defined": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmmirror.com/defined/-/defined-1.0.1.tgz",
+			"integrity": "sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==",
+			"dev": true
+		},
+		"delayed-stream": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+			"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
+		},
+		"delegate": {
+			"version": "3.2.0",
+			"resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz",
+			"integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw=="
+		},
+		"detective": {
+			"version": "5.2.1",
+			"resolved": "https://registry.npmmirror.com/detective/-/detective-5.2.1.tgz",
+			"integrity": "sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw==",
+			"dev": true,
+			"requires": {
+				"acorn-node": "^1.8.2",
+				"defined": "^1.0.0",
+				"minimist": "^1.2.6"
+			}
+		},
+		"didyoumean": {
+			"version": "1.2.2",
+			"resolved": "https://registry.npmmirror.com/didyoumean/-/didyoumean-1.2.2.tgz",
+			"integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==",
+			"dev": true
+		},
+		"dir-glob": {
+			"version": "3.0.1",
+			"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
+			"integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
+			"dev": true,
+			"requires": {
+				"path-type": "^4.0.0"
+			}
+		},
+		"dlv": {
+			"version": "1.1.3",
+			"resolved": "https://registry.npmmirror.com/dlv/-/dlv-1.1.3.tgz",
+			"integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
+			"dev": true
+		},
+		"doctrine": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+			"integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+			"dev": true,
+			"requires": {
+				"esutils": "^2.0.2"
+			}
+		},
+		"dom7": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/dom7/-/dom7-3.0.0.tgz",
+			"integrity": "sha512-oNlcUdHsC4zb7Msx7JN3K0Nro1dzJ48knvBOnDPKJ2GV9wl1i5vydJZUSyOfrkKFDZEud/jBsTk92S/VGSAe/g==",
+			"requires": {
+				"ssr-window": "^3.0.0-alpha.1"
+			}
+		},
+		"echarts": {
+			"version": "5.4.1",
+			"resolved": "https://registry.npmjs.org/echarts/-/echarts-5.4.1.tgz",
+			"integrity": "sha512-9ltS3M2JB0w2EhcYjCdmtrJ+6haZcW6acBolMGIuf01Hql1yrIV01L1aRj7jsaaIULJslEP9Z3vKlEmnJaWJVQ==",
+			"requires": {
+				"tslib": "2.3.0",
+				"zrender": "5.4.1"
+			}
+		},
+		"echarts-gl": {
+			"version": "2.0.9",
+			"resolved": "https://registry.npmjs.org/echarts-gl/-/echarts-gl-2.0.9.tgz",
+			"integrity": "sha512-oKeMdkkkpJGWOzjgZUsF41DOh6cMsyrGGXimbjK2l6Xeq/dBQu4ShG2w2Dzrs/1bD27b2pLTGSaUzouY191gzA==",
+			"requires": {
+				"claygl": "^1.2.1",
+				"zrender": "^5.1.1"
+			}
+		},
+		"echarts-wordcloud": {
+			"version": "2.1.0",
+			"resolved": "https://registry.npmjs.org/echarts-wordcloud/-/echarts-wordcloud-2.1.0.tgz",
+			"integrity": "sha512-Kt1JmbcROgb+3IMI48KZECK2AP5lG6bSsOEs+AsuwaWJxQom31RTNd6NFYI01E/YaI1PFZeueaupjlmzSQasjQ==",
+			"requires": {}
+		},
+		"electron-to-chromium": {
+			"version": "1.4.313",
+			"resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.4.313.tgz",
+			"integrity": "sha512-QckB9OVqr2oybjIrbMI99uF+b9+iTja5weFe0ePbqLb5BHqXOJUO1SG6kDj/1WtWPRIBr51N153AEq8m7HuIaA==",
+			"dev": true
+		},
+		"element-plus": {
+			"version": "2.2.32",
+			"resolved": "https://registry.npmjs.org/element-plus/-/element-plus-2.2.32.tgz",
+			"integrity": "sha512-DTJMhYOy6MApbmh6z/95hPTK5WrBiNHGzV4IN+uEkup1WoimQ+Qyt8RxKdTe/X1LWEJ8YgWv/Cl8P4ocrt5z5g==",
+			"requires": {
+				"@ctrl/tinycolor": "^3.4.1",
+				"@element-plus/icons-vue": "^2.0.6",
+				"@floating-ui/dom": "^1.0.1",
+				"@popperjs/core": "npm:@sxzz/popperjs-es@^2.11.7",
+				"@types/lodash": "^4.14.182",
+				"@types/lodash-es": "^4.17.6",
+				"@vueuse/core": "^9.1.0",
+				"async-validator": "^4.2.5",
+				"dayjs": "^1.11.3",
+				"escape-html": "^1.0.3",
+				"lodash": "^4.17.21",
+				"lodash-es": "^4.17.21",
+				"lodash-unified": "^1.0.2",
+				"memoize-one": "^6.0.0",
+				"normalize-wheel-es": "^1.2.0"
+			}
+		},
+		"element-resize-detector": {
+			"version": "1.2.4",
+			"resolved": "https://registry.npmjs.org/element-resize-detector/-/element-resize-detector-1.2.4.tgz",
+			"integrity": "sha512-Fl5Ftk6WwXE0wqCgNoseKWndjzZlDCwuPTcoVZfCP9R3EHQF8qUtr3YUPNETegRBOKqQKPW3n4kiIWngGi8tKg==",
+			"requires": {
+				"batch-processor": "1.0.0"
+			}
+		},
+		"es5-ext": {
+			"version": "0.10.62",
+			"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz",
+			"integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==",
+			"requires": {
+				"es6-iterator": "^2.0.3",
+				"es6-symbol": "^3.1.3",
+				"next-tick": "^1.1.0"
+			}
+		},
+		"es6-iterator": {
+			"version": "2.0.3",
+			"resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
+			"integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==",
+			"requires": {
+				"d": "1",
+				"es5-ext": "^0.10.35",
+				"es6-symbol": "^3.1.1"
+			}
+		},
+		"es6-symbol": {
+			"version": "3.1.3",
+			"resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz",
+			"integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==",
+			"requires": {
+				"d": "^1.0.1",
+				"ext": "^1.1.2"
+			}
+		},
+		"esbuild": {
+			"version": "0.16.17",
+			"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.16.17.tgz",
+			"integrity": "sha512-G8LEkV0XzDMNwXKgM0Jwu3nY3lSTwSGY6XbxM9cr9+s0T/qSV1q1JVPBGzm3dcjhCic9+emZDmMffkwgPeOeLg==",
+			"dev": true,
+			"requires": {
+				"@esbuild/android-arm": "0.16.17",
+				"@esbuild/android-arm64": "0.16.17",
+				"@esbuild/android-x64": "0.16.17",
+				"@esbuild/darwin-arm64": "0.16.17",
+				"@esbuild/darwin-x64": "0.16.17",
+				"@esbuild/freebsd-arm64": "0.16.17",
+				"@esbuild/freebsd-x64": "0.16.17",
+				"@esbuild/linux-arm": "0.16.17",
+				"@esbuild/linux-arm64": "0.16.17",
+				"@esbuild/linux-ia32": "0.16.17",
+				"@esbuild/linux-loong64": "0.16.17",
+				"@esbuild/linux-mips64el": "0.16.17",
+				"@esbuild/linux-ppc64": "0.16.17",
+				"@esbuild/linux-riscv64": "0.16.17",
+				"@esbuild/linux-s390x": "0.16.17",
+				"@esbuild/linux-x64": "0.16.17",
+				"@esbuild/netbsd-x64": "0.16.17",
+				"@esbuild/openbsd-x64": "0.16.17",
+				"@esbuild/sunos-x64": "0.16.17",
+				"@esbuild/win32-arm64": "0.16.17",
+				"@esbuild/win32-ia32": "0.16.17",
+				"@esbuild/win32-x64": "0.16.17"
+			}
+		},
+		"escalade": {
+			"version": "3.1.1",
+			"resolved": "https://registry.npmmirror.com/escalade/-/escalade-3.1.1.tgz",
+			"integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
+			"dev": true
+		},
+		"escape-html": {
+			"version": "1.0.3",
+			"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+			"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
+		},
+		"escape-string-regexp": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+			"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+			"dev": true
+		},
+		"eslint": {
+			"version": "8.34.0",
+			"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.34.0.tgz",
+			"integrity": "sha512-1Z8iFsucw+7kSqXNZVslXS8Ioa4u2KM7GPwuKtkTFAqZ/cHMcEaR+1+Br0wLlot49cNxIiZk5wp8EAbPcYZxTg==",
+			"dev": true,
+			"requires": {
+				"@eslint/eslintrc": "^1.4.1",
+				"@humanwhocodes/config-array": "^0.11.8",
+				"@humanwhocodes/module-importer": "^1.0.1",
+				"@nodelib/fs.walk": "^1.2.8",
+				"ajv": "^6.10.0",
+				"chalk": "^4.0.0",
+				"cross-spawn": "^7.0.2",
+				"debug": "^4.3.2",
+				"doctrine": "^3.0.0",
+				"escape-string-regexp": "^4.0.0",
+				"eslint-scope": "^7.1.1",
+				"eslint-utils": "^3.0.0",
+				"eslint-visitor-keys": "^3.3.0",
+				"espree": "^9.4.0",
+				"esquery": "^1.4.0",
+				"esutils": "^2.0.2",
+				"fast-deep-equal": "^3.1.3",
+				"file-entry-cache": "^6.0.1",
+				"find-up": "^5.0.0",
+				"glob-parent": "^6.0.2",
+				"globals": "^13.19.0",
+				"grapheme-splitter": "^1.0.4",
+				"ignore": "^5.2.0",
+				"import-fresh": "^3.0.0",
+				"imurmurhash": "^0.1.4",
+				"is-glob": "^4.0.0",
+				"is-path-inside": "^3.0.3",
+				"js-sdsl": "^4.1.4",
+				"js-yaml": "^4.1.0",
+				"json-stable-stringify-without-jsonify": "^1.0.1",
+				"levn": "^0.4.1",
+				"lodash.merge": "^4.6.2",
+				"minimatch": "^3.1.2",
+				"natural-compare": "^1.4.0",
+				"optionator": "^0.9.1",
+				"regexpp": "^3.2.0",
+				"strip-ansi": "^6.0.1",
+				"strip-json-comments": "^3.1.0",
+				"text-table": "^0.2.0"
+			},
+			"dependencies": {
+				"eslint-scope": {
+					"version": "7.1.1",
+					"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz",
+					"integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==",
+					"dev": true,
+					"requires": {
+						"esrecurse": "^4.3.0",
+						"estraverse": "^5.2.0"
+					}
+				},
+				"estraverse": {
+					"version": "5.3.0",
+					"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+					"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+					"dev": true
+				}
+			}
+		},
+		"eslint-plugin-vue": {
+			"version": "9.9.0",
+			"resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.9.0.tgz",
+			"integrity": "sha512-YbubS7eK0J7DCf0U2LxvVP7LMfs6rC6UltihIgval3azO3gyDwEGVgsCMe1TmDiEkl6GdMKfRpaME6QxIYtzDQ==",
+			"dev": true,
+			"requires": {
+				"eslint-utils": "^3.0.0",
+				"natural-compare": "^1.4.0",
+				"nth-check": "^2.0.1",
+				"postcss-selector-parser": "^6.0.9",
+				"semver": "^7.3.5",
+				"vue-eslint-parser": "^9.0.1",
+				"xml-name-validator": "^4.0.0"
+			}
+		},
+		"eslint-scope": {
+			"version": "5.1.1",
+			"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
+			"integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
+			"dev": true,
+			"requires": {
+				"esrecurse": "^4.3.0",
+				"estraverse": "^4.1.1"
+			}
+		},
+		"eslint-utils": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz",
+			"integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==",
+			"dev": true,
+			"requires": {
+				"eslint-visitor-keys": "^2.0.0"
+			},
+			"dependencies": {
+				"eslint-visitor-keys": {
+					"version": "2.1.0",
+					"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
+					"integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
+					"dev": true
+				}
+			}
+		},
+		"eslint-visitor-keys": {
+			"version": "3.3.0",
+			"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz",
+			"integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==",
+			"dev": true
+		},
+		"espree": {
+			"version": "9.4.1",
+			"resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz",
+			"integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==",
+			"dev": true,
+			"requires": {
+				"acorn": "^8.8.0",
+				"acorn-jsx": "^5.3.2",
+				"eslint-visitor-keys": "^3.3.0"
+			}
+		},
+		"esquery": {
+			"version": "1.4.2",
+			"resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.2.tgz",
+			"integrity": "sha512-JVSoLdTlTDkmjFmab7H/9SL9qGSyjElT3myyKp7krqjVFQCDLmj1QFaCLRFBszBKI0XVZaiiXvuPIX3ZwHe1Ng==",
+			"dev": true,
+			"requires": {
+				"estraverse": "^5.1.0"
+			},
+			"dependencies": {
+				"estraverse": {
+					"version": "5.3.0",
+					"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+					"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+					"dev": true
+				}
+			}
+		},
+		"esrecurse": {
+			"version": "4.3.0",
+			"resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+			"integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+			"dev": true,
+			"requires": {
+				"estraverse": "^5.2.0"
+			},
+			"dependencies": {
+				"estraverse": {
+					"version": "5.3.0",
+					"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+					"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+					"dev": true
+				}
+			}
+		},
+		"estraverse": {
+			"version": "4.3.0",
+			"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
+			"integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+			"dev": true
+		},
+		"estree-walker": {
+			"version": "2.0.2",
+			"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
+			"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
+		},
+		"esutils": {
+			"version": "2.0.3",
+			"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+			"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+			"dev": true
+		},
+		"event-emitter": {
+			"version": "0.3.5",
+			"resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz",
+			"integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==",
+			"requires": {
+				"d": "1",
+				"es5-ext": "~0.10.14"
+			}
+		},
+		"ext": {
+			"version": "1.7.0",
+			"resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz",
+			"integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==",
+			"requires": {
+				"type": "^2.7.2"
+			},
+			"dependencies": {
+				"type": {
+					"version": "2.7.2",
+					"resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz",
+					"integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw=="
+				}
+			}
+		},
+		"fast-deep-equal": {
+			"version": "3.1.3",
+			"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+			"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+			"dev": true
+		},
+		"fast-glob": {
+			"version": "3.2.12",
+			"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz",
+			"integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==",
+			"dev": true,
+			"requires": {
+				"@nodelib/fs.stat": "^2.0.2",
+				"@nodelib/fs.walk": "^1.2.3",
+				"glob-parent": "^5.1.2",
+				"merge2": "^1.3.0",
+				"micromatch": "^4.0.4"
+			},
+			"dependencies": {
+				"glob-parent": {
+					"version": "5.1.2",
+					"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+					"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+					"dev": true,
+					"requires": {
+						"is-glob": "^4.0.1"
+					}
+				}
+			}
+		},
+		"fast-json-stable-stringify": {
+			"version": "2.1.0",
+			"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+			"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+			"dev": true
+		},
+		"fast-levenshtein": {
+			"version": "2.0.6",
+			"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+			"integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+			"dev": true
+		},
+		"fastq": {
+			"version": "1.15.0",
+			"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz",
+			"integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==",
+			"dev": true,
+			"requires": {
+				"reusify": "^1.0.4"
+			}
+		},
+		"file-entry-cache": {
+			"version": "6.0.1",
+			"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
+			"integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
+			"dev": true,
+			"requires": {
+				"flat-cache": "^3.0.4"
+			}
+		},
+		"fill-range": {
+			"version": "7.0.1",
+			"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+			"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+			"dev": true,
+			"requires": {
+				"to-regex-range": "^5.0.1"
+			}
+		},
+		"find-up": {
+			"version": "5.0.0",
+			"resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+			"integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+			"dev": true,
+			"requires": {
+				"locate-path": "^6.0.0",
+				"path-exists": "^4.0.0"
+			}
+		},
+		"flat-cache": {
+			"version": "3.0.4",
+			"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz",
+			"integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==",
+			"dev": true,
+			"requires": {
+				"flatted": "^3.1.0",
+				"rimraf": "^3.0.2"
+			}
+		},
+		"flatted": {
+			"version": "3.2.7",
+			"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz",
+			"integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==",
+			"dev": true
+		},
+		"follow-redirects": {
+			"version": "1.15.2",
+			"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
+			"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA=="
+		},
+		"form-data": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+			"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+			"requires": {
+				"asynckit": "^0.4.0",
+				"combined-stream": "^1.0.8",
+				"mime-types": "^2.1.12"
+			}
+		},
+		"fraction.js": {
+			"version": "4.2.0",
+			"resolved": "https://registry.npmmirror.com/fraction.js/-/fraction.js-4.2.0.tgz",
+			"integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==",
+			"dev": true
+		},
+		"fs.realpath": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+			"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+			"dev": true
+		},
+		"fsevents": {
+			"version": "2.3.2",
+			"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+			"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+			"dev": true,
+			"optional": true
+		},
+		"function-bind": {
+			"version": "1.1.1",
+			"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+			"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
+		},
+		"get-intrinsic": {
+			"version": "1.2.0",
+			"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz",
+			"integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==",
+			"requires": {
+				"function-bind": "^1.1.1",
+				"has": "^1.0.3",
+				"has-symbols": "^1.0.3"
+			}
+		},
+		"glob": {
+			"version": "7.2.3",
+			"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+			"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+			"dev": true,
+			"requires": {
+				"fs.realpath": "^1.0.0",
+				"inflight": "^1.0.4",
+				"inherits": "2",
+				"minimatch": "^3.1.1",
+				"once": "^1.3.0",
+				"path-is-absolute": "^1.0.0"
+			}
+		},
+		"glob-parent": {
+			"version": "6.0.2",
+			"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+			"integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+			"dev": true,
+			"requires": {
+				"is-glob": "^4.0.3"
+			}
+		},
+		"globals": {
+			"version": "13.20.0",
+			"resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz",
+			"integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==",
+			"dev": true,
+			"requires": {
+				"type-fest": "^0.20.2"
+			}
+		},
+		"globby": {
+			"version": "11.1.0",
+			"resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
+			"integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
+			"dev": true,
+			"requires": {
+				"array-union": "^2.1.0",
+				"dir-glob": "^3.0.1",
+				"fast-glob": "^3.2.9",
+				"ignore": "^5.2.0",
+				"merge2": "^1.4.1",
+				"slash": "^3.0.0"
+			}
+		},
+		"good-listener": {
+			"version": "1.2.2",
+			"resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz",
+			"integrity": "sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw==",
+			"requires": {
+				"delegate": "^3.1.2"
+			}
+		},
+		"grapheme-splitter": {
+			"version": "1.0.4",
+			"resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz",
+			"integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==",
+			"dev": true
+		},
+		"has": {
+			"version": "1.0.3",
+			"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+			"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+			"requires": {
+				"function-bind": "^1.1.1"
+			}
+		},
+		"has-flag": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+			"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+			"dev": true
+		},
+		"has-symbols": {
+			"version": "1.0.3",
+			"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
+			"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A=="
+		},
+		"html-void-elements": {
+			"version": "2.0.1",
+			"resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-2.0.1.tgz",
+			"integrity": "sha512-0quDb7s97CfemeJAnW9wC0hw78MtW7NU3hqtCD75g2vFlDLt36llsYD7uB7SUzojLMP24N5IatXf7ylGXiGG9A=="
+		},
+		"i18next": {
+			"version": "20.6.1",
+			"resolved": "https://registry.npmjs.org/i18next/-/i18next-20.6.1.tgz",
+			"integrity": "sha512-yCMYTMEJ9ihCwEQQ3phLo7I/Pwycf8uAx+sRHwwk5U9Aui/IZYgQRyMqXafQOw5QQ7DM1Z+WyEXWIqSuJHhG2A==",
+			"requires": {
+				"@babel/runtime": "^7.12.0"
+			}
+		},
+		"ignore": {
+			"version": "5.2.4",
+			"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
+			"integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==",
+			"dev": true
+		},
+		"immer": {
+			"version": "9.0.19",
+			"resolved": "https://registry.npmjs.org/immer/-/immer-9.0.19.tgz",
+			"integrity": "sha512-eY+Y0qcsB4TZKwgQzLaE/lqYMlKhv5J9dyd2RhhtGhNo2njPXDqU9XPfcNfa3MIDsdtZt5KlkIsirlo4dHsWdQ=="
+		},
+		"immutable": {
+			"version": "4.2.4",
+			"resolved": "https://registry.npmjs.org/immutable/-/immutable-4.2.4.tgz",
+			"integrity": "sha512-WDxL3Hheb1JkRN3sQkyujNlL/xRjAo3rJtaU5xeufUauG66JdMr32bLj4gF+vWl84DIA3Zxw7tiAjneYzRRw+w==",
+			"dev": true
+		},
+		"import-fresh": {
+			"version": "3.3.0",
+			"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
+			"integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+			"dev": true,
+			"requires": {
+				"parent-module": "^1.0.0",
+				"resolve-from": "^4.0.0"
+			}
+		},
+		"imurmurhash": {
+			"version": "0.1.4",
+			"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+			"integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+			"dev": true
+		},
+		"inflight": {
+			"version": "1.0.6",
+			"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+			"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+			"dev": true,
+			"requires": {
+				"once": "^1.3.0",
+				"wrappy": "1"
+			}
+		},
+		"inherits": {
+			"version": "2.0.4",
+			"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+			"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+			"dev": true
+		},
+		"is-binary-path": {
+			"version": "2.1.0",
+			"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+			"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+			"dev": true,
+			"requires": {
+				"binary-extensions": "^2.0.0"
+			}
+		},
+		"is-core-module": {
+			"version": "2.11.0",
+			"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz",
+			"integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==",
+			"dev": true,
+			"requires": {
+				"has": "^1.0.3"
+			}
+		},
+		"is-extglob": {
+			"version": "2.1.1",
+			"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+			"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+			"dev": true
+		},
+		"is-glob": {
+			"version": "4.0.3",
+			"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+			"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+			"dev": true,
+			"requires": {
+				"is-extglob": "^2.1.1"
+			}
+		},
+		"is-hotkey": {
+			"version": "0.2.0",
+			"resolved": "https://registry.npmjs.org/is-hotkey/-/is-hotkey-0.2.0.tgz",
+			"integrity": "sha512-UknnZK4RakDmTgz4PI1wIph5yxSs/mvChWs9ifnlXsKuXgWmOkY/hAE0H/k2MIqH0RlRye0i1oC07MCRSD28Mw=="
+		},
+		"is-number": {
+			"version": "7.0.0",
+			"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+			"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+			"dev": true
+		},
+		"is-path-inside": {
+			"version": "3.0.3",
+			"resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
+			"integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
+			"dev": true
+		},
+		"is-plain-object": {
+			"version": "5.0.0",
+			"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
+			"integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q=="
+		},
+		"is-url": {
+			"version": "1.2.4",
+			"resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz",
+			"integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww=="
+		},
+		"isexe": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+			"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+			"dev": true
+		},
+		"js-cookie": {
+			"version": "3.0.1",
+			"resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.1.tgz",
+			"integrity": "sha512-+0rgsUXZu4ncpPxRL+lNEptWMOWl9etvPHc/koSRp6MPwpRYAhmk0dUG00J4bxVV3r9uUzfo24wW0knS07SKSw=="
+		},
+		"js-sdsl": {
+			"version": "4.3.0",
+			"resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz",
+			"integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==",
+			"dev": true
+		},
+		"js-table2excel": {
+			"version": "1.0.3",
+			"resolved": "https://registry.npmjs.org/js-table2excel/-/js-table2excel-1.0.3.tgz",
+			"integrity": "sha512-ivzOdgYqOD3zqzJZfW0Nm35P9BWffxD0Unwr+2QBeEawV7hhRY9RHBVNcvO6A9PhGkMyqPVL/u4/NeufaTTTXw=="
+		},
+		"js-yaml": {
+			"version": "4.1.0",
+			"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+			"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+			"dev": true,
+			"requires": {
+				"argparse": "^2.0.1"
+			}
+		},
+		"json-schema-traverse": {
+			"version": "0.4.1",
+			"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+			"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+			"dev": true
+		},
+		"json-stable-stringify-without-jsonify": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+			"integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+			"dev": true
+		},
+		"jsplumb": {
+			"version": "2.15.6",
+			"resolved": "https://registry.npmjs.org/jsplumb/-/jsplumb-2.15.6.tgz",
+			"integrity": "sha512-sIpbpz5eMVM+vV+MQzFCidlaa1RsknrQs6LOTKYDjYUDdTAi2AN2bFi94TxB33TifcIsRNV1jebcaxg0tCoPzg=="
+		},
+		"levn": {
+			"version": "0.4.1",
+			"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+			"integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+			"dev": true,
+			"requires": {
+				"prelude-ls": "^1.2.1",
+				"type-check": "~0.4.0"
+			}
+		},
+		"lilconfig": {
+			"version": "2.0.6",
+			"resolved": "https://registry.npmmirror.com/lilconfig/-/lilconfig-2.0.6.tgz",
+			"integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==",
+			"dev": true
+		},
+		"locate-path": {
+			"version": "6.0.0",
+			"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+			"integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+			"dev": true,
+			"requires": {
+				"p-locate": "^5.0.0"
+			}
+		},
+		"lodash": {
+			"version": "4.17.21",
+			"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+			"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
+		},
+		"lodash-es": {
+			"version": "4.17.21",
+			"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
+			"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
+		},
+		"lodash-unified": {
+			"version": "1.0.3",
+			"resolved": "https://registry.npmjs.org/lodash-unified/-/lodash-unified-1.0.3.tgz",
+			"integrity": "sha512-WK9qSozxXOD7ZJQlpSqOT+om2ZfcT4yO+03FuzAHD0wF6S0l0090LRPDx3vhTTLZ8cFKpBn+IOcVXK6qOcIlfQ==",
+			"requires": {}
+		},
+		"lodash.camelcase": {
+			"version": "4.3.0",
+			"resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
+			"integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA=="
+		},
+		"lodash.clonedeep": {
+			"version": "4.5.0",
+			"resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
+			"integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ=="
+		},
+		"lodash.debounce": {
+			"version": "4.0.8",
+			"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
+			"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="
+		},
+		"lodash.foreach": {
+			"version": "4.5.0",
+			"resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz",
+			"integrity": "sha512-aEXTF4d+m05rVOAUG3z4vZZ4xVexLKZGF0lIxuHZ1Hplpk/3B6Z1+/ICICYRLm7c41Z2xiejbkCkJoTlypoXhQ=="
+		},
+		"lodash.isequal": {
+			"version": "4.5.0",
+			"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
+			"integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ=="
+		},
+		"lodash.merge": {
+			"version": "4.6.2",
+			"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+			"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+			"dev": true
+		},
+		"lodash.throttle": {
+			"version": "4.1.1",
+			"resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
+			"integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ=="
+		},
+		"lodash.toarray": {
+			"version": "4.4.0",
+			"resolved": "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz",
+			"integrity": "sha512-QyffEA3i5dma5q2490+SgCvDN0pXLmRGSyAANuVi0HQ01Pkfr9fuoKQW8wm1wGBnJITs/mS7wQvS6VshUEBFCw=="
+		},
+		"lru-cache": {
+			"version": "6.0.0",
+			"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+			"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+			"dev": true,
+			"requires": {
+				"yallist": "^4.0.0"
+			}
+		},
+		"magic-string": {
+			"version": "0.25.9",
+			"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz",
+			"integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==",
+			"requires": {
+				"sourcemap-codec": "^1.4.8"
+			}
+		},
+		"memoize-one": {
+			"version": "6.0.0",
+			"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz",
+			"integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw=="
+		},
+		"merge2": {
+			"version": "1.4.1",
+			"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+			"integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+			"dev": true
+		},
+		"micromatch": {
+			"version": "4.0.5",
+			"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
+			"integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
+			"dev": true,
+			"requires": {
+				"braces": "^3.0.2",
+				"picomatch": "^2.3.1"
+			}
+		},
+		"mime-db": {
+			"version": "1.52.0",
+			"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+			"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
+		},
+		"mime-match": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/mime-match/-/mime-match-1.0.2.tgz",
+			"integrity": "sha512-VXp/ugGDVh3eCLOBCiHZMYWQaTNUHv2IJrut+yXA6+JbLPXHglHwfS/5A5L0ll+jkCY7fIzRJcH6OIunF+c6Cg==",
+			"requires": {
+				"wildcard": "^1.1.0"
+			}
+		},
+		"mime-types": {
+			"version": "2.1.35",
+			"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+			"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+			"requires": {
+				"mime-db": "1.52.0"
+			}
+		},
+		"minimatch": {
+			"version": "3.1.2",
+			"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+			"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+			"dev": true,
+			"requires": {
+				"brace-expansion": "^1.1.7"
+			}
+		},
+		"minimist": {
+			"version": "1.2.8",
+			"resolved": "https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz",
+			"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+			"dev": true
+		},
+		"mitt": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.0.tgz",
+			"integrity": "sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ=="
+		},
+		"ms": {
+			"version": "2.1.2",
+			"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+			"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+			"dev": true
+		},
+		"namespace-emitter": {
+			"version": "2.0.1",
+			"resolved": "https://registry.npmjs.org/namespace-emitter/-/namespace-emitter-2.0.1.tgz",
+			"integrity": "sha512-N/sMKHniSDJBjfrkbS/tpkPj4RAbvW3mr8UAzvlMHyun93XEm83IAvhWtJVHo+RHn/oO8Job5YN4b+wRjSVp5g=="
+		},
+		"nanoid": {
+			"version": "3.3.4",
+			"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
+			"integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw=="
+		},
+		"natural-compare": {
+			"version": "1.4.0",
+			"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+			"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+			"dev": true
+		},
+		"natural-compare-lite": {
+			"version": "1.4.0",
+			"resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz",
+			"integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==",
+			"dev": true
+		},
+		"next-tick": {
+			"version": "1.1.0",
+			"resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz",
+			"integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ=="
+		},
+		"node-releases": {
+			"version": "2.0.10",
+			"resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.10.tgz",
+			"integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==",
+			"dev": true
+		},
+		"normalize-path": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+			"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+			"dev": true
+		},
+		"normalize-range": {
+			"version": "0.1.2",
+			"resolved": "https://registry.npmmirror.com/normalize-range/-/normalize-range-0.1.2.tgz",
+			"integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==",
+			"dev": true
+		},
+		"normalize-wheel-es": {
+			"version": "1.2.0",
+			"resolved": "https://registry.npmjs.org/normalize-wheel-es/-/normalize-wheel-es-1.2.0.tgz",
+			"integrity": "sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw=="
+		},
+		"nprogress": {
+			"version": "0.2.0",
+			"resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz",
+			"integrity": "sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA=="
+		},
+		"nth-check": {
+			"version": "2.1.1",
+			"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
+			"integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
+			"dev": true,
+			"requires": {
+				"boolbase": "^1.0.0"
+			}
+		},
+		"object-hash": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmmirror.com/object-hash/-/object-hash-3.0.0.tgz",
+			"integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
+			"dev": true
+		},
+		"object-inspect": {
+			"version": "1.12.3",
+			"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz",
+			"integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g=="
+		},
+		"once": {
+			"version": "1.4.0",
+			"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+			"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+			"dev": true,
+			"requires": {
+				"wrappy": "1"
+			}
+		},
+		"optionator": {
+			"version": "0.9.1",
+			"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
+			"integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==",
+			"dev": true,
+			"requires": {
+				"deep-is": "^0.1.3",
+				"fast-levenshtein": "^2.0.6",
+				"levn": "^0.4.1",
+				"prelude-ls": "^1.2.1",
+				"type-check": "^0.4.0",
+				"word-wrap": "^1.2.3"
+			}
+		},
+		"p-limit": {
+			"version": "3.1.0",
+			"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+			"integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+			"dev": true,
+			"requires": {
+				"yocto-queue": "^0.1.0"
+			}
+		},
+		"p-locate": {
+			"version": "5.0.0",
+			"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+			"integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+			"dev": true,
+			"requires": {
+				"p-limit": "^3.0.2"
+			}
+		},
+		"parent-module": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+			"integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+			"dev": true,
+			"requires": {
+				"callsites": "^3.0.0"
+			}
+		},
+		"path-exists": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+			"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+			"dev": true
+		},
+		"path-is-absolute": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+			"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+			"dev": true
+		},
+		"path-key": {
+			"version": "3.1.1",
+			"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+			"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+			"dev": true
+		},
+		"path-parse": {
+			"version": "1.0.7",
+			"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+			"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+			"dev": true
+		},
+		"path-type": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
+			"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+			"dev": true
+		},
+		"picocolors": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
+			"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
+		},
+		"picomatch": {
+			"version": "2.3.1",
+			"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+			"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+			"dev": true
+		},
+		"pify": {
+			"version": "2.3.0",
+			"resolved": "https://registry.npmmirror.com/pify/-/pify-2.3.0.tgz",
+			"integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
+			"dev": true
+		},
+		"pinia": {
+			"version": "2.0.32",
+			"resolved": "https://registry.npmjs.org/pinia/-/pinia-2.0.32.tgz",
+			"integrity": "sha512-8Tw4OrpCSJ028UUyp0gYPP/wyjigLoEceuO/x1G+FlHVf73337e5vLm4uDmrRIoBG1hvaed/eSHnrCFjOc4nkA==",
+			"requires": {
+				"@vue/devtools-api": "^6.5.0",
+				"vue-demi": "*"
+			},
+			"dependencies": {
+				"vue-demi": {
+					"version": "0.13.11",
+					"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.13.11.tgz",
+					"integrity": "sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==",
+					"requires": {}
+				}
+			}
+		},
+		"postcss": {
+			"version": "8.4.21",
+			"resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.4.21.tgz",
+			"integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==",
+			"requires": {
+				"nanoid": "^3.3.4",
+				"picocolors": "^1.0.0",
+				"source-map-js": "^1.0.2"
+			}
+		},
+		"postcss-import": {
+			"version": "14.1.0",
+			"resolved": "https://registry.npmmirror.com/postcss-import/-/postcss-import-14.1.0.tgz",
+			"integrity": "sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==",
+			"dev": true,
+			"requires": {
+				"postcss-value-parser": "^4.0.0",
+				"read-cache": "^1.0.0",
+				"resolve": "^1.1.7"
+			}
+		},
+		"postcss-js": {
+			"version": "4.0.1",
+			"resolved": "https://registry.npmmirror.com/postcss-js/-/postcss-js-4.0.1.tgz",
+			"integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==",
+			"dev": true,
+			"requires": {
+				"camelcase-css": "^2.0.1"
+			}
+		},
+		"postcss-load-config": {
+			"version": "3.1.4",
+			"resolved": "https://registry.npmmirror.com/postcss-load-config/-/postcss-load-config-3.1.4.tgz",
+			"integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==",
+			"dev": true,
+			"requires": {
+				"lilconfig": "^2.0.5",
+				"yaml": "^1.10.2"
+			}
+		},
+		"postcss-nested": {
+			"version": "6.0.0",
+			"resolved": "https://registry.npmmirror.com/postcss-nested/-/postcss-nested-6.0.0.tgz",
+			"integrity": "sha512-0DkamqrPcmkBDsLn+vQDIrtkSbNkv5AD/M322ySo9kqFkCIYklym2xEmWkwo+Y3/qZo34tzEPNUw4y7yMCdv5w==",
+			"dev": true,
+			"requires": {
+				"postcss-selector-parser": "^6.0.10"
+			}
+		},
+		"postcss-selector-parser": {
+			"version": "6.0.11",
+			"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz",
+			"integrity": "sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g==",
+			"dev": true,
+			"requires": {
+				"cssesc": "^3.0.0",
+				"util-deprecate": "^1.0.2"
+			}
+		},
+		"postcss-value-parser": {
+			"version": "4.2.0",
+			"resolved": "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
+			"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
+			"dev": true
+		},
+		"preact": {
+			"version": "10.12.1",
+			"resolved": "https://registry.npmjs.org/preact/-/preact-10.12.1.tgz",
+			"integrity": "sha512-l8386ixSsBdbreOAkqtrwqHwdvR35ID8c3rKPa8lCWuO86dBi32QWHV4vfsZK1utLLFMvw+Z5Ad4XLkZzchscg=="
+		},
+		"prelude-ls": {
+			"version": "1.2.1",
+			"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+			"integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+			"dev": true
+		},
+		"prettier": {
+			"version": "2.8.4",
+			"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.4.tgz",
+			"integrity": "sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==",
+			"dev": true
+		},
+		"print-js": {
+			"version": "1.6.0",
+			"resolved": "https://registry.npmjs.org/print-js/-/print-js-1.6.0.tgz",
+			"integrity": "sha512-BfnOIzSKbqGRtO4o0rnj/K3681BSd2QUrsIZy/+WdCIugjIswjmx3lDEZpXB2ruGf9d4b3YNINri81+J0FsBWg=="
+		},
+		"prismjs": {
+			"version": "1.29.0",
+			"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz",
+			"integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q=="
+		},
+		"proxy-from-env": {
+			"version": "1.1.0",
+			"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+			"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
+		},
+		"punycode": {
+			"version": "2.3.0",
+			"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz",
+			"integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==",
+			"dev": true
+		},
+		"qrcodejs2-fixes": {
+			"version": "0.0.2",
+			"resolved": "https://registry.npmjs.org/qrcodejs2-fixes/-/qrcodejs2-fixes-0.0.2.tgz",
+			"integrity": "sha512-wMUXYMOixAEJlLnjk5MbLiFaz0gQObWYm/TIFWB5+j7sTY5gPyr09Cx1EpcLYbsgfFdN3wHjrKAhZofTuCBGhg=="
+		},
+		"qs": {
+			"version": "6.11.0",
+			"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
+			"integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
+			"requires": {
+				"side-channel": "^1.0.4"
+			}
+		},
+		"queue-microtask": {
+			"version": "1.2.3",
+			"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+			"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+			"dev": true
+		},
+		"quick-lru": {
+			"version": "5.1.1",
+			"resolved": "https://registry.npmmirror.com/quick-lru/-/quick-lru-5.1.1.tgz",
+			"integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==",
+			"dev": true
+		},
+		"read-cache": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmmirror.com/read-cache/-/read-cache-1.0.0.tgz",
+			"integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==",
+			"dev": true,
+			"requires": {
+				"pify": "^2.3.0"
+			}
+		},
+		"readdirp": {
+			"version": "3.6.0",
+			"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+			"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+			"dev": true,
+			"requires": {
+				"picomatch": "^2.2.1"
+			}
+		},
+		"regenerator-runtime": {
+			"version": "0.13.11",
+			"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
+			"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg=="
+		},
+		"regexpp": {
+			"version": "3.2.0",
+			"resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz",
+			"integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==",
+			"dev": true
+		},
+		"resolve": {
+			"version": "1.22.1",
+			"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
+			"integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
+			"dev": true,
+			"requires": {
+				"is-core-module": "^2.9.0",
+				"path-parse": "^1.0.7",
+				"supports-preserve-symlinks-flag": "^1.0.0"
+			}
+		},
+		"resolve-from": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+			"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+			"dev": true
+		},
+		"reusify": {
+			"version": "1.0.4",
+			"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
+			"integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+			"dev": true
+		},
+		"rimraf": {
+			"version": "3.0.2",
+			"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+			"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+			"dev": true,
+			"requires": {
+				"glob": "^7.1.3"
+			}
+		},
+		"rollup": {
+			"version": "3.17.2",
+			"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.17.2.tgz",
+			"integrity": "sha512-qMNZdlQPCkWodrAZ3qnJtvCAl4vpQ8q77uEujVCCbC/6CLB7Lcmvjq7HyiOSnf4fxTT9XgsE36oLHJBH49xjqA==",
+			"dev": true,
+			"requires": {
+				"fsevents": "~2.3.2"
+			}
+		},
+		"run-parallel": {
+			"version": "1.2.0",
+			"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+			"integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+			"dev": true,
+			"requires": {
+				"queue-microtask": "^1.2.2"
+			}
+		},
+		"sass": {
+			"version": "1.58.3",
+			"resolved": "https://registry.npmjs.org/sass/-/sass-1.58.3.tgz",
+			"integrity": "sha512-Q7RaEtYf6BflYrQ+buPudKR26/lH+10EmO9bBqbmPh/KeLqv8bjpTNqxe71ocONqXq+jYiCbpPUmQMS+JJPk4A==",
+			"dev": true,
+			"requires": {
+				"chokidar": ">=3.0.0 <4.0.0",
+				"immutable": "^4.0.0",
+				"source-map-js": ">=0.6.2 <2.0.0"
+			}
+		},
+		"screenfull": {
+			"version": "6.0.2",
+			"resolved": "https://registry.npmjs.org/screenfull/-/screenfull-6.0.2.tgz",
+			"integrity": "sha512-AQdy8s4WhNvUZ6P8F6PB21tSPIYKniic+Ogx0AacBMjKP1GUHN2E9URxQHtCusiwxudnCKkdy4GrHXPPJSkCCw=="
+		},
+		"scroll-into-view-if-needed": {
+			"version": "2.2.31",
+			"resolved": "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.31.tgz",
+			"integrity": "sha512-dGCXy99wZQivjmjIqihaBQNjryrz5rueJY7eHfTdyWEiR4ttYpsajb14rn9s5d4DY4EcY6+4+U/maARBXJedkA==",
+			"requires": {
+				"compute-scroll-into-view": "^1.0.20"
+			}
+		},
+		"select": {
+			"version": "1.1.2",
+			"resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz",
+			"integrity": "sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA=="
+		},
+		"semver": {
+			"version": "7.3.8",
+			"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
+			"integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
+			"dev": true,
+			"requires": {
+				"lru-cache": "^6.0.0"
+			}
+		},
+		"shebang-command": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+			"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+			"dev": true,
+			"requires": {
+				"shebang-regex": "^3.0.0"
+			}
+		},
+		"shebang-regex": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+			"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+			"dev": true
+		},
+		"side-channel": {
+			"version": "1.0.4",
+			"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
+			"integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
+			"requires": {
+				"call-bind": "^1.0.0",
+				"get-intrinsic": "^1.0.2",
+				"object-inspect": "^1.9.0"
+			}
+		},
+		"slash": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+			"integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+			"dev": true
+		},
+		"slate": {
+			"version": "0.72.8",
+			"resolved": "https://registry.npmjs.org/slate/-/slate-0.72.8.tgz",
+			"integrity": "sha512-/nJwTswQgnRurpK+bGJFH1oM7naD5qDmHd89JyiKNT2oOKD8marW0QSBtuFnwEbL5aGCS8AmrhXQgNOsn4osAw==",
+			"requires": {
+				"immer": "^9.0.6",
+				"is-plain-object": "^5.0.0",
+				"tiny-warning": "^1.0.3"
+			}
+		},
+		"slate-history": {
+			"version": "0.66.0",
+			"resolved": "https://registry.npmjs.org/slate-history/-/slate-history-0.66.0.tgz",
+			"integrity": "sha512-6MWpxGQZiMvSINlCbMW43E2YBSVMCMCIwQfBzGssjWw4kb0qfvj0pIdblWNRQZD0hR6WHP+dHHgGSeVdMWzfng==",
+			"requires": {
+				"is-plain-object": "^5.0.0"
+			}
+		},
+		"snabbdom": {
+			"version": "3.5.1",
+			"resolved": "https://registry.npmjs.org/snabbdom/-/snabbdom-3.5.1.tgz",
+			"integrity": "sha512-wHMNIOjkm/YNE5EM3RCbr/+DVgPg6AqQAX1eOxO46zYNvCXjKP5Y865tqQj3EXnaMBjkxmQA5jFuDpDK/dbfiA=="
+		},
+		"sortablejs": {
+			"version": "1.15.0",
+			"resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.15.0.tgz",
+			"integrity": "sha512-bv9qgVMjUMf89wAvM6AxVvS/4MX3sPeN0+agqShejLU5z5GX4C75ow1O2e5k4L6XItUyAK3gH6AxSbXrOM5e8w=="
+		},
+		"source-map": {
+			"version": "0.6.1",
+			"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+			"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
+		},
+		"source-map-js": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
+			"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw=="
+		},
+		"sourcemap-codec": {
+			"version": "1.4.8",
+			"resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
+			"integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA=="
+		},
+		"splitpanes": {
+			"version": "3.1.5",
+			"resolved": "https://registry.npmjs.org/splitpanes/-/splitpanes-3.1.5.tgz",
+			"integrity": "sha512-r3Mq2ITFQ5a2VXLOy4/Sb2Ptp7OfEO8YIbhVJqJXoFc9hc5nTXXkCvtVDjIGbvC0vdE7tse+xTM9BMjsszP6bw=="
+		},
+		"ssr-window": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/ssr-window/-/ssr-window-3.0.0.tgz",
+			"integrity": "sha512-q+8UfWDg9Itrg0yWK7oe5p/XRCJpJF9OBtXfOPgSJl+u3Xd5KI328RUEvUqSMVM9CiQUEf1QdBzJMkYGErj9QA=="
+		},
+		"strip-ansi": {
+			"version": "6.0.1",
+			"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+			"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+			"dev": true,
+			"requires": {
+				"ansi-regex": "^5.0.1"
+			}
+		},
+		"strip-json-comments": {
+			"version": "3.1.1",
+			"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+			"integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+			"dev": true
+		},
+		"supports-color": {
+			"version": "7.2.0",
+			"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+			"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+			"dev": true,
+			"requires": {
+				"has-flag": "^4.0.0"
+			}
+		},
+		"supports-preserve-symlinks-flag": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+			"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+			"dev": true
+		},
+		"tailwindcss": {
+			"version": "3.2.7",
+			"resolved": "https://registry.npmmirror.com/tailwindcss/-/tailwindcss-3.2.7.tgz",
+			"integrity": "sha512-B6DLqJzc21x7wntlH/GsZwEXTBttVSl1FtCzC8WP4oBc/NKef7kaax5jeihkkCEWc831/5NDJ9gRNDK6NEioQQ==",
+			"dev": true,
+			"requires": {
+				"arg": "^5.0.2",
+				"chokidar": "^3.5.3",
+				"color-name": "^1.1.4",
+				"detective": "^5.2.1",
+				"didyoumean": "^1.2.2",
+				"dlv": "^1.1.3",
+				"fast-glob": "^3.2.12",
+				"glob-parent": "^6.0.2",
+				"is-glob": "^4.0.3",
+				"lilconfig": "^2.0.6",
+				"micromatch": "^4.0.5",
+				"normalize-path": "^3.0.0",
+				"object-hash": "^3.0.0",
+				"picocolors": "^1.0.0",
+				"postcss": "^8.0.9",
+				"postcss-import": "^14.1.0",
+				"postcss-js": "^4.0.0",
+				"postcss-load-config": "^3.1.4",
+				"postcss-nested": "6.0.0",
+				"postcss-selector-parser": "^6.0.11",
+				"postcss-value-parser": "^4.2.0",
+				"quick-lru": "^5.1.1",
+				"resolve": "^1.22.1"
+			}
+		},
+		"text-table": {
+			"version": "0.2.0",
+			"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+			"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
+			"dev": true
+		},
+		"tiny-emitter": {
+			"version": "2.1.0",
+			"resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
+			"integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q=="
+		},
+		"tiny-warning": {
+			"version": "1.0.3",
+			"resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz",
+			"integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA=="
+		},
+		"to-regex-range": {
+			"version": "5.0.1",
+			"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+			"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+			"dev": true,
+			"requires": {
+				"is-number": "^7.0.0"
+			}
+		},
+		"ts-enum-util": {
+			"version": "4.0.2",
+			"resolved": "https://registry.npmmirror.com/ts-enum-util/-/ts-enum-util-4.0.2.tgz",
+			"integrity": "sha512-BB5qjvHYgYgOB/CaoA1Cy/B2QNnZ+nVBrJ15VV/AXGWx+AO83k5wgeLOJvkSLoKKavvH/M8Wj4ZbgROjsuYwzw=="
+		},
+		"tslib": {
+			"version": "2.3.0",
+			"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz",
+			"integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg=="
+		},
+		"tsutils": {
+			"version": "3.21.0",
+			"resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz",
+			"integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==",
+			"dev": true,
+			"requires": {
+				"tslib": "^1.8.1"
+			},
+			"dependencies": {
+				"tslib": {
+					"version": "1.14.1",
+					"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+					"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+					"dev": true
+				}
+			}
+		},
+		"type": {
+			"version": "1.2.0",
+			"resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
+			"integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg=="
+		},
+		"type-check": {
+			"version": "0.4.0",
+			"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+			"integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+			"dev": true,
+			"requires": {
+				"prelude-ls": "^1.2.1"
+			}
+		},
+		"type-fest": {
+			"version": "0.20.2",
+			"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+			"integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+			"dev": true
+		},
+		"typescript": {
+			"version": "4.9.5",
+			"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
+			"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
+			"devOptional": true
+		},
+		"update-browserslist-db": {
+			"version": "1.0.10",
+			"resolved": "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz",
+			"integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==",
+			"dev": true,
+			"requires": {
+				"escalade": "^3.1.1",
+				"picocolors": "^1.0.0"
+			}
+		},
+		"uri-js": {
+			"version": "4.4.1",
+			"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+			"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+			"dev": true,
+			"requires": {
+				"punycode": "^2.1.0"
+			}
+		},
+		"util-deprecate": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+			"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+			"dev": true
+		},
+		"vite": {
+			"version": "4.1.4",
+			"resolved": "https://registry.npmjs.org/vite/-/vite-4.1.4.tgz",
+			"integrity": "sha512-3knk/HsbSTKEin43zHu7jTwYWv81f8kgAL99G5NWBcA1LKvtvcVAC4JjBH1arBunO9kQka+1oGbrMKOjk4ZrBg==",
+			"dev": true,
+			"requires": {
+				"esbuild": "^0.16.14",
+				"fsevents": "~2.3.2",
+				"postcss": "^8.4.21",
+				"resolve": "^1.22.1",
+				"rollup": "^3.10.0"
+			}
+		},
+		"vite-plugin-vue-setup-extend": {
+			"version": "0.4.0",
+			"resolved": "https://registry.npmjs.org/vite-plugin-vue-setup-extend/-/vite-plugin-vue-setup-extend-0.4.0.tgz",
+			"integrity": "sha512-WMbjPCui75fboFoUTHhdbXzu4Y/bJMv5N9QT9a7do3wNMNHHqrk+Tn2jrSJU0LS5fGl/EG+FEDBYVUeWIkDqXQ==",
+			"dev": true,
+			"requires": {
+				"@vue/compiler-sfc": "^3.2.29",
+				"magic-string": "^0.25.7"
+			}
+		},
+		"vue": {
+			"version": "3.2.47",
+			"resolved": "https://registry.npmjs.org/vue/-/vue-3.2.47.tgz",
+			"integrity": "sha512-60188y/9Dc9WVrAZeUVSDxRQOZ+z+y5nO2ts9jWXSTkMvayiWxCWOWtBQoYjLeccfXkiiPZWAHcV+WTPhkqJHQ==",
+			"requires": {
+				"@vue/compiler-dom": "3.2.47",
+				"@vue/compiler-sfc": "3.2.47",
+				"@vue/runtime-dom": "3.2.47",
+				"@vue/server-renderer": "3.2.47",
+				"@vue/shared": "3.2.47"
+			}
+		},
+		"vue-clipboard3": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/vue-clipboard3/-/vue-clipboard3-2.0.0.tgz",
+			"integrity": "sha512-Q9S7dzWGax7LN5iiSPcu/K1GGm2gcBBlYwmMsUc5/16N6w90cbKow3FnPmPs95sungns4yvd9/+JhbAznECS2A==",
+			"requires": {
+				"clipboard": "^2.0.6"
+			}
+		},
+		"vue-eslint-parser": {
+			"version": "9.1.0",
+			"resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.1.0.tgz",
+			"integrity": "sha512-NGn/iQy8/Wb7RrRa4aRkokyCZfOUWk19OP5HP6JEozQFX5AoS/t+Z0ZN7FY4LlmWc4FNI922V7cvX28zctN8dQ==",
+			"dev": true,
+			"requires": {
+				"debug": "^4.3.4",
+				"eslint-scope": "^7.1.1",
+				"eslint-visitor-keys": "^3.3.0",
+				"espree": "^9.3.1",
+				"esquery": "^1.4.0",
+				"lodash": "^4.17.21",
+				"semver": "^7.3.6"
+			},
+			"dependencies": {
+				"eslint-scope": {
+					"version": "7.1.1",
+					"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz",
+					"integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==",
+					"dev": true,
+					"requires": {
+						"esrecurse": "^4.3.0",
+						"estraverse": "^5.2.0"
+					}
+				},
+				"estraverse": {
+					"version": "5.3.0",
+					"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+					"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+					"dev": true
+				}
+			}
+		},
+		"vue-grid-layout": {
+			"version": "3.0.0-beta1",
+			"resolved": "https://registry.npmjs.org/vue-grid-layout/-/vue-grid-layout-3.0.0-beta1.tgz",
+			"integrity": "sha512-MsW0yfYNtnAO/uDhfZvkP6effxSJxvhAFbIL37x6Rn3vW9xf0WHVefKaSbQMLpSq3mXnR6ut0pg2Cd5lqIIZzg==",
+			"requires": {
+				"@interactjs/actions": "^1.10.2",
+				"@interactjs/auto-start": "^1.10.2",
+				"@interactjs/dev-tools": "^1.10.2",
+				"@interactjs/interactjs": "^1.10.2",
+				"@interactjs/modifiers": "^1.10.2",
+				"element-resize-detector": "^1.2.1",
+				"mitt": "^2.1.0"
+			},
+			"dependencies": {
+				"mitt": {
+					"version": "2.1.0",
+					"resolved": "https://registry.npmjs.org/mitt/-/mitt-2.1.0.tgz",
+					"integrity": "sha512-ILj2TpLiysu2wkBbWjAmww7TkZb65aiQO+DkVdUTBpBXq+MHYiETENkKFMtsJZX1Lf4pe4QOrTSjIfUwN5lRdg=="
+				}
+			}
+		},
+		"vue-i18n": {
+			"version": "9.2.2",
+			"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.2.2.tgz",
+			"integrity": "sha512-yswpwtj89rTBhegUAv9Mu37LNznyu3NpyLQmozF3i1hYOhwpG8RjcjIFIIfnu+2MDZJGSZPXaKWvnQA71Yv9TQ==",
+			"requires": {
+				"@intlify/core-base": "9.2.2",
+				"@intlify/shared": "9.2.2",
+				"@intlify/vue-devtools": "9.2.2",
+				"@vue/devtools-api": "^6.2.1"
+			}
+		},
+		"vue-router": {
+			"version": "4.1.6",
+			"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.1.6.tgz",
+			"integrity": "sha512-DYWYwsG6xNPmLq/FmZn8Ip+qrhFEzA14EI12MsMgVxvHFDYvlr4NXpVF5hrRH1wVcDP8fGi5F4rxuJSl8/r+EQ==",
+			"requires": {
+				"@vue/devtools-api": "^6.4.5"
+			}
+		},
+		"vuedraggable": {
+			"version": "4.1.0",
+			"resolved": "https://registry.npmmirror.com/vuedraggable/-/vuedraggable-4.1.0.tgz",
+			"integrity": "sha512-FU5HCWBmsf20GpP3eudURW3WdWTKIbEIQxh9/8GE806hydR9qZqRRxRE3RjqX7PkuLuMQG/A7n3cfj9rCEchww==",
+			"requires": {
+				"sortablejs": "1.14.0"
+			},
+			"dependencies": {
+				"sortablejs": {
+					"version": "1.14.0",
+					"resolved": "https://registry.npmmirror.com/sortablejs/-/sortablejs-1.14.0.tgz",
+					"integrity": "sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w=="
+				}
+			}
+		},
+		"which": {
+			"version": "2.0.2",
+			"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+			"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+			"dev": true,
+			"requires": {
+				"isexe": "^2.0.0"
+			}
+		},
+		"wildcard": {
+			"version": "1.1.2",
+			"resolved": "https://registry.npmjs.org/wildcard/-/wildcard-1.1.2.tgz",
+			"integrity": "sha512-DXukZJxpHA8LuotRwL0pP1+rS6CS7FF2qStDDE1C7DDg2rLud2PXRMuEDYIPhgEezwnlHNL4c+N6MfMTjCGTng=="
+		},
+		"word-wrap": {
+			"version": "1.2.3",
+			"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
+			"integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
+			"dev": true
+		},
+		"wrappy": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+			"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+			"dev": true
+		},
+		"xml-name-validator": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz",
+			"integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==",
+			"dev": true
+		},
+		"xtend": {
+			"version": "4.0.2",
+			"resolved": "https://registry.npmmirror.com/xtend/-/xtend-4.0.2.tgz",
+			"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
+			"dev": true
+		},
+		"yallist": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+			"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+			"dev": true
+		},
+		"yaml": {
+			"version": "1.10.2",
+			"resolved": "https://registry.npmmirror.com/yaml/-/yaml-1.10.2.tgz",
+			"integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
+			"dev": true
+		},
+		"yocto-queue": {
+			"version": "0.1.0",
+			"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+			"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+			"dev": true
+		},
+		"zrender": {
+			"version": "5.4.1",
+			"resolved": "https://registry.npmjs.org/zrender/-/zrender-5.4.1.tgz",
+			"integrity": "sha512-M4Z05BHWtajY2241EmMPHglDQAJ1UyHQcYsxDNzD9XLSkPDqMq4bB28v9Pb4mvHnVQ0GxyTklZ/69xCFP6RXBA==",
+			"requires": {
+				"tslib": "2.3.0"
+			}
+		}
+	}
+}
Added +90 -0
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..32be4ba
--- /dev/null
+++ b/package.json
@@ -0,0 +1,90 @@
+{
+	"name": "vue-next-admin",
+	"version": "2.4.3",
+	"description": "vue3 vite next admin template",
+	"author": "lyt_20201208",
+	"license": "MIT",
+	"scripts": {
+		"dev": "vite --force",
+		"build": "vite build",
+		"lint-fix": "eslint --fix --ext .js --ext .jsx --ext .vue src/"
+	},
+	"dependencies": {
+		"@element-plus/icons-vue": "^2.0.10",
+		"@form-create/element-ui": "^3.1.18",
+		"@wangeditor/editor": "^5.1.23",
+		"@wangeditor/editor-for-vue": "^5.1.12",
+		"axios": "^1.3.3",
+		"countup.js": "^2.4.2",
+		"cropperjs": "^1.5.13",
+		"echarts": "^5.4.1",
+		"echarts-gl": "^2.0.9",
+		"echarts-wordcloud": "^2.1.0",
+		"element-plus": "^2.2.32",
+		"js-cookie": "^3.0.1",
+		"js-table2excel": "^1.0.3",
+		"jsplumb": "^2.15.6",
+		"mitt": "^3.0.0",
+		"nprogress": "^0.2.0",
+		"pinia": "^2.0.32",
+		"print-js": "^1.6.0",
+		"qrcodejs2-fixes": "^0.0.2",
+		"qs": "^6.11.0",
+		"screenfull": "^6.0.2",
+		"sortablejs": "^1.15.0",
+		"splitpanes": "^3.1.5",
+		"ts-enum-util": "^4.0.2",
+		"vue": "^3.2.47",
+		"vue-clipboard3": "^2.0.0",
+		"vue-grid-layout": "^3.0.0-beta1",
+		"vue-i18n": "^9.2.2",
+		"vue-router": "^4.1.6",
+		"vuedraggable": "^4.1.0"
+	},
+	"devDependencies": {
+		"@iconify/vue": "^4.1.0",
+		"@types/node": "^18.14.0",
+		"@types/nprogress": "^0.2.0",
+		"@types/sortablejs": "^1.15.0",
+		"@typescript-eslint/eslint-plugin": "^5.53.0",
+		"@typescript-eslint/parser": "^5.53.0",
+		"@vitejs/plugin-vue": "^4.0.0",
+		"@vue/compiler-sfc": "^3.2.47",
+		"autoprefixer": "^10.4.13",
+		"eslint": "^8.34.0",
+		"eslint-plugin-vue": "^9.9.0",
+		"postcss": "^8.4.21",
+		"prettier": "^2.8.4",
+		"sass": "^1.58.3",
+		"tailwindcss": "^3.2.7",
+		"typescript": "^4.9.5",
+		"vite": "^4.1.4",
+		"vite-plugin-vue-setup-extend": "^0.4.0",
+		"vue-eslint-parser": "^9.1.0"
+	},
+	"browserslist": [
+		"> 1%",
+		"last 2 versions",
+		"not dead"
+	],
+	"bugs": {
+		"url": "https://gitee.com/lyt-top/vue-next-admin/issues"
+	},
+	"engines": {
+		"node": ">=16.0.0",
+		"npm": ">= 7.0.0"
+	},
+	"keywords": [
+		"vue",
+		"vue3",
+		"vuejs/vue-next",
+		"element-ui",
+		"element-plus",
+		"vue-next-admin",
+		"next-admin"
+	],
+	"repository": {
+		"type": "git",
+		"url": "https://gitee.com/lyt-top/vue-next-admin.git"
+	}
+}
Added +6 -0
diff --git a/postcss.config.js b/postcss.config.js
new file mode 100644
index 0000000..33ad091
--- /dev/null
+++ b/postcss.config.js
@@ -0,0 +1,6 @@
+module.exports = {
+  plugins: {
+    tailwindcss: {},
+    autoprefixer: {},
+  },
+}
Added +0 -0
diff --git a/public/favicon.ico b/public/favicon.ico
new file mode 100644
index 0000000..542c1bc
Binary files /dev/null and b/public/favicon.ico differ
Modified +10 -17
diff --git a/README.md b/README.md
index 53cafa0..d7c1f35 100644
--- a/README.md
+++ b/README.md
@@ -1,23 +1,26 @@
 # NewLife.QuickVue
 
 #### 介绍
+
 前端页面快速开发
 
 #### 软件架构
-软件架构说明
 
+软件架构说明
 
 #### 安装教程
 
-1.  xxxx
-2.  xxxx
-3.  xxxx
+1.  克隆项目到本地
+2.  npm i
+3.  npm run dev
 
 #### 使用说明
 
-1.  xxxx
-2.  xxxx
-3.  xxxx
+1.  从接口读取到的路由默认使用 views/modules/index.vue 文件
+2.  在 views/modules 下面按照路由规则新建文件会自动引用
+3.  可参考 views/modules/admin/user/index 进行页面编写
+4.  page 组件可通过 usePageSetting 函数按照一定的规则进行页面配置
+    若组件不够可以增加组件,对于复杂组件支持使用自定义插槽渲染(通过配置项 slot)
 
 #### 参与贡献
 
@@ -25,13 +28,3 @@
 2.  新建 Feat_xxx 分支
 3.  提交代码
 4.  新建 Pull Request
-
-
-#### 特技
-
-1.  使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md
-2.  Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com)
-3.  你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目
-4.  [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目
-5.  Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help)
-6.  Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
Added +40 -0
diff --git a/src/api/menu.ts b/src/api/menu.ts
new file mode 100644
index 0000000..116b65b
--- /dev/null
+++ b/src/api/menu.ts
@@ -0,0 +1,40 @@
+import { Menu } from '../model/api/menu';
+import { ApiResult } from '/@/model/api/common';
+import { GetMenuTree } from '/@/model/api/login';
+import request from '/@/utils/request';
+
+/**
+ * 以下为模拟接口地址,gitee 的不通,就换自己的真实接口地址
+ *
+ * (不建议写成 request.post(xxx),因为这样 post 时,无法 params 与 data 同时传参)
+ *
+ * 后端控制菜单模拟json,路径在 https://gitee.com/lyt-top/vue-next-admin-images/tree/master/menu
+ * 后端控制路由,isRequestRoutes 为 true,则开启后端控制路由
+ * @method getAdminMenu 获取后端动态路由菜单(admin)
+ * @method getTestMenu 获取后端动态路由菜单(test)
+ */
+export function useMenuApi() {
+	return {
+		getAdminMenu: (params?: object) => {
+			return request<GetMenuTree>({
+				url: '/admin/index/GetMenuTree',
+				method: 'get',
+				params,
+			});
+		},
+		getTestMenu: (params?: object) => {
+			return request<GetMenuTree>({
+				url: '/admin/index/GetMenuTree',
+				method: 'get',
+				params,
+			});
+		},
+		getMenu: (params?: object) => {
+			return request<Menu[]>({
+				url: '/Admin/Index/GetMenuTree',
+				method: 'get',
+				params,
+			});
+		},
+	};
+}
Added +70 -0
diff --git a/src/api/page.ts b/src/api/page.ts
new file mode 100644
index 0000000..8ceeaf2
--- /dev/null
+++ b/src/api/page.ts
@@ -0,0 +1,70 @@
+import { PageProps } from '../model/api/common';
+import { Column } from '../model/api/page';
+import request from '/@/utils/request';
+
+export enum ColumnKind {
+	LIST = 1,
+	DETAIL,
+	ADD,
+	EDIT,
+	SEARCH
+}
+/**
+ * (不建议写成 request.post(xxx),因为这样 post 时,无法 params 与 data 同时传参)
+ *
+ * 登录api接口集合
+ * @method signIn 用户登录
+ * @method signOut 用户退出登录
+ */
+export function usePageApi() {
+	return {
+    getColumns: (type: string, kind: ColumnKind) => {
+			return request<Column[]>({
+				url: `${type}/GetFields`,
+				method: 'get',
+				params: {
+					kind
+				}
+			});
+		},
+		getTableData: <T extends {}>(type: string, params: EmptyObjectType & PageProps) => {
+			return request<T[]>({
+				url: type,
+				method: 'get',
+				params
+			});
+		},
+		getTableDetail: <T extends {}>(type: string, id: number) => {
+			return request<T>({
+				url: type + '/Detail',
+				method: 'get',
+				params: {
+					id
+				}
+			});
+		},
+		setTableItem: (type: string, data: EmptyObjectType) => {
+			return request({
+				url: type,
+				method: 'put',
+				data
+			});
+		},
+		addTableItem: (type: string, data: EmptyObjectType) => {
+			return request({
+				url: type,
+				method: 'post',
+				data
+			});
+		},
+		delTableItem: (type: string, id: number) => {
+			return request({
+				url: type,
+				method: 'delete',
+				params: {
+					id
+				}
+			});
+		},
+	};
+}
\ No newline at end of file
Added +34 -0
diff --git a/src/api/user.ts b/src/api/user.ts
new file mode 100644
index 0000000..6771e22
--- /dev/null
+++ b/src/api/user.ts
@@ -0,0 +1,34 @@
+import { Login, UserInfo } from '../model/api/user';
+import request from '/@/utils/request';
+
+/**
+ * (不建议写成 request.post(xxx),因为这样 post 时,无法 params 与 data 同时传参)
+ *
+ * 登录api接口集合
+ * @method signIn 用户登录
+ * @method signOut 用户退出登录
+ */
+export function useUserApi() {
+	return {
+    signIn: (data: object) => {
+			return request<Login>({
+				url: '/admin/user/login',
+				method: 'post',
+				data,
+			});
+		},
+		signOut: (data: object) => {
+			return request({
+				url: '/user/signOut',
+				method: 'post',
+				data,
+			});
+		},
+		info: () => {
+			return request<UserInfo>({
+				url: '/Admin/User/Info/',
+				method: 'get'
+			});
+		},
+	};
+}
\ No newline at end of file
Added +101 -0
diff --git a/src/App.vue b/src/App.vue
new file mode 100644
index 0000000..ba1ca55
--- /dev/null
+++ b/src/App.vue
@@ -0,0 +1,101 @@
+<template>
+	<el-config-provider :size="getGlobalComponentSize" :locale="getGlobalI18n">
+		<router-view v-show="setLockScreen" />
+		<LockScreen v-if="themeConfig.isLockScreen" />
+		<Setings ref="setingsRef" v-show="setLockScreen" />
+		<CloseFull v-if="!themeConfig.isLockScreen" />
+		<!-- <Upgrade v-if="getVersion" /> -->
+		<!-- <Sponsors /> -->
+	</el-config-provider>
+</template>
+
+<script setup lang="ts" name="app">
+import { defineAsyncComponent, computed, ref, onBeforeMount, onMounted, onUnmounted, nextTick, watch } from 'vue';
+import { useRoute } from 'vue-router';
+import { useI18n } from 'vue-i18n';
+import { storeToRefs } from 'pinia';
+import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
+import { useThemeConfig } from '/@/stores/themeConfig';
+import other from '/@/utils/other';
+import { Local, Session } from '/@/utils/storage';
+import mittBus from '/@/utils/mitt';
+import setIntroduction from '/@/utils/setIconfont';
+
+// 引入组件
+const LockScreen = defineAsyncComponent(() => import('/@/layout/lockScreen/index.vue'));
+const Setings = defineAsyncComponent(() => import('/@/layout/navBars/breadcrumb/setings.vue'));
+const CloseFull = defineAsyncComponent(() => import('/@/layout/navBars/breadcrumb/closeFull.vue'));
+const Upgrade = defineAsyncComponent(() => import('/@/layout/upgrade/index.vue'));
+const Sponsors = defineAsyncComponent(() => import('/@/layout/sponsors/index.vue'));
+
+// 定义变量内容
+const { messages, locale } = useI18n();
+const setingsRef = ref();
+const route = useRoute();
+const stores = useTagsViewRoutes();
+const storesThemeConfig = useThemeConfig();
+const { themeConfig } = storeToRefs(storesThemeConfig);
+
+// 设置锁屏时组件显示隐藏
+const setLockScreen = computed(() => {
+	// 防止锁屏后,刷新出现不相关界面
+	// https://gitee.com/lyt-top/vue-next-admin/issues/I6AF8P
+	return themeConfig.value.isLockScreen ? themeConfig.value.lockScreenTime > 1 : themeConfig.value.lockScreenTime >= 0;
+});
+// 获取版本号
+const getVersion = computed(() => {
+	let isVersion = false;
+	if (route.path !== '/login') {
+		// @ts-ignore
+		if ((Local.get('version') && Local.get('version') !== __NEXT_VERSION__) || !Local.get('version')) isVersion = true;
+	}
+	return isVersion;
+});
+// 获取全局组件大小
+const getGlobalComponentSize = computed(() => {
+	return other.globalComponentSize();
+});
+// 获取全局 i18n
+const getGlobalI18n = computed(() => {
+	return messages.value[locale.value];
+});
+// 设置初始化,防止刷新时恢复默认
+onBeforeMount(() => {
+	// 设置批量第三方 icon 图标
+	setIntroduction.cssCdn();
+	// 设置批量第三方 js
+	setIntroduction.jsCdn();
+});
+// 页面加载时
+onMounted(() => {
+	nextTick(() => {
+		// 监听布局配'置弹窗点击打开
+		mittBus.on('openSetingsDrawer', () => {
+			setingsRef.value.openDrawer();
+		});
+		// 获取缓存中的布局配置
+		if (Local.get('themeConfig')) {
+			storesThemeConfig.setThemeConfig({ themeConfig: Local.get('themeConfig') });
+			document.documentElement.style.cssText = Local.get('themeConfigStyle');
+		}
+		// 获取缓存中的全屏配置
+		if (Session.get('isTagsViewCurrenFull')) {
+			stores.setCurrenFullscreen(Session.get('isTagsViewCurrenFull'));
+		}
+	});
+});
+// 页面销毁时,关闭监听布局配置/i18n监听
+onUnmounted(() => {
+	mittBus.off('openSetingsDrawer', () => {});
+});
+// 监听路由的变化,设置网站标题
+watch(
+	() => route.path,
+	() => {
+		other.useTitle();
+	},
+	{
+		deep: true,
+	}
+);
+</script>
Added +0 -0
diff --git a/src/assets/ccflowRightNextAdmin.png b/src/assets/ccflowRightNextAdmin.png
new file mode 100644
index 0000000..9b44a6e
Binary files /dev/null and b/src/assets/ccflowRightNextAdmin.png differ
Added +19 -0
diff --git a/src/assets/login-bg.svg b/src/assets/login-bg.svg
new file mode 100644
index 0000000..a345a54
--- /dev/null
+++ b/src/assets/login-bg.svg
@@ -0,0 +1,19 @@
+<svg width="100" height="969"
+  xmlns="http://www.w3.org/2000/svg"
+  xmlns:xlink="http://www.w3.org/1999/xlink" shape-rendering="auto" preserveAspectRatio="none" class="layout-footer-waves">
+  <g id="Layer_1">
+    <title>Layer 1</title>
+    <g transform="rotate(2.18686 -4.09974 506.2)" stroke="null" id="svg_5">
+      <defs stroke="null" transform="translate(0.00121616 0.0635973) translate(-0.844727 0.185777) translate(-4.69751 0.0674522) translate(4.80739 0.470252) translate(4.47211 0.0666505) translate(-47.1425 4.28569) translate(7.74305 4.90188) translate(-28.5712 -22.857) translate(-0.219291 4.06319) translate(-1.32311 -4.516) translate(22.5706 18.2109) translate(-49.4342 -5.96675) translate(-0.909089 0) translate(0.909089 0) translate(-0.909089 0) translate(-0.909089 0) translate(-0.909089 0) translate(-3.33335 0) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(-7.34665 -0.396492) translate(-1.66666 0) translate(-1.66666 0) translate(-0.12551 2.32567) translate(-0.707827 0.218105) translate(-1.66666 0) translate(1.66666 0) translate(-1.66666 0) translate(-1.66666 0) translate(-1.66666 0) translate(-1.66666 0) translate(-1.66666 0) translate(-2.15731 0.0264734) translate(1.66666 0) translate(-1.66666 0) translate(-1.66666 0) translate(-1.66666 0) translate(-1.66666 0) translate(-1.66666 0) translate(-1.66666 0) translate(-1.66666 0) translate(-1.66666 0) translate(-1.66666 0) translate(-1.66666 0) translate(-1.66666 0) translate(0 -1.66666) translate(0 -1.66666) translate(1.66666 0) translate(0 -1.66666) translate(1.66666 0) translate(0 -1.66666) translate(0 -1.66666) translate(0 -1.66666) translate(1.66666 0) translate(1.66666 0) translate(1.66666 0) translate(0 -1.66666) translate(0 -1.66666) translate(0 -1.66666) translate(0 -1.66666) translate(0 -1.66666) translate(0 -1.66666) translate(0 -1.66666) translate(0 -1.66666) translate(0 -1.66666) translate(0 -1.66666) translate(0 -1.66666) translate(0 -1.66666) translate(0 -1.66666) translate(0 -1.66666) translate(0 -1.66666) translate(0 -1.66666) translate(0 -1.66666) translate(0 -1.66666) translate(0 -1.66666) translate(0 -1.66666) translate(0 -1.66666) translate(0 -1.66666) translate(0 -1.66666) translate(0 -1.66666) translate(0 -1.66666) translate(1.66666 0) translate(1.66666 0) translate(1.66666 0) translate(1.66666 0) translate(1.66666 0) translate(1.66666 0) translate(1.66666 0) translate(1.66666 0) translate(1.66666 0) translate(1.66666 0) translate(1.66666 0) translate(1.66666 0) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(-1.66666 0) translate(-1.66666 0) translate(-1.66666 0) translate(-1.66666 0) translate(0 -1.66666) translate(0 -1.66666) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(-1.66666 0) translate(-1.66666 0) translate(-1.66666 0) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(-1.66666 0) translate(-1.66666 0) translate(-1.66666 0) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(-1.66666 0) translate(0 1.66666) translate(-1.66666 0) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(-1.66666 0) translate(-1.66666 0) translate(-1.66666 0) translate(-1.66666 0) translate(0 -1.66666) translate(0 -1.66666) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(0 -1.66666) translate(0 -1.66666) translate(0 -1.66666) translate(-1.66666 0) translate(-1.66666 0) translate(-1.66666 0) translate(-1.66666 0) translate(-1.66666 0) translate(-1.66666 0) translate(-1.66666 0) translate(-1.66666 0) translate(-1.66666 0) translate(-1.56355 1.02885) translate(-7.50004 -25.0001) translate(-3.19245 0.846904) translate(5.00003 -97.5005) translate(-9.62431 -0.829817) translate(27.5002 -220.001) translate(-6.96615 1.62281) translate(1.10472 -2.23004) translate(-9.63847 0.899196) translate(7.04019 0.0560654) translate(-11.25 -16.25) translate(2.11523 2.02597) translate(-27.1193 -20.3394) translate(-10.8798 -0.397069) translate(-44.0688 -110.172) translate(-1.69495 5.08486) translate(-8.57191 2.416) translate(-67.7982 -69.4931) translate(-0.127168 3.4846) translate(-16.9495 -283.057) translate(-4.64027 -0.169348) translate(1 0) translate(71.5022 0) translate(0 127.337) translate(-78.8263 -168.947) scale(0.564803 0.937256) translate(78.8263 168.947) translate(-139.564 1126.64) scale(1 0.752632) translate(139.564 -1126.64) translate(-139.564 190.036) scale(1 0.902375) translate(139.564 -190.036) translate(-139.564 210.595) scale(1.30003 1) translate(139.564 -210.595) translate(-107.355 1517.49) scale(1 1.16096) translate(107.355 -1517.49) translate(-107.355 0.205247) scale(1 1.13183) translate(107.355 -0.205247) translate(-136.758 -338.436) scale(1.50148 1) translate(136.758 338.436) translate(-152.694 -417.354) scale(1.2612 1.21266) translate(152.694 417.354) translate(-160.148 -445.389) scale(1 1.22519) translate(160.148 445.389) translate(-187.477 -380.211) scale(1.1479 0.963065) translate(187.477 380.211) translate(-169.043 -406.673) scale(0.986706 0.876627) translate(169.043 406.673) translate(-166.85 -463.854) scale(1.08382 1.19196) translate(166.85 463.854) translate(-159.592 918.456) scale(0.859634 1.01716) translate(159.592 -918.456) translate(-184.899 -405.678) scale(1.135 1.11385) translate(184.899 405.678) translate(-150.577 -518.346) scale(0.986699 1.09676) translate(150.577 518.346) translate(-155.421 -535.896) scale(1.04426 1.02871) translate(155.421 535.896) translate(-155.064 -536.049) scale(1.0463 1.01338) translate(155.064 536.049) translate(-165.783 -521.136) scale(1.00568 1.01887) translate(165.783 521.136) translate(-170.657 -511.469) scale(1.01013 1.00598) translate(170.657 511.469) translate(-169.334 -508.298) scale(1.09126 1) translate(169.334 508.298) translate(-156.911 -506.897) scale(1 1.06292) translate(156.911 506.897) translate(-163.644 765.54) scale(0.881707 0.600266) translate(163.644 -765.54) translate(45.8836 -37.1657) scale(0.309 0.684766) translate(-45.8836 37.1657) translate(-69.5385 1277.7) scale(0.415094 0.969376) translate(69.5385 -1277.7) translate(-173.403 4.77105) scale(2.29193 1) translate(173.403 -4.77105) translate(-131.465 -21.955) scale(1.62026 0.838547) translate(131.465 21.955) translate(-128.274 -10.6013) scale(0.965372 1.26492) translate(128.274 10.6013) translate(-127.333 -8.29175) scale(0.874565 1.22473) translate(127.333 8.29175) translate(-138.784 -6.2554) scale(1.00883 0.82026) translate(138.784 6.2554) translate(-144.168 -7.53605) scale(1.05722 1.04501) translate(144.168 7.53605) translate(-137.487 -6.97423) scale(1.01704 0.999876) translate(137.487 6.97423)">
+        <path stroke="null" id="svg_3" d="m-160,44c30,0 58,-18 88,-18s58,18 88,18s58,-18 88,-18s58,18 88,18l0,44l-352,0l0,-44z"/>
+      </defs>
+      <g stroke="null" id="svg_6">
+        <title stroke="null">Layer 1</title>
+        <g stroke="null" transform="matrix(-0.0217456 0.782606 -0.765199 -0.0222404 584.591 1129.22)" class="layout-footer-waves-g" id="svg_1">
+          <use stroke="null" href="#svg_3" x="-328.61098" y="405.36815" fill="rgba(211, 239, 255, 1)" id="svg_4" transform="matrix(3.69628 0 0 3.42743 381.903 -787.25)"/>
+          <use stroke="null" href="#svg_3" x="-333.61098" y="401.76815" fill="rgba(211, 239, 255, 0.5)" id="svg_2" transform="matrix(3.69628 0 0 3.42743 381.903 -787.25)"/>
+        </g>
+      </g>
+    </g>
+  </g>
+</svg>
\ No newline at end of file
Added +1 -0
diff --git a/src/assets/login-icon-two.svg b/src/assets/login-icon-two.svg
new file mode 100644
index 0000000..b930211
--- /dev/null
+++ b/src/assets/login-icon-two.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" data-name="Layer 1" width="1151.5635" height="842.24197" viewBox="0 0 1151.5635 842.24197" xmlns:xlink="http://www.w3.org/1999/xlink"><title>sunlight</title><path d="M1080.33549,309.07821h-.00006c-22.0216,0-39.87364,19.81184-39.87364,44.251v31.0502h9.54961l5.52868-11.50435-1.38218,11.50435h61.38126l5.02608-10.4585-1.25651,10.4585h6.91087v-24.38C1126.2196,331.87644,1105.67658,309.07821,1080.33549,309.07821Z" transform="translate(-24.21825 -28.87901)" fill="#2f2e41"/><path d="M1042.88322,452.6347l-6.62276,25.38724,61.81242-2.20758-1.1038-20.97207S1050.60977,442.70056,1042.88322,452.6347Z" transform="translate(-24.21825 -28.87901)" fill="#ffb9b9"/><polygon points="959.06 525.305 978.928 569.456 997.693 546.277 982.24 515.371 959.06 525.305" fill="#ffb9b9"/><polygon points="1008.731 687.562 1010.938 738.337 1035.222 738.337 1031.91 687.562 1008.731 687.562" fill="#ffb9b9"/><path d="M1004.25045,507.82436l11.03794,36.42517,16.55689,176.60691s15.45311,4.41518,26.491-1.10379l2.20759-24.28345,4.41517-139.07794s20.97207-11.03793,18.76448-26.491-7.72655-36.42517-7.72655-36.42517Z" transform="translate(-24.21825 -28.87901)" fill="#2f2e41"/><path d="M1038.468,755.074h-6.62276s-18.76448,19.86828-25.38724,22.07587-40.84035,20.97207-13.24552,24.28345,45.25552-11.03794,45.25552-11.03794,8.83035,5.519,20.97207,1.1038,5.519-17.66069,5.519-17.66069-6.16367-20.18749-6.39321-20.02789S1051.71356,763.90438,1038.468,755.074Z" transform="translate(-24.21825 -28.87901)" fill="#2f2e41"/><circle cx="1048.46738" cy="323.31051" r="25.38724" fill="#ffb9b9"/><path d="M1110.2146,385.30331l-11.67815,9.73547-1.56737,1.30247-1.10379,13.24551h-43.04794s5.87219-5.08847,11.32493-10.35358c.11036-.1214.23176-.25385.36427-.37532.73953-.71743,1.40178-1.37974,1.99787-1.97577.32007-.33112.6291-.64021.90505-.92721.45259-.47462.89414-.92715,1.33561-1.37974a.03829.03829,0,0,0,.01105-.022,43.07955,43.07955,0,0,0,3.17893-3.73083l.01105-.011a12.81791,12.81791,0,0,0,.73952-1.09274c3.31138-5.519-4.41517-16.5569-4.41517-16.5569l24.28345-12.14173c.84994,11.96508,10.97171,19.95654,15.49723,22.95889C1109.37571,384.85072,1110.2146,385.30331,1110.2146,385.30331Z" transform="translate(-24.21825 -28.87901)" fill="#ffb9b9"/><path d="M1169.81943,570.74057c-16.5569,4.41517-17.66069-28.69862-17.66069-28.69862l-22.59765-45.1953a104.51533,104.51533,0,0,1-9.73594-30.31979l-5.19538-32.65665-1.57841,1.88745v.01105l-2.83676,1.41288-17.24124-27.59483-4.83462-7.72655v-8.83034l16.55689-7.72656h6.62276s.1435-.02209.39735-.04419c3.04648-.32008,22.87063-1.64464,23.8861,17.70489.39969,7.59413.36519,13.88569.15854,18.82226a145.76191,145.76191,0,0,0,3.84972,40.63177c1.16857,4.79412,2.44343,8.95772,3.7183,11.18873,4.41517,7.72655,19.86827,68.43518,19.86827,68.43518S1186.37633,566.3254,1169.81943,570.74057Z" transform="translate(-24.21825 -28.87901)" fill="#ffb9b9"/><path d="M1068.27046,397.445l-4.18335,7.62718-2.47256,4.51454L1049.506,431.66263s-3.43278,2.80361-8.99594,6.42408c-9.24979,6.01562-24.4159,14.272-39.571,15.65178-24.28345,2.20759-1.10379-76.16173-1.10379-76.16173s7.72655-30.90621,18.76449-27.59483,2.20758,14.34931-5.519,35.32138S1011.977,429.455,1011.977,429.455s23.17965-14.34931,35.32138-25.38724c6.32471-5.75079,11.75542-7.30711,15.49723-7.4727h.011a14.05473,14.05473,0,0,1,3.69775.287A8.35071,8.35071,0,0,1,1068.27046,397.445Z" transform="translate(-24.21825 -28.87901)" fill="#ffb9b9"/><path d="M1071.93506,390.82228c-.04413.1214-.56294,1.10379-4.52559,5.1326A53.271,53.271,0,0,0,1071.93506,390.82228Z" transform="translate(-24.21825 -28.87901)" fill="#d0cde1"/><path d="M1114.07788,434.42211l-1.02652,1.3466-16.08228,27.90392s-20.97207-7.72655-36.42517-9.93414a21.64727,21.64727,0,0,0-22.06482,9.901l4.40413-17.62754-2.37319-7.92523c-.27594-.92721-.596-1.95373-.93819-3.1127-3.31138-11.03793,8.83034-23.17966,8.83034-23.17966l14.39344-15.19925h.011a14.05473,14.05473,0,0,1,3.69775.287c-.59609.596-1.25834,1.25834-1.99787,1.97577-.13251.12147-.25391.25392-.36427.37532-4.22754,4.3158-2.031,5.519-.05517,5.839a9.71986,9.71986,0,0,0,1.97576.09937c4.6028.66225,14.68044-3.3776,22.07587-6.75521,5.03329-2.29591,8.83034-4.28272,8.83034-4.28272l9.63609-8.83035,1.446-1.32456c1.32457.872,2.16346,1.32456,2.16346,1.32456h1.10379s.1435-.02209.39735-.04419l-5.02587,3.72943a18.68469,18.68469,0,0,0-7.5132,16.183h0Z" transform="translate(-24.21825 -28.87901)" fill="#d0cde1"/><path d="M1053.92115,465.88022l-16.1372,6.30165s-87.61936-33.89648-100.86488-6.30165,46.35931,93.82242,46.35931,93.82242l26.491-12.14173-11.03793-33.11379s64.02,54.08586,90.511,24.28345,8.83035-71.94211,8.83035-71.94211S1081.516,453.73849,1053.92115,465.88022Z" transform="translate(-24.21825 -28.87901)" fill="#2f2e41"/><path d="M1007.56183,586.19367l-9.93413,7.72656s8.83034,35.32138,5.519,40.84034-9.93414,20.97208,2.20759,19.86828,25.38724-19.86828,26.491-26.491,8.83035-26.491,8.83035-26.491,13.24552-30.90621,3.31138-32.01-25.75662-1.84256-25.75662-1.84256Z" transform="translate(-24.21825 -28.87901)" fill="#2f2e41"/><path d="M1091.67085,321.26789a20.91185,20.91185,0,0,0-16.50006-8.4201h-.78284c-15.09365,0-27.32941,13.658-27.32941,30.50606v.00006h5.05749l.81677-6.217,1.19755,6.217H1084.126l2.51307-5.25968-.62828,5.25968h5.901q4.13043,20.51274-11.86947,41.02548h10.05221l5.02608-10.51935-1.2565,10.51935h19.162l3.76958-24.19458C1116.79567,342.06532,1106.26771,326.70034,1091.67085,321.26789Z" transform="translate(-24.21825 -28.87901)" fill="#2f2e41"/><circle cx="971.03374" cy="113.59071" r="91" fill="#ff6584"/><path d="M771.29477,567.032c-1.6806,13.391-3.05862,26.28082-4.18757,38.571h36.55924q1.99625-19.38064,5.00548-38.571Z" transform="translate(-24.21825 -28.87901)" fill="#3f3d56"/><path d="M801.19676,703.21105q-1.17629-19.19032-.9509-38.571H763.184c-.60171,14.7787-.83742,27.751-.88075,38.571Z" transform="translate(-24.21825 -28.87901)" fill="#3f3d56"/><path d="M864.4142,361.58256C894.89157,280.388,924.75032,225.797,924.75032,225.797L893.6574,207.29872c-32.37571,48.82045-56.84558,101.59229-75.3218,154.28384Z" transform="translate(-24.21825 -28.87901)" fill="#3f3d56"/><path d="M766.21242,615.836c-1.14212,13.751-1.97242,26.66388-2.56481,38.571h36.792q.53185-19.28516,2.25137-38.571Z" transform="translate(-24.21825 -28.87901)" fill="#3f3d56"/><path d="M778.50126,518.22788c-2.24695,13.1965-4.19314,26.08124-5.8747,38.571h37.70491q3.28452-19.4853,7.41146-38.571Z" transform="translate(-24.21825 -28.87901)" fill="#3f3d56"/><path d="M814.82354,371.81567q-6.5049,19.34635-11.981,38.571h44.39012c4.3882-13.35371,8.88306-26.24037,13.39195-38.571Z" transform="translate(-24.21825 -28.87901)" fill="#3f3d56"/><path d="M787.981,469.42381q-4.29519,19.6076-7.68675,38.571H820.004q4.44783-19.61091,9.56837-38.571Z" transform="translate(-24.21825 -28.87901)" fill="#3f3d56"/><path d="M762.3185,713.44417c.12069,16.62136.66994,25.97636.66994,25.97636l-.39358,7.478h43.29393q-2.59815-16.58382-3.96368-33.4544Z" transform="translate(-24.21825 -28.87901)" fill="#3f3d56"/><path d="M799.97844,420.61974q-5.32646,19.46534-9.69876,38.571H832.379q5.53043-19.7784,11.542-38.571Z" transform="translate(-24.21825 -28.87901)" fill="#3f3d56"/><path d="M1110.252,261.1651c-15.40963-32.21182-30.90977-56.23791-46.0196-73.94267l-7.89046,12.123.671-20.13755a158.22113,158.22113,0,0,0-16.6583-15.2789L1029.57149,180.496l.78947-23.69016a99.14041,99.14041,0,0,0-20.48314-10.11247l-7.97482,12.25252.48352-14.50965c-27.51475-6.905-50.1228,2.55337-63.22081,10.629l-7.86615-1.17354,13.49035-5.63347a99.1429,99.1429,0,0,0-6.25118-21.97146l-23.44384-3.4975L933.3355,115.172a158.21342,158.21342,0,0,0-12.02277-19.14149l-19.92822-2.973,13.34774-5.574c-14.68838-18.056-35.52378-37.63644-64.42682-58.60454,0,0-2.13827,18.67657-3.3293,44.71673l19.575,20.025-20.111-5.20116c-.21044,7.9976-.30047,16.41755-.20141,25.00812l14.43606,14.76794-14.20045-3.67257c.20688,6.56884.54118,13.17147,1.02835,19.7a77.7484,77.7484,0,0,0-37.38522.21323l.48352,14.50965-7.97482-12.25252A99.14066,99.14066,0,0,0,782.143,156.80588l.78947,23.69016L772.14939,163.929a158.22118,158.22118,0,0,0-16.65831,15.2789l.671,20.13755-7.89046-12.123c-15.10983,17.70476-30.61,41.73085-46.0195,73.94267,0,0,18.75584-1.2664,44.58355-4.79292l16.16488-22.86653L761.513,254.225c7.90429-1.2359,16.20236-2.66638,24.63409-4.31364l11.92111-16.86355-1.05035,14.63006c11.76-2.54214,23.55587-5.52,34.6978-8.989l8.00048,7.44239-14.31143-2.98448a99.14327,99.14327,0,0,0-7.27222,21.65494l17.35524,16.14443-19.35091-4.03541a158.216,158.216,0,0,0-.91179,22.58566l14.75256,13.72341-14.16008-2.95292c1.90026,23.19813,8.00769,51.13034,19.98347,84.7701,0,0,12.327-14.19261,28.03786-34.99356l-4.81657-27.58585,13.64313,15.66426c4.697-6.47636,9.53377-13.36913,14.311-20.50951l-3.55213-20.344L893.0578,318.329c9.67465-15.1043,18.722-30.948,25.38657-46.03838A385.08247,385.08247,0,0,0,946.086,304.50881l7.38-12.67579.33948,20.64894c6.03555,6.11391,12.08293,11.97343,17.91467,17.45027l10.45191-17.95172.46027,27.99943c19.34428,17.473,34.12174,29.09258,34.12174,29.09258,5.43182-35.29231,6.17421-63.8748,3.67531-87.01609l-13.35149,5.56466,11.9067-16.25425a158.22134,158.22134,0,0,0-5.14539-22.01061l-18.246,7.60454,8.67646-11.84438q5.59182,1.34816,11.21649,2.56145l-1.05035-14.63006,11.92111,16.86355c8.43173,1.64726,16.7298,3.07774,24.63409,4.31364l-1.48755-20.71932,16.16488,22.86653C1091.49615,259.8987,1110.252,261.1651,1110.252,261.1651Z" transform="translate(-24.21825 -28.87901)" fill="#e6a23c"/><path d="M706.516,543.343c1.1863,9.45251,2.159,18.55119,2.95593,27.2266H683.6654q-1.40913-13.68048-3.53328-27.2266Z" transform="translate(-24.21825 -28.87901)" fill="#3f3d56"/><path d="M685.40871,639.4695q.83031-13.54614.67122-27.2266h26.16136c.42474,10.432.59112,19.589.62171,27.2266Z" transform="translate(-24.21825 -28.87901)" fill="#3f3d56"/><path d="M640.78457,398.31967c-21.51346-57.31385-42.59025-95.84872-42.59025-95.84872l21.948-13.05765c22.85347,34.46154,40.12634,71.71229,53.16839,108.90637Z" transform="translate(-24.21825 -28.87901)" fill="#3f3d56"/><path d="M710.10356,577.79293c.8062,9.70662,1.3923,18.82158,1.81046,27.22659H685.94319q-.37542-13.61307-1.5892-27.22659Z" transform="translate(-24.21825 -28.87901)" fill="#3f3d56"/><path d="M701.42907,508.893c1.58609,9.31519,2.95987,18.41031,4.14686,27.22659H678.96066q-2.31847-13.75434-5.23162-27.22659Z" transform="translate(-24.21825 -28.87901)" fill="#3f3d56"/><path d="M675.78979,405.54305q4.59169,13.65627,8.45716,27.2266H652.91271c-3.09755-9.42616-6.2704-18.52264-9.45315-27.2266Z" transform="translate(-24.21825 -28.87901)" fill="#3f3d56"/><path d="M694.73746,474.443q3.0319,13.84068,5.42595,27.2266H672.133q-3.13965-13.843-6.75415-27.2266Z" transform="translate(-24.21825 -28.87901)" fill="#3f3d56"/><path d="M712.85221,646.69288c-.08519,11.73274-.47289,18.33627-.47289,18.33627l.27782,5.27863H682.09668q1.834-11.70624,2.79789-23.6149Z" transform="translate(-24.21825 -28.87901)" fill="#3f3d56"/><path d="M686.26869,439.993q3.75987,13.74026,6.8462,27.22659H663.39766q-3.90382-13.96124-8.14726-27.22659Z" transform="translate(-24.21825 -28.87901)" fill="#3f3d56"/><path d="M467.25181,327.43668c10.87739-22.73779,21.81868-39.6974,32.48446-52.1949l5.56974,8.55742-.47364-14.21476a111.68489,111.68489,0,0,1,11.75882-10.78512l7.61163,11.69442-.55728-16.72249a69.98142,69.98142,0,0,1,14.4587-7.13822l5.6293,8.64885-.34131-10.24212c19.4222-4.8741,35.38084,1.80238,44.62651,7.50279l5.55258-.82838-9.52261-3.97657a69.98374,69.98374,0,0,1,4.4126-15.50928l16.54861-2.46883-12.87573-5.37687a111.68,111.68,0,0,1,8.48667-13.51166l14.067-2.09862-9.422-3.93455c10.36829-12.74544,25.07564-26.56694,45.47782-41.368,0,0,1.50937,13.18347,2.35009,31.56478l-13.81766,14.1353,14.196-3.67141c.14854,5.64537.21209,11.58887.14216,17.65281l-10.19016,10.42444,10.02385-2.59241c-.146,4.63684-.382,9.29752-.72589,13.90591a54.88123,54.88123,0,0,1,26.3896.15051l-.34131,10.24212,5.62929-8.64885a69.98117,69.98117,0,0,1,14.4587,7.13822l-.55727,16.72249,7.61163-11.69442a111.68687,111.68687,0,0,1,11.75882,10.78512l-.47364,14.21476,5.56974-8.55742c10.66577,12.4975,21.60706,29.45711,32.48439,52.1949,0,0-13.23943-.89394-31.47078-3.38325l-11.41052-16.1411,1.05,14.62542c-5.57951-.8724-11.437-1.88215-17.38879-3.04492l-8.41491-11.9037.74142,10.32711c-8.30116-1.79445-16.62769-3.8965-24.49259-6.34518l-5.6474,5.25346,10.10219-2.10669a69.98325,69.98325,0,0,1,5.13334,15.28586L661.2033,341.39976l13.65948-2.84853a111.68285,111.68285,0,0,1,.64362,15.94284l-10.41359,9.68713,9.99537-2.08442c-1.34137,16.37518-5.6525,36.092-14.106,59.83779,0,0-8.70142-10.01832-19.79145-24.70136l3.39993-19.47239L634.96021,388.818c-3.31556-4.57156-6.72974-9.437-10.10193-14.47732l2.50739-14.36046-6.80014,7.80752A247.60106,247.60106,0,0,1,602.64558,335.29a271.82334,271.82334,0,0,1-19.51173,22.74229l-5.20944-8.94763-.23963,14.57574c-4.2604,4.31571-8.52914,8.45185-12.64567,12.31786l-7.37783-12.67182-.32489,19.76432c-13.65481,12.33393-24.086,20.536-24.086,20.536-3.83423-24.91224-4.35827-45.08815-2.59434-61.4232l9.4246,3.928-8.40474-11.47361a111.68612,111.68612,0,0,1,3.632-15.53692l12.87953,5.36792-6.12457-8.36075q-3.94716.95164-7.91753,1.80808l.74143-10.32711-8.41492,11.9037c-5.95181,1.16277-11.80928,2.17252-17.38879,3.04492l1.05-14.62542-11.41052,16.1411C480.49124,326.54274,467.25181,327.43668,467.25181,327.43668Z" transform="translate(-24.21825 -28.87901)" fill="#e6a23c"/><ellipse cx="379.53374" cy="720.59071" rx="147.5" ry="14" fill="#3f3d56"/><ellipse cx="734.53374" cy="720.59071" rx="147.5" ry="14" fill="#3f3d56"/><path d="M870.752,606.96972s10.65,73.95-40.48,117.67c-33.09,28.29-80.27,32.92-118.99,13.01-14.38-7.39-29.72-18.62-44.37006-35.57a191.08437,191.08437,0,0,1-15.26-20.3,242.48469,242.48469,0,0,1-17.73-32.37q-4.8-10.485-9.17-22.44c-.56-1.53-1.08-3.03-1.58-4.52-26.55-78.91,25.09-101.93,72.49-107.45a243.44911,243.44911,0,0,1,29.96-1.51,268.64314,268.64314,0,0,1,35.13,2.48s.97.01,2.72.08a210.98673,210.98673,0,0,1,26.93005,2.99C827.022,525.46972,883.072,545.38971,870.752,606.96972Z" transform="translate(-24.21825 -28.87901)" fill="#e6a23c"/><path d="M544.752,626.96972c-23.34,64.07-56.95,95.47-86.53,110.68-38.72,19.91-85.9,15.28-118.99-13.01a104.52345,104.52345,0,0,1-9-8.67,108.90024,108.90024,0,0,1-15.01-20.66,128.65459,128.65459,0,0,1-12.09-30.09,161.53735,161.53735,0,0,1-5.17-35.23,140.93566,140.93566,0,0,1,.79-23.02c-11.53-57.65,36.85-78.78,73.12-86.52a215.32462,215.32462,0,0,1,31.83-4.29c3.21-.18,5.05-.19,5.05-.19a276.07877,276.07877,0,0,1,57.57-1.71C516.412,518.29968,576.402,540.09973,544.752,626.96972Z" transform="translate(-24.21825 -28.87901)" fill="#e6a23c"/><path d="M403.702,516.15973l-100.57,149.06a161.53735,161.53735,0,0,1-5.17-35.23l73.91-109.54A215.32462,215.32462,0,0,1,403.702,516.15973Z" transform="translate(-24.21825 -28.87901)" fill="#fff" opacity="0.2"/><path d="M466.322,514.2597l-136.09,201.71a108.90024,108.90024,0,0,1-15.01-20.66l122.6-181.71A262.51415,262.51415,0,0,1,466.322,514.2597Z" transform="translate(-24.21825 -28.87901)" fill="#fff" opacity="0.2"/><path d="M725.622,513.48968l-91.7,135.92q-4.8-10.485-9.17-22.44c-.56-1.53-1.08-3.03-1.58-4.52l72.49-107.45A243.44911,243.44911,0,0,1,725.622,513.48968Z" transform="translate(-24.21825 -28.87901)" fill="#fff" opacity="0.2"/><path d="M790.402,519.03973l-123.49,183.04a191.08437,191.08437,0,0,1-15.26-20.3l111.81995-165.73A210.98673,210.98673,0,0,1,790.402,519.03973Z" transform="translate(-24.21825 -28.87901)" fill="#fff" opacity="0.2"/><path d="M894.752,599.96972H871.86472c10.12915-83.03949-111.11273-84-111.11273-84a276.47448,276.47448,0,0,0-58-1.67132v-.32868h-237v.24872a276.75793,276.75793,0,0,0-57,1.75128s-121.24188.96051-111.11273,84H275.752v22H297.8389c-.22119,23.90107,4.52283,71.14319,41.39307,102.67,33.09,28.29,80.27,32.92,118.99,13.01,29.58-15.21,63.19-46.61,86.53-110.68,7.996-21.94982,10.13965-39.74377,8.07287-54.14819a635.39219,635.39219,0,0,1,63.85425,0c-2.06677,14.40442.07684,32.19837,8.07288,54.14819,23.34,64.07,56.95,95.47,86.53,110.68,38.72,19.91,85.9,15.28,118.99-13.01,36.87024-31.5268,41.61425-78.76892,41.39306-102.67H894.752Zm-360.54,27.41c-22.21,60.97-54.61,89.49-82.4,102.65-33.81,16.01-74.1,11.56-103.12-12.03-35.08-28.52-39.75-72.35-39.62-94.69.06-8.88-.34-17.74-.69-26.61-2.77-69.71,101.83-70.53,101.83-70.53S580.722,499.72973,534.212,627.3797Zm83.38928-59.56537a424.54686,424.54686,0,0,0-65.69861,0c-5.48632-24.48639-23.97729-38.28705-45.937-45.84461H663.53824C641.57859,529.52728,623.08762,543.32794,617.60129,567.81433ZM862.202,596.3797c-.36,8.95-.76,17.9-.7,26.85.14,22.55-4.58,66.79-39.98,95.56-29.28,23.81-69.92005,28.31-104.03,12.17-28.06-13.27-60.78-42.05-83.21-103.62-46.93006-128.83,125.15-102.14,125.15-102.14S864.992,526.01971,862.202,596.3797Z" transform="translate(-24.21825 -28.87901)" fill="#3f3d56"/><path d="M428.14356,741.71275c-.11325-.18506-2.78356-4.64376-3.70932-13.90237-.84914-8.49429-.30313-22.81207,7.12226-42.7842,14.06719-37.83586-3.24186-68.36391-3.41872-68.668l.854-.49541a75.78134,75.78134,0,0,1,7.14973,20.25453,88.3638,88.3638,0,0,1-3.65968,49.253c-14.04309,37.77129-3.60282,55.65189-3.49584,55.82827Z" transform="translate(-24.21825 -28.87901)" fill="#3f3d56"/><circle cx="398.4247" cy="578.84223" r="6.41529" fill="#3f3d56"/><circle cx="418.65754" cy="602.52946" r="6.41529" fill="#3f3d56"/><circle cx="404.83999" cy="618.32095" r="6.41529" fill="#fff"/><circle cx="421.61845" cy="631.64502" r="6.41529" fill="#fff"/><circle cx="399.90515" cy="652.37134" r="6.41529" fill="#3f3d56"/><path d="M432.01914,741.94889s-6.41529-15.79149,12.83059-27.63511Z" transform="translate(-24.21825 -28.87901)" fill="#3f3d56"/><path d="M424.12933,741.66244s-2.91966-16.79294-25.51732-16.649Z" transform="translate(-24.21825 -28.87901)" fill="#3f3d56"/><path d="M363.072,370.10974a6.35543,6.35543,0,0,1-2.83,5.21l-.24.15h-106.8l.43-1.31c.07-.23,7.89-22.92005,42.5-18.54,3.26-1.12,35.12-11.52,54.19.17C358.782,360.96972,363.072,365.78973,363.072,370.10974Z" transform="translate(-24.21825 -28.87901)" fill="#f1f1f1"/><path d="M561.072,447.10974a6.35543,6.35543,0,0,1-2.83,5.21l-.24.15h-106.8l.43-1.31c.07-.23,7.89-22.92005,42.5-18.54,3.26-1.12,35.12-11.52,54.19.17C556.782,437.96972,561.072,442.78973,561.072,447.10974Z" transform="translate(-24.21825 -28.87901)" fill="#f1f1f1"/><path d="M606.072,137.10974a6.35543,6.35543,0,0,1-2.83,5.21l-.24.15h-106.8l.43-1.31c.07-.23,7.89-22.92,42.5-18.54,3.26-1.12,35.12-11.52,54.19.17C601.782,127.96972,606.072,132.78973,606.072,137.10974Z" transform="translate(-24.21825 -28.87901)" fill="#f1f1f1"/><path d="M53.17754,652.8835l9.13713,28.28159s-8.26692,26.54118-4.351,56.12807,1.7404,30.4571,1.7404,30.4571-39.12624-2.25247-35.21033,9.49526,42.172,4.86308,42.172,4.86308,7.34723-1.97821,10.828-22.86308S89.72606,698.134,89.72606,698.134l-8.22935-37.97489Z" transform="translate(-24.21825 -28.87901)" fill="#ffb8b8"/><path d="M56.22325,595.015S39.6894,601.10644,39.6894,617.64029s6.09142,39.15912,10.00733,40.46443,30.022,19.14446,34.80811,6.52652S56.22325,595.015,56.22325,595.015Z" transform="translate(-24.21825 -28.87901)" fill="#d0cde1"/><polygon points="170.367 761.932 205.175 760.626 231.717 756.71 223.885 782.816 159.49 784.557 170.367 761.932" fill="#ffb8b8"/><path d="M244.62214,794.29144s-.43511-9.57223-4.351-9.13713-1.3053-3.48081,2.17551-4.351,13.48814-5.22122,13.48814-5.22122l39.59422,2.61061S321.2,800.818,310.75754,813.0008s-37.41872,1.3053-37.41872,1.3053l-29.15179.4351S234.6148,799.07755,244.62214,794.29144Z" transform="translate(-24.21825 -28.87901)" fill="#2f2e41"/><polygon points="236.068 731.91 248.685 791.083 274.792 785.862 259.998 711.895 244.334 703.628 236.068 731.91" fill="#ffb8b8"/><path d="M110.17582,753.39191s-73.097,33.0677-42.20483,66.57051c0,0,0,15.66365,32.6326,12.61794s95.7223-8.702,98.768-6.96162,12.61793-34.373,4.351-36.11342-18.27426-3.91591-18.27426-3.91591,8.702-1.3053,8.702-8.702c0,0,24.80078-1.7404,32.1975-17.404S240.70622,732.072,240.70622,732.072l16.969,43.51013s19.57956-36.11341,34.373-20.01466c0,0-16.53385-103.98922-29.15179-113.56145s-21.32-12.61794-33.0677-3.91591-22.62527,30.89219-22.62527,30.89219Z" transform="translate(-24.21825 -28.87901)" fill="#2f2e41"/><path d="M284.65146,802.12326a98.03577,98.03577,0,0,0-8.702,7.39673c-3.48081,3.48081-8.26692,1.7404-8.702,0s-8.26692,1.3053-9.13712,13.92324-6.09142,23.93057,7.83182,27.84648,13.48814,15.22855,13.48814,15.22855a46.93427,46.93427,0,0,0,35.24321,2.17551c19.57956-6.52652,12.61794-13.48814,12.61794-13.48814l-26.10608-43.075S294.22369,799.07755,284.65146,802.12326Z" transform="translate(-24.21825 -28.87901)" fill="#2f2e41"/><path d="M212.42464,586.313s41.76972,14.35834,46.12074,17.404,23.93057,17.404,14.35834,24.80078-17.40405,12.18283-36.11341,6.52652S197.19609,611.984,197.19609,611.984Z" transform="translate(-24.21825 -28.87901)" fill="#ffb8b8"/><path d="M170.65491,567.16854s16.53385-3.04571,28.28158,3.48081,21.75507,19.57956,21.75507,19.57956S209.814,613.28928,201.9822,616.77009,179.792,602.84685,179.792,602.84685Z" transform="translate(-24.21825 -28.87901)" fill="#d0cde1"/><path d="M94.51217,554.5506s.4351,16.96895-1.3053,18.70936,20.88486,53.08236,45.68564,30.45709,5.65632-31.7624,5.65632-31.7624-9.13713-16.969-8.702-18.70936-40.46443,0-40.46443,0Z" transform="translate(-24.21825 -28.87901)" fill="#ffb8b8"/><path d="M94.51217,554.5506s.4351,16.96895-1.3053,18.70936,20.88486,53.08236,45.68564,30.45709,5.65632-31.7624,5.65632-31.7624-9.13713-16.969-8.702-18.70936-40.46443,0-40.46443,0Z" transform="translate(-24.21825 -28.87901)" opacity="0.1"/><path d="M101.47379,587.1832s16.53385,11.31263,23.49548,8.702,15.66364-13.92325,16.53385-14.79345,45.25054,88.32557,45.25054,88.32557-6.52652,32.6326-23.93058,42.20483-17.1865,17.18651-19.362,23.713-30.23954-20.66732-30.23954-20.66732L95.38237,638.96026V592.83952Z" transform="translate(-24.21825 -28.87901)" fill="#d0cde1"/><path d="M94.29462,566.951s-5.43877-1.08776-11.09508,3.69836S51.002,593.27462,52.30734,595.015s13.053,26.54118,15.66365,36.54852,6.52652,11.31263,6.52652,11.31263,9.13713,6.52652,7.83182,11.31264-6.96162,70.05131.87021,84.40966,6.52652,26.97628,4.351,30.45709,20.88486-10.00733,20.88486-10.00733,34.373-27.84649,36.98362-36.11341a97.263,97.263,0,0,0,3.48081-17.40406s-22.19017-87.89047-29.15179-92.67658S94.29462,566.951,94.29462,566.951Z" transform="translate(-24.21825 -28.87901)" fill="#575a89"/><path d="M142.0946,567.29509s10.72115-6.65307,12.89666-4.91267,22.62527,1.74041,23.06037,3.48081,1.3053,43.51014,16.09875,54.38767,5.22122,32.6326,5.22122,32.6326l10.87753,17.40406s-59.17285,56.99362-55.25787,37.41871c.21755-1.08775,0-1.3053,0-1.3053s-23.06037-98.768-20.01466-103.98922S143.55629,573.51306,142.0946,567.29509Z" transform="translate(-24.21825 -28.87901)" fill="#575a89"/><path d="M233.3095,607.19786l-59.17378,20.44976s-58.73868,13.92325-49.60156,30.022S172.39531,646.357,172.39531,646.357l78.7996-8.82909S282.476,611.11377,233.3095,607.19786Z" transform="translate(-24.21825 -28.87901)" fill="#ffb8b8"/><circle cx="93.78939" cy="509.57283" r="32.1975" fill="#ffb8b8"/><path d="M92.36668,506.4531l-5.95587-2.16578s12.45312-12.45312,29.77934-11.37019l-4.8731-4.873s11.91174-4.33148,22.74055,7.03872c5.69249,5.97707,12.27878,13.00279,16.38465,20.91719h6.37832l-2.66208,5.32415,9.31726,5.32415-9.56325-.95636a26.866,26.866,0,0,1-.90453,13.789l-2.16569,5.95579s-8.66312-17.32606-8.66312-19.49184v5.4144s-5.95587-4.87294-5.95587-8.12156l-3.24862,3.79009-1.62432-5.95587-20.0333,5.95587,3.24863-4.87294-12.45312,1.62431,4.8731-5.95587s-14.07744,7.03872-14.619,12.99459-7.58,13.536-7.58,13.536L81.538,538.93944S76.66493,514.57467,92.36668,506.4531Z" transform="translate(-24.21825 -28.87901)" fill="#2f2e41"/><path d="M146.61566,540.19858s1.05805,7.34674-4.02158,11.6902a10.68163,10.68163,0,0,1-11.82134,1.29251,14.7297,14.7297,0,0,1-4.40805-3.53378,18.98358,18.98358,0,0,1-1.516-2.01675,24.0899,24.0899,0,0,1-1.76144-3.21588q-.47687-1.04165-.911-2.22935c-.05564-.152-.1073-.301-.157-.449-2.63767-7.8395,2.49263-10.12648,7.20169-10.67488a24.18681,24.18681,0,0,1,2.97645-.15,26.68959,26.68959,0,0,1,3.49007.24639s.09637.001.27022.00794a20.96175,20.96175,0,0,1,2.67543.29706C142.2712,532.10177,147.83962,534.08077,146.61566,540.19858Z" transform="translate(-24.21825 -28.87901)" fill="#e6a23c"/><path d="M114.22842,542.18553c-2.31877,6.36518-5.65784,9.48469-8.59653,10.99576a10.68165,10.68165,0,0,1-11.82135-1.29251,10.38378,10.38378,0,0,1-.89412-.86134,10.81892,10.81892,0,0,1-1.49121-2.05252,12.78124,12.78124,0,0,1-1.2011-2.98936,16.04808,16.04808,0,0,1-.51363-3.5,14.00278,14.00278,0,0,1,.07848-2.287c-1.14547-5.72738,3.661-7.82659,7.26428-8.59554a21.39066,21.39066,0,0,1,3.16223-.42619c.3189-.01789.50171-.01888.50171-.01888a27.42839,27.42839,0,0,1,5.71942-.16989C111.41291,531.38944,117.37277,533.55522,114.22842,542.18553Z" transform="translate(-24.21825 -28.87901)" fill="#e6a23c"/><path d="M100.21547,531.17685l-9.99136,14.80871a16.04808,16.04808,0,0,1-.51363-3.5L97.05324,531.603A21.39066,21.39066,0,0,1,100.21547,531.17685Z" transform="translate(-24.21825 -28.87901)" fill="#fff" opacity="0.2"/><path d="M106.4366,530.98808,92.91642,551.02744a10.81892,10.81892,0,0,1-1.49121-2.05252l12.18-18.0524A26.08038,26.08038,0,0,1,106.4366,530.98808Z" transform="translate(-24.21825 -28.87901)" fill="#fff" opacity="0.2"/><path d="M132.19738,530.91158l-9.11016,13.5033q-.47687-1.04165-.911-2.22935c-.05564-.152-.1073-.301-.157-.449l7.20169-10.67488A24.18681,24.18681,0,0,1,132.19738,530.91158Z" transform="translate(-24.21825 -28.87901)" fill="#fff" opacity="0.2"/><path d="M138.6331,531.463l-12.26841,18.18454a18.98358,18.98358,0,0,1-1.516-2.01675l11.109-16.46485A20.96175,20.96175,0,0,1,138.6331,531.463Z" transform="translate(-24.21825 -28.87901)" fill="#fff" opacity="0.2"/><path d="M149,539.50315h-2.27379c1.0063-8.24976-11.03876-8.34518-11.03876-8.34518a27.46661,27.46661,0,0,0-5.76215-.166v-.03266H106.38v.02471a27.49535,27.49535,0,0,0-5.6628.174s-12.04507.09542-11.03876,8.34518H87.504v2.18564h2.19428c-.022,2.37451.44933,7.06789,4.11229,10.2a10.68165,10.68165,0,0,0,11.82135,1.29251c2.93869-1.51107,6.27776-4.63058,8.59653-10.99576a11.22868,11.22868,0,0,0,.802-5.37948,63.12484,63.12484,0,0,1,6.34375,0,11.22856,11.22856,0,0,0,.802,5.37948c2.31877,6.36518,5.65783,9.48469,8.59653,10.99576a10.68163,10.68163,0,0,0,11.82134-1.29251c3.663-3.1321,4.13427-7.82548,4.1123-10.2H149Zm-35.8187,2.72311c-2.20651,6.05721-5.42537,8.89059-8.18623,10.198a9.69145,9.69145,0,0,1-10.2447-1.19515c-3.48511-2.83339-3.94906-7.18778-3.93614-9.40721.006-.8822-.03378-1.76242-.06855-2.64363-.27519-6.92551,10.11654-7.007,10.11654-7.007S117.80194,529.54457,113.1813,542.22626Zm8.28451-5.91767a42.17881,42.17881,0,0,0-6.527,0c-.54505-2.43265-2.38208-3.80371-4.56371-4.55454h15.65441C123.84789,532.50488,122.01086,533.87594,121.46581,536.30859Zm24.30043,2.8379c-.03576.88916-.0755,1.77832-.06954,2.66748.01391,2.24028-.455,6.63541-3.97191,9.49363a9.77755,9.77755,0,0,1-10.33511,1.20906c-2.78768-1.31834-6.03833-4.17756-8.26669-10.29437-4.66238-12.79893,12.43332-10.14735,12.43332-10.14735S146.04342,532.15641,145.76624,539.14649Z" transform="translate(-24.21825 -28.87901)" fill="#3f3d56"/></svg>
\ No newline at end of file
Added +1 -0
diff --git a/src/assets/login-main.svg b/src/assets/login-main.svg
new file mode 100644
index 0000000..60dbe7c
--- /dev/null
+++ b/src/assets/login-main.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" data-name="Layer 1" width="1030.01" height="729.86" viewBox="0 0 1030.01 729.86"><defs><linearGradient id="a5312bde-8c7e-4767-9e17-08a43f21bf92-27" x1="889.13" y1="701.85" x2="889.13" y2="363.3" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="gray" stop-opacity="0.25"/><stop offset="0.54" stop-color="gray" stop-opacity="0.12"/><stop offset="1" stop-color="gray" stop-opacity="0.1"/></linearGradient><linearGradient id="016d359d-0d48-4404-968f-1612e9b5c7a7-28" x1="376.53" y1="777.06" x2="376.53" y2="381.85" xlink:href="#a5312bde-8c7e-4767-9e17-08a43f21bf92-27"/></defs><title>static_assets</title><ellipse cx="814.66" cy="585.22" rx="211.36" ry="41.08" fill="#f9a826" opacity="0.1"/><g opacity="0.5"><path d="M718.61,188.63c-2.85.81-6.34,2.92-8.06,8.69-3.37,11.31,4.46,19,1.88,30.79a23,23,0,0,1-4,8.64c-3.15,4.27-6.57,6.29-9.89,9.28a29.5,29.5,0,0,0-8.69,14.27,39.5,39.5,0,0,0-.39,18.55c1.18,5.12,3.19,8.85,4.95,13a96,96,0,0,1,5.11,16.37,39.87,39.87,0,0,1,1.12,8.32,73.21,73.21,0,0,1-1,11.12l-4.79,33.42a100.43,100.43,0,0,0-1.31,13.21,65.61,65.61,0,0,0,1.53,13.19c1.68,8.84,3.49,18,7.34,22.68s10.41,3,14.44-6.47c3.83-9.06,3.92-21.15,7.3-30.83,5.33-15.25,15.92-17.3,21.8-31.85,5.64-14,3.91-31.08,6.77-47,2-11.41,6.41-21.81,9-33.08,3.72-16.23,3.35-33.08.38-45.56a60.55,60.55,0,0,0-14.18-27.54c-2.49-2.75-5.16-5.2-8.29-5.45-3.33-.26-6.24,3.93-9.53,5.19C726.3,189.06,722.48,187.53,718.61,188.63Z" transform="translate(-84.99 -85.07)" fill="#f9a826"/><path d="M736.64,183.23S731,327.57,707.28,412.68" transform="translate(-84.99 -85.07)" fill="none" stroke="#535461" stroke-miterlimit="10" stroke-width="2"/><path d="M710.41,198s25.35,23.08,20.54,67" transform="translate(-84.99 -85.07)" fill="none" stroke="#535461" stroke-miterlimit="10" stroke-width="2"/><path d="M689.14,263.68s41.61,8.37,38.52,33.26" transform="translate(-84.99 -85.07)" fill="none" stroke="#535461" stroke-miterlimit="10" stroke-width="2"/><path d="M762.17,215.07s-16.84,14.74-31.84,56.49" transform="translate(-84.99 -85.07)" fill="none" stroke="#535461" stroke-miterlimit="10" stroke-width="2"/><path d="M745.72,341.25s-11.49,9-20.71-22.51" transform="translate(-84.99 -85.07)" fill="none" stroke="#535461" stroke-miterlimit="10" stroke-width="2"/><path d="M696.14,359.66s23.43-3.06,25.59-17.73" transform="translate(-84.99 -85.07)" fill="none" stroke="#535461" stroke-miterlimit="10" stroke-width="2"/></g><path d="M775.25,91.82c-5.8.86-12.78,3.55-15.65,11.84-5.63,16.26,11.48,28.57,7.54,45.56A25.53,25.53,0,0,1,760,161.47c-6,5.91-12.86,8.49-19.36,12.5s-13.25,10.32-16.3,20c-2.93,9.31-1.66,19.59,1.39,27.27s7.66,13.41,11.78,19.73A133.69,133.69,0,0,1,750,265.67,46.94,46.94,0,0,1,753.32,278c.53,5.2-.14,10.79-.81,16.26L746.56,343c-.79,6.48-1.59,13.06-1.14,19.3.51,7.19,2.64,13.47,4.74,19.6,4.52,13.22,9.36,27,17.89,34.28s21.91,5.72,29.12-7.82c6.86-12.89,5.6-30.68,11.45-44.53,9.22-21.83,30.9-23.6,41.33-44.32,10-19.88,4.41-45.31,8.43-68.41,2.88-16.55,10.68-31.35,14.68-47.64,5.77-23.47,3-48.32-4.62-67s-19.67-32-32.6-42.23c-5.48-4.35-11.29-8.28-17.8-9-6.92-.77-12.45,5.05-19.1,6.52C791.2,93.36,783.12,90.65,775.25,91.82Z" transform="translate(-84.99 -85.07)" fill="#f9a826"/><path d="M811.93,86s5.46,211.89-33.56,334.42" transform="translate(-84.99 -85.07)" fill="none" stroke="#535461" stroke-miterlimit="10" stroke-width="2"/><path d="M759.38,104.68s55.2,37,50.47,101.16" transform="translate(-84.99 -85.07)" fill="none" stroke="#535461" stroke-miterlimit="10" stroke-width="2"/><path d="M723.15,198.85s87.12,17.26,83.68,53.54" transform="translate(-84.99 -85.07)" fill="none" stroke="#535461" stroke-miterlimit="10" stroke-width="2"/><path d="M868.54,135.92s-33.11,19.71-59.19,79.41" transform="translate(-84.99 -85.07)" fill="none" stroke="#535461" stroke-miterlimit="10" stroke-width="2"/><path d="M849.43,319.79s-22.7,12-45.53-35.61" transform="translate(-84.99 -85.07)" fill="none" stroke="#535461" stroke-miterlimit="10" stroke-width="2"/><path d="M749,341s48.13-1.73,50.87-23.08" transform="translate(-84.99 -85.07)" fill="none" stroke="#535461" stroke-miterlimit="10" stroke-width="2"/><path d="M152.24,663.66s8.38,11-3.87,27.5S126,721.68,130.11,732c0,0,18.48-30.73,33.52-31.16S168.79,682.14,152.24,663.66Z" transform="translate(-84.99 -85.07)" fill="#f9a826"/><path d="M152.24,663.66A13.57,13.57,0,0,1,154,667.1c14.68,17.24,22.49,33.33,8.39,33.74-13.14.38-28.9,23.87-32.68,29.81a12.74,12.74,0,0,0,.45,1.35s18.48-30.73,33.52-31.16S168.79,682.14,152.24,663.66Z" transform="translate(-84.99 -85.07)" opacity="0.1"/><path d="M167.82,677.63c0,3.86-.43,7-1,7s-1-3.13-1-7,.54-2,1.07-2S167.82,673.77,167.82,677.63Z" transform="translate(-84.99 -85.07)" fill="#ffd037"/><path d="M173.17,682.23c-3.39,1.85-6.34,3-6.59,2.49s2.28-2.35,5.67-4.19,2.05-.5,2.31,0S176.55,680.38,173.17,682.23Z" transform="translate(-84.99 -85.07)" fill="#ffd037"/><path d="M108,663.66s-8.38,11,3.87,27.5,22.35,30.51,18.26,40.83c0,0-18.48-30.73-33.52-31.16S91.43,682.14,108,663.66Z" transform="translate(-84.99 -85.07)" fill="#f9a826"/><path d="M108,663.66a13.57,13.57,0,0,0-1.71,3.44c-14.68,17.24-22.49,33.33-8.39,33.74,13.14.38,28.9,23.87,32.68,29.81a12.74,12.74,0,0,1-.45,1.35s-18.48-30.73-33.52-31.16S91.43,682.14,108,663.66Z" transform="translate(-84.99 -85.07)" opacity="0.1"/><path d="M92.4,677.63c0,3.86.43,7,1,7s1-3.13,1-7-.54-2-1.07-2S92.4,673.77,92.4,677.63Z" transform="translate(-84.99 -85.07)" fill="#ffd037"/><path d="M87.05,682.23c3.39,1.85,6.34,3,6.59,2.49s-2.28-2.35-5.67-4.19-2.05-.5-2.31,0S83.67,680.38,87.05,682.23Z" transform="translate(-84.99 -85.07)" fill="#ffd037"/><path d="M95.07,730.56s23.43-.72,30.49-5.75,36-11,37.79-3,35.21,40.11,8.76,40.32-61.46-4.12-68.51-8.41S95.07,730.56,95.07,730.56Z" transform="translate(-84.99 -85.07)" fill="#a8a8a8"/><path d="M172.58,759.35c-26.45.21-61.46-4.12-68.51-8.41-5.37-3.27-7.51-15-8.22-20.41l-.78,0s1.48,18.9,8.53,23.19,42.06,8.63,68.51,8.41c7.64-.06,10.27-2.78,10.13-6.8C181.18,757.79,178.27,759.31,172.58,759.35Z" transform="translate(-84.99 -85.07)" opacity="0.2"/><path d="M1067.35,659.11a11.67,11.67,0,0,0,3.83-5.78c.5-2.3-.48-5.05-2.68-5.89-2.46-.94-5.09.76-7.08,2.49s-4.28,3.69-6.89,3.32a10.48,10.48,0,0,0,3.24-9.81,4.1,4.1,0,0,0-.9-2c-1.37-1.46-3.84-.83-5.48.32-5.2,3.66-6.65,10.72-6.68,17.08-.52-2.29-.08-4.68-.1-7s-.66-5-2.64-6.22a8,8,0,0,0-4-.95c-2.34-.09-4.94.15-6.54,1.86-2,2.12-1.47,5.69.26,8s4.35,3.8,6.77,5.42a15,15,0,0,1,4.84,4.61,4.57,4.57,0,0,1,.36.82h14.65A40.83,40.83,0,0,0,1067.35,659.11Z" transform="translate(-84.99 -85.07)" fill="#f9a826"/><path d="M231.72,565.39c31.82,40.46,19.46,99.57-.76,146.9-6.77,15.85-14.43,32.62-11.57,49.61,3.49,20.78,22,36,41.52,44,35.5,14.68,78,11.18,110.61-9.11,28.19-17.54,48.38-46.06,77.05-62.8,48-28,109.06-17.43,161.16,1.86,36.86,13.65,77.16,31.41,113.79,17.14,25.77-10,43.59-34.47,53.3-60.37,4.69-12.51,8.09-26,17-36,5.32-5.89,12.26-10.08,19.38-13.6,65-32.21,147-15,210.08-50.68,42.61-24.12,69.89-69.63,82-117.08s10.67-97.21,7.6-146.07c-2.18-34.73-6.09-71.8-28.28-98.61-23.47-28.35-63.76-38.9-100.17-33.51S915,221.76,886,244.38c-36.29,28.27-69.94,63.48-114.5,74.94-30.34,7.8-62.26,3.53-93.56,2.06-52.32-2.47-104.65,3-156.68,9-49.81,5.76-100,12.1-147.43,28.3-33.58,11.47-60.5,32.28-92.21,46.82-20.67,9.48-43.21,12.1-62.77,24.17-24.11,14.87-46.2,40.74-40,71C184.67,529.48,214.82,543.92,231.72,565.39Z" transform="translate(-84.99 -85.07)" fill="#f9a826" opacity="0.1"/><ellipse cx="552.35" cy="439.22" rx="427.67" ry="41.08" fill="#f9a826" opacity="0.1"/><ellipse cx="223.66" cy="676.22" rx="211.36" ry="41.08" fill="#f9a826" opacity="0.1"/><path d="M524.71,511.48l-3.35,4.87s3.65,4.87,4,5.48,10.65-.3,10.65-.3l.91-3.65-1.52-4.26Z" transform="translate(-84.99 -85.07)" fill="#e6e6f0"/><polygon points="327.09 423.67 320.39 424.88 324.65 433.11 329.22 435.54 333.48 438.58 334.7 435.24 331.65 428.84 327.09 423.67" fill="#fdc2cc"/><path d="M521.67,429.59s35.62,8.52,42.31,18c0,0,6.09,5.18,0,14.61s-14.61,30.75-14.61,30.75-6.09,20.7-8.52,22.22-16.44,2.74-17.66-3c0,0,7.91-12.48,7.91-18.27s13.09-33.18,13.09-33.18-29.83-3.35-39.27-1.22S509.19,426.24,521.67,429.59Z" transform="translate(-84.99 -85.07)" fill="#565171"/><path d="M521.67,429.59s35.62,8.52,42.31,18c0,0,6.09,5.18,0,14.61s-14.61,30.75-14.61,30.75-6.09,20.7-8.52,22.22-16.44,2.74-17.66-3c0,0,7.91-12.48,7.91-18.27s13.09-33.18,13.09-33.18-29.83-3.35-39.27-1.22S509.19,426.24,521.67,429.59Z" transform="translate(-84.99 -85.07)" opacity="0.1"/><path d="M514.36,437.81c-.07,1.77-.26,3.76-.53,5.9-1.95,15.5-8.3,38.55-8.3,38.55s-6.39,13.7-8.22,22.53-14.31,11-14.31,11-34.1,4-41.1,2.44-18,4.57-23.44,3.65-8.83-13.7-8.83-13.7a66,66,0,0,0,27.4-5.78c14-6.39,34.1-3.35,34.1-3.35a11.45,11.45,0,0,0,7-5.78c2.13-4.57,2.13-32.27,1.52-34.1-4-12-3.67-19.7-2.52-24.19a12.07,12.07,0,0,1,2.52-5.34Z" transform="translate(-84.99 -85.07)" fill="#565171"/><path d="M514.36,437.81c-.07,1.77-.26,3.76-.53,5.9-3.06.24-11.64.47-18-3.46-6-3.68-14.73-4.93-18.66-5.32a12.07,12.07,0,0,1,2.52-5.34Z" transform="translate(-84.99 -85.07)" opacity="0.1"/><path d="M518.57,434c-1.42,4-3.6,8.69-3.6,8.69s-11.26,1.52-19.18-3.35-20.7-5.48-20.7-5.48c2.07-1.27,3.47-5.13,4.34-8.51a53.93,53.93,0,0,0,1.14-5.8s37.44,7.61,39,8.83C520.15,428.86,519.53,431.28,518.57,434Z" transform="translate(-84.99 -85.07)" fill="#e6e6f0"/><path d="M585.29,305.69,539.88,333.1l-4.51,2.72V322.43l3.13-1.5,38.27-18.29s1.83-33.18,6.7-28S585.29,305.69,585.29,305.69Z" transform="translate(-84.99 -85.07)" fill="#fdc2cc"/><path d="M539.88,333.1l-4.51,2.72V322.43l3.13-1.5A98.54,98.54,0,0,0,539.88,333.1Z" transform="translate(-84.99 -85.07)" opacity="0.1"/><path d="M504.93,338l32.57-18.87s.61,18.27,4.26,21c0,0-13.39,4-18.57,12.48Z" transform="translate(-84.99 -85.07)" fill="#b45181"/><path d="M504.93,338l32.57-18.87s.61,18.27,4.26,21c0,0-13.39,4-18.57,12.48Z" transform="translate(-84.99 -85.07)" opacity="0.1"/><path d="M585,354.09s-15.49,6.75-25.75,9.23a20.22,20.22,0,0,1-7.13.82c-7-1.22,1.83-10,1.83-10l3.68-.57,19.76-3.08s2.13-30.44,5.78-28S585,354.09,585,354.09Z" transform="translate(-84.99 -85.07)" fill="#fdc2cc"/><path d="M519.54,326.69s-13.09,9.74-8.22,21.92-25.27-7.31-25.27-7.31,9.74-14.61,11.87-25.57S519.54,326.69,519.54,326.69Z" transform="translate(-84.99 -85.07)" fill="#fdc2cc"/><path d="M518.57,434a69,69,0,0,0-17.6-4.1c-8.8-1-16.86-2.69-21.53-4.55a53.93,53.93,0,0,0,1.14-5.8s37.44,7.61,39,8.83C520.15,428.86,519.53,431.28,518.57,434Z" transform="translate(-84.99 -85.07)" opacity="0.1"/><path d="M559.24,363.32a20.22,20.22,0,0,1-7.13.82c-7-1.22,1.83-10,1.83-10l3.68-.57C559.16,355.94,560.73,359.64,559.24,363.32Z" transform="translate(-84.99 -85.07)" opacity="0.1"/><path d="M498.23,332.17s-11.87-.3-16.13,4.57-3.35,18-3.35,18-.89,26.29,5.78,41a2,2,0,0,1,.07,1.51c-.07.18-.07.07-.07.07s-4.26,19.18-8.52,22.53,9.13,7.31,25,9.13,22.22,6.39,22.22,6.39,2.74-3,3-9.74S522,369.92,522,369.92s16.74,4.26,26.79-.91c0,0,4.57,2.44,8.83-4.26s-3-14-3-14-6.7,6.7-25.27-2.13S504,331.87,498.23,332.17Z" transform="translate(-84.99 -85.07)" fill="#b45181"/><path d="M407.21,509.35h-3.65v5.18l3.65,8.22s4.87.91,5.18.91,4.26-.91,4.26-.91l1.22-2.13S404.47,515.43,407.21,509.35Z" transform="translate(-84.99 -85.07)" fill="#e6e6f0"/><path d="M521.67,514.22l-5.48,12.18,23.14,11,18.27.61s1.52-4.26-4.57-6.39l-4.87-.91s-6.39-11.57-7-17c0,0-6.09,1.22-7,5.78C534.15,519.39,525.63,523.65,521.67,514.22Z" transform="translate(-84.99 -85.07)" fill="#b45181"/><path d="M404.47,508.13l-1.86,1.52-8.18,6.7,18.87,25s10.65,4.57,15.53,0c0,0,3-3-3.65-5.48,0,0-5.18.91-5.18-4.26s1.22-10.65,3-11.87c0,0-4.57-1.52-5.78-.3a19.32,19.32,0,0,0-2.44,3.35S403.86,518.48,404.47,508.13Z" transform="translate(-84.99 -85.07)" fill="#b45181"/><path d="M497.32,316.34a37,37,0,0,1-1.22,4.56,15.83,15.83,0,0,0,20.27,8.71,25.18,25.18,0,0,1,2.57-2.31S499.45,305.38,497.32,316.34Z" transform="translate(-84.99 -85.07)" opacity="0.1"/><path d="M495.64,357s9.44,12.48,23.14,12.79" transform="translate(-84.99 -85.07)" opacity="0.1"/><g opacity="0.1"><path d="M552.06,369.22a3.44,3.44,0,0,1-.56-.21q-.44.23-.9.43A5.69,5.69,0,0,0,552.06,369.22Z" transform="translate(-84.99 -85.07)"/><path d="M555.3,352a15.06,15.06,0,0,0-1.06-1.22s-2,2-6.94,2.38A16.8,16.8,0,0,0,555.3,352Z" transform="translate(-84.99 -85.07)"/><path d="M524.71,369.92l.06.65a60.46,60.46,0,0,0,12.18,1A63.38,63.38,0,0,1,524.71,369.92Z" transform="translate(-84.99 -85.07)"/><path d="M503.71,429c-15.83-1.83-29.22-5.78-25-9.13s8.52-22.53,8.52-22.53,0,.11.07-.07a2,2,0,0,0-.07-1.51c-6.67-14.75-5.78-41-5.78-41s-.91-13.09,3.35-18c3.17-3.63,10.56-4.38,14.13-4.53a6.47,6.47,0,0,0-1,0s-11.87-.3-16.13,4.57-3.35,18-3.35,18-.89,26.29,5.78,41a2,2,0,0,1,.07,1.51c-.07.18-.07.07-.07.07S480,416.5,475.7,419.85s9.13,7.31,25,9.13,22.22,6.39,22.22,6.39a7.8,7.8,0,0,0,.84-1.23C520.67,432.65,514.31,430.2,503.71,429Z" transform="translate(-84.99 -85.07)"/></g><circle cx="426.33" cy="229.14" r="15.83" fill="#fdc2cc"/><path d="M533.85,307.13c-1.31,3.28-5.22,4.52-8.65,5.37-4.9,1.2-10.18,2.38-14.87.52a3.25,3.25,0,0,0-2-.39,2.66,2.66,0,0,0-1.32,1.15c-5.17,7.36-4.33,17.17-5.67,26.07-.42,2.77-1.13,5.63-3,7.7-3.64,4-10.51,3.85-13.86,8.1-2.21,2.8-2.23,6.69-2.21,10.26q0,5.71-.1,11.43c-.06,2.5-.18,5.09-1.31,7.31-1.92,3.76-6.2,5.55-10.15,7-2.46.92-5,1.85-7.64,1.6s-5.28-2-5.62-4.64c-.47-3.58,3.22-6.08,5.76-8.66,4.06-4.11,5.73-10.09,5.88-15.87a17,17,0,0,0-1-6.87c-.93-2.27-2.59-4.18-3.59-6.42-3.08-6.9.72-14.88,5.27-20.92s10.14-11.93,11.11-19.42a26.93,26.93,0,0,1,.69-4.81c.7-2.18,2.36-8.2,4.19-9.58a21.77,21.77,0,0,1,10-4.16c2.81-.41,5.68-.26,8.47-.8,3.26-.63,6.29-2.18,9.55-2.76,7.05-1.25,14.7,6.94,17.79,13.4C532.42,303.72,534.65,305.12,533.85,307.13Z" transform="translate(-84.99 -85.07)" opacity="0.1"/><path d="M533.85,306.52c-1.31,3.28-5.22,4.52-8.65,5.37-4.9,1.2-10.18,2.38-14.87.52a3.25,3.25,0,0,0-2-.39,2.66,2.66,0,0,0-1.32,1.15c-5.17,7.36-4.33,17.17-5.67,26.07-.42,2.77-1.13,5.63-3,7.7-3.64,4-10.51,3.85-13.86,8.1-2.21,2.8-2.23,6.69-2.21,10.26q0,5.71-.1,11.43c-.06,2.5-.18,5.09-1.31,7.31-1.92,3.76-6.2,5.55-10.15,7-2.46.92-5,1.85-7.64,1.6s-5.28-2-5.62-4.64c-.47-3.58,3.22-6.08,5.76-8.66,4.06-4.11,5.73-10.09,5.88-15.87a17,17,0,0,0-1-6.87c-.93-2.27-2.59-4.18-3.59-6.42-3.08-6.9.72-14.88,5.27-20.92s10.14-11.93,11.11-19.42a26.93,26.93,0,0,1,.69-4.81c.7-2.18,2.36-8.2,4.19-9.58a21.77,21.77,0,0,1,10-4.16c2.81-.41,5.68-.26,8.47-.8,3.26-.63,6.29-2.18,9.55-2.76,7.05-1.25,14.7,6.94,17.79,13.4C532.42,303.11,534.65,304.51,533.85,306.52Z" transform="translate(-84.99 -85.07)" fill="#f569bc"/><path d="M585,276.09H848.17V539.24H585Z" transform="translate(-84.99 -85.07)" fill="#f7df1e"/><path d="M654.21,496l20.14-12.19c3.89,6.89,7.42,12.72,15.9,12.72,8.13,0,13.25-3.18,13.25-15.54V396.9h24.73v84.43c0,25.61-15,37.27-36.92,37.27-19.78,0-31.26-10.25-37.09-22.61m87.44-2.65,20.13-11.66c5.3,8.66,12.19,15,24.38,15,10.25,0,16.78-5.12,16.78-12.19,0-8.48-6.71-11.48-18-16.43l-6.18-2.65c-17.84-7.59-29.68-17.13-29.68-37.27,0-18.55,14.13-32.68,36.21-32.68,15.72,0,27,5.48,35.15,19.78l-19.25,12.37c-4.24-7.6-8.83-10.6-15.9-10.6-7.24,0-11.84,4.59-11.84,10.6,0,7.42,4.59,10.42,15.19,15l6.18,2.65c21,9,32.86,18.19,32.86,38.86,0,22.26-17.49,34.45-41,34.45-23,0-37.8-11-45-25.26" transform="translate(-84.99 -85.07)"/><path d="M963.61,685.28S953.34,686,947,675a36.88,36.88,0,0,0-4.37-6.21,21,21,0,0,1-3.07-11.15c.35-8.15-8.15-52.79-8.15-52.79s1.06-17-7.44-27.63c-5.6-7-13.65-27-18.5-39.92a2.67,2.67,0,0,0,1.14.6l-.2-1.11.2,0L904.1,523a62.88,62.88,0,0,1-9.16-13.88l.57-.12,7.52-1.58s12.22-17.37,13-25.91l1,0h1.41a294,294,0,0,1,33.86,1.77h0c0-.05-.08-.27-.18-.62h0a42.5,42.5,0,0,1-1.1-5.23l.7,0,7-.46s24.09-26.92,15.23-31.88-18.07,22.32-18.07,22.32l-3,.54-.81.15c.08-.12.16-.23.25-.34S913,466,906.93,456.42c0,0-17.36-13.82-18.07-23a10.39,10.39,0,0,0-6.24-8.84,13.62,13.62,0,0,0-6.89-1.29l-8.13.56.57,1-.23,0a27.35,27.35,0,0,1-1.28-3.6l.17,0c-.06-.25-.11-.5-.16-.75a19.13,19.13,0,0,0,14.09-23.56,7.65,7.65,0,0,0,2.39-.07c3-.61,5.11-3.65,5.19-6.69,0-.2,0-.4,0-.59,0,0,0-.08,0-.12a10.28,10.28,0,0,0-3.95-7.9c-1.62-1.35-3.56-2.31-5-3.81-1.75-1.79-2.71-4.19-4.07-6.3a18.7,18.7,0,0,0-9.6-7.47c-2.93-1-6.53-1.1-8.78,1-1.16,1.09-1.83,2.67-3.13,3.59-1.87,1.34-4.41.94-6.7,1.21a12.78,12.78,0,0,0-9.63,7.21,16.61,16.61,0,0,0-1.58,7.62,20.35,20.35,0,0,0,.64,5.35c.44,1.74,1.09,3.42,1.57,5.16,1,3.6,1.23,7.39,2.38,10.94a13.77,13.77,0,0,0,4.43,6.73l-.34,1.46c-.22,0-5.74,1.23-9.55,9.56-3,6.57-13.21,13.35-17.7,16.09a7.38,7.38,0,0,0-3.34,4.64,24.1,24.1,0,0,0-.57,5.89c-.08,4.27.81,10.18,4.24,17.67,7.79,17,13.46,24.44,13.46,24.44s.43,5.21.46,10.7c0,4.09-.3,8.16-1.17,10.2-.74,1.72-1,6.11-.94,11.13,0,9.62.94,22.17.94,22.17s2.71.52,6.9,1.13l2.61,45.92a22.08,22.08,0,0,1-5,15.34,33.94,33.94,0,0,1-6.29,6c-7.44,5.31-23.38,61.29-23.38,61.29s.78,0,2.06.15l-4.9,13.67s19.58,15,31.49,10.9a11.54,11.54,0,0,1,4-.56c2.35,0,7.38-1.86,4.58-4.31a181.52,181.52,0,0,1-15.27-15.88,8.12,8.12,0,0,1,1.1.65v-.11l.17.11s0-.55.13-1.45l-.17-.06c.36-3.68,1.5-13,3.76-14.08,2.83-1.42,23.74-42.87,23.74-42.87l20.9-53.85s16.65,28.7,30.82,33.65v12s7.79,35.43,10.63,39.32,0,19.48,0,19.48a9.71,9.71,0,0,0,1.91,1.68l-2.27,12.84s16.3-.71,43.93,7.79C961.84,695.55,972.47,690.95,963.61,685.28ZM898.43,500l-3.45.86-.68.17c-.07-.43-.12-.68-.12-.68a7,7,0,0,1-2.51.61,46.15,46.15,0,0,1-2.1-11.95,80.52,80.52,0,0,1-2.11-14.8c0-2.58.43-4.83,1.4-6.1l1.15.09.8.76.06.05.41.37h0l.43.38.07.06.46.41.08.07.47.4,0,0,.53.44.12.1.56.45,0,0,.55.44.13.1.61.46.13.09.58.43.1.07.66.47.16.11.7.48.06,0,.68.45.16.11.73.47.16.1.68.42.13.08.78.46.19.11.81.45.06,0,.79.42.19.1.84.42.17.08.78.36.15.07.88.38.21.09.91.37h0C901.33,485.5,898.43,500,898.43,500Z" transform="translate(-84.99 -85.07)" fill="url(#a5312bde-8c7e-4767-9e17-08a43f21bf92-27)"/><path d="M811.05,668.16l-6.57,18.33s18,13.8,29.7,10.94a32.69,32.69,0,0,1,5.37-.87c2.42-.18,6.59-1.95,4-4.19-8.3-7.26-19.37-20.75-19.37-20.75Z" transform="translate(-84.99 -85.07)" fill="#4f4d59"/><path d="M920.34,666.43l-3.11,17.64s15.91-.69,42.89,7.61c0,0,10.38-4.5,1.73-10,0,0-10,.69-16.25-10s-10-9.34-10-9.34Z" transform="translate(-84.99 -85.07)" fill="#4f4d59"/><path d="M838,538.47l3,53.68a22.2,22.2,0,0,1-5.29,15.7,32.54,32.54,0,0,1-5.71,5.33c-7.26,5.19-22.83,59.83-22.83,59.83s17.64,1,22.48,4.5c0,0,1-13.83,3.8-15.22s23.17-41.85,23.17-41.85l20.41-52.57s16.25,28,30.09,32.86v11.76s7.61,34.59,10.38,38.39,0,19,0,19,8,10,26.28-.69c0,0-5.88-6.57-5.53-14.53s-8-51.53-8-51.53,1-16.6-7.26-27S901,526,901,526l-9.52-20.48Z" transform="translate(-84.99 -85.07)" fill="#c48c96"/><g opacity="0.1"><path d="M810.53,673s15.56-54.64,22.83-59.83a33,33,0,0,0,6-5.68,22,22,0,0,0,5-15.34l-3.05-53.69,51-31.44-.69-1.49L838.2,538.47l3.05,53.69a22,22,0,0,1-5,15.34,33,33,0,0,1-6,5.68C823,618.36,807.42,673,807.42,673s17.64,1,22.48,4.5c0,0,0-.54.13-1.41C823.22,673.75,810.53,673,810.53,673Z" transform="translate(-84.99 -85.07)"/><path d="M920.86,669.89s2.77-15.22,0-19-10.38-38.39-10.38-38.39V600.72c-13.83-4.84-30.09-32.86-30.09-32.86L879.19,571c4.69,7.44,17.2,25.9,28.19,29.74v11.76s7.61,34.59,10.38,38.39,0,19,0,19,3.86,4.86,12.38,4.2C923.77,673.56,920.86,669.89,920.86,669.89Z" transform="translate(-84.99 -85.07)"/></g><path d="M957,478.29l-6.79.45-3.58.24v-8.65l4.7-.85,2.91-.53s9-26.63,17.64-21.79S957,478.29,957,478.29Z" transform="translate(-84.99 -85.07)" fill="#714b4f"/><path d="M848.06,407s-4.5,17.29-5.88,28,32.16,2.08,32.16,2.08-12.8-15.56-5.53-23.86Z" transform="translate(-84.99 -85.07)" fill="#714b4f"/><path d="M884.37,479l.69,6.57-10.72-.69-10.72-9.34L852.9,445.44,846,434.71l-5.19-6.57,2.08-8s.95.83,2.55,2.05c5.27,4,17.62,12.41,26.65,10.09a10.76,10.76,0,0,0,1.23-.39l13.49,23.17Z" transform="translate(-84.99 -85.07)" fill="#e6e6f0"/><path d="M845.64,418.11s-5.53,1-9.34,9.34c-2.92,6.38-12.8,13-17.21,15.67a7.38,7.38,0,0,0-3.35,4.66c-.89,3.94-1.23,11.29,3.61,21.86,7.61,16.6,13.14,23.86,13.14,23.86s1.38,16.6-.69,21.44,0,31.47,0,31.47,36.31,6.92,48.42,0,10.38-33.55,10.38-33.55,11.41,24.21,15.56,24.9l-2.42-13.49s-13.49-14.53-14.18-33.2c0,0-4.15-16.95-.69-21.44,0,0,58.45,4.84,61.91.69,0,0-38.39-1.73-44.27-11.07,0,0-16.95-13.49-17.64-22.48a9.94,9.94,0,0,0-5.19-8.15,13.54,13.54,0,0,0-7.75-1.73l-7.81.54s13.49,23.86,12.8,27,.69,28.36.69,28.36S866,449.24,859.13,445.44,844.25,426,844.25,426l1.38-7.9" transform="translate(-84.99 -85.07)" opacity="0.1"/><path d="M884.37,479l.69,6.57-10.72-.69-10.72-9.34L852.9,445.44,846,434.71l-5.19-6.57,2.08-8s.95.83,2.55,2.05l-.48,2.73s8,15.62,14.87,19.43,22.48,37.35,22.48,37.35-1.38-25.25-.69-28.36c.47-2.11-5.59-13.77-9.53-21.06a10.76,10.76,0,0,0,1.23-.39l13.49,23.17Z" transform="translate(-84.99 -85.07)" opacity="0.1"/><path d="M950.21,478.74l-3.58.24v-8.65l4.7-.85C949.69,471.65,949.75,475.49,950.21,478.74Z" transform="translate(-84.99 -85.07)" opacity="0.1"/><path d="M845.64,417.08s-5.53,1-9.34,9.34c-2.92,6.38-12.8,13-17.21,15.67a7.38,7.38,0,0,0-3.35,4.66c-.89,3.94-1.23,11.29,3.61,21.86,7.61,16.6,13.14,23.86,13.14,23.86s1.38,16.6-.69,21.44,0,31.47,0,31.47,36.31,6.92,48.42,0,10.38-33.55,10.38-33.55S902,536,906.16,536.74l-2.42-13.49s-13.49-14.53-14.18-33.2c0,0-4.15-16.95-.69-21.44,0,0,13.49,14.53,28.71,14.18a286.36,286.36,0,0,1,33.2,1.73s-3.46-11.07,0-15.22c0,0-38.39-1.73-44.27-11.07,0,0-16.95-13.49-17.64-22.48a9.94,9.94,0,0,0-5.19-8.15,13.54,13.54,0,0,0-7.75-1.73l-7.81.54s13.49,23.86,12.8,27,.69,28.36.69,28.36S866,448.2,859.13,444.4,844.25,425,844.25,425l1.38-7.9" transform="translate(-84.99 -85.07)" fill="#c48c96"/><path d="M838,453.39s8.3,28,13.14,31.47,34.24,21.1,42.89,16.95c0,0,2.08,10.38-1,12.8,0,0-32.25-1.26-32.51-1.38,0,0-21.1-2.77-24.56-13.14s-7.61-28-7.61-28Z" transform="translate(-84.99 -85.07)" opacity="0.1"/><ellipse cx="797.31" cy="398.24" rx="1.21" ry="1.73" fill="#ba7855"/><ellipse cx="800.76" cy="410.34" rx="1.21" ry="1.73" fill="#ba7855"/><g opacity="0.1"><path d="M834.23,545s-2.08-26.63,0-31.47.69-21.44.69-21.44-5.53-7.26-13.14-23.86c-4.85-10.57-4.5-17.92-3.61-21.86a7.38,7.38,0,0,1,3.35-4.66c4.41-2.7,14.28-9.29,17.21-15.67,2.15-4.68,4.84-7.05,6.78-8.23l.13-.76s-5.53,1-9.34,9.34c-2.92,6.38-12.8,13-17.21,15.67a7.38,7.38,0,0,0-3.35,4.66c-.89,3.94-1.23,11.29,3.61,21.86,7.61,16.6,13.14,23.86,13.14,23.86s1.38,16.6-.69,21.44,0,31.47,0,31.47,25.94,4.94,41.28,2.29C857.59,549.49,834.23,545,834.23,545Z" transform="translate(-84.99 -85.07)"/><path d="M880.91,453.39c-.47,2.13,0,14.66.39,22.34,1.67,3.41,2.72,5.68,2.72,5.68s-1.38-25.25-.69-28.36c.66-3-11.49-24.66-12.7-26.8l-2.52.17S881.61,450.28,880.91,453.39Z" transform="translate(-84.99 -85.07)"/><path d="M920,482.44c-15.22.35-28.71-14.18-28.71-14.18a5.54,5.54,0,0,0-.87,1.87c4,3.72,15,12.93,27.16,12.65a286.36,286.36,0,0,1,33.2,1.73s-.07-.23-.18-.62A275.32,275.32,0,0,0,920,482.44Z" transform="translate(-84.99 -85.07)"/><path d="M893,511.49s.14,2.16.06,5.4c3.6,7.14,10.15,19.35,13.08,19.84l-.51-2.84C900.6,527.57,893,511.49,893,511.49Z" transform="translate(-84.99 -85.07)"/></g><path d="M902.7,508l-7.35,1.55-5.8,1.22,3.11-8.65,2.17-.54,3.37-.84s4.84-24.21,14.53-22.48S902.7,508,902.7,508Z" transform="translate(-84.99 -85.07)" fill="#714b4f"/><path d="M895.36,509.58l-5.8,1.22,3.11-8.65,2.17-.54A34.54,34.54,0,0,1,895.36,509.58Z" transform="translate(-84.99 -85.07)" opacity="0.1"/><path d="M838,452.7s8.3,28,13.14,31.47,34.24,21.1,42.89,16.95c0,0,2.08,10.38-1,12.8,0,0-32.25-1.26-32.51-1.38,0,0-21.1-2.77-24.56-13.14s-7.61-28-7.61-28Z" transform="translate(-84.99 -85.07)" fill="#c48c96"/><path d="M848.4,407.74s-.7,2.71-1.64,6.65a18.67,18.67,0,0,0,20.61,9.5c-.83-3.55-.65-7.14,1.78-9.92Z" transform="translate(-84.99 -85.07)" opacity="0.1"/><circle cx="777.94" cy="319.9" r="18.68" fill="#714b4f"/><path d="M879.15,399.83a10,10,0,0,0,4.16.26,6.85,6.85,0,0,0,5.06-6.53,10,10,0,0,0-3.85-7.71c-1.58-1.32-3.47-2.25-4.92-3.72-1.71-1.75-2.65-4.09-4-6.15a18.25,18.25,0,0,0-9.37-7.29c-2.86-1-6.37-1.08-8.57,1-1.13,1.07-1.79,2.6-3.05,3.51-1.83,1.3-4.31.92-6.54,1.18a12.47,12.47,0,0,0-9.4,7,17.61,17.61,0,0,0-.92,12c.43,1.7,1.07,3.34,1.53,5,1,3.52,1.2,7.21,2.32,10.68s3.43,6.87,6.93,7.91a21.91,21.91,0,0,1,2.23-11.29,3.24,3.24,0,0,1,1.58-1.76,3.82,3.82,0,0,1,1.9,0,19,19,0,0,0,12.65-2.56c1.33-.8,3.4-3.56,4.81-3.83C873.52,397.22,877.25,399.41,879.15,399.83Z" transform="translate(-84.99 -85.07)" opacity="0.1"/><path d="M879.15,399.14a10,10,0,0,0,4.16.26,6.85,6.85,0,0,0,5.06-6.53,10,10,0,0,0-3.85-7.71c-1.58-1.32-3.47-2.25-4.92-3.72-1.71-1.75-2.65-4.09-4-6.15a18.25,18.25,0,0,0-9.37-7.29c-2.86-1-6.37-1.08-8.57,1-1.13,1.07-1.79,2.6-3.05,3.51-1.83,1.3-4.31.92-6.54,1.18a12.47,12.47,0,0,0-9.4,7,17.61,17.61,0,0,0-.92,12c.43,1.7,1.07,3.34,1.53,5,1,3.52,1.2,7.21,2.32,10.68s3.43,6.87,6.93,7.91A21.91,21.91,0,0,1,850.78,405a3.24,3.24,0,0,1,1.58-1.76,3.82,3.82,0,0,1,1.9,0,19,19,0,0,0,12.65-2.56c1.33-.8,3.4-3.56,4.81-3.83C873.52,396.53,877.25,398.72,879.15,399.14Z" transform="translate(-84.99 -85.07)" fill="#503f43"/><path d="M1065.5,337.49l-15.24,170.7-68.5,19-68.31-19L898.23,337.49H1065.5Z" transform="translate(-84.99 -85.07)" fill="#e44d26"/><path d="M981.86,512.67l55.35-15.34,13-145.88H981.86Z" transform="translate(-84.99 -85.07)" fill="#f16529"/><path d="M952.24,393.33h29.62V372.39H929.36l.5,5.62,5.15,57.7h46.86V414.77H954.16ZM957,446.18h-21l2.93,32.88L981.77,491l.1,0V469.15l-.09,0-23.32-6.3L957,446.18Z" transform="translate(-84.99 -85.07)" fill="#ebebeb"/><path d="M913.8,291.09h10.64V301.6h9.73V291.09h10.64v31.84H934.17V312.27h-9.73v10.66H913.8V291.09Zm45,10.56h-9.37V291.09h29.38v10.56h-9.37v21.28H958.81V301.65h0Zm24.68-10.56h11.1l6.83,11.19,6.82-11.19h11.1v31.84h-10.6V307.15l-7.32,11.32h-.18l-7.33-11.32v15.78H983.48Zm41.14,0h10.64V312.4h15v10.52h-25.61V291.09Z" transform="translate(-84.99 -85.07)"/><path d="M981.79,435.71h25.78l-2.43,27.16-23.35,6.3V491l42.93-11.9.31-3.54,4.92-55.13.51-5.62H981.79v20.94Zm0-42.43v.05h50.58l.42-4.71,1-10.62.5-5.62H981.79v20.89Z" transform="translate(-84.99 -85.07)" fill="#fff"/><path d="M451.72,463.51c-1.95-5.71-4.76-13-7-14.66a2.09,2.09,0,0,0-.44-.27H444v-7.05l-5.26.84a50.27,50.27,0,0,1-1.91-7.81,3.63,3.63,0,0,0,3.95.79c-.09-.14-.16-.29-.24-.44l.24-.1a9.77,9.77,0,0,1-1.27-4.79,17.44,17.44,0,0,1,.73-4.52c.94-3.25,2.53-6.29,3.42-9.55a23.22,23.22,0,0,0,.8-6.46A23.54,23.54,0,0,0,442,398.84a6.22,6.22,0,0,0-1.86-2.43,24,24,0,0,0-2.28-1.11,12.31,12.31,0,0,1-3.31-3.3,29.74,29.74,0,0,0-14.37-9.59,8.41,8.41,0,0,0-4.59-.36c-3.53,1-4.82,5.33-7.68,7.62-2.53,2-6,2.23-9.16,2.91a15.58,15.58,0,0,0-8.53,4.47,9.26,9.26,0,0,0-2.29,6.18,6.21,6.21,0,0,0,2.53,5.52,7.93,7.93,0,0,0,6.7.95,22.79,22.79,0,0,0,17.25,28.65l.62,1.1h0a47.19,47.19,0,0,1,4.27,11.17c-4.84,2.75-10.13,5.32-13.32,5.56a22.45,22.45,0,0,1-.69,3.69,20.37,20.37,0,0,1-4.19,8.25c-4.34,4.88-23.87,41.78-23.87,41.78s-.62,1.47-1.62,4c-12.13,1.49-31.92,3.64-37.45,2.53l-21.7-20.62s-7,5.35-6.4,11.85l-3.91-3.72s-20.62,15.73,11.94,26l49.54,5.75c-4.72,14.24-9.12,30.81-7.76,39.83h.41a18.73,18.73,0,0,0,2.83-.22l-.09,1.6-4.77,85.43s-1.09,15.73,13.56,22.79c0,0,17.91,52.63,10.85,58.6,0,0,.84.44,2.25,1.06l-14.73,13s-10.85,2.71-16.82,2.17a4.31,4.31,0,0,0-4.76,3.21c0,.11,0,.22-.06.34l0,.14c0,.11,0,.23,0,.35s0,.09,0,.13,0,.2,0,.3l0,.13c0,.12,0,.23.07.35l0,.13q.05.19.12.37l0,.12c.05.12.11.24.17.36l0,.08c.07.12.14.24.22.36l.07.1q.13.19.29.38l.08.1c.11.13.23.25.36.38l.06,0,.4.34.09.07.49.35.12.08.57.33.09,0,.61.3.09,0,.71.29.15.06.79.26.13,0,.85.22.07,0,1,.19.19,0,1,.15.19,0,1.15.11c2.23.17,5.24.23,8.72.22h0s0,.09,0,.13,0,.2,0,.3l0,.13c0,.12,0,.23.07.35l0,.13q.05.19.12.37l0,.12c.05.12.11.24.17.36l0,.08c.07.12.14.24.22.36l.07.1q.13.19.29.38l.08.1c.11.13.23.25.36.38l.05,0,.4.34.09.07.49.35.12.08.56.33.09,0,.61.3.09,0,.71.29.15.06.79.26.13,0,.85.22.07,0,1,.19.19,0,1,.15.19,0,1.15.11c14.11,1.09,59.14-2.17,59.14-2.17l0-.13v0l0-.16v0c.45-2.1,2.61-14.31-5.07-23.34.23-.24.45-.49.66-.75,0,0-6-10.31-2.71-13.56S413.63,677,413.63,677s-10.31-16.28-4.34-34.73-.54-38.52-.54-38.52S423,599.53,423.26,582c0-.53,0-1.08,0-1.63a5.76,5.76,0,0,0,.9.06,4.21,4.21,0,0,0,.6,0s-2.71-9.77.54-16.82,10.31-.54,16.82-29.3c4.71-20.81,8.85-44.74,10.79-56.39A29.69,29.69,0,0,0,451.72,463.51Zm-123.82,61-4.95-4.7c1.64.68,3.46,1.34,5.49,2L347.09,524C338.77,524.75,331,525.17,327.9,524.54Z" transform="translate(-84.99 -85.07)" fill="url(#016d359d-0d48-4404-968f-1612e9b5c7a7-28)"/><path d="M375.13,521.77s-38.38,5.33-46.38,3.73l-21.32-20.26s-20.26,15.46,11.73,25.59l59.7,6.93Z" transform="translate(-84.99 -85.07)" fill="#fdc2cc"/><path d="M375.13,521.77s-38.38,5.33-46.38,3.73l-21.32-20.26s-20.26,15.46,11.73,25.59l59.7,6.93Z" transform="translate(-84.99 -85.07)" opacity="0.05"/><path d="M300,535.22l-82.94-23-18.48-207.3H401.67l-18.5,207.27L300,535.22Z" transform="translate(-84.99 -85.07)" fill="#264de4"/><path d="M367.33,499l15.81-177.12h-83V517.59L367.33,499Z" transform="translate(-84.99 -85.07)" fill="#2965f1"/><path d="M241,398.73l2.28,25.42h56.9V398.73Zm-4.58-51.46,2.31,25.42h61.44V347.27H236.37Zm63.75,117.49-.11,0-28.32-7.65-1.81-20.28H244.36l3.56,39.92L300,491.24l.12,0Z" transform="translate(-84.99 -85.07)" fill="#ebebeb"/><path d="M246.38,248.56h30.79v12.88H259.25v12.88h17.92v12.88H246.38Zm37,0h30.79v11.2H296.21V262h17.92v25.76H283.33V276h17.92v-2.24H283.33V248.56Zm37,0h30.79v11.2H333.16V262h17.92v25.76H320.28V276H338.2v-2.24H320.28V248.56Z" transform="translate(-84.99 -85.07)"/><path d="M359.14,398.73l4.59-51.46H300v25.42h35.83l-2.31,26H300v25.42h31.31l-3,33L300,464.78v26.45l52.12-14.45.38-4.3,6-66.94.62-6.83Z" transform="translate(-84.99 -85.07)" fill="#fff"/><path d="M389,738.19l-18.66,16.52s-10.66,2.67-16.52,2.13-8.53,9.06,5.33,10.13,58.1-2.13,58.1-2.13,4.26-17.06-8.53-26.65Z" transform="translate(-84.99 -85.07)" fill="#47465a"/><path d="M389,738.19l-18.66,16.52s-10.66,2.67-16.52,2.13-8.53,9.06,5.33,10.13,58.1-2.13,58.1-2.13,4.26-17.06-8.53-26.65Z" transform="translate(-84.99 -85.07)" opacity="0.05"/><path d="M417.91,757.47a29.4,29.4,0,0,1-.67,5.78s-44.24,3.2-58.1,2.13c-6.67-.51-9.5-3-10-5.38-.61,2.74,1.81,6.35,10,7,13.86,1.07,58.1-2.13,58.1-2.13A30.8,30.8,0,0,0,417.91,757.47Z" transform="translate(-84.99 -85.07)" fill="#fff" opacity="0.3"/><path d="M410.84,436s11.73,16.52,7.46,27.72,22.39-11.73,22.39-11.73S432.16,437,436.43,421Z" transform="translate(-84.99 -85.07)" fill="#fdc2cc"/><path d="M407.64,744.59,389,761.11s-10.66,2.67-16.52,2.13-8.53,9.06,5.33,10.13,58.1-2.13,58.1-2.13,4.26-17.06-8.53-26.65Z" transform="translate(-84.99 -85.07)" fill="#47465a"/><path d="M413,740.86c-2.78,3.43-6.7,4.75-10.8,4.95-9.32.44-19.58-4.95-19.58-4.95,6.93-5.86-10.66-57.57-10.66-57.57-14.39-6.93-13.33-22.39-13.33-22.39L363.29,577l1.18-21.08,14.93-9.06,11.76,9,11.16,8.56a44.86,44.86,0,0,1,1.4,8.32c1.3,19.59-14.19,24.2-14.19,24.2s6.4,19.72.53,37.85,4.26,34.12,4.26,34.12,19.19,55.44,16,58.64S413,740.86,413,740.86Z" transform="translate(-84.99 -85.07)" fill="#565171"/><path d="M413,740.86c-2.78,3.43-6.7,4.75-10.8,4.95-9.32.44-19.58-4.95-19.58-4.95,6.93-5.86-10.66-57.57-10.66-57.57-14.39-6.93-13.33-22.39-13.33-22.39L363.29,577l1.18-21.08,14.93-9.06,11.76,9,11.16,8.56a44.86,44.86,0,0,1,1.4,8.32c1.3,19.59-14.19,24.2-14.19,24.2s6.4,19.72.53,37.85,4.26,34.12,4.26,34.12,19.19,55.44,16,58.64S413,740.86,413,740.86Z" transform="translate(-84.99 -85.07)" opacity="0.05"/><path d="M431.63,747.25c-9.06,11.19-30.38,0-30.38,0a3.4,3.4,0,0,0,.93-1.45c3.91-10.63-13.19-61.45-13.19-61.45-14.39-6.93-16-23.45-16-23.45l-3.77-85.42-.49-11.06,22.43-8.56,6.89-2.63L421,570.82A41.57,41.57,0,0,1,422.44,582c-.29,17.21-14.26,21.37-14.26,21.37s6.4,19.72.53,37.85S413,675.29,413,675.29s19.19,55.44,16,58.64S431.63,747.25,431.63,747.25Z" transform="translate(-84.99 -85.07)" fill="#565171"/><path d="M422.44,582c-4.61-.66-13.94-6.21-18.73-9.22-1.83-1.15-3-1.92-3-1.92s-4.8-13.33-17.59-3.2a47.89,47.89,0,0,1-13.9,7.86,32.52,32.52,0,0,1-5.93,1.49l1.18-21.08,14.93-9.06,11.76,9,6.89-2.63L421,570.82A41.57,41.57,0,0,1,422.44,582Z" transform="translate(-84.99 -85.07)" opacity="0.1"/><path d="M451.29,479.71c-1.9,11.44-6,35-10.6,55.39-6.4,28.25-13.33,21.85-16.52,28.78s-.53,16.52-.53,16.52c-5.33.53-22.92-11.19-22.92-11.19s-4.8-13.33-17.59-3.2-22.92,9.59-22.92,9.59c-2.67-17.59,17.06-64.5,17.06-64.5s19.19-36.25,23.45-41a20,20,0,0,0,4.12-8.1,22.05,22.05,0,0,0,.68-3.62c6.93-.53,24-12.26,24-12.26l13.33-2.13v6.93a2,2,0,0,1,.44.27c2.19,1.67,5,8.79,6.87,14.4A29.17,29.17,0,0,1,451.29,479.71Z" transform="translate(-84.99 -85.07)" fill="#e1a0a7"/><path d="M389,529.77l-6.89-.8-52.81-6.13c-32-10.13-11.73-25.59-11.73-25.59l21.32,20.26c6.15,1.23,30.28-1.64,41.09-3,3.24-.42,5.29-.7,5.29-.7Z" transform="translate(-84.99 -85.07)" fill="#fdc2cc"/><path d="M440.69,457.81S426.83,446.62,406,482.86s-25.59,30.38-25.59,30.38,4.26,16,2.13,19.19c0,0,14.39-1.07,19.72,0s38.38-40.51,38.38-40.51S457.75,468.47,440.69,457.81Z" transform="translate(-84.99 -85.07)" opacity="0.1"/><path d="M440.16,455.68s-13.86-11.19-34.65,25.05-25.59,30.38-25.59,30.38,16.52,18.12,21.85,19.19,38.38-40.51,38.38-40.51S457.22,466.34,440.16,455.68Z" transform="translate(-84.99 -85.07)" opacity="0.1"/><path d="M382.1,529c.22-3.95-1.27-10.89-2.13-14.49,3.24-.42,5.29-.7,5.29-.7l3.73,16Z" transform="translate(-84.99 -85.07)" opacity="0.1"/><path d="M440.69,456.74S426.83,445.55,406,481.8s-25.59,30.38-25.59,30.38,4.26,16,2.13,19.19c0,0,14.39-1.07,19.72,0s38.38-40.51,38.38-40.51S457.75,467.4,440.69,456.74Z" transform="translate(-84.99 -85.07)" fill="#e1a0a7"/><path d="M437,422.09,411.37,437a54.69,54.69,0,0,1,3,4.88,22.49,22.49,0,0,0,4.48.45c6.83,0,10-7.17,14.13-12C432.68,426.49,435.82,426.39,437,422.09Z" transform="translate(-84.99 -85.07)" opacity="0.1"/><circle cx="333.31" cy="333.83" r="22.39" fill="#fdc2cc"/><path d="M390,400.79a15.3,15.3,0,0,1,8.38-4.4c3.11-.67,6.52-.87,9-2.86,2.81-2.25,4.07-6.53,7.54-7.49a8.27,8.27,0,0,1,4.51.36,29.21,29.21,0,0,1,14.12,9.42,12.09,12.09,0,0,0,3.26,3.25,23.6,23.6,0,0,1,2.24,1.09,6.11,6.11,0,0,1,1.82,2.39,23.18,23.18,0,0,1,1.62,16.27c-.87,3.21-2.43,6.19-3.36,9.39s-1.16,6.82.53,9.68c-1.64.84-3.71-.22-4.74-1.75s-1.34-3.41-1.88-5.18a19.75,19.75,0,0,0-5.78-9c-1.68-1.48-4.68-2.48-5.85-.57-.45.74-.42,1.69-.81,2.46-.76,1.51-3,1.81-4.43,1a6.79,6.79,0,0,1-2.77-4.11c-2.19-7.43-5.94-12.1-13.88-8.93-3,1.18-6.45,2-9.3,0C386.57,409.13,387.3,403.82,390,400.79Z" transform="translate(-84.99 -85.07)" opacity="0.1"/><path d="M390,400.25a15.3,15.3,0,0,1,8.38-4.4c3.11-.67,6.52-.87,9-2.86,2.81-2.25,4.07-6.53,7.54-7.49a8.27,8.27,0,0,1,4.51.36,29.21,29.21,0,0,1,14.12,9.42,12.09,12.09,0,0,0,3.26,3.25,23.6,23.6,0,0,1,2.24,1.09,6.11,6.11,0,0,1,1.82,2.39,23.18,23.18,0,0,1,1.62,16.27c-.87,3.21-2.43,6.19-3.36,9.39s-1.16,6.82.53,9.68c-1.64.84-3.71-.22-4.74-1.75s-1.34-3.41-1.88-5.18a19.75,19.75,0,0,0-5.78-9c-1.68-1.48-4.68-2.48-5.85-.57-.45.74-.42,1.69-.81,2.46-.76,1.51-3,1.81-4.43,1a6.79,6.79,0,0,1-2.77-4.11c-2.19-7.43-5.94-12.1-13.88-8.93-3,1.18-6.45,2-9.3,0C386.57,408.6,387.3,403.29,390,400.25Z" transform="translate(-84.99 -85.07)" fill="#8f5a6a"/><path d="M443.26,451.15H432.43s-21.32,10.66-21.85,10.13c-.36-.36-3.66.26-5.75.69a22.05,22.05,0,0,0,.68-3.62c6.93-.53,24-12.26,24-12.26l13.33-2.13v6.93A2,2,0,0,1,443.26,451.15Z" transform="translate(-84.99 -85.07)" opacity="0.05"/><path d="M436.56,763.86a29.4,29.4,0,0,1-.67,5.78s-44.24,3.2-58.1,2.13c-6.67-.51-9.5-3-10-5.38-.61,2.74,1.81,6.35,10,7,13.86,1.07,58.1-2.13,58.1-2.13A30.8,30.8,0,0,0,436.56,763.86Z" transform="translate(-84.99 -85.07)" fill="#fff" opacity="0.3"/><g opacity="0.05"><path d="M378.06,565.48c-7.19,5.7-13.55,8-17.73,9,0,.4.08.79.14,1.17,0,0,10.13.53,22.92-9.59a17.18,17.18,0,0,1,6.85-3.58C387.45,561.11,383.47,561.2,378.06,565.48Z" transform="translate(-84.99 -85.07)"/><path d="M450.4,465.54c-1.91-5.61-4.68-12.72-6.87-14.4a2,2,0,0,0-.44-.27s-4.41.15-4.26.27c2.19,1.67,4.33,8.26,6.24,13.86a29.17,29.17,0,0,1,1.16,14.16c-1.9,11.44-6,35-10.6,55.39-6.4,28.25-13.33,21.85-16.52,28.78-2.62,5.68-1.31,13.14-.74,15.65a13.41,13.41,0,0,0,5.54,1.41s-2.67-9.59.53-16.52,10.13-.53,16.52-28.78c4.63-20.44,8.7-44,10.6-55.39A29.17,29.17,0,0,0,450.4,465.54Z" transform="translate(-84.99 -85.07)"/></g><path d="M512.35,565.11a11.67,11.67,0,0,0,3.83-5.78c.5-2.3-.48-5.05-2.68-5.89-2.46-.94-5.09.76-7.08,2.49s-4.28,3.69-6.89,3.32a10.48,10.48,0,0,0,3.24-9.81,4.1,4.1,0,0,0-.9-2c-1.37-1.46-3.84-.83-5.48.32-5.2,3.66-6.65,10.72-6.68,17.08-.52-2.29-.08-4.68-.1-7s-.66-5-2.64-6.22a8,8,0,0,0-4-.95c-2.34-.09-4.94.15-6.54,1.86-2,2.12-1.47,5.69.26,8s4.35,3.8,6.77,5.42a15,15,0,0,1,4.84,4.61,4.57,4.57,0,0,1,.36.82h14.65A40.83,40.83,0,0,0,512.35,565.11Z" transform="translate(-84.99 -85.07)" fill="#f9a826"/><path d="M539.26,752.88c2.33-2.17,4.52-4.73,5.2-7.84s-.65-6.85-3.63-8c-3.33-1.28-6.9,1-9.6,3.37s-5.8,5-9.33,4.5a14.21,14.21,0,0,0,4.39-13.3,5.56,5.56,0,0,0-1.22-2.7c-1.85-2-5.21-1.13-7.42.43-7,5-9,14.53-9.05,23.15-.71-3.11-.11-6.35-.13-9.54s-.89-6.72-3.58-8.44a10.79,10.79,0,0,0-5.46-1.28c-3.17-.12-6.7.2-8.86,2.52-2.69,2.88-2,7.71.35,10.88s5.9,5.16,9.17,7.35a20.34,20.34,0,0,1,6.55,6.25,6.19,6.19,0,0,1,.49,1.12H527A55.36,55.36,0,0,0,539.26,752.88Z" transform="translate(-84.99 -85.07)" fill="#f9a826"/></svg>
\ No newline at end of file
Added +0 -0
diff --git a/src/assets/logo-mini.png b/src/assets/logo-mini.png
new file mode 100644
index 0000000..542c1bc
Binary files /dev/null and b/src/assets/logo-mini.png differ
Added +9 -0
diff --git a/src/assets/logo-mini1.svg b/src/assets/logo-mini1.svg
new file mode 100644
index 0000000..53df94c
--- /dev/null
+++ b/src/assets/logo-mini1.svg
@@ -0,0 +1,9 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 50" width="64" height="50">
+	<defs>
+		<image width="64" height="50" id="img1" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAAAyCAYAAADsg90UAAAAAXNSR0IB2cksfwAACI5JREFUeJy1WgtsW9UZdvNq0xU6WhZoCCVx7Gunbld1GSsMdYpGREhwr+2IMbQWqRoTk+iWPZi2wWhm1kwEmqV52feRJrbvvd6DiUnbQCAemzY2xh4tSB0wOqZIqAtLYzultIWEpt13btLq+sap/R8nRzpK4vh+5/++85///8851yEk1V+hX6B0tyE3OWwt0x789VR78AKlZ0KBz1oxBE3+BdUW9PNuQ7nJbk/BDQ/v4hi0x44z1R64jyrAVCjQ/fLNN8/ZoUWdgqGc57AFE6I86giH+QRw6XI9QE4RBz3iTAyVW3EyQdEDUmeIIhyeFNtMHO/o0B4e8qYASfVvLk2p4BKgdnioFCAvEQeddhnyFivOpOgvBaFXiAKcygQDrk927S/zjkae5xUA/X3YI/C5AFwHLrSPw+2+YYfCMugiCvDaCbGt0hc9uAXuP1OEAPACZQ+fAGguQ9nGgglx0OfsOAhq24lBcG/YYQa/kWLIz3XlCW4B6mKRCoCMEQdNu3VpnRVnMhBYA2L/LVCATCooXuNJSHXFzv58P+mMyyu5RQDAKNntdLnNjoN0+NMCM0D8901NDkGXuyBAseRZn3Ubagu3AFjTd1GXAdZdpCqurMgSIBS4BwRn8wgwje/d4pV6yz2adGQJyM/bo/ZyC+DSlWqApIiDHqtPyFdlCyBeD4Lv5xHgr5mAWO4ZjTRh9s8tlQDoryIbrOISoLYvvAIALxIHnIHn3GnFmfDfztLhH/IEv/vYd72xyKElJM/6Gdjj4fYCuNA3yYMa6ogdBwQfvIwAk+mguHZz3+MbMPsfLrEALD3v5RfAkH3UOID+Jqqwj2ULIDZeJvhF2He8I0NfX2ry8wI85VSy41LBzaUPsXT4L/Iy0OUbrTgpsXUlyI7lEOAsvGOboPRdKejK28shAPqpek1etxjHArxAiXCo/pAdB2QPLRQg8BLcv8ITi4h4bnaZBGD2+IsQQG0DCDUyv2LfjcHVQyB93iLAecz+bvY/T1x6YrnImwIkVakIAeSrAfIucdCz7qRcY8VJh8RrQDplWfvHxttaVvkGe6vg/h8tpwDoR926wpcOb9CkEhQ4Mbrqym4rTkrcydLhs5bU95Wwwwx+311m8qxPQ4CtvE6AdKjcLlCrQkNJXNXdnYUD0t+eF+AE1v76rV0/qvTEo/8kkkGJq/yFLIKhPsgtgEszl8E4cdAxPHdllgDBwGZW9k6Fguaa9A4PtMEwkrDe0chhCPAZ/E5dNn/kPiWqGe5mVSH1rJCdze2w4rDNDsi/gdn/FPtb0KTfUmeyQenvuE45WCLQd6unsc3fwKeAw0yHX+Zwu247DpbB3emQv3R7x7du8GgybdtrKGc39Ty23rQnqfKk5y9wCwD1XACZJg56xGUMZFVhx1taytjPBnWAfuihyT+3TEiAQwC1Mhzmqwrr9QhbBoeJg36A6Ou0Y/kGe2qQ+mhiGspHDcODTZfs0eRr8fkE0Z4xpyGt4RJgTnX1carq8Jw9dhzv6BB5k+VJyK9t6+y8dNK74bGHMSHK00Scc/CC7fwC6MotAjX6GuqTzsTBkosYW/f9YKUnIb1OFiAW/c4Ce5Lq18jLKKnu5xaApTWBHn0n8Nz6ixhw4zvJ5HXlvU2R3gWui02XizwhrEznbTW9vQ64nUEckBUut13EQOHzFFUA70jErBv+09xcYrXHqUXK8H9qIXXapSs1dm4FN1SF95DdzlD72LMNUt/15CMvXT65NfzIRtQOq5FCv7jAHkPppdqDZ3YvZFZgY8WEQN8dvvFxpB8hLvWT3T8uyWxckG9CEfX38fk0ahGgmboMMIk/2xg5UJKbYZ62sf9RbI7UPxGJzLgT0R2Y/RM0z1HO+QZ6Gl/3+dh2Wmdb6HQwUJc9IfI6fPc40Z5xCLeW2wvw8Pfoy0B5mfoMAuYLn+7oKE0FxZqp+YvWTHugw24PvvtLIjZLh83cAghzmxHqASb5qntz/4EvsfEy7UHroepvJoKtWdUcPPKrVGwsA6k2FuYTAPXAGoC8RfYCStflqRvv3VuB4Hc1SI9bBJjAZ1VWe+p1+TqBHpfG+M8Ksa0EgLacAnhiETNzIPjdbztLPIfPWrME0CSkQ+UfxDFmkQ5v5RPAYcYBckFTcDfUWc+hwZsw06tA+EiOi5RoDns66ctA/Qm3AE4tuhYgZ5Zl9uPR32165PtlINoMwjM5jtPfSvn9WTe/iEufw7PUW+U3a2NSKZ8CTU0s+DyzHAJgq9yuNDaywxNtkQuVWaTDBqs5SIdsQt4hjsXOCvmvzrDu6Fdnebo3Hj3u69xXngoFqkH09KL3ie2BB6y2XCsfZNt1apnOqsL7uY/KoB57k+SDJRUhIZlX2iD4UJ4b5ef+3dqabY9hlunUdPt0XXyIryqs1yKVAv3qbPFuKNNedaAR5FeD4NE8AqRTwZ1ZmxqnHv0EcI4Rxz3hNqSqxThevrEXqpJq31IJgOD3TN3+h0tYmmPpLt9bJex80W4SRBzmEF7kE8Bhbo5aONwutwCx6K4JUVyByu/JfOTn+6EFAiTNN1uoAgxyC1BvqFcAZLJo8pr8rlvuq8wExfpFUl+u/s7/dt6R9UKkS5ergHeaOP6Yc2SQLw7Mqa6SDznsHakvzLAyoeAPCyRvVoXpUGCb1ZZqRWFnhdQXPWcg3Jac5App7C2MImf/5Kb+nupJ0X8FSL1NEOACEyyHPQ9QbWA73CIEkBuEIqrCzUO9CsNB6esCqQ8pAqD/Oe33l9vs2SLQd6svuhJSWW6GeVptLFKGbMD9eltjZ2cjw5m4o60ChF4lCvBeJhjIuntAYF4N3KNEO9Lwgmo+F/D5mNt1cbr/2OeD916qx+HSB4gCsDdNdmXZM7dbjRJtYXeZd/EJ4DDX3Q4uARLSj604yO23UgVA2jRy2EO+OkNX/w+fZNm8pw5QbAAAAABJRU5ErkJggg=="/>
+	</defs>
+	<style>
+		tspan { white-space:pre }
+	</style>
+	<use id="Background" href="#img1" x="0" y="0" />
+</svg>
\ No newline at end of file
Added +26 -0
diff --git a/src/components/auth/auth.vue b/src/components/auth/auth.vue
new file mode 100644
index 0000000..0585888
--- /dev/null
+++ b/src/components/auth/auth.vue
@@ -0,0 +1,26 @@
+<template>
+	<slot v-if="getUserAuthBtnList" />
+</template>
+
+<script setup lang="ts" name="auth">
+import { computed } from 'vue';
+import { storeToRefs } from 'pinia';
+import { useUserInfo } from '/@/stores/userInfo';
+
+// 定义父组件传过来的值
+const props = defineProps({
+	value: {
+		type: String,
+		default: () => '',
+	},
+});
+
+// 定义变量内容
+const stores = useUserInfo();
+const { userInfos } = storeToRefs(stores);
+
+// 获取 pinia 中的用户权限
+const getUserAuthBtnList = computed(() => {
+	return userInfos.value.authBtnList.some((v: string) => v === props.value);
+});
+</script>
Added +27 -0
diff --git a/src/components/auth/authAll.vue b/src/components/auth/authAll.vue
new file mode 100644
index 0000000..54c8d58
--- /dev/null
+++ b/src/components/auth/authAll.vue
@@ -0,0 +1,27 @@
+<template>
+	<slot v-if="getUserAuthBtnList" />
+</template>
+
+<script setup lang="ts" name="authAll">
+import { computed } from 'vue';
+import { storeToRefs } from 'pinia';
+import { useUserInfo } from '/@/stores/userInfo';
+import { judementSameArr } from '/@/utils/arrayOperation';
+
+// 定义父组件传过来的值
+const props = defineProps({
+	value: {
+		type: Array,
+		default: () => [],
+	},
+});
+
+// 定义变量内容
+const stores = useUserInfo();
+const { userInfos } = storeToRefs(stores);
+
+// 获取 pinia 中的用户权限
+const getUserAuthBtnList = computed(() => {
+	return judementSameArr(props.value, userInfos.value.authBtnList);
+});
+</script>
Added +32 -0
diff --git a/src/components/auth/auths.vue b/src/components/auth/auths.vue
new file mode 100644
index 0000000..41b8b27
--- /dev/null
+++ b/src/components/auth/auths.vue
@@ -0,0 +1,32 @@
+<template>
+	<slot v-if="getUserAuthBtnList" />
+</template>
+
+<script setup lang="ts" name="auths">
+import { computed } from 'vue';
+import { storeToRefs } from 'pinia';
+import { useUserInfo } from '/@/stores/userInfo';
+
+// 定义父组件传过来的值
+const props = defineProps({
+	value: {
+		type: Array,
+		default: () => [],
+	},
+});
+
+// 定义变量内容
+const stores = useUserInfo();
+const { userInfos } = storeToRefs(stores);
+
+// 获取 pinia 中的用户权限
+const getUserAuthBtnList = computed(() => {
+	let flag = false;
+	userInfos.value.authBtnList.map((val: string) => {
+		props.value.map((v) => {
+			if (val === v) flag = true;
+		});
+	});
+	return flag;
+});
+</script>
Added +26 -0
diff --git a/src/components/checkboxGroup/index.vue b/src/components/checkboxGroup/index.vue
new file mode 100644
index 0000000..3a60e47
--- /dev/null
+++ b/src/components/checkboxGroup/index.vue
@@ -0,0 +1,26 @@
+<template>
+  <el-checkbox-group>
+    <el-checkbox v-for="item in myOptions" :key="item[props.valueKey]" v-bind="item" :label="item[props.valueKey]">
+      {{item[props.labelKey]}}
+    </el-checkbox>
+  </el-checkbox-group>
+  
+</template>
+
+<script setup lang="ts">
+import useOptions from '/@/hook/useOptions';
+interface Props {
+  options?: Array<EmptyObjectType>;
+  valueKey?: string;
+  labelKey?: string;
+  resultKey?: string;
+  api?: () => Promise<EmptyObjectType | Array<EmptyObjectType>>;
+  url?: string;
+}
+const props = withDefaults(defineProps<Props>(), {
+  valueKey: 'id',
+  labelKey: 'name',
+  resultKey: 'data',
+});
+const { myOptions } = useOptions(props)
+</script>
Added +143 -0
diff --git a/src/components/cropper/index.vue b/src/components/cropper/index.vue
new file mode 100644
index 0000000..d7448d9
--- /dev/null
+++ b/src/components/cropper/index.vue
@@ -0,0 +1,143 @@
+<template>
+	<div>
+		<el-dialog title="更换头像" v-model="state.isShowDialog" width="769px">
+			<div class="cropper-warp">
+				<div class="cropper-warp-left">
+					<img :src="state.cropperImg" class="cropper-warp-left-img" />
+				</div>
+				<div class="cropper-warp-right">
+					<div class="cropper-warp-right-title">预览</div>
+					<div class="cropper-warp-right-item">
+						<div class="cropper-warp-right-value">
+							<img :src="state.cropperImgBase64" class="cropper-warp-right-value-img" />
+						</div>
+						<div class="cropper-warp-right-label">100 x 100</div>
+					</div>
+					<div class="cropper-warp-right-item">
+						<div class="cropper-warp-right-value">
+							<img :src="state.cropperImgBase64" class="cropper-warp-right-value-img cropper-size" />
+						</div>
+						<div class="cropper-warp-right-label">50 x 50</div>
+					</div>
+				</div>
+			</div>
+			<template #footer>
+				<span class="dialog-footer">
+					<el-button @click="onCancel" size="default">取 消</el-button>
+					<el-button type="primary" @click="onSubmit" size="default">更 换</el-button>
+				</span>
+			</template>
+		</el-dialog>
+	</div>
+</template>
+
+<script setup lang="ts" name="cropper">
+import { reactive, nextTick } from 'vue';
+import Cropper from 'cropperjs';
+import 'cropperjs/dist/cropper.css';
+
+// 定义变量内容
+const state = reactive({
+	isShowDialog: false,
+	cropperImg: '',
+	cropperImgBase64: '',
+	cropper: '' as RefType,
+});
+
+// 打开弹窗
+const openDialog = (imgs: string) => {
+	state.cropperImg = imgs;
+	state.isShowDialog = true;
+	nextTick(() => {
+		initCropper();
+	});
+};
+// 关闭弹窗
+const closeDialog = () => {
+	state.isShowDialog = false;
+};
+// 取消
+const onCancel = () => {
+	closeDialog();
+};
+// 更换
+const onSubmit = () => {
+	// state.cropperImgBase64 = state.cropper.getCroppedCanvas().toDataURL('image/jpeg');
+};
+// 初始化cropperjs图片裁剪
+const initCropper = () => {
+	const letImg = <HTMLImageElement>document.querySelector('.cropper-warp-left-img');
+	state.cropper = new Cropper(letImg, {
+		viewMode: 1,
+		dragMode: 'none',
+		initialAspectRatio: 1,
+		aspectRatio: 1,
+		preview: '.before',
+		background: false,
+		autoCropArea: 0.6,
+		zoomOnWheel: false,
+		crop: () => {
+			state.cropperImgBase64 = state.cropper.getCroppedCanvas().toDataURL('image/jpeg');
+		},
+	});
+};
+
+// 暴露变量
+defineExpose({
+	openDialog,
+});
+</script>
+
+<style scoped lang="scss">
+.cropper-warp {
+	display: flex;
+	.cropper-warp-left {
+		position: relative;
+		display: inline-block;
+		height: 350px;
+		flex: 1;
+		border: 1px solid var(--el-border-color);
+		background: var(--el-color-white);
+		overflow: hidden;
+		background-repeat: no-repeat;
+		cursor: move;
+		border-radius: var(--el-border-radius-base);
+		.cropper-warp-left-img {
+			width: 100%;
+			height: 100%;
+		}
+	}
+	.cropper-warp-right {
+		width: 150px;
+		height: 350px;
+		.cropper-warp-right-title {
+			text-align: center;
+			height: 20px;
+			line-height: 20px;
+		}
+		.cropper-warp-right-item {
+			margin: 15px 0;
+			.cropper-warp-right-value {
+				display: flex;
+				.cropper-warp-right-value-img {
+					width: 100px;
+					height: 100px;
+					border-radius: var(--el-border-radius-circle);
+					margin: auto;
+				}
+				.cropper-size {
+					width: 50px;
+					height: 50px;
+				}
+			}
+			.cropper-warp-right-label {
+				text-align: center;
+				font-size: 12px;
+				color: var(--el-text-color-primary);
+				height: 30px;
+				line-height: 30px;
+			}
+		}
+	}
+}
+</style>
Added +101 -0
diff --git a/src/components/editor/index.vue b/src/components/editor/index.vue
new file mode 100644
index 0000000..c5fed8a
--- /dev/null
+++ b/src/components/editor/index.vue
@@ -0,0 +1,101 @@
+<template>
+	<div class="editor-container">
+		<Toolbar :editor="editorRef" :mode="mode" />
+		<Editor
+			:mode="mode"
+			:defaultConfig="state.editorConfig"
+			:style="{ height }"
+			v-model="state.editorVal"
+			@onCreated="handleCreated"
+			@onChange="handleChange"
+		/>
+	</div>
+</template>
+
+<script setup lang="ts" name="wngEditor">
+// https://www.wangeditor.com/v5/for-frame.html#vue3
+import '@wangeditor/editor/dist/css/style.css';
+import { reactive, shallowRef, watch, onBeforeUnmount } from 'vue';
+import { IDomEditor } from '@wangeditor/editor';
+import { Toolbar, Editor } from '@wangeditor/editor-for-vue';
+
+// 定义父组件传过来的值
+const props = defineProps({
+	// 是否禁用
+	disable: {
+		type: Boolean,
+		default: () => false,
+	},
+	// 内容框默认 placeholder
+	placeholder: {
+		type: String,
+		default: () => '请输入内容...',
+	},
+	// https://www.wangeditor.com/v5/getting-started.html#mode-%E6%A8%A1%E5%BC%8F
+	// 模式,可选 <default|simple>,默认 default
+	mode: {
+		type: String,
+		default: () => 'default',
+	},
+	// 高度
+	height: {
+		type: String,
+		default: () => '310px',
+	},
+	// 双向绑定,用于获取 editor.getHtml()
+	getHtml: String,
+	// 双向绑定,用于获取 editor.getText()
+	getText: String,
+});
+
+// 定义子组件向父组件传值/事件
+const emit = defineEmits(['update:getHtml', 'update:getText']);
+
+// 定义变量内容
+const editorRef = shallowRef();
+const state = reactive({
+	editorConfig: {
+		placeholder: props.placeholder,
+	},
+	editorVal: props.getHtml,
+});
+
+// 编辑器回调函数
+const handleCreated = (editor: IDomEditor) => {
+	editorRef.value = editor;
+};
+// 编辑器内容改变时
+const handleChange = (editor: IDomEditor) => {
+	emit('update:getHtml', editor.getHtml());
+	emit('update:getText', editor.getText());
+};
+// 页面销毁时
+onBeforeUnmount(() => {
+	const editor = editorRef.value;
+	if (editor == null) return;
+	editor.destroy();
+});
+// 监听是否禁用改变
+// https://gitee.com/lyt-top/vue-next-admin/issues/I4LM7I
+watch(
+	() => props.disable,
+	(bool) => {
+		const editor = editorRef.value;
+		if (editor == null) return;
+		bool ? editor.disable() : editor.enable();
+	},
+	{
+		deep: true,
+	}
+);
+// 监听双向绑定值改变,用于回显
+watch(
+	() => props.getHtml,
+	(val) => {
+		state.editorVal = val;
+	},
+	{
+		deep: true,
+	}
+);
+</script>
Added +23 -0
diff --git a/src/components/form/component.ts b/src/components/form/component.ts
new file mode 100644
index 0000000..91ac505
--- /dev/null
+++ b/src/components/form/component.ts
@@ -0,0 +1,23 @@
+import { ElAutocomplete, ElCascader, ElCheckbox, ElColorPicker, ElDatePicker, ElInput, ElInputNumber, ElRadio, ElRate, ElSlider, ElSwitch, ElTimePicker, ElTimeSelect, ElUpload } from "element-plus";
+import Select from '../select/index.vue'
+import CheckboxGroup from '../checkboxGroup/index.vue'
+import RadioGroup from '../radioGroup/index.vue'
+export const forms = {
+  autocomplete: ElAutocomplete,
+  cascader: ElCascader,
+  checkbox: ElCheckbox,
+  checkboxGroup: CheckboxGroup,
+  colorPicker: ElColorPicker,
+  datePicker: ElDatePicker,
+  input: ElInput,
+  inputNumber: ElInputNumber,
+  radioGroup: RadioGroup,
+  radio: ElRadio,
+  rate: ElRate,
+  select: Select,
+  slider: ElSlider,
+  switch: ElSwitch,
+  timePicker: ElTimePicker,
+  timeSelect: ElTimeSelect,
+  upload: ElUpload
+}
Added +32 -0
diff --git a/src/components/form/hooks/useForm.ts b/src/components/form/hooks/useForm.ts
new file mode 100644
index 0000000..f4a2889
--- /dev/null
+++ b/src/components/form/hooks/useForm.ts
@@ -0,0 +1,32 @@
+import { FormInstance } from "element-plus";
+import { computed, ref } from "vue";
+import { FormEmits, FormProps } from "../model/form";
+
+export default function useForm (props: FormProps, emits: FormEmits) {
+  const formEl = ref<FormInstance>();
+  const formValue = computed({
+    get () {
+      return props.modelValue;
+    },
+    set (val) {
+      emits('update:modelValue', val);
+    }
+  });
+  const onChange = (...props: any[]) => {
+    emits('change', ...props);
+  }
+  const getColBind = (col?: number | Col) => {
+    if (typeof col === 'number') {
+      return {
+        span: col
+      }
+    }
+    return col
+  }
+  return {
+    formEl,
+    formValue,
+    onChange,
+    getColBind
+  }
+}
\ No newline at end of file
Added +34 -0
diff --git a/src/components/form/hooks/useFormWrapper.ts b/src/components/form/hooks/useFormWrapper.ts
new file mode 100644
index 0000000..ccc40e4
--- /dev/null
+++ b/src/components/form/hooks/useFormWrapper.ts
@@ -0,0 +1,34 @@
+import { ElDialog, ElDrawer } from "element-plus";
+import { computed } from "vue";
+import { WrapperEmits, WrapperProps } from "../model/wrapper";
+
+
+export default function useFormWrapper (props: WrapperProps, emits: WrapperEmits) {
+  const layoutComponent = computed(() => {
+    const is = {
+      div: 'div',
+      dialog: ElDialog,
+      drawer: ElDrawer,
+    }
+    return is[props.wrapper]
+  })
+  const layoutBind = computed(() => {
+    return props.wrapper === 'div' ? {} : {
+      title: props.title,
+      [props.wrapper === 'dialog' ? 'width' : 'size']: '50%',
+    }
+  });
+  const layoutVisible = computed({
+    get () {
+      return props.visible
+    },
+    set (val) {
+      emits('update:visible', val)
+    }
+  })
+  return {
+    layoutComponent,
+    layoutBind,
+    layoutVisible
+  }
+}
Added +107 -0
diff --git a/src/components/form/index.vue b/src/components/form/index.vue
new file mode 100644
index 0000000..4f5729a
--- /dev/null
+++ b/src/components/form/index.vue
@@ -0,0 +1,107 @@
+<template>
+  <component :is="layoutComponent" v-bind="layoutBind" v-model="layoutVisible" class="form-wrapper">
+    <div class="h-full flex flex-col overflow-auto form-body">
+      <div class="flex-1 overflow-auto p-4">
+        <el-form ref="formEl" :model="formValue" size="default" label-width="100px" class="table-form">
+          <el-row>
+            <template v-for="(item, key) in config" :key="key">
+              <el-col v-bind="getColBind(item.col)" v-if="item.if === undefined ? true : item.if" v-show="item.show === undefined ? true : item.show">
+                <el-form-item
+                  class="form-item"
+                  :label="item.label"
+                  :prop="item.prop"
+                  :rules="[{ required: item.required, message: `${item.label}不能为空`, trigger: item.component === 'input' || item.component === 'inputNumber' ? 'blur' : 'change' }]"
+                  >
+                  <slot v-if="item.slot" :name="item.slot" :model="formValue" :prop="item.prop"></slot>
+                  <component v-else v-bind="item.props" v-model="formValue[item.prop]" :is="forms[item.component || 'input']" @change="onChange"></component>
+                </el-form-item>
+              </el-col>
+            </template>
+            <slot name="form-after"></slot>
+          </el-row>
+        </el-form>
+      </div>
+      <div class="p-4 flex justify-end border-t" v-if="handleVisible">
+        <el-button type="primary" @click="submit">
+          保存
+        </el-button>
+        <el-button @click="cancel">取消</el-button>
+      </div>
+    </div>
+  </component>
+</template>
+
+<script setup lang="ts">
+import { ElMessage } from 'element-plus';
+import { forms } from './component';
+import { ColumnConfig } from './model/form';
+import useFormWrapper from './hooks/useFormWrapper';
+// import { WrapperEmits, WrapperProps } from './model/wrapper';
+import useForm from './hooks/useForm';
+
+interface Props {
+  wrapper?: 'div' | 'dialog' | 'drawer';
+  title?: string;
+  visible?: boolean;
+  config: ColumnConfig[];
+  modelValue: EmptyObjectType;
+  handleVisible?: boolean;
+}
+interface Emits {
+  (e: 'update:visible', val: boolean): void;
+  (e: 'update:modelValue', val: EmptyObjectType): void;
+  (e: 'change', ...val: any[] ): void;
+  (e: 'submit', val: EmptyObjectType): void;
+  (e: 'cancel'): void;
+}
+const props = withDefaults(defineProps<Props>(), {
+  wrapper: 'div',
+  config: () => [],
+  modelValue: () => ({}),
+  handleVisible: true,
+});
+const emits = defineEmits<Emits>();
+
+const { layoutComponent, layoutBind, layoutVisible } = useFormWrapper(props, emits);
+const { formEl, formValue, onChange, getColBind } = useForm(props, emits)
+
+const submit = async () => {
+  if (!formEl.value) return
+  await formEl.value.validate((valid, fields) => {
+    if (valid) {
+      emits('submit', formValue.value)
+      layoutVisible.value = false;
+    } else {
+      // console.log('error submit!', fields)
+      ElMessage.error(fields)
+    }
+  })
+}
+const cancel = () => {
+  layoutVisible.value = false;
+  emits('cancel')
+}
+defineExpose({
+  formEl
+})
+</script>
+
+<style lang="scss" scoped>
+.form-item {
+  :deep(.el-date-editor),
+  :deep(.el-date-editor) .el-input__wrapper,
+  :deep(.el-select) {
+    width: 100%;
+  }
+}
+</style>
+<style lang="scss">
+.form-wrapper {
+  .el-dialog__body {
+    padding: 0 !important;
+    .form-body {
+      max-height: calc(90vh - 111px) !important;
+    }
+  }
+}
+</style>
\ No newline at end of file
Added +50 -0
diff --git a/src/components/form/model/form.ts b/src/components/form/model/form.ts
new file mode 100644
index 0000000..09db69b
--- /dev/null
+++ b/src/components/form/model/form.ts
@@ -0,0 +1,50 @@
+import { FormRule } from "@form-create/element-ui";
+import { DefineComponent } from "vue";
+import { forms } from '../component';
+
+export type FormType = typeof forms;
+export type InstanceProps<T extends DefineComponent> = InstanceType<T>['$props'];
+export type ColumnProp<T> = T extends keyof FormType ? InstanceType<FormType[T]>['$props'] : undefined;
+
+export interface ColumnConfig {
+  // 字段名
+	prop: string;
+  // 字段中文名称
+  label?: string;
+  // 组件
+	component?: keyof FormType;
+  // 是否渲染
+	if?: boolean;
+  // 是否显示
+	show?: boolean;
+  // 自定义组件插槽
+  slot?: string;
+  // 必填
+	required?: boolean;
+  // 组件参数
+	props?: ColumnProp<ColumnConfig['component']>;
+  // 校验规则
+  rules?: FormRule[];
+  // 排序下标
+  index?: number;
+  // 所占列数
+	col?: number | Col;
+  // isCheck?: boolean;
+}
+
+export interface Col {
+  xs: number;
+  sm: number;
+  md: number;
+  lg: number;
+  xl: number;
+}
+
+export interface FormProps {
+  config: ColumnConfig[];
+  modelValue: EmptyObjectType;
+}
+export interface FormEmits {
+  (e: 'update:modelValue', val: EmptyObjectType): void;
+  (e: 'change', ...val: any[] ): void;
+}
\ No newline at end of file
Added +9 -0
diff --git a/src/components/form/model/wrapper.ts b/src/components/form/model/wrapper.ts
new file mode 100644
index 0000000..4118a97
--- /dev/null
+++ b/src/components/form/model/wrapper.ts
@@ -0,0 +1,9 @@
+export interface WrapperProps {
+  wrapper: 'div' | 'dialog' | 'drawer';
+  title?: string;
+  visible: boolean
+}
+
+export interface WrapperEmits {
+  (e: 'update:visible', val: boolean): void;
+}
\ No newline at end of file
Added +241 -0
diff --git a/src/components/iconSelector/index.vue b/src/components/iconSelector/index.vue
new file mode 100644
index 0000000..0c44d11
--- /dev/null
+++ b/src/components/iconSelector/index.vue
@@ -0,0 +1,241 @@
+<template>
+	<div class="icon-selector w100 h100">
+		<el-input
+			v-model="state.fontIconSearch"
+			:placeholder="state.fontIconPlaceholder"
+			:clearable="clearable"
+			:disabled="disabled"
+			:size="size"
+			ref="inputWidthRef"
+			@clear="onClearFontIcon"
+			@focus="onIconFocus"
+			@blur="onIconBlur"
+		>
+			<template #prepend>
+				<SvgIcon
+					:name="state.fontIconPrefix === '' ? prepend : state.fontIconPrefix"
+					class="font14"
+					v-if="state.fontIconPrefix === '' ? prepend?.indexOf('ele-') > -1 : state.fontIconPrefix?.indexOf('ele-') > -1"
+				/>
+				<i v-else :class="state.fontIconPrefix === '' ? prepend : state.fontIconPrefix" class="font14"></i>
+			</template>
+		</el-input>
+		<el-popover
+			placement="bottom"
+			:width="state.fontIconWidth"
+			transition="el-zoom-in-top"
+			popper-class="icon-selector-popper"
+			trigger="click"
+			:virtual-ref="inputWidthRef"
+			virtual-triggering
+		>
+			<template #default>
+				<div class="icon-selector-warp">
+					<div class="icon-selector-warp-title">{{ title }}</div>
+					<el-tabs v-model="state.fontIconTabActive" @tab-click="onIconClick">
+						<el-tab-pane lazy label="ali" name="ali">
+							<IconList :list="fontIconSheetsFilterList" :empty="emptyDescription" :prefix="state.fontIconPrefix" @get-icon="onColClick" />
+						</el-tab-pane>
+						<el-tab-pane lazy label="ele" name="ele">
+							<IconList :list="fontIconSheetsFilterList" :empty="emptyDescription" :prefix="state.fontIconPrefix" @get-icon="onColClick" />
+						</el-tab-pane>
+						<el-tab-pane lazy label="awe" name="awe">
+							<IconList :list="fontIconSheetsFilterList" :empty="emptyDescription" :prefix="state.fontIconPrefix" @get-icon="onColClick" />
+						</el-tab-pane>
+					</el-tabs>
+				</div>
+			</template>
+		</el-popover>
+	</div>
+</template>
+
+<script setup lang="ts" name="iconSelector">
+import { defineAsyncComponent, ref, reactive, onMounted, nextTick, computed, watch } from 'vue';
+import type { TabsPaneContext } from 'element-plus';
+import initIconfont from '/@/utils/getStyleSheets';
+import '/@/theme/iconSelector.scss';
+
+// 定义父组件传过来的值
+const props = defineProps({
+	// 输入框前置内容
+	prepend: {
+		type: String,
+		default: () => 'ele-Pointer',
+	},
+	// 输入框占位文本
+	placeholder: {
+		type: String,
+		default: () => '请输入内容搜索图标或者选择图标',
+	},
+	// 输入框占位文本
+	size: {
+		type: String,
+		default: () => 'default',
+	},
+	// 弹窗标题
+	title: {
+		type: String,
+		default: () => '请选择图标',
+	},
+	// 禁用
+	disabled: {
+		type: Boolean,
+		default: () => false,
+	},
+	// 是否可清空
+	clearable: {
+		type: Boolean,
+		default: () => true,
+	},
+	// 自定义空状态描述文字
+	emptyDescription: {
+		type: String,
+		default: () => '无相关图标',
+	},
+	// 双向绑定值,默认为 modelValue,
+	// 参考:https://v3.cn.vuejs.org/guide/migration/v-model.html#%E8%BF%81%E7%A7%BB%E7%AD%96%E7%95%A5
+	// 参考:https://v3.cn.vuejs.org/guide/component-custom-events.html#%E5%A4%9A%E4%B8%AA-v-model-%E7%BB%91%E5%AE%9A
+	modelValue: String,
+});
+
+// 定义子组件向父组件传值/事件
+const emit = defineEmits(['update:modelValue', 'get', 'clear']);
+
+// 引入组件
+const IconList = defineAsyncComponent(() => import('/@/components/iconSelector/list.vue'));
+
+// 定义变量内容
+const inputWidthRef = ref();
+const state = reactive({
+	fontIconPrefix: '',
+	fontIconWidth: 0,
+	fontIconSearch: '',
+	fontIconPlaceholder: '',
+	fontIconTabActive: 'ali',
+	fontIconList: {
+		ali: [],
+		ele: [],
+		awe: [],
+	},
+});
+
+// 处理 input 获取焦点时,modelValue 有值时,改变 input 的 placeholder 值
+const onIconFocus = () => {
+	if (!props.modelValue) return false;
+	state.fontIconSearch = '';
+	state.fontIconPlaceholder = props.modelValue;
+};
+// 处理 input 失去焦点时,为空将清空 input 值,为点击选中图标时,将取原先值
+const onIconBlur = () => {
+	const list = fontIconTabNameList();
+	setTimeout(() => {
+		const icon = list.filter((icon: string) => icon === state.fontIconSearch);
+		if (icon.length <= 0) state.fontIconSearch = '';
+	}, 300);
+};
+// 图标搜索及图标数据显示
+const fontIconSheetsFilterList = computed(() => {
+	const list = fontIconTabNameList();
+	if (!state.fontIconSearch) return list;
+	let search = state.fontIconSearch.trim().toLowerCase();
+	return list.filter((item: string) => {
+		if (item.toLowerCase().indexOf(search) !== -1) return item;
+	});
+});
+// 根据 tab name 类型设置图标
+const fontIconTabNameList = () => {
+	let iconList: any = [];
+	if (state.fontIconTabActive === 'ali') iconList = state.fontIconList.ali;
+	else if (state.fontIconTabActive === 'ele') iconList = state.fontIconList.ele;
+	else if (state.fontIconTabActive === 'awe') iconList = state.fontIconList.awe;
+	return iconList;
+};
+// 处理 icon 双向绑定数值回显
+const initModeValueEcho = () => {
+	if (props.modelValue === '') return ((<string | undefined>state.fontIconPlaceholder) = props.placeholder);
+	(<string | undefined>state.fontIconPlaceholder) = props.modelValue;
+	(<string | undefined>state.fontIconPrefix) = props.modelValue;
+};
+// 处理 icon 类型,用于回显时,tab 高亮与初始化数据
+const initFontIconName = () => {
+	let name = 'ali';
+	if (props.modelValue!.indexOf('iconfont') > -1) name = 'ali';
+	else if (props.modelValue!.indexOf('ele-') > -1) name = 'ele';
+	else if (props.modelValue!.indexOf('fa') > -1) name = 'awe';
+	// 初始化 tab 高亮回显
+	state.fontIconTabActive = name;
+	return name;
+};
+// 初始化数据
+const initFontIconData = async (name: string) => {
+	if (name === 'ali') {
+		// 阿里字体图标使用 `iconfont xxx`
+		if (state.fontIconList.ali.length > 0) return;
+		await initIconfont.ali().then((res: any) => {
+			state.fontIconList.ali = res.map((i: string) => `iconfont ${i}`);
+		});
+	} else if (name === 'ele') {
+		// element plus 图标
+		if (state.fontIconList.ele.length > 0) return;
+		await initIconfont.ele().then((res: any) => {
+			state.fontIconList.ele = res;
+		});
+	} else if (name === 'awe') {
+		// fontawesome字体图标使用 `fa xxx`
+		if (state.fontIconList.awe.length > 0) return;
+		await initIconfont.awe().then((res: any) => {
+			state.fontIconList.awe = res.map((i: string) => `fa ${i}`);
+		});
+	}
+	// 初始化 input 的 placeholder
+	// 参考(单项数据流):https://cn.vuejs.org/v2/guide/components-props.html?#%E5%8D%95%E5%90%91%E6%95%B0%E6%8D%AE%E6%B5%81
+	state.fontIconPlaceholder = props.placeholder;
+	// 初始化双向绑定回显
+	initModeValueEcho();
+};
+// 图标点击切换
+const onIconClick = (pane: TabsPaneContext) => {
+	initFontIconData(pane.paneName as string);
+	inputWidthRef.value.focus();
+};
+// 获取当前点击的 icon 图标
+const onColClick = (v: string) => {
+	state.fontIconPlaceholder = v;
+	state.fontIconPrefix = v;
+	emit('get', state.fontIconPrefix);
+	emit('update:modelValue', state.fontIconPrefix);
+	inputWidthRef.value.focus();
+};
+// 清空当前点击的 icon 图标
+const onClearFontIcon = () => {
+	state.fontIconPrefix = '';
+	emit('clear', state.fontIconPrefix);
+	emit('update:modelValue', state.fontIconPrefix);
+};
+// 获取 input 的宽度
+const getInputWidth = () => {
+	nextTick(() => {
+		state.fontIconWidth = inputWidthRef.value.$el.offsetWidth;
+	});
+};
+// 监听页面宽度改变
+const initResize = () => {
+	window.addEventListener('resize', () => {
+		getInputWidth();
+	});
+};
+// 页面加载时
+onMounted(() => {
+	initFontIconData(initFontIconName());
+	initResize();
+	getInputWidth();
+});
+// 监听双向绑定 modelValue 的变化
+watch(
+	() => props.modelValue,
+	() => {
+		initModeValueEcho();
+		initFontIconName();
+	}
+);
+</script>
Added +84 -0
diff --git a/src/components/iconSelector/list.vue b/src/components/iconSelector/list.vue
new file mode 100644
index 0000000..8bf837b
--- /dev/null
+++ b/src/components/iconSelector/list.vue
@@ -0,0 +1,84 @@
+<template>
+	<div class="icon-selector-warp-row">
+		<el-scrollbar ref="selectorScrollbarRef">
+			<el-row :gutter="10" v-if="props.list.length > 0">
+				<el-col :xs="6" :sm="4" :md="4" :lg="4" :xl="4" v-for="(v, k) in list" :key="k" @click="onColClick(v)">
+					<div class="icon-selector-warp-item" :class="{ 'icon-selector-active': prefix === v }">
+						<SvgIcon :name="v" />
+					</div>
+				</el-col>
+			</el-row>
+			<el-empty :image-size="100" v-if="list.length <= 0" :description="empty"></el-empty>
+		</el-scrollbar>
+	</div>
+</template>
+
+<script setup lang="ts" name="iconSelectorList">
+// 定义父组件传过来的值
+const props = defineProps({
+	// 图标列表数据
+	list: {
+		type: Array,
+		default: () => [],
+	},
+	// 自定义空状态描述文字
+	empty: {
+		type: String,
+		default: () => '无相关图标',
+	},
+	// 高亮当前选中图标
+	prefix: {
+		type: String,
+		default: () => '',
+	},
+});
+
+// 定义子组件向父组件传值/事件
+const emit = defineEmits(['get-icon']);
+
+// 当前 icon 图标点击时
+const onColClick = (v: unknown | string) => {
+	emit('get-icon', v);
+};
+</script>
+
+<style scoped lang="scss">
+.icon-selector-warp-row {
+	height: 230px;
+	overflow: hidden;
+	.el-row {
+		padding: 15px;
+	}
+	.el-scrollbar__bar.is-horizontal {
+		display: none;
+	}
+	.icon-selector-warp-item {
+		display: flex;
+		justify-content: center;
+		align-items: center;
+		border: 1px solid var(--el-border-color);
+		border-radius: 5px;
+		margin-bottom: 10px;
+		height: 30px;
+		i {
+			font-size: 20px;
+			color: var(--el-text-color-regular);
+		}
+		&:hover {
+			cursor: pointer;
+			background-color: var(--el-color-primary-light-9);
+			border: 1px solid var(--el-color-primary-light-5);
+			i {
+				color: var(--el-color-primary);
+			}
+		}
+	}
+	.icon-selector-active {
+		background-color: var(--el-color-primary-light-9);
+		border: 1px solid var(--el-color-primary-light-5);
+		i {
+			color: var(--el-color-primary);
+		}
+	}
+}
+</style>
Added +191 -0
diff --git a/src/components/noticeBar/index.vue b/src/components/noticeBar/index.vue
new file mode 100644
index 0000000..8198bce
--- /dev/null
+++ b/src/components/noticeBar/index.vue
@@ -0,0 +1,191 @@
+<template>
+	<div class="notice-bar" :style="{ background, height: `${height}px` }" v-show="!state.isMode">
+		<div class="notice-bar-warp" :style="{ color, fontSize: `${size}px` }">
+			<i v-if="leftIcon" class="notice-bar-warp-left-icon" :class="leftIcon"></i>
+			<div class="notice-bar-warp-text-box" ref="noticeBarWarpRef">
+				<div class="notice-bar-warp-text" ref="noticeBarTextRef" v-if="!scrollable">{{ text }}</div>
+				<div class="notice-bar-warp-slot" v-else><slot /></div>
+			</div>
+			<SvgIcon :name="rightIcon" v-if="rightIcon" class="notice-bar-warp-right-icon" @click="onRightIconClick" />
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts" name="noticeBar">
+import { reactive, ref, onMounted, nextTick } from 'vue';
+
+// 定义父组件传过来的值
+const props = defineProps({
+	// 通知栏模式,可选值为 closeable link
+	mode: {
+		type: String,
+		default: () => '',
+	},
+	// 通知文本内容
+	text: {
+		type: String,
+		default: () => '',
+	},
+	// 通知文本颜色
+	color: {
+		type: String,
+		default: () => 'var(--el-color-warning)',
+	},
+	// 通知背景色
+	background: {
+		type: String,
+		default: () => 'var(--el-color-warning-light-9)',
+	},
+	// 字体大小,单位px
+	size: {
+		type: [Number, String],
+		default: () => 14,
+	},
+	// 通知栏高度,单位px
+	height: {
+		type: Number,
+		default: () => 40,
+	},
+	// 动画延迟时间 (s)
+	delay: {
+		type: Number,
+		default: () => 1,
+	},
+	// 滚动速率 (px/s)
+	speed: {
+		type: Number,
+		default: () => 100,
+	},
+	// 是否开启垂直滚动
+	scrollable: {
+		type: Boolean,
+		default: () => false,
+	},
+	// 自定义左侧图标
+	leftIcon: {
+		type: String,
+		default: () => '',
+	},
+	// 自定义右侧图标
+	rightIcon: {
+		type: String,
+		default: () => '',
+	},
+});
+
+// 定义子组件向父组件传值/事件
+const emit = defineEmits(['close', 'link']);
+
+// 定义变量内容
+const noticeBarWarpRef = ref();
+const noticeBarTextRef = ref();
+const state = reactive({
+	order: 1,
+	oneTime: 0,
+	twoTime: 0,
+	warpOWidth: 0,
+	textOWidth: 0,
+	isMode: false,
+});
+
+// 初始化 animation 各项参数
+const initAnimation = () => {
+	nextTick(() => {
+		state.warpOWidth = noticeBarWarpRef.value.offsetWidth;
+		state.textOWidth = noticeBarTextRef.value.offsetWidth;
+		document.styleSheets[0].insertRule(`@keyframes oneAnimation {0% {left: 0px;} 100% {left: -${state.textOWidth}px;}}`);
+		document.styleSheets[0].insertRule(`@keyframes twoAnimation {0% {left: ${state.warpOWidth}px;} 100% {left: -${state.textOWidth}px;}}`);
+		computeAnimationTime();
+		setTimeout(() => {
+			changeAnimation();
+		}, props.delay * 1000);
+	});
+};
+// 计算 animation 滚动时长
+const computeAnimationTime = () => {
+	state.oneTime = state.textOWidth / props.speed;
+	state.twoTime = (state.textOWidth + state.warpOWidth) / props.speed;
+};
+// 改变 animation 动画调用
+const changeAnimation = () => {
+	if (state.order === 1) {
+		noticeBarTextRef.value.style.cssText = `animation: oneAnimation ${state.oneTime}s linear; opactity: 1;}`;
+		state.order = 2;
+	} else {
+		noticeBarTextRef.value.style.cssText = `animation: twoAnimation ${state.twoTime}s linear infinite; opacity: 1;`;
+	}
+};
+// 监听 animation 动画的结束
+const listenerAnimationend = () => {
+	noticeBarTextRef.value.addEventListener(
+		'animationend',
+		() => {
+			changeAnimation();
+		},
+		false
+	);
+};
+// 右侧 icon 图标点击
+const onRightIconClick = () => {
+	if (!props.mode) return false;
+	if (props.mode === 'closeable') {
+		state.isMode = true;
+		emit('close');
+	} else if (props.mode === 'link') {
+		emit('link');
+	}
+};
+// 页面加载时
+onMounted(() => {
+	if (props.scrollable) return false;
+	initAnimation();
+	listenerAnimationend();
+});
+</script>
+
+<style scoped lang="scss">
+.notice-bar {
+	padding: 0 15px;
+	width: 100%;
+	border-radius: 4px;
+	.notice-bar-warp {
+		display: flex;
+		align-items: center;
+		width: 100%;
+		height: inherit;
+		.notice-bar-warp-text-box {
+			flex: 1;
+			height: inherit;
+			display: flex;
+			align-items: center;
+			overflow: hidden;
+			position: relative;
+			.notice-bar-warp-text {
+				white-space: nowrap;
+				position: absolute;
+				left: 0;
+			}
+			.notice-bar-warp-slot {
+				width: 100%;
+				white-space: nowrap;
+				:deep(.el-carousel__item) {
+					display: flex;
+					align-items: center;
+				}
+			}
+		}
+		.notice-bar-warp-left-icon {
+			width: 24px;
+			font-size: inherit !important;
+		}
+		.notice-bar-warp-right-icon {
+			width: 24px;
+			text-align: right;
+			font-size: inherit !important;
+			&:hover {
+				cursor: pointer;
+			}
+		}
+	}
+}
+</style>
Added +88 -0
diff --git a/src/components/page/edit.vue b/src/components/page/edit.vue
new file mode 100644
index 0000000..51bbedd
--- /dev/null
+++ b/src/components/page/edit.vue
@@ -0,0 +1,88 @@
+<template>
+  <div v-loading="loading">
+    <Form :config="config" v-model="formData" v-model:visible="layoutVisible" :wrapper="wrapper" :title="isUpdate?'修改':'添加'" @submit="submit">
+      <template v-for="item in config.filter(item => item.slot)" :key="item.prop" #[`${item.slot!}`]="data">
+				<slot :name="item.slot" :model="data.model" :prop="data.prop"></slot>
+			</template>
+    </Form>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { computed, ref } from 'vue';
+import { usePageApi } from '/@/api/page';
+import Form from '/@/components/form/index.vue';
+import { ColumnConfig } from '../form/model/form';
+import { ElMessage } from 'element-plus';
+import { EventSettingRef } from './model';
+interface Props {
+  type: string;
+  wrapper?: 'div' | 'dialog' | 'drawer';
+  visible: boolean;
+  modelValue?: EmptyObjectType;
+  config: ColumnConfig[];
+}
+interface Emits {
+  (e: 'update:visible', val: boolean): void;
+  (e: 'update:modelValue', val: EmptyObjectType): void;
+  (e: 'submitSuccess'): void;
+}
+const props = withDefaults(defineProps<Props>(), {
+  modelValue: () => ({})
+});
+const emits = defineEmits<Emits>();
+const layoutVisible = computed({
+  get () {
+    return props.visible
+  },
+  set (val) {
+    emits('update:visible', val)
+  }
+})
+const loading = ref(false);
+const formData = computed({
+  get () {
+    return props.modelValue || {}
+  },
+  set (val) {
+    emits('update:modelValue', val)
+  }
+});
+const isUpdate = ref(false);
+const pageApi = usePageApi();
+
+const handleAdd = () => {
+  if (isUpdate.value) {
+    formData.value = {}
+  }
+  isUpdate.value = false;
+  layoutVisible.value = true;
+}
+const handleEdit = (id: number) => {
+  formData.value = {}
+  isUpdate.value = true;
+  layoutVisible.value = true;
+  loading.value = true
+  pageApi.getTableDetail(props.type, id).then(res => {
+    loading.value = false
+		formData.value = res.data
+	}).catch(() => {
+    loading.value = false
+  })
+}
+const submit = () => {
+  const submitApi = isUpdate.value ? pageApi.setTableItem : pageApi.addTableItem
+  submitApi(props.type, formData.value).then(() => {
+    ElMessage.success('保存成功!')
+    emits('submitSuccess')
+  })
+}
+defineExpose({
+  handleAdd,
+  handleEdit
+})
+</script>
+
+<style scoped>
+
+</style>
\ No newline at end of file
Added +64 -0
diff --git a/src/components/page/hook/useGetColumnsForm.ts b/src/components/page/hook/useGetColumnsForm.ts
new file mode 100644
index 0000000..eac8b1f
--- /dev/null
+++ b/src/components/page/hook/useGetColumnsForm.ts
@@ -0,0 +1,64 @@
+import { ref, Ref, watch } from "vue";
+import { ColumnConfig } from "../../form/model/form";
+import { TableColumn } from "../../table/type";
+import { EventSettingRef } from "../model";
+import { ColumnKind, usePageApi } from "/@/api/page";
+import { getFormConfigByFields, toCamelCase } from "/@/utils/other";
+interface Props {
+  type: string;
+	searchData?: EmptyObjectType;
+}
+interface Emits {
+	(e: 'update:searchData', val: EmptyObjectType): void;
+	(e: 'setting', val: EventSettingRef): void;
+}
+
+export default function useGetColumnsForm (props: Props, emits: Emits) {
+  const pageApi = usePageApi()
+
+  const editForm = ref({});
+  
+  const searchForm = ref<EmptyObjectType>(props.searchData || {});
+  watch(() => props.searchData, (val) => {
+    searchForm.value = val || {}
+  })
+  watch(searchForm, (val) => {
+    emits('update:searchData', val)
+  })
+
+  const editConfig = ref([]) as Ref<ColumnConfig[]>;
+  const addConfig = ref([]) as Ref<ColumnConfig[]>;
+  const search = ref([]) as Ref<ColumnConfig[]>;
+  const columns = ref([]) as Ref<TableColumn[]>;
+  const col = { xs: 24, sm: 12, md: 8, lg: 8, xl: 6 }
+  pageApi.getColumns(props.type, ColumnKind.SEARCH).then(res => {
+    search.value = getFormConfigByFields(res.data, () => ({
+      required: false,
+      col
+    }))
+    emits('setting', { type: ColumnKind.SEARCH, config: search, formData: searchForm })
+  })
+  pageApi.getColumns(props.type, ColumnKind.LIST).then(res => {
+    columns.value = getFormConfigByFields(res.data, (item) => ({
+      prop: toCamelCase(item.name),
+    }))
+    emits('setting', { type: ColumnKind.LIST, config: columns })
+  })
+  pageApi.getColumns(props.type, ColumnKind.EDIT).then(res => {
+    editConfig.value = getFormConfigByFields(res.data)
+    emits('setting', { type: ColumnKind.EDIT, config: editConfig, formData: editForm })
+  })
+  pageApi.getColumns(props.type, ColumnKind.ADD).then(res => {
+    addConfig.value = getFormConfigByFields(res.data)
+    emits('setting', { type: ColumnKind.ADD, config: addConfig, formData: editForm })
+  })
+
+  return {
+    searchForm,
+    editForm,
+    editConfig,
+    addConfig,
+    search,
+    columns
+  }
+}
\ No newline at end of file
Added +139 -0
diff --git a/src/components/page/index.vue b/src/components/page/index.vue
new file mode 100644
index 0000000..4a2534e
--- /dev/null
+++ b/src/components/page/index.vue
@@ -0,0 +1,139 @@
+<template>
+	<div class="table-demo-container layout-padding">
+		<div class="table-demo-padding layout-padding-view layout-padding-auto">
+			<Table
+				ref="tableRef"
+				:data="data"
+				:config="config"
+				v-model:columns="columns"
+				:search="search"
+				:param="param"
+				:pager-visible="pagerVisible"
+				v-model:search-data="searchForm"
+				class="table-demo"
+				@del="onTableDelRow"
+				@add="onTableAdd"
+				@edit="onTableEdit"
+				@search="onSearch"
+				@sortHeader="onSortHeader">
+				<template v-for="item in search.filter(v => v.slot)" :key="item.prop" #[`${item.slot}`]="data">
+					<slot :name="item.slot" :model="data.model" :prop="data.prop"></slot>
+				</template>
+				<template v-for="item in columns.filter(v => v.slot)" :key="item.prop" #[`${item.slot}`]="data">
+					<slot :name="item.slot" :scope="data.scope"></slot>
+				</template>
+			</Table>
+		</div>
+		<Edit ref="editEl" v-model:visible="editVisible" :type="type" wrapper="drawer" :config="isUpdate ? editConfig : addConfig" :key="type" v-model="editForm" :isUpdate="isUpdate" @submit-success="getTableData">
+			<template v-for="item in editConfig.filter(item => item.slot)" :key="item.prop" #[`${item.slot}`]="data">
+				<slot :name="item.slot" :model="data.model" :prop="data.prop"></slot>
+			</template>
+		</Edit>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { defineAsyncComponent, ref } from 'vue';
+import { ElMessage } from 'element-plus';
+import { usePageApi } from '../../api/page';
+import Edit from './edit.vue';
+import { TableColumn } from '../table/type';
+import { EventSettingRef } from './model';
+import useGetColumnsForm from './hook/useGetColumnsForm';
+
+interface Props {
+	type: string;
+	searchData?: EmptyObjectType;
+}
+interface Emits {
+	(e: 'update:searchData', val: EmptyObjectType): void;
+	(e: 'setting', val: EventSettingRef): void;
+}
+const props = withDefaults(defineProps<Props>(), {
+	searchData: () => ({})
+});
+const emits = defineEmits<Emits>();
+const pageApi = usePageApi()
+const editVisible = ref(false);
+// 引入组件
+const Table = defineAsyncComponent(() => import('/@/components/table/index.vue'));
+
+// 初始化配置项数据与表单数据
+const { searchForm, editForm, editConfig, addConfig, search, columns } = useGetColumnsForm(props, emits)
+
+// 定义变量内容
+const tableRef = ref<RefType>();
+const isUpdate = ref(false);
+const pagerVisible = ref(false);
+const data = ref<EmptyObjectType[]>([]);
+const editEl = ref<InstanceType<typeof Edit>>();
+const config = ref<TableConfigType>({
+	total: 0, // 列表总数
+	loading: true, // loading 加载
+	isBorder: false, // 是否显示表格边框
+	isSerialNo: false, // 是否显示表格序号
+	isSelection: true, // 是否显示表格多选
+	isOperate: true, // 是否显示表格操作栏
+})
+const param = ref({
+	pageIndex: 1,
+	pageSize: 20,
+})
+
+// 初始化列表数据
+const getTableData = () => {
+	config.value.loading = true;
+	data.value = [];
+	pageApi.getTableData(props.type, { ...param.value, ...searchForm.value }).then(res => {
+		if (Array.isArray(res.data)) {
+			data.value = res.data || []
+		}
+		if (res.page) {
+			pagerVisible.value = true
+			config.value.total = res.page.totalCount
+		}
+		config.value.loading = false;
+	}).catch(() => {
+		config.value.loading = false;
+	})
+};
+
+// 删除当前项回调
+const onTableDelRow = (item: EmptyObjectType) => {
+	pageApi.delTableItem(props.type, item.id).then(() =>{
+		ElMessage.success(`删除成功!`);
+		getTableData();
+	})
+	getTableData();
+};
+const onTableAdd = () => {
+	editEl.value?.handleAdd();
+}
+const onTableEdit = (item: EmptyObjectType) => {
+	editEl.value?.handleEdit(item.id);
+}
+// 分页改变时回调
+const onSearch = (page: TableDemoPageType) => {
+	param.value.pageIndex = page.pageIndex;
+	param.value.pageSize = page.pageSize;
+	getTableData();
+};
+// 拖动显示列排序回调
+const onSortHeader = (data: TableColumn[]) => {
+	columns.value = data;
+};
+getTableData();
+
+</script>
+
+<style scoped lang="scss">
+.table-demo-container {
+	.table-demo-padding {
+		padding: 15px;
+		.table-demo {
+			flex: 1;
+			overflow: hidden;
+		}
+	}
+}
+</style>
Added +44 -0
diff --git a/src/components/page/model/index.ts b/src/components/page/model/index.ts
new file mode 100644
index 0000000..b635ab8
--- /dev/null
+++ b/src/components/page/model/index.ts
@@ -0,0 +1,44 @@
+import { Ref } from "vue";
+import { ColumnConfig } from "../../form/model/form";
+import { TableColumn } from "../../table/type";
+import { ColumnKind } from "/@/api/page";
+
+export interface TableDemoState {
+	tableData: {
+		data: EmptyObjectType[];
+		header: TableColumn[];
+		config: {
+			total: number;
+			loading: boolean;
+			isBorder: boolean;
+			isSelection: boolean;
+			isSerialNo: boolean;
+			isOperate: boolean;
+		};
+		search: ColumnConfig[];
+		param: {
+			pageNum: number;
+			pageSize: number;
+		};
+	};
+}
+
+type EventSettingRefConfig<T> = T extends ColumnKind.LIST ? Array<TableColumn> : Array<ColumnConfig>
+
+export interface EventSettingRef {
+	type: ColumnKind;
+	config: Ref<EventSettingRefConfig<EventSettingRef['type']>>;
+	formData?: Ref<EmptyObjectType>;
+}
+
+// type EventSettingPropsList = {
+// 	in: ColumnKind.LIST
+// } & TableColumn
+// type EventSettingPropsForm = {
+// 	in: ColumnKind.ADD | ColumnKind.DETAIL | ColumnKind.EDIT | ColumnKind.SEARCH
+// } & ColumnConfig
+// type EventSettingPropsCommon = ({in?: ColumnKind[]} & TableColumn) | ({ in?: ColumnKind[]} & ColumnConfig);
+
+export interface EventSettingProps {
+	columns?: Array<({ in?: ColumnKind | Array<ColumnKind> } & TableColumn) | ({ in?: ColumnKind | Array<ColumnKind> } & ColumnConfig)>
+}
Added +26 -0
diff --git a/src/components/radioGroup/index.vue b/src/components/radioGroup/index.vue
new file mode 100644
index 0000000..9627e07
--- /dev/null
+++ b/src/components/radioGroup/index.vue
@@ -0,0 +1,26 @@
+<template>
+  <el-radio-group>
+    <el-radio v-for="item in myOptions" :key="item[props.valueKey]" v-bind="item" :label="item[props.valueKey]">
+      {{item[props.labelKey]}}
+    </el-radio>
+  </el-radio-group>
+  
+</template>
+
+<script setup lang="ts">
+import useOptions from '/@/hook/useOptions';
+interface Props {
+  options?: Array<EmptyObjectType>;
+  valueKey?: string;
+  labelKey?: string;
+  resultKey?: string;
+  api?: () => Promise<EmptyObjectType | Array<EmptyObjectType>>;
+  url?: string;
+}
+const props = withDefaults(defineProps<Props>(), {
+  valueKey: 'id',
+  labelKey: 'name',
+  resultKey: 'data',
+});
+const { myOptions } = useOptions(props)
+</script>
Added +33 -0
diff --git a/src/components/select/index.vue b/src/components/select/index.vue
new file mode 100644
index 0000000..5085aac
--- /dev/null
+++ b/src/components/select/index.vue
@@ -0,0 +1,33 @@
+<template>
+  <el-select v-bind="$attrs">
+    <el-option v-for="item in myOptions"
+      :key="item[valueKey]"
+      :label="item[labelKey]"
+      :disabled="item.disabled"
+      :value="item[valueKey]">
+    </el-option>
+  </el-select>
+</template>
+
+<script setup lang="ts">
+import useOptions from '/@/hook/useOptions';
+interface Props {
+  options?: Array<EmptyObjectType>;
+  valueKey?: string;
+  labelKey?: string;
+  resultKey?: string;
+  api?: () => Promise<EmptyObjectType | Array<EmptyObjectType>>;
+  url?: string;
+}
+const props = withDefaults(defineProps<Props>(), {
+  valueKey: 'id',
+  labelKey: 'name',
+  resultKey: 'data',
+});
+const { myOptions } = useOptions(props)
+
+</script>
+
+<style scoped>
+
+</style>
\ No newline at end of file
Added +63 -0
diff --git a/src/components/svgIcon/index.vue b/src/components/svgIcon/index.vue
new file mode 100644
index 0000000..576ee4c
--- /dev/null
+++ b/src/components/svgIcon/index.vue
@@ -0,0 +1,63 @@
+<template>
+	<i v-if="isShowIconSvg" class="el-icon" :style="setIconSvgStyle">
+		<component :is="getIconName" />
+	</i>
+	<div v-else-if="isShowIconImg" :style="setIconImgOutStyle">
+		<img :src="getIconName" :style="setIconSvgInsStyle" />
+	</div>
+	<Icon v-else-if="getIconName?.indexOf(':') !== -1" :icon="getIconName" :style="setIconSvgStyle"/>
+	<i v-else :class="getIconName" :style="setIconSvgStyle" />
+</template>
+
+<script setup lang="ts" name="svgIcon">
+import { computed } from 'vue';
+// 定义父组件传过来的值
+const props = defineProps({
+	// svg 图标组件名字
+	name: {
+		type: String,
+	},
+	// svg 大小
+	size: {
+		type: Number,
+		default: () => 14,
+	},
+	// svg 颜色
+	color: {
+		type: String,
+	},
+});
+
+// 在线链接、本地引入地址前缀
+// https://gitee.com/lyt-top/vue-next-admin/issues/I62OVL
+const linesString = ['https', 'http', '/src', '/assets', 'data:image', import.meta.env.VITE_PUBLIC_PATH];
+
+// 获取 icon 图标名称
+const getIconName = computed(() => {
+	return props?.name;
+});
+// 用于判断 element plus 自带 svg 图标的显示、隐藏
+const isShowIconSvg = computed(() => {
+	return props?.name?.startsWith('ele-');
+});
+// 用于判断在线链接、本地引入等图标显示、隐藏
+const isShowIconImg = computed(() => {
+	return linesString.find((str) => props.name?.startsWith(str));
+});
+// 设置图标样式
+const setIconSvgStyle = computed(() => {
+	return `font-size: ${props.size}px;color: ${props.color};`;
+});
+// 设置图片样式
+const setIconImgOutStyle = computed(() => {
+	return `width: ${props.size}px;height: ${props.size}px;display: inline-block;overflow: hidden;`;
+});
+// 设置图片样式
+// https://gitee.com/lyt-top/vue-next-admin/issues/I59ND0
+const setIconSvgInsStyle = computed(() => {
+	const filterStyle: string[] = [];
+	const compatibles: string[] = ['-webkit', '-ms', '-o', '-moz'];
+	compatibles.forEach((j) => filterStyle.push(`${j}-filter: drop-shadow(${props.color} 30px 0);`));
+	return `width: ${props.size}px;height: ${props.size}px;position: relative;left: -${props.size}px;${filterStyle.join('')}`;
+});
+</script>
Added +328 -0
diff --git a/src/components/table/index.vue b/src/components/table/index.vue
new file mode 100644
index 0000000..5cfc9a7
--- /dev/null
+++ b/src/components/table/index.vue
@@ -0,0 +1,328 @@
+<template>
+	<div class="table-container">
+		<Search :search="search" @search="onSearch" v-model="searchDataRef">
+			<template #handle-after>
+				<el-button size="default" type="primary" @click="onAdd">添加 </el-button>
+			</template>
+			<template v-for="item in search.filter(item => item.slot)" :key="item.prop" #[`${item.slot}`]="data">
+				<slot :name="item.slot" :model="data.model" :prop="data.prop"></slot>
+			</template>
+		</Search>
+		<el-table
+			:data="data"
+			:border="setBorder"
+			v-bind="$attrs"
+			row-key="id"
+			stripe
+			style="width: 100%"
+			v-loading="config.loading"
+			@selection-change="onSelectionChange">
+			<el-table-column type="selection" :reserve-selection="true" width="40" fixed="left" v-if="config.isSelection && setHeader.length" />
+			<el-table-column type="index" label="序号" width="60" v-if="config.isSerialNo && setHeader.length" />
+			<template v-for="(item, index) in setHeader" :key="index">
+				<el-table-column
+					show-overflow-tooltip
+					:prop="item.prop"
+					:width="item.width"
+					:label="item.label"
+					v-if="item.if === undefined || item.if"
+				>
+					<template v-slot="scope">
+						<slot v-if="item.slot" :name="item.slot" :scope="scope" :model="scope.row" :prop="item.prop"></slot>
+						<template v-else-if="item.component === 'upload'">
+							<img :src="scope.row[item.prop]" width="50px" height="50px" />
+						</template>
+						<template v-else-if="item.component === 'switch'">
+							<el-tag :type="scope.row[item.prop] ? '' : 'info'" effect="dark">
+								<Icon v-if="scope.row[item.prop]" icon="material-symbols:check" class="text-lg"></Icon>
+								<Icon v-else icon="material-symbols:close" class="text-lg"></Icon>
+							</el-tag>
+						</template>
+						<template v-else>
+							{{ scope.row[item.prop] }}
+						</template>
+					</template>
+				</el-table-column>
+			</template>
+			<el-table-column label="操作" width="100" fixed="right" v-if="config.isOperate && setHeader.length">
+				<template v-slot="scope">
+					<el-button text type="primary" @click="onEdit(scope.row)">修改</el-button>
+					<el-popconfirm title="确定删除吗?" @confirm="onDel(scope.row)">
+						<template #reference>
+							<el-button text type="danger">删除</el-button>
+						</template>
+					</el-popconfirm>
+				</template>
+			</el-table-column>
+			<template #empty>
+				<el-empty description="暂无数据" />
+			</template>
+			<slot></slot>
+		</el-table>
+		<div class="table-footer mt15">
+			<el-pagination
+				v-if="pagerVisible"
+				v-model:current-page="state.page.pageIndex"
+				v-model:page-size="state.page.pageSize"
+				:pager-count="5"
+				:page-sizes="[10, 20, 30]"
+				:total="config.total"
+				layout="total, sizes, prev, pager, next, jumper"
+				background
+				@size-change="onHandleSizeChange"
+				@current-change="onHandleCurrentChange"
+			>
+			</el-pagination>
+			<div class="table-footer-tool">
+				<SvgIcon name="iconfont icon-yunxiazai_o" :size="22" title="导出" @click="onImportTable" />
+				<SvgIcon name="iconfont icon-shuaxin" :size="22" title="刷新" @click="onRefreshTable" />
+				<el-popover
+					placement="top-end"
+					trigger="click"
+					transition="el-zoom-in-top"
+					popper-class="table-tool-popper"
+					:width="300"
+					:persistent="false"
+					@show="onSetTable"
+				>
+					<template #reference>
+						<SvgIcon name="iconfont icon-quanjushezhi_o" :size="22" title="设置" />
+					</template>
+					<template #default>
+						<div class="tool-box">
+							<el-tooltip content="拖动进行排序" placement="top-start">
+								<SvgIcon name="fa fa-question-circle-o" :size="17" class="ml11" color="#909399" />
+							</el-tooltip>
+							<el-checkbox
+								v-model="state.checkListAll"
+								:indeterminate="state.checkListIndeterminate"
+								class="ml10 mr1"
+								label="列显示"
+								@change="onCheckAllChange"
+							/>
+							<el-checkbox v-model="getConfig.isSerialNo" class="ml12 mr1" label="序号" />
+							<el-checkbox v-model="getConfig.isSelection" class="ml12 mr1" label="多选" />
+						</div>
+						<el-scrollbar>
+							<draggable
+								class="tool-sortable"
+								v-model="tableColumns" 
+								item-key="prop">
+								<template #item="{element}">
+									<div class="tool-sortable-item">
+										<i class="fa fa-arrows-alt handle cursor-pointer"></i>
+										<el-checkbox v-model="element.isCheck" size="default" class="ml12 mr8" :label="element.label" @change="onCheckChange" />
+									</div>
+								</template>
+							</draggable>
+							<!-- <div ref="toolSetRef" class="tool-sortable">
+								<div class="tool-sortable-item" v-for="v in columns" :key="v.prop" :data-key="v.prop">
+									<i class="fa fa-arrows-alt handle cursor-pointer"></i>
+									<el-checkbox v-model="v.isCheck" size="default" class="ml12 mr8" :label="v.label" @change="onCheckChange" />
+								</div>
+							</div> -->
+						</el-scrollbar>
+					</template>
+				</el-popover>
+			</div>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts" name="netxTable">
+import { reactive, computed, nextTick, watch, Ref } from 'vue';
+import { ElMessage } from 'element-plus';
+import table2excel from 'js-table2excel';
+import { storeToRefs } from 'pinia';
+import { useThemeConfig } from '/@/stores/themeConfig';
+import '/@/theme/tableTool.scss';
+import Search from './search.vue';
+import { ColumnConfig } from '../form/model/form';
+import { TableColumn } from './type';
+import draggable from 'vuedraggable'
+import { useVModel } from '@vueuse/core';
+// import useVModel from '/@/hook/useVModel';
+interface Param {
+	pageIndex?: number;
+	pageSize?: number;
+}
+interface Props {
+	data: EmptyObjectType[];
+	columns: TableColumn[];
+	config: TableConfigType;
+	search: ColumnConfig[];
+	param: Param;
+	pagerVisible: boolean;
+	searchData: EmptyObjectType;
+}
+// 定义父组件传过来的值
+const props = withDefaults(defineProps<Props>(), {
+	data: () => [],
+	columns: () => [],
+	config: () => ({
+		total: 0, // 列表总数
+		loading: true, // loading 加载
+		isBorder: false, // 是否显示表格边框
+		isSerialNo: false, // 是否显示表格序号
+		isSelection: true, // 是否显示表格多选
+		isOperate: true, // 是否显示表格操作栏
+	}),
+	search: () => [],
+	param: () => ({
+		pageIndex: 1,
+		pageSize: 10,
+	}),
+	pagerVisible: true,
+	searchData: () => ({})
+});
+
+// 定义子组件向父组件传值/事件
+const emit = defineEmits(['del', 'edit', 'add', 'search', 'sortHeader', 'update:searchData', 'update:columns']);
+const tableColumns = useVModel(props, 'columns', emit) as Ref<TableColumn[]>;
+// 定义变量内容
+// const toolSetRef = ref();
+const storesThemeConfig = useThemeConfig();
+const { themeConfig } = storeToRefs(storesThemeConfig);
+const state = reactive({
+	page: props.param,
+	selectlist: [] as EmptyObjectType[],
+	checkListAll: true,
+	checkListIndeterminate: false,
+});
+// const searchDataRef = useVModel(props, 'searchData', emit);
+const searchDataRef = computed({
+	get () {
+		return props.searchData || {}
+	},
+	set (val) {
+		emit('update:searchData', val)
+	}
+})
+
+// 设置边框显示/隐藏
+const setBorder = computed(() => {
+	return props.config.isBorder ? true : false;
+});
+// 获取父组件 配置项(必传)
+const getConfig = computed(() => {
+	return props.config;
+});
+// 设置 tool columns 数据
+const setHeader = computed(() => {
+	return props.columns.filter((v) => v.isCheck);
+});
+watch(() => props.columns, (val) => {
+	val.forEach(item => {
+		if (item.isCheck === undefined) {
+			item.isCheck = true
+		}
+	})
+})
+// tool 列显示全选改变时
+const onCheckAllChange = <T>(val: T) => {
+	if (val) props.columns.forEach((v) => (v.isCheck = true));
+	else props.columns.forEach((v) => (v.isCheck = false));
+	state.checkListIndeterminate = false;
+};
+// tool 列显示当前项改变时
+const onCheckChange = () => {
+	const headers = props.columns.filter((v) => v.isCheck).length;
+	state.checkListAll = headers === props.columns.length;
+	state.checkListIndeterminate = headers > 0 && headers < props.columns.length;
+};
+// 表格多选改变时,用于导出
+const onSelectionChange = (val: EmptyObjectType[]) => {
+	state.selectlist = val;
+};
+const onEdit = (row: EmptyObjectType) => {
+	emit('edit', row);
+}
+// 删除当前项
+const onDel = (row: EmptyObjectType) => {
+	emit('del', row);
+};
+const onAdd = () => {
+	emit('add');
+}
+// 分页改变
+const onHandleSizeChange = (val: number) => {
+	state.page.pageSize = val;
+	state.page.pageIndex = 1
+	emit('search', state.page);
+};
+// 分页改变
+const onHandleCurrentChange = (val: number) => {
+	state.page.pageIndex = val;
+	emit('search', state.page);
+};
+// 搜索时,分页还原成默认
+const pageReset = () => {
+	state.page.pageIndex = 1;
+	emit('search', state.page);
+};
+// 导出
+const onImportTable = () => {
+	if (state.selectlist.length <= 0) return ElMessage.warning('请先选择要导出的数据');
+	table2excel(props.columns, state.selectlist, `${themeConfig.value.globalTitle} ${new Date().toLocaleString()}`);
+};
+// 刷新
+const onRefreshTable = () => {
+	emit('search', state.page);
+};
+// 设置
+const onSetTable = () => {
+	nextTick(() => {
+		// const sortable = Sortable.create(toolSetRef.value, {
+		// 	handle: '.handle',
+		// 	dataIdAttr: 'data-key',
+		// 	animation: 150,
+		// 	onEnd: () => {
+		// 		const headerList: EmptyObjectType[] = [];
+		// 		sortable.toArray().forEach((val) => {
+		// 			props.columns.forEach((v) => {
+		// 				if (v.prop === val) headerList.push({ ...v });
+		// 			});
+		// 		});
+		// 		emit('sortHeader', headerList);
+		// 	},
+		// });
+	});
+};
+
+const onSearch = (data: EmptyObjectType) => {
+	state.page = Object.assign({}, state.page, { ...data });
+	pageReset();
+};
+
+// 暴露变量
+defineExpose({
+	pageReset,
+});
+</script>
+
+<style scoped lang="scss">
+.table-container {
+	display: flex;
+	flex-direction: column;
+	.el-table {
+		flex: 1;
+	}
+	.table-footer {
+		display: flex;
+		.table-footer-tool {
+			flex: 1;
+			display: flex;
+			align-items: center;
+			justify-content: flex-end;
+			i {
+				margin-right: 10px;
+				cursor: pointer;
+				color: var(--el-text-color-regular);
+				&:last-of-type {
+					margin-right: 0;
+				}
+			}
+		}
+	}
+}
+</style>
Added +109 -0
diff --git a/src/components/table/search.vue b/src/components/table/search.vue
new file mode 100644
index 0000000..64246df
--- /dev/null
+++ b/src/components/table/search.vue
@@ -0,0 +1,109 @@
+<template>
+	<div class="table-search-container" v-if="props.search.length > 0">
+		<Form ref="formRef" v-model="formData" :config="config" class="table-form" :handleVisible="false">
+			<template #form-after>
+				<el-col class="!flex-1 !max-w-none">
+					<el-form-item class="table-form-btn" :label-width="search.length <= 1 ? '10px' : '100px'">
+						<template #label v-if="search.length > 1">
+							<div class="table-form-btn-toggle ml10" @click="state.isToggle = !state.isToggle">
+								<span>{{ state.isToggle ? '收起筛选' : '展开筛选' }}</span>
+								<SvgIcon :name="state.isToggle ? 'ele-ArrowUp' : 'ele-ArrowDown'" />
+							</div>
+						</template>
+						<div class="flex justify-between w-full">
+							<div>
+								<el-button size="default" type="primary" @click="onSearch">查询 </el-button>
+								<el-button size="default" type="info" class="ml10" @click="onReset"> 重置 </el-button>
+							</div>
+							<div class="ml-2.5">
+								<slot name="handle-after"></slot>
+							</div>
+						</div>
+					</el-form-item>
+				</el-col>
+			</template>
+			<template v-for="item in config.filter(item => item.slot)" :key="item.prop" #[`${item.slot!}`]="data">
+				<slot :name="item.slot" :model="data.model" :prop="data.prop"></slot>
+			</template>
+		</Form>
+	</div>
+</template>
+
+<script setup lang="ts" name="makeTableDemoSearch">
+import { reactive, ref, onMounted, computed } from 'vue';
+import Form from '/@/components/form/index.vue'
+import { ColumnConfig } from '../form/model/form';
+interface Props {
+	search: ColumnConfig[];
+	modelValue: EmptyObjectType;
+}
+interface Emits {
+	(e: 'search', val: EmptyObjectType): void;
+	(e: 'update:modelValue', val: EmptyObjectType): void;
+}
+// 定义父组件传过来的值
+const props = withDefaults(defineProps<Props>(), {
+	search: () => [],
+});
+// 定义子组件向父组件传值/事件
+const emits = defineEmits<Emits>();
+
+const formData = computed({
+	get () {
+		return props.modelValue
+	},
+	set (val) {
+		emits('update:modelValue', val)
+	}
+})
+const config = computed(() => {
+	return props.search.map((item, index) => ({
+		...item,
+		show: index === 0 || state.isToggle
+	}))
+})
+
+// 定义变量内容
+const formRef = ref<InstanceType<typeof Form>>();
+const state = reactive({
+	// form: {},
+	isToggle: false,
+});
+
+// 查询
+const onSearch = () => {
+	if (!formRef.value) return;
+	emits('search', formData.value);
+};
+// 重置
+const onReset = () => {
+	if (!formRef.value) return;
+	formRef.value?.formEl?.resetFields();
+	emits('search', formData.value);
+};
+// 初始化 form 字段,取自父组件 search.prop
+const initFormField = () => {
+	if (props.search.length <= 0) return false;
+	props.search.forEach((v) => (formData.value[v.prop] = ''));
+};
+// 页面加载时
+onMounted(() => {
+	initFormField();
+});
+</script>
+
+<style scoped lang="scss">
+.table-search-container {
+	display: flex;
+	.table-form {
+		flex: 1;
+		.table-form-btn-toggle {
+			white-space: nowrap;
+			user-select: none;
+			display: flex;
+			align-items: center;
+			color: var(--el-color-primary);
+		}
+	}
+}
+</style>
Added +27 -0
diff --git a/src/components/table/type.ts b/src/components/table/type.ts
new file mode 100644
index 0000000..27481c6
--- /dev/null
+++ b/src/components/table/type.ts
@@ -0,0 +1,27 @@
+import { FormRule } from "@form-create/element-ui";
+import { ColumnProp, FormType } from "../form/model/form";
+
+export interface TableColumn {
+  // 字段名
+	prop: string;
+  // 字段中文名
+  label?: string;
+  // 组件
+	component?: keyof FormType;
+  // 宽度
+  width?: string | number;
+  // 是否勾选显示
+  isCheck?: boolean;
+  // 自定义组件插槽
+  slot?: string;
+  // 是否必填
+	required?: boolean;
+  // 参数
+	props?: ColumnProp<TableColumn['component']>;
+  // 规则
+  rules?: FormRule[];
+  // 排序下标
+  index?: number;
+  // 是否渲染
+  if?: boolean;
+}
Added +40 -0
diff --git a/src/directive/authDirective.ts b/src/directive/authDirective.ts
new file mode 100644
index 0000000..5971e64
--- /dev/null
+++ b/src/directive/authDirective.ts
@@ -0,0 +1,40 @@
+import type { App } from 'vue';
+import { useUserInfo } from '/@/stores/userInfo';
+import { judementSameArr } from '/@/utils/arrayOperation';
+
+/**
+ * 用户权限指令
+ * @directive 单个权限验证(v-auth="xxx")
+ * @directive 多个权限验证,满足一个则显示(v-auths="[xxx,xxx]")
+ * @directive 多个权限验证,全部满足则显示(v-auth-all="[xxx,xxx]")
+ */
+export function authDirective(app: App) {
+	// 单个权限验证(v-auth="xxx")
+	app.directive('auth', {
+		mounted(el, binding) {
+			const stores = useUserInfo();
+			if (!stores.userInfos.authBtnList.some((v: string) => v === binding.value)) el.parentNode.removeChild(el);
+		},
+	});
+	// 多个权限验证,满足一个则显示(v-auths="[xxx,xxx]")
+	app.directive('auths', {
+		mounted(el, binding) {
+			let flag = false;
+			const stores = useUserInfo();
+			stores.userInfos.authBtnList.map((val: string) => {
+				binding.value.map((v: string) => {
+					if (val === v) flag = true;
+				});
+			});
+			if (!flag) el.parentNode.removeChild(el);
+		},
+	});
+	// 多个权限验证,全部满足则显示(v-auth-all="[xxx,xxx]")
+	app.directive('auth-all', {
+		mounted(el, binding) {
+			const stores = useUserInfo();
+			const flag = judementSameArr(binding.value, stores.userInfos.authBtnList);
+			if (!flag) el.parentNode.removeChild(el);
+		},
+	});
+}
Added +178 -0
diff --git a/src/directive/customDirective.ts b/src/directive/customDirective.ts
new file mode 100644
index 0000000..c67350f
--- /dev/null
+++ b/src/directive/customDirective.ts
@@ -0,0 +1,178 @@
+import type { App } from 'vue';
+
+/**
+ * 按钮波浪指令
+ * @directive 默认方式:v-waves,如 `<div v-waves></div>`
+ * @directive 参数方式:v-waves=" |light|red|orange|purple|green|teal",如 `<div v-waves="'light'"></div>`
+ */
+export function wavesDirective(app: App) {
+	app.directive('waves', {
+		mounted(el, binding) {
+			el.classList.add('waves-effect');
+			binding.value && el.classList.add(`waves-${binding.value}`);
+			function setConvertStyle(obj: { [key: string]: unknown }) {
+				let style: string = '';
+				for (let i in obj) {
+					if (obj.hasOwnProperty(i)) style += `${i}:${obj[i]};`;
+				}
+				return style;
+			}
+			function onCurrentClick(e: { [key: string]: unknown }) {
+				let elDiv = document.createElement('div');
+				elDiv.classList.add('waves-ripple');
+				el.appendChild(elDiv);
+				let styles = {
+					left: `${e.layerX}px`,
+					top: `${e.layerY}px`,
+					opacity: 1,
+					transform: `scale(${(el.clientWidth / 100) * 10})`,
+					'transition-duration': `750ms`,
+					'transition-timing-function': `cubic-bezier(0.250, 0.460, 0.450, 0.940)`,
+				};
+				elDiv.setAttribute('style', setConvertStyle(styles));
+				setTimeout(() => {
+					elDiv.setAttribute(
+						'style',
+						setConvertStyle({
+							opacity: 0,
+							transform: styles.transform,
+							left: styles.left,
+							top: styles.top,
+						})
+					);
+					setTimeout(() => {
+						elDiv && el.removeChild(elDiv);
+					}, 750);
+				}, 450);
+			}
+			el.addEventListener('mousedown', onCurrentClick, false);
+		},
+		unmounted(el) {
+			el.addEventListener('mousedown', () => {});
+		},
+	});
+}
+
+/**
+ * 自定义拖动指令
+ * @description  使用方式:v-drag="[dragDom,dragHeader]",如 `<div v-drag="['.drag-container .el-dialog', '.drag-container .el-dialog__header']"></div>`
+ * @description dragDom 要拖动的元素,dragHeader 要拖动的 Header 位置
+ * @link 注意:https://github.com/element-plus/element-plus/issues/522
+ * @lick 参考:https://blog.csdn.net/weixin_46391323/article/details/105228020?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_title-10&spm=1001.2101.3001.4242
+ */
+export function dragDirective(app: App) {
+	app.directive('drag', {
+		mounted(el, binding) {
+			if (!binding.value) return false;
+
+			const dragDom = document.querySelector(binding.value[0]) as HTMLElement;
+			const dragHeader = document.querySelector(binding.value[1]) as HTMLElement;
+
+			dragHeader.onmouseover = () => (dragHeader.style.cursor = `move`);
+
+			function down(e: any, type: string) {
+				// 鼠标按下,计算当前元素距离可视区的距离
+				const disX = type === 'pc' ? e.clientX - dragHeader.offsetLeft : e.touches[0].clientX - dragHeader.offsetLeft;
+				const disY = type === 'pc' ? e.clientY - dragHeader.offsetTop : e.touches[0].clientY - dragHeader.offsetTop;
+
+				// body当前宽度
+				const screenWidth = document.body.clientWidth;
+				// 可见区域高度(应为body高度,可某些环境下无法获取)
+				const screenHeight = document.documentElement.clientHeight;
+
+				// 对话框宽度
+				const dragDomWidth = dragDom.offsetWidth;
+				// 对话框高度
+				const dragDomheight = dragDom.offsetHeight;
+
+				const minDragDomLeft = dragDom.offsetLeft;
+				const maxDragDomLeft = screenWidth - dragDom.offsetLeft - dragDomWidth;
+
+				const minDragDomTop = dragDom.offsetTop;
+				const maxDragDomTop = screenHeight - dragDom.offsetTop - dragDomheight;
+
+				// 获取到的值带px 正则匹配替换
+				let styL: any = getComputedStyle(dragDom).left;
+				let styT: any = getComputedStyle(dragDom).top;
+
+				// 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px
+				if (styL.includes('%')) {
+					styL = +document.body.clientWidth * (+styL.replace(/\%/g, '') / 100);
+					styT = +document.body.clientHeight * (+styT.replace(/\%/g, '') / 100);
+				} else {
+					styL = +styL.replace(/\px/g, '');
+					styT = +styT.replace(/\px/g, '');
+				}
+
+				return {
+					disX,
+					disY,
+					minDragDomLeft,
+					maxDragDomLeft,
+					minDragDomTop,
+					maxDragDomTop,
+					styL,
+					styT,
+				};
+			}
+
+			function move(e: any, type: string, obj: any) {
+				let { disX, disY, minDragDomLeft, maxDragDomLeft, minDragDomTop, maxDragDomTop, styL, styT } = obj;
+
+				// 通过事件委托,计算移动的距离
+				let left = type === 'pc' ? e.clientX - disX : e.touches[0].clientX - disX;
+				let top = type === 'pc' ? e.clientY - disY : e.touches[0].clientY - disY;
+
+				// 边界处理
+				if (-left > minDragDomLeft) {
+					left = -minDragDomLeft;
+				} else if (left > maxDragDomLeft) {
+					left = maxDragDomLeft;
+				}
+
+				if (-top > minDragDomTop) {
+					top = -minDragDomTop;
+				} else if (top > maxDragDomTop) {
+					top = maxDragDomTop;
+				}
+
+				// 移动当前元素
+				dragDom.style.cssText += `;left:${left + styL}px;top:${top + styT}px;`;
+			}
+
+			/**
+			 * pc端
+			 * onmousedown 鼠标按下触发事件
+			 * onmousemove 鼠标按下时持续触发事件
+			 * onmouseup 鼠标抬起触发事件
+			 */
+			dragHeader.onmousedown = (e) => {
+				const obj = down(e, 'pc');
+				document.onmousemove = (e) => {
+					move(e, 'pc', obj);
+				};
+				document.onmouseup = () => {
+					document.onmousemove = null;
+					document.onmouseup = null;
+				};
+			};
+
+			/**
+			 * 移动端
+			 * ontouchstart 当按下手指时,触发ontouchstart
+			 * ontouchmove 当移动手指时,触发ontouchmove
+			 * ontouchend 当移走手指时,触发ontouchend
+			 */
+			dragHeader.ontouchstart = (e) => {
+				const obj = down(e, 'app');
+				document.ontouchmove = (e) => {
+					move(e, 'app', obj);
+				};
+				document.ontouchend = () => {
+					document.ontouchmove = null;
+					document.ontouchend = null;
+				};
+			};
+		},
+	});
+}
Added +18 -0
diff --git a/src/directive/index.ts b/src/directive/index.ts
new file mode 100644
index 0000000..e2e1c57
--- /dev/null
+++ b/src/directive/index.ts
@@ -0,0 +1,18 @@
+import type { App } from 'vue';
+import { authDirective } from '/@/directive/authDirective';
+import { wavesDirective, dragDirective } from '/@/directive/customDirective';
+
+/**
+ * 导出指令方法:v-xxx
+ * @methods authDirective 用户权限指令,用法:v-auth
+ * @methods wavesDirective 按钮波浪指令,用法:v-waves
+ * @methods dragDirective 自定义拖动指令,用法:v-drag
+ */
+export function directive(app: App) {
+	// 用户权限指令
+	authDirective(app);
+	// 按钮波浪指令
+	wavesDirective(app);
+	// 自定义拖动指令
+	dragDirective(app);
+}
Added +30 -0
diff --git a/src/hook/useOptions.ts b/src/hook/useOptions.ts
new file mode 100644
index 0000000..33b63b2
--- /dev/null
+++ b/src/hook/useOptions.ts
@@ -0,0 +1,30 @@
+import { ref, watch } from "vue";
+import { usePageApi } from "../api/page";
+
+interface Props {
+  options?: Array<EmptyObjectType>;
+  valueKey?: string;
+  labelKey?: string;
+  resultKey?: string;
+  api?: () => Promise<EmptyObjectType | Array<EmptyObjectType>>;
+  url?: string;
+}
+
+export default function useOptions (props: Props) {
+  const myOptions = ref(props.options);
+  watch(() => props.options, (val) => {
+    myOptions.value = val
+  })
+  if (props.api) {
+    props.api().then(res => {
+      myOptions.value = props.resultKey ? res[props.resultKey] : res
+    })
+  } else if (props.url) {
+    usePageApi().getTableData(props.url, { pageIndex: 0 }).then(res => {
+      myOptions.value = props.resultKey ? res[props.resultKey] : res
+    })
+  }
+  return {
+    myOptions
+  }
+}
\ No newline at end of file
Added +82 -0
diff --git a/src/hook/usePageSetting.ts b/src/hook/usePageSetting.ts
new file mode 100644
index 0000000..18de975
--- /dev/null
+++ b/src/hook/usePageSetting.ts
@@ -0,0 +1,82 @@
+import { reactive, unref } from "vue";
+import { ColumnConfig } from "../components/form/model/form";
+import { TableColumn } from "../components/table/type";
+import { EventSettingProps, EventSettingRef } from "../components/page/model";
+import { ColumnKind } from "/@/api/page";
+import { deepMerge } from "/@/utils/other";
+// 各区域配置项(响应式)
+interface UseColumns {
+  table?: TableColumn[];
+  search?: ColumnConfig[];
+  add?: ColumnConfig[];
+  edit?: ColumnConfig[];
+  detail?: ColumnConfig[];
+}
+// 各区域表单值(响应式)
+interface UseForms {
+  // 搜索区域表单
+  search?: EmptyObjectType;
+  // 添加、修改、详情表单
+  data?: EmptyObjectType;
+}
+/**
+ * 页面配置
+ * @param newSettings 配置规则
+ * @returns  setting: 回调方法; columns: 配置; forms: 表单;
+ */
+export default function usePageSetting (newSettings: EventSettingProps) {
+  const columns: UseColumns = reactive({
+    table: undefined,
+    search: undefined,
+    add: undefined,
+    edit: undefined,
+    detail: undefined,
+  })
+  const forms: UseForms = reactive({
+    search: undefined,
+    data: undefined
+  })
+  // 回调方法
+  const setting = (oldSetting: EventSettingRef) => {
+    if (oldSetting.type === ColumnKind.SEARCH) {
+      columns.search = oldSetting.config as unknown as ColumnConfig[];
+      forms.search = oldSetting.formData;
+    } else if (oldSetting.type === ColumnKind.LIST) {
+      columns.table = oldSetting.config as unknown as TableColumn[]
+    } else if (oldSetting.type === ColumnKind.ADD) {
+      columns.add = oldSetting.config as unknown as ColumnConfig[]
+      forms.data = oldSetting.formData;
+    } else if (oldSetting.type === ColumnKind.EDIT) {
+      columns.edit = oldSetting.config as unknown as ColumnConfig[]
+      forms.data = oldSetting.formData;
+    } else if (oldSetting.type === ColumnKind.DETAIL) {
+      columns.detail = oldSetting.config as unknown as ColumnConfig[]
+      forms.data = oldSetting.formData;
+    }
+    if (newSettings.columns) {
+      let newArr = newSettings.columns.filter(item => item.in === undefined || item.in === oldSetting.type || (Array.isArray(item.in) && item.in.some(v => v === oldSetting.type)))
+      let oldArrVal: Array<ColumnConfig | TableColumn> = unref(oldSetting.config)
+      newArr.forEach((newItem) => {
+        let oldItem = oldArrVal.find(v => v.prop === newItem.prop)
+        if (oldItem) {
+          // 合并配置
+          oldItem = deepMerge(oldItem, newItem)
+        } else if (newItem.in) {
+          // 追加配置
+          oldArrVal.push(newItem)
+        }
+      })
+      // 排序
+      let sortArr = oldArrVal.filter(item => item.index !== undefined).sort((item1, item2) => item1.index! - item2.index!)
+      oldArrVal = oldArrVal.filter(item => item.index === undefined)
+      sortArr.forEach(item => {
+        oldArrVal.splice(item.index!, 0, item)
+      })
+    }
+  }
+  return {
+    setting,
+    columns,
+    forms
+  }
+}
\ No newline at end of file
Added +68 -0
diff --git a/src/i18n/index.ts b/src/i18n/index.ts
new file mode 100644
index 0000000..2b9072a
--- /dev/null
+++ b/src/i18n/index.ts
@@ -0,0 +1,68 @@
+import { createI18n } from 'vue-i18n';
+import pinia from '/@/stores/index';
+import { storeToRefs } from 'pinia';
+import { useThemeConfig } from '/@/stores/themeConfig';
+
+// 定义语言国际化内容
+
+/**
+ * 说明:
+ * 须在 pages 下新建文件夹(建议 `要国际化界面目录` 与 `i18n 目录` 相同,方便查找),
+ * 注意国际化定义的字段,不要与原有的定义字段相同。
+ * 1、/src/i18n/lang 下的 ts 为框架的国际化内容
+ * 2、/src/i18n/pages 下的 ts 为各界面的国际化内容
+ */
+
+// element plus 自带国际化
+import enLocale from 'element-plus/lib/locale/lang/en';
+import zhcnLocale from 'element-plus/lib/locale/lang/zh-cn';
+import zhtwLocale from 'element-plus/lib/locale/lang/zh-tw';
+
+// 定义变量内容
+const messages = {};
+const element = { en: enLocale, 'zh-cn': zhcnLocale, 'zh-tw': zhtwLocale };
+const itemize = { en: [], 'zh-cn': [], 'zh-tw': [] };
+const modules: Record<string, any> = import.meta.glob('./**/*.ts', { eager: true });
+
+// 对自动引入的 modules 进行分类 en、zh-cn、zh-tw
+// https://vitejs.cn/vite3-cn/guide/features.html#glob-import
+for (const path in modules) {
+	const key = path.match(/(\S+)\/(\S+).ts/);
+	if (itemize[key![2]]) itemize[key![2]].push(modules[path].default);
+	else itemize[key![2]] = modules[path];
+}
+
+// 合并数组对象(非标准数组对象,数组中对象的每项 key、value 都不同)
+function mergeArrObj<T>(list: T, key: string) {
+	let obj = {};
+	list[key].forEach((i: EmptyObjectType) => {
+		obj = Object.assign({}, obj, i);
+	});
+	return obj;
+}
+
+// 处理最终格式
+for (const key in itemize) {
+	messages[key] = {
+		name: key,
+		el: element[key].el,
+		message: mergeArrObj(itemize, key),
+	};
+}
+
+// 读取 pinia 默认语言
+const stores = useThemeConfig(pinia);
+const { themeConfig } = storeToRefs(stores);
+
+// 导出语言国际化
+// https://vue-i18n.intlify.dev/guide/essentials/fallback.html#explicit-fallback-with-one-locale
+export const i18n = createI18n({
+	legacy: false,
+	silentTranslationWarn: true,
+	missingWarn: false,
+	silentFallbackWarn: true,
+	fallbackWarn: false,
+	locale: themeConfig.value.globalI18n,
+	fallbackLocale: zhcnLocale.name,
+	messages,
+});
Added +192 -0
diff --git a/src/i18n/lang/en.ts b/src/i18n/lang/en.ts
new file mode 100644
index 0000000..83d1129
--- /dev/null
+++ b/src/i18n/lang/en.ts
@@ -0,0 +1,192 @@
+// 定义内容
+export default {
+	router: {
+		home: 'home',
+		system: 'system',
+		systemMenu: 'systemMenu',
+		systemRole: 'systemRole',
+		systemUser: 'systemUser',
+		systemDept: 'systemDept',
+		systemDic: 'systemDic',
+		limits: 'limits',
+		limitsFrontEnd: 'FrontEnd',
+		limitsFrontEndPage: 'FrontEndPage',
+		limitsFrontEndBtn: 'FrontEndBtn',
+		limitsBackEnd: 'BackEnd',
+		limitsBackEndEndPage: 'BackEndEndPage',
+		menu: 'menu',
+		menu1: 'menu1',
+		menu11: 'menu11',
+		menu12: 'menu12',
+		menu121: 'menu121',
+		menu122: 'menu122',
+		menu13: 'menu13',
+		menu2: 'menu2',
+		funIndex: 'function',
+		funTagsView: 'funTagsView',
+		funCountup: 'countup',
+		funWangEditor: 'wangEditor',
+		funCropper: 'cropper',
+		funQrcode: 'qrcode',
+		funEchartsMap: 'EchartsMap',
+		funPrintJs: 'PrintJs',
+		funClipboard: 'Copy cut',
+		funGridLayout: 'Drag layout',
+		funSplitpanes: 'Pane splitter',
+		funDragVerify: 'Validator',
+		pagesIndex: 'pages',
+		pagesFiltering: 'Filtering',
+		pagesFilteringDetails: 'FilteringDetails',
+		pagesFilteringDetails1: 'FilteringDetails1',
+		pagesIocnfont: 'iconfont icon',
+		pagesElement: 'element icon',
+		pagesAwesome: 'awesome icon',
+		pagesFormAdapt: 'FormAdapt',
+		pagesTableRules: 'pagesTableRules',
+		pagesFormI18n: 'FormI18n',
+		pagesFormRules: 'Multi form validation',
+		pagesDynamicForm: 'Dynamic complex form',
+		pagesWorkflow: 'Workflow',
+		pagesListAdapt: 'ListAdapt',
+		pagesWaterfall: 'Waterfall',
+		pagesSteps: 'Steps',
+		pagesPreview: 'Large preview',
+		pagesWaves: 'Wave effect',
+		pagesTree: 'tree alter table',
+		pagesDrag: 'Drag command',
+		pagesLazyImg: 'Image lazy loading',
+		makeIndex: 'makeIndex',
+		makeSelector: 'Icon selector',
+		makeNoticeBar: 'notification bar',
+		makeSvgDemo: 'Svgicon demo',
+		makeTableDemo: 'table demo',
+		paramsIndex: 'Routing parameters',
+		paramsCommon: 'General routing',
+		paramsDynamic: 'Dynamic routing',
+		paramsCommonDetails: 'General routing details',
+		paramsDynamicDetails: 'Dynamic routing details',
+		chartIndex: 'chartIndex',
+		visualizingIndex: 'visualizingIndex',
+		visualizingLinkDemo1: 'visualizingLinkDemo1',
+		visualizingLinkDemo2: 'visualizingLinkDemo2',
+		personal: 'personal',
+		tools: 'tools',
+		layoutLinkView: 'LinkView',
+		layoutIframeViewOne: 'IframeViewOne',
+		layoutIframeViewTwo: 'IframeViewTwo',
+	},
+	staticRoutes: {
+		signIn: 'signIn',
+		notFound: 'notFound',
+		noPower: 'noPower',
+	},
+	user: {
+		title0: 'Component size',
+		title1: 'Language switching',
+		title2: 'Menu search',
+		title3: 'Layout configuration',
+		title4: 'news',
+		title5: 'Full screen on',
+		title6: 'Full screen off',
+		dropdownLarge: 'large',
+		dropdownDefault: 'default',
+		dropdownSmall: 'small',
+		dropdown1: 'home page',
+		dropdown2: 'Personal Center',
+		dropdown3: '404',
+		dropdown4: '401',
+		dropdown5: 'Log out',
+		dropdown6: 'Code warehouse',
+		searchPlaceholder: 'Menu search: support Chinese, routing path',
+		newTitle: 'notice',
+		newBtn: 'All read',
+		newGo: 'Go to the notification center',
+		newDesc: 'No notice',
+		logOutTitle: 'Tips',
+		logOutMessage: 'This operation will log out. Do you want to continue?',
+		logOutConfirm: 'determine',
+		logOutCancel: 'cancel',
+		logOutExit: 'Exiting',
+	},
+	tagsView: {
+		refresh: 'refresh',
+		close: 'close',
+		closeOther: 'closeOther',
+		closeAll: 'closeAll',
+		fullscreen: 'fullscreen',
+		closeFullscreen: 'closeFullscreen',
+	},
+	notFound: {
+		foundTitle: 'Wrong address input, please re-enter the address~',
+		foundMsg: 'You can check the web address first, and then re-enter or give us feedback.',
+		foundBtn: 'Back to home page',
+	},
+	noAccess: {
+		accessTitle: 'You are not authorized to operate~',
+		accessMsg: 'Contact information: add QQ group discussion 665452019',
+		accessBtn: 'Reauthorization',
+	},
+	layout: {
+		configTitle: 'Layout configuration',
+		oneTitle: 'Global Themes',
+		twoTopTitle: 'top bar set up',
+		twoMenuTitle: 'Menu set up',
+		twoColumnsTitle: 'Columns set up',
+		twoTopBar: 'Top bar background',
+		twoTopBarColor: 'Top bar default font color',
+		twoIsTopBarColorGradual: 'Top bar gradient',
+		twoMenuBar: 'Menu background',
+		twoMenuBarColor: 'Menu default font color',
+		twoMenuBarActiveColor: 'Menu Highlight Color',
+		twoIsMenuBarColorGradual: 'Menu gradient',
+		twoColumnsMenuBar: 'Column menu background',
+		twoColumnsMenuBarColor: 'Default font color bar menu',
+		twoIsColumnsMenuBarColorGradual: 'Column gradient',
+		twoIsColumnsMenuHoverPreload: 'Column Menu Hover Preload',
+		threeTitle: 'Interface settings',
+		threeIsCollapse: 'Menu horizontal collapse',
+		threeIsUniqueOpened: 'Menu accordion',
+		threeIsFixedHeader: 'Fixed header',
+		threeIsClassicSplitMenu: 'Classic layout split menu',
+		threeIsLockScreen: 'Open the lock screen',
+		threeLockScreenTime: 'screen locking(s/s)',
+		fourTitle: 'Interface display',
+		fourIsShowLogo: 'Sidebar logo',
+		fourIsBreadcrumb: 'Open breadcrumb',
+		fourIsBreadcrumbIcon: 'Open breadcrumb icon',
+		fourIsTagsview: 'Open tagsview',
+		fourIsTagsviewIcon: 'Open tagsview Icon',
+		fourIsCacheTagsView: 'Enable tagsview cache',
+		fourIsSortableTagsView: 'Enable tagsview drag',
+		fourIsShareTagsView: 'Enable tagsview sharing',
+		fourIsFooter: 'Open footer',
+		fourIsGrayscale: 'Grey model',
+		fourIsInvert: 'Color weak mode',
+		fourIsDark: 'Dark Mode',
+		fourIsWartermark: 'Turn on watermark',
+		fourWartermarkText: 'Watermark copy',
+		fiveTitle: 'Other settings',
+		fiveTagsStyle: 'Tagsview style',
+		fiveAnimation: 'page animation',
+		fiveColumnsAsideStyle: 'Column style',
+		fiveColumnsAsideLayout: 'Column layout',
+		sixTitle: 'Layout switch',
+		sixDefaults: 'One',
+		sixClassic: 'Two',
+		sixTransverse: 'Three',
+		sixColumns: 'Four',
+		tipText: 'Click the button below to copy the layout configuration to `/src/stores/themeConfig.ts` It has been modified in.',
+		copyText: 'replication configuration',
+		resetText: 'restore default',
+		copyTextSuccess: 'Copy succeeded!',
+		copyTextError: 'Copy failed!',
+	},
+	upgrade: {
+		title: 'New version',
+		msg: 'The new version is available, please update it now! Dont worry, the update is fast!',
+		desc: 'Prompt: Update will restore the default configuration',
+		btnOne: 'Cruel refusal',
+		btnTwo: 'Update now',
+		btnTwoLoading: 'Updating',
+	},
+};
Added +192 -0
diff --git a/src/i18n/lang/zh-cn.ts b/src/i18n/lang/zh-cn.ts
new file mode 100644
index 0000000..e0713b9
--- /dev/null
+++ b/src/i18n/lang/zh-cn.ts
@@ -0,0 +1,192 @@
+// 定义内容
+export default {
+	router: {
+		home: '首页',
+		system: '系统设置',
+		systemMenu: '菜单管理',
+		systemRole: '角色管理',
+		systemUser: '用户管理',
+		systemDept: '部门管理',
+		systemDic: '字典管理',
+		limits: '权限管理',
+		limitsFrontEnd: '前端控制',
+		limitsFrontEndPage: '页面权限',
+		limitsFrontEndBtn: '按钮权限',
+		limitsBackEnd: '后端控制',
+		limitsBackEndEndPage: '页面权限',
+		menu: '菜单嵌套',
+		menu1: '菜单1',
+		menu11: '菜单11',
+		menu12: '菜单12',
+		menu121: '菜单121',
+		menu122: '菜单122',
+		menu13: '菜单13',
+		menu2: '菜单2',
+		funIndex: '功能',
+		funTagsView: 'tagsView 操作',
+		funCountup: '数字滚动',
+		funWangEditor: 'Editor 编辑器',
+		funCropper: '图片裁剪',
+		funQrcode: '二维码生成',
+		funEchartsMap: '地理坐标/地图',
+		funPrintJs: '页面打印',
+		funClipboard: '复制剪切',
+		funGridLayout: '拖拽布局',
+		funSplitpanes: '窗格拆分器',
+		funDragVerify: '验证器',
+		pagesIndex: '页面',
+		pagesFiltering: '过滤筛选组件',
+		pagesFilteringDetails: '过滤筛选组件详情',
+		pagesFilteringDetails1: '过滤筛选组件详情111',
+		pagesIocnfont: 'ali 字体图标',
+		pagesElement: 'ele 字体图标',
+		pagesAwesome: 'awe 字体图标',
+		pagesFormAdapt: '表单自适应',
+		pagesTableRules: '表单表格验证',
+		pagesFormI18n: '表单国际化',
+		pagesFormRules: '多表单验证',
+		pagesDynamicForm: '动态复杂表单',
+		pagesWorkflow: '工作流',
+		pagesListAdapt: '列表自适应',
+		pagesWaterfall: '瀑布屏',
+		pagesSteps: '步骤条',
+		pagesPreview: '大图预览',
+		pagesWaves: '波浪效果',
+		pagesTree: '树形改表格',
+		pagesDrag: '拖动指令',
+		pagesLazyImg: '图片懒加载',
+		makeIndex: '组件封装',
+		makeSelector: '图标选择器',
+		makeNoticeBar: '滚动通知栏',
+		makeSvgDemo: 'svgIcon 演示',
+		makeTableDemo: '表格封装演示',
+		paramsIndex: '路由参数',
+		paramsCommon: '普通路由',
+		paramsDynamic: '动态路由',
+		paramsCommonDetails: '普通路由详情',
+		paramsDynamicDetails: '动态路由详情',
+		chartIndex: '大数据图表',
+		visualizingIndex: '数据可视化',
+		visualizingLinkDemo1: '数据可视化演示1',
+		visualizingLinkDemo2: '数据可视化演示2',
+		personal: '个人中心',
+		tools: '工具类集合',
+		layoutLinkView: '外链',
+		layoutIframeViewOne: '内嵌 iframe1',
+		layoutIframeViewTwo: '内嵌 iframe2',
+	},
+	staticRoutes: {
+		signIn: '登录',
+		notFound: '找不到此页面',
+		noPower: '没有权限',
+	},
+	user: {
+		title0: '组件大小',
+		title1: '语言切换',
+		title2: '菜单搜索',
+		title3: '布局配置',
+		title4: '消息',
+		title5: '开全屏',
+		title6: '关全屏',
+		dropdownLarge: '大型',
+		dropdownDefault: '默认',
+		dropdownSmall: '小型',
+		dropdown1: '首页',
+		dropdown2: '个人中心',
+		dropdown3: '404',
+		dropdown4: '401',
+		dropdown5: '退出登录',
+		dropdown6: '代码仓库',
+		searchPlaceholder: '菜单搜索:支持中文、路由路径',
+		newTitle: '通知',
+		newBtn: '全部已读',
+		newGo: '前往通知中心',
+		newDesc: '暂无通知',
+		logOutTitle: '提示',
+		logOutMessage: '此操作将退出登录, 是否继续?',
+		logOutConfirm: '确定',
+		logOutCancel: '取消',
+		logOutExit: '退出中',
+	},
+	tagsView: {
+		refresh: '刷新',
+		close: '关闭',
+		closeOther: '关闭其它',
+		closeAll: '全部关闭',
+		fullscreen: '当前页全屏',
+		closeFullscreen: '关闭全屏',
+	},
+	notFound: {
+		foundTitle: '地址输入错误,请重新输入地址~',
+		foundMsg: '您可以先检查网址,然后重新输入或给我们反馈问题。',
+		foundBtn: '返回首页',
+	},
+	noAccess: {
+		accessTitle: '您未被授权,没有操作权限~',
+		accessMsg: '联系方式:加QQ群探讨 665452019',
+		accessBtn: '重新授权',
+	},
+	layout: {
+		configTitle: '布局配置',
+		oneTitle: '全局主题',
+		twoTopTitle: '顶栏设置',
+		twoMenuTitle: '菜单设置',
+		twoColumnsTitle: '分栏设置',
+		twoTopBar: '顶栏背景',
+		twoTopBarColor: '顶栏默认字体颜色',
+		twoIsTopBarColorGradual: '顶栏背景渐变',
+		twoMenuBar: '菜单背景',
+		twoMenuBarColor: '菜单默认字体颜色',
+		twoMenuBarActiveColor: '菜单高亮背景色',
+		twoIsMenuBarColorGradual: '菜单背景渐变',
+		twoColumnsMenuBar: '分栏菜单背景',
+		twoColumnsMenuBarColor: '分栏菜单默认字体颜色',
+		twoIsColumnsMenuBarColorGradual: '分栏菜单背景渐变',
+		twoIsColumnsMenuHoverPreload: '分栏菜单鼠标悬停预加载',
+		threeTitle: '界面设置',
+		threeIsCollapse: '菜单水平折叠',
+		threeIsUniqueOpened: '菜单手风琴',
+		threeIsFixedHeader: '固定 Header',
+		threeIsClassicSplitMenu: '经典布局分割菜单',
+		threeIsLockScreen: '开启锁屏',
+		threeLockScreenTime: '自动锁屏(s/秒)',
+		fourTitle: '界面显示',
+		fourIsShowLogo: '侧边栏 Logo',
+		fourIsBreadcrumb: '开启 Breadcrumb',
+		fourIsBreadcrumbIcon: '开启 Breadcrumb 图标',
+		fourIsTagsview: '开启 Tagsview',
+		fourIsTagsviewIcon: '开启 Tagsview 图标',
+		fourIsCacheTagsView: '开启 TagsView 缓存',
+		fourIsSortableTagsView: '开启 TagsView 拖拽',
+		fourIsShareTagsView: '开启 TagsView 共用',
+		fourIsFooter: '开启 Footer',
+		fourIsGrayscale: '灰色模式',
+		fourIsInvert: '色弱模式',
+		fourIsDark: '深色模式',
+		fourIsWartermark: '开启水印',
+		fourWartermarkText: '水印文案',
+		fiveTitle: '其它设置',
+		fiveTagsStyle: 'Tagsview 风格',
+		fiveAnimation: '主页面切换动画',
+		fiveColumnsAsideStyle: '分栏高亮风格',
+		fiveColumnsAsideLayout: '分栏布局风格',
+		sixTitle: '布局切换',
+		sixDefaults: '默认',
+		sixClassic: '经典',
+		sixTransverse: '横向',
+		sixColumns: '分栏',
+		tipText: '点击下方按钮,复制布局配置去 `src/stores/themeConfig.ts` 中修改。',
+		copyText: '一键复制配置',
+		resetText: '一键恢复默认',
+		copyTextSuccess: '复制成功!',
+		copyTextError: '复制失败!',
+	},
+	upgrade: {
+		title: '新版本升级',
+		msg: '新版本来啦,马上更新尝鲜吧!不用担心,更新很快的哦!',
+		desc: '提示:更新会还原默认配置',
+		btnOne: '残忍拒绝',
+		btnTwo: '马上更新',
+		btnTwoLoading: '更新中',
+	},
+};
Added +192 -0
diff --git a/src/i18n/lang/zh-tw.ts b/src/i18n/lang/zh-tw.ts
new file mode 100644
index 0000000..35e406f
--- /dev/null
+++ b/src/i18n/lang/zh-tw.ts
@@ -0,0 +1,192 @@
+// 定义内容
+export default {
+	router: {
+		home: '首頁',
+		system: '系統設置',
+		systemMenu: '選單管理',
+		systemRole: '角色管理',
+		systemUser: '用戶管理',
+		systemDept: '部門管理',
+		systemDic: '字典管理',
+		limits: '許可權管理',
+		limitsFrontEnd: '前端控制',
+		limitsFrontEndPage: '頁面許可權',
+		limitsFrontEndBtn: '按鈕許可權',
+		limitsBackEnd: '後端控制',
+		limitsBackEndEndPage: '頁面許可權',
+		menu: '選單嵌套',
+		menu1: '選單1',
+		menu11: '選單11',
+		menu12: '選單12',
+		menu121: '選單121',
+		menu122: '選單122',
+		menu13: '選單13',
+		menu2: '選單2',
+		funIndex: '功能',
+		funTagsView: 'tagsView 操作',
+		funCountup: '數位滾動',
+		funWangEditor: 'Editor 編輯器',
+		funCropper: '圖片裁剪',
+		funQrcode: '二維碼生成',
+		funEchartsMap: '地理座標/地圖',
+		funPrintJs: '頁面列印',
+		funClipboard: '複製剪切',
+		funGridLayout: '拖拽佈局',
+		funSplitpanes: '窗格折開器',
+		funDragVerify: '驗證器',
+		pagesIndex: '頁面',
+		pagesFiltering: '過濾篩選組件',
+		pagesFilteringDetails: '過濾篩選組件詳情',
+		pagesFilteringDetails1: '過濾篩選組件詳情111',
+		pagesIocnfont: 'ali 字體圖標',
+		pagesElement: 'ele 字體圖標',
+		pagesAwesome: 'awe 字體圖標',
+		pagesFormAdapt: '表單自我調整',
+		pagesTableRules: '表單表格驗證',
+		pagesFormI18n: '表單國際化',
+		pagesFormRules: '多表單驗證',
+		pagesDynamicForm: '動態複雜表單',
+		pagesWorkflow: '工作流',
+		pagesListAdapt: '清單自我調整',
+		pagesWaterfall: '瀑布屏',
+		pagesSteps: '步驟條',
+		pagesPreview: '大圖預覽',
+		pagesWaves: '波浪效果',
+		pagesTree: '樹形改表格',
+		pagesDrag: '拖動指令',
+		pagesLazyImg: '圖片懶加載',
+		makeIndex: '組件封裝',
+		makeSelector: '圖標選擇器',
+		makeNoticeBar: '滾動通知欄',
+		makeSvgDemo: 'svgIcon 演示',
+		makeTableDemo: '表格封裝演示',
+		paramsIndex: '路由參數',
+		paramsCommon: '普通路由',
+		paramsDynamic: '動態路由',
+		paramsCommonDetails: '普通路由詳情',
+		paramsDynamicDetails: '動態路由詳情',
+		chartIndex: '大資料圖表',
+		visualizingIndex: '數據視覺化',
+		visualizingLinkDemo1: '數據視覺化演示1',
+		visualizingLinkDemo2: '數據視覺化演示2',
+		personal: '個人中心',
+		tools: '工具類集合',
+		layoutLinkView: '外鏈',
+		layoutIframeViewOne: '内嵌 iframe1',
+		layoutIframeViewTwo: '内嵌 iframe2',
+	},
+	staticRoutes: {
+		signIn: '登入',
+		notFound: '找不到此頁面',
+		noPower: '沒有許可權',
+	},
+	user: {
+		title0: '組件大小',
+		title1: '語言切換',
+		title2: '選單蒐索',
+		title3: '佈局配寘',
+		title4: '消息',
+		title5: '開全屏',
+		title6: '關全屏',
+		dropdownLarge: '大型',
+		dropdownDefault: '默認',
+		dropdownSmall: '小型',
+		dropdown1: '首頁',
+		dropdown2: '個人中心',
+		dropdown3: '404',
+		dropdown4: '401',
+		dropdown5: '登出',
+		dropdown6: '程式碼倉庫',
+		searchPlaceholder: '選單蒐索:支援中文、路由路徑',
+		newTitle: '通知',
+		newBtn: '全部已讀',
+		newGo: '前往通知中心',
+		newDesc: '暫無通知',
+		logOutTitle: '提示',
+		logOutMessage: '此操作將登出,是否繼續?',
+		logOutConfirm: '確定',
+		logOutCancel: '取消',
+		logOutExit: '退出中',
+	},
+	tagsView: {
+		refresh: '重繪',
+		close: '關閉',
+		closeOther: '關閉其它',
+		closeAll: '全部關閉',
+		fullscreen: '當前頁全屏',
+		closeFullscreen: '關閉全屏',
+	},
+	notFound: {
+		foundTitle: '地址輸入錯誤,請重新輸入地址~',
+		foundMsg: '您可以先檢查網址,然後重新輸入或給我們迴響問題。',
+		foundBtn: '返回首頁',
+	},
+	noAccess: {
+		accessTitle: '您未被授權,沒有操作許可權~',
+		accessMsg: '聯繫方式:加QQ群探討665452019',
+		accessBtn: '重新授權',
+	},
+	layout: {
+		configTitle: '佈局配寘',
+		oneTitle: '全域主題',
+		twoTopTitle: '頂欄設定',
+		twoMenuTitle: '選單設定',
+		twoColumnsTitle: '分欄設定',
+		twoTopBar: '頂欄背景',
+		twoTopBarColor: '頂欄默認字體顏色',
+		twoIsTopBarColorGradual: '頂欄背景漸變',
+		twoMenuBar: '選單背景',
+		twoMenuBarColor: '選單默認字體顏色',
+		twoMenuBarActiveColor: '選單高亮背景色',
+		twoIsMenuBarColorGradual: '選單背景漸變',
+		twoColumnsMenuBar: '分欄選單背景',
+		twoColumnsMenuBarColor: '分欄選單默認字體顏色',
+		twoIsColumnsMenuBarColorGradual: '分欄選單背景漸變',
+		twoIsColumnsMenuHoverPreload: '分欄選單滑鼠懸停預加載',
+		threeTitle: '介面設定',
+		threeIsCollapse: '選單水准折疊',
+		threeIsUniqueOpened: '選單手風琴',
+		threeIsFixedHeader: '固定 Header',
+		threeIsClassicSplitMenu: '經典佈局分割選單',
+		threeIsLockScreen: '開啟鎖屏',
+		threeLockScreenTime: '自動鎖屏(s/秒)',
+		fourTitle: '介面顯示',
+		fourIsShowLogo: '側邊欄 Logo',
+		fourIsBreadcrumb: '開啟 Breadcrumb',
+		fourIsBreadcrumbIcon: '開啟 Breadcrumb 圖標',
+		fourIsTagsview: '開啟 Tagsview',
+		fourIsTagsviewIcon: '開啟 Tagsview 圖標',
+		fourIsCacheTagsView: '開啟 TagsView 緩存',
+		fourIsSortableTagsView: '開啟 TagsView 拖拽',
+		fourIsShareTagsView: '開啟 TagsView 共用',
+		fourIsFooter: '開啟 Footer',
+		fourIsGrayscale: '灰色模式',
+		fourIsInvert: '色弱模式',
+		fourIsDark: '深色模式',
+		fourIsWartermark: '開啟浮水印',
+		fourWartermarkText: '浮水印文案',
+		fiveTitle: '其它設定',
+		fiveTagsStyle: 'Tagsview 風格',
+		fiveAnimation: '主頁面切換動畫',
+		fiveColumnsAsideStyle: '分欄高亮風格',
+		fiveColumnsAsideLayout: '分欄佈局風格',
+		sixTitle: '佈局切換',
+		sixDefaults: '默認',
+		sixClassic: '經典',
+		sixTransverse: '橫向',
+		sixColumns: '分欄',
+		tipText: '點擊下方按鈕,複製佈局配寘去`src/stores/themeConfig.ts`中修改。',
+		copyText: '一鍵複製配寘',
+		resetText: '一鍵恢復默認',
+		copyTextSuccess: '複製成功!',
+		copyTextError: '複製失敗!',
+	},
+	upgrade: {
+		title: '新版本陞級',
+		msg: '新版本來啦,馬上更新嘗鮮吧! 不用擔心,更新很快的哦!',
+		desc: '提示:更新會還原默認配寘',
+		btnOne: '殘忍拒絕',
+		btnTwo: '馬上更新',
+		btnTwoLoading: '更新中',
+	},
+};
Added +13 -0
diff --git a/src/i18n/pages/formI18n/en.ts b/src/i18n/pages/formI18n/en.ts
new file mode 100644
index 0000000..b3c54d6
--- /dev/null
+++ b/src/i18n/pages/formI18n/en.ts
@@ -0,0 +1,13 @@
+// 定义内容
+export default {
+	formI18nLabel: {
+		name: 'name',
+		email: 'email',
+		autograph: 'autograph',
+	},
+	formI18nPlaceholder: {
+		name: 'Please enter your name',
+		email: 'Please enter the users Department',
+		autograph: 'Please enter the login account name',
+	},
+};
Added +13 -0
diff --git a/src/i18n/pages/formI18n/zh-cn.ts b/src/i18n/pages/formI18n/zh-cn.ts
new file mode 100644
index 0000000..0bed3ec
--- /dev/null
+++ b/src/i18n/pages/formI18n/zh-cn.ts
@@ -0,0 +1,13 @@
+// 定义内容
+export default {
+	formI18nLabel: {
+		name: '姓名',
+		email: '用户归属部门',
+		autograph: '登陆账户名',
+	},
+	formI18nPlaceholder: {
+		name: '请输入姓名',
+		email: '请输入用户归属部门',
+		autograph: '请输入登陆账户名',
+	},
+};
Added +13 -0
diff --git a/src/i18n/pages/formI18n/zh-tw.ts b/src/i18n/pages/formI18n/zh-tw.ts
new file mode 100644
index 0000000..393ac03
--- /dev/null
+++ b/src/i18n/pages/formI18n/zh-tw.ts
@@ -0,0 +1,13 @@
+// 定义内容
+export default {
+	formI18nLabel: {
+		name: '姓名',
+		email: '用戶歸屬部門',
+		autograph: '登入帳戶名',
+	},
+	formI18nPlaceholder: {
+		name: '請輸入姓名',
+		email: '請輸入用戶歸屬部門',
+		autograph: '請輸入登入帳戶名',
+	},
+};
Added +29 -0
diff --git a/src/i18n/pages/login/en.ts b/src/i18n/pages/login/en.ts
new file mode 100644
index 0000000..2654a18
--- /dev/null
+++ b/src/i18n/pages/login/en.ts
@@ -0,0 +1,29 @@
+// 定义内容
+export default {
+	label: {
+		one1: 'User name login',
+		two2: 'Mobile number',
+	},
+	link: {
+		one3: 'Third party login',
+		two4: 'Links',
+	},
+	account: {
+		accountPlaceholder1: 'The user name admin or not is common',
+		accountPlaceholder2: 'Password: 123456',
+		accountPlaceholder3: 'Please enter the verification code',
+		accountBtnText: 'Sign in',
+	},
+	mobile: {
+		placeholder1: 'Please input mobile phone number',
+		placeholder2: 'Please enter the verification code',
+		codeText: 'Get code',
+		btnText: 'Sign in',
+		msgText:
+			'Warm tip: it is recommended to use Google, Microsoft edge, version 79.0.1072.62 and above browsers, and 360 browser, please use speed mode',
+	},
+	scan: {
+		text: 'Open the mobile phone to scan and quickly log in / register',
+	},
+	signInText: 'welcome back!',
+};
Added +28 -0
diff --git a/src/i18n/pages/login/zh-cn.ts b/src/i18n/pages/login/zh-cn.ts
new file mode 100644
index 0000000..d3f43ae
--- /dev/null
+++ b/src/i18n/pages/login/zh-cn.ts
@@ -0,0 +1,28 @@
+// 定义内容
+export default {
+	label: {
+		one1: '用户名登录',
+		two2: '手机号登录',
+	},
+	link: {
+		one3: '第三方登录',
+		two4: '友情链接',
+	},
+	account: {
+		accountPlaceholder1: '用户名',
+		accountPlaceholder2: '密码',
+		accountPlaceholder3: '请输入验证码',
+		accountBtnText: '登 录',
+	},
+	mobile: {
+		placeholder1: '请输入手机号',
+		placeholder2: '请输入验证码',
+		codeText: '获取验证码',
+		btnText: '登 录',
+		msgText: '* 温馨提示:建议使用谷歌、Microsoft Edge,版本 79.0.1072.62 及以上浏览器,360浏览器请使用极速模式',
+	},
+	scan: {
+		text: '打开手机扫一扫,快速登录/注册',
+	},
+	signInText: '欢迎回来!',
+};
Added +28 -0
diff --git a/src/i18n/pages/login/zh-tw.ts b/src/i18n/pages/login/zh-tw.ts
new file mode 100644
index 0000000..138e8c8
--- /dev/null
+++ b/src/i18n/pages/login/zh-tw.ts
@@ -0,0 +1,28 @@
+// 定义内容
+export default {
+	label: {
+		one1: '用戶名登入',
+		two2: '手機號登入',
+	},
+	link: {
+		one3: '協力廠商登入',
+		two4: '友情連結',
+	},
+	account: {
+		accountPlaceholder1: '用戶名admin或不輸均為common',
+		accountPlaceholder2: '密碼:123456',
+		accountPlaceholder3: '請輸入驗證碼',
+		accountBtnText: '登入',
+	},
+	mobile: {
+		placeholder1: '請輸入手機號',
+		placeholder2: '請輸入驗證碼',
+		codeText: '獲取驗證碼',
+		btnText: '登入',
+		msgText: '* 溫馨提示:建議使用穀歌、Microsoft Edge,版本79.0.1072.62及以上瀏覽器,360瀏覽器請使用極速模式',
+	},
+	scan: {
+		text: '打開手機掃一掃,快速登錄/注册',
+	},
+	signInText: '歡迎回來!',
+};
Added +165 -0
diff --git a/src/layout/component/aside.vue b/src/layout/component/aside.vue
new file mode 100644
index 0000000..8b5e248
--- /dev/null
+++ b/src/layout/component/aside.vue
@@ -0,0 +1,165 @@
+<template>
+	<div class="h100" v-show="!isTagsViewCurrenFull">
+		<el-aside class="layout-aside !shadow-none" :class="setCollapseStyle">
+			<Logo v-if="setShowLogo" class="aside-logo" />
+			<el-scrollbar class="flex-auto" ref="layoutAsideScrollbarRef" @mouseenter="onAsideEnterLeave(true)" @mouseleave="onAsideEnterLeave(false)">
+				<Vertical :menuList="state.menuList" />
+			</el-scrollbar>
+		</el-aside>
+	</div>
+</template>
+
+<script setup lang="ts" name="layoutAside">
+import { defineAsyncComponent, reactive, computed, watch, onBeforeMount, ref } from 'vue';
+import { storeToRefs } from 'pinia';
+import pinia from '/@/stores/index';
+import { useRoutesList } from '/@/stores/routesList';
+import { useThemeConfig } from '/@/stores/themeConfig';
+import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
+import mittBus from '/@/utils/mitt';
+
+// 引入组件
+const Logo = defineAsyncComponent(() => import('/@/layout/logo/index.vue'));
+const Vertical = defineAsyncComponent(() => import('/@/layout/navMenu/vertical.vue'));
+
+// 定义变量内容
+const layoutAsideScrollbarRef = ref();
+const stores = useRoutesList();
+const storesThemeConfig = useThemeConfig();
+const storesTagsViewRoutes = useTagsViewRoutes();
+const { routesList } = storeToRefs(stores);
+const { themeConfig } = storeToRefs(storesThemeConfig);
+const { isTagsViewCurrenFull } = storeToRefs(storesTagsViewRoutes);
+const state = reactive<AsideState>({
+	menuList: [],
+	clientWidth: 0,
+});
+
+// 设置菜单展开/收起时的宽度
+const setCollapseStyle = computed(() => {
+	const { layout, isCollapse, menuBar } = themeConfig.value;
+	const asideBrTheme = ['#FFFFFF', '#FFF', '#fff', '#ffffff'];
+	const asideBrColor = asideBrTheme.includes(menuBar) ? 'layout-el-aside-br-color' : '';
+	// 判断是否是手机端
+	if (state.clientWidth <= 1000) {
+		if (isCollapse) {
+			document.body.setAttribute('class', 'el-popup-parent--hidden');
+			const asideEle = document.querySelector('.layout-container') as HTMLElement;
+			const modeDivs = document.createElement('div');
+			modeDivs.setAttribute('class', 'layout-aside-mobile-mode');
+			asideEle.appendChild(modeDivs);
+			modeDivs.addEventListener('click', closeLayoutAsideMobileMode);
+			return [asideBrColor, 'layout-aside-mobile', 'layout-aside-mobile-open'];
+		} else {
+			// 关闭弹窗
+			closeLayoutAsideMobileMode();
+			return [asideBrColor, 'layout-aside-mobile', 'layout-aside-mobile-close'];
+		}
+	} else {
+		if (layout === 'columns') {
+			// 分栏布局,菜单收起时宽度给 1px
+			if (isCollapse) return [asideBrColor, 'layout-aside-pc-1'];
+			else return [asideBrColor, 'layout-aside-pc-220'];
+		} else {
+			// 其它布局给 64px
+			if (isCollapse) return [asideBrColor, 'layout-aside-pc-64'];
+			else return [asideBrColor, 'layout-aside-pc-220'];
+		}
+	}
+});
+// 设置显示/隐藏 logo
+const setShowLogo = computed(() => {
+	let { layout, isShowLogo } = themeConfig.value;
+	return (isShowLogo && layout === 'defaults') || (isShowLogo && layout === 'columns');
+});
+// 关闭移动端蒙版
+const closeLayoutAsideMobileMode = () => {
+	const el = document.querySelector('.layout-aside-mobile-mode');
+	el?.setAttribute('style', 'animation: error-img-two 0.3s');
+	setTimeout(() => {
+		el?.parentNode?.removeChild(el);
+	}, 300);
+	const clientWidth = document.body.clientWidth;
+	if (clientWidth < 1000) themeConfig.value.isCollapse = false;
+	document.body.setAttribute('class', '');
+};
+// 设置/过滤路由(非静态路由/是否显示在菜单中)
+const setFilterRoutes = () => {
+	if (themeConfig.value.layout === 'columns') return false;
+	state.menuList = filterRoutesFun(routesList.value);
+};
+// 路由过滤递归函数
+const filterRoutesFun = <T extends RouteItem>(arr: T[]): T[] => {
+	return arr
+		.filter((item: T) => !item.meta?.isHide)
+		.map((item: T) => {
+			item = Object.assign({}, item);
+			if (item.children) item.children = filterRoutesFun(item.children);
+			return item;
+		});
+};
+// 设置菜单导航是否固定(移动端)
+const initMenuFixed = (clientWidth: number) => {
+	state.clientWidth = clientWidth;
+};
+// 鼠标移入、移出
+const onAsideEnterLeave = (bool: Boolean) => {
+	let { layout } = themeConfig.value;
+	if (layout !== 'columns') return false;
+	if (!bool) mittBus.emit('restoreDefault');
+	// 开启 `分栏菜单鼠标悬停预加载` 才设置,防止 columnsAside.vue 监听 pinia.state
+	if (themeConfig.value.isColumnsMenuHoverPreload) stores.setColumnsMenuHover(bool);
+};
+// 页面加载前
+onBeforeMount(() => {
+	initMenuFixed(document.body.clientWidth);
+	setFilterRoutes();
+	// 此界面不需要取消监听(mittBus.off('setSendColumnsChildren))
+	// 因为切换布局时有的监听需要使用,取消了监听,某些操作将不生效
+	mittBus.on('setSendColumnsChildren', (res: MittMenu) => {
+		state.menuList = res.children;
+	});
+	// 开启经典布局分割菜单时,设置菜单数据
+	mittBus.on('setSendClassicChildren', (res: MittMenu) => {
+		let { layout, isClassicSplitMenu } = themeConfig.value;
+		if (layout === 'classic' && isClassicSplitMenu) {
+			state.menuList = [];
+			state.menuList = res.children;
+		}
+	});
+	// 开启经典布局分割菜单时,重新处理菜单数据
+	mittBus.on('getBreadcrumbIndexSetFilterRoutes', () => {
+		setFilterRoutes();
+	});
+	// 监听窗口大小改变时(适配移动端)
+	mittBus.on('layoutMobileResize', (res: LayoutMobileResize) => {
+		initMenuFixed(res.clientWidth);
+		closeLayoutAsideMobileMode();
+	});
+});
+// 监听 themeConfig 配置文件的变化,更新菜单 el-scrollbar 的高度
+watch(themeConfig.value, (val) => {
+	if (val.isShowLogoChange !== val.isShowLogo) {
+		if (layoutAsideScrollbarRef.value) layoutAsideScrollbarRef.value.update();
+	}
+});
+// 监听 pinia 值的变化,动态赋值给菜单中
+watch(
+	pinia.state,
+	(val) => {
+		let { layout, isClassicSplitMenu } = val.themeConfig.themeConfig;
+		if (layout === 'classic' && isClassicSplitMenu) return false;
+		setFilterRoutes();
+	},
+	{
+		deep: true,
+	}
+);
+</script>
+<style scoped lang="scss">
+.aside-logo {
+	&:deep(span) {
+		color: var(--next-bg-menuBarColor);
+	}
+}
+</style>
\ No newline at end of file
Added +281 -0
diff --git a/src/layout/component/columnsAside.vue b/src/layout/component/columnsAside.vue
new file mode 100644
index 0000000..d6ceedc
--- /dev/null
+++ b/src/layout/component/columnsAside.vue
@@ -0,0 +1,281 @@
+<template>
+	<div class="layout-columns-aside">
+		<el-scrollbar>
+			<ul @mouseleave="onColumnsAsideMenuMouseleave()">
+				<li
+					v-for="(v, k) in state.columnsAsideList"
+					:key="k"
+					@click="onColumnsAsideMenuClick(v)"
+					@mouseenter="onColumnsAsideMenuMouseenter(v, k)"
+					:ref="
+						(el) => {
+							if (el) columnsAsideOffsetTopRefs[k] = el;
+						}
+					"
+					:class="{ 'layout-columns-active': state.liIndex === k, 'layout-columns-hover': state.liHoverIndex === k }"
+					:title="$t(v.meta.title)"
+				>
+					<div :class="themeConfig.columnsAsideLayout" v-if="!v.meta.isLink || (v.meta.isLink && v.meta.isIframe)">
+						<SvgIcon :name="v.meta.icon" />
+						<div class="columns-vertical-title font12">
+							{{
+								$t(v.meta.title) && $t(v.meta.title).length >= 4
+									? $t(v.meta.title).substr(0, themeConfig.columnsAsideLayout === 'columns-vertical' ? 4 : 3)
+									: $t(v.meta.title)
+							}}
+						</div>
+					</div>
+					<div :class="themeConfig.columnsAsideLayout" v-else>
+						<a :href="v.meta.isLink" target="_blank">
+							<SvgIcon :name="v.meta.icon" />
+							<div class="columns-vertical-title font12">
+								{{
+									$t(v.meta.title) && $t(v.meta.title).length >= 4
+										? $t(v.meta.title).substr(0, themeConfig.columnsAsideLayout === 'columns-vertical' ? 4 : 3)
+										: $t(v.meta.title)
+								}}
+							</div>
+						</a>
+					</div>
+				</li>
+				<div ref="columnsAsideActiveRef" :class="themeConfig.columnsAsideStyle"></div>
+			</ul>
+		</el-scrollbar>
+	</div>
+</template>
+
+<script setup lang="ts" name="layoutColumnsAside">
+import { reactive, ref, onMounted, nextTick, watch, onUnmounted } from 'vue';
+import { useRoute, useRouter, onBeforeRouteUpdate, RouteRecordRaw } from 'vue-router';
+import { storeToRefs } from 'pinia';
+import pinia from '/@/stores/index';
+import { useRoutesList } from '/@/stores/routesList';
+import { useThemeConfig } from '/@/stores/themeConfig';
+import mittBus from '/@/utils/mitt';
+
+// 定义变量内容
+const columnsAsideOffsetTopRefs = ref<RefType>([]);
+const columnsAsideActiveRef = ref();
+const stores = useRoutesList();
+const storesThemeConfig = useThemeConfig();
+const { routesList, isColumnsMenuHover, isColumnsNavHover } = storeToRefs(stores);
+const { themeConfig } = storeToRefs(storesThemeConfig);
+const route = useRoute();
+const router = useRouter();
+const state = reactive<ColumnsAsideState>({
+	columnsAsideList: [],
+	liIndex: 0,
+	liOldIndex: null,
+	liHoverIndex: null,
+	liOldPath: null,
+	difference: 0,
+	routeSplit: [],
+});
+
+// 设置菜单高亮位置移动
+const setColumnsAsideMove = (k: number) => {
+	if (k === undefined) return false;
+	state.liIndex = k;
+	columnsAsideActiveRef.value.style.top = `${columnsAsideOffsetTopRefs.value[k].offsetTop + state.difference}px`;
+};
+// 菜单高亮点击事件
+const onColumnsAsideMenuClick = async (v: RouteItem) => {
+	let { path, redirect } = v;
+	if (redirect) router.push(redirect);
+	else router.push(path);
+	// 一个路由设置自动收起菜单
+	// https://gitee.com/lyt-top/vue-next-admin/issues/I6HW7H
+	if (!v.children) themeConfig.value.isCollapse = true;
+	else if (v.children.length > 1) themeConfig.value.isCollapse = false;
+};
+// 鼠标移入时,显示当前的子级菜单
+const onColumnsAsideMenuMouseenter = (v: RouteRecordRaw, k: number) => {
+	if (!themeConfig.value.isColumnsMenuHoverPreload) return false;
+	let { path } = v;
+	state.liOldPath = path;
+	state.liOldIndex = k;
+	state.liHoverIndex = k;
+	mittBus.emit('setSendColumnsChildren', setSendChildren(path));
+	stores.setColumnsMenuHover(false);
+	stores.setColumnsNavHover(true);
+};
+// 鼠标移走时,显示原来的子级菜单
+const onColumnsAsideMenuMouseleave = async () => {
+	if (!themeConfig.value.isColumnsMenuHoverPreload) return false;
+	await stores.setColumnsNavHover(false);
+	// 添加延时器,防止拿到的 store.state.routesList 值不是最新的
+	setTimeout(() => {
+		if (!isColumnsMenuHover && !isColumnsNavHover) mittBus.emit('restoreDefault');
+	}, 100);
+};
+// 设置高亮动态位置
+const onColumnsAsideDown = (k: number) => {
+	nextTick(() => {
+		setColumnsAsideMove(k);
+	});
+};
+// 设置/过滤路由(非静态路由/是否显示在菜单中)
+const setFilterRoutes = () => {
+	state.columnsAsideList = filterRoutesFun(routesList.value);
+	const resData: MittMenu = setSendChildren(route.path);
+	if (Object.keys(resData).length <= 0) return false;
+	onColumnsAsideDown(resData.item?.k);
+	// 刷新时,初始化一个路由设置自动收起菜单
+	// https://gitee.com/lyt-top/vue-next-admin/issues/I6HW7H
+	resData.children.length <= 1 ? (themeConfig.value.isCollapse = true) : (themeConfig.value.isCollapse = false);
+	mittBus.emit('setSendColumnsChildren', resData);
+};
+// 传送当前子级数据到菜单中
+const setSendChildren = (path: string) => {
+	const currentPathSplit = path.split('/');
+	let currentData: MittMenu = { children: [] };
+	state.columnsAsideList.map((v: RouteItem, k: number) => {
+		if (v.path === `/${currentPathSplit[1]}`) {
+			v['k'] = k;
+			currentData['item'] = { ...v };
+			currentData['children'] = [{ ...v }];
+			if (v.children) currentData['children'] = v.children;
+		}
+	});
+	return currentData;
+};
+// 路由过滤递归函数
+const filterRoutesFun = <T extends RouteItem>(arr: T[]): T[] => {
+	return arr
+		.filter((item: T) => !item.meta?.isHide)
+		.map((item: T) => {
+			item = Object.assign({}, item);
+			if (item.children) item.children = filterRoutesFun(item.children);
+			return item;
+		});
+};
+// tagsView 点击时,根据路由查找下标 columnsAsideList,实现左侧菜单高亮
+const setColumnsMenuHighlight = (path: string) => {
+	state.routeSplit = path.split('/');
+	state.routeSplit.shift();
+	const routeFirst = `/${state.routeSplit[0]}`;
+	const currentSplitRoute = state.columnsAsideList.find((v: RouteItem) => v.path === routeFirst);
+	if (!currentSplitRoute) return false;
+	// 延迟拿值,防止取不到
+	setTimeout(() => {
+		onColumnsAsideDown(currentSplitRoute.k);
+	}, 0);
+};
+// 页面加载时
+onMounted(() => {
+	setFilterRoutes();
+	// 销毁变量,防止鼠标再次移入时,保留了上次的记录
+	mittBus.on('restoreDefault', () => {
+		state.liOldIndex = null;
+		state.liOldPath = null;
+	});
+});
+// 页面卸载时
+onUnmounted(() => {
+	mittBus.off('restoreDefault', () => {});
+});
+// 路由更新时
+onBeforeRouteUpdate((to) => {
+	setColumnsMenuHighlight(to.path);
+	mittBus.emit('setSendColumnsChildren', setSendChildren(to.path));
+});
+// 监听布局配置信息的变化,动态增加菜单高亮位置移动像素
+watch(
+	pinia.state,
+	(val) => {
+		val.themeConfig.themeConfig.columnsAsideStyle === 'columnsRound' ? (state.difference = 3) : (state.difference = 0);
+		if (!val.routesList.isColumnsMenuHover && !val.routesList.isColumnsNavHover) {
+			state.liHoverIndex = null;
+			mittBus.emit('setSendColumnsChildren', setSendChildren(route.path));
+		} else {
+			state.liHoverIndex = state.liOldIndex;
+			if (!state.liOldPath) return false;
+			mittBus.emit('setSendColumnsChildren', setSendChildren(state.liOldPath));
+		}
+	},
+	{
+		deep: true,
+	}
+);
+</script>
+
+<style scoped lang="scss">
+.layout-columns-aside {
+	width: 70px;
+	height: 100%;
+	background: var(--next-bg-columnsMenuBar);
+	ul {
+		position: relative;
+		.layout-columns-active,
+		.layout-columns-active a {
+			color: var(--next-bg-columnsMenuBarColor) !important;
+			transition: 0.3s ease-in-out;
+		}
+		.layout-columns-hover {
+			color: var(--el-color-primary);
+			a {
+				color: var(--el-color-primary);
+			}
+		}
+		li {
+			color: var(--next-bg-columnsMenuBarColor);
+			width: 100%;
+			height: 50px;
+			text-align: center;
+			display: flex;
+			cursor: pointer;
+			position: relative;
+			z-index: 1;
+			&:hover {
+				@extend .layout-columns-hover;
+			}
+			.columns-vertical {
+				margin: auto;
+				.columns-vertical-title {
+					padding-top: 1px;
+				}
+			}
+			.columns-horizontal {
+				display: flex;
+				height: 50px;
+				width: 100%;
+				align-items: center;
+				padding: 0 5px;
+				i {
+					margin-right: 3px;
+				}
+				a {
+					display: flex;
+					.columns-horizontal-title {
+						padding-top: 1px;
+					}
+				}
+			}
+			a {
+				text-decoration: none;
+				color: var(--next-bg-columnsMenuBarColor);
+			}
+		}
+		.columns-round {
+			background: var(--el-color-primary);
+			color: var(--el-color-white);
+			position: absolute;
+			left: 50%;
+			top: 2px;
+			height: 44px;
+			width: 65px;
+			transform: translateX(-50%);
+			z-index: 0;
+			transition: 0.3s ease-in-out;
+			border-radius: 5px;
+		}
+		.columns-card {
+			@extend .columns-round;
+			top: 0;
+			height: 50px;
+			width: 100%;
+			border-radius: 0;
+		}
+	}
+}
+</style>
Added +18 -0
diff --git a/src/layout/component/header.vue b/src/layout/component/header.vue
new file mode 100644
index 0000000..29aa179
--- /dev/null
+++ b/src/layout/component/header.vue
@@ -0,0 +1,18 @@
+<template>
+	<el-header class="layout-header" v-show="!isTagsViewCurrenFull">
+		<NavBarsIndex />
+	</el-header>
+</template>
+
+<script setup lang="ts" name="layoutHeader">
+import { defineAsyncComponent } from 'vue';
+import { storeToRefs } from 'pinia';
+import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
+
+// 引入组件
+const NavBarsIndex = defineAsyncComponent(() => import('/@/layout/navBars/index.vue'));
+
+// 定义变量内容
+const storesTagsViewRoutes = useTagsViewRoutes();
+const { isTagsViewCurrenFull } = storeToRefs(storesTagsViewRoutes);
+</script>
Added +65 -0
diff --git a/src/layout/component/main.vue b/src/layout/component/main.vue
new file mode 100644
index 0000000..36cec94
--- /dev/null
+++ b/src/layout/component/main.vue
@@ -0,0 +1,65 @@
+<template>
+	<el-main class="layout-main" :style="isFixedHeader ? `height: calc(100% - ${setMainHeight})` : `minHeight: calc(100% - ${setMainHeight})`">
+		<el-scrollbar
+			ref="layoutMainScrollbarRef"
+			class="layout-main-scroll layout-backtop-header-fixed"
+			wrap-class="layout-main-scroll"
+			view-class="layout-main-scroll"
+		>
+			<LayoutParentView />
+			<LayoutFooter v-if="isFooter" />
+		</el-scrollbar>
+		<el-backtop :target="setBacktopClass" />
+	</el-main>
+</template>
+
+<script setup lang="ts" name="layoutMain">
+import { defineAsyncComponent, onMounted, computed, ref } from 'vue';
+import { useRoute } from 'vue-router';
+import { storeToRefs } from 'pinia';
+import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
+import { useThemeConfig } from '/@/stores/themeConfig';
+import { NextLoading } from '/@/utils/loading';
+
+// 引入组件
+const LayoutParentView = defineAsyncComponent(() => import('/@/layout/routerView/parent.vue'));
+const LayoutFooter = defineAsyncComponent(() => import('/@/layout/footer/index.vue'));
+
+// 定义变量内容
+const layoutMainScrollbarRef = ref();
+const route = useRoute();
+const storesTagsViewRoutes = useTagsViewRoutes();
+const storesThemeConfig = useThemeConfig();
+const { themeConfig } = storeToRefs(storesThemeConfig);
+const { isTagsViewCurrenFull } = storeToRefs(storesTagsViewRoutes);
+
+// 设置 footer 显示/隐藏
+const isFooter = computed(() => {
+	return themeConfig.value.isFooter && !route.meta.isIframe;
+});
+// 设置 header 固定
+const isFixedHeader = computed(() => {
+	return themeConfig.value.isFixedHeader;
+});
+// 设置 Backtop 回到顶部
+const setBacktopClass = computed(() => {
+	if (themeConfig.value.isFixedHeader) return `.layout-backtop-header-fixed .el-scrollbar__wrap`;
+	else return `.layout-backtop .el-scrollbar__wrap`;
+});
+// 设置主内容区的高度
+const setMainHeight = computed(() => {
+	if (isTagsViewCurrenFull.value) return '0px';
+	const { isTagsview, layout } = themeConfig.value;
+	if (isTagsview && layout !== 'classic') return '85px';
+	else return '51px';
+});
+// 页面加载前
+onMounted(() => {
+	NextLoading.done(600);
+});
+
+// 暴露变量
+defineExpose({
+	layoutMainScrollbarRef,
+});
+</script>
Added +25 -0
diff --git a/src/layout/footer/index.vue b/src/layout/footer/index.vue
new file mode 100644
index 0000000..97cffbf
--- /dev/null
+++ b/src/layout/footer/index.vue
@@ -0,0 +1,25 @@
+<template>
+	<div class="layout-footer pb15">
+		<div class="layout-footer-warp">
+			<div>vue-next-admin,Made by lyt with ❤️</div>
+			<div class="mt5">深圳市 xxx 公司版权所有</div>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts" name="layoutFooter">
+// 此处需有内容(注释也得),否则缓存将失败
+</script>
+
+<style scoped lang="scss">
+.layout-footer {
+	width: 100%;
+	display: flex;
+	&-warp {
+		margin: auto;
+		color: var(--el-text-color-secondary);
+		text-align: center;
+		animation: error-num 0.3s ease;
+	}
+}
+</style>
Added +50 -0
diff --git a/src/layout/index.vue b/src/layout/index.vue
new file mode 100644
index 0000000..f4ee956
--- /dev/null
+++ b/src/layout/index.vue
@@ -0,0 +1,50 @@
+<template>
+	<component :is="layouts[themeConfig.layout]" />
+</template>
+
+<script setup lang="ts" name="layout">
+import { onBeforeMount, onUnmounted, defineAsyncComponent } from 'vue';
+import { storeToRefs } from 'pinia';
+import { useThemeConfig } from '/@/stores/themeConfig';
+import { Local } from '/@/utils/storage';
+import mittBus from '/@/utils/mitt';
+
+// 引入组件
+const layouts: any = {
+	defaults: defineAsyncComponent(() => import('/@/layout/main/defaults.vue')),
+	classic: defineAsyncComponent(() => import('/@/layout/main/classic.vue')),
+	transverse: defineAsyncComponent(() => import('/@/layout/main/transverse.vue')),
+	columns: defineAsyncComponent(() => import('/@/layout/main/columns.vue')),
+};
+
+// 定义变量内容
+const storesThemeConfig = useThemeConfig();
+const { themeConfig } = storeToRefs(storesThemeConfig);
+
+// 窗口大小改变时(适配移动端)
+const onLayoutResize = () => {
+	if (!Local.get('oldLayout')) Local.set('oldLayout', themeConfig.value.layout);
+	const clientWidth = document.body.clientWidth;
+	if (clientWidth < 1000) {
+		themeConfig.value.isCollapse = false;
+		mittBus.emit('layoutMobileResize', {
+			layout: 'defaults',
+			clientWidth,
+		});
+	} else {
+		mittBus.emit('layoutMobileResize', {
+			layout: Local.get('oldLayout') ? Local.get('oldLayout') : themeConfig.value.layout,
+			clientWidth,
+		});
+	}
+};
+// 页面加载前
+onBeforeMount(() => {
+	onLayoutResize();
+	window.addEventListener('resize', onLayoutResize);
+});
+// 页面卸载时
+onUnmounted(() => {
+	window.removeEventListener('resize', onLayoutResize);
+});
+</script>
Added +352 -0
diff --git a/src/layout/lockScreen/index.vue b/src/layout/lockScreen/index.vue
new file mode 100644
index 0000000..a565e5d
--- /dev/null
+++ b/src/layout/lockScreen/index.vue
@@ -0,0 +1,352 @@
+<template>
+	<div v-show="state.isShowLockScreen">
+		<div class="layout-lock-screen-mask"></div>
+		<div class="layout-lock-screen-img" :class="{ 'layout-lock-screen-filter': state.isShowLoockLogin }"></div>
+		<div class="layout-lock-screen">
+			<div
+				class="layout-lock-screen-date"
+				ref="layoutLockScreenDateRef"
+				@mousedown="onDownPc"
+				@mousemove="onMovePc"
+				@mouseup="onEnd"
+				@touchstart.stop="onDownApp"
+				@touchmove.stop="onMoveApp"
+				@touchend.stop="onEnd"
+			>
+				<div class="layout-lock-screen-date-box">
+					<div class="layout-lock-screen-date-box-time">
+						{{ state.time.hm }}<span class="layout-lock-screen-date-box-minutes">{{ state.time.s }}</span>
+					</div>
+					<div class="layout-lock-screen-date-box-info">{{ state.time.mdq }}</div>
+				</div>
+				<div class="layout-lock-screen-date-top">
+					<SvgIcon name="ele-Top" />
+					<div class="layout-lock-screen-date-top-text">上滑解锁</div>
+				</div>
+			</div>
+			<transition name="el-zoom-in-center">
+				<div v-show="state.isShowLoockLogin" class="layout-lock-screen-login">
+					<div class="layout-lock-screen-login-box">
+						<div class="layout-lock-screen-login-box-img">
+							<img src="https://img2.baidu.com/it/u=1978192862,2048448374&fm=253&fmt=auto&app=138&f=JPEG?w=504&h=500" />
+						</div>
+						<div class="layout-lock-screen-login-box-name">Administrator</div>
+						<div class="layout-lock-screen-login-box-value">
+							<el-input
+								placeholder="请输入密码"
+								ref="layoutLockScreenInputRef"
+								v-model="state.lockScreenPassword"
+								@keyup.enter.native.stop="onLockScreenSubmit()"
+							>
+								<template #append>
+									<el-button @click="onLockScreenSubmit">
+										<el-icon class="el-input__icon">
+											<ele-Right />
+										</el-icon>
+									</el-button>
+								</template>
+							</el-input>
+						</div>
+					</div>
+					<div class="layout-lock-screen-login-icon">
+						<SvgIcon name="ele-Microphone" :size="20" />
+						<SvgIcon name="ele-AlarmClock" :size="20" />
+						<SvgIcon name="ele-SwitchButton" :size="20" />
+					</div>
+				</div>
+			</transition>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts" name="layoutLockScreen">
+import { nextTick, onMounted, reactive, ref, onUnmounted } from 'vue';
+import { formatDate } from '/@/utils/formatTime';
+import { Local } from '/@/utils/storage';
+import { storeToRefs } from 'pinia';
+import { useThemeConfig } from '/@/stores/themeConfig';
+
+// 定义变量内容
+const layoutLockScreenDateRef = ref<HtmlType>();
+const layoutLockScreenInputRef = ref();
+const storesThemeConfig = useThemeConfig();
+const { themeConfig } = storeToRefs(storesThemeConfig);
+const state = reactive({
+	transparency: 1,
+	downClientY: 0,
+	moveDifference: 0,
+	isShowLoockLogin: false,
+	isFlags: false,
+	querySelectorEl: '' as HtmlType,
+	time: {
+		hm: '',
+		s: '',
+		mdq: '',
+	},
+	setIntervalTime: 0,
+	isShowLockScreen: false,
+	isShowLockScreenIntervalTime: 0,
+	lockScreenPassword: '',
+});
+
+// 鼠标按下 pc
+const onDownPc = (down: MouseEvent) => {
+	state.isFlags = true;
+	state.downClientY = down.clientY;
+};
+// 鼠标按下 app
+const onDownApp = (down: TouchEvent) => {
+	state.isFlags = true;
+	state.downClientY = down.touches[0].clientY;
+};
+// 鼠标移动 pc
+const onMovePc = (move: MouseEvent) => {
+	state.moveDifference = move.clientY - state.downClientY;
+	onMove();
+};
+// 鼠标移动 app
+const onMoveApp = (move: TouchEvent) => {
+	state.moveDifference = move.touches[0].clientY - state.downClientY;
+	onMove();
+};
+// 鼠标移动事件
+const onMove = () => {
+	if (state.isFlags) {
+		const el = <HTMLElement>state.querySelectorEl;
+		const opacitys = (state.transparency -= 1 / 200);
+		if (state.moveDifference >= 0) return false;
+		el.setAttribute('style', `top:${state.moveDifference}px;cursor:pointer;opacity:${opacitys};`);
+		if (state.moveDifference < -400) {
+			el.setAttribute('style', `top:${-el.clientHeight}px;cursor:pointer;transition:all 0.3s ease;`);
+			state.moveDifference = -el.clientHeight;
+			setTimeout(() => {
+				el && el.parentNode?.removeChild(el);
+			}, 300);
+		}
+		if (state.moveDifference === -el.clientHeight) {
+			state.isShowLoockLogin = true;
+			layoutLockScreenInputRef.value.focus();
+		}
+	}
+};
+// 鼠标松开
+const onEnd = () => {
+	state.isFlags = false;
+	state.transparency = 1;
+	if (state.moveDifference >= -400) {
+		(<HTMLElement>state.querySelectorEl).setAttribute('style', `top:0px;opacity:1;transition:all 0.3s ease;`);
+	}
+};
+// 获取要拖拽的初始元素
+const initGetElement = () => {
+	nextTick(() => {
+		state.querySelectorEl = layoutLockScreenDateRef.value;
+	});
+};
+// 时间初始化
+const initTime = () => {
+	state.time.hm = formatDate(new Date(), 'HH:MM');
+	state.time.s = formatDate(new Date(), 'SS');
+	state.time.mdq = formatDate(new Date(), 'mm月dd日,WWW');
+};
+// 时间初始化定时器
+const initSetTime = () => {
+	initTime();
+	state.setIntervalTime = window.setInterval(() => {
+		initTime();
+	}, 1000);
+};
+// 锁屏时间定时器
+const initLockScreen = () => {
+	if (themeConfig.value.isLockScreen) {
+		state.isShowLockScreenIntervalTime = window.setInterval(() => {
+			if (themeConfig.value.lockScreenTime <= 1) {
+				state.isShowLockScreen = true;
+				setLocalThemeConfig();
+				return false;
+			}
+			themeConfig.value.lockScreenTime--;
+		}, 1000);
+	} else {
+		clearInterval(state.isShowLockScreenIntervalTime);
+	}
+};
+// 存储布局配置
+const setLocalThemeConfig = () => {
+	themeConfig.value.isDrawer = false;
+	Local.set('themeConfig', themeConfig.value);
+};
+// 密码输入点击事件
+const onLockScreenSubmit = () => {
+	themeConfig.value.isLockScreen = false;
+	themeConfig.value.lockScreenTime = 30;
+	setLocalThemeConfig();
+};
+// 页面加载时
+onMounted(() => {
+	initGetElement();
+	initSetTime();
+	initLockScreen();
+});
+// 页面卸载时
+onUnmounted(() => {
+	window.clearInterval(state.setIntervalTime);
+	window.clearInterval(state.isShowLockScreenIntervalTime);
+});
+</script>
+
+<style scoped lang="scss">
+.layout-lock-screen-fixed {
+	position: fixed;
+	top: 0;
+	left: 0;
+	width: 100%;
+	height: 100%;
+}
+.layout-lock-screen-filter {
+	filter: blur(1px);
+}
+.layout-lock-screen-mask {
+	background: var(--el-color-white);
+	@extend .layout-lock-screen-fixed;
+	z-index: 9999990;
+}
+.layout-lock-screen-img {
+	@extend .layout-lock-screen-fixed;
+	background-image: url('https://img-blog.csdnimg.cn/afa9c317667f47d5bea34b85af45979e.png#pic_center');
+	background-size: 100% 100%;
+	z-index: 9999991;
+}
+.layout-lock-screen {
+	@extend .layout-lock-screen-fixed;
+	z-index: 9999992;
+	&-date {
+		position: absolute;
+		left: 0;
+		top: 0;
+		width: 100%;
+		height: 100%;
+		color: var(--el-color-white);
+		z-index: 9999993;
+		user-select: none;
+		&-box {
+			position: absolute;
+			left: 30px;
+			bottom: 50px;
+			&-time {
+				font-size: 100px;
+				color: var(--el-color-white);
+			}
+			&-info {
+				font-size: 40px;
+				color: var(--el-color-white);
+			}
+			&-minutes {
+				font-size: 16px;
+			}
+		}
+		&-top {
+			width: 40px;
+			height: 40px;
+			line-height: 40px;
+			border-radius: 100%;
+			border: 1px solid var(--el-border-color-light, #ebeef5);
+			background: rgba(255, 255, 255, 0.1);
+			color: var(--el-color-white);
+			opacity: 0.8;
+			position: absolute;
+			right: 30px;
+			bottom: 50px;
+			text-align: center;
+			overflow: hidden;
+			transition: all 0.3s ease;
+			i {
+				transition: all 0.3s ease;
+			}
+			&-text {
+				opacity: 0;
+				position: absolute;
+				top: 150%;
+				font-size: 12px;
+				color: var(--el-color-white);
+				left: 50%;
+				line-height: 1.2;
+				transform: translate(-50%, -50%);
+				transition: all 0.3s ease;
+				width: 35px;
+			}
+			&:hover {
+				border: 1px solid rgba(255, 255, 255, 0.5);
+				background: rgba(255, 255, 255, 0.2);
+				box-shadow: 0 0 12px 0 rgba(255, 255, 255, 0.5);
+				color: var(--el-color-white);
+				opacity: 1;
+				transition: all 0.3s ease;
+				i {
+					transform: translateY(-40px);
+					transition: all 0.3s ease;
+				}
+				.layout-lock-screen-date-top-text {
+					opacity: 1;
+					top: 50%;
+					transition: all 0.3s ease;
+				}
+			}
+		}
+	}
+	&-login {
+		position: relative;
+		z-index: 9999994;
+		width: 100%;
+		height: 100%;
+		left: 0;
+		top: 0;
+		display: flex;
+		flex-direction: column;
+		justify-content: center;
+		color: var(--el-color-white);
+		&-box {
+			text-align: center;
+			margin: auto;
+			&-img {
+				width: 180px;
+				height: 180px;
+				margin: auto;
+				img {
+					width: 100%;
+					height: 100%;
+					border-radius: 100%;
+				}
+			}
+			&-name {
+				font-size: 26px;
+				margin: 15px 0 30px;
+			}
+		}
+		&-icon {
+			position: absolute;
+			right: 30px;
+			bottom: 30px;
+			i {
+				font-size: 20px;
+				margin-left: 15px;
+				cursor: pointer;
+				opacity: 0.8;
+				&:hover {
+					opacity: 1;
+				}
+			}
+		}
+	}
+}
+:deep(.el-input-group__append) {
+	background: var(--el-color-white);
+	padding: 0px 15px;
+}
+:deep(.el-input__inner) {
+	border-right-color: var(--el-border-color-extra-light);
+	&:hover {
+		border-color: var(--el-border-color-extra-light);
+	}
+}
+</style>
Added +77 -0
diff --git a/src/layout/logo/index.vue b/src/layout/logo/index.vue
new file mode 100644
index 0000000..dc5810b
--- /dev/null
+++ b/src/layout/logo/index.vue
@@ -0,0 +1,77 @@
+<template>
+	<div class="layout-logo" v-if="setShowLogo" @click="onThemeConfigChange">
+		<img :src="logoMini" class="layout-logo-medium-img" />
+		<span>{{ themeConfig.globalTitle }}</span>
+	</div>
+	<div class="layout-logo-size" v-else @click="onThemeConfigChange">
+		<img :src="logoMini" class="layout-logo-size-img" />
+	</div>
+</template>
+
+<script setup lang="ts" name="layoutLogo">
+import { computed } from 'vue';
+import { storeToRefs } from 'pinia';
+import { useThemeConfig } from '/@/stores/themeConfig';
+import logoMini from '/@/assets/logo-mini.png';
+
+// 定义变量内容
+const storesThemeConfig = useThemeConfig();
+const { themeConfig } = storeToRefs(storesThemeConfig);
+
+// 设置 logo 的显示。classic 经典布局默认显示 logo
+const setShowLogo = computed(() => {
+	let { isCollapse, layout } = themeConfig.value;
+	return !isCollapse || layout === 'classic' || document.body.clientWidth < 1000;
+});
+// logo 点击实现菜单展开/收起
+const onThemeConfigChange = () => {
+	if (themeConfig.value.layout === 'transverse') return false;
+	themeConfig.value.isCollapse = !themeConfig.value.isCollapse;
+};
+</script>
+
+<style scoped lang="scss">
+.layout-logo {
+	width: 220px;
+	height: 50px;
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	box-shadow: rgb(0 21 41 / 2%) 0px 1px 4px;
+	color: var(--el-color-primary);
+	// color: #26a59a;
+	font-size: 16px;
+	cursor: pointer;
+	animation: logoAnimation 0.3s ease-in-out;
+	span {
+		white-space: nowrap;
+		display: inline-block;
+		// color: var(--next-bg-menuBarColor);
+	}
+	// &:hover {
+	// 	span {
+	// 		color: var(--color-primary-light-2);
+	// 	}
+	// }
+	&-medium-img {
+		width: 20px;
+		margin-right: 5px;
+	}
+}
+.layout-logo-size {
+	width: 100%;
+	height: 50px;
+	display: flex;
+	cursor: pointer;
+	animation: logoAnimation 0.3s ease-in-out;
+	&-img {
+		width: 20px;
+		margin: auto;
+	}
+	&:hover {
+		img {
+			animation: logoAnimation 0.3s ease-in-out;
+		}
+	}
+}
+</style>
Added +71 -0
diff --git a/src/layout/main/classic.vue b/src/layout/main/classic.vue
new file mode 100644
index 0000000..56d3b8e
--- /dev/null
+++ b/src/layout/main/classic.vue
@@ -0,0 +1,71 @@
+<template>
+	<el-container class="layout-container flex-center">
+		<LayoutHeader />
+		<el-container class="layout-mian-height-50">
+			<LayoutAside />
+			<div class="flex-center layout-backtop">
+				<LayoutTagsView v-if="isTagsview" />
+				<LayoutMain ref="layoutMainRef" />
+			</div>
+		</el-container>
+	</el-container>
+</template>
+
+<script setup lang="ts" name="layoutClassic">
+import { defineAsyncComponent, computed, ref, watch, nextTick, onMounted } from 'vue';
+import { useRoute } from 'vue-router';
+import { storeToRefs } from 'pinia';
+import { useThemeConfig } from '/@/stores/themeConfig';
+
+// 引入组件
+const LayoutAside = defineAsyncComponent(() => import('/@/layout/component/aside.vue'));
+const LayoutHeader = defineAsyncComponent(() => import('/@/layout/component/header.vue'));
+const LayoutMain = defineAsyncComponent(() => import('/@/layout/component/main.vue'));
+const LayoutTagsView = defineAsyncComponent(() => import('/@/layout/navBars/tagsView/tagsView.vue'));
+
+// 定义变量内容
+const layoutMainRef = ref<InstanceType<typeof LayoutMain>>();
+const route = useRoute();
+const storesThemeConfig = useThemeConfig();
+const { themeConfig } = storeToRefs(storesThemeConfig);
+
+// 判断是否显示 tasgview
+const isTagsview = computed(() => {
+	return themeConfig.value.isTagsview;
+});
+// 重置滚动条高度,更新子级 scrollbar
+const updateScrollbar = () => {
+	layoutMainRef.value?.layoutMainScrollbarRef.update();
+};
+// 重置滚动条高度,由于组件是异步引入的
+const initScrollBarHeight = () => {
+	nextTick(() => {
+		setTimeout(() => {
+			updateScrollbar();
+			// '!' not null 断言操作符,不执行运行时检查
+			layoutMainRef.value!.layoutMainScrollbarRef.wrapRef.scrollTop = 0;
+		}, 500);
+	});
+};
+// 页面加载时
+onMounted(() => {
+	initScrollBarHeight();
+});
+// 监听路由的变化,切换界面时,滚动条置顶
+watch(
+	() => route.path,
+	() => {
+		initScrollBarHeight();
+	}
+);
+// 监听 themeConfig 配置文件的变化,更新菜单 el-scrollbar 的高度
+watch(
+	themeConfig,
+	() => {
+		updateScrollbar();
+	},
+	{
+		deep: true,
+	}
+);
+</script>
Added +71 -0
diff --git a/src/layout/main/columns.vue b/src/layout/main/columns.vue
new file mode 100644
index 0000000..e838729
--- /dev/null
+++ b/src/layout/main/columns.vue
@@ -0,0 +1,71 @@
+<template>
+	<el-container class="layout-container">
+		<ColumnsAside />
+		<el-container class="layout-columns-warp layout-container-view h100">
+			<LayoutAside />
+			<el-scrollbar ref="layoutScrollbarRef" class="layout-backtop">
+				<LayoutHeader />
+				<LayoutMain ref="layoutMainRef" />
+			</el-scrollbar>
+		</el-container>
+	</el-container>
+</template>
+
+<script setup lang="ts" name="layoutColumns">
+import { defineAsyncComponent, watch, onMounted, nextTick, ref } from 'vue';
+import { useRoute } from 'vue-router';
+import { storeToRefs } from 'pinia';
+import { useThemeConfig } from '/@/stores/themeConfig';
+
+// 引入组件
+const LayoutAside = defineAsyncComponent(() => import('/@/layout/component/aside.vue'));
+const LayoutHeader = defineAsyncComponent(() => import('/@/layout/component/header.vue'));
+const LayoutMain = defineAsyncComponent(() => import('/@/layout/component/main.vue'));
+const ColumnsAside = defineAsyncComponent(() => import('/@/layout/component/columnsAside.vue'));
+
+// 定义变量内容
+const layoutScrollbarRef = ref<RefType>('');
+const layoutMainRef = ref<InstanceType<typeof LayoutMain>>();
+const route = useRoute();
+const storesThemeConfig = useThemeConfig();
+const { themeConfig } = storeToRefs(storesThemeConfig);
+
+// 重置滚动条高度
+const updateScrollbar = () => {
+	// 更新父级 scrollbar
+	layoutScrollbarRef.value.update();
+	// 更新子级 scrollbar
+	layoutMainRef.value && layoutMainRef.value!.layoutMainScrollbarRef.update();
+};
+// 重置滚动条高度,由于组件是异步引入的
+const initScrollBarHeight = () => {
+	nextTick(() => {
+		setTimeout(() => {
+			updateScrollbar();
+			layoutScrollbarRef.value.wrapRef.scrollTop = 0;
+			layoutMainRef.value!.layoutMainScrollbarRef.wrapRef.scrollTop = 0;
+		}, 500);
+	});
+};
+// 页面加载时
+onMounted(() => {
+	initScrollBarHeight();
+});
+// 监听路由的变化,切换界面时,滚动条置顶
+watch(
+	() => route.path,
+	() => {
+		initScrollBarHeight();
+	}
+);
+// 监听 themeConfig 配置文件的变化,更新菜单 el-scrollbar 的高度
+watch(
+	themeConfig,
+	() => {
+		updateScrollbar();
+	},
+	{
+		deep: true,
+	}
+);
+</script>
Added +71 -0
diff --git a/src/layout/main/defaults.vue b/src/layout/main/defaults.vue
new file mode 100644
index 0000000..5a9a16c
--- /dev/null
+++ b/src/layout/main/defaults.vue
@@ -0,0 +1,71 @@
+<template>
+	<el-container class="layout-container">
+		<LayoutAside />
+		<el-container class="layout-container-view h100">
+			<el-scrollbar ref="layoutScrollbarRef" class="layout-backtop">
+				<LayoutHeader />
+				<LayoutMain ref="layoutMainRef" />
+			</el-scrollbar>
+		</el-container>
+	</el-container>
+</template>
+
+<script setup lang="ts" name="layoutDefaults">
+import { defineAsyncComponent, watch, onMounted, nextTick, ref } from 'vue';
+import { useRoute } from 'vue-router';
+import { storeToRefs } from 'pinia';
+import { useThemeConfig } from '/@/stores/themeConfig';
+import { NextLoading } from '/@/utils/loading';
+
+// 引入组件
+const LayoutAside = defineAsyncComponent(() => import('/@/layout/component/aside.vue'));
+const LayoutHeader = defineAsyncComponent(() => import('/@/layout/component/header.vue'));
+const LayoutMain = defineAsyncComponent(() => import('/@/layout/component/main.vue'));
+
+// 定义变量内容
+const layoutScrollbarRef = ref<RefType>('');
+const layoutMainRef = ref<InstanceType<typeof LayoutMain>>();
+const route = useRoute();
+const storesThemeConfig = useThemeConfig();
+const { themeConfig } = storeToRefs(storesThemeConfig);
+
+// 重置滚动条高度
+const updateScrollbar = () => {
+	// 更新父级 scrollbar
+	layoutScrollbarRef.value.update();
+	// 更新子级 scrollbar
+	layoutMainRef.value!.layoutMainScrollbarRef.update();
+};
+// 重置滚动条高度,由于组件是异步引入的
+const initScrollBarHeight = () => {
+	nextTick(() => {
+		setTimeout(() => {
+			updateScrollbar();
+			layoutScrollbarRef.value.wrapRef.scrollTop = 0;
+			layoutMainRef.value!.layoutMainScrollbarRef.wrapRef.scrollTop = 0;
+		}, 500);
+	});
+};
+// 页面加载时
+onMounted(() => {
+	initScrollBarHeight();
+	NextLoading.done(600);
+});
+// 监听路由的变化,切换界面时,滚动条置顶
+watch(
+	() => route.path,
+	() => {
+		initScrollBarHeight();
+	}
+);
+// 监听 themeConfig 配置文件的变化,更新菜单 el-scrollbar 的高度
+watch(
+	themeConfig,
+	() => {
+		updateScrollbar();
+	},
+	{
+		deep: true,
+	}
+);
+</script>
Added +58 -0
diff --git a/src/layout/main/transverse.vue b/src/layout/main/transverse.vue
new file mode 100644
index 0000000..1cd7773
--- /dev/null
+++ b/src/layout/main/transverse.vue
@@ -0,0 +1,58 @@
+<template>
+	<el-container class="layout-container flex-center layout-backtop">
+		<LayoutHeader />
+		<LayoutMain ref="layoutMainRef" />
+	</el-container>
+</template>
+
+<script setup lang="ts" name="layoutTransverse">
+import { defineAsyncComponent, ref, watch, nextTick, onMounted } from 'vue';
+import { useRoute } from 'vue-router';
+import { storeToRefs } from 'pinia';
+import { useThemeConfig } from '/@/stores/themeConfig';
+
+// 引入组件
+const LayoutHeader = defineAsyncComponent(() => import('/@/layout/component/header.vue'));
+const LayoutMain = defineAsyncComponent(() => import('/@/layout/component/main.vue'));
+
+// 定义变量内容
+const layoutMainRef = ref<InstanceType<typeof LayoutMain>>();
+const storesThemeConfig = useThemeConfig();
+const { themeConfig } = storeToRefs(storesThemeConfig);
+const route = useRoute();
+
+// 重置滚动条高度,更新子级 scrollbar
+const updateScrollbar = () => {
+	layoutMainRef.value!.layoutMainScrollbarRef.update();
+};
+// 重置滚动条高度,由于组件是异步引入的
+const initScrollBarHeight = () => {
+	nextTick(() => {
+		setTimeout(() => {
+			updateScrollbar();
+			layoutMainRef.value!.layoutMainScrollbarRef.wrapRef.scrollTop = 0;
+		}, 500);
+	});
+};
+// 页面加载时
+onMounted(() => {
+	initScrollBarHeight();
+});
+// 监听路由的变化,切换界面时,滚动条置顶
+watch(
+	() => route.path,
+	() => {
+		initScrollBarHeight();
+	}
+);
+// 监听 themeConfig 配置文件的变化,更新菜单 el-scrollbar 的高度
+watch(
+	themeConfig,
+	() => {
+		updateScrollbar();
+	},
+	{
+		deep: true,
+	}
+);
+</script>
Added +146 -0
diff --git a/src/layout/navBars/breadcrumb/breadcrumb.vue b/src/layout/navBars/breadcrumb/breadcrumb.vue
new file mode 100644
index 0000000..7dfaca6
--- /dev/null
+++ b/src/layout/navBars/breadcrumb/breadcrumb.vue
@@ -0,0 +1,146 @@
+<template>
+	<div v-if="isShowBreadcrumb" class="layout-navbars-breadcrumb">
+		<SvgIcon
+			class="layout-navbars-breadcrumb-icon"
+			:name="themeConfig.isCollapse ? 'ele-Expand' : 'ele-Fold'"
+			:size="16"
+			@click="onThemeConfigChange"
+		/>
+		<el-breadcrumb class="layout-navbars-breadcrumb-hide">
+			<transition-group name="breadcrumb">
+				<el-breadcrumb-item v-for="(v, k) in state.breadcrumbList" :key="!v.meta.tagsViewName ? v.meta.title : v.meta.tagsViewName">
+					<span v-if="k === state.breadcrumbList.length - 1" class="layout-navbars-breadcrumb-span">
+						<SvgIcon :name="v.meta.icon" class="layout-navbars-breadcrumb-iconfont" v-if="themeConfig.isBreadcrumbIcon" />
+						<div v-if="!v.meta.tagsViewName">{{ $t(v.meta.title) }}</div>
+						<div v-else>{{ v.meta.tagsViewName }}</div>
+					</span>
+					<a v-else @click.prevent="onBreadcrumbClick(v)">
+						<SvgIcon :name="v.meta.icon" class="layout-navbars-breadcrumb-iconfont" v-if="themeConfig.isBreadcrumbIcon" />{{ $t(v.meta.title) }}
+					</a>
+				</el-breadcrumb-item>
+			</transition-group>
+		</el-breadcrumb>
+	</div>
+</template>
+
+<script setup lang="ts" name="layoutBreadcrumb">
+import { reactive, computed, onMounted } from 'vue';
+import { onBeforeRouteUpdate, useRoute, useRouter } from 'vue-router';
+import { Local } from '/@/utils/storage';
+import other from '/@/utils/other';
+import { storeToRefs } from 'pinia';
+import { useThemeConfig } from '/@/stores/themeConfig';
+import { useRoutesList } from '/@/stores/routesList';
+
+// 定义变量内容
+const stores = useRoutesList();
+const storesThemeConfig = useThemeConfig();
+const { themeConfig } = storeToRefs(storesThemeConfig);
+const { routesList } = storeToRefs(stores);
+const route = useRoute();
+const router = useRouter();
+const state = reactive<BreadcrumbState>({
+	breadcrumbList: [],
+	routeSplit: [],
+	routeSplitFirst: '',
+	routeSplitIndex: 1,
+});
+
+// 动态设置经典、横向布局不显示
+const isShowBreadcrumb = computed(() => {
+	initRouteSplit(route.path);
+	const { layout, isBreadcrumb } = themeConfig.value;
+	if (layout === 'classic' || layout === 'transverse') return false;
+	else return isBreadcrumb ? true : false;
+});
+// 面包屑点击时
+const onBreadcrumbClick = (v: RouteItem) => {
+	const { redirect, path } = v;
+	if (redirect) router.push(redirect);
+	else router.push(path);
+};
+// 展开/收起左侧菜单点击
+const onThemeConfigChange = () => {
+	themeConfig.value.isCollapse = !themeConfig.value.isCollapse;
+	setLocalThemeConfig();
+};
+// 存储布局配置
+const setLocalThemeConfig = () => {
+	Local.remove('themeConfig');
+	Local.set('themeConfig', themeConfig.value);
+};
+// 处理面包屑数据
+const getBreadcrumbList = (arr: RouteItems) => {
+	arr.forEach((item: RouteItem) => {
+		state.routeSplit.forEach((v: string, k: number, arrs: string[]) => {
+			if (state.routeSplitFirst === item.path) {
+				state.routeSplitFirst += `/${arrs[state.routeSplitIndex]}`;
+				state.breadcrumbList.push(item);
+				state.routeSplitIndex++;
+				if (item.children) getBreadcrumbList(item.children);
+			}
+		});
+	});
+};
+// 当前路由字符串切割成数组,并删除第一项空内容
+const initRouteSplit = (path: string) => {
+	if (!themeConfig.value.isBreadcrumb) return false;
+	state.breadcrumbList = [routesList.value[0]];
+	state.routeSplit = path.split('/');
+	state.routeSplit.shift();
+	state.routeSplitFirst = `/${state.routeSplit[0]}`;
+	state.routeSplitIndex = 1;
+	getBreadcrumbList(routesList.value);
+	if (route.name === 'home' || (route.name === 'notFound' && state.breadcrumbList.length > 1)) state.breadcrumbList.shift();
+	if (state.breadcrumbList.length > 0)
+		state.breadcrumbList[state.breadcrumbList.length - 1].meta.tagsViewName = other.setTagsViewNameI18n(<RouteToFrom>route);
+};
+// 页面加载时
+onMounted(() => {
+	initRouteSplit(route.path);
+});
+// 路由更新时
+onBeforeRouteUpdate((to) => {
+	initRouteSplit(to.path);
+});
+</script>
+
+<style scoped lang="scss">
+.layout-navbars-breadcrumb {
+	flex: 1;
+	height: inherit;
+	display: flex;
+	align-items: center;
+	.layout-navbars-breadcrumb-icon {
+		cursor: pointer;
+		font-size: 18px;
+		color: var(--next-bg-topBarColor);
+		height: 100%;
+		width: 40px;
+		opacity: 0.8;
+		&:hover {
+			opacity: 1;
+		}
+	}
+	.layout-navbars-breadcrumb-span {
+		display: flex;
+		opacity: 0.7;
+		color: var(--next-bg-topBarColor);
+	}
+	.layout-navbars-breadcrumb-iconfont {
+		font-size: 14px;
+		margin-right: 5px;
+	}
+	:deep(.el-breadcrumb__separator) {
+		opacity: 0.7;
+		color: var(--next-bg-topBarColor);
+	}
+	:deep(.el-breadcrumb__inner a, .el-breadcrumb__inner.is-link) {
+		font-weight: unset !important;
+		color: var(--next-bg-topBarColor);
+		&:hover {
+			color: var(--el-color-primary) !important;
+		}
+	}
+}
+</style>
Added +53 -0
diff --git a/src/layout/navBars/breadcrumb/closeFull.vue b/src/layout/navBars/breadcrumb/closeFull.vue
new file mode 100644
index 0000000..080dad5
--- /dev/null
+++ b/src/layout/navBars/breadcrumb/closeFull.vue
@@ -0,0 +1,53 @@
+<template>
+	<div class="layout-navbars-close-full" v-if="isTagsViewCurrenFull">
+		<div class="layout-navbars-close-full-icon">
+			<SvgIcon name="ele-Close" :title="$t('message.tagsView.closeFullscreen')" @click="onCloseFullscreen" />
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts" name="layoutCloseFull">
+import { storeToRefs } from 'pinia';
+import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
+
+// 定义变量内容
+const stores = useTagsViewRoutes();
+const { isTagsViewCurrenFull } = storeToRefs(stores);
+
+// 关闭当前全屏
+const onCloseFullscreen = () => {
+	stores.setCurrenFullscreen(false);
+};
+</script>
+
+<style scoped lang="scss">
+.layout-navbars-close-full {
+	position: fixed;
+	z-index: 9999999999;
+	right: -30px;
+	top: -30px;
+	.layout-navbars-close-full-icon {
+		width: 60px;
+		height: 60px;
+		border-radius: 100%;
+		cursor: pointer;
+		background: rgba(0, 0, 0, 0.1);
+		transition: all 0.3s ease;
+		position: relative;
+		:deep(i) {
+			position: absolute;
+			left: 10px;
+			top: 35px;
+			color: #333333;
+			transition: all 0.3s ease;
+		}
+	}
+	&:hover {
+		transition: all 0.3s ease;
+		:deep(i) {
+			color: var(--el-color-primary);
+			transition: all 0.3s ease;
+		}
+	}
+}
+</style>
Added +107 -0
diff --git a/src/layout/navBars/breadcrumb/index.vue b/src/layout/navBars/breadcrumb/index.vue
new file mode 100644
index 0000000..29bd1bc
--- /dev/null
+++ b/src/layout/navBars/breadcrumb/index.vue
@@ -0,0 +1,107 @@
+<template>
+	<div class="layout-navbars-breadcrumb-index">
+		<Logo v-if="setIsShowLogo" />
+		<Breadcrumb />
+		<Horizontal :menuList="state.menuList" v-if="isLayoutTransverse" />
+		<User />
+	</div>
+</template>
+
+<script setup lang="ts" name="layoutBreadcrumbIndex">
+import { defineAsyncComponent, computed, reactive, onMounted, onUnmounted } from 'vue';
+import { useRoute } from 'vue-router';
+import { storeToRefs } from 'pinia';
+import { useRoutesList } from '/@/stores/routesList';
+import { useThemeConfig } from '/@/stores/themeConfig';
+import mittBus from '/@/utils/mitt';
+
+// 引入组件
+const Breadcrumb = defineAsyncComponent(() => import('/@/layout/navBars/breadcrumb/breadcrumb.vue'));
+const User = defineAsyncComponent(() => import('/@/layout/navBars/breadcrumb/user.vue'));
+const Logo = defineAsyncComponent(() => import('/@/layout/logo/index.vue'));
+const Horizontal = defineAsyncComponent(() => import('/@/layout/navMenu/horizontal.vue'));
+
+// 定义变量内容
+const stores = useRoutesList();
+const storesThemeConfig = useThemeConfig();
+const { themeConfig } = storeToRefs(storesThemeConfig);
+const { routesList } = storeToRefs(stores);
+const route = useRoute();
+const state = reactive({
+	menuList: [] as RouteItems,
+});
+
+// 设置 logo 显示/隐藏
+const setIsShowLogo = computed(() => {
+	let { isShowLogo, layout } = themeConfig.value;
+	return (isShowLogo && layout === 'classic') || (isShowLogo && layout === 'transverse');
+});
+// 设置是否显示横向导航菜单
+const isLayoutTransverse = computed(() => {
+	let { layout, isClassicSplitMenu } = themeConfig.value;
+	return layout === 'transverse' || (isClassicSplitMenu && layout === 'classic');
+});
+// 设置/过滤路由(非静态路由/是否显示在菜单中)
+const setFilterRoutes = () => {
+	let { layout, isClassicSplitMenu } = themeConfig.value;
+	if (layout === 'classic' && isClassicSplitMenu) {
+		state.menuList = delClassicChildren(filterRoutesFun(routesList.value));
+		const resData = setSendClassicChildren(route.path);
+		mittBus.emit('setSendClassicChildren', resData);
+	} else {
+		state.menuList = filterRoutesFun(routesList.value);
+	}
+};
+// 设置了分割菜单时,删除底下 children
+const delClassicChildren = <T extends ChilType>(arr: T[]): T[] => {
+	arr.map((v: T) => {
+		if (v.children) delete v.children;
+	});
+	return arr;
+};
+// 路由过滤递归函数
+const filterRoutesFun = <T extends RouteItem>(arr: T[]): T[] => {
+	return arr
+		.filter((item: T) => !item.meta?.isHide)
+		.map((item: T) => {
+			item = Object.assign({}, item);
+			if (item.children) item.children = filterRoutesFun(item.children);
+			return item;
+		});
+};
+// 传送当前子级数据到菜单中
+const setSendClassicChildren = (path: string) => {
+	const currentPathSplit = path.split('/');
+	let currentData: MittMenu = { children: [] };
+	filterRoutesFun(routesList.value).map((v: RouteItem, k: number) => {
+		if (v.path === `/${currentPathSplit[1]}`) {
+			v['k'] = k;
+			currentData['item'] = { ...v };
+			currentData['children'] = [{ ...v }];
+			if (v.children) currentData['children'] = v.children;
+		}
+	});
+	return currentData;
+};
+// 页面加载时
+onMounted(() => {
+	setFilterRoutes();
+	mittBus.on('getBreadcrumbIndexSetFilterRoutes', () => {
+		setFilterRoutes();
+	});
+});
+// 页面卸载时
+onUnmounted(() => {
+	mittBus.off('getBreadcrumbIndexSetFilterRoutes', () => {});
+});
+</script>
+
+<style scoped lang="scss">
+.layout-navbars-breadcrumb-index {
+	height: 50px;
+	display: flex;
+	align-items: center;
+	background: var(--next-bg-topBar);
+	border-bottom: 1px solid var(--next-border-color-light);
+}
+</style>
Added +124 -0
diff --git a/src/layout/navBars/breadcrumb/search.vue b/src/layout/navBars/breadcrumb/search.vue
new file mode 100644
index 0000000..624d997
--- /dev/null
+++ b/src/layout/navBars/breadcrumb/search.vue
@@ -0,0 +1,124 @@
+<template>
+	<div class="layout-search-dialog">
+		<el-dialog v-model="state.isShowSearch" destroy-on-close :show-close="false">
+			<template #footer>
+				<el-autocomplete
+					v-model="state.menuQuery"
+					:fetch-suggestions="menuSearch"
+					:placeholder="$t('message.user.searchPlaceholder')"
+					ref="layoutMenuAutocompleteRef"
+					@select="onHandleSelect"
+					:fit-input-width="true"
+				>
+					<template #prefix>
+						<el-icon class="el-input__icon">
+							<ele-Search />
+						</el-icon>
+					</template>
+					<template #default="{ item }">
+						<div>
+							<SvgIcon :name="item.meta.icon" class="mr5" />
+							{{ $t(item.meta.title) }}
+						</div>
+					</template>
+				</el-autocomplete>
+			</template>
+		</el-dialog>
+	</div>
+</template>
+
+<script setup lang="ts" name="layoutBreadcrumbSearch">
+import { reactive, ref, nextTick } from 'vue';
+import { useRouter } from 'vue-router';
+import { useI18n } from 'vue-i18n';
+import { storeToRefs } from 'pinia';
+import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
+
+// 定义变量内容
+const storesTagsViewRoutes = useTagsViewRoutes();
+const { tagsViewRoutes } = storeToRefs(storesTagsViewRoutes);
+const layoutMenuAutocompleteRef = ref();
+const { t } = useI18n();
+const router = useRouter();
+const state = reactive<SearchState>({
+	isShowSearch: false,
+	menuQuery: '',
+	tagsViewList: [],
+});
+
+// 搜索弹窗打开
+const openSearch = () => {
+	state.menuQuery = '';
+	state.isShowSearch = true;
+	initTageView();
+	nextTick(() => {
+		setTimeout(() => {
+			layoutMenuAutocompleteRef.value.focus();
+		});
+	});
+};
+// 搜索弹窗关闭
+const closeSearch = () => {
+	state.isShowSearch = false;
+};
+// 菜单搜索数据过滤
+const menuSearch = (queryString: string, cb: Function) => {
+	let results = queryString ? state.tagsViewList.filter(createFilter(queryString)) : state.tagsViewList;
+	cb(results);
+};
+// 菜单搜索过滤
+const createFilter = (queryString: string) => {
+	return (restaurant: RouteItem) => {
+		return (
+			restaurant.path.toLowerCase().indexOf(queryString.toLowerCase()) > -1 ||
+			restaurant.meta!.title!.toLowerCase().indexOf(queryString.toLowerCase()) > -1 ||
+			t(restaurant.meta!.title!).indexOf(queryString.toLowerCase()) > -1
+		);
+	};
+};
+// 初始化菜单数据
+const initTageView = () => {
+	if (state.tagsViewList.length > 0) return false;
+	tagsViewRoutes.value.map((v: RouteItem) => {
+		if (!v.meta?.isHide) state.tagsViewList.push({ ...v });
+	});
+};
+// 当前菜单选中时
+const onHandleSelect = (item: RouteItem) => {
+	let { path, redirect } = item;
+	if (item.meta?.isLink && !item.meta?.isIframe) window.open(item.meta?.isLink);
+	else if (redirect) router.push(redirect);
+	else router.push(path);
+	closeSearch();
+};
+
+// 暴露变量
+defineExpose({
+	openSearch,
+});
+</script>
+
+<style scoped lang="scss">
+.layout-search-dialog {
+	position: relative;
+	:deep(.el-dialog) {
+		.el-dialog__header,
+		.el-dialog__body {
+			display: none;
+		}
+		.el-dialog__footer {
+			position: absolute;
+			left: 50%;
+			transform: translateX(-50%);
+			top: -53vh;
+		}
+	}
+	:deep(.el-autocomplete) {
+		width: 560px;
+		position: absolute;
+		top: 150px;
+		left: 50%;
+		transform: translateX(-50%);
+	}
+}
+</style>
Added +824 -0
diff --git a/src/layout/navBars/breadcrumb/setings.vue b/src/layout/navBars/breadcrumb/setings.vue
new file mode 100644
index 0000000..45421c5
--- /dev/null
+++ b/src/layout/navBars/breadcrumb/setings.vue
@@ -0,0 +1,824 @@
+<template>
+	<div class="layout-breadcrumb-seting">
+		<el-drawer
+			:title="$t('message.layout.configTitle')"
+			v-model="getThemeConfig.isDrawer"
+			direction="rtl"
+			destroy-on-close
+			size="260px"
+			@close="onDrawerClose"
+		>
+			<el-scrollbar class="layout-breadcrumb-seting-bar">
+				<!-- 全局主题 -->
+				<el-divider content-position="left">{{ $t('message.layout.oneTitle') }}</el-divider>
+				<div class="layout-breadcrumb-seting-bar-flex">
+					<div class="layout-breadcrumb-seting-bar-flex-label">primary</div>
+					<div class="layout-breadcrumb-seting-bar-flex-value">
+						<el-color-picker v-model="getThemeConfig.primary" size="default" @change="onColorPickerChange"> </el-color-picker>
+					</div>
+				</div>
+				<div class="layout-breadcrumb-seting-bar-flex mt15">
+					<div class="layout-breadcrumb-seting-bar-flex-label">{{ $t('message.layout.fourIsDark') }}</div>
+					<div class="layout-breadcrumb-seting-bar-flex-value">
+						<el-switch v-model="getThemeConfig.isIsDark" size="small" @change="onAddDarkChange"></el-switch>
+					</div>
+				</div>
+
+				<!-- 顶栏设置 -->
+				<el-divider content-position="left">{{ $t('message.layout.twoTopTitle') }}</el-divider>
+				<div class="layout-breadcrumb-seting-bar-flex">
+					<div class="layout-breadcrumb-seting-bar-flex-label">{{ $t('message.layout.twoTopBar') }}</div>
+					<div class="layout-breadcrumb-seting-bar-flex-value">
+						<el-color-picker v-model="getThemeConfig.topBar" size="default" @change="onBgColorPickerChange('topBar')"> </el-color-picker>
+					</div>
+				</div>
+				<div class="layout-breadcrumb-seting-bar-flex">
+					<div class="layout-breadcrumb-seting-bar-flex-label">{{ $t('message.layout.twoTopBarColor') }}</div>
+					<div class="layout-breadcrumb-seting-bar-flex-value">
+						<el-color-picker v-model="getThemeConfig.topBarColor" size="default" @change="onBgColorPickerChange('topBarColor')"> </el-color-picker>
+					</div>
+				</div>
+				<div class="layout-breadcrumb-seting-bar-flex mt10">
+					<div class="layout-breadcrumb-seting-bar-flex-label">{{ $t('message.layout.twoIsTopBarColorGradual') }}</div>
+					<div class="layout-breadcrumb-seting-bar-flex-value">
+						<el-switch v-model="getThemeConfig.isTopBarColorGradual" size="small" @change="onTopBarGradualChange"></el-switch>
+					</div>
+				</div>
+
+				<!-- 菜单设置 -->
+				<el-divider content-position="left">{{ $t('message.layout.twoMenuTitle') }}</el-divider>
+				<div class="layout-breadcrumb-seting-bar-flex">
+					<div class="layout-breadcrumb-seting-bar-flex-label">{{ $t('message.layout.twoMenuBar') }}</div>
+					<div class="layout-breadcrumb-seting-bar-flex-value">
+						<el-color-picker v-model="getThemeConfig.menuBar" size="default" @change="onBgColorPickerChange('menuBar')"> </el-color-picker>
+					</div>
+				</div>
+				<div class="layout-breadcrumb-seting-bar-flex">
+					<div class="layout-breadcrumb-seting-bar-flex-label">{{ $t('message.layout.twoMenuBarColor') }}</div>
+					<div class="layout-breadcrumb-seting-bar-flex-value">
+						<el-color-picker v-model="getThemeConfig.menuBarColor" size="default" @change="onBgColorPickerChange('menuBarColor')"> </el-color-picker>
+					</div>
+				</div>
+				<div class="layout-breadcrumb-seting-bar-flex">
+					<div class="layout-breadcrumb-seting-bar-flex-label">{{ $t('message.layout.twoMenuBarActiveColor') }}</div>
+					<div class="layout-breadcrumb-seting-bar-flex-value">
+						<el-color-picker
+							v-model="getThemeConfig.menuBarActiveColor"
+							size="default"
+							show-alpha
+							@change="onBgColorPickerChange('menuBarActiveColor')"
+						/>
+					</div>
+				</div>
+				<div class="layout-breadcrumb-seting-bar-flex mt14">
+					<div class="layout-breadcrumb-seting-bar-flex-label">{{ $t('message.layout.twoIsMenuBarColorGradual') }}</div>
+					<div class="layout-breadcrumb-seting-bar-flex-value">
+						<el-switch v-model="getThemeConfig.isMenuBarColorGradual" size="small" @change="onMenuBarGradualChange"></el-switch>
+					</div>
+				</div>
+
+				<!-- 分栏设置 -->
+				<el-divider content-position="left" :style="{ opacity: getThemeConfig.layout !== 'columns' ? 0.5 : 1 }">{{
+					$t('message.layout.twoColumnsTitle')
+				}}</el-divider>
+				<div class="layout-breadcrumb-seting-bar-flex" :style="{ opacity: getThemeConfig.layout !== 'columns' ? 0.5 : 1 }">
+					<div class="layout-breadcrumb-seting-bar-flex-label">{{ $t('message.layout.twoColumnsMenuBar') }}</div>
+					<div class="layout-breadcrumb-seting-bar-flex-value">
+						<el-color-picker
+							v-model="getThemeConfig.columnsMenuBar"
+							size="default"
+							@change="onBgColorPickerChange('columnsMenuBar')"
+							:disabled="getThemeConfig.layout !== 'columns'"
+						>
+						</el-color-picker>
+					</div>
+				</div>
+				<div class="layout-breadcrumb-seting-bar-flex" :style="{ opacity: getThemeConfig.layout !== 'columns' ? 0.5 : 1 }">
+					<div class="layout-breadcrumb-seting-bar-flex-label">{{ $t('message.layout.twoColumnsMenuBarColor') }}</div>
+					<div class="layout-breadcrumb-seting-bar-flex-value">
+						<el-color-picker
+							v-model="getThemeConfig.columnsMenuBarColor"
+							size="default"
+							@change="onBgColorPickerChange('columnsMenuBarColor')"
+							:disabled="getThemeConfig.layout !== 'columns'"
+						>
+						</el-color-picker>
+					</div>
+				</div>
+				<div class="layout-breadcrumb-seting-bar-flex mt14" :style="{ opacity: getThemeConfig.layout !== 'columns' ? 0.5 : 1 }">
+					<div class="layout-breadcrumb-seting-bar-flex-label">{{ $t('message.layout.twoIsColumnsMenuBarColorGradual') }}</div>
+					<div class="layout-breadcrumb-seting-bar-flex-value">
+						<el-switch
+							v-model="getThemeConfig.isColumnsMenuBarColorGradual"
+							size="small"
+							@change="onColumnsMenuBarGradualChange"
+							:disabled="getThemeConfig.layout !== 'columns'"
+						></el-switch>
+					</div>
+				</div>
+				<div class="layout-breadcrumb-seting-bar-flex mt14" :style="{ opacity: getThemeConfig.layout !== 'columns' ? 0.5 : 1 }">
+					<div class="layout-breadcrumb-seting-bar-flex-label">{{ $t('message.layout.twoIsColumnsMenuHoverPreload') }}</div>
+					<div class="layout-breadcrumb-seting-bar-flex-value">
+						<el-switch
+							v-model="getThemeConfig.isColumnsMenuHoverPreload"
+							size="small"
+							@change="onColumnsMenuHoverPreloadChange"
+							:disabled="getThemeConfig.layout !== 'columns'"
+						></el-switch>
+					</div>
+				</div>
+
+				<!-- 界面设置 -->
+				<el-divider content-position="left">{{ $t('message.layout.threeTitle') }}</el-divider>
+				<div class="layout-breadcrumb-seting-bar-flex" :style="{ opacity: getThemeConfig.layout === 'transverse' ? 0.5 : 1 }">
+					<div class="layout-breadcrumb-seting-bar-flex-label">{{ $t('message.layout.threeIsCollapse') }}</div>
+					<div class="layout-breadcrumb-seting-bar-flex-value">
+						<el-switch
+							v-model="getThemeConfig.isCollapse"
+							:disabled="getThemeConfig.layout === 'transverse'"
+							size="small"
+							@change="onThemeConfigChange"
+						></el-switch>
+					</div>
+				</div>
+				<div class="layout-breadcrumb-seting-bar-flex mt15" :style="{ opacity: getThemeConfig.layout === 'transverse' ? 0.5 : 1 }">
+					<div class="layout-breadcrumb-seting-bar-flex-label">{{ $t('message.layout.threeIsUniqueOpened') }}</div>
+					<div class="layout-breadcrumb-seting-bar-flex-value">
+						<el-switch
+							v-model="getThemeConfig.isUniqueOpened"
+							:disabled="getThemeConfig.layout === 'transverse'"
+							size="small"
+							@change="setLocalThemeConfig"
+						></el-switch>
+					</div>
+				</div>
+				<div class="layout-breadcrumb-seting-bar-flex mt15">
+					<div class="layout-breadcrumb-seting-bar-flex-label">{{ $t('message.layout.threeIsFixedHeader') }}</div>
+					<div class="layout-breadcrumb-seting-bar-flex-value">
+						<el-switch v-model="getThemeConfig.isFixedHeader" size="small" @change="onIsFixedHeaderChange"></el-switch>
+					</div>
+				</div>
+				<div class="layout-breadcrumb-seting-bar-flex mt15" :style="{ opacity: getThemeConfig.layout !== 'classic' ? 0.5 : 1 }">
+					<div class="layout-breadcrumb-seting-bar-flex-label">{{ $t('message.layout.threeIsClassicSplitMenu') }}</div>
+					<div class="layout-breadcrumb-seting-bar-flex-value">
+						<el-switch
+							v-model="getThemeConfig.isClassicSplitMenu"
+							:disabled="getThemeConfig.layout !== 'classic'"
+							size="small"
+							@change="onClassicSplitMenuChange"
+						>
+						</el-switch>
+					</div>
+				</div>
+				<div class="layout-breadcrumb-seting-bar-flex mt15">
+					<div class="layout-breadcrumb-seting-bar-flex-label">{{ $t('message.layout.threeIsLockScreen') }}</div>
+					<div class="layout-breadcrumb-seting-bar-flex-value">
+						<el-switch v-model="getThemeConfig.isLockScreen" size="small" @change="setLocalThemeConfig"></el-switch>
+					</div>
+				</div>
+				<div class="layout-breadcrumb-seting-bar-flex mt11">
+					<div class="layout-breadcrumb-seting-bar-flex-label">{{ $t('message.layout.threeLockScreenTime') }}</div>
+					<div class="layout-breadcrumb-seting-bar-flex-value">
+						<el-input-number
+							v-model="getThemeConfig.lockScreenTime"
+							controls-position="right"
+							:min="1"
+							:max="9999"
+							@change="setLocalThemeConfig"
+							size="default"
+							style="width: 90px"
+						>
+						</el-input-number>
+					</div>
+				</div>
+
+				<!-- 界面显示 -->
+				<el-divider content-position="left">{{ $t('message.layout.fourTitle') }}</el-divider>
+				<div class="layout-breadcrumb-seting-bar-flex mt15">
+					<div class="layout-breadcrumb-seting-bar-flex-label">{{ $t('message.layout.fourIsShowLogo') }}</div>
+					<div class="layout-breadcrumb-seting-bar-flex-value">
+						<el-switch v-model="getThemeConfig.isShowLogo" size="small" @change="onIsShowLogoChange"></el-switch>
+					</div>
+				</div>
+				<div
+					class="layout-breadcrumb-seting-bar-flex mt15"
+					:style="{ opacity: getThemeConfig.layout === 'classic' || getThemeConfig.layout === 'transverse' ? 0.5 : 1 }"
+				>
+					<div class="layout-breadcrumb-seting-bar-flex-label">{{ $t('message.layout.fourIsBreadcrumb') }}</div>
+					<div class="layout-breadcrumb-seting-bar-flex-value">
+						<el-switch
+							v-model="getThemeConfig.isBreadcrumb"
+							:disabled="getThemeConfig.layout === 'classic' || getThemeConfig.layout === 'transverse'"
+							size="small"
+							@change="onIsBreadcrumbChange"
+						></el-switch>
+					</div>
+				</div>
+				<div class="layout-breadcrumb-seting-bar-flex mt15">
+					<div class="layout-breadcrumb-seting-bar-flex-label">{{ $t('message.layout.fourIsBreadcrumbIcon') }}</div>
+					<div class="layout-breadcrumb-seting-bar-flex-value">
+						<el-switch v-model="getThemeConfig.isBreadcrumbIcon" size="small" @change="setLocalThemeConfig"></el-switch>
+					</div>
+				</div>
+				<div class="layout-breadcrumb-seting-bar-flex mt15">
+					<div class="layout-breadcrumb-seting-bar-flex-label">{{ $t('message.layout.fourIsTagsview') }}</div>
+					<div class="layout-breadcrumb-seting-bar-flex-value">
+						<el-switch v-model="getThemeConfig.isTagsview" size="small" @change="setLocalThemeConfig"></el-switch>
+					</div>
+				</div>
+				<div class="layout-breadcrumb-seting-bar-flex mt15">
+					<div class="layout-breadcrumb-seting-bar-flex-label">{{ $t('message.layout.fourIsTagsviewIcon') }}</div>
+					<div class="layout-breadcrumb-seting-bar-flex-value">
+						<el-switch v-model="getThemeConfig.isTagsviewIcon" size="small" @change="setLocalThemeConfig"></el-switch>
+					</div>
+				</div>
+				<div class="layout-breadcrumb-seting-bar-flex mt15">
+					<div class="layout-breadcrumb-seting-bar-flex-label">{{ $t('message.layout.fourIsCacheTagsView') }}</div>
+					<div class="layout-breadcrumb-seting-bar-flex-value">
+						<el-switch v-model="getThemeConfig.isCacheTagsView" size="small" @change="setLocalThemeConfig"></el-switch>
+					</div>
+				</div>
+				<div class="layout-breadcrumb-seting-bar-flex mt15" :style="{ opacity: state.isMobile ? 0.5 : 1 }">
+					<div class="layout-breadcrumb-seting-bar-flex-label">{{ $t('message.layout.fourIsSortableTagsView') }}</div>
+					<div class="layout-breadcrumb-seting-bar-flex-value">
+						<el-switch
+							v-model="getThemeConfig.isSortableTagsView"
+							:disabled="state.isMobile ? true : false"
+							size="small"
+							@change="onSortableTagsViewChange"
+						></el-switch>
+					</div>
+				</div>
+				<div class="layout-breadcrumb-seting-bar-flex mt15">
+					<div class="layout-breadcrumb-seting-bar-flex-label">{{ $t('message.layout.fourIsShareTagsView') }}</div>
+					<div class="layout-breadcrumb-seting-bar-flex-value">
+						<el-switch v-model="getThemeConfig.isShareTagsView" size="small" @change="onShareTagsViewChange"></el-switch>
+					</div>
+				</div>
+				<div class="layout-breadcrumb-seting-bar-flex mt15">
+					<div class="layout-breadcrumb-seting-bar-flex-label">{{ $t('message.layout.fourIsFooter') }}</div>
+					<div class="layout-breadcrumb-seting-bar-flex-value">
+						<el-switch v-model="getThemeConfig.isFooter" size="small" @change="setLocalThemeConfig"></el-switch>
+					</div>
+				</div>
+				<div class="layout-breadcrumb-seting-bar-flex mt15">
+					<div class="layout-breadcrumb-seting-bar-flex-label">{{ $t('message.layout.fourIsGrayscale') }}</div>
+					<div class="layout-breadcrumb-seting-bar-flex-value">
+						<el-switch v-model="getThemeConfig.isGrayscale" size="small" @change="onAddFilterChange('grayscale')"></el-switch>
+					</div>
+				</div>
+				<div class="layout-breadcrumb-seting-bar-flex mt15">
+					<div class="layout-breadcrumb-seting-bar-flex-label">{{ $t('message.layout.fourIsInvert') }}</div>
+					<div class="layout-breadcrumb-seting-bar-flex-value">
+						<el-switch v-model="getThemeConfig.isInvert" size="small" @change="onAddFilterChange('invert')"></el-switch>
+					</div>
+				</div>
+				<div class="layout-breadcrumb-seting-bar-flex mt15">
+					<div class="layout-breadcrumb-seting-bar-flex-label">{{ $t('message.layout.fourIsWartermark') }}</div>
+					<div class="layout-breadcrumb-seting-bar-flex-value">
+						<el-switch v-model="getThemeConfig.isWartermark" size="small" @change="onWartermarkChange"></el-switch>
+					</div>
+				</div>
+				<div class="layout-breadcrumb-seting-bar-flex mt14">
+					<div class="layout-breadcrumb-seting-bar-flex-label">{{ $t('message.layout.fourWartermarkText') }}</div>
+					<div class="layout-breadcrumb-seting-bar-flex-value">
+						<el-input v-model="getThemeConfig.wartermarkText" size="default" style="width: 90px" @input="onWartermarkTextInput"></el-input>
+					</div>
+				</div>
+
+				<!-- 其它设置 -->
+				<el-divider content-position="left">{{ $t('message.layout.fiveTitle') }}</el-divider>
+				<div class="layout-breadcrumb-seting-bar-flex mt15">
+					<div class="layout-breadcrumb-seting-bar-flex-label">{{ $t('message.layout.fiveTagsStyle') }}</div>
+					<div class="layout-breadcrumb-seting-bar-flex-value">
+						<el-select v-model="getThemeConfig.tagsStyle" placeholder="请选择" size="default" style="width: 90px" @change="setLocalThemeConfig">
+							<el-option label="风格1" value="tags-style-one"></el-option>
+							<el-option label="风格4" value="tags-style-four"></el-option>
+							<el-option label="风格5" value="tags-style-five"></el-option>
+						</el-select>
+					</div>
+				</div>
+				<div class="layout-breadcrumb-seting-bar-flex mt15">
+					<div class="layout-breadcrumb-seting-bar-flex-label">{{ $t('message.layout.fiveAnimation') }}</div>
+					<div class="layout-breadcrumb-seting-bar-flex-value">
+						<el-select v-model="getThemeConfig.animation" placeholder="请选择" size="default" style="width: 90px" @change="setLocalThemeConfig">
+							<el-option label="slide-right" value="slide-right"></el-option>
+							<el-option label="slide-left" value="slide-left"></el-option>
+							<el-option label="opacitys" value="opacitys"></el-option>
+						</el-select>
+					</div>
+				</div>
+				<div class="layout-breadcrumb-seting-bar-flex mt15" :style="{ opacity: getThemeConfig.layout !== 'columns' ? 0.5 : 1 }">
+					<div class="layout-breadcrumb-seting-bar-flex-label">{{ $t('message.layout.fiveColumnsAsideStyle') }}</div>
+					<div class="layout-breadcrumb-seting-bar-flex-value">
+						<el-select
+							v-model="getThemeConfig.columnsAsideStyle"
+							placeholder="请选择"
+							size="default"
+							style="width: 90px"
+							:disabled="getThemeConfig.layout !== 'columns' ? true : false"
+							@change="setLocalThemeConfig"
+						>
+							<el-option label="圆角" value="columns-round"></el-option>
+							<el-option label="卡片" value="columns-card"></el-option>
+						</el-select>
+					</div>
+				</div>
+				<div class="layout-breadcrumb-seting-bar-flex mt15 mb27" :style="{ opacity: getThemeConfig.layout !== 'columns' ? 0.5 : 1 }">
+					<div class="layout-breadcrumb-seting-bar-flex-label">{{ $t('message.layout.fiveColumnsAsideLayout') }}</div>
+					<div class="layout-breadcrumb-seting-bar-flex-value">
+						<el-select
+							v-model="getThemeConfig.columnsAsideLayout"
+							placeholder="请选择"
+							size="default"
+							style="width: 90px"
+							:disabled="getThemeConfig.layout !== 'columns' ? true : false"
+							@change="setLocalThemeConfig"
+						>
+							<el-option label="水平" value="columns-horizontal"></el-option>
+							<el-option label="垂直" value="columns-vertical"></el-option>
+						</el-select>
+					</div>
+				</div>
+
+				<!-- 布局切换 -->
+				<el-divider content-position="left">{{ $t('message.layout.sixTitle') }}</el-divider>
+				<div class="layout-drawer-content-flex">
+					<!-- defaults 布局 -->
+					<div class="layout-drawer-content-item" @click="onSetLayout('defaults')">
+						<section class="el-container el-circular" :class="{ 'drawer-layout-active': getThemeConfig.layout === 'defaults' }">
+							<aside class="el-aside" style="width: 20px"></aside>
+							<section class="el-container is-vertical">
+								<header class="el-header" style="height: 10px"></header>
+								<main class="el-main"></main>
+							</section>
+						</section>
+						<div class="layout-tips-warp" :class="{ 'layout-tips-warp-active': getThemeConfig.layout === 'defaults' }">
+							<div class="layout-tips-box">
+								<p class="layout-tips-txt">{{ $t('message.layout.sixDefaults') }}</p>
+							</div>
+						</div>
+					</div>
+					<!-- classic 布局 -->
+					<div class="layout-drawer-content-item" @click="onSetLayout('classic')">
+						<section class="el-container is-vertical el-circular" :class="{ 'drawer-layout-active': getThemeConfig.layout === 'classic' }">
+							<header class="el-header" style="height: 10px"></header>
+							<section class="el-container">
+								<aside class="el-aside" style="width: 20px"></aside>
+								<section class="el-container is-vertical">
+									<main class="el-main"></main>
+								</section>
+							</section>
+						</section>
+						<div class="layout-tips-warp" :class="{ 'layout-tips-warp-active': getThemeConfig.layout === 'classic' }">
+							<div class="layout-tips-box">
+								<p class="layout-tips-txt">{{ $t('message.layout.sixClassic') }}</p>
+							</div>
+						</div>
+					</div>
+					<!-- transverse 布局 -->
+					<div class="layout-drawer-content-item" @click="onSetLayout('transverse')">
+						<section class="el-container is-vertical el-circular" :class="{ 'drawer-layout-active': getThemeConfig.layout === 'transverse' }">
+							<header class="el-header" style="height: 10px"></header>
+							<section class="el-container">
+								<section class="el-container is-vertical">
+									<main class="el-main"></main>
+								</section>
+							</section>
+						</section>
+						<div class="layout-tips-warp" :class="{ 'layout-tips-warp-active': getThemeConfig.layout === 'transverse' }">
+							<div class="layout-tips-box">
+								<p class="layout-tips-txt">{{ $t('message.layout.sixTransverse') }}</p>
+							</div>
+						</div>
+					</div>
+					<!-- columns 布局 -->
+					<div class="layout-drawer-content-item" @click="onSetLayout('columns')">
+						<section class="el-container el-circular" :class="{ 'drawer-layout-active': getThemeConfig.layout === 'columns' }">
+							<aside class="el-aside-dark" style="width: 10px"></aside>
+							<aside class="el-aside" style="width: 20px"></aside>
+							<section class="el-container is-vertical">
+								<header class="el-header" style="height: 10px"></header>
+								<main class="el-main"></main>
+							</section>
+						</section>
+						<div class="layout-tips-warp" :class="{ 'layout-tips-warp-active': getThemeConfig.layout === 'columns' }">
+							<div class="layout-tips-box">
+								<p class="layout-tips-txt">{{ $t('message.layout.sixColumns') }}</p>
+							</div>
+						</div>
+					</div>
+				</div>
+				<div class="copy-config">
+					<el-alert :title="$t('message.layout.tipText')" type="warning" :closable="false"> </el-alert>
+					<el-button size="default" class="copy-config-btn" type="primary" ref="copyConfigBtnRef" @click="onCopyConfigClick">
+						<el-icon class="mr5">
+							<ele-CopyDocument />
+						</el-icon>
+						{{ $t('message.layout.copyText') }}
+					</el-button>
+					<el-button size="default" class="copy-config-btn-reset" type="info" @click="onResetConfigClick">
+						<el-icon class="mr5">
+							<ele-RefreshRight />
+						</el-icon>
+						{{ $t('message.layout.resetText') }}
+					</el-button>
+				</div>
+			</el-scrollbar>
+		</el-drawer>
+	</div>
+</template>
+
+<script setup lang="ts" name="layoutBreadcrumbSeting">
+import { nextTick, onUnmounted, onMounted, computed, reactive } from 'vue';
+import { ElMessage } from 'element-plus';
+import { useI18n } from 'vue-i18n';
+import { storeToRefs } from 'pinia';
+import { useThemeConfig } from '/@/stores/themeConfig';
+import { useChangeColor } from '/@/utils/theme';
+import { verifyAndSpace } from '/@/utils/toolsValidate';
+import { Local } from '/@/utils/storage';
+import Watermark from '/@/utils/watermark';
+import commonFunction from '/@/utils/commonFunction';
+import other from '/@/utils/other';
+import mittBus from '/@/utils/mitt';
+
+// 定义变量内容
+const { locale } = useI18n();
+const storesThemeConfig = useThemeConfig();
+const { themeConfig } = storeToRefs(storesThemeConfig);
+const { copyText } = commonFunction();
+const { getLightColor, getDarkColor } = useChangeColor();
+const state = reactive({
+	isMobile: false,
+});
+
+// 获取布局配置信息
+const getThemeConfig = computed(() => {
+	return themeConfig.value;
+});
+// 1、全局主题
+const onColorPickerChange = () => {
+	if (!getThemeConfig.value.primary) return ElMessage.warning('全局主题 primary 颜色值不能为空');
+	// 颜色加深
+	document.documentElement.style.setProperty('--el-color-primary-dark-2', `${getDarkColor(getThemeConfig.value.primary, 0.1)}`);
+	document.documentElement.style.setProperty('--el-color-primary', getThemeConfig.value.primary);
+	// 颜色变浅
+	for (let i = 1; i <= 9; i++) {
+		document.documentElement.style.setProperty(`--el-color-primary-light-${i}`, `${getLightColor(getThemeConfig.value.primary, i / 10)}`);
+	}
+	setDispatchThemeConfig();
+};
+// 2、菜单 / 顶栏
+const onBgColorPickerChange = (bg: string) => {
+	document.documentElement.style.setProperty(`--next-bg-${bg}`, themeConfig.value[bg]);
+	if (bg === 'menuBar') {
+		document.documentElement.style.setProperty(`--next-bg-menuBar-light-1`, getLightColor(getThemeConfig.value.menuBar, 0.05));
+	}
+	onTopBarGradualChange();
+	onMenuBarGradualChange();
+	onColumnsMenuBarGradualChange();
+	setDispatchThemeConfig();
+};
+// 2、菜单 / 顶栏 --> 顶栏背景渐变
+const onTopBarGradualChange = () => {
+	setGraduaFun('.layout-navbars-breadcrumb-index', getThemeConfig.value.isTopBarColorGradual, getThemeConfig.value.topBar);
+};
+// 2、菜单 / 顶栏 --> 菜单背景渐变
+const onMenuBarGradualChange = () => {
+	setGraduaFun('.layout-container .el-aside', getThemeConfig.value.isMenuBarColorGradual, getThemeConfig.value.menuBar);
+};
+// 2、菜单 / 顶栏 --> 分栏菜单背景渐变
+const onColumnsMenuBarGradualChange = () => {
+	setGraduaFun('.layout-container .layout-columns-aside', getThemeConfig.value.isColumnsMenuBarColorGradual, getThemeConfig.value.columnsMenuBar);
+};
+// 2、菜单 / 顶栏 --> 背景渐变函数
+const setGraduaFun = (el: string, bool: boolean, color: string) => {
+	setTimeout(() => {
+		let els = document.querySelector(el);
+		if (!els) return false;
+		document.documentElement.style.setProperty('--el-menu-bg-color', document.documentElement.style.getPropertyValue('--next-bg-menuBar'));
+		if (bool) els.setAttribute('style', `background:linear-gradient(to bottom left , ${color}, ${getLightColor(color, 0.6)}) !important;`);
+		else els.setAttribute('style', ``);
+		setLocalThemeConfig();
+	}, 200);
+};
+// 2、分栏设置 ->
+const onColumnsMenuHoverPreloadChange = () => {
+	setLocalThemeConfig();
+};
+// 3、界面设置 --> 菜单水平折叠
+const onThemeConfigChange = () => {
+	setDispatchThemeConfig();
+};
+// 3、界面设置 --> 固定 Header
+const onIsFixedHeaderChange = () => {
+	getThemeConfig.value.isFixedHeaderChange = getThemeConfig.value.isFixedHeader ? false : true;
+	setLocalThemeConfig();
+};
+// 3、界面设置 --> 经典布局分割菜单
+const onClassicSplitMenuChange = () => {
+	getThemeConfig.value.isBreadcrumb = false;
+	setLocalThemeConfig();
+	mittBus.emit('getBreadcrumbIndexSetFilterRoutes');
+};
+// 4、界面显示 --> 侧边栏 Logo
+const onIsShowLogoChange = () => {
+	getThemeConfig.value.isShowLogoChange = getThemeConfig.value.isShowLogo ? false : true;
+	setLocalThemeConfig();
+};
+// 4、界面显示 --> 面包屑 Breadcrumb
+const onIsBreadcrumbChange = () => {
+	if (getThemeConfig.value.layout === 'classic') {
+		getThemeConfig.value.isClassicSplitMenu = false;
+	}
+	setLocalThemeConfig();
+};
+// 4、界面显示 --> 开启 TagsView 拖拽
+const onSortableTagsViewChange = () => {
+	mittBus.emit('openOrCloseSortable');
+	setLocalThemeConfig();
+};
+// 4、界面显示 --> 开启 TagsView 共用
+const onShareTagsViewChange = () => {
+	mittBus.emit('openShareTagsView');
+	setLocalThemeConfig();
+};
+// 4、界面显示 --> 灰色模式/色弱模式
+const onAddFilterChange = (attr: string) => {
+	if (attr === 'grayscale') {
+		if (getThemeConfig.value.isGrayscale) getThemeConfig.value.isInvert = false;
+	} else {
+		if (getThemeConfig.value.isInvert) getThemeConfig.value.isGrayscale = false;
+	}
+	const cssAttr =
+		attr === 'grayscale' ? `grayscale(${getThemeConfig.value.isGrayscale ? 1 : 0})` : `invert(${getThemeConfig.value.isInvert ? '80%' : '0%'})`;
+	const appEle = document.body;
+	appEle.setAttribute('style', `filter: ${cssAttr}`);
+	setLocalThemeConfig();
+};
+// 4、界面显示 --> 深色模式
+const onAddDarkChange = () => {
+	const body = document.documentElement as HTMLElement;
+	if (getThemeConfig.value.isIsDark) body.setAttribute('data-theme', 'dark');
+	else body.setAttribute('data-theme', '');
+};
+// 4、界面显示 --> 开启水印
+const onWartermarkChange = () => {
+	getThemeConfig.value.isWartermark ? Watermark.set(getThemeConfig.value.wartermarkText) : Watermark.del();
+	setLocalThemeConfig();
+};
+// 4、界面显示 --> 水印文案
+const onWartermarkTextInput = (val: string) => {
+	getThemeConfig.value.wartermarkText = verifyAndSpace(val);
+	if (getThemeConfig.value.wartermarkText === '') return false;
+	if (getThemeConfig.value.isWartermark) Watermark.set(getThemeConfig.value.wartermarkText);
+	setLocalThemeConfig();
+};
+// 5、布局切换
+const onSetLayout = (layout: string) => {
+	Local.set('oldLayout', layout);
+	if (getThemeConfig.value.layout === layout) return false;
+	if (layout === 'transverse') getThemeConfig.value.isCollapse = false;
+	getThemeConfig.value.layout = layout;
+	getThemeConfig.value.isDrawer = false;
+	initLayoutChangeFun();
+};
+// 设置布局切换函数
+const initLayoutChangeFun = () => {
+	onBgColorPickerChange('menuBar');
+	onBgColorPickerChange('menuBarColor');
+	onBgColorPickerChange('menuBarActiveColor');
+	onBgColorPickerChange('topBar');
+	onBgColorPickerChange('topBarColor');
+	onBgColorPickerChange('columnsMenuBar');
+	onBgColorPickerChange('columnsMenuBarColor');
+};
+// 关闭弹窗时,初始化变量。变量用于处理 layoutScrollbarRef.value.update() 更新滚动条高度
+const onDrawerClose = () => {
+	getThemeConfig.value.isFixedHeaderChange = false;
+	getThemeConfig.value.isShowLogoChange = false;
+	getThemeConfig.value.isDrawer = false;
+	setLocalThemeConfig();
+};
+// 布局配置弹窗打开
+const openDrawer = () => {
+	getThemeConfig.value.isDrawer = true;
+};
+// 触发 store 布局配置更新
+const setDispatchThemeConfig = () => {
+	setLocalThemeConfig();
+	setLocalThemeConfigStyle();
+};
+// 存储布局配置
+const setLocalThemeConfig = () => {
+	Local.remove('themeConfig');
+	Local.set('themeConfig', getThemeConfig.value);
+};
+// 存储布局配置全局主题样式(html根标签)
+const setLocalThemeConfigStyle = () => {
+	Local.set('themeConfigStyle', document.documentElement.style.cssText);
+};
+// 一键复制配置
+const onCopyConfigClick = () => {
+	let copyThemeConfig = Local.get('themeConfig');
+	copyThemeConfig.isDrawer = false;
+	copyText(JSON.stringify(copyThemeConfig)).then(() => {
+		getThemeConfig.value.isDrawer = false;
+	});
+};
+// 一键恢复默认
+const onResetConfigClick = () => {
+	Local.clear();
+	window.location.reload();
+	// @ts-ignore
+	Local.set('version', __NEXT_VERSION__);
+};
+// 初始化菜单样式等
+const initSetStyle = () => {
+	// 2、菜单 / 顶栏 --> 顶栏背景渐变
+	onTopBarGradualChange();
+	// 2、菜单 / 顶栏 --> 菜单背景渐变
+	onMenuBarGradualChange();
+	// 2、菜单 / 顶栏 --> 分栏菜单背景渐变
+	onColumnsMenuBarGradualChange();
+};
+onMounted(() => {
+	nextTick(() => {
+		// 判断当前布局是否不相同,不相同则初始化当前布局的样式,防止监听窗口大小改变时,布局配置logo、菜单背景等部分布局失效问题
+		if (!Local.get('frequency')) initLayoutChangeFun();
+		Local.set('frequency', 1);
+		// 监听窗口大小改变,非默认布局,设置成默认布局(适配移动端)
+		mittBus.on('layoutMobileResize', (res: LayoutMobileResize) => {
+			getThemeConfig.value.layout = res.layout;
+			getThemeConfig.value.isDrawer = false;
+			initLayoutChangeFun();
+			state.isMobile = other.isMobile();
+		});
+		setTimeout(() => {
+			// 默认样式
+			onColorPickerChange();
+			// 灰色模式
+			if (getThemeConfig.value.isGrayscale) onAddFilterChange('grayscale');
+			// 色弱模式
+			if (getThemeConfig.value.isInvert) onAddFilterChange('invert');
+			// 深色模式
+			if (getThemeConfig.value.isIsDark) onAddDarkChange();
+			// 开启水印
+			onWartermarkChange();
+			// 语言国际化
+			if (Local.get('themeConfig')) locale.value = Local.get('themeConfig').globalI18n;
+			// 初始化菜单样式等
+			initSetStyle();
+		}, 100);
+	});
+});
+onUnmounted(() => {
+	mittBus.off('layoutMobileResize', () => {});
+});
+
+// 暴露变量
+defineExpose({
+	openDrawer,
+});
+</script>
+
+<style scoped lang="scss">
+.layout-breadcrumb-seting-bar {
+	height: calc(100vh - 50px);
+	padding: 0 15px;
+	:deep(.el-scrollbar__view) {
+		overflow-x: hidden !important;
+	}
+	.layout-breadcrumb-seting-bar-flex {
+		display: flex;
+		align-items: center;
+		margin-bottom: 5px;
+		&-label {
+			flex: 1;
+			color: var(--el-text-color-primary);
+		}
+	}
+	.layout-drawer-content-flex {
+		overflow: hidden;
+		display: flex;
+		flex-wrap: wrap;
+		align-content: flex-start;
+		margin: 0 -5px;
+		.layout-drawer-content-item {
+			width: 50%;
+			height: 70px;
+			cursor: pointer;
+			border: 1px solid transparent;
+			position: relative;
+			padding: 5px;
+			.el-container {
+				height: 100%;
+				.el-aside-dark {
+					background-color: var(--next-color-seting-header);
+				}
+				.el-aside {
+					background-color: var(--next-color-seting-aside);
+				}
+				.el-header {
+					background-color: var(--next-color-seting-header);
+				}
+				.el-main {
+					background-color: var(--next-color-seting-main);
+				}
+			}
+			.el-circular {
+				border-radius: 2px;
+				overflow: hidden;
+				border: 1px solid transparent;
+				transition: all 0.3s ease-in-out;
+			}
+			.drawer-layout-active {
+				border: 1px solid;
+				border-color: var(--el-color-primary);
+			}
+			.layout-tips-warp,
+			.layout-tips-warp-active {
+				transition: all 0.3s ease-in-out;
+				position: absolute;
+				left: 50%;
+				top: 50%;
+				transform: translate(-50%, -50%);
+				border: 1px solid;
+				border-color: var(--el-color-primary-light-5);
+				border-radius: 100%;
+				padding: 4px;
+				.layout-tips-box {
+					transition: inherit;
+					width: 30px;
+					height: 30px;
+					z-index: 9;
+					border: 1px solid;
+					border-color: var(--el-color-primary-light-5);
+					border-radius: 100%;
+					.layout-tips-txt {
+						transition: inherit;
+						position: relative;
+						top: 5px;
+						font-size: 12px;
+						line-height: 1;
+						letter-spacing: 2px;
+						white-space: nowrap;
+						color: var(--el-color-primary-light-5);
+						text-align: center;
+						transform: rotate(30deg);
+						left: -1px;
+						background-color: var(--next-color-seting-main);
+						width: 32px;
+						height: 17px;
+						line-height: 17px;
+					}
+				}
+			}
+			.layout-tips-warp-active {
+				border: 1px solid;
+				border-color: var(--el-color-primary);
+				.layout-tips-box {
+					border: 1px solid;
+					border-color: var(--el-color-primary);
+					.layout-tips-txt {
+						color: var(--el-color-primary) !important;
+						background-color: var(--next-color-seting-main) !important;
+					}
+				}
+			}
+			&:hover {
+				.el-circular {
+					transition: all 0.3s ease-in-out;
+					border: 1px solid;
+					border-color: var(--el-color-primary);
+				}
+				.layout-tips-warp {
+					transition: all 0.3s ease-in-out;
+					border-color: var(--el-color-primary);
+					.layout-tips-box {
+						transition: inherit;
+						border-color: var(--el-color-primary);
+						.layout-tips-txt {
+							transition: inherit;
+							color: var(--el-color-primary) !important;
+							background-color: var(--next-color-seting-main) !important;
+						}
+					}
+				}
+			}
+		}
+	}
+	.copy-config {
+		margin: 10px 0;
+		.copy-config-btn {
+			width: 100%;
+			margin-top: 15px;
+		}
+		.copy-config-btn-reset {
+			width: 100%;
+			margin: 10px 0 0;
+		}
+	}
+}
+</style>
Added +258 -0
diff --git a/src/layout/navBars/breadcrumb/user.vue b/src/layout/navBars/breadcrumb/user.vue
new file mode 100644
index 0000000..9337211
--- /dev/null
+++ b/src/layout/navBars/breadcrumb/user.vue
@@ -0,0 +1,258 @@
+<template>
+	<div class="layout-navbars-breadcrumb-user pr15" :style="{ flex: layoutUserFlexNum }">
+		<el-dropdown :show-timeout="70" :hide-timeout="50" trigger="click" @command="onComponentSizeChange">
+			<div class="layout-navbars-breadcrumb-user-icon">
+				<i class="iconfont icon-ziti" :title="$t('message.user.title0')"></i>
+			</div>
+			<template #dropdown>
+				<el-dropdown-menu>
+					<el-dropdown-item command="large" :disabled="state.disabledSize === 'large'">{{ $t('message.user.dropdownLarge') }}</el-dropdown-item>
+					<el-dropdown-item command="default" :disabled="state.disabledSize === 'default'">{{ $t('message.user.dropdownDefault') }}</el-dropdown-item>
+					<el-dropdown-item command="small" :disabled="state.disabledSize === 'small'">{{ $t('message.user.dropdownSmall') }}</el-dropdown-item>
+				</el-dropdown-menu>
+			</template>
+		</el-dropdown>
+		<el-dropdown :show-timeout="70" :hide-timeout="50" trigger="click" @command="onLanguageChange">
+			<div class="layout-navbars-breadcrumb-user-icon">
+				<i
+					class="iconfont"
+					:class="state.disabledI18n === 'en' ? 'icon-fuhao-yingwen' : 'icon-fuhao-zhongwen'"
+					:title="$t('message.user.title1')"
+				></i>
+			</div>
+			<template #dropdown>
+				<el-dropdown-menu>
+					<el-dropdown-item command="zh-cn" :disabled="state.disabledI18n === 'zh-cn'">简体中文</el-dropdown-item>
+					<el-dropdown-item command="en" :disabled="state.disabledI18n === 'en'">English</el-dropdown-item>
+					<el-dropdown-item command="zh-tw" :disabled="state.disabledI18n === 'zh-tw'">繁體中文</el-dropdown-item>
+				</el-dropdown-menu>
+			</template>
+		</el-dropdown>
+		<div class="layout-navbars-breadcrumb-user-icon" @click="onSearchClick">
+			<el-icon :title="$t('message.user.title2')">
+				<ele-Search />
+			</el-icon>
+		</div>
+		<div class="layout-navbars-breadcrumb-user-icon" @click="onLayoutSetingClick">
+			<i class="icon-skin iconfont" :title="$t('message.user.title3')"></i>
+		</div>
+		<div class="layout-navbars-breadcrumb-user-icon">
+			<el-popover placement="bottom" trigger="click" transition="el-zoom-in-top" :width="300" :persistent="false">
+				<template #reference>
+					<el-badge :is-dot="true">
+						<el-icon :title="$t('message.user.title4')">
+							<ele-Bell />
+						</el-icon>
+					</el-badge>
+				</template>
+				<template #default>
+					<UserNews />
+				</template>
+			</el-popover>
+		</div>
+		<div class="layout-navbars-breadcrumb-user-icon mr10" @click="onScreenfullClick">
+			<i
+				class="iconfont"
+				:title="state.isScreenfull ? $t('message.user.title6') : $t('message.user.title5')"
+				:class="!state.isScreenfull ? 'icon-fullscreen' : 'icon-tuichuquanping'"
+			></i>
+		</div>
+		<el-dropdown :show-timeout="70" :hide-timeout="50" @command="onHandleCommandClick">
+			<span class="layout-navbars-breadcrumb-user-link">
+				<img :src="userInfos.photo" class="layout-navbars-breadcrumb-user-link-photo mr5" />
+				{{ userInfos.userName === '' ? 'common' : userInfos.userName }}
+				<el-icon class="el-icon--right">
+					<ele-ArrowDown />
+				</el-icon>
+			</span>
+			<template #dropdown>
+				<el-dropdown-menu>
+					<el-dropdown-item command="/home">{{ $t('message.user.dropdown1') }}</el-dropdown-item>
+					<el-dropdown-item command="wareHouse">{{ $t('message.user.dropdown6') }}</el-dropdown-item>
+					<el-dropdown-item command="/personal">{{ $t('message.user.dropdown2') }}</el-dropdown-item>
+					<el-dropdown-item command="/404">{{ $t('message.user.dropdown3') }}</el-dropdown-item>
+					<el-dropdown-item command="/401">{{ $t('message.user.dropdown4') }}</el-dropdown-item>
+					<el-dropdown-item divided command="logOut">{{ $t('message.user.dropdown5') }}</el-dropdown-item>
+				</el-dropdown-menu>
+			</template>
+		</el-dropdown>
+		<Search ref="searchRef" />
+	</div>
+</template>
+
+<script setup lang="ts" name="layoutBreadcrumbUser">
+import { defineAsyncComponent, ref, computed, reactive, onMounted } from 'vue';
+import { useRouter } from 'vue-router';
+import { ElMessageBox, ElMessage } from 'element-plus';
+import screenfull from 'screenfull';
+import { useI18n } from 'vue-i18n';
+import { storeToRefs } from 'pinia';
+import { useUserInfo } from '/@/stores/userInfo';
+import { useThemeConfig } from '/@/stores/themeConfig';
+import other from '/@/utils/other';
+import mittBus from '/@/utils/mitt';
+import { Session, Local } from '/@/utils/storage';
+
+// 引入组件
+const UserNews = defineAsyncComponent(() => import('/@/layout/navBars/breadcrumb/userNews.vue'));
+const Search = defineAsyncComponent(() => import('/@/layout/navBars/breadcrumb/search.vue'));
+
+// 定义变量内容
+const { locale, t } = useI18n();
+const router = useRouter();
+const stores = useUserInfo();
+const storesThemeConfig = useThemeConfig();
+const { userInfos } = storeToRefs(stores);
+const { themeConfig } = storeToRefs(storesThemeConfig);
+const searchRef = ref();
+const state = reactive({
+	isScreenfull: false,
+	disabledI18n: 'zh-cn',
+	disabledSize: 'large',
+});
+
+// 设置分割样式
+const layoutUserFlexNum = computed(() => {
+	let num: string | number = '';
+	const { layout, isClassicSplitMenu } = themeConfig.value;
+	const layoutArr: string[] = ['defaults', 'columns'];
+	if (layoutArr.includes(layout) || (layout === 'classic' && !isClassicSplitMenu)) num = '1';
+	else num = '';
+	return num;
+});
+// 全屏点击时
+const onScreenfullClick = () => {
+	if (!screenfull.isEnabled) {
+		ElMessage.warning('暂不不支持全屏');
+		return false;
+	}
+	screenfull.toggle();
+	screenfull.on('change', () => {
+		if (screenfull.isFullscreen) state.isScreenfull = true;
+		else state.isScreenfull = false;
+	});
+};
+// 布局配置 icon 点击时
+const onLayoutSetingClick = () => {
+	mittBus.emit('openSetingsDrawer');
+};
+// 下拉菜单点击时
+const onHandleCommandClick = (path: string) => {
+	if (path === 'logOut') {
+		ElMessageBox({
+			closeOnClickModal: false,
+			closeOnPressEscape: false,
+			title: t('message.user.logOutTitle'),
+			message: t('message.user.logOutMessage'),
+			showCancelButton: true,
+			confirmButtonText: t('message.user.logOutConfirm'),
+			cancelButtonText: t('message.user.logOutCancel'),
+			buttonSize: 'default',
+			beforeClose: (action, instance, done) => {
+				if (action === 'confirm') {
+					instance.confirmButtonLoading = true;
+					instance.confirmButtonText = t('message.user.logOutExit');
+					setTimeout(() => {
+						done();
+						setTimeout(() => {
+							instance.confirmButtonLoading = false;
+						}, 300);
+					}, 700);
+				} else {
+					done();
+				}
+			},
+		})
+			.then(async () => {
+				// 清除缓存/token等
+				Session.clear();
+				// 使用 reload 时,不需要调用 resetRoute() 重置路由
+				window.location.reload();
+			})
+			.catch(() => {});
+	} else if (path === 'wareHouse') {
+		window.open('https://gitee.com/lyt-top/vue-next-admin');
+	} else {
+		router.push(path);
+	}
+};
+// 菜单搜索点击
+const onSearchClick = () => {
+	searchRef.value.openSearch();
+};
+// 组件大小改变
+const onComponentSizeChange = (size: string) => {
+	Local.remove('themeConfig');
+	themeConfig.value.globalComponentSize = size;
+	Local.set('themeConfig', themeConfig.value);
+	initI18nOrSize('globalComponentSize', 'disabledSize');
+	window.location.reload();
+};
+// 语言切换
+const onLanguageChange = (lang: string) => {
+	Local.remove('themeConfig');
+	themeConfig.value.globalI18n = lang;
+	Local.set('themeConfig', themeConfig.value);
+	locale.value = lang;
+	other.useTitle();
+	initI18nOrSize('globalI18n', 'disabledI18n');
+};
+// 初始化组件大小/i18n
+const initI18nOrSize = (value: string, attr: string) => {
+	state[attr] = Local.get('themeConfig')[value];
+};
+// 页面加载时
+onMounted(() => {
+	if (Local.get('themeConfig')) {
+		initI18nOrSize('globalComponentSize', 'disabledSize');
+		initI18nOrSize('globalI18n', 'disabledI18n');
+	}
+});
+</script>
+
+<style scoped lang="scss">
+.layout-navbars-breadcrumb-user {
+	display: flex;
+	align-items: center;
+	justify-content: flex-end;
+	&-link {
+		height: 100%;
+		display: flex;
+		align-items: center;
+		white-space: nowrap;
+		&-photo {
+			width: 25px;
+			height: 25px;
+			border-radius: 100%;
+		}
+	}
+	&-icon {
+		padding: 0 10px;
+		cursor: pointer;
+		color: var(--next-bg-topBarColor);
+		height: 50px;
+		line-height: 50px;
+		display: flex;
+		align-items: center;
+		&:hover {
+			background: var(--next-color-user-hover);
+			i {
+				display: inline-block;
+				animation: logoAnimation 0.3s ease-in-out;
+			}
+		}
+	}
+	:deep(.el-dropdown) {
+		color: var(--next-bg-topBarColor);
+	}
+	:deep(.el-badge) {
+		height: 40px;
+		line-height: 40px;
+		display: flex;
+		align-items: center;
+	}
+	:deep(.el-badge__content.is-fixed) {
+		top: 12px;
+	}
+}
+</style>
Added +107 -0
diff --git a/src/layout/navBars/breadcrumb/userNews.vue b/src/layout/navBars/breadcrumb/userNews.vue
new file mode 100644
index 0000000..340d880
--- /dev/null
+++ b/src/layout/navBars/breadcrumb/userNews.vue
@@ -0,0 +1,107 @@
+<template>
+	<div class="layout-navbars-breadcrumb-user-news">
+		<div class="head-box">
+			<div class="head-box-title">{{ $t('message.user.newTitle') }}</div>
+			<div class="head-box-btn" v-if="state.newsList.length > 0" @click="onAllReadClick">{{ $t('message.user.newBtn') }}</div>
+		</div>
+		<div class="content-box">
+			<template v-if="state.newsList.length > 0">
+				<div class="content-box-item" v-for="(v, k) in state.newsList" :key="k">
+					<div>{{ v.label }}</div>
+					<div class="content-box-msg">
+						{{ v.value }}
+					</div>
+					<div class="content-box-time">{{ v.time }}</div>
+				</div>
+			</template>
+			<el-empty :description="$t('message.user.newDesc')" v-else></el-empty>
+		</div>
+		<div class="foot-box" @click="onGoToGiteeClick" v-if="state.newsList.length > 0">{{ $t('message.user.newGo') }}</div>
+	</div>
+</template>
+
+<script setup lang="ts" name="layoutBreadcrumbUserNews">
+import { reactive } from 'vue';
+
+// 定义变量内容
+const state = reactive({
+	newsList: [
+		{
+			label: '关于版本发布的通知',
+			value: 'vue-next-admin,基于 vue3 + CompositionAPI + typescript + vite + element plus,正式发布时间:2021年02月28日!',
+			time: '2020-12-08',
+		},
+		{
+			label: '关于学习交流的通知',
+			value: 'QQ群号码 665452019,欢迎小伙伴入群学习交流探讨!',
+			time: '2020-12-08',
+		},
+	],
+});
+
+// 全部已读点击
+const onAllReadClick = () => {
+	state.newsList = [];
+};
+// 前往通知中心点击
+const onGoToGiteeClick = () => {
+	window.open('https://gitee.com/lyt-top/vue-next-admin');
+};
+</script>
+
+<style scoped lang="scss">
+.layout-navbars-breadcrumb-user-news {
+	.head-box {
+		display: flex;
+		border-bottom: 1px solid var(--el-border-color-lighter);
+		box-sizing: border-box;
+		color: var(--el-text-color-primary);
+		justify-content: space-between;
+		height: 35px;
+		align-items: center;
+		.head-box-btn {
+			color: var(--el-color-primary);
+			font-size: 13px;
+			cursor: pointer;
+			opacity: 0.8;
+			&:hover {
+				opacity: 1;
+			}
+		}
+	}
+	.content-box {
+		font-size: 13px;
+		.content-box-item {
+			padding-top: 12px;
+			&:last-of-type {
+				padding-bottom: 12px;
+			}
+			.content-box-msg {
+				color: var(--el-text-color-secondary);
+				margin-top: 5px;
+				margin-bottom: 5px;
+			}
+			.content-box-time {
+				color: var(--el-text-color-secondary);
+			}
+		}
+	}
+	.foot-box {
+		height: 35px;
+		color: var(--el-color-primary);
+		font-size: 13px;
+		cursor: pointer;
+		opacity: 0.8;
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		border-top: 1px solid var(--el-border-color-lighter);
+		&:hover {
+			opacity: 1;
+		}
+	}
+	:deep(.el-empty__description p) {
+		font-size: 13px;
+	}
+}
+</style>
Added +35 -0
diff --git a/src/layout/navBars/index.vue b/src/layout/navBars/index.vue
new file mode 100644
index 0000000..aa8edb9
--- /dev/null
+++ b/src/layout/navBars/index.vue
@@ -0,0 +1,35 @@
+<template>
+	<div class="layout-navbars-container">
+		<BreadcrumbIndex />
+		<TagsView v-if="setShowTagsView" />
+	</div>
+</template>
+
+<script setup lang="ts" name="layoutNavBars">
+import { defineAsyncComponent, computed } from 'vue';
+import { storeToRefs } from 'pinia';
+import { useThemeConfig } from '/@/stores/themeConfig';
+
+// 引入组件
+const BreadcrumbIndex = defineAsyncComponent(() => import('/@/layout/navBars/breadcrumb/index.vue'));
+const TagsView = defineAsyncComponent(() => import('/@/layout/navBars/tagsView/tagsView.vue'));
+
+// 定义变量内容
+const storesThemeConfig = useThemeConfig();
+const { themeConfig } = storeToRefs(storesThemeConfig);
+
+// 是否显示 tagsView
+const setShowTagsView = computed(() => {
+	let { layout, isTagsview } = themeConfig.value;
+	return layout !== 'classic' && isTagsview;
+});
+</script>
+
+<style scoped lang="scss">
+.layout-navbars-container {
+	display: flex;
+	flex-direction: column;
+	width: 100%;
+	height: 100%;
+}
+</style>
Added +138 -0
diff --git a/src/layout/navBars/tagsView/contextmenu.vue b/src/layout/navBars/tagsView/contextmenu.vue
new file mode 100644
index 0000000..92a228d
--- /dev/null
+++ b/src/layout/navBars/tagsView/contextmenu.vue
@@ -0,0 +1,138 @@
+<template>
+	<transition name="el-zoom-in-center">
+		<div
+			aria-hidden="true"
+			class="el-dropdown__popper el-popper is-light is-pure custom-contextmenu"
+			role="tooltip"
+			data-popper-placement="bottom"
+			:style="`top: ${dropdowns.y + 5}px;left: ${dropdowns.x}px;`"
+			:key="Math.random()"
+			v-show="state.isShow"
+		>
+			<ul class="el-dropdown-menu">
+				<template v-for="(v, k) in state.dropdownList">
+					<li
+						class="el-dropdown-menu__item"
+						aria-disabled="false"
+						tabindex="-1"
+						:key="k"
+						v-if="!v.affix"
+						@click="onCurrentContextmenuClick(v.contextMenuClickId)"
+					>
+						<SvgIcon :name="v.icon" />
+						<span>{{ $t(v.txt) }}</span>
+					</li>
+				</template>
+			</ul>
+			<div class="el-popper__arrow" :style="{ left: `${state.arrowLeft}px` }"></div>
+		</div>
+	</transition>
+</template>
+
+<script setup lang="ts" name="layoutTagsViewContextmenu">
+import { computed, reactive, onMounted, onUnmounted, watch } from 'vue';
+
+// 定义父组件传过来的值
+const props = defineProps({
+	dropdown: {
+		type: Object,
+		default: () => {
+			return {
+				x: 0,
+				y: 0,
+			};
+		},
+	},
+});
+
+// 定义子组件向父组件传值/事件
+const emit = defineEmits(['currentContextmenuClick']);
+
+// 定义变量内容
+const state = reactive({
+	isShow: false,
+	dropdownList: [
+		{ contextMenuClickId: 0, txt: 'message.tagsView.refresh', affix: false, icon: 'ele-RefreshRight' },
+		{ contextMenuClickId: 1, txt: 'message.tagsView.close', affix: false, icon: 'ele-Close' },
+		{ contextMenuClickId: 2, txt: 'message.tagsView.closeOther', affix: false, icon: 'ele-CircleClose' },
+		{ contextMenuClickId: 3, txt: 'message.tagsView.closeAll', affix: false, icon: 'ele-FolderDelete' },
+		{
+			contextMenuClickId: 4,
+			txt: 'message.tagsView.fullscreen',
+			affix: false,
+			icon: 'iconfont icon-fullscreen',
+		},
+	],
+	item: {},
+	arrowLeft: 10,
+});
+
+// 父级传过来的坐标 x,y 值
+const dropdowns = computed(() => {
+	// 117 为 `Dropdown 下拉菜单` 的宽度
+	if (props.dropdown.x + 117 > document.documentElement.clientWidth) {
+		return {
+			x: document.documentElement.clientWidth - 117 - 5,
+			y: props.dropdown.y,
+		};
+	} else {
+		return props.dropdown;
+	}
+});
+// 当前项菜单点击
+const onCurrentContextmenuClick = (contextMenuClickId: number) => {
+	emit('currentContextmenuClick', Object.assign({}, { contextMenuClickId }, state.item));
+};
+// 打开右键菜单:判断是否固定,固定则不显示关闭按钮
+const openContextmenu = (item: RouteItem) => {
+	state.item = item;
+	item.meta?.isAffix ? (state.dropdownList[1].affix = true) : (state.dropdownList[1].affix = false);
+	closeContextmenu();
+	setTimeout(() => {
+		state.isShow = true;
+	}, 10);
+};
+// 关闭右键菜单
+const closeContextmenu = () => {
+	state.isShow = false;
+};
+// 监听页面监听进行右键菜单的关闭
+onMounted(() => {
+	document.body.addEventListener('click', closeContextmenu);
+});
+// 页面卸载时,移除右键菜单监听事件
+onUnmounted(() => {
+	document.body.removeEventListener('click', closeContextmenu);
+});
+// 监听下拉菜单位置
+watch(
+	() => props.dropdown,
+	({ x }) => {
+		if (x + 117 > document.documentElement.clientWidth) state.arrowLeft = 117 - (document.documentElement.clientWidth - x);
+		else state.arrowLeft = 10;
+	},
+	{
+		deep: true,
+	}
+);
+
+// 暴露变量
+defineExpose({
+	openContextmenu,
+});
+</script>
+
+<style scoped lang="scss">
+.custom-contextmenu {
+	transform-origin: center top;
+	z-index: 2190;
+	position: fixed;
+	.el-dropdown-menu__item {
+		font-size: 12px !important;
+		white-space: nowrap;
+		i {
+			font-size: 12px !important;
+		}
+	}
+}
+</style>
Added +726 -0
diff --git a/src/layout/navBars/tagsView/tagsView.vue b/src/layout/navBars/tagsView/tagsView.vue
new file mode 100644
index 0000000..4383cc0
--- /dev/null
+++ b/src/layout/navBars/tagsView/tagsView.vue
@@ -0,0 +1,726 @@
+<template>
+	<div class="layout-navbars-tagsview" :class="{ 'layout-navbars-tagsview-shadow': getThemeConfig.layout === 'classic' }">
+		<el-scrollbar ref="scrollbarRef" @wheel.prevent="onHandleScroll">
+			<ul class="layout-navbars-tagsview-ul" :class="setTagsStyle" ref="tagsUlRef">
+				<li
+					v-for="(v, k) in state.tagsViewList"
+					:key="k"
+					class="layout-navbars-tagsview-ul-li"
+					:data-url="v.url"
+					:class="{ 'is-active': isActive(v) }"
+					@contextmenu.prevent="onContextmenu(v, $event)"
+					@mousedown="onMousedownMenu(v, $event)"
+					@click="onTagsClick(v, k)"
+					:ref="
+						(el) => {
+							if (el) tagsRefs[k] = el;
+						}
+					"
+				>
+					<i class="iconfont icon-webicon318 layout-navbars-tagsview-ul-li-iconfont" v-if="isActive(v)"></i>
+					<SvgIcon :name="v.meta.icon" v-if="!isActive(v) && getThemeConfig.isTagsviewIcon" class="pr5" />
+					<span>{{ setTagsViewNameI18n(v) }}</span>
+					<template v-if="isActive(v)">
+						<SvgIcon
+							name="ele-RefreshRight"
+							class="ml5 layout-navbars-tagsview-ul-li-refresh"
+							@click.stop="refreshCurrentTagsView($route.fullPath)"
+						/>
+						<SvgIcon
+							name="ele-Close"
+							class="layout-navbars-tagsview-ul-li-icon layout-icon-active"
+							v-if="!v.meta.isAffix"
+							@click.stop="closeCurrentTagsView(getThemeConfig.isShareTagsView ? v.path : v.url)"
+						/>
+					</template>
+					<SvgIcon
+						name="ele-Close"
+						class="layout-navbars-tagsview-ul-li-icon layout-icon-three"
+						v-if="!v.meta.isAffix"
+						@click.stop="closeCurrentTagsView(getThemeConfig.isShareTagsView ? v.path : v.url)"
+					/>
+				</li>
+			</ul>
+		</el-scrollbar>
+		<Contextmenu :dropdown="state.dropdown" ref="contextmenuRef" @currentContextmenuClick="onCurrentContextmenuClick" />
+	</div>
+</template>
+
+<script setup lang="ts" name="layoutTagsView">
+import { defineAsyncComponent, reactive, onMounted, computed, ref, nextTick, onBeforeUpdate, onBeforeMount, onUnmounted, watch } from 'vue';
+import { useRoute, useRouter, onBeforeRouteUpdate } from 'vue-router';
+import Sortable from 'sortablejs';
+import { ElMessage } from 'element-plus';
+import { storeToRefs } from 'pinia';
+import pinia from '/@/stores/index';
+import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
+import { useThemeConfig } from '/@/stores/themeConfig';
+import { useKeepALiveNames } from '/@/stores/keepAliveNames';
+import { Session } from '/@/utils/storage';
+import { isObjectValueEqual } from '/@/utils/arrayOperation';
+import other from '/@/utils/other';
+import mittBus from '/@/utils/mitt';
+
+// 引入组件
+const Contextmenu = defineAsyncComponent(() => import('/@/layout/navBars/tagsView/contextmenu.vue'));
+
+// 定义变量内容
+const tagsRefs = ref<RefType>([]);
+const scrollbarRef = ref();
+const contextmenuRef = ref();
+const tagsUlRef = ref();
+const stores = useTagsViewRoutes();
+const storesThemeConfig = useThemeConfig();
+const storesTagsViewRoutes = useTagsViewRoutes();
+const { themeConfig } = storeToRefs(storesThemeConfig);
+const { tagsViewRoutes } = storeToRefs(storesTagsViewRoutes);
+const storesKeepALiveNames = useKeepALiveNames();
+const route = useRoute();
+const router = useRouter();
+const state = reactive<TagsViewState>({
+	routeActive: '',
+	routePath: route.path,
+	dropdown: { x: '', y: '' },
+	sortable: '',
+	tagsRefsIndex: 0,
+	tagsViewList: [],
+	tagsViewRoutesList: [],
+});
+
+// 动态设置 tagsView 风格样式
+const setTagsStyle = computed(() => {
+	return themeConfig.value.tagsStyle;
+});
+// 获取布局配置信息
+const getThemeConfig = computed(() => {
+	return themeConfig.value;
+});
+// 设置 自定义 tagsView 名称、 自定义 tagsView 名称国际化
+const setTagsViewNameI18n = computed(() => {
+	return (v: RouteItem) => {
+		return other.setTagsViewNameI18n(v);
+	};
+});
+// 设置 tagsView 高亮
+const isActive = (v: RouteItem) => {
+	if (getThemeConfig.value.isShareTagsView) {
+		return v.path === state.routePath;
+	} else {
+		if ((v.query && Object.keys(v.query).length) || (v.params && Object.keys(v.params).length)) {
+			// 普通传参
+			return v.url ? v.url === state.routeActive : v.path === state.routeActive;
+		} else {
+			// 通过 name 传参,params 取值,刷新页面参数消失
+			// https://gitee.com/lyt-top/vue-next-admin/issues/I51RS9
+			return v.path === state.routePath;
+		}
+	}
+};
+// 存储 tagsViewList 到浏览器临时缓存中,页面刷新时,保留记录
+const addBrowserSetSession = (tagsViewList: Array<object>) => {
+	Session.set('tagsViewList', tagsViewList);
+};
+// 获取 pinia 中的 tagsViewRoutes 列表
+const getTagsViewRoutes = async () => {
+	state.routeActive = await setTagsViewHighlight(route);
+	state.routePath = (await route.meta.isDynamic) ? route.meta.isDynamicPath : route.path;
+	state.tagsViewList = [];
+	state.tagsViewRoutesList = tagsViewRoutes.value;
+	initTagsView();
+};
+// pinia 中获取路由信息:如果是设置了固定的(isAffix),进行初始化显示
+const initTagsView = async () => {
+	if (Session.get('tagsViewList') && getThemeConfig.value.isCacheTagsView) {
+		state.tagsViewList = await Session.get('tagsViewList');
+	} else {
+		await state.tagsViewRoutesList.map((v: RouteItem) => {
+			if (v.meta?.isAffix && !v.meta.isHide) {
+				v.url = setTagsViewHighlight(v);
+				state.tagsViewList.push({ ...v });
+				storesKeepALiveNames.addCachedView(v);
+			}
+		});
+		await addTagsView(route.path, <RouteToFrom>route);
+	}
+	// 初始化当前元素(li)的下标
+	getTagsRefsIndex(getThemeConfig.value.isShareTagsView ? state.routePath : state.routeActive);
+};
+// 处理可开启多标签详情,单标签详情(动态路由(xxx/:id/:name"),普通路由处理)
+const solveAddTagsView = async (path: string, to?: RouteToFrom) => {
+	let isDynamicPath = to?.meta?.isDynamic ? to.meta.isDynamicPath : path;
+	let current = state.tagsViewList.filter(
+		(v: RouteItem) =>
+			v.path === isDynamicPath &&
+			isObjectValueEqual(
+				to?.meta?.isDynamic ? (v.params ? v.params : null) : v.query ? v.query : null,
+				to?.meta?.isDynamic ? (to?.params ? to?.params : null) : to?.query ? to?.query : null
+			)
+	);
+	if (current.length <= 0) {
+		// 防止:Avoid app logic that relies on enumerating keys on a component instance. The keys will be empty in production mode to avoid performance overhead.
+		let findItem = state.tagsViewRoutesList.find((v: RouteItem) => v.path === isDynamicPath);
+		if (!findItem) return false;
+		if (findItem.meta.isAffix) return false;
+		if (findItem.meta.isLink && !findItem.meta.isIframe) return false;
+		to?.meta?.isDynamic ? (findItem.params = to.params) : (findItem.query = to?.query);
+		findItem.url = setTagsViewHighlight(findItem);
+		state.tagsViewList.push({ ...findItem });
+		await storesKeepALiveNames.addCachedView(findItem);
+		addBrowserSetSession(state.tagsViewList);
+	}
+};
+// 处理单标签时,第二次的值未覆盖第一次的 tagsViewList 值(Session Storage)
+const singleAddTagsView = (path: string, to?: RouteToFrom) => {
+	let isDynamicPath = to?.meta?.isDynamic ? to.meta.isDynamicPath : path;
+	state.tagsViewList.forEach((v) => {
+		if (
+			v.path === isDynamicPath &&
+			!isObjectValueEqual(
+				to?.meta?.isDynamic ? (v.params ? v.params : null) : v.query ? v.query : null,
+				to?.meta?.isDynamic ? (to?.params ? to?.params : null) : to?.query ? to?.query : null
+			)
+		) {
+			to?.meta?.isDynamic ? (v.params = to.params) : (v.query = to?.query);
+			v.url = setTagsViewHighlight(v);
+			addBrowserSetSession(state.tagsViewList);
+		}
+	});
+};
+// 1、添加 tagsView:未设置隐藏(isHide)也添加到在 tagsView 中(可开启多标签详情,单标签详情)
+const addTagsView = (path: string, to?: RouteToFrom) => {
+	// 防止拿取不到路由信息
+	nextTick(async () => {
+		// 修复:https://gitee.com/lyt-top/vue-next-admin/issues/I3YX6G
+		let item: RouteItem;
+		if (to?.meta?.isDynamic) {
+			// 动态路由(xxx/:id/:name"):参数不同,开启多个 tagsview
+			if (!getThemeConfig.value.isShareTagsView) await solveAddTagsView(path, to);
+			else await singleAddTagsView(path, to);
+			if (state.tagsViewList.some((v: RouteItem) => v.path === to?.meta?.isDynamicPath)) {
+				// 防止首次进入界面时(登录进入) tagsViewList 不存浏览器中
+				addBrowserSetSession(state.tagsViewList);
+				return false;
+			}
+			item = state.tagsViewRoutesList.find((v: RouteItem) => v.path === to?.meta?.isDynamicPath);
+		} else {
+			// 普通路由:参数不同,开启多个 tagsview
+			if (!getThemeConfig.value.isShareTagsView) await solveAddTagsView(path, to);
+			else await singleAddTagsView(path, to);
+			if (state.tagsViewList.some((v: RouteItem) => v.path === path)) {
+				// 防止首次进入界面时(登录进入) tagsViewList 不存浏览器中
+				addBrowserSetSession(state.tagsViewList);
+				return false;
+			}
+			item = state.tagsViewRoutesList.find((v: RouteItem) => v.path === path);
+		}
+		if (!item) return false;
+		if (item?.meta?.isLink && !item.meta.isIframe) return false;
+		if (to?.meta?.isDynamic) item.params = to?.params ? to?.params : route.params;
+		else item.query = to?.query ? to?.query : route.query;
+		item.url = setTagsViewHighlight(item);
+		await storesKeepALiveNames.addCachedView(item);
+		await state.tagsViewList.push({ ...item });
+		await addBrowserSetSession(state.tagsViewList);
+	});
+};
+// 2、刷新当前 tagsView:
+const refreshCurrentTagsView = async (fullPath: string) => {
+	const decodeURIPath = decodeURI(fullPath);
+	let item: RouteToFrom = {};
+	state.tagsViewList.forEach((v: RouteItem) => {
+		v.transUrl = transUrlParams(v);
+		if (v.transUrl) {
+			if (v.transUrl === transUrlParams(v)) item = v;
+		} else {
+			if (v.path === decodeURIPath) item = v;
+		}
+	});
+	if (!item) return false;
+	await storesKeepALiveNames.delCachedView(item);
+	mittBus.emit('onTagsViewRefreshRouterView', fullPath);
+	if (item.meta?.isKeepAlive) storesKeepALiveNames.addCachedView(item);
+};
+// 3、关闭当前 tagsView:如果是设置了固定的(isAffix),不可以关闭
+const closeCurrentTagsView = (path: string) => {
+	state.tagsViewList.map((v: RouteItem, k: number, arr: RouteItems) => {
+		if (!v.meta?.isAffix) {
+			if (getThemeConfig.value.isShareTagsView ? v.path === path : v.url === path) {
+				storesKeepALiveNames.delCachedView(v);
+				state.tagsViewList.splice(k, 1);
+				setTimeout(() => {
+					if (state.tagsViewList.length === k && getThemeConfig.value.isShareTagsView ? state.routePath === path : state.routeActive === path) {
+						// 最后一个且高亮时
+						if (arr[arr.length - 1].meta.isDynamic) {
+							// 动态路由(xxx/:id/:name")
+							if (k !== arr.length) router.push({ name: arr[k].name, params: arr[k].params });
+							else router.push({ name: arr[arr.length - 1].name, params: arr[arr.length - 1].params });
+						} else {
+							// 普通路由
+							if (k !== arr.length) router.push({ path: arr[k].path, query: arr[k].query });
+							else router.push({ path: arr[arr.length - 1].path, query: arr[arr.length - 1].query });
+						}
+					} else {
+						// 非最后一个且高亮时,跳转到下一个
+						if (state.tagsViewList.length !== k && getThemeConfig.value.isShareTagsView ? state.routePath === path : state.routeActive === path) {
+							if (arr[k].meta.isDynamic) {
+								// 动态路由(xxx/:id/:name")
+								router.push({ name: arr[k].name, params: arr[k].params });
+							} else {
+								// 普通路由
+								router.push({ path: arr[k].path, query: arr[k].query });
+							}
+						}
+					}
+				}, 0);
+			}
+		}
+	});
+	addBrowserSetSession(state.tagsViewList);
+};
+// 4、关闭其它 tagsView:如果是设置了固定的(isAffix),不进行关闭
+const closeOtherTagsView = (path: string) => {
+	if (Session.get('tagsViewList')) {
+		state.tagsViewList = [];
+		Session.get('tagsViewList').map((v: RouteItem) => {
+			if (v.meta?.isAffix && !v.meta.isHide) {
+				v.url = setTagsViewHighlight(v);
+				storesKeepALiveNames.delOthersCachedViews(v);
+				state.tagsViewList.push({ ...v });
+			}
+		});
+		addTagsView(path, <RouteToFrom>route);
+		addBrowserSetSession(state.tagsViewList);
+	}
+};
+// 5、关闭全部 tagsView:如果是设置了固定的(isAffix),不进行关闭
+const closeAllTagsView = () => {
+	if (Session.get('tagsViewList')) {
+		storesKeepALiveNames.delAllCachedViews();
+		state.tagsViewList = [];
+		Session.get('tagsViewList').map((v: RouteItem) => {
+			if (v.meta?.isAffix && !v.meta.isHide) {
+				v.url = setTagsViewHighlight(v);
+				state.tagsViewList.push({ ...v });
+				router.push({ path: state.tagsViewList[state.tagsViewList.length - 1].path });
+			}
+		});
+		addBrowserSetSession(state.tagsViewList);
+	}
+};
+// 6、开启当前页面全屏
+const openCurrenFullscreen = async (path: string) => {
+	const item = state.tagsViewList.find((v: RouteItem) => (getThemeConfig.value.isShareTagsView ? v.path === path : v.url === path));
+	if (item.meta.isDynamic) await router.push({ name: item.name, params: item.params });
+	else await router.push({ name: item.name, query: item.query });
+	stores.setCurrenFullscreen(true);
+};
+// 当前项右键菜单点击,拿 `当前点击的路由路径` 对比 `tagsView 路由数组`,取当前点击项的详细路由信息
+// 防止 tagsView 非当前页演示时,操作异常
+// https://gitee.com/lyt-top/vue-next-admin/issues/I61VS9
+const getCurrentRouteItem = (item: RouteItem): any => {
+	let resItem: RouteToFrom = {};
+	state.tagsViewList.forEach((v: RouteItem) => {
+		v.transUrl = transUrlParams(v);
+		if (v.transUrl) {
+			// 动态路由、普通路由带参数
+			if (v.transUrl === transUrlParams(v) && v.transUrl === item.commonUrl) resItem = v;
+		} else {
+			// 路由不带参数
+			if (v.path === decodeURI(item.path)) resItem = v;
+		}
+	});
+	if (!resItem) return null;
+	else return resItem;
+};
+// 当前项右键菜单点击
+const onCurrentContextmenuClick = async (item: RouteItem) => {
+	item.commonUrl = transUrlParams(item);
+	if (!getCurrentRouteItem(item)) return ElMessage({ type: 'warning', message: '请正确输入路径及完整参数(query、params)' });
+	const { path, name, params, query, meta, url } = getCurrentRouteItem(item);
+	switch (item.contextMenuClickId) {
+		case 0:
+			// 刷新当前
+			if (meta.isDynamic) await router.push({ name, params });
+			else await router.push({ path, query });
+			refreshCurrentTagsView(route.fullPath);
+			break;
+		case 1:
+			// 关闭当前
+			closeCurrentTagsView(getThemeConfig.value.isShareTagsView ? path : url);
+			break;
+		case 2:
+			// 关闭其它
+			if (meta.isDynamic) await router.push({ name, params });
+			else await router.push({ path, query });
+			closeOtherTagsView(path);
+			break;
+		case 3:
+			// 关闭全部
+			closeAllTagsView();
+			break;
+		case 4:
+			// 开启当前页面全屏
+			openCurrenFullscreen(getThemeConfig.value.isShareTagsView ? path : url);
+			break;
+	}
+};
+// 右键点击时:传 x,y 坐标值到子组件中(props)
+const onContextmenu = (v: RouteItem, e: MouseEvent) => {
+	const { clientX, clientY } = e;
+	state.dropdown.x = clientX;
+	state.dropdown.y = clientY;
+	contextmenuRef.value.openContextmenu(v);
+};
+// 鼠标按下时,判断是鼠标中键就关闭当前 tasgview
+const onMousedownMenu = (v: RouteItem, e: MouseEvent) => {
+	if (!v.meta?.isAffix && e.button === 1) {
+		const item = Object.assign({}, { contextMenuClickId: 1, ...v });
+		onCurrentContextmenuClick(item);
+	}
+};
+// 当前的 tagsView 项点击时
+const onTagsClick = (v: RouteItem, k: number) => {
+	state.tagsRefsIndex = k;
+	router.push(v);
+};
+// 处理 url,地址栏链接有参数时,tagsview 右键菜单刷新功能失效问题,感谢 @ZzZz-RIPPER、@dejavuuuuu
+// https://gitee.com/lyt-top/vue-next-admin/issues/I5K3YO
+// https://gitee.com/lyt-top/vue-next-admin/issues/I61VS9
+const transUrlParams = (v: RouteItem) => {
+	let params = v.query && Object.keys(v.query).length > 0 ? v.query : v.params;
+	if (!params) return '';
+	let path = '';
+	for (let [key, value] of Object.entries(params)) {
+		if (v.meta?.isDynamic) path += `/${value}`;
+		else path += `&${key}=${value}`;
+	}
+	// 判断是否是动态路由(xxx/:id/:name")isDynamic
+	if (v.meta?.isDynamic) {
+		/**
+		 *
+		 * isFnClick 用于判断是通过方法调用,还是直接右键菜单点击(此处只针对动态路由)
+		 * 原因:
+		 * 1、右键菜单点击时,路由的 path 还是原始定义的路由格式,如:/params/dynamic/details/:t/:id/:tagsViewName
+		 * 2、通过事件调用时,路由的 path 不是原始定义的路由格式,如:/params/dynamic/details/vue-next-admin/111/我是动态路由测试tagsViewName(非国际化)
+		 *
+		 * 所以右侧菜单点击时,需要处理路径拼接 v.path.split(':')[0],得到路径 + 参数的完整路径
+		 */
+		return v.isFnClick ? decodeURI(v.path) : `${v.path.split(':')[0]}${path.replace(/^\//, '')}`;
+	} else {
+		return `${v.path}${path.replace(/^&/, '?')}`;
+	}
+};
+// 处理 tagsView 高亮(多标签详情时使用,单标签详情未使用)
+const setTagsViewHighlight = (v: RouteToFrom) => {
+	let params = v.query && Object.keys(v.query).length > 0 ? v.query : v.params;
+	if (!params || Object.keys(params).length <= 0) return v.path;
+	let path = '';
+	for (let i in params) {
+		path += params[i];
+	}
+	// 判断是否是动态路由(xxx/:id/:name")
+	return `${v.meta?.isDynamic ? v.meta.isDynamicPath : v.path}-${path}`;
+};
+// 鼠标滚轮滚动
+const onHandleScroll = (e: WheelEventType) => {
+	scrollbarRef.value.$refs.wrapRef.scrollLeft += e.wheelDelta / 4;
+};
+// tagsView 横向滚动
+const tagsViewmoveToCurrentTag = () => {
+	nextTick(() => {
+		if (tagsRefs.value.length <= 0) return false;
+		// 当前 li 元素
+		let liDom = tagsRefs.value[state.tagsRefsIndex];
+		// 当前 li 元素下标
+		let liIndex = state.tagsRefsIndex;
+		// 当前 ul 下 li 元素总长度
+		let liLength = tagsRefs.value.length;
+		// 最前 li
+		let liFirst = tagsRefs.value[0];
+		// 最后 li
+		let liLast = tagsRefs.value[tagsRefs.value.length - 1];
+		// 当前滚动条的值
+		let scrollRefs = scrollbarRef.value.$refs.wrapRef;
+		// 当前滚动条滚动宽度
+		let scrollS = scrollRefs.scrollWidth;
+		// 当前滚动条偏移宽度
+		let offsetW = scrollRefs.offsetWidth;
+		// 当前滚动条偏移距离
+		let scrollL = scrollRefs.scrollLeft;
+		// 上一个 tags li dom
+		let liPrevTag = tagsRefs.value[state.tagsRefsIndex - 1];
+		// 下一个 tags li dom
+		let liNextTag = tagsRefs.value[state.tagsRefsIndex + 1];
+		// 上一个 tags li dom 的偏移距离
+		let beforePrevL = 0;
+		// 下一个 tags li dom 的偏移距离
+		let afterNextL = 0;
+		if (liDom === liFirst) {
+			// 头部
+			scrollRefs.scrollLeft = 0;
+		} else if (liDom === liLast) {
+			// 尾部
+			scrollRefs.scrollLeft = scrollS - offsetW;
+		} else {
+			// 非头/尾部
+			if (liIndex === 0) beforePrevL = liFirst.offsetLeft - 5;
+			else beforePrevL = liPrevTag?.offsetLeft - 5;
+			if (liIndex === liLength) afterNextL = liLast.offsetLeft + liLast.offsetWidth + 5;
+			else afterNextL = liNextTag.offsetLeft + liNextTag.offsetWidth + 5;
+			if (afterNextL > scrollL + offsetW) {
+				scrollRefs.scrollLeft = afterNextL - offsetW;
+			} else if (beforePrevL < scrollL) {
+				scrollRefs.scrollLeft = beforePrevL;
+			}
+		}
+		// 更新滚动条,防止不出现
+		scrollbarRef.value.update();
+	});
+};
+// 获取 tagsView 的下标:用于处理 tagsView 点击时的横向滚动
+const getTagsRefsIndex = (path: string | unknown) => {
+	nextTick(async () => {
+		// await 使用该写法,防止拿取不到 tagsViewList 列表数据不完整
+		let tagsViewList = await state.tagsViewList;
+		state.tagsRefsIndex = tagsViewList.findIndex((v: RouteItem) => {
+			if (getThemeConfig.value.isShareTagsView) {
+				return v.path === path;
+			} else {
+				return v.url === path;
+			}
+		});
+		// 添加初始化横向滚动条移动到对应位置
+		tagsViewmoveToCurrentTag();
+	});
+};
+// 设置 tagsView 可以进行拖拽
+const initSortable = async () => {
+	const el = <HTMLElement>document.querySelector('.layout-navbars-tagsview-ul');
+	if (!el) return false;
+	state.sortable.el && state.sortable.destroy();
+	state.sortable = Sortable.create(el, {
+		animation: 300,
+		dataIdAttr: 'data-url',
+		disabled: getThemeConfig.value.isSortableTagsView ? false : true,
+		onEnd: () => {
+			const sortEndList: RouteItem[] = [];
+			state.sortable.toArray().map((val: string) => {
+				state.tagsViewList.map((v: RouteItem) => {
+					if (v.url === val) sortEndList.push({ ...v });
+				});
+			});
+			addBrowserSetSession(sortEndList);
+		},
+	});
+};
+// 拖动问题,https://gitee.com/lyt-top/vue-next-admin/issues/I3ZRRI
+const onSortableResize = async () => {
+	await initSortable();
+	if (other.isMobile()) state.sortable.el && state.sortable.destroy();
+};
+// 页面加载前
+onBeforeMount(() => {
+	// 初始化,防止手机端直接访问时还可以拖拽
+	onSortableResize();
+	// 拖动问题,https://gitee.com/lyt-top/vue-next-admin/issues/I3ZRRI
+	window.addEventListener('resize', onSortableResize);
+	// 监听非本页面调用 0 刷新当前,1 关闭当前,2 关闭其它,3 关闭全部 4 当前页全屏
+	mittBus.on('onCurrentContextmenuClick', (data: RouteItem) => {
+		// 通过方法点击关闭 tagsView
+		data.isFnClick = true;
+		onCurrentContextmenuClick(data);
+	});
+	// 监听布局配置界面开启/关闭拖拽
+	mittBus.on('openOrCloseSortable', () => {
+		initSortable();
+	});
+	// 监听布局配置开启 TagsView 共用,为了演示还原默认值
+	mittBus.on('openShareTagsView', () => {
+		if (getThemeConfig.value.isShareTagsView) {
+			router.push('/home');
+			state.tagsViewList = [];
+			state.tagsViewRoutesList.map((v: RouteItem) => {
+				if (v.meta?.isAffix && !v.meta.isHide) {
+					v.url = setTagsViewHighlight(v);
+					state.tagsViewList.push({ ...v });
+				}
+			});
+		}
+	});
+});
+// 页面卸载时
+onUnmounted(() => {
+	// 取消非本页面调用监听
+	mittBus.off('onCurrentContextmenuClick', () => {});
+	// 取消监听布局配置界面开启/关闭拖拽
+	mittBus.off('openOrCloseSortable', () => {});
+	// 取消监听布局配置开启 TagsView 共用
+	mittBus.off('openShareTagsView', () => {});
+	// 取消窗口 resize 监听
+	window.removeEventListener('resize', onSortableResize);
+});
+// 页面更新时
+onBeforeUpdate(() => {
+	tagsRefs.value = [];
+});
+// 页面加载时
+onMounted(() => {
+	// 初始化 pinia 中的 tagsViewRoutes 列表
+	getTagsViewRoutes();
+	initSortable();
+});
+// 路由更新时(组件内生命钩子)
+onBeforeRouteUpdate(async (to) => {
+	state.routeActive = setTagsViewHighlight(to);
+	state.routePath = to.meta.isDynamic ? to.meta.isDynamicPath : to.path;
+	await addTagsView(to.path, <RouteToFrom>to);
+	getTagsRefsIndex(getThemeConfig.value.isShareTagsView ? state.routePath : state.routeActive);
+});
+// 监听路由的变化,动态赋值给 tagsView
+watch(
+	pinia.state,
+	(val) => {
+		if (val.tagsViewRoutes.tagsViewRoutes.length === state.tagsViewRoutesList.length) return false;
+		getTagsViewRoutes();
+	},
+	{
+		deep: true,
+	}
+);
+</script>
+
+<style scoped lang="scss">
+.layout-navbars-tagsview {
+	background-color: var(--el-color-white);
+	border-bottom: 1px solid var(--next-border-color-light);
+	position: relative;
+	z-index: 4;
+	:deep(.el-scrollbar__wrap) {
+		overflow-x: auto !important;
+	}
+	&-ul {
+		list-style: none;
+		margin: 0;
+		padding: 0;
+		height: 34px;
+		display: flex;
+		align-items: center;
+		color: var(--el-text-color-regular);
+		font-size: 12px;
+		white-space: nowrap;
+		padding: 0 15px;
+		&-li {
+			height: 26px;
+			line-height: 26px;
+			display: flex;
+			align-items: center;
+			border: 1px solid var(--el-border-color-lighter);
+			padding: 0 15px;
+			margin-right: 5px;
+			border-radius: 2px;
+			position: relative;
+			z-index: 0;
+			cursor: pointer;
+			justify-content: space-between;
+			&:hover {
+				background-color: var(--el-color-primary-light-9);
+				color: var(--el-color-primary);
+				border-color: var(--el-color-primary-light-5);
+			}
+			&-iconfont {
+				position: relative;
+				left: -5px;
+				font-size: 12px;
+			}
+			&-icon {
+				border-radius: 100%;
+				position: relative;
+				height: 14px;
+				width: 14px;
+				text-align: center;
+				line-height: 14px;
+				right: -5px;
+				&:hover {
+					color: var(--el-color-white);
+					background-color: var(--el-color-primary-light-3);
+				}
+			}
+			.layout-icon-active {
+				display: block;
+			}
+			.layout-icon-three {
+				display: none;
+			}
+		}
+		.is-active {
+			color: var(--el-color-white);
+			background: var(--el-color-primary);
+			border-color: var(--el-color-primary);
+			transition: border-color 3s ease;
+		}
+	}
+	// 风格4
+	.tags-style-four {
+		.layout-navbars-tagsview-ul-li {
+			margin-right: 0 !important;
+			border: none !important;
+			position: relative;
+			border-radius: 3px !important;
+			.layout-icon-active {
+				display: none;
+			}
+			.layout-icon-three {
+				display: block;
+			}
+			&:hover {
+				background: none !important;
+			}
+		}
+		.is-active {
+			background: none !important;
+			color: var(--el-color-primary) !important;
+		}
+	}
+	// 风格5
+	.tags-style-five {
+		align-items: flex-end;
+		.tags-style-five-svg {
+			-webkit-mask-image: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNzAiIGhlaWdodD0iNzAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgZmlsbD0ibm9uZSI+CgogPGc+CiAgPHRpdGxlPkxheWVyIDE8L3RpdGxlPgogIDxwYXRoIHRyYW5zZm9ybT0icm90YXRlKC0wLjEzMzUwNiA1MC4xMTkyIDUwKSIgaWQ9InN2Z18xIiBkPSJtMTAwLjExOTE5LDEwMGMtNTUuMjI4LDAgLTEwMCwtNDQuNzcyIC0xMDAsLTEwMGwwLDEwMGwxMDAsMHoiIG9wYWNpdHk9InVuZGVmaW5lZCIgc3Ryb2tlPSJudWxsIiBmaWxsPSIjRjhFQUU3Ii8+CiAgPHBhdGggZD0ibS0wLjYzNzY2LDcuMzEyMjhjMC4xMTkxOSwwIDAuMjE3MzcsMC4wNTc5NiAwLjQ3Njc2LDAuMTE5MTljMC4yMzIsMC4wNTQ3NyAwLjI3MzI5LDAuMDM0OTEgMC4zNTc1NywwLjExOTE5YzAuMDg0MjgsMC4wODQyOCAwLjM1NzU3LDAgMC40NzY3NiwwbDAuMTE5MTksMGwwLjIzODM4LDAiIGlkPSJzdmdfMiIgc3Ryb2tlPSJudWxsIiBmaWxsPSJub25lIi8+CiAgPHBhdGggZD0ibTI4LjkyMTM0LDY5LjA1MjQ0YzAsMC4xMTkxOSAwLDAuMjM4MzggMCwwLjM1NzU3bDAsMC4xMTkxOWwwLDAuMTE5MTkiIGlkPSJzdmdfMyIgc3Ryb2tlPSJudWxsIiBmaWxsPSJub25lIi8+CiAgPHJlY3QgaWQ9InN2Z180IiBoZWlnaHQ9IjAiIHdpZHRoPSIxLjMxMTA4IiB5PSI2LjgzNTUyIiB4PSItMC4wNDE3MSIgc3Ryb2tlPSJudWxsIiBmaWxsPSJub25lIi8+CiAgPHJlY3QgaWQ9InN2Z181IiBoZWlnaHQ9IjEuNzg3ODQiIHdpZHRoPSIwLjExOTE5IiB5PSI2OC40NTY1IiB4PSIyOC45MjEzNCIgc3Ryb2tlPSJudWxsIiBmaWxsPSJub25lIi8+CiAgPHJlY3QgaWQ9InN2Z182IiBoZWlnaHQ9IjQuODg2NzciIHdpZHRoPSIxOS4wNzAzMiIgeT0iNTEuMjkzMjEiIHg9IjM2LjY2ODY2IiBzdHJva2U9Im51bGwiIGZpbGw9Im5vbmUiLz4KIDwvZz4KPC9zdmc+'),
+				url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNzAiIGhlaWdodD0iNzAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgZmlsbD0ibm9uZSI+CiA8Zz4KICA8dGl0bGU+TGF5ZXIgMTwvdGl0bGU+CiAgPHBhdGggdHJhbnNmb3JtPSJyb3RhdGUoLTg5Ljc2MjQgNy4zMzAxNCA1NS4xMjUyKSIgc3Ryb2tlPSJudWxsIiBpZD0ic3ZnXzEiIGZpbGw9IiNGOEVBRTciIGQ9Im02Mi41NzQ0OSwxMTcuNTIwODZjLTU1LjIyOCwwIC0xMDAsLTQ0Ljc3MiAtMTAwLC0xMDBsMCwxMDBsMTAwLDB6IiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPgogIDxwYXRoIGQ9Im0tMC42Mzc2Niw3LjMxMjI4YzAuMTE5MTksMCAwLjIxNzM3LDAuMDU3OTYgMC40NzY3NiwwLjExOTE5YzAuMjMyLDAuMDU0NzcgMC4yNzMyOSwwLjAzNDkxIDAuMzU3NTcsMC4xMTkxOWMwLjA4NDI4LDAuMDg0MjggMC4zNTc1NywwIDAuNDc2NzYsMGwwLjExOTE5LDBsMC4yMzgzOCwwIiBpZD0ic3ZnXzIiIHN0cm9rZT0ibnVsbCIgZmlsbD0ibm9uZSIvPgogIDxwYXRoIGQ9Im0yOC45MjEzNCw2OS4wNTI0NGMwLDAuMTE5MTkgMCwwLjIzODM4IDAsMC4zNTc1N2wwLDAuMTE5MTlsMCwwLjExOTE5IiBpZD0ic3ZnXzMiIHN0cm9rZT0ibnVsbCIgZmlsbD0ibm9uZSIvPgogIDxyZWN0IGlkPSJzdmdfNCIgaGVpZ2h0PSIwIiB3aWR0aD0iMS4zMTEwOCIgeT0iNi44MzU1MiIgeD0iLTAuMDQxNzEiIHN0cm9rZT0ibnVsbCIgZmlsbD0ibm9uZSIvPgogIDxyZWN0IGlkPSJzdmdfNSIgaGVpZ2h0PSIxLjc4Nzg0IiB3aWR0aD0iMC4xMTkxOSIgeT0iNjguNDU2NSIgeD0iMjguOTIxMzQiIHN0cm9rZT0ibnVsbCIgZmlsbD0ibm9uZSIvPgogIDxyZWN0IGlkPSJzdmdfNiIgaGVpZ2h0PSI0Ljg4Njc3IiB3aWR0aD0iMTkuMDcwMzIiIHk9IjUxLjI5MzIxIiB4PSIzNi42Njg2NiIgc3Ryb2tlPSJudWxsIiBmaWxsPSJub25lIi8+CiA8L2c+Cjwvc3ZnPg=='),
+				url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'><rect rx='8' width='100%' height='100%' fill='%23F8EAE7'/></svg>");
+			-webkit-mask-size: 18px 30px, 20px 30px, calc(100% - 30px) calc(100% + 17px);
+			-webkit-mask-position: right bottom, left bottom, center top;
+			-webkit-mask-repeat: no-repeat;
+		}
+		.layout-navbars-tagsview-ul-li {
+			padding: 0 5px;
+			border-width: 15px 27px 15px;
+			border-style: solid;
+			border-color: transparent;
+			margin: 0 -15px;
+			.layout-icon-active,
+			.layout-navbars-tagsview-ul-li-iconfont,
+			.layout-navbars-tagsview-ul-li-refresh {
+				display: none;
+			}
+			.layout-icon-three {
+				display: block;
+			}
+			&:hover {
+				@extend .tags-style-five-svg;
+				background: var(--el-color-primary-light-9);
+				color: unset;
+			}
+		}
+		.is-active {
+			@extend .tags-style-five-svg;
+			background: var(--el-color-primary-light-9) !important;
+			color: var(--el-color-primary) !important;
+			z-index: 1;
+		}
+	}
+}
+.layout-navbars-tagsview-shadow {
+	box-shadow: rgb(0 21 41 / 4%) 0px 1px 4px;
+}
+</style>
Added +159 -0
diff --git a/src/layout/navMenu/horizontal.vue b/src/layout/navMenu/horizontal.vue
new file mode 100644
index 0000000..1463376
--- /dev/null
+++ b/src/layout/navMenu/horizontal.vue
@@ -0,0 +1,159 @@
+<template>
+	<div class="el-menu-horizontal-warp">
+		<el-scrollbar @wheel.native.prevent="onElMenuHorizontalScroll" ref="elMenuHorizontalScrollRef">
+			<el-menu router :default-active="state.defaultActive" :ellipsis="false" background-color="transparent" mode="horizontal">
+				<template v-for="val in menuLists">
+					<el-sub-menu :index="val.path" v-if="val.children && val.children.length > 0" :key="val.path">
+						<template #title>
+							<SvgIcon v-if="val.meta.icon" class="iconfont" :name="val.meta.icon" />
+							<span>{{ $t(val.meta.title) }}</span>
+						</template>
+						<SubItem :chil="val.children" />
+					</el-sub-menu>
+					<template v-else>
+						<el-menu-item :index="val.path" :key="val.path">
+							<template #title v-if="!val.meta.isLink || (val.meta.isLink && val.meta.isIframe)">
+								<SvgIcon v-if="val.meta.icon" class="iconfont" :name="val.meta.icon" />
+								{{ $t(val.meta.title) }}
+							</template>
+							<template #title v-else>
+								<a class="w100" @click.prevent="onALinkClick(val)">
+									<SvgIcon v-if="val.meta.icon" class="iconfont" :name="val.meta.icon" />
+									{{ $t(val.meta.title) }}
+								</a>
+							</template>
+						</el-menu-item>
+					</template>
+				</template>
+			</el-menu>
+		</el-scrollbar>
+	</div>
+</template>
+
+<script setup lang="ts" name="navMenuHorizontal">
+import { defineAsyncComponent, reactive, computed, onMounted, nextTick, onBeforeMount, ref } from 'vue';
+import { useRoute, onBeforeRouteUpdate, RouteRecordRaw } from 'vue-router';
+import { storeToRefs } from 'pinia';
+import { useRoutesList } from '/@/stores/routesList';
+import { useThemeConfig } from '/@/stores/themeConfig';
+import other from '/@/utils/other';
+import mittBus from '/@/utils/mitt';
+
+// 引入组件
+const SubItem = defineAsyncComponent(() => import('/@/layout/navMenu/subItem.vue'));
+
+// 定义父组件传过来的值
+const props = defineProps({
+	// 菜单列表
+	menuList: {
+		type: Array<RouteRecordRaw>,
+		default: () => [],
+	},
+});
+
+// 定义变量内容
+const elMenuHorizontalScrollRef = ref();
+const stores = useRoutesList();
+const storesThemeConfig = useThemeConfig();
+const { routesList } = storeToRefs(stores);
+const { themeConfig } = storeToRefs(storesThemeConfig);
+const route = useRoute();
+const state = reactive({
+	defaultActive: '' as string | undefined,
+});
+
+// 获取父级菜单数据
+const menuLists = computed(() => {
+	return <RouteItems>props.menuList;
+});
+// 设置横向滚动条可以鼠标滚轮滚动
+const onElMenuHorizontalScroll = (e: WheelEventType) => {
+	const eventDelta = e.wheelDelta || -e.deltaY * 40;
+	elMenuHorizontalScrollRef.value.$refs.wrapRef.scrollLeft = elMenuHorizontalScrollRef.value.$refs.wrapRef.scrollLeft + eventDelta / 4;
+};
+// 初始化数据,页面刷新时,滚动条滚动到对应位置
+const initElMenuOffsetLeft = () => {
+	nextTick(() => {
+		let els = <HTMLElement>document.querySelector('.el-menu.el-menu--horizontal li.is-active');
+		if (!els) return false;
+		elMenuHorizontalScrollRef.value.$refs.wrapRef.scrollLeft = els.offsetLeft;
+	});
+};
+// 路由过滤递归函数
+const filterRoutesFun = <T extends RouteItem>(arr: T[]): T[] => {
+	return arr
+		.filter((item: T) => !item.meta?.isHide)
+		.map((item: T) => {
+			item = Object.assign({}, item);
+			if (item.children) item.children = filterRoutesFun(item.children);
+			return item;
+		});
+};
+// 传送当前子级数据到菜单中
+const setSendClassicChildren = (path: string) => {
+	const currentPathSplit = path.split('/');
+	let currentData: MittMenu = { children: [] };
+	filterRoutesFun(routesList.value).map((v, k) => {
+		if (v.path === `/${currentPathSplit[1]}`) {
+			v['k'] = k;
+			currentData['item'] = { ...v };
+			currentData['children'] = [{ ...v }];
+			if (v.children) currentData['children'] = v.children;
+		}
+	});
+	return currentData;
+};
+// 设置页面当前路由高亮
+const setCurrentRouterHighlight = (currentRoute: RouteToFrom) => {
+	const { path, meta } = currentRoute;
+	if (themeConfig.value.layout === 'classic') {
+		state.defaultActive = `/${path?.split('/')[1]}`;
+	} else {
+		const pathSplit = meta?.isDynamic ? meta.isDynamicPath!.split('/') : path!.split('/');
+		if (pathSplit.length >= 4 && meta?.isHide) state.defaultActive = pathSplit.splice(0, 3).join('/');
+		else state.defaultActive = path;
+	}
+};
+// 打开外部链接
+const onALinkClick = (val: RouteItem) => {
+	other.handleOpenLink(val);
+};
+// 页面加载前
+onBeforeMount(() => {
+	setCurrentRouterHighlight(route);
+});
+// 页面加载时
+onMounted(() => {
+	initElMenuOffsetLeft();
+});
+// 路由更新时
+onBeforeRouteUpdate((to) => {
+	// 修复:https://gitee.com/lyt-top/vue-next-admin/issues/I3YX6G
+	setCurrentRouterHighlight(to);
+	// 修复经典布局开启切割菜单时,点击tagsView后左侧导航菜单数据不变的问题
+	let { layout, isClassicSplitMenu } = themeConfig.value;
+	if (layout === 'classic' && isClassicSplitMenu) {
+		mittBus.emit('setSendClassicChildren', setSendClassicChildren(to.path));
+	}
+});
+</script>
+
+<style scoped lang="scss">
+.el-menu-horizontal-warp {
+	flex: 1;
+	overflow: hidden;
+	margin-right: 30px;
+	:deep(.el-scrollbar__bar.is-vertical) {
+		display: none;
+	}
+	:deep(a) {
+		width: 100%;
+	}
+	.el-menu.el-menu--horizontal {
+		display: flex;
+		height: 100%;
+		width: 100%;
+		box-sizing: border-box;
+	}
+}
+</style>
Added +49 -0
diff --git a/src/layout/navMenu/subItem.vue b/src/layout/navMenu/subItem.vue
new file mode 100644
index 0000000..9cc08b1
--- /dev/null
+++ b/src/layout/navMenu/subItem.vue
@@ -0,0 +1,49 @@
+<template>
+	<template v-for="val in chils">
+		<el-sub-menu :index="val.path" :key="val.path" v-if="val.children && val.children.length > 0">
+			<template #title>
+				<SvgIcon v-if="val.meta.icon" class="iconfont" :name="val.meta.icon" />
+				<span>{{ $t(val.meta.title) }}</span>
+			</template>
+			<sub-item :chil="val.children" />
+		</el-sub-menu>
+		<template v-else>
+			<el-menu-item :index="val.path" :key="val.path">
+				<template v-if="!val.meta.isLink || (val.meta.isLink && val.meta.isIframe)">
+					<SvgIcon v-if="val.meta.icon" class="iconfont" :name="val.meta.icon" />
+					<span>{{ $t(val.meta.title) }}</span>
+				</template>
+				<template v-else>
+					<a class="w100" @click.prevent="onALinkClick(val)">
+						<SvgIcon v-if="val.meta.icon" class="iconfont" :name="val.meta.icon" />
+						{{ $t(val.meta.title) }}
+					</a>
+				</template>
+			</el-menu-item>
+		</template>
+	</template>
+</template>
+
+<script setup lang="ts" name="navMenuSubItem">
+import { computed } from 'vue';
+import { RouteRecordRaw } from 'vue-router';
+import other from '/@/utils/other';
+
+// 定义父组件传过来的值
+const props = defineProps({
+	// 菜单列表
+	chil: {
+		type: Array<RouteRecordRaw>,
+		default: () => [],
+	},
+});
+
+// 获取父级菜单数据
+const chils = computed(() => {
+	return <RouteItems>props.chil;
+});
+// 打开外部链接
+const onALinkClick = (val: RouteItem) => {
+	other.handleOpenLink(val);
+};
+</script>
Added +102 -0
diff --git a/src/layout/navMenu/vertical.vue b/src/layout/navMenu/vertical.vue
new file mode 100644
index 0000000..25264c9
--- /dev/null
+++ b/src/layout/navMenu/vertical.vue
@@ -0,0 +1,102 @@
+<template>
+	<el-menu
+		router
+		:default-active="state.defaultActive"
+		background-color="transparent"
+		:collapse="state.isCollapse"
+		:unique-opened="getThemeConfig.isUniqueOpened"
+		:collapse-transition="false"
+	>
+		<template v-for="val in menuLists">
+			<el-sub-menu :index="val.path" v-if="val.children && val.children.length > 0" :key="val.path">
+				<template #title>
+					<SvgIcon v-if="val.meta.icon" class="iconfont" :name="val.meta.icon" />
+					<span>{{ $t(val.meta.title) }}</span>
+				</template>
+				<SubItem :chil="val.children" />
+			</el-sub-menu>
+			<template v-else>
+				<el-menu-item :index="val.path" :key="val.path">
+					<SvgIcon v-if="val.meta.icon" class="iconfont" :name="val.meta.icon" />
+					<template #title v-if="!val.meta.isLink || (val.meta.isLink && val.meta.isIframe)">
+						<span>{{ $t(val.meta.title) }}</span>
+					</template>
+					<template #title v-else>
+						<a class="w100" @click.prevent="onALinkClick(val)">{{ $t(val.meta.title) }}</a>
+					</template>
+				</el-menu-item>
+			</template>
+		</template>
+	</el-menu>
+</template>
+
+<script setup lang="ts" name="navMenuVertical">
+import { defineAsyncComponent, reactive, computed, onMounted, watch } from 'vue';
+import { useRoute, onBeforeRouteUpdate, RouteRecordRaw } from 'vue-router';
+import { storeToRefs } from 'pinia';
+import { useThemeConfig } from '/@/stores/themeConfig';
+import other from '/@/utils/other';
+
+// 引入组件
+const SubItem = defineAsyncComponent(() => import('/@/layout/navMenu/subItem.vue'));
+
+// 定义父组件传过来的值
+const props = defineProps({
+	// 菜单列表
+	menuList: {
+		type: Array<RouteRecordRaw>,
+		default: () => [],
+	},
+});
+
+// 定义变量内容
+const storesThemeConfig = useThemeConfig();
+const { themeConfig } = storeToRefs(storesThemeConfig);
+const route = useRoute();
+const state = reactive({
+	// 修复:https://gitee.com/lyt-top/vue-next-admin/issues/I3YX6G
+	defaultActive: route.meta.isDynamic ? route.meta.isDynamicPath : route.path,
+	isCollapse: false,
+});
+
+// 获取父级菜单数据
+const menuLists = computed(() => {
+	return <RouteItems>props.menuList;
+});
+// 获取布局配置信息
+const getThemeConfig = computed(() => {
+	return themeConfig.value;
+});
+// 菜单高亮(详情时,父级高亮)
+const setParentHighlight = (currentRoute: RouteToFrom) => {
+	const { path, meta } = currentRoute;
+	const pathSplit = meta?.isDynamic ? meta.isDynamicPath!.split('/') : path!.split('/');
+	if (pathSplit.length >= 4 && meta?.isHide) return pathSplit.splice(0, 3).join('/');
+	else return path;
+};
+// 打开外部链接
+const onALinkClick = (val: RouteItem) => {
+	other.handleOpenLink(val);
+};
+// 页面加载时
+onMounted(() => {
+	state.defaultActive = setParentHighlight(route);
+});
+// 路由更新时
+onBeforeRouteUpdate((to) => {
+	// 修复:https://gitee.com/lyt-top/vue-next-admin/issues/I3YX6G
+	state.defaultActive = setParentHighlight(to);
+	const clientWidth = document.body.clientWidth;
+	if (clientWidth < 1000) themeConfig.value.isCollapse = false;
+});
+// 设置菜单的收起/展开
+watch(
+	themeConfig.value,
+	() => {
+		document.body.clientWidth <= 1000 ? (state.isCollapse = false) : (state.isCollapse = themeConfig.value.isCollapse);
+	},
+	{
+		immediate: true,
+	}
+);
+</script>
Added +101 -0
diff --git a/src/layout/routerView/iframes.vue b/src/layout/routerView/iframes.vue
new file mode 100644
index 0000000..8e1f958
--- /dev/null
+++ b/src/layout/routerView/iframes.vue
@@ -0,0 +1,101 @@
+<template>
+	<div class="layout-padding layout-padding-unset layout-iframe">
+		<div class="layout-padding-auto layout-padding-view">
+			<div class="w100" v-for="v in setIframeList" :key="v.path" v-loading="v.meta.loading" element-loading-background="white">
+				<transition-group :name="name">
+					<iframe
+						:src="v.meta.isLink"
+						:key="v.path"
+						frameborder="0"
+						height="100%"
+						width="100%"
+						style="position: absolute"
+						:data-url="v.path"
+						v-show="getRoutePath === v.path"
+						ref="iframeRef"
+					/>
+				</transition-group>
+			</div>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts" name="layoutIframeView">
+import { computed, watch, ref, nextTick } from 'vue';
+import { useRoute } from 'vue-router';
+
+// 定义父组件传过来的值
+const props = defineProps({
+	// 刷新 iframe
+	refreshKey: {
+		type: String,
+		default: () => '',
+	},
+	// 过渡动画 name
+	name: {
+		type: String,
+		default: () => 'slide-right',
+	},
+	// iframe 列表
+	list: {
+		type: Array,
+		default: () => [],
+	},
+});
+
+// 定义变量内容
+const iframeRef = ref();
+const route = useRoute();
+
+// 处理 list 列表,当打开时,才进行加载
+const setIframeList = computed(() => {
+	return (<RouteItems>props.list).filter((v: RouteItem) => v.meta?.isIframeOpen);
+});
+// 获取 iframe 当前路由 path
+const getRoutePath = computed(() => {
+	return route.path;
+});
+// 关闭 iframe loading
+const closeIframeLoading = (val: string, item: RouteItem) => {
+	nextTick(() => {
+		if (!iframeRef.value) return false;
+		iframeRef.value.forEach((v: HTMLElement) => {
+			if (v.dataset.url === val) {
+				v.onload = () => {
+					if (item.meta?.isIframeOpen && item.meta.loading) item.meta.loading = false;
+				};
+			}
+		});
+	});
+};
+// 监听路由变化,初始化 iframe 数据,防止多个 iframe 时,切换不生效
+watch(
+	() => route.fullPath,
+	(val) => {
+		const item: any = props.list.find((v: any) => v.path === val);
+		if (!item) return false;
+		if (!item.meta.isIframeOpen) item.meta.isIframeOpen = true;
+		closeIframeLoading(val, item);
+	},
+	{
+		immediate: true,
+	}
+);
+// 监听 iframe refreshKey 变化,用于 tagsview 右键菜单刷新
+watch(
+	() => props.refreshKey,
+	() => {
+		const item: any = props.list.find((v: any) => v.path === route.path);
+		if (!item) return false;
+		if (item.meta.isIframeOpen) item.meta.isIframeOpen = false;
+		setTimeout(() => {
+			item.meta.isIframeOpen = true;
+			item.meta.loading = true;
+			closeIframeLoading(route.fullPath, item);
+		});
+	},
+	{
+		deep: true,
+	}
+);
+</script>
Added +93 -0
diff --git a/src/layout/routerView/link.vue b/src/layout/routerView/link.vue
new file mode 100644
index 0000000..ccdc3a4
--- /dev/null
+++ b/src/layout/routerView/link.vue
@@ -0,0 +1,93 @@
+<template>
+	<div class="layout-padding layout-link-container">
+		<div class="layout-padding-auto layout-padding-view">
+			<div class="layout-link-warp">
+				<i class="layout-link-icon iconfont icon-xingqiu"></i>
+				<div class="layout-link-msg">页面 "{{ $t(state.title) }}" 已在新窗口中打开</div>
+				<el-button class="mt30" round size="default" @click="onGotoFullPage">
+					<i class="iconfont icon-lianjie"></i>
+					<span>立即前往体验</span>
+				</el-button>
+			</div>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts" name="layoutLinkView">
+import { reactive, watch } from 'vue';
+import { useRoute } from 'vue-router';
+import { verifyUrl } from '/@/utils/toolsValidate';
+
+// 定义变量内容
+const route = useRoute();
+const state = reactive<LinkViewState>({
+	title: '',
+	isLink: '',
+});
+
+// 立即前往
+const onGotoFullPage = () => {
+	const { origin, pathname } = window.location;
+	if (verifyUrl(<string>state.isLink)) window.open(state.isLink);
+	else window.open(`${origin}${pathname}#${state.isLink}`);
+};
+// 监听路由的变化,设置内容
+watch(
+	() => route.path,
+	() => {
+		state.title = <string>route.meta.title;
+		state.isLink = <string>route.meta.isLink;
+	},
+	{
+		immediate: true,
+	}
+);
+</script>
+
+<style scoped lang="scss">
+.layout-link-container {
+	.layout-link-warp {
+		margin: auto;
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+		justify-content: center;
+		i.layout-link-icon {
+			position: relative;
+			font-size: 100px;
+			color: var(--el-color-primary);
+			&::after {
+				content: '';
+				position: absolute;
+				left: 50px;
+				top: 0;
+				width: 15px;
+				height: 100px;
+				background: linear-gradient(
+					rgba(255, 255, 255, 0.01),
+					rgba(255, 255, 255, 0.01),
+					rgba(255, 255, 255, 0.01),
+					rgba(255, 255, 255, 0.05),
+					rgba(255, 255, 255, 0.05),
+					rgba(255, 255, 255, 0.05),
+					rgba(235, 255, 255, 0.5),
+					rgba(255, 255, 255, 0.05),
+					rgba(255, 255, 255, 0.05),
+					rgba(255, 255, 255, 0.05),
+					rgba(255, 255, 255, 0.01),
+					rgba(255, 255, 255, 0.01),
+					rgba(255, 255, 255, 0.01)
+				);
+				transform: rotate(-15deg);
+				animation: toRight 5s linear infinite;
+			}
+		}
+		.layout-link-msg {
+			font-size: 12px;
+			color: var(--next-bg-topBarColor);
+			opacity: 0.7;
+			margin-top: 15px;
+		}
+	}
+}
+</style>
Added +108 -0
diff --git a/src/layout/routerView/parent.vue b/src/layout/routerView/parent.vue
new file mode 100644
index 0000000..4124f7f
--- /dev/null
+++ b/src/layout/routerView/parent.vue
@@ -0,0 +1,108 @@
+<template>
+	<div class="layout-parent">
+		<router-view v-slot="{ Component }">
+			<transition :name="setTransitionName" mode="out-in">
+				<keep-alive :include="getKeepAliveNames">
+					<component :is="Component" :key="state.refreshRouterViewKey" class="w100" v-show="!isIframePage" />
+				</keep-alive>
+			</transition>
+		</router-view>
+		<transition :name="setTransitionName" mode="out-in">
+			<Iframes class="w100" v-show="isIframePage" :refreshKey="state.iframeRefreshKey" :name="setTransitionName" :list="state.iframeList" />
+		</transition>
+	</div>
+</template>
+
+<script setup lang="ts" name="layoutParentView">
+import { defineAsyncComponent, computed, reactive, onBeforeMount, onUnmounted, nextTick, watch, onMounted } from 'vue';
+import { useRoute, useRouter } from 'vue-router';
+import { storeToRefs } from 'pinia';
+import { useKeepALiveNames } from '/@/stores/keepAliveNames';
+import { useThemeConfig } from '/@/stores/themeConfig';
+import { Session } from '/@/utils/storage';
+import mittBus from '/@/utils/mitt';
+
+// 引入组件
+const Iframes = defineAsyncComponent(() => import('/@/layout/routerView/iframes.vue'));
+
+// 定义变量内容
+const route = useRoute();
+const router = useRouter();
+const storesKeepAliveNames = useKeepALiveNames();
+const storesThemeConfig = useThemeConfig();
+const { keepAliveNames, cachedViews } = storeToRefs(storesKeepAliveNames);
+const { themeConfig } = storeToRefs(storesThemeConfig);
+const state = reactive<ParentViewState>({
+	refreshRouterViewKey: '', // 非 iframe tagsview 右键菜单刷新时
+	iframeRefreshKey: '', // iframe tagsview 右键菜单刷新时
+	keepAliveNameList: [],
+	iframeList: [],
+});
+
+// 设置主界面切换动画
+const setTransitionName = computed(() => {
+	return themeConfig.value.animation;
+});
+// 获取组件缓存列表(name值)
+const getKeepAliveNames = computed(() => {
+	return themeConfig.value.isTagsview ? cachedViews.value : state.keepAliveNameList;
+});
+// 设置 iframe 显示/隐藏
+const isIframePage = computed(() => {
+	return route.meta.isIframe;
+});
+// 获取 iframe 组件列表(未进行渲染)
+const getIframeListRoutes = async () => {
+	router.getRoutes().forEach((v) => {
+		if (v.meta.isIframe) {
+			v.meta.isIframeOpen = false;
+			v.meta.loading = true;
+			state.iframeList.push({ ...v });
+		}
+	});
+};
+// 页面加载前,处理缓存,页面刷新时路由缓存处理
+onBeforeMount(() => {
+	state.keepAliveNameList = keepAliveNames.value;
+	mittBus.on('onTagsViewRefreshRouterView', (fullPath: string) => {
+		state.keepAliveNameList = keepAliveNames.value.filter((name: string) => route.name !== name);
+		state.refreshRouterViewKey = '';
+		state.iframeRefreshKey = '';
+		nextTick(() => {
+			state.refreshRouterViewKey = fullPath;
+			state.iframeRefreshKey = fullPath;
+			state.keepAliveNameList = keepAliveNames.value;
+		});
+	});
+});
+// 页面加载时
+onMounted(() => {
+	getIframeListRoutes();
+	// https://gitee.com/lyt-top/vue-next-admin/issues/I58U75
+	// https://gitee.com/lyt-top/vue-next-admin/issues/I59RXK
+	// https://gitee.com/lyt-top/vue-next-admin/pulls/40
+	nextTick(() => {
+		setTimeout(() => {
+			if (themeConfig.value.isCacheTagsView) {
+				let tagsViewArr: RouteItem[] = Session.get('tagsViewList') || [];
+				cachedViews.value = tagsViewArr.filter((item) => item.meta?.isKeepAlive).map((item) => item.name as string);
+			}
+		}, 0);
+	});
+});
+// 页面卸载时
+onUnmounted(() => {
+	mittBus.off('onTagsViewRefreshRouterView', () => {});
+});
+// 监听路由变化,防止 tagsView 多标签时,切换动画消失
+// https://toscode.gitee.com/lyt-top/vue-next-admin/pulls/38/files
+watch(
+	() => route.fullPath,
+	() => {
+		state.refreshRouterViewKey = decodeURI(route.fullPath);
+	},
+	{
+		immediate: true,
+	}
+);
+</script>
Added +108 -0
diff --git a/src/layout/sponsors/index.vue b/src/layout/sponsors/index.vue
new file mode 100644
index 0000000..6f85895
--- /dev/null
+++ b/src/layout/sponsors/index.vue
@@ -0,0 +1,108 @@
+<template>
+	<div class="sponsors-container" title="点击前往体验" v-show="state.sponsors.isShow" @click="onSponsorsClick">
+		<el-carousel height="240px" indicator-position="none" :arrow="setCarouselShow" @change="onCarouselChange">
+			<el-carousel-item v-for="(v, k) in state.sponsors.list" :key="k">
+				<img :src="v.url" class="sponsors-img" />
+				<div class="sponsors-text" v-html="v.text"></div>
+			</el-carousel-item>
+		</el-carousel>
+		<div class="sponsors-close">
+			<SvgIcon name="ele-Close" :size="12" title="关闭赞助商" @click.stop="onCloseSponsors" />
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts" name="layoutSponsors">
+import { reactive, computed, onMounted } from 'vue';
+import sponsorsOne from '/@/assets/ccflowRightNextAdmin.png';
+
+// 定义变量内容
+const state = reactive({
+	sponsors: {
+		list: [
+			{
+				url: sponsorsOne,
+				text: `驰骋BPM系统包含表单引擎+流程引擎+权限控制,方便集成,配置灵活,功能强大,适合中国国情的工作流引擎.演示:http://demo.ccflow.org。右上角点star方可加群: 1060674395`,
+				link: 'http://www.ccflow.org/',
+			},
+		],
+		isShow: false,
+		index: 0,
+	},
+});
+
+// 设置轮播图箭头显示
+const setCarouselShow = computed(() => {
+	return state.sponsors.list.length <= 1 ? 'never' : 'hover';
+});
+// 关闭赞助商
+const onCloseSponsors = () => {
+	state.sponsors.isShow = false;
+};
+// 轮播图改变时
+const onCarouselChange = (e: number) => {
+	state.sponsors.index = e;
+};
+// 当前项内容点击
+const onSponsorsClick = () => {
+	window.open(state.sponsors.list[state.sponsors.index].link);
+};
+// 延迟显示,防止影响其它界面加载
+const delayShow = () => {
+	setTimeout(() => {
+		state.sponsors.isShow = true;
+	}, 3000);
+};
+// 页面加载时
+onMounted(() => {
+	delayShow();
+});
+</script>
+
+<style scoped lang="scss">
+.sponsors-container {
+	position: fixed;
+	right: 15px;
+	bottom: 15px;
+	z-index: 3;
+	width: 200px;
+	background-color: var(--next-bg-main-color);
+	box-shadow: var(--el-box-shadow-lighter);
+	border-radius: 5px;
+	overflow: hidden;
+	cursor: pointer;
+	.sponsors-img {
+		width: 100%;
+		height: 80px;
+	}
+	.sponsors-text {
+		padding: 10px;
+		color: var(--el-text-color-regular);
+		font-size: var(--el-dialog-content-font-size);
+	}
+	.sponsors-close {
+		width: 60px;
+		height: 60px;
+		border-radius: 100%;
+		background: rgba(0, 0, 0, 0.05);
+		transition: all 0.3s ease;
+		position: absolute;
+		right: -35px;
+		bottom: -35px;
+		:deep(i) {
+			position: absolute;
+			left: 9px;
+			top: 9px;
+			color: #afafaf;
+			transition: all 0.3s ease;
+		}
+		&:hover {
+			transition: all 0.3s ease;
+			:deep(i) {
+				color: var(--el-color-primary);
+				transition: all 0.3s ease;
+			}
+		}
+	}
+}
+</style>
Added +151 -0
diff --git a/src/layout/upgrade/index.vue b/src/layout/upgrade/index.vue
new file mode 100644
index 0000000..6dd350d
--- /dev/null
+++ b/src/layout/upgrade/index.vue
@@ -0,0 +1,151 @@
+<template>
+	<div class="upgrade-dialog">
+		<el-dialog
+			v-model="state.isUpgrade"
+			width="300px"
+			destroy-on-close
+			:show-close="false"
+			:close-on-click-modal="false"
+			:close-on-press-escape="false"
+		>
+			<div class="upgrade-title">
+				<div class="upgrade-title-warp">
+					<span class="upgrade-title-warp-txt">{{ $t('message.upgrade.title') }}</span>
+					<span class="upgrade-title-warp-version">v{{ state.version }}</span>
+				</div>
+			</div>
+			<div class="upgrade-content">
+				{{ getThemeConfig.globalTitle }} {{ $t('message.upgrade.msg') }}
+				<div class="mt5">
+					<el-link type="primary" class="font12" href="https://gitee.com/lyt-top/vue-next-admin/blob/master/CHANGELOG.md" target="_black">
+						CHANGELOG.md
+					</el-link>
+				</div>
+				<div class="upgrade-content-desc mt5">{{ $t('message.upgrade.desc') }}</div>
+			</div>
+			<div class="upgrade-btn">
+				<el-button round size="default" type="info" text @click="onCancel">{{ $t('message.upgrade.btnOne') }}</el-button>
+				<el-button type="primary" round size="default" @click="onUpgrade" :loading="state.isLoading">{{ state.btnTxt }}</el-button>
+			</div>
+		</el-dialog>
+	</div>
+</template>
+
+<script setup lang="ts" name="layoutUpgrade">
+import { reactive, computed, onMounted } from 'vue';
+import { useI18n } from 'vue-i18n';
+import { storeToRefs } from 'pinia';
+import { useThemeConfig } from '/@/stores/themeConfig';
+import { Local } from '/@/utils/storage';
+
+// 定义变量内容
+const { t } = useI18n();
+const storesThemeConfig = useThemeConfig();
+const { themeConfig } = storeToRefs(storesThemeConfig);
+const state = reactive({
+	isUpgrade: false,
+	// @ts-ignore
+	version: __NEXT_VERSION__,
+	isLoading: false,
+	btnTxt: '',
+});
+
+// 获取布局配置信息
+const getThemeConfig = computed(() => {
+	return themeConfig.value;
+});
+// 残忍拒绝
+const onCancel = () => {
+	state.isUpgrade = false;
+};
+// 马上更新
+const onUpgrade = () => {
+	state.isLoading = true;
+	state.btnTxt = t('message.upgrade.btnTwoLoading');
+	setTimeout(() => {
+		Local.clear();
+		window.location.reload();
+		Local.set('version', state.version);
+	}, 2000);
+};
+// 延迟显示,防止刷新时界面显示太快
+const delayShow = () => {
+	setTimeout(() => {
+		state.isUpgrade = true;
+	}, 2000);
+};
+// 页面加载时
+onMounted(() => {
+	delayShow();
+	setTimeout(() => {
+		state.btnTxt = t('message.upgrade.btnTwo');
+	}, 200);
+});
+</script>
+
+<style scoped lang="scss">
+.upgrade-dialog {
+	:deep(.el-dialog) {
+		.el-dialog__body {
+			padding: 0 !important;
+		}
+		.el-dialog__header {
+			display: none !important;
+		}
+		.upgrade-title {
+			text-align: center;
+			height: 130px;
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			position: relative;
+			&::after {
+				content: '';
+				position: absolute;
+				background-color: var(--el-color-primary-light-1);
+				width: 130%;
+				height: 130px;
+				border-bottom-left-radius: 100%;
+				border-bottom-right-radius: 100%;
+			}
+			.upgrade-title-warp {
+				z-index: 1;
+				position: relative;
+				.upgrade-title-warp-txt {
+					color: var(--next-color-white);
+					font-size: 22px;
+					letter-spacing: 3px;
+				}
+				.upgrade-title-warp-version {
+					color: var(--next-color-white);
+					background-color: var(--el-color-primary-light-4);
+					font-size: 12px;
+					position: absolute;
+					display: flex;
+					top: -2px;
+					right: -50px;
+					padding: 2px 4px;
+					border-radius: 2px;
+				}
+			}
+		}
+		.upgrade-content {
+			padding: 20px;
+			line-height: 22px;
+			.upgrade-content-desc {
+				color: var(--el-color-info-light-5);
+				font-size: 12px;
+			}
+		}
+		.upgrade-btn {
+			border-top: 1px solid var(--el-border-color-lighter, #ebeef5);
+			display: flex;
+			justify-content: space-around;
+			padding: 15px 20px;
+			.el-button {
+				width: 100%;
+			}
+		}
+	}
+}
+</style>
Added +26 -0
diff --git a/src/main.ts b/src/main.ts
new file mode 100644
index 0000000..3561d5c
--- /dev/null
+++ b/src/main.ts
@@ -0,0 +1,26 @@
+import { createApp } from 'vue';
+import pinia from '/@/stores/index';
+import App from './App.vue';
+import router from './router';
+import { directive } from '/@/directive/index';
+import { i18n } from '/@/i18n/index';
+import other from '/@/utils/other';
+
+import ElementPlus from 'element-plus';
+import 'element-plus/dist/index.css';
+import '/@/theme/index.scss';
+import '/@/theme/tailwind.scss';
+import VueGridLayout from 'vue-grid-layout';
+import { Icon } from '@iconify/vue';
+import formCreate from '@form-create/element-ui';
+import install from '@form-create/element-ui/auto-import';
+import page from '/@/components/page/index.vue';
+
+formCreate.use(install);
+const app = createApp(App);
+
+directive(app);
+other.elSvg(app);
+app.component('Icon', Icon);
+app.component('Page', page);
+app.use(pinia).use(router).use(ElementPlus, { i18n: i18n.global.t }).use(i18n).use(VueGridLayout).use(formCreate).mount('#app');
Added +22 -0
diff --git a/src/model/api/common.ts b/src/model/api/common.ts
new file mode 100644
index 0000000..7465214
--- /dev/null
+++ b/src/model/api/common.ts
@@ -0,0 +1,22 @@
+export interface ApiResult<T> {
+  code: number;
+  data: T,
+  message: string;
+  page: Page;
+}
+
+// export interface ApiPagerResult<T> extends ApiResult<T> {
+//   pager: Pager;
+// }
+
+export interface PageProps {
+  pageIndex?: number;
+  pageSize?: number;
+}
+
+export interface Page {
+  pageIndex: number;
+  pageSize: number;
+  totalCount: number;
+  longTotalCount: string;
+}
Added +0 -0
diff --git a/src/model/api/login.ts b/src/model/api/login.ts
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/model/api/login.ts
Added +21 -0
diff --git a/src/model/api/menu.ts b/src/model/api/menu.ts
new file mode 100644
index 0000000..f82f337
--- /dev/null
+++ b/src/model/api/menu.ts
@@ -0,0 +1,21 @@
+export interface Menu {
+  id: number;
+  name: string;
+  displayName: string;
+  parentID: number;
+  url: string;
+  icon?: string;
+  visible: boolean;
+  newWindow: boolean;
+  permissions: Permissions;
+  children: Menu[];
+}
+
+interface Permissions {
+  '1'?: string;
+  '2'?: string;
+  '4'?: string;
+  '8'?: string;
+  '16'?: string;
+  '32'?: string;
+}
Added +16 -0
diff --git a/src/model/api/page.ts b/src/model/api/page.ts
new file mode 100644
index 0000000..df0afb7
--- /dev/null
+++ b/src/model/api/page.ts
@@ -0,0 +1,16 @@
+export interface Column {
+  name: string;
+  displayName: string;
+  description: string;
+  category: string;
+  typeName: string;
+  itemType?: any;
+  length: number;
+  precision: number;
+  scale: number;
+  nullable: boolean;
+  primaryKey: boolean;
+  readonly: boolean;
+  mapField: string;
+  groupView?: any;
+}
\ No newline at end of file
Added +52 -0
diff --git a/src/model/api/user.ts b/src/model/api/user.ts
new file mode 100644
index 0000000..8b96648
--- /dev/null
+++ b/src/model/api/user.ts
@@ -0,0 +1,52 @@
+export interface UserInfo {
+  id: number;
+  name: string;
+  password: string;
+  displayName: string;
+  sex: string;
+  mail: string;
+  mobile: string;
+  code?: any;
+  avatar: string;
+  roleID: number;
+  roleIds?: any;
+  roleName: string;
+  roleNames: string;
+  departmentID: number;
+  online: boolean;
+  enable: boolean;
+  logins: number;
+  lastLogin: string;
+  lastLoginIP: string;
+  registerTime: string;
+  registerIP?: any;
+  ex1: number;
+  ex2: number;
+  ex3: number;
+  ex4?: any;
+  ex5?: any;
+  ex6?: any;
+  updateUser?: any;
+  updateUserID: number;
+  updateIP: string;
+  updateTime: string;
+  remark?: any;
+  permission: string;
+}
+
+export type Login = {
+  token: string;
+}
+
+export interface GetMenuTreeItem {
+  children: GetMenuTreeItem[]
+  displayName: string;
+  icon: string;
+  id: number;
+  name: string;
+  newWindow: boolean;
+  parentID: number;
+  permissions: Object;
+  url: string;
+  visible: boolean;
+}
Added +253 -0
diff --git a/src/router/backEnd.ts b/src/router/backEnd.ts
new file mode 100644
index 0000000..c49358a
--- /dev/null
+++ b/src/router/backEnd.ts
@@ -0,0 +1,253 @@
+import { RouteRecordRaw } from 'vue-router';
+import { storeToRefs } from 'pinia';
+import pinia from '/@/stores/index';
+import { useUserInfo } from '/@/stores/userInfo';
+import { useRequestOldRoutes } from '/@/stores/requestOldRoutes';
+import { Session } from '/@/utils/storage';
+import { NextLoading } from '/@/utils/loading';
+import { dynamicRoutes, notFoundAndNoPower } from '/@/router/route';
+import { formatTwoStageRoutes, formatFlatteningRoutes, router } from '/@/router/index';
+import { useRoutesList } from '/@/stores/routesList';
+import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
+import { useMenuApi } from '../api/menu';
+import { Menu } from '../model/api/menu';
+import { toCamelCase } from '../utils/other';
+import { Component, h, shallowRef } from 'vue';
+
+
+// 后端控制路由
+
+// 引入 api 请求接口
+const menuApi = useMenuApi();
+
+/**
+ * 获取目录下的 .vue、.tsx 全部文件
+ * @method import.meta.glob
+ * @link 参考:https://cn.vitejs.dev/guide/features.html#json
+ */
+// const layouModules: any = import.meta.glob('../layout/routerView/*.{vue,tsx}');
+// const viewsModules: any = import.meta.glob('../views/**/*.{vue,tsx}');
+// const dynamicViewsModules: Record<string, Function> = Object.assign({}, { ...layouModules }, { ...viewsModules });
+const modules = import.meta.glob('../views/modules/**/**.vue')
+
+/**
+ * 后端控制路由:初始化方法,防止刷新时路由丢失
+ * @method NextLoading 界面 loading 动画开始执行
+ * @method useUserInfo().setUserInfos() 触发初始化用户信息 pinia
+ * @method useRequestOldRoutes().setRequestOldRoutes() 存储接口原始路由(未处理component),根据需求选择使用
+ * @method setAddRoute 添加动态路由
+ * @method setFilterMenuAndCacheTagsViewRoutes 设置路由到 pinia routesList 中(已处理成多级嵌套路由)及缓存多级嵌套数组处理后的一维数组
+ */
+export async function initBackEndControlRoutes() {
+	// 界面 loading 动画开始执行
+	if (window.nextLoading === undefined) NextLoading.start();
+	// 无 token 停止执行下一步
+	if (!Session.get('token')) return false;
+	// 触发初始化用户信息 pinia
+	// https://gitee.com/lyt-top/vue-next-admin/issues/I5F1HP
+	await useUserInfo().setUserInfos();
+	// 获取路由菜单数据
+	const res = await menuApi.getMenu();
+	// 无登录权限时,添加判断
+	// https://gitee.com/lyt-top/vue-next-admin/issues/I64HVO
+	if (res.data.length <= 0) return Promise.resolve(true);
+	// 存储接口原始路由(未处理component),根据需求选择使用
+	useRequestOldRoutes().setRequestOldRoutes(JSON.parse(JSON.stringify(res.data)));
+	// 处理路由(component),替换 dynamicRoutes(/@/router/route)第一个顶级 children 的路由
+	dynamicRoutes[0].children = [
+		{
+			path: '/home',
+			name: 'home',
+			component: () => import('/@/views/home/index.vue'),
+			meta: {
+				title: 'message.router.home',
+				isLink: '',
+				isHide: false,
+				isKeepAlive: true,
+				isAffix: true,
+				isIframe: false,
+				roles: ['admin', 'common'],
+				icon: 'fa:home',
+			},
+		},
+		{
+			path: '/personal',
+			name: 'personal',
+			component: () => import('/@/views/personal/index.vue'),
+			meta: {
+				title: 'message.router.personal',
+				isLink: '',
+				isHide: true,
+				isKeepAlive: true,
+				isAffix: false,
+				isIframe: false,
+				roles: ['admin', 'common'],
+				icon: 'iconfont icon-gerenzhongxin',
+			},
+		},
+		...await backEndComponent(res.data)
+	];
+	// 添加动态路由
+	await setAddRoute();
+	// 设置路由到 pinia routesList 中(已处理成多级嵌套路由)及缓存多级嵌套数组处理后的一维数组
+	await setFilterMenuAndCacheTagsViewRoutes();
+}
+
+/**
+ * 设置路由到 pinia routesList 中(已处理成多级嵌套路由)及缓存多级嵌套数组处理后的一维数组
+ * @description 用于左侧菜单、横向菜单的显示
+ * @description 用于 tagsView、菜单搜索中:未过滤隐藏的(isHide)
+ */
+export async function setFilterMenuAndCacheTagsViewRoutes() {
+	const storesRoutesList = useRoutesList(pinia);
+	storesRoutesList.setRoutesList(dynamicRoutes[0].children as any);
+	setCacheTagsViewRoutes();
+}
+
+/**
+ * 缓存多级嵌套数组处理后的一维数组
+ * @description 用于 tagsView、菜单搜索中:未过滤隐藏的(isHide)
+ */
+export function setCacheTagsViewRoutes() {
+	const storesTagsView = useTagsViewRoutes(pinia);
+	storesTagsView.setTagsViewRoutes(formatTwoStageRoutes(formatFlatteningRoutes(dynamicRoutes))[0].children);
+}
+
+/**
+ * 处理路由格式及添加捕获所有路由或 404 Not found 路由
+ * @description 替换 dynamicRoutes(/@/router/route)第一个顶级 children 的路由
+ * @returns 返回替换后的路由数组
+ */
+export function setFilterRouteEnd() {
+	let filterRouteEnd: any = formatTwoStageRoutes(formatFlatteningRoutes(dynamicRoutes));
+	// notFoundAndNoPower 防止 404、401 不在 layout 布局中,不设置的话,404、401 界面将全屏显示
+	// 关联问题 No match found for location with path 'xxx'
+	filterRouteEnd[0].children = [...filterRouteEnd[0].children, ...notFoundAndNoPower];
+	return filterRouteEnd;
+}
+
+/**
+ * 添加动态路由
+ * @method router.addRoute
+ * @description 此处循环为 dynamicRoutes(/@/router/route)第一个顶级 children 的路由一维数组,非多级嵌套
+ * @link 参考:https://next.router.vuejs.org/zh/api/#addroute
+ */
+export async function setAddRoute() {
+	await setFilterRouteEnd().forEach((route: RouteRecordRaw) => {
+		router.addRoute(route);
+	});
+}
+
+/**
+ * 请求后端路由菜单接口
+ * @description isRequestRoutes 为 true,则开启后端控制路由
+ * @returns 返回后端路由菜单数据
+ */
+export function getBackEndControlRoutes() {
+	// 模拟 admin 与 test
+	const stores = useUserInfo(pinia);
+	const { userInfos } = storeToRefs(stores);
+	const auth = userInfos.value.roles[0];
+	// 管理员 admin
+	if (auth === 'admin') return menuApi.getAdminMenu();
+	// 其它用户 test
+	else return menuApi.getTestMenu();
+}
+
+/**
+ * 将指定组件设置自定义名称
+ *
+ * @param {String} name 组件自定义名称
+ * @param {Component | Promise<Component>} component
+ * @return {Component}
+ */
+ export function createCustomComponent (name: string, component: Component) {
+  return {
+    name,
+		setup () {
+			const component = shallowRef();
+			return {
+				component
+			}
+		},
+    async created () {
+      if (component instanceof Promise) {
+        try {
+          const module = await component
+          this.component = module?.default
+        } catch (error) {
+          // console.error(`can not resolve component ${name}, error:`, error)
+        }
+        return
+      }
+      this.component = component
+    },
+    render () {
+      return this.component ? h(this.component) : null
+    },
+  } as Component
+}
+
+
+/**
+ * 重新请求后端路由菜单接口
+ * @description 用于菜单管理界面刷新菜单(未进行测试)
+ * @description 路径:/src/views/system/menu/component/addMenu.vue
+ */
+export async function setBackEndControlRefreshRoutes() {
+	await getBackEndControlRoutes();
+}
+
+/**
+ * 后端路由 component 转换
+ * @param routes 后端返回的路由表数组
+ * @returns 返回处理成函数后的 component
+ */
+export function backEndComponent(routes: Menu[]): Array<RouteRecordRaw> {
+	if (!routes) return [];
+	return routes.map((item) => {
+		// if (item.component) item.component = dynamicImport(dynamicViewsModules, item.component as string);
+		// item.children && backEndComponent(item.children); 
+		const url = item.url.split('/').map(v => toCamelCase(v)).join('/')
+		const component = modules[`../views/modules${url}.vue`] || modules[`../views/modules${url}/index.vue`]
+		return {
+			path: url,
+			name: item.name,
+			// component: component || dynamicImport(dynamicViewsModules, 'modules/index') as never,
+			component: component || createCustomComponent(item.name, import('../views/modules/index.vue')),
+			props: { type: url },
+			meta: {
+				title: item.displayName,
+				isLink: "",
+				isHide: false,
+				isKeepAlive: true,
+				isAffix: false,
+				isIframe: item.newWindow,
+				// roles
+				icon: item.icon ? item.icon.replace(/^fa-/, 'fa:') : ''
+			},
+			children: backEndComponent(item.children)
+		};
+	});
+}
+
+/**
+ * 后端路由 component 转换函数
+ * @param dynamicViewsModules 获取目录下的 .vue、.tsx 全部文件
+ * @param component 当前要处理项 component
+ * @returns 返回处理成函数后的 component
+ */
+export function dynamicImport(dynamicViewsModules: Record<string, Function>, component: string) {
+	const keys = Object.keys(dynamicViewsModules);
+	const matchKeys = keys.filter((key) => {
+		const k = key.replace(/..\/views|../, '');
+		return k.startsWith(`${component}`) || k.startsWith(`/${component}`);
+	});
+	if (matchKeys?.length === 1) {
+		const matchKey = matchKeys[0];
+		return dynamicViewsModules[matchKey];
+	}
+	if (matchKeys?.length > 1) {
+		return false;
+	}
+}
Added +153 -0
diff --git a/src/router/frontEnd.ts b/src/router/frontEnd.ts
new file mode 100644
index 0000000..21337b6
--- /dev/null
+++ b/src/router/frontEnd.ts
@@ -0,0 +1,153 @@
+import { RouteRecordRaw } from 'vue-router';
+import { storeToRefs } from 'pinia';
+import { formatTwoStageRoutes, formatFlatteningRoutes, router } from '/@/router/index';
+import { dynamicRoutes, notFoundAndNoPower } from '/@/router/route';
+import pinia from '/@/stores/index';
+import { Session } from '/@/utils/storage';
+import { useUserInfo } from '/@/stores/userInfo';
+import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
+import { useRoutesList } from '/@/stores/routesList';
+import { NextLoading } from '/@/utils/loading';
+
+// 前端控制路由
+
+/**
+ * 前端控制路由:初始化方法,防止刷新时路由丢失
+ * @method  NextLoading 界面 loading 动画开始执行
+ * @method useUserInfo(pinia).setUserInfos() 触发初始化用户信息 pinia
+ * @method setAddRoute 添加动态路由
+ * @method setFilterMenuAndCacheTagsViewRoutes 设置递归过滤有权限的路由到 pinia routesList 中(已处理成多级嵌套路由)及缓存多级嵌套数组处理后的一维数组
+ */
+export async function initFrontEndControlRoutes() {
+	// 界面 loading 动画开始执行
+	if (window.nextLoading === undefined) NextLoading.start();
+	// 无 token 停止执行下一步
+	if (!Session.get('token')) return false;
+	// 触发初始化用户信息 pinia
+	// https://gitee.com/lyt-top/vue-next-admin/issues/I5F1HP
+	await useUserInfo(pinia).setUserInfos();
+	// 无登录权限时,添加判断
+	// https://gitee.com/lyt-top/vue-next-admin/issues/I64HVO
+	if (useUserInfo().userInfos.roles.length <= 0) return Promise.resolve(true);
+	// 添加动态路由
+	await setAddRoute();
+	// 设置递归过滤有权限的路由到 pinia routesList 中(已处理成多级嵌套路由)及缓存多级嵌套数组处理后的一维数组
+	await setFilterMenuAndCacheTagsViewRoutes();
+}
+
+/**
+ * 添加动态路由
+ * @method router.addRoute
+ * @description 此处循环为 dynamicRoutes(/@/router/route)第一个顶级 children 的路由一维数组,非多级嵌套
+ * @link 参考:https://next.router.vuejs.org/zh/api/#addroute
+ */
+export async function setAddRoute() {
+	await setFilterRouteEnd().forEach((route: RouteRecordRaw) => {
+		router.addRoute(route);
+	});
+}
+
+/**
+ * 删除/重置路由
+ * @method router.removeRoute
+ * @description 此处循环为 dynamicRoutes(/@/router/route)第一个顶级 children 的路由一维数组,非多级嵌套
+ * @link 参考:https://next.router.vuejs.org/zh/api/#push
+ */
+export async function frontEndsResetRoute() {
+	await setFilterRouteEnd().forEach((route: RouteRecordRaw) => {
+		const routeName: any = route.name;
+		router.hasRoute(routeName) && router.removeRoute(routeName);
+	});
+}
+
+/**
+ * 获取有当前用户权限标识的路由数组,进行对原路由的替换
+ * @description 替换 dynamicRoutes(/@/router/route)第一个顶级 children 的路由
+ * @returns 返回替换后的路由数组
+ */
+export function setFilterRouteEnd() {
+	let filterRouteEnd: any = formatTwoStageRoutes(formatFlatteningRoutes(dynamicRoutes));
+	// notFoundAndNoPower 防止 404、401 不在 layout 布局中,不设置的话,404、401 界面将全屏显示
+	// 关联问题 No match found for location with path 'xxx'
+	filterRouteEnd[0].children = [...setFilterRoute(filterRouteEnd[0].children), ...notFoundAndNoPower];
+	return filterRouteEnd;
+}
+
+/**
+ * 获取当前用户权限标识去比对路由表(未处理成多级嵌套路由)
+ * @description 这里主要用于动态路由的添加,router.addRoute
+ * @link 参考:https://next.router.vuejs.org/zh/api/#addroute
+ * @param chil dynamicRoutes(/@/router/route)第一个顶级 children 的下路由集合
+ * @returns 返回有当前用户权限标识的路由数组
+ */
+export function setFilterRoute(chil: any) {
+	const stores = useUserInfo(pinia);
+	const { userInfos } = storeToRefs(stores);
+	let filterRoute: any = [];
+	chil.forEach((route: any) => {
+		if (route.meta.roles) {
+			route.meta.roles.forEach((metaRoles: any) => {
+				userInfos.value.roles.forEach((roles: any) => {
+					if (metaRoles === roles) filterRoute.push({ ...route });
+				});
+			});
+		}
+	});
+	return filterRoute;
+}
+
+/**
+ * 缓存多级嵌套数组处理后的一维数组
+ * @description 用于 tagsView、菜单搜索中:未过滤隐藏的(isHide)
+ */
+export function setCacheTagsViewRoutes() {
+	// 获取有权限的路由,否则 tagsView、菜单搜索中无权限的路由也将显示
+	const stores = useUserInfo(pinia);
+	const storesTagsView = useTagsViewRoutes(pinia);
+	const { userInfos } = storeToRefs(stores);
+	let rolesRoutes = setFilterHasRolesMenu(dynamicRoutes, userInfos.value.roles);
+	// 添加到 pinia setTagsViewRoutes 中
+	storesTagsView.setTagsViewRoutes(formatTwoStageRoutes(formatFlatteningRoutes(rolesRoutes))[0].children);
+}
+
+/**
+ * 设置递归过滤有权限的路由到 pinia routesList 中(已处理成多级嵌套路由)及缓存多级嵌套数组处理后的一维数组
+ * @description 用于左侧菜单、横向菜单的显示
+ * @description 用于 tagsView、菜单搜索中:未过滤隐藏的(isHide)
+ */
+export function setFilterMenuAndCacheTagsViewRoutes() {
+	const stores = useUserInfo(pinia);
+	const storesRoutesList = useRoutesList(pinia);
+	const { userInfos } = storeToRefs(stores);
+	storesRoutesList.setRoutesList(setFilterHasRolesMenu(dynamicRoutes[0].children, userInfos.value.roles));
+	setCacheTagsViewRoutes();
+}
+
+/**
+ * 判断路由 `meta.roles` 中是否包含当前登录用户权限字段
+ * @param roles 用户权限标识,在 userInfos(用户信息)的 roles(登录页登录时缓存到浏览器)数组
+ * @param route 当前循环时的路由项
+ * @returns 返回对比后有权限的路由项
+ */
+export function hasRoles(roles: any, route: any) {
+	if (route.meta && route.meta.roles) return roles.some((role: any) => route.meta.roles.includes(role));
+	else return true;
+}
+
+/**
+ * 获取当前用户权限标识去比对路由表,设置递归过滤有权限的路由
+ * @param routes 当前路由 children
+ * @param roles 用户权限标识,在 userInfos(用户信息)的 roles(登录页登录时缓存到浏览器)数组
+ * @returns 返回有权限的路由数组 `meta.roles` 中控制
+ */
+export function setFilterHasRolesMenu(routes: any, roles: any) {
+	const menu: any = [];
+	routes.forEach((route: any) => {
+		const item = { ...route };
+		if (hasRoles(roles, item)) {
+			if (item.children) item.children = setFilterHasRolesMenu(item.children, roles);
+			menu.push(item);
+		}
+	});
+	return menu;
+}
Added +137 -0
diff --git a/src/router/index.ts b/src/router/index.ts
new file mode 100644
index 0000000..a92deab
--- /dev/null
+++ b/src/router/index.ts
@@ -0,0 +1,137 @@
+import { createRouter, createWebHashHistory } from 'vue-router';
+import NProgress from 'nprogress';
+import 'nprogress/nprogress.css';
+import pinia from '/@/stores/index';
+import { storeToRefs } from 'pinia';
+import { useKeepALiveNames } from '/@/stores/keepAliveNames';
+import { useRoutesList } from '/@/stores/routesList';
+import { useThemeConfig } from '/@/stores/themeConfig';
+import { Session } from '/@/utils/storage';
+import { staticRoutes, notFoundAndNoPower } from '/@/router/route';
+import { initFrontEndControlRoutes } from '/@/router/frontEnd';
+import { initBackEndControlRoutes } from '/@/router/backEnd';
+
+/**
+ * 1、前端控制路由时:isRequestRoutes 为 false,需要写 roles,需要走 setFilterRoute 方法。
+ * 2、后端控制路由时:isRequestRoutes 为 true,不需要写 roles,不需要走 setFilterRoute 方法),
+ * 相关方法已拆解到对应的 `backEnd.ts` 与 `frontEnd.ts`(他们互不影响,不需要同时改 2 个文件)。
+ * 特别说明:
+ * 1、前端控制:路由菜单由前端去写(无菜单管理界面,有角色管理界面),角色管理中有 roles 属性,需返回到 userInfo 中。
+ * 2、后端控制:路由菜单由后端返回(有菜单管理界面、有角色管理界面)
+ */
+
+// 读取 `/src/stores/themeConfig.ts` 是否开启后端控制路由配置
+const storesThemeConfig = useThemeConfig(pinia);
+const { themeConfig } = storeToRefs(storesThemeConfig);
+const { isRequestRoutes } = themeConfig.value;
+
+/**
+ * 创建一个可以被 Vue 应用程序使用的路由实例
+ * @method createRouter(options: RouterOptions): Router
+ * @link 参考:https://next.router.vuejs.org/zh/api/#createrouter
+ */
+export const router = createRouter({
+	history: createWebHashHistory(),
+	/**
+	 * 说明:
+	 * 1、notFoundAndNoPower 默认添加 404、401 界面,防止一直提示 No match found for location with path 'xxx'
+	 * 2、backEnd.ts(后端控制路由)、frontEnd.ts(前端控制路由) 中也需要加 notFoundAndNoPower 404、401 界面。
+	 *    防止 404、401 不在 layout 布局中,不设置的话,404、401 界面将全屏显示
+	 */
+	routes: [...notFoundAndNoPower, ...staticRoutes],
+});
+
+/**
+ * 路由多级嵌套数组处理成一维数组
+ * @param arr 传入路由菜单数据数组
+ * @returns 返回处理后的一维路由菜单数组
+ */
+export function formatFlatteningRoutes(arr: any) {
+	if (arr.length <= 0) return false;
+	for (let i = 0; i < arr.length; i++) {
+		if (arr[i].children) {
+			arr = arr.slice(0, i + 1).concat(arr[i].children, arr.slice(i + 1));
+		}
+	}
+	return arr;
+}
+
+/**
+ * 一维数组处理成多级嵌套数组(只保留二级:也就是二级以上全部处理成只有二级,keep-alive 支持二级缓存)
+ * @description isKeepAlive 处理 `name` 值,进行缓存。顶级关闭,全部不缓存
+ * @link 参考:https://v3.cn.vuejs.org/api/built-in-components.html#keep-alive
+ * @param arr 处理后的一维路由菜单数组
+ * @returns 返回将一维数组重新处理成 `定义动态路由(dynamicRoutes)` 的格式
+ */
+export function formatTwoStageRoutes(arr: any) {
+	if (arr.length <= 0) return false;
+	const newArr: any = [];
+	const cacheList: Array<string> = [];
+	arr.forEach((v: any) => {
+		if (v.path === '/') {
+			newArr.push({ component: v.component, name: v.name, path: v.path, redirect: v.redirect, meta: v.meta, children: [] });
+		} else {
+			// 判断是否是动态路由(xx/:id/:name),用于 tagsView 等中使用
+			// 修复:https://gitee.com/lyt-top/vue-next-admin/issues/I3YX6G
+			if (v.path.indexOf('/:') > -1) {
+				v.meta['isDynamic'] = true;
+				v.meta['isDynamicPath'] = v.path;
+			}
+			newArr[0].children.push({ ...v });
+			// 存 name 值,keep-alive 中 include 使用,实现路由的缓存
+			// 路径:/@/layout/routerView/parent.vue
+			if (newArr[0].meta.isKeepAlive && v.meta.isKeepAlive) {
+				cacheList.push(v.name);
+				const stores = useKeepALiveNames(pinia);
+				stores.setCacheKeepAlive(cacheList);
+			}
+		}
+	});
+	return newArr;
+}
+
+// 路由加载前
+router.beforeEach(async (to, from, next) => {
+	NProgress.configure({ showSpinner: false });
+	if (to.meta.title) NProgress.start();
+	const token = Session.get('token');
+	if (to.path === '/login' && !token) {
+		next();
+		NProgress.done();
+	} else {
+		if (!token) {
+			next(`/login?redirect=${to.path}&params=${JSON.stringify(to.query ? to.query : to.params)}`);
+			Session.clear();
+			NProgress.done();
+		} else if (token && to.path === '/login') {
+			next('/home');
+			NProgress.done();
+		} else {
+			const storesRoutesList = useRoutesList(pinia);
+			const { routesList } = storeToRefs(storesRoutesList);
+			if (routesList.value.length === 0) {
+				if (isRequestRoutes) {
+					// 后端控制路由:路由数据初始化,防止刷新时丢失
+					await initBackEndControlRoutes();
+					// 解决刷新时,一直跳 404 页面问题,关联问题 No match found for location with path 'xxx'
+					// to.query 防止页面刷新时,普通路由带参数时,参数丢失。动态路由(xxx/:id/:name")isDynamic 无需处理
+					next({ path: to.path, query: to.query });
+				} else {
+					// https://gitee.com/lyt-top/vue-next-admin/issues/I5F1HP
+					await initFrontEndControlRoutes();
+					next({ path: to.path, query: to.query });
+				}
+			} else {
+				next();
+			}
+		}
+	}
+});
+
+// 路由加载后
+router.afterEach(() => {
+	NProgress.done();
+});
+
+// 导出路由
+export default router;
Added +1230 -0
diff --git a/src/router/route.ts b/src/router/route.ts
new file mode 100644
index 0000000..95e6c84
--- /dev/null
+++ b/src/router/route.ts
@@ -0,0 +1,1230 @@
+import { RouteRecordRaw } from 'vue-router';
+
+/**
+ * 建议:路由 path 路径与文件夹名称相同,找文件可浏览器地址找,方便定位文件位置
+ *
+ * 路由meta对象参数说明
+ * meta: {
+ *      title:          菜单栏及 tagsView 栏、菜单搜索名称(国际化)
+ *      isLink:        是否超链接菜单,开启外链条件,`1、isLink: 链接地址不为空 2、isIframe:false`
+ *      isHide:        是否隐藏此路由
+ *      isKeepAlive:   是否缓存组件状态
+ *      isAffix:       是否固定在 tagsView 栏上
+ *      isIframe:      是否内嵌窗口,开启条件,`1、isIframe:true 2、isLink:链接地址不为空`
+ *      roles:         当前路由权限标识,取角色管理。控制路由显示、隐藏。超级管理员:admin 普通角色:common
+ *      icon:          菜单、tagsView 图标,阿里:加 `iconfont xxx`,fontawesome:加 `fa xxx`
+ * }
+ */
+
+// 扩展 RouteMeta 接口
+declare module 'vue-router' {
+	interface RouteMeta {
+		title?: string;
+		isLink?: string;
+		isHide?: boolean;
+		isKeepAlive?: boolean;
+		isAffix?: boolean;
+		isIframe?: boolean;
+		roles?: string[];
+		icon?: string;
+	}
+}
+
+/**
+ * 定义动态路由
+ * 前端添加路由,请在顶级节点的 `children 数组` 里添加
+ * @description 未开启 isRequestRoutes 为 true 时使用(前端控制路由),开启时第一个顶级 children 的路由将被替换成接口请求回来的路由数据
+ * @description 各字段请查看 `/@/views/system/menu/component/addMenu.vue 下的 ruleForm`
+ * @returns 返回路由菜单数据
+ */
+export const dynamicRoutes: Array<RouteRecordRaw> = [
+	{
+		path: '/',
+		name: '/',
+		component: () => import('/@/layout/index.vue'),
+		redirect: '/home',
+		meta: {
+			isKeepAlive: true,
+		},
+		children: [
+			{
+				path: '/home',
+				name: 'home',
+				component: () => import('/@/views/home/index.vue'),
+				meta: {
+					title: 'message.router.home',
+					isLink: '',
+					isHide: false,
+					isKeepAlive: true,
+					isAffix: true,
+					isIframe: false,
+					roles: ['admin', 'common'],
+					icon: 'iconfont icon-shouye',
+				},
+			},
+			{
+				path: '/personal',
+				name: 'personal',
+				component: () => import('/@/views/personal/index.vue'),
+				meta: {
+					title: 'message.router.personal',
+					isLink: '',
+					isHide: false,
+					isKeepAlive: true,
+					isAffix: false,
+					isIframe: false,
+					roles: ['admin', 'common'],
+					icon: 'iconfont icon-gerenzhongxin',
+				},
+			},
+			// {
+			// 	path: '/system',
+			// 	name: 'system',
+			// 	component: () => import('/@/layout/routerView/parent.vue'),
+			// 	redirect: '/system/menu',
+			// 	meta: {
+			// 		title: 'message.router.system',
+			// 		isLink: '',
+			// 		isHide: false,
+			// 		isKeepAlive: true,
+			// 		isAffix: false,
+			// 		isIframe: false,
+			// 		roles: ['admin'],
+			// 		icon: 'iconfont icon-xitongshezhi',
+			// 	},
+			// 	children: [
+			// 		{
+			// 			path: '/system/menu',
+			// 			name: 'systemMenu',
+			// 			component: () => import('/@/views/system/menu/index.vue'),
+			// 			meta: {
+			// 				title: 'message.router.systemMenu',
+			// 				isLink: '',
+			// 				isHide: false,
+			// 				isKeepAlive: true,
+			// 				isAffix: false,
+			// 				isIframe: false,
+			// 				roles: ['admin'],
+			// 				icon: 'iconfont icon-caidan',
+			// 			},
+			// 		},
+			// 		{
+			// 			path: '/system/role',
+			// 			name: 'systemRole',
+			// 			component: () => import('/@/views/system/role/index.vue'),
+			// 			meta: {
+			// 				title: 'message.router.systemRole',
+			// 				isLink: '',
+			// 				isHide: false,
+			// 				isKeepAlive: true,
+			// 				isAffix: false,
+			// 				isIframe: false,
+			// 				roles: ['admin'],
+			// 				icon: 'ele-ColdDrink',
+			// 			},
+			// 		},
+			// 		{
+			// 			path: '/system/user',
+			// 			name: 'systemUser',
+			// 			component: () => import('/@/views/system/user/index.vue'),
+			// 			meta: {
+			// 				title: 'message.router.systemUser',
+			// 				isLink: '',
+			// 				isHide: false,
+			// 				isKeepAlive: true,
+			// 				isAffix: false,
+			// 				isIframe: false,
+			// 				roles: ['admin'],
+			// 				icon: 'iconfont icon-icon-',
+			// 			},
+			// 		},
+			// 		{
+			// 			path: '/system/dept',
+			// 			name: 'systemDept',
+			// 			component: () => import('/@/views/system/dept/index.vue'),
+			// 			meta: {
+			// 				title: 'message.router.systemDept',
+			// 				isLink: '',
+			// 				isHide: false,
+			// 				isKeepAlive: true,
+			// 				isAffix: false,
+			// 				isIframe: false,
+			// 				roles: ['admin'],
+			// 				icon: 'ele-OfficeBuilding',
+			// 			},
+			// 		},
+			// 		{
+			// 			path: '/system/dic',
+			// 			name: 'systemDic',
+			// 			component: () => import('/@/views/system/dic/index.vue'),
+			// 			meta: {
+			// 				title: 'message.router.systemDic',
+			// 				isLink: '',
+			// 				isHide: false,
+			// 				isKeepAlive: true,
+			// 				isAffix: false,
+			// 				isIframe: false,
+			// 				roles: ['admin'],
+			// 				icon: 'ele-SetUp',
+			// 			},
+			// 		},
+			// 	],
+			// },
+			// {
+			// 	path: '/limits',
+			// 	name: 'limits',
+			// 	component: () => import('/@/layout/routerView/parent.vue'),
+			// 	redirect: '/limits/frontEnd',
+			// 	meta: {
+			// 		title: 'message.router.limits',
+			// 		isLink: '',
+			// 		isHide: false,
+			// 		isKeepAlive: true,
+			// 		isAffix: false,
+			// 		isIframe: false,
+			// 		roles: ['admin', 'common'],
+			// 		icon: 'iconfont icon-quanxian',
+			// 	},
+			// 	children: [
+			// 		{
+			// 			path: '/limits/frontEnd',
+			// 			name: 'limitsFrontEnd',
+			// 			component: () => import('/@/layout/routerView/parent.vue'),
+			// 			redirect: '/limits/frontEnd/page',
+			// 			meta: {
+			// 				title: 'message.router.limitsFrontEnd',
+			// 				isLink: '',
+			// 				isHide: false,
+			// 				isKeepAlive: true,
+			// 				isAffix: false,
+			// 				isIframe: false,
+			// 				roles: ['admin', 'common'],
+			// 				icon: '',
+			// 			},
+			// 			children: [
+			// 				{
+			// 					path: '/limits/frontEnd/page',
+			// 					name: 'limitsFrontEndPage',
+			// 					component: () => import('/@/views/limits/frontEnd/page/index.vue'),
+			// 					meta: {
+			// 						title: 'message.router.limitsFrontEndPage',
+			// 						isLink: '',
+			// 						isHide: false,
+			// 						isKeepAlive: true,
+			// 						isAffix: false,
+			// 						isIframe: false,
+			// 						roles: ['admin', 'common'],
+			// 						icon: '',
+			// 					},
+			// 				},
+			// 				{
+			// 					path: '/limits/frontEnd/btn',
+			// 					name: 'limitsFrontEndBtn',
+			// 					component: () => import('/@/views/limits/frontEnd/btn/index.vue'),
+			// 					meta: {
+			// 						title: 'message.router.limitsFrontEndBtn',
+			// 						isLink: '',
+			// 						isHide: false,
+			// 						isKeepAlive: true,
+			// 						isAffix: false,
+			// 						isIframe: false,
+			// 						roles: ['admin', 'common'],
+			// 						icon: '',
+			// 					},
+			// 				},
+			// 			],
+			// 		},
+			// 		{
+			// 			path: '/limits/backEnd',
+			// 			name: 'limitsBackEnd',
+			// 			component: () => import('/@/layout/routerView/parent.vue'),
+			// 			meta: {
+			// 				title: 'message.router.limitsBackEnd',
+			// 				isLink: '',
+			// 				isHide: false,
+			// 				isKeepAlive: true,
+			// 				isAffix: false,
+			// 				isIframe: false,
+			// 				roles: ['admin', 'common'],
+			// 				icon: '',
+			// 			},
+			// 			children: [
+			// 				{
+			// 					path: '/limits/backEnd/page',
+			// 					name: 'limitsBackEndEndPage',
+			// 					component: () => import('/@/views/limits/backEnd/page/index.vue'),
+			// 					meta: {
+			// 						title: 'message.router.limitsBackEndEndPage',
+			// 						isLink: '',
+			// 						isHide: false,
+			// 						isKeepAlive: true,
+			// 						isAffix: false,
+			// 						isIframe: false,
+			// 						roles: ['admin', 'common'],
+			// 						icon: '',
+			// 					},
+			// 				},
+			// 			],
+			// 		},
+			// 	],
+			// },
+			// {
+			// 	path: '/menu',
+			// 	name: 'menu',
+			// 	component: () => import('/@/layout/routerView/parent.vue'),
+			// 	redirect: '/menu/menu1',
+			// 	meta: {
+			// 		title: 'message.router.menu',
+			// 		isLink: '',
+			// 		isHide: false,
+			// 		isKeepAlive: true,
+			// 		isAffix: false,
+			// 		isIframe: false,
+			// 		roles: ['admin', 'common'],
+			// 		icon: 'iconfont icon-caidan',
+			// 	},
+			// 	children: [
+			// 		{
+			// 			path: '/menu/menu1',
+			// 			name: 'menu1',
+			// 			component: () => import('/@/layout/routerView/parent.vue'),
+			// 			redirect: '/menu/menu1/menu11',
+			// 			meta: {
+			// 				title: 'message.router.menu1',
+			// 				isLink: '',
+			// 				isHide: false,
+			// 				isKeepAlive: true,
+			// 				isAffix: false,
+			// 				isIframe: false,
+			// 				roles: ['admin', 'common'],
+			// 				icon: 'iconfont icon-caidan',
+			// 			},
+			// 			children: [
+			// 				{
+			// 					path: '/menu/menu1/menu11',
+			// 					name: 'menu11',
+			// 					component: () => import('/@/views/menu/menu1/menu11/index.vue'),
+			// 					meta: {
+			// 						title: 'message.router.menu11',
+			// 						isLink: '',
+			// 						isHide: false,
+			// 						isKeepAlive: true,
+			// 						isAffix: false,
+			// 						isIframe: false,
+			// 						roles: ['admin', 'common'],
+			// 						icon: 'iconfont icon-caidan',
+			// 					},
+			// 				},
+			// 				{
+			// 					path: '/menu/menu1/menu12',
+			// 					name: 'menu12',
+			// 					component: () => import('/@/layout/routerView/parent.vue'),
+			// 					redirect: '/menu/menu1/menu12/menu121',
+			// 					meta: {
+			// 						title: 'message.router.menu12',
+			// 						isLink: '',
+			// 						isHide: false,
+			// 						isKeepAlive: true,
+			// 						isAffix: false,
+			// 						isIframe: false,
+			// 						roles: ['admin', 'common'],
+			// 						icon: 'iconfont icon-caidan',
+			// 					},
+			// 					children: [
+			// 						{
+			// 							path: '/menu/menu1/menu12/menu121',
+			// 							name: 'menu121',
+			// 							component: () => import('/@/views/menu/menu1/menu12/menu121/index.vue'),
+			// 							meta: {
+			// 								title: 'message.router.menu121',
+			// 								isLink: '',
+			// 								isHide: false,
+			// 								isKeepAlive: true,
+			// 								isAffix: false,
+			// 								isIframe: false,
+			// 								roles: ['admin', 'common'],
+			// 								icon: 'iconfont icon-caidan',
+			// 							},
+			// 						},
+			// 						{
+			// 							path: '/menu/menu1/menu12/menu122',
+			// 							name: 'menu122',
+			// 							component: () => import('/@/views/menu/menu1/menu12/menu122/index.vue'),
+			// 							meta: {
+			// 								title: 'message.router.menu122',
+			// 								isLink: '',
+			// 								isHide: false,
+			// 								isKeepAlive: true,
+			// 								isAffix: false,
+			// 								isIframe: false,
+			// 								roles: ['admin', 'common'],
+			// 								icon: 'iconfont icon-caidan',
+			// 							},
+			// 						},
+			// 					],
+			// 				},
+			// 				{
+			// 					path: '/menu/menu1/menu13',
+			// 					name: 'menu13',
+			// 					component: () => import('/@/views/menu/menu1/menu13/index.vue'),
+			// 					meta: {
+			// 						title: 'message.router.menu13',
+			// 						isLink: '',
+			// 						isHide: false,
+			// 						isKeepAlive: true,
+			// 						isAffix: false,
+			// 						isIframe: false,
+			// 						roles: ['admin', 'common'],
+			// 						icon: 'iconfont icon-caidan',
+			// 					},
+			// 				},
+			// 			],
+			// 		},
+			// 		{
+			// 			path: '/menu/menu2',
+			// 			name: 'menu2',
+			// 			component: () => import('/@/views/menu/menu2/index.vue'),
+			// 			meta: {
+			// 				title: 'message.router.menu2',
+			// 				isLink: '',
+			// 				isHide: false,
+			// 				isKeepAlive: true,
+			// 				isAffix: false,
+			// 				isIframe: false,
+			// 				roles: ['admin', 'common'],
+			// 				icon: 'iconfont icon-caidan',
+			// 			},
+			// 		},
+			// 	],
+			// },
+			// {
+			// 	path: '/fun',
+			// 	name: 'funIndex',
+			// 	component: () => import('/@/layout/routerView/parent.vue'),
+			// 	redirect: '/fun/tagsView',
+			// 	meta: {
+			// 		title: 'message.router.funIndex',
+			// 		isLink: '',
+			// 		isHide: false,
+			// 		isKeepAlive: true,
+			// 		isAffix: false,
+			// 		isIframe: false,
+			// 		roles: ['admin', 'common'],
+			// 		icon: 'iconfont icon-crew_feature',
+			// 	},
+			// 	children: [
+			// 		{
+			// 			path: '/fun/tagsView',
+			// 			name: 'funTagsView',
+			// 			component: () => import('/@/views/fun/tagsView/index.vue'),
+			// 			meta: {
+			// 				title: 'message.router.funTagsView',
+			// 				isLink: '',
+			// 				isHide: false,
+			// 				isKeepAlive: true,
+			// 				isAffix: false,
+			// 				isIframe: false,
+			// 				roles: ['admin', 'common'],
+			// 				icon: 'ele-Pointer',
+			// 			},
+			// 		},
+			// 		{
+			// 			path: '/fun/countup',
+			// 			name: 'funCountup',
+			// 			component: () => import('/@/views/fun/countup/index.vue'),
+			// 			meta: {
+			// 				title: 'message.router.funCountup',
+			// 				isLink: '',
+			// 				isHide: false,
+			// 				isKeepAlive: true,
+			// 				isAffix: false,
+			// 				isIframe: false,
+			// 				roles: ['admin', 'common'],
+			// 				icon: 'ele-Odometer',
+			// 			},
+			// 		},
+			// 		{
+			// 			path: '/fun/wangEditor',
+			// 			name: 'funWangEditor',
+			// 			component: () => import('/@/views/fun/wangEditor/index.vue'),
+			// 			meta: {
+			// 				title: 'message.router.funWangEditor',
+			// 				isLink: '',
+			// 				isHide: false,
+			// 				isKeepAlive: true,
+			// 				isAffix: false,
+			// 				isIframe: false,
+			// 				roles: ['admin', 'common'],
+			// 				icon: 'iconfont icon-fuwenbenkuang',
+			// 			},
+			// 		},
+			// 		{
+			// 			path: '/fun/cropper',
+			// 			name: 'funCropper',
+			// 			component: () => import('/@/views/fun/cropper/index.vue'),
+			// 			meta: {
+			// 				title: 'message.router.funCropper',
+			// 				isLink: '',
+			// 				isHide: false,
+			// 				isKeepAlive: true,
+			// 				isAffix: false,
+			// 				isIframe: false,
+			// 				roles: ['admin', 'common'],
+			// 				icon: 'iconfont icon-caijian',
+			// 			},
+			// 		},
+			// 		{
+			// 			path: '/fun/qrcode',
+			// 			name: 'funQrcode',
+			// 			component: () => import('/@/views/fun/qrcode/index.vue'),
+			// 			meta: {
+			// 				title: 'message.router.funQrcode',
+			// 				isLink: '',
+			// 				isHide: false,
+			// 				isKeepAlive: true,
+			// 				isAffix: false,
+			// 				isIframe: false,
+			// 				roles: ['admin', 'common'],
+			// 				icon: 'iconfont icon-ico',
+			// 			},
+			// 		},
+			// 		{
+			// 			path: '/fun/echartsMap',
+			// 			name: 'funEchartsMap',
+			// 			component: () => import('/@/views/fun/echartsMap/index.vue'),
+			// 			meta: {
+			// 				title: 'message.router.funEchartsMap',
+			// 				isLink: '',
+			// 				isHide: false,
+			// 				isKeepAlive: true,
+			// 				isAffix: false,
+			// 				isIframe: false,
+			// 				roles: ['admin', 'common'],
+			// 				icon: 'iconfont icon-ditu',
+			// 			},
+			// 		},
+			// 		{
+			// 			path: '/fun/printJs',
+			// 			name: 'funPrintJs',
+			// 			component: () => import('/@/views/fun/printJs/index.vue'),
+			// 			meta: {
+			// 				title: 'message.router.funPrintJs',
+			// 				isLink: '',
+			// 				isHide: false,
+			// 				isKeepAlive: true,
+			// 				isAffix: false,
+			// 				isIframe: false,
+			// 				roles: ['admin', 'common'],
+			// 				icon: 'ele-Printer',
+			// 			},
+			// 		},
+			// 		{
+			// 			path: '/fun/clipboard',
+			// 			name: 'funClipboard',
+			// 			component: () => import('/@/views/fun/clipboard/index.vue'),
+			// 			meta: {
+			// 				title: 'message.router.funClipboard',
+			// 				isLink: '',
+			// 				isHide: false,
+			// 				isKeepAlive: true,
+			// 				isAffix: false,
+			// 				isIframe: false,
+			// 				roles: ['admin', 'common'],
+			// 				icon: 'ele-DocumentCopy',
+			// 			},
+			// 		},
+			// 		{
+			// 			path: '/fun/gridLayout',
+			// 			name: 'funGridLayout',
+			// 			component: () => import('/@/views/fun/gridLayout/index.vue'),
+			// 			meta: {
+			// 				title: 'message.router.funGridLayout',
+			// 				isLink: '',
+			// 				isHide: false,
+			// 				isKeepAlive: true,
+			// 				isAffix: false,
+			// 				isIframe: false,
+			// 				roles: ['admin', 'common'],
+			// 				icon: 'iconfont icon-tuodong',
+			// 			},
+			// 		},
+			// 		{
+			// 			path: '/fun/splitpanes',
+			// 			name: 'funSplitpanes',
+			// 			component: () => import('/@/views/fun/splitpanes/index.vue'),
+			// 			meta: {
+			// 				title: 'message.router.funSplitpanes',
+			// 				isLink: '',
+			// 				isHide: false,
+			// 				isKeepAlive: true,
+			// 				isAffix: false,
+			// 				isIframe: false,
+			// 				roles: ['admin', 'common'],
+			// 				icon: 'iconfont icon--chaifenlie',
+			// 			},
+			// 		},
+			// 	],
+			// },
+			// {
+			// 	path: '/pages',
+			// 	name: 'pagesIndex',
+			// 	component: () => import('/@/layout/routerView/parent.vue'),
+			// 	redirect: '/pages/filtering',
+			// 	meta: {
+			// 		title: 'message.router.pagesIndex',
+			// 		isLink: '',
+			// 		isHide: false,
+			// 		isKeepAlive: true,
+			// 		isAffix: false,
+			// 		isIframe: false,
+			// 		roles: ['admin', 'common'],
+			// 		icon: 'iconfont icon-fuzhiyemian',
+			// 	},
+			// 	children: [
+			// 		{
+			// 			path: '/pages/filtering',
+			// 			name: 'pagesFiltering',
+			// 			component: () => import('/@/views/pages/filtering/index.vue'),
+			// 			meta: {
+			// 				title: 'message.router.pagesFiltering',
+			// 				isLink: '',
+			// 				isHide: false,
+			// 				isKeepAlive: true,
+			// 				isAffix: false,
+			// 				isIframe: false,
+			// 				roles: ['admin', 'common'],
+			// 				icon: 'ele-Sell',
+			// 			},
+			// 			/**
+			// 			 * 注意此处详情写法:
+			// 			 * 1、嵌套进父级里时,面包屑显示为:首页/页面/过滤筛选组件/过滤筛选组件详情
+			// 			 * 2、不嵌套进父级时,面包屑显示为:首页/页面/过滤筛选组件/过滤筛选组件详情
+			// 			 * 3、想要父级不高亮,面包屑显示为:首页/页面/过滤筛选组件详情,设置路径为:/pages/filteringDetails
+			// 			 */
+			// 			children: [
+			// 				{
+			// 					path: '/pages/filtering/details',
+			// 					name: 'pagesFilteringDetails',
+			// 					component: () => import('/@/views/pages/filtering/details.vue'),
+			// 					meta: {
+			// 						title: 'message.router.pagesFilteringDetails',
+			// 						isLink: '',
+			// 						isHide: true,
+			// 						isKeepAlive: false,
+			// 						isAffix: false,
+			// 						isIframe: false,
+			// 						roles: ['admin', 'common'],
+			// 						icon: 'ele-Sunny',
+			// 					},
+			// 				},
+			// 			],
+			// 		},
+			// 		{
+			// 			path: '/pages/filtering/details1',
+			// 			name: 'pagesFilteringDetails1',
+			// 			component: () => import('/@/views/pages/filtering/details1.vue'),
+			// 			meta: {
+			// 				title: 'message.router.pagesFilteringDetails1',
+			// 				isLink: '',
+			// 				isHide: true,
+			// 				isKeepAlive: false,
+			// 				isAffix: false,
+			// 				isIframe: false,
+			// 				roles: ['admin', 'common'],
+			// 				icon: 'ele-Sunny',
+			// 			},
+			// 		},
+			// 		{
+			// 			path: '/pages/iocnfont',
+			// 			name: 'pagesIocnfont',
+			// 			component: () => import('/@/views/pages/iocnfont/index.vue'),
+			// 			meta: {
+			// 				title: 'message.router.pagesIocnfont',
+			// 				isLink: '',
+			// 				isHide: false,
+			// 				isKeepAlive: true,
+			// 				isAffix: false,
+			// 				isIframe: false,
+			// 				roles: ['admin', 'common'],
+			// 				icon: 'ele-Present',
+			// 			},
+			// 		},
+			// 		{
+			// 			path: '/pages/element',
+			// 			name: 'pagesElement',
+			// 			component: () => import('/@/views/pages/element/index.vue'),
+			// 			meta: {
+			// 				title: 'message.router.pagesElement',
+			// 				isLink: '',
+			// 				isHide: false,
+			// 				isKeepAlive: true,
+			// 				isAffix: false,
+			// 				isIframe: false,
+			// 				roles: ['admin', 'common'],
+			// 				icon: 'ele-Eleme',
+			// 			},
+			// 		},
+			// 		{
+			// 			path: '/pages/awesome',
+			// 			name: 'pagesAwesome',
+			// 			component: () => import('/@/views/pages/awesome/index.vue'),
+			// 			meta: {
+			// 				title: 'message.router.pagesAwesome',
+			// 				isLink: '',
+			// 				isHide: false,
+			// 				isKeepAlive: true,
+			// 				isAffix: false,
+			// 				isIframe: false,
+			// 				roles: ['admin', 'common'],
+			// 				icon: 'ele-SetUp',
+			// 			},
+			// 		},
+			// 		{
+			// 			path: '/pages/formAdapt',
+			// 			name: 'pagesFormAdapt',
+			// 			component: () => import('/@/views/pages/formAdapt/index.vue'),
+			// 			meta: {
+			// 				title: 'message.router.pagesFormAdapt',
+			// 				isLink: '',
+			// 				isHide: false,
+			// 				isKeepAlive: true,
+			// 				isAffix: false,
+			// 				isIframe: false,
+			// 				roles: ['admin', 'common'],
+			// 				icon: 'iconfont icon-biaodan',
+			// 			},
+			// 		},
+			// 		{
+			// 			path: '/pages/tableRules',
+			// 			name: 'pagesTableRules',
+			// 			component: () => import('/@/views/pages/tableRules/index.vue'),
+			// 			meta: {
+			// 				title: 'message.router.pagesTableRules',
+			// 				isLink: '',
+			// 				isHide: false,
+			// 				isKeepAlive: true,
+			// 				isAffix: false,
+			// 				isIframe: false,
+			// 				roles: ['admin', 'common'],
+			// 				icon: 'iconfont icon-jiliandongxuanzeqi',
+			// 			},
+			// 		},
+			// 		{
+			// 			path: '/pages/formI18n',
+			// 			name: 'pagesFormI18n',
+			// 			component: () => import('/@/views/pages/formI18n/index.vue'),
+			// 			meta: {
+			// 				title: 'message.router.pagesFormI18n',
+			// 				isLink: '',
+			// 				isHide: false,
+			// 				isKeepAlive: true,
+			// 				isAffix: false,
+			// 				isIframe: false,
+			// 				roles: ['admin', 'common'],
+			// 				icon: 'iconfont icon-diqiu',
+			// 			},
+			// 		},
+			// 		{
+			// 			path: '/pages/formRules',
+			// 			name: 'pagesFormRules',
+			// 			component: () => import('/@/views/pages/formRules/index.vue'),
+			// 			meta: {
+			// 				title: 'message.router.pagesFormRules',
+			// 				isLink: '',
+			// 				isHide: false,
+			// 				isKeepAlive: true,
+			// 				isAffix: false,
+			// 				isIframe: false,
+			// 				roles: ['admin', 'common'],
+			// 				icon: 'iconfont icon-shuxing',
+			// 			},
+			// 		},
+			// 		{
+			// 			path: '/pages/listAdapt',
+			// 			name: 'pagesListAdapt',
+			// 			component: () => import('/@/views/pages/listAdapt/index.vue'),
+			// 			meta: {
+			// 				title: 'message.router.pagesListAdapt',
+			// 				isLink: '',
+			// 				isHide: false,
+			// 				isKeepAlive: true,
+			// 				isAffix: false,
+			// 				isIframe: false,
+			// 				roles: ['admin', 'common'],
+			// 				icon: 'iconfont icon-chazhaobiaodanliebiao',
+			// 			},
+			// 		},
+			// 		{
+			// 			path: '/pages/waterfall',
+			// 			name: 'pagesWaterfall',
+			// 			component: () => import('/@/views/pages/waterfall/index.vue'),
+			// 			meta: {
+			// 				title: 'message.router.pagesWaterfall',
+			// 				isLink: '',
+			// 				isHide: false,
+			// 				isKeepAlive: true,
+			// 				isAffix: false,
+			// 				isIframe: false,
+			// 				roles: ['admin', 'common'],
+			// 				icon: 'iconfont icon-zidingyibuju',
+			// 			},
+			// 		},
+			// 		{
+			// 			path: '/pages/steps',
+			// 			name: 'pagesSteps',
+			// 			component: () => import('/@/views/pages/steps/index.vue'),
+			// 			meta: {
+			// 				title: 'message.router.pagesSteps',
+			// 				isLink: '',
+			// 				isHide: false,
+			// 				isKeepAlive: true,
+			// 				isAffix: false,
+			// 				isIframe: false,
+			// 				roles: ['admin', 'common'],
+			// 				icon: 'iconfont icon-step',
+			// 			},
+			// 		},
+			// 		{
+			// 			path: '/pages/preview',
+			// 			name: 'pagesPreview',
+			// 			component: () => import('/@/views/pages/preview/index.vue'),
+			// 			meta: {
+			// 				title: 'message.router.pagesPreview',
+			// 				isLink: '',
+			// 				isHide: false,
+			// 				isKeepAlive: true,
+			// 				isAffix: false,
+			// 				isIframe: false,
+			// 				roles: ['admin', 'common'],
+			// 				icon: 'iconfont icon-15tupianyulan',
+			// 			},
+			// 		},
+			// 		{
+			// 			path: '/pages/waves',
+			// 			name: 'pagesWaves',
+			// 			component: () => import('/@/views/pages/waves/index.vue'),
+			// 			meta: {
+			// 				title: 'message.router.pagesWaves',
+			// 				isLink: '',
+			// 				isHide: false,
+			// 				isKeepAlive: true,
+			// 				isAffix: false,
+			// 				isIframe: false,
+			// 				roles: ['admin', 'common'],
+			// 				icon: 'iconfont icon-bolangneng',
+			// 			},
+			// 		},
+			// 		{
+			// 			path: '/pages/tree',
+			// 			name: 'pagesTree',
+			// 			component: () => import('/@/views/pages/tree/index.vue'),
+			// 			meta: {
+			// 				title: 'message.router.pagesTree',
+			// 				isLink: '',
+			// 				isHide: false,
+			// 				isKeepAlive: true,
+			// 				isAffix: false,
+			// 				isIframe: false,
+			// 				roles: ['admin', 'common'],
+			// 				icon: 'iconfont icon-shuxingtu',
+			// 			},
+			// 		},
+			// 		{
+			// 			path: '/pages/drag',
+			// 			name: 'pagesDrag',
+			// 			component: () => import('/@/views/pages/drag/index.vue'),
+			// 			meta: {
+			// 				title: 'message.router.pagesDrag',
+			// 				isLink: '',
+			// 				isHide: false,
+			// 				isKeepAlive: true,
+			// 				isAffix: false,
+			// 				isIframe: false,
+			// 				roles: ['admin', 'common'],
+			// 				icon: 'ele-Pointer',
+			// 			},
+			// 		},
+			// 		{
+			// 			path: '/pages/lazyImg',
+			// 			name: 'pagesLazyImg',
+			// 			component: () => import('/@/views/pages/lazyImg/index.vue'),
+			// 			meta: {
+			// 				title: 'message.router.pagesLazyImg',
+			// 				isLink: '',
+			// 				isHide: false,
+			// 				isKeepAlive: true,
+			// 				isAffix: false,
+			// 				isIframe: false,
+			// 				roles: ['admin'],
+			// 				icon: 'ele-PictureFilled',
+			// 			},
+			// 		},
+			// 		{
+			// 			path: '/pages/dynamicForm',
+			// 			name: 'pagesDynamicForm',
+			// 			component: () => import('/@/views/pages/dynamicForm/index.vue'),
+			// 			meta: {
+			// 				title: 'message.router.pagesDynamicForm',
+			// 				isLink: '',
+			// 				isHide: false,
+			// 				isKeepAlive: true,
+			// 				isAffix: false,
+			// 				isIframe: false,
+			// 				roles: ['admin'],
+			// 				icon: 'iconfont icon-wenducanshu-05',
+			// 			},
+			// 		},
+			// 		{
+			// 			path: '/pages/workflow',
+			// 			name: 'pagesWorkflow',
+			// 			component: () => import('/@/views/pages/workflow/index.vue'),
+			// 			meta: {
+			// 				title: 'message.router.pagesWorkflow',
+			// 				isLink: '',
+			// 				isHide: false,
+			// 				isKeepAlive: true,
+			// 				isAffix: false,
+			// 				isIframe: false,
+			// 				roles: ['admin'],
+			// 				icon: 'ele-Connection',
+			// 			},
+			// 		},
+			// 	],
+			// },
+			// {
+			// 	path: '/make',
+			// 	name: 'makeIndex',
+			// 	component: () => import('/@/layout/routerView/parent.vue'),
+			// 	redirect: '/make/selector',
+			// 	meta: {
+			// 		title: 'message.router.makeIndex',
+			// 		isLink: '',
+			// 		isHide: false,
+			// 		isKeepAlive: true,
+			// 		isAffix: false,
+			// 		isIframe: false,
+			// 		roles: ['admin'],
+			// 		icon: 'iconfont icon-siweidaotu',
+			// 	},
+			// 	children: [
+			// 		{
+			// 			path: '/make/selector',
+			// 			name: 'makeSelector',
+			// 			component: () => import('/@/views/make/selector/index.vue'),
+			// 			meta: {
+			// 				title: 'message.router.makeSelector',
+			// 				isLink: '',
+			// 				isHide: false,
+			// 				isKeepAlive: true,
+			// 				isAffix: false,
+			// 				isIframe: false,
+			// 				roles: ['admin', 'common'],
+			// 				icon: 'iconfont icon-xuanzeqi',
+			// 			},
+			// 		},
+			// 		{
+			// 			path: '/make/noticeBar',
+			// 			name: 'makeNoticeBar',
+			// 			component: () => import('/@/views/make/noticeBar/index.vue'),
+			// 			meta: {
+			// 				title: 'message.router.makeNoticeBar',
+			// 				isLink: '',
+			// 				isHide: false,
+			// 				isKeepAlive: true,
+			// 				isAffix: false,
+			// 				isIframe: false,
+			// 				roles: ['admin', 'common'],
+			// 				icon: 'ele-Bell',
+			// 			},
+			// 		},
+			// 		{
+			// 			path: '/make/svgDemo',
+			// 			name: 'makeSvgDemo',
+			// 			component: () => import('/@/views/make/svgDemo/index.vue'),
+			// 			meta: {
+			// 				title: 'message.router.makeSvgDemo',
+			// 				isLink: '',
+			// 				isHide: false,
+			// 				isKeepAlive: true,
+			// 				isAffix: false,
+			// 				isIframe: false,
+			// 				roles: ['admin', 'common'],
+			// 				icon: 'fa fa-thumbs-o-up',
+			// 			},
+			// 		},
+			// 		{
+			// 			path: '/make/tableDemo',
+			// 			name: 'makeTableDemo',
+			// 			component: () => import('/@/views/make/tableDemo/index.vue'),
+			// 			meta: {
+			// 				title: 'message.router.makeTableDemo',
+			// 				isLink: '',
+			// 				isHide: false,
+			// 				isKeepAlive: true,
+			// 				isAffix: false,
+			// 				isIframe: false,
+			// 				roles: ['admin', 'common'],
+			// 				icon: 'iconfont icon-shuju',
+			// 			},
+			// 		},
+			// 	],
+			// },
+			// {
+			// 	path: '/params',
+			// 	name: 'paramsIndex',
+			// 	component: () => import('/@/layout/routerView/parent.vue'),
+			// 	redirect: '/params/common',
+			// 	meta: {
+			// 		title: 'message.router.paramsIndex',
+			// 		isLink: '',
+			// 		isHide: false,
+			// 		isKeepAlive: true,
+			// 		isAffix: false,
+			// 		isIframe: false,
+			// 		roles: ['admin'],
+			// 		icon: 'iconfont icon-zhongduancanshu',
+			// 	},
+			// 	children: [
+			// 		{
+			// 			path: '/params/common',
+			// 			name: 'paramsCommon',
+			// 			component: () => import('/@/views/params/common/index.vue'),
+			// 			meta: {
+			// 				title: 'message.router.paramsCommon',
+			// 				isLink: '',
+			// 				isHide: false,
+			// 				isKeepAlive: true,
+			// 				isAffix: false,
+			// 				isIframe: false,
+			// 				roles: ['admin'],
+			// 				icon: 'iconfont icon-putong',
+			// 			},
+			// 		},
+			// 		{
+			// 			path: '/params/common/details',
+			// 			name: 'paramsCommonDetails',
+			// 			component: () => import('/@/views/params/common/details.vue'),
+			// 			meta: {
+			// 				title: 'message.router.paramsCommonDetails',
+			// 				isLink: '',
+			// 				isHide: true,
+			// 				isKeepAlive: true,
+			// 				isAffix: false,
+			// 				isIframe: false,
+			// 				roles: ['admin'],
+			// 				icon: 'ele-Comment',
+			// 			},
+			// 		},
+			// 		{
+			// 			path: '/params/dynamic',
+			// 			name: 'paramsDynamic',
+			// 			component: () => import('/@/views/params/dynamic/index.vue'),
+			// 			meta: {
+			// 				title: 'message.router.paramsDynamic',
+			// 				isLink: '',
+			// 				isHide: false,
+			// 				isKeepAlive: true,
+			// 				isAffix: false,
+			// 				isIframe: false,
+			// 				roles: ['admin'],
+			// 				icon: 'iconfont icon-dongtai',
+			// 			},
+			// 		},
+			// 		/**
+			// 		 * tagsViewName 为要设置不同的 "tagsView 名称" 字段
+			// 		 * 如若需设置不同 "tagsView 名称",tagsViewName 字段必须要有
+			// 		 */
+			// 		{
+			// 			path: '/params/dynamic/details/:t/:id/:tagsViewName',
+			// 			name: 'paramsDynamicDetails',
+			// 			component: () => import('/@/views/params/dynamic/details.vue'),
+			// 			meta: {
+			// 				title: 'message.router.paramsDynamicDetails',
+			// 				isLink: '',
+			// 				isHide: true,
+			// 				isKeepAlive: true,
+			// 				isAffix: false,
+			// 				isIframe: false,
+			// 				roles: ['admin'],
+			// 				icon: 'ele-Lightning',
+			// 			},
+			// 		},
+			// 	],
+			// },
+			// {
+			// 	path: '/visualizing',
+			// 	name: 'visualizingIndex',
+			// 	component: () => import('/@/layout/routerView/parent.vue'),
+			// 	redirect: '/visualizing/visualizingLinkDemo1',
+			// 	meta: {
+			// 		title: 'message.router.visualizingIndex',
+			// 		isLink: '',
+			// 		isHide: false,
+			// 		isKeepAlive: true,
+			// 		isAffix: false,
+			// 		isIframe: false,
+			// 		roles: ['admin'],
+			// 		icon: 'ele-ChatLineRound',
+			// 	},
+			// 	/**
+			// 	 * 打开内置全屏
+			// 	 * component 都为 `() => import('/@/layout/routerView/link.vue')`
+			// 	 * isLink 链接为内置的路由地址,地址为 staticRoutes 中定义
+			// 	 */
+			// 	children: [
+			// 		{
+			// 			path: '/visualizing/visualizingLinkDemo1',
+			// 			name: 'visualizingLinkDemo1',
+			// 			component: () => import('/@/layout/routerView/link.vue'),
+			// 			meta: {
+			// 				title: 'message.router.visualizingLinkDemo1',
+			// 				isLink: '/visualizingDemo1',
+			// 				isHide: false,
+			// 				isKeepAlive: false,
+			// 				isAffix: false,
+			// 				isIframe: false,
+			// 				roles: ['admin'],
+			// 				icon: 'iconfont icon-caozuo-wailian',
+			// 			},
+			// 		},
+			// 		{
+			// 			path: '/visualizing/visualizingLinkDemo2',
+			// 			name: 'visualizingLinkDemo2',
+			// 			component: () => import('/@/layout/routerView/link.vue'),
+			// 			meta: {
+			// 				title: 'message.router.visualizingLinkDemo2',
+			// 				isLink: '/visualizingDemo2',
+			// 				isHide: false,
+			// 				isKeepAlive: false,
+			// 				isAffix: false,
+			// 				isIframe: false,
+			// 				roles: ['admin'],
+			// 				icon: 'iconfont icon-caozuo-wailian',
+			// 			},
+			// 		},
+			// 	],
+			// },
+			// {
+			// 	path: '/chart',
+			// 	name: 'chartIndex',
+			// 	component: () => import('/@/views/chart/index.vue'),
+			// 	meta: {
+			// 		title: 'message.router.chartIndex',
+			// 		isLink: '',
+			// 		isHide: false,
+			// 		isKeepAlive: true,
+			// 		isAffix: false,
+			// 		isIframe: false,
+			// 		roles: ['admin', 'common'],
+			// 		icon: 'iconfont icon-ico_shuju',
+			// 	},
+			// },
+			// {
+			// 	path: '/tools',
+			// 	name: 'tools',
+			// 	component: () => import('/@/views/tools/index.vue'),
+			// 	meta: {
+			// 		title: 'message.router.tools',
+			// 		isLink: '',
+			// 		isHide: false,
+			// 		isKeepAlive: true,
+			// 		isAffix: false,
+			// 		isIframe: false,
+			// 		roles: ['admin', 'common'],
+			// 		icon: 'iconfont icon-gongju',
+			// 	},
+			// },
+			// {
+			// 	path: '/link',
+			// 	name: 'layoutLinkView',
+			// 	component: () => import('/@/layout/routerView/link.vue'),
+			// 	meta: {
+			// 		title: 'message.router.layoutLinkView',
+			// 		isLink: 'https://element-plus.gitee.io/#/zh-CN/component/installation',
+			// 		isHide: false,
+			// 		isKeepAlive: false,
+			// 		isAffix: false,
+			// 		isIframe: false,
+			// 		roles: ['admin'],
+			// 		icon: 'iconfont icon-caozuo-wailian',
+			// 	},
+			// },
+			// {
+			// 	path: '/iframesOne',
+			// 	name: 'layoutIframeViewOne',
+			// 	component: () => import('/@/layout/routerView/iframes.vue'),
+			// 	meta: {
+			// 		title: 'message.router.layoutIframeViewOne',
+			// 		isLink: 'https://nodejs.org/zh-cn/',
+			// 		isHide: false,
+			// 		isKeepAlive: true,
+			// 		isAffix: true,
+			// 		isIframe: true,
+			// 		roles: ['admin'],
+			// 		icon: 'iconfont icon-neiqianshujuchucun',
+			// 	},
+			// },
+			// {
+			// 	path: '/iframesTwo',
+			// 	name: 'layoutIframeViewTwo',
+			// 	component: () => import('/@/layout/routerView/iframes.vue'),
+			// 	meta: {
+			// 		title: 'message.router.layoutIframeViewTwo',
+			// 		isLink: 'https://undraw.co/illustrations',
+			// 		isHide: false,
+			// 		isKeepAlive: true,
+			// 		isAffix: true,
+			// 		isIframe: true,
+			// 		roles: ['admin'],
+			// 		icon: 'iconfont icon-neiqianshujuchucun',
+			// 	},
+			// },
+		],
+	},
+];
+
+/**
+ * 定义404、401界面
+ * @link 参考:https://next.router.vuejs.org/zh/guide/essentials/history-mode.html#netlify
+ */
+export const notFoundAndNoPower = [
+	{
+		path: '/:path(.*)*',
+		name: 'notFound',
+		component: () => import('/@/views/error/404.vue'),
+		meta: {
+			title: 'message.staticRoutes.notFound',
+			isHide: true,
+		},
+	},
+	{
+		path: '/401',
+		name: 'noPower',
+		component: () => import('/@/views/error/401.vue'),
+		meta: {
+			title: 'message.staticRoutes.noPower',
+			isHide: true,
+		},
+	},
+];
+
+/**
+ * 定义静态路由(默认路由)
+ * 此路由不要动,前端添加路由的话,请在 `dynamicRoutes 数组` 中添加
+ * @description 前端控制直接改 dynamicRoutes 中的路由,后端控制不需要修改,请求接口路由数据时,会覆盖 dynamicRoutes 第一个顶级 children 的内容(全屏,不包含 layout 中的路由出口)
+ * @returns 返回路由菜单数据
+ */
+export const staticRoutes: Array<RouteRecordRaw> = [
+	{
+		path: '/login',
+		name: 'login',
+		component: () => import('/@/views/login/index.vue'),
+		meta: {
+			title: '登录',
+		},
+	},
+	/**
+	 * 提示:写在这里的为全屏界面,不建议写在这里
+	 * 请写在 `dynamicRoutes` 路由数组中
+	 */
+];
Added +8 -0
diff --git a/src/stores/index.ts b/src/stores/index.ts
new file mode 100644
index 0000000..27c377e
--- /dev/null
+++ b/src/stores/index.ts
@@ -0,0 +1,8 @@
+// https://pinia.vuejs.org/
+import { createPinia } from 'pinia';
+
+// 创建
+const pinia = createPinia();
+
+// 导出
+export default pinia;
Added +35 -0
diff --git a/src/stores/keepAliveNames.ts b/src/stores/keepAliveNames.ts
new file mode 100644
index 0000000..981198f
--- /dev/null
+++ b/src/stores/keepAliveNames.ts
@@ -0,0 +1,35 @@
+import { defineStore } from 'pinia';
+
+/**
+ * 路由缓存列表
+ * @methods setCacheKeepAlive 设置要缓存的路由 names(开启 Tagsview)
+ * @methods addCachedView 添加要缓存的路由 names(关闭 Tagsview)
+ * @methods delCachedView 删除要缓存的路由 names(关闭 Tagsview)
+ * @methods delOthersCachedViews 右键菜单`关闭其它`,删除要缓存的路由 names(关闭 Tagsview)
+ * @methods delAllCachedViews 右键菜单`全部关闭`,删除要缓存的路由 names(关闭 Tagsview)
+ */
+export const useKeepALiveNames = defineStore('keepALiveNames', {
+	state: (): KeepAliveNamesState => ({
+		keepAliveNames: [],
+		cachedViews: [],
+	}),
+	actions: {
+		async setCacheKeepAlive(data: Array<string>) {
+			this.keepAliveNames = data;
+		},
+		async addCachedView(view: any) {
+			if (view.meta.isKeepAlive) this.cachedViews?.push(view.name);
+		},
+		async delCachedView(view: any) {
+			const index = this.cachedViews.indexOf(view.name);
+			index > -1 && this.cachedViews.splice(index, 1);
+		},
+		async delOthersCachedViews(view: any) {
+			if (view.meta.isKeepAlive) this.cachedViews = [view.name];
+			else this.cachedViews = [];
+		},
+		async delAllCachedViews() {
+			this.cachedViews = [];
+		},
+	},
+});
Added +16 -0
diff --git a/src/stores/requestOldRoutes.ts b/src/stores/requestOldRoutes.ts
new file mode 100644
index 0000000..b14ecc2
--- /dev/null
+++ b/src/stores/requestOldRoutes.ts
@@ -0,0 +1,16 @@
+import { defineStore } from 'pinia';
+
+/**
+ * 后端返回原始路由(未处理时)
+ * @methods setCacheKeepAlive 设置接口原始路由数据
+ */
+export const useRequestOldRoutes = defineStore('requestOldRoutes', {
+	state: (): RequestOldRoutesState => ({
+		requestOldRoutes: [],
+	}),
+	actions: {
+		async setRequestOldRoutes(routes: Array<string>) {
+			this.requestOldRoutes = routes;
+		},
+	},
+});
Added +26 -0
diff --git a/src/stores/routesList.ts b/src/stores/routesList.ts
new file mode 100644
index 0000000..5e3a249
--- /dev/null
+++ b/src/stores/routesList.ts
@@ -0,0 +1,26 @@
+import { defineStore } from 'pinia';
+
+/**
+ * 路由列表
+ * @methods setRoutesList 设置路由数据
+ * @methods setColumnsMenuHover 设置分栏布局菜单鼠标移入 boolean
+ * @methods setColumnsNavHover 设置分栏布局最左侧导航鼠标移入 boolean
+ */
+export const useRoutesList = defineStore('routesList', {
+	state: (): RoutesListState => ({
+		routesList: [],
+		isColumnsMenuHover: false,
+		isColumnsNavHover: false,
+	}),
+	actions: {
+		async setRoutesList(data: Array<string>) {
+			this.routesList = data;
+		},
+		async setColumnsMenuHover(bool: Boolean) {
+			this.isColumnsMenuHover = bool;
+		},
+		async setColumnsNavHover(bool: Boolean) {
+			this.isColumnsNavHover = bool;
+		},
+	},
+});
Added +23 -0
diff --git a/src/stores/tagsViewRoutes.ts b/src/stores/tagsViewRoutes.ts
new file mode 100644
index 0000000..cfb3e39
--- /dev/null
+++ b/src/stores/tagsViewRoutes.ts
@@ -0,0 +1,23 @@
+import { defineStore } from 'pinia';
+import { Session } from '/@/utils/storage';
+
+/**
+ * TagsView 路由列表
+ * @methods setTagsViewRoutes 设置 TagsView 路由列表
+ * @methods setCurrenFullscreen 设置开启/关闭全屏时的 boolean 状态
+ */
+export const useTagsViewRoutes = defineStore('tagsViewRoutes', {
+	state: (): TagsViewRoutesState => ({
+		tagsViewRoutes: [],
+		isTagsViewCurrenFull: false,
+	}),
+	actions: {
+		async setTagsViewRoutes(data: Array<string>) {
+			this.tagsViewRoutes = data;
+		},
+		setCurrenFullscreen(bool: Boolean) {
+			Session.set('isTagsViewCurrenFull', bool);
+			this.isTagsViewCurrenFull = bool;
+		},
+	},
+});
Added +157 -0
diff --git a/src/stores/themeConfig.ts b/src/stores/themeConfig.ts
new file mode 100644
index 0000000..0213a49
--- /dev/null
+++ b/src/stores/themeConfig.ts
@@ -0,0 +1,157 @@
+import { defineStore } from 'pinia';
+
+/**
+ * 布局配置
+ * 修复:https://gitee.com/lyt-top/vue-next-admin/issues/I567R1,感谢@lanbao123
+ * 2020.05.28 by lyt 优化。开发时配置不生效问题
+ * 修改配置时:
+ * 1、需要每次都清理 `window.localStorage` 浏览器永久缓存
+ * 2、或者点击布局配置最底部 `一键恢复默认` 按钮即可看到效果
+ */
+export const useThemeConfig = defineStore('themeConfig', {
+	state: (): ThemeConfigState => ({
+		// themeConfig: {
+		// 	// 是否开启布局配置抽屉
+		// 	isDrawer: false,
+
+		// 	/**
+		// 	 * 全局主题
+		// 	 */
+		// 	// 默认 primary 主题颜色
+		// 	primary: '#409eff',
+		// 	// 是否开启深色模式
+		// 	isIsDark: false,
+
+		// 	/**
+		// 	 * 顶栏设置
+		// 	 */
+		// 	// 默认顶栏导航背景颜色
+		// 	topBar: '#ffffff',
+		// 	// 默认顶栏导航字体颜色
+		// 	topBarColor: '#606266',
+		// 	// 是否开启顶栏背景颜色渐变
+		// 	isTopBarColorGradual: false,
+
+		// 	/**
+		// 	 * 菜单设置
+		// 	 */
+		// 	// 默认菜单导航背景颜色
+		// 	menuBar: '#545c64',
+		// 	// 默认菜单导航字体颜色
+		// 	menuBarColor: '#eaeaea',
+		// 	// 默认菜单高亮背景色
+		// 	menuBarActiveColor: 'rgba(0, 0, 0, 0.2)',
+		// 	// 是否开启菜单背景颜色渐变
+		// 	isMenuBarColorGradual: false,
+
+		// 	/**
+		// 	 * 分栏设置
+		// 	 */
+		// 	// 默认分栏菜单背景颜色
+		// 	columnsMenuBar: '#545c64',
+		// 	// 默认分栏菜单字体颜色
+		// 	columnsMenuBarColor: '#e6e6e6',
+		// 	// 是否开启分栏菜单背景颜色渐变
+		// 	isColumnsMenuBarColorGradual: false,
+		// 	// 是否开启分栏菜单鼠标悬停预加载(预览菜单)
+		// 	isColumnsMenuHoverPreload: false,
+
+		// 	/**
+		// 	 * 界面设置
+		// 	 */
+		// 	// 是否开启菜单水平折叠效果
+		// 	isCollapse: false,
+		// 	// 是否开启菜单手风琴效果
+		// 	isUniqueOpened: true,
+		// 	// 是否开启固定 Header
+		// 	isFixedHeader: false,
+		// 	// 初始化变量,用于更新菜单 el-scrollbar 的高度,请勿删除
+		// 	isFixedHeaderChange: false,
+		// 	// 是否开启经典布局分割菜单(仅经典布局生效)
+		// 	isClassicSplitMenu: false,
+		// 	// 是否开启自动锁屏
+		// 	isLockScreen: false,
+		// 	// 开启自动锁屏倒计时(s/秒)
+		// 	lockScreenTime: 30,
+
+		// 	/**
+		// 	 * 界面显示
+		// 	 */
+		// 	// 是否开启侧边栏 Logo
+		// 	isShowLogo: false,
+		// 	// 初始化变量,用于 el-scrollbar 的高度更新,请勿删除
+		// 	isShowLogoChange: false,
+		// 	// 是否开启 Breadcrumb,强制经典、横向布局不显示
+		// 	isBreadcrumb: true,
+		// 	// 是否开启 Tagsview
+		// 	isTagsview: true,
+		// 	// 是否开启 Breadcrumb 图标
+		// 	isBreadcrumbIcon: false,
+		// 	// 是否开启 Tagsview 图标
+		// 	isTagsviewIcon: false,
+		// 	// 是否开启 TagsView 缓存
+		// 	isCacheTagsView: false,
+		// 	// 是否开启 TagsView 拖拽
+		// 	isSortableTagsView: true,
+		// 	// 是否开启 TagsView 共用
+		// 	isShareTagsView: false,
+		// 	// 是否开启 Footer 底部版权信息
+		// 	isFooter: false,
+		// 	// 是否开启灰色模式
+		// 	isGrayscale: false,
+		// 	// 是否开启色弱模式
+		// 	isInvert: false,
+		// 	// 是否开启水印
+		// 	isWartermark: true,
+		// 	// 水印文案
+		// 	wartermarkText: 'vue-next-admin',
+
+		// 	/**
+		// 	 * 其它设置
+		// 	 */
+		// 	// Tagsview 风格:可选值"<tags-style-one|tags-style-four|tags-style-five>",默认 tags-style-five
+		// 	// 定义的值与 `/src/layout/navBars/tagsView/tagsView.vue` 中的 class 同名
+		// 	tagsStyle: 'tags-style-five',
+		// 	// 主页面切换动画:可选值"<slide-right|slide-left|opacitys>",默认 slide-right
+		// 	animation: 'slide-right',
+		// 	// 分栏高亮风格:可选值"<columns-round|columns-card>",默认 columns-round
+		// 	columnsAsideStyle: 'columns-round',
+		// 	// 分栏布局风格:可选值"<columns-horizontal|columns-vertical>",默认 columns-horizontal
+		// 	columnsAsideLayout: 'columns-vertical',
+
+		// 	/**
+		// 	 * 布局切换
+		// 	 * 注意:为了演示,切换布局时,颜色会被还原成默认,代码位置:/@/layout/navBars/breadcrumb/setings.vue
+		// 	 * 中的 `initSetLayoutChange(设置布局切换,重置主题样式)` 方法
+		// 	 */
+		// 	// 布局切换:可选值"<defaults|classic|transverse|columns>",默认 defaults
+		// 	layout: 'defaults',
+
+		// 	/**
+		// 	 * 后端控制路由
+		// 	 */
+		// 	// 是否开启后端控制路由
+		// 	isRequestRoutes: false,
+
+		// 	/**
+		// 	 * 全局网站标题 / 副标题
+		// 	 */
+		// 	// 网站主标题(菜单导航、浏览器当前网页标题)
+		// 	globalTitle: 'vue-next-admin',
+		// 	// 网站副标题(登录页顶部文字)
+		// 	globalViceTitle: 'vueNextAdmin',
+		// 	// 网站副标题(登录页顶部文字)
+		// 	globalViceTitleMsg: '专注、免费、开源、维护、解疑',
+		// 	// 默认初始语言,可选值"<zh-cn|en|zh-tw>",默认 zh-cn
+		// 	globalI18n: 'zh-cn',
+		// 	// 默认全局组件大小,可选值"<large|'default'|small>",默认 'large'
+		// 	globalComponentSize: 'large',
+		// },
+		themeConfig: {"isDrawer":false,"primary":"#523DFF","isIsDark":false,"topBar":"#ffffff","topBarColor":"#606266","isTopBarColorGradual":false,"menuBar":"#FCFCFC","menuBarColor":"#454545","menuBarActiveColor":"rgba(255, 255, 255, 1)","isMenuBarColorGradual":false,"columnsMenuBar":"#545c64","columnsMenuBarColor":"#e6e6e6","isColumnsMenuBarColorGradual":false,"isColumnsMenuHoverPreload":false,"isCollapse":false,"isUniqueOpened":true,"isFixedHeader":true,"isFixedHeaderChange":false,"isClassicSplitMenu":false,"isLockScreen":false,"lockScreenTime":29,"isShowLogo":true,"isShowLogoChange":false,"isBreadcrumb":false,"isTagsview":true,"isBreadcrumbIcon":false,"isTagsviewIcon":false,"isCacheTagsView":false,"isSortableTagsView":true,"isShareTagsView":false,"isFooter":false,"isGrayscale":false,"isInvert":false,"isWartermark":false,"wartermarkText":"新生命平台","tagsStyle":"tags-style-one","animation":"slide-right","columnsAsideStyle":"columns-round","columnsAsideLayout":"columns-vertical","layout":"classic","isRequestRoutes":true,"globalTitle":"新生命平台","globalViceTitle":"新生命平台","globalViceTitleMsg":"","globalI18n":"zh-cn","globalComponentSize":"default"}
+	}),
+	actions: {
+		setThemeConfig(data: ThemeConfigState) {
+			this.themeConfig = data.themeConfig;
+		},
+	},
+});
Added +50 -0
diff --git a/src/stores/userInfo.ts b/src/stores/userInfo.ts
new file mode 100644
index 0000000..17eecde
--- /dev/null
+++ b/src/stores/userInfo.ts
@@ -0,0 +1,50 @@
+import { defineStore } from 'pinia';
+import Cookies from 'js-cookie';
+import { Session } from '/@/utils/storage';
+import { useUserApi } from '../api/user';
+const userApi = useUserApi();
+
+/**
+ * 用户信息
+ * @methods setUserInfos 设置用户信息
+ */
+export const useUserInfo = defineStore('userInfo', {
+	state: (): UserInfosState => ({
+		userInfos: {
+			userName: '',
+			photo: '',
+			time: 0,
+			roles: [],
+			authBtnList: [],
+		},
+	}),
+	actions: {
+		async setUserInfos() {
+			// 存储用户信息到浏览器缓存
+			if (Session.get('userInfo')) {
+				this.userInfos = Session.get('userInfo');
+			} else {
+				const userInfos: any = await this.getApiUserInfo();
+				this.userInfos = userInfos;
+			}
+		},
+		// 模拟接口数据
+		// https://gitee.com/lyt-top/vue-next-admin/issues/I5F1HP
+		async getApiUserInfo() {
+			return new Promise((resolve) => {
+				userApi.info().then(res => {		
+					let defaultBtnRoles: Array<string> = res.data.permission.split(',');
+					// let defaultAuthBtnList: Array<string> = [];
+					const userInfos = {
+						userName: res.data.name,
+						photo: res.data.avatar,
+						time: new Date().getTime(),
+						// roles: [],
+						authBtnList: defaultBtnRoles,
+					};
+					resolve(userInfos);
+				})
+			});
+		},
+	},
+});
Added +324 -0
diff --git a/src/theme/app.scss b/src/theme/app.scss
new file mode 100644
index 0000000..ef9d351
--- /dev/null
+++ b/src/theme/app.scss
@@ -0,0 +1,324 @@
+/* 初始化样式
+------------------------------- */
+* {
+	margin: 0;
+	padding: 0;
+	box-sizing: border-box;
+	outline: none !important;
+}
+
+:root {
+	--next-color-white: #ffffff;
+	--next-bg-main-color: #f8f8f8;
+	--next-bg-color: #f5f5ff;
+	--next-border-color-light: #f1f2f3;
+	--next-color-primary-lighter: #ecf5ff;
+	--next-color-success-lighter: #f0f9eb;
+	--next-color-warning-lighter: #fdf6ec;
+	--next-color-danger-lighter: #fef0f0;
+	--next-color-dark-hover: #0000001a;
+	--next-color-menu-hover: rgba(0, 0, 0, 0.2);
+	--next-color-user-hover: rgba(0, 0, 0, 0.04);
+	--next-color-seting-main: #e9eef3;
+	--next-color-seting-aside: #d3dce6;
+	--next-color-seting-header: #b3c0d1;
+}
+
+html,
+body,
+#app {
+	margin: 0;
+	padding: 0;
+	width: 100%;
+	height: 100%;
+	font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, SimSun, sans-serif;
+	font-weight: 400;
+	-webkit-font-smoothing: antialiased;
+	-webkit-tap-highlight-color: transparent;
+	background-color: var(--next-bg-main-color);
+	font-size: 14px;
+	overflow: hidden;
+	position: relative;
+}
+
+/* 主布局样式
+------------------------------- */
+.layout-container {
+	width: 100%;
+	height: 100%;
+	.layout-pd {
+		padding: 15px !important;
+	}
+	.layout-flex {
+		display: flex;
+		flex-direction: column;
+		flex: 1;
+	}
+	.layout-aside {
+		background: var(--next-bg-menuBar);
+		box-shadow: 2px 0 6px rgb(0 21 41 / 1%);
+		height: inherit;
+		position: relative;
+		z-index: 1;
+		display: flex;
+		flex-direction: column;
+		overflow-x: hidden !important;
+		.el-scrollbar__view {
+			overflow: hidden;
+		}
+	}
+	.layout-header {
+		padding: 0 !important;
+		height: auto !important;
+	}
+	.layout-main {
+		padding: 0 !important;
+		overflow: hidden;
+		width: 100%;
+		background-color: var(--next-bg-main-color);
+		display: flex;
+		flex-direction: column;
+		// 内层 el-scrollbar样式,用于界面高度自适应(main.vue)
+		.layout-main-scroll {
+			@extend .layout-flex;
+			.layout-parent {
+				@extend .layout-flex;
+				position: relative;
+			}
+		}
+	}
+	// 用于界面高度自适应
+	.layout-padding {
+		@extend .layout-pd;
+		position: absolute;
+		left: 0;
+		top: 0;
+		height: 100%;
+		overflow: hidden;
+		@extend .layout-flex;
+		&-auto {
+			height: inherit;
+			@extend .layout-flex;
+		}
+		&-view {
+			background: var(--el-color-white);
+			width: 100%;
+			height: 100%;
+			border-radius: 4px;
+			border: 1px solid var(--el-border-color-light, #ebeef5);
+			overflow: hidden;
+		}
+	}
+	// 用于界面高度自适应,主视图区 main 的内边距,用于 iframe
+	.layout-padding-unset {
+		padding: 0 !important;
+		&-view {
+			border-radius: 0 !important;
+			border: none !important;
+		}
+	}
+	// 用于设置 iframe loading 时的高度(loading 垂直居中显示)
+	.layout-iframe {
+		.el-loading-parent--relative {
+			height: 100%;
+		}
+	}
+	.el-scrollbar {
+		width: 100%;
+	}
+	.layout-el-aside-br-color {
+		border-right: 1px solid var(--el-border-color-light, #ebeef5);
+	}
+	// pc端左侧导航样式
+	.layout-aside-pc-220 {
+		width: 220px !important;
+		transition: width 0.3s ease;
+	}
+	.layout-aside-pc-64 {
+		width: 64px !important;
+		transition: width 0.3s ease;
+	}
+	.layout-aside-pc-1 {
+		width: 1px !important;
+		transition: width 0.3s ease;
+	}
+	// 手机端左侧导航样式
+	.layout-aside-mobile {
+		position: fixed;
+		top: 0;
+		left: -220px;
+		width: 220px;
+		z-index: 9999999;
+	}
+	.layout-aside-mobile-close {
+		left: -220px;
+		transition: all 0.3s cubic-bezier(0.39, 0.58, 0.57, 1);
+	}
+	.layout-aside-mobile-open {
+		left: 0;
+		transition: all 0.3s cubic-bezier(0.22, 0.61, 0.36, 1);
+	}
+	.layout-aside-mobile-mode {
+		position: fixed;
+		top: 0;
+		right: 0;
+		bottom: 0;
+		left: 0;
+		height: 100%;
+		background-color: rgba(0, 0, 0, 0.5);
+		z-index: 9999998;
+		animation: error-img 0.3s;
+	}
+	.layout-mian-height-50 {
+		height: calc(100vh - 50px);
+	}
+	.layout-columns-warp {
+		flex: 1;
+		display: flex;
+		overflow: hidden;
+	}
+	.layout-hide {
+		display: none;
+	}
+}
+
+/* element plus 全局样式
+------------------------------- */
+.layout-breadcrumb-seting {
+	.el-divider {
+		background-color: rgb(230, 230, 230);
+	}
+}
+
+/* nprogress 进度条跟随主题颜色
+------------------------------- */
+#nprogress {
+	.bar {
+		background: var(--el-color-primary) !important;
+		z-index: 9999999 !important;
+	}
+}
+
+/* flex 弹性布局
+------------------------------- */
+.flex {
+	display: flex;
+}
+.flex-auto {
+	flex: 1;
+	overflow: hidden;
+}
+.flex-center {
+	@extend .flex;
+	flex-direction: column;
+	width: 100%;
+	overflow: hidden;
+}
+.flex-margin {
+	margin: auto;
+}
+.flex-warp {
+	display: flex;
+	flex-wrap: wrap;
+	align-content: flex-start;
+	margin: 0 -5px;
+	.flex-warp-item {
+		padding: 5px;
+		.flex-warp-item-box {
+			width: 100%;
+			height: 100%;
+		}
+	}
+}
+
+/* cursor 鼠标形状
+------------------------------- */
+// 默认
+.cursor-default {
+	cursor: default !important;
+}
+// 帮助
+.cursor-help {
+	cursor: help !important;
+}
+// 手指
+.cursor-pointer {
+	cursor: pointer !important;
+}
+// 移动
+.cursor-move {
+	cursor: move !important;
+}
+
+/* 宽高 100%
+------------------------------- */
+.w100 {
+	width: 100% !important;
+}
+.h100 {
+	height: 100% !important;
+}
+.vh100 {
+	height: 100vh !important;
+}
+.max100vh {
+	max-height: 100vh !important;
+}
+.min100vh {
+	min-height: 100vh !important;
+}
+
+/* 颜色值
+------------------------------- */
+.color-primary {
+	color: var(--el-color-primary);
+}
+.color-success {
+	color: var(--el-color-success);
+}
+.color-warning {
+	color: var(--el-color-warning);
+}
+.color-danger {
+	color: var(--el-color-danger);
+}
+.color-info {
+	color: var(--el-color-info);
+}
+
+/* 字体大小全局样式
+------------------------------- */
+@for $i from 10 through 32 {
+	.font#{$i} {
+		font-size: #{$i}px !important;
+	}
+}
+
+/* 外边距、内边距全局样式
+------------------------------- */
+@for $i from 1 through 35 {
+	.mt#{$i} {
+		margin-top: #{$i}px !important;
+	}
+	.mr#{$i} {
+		margin-right: #{$i}px !important;
+	}
+	.mb#{$i} {
+		margin-bottom: #{$i}px !important;
+	}
+	.ml#{$i} {
+		margin-left: #{$i}px !important;
+	}
+	.pt#{$i} {
+		padding-top: #{$i}px !important;
+	}
+	.pr#{$i} {
+		padding-right: #{$i}px !important;
+	}
+	.pb#{$i} {
+		padding-bottom: #{$i}px !important;
+	}
+	.pl#{$i} {
+		padding-left: #{$i}px !important;
+	}
+}
Added +147 -0
diff --git a/src/theme/common/transition.scss b/src/theme/common/transition.scss
new file mode 100644
index 0000000..b3609cd
--- /dev/null
+++ b/src/theme/common/transition.scss
@@ -0,0 +1,147 @@
+/* 页面切换动画
+------------------------------- */
+.slide-right-enter-active,
+.slide-right-leave-active,
+.slide-left-enter-active,
+.slide-left-leave-active {
+	will-change: transform;
+	transition: all 0.3s ease;
+}
+// slide-right
+.slide-right-enter-from {
+	opacity: 0;
+	transform: translateX(-20px);
+}
+.slide-right-leave-to {
+	opacity: 0;
+	transform: translateX(20px);
+}
+// slide-left
+.slide-left-enter-from {
+	@extend .slide-right-leave-to;
+}
+.slide-left-leave-to {
+	@extend .slide-right-enter-from;
+}
+// opacitys
+.opacitys-enter-active,
+.opacitys-leave-active {
+	will-change: transform;
+	transition: all 0.3s ease;
+}
+.opacitys-enter-from,
+.opacitys-leave-to {
+	opacity: 0;
+}
+
+/* Breadcrumb 面包屑过渡动画
+------------------------------- */
+.breadcrumb-enter-active,
+.breadcrumb-leave-active {
+	transition: all 0.5s ease;
+}
+.breadcrumb-enter-from,
+.breadcrumb-leave-active {
+	opacity: 0;
+	transform: translateX(20px);
+}
+.breadcrumb-leave-active {
+	position: absolute;
+	z-index: -1;
+}
+
+/* logo 过渡动画
+------------------------------- */
+@keyframes logoAnimation {
+	0% {
+		transform: scale(0);
+	}
+	80% {
+		transform: scale(1.2);
+	}
+	100% {
+		transform: scale(1);
+	}
+}
+
+/* 404、401 过渡动画
+------------------------------- */
+@keyframes error-num {
+	0% {
+		transform: translateY(60px);
+		opacity: 0;
+	}
+	100% {
+		transform: translateY(0);
+		opacity: 1;
+	}
+}
+@keyframes error-img {
+	0% {
+		opacity: 0;
+	}
+	100% {
+		opacity: 1;
+	}
+}
+@keyframes error-img-two {
+	0% {
+		opacity: 1;
+	}
+	100% {
+		opacity: 0;
+	}
+}
+
+/* 登录页动画
+------------------------------- */
+@keyframes loginLeft {
+	0% {
+		left: -100%;
+	}
+	50%,
+	100% {
+		left: 100%;
+	}
+}
+@keyframes loginTop {
+	0% {
+		top: -100%;
+	}
+	50%,
+	100% {
+		top: 100%;
+	}
+}
+@keyframes loginRight {
+	0% {
+		right: -100%;
+	}
+	50%,
+	100% {
+		right: 100%;
+	}
+}
+@keyframes loginBottom {
+	0% {
+		bottom: -100%;
+	}
+	50%,
+	100% {
+		bottom: 100%;
+	}
+}
+
+/* 左右左 link.vue
+------------------------------- */
+@keyframes toRight {
+	0% {
+		left: -5px;
+	}
+	50% {
+		left: 100%;
+	}
+	100% {
+		left: -5px;
+	}
+}
Added +249 -0
diff --git a/src/theme/dark.scss b/src/theme/dark.scss
new file mode 100644
index 0000000..ef7534b
--- /dev/null
+++ b/src/theme/dark.scss
@@ -0,0 +1,249 @@
+/* 深色模式样式
+------------------------------- */
+[data-theme='dark'] {
+	// 变量(自定义时,只需修改这里的值)
+	--next-bg-main: #1f1f1f;
+	--next-color-white: #ffffff;
+	--next-color-disabled: #191919;
+	--next-color-bar: #dadada;
+	--next-color-primary: #303030;
+	--next-border-color: #424242;
+	--next-border-black: #333333;
+	--next-border-columns: #2a2a2a;
+	--next-color-seting: #505050;
+	--next-text-color-regular: #9b9da1;
+	--next-text-color-placeholder: #7a7a7a;
+	--next-color-hover: #3c3c3c;
+	--next-color-hover-rgba: rgba(0, 0, 0, 0.3);
+
+	// root
+	--next-bg-main-color: var(--next-bg-main) !important;
+	--next-bg-topBar: var(--next-color-disabled) !important;
+	--next-bg-topBarColor: var(--next-color-bar) !important;
+	--next-bg-menuBar: var(--next-color-disabled) !important;
+	--next-bg-menuBarColor: var(--next-color-bar) !important;
+	--next-bg-menuBarActiveColor: var(--next-color-hover-rgba) !important;
+	--next-bg-columnsMenuBar: var(--next-color-disabled) !important;
+	--next-bg-columnsMenuBarColor: var(--next-color-bar) !important;
+	--next-border-color-light: var(--next-border-black) !important;
+	--next-color-primary-lighter: var(--next-color-primary) !important;
+	--next-color-success-lighter: var(--next-color-primary) !important;
+	--next-color-warning-lighter: var(--next-color-primary) !important;
+	--next-color-danger-lighter: var(--next-color-primary) !important;
+	--next-bg-color: var(--next-color-primary) !important;
+	--next-color-dark-hover: var(--next-color-hover) !important;
+	--next-color-menu-hover: var(--next-color-hover-rgba) !important;
+	--next-color-user-hover: var(--next-color-hover-rgba) !important;
+	--next-color-seting-main: var(--next-color-seting) !important;
+	--next-color-seting-aside: var(--next-color-hover) !important;
+	--next-color-seting-header: var(--next-color-primary) !important;
+
+	// element plus
+	--el-color-white: var(--next-color-disabled) !important;
+	--el-text-color-primary: var(--next-color-bar) !important;
+	--el-border-color: var(--next-border-black) !important;
+	--el-border-color-light: var(--next-border-black) !important;
+	--el-border-color-lighter: var(--next-border-black) !important;
+	--el-border-color-extra-light: var(--el-color-primary-light-8) !important;
+	--el-text-color-regular: var(--next-text-color-regular) !important;
+	--el-bg-color: var(--next-color-disabled) !important;
+	--el-color-primary-light-9: var(--next-color-hover) !important;
+	--el-text-color-disabled: var(--next-text-color-placeholder) !important;
+	--el-text-color-disabled-base: var(--el-color-primary) !important;
+	--el-text-color-placeholder: var(--next-text-color-placeholder) !important;
+	--el-disabled-bg-color: var(--next-color-disabled) !important;
+	--el-fill-base: var(--next-color-white) !important;
+	--el-fill-colo: var(--next-color-hover-rgba) !important;
+	--el-fill-color: var(--next-color-hover-rgba) !important;
+	--el-fill-color-blank: var(--next-color-disabled) !important;
+	--el-fill-color-light: var(--next-color-hover-rgba) !important;
+	--el-bg-color-overlay: var(--el-color-primary-light-9) !important;
+	--el-mask-color: rgb(42 42 42 / 80%);
+	--el-fill-color-lighter: var(--next-color-hover-rgba) !important;
+
+	// button
+	.el-button {
+		&:hover {
+			border-color: var(--next-border-color) !important;
+		}
+	}
+	.el-button--primary,
+	.el-button--info,
+	.el-button--danger,
+	.el-button--success,
+	.el-button--warning {
+		--el-button-text-color: var(--next-color-white) !important;
+		--el-button-hover-text-color: var(--next-color-white) !important;
+		--el-button-disabled-text-color: var(--next-color-white) !important;
+		&:hover {
+			border-color: var(--el-button-hover-border-color, var(--el-button-hover-bg-color)) !important;
+		}
+	}
+
+	// drawer
+	.el-divider__text {
+		background-color: var(--el-color-white) !important;
+	}
+	.el-drawer {
+		border-left: 1px solid var(--next-border-color-light) !important;
+	}
+
+	// tabs
+	.el-tabs--border-card {
+		background-color: var(--el-color-white) !important;
+	}
+	.el-tabs--border-card > .el-tabs__header .el-tabs__item.is-active {
+		background: var(--next-color-primary-lighter);
+	}
+
+	// alert / notice-bar
+	.home-card-item {
+		border: 1px solid var(--next-border-color-light) !important;
+	}
+	.el-alert,
+	.notice-bar {
+		border: 1px solid var(--next-border-color) !important;
+		background-color: var(--next-color-disabled) !important;
+	}
+
+	// menu
+	.layout-aside {
+		border-right: 1px solid var(--next-border-color-light) !important;
+	}
+
+	// colorPicker
+	.el-color-picker__mask {
+		background: unset !important;
+	}
+	.el-color-picker__trigger {
+		border: 1px solid var(--next-border-color-light) !important;
+	}
+
+	// popper / dropdown
+	.el-popper {
+		border: 1px solid var(--next-border-color) !important;
+		color: var(--el-text-color-primary) !important;
+		.el-popper__arrow:before {
+			background: var(--el-color-white) !important;
+			border: 1px solid var(--next-border-color);
+		}
+		a {
+			color: var(--el-text-color-primary) !important;
+		}
+	}
+	.el-popper,
+	.el-dropdown-menu {
+		background: var(--el-color-white) !important;
+	}
+	.el-dropdown-menu__item:hover:not(.is-disabled) {
+		background: var(--el-bg-color) !important;
+	}
+	.el-dropdown-menu__item.is-disabled {
+		font-weight: 700 !important;
+	}
+
+	// input
+	.el-input-group__append,
+	.el-input-group__prepend {
+		border: var(--el-input-border) !important;
+		border-right: none !important;
+		background: var(--next-color-disabled) !important;
+		border-left: 0 !important;
+	}
+	.el-input-number__decrease,
+	.el-input-number__increase {
+		background: var(--next-color-disabled) !important;
+	}
+
+	// tag
+	.el-select .el-select__tags .el-tag {
+		background-color: var(--next-bg-color) !important;
+	}
+
+	// pagination
+	.el-pagination.is-background .el-pager li:not(.disabled).active {
+		color: var(--next-color-white) !important;
+	}
+	.el-pagination.is-background .btn-next,
+	.el-pagination.is-background .btn-prev,
+	.el-pagination.is-background .el-pager li {
+		background-color: var(--next-bg-color);
+	}
+	/*深色模式时分页高亮问题*/
+	.el-pagination.is-background .btn-next.is-active,
+	.el-pagination.is-background .btn-prev.is-active,
+	.el-pagination.is-background .el-pager li.is-active {
+		color: var(--next-color-white) !important;
+	}
+
+	// radio
+	.el-radio-button:not(.is-active) .el-radio-button__inner {
+		border: 1px solid var(--next-border-color-light) !important;
+		border-left: 0 !important;
+	}
+	.el-radio-button.is-active .el-radio-button__inner {
+		color: var(--next-color-white) !important;
+	}
+
+	// countup
+	.countup-card-item-flex {
+		color: var(--el-text-color-primary) !important;
+	}
+
+	// editor
+	.editor-container {
+		.w-e-toolbar {
+			background: var(--el-color-white) !important;
+			border: 1px solid var(--next-border-color-light) !important;
+			.w-e-menu:hover {
+				background: var(--next-color-user-hover) !important;
+				i {
+					color: var(--el-text-color-primary) !important;
+				}
+			}
+		}
+		.w-e-text-container {
+			border: 1px solid var(--next-border-color-light) !important;
+			border-top: none !important;
+			.w-e-text {
+				background: var(--el-color-white) !important;
+			}
+		}
+	}
+
+	// date-picker
+	.el-picker-panel {
+		background: var(--el-color-white) !important;
+	}
+
+	// dialog
+	.el-dialog {
+		border: 1px solid var(--el-border-color-lighter);
+		.el-dialog__header {
+			color: var(--el-text-color-primary) !important;
+		}
+	}
+
+	// columns
+	.layout-columns-aside ul .layout-columns-active {
+		color: var(--next-color-white) !important;
+	}
+	.layout-columns-aside {
+		border-right: 1px solid var(--next-border-columns);
+	}
+
+	// tagsView
+	.tags-style-one {
+		.is-active {
+			color: var(--el-text-color-primary) !important;
+		}
+		.layout-navbars-tagsview-ul-li:hover {
+			border-color: var(--el-border-color-lighter) !important;
+		}
+	}
+
+	// loading
+	.el-loading-mask {
+		background-color: var(--next-bg-main) !important;
+	}
+}
Added +333 -0
diff --git a/src/theme/element.scss b/src/theme/element.scss
new file mode 100644
index 0000000..fa4e78b
--- /dev/null
+++ b/src/theme/element.scss
@@ -0,0 +1,333 @@
+@import 'mixins/index.scss';
+
+/* Button 按钮
+------------------------------- */
+// 第三方字体图标大小
+.el-button:not(.is-circle) i.el-icon,
+.el-button i.iconfont,
+.el-button i.fa,
+.el-button--default i.iconfont,
+.el-button--default i.fa {
+	font-size: 14px !important;
+	margin-right: 5px;
+}
+.el-button--small i.iconfont,
+.el-button--small i.fa {
+	font-size: 12px !important;
+	margin-right: 5px;
+}
+
+/* Input 输入框、InputNumber 计数器
+------------------------------- */
+// 菜单搜索
+.el-autocomplete-suggestion__wrap {
+	max-height: 280px !important;
+}
+
+/* Form 表单
+------------------------------- */
+.el-form {
+	// 用于修改弹窗时表单内容间隔太大问题,如系统设置的新增菜单弹窗里的表单内容
+	// .el-form-item:last-of-type {
+	// 	margin-bottom: 0 !important;
+	// }
+	// 修复行内表单最后一个 el-form-item 位置下移问题
+	&.el-form--inline {
+		.el-form-item--large.el-form-item:last-of-type {
+			margin-bottom: 22px !important;
+		}
+		.el-form-item--default.el-form-item:last-of-type,
+		.el-form-item--small.el-form-item:last-of-type {
+			margin-bottom: 18px !important;
+		}
+	}
+	// https://gitee.com/lyt-top/vue-next-admin/issues/I5K1PM
+	.el-form-item .el-form-item__label .el-icon {
+		margin-right: 0px;
+	}
+}
+
+/* Alert 警告
+------------------------------- */
+.el-alert {
+	border: 1px solid;
+}
+.el-alert__title {
+	word-break: break-all;
+}
+
+/* Message 消息提示
+------------------------------- */
+.el-message {
+	min-width: unset !important;
+	padding: 15px !important;
+	box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.02);
+}
+
+/* NavMenu 导航菜单
+------------------------------- */
+// 鼠标 hover 时颜色
+.el-menu-hover-bg-color {
+	background-color: var(--next-bg-menuBarActiveColor) !important;
+}
+// 默认样式修改
+.el-menu {
+	border-right: none !important;
+	width: 220px;
+}
+.el-menu-item {
+	height: 56px !important;
+	line-height: 56px !important;
+}
+.el-menu-item,
+.el-sub-menu__title {
+	color: var(--next-bg-menuBarColor);
+}
+// 修复点击左侧菜单折叠再展开时,宽度不跟随问题
+.el-menu--collapse {
+	width: 64px !important;
+}
+// 外部链接时
+.el-menu-item a,
+.el-menu-item a:hover,
+.el-menu-item i,
+.el-sub-menu__title i {
+	color: inherit;
+	text-decoration: none;
+}
+// 第三方图标字体间距/大小设置
+.el-menu-item .iconfont,
+.el-sub-menu .iconfont,
+.el-menu-item .fa,
+.el-sub-menu .fa {
+	@include generalIcon;
+}
+// 水平菜单、横向菜单高亮 背景色,鼠标 hover 时,有子级菜单的背景色
+.el-menu-item.is-active,
+.el-sub-menu.is-active .el-sub-menu__title,
+.el-sub-menu:not(.is-opened):hover .el-sub-menu__title {
+	@extend .el-menu-hover-bg-color;
+}
+.el-menu-item:hover {
+	@extend .el-menu-hover-bg-color;
+}
+.el-sub-menu.is-active.is-opened .el-sub-menu__title {
+	background-color: unset !important;
+}
+
+.el-menu>.el-sub-menu.is-active[aria-expanded="false"]>.el-sub-menu__title {
+	color: var(--el-color-primary);
+	background-color: var(--next-bg-menuBarActiveColor) !important;
+}
+.el-sub-menu__title:hover,
+.el-menu-item:hover,
+.el-sub-menu.is-active.is-opened .el-sub-menu__title:hover {
+	color: var(--el-color-primary-light-3);
+	background-color: var(--next-bg-menuBarActiveColor) !important;
+}
+
+// 子级菜单背景颜色
+// .el-menu--inline {
+// 	background: var(--next-bg-menuBar-light-1);
+// }
+// 水平菜单、横向菜单折叠 a 标签
+.el-popper.is-dark a {
+	color: var(--el-color-white) !important;
+	text-decoration: none;
+}
+// 水平菜单、横向菜单折叠背景色
+.el-popper.is-pure.is-light {
+	// 水平菜单
+	.el-menu--vertical {
+		background: var(--next-bg-menuBar);
+		.el-sub-menu.is-active .el-sub-menu__title {
+			color: var(--el-menu-active-color);
+		}
+		.el-popper.is-pure.is-light {
+			.el-menu--vertical {
+				.el-sub-menu .el-sub-menu__title {
+					background-color: unset !important;
+					color: var(--next-bg-menuBarColor);
+				}
+				.el-sub-menu.is-active .el-sub-menu__title {
+					color: var(--el-menu-active-color);
+				}
+			}
+		}
+	}
+	// 横向菜单
+	.el-menu--horizontal {
+		background: var(--next-bg-topBar);
+		.el-menu-item,
+		.el-sub-menu {
+			height: 50px !important;
+			line-height: 50px !important;
+			color: var(--next-bg-topBarColor);
+			.el-sub-menu__title {
+				height: 50px !important;
+				line-height: 50px !important;
+				color: var(--next-bg-topBarColor);
+			}
+			.el-popper.is-pure.is-light {
+				.el-menu--horizontal {
+					.el-sub-menu .el-sub-menu__title {
+						background-color: unset !important;
+						color: var(--next-bg-topBarColor);
+					}
+					.el-sub-menu.is-active .el-sub-menu__title {
+						color: var(--el-menu-active-color);
+					}
+				}
+			}
+		}
+		.el-menu-item.is-active,
+		.el-sub-menu.is-active .el-sub-menu__title {
+			color: var(--el-menu-active-color);
+		}
+	}
+}
+// 横向菜单(经典、横向)布局
+.el-menu.el-menu--horizontal {
+	border-bottom: none !important;
+	width: 100% !important;
+	.el-menu-item,
+	.el-sub-menu__title {
+		height: 50px !important;
+		color: var(--next-bg-topBarColor);
+	}
+	.el-menu-item:not(.is-active):hover,
+	.el-sub-menu:not(.is-active):hover .el-sub-menu__title {
+		color: var(--next-bg-topBarColor);
+	}
+}
+
+/* Tabs 标签页
+------------------------------- */
+.el-tabs__nav-wrap::after {
+	height: 1px !important;
+}
+
+/* Dropdown 下拉菜单
+------------------------------- */
+.el-dropdown-menu {
+	list-style: none !important; /*修复 Dropdown 下拉菜单样式问题 2022.03.04*/
+}
+.el-dropdown-menu .el-dropdown-menu__item {
+	white-space: nowrap;
+	&:not(.is-disabled):hover {
+		background-color: var(--el-dropdown-menuItem-hover-fill);
+		color: var(--el-dropdown-menuItem-hover-color);
+	}
+}
+
+/* Steps 步骤条
+------------------------------- */
+.el-step__icon-inner {
+	font-size: 30px !important;
+	font-weight: 400 !important;
+}
+.el-step__title {
+	font-size: 14px;
+}
+
+/* Dialog 对话框
+------------------------------- */
+.el-overlay {
+	overflow: hidden;
+	.el-overlay-dialog {
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		position: unset !important;
+		width: 100%;
+		height: 100%;
+		.el-dialog {
+			margin: 0 auto !important;
+			position: absolute;
+			.el-dialog__body {
+				// padding: 20px !important;
+			}
+		}
+	}
+}
+.el-dialog__body {
+	max-height: calc(90vh - 111px) !important;
+	overflow-y: auto;
+	overflow-x: hidden;
+}
+
+/* Card 卡片
+------------------------------- */
+.el-card__header {
+	padding: 15px 20px;
+}
+
+/* Table 表格 element plus 2.2.0 版本
+------------------------------- */
+.el-table {
+	.el-button.is-text {
+		padding: 0;
+	}
+}
+
+/* scrollbar
+------------------------------- */
+.el-scrollbar__bar {
+	z-index: 4;
+}
+/*防止页面切换时,滚动条高度不变的问题(滚动条高度非滚动条滚动高度)*/
+.el-scrollbar__wrap {
+	max-height: 100%;
+}
+.el-select-dropdown .el-scrollbar__wrap {
+	overflow-x: scroll !important;
+}
+/*修复Select 选择器高度问题*/
+.el-select-dropdown__wrap {
+	max-height: 274px !important;
+}
+/*修复Cascader 级联选择器高度问题*/
+.el-cascader-menu__wrap.el-scrollbar__wrap {
+	height: 204px !important;
+}
+/*用于界面高度自适应(main.vue),区分 scrollbar__view,防止其它使用 scrollbar 的地方出现滚动条消失*/
+.layout-container-view .el-scrollbar__view {
+	height: 100%;
+}
+/*防止分栏布局二级菜单很多时,滚动条消失问题*/
+.layout-columns-warp .layout-aside .el-scrollbar__view {
+	height: unset !important;
+}
+
+/* Pagination 分页
+------------------------------- */
+.el-pagination__editor {
+	margin-right: 8px;
+}
+/*深色模式时分页高亮问题*/
+.el-pagination.is-background .btn-next.is-active,
+.el-pagination.is-background .btn-prev.is-active,
+.el-pagination.is-background .el-pager li.is-active {
+	background-color: var(--el-color-primary) !important;
+	color: var(--el-color-white) !important;
+}
+
+/* Drawer 抽屉
+------------------------------- */
+.el-drawer {
+	--el-drawer-padding-primary: unset !important;
+	.el-drawer__header {
+		padding: 0 15px !important;
+		height: 50px;
+		display: flex;
+		align-items: center;
+		margin-bottom: 0 !important;
+		border-bottom: 1px solid var(--el-border-color);
+		color: var(--el-text-color-primary);
+	}
+	.el-drawer__body {
+		width: 100%;
+		height: 100%;
+		overflow: auto;
+	}
+}
Added +31 -0
diff --git a/src/theme/iconSelector.scss b/src/theme/iconSelector.scss
new file mode 100644
index 0000000..569f614
--- /dev/null
+++ b/src/theme/iconSelector.scss
@@ -0,0 +1,31 @@
+/* Popover 弹出框(图标选择器)
+------------------------------- */
+.icon-selector-popper {
+	padding: 0 !important;
+	.icon-selector-warp {
+		height: 260px;
+		overflow: hidden;
+		position: relative;
+		.icon-selector-warp-title {
+			position: absolute;
+			height: 40px;
+			line-height: 40px;
+			left: 15px;
+		}
+		.el-tabs__header {
+			display: flex;
+			justify-content: flex-end;
+			padding: 0 15px;
+			border-bottom: 1px solid var(--el-border-color-light);
+			margin: 0 !important;
+			.el-tabs__nav-wrap {
+				&::after {
+					height: 0 !important;
+				}
+				.el-tabs__item {
+					padding: 0 5px !important;
+				}
+			}
+		}
+	}
+}
Added +8 -0
diff --git a/src/theme/index.scss b/src/theme/index.scss
new file mode 100644
index 0000000..f5d59ae
--- /dev/null
+++ b/src/theme/index.scss
@@ -0,0 +1,8 @@
+@import './app.scss';
+@import 'common/transition.scss';
+@import './other.scss';
+@import './element.scss';
+@import './media/media.scss';
+@import './waves.scss';
+@import './dark.scss';
+
Added +51 -0
diff --git a/src/theme/loading.scss b/src/theme/loading.scss
new file mode 100644
index 0000000..c28c7b9
--- /dev/null
+++ b/src/theme/loading.scss
@@ -0,0 +1,51 @@
+.loading-next {
+	width: 100%;
+	height: 100%;
+}
+.loading-next .loading-next-box {
+	position: absolute;
+	top: 50%;
+	left: 50%;
+	transform: translate(-50%, -50%);
+}
+.loading-next .loading-next-box-warp {
+	width: 80px;
+	height: 80px;
+}
+.loading-next .loading-next-box-warp .loading-next-box-item {
+	width: 33.333333%;
+	height: 33.333333%;
+	background: var(--el-color-primary);
+	float: left;
+	animation: loading-next-animation 1.2s infinite ease;
+	border-radius: 1px;
+}
+.loading-next .loading-next-box-warp .loading-next-box-item:nth-child(7) {
+	animation-delay: 0s;
+}
+.loading-next .loading-next-box-warp .loading-next-box-item:nth-child(4),
+.loading-next .loading-next-box-warp .loading-next-box-item:nth-child(8) {
+	animation-delay: 0.1s;
+}
+.loading-next .loading-next-box-warp .loading-next-box-item:nth-child(1),
+.loading-next .loading-next-box-warp .loading-next-box-item:nth-child(5),
+.loading-next .loading-next-box-warp .loading-next-box-item:nth-child(9) {
+	animation-delay: 0.2s;
+}
+.loading-next .loading-next-box-warp .loading-next-box-item:nth-child(2),
+.loading-next .loading-next-box-warp .loading-next-box-item:nth-child(6) {
+	animation-delay: 0.3s;
+}
+.loading-next .loading-next-box-warp .loading-next-box-item:nth-child(3) {
+	animation-delay: 0.4s;
+}
+@keyframes loading-next-animation {
+	0%,
+	70%,
+	100% {
+		transform: scale3D(1, 1, 1);
+	}
+	35% {
+		transform: scale3D(0, 0, 1);
+	}
+}
Added +94 -0
diff --git a/src/theme/media/chart.scss b/src/theme/media/chart.scss
new file mode 100644
index 0000000..8485e39
--- /dev/null
+++ b/src/theme/media/chart.scss
@@ -0,0 +1,94 @@
+@import './index.scss';
+
+/* 页面宽度小于768px
+------------------------------- */
+@media screen and (max-width: $sm) {
+	.big-data-down-left {
+		width: 100% !important;
+		flex-direction: unset !important;
+		flex-wrap: wrap;
+		.flex-warp-item {
+			min-height: 196.24px;
+			padding: 0 7.5px 15px 15px !important;
+			.flex-warp-item-box {
+				border: none !important;
+				border-bottom: 1px solid #ebeef5 !important;
+			}
+		}
+	}
+	.big-data-down-center {
+		width: 100% !important;
+		.big-data-down-center-one,
+		.big-data-down-center-two {
+			min-height: 196.24px;
+			padding-left: 15px !important;
+			.big-data-down-center-one-content {
+				border: none !important;
+				border-bottom: 1px solid #ebeef5 !important;
+			}
+			.flex-warp-item-box {
+				@extend .big-data-down-center-one-content;
+			}
+		}
+	}
+	.big-data-down-right {
+		.flex-warp-item {
+			.flex-warp-item-box {
+				border: none !important;
+				border-bottom: 1px solid #ebeef5 !important;
+			}
+			&:nth-of-type(2) {
+				padding-left: 15px !important;
+			}
+			&:last-of-type {
+				.flex-warp-item-box {
+					border: none !important;
+				}
+			}
+		}
+	}
+}
+
+/* 页面宽度大于768px小于1200px
+------------------------------- */
+@media screen and (min-width: $sm) and (max-width: $lg) {
+	.chart-warp-bottom {
+		.big-data-down-left {
+			width: 50% !important;
+		}
+		.big-data-down-center {
+			width: 50% !important;
+		}
+		.big-data-down-right {
+			.flex-warp-item {
+				width: 50% !important;
+				&:nth-of-type(2) {
+					padding-left: 7.5px !important;
+				}
+			}
+		}
+	}
+}
+
+/* 页面宽度小于1200px
+------------------------------- */
+@media screen and (max-width: $lg) {
+	.chart-warp-top {
+		.up-left {
+			display: none;
+		}
+	}
+	.chart-warp-bottom {
+		overflow-y: auto !important;
+		flex-wrap: wrap;
+		.big-data-down-right {
+			width: 100% !important;
+			flex-direction: unset !important;
+			flex-wrap: wrap;
+			.flex-warp-item {
+				min-height: 196.24px;
+				padding: 0 7.5px 15px 15px !important;
+			}
+		}
+	}
+}
Added +10 -0
diff --git a/src/theme/media/cityLinkage.scss b/src/theme/media/cityLinkage.scss
new file mode 100644
index 0000000..1394156
--- /dev/null
+++ b/src/theme/media/cityLinkage.scss
@@ -0,0 +1,10 @@
+@import './index.scss';
+
+/* 页面宽度小于576px
+------------------------------- */
+@media screen and (max-width: $xs) {
+	.el-cascader__dropdown.el-popper {
+		overflow: auto;
+		max-width: 100%;
+	}
+}
Added +25 -0
diff --git a/src/theme/media/date.scss b/src/theme/media/date.scss
new file mode 100644
index 0000000..1a50397
--- /dev/null
+++ b/src/theme/media/date.scss
@@ -0,0 +1,25 @@
+@import './index.scss';
+
+/* 页面宽度小于768px
+------------------------------- */
+@media screen and (max-width: $sm) {
+	// 时间选择器适配
+	.el-date-range-picker {
+		width: 100vw;
+		.el-picker-panel__body {
+			min-width: 100%;
+			.el-date-range-picker__content {
+				.el-date-range-picker__header div {
+					margin-left: 22px;
+					margin-right: 0px;
+				}
+				& + .el-date-range-picker__content {
+					.el-date-range-picker__header div {
+						margin-left: 0px;
+						margin-right: 22px;
+					}
+				}
+			}
+		}
+	}
+}
Added +12 -0
diff --git a/src/theme/media/dialog.scss b/src/theme/media/dialog.scss
new file mode 100644
index 0000000..023ccae
--- /dev/null
+++ b/src/theme/media/dialog.scss
@@ -0,0 +1,12 @@
+@import './index.scss';
+
+/* 页面宽度小于800px
+------------------------------- */
+@media screen and (max-width: 800px) {
+	.el-dialog {
+		width: 90% !important;
+	}
+	.el-dialog.is-fullscreen {
+		width: 100% !important;
+	}
+}
Added +45 -0
diff --git a/src/theme/media/error.scss b/src/theme/media/error.scss
new file mode 100644
index 0000000..f35015f
--- /dev/null
+++ b/src/theme/media/error.scss
@@ -0,0 +1,45 @@
+@import './index.scss';
+
+/* 页面宽度小于768px
+------------------------------- */
+@media screen and (max-width: $sm) {
+	.error {
+		.error-flex {
+			flex-direction: column-reverse !important;
+			height: auto !important;
+			width: 100% !important;
+		}
+		.right,
+		.left {
+			flex: unset !important;
+			display: flex !important;
+		}
+		.left-item {
+			margin: auto !important;
+		}
+		.right img {
+			max-width: 450px !important;
+			@extend .left-item;
+		}
+	}
+}
+
+/* 页面宽度大于768px小于992px
+------------------------------- */
+@media screen and (min-width: $sm) and (max-width: $md) {
+	.error {
+		.error-flex {
+			padding-left: 30px !important;
+		}
+	}
+}
+
+/* 页面宽度小于1200px
+------------------------------- */
+@media screen and (max-width: $lg) {
+	.error {
+		.error-flex {
+			padding: 0 30px;
+		}
+	}
+}
Added +31 -0
diff --git a/src/theme/media/form.scss b/src/theme/media/form.scss
new file mode 100644
index 0000000..eb1d883
--- /dev/null
+++ b/src/theme/media/form.scss
@@ -0,0 +1,31 @@
+@import './index.scss';
+
+/* 页面宽度小于576px
+------------------------------- */
+@media screen and (max-width: $xs) {
+	.el-form-item__label {
+		width: 100% !important;
+		text-align: left !important;
+		// 移动端 label 右对齐问题
+		justify-content: flex-start !important;
+	}
+	.el-form-item__content {
+		margin-left: 0 !important;
+	}
+	.el-form-item {
+		// 响应式表单时,登录页需要重新处理
+		display: unset !important;
+	}
+	// 表格演示中的表单筛选
+	.table-form-btn {
+		display: flex !important;
+		.el-form-item__label {
+			width: auto !important;
+		}
+	}
+	// 表格演示中的表单筛选最大高度,适配移动端
+	.table-search-container {
+		max-height: 160px;
+		overflow: auto;
+	}
+}
Added +23 -0
diff --git a/src/theme/media/home.scss b/src/theme/media/home.scss
new file mode 100644
index 0000000..5a2417e
--- /dev/null
+++ b/src/theme/media/home.scss
@@ -0,0 +1,23 @@
+@import './index.scss';
+
+/* 页面宽度小于768px
+------------------------------- */
+@media screen and (max-width: $sm) {
+	.home-media,
+	.home-media-sm {
+		margin-top: 15px;
+	}
+}
+
+/* 页面宽度小于1200px
+------------------------------- */
+@media screen and (max-width: $lg) {
+	.home-media-lg {
+		margin-top: 15px;
+	}
+	.home-monitor {
+		.flex-warp-item {
+			width: 33.33% !important;
+		}
+	}
+}
Added +15 -0
diff --git a/src/theme/media/index.scss b/src/theme/media/index.scss
new file mode 100644
index 0000000..4761c0c
--- /dev/null
+++ b/src/theme/media/index.scss
@@ -0,0 +1,15 @@
+/* 栅格布局(媒体查询变量)
+* https://developer.mozilla.org/zh-CN/docs/Learn/CSS/CSS_layout/Media_queries
+* $us ≥376px  响应式栅格
+* $xs ≥576px  响应式栅格
+* $sm ≥768px  响应式栅格
+* $md ≥992px  响应式栅格
+* $lg ≥1200px 响应式栅格
+* $xl ≥1920px 响应式栅格
+------------------------------- */
+$us: 376px;
+$xs: 576px;
+$sm: 768px;
+$md: 992px;
+$lg: 1200px;
+$xl: 1920px;
Added +59 -0
diff --git a/src/theme/media/layout.scss b/src/theme/media/layout.scss
new file mode 100644
index 0000000..df8ce56
--- /dev/null
+++ b/src/theme/media/layout.scss
@@ -0,0 +1,59 @@
+@import './index.scss';
+
+/* 页面宽度小于576px
+------------------------------- */
+@media screen and (max-width: $xs) {
+	// MessageBox 弹框
+	.el-message-box {
+		width: 80% !important;
+	}
+}
+
+/* 页面宽度小于768px
+------------------------------- */
+@media screen and (max-width: $sm) {
+	// Breadcrumb 面包屑
+	.layout-navbars-breadcrumb-hide {
+		display: none;
+	}
+	// 外链视图
+	.layout-view-link {
+		a {
+			max-width: 80%;
+			text-align: center;
+		}
+	}
+	// 菜单搜索
+	.layout-search-dialog {
+		.el-autocomplete {
+			width: 80% !important;
+		}
+	}
+}
+
+/* 页面宽度小于1000px
+------------------------------- */
+@media screen and (max-width: 1000px) {
+	// 布局配置
+	.layout-drawer-content-flex {
+		position: relative;
+		&::after {
+			content: '手机版不支持切换布局';
+			position: absolute;
+			top: 0;
+			right: 0;
+			bottom: 0;
+			left: 0;
+			z-index: 1;
+			text-align: center;
+			height: 140px;
+			line-height: 140px;
+			background: rgba(255, 255, 255, 0.9);
+			color: #666666;
+		}
+	}
+	// pagination 分页中的工具栏
+	.table-footer-tool {
+		display: none !important;
+	}
+}
Added +74 -0
diff --git a/src/theme/media/login.scss b/src/theme/media/login.scss
new file mode 100644
index 0000000..29cdbb0
--- /dev/null
+++ b/src/theme/media/login.scss
@@ -0,0 +1,74 @@
+@import './index.scss';
+
+/* 页面宽度小于1200px
+------------------------------- */
+@media screen and (max-width: $lg) and (min-width: $xs) {
+	.login-container {
+		.login-left {
+			.login-left-img {
+				top: 90% !important;
+				left: 12% !important;
+				width: 30% !important;
+				height: 18% !important;
+			}
+		}
+		.login-right {
+			position: absolute;
+			top: 50%;
+			left: 50%;
+			transform: translate(-50%, -50%);
+		}
+	}
+}
+
+/* 页面宽度小于576px
+------------------------------- */
+@media screen and (max-width: $xs) {
+	.login-container {
+		.login-left {
+			display: none;
+		}
+		.login-right {
+			width: 100% !important;
+			.login-right-warp {
+				width: 100% !important;
+				height: 100% !important;
+				border: none !important;
+				.login-right-warp-mian {
+					.el-form-item {
+						display: flex !important;
+					}
+					.login-right-warp-main-title {
+						font-size: 20px !important;
+					}
+				}
+				.login-right-warp-one {
+					&::after {
+						right: 0 !important;
+					}
+				}
+				.login-right-warp-two {
+					&::before {
+						bottom: 1px !important;
+					}
+				}
+			}
+		}
+	}
+}
+
+/* 页面宽度小于375px
+------------------------------- */
+@media screen and (max-width: $us) {
+	.login-container {
+		.login-right {
+			.login-right-warp {
+				.login-right-warp-mian {
+					.login-right-warp-main-title {
+						font-size: 18px !important;
+					}
+				}
+			}
+		}
+	}
+}
Added +13 -0
diff --git a/src/theme/media/media.scss b/src/theme/media/media.scss
new file mode 100644
index 0000000..bed1c35
--- /dev/null
+++ b/src/theme/media/media.scss
@@ -0,0 +1,13 @@
+@import './login.scss';
+@import './error.scss';
+@import './layout.scss';
+@import './personal.scss';
+@import './tagsView.scss';
+@import './home.scss';
+@import './chart.scss';
+@import './form.scss';
+@import './scrollbar.scss';
+@import './pagination.scss';
+@import './dialog.scss';
+@import './cityLinkage.scss';
+@import './date.scss';
Added +15 -0
diff --git a/src/theme/media/pagination.scss b/src/theme/media/pagination.scss
new file mode 100644
index 0000000..37af75f
--- /dev/null
+++ b/src/theme/media/pagination.scss
@@ -0,0 +1,15 @@
+@import './index.scss';
+
+/* 页面宽度小于576px
+------------------------------- */
+@media screen and (max-width: $xs) {
+	.el-pager,
+	.el-pagination__jump {
+		display: none !important;
+	}
+	// 默认居中对齐
+	.el-pagination,
+	.table-footer {
+		justify-content: center !important;
+	}
+}
Added +16 -0
diff --git a/src/theme/media/personal.scss b/src/theme/media/personal.scss
new file mode 100644
index 0000000..7ec0d4a
--- /dev/null
+++ b/src/theme/media/personal.scss
@@ -0,0 +1,16 @@
+@import './index.scss';
+
+/* 页面宽度小于768px
+------------------------------- */
+@media screen and (max-width: $sm) {
+	.personal-info {
+		padding-left: 0 !important;
+		margin-top: 15px;
+	}
+	.personal-recommend-col {
+		margin-bottom: 15px;
+		&:last-of-type {
+			margin-bottom: 0;
+		}
+	}
+}
Added +56 -0
diff --git a/src/theme/media/scrollbar.scss b/src/theme/media/scrollbar.scss
new file mode 100644
index 0000000..968a79d
--- /dev/null
+++ b/src/theme/media/scrollbar.scss
@@ -0,0 +1,56 @@
+@import './index.scss';
+
+/* 页面宽度小于768px
+------------------------------- */
+@media screen and (max-width: $sm) {
+	// 滚动条的宽度
+	::-webkit-scrollbar {
+		width: 3px !important;
+		height: 3px !important;
+	}
+	::-webkit-scrollbar-track-piece {
+		background-color: var(--next-bg-main-color);
+	}
+	// 滚动条的设置
+	::-webkit-scrollbar-thumb {
+		background-color: rgba(144, 147, 153, 0.3);
+		background-clip: padding-box;
+		min-height: 28px;
+		border-radius: 5px;
+		transition: 0.3s background-color;
+	}
+	::-webkit-scrollbar-thumb:hover {
+		background-color: rgba(144, 147, 153, 0.5);
+	}
+	// element plus scrollbar
+	.el-scrollbar__bar.is-vertical {
+		width: 2px !important;
+	}
+	.el-scrollbar__bar.is-horizontal {
+		height: 2px !important;
+	}
+}
+
+/* 页面宽度大于768px
+------------------------------- */
+@media screen and (min-width: 769px) {
+	// 滚动条的宽度
+	::-webkit-scrollbar {
+		width: 7px;
+		height: 7px;
+	}
+	::-webkit-scrollbar-track-piece {
+		background-color: var(--next-bg-main-color);
+	}
+	// 滚动条的设置
+	::-webkit-scrollbar-thumb {
+		background-color: rgba(144, 147, 153, 0.3);
+		background-clip: padding-box;
+		min-height: 28px;
+		border-radius: 5px;
+		transition: 0.3s background-color;
+	}
+	::-webkit-scrollbar-thumb:hover {
+		background-color: rgba(144, 147, 153, 0.5);
+	}
+}
Added +11 -0
diff --git a/src/theme/media/tagsView.scss b/src/theme/media/tagsView.scss
new file mode 100644
index 0000000..b71674e
--- /dev/null
+++ b/src/theme/media/tagsView.scss
@@ -0,0 +1,11 @@
+@import './index.scss';
+
+/* 页面宽度小于768px
+------------------------------- */
+@media screen and (max-width: $sm) {
+	.tags-view-form {
+		.tags-view-form-col {
+			margin-bottom: 20px;
+		}
+	}
+}
Added +56 -0
diff --git a/src/theme/mixins/index.scss b/src/theme/mixins/index.scss
new file mode 100644
index 0000000..61f3c6b
--- /dev/null
+++ b/src/theme/mixins/index.scss
@@ -0,0 +1,56 @@
+/* 第三方图标字体间距/大小设置
+------------------------------- */
+@mixin generalIcon {
+	font-size: 14px !important;
+	display: inline-block;
+	vertical-align: middle;
+	margin-right: 5px;
+	width: 24px;
+	text-align: center;
+	justify-content: center;
+}
+
+/* 文本不换行
+------------------------------- */
+@mixin text-no-wrap() {
+	text-overflow: ellipsis;
+	overflow: hidden;
+	white-space: nowrap;
+}
+
+/* 多行文本溢出
+  ------------------------------- */
+@mixin text-ellipsis($line: 2) {
+	overflow: hidden;
+	word-break: break-all;
+	text-overflow: ellipsis;
+	display: -webkit-box;
+	-webkit-line-clamp: $line;
+	-webkit-box-orient: vertical;
+}
+
+/* 滚动条(页面未使用) div 中使用:
+  ------------------------------- */
+// .test {
+//   @include scrollBar;
+// }
+@mixin scrollBar {
+	// 滚动条凹槽的颜色,还可以设置边框属性
+	&::-webkit-scrollbar-track-piece {
+		background-color: #f8f8f8;
+	}
+	// 滚动条的宽度
+	&::-webkit-scrollbar {
+		width: 9px;
+		height: 9px;
+	}
+	// 滚动条的设置
+	&::-webkit-scrollbar-thumb {
+		background-color: #dddddd;
+		background-clip: padding-box;
+		min-height: 28px;
+	}
+	&::-webkit-scrollbar-thumb:hover {
+		background-color: #bbb;
+	}
+}
Added +36 -0
diff --git a/src/theme/other.scss b/src/theme/other.scss
new file mode 100644
index 0000000..bbc3c61
--- /dev/null
+++ b/src/theme/other.scss
@@ -0,0 +1,36 @@
+/* wangeditor 富文本编辑器
+------------------------------- */
+.editor-container {
+	z-index: 10; // 用于 wangeditor 点击全屏时
+	.w-e-toolbar {
+		border: 1px solid var(--el-border-color-light, #ebeef5) !important;
+		border-bottom: 1px solid var(--el-border-color-light, #ebeef5) !important;
+		border-top-left-radius: 3px;
+		border-top-right-radius: 3px;
+		z-index: 2 !important;
+	}
+	.w-e-text-container {
+		border: 1px solid var(--el-border-color-light, #ebeef5) !important;
+		border-top: none !important;
+		border-bottom-left-radius: 3px;
+		border-bottom-right-radius: 3px;
+		z-index: 1 !important;
+	}
+}
+
+[data-theme='dark'] {
+	// textarea - css vars
+	--w-e-textarea-bg-color: var(--el-color-white) !important;
+	--w-e-textarea-color: var(--el-text-color-primary) !important;
+
+	// toolbar - css vars
+	--w-e-toolbar-color: var(--el-text-color-primary) !important;
+	--w-e-toolbar-bg-color: var(--el-color-white) !important;
+	--w-e-toolbar-active-color: var(--el-text-color-primary) !important;
+	--w-e-toolbar-active-bg-color: var(--next-color-menu-hover) !important;
+	--w-e-toolbar-border-color: var(--el-border-color-light, #ebeef5) !important;
+
+	// modal - css vars
+	--w-e-modal-button-bg-color: var(--el-color-primary) !important;
+	--w-e-modal-button-border-color: var(--el-color-primary) !important;
+}
Added +27 -0
diff --git a/src/theme/tableTool.scss b/src/theme/tableTool.scss
new file mode 100644
index 0000000..d5ab55e
--- /dev/null
+++ b/src/theme/tableTool.scss
@@ -0,0 +1,27 @@
+.table-tool-popper {
+	padding: 0 !important;
+	.tool-box {
+		display: flex;
+		border-bottom: 1px solid var(--el-border-color-lighter);
+		box-sizing: border-box;
+		color: var(--el-text-color-primary);
+		height: 40px;
+		align-items: center;
+	}
+	.tool-sortable {
+		max-height: 303px;
+		.tool-sortable-item {
+			display: flex;
+			box-sizing: border-box;
+			color: var(--el-text-color-primary);
+			align-items: center;
+			padding: 0 12px;
+			&:hover {
+				background: var(--el-fill-color-lighter);
+			}
+			i {
+				opacity: 0.7;
+			}
+		}
+	}
+}
Added +3 -0
diff --git a/src/theme/tailwind.scss b/src/theme/tailwind.scss
new file mode 100644
index 0000000..bd6213e
--- /dev/null
+++ b/src/theme/tailwind.scss
@@ -0,0 +1,3 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
\ No newline at end of file
Added +101 -0
diff --git a/src/theme/waves.scss b/src/theme/waves.scss
new file mode 100644
index 0000000..23add2c
--- /dev/null
+++ b/src/theme/waves.scss
@@ -0,0 +1,101 @@
+/* Waves v0.6.0
+* http://fian.my.id/Waves
+*
+* Copyright 2014 Alfiana E. Sibuea and other contributors
+* Released under the MIT license
+* https://github.com/fians/Waves/blob/master/LICENSE
+*/
+.waves-effect {
+	position: relative;
+	cursor: pointer;
+	display: inline-block;
+	overflow: hidden;
+	-webkit-user-select: none;
+	-moz-user-select: none;
+	-ms-user-select: none;
+	user-select: none;
+	-webkit-tap-highlight-color: transparent;
+	vertical-align: middle;
+	z-index: 1;
+	will-change: opacity, transform;
+	transition: all 0.3s ease-out;
+}
+.waves-effect .waves-ripple {
+	position: absolute;
+	border-radius: 50%;
+	width: 20px;
+	height: 20px;
+	margin-top: -10px;
+	margin-left: -10px;
+	opacity: 0;
+	background: rgba(0, 0, 0, 0.2);
+	transition: all 0.7s ease-out;
+	transition-property: opacity, -webkit-transform;
+	transition-property: transform, opacity;
+	transition-property: transform, opacity, -webkit-transform;
+	-webkit-transform: scale(0);
+	transform: scale(0);
+	pointer-events: none;
+}
+.waves-effect.waves-light .waves-ripple {
+	background-color: rgba(255, 255, 255, 0.45);
+}
+.waves-effect.waves-red .waves-ripple {
+	background-color: rgba(244, 67, 54, 0.7);
+}
+.waves-effect.waves-yellow .waves-ripple {
+	background-color: rgba(255, 235, 59, 0.7);
+}
+.waves-effect.waves-orange .waves-ripple {
+	background-color: rgba(255, 152, 0, 0.7);
+}
+.waves-effect.waves-purple .waves-ripple {
+	background-color: rgba(156, 39, 176, 0.7);
+}
+.waves-effect.waves-green .waves-ripple {
+	background-color: rgba(76, 175, 80, 0.7);
+}
+.waves-effect.waves-teal .waves-ripple {
+	background-color: rgba(0, 150, 136, 0.7);
+}
+.waves-effect input[type='button'],
+.waves-effect input[type='reset'],
+.waves-effect input[type='submit'] {
+	border: 0;
+	font-style: normal;
+	font-size: inherit;
+	text-transform: inherit;
+	background: none;
+}
+.waves-notransition {
+	transition: none !important;
+}
+.waves-circle {
+	-webkit-transform: translateZ(0);
+	transform: translateZ(0);
+	-webkit-mask-image: -webkit-radial-gradient(circle, #fff 100%, #000 100%);
+}
+.waves-input-wrapper {
+	border-radius: 0.2em;
+	vertical-align: bottom;
+}
+.waves-input-wrapper .waves-button-input {
+	position: relative;
+	top: 0;
+	left: 0;
+	z-index: 1;
+}
+.waves-circle {
+	text-align: center;
+	width: 2.5em;
+	height: 2.5em;
+	line-height: 2.5em;
+	border-radius: 50%;
+	-webkit-mask-image: none;
+}
+.waves-block {
+	display: block;
+}
+a.waves-effect .waves-ripple {
+	z-index: -1;
+}
Added +13 -0
diff --git a/src/types/axios.d.ts b/src/types/axios.d.ts
new file mode 100644
index 0000000..bcd0a21
--- /dev/null
+++ b/src/types/axios.d.ts
@@ -0,0 +1,13 @@
+/* eslint-disable */
+import * as axios from 'axios';
+
+// 扩展 axios 数据返回类型,可自行扩展
+declare module 'axios' {
+	export interface AxiosResponse<T = any> {
+		code: number;
+		data: T;
+		message: string;
+		type?: string;
+		[key: string]: T;
+	}
+}
Added +111 -0
diff --git a/src/types/global.d.ts b/src/types/global.d.ts
new file mode 100644
index 0000000..a46b866
--- /dev/null
+++ b/src/types/global.d.ts
@@ -0,0 +1,111 @@
+// 申明外部 npm 插件模块
+declare module 'vue-grid-layout';
+declare module 'qrcodejs2-fixes';
+declare module 'splitpanes';
+declare module 'js-cookie';
+declare module '@wangeditor/editor-for-vue';
+declare module 'js-table2excel';
+declare module 'qs';
+
+// 声明一个模块,防止引入文件时报错
+declare module '*.json';
+declare module '*.png';
+declare module '*.jpg';
+declare module '*.scss';
+declare module '*.ts';
+declare module '*.js';
+
+// 声明文件,*.vue 后缀的文件交给 vue 模块来处理
+declare module '*.vue' {
+	import type { DefineComponent } from 'vue';
+	const component: DefineComponent<{}, {}, any>;
+	export default component;
+}
+
+// 声明文件,定义全局变量
+/* eslint-disable */
+declare interface Window {
+	nextLoading: boolean;
+}
+
+// 声明路由当前项类型
+declare type RouteItem<T = any> = {
+	path: string;
+	name?: string | symbol | undefined | null;
+	redirect?: string;
+	k?: T;
+	meta?: {
+		title?: string;
+		isLink?: string;
+		isHide?: boolean;
+		isKeepAlive?: boolean;
+		isAffix?: boolean;
+		isIframe?: boolean;
+		roles?: string[];
+		icon?: string;
+		isDynamic?: boolean;
+		isDynamicPath?: string;
+		isIframeOpen?: string;
+		loading?: boolean;
+	};
+	children: T[];
+	query?: { [key: string]: T };
+	params?: { [key: string]: T };
+	contextMenuClickId?: string | number;
+	commonUrl?: string;
+	isFnClick?: boolean;
+	url?: string;
+	transUrl?: string;
+	title?: string;
+	id?: string | number;
+};
+
+// 声明路由 to from
+declare interface RouteToFrom<T = any> extends RouteItem {
+	path?: string;
+	children?: T[];
+}
+
+// 声明路由当前项类型集合
+declare type RouteItems<T extends RouteItem = any> = T[];
+
+// 声明 ref
+declare type RefType<T = any> = T | null;
+
+// 声明 HTMLElement
+declare type HtmlType = HTMLElement | string | undefined | null;
+
+// 申明 children 可选
+declare type ChilType<T = any> = {
+	children?: T[];
+};
+
+// 申明 数组
+declare type EmptyArrayType<T = any> = T[];
+
+// 申明 对象
+declare type EmptyObjectType<T = any> = {
+	[key: string]: T;
+};
+
+// 申明 select option
+declare type SelectOptionType = {
+	value: string | number;
+	label: string | number;
+};
+
+// 鼠标滚轮滚动类型
+declare interface WheelEventType extends WheelEvent {
+	wheelDelta: number;
+}
+
+// table 数据格式公共类型
+declare interface TableType<T = any> {
+	total: number;
+	loading: boolean;
+	param: {
+		pageNum: number;
+		pageSize: number;
+		[key: string]: T;
+	};
+}
Added +59 -0
diff --git a/src/types/layout.d.ts b/src/types/layout.d.ts
new file mode 100644
index 0000000..82904ef
--- /dev/null
+++ b/src/types/layout.d.ts
@@ -0,0 +1,59 @@
+// aside
+declare type AsideState = {
+	menuList: RouteRecordRaw[];
+	clientWidth: number;
+};
+
+// columnsAside
+declare type ColumnsAsideState<T = any> = {
+	columnsAsideList: T[];
+	liIndex: number;
+	liOldIndex: null | number;
+	liHoverIndex: null | number;
+	liOldPath: null | string;
+	difference: number;
+	routeSplit: string[];
+};
+
+// navBars breadcrumb
+declare type BreadcrumbState<T = any> = {
+	breadcrumbList: T[];
+	routeSplit: string[];
+	routeSplitFirst: string;
+	routeSplitIndex: number;
+};
+
+// navBars search
+declare type SearchState<T = any> = {
+	isShowSearch: boolean;
+	menuQuery: string;
+	tagsViewList: T[];
+};
+
+// navBars tagsView
+declare type TagsViewState<T = any> = {
+	routeActive: string | T;
+	routePath: string | unknown;
+	dropdown: {
+		x: string | number;
+		y: string | number;
+	};
+	sortable: T;
+	tagsRefsIndex: number;
+	tagsViewList: T[];
+	tagsViewRoutesList: T[];
+};
+
+// navBars parent
+declare type ParentViewState<T = any> = {
+	refreshRouterViewKey: string;
+	iframeRefreshKey: string;
+	keepAliveNameList: string[];
+	iframeList: T[];
+};
+
+// navBars link
+declare type LinkViewState = {
+	title: string;
+	isLink: string;
+};
Added +38 -0
diff --git a/src/types/mitt.d.ts b/src/types/mitt.d.ts
new file mode 100644
index 0000000..b68b80d
--- /dev/null
+++ b/src/types/mitt.d.ts
@@ -0,0 +1,38 @@
+/**
+ * mitt 事件类型定义
+ *
+ * @method openSetingsDrawer 打开布局设置弹窗
+ * @method restoreDefault 分栏布局,鼠标移入、移出数据显示
+ * @method setSendColumnsChildren 分栏布局,鼠标移入、移出菜单数据传入到 navMenu 下的菜单中
+ * @method setSendClassicChildren 经典布局,开启切割菜单时,菜单数据传入到 navMenu 下的菜单中
+ * @method getBreadcrumbIndexSetFilterRoutes 布局设置弹窗,开启切割菜单时,菜单数据传入到 navMenu 下的菜单中
+ * @method layoutMobileResize 浏览器窗口改变时,用于适配移动端界面显示
+ * @method openOrCloseSortable 布局设置弹窗,开启 TagsView 拖拽
+ * @method openShareTagsView 布局设置弹窗,开启 TagsView 共用
+ * @method onTagsViewRefreshRouterView tagsview 刷新界面
+ * @method onCurrentContextmenuClick tagsview 右键菜单每项点击时
+ */
+declare type MittType<T = any> = {
+	openSetingsDrawer?: string;
+	restoreDefault?: string;
+	setSendColumnsChildren: T;
+	setSendClassicChildren: T;
+	getBreadcrumbIndexSetFilterRoutes?: string;
+	layoutMobileResize: T;
+	openOrCloseSortable?: string;
+	openShareTagsView?: string;
+	onTagsViewRefreshRouterView?: T;
+	onCurrentContextmenuClick?: T;
+};
+
+// mitt 参数类型定义
+declare type LayoutMobileResize = {
+	layout: string;
+	clientWidth: number;
+};
+
+// mitt 参数菜单类型
+declare type MittMenu = {
+	children: RouteRecordRaw[];
+	item?: RouteItem;
+};
Added +91 -0
diff --git a/src/types/pinia.d.ts b/src/types/pinia.d.ts
new file mode 100644
index 0000000..09c6e47
--- /dev/null
+++ b/src/types/pinia.d.ts
@@ -0,0 +1,91 @@
+/**
+ * pinia 类型定义
+ */
+
+// 用户信息
+declare interface UserInfosState<T = any> {
+	userInfos: {
+		authBtnList: string[];
+		photo: string;
+		roles: string[];
+		time: number;
+		userName: string;
+		[key: string]: T;
+	};
+}
+
+// 路由缓存列表
+declare interface KeepAliveNamesState {
+	keepAliveNames: string[];
+	cachedViews: string[];
+}
+
+// 后端返回原始路由(未处理时)
+declare interface RequestOldRoutesState {
+	requestOldRoutes: string[];
+}
+
+// TagsView 路由列表
+declare interface TagsViewRoutesState<T = any> {
+	tagsViewRoutes: T[];
+	isTagsViewCurrenFull: Boolean;
+}
+
+// 路由列表
+declare interface RoutesListState<T = any> {
+	routesList: T[];
+	isColumnsMenuHover: Boolean;
+	isColumnsNavHover: Boolean;
+}
+
+// 布局配置
+declare interface ThemeConfigState {
+	themeConfig: {
+		isDrawer: boolean;
+		primary: string;
+		topBar: string;
+		topBarColor: string;
+		isTopBarColorGradual: boolean;
+		menuBar: string;
+		menuBarColor: string;
+		menuBarActiveColor: string;
+		isMenuBarColorGradual: boolean;
+		columnsMenuBar: string;
+		columnsMenuBarColor: string;
+		isColumnsMenuBarColorGradual: boolean;
+		isColumnsMenuHoverPreload: boolean;
+		isCollapse: boolean;
+		isUniqueOpened: boolean;
+		isFixedHeader: boolean;
+		isFixedHeaderChange: boolean;
+		isClassicSplitMenu: boolean;
+		isLockScreen: boolean;
+		lockScreenTime: number;
+		isShowLogo: boolean;
+		isShowLogoChange: boolean;
+		isBreadcrumb: boolean;
+		isTagsview: boolean;
+		isBreadcrumbIcon: boolean;
+		isTagsviewIcon: boolean;
+		isCacheTagsView: boolean;
+		isSortableTagsView: boolean;
+		isShareTagsView: boolean;
+		isFooter: boolean;
+		isGrayscale: boolean;
+		isInvert: boolean;
+		isIsDark: boolean;
+		isWartermark: boolean;
+		wartermarkText: string;
+		tagsStyle: string;
+		animation: string;
+		columnsAsideStyle: string;
+		columnsAsideLayout: string;
+		layout: string;
+		isRequestRoutes: boolean;
+		globalTitle: string;
+		globalViceTitle: string;
+		globalViceTitleMsg: string;
+		globalI18n: string;
+		globalComponentSize: string;
+	};
+}
Added +332 -0
diff --git a/src/types/views.d.ts b/src/types/views.d.ts
new file mode 100644
index 0000000..d41ad4f
--- /dev/null
+++ b/src/types/views.d.ts
@@ -0,0 +1,332 @@
+// import { FormConfig } from "../components/form/model/form";
+
+/**
+ * views personal
+ */
+type NewInfo = {
+	title: string;
+	date: string;
+	link: string;
+};
+type Recommend = {
+	title: string;
+	msg: string;
+	icon: string;
+	bg: string;
+	iconColor: string;
+};
+declare type PersonalState = {
+	newsInfoList: NewInfo[];
+	recommendList: Recommend[];
+	personalForm: {
+		name: string;
+		email: string;
+		autograph: string;
+		occupation: string;
+		phone: string;
+		sex: string;
+	};
+};
+
+/**
+ * views visualizing
+ */
+declare type Demo2State<T = any> = {
+	time: {
+		txt: string;
+		fun: number;
+	};
+	dropdownList: T[];
+	dropdownActive: string;
+	skyList: T[];
+	dBtnList: T[];
+	chartData4Index: number;
+	dBtnActive: number;
+	earth3DBtnList: T[];
+	chartData4List: T[];
+	myCharts: T[];
+};
+
+/**
+ * views params
+ */
+declare type ParamsState = {
+	value: string;
+	tagsViewName: string;
+	tagsViewNameIsI18n: boolean;
+};
+
+/**
+ * views system
+ */
+// role
+declare interface RowRoleType {
+	roleName: string;
+	roleSign: string;
+	describe: string;
+	sort: number;
+	status: boolean;
+	createTime: string;
+}
+
+interface SysRoleTableType extends TableType {
+	data: RowRoleType[];
+}
+
+declare interface SysRoleState {
+	tableData: SysRoleTableType;
+}
+
+declare type TreeType = {
+	id: number;
+	label: string;
+	children?: TreeType[];
+};
+
+// user
+declare type RowUserType<T = any> = {
+	userName: string;
+	userNickname: string;
+	roleSign: string;
+	department: string[];
+	phone: string;
+	email: string;
+	sex: string;
+	password: string;
+	overdueTime: T;
+	status: boolean;
+	describe: string;
+	createTime: T;
+};
+
+interface SysUserTableType extends TableType {
+	data: RowUserType[];
+}
+
+declare interface SysUserState {
+	tableData: SysUserTableType;
+}
+
+declare type DeptTreeType = {
+	deptName: string;
+	createTime: string;
+	status: boolean;
+	sort: number;
+	describe: string;
+	id: number | string;
+	children?: DeptTreeType[];
+};
+
+// dept
+declare interface RowDeptType extends DeptTreeType {
+	deptLevel: string[];
+	person: string;
+	phone: string;
+	email: string;
+}
+
+interface SysDeptTableType extends TableType {
+	data: DeptTreeType[];
+}
+
+declare interface SysDeptState {
+	tableData: SysDeptTableType;
+}
+
+// dic
+type ListType = {
+	id: number;
+	label: string;
+	value: string;
+};
+
+declare interface RowDicType {
+	dicName: string;
+	fieldName: string;
+	describe: string;
+	status: boolean;
+	createTime: string;
+	list: ListType[];
+}
+
+interface SysDicTableType extends TableType {
+	data: RowDicType[];
+}
+
+declare interface SysDicState {
+	tableData: SysDicTableType;
+}
+
+/**
+ * views pages
+ */
+//  filtering
+declare type FilteringChilType = {
+	id: number | string;
+	label: string;
+	active: boolean;
+};
+
+declare type FilterListType = {
+	img: string;
+	title: string;
+	evaluate: string;
+	collection: string;
+	price: string;
+	monSales: string;
+	id: number | string;
+	loading?: boolean;
+};
+
+declare type FilteringRowType = {
+	title: string;
+	isMore: boolean;
+	isShowMore: boolean;
+	id: number | string;
+	children: FilteringChilType[];
+};
+
+// tableRules
+declare type TableRulesHeaderType = {
+	prop: string;
+	width: string | number;
+	label: string;
+	isRequired?: boolean;
+	isTooltip?: boolean;
+	type: string;
+};
+
+declare type TableRulesState = {
+	tableData: {
+		data: EmptyObjectType[];
+		header: TableRulesHeaderType[];
+		option: SelectOptionType[];
+	};
+};
+
+declare type TableRulesOneProps = {
+	name: string;
+	email: string;
+	autograph: string;
+	occupation: string;
+};
+
+// tree
+declare type RowTreeType = {
+	id: number;
+	label: string;
+	label1: string;
+	label2: string;
+	isShow: boolean;
+	children?: RowTreeType[];
+};
+
+// workflow index
+declare type NodeListState = {
+	id: string | number;
+	nodeId: string | undefined;
+	class: HTMLElement | string;
+	left: number | string;
+	top: number | string;
+	icon: string;
+	name: string;
+};
+
+declare type LineListState = {
+	sourceId: string;
+	targetId: string;
+	label: string;
+};
+
+declare type XyState = {
+	x: string | number;
+	y: string | number;
+};
+
+declare type WorkflowState<T = any> = {
+	leftNavList: T[];
+	dropdownNode: XyState;
+	dropdownLine: XyState;
+	isShow: boolean;
+	jsPlumb: T;
+	jsPlumbNodeIndex: null | number;
+	jsplumbDefaults: T;
+	jsplumbMakeSource: T;
+	jsplumbMakeTarget: T;
+	jsplumbConnect: T;
+	jsplumbData: {
+		nodeList: NodeListState[];
+		lineList: LineListState[];
+	};
+};
+
+// workflow drawer
+declare type WorkflowDrawerNodeState<T = any> = {
+	node: { [key: string]: T };
+	nodeRules: T;
+	form: T;
+	tabsActive: string;
+	loading: {
+		extend: boolean;
+	};
+};
+
+declare type WorkflowDrawerLabelType = {
+	type: string;
+	label: string;
+};
+
+declare type WorkflowDrawerState<T = any> = {
+	isOpen: boolean;
+	nodeData: {
+		type: string;
+	};
+	jsplumbConn: T;
+};
+
+/**
+ * views make
+ */
+// tableDemo
+declare type TableDemoPageType = {
+	pageIndex: number;
+	pageSize: number;
+};
+
+declare type TableSearchType = {
+	label: string;
+	prop: string;
+	placeholder?: string;
+	required: boolean;
+	type: string;
+	options?: SelectOptionType[];
+};
+
+interface TableConfigType {
+  total: number;
+  loading: boolean;
+  isBorder: boolean;
+  isSerialNo: boolean;
+  isSelection: boolean;
+  isOperate: boolean;
+}
+
+// declare type TableDemoState = {
+// 	tableData: {
+// 		data: EmptyObjectType[];
+// 		header: TableHeaderType[];
+// 		config: {
+// 			total: number;
+// 			loading: boolean;
+// 			isBorder: boolean;
+// 			isSelection: boolean;
+// 			isSerialNo: boolean;
+// 			isOperate: boolean;
+// 		};
+// 		search: FormConfig[];
+// 		param: {
+// 			pageNum: number;
+// 			pageSize: number;
+// 		};
+// 	};
+// };
Added +65 -0
diff --git a/src/utils/arrayOperation.ts b/src/utils/arrayOperation.ts
new file mode 100644
index 0000000..08c8c31
--- /dev/null
+++ b/src/utils/arrayOperation.ts
@@ -0,0 +1,65 @@
+/**
+ * 判断两数组字符串是否相同(用于按钮权限验证),数组字符串中存在相同时会自动去重(按钮权限标识不会重复)
+ * @param news 新数据
+ * @param old 源数据
+ * @returns 两数组相同返回 `true`,反之则反
+ */
+export function judementSameArr(newArr: unknown[] | string[], oldArr: string[]): boolean {
+	const news = removeDuplicate(newArr);
+	const olds = removeDuplicate(oldArr);
+	let count = 0;
+	const leng = news.length;
+	for (let i in olds) {
+		for (let j in news) {
+			if (olds[i] === news[j]) count++;
+		}
+	}
+	return count === leng ? true : false;
+}
+
+/**
+ * 判断两个对象是否相同
+ * @param a 要比较的对象一
+ * @param b 要比较的对象二
+ * @returns 相同返回 true,反之则反
+ */
+export function isObjectValueEqual<T>(a: T, b: T): boolean {
+	if (!a || !b) return false;
+	let aProps = Object.getOwnPropertyNames(a);
+	let bProps = Object.getOwnPropertyNames(b);
+	if (aProps.length != bProps.length) return false;
+	for (let i = 0; i < aProps.length; i++) {
+		let propName = aProps[i];
+		let propA = a[propName];
+		let propB = b[propName];
+		if (!b.hasOwnProperty(propName)) return false;
+		if (propA instanceof Object) {
+			if (!isObjectValueEqual(propA, propB)) return false;
+		} else if (propA !== propB) {
+			return false;
+		}
+	}
+	return true;
+}
+
+/**
+ * 数组、数组对象去重
+ * @param arr 数组内容
+ * @param attr 需要去重的键值(数组对象)
+ * @returns
+ */
+export function removeDuplicate(arr: EmptyArrayType, attr?: string) {
+	if (!Object.keys(arr).length) {
+		return arr;
+	} else {
+		if (attr) {
+			const obj: EmptyObjectType = {};
+			return arr.reduce((cur: EmptyArrayType[], item: EmptyArrayType) => {
+				obj[item[attr]] ? '' : (obj[item[attr]] = true && item[attr] && cur.push(item));
+				return cur;
+			}, []);
+		} else {
+			return [...new Set(arr)];
+		}
+	}
+}
Added +38 -0
diff --git a/src/utils/authFunction.ts b/src/utils/authFunction.ts
new file mode 100644
index 0000000..84c0ab4
--- /dev/null
+++ b/src/utils/authFunction.ts
@@ -0,0 +1,38 @@
+import { useUserInfo } from '/@/stores/userInfo';
+import { judementSameArr } from '/@/utils/arrayOperation';
+
+/**
+ * 单个权限验证
+ * @param value 权限值
+ * @returns 有权限,返回 `true`,反之则反
+ */
+export function auth(value: string): boolean {
+	const stores = useUserInfo();
+	return stores.userInfos.authBtnList.some((v: string) => v === value);
+}
+
+/**
+ * 多个权限验证,满足一个则为 true
+ * @param value 权限值
+ * @returns 有权限,返回 `true`,反之则反
+ */
+export function auths(value: Array<string>): boolean {
+	let flag = false;
+	const stores = useUserInfo();
+	stores.userInfos.authBtnList.map((val: string) => {
+		value.map((v: string) => {
+			if (val === v) flag = true;
+		});
+	});
+	return flag;
+}
+
+/**
+ * 多个权限验证,全部满足则为 true
+ * @param value 权限值
+ * @returns 有权限,返回 `true`,反之则反
+ */
+export function authAll(value: Array<string>): boolean {
+	const stores = useUserInfo();
+	return judementSameArr(value, stores.userInfos.authBtnList);
+}
Added +66 -0
diff --git a/src/utils/commonFunction.ts b/src/utils/commonFunction.ts
new file mode 100644
index 0000000..c78fe69
--- /dev/null
+++ b/src/utils/commonFunction.ts
@@ -0,0 +1,66 @@
+// 通用函数
+import useClipboard from 'vue-clipboard3';
+import { ElMessage } from 'element-plus';
+import { formatDate } from '/@/utils/formatTime';
+import { useI18n } from 'vue-i18n';
+
+export default function () {
+	const { t } = useI18n();
+	const { toClipboard } = useClipboard();
+
+	// 百分比格式化
+	const percentFormat = (row: EmptyArrayType, column: number, cellValue: string) => {
+		return cellValue ? `${cellValue}%` : '-';
+	};
+	// 列表日期时间格式化
+	const dateFormatYMD = (row: EmptyArrayType, column: number, cellValue: string) => {
+		if (!cellValue) return '-';
+		return formatDate(new Date(cellValue), 'YYYY-mm-dd');
+	};
+	// 列表日期时间格式化
+	const dateFormatYMDHMS = (row: EmptyArrayType, column: number, cellValue: string) => {
+		if (!cellValue) return '-';
+		return formatDate(new Date(cellValue), 'YYYY-mm-dd HH:MM:SS');
+	};
+	// 列表日期时间格式化
+	const dateFormatHMS = (row: EmptyArrayType, column: number, cellValue: string) => {
+		if (!cellValue) return '-';
+		let time = 0;
+		if (typeof row === 'number') time = row;
+		if (typeof cellValue === 'number') time = cellValue;
+		return formatDate(new Date(time * 1000), 'HH:MM:SS');
+	};
+	// 小数格式化
+	const scaleFormat = (value: string = '0', scale: number = 4) => {
+		return Number.parseFloat(value).toFixed(scale);
+	};
+	// 小数格式化
+	const scale2Format = (value: string = '0') => {
+		return Number.parseFloat(value).toFixed(2);
+	};
+	// 点击复制文本
+	const copyText = (text: string) => {
+		return new Promise((resolve, reject) => {
+			try {
+				//复制
+				toClipboard(text);
+				//下面可以设置复制成功的提示框等操作
+				ElMessage.success(t('message.layout.copyTextSuccess'));
+				resolve(text);
+			} catch (e) {
+				//复制失败
+				ElMessage.error(t('message.layout.copyTextError'));
+				reject(e);
+			}
+		});
+	};
+	return {
+		percentFormat,
+		dateFormatYMD,
+		dateFormatYMDHMS,
+		dateFormatHMS,
+		scaleFormat,
+		scale2Format,
+		copyText,
+	};
+}
Added +137 -0
diff --git a/src/utils/formatTime.ts b/src/utils/formatTime.ts
new file mode 100644
index 0000000..441e30c
--- /dev/null
+++ b/src/utils/formatTime.ts
@@ -0,0 +1,137 @@
+/**
+ * 时间日期转换
+ * @param date 当前时间,new Date() 格式
+ * @param format 需要转换的时间格式字符串
+ * @description format 字符串随意,如 `YYYY-mm、YYYY-mm-dd`
+ * @description format 季度:"YYYY-mm-dd HH:MM:SS QQQQ"
+ * @description format 星期:"YYYY-mm-dd HH:MM:SS WWW"
+ * @description format 几周:"YYYY-mm-dd HH:MM:SS ZZZ"
+ * @description format 季度 + 星期 + 几周:"YYYY-mm-dd HH:MM:SS WWW QQQQ ZZZ"
+ * @returns 返回拼接后的时间字符串
+ */
+export function formatDate(date: Date, format: string): string {
+	let we = date.getDay(); // 星期
+	let z = getWeek(date); // 周
+	let qut = Math.floor((date.getMonth() + 3) / 3).toString(); // 季度
+	const opt: { [key: string]: string } = {
+		'Y+': date.getFullYear().toString(), // 年
+		'm+': (date.getMonth() + 1).toString(), // 月(月份从0开始,要+1)
+		'd+': date.getDate().toString(), // 日
+		'H+': date.getHours().toString(), // 时
+		'M+': date.getMinutes().toString(), // 分
+		'S+': date.getSeconds().toString(), // 秒
+		'q+': qut, // 季度
+	};
+	// 中文数字 (星期)
+	const week: { [key: string]: string } = {
+		'0': '日',
+		'1': '一',
+		'2': '二',
+		'3': '三',
+		'4': '四',
+		'5': '五',
+		'6': '六',
+	};
+	// 中文数字(季度)
+	const quarter: { [key: string]: string } = {
+		'1': '一',
+		'2': '二',
+		'3': '三',
+		'4': '四',
+	};
+	if (/(W+)/.test(format))
+		format = format.replace(RegExp.$1, RegExp.$1.length > 1 ? (RegExp.$1.length > 2 ? '星期' + week[we] : '周' + week[we]) : week[we]);
+	if (/(Q+)/.test(format)) format = format.replace(RegExp.$1, RegExp.$1.length == 4 ? '第' + quarter[qut] + '季度' : quarter[qut]);
+	if (/(Z+)/.test(format)) format = format.replace(RegExp.$1, RegExp.$1.length == 3 ? '第' + z + '周' : z + '');
+	for (let k in opt) {
+		let r = new RegExp('(' + k + ')').exec(format);
+		// 若输入的长度不为1,则前面补零
+		if (r) format = format.replace(r[1], RegExp.$1.length == 1 ? opt[k] : opt[k].padStart(RegExp.$1.length, '0'));
+	}
+	return format;
+}
+
+/**
+ * 获取当前日期是第几周
+ * @param dateTime 当前传入的日期值
+ * @returns 返回第几周数字值
+ */
+export function getWeek(dateTime: Date): number {
+	let temptTime = new Date(dateTime.getTime());
+	// 周几
+	let weekday = temptTime.getDay() || 7;
+	// 周1+5天=周六
+	temptTime.setDate(temptTime.getDate() - weekday + 1 + 5);
+	let firstDay = new Date(temptTime.getFullYear(), 0, 1);
+	let dayOfWeek = firstDay.getDay();
+	let spendDay = 1;
+	if (dayOfWeek != 0) spendDay = 7 - dayOfWeek + 1;
+	firstDay = new Date(temptTime.getFullYear(), 0, 1 + spendDay);
+	let d = Math.ceil((temptTime.valueOf() - firstDay.valueOf()) / 86400000);
+	let result = Math.ceil(d / 7);
+	return result;
+}
+
+/**
+ * 将时间转换为 `几秒前`、`几分钟前`、`几小时前`、`几天前`
+ * @param param 当前时间,new Date() 格式或者字符串时间格式
+ * @param format 需要转换的时间格式字符串
+ * @description param 10秒:  10 * 1000
+ * @description param 1分:   60 * 1000
+ * @description param 1小时: 60 * 60 * 1000
+ * @description param 24小时:60 * 60 * 24 * 1000
+ * @description param 3天:   60 * 60* 24 * 1000 * 3
+ * @returns 返回拼接后的时间字符串
+ */
+export function formatPast(param: string | Date, format: string = 'YYYY-mm-dd'): string {
+	// 传入格式处理、存储转换值
+	let t: any, s: number;
+	// 获取js 时间戳
+	let time: number = new Date().getTime();
+	// 是否是对象
+	typeof param === 'string' || 'object' ? (t = new Date(param).getTime()) : (t = param);
+	// 当前时间戳 - 传入时间戳
+	time = Number.parseInt(`${time - t}`);
+	if (time < 10000) {
+		// 10秒内
+		return '刚刚';
+	} else if (time < 60000 && time >= 10000) {
+		// 超过10秒少于1分钟内
+		s = Math.floor(time / 1000);
+		return `${s}秒前`;
+	} else if (time < 3600000 && time >= 60000) {
+		// 超过1分钟少于1小时
+		s = Math.floor(time / 60000);
+		return `${s}分钟前`;
+	} else if (time < 86400000 && time >= 3600000) {
+		// 超过1小时少于24小时
+		s = Math.floor(time / 3600000);
+		return `${s}小时前`;
+	} else if (time < 259200000 && time >= 86400000) {
+		// 超过1天少于3天内
+		s = Math.floor(time / 86400000);
+		return `${s}天前`;
+	} else {
+		// 超过3天
+		let date = typeof param === 'string' || 'object' ? new Date(param) : param;
+		return formatDate(date, format);
+	}
+}
+
+/**
+ * 时间问候语
+ * @param param 当前时间,new Date() 格式
+ * @description param 调用 `formatAxis(new Date())` 输出 `上午好`
+ * @returns 返回拼接后的时间字符串
+ */
+export function formatAxis(param: Date): string {
+	let hour: number = new Date(param).getHours();
+	if (hour < 6) return '凌晨好';
+	else if (hour < 9) return '早上好';
+	else if (hour < 12) return '上午好';
+	else if (hour < 14) return '中午好';
+	else if (hour < 17) return '下午好';
+	else if (hour < 19) return '傍晚好';
+	else if (hour < 22) return '晚上好';
+	else return '夜里好';
+}
Added +101 -0
diff --git a/src/utils/getStyleSheets.ts b/src/utils/getStyleSheets.ts
new file mode 100644
index 0000000..90252c3
--- /dev/null
+++ b/src/utils/getStyleSheets.ts
@@ -0,0 +1,101 @@
+import { nextTick } from 'vue';
+import * as svg from '@element-plus/icons-vue';
+
+// 获取阿里字体图标
+const getAlicdnIconfont = () => {
+	return new Promise((resolve, reject) => {
+		nextTick(() => {
+			const styles: any = document.styleSheets;
+			let sheetsList = [];
+			let sheetsIconList = [];
+			for (let i = 0; i < styles.length; i++) {
+				if (styles[i].href && styles[i].href.indexOf('at.alicdn.com') > -1) {
+					sheetsList.push(styles[i]);
+				}
+			}
+			for (let i = 0; i < sheetsList.length; i++) {
+				for (let j = 0; j < sheetsList[i].cssRules.length; j++) {
+					if (sheetsList[i].cssRules[j].selectorText && sheetsList[i].cssRules[j].selectorText.indexOf('.icon-') > -1) {
+						sheetsIconList.push(
+							`${sheetsList[i].cssRules[j].selectorText.substring(1, sheetsList[i].cssRules[j].selectorText.length).replace(/\:\:before/gi, '')}`
+						);
+					}
+				}
+			}
+			if (sheetsIconList.length > 0) resolve(sheetsIconList);
+			else reject('未获取到值,请刷新重试');
+		});
+	});
+};
+
+// 初始化获取 css 样式,获取 element plus 自带 svg 图标,增加了 ele- 前缀,使用时:ele-Aim
+const getElementPlusIconfont = () => {
+	return new Promise((resolve, reject) => {
+		nextTick(() => {
+			const icons = svg as any;
+			const sheetsIconList = [];
+			for (const i in icons) {
+				sheetsIconList.push(`ele-${icons[i].name}`);
+			}
+			if (sheetsIconList.length > 0) resolve(sheetsIconList);
+			else reject('未获取到值,请刷新重试');
+		});
+	});
+};
+
+// 初始化获取 css 样式,这里使用 fontawesome 的图标
+const getAwesomeIconfont = () => {
+	return new Promise((resolve, reject) => {
+		nextTick(() => {
+			const styles: any = document.styleSheets;
+			let sheetsList = [];
+			let sheetsIconList = [];
+			for (let i = 0; i < styles.length; i++) {
+				if (styles[i].href && styles[i].href.indexOf('netdna.bootstrapcdn.com') > -1) {
+					sheetsList.push(styles[i]);
+				}
+			}
+			for (let i = 0; i < sheetsList.length; i++) {
+				for (let j = 0; j < sheetsList[i].cssRules.length; j++) {
+					if (
+						sheetsList[i].cssRules[j].selectorText &&
+						sheetsList[i].cssRules[j].selectorText.indexOf('.fa-') === 0 &&
+						sheetsList[i].cssRules[j].selectorText.indexOf(',') === -1
+					) {
+						if (/::before/.test(sheetsList[i].cssRules[j].selectorText)) {
+							sheetsIconList.push(
+								`${sheetsList[i].cssRules[j].selectorText.substring(1, sheetsList[i].cssRules[j].selectorText.length).replace(/\:\:before/gi, '')}`
+							);
+						}
+					}
+				}
+			}
+			if (sheetsIconList.length > 0) resolve(sheetsIconList.reverse());
+			else reject('未获取到值,请刷新重试');
+		});
+	});
+};
+
+/**
+ * 获取字体图标 `document.styleSheets`
+ * @method ali 获取阿里字体图标 `<i class="iconfont 图标类名"></i>`
+ * @method ele 获取 element plus 自带图标 `<i class="图标类名"></i>`
+ * @method ali 获取 fontawesome 的图标 `<i class="fa 图标类名"></i>`
+ */
+const initIconfont = {
+	// iconfont
+	ali: () => {
+		return getAlicdnIconfont();
+	},
+	// element plus
+	ele: () => {
+		return getElementPlusIconfont();
+	},
+	// fontawesome
+	awe: () => {
+		return getAwesomeIconfont();
+	},
+};
+
+// 导出方法
+export default initIconfont;
Added +44 -0
diff --git a/src/utils/loading.ts b/src/utils/loading.ts
new file mode 100644
index 0000000..5fd020c
--- /dev/null
+++ b/src/utils/loading.ts
@@ -0,0 +1,44 @@
+import { nextTick } from 'vue';
+import '/@/theme/loading.scss';
+
+/**
+ * 页面全局 Loading
+ * @method start 创建 loading
+ * @method done 移除 loading
+ */
+export const NextLoading = {
+	// 创建 loading
+	start: () => {
+		const bodys: Element = document.body;
+		const div = <HTMLElement>document.createElement('div');
+		div.setAttribute('class', 'loading-next');
+		const htmls = `
+			<div class="loading-next-box">
+				<div class="loading-next-box-warp">
+					<div class="loading-next-box-item"></div>
+					<div class="loading-next-box-item"></div>
+					<div class="loading-next-box-item"></div>
+					<div class="loading-next-box-item"></div>
+					<div class="loading-next-box-item"></div>
+					<div class="loading-next-box-item"></div>
+					<div class="loading-next-box-item"></div>
+					<div class="loading-next-box-item"></div>
+					<div class="loading-next-box-item"></div>
+				</div>
+			</div>
+		`;
+		div.innerHTML = htmls;
+		bodys.insertBefore(div, bodys.childNodes[0]);
+		window.nextLoading = true;
+	},
+	// 移除 loading
+	done: (time: number = 0) => {
+		nextTick(() => {
+			setTimeout(() => {
+				window.nextLoading = false;
+				const el = <HTMLElement>document.querySelector('.loading-next');
+				el?.parentNode?.removeChild(el);
+			}, time);
+		});
+	},
+};
Added +8 -0
diff --git a/src/utils/mitt.ts b/src/utils/mitt.ts
new file mode 100644
index 0000000..8e73d4c
--- /dev/null
+++ b/src/utils/mitt.ts
@@ -0,0 +1,8 @@
+// https://www.npmjs.com/package/mitt
+import mitt, { Emitter } from 'mitt';
+
+// 类型
+const emitter: Emitter<MittType> = mitt<MittType>();
+
+// 导出
+export default emitter;
Added +289 -0
diff --git a/src/utils/other.ts b/src/utils/other.ts
new file mode 100644
index 0000000..e201a85
--- /dev/null
+++ b/src/utils/other.ts
@@ -0,0 +1,289 @@
+import { nextTick, defineAsyncComponent } from 'vue';
+import type { App } from 'vue';
+import * as svg from '@element-plus/icons-vue';
+import router from '/@/router/index';
+import pinia from '/@/stores/index';
+import { storeToRefs } from 'pinia';
+import { useThemeConfig } from '/@/stores/themeConfig';
+import { i18n } from '/@/i18n/index';
+import { Local } from '/@/utils/storage';
+import { verifyUrl } from '/@/utils/toolsValidate';
+import { Column } from '../model/api/page';
+import { ColumnConfig } from '../components/form/model/form';
+import { TableColumn } from '../components/table/type';
+import { h } from 'vue'
+
+// 引入组件
+const SvgIcon = defineAsyncComponent(() => import('/@/components/svgIcon/index.vue'));
+
+/**
+ * 导出全局注册 element plus svg 图标
+ * @param app vue 实例
+ * @description 使用:https://element-plus.gitee.io/zh-CN/component/icon.html
+ */
+export function elSvg(app: App) {
+	const icons = svg as any;
+	for (const i in icons) {
+		app.component(`ele-${icons[i].name}`, icons[i]);
+	}
+	app.component('SvgIcon', SvgIcon);
+}
+
+/**
+ * 设置浏览器标题国际化
+ * @method const title = useTitle(); ==> title()
+ */
+export function useTitle() {
+	const stores = useThemeConfig(pinia);
+	const { themeConfig } = storeToRefs(stores);
+	nextTick(() => {
+		let webTitle = '';
+		let globalTitle: string = themeConfig.value.globalTitle;
+		const { path, meta } = router.currentRoute.value;
+		if (path === '/login') {
+			webTitle = <string>meta.title;
+		} else {
+			webTitle = setTagsViewNameI18n(router.currentRoute.value);
+		}
+		document.title = `${webTitle} - ${globalTitle}` || globalTitle;
+	});
+}
+
+/**
+ * 设置 自定义 tagsView 名称、 自定义 tagsView 名称国际化
+ * @param params 路由 query、params 中的 tagsViewName
+ * @returns 返回当前 tagsViewName 名称
+ */
+export function setTagsViewNameI18n(item: any) {
+	let tagsViewName: string = '';
+	const { query, params, meta } = item;
+	// 修复tagsViewName匹配到其他含下列单词的路由
+	// https://gitee.com/lyt-top/vue-next-admin/pulls/44/files
+	const pattern = /^\{("(zh-cn|en|zh-tw)":"[^,]+",?){1,3}}$/;
+	if (query?.tagsViewName || params?.tagsViewName) {
+		if (pattern.test(query?.tagsViewName) || pattern.test(params?.tagsViewName)) {
+			// 国际化
+			const urlTagsParams = (query?.tagsViewName && JSON.parse(query?.tagsViewName)) || (params?.tagsViewName && JSON.parse(params?.tagsViewName));
+			tagsViewName = urlTagsParams[i18n.global.locale.value];
+		} else {
+			// 非国际化
+			tagsViewName = query?.tagsViewName || params?.tagsViewName;
+		}
+	} else {
+		// 非自定义 tagsView 名称
+		tagsViewName = i18n.global.t(meta.title);
+	}
+	return tagsViewName;
+}
+
+/**
+ * 图片懒加载
+ * @param el dom 目标元素
+ * @param arr 列表数据
+ * @description data-xxx 属性用于存储页面或应用程序的私有自定义数据
+ */
+export const lazyImg = (el: string, arr: EmptyArrayType) => {
+	const io = new IntersectionObserver((res) => {
+		res.forEach((v: any) => {
+			if (v.isIntersecting) {
+				const { img, key } = v.target.dataset;
+				v.target.src = img;
+				v.target.onload = () => {
+					io.unobserve(v.target);
+					arr[key]['loading'] = false;
+				};
+			}
+		});
+	});
+	nextTick(() => {
+		document.querySelectorAll(el).forEach((img) => io.observe(img));
+	});
+};
+
+/**
+ * 全局组件大小
+ * @returns 返回 `window.localStorage` 中读取的缓存值 `globalComponentSize`
+ */
+export const globalComponentSize = (): string => {
+	const stores = useThemeConfig(pinia);
+	const { themeConfig } = storeToRefs(stores);
+	return Local.get('themeConfig')?.globalComponentSize || themeConfig.value?.globalComponentSize;
+};
+
+/**
+ * 对象深克隆
+ * @param obj 源对象
+ * @returns 克隆后的对象
+ */
+export function deepClone(obj: EmptyObjectType) {
+	let newObj: EmptyObjectType;
+	try {
+		newObj = obj.push ? [] : {};
+	} catch (error) {
+		newObj = {};
+	}
+	for (let attr in obj) {
+		if (obj[attr] && typeof obj[attr] === 'object') {
+			newObj[attr] = deepClone(obj[attr]);
+		} else {
+			newObj[attr] = obj[attr];
+		}
+	}
+	return newObj;
+}
+
+/**
+ * 判断是否是移动端
+ */
+export function isMobile() {
+	if (
+		navigator.userAgent.match(
+			/('phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone')/i
+		)
+	) {
+		return true;
+	} else {
+		return false;
+	}
+}
+
+/**
+ * 判断数组对象中所有属性是否为空,为空则删除当前行对象
+ * @description @感谢大黄
+ * @param list 数组对象
+ * @returns 删除空值后的数组对象
+ */
+export function handleEmpty(list: EmptyArrayType) {
+	const arr = [];
+	for (const i in list) {
+		const d = [];
+		for (const j in list[i]) {
+			d.push(list[i][j]);
+		}
+		const leng = d.filter((item) => item === '').length;
+		if (leng !== d.length) {
+			arr.push(list[i]);
+		}
+	}
+	return arr;
+}
+
+/**
+ * 打开外部链接
+ * @param val 当前点击项菜单
+ */
+export function handleOpenLink(val: RouteItem) {
+	const { origin, pathname } = window.location;
+	router.push(val.path);
+	if (verifyUrl(<string>val.meta?.isLink)) window.open(val.meta?.isLink);
+	else window.open(`${origin}${pathname}#${val.meta?.isLink}`);
+}
+/**
+ * 字符串转小驼峰
+ * @param str 转换的字符串
+ */
+export function toCamelCase(str = '') {
+	return str.replace(/^[A-Z]*/, (str) => str.toLowerCase())
+		.replace(/[-_](\w)/g, (_, p1) => {
+			return p1.toUpperCase();
+		}).replace(/^\w/, function(match) {
+			return match.toLowerCase();
+		});
+}
+/**
+ * 通过表配置获取表单配置
+ * @param fields 配置项列表
+ */
+export function getFormConfigByFields (fields: Column[] = [], fun?: (item: Column) => EmptyObjectType) {
+	const propTypes = {
+		Int32: 'inputNumber',
+		Int64: 'inputNumber',
+		String: 'input',
+		Boolean: 'switch',
+		DateTime: 'datePicker'
+	}
+	const propProps = {
+		DateTime: {
+			type: 'datetime'
+		}
+	}
+	return fields.map(item => ({
+    component: propTypes[item.typeName],
+    label: item.displayName,
+    prop: toCamelCase(item.mapField || item.name),
+    props: propProps[item.typeName],
+		...(fun ? fun(item) : {})
+  }))
+}
+
+export function is(val: unknown, type: string) {
+  return toString.call(val) === `[object ${type}]`;
+}
+
+export function isObject(val: any): val is Record<any, any> {
+  return val !== null && is(val, 'Object');
+}
+// 深度合并
+export function deepMerge<T = any>(src: any = {}, ...targets: Array<any>): T {
+  let key: string;
+  targets.forEach((target) => {
+    for (key in target) {
+      src[key] = isObject(src[key]) ? deepMerge(src[key], target[key]) : (src[key] = target[key]);
+    }
+  });
+  return src;
+}
+
+/**
+ * 统一批量导出
+ * @method elSvg 导出全局注册 element plus svg 图标
+ * @method useTitle 设置浏览器标题国际化
+ * @method setTagsViewNameI18n 设置 自定义 tagsView 名称、 自定义 tagsView 名称国际化
+ * @method lazyImg 图片懒加载
+ * @method globalComponentSize() element plus 全局组件大小
+ * @method deepClone 对象深克隆
+ * @method isMobile 判断是否是移动端
+ * @method handleEmpty 判断数组对象中所有属性是否为空,为空则删除当前行对象
+ * @method handleOpenLink 打开外部链接
+ */
+const other = {
+	elSvg: (app: App) => {
+		elSvg(app);
+	},
+	useTitle: () => {
+		useTitle();
+	},
+	setTagsViewNameI18n(route: RouteToFrom) {
+		return setTagsViewNameI18n(route);
+	},
+	lazyImg: (el: string, arr: EmptyArrayType) => {
+		lazyImg(el, arr);
+	},
+	globalComponentSize: () => {
+		return globalComponentSize();
+	},
+	deepClone: (obj: EmptyObjectType) => {
+		return deepClone(obj);
+	},
+	isMobile: () => {
+		return isMobile();
+	},
+	handleEmpty: (list: EmptyArrayType) => {
+		return handleEmpty(list);
+	},
+	handleOpenLink: (val: RouteItem) => {
+		handleOpenLink(val);
+	},
+	toCamelCase: (str: string) => {
+		return toCamelCase(str);
+	},
+	getFormConfigByFields (fields: Column[]) {
+		return getFormConfigByFields(fields);
+	},
+	deepMerge (src: any = {}, ...targets: Array<any>) {
+		return deepMerge(src, ...targets)
+	}
+};
+
+// 统一批量导出
+export default other;
Added +81 -0
diff --git a/src/utils/request.ts b/src/utils/request.ts
new file mode 100644
index 0000000..3e33dce
--- /dev/null
+++ b/src/utils/request.ts
@@ -0,0 +1,81 @@
+import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';
+import { ElMessage, ElMessageBox } from 'element-plus';
+import { Session } from '/@/utils/storage';
+import qs from 'qs';
+import { ApiResult } from '../model/api/common';
+
+// 配置新建一个 axios 实例
+const service: AxiosInstance = axios.create({
+	baseURL: import.meta.env.VITE_API_URL,
+	timeout: 50000,
+	headers: { 'Content-Type': 'application/json' },
+	paramsSerializer: {
+		serialize(params) {
+			return qs.stringify(params, { allowDots: true });
+		},
+	},
+});
+
+// 添加请求拦截器
+service.interceptors.request.use(
+	(config) => {
+		// 在发送请求之前做些什么 token
+		if (Session.get('token')) {
+			config.headers!['Authorization'] = `${Session.get('token')}`;
+		}
+		return config;
+	},
+	(error) => {
+		// 对请求错误做些什么
+		return Promise.reject(error);
+	}
+);
+
+// 添加响应拦截器
+service.interceptors.response.use(
+	(response) => {
+		ElMessage
+		// 对响应数据做点什么
+		const res = response.data;
+		if (res.code && res.code !== 0) {
+			// `token` 过期或者账号已在别处登录
+			if (res.code === 401 || res.code === 4001) {
+				Session.clear(); // 清除浏览器全部临时缓存
+				window.location.href = '/'; // 去登录页
+				ElMessageBox.alert('你已被登出,请重新登录', '提示', {})
+					.then(() => {})
+					.catch(() => {});
+			} else if (res.message) {
+				ElMessage.error(res.message)
+			}
+			return Promise.reject(service.interceptors.response);
+		} else {
+			return response;
+		}
+	},
+	(error) => {
+		// 对响应错误做点什么
+		if (error.response.status === 401) {
+			Session.clear(); // 清除浏览器全部临时缓存
+			window.location.href = '/'; // 去登录页
+		} else if (error.message.indexOf('timeout') != -1) {
+			ElMessage.error('网络超时');
+		} else if (error.message == 'Network Error') {
+			ElMessage.error('网络连接错误');
+		} else {
+			if (error.response.data) ElMessage.error(error.response.statusText);
+			else ElMessage.error('接口路径找不到');
+		}
+		return Promise.reject(error);
+	}
+);
+
+// 导出 axios 实例
+// export default service;
+
+export default <T> (config: AxiosRequestConfig) => {
+  // 指定promise实例成功之后的回调函数的第一个参数的类型为Response<T>
+  return service<ApiResult<T>>(config).then(res => {
+		return res.data
+	})
+}
Added +48 -0
diff --git a/src/utils/setIconfont.ts b/src/utils/setIconfont.ts
new file mode 100644
index 0000000..6f4270e
--- /dev/null
+++ b/src/utils/setIconfont.ts
@@ -0,0 +1,48 @@
+// 字体图标 url
+const cssCdnUrlList: Array<string> = [
+	'//at.alicdn.com/t/c/font_2298093_rnp72ifj3ba.css',
+	'//netdna.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css',
+];
+// 第三方 js url
+const jsCdnUrlList: Array<string> = [];
+
+// 动态批量设置字体图标
+export function setCssCdn() {
+	if (cssCdnUrlList.length <= 0) return false;
+	cssCdnUrlList.map((v) => {
+		let link = document.createElement('link');
+		link.rel = 'stylesheet';
+		link.href = v;
+		link.crossOrigin = 'anonymous';
+		document.getElementsByTagName('head')[0].appendChild(link);
+	});
+}
+
+// 动态批量设置第三方js
+export function setJsCdn() {
+	if (jsCdnUrlList.length <= 0) return false;
+	jsCdnUrlList.map((v) => {
+		let link = document.createElement('script');
+		link.src = v;
+		document.body.appendChild(link);
+	});
+}
+
+/**
+ * 批量设置字体图标、动态js
+ * @method cssCdn 动态批量设置字体图标
+ * @method jsCdn 动态批量设置第三方js
+ */
+const setIntroduction = {
+	// 设置css
+	cssCdn: () => {
+		setCssCdn();
+	},
+	// 设置js
+	jsCdn: () => {
+		setJsCdn();
+	},
+};
+
+// 导出函数方法
+export default setIntroduction;
Added +64 -0
diff --git a/src/utils/storage.ts b/src/utils/storage.ts
new file mode 100644
index 0000000..09213b4
--- /dev/null
+++ b/src/utils/storage.ts
@@ -0,0 +1,64 @@
+import Cookies from 'js-cookie';
+
+/**
+ * window.localStorage 浏览器永久缓存
+ * @method set 设置永久缓存
+ * @method get 获取永久缓存
+ * @method remove 移除永久缓存
+ * @method clear 移除全部永久缓存
+ */
+export const Local = {
+	// 查看 v2.4.3版本更新日志
+	setKey(key: string) {
+		// @ts-ignore
+		return `${__NEXT_NAME__}:${key}`;
+	},
+	// 设置永久缓存
+	set<T>(key: string, val: T) {
+		window.localStorage.setItem(Local.setKey(key), JSON.stringify(val));
+	},
+	// 获取永久缓存
+	get(key: string) {
+		let json = <string>window.localStorage.getItem(Local.setKey(key));
+		return JSON.parse(json);
+	},
+	// 移除永久缓存
+	remove(key: string) {
+		window.localStorage.removeItem(Local.setKey(key));
+	},
+	// 移除全部永久缓存
+	clear() {
+		window.localStorage.clear();
+	},
+};
+
+/**
+ * window.sessionStorage 浏览器临时缓存
+ * @method set 设置临时缓存
+ * @method get 获取临时缓存
+ * @method remove 移除临时缓存
+ * @method clear 移除全部临时缓存
+ */
+export const Session = {
+	// 设置临时缓存
+	set<T>(key: string, val: T) {
+		if (key === 'token') return Cookies.set(key, val);
+		window.sessionStorage.setItem(Local.setKey(key), JSON.stringify(val));
+	},
+	// 获取临时缓存
+	get(key: string) {
+		if (key === 'token') return Cookies.get(key);
+		let json = <string>window.sessionStorage.getItem(Local.setKey(key));
+		return JSON.parse(json);
+	},
+	// 移除临时缓存
+	remove(key: string) {
+		if (key === 'token') return Cookies.remove(key);
+		window.sessionStorage.removeItem(Local.setKey(key));
+	},
+	// 移除全部临时缓存
+	clear() {
+		Cookies.remove('token');
+		window.sessionStorage.clear();
+	},
+};
Added +63 -0
diff --git a/src/utils/theme.ts b/src/utils/theme.ts
new file mode 100644
index 0000000..398053a
--- /dev/null
+++ b/src/utils/theme.ts
@@ -0,0 +1,63 @@
+import { ElMessage } from 'element-plus';
+
+/**
+ * 颜色转换函数
+ * @method hexToRgb hex 颜色转 rgb 颜色
+ * @method rgbToHex rgb 颜色转 Hex 颜色
+ * @method getDarkColor 加深颜色值
+ * @method getLightColor 变浅颜色值
+ */
+export function useChangeColor() {
+	// str 颜色值字符串
+	const hexToRgb = (str: string): any => {
+		let hexs: any = '';
+		let reg = /^\#?[0-9A-Fa-f]{6}$/;
+		if (!reg.test(str)) {
+			ElMessage.warning('输入错误的hex');
+			return '';
+		}
+		str = str.replace('#', '');
+		hexs = str.match(/../g);
+		for (let i = 0; i < 3; i++) hexs[i] = parseInt(hexs[i], 16);
+		return hexs;
+	};
+	// r 代表红色 | g 代表绿色 | b 代表蓝色
+	const rgbToHex = (r: any, g: any, b: any): string => {
+		let reg = /^\d{1,3}$/;
+		if (!reg.test(r) || !reg.test(g) || !reg.test(b)) {
+			ElMessage.warning('输入错误的rgb颜色值');
+			return '';
+		}
+		let hexs = [r.toString(16), g.toString(16), b.toString(16)];
+		for (let i = 0; i < 3; i++) if (hexs[i].length == 1) hexs[i] = `0${hexs[i]}`;
+		return `#${hexs.join('')}`;
+	};
+	// color 颜色值字符串 | level 变浅的程度,限0-1之间
+	const getDarkColor = (color: string, level: number): string => {
+		let reg = /^\#?[0-9A-Fa-f]{6}$/;
+		if (!reg.test(color)) {
+			ElMessage.warning('输入错误的hex颜色值');
+			return '';
+		}
+		let rgb = useChangeColor().hexToRgb(color);
+		for (let i = 0; i < 3; i++) rgb[i] = Math.floor(rgb[i] * (1 - level));
+		return useChangeColor().rgbToHex(rgb[0], rgb[1], rgb[2]);
+	};
+	// color 颜色值字符串 | level 加深的程度,限0-1之间
+	const getLightColor = (color: string, level: number): string => {
+		let reg = /^\#?[0-9A-Fa-f]{6}$/;
+		if (!reg.test(color)) {
+			ElMessage.warning('输入错误的hex颜色值');
+			return '';
+		}
+		let rgb = useChangeColor().hexToRgb(color);
+		for (let i = 0; i < 3; i++) rgb[i] = Math.floor((255 - rgb[i]) * level + rgb[i]);
+		return useChangeColor().rgbToHex(rgb[0], rgb[1], rgb[2]);
+	};
+	return {
+		hexToRgb,
+		rgbToHex,
+		getDarkColor,
+		getLightColor,
+	};
+}
Added +370 -0
diff --git a/src/utils/toolsValidate.ts b/src/utils/toolsValidate.ts
new file mode 100644
index 0000000..f2cb9d6
--- /dev/null
+++ b/src/utils/toolsValidate.ts
@@ -0,0 +1,370 @@
+/**
+ * 2020.11.29 lyt 整理
+ * 工具类集合,适用于平时开发
+ * 新增多行注释信息,鼠标放到方法名即可查看
+ */
+
+/**
+ * 验证百分比(不可以小数)
+ * @param val 当前值字符串
+ * @returns 返回处理后的字符串
+ */
+export function verifyNumberPercentage(val: string): string {
+	// 匹配空格
+	let v = val.replace(/(^\s*)|(\s*$)/g, '');
+	// 只能是数字和小数点,不能是其他输入
+	v = v.replace(/[^\d]/g, '');
+	// 不能以0开始
+	v = v.replace(/^0/g, '');
+	// 数字超过100,赋值成最大值100
+	v = v.replace(/^[1-9]\d\d{1,3}$/, '100');
+	// 返回结果
+	return v;
+}
+
+/**
+ * 验证百分比(可以小数)
+ * @param val 当前值字符串
+ * @returns 返回处理后的字符串
+ */
+export function verifyNumberPercentageFloat(val: string): string {
+	let v = verifyNumberIntegerAndFloat(val);
+	// 数字超过100,赋值成最大值100
+	v = v.replace(/^[1-9]\d\d{1,3}$/, '100');
+	// 超过100之后不给再输入值
+	v = v.replace(/^100\.$/, '100');
+	// 返回结果
+	return v;
+}
+
+/**
+ * 小数或整数(不可以负数)
+ * @param val 当前值字符串
+ * @returns 返回处理后的字符串
+ */
+export function verifyNumberIntegerAndFloat(val: string) {
+	// 匹配空格
+	let v = val.replace(/(^\s*)|(\s*$)/g, '');
+	// 只能是数字和小数点,不能是其他输入
+	v = v.replace(/[^\d.]/g, '');
+	// 以0开始只能输入一个
+	v = v.replace(/^0{2}$/g, '0');
+	// 保证第一位只能是数字,不能是点
+	v = v.replace(/^\./g, '');
+	// 小数只能出现1位
+	v = v.replace('.', '$#$').replace(/\./g, '').replace('$#$', '.');
+	// 小数点后面保留2位
+	v = v.replace(/^(\-)*(\d+)\.(\d\d).*$/, '$1$2.$3');
+	// 返回结果
+	return v;
+}
+
+/**
+ * 正整数验证
+ * @param val 当前值字符串
+ * @returns 返回处理后的字符串
+ */
+export function verifiyNumberInteger(val: string) {
+	// 匹配空格
+	let v = val.replace(/(^\s*)|(\s*$)/g, '');
+	// 去掉 '.' , 防止贴贴的时候出现问题 如 0.1.12.12
+	v = v.replace(/[\.]*/g, '');
+	// 去掉以 0 开始后面的数, 防止贴贴的时候出现问题 如 00121323
+	v = v.replace(/(^0[\d]*)$/g, '0');
+	// 首位是0,只能出现一次
+	v = v.replace(/^0\d$/g, '0');
+	// 只匹配数字
+	v = v.replace(/[^\d]/g, '');
+	// 返回结果
+	return v;
+}
+
+/**
+ * 去掉中文及空格
+ * @param val 当前值字符串
+ * @returns 返回处理后的字符串
+ */
+export function verifyCnAndSpace(val: string) {
+	// 匹配中文与空格
+	let v = val.replace(/[\u4e00-\u9fa5\s]+/g, '');
+	// 匹配空格
+	v = v.replace(/(^\s*)|(\s*$)/g, '');
+	// 返回结果
+	return v;
+}
+
+/**
+ * 去掉英文及空格
+ * @param val 当前值字符串
+ * @returns 返回处理后的字符串
+ */
+export function verifyEnAndSpace(val: string) {
+	// 匹配英文与空格
+	let v = val.replace(/[a-zA-Z]+/g, '');
+	// 匹配空格
+	v = v.replace(/(^\s*)|(\s*$)/g, '');
+	// 返回结果
+	return v;
+}
+
+/**
+ * 禁止输入空格
+ * @param val 当前值字符串
+ * @returns 返回处理后的字符串
+ */
+export function verifyAndSpace(val: string) {
+	// 匹配空格
+	let v = val.replace(/(^\s*)|(\s*$)/g, '');
+	// 返回结果
+	return v;
+}
+
+/**
+ * 金额用 `,` 区分开
+ * @param val 当前值字符串
+ * @returns 返回处理后的字符串
+ */
+export function verifyNumberComma(val: string) {
+	// 调用小数或整数(不可以负数)方法
+	let v: any = verifyNumberIntegerAndFloat(val);
+	// 字符串转成数组
+	v = v.toString().split('.');
+	// \B 匹配非单词边界,两边都是单词字符或者两边都是非单词字符
+	v[0] = v[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',');
+	// 数组转字符串
+	v = v.join('.');
+	// 返回结果
+	return v;
+}
+
+/**
+ * 匹配文字变色(搜索时)
+ * @param val 当前值字符串
+ * @param text 要处理的字符串值
+ * @param color 搜索到时字体高亮颜色
+ * @returns 返回处理后的字符串
+ */
+export function verifyTextColor(val: string, text = '', color = 'red') {
+	// 返回内容,添加颜色
+	let v = text.replace(new RegExp(val, 'gi'), `<span style='color: ${color}'>${val}</span>`);
+	// 返回结果
+	return v;
+}
+
+/**
+ * 数字转中文大写
+ * @param val 当前值字符串
+ * @param unit 默认:仟佰拾亿仟佰拾万仟佰拾元角分
+ * @returns 返回处理后的字符串
+ */
+export function verifyNumberCnUppercase(val: any, unit = '仟佰拾亿仟佰拾万仟佰拾元角分', v = '') {
+	// 当前内容字符串添加 2个0,为什么??
+	val += '00';
+	// 返回某个指定的字符串值在字符串中首次出现的位置,没有出现,则该方法返回 -1
+	let lookup = val.indexOf('.');
+	// substring:不包含结束下标内容,substr:包含结束下标内容
+	if (lookup >= 0) val = val.substring(0, lookup) + val.substr(lookup + 1, 2);
+	// 根据内容 val 的长度,截取返回对应大写
+	unit = unit.substr(unit.length - val.length);
+	// 循环截取拼接大写
+	for (let i = 0; i < val.length; i++) {
+		v += '零壹贰叁肆伍陆柒捌玖'.substr(val.substr(i, 1), 1) + unit.substr(i, 1);
+	}
+	// 正则处理
+	v = v
+		.replace(/零角零分$/, '整')
+		.replace(/零[仟佰拾]/g, '零')
+		.replace(/零{2,}/g, '零')
+		.replace(/零([亿|万])/g, '$1')
+		.replace(/零+元/, '元')
+		.replace(/亿零{0,3}万/, '亿')
+		.replace(/^元/, '零元');
+	// 返回结果
+	return v;
+}
+
+/**
+ * 手机号码
+ * @param val 当前值字符串
+ * @returns 返回 true: 手机号码正确
+ */
+export function verifyPhone(val: string) {
+	// false: 手机号码不正确
+	if (!/^((12[0-9])|(13[0-9])|(14[5|7])|(15([0-3]|[5-9]))|(18[0|1,5-9]))\d{8}$/.test(val)) return false;
+	// true: 手机号码正确
+	else return true;
+}
+
+/**
+ * 国内电话号码
+ * @param val 当前值字符串
+ * @returns 返回 true: 国内电话号码正确
+ */
+export function verifyTelPhone(val: string) {
+	// false: 国内电话号码不正确
+	if (!/\d{3}-\d{8}|\d{4}-\d{7}/.test(val)) return false;
+	// true: 国内电话号码正确
+	else return true;
+}
+
+/**
+ * 登录账号 (字母开头,允许5-16字节,允许字母数字下划线)
+ * @param val 当前值字符串
+ * @returns 返回 true: 登录账号正确
+ */
+export function verifyAccount(val: string) {
+	// false: 登录账号不正确
+	if (!/^[a-zA-Z][a-zA-Z0-9_]{4,15}$/.test(val)) return false;
+	// true: 登录账号正确
+	else return true;
+}
+
+/**
+ * 密码 (以字母开头,长度在6~16之间,只能包含字母、数字和下划线)
+ * @param val 当前值字符串
+ * @returns 返回 true: 密码正确
+ */
+export function verifyPassword(val: string) {
+	// false: 密码不正确
+	if (!/^[a-zA-Z]\w{5,15}$/.test(val)) return false;
+	// true: 密码正确
+	else return true;
+}
+
+/**
+ * 强密码 (字母+数字+特殊字符,长度在6-16之间)
+ * @param val 当前值字符串
+ * @returns 返回 true: 强密码正确
+ */
+export function verifyPasswordPowerful(val: string) {
+	// false: 强密码不正确
+	if (!/^(?![a-zA-z]+$)(?!\d+$)(?![!@#$%^&\.*]+$)(?![a-zA-z\d]+$)(?![a-zA-z!@#$%^&\.*]+$)(?![\d!@#$%^&\.*]+$)[a-zA-Z\d!@#$%^&\.*]{6,16}$/.test(val))
+		return false;
+	// true: 强密码正确
+	else return true;
+}
+
+/**
+ * 密码强度
+ * @param val 当前值字符串
+ * @description 弱:纯数字,纯字母,纯特殊字符
+ * @description 中:字母+数字,字母+特殊字符,数字+特殊字符
+ * @description 强:字母+数字+特殊字符
+ * @returns 返回处理后的字符串:弱、中、强
+ */
+export function verifyPasswordStrength(val: string) {
+	let v = '';
+	// 弱:纯数字,纯字母,纯特殊字符
+	if (/^(?:\d+|[a-zA-Z]+|[!@#$%^&\.*]+){6,16}$/.test(val)) v = '弱';
+	// 中:字母+数字,字母+特殊字符,数字+特殊字符
+	if (/^(?![a-zA-z]+$)(?!\d+$)(?![!@#$%^&\.*]+$)[a-zA-Z\d!@#$%^&\.*]{6,16}$/.test(val)) v = '中';
+	// 强:字母+数字+特殊字符
+	if (/^(?![a-zA-z]+$)(?!\d+$)(?![!@#$%^&\.*]+$)(?![a-zA-z\d]+$)(?![a-zA-z!@#$%^&\.*]+$)(?![\d!@#$%^&\.*]+$)[a-zA-Z\d!@#$%^&\.*]{6,16}$/.test(val))
+		v = '强';
+	// 返回结果
+	return v;
+}
+
+/**
+ * IP地址
+ * @param val 当前值字符串
+ * @returns 返回 true: IP地址正确
+ */
+export function verifyIPAddress(val: string) {
+	// false: IP地址不正确
+	if (
+		!/^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/.test(
+			val
+		)
+	)
+		return false;
+	// true: IP地址正确
+	else return true;
+}
+
+/**
+ * 邮箱
+ * @param val 当前值字符串
+ * @returns 返回 true: 邮箱正确
+ */
+export function verifyEmail(val: string) {
+	// false: 邮箱不正确
+	if (
+		!/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(
+			val
+		)
+	)
+		return false;
+	// true: 邮箱正确
+	else return true;
+}
+
+/**
+ * 身份证
+ * @param val 当前值字符串
+ * @returns 返回 true: 身份证正确
+ */
+export function verifyIdCard(val: string) {
+	// false: 身份证不正确
+	if (!/^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/.test(val)) return false;
+	// true: 身份证正确
+	else return true;
+}
+
+/**
+ * 姓名
+ * @param val 当前值字符串
+ * @returns 返回 true: 姓名正确
+ */
+export function verifyFullName(val: string) {
+	// false: 姓名不正确
+	if (!/^[\u4e00-\u9fa5]{1,6}(·[\u4e00-\u9fa5]{1,6}){0,2}$/.test(val)) return false;
+	// true: 姓名正确
+	else return true;
+}
+
+/**
+ * 邮政编码
+ * @param val 当前值字符串
+ * @returns 返回 true: 邮政编码正确
+ */
+export function verifyPostalCode(val: string) {
+	// false: 邮政编码不正确
+	if (!/^[1-9][0-9]{5}$/.test(val)) return false;
+	// true: 邮政编码正确
+	else return true;
+}
+
+/**
+ * url 处理
+ * @param val 当前值字符串
+ * @returns 返回 true: url 正确
+ */
+export function verifyUrl(val: string) {
+	// false: url不正确
+	if (
+		!/^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})).?)(?::\d{2,5})?(?:[/?#]\S*)?$/i.test(
+			val
+		)
+	)
+		return false;
+	// true: url正确
+	else return true;
+}
+
+/**
+ * 车牌号
+ * @param val 当前值字符串
+ * @returns 返回 true:车牌号正确
+ */
+export function verifyCarNum(val: string) {
+	// false: 车牌号不正确
+	if (
+		!/^(([京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领][A-Z](([0-9]{5}[DF])|([DF]([A-HJ-NP-Z0-9])[0-9]{4})))|([京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领][A-Z][A-HJ-NP-Z0-9]{4}[A-HJ-NP-Z0-9挂学警港澳使领]))$/.test(
+			val
+		)
+	)
+		return false;
+	// true:车牌号正确
+	else return true;
+}
Added +47 -0
diff --git a/src/utils/watermark.ts b/src/utils/watermark.ts
new file mode 100644
index 0000000..aba49e7
--- /dev/null
+++ b/src/utils/watermark.ts
@@ -0,0 +1,47 @@
+// 页面添加水印效果
+const setWatermark = (str: string) => {
+	const id = '1.23452384164.123412416';
+	if (document.getElementById(id) !== null) document.body.removeChild(<HTMLElement>document.getElementById(id));
+	const can = document.createElement('canvas');
+	can.width = 200;
+	can.height = 130;
+	const cans = <CanvasRenderingContext2D>can.getContext('2d');
+	cans.rotate((-20 * Math.PI) / 180);
+	cans.font = '12px Vedana';
+	cans.fillStyle = 'rgba(200, 200, 200, 0.30)';
+	cans.textBaseline = 'middle';
+	cans.fillText(str, can.width / 10, can.height / 2);
+	const div = document.createElement('div');
+	div.id = id;
+	div.style.pointerEvents = 'none';
+	div.style.top = '0px';
+	div.style.left = '0px';
+	div.style.position = 'fixed';
+	div.style.zIndex = '10000000';
+	div.style.width = `${document.documentElement.clientWidth}px`;
+	div.style.height = `${document.documentElement.clientHeight}px`;
+	div.style.background = `url(${can.toDataURL('image/png')}) left top repeat`;
+	document.body.appendChild(div);
+	return id;
+};
+
+/**
+ * 页面添加水印效果
+ * @method set 设置水印
+ * @method del 删除水印
+ */
+const watermark = {
+	// 设置水印
+	set: (str: string) => {
+		let id = setWatermark(str);
+		if (document.getElementById(id) === null) id = setWatermark(str);
+	},
+	// 删除水印
+	del: () => {
+		let id = '1.23452384164.123412416';
+		if (document.getElementById(id) !== null) document.body.removeChild(<HTMLElement>document.getElementById(id));
+	},
+};
+
+// 导出方法
+export default watermark;
Added +91 -0
diff --git a/src/views/error/401.vue b/src/views/error/401.vue
new file mode 100644
index 0000000..5441840
--- /dev/null
+++ b/src/views/error/401.vue
@@ -0,0 +1,91 @@
+<template>
+	<div class="error layout-padding">
+		<div class="layout-padding-auto layout-padding-view">
+			<div class="error-flex">
+				<div class="left">
+					<div class="left-item">
+						<div class="left-item-animation left-item-num">401</div>
+						<div class="left-item-animation left-item-title">{{ $t('message.noAccess.accessTitle') }}</div>
+						<div class="left-item-animation left-item-msg">{{ $t('message.noAccess.accessMsg') }}</div>
+						<div class="left-item-animation left-item-btn">
+							<el-button type="primary" size="default" round @click="onSetAuth">{{ $t('message.noAccess.accessBtn') }}</el-button>
+						</div>
+					</div>
+				</div>
+				<div class="right">
+					<img
+						src="https://img-blog.csdnimg.cn/3333f265772a4fa89287993500ecbf96.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAbHl0LXRvcA==,size_16,color_FFFFFF,t_70,g_se,x_16"
+					/>
+				</div>
+			</div>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts" name="noPower">
+import { Session } from '/@/utils/storage';
+
+const onSetAuth = () => {
+	// https://gitee.com/lyt-top/vue-next-admin/issues/I5C3JS
+	// 清除缓存/token等
+	Session.clear();
+	// 使用 reload 时,不需要调用 resetRoute() 重置路由
+	window.location.reload();
+};
+</script>
+
+<style scoped lang="scss">
+.error {
+	height: 100%;
+	.error-flex {
+		margin: auto;
+		display: flex;
+		height: 350px;
+		width: 900px;
+		.left {
+			flex: 1;
+			height: 100%;
+			align-items: center;
+			display: flex;
+			.left-item {
+				.left-item-animation {
+					opacity: 0;
+					animation-name: error-num;
+					animation-duration: 0.5s;
+					animation-fill-mode: forwards;
+				}
+				.left-item-num {
+					color: var(--el-color-info);
+					font-size: 55px;
+				}
+				.left-item-title {
+					font-size: 20px;
+					color: var(--el-text-color-primary);
+					margin: 15px 0 5px 0;
+					animation-delay: 0.1s;
+				}
+				.left-item-msg {
+					color: var(--el-text-color-secondary);
+					font-size: 12px;
+					margin-bottom: 30px;
+					animation-delay: 0.2s;
+				}
+				.left-item-btn {
+					animation-delay: 0.2s;
+				}
+			}
+		}
+		.right {
+			flex: 1;
+			opacity: 0;
+			animation-name: error-img;
+			animation-duration: 2s;
+			animation-fill-mode: forwards;
+			img {
+				width: 100%;
+				height: 100%;
+			}
+		}
+	}
+}
+</style>
Added +91 -0
diff --git a/src/views/error/404.vue b/src/views/error/404.vue
new file mode 100644
index 0000000..0b7d899
--- /dev/null
+++ b/src/views/error/404.vue
@@ -0,0 +1,91 @@
+<template>
+	<div class="error layout-padding">
+		<div class="layout-padding-auto layout-padding-view">
+			<div class="error-flex">
+				<div class="left">
+					<div class="left-item">
+						<div class="left-item-animation left-item-num">404</div>
+						<div class="left-item-animation left-item-title">{{ $t('message.notFound.foundTitle') }}</div>
+						<div class="left-item-animation left-item-msg">{{ $t('message.notFound.foundMsg') }}</div>
+						<div class="left-item-animation left-item-btn">
+							<el-button type="primary" size="default" round @click="onGoHome">{{ $t('message.notFound.foundBtn') }}</el-button>
+						</div>
+					</div>
+				</div>
+				<div class="right">
+					<img
+						src="https://img-blog.csdnimg.cn/9eb1d85a417f4ed1ba7107f149ce3da1.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAbHl0LXRvcA==,size_16,color_FFFFFF,t_70,g_se,x_16"
+					/>
+				</div>
+			</div>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts" name="notFound">
+import { useRouter } from 'vue-router';
+
+// 定义变量内容
+const router = useRouter();
+
+// 返回首页
+const onGoHome = () => {
+	router.push('/');
+};
+</script>
+
+<style scoped lang="scss">
+.error {
+	height: 100%;
+	.error-flex {
+		margin: auto;
+		display: flex;
+		height: 350px;
+		width: 900px;
+		.left {
+			flex: 1;
+			height: 100%;
+			align-items: center;
+			display: flex;
+			.left-item {
+				.left-item-animation {
+					opacity: 0;
+					animation-name: error-num;
+					animation-duration: 0.5s;
+					animation-fill-mode: forwards;
+				}
+				.left-item-num {
+					color: var(--el-color-info);
+					font-size: 55px;
+				}
+				.left-item-title {
+					font-size: 20px;
+					color: var(--el-text-color-primary);
+					margin: 15px 0 5px 0;
+					animation-delay: 0.1s;
+				}
+				.left-item-msg {
+					color: var(--el-text-color-secondary);
+					font-size: 12px;
+					margin-bottom: 30px;
+					animation-delay: 0.2s;
+				}
+				.left-item-btn {
+					animation-delay: 0.2s;
+				}
+			}
+		}
+		.right {
+			flex: 1;
+			opacity: 0;
+			animation-name: error-img;
+			animation-duration: 2s;
+			animation-fill-mode: forwards;
+			img {
+				width: 100%;
+				height: 100%;
+			}
+		}
+	}
+}
+</style>
Added +633 -0
diff --git a/src/views/home/index.vue b/src/views/home/index.vue
new file mode 100644
index 0000000..d351e1e
--- /dev/null
+++ b/src/views/home/index.vue
@@ -0,0 +1,633 @@
+<template>
+	<div class="home-container layout-pd">
+		<el-row :gutter="15" class="home-card-one mb15">
+			<el-col
+				:xs="24"
+				:sm="12"
+				:md="12"
+				:lg="6"
+				:xl="6"
+				v-for="(v, k) in state.homeOne"
+				:key="k"
+				:class="{ 'home-media home-media-lg': k > 1, 'home-media-sm': k === 1 }"
+			>
+				<div class="home-card-item flex">
+					<div class="flex-margin flex w100" :class="` home-one-animation${k}`">
+						<div class="flex-auto">
+							<span class="font30">{{ v.num1 }}</span>
+							<span class="ml5 font16" :style="{ color: v.color1 }">{{ v.num2 }}%</span>
+							<div class="mt10">{{ v.num3 }}</div>
+						</div>
+						<div class="home-card-item-icon flex" :style="{ background: `var(${v.color2})` }">
+							<i class="flex-margin font32" :class="v.num4" :style="{ color: `var(${v.color3})` }"></i>
+						</div>
+					</div>
+				</div>
+			</el-col>
+		</el-row>
+		<el-row :gutter="15" class="home-card-two mb15">
+			<el-col :xs="24" :sm="14" :md="14" :lg="16" :xl="16">
+				<div class="home-card-item">
+					<div style="height: 100%" ref="homeLineRef"></div>
+				</div>
+			</el-col>
+			<el-col :xs="24" :sm="10" :md="10" :lg="8" :xl="8" class="home-media">
+				<div class="home-card-item">
+					<div style="height: 100%" ref="homePieRef"></div>
+				</div>
+			</el-col>
+		</el-row>
+		<el-row :gutter="15" class="home-card-three">
+			<el-col :xs="24" :sm="10" :md="10" :lg="8" :xl="8">
+				<div class="home-card-item">
+					<div class="home-card-item-title">快捷导航工具</div>
+					<div class="home-monitor">
+						<div class="flex-warp">
+							<div class="flex-warp-item" v-for="(v, k) in state.homeThree" :key="k">
+								<div class="flex-warp-item-box" :class="`home-animation${k}`">
+									<div class="flex-margin">
+										<i :class="v.icon" :style="{ color: v.iconColor }"></i>
+										<span class="pl5">{{ v.label }}</span>
+										<div class="mt10">{{ v.value }}</div>
+									</div>
+								</div>
+							</div>
+						</div>
+					</div>
+				</div>
+			</el-col>
+			<el-col :xs="24" :sm="14" :md="14" :lg="16" :xl="16" class="home-media">
+				<div class="home-card-item">
+					<div style="height: 100%" ref="homeBarRef"></div>
+				</div>
+			</el-col>
+		</el-row>
+	</div>
+</template>
+
+<script setup lang="ts" name="home">
+import { reactive, onMounted, ref, watch, nextTick, onActivated, markRaw } from 'vue';
+import * as echarts from 'echarts';
+import { storeToRefs } from 'pinia';
+import { useThemeConfig } from '/@/stores/themeConfig';
+import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
+
+// 定义变量内容
+const homeLineRef = ref();
+const homePieRef = ref();
+const homeBarRef = ref();
+const storesTagsViewRoutes = useTagsViewRoutes();
+const storesThemeConfig = useThemeConfig();
+const { themeConfig } = storeToRefs(storesThemeConfig);
+const { isTagsViewCurrenFull } = storeToRefs(storesTagsViewRoutes);
+const state = reactive({
+	global: {
+		homeChartOne: null,
+		homeChartTwo: null,
+		homeCharThree: null,
+		dispose: [null, '', undefined],
+	} as any,
+	homeOne: [
+		{
+			num1: '125,12',
+			num2: '-12.32',
+			num3: '订单统计信息',
+			num4: 'fa fa-meetup',
+			color1: '#FF6462',
+			color2: '--next-color-primary-lighter',
+			color3: '--el-color-primary',
+		},
+		{
+			num1: '653,33',
+			num2: '+42.32',
+			num3: '月度计划信息',
+			num4: 'iconfont icon-ditu',
+			color1: '#6690F9',
+			color2: '--next-color-success-lighter',
+			color3: '--el-color-success',
+		},
+		{
+			num1: '125,65',
+			num2: '+17.32',
+			num3: '年度计划信息',
+			num4: 'iconfont icon-zaosheng',
+			color1: '#6690F9',
+			color2: '--next-color-warning-lighter',
+			color3: '--el-color-warning',
+		},
+		{
+			num1: '520,43',
+			num2: '-10.01',
+			num3: '访问统计信息',
+			num4: 'fa fa-github-alt',
+			color1: '#FF6462',
+			color2: '--next-color-danger-lighter',
+			color3: '--el-color-danger',
+		},
+	],
+	homeThree: [
+		{
+			icon: 'iconfont icon-yangan',
+			label: '浅粉红',
+			value: '2.1%OBS/M',
+			iconColor: '#F72B3F',
+		},
+		{
+			icon: 'iconfont icon-wendu',
+			label: '深红(猩红)',
+			value: '30℃',
+			iconColor: '#91BFF8',
+		},
+		{
+			icon: 'iconfont icon-shidu',
+			label: '淡紫红',
+			value: '57%RH',
+			iconColor: '#88D565',
+		},
+		{
+			icon: 'iconfont icon-shidu',
+			label: '弱紫罗兰红',
+			value: '107w',
+			iconColor: '#88D565',
+		},
+		{
+			icon: 'iconfont icon-zaosheng',
+			label: '中紫罗兰红',
+			value: '57DB',
+			iconColor: '#FBD4A0',
+		},
+		{
+			icon: 'iconfont icon-zaosheng',
+			label: '紫罗兰',
+			value: '57PV',
+			iconColor: '#FBD4A0',
+		},
+		{
+			icon: 'iconfont icon-zaosheng',
+			label: '暗紫罗兰',
+			value: '517Cpd',
+			iconColor: '#FBD4A0',
+		},
+		{
+			icon: 'iconfont icon-zaosheng',
+			label: '幽灵白',
+			value: '12kg',
+			iconColor: '#FBD4A0',
+		},
+		{
+			icon: 'iconfont icon-zaosheng',
+			label: '海军蓝',
+			value: '64fm',
+			iconColor: '#FBD4A0',
+		},
+	],
+	myCharts: [] as EmptyArrayType,
+	charts: {
+		theme: '',
+		bgColor: '',
+		color: '#303133',
+	},
+});
+
+// 折线图
+const initLineChart = () => {
+	if (!state.global.dispose.some((b: any) => b === state.global.homeChartOne)) state.global.homeChartOne.dispose();
+	state.global.homeChartOne = markRaw(echarts.init(homeLineRef.value, state.charts.theme));
+	const option = {
+		backgroundColor: state.charts.bgColor,
+		title: {
+			text: '政策补贴额度',
+			x: 'left',
+			textStyle: { fontSize: '15', color: state.charts.color },
+		},
+		grid: { top: 70, right: 20, bottom: 30, left: 30 },
+		tooltip: { trigger: 'axis' },
+		legend: { data: ['预购队列', '最新成交价'], right: 0 },
+		xAxis: {
+			data: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
+		},
+		yAxis: [
+			{
+				type: 'value',
+				name: '价格',
+				splitLine: { show: true, lineStyle: { type: 'dashed', color: '#f5f5f5' } },
+			},
+		],
+		series: [
+			{
+				name: '预购队列',
+				type: 'line',
+				symbolSize: 6,
+				symbol: 'circle',
+				smooth: true,
+				data: [0, 41.1, 30.4, 65.1, 53.3, 53.3, 53.3, 41.1, 30.4, 65.1, 53.3, 10],
+				lineStyle: { color: '#fe9a8b' },
+				itemStyle: { color: '#fe9a8b', borderColor: '#fe9a8b' },
+				areaStyle: {
+					color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+						{ offset: 0, color: '#fe9a8bb3' },
+						{ offset: 1, color: '#fe9a8b03' },
+					]),
+				},
+			},
+			{
+				name: '最新成交价',
+				type: 'line',
+				symbolSize: 6,
+				symbol: 'circle',
+				smooth: true,
+				data: [0, 24.1, 7.2, 15.5, 42.4, 42.4, 42.4, 24.1, 7.2, 15.5, 42.4, 0],
+				lineStyle: { color: '#9E87FF' },
+				itemStyle: { color: '#9E87FF', borderColor: '#9E87FF' },
+				areaStyle: {
+					color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+						{ offset: 0, color: '#9E87FFb3' },
+						{ offset: 1, color: '#9E87FF03' },
+					]),
+				},
+				emphasis: {
+					itemStyle: {
+						color: {
+							type: 'radial',
+							x: 0.5,
+							y: 0.5,
+							r: 0.5,
+							colorStops: [
+								{ offset: 0, color: '#9E87FF' },
+								{ offset: 0.4, color: '#9E87FF' },
+								{ offset: 0.5, color: '#fff' },
+								{ offset: 0.7, color: '#fff' },
+								{ offset: 0.8, color: '#fff' },
+								{ offset: 1, color: '#fff' },
+							],
+						},
+						borderColor: '#9E87FF',
+						borderWidth: 2,
+					},
+				},
+			},
+		],
+	};
+	state.global.homeChartOne.setOption(option);
+	state.myCharts.push(state.global.homeChartOne);
+};
+// 饼图
+const initPieChart = () => {
+	if (!state.global.dispose.some((b: any) => b === state.global.homeChartTwo)) state.global.homeChartTwo.dispose();
+	state.global.homeChartTwo = markRaw(echarts.init(homePieRef.value, state.charts.theme));
+	var getname = ['房屋及结构物', '专用设备', '通用设备', '文物和陈列品', '图书、档案'];
+	var getvalue = [34.2, 38.87, 17.88, 9.05, 2.05];
+	var data = [];
+	for (var i = 0; i < getname.length; i++) {
+		data.push({ name: getname[i], value: getvalue[i] });
+	}
+	const colorList = ['#51A3FC', '#36C78B', '#FEC279', '#968AF5', '#E790E8'];
+	const option = {
+		backgroundColor: state.charts.bgColor,
+		title: {
+			text: '房屋建筑工程',
+			x: 'left',
+			textStyle: { fontSize: '15', color: state.charts.color },
+		},
+		tooltip: { trigger: 'item', formatter: '{b} <br/> {c}%' },
+		graphic: {
+			elements: [
+				{
+					type: 'image',
+					z: -1,
+					style: {
+						image: themeConfig.value.isIsDark
+							? ''
+							: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAK0AAACtCAYAAADCr/9DAAAcoElEQVR4Xu19e7wcRZn28/ZM90xXzzknOYEkIAEiBAUUFyFc4wKCCAt8gHhBUEDFG8K3Iri6+3ETL0hQ9FthvYC4gAb0cwFRQcUlKiIIbpRbEBGUREJIyHWmq2e6Z+r9fjU5iQnJOWcuPV195nT/l5yq93nep57p6amueouQXV0rwMzW6tWrS4XCsFvPVQWIyAphKweR3ShUGw2EAwMIAfhE1OgacJIHoEme/7jpM3O+UgnnWFZuDpF6pQJ2JtDOBJ4JwgxmmgbwEABr3GAAM1Ah8CqAVgC8nEBLFWgJQz1Liv7sefZTRBS0EGvSNslMu9nQL1u2TEyZsv3rARygwK8nxuvIwh7McBJ0iL4T/xXgR8DW75nxcL2ef2jKFFqTIIdUQ01q02qTDg5v/wZLqSMZ9I8AtGHtFI6YAmExFO4jsu6N3Ny9Q0SrU8gzEUqTzrSrV8ud7aJ1okW54wg4jJmLiSgdL4i+Gz8Moh9xQ91ZKhUeizd8uqNNCtNKya9QqnYqWdbbAcwF0Fd5M/AnZv5/UFgwMFBYnG7Ldc+urwZvczmY2Q6CxolMfDaYjwKQ616uCRHhd2BcX63at0ybRusnBOM2SfadaYOAd2Wun8PgMwFMb1OPvmlORBUGfw8K13qes6hvEuunr0nfD/dn4gsJdAqAfD8NUpe5MBF+wWR9QRRydxMRdxnPePcJf6etBNGRFvNFDBxuXM20EyA8QWx9xnVz3yMilXa6o/GbsKYNgugNivlzAOZNVPGN8SY8rpgvK7nObRPxzjvhTFsu1/ay8rgKTP9kbND7BZixyLLoQte1F06klCaMadetWzecz7uXg/DB7Jk1bovRD3JW42PFYvHZuCP3Il7qTcvMJKvRe8D0eYC374UIWcymAlUwf14I5/NEVEuzJqk27bp11Tm2bV3HwGFpFrGfuDHwlKLG+wdd97605pVK0+q7axBE/8zAZwGItIrXx7wUA9esWfXiJ2fNmpW6FWepM63v8w6M6EYivKmPTTExUiM8wQ2cXio5j6SJcKpMu96vnZQj6zqAt0uTSJOcS41B/+a5+S+lZXosFaZduJDzcw+MPk/Ax/rpLV1/mZ1+FLn5M6aQ+XW9xk1bLvN0ykXfpeyN1kTw+F9Y4WTTjwtGTavXC4DodoB3mggjlnFsrun0AT5LiML3TelhzLRS1t7KoBuz2QFTQ98VrgLzJZ5X0LM7iV9GTCtl+FEGvtjiZsDERckAW1SAcN1DD9rnHHEE1VvsEUuzxE1bkbX5BPp4LOyzIOYVYP6hEM7biaiaFJnETKtrA8hq9DUw3p9UchlOMgro9bpu0T5BLzxPAjER0zJzTlajG8A4I4mkMozkFSDggWq1cuzw8PC6XqP33LT6DhtUo/9kxrt7nUwW37gCD/iVtUdPnz69p3fcnpvW98PrQXifcTkzAokoQET3rnpp+fG9XLPQU9NKGX2RwfotV3ZNIgWY+S5POCcRUdSLtHtm2oqMLiTwVb0gncVMvwIM3Oq59mm9WK/QE9NKWX8HQy2YZPOwzIz1ROQDqqbvMrp4HYAiQLqKjS5SN1lqLzQ/VQSeL0ThE3F/xGI3rZTRwQzWe44KcZM1Ha9ZS4D5CRAeJcZitqxnuK6WAOGLnue9RDT6JLv+QVoul4cLhYHpzPVZjQbvRkSvAngfxdiHCMOm8+sBPoNwtuc6N8QZO1bTrpJyVhH2wwDPiJOkqVjMWG0R7mELv2LLvt+z8Vivtl5Xq9XdmfOHKqh5YD4GoL5Yj0GEEExHCmH/Oq5xjM20upCbDCK9RWP/uMiZiEOEv0DXxYJ1h+vmHzJVBLlcq72GFE4gJl1/7B9MaBEj5ouE+v5CiL/FETM20/p+eJ3+KoiDVNIxGCgT4RZifEsI58Gk8cfDq1Z5j7oK30OwzgR4h/Hap/TvDwjXPiyOGYVYTFsJwncT46aUijUqLQKeZsaXhLBvTuoVZDca6R92QdA4iaEuAHBQN7FM9CXQ1ULYmntXV9emXVetzrHZ+h9mDHTFJMHOzHiEiS8vuc4dvXpG7XU6UkaHKlaXENHRvcaKMT6DreM9L39XNzG7Mq3+5Mugfj/AB3RDIqm+uo6rBesi1819vxfzh0nlsTmOlNE8Bl8xccpD0YvCzb+WiFZ2qldXpvWD6FIwX9YpeFL9mLHWIvq06+avISJ9ykzfXXpuHFBXMrBL2pMj0B1C2Cd3yrNj01Yq4T+QhYdSekbBZnrwf4Hr53me90KnIk2UfsuXL/dKg8OXE/DPaX+RwcTvKrmF73SibUembS41DCJtWH2wRiovZqxQxB8aFIXbU0mwh6R8358L2P8Jwl49hOkyNL2kGvm9Bgbaf0zoyLRSRh9jsN4uk86L+W6lnLMGBvRZXZPz0vPmQRB9gYFz0rotnwg3C9dpe41126aVUu7EyD8JoJRCOzT0M7YQzmf75YdWtxo3N5ASfROMwW5j9aA/W0RHtltqtAPThrcwcGoPEugyJK1hhXeWSvZPuwzUd911Td9cnu5gxpzUJUd4/KEH7X3b2RzZlmmljA5hsH6H3Fa/3gvFf1UNHDcZjiPqVEtmHg6C6HYG9CF/6boYH/E85z9aJdWy+Zp1YoPoAQAHtho8iXbM/Aewc0ypRC8mgTeRMZi5IGW4AERvSVkeK4Vr707U2hFSLZtWytopDDJWVWQUkR8Urn0sEa1N2SCkls7IJlP9jKuPrErPxfwZzytc3Aqhlkw7sv37cTD2bCVoQm30AoyjJ8KagYT0aBlmZDyvB+M9LXfqfcOKatR2GxgYGHfGpyXTVoLaacTU0URwj3JdFNb8I6dOnZrdYTsUWBvXD6LvUIp+VOvtWUIU/mW8lMY1bTM5GT5ORCm5y/KzquEc1Mmk9HhiTLa/66NYfRn9OC0FrPW3Zj0KZg8ODr401liMa9r1snZyDnRbOgaUXmpYjUMHi8U/pYPPxGfBzEO+jO4nwt6pyIb5cs8rXNqVaaUMf8PAwSlIqG4RHeW69i9TwKWvKAQB76K4/rt0VGCnVcLN70xEcjSRx7zTShkexICe5jJ+sVIXlErFq40T6VMClWp0FCn+SRoW2ijGOQOe89VOTbuAgXeaHye+zRMFfVBzdvVQAd+vXQyiy3sI0VJoZiwuec6ojyuj3mnL5fL0XL6whNn0VnD6Wz3Kv25oiFa3lHHWqGMF9ByuH0QLCXhDx0Fi6qiIDh8Y5VFwVNNWZPXjBGt+TBw6DaMXVLzJde3/7jRA1q89BfTzbYOjxwhmt08RsEAI5/RtsR/VtH4QLjb+MoFwvec6WT3b9nzXdWvfDz8CwjVdB+ouQBDW7B2nTt36bec2TeuH4f6o4+HuMLvszbw8FM6eU7NXtF0K2X735huzDTUsDmm/d4w9GB/0POcbL4+4TdNWZO0qAl0YI3zboSyyznLdvD5IJLsMKDCynep3JmcTGPhlSTiHj2va5rm01egvzAY3yDEeFsI+MFvIbcCtm0H6fvh1ED5gkIWSHM3a3vOWbc5hqzvtSAG53xgkCovo8OwlgskR2IDt+/4OIPvPRo/NYpzrec61Y5u2Gs1nxeZOn2H83POc7DBn855tMpCydhWbfFQk3OO5zhYFSba60/p++BgIrzGlGYEOFcI2eqc3lXsacZl5uyCI/sqAZ4hfrbzenjZzpq77u+HawrRSylmM/HMGt9Pc7wlnniFxMthRFPD98BoQPmJMIMs6wSvmf7RN0/p++H4QtppiSIwsWyd7Xv6OxPAyoJYUqFZ5t4aKnjI4k/AfnnA2fWi2uNNWZKhPA9f1UA1cvES4zuyJWhDOgGCJQvoy+gHA/ytR0I2PA4Q/Cdd51bbvtDL6G8CvMEFM1yvwvMKnjGBnoOMq4PvV40HWD8dt2JsGrBr2zI3FVzbdadcGwWybc8/2BnPcqKpWrc8eHhZLxm2ZNTCiQHNDpAyXgshIUWcCv0WMlLjaZNogqJ+uWH3bhCIE/EII5wgT2Blm6wpIGV3N4PNb7xFfSwJ9QQi7ORW7ybS+rF8DKDO/ENss1hCfFFmkdhQw+eKJQPcLYTdnljYzbajnRk1sq2GCPUsIer4dAbO2ySuwoWBL/XlD5z74wrUH9Q/1pmlHVvWsh5kJ5Ec94bwu+SHIEDtRwPfDb4Lw3k76dtsnZ6lXF4vFp5qmXVutvtJW1jPdBu2kP4Pnl3pwql8nXLI+4ytQlrVTLdAt47eMvwWBTxGicFvTtL5fPx6kjExnsEXHlIpZpcP4h7g3ESuVykyyHL3qatzyA7EzILrYc+3PNIErsno+wTKx07UhXHsqEZVjTzAL2DMFpAyfZmD3ngGMFphxk+c5Z47cacNrQc2K0YlezHii5DnGFuckmmwfgckgvJkZ7zKQUnNtygbTyuiHAB+fNAkiLBDutjevJc0lw2tdASnD8xlI/JuZQH8Twp614fHADx8hwj6t046nJYE+KYR9ZTzRsihJKVCpRG8ii3+WFN5mOPpxsrjxTrsC4O0TJ8HqRM8r3pk4bgbYlQJS8ixGZOSVO8HeiRYu5PwBB0Y1AFZXmXTQmRXvUyoVHuuga9bFoAIj8/oBACdxGoz9SFeSsXIFI6XfhWsPZEWREx/2WAB9WXsaoMRnEFipN1O1Wt2joSy9wDfZi7HW85ypyYJmaHEpUJGhLp+01fbuuOKPFofA7yDfD/cHJV+YQx+uXBJ/X9jb62Sz+PEqYGzDAOMDVC4Hh1m53C/iTamlaL/1hHNQSy2zRqlTwA/Cr4HxwaSJ6XN/qVKpHkOWdbcB8IVCOG9MGjfDi0cBKcOrGUh8ba2eJiXfrx4HsjbtdIwnpRaiEP3Ec+1jW2iZNUmhAhVZu0IbKHFqRBeR79dOBJGBHbB8pycKJyaedAYYiwK+X7sMRGOejRAL0MuDEF1m7k7L/GPPKyT+6rgnQk7CoL5f+xSILkk8deZLTZr2p55XOCbxpDPAWBSoyNqVBBr3zK9YwDYLwuB/pSCIjlDM98YdvIV4v/aEY7xMegs8sybbUEDK8N8ZOC9pcQh0AZk6wYYZj5U8J/FFOkmL3K94MghvYsa7E8/PwoepVqu9pt4gA+//6QVP2DsmnnQGGIsCfhDdBebEZ3+Y+F0kJe/EiJbGkkl7QerNZWZEjfa6Za3ToIAvw0UA9k2aCyt1LC1btkwMTdluUxnFJElUg/rO06YJEx+YJNPsS6yKH75EhGmJJ8eYO7KeNtR7tEpJExjrrKikuWR4rSuwevXqoUKxZOQEeIvs2U3TyiB8mtnARjULH/SKW59e0rp8WUsTCvh+OBeEh0xgN5ezamBTy8wAXOsJ51wTyWeYnStQCcKziPGtziN02pPWeMIeHtkjVruRiM7oNFSn/Ri4ryScf+y0f9bPjAIVGf5fAv63AfRmNaINz7RBdAmYE68NS4SKW2zWPagbECCD7FABX4b6ZHoDy0rpdk/Yb2maNgjqpylW3+kwh+66Mfb3POd/uguS9U5KAWZ2ZRDpH2GJ7w8ji64SRftfNtxpw3A/1KFP50v8YqjzS6L45cSBM8COFAiC6DDFbGLTgC7E9D7PdW7YWDVRyCDSVRNzHWXSRSdmvrvkFf6pixBZ1wQVKPu1T1tEFyUIuQmKgIOFcB7cVERMBuEfmbHpMIYESQXCtYeJqJogZgbVoQK+DPVB3/t32L2bbg2/Yk+ZPp0qfzetDG9h4NRuonbcl9UJnldMfvdEx4QnZ0cp5U4j58wlXyMD/FRJFF6tld9k2oqMLiTwVUaGg3CT5zpnGsHOQFtWwFQNL02Qwd8uiUJzVdkm066Xcl4O+ftaziDGhsxYv3LF8zNmz56dPSLEqGvcoXwZPgjgwLjjthKPoM4TonjNFqZl5uLIVEahlSBxt2HFp5VKBSMVpuPOpR/jlcu1Pa0cPWGkmLIWNI/Xe47z+y1Mq/9RkeGvCDCym4CBhaVsS3lq/S5l9EUGf8wMQVor3Py0jad5blGC3A+iT4E5+c1qG5TgRl3tOThYTL5Ek5mRmDCoS5cudYenzVwC8HZmSNOdnrA37dx+2Snk0TwGG3mu1WJYhK+7rvMhM8JkqKMpYPqgbwLOE8JpPs9u9XjAzHlfRiuJMMXQEAaqUdt1YGBghSH8DPZlCuiynn4QLSYYmcNvsqlHao+hoeLT2zSt/k8pw1sZeIep0dPTbkIUEt+abCrftONWgtppxGRmXcqGX31PCeE052fHMG3tnQxaYFBMySrcrVQqLTfIIYPecChiLqhGi5mxhylBNi6SGdO0zFwKqvUXmVmYIgpY13oiny0ONzcATWTfD84G5a4zSYPyOEg4zm/HNO0GsrXvg+gUg2TrqsH7DgwUHjfIYVJDr1y5ckB4g08BtIMpIYjwnFu0ZxMRj2taKetvY6jvmSLbxCX6uefabzLKYRKDSxnNZ3DzqHpTF4GuFMLeqjLjNo+K1NvKB4e2e4EIg6YIa1yLcIbrOjeb5DAZsf0w3Bf15sbFvMn8VYNfu61v21HPNy374TcswvtNkmbGKk/YexLRSpM8JhO2nvaUQaSfIV9vNG/Gw57nHLAtDqOaVsrwAAa2eAA2kgTznZ6X1bFNSvuyX/uUZaKE58sTHKO8wJgnSfsy1Hu3zH7idDKMD3ue87WkBm6y4qyX8tA88r9gw48FAK3zK2t2mj59eqWtO61uHAThexTjhhQMomSFQ0sl5w8p4NKXFJh5uyCIfsfALilI8CuecEbdoj7mnZaZCzKoPwfwjBQk8qxw7f2JaE0KuPQVBf0SQVbrPwHzUSlIrFGP1KuHhop/Ho3LmKbVnfwguhjMl6cgGT0Ndo8o5o8joigVfPqEhJTRlxj80XSkw7d7ovCWsbiMa9p163g4b0fPmShQt03ijBs8z3lfOgSe+CykDM9l4CtpyYSAA4VwxqwTNq5pdTIVWZtPIKMTzVuIyvxpzyuYWveblvHtmoeUtVMY9F0TpQO2RZ4Z95Q85+jxEmvJtCOHPj8LwBsvYFJ/14dUCGGb2YiZVJI9xPH96rEg63YARrZXbXtWgOYJYd8/Xtotmbb5bOvXPgeifx0vYIJ/Z333F8L+YoKYfQGlDUtW7r90iaPUJMR8t9di0ZaWTbuGeYoTRM8AGE5NopoI0WWeaydePC9VGrRBpixrp1horo9NzR0WgGKF/Vqd0mzZtBuebcOPEvClNjRKpinjq0LY52XnN4wtd9kPP2xR80dX4uWvxmTGuNHznLNaNUtbpn3iCXZ2mR0+TqA5rQIk1o75LimdU7ffnnQp/uzaTAE9D+sH0XwCDO2mHXM4fIL9KiHo+VYHrS3T6qDVanRMQ3Hip5a3lBDjSaX4lIGBwpMttZ8EjZh5WFbDBWB6cxrTZaJ/K7n2Fe1wa9u0OriU0W0MPrkdoKTaMlC2wB8QonBrUphpxZEyOoShFgCUhlezW8nE4Kc819mHiMJ2NOzQtHInhbzeoTnQDliSbQm42XXtc4lIlzCdVNfChZw/4KD6RWD+P6bXxI4hPCuiNw64dtu1bjsyrSbi++E5IFybcjcsgWWd4xXzP045z9joVSo1fef6JshIOc7W8yBc77lOR+u1OzYtM+vDoO9l4PDWmZppyeDvWmhcIIRo+WHfDNPOUVes4JIohZcQSK8hsDuPlEjPpbVq5bXDw8PrOkHr2LQabE0Q7Gqr3COmt+W0kjgRlRnqqvK6NVfPnDnTyAmVrfBst82GFVrRmVD8GZC5TYht8FYW0dGua/93G322aNqVaXWkShCeQYwbOyWQfD9eTqDPua593USuPq4rv1SC8K0W0aVg7JW8jp0hMvDlknDO76z3hl5dm7ZpXBl+m4DTuyGSdF9mvEjgaxqN2lcHBwdXJY3fKZ4uyVqR0Rk5C+czY4vKK53GTLDfomXPP3fInDlzat1gxmJavUfeKw09NAFFBBH5DL6FGNcLsWVRiG6Ejbvvump195yiswnWewHePu74vY7HjHWNnJo7VPx7Ta5OMWMxrQav1Wp7N5T1oK5Q0ykZ0/0Y/Eci61ZVV99LwwsKn3lHDqK3EfB2AAfH9c1oQGdW4LcOiMJtcWDHZlpNRsra20bWZ8YaN45E247BeJKI7lIW7vEK+d/oH3Jtx2izw9NPP12Y8YpXzM1x7kgiOpaBubr8Q5th0tec+bOeV4jtGKfYzeX7tU/D0DlTPRytBoBHwc0t9Y8Q0aNRJP84NDS0ulPMFStWlDxvyhzLsvZpsNrHAh3AYH3UUbHTmOnsR3cKN3/yxirecXCM3bQj87e6XKj+Suv3S88z/oWZl1sWrVSMVcRcgYUqM+o5siwFFJnZI8I0KF1Jm2YwsCsRJtxzaQeD+YfyenvezJkU6xRj7KbViW0odz7j5wAO6SDRrEtfKMBLwPWDPc9bFnc6PTGtJrmWeWpehvcR0d5xk87ipV6Bl/I5PqxQKCzuBdOemVaT9X1/R7Ls+5jxyl6Qz2KmUoG1YBzpec6iXrHrqWmbd9y1wWzbsX4J0KxeJZHFTYcCRFQB481C2L/pJaOem1aTX7euunvepnsz4/ZyKM3G1i9pGnWcMDBgL+w1k0RMq5MIAp6tONTG3bXXSWXxk1VAz2ETcLzr2r9KAjkx0+pk9CnWinM/I6I9k0guw0hCAVpVBx83lOAr8ERNqyVk5mkyiH4E4KAkJM0weqkAL1ENHDsw0JtZgtGYJ27aEeO6MqgvAPikXkqaxe6pAr8H28d7HsU+DzseayOmHTGuFQThlQy6YAIvBBlP3z79O93pV9acPlrR414nbcy0GxPTi8gt0NdSVaKn16pP3PgM5iuEcC6Ocy1Bu3IYN60m7PvhfiD+fjaz0O7wJdder4dl4vfGtbywG+apMK1OQL/2tYPwWwBtOiK9m8SyvrEqsChn2e8oFmnU6tyxoo0TLDWm3cizLMNzLWA+gPRU9EtyRNKFpRj4d8+1P9FuQY1eppE60+pky+XaXlaObgKwXy+Tz2KPqcBSVvTeUsnWq/VSdaXStCOzC3k/qF9A4Euzu26inlEgfF0U7U+mtTpPak27cZiq1erudWV9g4AjEh26yQjGeLJB9Q8MCvHrNKefetOO3HXJ98PTybKuAHinNAs6EbkxYz2YP/fCC0u+3O327iTynxCm3SgEMwtZrX8C3DwdO/uh1r1DGmgWWoku8jzvhe7DJRNhQpl2oyRS8iwmfb4ZdPXotNetSmYk20NhgH/Mii5utWR8e+F723pCmvbvz7v8ykYjuoQIp5s/z7W3AxVbdKKfEvMl453VFRteDwJNaNNuZt7dGxx9HIwz+m8LdiyjrgC6k8BXCuE8GEtEg0H6wrQb9SuXebqVr38IjA8BvINBXVMBrauiE3Bjo66+MjhY/FMqSMVAoq9Mu9kPNrsShCdZZJ0N5iNTd5pLDAM3TohFYFwnpf2dfjw4pS9Nu/mASilnKcq9i0CngfGa3vvFGMJSBt+KHL5dKhQeNcYiAeC+N+3mGpZrtb0shVNY0UlE2Heir+Mlwp9B9AMo3Oa6+QeIiBPwjHGISWXazdX2fd4RiI4lwlEMvBHAdOOjMQ4B/YwK5vssop/V6/zTwcHCH9POuRf8Jq1pXy5muVzb08rTPCg+CBbNBUNvvsz3QvQWYzIB+hDthxXUb4mt+4WwF2WnUsZUCbzFQZhQzZr1yGbM2Jsb2Nti3pMZc0C0GzN2IcKUGJPxwVgComcY/Azp9/+NxuJ6PXi004M0YuSWylDZnbaDYdGFo2u12g5K5WbUWW1nWTTFYgwo6OqIVgEKNpPKE6MGywqJEQJcY4ZPZK1pEK9GXa1Qylk+NEQdlwvtgHpfdPn/ixNifr4QLGYAAAAASUVORK5CYII=',
+						width: 230,
+						height: 230,
+					},
+					left: '16.5%',
+					top: 'center',
+				},
+			],
+		},
+		legend: {
+			type: 'scroll',
+			orient: 'vertical',
+			right: '0%',
+			left: '65%',
+			top: 'center',
+			itemWidth: 14,
+			itemHeight: 14,
+			data: getname,
+			textStyle: {
+				rich: {
+					name: {
+						fontSize: 14,
+						fontWeight: 400,
+						width: 200,
+						height: 35,
+						padding: [0, 0, 0, 60],
+						color: state.charts.color,
+					},
+					rate: {
+						fontSize: 15,
+						fontWeight: 500,
+						height: 35,
+						width: 40,
+						padding: [0, 0, 0, 30],
+						color: state.charts.color,
+					},
+				},
+			},
+		},
+		series: [
+			{
+				type: 'pie',
+				radius: ['82', themeConfig.value.isIsDark ? '50' : '102'],
+				center: ['32%', '50%'],
+				itemStyle: {
+					color: function (params: any) {
+						return colorList[params.dataIndex];
+					},
+				},
+				label: { show: false },
+				labelLine: { show: false },
+				data: data,
+			},
+		],
+	};
+	state.global.homeChartTwo.setOption(option);
+	state.myCharts.push(state.global.homeChartTwo);
+};
+// 柱状图
+const initBarChart = () => {
+	if (!state.global.dispose.some((b: any) => b === state.global.homeCharThree)) state.global.homeCharThree.dispose();
+	state.global.homeCharThree = markRaw(echarts.init(homeBarRef.value, state.charts.theme));
+	const option = {
+		backgroundColor: state.charts.bgColor,
+		title: {
+			text: '地热开发利用',
+			x: 'left',
+			textStyle: { fontSize: '15', color: state.charts.color },
+		},
+		tooltip: { trigger: 'axis' },
+		legend: { data: ['供温', '回温', '压力值(Mpa)'], right: 0 },
+		grid: { top: 70, right: 80, bottom: 30, left: 80 },
+		xAxis: [
+			{
+				type: 'category',
+				data: ['1km', '2km', '3km', '4km', '5km', '6km'],
+				boundaryGap: true,
+				axisTick: { show: false },
+			},
+		],
+		yAxis: [
+			{
+				name: '供回温度(℃)',
+				nameLocation: 'middle',
+				nameTextStyle: { padding: [3, 4, 50, 6] },
+				splitLine: { show: true, lineStyle: { type: 'dashed', color: '#f5f5f5' } },
+				axisLine: { show: false },
+				axisTick: { show: false },
+				axisLabel: { color: state.charts.color, formatter: '{value} ' },
+			},
+			{
+				name: '压力值(Mpa)',
+				nameLocation: 'middle',
+				nameTextStyle: { padding: [50, 4, 5, 6] },
+				splitLine: { show: false },
+				axisLine: { show: false },
+				axisTick: { show: false },
+				axisLabel: { color: state.charts.color, formatter: '{value} ' },
+			},
+		],
+		series: [
+			{
+				name: '供温',
+				type: 'line',
+				smooth: true,
+				showSymbol: true,
+				// 矢量画五角星
+				symbol: 'path://M150 0 L80 175 L250 75 L50 75 L220 175 Z',
+				symbolSize: 12,
+				yAxisIndex: 0,
+				areaStyle: {
+					color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+						{ offset: 0, color: 'rgba(250,180,101,0.3)' },
+						{ offset: 1, color: 'rgba(250,180,101,0)' },
+					]),
+					shadowColor: 'rgba(250,180,101,0.2)',
+					shadowBlur: 20,
+				},
+				itemStyle: { color: '#FF8000' },
+				// data中可以使用对象,value代表相应的值,另外可加入自定义的属性
+				data: [
+					{ value: 1, stationName: 's1' },
+					{ value: 3, stationName: 's2' },
+					{ value: 4, stationName: 's3' },
+					{ value: 9, stationName: 's4' },
+					{ value: 3, stationName: 's5' },
+					{ value: 2, stationName: 's6' },
+				],
+			},
+			{
+				name: '回温',
+				type: 'line',
+				smooth: true,
+				showSymbol: true,
+				symbol: 'emptyCircle',
+				symbolSize: 12,
+				yAxisIndex: 0,
+				areaStyle: {
+					color: new echarts.graphic.LinearGradient(
+						0,
+						0,
+						0,
+						1,
+						[
+							{ offset: 0, color: 'rgba(199, 237, 250,0.5)' },
+							{ offset: 1, color: 'rgba(199, 237, 250,0.2)' },
+						],
+						false
+					),
+				},
+				itemStyle: {
+					color: '#3bbc86',
+				},
+				data: [
+					{ value: 31, stationName: 's1' },
+					{ value: 36, stationName: 's2' },
+					{ value: 54, stationName: 's3' },
+					{ value: 24, stationName: 's4' },
+					{ value: 73, stationName: 's5' },
+					{ value: 22, stationName: 's6' },
+				],
+			},
+			{
+				name: '压力值(Mpa)',
+				type: 'bar',
+				barWidth: 30,
+				yAxisIndex: 1,
+				itemStyle: {
+					color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+						{ offset: 0, color: 'rgba(108,80,243,0.3)' },
+						{ offset: 1, color: 'rgba(108,80,243,0)' },
+					]),
+					//柱状图圆角
+					borderRadius: [30, 30, 0, 0],
+				},
+				data: [
+					{ value: 11, stationName: 's1' },
+					{ value: 34, stationName: 's2' },
+					{ value: 54, stationName: 's3' },
+					{ value: 39, stationName: 's4' },
+					{ value: 63, stationName: 's5' },
+					{ value: 24, stationName: 's6' },
+				],
+			},
+		],
+	};
+	state.global.homeCharThree.setOption(option);
+	state.myCharts.push(state.global.homeCharThree);
+};
+// 批量设置 echarts resize
+const initEchartsResizeFun = () => {
+	nextTick(() => {
+		for (let i = 0; i < state.myCharts.length; i++) {
+			setTimeout(() => {
+				state.myCharts[i].resize();
+			}, i * 1000);
+		}
+	});
+};
+// 批量设置 echarts resize
+const initEchartsResize = () => {
+	window.addEventListener('resize', initEchartsResizeFun);
+};
+// 页面加载时
+onMounted(() => {
+	initEchartsResize();
+});
+// 由于页面缓存原因,keep-alive
+onActivated(() => {
+	initEchartsResizeFun();
+});
+// 监听 pinia 中的 tagsview 开启全屏变化,重新 resize 图表,防止不出现/大小不变等
+watch(
+	() => isTagsViewCurrenFull.value,
+	() => {
+		initEchartsResizeFun();
+	}
+);
+// 监听 pinia 中是否开启深色主题
+watch(
+	() => themeConfig.value.isIsDark,
+	(isIsDark) => {
+		nextTick(() => {
+			state.charts.theme = isIsDark ? 'dark' : '';
+			state.charts.bgColor = isIsDark ? 'transparent' : '';
+			state.charts.color = isIsDark ? '#dadada' : '#303133';
+			setTimeout(() => {
+				initLineChart();
+			}, 500);
+			setTimeout(() => {
+				initPieChart();
+			}, 700);
+			setTimeout(() => {
+				initBarChart();
+			}, 1000);
+		});
+	},
+	{
+		deep: true,
+		immediate: true,
+	}
+);
+</script>
+
+<style scoped lang="scss">
+$homeNavLengh: 8;
+.home-container {
+	overflow: hidden;
+	.home-card-one,
+	.home-card-two,
+	.home-card-three {
+		.home-card-item {
+			width: 100%;
+			height: 130px;
+			border-radius: 4px;
+			transition: all ease 0.3s;
+			padding: 20px;
+			overflow: hidden;
+			background: var(--el-color-white);
+			color: var(--el-text-color-primary);
+			border: 1px solid var(--next-border-color-light);
+			&:hover {
+				box-shadow: 0 2px 12px var(--next-color-dark-hover);
+				transition: all ease 0.3s;
+			}
+			&-icon {
+				width: 70px;
+				height: 70px;
+				border-radius: 100%;
+				flex-shrink: 1;
+				i {
+					color: var(--el-text-color-placeholder);
+				}
+			}
+			&-title {
+				font-size: 15px;
+				font-weight: bold;
+				height: 30px;
+			}
+		}
+	}
+	.home-card-one {
+		@for $i from 0 through 3 {
+			.home-one-animation#{$i} {
+				opacity: 0;
+				animation-name: error-num;
+				animation-duration: 0.5s;
+				animation-fill-mode: forwards;
+				animation-delay: calc($i/4) + s;
+			}
+		}
+	}
+	.home-card-two,
+	.home-card-three {
+		.home-card-item {
+			height: 400px;
+			width: 100%;
+			overflow: hidden;
+			.home-monitor {
+				height: 100%;
+				.flex-warp-item {
+					width: 25%;
+					height: 111px;
+					display: flex;
+					.flex-warp-item-box {
+						margin: auto;
+						text-align: center;
+						color: var(--el-text-color-primary);
+						display: flex;
+						border-radius: 5px;
+						background: var(--next-bg-color);
+						cursor: pointer;
+						transition: all 0.3s ease;
+						&:hover {
+							background: var(--el-color-primary-light-9);
+							transition: all 0.3s ease;
+						}
+					}
+					@for $i from 0 through $homeNavLengh {
+						.home-animation#{$i} {
+							opacity: 0;
+							animation-name: error-num;
+							animation-duration: 0.5s;
+							animation-fill-mode: forwards;
+							animation-delay: calc($i/10) + s;
+						}
+					}
+				}
+			}
+		}
+	}
+}
+</style>
Added +181 -0
diff --git a/src/views/login/component/account.vue b/src/views/login/component/account.vue
new file mode 100644
index 0000000..7f29d74
--- /dev/null
+++ b/src/views/login/component/account.vue
@@ -0,0 +1,181 @@
+<template>
+	<el-form size="large" class="login-content-form">
+		<el-form-item class="login-animation1">
+			<el-input text :placeholder="$t('message.account.accountPlaceholder1')" v-model="state.ruleForm.userName" clearable autocomplete="off">
+				<template #prefix>
+					<el-icon class="el-input__icon"><ele-User /></el-icon>
+				</template>
+			</el-input>
+		</el-form-item>
+		<el-form-item class="login-animation2">
+			<el-input
+				:type="state.isShowPassword ? 'text' : 'password'"
+				:placeholder="$t('message.account.accountPlaceholder2')"
+				v-model="state.ruleForm.password"
+				autocomplete="off"
+			>
+				<template #prefix>
+					<el-icon class="el-input__icon"><ele-Unlock /></el-icon>
+				</template>
+				<template #suffix>
+					<i
+						class="iconfont el-input__icon login-content-password"
+						:class="state.isShowPassword ? 'icon-yincangmima' : 'icon-xianshimima'"
+						@click="state.isShowPassword = !state.isShowPassword"
+					>
+					</i>
+				</template>
+			</el-input>
+		</el-form-item>
+		<el-form-item class="login-animation3">
+			<el-col :span="15">
+				<el-input
+					text
+					maxlength="4"
+					:placeholder="$t('message.account.accountPlaceholder3')"
+					v-model="state.ruleForm.code"
+					clearable
+					autocomplete="off"
+				>
+					<template #prefix>
+						<el-icon class="el-input__icon"><ele-Position /></el-icon>
+					</template>
+				</el-input>
+			</el-col>
+			<el-col :span="1"></el-col>
+			<el-col :span="8">
+				<el-button class="login-content-code" v-waves>1234</el-button>
+			</el-col>
+		</el-form-item>
+		<el-form-item class="login-animation4">
+			<el-button type="primary" class="login-content-submit" round v-waves @click="onSignIn" :loading="state.loading.signIn">
+				<span>{{ $t('message.account.accountBtnText') }}</span>
+			</el-button>
+		</el-form-item>
+	</el-form>
+</template>
+
+<script setup lang="ts" name="loginAccount">
+import { reactive, computed } from 'vue';
+import { useRoute, useRouter } from 'vue-router';
+import { ElMessage } from 'element-plus';
+import { useI18n } from 'vue-i18n';
+import Cookies from 'js-cookie';
+import { storeToRefs } from 'pinia';
+import { useThemeConfig } from '/@/stores/themeConfig';
+import { initFrontEndControlRoutes } from '/@/router/frontEnd';
+import { initBackEndControlRoutes } from '/@/router/backEnd';
+import { Session } from '/@/utils/storage';
+import { formatAxis } from '/@/utils/formatTime';
+import { NextLoading } from '/@/utils/loading';
+import { useUserApi } from '/@/api/user';
+
+// 定义变量内容
+const { t } = useI18n();
+const storesThemeConfig = useThemeConfig();
+const { themeConfig } = storeToRefs(storesThemeConfig);
+const route = useRoute();
+const router = useRouter();
+const userApi = useUserApi();
+const state = reactive({
+	isShowPassword: false,
+	ruleForm: {
+		userName: '',
+		password: '',
+		code: '',
+	},
+	loading: {
+		signIn: false,
+	},
+});
+
+// 时间获取
+const currentTime = computed(() => {
+	return formatAxis(new Date());
+});
+// 登录
+const onSignIn = async () => {
+	state.loading.signIn = true;
+	// 存储 token 到浏览器缓存
+	// 模拟数据,对接接口时,记得删除多余代码及对应依赖的引入。用于 `/src/stores/userInfo.ts` 中不同用户登录判断(模拟数据)
+	userApi.signIn(state.ruleForm).then(async res => {
+		// console.log(res.data.token)
+		Session.set('token', res.data.token);
+		Cookies.set('userName', state.ruleForm.userName);
+		if (!themeConfig.value.isRequestRoutes) {
+			// 前端控制路由,2、请注意执行顺序
+			const isNoPower = await initFrontEndControlRoutes();
+			signInSuccess(isNoPower);
+		} else {
+			// 模拟后端控制路由,isRequestRoutes 为 true,则开启后端控制路由
+			// 添加完动态路由,再进行 router 跳转,否则可能报错 No match found for location with path "/"
+			const isNoPower = await initBackEndControlRoutes();
+			// 执行完 initBackEndControlRoutes,再执行 signInSuccess
+			signInSuccess(isNoPower);
+		}
+	}).catch(() => {
+		state.loading.signIn = false;
+	})
+};
+// 登录成功后的跳转
+const signInSuccess = (isNoPower: boolean | undefined) => {
+	if (isNoPower) {
+		ElMessage.warning('抱歉,您没有登录权限');
+		Session.clear();
+	} else {
+		// 初始化登录成功时间问候语
+		let currentTimeInfo = currentTime.value;
+		// 登录成功,跳到转首页
+		// 如果是复制粘贴的路径,非首页/登录页,那么登录成功后重定向到对应的路径中
+		if (route.query?.redirect) {
+			router.push({
+				path: <string>route.query?.redirect,
+				query: Object.keys(<string>route.query?.params).length > 0 ? JSON.parse(<string>route.query?.params) : '',
+			});
+		} else {
+			router.push('/');
+		}
+		// 登录成功提示
+		const signInText = t('message.signInText');
+		ElMessage.success(`${currentTimeInfo},${signInText}`);
+		// 添加 loading,防止第一次进入界面时出现短暂空白
+		NextLoading.start();
+	}
+	state.loading.signIn = false;
+};
+</script>
+
+<style scoped lang="scss">
+.login-content-form {
+	margin-top: 20px;
+	@for $i from 1 through 4 {
+		.login-animation#{$i} {
+			opacity: 0;
+			animation-name: error-num;
+			animation-duration: 0.5s;
+			animation-fill-mode: forwards;
+			animation-delay: calc($i/10) + s;
+		}
+	}
+	.login-content-password {
+		display: inline-block;
+		width: 20px;
+		cursor: pointer;
+		&:hover {
+			color: #909399;
+		}
+	}
+	.login-content-code {
+		width: 100%;
+		padding: 0;
+		font-weight: bold;
+		letter-spacing: 5px;
+	}
+	.login-content-submit {
+		width: 100%;
+		letter-spacing: 2px;
+		font-weight: 300;
+		margin-top: 15px;
+	}
+}
+</style>
Added +70 -0
diff --git a/src/views/login/component/mobile.vue b/src/views/login/component/mobile.vue
new file mode 100644
index 0000000..6f47b55
--- /dev/null
+++ b/src/views/login/component/mobile.vue
@@ -0,0 +1,70 @@
+<template>
+	<el-form size="large" class="login-content-form">
+		<el-form-item class="login-animation1">
+			<el-input text :placeholder="$t('message.mobile.placeholder1')" v-model="state.ruleForm.userName" clearable autocomplete="off">
+				<template #prefix>
+					<i class="iconfont icon-dianhua el-input__icon"></i>
+				</template>
+			</el-input>
+		</el-form-item>
+		<el-form-item class="login-animation2">
+			<el-col :span="15">
+				<el-input text maxlength="4" :placeholder="$t('message.mobile.placeholder2')" v-model="state.ruleForm.code" clearable autocomplete="off">
+					<template #prefix>
+						<el-icon class="el-input__icon"><ele-Position /></el-icon>
+					</template>
+				</el-input>
+			</el-col>
+			<el-col :span="1"></el-col>
+			<el-col :span="8">
+				<el-button v-waves class="login-content-code">{{ $t('message.mobile.codeText') }}</el-button>
+			</el-col>
+		</el-form-item>
+		<el-form-item class="login-animation3">
+			<el-button round type="primary" v-waves class="login-content-submit">
+				<span>{{ $t('message.mobile.btnText') }}</span>
+			</el-button>
+		</el-form-item>
+		<div class="font12 mt30 login-animation4 login-msg">{{ $t('message.mobile.msgText') }}</div>
+	</el-form>
+</template>
+
+<script setup lang="ts" name="loginMobile">
+import { reactive } from 'vue';
+
+// 定义变量内容
+const state = reactive({
+	ruleForm: {
+		userName: '',
+		code: '',
+	},
+});
+</script>
+
+<style scoped lang="scss">
+.login-content-form {
+	margin-top: 20px;
+	@for $i from 1 through 4 {
+		.login-animation#{$i} {
+			opacity: 0;
+			animation-name: error-num;
+			animation-duration: 0.5s;
+			animation-fill-mode: forwards;
+			animation-delay: calc($i/10) + s;
+		}
+	}
+	.login-content-code {
+		width: 100%;
+		padding: 0;
+	}
+	.login-content-submit {
+		width: 100%;
+		letter-spacing: 2px;
+		font-weight: 300;
+		margin-top: 15px;
+	}
+	.login-msg {
+		color: var(--el-text-color-placeholder);
+	}
+}
+</style>
Added +63 -0
diff --git a/src/views/login/component/scan.vue b/src/views/login/component/scan.vue
new file mode 100644
index 0000000..c719bf0
--- /dev/null
+++ b/src/views/login/component/scan.vue
@@ -0,0 +1,63 @@
+<template>
+	<div class="login-scan-container">
+		<div ref="qrcodeRef"></div>
+		<div class="font12 mt20 login-msg">
+			<i class="iconfont icon-saoyisao mr5"></i>
+			<span>{{ $t('message.scan.text') }}</span>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts" name="loginScan">
+import { ref, onMounted, nextTick } from 'vue';
+import QRCode from 'qrcodejs2-fixes';
+
+// 定义变量内容
+const qrcodeRef = ref<HTMLElement | null>(null);
+
+// 初始化生成二维码
+const initQrcode = () => {
+	nextTick(() => {
+		(<HTMLElement>qrcodeRef.value).innerHTML = '';
+		new QRCode(qrcodeRef.value, {
+			text: `https://qm.qq.com/cgi-bin/qm/qr?k=RdUY97Vx0T0vZ_1OOu-X1yFNkWgDwbjC&jump_from=webapi`,
+			width: 260,
+			height: 260,
+			colorDark: '#000000',
+			colorLight: '#ffffff',
+		});
+	});
+};
+// 页面加载时
+onMounted(() => {
+	initQrcode();
+});
+</script>
+
+<style scoped lang="scss">
+.login-scan-animation {
+	opacity: 0;
+	animation-name: error-num;
+	animation-duration: 0.5s;
+	animation-fill-mode: forwards;
+}
+.login-scan-container {
+	padding: 0 20px 20px;
+	display: flex;
+	flex-direction: column;
+	text-align: center;
+	@extend .login-scan-animation;
+	animation-delay: 0.1s;
+	:deep(img) {
+		margin: auto;
+	}
+	.login-msg {
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		color: var(--el-text-color-placeholder);
+		@extend .login-scan-animation;
+		animation-delay: 0.2s;
+	}
+}
+</style>
Added +254 -0
diff --git a/src/views/login/index.vue b/src/views/login/index.vue
new file mode 100644
index 0000000..111539d
--- /dev/null
+++ b/src/views/login/index.vue
@@ -0,0 +1,254 @@
+<template>
+	<div class="login-container flex">
+		<div class="login-left">
+			<div class="login-left-logo">
+				<img :src="logoMini" />
+				<div class="login-left-logo-text">
+					<span>{{ getThemeConfig.globalViceTitle }}</span>
+					<span class="login-left-logo-text-msg">{{ getThemeConfig.globalViceTitleMsg }}</span>
+				</div>
+			</div>
+			<div class="login-left-img">
+				<img :src="loginMain" />
+			</div>
+			<img :src="loginBg" class="login-left-waves" />
+		</div>
+		<div class="login-right flex">
+			<div class="login-right-warp flex-margin">
+				<span class="login-right-warp-one"></span>
+				<span class="login-right-warp-two"></span>
+				<div class="login-right-warp-mian">
+					<div class="login-right-warp-main-title">{{ getThemeConfig.globalTitle }} 欢迎您!</div>
+					<div class="login-right-warp-main-form">
+						<div v-if="!state.isScan">
+							<el-tabs v-model="state.tabsActiveName">
+								<el-tab-pane :label="$t('message.label.one1')" name="account">
+									<Account />
+								</el-tab-pane>
+								<el-tab-pane :label="$t('message.label.two2')" name="mobile">
+									<Mobile />
+								</el-tab-pane>
+							</el-tabs>
+						</div>
+						<Scan v-if="state.isScan" />
+						<div class="login-content-main-sacn" @click="state.isScan = !state.isScan">
+							<i class="iconfont" :class="state.isScan ? 'icon-diannao1' : 'icon-barcode-qr'"></i>
+							<div class="login-content-main-sacn-delta"></div>
+						</div>
+					</div>
+				</div>
+			</div>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts" name="loginIndex">
+import { defineAsyncComponent, onMounted, reactive, computed } from 'vue';
+import { storeToRefs } from 'pinia';
+import { useThemeConfig } from '/@/stores/themeConfig';
+import { NextLoading } from '/@/utils/loading';
+import logoMini from '/@/assets/logo-mini.png';
+import loginMain from '/@/assets/login-main.svg';
+import loginBg from '/@/assets/login-bg.svg';
+
+// 引入组件
+const Account = defineAsyncComponent(() => import('/@/views/login/component/account.vue'));
+const Mobile = defineAsyncComponent(() => import('/@/views/login/component/mobile.vue'));
+const Scan = defineAsyncComponent(() => import('/@/views/login/component/scan.vue'));
+
+// 定义变量内容
+const storesThemeConfig = useThemeConfig();
+const { themeConfig } = storeToRefs(storesThemeConfig);
+const state = reactive({
+	tabsActiveName: 'account',
+	isScan: false,
+});
+
+// 获取布局配置信息
+const getThemeConfig = computed(() => {
+	return themeConfig.value;
+});
+// 页面加载时
+onMounted(() => {
+	NextLoading.done();
+});
+</script>
+
+<style scoped lang="scss">
+.login-container {
+	height: 100%;
+	background: var(--el-color-white);
+	.login-left {
+		flex: 1;
+		position: relative;
+		background-color: rgba(211, 239, 255, 1);
+		margin-right: 100px;
+		.login-left-logo {
+			display: flex;
+			align-items: center;
+			position: absolute;
+			top: 50px;
+			left: 80px;
+			z-index: 1;
+			animation: logoAnimation 0.3s ease;
+			img {
+				width: 52px;
+				height: 52px;
+			}
+			.login-left-logo-text {
+				display: flex;
+				flex-direction: column;
+				span {
+					margin-left: 10px;
+					font-size: 28px;
+					color: #26a59a;
+				}
+				.login-left-logo-text-msg {
+					font-size: 12px;
+					color: #32a99e;
+				}
+			}
+		}
+		.login-left-img {
+			position: absolute;
+			top: 50%;
+			left: 50%;
+			transform: translate(-50%, -50%);
+			width: 100%;
+			height: 52%;
+			img {
+				width: 100%;
+				height: 100%;
+				animation: error-num 0.6s ease;
+			}
+		}
+		.login-left-waves {
+			position: absolute;
+			top: 0;
+			right: -100px;
+		}
+	}
+	.login-right {
+		width: 700px;
+		.login-right-warp {
+			border: 1px solid var(--el-color-primary-light-3);
+			border-radius: 3px;
+			width: 500px;
+			height: 500px;
+			position: relative;
+			overflow: hidden;
+			background-color: var(--el-color-white);
+			.login-right-warp-one,
+			.login-right-warp-two {
+				position: absolute;
+				display: block;
+				width: inherit;
+				height: inherit;
+				&::before,
+				&::after {
+					content: '';
+					position: absolute;
+					z-index: 1;
+				}
+			}
+			.login-right-warp-one {
+				&::before {
+					filter: hue-rotate(0deg);
+					top: 0px;
+					left: 0;
+					width: 100%;
+					height: 3px;
+					background: linear-gradient(90deg, transparent, var(--el-color-primary));
+					animation: loginLeft 3s linear infinite;
+				}
+				&::after {
+					filter: hue-rotate(60deg);
+					top: -100%;
+					right: 2px;
+					width: 3px;
+					height: 100%;
+					background: linear-gradient(180deg, transparent, var(--el-color-primary));
+					animation: loginTop 3s linear infinite;
+					animation-delay: 0.7s;
+				}
+			}
+			.login-right-warp-two {
+				&::before {
+					filter: hue-rotate(120deg);
+					bottom: 2px;
+					right: -100%;
+					width: 100%;
+					height: 3px;
+					background: linear-gradient(270deg, transparent, var(--el-color-primary));
+					animation: loginRight 3s linear infinite;
+					animation-delay: 1.4s;
+				}
+				&::after {
+					filter: hue-rotate(300deg);
+					bottom: -100%;
+					left: 0px;
+					width: 3px;
+					height: 100%;
+					background: linear-gradient(360deg, transparent, var(--el-color-primary));
+					animation: loginBottom 3s linear infinite;
+					animation-delay: 2.1s;
+				}
+			}
+			.login-right-warp-mian {
+				display: flex;
+				flex-direction: column;
+				height: 100%;
+				.login-right-warp-main-title {
+					height: 130px;
+					line-height: 130px;
+					font-size: 27px;
+					text-align: center;
+					letter-spacing: 3px;
+					animation: logoAnimation 0.3s ease;
+					animation-delay: 0.3s;
+					color: var(--el-text-color-primary);
+				}
+				.login-right-warp-main-form {
+					flex: 1;
+					padding: 0 50px 50px;
+					.login-content-main-sacn {
+						position: absolute;
+						top: 0;
+						right: 0;
+						width: 50px;
+						height: 50px;
+						overflow: hidden;
+						cursor: pointer;
+						transition: all ease 0.3s;
+						color: var(--el-color-primary);
+						&-delta {
+							position: absolute;
+							width: 35px;
+							height: 70px;
+							z-index: 2;
+							top: 2px;
+							right: 21px;
+							background: var(--el-color-white);
+							transform: rotate(-45deg);
+						}
+						&:hover {
+							opacity: 1;
+							transition: all ease 0.3s;
+							color: var(--el-color-primary) !important;
+						}
+						i {
+							width: 47px;
+							height: 50px;
+							display: inline-block;
+							font-size: 48px;
+							position: absolute;
+							right: 1px;
+							top: 0px;
+						}
+					}
+				}
+			}
+		}
+	}
+}
+</style>
Added +51 -0
diff --git a/src/views/modules/admin/user/index.vue b/src/views/modules/admin/user/index.vue
new file mode 100644
index 0000000..dd5ba08
--- /dev/null
+++ b/src/views/modules/admin/user/index.vue
@@ -0,0 +1,51 @@
+<template>
+  <Page v-bind="$attrs" @setting="setting">
+    <template #mail>
+      测试
+    </template>
+  </Page>
+</template>
+
+<script setup lang="ts" name="User">
+import usePageSetting from '/@/hook/usePageSetting'
+import { ColumnKind, usePageApi } from '/@/api/page';
+const { setting, columns, forms } = usePageSetting({
+  columns: [
+    {
+      prop: 'sex',
+      component: 'radioGroup',
+      props: {
+        options: [{ id: 1, name: '男' }, { id: 2, name: '女' }]
+      },
+    },
+    {
+      prop: 'mail',
+      slot: 'mail',
+    },
+    {
+      prop: 'departmentID',
+      component: 'select',
+      props: {
+        api: () => usePageApi().getTableData('/admin/department', { pageIndex: 0 })
+      }
+    },
+    {
+      prop: 'roleID',
+      component: 'select',
+      props: {
+        url: '/admin/role'
+      }
+    },
+    {
+      in: [ColumnKind.ADD, ColumnKind.EDIT],
+      prop: 'name',
+      props: {
+        onChange: (val: string) => {
+          forms.data!.mail = val
+          columns.add!.find(item => item.prop === 'sex')!.if = !val
+        }
+      }
+    }
+  ]
+})
+</script>
Added +8 -0
diff --git a/src/views/modules/index.vue b/src/views/modules/index.vue
new file mode 100644
index 0000000..a85ffdc
--- /dev/null
+++ b/src/views/modules/index.vue
@@ -0,0 +1,8 @@
+<template>
+  <Page :type="route.path"></Page>
+</template>
+
+<script setup lang="ts">
+import { useRoute } from 'vue-router';
+const route = useRoute();
+</script>
Added +373 -0
diff --git a/src/views/personal/index.vue b/src/views/personal/index.vue
new file mode 100644
index 0000000..4ca238e
--- /dev/null
+++ b/src/views/personal/index.vue
@@ -0,0 +1,373 @@
+<template>
+	<div class="personal layout-pd">
+		<el-row>
+			<!-- 个人信息 -->
+			<el-col :xs="24" :sm="16">
+				<el-card shadow="hover" header="个人信息">
+					<div class="personal-user">
+						<div class="personal-user-left">
+							<el-upload class="h100 personal-user-left-upload" action="https://jsonplaceholder.typicode.com/posts/" multiple :limit="1">
+								<img src="https://img2.baidu.com/it/u=1978192862,2048448374&fm=253&fmt=auto&app=138&f=JPEG?w=504&h=500" />
+							</el-upload>
+						</div>
+						<div class="personal-user-right">
+							<el-row>
+								<el-col :span="24" class="personal-title mb18">{{ currentTime }},admin,生活变的再糟糕,也不妨碍我变得更好! </el-col>
+								<el-col :span="24">
+									<el-row>
+										<el-col :xs="24" :sm="8" class="personal-item mb6">
+											<div class="personal-item-label">昵称:</div>
+											<div class="personal-item-value">小柒</div>
+										</el-col>
+										<el-col :xs="24" :sm="16" class="personal-item mb6">
+											<div class="personal-item-label">身份:</div>
+											<div class="personal-item-value">超级管理</div>
+										</el-col>
+									</el-row>
+								</el-col>
+								<el-col :span="24">
+									<el-row>
+										<el-col :xs="24" :sm="8" class="personal-item mb6">
+											<div class="personal-item-label">登录IP:</div>
+											<div class="personal-item-value">192.168.1.1</div>
+										</el-col>
+										<el-col :xs="24" :sm="16" class="personal-item mb6">
+											<div class="personal-item-label">登录时间:</div>
+											<div class="personal-item-value">2021-02-05 18:47:26</div>
+										</el-col>
+									</el-row>
+								</el-col>
+							</el-row>
+						</div>
+					</div>
+				</el-card>
+			</el-col>
+
+			<!-- 消息通知 -->
+			<el-col :xs="24" :sm="8" class="pl15 personal-info">
+				<el-card shadow="hover">
+					<template #header>
+						<span>消息通知</span>
+						<span class="personal-info-more">更多</span>
+					</template>
+					<div class="personal-info-box">
+						<ul class="personal-info-ul">
+							<li v-for="(v, k) in state.newsInfoList" :key="k" class="personal-info-li">
+								<a :href="v.link" target="_block" class="personal-info-li-title">{{ v.title }}</a>
+							</li>
+						</ul>
+					</div>
+				</el-card>
+			</el-col>
+
+			<!-- 营销推荐 -->
+			<el-col :span="24">
+				<el-card shadow="hover" class="mt15" header="营销推荐">
+					<el-row :gutter="15" class="personal-recommend-row">
+						<el-col :sm="6" v-for="(v, k) in state.recommendList" :key="k" class="personal-recommend-col">
+							<div class="personal-recommend" :style="{ 'background-color': v.bg }">
+								<SvgIcon :name="v.icon" :size="70" :style="{ color: v.iconColor }" />
+								<div class="personal-recommend-auto">
+									<div>{{ v.title }}</div>
+									<div class="personal-recommend-msg">{{ v.msg }}</div>
+								</div>
+							</div>
+						</el-col>
+					</el-row>
+				</el-card>
+			</el-col>
+
+			<!-- 更新信息 -->
+			<el-col :span="24">
+				<el-card shadow="hover" class="mt15 personal-edit" header="更新信息">
+					<div class="personal-edit-title">基本信息</div>
+					<el-form :model="state.personalForm" size="default" label-width="40px" class="mt35 mb35">
+						<el-row :gutter="35">
+							<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
+								<el-form-item label="昵称">
+									<el-input v-model="state.personalForm.name" placeholder="请输入昵称" clearable></el-input>
+								</el-form-item>
+							</el-col>
+							<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
+								<el-form-item label="邮箱">
+									<el-input v-model="state.personalForm.email" placeholder="请输入邮箱" clearable></el-input>
+								</el-form-item>
+							</el-col>
+							<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
+								<el-form-item label="签名">
+									<el-input v-model="state.personalForm.autograph" placeholder="请输入签名" clearable></el-input>
+								</el-form-item>
+							</el-col>
+							<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
+								<el-form-item label="职业">
+									<el-select v-model="state.personalForm.occupation" placeholder="请选择职业" clearable class="w100">
+										<el-option label="计算机 / 互联网 / 通信" value="1"></el-option>
+										<el-option label="生产 / 工艺 / 制造" value="2"></el-option>
+										<el-option label="医疗 / 护理 / 制药" value="3"></el-option>
+									</el-select>
+								</el-form-item>
+							</el-col>
+							<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
+								<el-form-item label="手机">
+									<el-input v-model="state.personalForm.phone" placeholder="请输入手机" clearable></el-input>
+								</el-form-item>
+							</el-col>
+							<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
+								<el-form-item label="性别">
+									<el-select v-model="state.personalForm.sex" placeholder="请选择性别" clearable class="w100">
+										<el-option label="男" value="1"></el-option>
+										<el-option label="女" value="2"></el-option>
+									</el-select>
+								</el-form-item>
+							</el-col>
+							<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+								<el-form-item>
+									<el-button type="primary">
+										<el-icon>
+											<ele-Position />
+										</el-icon>
+										更新个人信息
+									</el-button>
+								</el-form-item>
+							</el-col>
+						</el-row>
+					</el-form>
+					<div class="personal-edit-title mb15">账号安全</div>
+					<div class="personal-edit-safe-box">
+						<div class="personal-edit-safe-item">
+							<div class="personal-edit-safe-item-left">
+								<div class="personal-edit-safe-item-left-label">账户密码</div>
+								<div class="personal-edit-safe-item-left-value">当前密码强度:强</div>
+							</div>
+							<div class="personal-edit-safe-item-right">
+								<el-button text type="primary">立即修改</el-button>
+							</div>
+						</div>
+					</div>
+					<div class="personal-edit-safe-box">
+						<div class="personal-edit-safe-item">
+							<div class="personal-edit-safe-item-left">
+								<div class="personal-edit-safe-item-left-label">密保手机</div>
+								<div class="personal-edit-safe-item-left-value">已绑定手机:132****4108</div>
+							</div>
+							<div class="personal-edit-safe-item-right">
+								<el-button text type="primary">立即修改</el-button>
+							</div>
+						</div>
+					</div>
+					<div class="personal-edit-safe-box">
+						<div class="personal-edit-safe-item">
+							<div class="personal-edit-safe-item-left">
+								<div class="personal-edit-safe-item-left-label">密保问题</div>
+								<div class="personal-edit-safe-item-left-value">已设置密保问题,账号安全大幅度提升</div>
+							</div>
+							<div class="personal-edit-safe-item-right">
+								<el-button text type="primary">立即设置</el-button>
+							</div>
+						</div>
+					</div>
+					<div class="personal-edit-safe-box">
+						<div class="personal-edit-safe-item">
+							<div class="personal-edit-safe-item-left">
+								<div class="personal-edit-safe-item-left-label">绑定QQ</div>
+								<div class="personal-edit-safe-item-left-value">已绑定QQ:110****566</div>
+							</div>
+							<div class="personal-edit-safe-item-right">
+								<el-button text type="primary">立即设置</el-button>
+							</div>
+						</div>
+					</div>
+				</el-card>
+			</el-col>
+		</el-row>
+	</div>
+</template>
+
+<script setup lang="ts" name="personal">
+import { reactive, computed } from 'vue';
+import { formatAxis } from '/@/utils/formatTime';
+import { newsInfoList, recommendList } from './mock';
+
+// 定义变量内容
+const state = reactive<PersonalState>({
+	newsInfoList,
+	recommendList,
+	personalForm: {
+		name: '',
+		email: '',
+		autograph: '',
+		occupation: '',
+		phone: '',
+		sex: '',
+	},
+});
+
+// 当前时间提示语
+const currentTime = computed(() => {
+	return formatAxis(new Date());
+});
+</script>
+
+<style scoped lang="scss">
+@import '../../theme/mixins/index.scss';
+.personal {
+	.personal-user {
+		height: 130px;
+		display: flex;
+		align-items: center;
+		.personal-user-left {
+			width: 100px;
+			height: 130px;
+			border-radius: 3px;
+			:deep(.el-upload) {
+				height: 100%;
+			}
+			.personal-user-left-upload {
+				img {
+					width: 100%;
+					height: 100%;
+					border-radius: 3px;
+				}
+				&:hover {
+					img {
+						animation: logoAnimation 0.3s ease-in-out;
+					}
+				}
+			}
+		}
+		.personal-user-right {
+			flex: 1;
+			padding: 0 15px;
+			.personal-title {
+				font-size: 18px;
+				@include text-ellipsis(1);
+			}
+			.personal-item {
+				display: flex;
+				align-items: center;
+				font-size: 13px;
+				.personal-item-label {
+					color: var(--el-text-color-secondary);
+					@include text-ellipsis(1);
+				}
+				.personal-item-value {
+					@include text-ellipsis(1);
+				}
+			}
+		}
+	}
+	.personal-info {
+		.personal-info-more {
+			float: right;
+			color: var(--el-text-color-secondary);
+			font-size: 13px;
+			&:hover {
+				color: var(--el-color-primary);
+				cursor: pointer;
+			}
+		}
+		.personal-info-box {
+			height: 130px;
+			overflow: hidden;
+			.personal-info-ul {
+				list-style: none;
+				.personal-info-li {
+					font-size: 13px;
+					padding-bottom: 10px;
+					.personal-info-li-title {
+						display: inline-block;
+						@include text-ellipsis(1);
+						color: var(--el-text-color-secondary);
+						text-decoration: none;
+					}
+					& a:hover {
+						color: var(--el-color-primary);
+						cursor: pointer;
+					}
+				}
+			}
+		}
+	}
+	.personal-recommend-row {
+		.personal-recommend-col {
+			.personal-recommend {
+				position: relative;
+				height: 100px;
+				border-radius: 3px;
+				overflow: hidden;
+				cursor: pointer;
+				&:hover {
+					i {
+						right: 0px !important;
+						bottom: 0px !important;
+						transition: all ease 0.3s;
+					}
+				}
+				i {
+					position: absolute;
+					right: -10px;
+					bottom: -10px;
+					font-size: 70px;
+					transform: rotate(-30deg);
+					transition: all ease 0.3s;
+				}
+				.personal-recommend-auto {
+					padding: 15px;
+					position: absolute;
+					left: 0;
+					top: 5%;
+					color: var(--next-color-white);
+					.personal-recommend-msg {
+						font-size: 12px;
+						margin-top: 10px;
+					}
+				}
+			}
+		}
+	}
+	.personal-edit {
+		.personal-edit-title {
+			position: relative;
+			padding-left: 10px;
+			color: var(--el-text-color-regular);
+			&::after {
+				content: '';
+				width: 2px;
+				height: 10px;
+				position: absolute;
+				left: 0;
+				top: 50%;
+				transform: translateY(-50%);
+				background: var(--el-color-primary);
+			}
+		}
+		.personal-edit-safe-box {
+			border-bottom: 1px solid var(--el-border-color-light, #ebeef5);
+			padding: 15px 0;
+			.personal-edit-safe-item {
+				width: 100%;
+				display: flex;
+				align-items: center;
+				justify-content: space-between;
+				.personal-edit-safe-item-left {
+					flex: 1;
+					overflow: hidden;
+					.personal-edit-safe-item-left-label {
+						color: var(--el-text-color-regular);
+						margin-bottom: 5px;
+					}
+					.personal-edit-safe-item-left-value {
+						color: var(--el-text-color-secondary);
+						@include text-ellipsis(1);
+						margin-right: 15px;
+					}
+				}
+			}
+			&:last-of-type {
+				padding-bottom: 0;
+				border-bottom: none;
+			}
+		}
+	}
+}
+</style>
Added +66 -0
diff --git a/src/views/personal/mock.ts b/src/views/personal/mock.ts
new file mode 100644
index 0000000..ca261b6
--- /dev/null
+++ b/src/views/personal/mock.ts
@@ -0,0 +1,66 @@
+/**
+ * 消息通知
+ * @returns 返回模拟数据
+ */
+export const newsInfoList = [
+	{
+		title: '[发布] 2021年02月28日发布基于 vue3.x + vite v1.0.0 版本',
+		date: '02/28',
+		link: 'https://gitee.com/lyt-top/vue-next-admin',
+	},
+	{
+		title: '[发布] 2021年04月15日发布 vue2.x + webpack 重构版本',
+		date: '04/15',
+		link: 'https://gitee.com/lyt-top/vue-next-admin/tree/vue-prev-admin/',
+	},
+	{
+		title: '[重构] 2021年04月10日 重构 vue2.x + webpack v1.0.0 版本',
+		date: '04/10',
+		link: 'https://gitee.com/lyt-top/vue-next-admin/tree/vue-prev-admin/',
+	},
+	{
+		title: '[预览] 2020年12月08日,基于 vue3.x 版本后台模板的预览',
+		date: '12/08',
+		link: 'http://lyt-top.gitee.io/vue-next-admin-preview/#/login',
+	},
+	{
+		title: '[预览] 2020年11月15日,基于 vue2.x 版本后台模板的预览',
+		date: '11/15',
+		link: 'https://lyt-top.gitee.io/vue-prev-admin-preview/#/login',
+	},
+];
+
+/**
+ * 营销推荐
+ * @returns 返回模拟数据
+ */
+export const recommendList = [
+	{
+		title: '优惠券',
+		msg: '现金券、折扣券、营销必备',
+		icon: 'ele-Food',
+		bg: '#48D18D',
+		iconColor: '#64d89d',
+	},
+	{
+		title: '多人拼团',
+		msg: '社交电商、开辟流量',
+		icon: 'ele-ShoppingCart',
+		bg: '#F95959',
+		iconColor: '#F86C6B',
+	},
+	{
+		title: '分销中心',
+		msg: '轻松招募分销员,成功推广奖励',
+		icon: 'ele-School',
+		bg: '#8595F4',
+		iconColor: '#92A1F4',
+	},
+	{
+		title: '秒杀',
+		msg: '超低价抢购引导更多销量',
+		icon: 'ele-AlarmClock',
+		bg: '#FEBB50',
+		iconColor: '#FDC566',
+	},
+];
Added +11 -0
diff --git a/tailwind.config.js b/tailwind.config.js
new file mode 100644
index 0000000..12f8da6
--- /dev/null
+++ b/tailwind.config.js
@@ -0,0 +1,11 @@
+/** @type {import('tailwindcss').Config} */
+module.exports = {
+  content: [
+    "./index.html",
+    "./src/**/*.{vue,js,ts,jsx,tsx}",
+  ],
+  theme: {
+    extend: {},
+  },
+  plugins: [],
+}
\ No newline at end of file
Added +75 -0
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..98f1fdc
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,75 @@
+{
+	"compilerOptions": {
+		/* Visit https://aka.ms/tsconfig.json to read more about this file */
+
+		/* Basic Options */
+		// "incremental": true,                   /* Enable incremental compilation */
+		"target": "esnext" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */,
+		"module": "esnext" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */,
+		"lib": ["esnext", "dom", "dom.iterable", "scripthost"] /* Specify library files to be included in the compilation. */,
+		// "allowJs": true,                       /* Allow javascript files to be compiled. */
+		// "checkJs": true,                       /* Report errors in .js files. */
+		"jsx": "preserve" /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */,
+		// "declaration": true /* Generates corresponding '.d.ts' file. */,
+		// "declarationMap": true,                /* Generates a sourcemap for each corresponding '.d.ts' file. */
+		// "sourceMap": true,                     /* Generates corresponding '.map' file. */
+		// "outFile": "./",                       /* Concatenate and emit output to single file. */
+		// "outDir": "./",                        /* Redirect output structure to the directory. */
+		// "rootDir": "./",                       /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
+		// "composite": true,                     /* Enable project compilation */
+		// "tsBuildInfoFile": "./",               /* Specify file to store incremental compilation information */
+		// "removeComments": true,                /* Do not emit comments to output. */
+		// "noEmit": true,                        /* Do not emit outputs. */
+		// "importHelpers": true /* Import emit helpers from 'tslib'. */,
+		// "downlevelIteration": true /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */,
+		"isolatedModules": true /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */,
+
+		/* Strict Type-Checking Options */
+		"strict": true /* Enable all strict type-checking options. */,
+		// "noImplicitAny": true,                 /* Raise error on expressions and declarations with an implied 'any' type. */
+		// "strictNullChecks": true,              /* Enable strict null checks. */
+		// "strictFunctionTypes": true,           /* Enable strict checking of function types. */
+		// "strictBindCallApply": true,           /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
+		// "strictPropertyInitialization": true,  /* Enable strict checking of property initialization in classes. */
+		// "noImplicitThis": true,                /* Raise error on 'this' expressions with an implied 'any' type. */
+		// "alwaysStrict": true,                  /* Parse in strict mode and emit "use strict" for each source file. */
+
+		/* Additional Checks */
+		// "noUnusedLocals": true,                /* Report errors on unused locals. */
+		// "noUnusedParameters": true,            /* Report errors on unused parameters. */
+		// "noImplicitReturns": true,             /* Report error when not all code paths in function return a value. */
+		// "noFallthroughCasesInSwitch": true,    /* Report errors for fallthrough cases in switch statement. */
+		// "noUncheckedIndexedAccess": true,      /* Include 'undefined' in index signature results */
+
+		/* Module Resolution Options */
+		"moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */,
+		"baseUrl": "." /* Base directory to resolve non-absolute module names. */,
+		"paths": {
+			"/@/*": ["src/*"]
+		} /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */,
+		// "rootDirs": [],                        /* List of root folders whose combined content represents the structure of the project at runtime. */
+		// "typeRoots": [],                       /* List of folders to include type definitions from. */
+		"types": ["vite/client"] /* Type declaration files to be included in compilation. */,
+		"allowSyntheticDefaultImports": true /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */,
+		"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */,
+		// "preserveSymlinks": true,              /* Do not resolve the real path of symlinks. */
+		// "allowUmdGlobalAccess": true,          /* Allow accessing UMD globals from modules. */
+
+		/* Source Map Options */
+		// "sourceRoot": "",                      /* Specify the location where debugger should locate TypeScript files instead of source locations. */
+		// "mapRoot": "",                         /* Specify the location where debugger should locate map files instead of generated locations. */
+		// "inlineSourceMap": true,               /* Emit a single file with source maps instead of having a separate file. */
+		// "inlineSources": true,                 /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
+
+		/* Experimental Options */
+		"experimentalDecorators": true /* Enables experimental support for ES7 decorators. */,
+		// "emitDecoratorMetadata": true,         /* Enables experimental support for emitting type metadata for decorators. */
+
+		/* Advanced Options */
+		"skipLibCheck": true /* Skip type checking of declaration files. */,
+		"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */,
+		"suppressImplicitAnyIndexErrors": true
+	},
+	"include": ["src/**/*.ts", "src/**/*.vue", "src/**/*.tsx", "src/**/*.d.ts"], // **Represents any directory, and * represents any file. Indicates that all files in the src directory will be compiled
+	"exclude": ["node_modules", "dist"] // Indicates the file directory that does not need to be compiled
+}
Added +66 -0
diff --git a/vite.config.ts b/vite.config.ts
new file mode 100644
index 0000000..604cddb
--- /dev/null
+++ b/vite.config.ts
@@ -0,0 +1,66 @@
+import vue from '@vitejs/plugin-vue';
+import { resolve } from 'path';
+import { defineConfig, loadEnv, ConfigEnv } from 'vite';
+import vueSetupExtend from 'vite-plugin-vue-setup-extend';
+
+const pathResolve = (dir: string) => {
+	return resolve(__dirname, '.', dir);
+};
+
+const alias: Record<string, string> = {
+	'/@': pathResolve('./src/'),
+	'vue-i18n': 'vue-i18n/dist/vue-i18n.cjs.js',
+};
+
+const viteConfig = defineConfig((mode: ConfigEnv) => {
+	const env = loadEnv(mode.mode, process.cwd());
+	return {
+		plugins: [vue(), vueSetupExtend()],
+		root: process.cwd(),
+		resolve: { alias },
+		base: mode.command === 'serve' ? './' : env.VITE_PUBLIC_PATH,
+		optimizeDeps: {
+			include: ['element-plus/lib/locale/lang/zh-cn', 'element-plus/lib/locale/lang/en', 'element-plus/lib/locale/lang/zh-tw'],
+		},
+		server: {
+			host: '0.0.0.0',
+			port: env.VITE_PORT as unknown as number,
+			open: JSON.parse(env.VITE_OPEN),
+			hmr: true,
+			proxy: {
+				'/base-api': {
+					target: 'https://cube.newlifex.com',
+					ws: true,
+					changeOrigin: true,
+					rewrite: (path) => path.replace(/^\/base-api/, ''),
+				},
+			},
+		},
+		build: {
+			outDir: 'dist',
+			chunkSizeWarningLimit: 1500,
+			rollupOptions: {
+				output: {
+					entryFileNames: `assets/[name].[hash].js`,
+					chunkFileNames: `assets/[name].[hash].js`,
+					assetFileNames: `assets/[name].[hash].[ext]`,
+					compact: true,
+					manualChunks: {
+						vue: ['vue', 'vue-router', 'pinia'],
+						echarts: ['echarts'],
+					},
+				},
+			},
+		},
+		css: { preprocessorOptions: { css: { charset: false } } },
+		define: {
+			__VUE_I18N_LEGACY_API__: JSON.stringify(false),
+			__VUE_I18N_FULL_INSTALL__: JSON.stringify(false),
+			__INTLIFY_PROD_DEVTOOLS__: JSON.stringify(false),
+			__NEXT_VERSION__: JSON.stringify(process.env.npm_package_version),
+			__NEXT_NAME__: JSON.stringify(process.env.npm_package_name),
+		},
+	};
+});
+
+export default viteConfig;