From 0a3027eccd3b0bb6542e5fc831f7e02740dcfc95 Mon Sep 17 00:00:00 2001
From: Your Name <123456>
Date: 星期一, 27 六月 2022 15:58:48 +0800
Subject: [PATCH] 'lct'

---
 src/components/auth/auth.vue                             |   30 
 source.d.ts                                              |    6 
 src/layout/index.vue                                     |   54 
 src/views/system/department/index.vue                    |  121 
 src/views/pages/formRules/component/formRulesThree.vue   |   50 
 src/layout/routerView/parent.vue                         |   88 
 src/theme/media/error.scss                               |   45 
 src/components/cropper/index.vue                         |  149 
 src/views/pages/dynamicForm/index.vue                    |  204 
 src/views/visualizing/demo1.vue                          | 1278 ++
 src/views/dashboard/index.vue                            |   35 
 src/utils/authDirective.ts                               |   40 
 src/views/make/selector/index.vue                        |  126 
 src/api/department/index.ts                              |   26 
 src/views/login/component/account.vue                    |  196 
 src/views/pages/filtering/details1.vue                   |   39 
 src/layout/lockScreen/index.vue                          |  372 
 src/utils/customDirective.ts                             |  178 
 src/views/error/404.vue                                  |  115 
 src/views/system/role/component/editRole.vue             |  242 
 src/layout/navBars/breadcrumb/search.vue                 |  138 
 src/views/system/user/component/editUser.vue             |  202 
 src/views/menu/menu2/index.vue                           |   21 
 .env.development                                         |    7 
 src/views/pages/filtering/details.vue                    |   39 
 src/layout/navBars/index.vue                             |   40 
 yarn.lock                                                | 2236 +++
 src/theme/media/home.scss                                |   23 
 src/views/pages/dynamicForm/mock.ts                      |  119 
 src/views/pages/element/index.vue                        |   89 
 src/views/system/menu/component/menuDialog.vue           |  247 
 src/views/system/role/index.vue                          |  167 
 package-lock.json                                        | 7716 +++++++++++++
 src/theme/media/dialog.scss                              |   12 
 src/views/login/index.vue                                |  190 
 src/views/pages/workflow/component/tool/index.vue        |   79 
 src/i18n/pages/login/zh-cn.ts                            |   28 
 src/views/pages/listAdapt/mock.ts                        |   93 
 vite.config.ts                                           |   79 
 src/views/chart/index.vue                                |  492 
 src/views/pages/tableRules/index.vue                     |  129 
 src/views/pages/formRules/component/formRulesOne.vue     |   68 
 src/views/error/401.vue                                  |  117 
 src/i18n/pages/formI18n/en.ts                            |   13 
 src/views/pages/workflow/js/mock.ts                      |  262 
 src/views/pages/workflow/component/drawer/index.vue      |   73 
 src/components/editor/index.vue                          |  115 
 src/views/pages/waterfall/index.vue                      |  174 
 src/layout/navMenu/vertical.vue                          |  101 
 src/theme/mixins/index.scss                              |   56 
 src/theme/media/personal.scss                            |   16 
 src/views/visualizing/images/bathymetry.jpg              |    0 
 src/router/backEnd.ts                                    |  110 
 src/theme/media/media.scss                               |   13 
 src/main.ts                                              |   22 
 src/views/menu/menu1/menu12/menu121/index.vue            |   21 
 src/utils/commonFunction.ts                              |   65 
 src/utils/theme.ts                                       |   59 
 src/views/test/index.vue                                 |   13 
 src/api/menu/index.ts                                    |   39 
 src/layout/navMenu/subItem.vue                           |   48 
 src/i18n/pages/login/zh-tw.ts                            |   28 
 src/layout/navBars/breadcrumb/setings.vue                |  816 +
 src/components/auth/authAll.vue                          |   31 
 src/theme/media/cityLinkage.scss                         |   10 
 src/layout/main/transverse.vue                           |   17 
 src/theme/index.scss                                     |    8 
 src/router/frontEnd.ts                                   |   97 
 src/views/menu/menu1/menu12/menu122/index.vue            |   21 
 src/views/visualizing/demo2.vue                          | 1344 ++
 src/views/pages/lazyImg/mock.ts                          |  313 
 .eslintrc.js                                             |   63 
 src/theme/loading.scss                                   |   51 
 src/views/pages/workflow/component/drawer/node.vue       |  272 
 index.html                                               |   31 
 src/views/system/dic/component/editDic.vue               |  162 
 src/views/system/dic/index.vue                           |  159 
 src/api/login/index.ts                                   |   25 
 LICENSE                                                  |   21 
 src/i18n/pages/login/en.ts                               |   29 
 src/utils/formatTime.ts                                  |  137 
 src/views/params/dynamic/index.vue                       |  102 
 CHANGELOG.md                                             |  356 
 src/stores/tagsViewRoutes.ts                             |   24 
 src/views/chart/chart.ts                                 |   59 
 src/views/pages/awesome/index.vue                        |   87 
 src/layout/logo/index.vue                                |   85 
 src/views/system/user/component/addUser.vue              |  200 
 tsconfig.json                                            |   72 
 src/layout/navBars/breadcrumb/userNews.vue               |  115 
 src/theme/media/date.scss                                |   25 
 src/theme/media/tagsView.scss                            |   11 
 src/views/pages/iocnfont/index.vue                       |   87 
 src/utils/getStyleSheets.ts                              |  101 
 src/layout/main/classic.vue                              |   35 
 src/views/pages/filtering/mock.ts                        |  201 
 src/utils/setIconfont.ts                                 |   48 
 src/stores/index.ts                                      |    8 
 src/views/visualizing/images/world.jpg                   |    0 
 src/views/system/dic/component/addDic.vue                |  129 
 src/views/pages/formI18n/index.vue                       |   59 
 src/views/pages/workflow/index.vue                       |  693 +
 src/views/system/user/index.vue                          |  177 
 src/views/pages/preview/index.vue                        |   28 
 src/utils/other.ts                                       |  200 
 package.json                                             |   81 
 src/App.vue                                              |  100 
 src/layout/navBars/breadcrumb/closeFull.vue              |   61 
 .prettierrc.js                                           |   39 
 src/layout/component/aside.vue                           |  163 
 src/stores/keepAliveNames.ts                             |   37 
 src/views/visualizing/mock/demo2.ts                      |  131 
 src/layout/main/columns.vue                              |   41 
 src/theme/media/login.scss                               |   63 
 src/views/params/dynamic/details.vue                     |   52 
 src/utils/toolsValidate.ts                               |  370 
 src/utils/authFunction.ts                                |   38 
 src/theme/media/chart.scss                               |   94 
 src/router/route.ts                                      |  102 
 src/views/make/noticeBar/index.vue                       |  164 
 src/views/pages/workflow/component/tool/help.vue         |   39 
 src/views/pages/drag/index.vue                           |   66 
 src/layout/navBars/breadcrumb/user.vue                   |  298 
 src/views/pages/formRules/index.vue                      |   80 
 src/i18n/pages/formI18n/zh-tw.ts                         |   13 
 src/theme/element.scss                                   |  282 
 src/theme/waves.scss                                     |  101 
 src/theme/iconSelector.scss                              |   70 
 src/i18n/pages/formI18n/zh-cn.ts                         |   13 
 src/views/home/index.vue                                 |  185 
 src/stores/interface/index.ts                            |   91 
 src/views/params/common/index.vue                        |  100 
 src/components/editor/toolbar.ts                         |   60 
 src/i18n/lang/en.ts                                      |  180 
 src/theme/dark.scss                                      |  236 
 src/views/chart/head.vue                                 |  107 
 src/stores/routesList.ts                                 |   27 
 src/views/pages/formAdapt/index.vue                      |  114 
 src/views/system/menu/index.vue                          |  128 
 src/views/pages/filtering/index.vue                      |  355 
 src/layout/component/main.vue                            |  101 
 plugins.d.ts                                             |    4 
 src/views/menu/menu1/menu13/index.vue                    |   27 
 src/layout/routerView/iframes.vue                        |   66 
 src/utils/arrayOperation.ts                              |   66 
 src/theme/media/layout.scss                              |   55 
 src/views/pages/steps/index.vue                          |   51 
 src/theme/media/form.scss                                |   16 
 src/utils/storage.ts                                     |   59 
 src/i18n/index.ts                                        |   67 
 src/views/pages/listAdapt/index.vue                      |  209 
 src/components/auth/auths.vue                            |   36 
 src/layout/navBars/tagsView/tagsView.vue                 |  734 +
 src/layout/footer/index.vue                              |   47 
 src/theme/common/transition.scss                         |   94 
 src/views/system/role/component/addRole.vue              |  240 
 .eslintignore                                            |   18 
 src/views/system/department/component/editDept.vue       |  179 
 src/views/make/svgDemo/index.vue                         |   59 
 shim.d.ts                                                |   13 
 src/api/role/index.ts                                    |   12 
 src/stores/themeConfig.ts                                |  146 
 src/assets/login-icon-two.svg                            |    1 
 src/theme/media/index.scss                               |   15 
 src/views/pages/waves/index.vue                          |  134 
 src/components/iconSelector/index.vue                    |  252 
 src/layout/navBars/breadcrumb/index.vue                  |  119 
 src/views/pages/lazyImg/index.vue                        |  194 
 src/utils/wartermark.ts                                  |   47 
 src/theme/media/scrollbar.scss                           |   56 
 public/favicon.ico                                       |    0 
 src/router/index.ts                                      |  110 
 src/assets/logo-mini.svg                                 |    9 
 src/theme/media/pagination.scss                          |   15 
 src/views/menu/menu1/menu11/index.vue                    |   21 
 src/layout/main/defaults.vue                             |   47 
 src/utils/request.ts                                     |   72 
 src/views/system/department/component/deptDialog.vue     |  161 
 src/theme/other.scss                                     |   36 
 src/views/params/common/details.vue                      |   52 
 src/layout/component/header.vue                          |   34 
 src/components/svgIcon/index.vue                         |   73 
 .env.production                                          |    5 
 src/views/chart/chart.scss                               |  434 
 src/layout/navBars/breadcrumb/breadcrumb.vue             |  163 
 src/layout/navMenu/horizontal.vue                        |  157 
 src/i18n/lang/zh-tw.ts                                   |  180 
 src/utils/loading.ts                                     |   42 
 src/utils/directive.ts                                   |   18 
 src/views/personal/mock.ts                               |   66 
 src/layout/routerView/link.vue                           |   61 
 src/i18n/lang/zh-cn.ts                                   |  180 
 src/stores/userInfo.ts                                   |   65 
 src/layout/component/columnsAside.vue                    |  292 
 src/layout/navBars/tagsView/contextmenu.vue              |  138 
 src/views/pages/formRules/component/formRulesTwo.vue     |   52 
 src/views/personal/index.vue                             |  387 
 src/views/visualizing/mock/demo1.ts                      |   51 
 .env                                                     |    8 
 src/components/noticeBar/index.vue                       |  195 
 src/stores/requestOldRoutes.ts                           |   17 
 src/theme/app.scss                                       |  281 
 src/views/pages/workflow/js/config.ts                    |   99 
 src/views/pages/tree/index.vue                           |  258 
 src/views/pages/workflow/component/contextmenu/index.vue |  107 
 src/views/pages/workflow/component/drawer/line.vue       |   62 
 206 files changed, 34,608 insertions(+), 0 deletions(-)

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
diff --git a/.env.development b/.env.development
new file mode 100644
index 0000000..0c99294
--- /dev/null
+++ b/.env.development
@@ -0,0 +1,7 @@
+# 本地环境
+ENV = 'development'
+
+# 本地环境接口地址
+VITE_API_URL = 'http://192.168.0.35:8008'
+
+# VITE_API_URL = 'http://192.168.0.8:8008'
diff --git a/.env.production b/.env.production
new file mode 100644
index 0000000..0336f50
--- /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
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
diff --git a/.eslintrc.js b/.eslintrc.js
new file mode 100644
index 0000000..732bfbe
--- /dev/null
+++ b/.eslintrc.js
@@ -0,0 +1,63 @@
+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'],
+	rules: {
+		// http://eslint.cn/docs/rules/
+		// https://eslint.vuejs.org/rules/
+		'@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',
+		'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',
+		'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',
+	},
+};
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',
+};
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..34eedbe
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,356 @@
+# <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.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` 左侧菜单数据不变问题
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..6f6a7ea
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+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
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+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.
\ No newline at end of file
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..a550d07
--- /dev/null
+++ b/index.html
@@ -0,0 +1,31 @@
+<!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 的后台开源免费管理系统模板!"
+		/>
+		<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>
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..1c0d853
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,7716 @@
+{
+	"name": "vue-next-admin",
+	"version": "2.1.1",
+	"lockfileVersion": 2,
+	"requires": true,
+	"packages": {
+		"": {
+			"name": "vue-next-admin",
+			"version": "2.1.1",
+			"license": "MIT",
+			"dependencies": {
+				"@element-plus/icons-vue": "^2.0.3",
+				"@wangeditor/editor": "^5.1.1",
+				"axios": "^0.27.2",
+				"countup.js": "^2.2.0",
+				"cropperjs": "^1.5.12",
+				"echarts": "^5.3.2",
+				"echarts-gl": "^2.0.9",
+				"echarts-wordcloud": "^2.0.0",
+				"element-plus": "^2.2.2",
+				"js-cookie": "^3.0.1",
+				"jsplumb": "^2.15.6",
+				"mitt": "^3.0.0",
+				"nprogress": "^0.2.0",
+				"pinia": "^2.0.14",
+				"print-js": "^1.6.0",
+				"qrcodejs2-fixes": "^0.0.2",
+				"screenfull": "^6.0.1",
+				"sortablejs": "^1.15.0",
+				"splitpanes": "^3.1.1",
+				"vue": "^3.2.36",
+				"vue-clipboard3": "^2.0.0",
+				"vue-grid-layout": "^3.0.0-beta1",
+				"vue-i18n": "^9.1.10",
+				"vue-router": "^4.0.15"
+			},
+			"devDependencies": {
+				"@types/node": "^17.0.39",
+				"@types/nprogress": "^0.2.0",
+				"@types/sortablejs": "^1.13.0",
+				"@typescript-eslint/eslint-plugin": "^5.27.0",
+				"@typescript-eslint/parser": "^5.27.0",
+				"@vitejs/plugin-vue": "^2.3.3",
+				"@vue/compiler-sfc": "^3.2.36",
+				"dotenv": "^16.0.1",
+				"eslint": "^8.17.0",
+				"eslint-plugin-vue": "^9.1.0",
+				"prettier": "^2.6.2",
+				"sass": "^1.52.2",
+				"sass-loader": "^13.0.0",
+				"typescript": "^4.7.3",
+				"vite": "^2.9.9",
+				"vue-eslint-parser": "^9.0.2"
+			},
+			"engines": {
+				"node": ">=12.0.0",
+				"npm": ">= 6.0.0"
+			}
+		},
+		"node_modules/@babel/parser": {
+			"version": "7.18.0",
+			"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.0.tgz",
+			"integrity": "sha512-AqDccGC+m5O/iUStSJy3DGRIUFu7WbY/CppZYwrEUB4N0tZlnI8CSTsgL7v5fHVFmUbRv2sd+yy27o8Ydt4MGg==",
+			"bin": {
+				"parser": "bin/babel-parser.js"
+			},
+			"engines": {
+				"node": ">=6.0.0"
+			}
+		},
+		"node_modules/@babel/runtime": {
+			"version": "7.18.0",
+			"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.0.tgz",
+			"integrity": "sha512-YMQvx/6nKEaucl0MY56mwIG483xk8SDNdlUwb2Ts6FUpr7fm85DxEmsY18LXBNhcTz6tO6JwZV8w1W06v8UKeg==",
+			"dependencies": {
+				"regenerator-runtime": "^0.13.4"
+			},
+			"engines": {
+				"node": ">=6.9.0"
+			}
+		},
+		"node_modules/@ctrl/tinycolor": {
+			"version": "3.4.1",
+			"resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.4.1.tgz",
+			"integrity": "sha512-ej5oVy6lykXsvieQtqZxCOaLT+xD4+QNarq78cIYISHmZXshCvROLudpQN3lfL8G0NL7plMSSK+zlyvCaIJ4Iw==",
+			"engines": {
+				"node": ">=10"
+			}
+		},
+		"node_modules/@element-plus/icons-vue": {
+			"version": "2.0.3",
+			"resolved": "https://registry.npmjs.org/@element-plus/icons-vue/-/icons-vue-2.0.3.tgz",
+			"integrity": "sha512-dI9hazWIJF5AXsFDWLsdGqVIQMJ5Kq70fw1RScuMW6+UNqfJpRYFOqhya8RHpjajIZZnQx260Ll9AjTcu2HSOA==",
+			"peerDependencies": {
+				"vue": "^3.2.0"
+			}
+		},
+		"node_modules/@eslint/eslintrc": {
+			"version": "1.3.0",
+			"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz",
+			"integrity": "sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==",
+			"dev": true,
+			"dependencies": {
+				"ajv": "^6.12.4",
+				"debug": "^4.3.2",
+				"espree": "^9.3.2",
+				"globals": "^13.15.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"
+			}
+		},
+		"node_modules/@floating-ui/core": {
+			"version": "0.7.1",
+			"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-0.7.1.tgz",
+			"integrity": "sha512-grcqEmI8DTIolufpxhJagVeJmvloxBXE6xxSrVnSXz/Wz1uUIsC85ad+UNBqAoBOvzLxE42wvDj3YkmSGqWRxA=="
+		},
+		"node_modules/@floating-ui/dom": {
+			"version": "0.5.1",
+			"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-0.5.1.tgz",
+			"integrity": "sha512-dkPSy5JPiQEtljc3VpG9lauYctxfLlqj/8N9f+lmsR92gQaSVMAWuBbFBH2keY5DmdQn3p4Dv1dQd+e8osH+/g==",
+			"dependencies": {
+				"@floating-ui/core": "^0.7.1"
+			}
+		},
+		"node_modules/@humanwhocodes/config-array": {
+			"version": "0.9.5",
+			"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz",
+			"integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==",
+			"dev": true,
+			"dependencies": {
+				"@humanwhocodes/object-schema": "^1.2.1",
+				"debug": "^4.1.1",
+				"minimatch": "^3.0.4"
+			},
+			"engines": {
+				"node": ">=10.10.0"
+			}
+		},
+		"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/@interactjs/actions": {
+			"version": "1.10.11",
+			"resolved": "https://registry.npmjs.org/@interactjs/actions/-/actions-1.10.11.tgz",
+			"integrity": "sha512-P39zeefr4hkmKx+5nZ+mrH1s0l2YJ3gIHrthXmE81n6MlMa42m0WtHcTms4C5JTTNBP2EEDY+KGgGxSnmJKvUw==",
+			"optionalDependencies": {
+				"@interactjs/interact": "1.10.11"
+			},
+			"peerDependencies": {
+				"@interactjs/core": "1.10.11",
+				"@interactjs/utils": "1.10.11"
+			}
+		},
+		"node_modules/@interactjs/auto-scroll": {
+			"version": "1.10.11",
+			"resolved": "https://registry.npmjs.org/@interactjs/auto-scroll/-/auto-scroll-1.10.11.tgz",
+			"integrity": "sha512-feHNjhi0EMNLV2nQcEgjYPz2mI54aeSW2RiaoNtFLyBvtXKp0b4DmluwDv6DvuXmUpDwD5g/Hk1gGM2rgl7iqQ==",
+			"optionalDependencies": {
+				"@interactjs/interact": "1.10.11"
+			},
+			"peerDependencies": {
+				"@interactjs/utils": "1.10.11"
+			}
+		},
+		"node_modules/@interactjs/auto-start": {
+			"version": "1.10.11",
+			"resolved": "https://registry.npmjs.org/@interactjs/auto-start/-/auto-start-1.10.11.tgz",
+			"integrity": "sha512-cIg5CcalCPtC6AiGq6j/0hKUtL2MweEpvw12FuB19sz2Q9Dye0J4GliHKhOYvtumNinnvfVAZ4FZMqZEuX7YZA==",
+			"optionalDependencies": {
+				"@interactjs/interact": "1.10.11"
+			},
+			"peerDependencies": {
+				"@interactjs/core": "1.10.11",
+				"@interactjs/utils": "1.10.11"
+			}
+		},
+		"node_modules/@interactjs/core": {
+			"version": "1.10.11",
+			"resolved": "https://registry.npmjs.org/@interactjs/core/-/core-1.10.11.tgz",
+			"integrity": "sha512-aJ50ccVeszpJt7wPH7Yfqm7f1aG1SA94qd90P0NaESh5/QUXn4CESO6igobo4DFHQ5z+1Rfdl8aphP4JxlH4gw==",
+			"peerDependencies": {
+				"@interactjs/utils": "1.10.11"
+			}
+		},
+		"node_modules/@interactjs/dev-tools": {
+			"version": "1.10.11",
+			"resolved": "https://registry.npmjs.org/@interactjs/dev-tools/-/dev-tools-1.10.11.tgz",
+			"integrity": "sha512-BP2FNfMbF7zLuOAUGMkDhCo1e1B0fnqyb9ih/Y8yAIJuoLrZxP/9htbsS1vZOIVZ4UgtrId4cYOwfcAZBMQtmw==",
+			"optionalDependencies": {
+				"@interactjs/interact": "1.10.11"
+			},
+			"peerDependencies": {
+				"@interactjs/modifiers": "1.10.11",
+				"@interactjs/utils": "1.10.11"
+			}
+		},
+		"node_modules/@interactjs/inertia": {
+			"version": "1.10.11",
+			"resolved": "https://registry.npmjs.org/@interactjs/inertia/-/inertia-1.10.11.tgz",
+			"integrity": "sha512-h+sknCzRqBSyHy4ctPNsq56mxkAMMdwHWD6en7rDEw899gdGKYaXVDVdv1jMfiwNRw0eRFBNoCiol8r3a/a3Jw==",
+			"dependencies": {
+				"@interactjs/offset": "1.10.11"
+			},
+			"optionalDependencies": {
+				"@interactjs/interact": "1.10.11"
+			},
+			"peerDependencies": {
+				"@interactjs/core": "1.10.11",
+				"@interactjs/modifiers": "1.10.11",
+				"@interactjs/utils": "1.10.11"
+			}
+		},
+		"node_modules/@interactjs/interact": {
+			"version": "1.10.11",
+			"resolved": "https://registry.npmjs.org/@interactjs/interact/-/interact-1.10.11.tgz",
+			"integrity": "sha512-0iZJ9l547JuBA/lKxK4ARGYVmMqRSsAdA8gXL1zWe51qEIQq8PyWmMipoi8JbDaL7exC2THKwkXu5uq5ndT+iA==",
+			"dependencies": {
+				"@interactjs/core": "1.10.11",
+				"@interactjs/types": "1.10.11",
+				"@interactjs/utils": "1.10.11"
+			}
+		},
+		"node_modules/@interactjs/interactjs": {
+			"version": "1.10.11",
+			"resolved": "https://registry.npmjs.org/@interactjs/interactjs/-/interactjs-1.10.11.tgz",
+			"integrity": "sha512-cGOxf6rp3Y8/sk88LhIT0XDn4gCiCzAnUG5Kkj9SAqiUO6BK/9+Wbp1IBkNaPgl/8uG8gNHh/dXBrlBBNcqJAg==",
+			"dependencies": {
+				"@interactjs/actions": "1.10.11",
+				"@interactjs/auto-scroll": "1.10.11",
+				"@interactjs/auto-start": "1.10.11",
+				"@interactjs/core": "1.10.11",
+				"@interactjs/dev-tools": "1.10.11",
+				"@interactjs/inertia": "1.10.11",
+				"@interactjs/interact": "1.10.11",
+				"@interactjs/modifiers": "1.10.11",
+				"@interactjs/offset": "1.10.11",
+				"@interactjs/pointer-events": "1.10.11",
+				"@interactjs/reflow": "1.10.11",
+				"@interactjs/utils": "1.10.11"
+			}
+		},
+		"node_modules/@interactjs/modifiers": {
+			"version": "1.10.11",
+			"resolved": "https://registry.npmjs.org/@interactjs/modifiers/-/modifiers-1.10.11.tgz",
+			"integrity": "sha512-ltqX1RSqeAIikixlQBlyEUdclT5+rbfIGi3sIdLLYaIZQnltYkWqL9MHKx/w5b+hV+Mc0p5MLUFWJbTdkSCZ9g==",
+			"dependencies": {
+				"@interactjs/snappers": "1.10.11"
+			},
+			"optionalDependencies": {
+				"@interactjs/interact": "1.10.11"
+			},
+			"peerDependencies": {
+				"@interactjs/core": "1.10.11",
+				"@interactjs/utils": "1.10.11"
+			}
+		},
+		"node_modules/@interactjs/offset": {
+			"version": "1.10.11",
+			"resolved": "https://registry.npmjs.org/@interactjs/offset/-/offset-1.10.11.tgz",
+			"integrity": "sha512-mBT7eIfy5ivofECiv+VwtEwwIMLV54fT9ujSMWJPduxdSYIHepUWgEf/3zjJknFh6jQc7pqz9dtjvVvyzRCLlQ==",
+			"optionalDependencies": {
+				"@interactjs/interact": "1.10.11"
+			},
+			"peerDependencies": {
+				"@interactjs/core": "1.10.11",
+				"@interactjs/utils": "1.10.11"
+			}
+		},
+		"node_modules/@interactjs/pointer-events": {
+			"version": "1.10.11",
+			"resolved": "https://registry.npmjs.org/@interactjs/pointer-events/-/pointer-events-1.10.11.tgz",
+			"integrity": "sha512-yBT8JJVMZ+MgBay5l1WAHnL8ch/mZsRfaFahti+QFYeQyRloDtsWmEMDSYI/Onyy9+hS3gN/ge77ArGciZZ0Ow==",
+			"optionalDependencies": {
+				"@interactjs/interact": "1.10.11"
+			},
+			"peerDependencies": {
+				"@interactjs/core": "1.10.11",
+				"@interactjs/utils": "1.10.11"
+			}
+		},
+		"node_modules/@interactjs/reflow": {
+			"version": "1.10.11",
+			"resolved": "https://registry.npmjs.org/@interactjs/reflow/-/reflow-1.10.11.tgz",
+			"integrity": "sha512-NSCtcCkjImOYSbxzzv2kFqR9t49J8KlhEr9UoePc7GyLbNXsiv3WQ3n0ehZd7CgZXQDiVXnP2UnmIOv5Zd4HQg==",
+			"optionalDependencies": {
+				"@interactjs/interact": "1.10.11"
+			},
+			"peerDependencies": {
+				"@interactjs/core": "1.10.11",
+				"@interactjs/utils": "1.10.11"
+			}
+		},
+		"node_modules/@interactjs/snappers": {
+			"version": "1.10.11",
+			"resolved": "https://registry.npmjs.org/@interactjs/snappers/-/snappers-1.10.11.tgz",
+			"integrity": "sha512-yYtOMUZ7aFUZ1IYheq9Tj5hZ4J1r5dnaXhLF44WsI/awQ5L0DjZf07GPWof0B+7rZHEVudxyQNbPfFmb+1K94Q==",
+			"optionalDependencies": {
+				"@interactjs/interact": "1.10.11"
+			},
+			"peerDependencies": {
+				"@interactjs/utils": "1.10.11"
+			}
+		},
+		"node_modules/@interactjs/types": {
+			"version": "1.10.11",
+			"resolved": "https://registry.npmjs.org/@interactjs/types/-/types-1.10.11.tgz",
+			"integrity": "sha512-YRsVFWjL8Gkkvlx3qnjeaxW4fnibSJ9791g8BA7Pv5ANByI64WmtR1vU7A2rXcrOn8XvyCEfY0ss1s8NhZP+MA=="
+		},
+		"node_modules/@interactjs/utils": {
+			"version": "1.10.11",
+			"resolved": "https://registry.npmjs.org/@interactjs/utils/-/utils-1.10.11.tgz",
+			"integrity": "sha512-410ZoxKF+r1roeSelL+WHXfdryUMg5iykC1XwQ3l6XqNw43IMACzyvTH6k6Pwxj7w7x42nce0Qdn1GQ3Y8xyCw=="
+		},
+		"node_modules/@intlify/core-base": {
+			"version": "9.1.10",
+			"resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.1.10.tgz",
+			"integrity": "sha512-So9CNUavB/IsZ+zBmk2Cv6McQp6vc2wbGi1S0XQmJ8Vz+UFcNn9MFXAe9gY67PreIHrbLsLxDD0cwo1qsxM1Nw==",
+			"dependencies": {
+				"@intlify/devtools-if": "9.1.10",
+				"@intlify/message-compiler": "9.1.10",
+				"@intlify/message-resolver": "9.1.10",
+				"@intlify/runtime": "9.1.10",
+				"@intlify/shared": "9.1.10",
+				"@intlify/vue-devtools": "9.1.10"
+			},
+			"engines": {
+				"node": ">= 10"
+			}
+		},
+		"node_modules/@intlify/devtools-if": {
+			"version": "9.1.10",
+			"resolved": "https://registry.npmjs.org/@intlify/devtools-if/-/devtools-if-9.1.10.tgz",
+			"integrity": "sha512-SHaKoYu6sog3+Q8js1y3oXLywuogbH1sKuc7NSYkN3GElvXSBaMoCzW+we0ZSFqj/6c7vTNLg9nQ6rxhKqYwnQ==",
+			"dependencies": {
+				"@intlify/shared": "9.1.10"
+			},
+			"engines": {
+				"node": ">= 10"
+			}
+		},
+		"node_modules/@intlify/message-compiler": {
+			"version": "9.1.10",
+			"resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.1.10.tgz",
+			"integrity": "sha512-+JiJpXff/XTb0EadYwdxOyRTB0hXNd4n1HaJ/a4yuV960uRmPXaklJsedW0LNdcptd/hYUZtCkI7Lc9J5C1gxg==",
+			"dependencies": {
+				"@intlify/message-resolver": "9.1.10",
+				"@intlify/shared": "9.1.10",
+				"source-map": "0.6.1"
+			},
+			"engines": {
+				"node": ">= 10"
+			}
+		},
+		"node_modules/@intlify/message-resolver": {
+			"version": "9.1.10",
+			"resolved": "https://registry.npmjs.org/@intlify/message-resolver/-/message-resolver-9.1.10.tgz",
+			"integrity": "sha512-5YixMG/M05m0cn9+gOzd4EZQTFRUu8RGhzxJbR1DWN21x/Z3bJ8QpDYj6hC4FwBj5uKsRfKpJQ3Xqg98KWoA+w==",
+			"engines": {
+				"node": ">= 10"
+			}
+		},
+		"node_modules/@intlify/runtime": {
+			"version": "9.1.10",
+			"resolved": "https://registry.npmjs.org/@intlify/runtime/-/runtime-9.1.10.tgz",
+			"integrity": "sha512-7QsuByNzpe3Gfmhwq6hzgXcMPpxz8Zxb/XFI6s9lQdPLPe5Lgw4U1ovRPZTOs6Y2hwitR3j/HD8BJNGWpJnOFA==",
+			"dependencies": {
+				"@intlify/message-compiler": "9.1.10",
+				"@intlify/message-resolver": "9.1.10",
+				"@intlify/shared": "9.1.10"
+			},
+			"engines": {
+				"node": ">= 10"
+			}
+		},
+		"node_modules/@intlify/shared": {
+			"version": "9.1.10",
+			"resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.1.10.tgz",
+			"integrity": "sha512-Om54xJeo1Vw+K1+wHYyXngE8cAbrxZHpWjYzMR9wCkqbhGtRV5VLhVc214Ze2YatPrWlS2WSMOWXR8JktX/IgA==",
+			"engines": {
+				"node": ">= 10"
+			}
+		},
+		"node_modules/@intlify/vue-devtools": {
+			"version": "9.1.10",
+			"resolved": "https://registry.npmjs.org/@intlify/vue-devtools/-/vue-devtools-9.1.10.tgz",
+			"integrity": "sha512-5l3qYARVbkWAkagLu1XbDUWRJSL8br1Dj60wgMaKB0+HswVsrR6LloYZTg7ozyvM621V6+zsmwzbQxbVQyrytQ==",
+			"dependencies": {
+				"@intlify/message-resolver": "9.1.10",
+				"@intlify/runtime": "9.1.10",
+				"@intlify/shared": "9.1.10"
+			},
+			"engines": {
+				"node": ">= 10"
+			}
+		},
+		"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/eslint": {
+			"version": "8.4.2",
+			"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.2.tgz",
+			"integrity": "sha512-Z1nseZON+GEnFjJc04sv4NSALGjhFwy6K0HXt7qsn5ArfAKtb63dXNJHf+1YW6IpOIYRBGUbu3GwJdj8DGnCjA==",
+			"dev": true,
+			"peer": true,
+			"dependencies": {
+				"@types/estree": "*",
+				"@types/json-schema": "*"
+			}
+		},
+		"node_modules/@types/eslint-scope": {
+			"version": "3.7.3",
+			"resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.3.tgz",
+			"integrity": "sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g==",
+			"dev": true,
+			"peer": true,
+			"dependencies": {
+				"@types/eslint": "*",
+				"@types/estree": "*"
+			}
+		},
+		"node_modules/@types/estree": {
+			"version": "0.0.51",
+			"resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz",
+			"integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==",
+			"dev": true,
+			"peer": true
+		},
+		"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.182",
+			"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.182.tgz",
+			"integrity": "sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q=="
+		},
+		"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": "17.0.39",
+			"resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.39.tgz",
+			"integrity": "sha512-JDU3YLlnPK3WDao6/DlXLOgSNpG13ct+CwIO17V8q0/9fWJyeMJJ/VyZ1lv8kDprihvZMydzVwf0tQOqGiY2Nw==",
+			"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/sortablejs": {
+			"version": "1.13.0",
+			"resolved": "https://registry.npmjs.org/@types/sortablejs/-/sortablejs-1.13.0.tgz",
+			"integrity": "sha512-C3064MH72iEfeGCYEGCt7FCxXoAXaMPG0QPnstcxvPmbl54erpISu06d++FY37Smja64iWy5L8wOyHHBghWbJQ==",
+			"dev": true
+		},
+		"node_modules/@typescript-eslint/eslint-plugin": {
+			"version": "5.27.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.27.0.tgz",
+			"integrity": "sha512-DDrIA7GXtmHXr1VCcx9HivA39eprYBIFxbQEHI6NyraRDxCGpxAFiYQAT/1Y0vh1C+o2vfBiy4IuPoXxtTZCAQ==",
+			"dev": true,
+			"dependencies": {
+				"@typescript-eslint/scope-manager": "5.27.0",
+				"@typescript-eslint/type-utils": "5.27.0",
+				"@typescript-eslint/utils": "5.27.0",
+				"debug": "^4.3.4",
+				"functional-red-black-tree": "^1.0.1",
+				"ignore": "^5.2.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.27.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.27.0.tgz",
+			"integrity": "sha512-8oGjQF46c52l7fMiPPvX4It3u3V3JipssqDfHQ2hcR0AeR8Zge+OYyKUCm5b70X72N1qXt0qgHenwN6Gc2SXZA==",
+			"dev": true,
+			"dependencies": {
+				"@typescript-eslint/scope-manager": "5.27.0",
+				"@typescript-eslint/types": "5.27.0",
+				"@typescript-eslint/typescript-estree": "5.27.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.27.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.27.0.tgz",
+			"integrity": "sha512-VnykheBQ/sHd1Vt0LJ1JLrMH1GzHO+SzX6VTXuStISIsvRiurue/eRkTqSrG0CexHQgKG8shyJfR4o5VYioB9g==",
+			"dev": true,
+			"dependencies": {
+				"@typescript-eslint/types": "5.27.0",
+				"@typescript-eslint/visitor-keys": "5.27.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.27.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.27.0.tgz",
+			"integrity": "sha512-vpTvRRchaf628Hb/Xzfek+85o//zEUotr1SmexKvTfs7czXfYjXVT/a5yDbpzLBX1rhbqxjDdr1Gyo0x1Fc64g==",
+			"dev": true,
+			"dependencies": {
+				"@typescript-eslint/utils": "5.27.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.27.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.27.0.tgz",
+			"integrity": "sha512-lY6C7oGm9a/GWhmUDOs3xAVRz4ty/XKlQ2fOLr8GAIryGn0+UBOoJDWyHer3UgrHkenorwvBnphhP+zPmzmw0A==",
+			"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.27.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.27.0.tgz",
+			"integrity": "sha512-QywPMFvgZ+MHSLRofLI7BDL+UczFFHyj0vF5ibeChDAJgdTV8k4xgEwF0geFhVlPc1p8r70eYewzpo6ps+9LJQ==",
+			"dev": true,
+			"dependencies": {
+				"@typescript-eslint/types": "5.27.0",
+				"@typescript-eslint/visitor-keys": "5.27.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.27.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.27.0.tgz",
+			"integrity": "sha512-nZvCrkIJppym7cIbP3pOwIkAefXOmfGPnCM0LQfzNaKxJHI6VjI8NC662uoiPlaf5f6ymkTy9C3NQXev2mdXmA==",
+			"dev": true,
+			"dependencies": {
+				"@types/json-schema": "^7.0.9",
+				"@typescript-eslint/scope-manager": "5.27.0",
+				"@typescript-eslint/types": "5.27.0",
+				"@typescript-eslint/typescript-estree": "5.27.0",
+				"eslint-scope": "^5.1.1",
+				"eslint-utils": "^3.0.0"
+			},
+			"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.27.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.27.0.tgz",
+			"integrity": "sha512-46cYrteA2MrIAjv9ai44OQDUoCZyHeGIc4lsjCUX2WT6r4C+kidz1bNiR4017wHOPUythYeH+Sc7/cFP97KEAA==",
+			"dev": true,
+			"dependencies": {
+				"@typescript-eslint/types": "5.27.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.1.0",
+			"resolved": "https://registry.npmjs.org/@uppy/companion-client/-/companion-client-2.1.0.tgz",
+			"integrity": "sha512-1Zsag2z9kygXuVbUxqHhkTJUmEHwbejjyf7vmm+P/AiVgK3O37JINYBGOpdTJNgbC9UydLBjleXo8peDVgpg8Q==",
+			"dependencies": {
+				"@uppy/utils": "^4.0.7",
+				"namespace-emitter": "^2.0.1"
+			}
+		},
+		"node_modules/@uppy/core": {
+			"version": "2.2.0",
+			"resolved": "https://registry.npmjs.org/@uppy/core/-/core-2.2.0.tgz",
+			"integrity": "sha512-qdDoNCjrVjjOmFCFCxc+HEbtbQ9K0k6LKNbZZwWK7d4Cx3xEa6VsxmqVhfFL6ekH2gyboqYV8Z5IbRkJT/0Nqg==",
+			"dependencies": {
+				"@transloadit/prettier-bytes": "0.0.7",
+				"@uppy/store-default": "^2.0.3",
+				"@uppy/utils": "^4.0.7",
+				"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.0.3",
+			"resolved": "https://registry.npmjs.org/@uppy/store-default/-/store-default-2.0.3.tgz",
+			"integrity": "sha512-2BGlN1sW0cFv4rOqTK8dfSg579S984N1HxCJxLFqeW9nWD6zd/O8Omyd85tbxGQ+FLZLTmLOm/feD0YeCBMahg=="
+		},
+		"node_modules/@uppy/utils": {
+			"version": "4.0.7",
+			"resolved": "https://registry.npmjs.org/@uppy/utils/-/utils-4.0.7.tgz",
+			"integrity": "sha512-nKViMT8XchKy+NWpb3DtVKuzZBmW7au26LrMq89EsvTwIOT6UR9+7bmz/+zr3+lc7UC7vMgNChIC6G+/Ya9wWQ==",
+			"dependencies": {
+				"lodash.throttle": "^4.1.1"
+			}
+		},
+		"node_modules/@uppy/xhr-upload": {
+			"version": "2.1.0",
+			"resolved": "https://registry.npmjs.org/@uppy/xhr-upload/-/xhr-upload-2.1.0.tgz",
+			"integrity": "sha512-io1uNu7lGkhIkMnt13bu3FYSAdRbBRWl8n/6njYi+727Jyr0XhKfmBYV9OiruFSxLz5Bfxkw2gTs6e0qUb63nA==",
+			"dependencies": {
+				"@uppy/companion-client": "^2.1.0",
+				"@uppy/utils": "^4.0.7",
+				"nanoid": "^3.1.25"
+			},
+			"peerDependencies": {
+				"@uppy/core": "^2.2.0"
+			}
+		},
+		"node_modules/@vitejs/plugin-vue": {
+			"version": "2.3.3",
+			"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-2.3.3.tgz",
+			"integrity": "sha512-SmQLDyhz+6lGJhPELsBdzXGc+AcaT8stgkbiTFGpXPe8Tl1tJaBw1A6pxDqDuRsVkD8uscrkx3hA7QDOoKYtyw==",
+			"dev": true,
+			"engines": {
+				"node": ">=12.0.0"
+			},
+			"peerDependencies": {
+				"vite": "^2.5.10",
+				"vue": "^3.2.25"
+			}
+		},
+		"node_modules/@vue/compiler-core": {
+			"version": "3.2.36",
+			"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.36.tgz",
+			"integrity": "sha512-bbyZM5hvBicv0PW3KUfVi+x3ylHnfKG7DOn5wM+f2OztTzTjLEyBb/5yrarIYpmnGitVGbjZqDbODyW4iK8hqw==",
+			"dependencies": {
+				"@babel/parser": "^7.16.4",
+				"@vue/shared": "3.2.36",
+				"estree-walker": "^2.0.2",
+				"source-map": "^0.6.1"
+			}
+		},
+		"node_modules/@vue/compiler-dom": {
+			"version": "3.2.36",
+			"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.36.tgz",
+			"integrity": "sha512-tcOTAOiW4s24QLnq+ON6J+GRONXJ+A/mqKCORi0LSlIh8XQlNnlm24y8xIL8la+ZDgkdbjarQ9ZqYSvEja6gVA==",
+			"dependencies": {
+				"@vue/compiler-core": "3.2.36",
+				"@vue/shared": "3.2.36"
+			}
+		},
+		"node_modules/@vue/compiler-sfc": {
+			"version": "3.2.36",
+			"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.36.tgz",
+			"integrity": "sha512-AvGb4bTj4W8uQ4BqaSxo7UwTEqX5utdRSMyHy58OragWlt8nEACQ9mIeQh3K4di4/SX+41+pJrLIY01lHAOFOA==",
+			"dependencies": {
+				"@babel/parser": "^7.16.4",
+				"@vue/compiler-core": "3.2.36",
+				"@vue/compiler-dom": "3.2.36",
+				"@vue/compiler-ssr": "3.2.36",
+				"@vue/reactivity-transform": "3.2.36",
+				"@vue/shared": "3.2.36",
+				"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.36",
+			"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.36.tgz",
+			"integrity": "sha512-+KugInUFRvOxEdLkZwE+W43BqHyhBh0jpYXhmqw1xGq2dmE6J9eZ8UUSOKNhdHtQ/iNLWWeK/wPZkVLUf3YGaw==",
+			"dependencies": {
+				"@vue/compiler-dom": "3.2.36",
+				"@vue/shared": "3.2.36"
+			}
+		},
+		"node_modules/@vue/devtools-api": {
+			"version": "6.1.4",
+			"resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.1.4.tgz",
+			"integrity": "sha512-IiA0SvDrJEgXvVxjNkHPFfDx6SXw0b/TUkqMcDZWNg9fnCAHbTpoo59YfJ9QLFkwa3raau5vSlRVzMSLDnfdtQ=="
+		},
+		"node_modules/@vue/reactivity": {
+			"version": "3.2.36",
+			"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.36.tgz",
+			"integrity": "sha512-c2qvopo0crh9A4GXi2/2kfGYMxsJW4tVILrqRPydVGZHhq0fnzy6qmclWOhBFckEhmyxmpHpdJtIRYGeKcuhnA==",
+			"dependencies": {
+				"@vue/shared": "3.2.36"
+			}
+		},
+		"node_modules/@vue/reactivity-transform": {
+			"version": "3.2.36",
+			"resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.36.tgz",
+			"integrity": "sha512-Jk5o2BhpODC9XTA7o4EL8hSJ4JyrFWErLtClG3NH8wDS7ri9jBDWxI7/549T7JY9uilKsaNM+4pJASLj5dtRwA==",
+			"dependencies": {
+				"@babel/parser": "^7.16.4",
+				"@vue/compiler-core": "3.2.36",
+				"@vue/shared": "3.2.36",
+				"estree-walker": "^2.0.2",
+				"magic-string": "^0.25.7"
+			}
+		},
+		"node_modules/@vue/runtime-core": {
+			"version": "3.2.36",
+			"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.36.tgz",
+			"integrity": "sha512-PTWBD+Lub+1U3/KhbCExrfxyS14hstLX+cBboxVHaz+kXoiDLNDEYAovPtxeTutbqtClIXtft+wcGdC+FUQ9qQ==",
+			"dependencies": {
+				"@vue/reactivity": "3.2.36",
+				"@vue/shared": "3.2.36"
+			}
+		},
+		"node_modules/@vue/runtime-dom": {
+			"version": "3.2.36",
+			"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.36.tgz",
+			"integrity": "sha512-gYPYblm7QXHVuBohqNRRT7Wez0f2Mx2D40rb4fleehrJU9CnkjG0phhcGEZFfGwCmHZRqBCRgbFWE98bPULqkg==",
+			"dependencies": {
+				"@vue/runtime-core": "3.2.36",
+				"@vue/shared": "3.2.36",
+				"csstype": "^2.6.8"
+			}
+		},
+		"node_modules/@vue/server-renderer": {
+			"version": "3.2.36",
+			"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.36.tgz",
+			"integrity": "sha512-uZE0+jfye6yYXWvAQYeHZv+f50sRryvy16uiqzk3jn8hEY8zTjI+rzlmZSGoE915k+W/Ol9XSw6vxOUD8dGkUg==",
+			"dependencies": {
+				"@vue/compiler-ssr": "3.2.36",
+				"@vue/shared": "3.2.36"
+			},
+			"peerDependencies": {
+				"vue": "3.2.36"
+			}
+		},
+		"node_modules/@vue/shared": {
+			"version": "3.2.36",
+			"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.36.tgz",
+			"integrity": "sha512-JtB41wXl7Au3+Nl3gD16Cfpj7k/6aCroZ6BbOiCMFCMvrOpkg/qQUXTso2XowaNqBbnkuGHurLAqkLBxNGc1hQ=="
+		},
+		"node_modules/@vueuse/core": {
+			"version": "8.5.0",
+			"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-8.5.0.tgz",
+			"integrity": "sha512-VEJ6sGNsPlUp0o9BGda2YISvDZbhWJSOJu5zlp2TufRGVrLcYUKr31jyFEOj6RXzG3k/H4aCYeZyjpItfU8glw==",
+			"dependencies": {
+				"@vueuse/metadata": "8.5.0",
+				"@vueuse/shared": "8.5.0",
+				"vue-demi": "*"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/antfu"
+			},
+			"peerDependencies": {
+				"@vue/composition-api": "^1.1.0",
+				"vue": "^2.6.0 || ^3.2.0"
+			},
+			"peerDependenciesMeta": {
+				"@vue/composition-api": {
+					"optional": true
+				},
+				"vue": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/@vueuse/core/node_modules/@vueuse/shared": {
+			"version": "8.5.0",
+			"resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-8.5.0.tgz",
+			"integrity": "sha512-qKG+SZb44VvGD4dU5cQ63z4JE2Yk39hQUecR0a9sEdJA01cx+XrxAvFKJfPooxwoiqalAVw/ktWK6xbyc/jS3g==",
+			"dependencies": {
+				"vue-demi": "*"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/antfu"
+			},
+			"peerDependencies": {
+				"@vue/composition-api": "^1.1.0",
+				"vue": "^2.6.0 || ^3.2.0"
+			},
+			"peerDependenciesMeta": {
+				"@vue/composition-api": {
+					"optional": true
+				},
+				"vue": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/@vueuse/core/node_modules/vue-demi": {
+			"version": "0.12.5",
+			"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.12.5.tgz",
+			"integrity": "sha512-BREuTgTYlUr0zw0EZn3hnhC3I6gPWv+Kwh4MCih6QcAeaTlaIX0DwOVN0wHej7hSvDPecz4jygy/idsgKfW58Q==",
+			"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": "8.5.0",
+			"resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-8.5.0.tgz",
+			"integrity": "sha512-WxsD+Cd+bn+HcjpY6Dl9FJ8ywTRTT9pTwk3bCQpzEhXVYAyNczKDSahk50fCfIJKeWHhyI4B2+/ZEOxQAkUr0g==",
+			"funding": {
+				"url": "https://github.com/sponsors/antfu"
+			}
+		},
+		"node_modules/@wangeditor/basic-modules": {
+			"version": "1.1.1",
+			"resolved": "https://registry.npmjs.org/@wangeditor/basic-modules/-/basic-modules-1.1.1.tgz",
+			"integrity": "sha512-tQl2Pw8M2g3CM+ESx2phzr9zSKeuFCM1AMBoPdnlbatU7Dnae0CsEB/b3C+gI0dIQzM2jh34yTmqgbbhrwuRLg==",
+			"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.2",
+			"resolved": "https://registry.npmjs.org/@wangeditor/code-highlight/-/code-highlight-1.0.2.tgz",
+			"integrity": "sha512-SCtOcUxjKqIso/LSxGSOaYr3G6MC2En0gNTyHIMCG928T0fo0ufaqp/vIXKQzVL2Y+X/CSAOB2EbrFlgGvr0AQ==",
+			"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.1",
+			"resolved": "https://registry.npmjs.org/@wangeditor/core/-/core-1.1.1.tgz",
+			"integrity": "sha512-SrbvOGlONMNMOeFIJI7fC9x0/6T6LvQHTITPCqjgbCm2QF+POcrHzRKGQOqKCsyKi9UJz9hLsjsvJnvP10rxjQ==",
+			"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.1",
+			"resolved": "https://registry.npmjs.org/@wangeditor/editor/-/editor-5.1.1.tgz",
+			"integrity": "sha512-BtccuHFm0QvYunIhIu7tllQWkwppkmEkD3OJ5Mn+F0REPQ/Z3HiEXbtlss2t9c/kHO4CtiFwv2XD/k/VEg7taA==",
+			"dependencies": {
+				"@uppy/core": "^2.1.1",
+				"@uppy/xhr-upload": "^2.0.3",
+				"@wangeditor/basic-modules": "^1.1.1",
+				"@wangeditor/code-highlight": "^1.0.2",
+				"@wangeditor/core": "^1.1.1",
+				"@wangeditor/list-module": "^1.0.2",
+				"@wangeditor/table-module": "^1.1.0",
+				"@wangeditor/upload-image-module": "^1.0.1",
+				"@wangeditor/video-module": "^1.1.0",
+				"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/list-module": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/@wangeditor/list-module/-/list-module-1.0.2.tgz",
+			"integrity": "sha512-VfENZEFvsLTiLxN/cj8cibFGy9NVV+/cfATTiLiH9ef+8lgKv8apttXYVlqIAfnlJLLuCk0cIm8c/zH+hbtrZg==",
+			"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.0",
+			"resolved": "https://registry.npmjs.org/@wangeditor/table-module/-/table-module-1.1.0.tgz",
+			"integrity": "sha512-QpjCXSzsXcsR0pEI5Pu28e8aYh9+lHcVV4TTmGV6lRGE/etQF3PHUZNGUlfhkCgmGPq+E7n/Whb4RpAM3PJVhw==",
+			"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.1",
+			"resolved": "https://registry.npmjs.org/@wangeditor/upload-image-module/-/upload-image-module-1.0.1.tgz",
+			"integrity": "sha512-vgUV4ENttTITblqtVuzleIq732OmzmzzgrIvX6b3GRGPSw5u8glJ/87tOEhvHjHECc4oFo18B7xzJ1GpBj79/w==",
+			"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.0",
+			"resolved": "https://registry.npmjs.org/@wangeditor/video-module/-/video-module-1.1.0.tgz",
+			"integrity": "sha512-VR6x7Vk9ebvXtxCPwobiNiTGZGgqEzCVc6ViWlNH3v4jlDIeo/s7N7OCgpvELR7X/X7GHecBu7wySDkHIskB5w==",
+			"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/@webassemblyjs/ast": {
+			"version": "1.11.1",
+			"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz",
+			"integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==",
+			"dev": true,
+			"peer": true,
+			"dependencies": {
+				"@webassemblyjs/helper-numbers": "1.11.1",
+				"@webassemblyjs/helper-wasm-bytecode": "1.11.1"
+			}
+		},
+		"node_modules/@webassemblyjs/floating-point-hex-parser": {
+			"version": "1.11.1",
+			"resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz",
+			"integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==",
+			"dev": true,
+			"peer": true
+		},
+		"node_modules/@webassemblyjs/helper-api-error": {
+			"version": "1.11.1",
+			"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz",
+			"integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==",
+			"dev": true,
+			"peer": true
+		},
+		"node_modules/@webassemblyjs/helper-buffer": {
+			"version": "1.11.1",
+			"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz",
+			"integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==",
+			"dev": true,
+			"peer": true
+		},
+		"node_modules/@webassemblyjs/helper-numbers": {
+			"version": "1.11.1",
+			"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz",
+			"integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==",
+			"dev": true,
+			"peer": true,
+			"dependencies": {
+				"@webassemblyjs/floating-point-hex-parser": "1.11.1",
+				"@webassemblyjs/helper-api-error": "1.11.1",
+				"@xtuc/long": "4.2.2"
+			}
+		},
+		"node_modules/@webassemblyjs/helper-wasm-bytecode": {
+			"version": "1.11.1",
+			"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz",
+			"integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==",
+			"dev": true,
+			"peer": true
+		},
+		"node_modules/@webassemblyjs/helper-wasm-section": {
+			"version": "1.11.1",
+			"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz",
+			"integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==",
+			"dev": true,
+			"peer": true,
+			"dependencies": {
+				"@webassemblyjs/ast": "1.11.1",
+				"@webassemblyjs/helper-buffer": "1.11.1",
+				"@webassemblyjs/helper-wasm-bytecode": "1.11.1",
+				"@webassemblyjs/wasm-gen": "1.11.1"
+			}
+		},
+		"node_modules/@webassemblyjs/ieee754": {
+			"version": "1.11.1",
+			"resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz",
+			"integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==",
+			"dev": true,
+			"peer": true,
+			"dependencies": {
+				"@xtuc/ieee754": "^1.2.0"
+			}
+		},
+		"node_modules/@webassemblyjs/leb128": {
+			"version": "1.11.1",
+			"resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz",
+			"integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==",
+			"dev": true,
+			"peer": true,
+			"dependencies": {
+				"@xtuc/long": "4.2.2"
+			}
+		},
+		"node_modules/@webassemblyjs/utf8": {
+			"version": "1.11.1",
+			"resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz",
+			"integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==",
+			"dev": true,
+			"peer": true
+		},
+		"node_modules/@webassemblyjs/wasm-edit": {
+			"version": "1.11.1",
+			"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz",
+			"integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==",
+			"dev": true,
+			"peer": true,
+			"dependencies": {
+				"@webassemblyjs/ast": "1.11.1",
+				"@webassemblyjs/helper-buffer": "1.11.1",
+				"@webassemblyjs/helper-wasm-bytecode": "1.11.1",
+				"@webassemblyjs/helper-wasm-section": "1.11.1",
+				"@webassemblyjs/wasm-gen": "1.11.1",
+				"@webassemblyjs/wasm-opt": "1.11.1",
+				"@webassemblyjs/wasm-parser": "1.11.1",
+				"@webassemblyjs/wast-printer": "1.11.1"
+			}
+		},
+		"node_modules/@webassemblyjs/wasm-gen": {
+			"version": "1.11.1",
+			"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz",
+			"integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==",
+			"dev": true,
+			"peer": true,
+			"dependencies": {
+				"@webassemblyjs/ast": "1.11.1",
+				"@webassemblyjs/helper-wasm-bytecode": "1.11.1",
+				"@webassemblyjs/ieee754": "1.11.1",
+				"@webassemblyjs/leb128": "1.11.1",
+				"@webassemblyjs/utf8": "1.11.1"
+			}
+		},
+		"node_modules/@webassemblyjs/wasm-opt": {
+			"version": "1.11.1",
+			"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz",
+			"integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==",
+			"dev": true,
+			"peer": true,
+			"dependencies": {
+				"@webassemblyjs/ast": "1.11.1",
+				"@webassemblyjs/helper-buffer": "1.11.1",
+				"@webassemblyjs/wasm-gen": "1.11.1",
+				"@webassemblyjs/wasm-parser": "1.11.1"
+			}
+		},
+		"node_modules/@webassemblyjs/wasm-parser": {
+			"version": "1.11.1",
+			"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz",
+			"integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==",
+			"dev": true,
+			"peer": true,
+			"dependencies": {
+				"@webassemblyjs/ast": "1.11.1",
+				"@webassemblyjs/helper-api-error": "1.11.1",
+				"@webassemblyjs/helper-wasm-bytecode": "1.11.1",
+				"@webassemblyjs/ieee754": "1.11.1",
+				"@webassemblyjs/leb128": "1.11.1",
+				"@webassemblyjs/utf8": "1.11.1"
+			}
+		},
+		"node_modules/@webassemblyjs/wast-printer": {
+			"version": "1.11.1",
+			"resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz",
+			"integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==",
+			"dev": true,
+			"peer": true,
+			"dependencies": {
+				"@webassemblyjs/ast": "1.11.1",
+				"@xtuc/long": "4.2.2"
+			}
+		},
+		"node_modules/@xtuc/ieee754": {
+			"version": "1.2.0",
+			"resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
+			"integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==",
+			"dev": true,
+			"peer": true
+		},
+		"node_modules/@xtuc/long": {
+			"version": "4.2.2",
+			"resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz",
+			"integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
+			"dev": true,
+			"peer": true
+		},
+		"node_modules/acorn": {
+			"version": "8.7.1",
+			"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz",
+			"integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==",
+			"dev": true,
+			"bin": {
+				"acorn": "bin/acorn"
+			},
+			"engines": {
+				"node": ">=0.4.0"
+			}
+		},
+		"node_modules/acorn-import-assertions": {
+			"version": "1.8.0",
+			"resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz",
+			"integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==",
+			"dev": true,
+			"peer": true,
+			"peerDependencies": {
+				"acorn": "^8"
+			}
+		},
+		"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/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/ajv-keywords": {
+			"version": "3.5.2",
+			"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
+			"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
+			"dev": true,
+			"peer": true,
+			"peerDependencies": {
+				"ajv": "^6.9.1"
+			}
+		},
+		"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.2",
+			"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
+			"integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
+			"dev": true,
+			"dependencies": {
+				"normalize-path": "^3.0.0",
+				"picomatch": "^2.0.4"
+			},
+			"engines": {
+				"node": ">= 8"
+			}
+		},
+		"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.1.1",
+			"resolved": "https://registry.npmjs.org/async-validator/-/async-validator-4.1.1.tgz",
+			"integrity": "sha512-p4DO/JXwjs8klJyJL8Q2oM4ks5fUTze/h5k10oPPKMiLe1fj3G1QMzPHNmN1Py4ycOk7WlO2DcGXv1qiESJCZA=="
+		},
+		"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/axios": {
+			"version": "0.27.2",
+			"resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
+			"integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
+			"dependencies": {
+				"follow-redirects": "^1.14.9",
+				"form-data": "^4.0.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.20.3",
+			"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.3.tgz",
+			"integrity": "sha512-NBhymBQl1zM0Y5dQT/O+xiLP9/rzOIQdKM/eMJBAq7yBgaB6krIYLGejrwVYnSHZdqjscB1SPuAjHwxjvN6Wdg==",
+			"dev": true,
+			"funding": [
+				{
+					"type": "opencollective",
+					"url": "https://opencollective.com/browserslist"
+				},
+				{
+					"type": "tidelift",
+					"url": "https://tidelift.com/funding/github/npm/browserslist"
+				}
+			],
+			"peer": true,
+			"dependencies": {
+				"caniuse-lite": "^1.0.30001332",
+				"electron-to-chromium": "^1.4.118",
+				"escalade": "^3.1.1",
+				"node-releases": "^2.0.3",
+				"picocolors": "^1.0.0"
+			},
+			"bin": {
+				"browserslist": "cli.js"
+			},
+			"engines": {
+				"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+			}
+		},
+		"node_modules/buffer-from": {
+			"version": "1.1.2",
+			"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
+			"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
+			"dev": true,
+			"peer": true
+		},
+		"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/caniuse-lite": {
+			"version": "1.0.30001342",
+			"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001342.tgz",
+			"integrity": "sha512-bn6sOCu7L7jcbBbyNhLg0qzXdJ/PMbybZTH/BA6Roet9wxYRm6Tr9D0s0uhLkOZ6MSG+QU6txUgdpr3MXIVqjA==",
+			"dev": true,
+			"funding": [
+				{
+					"type": "opencollective",
+					"url": "https://opencollective.com/browserslist"
+				},
+				{
+					"type": "tidelift",
+					"url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+				}
+			],
+			"peer": 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/chrome-trace-event": {
+			"version": "1.0.3",
+			"resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz",
+			"integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==",
+			"dev": true,
+			"peer": true,
+			"engines": {
+				"node": ">=6.0"
+			}
+		},
+		"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/commander": {
+			"version": "2.20.3",
+			"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+			"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+			"dev": true,
+			"peer": true
+		},
+		"node_modules/compute-scroll-into-view": {
+			"version": "1.0.17",
+			"resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-1.0.17.tgz",
+			"integrity": "sha512-j4dx+Fb0URmzbwwMUrhqWM2BEWHdFGx+qZ9qqASHRPqvTYdqvWnHg0H1hIbcyLnvgnoNAVMlwkepyqM3DaIFUg=="
+		},
+		"node_modules/concat-map": {
+			"version": "0.0.1",
+			"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+			"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+			"dev": true
+		},
+		"node_modules/countup.js": {
+			"version": "2.2.0",
+			"resolved": "https://registry.npmjs.org/countup.js/-/countup.js-2.2.0.tgz",
+			"integrity": "sha512-m0TvFNXm9/eFqJm+QiKVI8e0wRUHzlQSewz9dqVjlhl2DFoZtceLbomwzxHz0hJ1+r4zBC7wSpR/TpthG49h6g=="
+		},
+		"node_modules/cropperjs": {
+			"version": "1.5.12",
+			"resolved": "https://registry.npmjs.org/cropperjs/-/cropperjs-1.5.12.tgz",
+			"integrity": "sha512-re7UdjE5UnwdrovyhNzZ6gathI4Rs3KGCBSc8HCIjUo5hO42CtzyblmWLj6QWVw7huHyDMfpKxhiO2II77nhDw=="
+		},
+		"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.20",
+			"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.20.tgz",
+			"integrity": "sha512-/WwNkdXfckNgw6S5R125rrW8ez139lBHWouiBvX8dfMFtcn6V81REDqnH7+CRpRipfYlyU1CmOnOxrmGcFOjeA=="
+		},
+		"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.2",
+			"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.2.tgz",
+			"integrity": "sha512-F4LXf1OeU9hrSYRPTTj/6FbO4HTjPKXvEIC1P2kcnFurViINCVk3ZV0xAS3XVx9MkMsXbbqlK6hjseaYbgKEHw=="
+		},
+		"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/delayed-stream": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+			"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
+			"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/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/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/dotenv": {
+			"version": "16.0.1",
+			"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.1.tgz",
+			"integrity": "sha512-1K6hR6wtk2FviQ4kEiSjFiH5rpzEVi8WW0x96aztHVMhEspNpc4DVOUTEHtEva5VThQ8IaBX1Pe4gSzpVVUsKQ==",
+			"dev": true,
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/echarts": {
+			"version": "5.3.2",
+			"resolved": "https://registry.npmjs.org/echarts/-/echarts-5.3.2.tgz",
+			"integrity": "sha512-LWCt7ohOKdJqyiBJ0OGBmE9szLdfA9sGcsMEi+GGoc6+Xo75C+BkcT/6NNGRHAWtnQl2fNow05AQjznpap28TQ==",
+			"dependencies": {
+				"tslib": "2.3.0",
+				"zrender": "5.3.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.0.0",
+			"resolved": "https://registry.npmjs.org/echarts-wordcloud/-/echarts-wordcloud-2.0.0.tgz",
+			"integrity": "sha512-K7l6pTklqdW7ZWzT/1CS0KhBSINr/cd7c5N1fVMzZMwLQHEwT7x+nivK7g5hkVh7WNcAv4Dn6/ZS5zMKRozC1g==",
+			"peerDependencies": {
+				"echarts": "^5.0.1"
+			}
+		},
+		"node_modules/electron-to-chromium": {
+			"version": "1.4.137",
+			"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.137.tgz",
+			"integrity": "sha512-0Rcpald12O11BUogJagX3HsCN3FE83DSqWjgXoHo5a72KUKMSfI39XBgJpgNNxS9fuGzytaFjE06kZkiVFy2qA==",
+			"dev": true,
+			"peer": true
+		},
+		"node_modules/element-plus": {
+			"version": "2.2.2",
+			"resolved": "https://registry.npmjs.org/element-plus/-/element-plus-2.2.2.tgz",
+			"integrity": "sha512-yGcj2Ayb0jZO1WbI51tHJ4efhlfWKlBqqGtWbzhq+tcpfaKzJZN+IHRouuFasqn0ZV3tWCDu1jggDR1+9y7XfQ==",
+			"dependencies": {
+				"@ctrl/tinycolor": "^3.4.1",
+				"@element-plus/icons-vue": "^1.1.4",
+				"@floating-ui/dom": "^0.5.0",
+				"@popperjs/core": "npm:@sxzz/popperjs-es@^2.11.7",
+				"@types/lodash": "^4.14.182",
+				"@types/lodash-es": "^4.17.6",
+				"@vueuse/core": "^8.5.0",
+				"async-validator": "^4.1.1",
+				"dayjs": "^1.11.2",
+				"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.1.2"
+			},
+			"peerDependencies": {
+				"vue": "^3.2.0"
+			}
+		},
+		"node_modules/element-plus/node_modules/@element-plus/icons-vue": {
+			"version": "1.1.4",
+			"resolved": "https://registry.npmjs.org/@element-plus/icons-vue/-/icons-vue-1.1.4.tgz",
+			"integrity": "sha512-Iz/nHqdp1sFPmdzRwHkEQQA3lKvoObk8azgABZ81QUOpW9s/lUyQVUSh0tNtEPZXQlKwlSh7SPgoVxzrE0uuVQ==",
+			"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/enhanced-resolve": {
+			"version": "5.9.3",
+			"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.3.tgz",
+			"integrity": "sha512-Bq9VSor+kjvW3f9/MiiR4eE3XYgOl7/rS8lnSxbRbF3kS0B2r+Y9w5krBWxZgDxASVZbdYrn5wT4j/Wb0J9qow==",
+			"dev": true,
+			"peer": true,
+			"dependencies": {
+				"graceful-fs": "^4.2.4",
+				"tapable": "^2.2.0"
+			},
+			"engines": {
+				"node": ">=10.13.0"
+			}
+		},
+		"node_modules/es-module-lexer": {
+			"version": "0.9.3",
+			"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz",
+			"integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==",
+			"dev": true,
+			"peer": true
+		},
+		"node_modules/es5-ext": {
+			"version": "0.10.61",
+			"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.61.tgz",
+			"integrity": "sha512-yFhIqQAzu2Ca2I4SE2Au3rxVfmohU9Y7wqGR+s7+H7krk26NXhIRAZDgqd6xqjCEFUomDEA3/Bo/7fKmIkW1kA==",
+			"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": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=",
+			"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.14.39",
+			"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.39.tgz",
+			"integrity": "sha512-2kKujuzvRWYtwvNjYDY444LQIA3TyJhJIX3Yo4+qkFlDDtGlSicWgeHVJqMUP/2sSfH10PGwfsj+O2ro1m10xQ==",
+			"dev": true,
+			"hasInstallScript": true,
+			"bin": {
+				"esbuild": "bin/esbuild"
+			},
+			"engines": {
+				"node": ">=12"
+			},
+			"optionalDependencies": {
+				"esbuild-android-64": "0.14.39",
+				"esbuild-android-arm64": "0.14.39",
+				"esbuild-darwin-64": "0.14.39",
+				"esbuild-darwin-arm64": "0.14.39",
+				"esbuild-freebsd-64": "0.14.39",
+				"esbuild-freebsd-arm64": "0.14.39",
+				"esbuild-linux-32": "0.14.39",
+				"esbuild-linux-64": "0.14.39",
+				"esbuild-linux-arm": "0.14.39",
+				"esbuild-linux-arm64": "0.14.39",
+				"esbuild-linux-mips64le": "0.14.39",
+				"esbuild-linux-ppc64le": "0.14.39",
+				"esbuild-linux-riscv64": "0.14.39",
+				"esbuild-linux-s390x": "0.14.39",
+				"esbuild-netbsd-64": "0.14.39",
+				"esbuild-openbsd-64": "0.14.39",
+				"esbuild-sunos-64": "0.14.39",
+				"esbuild-windows-32": "0.14.39",
+				"esbuild-windows-64": "0.14.39",
+				"esbuild-windows-arm64": "0.14.39"
+			}
+		},
+		"node_modules/esbuild-android-64": {
+			"version": "0.14.39",
+			"resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.39.tgz",
+			"integrity": "sha512-EJOu04p9WgZk0UoKTqLId9VnIsotmI/Z98EXrKURGb3LPNunkeffqQIkjS2cAvidh+OK5uVrXaIP229zK6GvhQ==",
+			"cpu": [
+				"x64"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"android"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/esbuild-android-arm64": {
+			"version": "0.14.39",
+			"resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.39.tgz",
+			"integrity": "sha512-+twajJqO7n3MrCz9e+2lVOnFplRsaGRwsq1KL/uOy7xK7QdRSprRQcObGDeDZUZsacD5gUkk6OiHiYp6RzU3CA==",
+			"cpu": [
+				"arm64"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"android"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/esbuild-darwin-64": {
+			"version": "0.14.39",
+			"resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.39.tgz",
+			"integrity": "sha512-ImT6eUw3kcGcHoUxEcdBpi6LfTRWaV6+qf32iYYAfwOeV+XaQ/Xp5XQIBiijLeo+LpGci9M0FVec09nUw41a5g==",
+			"cpu": [
+				"x64"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"darwin"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/esbuild-darwin-arm64": {
+			"version": "0.14.39",
+			"resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.39.tgz",
+			"integrity": "sha512-/fcQ5UhE05OiT+bW5v7/up1bDsnvaRZPJxXwzXsMRrr7rZqPa85vayrD723oWMT64dhrgWeA3FIneF8yER0XTw==",
+			"cpu": [
+				"arm64"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"darwin"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/esbuild-freebsd-64": {
+			"version": "0.14.39",
+			"resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.39.tgz",
+			"integrity": "sha512-oMNH8lJI4wtgN5oxuFP7BQ22vgB/e3Tl5Woehcd6i2r6F3TszpCnNl8wo2d/KvyQ4zvLvCWAlRciumhQg88+kQ==",
+			"cpu": [
+				"x64"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"freebsd"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/esbuild-freebsd-arm64": {
+			"version": "0.14.39",
+			"resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.39.tgz",
+			"integrity": "sha512-1GHK7kwk57ukY2yI4ILWKJXaxfr+8HcM/r/JKCGCPziIVlL+Wi7RbJ2OzMcTKZ1HpvEqCTBT/J6cO4ZEwW4Ypg==",
+			"cpu": [
+				"arm64"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"freebsd"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/esbuild-linux-32": {
+			"version": "0.14.39",
+			"resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.39.tgz",
+			"integrity": "sha512-g97Sbb6g4zfRLIxHgW2pc393DjnkTRMeq3N1rmjDUABxpx8SjocK4jLen+/mq55G46eE2TA0MkJ4R3SpKMu7dg==",
+			"cpu": [
+				"ia32"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/esbuild-linux-64": {
+			"version": "0.14.39",
+			"resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.39.tgz",
+			"integrity": "sha512-4tcgFDYWdI+UbNMGlua9u1Zhu0N5R6u9tl5WOM8aVnNX143JZoBZLpCuUr5lCKhnD0SCO+5gUyMfupGrHtfggQ==",
+			"cpu": [
+				"x64"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/esbuild-linux-arm": {
+			"version": "0.14.39",
+			"resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.39.tgz",
+			"integrity": "sha512-t0Hn1kWVx5UpCzAJkKRfHeYOLyFnXwYynIkK54/h3tbMweGI7dj400D1k0Vvtj2u1P+JTRT9tx3AjtLEMmfVBQ==",
+			"cpu": [
+				"arm"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/esbuild-linux-arm64": {
+			"version": "0.14.39",
+			"resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.39.tgz",
+			"integrity": "sha512-23pc8MlD2D6Px1mV8GMglZlKgwgNKAO8gsgsLLcXWSs9lQsCYkIlMo/2Ycfo5JrDIbLdwgP8D2vpfH2KcBqrDQ==",
+			"cpu": [
+				"arm64"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/esbuild-linux-mips64le": {
+			"version": "0.14.39",
+			"resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.39.tgz",
+			"integrity": "sha512-epwlYgVdbmkuRr5n4es3B+yDI0I2e/nxhKejT9H0OLxFAlMkeQZxSpxATpDc9m8NqRci6Kwyb/SfmD1koG2Zuw==",
+			"cpu": [
+				"mips64el"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/esbuild-linux-ppc64le": {
+			"version": "0.14.39",
+			"resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.39.tgz",
+			"integrity": "sha512-W/5ezaq+rQiQBThIjLMNjsuhPHg+ApVAdTz2LvcuesZFMsJoQAW2hutoyg47XxpWi7aEjJGrkS26qCJKhRn3QQ==",
+			"cpu": [
+				"ppc64"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/esbuild-linux-riscv64": {
+			"version": "0.14.39",
+			"resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.39.tgz",
+			"integrity": "sha512-IS48xeokcCTKeQIOke2O0t9t14HPvwnZcy+5baG13Z1wxs9ZrC5ig5ypEQQh4QMKxURD5TpCLHw2W42CLuVZaA==",
+			"cpu": [
+				"riscv64"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/esbuild-linux-s390x": {
+			"version": "0.14.39",
+			"resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.39.tgz",
+			"integrity": "sha512-zEfunpqR8sMomqXhNTFEKDs+ik7HC01m3M60MsEjZOqaywHu5e5682fMsqOlZbesEAAaO9aAtRBsU7CHnSZWyA==",
+			"cpu": [
+				"s390x"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/esbuild-netbsd-64": {
+			"version": "0.14.39",
+			"resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.39.tgz",
+			"integrity": "sha512-Uo2suJBSIlrZCe4E0k75VDIFJWfZy+bOV6ih3T4MVMRJh1lHJ2UyGoaX4bOxomYN3t+IakHPyEoln1+qJ1qYaA==",
+			"cpu": [
+				"x64"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"netbsd"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/esbuild-openbsd-64": {
+			"version": "0.14.39",
+			"resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.39.tgz",
+			"integrity": "sha512-secQU+EpgUPpYjJe3OecoeGKVvRMLeKUxSMGHnK+aK5uQM3n1FPXNJzyz1LHFOo0WOyw+uoCxBYdM4O10oaCAA==",
+			"cpu": [
+				"x64"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"openbsd"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/esbuild-sunos-64": {
+			"version": "0.14.39",
+			"resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.39.tgz",
+			"integrity": "sha512-qHq0t5gePEDm2nqZLb+35p/qkaXVS7oIe32R0ECh2HOdiXXkj/1uQI9IRogGqKkK+QjDG+DhwiUw7QoHur/Rwg==",
+			"cpu": [
+				"x64"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"sunos"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/esbuild-windows-32": {
+			"version": "0.14.39",
+			"resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.39.tgz",
+			"integrity": "sha512-XPjwp2OgtEX0JnOlTgT6E5txbRp6Uw54Isorm3CwOtloJazeIWXuiwK0ONJBVb/CGbiCpS7iP2UahGgd2p1x+Q==",
+			"cpu": [
+				"ia32"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"win32"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/esbuild-windows-64": {
+			"version": "0.14.39",
+			"resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.39.tgz",
+			"integrity": "sha512-E2wm+5FwCcLpKsBHRw28bSYQw0Ikxb7zIMxw3OPAkiaQhLVr3dnVO8DofmbWhhf6b97bWzg37iSZ45ZDpLw7Ow==",
+			"cpu": [
+				"x64"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"win32"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/esbuild-windows-arm64": {
+			"version": "0.14.39",
+			"resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.39.tgz",
+			"integrity": "sha512-sBZQz5D+Gd0EQ09tZRnz/PpVdLwvp/ufMtJ1iDFYddDaPpZXKqPyaxfYBLs3ueiaksQ26GGa7sci0OqFzNs7KA==",
+			"cpu": [
+				"arm64"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"win32"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/escalade": {
+			"version": "3.1.1",
+			"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
+			"integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
+			"dev": true,
+			"peer": 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": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
+		},
+		"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.17.0",
+			"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.17.0.tgz",
+			"integrity": "sha512-gq0m0BTJfci60Fz4nczYxNAlED+sMcihltndR8t9t1evnU/azx53x3t2UHXC/uRjcbvRw/XctpaNygSTcQD+Iw==",
+			"dev": true,
+			"dependencies": {
+				"@eslint/eslintrc": "^1.3.0",
+				"@humanwhocodes/config-array": "^0.9.2",
+				"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.3.2",
+				"esquery": "^1.4.0",
+				"esutils": "^2.0.2",
+				"fast-deep-equal": "^3.1.3",
+				"file-entry-cache": "^6.0.1",
+				"functional-red-black-tree": "^1.0.1",
+				"glob-parent": "^6.0.1",
+				"globals": "^13.15.0",
+				"ignore": "^5.2.0",
+				"import-fresh": "^3.0.0",
+				"imurmurhash": "^0.1.4",
+				"is-glob": "^4.0.0",
+				"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",
+				"v8-compile-cache": "^2.0.3"
+			},
+			"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.1.0",
+			"resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.1.0.tgz",
+			"integrity": "sha512-EPCeInPicQ/YyfOWJDr1yfEeSNoFCMzUus107lZyYi37xejdOolNzS5MXGXp8+9bkoKZMdv/1AcZzQebME6r+g==",
+			"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.3.2",
+			"resolved": "https://registry.npmjs.org/espree/-/espree-9.3.2.tgz",
+			"integrity": "sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==",
+			"dev": true,
+			"dependencies": {
+				"acorn": "^8.7.1",
+				"acorn-jsx": "^5.3.2",
+				"eslint-visitor-keys": "^3.3.0"
+			},
+			"engines": {
+				"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+			}
+		},
+		"node_modules/esquery": {
+			"version": "1.4.0",
+			"resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz",
+			"integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==",
+			"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": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=",
+			"dependencies": {
+				"d": "1",
+				"es5-ext": "~0.10.14"
+			}
+		},
+		"node_modules/events": {
+			"version": "3.3.0",
+			"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
+			"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
+			"dev": true,
+			"peer": true,
+			"engines": {
+				"node": ">=0.8.x"
+			}
+		},
+		"node_modules/ext": {
+			"version": "1.6.0",
+			"resolved": "https://registry.npmjs.org/ext/-/ext-1.6.0.tgz",
+			"integrity": "sha512-sdBImtzkq2HpkdRLtlLWDa6w4DX22ijZLKx8BMPUuKe1c5lbN6xwQDQCxSfxBQnHZ13ls/FH0MQZx/q/gr6FQg==",
+			"dependencies": {
+				"type": "^2.5.0"
+			}
+		},
+		"node_modules/ext/node_modules/type": {
+			"version": "2.6.0",
+			"resolved": "https://registry.npmjs.org/type/-/type-2.6.0.tgz",
+			"integrity": "sha512-eiDBDOmkih5pMbo9OqsqPRGMljLodLcwd5XD5JbtNB0o89xZAwynY9EdCDsJU7LtcVCClu9DvM7/0Ep1hYX3EQ=="
+		},
+		"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.11",
+			"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz",
+			"integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==",
+			"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": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
+			"dev": true
+		},
+		"node_modules/fastq": {
+			"version": "1.13.0",
+			"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz",
+			"integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==",
+			"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/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.5",
+			"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz",
+			"integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==",
+			"dev": true
+		},
+		"node_modules/follow-redirects": {
+			"version": "1.15.0",
+			"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.0.tgz",
+			"integrity": "sha512-aExlJShTV4qOUOL7yF1U5tvLCB0xQuudbf6toyYA0E/acBNw71mvjFTnLaRp50aQaYocMR0a/RMMBIHeZnGyjQ==",
+			"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/fs.realpath": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+			"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+			"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==",
+			"dev": true
+		},
+		"node_modules/functional-red-black-tree": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
+			"integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
+			"dev": true
+		},
+		"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/glob-to-regexp": {
+			"version": "0.4.1",
+			"resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz",
+			"integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==",
+			"dev": true,
+			"peer": true
+		},
+		"node_modules/globals": {
+			"version": "13.15.0",
+			"resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz",
+			"integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==",
+			"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": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=",
+			"dependencies": {
+				"delegate": "^3.1.2"
+			}
+		},
+		"node_modules/graceful-fs": {
+			"version": "4.2.10",
+			"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
+			"integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==",
+			"dev": true,
+			"peer": 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==",
+			"dev": true,
+			"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/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.0",
+			"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz",
+			"integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==",
+			"dev": true,
+			"engines": {
+				"node": ">= 4"
+			}
+		},
+		"node_modules/immer": {
+			"version": "9.0.14",
+			"resolved": "https://registry.npmjs.org/immer/-/immer-9.0.14.tgz",
+			"integrity": "sha512-ubBeqQutOSLIFCUBN03jGeOS6a3DoYlSYwYJTa+gSKEZKU5redJIqkIdZ3JVv/4RZpfcXdAWH5zCNLWPRv2WDw==",
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/immer"
+			}
+		},
+		"node_modules/immutable": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/immutable/-/immutable-4.0.0.tgz",
+			"integrity": "sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw==",
+			"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": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
+			"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": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+			"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.9.0",
+			"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz",
+			"integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==",
+			"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": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+			"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-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": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+			"dev": true
+		},
+		"node_modules/jest-worker": {
+			"version": "27.5.1",
+			"resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz",
+			"integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==",
+			"dev": true,
+			"peer": true,
+			"dependencies": {
+				"@types/node": "*",
+				"merge-stream": "^2.0.0",
+				"supports-color": "^8.0.0"
+			},
+			"engines": {
+				"node": ">= 10.13.0"
+			}
+		},
+		"node_modules/jest-worker/node_modules/supports-color": {
+			"version": "8.1.1",
+			"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+			"integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+			"dev": true,
+			"peer": true,
+			"dependencies": {
+				"has-flag": "^4.0.0"
+			},
+			"engines": {
+				"node": ">=10"
+			},
+			"funding": {
+				"url": "https://github.com/chalk/supports-color?sponsor=1"
+			}
+		},
+		"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-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-parse-even-better-errors": {
+			"version": "2.3.1",
+			"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+			"integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
+			"dev": true,
+			"peer": true
+		},
+		"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": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
+			"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/klona": {
+			"version": "2.0.5",
+			"resolved": "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz",
+			"integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==",
+			"dev": true,
+			"engines": {
+				"node": ">= 8"
+			}
+		},
+		"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/loader-runner": {
+			"version": "4.3.0",
+			"resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz",
+			"integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==",
+			"dev": true,
+			"peer": true,
+			"engines": {
+				"node": ">=6.11.5"
+			}
+		},
+		"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.2",
+			"resolved": "https://registry.npmjs.org/lodash-unified/-/lodash-unified-1.0.2.tgz",
+			"integrity": "sha512-OGbEy+1P+UT26CYi4opY4gebD8cWRDxAT6MAObIVQMiqYdxZr1g3QHWCToVsm31x2NkLS4K3+MC2qInaRMa39g==",
+			"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": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY="
+		},
+		"node_modules/lodash.clonedeep": {
+			"version": "4.5.0",
+			"resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
+			"integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8="
+		},
+		"node_modules/lodash.debounce": {
+			"version": "4.0.8",
+			"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
+			"integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168="
+		},
+		"node_modules/lodash.foreach": {
+			"version": "4.5.0",
+			"resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz",
+			"integrity": "sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM="
+		},
+		"node_modules/lodash.isequal": {
+			"version": "4.5.0",
+			"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
+			"integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA="
+		},
+		"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.sortby": {
+			"version": "4.7.0",
+			"resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
+			"integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=",
+			"dev": true,
+			"peer": true
+		},
+		"node_modules/lodash.throttle": {
+			"version": "4.1.1",
+			"resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
+			"integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ="
+		},
+		"node_modules/lodash.toarray": {
+			"version": "4.4.0",
+			"resolved": "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz",
+			"integrity": "sha1-JMS/zWsvuji/0FlNsRedjptlZWE="
+		},
+		"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/merge-stream": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
+			"integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
+			"dev": true,
+			"peer": true
+		},
+		"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": "sha1-P4fDHprxpf1IX7nbE0Qosju7e6g=",
+			"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/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": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
+			"dev": true
+		},
+		"node_modules/neo-async": {
+			"version": "2.6.2",
+			"resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
+			"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
+			"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.4",
+			"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.4.tgz",
+			"integrity": "sha512-gbMzqQtTtDz/00jQzZ21PQzdI9PyLYqUSvD0p3naOhX4odFji0ZxYdnVwPTxmSwkmxhcFImpozceidSG+AgoPQ==",
+			"dev": true,
+			"peer": 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-wheel-es": {
+			"version": "1.1.2",
+			"resolved": "https://registry.npmjs.org/normalize-wheel-es/-/normalize-wheel-es-1.1.2.tgz",
+			"integrity": "sha512-scX83plWJXYH1J4+BhAuIHadROzxX0UBF3+HuZNY2Ks8BciE7tSTQ+5JhTsvzjaO0/EJdm4JBGrfObKxFf3Png=="
+		},
+		"node_modules/nprogress": {
+			"version": "0.2.0",
+			"resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz",
+			"integrity": "sha1-y480xTIT2JVyP8urkH6UIq28r7E="
+		},
+		"node_modules/nth-check": {
+			"version": "2.0.1",
+			"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz",
+			"integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==",
+			"dev": true,
+			"dependencies": {
+				"boolbase": "^1.0.0"
+			},
+			"funding": {
+				"url": "https://github.com/fb55/nth-check?sponsor=1"
+			}
+		},
+		"node_modules/once": {
+			"version": "1.4.0",
+			"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+			"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+			"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/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-is-absolute": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+			"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+			"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/pinia": {
+			"version": "2.0.14",
+			"resolved": "https://registry.npmjs.org/pinia/-/pinia-2.0.14.tgz",
+			"integrity": "sha512-0nPuZR4TetT/WcLN+feMSjWJku3SQU7dBbXC6uw+R6FLQJCsg+/0pzXyD82T1FmAYe0lsx+jnEDQ1BLgkRKlxA==",
+			"dependencies": {
+				"@vue/devtools-api": "^6.1.4",
+				"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.12.5",
+			"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.12.5.tgz",
+			"integrity": "sha512-BREuTgTYlUr0zw0EZn3hnhC3I6gPWv+Kwh4MCih6QcAeaTlaIX0DwOVN0wHej7hSvDPecz4jygy/idsgKfW58Q==",
+			"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.14",
+			"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz",
+			"integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==",
+			"funding": [
+				{
+					"type": "opencollective",
+					"url": "https://opencollective.com/postcss/"
+				},
+				{
+					"type": "tidelift",
+					"url": "https://tidelift.com/funding/github/npm/postcss"
+				}
+			],
+			"dependencies": {
+				"nanoid": "^3.3.4",
+				"picocolors": "^1.0.0",
+				"source-map-js": "^1.0.2"
+			},
+			"engines": {
+				"node": "^10 || ^12 || >=14"
+			}
+		},
+		"node_modules/postcss-selector-parser": {
+			"version": "6.0.10",
+			"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz",
+			"integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==",
+			"dev": true,
+			"dependencies": {
+				"cssesc": "^3.0.0",
+				"util-deprecate": "^1.0.2"
+			},
+			"engines": {
+				"node": ">=4"
+			}
+		},
+		"node_modules/preact": {
+			"version": "10.7.2",
+			"resolved": "https://registry.npmjs.org/preact/-/preact-10.7.2.tgz",
+			"integrity": "sha512-GLjn0I3r6ka+NvxJUppsVFqb4V0qDTEHT/QxHlidPuClGaxF/4AI2Qti4a0cv3XMh5n1+D3hLScW10LRIm5msQ==",
+			"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.6.2",
+			"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.6.2.tgz",
+			"integrity": "sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew==",
+			"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.28.0",
+			"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.28.0.tgz",
+			"integrity": "sha512-8aaXdYvl1F7iC7Xm1spqSaY/OJBpYW3v+KJ+F17iYxvdc8sfjW194COK5wVhMZX45tGteiBQgdvD/nhxcRwylw==",
+			"engines": {
+				"node": ">=6"
+			}
+		},
+		"node_modules/punycode": {
+			"version": "2.1.1",
+			"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+			"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+			"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/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/randombytes": {
+			"version": "2.1.0",
+			"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
+			"integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
+			"dev": true,
+			"peer": true,
+			"dependencies": {
+				"safe-buffer": "^5.1.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.9",
+			"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz",
+			"integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA=="
+		},
+		"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.0",
+			"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz",
+			"integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==",
+			"dev": true,
+			"dependencies": {
+				"is-core-module": "^2.8.1",
+				"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": "2.74.1",
+			"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.74.1.tgz",
+			"integrity": "sha512-K2zW7kV8Voua5eGkbnBtWYfMIhYhT9Pel2uhBk2WO5eMee161nPze/XRfvEQPFYz7KgrCCnmh2Wy0AMFLGGmMA==",
+			"dev": true,
+			"bin": {
+				"rollup": "dist/bin/rollup"
+			},
+			"engines": {
+				"node": ">=10.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/safe-buffer": {
+			"version": "5.2.1",
+			"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+			"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+			"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"
+				}
+			],
+			"peer": true
+		},
+		"node_modules/sass": {
+			"version": "1.52.2",
+			"resolved": "https://registry.npmjs.org/sass/-/sass-1.52.2.tgz",
+			"integrity": "sha512-mfHB2VSeFS7sZlPv9YohB9GB7yWIgQNTGniQwfQ04EoQN0wsQEv7SwpCwy/x48Af+Z3vDeFXz+iuXM3HK/phZQ==",
+			"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/sass-loader": {
+			"version": "13.0.0",
+			"resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-13.0.0.tgz",
+			"integrity": "sha512-IHCFecI+rbPvXE2zO/mqdVFe8MU7ElGrwga9hh2H65Ru4iaBJAMRteum1c4Gsxi9Cq1FOtTEDd6+/AEYuQDM4Q==",
+			"dev": true,
+			"dependencies": {
+				"klona": "^2.0.4",
+				"neo-async": "^2.6.2"
+			},
+			"engines": {
+				"node": ">= 14.15.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/webpack"
+			},
+			"peerDependencies": {
+				"fibers": ">= 3.1.0",
+				"node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0",
+				"sass": "^1.3.0",
+				"sass-embedded": "*",
+				"webpack": "^5.0.0"
+			},
+			"peerDependenciesMeta": {
+				"fibers": {
+					"optional": true
+				},
+				"node-sass": {
+					"optional": true
+				},
+				"sass": {
+					"optional": true
+				},
+				"sass-embedded": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/schema-utils": {
+			"version": "3.1.1",
+			"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz",
+			"integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==",
+			"dev": true,
+			"peer": true,
+			"dependencies": {
+				"@types/json-schema": "^7.0.8",
+				"ajv": "^6.12.5",
+				"ajv-keywords": "^3.5.2"
+			},
+			"engines": {
+				"node": ">= 10.13.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/webpack"
+			}
+		},
+		"node_modules/screenfull": {
+			"version": "6.0.1",
+			"resolved": "https://registry.npmjs.org/screenfull/-/screenfull-6.0.1.tgz",
+			"integrity": "sha512-yzQW+j4zMUBQC51xxWaoDYjxOtl8Kn+xvue3p6v/fv2pIi1jH4AldgVLU8TBfFVgH2x3VXlf3+YiA/AYIPlaew==",
+			"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.29",
+			"resolved": "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.29.tgz",
+			"integrity": "sha512-hxpAR6AN+Gh53AdAimHM6C8oTN1ppwVZITihix+WqalywBeFcQ6LdQP5ABNl26nX8GTEL7VT+b8lKpdqq65wXg==",
+			"dependencies": {
+				"compute-scroll-into-view": "^1.0.17"
+			}
+		},
+		"node_modules/select": {
+			"version": "1.1.2",
+			"resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz",
+			"integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0="
+		},
+		"node_modules/semver": {
+			"version": "7.3.7",
+			"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
+			"integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
+			"dev": true,
+			"dependencies": {
+				"lru-cache": "^6.0.0"
+			},
+			"bin": {
+				"semver": "bin/semver.js"
+			},
+			"engines": {
+				"node": ">=10"
+			}
+		},
+		"node_modules/serialize-javascript": {
+			"version": "6.0.0",
+			"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz",
+			"integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==",
+			"dev": true,
+			"peer": true,
+			"dependencies": {
+				"randombytes": "^2.1.0"
+			}
+		},
+		"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/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.0",
+			"resolved": "https://registry.npmjs.org/snabbdom/-/snabbdom-3.5.0.tgz",
+			"integrity": "sha512-Ff5BKG18KrrPuskHJlA9aujPHqEabItaDl96l7ZZndF4zt5AYSczz7ZjjgQAX5IBd5cd25lw9NfgX21yVUJ+9g==",
+			"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/source-map-support": {
+			"version": "0.5.21",
+			"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
+			"integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+			"dev": true,
+			"peer": true,
+			"dependencies": {
+				"buffer-from": "^1.0.0",
+				"source-map": "^0.6.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=="
+		},
+		"node_modules/splitpanes": {
+			"version": "3.1.1",
+			"resolved": "https://registry.npmjs.org/splitpanes/-/splitpanes-3.1.1.tgz",
+			"integrity": "sha512-VUkxDJfIGSvTM/fm/+OSrx8ha9URwE/9B8FPvfzoBuAxVELIHBWpsfnJXIXv77zVwuex//QQL4kTU9SDBPeHjA=="
+		},
+		"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/tapable": {
+			"version": "2.2.1",
+			"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
+			"integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
+			"dev": true,
+			"peer": true,
+			"engines": {
+				"node": ">=6"
+			}
+		},
+		"node_modules/terser": {
+			"version": "5.13.1",
+			"resolved": "https://registry.npmjs.org/terser/-/terser-5.13.1.tgz",
+			"integrity": "sha512-hn4WKOfwnwbYfe48NgrQjqNOH9jzLqRcIfbYytOXCOv46LBfWr9bDS17MQqOi+BWGD0sJK3Sj5NC/gJjiojaoA==",
+			"dev": true,
+			"peer": true,
+			"dependencies": {
+				"acorn": "^8.5.0",
+				"commander": "^2.20.0",
+				"source-map": "~0.8.0-beta.0",
+				"source-map-support": "~0.5.20"
+			},
+			"bin": {
+				"terser": "bin/terser"
+			},
+			"engines": {
+				"node": ">=10"
+			}
+		},
+		"node_modules/terser-webpack-plugin": {
+			"version": "5.3.1",
+			"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.1.tgz",
+			"integrity": "sha512-GvlZdT6wPQKbDNW/GDQzZFg/j4vKU96yl2q6mcUkzKOgW4gwf1Z8cZToUCrz31XHlPWH8MVb1r2tFtdDtTGJ7g==",
+			"dev": true,
+			"peer": true,
+			"dependencies": {
+				"jest-worker": "^27.4.5",
+				"schema-utils": "^3.1.1",
+				"serialize-javascript": "^6.0.0",
+				"source-map": "^0.6.1",
+				"terser": "^5.7.2"
+			},
+			"engines": {
+				"node": ">= 10.13.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/webpack"
+			},
+			"peerDependencies": {
+				"webpack": "^5.1.0"
+			},
+			"peerDependenciesMeta": {
+				"@swc/core": {
+					"optional": true
+				},
+				"esbuild": {
+					"optional": true
+				},
+				"uglify-js": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/terser/node_modules/source-map": {
+			"version": "0.8.0-beta.0",
+			"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz",
+			"integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==",
+			"dev": true,
+			"peer": true,
+			"dependencies": {
+				"whatwg-url": "^7.0.0"
+			},
+			"engines": {
+				"node": ">= 8"
+			}
+		},
+		"node_modules/text-table": {
+			"version": "0.2.0",
+			"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+			"integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
+			"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/tr46": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz",
+			"integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=",
+			"dev": true,
+			"peer": true,
+			"dependencies": {
+				"punycode": "^2.1.0"
+			}
+		},
+		"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.7.3",
+			"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.3.tgz",
+			"integrity": "sha512-WOkT3XYvrpXx4vMMqlD+8R8R37fZkjyLGlxavMc4iB8lrl8L0DeTcHbYgw/v0N/z9wAFsgBhcsF0ruoySS22mA==",
+			"devOptional": true,
+			"bin": {
+				"tsc": "bin/tsc",
+				"tsserver": "bin/tsserver"
+			},
+			"engines": {
+				"node": ">=4.2.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": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
+			"dev": true
+		},
+		"node_modules/v8-compile-cache": {
+			"version": "2.3.0",
+			"resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz",
+			"integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==",
+			"dev": true
+		},
+		"node_modules/vite": {
+			"version": "2.9.9",
+			"resolved": "https://registry.npmjs.org/vite/-/vite-2.9.9.tgz",
+			"integrity": "sha512-ffaam+NgHfbEmfw/Vuh6BHKKlI/XIAhxE5QSS7gFLIngxg171mg1P3a4LSRME0z2ZU1ScxoKzphkipcYwSD5Ew==",
+			"dev": true,
+			"dependencies": {
+				"esbuild": "^0.14.27",
+				"postcss": "^8.4.13",
+				"resolve": "^1.22.0",
+				"rollup": "^2.59.0"
+			},
+			"bin": {
+				"vite": "bin/vite.js"
+			},
+			"engines": {
+				"node": ">=12.2.0"
+			},
+			"optionalDependencies": {
+				"fsevents": "~2.3.2"
+			},
+			"peerDependencies": {
+				"less": "*",
+				"sass": "*",
+				"stylus": "*"
+			},
+			"peerDependenciesMeta": {
+				"less": {
+					"optional": true
+				},
+				"sass": {
+					"optional": true
+				},
+				"stylus": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/vue": {
+			"version": "3.2.36",
+			"resolved": "https://registry.npmjs.org/vue/-/vue-3.2.36.tgz",
+			"integrity": "sha512-5yTXmrE6gW8IQgttzHW5bfBiFA6mx35ZXHjGLDmKYzW6MMmYvCwuKybANRepwkMYeXw2v1buGg3/lPICY5YlZw==",
+			"dependencies": {
+				"@vue/compiler-dom": "3.2.36",
+				"@vue/compiler-sfc": "3.2.36",
+				"@vue/runtime-dom": "3.2.36",
+				"@vue/server-renderer": "3.2.36",
+				"@vue/shared": "3.2.36"
+			}
+		},
+		"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.0.2",
+			"resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.0.2.tgz",
+			"integrity": "sha512-uCPQwTGjOtAYrwnU+76pYxalhjsh7iFBsHwBqDHiOPTxtICDaraO4Szw54WFTNZTAEsgHHzqFOu1mmnBOBRzDA==",
+			"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.1.10",
+			"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.1.10.tgz",
+			"integrity": "sha512-jpr7gV5KPk4n+sSPdpZT8Qx3XzTcNDWffRlHV/cT2NUyEf+sEgTTmLvnBAibjOFJ0zsUyZlVTAWH5DDnYep+1g==",
+			"dependencies": {
+				"@intlify/core-base": "9.1.10",
+				"@intlify/shared": "9.1.10",
+				"@intlify/vue-devtools": "9.1.10",
+				"@vue/devtools-api": "^6.0.0-beta.7"
+			},
+			"engines": {
+				"node": ">= 10"
+			},
+			"peerDependencies": {
+				"vue": "^3.0.0"
+			}
+		},
+		"node_modules/vue-router": {
+			"version": "4.0.15",
+			"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.0.15.tgz",
+			"integrity": "sha512-xa+pIN9ZqORdIW1MkN2+d9Ui2pCM1b/UMgwYUCZOiFYHAvz/slKKBDha8DLrh5aCG/RibtrpyhKjKOZ85tYyWg==",
+			"dependencies": {
+				"@vue/devtools-api": "^6.0.0"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/posva"
+			},
+			"peerDependencies": {
+				"vue": "^3.2.0"
+			}
+		},
+		"node_modules/watchpack": {
+			"version": "2.3.1",
+			"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.1.tgz",
+			"integrity": "sha512-x0t0JuydIo8qCNctdDrn1OzH/qDzk2+rdCOC3YzumZ42fiMqmQ7T3xQurykYMhYfHaPHTp4ZxAx2NfUo1K6QaA==",
+			"dev": true,
+			"peer": true,
+			"dependencies": {
+				"glob-to-regexp": "^0.4.1",
+				"graceful-fs": "^4.1.2"
+			},
+			"engines": {
+				"node": ">=10.13.0"
+			}
+		},
+		"node_modules/webidl-conversions": {
+			"version": "4.0.2",
+			"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
+			"integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==",
+			"dev": true,
+			"peer": true
+		},
+		"node_modules/webpack": {
+			"version": "5.72.1",
+			"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.72.1.tgz",
+			"integrity": "sha512-dXG5zXCLspQR4krZVR6QgajnZOjW2K/djHvdcRaDQvsjV9z9vaW6+ja5dZOYbqBBjF6kGXka/2ZyxNdc+8Jung==",
+			"dev": true,
+			"peer": true,
+			"dependencies": {
+				"@types/eslint-scope": "^3.7.3",
+				"@types/estree": "^0.0.51",
+				"@webassemblyjs/ast": "1.11.1",
+				"@webassemblyjs/wasm-edit": "1.11.1",
+				"@webassemblyjs/wasm-parser": "1.11.1",
+				"acorn": "^8.4.1",
+				"acorn-import-assertions": "^1.7.6",
+				"browserslist": "^4.14.5",
+				"chrome-trace-event": "^1.0.2",
+				"enhanced-resolve": "^5.9.3",
+				"es-module-lexer": "^0.9.0",
+				"eslint-scope": "5.1.1",
+				"events": "^3.2.0",
+				"glob-to-regexp": "^0.4.1",
+				"graceful-fs": "^4.2.9",
+				"json-parse-even-better-errors": "^2.3.1",
+				"loader-runner": "^4.2.0",
+				"mime-types": "^2.1.27",
+				"neo-async": "^2.6.2",
+				"schema-utils": "^3.1.0",
+				"tapable": "^2.1.1",
+				"terser-webpack-plugin": "^5.1.3",
+				"watchpack": "^2.3.1",
+				"webpack-sources": "^3.2.3"
+			},
+			"bin": {
+				"webpack": "bin/webpack.js"
+			},
+			"engines": {
+				"node": ">=10.13.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/webpack"
+			},
+			"peerDependenciesMeta": {
+				"webpack-cli": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/webpack-sources": {
+			"version": "3.2.3",
+			"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz",
+			"integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==",
+			"dev": true,
+			"peer": true,
+			"engines": {
+				"node": ">=10.13.0"
+			}
+		},
+		"node_modules/whatwg-url": {
+			"version": "7.1.0",
+			"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz",
+			"integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==",
+			"dev": true,
+			"peer": true,
+			"dependencies": {
+				"lodash.sortby": "^4.7.0",
+				"tr46": "^1.0.1",
+				"webidl-conversions": "^4.0.2"
+			}
+		},
+		"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": "sha1-pwIEUwhNjNLv5wup02liY94XEKU="
+		},
+		"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": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+			"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/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/zrender": {
+			"version": "5.3.1",
+			"resolved": "https://registry.npmjs.org/zrender/-/zrender-5.3.1.tgz",
+			"integrity": "sha512-7olqIjy0gWfznKr6vgfnGBk7y4UtdMvdwFmK92vVQsQeDPyzkHW1OlrLEKg6GHz1W5ePf0FeN1q2vkl/HFqhXw==",
+			"dependencies": {
+				"tslib": "2.3.0"
+			}
+		}
+	},
+	"dependencies": {
+		"@babel/parser": {
+			"version": "7.18.0",
+			"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.0.tgz",
+			"integrity": "sha512-AqDccGC+m5O/iUStSJy3DGRIUFu7WbY/CppZYwrEUB4N0tZlnI8CSTsgL7v5fHVFmUbRv2sd+yy27o8Ydt4MGg=="
+		},
+		"@babel/runtime": {
+			"version": "7.18.0",
+			"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.0.tgz",
+			"integrity": "sha512-YMQvx/6nKEaucl0MY56mwIG483xk8SDNdlUwb2Ts6FUpr7fm85DxEmsY18LXBNhcTz6tO6JwZV8w1W06v8UKeg==",
+			"requires": {
+				"regenerator-runtime": "^0.13.4"
+			}
+		},
+		"@ctrl/tinycolor": {
+			"version": "3.4.1",
+			"resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.4.1.tgz",
+			"integrity": "sha512-ej5oVy6lykXsvieQtqZxCOaLT+xD4+QNarq78cIYISHmZXshCvROLudpQN3lfL8G0NL7plMSSK+zlyvCaIJ4Iw=="
+		},
+		"@element-plus/icons-vue": {
+			"version": "2.0.3",
+			"resolved": "https://registry.npmjs.org/@element-plus/icons-vue/-/icons-vue-2.0.3.tgz",
+			"integrity": "sha512-dI9hazWIJF5AXsFDWLsdGqVIQMJ5Kq70fw1RScuMW6+UNqfJpRYFOqhya8RHpjajIZZnQx260Ll9AjTcu2HSOA==",
+			"requires": {}
+		},
+		"@eslint/eslintrc": {
+			"version": "1.3.0",
+			"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz",
+			"integrity": "sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==",
+			"dev": true,
+			"requires": {
+				"ajv": "^6.12.4",
+				"debug": "^4.3.2",
+				"espree": "^9.3.2",
+				"globals": "^13.15.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": "0.7.1",
+			"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-0.7.1.tgz",
+			"integrity": "sha512-grcqEmI8DTIolufpxhJagVeJmvloxBXE6xxSrVnSXz/Wz1uUIsC85ad+UNBqAoBOvzLxE42wvDj3YkmSGqWRxA=="
+		},
+		"@floating-ui/dom": {
+			"version": "0.5.1",
+			"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-0.5.1.tgz",
+			"integrity": "sha512-dkPSy5JPiQEtljc3VpG9lauYctxfLlqj/8N9f+lmsR92gQaSVMAWuBbFBH2keY5DmdQn3p4Dv1dQd+e8osH+/g==",
+			"requires": {
+				"@floating-ui/core": "^0.7.1"
+			}
+		},
+		"@humanwhocodes/config-array": {
+			"version": "0.9.5",
+			"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz",
+			"integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==",
+			"dev": true,
+			"requires": {
+				"@humanwhocodes/object-schema": "^1.2.1",
+				"debug": "^4.1.1",
+				"minimatch": "^3.0.4"
+			}
+		},
+		"@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
+		},
+		"@interactjs/actions": {
+			"version": "1.10.11",
+			"resolved": "https://registry.npmjs.org/@interactjs/actions/-/actions-1.10.11.tgz",
+			"integrity": "sha512-P39zeefr4hkmKx+5nZ+mrH1s0l2YJ3gIHrthXmE81n6MlMa42m0WtHcTms4C5JTTNBP2EEDY+KGgGxSnmJKvUw==",
+			"requires": {
+				"@interactjs/interact": "1.10.11"
+			}
+		},
+		"@interactjs/auto-scroll": {
+			"version": "1.10.11",
+			"resolved": "https://registry.npmjs.org/@interactjs/auto-scroll/-/auto-scroll-1.10.11.tgz",
+			"integrity": "sha512-feHNjhi0EMNLV2nQcEgjYPz2mI54aeSW2RiaoNtFLyBvtXKp0b4DmluwDv6DvuXmUpDwD5g/Hk1gGM2rgl7iqQ==",
+			"requires": {
+				"@interactjs/interact": "1.10.11"
+			}
+		},
+		"@interactjs/auto-start": {
+			"version": "1.10.11",
+			"resolved": "https://registry.npmjs.org/@interactjs/auto-start/-/auto-start-1.10.11.tgz",
+			"integrity": "sha512-cIg5CcalCPtC6AiGq6j/0hKUtL2MweEpvw12FuB19sz2Q9Dye0J4GliHKhOYvtumNinnvfVAZ4FZMqZEuX7YZA==",
+			"requires": {
+				"@interactjs/interact": "1.10.11"
+			}
+		},
+		"@interactjs/core": {
+			"version": "1.10.11",
+			"resolved": "https://registry.npmjs.org/@interactjs/core/-/core-1.10.11.tgz",
+			"integrity": "sha512-aJ50ccVeszpJt7wPH7Yfqm7f1aG1SA94qd90P0NaESh5/QUXn4CESO6igobo4DFHQ5z+1Rfdl8aphP4JxlH4gw==",
+			"requires": {}
+		},
+		"@interactjs/dev-tools": {
+			"version": "1.10.11",
+			"resolved": "https://registry.npmjs.org/@interactjs/dev-tools/-/dev-tools-1.10.11.tgz",
+			"integrity": "sha512-BP2FNfMbF7zLuOAUGMkDhCo1e1B0fnqyb9ih/Y8yAIJuoLrZxP/9htbsS1vZOIVZ4UgtrId4cYOwfcAZBMQtmw==",
+			"requires": {
+				"@interactjs/interact": "1.10.11"
+			}
+		},
+		"@interactjs/inertia": {
+			"version": "1.10.11",
+			"resolved": "https://registry.npmjs.org/@interactjs/inertia/-/inertia-1.10.11.tgz",
+			"integrity": "sha512-h+sknCzRqBSyHy4ctPNsq56mxkAMMdwHWD6en7rDEw899gdGKYaXVDVdv1jMfiwNRw0eRFBNoCiol8r3a/a3Jw==",
+			"requires": {
+				"@interactjs/interact": "1.10.11",
+				"@interactjs/offset": "1.10.11"
+			}
+		},
+		"@interactjs/interact": {
+			"version": "1.10.11",
+			"resolved": "https://registry.npmjs.org/@interactjs/interact/-/interact-1.10.11.tgz",
+			"integrity": "sha512-0iZJ9l547JuBA/lKxK4ARGYVmMqRSsAdA8gXL1zWe51qEIQq8PyWmMipoi8JbDaL7exC2THKwkXu5uq5ndT+iA==",
+			"requires": {
+				"@interactjs/core": "1.10.11",
+				"@interactjs/types": "1.10.11",
+				"@interactjs/utils": "1.10.11"
+			}
+		},
+		"@interactjs/interactjs": {
+			"version": "1.10.11",
+			"resolved": "https://registry.npmjs.org/@interactjs/interactjs/-/interactjs-1.10.11.tgz",
+			"integrity": "sha512-cGOxf6rp3Y8/sk88LhIT0XDn4gCiCzAnUG5Kkj9SAqiUO6BK/9+Wbp1IBkNaPgl/8uG8gNHh/dXBrlBBNcqJAg==",
+			"requires": {
+				"@interactjs/actions": "1.10.11",
+				"@interactjs/auto-scroll": "1.10.11",
+				"@interactjs/auto-start": "1.10.11",
+				"@interactjs/core": "1.10.11",
+				"@interactjs/dev-tools": "1.10.11",
+				"@interactjs/inertia": "1.10.11",
+				"@interactjs/interact": "1.10.11",
+				"@interactjs/modifiers": "1.10.11",
+				"@interactjs/offset": "1.10.11",
+				"@interactjs/pointer-events": "1.10.11",
+				"@interactjs/reflow": "1.10.11",
+				"@interactjs/utils": "1.10.11"
+			}
+		},
+		"@interactjs/modifiers": {
+			"version": "1.10.11",
+			"resolved": "https://registry.npmjs.org/@interactjs/modifiers/-/modifiers-1.10.11.tgz",
+			"integrity": "sha512-ltqX1RSqeAIikixlQBlyEUdclT5+rbfIGi3sIdLLYaIZQnltYkWqL9MHKx/w5b+hV+Mc0p5MLUFWJbTdkSCZ9g==",
+			"requires": {
+				"@interactjs/interact": "1.10.11",
+				"@interactjs/snappers": "1.10.11"
+			}
+		},
+		"@interactjs/offset": {
+			"version": "1.10.11",
+			"resolved": "https://registry.npmjs.org/@interactjs/offset/-/offset-1.10.11.tgz",
+			"integrity": "sha512-mBT7eIfy5ivofECiv+VwtEwwIMLV54fT9ujSMWJPduxdSYIHepUWgEf/3zjJknFh6jQc7pqz9dtjvVvyzRCLlQ==",
+			"requires": {
+				"@interactjs/interact": "1.10.11"
+			}
+		},
+		"@interactjs/pointer-events": {
+			"version": "1.10.11",
+			"resolved": "https://registry.npmjs.org/@interactjs/pointer-events/-/pointer-events-1.10.11.tgz",
+			"integrity": "sha512-yBT8JJVMZ+MgBay5l1WAHnL8ch/mZsRfaFahti+QFYeQyRloDtsWmEMDSYI/Onyy9+hS3gN/ge77ArGciZZ0Ow==",
+			"requires": {
+				"@interactjs/interact": "1.10.11"
+			}
+		},
+		"@interactjs/reflow": {
+			"version": "1.10.11",
+			"resolved": "https://registry.npmjs.org/@interactjs/reflow/-/reflow-1.10.11.tgz",
+			"integrity": "sha512-NSCtcCkjImOYSbxzzv2kFqR9t49J8KlhEr9UoePc7GyLbNXsiv3WQ3n0ehZd7CgZXQDiVXnP2UnmIOv5Zd4HQg==",
+			"requires": {
+				"@interactjs/interact": "1.10.11"
+			}
+		},
+		"@interactjs/snappers": {
+			"version": "1.10.11",
+			"resolved": "https://registry.npmjs.org/@interactjs/snappers/-/snappers-1.10.11.tgz",
+			"integrity": "sha512-yYtOMUZ7aFUZ1IYheq9Tj5hZ4J1r5dnaXhLF44WsI/awQ5L0DjZf07GPWof0B+7rZHEVudxyQNbPfFmb+1K94Q==",
+			"requires": {
+				"@interactjs/interact": "1.10.11"
+			}
+		},
+		"@interactjs/types": {
+			"version": "1.10.11",
+			"resolved": "https://registry.npmjs.org/@interactjs/types/-/types-1.10.11.tgz",
+			"integrity": "sha512-YRsVFWjL8Gkkvlx3qnjeaxW4fnibSJ9791g8BA7Pv5ANByI64WmtR1vU7A2rXcrOn8XvyCEfY0ss1s8NhZP+MA=="
+		},
+		"@interactjs/utils": {
+			"version": "1.10.11",
+			"resolved": "https://registry.npmjs.org/@interactjs/utils/-/utils-1.10.11.tgz",
+			"integrity": "sha512-410ZoxKF+r1roeSelL+WHXfdryUMg5iykC1XwQ3l6XqNw43IMACzyvTH6k6Pwxj7w7x42nce0Qdn1GQ3Y8xyCw=="
+		},
+		"@intlify/core-base": {
+			"version": "9.1.10",
+			"resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.1.10.tgz",
+			"integrity": "sha512-So9CNUavB/IsZ+zBmk2Cv6McQp6vc2wbGi1S0XQmJ8Vz+UFcNn9MFXAe9gY67PreIHrbLsLxDD0cwo1qsxM1Nw==",
+			"requires": {
+				"@intlify/devtools-if": "9.1.10",
+				"@intlify/message-compiler": "9.1.10",
+				"@intlify/message-resolver": "9.1.10",
+				"@intlify/runtime": "9.1.10",
+				"@intlify/shared": "9.1.10",
+				"@intlify/vue-devtools": "9.1.10"
+			}
+		},
+		"@intlify/devtools-if": {
+			"version": "9.1.10",
+			"resolved": "https://registry.npmjs.org/@intlify/devtools-if/-/devtools-if-9.1.10.tgz",
+			"integrity": "sha512-SHaKoYu6sog3+Q8js1y3oXLywuogbH1sKuc7NSYkN3GElvXSBaMoCzW+we0ZSFqj/6c7vTNLg9nQ6rxhKqYwnQ==",
+			"requires": {
+				"@intlify/shared": "9.1.10"
+			}
+		},
+		"@intlify/message-compiler": {
+			"version": "9.1.10",
+			"resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.1.10.tgz",
+			"integrity": "sha512-+JiJpXff/XTb0EadYwdxOyRTB0hXNd4n1HaJ/a4yuV960uRmPXaklJsedW0LNdcptd/hYUZtCkI7Lc9J5C1gxg==",
+			"requires": {
+				"@intlify/message-resolver": "9.1.10",
+				"@intlify/shared": "9.1.10",
+				"source-map": "0.6.1"
+			}
+		},
+		"@intlify/message-resolver": {
+			"version": "9.1.10",
+			"resolved": "https://registry.npmjs.org/@intlify/message-resolver/-/message-resolver-9.1.10.tgz",
+			"integrity": "sha512-5YixMG/M05m0cn9+gOzd4EZQTFRUu8RGhzxJbR1DWN21x/Z3bJ8QpDYj6hC4FwBj5uKsRfKpJQ3Xqg98KWoA+w=="
+		},
+		"@intlify/runtime": {
+			"version": "9.1.10",
+			"resolved": "https://registry.npmjs.org/@intlify/runtime/-/runtime-9.1.10.tgz",
+			"integrity": "sha512-7QsuByNzpe3Gfmhwq6hzgXcMPpxz8Zxb/XFI6s9lQdPLPe5Lgw4U1ovRPZTOs6Y2hwitR3j/HD8BJNGWpJnOFA==",
+			"requires": {
+				"@intlify/message-compiler": "9.1.10",
+				"@intlify/message-resolver": "9.1.10",
+				"@intlify/shared": "9.1.10"
+			}
+		},
+		"@intlify/shared": {
+			"version": "9.1.10",
+			"resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.1.10.tgz",
+			"integrity": "sha512-Om54xJeo1Vw+K1+wHYyXngE8cAbrxZHpWjYzMR9wCkqbhGtRV5VLhVc214Ze2YatPrWlS2WSMOWXR8JktX/IgA=="
+		},
+		"@intlify/vue-devtools": {
+			"version": "9.1.10",
+			"resolved": "https://registry.npmjs.org/@intlify/vue-devtools/-/vue-devtools-9.1.10.tgz",
+			"integrity": "sha512-5l3qYARVbkWAkagLu1XbDUWRJSL8br1Dj60wgMaKB0+HswVsrR6LloYZTg7ozyvM621V6+zsmwzbQxbVQyrytQ==",
+			"requires": {
+				"@intlify/message-resolver": "9.1.10",
+				"@intlify/runtime": "9.1.10",
+				"@intlify/shared": "9.1.10"
+			}
+		},
+		"@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/eslint": {
+			"version": "8.4.2",
+			"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.2.tgz",
+			"integrity": "sha512-Z1nseZON+GEnFjJc04sv4NSALGjhFwy6K0HXt7qsn5ArfAKtb63dXNJHf+1YW6IpOIYRBGUbu3GwJdj8DGnCjA==",
+			"dev": true,
+			"peer": true,
+			"requires": {
+				"@types/estree": "*",
+				"@types/json-schema": "*"
+			}
+		},
+		"@types/eslint-scope": {
+			"version": "3.7.3",
+			"resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.3.tgz",
+			"integrity": "sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g==",
+			"dev": true,
+			"peer": true,
+			"requires": {
+				"@types/eslint": "*",
+				"@types/estree": "*"
+			}
+		},
+		"@types/estree": {
+			"version": "0.0.51",
+			"resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz",
+			"integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==",
+			"dev": true,
+			"peer": true
+		},
+		"@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.182",
+			"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.182.tgz",
+			"integrity": "sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q=="
+		},
+		"@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": "17.0.39",
+			"resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.39.tgz",
+			"integrity": "sha512-JDU3YLlnPK3WDao6/DlXLOgSNpG13ct+CwIO17V8q0/9fWJyeMJJ/VyZ1lv8kDprihvZMydzVwf0tQOqGiY2Nw==",
+			"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/sortablejs": {
+			"version": "1.13.0",
+			"resolved": "https://registry.npmjs.org/@types/sortablejs/-/sortablejs-1.13.0.tgz",
+			"integrity": "sha512-C3064MH72iEfeGCYEGCt7FCxXoAXaMPG0QPnstcxvPmbl54erpISu06d++FY37Smja64iWy5L8wOyHHBghWbJQ==",
+			"dev": true
+		},
+		"@typescript-eslint/eslint-plugin": {
+			"version": "5.27.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.27.0.tgz",
+			"integrity": "sha512-DDrIA7GXtmHXr1VCcx9HivA39eprYBIFxbQEHI6NyraRDxCGpxAFiYQAT/1Y0vh1C+o2vfBiy4IuPoXxtTZCAQ==",
+			"dev": true,
+			"requires": {
+				"@typescript-eslint/scope-manager": "5.27.0",
+				"@typescript-eslint/type-utils": "5.27.0",
+				"@typescript-eslint/utils": "5.27.0",
+				"debug": "^4.3.4",
+				"functional-red-black-tree": "^1.0.1",
+				"ignore": "^5.2.0",
+				"regexpp": "^3.2.0",
+				"semver": "^7.3.7",
+				"tsutils": "^3.21.0"
+			}
+		},
+		"@typescript-eslint/parser": {
+			"version": "5.27.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.27.0.tgz",
+			"integrity": "sha512-8oGjQF46c52l7fMiPPvX4It3u3V3JipssqDfHQ2hcR0AeR8Zge+OYyKUCm5b70X72N1qXt0qgHenwN6Gc2SXZA==",
+			"dev": true,
+			"requires": {
+				"@typescript-eslint/scope-manager": "5.27.0",
+				"@typescript-eslint/types": "5.27.0",
+				"@typescript-eslint/typescript-estree": "5.27.0",
+				"debug": "^4.3.4"
+			}
+		},
+		"@typescript-eslint/scope-manager": {
+			"version": "5.27.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.27.0.tgz",
+			"integrity": "sha512-VnykheBQ/sHd1Vt0LJ1JLrMH1GzHO+SzX6VTXuStISIsvRiurue/eRkTqSrG0CexHQgKG8shyJfR4o5VYioB9g==",
+			"dev": true,
+			"requires": {
+				"@typescript-eslint/types": "5.27.0",
+				"@typescript-eslint/visitor-keys": "5.27.0"
+			}
+		},
+		"@typescript-eslint/type-utils": {
+			"version": "5.27.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.27.0.tgz",
+			"integrity": "sha512-vpTvRRchaf628Hb/Xzfek+85o//zEUotr1SmexKvTfs7czXfYjXVT/a5yDbpzLBX1rhbqxjDdr1Gyo0x1Fc64g==",
+			"dev": true,
+			"requires": {
+				"@typescript-eslint/utils": "5.27.0",
+				"debug": "^4.3.4",
+				"tsutils": "^3.21.0"
+			}
+		},
+		"@typescript-eslint/types": {
+			"version": "5.27.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.27.0.tgz",
+			"integrity": "sha512-lY6C7oGm9a/GWhmUDOs3xAVRz4ty/XKlQ2fOLr8GAIryGn0+UBOoJDWyHer3UgrHkenorwvBnphhP+zPmzmw0A==",
+			"dev": true
+		},
+		"@typescript-eslint/typescript-estree": {
+			"version": "5.27.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.27.0.tgz",
+			"integrity": "sha512-QywPMFvgZ+MHSLRofLI7BDL+UczFFHyj0vF5ibeChDAJgdTV8k4xgEwF0geFhVlPc1p8r70eYewzpo6ps+9LJQ==",
+			"dev": true,
+			"requires": {
+				"@typescript-eslint/types": "5.27.0",
+				"@typescript-eslint/visitor-keys": "5.27.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.27.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.27.0.tgz",
+			"integrity": "sha512-nZvCrkIJppym7cIbP3pOwIkAefXOmfGPnCM0LQfzNaKxJHI6VjI8NC662uoiPlaf5f6ymkTy9C3NQXev2mdXmA==",
+			"dev": true,
+			"requires": {
+				"@types/json-schema": "^7.0.9",
+				"@typescript-eslint/scope-manager": "5.27.0",
+				"@typescript-eslint/types": "5.27.0",
+				"@typescript-eslint/typescript-estree": "5.27.0",
+				"eslint-scope": "^5.1.1",
+				"eslint-utils": "^3.0.0"
+			}
+		},
+		"@typescript-eslint/visitor-keys": {
+			"version": "5.27.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.27.0.tgz",
+			"integrity": "sha512-46cYrteA2MrIAjv9ai44OQDUoCZyHeGIc4lsjCUX2WT6r4C+kidz1bNiR4017wHOPUythYeH+Sc7/cFP97KEAA==",
+			"dev": true,
+			"requires": {
+				"@typescript-eslint/types": "5.27.0",
+				"eslint-visitor-keys": "^3.3.0"
+			}
+		},
+		"@uppy/companion-client": {
+			"version": "2.1.0",
+			"resolved": "https://registry.npmjs.org/@uppy/companion-client/-/companion-client-2.1.0.tgz",
+			"integrity": "sha512-1Zsag2z9kygXuVbUxqHhkTJUmEHwbejjyf7vmm+P/AiVgK3O37JINYBGOpdTJNgbC9UydLBjleXo8peDVgpg8Q==",
+			"requires": {
+				"@uppy/utils": "^4.0.7",
+				"namespace-emitter": "^2.0.1"
+			}
+		},
+		"@uppy/core": {
+			"version": "2.2.0",
+			"resolved": "https://registry.npmjs.org/@uppy/core/-/core-2.2.0.tgz",
+			"integrity": "sha512-qdDoNCjrVjjOmFCFCxc+HEbtbQ9K0k6LKNbZZwWK7d4Cx3xEa6VsxmqVhfFL6ekH2gyboqYV8Z5IbRkJT/0Nqg==",
+			"requires": {
+				"@transloadit/prettier-bytes": "0.0.7",
+				"@uppy/store-default": "^2.0.3",
+				"@uppy/utils": "^4.0.7",
+				"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.0.3",
+			"resolved": "https://registry.npmjs.org/@uppy/store-default/-/store-default-2.0.3.tgz",
+			"integrity": "sha512-2BGlN1sW0cFv4rOqTK8dfSg579S984N1HxCJxLFqeW9nWD6zd/O8Omyd85tbxGQ+FLZLTmLOm/feD0YeCBMahg=="
+		},
+		"@uppy/utils": {
+			"version": "4.0.7",
+			"resolved": "https://registry.npmjs.org/@uppy/utils/-/utils-4.0.7.tgz",
+			"integrity": "sha512-nKViMT8XchKy+NWpb3DtVKuzZBmW7au26LrMq89EsvTwIOT6UR9+7bmz/+zr3+lc7UC7vMgNChIC6G+/Ya9wWQ==",
+			"requires": {
+				"lodash.throttle": "^4.1.1"
+			}
+		},
+		"@uppy/xhr-upload": {
+			"version": "2.1.0",
+			"resolved": "https://registry.npmjs.org/@uppy/xhr-upload/-/xhr-upload-2.1.0.tgz",
+			"integrity": "sha512-io1uNu7lGkhIkMnt13bu3FYSAdRbBRWl8n/6njYi+727Jyr0XhKfmBYV9OiruFSxLz5Bfxkw2gTs6e0qUb63nA==",
+			"requires": {
+				"@uppy/companion-client": "^2.1.0",
+				"@uppy/utils": "^4.0.7",
+				"nanoid": "^3.1.25"
+			}
+		},
+		"@vitejs/plugin-vue": {
+			"version": "2.3.3",
+			"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-2.3.3.tgz",
+			"integrity": "sha512-SmQLDyhz+6lGJhPELsBdzXGc+AcaT8stgkbiTFGpXPe8Tl1tJaBw1A6pxDqDuRsVkD8uscrkx3hA7QDOoKYtyw==",
+			"dev": true,
+			"requires": {}
+		},
+		"@vue/compiler-core": {
+			"version": "3.2.36",
+			"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.36.tgz",
+			"integrity": "sha512-bbyZM5hvBicv0PW3KUfVi+x3ylHnfKG7DOn5wM+f2OztTzTjLEyBb/5yrarIYpmnGitVGbjZqDbODyW4iK8hqw==",
+			"requires": {
+				"@babel/parser": "^7.16.4",
+				"@vue/shared": "3.2.36",
+				"estree-walker": "^2.0.2",
+				"source-map": "^0.6.1"
+			}
+		},
+		"@vue/compiler-dom": {
+			"version": "3.2.36",
+			"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.36.tgz",
+			"integrity": "sha512-tcOTAOiW4s24QLnq+ON6J+GRONXJ+A/mqKCORi0LSlIh8XQlNnlm24y8xIL8la+ZDgkdbjarQ9ZqYSvEja6gVA==",
+			"requires": {
+				"@vue/compiler-core": "3.2.36",
+				"@vue/shared": "3.2.36"
+			}
+		},
+		"@vue/compiler-sfc": {
+			"version": "3.2.36",
+			"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.36.tgz",
+			"integrity": "sha512-AvGb4bTj4W8uQ4BqaSxo7UwTEqX5utdRSMyHy58OragWlt8nEACQ9mIeQh3K4di4/SX+41+pJrLIY01lHAOFOA==",
+			"requires": {
+				"@babel/parser": "^7.16.4",
+				"@vue/compiler-core": "3.2.36",
+				"@vue/compiler-dom": "3.2.36",
+				"@vue/compiler-ssr": "3.2.36",
+				"@vue/reactivity-transform": "3.2.36",
+				"@vue/shared": "3.2.36",
+				"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.36",
+			"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.36.tgz",
+			"integrity": "sha512-+KugInUFRvOxEdLkZwE+W43BqHyhBh0jpYXhmqw1xGq2dmE6J9eZ8UUSOKNhdHtQ/iNLWWeK/wPZkVLUf3YGaw==",
+			"requires": {
+				"@vue/compiler-dom": "3.2.36",
+				"@vue/shared": "3.2.36"
+			}
+		},
+		"@vue/devtools-api": {
+			"version": "6.1.4",
+			"resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.1.4.tgz",
+			"integrity": "sha512-IiA0SvDrJEgXvVxjNkHPFfDx6SXw0b/TUkqMcDZWNg9fnCAHbTpoo59YfJ9QLFkwa3raau5vSlRVzMSLDnfdtQ=="
+		},
+		"@vue/reactivity": {
+			"version": "3.2.36",
+			"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.36.tgz",
+			"integrity": "sha512-c2qvopo0crh9A4GXi2/2kfGYMxsJW4tVILrqRPydVGZHhq0fnzy6qmclWOhBFckEhmyxmpHpdJtIRYGeKcuhnA==",
+			"requires": {
+				"@vue/shared": "3.2.36"
+			}
+		},
+		"@vue/reactivity-transform": {
+			"version": "3.2.36",
+			"resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.36.tgz",
+			"integrity": "sha512-Jk5o2BhpODC9XTA7o4EL8hSJ4JyrFWErLtClG3NH8wDS7ri9jBDWxI7/549T7JY9uilKsaNM+4pJASLj5dtRwA==",
+			"requires": {
+				"@babel/parser": "^7.16.4",
+				"@vue/compiler-core": "3.2.36",
+				"@vue/shared": "3.2.36",
+				"estree-walker": "^2.0.2",
+				"magic-string": "^0.25.7"
+			}
+		},
+		"@vue/runtime-core": {
+			"version": "3.2.36",
+			"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.36.tgz",
+			"integrity": "sha512-PTWBD+Lub+1U3/KhbCExrfxyS14hstLX+cBboxVHaz+kXoiDLNDEYAovPtxeTutbqtClIXtft+wcGdC+FUQ9qQ==",
+			"requires": {
+				"@vue/reactivity": "3.2.36",
+				"@vue/shared": "3.2.36"
+			}
+		},
+		"@vue/runtime-dom": {
+			"version": "3.2.36",
+			"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.36.tgz",
+			"integrity": "sha512-gYPYblm7QXHVuBohqNRRT7Wez0f2Mx2D40rb4fleehrJU9CnkjG0phhcGEZFfGwCmHZRqBCRgbFWE98bPULqkg==",
+			"requires": {
+				"@vue/runtime-core": "3.2.36",
+				"@vue/shared": "3.2.36",
+				"csstype": "^2.6.8"
+			}
+		},
+		"@vue/server-renderer": {
+			"version": "3.2.36",
+			"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.36.tgz",
+			"integrity": "sha512-uZE0+jfye6yYXWvAQYeHZv+f50sRryvy16uiqzk3jn8hEY8zTjI+rzlmZSGoE915k+W/Ol9XSw6vxOUD8dGkUg==",
+			"requires": {
+				"@vue/compiler-ssr": "3.2.36",
+				"@vue/shared": "3.2.36"
+			}
+		},
+		"@vue/shared": {
+			"version": "3.2.36",
+			"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.36.tgz",
+			"integrity": "sha512-JtB41wXl7Au3+Nl3gD16Cfpj7k/6aCroZ6BbOiCMFCMvrOpkg/qQUXTso2XowaNqBbnkuGHurLAqkLBxNGc1hQ=="
+		},
+		"@vueuse/core": {
+			"version": "8.5.0",
+			"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-8.5.0.tgz",
+			"integrity": "sha512-VEJ6sGNsPlUp0o9BGda2YISvDZbhWJSOJu5zlp2TufRGVrLcYUKr31jyFEOj6RXzG3k/H4aCYeZyjpItfU8glw==",
+			"requires": {
+				"@vueuse/metadata": "8.5.0",
+				"@vueuse/shared": "8.5.0",
+				"vue-demi": "*"
+			},
+			"dependencies": {
+				"@vueuse/shared": {
+					"version": "8.5.0",
+					"resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-8.5.0.tgz",
+					"integrity": "sha512-qKG+SZb44VvGD4dU5cQ63z4JE2Yk39hQUecR0a9sEdJA01cx+XrxAvFKJfPooxwoiqalAVw/ktWK6xbyc/jS3g==",
+					"requires": {
+						"vue-demi": "*"
+					}
+				},
+				"vue-demi": {
+					"version": "0.12.5",
+					"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.12.5.tgz",
+					"integrity": "sha512-BREuTgTYlUr0zw0EZn3hnhC3I6gPWv+Kwh4MCih6QcAeaTlaIX0DwOVN0wHej7hSvDPecz4jygy/idsgKfW58Q==",
+					"requires": {}
+				}
+			}
+		},
+		"@vueuse/metadata": {
+			"version": "8.5.0",
+			"resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-8.5.0.tgz",
+			"integrity": "sha512-WxsD+Cd+bn+HcjpY6Dl9FJ8ywTRTT9pTwk3bCQpzEhXVYAyNczKDSahk50fCfIJKeWHhyI4B2+/ZEOxQAkUr0g=="
+		},
+		"@wangeditor/basic-modules": {
+			"version": "1.1.1",
+			"resolved": "https://registry.npmjs.org/@wangeditor/basic-modules/-/basic-modules-1.1.1.tgz",
+			"integrity": "sha512-tQl2Pw8M2g3CM+ESx2phzr9zSKeuFCM1AMBoPdnlbatU7Dnae0CsEB/b3C+gI0dIQzM2jh34yTmqgbbhrwuRLg==",
+			"requires": {
+				"is-url": "^1.2.4"
+			}
+		},
+		"@wangeditor/code-highlight": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/@wangeditor/code-highlight/-/code-highlight-1.0.2.tgz",
+			"integrity": "sha512-SCtOcUxjKqIso/LSxGSOaYr3G6MC2En0gNTyHIMCG928T0fo0ufaqp/vIXKQzVL2Y+X/CSAOB2EbrFlgGvr0AQ==",
+			"requires": {
+				"prismjs": "^1.23.0"
+			}
+		},
+		"@wangeditor/core": {
+			"version": "1.1.1",
+			"resolved": "https://registry.npmjs.org/@wangeditor/core/-/core-1.1.1.tgz",
+			"integrity": "sha512-SrbvOGlONMNMOeFIJI7fC9x0/6T6LvQHTITPCqjgbCm2QF+POcrHzRKGQOqKCsyKi9UJz9hLsjsvJnvP10rxjQ==",
+			"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.1",
+			"resolved": "https://registry.npmjs.org/@wangeditor/editor/-/editor-5.1.1.tgz",
+			"integrity": "sha512-BtccuHFm0QvYunIhIu7tllQWkwppkmEkD3OJ5Mn+F0REPQ/Z3HiEXbtlss2t9c/kHO4CtiFwv2XD/k/VEg7taA==",
+			"requires": {
+				"@uppy/core": "^2.1.1",
+				"@uppy/xhr-upload": "^2.0.3",
+				"@wangeditor/basic-modules": "^1.1.1",
+				"@wangeditor/code-highlight": "^1.0.2",
+				"@wangeditor/core": "^1.1.1",
+				"@wangeditor/list-module": "^1.0.2",
+				"@wangeditor/table-module": "^1.1.0",
+				"@wangeditor/upload-image-module": "^1.0.1",
+				"@wangeditor/video-module": "^1.1.0",
+				"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/list-module": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/@wangeditor/list-module/-/list-module-1.0.2.tgz",
+			"integrity": "sha512-VfENZEFvsLTiLxN/cj8cibFGy9NVV+/cfATTiLiH9ef+8lgKv8apttXYVlqIAfnlJLLuCk0cIm8c/zH+hbtrZg==",
+			"requires": {}
+		},
+		"@wangeditor/table-module": {
+			"version": "1.1.0",
+			"resolved": "https://registry.npmjs.org/@wangeditor/table-module/-/table-module-1.1.0.tgz",
+			"integrity": "sha512-QpjCXSzsXcsR0pEI5Pu28e8aYh9+lHcVV4TTmGV6lRGE/etQF3PHUZNGUlfhkCgmGPq+E7n/Whb4RpAM3PJVhw==",
+			"requires": {}
+		},
+		"@wangeditor/upload-image-module": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/@wangeditor/upload-image-module/-/upload-image-module-1.0.1.tgz",
+			"integrity": "sha512-vgUV4ENttTITblqtVuzleIq732OmzmzzgrIvX6b3GRGPSw5u8glJ/87tOEhvHjHECc4oFo18B7xzJ1GpBj79/w==",
+			"requires": {}
+		},
+		"@wangeditor/video-module": {
+			"version": "1.1.0",
+			"resolved": "https://registry.npmjs.org/@wangeditor/video-module/-/video-module-1.1.0.tgz",
+			"integrity": "sha512-VR6x7Vk9ebvXtxCPwobiNiTGZGgqEzCVc6ViWlNH3v4jlDIeo/s7N7OCgpvELR7X/X7GHecBu7wySDkHIskB5w==",
+			"requires": {}
+		},
+		"@webassemblyjs/ast": {
+			"version": "1.11.1",
+			"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz",
+			"integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==",
+			"dev": true,
+			"peer": true,
+			"requires": {
+				"@webassemblyjs/helper-numbers": "1.11.1",
+				"@webassemblyjs/helper-wasm-bytecode": "1.11.1"
+			}
+		},
+		"@webassemblyjs/floating-point-hex-parser": {
+			"version": "1.11.1",
+			"resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz",
+			"integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==",
+			"dev": true,
+			"peer": true
+		},
+		"@webassemblyjs/helper-api-error": {
+			"version": "1.11.1",
+			"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz",
+			"integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==",
+			"dev": true,
+			"peer": true
+		},
+		"@webassemblyjs/helper-buffer": {
+			"version": "1.11.1",
+			"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz",
+			"integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==",
+			"dev": true,
+			"peer": true
+		},
+		"@webassemblyjs/helper-numbers": {
+			"version": "1.11.1",
+			"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz",
+			"integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==",
+			"dev": true,
+			"peer": true,
+			"requires": {
+				"@webassemblyjs/floating-point-hex-parser": "1.11.1",
+				"@webassemblyjs/helper-api-error": "1.11.1",
+				"@xtuc/long": "4.2.2"
+			}
+		},
+		"@webassemblyjs/helper-wasm-bytecode": {
+			"version": "1.11.1",
+			"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz",
+			"integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==",
+			"dev": true,
+			"peer": true
+		},
+		"@webassemblyjs/helper-wasm-section": {
+			"version": "1.11.1",
+			"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz",
+			"integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==",
+			"dev": true,
+			"peer": true,
+			"requires": {
+				"@webassemblyjs/ast": "1.11.1",
+				"@webassemblyjs/helper-buffer": "1.11.1",
+				"@webassemblyjs/helper-wasm-bytecode": "1.11.1",
+				"@webassemblyjs/wasm-gen": "1.11.1"
+			}
+		},
+		"@webassemblyjs/ieee754": {
+			"version": "1.11.1",
+			"resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz",
+			"integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==",
+			"dev": true,
+			"peer": true,
+			"requires": {
+				"@xtuc/ieee754": "^1.2.0"
+			}
+		},
+		"@webassemblyjs/leb128": {
+			"version": "1.11.1",
+			"resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz",
+			"integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==",
+			"dev": true,
+			"peer": true,
+			"requires": {
+				"@xtuc/long": "4.2.2"
+			}
+		},
+		"@webassemblyjs/utf8": {
+			"version": "1.11.1",
+			"resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz",
+			"integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==",
+			"dev": true,
+			"peer": true
+		},
+		"@webassemblyjs/wasm-edit": {
+			"version": "1.11.1",
+			"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz",
+			"integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==",
+			"dev": true,
+			"peer": true,
+			"requires": {
+				"@webassemblyjs/ast": "1.11.1",
+				"@webassemblyjs/helper-buffer": "1.11.1",
+				"@webassemblyjs/helper-wasm-bytecode": "1.11.1",
+				"@webassemblyjs/helper-wasm-section": "1.11.1",
+				"@webassemblyjs/wasm-gen": "1.11.1",
+				"@webassemblyjs/wasm-opt": "1.11.1",
+				"@webassemblyjs/wasm-parser": "1.11.1",
+				"@webassemblyjs/wast-printer": "1.11.1"
+			}
+		},
+		"@webassemblyjs/wasm-gen": {
+			"version": "1.11.1",
+			"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz",
+			"integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==",
+			"dev": true,
+			"peer": true,
+			"requires": {
+				"@webassemblyjs/ast": "1.11.1",
+				"@webassemblyjs/helper-wasm-bytecode": "1.11.1",
+				"@webassemblyjs/ieee754": "1.11.1",
+				"@webassemblyjs/leb128": "1.11.1",
+				"@webassemblyjs/utf8": "1.11.1"
+			}
+		},
+		"@webassemblyjs/wasm-opt": {
+			"version": "1.11.1",
+			"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz",
+			"integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==",
+			"dev": true,
+			"peer": true,
+			"requires": {
+				"@webassemblyjs/ast": "1.11.1",
+				"@webassemblyjs/helper-buffer": "1.11.1",
+				"@webassemblyjs/wasm-gen": "1.11.1",
+				"@webassemblyjs/wasm-parser": "1.11.1"
+			}
+		},
+		"@webassemblyjs/wasm-parser": {
+			"version": "1.11.1",
+			"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz",
+			"integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==",
+			"dev": true,
+			"peer": true,
+			"requires": {
+				"@webassemblyjs/ast": "1.11.1",
+				"@webassemblyjs/helper-api-error": "1.11.1",
+				"@webassemblyjs/helper-wasm-bytecode": "1.11.1",
+				"@webassemblyjs/ieee754": "1.11.1",
+				"@webassemblyjs/leb128": "1.11.1",
+				"@webassemblyjs/utf8": "1.11.1"
+			}
+		},
+		"@webassemblyjs/wast-printer": {
+			"version": "1.11.1",
+			"resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz",
+			"integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==",
+			"dev": true,
+			"peer": true,
+			"requires": {
+				"@webassemblyjs/ast": "1.11.1",
+				"@xtuc/long": "4.2.2"
+			}
+		},
+		"@xtuc/ieee754": {
+			"version": "1.2.0",
+			"resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
+			"integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==",
+			"dev": true,
+			"peer": true
+		},
+		"@xtuc/long": {
+			"version": "4.2.2",
+			"resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz",
+			"integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
+			"dev": true,
+			"peer": true
+		},
+		"acorn": {
+			"version": "8.7.1",
+			"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz",
+			"integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==",
+			"dev": true
+		},
+		"acorn-import-assertions": {
+			"version": "1.8.0",
+			"resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz",
+			"integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==",
+			"dev": true,
+			"peer": true,
+			"requires": {}
+		},
+		"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": {}
+		},
+		"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"
+			}
+		},
+		"ajv-keywords": {
+			"version": "3.5.2",
+			"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
+			"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
+			"dev": true,
+			"peer": true,
+			"requires": {}
+		},
+		"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.2",
+			"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
+			"integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
+			"dev": true,
+			"requires": {
+				"normalize-path": "^3.0.0",
+				"picomatch": "^2.0.4"
+			}
+		},
+		"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.1.1",
+			"resolved": "https://registry.npmjs.org/async-validator/-/async-validator-4.1.1.tgz",
+			"integrity": "sha512-p4DO/JXwjs8klJyJL8Q2oM4ks5fUTze/h5k10oPPKMiLe1fj3G1QMzPHNmN1Py4ycOk7WlO2DcGXv1qiESJCZA=="
+		},
+		"asynckit": {
+			"version": "0.4.0",
+			"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+			"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
+		},
+		"axios": {
+			"version": "0.27.2",
+			"resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
+			"integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
+			"requires": {
+				"follow-redirects": "^1.14.9",
+				"form-data": "^4.0.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.20.3",
+			"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.3.tgz",
+			"integrity": "sha512-NBhymBQl1zM0Y5dQT/O+xiLP9/rzOIQdKM/eMJBAq7yBgaB6krIYLGejrwVYnSHZdqjscB1SPuAjHwxjvN6Wdg==",
+			"dev": true,
+			"peer": true,
+			"requires": {
+				"caniuse-lite": "^1.0.30001332",
+				"electron-to-chromium": "^1.4.118",
+				"escalade": "^3.1.1",
+				"node-releases": "^2.0.3",
+				"picocolors": "^1.0.0"
+			}
+		},
+		"buffer-from": {
+			"version": "1.1.2",
+			"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
+			"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
+			"dev": true,
+			"peer": true
+		},
+		"callsites": {
+			"version": "3.1.0",
+			"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+			"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+			"dev": true
+		},
+		"caniuse-lite": {
+			"version": "1.0.30001342",
+			"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001342.tgz",
+			"integrity": "sha512-bn6sOCu7L7jcbBbyNhLg0qzXdJ/PMbybZTH/BA6Roet9wxYRm6Tr9D0s0uhLkOZ6MSG+QU6txUgdpr3MXIVqjA==",
+			"dev": true,
+			"peer": 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"
+					}
+				}
+			}
+		},
+		"chrome-trace-event": {
+			"version": "1.0.3",
+			"resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz",
+			"integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==",
+			"dev": true,
+			"peer": true
+		},
+		"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"
+			}
+		},
+		"commander": {
+			"version": "2.20.3",
+			"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+			"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+			"dev": true,
+			"peer": true
+		},
+		"compute-scroll-into-view": {
+			"version": "1.0.17",
+			"resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-1.0.17.tgz",
+			"integrity": "sha512-j4dx+Fb0URmzbwwMUrhqWM2BEWHdFGx+qZ9qqASHRPqvTYdqvWnHg0H1hIbcyLnvgnoNAVMlwkepyqM3DaIFUg=="
+		},
+		"concat-map": {
+			"version": "0.0.1",
+			"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+			"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+			"dev": true
+		},
+		"countup.js": {
+			"version": "2.2.0",
+			"resolved": "https://registry.npmjs.org/countup.js/-/countup.js-2.2.0.tgz",
+			"integrity": "sha512-m0TvFNXm9/eFqJm+QiKVI8e0wRUHzlQSewz9dqVjlhl2DFoZtceLbomwzxHz0hJ1+r4zBC7wSpR/TpthG49h6g=="
+		},
+		"cropperjs": {
+			"version": "1.5.12",
+			"resolved": "https://registry.npmjs.org/cropperjs/-/cropperjs-1.5.12.tgz",
+			"integrity": "sha512-re7UdjE5UnwdrovyhNzZ6gathI4Rs3KGCBSc8HCIjUo5hO42CtzyblmWLj6QWVw7huHyDMfpKxhiO2II77nhDw=="
+		},
+		"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.20",
+			"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.20.tgz",
+			"integrity": "sha512-/WwNkdXfckNgw6S5R125rrW8ez139lBHWouiBvX8dfMFtcn6V81REDqnH7+CRpRipfYlyU1CmOnOxrmGcFOjeA=="
+		},
+		"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.2",
+			"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.2.tgz",
+			"integrity": "sha512-F4LXf1OeU9hrSYRPTTj/6FbO4HTjPKXvEIC1P2kcnFurViINCVk3ZV0xAS3XVx9MkMsXbbqlK6hjseaYbgKEHw=="
+		},
+		"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
+		},
+		"delayed-stream": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+			"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
+		},
+		"delegate": {
+			"version": "3.2.0",
+			"resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz",
+			"integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw=="
+		},
+		"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"
+			}
+		},
+		"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"
+			}
+		},
+		"dotenv": {
+			"version": "16.0.1",
+			"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.1.tgz",
+			"integrity": "sha512-1K6hR6wtk2FviQ4kEiSjFiH5rpzEVi8WW0x96aztHVMhEspNpc4DVOUTEHtEva5VThQ8IaBX1Pe4gSzpVVUsKQ==",
+			"dev": true
+		},
+		"echarts": {
+			"version": "5.3.2",
+			"resolved": "https://registry.npmjs.org/echarts/-/echarts-5.3.2.tgz",
+			"integrity": "sha512-LWCt7ohOKdJqyiBJ0OGBmE9szLdfA9sGcsMEi+GGoc6+Xo75C+BkcT/6NNGRHAWtnQl2fNow05AQjznpap28TQ==",
+			"requires": {
+				"tslib": "2.3.0",
+				"zrender": "5.3.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.0.0",
+			"resolved": "https://registry.npmjs.org/echarts-wordcloud/-/echarts-wordcloud-2.0.0.tgz",
+			"integrity": "sha512-K7l6pTklqdW7ZWzT/1CS0KhBSINr/cd7c5N1fVMzZMwLQHEwT7x+nivK7g5hkVh7WNcAv4Dn6/ZS5zMKRozC1g==",
+			"requires": {}
+		},
+		"electron-to-chromium": {
+			"version": "1.4.137",
+			"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.137.tgz",
+			"integrity": "sha512-0Rcpald12O11BUogJagX3HsCN3FE83DSqWjgXoHo5a72KUKMSfI39XBgJpgNNxS9fuGzytaFjE06kZkiVFy2qA==",
+			"dev": true,
+			"peer": true
+		},
+		"element-plus": {
+			"version": "2.2.2",
+			"resolved": "https://registry.npmjs.org/element-plus/-/element-plus-2.2.2.tgz",
+			"integrity": "sha512-yGcj2Ayb0jZO1WbI51tHJ4efhlfWKlBqqGtWbzhq+tcpfaKzJZN+IHRouuFasqn0ZV3tWCDu1jggDR1+9y7XfQ==",
+			"requires": {
+				"@ctrl/tinycolor": "^3.4.1",
+				"@element-plus/icons-vue": "^1.1.4",
+				"@floating-ui/dom": "^0.5.0",
+				"@popperjs/core": "npm:@sxzz/popperjs-es@^2.11.7",
+				"@types/lodash": "^4.14.182",
+				"@types/lodash-es": "^4.17.6",
+				"@vueuse/core": "^8.5.0",
+				"async-validator": "^4.1.1",
+				"dayjs": "^1.11.2",
+				"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.1.2"
+			},
+			"dependencies": {
+				"@element-plus/icons-vue": {
+					"version": "1.1.4",
+					"resolved": "https://registry.npmjs.org/@element-plus/icons-vue/-/icons-vue-1.1.4.tgz",
+					"integrity": "sha512-Iz/nHqdp1sFPmdzRwHkEQQA3lKvoObk8azgABZ81QUOpW9s/lUyQVUSh0tNtEPZXQlKwlSh7SPgoVxzrE0uuVQ==",
+					"requires": {}
+				}
+			}
+		},
+		"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"
+			}
+		},
+		"enhanced-resolve": {
+			"version": "5.9.3",
+			"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.3.tgz",
+			"integrity": "sha512-Bq9VSor+kjvW3f9/MiiR4eE3XYgOl7/rS8lnSxbRbF3kS0B2r+Y9w5krBWxZgDxASVZbdYrn5wT4j/Wb0J9qow==",
+			"dev": true,
+			"peer": true,
+			"requires": {
+				"graceful-fs": "^4.2.4",
+				"tapable": "^2.2.0"
+			}
+		},
+		"es-module-lexer": {
+			"version": "0.9.3",
+			"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz",
+			"integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==",
+			"dev": true,
+			"peer": true
+		},
+		"es5-ext": {
+			"version": "0.10.61",
+			"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.61.tgz",
+			"integrity": "sha512-yFhIqQAzu2Ca2I4SE2Au3rxVfmohU9Y7wqGR+s7+H7krk26NXhIRAZDgqd6xqjCEFUomDEA3/Bo/7fKmIkW1kA==",
+			"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": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=",
+			"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.14.39",
+			"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.39.tgz",
+			"integrity": "sha512-2kKujuzvRWYtwvNjYDY444LQIA3TyJhJIX3Yo4+qkFlDDtGlSicWgeHVJqMUP/2sSfH10PGwfsj+O2ro1m10xQ==",
+			"dev": true,
+			"requires": {
+				"esbuild-android-64": "0.14.39",
+				"esbuild-android-arm64": "0.14.39",
+				"esbuild-darwin-64": "0.14.39",
+				"esbuild-darwin-arm64": "0.14.39",
+				"esbuild-freebsd-64": "0.14.39",
+				"esbuild-freebsd-arm64": "0.14.39",
+				"esbuild-linux-32": "0.14.39",
+				"esbuild-linux-64": "0.14.39",
+				"esbuild-linux-arm": "0.14.39",
+				"esbuild-linux-arm64": "0.14.39",
+				"esbuild-linux-mips64le": "0.14.39",
+				"esbuild-linux-ppc64le": "0.14.39",
+				"esbuild-linux-riscv64": "0.14.39",
+				"esbuild-linux-s390x": "0.14.39",
+				"esbuild-netbsd-64": "0.14.39",
+				"esbuild-openbsd-64": "0.14.39",
+				"esbuild-sunos-64": "0.14.39",
+				"esbuild-windows-32": "0.14.39",
+				"esbuild-windows-64": "0.14.39",
+				"esbuild-windows-arm64": "0.14.39"
+			}
+		},
+		"esbuild-android-64": {
+			"version": "0.14.39",
+			"resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.39.tgz",
+			"integrity": "sha512-EJOu04p9WgZk0UoKTqLId9VnIsotmI/Z98EXrKURGb3LPNunkeffqQIkjS2cAvidh+OK5uVrXaIP229zK6GvhQ==",
+			"dev": true,
+			"optional": true
+		},
+		"esbuild-android-arm64": {
+			"version": "0.14.39",
+			"resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.39.tgz",
+			"integrity": "sha512-+twajJqO7n3MrCz9e+2lVOnFplRsaGRwsq1KL/uOy7xK7QdRSprRQcObGDeDZUZsacD5gUkk6OiHiYp6RzU3CA==",
+			"dev": true,
+			"optional": true
+		},
+		"esbuild-darwin-64": {
+			"version": "0.14.39",
+			"resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.39.tgz",
+			"integrity": "sha512-ImT6eUw3kcGcHoUxEcdBpi6LfTRWaV6+qf32iYYAfwOeV+XaQ/Xp5XQIBiijLeo+LpGci9M0FVec09nUw41a5g==",
+			"dev": true,
+			"optional": true
+		},
+		"esbuild-darwin-arm64": {
+			"version": "0.14.39",
+			"resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.39.tgz",
+			"integrity": "sha512-/fcQ5UhE05OiT+bW5v7/up1bDsnvaRZPJxXwzXsMRrr7rZqPa85vayrD723oWMT64dhrgWeA3FIneF8yER0XTw==",
+			"dev": true,
+			"optional": true
+		},
+		"esbuild-freebsd-64": {
+			"version": "0.14.39",
+			"resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.39.tgz",
+			"integrity": "sha512-oMNH8lJI4wtgN5oxuFP7BQ22vgB/e3Tl5Woehcd6i2r6F3TszpCnNl8wo2d/KvyQ4zvLvCWAlRciumhQg88+kQ==",
+			"dev": true,
+			"optional": true
+		},
+		"esbuild-freebsd-arm64": {
+			"version": "0.14.39",
+			"resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.39.tgz",
+			"integrity": "sha512-1GHK7kwk57ukY2yI4ILWKJXaxfr+8HcM/r/JKCGCPziIVlL+Wi7RbJ2OzMcTKZ1HpvEqCTBT/J6cO4ZEwW4Ypg==",
+			"dev": true,
+			"optional": true
+		},
+		"esbuild-linux-32": {
+			"version": "0.14.39",
+			"resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.39.tgz",
+			"integrity": "sha512-g97Sbb6g4zfRLIxHgW2pc393DjnkTRMeq3N1rmjDUABxpx8SjocK4jLen+/mq55G46eE2TA0MkJ4R3SpKMu7dg==",
+			"dev": true,
+			"optional": true
+		},
+		"esbuild-linux-64": {
+			"version": "0.14.39",
+			"resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.39.tgz",
+			"integrity": "sha512-4tcgFDYWdI+UbNMGlua9u1Zhu0N5R6u9tl5WOM8aVnNX143JZoBZLpCuUr5lCKhnD0SCO+5gUyMfupGrHtfggQ==",
+			"dev": true,
+			"optional": true
+		},
+		"esbuild-linux-arm": {
+			"version": "0.14.39",
+			"resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.39.tgz",
+			"integrity": "sha512-t0Hn1kWVx5UpCzAJkKRfHeYOLyFnXwYynIkK54/h3tbMweGI7dj400D1k0Vvtj2u1P+JTRT9tx3AjtLEMmfVBQ==",
+			"dev": true,
+			"optional": true
+		},
+		"esbuild-linux-arm64": {
+			"version": "0.14.39",
+			"resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.39.tgz",
+			"integrity": "sha512-23pc8MlD2D6Px1mV8GMglZlKgwgNKAO8gsgsLLcXWSs9lQsCYkIlMo/2Ycfo5JrDIbLdwgP8D2vpfH2KcBqrDQ==",
+			"dev": true,
+			"optional": true
+		},
+		"esbuild-linux-mips64le": {
+			"version": "0.14.39",
+			"resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.39.tgz",
+			"integrity": "sha512-epwlYgVdbmkuRr5n4es3B+yDI0I2e/nxhKejT9H0OLxFAlMkeQZxSpxATpDc9m8NqRci6Kwyb/SfmD1koG2Zuw==",
+			"dev": true,
+			"optional": true
+		},
+		"esbuild-linux-ppc64le": {
+			"version": "0.14.39",
+			"resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.39.tgz",
+			"integrity": "sha512-W/5ezaq+rQiQBThIjLMNjsuhPHg+ApVAdTz2LvcuesZFMsJoQAW2hutoyg47XxpWi7aEjJGrkS26qCJKhRn3QQ==",
+			"dev": true,
+			"optional": true
+		},
+		"esbuild-linux-riscv64": {
+			"version": "0.14.39",
+			"resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.39.tgz",
+			"integrity": "sha512-IS48xeokcCTKeQIOke2O0t9t14HPvwnZcy+5baG13Z1wxs9ZrC5ig5ypEQQh4QMKxURD5TpCLHw2W42CLuVZaA==",
+			"dev": true,
+			"optional": true
+		},
+		"esbuild-linux-s390x": {
+			"version": "0.14.39",
+			"resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.39.tgz",
+			"integrity": "sha512-zEfunpqR8sMomqXhNTFEKDs+ik7HC01m3M60MsEjZOqaywHu5e5682fMsqOlZbesEAAaO9aAtRBsU7CHnSZWyA==",
+			"dev": true,
+			"optional": true
+		},
+		"esbuild-netbsd-64": {
+			"version": "0.14.39",
+			"resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.39.tgz",
+			"integrity": "sha512-Uo2suJBSIlrZCe4E0k75VDIFJWfZy+bOV6ih3T4MVMRJh1lHJ2UyGoaX4bOxomYN3t+IakHPyEoln1+qJ1qYaA==",
+			"dev": true,
+			"optional": true
+		},
+		"esbuild-openbsd-64": {
+			"version": "0.14.39",
+			"resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.39.tgz",
+			"integrity": "sha512-secQU+EpgUPpYjJe3OecoeGKVvRMLeKUxSMGHnK+aK5uQM3n1FPXNJzyz1LHFOo0WOyw+uoCxBYdM4O10oaCAA==",
+			"dev": true,
+			"optional": true
+		},
+		"esbuild-sunos-64": {
+			"version": "0.14.39",
+			"resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.39.tgz",
+			"integrity": "sha512-qHq0t5gePEDm2nqZLb+35p/qkaXVS7oIe32R0ECh2HOdiXXkj/1uQI9IRogGqKkK+QjDG+DhwiUw7QoHur/Rwg==",
+			"dev": true,
+			"optional": true
+		},
+		"esbuild-windows-32": {
+			"version": "0.14.39",
+			"resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.39.tgz",
+			"integrity": "sha512-XPjwp2OgtEX0JnOlTgT6E5txbRp6Uw54Isorm3CwOtloJazeIWXuiwK0ONJBVb/CGbiCpS7iP2UahGgd2p1x+Q==",
+			"dev": true,
+			"optional": true
+		},
+		"esbuild-windows-64": {
+			"version": "0.14.39",
+			"resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.39.tgz",
+			"integrity": "sha512-E2wm+5FwCcLpKsBHRw28bSYQw0Ikxb7zIMxw3OPAkiaQhLVr3dnVO8DofmbWhhf6b97bWzg37iSZ45ZDpLw7Ow==",
+			"dev": true,
+			"optional": true
+		},
+		"esbuild-windows-arm64": {
+			"version": "0.14.39",
+			"resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.39.tgz",
+			"integrity": "sha512-sBZQz5D+Gd0EQ09tZRnz/PpVdLwvp/ufMtJ1iDFYddDaPpZXKqPyaxfYBLs3ueiaksQ26GGa7sci0OqFzNs7KA==",
+			"dev": true,
+			"optional": true
+		},
+		"escalade": {
+			"version": "3.1.1",
+			"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
+			"integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
+			"dev": true,
+			"peer": true
+		},
+		"escape-html": {
+			"version": "1.0.3",
+			"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+			"integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
+		},
+		"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.17.0",
+			"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.17.0.tgz",
+			"integrity": "sha512-gq0m0BTJfci60Fz4nczYxNAlED+sMcihltndR8t9t1evnU/azx53x3t2UHXC/uRjcbvRw/XctpaNygSTcQD+Iw==",
+			"dev": true,
+			"requires": {
+				"@eslint/eslintrc": "^1.3.0",
+				"@humanwhocodes/config-array": "^0.9.2",
+				"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.3.2",
+				"esquery": "^1.4.0",
+				"esutils": "^2.0.2",
+				"fast-deep-equal": "^3.1.3",
+				"file-entry-cache": "^6.0.1",
+				"functional-red-black-tree": "^1.0.1",
+				"glob-parent": "^6.0.1",
+				"globals": "^13.15.0",
+				"ignore": "^5.2.0",
+				"import-fresh": "^3.0.0",
+				"imurmurhash": "^0.1.4",
+				"is-glob": "^4.0.0",
+				"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",
+				"v8-compile-cache": "^2.0.3"
+			},
+			"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.1.0",
+			"resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.1.0.tgz",
+			"integrity": "sha512-EPCeInPicQ/YyfOWJDr1yfEeSNoFCMzUus107lZyYi37xejdOolNzS5MXGXp8+9bkoKZMdv/1AcZzQebME6r+g==",
+			"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.3.2",
+			"resolved": "https://registry.npmjs.org/espree/-/espree-9.3.2.tgz",
+			"integrity": "sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==",
+			"dev": true,
+			"requires": {
+				"acorn": "^8.7.1",
+				"acorn-jsx": "^5.3.2",
+				"eslint-visitor-keys": "^3.3.0"
+			}
+		},
+		"esquery": {
+			"version": "1.4.0",
+			"resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz",
+			"integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==",
+			"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": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=",
+			"requires": {
+				"d": "1",
+				"es5-ext": "~0.10.14"
+			}
+		},
+		"events": {
+			"version": "3.3.0",
+			"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
+			"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
+			"dev": true,
+			"peer": true
+		},
+		"ext": {
+			"version": "1.6.0",
+			"resolved": "https://registry.npmjs.org/ext/-/ext-1.6.0.tgz",
+			"integrity": "sha512-sdBImtzkq2HpkdRLtlLWDa6w4DX22ijZLKx8BMPUuKe1c5lbN6xwQDQCxSfxBQnHZ13ls/FH0MQZx/q/gr6FQg==",
+			"requires": {
+				"type": "^2.5.0"
+			},
+			"dependencies": {
+				"type": {
+					"version": "2.6.0",
+					"resolved": "https://registry.npmjs.org/type/-/type-2.6.0.tgz",
+					"integrity": "sha512-eiDBDOmkih5pMbo9OqsqPRGMljLodLcwd5XD5JbtNB0o89xZAwynY9EdCDsJU7LtcVCClu9DvM7/0Ep1hYX3EQ=="
+				}
+			}
+		},
+		"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.11",
+			"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz",
+			"integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==",
+			"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": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
+			"dev": true
+		},
+		"fastq": {
+			"version": "1.13.0",
+			"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz",
+			"integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==",
+			"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"
+			}
+		},
+		"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.5",
+			"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz",
+			"integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==",
+			"dev": true
+		},
+		"follow-redirects": {
+			"version": "1.15.0",
+			"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.0.tgz",
+			"integrity": "sha512-aExlJShTV4qOUOL7yF1U5tvLCB0xQuudbf6toyYA0E/acBNw71mvjFTnLaRp50aQaYocMR0a/RMMBIHeZnGyjQ=="
+		},
+		"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"
+			}
+		},
+		"fs.realpath": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+			"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+			"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==",
+			"dev": true
+		},
+		"functional-red-black-tree": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
+			"integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
+			"dev": true
+		},
+		"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"
+			}
+		},
+		"glob-to-regexp": {
+			"version": "0.4.1",
+			"resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz",
+			"integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==",
+			"dev": true,
+			"peer": true
+		},
+		"globals": {
+			"version": "13.15.0",
+			"resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz",
+			"integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==",
+			"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": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=",
+			"requires": {
+				"delegate": "^3.1.2"
+			}
+		},
+		"graceful-fs": {
+			"version": "4.2.10",
+			"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
+			"integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==",
+			"dev": true,
+			"peer": true
+		},
+		"has": {
+			"version": "1.0.3",
+			"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+			"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+			"dev": true,
+			"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
+		},
+		"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.0",
+			"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz",
+			"integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==",
+			"dev": true
+		},
+		"immer": {
+			"version": "9.0.14",
+			"resolved": "https://registry.npmjs.org/immer/-/immer-9.0.14.tgz",
+			"integrity": "sha512-ubBeqQutOSLIFCUBN03jGeOS6a3DoYlSYwYJTa+gSKEZKU5redJIqkIdZ3JVv/4RZpfcXdAWH5zCNLWPRv2WDw=="
+		},
+		"immutable": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/immutable/-/immutable-4.0.0.tgz",
+			"integrity": "sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw==",
+			"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": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
+			"dev": true
+		},
+		"inflight": {
+			"version": "1.0.6",
+			"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+			"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+			"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.9.0",
+			"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz",
+			"integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==",
+			"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": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+			"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-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": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+			"dev": true
+		},
+		"jest-worker": {
+			"version": "27.5.1",
+			"resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz",
+			"integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==",
+			"dev": true,
+			"peer": true,
+			"requires": {
+				"@types/node": "*",
+				"merge-stream": "^2.0.0",
+				"supports-color": "^8.0.0"
+			},
+			"dependencies": {
+				"supports-color": {
+					"version": "8.1.1",
+					"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+					"integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+					"dev": true,
+					"peer": true,
+					"requires": {
+						"has-flag": "^4.0.0"
+					}
+				}
+			}
+		},
+		"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-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-parse-even-better-errors": {
+			"version": "2.3.1",
+			"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+			"integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
+			"dev": true,
+			"peer": true
+		},
+		"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": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
+			"dev": true
+		},
+		"jsplumb": {
+			"version": "2.15.6",
+			"resolved": "https://registry.npmjs.org/jsplumb/-/jsplumb-2.15.6.tgz",
+			"integrity": "sha512-sIpbpz5eMVM+vV+MQzFCidlaa1RsknrQs6LOTKYDjYUDdTAi2AN2bFi94TxB33TifcIsRNV1jebcaxg0tCoPzg=="
+		},
+		"klona": {
+			"version": "2.0.5",
+			"resolved": "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz",
+			"integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==",
+			"dev": true
+		},
+		"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"
+			}
+		},
+		"loader-runner": {
+			"version": "4.3.0",
+			"resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz",
+			"integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==",
+			"dev": true,
+			"peer": true
+		},
+		"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.2",
+			"resolved": "https://registry.npmjs.org/lodash-unified/-/lodash-unified-1.0.2.tgz",
+			"integrity": "sha512-OGbEy+1P+UT26CYi4opY4gebD8cWRDxAT6MAObIVQMiqYdxZr1g3QHWCToVsm31x2NkLS4K3+MC2qInaRMa39g==",
+			"requires": {}
+		},
+		"lodash.camelcase": {
+			"version": "4.3.0",
+			"resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
+			"integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY="
+		},
+		"lodash.clonedeep": {
+			"version": "4.5.0",
+			"resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
+			"integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8="
+		},
+		"lodash.debounce": {
+			"version": "4.0.8",
+			"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
+			"integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168="
+		},
+		"lodash.foreach": {
+			"version": "4.5.0",
+			"resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz",
+			"integrity": "sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM="
+		},
+		"lodash.isequal": {
+			"version": "4.5.0",
+			"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
+			"integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA="
+		},
+		"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.sortby": {
+			"version": "4.7.0",
+			"resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
+			"integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=",
+			"dev": true,
+			"peer": true
+		},
+		"lodash.throttle": {
+			"version": "4.1.1",
+			"resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
+			"integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ="
+		},
+		"lodash.toarray": {
+			"version": "4.4.0",
+			"resolved": "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz",
+			"integrity": "sha1-JMS/zWsvuji/0FlNsRedjptlZWE="
+		},
+		"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=="
+		},
+		"merge-stream": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
+			"integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
+			"dev": true,
+			"peer": true
+		},
+		"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": "sha1-P4fDHprxpf1IX7nbE0Qosju7e6g=",
+			"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"
+			}
+		},
+		"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": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
+			"dev": true
+		},
+		"neo-async": {
+			"version": "2.6.2",
+			"resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
+			"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
+			"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.4",
+			"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.4.tgz",
+			"integrity": "sha512-gbMzqQtTtDz/00jQzZ21PQzdI9PyLYqUSvD0p3naOhX4odFji0ZxYdnVwPTxmSwkmxhcFImpozceidSG+AgoPQ==",
+			"dev": true,
+			"peer": 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-wheel-es": {
+			"version": "1.1.2",
+			"resolved": "https://registry.npmjs.org/normalize-wheel-es/-/normalize-wheel-es-1.1.2.tgz",
+			"integrity": "sha512-scX83plWJXYH1J4+BhAuIHadROzxX0UBF3+HuZNY2Ks8BciE7tSTQ+5JhTsvzjaO0/EJdm4JBGrfObKxFf3Png=="
+		},
+		"nprogress": {
+			"version": "0.2.0",
+			"resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz",
+			"integrity": "sha1-y480xTIT2JVyP8urkH6UIq28r7E="
+		},
+		"nth-check": {
+			"version": "2.0.1",
+			"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz",
+			"integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==",
+			"dev": true,
+			"requires": {
+				"boolbase": "^1.0.0"
+			}
+		},
+		"once": {
+			"version": "1.4.0",
+			"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+			"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+			"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"
+			}
+		},
+		"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-is-absolute": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+			"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+			"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
+		},
+		"pinia": {
+			"version": "2.0.14",
+			"resolved": "https://registry.npmjs.org/pinia/-/pinia-2.0.14.tgz",
+			"integrity": "sha512-0nPuZR4TetT/WcLN+feMSjWJku3SQU7dBbXC6uw+R6FLQJCsg+/0pzXyD82T1FmAYe0lsx+jnEDQ1BLgkRKlxA==",
+			"requires": {
+				"@vue/devtools-api": "^6.1.4",
+				"vue-demi": "*"
+			},
+			"dependencies": {
+				"vue-demi": {
+					"version": "0.12.5",
+					"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.12.5.tgz",
+					"integrity": "sha512-BREuTgTYlUr0zw0EZn3hnhC3I6gPWv+Kwh4MCih6QcAeaTlaIX0DwOVN0wHej7hSvDPecz4jygy/idsgKfW58Q==",
+					"requires": {}
+				}
+			}
+		},
+		"postcss": {
+			"version": "8.4.14",
+			"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz",
+			"integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==",
+			"requires": {
+				"nanoid": "^3.3.4",
+				"picocolors": "^1.0.0",
+				"source-map-js": "^1.0.2"
+			}
+		},
+		"postcss-selector-parser": {
+			"version": "6.0.10",
+			"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz",
+			"integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==",
+			"dev": true,
+			"requires": {
+				"cssesc": "^3.0.0",
+				"util-deprecate": "^1.0.2"
+			}
+		},
+		"preact": {
+			"version": "10.7.2",
+			"resolved": "https://registry.npmjs.org/preact/-/preact-10.7.2.tgz",
+			"integrity": "sha512-GLjn0I3r6ka+NvxJUppsVFqb4V0qDTEHT/QxHlidPuClGaxF/4AI2Qti4a0cv3XMh5n1+D3hLScW10LRIm5msQ=="
+		},
+		"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.6.2",
+			"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.6.2.tgz",
+			"integrity": "sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew==",
+			"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.28.0",
+			"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.28.0.tgz",
+			"integrity": "sha512-8aaXdYvl1F7iC7Xm1spqSaY/OJBpYW3v+KJ+F17iYxvdc8sfjW194COK5wVhMZX45tGteiBQgdvD/nhxcRwylw=="
+		},
+		"punycode": {
+			"version": "2.1.1",
+			"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+			"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+			"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=="
+		},
+		"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
+		},
+		"randombytes": {
+			"version": "2.1.0",
+			"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
+			"integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
+			"dev": true,
+			"peer": true,
+			"requires": {
+				"safe-buffer": "^5.1.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.9",
+			"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz",
+			"integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA=="
+		},
+		"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.0",
+			"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz",
+			"integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==",
+			"dev": true,
+			"requires": {
+				"is-core-module": "^2.8.1",
+				"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": "2.74.1",
+			"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.74.1.tgz",
+			"integrity": "sha512-K2zW7kV8Voua5eGkbnBtWYfMIhYhT9Pel2uhBk2WO5eMee161nPze/XRfvEQPFYz7KgrCCnmh2Wy0AMFLGGmMA==",
+			"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"
+			}
+		},
+		"safe-buffer": {
+			"version": "5.2.1",
+			"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+			"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+			"dev": true,
+			"peer": true
+		},
+		"sass": {
+			"version": "1.52.2",
+			"resolved": "https://registry.npmjs.org/sass/-/sass-1.52.2.tgz",
+			"integrity": "sha512-mfHB2VSeFS7sZlPv9YohB9GB7yWIgQNTGniQwfQ04EoQN0wsQEv7SwpCwy/x48Af+Z3vDeFXz+iuXM3HK/phZQ==",
+			"dev": true,
+			"requires": {
+				"chokidar": ">=3.0.0 <4.0.0",
+				"immutable": "^4.0.0",
+				"source-map-js": ">=0.6.2 <2.0.0"
+			}
+		},
+		"sass-loader": {
+			"version": "13.0.0",
+			"resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-13.0.0.tgz",
+			"integrity": "sha512-IHCFecI+rbPvXE2zO/mqdVFe8MU7ElGrwga9hh2H65Ru4iaBJAMRteum1c4Gsxi9Cq1FOtTEDd6+/AEYuQDM4Q==",
+			"dev": true,
+			"requires": {
+				"klona": "^2.0.4",
+				"neo-async": "^2.6.2"
+			}
+		},
+		"schema-utils": {
+			"version": "3.1.1",
+			"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz",
+			"integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==",
+			"dev": true,
+			"peer": true,
+			"requires": {
+				"@types/json-schema": "^7.0.8",
+				"ajv": "^6.12.5",
+				"ajv-keywords": "^3.5.2"
+			}
+		},
+		"screenfull": {
+			"version": "6.0.1",
+			"resolved": "https://registry.npmjs.org/screenfull/-/screenfull-6.0.1.tgz",
+			"integrity": "sha512-yzQW+j4zMUBQC51xxWaoDYjxOtl8Kn+xvue3p6v/fv2pIi1jH4AldgVLU8TBfFVgH2x3VXlf3+YiA/AYIPlaew=="
+		},
+		"scroll-into-view-if-needed": {
+			"version": "2.2.29",
+			"resolved": "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.29.tgz",
+			"integrity": "sha512-hxpAR6AN+Gh53AdAimHM6C8oTN1ppwVZITihix+WqalywBeFcQ6LdQP5ABNl26nX8GTEL7VT+b8lKpdqq65wXg==",
+			"requires": {
+				"compute-scroll-into-view": "^1.0.17"
+			}
+		},
+		"select": {
+			"version": "1.1.2",
+			"resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz",
+			"integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0="
+		},
+		"semver": {
+			"version": "7.3.7",
+			"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
+			"integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
+			"dev": true,
+			"requires": {
+				"lru-cache": "^6.0.0"
+			}
+		},
+		"serialize-javascript": {
+			"version": "6.0.0",
+			"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz",
+			"integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==",
+			"dev": true,
+			"peer": true,
+			"requires": {
+				"randombytes": "^2.1.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
+		},
+		"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.0",
+			"resolved": "https://registry.npmjs.org/snabbdom/-/snabbdom-3.5.0.tgz",
+			"integrity": "sha512-Ff5BKG18KrrPuskHJlA9aujPHqEabItaDl96l7ZZndF4zt5AYSczz7ZjjgQAX5IBd5cd25lw9NfgX21yVUJ+9g=="
+		},
+		"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=="
+		},
+		"source-map-support": {
+			"version": "0.5.21",
+			"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
+			"integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+			"dev": true,
+			"peer": true,
+			"requires": {
+				"buffer-from": "^1.0.0",
+				"source-map": "^0.6.0"
+			}
+		},
+		"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.1",
+			"resolved": "https://registry.npmjs.org/splitpanes/-/splitpanes-3.1.1.tgz",
+			"integrity": "sha512-VUkxDJfIGSvTM/fm/+OSrx8ha9URwE/9B8FPvfzoBuAxVELIHBWpsfnJXIXv77zVwuex//QQL4kTU9SDBPeHjA=="
+		},
+		"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
+		},
+		"tapable": {
+			"version": "2.2.1",
+			"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
+			"integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
+			"dev": true,
+			"peer": true
+		},
+		"terser": {
+			"version": "5.13.1",
+			"resolved": "https://registry.npmjs.org/terser/-/terser-5.13.1.tgz",
+			"integrity": "sha512-hn4WKOfwnwbYfe48NgrQjqNOH9jzLqRcIfbYytOXCOv46LBfWr9bDS17MQqOi+BWGD0sJK3Sj5NC/gJjiojaoA==",
+			"dev": true,
+			"peer": true,
+			"requires": {
+				"acorn": "^8.5.0",
+				"commander": "^2.20.0",
+				"source-map": "~0.8.0-beta.0",
+				"source-map-support": "~0.5.20"
+			},
+			"dependencies": {
+				"source-map": {
+					"version": "0.8.0-beta.0",
+					"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz",
+					"integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==",
+					"dev": true,
+					"peer": true,
+					"requires": {
+						"whatwg-url": "^7.0.0"
+					}
+				}
+			}
+		},
+		"terser-webpack-plugin": {
+			"version": "5.3.1",
+			"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.1.tgz",
+			"integrity": "sha512-GvlZdT6wPQKbDNW/GDQzZFg/j4vKU96yl2q6mcUkzKOgW4gwf1Z8cZToUCrz31XHlPWH8MVb1r2tFtdDtTGJ7g==",
+			"dev": true,
+			"peer": true,
+			"requires": {
+				"jest-worker": "^27.4.5",
+				"schema-utils": "^3.1.1",
+				"serialize-javascript": "^6.0.0",
+				"source-map": "^0.6.1",
+				"terser": "^5.7.2"
+			}
+		},
+		"text-table": {
+			"version": "0.2.0",
+			"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+			"integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
+			"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"
+			}
+		},
+		"tr46": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz",
+			"integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=",
+			"dev": true,
+			"peer": true,
+			"requires": {
+				"punycode": "^2.1.0"
+			}
+		},
+		"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.7.3",
+			"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.3.tgz",
+			"integrity": "sha512-WOkT3XYvrpXx4vMMqlD+8R8R37fZkjyLGlxavMc4iB8lrl8L0DeTcHbYgw/v0N/z9wAFsgBhcsF0ruoySS22mA==",
+			"devOptional": true
+		},
+		"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": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
+			"dev": true
+		},
+		"v8-compile-cache": {
+			"version": "2.3.0",
+			"resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz",
+			"integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==",
+			"dev": true
+		},
+		"vite": {
+			"version": "2.9.9",
+			"resolved": "https://registry.npmjs.org/vite/-/vite-2.9.9.tgz",
+			"integrity": "sha512-ffaam+NgHfbEmfw/Vuh6BHKKlI/XIAhxE5QSS7gFLIngxg171mg1P3a4LSRME0z2ZU1ScxoKzphkipcYwSD5Ew==",
+			"dev": true,
+			"requires": {
+				"esbuild": "^0.14.27",
+				"fsevents": "~2.3.2",
+				"postcss": "^8.4.13",
+				"resolve": "^1.22.0",
+				"rollup": "^2.59.0"
+			}
+		},
+		"vue": {
+			"version": "3.2.36",
+			"resolved": "https://registry.npmjs.org/vue/-/vue-3.2.36.tgz",
+			"integrity": "sha512-5yTXmrE6gW8IQgttzHW5bfBiFA6mx35ZXHjGLDmKYzW6MMmYvCwuKybANRepwkMYeXw2v1buGg3/lPICY5YlZw==",
+			"requires": {
+				"@vue/compiler-dom": "3.2.36",
+				"@vue/compiler-sfc": "3.2.36",
+				"@vue/runtime-dom": "3.2.36",
+				"@vue/server-renderer": "3.2.36",
+				"@vue/shared": "3.2.36"
+			}
+		},
+		"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.0.2",
+			"resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.0.2.tgz",
+			"integrity": "sha512-uCPQwTGjOtAYrwnU+76pYxalhjsh7iFBsHwBqDHiOPTxtICDaraO4Szw54WFTNZTAEsgHHzqFOu1mmnBOBRzDA==",
+			"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.1.10",
+			"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.1.10.tgz",
+			"integrity": "sha512-jpr7gV5KPk4n+sSPdpZT8Qx3XzTcNDWffRlHV/cT2NUyEf+sEgTTmLvnBAibjOFJ0zsUyZlVTAWH5DDnYep+1g==",
+			"requires": {
+				"@intlify/core-base": "9.1.10",
+				"@intlify/shared": "9.1.10",
+				"@intlify/vue-devtools": "9.1.10",
+				"@vue/devtools-api": "^6.0.0-beta.7"
+			}
+		},
+		"vue-router": {
+			"version": "4.0.15",
+			"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.0.15.tgz",
+			"integrity": "sha512-xa+pIN9ZqORdIW1MkN2+d9Ui2pCM1b/UMgwYUCZOiFYHAvz/slKKBDha8DLrh5aCG/RibtrpyhKjKOZ85tYyWg==",
+			"requires": {
+				"@vue/devtools-api": "^6.0.0"
+			}
+		},
+		"watchpack": {
+			"version": "2.3.1",
+			"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.1.tgz",
+			"integrity": "sha512-x0t0JuydIo8qCNctdDrn1OzH/qDzk2+rdCOC3YzumZ42fiMqmQ7T3xQurykYMhYfHaPHTp4ZxAx2NfUo1K6QaA==",
+			"dev": true,
+			"peer": true,
+			"requires": {
+				"glob-to-regexp": "^0.4.1",
+				"graceful-fs": "^4.1.2"
+			}
+		},
+		"webidl-conversions": {
+			"version": "4.0.2",
+			"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
+			"integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==",
+			"dev": true,
+			"peer": true
+		},
+		"webpack": {
+			"version": "5.72.1",
+			"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.72.1.tgz",
+			"integrity": "sha512-dXG5zXCLspQR4krZVR6QgajnZOjW2K/djHvdcRaDQvsjV9z9vaW6+ja5dZOYbqBBjF6kGXka/2ZyxNdc+8Jung==",
+			"dev": true,
+			"peer": true,
+			"requires": {
+				"@types/eslint-scope": "^3.7.3",
+				"@types/estree": "^0.0.51",
+				"@webassemblyjs/ast": "1.11.1",
+				"@webassemblyjs/wasm-edit": "1.11.1",
+				"@webassemblyjs/wasm-parser": "1.11.1",
+				"acorn": "^8.4.1",
+				"acorn-import-assertions": "^1.7.6",
+				"browserslist": "^4.14.5",
+				"chrome-trace-event": "^1.0.2",
+				"enhanced-resolve": "^5.9.3",
+				"es-module-lexer": "^0.9.0",
+				"eslint-scope": "5.1.1",
+				"events": "^3.2.0",
+				"glob-to-regexp": "^0.4.1",
+				"graceful-fs": "^4.2.9",
+				"json-parse-even-better-errors": "^2.3.1",
+				"loader-runner": "^4.2.0",
+				"mime-types": "^2.1.27",
+				"neo-async": "^2.6.2",
+				"schema-utils": "^3.1.0",
+				"tapable": "^2.1.1",
+				"terser-webpack-plugin": "^5.1.3",
+				"watchpack": "^2.3.1",
+				"webpack-sources": "^3.2.3"
+			}
+		},
+		"webpack-sources": {
+			"version": "3.2.3",
+			"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz",
+			"integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==",
+			"dev": true,
+			"peer": true
+		},
+		"whatwg-url": {
+			"version": "7.1.0",
+			"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz",
+			"integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==",
+			"dev": true,
+			"peer": true,
+			"requires": {
+				"lodash.sortby": "^4.7.0",
+				"tr46": "^1.0.1",
+				"webidl-conversions": "^4.0.2"
+			}
+		},
+		"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": "sha1-pwIEUwhNjNLv5wup02liY94XEKU="
+		},
+		"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": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+			"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
+		},
+		"yallist": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+			"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+			"dev": true
+		},
+		"zrender": {
+			"version": "5.3.1",
+			"resolved": "https://registry.npmjs.org/zrender/-/zrender-5.3.1.tgz",
+			"integrity": "sha512-7olqIjy0gWfznKr6vgfnGBk7y4UtdMvdwFmK92vVQsQeDPyzkHW1OlrLEKg6GHz1W5ePf0FeN1q2vkl/HFqhXw==",
+			"requires": {
+				"tslib": "2.3.0"
+			}
+		}
+	}
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..fbe32d7
--- /dev/null
+++ b/package.json
@@ -0,0 +1,81 @@
+{
+	"name": "vue-next-admin",
+	"version": "2.1.1",
+	"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.3",
+		"@wangeditor/editor": "^5.1.1",
+		"axios": "^0.27.2",
+		"countup.js": "^2.2.0",
+		"cropperjs": "^1.5.12",
+		"echarts": "^5.3.2",
+		"echarts-gl": "^2.0.9",
+		"echarts-wordcloud": "^2.0.0",
+		"element-plus": "^2.2.2",
+		"js-cookie": "^3.0.1",
+		"jsplumb": "^2.15.6",
+		"mitt": "^3.0.0",
+		"nprogress": "^0.2.0",
+		"pinia": "^2.0.14",
+		"print-js": "^1.6.0",
+		"qrcodejs2-fixes": "^0.0.2",
+		"screenfull": "^6.0.1",
+		"sortablejs": "^1.15.0",
+		"splitpanes": "^3.1.1",
+		"vue": "^3.2.36",
+		"vue-clipboard3": "^2.0.0",
+		"vue-grid-layout": "^3.0.0-beta1",
+		"vue-i18n": "^9.1.10",
+		"vue-router": "^4.0.15"
+	},
+	"devDependencies": {
+		"@types/node": "^17.0.39",
+		"@types/nprogress": "^0.2.0",
+		"@types/sortablejs": "^1.13.0",
+		"@typescript-eslint/eslint-plugin": "^5.27.0",
+		"@typescript-eslint/parser": "^5.27.0",
+		"@vitejs/plugin-vue": "^2.3.3",
+		"@vue/compiler-sfc": "^3.2.36",
+		"dotenv": "^16.0.1",
+		"eslint": "^8.17.0",
+		"eslint-plugin-vue": "^9.1.0",
+		"prettier": "^2.6.2",
+		"sass": "^1.52.2",
+		"sass-loader": "^13.0.0",
+		"typescript": "^4.7.3",
+		"vite": "^2.9.9",
+		"vue-eslint-parser": "^9.0.2"
+	},
+	"browserslist": [
+		"> 1%",
+		"last 2 versions",
+		"not dead"
+	],
+	"bugs": {
+		"url": "https://gitee.com/lyt-top/vue-next-admin/issues"
+	},
+	"engines": {
+		"node": ">=12.0.0",
+		"npm": ">= 6.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"
+	}
+}
diff --git a/plugins.d.ts b/plugins.d.ts
new file mode 100644
index 0000000..be0457a
--- /dev/null
+++ b/plugins.d.ts
@@ -0,0 +1,4 @@
+declare module 'vue-grid-layout';
+declare module 'qrcodejs2-fixes';
+declare module 'splitpanes';
+declare module 'js-cookie';
diff --git a/public/favicon.ico b/public/favicon.ico
new file mode 100644
index 0000000..9da231b
--- /dev/null
+++ b/public/favicon.ico
Binary files differ
diff --git a/shim.d.ts b/shim.d.ts
new file mode 100644
index 0000000..3e9bfd0
--- /dev/null
+++ b/shim.d.ts
@@ -0,0 +1,13 @@
+/* eslint-disable */
+
+// 声明文件,*.vue 后缀的文件交给 vue 模块来处理
+declare module '*.vue' {
+	import type { DefineComponent } from 'vue';
+	const component: DefineComponent<{}, {}, any>;
+	export default component;
+}
+
+// 声明文件,定义全局变量。其它 app.config.globalProperties.xxx,使用 getCurrentInstance() 来获取
+interface Window {
+	nextLoading: boolean;
+}
diff --git a/source.d.ts b/source.d.ts
new file mode 100644
index 0000000..2f9c768
--- /dev/null
+++ b/source.d.ts
@@ -0,0 +1,6 @@
+declare module '*.json';
+declare module '*.png';
+declare module '*.jpg';
+declare module '*.scss';
+declare module '*.ts';
+declare module '*.js';
diff --git a/src/App.vue b/src/App.vue
new file mode 100644
index 0000000..e69c74b
--- /dev/null
+++ b/src/App.vue
@@ -0,0 +1,100 @@
+<template>
+	<el-config-provider :size="getGlobalComponentSize" :locale="i18nLocale">
+		<router-view v-show="themeConfig.lockScreenTime > 1" />
+		<LockScreen v-if="themeConfig.isLockScreen" />
+		<Setings ref="setingsRef" v-show="themeConfig.lockScreenTime > 1" />
+		<CloseFull v-if="!themeConfig.isLockScreen" />
+	</el-config-provider>
+</template>
+
+<script lang="ts">
+import { computed, ref, getCurrentInstance, onBeforeMount, onMounted, onUnmounted, nextTick, defineComponent, watch, reactive, toRefs } from 'vue';
+import { useRoute } from 'vue-router';
+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 setIntroduction from '/@/utils/setIconfont';
+import LockScreen from '/@/layout/lockScreen/index.vue';
+import Setings from '/@/layout/navBars/breadcrumb/setings.vue';
+import CloseFull from '/@/layout/navBars/breadcrumb/closeFull.vue';
+import { initBackEndControlRoutes } from './router/backEnd';
+
+export default ({
+	name: 'app',
+	components: { LockScreen, Setings, CloseFull },
+	setup() {
+		const { proxy } = <any>getCurrentInstance();
+		const setingsRef = ref();
+		const route = useRoute();
+		const stores = useTagsViewRoutes();
+		const storesThemeConfig = useThemeConfig();
+		const { themeConfig } = storeToRefs(storesThemeConfig);
+		const state = reactive({
+			i18nLocale: null,
+		});
+
+		// 获取全局组件大小
+		const getGlobalComponentSize = computed(() => {
+			return other.globalComponentSize();
+		});
+		// 布局配置弹窗打开
+		const openSetingsDrawer = () => {
+			setingsRef.value.openDrawer();
+		};
+		// 设置初始化,防止刷新时恢复默认
+		onBeforeMount(() => {
+			// 设置批量第三方 icon 图标
+			setIntroduction.cssCdn();
+			// 设置批量第三方 js
+			setIntroduction.jsCdn();
+		});
+		// 页面加载时
+		onMounted( () => {
+			nextTick(() => {
+				// 监听布局配置弹窗点击打开
+				proxy.mittBus.on('openSetingsDrawer', () => {
+					openSetingsDrawer();
+				});
+				// 设置 i18n,App.vue 中的 el-config-provider
+				proxy.mittBus.on('getI18nConfig', (locale: string) => {
+					(state.i18nLocale as string | null) = locale;
+				});
+				// 获取缓存中的布局配置
+				if (Local.get('themeConfig')) {
+					storesThemeConfig.setThemeConfig(Local.get('themeConfig'));
+					document.documentElement.style.cssText = Local.get('themeConfigStyle');
+				}
+				// 获取缓存中的全屏配置
+				if (Session.get('isTagsViewCurrenFull')) {
+					stores.setCurrenFullscreen(Session.get('isTagsViewCurrenFull'));
+				}
+			});
+			// if(!Session.get('token')) return
+			// initBackEndControlRoutes()
+		});
+		// 页面销毁时,关闭监听布局配置/i18n监听
+		onUnmounted(() => {
+			proxy.mittBus.off('openSetingsDrawer', () => {});
+			proxy.mittBus.off('getI18nConfig', () => {});
+		});
+		// 监听路由的变化,设置网站标题
+		watch(
+			() => route.path,
+			() => {
+				other.useTitle();
+			},
+			{
+				deep: true,
+			}
+		);
+		return {
+			themeConfig,
+			setingsRef,
+			getGlobalComponentSize,
+			...toRefs(state),
+		};
+	},
+});
+</script>
diff --git a/src/api/department/index.ts b/src/api/department/index.ts
new file mode 100644
index 0000000..0ed427c
--- /dev/null
+++ b/src/api/department/index.ts
@@ -0,0 +1,26 @@
+import request from '/@/utils/request';
+
+export function departmentApi() {
+    return {
+        getDepartmentList: () => {
+            return request({
+                url: `/department/list`,
+                method: 'post',
+            });
+        },
+        addDepartment: (data: object) => {
+            return request({
+                url: `/department/add`,
+                method: 'post',
+                data:data
+            });
+        },
+        modDepartment: (data: object) => {
+            return request({
+                url: `/department/mod`,
+                method: 'post',
+                data:data
+            });
+        },
+    };
+}
diff --git a/src/api/login/index.ts b/src/api/login/index.ts
new file mode 100644
index 0000000..ca48515
--- /dev/null
+++ b/src/api/login/index.ts
@@ -0,0 +1,25 @@
+import request from '/@/utils/request';
+
+/**
+ * 登录api接口集合
+ * @method signIn 用户登录
+ * @method signOut 用户退出登录
+ */
+export function useLoginApi() {
+	return {
+		signIn: (params: object) => {
+			return request({
+				url: '/auth/login',
+				method: 'post',
+				data: params,
+			});
+		},
+		signOut: (params: object) => {
+			return request({
+				url: '/user/signOut',
+				method: 'post',
+				data: params,
+			});
+		},
+	};
+}
diff --git a/src/api/menu/index.ts b/src/api/menu/index.ts
new file mode 100644
index 0000000..2af998a
--- /dev/null
+++ b/src/api/menu/index.ts
@@ -0,0 +1,39 @@
+import request from '/@/utils/request';
+
+/**
+ * 后端控制菜单模拟json,路径在 https://gitee.com/lyt-top/vue-next-admin-images/tree/master/menu
+ * 后端控制路由,isRequestRoutes 为 true,则开启后端控制路由
+ * @method getMenuAdmin 获取后端动态路由菜单(admin)
+ * @method getMenuTest 获取后端动态路由菜单(test)
+ */
+export function useMenuApi() {
+	return {
+		getMenuAdmin: (value?: string) => {
+			return request({
+				url: `/auth/menu?projectId= ${value}`,
+				method: 'post',
+			});
+		},
+		addMenu: (value?: object) => {
+			return request({
+				url: `/menu/add`,
+				method: 'post',
+				data:value
+			});
+		},
+		modMenu: (value?: object) => {
+			return request({
+				url: `/menu/mod`,
+				method: 'post',
+				data:value
+			});
+		},
+		// getMenuTest: (params?: string) => {
+		// 	return request({
+		// 		url: '/gitee/lyt-top/vue-next-admin-images/raw/master/menu/testMenu.json',
+		// 		method: 'get',
+		// 		params,
+		// 	});
+		// },
+	};
+}
diff --git a/src/api/role/index.ts b/src/api/role/index.ts
new file mode 100644
index 0000000..137195d
--- /dev/null
+++ b/src/api/role/index.ts
@@ -0,0 +1,12 @@
+import request from '/@/utils/request';
+
+export function useRoleApi() {
+    return {
+        getRoleList: () => {
+            return request({
+                url: `/role/list`,
+                method: 'post',
+            });
+        },
+    };
+}
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
diff --git a/src/assets/logo-mini.svg b/src/assets/logo-mini.svg
new file mode 100644
index 0000000..53df94c
--- /dev/null
+++ b/src/assets/logo-mini.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
diff --git a/src/components/auth/auth.vue b/src/components/auth/auth.vue
new file mode 100644
index 0000000..ae010d4
--- /dev/null
+++ b/src/components/auth/auth.vue
@@ -0,0 +1,30 @@
+<template>
+	<slot v-if="getUserAuthBtnList" />
+</template>
+
+<script lang="ts">
+import { computed, defineComponent } from 'vue';
+import { storeToRefs } from 'pinia';
+import { useUserInfo } from '/@/stores/userInfo';
+
+export default defineComponent({
+	name: 'auth',
+	props: {
+		value: {
+			type: String,
+			default: () => '',
+		},
+	},
+	setup(props) {
+		const stores = useUserInfo();
+		const { userInfos } = storeToRefs(stores);
+		// 获取 vuex 中的用户权限
+		const getUserAuthBtnList = computed(() => {
+			return userInfos.value.authBtnList.some((v: string) => v === props.value);
+		});
+		return {
+			getUserAuthBtnList,
+		};
+	},
+});
+</script>
diff --git a/src/components/auth/authAll.vue b/src/components/auth/authAll.vue
new file mode 100644
index 0000000..76c5e01
--- /dev/null
+++ b/src/components/auth/authAll.vue
@@ -0,0 +1,31 @@
+<template>
+	<slot v-if="getUserAuthBtnList" />
+</template>
+
+<script lang="ts">
+import { computed, defineComponent } from 'vue';
+import { storeToRefs } from 'pinia';
+import { useUserInfo } from '/@/stores/userInfo';
+import { judementSameArr } from '/@/utils/arrayOperation';
+
+export default defineComponent({
+	name: 'authAll',
+	props: {
+		value: {
+			type: Array,
+			default: () => [],
+		},
+	},
+	setup(props) {
+		const stores = useUserInfo();
+		const { userInfos } = storeToRefs(stores);
+		// 获取 pinia 中的用户权限
+		const getUserAuthBtnList = computed(() => {
+			return judementSameArr(props.value, userInfos.value.authBtnList);
+		});
+		return {
+			getUserAuthBtnList,
+		};
+	},
+});
+</script>
diff --git a/src/components/auth/auths.vue b/src/components/auth/auths.vue
new file mode 100644
index 0000000..ef31019
--- /dev/null
+++ b/src/components/auth/auths.vue
@@ -0,0 +1,36 @@
+<template>
+	<slot v-if="getUserAuthBtnList" />
+</template>
+
+<script lang="ts">
+import { computed, defineComponent } from 'vue';
+import { storeToRefs } from 'pinia';
+import { useUserInfo } from '/@/stores/userInfo';
+
+export default defineComponent({
+	name: 'auths',
+	props: {
+		value: {
+			type: Array,
+			default: () => [],
+		},
+	},
+	setup(props) {
+		const stores = useUserInfo();
+		const { userInfos } = storeToRefs(stores);
+		// 获取 vuex 中的用户权限
+		const getUserAuthBtnList = computed(() => {
+			let flag = false;
+			userInfos.value.authBtnList.map((val: string) => {
+				props.value.map((v) => {
+					if (val === v) flag = true;
+				});
+			});
+			return flag;
+		});
+		return {
+			getUserAuthBtnList,
+		};
+	},
+});
+</script>
diff --git a/src/components/cropper/index.vue b/src/components/cropper/index.vue
new file mode 100644
index 0000000..b23a266
--- /dev/null
+++ b/src/components/cropper/index.vue
@@ -0,0 +1,149 @@
+<template>
+	<div>
+		<el-dialog title="更换头像" v-model="isShowDialog" width="769px">
+			<div class="cropper-warp">
+				<div class="cropper-warp-left">
+					<img :src="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="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="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 lang="ts">
+import { reactive, toRefs, nextTick, defineComponent } from 'vue';
+import Cropper from 'cropperjs';
+import 'cropperjs/dist/cropper.css';
+
+export default defineComponent({
+	name: 'cropperIndex',
+	setup() {
+		const state = reactive({
+			isShowDialog: false,
+			cropperImg: '',
+			cropperImgBase64: '',
+			cropper: null,
+		});
+		// 打开弹窗
+		const openDialog = (imgs: any) => {
+			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: any = document.querySelector('.cropper-warp-left-img');
+			(<any>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 = (<any>state.cropper).getCroppedCanvas().toDataURL('image/jpeg');
+				},
+			});
+		};
+		return {
+			openDialog,
+			closeDialog,
+			onCancel,
+			onSubmit,
+			initCropper,
+			...toRefs(state),
+		};
+	},
+});
+</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>
diff --git a/src/components/editor/index.vue b/src/components/editor/index.vue
new file mode 100644
index 0000000..78e5726
--- /dev/null
+++ b/src/components/editor/index.vue
@@ -0,0 +1,115 @@
+<template>
+	<div class="editor-container">
+		<div ref="editorToolbar"></div>
+		<div ref="editorContent" :style="{ height }"></div>
+	</div>
+</template>
+
+<script lang="ts">
+import { toRefs, reactive, onMounted, watch, defineComponent } from 'vue';
+import { createEditor, createToolbar, IEditorConfig, IToolbarConfig, IDomEditor } from '@wangeditor/editor';
+import '@wangeditor/editor/dist/css/style.css';
+import { toolbarKeys } from './toolbar';
+
+// 定义接口来定义对象的类型
+interface WangeditorState {
+	editorToolbar: HTMLDivElement | null;
+	editorContent: HTMLDivElement | null;
+	editor: any;
+}
+
+export default defineComponent({
+	name: 'wngEditor',
+	props: {
+		// 节点 id
+		id: {
+			type: String,
+			default: () => 'wangeditor',
+		},
+		// 是否禁用
+		isDisable: {
+			type: Boolean,
+			default: () => false,
+		},
+		// 内容框默认 placeholder
+		placeholder: {
+			type: String,
+			default: () => '请输入内容',
+		},
+		// 双向绑定:双向绑定值,字段名为固定,改了之后将不生效
+		// 参考:https://v3.cn.vuejs.org/guide/migration/v-model.html#%E8%BF%81%E7%A7%BB%E7%AD%96%E7%95%A5
+		modelValue: String,
+		// 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',
+		},
+	},
+	setup(props, { emit }) {
+		const state = reactive<WangeditorState>({
+			editorToolbar: null,
+			editor: null,
+			editorContent: null,
+		});
+		// 富文本配置
+		const wangeditorConfig = () => {
+			const editorConfig: Partial<IEditorConfig> = { MENU_CONF: {} };
+			props.isDisable ? (editorConfig.readOnly = true) : (editorConfig.readOnly = false);
+			editorConfig.placeholder = props.placeholder;
+			editorConfig.onChange = (editor: IDomEditor) => {
+				// console.log('content', editor.children);
+				// console.log('html', editor.getHtml());
+				emit('update:modelValue', editor.getHtml());
+			};
+			(<any>editorConfig).MENU_CONF['uploadImage'] = {
+				base64LimitSize: 10 * 1024 * 1024,
+			};
+			return editorConfig;
+		};
+		//
+		const toolbarConfig = () => {
+			const toolbarConfig: Partial<IToolbarConfig> = {};
+			toolbarConfig.toolbarKeys = toolbarKeys;
+			return toolbarConfig;
+		};
+		// 初始化富文本
+		// https://www.wangeditor.com/
+		const initWangeditor = () => {
+			state.editor = createEditor({
+				html: props.modelValue,
+				selector: state.editorContent!,
+				config: wangeditorConfig(),
+				mode: props.mode,
+			});
+			createToolbar({
+				editor: state.editor,
+				selector: state.editorToolbar!,
+				mode: props.mode,
+				config: toolbarConfig(),
+			});
+		};
+		// 页面加载时
+		onMounted(() => {
+			initWangeditor();
+		});
+		// 监听双向绑定值的改变
+		// https://gitee.com/lyt-top/vue-next-admin/issues/I4LM7I
+		watch(
+			() => props.modelValue,
+			(value) => {
+				state.editor.clear();
+				state.editor.dangerouslyInsertHtml(value);
+			}
+		);
+		return {
+			...toRefs(state),
+		};
+	},
+});
+</script>
diff --git a/src/components/editor/toolbar.ts b/src/components/editor/toolbar.ts
new file mode 100644
index 0000000..14a9bf3
--- /dev/null
+++ b/src/components/editor/toolbar.ts
@@ -0,0 +1,60 @@
+/**
+ * 工具栏配置
+ */
+export const toolbarKeys = [
+	'headerSelect',
+	'blockquote',
+	'|',
+	'bold',
+	'underline',
+	'italic',
+	{
+		key: 'group-more-style',
+		title: '更多',
+		iconSvg:
+			'<svg viewBox="0 0 1024 1024"><path d="M204.8 505.6m-76.8 0a76.8 76.8 0 1 0 153.6 0 76.8 76.8 0 1 0-153.6 0Z"></path><path d="M505.6 505.6m-76.8 0a76.8 76.8 0 1 0 153.6 0 76.8 76.8 0 1 0-153.6 0Z"></path><path d="M806.4 505.6m-76.8 0a76.8 76.8 0 1 0 153.6 0 76.8 76.8 0 1 0-153.6 0Z"></path></svg>',
+		menuKeys: ['through', 'code', 'sup', 'sub', 'clearStyle'],
+	},
+	'color',
+	'bgColor',
+	'|',
+	'fontSize',
+	'fontFamily',
+	'lineHeight',
+	'|',
+	'bulletedList',
+	'numberedList',
+	'todo',
+	{
+		key: 'group-justify',
+		title: '对齐',
+		iconSvg:
+			'<svg viewBox="0 0 1024 1024"><path d="M768 793.6v102.4H51.2v-102.4h716.8z m204.8-230.4v102.4H51.2v-102.4h921.6z m-204.8-230.4v102.4H51.2v-102.4h716.8zM972.8 102.4v102.4H51.2V102.4h921.6z"></path></svg>',
+		menuKeys: ['justifyLeft', 'justifyRight', 'justifyCenter', 'justifyJustify'],
+	},
+	{
+		key: 'group-indent',
+		title: '缩进',
+		iconSvg:
+			'<svg viewBox="0 0 1024 1024"><path d="M0 64h1024v128H0z m384 192h640v128H384z m0 192h640v128H384z m0 192h640v128H384zM0 832h1024v128H0z m0-128V320l256 192z"></path></svg>',
+		menuKeys: ['indent', 'delIndent'],
+	},
+	'|',
+	'emotion',
+	'insertLink',
+	{
+		key: 'group-image',
+		title: '图片',
+		iconSvg:
+			'<svg viewBox="0 0 1024 1024"><path d="M959.877 128l0.123 0.123v767.775l-0.123 0.122H64.102l-0.122-0.122V128.123l0.122-0.123h895.775zM960 64H64C28.795 64 0 92.795 0 128v768c0 35.205 28.795 64 64 64h896c35.205 0 64-28.795 64-64V128c0-35.205-28.795-64-64-64zM832 288.01c0 53.023-42.988 96.01-96.01 96.01s-96.01-42.987-96.01-96.01S682.967 192 735.99 192 832 234.988 832 288.01zM896 832H128V704l224.01-384 256 320h64l224.01-192z"></path></svg>',
+		menuKeys: ['uploadImage'],
+	},
+	'insertTable',
+	'codeBlock',
+	'divider',
+	'|',
+	'undo',
+	'redo',
+	'|',
+	'fullScreen',
+];
diff --git a/src/components/iconSelector/index.vue b/src/components/iconSelector/index.vue
new file mode 100644
index 0000000..07de786
--- /dev/null
+++ b/src/components/iconSelector/index.vue
@@ -0,0 +1,252 @@
+<template>
+	<div class="icon-selector w100 h100">
+		<el-popover
+			placement="bottom"
+			:width="fontIconWidth"
+			trigger="click"
+			transition="el-zoom-in-top"
+			popper-class="icon-selector-popper"
+			@show="onPopoverShow"
+		>
+			<template #reference>
+				<el-input
+					v-model="fontIconSearch"
+					:placeholder="fontIconPlaceholder"
+					:clearable="clearable"
+					:disabled="disabled"
+					:size="size"
+					ref="inputWidthRef"
+					@clear="onClearFontIcon"
+					@focus="onIconFocus"
+					@blur="onIconBlur"
+				>
+					<template #prepend>
+						<SvgIcon
+							:name="fontIconPrefix === '' ? prepend : fontIconPrefix"
+							class="font14"
+							v-if="fontIconPrefix === '' ? prepend?.indexOf('ele-') > -1 : fontIconPrefix?.indexOf('ele-') > -1"
+						/>
+						<i v-else :class="fontIconPrefix === '' ? prepend : fontIconPrefix" class="font14"></i>
+					</template>
+				</el-input>
+			</template>
+			<template #default>
+				<div class="icon-selector-warp">
+					<div class="icon-selector-warp-title flex">
+						<div class="flex-auto">{{ title }}</div>
+						<div class="icon-selector-warp-title-tab" v-if="type === 'all'">
+							<span :class="{ 'span-active': fontIconType === 'ali' }" @click="onIconChange('ali')" class="ml10" title="iconfont 图标">ali</span>
+							<span :class="{ 'span-active': fontIconType === 'ele' }" @click="onIconChange('ele')" class="ml10" title="elementPlus 图标">ele</span>
+							<span :class="{ 'span-active': fontIconType === 'awe' }" @click="onIconChange('awe')" class="ml10" title="fontawesome 图标">awe</span>
+						</div>
+					</div>
+					<div class="icon-selector-warp-row">
+						<el-scrollbar ref="selectorScrollbarRef">
+							<el-row :gutter="10" v-if="fontIconSheetsFilterList.length > 0">
+								<el-col :xs="6" :sm="4" :md="4" :lg="4" :xl="4" @click="onColClick(v)" v-for="(v, k) in fontIconSheetsFilterList" :key="k">
+									<div class="icon-selector-warp-item" :class="{ 'icon-selector-active': fontIconPrefix === v }">
+										<div class="flex-margin">
+											<div class="icon-selector-warp-item-value">
+												<SvgIcon :name="v" />
+											</div>
+										</div>
+									</div>
+								</el-col>
+							</el-row>
+							<el-empty :image-size="100" v-if="fontIconSheetsFilterList.length <= 0" :description="emptyDescription"></el-empty>
+						</el-scrollbar>
+					</div>
+				</div>
+			</template>
+		</el-popover>
+	</div>
+</template>
+
+<script lang="ts">
+import { ref, toRefs, reactive, onMounted, nextTick, computed, watch, defineComponent } from 'vue';
+import initIconfont from '/@/utils/getStyleSheets';
+
+export default defineComponent({
+	name: 'iconSelector',
+	emits: ['update:modelValue', 'get', 'clear'],
+	props: {
+		// 输入框前置内容
+		prepend: {
+			type: String,
+			default: () => 'ele-Pointer',
+		},
+		// 输入框占位文本
+		placeholder: {
+			type: String,
+			default: () => '请输入内容搜索图标或者选择图标',
+		},
+		// 输入框占位文本
+		size: {
+			type: String,
+			default: () => 'default',
+		},
+		// 弹窗标题
+		title: {
+			type: String,
+			default: () => '请选择图标',
+		},
+		// icon 图标类型
+		type: {
+			type: String,
+			default: () => 'ele',
+		},
+		// 禁用
+		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,
+	},
+	setup(props, { emit }) {
+		const inputWidthRef = ref();
+		const selectorScrollbarRef = ref();
+		const state = reactive({
+			fontIconPrefix: '',
+			fontIconWidth: 0,
+			fontIconSearch: '',
+			fontIconTabsIndex: 0,
+			fontIconSheetsList: [],
+			fontIconPlaceholder: '',
+			fontIconType: 'ali',
+			fontIconShow: true,
+		});
+		// 处理 input 获取焦点时,modelValue 有值时,改变 input 的 placeholder 值
+		const onIconFocus = () => {
+			if (!props.modelValue) return false;
+			state.fontIconSearch = '';
+			state.fontIconPlaceholder = props.modelValue;
+		};
+		// 处理 input 失去焦点时,为空将清空 input 值,为点击选中图标时,将取原先值
+		const onIconBlur = () => {
+			setTimeout(() => {
+				const icon = state.fontIconSheetsList.filter((icon: string) => icon === state.fontIconSearch);
+				if (icon.length <= 0) state.fontIconSearch = '';
+			}, 300);
+		};
+		// 处理 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 type 类型为 all 时,类型 ali、ele、awe 回显问题
+		const initFontIconTypeEcho = () => {
+			if ((<any>props.modelValue)?.indexOf('iconfont') > -1) onIconChange('ali');
+			else if ((<any>props.modelValue)?.indexOf('ele-') > -1) onIconChange('ele');
+			else if ((<any>props.modelValue)?.indexOf('fa') > -1) onIconChange('awe');
+			else onIconChange('ali');
+		};
+		// 图标搜索及图标数据显示
+		const fontIconSheetsFilterList = computed(() => {
+			if (!state.fontIconSearch) return state.fontIconSheetsList;
+			let search = state.fontIconSearch.trim().toLowerCase();
+			return state.fontIconSheetsList.filter((item: any) => {
+				if (item.toLowerCase().indexOf(search) !== -1) return item;
+			});
+		});
+		// 获取 input 的宽度
+		const getInputWidth = () => {
+			nextTick(() => {
+				state.fontIconWidth = inputWidthRef.value.$el.offsetWidth;
+			});
+		};
+		// 监听页面宽度改变
+		const initResize = () => {
+			window.addEventListener('resize', () => {
+				getInputWidth();
+			});
+		};
+		// 初始化数据
+		const initFontIconData = async (type: string) => {
+			state.fontIconSheetsList = [];
+			if (type === 'ali') {
+				await initIconfont.ali().then((res: any) => {
+					// 阿里字体图标使用 `iconfont xxx`
+					state.fontIconSheetsList = res.map((i: string) => `iconfont ${i}`);
+				});
+			} else if (type === 'ele') {
+				await initIconfont.ele().then((res: any) => {
+					state.fontIconSheetsList = res;
+				});
+			} else if (type === 'awe') {
+				await initIconfont.awe().then((res: any) => {
+					// fontawesome字体图标使用 `fa xxx`
+					state.fontIconSheetsList = 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 onIconChange = (type: string) => {
+			state.fontIconType = type;
+			initFontIconData(type);
+		};
+		// 获取当前点击的 icon 图标
+		const onColClick = (v: any) => {
+			state.fontIconPlaceholder = v;
+			state.fontIconPrefix = v;
+			emit('get', state.fontIconPrefix);
+			emit('update:modelValue', state.fontIconPrefix);
+		};
+		// 清空当前点击的 icon 图标
+		const onClearFontIcon = () => {
+			state.fontIconPrefix = '';
+			emit('clear', state.fontIconPrefix);
+			emit('update:modelValue', state.fontIconPrefix);
+		};
+		// 监听 Popover 打开,用于双向绑定值回显
+		const onPopoverShow = () => {
+			initModeValueEcho();
+			initFontIconTypeEcho();
+		};
+		// 页面加载时
+		onMounted(() => {
+			initModeValueEcho();
+			initResize();
+			getInputWidth();
+		});
+
+		// 监听双向绑定 modelValue 的变化
+		watch(
+			() => props.modelValue,
+			() => {
+				initModeValueEcho();
+			}
+		);
+		return {
+			inputWidthRef,
+			selectorScrollbarRef,
+			fontIconSheetsFilterList,
+			onColClick,
+			onIconChange,
+			onClearFontIcon,
+			onIconFocus,
+			onIconBlur,
+			onPopoverShow,
+			...toRefs(state),
+		};
+	},
+});
+</script>
diff --git a/src/components/noticeBar/index.vue b/src/components/noticeBar/index.vue
new file mode 100644
index 0000000..6a81bfd
--- /dev/null
+++ b/src/components/noticeBar/index.vue
@@ -0,0 +1,195 @@
+<template>
+	<div class="notice-bar" :style="{ background, height: `${height}px` }" v-show="!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 lang="ts">
+import { toRefs, reactive, defineComponent, ref, onMounted, nextTick } from 'vue';
+
+export default defineComponent({
+	name: 'noticeBar',
+	props: {
+		// 通知栏模式,可选值为 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: () => '',
+		},
+	},
+	setup(props, { emit }) {
+		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();
+		});
+		return {
+			noticeBarWarpRef,
+			noticeBarTextRef,
+			onRightIconClick,
+			...toRefs(state),
+		};
+	},
+});
+</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;
+				::v-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>
diff --git a/src/components/svgIcon/index.vue b/src/components/svgIcon/index.vue
new file mode 100644
index 0000000..1fab298
--- /dev/null
+++ b/src/components/svgIcon/index.vue
@@ -0,0 +1,73 @@
+<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>
+	<i v-else :class="getIconName" :style="setIconSvgStyle" />
+</template>
+
+<script lang="ts">
+import { computed, defineComponent } from 'vue';
+
+export default defineComponent({
+	name: 'svgIcon',
+	props: {
+		// svg 图标组件名字
+		name: {
+			type: String,
+		},
+		// svg 大小
+		size: {
+			type: Number,
+			default: () => 14,
+		},
+		// svg 颜色
+		color: {
+			type: String,
+		},
+	},
+	setup(props) {
+		// 在线链接、本地引入地址前缀
+		const linesString = ['https', 'http', '/src', '/assets', 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('')}`;
+		});
+		return {
+			getIconName,
+			isShowIconSvg,
+			isShowIconImg,
+			setIconSvgStyle,
+			setIconImgOutStyle,
+			setIconSvgInsStyle,
+		};
+	},
+});
+</script>
diff --git a/src/i18n/index.ts b/src/i18n/index.ts
new file mode 100644
index 0000000..a45e72c
--- /dev/null
+++ b/src/i18n/index.ts
@@ -0,0 +1,67 @@
+import { createI18n } from 'vue-i18n';
+import pinia from '/@/stores/index';
+import { storeToRefs } from 'pinia';
+import { useThemeConfig } from '/@/stores/themeConfig';
+import zhcnLocale from 'element-plus/lib/locale/lang/zh-cn';
+import enLocale from 'element-plus/lib/locale/lang/en';
+import zhtwLocale from 'element-plus/lib/locale/lang/zh-tw';
+
+import nextZhcn from '/@/i18n/lang/zh-cn';
+import nextEn from '/@/i18n/lang/en';
+import nextZhtw from '/@/i18n/lang/zh-tw';
+
+import pagesLoginZhcn from '/@/i18n/pages/login/zh-cn';
+import pagesLoginEn from '/@/i18n/pages/login/en';
+import pagesLoginZhtw from '/@/i18n/pages/login/zh-tw';
+import pagesFormI18nZhcn from '/@/i18n/pages/formI18n/zh-cn';
+import pagesFormI18nEn from '/@/i18n/pages/formI18n/en';
+import pagesFormI18nZhtw from '/@/i18n/pages/formI18n/zh-tw';
+
+// 定义语言国际化内容
+/**
+ * 说明:
+ * /src/i18n/lang 下的 ts 为框架的国际化内容
+ * /src/i18n/pages 下的 ts 为各界面的国际化内容
+ */
+const messages = {
+	[zhcnLocale.name]: {
+		...zhcnLocale,
+		message: {
+			...nextZhcn,
+			...pagesLoginZhcn,
+			...pagesFormI18nZhcn,
+		},
+	},
+	[enLocale.name]: {
+		...enLocale,
+		message: {
+			...nextEn,
+			...pagesLoginEn,
+			...pagesFormI18nEn,
+		},
+	},
+	[zhtwLocale.name]: {
+		...zhtwLocale,
+		message: {
+			...nextZhtw,
+			...pagesLoginZhtw,
+			...pagesFormI18nZhtw,
+		},
+	},
+};
+
+// 读取 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({
+	silentTranslationWarn: true,
+	missingWarn: false,
+	silentFallbackWarn: true,
+	fallbackWarn: false,
+	locale: themeConfig.value.globalI18n,
+	fallbackLocale: zhcnLocale.name,
+	messages,
+});
diff --git a/src/i18n/lang/en.ts b/src/i18n/lang/en.ts
new file mode 100644
index 0000000..46ae830
--- /dev/null
+++ b/src/i18n/lang/en.ts
@@ -0,0 +1,180 @@
+// 定义内容
+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',
+		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',
+		layoutIfameView: 'IfameView',
+	},
+	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',
+		twoIsMenuBarColorGradual: 'Menu gradient',
+		twoColumnsMenuBar: 'Column menu background',
+		twoColumnsMenuBarColor: 'Default font color bar menu',
+		twoIsColumnsMenuBarColorGradual: 'Column gradient',
+		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!',
+	},
+};
diff --git a/src/i18n/lang/zh-cn.ts b/src/i18n/lang/zh-cn.ts
new file mode 100644
index 0000000..79ef328
--- /dev/null
+++ b/src/i18n/lang/zh-cn.ts
@@ -0,0 +1,180 @@
+// 定义内容
+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 演示',
+		paramsIndex: '路由参数',
+		paramsCommon: '普通路由',
+		paramsDynamic: '动态路由',
+		paramsCommonDetails: '普通路由详情',
+		paramsDynamicDetails: '动态路由详情',
+		chartIndex: '大数据图表',
+		visualizingIndex: '数据可视化',
+		visualizingLinkDemo1: '数据可视化演示1',
+		visualizingLinkDemo2: '数据可视化演示2',
+		personal: '个人中心',
+		tools: '工具类集合',
+		layoutLinkView: '外链',
+		layoutIfameView: '内嵌 iframe',
+	},
+	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: '菜单默认字体颜色',
+		twoIsMenuBarColorGradual: '菜单背景渐变',
+		twoColumnsMenuBar: '分栏菜单背景',
+		twoColumnsMenuBarColor: '分栏菜单默认字体颜色',
+		twoIsColumnsMenuBarColorGradual: '分栏菜单背景渐变',
+		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: '复制失败!',
+	},
+};
diff --git a/src/i18n/lang/zh-tw.ts b/src/i18n/lang/zh-tw.ts
new file mode 100644
index 0000000..d900abb
--- /dev/null
+++ b/src/i18n/lang/zh-tw.ts
@@ -0,0 +1,180 @@
+// 定义内容
+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 演示',
+		paramsIndex: '路由參數',
+		paramsCommon: '普通路由',
+		paramsDynamic: '動態路由',
+		paramsCommonDetails: '普通路由詳情',
+		paramsDynamicDetails: '動態路由詳情',
+		chartIndex: '大資料圖表',
+		visualizingIndex: '數據視覺化',
+		visualizingLinkDemo1: '數據視覺化演示1',
+		visualizingLinkDemo2: '數據視覺化演示2',
+		personal: '個人中心',
+		tools: '工具類集合',
+		layoutLinkView: '外鏈',
+		layoutIfameView: '内嵌 iframe',
+	},
+	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: '選單默認字體顏色',
+		twoIsMenuBarColorGradual: '選單背景漸變',
+		twoColumnsMenuBar: '分欄選單背景',
+		twoColumnsMenuBarColor: '分欄選單默認字體顏色',
+		twoIsColumnsMenuBarColorGradual: '分欄選單背景漸變',
+		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: '複製失敗!',
+	},
+};
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',
+	},
+};
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: '请输入登陆账户名',
+	},
+};
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: '請輸入登入帳戶名',
+	},
+};
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!',
+};
diff --git a/src/i18n/pages/login/zh-cn.ts b/src/i18n/pages/login/zh-cn.ts
new file mode 100644
index 0000000..3367b53
--- /dev/null
+++ b/src/i18n/pages/login/zh-cn.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: '欢迎回来!',
+};
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: '歡迎回來!',
+};
diff --git a/src/layout/component/aside.vue b/src/layout/component/aside.vue
new file mode 100644
index 0000000..d4ee363
--- /dev/null
+++ b/src/layout/component/aside.vue
@@ -0,0 +1,163 @@
+<template>
+	<div class="h100" v-show="!isTagsViewCurrenFull">
+		<el-aside class="layout-aside" :class="setCollapseStyle">
+			<Logo v-if="setShowLogo" />
+			<el-scrollbar class="flex-auto" ref="layoutAsideScrollbarRef" @mouseenter="onAsideEnterLeave(true)" @mouseleave="onAsideEnterLeave(false)">
+				<Vertical :menuList="menuList" />
+			</el-scrollbar>
+		</el-aside>
+	</div>
+</template>
+
+<script lang="ts">
+import { toRefs, reactive, computed, watch, getCurrentInstance, onBeforeMount, defineComponent } 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 Logo from '/@/layout/logo/index.vue';
+import Vertical from '/@/layout/navMenu/vertical.vue';
+
+export default defineComponent({
+	name: 'layoutAside',
+	components: { Logo, Vertical },
+	setup() {
+		const { proxy } = <any>getCurrentInstance();
+		const stores = useRoutesList();
+		const storesThemeConfig = useThemeConfig();
+		const storesTagsViewRoutes = useTagsViewRoutes();
+		const { routesList } = storeToRefs(stores);
+		const { themeConfig } = storeToRefs(storesThemeConfig);
+		const { isTagsViewCurrenFull } = storeToRefs(storesTagsViewRoutes);
+		const state = reactive({
+			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'];
+				}
+			}
+		});
+		// 关闭移动端蒙版
+		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', '');
+		};
+		// 设置显示/隐藏 logo
+		const setShowLogo = computed(() => {
+			let { layout, isShowLogo } = themeConfig.value;
+			return (isShowLogo && layout === 'defaults') || (isShowLogo && layout === 'columns');
+		});
+		// 设置/过滤路由(非静态路由/是否显示在菜单中)
+		const setFilterRoutes = () => {
+			if (themeConfig.value.layout === 'columns') return false;
+			(state.menuList as any) = filterRoutesFun(routesList.value);
+		};
+		// 路由过滤递归函数
+		const filterRoutesFun = (arr: Array<string>) => {
+			return arr
+				.filter((item: any) => !item.meta.isHide)
+				.map((item: any) => {
+					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) proxy.mittBus.emit('restoreDefault');
+			stores.setColumnsMenuHover(bool);
+		};
+		// 监听 themeConfig 配置文件的变化,更新菜单 el-scrollbar 的高度
+		watch(themeConfig.value, (val) => {
+			if (val.isShowLogoChange !== val.isShowLogo) {
+				if (!proxy.$refs.layoutAsideScrollbarRef) return false;
+				proxy.$refs.layoutAsideScrollbarRef.update();
+			}
+		});
+		// 监听vuex值的变化,动态赋值给菜单中
+		watch(
+			pinia.state,
+			(val) => {
+				let { layout, isClassicSplitMenu } = val.themeConfig.themeConfig;
+				if (layout === 'classic' && isClassicSplitMenu) return false;
+				setFilterRoutes();
+			},
+			{
+				deep: true,
+			}
+		);
+		// 页面加载前
+		onBeforeMount(() => {
+			initMenuFixed(document.body.clientWidth);
+			setFilterRoutes();
+			// 此界面不需要取消监听(proxy.mittBus.off('setSendColumnsChildren))
+			// 因为切换布局时有的监听需要使用,取消了监听,某些操作将不生效
+			proxy.mittBus.on('setSendColumnsChildren', (res: any) => {
+				state.menuList = res.children;
+			});
+			proxy.mittBus.on('setSendClassicChildren', (res: any) => {
+				let { layout, isClassicSplitMenu } = themeConfig.value;
+				if (layout === 'classic' && isClassicSplitMenu) {
+					state.menuList = [];
+					state.menuList = res.children;
+				}
+			});
+			proxy.mittBus.on('getBreadcrumbIndexSetFilterRoutes', () => {
+				setFilterRoutes();
+			});
+			proxy.mittBus.on('layoutMobileResize', (res: any) => {
+				initMenuFixed(res.clientWidth);
+				closeLayoutAsideMobileMode();
+			});
+		});
+		return {
+			setCollapseStyle,
+			setShowLogo,
+			isTagsViewCurrenFull,
+			onAsideEnterLeave,
+			...toRefs(state),
+		};
+	},
+});
+</script>
diff --git a/src/layout/component/columnsAside.vue b/src/layout/component/columnsAside.vue
new file mode 100644
index 0000000..7fd7806
--- /dev/null
+++ b/src/layout/component/columnsAside.vue
@@ -0,0 +1,292 @@
+<template>
+	<div class="layout-columns-aside">
+		<el-scrollbar>
+			<ul @mouseleave="onColumnsAsideMenuMouseleave()">
+				<li
+					v-for="(v, k) in columnsAsideList"
+					:key="k"
+					@click="onColumnsAsideMenuClick(v, k)"
+					@mouseenter="onColumnsAsideMenuMouseenter(v, k)"
+					:ref="
+						(el) => {
+							if (el) columnsAsideOffsetTopRefs[k] = el;
+						}
+					"
+					:class="{ 'layout-columns-active': liIndex === k, 'layout-columns-hover': 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 lang="ts">
+import { reactive, toRefs, ref, onMounted, nextTick, getCurrentInstance, watch, onUnmounted, defineComponent } 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';
+
+// 定义接口来定义对象的类型
+interface ColumnsAsideState {
+	columnsAsideList: any[];
+	liIndex: number;
+	liOldIndex: null | number;
+	liHoverIndex: null | number;
+	liOldPath: null | string;
+	difference: number;
+	routeSplit: string[];
+}
+
+export default defineComponent({
+	name: 'layoutColumnsAside',
+	setup() {
+		const columnsAsideOffsetTopRefs: any = ref([]);
+		const columnsAsideActiveRef = ref();
+		const { proxy } = <any>getCurrentInstance();
+		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) => {
+			state.liIndex = k;
+			columnsAsideActiveRef.value.style.top = `${columnsAsideOffsetTopRefs.value[k].offsetTop + state.difference}px`;
+		};
+		// 菜单高亮点击事件
+		const onColumnsAsideMenuClick = (v: Object, k: number) => {
+			setColumnsAsideMove(k);
+			let { path, redirect } = v as any;
+			if (redirect) router.push(redirect);
+			else router.push(path);
+		};
+		// 鼠标移入时,显示当前的子级菜单
+		const onColumnsAsideMenuMouseenter = (v: RouteRecordRaw, k: number) => {
+			let { path } = v;
+			state.liOldPath = path;
+			state.liOldIndex = k;
+			state.liHoverIndex = k;
+			proxy.mittBus.emit('setSendColumnsChildren', setSendChildren(path));
+			stores.setColumnsMenuHover(false);
+			stores.setColumnsNavHover(true);
+		};
+		// 鼠标移走时,显示原来的子级菜单
+		const onColumnsAsideMenuMouseleave = async () => {
+			await stores.setColumnsNavHover(false);
+			// 添加延时器,防止拿到的 store.state.routesList 值不是最新的
+			setTimeout(() => {
+				if (!isColumnsMenuHover && !isColumnsNavHover) proxy.mittBus.emit('restoreDefault');
+			}, 100);
+		};
+		// 设置高亮动态位置
+		const onColumnsAsideDown = (k: number) => {
+			nextTick(() => {
+				setColumnsAsideMove(k);
+			});
+		};
+		// 设置/过滤路由(非静态路由/是否显示在菜单中)
+		const setFilterRoutes = () => {
+			state.columnsAsideList = filterRoutesFun(routesList.value);
+			const resData: any = setSendChildren(route.path);
+			if (Object.keys(resData).length <= 0) return false;
+			onColumnsAsideDown(resData.item[0].k);
+			proxy.mittBus.emit('setSendColumnsChildren', resData);
+		};
+		// 传送当前子级数据到菜单中
+		const setSendChildren = (path: string) => {
+			const currentPathSplit = path.split('/');
+			let currentData: any = {};
+			state.columnsAsideList.map((v: any, 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 = (arr: Array<string>) => {
+			return arr
+				.filter((item: any) => !item.meta.isHide)
+				.map((item: any) => {
+					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: any) => v.path === routeFirst);
+			if (!currentSplitRoute) return false;
+			// 延迟拿值,防止取不到
+			setTimeout(() => {
+				onColumnsAsideDown((<any>currentSplitRoute).k);
+			}, 0);
+		};
+		// 监听布局配置信息的变化,动态增加菜单高亮位置移动像素
+		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;
+					proxy.mittBus.emit('setSendColumnsChildren', setSendChildren(route.path));
+				} else {
+					state.liHoverIndex = state.liOldIndex;
+					if (!state.liOldPath) return false;
+					proxy.mittBus.emit('setSendColumnsChildren', setSendChildren(state.liOldPath));
+				}
+			},
+			{
+				deep: true,
+			}
+		);
+		// 页面加载时
+		onMounted(() => {
+			setFilterRoutes();
+			// 销毁变量,防止鼠标再次移入时,保留了上次的记录
+			proxy.mittBus.on('restoreDefault', () => {
+				state.liOldIndex = null;
+				state.liOldPath = null;
+			});
+		});
+		// 页面卸载时
+		onUnmounted(() => {
+			proxy.mittBus.off('restoreDefault', () => {});
+		});
+		// 路由更新时
+		onBeforeRouteUpdate((to) => {
+			setColumnsMenuHighlight(to.path);
+			proxy.mittBus.emit('setSendColumnsChildren', setSendChildren(to.path));
+		});
+		return {
+			themeConfig,
+			columnsAsideOffsetTopRefs,
+			columnsAsideActiveRef,
+			onColumnsAsideDown,
+			onColumnsAsideMenuClick,
+			onColumnsAsideMenuMouseenter,
+			onColumnsAsideMenuMouseleave,
+			...toRefs(state),
+		};
+	},
+});
+</script>
+
+<style scoped lang="scss">
+.layout-columns-aside {
+	width: 70px;
+	height: 100%;
+	background: var(--next-bg-columnsMenuBar);
+	ul {
+		position: relative;
+		li {
+			color: var(--next-bg-columnsMenuBarColor);
+			width: 100%;
+			height: 50px;
+			text-align: center;
+			display: flex;
+			cursor: pointer;
+			position: relative;
+			z-index: 1;
+			.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);
+			}
+		}
+		.layout-columns-active {
+			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);
+			}
+		}
+		.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>
diff --git a/src/layout/component/header.vue b/src/layout/component/header.vue
new file mode 100644
index 0000000..21c9e2d
--- /dev/null
+++ b/src/layout/component/header.vue
@@ -0,0 +1,34 @@
+<template>
+	<el-header class="layout-header" :height="setHeaderHeight" v-show="!isTagsViewCurrenFull">
+		<NavBarsIndex />
+	</el-header>
+</template>
+
+<script lang="ts">
+import { computed, defineComponent } from 'vue';
+import { storeToRefs } from 'pinia';
+import { useThemeConfig } from '/@/stores/themeConfig';
+import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
+import NavBarsIndex from '/@/layout/navBars/index.vue';
+
+export default defineComponent({
+	name: 'layoutHeader',
+	components: { NavBarsIndex },
+	setup() {
+		const storesTagsViewRoutes = useTagsViewRoutes();
+		const storesThemeConfig = useThemeConfig();
+		const { themeConfig } = storeToRefs(storesThemeConfig);
+		const { isTagsViewCurrenFull } = storeToRefs(storesTagsViewRoutes);
+		// 设置 header 的高度
+		const setHeaderHeight = computed(() => {
+			let { isTagsview, layout } = themeConfig.value;
+			if (isTagsview && layout !== 'classic') return '84px';
+			else return '50px';
+		});
+		return {
+			setHeaderHeight,
+			isTagsViewCurrenFull,
+		};
+	},
+});
+</script>
diff --git a/src/layout/component/main.vue b/src/layout/component/main.vue
new file mode 100644
index 0000000..18eba60
--- /dev/null
+++ b/src/layout/component/main.vue
@@ -0,0 +1,101 @@
+<template>
+	<el-main class="layout-main">
+		<el-scrollbar
+			ref="layoutScrollbarRef"
+			:class="{
+				'layout-scrollbar':
+					(!isClassicOrTransverse && !currentRouteMeta.isLink && !currentRouteMeta.isIframe) ||
+					(!isClassicOrTransverse && currentRouteMeta.isLink && !currentRouteMeta.isIframe),
+			}"
+		>
+			<LayoutParentView
+				:style="{
+					padding: !isClassicOrTransverse || (currentRouteMeta.isLink && currentRouteMeta.isIframe) ? '0' : '15px',
+					transition: 'padding 0.3s ease-in-out',
+				}"
+			/>
+			<Footer v-if="themeConfig.isFooter" />
+		</el-scrollbar>
+	</el-main>
+</template>
+
+<script lang="ts">
+import { defineComponent, toRefs, reactive, getCurrentInstance, watch, onMounted, computed } from 'vue';
+import { useRoute } from 'vue-router';
+import { storeToRefs } from 'pinia';
+import { useThemeConfig } from '/@/stores/themeConfig';
+import { NextLoading } from '/@/utils/loading';
+import LayoutParentView from '/@/layout/routerView/parent.vue';
+import Footer from '/@/layout/footer/index.vue';
+
+// 定义接口来定义对象的类型
+interface MainState {
+	headerHeight: string | number;
+	currentRouteMeta: any;
+}
+
+export default defineComponent({
+	name: 'layoutMain',
+	components: { LayoutParentView, Footer },
+	setup() {
+		const { proxy } = <any>getCurrentInstance();
+		const storesThemeConfig = useThemeConfig();
+		const { themeConfig } = storeToRefs(storesThemeConfig);
+		const route = useRoute();
+		const state = reactive<MainState>({
+			headerHeight: '',
+			currentRouteMeta: {},
+		});
+		// 判断布局
+		const isClassicOrTransverse = computed(() => {
+			const { layout } = themeConfig.value;
+			return layout === 'classic' || layout === 'transverse';
+		});
+		// 设置 main 的高度
+		const initHeaderHeight = () => {
+			const bool = state.currentRouteMeta.isLink && state.currentRouteMeta.isIframe;
+			let { isTagsview } = themeConfig.value;
+			if (isTagsview) return (state.headerHeight = bool ? `86px` : `115px`);
+			else return (state.headerHeight = `80px`);
+		};
+		// 初始化获取当前路由 meta,用于设置 iframes padding
+		const initGetMeta = () => {
+			state.currentRouteMeta = route.meta;
+		};
+		// 页面加载前
+		onMounted(async () => {
+			await initGetMeta();
+			initHeaderHeight();
+			NextLoading.done();
+		});
+		// 监听路由变化
+		watch(
+			() => route.path,
+			() => {
+				state.currentRouteMeta = route.meta;
+				const bool = state.currentRouteMeta.isLink && state.currentRouteMeta.isIframe;
+				state.headerHeight = bool ? `86px` : `115px`;
+				proxy.$refs.layoutScrollbarRef.update();
+			}
+		);
+		// 监听 themeConfig 配置文件的变化,更新菜单 el-scrollbar 的高度
+		watch(
+			themeConfig,
+			(val) => {
+				state.currentRouteMeta = route.meta;
+				const bool = state.currentRouteMeta.isLink && state.currentRouteMeta.isIframe;
+				state.headerHeight = val.isTagsview ? (bool ? `86px` : `115px`) : '51px';
+				proxy.$refs?.layoutScrollbarRef?.update();
+			},
+			{
+				deep: true,
+			}
+		);
+		return {
+			themeConfig,
+			isClassicOrTransverse,
+			...toRefs(state),
+		};
+	},
+});
+</script>
diff --git a/src/layout/footer/index.vue b/src/layout/footer/index.vue
new file mode 100644
index 0000000..ef00591
--- /dev/null
+++ b/src/layout/footer/index.vue
@@ -0,0 +1,47 @@
+<template>
+	<div class="layout-footer mt15" v-show="isDelayFooter">
+		<div class="layout-footer-warp">
+			<div>vue-next-admin,Made by lyt with ❤️</div>
+			<div class="mt5">深圳市 xxx 公司版权所有</div>
+		</div>
+	</div>
+</template>
+
+<script lang="ts">
+import { toRefs, reactive, defineComponent } from 'vue';
+import { onBeforeRouteUpdate } from 'vue-router';
+
+export default defineComponent({
+	name: 'layoutFooter',
+	setup() {
+		const state = reactive({
+			isDelayFooter: true,
+		});
+		// 路由改变时,等主界面动画加载完毕再显示 footer
+		onBeforeRouteUpdate(() => {
+			setTimeout(() => {
+				state.isDelayFooter = false;
+				setTimeout(() => {
+					state.isDelayFooter = true;
+				}, 800);
+			}, 0);
+		});
+		return {
+			...toRefs(state),
+		};
+	},
+});
+</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 1s ease-in-out;
+	}
+}
+</style>
diff --git a/src/layout/index.vue b/src/layout/index.vue
new file mode 100644
index 0000000..50643a4
--- /dev/null
+++ b/src/layout/index.vue
@@ -0,0 +1,54 @@
+<template>
+	<component :is="themeConfig.layout" />
+</template>
+
+<script lang="ts">
+import { onBeforeMount, onUnmounted, getCurrentInstance, defineComponent, defineAsyncComponent } from 'vue';
+import { storeToRefs } from 'pinia';
+import { useThemeConfig } from '/@/stores/themeConfig';
+import { Local } from '/@/utils/storage';
+
+export default defineComponent({
+	name: 'layout',
+	components: {
+		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')),
+	},
+	setup() {
+		const { proxy } = <any>getCurrentInstance();
+		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;
+				proxy.mittBus.emit('layoutMobileResize', {
+					layout: 'defaults',
+					clientWidth,
+				});
+			} else {
+				proxy.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);
+		});
+		return {
+			themeConfig,
+		};
+	},
+});
+</script>
diff --git a/src/layout/lockScreen/index.vue b/src/layout/lockScreen/index.vue
new file mode 100644
index 0000000..10b8fed
--- /dev/null
+++ b/src/layout/lockScreen/index.vue
@@ -0,0 +1,372 @@
+<template>
+	<div v-show="isShowLockScreen">
+		<div class="layout-lock-screen-mask"></div>
+		<div class="layout-lock-screen-img" :class="{ 'layout-lock-screen-filter': isShowLoockLogin }"></div>
+		<div class="layout-lock-screen">
+			<div
+				class="layout-lock-screen-date"
+				ref="layoutLockScreenDateRef"
+				@mousedown="onDown"
+				@mousemove="onMove"
+				@mouseup="onEnd"
+				@touchstart.stop="onDown"
+				@touchmove.stop="onMove"
+				@touchend.stop="onEnd"
+			>
+				<div class="layout-lock-screen-date-box">
+					<div class="layout-lock-screen-date-box-time">
+						{{ time.hm }}<span class="layout-lock-screen-date-box-minutes">{{ time.s }}</span>
+					</div>
+					<div class="layout-lock-screen-date-box-info">{{ 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="isShowLoockLogin" class="layout-lock-screen-login">
+					<div class="layout-lock-screen-login-box">
+						<div class="layout-lock-screen-login-box-img">
+							<img src="https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1813762643,1914315241&fm=26&gp=0.jpg" />
+						</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="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 lang="ts">
+import { nextTick, onMounted, reactive, toRefs, ref, onUnmounted, getCurrentInstance, defineComponent } from 'vue';
+import { formatDate } from '/@/utils/formatTime';
+import { Local } from '/@/utils/storage';
+import { storeToRefs } from 'pinia';
+import { useThemeConfig } from '/@/stores/themeConfig';
+
+// 定义接口来定义对象的类型
+interface LockScreenState {
+	transparency: number;
+	downClientY: number;
+	moveDifference: number;
+	isShowLoockLogin: boolean;
+	isFlags: boolean;
+	querySelectorEl: HTMLElement | string;
+	time: {
+		hm: string;
+		s: string;
+		mdq: string;
+	};
+	setIntervalTime: number;
+	isShowLockScreen: boolean;
+	isShowLockScreenIntervalTime: number;
+	lockScreenPassword: string;
+}
+
+export default defineComponent({
+	name: 'layoutLockScreen',
+	setup() {
+		const { proxy } = <any>getCurrentInstance();
+		const layoutLockScreenInputRef = ref();
+		const storesThemeConfig = useThemeConfig();
+		const { themeConfig } = storeToRefs(storesThemeConfig);
+		const state = reactive<LockScreenState>({
+			transparency: 1,
+			downClientY: 0,
+			moveDifference: 0,
+			isShowLoockLogin: false,
+			isFlags: false,
+			querySelectorEl: '',
+			time: {
+				hm: '',
+				s: '',
+				mdq: '',
+			},
+			setIntervalTime: 0,
+			isShowLockScreen: false,
+			isShowLockScreenIntervalTime: 0,
+			lockScreenPassword: '',
+		});
+		// 鼠标按下
+		const onDown = (down: any) => {
+			state.isFlags = true;
+			state.downClientY = down.touches ? down.touches[0].clientY : down.clientY;
+		};
+		// 鼠标移动
+		const onMove = (move: any) => {
+			if (state.isFlags) {
+				const el = <HTMLElement>state.querySelectorEl;
+				const opacitys = (state.transparency -= 1 / 200);
+				if (move.touches) {
+					state.moveDifference = move.touches[0].clientY - state.downClientY;
+				} else {
+					state.moveDifference = move.clientY - state.downClientY;
+				}
+				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 = proxy.$refs.layoutLockScreenDateRef;
+			});
+		};
+		// 时间初始化
+		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);
+		});
+		return {
+			layoutLockScreenInputRef,
+			onDown,
+			onMove,
+			onEnd,
+			onLockScreenSubmit,
+			...toRefs(state),
+		};
+	},
+});
+</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;
+				}
+			}
+		}
+	}
+}
+::v-deep(.el-input-group__append) {
+	background: var(--el-color-white);
+	padding: 0px 15px;
+}
+::v-deep(.el-input__inner) {
+	border-right-color: var(--el-border-color-extra-light);
+	&:hover {
+		border-color: var(--el-border-color-extra-light);
+	}
+}
+</style>
diff --git a/src/layout/logo/index.vue b/src/layout/logo/index.vue
new file mode 100644
index 0000000..3d2b543
--- /dev/null
+++ b/src/layout/logo/index.vue
@@ -0,0 +1,85 @@
+<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 lang="ts">
+import { computed, defineComponent } from 'vue';
+import { storeToRefs } from 'pinia';
+import { useThemeConfig } from '/@/stores/themeConfig';
+
+import logoMini from '/@/assets/logo-mini.svg';
+
+export default defineComponent({
+	name: 'layoutLogo',
+	setup() {
+		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;
+		};
+		return {
+			logoMini,
+			setShowLogo,
+			themeConfig,
+			onThemeConfigChange,
+		};
+	},
+});
+</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);
+	font-size: 16px;
+	cursor: pointer;
+	animation: logoAnimation 0.3s ease-in-out;
+	span {
+		white-space: nowrap;
+		display: inline-block;
+	}
+	&: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>
diff --git a/src/layout/main/classic.vue b/src/layout/main/classic.vue
new file mode 100644
index 0000000..b92290f
--- /dev/null
+++ b/src/layout/main/classic.vue
@@ -0,0 +1,35 @@
+<template>
+	<el-container class="layout-container flex-center">
+		<Header />
+		<el-container class="layout-mian-height-50">
+			<Aside />
+			<div class="flex-center layout-backtop">
+				<TagsView v-if="themeConfig.isTagsview" />
+				<Main />
+			</div>
+		</el-container>
+		<el-backtop target=".layout-backtop .el-main .el-scrollbar__wrap"></el-backtop>
+	</el-container>
+</template>
+
+<script lang="ts">
+import { defineComponent } from 'vue';
+import { storeToRefs } from 'pinia';
+import { useThemeConfig } from '/@/stores/themeConfig';
+import Aside from '/@/layout/component/aside.vue';
+import Header from '/@/layout/component/header.vue';
+import Main from '/@/layout/component/main.vue';
+import TagsView from '/@/layout/navBars/tagsView/tagsView.vue';
+
+export default defineComponent({
+	name: 'layoutClassic',
+	components: { Aside, Header, Main, TagsView },
+	setup() {
+		const storesThemeConfig = useThemeConfig();
+		const { themeConfig } = storeToRefs(storesThemeConfig);
+		return {
+			themeConfig,
+		};
+	},
+});
+</script>
diff --git a/src/layout/main/columns.vue b/src/layout/main/columns.vue
new file mode 100644
index 0000000..0aa1d31
--- /dev/null
+++ b/src/layout/main/columns.vue
@@ -0,0 +1,41 @@
+<template>
+	<el-container class="layout-container">
+		<ColumnsAside />
+		<div class="layout-columns-warp">
+			<Aside />
+			<el-container class="flex-center layout-backtop" :class="{ 'layout-backtop': !isFixedHeader }">
+				<Header v-if="isFixedHeader" />
+				<el-scrollbar :class="{ 'layout-backtop': isFixedHeader }">
+					<Header v-if="!isFixedHeader" />
+					<Main />
+				</el-scrollbar>
+			</el-container>
+		</div>
+		<el-backtop target=".layout-backtop .el-scrollbar__wrap"></el-backtop>
+	</el-container>
+</template>
+
+<script lang="ts">
+import { computed, defineComponent } from 'vue';
+import { storeToRefs } from 'pinia';
+import { useThemeConfig } from '/@/stores/themeConfig';
+import Aside from '/@/layout/component/aside.vue';
+import Header from '/@/layout/component/header.vue';
+import Main from '/@/layout/component/main.vue';
+import ColumnsAside from '/@/layout/component/columnsAside.vue';
+
+export default defineComponent({
+	name: 'layoutColumns',
+	components: { Aside, Header, Main, ColumnsAside },
+	setup() {
+		const storesThemeConfig = useThemeConfig();
+		const { themeConfig } = storeToRefs(storesThemeConfig);
+		const isFixedHeader = computed(() => {
+			return themeConfig.value.isFixedHeader;
+		});
+		return {
+			isFixedHeader,
+		};
+	},
+});
+</script>
diff --git a/src/layout/main/defaults.vue b/src/layout/main/defaults.vue
new file mode 100644
index 0000000..0cabb0c
--- /dev/null
+++ b/src/layout/main/defaults.vue
@@ -0,0 +1,47 @@
+<template>
+	<el-container class="layout-container">
+		<Aside />
+		<el-container class="flex-center" :class="{ 'layout-backtop': !isFixedHeader }">
+			<Header v-if="isFixedHeader" />
+			<el-scrollbar ref="layoutDefaultsScrollbarRef" :class="{ 'layout-backtop': isFixedHeader }">
+				<Header v-if="!isFixedHeader" />
+				<Main />
+			</el-scrollbar>
+		</el-container>
+		<el-backtop target=".layout-backtop .el-scrollbar__wrap"></el-backtop>
+	</el-container>
+</template>
+
+<script lang="ts">
+import { computed, getCurrentInstance, watch, defineComponent } from 'vue';
+import { useRoute } from 'vue-router';
+import { storeToRefs } from 'pinia';
+import { useThemeConfig } from '/@/stores/themeConfig';
+import Aside from '/@/layout/component/aside.vue';
+import Header from '/@/layout/component/header.vue';
+import Main from '/@/layout/component/main.vue';
+
+export default defineComponent({
+	name: 'layoutDefaults',
+	components: { Aside, Header, Main },
+	setup() {
+		const { proxy } = <any>getCurrentInstance();
+		const route = useRoute();
+		const storesThemeConfig = useThemeConfig();
+		const { themeConfig } = storeToRefs(storesThemeConfig);
+		const isFixedHeader = computed(() => {
+			return themeConfig.value.isFixedHeader;
+		});
+		// 监听路由的变化
+		watch(
+			() => route.path,
+			() => {
+				proxy.$refs.layoutDefaultsScrollbarRef.wrap$.scrollTop = 0;
+			}
+		);
+		return {
+			isFixedHeader,
+		};
+	},
+});
+</script>
diff --git a/src/layout/main/transverse.vue b/src/layout/main/transverse.vue
new file mode 100644
index 0000000..538f911
--- /dev/null
+++ b/src/layout/main/transverse.vue
@@ -0,0 +1,17 @@
+<template>
+	<el-container class="layout-container flex-center layout-backtop">
+		<Header />
+		<Main />
+		<el-backtop target=".layout-backtop .el-main .el-scrollbar__wrap"></el-backtop>
+	</el-container>
+</template>
+
+<script lang="ts">
+import Header from '/@/layout/component/header.vue';
+import Main from '/@/layout/component/main.vue';
+
+export default {
+	name: 'layoutTransverse',
+	components: { Header, Main },
+};
+</script>
diff --git a/src/layout/navBars/breadcrumb/breadcrumb.vue b/src/layout/navBars/breadcrumb/breadcrumb.vue
new file mode 100644
index 0000000..541a841
--- /dev/null
+++ b/src/layout/navBars/breadcrumb/breadcrumb.vue
@@ -0,0 +1,163 @@
+<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 breadcrumbList" :key="!v.meta.tagsViewName ? v.meta.title : v.meta.tagsViewName">
+					<span v-if="k === 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 lang="ts">
+import { toRefs, reactive, computed, onMounted, defineComponent } 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';
+
+// 定义接口来定义对象的类型
+interface BreadcrumbState {
+	breadcrumbList: Array<any>;
+	routeSplit: Array<string>;
+	routeSplitFirst: string;
+	routeSplitIndex: number;
+}
+
+export default defineComponent({
+	name: 'layoutBreadcrumb',
+	setup() {
+		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: any) => {
+			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: Array<string>) => {
+			arr.forEach((item: any) => {
+				state.routeSplit.forEach((v: any, k: number, arrs: any) => {
+					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(route);
+		};
+		// 页面加载时
+		onMounted(() => {
+			initRouteSplit(route.path);
+		});
+		// 路由更新时
+		onBeforeRouteUpdate((to) => {
+			initRouteSplit(to.path);
+		});
+		return {
+			onThemeConfigChange,
+			isShowBreadcrumb,
+			themeConfig,
+			onBreadcrumbClick,
+			...toRefs(state),
+		};
+	},
+});
+</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;
+	}
+	::v-deep(.el-breadcrumb__separator) {
+		opacity: 0.7;
+		color: var(--next-bg-topBarColor);
+	}
+	::v-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>
diff --git a/src/layout/navBars/breadcrumb/closeFull.vue b/src/layout/navBars/breadcrumb/closeFull.vue
new file mode 100644
index 0000000..a0f0525
--- /dev/null
+++ b/src/layout/navBars/breadcrumb/closeFull.vue
@@ -0,0 +1,61 @@
+<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 lang="ts">
+import { defineComponent } from 'vue';
+import { storeToRefs } from 'pinia';
+import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
+
+export default defineComponent({
+	name: 'layoutCloseFull',
+	setup() {
+		const stores = useTagsViewRoutes();
+		const { isTagsViewCurrenFull } = storeToRefs(stores);
+		// 关闭当前全屏
+		const onCloseFullscreen = () => {
+			stores.setCurrenFullscreen(false);
+		};
+		return {
+			isTagsViewCurrenFull,
+			onCloseFullscreen,
+		};
+	},
+});
+</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;
+		::v-deep(i) {
+			position: absolute;
+			left: 10px;
+			top: 35px;
+			color: #333333;
+			transition: all 0.3s ease;
+		}
+	}
+	&:hover {
+		transition: all 0.3s ease;
+		::v-deep(i) {
+			color: var(--el-color-primary);
+			transition: all 0.3s ease;
+		}
+	}
+}
+</style>
diff --git a/src/layout/navBars/breadcrumb/index.vue b/src/layout/navBars/breadcrumb/index.vue
new file mode 100644
index 0000000..2810718
--- /dev/null
+++ b/src/layout/navBars/breadcrumb/index.vue
@@ -0,0 +1,119 @@
+<template>
+	<div class="layout-navbars-breadcrumb-index">
+		<Logo v-if="setIsShowLogo" />
+		<Breadcrumb />
+		<Horizontal :menuList="menuList" v-if="isLayoutTransverse" />
+		<User />
+	</div>
+</template>
+
+<script lang="ts">
+import { computed, reactive, toRefs, onMounted, onUnmounted, getCurrentInstance, defineComponent } from 'vue';
+import { useRoute } from 'vue-router';
+import { storeToRefs } from 'pinia';
+import { useRoutesList } from '/@/stores/routesList';
+import { useThemeConfig } from '/@/stores/themeConfig';
+import Breadcrumb from '/@/layout/navBars/breadcrumb/breadcrumb.vue';
+import User from '/@/layout/navBars/breadcrumb/user.vue';
+import Logo from '/@/layout/logo/index.vue';
+import Horizontal from '/@/layout/navMenu/horizontal.vue';
+
+// 定义接口来定义对象的类型
+interface IndexState {
+	menuList: object[];
+}
+
+export default defineComponent({
+	name: 'layoutBreadcrumbIndex',
+	components: { Breadcrumb, User, Logo, Horizontal },
+	setup() {
+		const { proxy } = <any>getCurrentInstance();
+		const stores = useRoutesList();
+		const storesThemeConfig = useThemeConfig();
+		const { themeConfig } = storeToRefs(storesThemeConfig);
+		const { routesList } = storeToRefs(stores);
+		const route = useRoute();
+		const state = reactive<IndexState>({
+			menuList: [],
+		});
+		// 设置 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);
+				proxy.mittBus.emit('setSendClassicChildren', resData);
+			} else {
+				state.menuList = filterRoutesFun(routesList.value);
+			}
+		};
+		// 设置了分割菜单时,删除底下 children
+		const delClassicChildren = (arr: Array<object>) => {
+			arr.map((v: any) => {
+				if (v.children) delete v.children;
+			});
+			return arr;
+		};
+		// 路由过滤递归函数
+		const filterRoutesFun = (arr: Array<string>) => {
+			return arr
+				.filter((item: any) => !item.meta.isHide)
+				.map((item: any) => {
+					item = Object.assign({}, item);
+					if (item.children) item.children = filterRoutesFun(item.children);
+					return item;
+				});
+		};
+		// 传送当前子级数据到菜单中
+		const setSendClassicChildren = (path: string) => {
+			const currentPathSplit = path.split('/');
+			let currentData: any = {};
+			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;
+		};
+		// 页面加载时
+		onMounted(() => {
+			setFilterRoutes();
+			proxy.mittBus.on('getBreadcrumbIndexSetFilterRoutes', () => {
+				setFilterRoutes();
+			});
+		});
+		// 页面卸载时
+		onUnmounted(() => {
+			proxy.mittBus.off('getBreadcrumbIndexSetFilterRoutes', () => {});
+		});
+		return {
+			setIsShowLogo,
+			isLayoutTransverse,
+			...toRefs(state),
+		};
+	},
+});
+</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>
diff --git a/src/layout/navBars/breadcrumb/search.vue b/src/layout/navBars/breadcrumb/search.vue
new file mode 100644
index 0000000..431043c
--- /dev/null
+++ b/src/layout/navBars/breadcrumb/search.vue
@@ -0,0 +1,138 @@
+<template>
+	<div class="layout-search-dialog">
+		<el-dialog v-model="isShowSearch" width="300px" destroy-on-close :modal="false" fullscreen :show-close="false">
+			<el-autocomplete
+				v-model="menuQuery"
+				:fetch-suggestions="menuSearch"
+				:placeholder="$t('message.user.searchPlaceholder')"
+				ref="layoutMenuAutocompleteRef"
+				@select="onHandleSelect"
+				@blur="onSearchBlur"
+			>
+				<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>
+		</el-dialog>
+	</div>
+</template>
+
+<script lang="ts">
+import { reactive, toRefs, defineComponent, ref, nextTick } from 'vue';
+import { useRouter } from 'vue-router';
+import { useI18n } from 'vue-i18n';
+import { storeToRefs } from 'pinia';
+import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
+
+// 定义接口来定义对象的类型
+interface SearchState {
+	isShowSearch: boolean;
+	menuQuery: string;
+	tagsViewList: object[];
+}
+interface Restaurant {
+	path: string;
+	meta: {
+		title: string;
+	};
+}
+
+export default defineComponent({
+	name: 'layoutBreadcrumbSearch',
+	setup() {
+		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: any = (queryString: string) => {
+			return (restaurant: Restaurant) => {
+				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: any) => {
+				if (!v.meta.isHide) state.tagsViewList.push({ ...v });
+			});
+		};
+		// 当前菜单选中时
+		const onHandleSelect = (item: any) => {
+			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();
+		};
+		// input 失去焦点时
+		const onSearchBlur = () => {
+			closeSearch();
+		};
+		return {
+			layoutMenuAutocompleteRef,
+			openSearch,
+			closeSearch,
+			menuSearch,
+			onHandleSelect,
+			onSearchBlur,
+			...toRefs(state),
+		};
+	},
+});
+</script>
+
+<style scoped lang="scss">
+.layout-search-dialog {
+	::v-deep(.el-dialog) {
+		box-shadow: unset !important;
+		border-radius: 0 !important;
+		background: rgba(0, 0, 0, 0.5);
+	}
+	::v-deep(.el-autocomplete) {
+		width: 560px;
+		position: absolute;
+		top: 100px;
+		left: 50%;
+		transform: translateX(-50%);
+	}
+}
+</style>
diff --git a/src/layout/navBars/breadcrumb/setings.vue b/src/layout/navBars/breadcrumb/setings.vue
new file mode 100644
index 0000000..856f260
--- /dev/null
+++ b/src/layout/navBars/breadcrumb/setings.vue
@@ -0,0 +1,816 @@
+<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 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>
+
+				<!-- 界面设置 -->
+				<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: 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="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($event)"></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 lang="ts">
+import { nextTick, onUnmounted, onMounted, getCurrentInstance, defineComponent, computed, reactive, toRefs } from 'vue';
+import { ElMessage } from 'element-plus';
+import { storeToRefs } from 'pinia';
+import { useThemeConfig } from '/@/stores/themeConfig';
+import { getLightColor, getDarkColor } from '/@/utils/theme';
+import { verifyAndSpace } from '/@/utils/toolsValidate';
+import { Local } from '/@/utils/storage';
+import Watermark from '/@/utils/wartermark';
+import commonFunction from '/@/utils/commonFunction';
+import other from '/@/utils/other';
+
+export default defineComponent({
+	name: 'layoutBreadcrumbSeting',
+	setup() {
+		const { proxy } = <any>getCurrentInstance();
+		const storesThemeConfig = useThemeConfig();
+		const { themeConfig } = storeToRefs(storesThemeConfig);
+		const { copyText } = commonFunction();
+		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}`, (<any>getThemeConfig.value)[bg]);
+			if (bg === 'menuBar') {
+				document.documentElement.style.setProperty(`--next-bg-menuBar-light-1`, <any>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);
+		};
+		// 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();
+			proxy.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 = () => {
+			proxy.mittBus.emit('openOrCloseSortable');
+			setLocalThemeConfig();
+		};
+		// 4、界面显示 --> 开启 TagsView 共用
+		const onShareTagsViewChange = () => {
+			proxy.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: any = 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: any) => {
+			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('topBar');
+			onBgColorPickerChange('topBarColor');
+			onBgColorPickerChange('columnsMenuBar');
+			onBgColorPickerChange('columnsMenuBarColor');
+		};
+		// 关闭弹窗时,初始化变量。变量用于处理 proxy.$refs.layoutScrollbarRef.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();
+		};
+		// 初始化菜单样式等
+		const initSetStyle = () => {
+			// 2、菜单 / 顶栏 --> 顶栏背景渐变
+			onTopBarGradualChange();
+			// 2、菜单 / 顶栏 --> 菜单背景渐变
+			onMenuBarGradualChange();
+			// 2、菜单 / 顶栏 --> 分栏菜单背景渐变
+			onColumnsMenuBarGradualChange();
+		};
+		onMounted(() => {
+			nextTick(() => {
+				// 判断当前布局是否不相同,不相同则初始化当前布局的样式,防止监听窗口大小改变时,布局配置logo、菜单背景等部分布局失效问题
+				if (!Local.get('frequency')) initLayoutChangeFun();
+				Local.set('frequency', 1);
+				// 监听窗口大小改变,非默认布局,设置成默认布局(适配移动端)
+				proxy.mittBus.on('layoutMobileResize', (res: any) => {
+					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')) proxy.$i18n.locale = Local.get('themeConfig').globalI18n;
+					// 初始化菜单样式等
+					initSetStyle();
+				}, 100);
+			});
+		});
+		onUnmounted(() => {
+			proxy.mittBus.off('layoutMobileResize', () => {});
+		});
+		return {
+			openDrawer,
+			onColorPickerChange,
+			onBgColorPickerChange,
+			onTopBarGradualChange,
+			onMenuBarGradualChange,
+			onColumnsMenuBarGradualChange,
+			onThemeConfigChange,
+			onIsFixedHeaderChange,
+			onIsShowLogoChange,
+			getThemeConfig,
+			onDrawerClose,
+			onAddFilterChange,
+			onAddDarkChange,
+			onWartermarkChange,
+			onWartermarkTextInput,
+			onSetLayout,
+			setLocalThemeConfig,
+			onClassicSplitMenuChange,
+			onIsBreadcrumbChange,
+			onSortableTagsViewChange,
+			onShareTagsViewChange,
+			onCopyConfigClick,
+			onResetConfigClick,
+			...toRefs(state),
+		};
+	},
+});
+</script>
+
+<style scoped lang="scss">
+.layout-breadcrumb-seting-bar {
+	height: calc(100vh - 50px);
+	padding: 0 15px;
+	::v-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>
diff --git a/src/layout/navBars/breadcrumb/user.vue b/src/layout/navBars/breadcrumb/user.vue
new file mode 100644
index 0000000..6782651
--- /dev/null
+++ b/src/layout/navBars/breadcrumb/user.vue
@@ -0,0 +1,298 @@
+<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="disabledSize === 'large'">{{ $t('message.user.dropdownLarge') }}</el-dropdown-item>
+					<el-dropdown-item command="default" :disabled="disabledSize === 'default'">{{ $t('message.user.dropdownDefault') }}</el-dropdown-item>
+					<el-dropdown-item command="small" :disabled="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="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="disabledI18n === 'zh-cn'">简体中文</el-dropdown-item>
+					<el-dropdown-item command="en" :disabled="disabledI18n === 'en'">English</el-dropdown-item>
+					<el-dropdown-item command="zh-tw" :disabled="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="isScreenfull ? $t('message.user.title6') : $t('message.user.title5')"
+				:class="!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 lang="ts">
+import { ref, getCurrentInstance, computed, reactive, toRefs, onMounted, defineComponent } 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 { Session, Local } from '/@/utils/storage';
+import UserNews from '/@/layout/navBars/breadcrumb/userNews.vue';
+import Search from '/@/layout/navBars/breadcrumb/search.vue';
+
+export default defineComponent({
+	name: 'layoutBreadcrumbUser',
+	components: { UserNews, Search },
+	setup() {
+		const { t } = useI18n();
+		const { proxy } = <any>getCurrentInstance();
+		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 = () => {
+			proxy.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 () => {
+						Session.clear(); // 清除缓存/token等
+						// 使用 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);
+			initComponentSize();
+			window.location.reload();
+		};
+		// 语言切换
+		const onLanguageChange = (lang: string) => {
+			Local.remove('themeConfig');
+			themeConfig.value.globalI18n = lang;
+			Local.set('themeConfig', themeConfig.value);
+			proxy.$i18n.locale = lang;
+			initI18n();
+			other.useTitle();
+		};
+		// 设置 element plus 组件的国际化
+		const setI18nConfig = (locale: string) => {
+			proxy.mittBus.emit('getI18nConfig', proxy.$i18n.messages[locale]);
+		};
+		// 初始化言语国际化
+		const initI18n = () => {
+			switch (Local.get('themeConfig').globalI18n) {
+				case 'zh-cn':
+					state.disabledI18n = 'zh-cn';
+					setI18nConfig('zh-cn');
+					break;
+				case 'en':
+					state.disabledI18n = 'en';
+					setI18nConfig('en');
+					break;
+				case 'zh-tw':
+					state.disabledI18n = 'zh-tw';
+					setI18nConfig('zh-tw');
+					break;
+			}
+		};
+		// 初始化全局组件大小
+		const initComponentSize = () => {
+			switch (Local.get('themeConfig').globalComponentSize) {
+				case 'large':
+					state.disabledSize = 'large';
+					break;
+				case 'default':
+					state.disabledSize = 'default';
+					break;
+				case 'small':
+					state.disabledSize = 'small';
+					break;
+			}
+		};
+		// 页面加载时
+		onMounted(() => {
+			if (Local.get('themeConfig')) {
+				initI18n();
+				initComponentSize();
+			}
+		});
+		return {
+			userInfos,
+			onLayoutSetingClick,
+			onHandleCommandClick,
+			onScreenfullClick,
+			onSearchClick,
+			onComponentSizeChange,
+			onLanguageChange,
+			searchRef,
+			layoutUserFlexNum,
+			...toRefs(state),
+		};
+	},
+});
+</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;
+			}
+		}
+	}
+	::v-deep(.el-dropdown) {
+		color: var(--next-bg-topBarColor);
+	}
+	::v-deep(.el-badge) {
+		height: 40px;
+		line-height: 40px;
+		display: flex;
+		align-items: center;
+	}
+	::v-deep(.el-badge__content.is-fixed) {
+		top: 12px;
+	}
+}
+</style>
diff --git a/src/layout/navBars/breadcrumb/userNews.vue b/src/layout/navBars/breadcrumb/userNews.vue
new file mode 100644
index 0000000..cae954a
--- /dev/null
+++ b/src/layout/navBars/breadcrumb/userNews.vue
@@ -0,0 +1,115 @@
+<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="newsList.length > 0" @click="onAllReadClick">{{ $t('message.user.newBtn') }}</div>
+		</div>
+		<div class="content-box">
+			<template v-if="newsList.length > 0">
+				<div class="content-box-item" v-for="(v, k) in 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="newsList.length > 0">{{ $t('message.user.newGo') }}</div>
+	</div>
+</template>
+
+<script lang="ts">
+import { reactive, toRefs, defineComponent } from 'vue';
+
+export default defineComponent({
+	name: 'layoutBreadcrumbUserNews',
+	setup() {
+		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');
+		};
+		return {
+			onAllReadClick,
+			onGoToGiteeClick,
+			...toRefs(state),
+		};
+	},
+});
+</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;
+		}
+	}
+	::v-deep(.el-empty__description p) {
+		font-size: 13px;
+	}
+}
+</style>
diff --git a/src/layout/navBars/index.vue b/src/layout/navBars/index.vue
new file mode 100644
index 0000000..f41a0cf
--- /dev/null
+++ b/src/layout/navBars/index.vue
@@ -0,0 +1,40 @@
+<template>
+	<div class="layout-navbars-container">
+		<BreadcrumbIndex />
+		<TagsView v-if="setShowTagsView" />
+	</div>
+</template>
+
+<script lang="ts">
+import { computed, defineComponent } from 'vue';
+import { storeToRefs } from 'pinia';
+import { useThemeConfig } from '/@/stores/themeConfig';
+import BreadcrumbIndex from '/@/layout/navBars/breadcrumb/index.vue';
+import TagsView from '/@/layout/navBars/tagsView/tagsView.vue';
+
+export default defineComponent({
+	name: 'layoutNavBars',
+	components: { BreadcrumbIndex, TagsView },
+	setup() {
+		const storesThemeConfig = useThemeConfig();
+		const { themeConfig } = storeToRefs(storesThemeConfig);
+		// 是否显示 tagsView
+		const setShowTagsView = computed(() => {
+			let { layout, isTagsview } = themeConfig.value;
+			return layout !== 'classic' && isTagsview;
+		});
+		return {
+			setShowTagsView,
+		};
+	},
+});
+</script>
+
+<style scoped lang="scss">
+.layout-navbars-container {
+	display: flex;
+	flex-direction: column;
+	width: 100%;
+	height: 100%;
+}
+</style>
diff --git a/src/layout/navBars/tagsView/contextmenu.vue b/src/layout/navBars/tagsView/contextmenu.vue
new file mode 100644
index 0000000..c717aae
--- /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="isShow"
+		>
+			<ul class="el-dropdown-menu">
+				<template v-for="(v, k) in 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: `${arrowLeft}px` }"></div>
+		</div>
+	</transition>
+</template>
+
+<script lang="ts">
+import { computed, defineComponent, reactive, toRefs, onMounted, onUnmounted, watch } from 'vue';
+
+export default defineComponent({
+	name: 'layoutTagsViewContextmenu',
+	props: {
+		dropdown: {
+			type: Object,
+			default: () => {
+				return {
+					x: 0,
+					y: 0,
+				};
+			},
+		},
+	},
+	setup(props, { emit }) {
+		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: any) => {
+			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,
+			}
+		);
+		return {
+			dropdowns,
+			openContextmenu,
+			closeContextmenu,
+			onCurrentContextmenuClick,
+			...toRefs(state),
+		};
+	},
+});
+</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>
diff --git a/src/layout/navBars/tagsView/tagsView.vue b/src/layout/navBars/tagsView/tagsView.vue
new file mode 100644
index 0000000..67e1b57
--- /dev/null
+++ b/src/layout/navBars/tagsView/tagsView.vue
@@ -0,0 +1,734 @@
+<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 tagsViewList"
+					:key="k"
+					class="layout-navbars-tagsview-ul-li"
+					:data-url="v.url"
+					:class="{ 'is-active': isActive(v) }"
+					@contextmenu.prevent="onContextmenu(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="dropdown" ref="contextmenuRef" @currentContextmenuClick="onCurrentContextmenuClick" />
+	</div>
+</template>
+
+<script lang="ts">
+import {
+	toRefs,
+	reactive,
+	onMounted,
+	computed,
+	ref,
+	nextTick,
+	onBeforeUpdate,
+	onBeforeMount,
+	onUnmounted,
+	getCurrentInstance,
+	watch,
+	defineComponent,
+} 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 Contextmenu from '/@/layout/navBars/tagsView/contextmenu.vue';
+
+// 定义接口来定义对象的类型
+interface TagsViewState {
+	routeActive: string;
+	routePath: string | unknown;
+	dropdown: {
+		x: string | number;
+		y: string | number;
+	};
+	sortable: any;
+	tagsRefsIndex: number;
+	tagsViewList: any[];
+	tagsViewRoutesList: any[];
+}
+interface RouteParams {
+	path: string;
+	url: string;
+	query: object;
+	params: object;
+}
+interface CurrentContextmenu {
+	meta: {
+		isDynamic: boolean;
+	};
+	params: any;
+	query: any;
+	path: string;
+	contextMenuClickId: string | number;
+}
+
+export default defineComponent({
+	name: 'layoutTagsView',
+	components: { Contextmenu },
+	setup() {
+		const { proxy } = <any>getCurrentInstance();
+		const tagsRefs = ref<any[]>([]);
+		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: any) => {
+				return other.setTagsViewNameI18n(v);
+			};
+		});
+		// 设置 tagsView 高亮
+		const isActive = (v: RouteParams) => {
+			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);
+		};
+		// 获取 vuex 中的 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();
+		};
+		// vuex 中获取路由信息:如果是设置了固定的(isAffix),进行初始化显示
+		const initTagsView = async () => {
+			if (Session.get('tagsViewList') && getThemeConfig.value.isCacheTagsView) {
+				state.tagsViewList = await Session.get('tagsViewList');
+			} else {
+				await state.tagsViewRoutesList.map((v: any) => {
+					if (v.meta.isAffix && !v.meta.isHide) {
+						v.url = setTagsViewHighlight(v);
+						state.tagsViewList.push({ ...v });
+						storesKeepALiveNames.addCachedView(v);
+					}
+				});
+				await addTagsView(route.path, route);
+			}
+			// 初始化当前元素(li)的下标
+			getTagsRefsIndex(getThemeConfig.value.isShareTagsView ? state.routePath : state.routeActive);
+		};
+		// 处理可开启多标签详情,单标签详情(动态路由(xxx/:id/:name"),普通路由处理)
+		const solveAddTagsView = async (path: string, to?: any) => {
+			let isDynamicPath = to.meta.isDynamic ? to.meta.isDynamicPath : path;
+			let current = state.tagsViewList.filter(
+				(v: any) =>
+					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: any) => 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?: any) => {
+			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?: any) => {
+			// 防止拿取不到路由信息
+			nextTick(async () => {
+				// 修复:https://gitee.com/lyt-top/vue-next-admin/issues/I3YX6G
+				let item: any = '';
+				if (to && 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: any) => v.path === to.meta.isDynamicPath)) return false;
+					item = state.tagsViewRoutesList.find((v: any) => 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: any) => v.path === path)) return false;
+					item = state.tagsViewRoutesList.find((v: any) => v.path === path);
+				}
+				if (!item) return false;
+				if (item.meta.isLink && !item.meta.isIframe) return false;
+				if (to && 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 item = state.tagsViewList.find((v: any) => (getThemeConfig.value.isShareTagsView ? v.path === fullPath : v.url === fullPath));
+			if (item != null) {
+				await storesKeepALiveNames.delCachedView(item);
+				proxy.mittBus.emit('onTagsViewRefreshRouterView', fullPath);
+				if (item.meta.isKeepAlive) storesKeepALiveNames.addCachedView(item);
+			}
+		};
+		// 3、关闭当前 tagsView:如果是设置了固定的(isAffix),不可以关闭
+		const closeCurrentTagsView = (path: string) => {
+			state.tagsViewList.map((v: any, k: number, arr: any) => {
+				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: any) => {
+					if (v.meta.isAffix && !v.meta.isHide) {
+						v.url = setTagsViewHighlight(v);
+						storesKeepALiveNames.delOthersCachedViews(v);
+						state.tagsViewList.push({ ...v });
+					}
+				});
+				addTagsView(path, route);
+				addBrowserSetSession(state.tagsViewList);
+			}
+		};
+		// 5、关闭全部 tagsView:如果是设置了固定的(isAffix),不进行关闭
+		const closeAllTagsView = () => {
+			if (Session.get('tagsViewList')) {
+				storesKeepALiveNames.delAllCachedViews();
+				state.tagsViewList = [];
+				Session.get('tagsViewList').map((v: any) => {
+					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: any) => (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 非当前页演示时,操作异常
+		const getCurrentRouteItem = (path: string, cParams: any) => {
+			const itemRoute = Session.get('tagsViewList') ? Session.get('tagsViewList') : state.tagsViewList;
+			return itemRoute.find((v: any) => {
+				if (
+					v.path === path &&
+					isObjectValueEqual(
+						v.meta.isDynamic ? (v.params ? v.params : null) : v.query ? v.query : null,
+						cParams && Object.keys(cParams ? cParams : {}).length > 0 ? cParams : null
+					)
+				) {
+					return v;
+				} else if (v.path === path && Object.keys(cParams ? cParams : {}).length <= 0) {
+					return v;
+				}
+			});
+		};
+		// 当前项右键菜单点击
+		const onCurrentContextmenuClick = async (item: CurrentContextmenu) => {
+			const cParams = item.meta.isDynamic ? item.params : item.query;
+			if (!getCurrentRouteItem(item.path, cParams)) return ElMessage({ type: 'warning', message: '请正确输入路径及完整参数(query、params)' });
+			const { path, name, params, query, meta, url } = getCurrentRouteItem(item.path, cParams);
+			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: any, e: any) => {
+			const { clientX, clientY } = e;
+			state.dropdown.x = clientX;
+			state.dropdown.y = clientY;
+			contextmenuRef.value.openContextmenu(v);
+		};
+		// 当前的 tagsView 项点击时
+		const onTagsClick = (v: any, k: number) => {
+			state.tagsRefsIndex = k;
+			router.push(v);
+		};
+		// 处理 tagsView 高亮(多标签详情时使用,单标签详情未使用)
+		const setTagsViewHighlight = (v: any) => {
+			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 updateScrollbar = () => {
+			proxy.$refs.scrollbarRef.update();
+		};
+		// 鼠标滚轮滚动
+		const onHandleScroll = (e: any) => {
+			proxy.$refs.scrollbarRef.$refs.wrap$.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: any = tagsRefs.value[0];
+				// 最后 li
+				let liLast: any = tagsRefs.value[tagsRefs.value.length - 1];
+				// 当前滚动条的值
+				let scrollRefs = proxy.$refs.scrollbarRef.$refs.wrap$;
+				// 当前滚动条滚动宽度
+				let scrollS = scrollRefs.scrollWidth;
+				// 当前滚动条偏移宽度
+				let offsetW = scrollRefs.offsetWidth;
+				// 当前滚动条偏移距离
+				let scrollL = scrollRefs.scrollLeft;
+				// 上一个 tags li dom
+				let liPrevTag: any = tagsRefs.value[state.tagsRefsIndex - 1];
+				// 下一个 tags li dom
+				let liNextTag: any = tagsRefs.value[state.tagsRefsIndex + 1];
+				// 上一个 tags li dom 的偏移距离
+				let beforePrevL: any = '';
+				// 下一个 tags li dom 的偏移距离
+				let afterNextL: any = '';
+				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;
+					}
+				}
+				// 更新滚动条,防止不出现
+				updateScrollbar();
+			});
+		};
+		// 获取 tagsView 的下标:用于处理 tagsView 点击时的横向滚动
+		const getTagsRefsIndex = (path: string | unknown) => {
+			nextTick(async () => {
+				// await 使用该写法,防止拿取不到 tagsViewList 列表数据不完整
+				let tagsViewList = await state.tagsViewList;
+				state.tagsRefsIndex = tagsViewList.findIndex((v: any) => {
+					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: any = [];
+					state.sortable.toArray().map((val: any) => {
+						state.tagsViewList.map((v: any) => {
+							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 当前页全屏
+			proxy.mittBus.on('onCurrentContextmenuClick', (data: CurrentContextmenu) => {
+				onCurrentContextmenuClick(data);
+			});
+			// 监听布局配置界面开启/关闭拖拽
+			proxy.mittBus.on('openOrCloseSortable', () => {
+				initSortable();
+			});
+			// 监听布局配置开启 TagsView 共用,为了演示还原默认值
+			proxy.mittBus.on('openShareTagsView', () => {
+				if (getThemeConfig.value.isShareTagsView) {
+					router.push('/home');
+					state.tagsViewList = [];
+					state.tagsViewRoutesList.map((v: any) => {
+						if (v.meta.isAffix && !v.meta.isHide) {
+							v.url = setTagsViewHighlight(v);
+							state.tagsViewList.push({ ...v });
+						}
+					});
+				}
+			});
+		});
+		// 页面卸载时
+		onUnmounted(() => {
+			// 取消非本页面调用监听
+			proxy.mittBus.off('onCurrentContextmenuClick', () => {});
+			// 取消监听布局配置界面开启/关闭拖拽
+			proxy.mittBus.off('openOrCloseSortable', () => {});
+			// 取消监听布局配置开启 TagsView 共用
+			proxy.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, 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,
+			}
+		);
+		return {
+			isActive,
+			onContextmenu,
+			onTagsClick,
+			tagsRefs,
+			contextmenuRef,
+			scrollbarRef,
+			tagsUlRef,
+			onHandleScroll,
+			getThemeConfig,
+			setTagsStyle,
+			setTagsViewNameI18n,
+			refreshCurrentTagsView,
+			closeCurrentTagsView,
+			onCurrentContextmenuClick,
+			...toRefs(state),
+		};
+	},
+});
+</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;
+	::v-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-box-image: url("data:image/svg+xml,%3Csvg width='68' height='34' viewBox='0 0 68 34' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='m27,0c-7.99582,0 -11.95105,0.00205 -12,12l0,6c0,8.284 -0.48549,16.49691 -8.76949,16.49691l54.37857,-0.11145c-8.284,0 -8.60908,-8.10146 -8.60908,-16.38546l0,-6c0.11145,-12.08445 -4.38441,-12 -12,-12l-13,0z' fill='%23409eff'/%3E%3C/svg%3E")
+				12 27 15;
+		}
+		.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>
diff --git a/src/layout/navMenu/horizontal.vue b/src/layout/navMenu/horizontal.vue
new file mode 100644
index 0000000..f738dd6
--- /dev/null
+++ b/src/layout/navMenu/horizontal.vue
@@ -0,0 +1,157 @@
+<template>
+	<div class="el-menu-horizontal-warp">
+		<el-scrollbar @wheel.native.prevent="onElMenuHorizontalScroll" ref="elMenuHorizontalScrollRef">
+			<el-menu router :default-active="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 :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 :name="val.meta.icon" />
+								{{ $t(val.meta.title) }}
+							</template>
+							<template #title v-else>
+								<a :href="val.meta.isLink" target="_blank" rel="opener" class="w100">
+									<SvgIcon :name="val.meta.icon" />
+									{{ $t(val.meta.title) }}
+								</a>
+							</template>
+						</el-menu-item>
+					</template>
+				</template>
+			</el-menu>
+		</el-scrollbar>
+	</div>
+</template>
+
+<script lang="ts">
+import { toRefs, reactive, computed, defineComponent, getCurrentInstance, onMounted, nextTick, onBeforeMount } from 'vue';
+import { useRoute, onBeforeRouteUpdate } from 'vue-router';
+import { storeToRefs } from 'pinia';
+import { useRoutesList } from '/@/stores/routesList';
+import { useThemeConfig } from '/@/stores/themeConfig';
+import SubItem from '/@/layout/navMenu/subItem.vue';
+
+export default defineComponent({
+	name: 'navMenuHorizontal',
+	components: { SubItem },
+	props: {
+		menuList: {
+			type: Array,
+			default: () => [],
+		},
+	},
+	setup(props) {
+		const { proxy } = <any>getCurrentInstance();
+		const stores = useRoutesList();
+		const storesThemeConfig = useThemeConfig();
+		const { routesList } = storeToRefs(stores);
+		const { themeConfig } = storeToRefs(storesThemeConfig);
+		const route = useRoute();
+		const state = reactive({
+			defaultActive: null,
+		});
+		// 获取父级菜单数据
+		const menuLists = computed(() => {
+			return <any>props.menuList;
+		});
+		// 设置横向滚动条可以鼠标滚轮滚动
+		const onElMenuHorizontalScroll = (e: any) => {
+			const eventDelta = e.wheelDelta || -e.deltaY * 40;
+			proxy.$refs.elMenuHorizontalScrollRef.$refs.wrap$.scrollLeft = proxy.$refs.elMenuHorizontalScrollRef.$refs.wrap$.scrollLeft + eventDelta / 4;
+		};
+		// 初始化数据,页面刷新时,滚动条滚动到对应位置
+		const initElMenuOffsetLeft = () => {
+			nextTick(() => {
+				let els: any = document.querySelector('.el-menu.el-menu--horizontal li.is-active');
+				if (!els) return false;
+				proxy.$refs.elMenuHorizontalScrollRef.$refs.wrap$.scrollLeft = els.offsetLeft;
+			});
+		};
+		// 路由过滤递归函数
+		const filterRoutesFun = (arr: Array<string>) => {
+			return arr
+				.filter((item: any) => !item.meta.isHide)
+				.map((item: any) => {
+					item = Object.assign({}, item);
+					if (item.children) item.children = filterRoutesFun(item.children);
+					return item;
+				});
+		};
+		// 传送当前子级数据到菜单中
+		const setSendClassicChildren = (path: string) => {
+			const currentPathSplit = path.split('/');
+			let currentData: any = {};
+			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: any) => {
+			const { path, meta } = currentRoute;
+			if (themeConfig.value.layout === 'classic') {
+				(<any>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;
+			}
+		};
+		// 页面加载前
+		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) {
+				proxy.mittBus.emit('setSendClassicChildren', setSendClassicChildren(to.path));
+			}
+		});
+		return {
+			menuLists,
+			onElMenuHorizontalScroll,
+			...toRefs(state),
+		};
+	},
+});
+</script>
+
+<style scoped lang="scss">
+.el-menu-horizontal-warp {
+	flex: 1;
+	overflow: hidden;
+	margin-right: 30px;
+	::v-deep(.el-scrollbar__bar.is-vertical) {
+		display: none;
+	}
+	::v-deep(a) {
+		width: 100%;
+	}
+	.el-menu.el-menu--horizontal {
+		display: flex;
+		height: 100%;
+		width: 100%;
+		box-sizing: border-box;
+	}
+}
+</style>
diff --git a/src/layout/navMenu/subItem.vue b/src/layout/navMenu/subItem.vue
new file mode 100644
index 0000000..3fcefd3
--- /dev/null
+++ b/src/layout/navMenu/subItem.vue
@@ -0,0 +1,48 @@
+<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 :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 :name="val.meta.icon" />
+					<span>{{ $t(val.meta.title) }}</span>
+				</template>
+				<template v-else>
+					<a :href="val.meta.isLink" target="_blank" rel="opener" class="w100">
+						<SvgIcon :name="val.meta.icon" />
+						{{ $t(val.meta.title) }}
+					</a>
+				</template>
+			</el-menu-item>
+		</template>
+	</template>
+</template>
+
+<script lang="ts">
+import { computed, defineComponent } from 'vue';
+
+export default defineComponent({
+	name: 'navMenuSubItem',
+	props: {
+		chil: {
+			type: Array,
+			default: () => [],
+		},
+	},
+	setup(props) {
+		// 获取父级菜单数据
+		const chils = computed(() => {
+			return <any>props.chil;
+		});
+		return {
+			chils,
+		};
+	},
+});
+</script>
diff --git a/src/layout/navMenu/vertical.vue b/src/layout/navMenu/vertical.vue
new file mode 100644
index 0000000..1bfa825
--- /dev/null
+++ b/src/layout/navMenu/vertical.vue
@@ -0,0 +1,101 @@
+<template>
+	<el-menu
+		router
+		:default-active="defaultActive"
+		background-color="transparent"
+		:collapse="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 :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 :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 :href="val.meta.isLink" target="_blank" rel="opener" class="w100">{{ $t(val.meta.title) }}</a>
+					</template>
+				</el-menu-item>
+			</template>
+		</template>
+	</el-menu>
+</template>
+
+<script lang="ts">
+import { toRefs, reactive, computed, defineComponent, onMounted, watch } from 'vue';
+import { useRoute, onBeforeRouteUpdate } from 'vue-router';
+import { storeToRefs } from 'pinia';
+import { useThemeConfig } from '/@/stores/themeConfig';
+import SubItem from '/@/layout/navMenu/subItem.vue';
+
+export default defineComponent({
+	name: 'navMenuVertical',
+	components: { SubItem },
+	props: {
+		menuList: {
+			type: Array,
+			default: () => [],
+		},
+	},
+	setup(props) {
+		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 <any>props.menuList;
+		});
+		// 获取布局配置信息
+		const getThemeConfig = computed(() => {
+			return themeConfig.value;
+		});
+		// 菜单高亮(详情时,父级高亮)
+		const setParentHighlight = (currentRoute: any) => {
+			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;
+		};
+		// 设置菜单的收起/展开
+		watch(
+			themeConfig.value,
+			() => {
+				document.body.clientWidth <= 1000 ? (state.isCollapse = false) : (state.isCollapse = themeConfig.value.isCollapse);
+			},
+			{
+				immediate: true,
+			}
+		);
+		// 页面加载时
+		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;
+		});
+		return {
+			menuLists,
+			getThemeConfig,
+			...toRefs(state),
+		};
+	},
+});
+</script>
diff --git a/src/layout/routerView/iframes.vue b/src/layout/routerView/iframes.vue
new file mode 100644
index 0000000..43e71c5
--- /dev/null
+++ b/src/layout/routerView/iframes.vue
@@ -0,0 +1,66 @@
+<template>
+	<div class="layout-view-bg-white flex mt1" :style="{ height: `calc(100vh - ${setIframeHeight}`, border: 'none' }" v-loading="iframeLoading">
+		<iframe :src="iframeUrl" frameborder="0" height="100%" width="100%" ref="iframeDom" v-show="!iframeLoading"></iframe>
+	</div>
+</template>
+
+<script lang="ts">
+import { defineComponent, reactive, toRefs, onMounted, nextTick, watch, computed } from 'vue';
+import { storeToRefs } from 'pinia';
+import { useRoute } from 'vue-router';
+import { useThemeConfig } from '/@/stores/themeConfig';
+import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
+
+export default defineComponent({
+	name: 'layoutIfameView',
+	setup() {
+		const storesThemeConfig = useThemeConfig();
+		const storesTagsViewRoutes = useTagsViewRoutes();
+		const { themeConfig } = storeToRefs(storesThemeConfig);
+		const { isTagsViewCurrenFull } = storeToRefs(storesTagsViewRoutes);
+		const route = useRoute();
+		const state = reactive({
+			iframeDom: null as HTMLIFrameElement | null,
+			iframeLoading: true,
+			iframeUrl: '',
+		});
+		// 初始化页面加载 loading
+		const initIframeLoad = () => {
+			state.iframeUrl = <any>route.meta.isLink;
+			nextTick(() => {
+				state.iframeLoading = true;
+				const iframe = state.iframeDom;
+				if (!iframe) return false;
+				iframe.onload = () => {
+					state.iframeLoading = false;
+				};
+			});
+		};
+		// 设置 iframe 的高度
+		const setIframeHeight = computed(() => {
+			let { isTagsview } = themeConfig.value;
+			if (isTagsViewCurrenFull.value) {
+				return `1px`;
+			} else {
+				if (isTagsview) return `86px`;
+				else return `51px`;
+			}
+		});
+		// 页面加载时
+		onMounted(() => {
+			initIframeLoad();
+		});
+		// 监听路由变化,多个 iframe 时使用
+		watch(
+			() => route.path,
+			() => {
+				initIframeLoad();
+			}
+		);
+		return {
+			setIframeHeight,
+			...toRefs(state),
+		};
+	},
+});
+</script>
diff --git a/src/layout/routerView/link.vue b/src/layout/routerView/link.vue
new file mode 100644
index 0000000..a48f002
--- /dev/null
+++ b/src/layout/routerView/link.vue
@@ -0,0 +1,61 @@
+<template>
+	<div class="layout-view-bg-white flex layout-view-link" :style="{ height: `calc(100vh - ${setLinkHeight}` }">
+		<a :href="currentRouteMeta.isLink" target="_blank" rel="opener" class="flex-margin">
+			{{ $t(currentRouteMeta.title) }}:{{ currentRouteMeta.isLink }}
+		</a>
+	</div>
+</template>
+
+<script lang="ts">
+import { defineComponent, toRefs, reactive, computed, watch } from 'vue';
+import { useRoute, RouteMeta } from 'vue-router';
+import { storeToRefs } from 'pinia';
+import { useThemeConfig } from '/@/stores/themeConfig';
+
+// 定义接口来定义对象的类型
+interface LinkViewState {
+	currentRouteMeta: {
+		isLink: string;
+		title: string;
+	};
+}
+interface LinkViewRouteMeta extends RouteMeta {
+	isLink: string;
+	title: string;
+}
+
+export default defineComponent({
+	name: 'layoutLinkView',
+	setup() {
+		const storesThemeConfig = useThemeConfig();
+		const { themeConfig } = storeToRefs(storesThemeConfig);
+		const route = useRoute();
+		const state = reactive<LinkViewState>({
+			currentRouteMeta: {
+				isLink: '',
+				title: '',
+			},
+		});
+		// 设置 link 的高度
+		const setLinkHeight = computed(() => {
+			let { isTagsview } = themeConfig.value;
+			if (isTagsview) return `115px`;
+			else return `80px`;
+		});
+		// 监听路由的变化,设置内容
+		watch(
+			() => route.path,
+			() => {
+				state.currentRouteMeta = <LinkViewRouteMeta>route.meta;
+			},
+			{
+				immediate: true,
+			}
+		);
+		return {
+			setLinkHeight,
+			...toRefs(state),
+		};
+	},
+});
+</script>
diff --git a/src/layout/routerView/parent.vue b/src/layout/routerView/parent.vue
new file mode 100644
index 0000000..db22141
--- /dev/null
+++ b/src/layout/routerView/parent.vue
@@ -0,0 +1,88 @@
+<template>
+	<div class="h100">
+		<router-view v-slot="{ Component }">
+			<transition :name="setTransitionName" mode="out-in">
+				<keep-alive :include="getKeepAliveNames">
+					<component :is="Component" :key="refreshRouterViewKey" class="w100" />
+				</keep-alive>
+			</transition>
+		</router-view>
+	</div>
+</template>
+
+<script lang="ts">
+import { computed, defineComponent, toRefs, reactive, getCurrentInstance, onBeforeMount, onUnmounted, nextTick, watch, onMounted } from 'vue';
+import { useRoute } from 'vue-router';
+import { storeToRefs } from 'pinia';
+import { useKeepALiveNames } from '/@/stores/keepAliveNames';
+import { useThemeConfig } from '/@/stores/themeConfig';
+import { Session } from '/@/utils/storage';
+
+// 定义接口来定义对象的类型
+interface ParentViewState {
+	refreshRouterViewKey: null | string;
+	keepAliveNameList: string[];
+}
+
+export default defineComponent({
+	name: 'layoutParentView',
+	setup() {
+		const { proxy } = <any>getCurrentInstance();
+		const route = useRoute();
+		const storesKeepAliveNames = useKeepALiveNames();
+		const storesThemeConfig = useThemeConfig();
+		const { keepAliveNames, cachedViews } = storeToRefs(storesKeepAliveNames);
+		const { themeConfig } = storeToRefs(storesThemeConfig);
+		const state = reactive<ParentViewState>({
+			refreshRouterViewKey: null,
+			keepAliveNameList: [],
+		});
+		// 设置主界面切换动画
+		const setTransitionName = computed(() => {
+			return themeConfig.value.animation;
+		});
+		// 获取组件缓存列表(name值)
+		const getKeepAliveNames = computed(() => {
+			return themeConfig.value.isTagsview ? cachedViews.value : state.keepAliveNameList;
+		});
+		// 页面加载前,处理缓存,页面刷新时路由缓存处理
+		onBeforeMount(() => {
+			state.keepAliveNameList = keepAliveNames.value;
+			proxy.mittBus.on('onTagsViewRefreshRouterView', (fullPath: string) => {
+				state.keepAliveNameList = keepAliveNames.value.filter((name: string) => route.name !== name);
+				state.refreshRouterViewKey = null;
+				nextTick(() => {
+					state.refreshRouterViewKey = fullPath;
+					state.keepAliveNameList = keepAliveNames.value;
+				});
+			});
+		});
+		// 页面加载时
+		onMounted(() => {
+			// https://gitee.com/lyt-top/vue-next-admin/issues/I58U75
+			// https://gitee.com/lyt-top/vue-next-admin/issues/I59RXK
+			nextTick(() => {
+				setTimeout(() => {
+					if (themeConfig.value.isCacheTagsView) cachedViews.value = Session.get('tagsViewList')?.map((item: any) => item.name);
+				}, 0);
+			});
+		});
+		// 页面卸载时
+		onUnmounted(() => {
+			proxy.mittBus.off('onTagsViewRefreshRouterView', () => {});
+		});
+		// 监听路由变化,防止 tagsView 多标签时,切换动画消失
+		watch(
+			() => route.fullPath,
+			() => {
+				state.refreshRouterViewKey = decodeURI(route.fullPath);
+			}
+		);
+		return {
+			setTransitionName,
+			getKeepAliveNames,
+			...toRefs(state),
+		};
+	},
+});
+</script>
diff --git a/src/main.ts b/src/main.ts
new file mode 100644
index 0000000..acd3d24
--- /dev/null
+++ b/src/main.ts
@@ -0,0 +1,22 @@
+import { createApp } from 'vue';
+import pinia from '/@/stores/index';
+import App from './App.vue';
+import router from './router';
+import { directive } from '/@/utils/directive';
+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 mitt from 'mitt';
+import VueGridLayout from 'vue-grid-layout';
+
+const app = createApp(App);
+
+directive(app);
+other.elSvg(app);
+
+app.use(pinia).use(router).use(ElementPlus, { i18n: i18n.global.t }).use(i18n).use(VueGridLayout).mount('#app');
+
+app.config.globalProperties.mittBus = mitt();
diff --git a/src/router/backEnd.ts b/src/router/backEnd.ts
new file mode 100644
index 0000000..6d7f365
--- /dev/null
+++ b/src/router/backEnd.ts
@@ -0,0 +1,110 @@
+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/index';
+import { ElMessage } from 'element-plus';
+
+const menuApi = useMenuApi();
+
+const layouModules: any = import.meta.glob('../layout/routerView/*.{vue,tsx}');
+const viewsModules: any = import.meta.glob('../views/**/*.{vue,tsx}');
+
+
+/**
+ * 获取目录下的 .vue、.tsx 全部文件
+ * @method import.meta.glob
+ * @link 参考:https://cn.vitejs.dev/guide/features.html#json
+ */
+const dynamicViewsModules: Record<string, Function> = Object.assign({}, { ...layouModules }, { ...viewsModules });
+
+
+export async function initBackEndControlRoutes() {
+	if (window.nextLoading === undefined) NextLoading.start();
+	if (!Session.get('token')) return false;
+	useUserInfo().setUserInfos();
+	// if(Session.get('ifMenu'))
+	const res = await getBackEndControlRoutes(Session.get('projectId'));
+	useRequestOldRoutes().setRequestOldRoutes(JSON.parse(JSON.stringify(res.data.data)));
+	dynamicRoutes[0].children = await backEndComponent(res.data.data);
+	await setAddRoute();
+	await setFilterMenuAndCacheTagsViewRoutes();
+    }
+
+
+export function setFilterMenuAndCacheTagsViewRoutes() {
+	const storesRoutesList = useRoutesList(pinia);
+	storesRoutesList.setRoutesList(dynamicRoutes[0].children as any);
+	setCacheTagsViewRoutes();
+}
+
+
+export function setCacheTagsViewRoutes() {
+	const storesTagsView = useTagsViewRoutes(pinia);
+	storesTagsView.setTagsViewRoutes(formatTwoStageRoutes(formatFlatteningRoutes(dynamicRoutes))[0].children);
+}
+
+
+export function setFilterRouteEnd() {
+	let filterRouteEnd: any = formatTwoStageRoutes(formatFlatteningRoutes(dynamicRoutes));
+	filterRouteEnd[0].children = [...filterRouteEnd[0].children, ...notFoundAndNoPower];
+	return filterRouteEnd;
+}
+
+
+export async function setAddRoute() {
+	await setFilterRouteEnd().forEach((route: RouteRecordRaw) => {
+		router.addRoute(route);
+	});
+}
+
+
+export async function getBackEndControlRoutes(value : string) {
+	const stores = useUserInfo(pinia);
+	const { userInfos } = storeToRefs(stores);
+	const auth = userInfos.value.roles[0];
+	return menuApi.getMenuAdmin(value);
+
+}
+
+/**
+ * 重新请求后端路由菜单接口
+ * @description 用于菜单管理界面刷新菜单(未进行测试)
+ * @description 路径:/src/views/system/menu/component/menuDialog.vue
+ */
+export function setBackEndControlRefreshRoutes() {
+	getBackEndControlRoutes(Session.get('projectId'));
+}
+
+
+export function backEndComponent(routes: any) {
+	if (!routes) return;
+	return routes.map((item: any) => {
+		if (item.component) item.component = dynamicImport(dynamicViewsModules, item.component as string);
+		item.children && backEndComponent(item.children);
+		return item;
+	});
+}
+
+
+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;
+	}
+}
diff --git a/src/router/frontEnd.ts b/src/router/frontEnd.ts
new file mode 100644
index 0000000..af8432b
--- /dev/null
+++ b/src/router/frontEnd.ts
@@ -0,0 +1,97 @@
+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';
+
+
+export async function initFrontEndControlRoutes() {
+	if (window.nextLoading === undefined) NextLoading.start();
+	if (!Session.get('token')) return false;
+	useUserInfo(pinia).setUserInfos();
+	await setAddRoute();
+	await setFilterMenuAndCacheTagsViewRoutes();
+}
+
+
+export async function setAddRoute() {
+	await setFilterRouteEnd().forEach((route: RouteRecordRaw) => {
+		router.addRoute(route);
+	});
+}
+
+
+export async function frontEndsResetRoute() {
+	await setFilterRouteEnd().forEach((route: RouteRecordRaw) => {
+		const routeName: any = route.name;
+		router.hasRoute(routeName) && router.removeRoute(routeName);
+	});
+}
+
+
+export function setFilterRouteEnd() {
+	let filterRouteEnd: any = formatTwoStageRoutes(formatFlatteningRoutes(dynamicRoutes));
+	filterRouteEnd[0].children = [...setFilterRoute(filterRouteEnd[0].children), ...notFoundAndNoPower];
+	return filterRouteEnd;
+}
+
+
+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;
+}
+
+
+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);
+}
+
+
+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();
+}
+
+
+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;
+}
+
+
+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;
+}
diff --git a/src/router/index.ts b/src/router/index.ts
new file mode 100644
index 0000000..1554262
--- /dev/null
+++ b/src/router/index.ts
@@ -0,0 +1,110 @@
+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 } from '/@/router/route';
+import { initFrontEndControlRoutes } from '/@/router/frontEnd';
+import { initBackEndControlRoutes } from '/@/router/backEnd';
+
+
+
+// 读取 `/src/stores/themeConfig.ts` 是否开启后端控制路由配置
+const storesThemeConfig = useThemeConfig(pinia);
+const { themeConfig } = storeToRefs(storesThemeConfig);
+const { isRequestRoutes } = themeConfig.value;
+if (isRequestRoutes) staticRoutes.splice(0, 1);
+
+
+export const router = createRouter({
+	history: createWebHashHistory(),
+	routes: staticRoutes,
+});
+
+
+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;
+}
+
+
+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;
+}
+
+// isRequestRoutes 为 true,则开启后端控制路由,路径:`/src/stores/themeConfig.ts`
+if (!isRequestRoutes) initFrontEndControlRoutes();
+
+// 路由加载前
+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();
+					// 动态添加路由:防止非首页刷新时跳转回首页的问题
+					// 确保 addRoute() 时动态添加的路由已经被完全加载上去
+					next({ ...to, replace: true });
+				}
+			} else {
+				next();
+			}
+		}
+	}
+});
+
+// 路由加载后
+router.afterEach(() => {
+	NProgress.done();
+});
+
+// 导出路由
+export default router;
diff --git a/src/router/route.ts b/src/router/route.ts
new file mode 100644
index 0000000..0177847
--- /dev/null
+++ b/src/router/route.ts
@@ -0,0 +1,102 @@
+import { RouteRecordRaw } from 'vue-router';
+
+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',
+					button:'121231'
+				},
+			},
+		],
+	},
+];
+
+
+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,
+		},
+	},
+];
+
+export const staticRoutes: Array<RouteRecordRaw> = [
+	{
+		path: '/',
+		name: '/',
+		component: () => import('/@/layout/index.vue'),
+		meta: {
+			title: '布局界面',
+		},
+		children: [
+			// 请不要往这里 `children` 中添加内容,此内容为了防止 No match found for location with path "xxx" 问题
+			...notFoundAndNoPower,
+		],
+	},
+	{
+		path: '/login',
+		name: 'login',
+		component: () => import('/@/views/login/index.vue'),
+		meta: {
+			title: '登录',
+		},
+	},
+	{
+		path: '/dashboard',
+		name: 'dashboard',
+		component: () => import('/@/views/dashboard/index.vue'),
+		meta: {
+			title: '系统选择',
+		},
+	},
+
+	{
+		path: '/visualizingDemo1',
+		name: 'visualizingDemo1',
+		component: () => import('/@/views/visualizing/demo1.vue'),
+		meta: {
+			title: 'message.router.visualizingLinkDemo1',
+		},
+	},
+	{
+		path: '/visualizingDemo2',
+		name: 'visualizingDemo2',
+		component: () => import('/@/views/visualizing/demo2.vue'),
+		meta: {
+			title: 'message.router.visualizingLinkDemo2',
+		},
+	},
+];
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;
diff --git a/src/stores/interface/index.ts b/src/stores/interface/index.ts
new file mode 100644
index 0000000..2690f4b
--- /dev/null
+++ b/src/stores/interface/index.ts
@@ -0,0 +1,91 @@
+/**
+ * 定义接口来定义对象的类型
+ * `stores` 全部类型定义在这里
+ */
+
+// 用户信息
+export interface UserInfosState {
+	authBtnList: string[];
+	photo: string;
+	roles: string[];
+	time: number;
+	userName: string;
+	sign:string;
+}
+export interface UserInfosStates {
+	userInfos: UserInfosState;
+}
+
+// 路由缓存列表
+export interface KeepAliveNamesState {
+	keepAliveNames: string[];
+	cachedViews: string[];
+}
+
+// 后端返回原始路由(未处理时)
+export interface RequestOldRoutesState {
+	requestOldRoutes: string[];
+}
+
+// TagsView 路由列表
+export interface TagsViewRoutesState {
+	tagsViewRoutes: string[];
+	isTagsViewCurrenFull: Boolean;
+}
+
+// 路由列表
+export interface RoutesListState {
+	routesList: string[];
+	isColumnsMenuHover: Boolean;
+	isColumnsNavHover: Boolean;
+}
+
+// 布局配置
+export interface ThemeConfigState {
+	isDrawer: boolean;
+	primary: string;
+	topBar: string;
+	topBarColor: string;
+	isTopBarColorGradual: boolean;
+	menuBar: string;
+	menuBarColor: string;
+	isMenuBarColorGradual: boolean;
+	columnsMenuBar: string;
+	columnsMenuBarColor: string;
+	isColumnsMenuBarColorGradual: 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;
+	globalI18n: string;
+	globalComponentSize: string;
+}
+export interface ThemeConfigStates {
+	themeConfig: ThemeConfigState;
+}
diff --git a/src/stores/keepAliveNames.ts b/src/stores/keepAliveNames.ts
new file mode 100644
index 0000000..32e0389
--- /dev/null
+++ b/src/stores/keepAliveNames.ts
@@ -0,0 +1,37 @@
+import { defineStore } from 'pinia';
+import { KeepAliveNamesState } from './interface';
+
+/**
+ * 路由缓存列表
+ * @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 (this.cachedViews.includes(view.name)) return;
+			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 = [];
+		},
+	},
+});
diff --git a/src/stores/requestOldRoutes.ts b/src/stores/requestOldRoutes.ts
new file mode 100644
index 0000000..be9b5cd
--- /dev/null
+++ b/src/stores/requestOldRoutes.ts
@@ -0,0 +1,17 @@
+import { defineStore } from 'pinia';
+import { RequestOldRoutesState } from './interface';
+
+/**
+ * 后端返回原始路由(未处理时)
+ * @methods setCacheKeepAlive 设置接口原始路由数据
+ */
+export const useRequestOldRoutes = defineStore('requestOldRoutes', {
+	state: (): RequestOldRoutesState => ({
+		requestOldRoutes: [],
+	}),
+	actions: {
+		async setRequestOldRoutes(routes: Array<string>) {
+			this.requestOldRoutes = routes;
+		},
+	},
+});
diff --git a/src/stores/routesList.ts b/src/stores/routesList.ts
new file mode 100644
index 0000000..7dd2b28
--- /dev/null
+++ b/src/stores/routesList.ts
@@ -0,0 +1,27 @@
+import { defineStore } from 'pinia';
+import { RoutesListState } from './interface';
+
+/**
+ * 路由列表
+ * @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;
+		},
+	},
+});
diff --git a/src/stores/tagsViewRoutes.ts b/src/stores/tagsViewRoutes.ts
new file mode 100644
index 0000000..7a5e6f4
--- /dev/null
+++ b/src/stores/tagsViewRoutes.ts
@@ -0,0 +1,24 @@
+import { defineStore } from 'pinia';
+import { TagsViewRoutesState } from './interface';
+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;
+		},
+	},
+});
diff --git a/src/stores/themeConfig.ts b/src/stores/themeConfig.ts
new file mode 100644
index 0000000..198e155
--- /dev/null
+++ b/src/stores/themeConfig.ts
@@ -0,0 +1,146 @@
+import { defineStore } from 'pinia';
+import { ThemeConfigStates, ThemeConfigState } from './interface';
+
+/**
+ * 布局配置
+ * 修复: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: (): ThemeConfigStates => ({
+		themeConfig: {
+			// 是否开启布局配置抽屉
+			isDrawer: false,
+
+			/**
+			 * 全局主题
+			 */
+			// 默认 primary 主题颜色
+			primary: '#409eff',
+			// 是否开启深色模式
+			isIsDark: false,
+
+			/**
+			 * 菜单 / 顶栏
+			 * 注意:v1.0.17 版本去除设置布局切换,重置主题样式(initSetLayoutChange),
+			 * 切换布局需手动设置样式,设置的样式自动同步各布局,
+			 * 代码位置:/@/layout/navBars/breadcrumb/setings.vue
+			 */
+			// 默认顶栏导航背景颜色
+			topBar: '#ffffff',
+			// 默认顶栏导航字体颜色
+			topBarColor: '#606266',
+			// 是否开启顶栏背景颜色渐变
+			isTopBarColorGradual: false,
+			// 默认菜单导航背景颜色
+			menuBar: '#545c64',
+			// 默认菜单导航字体颜色
+			menuBarColor: '#eaeaea',
+			// 是否开启菜单背景颜色渐变
+			isMenuBarColorGradual: false,
+			// 默认分栏菜单背景颜色
+			columnsMenuBar: '#545c64',
+			// 默认分栏菜单字体颜色
+			columnsMenuBarColor: '#e6e6e6',
+			// 是否开启分栏菜单背景颜色渐变
+			isColumnsMenuBarColorGradual: false,
+
+			/**
+			 * 界面设置
+			 */
+			// 是否开启菜单水平折叠效果
+			isCollapse: false,
+			// 是否开启菜单手风琴效果
+			isUniqueOpened: false,
+			// 是否开启固定 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: false,
+			// 水印文案
+			wartermarkText: 'small@小柒',
+
+			/**
+			 * 其它设置
+			 */
+			// 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: true,
+
+			/**
+			 * 全局网站标题 / 副标题
+			 */
+			// 网站主标题(菜单导航、浏览器当前网页标题)
+			globalTitle: 'vue-next-admin',
+			// 网站副标题(登录页顶部文字)
+			globalViceTitle: 'vueNextAdmin',
+			// 默认初始语言,可选值"<zh-cn|en|zh-tw>",默认 zh-cn
+			globalI18n: 'zh-cn',
+			// 默认全局组件大小,可选值"<large|'default'|small>",默认 'large'
+			globalComponentSize: 'large',
+		},
+	}),
+	actions: {
+		setThemeConfig(data: ThemeConfigState) {
+			this.themeConfig = data;
+		},
+	},
+});
diff --git a/src/stores/userInfo.ts b/src/stores/userInfo.ts
new file mode 100644
index 0000000..97176a8
--- /dev/null
+++ b/src/stores/userInfo.ts
@@ -0,0 +1,65 @@
+import { defineStore } from 'pinia';
+import Cookies from 'js-cookie';
+import { UserInfosStates } from './interface';
+import { Session } from '/@/utils/storage';
+
+/**
+ * 用户信息
+ * @methods setUserInfos 设置用户信息
+ */
+export const useUserInfo = defineStore('userInfo', {
+	state: (): UserInfosStates => ({
+		userInfos: {
+			userName: '',
+			photo: 'https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=317673774,2961727727&fm=26&gp=0.jpg',
+			time: 0,
+			roles: [],
+			authBtnList: [],
+			sign:'',
+		},
+	}),
+	actions: {
+		async setUserInfos() {
+			const userName = Cookies.get('userName');
+			// 模拟数据
+			let defaultRoles: Array<string> = [];
+			let defaultAuthBtnList: Array<string> = [];
+			// admin 页面权限标识,对应路由 meta.roles,用于控制路由的显示/隐藏
+			let adminRoles: Array<string> = ['admin'];
+			// admin 按钮权限标识
+			let adminAuthBtnList: Array<string> = ['btn.add', 'btn.del', 'btn.edit', 'btn.link'];
+			// test 页面权限标识,对应路由 meta.roles,用于控制路由的显示/隐藏
+			let testRoles: Array<string> = ['common'];
+			// test 按钮权限标识
+			let testAuthBtnList: Array<string> = ['btn.add', 'btn.link'];
+			// 不同用户模拟不同的用户权限
+			if (userName === 'admin') {
+				defaultRoles = adminRoles;
+				defaultAuthBtnList = adminAuthBtnList;
+			} else {
+				defaultRoles = testRoles;
+				defaultAuthBtnList = testAuthBtnList;
+			}
+			// 用户信息模拟数据
+			const userInfos = {
+				userName: userName,
+				photo:
+					userName === 'admin'
+						? 'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1813762643,1914315241&fm=26&gp=0.jpg'
+						: 'https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=317673774,2961727727&fm=26&gp=0.jpg',
+				time: new Date().getTime(),
+				roles: defaultRoles,
+				authBtnList: defaultAuthBtnList,
+				sign:'',
+			};
+			// 存储用户信息到浏览器缓存
+			Session.set('userInfo', userInfos);
+
+			if (Session.get('userInfo')) {
+				this.userInfos = Session.get('userInfo');
+			} else {
+				this.userInfos = userInfos;
+			}
+		},
+	},
+});
diff --git a/src/theme/app.scss b/src/theme/app.scss
new file mode 100644
index 0000000..4c75296
--- /dev/null
+++ b/src/theme/app.scss
@@ -0,0 +1,281 @@
+/* 初始化样式
+------------------------------- */
+* {
+	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-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;
+	}
+	.layout-main {
+		padding: 0 !important;
+		overflow: hidden;
+		width: 100%;
+		background-color: var(--next-bg-main-color);
+	}
+	.el-scrollbar {
+		width: 100%;
+	}
+	// 此字段多次用到,建议不删除,如需修改,请重写覆盖样式
+	.layout-view-bg-white {
+		background: var(--el-color-white);
+		width: 100%;
+		height: 100%;
+		border-radius: 4px;
+		border: 1px solid var(--el-border-color-light, #ebeef5);
+	}
+	.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-scrollbar {
+		@extend .el-scrollbar;
+		padding: 15px;
+	}
+	.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;
+	}
+}
diff --git a/src/theme/common/transition.scss b/src/theme/common/transition.scss
new file mode 100644
index 0000000..a03a7bb
--- /dev/null
+++ b/src/theme/common/transition.scss
@@ -0,0 +1,94 @@
+/* 页面切换动画
+------------------------------- */
+.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;
+	}
+}
diff --git a/src/theme/dark.scss b/src/theme/dark.scss
new file mode 100644
index 0000000..c922da1
--- /dev/null
+++ b/src/theme/dark.scss
@@ -0,0 +1,236 @@
+/* 深色模式样式
+------------------------------- */
+[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-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%);
+
+	// 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);
+	}
+
+	// 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;
+		}
+	}
+}
diff --git a/src/theme/element.scss b/src/theme/element.scss
new file mode 100644
index 0000000..5137cb4
--- /dev/null
+++ b/src/theme/element.scss
@@ -0,0 +1,282 @@
+@import 'mixins/index.scss';
+
+/* Button 按钮
+------------------------------- */
+// 第三方字体图标大小
+.el-button 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-input {
+	height: 100%;
+}
+// 菜单搜索
+.el-autocomplete-suggestion__wrap {
+	max-height: 280px !important;
+}
+
+/* Form 表单
+------------------------------- */
+.el-form {
+	.el-form-item:last-of-type {
+		margin-bottom: 0 !important;
+	}
+}
+
+/* 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-color-menu-hover) !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-sub-menu.is-active.is-opened .el-sub-menu__title {
+	background-color: unset !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;
+}
+.el-select-dropdown__wrap {
+	max-height: 274px !important; /*修复Select 选择器高度问题*/
+}
+.el-cascader-menu__wrap.el-scrollbar__wrap {
+	height: 204px !important; /*修复Cascader 级联选择器高度问题*/
+}
+
+/* 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;
+	}
+}
diff --git a/src/theme/iconSelector.scss b/src/theme/iconSelector.scss
new file mode 100644
index 0000000..970201e
--- /dev/null
+++ b/src/theme/iconSelector.scss
@@ -0,0 +1,70 @@
+/* Popover 弹出框(图标选择器)
+------------------------------- */
+.icon-selector-popper {
+	padding: 0 !important;
+	.icon-selector-warp {
+		height: 260px;
+		overflow: hidden;
+		.icon-selector-warp-title {
+			height: 40px;
+			line-height: 40px;
+			padding: 0 15px;
+			.icon-selector-warp-title-tab {
+				span {
+					cursor: pointer;
+					&:hover {
+						color: var(--el-color-primary);
+						text-decoration: underline;
+					}
+				}
+				.span-active {
+					color: var(--el-color-primary);
+					text-decoration: underline;
+				}
+			}
+		}
+		.icon-selector-warp-row {
+			height: 230px;
+			overflow: hidden;
+			border-top: 1px solid var(--el-border-color);
+			.el-row {
+				padding: 15px;
+			}
+			.el-scrollbar__bar.is-horizontal {
+				display: none;
+			}
+			.icon-selector-warp-item {
+				display: flex;
+				border: 1px solid var(--el-border-color);
+				padding: 5px;
+				border-radius: 5px;
+				margin-bottom: 10px;
+				.icon-selector-warp-item-value {
+					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);
+					.icon-selector-warp-item-value {
+						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);
+				.icon-selector-warp-item-value {
+					i {
+						color: var(--el-color-primary);
+					}
+				}
+			}
+		}
+	}
+}
diff --git a/src/theme/index.scss b/src/theme/index.scss
new file mode 100644
index 0000000..c574e00
--- /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 './iconSelector.scss';
+@import './media/media.scss';
+@import './waves.scss';
+@import './dark.scss';
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);
+	}
+}
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;
+			}
+		}
+	}
+}
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%;
+	}
+}
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;
+					}
+				}
+			}
+		}
+	}
+}
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;
+	}
+}
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;
+		}
+	}
+}
diff --git a/src/theme/media/form.scss b/src/theme/media/form.scss
new file mode 100644
index 0000000..098300b
--- /dev/null
+++ b/src/theme/media/form.scss
@@ -0,0 +1,16 @@
+@import './index.scss';
+
+/* 页面宽度小于576px
+------------------------------- */
+@media screen and (max-width: $xs) {
+	.el-form-item__label {
+		width: 100% !important;
+		text-align: left !important;
+	}
+	.el-form-item__content {
+		margin-left: 0 !important;
+	}
+	.el-form-item {
+		display: unset !important;
+	}
+}
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;
+		}
+	}
+}
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;
diff --git a/src/theme/media/layout.scss b/src/theme/media/layout.scss
new file mode 100644
index 0000000..77cbec0
--- /dev/null
+++ b/src/theme/media/layout.scss
@@ -0,0 +1,55 @@
+@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;
+		}
+	}
+}
diff --git a/src/theme/media/login.scss b/src/theme/media/login.scss
new file mode 100644
index 0000000..41a0159
--- /dev/null
+++ b/src/theme/media/login.scss
@@ -0,0 +1,63 @@
+@import './index.scss';
+
+/* 页面宽度小于992px
+------------------------------- */
+@media screen and (max-width: $lg) {
+	.login-container {
+		.login-icon-group {
+			&::before {
+				content: '';
+				height: 70% !important;
+				transition: all 0.3s ease;
+			}
+			&::after {
+				content: '';
+				width: 100px !important;
+				height: 200px !important;
+				transition: all 0.3s ease;
+			}
+		}
+	}
+}
+
+/* 页面宽度小于992px
+------------------------------- */
+@media screen and (max-width: $md) {
+	.login-content {
+		right: unset !important;
+		left: 50% !important;
+		transform: translate(-50%, -50%) translate3d(0, 0, 0) !important;
+	}
+}
+
+/* 页面宽度小于576px
+------------------------------- */
+@media screen and (max-width: $xs) {
+	.login-container {
+		.login-icon-group {
+			display: none !important;
+		}
+		.login-content {
+			width: 100% !important;
+			height: 100% !important;
+			padding: 20px 0 !important;
+			border-radius: 0 !important;
+			box-shadow: unset !important;
+			border: none !important;
+		}
+		.el-form-item {
+			display: flex !important;
+		}
+	}
+}
+
+/* 页面宽度小于375px
+------------------------------- */
+@media screen and (max-width: $us) {
+	.login-container {
+		.login-content-title {
+			font-size: 18px !important;
+			transition: all 0.3s ease;
+		}
+	}
+}
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';
diff --git a/src/theme/media/pagination.scss b/src/theme/media/pagination.scss
new file mode 100644
index 0000000..400ebaa
--- /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 {
+	text-align: center !important;
+}
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;
+		}
+	}
+}
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);
+	}
+}
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;
+		}
+	}
+}
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;
+	}
+}
diff --git a/src/theme/other.scss b/src/theme/other.scss
new file mode 100644
index 0000000..a0451b8
--- /dev/null
+++ b/src/theme/other.scss
@@ -0,0 +1,36 @@
+/* wangeditor富文本编辑器
+------------------------------- */
+.editor-container {
+	z-index: 9999;
+	.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;
+}
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;
+}
diff --git a/src/utils/arrayOperation.ts b/src/utils/arrayOperation.ts
new file mode 100644
index 0000000..a85deee
--- /dev/null
+++ b/src/utils/arrayOperation.ts
@@ -0,0 +1,66 @@
+/**
+ * 判断两数组字符串是否相同(用于按钮权限验证),数组字符串中存在相同时会自动去重(按钮权限标识不会重复)
+ * @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 = olds.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(a: { [key: string]: any }, b: { [key: string]: any }) {
+	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: any, attr?: string) {
+	if (!arr && !arr.length) {
+		return arr;
+	} else {
+		if (attr) {
+			const obj: any = {};
+			const newArr = arr.reduce((cur: any, item: any) => {
+				obj[item[attr]] ? '' : (obj[item[attr]] = true && item[attr] && cur.push(item));
+				return cur;
+			}, []);
+			return newArr;
+		} else {
+			return Array.from(new Set([...arr]));
+		}
+	}
+}
diff --git a/src/utils/authDirective.ts b/src/utils/authDirective.ts
new file mode 100644
index 0000000..5971e64
--- /dev/null
+++ b/src/utils/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);
+		},
+	});
+}
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);
+}
diff --git a/src/utils/commonFunction.ts b/src/utils/commonFunction.ts
new file mode 100644
index 0000000..1c069e6
--- /dev/null
+++ b/src/utils/commonFunction.ts
@@ -0,0 +1,65 @@
+// 通用函数
+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: any, column: number, cellValue: any) => {
+		return cellValue ? `${cellValue}%` : '-';
+	};
+	//列表日期时间格式化
+	const dateFormatYMD = (row: any, column: number, cellValue: any) => {
+		if (!cellValue) return '-';
+		return formatDate(new Date(cellValue), 'YYYY-mm-dd');
+	};
+	//列表日期时间格式化
+	const dateFormatYMDHMS = (row: any, column: number, cellValue: any) => {
+		if (!cellValue) return '-';
+		return formatDate(new Date(cellValue), 'YYYY-mm-dd HH:MM:SS');
+	};
+	//列表日期时间格式化
+	const dateFormatHMS = (row: any, column: number, cellValue: any) => {
+		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: any = 0, scale: number = 4) => {
+		return Number.parseFloat(value).toFixed(scale);
+	};
+	// 小数格式化
+	const scale2Format = (value: any = 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,
+	};
+}
diff --git a/src/utils/customDirective.ts b/src/utils/customDirective.ts
new file mode 100644
index 0000000..c67350f
--- /dev/null
+++ b/src/utils/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;
+				};
+			};
+		},
+	});
+}
diff --git a/src/utils/directive.ts b/src/utils/directive.ts
new file mode 100644
index 0000000..a75b187
--- /dev/null
+++ b/src/utils/directive.ts
@@ -0,0 +1,18 @@
+import type { App } from 'vue';
+import { authDirective } from '/@/utils/authDirective';
+import { wavesDirective, dragDirective } from '/@/utils/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);
+}
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 '夜里好';
+}
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;
diff --git a/src/utils/loading.ts b/src/utils/loading.ts
new file mode 100644
index 0000000..c23fb77
--- /dev/null
+++ b/src/utils/loading.ts
@@ -0,0 +1,42 @@
+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: () => {
+		nextTick(() => {
+			window.nextLoading = false;
+			const el = <HTMLElement>document.querySelector('.loading-next');
+			el?.parentNode?.removeChild(el);
+		});
+	},
+};
diff --git a/src/utils/other.ts b/src/utils/other.ts
new file mode 100644
index 0000000..6bb4fbd
--- /dev/null
+++ b/src/utils/other.ts
@@ -0,0 +1,200 @@
+import { nextTick } 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 SvgIcon from '/@/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 = <any>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: any = '';
+	const { query, params, meta } = item;
+	if (query?.tagsViewName || params?.tagsViewName) {
+		if (/\/zh-cn|en|zh-tw\//.test(query?.tagsViewName) || /\/(zh-cn|en|zh-tw)\//.test(params?.tagsViewName)) {
+			// 国际化
+			const urlTagsParams = (query?.tagsViewName && JSON.parse(query?.tagsViewName)) || (params?.tagsViewName && JSON.parse(params?.tagsViewName));
+			tagsViewName = urlTagsParams[i18n.global.locale];
+		} else {
+			// 非国际化
+			tagsViewName = query?.tagsViewName || params?.tagsViewName;
+		}
+	} else {
+		// 非自定义 tagsView 名称
+		tagsViewName = i18n.global.t(<any>meta.title);
+	}
+	return tagsViewName;
+}
+
+/**
+ * 图片懒加载
+ * @param el dom 目标元素
+ * @param arr 列表数据
+ * @description data-xxx 属性用于存储页面或应用程序的私有自定义数据
+ */
+export const lazyImg = (el: any, arr: any) => {
+	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: any) {
+	let newObj: any;
+	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: any) {
+	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;
+}
+
+/**
+ * 统一批量导出
+ * @method elSvg 导出全局注册 element plus svg 图标
+ * @method useTitle 设置浏览器标题国际化
+ * @method setTagsViewNameI18n 设置 自定义 tagsView 名称、 自定义 tagsView 名称国际化
+ * @method lazyImg 图片懒加载
+ * @method globalComponentSize() element plus 全局组件大小
+ * @method deepClone 对象深克隆
+ * @method isMobile 判断是否是移动端
+ * @method handleEmpty 判断数组对象中所有属性是否为空,为空则删除当前行对象
+ */
+const other = {
+	elSvg: (app: App) => {
+		elSvg(app);
+	},
+	useTitle: () => {
+		useTitle();
+	},
+	setTagsViewNameI18n(route: any) {
+		return setTagsViewNameI18n(route);
+	},
+	lazyImg: (el: any, arr: any) => {
+		lazyImg(el, arr);
+	},
+	globalComponentSize: () => {
+		return globalComponentSize();
+	},
+	deepClone: (obj: any) => {
+		return deepClone(obj);
+	},
+	isMobile: () => {
+		return isMobile();
+	},
+	handleEmpty: (list: any) => {
+		return handleEmpty(list);
+	},
+};
+
+// 统一批量导出
+export default other;
diff --git a/src/utils/request.ts b/src/utils/request.ts
new file mode 100644
index 0000000..146e09b
--- /dev/null
+++ b/src/utils/request.ts
@@ -0,0 +1,72 @@
+import axios from 'axios';
+import { ElMessage, ElMessageBox } from 'element-plus';
+import { Session } from '/@/utils/storage';
+
+// 配置新建一个 axios 实例
+const service = axios.create({
+	baseURL: import.meta.env.VITE_API_URL as any,
+	timeout: 50000,
+	headers: { 'Content-Type': 'application/json' },
+});
+
+service.interceptors.request.use(
+	(config) => {
+		// 在发送请求之前做些什么 token
+		if (Session.get('token')) {
+			(<any>config.headers).common['Authorization'] = `${Session.get('token')}`;
+			(<any>config.headers).common['uid'] = `${Session.get('sign')}`;
+		}
+		return config;
+	},
+	(error) => {
+		// 对请求错误做些什么
+		return Promise.reject(error);
+	}
+);
+
+service.interceptors.response.use(
+
+	(response) => {
+		// 对响应数据做点什么
+		if(response.data.code && (response.data.code ==='A0215' || response.data.code === 'A0214')){
+			Session.clear()
+			window.location.href = '/'
+			return Promise.reject(response)
+		}
+		// if(response.data.code && response.data.code !== '200'){
+			return Promise.resolve(response)
+		// }
+		// Session.clear()
+		// window.location.href = '/'
+		// return Promise.reject(response)
+		// const res = response.data;
+		// debugger
+		// if (res.code && res.code !== 0) {
+		// 	// `token` 过期或者账号已在别处登录
+		// 	if (res.code === 401 || res.code === 4001) {
+		// 		Session.clear(); // 清除浏览器全部临时缓存
+		// 		window.location.href = '/'; // 去登录页
+		// 		ElMessageBox.alert('你已被登出,请重新登录', '提示', {})
+		// 			.then(() => {})
+		// 			.catch(() => {});
+		// }
+		// 	return Promise.reject(service.interceptors.response);
+		// } else {
+		// 	return response.data;
+		// }
+	},
+	(error) => {
+		// 对响应错误做点什么
+		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);
+	}
+);
+
+export default service;
diff --git a/src/utils/setIconfont.ts b/src/utils/setIconfont.ts
new file mode 100644
index 0000000..a6acf68
--- /dev/null
+++ b/src/utils/setIconfont.ts
@@ -0,0 +1,48 @@
+// 字体图标 url
+const cssCdnUrlList: Array<string> = [
+	'//at.alicdn.com/t/font_2298093_y6u00apwst.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;
diff --git a/src/utils/storage.ts b/src/utils/storage.ts
new file mode 100644
index 0000000..a983f80
--- /dev/null
+++ b/src/utils/storage.ts
@@ -0,0 +1,59 @@
+import Cookies from 'js-cookie';
+
+/**
+ * window.localStorage 浏览器永久缓存
+ * @method set 设置永久缓存
+ * @method get 获取永久缓存
+ * @method remove 移除永久缓存
+ * @method clear 移除全部永久缓存
+ */
+export const Local = {
+	// 设置永久缓存
+	set(key: string, val: any) {
+		window.localStorage.setItem(key, JSON.stringify(val));
+	},
+	// 获取永久缓存
+	get(key: string) {
+		let json: any = window.localStorage.getItem(key);
+		return JSON.parse(json);
+	},
+	// 移除永久缓存
+	remove(key: string) {
+		window.localStorage.removeItem(key);
+	},
+	// 移除全部永久缓存
+	clear() {
+		window.localStorage.clear();
+	},
+};
+
+/**
+ * window.sessionStorage 浏览器临时缓存
+ * @method set 设置临时缓存
+ * @method get 获取临时缓存
+ * @method remove 移除临时缓存
+ * @method clear 移除全部临时缓存
+ */
+export const Session = {
+	// 设置临时缓存
+	set(key: string, val: any) {
+		if (key === 'token') return Cookies.set(key, val);
+		window.sessionStorage.setItem(key, JSON.stringify(val));
+	},
+	// 获取临时缓存
+	get(key: string) {
+		if (key === 'token') return Cookies.get(key);
+		let json: any = window.sessionStorage.getItem(key);
+		return JSON.parse(json);
+	},
+	// 移除临时缓存
+	remove(key: string) {
+		if (key === 'token') return Cookies.remove(key);
+		window.sessionStorage.removeItem(key);
+	},
+	// 移除全部临时缓存
+	clear() {
+		Cookies.remove('token');
+		window.sessionStorage.clear();
+	},
+};
diff --git a/src/utils/theme.ts b/src/utils/theme.ts
new file mode 100644
index 0000000..5561e64
--- /dev/null
+++ b/src/utils/theme.ts
@@ -0,0 +1,59 @@
+import { ElMessage } from 'element-plus';
+
+/**
+ * hex颜色转rgb颜色
+ * @param str 颜色值字符串
+ * @returns 返回处理后的颜色值
+ */
+export function hexToRgb(str: any) {
+	let hexs: any = '';
+	let reg = /^\#?[0-9A-Fa-f]{6}$/;
+	if (!reg.test(str)) return ElMessage.warning('输入错误的hex');
+	str = str.replace('#', '');
+	hexs = str.match(/../g);
+	for (let i = 0; i < 3; i++) hexs[i] = parseInt(hexs[i], 16);
+	return hexs;
+}
+
+/**
+ * rgb颜色转Hex颜色
+ * @param r 代表红色
+ * @param g 代表绿色
+ * @param b 代表蓝色
+ * @returns 返回处理后的颜色值
+ */
+export function rgbToHex(r: any, g: any, b: any) {
+	let reg = /^\d{1,3}$/;
+	if (!reg.test(r) || !reg.test(g) || !reg.test(b)) return ElMessage.warning('输入错误的rgb颜色值');
+	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('')}`;
+}
+
+/**
+ * 加深颜色值
+ * @param color 颜色值字符串
+ * @param level 加深的程度,限0-1之间
+ * @returns 返回处理后的颜色值
+ */
+export function getDarkColor(color: string, level: number) {
+	let reg = /^\#?[0-9A-Fa-f]{6}$/;
+	if (!reg.test(color)) return ElMessage.warning('输入错误的hex颜色值');
+	let rgb = hexToRgb(color);
+	for (let i = 0; i < 3; i++) rgb[i] = Math.floor(rgb[i] * (1 - level));
+	return rgbToHex(rgb[0], rgb[1], rgb[2]);
+}
+
+/**
+ * 变浅颜色值
+ * @param color 颜色值字符串
+ * @param level 加深的程度,限0-1之间
+ * @returns 返回处理后的颜色值
+ */
+export function getLightColor(color: string, level: number) {
+	let reg = /^\#?[0-9A-Fa-f]{6}$/;
+	if (!reg.test(color)) return ElMessage.warning('输入错误的hex颜色值');
+	let rgb = hexToRgb(color);
+	for (let i = 0; i < 3; i++) rgb[i] = Math.floor((255 - rgb[i]) * level + rgb[i]);
+	return rgbToHex(rgb[0], rgb[1], rgb[2]);
+}
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;
+}
diff --git a/src/utils/wartermark.ts b/src/utils/wartermark.ts
new file mode 100644
index 0000000..b897ed1
--- /dev/null
+++ b/src/utils/wartermark.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: any = 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 = '15px';
+	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;
diff --git a/src/views/chart/chart.scss b/src/views/chart/chart.scss
new file mode 100644
index 0000000..32409e5
--- /dev/null
+++ b/src/views/chart/chart.scss
@@ -0,0 +1,434 @@
+.chart-scrollbar {
+	.chart-warp {
+		display: flex;
+		flex-direction: column;
+		height: 100%;
+		.chart-warp-bottom {
+			flex: 1;
+			overflow: hidden;
+			display: flex;
+			.big-data-down-left,
+			.big-data-down-right {
+				width: 30%;
+				display: flex;
+				flex-direction: column;
+				.flex-warp-item {
+					padding: 0 7.5px 15px 15px;
+					width: 100%;
+					height: 33.33%;
+					.flex-warp-item-box {
+						width: 100%;
+						height: 100%;
+						background: var(--el-color-white);
+						border: 1px solid var(--el-border-color-lighter);
+						border-radius: 4px;
+						display: flex;
+						flex-direction: column;
+						padding: 15px;
+						transition: all ease 0.3s;
+						&:hover {
+							box-shadow: 0 2px 12px 0 rgb(0 0 0 / 10%);
+							transition: all ease 0.3s;
+						}
+						.flex-title {
+							margin-bottom: 15px;
+							display: flex;
+							justify-content: space-between;
+							.flex-title-small {
+								font-size: 12px;
+							}
+						}
+						.flex-content {
+							flex: 1;
+							font-size: 12px;
+						}
+						.flex-content-overflow {
+							overflow: hidden;
+						}
+					}
+				}
+			}
+			.big-data-down-left {
+				color: var(--el-text-color-primary);
+				.sky {
+					display: flex;
+					align-items: center;
+					.sky-left {
+						font-size: 30px;
+					}
+					.sky-center {
+						flex: 1;
+						overflow: hidden;
+						padding: 0 10px;
+						font {
+							margin-right: 15px;
+						}
+						.span {
+							background: #22bc76;
+							border-radius: 2px;
+							padding: 0 5px;
+							color: var(--el-color-white);
+						}
+					}
+					.sky-right {
+						span {
+							font-size: 30px;
+						}
+						font {
+							font-size: 20px;
+						}
+					}
+				}
+				.sky-dd {
+					.sky-dl {
+						display: flex;
+						align-items: center;
+						height: 28px;
+						overflow: hidden;
+						div {
+							flex: 1;
+							overflow: hidden;
+							i {
+								font-size: 14px;
+							}
+						}
+						.tip {
+							overflow: hidden;
+							white-space: nowrap;
+							text-overflow: ellipsis;
+						}
+					}
+					.sky-dl-first {
+						color: var(--el-color-primary);
+					}
+				}
+				.d-states {
+					display: flex;
+					.d-states-item {
+						flex: 1;
+						display: flex;
+						align-items: center;
+						overflow: hidden;
+						i {
+							font-size: 20px;
+							height: 33px;
+							width: 33px;
+							line-height: 33px;
+							text-align: center;
+							border-radius: 100%;
+							flex-shrink: 1;
+							color: var(--el-color-white);
+							display: flex;
+							align-items: center;
+							justify-content: center;
+						}
+						.i-bg1 {
+							background: #22bc76;
+						}
+						.i-bg2 {
+							background: #e2356d;
+						}
+						.i-bg3 {
+							background: #43bbef;
+						}
+						.d-states-flex {
+							overflow: hidden;
+							padding: 0 10px 0;
+							.d-states-item-label {
+								color: var(--el-color-primary);
+								overflow: hidden;
+								white-space: nowrap;
+								text-overflow: ellipsis;
+							}
+							.d-states-item-value {
+								font-size: 14px;
+								text-align: center;
+								margin-top: 3px;
+								color: var(--el-color-primary);
+							}
+						}
+					}
+				}
+				.d-btn {
+					margin-top: 5px;
+					.d-btn-item {
+						border: 1px solid var(--el-color-primary);
+						display: flex;
+						width: 100%;
+						border-radius: 35px;
+						align-items: center;
+						padding: 5px;
+						margin-top: 15px;
+						cursor: pointer;
+						transition: all ease 0.3s;
+						color: var(--el-color-primary);
+						.d-btn-item-left {
+							font-size: 20px;
+							border: 1px solid var(--el-color-primary);
+							width: 25px;
+							height: 25px;
+							line-height: 25px;
+							border-radius: 100%;
+							text-align: center;
+							font-size: 14px;
+						}
+						.d-btn-item-center {
+							padding: 0 10px;
+							flex: 1;
+						}
+						.d-btn-item-eight {
+							text-align: right;
+							padding-right: 10px;
+						}
+					}
+				}
+			}
+			.big-data-down-center {
+				width: 40%;
+				display: flex;
+				flex-direction: column;
+				.big-data-down-center-one {
+					height: 66.67%;
+					padding: 0 7.5px 15px;
+					.big-data-down-center-one-content {
+						height: 100%;
+						background: var(--el-color-white);
+						padding: 15px;
+						border: 1px solid var(--el-border-color-lighter);
+						border-radius: 4px;
+						transition: all ease 0.3s;
+						&:hover {
+							box-shadow: 0 2px 12px 0 rgb(0 0 0 / 10%);
+							transition: all ease 0.3s;
+						}
+					}
+				}
+				.big-data-down-center-two {
+					padding: 0 7.5px 15px;
+					height: 33.33%;
+					.flex-warp-item-box {
+						width: 100%;
+						height: 100%;
+						background: var(--el-color-white);
+						display: flex;
+						flex-direction: column;
+						padding: 15px;
+						border: 1px solid var(--el-border-color-lighter);
+						border-radius: 4px;
+						transition: all ease 0.3s;
+						&:hover {
+							box-shadow: 0 2px 12px 0 rgb(0 0 0 / 10%);
+							transition: all ease 0.3s;
+						}
+						.flex-title {
+							margin-bottom: 15px;
+							color: var(--el-text-color-primary);
+							display: flex;
+							justify-content: space-between;
+							.flex-title-small {
+								font-size: 12px;
+							}
+						}
+						.flex-content {
+							flex: 1;
+							font-size: 12px;
+							display: flex;
+							height: calc(100% - 30px);
+							.flex-content-left {
+								display: flex;
+								flex-wrap: wrap;
+								width: 120px;
+								height: 100%;
+								.monitor-item {
+									width: 50%;
+									display: flex;
+									align-items: center;
+									.monitor-wave {
+										cursor: pointer;
+										width: 40px;
+										height: 40px;
+										position: relative;
+										background-color: var(--el-color-primary);
+										border-radius: 50%;
+										overflow: hidden;
+										text-align: center;
+										&::before,
+										&::after {
+											content: '';
+											position: absolute;
+											left: 50%;
+											width: 40px;
+											height: 40px;
+											background: #f4f4f4;
+											animation: roateOne 10s linear infinite;
+											transform: translateX(-50%);
+											z-index: 1;
+										}
+										&::before {
+											bottom: 10px;
+											border-radius: 60%;
+										}
+										&::after {
+											bottom: 8px;
+											opacity: 0.7;
+											border-radius: 37%;
+										}
+										.monitor-z-index {
+											position: relative;
+											z-index: 2;
+											color: var(--el-color-primary);
+											display: flex;
+											align-items: center;
+											height: 100%;
+											justify-content: center;
+										}
+									}
+									@keyframes roateOne {
+										0% {
+											transform: translate(-50%, 0) rotateZ(0deg);
+										}
+										50% {
+											transform: translate(-50%, -2%) rotateZ(180deg);
+										}
+										100% {
+											transform: translate(-50%, 0%) rotateZ(360deg);
+										}
+									}
+									.monitor-active {
+										background-color: #22bc76;
+										.monitor-z-index {
+											color: #22bc76;
+										}
+									}
+								}
+							}
+							.flex-content-right {
+								flex: 1;
+							}
+						}
+					}
+				}
+			}
+			.big-data-down-right {
+				.flex-warp-item {
+					padding: 0 15px 15px 7.5px;
+					.flex-title {
+						color: var(--el-text-color-primary);
+					}
+					.flex-content {
+						display: flex;
+						flex-direction: column;
+						.task {
+							display: flex;
+							height: 45px;
+							.task-item {
+								flex: 1;
+								color: var(--el-color-white);
+								display: flex;
+								justify-content: center;
+								.task-item-box {
+									position: relative;
+									width: 45px;
+									height: 45px;
+									overflow: hidden;
+									border-radius: 100%;
+									z-index: 0;
+									display: flex;
+									align-items: center;
+									flex-direction: column;
+									justify-content: center;
+									box-shadow: 0 10px 12px 0 rgba(0, 0, 0, 0.3);
+									&::before {
+										content: '';
+										position: absolute;
+										z-index: -2;
+										left: -50%;
+										top: -50%;
+										width: 200%;
+										height: 200%;
+										background-repeat: no-repeat;
+										background-size: 50% 50%, 50% 50%;
+										background-position: 0 0, 100% 0, 100% 100%, 0 100%;
+										background-image: linear-gradient(#19d4ae, #19d4ae), linear-gradient(#5ab1ef, #5ab1ef), linear-gradient(#fa6e86, #fa6e86),
+											linear-gradient(#ffb980, #ffb980);
+										animation: rotate 2s linear infinite;
+									}
+									&::after {
+										content: '';
+										position: absolute;
+										z-index: -1;
+										left: 1px;
+										top: 1px;
+										width: calc(100% - 2px);
+										height: calc(100% - 2px);
+										border-radius: 100%;
+									}
+									.task-item-value {
+										text-align: center;
+										font-size: 14px;
+										font-weight: bold;
+									}
+									.task-item-label {
+										text-align: center;
+									}
+								}
+								.task1 {
+									&::after {
+										background: #5492be;
+									}
+								}
+								.task2 {
+									&::after {
+										background: #43a177;
+									}
+								}
+								.task3 {
+									&::after {
+										background: #a76077;
+									}
+								}
+							}
+							.task-first-item {
+								flex-direction: column;
+								text-align: center;
+								color: var(--el-color-primary);
+								.task-first {
+									font-size: 20px;
+								}
+							}
+						}
+						.progress {
+							color: var(--el-text-color-primary);
+							display: flex;
+							flex-direction: column;
+							flex: 1;
+							justify-content: space-between;
+							margin-top: 15px;
+							.progress-item {
+								height: 33.33%;
+								display: flex;
+								align-items: center;
+								.progress-box {
+									flex: 1;
+									width: 100%;
+									margin-left: 10px;
+									::v-deep(.el-progress__text) {
+										color: var(--el-text-color-primary);
+										font-size: 12px !important;
+										text-align: right;
+									}
+									::v-deep(.el-progress-bar__outer) {
+										background-color: rgba(0, 0, 0, 0.1) !important;
+									}
+									::v-deep(.el-progress-bar) {
+										margin-right: -22px !important;
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		}
+	}
+}
diff --git a/src/views/chart/chart.ts b/src/views/chart/chart.ts
new file mode 100644
index 0000000..6d10fd6
--- /dev/null
+++ b/src/views/chart/chart.ts
@@ -0,0 +1,59 @@
+/**
+ * sky 天气
+ * @returns 返回模拟数据
+ */
+export const skyList = [
+	{
+		v1: '时间',
+		v2: '天气',
+		v3: '温度',
+		v5: '降水',
+		v7: '风力',
+		type: 'title',
+	},
+	{
+		v1: '今天',
+		v2: 'ele-Sunny',
+		v3: '20°/26°',
+		v5: '50%',
+		v7: '13m/s',
+	},
+	{
+		v1: '明天',
+		v2: 'ele-Lightning',
+		v3: '20°/26°',
+		v5: '50%',
+		v7: '13m/s',
+	},
+];
+
+/**
+ * 当前设置状态
+ * @returns 返回模拟数据
+ */
+export const dBtnList = [
+	{
+		v2: '阳光玫瑰种植',
+		v3: '126天',
+		v4: '设备在线',
+	},
+];
+
+/**
+ * 当前设备监测
+ * @returns 返回模拟数据
+ */
+export const chartData4List = [
+	{
+		label: '温度',
+	},
+	{
+		label: '光照',
+	},
+	{
+		label: '湿度',
+	},
+	{
+		label: '风力',
+	},
+];
diff --git a/src/views/chart/head.vue b/src/views/chart/head.vue
new file mode 100644
index 0000000..82842e8
--- /dev/null
+++ b/src/views/chart/head.vue
@@ -0,0 +1,107 @@
+<template>
+	<div class="big-data-up mb15">
+		<div class="up-left">
+			<i class="el-icon-time mr5"></i>
+			<span>{{ time.txt }}</span>
+		</div>
+		<div class="up-center">
+			<span>智慧农业系统平台</span>
+		</div>
+	</div>
+</template>
+
+<script lang="ts">
+import { reactive, toRefs, onBeforeMount, onUnmounted, defineComponent } from 'vue';
+import { formatDate } from '/@/utils/formatTime';
+
+export default defineComponent({
+	name: 'chartHead',
+	setup() {
+		const state = reactive({
+			time: {
+				txt: '',
+				fun: 0,
+			},
+		});
+		// 初始化时间
+		const initTime = () => {
+			state.time.txt = formatDate(new Date(), 'YYYY-mm-dd HH:MM:SS WWW QQQQ');
+			state.time.fun = window.setInterval(() => {
+				state.time.txt = formatDate(new Date(), 'YYYY-mm-dd HH:MM:SS WWW QQQQ');
+			}, 1000);
+		};
+		// 页面加载前
+		onBeforeMount(() => {
+			initTime();
+		});
+		// 页面卸载时
+		onUnmounted(() => {
+			window.clearInterval(state.time.fun);
+		});
+		return {
+			...toRefs(state),
+		};
+	},
+});
+</script>
+
+<style scoped lang="scss">
+.big-data-up {
+	height: 55px;
+	width: 100%;
+	display: flex;
+	align-items: center;
+	padding: 0 15px;
+	color: var(--el-color-primary);
+	overflow: hidden;
+	position: relative;
+	.up-left {
+		position: absolute;
+	}
+	.up-center {
+		width: 100%;
+		display: flex;
+		justify-content: center;
+		font-size: 18px;
+		letter-spacing: 5px;
+		background-image: -webkit-linear-gradient(
+			left,
+			var(--el-color-primary),
+			var(--el-color-primary-light-3) 25%,
+			var(--el-color-primary) 50%,
+			var(--el-color-primary-light-3) 75%,
+			var(--el-color-primary)
+		);
+		-webkit-text-fill-color: transparent;
+		-webkit-background-clip: text;
+		background-clip: text;
+		background-size: 200% 100%;
+		-webkit-animation: masked-animation-data-v-b02d8052 4s linear infinite;
+		animation: masked-animation-data-v-b02d8052 4s linear infinite;
+		-webkit-box-reflect: below -2px -webkit-gradient(linear, left top, left bottom, from(transparent), to(hsla(0, 0%, 100%, 0.1)));
+		position: relative;
+		@keyframes masked-animation {
+			0% {
+				background-position: 0 0;
+			}
+			100% {
+				background-position: -100% 0;
+			}
+		}
+		position: relative;
+		&::after {
+			content: '';
+			width: 250px;
+			position: absolute;
+			bottom: -15px;
+			left: 50%;
+			transform: translateX(-50%);
+			border: 1px transparent solid;
+			border-image: linear-gradient(to right, var(--el-color-primary-light-9), var(--el-color-primary)) 1 10;
+		}
+		span {
+			cursor: pointer;
+		}
+	}
+}
+</style>
diff --git a/src/views/chart/index.vue b/src/views/chart/index.vue
new file mode 100644
index 0000000..db3255e
--- /dev/null
+++ b/src/views/chart/index.vue
@@ -0,0 +1,492 @@
+<template>
+	<div class="chart-scrollbar layout-view-bg-white" :style="{ height: `calc(100vh - ${initTagViewHeight}` }">
+		<div class="chart-warp">
+			<div class="chart-warp-top">
+				<ChartHead />
+			</div>
+			<div class="chart-warp-bottom">
+				<!-- 左边 -->
+				<div class="big-data-down-left">
+					<div class="flex-warp-item">
+						<div class="flex-warp-item-box">
+							<div class="flex-title">天气预报</div>
+							<div class="flex-content">
+								<div class="sky">
+									<SvgIcon name="ele-Sunny" class="sky-left" />
+									<div class="sky-center">
+										<div class="mb2">
+											<span>多云转晴</span>
+											<span>东南风</span>
+											<span class="span ml5">良</span>
+										</div>
+									</div>
+									<div class="sky-right">
+										<span>25</span>
+										<span>°C</span>
+									</div>
+								</div>
+								<div class="sky-dd">
+									<div class="sky-dl" v-for="(v, k) in skyList" :key="k" :class="{ 'sky-dl-first': k === 1 }">
+										<div>{{ v.v1 }}</div>
+										<div v-if="v.type === 'title'">{{ v.v2 }}</div>
+										<div v-else>
+											<SvgIcon :name="v.v2" />
+										</div>
+										<div>{{ v.v3 }}</div>
+										<div class="tip">{{ v.v5 }}</div>
+										<div>{{ v.v7 }}</div>
+									</div>
+								</div>
+							</div>
+						</div>
+					</div>
+					<div class="flex-warp-item">
+						<div class="flex-warp-item-box">
+							<div class="flex-title">当前设备状态</div>
+							<div class="flex-content flex-content-overflow">
+								<div class="d-states">
+									<div class="d-states-item">
+										<SvgIcon name="ele-Odometer" class="i-bg1" />
+										<div class="d-states-flex">
+											<div class="d-states-item-label">园区设备数</div>
+											<div class="d-states-item-value">99</div>
+										</div>
+									</div>
+									<div class="d-states-item">
+										<SvgIcon name="ele-FirstAidKit" class="i-bg2" />
+										<div class="d-states-flex">
+											<div class="d-states-item-label">预警设备数</div>
+											<div class="d-states-item-value">10</div>
+										</div>
+									</div>
+									<div class="d-states-item">
+										<SvgIcon name="ele-VideoPlay" class="i-bg3" />
+										<div class="d-states-flex">
+											<div class="d-states-item-label">运行设备数</div>
+											<div class="d-states-item-value">20</div>
+										</div>
+									</div>
+								</div>
+								<div class="d-btn">
+									<div class="d-btn-item" v-for="(v, k) in dBtnList" :key="k">
+										<i class="d-btn-item-left el-icon-money"></i>
+										<div class="d-btn-item-center">
+											<div>{{ v.v2 }}|{{ v.v3 }}</div>
+										</div>
+										<div class="d-btn-item-eight">{{ v.v4 }}</div>
+									</div>
+								</div>
+							</div>
+						</div>
+					</div>
+					<div class="flex-warp-item">
+						<div class="flex-warp-item-box">
+							<div class="flex-title">近30天预警总数</div>
+							<div class="flex-content">
+								<div style="height: 100%" ref="chartsWarningRef"></div>
+							</div>
+						</div>
+					</div>
+				</div>
+
+				<!-- 中间 -->
+				<div class="big-data-down-center">
+					<div class="big-data-down-center-one">
+						<div class="big-data-down-center-one-content">
+							<div style="height: 100%" ref="chartsCenterOneRef"></div>
+						</div>
+					</div>
+					<div class="big-data-down-center-two">
+						<div class="flex-warp-item-box">
+							<div class="flex-title">
+								<span>当前设备监测</span>
+								<span class="flex-title-small">单位:次</span>
+							</div>
+							<div class="flex-content">
+								<div class="flex-content-left">
+									<div class="monitor-item" v-for="(v, k) in chartData4List" :key="k">
+										<div class="monitor-wave">
+											<div class="monitor-z-index">
+												<div class="monitor-item-label">{{ v.label }}</div>
+											</div>
+										</div>
+									</div>
+								</div>
+								<div class="flex-content-right">
+									<div style="height: 100%" ref="chartsMonitorRef"></div>
+								</div>
+							</div>
+						</div>
+					</div>
+				</div>
+
+				<!-- 右边 -->
+				<div class="big-data-down-right">
+					<div class="flex-warp-item">
+						<div class="flex-warp-item-box">
+							<div class="flex-title">
+								<span>近7天产品追溯扫码统计</span>
+								<span class="flex-title-small">单位:次</span>
+							</div>
+							<div class="flex-content">
+								<div style="height: 100%" ref="chartsSevenDaysRef"></div>
+							</div>
+						</div>
+					</div>
+					<div class="flex-warp-item">
+						<div class="flex-warp-item-box">
+							<div class="flex-title">当前任务统计</div>
+							<div class="flex-content">
+								<div class="task">
+									<div class="task-item task-first-item">
+										<div class="task-item-value task-first">25</div>
+										<div class="task-item-label">待办任务</div>
+									</div>
+									<div class="task-item">
+										<div class="task-item-box task1">
+											<div class="task-item-value">12</div>
+											<div class="task-item-label">施肥</div>
+										</div>
+									</div>
+									<div class="task-item">
+										<div class="task-item-box task2">
+											<div class="task-item-value">3</div>
+											<div class="task-item-label">施药</div>
+										</div>
+									</div>
+									<div class="task-item">
+										<div class="task-item-box task3">
+											<div class="task-item-value">5</div>
+											<div class="task-item-label">农事</div>
+										</div>
+									</div>
+								</div>
+								<div class="progress">
+									<div class="progress-item">
+										<span>施肥率</span>
+										<div class="progress-box">
+											<el-progress :percentage="70" color="#43bdf0"></el-progress>
+										</div>
+									</div>
+									<div class="progress-item">
+										<span>施药率</span>
+										<div class="progress-box">
+											<el-progress :percentage="36" color="#43bdf0"></el-progress>
+										</div>
+									</div>
+									<div class="progress-item">
+										<span>农事率</span>
+										<div class="progress-box">
+											<el-progress :percentage="91" color="#43bdf0"></el-progress>
+										</div>
+									</div>
+								</div>
+							</div>
+						</div>
+					</div>
+					<div class="flex-warp-item">
+						<div class="flex-warp-item-box">
+							<div class="flex-title">
+								<span>近7天投入品记录</span>
+								<span class="flex-title-small">单位:件</span>
+							</div>
+							<div class="flex-content">
+								<div style="height: 100%" ref="chartsInvestmentRef"></div>
+							</div>
+						</div>
+					</div>
+				</div>
+			</div>
+		</div>
+	</div>
+</template>
+
+<script lang="ts">
+import { toRefs, reactive, computed, onMounted, getCurrentInstance, watch, nextTick, onActivated, defineComponent } from 'vue';
+import ChartHead from '/@/views/chart/head.vue';
+import * as echarts from 'echarts';
+import 'echarts-wordcloud';
+import { storeToRefs } from 'pinia';
+import { useThemeConfig } from '/@/stores/themeConfig';
+import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
+import { skyList, dBtnList, chartData4List } from '/@/views/chart/chart';
+
+export default defineComponent({
+	name: 'chartIndex',
+	components: { ChartHead },
+	setup() {
+		const { proxy } = <any>getCurrentInstance();
+		const storesThemeConfig = useThemeConfig();
+		const storesTagsViewRoutes = useTagsViewRoutes();
+		const { themeConfig } = storeToRefs(storesThemeConfig);
+		const { isTagsViewCurrenFull } = storeToRefs(storesTagsViewRoutes);
+		const state = reactive({
+			skyList,
+			dBtnList,
+			chartData4List,
+			myCharts: [],
+		});
+		// 设置主内容的高度
+		const initTagViewHeight = computed(() => {
+			let { isTagsview } = themeConfig.value;
+			if (isTagsViewCurrenFull.value) {
+				return `30px`;
+			} else {
+				if (isTagsview) return `114px`;
+				else return `80px`;
+			}
+		});
+		// 初始化中间图表1
+		const initChartsCenterOne = () => {
+			const myChart = echarts.init(proxy.$refs.chartsCenterOneRef);
+			const option = {
+				grid: {
+					top: 15,
+					right: 15,
+					bottom: 20,
+					left: 30,
+				},
+				tooltip: {},
+				series: [
+					{
+						type: 'wordCloud',
+						sizeRange: [12, 40],
+						rotationRange: [0, 0],
+						rotationStep: 45,
+						gridSize: Math.random() * 20 + 5,
+						shape: 'circle',
+						width: '100%',
+						height: '100%',
+						textStyle: {
+							fontFamily: 'sans-serif',
+							fontWeight: 'bold',
+							color: function () {
+								return `rgb(${[Math.round(Math.random() * 160), Math.round(Math.random() * 160), Math.round(Math.random() * 160)].join(',')})`;
+							},
+						},
+						data: [
+							{ name: 'vue-next-admin', value: 520 },
+							{ name: 'lyt', value: 520 },
+							{ name: 'next-admin', value: 500 },
+							{ name: '更名', value: 420 },
+							{ name: '智慧农业', value: 520 },
+							{ name: '男神', value: 2.64 },
+							{ name: '好身材', value: 4.03 },
+							{ name: '校草', value: 24.95 },
+							{ name: '酷', value: 4.04 },
+							{ name: '时尚', value: 5.27 },
+							{ name: '阳光活力', value: 5.8 },
+							{ name: '初恋', value: 3.09 },
+							{ name: '英俊潇洒', value: 24.71 },
+							{ name: '霸气', value: 6.33 },
+							{ name: '腼腆', value: 2.55 },
+							{ name: '蠢萌', value: 3.88 },
+							{ name: '青春', value: 8.04 },
+							{ name: '网红', value: 5.87 },
+							{ name: '萌', value: 6.97 },
+							{ name: '认真', value: 2.53 },
+							{ name: '古典', value: 2.49 },
+							{ name: '温柔', value: 3.91 },
+							{ name: '有个性', value: 3.25 },
+							{ name: '可爱', value: 9.93 },
+							{ name: '幽默诙谐', value: 3.65 },
+						],
+					},
+				],
+			};
+			myChart.setOption(option);
+			(<any>state.myCharts).push(myChart);
+		};
+		// 初始化近7天产品追溯扫码统计
+		const initChartsSevenDays = () => {
+			const myChart = echarts.init(proxy.$refs.chartsSevenDaysRef);
+			const option = {
+				grid: {
+					top: 15,
+					right: 15,
+					bottom: 20,
+					left: 30,
+				},
+				tooltip: {
+					trigger: 'axis',
+				},
+				xAxis: {
+					type: 'category',
+					boundaryGap: false,
+					data: ['1天', '2天', '3天', '4天', '5天', '6天', '7天'],
+				},
+				yAxis: {
+					type: 'value',
+				},
+				series: [
+					{
+						name: '邮件营销',
+						type: 'line',
+						stack: '总量',
+						data: [12, 32, 11, 34, 90, 23, 21],
+					},
+					{
+						name: '联盟广告',
+						type: 'line',
+						stack: '总量',
+						data: [22, 82, 91, 24, 90, 30, 30],
+					},
+					{
+						name: '视频广告',
+						type: 'line',
+						stack: '总量',
+						data: [50, 32, 18, 14, 90, 30, 50],
+					},
+				],
+			};
+			myChart.setOption(option);
+			(<any>state.myCharts).push(myChart);
+		};
+		// 初始化近30天预警总数
+		const initChartsWarning = () => {
+			const myChart = echarts.init(proxy.$refs.chartsWarningRef);
+			const option = {
+				grid: {
+					top: 50,
+					right: 20,
+					bottom: 30,
+					left: 30,
+				},
+				tooltip: {
+					trigger: 'item',
+				},
+				series: [
+					{
+						name: '面积模式',
+						type: 'pie',
+						radius: [20, 50],
+						center: ['50%', '50%'],
+						roseType: 'area',
+						itemStyle: {
+							borderRadius: 8,
+						},
+						data: [
+							{ value: 40, name: '监测设备预警' },
+							{ value: 38, name: '天气预警' },
+							{ value: 32, name: '任务预警' },
+							{ value: 30, name: '病虫害预警' },
+						],
+					},
+				],
+			};
+			myChart.setOption(option);
+			(<any>state.myCharts).push(myChart);
+		};
+		// 初始化当前设备监测
+		const initChartsMonitor = () => {
+			const myChart = echarts.init(proxy.$refs.chartsMonitorRef);
+			const option = {
+				grid: {
+					top: 15,
+					right: 15,
+					bottom: 20,
+					left: 30,
+				},
+				tooltip: {
+					trigger: 'axis',
+				},
+				xAxis: {
+					type: 'category',
+					boundaryGap: false,
+					data: ['02:00', '04:00', '06:00', '08:00', '10:00', '12:00', '14:00'],
+				},
+				yAxis: {
+					type: 'value',
+				},
+				series: [
+					{
+						itemStyle: {
+							color: '#289df5',
+							borderColor: '#289df5',
+							areaStyle: {
+								type: 'default',
+								opacity: 0.1,
+							},
+						},
+						data: [20, 32, 31, 34, 12, 13, 20],
+						type: 'line',
+						areaStyle: {},
+					},
+				],
+			};
+			myChart.setOption(option);
+			(<any>state.myCharts).push(myChart);
+		};
+		// 初始化近7天投入品记录
+		const initChartsInvestment = () => {
+			const myChart = echarts.init(proxy.$refs.chartsInvestmentRef);
+			const option = {
+				grid: {
+					top: 15,
+					right: 15,
+					bottom: 20,
+					left: 30,
+				},
+				tooltip: {
+					trigger: 'axis',
+				},
+				xAxis: {
+					type: 'category',
+					data: ['1天', '2天', '3天', '4天', '5天', '6天', '7天'],
+				},
+				yAxis: {
+					type: 'value',
+				},
+				series: [
+					{
+						data: [10, 20, 15, 80, 70, 11, 30],
+						type: 'bar',
+					},
+				],
+			};
+			myChart.setOption(option);
+			(<any>state.myCharts).push(myChart);
+		};
+		// 批量设置 echarts resize
+		const initEchartsResizeFun = () => {
+			nextTick(() => {
+				for (let i = 0; i < state.myCharts.length; i++) {
+					(<any>state.myCharts[i]).resize();
+				}
+			});
+		};
+		// 批量设置 echarts resize
+		const initEchartsResize = () => {
+			window.addEventListener('resize', initEchartsResizeFun);
+		};
+		// 页面加载时
+		onMounted(() => {
+			initChartsCenterOne();
+			initChartsSevenDays();
+			initChartsWarning();
+			initChartsMonitor();
+			initChartsInvestment();
+			initEchartsResize();
+		});
+		// 由于页面缓存原因,keep-alive
+		onActivated(() => {
+			initEchartsResizeFun();
+		});
+		// 监听 vuex 中的 tagsview 开启全屏变化,重新 resize 图表,防止不出现/大小不变等
+		watch(
+			() => isTagsViewCurrenFull.value,
+			() => {
+				initEchartsResizeFun();
+			}
+		);
+		return {
+			initTagViewHeight,
+			...toRefs(state),
+		};
+	},
+});
+</script>
+
+<style scoped lang="scss">
+@import './chart.scss';
+</style>
diff --git a/src/views/dashboard/index.vue b/src/views/dashboard/index.vue
new file mode 100644
index 0000000..6b27d59
--- /dev/null
+++ b/src/views/dashboard/index.vue
@@ -0,0 +1,35 @@
+<template>
+    <div>
+        <button @click="toLink">跳转</button>
+    </div>
+</template>
+
+<script lang="ts">
+    import {useRoute, useRouter} from "vue-router";
+
+    export default {
+        name: "index",
+        setup() {
+            const route = useRoute();
+            const router = useRouter();
+            const toLink = () => {
+                if (route.query?.redirect) {
+                    router.push('/dashboard');
+                    // 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('/');
+                }
+            };
+            return {
+                toLink,
+            }
+        }
+    }
+</script>
+
+<style scoped>
+
+</style>
diff --git a/src/views/error/401.vue b/src/views/error/401.vue
new file mode 100644
index 0000000..3ca649d
--- /dev/null
+++ b/src/views/error/401.vue
@@ -0,0 +1,117 @@
+<template>
+	<div class="error layout-view-bg-white" :style="{ height: `calc(100vh - ${initTagViewHeight}` }">
+		<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" 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>
+</template>
+
+<script lang="ts">
+import { defineComponent, computed } from 'vue';
+import { useRouter } from 'vue-router';
+import { storeToRefs } from 'pinia';
+import { useThemeConfig } from '/@/stores/themeConfig';
+import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
+import { Session } from '/@/utils/storage';
+
+export default defineComponent({
+	name: '401',
+	setup() {
+		const storesThemeConfig = useThemeConfig();
+		const storesTagsViewRoutes = useTagsViewRoutes();
+		const { themeConfig } = storeToRefs(storesThemeConfig);
+		const { isTagsViewCurrenFull } = storeToRefs(storesTagsViewRoutes);
+		const router = useRouter();
+		const onSetAuth = () => {
+			Session.clear();
+			router.push('/login');
+		};
+		// 设置主内容的高度
+		const initTagViewHeight = computed(() => {
+			let { isTagsview } = themeConfig.value;
+			if (isTagsViewCurrenFull.value) {
+				return `30px`;
+			} else {
+				if (isTagsview) return `114px`;
+				else return `80px`;
+			}
+		});
+		return {
+			onSetAuth,
+			initTagViewHeight,
+		};
+	},
+});
+</script>
+
+<style scoped lang="scss">
+.error {
+	height: 100%;
+	background-color: var(--el-color-white);
+	display: flex;
+	.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>
diff --git a/src/views/error/404.vue b/src/views/error/404.vue
new file mode 100644
index 0000000..3a7ce22
--- /dev/null
+++ b/src/views/error/404.vue
@@ -0,0 +1,115 @@
+<template>
+	<div class="error layout-view-bg-white" :style="{ height: `calc(100vh - ${initTagViewHeight}` }">
+		<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" 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>
+</template>
+
+<script lang="ts">
+import { defineComponent, computed } from 'vue';
+import { useRouter } from 'vue-router';
+import { storeToRefs } from 'pinia';
+import { useThemeConfig } from '/@/stores/themeConfig';
+import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
+
+export default defineComponent({
+	name: '404',
+	setup() {
+		const storesThemeConfig = useThemeConfig();
+		const storesTagsViewRoutes = useTagsViewRoutes();
+		const { themeConfig } = storeToRefs(storesThemeConfig);
+		const { isTagsViewCurrenFull } = storeToRefs(storesTagsViewRoutes);
+		const router = useRouter();
+		const onGoHome = () => {
+			router.push('/');
+		};
+		// 设置主内容的高度
+		const initTagViewHeight = computed(() => {
+			let { isTagsview } = themeConfig.value;
+			if (isTagsViewCurrenFull.value) {
+				return `30px`;
+			} else {
+				if (isTagsview) return `114px`;
+				else return `80px`;
+			}
+		});
+		return {
+			onGoHome,
+			initTagViewHeight,
+		};
+	},
+});
+</script>
+
+<style scoped lang="scss">
+.error {
+	height: 100%;
+	background-color: var(--el-color-white);
+	display: flex;
+	.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>
diff --git a/src/views/home/index.vue b/src/views/home/index.vue
new file mode 100644
index 0000000..2923664
--- /dev/null
+++ b/src/views/home/index.vue
@@ -0,0 +1,185 @@
+<template>
+	<div class="home-container">
+		<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 homeOne"
+				:key="k"
+				:class="{ 'home-media home-media-lg': k > 1, 'home-media-sm': k === 1 }"
+			>
+				<div class="home-card-item flex" >
+					<el-button @click="renderMenu">{{v.name}}</el-button>
+				</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%"></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%"></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>
+			</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%"></div>
+				</div>
+			</el-col>
+		</el-row>
+	</div>
+</template>
+
+<script lang="ts">
+import { toRefs, reactive, defineComponent, onMounted, ref, watch, nextTick, onActivated } from 'vue';
+import * as echarts from 'echarts';
+import { storeToRefs } from 'pinia';
+import { useThemeConfig } from '/@/stores/themeConfig';
+import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
+import { initBackEndControlRoutes } from '/@/router/backEnd';
+import {Session} from "/@/utils/storage";
+
+let global: any = {
+	homeChartOne: null,
+	homeChartTwo: null,
+	homeCharThree: null,
+	dispose: [null, '', undefined],
+};
+
+interface stateType {
+	projectId:string,
+	homeOne: Array <type>
+}
+interface type {
+	id:number,
+	name: string
+}
+export default defineComponent({
+	name: 'home',
+	setup() {
+		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<stateType>({
+			projectId:'',
+			homeOne:[{id:1,name:'系统1'},{id:2,name:'系统2'},{id:3,name:'系统4'},{id:4,name:'系统4'}],
+		});
+		// 折线图
+		const renderMenu = async() => {
+			state.projectId = '1'
+			Session.set('projectId','1')
+			await initBackEndControlRoutes();
+		};
+		return {
+			renderMenu,
+			...toRefs(state),
+		};
+	},
+});
+</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/10) + 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>
diff --git a/src/views/login/component/account.vue b/src/views/login/component/account.vue
new file mode 100644
index 0000000..29b40dd
--- /dev/null
+++ b/src/views/login/component/account.vue
@@ -0,0 +1,196 @@
+<template>
+	<el-form size="large" class="login-content-form">
+		<el-form-item class="login-animation1">
+			<el-input type="text" :placeholder="$t('message.account.accountPlaceholder1')" v-model="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="isShowPassword ? 'text' : 'password'"
+				:placeholder="$t('message.account.accountPlaceholder2')"
+				v-model="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="isShowPassword ? 'icon-yincangmima' : 'icon-xianshimima'"
+						@click="isShowPassword = !isShowPassword"
+					>
+					</i>
+				</template>
+			</el-input>
+		</el-form-item>
+		<el-form-item class="login-animation3">
+			<el-col :span="15">
+				<el-input
+					type="text"
+					maxlength="4"
+					:placeholder="$t('message.account.accountPlaceholder3')"
+					v-model="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">1234</el-button>
+			</el-col>
+		</el-form-item>
+		<el-form-item class="login-animation4">
+			<el-button type="primary" class="login-content-submit" round @click="onSignIn" :loading="loading.signIn">
+				<span>{{ $t('message.account.accountBtnText') }}</span>
+			</el-button>
+		</el-form-item>
+	</el-form>
+</template>
+
+<script lang="ts">
+import { toRefs, reactive, defineComponent, 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 { useLoginApi } from '/@/api/login';
+import {useUserInfo} from "/@/stores/userInfo";
+
+export default defineComponent({
+	name: 'loginAccount',
+	setup() {
+		const { t } = useI18n();
+		const userInfo = useUserInfo()
+		const { userInfos } = storeToRefs(userInfo);
+		const storesThemeConfig = useThemeConfig();
+		const { themeConfig } = storeToRefs(storesThemeConfig);
+		const route = useRoute();
+		const router = useRouter();
+		const state = reactive({
+			isShowPassword: false,
+			ruleForm: {
+				username: 'admin',
+				password: '123456',
+			},
+			loading: {
+				signIn: false,
+			},
+		});
+		// 时间获取
+		const currentTime = computed(() => {
+			return formatAxis(new Date());
+		});
+		// 登录
+		const onSignIn = async () => {
+			state.loading.signIn = true;
+			// 存储 token 到浏览器缓存
+			let res = await useLoginApi().signIn(state.ruleForm)
+			if(res.data.code === '200'){
+				Session.set('ifMenu',false)
+				Session.set('projectId','')
+				Session.set('token', res.data.data.accessToken);
+				Session.set('sign',res.data.data.id)
+				await initFrontEndControlRoutes();
+				signInSuccess();
+			}else{
+				state.loading.signIn = false
+				ElMessage({
+					type:'warning',
+					message:res.data.msg
+				})
+			}
+			// Session.set('token', Math.random().toString(36).substr(0));
+			// // 模拟数据,对接接口时,记得删除多余代码及对应依赖的引入。用于 `/src/stores/userInfo.ts` 中不同用户登录判断(模拟数据)
+			// Cookies.set('userName', state.ruleForm.username);
+			// if (!themeConfig.value.isRequestRoutes) {
+			// 	// 前端控制路由,2、请注意执行顺序
+			// 	await initFrontEndControlRoutes();
+			// 	signInSuccess();
+			// } else {
+			// 	// 模拟后端控制路由,isRequestRoutes 为 true,则开启后端控制路由
+			// 	// 添加完动态路由,再进行 router 跳转,否则可能报错 No match found for location with path "/"
+			// 	await initBackEndControlRoutes();
+			// 	// 执行完 initBackEndControlRoutes,再执行 signInSuccess
+			// 	signInSuccess();
+			// }
+		};
+		// 登录成功后的跳转
+		const signInSuccess = async () => {
+			// 初始化登录成功时间问候语
+			let currentTimeInfo = currentTime.value;
+			// 登录成功,跳到转首页
+			// 如果是复制粘贴的路径,非首页/登录页,那么登录成功后重定向到对应的路径中
+			if (route.query?.redirect) {
+				router.push('/');
+				// 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('/login');
+			}
+			state.loading.signIn = true;
+			const signInText = t('message.signInText');
+			ElMessage.success(`${currentTimeInfo},${signInText}`);
+			// 登录成功提示
+			// 关闭 loading
+			// 添加 loading,防止第一次进入界面时出现短暂空白
+			// NextLoading.start();
+		};
+		return {
+			onSignIn,
+			...toRefs(state),
+		};
+	},
+});
+</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>
diff --git a/src/views/login/index.vue b/src/views/login/index.vue
new file mode 100644
index 0000000..3ded06e
--- /dev/null
+++ b/src/views/login/index.vue
@@ -0,0 +1,190 @@
+<template>
+	<div class="login-container">
+		<div class="login-icon-group">
+			<img :src="loginIconTwo" class="login-icon-group-icon" />
+		</div>
+		<div class="login-content">
+			<div class="login-content-main">
+				<h4 class="login-content-title ml15"></h4>
+				<div v-if="!isScan">
+					<el-tabs v-model="tabsActiveName">
+						<el-tab-pane :label="$t('message.label.one1')" name="account">
+							<Account />
+						</el-tab-pane>
+					</el-tabs>
+				</div>
+				<Scan v-if="isScan" />
+				<div class="login-content-main-sacn" @click="isScan = !isScan">
+					<div class="login-content-main-sacn-delta"></div>
+				</div>
+			</div>
+		</div>
+	</div>
+</template>
+
+<script lang="ts">
+import { toRefs, reactive, computed, defineComponent, onMounted } from 'vue';
+import { storeToRefs } from 'pinia';
+import { useThemeConfig } from '/@/stores/themeConfig';
+import logoMini from '/@/assets/logo-mini.svg';
+import loginIconTwo from '/@/assets/login-icon-two.svg';
+import { NextLoading } from '/@/utils/loading';
+import Account from '/@/views/login/component/account.vue';
+
+// 定义接口来定义对象的类型
+interface LoginState {
+	tabsActiveName: string;
+	isScan: boolean;
+}
+
+export default defineComponent({
+	name: 'loginIndex',
+	components: { Account, },
+	setup() {
+		const storesThemeConfig = useThemeConfig();
+		const { themeConfig } = storeToRefs(storesThemeConfig);
+		const state = reactive<LoginState>({
+			tabsActiveName: 'account',
+			isScan: false,
+		});
+		// 获取布局配置信息
+		const getThemeConfig = computed(() => {
+			return themeConfig.value;
+		});
+		// 页面加载时
+		onMounted(() => {
+			NextLoading.done();
+		});
+		return {
+			logoMini,
+			loginIconTwo,
+			getThemeConfig,
+			...toRefs(state),
+		};
+	},
+});
+</script>
+
+<style scoped lang="scss">
+.login-container {
+	width: 100%;
+	height: 100%;
+	position: relative;
+	background: var(--el-color-white);
+	.login-icon-group {
+		width: 100%;
+		height: 100%;
+		position: relative;
+		.login-icon-group-title {
+			position: absolute;
+			top: 50px;
+			left: 80px;
+			display: flex;
+			align-items: center;
+			img {
+				width: 30px;
+				height: 30px;
+			}
+			&-text {
+				padding-left: 15px;
+				color: var(--el-color-primary);
+			}
+		}
+		&::before {
+			content: '';
+			position: absolute;
+			bottom: 0;
+			left: 0;
+			width: 60%;
+			overflow: hidden;
+			height: 80%;
+			-webkit-mask-box-image: url("data:image/svg+xml,%3Csvg width='1200' height='770' xmlns='http://www.w3.org/2000/svg' fill='none'%3E%3Cg%3E%3Cpath id='svg_1' d='M58.4 47.77C104.6 59.51 135.26 67.37 162.11 78.04C188.97 88.72 226.33 102.69 265.92 123.55C305.51 144.4 366.96 167.09 441.43 121.52C515.9 75.95 546.48 61.01 577.69 46.27C608.9 31.53 625.86 23.69 680.26 12.28C734.65 0.87 837.29 10.7 867.29 21.8C897.29 32.9 935.51 51.9 962.21 95.45C988.9 139.01 972.91 177.36 951.37 221.39C929.83 265.43 883.49 306 890.44 337.33C897.4 368.66 974.73 412.18 974.73 411.47C974.73 412.18 1066.36 457.62 1106.36 491.06C1146.36 524.5 1178.8 563.36 1184.03 579.63C1189.26 595.9 1200.4 622.49 1181.55 676.88C1162.71 731.26 1127.16 764.32 1115.31 778.64C1103.45 792.96 5.34 783.61 4.32 784.63C3.3 785.65 -172.34 2.38 1.13 35.04L58.4 47.77L58.4 47.77Z' fill='%23409eff'%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E");
+			background: var(--el-color-primary-light-5);
+			transition: all 0.3s ease;
+		}
+		&::after {
+			content: '';
+			width: 150px;
+			height: 300px;
+			position: absolute;
+			right: 0;
+			top: 0;
+			-webkit-mask-box-image: url("data:image/svg+xml,%3Csvg width='150' height='300' xmlns='http://www.w3.org/2000/svg' fill='none'%3E%3Cg%3E%3Cpath id='svg_1' d='M-0.56 -0.28C41.94 36.17 67.73 18.94 93.33 33.96C118.93 48.98 107.58 73.56 101.94 89.76C96.29 105.96 50.09 217.83 47.87 231.18C45.64 244.52 46.02 255.2 64.4 270.05C82.79 284.91 121.99 292.31 111.98 289.81C101.97 287.32 153.96 301.48 151.83 299.9C149.69 298.32 149.98 -1.36 149.71 -1.18C149.98 -1.36 -43.06 -36.74 -0.56 -0.28L-0.56 -0.28Z' fill='%23409eff'%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E");
+			background: var(--el-color-primary-light-5);
+			transition: all 0.3s ease;
+		}
+		&-icon {
+			width: 60%;
+			height: 70%;
+			position: absolute;
+			left: 0;
+			bottom: 0;
+		}
+	}
+	.login-content {
+		width: 500px;
+		padding: 20px;
+		position: absolute;
+		right: 200px;
+		top: 50%;
+		transform: translateY(-50%) translate3d(0, 0, 0);
+		background-color: var(--el-color-white);
+		border: 5px solid var(--el-color-primary-light-8);
+		border-radius: 5px;
+		overflow: hidden;
+		z-index: 1;
+		height: 460px;
+		.login-content-main {
+			margin: 0 auto;
+			width: 80%;
+			.login-content-title {
+				color: var(--el-text-color-primary);
+				font-weight: 500;
+				font-size: 22px;
+				text-align: center;
+				letter-spacing: 4px;
+				margin: 15px 0 30px;
+				white-space: nowrap;
+				z-index: 5;
+				position: relative;
+				transition: all 0.3s ease;
+			}
+		}
+		.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-text-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: 2px;
+				top: -1px;
+			}
+		}
+	}
+}
+</style>
diff --git a/src/views/make/noticeBar/index.vue b/src/views/make/noticeBar/index.vue
new file mode 100644
index 0000000..2bb8730
--- /dev/null
+++ b/src/views/make/noticeBar/index.vue
@@ -0,0 +1,164 @@
+<template>
+	<div class="notice-bar-container">
+		<el-card shadow="hover" header="滚动通知栏:默认">
+			<NoticeBar
+				text="🎉🎉🔥基于vue3.x 、Typescript、vite、Element plus等,适配手机、平板、pc
+				的后台开源免费模板库(vue2.x请切换vue-prev-admin分支),仓库地址:https://gitee.com/lyt-top/vue-next-admin"
+			/>
+		</el-card>
+
+		<el-card shadow="hover" header="滚动通知栏:设置样式" class="mt15">
+			<NoticeBar
+				text="🎉🎉🔥基于vue3.x 、Typescript、vite、Element plus等,适配手机、平板、pc
+				的后台开源免费模板库(vue2.x请切换vue-prev-admin分支),仓库地址:https://gitee.com/lyt-top/vue-next-admin"
+				leftIcon="iconfont icon-tongzhi2"
+				rightIcon="ele-ArrowRight"
+				background="#ecf5ff"
+				color="#409eff"
+			/>
+		</el-card>
+
+		<el-card shadow="hover" header="滚动通知栏:搭配 NoticeBar 和 Carousel 走马灯 组件可以实现垂直滚动的效果" class="mt15">
+			<NoticeBar :scrollable="true">
+				<el-carousel height="40px" direction="vertical" :autoplay="true" indicator-position="none" :interval="3000">
+					<el-carousel-item v-for="v in noticeList" :key="v">{{ v }} </el-carousel-item>
+				</el-carousel>
+			</NoticeBar>
+		</el-card>
+
+		<el-card shadow="hover" header="滚动通知栏:参数" class="mt15">
+			<el-table :data="tableData" style="width: 100%">
+				<el-table-column prop="a1" label="参数"> </el-table-column>
+				<el-table-column prop="a2" label="说明"> </el-table-column>
+				<el-table-column prop="a3" label="类型"> </el-table-column>
+				<el-table-column prop="a4" label="可选值"> </el-table-column>
+				<el-table-column prop="a5" label="默认值"> </el-table-column>
+			</el-table>
+		</el-card>
+
+		<el-card shadow="hover" header="图标选择器(宽度自动):事件" class="mt15">
+			<el-table :data="tableData1" style="width: 100%">
+				<el-table-column prop="a1" label="事件名称"> </el-table-column>
+				<el-table-column prop="a2" label="说明"> </el-table-column>
+				<el-table-column prop="a3" label="类型"> </el-table-column>
+				<el-table-column prop="a4" label="回调参数"> </el-table-column>
+			</el-table>
+		</el-card>
+	</div>
+</template>
+
+<script lang="ts">
+import { toRefs, reactive, defineComponent } from 'vue';
+import NoticeBar from '/@/components/noticeBar/index.vue';
+
+export default defineComponent({
+	name: 'makeNoticeBar',
+	components: { NoticeBar },
+	setup() {
+		const state = reactive({
+			noticeList: [
+				'🎉🎉🔥基于vue3.x 、Typescript、vite、Element plus等',
+				'适配手机、平板、pc的后台开源免费模板库(vue2.x请切换vue-prev-admin分支)',
+				'仓库地址:https://gitee.com/lyt-top/vue-next-admin',
+				'演示地址:https://lyt-top.gitee.io/vue-next-admin-preview/#/login',
+			],
+			tableData: [
+				{
+					a1: 'mode',
+					a2: '通知栏模式,用于右侧 icon 图标点击',
+					a3: 'string',
+					a4: 'closeable / link',
+					a5: '',
+				},
+				{
+					a1: 'text',
+					a2: '通知文本内容,scrollable 为 false 时生效',
+					a3: 'string',
+					a4: '',
+					a5: '',
+				},
+				{
+					a1: 'color',
+					a2: '通知文本颜色',
+					a3: 'string',
+					a4: '',
+					a5: '#e6a23c',
+				},
+				{
+					a1: 'background',
+					a2: '通知背景色',
+					a3: 'string',
+					a4: '',
+					a5: '#fdf6ec',
+				},
+				{
+					a1: 'size',
+					a2: '字体大小,单位px',
+					a3: 'number / string',
+					a4: '',
+					a5: '14',
+				},
+				{
+					a1: 'height',
+					a2: '通知栏高度,单位px',
+					a3: 'number / string',
+					a4: '',
+					a5: '40',
+				},
+				{
+					a1: 'delay',
+					a2: '动画延迟时间 (s)',
+					a3: 'number / string',
+					a4: '',
+					a5: '1',
+				},
+				{
+					a1: 'speed',
+					a2: '滚动速率 (px/s)',
+					a3: 'number / string',
+					a4: '',
+					a5: '100',
+				},
+				{
+					a1: 'scrollable',
+					a2: '是否开启垂直滚动',
+					a3: 'boolean',
+					a4: 'true',
+					a5: 'false',
+				},
+				{
+					a1: 'leftIcon',
+					a2: '自定义左侧图标',
+					a3: 'string',
+					a4: '',
+					a5: '',
+				},
+				{
+					a1: 'rightIcon',
+					a2: '自定义右侧图标',
+					a3: 'string',
+					a4: '',
+					a5: '',
+				},
+			],
+			tableData1: [
+				{
+					a1: 'close',
+					a2: '通知栏模式(mode)closeable 时回调事件',
+					a3: 'function',
+					a4: '',
+				},
+				{
+					a1: 'link',
+					a2: '通知栏模式(mode)link 时回调事件',
+					a3: 'function',
+					a4: '',
+				},
+			],
+		});
+		return {
+			...toRefs(state),
+		};
+	},
+});
+</script>
diff --git a/src/views/make/selector/index.vue b/src/views/make/selector/index.vue
new file mode 100644
index 0000000..db7c34a
--- /dev/null
+++ b/src/views/make/selector/index.vue
@@ -0,0 +1,126 @@
+<template>
+	<div class="selector-container">
+		<el-card shadow="hover" header="图标选择器(宽度自动):">
+			<IconSelector @get="onGetIcon" @clear="onClearIcon" v-model="modelIcon" />
+		</el-card>
+
+		<el-card shadow="hover" header="图标选择器(宽度自动):参数" class="mt15">
+			<el-table :data="tableData" style="width: 100%">
+				<el-table-column prop="a1" label="参数"> </el-table-column>
+				<el-table-column prop="a2" label="说明"> </el-table-column>
+				<el-table-column prop="a3" label="类型"> </el-table-column>
+				<el-table-column prop="a4" label="可选值"> </el-table-column>
+				<el-table-column prop="a5" label="默认值"> </el-table-column>
+			</el-table>
+		</el-card>
+
+		<el-card shadow="hover" header="图标选择器(宽度自动):事件" class="mt15">
+			<el-table :data="tableData1" style="width: 100%">
+				<el-table-column prop="a1" label="事件名称"> </el-table-column>
+				<el-table-column prop="a2" label="说明"> </el-table-column>
+				<el-table-column prop="a3" label="类型"> </el-table-column>
+				<el-table-column prop="a4" label="回调参数"> </el-table-column>
+			</el-table>
+		</el-card>
+	</div>
+</template>
+
+<script lang="ts">
+import { toRefs, reactive, defineComponent } from 'vue';
+import IconSelector from '/@/components/iconSelector/index.vue';
+
+export default defineComponent({
+	name: 'makeSelector',
+	components: { IconSelector },
+	setup() {
+		const state = reactive({
+			modelIcon: '',
+			tableData: [
+				{
+					a1: 'prepend',
+					a2: '输入框前置内容,只能字体图标',
+					a3: 'string',
+					a4: '',
+					a5: 'ele-Pointer',
+				},
+				{
+					a1: 'placeholder',
+					a2: '输入框占位文本',
+					a3: 'string',
+					a4: '',
+					a5: '请输入内容搜索图标或者选择图标',
+				},
+				{
+					a1: 'size',
+					a2: '尺寸',
+					a3: 'string',
+					a4: 'large / default / small',
+					a5: 'default',
+				},
+				{
+					a1: 'title',
+					a2: '弹窗标题',
+					a3: 'string',
+					a4: '',
+					a5: '请选择图标',
+				},
+				{
+					a1: 'type',
+					a2: 'icon 图标类型',
+					a3: 'string',
+					a4: 'ali / ele / awe / all',
+					a5: 'ele',
+				},
+				{
+					a1: 'disabled',
+					a2: '禁用',
+					a3: 'boolean',
+					a4: 'true',
+					a5: 'false',
+				},
+				{
+					a1: 'clearable',
+					a2: '是否可清空',
+					a3: 'boolean',
+					a4: 'false',
+					a5: 'true',
+				},
+				{
+					a1: 'emptyDescription',
+					a2: '自定义空状态描述文字',
+					a3: 'String',
+					a4: '',
+					a5: '无相关图标',
+				},
+			],
+			tableData1: [
+				{
+					a1: 'get',
+					a2: '获取当前点击的 icon 图标',
+					a3: 'function',
+					a4: '(icon: string)',
+				},
+				{
+					a1: 'clear',
+					a2: '清空当前点击的 icon 图标',
+					a3: 'function',
+					a4: '(icon: string)',
+				},
+			],
+		});
+		// 获取当前点击的 icon 图标
+		const onGetIcon = (icon: string) => {
+			console.log(icon);
+		};
+		// 清空当前点击的 icon 图标
+		const onClearIcon = (icon: string) => {
+			console.log(icon);
+		};
+		return {
+			onGetIcon,
+			onClearIcon,
+			...toRefs(state),
+		};
+	},
+});
+</script>
diff --git a/src/views/make/svgDemo/index.vue b/src/views/make/svgDemo/index.vue
new file mode 100644
index 0000000..36fd320
--- /dev/null
+++ b/src/views/make/svgDemo/index.vue
@@ -0,0 +1,59 @@
+<template>
+	<div class="svg-demo-container">
+		<el-card shadow="hover" header="svgIcon:演示(支持本地svg)">
+			<SvgIcon name="iconfont icon-shuju1" color="red" :size="30" />
+			<SvgIcon name="ele-Trophy" color="var(--el-color-primary)" :size="30" />
+			<SvgIcon name="fa fa-flag-checkered" color="#09f" :size="30" />
+			<SvgIcon :name="logoMini" color="#09f" :size="30" />
+		</el-card>
+		<el-card shadow="hover" header="svgIcon:参数" class="mt15">
+			<el-table :data="tableData" style="width: 100%">
+				<el-table-column prop="a1" label="参数"> </el-table-column>
+				<el-table-column prop="a2" label="说明"> </el-table-column>
+				<el-table-column prop="a3" label="类型"> </el-table-column>
+				<el-table-column prop="a4" label="可选值"> </el-table-column>
+				<el-table-column prop="a5" label="默认值"> </el-table-column>
+			</el-table>
+		</el-card>
+	</div>
+</template>
+
+<script lang="ts">
+import { toRefs, reactive, defineComponent } from 'vue';
+import logoMini from '/@/assets/logo-mini.svg';
+
+export default defineComponent({
+	name: 'makeSvgDemo',
+	setup() {
+		const state = reactive({
+			tableData: [
+				{
+					a1: 'name',
+					a2: 'svg 图标组件名字 / svg 路径 url',
+					a3: 'string',
+					a4: '',
+					a5: '',
+				},
+				{
+					a1: 'size',
+					a2: 'svg 大小',
+					a3: 'number',
+					a4: '',
+					a5: 14,
+				},
+				{
+					a1: 'color',
+					a2: 'svg 颜色',
+					a3: 'string',
+					a4: '',
+					a5: '',
+				},
+			],
+		});
+		return {
+			logoMini,
+			...toRefs(state),
+		};
+	},
+});
+</script>
diff --git a/src/views/menu/menu1/menu11/index.vue b/src/views/menu/menu1/menu11/index.vue
new file mode 100644
index 0000000..944beb8
--- /dev/null
+++ b/src/views/menu/menu1/menu11/index.vue
@@ -0,0 +1,21 @@
+<template>
+	<div>
+		<el-input v-model="val" placeholder="menu11:请输入内容测试路由缓存"></el-input>
+	</div>
+</template>
+
+<script lang="ts">
+import { toRefs, reactive, defineComponent } from 'vue';
+
+export default defineComponent({
+	name: 'menu11',
+	setup() {
+		const state = reactive({
+			val: '',
+		});
+		return {
+			...toRefs(state),
+		};
+	},
+});
+</script>
diff --git a/src/views/menu/menu1/menu12/menu121/index.vue b/src/views/menu/menu1/menu12/menu121/index.vue
new file mode 100644
index 0000000..791130b
--- /dev/null
+++ b/src/views/menu/menu1/menu12/menu121/index.vue
@@ -0,0 +1,21 @@
+<template>
+	<div>
+		<el-input v-model="val" placeholder="menu121:请输入内容测试路由缓存"></el-input>
+	</div>
+</template>
+
+<script lang="ts">
+import { toRefs, reactive, defineComponent } from 'vue';
+
+export default defineComponent({
+	name: 'menu121',
+	setup() {
+		const state = reactive({
+			val: '',
+		});
+		return {
+			...toRefs(state),
+		};
+	},
+});
+</script>
diff --git a/src/views/menu/menu1/menu12/menu122/index.vue b/src/views/menu/menu1/menu12/menu122/index.vue
new file mode 100644
index 0000000..3db8096
--- /dev/null
+++ b/src/views/menu/menu1/menu12/menu122/index.vue
@@ -0,0 +1,21 @@
+<template>
+	<div>
+		<el-input v-model="val" placeholder="menu122:请输入内容测试路由缓存"></el-input>
+	</div>
+</template>
+
+<script lang="ts">
+import { toRefs, reactive, defineComponent } from 'vue';
+
+export default defineComponent({
+	name: 'menu122',
+	setup() {
+		const state = reactive({
+			val: '',
+		});
+		return {
+			...toRefs(state),
+		};
+	},
+});
+</script>
diff --git a/src/views/menu/menu1/menu13/index.vue b/src/views/menu/menu1/menu13/index.vue
new file mode 100644
index 0000000..f984dcc
--- /dev/null
+++ b/src/views/menu/menu1/menu13/index.vue
@@ -0,0 +1,27 @@
+<template>
+	<div>
+		<el-input v-model="val" placeholder="menu13:请输入内容测试路由缓存"></el-input>
+	</div>
+</template>
+
+<script lang="ts">
+import { toRefs, reactive, onActivated, onMounted, defineComponent } from 'vue';
+
+export default defineComponent({
+	name: 'menu13',
+	setup() {
+		const state = reactive({
+			val: '',
+		});
+		onMounted(() => {
+			console.log(2222);
+		});
+		onActivated(() => {
+			console.log(1111);
+		});
+		return {
+			...toRefs(state),
+		};
+	},
+});
+</script>
diff --git a/src/views/menu/menu2/index.vue b/src/views/menu/menu2/index.vue
new file mode 100644
index 0000000..0081506
--- /dev/null
+++ b/src/views/menu/menu2/index.vue
@@ -0,0 +1,21 @@
+<template>
+	<div>
+		<el-input v-model="val" placeholder="menu2:请输入内容测试路由缓存"></el-input>
+	</div>
+</template>
+
+<script lang="ts">
+import { toRefs, reactive, defineComponent } from 'vue';
+
+export default defineComponent({
+	name: 'menu2',
+	setup() {
+		const state = reactive({
+			val: '',
+		});
+		return {
+			...toRefs(state),
+		};
+	},
+});
+</script>
diff --git a/src/views/pages/awesome/index.vue b/src/views/pages/awesome/index.vue
new file mode 100644
index 0000000..52b53bb
--- /dev/null
+++ b/src/views/pages/awesome/index.vue
@@ -0,0 +1,87 @@
+<template>
+	<div class="awesome-container">
+		<el-card shadow="hover" :header="`fontawesome 字体图标(自动载入):${sheetsIconList.length - 24}个`">
+			<el-row class="iconfont-row">
+				<el-col :xs="12" :sm="8" :md="6" :lg="4" :xl="2" v-for="(v, k) in sheetsIconList" :key="k">
+					<div class="iconfont-warp">
+						<div class="flex-margin">
+							<div class="iconfont-warp-value">
+								<i :class="v" class="fa"></i>
+							</div>
+							<div class="iconfont-warp-label mt10">{{ v }}</div>
+						</div>
+					</div>
+				</el-col>
+			</el-row>
+		</el-card>
+	</div>
+</template>
+
+<script lang="ts">
+import { toRefs, reactive, onMounted, defineComponent } from 'vue';
+import initIconfont from '/@/utils/getStyleSheets';
+
+export default defineComponent({
+	name: 'pagesAwesome',
+	setup() {
+		const state = reactive({
+			sheetsIconList: [],
+		});
+		// 初始化获取 css 样式,这里使用fontawesome的图标(记得加上前缀 `fa`),其它第三方请自行做判断
+		const initGetStyleSheets = () => {
+			initIconfont.awe().then((res: any) => (state.sheetsIconList = res));
+		};
+		// 页面加载时
+		onMounted(() => {
+			initGetStyleSheets();
+		});
+		return {
+			...toRefs(state),
+		};
+	},
+});
+</script>
+
+<style scoped lang="scss">
+.awesome-container {
+	.iconfont-row {
+		border-top: 1px solid var(--next-border-color-light);
+		border-left: 1px solid var(--next-border-color-light);
+		.iconfont-warp {
+			text-align: center;
+			border-right: 1px solid var(--next-border-color-light);
+			border-bottom: 1px solid var(--next-border-color-light);
+			height: 120px;
+			overflow: hidden;
+			display: flex;
+			transition: all 0.3s ease;
+			&:hover {
+				box-shadow: 0 2px 12px var(--next-color-dark-hover);
+				cursor: pointer;
+				transition: all 0.3s ease;
+				.iconfont-warp-value {
+					i {
+						color: var(--el-color-primary);
+						transition: all 0.3s ease;
+					}
+				}
+				.iconfont-warp-label {
+					color: var(--el-color-primary);
+					transition: all 0.3s ease;
+				}
+			}
+			.iconfont-warp-value {
+				i {
+					color: #606266;
+					font-size: 32px;
+					transition: all 0.3s ease;
+				}
+			}
+			.iconfont-warp-label {
+				color: #99a9bf;
+				transition: all 0.3s ease;
+			}
+		}
+	}
+}
+</style>
diff --git a/src/views/pages/drag/index.vue b/src/views/pages/drag/index.vue
new file mode 100644
index 0000000..1b79498
--- /dev/null
+++ b/src/views/pages/drag/index.vue
@@ -0,0 +1,66 @@
+<template>
+	<div class="drag-container">
+		<el-card shadow="hover" header="拖动指令效果(v-drag)作用于 Dialog 对话框">
+			<el-button type="primary" @click="dialogVisible = true" size="default">
+				<el-icon>
+					<ele-Pointer />
+				</el-icon>
+				点击打开 Dialog
+			</el-button>
+		</el-card>
+
+		<el-card shadow="hover" header="自定义div" class="mt15">
+			<div class="drag-dom">
+				<div class="drag-header">
+					<el-button type="success" size="default" v-drag="['.drag-container .drag-dom', '.drag-container .drag-header']">
+						<el-icon>
+							<ele-Pointer />
+						</el-icon>
+						按住进行拖动测试
+					</el-button>
+				</div>
+			</div>
+		</el-card>
+
+		<el-dialog v-model="dialogVisible" width="769px">
+			<template #header>
+				<div v-drag="['.drag-container .el-dialog', '.drag-container .el-dialog__header']">拖动指令效果(v-drag)</div>
+			</template>
+			<p>鼠标放标题头进行 Dialog 对话框拖动</p>
+			<template #footer>
+				<span class="dialog-footer">
+					<el-button @click="dialogVisible = false" size="default">取 消</el-button>
+					<el-button type="primary" @click="dialogVisible = false" size="default">确 定</el-button>
+				</span>
+			</template>
+		</el-dialog>
+	</div>
+</template>
+
+<script lang="ts">
+import { toRefs, reactive, defineComponent } from 'vue';
+
+export default defineComponent({
+	name: 'pagesDrag',
+	setup() {
+		const state = reactive({
+			dialogVisible: false,
+		});
+		return {
+			...toRefs(state),
+		};
+	},
+});
+</script>
+
+<style scoped lang="scss">
+.drag-container {
+	.drag-dom {
+		position: relative;
+		display: inline-block;
+		.drag-header {
+			display: inline-block;
+		}
+	}
+}
+</style>
diff --git a/src/views/pages/dynamicForm/index.vue b/src/views/pages/dynamicForm/index.vue
new file mode 100644
index 0000000..d6a7a68
--- /dev/null
+++ b/src/views/pages/dynamicForm/index.vue
@@ -0,0 +1,204 @@
+<template>
+	<div class="dynamic-form-container">
+		<el-card shadow="hover" header="动态复杂表单">
+			<el-form :model="form" ref="formRulesOneRef" size="default" label-width="100px" class="mt35">
+				<el-row :gutter="35">
+					<el-col
+						:xs="val.xs"
+						:sm="val.sm"
+						:md="val.md"
+						:lg="val.md"
+						:xl="val.xl"
+						class="mb20"
+						v-show="val.isShow"
+						v-for="(val, key) in formData"
+						:key="key"
+					>
+						<template v-if="val.type !== ''">
+							<el-form-item
+								:label="val.label"
+								:prop="val.prop"
+								:rules="[{ required: val.required, message: `${val.label}不能为空`, trigger: val.type === 'input' ? 'blur' : 'change' }]"
+								v-if="val.type !== ''"
+							>
+								<el-input
+									v-model="form[val.prop]"
+									:placeholder="val.placeholder"
+									clearable
+									v-if="val.type === 'input'"
+									style="width: 100%"
+									:disabled="val.disabled"
+								></el-input>
+								<el-date-picker
+									v-model="form[val.prop]"
+									type="date"
+									:placeholder="val.placeholder"
+									v-else-if="val.type === 'date'"
+									style="width: 100%"
+									:disabled="val.disabled"
+								>
+								</el-date-picker>
+								<el-select
+									v-model="form[val.prop]"
+									:placeholder="val.placeholder"
+									v-else-if="val.type === 'select'"
+									style="width: 100%"
+									:disabled="val.disabled"
+								>
+									<el-option v-for="item in val.options" :key="item.value" :label="item.label" :value="item.value"> </el-option>
+								</el-select>
+								<el-input
+									type="textarea"
+									v-model="form[val.prop]"
+									:placeholder="val.placeholder"
+									clearable
+									v-if="val.type === 'textarea'"
+									style="width: 100%"
+									:disabled="val.disabled"
+								></el-input>
+							</el-form-item>
+						</template>
+						<template v-else>
+							<el-row :gutter="35" v-for="(v, k) in form.list" :key="k">
+								<el-col :xs="24" :sm="12" :md="8" :lg="8" :xl="6" class="mb20">
+									<el-form-item label="年度" :prop="`list[${k}].year`" :rules="[{ required: true, message: `年度不能为空`, trigger: 'blur' }]">
+										<template #label>
+											<el-button type="primary" circle size="small" @click="onAddRow" v-if="k === 0">
+												<el-icon>
+													<ele-Plus />
+												</el-icon>
+											</el-button>
+											<el-button type="danger" circle size="small" @click="onDelRow(k)" v-else>
+												<el-icon>
+													<ele-Delete />
+												</el-icon>
+											</el-button>
+											<span class="ml10">年度</span>
+										</template>
+										<el-input v-model="form.list[k].year" style="width: 100%" placeholder="请输入"> </el-input>
+									</el-form-item>
+								</el-col>
+								<el-col :xs="24" :sm="12" :md="8" :lg="8" :xl="6" class="mb20">
+									<el-form-item label="月度" :prop="`list[${k}].month`" :rules="[{ required: true, message: `月度不能为空`, trigger: 'blur' }]">
+										<el-input v-model="form.list[k].month" style="width: 100%" placeholder="请输入"> </el-input>
+									</el-form-item>
+								</el-col>
+								<el-col :xs="24" :sm="12" :md="8" :lg="8" :xl="6" class="mb20">
+									<el-form-item label="日度" :prop="`list[${k}].day`" :rules="[{ required: true, message: `日度不能为空`, trigger: 'blur' }]">
+										<el-input v-model="form.list[k].day" style="width: 100%" placeholder="请输入"> </el-input>
+									</el-form-item>
+								</el-col>
+							</el-row>
+						</template>
+					</el-col>
+				</el-row>
+			</el-form>
+		</el-card>
+		<el-row class="flex mt15">
+			<div class="flex-margin">
+				<el-button size="default" @click="onResetForm">
+					<el-icon>
+						<ele-RefreshRight />
+					</el-icon>
+					重置表单
+				</el-button>
+				<el-button size="default" type="primary" @click="onSubmitForm">
+					<SvgIcon name="iconfont icon-shuxing" />
+					验证表单
+				</el-button>
+			</div>
+		</el-row>
+	</div>
+</template>
+
+<script lang="ts">
+import { toRefs, reactive, onMounted, getCurrentInstance, defineComponent } from 'vue';
+import { formData } from './mock';
+
+// 定义接口来定义对象的类型
+interface FormDataOptions {
+	label: string;
+	value: string;
+}
+interface FormDataState {
+	label: string;
+	prop: string;
+	placeholder: string;
+	clearable: boolean;
+	disabled: boolean;
+	required: boolean;
+	type: string;
+	i18n: boolean;
+	i18nText: string;
+	isShow: boolean;
+	xs: number;
+	sm: number;
+	md: number;
+	lg: number;
+	xl: number;
+	options?: FormDataOptions[];
+}
+interface DynamicFormState {
+	formData: FormDataState[];
+	form: any;
+}
+
+export default defineComponent({
+	name: 'pagesDynamicForm',
+	setup() {
+		const { proxy } = <any>getCurrentInstance();
+		const state = reactive<DynamicFormState>({
+			formData,
+			form: {
+				name: '',
+				email: '',
+				autograph: '',
+				occupation: '',
+				list: [
+					{
+						year: '',
+						month: '',
+						day: '',
+					},
+				],
+				remarks: '',
+			},
+		});
+		// 新增行
+		const onAddRow = () => {
+			state.form.list.push({
+				year: '',
+				month: '',
+				day: '',
+			});
+		};
+		// 删除行
+		const onDelRow = (k: number) => {
+			state.form.list.splice(k, 1);
+		};
+		// 表单验证
+		const onSubmitForm = () => {
+			proxy.$refs.formRulesOneRef.validate((valid: boolean) => {
+				if (valid) {
+					proxy.$message.success('验证成功');
+				} else {
+					return false;
+				}
+			});
+		};
+		// 重置表单
+		const onResetForm = () => {
+			proxy.$refs.formRulesOneRef.resetFields();
+		};
+		// 页面加载时
+		onMounted(() => {});
+		return {
+			onAddRow,
+			onDelRow,
+			onSubmitForm,
+			onResetForm,
+			...toRefs(state),
+		};
+	},
+});
+</script>
diff --git a/src/views/pages/dynamicForm/mock.ts b/src/views/pages/dynamicForm/mock.ts
new file mode 100644
index 0000000..2293fe9
--- /dev/null
+++ b/src/views/pages/dynamicForm/mock.ts
@@ -0,0 +1,119 @@
+// 表单数据选项(自行扩展)
+export const formData = [
+	{
+		label: '姓名',
+		prop: 'name',
+		placeholder: '请输入姓名',
+		clearable: true,
+		disabled: false,
+		required: true,
+		type: 'input',
+		i18n: false,
+		i18nText: '',
+		isShow: true,
+		xs: 24,
+		sm: 12,
+		md: 8,
+		lg: 6,
+		xl: 4,
+	},
+	{
+		label: '邮箱',
+		prop: 'email',
+		placeholder: '请输入用户邮箱',
+		clearable: true,
+		disabled: false,
+		required: true,
+		type: 'input',
+		i18n: false,
+		i18nText: '',
+		isShow: true,
+		xs: 24,
+		sm: 12,
+		md: 8,
+		lg: 6,
+		xl: 4,
+	},
+	{
+		label: '登陆时间',
+		prop: 'autograph',
+		placeholder: '选择时间',
+		clearable: true,
+		disabled: false,
+		required: true,
+		type: 'date',
+		i18n: false,
+		i18nText: '',
+		isShow: true,
+		xs: 24,
+		sm: 12,
+		md: 8,
+		lg: 6,
+		xl: 4,
+	},
+	{
+		label: '职务',
+		prop: 'occupation',
+		placeholder: '请选择职务',
+		clearable: true,
+		disabled: false,
+		required: true,
+		type: 'select',
+		i18n: false,
+		i18nText: '',
+		options: [
+			{
+				label: '计算机 / 互联网 / 通信',
+				value: '1',
+			},
+			{
+				label: '生产 / 工艺 / 制造',
+				value: '2',
+			},
+			{
+				label: '医疗 / 护理 / 制药',
+				value: '3',
+			},
+		],
+		isShow: true,
+		xs: 24,
+		sm: 12,
+		md: 8,
+		lg: 6,
+		xl: 4,
+	},
+	{
+		label: '',
+		prop: '',
+		placeholder: '',
+		clearable: true,
+		disabled: false,
+		required: true,
+		type: '',
+		i18n: false,
+		i18nText: '',
+		isShow: true,
+		xs: 24,
+		sm: 24,
+		md: 24,
+		lg: 24,
+		xl: 24,
+	},
+	{
+		label: '备注',
+		prop: 'remarks',
+		placeholder: '请输入',
+		clearable: true,
+		disabled: false,
+		required: true,
+		type: 'textarea',
+		i18n: false,
+		i18nText: '',
+		isShow: true,
+		xs: 24,
+		sm: 24,
+		md: 24,
+		lg: 24,
+		xl: 24,
+	},
+];
diff --git a/src/views/pages/element/index.vue b/src/views/pages/element/index.vue
new file mode 100644
index 0000000..0bb2454
--- /dev/null
+++ b/src/views/pages/element/index.vue
@@ -0,0 +1,89 @@
+<template>
+	<div class="element-container">
+		<el-card shadow="hover" :header="`element plus 字体图标(自动载入,增加了 ele- 前缀,使用时:ele-Aim):${sheetsIconList.length}个`">
+			<el-row class="iconfont-row">
+				<el-col :xs="12" :sm="8" :md="6" :lg="4" :xl="2" v-for="(v, k) in sheetsIconList" :key="k">
+					<div class="iconfont-warp">
+						<div class="flex-margin">
+							<div class="iconfont-warp-value">
+								<SvgIcon :name="v" :size="30" />
+							</div>
+							<div class="iconfont-warp-label mt10">{{ v }}</div>
+						</div>
+					</div>
+				</el-col>
+			</el-row>
+		</el-card>
+	</div>
+</template>
+
+<script lang="ts">
+import { toRefs, reactive, onMounted, defineComponent } from 'vue';
+import initIconfont from '/@/utils/getStyleSheets';
+
+export default defineComponent({
+	name: 'pagesElement',
+	setup() {
+		const state = reactive({
+			sheetsIconList: [],
+		});
+		// 初始化获取 css 样式,获取 element plus 自带 svg 图标,增加了 ele- 前缀,使用时:ele-Aim
+		const initGetStyleSheets = () => {
+			initIconfont.ele().then((res: any) => {
+				state.sheetsIconList = res;
+			});
+		};
+		// 页面加载时
+		onMounted(() => {
+			initGetStyleSheets();
+		});
+		return {
+			...toRefs(state),
+		};
+	},
+});
+</script>
+
+<style scoped lang="scss">
+.element-container {
+	.iconfont-row {
+		border-top: 1px solid var(--next-border-color-light);
+		border-left: 1px solid var(--next-border-color-light);
+		.iconfont-warp {
+			text-align: center;
+			border-right: 1px solid var(--next-border-color-light);
+			border-bottom: 1px solid var(--next-border-color-light);
+			height: 120px;
+			overflow: hidden;
+			display: flex;
+			transition: all 0.3s ease;
+			&:hover {
+				box-shadow: 0 2px 12px var(--next-color-dark-hover);
+				cursor: pointer;
+				transition: all 0.3s ease;
+				.iconfont-warp-value {
+					i {
+						color: var(--el-color-primary);
+						transition: all 0.3s ease;
+					}
+				}
+				.iconfont-warp-label {
+					color: var(--el-color-primary);
+					transition: all 0.3s ease;
+				}
+			}
+			.iconfont-warp-value {
+				i {
+					color: #606266;
+					font-size: 32px;
+					transition: all 0.3s ease;
+				}
+			}
+			.iconfont-warp-label {
+				color: #99a9bf;
+				transition: all 0.3s ease;
+			}
+		}
+	}
+}
+</style>
diff --git a/src/views/pages/filtering/details.vue b/src/views/pages/filtering/details.vue
new file mode 100644
index 0000000..a2e5417
--- /dev/null
+++ b/src/views/pages/filtering/details.vue
@@ -0,0 +1,39 @@
+<template>
+	<div :style="{ height: `calc(100vh - ${initTagViewHeight}` }">
+		<div class="layout-view-bg-white">
+			<div class="w100 h100 flex">
+				<div class="flex-margin color-primary">filtering-details 测试界面</div>
+			</div>
+		</div>
+	</div>
+</template>
+
+<script lang="ts">
+import { computed, defineComponent } from 'vue';
+import { storeToRefs } from 'pinia';
+import { useThemeConfig } from '/@/stores/themeConfig';
+import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
+
+export default defineComponent({
+	name: 'pagesFilteringDetails',
+	setup() {
+		const storesTagsViewRoutes = useTagsViewRoutes();
+		const storesThemeConfig = useThemeConfig();
+		const { themeConfig } = storeToRefs(storesThemeConfig);
+		const { isTagsViewCurrenFull } = storeToRefs(storesTagsViewRoutes);
+		// 设置主内容的高度
+		const initTagViewHeight = computed(() => {
+			let { isTagsview } = themeConfig.value;
+			if (isTagsViewCurrenFull.value) {
+				return `30px`;
+			} else {
+				if (isTagsview) return `114px`;
+				else return `80px`;
+			}
+		});
+		return {
+			initTagViewHeight,
+		};
+	},
+});
+</script>
diff --git a/src/views/pages/filtering/details1.vue b/src/views/pages/filtering/details1.vue
new file mode 100644
index 0000000..2fde1b4
--- /dev/null
+++ b/src/views/pages/filtering/details1.vue
@@ -0,0 +1,39 @@
+<template>
+	<div :style="{ height: `calc(100vh - ${initTagViewHeight}` }">
+		<div class="layout-view-bg-white">
+			<div class="w100 h100 flex">
+				<div class="flex-margin color-primary">测试界面</div>
+			</div>
+		</div>
+	</div>
+</template>
+
+<script lang="ts">
+import { computed, defineComponent } from 'vue';
+import { storeToRefs } from 'pinia';
+import { useThemeConfig } from '/@/stores/themeConfig';
+import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
+
+export default defineComponent({
+	name: 'pagesFilteringDetails1',
+	setup() {
+		const storesTagsViewRoutes = useTagsViewRoutes();
+		const storesThemeConfig = useThemeConfig();
+		const { themeConfig } = storeToRefs(storesThemeConfig);
+		const { isTagsViewCurrenFull } = storeToRefs(storesTagsViewRoutes);
+		// 设置主内容的高度
+		const initTagViewHeight = computed(() => {
+			let { isTagsview } = themeConfig.value;
+			if (isTagsViewCurrenFull.value) {
+				return `30px`;
+			} else {
+				if (isTagsview) return `114px`;
+				else return `80px`;
+			}
+		});
+		return {
+			initTagViewHeight,
+		};
+	},
+});
+</script>
diff --git a/src/views/pages/filtering/index.vue b/src/views/pages/filtering/index.vue
new file mode 100644
index 0000000..7cb7ff5
--- /dev/null
+++ b/src/views/pages/filtering/index.vue
@@ -0,0 +1,355 @@
+<template>
+	<div class="filtering">
+		<el-card
+			shadow="hover"
+			class="filtering-list br-top-no"
+			v-loading="tableData.loading"
+			element-loading-text="加载中..."
+			element-loading-background="rgba(255, 255, 255, 0.1)"
+			:class="{ 'min-h-360': tableData.data.length <= 0 }"
+		>
+			<div
+				v-for="(val, key) in filtering"
+				:key="key"
+				:ref="
+					(el) => {
+						if (el) dlRefs[key] = el;
+					}
+				"
+				class="filtering-list-flex"
+			>
+				<div class="filtering-list-title">{{ val.title }}</div>
+				<div class="filtering-list-item" :style="{ height: val.isMore ? 'auto' : '50px' }">
+					<span class="span" :class="v.active ? 'dd-active' : ''" v-for="(v, k) in val.children" :key="k" @click="onSelItem(val, v)">{{
+						v.label
+					}}</span>
+					<div class="dd-more" v-if="val.isShowMore" @click="val.isMore = !val.isMore">
+						<span>{{ val.isMore ? '收起' : '展开' }}</span>
+						<i :class="val.isMore ? 'el-icon-arrow-down' : 'el-icon-arrow-right'"></i>
+					</div>
+				</div>
+			</div>
+			<div class="flex-warp mt15 mb15" v-if="tableData.data.length > 0">
+				<el-row :gutter="15">
+					<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb15" v-for="(v, k) in tableData.data" :key="k" @click="onTableItemClick(v)">
+						<div class="flex-warp-item">
+							<div class="flex-warp-item-box">
+								<div class="item-img">
+									<img :src="v.img" />
+								</div>
+								<div class="item-txt">
+									<div class="item-txt-title">{{ v.title }}</div>
+									<div class="item-txt-other">
+										<div style="width: 100%">
+											<div class="item-txt-msg mb10">
+												<span>评价 {{ v.evaluate }}</span>
+												<span class="ml10">收藏 {{ v.collection }}</span>
+											</div>
+											<div class="item-txt-msg item-txt-price">
+												<span class="font-price">
+													<span>¥</span>
+													<span class="font">{{ v.price }}</span>
+												</span>
+												<span>月销{{ v.monSales }}笔</span>
+											</div>
+										</div>
+									</div>
+								</div>
+							</div>
+						</div>
+					</el-col>
+				</el-row>
+			</div>
+			<div v-else class="filtering-no-data">
+				<div class="no-data-box">
+					<i class="el-icon-search"></i>
+					<div class="no-txt">暂无数据</div>
+				</div>
+			</div>
+			<template v-if="tableData.data.length > 0">
+				<el-pagination
+					style="text-align: right"
+					background
+					@size-change="onHandleSizeChange"
+					@current-change="onHandleCurrentChange"
+					:page-sizes="[10, 20, 30]"
+					:current-page="tableData.param.pageNum"
+					:page-size="tableData.param.pageSize"
+					layout="total, sizes, prev, pager, next, jumper"
+					:total="tableData.total"
+				>
+				</el-pagination>
+			</template>
+		</el-card>
+	</div>
+</template>
+
+<script lang="ts">
+import { ref, toRefs, reactive, onMounted, nextTick, defineComponent } from 'vue';
+import { useRouter } from 'vue-router';
+import { filtering, filterList } from './mock';
+
+export default defineComponent({
+	name: 'pagesFiltering',
+	setup() {
+		const dlRefs: any = ref([]);
+		const router = useRouter();
+		const state = reactive({
+			filtering,
+			tableData: {
+				data: filterList,
+				total: 99,
+				loading: false,
+				param: {
+					pageNum: 1,
+					pageSize: 10,
+				},
+			},
+		});
+		// 页面加载时
+		onMounted(() => {
+			initBtnToggle();
+			window.onresize = () => {
+				initBtnToggle();
+			};
+		});
+		// 初始化 `收起、展开` 按钮
+		const initBtnToggle = () => {
+			nextTick(() => {
+				const els = dlRefs.value;
+				els.map((v: any, k: number) => {
+					v.scrollHeight < v.lastChild.scrollHeight ? (state.filtering[k].isShowMore = true) : (state.filtering[k].isShowMore = false);
+				});
+			});
+		};
+		// 过滤当前选中的数据
+		const onSelItem = (val: any, v: any) => {
+			val.children.map((v: any) => (v.active = false));
+			v.active = true;
+			let arr = [];
+			state.filtering.map((item: any) => {
+				item.children.map((chil: any) => {
+					if (chil.active) {
+						arr.push({
+							...item,
+							children: [{ ...chil }],
+						});
+					}
+				});
+			});
+			state.tableData.loading = true;
+			setTimeout(() => {
+				state.tableData.loading = false;
+			}, 500);
+		};
+		// 当前列表项点击
+		const onTableItemClick = (v: any) => {
+			if (v.id === 1) {
+				router.push({
+					path: '/pages/filtering/details',
+					query: { id: v.id },
+				});
+			} else {
+				router.push({
+					path: '/pages/filtering/details1',
+					query: { id: v.id },
+				});
+			}
+		};
+		// 分页点击
+		const onHandleSizeChange = (val: number) => {
+			state.tableData.param.pageSize = val;
+		};
+		// 分页点击
+		const onHandleCurrentChange = (val: number) => {
+			state.tableData.param.pageNum = val;
+		};
+		return {
+			dlRefs,
+			onSelItem,
+			onTableItemClick,
+			onHandleSizeChange,
+			onHandleCurrentChange,
+			...toRefs(state),
+		};
+	},
+});
+</script>
+
+<style scoped lang="scss">
+.filtering {
+	.filtering-list {
+		overflow: hidden;
+		border-bottom: none !important;
+		.filtering-list-flex {
+			&:last-of-type {
+				.filtering-list-item {
+					border-bottom: none !important;
+				}
+			}
+			.filtering-list-title {
+				float: left;
+				width: 64px;
+				font-weight: 700;
+				position: relative;
+				color: #909399;
+				margin: 15px 0;
+				&:after {
+					content: '';
+					position: absolute;
+					border: 1px solid #909399;
+					border-width: 0 1px 1px 0;
+					width: 4px;
+					height: 4px;
+					transform: rotate(-45deg) translateY(-50%);
+					right: 10px;
+					top: 50%;
+				}
+			}
+			.filtering-list-item {
+				border-bottom: 1px dotted var(--next-border-color-light);
+				margin-left: 64px;
+				overflow: hidden;
+				position: relative;
+				.span {
+					color: #8d8d91;
+					font-size: 14px;
+					float: left;
+					padding: 0 15px;
+					margin: 15px 0;
+					&:hover {
+						color: var(--el-color-primary);
+						cursor: pointer;
+					}
+				}
+				.dd-active {
+					color: var(--el-color-primary);
+				}
+				.dd-more {
+					font-size: 12px;
+					position: absolute;
+					right: 0;
+					top: 16px;
+					color: #a5a5a5;
+					&:hover {
+						cursor: pointer;
+						color: #8d8d91;
+					}
+				}
+			}
+		}
+	}
+	.br-top-no {
+		border-top: none;
+		.flex-warp {
+			display: flex;
+			flex-wrap: wrap;
+			align-content: flex-start;
+			margin: 0 -5px;
+			.flex-warp-item {
+				padding: 5px;
+				width: 100%;
+				height: 360px;
+				.flex-warp-item-box {
+					border: 1px solid var(--next-border-color-light);
+					width: 100%;
+					height: 100%;
+					border-radius: 2px;
+					display: flex;
+					flex-direction: column;
+					transition: all 0.3s ease;
+					&:hover {
+						cursor: pointer;
+						border: 1px solid var(--el-color-primary);
+						transition: all 0.3s ease;
+						box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.03);
+						.item-txt-title {
+							color: var(--el-color-primary) !important;
+							transition: all 0.3s ease;
+						}
+						.item-img {
+							img {
+								transition: all 0.3s ease;
+								transform: translateZ(0) scale(1.05);
+							}
+						}
+					}
+					.item-img {
+						width: 100%;
+						height: 215px;
+						overflow: hidden;
+						img {
+							transition: all 0.3s ease;
+							width: 100%;
+							height: 100%;
+						}
+					}
+					.item-txt {
+						flex: 1;
+						padding: 15px;
+						display: flex;
+						flex-direction: column;
+						overflow: hidden;
+						.item-txt-title {
+							text-overflow: ellipsis;
+							overflow: hidden;
+							-webkit-line-clamp: 2;
+							-webkit-box-orient: vertical;
+							display: -webkit-box;
+							color: #666666;
+							transition: all 0.3s ease;
+							&:hover {
+								color: var(--el-color-primary);
+								text-decoration: underline;
+								transition: all 0.3s ease;
+							}
+						}
+						.item-txt-other {
+							flex: 1;
+							align-items: flex-end;
+							display: flex;
+							.item-txt-msg {
+								font-size: 12px;
+								color: #8d8d91;
+							}
+							.item-txt-price {
+								display: flex;
+								justify-content: space-between;
+								align-items: center;
+								.font-price {
+									color: #ff5000;
+									.font {
+										font-size: 22px;
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		}
+		::v-deep(.el-card__body) {
+			height: 100%;
+			.filtering-no-data {
+				display: flex;
+				height: 100%;
+				.no-data-box {
+					color: #cccccc;
+					margin: auto;
+					i {
+						font-size: 70px;
+					}
+					.no-txt {
+						font-size: 14px;
+						text-align: center;
+						margin-top: 15px;
+					}
+				}
+			}
+		}
+	}
+	.min-h-360 {
+		height: 360px;
+	}
+}
+</style>
diff --git a/src/views/pages/filtering/mock.ts b/src/views/pages/filtering/mock.ts
new file mode 100644
index 0000000..22e0f38
--- /dev/null
+++ b/src/views/pages/filtering/mock.ts
@@ -0,0 +1,201 @@
+// 导航数据
+export const filtering = [
+	{
+		title: '权限',
+		isMore: false,
+		isShowMore: false,
+		id: 0,
+		children: [
+			{
+				id: '01',
+				label: '全部',
+				active: true,
+			},
+			{
+				id: '02',
+				label: '普通用户',
+				active: false,
+			},
+			{
+				id: '03',
+				label: '管理员',
+				active: false,
+			},
+		],
+	},
+	{
+		title: '布局',
+		isMore: false,
+		isShowMore: false,
+		id: 1,
+		children: [
+			{
+				id: 11,
+				label: '全部',
+				active: true,
+			},
+			{
+				id: 12,
+				label: '默认',
+				active: false,
+			},
+			{
+				id: 13,
+				label: '经典',
+				active: false,
+			},
+			{
+				id: 14,
+				label: '横向',
+				active: false,
+			},
+			{
+				id: 15,
+				label: '分栏',
+				active: false,
+			},
+		],
+	},
+	{
+		title: '配置',
+		isMore: false,
+		isShowMore: false,
+		id: 2,
+		children: [
+			{
+				id: 21,
+				label: '全部',
+				active: true,
+			},
+			{
+				id: 22,
+				label: '开启 Breadcrumb',
+				active: false,
+			},
+			{
+				id: 23,
+				label: '开启 Tags-View',
+				active: false,
+			},
+			{
+				id: 24,
+				label: '固定 Header',
+				active: false,
+			},
+			{
+				id: 25,
+				label: '侧边栏 Logo',
+				active: false,
+			},
+			{
+				id: 26,
+				label: '开启折叠 NavMenu',
+				active: false,
+			},
+			{
+				id: 27,
+				label: '开启一个 NavMenu 展开',
+				active: false,
+			},
+			{
+				id: 28,
+				label: '登录用户头像',
+				active: false,
+			},
+		],
+	},
+];
+
+// 列表数据
+export const filterList = [
+	{
+		img: 'http://news.sznews.com/pic/2020-08/14/9d9c9a60-f0af-41aa-b617-683b07c87642.jpg',
+		title: '嘉陵江2020年第1号洪水”在嘉陵江支流涪江形成',
+		evaluate: (Math.random() * 10).toFixed(2),
+		collection: (Math.random() * 100).toFixed(2),
+		price: (Math.random() * 10).toFixed(2),
+		monSales: (Math.random() * 20).toFixed(2),
+		id: 1,
+	},
+	{
+		img: 'http://www.sznews.com/news/pic/2020-08/13/0ea47d3c-feb9-4bd7-8597-a8a373aa6340c6ec12c7-3b33-4528-91a6-85ec8ca1df67_watermark.png',
+		title: '让《民法典》走近群众 盐田街道开展人民调解宣传活动',
+		evaluate: (Math.random() * 10).toFixed(2),
+		collection: (Math.random() * 100).toFixed(2),
+		price: (Math.random() * 10).toFixed(2),
+		monSales: (Math.random() * 20).toFixed(2),
+		id: 2,
+	},
+	{
+		img: 'http://www.sznews.com/photo/pic/2020-08/12/a08d6eb0-1d53-4f76-a313-ad3e5d701f98.jpg',
+		title: '记者手记:可可西里,“挪”向“藏羚羊大产房”的14个半小时',
+		evaluate: (Math.random() * 10).toFixed(2),
+		collection: (Math.random() * 100).toFixed(2),
+		price: (Math.random() * 10).toFixed(2),
+		monSales: (Math.random() * 20).toFixed(2),
+		id: 3,
+	},
+	{
+		img: 'http://www.sznews.com/photo/pic/2020-08/11/43cc0e14-9bca-45b9-9a8b-342e09d6a4c7.jpg',
+		title: '以优异成绩庆祝深圳经济特区建立40周年',
+		evaluate: (Math.random() * 10).toFixed(2),
+		collection: (Math.random() * 100).toFixed(2),
+		price: (Math.random() * 10).toFixed(2),
+		monSales: (Math.random() * 20).toFixed(2),
+		id: 4,
+	},
+	{
+		img: 'http://www.sznews.com/photo/pic/2020-08/11/a4dc322b-68ec-40e6-8906-3124142c3e49.jpg',
+		title: '草原上的“太阳姑娘”',
+		evaluate: (Math.random() * 10).toFixed(2),
+		collection: (Math.random() * 100).toFixed(2),
+		price: (Math.random() * 10).toFixed(2),
+		monSales: (Math.random() * 20).toFixed(2),
+		id: 5,
+	},
+	{
+		img: 'http://www.sznews.com/zhuanti/pic/2020-08/07/57f087b4-4812-46cc-adb9-ead73621284e.png',
+		title: '奇观天下|带你走进非洲野生动物观光第一目的地',
+		evaluate: (Math.random() * 10).toFixed(2),
+		collection: (Math.random() * 100).toFixed(2),
+		price: (Math.random() * 10).toFixed(2),
+		monSales: (Math.random() * 20).toFixed(2),
+		id: 6,
+	},
+	{
+		img: 'http://news.sznews.com/pic/2020-09/02/t2_(101X54X600X335)7cd39301-d9cf-45f1-91c3-9575b1e5ce0e.jpg.2',
+		title: '五角大楼发布“中国军力报告” 华春莹: 罔顾事实,充满偏见',
+		evaluate: (Math.random() * 10).toFixed(2),
+		collection: (Math.random() * 100).toFixed(2),
+		price: (Math.random() * 10).toFixed(2),
+		monSales: (Math.random() * 20).toFixed(2),
+		id: 7,
+	},
+	{
+		img: 'http://news.sznews.com/pic/2020-09/02/b8b41d9c-0508-4498-8d37-6e597493769f.jpg',
+		title: '最新地铁消息汇总:4号线北延、2号线三期、8号线一期等今年通车',
+		evaluate: (Math.random() * 10).toFixed(2),
+		collection: (Math.random() * 100).toFixed(2),
+		price: (Math.random() * 10).toFixed(2),
+		monSales: (Math.random() * 20).toFixed(2),
+		id: 8,
+	},
+	{
+		img: 'http://www.sznews.com/photo/pic/2020-08/10/1635374c-f4d6-475c-ac47-1334176f365d.png',
+		title: '9月1日深圳新增5例无症状感染者!钟南山这段话冲上热搜!',
+		evaluate: (Math.random() * 10).toFixed(2),
+		collection: (Math.random() * 100).toFixed(2),
+		price: (Math.random() * 10).toFixed(2),
+		monSales: (Math.random() * 20).toFixed(2),
+		id: 9,
+	},
+	{
+		img: 'http://www.sznews.com/news/pic/2020-08/13/646e5458-92b7-4636-9940-9b0799babfe1.png',
+		title: '全能“小福宝” 为文明社区建设添砖加瓦',
+		evaluate: (Math.random() * 10).toFixed(2),
+		collection: (Math.random() * 100).toFixed(2),
+		price: (Math.random() * 10).toFixed(2),
+		monSales: (Math.random() * 20).toFixed(2),
+		id: 10,
+	},
+];
diff --git a/src/views/pages/formAdapt/index.vue b/src/views/pages/formAdapt/index.vue
new file mode 100644
index 0000000..3bbfe04
--- /dev/null
+++ b/src/views/pages/formAdapt/index.vue
@@ -0,0 +1,114 @@
+<template>
+	<div class="form-adapt-container">
+		<el-card shadow="hover" header="表单自适应演示(改变窗口查看效果)">
+			<el-form :model="form" size="default" label-width="100px" 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="form.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="form.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="form.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="form.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="form.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="form.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="12" :md="8" :lg="6" :xl="4" class="mb20">
+						<el-form-item label="登录密码">
+							<el-input v-model="form.phone1" 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="form.phone2" 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="form.phone3" 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="form.phone4" 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="form.phone5" 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="form.phone6" placeholder="请输入所属部门" clearable></el-input>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+						<el-form-item>
+							<el-button type="primary">
+								<SvgIcon name="iconfont icon-biaodan" />
+								更新个人信息
+							</el-button>
+						</el-form-item>
+					</el-col>
+				</el-row>
+			</el-form>
+		</el-card>
+	</div>
+</template>
+
+<script lang="ts">
+import { toRefs, reactive, defineComponent } from 'vue';
+
+export default defineComponent({
+	name: 'pagesFormAdapt',
+	setup() {
+		const state = reactive({
+			form: {
+				name: '',
+				email: '',
+				autograph: '',
+				occupation: '',
+				phone: '',
+				sex: '',
+				phone1: '',
+				phone2: '',
+				phone3: '',
+				phone4: '',
+				phone5: '',
+				phone6: '',
+			},
+		});
+		return {
+			...toRefs(state),
+		};
+	},
+});
+</script>
diff --git a/src/views/pages/formI18n/index.vue b/src/views/pages/formI18n/index.vue
new file mode 100644
index 0000000..b9fe6b8
--- /dev/null
+++ b/src/views/pages/formI18n/index.vue
@@ -0,0 +1,59 @@
+<template>
+	<div class="form-i18n-container">
+		<el-card shadow="hover" header="表单国际化演示(不适用于动态项 form-item)">
+			<div style="text-align: center; margin-top: 15px">
+				<el-radio-group v-model="radio" size="default" @change="onRadioChange">
+					<el-radio-button label="zh-cn">中文简体</el-radio-button>
+					<el-radio-button label="en">英文</el-radio-button>
+					<el-radio-button label="zh-tw">中文繁体</el-radio-button>
+				</el-radio-group>
+			</div>
+			<el-form :model="form" size="default" label-width="100px" class="mt35 mb35">
+				<el-row :gutter="35">
+					<el-col :xs="24" :sm="12" :md="8" :lg="8" :xl="8" class="mb20">
+						<el-form-item :label="$t('message.formI18nLabel.name')">
+							<el-input v-model="form.name" :placeholder="$t('message.formI18nPlaceholder.name')" clearable></el-input>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="8" :lg="8" :xl="8" class="mb20">
+						<el-form-item :label="$t('message.formI18nLabel.email')">
+							<el-input v-model="form.email" :placeholder="$t('message.formI18nPlaceholder.email')" clearable></el-input>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="8" :lg="8" :xl="8" class="mb20">
+						<el-form-item :label="$t('message.formI18nLabel.autograph')">
+							<el-input v-model="form.autograph" :placeholder="$t('message.formI18nPlaceholder.autograph')" clearable></el-input>
+						</el-form-item>
+					</el-col>
+				</el-row>
+			</el-form>
+		</el-card>
+	</div>
+</template>
+
+<script lang="ts">
+import { toRefs, reactive, defineComponent, getCurrentInstance } from 'vue';
+
+export default defineComponent({
+	name: 'pagesFormI18n',
+	setup() {
+		const { proxy } = <any>getCurrentInstance();
+		const state = reactive({
+			radio: 'zh-cn',
+			form: {
+				name: '',
+				email: '',
+				autograph: '',
+			},
+		});
+		// 单选框改变时
+		const onRadioChange = () => {
+			proxy.$i18n.locale = state.radio;
+		};
+		return {
+			onRadioChange,
+			...toRefs(state),
+		};
+	},
+});
+</script>
diff --git a/src/views/pages/formRules/component/formRulesOne.vue b/src/views/pages/formRules/component/formRulesOne.vue
new file mode 100644
index 0000000..ff9e568
--- /dev/null
+++ b/src/views/pages/formRules/component/formRulesOne.vue
@@ -0,0 +1,68 @@
+<template>
+	<div class="form-rules-one-container">
+		<el-form :model="form" :rules="rules" ref="formRulesOneRef" size="default" label-width="100px" class="mt35">
+			<el-row :gutter="35">
+				<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
+					<el-form-item label="姓名" prop="name">
+						<el-input v-model="form.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="邮箱" prop="email">
+						<el-input v-model="form.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="登陆账户名" prop="autograph">
+						<el-input v-model="form.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="职务" prop="occupation">
+						<el-select v-model="form.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-row>
+		</el-form>
+	</div>
+</template>
+
+<script lang="ts">
+import { toRefs, reactive, onMounted, defineComponent } from 'vue';
+
+export default defineComponent({
+	name: 'pagesFormRulesOne',
+	props: {
+		data: {
+			type: Object,
+			default: () => {},
+		},
+	},
+	setup(props) {
+		const state = reactive({
+			form: { name: '', email: '', autograph: '', occupation: '' },
+			rules: {
+				name: { required: true, message: '请输入姓名', trigger: 'blur' },
+				email: { required: true, message: '请输入用户邮箱', trigger: 'blur' },
+				autograph: { required: true, message: '请输入登陆账户名', trigger: 'blur' },
+				occupation: { required: true, message: '请选择职务', trigger: 'change' },
+			},
+		});
+		// 赋值回显
+		const initForm = () => {
+			state.form = <any>props.data;
+		};
+		// 页面加载时
+		onMounted(() => {
+			initForm();
+		});
+		return {
+			...toRefs(state),
+		};
+	},
+});
+</script>
diff --git a/src/views/pages/formRules/component/formRulesThree.vue b/src/views/pages/formRules/component/formRulesThree.vue
new file mode 100644
index 0000000..145aed4
--- /dev/null
+++ b/src/views/pages/formRules/component/formRulesThree.vue
@@ -0,0 +1,50 @@
+<template>
+	<div class="form-rules-three-container">
+		<el-form :model="form" :rules="rules" ref="formRulesThreeRef" size="default" label-width="100px" class="mt35">
+			<el-row :gutter="35">
+				<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
+					<el-form-item label="创建用户" prop="createUser">
+						<el-input v-model="form.createUser" 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="修改用户" prop="editUser">
+						<el-input v-model="form.editUser" 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="所属用户" prop="user">
+						<el-input v-model="form.user" 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="所属部门" prop="department">
+						<el-input v-model="form.department" placeholder="请输入所属部门" clearable></el-input>
+					</el-form-item>
+				</el-col>
+			</el-row>
+		</el-form>
+	</div>
+</template>
+
+<script lang="ts">
+import { toRefs, reactive, defineComponent } from 'vue';
+
+export default defineComponent({
+	name: 'pagesFormRulesThree',
+	setup() {
+		const state = reactive({
+			form: { createUser: '', editUser: '', user: '', department: '' },
+			rules: {
+				createUser: { required: true, message: '请输入创建用户', trigger: 'blur' },
+				editUser: { required: true, message: '请输入修改用户', trigger: 'blur' },
+				user: { required: true, message: '请输入所属用户', trigger: 'blur' },
+				department: { required: true, message: '请输入所属部门', trigger: 'blur' },
+			},
+		});
+		return {
+			...toRefs(state),
+		};
+	},
+});
+</script>
diff --git a/src/views/pages/formRules/component/formRulesTwo.vue b/src/views/pages/formRules/component/formRulesTwo.vue
new file mode 100644
index 0000000..fcb0604
--- /dev/null
+++ b/src/views/pages/formRules/component/formRulesTwo.vue
@@ -0,0 +1,52 @@
+<template>
+	<div class="form-rules-two-container">
+		<el-form :model="form" :rules="rules" ref="formRulesTwoRef" size="default" label-width="100px" class="mt35">
+			<el-row :gutter="35">
+				<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
+					<el-form-item label="手机" prop="phone">
+						<el-input v-model="form.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="form.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="12" :md="8" :lg="6" :xl="4" class="mb20">
+					<el-form-item label="登录密码" prop="password">
+						<el-input v-model="form.password" 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="权限角色" prop="auth">
+						<el-input v-model="form.auth" placeholder="请输入权限角色" clearable></el-input>
+					</el-form-item>
+				</el-col>
+			</el-row>
+		</el-form>
+	</div>
+</template>
+
+<script lang="ts">
+import { toRefs, reactive, defineComponent } from 'vue';
+
+export default defineComponent({
+	name: 'pagesFormRulesTwo',
+	setup() {
+		const state = reactive({
+			form: { phone: '', sex: '', password: '', auth: '' },
+			rules: {
+				phone: { required: true, message: '请输入手机', trigger: 'blur' },
+				password: { required: true, message: '请输入登录密码', trigger: 'blur' },
+				auth: { required: true, message: '请输入权限角色', trigger: 'blur' },
+			},
+		});
+		return {
+			...toRefs(state),
+		};
+	},
+});
+</script>
diff --git a/src/views/pages/formRules/index.vue b/src/views/pages/formRules/index.vue
new file mode 100644
index 0000000..9c24343
--- /dev/null
+++ b/src/views/pages/formRules/index.vue
@@ -0,0 +1,80 @@
+<template>
+	<div class="form-rules-container">
+		<el-card shadow="hover" header="表单组件1"> <FormRulesOne :data="formRulesOneData" ref="pagesFormRulesOneRef" /></el-card>
+		<el-card shadow="hover" header="表单组件2" class="mt15"><FormRulesTwo ref="pagesFormRulesTwoRef" /> </el-card>
+		<el-card shadow="hover" header="表单组件3" class="mt15"> <FormRulesThree ref="pagesFormRulesThreeRef" /></el-card>
+		<el-row class="flex mt15">
+			<div class="flex-margin">
+				<el-button size="default" @click="onResetForm">
+					<SvgIcon name="ele-RefreshRight" />
+					重置表单
+				</el-button>
+				<el-button size="default" type="primary" @click="onSubmitForm">
+					<SvgIcon name="iconfont icon-shuxing" />
+					验证表单
+				</el-button>
+			</div>
+		</el-row>
+	</div>
+</template>
+
+<script lang="ts">
+import { toRefs, reactive, defineComponent, getCurrentInstance } from 'vue';
+import { ElMessage } from 'element-plus';
+import FormRulesOne from '/@/views/pages/formRules/component/formRulesOne.vue';
+import FormRulesTwo from '/@/views/pages/formRules/component/formRulesTwo.vue';
+import FormRulesThree from '/@/views/pages/formRules/component/formRulesThree.vue';
+
+export default defineComponent({
+	name: 'pagesFormRules',
+	components: {
+		FormRulesOne,
+		FormRulesTwo,
+		FormRulesThree,
+	},
+	setup() {
+		const { proxy } = <any>getCurrentInstance();
+		const state = reactive({
+			formRulesOneData: {
+				name: 'lyt',
+				email: 'lyt123@.com',
+				autograph: 'lyt123456',
+				occupation: '1',
+			},
+		});
+		// 表单组件验证
+		const formRulesValidate = (pageRef: string, sonRef: string) => {
+			return new Promise((resolve) => {
+				proxy.$refs[pageRef].$refs[sonRef].validate((valid: boolean) => {
+					if (valid) resolve(valid);
+				});
+			});
+		};
+		// 表单组件重置
+		const formRulesResetFields = () => {
+			proxy.$refs.pagesFormRulesOneRef.$refs.formRulesOneRef.resetFields();
+			proxy.$refs.pagesFormRulesTwoRef.$refs.formRulesTwoRef.resetFields();
+			proxy.$refs.pagesFormRulesThreeRef.$refs.formRulesThreeRef.resetFields();
+		};
+		// 验证表单
+		const onSubmitForm = () => {
+			Promise.all([
+				formRulesValidate('pagesFormRulesOneRef', 'formRulesOneRef'),
+				formRulesValidate('pagesFormRulesTwoRef', 'formRulesTwoRef'),
+				formRulesValidate('pagesFormRulesThreeRef', 'formRulesThreeRef'),
+			]).then(() => {
+				ElMessage.success('表单全部验证成功');
+			});
+		};
+		// 重置表单
+		const onResetForm = () => {
+			formRulesResetFields();
+		};
+		return {
+			onSubmitForm,
+			onResetForm,
+			...toRefs(state),
+		};
+	},
+});
+</script>
diff --git a/src/views/pages/iocnfont/index.vue b/src/views/pages/iocnfont/index.vue
new file mode 100644
index 0000000..48a44f9
--- /dev/null
+++ b/src/views/pages/iocnfont/index.vue
@@ -0,0 +1,87 @@
+<template>
+	<div class="iconfont-container">
+		<el-card shadow="hover" :header="`iconfont 字体图标(自动载入):${sheetsIconList.length}个`">
+			<el-row class="iconfont-row">
+				<el-col :xs="12" :sm="8" :md="6" :lg="4" :xl="2" v-for="(v, k) in sheetsIconList" :key="k">
+					<div class="iconfont-warp">
+						<div class="flex-margin">
+							<div class="iconfont-warp-value">
+								<i :class="v" class="iconfont"></i>
+							</div>
+							<div class="iconfont-warp-label mt10">{{ v }}</div>
+						</div>
+					</div>
+				</el-col>
+			</el-row>
+		</el-card>
+	</div>
+</template>
+
+<script lang="ts">
+import { toRefs, reactive, onMounted, defineComponent } from 'vue';
+import initIconfont from '/@/utils/getStyleSheets';
+
+export default defineComponent({
+	name: 'pagesIocnfont',
+	setup() {
+		const state = reactive({
+			sheetsIconList: [],
+		});
+		// 初始化获取 css 样式,这里使用阿里的图标(记得加上前缀 `iconfont`),其它第三方请自行做判断
+		const initGetStyleSheets = () => {
+			initIconfont.ali().then((res: any) => (state.sheetsIconList = res));
+		};
+		// 页面加载时
+		onMounted(() => {
+			initGetStyleSheets();
+		});
+		return {
+			...toRefs(state),
+		};
+	},
+});
+</script>
+
+<style scoped lang="scss">
+.iconfont-container {
+	.iconfont-row {
+		border-top: 1px solid var(--next-border-color-light);
+		border-left: 1px solid var(--next-border-color-light);
+		.iconfont-warp {
+			text-align: center;
+			border-right: 1px solid var(--next-border-color-light);
+			border-bottom: 1px solid var(--next-border-color-light);
+			height: 120px;
+			overflow: hidden;
+			display: flex;
+			transition: all 0.3s ease;
+			&:hover {
+				box-shadow: 0 2px 12px var(--next-color-dark-hover);
+				cursor: pointer;
+				transition: all 0.3s ease;
+				.iconfont-warp-value {
+					i {
+						color: var(--el-color-primary);
+						transition: all 0.3s ease;
+					}
+				}
+				.iconfont-warp-label {
+					color: var(--el-color-primary);
+					transition: all 0.3s ease;
+				}
+			}
+			.iconfont-warp-value {
+				i {
+					color: #606266;
+					font-size: 32px;
+					transition: all 0.3s ease;
+				}
+			}
+			.iconfont-warp-label {
+				color: #99a9bf;
+				transition: all 0.3s ease;
+			}
+		}
+	}
+}
+</style>
diff --git a/src/views/pages/lazyImg/index.vue b/src/views/pages/lazyImg/index.vue
new file mode 100644
index 0000000..ad63f8a
--- /dev/null
+++ b/src/views/pages/lazyImg/index.vue
@@ -0,0 +1,194 @@
+<template>
+	<div class="lazy-img-container">
+		<el-card shadow="hover" header="图片懒加载演示(F12 切换到 Network Img下进行图片加载查看)">
+			<div class="flex-warp" v-if="tableData.data.length > 0">
+				<el-row :gutter="15">
+					<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb15" v-for="(v, k) in tableData.data" :key="k" @click="onTableItemClick(v)">
+						<div class="flex-warp-item">
+							<div class="flex-warp-item-box">
+								<div class="item-img" v-loading="v.loading">
+									<img :data-img="v.img" :data-key="k" :data-lazy-img-list="k" />
+								</div>
+								<div class="item-txt">
+									<div class="item-txt-title">{{ v.title }}</div>
+									<div class="item-txt-other">
+										<div style="width: 100%">
+											<div class="item-txt-msg mb10">
+												<span>评价 {{ v.evaluate }}</span>
+												<span class="ml10">收藏 {{ v.collection }}</span>
+											</div>
+											<div class="item-txt-msg item-txt-price">
+												<span class="font-price">
+													<span>¥</span>
+													<span class="font">{{ v.price }}</span>
+												</span>
+												<span>月销{{ v.monSales }}笔</span>
+											</div>
+										</div>
+									</div>
+								</div>
+							</div>
+						</div>
+					</el-col>
+				</el-row>
+			</div>
+			<el-empty v-else description="暂无数据"></el-empty>
+			<template v-if="tableData.data.length > 0">
+				<el-pagination
+					style="text-align: right"
+					background
+					@size-change="onHandleSizeChange"
+					@current-change="onHandleCurrentChange"
+					:page-sizes="[10, 20, 30]"
+					:current-page="tableData.param.pageNum"
+					:page-size="tableData.param.pageSize"
+					layout="total, sizes, prev, pager, next, jumper"
+					:total="tableData.total"
+				>
+				</el-pagination>
+			</template>
+		</el-card>
+	</div>
+</template>
+
+<script lang="ts">
+import { toRefs, reactive, onMounted, defineComponent } from 'vue';
+import { useRouter } from 'vue-router';
+import other from '/@/utils/other';
+import { filterList } from './mock';
+
+export default defineComponent({
+	name: 'pagesListAdapt',
+	setup() {
+		const router = useRouter();
+		const state = reactive({
+			tableData: {
+				data: filterList,
+				total: 99,
+				loading: false,
+				param: {
+					pageNum: 1,
+					pageSize: 10,
+				},
+			},
+		});
+		// 当前列表项点击
+		const onTableItemClick = (v: any) => {
+			router.push({
+				path: '/pages/filteringDetails',
+				query: { id: v.id },
+			});
+		};
+		// 分页点击
+		const onHandleSizeChange = (val: number) => {
+			state.tableData.param.pageSize = val;
+		};
+		// 分页点击
+		const onHandleCurrentChange = (val: number) => {
+			state.tableData.param.pageNum = val;
+		};
+		// 页面加载时
+		onMounted(() => {
+			other.lazyImg('[data-lazy-img-list]', state.tableData.data);
+		});
+		return {
+			onTableItemClick,
+			onHandleSizeChange,
+			onHandleCurrentChange,
+			...toRefs(state),
+		};
+	},
+});
+</script>
+
+<style scoped lang="scss">
+.lazy-img-container {
+	.flex-warp {
+		display: flex;
+		flex-wrap: wrap;
+		align-content: flex-start;
+		margin: 0 -5px;
+		.flex-warp-item {
+			padding: 5px;
+			width: 100%;
+			height: 360px;
+			.flex-warp-item-box {
+				border: 1px solid var(--next-border-color-light);
+				width: 100%;
+				height: 100%;
+				border-radius: 2px;
+				display: flex;
+				flex-direction: column;
+				transition: all 0.3s ease;
+				&:hover {
+					cursor: pointer;
+					border: 1px solid var(--el-color-primary);
+					transition: all 0.3s ease;
+					box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.03);
+					.item-txt-title {
+						color: var(--el-color-primary) !important;
+						transition: all 0.3s ease;
+					}
+					.item-img {
+						img {
+							transition: all 0.3s ease;
+							transform: translateZ(0) scale(1.05);
+						}
+					}
+				}
+				.item-img {
+					width: 100%;
+					height: 215px;
+					overflow: hidden;
+					img {
+						transition: all 0.3s ease;
+						width: 100%;
+						height: 100%;
+					}
+				}
+				.item-txt {
+					flex: 1;
+					padding: 15px;
+					display: flex;
+					flex-direction: column;
+					overflow: hidden;
+					.item-txt-title {
+						text-overflow: ellipsis;
+						overflow: hidden;
+						-webkit-line-clamp: 2;
+						-webkit-box-orient: vertical;
+						display: -webkit-box;
+						color: #666666;
+						transition: all 0.3s ease;
+						&:hover {
+							color: var(--el-color-primary);
+							text-decoration: underline;
+							transition: all 0.3s ease;
+						}
+					}
+					.item-txt-other {
+						flex: 1;
+						align-items: flex-end;
+						display: flex;
+						.item-txt-msg {
+							font-size: 12px;
+							color: #8d8d91;
+						}
+						.item-txt-price {
+							display: flex;
+							justify-content: space-between;
+							align-items: center;
+							.font-price {
+								color: #ff5000;
+								.font {
+									font-size: 22px;
+								}
+							}
+						}
+					}
+				}
+			}
+		}
+	}
+}
+</style>
diff --git a/src/views/pages/lazyImg/mock.ts b/src/views/pages/lazyImg/mock.ts
new file mode 100644
index 0000000..4eb3d29
--- /dev/null
+++ b/src/views/pages/lazyImg/mock.ts
@@ -0,0 +1,313 @@
+// 列表数据
+export const filterList = [
+	{
+		img: 'https://news.sznews.com/pic/2021-03/09/e37326cc-4583-48f3-aa00-ecc2392d319d.jpg',
+		title: '36分钟,深圳平均通勤时间出炉!GDP10强城市中仅输杭州',
+		evaluate: (Math.random() * 10).toFixed(2),
+		collection: (Math.random() * 100).toFixed(2),
+		price: (Math.random() * 10).toFixed(2),
+		monSales: (Math.random() * 20).toFixed(2),
+		id: 1,
+		loading: true,
+	},
+	{
+		img: 'http://news.sznews.com/pic/2021-03/09/78cf72b6-e2d9-459d-a368-470414a027f4679cf4ea-26fa-48c8-9fee-c2d092a91400.png',
+		title: '为爱而动,“红色鹊桥”三八妇女节交友联谊活动助力深圳女孩脱单',
+		evaluate: (Math.random() * 10).toFixed(2),
+		collection: (Math.random() * 100).toFixed(2),
+		price: (Math.random() * 10).toFixed(2),
+		monSales: (Math.random() * 20).toFixed(2),
+		id: 2,
+		loading: true,
+	},
+	{
+		img: 'http://news.sznews.com/pic/2021-03/09/1faf3c6e-1250-4e6b-b072-4a331553e027.jpg',
+		title: '粤桂协作“背水一战” 解决广西大化县3.7万人饮水难题',
+		evaluate: (Math.random() * 10).toFixed(2),
+		collection: (Math.random() * 100).toFixed(2),
+		price: (Math.random() * 10).toFixed(2),
+		monSales: (Math.random() * 20).toFixed(2),
+		id: 3,
+		loading: true,
+	},
+	{
+		img: 'https://news.sznews.com/pic/2021-03/09/9fcf6dd4-1e80-4497-bdc9-83dc7246d170.jpg.2',
+		title: '城镇就业女性平均薪酬6847元 女性职场渗透率提升',
+		evaluate: (Math.random() * 10).toFixed(2),
+		collection: (Math.random() * 100).toFixed(2),
+		price: (Math.random() * 10).toFixed(2),
+		monSales: (Math.random() * 20).toFixed(2),
+		id: 4,
+		loading: true,
+	},
+	{
+		img: 'https://news.sznews.com/pic/2021-03/09/1bd78227-4126-4a43-bdf6-48ead6edd1bf.jpg.2',
+		title: '深圳:实现“从0到1”源头创新,推进大湾区综合性国家科学中心建设!',
+		evaluate: (Math.random() * 10).toFixed(2),
+		collection: (Math.random() * 100).toFixed(2),
+		price: (Math.random() * 10).toFixed(2),
+		monSales: (Math.random() * 20).toFixed(2),
+		id: 5,
+		loading: true,
+	},
+	{
+		img: 'http://news.sznews.com/pic/2021-03/08/9ea943a3-3ae8-4f49-8296-711ec36ef8c6_watermark.png',
+		title: '煖声音第126期|愿你有诗酒趁年华的洒脱,也有岁月沉淀后的坚定从容',
+		evaluate: (Math.random() * 10).toFixed(2),
+		collection: (Math.random() * 100).toFixed(2),
+		price: (Math.random() * 10).toFixed(2),
+		monSales: (Math.random() * 20).toFixed(2),
+		id: 6,
+		loading: true,
+	},
+	{
+		img: 'https://news.sznews.com/pic/2021-03/08/a95ba232-1422-4f7e-b85f-c61d486c8659.jpg.2',
+		title: '姐妹们一起来吐槽,最不能接受男人的缺点!',
+		evaluate: (Math.random() * 10).toFixed(2),
+		collection: (Math.random() * 100).toFixed(2),
+		price: (Math.random() * 10).toFixed(2),
+		monSales: (Math.random() * 20).toFixed(2),
+		id: 7,
+		loading: true,
+	},
+	{
+		img: 'http://news.sznews.com/pic/2021-03/08/76816bf0-3899-4c7e-bc6e-079b5ba8725e.jpg',
+		title: '民生小事 | 手机遗落出租车 热心民警帮找回',
+		evaluate: (Math.random() * 10).toFixed(2),
+		collection: (Math.random() * 100).toFixed(2),
+		price: (Math.random() * 10).toFixed(2),
+		monSales: (Math.random() * 20).toFixed(2),
+		id: 8,
+		loading: true,
+	},
+	{
+		img: 'https://news.sznews.com/pic/2021-03/08/28ed70d4-71f5-4abb-bf7b-0294bece9e43.jpg.2',
+		title: '“十三五”:深圳交上靓丽答卷 发展动力加快转换',
+		evaluate: (Math.random() * 10).toFixed(2),
+		collection: (Math.random() * 100).toFixed(2),
+		price: (Math.random() * 10).toFixed(2),
+		monSales: (Math.random() * 20).toFixed(2),
+		id: 9,
+		loading: true,
+	},
+	{
+		img: 'http://news.sznews.com/pic/2021-03/05/d13ae31f-fd45-431a-b48e-c5895bbc193e.png',
+		title: '深圳湾公园一女子落水,三名男子接力及时施救',
+		evaluate: (Math.random() * 10).toFixed(2),
+		collection: (Math.random() * 100).toFixed(2),
+		price: (Math.random() * 10).toFixed(2),
+		monSales: (Math.random() * 20).toFixed(2),
+		id: 10,
+		loading: true,
+	},
+	{
+		img: 'http://z0.sinaimg.cn/auto/resize?size=235_156&img=http://n.sinaimg.cn/spider20210704/653/w930h523/20210704/d5d2-krwipas6444058.jpg',
+		title: '36分钟,深圳平均通勤时间出炉!GDP10强城市中仅输杭州',
+		evaluate: (Math.random() * 10).toFixed(2),
+		collection: (Math.random() * 100).toFixed(2),
+		price: (Math.random() * 10).toFixed(2),
+		monSales: (Math.random() * 20).toFixed(2),
+		id: 1,
+		loading: true,
+	},
+	{
+		img: 'http://z0.sinaimg.cn/auto/resize?size=235_156&img=http://n.sinaimg.cn/spider20210704/766/w930h636/20210704/b1ae-krwipas6332914.jpg',
+		title: '为爱而动,“红色鹊桥”三八妇女节交友联谊活动助力深圳女孩脱单',
+		evaluate: (Math.random() * 10).toFixed(2),
+		collection: (Math.random() * 100).toFixed(2),
+		price: (Math.random() * 10).toFixed(2),
+		monSales: (Math.random() * 20).toFixed(2),
+		id: 2,
+		loading: true,
+	},
+	{
+		img: 'http://z0.sinaimg.cn/auto/resize?size=235_156&img=http://n.sinaimg.cn/spider20210704/750/w930h620/20210704/2886-krwipas6264821.jpg',
+		title: '粤桂协作“背水一战” 解决广西大化县3.7万人饮水难题',
+		evaluate: (Math.random() * 10).toFixed(2),
+		collection: (Math.random() * 100).toFixed(2),
+		price: (Math.random() * 10).toFixed(2),
+		monSales: (Math.random() * 20).toFixed(2),
+		id: 3,
+		loading: true,
+	},
+	{
+		img: 'http://z0.sinaimg.cn/auto/resize?size=235_156&img=http://n.sinaimg.cn/spider20210704/750/w930h620/20210704/767c-krwipas6387862.jpg',
+		title: '城镇就业女性平均薪酬6847元 女性职场渗透率提升',
+		evaluate: (Math.random() * 10).toFixed(2),
+		collection: (Math.random() * 100).toFixed(2),
+		price: (Math.random() * 10).toFixed(2),
+		monSales: (Math.random() * 20).toFixed(2),
+		id: 4,
+		loading: true,
+	},
+	{
+		img: 'http://z0.sinaimg.cn/auto/resize?size=235_156&img=http://n.sinaimg.cn/spider20210704/111/w1024h687/20210704/1f65-krwipas5871436.jpg',
+		title: '盛夏的那考河湿地公园!',
+		evaluate: (Math.random() * 10).toFixed(2),
+		collection: (Math.random() * 100).toFixed(2),
+		price: (Math.random() * 10).toFixed(2),
+		monSales: (Math.random() * 20).toFixed(2),
+		id: 5,
+		loading: true,
+	},
+	{
+		img: 'http://z0.sinaimg.cn/auto/resize?size=235_156&img=http://n.sinaimg.cn/spider20210704/657/w930h527/20210704/7eae-krwipas5866609.jpg',
+		title: '煖声音第126期|愿你有诗酒趁年华的洒脱,也有岁月沉淀后的坚定从容',
+		evaluate: (Math.random() * 10).toFixed(2),
+		collection: (Math.random() * 100).toFixed(2),
+		price: (Math.random() * 10).toFixed(2),
+		monSales: (Math.random() * 20).toFixed(2),
+		id: 6,
+		loading: true,
+	},
+	{
+		img: 'http://z0.sinaimg.cn/auto/resize?size=235_156&img=http://n.sinaimg.cn/spider20210703/760/w930h630/20210703/124e-krwipas5596390.jpg',
+		title: '姐妹们一起来吐槽,最不能接受男人的缺点!',
+		evaluate: (Math.random() * 10).toFixed(2),
+		collection: (Math.random() * 100).toFixed(2),
+		price: (Math.random() * 10).toFixed(2),
+		monSales: (Math.random() * 20).toFixed(2),
+		id: 7,
+		loading: true,
+	},
+	{
+		img: 'http://z0.sinaimg.cn/auto/resize?size=235_156&img=http://n.sinaimg.cn/spider20210703/27/w930h697/20210703/9630-krwipas5514972.jpg',
+		title: '民生小事 | 手机遗落出租车 热心民警帮找回',
+		evaluate: (Math.random() * 10).toFixed(2),
+		collection: (Math.random() * 100).toFixed(2),
+		price: (Math.random() * 10).toFixed(2),
+		monSales: (Math.random() * 20).toFixed(2),
+		id: 8,
+		loading: true,
+	},
+	{
+		img: 'http://z0.sinaimg.cn/auto/resize?size=235_156&img=http://n.sinaimg.cn/spider20210703/750/w930h620/20210703/2fe3-krwipas5388050.jpg',
+		title: '“十三五”:深圳交上靓丽答卷 发展动力加快转换',
+		evaluate: (Math.random() * 10).toFixed(2),
+		collection: (Math.random() * 100).toFixed(2),
+		price: (Math.random() * 10).toFixed(2),
+		monSales: (Math.random() * 20).toFixed(2),
+		id: 9,
+		loading: true,
+	},
+	{
+		img: 'http://z0.sinaimg.cn/auto/resize?size=235_156&img=http://n.sinaimg.cn/spider20210703/724/w930h594/20210703/98b6-krwipas5234060.jpg',
+		title: '深圳湾公园一女子落水,三名男子接力及时施救',
+		evaluate: (Math.random() * 10).toFixed(2),
+		collection: (Math.random() * 100).toFixed(2),
+		price: (Math.random() * 10).toFixed(2),
+		monSales: (Math.random() * 20).toFixed(2),
+		id: 10,
+		loading: true,
+	},
+	{
+		img: 'http://z0.sinaimg.cn/auto/resize?size=235_156&img=http://n.sinaimg.cn/spider20210703/750/w930h620/20210703/f765-krwipas5194727.jpg',
+		title: '36分钟,深圳平均通勤时间出炉!GDP10强城市中仅输杭州',
+		evaluate: (Math.random() * 10).toFixed(2),
+		collection: (Math.random() * 100).toFixed(2),
+		price: (Math.random() * 10).toFixed(2),
+		monSales: (Math.random() * 20).toFixed(2),
+		id: 1,
+		loading: true,
+	},
+	{
+		img: 'http://z0.sinaimg.cn/auto/resize?size=235_156&img=http://n.sinaimg.cn/spider20210702/750/w930h620/20210702/5dde-krwipas4724976.jpg',
+		title: '为爱而动,“红色鹊桥”三八妇女节交友联谊活动助力深圳女孩脱单',
+		evaluate: (Math.random() * 10).toFixed(2),
+		collection: (Math.random() * 100).toFixed(2),
+		price: (Math.random() * 10).toFixed(2),
+		monSales: (Math.random() * 20).toFixed(2),
+		id: 2,
+		loading: true,
+	},
+	{
+		img: 'http://z0.sinaimg.cn/auto/resize?size=235_156&img=http://n.sinaimg.cn/spider20210702/750/w930h620/20210702/f45e-krwipas4566804.jpg',
+		title: '粤桂协作“背水一战” 解决广西大化县3.7万人饮水难题',
+		evaluate: (Math.random() * 10).toFixed(2),
+		collection: (Math.random() * 100).toFixed(2),
+		price: (Math.random() * 10).toFixed(2),
+		monSales: (Math.random() * 20).toFixed(2),
+		id: 3,
+		loading: true,
+	},
+	{
+		img: 'http://z0.sinaimg.cn/auto/resize?size=235_156&img=http://n.sinaimg.cn/spider20210702/750/w930h620/20210702/5579-krwipas4551382.jpg',
+		title: '城镇就业女性平均薪酬6847元 女性职场渗透率提升',
+		evaluate: (Math.random() * 10).toFixed(2),
+		collection: (Math.random() * 100).toFixed(2),
+		price: (Math.random() * 10).toFixed(2),
+		monSales: (Math.random() * 20).toFixed(2),
+		id: 4,
+		loading: true,
+	},
+	{
+		img: 'http://z0.sinaimg.cn/auto/resize?size=235_156&img=http://n.sinaimg.cn/spider20210702/750/w930h620/20210702/7c75-krwipas4543661.jpg',
+		title: '深圳:实现“从0到1”源头创新,推进大湾区综合性国家科学中心建设!',
+		evaluate: (Math.random() * 10).toFixed(2),
+		collection: (Math.random() * 100).toFixed(2),
+		price: (Math.random() * 10).toFixed(2),
+		monSales: (Math.random() * 20).toFixed(2),
+		id: 5,
+		loading: true,
+	},
+	{
+		img: 'http://z0.sinaimg.cn/auto/resize?size=235_156&img=http://n.sinaimg.cn/spider20210702/653/w930h523/20210702/ece2-krwipas4411140.jpg',
+		title: '煖声音第126期|愿你有诗酒趁年华的洒脱,也有岁月沉淀后的坚定从容',
+		evaluate: (Math.random() * 10).toFixed(2),
+		collection: (Math.random() * 100).toFixed(2),
+		price: (Math.random() * 10).toFixed(2),
+		monSales: (Math.random() * 20).toFixed(2),
+		id: 6,
+		loading: true,
+	},
+	{
+		img: 'http://z0.sinaimg.cn/auto/resize?size=235_156&img=http://n.sinaimg.cn/spider20210702/750/w930h620/20210702/f5c2-krwipas4215211.jpg',
+		title: '姐妹们一起来吐槽,最不能接受男人的缺点!',
+		evaluate: (Math.random() * 10).toFixed(2),
+		collection: (Math.random() * 100).toFixed(2),
+		price: (Math.random() * 10).toFixed(2),
+		monSales: (Math.random() * 20).toFixed(2),
+		id: 7,
+		loading: true,
+	},
+	{
+		img: 'http://z0.sinaimg.cn/auto/resize?size=235_156&img=http://n.sinaimg.cn/spider20210701/720/w930h590/20210701/eabc-krwipas3509204.jpg',
+		title: '民生小事 | 手机遗落出租车 热心民警帮找回',
+		evaluate: (Math.random() * 10).toFixed(2),
+		collection: (Math.random() * 100).toFixed(2),
+		price: (Math.random() * 10).toFixed(2),
+		monSales: (Math.random() * 20).toFixed(2),
+		id: 8,
+		loading: true,
+	},
+	{
+		img: 'http://z0.sinaimg.cn/auto/resize?size=235_156&img=http://n.sinaimg.cn/spider20210701/797/w930h667/20210701/4667-krwipas3365057.jpg',
+		title: '“十三五”:深圳交上靓丽答卷 发展动力加快转换',
+		evaluate: (Math.random() * 10).toFixed(2),
+		collection: (Math.random() * 100).toFixed(2),
+		price: (Math.random() * 10).toFixed(2),
+		monSales: (Math.random() * 20).toFixed(2),
+		id: 9,
+		loading: true,
+	},
+	{
+		img: 'http://z0.sinaimg.cn/auto/resize?size=235_156&img=http://n.sinaimg.cn/spider20210701/750/w930h620/20210701/baea-krwipas2976622.jpg',
+		title: '民众前往中共一大纪念馆参观',
+		evaluate: (Math.random() * 10).toFixed(2),
+		collection: (Math.random() * 100).toFixed(2),
+		price: (Math.random() * 10).toFixed(2),
+		monSales: (Math.random() * 20).toFixed(2),
+		id: 10,
+		loading: true,
+	},
+	{
+		img: 'http://z0.sinaimg.cn/auto/resize?size=235_156&img=http://n.sinaimg.cn/spider20210630/617/w850h567/20210630/5c96-krwipas1819108.jpg',
+		title: '延吉灯光秀美轮美奂 市民徜徉璀璨夜景',
+		evaluate: (Math.random() * 10).toFixed(2),
+		collection: (Math.random() * 100).toFixed(2),
+		price: (Math.random() * 10).toFixed(2),
+		monSales: (Math.random() * 20).toFixed(2),
+		id: 10,
+		loading: true,
+	},
+];
diff --git a/src/views/pages/listAdapt/index.vue b/src/views/pages/listAdapt/index.vue
new file mode 100644
index 0000000..15e27bc
--- /dev/null
+++ b/src/views/pages/listAdapt/index.vue
@@ -0,0 +1,209 @@
+<template>
+	<div class="list-adapt-container">
+		<el-card shadow="hover" header="列表自适应演示(改变窗口查看效果)">
+			<div class="flex-warp" v-if="tableData.data.length > 0">
+				<el-row :gutter="15">
+					<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb15" v-for="(v, k) in tableData.data" :key="k" @click="onTableItemClick(v)">
+						<div class="flex-warp-item">
+							<div class="flex-warp-item-box">
+								<div class="item-img">
+									<img :src="v.img" />
+								</div>
+								<div class="item-txt">
+									<div class="item-txt-title">{{ v.title }}</div>
+									<div class="item-txt-other">
+										<div style="width: 100%">
+											<div class="item-txt-msg mb10">
+												<span>评价 {{ v.evaluate }}</span>
+												<span class="ml10">收藏 {{ v.collection }}</span>
+											</div>
+											<div class="item-txt-msg item-txt-price">
+												<span class="font-price">
+													<span>¥</span>
+													<span class="font">{{ v.price }}</span>
+												</span>
+												<span>月销{{ v.monSales }}笔</span>
+											</div>
+										</div>
+									</div>
+								</div>
+							</div>
+						</div>
+					</el-col>
+				</el-row>
+			</div>
+			<el-empty v-else description="暂无数据"></el-empty>
+			<template v-if="tableData.data.length > 0">
+				<el-pagination
+					style="text-align: right"
+					background
+					@size-change="onHandleSizeChange"
+					@current-change="onHandleCurrentChange"
+					:page-sizes="[10, 20, 30]"
+					:current-page="tableData.param.pageNum"
+					:page-size="tableData.param.pageSize"
+					layout="total, sizes, prev, pager, next, jumper"
+					:total="tableData.total"
+				>
+				</el-pagination>
+			</template>
+		</el-card>
+	</div>
+</template>
+
+<script lang="ts">
+import { toRefs, reactive, defineComponent } from 'vue';
+import { useRouter } from 'vue-router';
+import { filterList } from './mock';
+
+// 定义接口来定义对象的类型
+interface ListAdaptRow {
+	img: string;
+	title: string;
+	evaluate: string;
+	collection: string;
+	price: string;
+	monSales: string;
+	id: number;
+}
+interface TableDataState {
+	tableData: {
+		data: Array<ListAdaptRow>;
+		total: number;
+		loading: boolean;
+		param: {
+			pageNum: number;
+			pageSize: number;
+		};
+	};
+}
+
+export default defineComponent({
+	name: 'pagesListAdapt',
+	setup() {
+		const router = useRouter();
+		const state = reactive<TableDataState>({
+			tableData: {
+				data: filterList,
+				total: 99,
+				loading: false,
+				param: {
+					pageNum: 1,
+					pageSize: 10,
+				},
+			},
+		});
+		// 当前列表项点击
+		const onTableItemClick = (v: ListAdaptRow) => {
+			router.push({
+				path: '/pages/filteringDetails',
+				query: { id: v.id },
+			});
+		};
+		// 分页点击
+		const onHandleSizeChange = (val: number) => {
+			state.tableData.param.pageSize = val;
+		};
+		// 分页点击
+		const onHandleCurrentChange = (val: number) => {
+			state.tableData.param.pageNum = val;
+		};
+		return {
+			onTableItemClick,
+			onHandleSizeChange,
+			onHandleCurrentChange,
+			...toRefs(state),
+		};
+	},
+});
+</script>
+
+<style scoped lang="scss">
+.flex-warp {
+	display: flex;
+	flex-wrap: wrap;
+	align-content: flex-start;
+	margin: 0 -5px;
+	.flex-warp-item {
+		padding: 5px;
+		width: 100%;
+		height: 360px;
+		.flex-warp-item-box {
+			border: 1px solid var(--next-border-color-light);
+			width: 100%;
+			height: 100%;
+			border-radius: 2px;
+			display: flex;
+			flex-direction: column;
+			transition: all 0.3s ease;
+			&:hover {
+				cursor: pointer;
+				border: 1px solid var(--el-color-primary);
+				transition: all 0.3s ease;
+				box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.03);
+				.item-txt-title {
+					color: var(--el-color-primary) !important;
+					transition: all 0.3s ease;
+				}
+				.item-img {
+					img {
+						transition: all 0.3s ease;
+						transform: translateZ(0) scale(1.05);
+					}
+				}
+			}
+			.item-img {
+				width: 100%;
+				height: 215px;
+				overflow: hidden;
+				img {
+					transition: all 0.3s ease;
+					width: 100%;
+					height: 100%;
+				}
+			}
+			.item-txt {
+				flex: 1;
+				padding: 15px;
+				display: flex;
+				flex-direction: column;
+				overflow: hidden;
+				.item-txt-title {
+					text-overflow: ellipsis;
+					overflow: hidden;
+					-webkit-line-clamp: 2;
+					-webkit-box-orient: vertical;
+					display: -webkit-box;
+					color: #666666;
+					transition: all 0.3s ease;
+					&:hover {
+						color: var(--el-color-primary);
+						text-decoration: underline;
+						transition: all 0.3s ease;
+					}
+				}
+				.item-txt-other {
+					flex: 1;
+					align-items: flex-end;
+					display: flex;
+					.item-txt-msg {
+						font-size: 12px;
+						color: #8d8d91;
+					}
+					.item-txt-price {
+						display: flex;
+						justify-content: space-between;
+						align-items: center;
+						.font-price {
+							color: #ff5000;
+							.font {
+								font-size: 22px;
+							}
+						}
+					}
+				}
+			}
+		}
+	}
+}
+</style>
diff --git a/src/views/pages/listAdapt/mock.ts b/src/views/pages/listAdapt/mock.ts
new file mode 100644
index 0000000..a31e9e3
--- /dev/null
+++ b/src/views/pages/listAdapt/mock.ts
@@ -0,0 +1,93 @@
+// 列表数据
+export const filterList = [
+	{
+		img: 'https://news.sznews.com/pic/2021-03/09/e37326cc-4583-48f3-aa00-ecc2392d319d.jpg',
+		title: '36分钟,深圳平均通勤时间出炉!GDP10强城市中仅输杭州',
+		evaluate: (Math.random() * 10).toFixed(2),
+		collection: (Math.random() * 100).toFixed(2),
+		price: (Math.random() * 10).toFixed(2),
+		monSales: (Math.random() * 20).toFixed(2),
+		id: 1,
+	},
+	{
+		img: 'http://news.sznews.com/pic/2021-03/09/78cf72b6-e2d9-459d-a368-470414a027f4679cf4ea-26fa-48c8-9fee-c2d092a91400.png',
+		title: '为爱而动,“红色鹊桥”三八妇女节交友联谊活动助力深圳女孩脱单',
+		evaluate: (Math.random() * 10).toFixed(2),
+		collection: (Math.random() * 100).toFixed(2),
+		price: (Math.random() * 10).toFixed(2),
+		monSales: (Math.random() * 20).toFixed(2),
+		id: 2,
+	},
+	{
+		img: 'http://news.sznews.com/pic/2021-03/09/1faf3c6e-1250-4e6b-b072-4a331553e027.jpg',
+		title: '粤桂协作“背水一战” 解决广西大化县3.7万人饮水难题',
+		evaluate: (Math.random() * 10).toFixed(2),
+		collection: (Math.random() * 100).toFixed(2),
+		price: (Math.random() * 10).toFixed(2),
+		monSales: (Math.random() * 20).toFixed(2),
+		id: 3,
+	},
+	{
+		img: 'https://news.sznews.com/pic/2021-03/09/9fcf6dd4-1e80-4497-bdc9-83dc7246d170.jpg.2',
+		title: '城镇就业女性平均薪酬6847元 女性职场渗透率提升',
+		evaluate: (Math.random() * 10).toFixed(2),
+		collection: (Math.random() * 100).toFixed(2),
+		price: (Math.random() * 10).toFixed(2),
+		monSales: (Math.random() * 20).toFixed(2),
+		id: 4,
+	},
+	{
+		img: 'https://news.sznews.com/pic/2021-03/09/1bd78227-4126-4a43-bdf6-48ead6edd1bf.jpg.2',
+		title: '深圳:实现“从0到1”源头创新,推进大湾区综合性国家科学中心建设!',
+		evaluate: (Math.random() * 10).toFixed(2),
+		collection: (Math.random() * 100).toFixed(2),
+		price: (Math.random() * 10).toFixed(2),
+		monSales: (Math.random() * 20).toFixed(2),
+		id: 5,
+	},
+	{
+		img: 'http://news.sznews.com/pic/2021-03/08/9ea943a3-3ae8-4f49-8296-711ec36ef8c6_watermark.png',
+		title: '煖声音第126期|愿你有诗酒趁年华的洒脱,也有岁月沉淀后的坚定从容',
+		evaluate: (Math.random() * 10).toFixed(2),
+		collection: (Math.random() * 100).toFixed(2),
+		price: (Math.random() * 10).toFixed(2),
+		monSales: (Math.random() * 20).toFixed(2),
+		id: 6,
+	},
+	{
+		img: 'https://news.sznews.com/pic/2021-03/08/a95ba232-1422-4f7e-b85f-c61d486c8659.jpg.2',
+		title: '姐妹们一起来吐槽,最不能接受男人的缺点!',
+		evaluate: (Math.random() * 10).toFixed(2),
+		collection: (Math.random() * 100).toFixed(2),
+		price: (Math.random() * 10).toFixed(2),
+		monSales: (Math.random() * 20).toFixed(2),
+		id: 7,
+	},
+	{
+		img: 'http://news.sznews.com/pic/2021-03/08/76816bf0-3899-4c7e-bc6e-079b5ba8725e.jpg',
+		title: '民生小事 | 手机遗落出租车 热心民警帮找回',
+		evaluate: (Math.random() * 10).toFixed(2),
+		collection: (Math.random() * 100).toFixed(2),
+		price: (Math.random() * 10).toFixed(2),
+		monSales: (Math.random() * 20).toFixed(2),
+		id: 8,
+	},
+	{
+		img: 'https://news.sznews.com/pic/2021-03/08/28ed70d4-71f5-4abb-bf7b-0294bece9e43.jpg.2',
+		title: '“十三五”:深圳交上靓丽答卷 发展动力加快转换',
+		evaluate: (Math.random() * 10).toFixed(2),
+		collection: (Math.random() * 100).toFixed(2),
+		price: (Math.random() * 10).toFixed(2),
+		monSales: (Math.random() * 20).toFixed(2),
+		id: 9,
+	},
+	{
+		img: 'http://news.sznews.com/pic/2021-03/05/d13ae31f-fd45-431a-b48e-c5895bbc193e.png',
+		title: '深圳湾公园一女子落水,三名男子接力及时施救',
+		evaluate: (Math.random() * 10).toFixed(2),
+		collection: (Math.random() * 100).toFixed(2),
+		price: (Math.random() * 10).toFixed(2),
+		monSales: (Math.random() * 20).toFixed(2),
+		id: 10,
+	},
+];
diff --git a/src/views/pages/preview/index.vue b/src/views/pages/preview/index.vue
new file mode 100644
index 0000000..ce3a771
--- /dev/null
+++ b/src/views/pages/preview/index.vue
@@ -0,0 +1,28 @@
+<template>
+	<div class="preview-container">
+		<el-card shadow="hover" header="element-plus 大图预览">
+			<el-image style="width: 100px; height: 100px; border-radius: 5px" :src="url" :preview-src-list="srcList" title="点击查看大图预览"> </el-image>
+		</el-card>
+	</div>
+</template>
+
+<script lang="ts">
+import { toRefs, reactive, defineComponent } from 'vue';
+
+export default defineComponent({
+	name: 'pagesPreview',
+	setup() {
+		const state = reactive({
+			url: 'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1813762643,1914315241&fm=26&gp=0.jpg',
+			srcList: [
+				'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1813762643,1914315241&fm=26&gp=0.jpg',
+				'https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=317673774,2961727727&fm=26&gp=0.jpg',
+				'https://fuss10.elemecdn.com/1/8e/aeffeb4de74e2fde4bd74fc7b4486jpeg.jpeg',
+			],
+		});
+		return {
+			...toRefs(state),
+		};
+	},
+});
+</script>
diff --git a/src/views/pages/steps/index.vue b/src/views/pages/steps/index.vue
new file mode 100644
index 0000000..becc196
--- /dev/null
+++ b/src/views/pages/steps/index.vue
@@ -0,0 +1,51 @@
+<template>
+	<div class="steps-container">
+		<el-card shadow="hover" header="element-plus 步骤条">
+			<el-steps :active="stepsActive">
+				<el-step title="第一步">
+					<template #icon>
+						<SvgIcon name="iconfont icon-0_round_solid" :size="20" />
+					</template>
+				</el-step>
+				<el-step title="第二步">
+					<template #icon>
+						<SvgIcon name="iconfont icon-2_round_solid" :size="20" />
+					</template>
+				</el-step>
+				<el-step title="第三步">
+					<template #icon>
+						<SvgIcon name="iconfont icon-3_round_solid" :size="20" />
+					</template>
+				</el-step>
+			</el-steps>
+			<el-result icon="success" title="成功提示" subTitle="请根据提示进行操作" v-if="stepsActive === 1"> </el-result>
+			<el-result icon="warning" title="警告提示" subTitle="请根据提示进行操作" v-else-if="stepsActive === 2"> </el-result>
+			<el-result icon="error" title="错误提示" subTitle="请根据提示进行操作" v-else-if="stepsActive === 3"> </el-result>
+			<el-button @click="onNextSteps" size="default" class="mt15" type="primary">
+				<SvgIcon name="iconfont icon-step" />
+				下一步
+			</el-button>
+		</el-card>
+	</div>
+</template>
+
+<script lang="ts">
+import { toRefs, reactive, defineComponent } from 'vue';
+
+export default defineComponent({
+	name: 'pagesSteps',
+	setup() {
+		const state = reactive({
+			stepsActive: 1,
+		});
+		// 下一步点击
+		const onNextSteps = () => {
+			if (state.stepsActive++ > 2) state.stepsActive = 1;
+		};
+		return {
+			onNextSteps,
+			...toRefs(state),
+		};
+	},
+});
+</script>
diff --git a/src/views/pages/tableRules/index.vue b/src/views/pages/tableRules/index.vue
new file mode 100644
index 0000000..2e17cde
--- /dev/null
+++ b/src/views/pages/tableRules/index.vue
@@ -0,0 +1,129 @@
+<template>
+	<el-card shadow="hover" header="表单表格验证">
+		<el-form ref="tableRulesRef" :model="tableData" size="default">
+			<el-table :data="tableData.data" border class="module-table-uncollected">
+				<el-table-column
+					v-for="(item, index) in tableData.header"
+					:key="index"
+					show-overflow-tooltip
+					:prop="item.prop"
+					:width="item.width"
+					:label="item.label"
+				>
+					<template v-slot:header>
+						<span v-if="item.isRequired" class="color-danger">*</span>
+						<span class="pl5">{{ item.label }}</span>
+						<el-tooltip v-if="item.isTooltip" effect="dark" content="这是tooltip" placement="top">
+							<i class="iconfont icon-quanxian" />
+						</el-tooltip>
+					</template>
+					<template v-slot="scope">
+						<el-form-item
+							:prop="`data.${scope.$index}.${item.prop}`"
+							:rules="[{ required: item.isRequired, message: '不能为空', trigger: `${item.type}` == 'input' ? 'blur' : 'change' }]"
+						>
+							<el-select v-if="item.type === 'select'" v-model="scope.row[item.prop]" placeholder="请选择">
+								<el-option v-for="sel in tableData.option" :key="sel.id" :label="sel.label" :value="sel.value" />
+							</el-select>
+							<el-date-picker
+								v-else-if="item.type === 'date'"
+								v-model="scope.row[item.prop]"
+								type="date"
+								placeholder="选择日期"
+								style="width: 100%"
+							/>
+							<el-input v-else-if="item.type === 'input'" v-model="scope.row[item.prop]" placeholder="请输入内容" />
+							<el-input v-else-if="item.type === 'dialog'" v-model="scope.row[item.prop]" readonly placeholder="请输入内容">
+								<template v-slot:suffix>
+									<i class="iconfont icon-shouye_dongtaihui" />
+								</template>
+							</el-input>
+						</el-form-item>
+					</template>
+				</el-table-column>
+			</el-table>
+		</el-form>
+		<el-row class="flex mt15">
+			<div class="flex-margin">
+				<el-button size="default" type="success" @click="onValidate">表格验证</el-button>
+				<el-button size="default" type="primary" @click="onAddRow">新增一行</el-button>
+			</div>
+		</el-row>
+	</el-card>
+</template>
+
+<script lang="ts">
+import { defineComponent, toRefs, reactive, ref } from 'vue';
+import { ElMessage } from 'element-plus';
+
+// 定义接口来定义对象的类型
+interface TableHeader {
+	prop: string;
+	width: string | number;
+	label: string;
+	isRequired?: boolean;
+	isTooltip?: boolean;
+	type: string;
+}
+interface TableRulesState {
+	tableData: {
+		data: any[];
+		header: TableHeader[];
+		option: any[];
+	};
+}
+
+export default defineComponent({
+	name: 'pagesTableRules',
+	setup() {
+		const tableRulesRef = ref();
+		const state = reactive<TableRulesState>({
+			tableData: {
+				data: [],
+				header: [
+					{ prop: 'a1', width: '', label: '一级分类', isRequired: true, type: 'select' },
+					{ prop: 'a2', width: '', label: '二级分类', isRequired: true, type: 'select' },
+					{ prop: 'a3', width: '', label: '三级分类', isRequired: true, type: 'select' },
+					{ prop: 'a4', width: '', label: '四级分类', isRequired: true, type: 'date' },
+					{ prop: 'a5', width: '', label: '五级分类', isRequired: true, type: 'input' },
+					{ prop: 'a6', width: '', label: '六级分类', isTooltip: true, type: 'dialog' },
+					{ prop: 'a7', width: '', label: '演示级分类', type: 'input' },
+					{ prop: 'a8', width: '', label: '颜色是分类', type: 'input' },
+				],
+				option: [
+					{ value: '选项1', label: '黄金糕' },
+					{ value: '选项2', label: '双皮奶' },
+					{ value: '选项3', label: '蚵仔煎' },
+				],
+			},
+		});
+		// 表格验证
+		const onValidate = () => {
+			if (state.tableData.data.length <= 0) return ElMessage.warning('请先点击增加一行');
+			tableRulesRef.value.validate((valid: any) => {
+				if (!valid) return ElMessage.warning('表格项必填未填');
+				ElMessage.success('全部验证通过');
+			});
+		};
+		// 新增一行
+		const onAddRow = () => {
+			state.tableData.data.push({
+				a1: '',
+				a2: '',
+				a3: '',
+				a4: '',
+				a5: '',
+				a6: '',
+				a7: '',
+				a8: '',
+			});
+		};
+		return {
+			onValidate,
+			onAddRow,
+			tableRulesRef,
+			...toRefs(state),
+		};
+	},
+});
+</script>
diff --git a/src/views/pages/tree/index.vue b/src/views/pages/tree/index.vue
new file mode 100644
index 0000000..afcc752
--- /dev/null
+++ b/src/views/pages/tree/index.vue
@@ -0,0 +1,258 @@
+<template>
+	<div class="tree-container">
+		<el-card shadow="hover" header="element plus Tree 树形控件改成表格">
+			<div v-loading="treeLoading">
+				<div class="tree-head">
+					<div class="tree-head-check"><el-checkbox v-model="treeCheckAll" @change="onCheckAllChange"></el-checkbox></div>
+					<div class="tree-head-one">商品 ID</div>
+					<div style="flex: 1; display: flex">
+						<div class="tree-head-two">商品名称</div>
+						<div class="tree-head-three">描述</div>
+					</div>
+				</div>
+				<el-tree :data="treeTableData" show-checkbox node-key="id" ref="treeTable" :props="treeDefaultProps" @check="onCheckTree">
+					<template #default="{ node, data }">
+						<span class="tree-custom-node">
+							<span style="flex: 1">{{ node.label }}</span>
+							<span v-if="data.isShow" style="flex: 1; display: flex">
+								<span type="text" size="default" style="flex: 1">{{ data.label1 }}</span>
+								<span type="text" size="default" style="flex: 1">{{ data.label2 }}</span>
+							</span>
+						</span>
+					</template>
+				</el-tree>
+			</div>
+			<el-button @click="onSelect" class="mt15" size="default" type="primary">
+				<SvgIcon name="iconfont icon-shuxingtu" />
+				选择元素
+			</el-button>
+		</el-card>
+	</div>
+</template>
+
+<script lang="ts">
+import { toRefs, reactive, onBeforeMount, getCurrentInstance, defineComponent } from 'vue';
+import { ElMessage } from 'element-plus';
+
+// 定义接口来定义对象的类型
+interface TreeDataState {
+	id: number;
+	label: string;
+	label1: string;
+	label2: string;
+	isShow: boolean;
+	children?: TreeDataState[];
+}
+interface TreeSate {
+	treeCheckAll: boolean;
+	treeLoading: boolean;
+	treeTableData: TreeDataState[];
+	treeDefaultProps: {
+		children: string;
+		label: string;
+	};
+	treeSelArr: TreeDataState[];
+	treeLength: number;
+}
+
+export default defineComponent({
+	name: 'pagesTree',
+	setup() {
+		const { proxy } = <any>getCurrentInstance();
+		const state = reactive<TreeSate>({
+			treeCheckAll: false,
+			treeLoading: false,
+			treeTableData: [],
+			treeDefaultProps: {
+				children: 'children',
+				label: 'label',
+			},
+			treeSelArr: [],
+			treeLength: 0,
+		});
+		// 初始化树的长度
+		const initTreeLengh = (arr: TreeDataState[]) => {
+			let count = 0;
+			arr.map((item) => {
+				if (item.children) {
+					count += item.children.length;
+				}
+			});
+			state.treeLength = count + arr.length;
+		};
+		// 全选改变时
+		const onCheckAllChange = () => {
+			if (state.treeCheckAll) {
+				proxy.$refs.treeTable.setCheckedNodes(state.treeTableData);
+			} else {
+				proxy.$refs.treeTable.setCheckedKeys([]);
+			}
+		};
+		// 节点选中状态发生变化时的回调
+		const onCheckTree = () => {
+			state.treeSelArr = [];
+			state.treeSelArr = proxy.$refs.treeTable.getCheckedNodes();
+			state.treeSelArr.length == state.treeLength ? (state.treeCheckAll = true) : (state.treeCheckAll = false);
+		};
+		// 选择元素按钮
+		const onSelect = () => {
+			let treeArr = proxy.$refs.treeTable.getCheckedNodes();
+			if (treeArr.length <= 0) {
+				ElMessage.warning('请选择元素');
+				return;
+			} else {
+				// console.log(proxy.$refs.treeTable.getCheckedNodes());
+			}
+		};
+		// 初始化树模拟数据
+		const getTreeData = () => {
+			state.treeTableData = [
+				{
+					id: 1,
+					label: '12987121',
+					label1: '好滋好味鸡蛋仔',
+					label2: '荷兰优质淡奶,奶香浓而不腻',
+					isShow: true,
+					children: [
+						{
+							id: 11,
+							label: '一级 1-1',
+							label1: '好滋好味鸡蛋仔',
+							label2: '荷兰优质淡奶,奶香浓而不腻',
+							isShow: false,
+						},
+						{
+							id: 12,
+							label: '一级 1-2',
+							label1: '好滋好味鸡蛋仔',
+							label2: '荷兰优质淡奶,奶香浓而不腻',
+							isShow: false,
+						},
+					],
+				},
+				{
+					id: 2,
+					label: '12987122',
+					label1: '好滋好味鸡蛋仔',
+					label2: '荷兰优质淡奶,奶香浓而不腻',
+					isShow: true,
+					children: [
+						{
+							id: 21,
+							label: '二级 2-1',
+							label1: '好滋好味鸡蛋仔',
+							label2: '荷兰优质淡奶,奶香浓而不腻',
+							isShow: false,
+						},
+						{
+							id: 22,
+							label: '二级 2-2',
+							label1: '好滋好味鸡蛋仔',
+							label2: '荷兰优质淡奶,奶香浓而不腻',
+							isShow: false,
+						},
+					],
+				},
+				{
+					id: 3,
+					label: '12987123',
+					label1: '好滋好味鸡蛋仔',
+					label2: '荷兰优质淡奶,奶香浓而不腻',
+					isShow: true,
+					children: [
+						{
+							id: 31,
+							label: '二级 3-1',
+							label1: '好滋好味鸡蛋仔',
+							label2: '荷兰优质淡奶,奶香浓而不腻',
+							isShow: false,
+						},
+						{
+							id: 32,
+							label: '二级 3-2',
+							label1: '好滋好味鸡蛋仔',
+							label2: '荷兰优质淡奶,奶香浓而不腻',
+							isShow: false,
+						},
+						{
+							id: 33,
+							label: '二级 3-3',
+							label1: '好滋好味鸡蛋仔',
+							label2: '荷兰优质淡奶,奶香浓而不腻',
+							isShow: false,
+						},
+					],
+				},
+			];
+			initTreeLengh(state.treeTableData);
+		};
+		// 页面加载前
+		onBeforeMount(() => {
+			getTreeData();
+		});
+		return {
+			getTreeData,
+			onCheckAllChange,
+			onCheckTree,
+			onSelect,
+			...toRefs(state),
+		};
+	},
+});
+</script>
+
+<style scoped lang="scss">
+.tree-container {
+	.tree-head {
+		height: 48px;
+		line-height: 48px;
+		border: 1px solid var(--next-border-color-light);
+		border-bottom: none;
+		display: flex;
+		padding-right: 8px;
+		font-weight: bold;
+		color: #909399;
+		.tree-head-check {
+			width: 38px;
+			text-align: right;
+		}
+		.tree-head-one,
+		.tree-head-two,
+		.tree-head-three {
+			flex: 1;
+		}
+		.tree-head-one {
+			padding-left: 8px;
+		}
+	}
+	.el-tree {
+		overflow: hidden;
+		border-bottom: 1px solid var(--next-border-color-light);
+		.tree-custom-node {
+			flex: 1;
+			display: flex;
+			align-items: center;
+			justify-content: space-between;
+			padding-right: 8px;
+			width: 100%;
+		}
+		&::v-deep(.el-tree-node) {
+			border: 1px solid var(--next-border-color-light);
+			border-bottom: none;
+			color: #606266;
+			.el-tree-node__content {
+				line-height: 57px !important;
+				height: 57px !important;
+			}
+			.el-tree-node__children {
+				.el-tree-node {
+					border: none;
+				}
+				.el-tree-node__content {
+					border-top: 1px solid var(--next-border-color-light);
+				}
+			}
+		}
+	}
+}
+</style>
diff --git a/src/views/pages/waterfall/index.vue b/src/views/pages/waterfall/index.vue
new file mode 100644
index 0000000..48b48ea
--- /dev/null
+++ b/src/views/pages/waterfall/index.vue
@@ -0,0 +1,174 @@
+<template>
+	<div class="waterfall-container">
+		<el-card shadow="hover" header="瀑布屏(布局一)" class="mb15">
+			<div class="waterfall-first">
+				<div class="waterfall-first-item" v-for="v in 30" :key="v" v-waves>
+					<div class="w100 h100 flex">
+						<span class="flex-margin">{{ v }}</span>
+					</div>
+				</div>
+			</div>
+		</el-card>
+		<el-card shadow="hover" header="瀑布屏(布局二)">
+			<div class="waterfall-last">
+				<div class="waterfall-last-item" v-for="v in 30" :key="v" v-waves="'light'">
+					<div class="w100 h100 flex">
+						<span class="flex-margin">{{ v }}</span>
+					</div>
+				</div>
+			</div>
+		</el-card>
+	</div>
+</template>
+
+<script lang="ts">
+import { toRefs, reactive, defineComponent } from 'vue';
+
+export default defineComponent({
+	name: 'pagesWaterfall',
+	setup() {
+		const state = reactive({});
+		return {
+			...toRefs(state),
+		};
+	},
+});
+</script>
+
+<style scoped lang="scss">
+.waterfall-container {
+	.waterfall-first {
+		display: grid;
+		grid-template-columns: repeat(auto-fill, minmax(188px, 1fr));
+		grid-gap: 0.25em;
+		grid-auto-flow: row dense;
+		grid-auto-rows: 20px;
+		.waterfall-first-item {
+			width: 100%;
+			background: var(--el-color-primary);
+			color: var(--el-color-white);
+			transition: all 0.3s ease;
+			border-radius: 3px;
+			&:nth-of-type(3n + 1) {
+				grid-row: auto / span 5;
+			}
+			&:nth-of-type(3n + 2) {
+				grid-row: auto / span 6;
+			}
+			&:nth-of-type(3n + 3) {
+				grid-row: auto / span 8;
+			}
+			&:hover {
+				box-shadow: 0 2px 12px 0 rgb(0 0 0 / 10%);
+				transition: all 0.3s ease;
+				cursor: pointer;
+			}
+		}
+	}
+	.waterfall-last {
+		display: grid;
+		grid-gap: 0.25em;
+		grid-auto-flow: row dense;
+		grid-auto-rows: minmax(188px, 20vmin);
+		grid-template-columns: 1fr;
+		.waterfall-last-item {
+			height: 100%;
+			background: var(--el-color-primary);
+			color: var(--el-color-white);
+			transition: all 0.3s ease;
+			border-radius: 3px;
+			&:hover {
+				box-shadow: 0 2px 12px 0 rgb(0 0 0 / 10%);
+				transition: all 0.3s ease;
+				cursor: pointer;
+			}
+		}
+	}
+	@media (min-width: 576px) {
+		.waterfall-last {
+			grid-template-columns: repeat(7, 1fr);
+			.waterfall-last-item {
+				&:nth-of-type(9n + 9) {
+					grid-column: auto / span 2;
+				}
+				&:nth-of-type(9n + 8) {
+					grid-column: auto / span 2;
+				}
+				&:nth-of-type(9n + 7) {
+					grid-column: auto / span 3;
+				}
+				&:nth-of-type(9n + 6) {
+					grid-column: auto / span 2;
+				}
+				&:nth-of-type(9n + 5) {
+					grid-column: auto / span 3;
+				}
+				&:nth-of-type(9n + 4) {
+					grid-column: auto / span 2;
+				}
+				&:nth-of-type(9n + 3) {
+					grid-column: auto / span 3;
+				}
+				&:nth-of-type(9n + 2) {
+					grid-column: auto / span 2;
+				}
+				&:nth-of-type(9n + 1) {
+					grid-column: auto / span 2;
+				}
+			}
+		}
+	}
+	@media (min-width: 576px) and (min-width: 1024px) {
+		.waterfall-last {
+			grid-template-columns: repeat(14, 1fr);
+			.waterfall-last-item {
+				&:nth-of-type(15n + 15) {
+					grid-column: auto / span 3;
+				}
+				&:nth-of-type(15n + 14) {
+					grid-column: auto / span 3;
+				}
+				&:nth-of-type(15n + 13) {
+					grid-column: auto / span 2;
+				}
+				&:nth-of-type(15n + 12) {
+					grid-column: auto / span 3;
+				}
+				&:nth-of-type(15n + 11) {
+					grid-column: auto / span 3;
+				}
+				&:nth-of-type(15n + 10) {
+					grid-column: auto / span 2;
+				}
+				&:nth-of-type(15n + 9) {
+					grid-column: auto / span 3;
+				}
+				&:nth-of-type(15n + 8) {
+					grid-column: auto / span 3;
+				}
+				&:nth-of-type(15n + 7) {
+					grid-column: auto / span 3;
+				}
+				&:nth-of-type(15n + 6) {
+					grid-column: auto / span 3;
+				}
+				&:nth-of-type(15n + 5) {
+					grid-column: auto / span 3;
+				}
+				&:nth-of-type(15n + 4) {
+					grid-column: auto / span 3;
+				}
+				&:nth-of-type(15n + 3) {
+					grid-column: auto / span 3;
+				}
+				&:nth-of-type(15n + 2) {
+					grid-column: auto / span 3;
+				}
+				&:nth-of-type(15n + 1) {
+					grid-column: auto / span 2;
+				}
+			}
+		}
+	}
+}
+</style>
diff --git a/src/views/pages/waves/index.vue b/src/views/pages/waves/index.vue
new file mode 100644
index 0000000..405c71a
--- /dev/null
+++ b/src/views/pages/waves/index.vue
@@ -0,0 +1,134 @@
+<template>
+	<div class="preview-container">
+		<el-card shadow="hover" header="波浪指令效果(v-waves)作用于 btn">
+			<el-row class="mb10" style="color: #808080">可选参数 v-waves=" |light|red|orange|purple|green|teal"</el-row>
+			<div class="flex-warp">
+				<div class="flex-warp-item">
+					<div class="flex-warp-item-box">
+						<el-button size="default" v-waves>
+							<SvgIcon name="iconfont icon-bolangnengshiyanchang" />
+							默认效果
+						</el-button>
+					</div>
+				</div>
+				<div class="flex-warp-item">
+					<div class="flex-warp-item-box">
+						<el-button type="primary" size="default" v-waves="'light'">
+							<SvgIcon name="iconfont icon-bolangnengshiyanchang" />
+							light 效果
+						</el-button>
+					</div>
+				</div>
+				<div class="flex-warp-item">
+					<div class="flex-warp-item-box">
+						<el-button type="success" size="default" v-waves="'red'">
+							<SvgIcon name="iconfont icon-bolangnengshiyanchang" />
+							red 效果
+						</el-button>
+					</div>
+				</div>
+				<div class="flex-warp-item">
+					<div class="flex-warp-item-box">
+						<el-button type="info" size="default" v-waves="'orange'">
+							<SvgIcon name="iconfont icon-bolangnengshiyanchang" />
+							orange 效果
+						</el-button>
+					</div>
+				</div>
+				<div class="flex-warp-item">
+					<div class="flex-warp-item-box">
+						<el-button type="warning" size="default" v-waves="'purple'">
+							<SvgIcon name="iconfont icon-bolangnengshiyanchang" />
+							purple 效果
+						</el-button>
+					</div>
+				</div>
+				<div class="flex-warp-item">
+					<div class="flex-warp-item-box">
+						<el-button type="danger" size="default" v-waves="'green'">
+							<SvgIcon name="iconfont icon-bolangnengshiyanchang" />
+							green 效果
+						</el-button>
+					</div>
+				</div>
+				<div class="flex-warp-item">
+					<div class="flex-warp-item-box">
+						<el-button type="primary" size="default" v-waves="'teal'">
+							<SvgIcon name="iconfont icon-bolangnengshiyanchang" />
+							teal 效果
+						</el-button>
+					</div>
+				</div>
+			</div>
+		</el-card>
+		<el-card shadow="hover" header="波浪指令效果(v-waves)作用于 div" class="mt15">
+			<div class="waterfall-first">
+				<div class="waterfall-first-item" v-for="v in 12" :key="v" v-waves>
+					<div class="w100 h100 flex">
+						<span class="flex-margin">{{ v }}</span>
+					</div>
+				</div>
+			</div>
+		</el-card>
+	</div>
+</template>
+
+<script lang="ts">
+import { toRefs, reactive, defineComponent } from 'vue';
+
+export default defineComponent({
+	name: 'pagesWaves',
+	setup() {
+		const state = reactive({});
+		return {
+			...toRefs(state),
+		};
+	},
+});
+</script>
+
+<style scoped lang="scss">
+.preview-container {
+	.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%;
+			}
+		}
+	}
+	.waterfall-first {
+		display: grid;
+		grid-template-columns: repeat(auto-fill, minmax(188px, 1fr));
+		grid-gap: 0.25em;
+		grid-auto-flow: row dense;
+		grid-auto-rows: 20px;
+		.waterfall-first-item {
+			width: 100%;
+			background: var(--el-color-primary);
+			color: var(--el-color-white);
+			transition: all 0.3s ease;
+			border-radius: 3px;
+			&:nth-of-type(3n + 1) {
+				grid-row: auto / span 5;
+			}
+			&:nth-of-type(3n + 2) {
+				grid-row: auto / span 6;
+			}
+			&:nth-of-type(3n + 3) {
+				grid-row: auto / span 8;
+			}
+			&:hover {
+				box-shadow: 0 2px 12px 0 rgb(0 0 0 / 10%);
+				transition: all 0.3s ease;
+				cursor: pointer;
+			}
+		}
+	}
+}
+</style>
diff --git a/src/views/pages/workflow/component/contextmenu/index.vue b/src/views/pages/workflow/component/contextmenu/index.vue
new file mode 100644
index 0000000..f55e06f
--- /dev/null
+++ b/src/views/pages/workflow/component/contextmenu/index.vue
@@ -0,0 +1,107 @@
+<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="isShow"
+		>
+			<ul class="el-dropdown-menu">
+				<li
+					v-for="(v, k) in dropdownList"
+					class="el-dropdown-menu__item"
+					aria-disabled="false"
+					tabindex="-1"
+					:key="k"
+					@click="onCurrentClick(v.contextMenuClickId)"
+				>
+					<SvgIcon :name="v.icon" />
+					<span>{{ v.txt }}{{ item.type === 'line' ? '线' : '节点' }}</span>
+				</li>
+			</ul>
+			<div class="el-popper__arrow" style="left: 10px"></div>
+		</div>
+	</transition>
+</template>
+
+<script lang="ts">
+import { computed, defineComponent, reactive, toRefs, onMounted, onUnmounted } from 'vue';
+
+export default defineComponent({
+	name: 'pagesWorkflowContextmenu',
+	props: {
+		dropdown: {
+			type: Object,
+		},
+	},
+	setup(props, { emit }) {
+		const state = reactive({
+			isShow: false,
+			dropdownList: [
+				{ contextMenuClickId: 0, txt: '删除', icon: 'ele-Delete' },
+				{ contextMenuClickId: 1, txt: '编辑', icon: 'ele-Edit' },
+			],
+			item: {
+				type: 'node',
+			},
+			conn: {},
+		});
+		// 父级传过来的坐标 x,y 值
+		const dropdowns = computed(() => {
+			return <any>props.dropdown;
+		});
+		// 当前项菜单点击
+		const onCurrentClick = (contextMenuClickId: number) => {
+			emit('current', Object.assign({}, { contextMenuClickId }, state.item), state.conn);
+		};
+		// 打开右键菜单:判断是否固定,固定则不显示关闭按钮
+		const openContextmenu = (item: any, conn = {}) => {
+			state.item = item;
+			state.conn = conn;
+			closeContextmenu();
+			setTimeout(() => {
+				state.isShow = true;
+			}, 10);
+		};
+		// 关闭右键菜单
+		const closeContextmenu = () => {
+			state.isShow = false;
+		};
+		// 监听页面监听进行右键菜单的关闭
+		onMounted(() => {
+			document.body.addEventListener('click', closeContextmenu);
+			document.body.addEventListener('contextmenu', closeContextmenu);
+		});
+		// 页面卸载时,移除右键菜单监听事件
+		onUnmounted(() => {
+			document.body.removeEventListener('click', closeContextmenu);
+			document.body.removeEventListener('contextmenu', closeContextmenu);
+		});
+		return {
+			dropdowns,
+			openContextmenu,
+			closeContextmenu,
+			onCurrentClick,
+			...toRefs(state),
+		};
+	},
+});
+</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>
diff --git a/src/views/pages/workflow/component/drawer/index.vue b/src/views/pages/workflow/component/drawer/index.vue
new file mode 100644
index 0000000..74478a2
--- /dev/null
+++ b/src/views/pages/workflow/component/drawer/index.vue
@@ -0,0 +1,73 @@
+<template>
+	<div>
+		<el-drawer :title="`${nodeData.type === 'line' ? '线' : '节点'}操作`" v-model="isOpen" size="320px">
+			<el-scrollbar>
+				<Line v-if="nodeData.type === 'line'" @change="onLineChange" @close="close" ref="lineRef" />
+				<Node v-else @submit="onNodeSubmit" @close="close" ref="nodeRef" />
+			</el-scrollbar>
+		</el-drawer>
+	</div>
+</template>
+
+<script lang="ts">
+import { defineComponent, reactive, toRefs, ref, nextTick } from 'vue';
+import Line from './line.vue';
+import Node from './node.vue';
+
+// 定义接口来定义对象的类型
+interface WorkflowDrawerState {
+	isOpen: boolean;
+	nodeData: {
+		type: string;
+	};
+	jsplumbConn: any;
+}
+
+export default defineComponent({
+	name: 'pagesWorkflowDrawer',
+	components: { Line, Node },
+	setup(props, { emit }) {
+		const lineRef = ref();
+		const nodeRef = ref();
+		const state = reactive<WorkflowDrawerState>({
+			isOpen: false,
+			nodeData: {
+				type: 'node',
+			},
+			jsplumbConn: {},
+		});
+		// 打开抽屉
+		const open = (item: any, conn: any) => {
+			state.isOpen = true;
+			state.jsplumbConn = conn;
+			state.nodeData = item;
+			nextTick(() => {
+				if (item.type === 'line') lineRef.value.getParentData(item);
+				else nodeRef.value.getParentData(item);
+			});
+		};
+		// 关闭
+		const close = () => {
+			state.isOpen = false;
+		};
+		// 线 label 内容改变时
+		const onLineChange = (label: any) => {
+			state.jsplumbConn.label = label;
+			emit('label', state.jsplumbConn);
+		};
+		// 节点内容改变时
+		const onNodeSubmit = (data: object) => {
+			emit('node', data);
+		};
+		return {
+			lineRef,
+			nodeRef,
+			open,
+			close,
+			onLineChange,
+			onNodeSubmit,
+			...toRefs(state),
+		};
+	},
+});
+</script>
diff --git a/src/views/pages/workflow/component/drawer/line.vue b/src/views/pages/workflow/component/drawer/line.vue
new file mode 100644
index 0000000..a19c92a
--- /dev/null
+++ b/src/views/pages/workflow/component/drawer/line.vue
@@ -0,0 +1,62 @@
+<template>
+	<div class="pt15 pr15 pb15 pl15">
+		<el-form :model="line" size="default" label-width="50px">
+			<el-form-item label="来往">
+				<el-input v-model="line.contact" placeholder="来往" clearable disabled></el-input>
+			</el-form-item>
+			<el-form-item label="类型">
+				<el-input v-model="line.type" placeholder="类型" clearable disabled></el-input>
+			</el-form-item>
+			<el-form-item label="label">
+				<el-input v-model="line.label" placeholder="请输入label内容" clearable></el-input>
+			</el-form-item>
+			<el-form-item>
+				<el-button @click="onLineTextReset">
+					<SvgIcon name="ele-RefreshRight" />
+					重置
+				</el-button>
+				<el-button @click="onLineTextChange" type="primary">
+					<SvgIcon name="ele-Check" />
+					保存
+				</el-button>
+			</el-form-item>
+		</el-form>
+	</div>
+</template>
+
+<script lang="ts">
+import { defineComponent, reactive, toRefs } from 'vue';
+
+// 定义接口来定义对象的类型
+interface WorkflowDrawerLineState {
+	line: any;
+}
+
+export default defineComponent({
+	name: 'pagesWorkflowDrawerLine',
+	setup(props, { emit }) {
+		const state = reactive<WorkflowDrawerLineState>({
+			line: {},
+		});
+		// 获取父组件数据
+		const getParentData = (data: object) => {
+			state.line = data;
+		};
+		// 重置
+		const onLineTextReset = () => {
+			state.line.label = '';
+		};
+		// 保存
+		const onLineTextChange = () => {
+			emit('change', state.line.label);
+			emit('close');
+		};
+		return {
+			getParentData,
+			onLineTextReset,
+			onLineTextChange,
+			...toRefs(state),
+		};
+	},
+});
+</script>
diff --git a/src/views/pages/workflow/component/drawer/node.vue b/src/views/pages/workflow/component/drawer/node.vue
new file mode 100644
index 0000000..b68ccde
--- /dev/null
+++ b/src/views/pages/workflow/component/drawer/node.vue
@@ -0,0 +1,272 @@
+<template>
+	<div class="workflow-drawer-node">
+		<el-tabs type="border-card" v-model="tabsActive">
+			<!-- 节点编辑 -->
+			<el-tab-pane label="节点编辑" name="1">
+				<el-scrollbar>
+					<el-form :model="node" :rules="nodeRules" ref="nodeFormRef" size="default" label-width="80px" class="pt15 pr15 pb15 pl15">
+						<el-form-item label="数据id" prop="id">
+							<el-input v-model="node.id" placeholder="请输入数据id" clearable disabled></el-input>
+						</el-form-item>
+						<el-form-item label="节点id" prop="nodeId">
+							<el-input v-model="node.nodeId" placeholder="请输入节点id" clearable disabled></el-input>
+						</el-form-item>
+						<el-form-item label="类型" prop="type">
+							<el-input v-model="node.type" placeholder="请输入类型" clearable disabled></el-input>
+						</el-form-item>
+						<el-form-item label="left坐标" prop="left">
+							<el-input v-model="node.left" placeholder="请输入left坐标" clearable disabled></el-input>
+						</el-form-item>
+						<el-form-item label="top坐标" prop="top">
+							<el-input v-model="node.top" placeholder="请输入top坐标" clearable disabled></el-input>
+						</el-form-item>
+						<el-form-item label="icon图标" prop="icon">
+							<el-input v-model="node.icon" placeholder="请输入icon图标" clearable></el-input>
+						</el-form-item>
+						<el-form-item label="名称" prop="name">
+							<el-input v-model="node.name" placeholder="请输入名称" clearable></el-input>
+						</el-form-item>
+						<el-form-item>
+							<el-button class="mb15" @click="onNodeRefresh">
+								<SvgIcon name="ele-RefreshRight" />
+								重置
+							</el-button>
+							<el-button type="primary" class="mb15" @click="onNodeSubmit">
+								<SvgIcon name="ele-Check" />
+								保存
+							</el-button>
+						</el-form-item>
+					</el-form>
+				</el-scrollbar>
+			</el-tab-pane>
+
+			<!-- 扩展表单 -->
+			<el-tab-pane label="扩展表单" name="2">
+				<el-scrollbar>
+					<el-form :model="form" ref="extendFormRef" size="default" label-width="80px" class="pt15 pr15 pb15 pl15">
+						<el-form-item
+							:label="val.label"
+							:prop="val.prop"
+							v-for="(val, key) in node.from"
+							:key="key"
+							:rules="[{ required: val.required, message: `${val.label}不能为空`, trigger: 'blur' }]"
+						>
+							<el-input
+								v-model="form[val.prop]"
+								:placeholder="val.placeholder"
+								clearable
+								v-if="val.type === 'input'"
+								:disabled="val.disabled"
+							></el-input>
+							<el-select v-model="form[val.prop]" placeholder="请选择" v-if="val.type === 'select'" clearable :disabled="val.disabled">
+								<el-option v-for="item in val.options" :key="item.value" :label="item.label" :value="item.value"> </el-option>
+							</el-select>
+							<el-checkbox-group v-model="form[val.prop]" v-if="val.type === 'checkbox'" :disabled="val.disabled">
+								<el-checkbox label="美食推荐" name="type"></el-checkbox>
+								<el-checkbox label="统计分析" name="type"></el-checkbox>
+							</el-checkbox-group>
+						</el-form-item>
+						<el-form-item>
+							<el-button class="mb15" @click="onExtendRefresh">
+								<SvgIcon name="ele-RefreshRight" />
+								重置
+							</el-button>
+							<el-button type="primary" class="mb15" @click="onExtendSubmit" :loading="loading.extend">
+								<SvgIcon name="ele-Check" />
+								保存
+							</el-button>
+						</el-form-item>
+					</el-form>
+				</el-scrollbar>
+			</el-tab-pane>
+
+			<!-- 图表可视化 -->
+			<el-tab-pane label="图表可视化" name="3">
+				<el-scrollbar>
+					<div class="flex-content-right">
+						<div style="height: 200px; width: 320px" ref="chartsMonitorRef"></div>
+					</div>
+				</el-scrollbar>
+			</el-tab-pane>
+		</el-tabs>
+	</div>
+</template>
+
+<script lang="ts">
+import { defineComponent, reactive, toRefs, ref, nextTick, getCurrentInstance } from 'vue';
+import { ElMessage } from 'element-plus';
+import * as echarts from 'echarts';
+
+// 定义接口来定义对象的类型
+interface WorkflowDrawerNodeState {
+	node: { [key: string]: any };
+	nodeRules: any;
+	form: any;
+	tabsActive: string;
+	loading: {
+		extend: boolean;
+	};
+}
+
+export default defineComponent({
+	name: 'pagesWorkflowDrawerNode',
+	setup(props, { emit }) {
+		const { proxy } = <any>getCurrentInstance();
+		const nodeFormRef = ref();
+		const extendFormRef = ref();
+		const chartsMonitorRef = ref();
+		const state = reactive<WorkflowDrawerNodeState>({
+			node: {},
+			nodeRules: {
+				id: [{ required: true, message: '请输入数据id', trigger: 'blur' }],
+				nodeId: [{ required: true, message: '请输入节点id', trigger: 'blur' }],
+				type: [{ required: true, message: '请输入类型', trigger: 'blur' }],
+				left: [{ required: true, message: '请输入left坐标', trigger: 'blur' }],
+				top: [{ required: true, message: '请输入top坐标', trigger: 'blur' }],
+				icon: [{ required: true, message: '请输入icon图标', trigger: 'blur' }],
+				name: [{ required: true, message: '请输入名称', trigger: 'blur' }],
+			},
+			form: {
+				module: [],
+			},
+			tabsActive: '1',
+			loading: {
+				extend: false,
+			},
+		});
+		// 获取父组件数据
+		const getParentData = (data: object) => {
+			state.tabsActive = '1';
+			state.node = data;
+			initChartsMonitor();
+		};
+		// 节点编辑-重置
+		const onNodeRefresh = () => {
+			state.node.icon = '';
+			state.node.name = '';
+		};
+		// 节点编辑-保存
+		const onNodeSubmit = () => {
+			nodeFormRef.value.validate((valid: boolean) => {
+				if (valid) {
+					emit('submit', state.node);
+					emit('close');
+				} else {
+					return false;
+				}
+			});
+		};
+		// 扩展表单-重置
+		const onExtendRefresh = () => {
+			extendFormRef.value.resetFields();
+		};
+		// 扩展表单-保存
+		const onExtendSubmit = () => {
+			extendFormRef.value.validate((valid: boolean) => {
+				if (valid) {
+					state.loading.extend = true;
+					setTimeout(() => {
+						state.loading.extend = false;
+						ElMessage.success('保存成功');
+						emit('close');
+					}, 1000);
+				} else {
+					return false;
+				}
+			});
+		};
+		// 图表可视化-初始化
+		const initChartsMonitor = () => {
+			const myChart = echarts.init(proxy.$refs.chartsMonitorRef);
+			const numsOne = [];
+			const numsTwo = [];
+			for (let i = 0; i < 7; i++) {
+				numsOne.push(`${Math.floor(Math.random() * 52 + 10)}:${Math.floor(Math.random() * 52 + 1)}`);
+				numsTwo.push(Math.floor(Math.random() * 52 + 1));
+			}
+			const option = {
+				grid: {
+					top: 50,
+					right: 30,
+					bottom: 30,
+					left: 50,
+				},
+				tooltip: {
+					trigger: 'axis',
+				},
+				xAxis: {
+					type: 'category',
+					boundaryGap: false,
+					data: numsOne,
+				},
+				yAxis: {
+					type: 'value',
+				},
+				series: [
+					{
+						itemStyle: {
+							color: '#289df5',
+							borderColor: '#289df5',
+							areaStyle: {
+								type: 'default',
+								opacity: 0.1,
+							},
+						},
+						data: numsTwo,
+						type: 'line',
+						areaStyle: {},
+					},
+				],
+			};
+			myChart.setOption(option);
+			nextTick(() => {
+				myChart.resize();
+			});
+		};
+		return {
+			nodeFormRef,
+			extendFormRef,
+			chartsMonitorRef,
+			getParentData,
+			onNodeRefresh,
+			onNodeSubmit,
+			onExtendRefresh,
+			onExtendSubmit,
+			...toRefs(state),
+		};
+	},
+});
+</script>
+
+<style scoped lang="scss">
+.workflow-drawer-node {
+	::v-deep {
+		.el-tabs {
+			box-shadow: unset;
+			border: unset;
+			.el-tabs__nav {
+				display: flex;
+				width: 100%;
+				.el-tabs__item {
+					flex: 1;
+					padding: unset;
+					text-align: center;
+					&:first-of-type.is-active {
+						border-left-color: transparent;
+					}
+					&:last-of-type.is-active {
+						border-right-color: transparent;
+					}
+				}
+			}
+			.el-tabs__content {
+				padding: 0;
+				height: calc(100vh - 90px);
+				.el-tab-pane {
+					height: 100%;
+				}
+			}
+		}
+	}
+}
+</style>
diff --git a/src/views/pages/workflow/component/tool/help.vue b/src/views/pages/workflow/component/tool/help.vue
new file mode 100644
index 0000000..09198b5
--- /dev/null
+++ b/src/views/pages/workflow/component/tool/help.vue
@@ -0,0 +1,39 @@
+<template>
+	<div class="workflow-tool-help">
+		<el-dialog v-model="isShow" width="769px">
+			<template #header>
+				<div v-drag="['.workflow-tool-help .el-dialog', '.workflow-tool-help .el-dialog__header']">使用帮助</div>
+			</template>
+			<div>1、拖入:鼠标移入左侧导航中,鼠标形状改变时拖动到右侧网格状的视图中。</div>
+			<div class="mt10">2、移动:鼠标移入到视图中的某个节点元素,鼠标形状改变时拖动改变位置。</div>
+			<div class="mt10">3、连线:鼠标移入到视图中的某个节点元素的icon(图标),鼠标形状改变(变成"+"),按下鼠标左键进行拖线连接。</div>
+			<div class="mt10">4、节点:鼠标移入到视图中的某个节点元素,点击鼠标右键可进行删除、编辑节点。</div>
+			<div class="mt10 mb10">5、线条:鼠标移入到视图中的某个线条,线条颜色改变时,点击鼠标右键可进行删除、编辑线条。</div>
+		</el-dialog>
+	</div>
+</template>
+
+<script lang="ts">
+import { defineComponent, reactive, toRefs } from 'vue';
+export default defineComponent({
+	name: 'pagesWorkflowToolHelp',
+	setup() {
+		const state = reactive({
+			isShow: false,
+		});
+		// 打开弹窗
+		const open = () => {
+			state.isShow = true;
+		};
+		// 关闭弹窗
+		const close = () => {
+			state.isShow = false;
+		};
+		return {
+			open,
+			close,
+			...toRefs(state),
+		};
+	},
+});
+</script>
diff --git a/src/views/pages/workflow/component/tool/index.vue b/src/views/pages/workflow/component/tool/index.vue
new file mode 100644
index 0000000..2cb9940
--- /dev/null
+++ b/src/views/pages/workflow/component/tool/index.vue
@@ -0,0 +1,79 @@
+<template>
+	<div class="workflow-tool">
+		<div class="pl15">{{ setToolTitle }}</div>
+		<div class="workflow-tool-right">
+			<div class="workflow-tool-icon" v-for="(v, k) in toolList" :key="k" :title="v.title" @click="onToolClick(v.fnName)">
+				<SvgIcon :name="v.icon" />
+			</div>
+		</div>
+	</div>
+</template>
+
+<script lang="ts">
+import { defineComponent, computed, reactive, toRefs } from 'vue';
+import { storeToRefs } from 'pinia';
+import { useThemeConfig } from '/@/stores/themeConfig';
+
+export default defineComponent({
+	name: 'pagesWorkflowTool',
+	setup(props, { emit }) {
+		const storesThemeConfig = useThemeConfig();
+		const { themeConfig } = storeToRefs(storesThemeConfig);
+		const state = reactive({
+			toolList: [
+				{ icon: 'ele-Help', title: '帮助', fnName: 'help' },
+				{ icon: 'ele-Download', title: '下载', fnName: 'download' },
+				{ icon: 'ele-Check', title: '提交', fnName: 'submit' },
+				{ icon: 'ele-DocumentCopy', title: '复制', fnName: 'copy' },
+				{ icon: 'ele-Delete', title: '删除', fnName: 'del' },
+				{ icon: 'ele-FullScreen', title: '全屏', fnName: 'fullscreen' },
+			],
+		});
+		// 设置 tool 标题
+		const setToolTitle = computed(() => {
+			let { globalTitle } = themeConfig.value;
+			return `${globalTitle}工作流`;
+		});
+		// 顶部工具栏
+		const onToolClick = (fnName: string) => {
+			emit('tool', fnName);
+		};
+		return {
+			setToolTitle,
+			onToolClick,
+			...toRefs(state),
+		};
+	},
+});
+</script>
+
+<style scoped lang="scss">
+.workflow-tool {
+	height: 35px;
+	display: flex;
+	align-items: center;
+	border-bottom: 1px solid var(--el-border-color-light, #ebeef5);
+	color: var(--el-text-color-primary);
+	.workflow-tool-right {
+		flex: 1;
+		display: flex;
+		justify-content: flex-end;
+	}
+	&-icon {
+		padding: 0 10px;
+		cursor: pointer;
+		color: var(--next-bg-topBarColor);
+		height: 35px;
+		line-height: 35px;
+		display: flex;
+		align-items: center;
+		&:hover {
+			background: rgba(0, 0, 0, 0.04);
+			i {
+				display: inline-block;
+				animation: logoAnimation 0.3s ease-in-out;
+			}
+		}
+	}
+}
+</style>
diff --git a/src/views/pages/workflow/index.vue b/src/views/pages/workflow/index.vue
new file mode 100644
index 0000000..a84e496
--- /dev/null
+++ b/src/views/pages/workflow/index.vue
@@ -0,0 +1,693 @@
+<template>
+	<div class="workflow-container">
+		<div class="workflow-mask" v-if="isShow"></div>
+		<div class="layout-view-bg-white flex" :style="{ height: `calc(100vh - ${setViewHeight}` }">
+			<div class="workflow">
+				<!-- 顶部工具栏 -->
+				<Tool @tool="onToolClick" />
+
+				<!-- 左侧导航区 -->
+				<div class="workflow-content">
+					<div class="workflow-left">
+						<el-scrollbar>
+							<div
+								ref="leftNavRefs"
+								v-for="(val, key) in leftNavList"
+								:key="val.id"
+								:style="{ height: val.isOpen ? 'auto' : '50px', overflow: 'hidden' }"
+								class="workflow-left-id"
+							>
+								<div class="workflow-left-title" @click="onTitleClick(val)">
+									<span>{{ val.title }}</span>
+									<SvgIcon :name="val.isOpen ? 'ele-ArrowDown' : 'ele-ArrowRight'" />
+								</div>
+								<div class="workflow-left-item" v-for="(v, k) in val.children" :key="k" :data-name="v.name" :data-icon="v.icon" :data-id="v.id">
+									<div class="workflow-left-item-icon">
+										<SvgIcon :name="v.icon" class="workflow-icon-drag" />
+										<div class="font10 pl5 name">{{ v.name }}</div>
+									</div>
+								</div>
+							</div>
+						</el-scrollbar>
+					</div>
+
+					<!-- 右侧绘画区 -->
+					<div class="workflow-right" ref="workflowRightRef">
+						<div
+							v-for="(v, k) in jsplumbData.nodeList"
+							:key="v.nodeId"
+							:id="v.nodeId"
+							:data-node-id="v.nodeId"
+							:class="v.class"
+							:style="{ left: v.left, top: v.top }"
+							@click="onItemCloneClick(k)"
+							@contextmenu.prevent="onContextmenu(v, k, $event)"
+						>
+							<div class="workflow-right-box" :class="{ 'workflow-right-active': jsPlumbNodeIndex === k }">
+								<div class="workflow-left-item-icon">
+									<SvgIcon :name="v.icon" class="workflow-icon-drag" />
+									<div class="font10 pl5 name">{{ v.name }}</div>
+								</div>
+							</div>
+						</div>
+					</div>
+				</div>
+			</div>
+		</div>
+
+		<!-- 节点右键菜单 -->
+		<Contextmenu :dropdown="dropdownNode" ref="contextmenuNodeRef" @current="onCurrentNodeClick" />
+		<!-- 线右键菜单 -->
+		<Contextmenu :dropdown="dropdownLine" ref="contextmenuLineRef" @current="onCurrentLineClick" />
+		<!-- 抽屉表单、线 -->
+		<Drawer ref="drawerRef" @label="setLineLabel" @node="setNodeContent" />
+
+		<!-- 顶部工具栏-帮助弹窗 -->
+		<Help ref="helpRef" />
+	</div>
+</template>
+
+<script lang="ts">
+import { defineComponent, toRefs, reactive, computed, onMounted, onUnmounted, nextTick, ref } from 'vue';
+import { ElMessage, ElMessageBox } from 'element-plus';
+import { jsPlumb } from 'jsplumb';
+import Sortable from 'sortablejs';
+import { storeToRefs } from 'pinia';
+import { useThemeConfig } from '/@/stores/themeConfig';
+import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
+import Tool from './component/tool/index.vue';
+import Help from './component/tool/help.vue';
+import Contextmenu from './component/contextmenu/index.vue';
+import Drawer from './component/drawer/index.vue';
+import commonFunction from '/@/utils/commonFunction';
+import { leftNavList } from './js/mock';
+import { jsplumbDefaults, jsplumbMakeSource, jsplumbMakeTarget, jsplumbConnect } from './js/config';
+
+// 定义接口来定义对象的类型
+interface NodeListState {
+	id: string | number;
+	nodeId: string | undefined;
+	class: HTMLElement | string;
+	left: number | string;
+	top: number | string;
+	icon: string;
+	name: string;
+}
+interface LineListState {
+	sourceId: string;
+	targetId: string;
+	label: string;
+}
+interface XyState {
+	x: string | number;
+	y: string | number;
+}
+interface WorkflowState {
+	workflowRightRef: HTMLDivElement | null;
+	leftNavRefs: any[];
+	leftNavList: any[];
+	dropdownNode: XyState;
+	dropdownLine: XyState;
+	isShow: boolean;
+	jsPlumb: any;
+	jsPlumbNodeIndex: null | number;
+	jsplumbDefaults: any;
+	jsplumbMakeSource: any;
+	jsplumbMakeTarget: any;
+	jsplumbConnect: any;
+	jsplumbData: {
+		nodeList: Array<NodeListState>;
+		lineList: Array<LineListState>;
+	};
+}
+
+export default defineComponent({
+	name: 'pagesWorkflow',
+	components: { Tool, Contextmenu, Drawer, Help },
+	setup() {
+		const contextmenuNodeRef = ref();
+		const contextmenuLineRef = ref();
+		const drawerRef = ref();
+		const helpRef = ref();
+		const stores = useTagsViewRoutes();
+		const storesThemeConfig = useThemeConfig();
+		const { themeConfig } = storeToRefs(storesThemeConfig);
+		const { isTagsViewCurrenFull } = storeToRefs(stores);
+		const { copyText } = commonFunction();
+		const state = reactive<WorkflowState>({
+			workflowRightRef: null as HTMLDivElement | null,
+			leftNavRefs: [],
+			leftNavList: [],
+			dropdownNode: { x: '', y: '' },
+			dropdownLine: { x: '', y: '' },
+			isShow: false,
+			jsPlumb: null,
+			jsPlumbNodeIndex: null,
+			jsplumbDefaults,
+			jsplumbMakeSource,
+			jsplumbMakeTarget,
+			jsplumbConnect,
+			jsplumbData: {
+				nodeList: [],
+				lineList: [],
+			},
+		});
+		// 设置 view 的高度
+		const setViewHeight = computed(() => {
+			let { isTagsview } = themeConfig.value;
+			if (isTagsViewCurrenFull.value) {
+				return `30px`;
+			} else {
+				if (isTagsview) return `114px`;
+				else return `80px`;
+			}
+		});
+		// 设置 宽度小于 768,不支持操
+		const setClientWidth = () => {
+			const clientWidth = document.body.clientWidth;
+			clientWidth < 768 ? (state.isShow = true) : (state.isShow = false);
+		};
+		// 左侧导航-数据初始化
+		const initLeftNavList = () => {
+			state.leftNavList = leftNavList;
+			state.jsplumbData = {
+				nodeList: [
+					{ nodeId: 'huej738hbji', left: '148px', top: '93px', class: 'workflow-right-clone', icon: 'iconfont icon-gongju', name: '引擎', id: '11' },
+					{
+						nodeId: '52kcszzyxrd',
+						left: '458px',
+						top: '203px',
+						class: 'workflow-right-clone',
+						icon: 'iconfont icon-shouye_dongtaihui',
+						name: '模版',
+						id: '12',
+					},
+					{
+						nodeId: 'nltskl6k4me',
+						left: '164px',
+						top: '350px',
+						class: 'workflow-right-clone',
+						icon: 'iconfont icon-zhongduancanshuchaxun',
+						name: '名称',
+						id: '13',
+					},
+				],
+				lineList: [
+					{ sourceId: 'huej738hbji', targetId: '52kcszzyxrd', label: '传送' },
+					{ sourceId: 'huej738hbji', targetId: 'nltskl6k4me', label: '' },
+				],
+			};
+		};
+		// 左侧导航-初始化拖动
+		const initSortable = () => {
+			state.leftNavRefs.forEach(v => {
+				Sortable.create(v as HTMLDivElement, {
+					group: {
+						name: 'vue-next-admin-1',
+						pull: 'clone',
+						put: false,
+					},
+					animation: 0,
+					sort: false,
+					draggable: '.workflow-left-item',
+					forceFallback: true,
+					onEnd: function (evt: any) {
+						const { name, icon, id } = evt.clone.dataset;
+						const { layerX, layerY, clientX, clientY } = evt.originalEvent;
+						const el = state.workflowRightRef!;
+						const { x, y, width, height } = el.getBoundingClientRect();
+						if (clientX < x || clientX > width + x || clientY < y || y > y + height) {
+							ElMessage.warning('请把节点拖入到画布中');
+						} else {
+							// 节点id(唯一)
+							const nodeId = Math.random().toString(36).substr(2, 12);
+							// 处理节点数据
+							const node = {
+								nodeId,
+								left: `${layerX - 40}px`,
+								top: `${layerY - 15}px`,
+								class: 'workflow-right-clone',
+								name,
+								icon,
+								id,
+							};
+							// 右侧视图内容数组
+							state.jsplumbData.nodeList.push(node);
+							// 元素加载完毕时
+							nextTick(() => {
+								// 整个节点作为source或者target
+								state.jsPlumb.makeSource(nodeId, state.jsplumbMakeSource);
+								// // 整个节点作为source或者target
+								state.jsPlumb.makeTarget(nodeId, state.jsplumbMakeTarget, jsplumbConnect);
+								// 设置节点可以拖拽(此处为id值,非class)
+								state.jsPlumb.draggable(nodeId, {
+									containment: 'parent',
+									stop: (el: any) => {
+										state.jsplumbData.nodeList.forEach((v) => {
+											if (v.nodeId === el.el.id) {
+												// 节点x, y重新赋值,防止再次从左侧导航中拖拽节点时,x, y恢复默认
+												v.left = `${el.pos[0]}px`;
+												v.top = `${el.pos[1]}px`;
+											}
+										});
+									},
+								});
+							});
+						}
+					},
+				});
+			});
+		};
+		// 初始化 jsPlumb
+		const initJsPlumb = () => {
+			(<any>jsPlumb).ready(() => {
+				state.jsPlumb = (<any>jsPlumb).getInstance({
+					detachable: false,
+					Container: 'workflow-right',
+				});
+				state.jsPlumb.fire('jsPlumbDemoLoaded', state.jsPlumb);
+				// 导入默认配置
+				state.jsPlumb.importDefaults(state.jsplumbDefaults);
+				// 会使整个jsPlumb立即重绘。
+				state.jsPlumb.setSuspendDrawing(false, true);
+				// 初始化节点、线的链接
+				initJsPlumbConnection();
+				// 点击线弹出右键菜单
+				state.jsPlumb.bind('contextmenu', (conn: any, originalEvent: MouseEvent) => {
+					originalEvent.preventDefault();
+					const { sourceId, targetId } = conn;
+					const { clientX, clientY } = originalEvent;
+					state.dropdownLine.x = clientX;
+					state.dropdownLine.y = clientY;
+					const v: any = state.jsplumbData.nodeList.find((v) => v.nodeId === targetId);
+					const line: any = state.jsplumbData.lineList.find((v) => v.sourceId === sourceId && v.targetId === targetId);
+					v.type = 'line';
+					v.label = line.label;
+					contextmenuLineRef.value.openContextmenu(v, conn);
+				});
+				// 连线之前
+				state.jsPlumb.bind('beforeDrop', (conn: any) => {
+					const { sourceId, targetId } = conn;
+					const item = state.jsplumbData.lineList.find((v) => v.sourceId === sourceId && v.targetId === targetId);
+					if (item) {
+						ElMessage.warning('关系已存在,不可重复连接');
+						return false;
+					} else {
+						return true;
+					}
+				});
+				// 连线时
+				state.jsPlumb.bind('connection', (conn: any) => {
+					const { sourceId, targetId } = conn;
+					state.jsplumbData.lineList.push({
+						sourceId,
+						targetId,
+						label: '',
+					});
+				});
+				// 删除连线时回调函数
+				state.jsPlumb.bind('connectionDetached', (conn: any) => {
+					const { sourceId, targetId } = conn;
+					state.jsplumbData.lineList = state.jsplumbData.lineList.filter((line) => {
+						if (line.sourceId == sourceId && line.targetId == targetId) {
+							return false;
+						}
+						return true;
+					});
+				});
+			});
+		};
+		// 初始化节点、线的链接
+		const initJsPlumbConnection = () => {
+			// 节点
+			state.jsplumbData.nodeList.forEach((v) => {
+				// 整个节点作为source或者target
+				state.jsPlumb.makeSource(v.nodeId, state.jsplumbMakeSource);
+				// 整个节点作为source或者target
+				state.jsPlumb.makeTarget(v.nodeId, state.jsplumbMakeTarget, jsplumbConnect);
+				// 设置节点可以拖拽(此处为id值,非class)
+				state.jsPlumb.draggable(v.nodeId, {
+					containment: 'parent',
+					stop: (el: any) => {
+						state.jsplumbData.nodeList.forEach((v) => {
+							if (v.nodeId === el.el.id) {
+								// 节点x, y重新赋值,防止再次从左侧导航中拖拽节点时,x, y恢复默认
+								v.left = `${el.pos[0]}px`;
+								v.top = `${el.pos[1]}px`;
+							}
+						});
+					},
+				});
+			});
+			// 线
+			state.jsplumbData.lineList.forEach((v) => {
+				state.jsPlumb.connect(
+					{
+						source: v.sourceId,
+						target: v.targetId,
+						label: v.label,
+					},
+					state.jsplumbConnect
+				);
+			});
+		};
+		// 左侧导航-菜单标题点击
+		const onTitleClick = (val: any) => {
+			val.isOpen = !val.isOpen;
+		};
+		// 右侧内容区-当前项点击
+		const onItemCloneClick = (k: number) => {
+			state.jsPlumbNodeIndex = k;
+		};
+		// 右侧内容区-当前项右键菜单点击
+		const onContextmenu = (v: any, k: number, e: MouseEvent) => {
+			state.jsPlumbNodeIndex = k;
+			const { clientX, clientY } = e;
+			state.dropdownNode.x = clientX;
+			state.dropdownNode.y = clientY;
+			v.type = 'node';
+			v.label = '';
+			let item: any = {};
+			state.leftNavList.forEach((l) => {
+				if (l.children) if (l.children.find((c: any) => c.id === v.id)) item = l.children.find((c: any) => c.id === v.id);
+			});
+			v.from = item.form;
+			contextmenuNodeRef.value.openContextmenu(v);
+		};
+		// 右侧内容区-当前项右键菜单点击回调(节点)
+		const onCurrentNodeClick = (item: any) => {
+			const { contextMenuClickId, nodeId } = item;
+			if (contextMenuClickId === 0) {
+				const nodeIndex = state.jsplumbData.nodeList.findIndex((item) => item.nodeId === nodeId);
+				state.jsplumbData.nodeList.splice(nodeIndex, 1);
+				state.jsPlumb.removeAllEndpoints(nodeId);
+				state.jsPlumbNodeIndex = null;
+			} else if (contextMenuClickId === 1) {
+				drawerRef.value.open(item);
+			}
+		};
+		// 右侧内容区-当前项右键菜单点击回调(线)
+		const onCurrentLineClick = (item: any, conn: any) => {
+			const { contextMenuClickId } = item;
+			const { endpoints } = conn;
+			const intercourse: any = [];
+			endpoints.forEach((v: any) => {
+				intercourse.push({
+					id: v.element.id,
+					innerText: v.element.innerText,
+				});
+			});
+			item.contact = `${intercourse[0].innerText}(${intercourse[0].id}) => ${intercourse[1].innerText}(${intercourse[1].id})`;
+			if (contextMenuClickId === 0) state.jsPlumb.deleteConnection(conn);
+			else if (contextMenuClickId === 1) drawerRef.value.open(item, conn);
+		};
+		// 设置线的 label
+		const setLineLabel = (obj: any) => {
+			const { sourceId, targetId, label } = obj;
+			const conn = state.jsPlumb.getConnections({
+				source: sourceId,
+				target: targetId,
+			})[0];
+			conn.setLabel(label);
+			if (!label || label === '') {
+				conn.addClass('workflow-right-empty-label');
+			} else {
+				conn.removeClass('workflow-right-empty-label');
+				conn.addClass('workflow-right-label');
+			}
+			state.jsplumbData.lineList.forEach((v) => {
+				if (v.sourceId === sourceId && v.targetId === targetId) v.label = label;
+			});
+		};
+		// 设置节点内容
+		const setNodeContent = (obj: any) => {
+			const { nodeId, name, icon } = obj;
+			// 设置节点 name 与 icon
+			state.jsplumbData.nodeList.forEach((v) => {
+				if (v.nodeId === nodeId) {
+					v.name = name;
+					v.icon = icon;
+				}
+			});
+			// 重绘
+			nextTick(() => {
+				state.jsPlumb.setSuspendDrawing(false, true);
+			});
+		};
+		// 顶部工具栏-当前项点击
+		const onToolClick = (fnName: String) => {
+			switch (fnName) {
+				case 'help':
+					onToolHelp();
+					break;
+				case 'download':
+					onToolDownload();
+					break;
+				case 'submit':
+					onToolSubmit();
+					break;
+				case 'copy':
+					onToolCopy();
+					break;
+				case 'del':
+					onToolDel();
+					break;
+				case 'fullscreen':
+					onToolFullscreen();
+					break;
+			}
+		};
+		// 顶部工具栏-帮助
+		const onToolHelp = () => {
+			nextTick(() => {
+				helpRef.value.open();
+			});
+		};
+		// 顶部工具栏-下载
+		const onToolDownload = () => {
+			const { globalTitle } = themeConfig.value;
+			const href = 'data:text/json;charset=utf-8,' + encodeURIComponent(JSON.stringify(state.jsplumbData, null, '\t'));
+			const aLink = document.createElement('a');
+			aLink.setAttribute('href', href);
+			aLink.setAttribute('download', `${globalTitle}工作流.json`);
+			aLink.click();
+			aLink.remove();
+			ElMessage.success('下载成功');
+		};
+		// 顶部工具栏-提交
+		const onToolSubmit = () => {
+			// console.log(state.jsplumbData);
+			ElMessage.success('数据提交成功');
+		};
+		// 顶部工具栏-复制
+		const onToolCopy = () => {
+			copyText(JSON.stringify(state.jsplumbData));
+		};
+		// 顶部工具栏-删除
+		const onToolDel = () => {
+			ElMessageBox.confirm('此操作将清空画布,是否继续?', '提示', {
+				confirmButtonText: '清空',
+				cancelButtonText: '取消',
+			})
+				.then(() => {
+					state.jsplumbData.nodeList.forEach((v) => {
+						state.jsPlumb.removeAllEndpoints(v.nodeId);
+					});
+					nextTick(() => {
+						state.jsplumbData = {
+							nodeList: [],
+							lineList: [],
+						};
+						ElMessage.success('清空画布成功');
+					});
+				})
+				.catch(() => {});
+		};
+		// 顶部工具栏-全屏
+		const onToolFullscreen = () => {
+			stores.setCurrenFullscreen(true);
+		};
+		// 页面加载时
+		onMounted(async () => {
+			await initLeftNavList();
+			initSortable();
+			initJsPlumb();
+			setClientWidth();
+			window.addEventListener('resize', setClientWidth);
+		});
+		// 页面卸载时
+		onUnmounted(() => {
+			window.removeEventListener('resize', setClientWidth);
+		});
+		return {
+			setViewHeight,
+			setClientWidth,
+			setLineLabel,
+			setNodeContent,
+			onTitleClick,
+			onItemCloneClick,
+			onContextmenu,
+			onCurrentNodeClick,
+			onCurrentLineClick,
+			contextmenuNodeRef,
+			contextmenuLineRef,
+			drawerRef,
+			helpRef,
+			onToolClick,
+			...toRefs(state),
+		};
+	},
+});
+</script>
+
+<style scoped lang="scss">
+.workflow-container {
+	position: relative;
+	.workflow {
+		display: flex;
+		height: 100%;
+		width: 100%;
+		flex-direction: column;
+		.workflow-content {
+			display: flex;
+			height: calc(100% - 35px);
+			.workflow-left {
+				width: 220px;
+				height: 100%;
+				border-right: 1px solid var(--el-border-color-light, #ebeef5);
+				::v-deep(.el-collapse-item__content) {
+					padding-bottom: 0;
+				}
+				.workflow-left-title {
+					height: 50px;
+					display: flex;
+					align-items: center;
+					padding: 0 10px;
+					border-top: 1px solid var(--el-border-color-light, #ebeef5);
+					color: var(--el-text-color-primary);
+					cursor: default;
+					span {
+						flex: 1;
+					}
+				}
+				.workflow-left-item {
+					display: inline-block;
+					width: calc(50% - 15px);
+					position: relative;
+					cursor: move;
+					margin: 0 0 10px 10px;
+					.workflow-left-item-icon {
+						height: 35px;
+						display: flex;
+						align-items: center;
+						transition: all 0.3s ease;
+						padding: 5px 10px;
+						border: 1px dashed transparent;
+						background: var(--next-bg-color);
+						border-radius: 3px;
+						i,
+						.name {
+							color: var(--el-text-color-secondary);
+							transition: all 0.3s ease;
+							white-space: nowrap;
+							text-overflow: ellipsis;
+							overflow: hidden;
+						}
+						&:hover {
+							transition: all 0.3s ease;
+							border: 1px dashed var(--el-color-primary);
+							background: var(--el-color-primary-light-9);
+							border-radius: 5px;
+							i,
+							.name {
+								transition: all 0.3s ease;
+								color: var(--el-color-primary);
+							}
+						}
+					}
+				}
+				& .workflow-left-id:first-of-type {
+					.workflow-left-title {
+						border-top: none;
+					}
+				}
+			}
+			.workflow-right {
+				flex: 1;
+				position: relative;
+				overflow: hidden;
+				height: 100%;
+				background-image: linear-gradient(90deg, rgb(156 214 255 / 15%) 10%, rgba(0, 0, 0, 0) 10%),
+					linear-gradient(rgb(156 214 255 / 15%) 10%, rgba(0, 0, 0, 0) 10%);
+				background-size: 10px 10px;
+				.workflow-right-clone {
+					position: absolute;
+					.workflow-right-box {
+						height: 35px;
+						align-items: center;
+						color: var(--el-text-color-secondary);
+						padding: 0 10px;
+						border-radius: 3px;
+						cursor: move;
+						transition: all 0.3s ease;
+						min-width: 94.5px;
+						background: var(--el-color-white);
+						border: 1px solid var(--el-border-color-light, #ebeef5);
+						.workflow-left-item-icon {
+							display: flex;
+							align-items: center;
+							height: 35px;
+						}
+						&:hover {
+							border: 1px dashed var(--el-color-primary);
+							background: var(--el-color-primary-light-9);
+							transition: all 0.3s ease;
+							color: var(--el-color-primary);
+							i {
+								cursor: Crosshair;
+							}
+						}
+					}
+					.workflow-right-active {
+						border: 1px dashed var(--el-color-primary);
+						background: var(--el-color-primary-light-9);
+						color: var(--el-color-primary);
+					}
+				}
+				::v-deep(.jtk-overlay):not(.aLabel) {
+					padding: 4px 10px;
+					border: 1px solid var(--el-border-color-light, #ebeef5) !important;
+					color: var(--el-text-color-secondary) !important;
+					background: var(--el-color-white) !important;
+					border-radius: 3px;
+					font-size: 10px;
+				}
+				::v-deep(.jtk-overlay.workflow-right-empty-label) {
+					display: none;
+				}
+			}
+		}
+	}
+	.workflow-mask {
+		position: absolute;
+		top: 0;
+		right: 0;
+		bottom: 0;
+		left: 0;
+		&::after {
+			content: '手机版不支持 jsPlumb 操作';
+			position: absolute;
+			top: 0;
+			right: 0;
+			bottom: 0;
+			left: 0;
+			z-index: 1;
+			background: rgba(255, 255, 255, 0.9);
+			color: #666666;
+			display: flex;
+			align-items: center;
+			justify-content: center;
+		}
+	}
+}
+</style>
diff --git a/src/views/pages/workflow/js/config.ts b/src/views/pages/workflow/js/config.ts
new file mode 100644
index 0000000..3784c02
--- /dev/null
+++ b/src/views/pages/workflow/js/config.ts
@@ -0,0 +1,99 @@
+// jsplumb 默认配置
+export const jsplumbDefaults = {
+	// 多个锚点 [源锚点,目标锚点]
+	Anchors: [
+		'Top',
+		'TopCenter',
+		'TopRight',
+		'TopLeft',
+		'Right',
+		'RightMiddle',
+		'Bottom',
+		'BottomCenter',
+		'BottomRight',
+		'BottomLeft',
+		'Left',
+		'LeftMiddle',
+	],
+	// 连线的容器id
+	Container: 'workflow-right',
+	// 设置链接线的形状,如直线或者曲线之类的。anchor可以去设置锚点的位置。可选值"<Bezier|Flowchart|StateMachine|Straight>"
+	Connector: ['Bezier', { curviness: 100 }],
+	// 节点是否可以用鼠标拖动使其断开,默认为true。即用鼠标链接上的连线,也可以使用鼠标拖动让其断开。设置成false,可以让其拖动也不会自动断开
+	ConnectionsDetachable: false,
+	// 删除线的时候节点不删除
+	DeleteEndpointsOnDetach: false,
+	//  每当添加或以其他方式创建 Endpoint 并且 jsPlumb 尚未给出任何明确的 Endpoint 定义时将使用
+	Endpoint: ['Blank', { Overlays: '' }],
+	// 连接中源和目标端点的默认外观
+	EndpointStyle: { fill: '#1879ffa1', outlineWidth: 1 },
+	// jsPlumb 的内部日志记录是否打开
+	LogEnabled: true,
+	// 连接器的默认外观
+	PaintStyle: {
+		stroke: '#E0E3E7',
+		strokeWidth: 1,
+		outlineStroke: 'transparent',
+		outlineWidth: 10,
+	},
+	// 用于配置任何可拖动元素的默认选项jsPlumb.draggable
+	DragOptions: { cursor: 'pointer', zIndex: 2000 },
+	// 添加到连接器和端点的默认叠加层。已弃用:从 4.x 开始,将不支持此功能。并非所有叠加层都可以连接到连接器和端点。
+	Overlays: [
+		[
+			'Arrow',
+			{
+				width: 10, // 箭头尾部的宽度
+				length: 8, // 从箭头的尾部到头部的距离
+				location: 1, // 位置,建议使用0~1之间
+				direction: 1, // 方向,默认值为1(表示向前),可选-1(表示向后)
+				foldback: 0.623, // 折回,也就是尾翼的角度,默认0.623,当为1时,为正三角
+			},
+		],
+		[
+			'Label',
+			{
+				label: '',
+				location: 0.5,
+				cssClass: 'aLabel',
+			},
+		],
+	],
+	// 默认渲染模式 svg、canvas
+	RenderMode: 'svg',
+	// 悬停状态下连接的默认外观
+	HoverPaintStyle: { stroke: '#b0b2b5', strokeWidth: 1 },
+	// 悬停状态下端点的默认外观
+	EndpointHoverStyle: { fill: 'red' },
+	// 端点和连接的默认范围。范围提供了对哪些端点可以连接到哪些其他端点的基本控制
+	Scope: 'jsPlumb_DefaultScope',
+};
+
+// 整个节点作为source或者target
+export const jsplumbMakeSource = {
+	// 设置可以拖拽的类名,只要鼠标移动到该类名上的DOM,就可以拖拽连线
+	filter: '.workflow-icon-drag',
+	filterExclude: false,
+	anchor: 'Continuous',
+	// 是否允许自己连接自己
+	allowLoopback: true,
+	maxConnections: -1,
+};
+
+// 整个节点作为source或者target
+export const jsplumbMakeTarget = {
+	filter: '.workflow-icon-drag',
+	filterExclude: false,
+	// 是否允许自己连接自己
+	anchor: 'Continuous',
+	allowLoopback: true,
+	dropOptions: { hoverClass: 'ef-drop-hover' },
+};
+
+// 连线参数
+export const jsplumbConnect = {
+	isSource: true,
+	isTarget: true,
+	// 动态锚点、提供了4个方向 Continuous、AutoDefault
+	anchor: 'Continuous',
+};
diff --git a/src/views/pages/workflow/js/mock.ts b/src/views/pages/workflow/js/mock.ts
new file mode 100644
index 0000000..151c3ac
--- /dev/null
+++ b/src/views/pages/workflow/js/mock.ts
@@ -0,0 +1,262 @@
+// 左侧菜单导航数据
+export const leftNavList = [
+	{
+		title: '工作流',
+		icon: 'iconfont icon-shouye',
+		isOpen: true,
+		id: '1',
+		children: [
+			{
+				icon: 'iconfont icon-gongju',
+				name: '引擎',
+				id: '11',
+				form: [
+					{
+						type: 'input',
+						label: '客户姓名',
+						prop: 'name',
+						placeholder: '请输入客户姓名',
+						required: true,
+						disabled: false,
+					},
+					{
+						type: 'select',
+						label: '性别',
+						prop: 'sex',
+						placeholder: '请选择性别',
+						required: true,
+						disabled: false,
+						options: [
+							{
+								value: '0',
+								label: '女',
+							},
+							{
+								value: '1',
+								label: '男',
+							},
+						],
+					},
+					{
+						type: 'input',
+						label: '员工编号',
+						prop: 'number',
+						placeholder: '请输入员工编号',
+						required: true,
+						disabled: false,
+					},
+					{
+						type: 'input',
+						label: '办公电话',
+						prop: 'mobile',
+						placeholder: '请输入办公电话',
+						required: true,
+						disabled: false,
+					},
+					{
+						type: 'select',
+						label: '权限分配',
+						prop: 'role',
+						placeholder: '请选择性别',
+						required: true,
+						disabled: false,
+						options: [
+							{
+								value: '0',
+								label: '编辑权限',
+							},
+							{
+								value: '1',
+								label: '删除权限',
+							},
+						],
+					},
+					{
+						type: 'checkbox',
+						label: '模块选择',
+						prop: 'module',
+						placeholder: '请选择模块',
+						required: true,
+						disabled: false,
+					},
+				],
+			},
+			{
+				icon: 'iconfont icon-shouye_dongtaihui',
+				name: '模版',
+				id: '12',
+				form: [
+					{
+						type: 'input',
+						label: '等级',
+						prop: 'grade',
+						placeholder: '请输入等级',
+						required: true,
+						disabled: false,
+					},
+					{
+						type: 'input',
+						label: '登记密码',
+						prop: 'password',
+						placeholder: '请输入登记密码',
+						required: true,
+						disabled: false,
+					},
+				],
+			},
+			{
+				icon: 'iconfont icon-zhongduancanshuchaxun',
+				name: '名称',
+				id: '13',
+				form: [
+					{
+						type: 'input',
+						label: '数据表',
+						prop: 'dataSheet',
+						placeholder: '请输入数据表',
+						required: true,
+						disabled: false,
+					},
+					{
+						type: 'input',
+						label: '字段配置',
+						prop: 'field',
+						placeholder: '请输入字段配置',
+						required: true,
+						disabled: false,
+					},
+				],
+			},
+			{
+				icon: 'iconfont icon-zhongduancanshu',
+				name: '版本',
+				id: '14',
+				form: [
+					{
+						type: 'input',
+						label: '发布模板',
+						prop: 'publish',
+						placeholder: '请输入发布模板',
+						required: true,
+						disabled: false,
+					},
+				],
+			},
+			{
+				icon: 'iconfont icon-bolangnengshiyanchang',
+				name: '建模',
+				id: '15',
+				form: [
+					{
+						type: 'input',
+						label: '内容模板',
+						prop: 'content',
+						placeholder: '请输入内容模板',
+						required: true,
+						disabled: false,
+					},
+				],
+			},
+			{
+				icon: 'iconfont icon-xingqiu',
+				name: '节点',
+				id: '16',
+				form: [
+					{
+						type: 'input',
+						label: '活动名称6',
+						prop: 'name16',
+					},
+				],
+			},
+		],
+	},
+	{
+		title: '流程',
+		isOpen: true,
+		icon: 'iconfont icon-caijian',
+		id: '2',
+		children: [
+			{
+				icon: 'iconfont icon-fuwenben',
+				name: '实例',
+				id: '21',
+				form: [
+					{
+						type: 'input',
+						label: '活动名称7',
+						prop: 'name21',
+					},
+				],
+			},
+			{
+				icon: 'iconfont icon-fuwenbenkuang',
+				name: '轨迹',
+				id: '22',
+				form: [
+					{
+						type: 'input',
+						label: '活动名称8',
+						prop: 'name22',
+					},
+				],
+			},
+			{
+				icon: 'iconfont icon-shangchuan',
+				name: '数据',
+				id: '23',
+				form: [
+					{
+						type: 'input',
+						label: '活动名称9',
+						prop: 'name23',
+					},
+				],
+			},
+		],
+	},
+	{
+		title: '任务',
+		isOpen: true,
+		icon: 'iconfont icon-shuju',
+		id: '3',
+		children: [
+			{
+				icon: 'iconfont icon-icon-',
+				name: '参与人',
+				id: '31',
+				form: [
+					{
+						type: 'input',
+						label: '活动名称1',
+						prop: 'name31',
+					},
+				],
+			},
+			{
+				icon: 'iconfont icon-gerenzhongxin',
+				name: '执行人',
+				id: '32',
+				form: [
+					{
+						type: 'input',
+						label: '活动名称2',
+						prop: 'name32',
+					},
+				],
+			},
+			{
+				icon: 'iconfont icon-fangkuang',
+				name: '工单',
+				id: '33',
+				form: [
+					{
+						type: 'input',
+						label: '活动名称3',
+						prop: 'name33',
+					},
+				],
+			},
+		],
+	},
+];
diff --git a/src/views/params/common/details.vue b/src/views/params/common/details.vue
new file mode 100644
index 0000000..9ba885e
--- /dev/null
+++ b/src/views/params/common/details.vue
@@ -0,0 +1,52 @@
+<template>
+	<div class="layout-view-bg-white flex" :style="{ height: `calc(100vh - ${setViewHeight}` }">
+		<div class="flex-margin color-primary">
+			<div>paramsCommonDetails</div>
+			<div class="mt10 mb10">路径:path: {{ params.path }}</div>
+			<div>参数:query: {{ params.query }}</div>
+		</div>
+	</div>
+</template>
+
+<script lang="ts">
+import { defineComponent, toRefs, reactive, computed, onMounted } from 'vue';
+import { useRoute } from 'vue-router';
+import { storeToRefs } from 'pinia';
+import { useThemeConfig } from '/@/stores/themeConfig';
+import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
+
+export default defineComponent({
+	name: 'paramsCommonDetails',
+	setup() {
+		const route = useRoute();
+		const storesTagsViewRoutes = useTagsViewRoutes();
+		const storesThemeConfig = useThemeConfig();
+		const { themeConfig } = storeToRefs(storesThemeConfig);
+		const { isTagsViewCurrenFull } = storeToRefs(storesTagsViewRoutes);
+		const state = reactive({
+			params: {
+				path: '',
+				query: '',
+			},
+		});
+		// 设置 view 的高度
+		const setViewHeight = computed(() => {
+			let { isTagsview } = themeConfig.value;
+			if (isTagsViewCurrenFull.value) {
+				return `30px`;
+			} else {
+				if (isTagsview) return `114px`;
+				else return `80px`;
+			}
+		});
+		// 页面加载时
+		onMounted(() => {
+			state.params = <any>route;
+		});
+		return {
+			setViewHeight,
+			...toRefs(state),
+		};
+	},
+});
+</script>
diff --git a/src/views/params/common/index.vue b/src/views/params/common/index.vue
new file mode 100644
index 0000000..733156c
--- /dev/null
+++ b/src/views/params/common/index.vue
@@ -0,0 +1,100 @@
+<template>
+	<div class="layout-view-bg-white flex" :style="{ height: `calc(100vh - ${setViewHeight}` }">
+		<div class="flex-margin" style="width: 400px">
+			<el-result icon="success" title="普通路由" subTitle="可 `开启 TagsView 共用` 进行单标签测试">
+				<template #extra>
+					<el-alert type="success" :closable="false" class="mb30">
+						<template #default>
+							<div>1、设置非国际化:格式:tagsViewName=xxx</div>
+							<br />
+							<div>2、设置国际化:格式:tagsViewName=JSON.stringify({"zh-cn":"测试用","en":"test+page","zh-tw":"測試用"})</div>
+							<br />
+							<div>3、设置国际化后,去顶栏切换语言查看演示效果</div>
+							<br />
+							<div>
+								4、 <a href="https://gitee.com/q7but" target="_black">感谢@q7but</a>、
+								<a href="https://gitee.com/lyt-top/vue-next-admin/pulls/22/files" target="_black">!22 add 添加自定义 tagVIewName 拓展,支持国际化</a>
+							</div>
+						</template>
+					</el-alert>
+					<el-input v-model="tagsViewName" placeholder="请输入tagsView 名称" clearable class="mb15" style="width: 400px"></el-input>
+					<el-input v-model="value" placeholder="请输入路由参数 id 值" clearable style="width: 400px"></el-input>
+					<el-button type="primary" size="default" class="mt15" @click="onGoDetailsClick">
+						<SvgIcon name="iconfont icon-putong" />
+						普通路由传参
+					</el-button>
+					<el-button type="primary" size="default" class="mt15" @click="onChangeI18n">
+						<SvgIcon name="iconfont icon-fuhao-zhongwen" />
+						{{ tagsViewNameIsI18n ? '普通的演示' : '国际化演示' }}
+					</el-button>
+				</template>
+			</el-result>
+		</div>
+	</div>
+</template>
+
+<script lang="ts">
+import { toRefs, reactive, computed, defineComponent } from 'vue';
+import { useRouter } from 'vue-router';
+import { storeToRefs } from 'pinia';
+import { useThemeConfig } from '/@/stores/themeConfig';
+import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
+
+export default defineComponent({
+	name: 'paramsCommon',
+	setup() {
+		const storesTagsViewRoutes = useTagsViewRoutes();
+		const storesThemeConfig = useThemeConfig();
+		const { themeConfig } = storeToRefs(storesThemeConfig);
+		const { isTagsViewCurrenFull } = storeToRefs(storesTagsViewRoutes);
+		const state = reactive({
+			value: '',
+			tagsViewName: '',
+			tagsViewNameIsI18n: false,
+		});
+		const router = useRouter();
+		// 设置 view 的高度
+		const setViewHeight = computed(() => {
+			let { isTagsview } = themeConfig.value;
+			if (isTagsViewCurrenFull.value) {
+				return `30px`;
+			} else {
+				if (isTagsview) return `114px`;
+				else return `80px`;
+			}
+		});
+		// 跳转到详情
+		/**
+		 * 设置 tagsView 名称:
+		 * 传不同的 tagsViewName 值
+		 */
+		const onGoDetailsClick = () => {
+			const params: any = { id: state.value };
+			if (state.tagsViewName) params.tagsViewName = state.tagsViewName;
+			router.push({
+				path: '/params/common/details',
+				query: params,
+			});
+			state.value = '';
+		};
+		const onChangeI18n = () => {
+			state.tagsViewNameIsI18n = !state.tagsViewNameIsI18n;
+			if (state.tagsViewNameIsI18n) {
+				state.tagsViewName = JSON.stringify({
+					'zh-cn': '测试用',
+					en: 'test page',
+					'zh-tw': '測試用',
+				});
+			} else {
+				state.tagsViewName = '我是普通路由测试tagsViewName(非国际化)';
+			}
+		};
+		return {
+			setViewHeight,
+			onGoDetailsClick,
+			onChangeI18n,
+			...toRefs(state),
+		};
+	},
+});
+</script>
diff --git a/src/views/params/dynamic/details.vue b/src/views/params/dynamic/details.vue
new file mode 100644
index 0000000..7283ba1
--- /dev/null
+++ b/src/views/params/dynamic/details.vue
@@ -0,0 +1,52 @@
+<template>
+	<div class="layout-view-bg-white flex" :style="{ height: `calc(100vh - ${setViewHeight}` }">
+		<div class="flex-margin color-primary">
+			<div>paramsDynamicDetails</div>
+			<div class="mt10 mb10">路径:path: {{ params.path }}</div>
+			<div>参数:params: {{ params.params }}</div>
+		</div>
+	</div>
+</template>
+
+<script lang="ts">
+import { defineComponent, toRefs, reactive, computed, onMounted } from 'vue';
+import { useRoute } from 'vue-router';
+import { storeToRefs } from 'pinia';
+import { useThemeConfig } from '/@/stores/themeConfig';
+import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
+
+export default defineComponent({
+	name: 'paramsDynamicDetails',
+	setup() {
+		const storesTagsViewRoutes = useTagsViewRoutes();
+		const storesThemeConfig = useThemeConfig();
+		const { themeConfig } = storeToRefs(storesThemeConfig);
+		const { isTagsViewCurrenFull } = storeToRefs(storesTagsViewRoutes);
+		const route = useRoute();
+		const state = reactive({
+			params: {
+				path: '',
+				params: '',
+			},
+		});
+		// 设置 view 的高度
+		const setViewHeight = computed(() => {
+			let { isTagsview } = themeConfig.value;
+			if (isTagsViewCurrenFull.value) {
+				return `30px`;
+			} else {
+				if (isTagsview) return `114px`;
+				else return `80px`;
+			}
+		});
+		// 页面加载时
+		onMounted(() => {
+			state.params = <any>route;
+		});
+		return {
+			setViewHeight,
+			...toRefs(state),
+		};
+	},
+});
+</script>
diff --git a/src/views/params/dynamic/index.vue b/src/views/params/dynamic/index.vue
new file mode 100644
index 0000000..5c39f5d
--- /dev/null
+++ b/src/views/params/dynamic/index.vue
@@ -0,0 +1,102 @@
+<template>
+	<div class="layout-view-bg-white flex" :style="{ height: `calc(100vh - ${setViewHeight}` }">
+		<div class="flex-margin" style="width: 400px">
+			<el-result icon="warning" title="动态路由" subTitle="可 `开启 TagsView 共用` 进行单标签测试">
+				<template #extra>
+					<el-alert type="success" :closable="false" class="mb30">
+						<template #default>
+							<div>1、设置非国际化:格式:tagsViewName=xxx</div>
+							<br />
+							<div>2、设置国际化:格式:tagsViewName=JSON.stringify({"zh-cn":"测试用","en":"test+page","zh-tw":"測試用"})</div>
+							<br />
+							<div>3、设置国际化后,去顶栏切换语言查看演示效果</div>
+							<br />
+							<div>
+								4、 <a href="https://gitee.com/q7but" target="_black">感谢@q7but</a>、
+								<a href="https://gitee.com/lyt-top/vue-next-admin/pulls/22/files" target="_black">!22 add 添加自定义 tagVIewName 拓展,支持国际化</a>
+							</div>
+						</template>
+					</el-alert>
+					<el-input v-model="tagsViewName" placeholder="请输入tagsView 名称" clearable class="mb15" style="width: 400px"></el-input>
+					<el-input v-model="value" placeholder="请输入路由参数id值" clearable style="width: 400px"></el-input>
+					<el-button type="primary" size="default" class="mt15" @click="onGoDetailsClick">
+						<SvgIcon name="iconfont icon-dongtai" />
+						动态路由传参
+					</el-button>
+					<el-button type="primary" size="default" class="mt15" @click="onChangeI18n">
+						<SvgIcon name="iconfont icon-fuhao-zhongwen" />
+						{{ tagsViewNameIsI18n ? '普通的演示' : '国际化演示' }}
+					</el-button>
+				</template>
+			</el-result>
+		</div>
+	</div>
+</template>
+
+<script lang="ts">
+import { defineComponent, toRefs, reactive, computed } from 'vue';
+import { useRouter } from 'vue-router';
+import { ElMessage } from 'element-plus';
+import { storeToRefs } from 'pinia';
+import { useThemeConfig } from '/@/stores/themeConfig';
+import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
+
+export default defineComponent({
+	name: 'paramsDynamic',
+	setup() {
+		const storesTagsViewRoutes = useTagsViewRoutes();
+		const storesThemeConfig = useThemeConfig();
+		const { themeConfig } = storeToRefs(storesThemeConfig);
+		const { isTagsViewCurrenFull } = storeToRefs(storesTagsViewRoutes);
+		const state = reactive({
+			value: '',
+			tagsViewName: '',
+			tagsViewNameIsI18n: false,
+		});
+		const router = useRouter();
+		// 设置 view 的高度
+		const setViewHeight = computed(() => {
+			let { isTagsview } = themeConfig.value;
+			if (isTagsViewCurrenFull.value) {
+				return `30px`;
+			} else {
+				if (isTagsview) return `114px`;
+				else return `80px`;
+			}
+		});
+		// 跳转到详情
+		const onGoDetailsClick = () => {
+			if (!state.tagsViewName) return ElMessage.warning('动态路由tagsViewName为必填,因为路由配置了');
+			if (!state.value) return ElMessage.warning('路由参数id值为必填');
+			// name 值为路由中的 name
+			router.push({
+				name: 'paramsDynamicDetails',
+				params: {
+					t: 'vue-next-admin',
+					id: state.value,
+					tagsViewName: state.tagsViewName,
+				},
+			});
+			state.value = '';
+		};
+		const onChangeI18n = () => {
+			state.tagsViewNameIsI18n = !state.tagsViewNameIsI18n;
+			if (state.tagsViewNameIsI18n) {
+				state.tagsViewName = JSON.stringify({
+					'zh-cn': '我是动态路由',
+					en: 'Im dynamic routing',
+					'zh-tw': '我是動態路由',
+				});
+			} else {
+				state.tagsViewName = '我是动态路由测试tagsViewName(非国际化)';
+			}
+		};
+		return {
+			setViewHeight,
+			onGoDetailsClick,
+			onChangeI18n,
+			...toRefs(state),
+		};
+	},
+});
+</script>
diff --git a/src/views/personal/index.vue b/src/views/personal/index.vue
new file mode 100644
index 0000000..5a29dc7
--- /dev/null
+++ b/src/views/personal/index.vue
@@ -0,0 +1,387 @@
+<template>
+	<div class="personal">
+		<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://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1813762643,1914315241&fm=26&gp=0.jpg" />
+							</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 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 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="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="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="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="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="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="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="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 lang="ts">
+import { toRefs, reactive, computed, defineComponent } from 'vue';
+import { formatAxis } from '/@/utils/formatTime';
+import { newsInfoList, recommendList } from './mock';
+
+// 定义接口来定义对象的类型
+interface PersonalState {
+	newsInfoList: any;
+	recommendList: any;
+	personalForm: any;
+}
+
+export default defineComponent({
+	name: 'personal',
+	setup() {
+		const state = reactive<PersonalState>({
+			newsInfoList,
+			recommendList,
+			personalForm: {
+				name: '',
+				email: '',
+				autograph: '',
+				occupation: '',
+				phone: '',
+				sex: '',
+			},
+		});
+		// 当前时间提示语
+		const currentTime = computed(() => {
+			return formatAxis(new Date());
+		});
+		return {
+			currentTime,
+			...toRefs(state),
+		};
+	},
+});
+</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;
+			::v-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>
diff --git a/src/views/personal/mock.ts b/src/views/personal/mock.ts
new file mode 100644
index 0000000..ff57575
--- /dev/null
+++ b/src/views/personal/mock.ts
@@ -0,0 +1,66 @@
+/**
+ * 消息通知
+ * @returns 返回模拟数据
+ */
+export const newsInfoList: Array<object> = [
+	{
+		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: Array<object> = [
+	{
+		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',
+	},
+];
diff --git a/src/views/system/department/component/deptDialog.vue b/src/views/system/department/component/deptDialog.vue
new file mode 100644
index 0000000..0477317
--- /dev/null
+++ b/src/views/system/department/component/deptDialog.vue
@@ -0,0 +1,161 @@
+<template>
+	<div class="system-add-dept-container">
+		<el-dialog :title="title" v-model="isShowDialog" width="600px">
+			<el-form :model="departmentForm" size="default" label-width="90px">
+				<el-row :gutter="35">
+					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+						<el-form-item label="上级部门">
+							<el-cascader
+								:options="deptData"
+								:props="{ checkStrictly: true, value: 'id', label: 'name' }"
+								placeholder="请选择部门"
+								clearable
+								class="w100"
+								v-model="departmentForm.struct"
+							>
+							</el-cascader>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+						<el-form-item label="部门名称">
+							<el-input v-model="departmentForm.name" placeholder="请输入部门名称" clearable></el-input>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+						<el-form-item label="部门描述">
+							<el-input v-model="departmentForm.info" type="textarea" placeholder="请输入部门描述" maxlength="150"></el-input>
+						</el-form-item>
+					</el-col>
+				</el-row>
+			</el-form>
+			<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 lang="ts">
+import { ElMessage } from 'element-plus';
+import { reactive, toRefs, onMounted, defineComponent } from 'vue';
+import { departmentApi } from "/@/api/department";
+
+// 定义接口来定义对象的类型
+interface TableDataRow {
+	struct: Array<string>;
+	name:string,
+	info:string,
+	parentId:string,
+	id:number,
+}
+interface DeptSate {
+	title:string;
+	isShowDialog: boolean;
+	departmentForm: {
+		struct: Array<string>;
+		name:string,
+		info:string,
+		parentId:string
+	};
+	deptData: Array<TableDataRow>;
+}
+
+export default defineComponent({
+	name: 'systemAddDept',
+	setup(prop,context) {
+		const state = reactive<DeptSate>({
+			title:'',
+			isShowDialog: false,
+			departmentForm: {
+				name:'',
+				parentId:'',
+				info:'',
+				struct:[]
+			},
+			deptData: [], // 部门数据
+		});
+		// 打开弹窗
+		const openDialog = (type:string, value: any,departmentList: []) => {
+			state.isShowDialog = true;
+			state.deptData = JSON.parse(JSON.stringify(departmentList))
+			if(type === '新增'){
+				state.title = '新增部门'
+				state.departmentForm = {
+					name:'',
+					parentId:'',
+					info:'',
+					struct:[]
+				}
+			}else{
+				state.title = '编辑部门'
+			}
+		};
+		// 关闭弹窗
+		const closeDialog = () => {
+			state.isShowDialog = false;
+		};
+		// 取消
+		const onCancel = () => {
+			closeDialog();
+		};
+		// 新增
+		const onSubmit = async () => {
+			if(state.departmentForm.struct && state.departmentForm.struct !== []){
+				let departmentId = JSON.parse(JSON.stringify(state.departmentForm.struct))
+				state.departmentForm.parentId = departmentId[departmentId.length - 1]
+			}
+			if(state.title === '新增部门'){
+				let res = await departmentApi().addDepartment(state.departmentForm)
+				if(res.data.code === '200'){
+					ElMessage({
+						type:'success',
+						message:'菜单新增成功',
+						duration:2000
+					})
+					closeDialog();
+					context.emit('getMenuList')
+				}else{
+					ElMessage({
+						type:'warning',
+						message:res.data.msg
+					})
+				}
+			}else{
+				let res = await departmentApi().modDepartment(state.departmentForm)
+				if(res.data.code === '200'){
+					ElMessage({
+						type:'success',
+						message:'菜单修改成功',
+						duration:2000
+					})
+					closeDialog();
+					context.emit('getMenuList')
+				}else{
+					ElMessage({
+						type:'warning',
+						message:res.data.msg
+					})
+				}
+			}
+		};
+		// 初始化部门数据
+		const initTableData = () => {
+
+		};
+		// 页面加载时
+		onMounted(() => {
+			initTableData();
+		});
+		return {
+			openDialog,
+			closeDialog,
+			onCancel,
+			onSubmit,
+			...toRefs(state),
+		};
+	},
+});
+</script>
diff --git a/src/views/system/department/component/editDept.vue b/src/views/system/department/component/editDept.vue
new file mode 100644
index 0000000..7b16f44
--- /dev/null
+++ b/src/views/system/department/component/editDept.vue
@@ -0,0 +1,179 @@
+<template>
+	<div class="system-edit-dept-container">
+		<el-dialog title="修改部门" v-model="isShowDialog" width="769px">
+			<el-form :model="ruleForm" size="default" label-width="90px">
+				<el-row :gutter="35">
+					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+						<el-form-item label="上级部门">
+							<el-cascader
+								:options="deptData"
+								:props="{ checkStrictly: true, value: 'deptName', label: 'deptName' }"
+								placeholder="请选择部门"
+								clearable
+								class="w100"
+								v-model="ruleForm.deptLevel"
+							>
+								<template #default="{ node, data }">
+									<span>{{ data.deptName }}</span>
+									<span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
+								</template>
+							</el-cascader>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="部门名称">
+							<el-input v-model="ruleForm.deptName" placeholder="请输入部门名称" clearable></el-input>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="负责人">
+							<el-input v-model="ruleForm.person" placeholder="请输入负责人" clearable></el-input>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="手机号">
+							<el-input v-model="ruleForm.phone" placeholder="请输入手机号" clearable></el-input>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="邮箱">
+							<el-input v-model="ruleForm.email" placeholder="请输入" clearable></el-input>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="排序">
+							<el-input-number v-model="ruleForm.sort" :min="0" :max="999" controls-position="right" placeholder="请输入排序" class="w100" />
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="部门状态">
+							<el-switch v-model="ruleForm.status" inline-prompt active-text="启" inactive-text="禁"></el-switch>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+						<el-form-item label="部门描述">
+							<el-input v-model="ruleForm.describe" type="textarea" placeholder="请输入部门描述" maxlength="150"></el-input>
+						</el-form-item>
+					</el-col>
+				</el-row>
+			</el-form>
+			<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 lang="ts">
+import { reactive, toRefs, onMounted, defineComponent } from 'vue';
+
+// 定义接口来定义对象的类型
+interface TableDataRow {
+	deptName: string;
+	createTime: string;
+	status: boolean;
+	sort: number;
+	describe: string;
+	id: number;
+	children?: TableDataRow[];
+}
+interface RuleFormState {
+	deptLevel: Array<string>;
+	deptName: string;
+	person: string;
+	phone: string | number;
+	email: string;
+	sort: number;
+	status: boolean;
+	describe: string;
+}
+interface DeptSate {
+	isShowDialog: boolean;
+	ruleForm: RuleFormState;
+	deptData: Array<TableDataRow>;
+}
+
+export default defineComponent({
+	name: 'systemEditDept',
+	setup() {
+		const state = reactive<DeptSate>({
+			isShowDialog: false,
+			ruleForm: {
+				deptLevel: [], // 上级部门
+				deptName: '', // 部门名称
+				person: '', // 负责人
+				phone: '', // 手机号
+				email: '', // 邮箱
+				sort: 0, // 排序
+				status: true, // 部门状态
+				describe: '', // 部门描述
+			},
+			deptData: [], // 部门数据
+		});
+		// 打开弹窗
+		const openDialog = (row: RuleFormState) => {
+			row.deptLevel = ['vueNextAdmin'];
+			row.person = 'lyt';
+			row.phone = '12345678910';
+			row.email = 'vueNextAdmin@123.com';
+			state.ruleForm = row;
+			state.isShowDialog = true;
+		};
+		// 关闭弹窗
+		const closeDialog = () => {
+			state.isShowDialog = false;
+		};
+		// 取消
+		const onCancel = () => {
+			closeDialog();
+		};
+		// 新增
+		const onSubmit = () => {
+			closeDialog();
+		};
+		// 初始化部门数据
+		const initTableData = () => {
+			state.deptData.push({
+				deptName: 'vueNextAdmin',
+				createTime: new Date().toLocaleString(),
+				status: true,
+				sort: Math.random(),
+				describe: '顶级部门',
+				id: Math.random(),
+				children: [
+					{
+						deptName: 'IT外包服务',
+						createTime: new Date().toLocaleString(),
+						status: true,
+						sort: Math.random(),
+						describe: '总部',
+						id: Math.random(),
+					},
+					{
+						deptName: '资本控股',
+						createTime: new Date().toLocaleString(),
+						status: true,
+						sort: Math.random(),
+						describe: '分部',
+						id: Math.random(),
+					},
+				],
+			});
+		};
+		// 页面加载时
+		onMounted(() => {
+			initTableData();
+		});
+		return {
+			openDialog,
+			closeDialog,
+			onCancel,
+			onSubmit,
+			...toRefs(state),
+		};
+	},
+});
+</script>
diff --git a/src/views/system/department/index.vue b/src/views/system/department/index.vue
new file mode 100644
index 0000000..4f33400
--- /dev/null
+++ b/src/views/system/department/index.vue
@@ -0,0 +1,121 @@
+<template>
+	<div class="system-dept-container">
+		<el-card shadow="hover">
+			<div class="system-dept-search mb15">
+				<el-input size="default" placeholder="请输入部门名称" style="max-width: 180px"> </el-input>
+				<el-button size="default" type="primary" class="ml10">
+					<el-icon>
+						<ele-Search />
+					</el-icon>
+					查询
+				</el-button>
+				<el-button size="default" type="success" class="ml10" @click="onOpenDeptDialog('新增','')">
+					<el-icon>
+						<ele-FolderAdd />
+					</el-icon>
+					新增部门
+				</el-button>
+			</div>
+			<el-table
+				:data="tableData.data"
+				style="width: 100%"
+				row-key="id"
+				default-expand-all
+				:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
+			>
+				<el-table-column prop="name" label="部门名称" show-overflow-tooltip> </el-table-column>
+				<el-table-column prop="status" label="部门状态" show-overflow-tooltip>
+					<template #default="scope">
+						<el-tag type="success" v-if="scope.row.status">启用</el-tag>
+						<el-tag type="info" v-else>禁用</el-tag>
+					</template>
+				</el-table-column>
+				<el-table-column prop="info" label="部门描述" show-overflow-tooltip></el-table-column>
+				<el-table-column label="操作" show-overflow-tooltip width="140">
+					<template #default="scope">
+						<el-button size="small" text type="primary" @click="onOpenDeptDialog('新增','')">新增</el-button>
+						<el-button size="small" text type="primary" @click="onOpenDeptDialog('修改',scope.row)">修改</el-button>
+						<el-button size="small" text type="primary" @click="onTabelRowDel(scope.row)">删除</el-button>
+					</template>
+				</el-table-column>
+			</el-table>
+		</el-card>
+		<deptDialog ref="deptDialog" />
+	</div>
+</template>
+
+<script lang="ts">
+import { ref, toRefs, reactive, onMounted, defineComponent } from 'vue';
+import { ElMessageBox, ElMessage } from 'element-plus';
+import deptDialog from '/@/views/system/department/component/deptDialog.vue';
+import {departmentApi} from "/@/api/department";
+
+// 定义接口来定义对象的类型
+interface TableDataRow {
+	name: string;
+	status: boolean;
+	parentId: number;
+	info: string;
+	id: number;
+	children?: TableDataRow[];
+}
+interface TableDataState {
+	tableData: {
+		data: Array<TableDataRow>;
+		total: number;
+		loading: boolean;
+	};
+}
+
+export default defineComponent({
+	name: 'systemDept',
+	components: { deptDialog },
+	setup() {
+		const deptDialog = ref();
+		const state = reactive<TableDataState>({
+			tableData: {
+				data: [],
+				total: 0,
+				loading: false,
+			},
+		});
+		// 初始化表格数据
+		const initTableData = async () => {
+			let res = await departmentApi().getDepartmentList()
+			if(res.data.code === '200'){
+				state.tableData.data = res.data.data
+				state.tableData.total = state.tableData.data.length;
+			}else{
+				ElMessage({
+					type:'warning',
+					message:res.data.msg
+				})
+			}
+		};
+		// 打开新增菜单弹窗
+		const onOpenDeptDialog = (type: string,value: any) => {
+			deptDialog.value.openDialog(type,value,state.tableData.data);
+		};
+		// 删除当前行
+		const onTabelRowDel = (row: TableDataRow) => {
+			ElMessageBox.confirm(`此操作将永久删除部门:${row.id}, 是否继续?`, '提示', {
+				confirmButtonText: '删除',
+				cancelButtonText: '取消',
+				type: 'warning',
+			}).then(() => {
+					ElMessage.success('删除成功');
+			}).catch(() => {});
+		};
+		// 页面加载时
+		onMounted(() => {
+			initTableData();
+		});
+		return {
+			deptDialog,
+			onOpenDeptDialog,
+			onTabelRowDel,
+			...toRefs(state),
+		};
+	},
+});
+</script>
diff --git a/src/views/system/dic/component/addDic.vue b/src/views/system/dic/component/addDic.vue
new file mode 100644
index 0000000..c964dd9
--- /dev/null
+++ b/src/views/system/dic/component/addDic.vue
@@ -0,0 +1,129 @@
+<template>
+	<div class="system-add-dic-container">
+		<el-dialog title="新增字典" v-model="isShowDialog" width="769px">
+			<el-alert title="半成品,交互过于复杂,请自行扩展!" type="warning" :closable="false" class="mb20"> </el-alert>
+			<el-form :model="ruleForm" size="default" label-width="90px">
+				<el-row :gutter="35">
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="字典名称">
+							<el-input v-model="ruleForm.dicName" placeholder="请输入字典名称" clearable></el-input>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="字段名">
+							<el-input v-model="ruleForm.fieldName" placeholder="请输入字段名,拼接 ruleForm.list" clearable></el-input>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+						<el-form-item label="字典状态">
+							<el-switch v-model="ruleForm.status" inline-prompt active-text="启" inactive-text="禁"></el-switch>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+						<el-row :gutter="35" v-for="(v, k) in ruleForm.list" :key="k">
+							<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+								<el-form-item :prop="`list[${k}].label`">
+									<template #label>
+										<el-button type="primary" circle size="small" @click="onAddRow" v-if="k === 0">
+											<el-icon>
+												<ele-Plus />
+											</el-icon>
+										</el-button>
+										<el-button type="danger" circle size="small" @click="onDelRow(k)" v-else>
+											<el-icon>
+												<ele-Delete />
+											</el-icon>
+										</el-button>
+										<span class="ml10">字段</span>
+									</template>
+									<el-input v-model="v.label" style="width: 100%" placeholder="请输入字段名"> </el-input>
+								</el-form-item>
+							</el-col>
+							<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+								<el-form-item label="属性" :prop="`list[${k}].value`">
+									<el-input v-model="v.value" style="width: 100%" placeholder="请输入属性值"> </el-input>
+								</el-form-item>
+							</el-col>
+						</el-row>
+					</el-col>
+					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+						<el-form-item label="字典描述">
+							<el-input v-model="ruleForm.describe" type="textarea" placeholder="请输入字典描述" maxlength="150"></el-input>
+						</el-form-item>
+					</el-col>
+				</el-row>
+			</el-form>
+			<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 lang="ts">
+import { reactive, toRefs, defineComponent } from 'vue';
+
+export default defineComponent({
+	name: 'systemAddDic',
+	setup() {
+		const state = reactive({
+			isShowDialog: false,
+			ruleForm: {
+				dicName: '', // 字典名称
+				fieldName: '', // 字段名
+				status: true, // 字典状态
+				list: [
+					// 子集字段 + 属性值
+					{
+						id: Math.random(),
+						label: '',
+						value: '',
+					},
+				],
+				describe: '', // 字典描述
+				fieldNameList: [], // 字段名: [{子集字段 + 属性值}]
+			},
+		});
+		// 打开弹窗
+		const openDialog = () => {
+			state.isShowDialog = true;
+		};
+		// 关闭弹窗
+		const closeDialog = () => {
+			state.isShowDialog = false;
+		};
+		// 取消
+		const onCancel = () => {
+			closeDialog();
+		};
+		// 新增
+		const onSubmit = () => {
+			closeDialog();
+		};
+		// 新增行
+		const onAddRow = () => {
+			state.ruleForm.list.push({
+				id: Math.random(),
+				label: '',
+				value: '',
+			});
+		};
+		// 删除行
+		const onDelRow = (k: number) => {
+			state.ruleForm.list.splice(k, 1);
+		};
+		return {
+			openDialog,
+			closeDialog,
+			onCancel,
+			onSubmit,
+			onAddRow,
+			onDelRow,
+			...toRefs(state),
+		};
+	},
+});
+</script>
diff --git a/src/views/system/dic/component/editDic.vue b/src/views/system/dic/component/editDic.vue
new file mode 100644
index 0000000..66b0365
--- /dev/null
+++ b/src/views/system/dic/component/editDic.vue
@@ -0,0 +1,162 @@
+<template>
+	<div class="system-edit-dic-container">
+		<el-dialog title="修改字典" v-model="isShowDialog" width="769px">
+			<el-alert title="半成品,交互过于复杂,请自行扩展!" type="warning" :closable="false" class="mb20"> </el-alert>
+			<el-form :model="ruleForm" size="default" label-width="90px">
+				<el-row :gutter="35">
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="字典名称">
+							<el-input v-model="ruleForm.dicName" placeholder="请输入字典名称" clearable></el-input>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="字段名">
+							<el-input v-model="ruleForm.fieldName" placeholder="请输入字段名,拼接 ruleForm.list" clearable></el-input>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+						<el-form-item label="字典状态">
+							<el-switch v-model="ruleForm.status" inline-prompt active-text="启" inactive-text="禁"></el-switch>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+						<el-row :gutter="35" v-for="(v, k) in ruleForm.list" :key="k">
+							<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+								<el-form-item :prop="`list[${k}].label`">
+									<template #label>
+										<el-button type="primary" circle size="small" @click="onAddRow" v-if="k === 0">
+											<el-icon>
+												<ele-Plus />
+											</el-icon>
+										</el-button>
+										<el-button type="danger" circle size="small" @click="onDelRow(k)" v-else>
+											<el-icon>
+												<ele-Delete />
+											</el-icon>
+										</el-button>
+										<span class="ml10">字段</span>
+									</template>
+									<el-input v-model="v.label" style="width: 100%" placeholder="请输入字段名"> </el-input>
+								</el-form-item>
+							</el-col>
+							<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+								<el-form-item label="属性" :prop="`list[${k}].value`">
+									<el-input v-model="v.value" style="width: 100%" placeholder="请输入属性值"> </el-input>
+								</el-form-item>
+							</el-col>
+						</el-row>
+					</el-col>
+					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+						<el-form-item label="字典描述">
+							<el-input v-model="ruleForm.describe" type="textarea" placeholder="请输入字典描述" maxlength="150"></el-input>
+						</el-form-item>
+					</el-col>
+				</el-row>
+			</el-form>
+			<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 lang="ts">
+import { reactive, toRefs, defineComponent } from 'vue';
+
+// 定义接口来定义对象的类型
+interface RuleFormList {
+	id: number;
+	label: string;
+	value: string;
+}
+interface RuleFormState {
+	dicName: string;
+	fieldName: string;
+	status: boolean;
+	list: RuleFormList[];
+	describe: string;
+	fieldNameList: Array<any>;
+}
+interface DicState {
+	isShowDialog: boolean;
+	ruleForm: RuleFormState;
+}
+
+export default defineComponent({
+	name: 'systemEditDic',
+	setup() {
+		const state = reactive<DicState>({
+			isShowDialog: false,
+			ruleForm: {
+				dicName: '', // 字典名称
+				fieldName: '', // 字段名
+				status: true, // 字典状态
+				list: [
+					// 子集字段 + 属性值
+					{
+						id: Math.random(),
+						label: '',
+						value: '',
+					},
+				],
+				describe: '', // 字典描述
+				fieldNameList: [], // 字段名: [{子集字段 + 属性值}]
+			},
+		});
+		// 打开弹窗
+		const openDialog = (row: RuleFormState) => {
+			if (row.fieldName === 'SYS_UERINFO') {
+				row.list = [
+					{ id: Math.random(), label: 'sex', value: '1' },
+					{ id: Math.random(), label: 'sex', value: '0' },
+				];
+			} else {
+				row.list = [
+					{ id: Math.random(), label: 'role', value: 'admin' },
+					{ id: Math.random(), label: 'role', value: 'common' },
+					{ id: Math.random(), label: 'roleName', value: '超级管理员' },
+					{ id: Math.random(), label: 'roleName', value: '普通用户' },
+				];
+			}
+			state.ruleForm = row;
+			state.isShowDialog = true;
+		};
+		// 关闭弹窗
+		const closeDialog = () => {
+			state.isShowDialog = false;
+		};
+		// 取消
+		const onCancel = () => {
+			closeDialog();
+		};
+		// 新增
+		const onSubmit = () => {
+			closeDialog();
+		};
+		// 新增行
+		const onAddRow = () => {
+			state.ruleForm.list.push({
+				id: Math.random(),
+				label: '',
+				value: '',
+			});
+		};
+		// 删除行
+		const onDelRow = (k: number) => {
+			state.ruleForm.list.splice(k, 1);
+		};
+		return {
+			openDialog,
+			closeDialog,
+			onCancel,
+			onSubmit,
+			onAddRow,
+			onDelRow,
+			...toRefs(state),
+		};
+	},
+});
+</script>
diff --git a/src/views/system/dic/index.vue b/src/views/system/dic/index.vue
new file mode 100644
index 0000000..034140c
--- /dev/null
+++ b/src/views/system/dic/index.vue
@@ -0,0 +1,159 @@
+<template>
+	<div class="system-dic-container">
+		<el-card shadow="hover">
+			<div class="system-user-search mb15">
+				<el-input size="default" placeholder="请输入字典名称" style="max-width: 180px"> </el-input>
+				<el-button size="default" type="primary" class="ml10">
+					<el-icon>
+						<ele-Search />
+					</el-icon>
+					查询
+				</el-button>
+				<el-button size="default" type="success" class="ml10" @click="onOpenAddDic">
+					<el-icon>
+						<ele-FolderAdd />
+					</el-icon>
+					新增字典
+				</el-button>
+			</div>
+			<el-table :data="tableData.data" style="width: 100%">
+				<el-table-column type="index" label="序号" width="50" />
+				<el-table-column prop="dicName" label="字典名称" show-overflow-tooltip></el-table-column>
+				<el-table-column prop="fieldName" label="字段名" show-overflow-tooltip></el-table-column>
+				<el-table-column prop="status" label="字典状态" show-overflow-tooltip>
+					<template #default="scope">
+						<el-tag type="success" v-if="scope.row.status">启用</el-tag>
+						<el-tag type="info" v-else>禁用</el-tag>
+					</template>
+				</el-table-column>
+				<el-table-column prop="describe" label="字典描述" show-overflow-tooltip></el-table-column>
+				<el-table-column prop="createTime" label="创建时间" show-overflow-tooltip></el-table-column>
+				<el-table-column label="操作" width="100">
+					<template #default="scope">
+						<el-button size="small" text type="primary" @click="onOpenEditDic(scope.row)">修改</el-button>
+						<el-button size="small" text type="primary" @click="onRowDel(scope.row)">删除</el-button>
+					</template>
+				</el-table-column>
+			</el-table>
+			<el-pagination
+				@size-change="onHandleSizeChange"
+				@current-change="onHandleCurrentChange"
+				class="mt15"
+				:pager-count="5"
+				:page-sizes="[10, 20, 30]"
+				v-model:current-page="tableData.param.pageNum"
+				background
+				v-model:page-size="tableData.param.pageSize"
+				layout="total, sizes, prev, pager, next, jumper"
+				:total="tableData.total"
+			>
+			</el-pagination>
+		</el-card>
+		<AddDic ref="addDicRef" />
+		<EditDic ref="editDicRef" />
+	</div>
+</template>
+
+<script lang="ts">
+import { toRefs, reactive, onMounted, ref, defineComponent } from 'vue';
+import { ElMessageBox, ElMessage } from 'element-plus';
+import AddDic from '/@/views/system/dic/component/addDic.vue';
+import EditDic from '/@/views/system/dic/component/editDic.vue';
+
+// 定义接口来定义对象的类型
+interface TableDataRow {
+	dicName: string;
+	fieldName: string;
+	describe: string;
+	status: boolean;
+	createTime: string;
+}
+interface TableDataState {
+	tableData: {
+		data: Array<TableDataRow>;
+		total: number;
+		loading: boolean;
+		param: {
+			pageNum: number;
+			pageSize: number;
+		};
+	};
+}
+
+export default defineComponent({
+	name: 'systemDic',
+	components: { AddDic, EditDic },
+	setup() {
+		const addDicRef = ref();
+		const editDicRef = ref();
+		const state = reactive<TableDataState>({
+			tableData: {
+				data: [],
+				total: 0,
+				loading: false,
+				param: {
+					pageNum: 1,
+					pageSize: 10,
+				},
+			},
+		});
+		// 初始化表格数据
+		const initTableData = () => {
+			const data: Array<TableDataRow> = [];
+			for (let i = 0; i < 2; i++) {
+				data.push({
+					dicName: i === 0 ? '角色标识' : '用户性别',
+					fieldName: i === 0 ? 'SYS_ROLE' : 'SYS_UERINFO',
+					describe: i === 0 ? '这是角色字典' : '这是用户性别字典',
+					status: true,
+					createTime: new Date().toLocaleString(),
+				});
+			}
+			state.tableData.data = data;
+			state.tableData.total = state.tableData.data.length;
+		};
+		// 打开新增字典弹窗
+		const onOpenAddDic = () => {
+			addDicRef.value.openDialog();
+		};
+		// 打开修改字典弹窗
+		const onOpenEditDic = (row: TableDataRow) => {
+			editDicRef.value.openDialog(row);
+		};
+		// 删除字典
+		const onRowDel = (row: TableDataRow) => {
+			ElMessageBox.confirm(`此操作将永久删除字典名称:“${row.dicName}”,是否继续?`, '提示', {
+				confirmButtonText: '确认',
+				cancelButtonText: '取消',
+				type: 'warning',
+			})
+				.then(() => {
+					ElMessage.success('删除成功');
+				})
+				.catch(() => {});
+		};
+		// 分页改变
+		const onHandleSizeChange = (val: number) => {
+			state.tableData.param.pageSize = val;
+		};
+		// 分页改变
+		const onHandleCurrentChange = (val: number) => {
+			state.tableData.param.pageNum = val;
+		};
+		// 页面加载时
+		onMounted(() => {
+			initTableData();
+		});
+		return {
+			addDicRef,
+			editDicRef,
+			onOpenAddDic,
+			onOpenEditDic,
+			onRowDel,
+			onHandleSizeChange,
+			onHandleCurrentChange,
+			...toRefs(state),
+		};
+	},
+});
+</script>
diff --git a/src/views/system/menu/component/menuDialog.vue b/src/views/system/menu/component/menuDialog.vue
new file mode 100644
index 0000000..ab33832
--- /dev/null
+++ b/src/views/system/menu/component/menuDialog.vue
@@ -0,0 +1,247 @@
+<template>
+	<div class="system-add-menu-container">
+		<el-dialog :title="title" v-model="isShowDialog" width="769px">
+			<el-form :model="ruleForm" size="default" label-width="80px">
+				<el-row :gutter="35">
+					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+						<el-form-item label="上级菜单">
+							<el-cascader
+								:options="menuData"
+								:props="{ checkStrictly: true, value: 'id', label: 'title' }"
+								placeholder="请选择上级菜单"
+								clearable
+								class="w100"
+								v-model="ruleForm.menuSuperior"
+							>
+							</el-cascader>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="菜单名称">
+							<el-input v-model="ruleForm.meta.title" placeholder="格式:message.router.xxx" clearable></el-input>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="路由名称">
+							<el-input v-model="ruleForm.name" placeholder="路由中的 name 值" clearable></el-input>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="路由路径">
+							<el-input v-model="ruleForm.path" placeholder="路由中的 path 值" clearable></el-input>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="重定向">
+							<el-input v-model="ruleForm.redirect" placeholder="请输入路由重定向" clearable></el-input>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="菜单图标">
+							<IconSelector placeholder="请输入菜单图标" v-model="ruleForm.meta.icon" type="all" />
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="组件路径">
+							<el-input v-model="ruleForm.component" placeholder="组件路径" clearable></el-input>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="权限标识">
+							<el-select v-model="ruleForm.meta.roles" multiple placeholder="取角色管理" clearable class="w100">
+								<el-option label="admin" value="admin"></el-option>
+								<el-option label="common" value="common"></el-option>
+							</el-select>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="菜单排序">
+							<el-input-number v-model="ruleForm.priority" controls-position="right" placeholder="请输入排序" class="w100"/>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="是否隐藏">
+							<el-radio-group v-model="ruleForm.meta.isHide">
+								<el-radio :label="true">隐藏</el-radio>
+								<el-radio :label="false">不隐藏</el-radio>
+							</el-radio-group>
+						</el-form-item>
+					</el-col>
+				</el-row>
+			</el-form>
+			<template #footer>
+				<span class="dialog-footer">
+					<el-button @click="onCancel" size="default">取 消</el-button>
+					<el-button type="primary" @click="onSubmit" size="default">{{ buttonName }}</el-button>
+				</span>
+			</template>
+		</el-dialog>
+	</div>
+</template>
+
+<script lang="ts">
+import { reactive, toRefs, onMounted, defineComponent } from 'vue';
+import { storeToRefs } from 'pinia';
+import { useRoutesList } from '/@/stores/routesList';
+import { i18n } from '/@/i18n/index';
+import IconSelector from '/@/components/iconSelector/index.vue';
+import { useMenuApi } from '/@/api/menu/index'
+import { ElMessageBox, ElMessage } from 'element-plus';
+import {Session} from "/@/utils/storage";
+// import { setBackEndControlRefreshRoutes } from "/@/router/backEnd";
+
+export default defineComponent({
+	name: 'systemAddMenu',
+	components: { IconSelector },
+	setup(props,context) {
+		const stores = useRoutesList();
+		const { routesList } = storeToRefs(stores);
+		const state = reactive({
+			isShowDialog: false,
+			title:'',
+			buttonName:'',
+			// 参数请参考 `/src/router/route.ts` 中的 `dynamicRoutes` 路由菜单格式
+			ruleForm: {
+				projectId:'',
+				parentId:0,
+				menuSuperior: [], // 上级菜单
+				menuType: 'menu', // 菜单类型
+				name: '', // 路由名称
+				component: '', // 组件路径
+				priority: 0, // 菜单排序
+				path: '', // 路由路径
+				redirect: '', // 路由重定向,有子集 children 时
+				meta: {
+					title: '', // 菜单名称
+					icon: '', // 菜单图标
+					isHide: false, // 是否隐藏
+					isKeepAlive: true, // 是否缓存
+					isAffix: false, // 是否固定
+					isLink: '', // 外链/内嵌时链接地址(http:xxx.com),开启外链条件,`1、isLink: 链接地址不为空`
+					isIframe: false, // 是否内嵌,开启条件,`1、isIframe:true 2、isLink:链接地址不为空`
+					roles: '', // 权限标识,取角色管理
+				},
+				btnPower: '', // 菜单类型为按钮时,权限标识
+			},
+			menuData: [], // 上级菜单数据
+		});
+		// 获取 vuex 中的路由
+		const getMenuList = (routes: any) => {
+			const arr: any = [];
+			routes.map((val: any) => {
+				val['title'] = val.meta.title;
+				val['id'] = val.id
+				arr.push(val);
+				if (val.children) getMenuList(val.children);
+			});
+			return arr;
+		};
+		// 打开弹窗
+		const openDialog = (type:string,value:any) => {
+			state.isShowDialog = true;
+			if(type === '新增'){
+				state.buttonName = '新增'
+				state.title = '新增菜单'
+				state.ruleForm = {
+					projectId:Session.get('projectId'),
+					parentId:0,
+					menuSuperior: [],
+					menuType: 'menu',
+					name: '',
+					component: '',
+					priority: 0,
+					path: '',
+					redirect: '',
+					meta: {
+						title: '',
+						icon: '',
+						isHide: false,
+						isKeepAlive: true,
+						isAffix: false,
+						isLink: '',
+						isIframe: false,
+						roles: '',
+					},
+					btnPower: '',
+				}
+			}else{
+				state.buttonName = '修改'
+				state.title = '修改菜单'
+				state.ruleForm = JSON.parse(JSON.stringify(value))
+			}
+		};
+		// 关闭弹窗
+		const closeDialog = () => {
+			state.isShowDialog = false;
+		};
+		// 是否内嵌下拉改变
+		// const onSelectIframeChange = () => {
+		// 	if (state.ruleForm.meta.isIframe) state.ruleForm.isLink = true;
+		// 	else state.ruleForm.isLink = false;
+		// };
+		// 取消
+		const onCancel = () => {
+			closeDialog();
+		};
+		// 新增
+		const onSubmit = async () => {
+			if(state.ruleForm.menuSuperior && state.ruleForm.menuSuperior !== []){
+				let menuId = JSON.parse(JSON.stringify(state.ruleForm.menuSuperior))
+				state.ruleForm.parentId = menuId[menuId.length - 1]
+			}
+			if(state.title === '新增'){
+				let res = await useMenuApi().addMenu(state.ruleForm)
+				if(res.data.code === '200'){
+					ElMessage({
+						type:'success',
+						message:'菜单新增成功',
+						duration:2000
+					})
+					closeDialog();
+					context.emit('getMenuList')
+				}else{
+					ElMessage({
+						type:'warning',
+						message:res.data.msg
+					})
+				}
+			}else{
+				let res = await useMenuApi().modMenu(state.ruleForm)
+				if(res.data.code === '200'){
+					ElMessage({
+						type:'success',
+						message:'菜单修改成功',
+						duration:2000
+					})
+					closeDialog();
+					context.emit('getMenuList')
+				}else{
+					ElMessage({
+						type:'warning',
+						message:res.data.msg
+					})
+				}
+			}
+
+			// closeDialog(); // 关闭弹窗
+			// setBackEndControlRefreshRoutes() // 刷新菜单,未进行后端接口测试
+		};
+
+		const show = () => {
+			console.log(JSON.parse(JSON.stringify(state.ruleForm.menuSuperior)));
+		}
+		// 页面加载时
+		onMounted(async () => {
+			let res = await useMenuApi().getMenuAdmin(Session.get('projectId'))
+			state.menuData = JSON.parse(JSON.stringify(getMenuList(res.data.data)))
+		});
+		return {
+			openDialog,
+			closeDialog,
+			onCancel,
+			onSubmit,
+			...toRefs(state),
+		};
+	},
+});
+</script>
diff --git a/src/views/system/menu/index.vue b/src/views/system/menu/index.vue
new file mode 100644
index 0000000..2197f6a
--- /dev/null
+++ b/src/views/system/menu/index.vue
@@ -0,0 +1,128 @@
+<template>
+	<div class="system-menu-container">
+		<el-card shadow="hover">
+			<div class="system-menu-search mb15">
+				<el-input size="default" placeholder="请输入菜单名称" style="max-width: 180px"> </el-input>
+				<el-button size="default" type="primary" class="ml10">
+					<el-icon>
+						<ele-Search />
+					</el-icon>
+					查询
+				</el-button>
+				<el-button size="default" type="success" class="ml10" @click="onOpenMenuDialog('新增')">
+					<el-icon>
+						<ele-FolderAdd />
+					</el-icon>
+					新增菜单
+				</el-button>
+			</div>
+			<el-table :data="menuTableData" style="width: 100%" row-key="path" :tree-props="{ children: 'children', hasChildren: 'hasChildren' }">
+				<el-table-column label="菜单名称" show-overflow-tooltip>
+					<template #default="scope">
+						<SvgIcon :name="scope.row.meta.icon" />
+						<span class="ml10">{{ scope.row.meta.title }}</span>
+					</template>
+				</el-table-column>
+				<el-table-column prop="path" label="路由路径" show-overflow-tooltip></el-table-column>
+				<el-table-column label="组件路径" show-overflow-tooltip>
+					<template #default="scope">
+						<span>{{ scope.row.component }}</span>
+					</template>
+				</el-table-column>
+				<el-table-column label="权限标识" show-overflow-tooltip>
+					<template #default="scope">
+						<span>{{ scope.row.meta.roles }}</span>
+					</template>
+				</el-table-column>
+				<el-table-column label="排序" show-overflow-tooltip width="80">
+					<template #default="scope">
+						{{ scope.$index }}
+					</template>
+				</el-table-column>
+				<el-table-column label="类型" show-overflow-tooltip width="80">
+					<template #default="scope">
+						<el-tag type="success" size="small">{{ scope.row.xx }}菜单</el-tag>
+					</template>
+				</el-table-column>
+				<el-table-column label="操作" show-overflow-tooltip width="140">
+					<template #default="scope">
+						<el-button size="small" text type="primary" @click="onOpenMenuDialog('新增')">新增</el-button>
+						<el-button size="small" text type="primary" @click="onOpenMenuDialog('修改',scope.row)">修改</el-button>
+						<el-button size="small" text type="primary" @click="onTabelRowDel(scope.row)">删除</el-button>
+					</template>
+				</el-table-column>
+			</el-table>
+		</el-card>
+		<menuDialog ref="menuDialog"  @getMenuList="getMenuList"/>
+	</div>
+</template>
+
+<script lang="ts">
+import { ref, toRefs, reactive, computed, onMounted, defineComponent } from 'vue';
+import { RouteRecordRaw } from 'vue-router';
+import { ElMessageBox, ElMessage } from 'element-plus';
+import { storeToRefs } from 'pinia';
+import { useRoutesList } from '/@/stores/routesList';
+import menuDialog from '/@/views/system/menu/component/menuDialog.vue';
+import {useMenuApi} from "/@/api/menu";
+import {Session} from "/@/utils/storage";
+import pinia from "/@/stores";
+import {dynamicRoutes} from "/@/router/route";
+
+export default defineComponent({
+	name: 'systemMenu',
+	components: { menuDialog },
+	setup() {
+		const stores = useRoutesList();
+		const { routesList } = storeToRefs(stores);
+		const menuDialog = ref();
+		const state = reactive({
+			menuData:[],
+		});
+		// 获取 vuex 中的路由
+		const menuTableData = computed(() => {
+			return routesList.value;
+		});
+		// 打开新增菜单弹窗
+		const onOpenMenuDialog = (type: string,value: any) => {
+			debugger
+			menuDialog.value.openDialog(type,value);
+		};
+		// 打开编辑菜单弹窗
+		// 删除当前行
+		const onTabelRowDel = (row: RouteRecordRaw) => {
+			ElMessageBox.confirm(`此操作将永久删除路由:${row.path}, 是否继续?`, '提示', {
+				confirmButtonText: '删除',
+				cancelButtonText: '取消',
+				type: 'warning',
+			}).then(() => {
+				ElMessage.success('删除成功');
+			}).catch(() => {});
+		};
+		const getMenuList = async () => {
+			let res = await useMenuApi().getMenuAdmin(Session.get('projectId'))
+			if(res.data.code === '200'){
+				// state.menuData = res.data.data
+				const storesRoutesList = useRoutesList(pinia);
+				storesRoutesList.setRoutesList(res.data.data);
+			}else{
+				ElMessage({
+					type:'warning',
+					message:res.data.msg
+				})
+			}
+		};
+		onMounted( () => {
+			getMenuList()
+		});
+		return {
+			getMenuList,
+			menuDialog,
+			onOpenMenuDialog,
+			menuTableData,
+			onTabelRowDel,
+			...toRefs(state),
+		};
+	},
+});
+</script>
diff --git a/src/views/system/role/component/addRole.vue b/src/views/system/role/component/addRole.vue
new file mode 100644
index 0000000..adb0e83
--- /dev/null
+++ b/src/views/system/role/component/addRole.vue
@@ -0,0 +1,240 @@
+<template>
+	<div class="system-add-role-container">
+		<el-dialog title="新增角色" v-model="isShowDialog" width="769px">
+			<el-form :model="ruleForm" size="default" label-width="90px">
+				<el-row :gutter="35">
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="角色名称">
+							<el-input v-model="ruleForm.roleName" placeholder="请输入角色名称" clearable></el-input>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="角色标识">
+							<template #label>
+								<el-tooltip effect="dark" content="用于 `router/route.ts` meta.roles" placement="top-start">
+									<span>角色标识</span>
+								</el-tooltip>
+							</template>
+							<el-input v-model="ruleForm.roleSign" placeholder="请输入角色标识" clearable></el-input>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="排序">
+							<el-input-number v-model="ruleForm.sort" :min="0" :max="999" controls-position="right" placeholder="请输入排序" class="w100" />
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="角色状态">
+							<el-switch v-model="ruleForm.status" inline-prompt active-text="启" inactive-text="禁"></el-switch>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+						<el-form-item label="角色描述">
+							<el-input v-model="ruleForm.describe" type="textarea" placeholder="请输入角色描述" maxlength="150"></el-input>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+						<el-form-item label="菜单权限">
+							<el-tree :data="menuData" :props="menuProps" show-checkbox class="menu-data-tree" />
+						</el-form-item>
+					</el-col>
+				</el-row>
+			</el-form>
+			<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 lang="ts">
+import { reactive, toRefs, defineComponent } from 'vue';
+
+// 定义接口来定义对象的类型
+interface MenuDataTree {
+	id: number;
+	label: string;
+	children?: MenuDataTree[];
+}
+interface RoleState {
+	isShowDialog: boolean;
+	ruleForm: {
+		roleName: string;
+		roleSign: string;
+		sort: number;
+		status: boolean;
+		describe: string;
+	};
+	menuData: Array<MenuDataTree>;
+	menuProps: {
+		children: string;
+		label: string;
+	};
+}
+
+export default defineComponent({
+	name: 'systemAddRole',
+	setup() {
+		const state = reactive<RoleState>({
+			isShowDialog: false,
+			ruleForm: {
+				roleName: '', // 角色名称
+				roleSign: '', // 角色标识
+				sort: 0, // 排序
+				status: true, // 角色状态
+				describe: '', // 角色描述
+			},
+			menuData: [],
+			menuProps: {
+				children: 'children',
+				label: 'label',
+			},
+		});
+		// 打开弹窗
+		const openDialog = () => {
+			state.isShowDialog = true;
+			getMenuData();
+		};
+		// 关闭弹窗
+		const closeDialog = () => {
+			state.isShowDialog = false;
+		};
+		// 取消
+		const onCancel = () => {
+			closeDialog();
+		};
+		// 新增
+		const onSubmit = () => {
+			closeDialog();
+		};
+		// 获取菜单结构数据
+		const getMenuData = () => {
+			state.menuData = [
+				{
+					id: 1,
+					label: '系统管理',
+					children: [
+						{
+							id: 11,
+							label: '菜单管理',
+							children: [
+								{
+									id: 111,
+									label: '菜单新增',
+								},
+								{
+									id: 112,
+									label: '菜单修改',
+								},
+								{
+									id: 113,
+									label: '菜单删除',
+								},
+								{
+									id: 114,
+									label: '菜单查询',
+								},
+							],
+						},
+						{
+							id: 12,
+							label: '角色管理',
+							children: [
+								{
+									id: 121,
+									label: '角色新增',
+								},
+								{
+									id: 122,
+									label: '角色修改',
+								},
+								{
+									id: 123,
+									label: '角色删除',
+								},
+								{
+									id: 124,
+									label: '角色查询',
+								},
+							],
+						},
+						{
+							id: 13,
+							label: '用户管理',
+							children: [
+								{
+									id: 131,
+									label: '用户新增',
+								},
+								{
+									id: 132,
+									label: '用户修改',
+								},
+								{
+									id: 133,
+									label: '用户删除',
+								},
+								{
+									id: 134,
+									label: '用户查询',
+								},
+							],
+						},
+					],
+				},
+				{
+					id: 2,
+					label: '权限管理',
+					children: [
+						{
+							id: 21,
+							label: '前端控制',
+							children: [
+								{
+									id: 211,
+									label: '页面权限',
+								},
+								{
+									id: 212,
+									label: '页面权限',
+								},
+							],
+						},
+						{
+							id: 22,
+							label: '后端控制',
+							children: [
+								{
+									id: 221,
+									label: '页面权限',
+								},
+							],
+						},
+					],
+				},
+			];
+		};
+		return {
+			openDialog,
+			closeDialog,
+			onCancel,
+			onSubmit,
+			...toRefs(state),
+		};
+	},
+});
+</script>
+
+<style scoped lang="scss">
+.system-add-role-container {
+	.menu-data-tree {
+		width: 100%;
+		border: 1px solid var(--el-border-color);
+		border-radius: var(--el-input-border-radius, var(--el-border-radius-base));
+		padding: 5px;
+	}
+}
+</style>
diff --git a/src/views/system/role/component/editRole.vue b/src/views/system/role/component/editRole.vue
new file mode 100644
index 0000000..fc6fe29
--- /dev/null
+++ b/src/views/system/role/component/editRole.vue
@@ -0,0 +1,242 @@
+<template>
+	<div class="system-edit-role-container">
+		<el-dialog title="修改角色" v-model="isShowDialog" width="769px">
+			<el-form :model="ruleForm" size="default" label-width="90px">
+				<el-row :gutter="35">
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="角色名称">
+							<el-input v-model="ruleForm.roleName" placeholder="请输入角色名称" clearable></el-input>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="角色标识">
+							<template #label>
+								<el-tooltip effect="dark" content="用于 `router/route.ts` meta.roles" placement="top-start">
+									<span>角色标识</span>
+								</el-tooltip>
+							</template>
+							<el-input v-model="ruleForm.roleSign" placeholder="请输入角色标识" clearable></el-input>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="排序">
+							<el-input-number v-model="ruleForm.sort" :min="0" :max="999" controls-position="right" placeholder="请输入排序" class="w100" />
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="角色状态">
+							<el-switch v-model="ruleForm.status" inline-prompt active-text="启" inactive-text="禁"></el-switch>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+						<el-form-item label="角色描述">
+							<el-input v-model="ruleForm.describe" type="textarea" placeholder="请输入角色描述" maxlength="150"></el-input>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+						<el-form-item label="菜单权限">
+							<el-tree :data="menuData" :props="menuProps" :default-checked-keys="[112, 113]" node-key="id" show-checkbox class="menu-data-tree" />
+						</el-form-item>
+					</el-col>
+				</el-row>
+			</el-form>
+			<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 lang="ts">
+import { reactive, toRefs, defineComponent } from 'vue';
+
+// 定义接口来定义对象的类型
+interface MenuDataTree {
+	id: number;
+	label: string;
+	children?: MenuDataTree[];
+}
+interface DialogRow {
+	roleName: string;
+	roleSign: string;
+	sort: number;
+	status: boolean;
+	describe: string;
+}
+interface RoleState {
+	isShowDialog: boolean;
+	ruleForm: DialogRow;
+	menuData: Array<MenuDataTree>;
+	menuProps: {
+		children: string;
+		label: string;
+	};
+}
+
+export default defineComponent({
+	name: 'systemEditRole',
+	setup() {
+		const state = reactive<RoleState>({
+			isShowDialog: false,
+			ruleForm: {
+				roleName: '', // 角色名称
+				roleSign: '', // 角色标识
+				sort: 0, // 排序
+				status: true, // 角色状态
+				describe: '', // 角色描述
+			},
+			menuData: [],
+			menuProps: {
+				children: 'children',
+				label: 'label',
+			},
+		});
+		// 打开弹窗
+		const openDialog = (row: DialogRow) => {
+			state.ruleForm = row;
+			state.isShowDialog = true;
+			getMenuData();
+		};
+		// 关闭弹窗
+		const closeDialog = () => {
+			state.isShowDialog = false;
+		};
+		// 取消
+		const onCancel = () => {
+			closeDialog();
+		};
+		// 新增
+		const onSubmit = () => {
+			closeDialog();
+		};
+		// 获取菜单结构数据
+		const getMenuData = () => {
+			state.menuData = [
+				{
+					id: 1,
+					label: '系统管理',
+					children: [
+						{
+							id: 11,
+							label: '菜单管理',
+							children: [
+								{
+									id: 111,
+									label: '菜单新增',
+								},
+								{
+									id: 112,
+									label: '菜单修改',
+								},
+								{
+									id: 113,
+									label: '菜单删除',
+								},
+								{
+									id: 114,
+									label: '菜单查询',
+								},
+							],
+						},
+						{
+							id: 12,
+							label: '角色管理',
+							children: [
+								{
+									id: 121,
+									label: '角色新增',
+								},
+								{
+									id: 122,
+									label: '角色修改',
+								},
+								{
+									id: 123,
+									label: '角色删除',
+								},
+								{
+									id: 124,
+									label: '角色查询',
+								},
+							],
+						},
+						{
+							id: 13,
+							label: '用户管理',
+							children: [
+								{
+									id: 131,
+									label: '用户新增',
+								},
+								{
+									id: 132,
+									label: '用户修改',
+								},
+								{
+									id: 133,
+									label: '用户删除',
+								},
+								{
+									id: 134,
+									label: '用户查询',
+								},
+							],
+						},
+					],
+				},
+				{
+					id: 2,
+					label: '权限管理',
+					children: [
+						{
+							id: 21,
+							label: '前端控制',
+							children: [
+								{
+									id: 211,
+									label: '页面权限',
+								},
+								{
+									id: 212,
+									label: '页面权限',
+								},
+							],
+						},
+						{
+							id: 22,
+							label: '后端控制',
+							children: [
+								{
+									id: 221,
+									label: '页面权限',
+								},
+							],
+						},
+					],
+				},
+			];
+		};
+		return {
+			openDialog,
+			closeDialog,
+			onCancel,
+			onSubmit,
+			...toRefs(state),
+		};
+	},
+});
+</script>
+
+<style scoped lang="scss">
+.system-edit-role-container {
+	.menu-data-tree {
+		width: 100%;
+		border: 1px solid var(--el-border-color);
+		border-radius: var(--el-input-border-radius, var(--el-border-radius-base));
+		padding: 5px;
+	}
+}
+</style>
diff --git a/src/views/system/role/index.vue b/src/views/system/role/index.vue
new file mode 100644
index 0000000..7835197
--- /dev/null
+++ b/src/views/system/role/index.vue
@@ -0,0 +1,167 @@
+<template>
+	<div class="system-role-container">
+		<el-card shadow="hover">
+			<div class="system-user-search mb15">
+				<el-input size="default" placeholder="请输入角色名称" style="max-width: 180px"> </el-input>
+				<el-button size="default" type="primary" class="ml10" @click="handleSearch">
+					<el-icon>
+						<ele-Search />
+					</el-icon>
+					查询
+				</el-button>
+				<el-button size="default" type="success" class="ml10" @click="onOpenAddRole">
+					<el-icon>
+						<ele-FolderAdd />
+					</el-icon>
+					新增角色
+				</el-button>
+			</div>
+			<el-table :data="tableData.data" style="width: 100%">
+				<el-table-column type="index" label="序号" width="60" />
+				<el-table-column prop="name" label="角色名称" show-overflow-tooltip></el-table-column>
+				<el-table-column prop="code" label="角色标识" show-overflow-tooltip></el-table-column>
+				<el-table-column prop="sort" label="排序" show-overflow-tooltip></el-table-column>
+				<el-table-column prop="status" label="角色状态" show-overflow-tooltip>
+					<template #default="scope">
+						<el-tag type="success" v-if="scope.row.status">启用</el-tag>
+						<el-tag type="info" v-else>禁用</el-tag>
+					</template>
+				</el-table-column>
+				<el-table-column prop="info" label="角色描述" show-overflow-tooltip></el-table-column>
+				<el-table-column prop="createTime" label="创建时间" show-overflow-tooltip></el-table-column>
+				<el-table-column label="操作" width="100">
+					<template #default="scope">
+						<el-button :disabled="scope.row.roleName === '超级管理员'" size="small" text type="primary" @click="onOpenEditRole(scope.row)"
+							>修改</el-button
+						>
+						<el-button :disabled="scope.row.roleName === '超级管理员'" size="small" text type="primary" @click="onRowDel(scope.row)">删除</el-button>
+					</template>
+				</el-table-column>
+			</el-table>
+			<el-pagination
+				@size-change="onHandleSizeChange"
+				@current-change="onHandleCurrentChange"
+				class="mt15"
+				:pager-count="5"
+				:page-sizes="[10, 20, 30]"
+				v-model:current-page="tableData.param.pageNum"
+				background
+				v-model:page-size="tableData.param.pageSize"
+				layout="total, sizes, prev, pager, next, jumper"
+				:total="tableData.total"
+			>
+			</el-pagination>
+		</el-card>
+		<AddRole ref="addRoleRef" />
+		<EditRole ref="editRoleRef" />
+	</div>
+</template>
+
+<script lang="ts">
+import { toRefs, reactive, onMounted, ref, defineComponent } from 'vue';
+import { ElMessageBox, ElMessage } from 'element-plus';
+import AddRole from '/@/views/system/role/component/addRole.vue';
+import EditRole from '/@/views/system/role/component/editRole.vue';
+import {useRoleApi} from "/@/api/role";
+
+// 定义接口来定义对象的类型
+interface TableData {
+	roleName: string;
+	roleSign: string;
+	describe: string;
+	sort: number;
+	status: boolean;
+	createTime: string;
+}
+interface TableDataState {
+	tableData: {
+		data: Array<TableData>;
+		total: number;
+		loading: boolean;
+		param: {
+			pageNum: number;
+			pageSize: number;
+		};
+	};
+}
+
+export default defineComponent({
+	name: 'systemRole',
+	components: { AddRole, EditRole },
+	setup() {
+		const addRoleRef = ref();
+		const editRoleRef = ref();
+		const state = reactive<TableDataState>({
+			tableData: {
+				data: [],
+				total: 0,
+				loading: false,
+				param: {
+					pageNum: 1,
+					pageSize: 10,
+				},
+			},
+		});
+		// 初始化表格数据
+		const initTableData = async () => {
+			let res = await useRoleApi().getRoleList()
+			if(res.data.code === '200'){
+				state.tableData.data = res.data.data;
+			}else{
+				ElMessage({
+					type:'warning',
+					message:res.data.msg
+				})
+			}
+
+		};
+		// 打开新增角色弹窗
+		const onOpenAddRole = () => {
+			addRoleRef.value.openDialog();
+		};
+		// 打开修改角色弹窗
+		const onOpenEditRole = (row: Object) => {
+			editRoleRef.value.openDialog(row);
+		};
+		// 删除角色
+		const onRowDel = (row: any) => {
+			ElMessageBox.confirm(`此操作将永久删除角色名称:“${row.roleName}”,是否继续?`, '提示', {
+				confirmButtonText: '确认',
+				cancelButtonText: '取消',
+				type: 'warning',
+			})
+				.then(() => {
+					ElMessage.success('删除成功');
+				})
+				.catch(() => {});
+		};
+		const handleSearch = () => {
+			debugger
+			initTableData()
+		}
+		// 分页改变
+		const onHandleSizeChange = (val: number) => {
+			state.tableData.param.pageSize = val;
+		};
+		// 分页改变
+		const onHandleCurrentChange = (val: number) => {
+			state.tableData.param.pageNum = val;
+		};
+		// 页面加载时
+		onMounted(() => {
+			initTableData();
+		});
+		return {
+			addRoleRef,
+			editRoleRef,
+			onOpenAddRole,
+			onOpenEditRole,
+			onRowDel,
+			onHandleSizeChange,
+			onHandleCurrentChange,
+			handleSearch,
+			...toRefs(state),
+		};
+	},
+});
+</script>
diff --git a/src/views/system/user/component/addUser.vue b/src/views/system/user/component/addUser.vue
new file mode 100644
index 0000000..4a78153
--- /dev/null
+++ b/src/views/system/user/component/addUser.vue
@@ -0,0 +1,200 @@
+<template>
+	<div class="system-add-user-container">
+		<el-dialog title="新增用户" v-model="isShowDialog" width="769px">
+			<el-form :model="ruleForm" size="default" label-width="90px">
+				<el-row :gutter="35">
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="账户名称">
+							<el-input v-model="ruleForm.userName" placeholder="请输入账户名称" clearable></el-input>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="用户昵称">
+							<el-input v-model="ruleForm.userNickname" placeholder="请输入用户昵称" clearable></el-input>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="关联角色">
+							<el-select v-model="ruleForm.roleSign" placeholder="请选择" clearable class="w100">
+								<el-option label="超级管理员" value="admin"></el-option>
+								<el-option label="普通用户" value="common"></el-option>
+							</el-select>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="部门">
+							<el-cascader
+								:options="deptData"
+								:props="{ checkStrictly: true, value: 'deptName', label: 'deptName' }"
+								placeholder="请选择部门"
+								clearable
+								class="w100"
+								v-model="ruleForm.department"
+							>
+								<template #default="{ node, data }">
+									<span>{{ data.deptName }}</span>
+									<span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
+								</template>
+							</el-cascader>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="手机号">
+							<el-input v-model="ruleForm.phone" placeholder="请输入手机号" clearable></el-input>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="邮箱">
+							<el-input v-model="ruleForm.email" placeholder="请输入" clearable></el-input>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="性别">
+							<el-select v-model="ruleForm.sex" placeholder="请选择" clearable class="w100">
+								<el-option label="男" value="男"></el-option>
+								<el-option label="女" value="女"></el-option>
+							</el-select>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="账户密码">
+							<el-input v-model="ruleForm.password" placeholder="请输入" type="password" clearable></el-input>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="账户过期">
+							<el-date-picker v-model="ruleForm.overdueTime" type="date" placeholder="请选择" class="w100"> </el-date-picker>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="用户状态">
+							<el-switch v-model="ruleForm.status" inline-prompt active-text="启" inactive-text="禁"></el-switch>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+						<el-form-item label="用户描述">
+							<el-input v-model="ruleForm.describe" type="textarea" placeholder="请输入用户描述" maxlength="150"></el-input>
+						</el-form-item>
+					</el-col>
+				</el-row>
+			</el-form>
+			<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 lang="ts">
+import { reactive, toRefs, onMounted, defineComponent } from 'vue';
+
+// 定义接口来定义对象的类型
+interface DeptData {
+	deptName: string;
+	createTime: string;
+	status: boolean;
+	sort: number | string;
+	describe: string;
+	id: number;
+	children?: DeptData[];
+}
+interface UserState {
+	isShowDialog: boolean;
+	ruleForm: {
+		userName: string;
+		userNickname: string;
+		roleSign: string;
+		department: any;
+		phone: string;
+		email: string;
+		sex: string;
+		password: string;
+		overdueTime: string;
+		status: boolean;
+		describe: string;
+	};
+	deptData: Array<DeptData>;
+}
+
+export default defineComponent({
+	name: 'systemAddUser',
+	setup() {
+		const state = reactive<UserState>({
+			isShowDialog: false,
+			ruleForm: {
+				userName: '', // 账户名称
+				userNickname: '', // 用户昵称
+				roleSign: '', // 关联角色
+				department: [], // 部门
+				phone: '', // 手机号
+				email: '', // 邮箱
+				sex: '', // 性别
+				password: '', // 账户密码
+				overdueTime: '', // 账户过期
+				status: true, // 用户状态
+				describe: '', // 用户描述
+			},
+			deptData: [], // 部门数据
+		});
+		// 打开弹窗
+		const openDialog = () => {
+			state.isShowDialog = true;
+		};
+		// 关闭弹窗
+		const closeDialog = () => {
+			state.isShowDialog = false;
+		};
+		// 取消
+		const onCancel = () => {
+			closeDialog();
+		};
+		// 新增
+		const onSubmit = () => {
+			closeDialog();
+		};
+		// 初始化部门数据
+		const initTableData = () => {
+			state.deptData.push({
+				deptName: 'vueNextAdmin',
+				createTime: new Date().toLocaleString(),
+				status: true,
+				sort: Math.random(),
+				describe: '顶级部门',
+				id: Math.random(),
+				children: [
+					{
+						deptName: 'IT外包服务',
+						createTime: new Date().toLocaleString(),
+						status: true,
+						sort: Math.random(),
+						describe: '总部',
+						id: Math.random(),
+					},
+					{
+						deptName: '资本控股',
+						createTime: new Date().toLocaleString(),
+						status: true,
+						sort: Math.random(),
+						describe: '分部',
+						id: Math.random(),
+					},
+				],
+			});
+		};
+		// 页面加载时
+		onMounted(() => {
+			initTableData();
+		});
+		return {
+			openDialog,
+			closeDialog,
+			onCancel,
+			onSubmit,
+			...toRefs(state),
+		};
+	},
+});
+</script>
diff --git a/src/views/system/user/component/editUser.vue b/src/views/system/user/component/editUser.vue
new file mode 100644
index 0000000..b7a0793
--- /dev/null
+++ b/src/views/system/user/component/editUser.vue
@@ -0,0 +1,202 @@
+<template>
+	<div class="system-edit-user-container">
+		<el-dialog title="修改用户" v-model="isShowDialog" width="769px">
+			<el-form :model="ruleForm" size="default" label-width="90px">
+				<el-row :gutter="35">
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="账户名称">
+							<el-input v-model="ruleForm.userName" placeholder="请输入账户名称" clearable></el-input>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="用户昵称">
+							<el-input v-model="ruleForm.userNickname" placeholder="请输入用户昵称" clearable></el-input>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="关联角色">
+							<el-select v-model="ruleForm.roleSign" placeholder="请选择" clearable class="w100">
+								<el-option label="超级管理员" value="admin"></el-option>
+								<el-option label="普通用户" value="common"></el-option>
+							</el-select>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="部门">
+							<el-cascader
+								:options="deptData"
+								:props="{ checkStrictly: true, value: 'deptName', label: 'deptName' }"
+								placeholder="请选择部门"
+								clearable
+								class="w100"
+								v-model="ruleForm.department"
+							>
+								<template #default="{ node, data }">
+									<span>{{ data.deptName }}</span>
+									<span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
+								</template>
+							</el-cascader>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="手机号">
+							<el-input v-model="ruleForm.phone" placeholder="请输入手机号" clearable></el-input>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="邮箱">
+							<el-input v-model="ruleForm.email" placeholder="请输入" clearable></el-input>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="性别">
+							<el-select v-model="ruleForm.sex" placeholder="请选择" clearable class="w100">
+								<el-option label="男" value="男"></el-option>
+								<el-option label="女" value="女"></el-option>
+							</el-select>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="账户密码">
+							<el-input v-model="ruleForm.password" placeholder="请输入" type="password" clearable></el-input>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="账户过期">
+							<el-date-picker v-model="ruleForm.overdueTime" type="date" placeholder="请选择" class="w100"> </el-date-picker>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="用户状态">
+							<el-switch v-model="ruleForm.status" inline-prompt active-text="启" inactive-text="禁"></el-switch>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+						<el-form-item label="用户描述">
+							<el-input v-model="ruleForm.describe" type="textarea" placeholder="请输入用户描述" maxlength="150"></el-input>
+						</el-form-item>
+					</el-col>
+				</el-row>
+			</el-form>
+			<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 lang="ts">
+import { reactive, toRefs, onMounted, defineComponent } from 'vue';
+
+// 定义接口来定义对象的类型
+interface DeptData {
+	deptName: string;
+	createTime: string;
+	status: boolean;
+	sort: number | string;
+	describe: string;
+	id: number;
+	children?: DeptData[];
+}
+interface RuleFormRow {
+	userName: string;
+	userNickname: string;
+	roleSign: string;
+	department: any;
+	phone: string;
+	email: string;
+	sex: string;
+	password: string;
+	overdueTime: string;
+	status: boolean;
+	describe: string;
+}
+interface UserState {
+	isShowDialog: boolean;
+	ruleForm: RuleFormRow;
+	deptData: Array<DeptData>;
+}
+
+export default defineComponent({
+	name: 'systemEditUser',
+	setup() {
+		const state = reactive<UserState>({
+			isShowDialog: false,
+			ruleForm: {
+				userName: '', // 账户名称
+				userNickname: '', // 用户昵称
+				roleSign: '', // 关联角色
+				department: [], // 部门
+				phone: '', // 手机号
+				email: '', // 邮箱
+				sex: '', // 性别
+				password: '', // 账户密码
+				overdueTime: '', // 账户过期
+				status: true, // 用户状态
+				describe: '', // 用户描述
+			},
+			deptData: [], // 部门数据
+		});
+		// 打开弹窗
+		const openDialog = (row: RuleFormRow) => {
+			state.ruleForm = row;
+			state.isShowDialog = true;
+		};
+		// 关闭弹窗
+		const closeDialog = () => {
+			state.isShowDialog = false;
+		};
+		// 取消
+		const onCancel = () => {
+			closeDialog();
+		};
+		// 新增
+		const onSubmit = () => {
+			closeDialog();
+		};
+		// 初始化部门数据
+		const initTableData = () => {
+			state.deptData.push({
+				deptName: 'vueNextAdmin',
+				createTime: new Date().toLocaleString(),
+				status: true,
+				sort: Math.random(),
+				describe: '顶级部门',
+				id: Math.random(),
+				children: [
+					{
+						deptName: 'IT外包服务',
+						createTime: new Date().toLocaleString(),
+						status: true,
+						sort: Math.random(),
+						describe: '总部',
+						id: Math.random(),
+					},
+					{
+						deptName: '资本控股',
+						createTime: new Date().toLocaleString(),
+						status: true,
+						sort: Math.random(),
+						describe: '分部',
+						id: Math.random(),
+					},
+				],
+			});
+		};
+		// 页面加载时
+		onMounted(() => {
+			initTableData();
+		});
+		return {
+			openDialog,
+			closeDialog,
+			onCancel,
+			onSubmit,
+			...toRefs(state),
+		};
+	},
+});
+</script>
diff --git a/src/views/system/user/index.vue b/src/views/system/user/index.vue
new file mode 100644
index 0000000..a120c59
--- /dev/null
+++ b/src/views/system/user/index.vue
@@ -0,0 +1,177 @@
+<template>
+	<div class="system-user-container">
+		<el-card shadow="hover">
+			<div class="system-user-search mb15">
+				<el-input size="default" placeholder="请输入用户名称" style="max-width: 180px"> </el-input>
+				<el-button size="default" type="primary" class="ml10">
+					<el-icon>
+						<ele-Search />
+					</el-icon>
+					查询
+				</el-button>
+				<el-button size="default" type="success" class="ml10" @click="onOpenAddUser">
+					<el-icon>
+						<ele-FolderAdd />
+					</el-icon>
+					新增用户
+				</el-button>
+			</div>
+			<el-table :data="tableData.data" style="width: 100%">
+				<el-table-column type="index" label="序号" width="60" />
+				<el-table-column prop="userName" label="账户名称" show-overflow-tooltip></el-table-column>
+				<el-table-column prop="userNickname" label="用户昵称" show-overflow-tooltip></el-table-column>
+				<el-table-column prop="roleSign" label="关联角色" show-overflow-tooltip></el-table-column>
+				<el-table-column prop="department" label="部门" show-overflow-tooltip></el-table-column>
+				<el-table-column prop="phone" label="手机号" show-overflow-tooltip></el-table-column>
+				<el-table-column prop="email" label="邮箱" show-overflow-tooltip></el-table-column>
+				<el-table-column prop="status" label="用户状态" show-overflow-tooltip>
+					<template #default="scope">
+						<el-tag type="success" v-if="scope.row.status">启用</el-tag>
+						<el-tag type="info" v-else>禁用</el-tag>
+					</template>
+				</el-table-column>
+				<el-table-column prop="describe" label="用户描述" show-overflow-tooltip></el-table-column>
+				<el-table-column prop="createTime" label="创建时间" show-overflow-tooltip></el-table-column>
+				<el-table-column label="操作" width="100">
+					<template #default="scope">
+						<el-button :disabled="scope.row.userName === 'admin'" size="small" text type="primary" @click="onOpenEditUser(scope.row)">修改</el-button>
+						<el-button :disabled="scope.row.userName === 'admin'" size="small" text type="primary" @click="onRowDel(scope.row)">删除</el-button>
+					</template>
+				</el-table-column>
+			</el-table>
+			<el-pagination
+				@size-change="onHandleSizeChange"
+				@current-change="onHandleCurrentChange"
+				class="mt15"
+				:pager-count="5"
+				:page-sizes="[10, 20, 30]"
+				v-model:current-page="tableData.param.pageNum"
+				background
+				v-model:page-size="tableData.param.pageSize"
+				layout="total, sizes, prev, pager, next, jumper"
+				:total="tableData.total"
+			>
+			</el-pagination>
+		</el-card>
+		<AddUer ref="addUserRef" />
+		<EditUser ref="editUserRef" />
+	</div>
+</template>
+
+<script lang="ts">
+import { toRefs, reactive, onMounted, ref, defineComponent } from 'vue';
+import { ElMessageBox, ElMessage } from 'element-plus';
+import AddUer from '/@/views/system/user/component/addUser.vue';
+import EditUser from '/@/views/system/user/component/editUser.vue';
+
+// 定义接口来定义对象的类型
+interface TableDataRow {
+	userName: string;
+	userNickname: string;
+	roleSign: string;
+	department: string[];
+	phone: string;
+	email: string;
+	sex: string;
+	password: string;
+	overdueTime: Date;
+	status: boolean;
+	describe: string;
+	createTime: string;
+}
+interface TableDataState {
+	tableData: {
+		data: Array<TableDataRow>;
+		total: number;
+		loading: boolean;
+		param: {
+			pageNum: number;
+			pageSize: number;
+		};
+	};
+}
+
+export default defineComponent({
+	name: 'systemUser',
+	components: { AddUer, EditUser },
+	setup() {
+		const addUserRef = ref();
+		const editUserRef = ref();
+		const state = reactive<TableDataState>({
+			tableData: {
+				data: [],
+				total: 0,
+				loading: false,
+				param: {
+					pageNum: 1,
+					pageSize: 10,
+				},
+			},
+		});
+		// 初始化表格数据
+		const initTableData = () => {
+			const data: Array<TableDataRow> = [];
+			for (let i = 0; i < 2; i++) {
+				data.push({
+					userName: i === 0 ? 'admin' : 'test',
+					userNickname: i === 0 ? '我是管理员' : '我是普通用户',
+					roleSign: i === 0 ? 'admin' : 'common',
+					department: i === 0 ? ['vueNextAdmin', 'IT外包服务'] : ['vueNextAdmin', '资本控股'],
+					phone: '12345678910',
+					email: 'vueNextAdmin@123.com',
+					sex: '女',
+					password: '123456',
+					overdueTime: new Date(),
+					status: true,
+					describe: i === 0 ? '不可删除' : '测试用户',
+					createTime: new Date().toLocaleString(),
+				});
+			}
+			state.tableData.data = data;
+			state.tableData.total = state.tableData.data.length;
+		};
+		// 打开新增用户弹窗
+		const onOpenAddUser = () => {
+			addUserRef.value.openDialog();
+		};
+		// 打开修改用户弹窗
+		const onOpenEditUser = (row: TableDataRow) => {
+			editUserRef.value.openDialog(row);
+		};
+		// 删除用户
+		const onRowDel = (row: TableDataRow) => {
+			ElMessageBox.confirm(`此操作将永久删除账户名称:“${row.userName}”,是否继续?`, '提示', {
+				confirmButtonText: '确认',
+				cancelButtonText: '取消',
+				type: 'warning',
+			})
+				.then(() => {
+					ElMessage.success('删除成功');
+				})
+				.catch(() => {});
+		};
+		// 分页改变
+		const onHandleSizeChange = (val: number) => {
+			state.tableData.param.pageSize = val;
+		};
+		// 分页改变
+		const onHandleCurrentChange = (val: number) => {
+			state.tableData.param.pageNum = val;
+		};
+		// 页面加载时
+		onMounted(() => {
+			initTableData();
+		});
+		return {
+			addUserRef,
+			editUserRef,
+			onOpenAddUser,
+			onOpenEditUser,
+			onRowDel,
+			onHandleSizeChange,
+			onHandleCurrentChange,
+			...toRefs(state),
+		};
+	},
+});
+</script>
diff --git a/src/views/test/index.vue b/src/views/test/index.vue
new file mode 100644
index 0000000..827727e
--- /dev/null
+++ b/src/views/test/index.vue
@@ -0,0 +1,13 @@
+<template>
+    
+</template>
+
+<script>
+    export default {
+        name: "index"
+    }
+</script>
+
+<style scoped>
+
+</style>
diff --git a/src/views/visualizing/demo1.vue b/src/views/visualizing/demo1.vue
new file mode 100644
index 0000000..cfc6166
--- /dev/null
+++ b/src/views/visualizing/demo1.vue
@@ -0,0 +1,1278 @@
+<template>
+	<div class="visualizing-demo1">
+		<!-- 地图 -->
+		<div ref="visualizingDemo1" style="height: 100%"></div>
+		<div class="visualizing-container">
+			<!-- 头部 -->
+			<div class="visualizing-container-head">
+				<div class="visualizing-container-head-left">
+					<div class="visualizing-container-head-left-text">
+						<div class="visualizing-container-head-left-text-box">{{ time.txt }}</div>
+					</div>
+				</div>
+				<div class="visualizing-container-head-center">
+					<div class="visualizing-container-head-center-box">
+						<div class="visualizing-container-head-center-maintitle">深圳市xxx软件科技有限公司</div>
+						<div class="visualizing-container-head-center-subtitle">旅游经济</div>
+					</div>
+				</div>
+				<div class="visualizing-container-head-right">
+					<div class="visualizing-container-head-right-text">
+						<div class="visualizing-container-head-right-text-box">🌤 多云转晴东南风 26~30℃</div>
+					</div>
+				</div>
+			</div>
+
+			<!-- 图表左侧 -->
+			<div class="visualizing-container-content-left">
+				<div class="visualizing-container-content-left-flex">
+					<div class="visualizing-container-title">
+						<i class="el-icon-s-shop"></i>
+						<span>产业概况</span>
+					</div>
+					<hr class="visualizing-container-title-colorful" />
+					<div ref="visualizingContentLeftTop" style="height: 100%"></div>
+				</div>
+				<div class="visualizing-container-content-left-flex">
+					<div class="visualizing-container-title">
+						<i class="el-icon-s-promotion"></i>
+						<span>A级风景区对比</span>
+					</div>
+					<hr class="visualizing-container-title-colorful" />
+					<div ref="visualizingContentLeftBottom" style="height: 100%"></div>
+				</div>
+			</div>
+
+			<!-- 图表中间 -->
+			<div class="visualizing-container-content-center">
+				<div class="visualizing-container-content-center-bottom">
+					<div class="visualizing-container-content-center-bottom-flex">
+						<div class="visualizing-container-title">
+							<i class="el-icon-s-custom"></i>
+							<span>游客过夜情况</span>
+						</div>
+						<hr class="visualizing-container-title-colorful" />
+						<div ref="visualizingContentCenterTop" style="height: 100%"></div>
+					</div>
+					<div class="visualizing-container-content-center-bottom-flex">
+						<div class="visualizing-container-title">
+							<i class="el-icon-s-flag"></i>
+							<span>游客驻留时长</span>
+						</div>
+						<hr class="visualizing-container-title-colorful" />
+						<div ref="visualizingContentCenterBottom" style="height: 100%"></div>
+					</div>
+				</div>
+			</div>
+
+			<!-- 图表右侧 -->
+			<div class="visualizing-container-content-right">
+				<div class="visualizing-container-content-right-flex">
+					<div class="visualizing-container-title">
+						<i class="el-icon-s-marketing"></i>
+						<span>当日游客趋势分析</span>
+					</div>
+					<hr class="visualizing-container-title-colorful" />
+					<div ref="visualizingContentRightTop" style="height: 100%"></div>
+				</div>
+				<div class="visualizing-container-content-right-flex">
+					<div class="visualizing-container-title">
+						<i class="el-icon-s-data"></i>
+						<span>当月游客趋势分析</span>
+					</div>
+					<hr class="visualizing-container-title-colorful" />
+					<div ref="visualizingContentRightBottom" style="height: 100%"></div>
+				</div>
+			</div>
+		</div>
+	</div>
+</template>
+
+<script lang="ts">
+import { toRefs, reactive, onMounted, onUnmounted, getCurrentInstance, defineComponent } from 'vue';
+import * as echarts from 'echarts';
+import 'echarts/extension/bmap/bmap';
+import { formatDate } from '/@/utils/formatTime';
+import { NextLoading } from '/@/utils/loading';
+import { echartsMapList, echartsMapData, echartsMapImgs } from './mock/demo1';
+
+// 定义接口来定义对象的类型
+interface Demo1State {
+	visualizingDemo1: any;
+	echartsMapList: any;
+	echartsMapData: any;
+	echartsMapImgs: any;
+	time: any;
+	myCharts: any[];
+}
+
+export default defineComponent({
+	name: 'visualizingLinkDemo1',
+	setup() {
+		const { proxy } = <any>getCurrentInstance();
+		const state = reactive<Demo1State>({
+			visualizingDemo1: null,
+			echartsMapList,
+			echartsMapData,
+			echartsMapImgs,
+			time: {
+				txt: '',
+				fun: 0,
+			},
+			myCharts: [],
+		});
+		// 初始化时间
+		const initTime = () => {
+			state.time.txt = formatDate(new Date(), 'YYYY-mm-dd HH:MM:SS WWW QQQQ ZZZ');
+			state.time.fun = window.setInterval(() => {
+				state.time.txt = formatDate(new Date(), 'YYYY-mm-dd HH:MM:SS WWW QQQQ ZZZ');
+			}, 1000);
+		};
+		// echartsMap 将坐标信息和对应物理量的值合在一起
+		const convertData = (data: any) => {
+			let res = [];
+			for (let i = 0; i < data.length; i++) {
+				let geoCoord = state.echartsMapData[data[i].name];
+				if (geoCoord) {
+					res.push({
+						name: data[i].name,
+						value: geoCoord.concat(data[i].value),
+					});
+				}
+			}
+			return res;
+		};
+		// 初始化 echartsMap(地图上的点)
+		const initEchartsMap = () => {
+			const myChart = echarts.init(<HTMLElement>state.visualizingDemo1);
+			const option = {
+				tooltip: {
+					trigger: 'item',
+					formatter(params: any) {
+						// 自定义鼠标放入样式
+						let item = state.echartsMapImgs.find((v: any) => v.name === params.name);
+						let html = `<div style="width: 240px">
+							<div style="display: flex; align-items: center">
+								<img src="${item?.url}" style="width: 50px; height: 50px; border-radius: 100%; position: relative; border: 4px solid #ffffff; margin-left: -4px" />
+								<div
+									style="
+										background: #51c1ff;
+										width: 100%;
+										height: 32px;
+										margin-left: -14px;
+										display: flex;
+										align-items: center;
+										padding-left: 20px;
+										color: #fff;
+									"
+								>
+									${item?.name}
+								</div>
+							</div>
+							<div style="margin-top: 10px; font-size: 12px">
+								<div style="width: 61px"><i class="el-icon-location-information" style="margin-right: 5px"></i>地址:</div>
+								<div style="flex: 1; white-space: pre-wrap; word-break: break-all; margin-top: 5px; color: #333">${item?.add}</div>
+							</div>
+							<div style="margin-top: 10px; font-size: 12px">
+								<div style="width: 61px"><i class="el-icon-chat-dot-round" style="margin-right: 5px"></i>概括:</div>
+								<div style="flex: 1; white-space: pre-wrap; word-break: break-all; margin-top: 5px; color: #333">${item?.dec}</div>
+							</div>
+						</div>`;
+						return html;
+					},
+				},
+				color: ['#ea7ccc'],
+				bmap: {
+					center: [114.064524, 22.549225],
+					zoom: 11,
+					roam: true,
+					mapStyle: {},
+				},
+				series: [
+					{
+						name: '门票收入',
+						type: 'scatter',
+						coordinateSystem: 'bmap',
+						data: convertData(state.echartsMapList),
+						symbolSize: function (val: any) {
+							return val[2] / 10;
+						},
+						encode: {
+							value: 2,
+						},
+						label: {
+							formatter: '{b}',
+							position: 'right',
+							show: false,
+						},
+						emphasis: {
+							label: {
+								show: true,
+							},
+						},
+					},
+					{
+						name: '门票收入',
+						type: 'effectScatter',
+						coordinateSystem: 'bmap',
+						data: convertData(
+							state.echartsMapList
+								.sort(function (a: any, b: any) {
+									return b.value - a.value;
+								})
+								.slice(0, 6)
+						),
+						symbolSize: function (val: any) {
+							return val[2] / 10;
+						},
+						encode: {
+							value: 2,
+						},
+						showEffectOn: 'render',
+						rippleEffect: {
+							brushType: 'stroke',
+						},
+						hoverAnimation: true,
+						label: {
+							formatter: '{b}',
+							position: 'right',
+							show: true,
+						},
+						itemStyle: {
+							shadowBlur: 100,
+							shadowColor: '#333',
+						},
+						zlevel: 1,
+					},
+				],
+			};
+			myChart.setOption(option);
+			state.myCharts.push(myChart);
+
+			// 地图
+			const map = (<any>myChart).getModel().getComponent('bmap').getBMap();
+			// BMAP_NORMAL_MAP :此地图类型展示普通街道视图
+			// BMAP_PERSPECTIVE_MAP :此地图类型展示透视图像视图。(这个还不会用)
+			// BMAP_SATELLITE_MAP:卫星地图 (没有坐标, 绿绿的一片的卫星地图)
+			// BMAP_HYBRID_MAP:混合地图 (既有坐标,也是绿绿的一片的卫星地图)
+			// eslint-disable-next-line no-undef
+			map.setMapType(BMAP_SATELLITE_MAP);
+			// eslint-disable-next-line no-undef
+			let bdary = new BMap.Boundary();
+			// 获取行政区域
+			bdary.get('深圳', function (rs: any) {
+				// 行政区域的点有多少个
+				let count = rs.boundaries.length;
+				for (let i = 0; i < count; i++) {
+					// eslint-disable-next-line no-undef
+					let ply = new BMap.Polygon(rs.boundaries[i], {
+						// 设置多边形边线线粗
+						strokeWeight: 4,
+						// 设置多边形边线透明度0-1
+						strokeOpacity: 1,
+						// 设置多边形边线样式为实线或虚线,取值 solid 或 dashed
+						StrokeStyle: 'dashed',
+						// 设置多边形边线颜色
+						strokeColor: '#febb50',
+						// 设置多边形填充颜色
+						fillColor: '',
+					});
+					// 建立多边形覆盖物
+					// 添加覆盖物
+					map.addOverlay(ply);
+					// 调整视野
+					map.setViewport(ply.getPath());
+				}
+				// 初始化地图,设置中心点坐标和地图级别
+				// new BMap.Point('深圳市', 11)
+				// eslint-disable-next-line no-undef
+				map.centerAndZoom(new BMap.Point(114.064524, 22.549225), 11);
+			});
+		};
+		// 产业概况
+		const initVisualizingContentLeftTop = () => {
+			const myChart = echarts.init(proxy.$refs.visualizingContentLeftTop);
+			const option = {
+				grid: {
+					top: 50,
+					right: 0,
+					bottom: 50,
+					left: 30,
+				},
+				tooltip: {
+					trigger: 'axis',
+				},
+				xAxis: {
+					data: ['1月', '2月', '3月', '4月', '5月', '6月'],
+					axisLine: {
+						lineStyle: {
+							color: 'rgba(22, 207, 208, 0.1)',
+							width: 1,
+						},
+					},
+					axisTick: {
+						show: false,
+					},
+					axisLabel: {
+						color: '#16cfd0',
+					},
+				},
+				yAxis: [
+					{
+						type: 'value',
+						name: '价格',
+						axisLine: {
+							show: true,
+							lineStyle: {
+								color: 'rgba(22, 207, 208, 0.1)',
+							},
+						},
+						axisLabel: {
+							color: '#16cfd0',
+						},
+						splitLine: {
+							show: true,
+							lineStyle: {
+								color: 'rgba(22, 207, 208, 0.3)',
+							},
+						},
+						splitArea: {
+							show: true,
+							areaStyle: {
+								color: 'rgba(22, 207, 208, 0.02)',
+							},
+						},
+						nameTextStyle: {
+							color: '#16cfd0',
+						},
+					},
+				],
+				series: [
+					{
+						name: '预购队列',
+						type: 'line',
+						data: [200, 85, 112, 275, 305, 415],
+						itemStyle: {
+							color: '#16cfd0',
+						},
+					},
+					{
+						name: '最新成交价',
+						type: 'line',
+						data: [50, 85, 22, 155, 170, 25],
+						itemStyle: {
+							color: '#febb50',
+						},
+					},
+				],
+			};
+			myChart.setOption(option);
+			state.myCharts.push(myChart);
+		};
+		// A级风景区对比
+		const initVisualizingContentLeftBottom = () => {
+			const myChart = echarts.init(proxy.$refs.visualizingContentLeftBottom);
+			const option = {
+				grid: {
+					top: 50,
+					right: 10,
+					bottom: 40,
+					left: 30,
+				},
+				tooltip: {
+					trigger: 'axis',
+				},
+				xAxis: {
+					type: 'category',
+					boundaryGap: false,
+					data: ['1月', '2月', '3月', '4月', '5月'],
+					axisLine: {
+						lineStyle: {
+							color: 'rgba(22, 207, 208, 0.1)',
+							width: 1,
+						},
+					},
+					axisTick: {
+						show: false,
+					},
+					axisLabel: {
+						interval: 0,
+						color: '#16cfd0',
+						textStyle: {
+							fontSize: 10,
+						},
+					},
+				},
+				yAxis: [
+					{
+						type: 'value',
+						name: '销量',
+						axisLabel: {
+							color: '#16cfd0',
+						},
+						splitLine: {
+							show: true,
+							lineStyle: {
+								color: 'rgba(22, 207, 208, 0.3)',
+							},
+						},
+						splitArea: {
+							show: true,
+							areaStyle: {
+								color: 'rgba(22, 207, 208, 0.02)',
+							},
+						},
+						nameTextStyle: {
+							color: '#16cfd0',
+						},
+					},
+				],
+				series: [
+					{
+						name: '客流',
+						type: 'line',
+						stack: '总量',
+						smooth: true,
+						lineStyle: {
+							width: 0,
+						},
+						areaStyle: {
+							opacity: 0.8,
+							color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+								{
+									offset: 0,
+									color: 'rgba(128, 255, 165)',
+								},
+								{
+									offset: 1,
+									color: 'rgba(1, 191, 236)',
+								},
+							]),
+						},
+						emphasis: {
+							focus: 'series',
+						},
+						data: [140, 232, 101, 264, 90],
+					},
+					{
+						name: '天数',
+						type: 'line',
+						stack: '总量',
+						smooth: true,
+						lineStyle: {
+							width: 0,
+						},
+						areaStyle: {
+							opacity: 0.8,
+							color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+								{
+									offset: 0,
+									color: 'rgba(0, 221, 255)',
+								},
+								{
+									offset: 1,
+									color: 'rgba(77, 119, 255)',
+								},
+							]),
+						},
+						emphasis: {
+							focus: 'series',
+						},
+						data: [120, 282, 111, 234, 220],
+					},
+				],
+			};
+			myChart.setOption(option);
+			state.myCharts.push(myChart);
+		};
+		// 游客过夜情况
+		const initVisualizingContentCenterTop = () => {
+			const myChart = echarts.init(proxy.$refs.visualizingContentCenterTop);
+			const min = 100;
+			const max = 1000;
+			const option = {
+				grid: {
+					top: 50,
+					right: 10,
+					bottom: 66,
+					left: 38,
+				},
+				tooltip: {
+					trigger: 'axis',
+					axisPointer: {
+						type: 'shadow',
+					},
+				},
+				xAxis: [
+					{
+						type: 'category',
+						data: ['地区', '地区', '地区', '地区', '地区', '地区', '地区', '地区', '地区', '地区'],
+						axisLabel: {
+							color: '#16cfd0',
+							textStyle: {
+								fontSize: 9,
+							},
+							interval: 0,
+							rotate: -45,
+						},
+						axisLine: {
+							lineStyle: {
+								color: 'rgba(22, 207, 208, 0.1)',
+								width: 1,
+							},
+						},
+						axisTick: {
+							show: false,
+						},
+					},
+				],
+				yAxis: [
+					{
+						type: 'value',
+						name: '天数',
+						nameGap: 25,
+						axisLine: {
+							show: true,
+							lineStyle: {
+								color: 'rgba(22, 207, 208, 0.1)',
+							},
+						},
+						axisLabel: {
+							color: '#16cfd0',
+						},
+						splitLine: {
+							show: true,
+							lineStyle: {
+								color: 'rgba(22, 207, 208, 0.3)',
+							},
+						},
+						splitArea: {
+							show: true,
+							areaStyle: {
+								color: 'rgba(22, 207, 208, 0.02)',
+							},
+						},
+						nameTextStyle: {
+							color: '#16cfd0',
+						},
+					},
+				],
+				series: [
+					{
+						type: 'bar',
+						barWidth: 15,
+						itemStyle: {
+							normal: {
+								color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+									{
+										offset: 0,
+										color: '#de682e',
+									},
+									{
+										offset: 1,
+										color: '#ecc232',
+									},
+								]),
+							},
+						},
+						label: {
+							normal: {
+								show: true,
+								position: 'top',
+								formatter: function (param: any) {
+									if (param.value == max || param.value == min) {
+										return '';
+									}
+									return param.value;
+								},
+								textStyle: {
+									color: 'rgba(22, 207, 208, 0.8)',
+									fontSize: 10,
+								},
+							},
+						},
+						markPoint: {
+							symbolSize: 30,
+							label: {
+								normal: {
+									textStyle: {
+										color: '#ffffff',
+										fontSize: 10,
+									},
+								},
+							},
+							data: [
+								{ name: '年最低', value: min, xAxis: 0, yAxis: 100 },
+								{ name: '年最高', value: max, xAxis: 9, yAxis: 1000 },
+							],
+						},
+						data: [100, 200, 300, 400, 500, 600, 700, 800, 900, 1000],
+					},
+				],
+			};
+			myChart.setOption(option);
+			state.myCharts.push(myChart);
+		};
+		// 游客驻留时长
+		const initVisualizingContentCenterBottom = () => {
+			const myChart = echarts.init(proxy.$refs.visualizingContentCenterBottom);
+			const option = {
+				tooltip: {
+					trigger: 'axis',
+					axisPointer: {
+						type: 'shadow',
+					},
+				},
+				grid: {
+					top: 26,
+					right: 10,
+					bottom: 66,
+					left: 45,
+				},
+				xAxis: {
+					type: 'value',
+					axisLabel: {
+						color: '#16cfd0',
+					},
+					splitLine: {
+						show: true,
+						lineStyle: {
+							color: 'rgba(22, 207, 208, 0.3)',
+						},
+					},
+				},
+				yAxis: {
+					type: 'category',
+					axisLabel: {
+						color: '#16cfd0',
+					},
+				},
+				series: [
+					{
+						name: '已完成',
+						type: 'bar',
+						stack: 'total',
+						label: {
+							show: true,
+						},
+						emphasis: {
+							focus: 'series',
+						},
+						barWidth: 12,
+						itemStyle: {
+							label: {
+								show: true,
+							},
+							labelLine: {
+								show: false,
+							},
+							color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
+								{ offset: 0, color: 'rgba(7,165,255,0.2)' },
+								{ offset: 1, color: 'rgba(7,165,255,1)' },
+							]),
+						},
+					},
+					{
+						name: '进行中',
+						type: 'bar',
+						stack: 'total',
+						label: {
+							show: true,
+						},
+						emphasis: {
+							focus: 'series',
+						},
+						barWidth: 12,
+						itemStyle: {
+							label: {
+								show: true,
+							},
+							labelLine: {
+								show: false,
+							},
+							color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
+								{ offset: 0, color: 'rgba(41,244,236,0)' },
+								{ offset: 1, color: 'rgba(41,244,236,1)' },
+							]),
+						},
+					},
+				],
+				dataset: {
+					source: [
+						{ status: '已签收', value1: 33, value2: 93 },
+						{ status: '配送中', value1: 53, value2: 32 },
+						{ status: '已出库', value1: 78, value2: 65 },
+						{ status: '采购中', value1: 12, value2: 35 },
+						{ status: '接单中', value1: 90, value2: 52 },
+					],
+				},
+			};
+			myChart.setOption(option);
+			state.myCharts.push(myChart);
+		};
+		// 当日游客趋势分析
+		const initVisualizingContentRightTop = () => {
+			const myChart = echarts.init(proxy.$refs.visualizingContentRightTop);
+			const option = {
+				grid: {
+					top: 50,
+					right: 30,
+					bottom: 50,
+					left: 20,
+				},
+				tooltip: {
+					trigger: 'axis',
+					axisPointer: {
+						type: 'shadow',
+					},
+				},
+				xAxis: {
+					data: ['1月', '2月', '3月', '4月', '5月', '6月'],
+					axisLine: {
+						lineStyle: {
+							color: 'rgba(22, 207, 208, 0.5)',
+							width: 1,
+						},
+					},
+					axisTick: {
+						show: false,
+					},
+					axisLabel: {
+						color: '#16cfd0',
+					},
+				},
+				yAxis: [
+					{
+						type: 'value',
+						name: '亿元',
+						axisLine: {
+							show: true,
+							lineStyle: {
+								color: 'rgba(22, 207, 208, 0.1)',
+							},
+						},
+						axisLabel: {
+							color: '#16cfd0',
+						},
+						splitLine: {
+							show: true,
+							lineStyle: {
+								color: 'rgba(22, 207, 208, 0.3)',
+							},
+						},
+						splitArea: {
+							show: true,
+							areaStyle: {
+								color: 'rgba(22, 207, 208, 0.02)',
+							},
+						},
+						nameTextStyle: {
+							color: '#16cfd0',
+						},
+					},
+					{
+						type: 'value',
+						name: '同比',
+						position: 'right',
+						axisLine: {
+							show: false,
+						},
+						axisLabel: {
+							show: true,
+							formatter: '{value}%',
+							textStyle: {
+								color: '#16cfd0',
+							},
+						},
+						splitLine: {
+							show: false,
+						},
+						axisTick: {
+							show: false,
+						},
+						splitArea: {
+							show: true,
+							areaStyle: {
+								color: 'rgba(22, 207, 208, 0.02)',
+							},
+						},
+						nameTextStyle: {
+							color: '#16cfd0',
+						},
+					},
+				],
+				series: [
+					{
+						name: '销售水量',
+						type: 'line',
+						yAxisIndex: 1,
+						smooth: true,
+						showAllSymbol: true,
+						symbol: 'circle',
+						itemStyle: {
+							color: '#058cff',
+						},
+						lineStyle: {
+							color: '#058cff',
+						},
+						areaStyle: {
+							color: 'rgba(5,140,255, 0.2)',
+						},
+						data: [4.2, 3.8, 4.8, 3.5, 2.9, 2.8],
+					},
+					{
+						name: '主营业务',
+						type: 'bar',
+						barWidth: 15,
+						itemStyle: {
+							normal: {
+								color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+									{
+										offset: 0,
+										color: '#00FFE3',
+									},
+									{
+										offset: 1,
+										color: '#4693EC',
+									},
+								]),
+							},
+						},
+						data: [4.2, 3.8, 4.8, 3.5, 2.9, 2.8],
+					},
+				],
+			};
+			myChart.setOption(option);
+			state.myCharts.push(myChart);
+		};
+		// 当月游客趋势分析
+		const initVisualizingContentRightBottom = () => {
+			const myChart = echarts.init(proxy.$refs.visualizingContentRightBottom);
+			const option = {
+				grid: {
+					top: 50,
+					right: 10,
+					bottom: 40,
+					left: 30,
+				},
+				tooltip: {
+					trigger: 'axis',
+				},
+				xAxis: {
+					data: ['1月', '2月', '3月', '4月', '5月', '6月'],
+					axisLine: {
+						lineStyle: {
+							color: 'rgba(22, 207, 208, 0.1)',
+							width: 1,
+						},
+					},
+					axisTick: {
+						show: false,
+					},
+					axisLabel: {
+						color: '#16cfd0',
+					},
+				},
+				yAxis: [
+					{
+						type: 'value',
+						name: '人数(万)',
+						axisLine: {
+							show: true,
+							lineStyle: {
+								color: 'rgba(22, 207, 208, 0.1)',
+							},
+						},
+						axisLabel: {
+							color: '#16cfd0',
+						},
+						splitLine: {
+							show: true,
+							lineStyle: {
+								color: 'rgba(22, 207, 208, 0.3)',
+							},
+						},
+						splitArea: {
+							show: true,
+							areaStyle: {
+								color: 'rgba(22, 207, 208, 0.02)',
+							},
+						},
+						nameTextStyle: {
+							color: '#16cfd0',
+						},
+					},
+				],
+				series: [
+					{
+						name: '预购队列',
+						type: 'line',
+						data: [20, 15, 40, 55, 65, 85],
+						smooth: true,
+						itemStyle: {
+							color: '#EA7CCC',
+						},
+					},
+					{
+						name: '最新成交价',
+						type: 'line',
+						data: [30, 45, 65, 85, 60, 105],
+						smooth: true,
+						itemStyle: {
+							color: '#FAC958',
+						},
+					},
+				],
+			};
+			myChart.setOption(option);
+			state.myCharts.push(myChart);
+		};
+		// 批量设置 echarts resize
+		const initEchartsResize = () => {
+			window.addEventListener('resize', () => {
+				for (let i = 0; i < state.myCharts.length; i++) {
+					state.myCharts[i].resize();
+				}
+			});
+		};
+		// 页面加载时
+		onMounted(async () => {
+			NextLoading.done();
+			initTime();
+			await initEchartsMap();
+			await initVisualizingContentLeftTop();
+			await initVisualizingContentLeftBottom();
+			await initVisualizingContentCenterTop();
+			await initVisualizingContentCenterBottom();
+			await initVisualizingContentRightTop();
+			await initVisualizingContentRightBottom();
+			await initEchartsResize();
+		});
+		// 页面卸载时
+		onUnmounted(() => {
+			window.clearInterval(state.time.fun);
+		});
+		return {
+			...toRefs(state),
+		};
+	},
+});
+</script>
+
+<style scoped lang="scss">
+// 左右两侧图表宽度
+$lrWidth: 288px;
+// 中部图表高度
+$cheight: 240px;
+// 标题宽度
+$titleWidth: 240px;
+.visualizing-demo1 {
+	height: 100%;
+	width: 100%;
+	position: relative;
+	::v-deep(.BMap_cpyCtrl) {
+		display: none;
+	}
+	::v-deep(.anchorBL) {
+		display: none;
+	}
+	.visualizing-container {
+		.visualizing-container-head {
+			height: 60px;
+			width: 100%;
+			position: absolute;
+			top: 0;
+			left: 0;
+			display: flex;
+			align-items: center;
+			color: #ffffff;
+			background: linear-gradient(to bottom, rgba(0, 0, 0, 0.9), rgba(0, 0, 0, 0.3), rgba(0, 0, 0, 0.02));
+			z-index: 3;
+			.visualizing-container-head-left {
+				flex: 1;
+				position: relative;
+				height: 60px;
+				.visualizing-container-head-left-text {
+					position: relative;
+					z-index: 1;
+					font-size: 12px;
+					opacity: 0.6;
+					width: 93%;
+					padding-left: 15px;
+					top: 50%;
+					transform: translateY(-100%);
+					.visualizing-container-head-left-text-box {
+						width: 100%;
+						position: relative;
+						padding-left: 15px;
+						&::after {
+							content: '';
+							height: 20px;
+							width: 100%;
+							position: absolute;
+							background: linear-gradient(to right, rgba(22, 207, 208, 0.1), rgba(22, 207, 208, 0.3));
+							top: -1px;
+							left: 0;
+							transform: skew(30deg);
+						}
+					}
+				}
+				&::before,
+				&::after {
+					content: '';
+					height: 3px;
+					background: linear-gradient(-90deg, #16cfd0, transparent);
+					position: absolute;
+					width: 100%;
+				}
+				&::before {
+					top: 1px;
+					right: 33px;
+					opacity: 0.8;
+				}
+				&::after {
+					top: 41px;
+					right: -2px;
+				}
+			}
+			.visualizing-container-head-center {
+				height: 60px;
+				font-size: 18px;
+				text-align: center;
+				display: flex;
+				flex-direction: column;
+				position: relative;
+				padding: 0 60px;
+				background: radial-gradient(rgba(0, 0, 0, 0.7) 5%, rgba(0, 0, 0, 0.5) 15%, rgba(0, 0, 0, 0.02) 70%);
+				.visualizing-container-head-center-box {
+					margin: auto;
+					position: relative;
+					z-index: 1;
+					.visualizing-container-head-center-maintitle {
+						position: relative;
+						&::before,
+						&::after {
+							content: '';
+							position: absolute;
+							min-width: 21%;
+							height: 29px;
+							top: 2px;
+							opacity: 0.3;
+							border-top: 1px solid #16cfd0;
+						}
+						&::before {
+							left: -70px;
+							border-right: 2px solid #16cfd0;
+							transform: skew(50deg);
+						}
+						&::after {
+							right: -70px;
+							border-left: 2px solid #16cfd0;
+							transform: skew(-50deg);
+						}
+					}
+					.visualizing-container-head-center-subtitle {
+						font-size: 14px;
+						opacity: 0.8;
+						position: relative;
+						&::before,
+						&::after {
+							content: '';
+							position: absolute;
+							min-width: 35%;
+							height: 23px;
+							bottom: 8px;
+							border-bottom: 2px solid #16cfd0;
+						}
+						&::before {
+							transform: skew(50deg);
+							left: -33px;
+							border-left: 3px solid #16cfd0;
+							border-image: linear-gradient(to right, #16cfd0, rgba(22, 207, 208, 0.02)) 1 10;
+						}
+						&::after {
+							transform: skew(-50deg);
+							right: -33px;
+							border-right: 3px solid #16cfd0;
+							border-image: linear-gradient(to left, rgba(22, 207, 208, 1), rgba(22, 207, 208, 0.02)) 1 10;
+						}
+					}
+					&::before,
+					&::after {
+						content: '';
+						position: absolute;
+						width: 36px;
+						height: 17px;
+						top: -8px;
+						border-bottom: 2px solid rgba(22, 207, 208, 0.7);
+					}
+					&::before {
+						transform: skew(32deg);
+						left: -89px;
+						border-left: 3px solid rgba(22, 207, 208, 0.7);
+					}
+					&::after {
+						transform: skew(-32deg);
+						right: -89px;
+						border-right: 3px solid rgba(22, 207, 208, 0.7);
+					}
+				}
+				&::before,
+				&::after {
+					content: '';
+					position: absolute;
+					min-width: 50%;
+					height: 15px;
+					bottom: 0px;
+					border-bottom: 4px solid #16cfd0;
+				}
+				&::before {
+					transform: skew(60deg);
+					left: 13px;
+					border-left: 5px solid #16cfd0;
+				}
+				&::after {
+					transform: skew(-60deg);
+					right: 13px;
+					border-right: 5px solid #16cfd0;
+				}
+			}
+			.visualizing-container-head-right {
+				flex: 1;
+				position: relative;
+				height: 60px;
+				.visualizing-container-head-right-text {
+					position: relative;
+					z-index: 1;
+					font-size: 12px;
+					opacity: 0.6;
+					width: 93%;
+					float: right;
+					text-align: right;
+					padding-right: 15px;
+					top: 50%;
+					transform: translateY(-100%);
+					.visualizing-container-head-right-text-box {
+						width: 100%;
+						position: relative;
+						padding-right: 15px;
+						&::after {
+							content: '';
+							height: 20px;
+							width: 100%;
+							position: absolute;
+							background: linear-gradient(to left, rgba(22, 207, 208, 0.1), rgba(22, 207, 208, 0.3));
+							top: -1px;
+							right: 0;
+							transform: skew(-30deg);
+						}
+					}
+				}
+				&::before,
+				&::after {
+					content: '';
+					height: 3px;
+					background: linear-gradient(90deg, #16cfd0, transparent);
+					position: absolute;
+					width: 100%;
+				}
+				&::before {
+					top: 1px;
+					left: 33px;
+					opacity: 0.8;
+				}
+				&::after {
+					top: 41px;
+					left: -2px;
+				}
+			}
+		}
+		.visualizing-container-title {
+			max-width: $titleWidth;
+			font-size: 14px;
+			color: #ffffff;
+			opacity: 0.8;
+			padding: 0 5px;
+			border-bottom: 1px solid #ffffff;
+			border-image: linear-gradient(to right, #ffffff, rgba(22, 207, 208, 0.02)) 1 10;
+			position: relative;
+			i {
+				padding-right: 5px;
+				color: orange;
+			}
+			&::after {
+				content: '';
+				position: absolute;
+				left: 0;
+				bottom: 0;
+				width: 1px;
+				height: 10px;
+				background: linear-gradient(to top, #ffffff, rgba(255, 255, 255, 0.5));
+			}
+		}
+		.visualizing-container-title-colorful {
+			max-width: $titleWidth;
+			border: 0;
+			padding: 1px;
+			background: linear-gradient(135deg, red, orange, green, #16cfd0, purple);
+			--mask-image: repeating-linear-gradient(135deg, #000 0px, #000 1px, transparent 1px, transparent 4px);
+			-webkit-mask-image: var(--mask-image);
+			mask-image: var(--mask-image);
+		}
+		.visualizing-container-content-left {
+			width: $lrWidth;
+			height: 100%;
+			position: absolute;
+			top: 0;
+			left: 0;
+			background: linear-gradient(to right, rgba(0, 0, 0, 0.9), rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.01));
+			padding-top: 60px;
+			z-index: 2;
+			.visualizing-container-content-left-flex {
+				height: 50%;
+				display: flex;
+				flex-direction: column;
+				padding-left: 15px;
+			}
+		}
+		.visualizing-container-content-center {
+			width: 100%;
+			height: $cheight;
+			position: absolute;
+			bottom: 0;
+			left: 0;
+			background: linear-gradient(to top, rgba(0, 0, 0, 0.9), rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.01));
+			z-index: 1;
+			.visualizing-container-content-center-bottom {
+				width: calc(100% - #{$lrWidth * 2});
+				height: $cheight;
+				left: $lrWidth;
+				bottom: 0;
+				position: absolute;
+				display: flex;
+				padding: 0 15px;
+				.visualizing-container-content-center-bottom-flex {
+					width: 50%;
+					padding: 0 15px;
+				}
+			}
+		}
+		.visualizing-container-content-right {
+			width: $lrWidth;
+			height: 100%;
+			position: absolute;
+			top: 0;
+			right: 0;
+			padding-top: 60px;
+			background: linear-gradient(to left, rgba(0, 0, 0, 0.9), rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.01));
+			z-index: 2;
+			.visualizing-container-content-right-flex {
+				height: 50%;
+				display: flex;
+				flex-direction: column;
+				padding-right: 15px;
+			}
+		}
+	}
+}
+</style>
diff --git a/src/views/visualizing/demo2.vue b/src/views/visualizing/demo2.vue
new file mode 100644
index 0000000..2e973fc
--- /dev/null
+++ b/src/views/visualizing/demo2.vue
@@ -0,0 +1,1344 @@
+<template>
+	<div class="visualizing-demo2">
+		<!-- 顶部 -->
+		<div class="big-data-up">
+			<div class="up-left">
+				<SvgIcon name="ele-Timer" class="mr5" />
+				<span>{{ time.txt }}</span>
+			</div>
+			<div class="up-center">
+				<span>智慧农业系统平台</span>
+			</div>
+			<div class="up-right">
+				<el-dropdown size="small">
+					<span class="el-dropdown-link">
+						{{ dropdownActive }}
+						<SvgIcon name="ele-ArrowDown" class="el-icon--right" />
+					</span>
+					<template #dropdown>
+						<el-dropdown-menu>
+							<el-dropdown-item v-for="(v, k) in dropdownList" :key="k">{{ v.label }} </el-dropdown-item>
+						</el-dropdown-menu>
+					</template>
+				</el-dropdown>
+				<div class="ml15">
+					<SvgIcon name="ele-Bell" class="mr5" />
+					<span>消息</span>
+				</div>
+				<div class="ml15">
+					<SvgIcon name="ele-User" class="mr5" />
+					<span>个人</span>
+				</div>
+				<div class="ml15">
+					<SvgIcon name="ele-SwitchButton" class="mr5" />
+					<span>返回</span>
+				</div>
+			</div>
+		</div>
+
+		<div class="big-data-down">
+			<!-- 左边 -->
+			<div class="big-data-down-left">
+				<div class="flex-warp-item">
+					<div class="flex-warp-item-box">
+						<div class="flex-title">天气预报</div>
+						<div class="flex-content flex-content-overflow">
+							<div class="sky">
+								<SvgIcon name="ele-Sunny" class="sky-left" />
+								<div class="sky-center">
+									<div class="mb2">
+										<span class="font">多云转晴</span>
+										<span class="font">东南风</span>
+										<span>良</span>
+									</div>
+									<div class="sky-tip">温馨提示:多云转晴,南风转北风风力3级</div>
+								</div>
+								<div class="sky-right">
+									<span>25</span>
+									<span class="font">°C</span>
+								</div>
+							</div>
+							<div class="sky-dd">
+								<div class="sky-dl" v-for="(v, k) in skyList" :key="k" :class="{ 'sky-dl-first': k === 1 }">
+									<div>{{ v.v1 }}</div>
+									<div v-if="v.type === 'title'">{{ v.v2 }}</div>
+									<div v-else>
+										<SvgIcon :name="v.v2" />
+									</div>
+									<div>{{ v.v3 }}</div>
+									<div>{{ v.v4 }}</div>
+									<div class="tip">{{ v.v5 }}</div>
+									<div>{{ v.v6 }}</div>
+									<div>{{ v.v7 }}</div>
+								</div>
+							</div>
+						</div>
+					</div>
+				</div>
+				<div class="flex-warp-item">
+					<div class="flex-warp-item-box">
+						<div class="flex-title">当前设备状态</div>
+						<div class="flex-content flex-content-overflow">
+							<div class="d-states">
+								<div class="d-states-item">
+									<SvgIcon name="ele-Odometer" class="i-bg1" />
+									<div class="d-states-flex">
+										<div class="d-states-item-label">园区设备数</div>
+										<div class="d-states-item-value">99</div>
+									</div>
+								</div>
+								<div class="d-states-item">
+									<SvgIcon name="ele-FirstAidKit" class="i-bg2" />
+									<div class="d-states-flex">
+										<div class="d-states-item-label">预警设备数</div>
+										<div class="d-states-item-value">10</div>
+									</div>
+								</div>
+								<div class="d-states-item">
+									<SvgIcon name="ele-VideoPlay" class="i-bg3" />
+									<div class="d-states-flex">
+										<div class="d-states-item-label">运行设备数</div>
+										<div class="d-states-item-value">20</div>
+									</div>
+								</div>
+							</div>
+							<div class="d-btn">
+								<div class="d-btn-item" v-for="(v, k) in dBtnList" :key="k" :class="{ 'd-btn-active': dBtnActive === k }">
+									<SvgIcon name="ele-Money" class="d-btn-item-left" />
+									<div class="d-btn-item-center">
+										<div>{{ v.v1 }}</div>
+										<div>{{ v.v2 }}|{{ v.v3 }}</div>
+									</div>
+									<div class="d-btn-item-eight">{{ v.v4 }}</div>
+								</div>
+							</div>
+						</div>
+					</div>
+				</div>
+				<div class="flex-warp-item">
+					<div class="flex-warp-item-box">
+						<div class="flex-title">近30天预警总数</div>
+						<div class="flex-content" ref="rightChartData1"></div>
+					</div>
+				</div>
+			</div>
+
+			<!-- 中间 -->
+			<div class="big-data-down-center">
+				<div class="big-data-down-center-one">
+					<div class="big-data-down-center-one-content" ref="rightChartData5">
+						<div ref="the3DEarth"></div>
+						<div :class="v.topLevelClass" v-for="(v, k) in earth3DBtnList" :key="k">
+							<div class="circle" v-for="i in 4" :key="i"></div>
+							<div class="text-box">
+								<SvgIcon :name="v.icon" />
+								<div class="text">{{ v.label }}</div>
+							</div>
+						</div>
+					</div>
+				</div>
+				<div class="big-data-down-center-two">
+					<div class="flex-warp-item-box">
+						<div class="flex-title">
+							<span>当前设备监测</span>
+							<span class="flex-title-small">单位:次</span>
+						</div>
+						<div class="flex-content">
+							<div class="flex-content-left">
+								<div class="monitor-item" v-for="(v, k) in chartData4List" :key="k">
+									<div class="monitor-wave" :class="{ 'monitor-active': k === chartData4Index }">
+										<div class="monitor-z-index">
+											<div class="monitor-item-label">{{ v.label }}</div>
+										</div>
+									</div>
+								</div>
+							</div>
+							<div class="flex-content-right" ref="rightChartData4"></div>
+						</div>
+					</div>
+				</div>
+			</div>
+
+			<!-- 右边 -->
+			<div class="big-data-down-right">
+				<div class="flex-warp-item">
+					<div class="flex-warp-item-box">
+						<div class="flex-title">
+							<span>近7天产品追溯扫码统计</span>
+							<span class="flex-title-small">单位:次</span>
+						</div>
+						<div class="flex-content" ref="rightChartData3"></div>
+					</div>
+				</div>
+				<div class="flex-warp-item">
+					<div class="flex-warp-item-box">
+						<div class="flex-title">当前任务统计</div>
+						<div class="flex-content">
+							<div class="task">
+								<div class="task-item task-first-item">
+									<div class="task-item-value task-first">25</div>
+									<div class="task-item-label">待办任务</div>
+								</div>
+								<div class="task-item">
+									<div class="task-item-box task1">
+										<div class="task-item-value">12</div>
+										<div class="task-item-label">施肥</div>
+									</div>
+								</div>
+								<div class="task-item">
+									<div class="task-item-box task2">
+										<div class="task-item-value">3</div>
+										<div class="task-item-label">施药</div>
+									</div>
+								</div>
+								<div class="task-item">
+									<div class="task-item-box task3">
+										<div class="task-item-value">5</div>
+										<div class="task-item-label">农事</div>
+									</div>
+								</div>
+								<div class="task-item">
+									<div class="task-item-box task4">
+										<div class="task-item-value">3</div>
+										<div class="task-item-label">巡园</div>
+									</div>
+								</div>
+								<div class="task-item">
+									<div class="task-item-box task5">
+										<div class="task-item-value">2</div>
+										<div class="task-item-label">采集</div>
+									</div>
+								</div>
+							</div>
+							<div ref="rightChartData6" class="progress"></div>
+						</div>
+					</div>
+				</div>
+				<div class="flex-warp-item">
+					<div class="flex-warp-item-box">
+						<div class="flex-title">
+							<span>近7天投入品记录</span>
+							<span class="flex-title-small">单位:件</span>
+						</div>
+						<div class="flex-content" ref="rightChartData2"></div>
+					</div>
+				</div>
+			</div>
+		</div>
+	</div>
+</template>
+
+<script lang="ts">
+import { toRefs, reactive, onMounted, onUnmounted, getCurrentInstance, defineComponent } from 'vue';
+import * as echarts from 'echarts';
+import 'echarts-gl';
+import { formatDate } from '/@/utils/formatTime';
+import { NextLoading } from '/@/utils/loading';
+import { dropdownList, skyList, dBtnList, earth3DBtnList, chartData4List } from './mock/demo2';
+import worldImg from './images/world.jpg';
+import bathymetryImg from './images/bathymetry.jpg';
+
+export default defineComponent({
+	name: 'visualizingLinkDemo2',
+	setup() {
+		const { proxy } = getCurrentInstance() as any;
+		const state = reactive({
+			time: {
+				txt: '',
+				fun: 0,
+			},
+			dropdownList,
+			dropdownActive: '请选择',
+			skyList,
+			dBtnList,
+			chartData4Index: 0,
+			dBtnActive: 0,
+			earth3DBtnList,
+			chartData4List,
+			myCharts: [],
+			the3DEarth: null as HTMLDivElement | null,
+		});
+		// 初始化时间
+		const initTime = () => {
+			state.time.txt = formatDate(new Date(), 'YYYY-mm-dd HH:MM:SS WWW QQQQ ZZZ');
+			state.time.fun = window.setInterval(() => {
+				state.time.txt = formatDate(new Date(), 'YYYY-mm-dd HH:MM:SS WWW QQQQ ZZZ');
+			}, 1000);
+		};
+		// 近30天预警总数
+		const initRightChartData1 = () => {
+			const myChart = echarts.init(proxy.$refs.rightChartData1);
+			const option = {
+				tooltip: {
+					trigger: 'item',
+				},
+				series: [
+					{
+						name: '面积模式',
+						type: 'pie',
+						radius: [10, 60],
+						center: ['50%', '50%'],
+						roseType: 'area',
+						itemStyle: {
+							borderRadius: 5,
+						},
+						data: [
+							{ name: '天气预警', value: 100 },
+							{ name: '病虫害预警', value: 50 },
+							{ name: '任务预警', value: 130 },
+							{ name: '监测设备预警', value: 62 },
+						],
+						label: {
+							color: '#c0d1f2',
+						},
+					},
+				],
+			};
+			myChart.setOption(option);
+			state.myCharts.push(myChart);
+		};
+		// 当前设备监测
+		const initRightChartData4 = () => {
+			const myChart = echarts.init(proxy.$refs.rightChartData4);
+			const option = {
+				grid: {
+					top: 10,
+					right: 10,
+					bottom: 20,
+					left: 30,
+				},
+				tooltip: {
+					trigger: 'axis',
+				},
+				xAxis: {
+					type: 'category',
+					boundaryGap: false,
+					data: ['1月', '2月', '3月', '4月', '5月', '6月'],
+					axisLine: {
+						lineStyle: {
+							color: 'rgba(22, 207, 208, 0.1)',
+							width: 1,
+						},
+					},
+					axisTick: {
+						show: false,
+					},
+					axisLabel: {
+						interval: 0,
+						color: '#c0d1f2',
+						textStyle: {
+							fontSize: 10,
+						},
+					},
+				},
+				yAxis: [
+					{
+						type: 'value',
+						axisLabel: {
+							color: '#c0d1f2',
+						},
+						splitLine: {
+							show: true,
+							lineStyle: {
+								color: 'rgba(22, 207, 208, 0.3)',
+							},
+						},
+						splitArea: {
+							show: true,
+							areaStyle: {
+								color: 'rgba(22, 207, 208, 0.02)',
+							},
+						},
+						nameTextStyle: {
+							color: '#16cfd0',
+						},
+					},
+				],
+				series: [
+					{
+						name: '温度',
+						type: 'line',
+						smooth: true,
+						lineStyle: {
+							width: 0,
+						},
+						areaStyle: {
+							opacity: 0.8,
+							color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+								{
+									offset: 0,
+									color: 'rgba(128, 255, 165)',
+								},
+								{
+									offset: 1,
+									color: 'rgba(1, 191, 236)',
+								},
+							]),
+						},
+						emphasis: {
+							focus: 'series',
+						},
+						data: [140, 232, 101, 264, 90, 70],
+					},
+				],
+			};
+			myChart.setOption(option);
+			state.myCharts.push(myChart);
+		};
+		// 近7天产品追溯扫码统计
+		const initRightChartData3 = () => {
+			const myChart = echarts.init(proxy.$refs.rightChartData3);
+			const option = {
+				grid: {
+					top: 10,
+					right: 0,
+					bottom: 20,
+					left: 30,
+				},
+				tooltip: {
+					trigger: 'axis',
+				},
+				xAxis: {
+					data: ['1月', '2月', '3月', '4月', '5月', '6月'],
+					axisLine: {
+						lineStyle: {
+							color: 'rgba(22, 207, 208, 0.1)',
+							width: 1,
+						},
+					},
+					axisTick: {
+						show: false,
+					},
+					axisLabel: {
+						color: '#c0d1f2',
+					},
+				},
+				yAxis: [
+					{
+						type: 'value',
+						axisLine: {
+							show: true,
+							lineStyle: {
+								color: 'rgba(22, 207, 208, 0.1)',
+							},
+						},
+						axisLabel: {
+							color: '#c0d1f2',
+						},
+						splitLine: {
+							show: true,
+							lineStyle: {
+								color: 'rgba(22, 207, 208, 0.3)',
+							},
+						},
+						splitArea: {
+							show: true,
+							areaStyle: {
+								color: 'rgba(22, 207, 208, 0.02)',
+							},
+						},
+						nameTextStyle: {
+							color: '#16cfd0',
+						},
+					},
+				],
+				series: [
+					{
+						name: '预购队列',
+						type: 'line',
+						data: [200, 85, 112, 275, 305, 415],
+						itemStyle: {
+							color: '#16cfd0',
+						},
+					},
+					{
+						name: '最新成交价',
+						type: 'line',
+						data: [50, 85, 22, 155, 170, 25],
+						itemStyle: {
+							color: '#febb50',
+						},
+					},
+				],
+			};
+			myChart.setOption(option);
+			state.myCharts.push(myChart);
+		};
+		// 当前任务统计
+		const initRightChartData6 = () => {
+			const myChart = echarts.init(proxy.$refs.rightChartData6);
+			const option = {
+				tooltip: {
+					trigger: 'axis',
+					axisPointer: {
+						type: 'shadow',
+					},
+				},
+				grid: {
+					top: 20,
+					right: 50,
+					bottom: 0,
+					left: 80,
+				},
+				xAxis: [
+					{
+						splitLine: {
+							show: false,
+						},
+						type: 'value',
+						show: false,
+					},
+				],
+				yAxis: [
+					{
+						splitLine: {
+							show: false,
+						},
+						axisLine: {
+							//y轴
+							show: false,
+						},
+						type: 'category',
+						axisTick: {
+							show: false,
+						},
+						inverse: true,
+						data: ['施肥任务完成率', '施药任务完成率', '农事任务完成率'],
+						axisLabel: {
+							color: '#A7D6F4',
+							fontSize: 12,
+						},
+					},
+				],
+				series: [
+					{
+						name: '标准化',
+						type: 'bar',
+						barWidth: 10, // 柱子宽度
+						label: {
+							show: true,
+							position: 'right', // 位置
+							color: '#A7D6F4',
+							fontSize: 12,
+							distance: 15, // 距离
+							formatter: '{c}%', // 这里是数据展示的时候显示的数据
+						}, // 柱子上方的数值
+						itemStyle: {
+							barBorderRadius: [0, 20, 20, 0], // 圆角(左上、右上、右下、左下)
+
+							color: new echarts.graphic.LinearGradient(
+								1,
+								0,
+								0,
+								0,
+								[
+									{
+										offset: 0,
+										color: '#51C5FD',
+									},
+									{
+										offset: 1,
+										color: '#005BB1',
+									},
+								],
+								false
+							), // 渐变
+						},
+						data: [75, 100, 60],
+					},
+				],
+			};
+			myChart.setOption(option);
+			state.myCharts.push(myChart);
+		};
+		// 近7天投入品记录
+		const initRightChartData2 = () => {
+			const myChart = echarts.init(proxy.$refs.rightChartData2);
+			const option = {
+				grid: {
+					top: 10,
+					right: 0,
+					bottom: 20,
+					left: 30,
+				},
+				tooltip: {
+					trigger: 'axis',
+					axisPointer: {
+						type: 'shadow',
+					},
+				},
+				xAxis: {
+					data: ['1月', '2月', '3月', '4月', '5月', '6月'],
+					axisLine: {
+						lineStyle: {
+							color: 'rgba(22, 207, 208, 0.5)',
+							width: 1,
+						},
+					},
+					axisTick: {
+						show: false,
+					},
+					axisLabel: {
+						color: '#c0d1f2',
+					},
+				},
+				yAxis: [
+					{
+						type: 'value',
+						axisLine: {
+							show: true,
+							lineStyle: {
+								color: 'rgba(22, 207, 208, 0.1)',
+							},
+						},
+						axisLabel: {
+							color: '#c0d1f2',
+						},
+						splitLine: {
+							show: true,
+							lineStyle: {
+								color: 'rgba(22, 207, 208, 0.3)',
+							},
+						},
+						splitArea: {
+							show: true,
+							areaStyle: {
+								color: 'rgba(22, 207, 208, 0.02)',
+							},
+						},
+						nameTextStyle: {
+							color: '#16cfd0',
+						},
+					},
+					{
+						type: 'value',
+						position: 'right',
+						axisLine: {
+							show: false,
+						},
+						axisLabel: {
+							show: true,
+							formatter: '{value}%',
+							textStyle: {
+								color: '#16cfd0',
+							},
+						},
+						splitLine: {
+							show: false,
+						},
+						axisTick: {
+							show: false,
+						},
+						splitArea: {
+							show: true,
+							areaStyle: {
+								color: 'rgba(22, 207, 208, 0.02)',
+							},
+						},
+						nameTextStyle: {
+							color: '#16cfd0',
+						},
+					},
+				],
+				series: [
+					{
+						name: '销售水量',
+						type: 'line',
+						yAxisIndex: 1,
+						smooth: true,
+						showAllSymbol: true,
+						symbol: 'circle',
+						itemStyle: {
+							color: '#058cff',
+						},
+						lineStyle: {
+							color: '#058cff',
+						},
+						areaStyle: {
+							color: 'rgba(5,140,255, 0.2)',
+						},
+						data: [4.2, 3.8, 4.8, 3.5, 2.9, 2.8],
+					},
+					{
+						name: '主营业务',
+						type: 'bar',
+						barWidth: 15,
+						itemStyle: {
+							normal: {
+								color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+									{
+										offset: 0,
+										color: '#00FFE3',
+									},
+									{
+										offset: 1,
+										color: '#4693EC',
+									},
+								]),
+							},
+						},
+						data: [4.2, 3.8, 4.8, 3.5, 2.9, 2.8],
+					},
+				],
+			};
+			myChart.setOption(option);
+			state.myCharts.push(myChart);
+		};
+		// 3DEarth 地图
+		const init3DEarth = (globeRadius) => {
+			let el = state.the3DEarth!;
+			el.style.height = `${proxy.$refs.rightChartData5.offsetHeight}px`;
+			const myChart = echarts.init(el);
+			const option = {
+				globe: {
+					baseTexture: worldImg,
+					heightTexture: bathymetryImg,
+					shading: 'realistic',
+					light: {
+						ambient: {
+							intensity: 0.4,
+						},
+						main: {
+							intensity: 0.4,
+						},
+					},
+					viewControl: {
+						autoRotate: true,
+					},
+					postEffect: {
+						enable: true,
+						bloom: {
+							enable: true,
+						},
+					},
+					globeRadius,
+				},
+				series: {
+					type: 'lines3D',
+					coordinateSystem: 'globe',
+					blendMode: 'lighter',
+					lineStyle: {
+						width: 1,
+						color: 'rgb(50, 50, 150)',
+						opacity: 0.1,
+					},
+					data: [],
+				},
+			};
+			// 随机模拟攻击线
+			let rodamData = function () {
+				let longitude = 105.18;
+				let longitude2 = Math.random() * 360 - 180;
+				let latitude = 37.51;
+				let latitude2 = Math.random() * 180 - 90;
+				return {
+					coords: [
+						[longitude2, latitude2],
+						[longitude, latitude],
+					],
+					value: (Math.random() * 3000).toFixed(2),
+				};
+			};
+			for (let i = 0; i < 150; i++) {
+				option.series.data = option.series.data.concat(rodamData());
+			}
+			myChart.setOption(option);
+		};
+		// 监听地球大小变化
+		const initAddEventListener3DEarth = () => {
+			let w = document.body.clientWidth;
+			let globeRadius = 0;
+			if (w >= 1920) globeRadius = 100;
+			else if (w > 1200 && w < 1920) globeRadius = 70;
+			else if (w > 992 && w < 1200) globeRadius = 60;
+			else if (w > 768 && w < 992) globeRadius = 40;
+			else if (w < 768) globeRadius = 20;
+			init3DEarth(globeRadius);
+		};
+		// 批量设置 echarts resize
+		const initEchartsResize = () => {
+			initAddEventListener3DEarth();
+			window.addEventListener('resize', () => {
+				for (let i = 0; i < state.myCharts.length; i++) {
+					state.myCharts[i].resize();
+				}
+				initAddEventListener3DEarth();
+			});
+		};
+		// 页面加载时
+		onMounted(async () => {
+			NextLoading.done();
+			initTime();
+			await initRightChartData1();
+			await initRightChartData4();
+			await initRightChartData3();
+			await initRightChartData2();
+			await initRightChartData6();
+			await initEchartsResize();
+		});
+		// 页面卸载时
+		onUnmounted(() => {
+			window.clearInterval(state.time.fun);
+		});
+		return {
+			...toRefs(state),
+		};
+	},
+});
+</script>
+
+<style scoped lang="scss">
+.visualizing-demo2 {
+	height: 100%;
+	width: 100%;
+	overflow: hidden;
+	background: url(https://img-blog.csdnimg.cn/6267533849444025811bf0840f9366e3.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAbHl0LXRvcA==,size_20,color_FFFFFF,t_70,g_se,x_16);
+	background-size: 100% 100%;
+	display: flex;
+	flex-direction: column;
+	font-size: 13px;
+	.big-data-up {
+		height: 70px;
+		width: 100%;
+		display: flex;
+		align-items: center;
+		padding: 0 15px;
+		color: #43bdf0;
+		overflow: hidden;
+		.up-left {
+			width: 30%;
+			font-style: italic;
+		}
+		.up-center {
+			width: 40%;
+			display: flex;
+			justify-content: center;
+			font-size: 20px;
+			letter-spacing: 5px;
+			background-image: -webkit-linear-gradient(left, #43bdf0, #c0d1f2 25%, #43bdf0 50%, #c0d1f2 75%, #43bdf0);
+			-webkit-text-fill-color: transparent;
+			background-clip: text;
+			-webkit-background-clip: text;
+			background-size: 200% 100%;
+			animation: masked-animation 4s infinite linear;
+			-webkit-box-reflect: below -2px -webkit-gradient(linear, left top, left bottom, from(rgba(0, 0, 0, 0)), to(rgba(255, 255, 255, 0.1)));
+			@keyframes masked-animation {
+				0% {
+					background-position: 0 0;
+				}
+				100% {
+					background-position: -100% 0;
+				}
+			}
+			position: relative;
+			&::after {
+				content: '';
+				width: 50%;
+				position: absolute;
+				bottom: -15px;
+				left: 50%;
+				transform: translateX(-50%);
+				border: 1px transparent solid;
+				border-image: linear-gradient(to right, rgba(0, 0, 0, 0.1), #43bdf0) 1 10;
+			}
+			span {
+				cursor: pointer;
+			}
+		}
+		.up-right {
+			width: 30%;
+			justify-content: flex-end;
+			display: flex;
+			align-items: center;
+			.ml15:hover {
+				cursor: pointer;
+			}
+			::v-deep(.el-dropdown) {
+				font-size: 13px !important;
+				color: #43bdf0;
+				cursor: pointer;
+			}
+		}
+	}
+	.big-data-down {
+		flex: 1;
+		overflow: hidden;
+		display: flex;
+		.big-data-down-left,
+		.big-data-down-right {
+			width: 30%;
+			display: flex;
+			flex-direction: column;
+			.flex-warp-item {
+				padding: 0 7.5px 15px 15px;
+				width: 100%;
+				height: 33.33%;
+				.flex-warp-item-box {
+					width: 100%;
+					height: 100%;
+					background: rgba(22, 34, 58, 0.4);
+					display: flex;
+					flex-direction: column;
+					padding: 15px;
+					.flex-title {
+						margin-bottom: 15px;
+						color: #c0d1f2;
+						display: flex;
+						justify-content: space-between;
+						.flex-title-small {
+							font-size: 12px;
+						}
+					}
+					.flex-content {
+						flex: 1;
+						font-size: 12px;
+					}
+					.flex-content-overflow {
+						overflow: hidden;
+					}
+				}
+			}
+		}
+		.big-data-down-left {
+			color: #c0d1f2;
+			.sky {
+				display: flex;
+				align-items: center;
+				.sky-left {
+					font-size: 30px;
+				}
+				.sky-center {
+					flex: 1;
+					overflow: hidden;
+					padding: 0 10px;
+					.font {
+						margin-right: 15px;
+						background: unset !important;
+						border-radius: unset !important;
+						padding: unset !important;
+					}
+					span {
+						background: #22bc76;
+						border-radius: 2px;
+						padding: 0 5px;
+					}
+					.sky-tip {
+						overflow: hidden;
+						white-space: nowrap;
+						text-overflow: ellipsis;
+					}
+				}
+				.sky-right {
+					span {
+						font-size: 30px;
+					}
+					font {
+						font-size: 20px;
+					}
+				}
+			}
+			.sky-dd {
+				.sky-dl {
+					display: flex;
+					align-items: center;
+					height: 28px;
+					overflow: hidden;
+					div {
+						flex: 1;
+						overflow: hidden;
+						i {
+							font-size: 14px;
+						}
+					}
+					.tip {
+						overflow: hidden;
+						white-space: nowrap;
+						text-overflow: ellipsis;
+					}
+					&:hover {
+						background: rgba(0, 0, 0, 0.05);
+						cursor: default;
+						border-radius: 4px;
+					}
+					&:first-child:hover {
+						background: unset;
+					}
+				}
+				.sky-dl-first {
+					color: #43bdf0;
+				}
+			}
+			.d-states {
+				display: flex;
+				.d-states-item {
+					flex: 1;
+					display: flex;
+					align-items: center;
+					overflow: hidden;
+					i {
+						font-size: 20px;
+						height: 33px;
+						width: 33px;
+						line-height: 33px;
+						text-align: center;
+						border-radius: 100%;
+						flex-shrink: 1;
+						display: flex;
+						align-items: center;
+						justify-content: center;
+					}
+					.i-bg1 {
+						background: #22bc76;
+					}
+					.i-bg2 {
+						background: #e2356d;
+					}
+					.i-bg3 {
+						background: #43bbef;
+					}
+					.d-states-flex {
+						overflow: hidden;
+						padding: 0 10px 0;
+						.d-states-item-label {
+							color: #43bdf0;
+							overflow: hidden;
+							white-space: nowrap;
+							text-overflow: ellipsis;
+						}
+						.d-states-item-value {
+							font-size: 20px;
+							text-align: center;
+						}
+					}
+				}
+			}
+			.d-btn {
+				margin-top: 15px;
+				.d-btn-item {
+					border: 1px solid #c0d1f2;
+					display: flex;
+					width: 100%;
+					height: 35px;
+					border-radius: 35px;
+					align-items: center;
+					padding: 0 4px;
+					margin-top: 15px;
+					cursor: pointer;
+					transition: all ease 0.3s;
+					.d-btn-item-left {
+						font-size: 20px;
+						border: 1px solid #c0d1f2;
+						width: 25px;
+						height: 25px;
+						line-height: 25px;
+						border-radius: 100%;
+						text-align: center;
+						font-size: 14px;
+					}
+					.d-btn-item-center {
+						padding: 0 10px;
+						flex: 1;
+					}
+					.d-btn-item-eight {
+						text-align: right;
+						padding-right: 10px;
+					}
+				}
+				.d-btn-active {
+					transition: all ease 0.3s;
+					border: 1px solid #43bdf0;
+					color: #43bdf0;
+					.d-btn-item-left {
+						border: 1px solid #43bdf0;
+					}
+				}
+			}
+		}
+		.big-data-down-center {
+			width: 40%;
+			display: flex;
+			flex-direction: column;
+			.big-data-down-center-one {
+				height: 66.67%;
+				padding: 0 7.5px 15px;
+				.big-data-down-center-one-content {
+					height: 100%;
+					position: relative;
+					.fixed-top,
+					.fixed-right,
+					.fixed-bottom,
+					.fixed-left {
+						position: absolute;
+						width: 100px;
+						height: 100px;
+						display: flex;
+						cursor: pointer;
+						.circle {
+							position: absolute;
+							border-radius: 50%;
+							background: rgba(0, 0, 0, 0.01);
+							z-index: 10;
+						}
+						.text-box {
+							position: relative;
+							z-index: 11;
+							color: #c0d1f2;
+							margin: auto;
+							text-align: center;
+							font-size: 12px;
+							i {
+								font-size: 28px;
+								margin-bottom: 10px;
+							}
+						}
+					}
+					.fixed-top {
+						left: 20px;
+						top: 20px;
+					}
+					.fixed-right {
+						right: 20px;
+						top: 20px;
+					}
+					.fixed-bottom {
+						right: 20px;
+						bottom: 20px;
+					}
+					.fixed-left {
+						left: 20px;
+						bottom: 20px;
+					}
+					.circle:nth-of-type(1) {
+						width: 100px;
+						height: 95px;
+						animation: turnAround 6s infinite linear;
+						box-shadow: 0 0 1px 0 #869fe4, inset 0 0 10px 0 #869fe4;
+					}
+					.circle:nth-of-type(2) {
+						width: 95px;
+						height: 100px;
+						animation: turnAround 10s infinite linear;
+						box-shadow: 0 0 1px 0 #3397f2, inset 0 0 10px 0 #3397f2;
+					}
+					.circle:nth-of-type(3) {
+						width: 110px;
+						height: 100px;
+						animation: turnAround 5s infinite linear;
+						box-shadow: 0 0 1px 0 #3eaadc, inset 0 0 10px 0 #3eaadc;
+					}
+					.circle:nth-of-type(4) {
+						width: 100px;
+						height: 110px;
+						animation: turnAround 15s infinite linear;
+						box-shadow: 0 0 1px 0 #09f, inset 0 0 10px 0 #09f;
+					}
+					@keyframes turnAround {
+						100% {
+							transform: rotate(360deg);
+						}
+					}
+				}
+			}
+			.big-data-down-center-two {
+				padding: 0 7.5px 15px;
+				height: 33.33%;
+				.flex-warp-item-box {
+					width: 100%;
+					height: 100%;
+					background: rgba(22, 34, 58, 0.4);
+					display: flex;
+					flex-direction: column;
+					padding: 15px;
+					.flex-title {
+						margin-bottom: 15px;
+						color: #c0d1f2;
+						display: flex;
+						justify-content: space-between;
+						.flex-title-small {
+							font-size: 12px;
+						}
+					}
+					.flex-content {
+						flex: 1;
+						font-size: 12px;
+						display: flex;
+						height: calc(100% - 30px);
+						.flex-content-left {
+							display: flex;
+							flex-wrap: wrap;
+							width: 120px;
+							height: 100%;
+							.monitor-item {
+								width: 50%;
+								display: flex;
+								align-items: center;
+								.monitor-wave {
+									cursor: pointer;
+									width: 40px;
+									height: 40px;
+									position: relative;
+									background-color: #43bdf0;
+									border-radius: 50%;
+									overflow: hidden;
+									text-align: center;
+									&::before,
+									&::after {
+										content: '';
+										position: absolute;
+										left: 50%;
+										width: 40px;
+										height: 40px;
+										background: #f4f4f4;
+										animation: roateOne 10s linear infinite;
+										transform: translateX(-50%);
+										z-index: 1;
+									}
+									&::before {
+										bottom: 10px;
+										border-radius: 60%;
+									}
+									&::after {
+										bottom: 8px;
+										opacity: 0.7;
+										border-radius: 37%;
+									}
+									.monitor-z-index {
+										position: relative;
+										z-index: 2;
+										color: #4eb8ff;
+										display: flex;
+										align-items: center;
+										height: 100%;
+										justify-content: center;
+										font-weight: bold;
+									}
+								}
+								@keyframes roateOne {
+									0% {
+										transform: translate(-50%, 0) rotateZ(0deg);
+									}
+									50% {
+										transform: translate(-50%, -2%) rotateZ(180deg);
+									}
+									100% {
+										transform: translate(-50%, 0%) rotateZ(360deg);
+									}
+								}
+								.monitor-active {
+									background-color: #22bc76;
+									.monitor-z-index {
+										color: #22bc76;
+									}
+								}
+							}
+						}
+						.flex-content-right {
+							flex: 1;
+						}
+					}
+				}
+			}
+		}
+		.big-data-down-right {
+			.flex-warp-item {
+				padding: 0 15px 15px 7.5px;
+				.flex-content {
+					display: flex;
+					flex-direction: column;
+					.task {
+						display: flex;
+						height: 45px;
+						.task-item {
+							flex: 1;
+							color: #c0d1f2;
+							display: flex;
+							justify-content: center;
+							.task-item-box {
+								position: relative;
+								width: 45px;
+								height: 45px;
+								overflow: hidden;
+								border-radius: 100%;
+								z-index: 0;
+								display: flex;
+								align-items: center;
+								flex-direction: column;
+								justify-content: center;
+								box-shadow: 0 10px 12px 0 rgba(0, 0, 0, 0.3);
+								&::before {
+									content: '';
+									position: absolute;
+									z-index: -2;
+									left: -50%;
+									top: -50%;
+									width: 200%;
+									height: 200%;
+									background-repeat: no-repeat;
+									background-size: 50% 50%, 50% 50%;
+									background-position: 0 0, 100% 0, 100% 100%, 0 100%;
+									background-image: linear-gradient(#19d4ae, #19d4ae), linear-gradient(#5ab1ef, #5ab1ef), linear-gradient(#fa6e86, #fa6e86),
+										linear-gradient(#ffb980, #ffb980);
+									animation: rotate 2s linear infinite;
+								}
+								&::after {
+									content: '';
+									position: absolute;
+									z-index: -1;
+									left: 1px;
+									top: 1px;
+									width: calc(100% - 2px);
+									height: calc(100% - 2px);
+									border-radius: 100%;
+								}
+								.task-item-value {
+									text-align: center;
+									font-size: 14px;
+									font-weight: bold;
+								}
+								.task-item-label {
+									text-align: center;
+								}
+							}
+							.task1 {
+								&::after {
+									background: #5492be;
+								}
+							}
+							.task2 {
+								&::after {
+									background: #43a177;
+								}
+							}
+							.task3 {
+								&::after {
+									background: #a76077;
+								}
+							}
+							.task4 {
+								&::after {
+									background: #b4825a;
+								}
+							}
+							.task5 {
+								&::after {
+									background: #74568f;
+								}
+							}
+						}
+						.task-first-item {
+							flex-direction: column;
+							text-align: center;
+							.task-first {
+								font-size: 20px;
+							}
+						}
+					}
+				}
+			}
+			.progress {
+				flex: 1;
+			}
+		}
+	}
+}
+</style>
diff --git a/src/views/visualizing/images/bathymetry.jpg b/src/views/visualizing/images/bathymetry.jpg
new file mode 100644
index 0000000..5f9eebe
--- /dev/null
+++ b/src/views/visualizing/images/bathymetry.jpg
Binary files differ
diff --git a/src/views/visualizing/images/world.jpg b/src/views/visualizing/images/world.jpg
new file mode 100644
index 0000000..22c7566
--- /dev/null
+++ b/src/views/visualizing/images/world.jpg
Binary files differ
diff --git a/src/views/visualizing/mock/demo1.ts b/src/views/visualizing/mock/demo1.ts
new file mode 100644
index 0000000..60fd944
--- /dev/null
+++ b/src/views/visualizing/mock/demo1.ts
@@ -0,0 +1,51 @@
+// 地图模拟数据
+export const echartsMapList: Array<object> = [
+	{ name: '深圳市人民政府', value: '100' },
+	{ name: '莲花山公园', value: '100' },
+	{ name: '世界之窗', value: '100' },
+	{ name: '华侨城欢乐谷', value: '100' },
+	{ name: '宝安区西乡', value: '100' },
+];
+
+// 地图经纬度数据
+export const echartsMapData: object = {
+	深圳市人民政府: [114.064524, 22.549225],
+	莲花山公园: [114.0658, 22.560072],
+	世界之窗: [113.979419, 22.540579],
+	华侨城欢乐谷: [113.986066, 22.548056],
+	宝安区西乡: [113.869053, 22.581714],
+};
+
+// 地图图片显示
+export const echartsMapImgs: Array<object> = [
+	{
+		url: 'https://img1.baidu.com/it/u=4244861097,3561366422&fm=11&fmt=auto&gp=0.jpg',
+		name: '深圳市人民政府',
+		add: '深圳市福田区福中三路市民中心C区',
+		dec: '深圳市人民政府是根据《中华人民共和国地方各级人民代表大会和地方各级人民政府组织法》设立的,是深圳市人民代表大会的执行机关,是深圳市的国家行政机关。',
+	},
+	{
+		url: 'https://img1.baidu.com/it/u=3793608028,4006842751&fm=26&fmt=auto&gp=0.jpg',
+		name: '莲花山公园',
+		add: '广东省深圳市福田区莲花街道莲花北社区红荔路6030号',
+		dec: '莲花山公园筹建于1992年10月10日 ,1997年6月23日正式对外局部开放。',
+	},
+	{
+		url: 'https://img0.baidu.com/it/u=1406340112,1927292660&fm=26&fmt=auto&gp=0.jpg',
+		name: '世界之窗',
+		add: '深圳市南山区深南大道9037号',
+		dec: '这里,世界首座实景拍摄悬空式球幕影院“飞跃美利坚””,为游客提供集休闲放松于一体的都市时尚生活空间。',
+	},
+	{
+		url: 'https://img0.baidu.com/it/u=3042342330,902556630&fm=26&fmt=auto&gp=0.jpg',
+		name: '华侨城欢乐谷',
+		add: '广东省深圳市南山区沙河街道星河街社区侨城西街1号',
+		dec: '深圳欢乐谷注重满足人们参与、体验的新型诱游需求,营造出自然、清新、活泼、惊奇、热烈、刺激的休闲旅游氛围。',
+	},
+	{
+		url: 'https://img2.baidu.com/it/u=1075072079,1229283519&fm=11&fmt=auto&gp=0.jpg',
+		name: '宝安区西乡',
+		add: '西乡街道下辖25个社区',
+		dec: '西乡街道,隶属于广东省深圳市宝安区,位于宝安区西南部,东接石岩街道,南接新安街道,西至珠江口岸边,北接航城街道。',
+	},
+];
diff --git a/src/views/visualizing/mock/demo2.ts b/src/views/visualizing/mock/demo2.ts
new file mode 100644
index 0000000..6b302b4
--- /dev/null
+++ b/src/views/visualizing/mock/demo2.ts
@@ -0,0 +1,131 @@
+// 顶部下来菜单
+export const dropdownList: Array<object> = [
+	{
+		label: '广东省农业农村厅',
+	},
+	{
+		label: '广西省农业农村厅',
+	},
+	{
+		label: '四川省农业农村厅',
+	},
+	{
+		label: '湖北省农业农村厅',
+	},
+	{
+		label: '福建省农业农村厅',
+	},
+	{
+		label: '山东省农业农村厅',
+	},
+	{
+		label: '江西省农业农村厅',
+	},
+];
+
+// sky 天气
+export const skyList: Array<object> = [
+	{
+		v1: '时间',
+		v2: '天气',
+		v3: '温度',
+		v4: '湿度',
+		v5: '降水概率',
+		v6: '风向',
+		v7: '风力',
+		type: 'title',
+	},
+	{
+		v1: '今天',
+		v2: 'ele-Sunny',
+		v3: '20°/26°',
+		v4: '80%',
+		v5: '50%',
+		v6: '东南风',
+		v7: '13m/s',
+	},
+	{
+		v1: '明天',
+		v2: 'ele-Lightning',
+		v3: '20°/26°',
+		v4: '80%',
+		v5: '50%',
+		v6: '东南风',
+		v7: '13m/s',
+	},
+	{
+		v1: '后天',
+		v2: 'ele-Sunny',
+		v3: '20°/26°',
+		v4: '80%',
+		v5: '50%',
+		v6: '东南风',
+		v7: '13m/s',
+	},
+];
+
+// 当前设置状态
+export const dBtnList: Array<object> = [
+	{
+		v1: '地块A-灌溉',
+		v2: '阳光玫瑰种植',
+		v3: '126天',
+		v4: '设备在线',
+	},
+	{
+		v1: '地块B-收割',
+		v2: '阳光玫瑰种植',
+		v3: '360天',
+		v4: '设备预警',
+	},
+];
+
+// 当前设备监测
+export const chartData4List: Array<object> = [
+	{
+		label: '温度',
+	},
+	{
+		label: '光照',
+	},
+	{
+		label: '湿度',
+	},
+	{
+		label: '风力',
+	},
+	{
+		label: '张力',
+	},
+	{
+		label: '气压',
+	},
+];
+
+// 3DEarth 地图周围按钮组
+export const earth3DBtnList: Array<object> = [
+	{
+		topLevelClass: 'fixed-top',
+		icon: 'ele-MagicStick',
+		label: '环境监测',
+		type: 0,
+	},
+	{
+		topLevelClass: 'fixed-right',
+		icon: 'ele-MoonNight',
+		label: '精准管理',
+		type: 1,
+	},
+	{
+		topLevelClass: 'fixed-bottom',
+		icon: 'ele-TrendCharts',
+		label: '数据报表',
+		type: 2,
+	},
+	{
+		topLevelClass: 'fixed-left',
+		icon: 'ele-Van',
+		label: '产品追溯',
+		type: 3,
+	},
+];
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..9d62054
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,72 @@
+{
+	"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. */
+	}
+}
diff --git a/vite.config.ts b/vite.config.ts
new file mode 100644
index 0000000..cf7bc27
--- /dev/null
+++ b/vite.config.ts
@@ -0,0 +1,79 @@
+import vue from '@vitejs/plugin-vue';
+import { resolve } from 'path';
+import { defineConfig, loadEnv, ConfigEnv } from 'vite';
+
+const pathResolve = (dir: string): any => {
+	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()],
+		root: process.cwd(),
+		resolve: { alias },
+		base: mode.command === 'serve' ? './' : env.VITE_PUBLIC_PATH,
+		hmr: true,
+		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: env.VITE_OPEN,
+			proxy: {
+				'/gitee': {
+					target: 'https://gitee.com',
+					ws: true,
+					changeOrigin: true,
+					rewrite: (path) => path.replace(/^\/gitee/, ''),
+				},
+			},
+		},
+		build: {
+			outDir: 'dist',
+			sourcemap: false,
+			chunkSizeWarningLimit: 1500,
+			rollupOptions: {
+				output: {
+					entryFileNames: `assets/[name].${new Date().getTime()}.js`,
+					chunkFileNames: `assets/[name].${new Date().getTime()}.js`,
+					assetFileNames: `assets/[name].${new Date().getTime()}.[ext]`,
+					compact: true,
+					manualChunks: {
+						vue: ['vue', 'vue-router', 'pinia'],
+						echarts: ['echarts'],
+					},
+				},
+			},
+		},
+		css: {
+			postcss: {
+				plugins: [
+					{
+						postcssPlugin: 'internal:charset-removal',
+						AtRule: {
+							charset: (atRule) => {
+								if (atRule.name === 'charset') {
+									atRule.remove();
+								}
+							},
+						},
+					},
+				],
+			},
+		},
+		define: {
+			__VUE_I18N_LEGACY_API__: JSON.stringify(false),
+			__VUE_I18N_FULL_INSTALL__: JSON.stringify(false),
+			__INTLIFY_PROD_DEVTOOLS__: JSON.stringify(false),
+		},
+	};
+});
+
+export default viteConfig;
diff --git a/yarn.lock b/yarn.lock
new file mode 100644
index 0000000..9c75e08
--- /dev/null
+++ b/yarn.lock
@@ -0,0 +1,2236 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@babel/parser@^7.16.4":
+  version "7.18.4"
+  resolved "https://registry.npmmirror.com/@babel/parser/-/parser-7.18.4.tgz#6774231779dd700e0af29f6ad8d479582d7ce5ef"
+  integrity sha512-FDge0dFazETFcxGw/EXzOkN8uJp0PC7Qbm+Pe9T+av2zlBpOgunFHkQPPn+eRuClU73JF+98D531UgayY89tow==
+
+"@babel/runtime@^7.12.0":
+  version "7.18.3"
+  resolved "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.18.3.tgz#c7b654b57f6f63cf7f8b418ac9ca04408c4579f4"
+  integrity sha512-38Y8f7YUhce/K7RMwTp7m0uCumpv9hZkitCbBClqQIow1qSbCvGkcegKOXpEWCQLfWmevgRiWokZ1GkpfhbZug==
+  dependencies:
+    regenerator-runtime "^0.13.4"
+
+"@ctrl/tinycolor@^3.4.1":
+  version "3.4.1"
+  resolved "https://registry.npmmirror.com/@ctrl/tinycolor/-/tinycolor-3.4.1.tgz#75b4c27948c81e88ccd3a8902047bcd797f38d32"
+  integrity sha512-ej5oVy6lykXsvieQtqZxCOaLT+xD4+QNarq78cIYISHmZXshCvROLudpQN3lfL8G0NL7plMSSK+zlyvCaIJ4Iw==
+
+"@element-plus/icons-vue@^2.0.3":
+  version "2.0.4"
+  resolved "https://registry.npmmirror.com/@element-plus/icons-vue/-/icons-vue-2.0.4.tgz#98fb9680c814a2a4f231b8bdabc8cd59b1b79d86"
+  integrity sha512-UeBVBU3fuBsYa9mzM7DgkRztQ1Aftw3sMTI/1gZsqXq2NWiCOi16ZYXXGIc0jFDIu+k6SojzdlxOjv+rN/Y6FQ==
+
+"@element-plus/icons-vue@^2.0.5":
+  version "2.0.5"
+  resolved "https://registry.npmmirror.com/@element-plus/icons-vue/-/icons-vue-2.0.5.tgz#8eb4143a7b5e4d8468d2e72af99eefee446f5ea0"
+  integrity sha512-jvNWyKcdvPvMDLTWjghrPY+bYHKqh7hbAFIPe+HWR073zilzt33csREzmKx3VwhdlJUW5u0nCqN+0rwI8jlH+w==
+
+"@eslint/eslintrc@^1.3.0":
+  version "1.3.0"
+  resolved "https://registry.npmmirror.com/@eslint/eslintrc/-/eslintrc-1.3.0.tgz#29f92c30bb3e771e4a2048c95fa6855392dfac4f"
+  integrity sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==
+  dependencies:
+    ajv "^6.12.4"
+    debug "^4.3.2"
+    espree "^9.3.2"
+    globals "^13.15.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@^0.7.2":
+  version "0.7.2"
+  resolved "https://registry.npmmirror.com/@floating-ui/core/-/core-0.7.2.tgz#f7af9613d080dc29360e77c970965b79b524d45a"
+  integrity sha512-FRVAkSNU/vGXLIsgbggcs70GkXKEOXgBBbNpYPNHSaKsCAMMd00NrjbtKTesxkdv9xm9N3+XiDlcFGY6WnatBg==
+
+"@floating-ui/dom@^0.5.2":
+  version "0.5.2"
+  resolved "https://registry.npmmirror.com/@floating-ui/dom/-/dom-0.5.2.tgz#908f3febbfc0d6696d70921616ec194fe07af183"
+  integrity sha512-z1DnEa7F3d8Fm/eXSbii8UEGpcjZGkQaYYUI0WpEVgD3vBfebDW8j/3ysusxonuMexoigA+A3b/fYH7sEqiwyg==
+  dependencies:
+    "@floating-ui/core" "^0.7.2"
+
+"@humanwhocodes/config-array@^0.9.2":
+  version "0.9.5"
+  resolved "https://registry.npmmirror.com/@humanwhocodes/config-array/-/config-array-0.9.5.tgz#2cbaf9a89460da24b5ca6531b8bbfc23e1df50c7"
+  integrity sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==
+  dependencies:
+    "@humanwhocodes/object-schema" "^1.2.1"
+    debug "^4.1.1"
+    minimatch "^3.0.4"
+
+"@humanwhocodes/object-schema@^1.2.1":
+  version "1.2.1"
+  resolved "https://registry.npmmirror.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45"
+  integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==
+
+"@interactjs/actions@1.10.13", "@interactjs/actions@^1.10.2":
+  version "1.10.13"
+  resolved "https://registry.npmmirror.com/@interactjs/actions/-/actions-1.10.13.tgz#278891ce7cfdc03124f1993cd45c434d43b71ef9"
+  integrity sha512-WRFfZKJ57Iwmn3w8nMvsOaryaRBBxYIWSX8iKTKsaqwHerQkd3el9yKp2miXsdsGkzabofqp5GDuB6Dwbq7Uew==
+  optionalDependencies:
+    "@interactjs/interact" "1.10.13"
+
+"@interactjs/auto-scroll@1.10.13":
+  version "1.10.13"
+  resolved "https://registry.npmmirror.com/@interactjs/auto-scroll/-/auto-scroll-1.10.13.tgz#f01599bc6b15b86027256c8dadf04349b28c4fb5"
+  integrity sha512-YfeMKjFtassTzFThDZPwZnzwGyHzELC+npzjjtb8AHcaMagYE89mZ+c61gSSDHJKpbp6YAEyU+F4uXuuhwwKsQ==
+  optionalDependencies:
+    "@interactjs/interact" "1.10.13"
+
+"@interactjs/auto-start@1.10.13", "@interactjs/auto-start@^1.10.2":
+  version "1.10.13"
+  resolved "https://registry.npmmirror.com/@interactjs/auto-start/-/auto-start-1.10.13.tgz#195a86f861820f3f6d3ff7d873e463fa164259ff"
+  integrity sha512-bZPL+aJd3YT9wlha7k7fcjjU8cXGUTAZN2G/eSds32pxhpjvlpKJMNvH0h2z7Mcnipyq9eRHVj0Q9oc1Zjy3qg==
+  optionalDependencies:
+    "@interactjs/interact" "1.10.13"
+
+"@interactjs/core@1.10.13":
+  version "1.10.13"
+  resolved "https://registry.npmmirror.com/@interactjs/core/-/core-1.10.13.tgz#62d7f9f98b047ab5046e8fe0fab58afd06f961cb"
+  integrity sha512-hHiY0yZ+vvmhGkJ4C3KhK3fFPh/ModRqdyqFMrlJ5djLCsilkA1SM+0WL6ckBwlIwKu2z+fHXLZuXt7v+7/Azw==
+
+"@interactjs/dev-tools@1.10.13", "@interactjs/dev-tools@^1.10.2":
+  version "1.10.13"
+  resolved "https://registry.npmmirror.com/@interactjs/dev-tools/-/dev-tools-1.10.13.tgz#6ac1ab3e369476321fc299b742b11ff1ccc3d4f1"
+  integrity sha512-niQ7k4xAr8vpgmYhv3aBjddNZjqvAvn57YWCR7YCrfnH+o72mDLnrYg5rcvXF88qr7Rv+xl22V1wBkTX6JOEzA==
+  optionalDependencies:
+    "@interactjs/interact" "1.10.13"
+
+"@interactjs/inertia@1.10.13":
+  version "1.10.13"
+  resolved "https://registry.npmmirror.com/@interactjs/inertia/-/inertia-1.10.13.tgz#57115d9b3d6f1d070b8a3013661f522926be50b4"
+  integrity sha512-9RRR92EsUjlMPQ5LH9SgP2Litcluk24VOqwm+LZP74w6+9SuApuVd0W+aZcXBGrOaUdm42KrywPU7801A0eomw==
+  dependencies:
+    "@interactjs/offset" "1.10.13"
+  optionalDependencies:
+    "@interactjs/interact" "1.10.13"
+
+"@interactjs/interact@1.10.13":
+  version "1.10.13"
+  resolved "https://registry.npmmirror.com/@interactjs/interact/-/interact-1.10.13.tgz#505724af99f46036bcfb2760de6788535a02d90b"
+  integrity sha512-ctz3CXpoxd4Z7FhayGERx6ufWNUjeP8Xim1KpdSrfpVv8SF0PDtgcYn3HHhL4ImeJtdn+yZFhdMc/x01MjvHHg==
+  dependencies:
+    "@interactjs/core" "1.10.13"
+    "@interactjs/types" "1.10.13"
+    "@interactjs/utils" "1.10.13"
+
+"@interactjs/interactjs@^1.10.2":
+  version "1.10.13"
+  resolved "https://registry.npmmirror.com/@interactjs/interactjs/-/interactjs-1.10.13.tgz#bd9d67755eadcd66459c12e96691e45982da00e3"
+  integrity sha512-RfSYrttHQkfj+hqkJb86upjcoUtmD0FVWr62xsAwJy0sCWKzk34n/ofXlBFPM1kl/d+LqpyINbZEKaaJDE6BOw==
+  dependencies:
+    "@interactjs/actions" "1.10.13"
+    "@interactjs/auto-scroll" "1.10.13"
+    "@interactjs/auto-start" "1.10.13"
+    "@interactjs/core" "1.10.13"
+    "@interactjs/dev-tools" "1.10.13"
+    "@interactjs/inertia" "1.10.13"
+    "@interactjs/interact" "1.10.13"
+    "@interactjs/modifiers" "1.10.13"
+    "@interactjs/offset" "1.10.13"
+    "@interactjs/pointer-events" "1.10.13"
+    "@interactjs/reflow" "1.10.13"
+    "@interactjs/utils" "1.10.13"
+
+"@interactjs/modifiers@1.10.13", "@interactjs/modifiers@^1.10.2":
+  version "1.10.13"
+  resolved "https://registry.npmmirror.com/@interactjs/modifiers/-/modifiers-1.10.13.tgz#09e0a8044909ef2c977873d7925ea165ba0370bd"
+  integrity sha512-WeIxPOtV/eZg23B95Z3dazy5Y6Kq5+1MQ2msnHJ22JBrJZ2nfNttXT6X9NNmXTPq1t5vqwoBsdlQ9YIwj78siQ==
+  dependencies:
+    "@interactjs/snappers" "1.10.13"
+  optionalDependencies:
+    "@interactjs/interact" "1.10.13"
+
+"@interactjs/offset@1.10.13":
+  version "1.10.13"
+  resolved "https://registry.npmmirror.com/@interactjs/offset/-/offset-1.10.13.tgz#8ca249ac6a0c6284765b3fd91ef657ea39adb5ed"
+  integrity sha512-fVZ5XAOHjafXzFTqt53bgwL5v4u6LgrYmx0/8ApMPLbrxX0W4IckETtAMx//3PUIOJ3Y6XhdKKKAOmPe8+LpiQ==
+  optionalDependencies:
+    "@interactjs/interact" "1.10.13"
+
+"@interactjs/pointer-events@1.10.13":
+  version "1.10.13"
+  resolved "https://registry.npmmirror.com/@interactjs/pointer-events/-/pointer-events-1.10.13.tgz#9b9032b3a07855f417473d85231cddcdf0c14c85"
+  integrity sha512-bIbajHzmQb6+ab53Apk9VzaSXXC03miuwuMXSn/jq9JvkVMgRaoCHjTXq8UI8eGyq5Pi4AT5kqnWhB3x9FqwEw==
+  optionalDependencies:
+    "@interactjs/interact" "1.10.13"
+
+"@interactjs/reflow@1.10.13":
+  version "1.10.13"
+  resolved "https://registry.npmmirror.com/@interactjs/reflow/-/reflow-1.10.13.tgz#ed2301d42873438c9f6d374b70331d20cc9edf8b"
+  integrity sha512-nTo0l6qEYlcWRmg3V+ko7HNDcsy/HljVc7F8LoJyRpgrE/ZXMKM8w7SPMdU3iGaQXGVNw6lPVXqJowPfS+LhIw==
+  optionalDependencies:
+    "@interactjs/interact" "1.10.13"
+
+"@interactjs/snappers@1.10.13":
+  version "1.10.13"
+  resolved "https://registry.npmmirror.com/@interactjs/snappers/-/snappers-1.10.13.tgz#bcc0985938145eb39020449daee6cd9050dea0e2"
+  integrity sha512-UT8IrFaZsLEMfiZn99o+X/7ixbhjo8+O/YqdlmHUJ3E/Ac+bVQmb1Qfz5VgiRNKgsjmTCi4uCj72Ikf7nf9kDg==
+  optionalDependencies:
+    "@interactjs/interact" "1.10.13"
+
+"@interactjs/types@1.10.13":
+  version "1.10.13"
+  resolved "https://registry.npmmirror.com/@interactjs/types/-/types-1.10.13.tgz#0ebcc9ab582cb27e84ccdd2a7a33c87076cd511e"
+  integrity sha512-VdsUBs2YSdrgB2ZuL2UL0WW3fH6XuINjdk7XE9K+r64X9k2IC3nQm7pIg7HjO3iiLtuBNUVEGy+eJwU1G8t/Lg==
+
+"@interactjs/utils@1.10.13":
+  version "1.10.13"
+  resolved "https://registry.npmmirror.com/@interactjs/utils/-/utils-1.10.13.tgz#dbdc8a15ed2470919b56f0e849d18da0c667ae95"
+  integrity sha512-WWgG2NmGIdPudytNaF5KhqnAZ/2JPpnUs9mX0AKwdbLSuG2n5kOYJv4t8IOHkXhPDuoY/PZ5r19ODHWUXkCVlQ==
+
+"@intlify/core-base@9.1.10":
+  version "9.1.10"
+  resolved "https://registry.npmmirror.com/@intlify/core-base/-/core-base-9.1.10.tgz#cbd3099f375c789a1b974f3ea79b6efb8bb148fa"
+  integrity sha512-So9CNUavB/IsZ+zBmk2Cv6McQp6vc2wbGi1S0XQmJ8Vz+UFcNn9MFXAe9gY67PreIHrbLsLxDD0cwo1qsxM1Nw==
+  dependencies:
+    "@intlify/devtools-if" "9.1.10"
+    "@intlify/message-compiler" "9.1.10"
+    "@intlify/message-resolver" "9.1.10"
+    "@intlify/runtime" "9.1.10"
+    "@intlify/shared" "9.1.10"
+    "@intlify/vue-devtools" "9.1.10"
+
+"@intlify/devtools-if@9.1.10":
+  version "9.1.10"
+  resolved "https://registry.npmmirror.com/@intlify/devtools-if/-/devtools-if-9.1.10.tgz#8704852a4fa547df43df71a16b1cc4b27e758aa3"
+  integrity sha512-SHaKoYu6sog3+Q8js1y3oXLywuogbH1sKuc7NSYkN3GElvXSBaMoCzW+we0ZSFqj/6c7vTNLg9nQ6rxhKqYwnQ==
+  dependencies:
+    "@intlify/shared" "9.1.10"
+
+"@intlify/message-compiler@9.1.10":
+  version "9.1.10"
+  resolved "https://registry.npmmirror.com/@intlify/message-compiler/-/message-compiler-9.1.10.tgz#271f5e1cb65f3cec4b1fb243e50615747613f4be"
+  integrity sha512-+JiJpXff/XTb0EadYwdxOyRTB0hXNd4n1HaJ/a4yuV960uRmPXaklJsedW0LNdcptd/hYUZtCkI7Lc9J5C1gxg==
+  dependencies:
+    "@intlify/message-resolver" "9.1.10"
+    "@intlify/shared" "9.1.10"
+    source-map "0.6.1"
+
+"@intlify/message-resolver@9.1.10":
+  version "9.1.10"
+  resolved "https://registry.npmmirror.com/@intlify/message-resolver/-/message-resolver-9.1.10.tgz#fb1dabdec2e29942df26f47e19444278a6e2f070"
+  integrity sha512-5YixMG/M05m0cn9+gOzd4EZQTFRUu8RGhzxJbR1DWN21x/Z3bJ8QpDYj6hC4FwBj5uKsRfKpJQ3Xqg98KWoA+w==
+
+"@intlify/runtime@9.1.10":
+  version "9.1.10"
+  resolved "https://registry.npmmirror.com/@intlify/runtime/-/runtime-9.1.10.tgz#70582a16810f68953d1cbf7183c8107a9137b580"
+  integrity sha512-7QsuByNzpe3Gfmhwq6hzgXcMPpxz8Zxb/XFI6s9lQdPLPe5Lgw4U1ovRPZTOs6Y2hwitR3j/HD8BJNGWpJnOFA==
+  dependencies:
+    "@intlify/message-compiler" "9.1.10"
+    "@intlify/message-resolver" "9.1.10"
+    "@intlify/shared" "9.1.10"
+
+"@intlify/shared@9.1.10":
+  version "9.1.10"
+  resolved "https://registry.npmmirror.com/@intlify/shared/-/shared-9.1.10.tgz#9e2527276b43ae3f354c4015eb04f855d9d7a707"
+  integrity sha512-Om54xJeo1Vw+K1+wHYyXngE8cAbrxZHpWjYzMR9wCkqbhGtRV5VLhVc214Ze2YatPrWlS2WSMOWXR8JktX/IgA==
+
+"@intlify/vue-devtools@9.1.10":
+  version "9.1.10"
+  resolved "https://registry.npmmirror.com/@intlify/vue-devtools/-/vue-devtools-9.1.10.tgz#c62535d86742bcd16593806a4fcae49f6fc8ae6d"
+  integrity sha512-5l3qYARVbkWAkagLu1XbDUWRJSL8br1Dj60wgMaKB0+HswVsrR6LloYZTg7ozyvM621V6+zsmwzbQxbVQyrytQ==
+  dependencies:
+    "@intlify/message-resolver" "9.1.10"
+    "@intlify/runtime" "9.1.10"
+    "@intlify/shared" "9.1.10"
+
+"@nodelib/fs.scandir@2.1.5":
+  version "2.1.5"
+  resolved "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
+  integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==
+  dependencies:
+    "@nodelib/fs.stat" "2.0.5"
+    run-parallel "^1.1.9"
+
+"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2":
+  version "2.0.5"
+  resolved "https://registry.npmmirror.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b"
+  integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==
+
+"@nodelib/fs.walk@^1.2.3":
+  version "1.2.8"
+  resolved "https://registry.npmmirror.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a"
+  integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==
+  dependencies:
+    "@nodelib/fs.scandir" "2.1.5"
+    fastq "^1.6.0"
+
+"@popperjs/core@npm:@sxzz/popperjs-es@^2.11.7":
+  version "2.11.7"
+  resolved "https://registry.npmmirror.com/@sxzz/popperjs-es/-/popperjs-es-2.11.7.tgz#a7f69e3665d3da9b115f9e71671dae1b97e13671"
+  integrity sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ==
+
+"@transloadit/prettier-bytes@0.0.7":
+  version "0.0.7"
+  resolved "https://registry.npmmirror.com/@transloadit/prettier-bytes/-/prettier-bytes-0.0.7.tgz#cdb5399f445fdd606ed833872fa0cabdbc51686b"
+  integrity sha512-VeJbUb0wEKbcwaSlj5n+LscBl9IPgLPkHVGBkh00cztv6X4L/TJXK58LzFuBKX7/GAfiGhIwH67YTLTlzvIzBA==
+
+"@types/event-emitter@^0.3.3":
+  version "0.3.3"
+  resolved "https://registry.npmmirror.com/@types/event-emitter/-/event-emitter-0.3.3.tgz#727032a9fc67565f96bbd78b2e2809275c97d7e7"
+  integrity sha512-UfnOK1pIxO7P+EgPRZXD9jMpimd8QEFcEZ5R67R1UhGbv4zghU5+NE7U8M8G9H5Jc8FI51rqDWQs6FtUfq2e/Q==
+
+"@types/json-schema@^7.0.9":
+  version "7.0.11"
+  resolved "https://registry.npmmirror.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3"
+  integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==
+
+"@types/lodash-es@^4.17.6":
+  version "4.17.6"
+  resolved "https://registry.npmmirror.com/@types/lodash-es/-/lodash-es-4.17.6.tgz#c2ed4c8320ffa6f11b43eb89e9eaeec65966a0a0"
+  integrity sha512-R+zTeVUKDdfoRxpAryaQNRKk3105Rrgx2CFRClIgRGaqDTdjsm8h6IYA8ir584W3ePzkZfst5xIgDwYrlh9HLg==
+  dependencies:
+    "@types/lodash" "*"
+
+"@types/lodash@*", "@types/lodash@^4.14.182":
+  version "4.14.182"
+  resolved "https://registry.npmmirror.com/@types/lodash/-/lodash-4.14.182.tgz#05301a4d5e62963227eaafe0ce04dd77c54ea5c2"
+  integrity sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q==
+
+"@types/node@^17.0.39":
+  version "17.0.41"
+  resolved "https://registry.npmmirror.com/@types/node/-/node-17.0.41.tgz#1607b2fd3da014ae5d4d1b31bc792a39348dfb9b"
+  integrity sha512-xA6drNNeqb5YyV5fO3OAEsnXLfO7uF0whiOfPTz5AeDo8KeZFmODKnvwPymMNO8qE/an8pVY/O50tig2SQCrGw==
+
+"@types/nprogress@^0.2.0":
+  version "0.2.0"
+  resolved "https://registry.npmmirror.com/@types/nprogress/-/nprogress-0.2.0.tgz#86c593682d4199212a0509cc3c4d562bbbd6e45f"
+  integrity sha512-1cYJrqq9GezNFPsWTZpFut/d4CjpZqA0vhqDUPFWYKF1oIyBz5qnoYMzR+0C/T96t3ebLAC1SSnwrVOm5/j74A==
+
+"@types/sortablejs@^1.13.0":
+  version "1.13.0"
+  resolved "https://registry.npmmirror.com/@types/sortablejs/-/sortablejs-1.13.0.tgz#870223438f8f2cd81157b128a4c0261adbcaa946"
+  integrity sha512-C3064MH72iEfeGCYEGCt7FCxXoAXaMPG0QPnstcxvPmbl54erpISu06d++FY37Smja64iWy5L8wOyHHBghWbJQ==
+
+"@typescript-eslint/eslint-plugin@^5.27.0":
+  version "5.27.1"
+  resolved "https://registry.npmmirror.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.27.1.tgz#fdf59c905354139046b41b3ed95d1609913d0758"
+  integrity sha512-6dM5NKT57ZduNnJfpY81Phe9nc9wolnMCnknb1im6brWi1RYv84nbMS3olJa27B6+irUVV1X/Wb+Am0FjJdGFw==
+  dependencies:
+    "@typescript-eslint/scope-manager" "5.27.1"
+    "@typescript-eslint/type-utils" "5.27.1"
+    "@typescript-eslint/utils" "5.27.1"
+    debug "^4.3.4"
+    functional-red-black-tree "^1.0.1"
+    ignore "^5.2.0"
+    regexpp "^3.2.0"
+    semver "^7.3.7"
+    tsutils "^3.21.0"
+
+"@typescript-eslint/parser@^5.27.0":
+  version "5.27.1"
+  resolved "https://registry.npmmirror.com/@typescript-eslint/parser/-/parser-5.27.1.tgz#3a4dcaa67e45e0427b6ca7bb7165122c8b569639"
+  integrity sha512-7Va2ZOkHi5NP+AZwb5ReLgNF6nWLGTeUJfxdkVUAPPSaAdbWNnFZzLZ4EGGmmiCTg+AwlbE1KyUYTBglosSLHQ==
+  dependencies:
+    "@typescript-eslint/scope-manager" "5.27.1"
+    "@typescript-eslint/types" "5.27.1"
+    "@typescript-eslint/typescript-estree" "5.27.1"
+    debug "^4.3.4"
+
+"@typescript-eslint/scope-manager@5.27.1":
+  version "5.27.1"
+  resolved "https://registry.npmmirror.com/@typescript-eslint/scope-manager/-/scope-manager-5.27.1.tgz#4d1504392d01fe5f76f4a5825991ec78b7b7894d"
+  integrity sha512-fQEOSa/QroWE6fAEg+bJxtRZJTH8NTskggybogHt4H9Da8zd4cJji76gA5SBlR0MgtwF7rebxTbDKB49YUCpAg==
+  dependencies:
+    "@typescript-eslint/types" "5.27.1"
+    "@typescript-eslint/visitor-keys" "5.27.1"
+
+"@typescript-eslint/type-utils@5.27.1":
+  version "5.27.1"
+  resolved "https://registry.npmmirror.com/@typescript-eslint/type-utils/-/type-utils-5.27.1.tgz#369f695199f74c1876e395ebea202582eb1d4166"
+  integrity sha512-+UC1vVUWaDHRnC2cQrCJ4QtVjpjjCgjNFpg8b03nERmkHv9JV9X5M19D7UFMd+/G7T/sgFwX2pGmWK38rqyvXw==
+  dependencies:
+    "@typescript-eslint/utils" "5.27.1"
+    debug "^4.3.4"
+    tsutils "^3.21.0"
+
+"@typescript-eslint/types@5.27.1":
+  version "5.27.1"
+  resolved "https://registry.npmmirror.com/@typescript-eslint/types/-/types-5.27.1.tgz#34e3e629501349d38be6ae97841298c03a6ffbf1"
+  integrity sha512-LgogNVkBhCTZU/m8XgEYIWICD6m4dmEDbKXESCbqOXfKZxRKeqpiJXQIErv66sdopRKZPo5l32ymNqibYEH/xg==
+
+"@typescript-eslint/typescript-estree@5.27.1":
+  version "5.27.1"
+  resolved "https://registry.npmmirror.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.27.1.tgz#7621ee78607331821c16fffc21fc7a452d7bc808"
+  integrity sha512-DnZvvq3TAJ5ke+hk0LklvxwYsnXpRdqUY5gaVS0D4raKtbznPz71UJGnPTHEFo0GDxqLOLdMkkmVZjSpET1hFw==
+  dependencies:
+    "@typescript-eslint/types" "5.27.1"
+    "@typescript-eslint/visitor-keys" "5.27.1"
+    debug "^4.3.4"
+    globby "^11.1.0"
+    is-glob "^4.0.3"
+    semver "^7.3.7"
+    tsutils "^3.21.0"
+
+"@typescript-eslint/utils@5.27.1":
+  version "5.27.1"
+  resolved "https://registry.npmmirror.com/@typescript-eslint/utils/-/utils-5.27.1.tgz#b4678b68a94bc3b85bf08f243812a6868ac5128f"
+  integrity sha512-mZ9WEn1ZLDaVrhRaYgzbkXBkTPghPFsup8zDbbsYTxC5OmqrFE7skkKS/sraVsLP3TcT3Ki5CSyEFBRkLH/H/w==
+  dependencies:
+    "@types/json-schema" "^7.0.9"
+    "@typescript-eslint/scope-manager" "5.27.1"
+    "@typescript-eslint/types" "5.27.1"
+    "@typescript-eslint/typescript-estree" "5.27.1"
+    eslint-scope "^5.1.1"
+    eslint-utils "^3.0.0"
+
+"@typescript-eslint/visitor-keys@5.27.1":
+  version "5.27.1"
+  resolved "https://registry.npmmirror.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.27.1.tgz#05a62666f2a89769dac2e6baa48f74e8472983af"
+  integrity sha512-xYs6ffo01nhdJgPieyk7HAOpjhTsx7r/oB9LWEhwAXgwn33tkr+W8DI2ChboqhZlC4q3TC6geDYPoiX8ROqyOQ==
+  dependencies:
+    "@typescript-eslint/types" "5.27.1"
+    eslint-visitor-keys "^3.3.0"
+
+"@uppy/companion-client@^2.2.1":
+  version "2.2.1"
+  resolved "https://registry.npmmirror.com/@uppy/companion-client/-/companion-client-2.2.1.tgz#cef4b15185dd8ee6024386c8aae809e8e05dcd53"
+  integrity sha512-Y3E10NJLMfp/wjgthNhx3gJtT67fzFCPNPFwpNNRs5iJsW6PANhJ420eyMUFzfmEZ56ZzGYxr5pzJZx8YxHICQ==
+  dependencies:
+    "@uppy/utils" "^4.1.0"
+    namespace-emitter "^2.0.1"
+
+"@uppy/core@^2.1.1":
+  version "2.3.1"
+  resolved "https://registry.npmmirror.com/@uppy/core/-/core-2.3.1.tgz#1d7a40f0a0b96a2709115bf7d087b4e6ab1403f4"
+  integrity sha512-KV04X7ueYbYX1p37/i3QsoQSw8IDP8Yb+Bh9KNN0X2Vcun6K2VnNjhVtPmPXtyjDZooK7lVIqhRX8TZWcSfgSQ==
+  dependencies:
+    "@transloadit/prettier-bytes" "0.0.7"
+    "@uppy/store-default" "^2.1.0"
+    "@uppy/utils" "^4.1.0"
+    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@^2.1.0":
+  version "2.1.0"
+  resolved "https://registry.npmmirror.com/@uppy/store-default/-/store-default-2.1.0.tgz#3fbcac626dd515668b88762d812017bd4f9b75d4"
+  integrity sha512-BkcR1wGw6Kwbvr8m1tKF9EDDWSTJoTGnVseBF/iW4bzR22assbtxZIE1iroo68UMqYEG4rv63SX4BUEtNvVjdA==
+
+"@uppy/utils@^4.1.0":
+  version "4.1.0"
+  resolved "https://registry.npmmirror.com/@uppy/utils/-/utils-4.1.0.tgz#19f3f08cd21b383cdcdf95adbec76d9c1a694b68"
+  integrity sha512-C47DUl4uLzmQZdW+VmetIgGRurXuPsvb+/pyYqh9DJn0Phep8u7AOj/tlJA5CHv4pefNHsFjXpaWfSUG3HtW3A==
+  dependencies:
+    lodash.throttle "^4.1.1"
+
+"@uppy/xhr-upload@^2.0.3":
+  version "2.1.2"
+  resolved "https://registry.npmmirror.com/@uppy/xhr-upload/-/xhr-upload-2.1.2.tgz#0f644e3371b611b10cd5ddbd7d799ab9f88a3786"
+  integrity sha512-VCsb7J5yHsof49nnUa+Y1n27UMtqHPttQmmoCa5hmjqa9R7ZISpBkXKOQmZo526eopKNuAKSAdkHWfCm8efJTA==
+  dependencies:
+    "@uppy/companion-client" "^2.2.1"
+    "@uppy/utils" "^4.1.0"
+    nanoid "^3.1.25"
+
+"@vitejs/plugin-vue@^2.3.3":
+  version "2.3.3"
+  resolved "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-2.3.3.tgz#fbf80cc039b82ac21a1acb0f0478de8f61fbf600"
+  integrity sha512-SmQLDyhz+6lGJhPELsBdzXGc+AcaT8stgkbiTFGpXPe8Tl1tJaBw1A6pxDqDuRsVkD8uscrkx3hA7QDOoKYtyw==
+
+"@vue/compiler-core@3.2.37":
+  version "3.2.37"
+  resolved "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.2.37.tgz#b3c42e04c0e0f2c496ff1784e543fbefe91e215a"
+  integrity sha512-81KhEjo7YAOh0vQJoSmAD68wLfYqJvoiD4ulyedzF+OEk/bk6/hx3fTNVfuzugIIaTrOx4PGx6pAiBRe5e9Zmg==
+  dependencies:
+    "@babel/parser" "^7.16.4"
+    "@vue/shared" "3.2.37"
+    estree-walker "^2.0.2"
+    source-map "^0.6.1"
+
+"@vue/compiler-dom@3.2.37":
+  version "3.2.37"
+  resolved "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.2.37.tgz#10d2427a789e7c707c872da9d678c82a0c6582b5"
+  integrity sha512-yxJLH167fucHKxaqXpYk7x8z7mMEnXOw3G2q62FTkmsvNxu4FQSu5+3UMb+L7fjKa26DEzhrmCxAgFLLIzVfqQ==
+  dependencies:
+    "@vue/compiler-core" "3.2.37"
+    "@vue/shared" "3.2.37"
+
+"@vue/compiler-sfc@3.2.37", "@vue/compiler-sfc@^3.2.36":
+  version "3.2.37"
+  resolved "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.2.37.tgz#3103af3da2f40286edcd85ea495dcb35bc7f5ff4"
+  integrity sha512-+7i/2+9LYlpqDv+KTtWhOZH+pa8/HnX/905MdVmAcI/mPQOBwkHHIzrsEsucyOIZQYMkXUiTkmZq5am/NyXKkg==
+  dependencies:
+    "@babel/parser" "^7.16.4"
+    "@vue/compiler-core" "3.2.37"
+    "@vue/compiler-dom" "3.2.37"
+    "@vue/compiler-ssr" "3.2.37"
+    "@vue/reactivity-transform" "3.2.37"
+    "@vue/shared" "3.2.37"
+    estree-walker "^2.0.2"
+    magic-string "^0.25.7"
+    postcss "^8.1.10"
+    source-map "^0.6.1"
+
+"@vue/compiler-ssr@3.2.37":
+  version "3.2.37"
+  resolved "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.2.37.tgz#4899d19f3a5fafd61524a9d1aee8eb0505313cff"
+  integrity sha512-7mQJD7HdXxQjktmsWp/J67lThEIcxLemz1Vb5I6rYJHR5vI+lON3nPGOH3ubmbvYGt8xEUaAr1j7/tIFWiEOqw==
+  dependencies:
+    "@vue/compiler-dom" "3.2.37"
+    "@vue/shared" "3.2.37"
+
+"@vue/devtools-api@^6.0.0", "@vue/devtools-api@^6.0.0-beta.7", "@vue/devtools-api@^6.1.4":
+  version "6.1.4"
+  resolved "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.1.4.tgz#b4aec2f4b4599e11ba774a50c67fa378c9824e53"
+  integrity sha512-IiA0SvDrJEgXvVxjNkHPFfDx6SXw0b/TUkqMcDZWNg9fnCAHbTpoo59YfJ9QLFkwa3raau5vSlRVzMSLDnfdtQ==
+
+"@vue/reactivity-transform@3.2.37":
+  version "3.2.37"
+  resolved "https://registry.npmmirror.com/@vue/reactivity-transform/-/reactivity-transform-3.2.37.tgz#0caa47c4344df4ae59f5a05dde2a8758829f8eca"
+  integrity sha512-IWopkKEb+8qpu/1eMKVeXrK0NLw9HicGviJzhJDEyfxTR9e1WtpnnbYkJWurX6WwoFP0sz10xQg8yL8lgskAZg==
+  dependencies:
+    "@babel/parser" "^7.16.4"
+    "@vue/compiler-core" "3.2.37"
+    "@vue/shared" "3.2.37"
+    estree-walker "^2.0.2"
+    magic-string "^0.25.7"
+
+"@vue/reactivity@3.2.37":
+  version "3.2.37"
+  resolved "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.2.37.tgz#5bc3847ac58828e2b78526e08219e0a1089f8848"
+  integrity sha512-/7WRafBOshOc6m3F7plwzPeCu/RCVv9uMpOwa/5PiY1Zz+WLVRWiy0MYKwmg19KBdGtFWsmZ4cD+LOdVPcs52A==
+  dependencies:
+    "@vue/shared" "3.2.37"
+
+"@vue/runtime-core@3.2.37":
+  version "3.2.37"
+  resolved "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.2.37.tgz#7ba7c54bb56e5d70edfc2f05766e1ca8519966e3"
+  integrity sha512-JPcd9kFyEdXLl/i0ClS7lwgcs0QpUAWj+SKX2ZC3ANKi1U4DOtiEr6cRqFXsPwY5u1L9fAjkinIdB8Rz3FoYNQ==
+  dependencies:
+    "@vue/reactivity" "3.2.37"
+    "@vue/shared" "3.2.37"
+
+"@vue/runtime-dom@3.2.37":
+  version "3.2.37"
+  resolved "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.2.37.tgz#002bdc8228fa63949317756fb1e92cdd3f9f4bbd"
+  integrity sha512-HimKdh9BepShW6YozwRKAYjYQWg9mQn63RGEiSswMbW+ssIht1MILYlVGkAGGQbkhSh31PCdoUcfiu4apXJoPw==
+  dependencies:
+    "@vue/runtime-core" "3.2.37"
+    "@vue/shared" "3.2.37"
+    csstype "^2.6.8"
+
+"@vue/server-renderer@3.2.37":
+  version "3.2.37"
+  resolved "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.2.37.tgz#840a29c8dcc29bddd9b5f5ffa22b95c0e72afdfc"
+  integrity sha512-kLITEJvaYgZQ2h47hIzPh2K3jG8c1zCVbp/o/bzQOyvzaKiCquKS7AaioPI28GNxIsE/zSx+EwWYsNxDCX95MA==
+  dependencies:
+    "@vue/compiler-ssr" "3.2.37"
+    "@vue/shared" "3.2.37"
+
+"@vue/shared@3.2.37":
+  version "3.2.37"
+  resolved "https://registry.npmmirror.com/@vue/shared/-/shared-3.2.37.tgz#8e6adc3f2759af52f0e85863dfb0b711ecc5c702"
+  integrity sha512-4rSJemR2NQIo9Klm1vabqWjD8rs/ZaJSzMxkMNeJS6lHiUjjUeYFbooN19NgFjztubEKh3WlZUeOLVdbbUWHsw==
+
+"@vueuse/core@^8.6.0":
+  version "8.6.0"
+  resolved "https://registry.npmmirror.com/@vueuse/core/-/core-8.6.0.tgz#a8f80363cc63d17382423f16beae57696f376e67"
+  integrity sha512-VirzExCm/N+QdrEWT7J4uSrvJ5hquKIAU9alQ37kUvIJk9XxCLxmfRnmekYc1kz2+6BnoyuKYXVmrMV351CB4w==
+  dependencies:
+    "@vueuse/metadata" "8.6.0"
+    "@vueuse/shared" "8.6.0"
+    vue-demi "*"
+
+"@vueuse/metadata@8.6.0":
+  version "8.6.0"
+  resolved "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-8.6.0.tgz#34771443a72ee891ae001a70aa05dd9a1d799372"
+  integrity sha512-F+CKPvaExsm7QgRr8y+ZNJFwXasn89rs5wth/HeX9lJ1q8XEt+HJ16Q5Sxh4rfG5YSKXrStveVge8TKvPjMjFA==
+
+"@vueuse/shared@8.6.0":
+  version "8.6.0"
+  resolved "https://registry.npmmirror.com/@vueuse/shared/-/shared-8.6.0.tgz#63dad9fc4b73a7fccbe5d6b97adeacf73d4fec41"
+  integrity sha512-Y/IVywZo7IfEoSSEtCYpkVEmPV7pU35mEIxV7PbD/D3ly18B3mEsBaPbtDkNM/QP3zAZ5mn4nEkOfddX4uwuIA==
+  dependencies:
+    vue-demi "*"
+
+"@wangeditor/basic-modules@^1.1.1":
+  version "1.1.1"
+  resolved "https://registry.npmmirror.com/@wangeditor/basic-modules/-/basic-modules-1.1.1.tgz#1ae0b11202b2ac319b4883e46f4d069ffce2f78d"
+  integrity sha512-tQl2Pw8M2g3CM+ESx2phzr9zSKeuFCM1AMBoPdnlbatU7Dnae0CsEB/b3C+gI0dIQzM2jh34yTmqgbbhrwuRLg==
+  dependencies:
+    is-url "^1.2.4"
+
+"@wangeditor/code-highlight@^1.0.2":
+  version "1.0.2"
+  resolved "https://registry.npmmirror.com/@wangeditor/code-highlight/-/code-highlight-1.0.2.tgz#df6bf7ac8d232c6afb0cb8189baa9f8d52feeded"
+  integrity sha512-SCtOcUxjKqIso/LSxGSOaYr3G6MC2En0gNTyHIMCG928T0fo0ufaqp/vIXKQzVL2Y+X/CSAOB2EbrFlgGvr0AQ==
+  dependencies:
+    prismjs "^1.23.0"
+
+"@wangeditor/core@^1.1.1":
+  version "1.1.1"
+  resolved "https://registry.npmmirror.com/@wangeditor/core/-/core-1.1.1.tgz#f749a7d65ad95dbffccdf4d6f11272be8134eb88"
+  integrity sha512-SrbvOGlONMNMOeFIJI7fC9x0/6T6LvQHTITPCqjgbCm2QF+POcrHzRKGQOqKCsyKi9UJz9hLsjsvJnvP10rxjQ==
+  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"
+
+"@wangeditor/editor@^5.1.1":
+  version "5.1.1"
+  resolved "https://registry.npmmirror.com/@wangeditor/editor/-/editor-5.1.1.tgz#009b11277de86a4052087d0428934e7d2dd30147"
+  integrity sha512-BtccuHFm0QvYunIhIu7tllQWkwppkmEkD3OJ5Mn+F0REPQ/Z3HiEXbtlss2t9c/kHO4CtiFwv2XD/k/VEg7taA==
+  dependencies:
+    "@uppy/core" "^2.1.1"
+    "@uppy/xhr-upload" "^2.0.3"
+    "@wangeditor/basic-modules" "^1.1.1"
+    "@wangeditor/code-highlight" "^1.0.2"
+    "@wangeditor/core" "^1.1.1"
+    "@wangeditor/list-module" "^1.0.2"
+    "@wangeditor/table-module" "^1.1.0"
+    "@wangeditor/upload-image-module" "^1.0.1"
+    "@wangeditor/video-module" "^1.1.0"
+    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/list-module@^1.0.2":
+  version "1.0.2"
+  resolved "https://registry.npmmirror.com/@wangeditor/list-module/-/list-module-1.0.2.tgz#c9e57c4c34bbcc32df3a9030df2fda0bb4f77a39"
+  integrity sha512-VfENZEFvsLTiLxN/cj8cibFGy9NVV+/cfATTiLiH9ef+8lgKv8apttXYVlqIAfnlJLLuCk0cIm8c/zH+hbtrZg==
+
+"@wangeditor/table-module@^1.1.0":
+  version "1.1.0"
+  resolved "https://registry.npmmirror.com/@wangeditor/table-module/-/table-module-1.1.0.tgz#5cd4ebdd0500915c51793efd6227c43b5e185d77"
+  integrity sha512-QpjCXSzsXcsR0pEI5Pu28e8aYh9+lHcVV4TTmGV6lRGE/etQF3PHUZNGUlfhkCgmGPq+E7n/Whb4RpAM3PJVhw==
+
+"@wangeditor/upload-image-module@^1.0.1":
+  version "1.0.1"
+  resolved "https://registry.npmmirror.com/@wangeditor/upload-image-module/-/upload-image-module-1.0.1.tgz#a074518d73ce9c5cfd3e8ee56d1adf831996f142"
+  integrity sha512-vgUV4ENttTITblqtVuzleIq732OmzmzzgrIvX6b3GRGPSw5u8glJ/87tOEhvHjHECc4oFo18B7xzJ1GpBj79/w==
+
+"@wangeditor/video-module@^1.1.0":
+  version "1.1.0"
+  resolved "https://registry.npmmirror.com/@wangeditor/video-module/-/video-module-1.1.0.tgz#664405b1eeef1e9ab09f84a50270a4394f885355"
+  integrity sha512-VR6x7Vk9ebvXtxCPwobiNiTGZGgqEzCVc6ViWlNH3v4jlDIeo/s7N7OCgpvELR7X/X7GHecBu7wySDkHIskB5w==
+
+acorn-jsx@^5.3.2:
+  version "5.3.2"
+  resolved "https://registry.npmmirror.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
+  integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
+
+acorn@^8.7.1:
+  version "8.7.1"
+  resolved "https://registry.npmmirror.com/acorn/-/acorn-8.7.1.tgz#0197122c843d1bf6d0a5e83220a788f278f63c30"
+  integrity sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==
+
+ajv@^6.10.0, ajv@^6.12.4:
+  version "6.12.6"
+  resolved "https://registry.npmmirror.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
+  integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
+  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"
+
+ansi-regex@^5.0.1:
+  version "5.0.1"
+  resolved "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
+  integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
+
+ansi-styles@^4.1.0:
+  version "4.3.0"
+  resolved "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
+  integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
+  dependencies:
+    color-convert "^2.0.1"
+
+anymatch@~3.1.2:
+  version "3.1.2"
+  resolved "https://registry.npmmirror.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716"
+  integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==
+  dependencies:
+    normalize-path "^3.0.0"
+    picomatch "^2.0.4"
+
+argparse@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
+  integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
+
+array-union@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.npmmirror.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d"
+  integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==
+
+async-validator@^4.1.1:
+  version "4.1.1"
+  resolved "https://registry.npmmirror.com/async-validator/-/async-validator-4.1.1.tgz#3cd1437faa2de64743f7d56649dd904c946a18fe"
+  integrity sha512-p4DO/JXwjs8klJyJL8Q2oM4ks5fUTze/h5k10oPPKMiLe1fj3G1QMzPHNmN1Py4ycOk7WlO2DcGXv1qiESJCZA==
+
+asynckit@^0.4.0:
+  version "0.4.0"
+  resolved "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
+  integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
+
+axios@^0.27.2:
+  version "0.27.2"
+  resolved "https://registry.npmmirror.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972"
+  integrity sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==
+  dependencies:
+    follow-redirects "^1.14.9"
+    form-data "^4.0.0"
+
+balanced-match@^1.0.0:
+  version "1.0.2"
+  resolved "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
+  integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
+
+batch-processor@1.0.0:
+  version "1.0.0"
+  resolved "https://registry.npmmirror.com/batch-processor/-/batch-processor-1.0.0.tgz#75c95c32b748e0850d10c2b168f6bdbe9891ace8"
+  integrity sha512-xoLQD8gmmR32MeuBHgH0Tzd5PuSZx71ZsbhVxOCRbgktZEPe4SQy7s9Z50uPp0F/f7iw2XmkHN2xkgbMfckMDA==
+
+binary-extensions@^2.0.0:
+  version "2.2.0"
+  resolved "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
+  integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
+
+boolbase@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.npmmirror.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
+  integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==
+
+brace-expansion@^1.1.7:
+  version "1.1.11"
+  resolved "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
+  integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
+  dependencies:
+    balanced-match "^1.0.0"
+    concat-map "0.0.1"
+
+braces@^3.0.2, braces@~3.0.2:
+  version "3.0.2"
+  resolved "https://registry.npmmirror.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
+  integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
+  dependencies:
+    fill-range "^7.0.1"
+
+callsites@^3.0.0:
+  version "3.1.0"
+  resolved "https://registry.npmmirror.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
+  integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
+
+chalk@^4.0.0:
+  version "4.1.2"
+  resolved "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
+  integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
+  dependencies:
+    ansi-styles "^4.1.0"
+    supports-color "^7.1.0"
+
+"chokidar@>=3.0.0 <4.0.0":
+  version "3.5.3"
+  resolved "https://registry.npmmirror.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
+  integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==
+  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"
+  optionalDependencies:
+    fsevents "~2.3.2"
+
+claygl@^1.2.1:
+  version "1.3.0"
+  resolved "https://registry.npmmirror.com/claygl/-/claygl-1.3.0.tgz#7a6e2903210519ac358848f5d78070ed211685f3"
+  integrity sha512-+gGtJjT6SSHD2l2yC3MCubW/sCV40tZuSs5opdtn79vFSGUgp/lH139RNEQ6Jy078/L0aV8odCw8RSrUcMfLaQ==
+
+clipboard@^2.0.6:
+  version "2.0.11"
+  resolved "https://registry.npmmirror.com/clipboard/-/clipboard-2.0.11.tgz#62180360b97dd668b6b3a84ec226975762a70be5"
+  integrity sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==
+  dependencies:
+    good-listener "^1.2.2"
+    select "^1.1.2"
+    tiny-emitter "^2.0.0"
+
+color-convert@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
+  integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
+  dependencies:
+    color-name "~1.1.4"
+
+color-name@~1.1.4:
+  version "1.1.4"
+  resolved "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
+  integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
+
+combined-stream@^1.0.8:
+  version "1.0.8"
+  resolved "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
+  integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
+  dependencies:
+    delayed-stream "~1.0.0"
+
+compute-scroll-into-view@^1.0.17:
+  version "1.0.17"
+  resolved "https://registry.npmmirror.com/compute-scroll-into-view/-/compute-scroll-into-view-1.0.17.tgz#6a88f18acd9d42e9cf4baa6bec7e0522607ab7ab"
+  integrity sha512-j4dx+Fb0URmzbwwMUrhqWM2BEWHdFGx+qZ9qqASHRPqvTYdqvWnHg0H1hIbcyLnvgnoNAVMlwkepyqM3DaIFUg==
+
+concat-map@0.0.1:
+  version "0.0.1"
+  resolved "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
+  integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
+
+countup.js@^2.2.0:
+  version "2.2.0"
+  resolved "https://registry.npmmirror.com/countup.js/-/countup.js-2.2.0.tgz#e20e247abf801190056c5eeed51ceb13cef6ea0c"
+  integrity sha512-m0TvFNXm9/eFqJm+QiKVI8e0wRUHzlQSewz9dqVjlhl2DFoZtceLbomwzxHz0hJ1+r4zBC7wSpR/TpthG49h6g==
+
+cropperjs@^1.5.12:
+  version "1.5.12"
+  resolved "https://registry.npmmirror.com/cropperjs/-/cropperjs-1.5.12.tgz#d9c0db2bfb8c0d769d51739e8f916bbc44e10f50"
+  integrity sha512-re7UdjE5UnwdrovyhNzZ6gathI4Rs3KGCBSc8HCIjUo5hO42CtzyblmWLj6QWVw7huHyDMfpKxhiO2II77nhDw==
+
+cross-spawn@^7.0.2:
+  version "7.0.3"
+  resolved "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
+  integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
+  dependencies:
+    path-key "^3.1.0"
+    shebang-command "^2.0.0"
+    which "^2.0.1"
+
+cssesc@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.npmmirror.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
+  integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==
+
+csstype@^2.6.8:
+  version "2.6.20"
+  resolved "https://registry.npmmirror.com/csstype/-/csstype-2.6.20.tgz#9229c65ea0b260cf4d3d997cb06288e36a8d6dda"
+  integrity sha512-/WwNkdXfckNgw6S5R125rrW8ez139lBHWouiBvX8dfMFtcn6V81REDqnH7+CRpRipfYlyU1CmOnOxrmGcFOjeA==
+
+d@1, d@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.npmmirror.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a"
+  integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==
+  dependencies:
+    es5-ext "^0.10.50"
+    type "^1.0.1"
+
+dayjs@^1.11.3:
+  version "1.11.3"
+  resolved "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.3.tgz#4754eb694a624057b9ad2224b67b15d552589258"
+  integrity sha512-xxwlswWOlGhzgQ4TKzASQkUhqERI3egRNqgV4ScR8wlANA/A9tZ7miXa44vTTKEq5l7vWoL5G57bG3zA+Kow0A==
+
+debug@^4.1.1, debug@^4.3.2, debug@^4.3.4:
+  version "4.3.4"
+  resolved "https://registry.npmmirror.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
+  integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
+  dependencies:
+    ms "2.1.2"
+
+deep-is@^0.1.3:
+  version "0.1.4"
+  resolved "https://registry.npmmirror.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831"
+  integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==
+
+delayed-stream@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
+  integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
+
+delegate@^3.1.2:
+  version "3.2.0"
+  resolved "https://registry.npmmirror.com/delegate/-/delegate-3.2.0.tgz#b66b71c3158522e8ab5744f720d8ca0c2af59166"
+  integrity sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==
+
+dir-glob@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.npmmirror.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f"
+  integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==
+  dependencies:
+    path-type "^4.0.0"
+
+doctrine@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.npmmirror.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961"
+  integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==
+  dependencies:
+    esutils "^2.0.2"
+
+dom7@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.npmmirror.com/dom7/-/dom7-3.0.0.tgz#b861ce5d67a6becd7aaa3ad02942ff14b1240331"
+  integrity sha512-oNlcUdHsC4zb7Msx7JN3K0Nro1dzJ48knvBOnDPKJ2GV9wl1i5vydJZUSyOfrkKFDZEud/jBsTk92S/VGSAe/g==
+  dependencies:
+    ssr-window "^3.0.0-alpha.1"
+
+dotenv@^16.0.1:
+  version "16.0.1"
+  resolved "https://registry.npmmirror.com/dotenv/-/dotenv-16.0.1.tgz#8f8f9d94876c35dac989876a5d3a82a267fdce1d"
+  integrity sha512-1K6hR6wtk2FviQ4kEiSjFiH5rpzEVi8WW0x96aztHVMhEspNpc4DVOUTEHtEva5VThQ8IaBX1Pe4gSzpVVUsKQ==
+
+echarts-gl@^2.0.9:
+  version "2.0.9"
+  resolved "https://registry.npmmirror.com/echarts-gl/-/echarts-gl-2.0.9.tgz#ee228a6c7520a6fb7bbb71ea94394f3637ade033"
+  integrity sha512-oKeMdkkkpJGWOzjgZUsF41DOh6cMsyrGGXimbjK2l6Xeq/dBQu4ShG2w2Dzrs/1bD27b2pLTGSaUzouY191gzA==
+  dependencies:
+    claygl "^1.2.1"
+    zrender "^5.1.1"
+
+echarts-wordcloud@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.npmmirror.com/echarts-wordcloud/-/echarts-wordcloud-2.0.0.tgz#52ef817895801ffe9e99dd1bacab7686b2dec04a"
+  integrity sha512-K7l6pTklqdW7ZWzT/1CS0KhBSINr/cd7c5N1fVMzZMwLQHEwT7x+nivK7g5hkVh7WNcAv4Dn6/ZS5zMKRozC1g==
+
+echarts@^5.3.2:
+  version "5.3.2"
+  resolved "https://registry.npmmirror.com/echarts/-/echarts-5.3.2.tgz#0a7b3be8c48a48b2e7cb1b82121df0c208d42d2c"
+  integrity sha512-LWCt7ohOKdJqyiBJ0OGBmE9szLdfA9sGcsMEi+GGoc6+Xo75C+BkcT/6NNGRHAWtnQl2fNow05AQjznpap28TQ==
+  dependencies:
+    tslib "2.3.0"
+    zrender "5.3.1"
+
+element-plus@^2.2.2:
+  version "2.2.5"
+  resolved "https://registry.npmmirror.com/element-plus/-/element-plus-2.2.5.tgz#2bb889660c9bcb9bb71e18619915b35e0f48d569"
+  integrity sha512-Kl0yn/PQca5YQo3M3NPBP4Xl71NQuMtDx5zNXZGVyl5FjdMujXiFB9SXKYGDUCgFU3d/Rl14vB4Fpmcl2Iz+Hw==
+  dependencies:
+    "@ctrl/tinycolor" "^3.4.1"
+    "@element-plus/icons-vue" "^2.0.5"
+    "@floating-ui/dom" "^0.5.2"
+    "@popperjs/core" "npm:@sxzz/popperjs-es@^2.11.7"
+    "@types/lodash" "^4.14.182"
+    "@types/lodash-es" "^4.17.6"
+    "@vueuse/core" "^8.6.0"
+    async-validator "^4.1.1"
+    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.1.2"
+
+element-resize-detector@^1.2.1:
+  version "1.2.4"
+  resolved "https://registry.npmmirror.com/element-resize-detector/-/element-resize-detector-1.2.4.tgz#3e6c5982dd77508b5fa7e6d5c02170e26325c9b1"
+  integrity sha512-Fl5Ftk6WwXE0wqCgNoseKWndjzZlDCwuPTcoVZfCP9R3EHQF8qUtr3YUPNETegRBOKqQKPW3n4kiIWngGi8tKg==
+  dependencies:
+    batch-processor "1.0.0"
+
+es5-ext@^0.10.35, es5-ext@^0.10.50, es5-ext@~0.10.14:
+  version "0.10.61"
+  resolved "https://registry.npmmirror.com/es5-ext/-/es5-ext-0.10.61.tgz#311de37949ef86b6b0dcea894d1ffedb909d3269"
+  integrity sha512-yFhIqQAzu2Ca2I4SE2Au3rxVfmohU9Y7wqGR+s7+H7krk26NXhIRAZDgqd6xqjCEFUomDEA3/Bo/7fKmIkW1kA==
+  dependencies:
+    es6-iterator "^2.0.3"
+    es6-symbol "^3.1.3"
+    next-tick "^1.1.0"
+
+es6-iterator@^2.0.3:
+  version "2.0.3"
+  resolved "https://registry.npmmirror.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7"
+  integrity sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==
+  dependencies:
+    d "1"
+    es5-ext "^0.10.35"
+    es6-symbol "^3.1.1"
+
+es6-symbol@^3.1.1, es6-symbol@^3.1.3:
+  version "3.1.3"
+  resolved "https://registry.npmmirror.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18"
+  integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==
+  dependencies:
+    d "^1.0.1"
+    ext "^1.1.2"
+
+esbuild-android-64@0.14.43:
+  version "0.14.43"
+  resolved "https://registry.npmmirror.com/esbuild-android-64/-/esbuild-android-64-0.14.43.tgz#59bf3edad6863c27aa92bbb5c1d83a9a5c981495"
+  integrity sha512-kqFXAS72K6cNrB6RiM7YJ5lNvmWRDSlpi7ZuRZ1hu1S3w0zlwcoCxWAyM23LQUyZSs1PbjHgdbbfYAN8IGh6xg==
+
+esbuild-android-arm64@0.14.43:
+  version "0.14.43"
+  resolved "https://registry.npmmirror.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.43.tgz#0258704edf92ce2463af6d2900b844b5423bed63"
+  integrity sha512-bKS2BBFh+7XZY9rpjiHGRNA7LvWYbZWP87pLehggTG7tTaCDvj8qQGOU/OZSjCSKDYbgY7Q+oDw8RlYQ2Jt2BA==
+
+esbuild-darwin-64@0.14.43:
+  version "0.14.43"
+  resolved "https://registry.npmmirror.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.43.tgz#72a47295678d4aa0656979baa8cf6d5c8c92656f"
+  integrity sha512-/3PSilx011ttoieRGkSZ0XV8zjBf2C9enV4ScMMbCT4dpx0mFhMOpFnCHkOK0pWGB8LklykFyHrWk2z6DENVUg==
+
+esbuild-darwin-arm64@0.14.43:
+  version "0.14.43"
+  resolved "https://registry.npmmirror.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.43.tgz#5f5823170b8d85b888957f0794e186caac447aca"
+  integrity sha512-1HyFUKs8DMCBOvw1Qxpr5Vv/ThNcVIFb5xgXWK3pyT40WPvgYIiRTwJCvNs4l8i5qWF8/CK5bQxJVDjQvtv0Yw==
+
+esbuild-freebsd-64@0.14.43:
+  version "0.14.43"
+  resolved "https://registry.npmmirror.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.43.tgz#e4a48b08181053837e6cd9bda19ae0af94d493b0"
+  integrity sha512-FNWc05TPHYgaXjbPZO5/rJKSBslfG6BeMSs8GhwnqAKP56eEhvmzwnIz1QcC9cRVyO+IKqWNfmHFkCa1WJTULA==
+
+esbuild-freebsd-arm64@0.14.43:
+  version "0.14.43"
+  resolved "https://registry.npmmirror.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.43.tgz#386e780d36c1dedf3a1cdab79e0bbacd873274e6"
+  integrity sha512-amrYopclz3VohqisOPR6hA3GOWA3LZC1WDLnp21RhNmoERmJ/vLnOpnrG2P/Zao+/erKTCUqmrCIPVtj58DRoA==
+
+esbuild-linux-32@0.14.43:
+  version "0.14.43"
+  resolved "https://registry.npmmirror.com/esbuild-linux-32/-/esbuild-linux-32-0.14.43.tgz#040ed6b9ebf06d73acdf2acce7f1cd0c12fbc6a5"
+  integrity sha512-KoxoEra+9O3AKVvgDFvDkiuddCds6q71owSQEYwjtqRV7RwbPzKxJa6+uyzUulHcyGVq0g15K0oKG5CFBcvYDw==
+
+esbuild-linux-64@0.14.43:
+  version "0.14.43"
+  resolved "https://registry.npmmirror.com/esbuild-linux-64/-/esbuild-linux-64-0.14.43.tgz#8abbb7594ab6a008f2aae72d95d8a4fdc59d9000"
+  integrity sha512-EwINwGMyiJMgBby5/SbMqKcUhS5AYAZ2CpEBzSowsJPNBJEdhkCTtEjk757TN/wxgbu3QklqDM6KghY660QCUw==
+
+esbuild-linux-arm64@0.14.43:
+  version "0.14.43"
+  resolved "https://registry.npmmirror.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.43.tgz#4e8e9ce77cbf7efec65e79e512b3d2fbd2da398f"
+  integrity sha512-UlSpjMWllAc70zYbHxWuDS3FJytyuR/gHJYBr8BICcTNb/TSOYVBg6U7b3jZ3mILTrgzwJUHwhEwK18FZDouUQ==
+
+esbuild-linux-arm@0.14.43:
+  version "0.14.43"
+  resolved "https://registry.npmmirror.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.43.tgz#9e41ee5e099c0ffdfd150da154330c2c0226cc96"
+  integrity sha512-e6YzQUoDxxtyamuF12eVzzRC7bbEFSZohJ6igQB9tBqnNmIQY3fI6Cns3z2wxtbZ3f2o6idkD2fQnlvs2902Dg==
+
+esbuild-linux-mips64le@0.14.43:
+  version "0.14.43"
+  resolved "https://registry.npmmirror.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.43.tgz#4b41f465a787f91cc4fe7dffa0dcabf655935a1a"
+  integrity sha512-f+v8cInPEL1/SDP//CfSYzcDNgE4CY3xgDV81DWm3KAPWzhvxARrKxB1Pstf5mB56yAslJDxu7ryBUPX207EZA==
+
+esbuild-linux-ppc64le@0.14.43:
+  version "0.14.43"
+  resolved "https://registry.npmmirror.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.43.tgz#ca15934f5b46728dd9ac05270e783e7feaca9eaf"
+  integrity sha512-5wZYMDGAL/K2pqkdIsW+I4IR41kyfHr/QshJcNpUfK3RjB3VQcPWOaZmc+74rm4ZjVirYrtz+jWw0SgxtxRanA==
+
+esbuild-linux-riscv64@0.14.43:
+  version "0.14.43"
+  resolved "https://registry.npmmirror.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.43.tgz#70fce2b5a0605a67e58b5a357b0e00be1029836d"
+  integrity sha512-lYcAOUxp85hC7lSjycJUVSmj4/9oEfSyXjb/ua9bNl8afonaduuqtw7hvKMoKuYnVwOCDw4RSfKpcnIRDWq+Bw==
+
+esbuild-linux-s390x@0.14.43:
+  version "0.14.43"
+  resolved "https://registry.npmmirror.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.43.tgz#318d03b4f4ccc7fa44ac7562121cf4a4529e477a"
+  integrity sha512-27e43ZhHvhFE4nM7HqtUbMRu37I/4eNSUbb8FGZWszV+uLzMIsHDwLoBiJmw7G9N+hrehNPeQ4F5Ujad0DrUKQ==
+
+esbuild-netbsd-64@0.14.43:
+  version "0.14.43"
+  resolved "https://registry.npmmirror.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.43.tgz#86130ce204ef0162a96e863b55851efecc92f423"
+  integrity sha512-2mH4QF6hHBn5zzAfxEI/2eBC0mspVsZ6UVo821LpAJKMvLJPBk3XJO5xwg7paDqSqpl7p6IRrAenW999AEfJhQ==
+
+esbuild-openbsd-64@0.14.43:
+  version "0.14.43"
+  resolved "https://registry.npmmirror.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.43.tgz#0229dc2db2ded97b03bb93bba7646b30ffdf5d0d"
+  integrity sha512-ZhQpiZjvqCqO8jKdGp9+8k9E/EHSA+zIWOg+grwZasI9RoblqJ1QiZqqi7jfd6ZrrG1UFBNGe4m0NFxCFbMVbg==
+
+esbuild-sunos-64@0.14.43:
+  version "0.14.43"
+  resolved "https://registry.npmmirror.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.43.tgz#17e316216eb9f1de25d52a9000356ae5b869e736"
+  integrity sha512-DgxSi9DaHReL9gYuul2rrQCAapgnCJkh3LSHPKsY26zytYppG0HgkgVF80zjIlvEsUbGBP/GHQzBtrezj/Zq1Q==
+
+esbuild-windows-32@0.14.43:
+  version "0.14.43"
+  resolved "https://registry.npmmirror.com/esbuild-windows-32/-/esbuild-windows-32-0.14.43.tgz#a173757bc6dfd0f2656ff40b64f7f9290745778e"
+  integrity sha512-Ih3+2O5oExiqm0mY6YYE5dR0o8+AspccQ3vIAtRodwFvhuyGLjb0Hbmzun/F3Lw19nuhPMu3sW2fqIJ5xBxByw==
+
+esbuild-windows-64@0.14.43:
+  version "0.14.43"
+  resolved "https://registry.npmmirror.com/esbuild-windows-64/-/esbuild-windows-64-0.14.43.tgz#c447b23126aad158c4fe6a394342cafd97926ed1"
+  integrity sha512-8NsuNfI8xwFuJbrCuI+aBqNTYkrWErejFO5aYM+yHqyHuL8mmepLS9EPzAzk8rvfaJrhN0+RvKWAcymViHOKEw==
+
+esbuild-windows-arm64@0.14.43:
+  version "0.14.43"
+  resolved "https://registry.npmmirror.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.43.tgz#3caed1b430d394d7a7836407b9d36c4750246e76"
+  integrity sha512-7ZlD7bo++kVRblJEoG+cepljkfP8bfuTPz5fIXzptwnPaFwGS6ahvfoYzY7WCf5v/1nX2X02HDraVItTgbHnKw==
+
+esbuild@^0.14.27:
+  version "0.14.43"
+  resolved "https://registry.npmmirror.com/esbuild/-/esbuild-0.14.43.tgz#c227d585c512d3e0f23b88f50b8e16501147f647"
+  integrity sha512-Uf94+kQmy/5jsFwKWiQB4hfo/RkM9Dh7b79p8yqd1tshULdr25G2szLz631NoH3s2ujnKEKVD16RmOxvCNKRFA==
+  optionalDependencies:
+    esbuild-android-64 "0.14.43"
+    esbuild-android-arm64 "0.14.43"
+    esbuild-darwin-64 "0.14.43"
+    esbuild-darwin-arm64 "0.14.43"
+    esbuild-freebsd-64 "0.14.43"
+    esbuild-freebsd-arm64 "0.14.43"
+    esbuild-linux-32 "0.14.43"
+    esbuild-linux-64 "0.14.43"
+    esbuild-linux-arm "0.14.43"
+    esbuild-linux-arm64 "0.14.43"
+    esbuild-linux-mips64le "0.14.43"
+    esbuild-linux-ppc64le "0.14.43"
+    esbuild-linux-riscv64 "0.14.43"
+    esbuild-linux-s390x "0.14.43"
+    esbuild-netbsd-64 "0.14.43"
+    esbuild-openbsd-64 "0.14.43"
+    esbuild-sunos-64 "0.14.43"
+    esbuild-windows-32 "0.14.43"
+    esbuild-windows-64 "0.14.43"
+    esbuild-windows-arm64 "0.14.43"
+
+escape-html@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.npmmirror.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
+  integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==
+
+escape-string-regexp@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
+  integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
+
+eslint-plugin-vue@^9.1.0:
+  version "9.1.0"
+  resolved "https://registry.npmmirror.com/eslint-plugin-vue/-/eslint-plugin-vue-9.1.0.tgz#b528941325e26a24bc5d5c5030c0a8996c36659c"
+  integrity sha512-EPCeInPicQ/YyfOWJDr1yfEeSNoFCMzUus107lZyYi37xejdOolNzS5MXGXp8+9bkoKZMdv/1AcZzQebME6r+g==
+  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"
+
+eslint-scope@^5.1.1:
+  version "5.1.1"
+  resolved "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c"
+  integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==
+  dependencies:
+    esrecurse "^4.3.0"
+    estraverse "^4.1.1"
+
+eslint-scope@^7.1.1:
+  version "7.1.1"
+  resolved "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-7.1.1.tgz#fff34894c2f65e5226d3041ac480b4513a163642"
+  integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==
+  dependencies:
+    esrecurse "^4.3.0"
+    estraverse "^5.2.0"
+
+eslint-utils@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.npmmirror.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672"
+  integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==
+  dependencies:
+    eslint-visitor-keys "^2.0.0"
+
+eslint-visitor-keys@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303"
+  integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==
+
+eslint-visitor-keys@^3.3.0:
+  version "3.3.0"
+  resolved "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826"
+  integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==
+
+eslint@^8.17.0:
+  version "8.17.0"
+  resolved "https://registry.npmmirror.com/eslint/-/eslint-8.17.0.tgz#1cfc4b6b6912f77d24b874ca1506b0fe09328c21"
+  integrity sha512-gq0m0BTJfci60Fz4nczYxNAlED+sMcihltndR8t9t1evnU/azx53x3t2UHXC/uRjcbvRw/XctpaNygSTcQD+Iw==
+  dependencies:
+    "@eslint/eslintrc" "^1.3.0"
+    "@humanwhocodes/config-array" "^0.9.2"
+    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.3.2"
+    esquery "^1.4.0"
+    esutils "^2.0.2"
+    fast-deep-equal "^3.1.3"
+    file-entry-cache "^6.0.1"
+    functional-red-black-tree "^1.0.1"
+    glob-parent "^6.0.1"
+    globals "^13.15.0"
+    ignore "^5.2.0"
+    import-fresh "^3.0.0"
+    imurmurhash "^0.1.4"
+    is-glob "^4.0.0"
+    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"
+    v8-compile-cache "^2.0.3"
+
+espree@^9.3.1, espree@^9.3.2:
+  version "9.3.2"
+  resolved "https://registry.npmmirror.com/espree/-/espree-9.3.2.tgz#f58f77bd334731182801ced3380a8cc859091596"
+  integrity sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==
+  dependencies:
+    acorn "^8.7.1"
+    acorn-jsx "^5.3.2"
+    eslint-visitor-keys "^3.3.0"
+
+esquery@^1.4.0:
+  version "1.4.0"
+  resolved "https://registry.npmmirror.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5"
+  integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==
+  dependencies:
+    estraverse "^5.1.0"
+
+esrecurse@^4.3.0:
+  version "4.3.0"
+  resolved "https://registry.npmmirror.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921"
+  integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==
+  dependencies:
+    estraverse "^5.2.0"
+
+estraverse@^4.1.1:
+  version "4.3.0"
+  resolved "https://registry.npmmirror.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d"
+  integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==
+
+estraverse@^5.1.0, estraverse@^5.2.0:
+  version "5.3.0"
+  resolved "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123"
+  integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==
+
+estree-walker@^2.0.2:
+  version "2.0.2"
+  resolved "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac"
+  integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==
+
+esutils@^2.0.2:
+  version "2.0.3"
+  resolved "https://registry.npmmirror.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
+  integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
+
+event-emitter@^0.3.5:
+  version "0.3.5"
+  resolved "https://registry.npmmirror.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39"
+  integrity sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==
+  dependencies:
+    d "1"
+    es5-ext "~0.10.14"
+
+ext@^1.1.2:
+  version "1.6.0"
+  resolved "https://registry.npmmirror.com/ext/-/ext-1.6.0.tgz#3871d50641e874cc172e2b53f919842d19db4c52"
+  integrity sha512-sdBImtzkq2HpkdRLtlLWDa6w4DX22ijZLKx8BMPUuKe1c5lbN6xwQDQCxSfxBQnHZ13ls/FH0MQZx/q/gr6FQg==
+  dependencies:
+    type "^2.5.0"
+
+fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
+  version "3.1.3"
+  resolved "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
+  integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
+
+fast-glob@^3.2.9:
+  version "3.2.11"
+  resolved "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9"
+  integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==
+  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"
+
+fast-json-stable-stringify@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.npmmirror.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
+  integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
+
+fast-levenshtein@^2.0.6:
+  version "2.0.6"
+  resolved "https://registry.npmmirror.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
+  integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==
+
+fastq@^1.6.0:
+  version "1.13.0"
+  resolved "https://registry.npmmirror.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c"
+  integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==
+  dependencies:
+    reusify "^1.0.4"
+
+file-entry-cache@^6.0.1:
+  version "6.0.1"
+  resolved "https://registry.npmmirror.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027"
+  integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==
+  dependencies:
+    flat-cache "^3.0.4"
+
+fill-range@^7.0.1:
+  version "7.0.1"
+  resolved "https://registry.npmmirror.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
+  integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
+  dependencies:
+    to-regex-range "^5.0.1"
+
+flat-cache@^3.0.4:
+  version "3.0.4"
+  resolved "https://registry.npmmirror.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11"
+  integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==
+  dependencies:
+    flatted "^3.1.0"
+    rimraf "^3.0.2"
+
+flatted@^3.1.0:
+  version "3.2.5"
+  resolved "https://registry.npmmirror.com/flatted/-/flatted-3.2.5.tgz#76c8584f4fc843db64702a6bd04ab7a8bd666da3"
+  integrity sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==
+
+follow-redirects@^1.14.9:
+  version "1.15.1"
+  resolved "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.1.tgz#0ca6a452306c9b276e4d3127483e29575e207ad5"
+  integrity sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==
+
+form-data@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.npmmirror.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
+  integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
+  dependencies:
+    asynckit "^0.4.0"
+    combined-stream "^1.0.8"
+    mime-types "^2.1.12"
+
+fs.realpath@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
+  integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
+
+fsevents@~2.3.2:
+  version "2.3.2"
+  resolved "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
+  integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
+
+function-bind@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
+  integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
+
+functional-red-black-tree@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.npmmirror.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
+  integrity sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==
+
+glob-parent@^5.1.2, glob-parent@~5.1.2:
+  version "5.1.2"
+  resolved "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
+  integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
+  dependencies:
+    is-glob "^4.0.1"
+
+glob-parent@^6.0.1:
+  version "6.0.2"
+  resolved "https://registry.npmmirror.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3"
+  integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==
+  dependencies:
+    is-glob "^4.0.3"
+
+glob@^7.1.3:
+  version "7.2.3"
+  resolved "https://registry.npmmirror.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
+  integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
+  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"
+
+globals@^13.15.0:
+  version "13.15.0"
+  resolved "https://registry.npmmirror.com/globals/-/globals-13.15.0.tgz#38113218c907d2f7e98658af246cef8b77e90bac"
+  integrity sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==
+  dependencies:
+    type-fest "^0.20.2"
+
+globby@^11.1.0:
+  version "11.1.0"
+  resolved "https://registry.npmmirror.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b"
+  integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==
+  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"
+
+good-listener@^1.2.2:
+  version "1.2.2"
+  resolved "https://registry.npmmirror.com/good-listener/-/good-listener-1.2.2.tgz#d53b30cdf9313dffb7dc9a0d477096aa6d145c50"
+  integrity sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw==
+  dependencies:
+    delegate "^3.1.2"
+
+has-flag@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
+  integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
+
+has@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.npmmirror.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
+  integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
+  dependencies:
+    function-bind "^1.1.1"
+
+html-void-elements@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.npmmirror.com/html-void-elements/-/html-void-elements-2.0.1.tgz#29459b8b05c200b6c5ee98743c41b979d577549f"
+  integrity sha512-0quDb7s97CfemeJAnW9wC0hw78MtW7NU3hqtCD75g2vFlDLt36llsYD7uB7SUzojLMP24N5IatXf7ylGXiGG9A==
+
+i18next@^20.4.0:
+  version "20.6.1"
+  resolved "https://registry.npmmirror.com/i18next/-/i18next-20.6.1.tgz#535e5f6e5baeb685c7d25df70db63bf3cc0aa345"
+  integrity sha512-yCMYTMEJ9ihCwEQQ3phLo7I/Pwycf8uAx+sRHwwk5U9Aui/IZYgQRyMqXafQOw5QQ7DM1Z+WyEXWIqSuJHhG2A==
+  dependencies:
+    "@babel/runtime" "^7.12.0"
+
+ignore@^5.2.0:
+  version "5.2.0"
+  resolved "https://registry.npmmirror.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a"
+  integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==
+
+immer@^9.0.6:
+  version "9.0.14"
+  resolved "https://registry.npmmirror.com/immer/-/immer-9.0.14.tgz#e05b83b63999d26382bb71676c9d827831248a48"
+  integrity sha512-ubBeqQutOSLIFCUBN03jGeOS6a3DoYlSYwYJTa+gSKEZKU5redJIqkIdZ3JVv/4RZpfcXdAWH5zCNLWPRv2WDw==
+
+immutable@^4.0.0:
+  version "4.1.0"
+  resolved "https://registry.npmmirror.com/immutable/-/immutable-4.1.0.tgz#f795787f0db780183307b9eb2091fcac1f6fafef"
+  integrity sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==
+
+import-fresh@^3.0.0, import-fresh@^3.2.1:
+  version "3.3.0"
+  resolved "https://registry.npmmirror.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b"
+  integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==
+  dependencies:
+    parent-module "^1.0.0"
+    resolve-from "^4.0.0"
+
+imurmurhash@^0.1.4:
+  version "0.1.4"
+  resolved "https://registry.npmmirror.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
+  integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==
+
+inflight@^1.0.4:
+  version "1.0.6"
+  resolved "https://registry.npmmirror.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
+  integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==
+  dependencies:
+    once "^1.3.0"
+    wrappy "1"
+
+inherits@2:
+  version "2.0.4"
+  resolved "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
+  integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
+
+is-binary-path@~2.1.0:
+  version "2.1.0"
+  resolved "https://registry.npmmirror.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09"
+  integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==
+  dependencies:
+    binary-extensions "^2.0.0"
+
+is-core-module@^2.8.1:
+  version "2.9.0"
+  resolved "https://registry.npmmirror.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69"
+  integrity sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==
+  dependencies:
+    has "^1.0.3"
+
+is-extglob@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
+  integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
+
+is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1:
+  version "4.0.3"
+  resolved "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
+  integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
+  dependencies:
+    is-extglob "^2.1.1"
+
+is-hotkey@^0.2.0:
+  version "0.2.0"
+  resolved "https://registry.npmmirror.com/is-hotkey/-/is-hotkey-0.2.0.tgz#1835a68171a91e5c9460869d96336947c8340cef"
+  integrity sha512-UknnZK4RakDmTgz4PI1wIph5yxSs/mvChWs9ifnlXsKuXgWmOkY/hAE0H/k2MIqH0RlRye0i1oC07MCRSD28Mw==
+
+is-number@^7.0.0:
+  version "7.0.0"
+  resolved "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
+  integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
+
+is-plain-object@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.npmmirror.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344"
+  integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==
+
+is-url@^1.2.4:
+  version "1.2.4"
+  resolved "https://registry.npmmirror.com/is-url/-/is-url-1.2.4.tgz#04a4df46d28c4cff3d73d01ff06abeb318a1aa52"
+  integrity sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==
+
+isexe@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
+  integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
+
+js-cookie@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.npmmirror.com/js-cookie/-/js-cookie-3.0.1.tgz#9e39b4c6c2f56563708d7d31f6f5f21873a92414"
+  integrity sha512-+0rgsUXZu4ncpPxRL+lNEptWMOWl9etvPHc/koSRp6MPwpRYAhmk0dUG00J4bxVV3r9uUzfo24wW0knS07SKSw==
+
+js-yaml@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.npmmirror.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
+  integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==
+  dependencies:
+    argparse "^2.0.1"
+
+json-schema-traverse@^0.4.1:
+  version "0.4.1"
+  resolved "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
+  integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
+
+json-stable-stringify-without-jsonify@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.npmmirror.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
+  integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==
+
+jsplumb@^2.15.6:
+  version "2.15.6"
+  resolved "https://registry.npmmirror.com/jsplumb/-/jsplumb-2.15.6.tgz#16d97a195a52cc8e4227d9e29971ff82b83e8faf"
+  integrity sha512-sIpbpz5eMVM+vV+MQzFCidlaa1RsknrQs6LOTKYDjYUDdTAi2AN2bFi94TxB33TifcIsRNV1jebcaxg0tCoPzg==
+
+klona@^2.0.4:
+  version "2.0.5"
+  resolved "https://registry.npmmirror.com/klona/-/klona-2.0.5.tgz#d166574d90076395d9963aa7a928fabb8d76afbc"
+  integrity sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==
+
+levn@^0.4.1:
+  version "0.4.1"
+  resolved "https://registry.npmmirror.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade"
+  integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==
+  dependencies:
+    prelude-ls "^1.2.1"
+    type-check "~0.4.0"
+
+lodash-es@^4.17.21:
+  version "4.17.21"
+  resolved "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee"
+  integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==
+
+lodash-unified@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.npmmirror.com/lodash-unified/-/lodash-unified-1.0.2.tgz#bb2694db3533781e5cce984af60cfaea318b83c1"
+  integrity sha512-OGbEy+1P+UT26CYi4opY4gebD8cWRDxAT6MAObIVQMiqYdxZr1g3QHWCToVsm31x2NkLS4K3+MC2qInaRMa39g==
+
+lodash.camelcase@^4.3.0:
+  version "4.3.0"
+  resolved "https://registry.npmmirror.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6"
+  integrity sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==
+
+lodash.clonedeep@^4.5.0:
+  version "4.5.0"
+  resolved "https://registry.npmmirror.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef"
+  integrity sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==
+
+lodash.debounce@^4.0.8:
+  version "4.0.8"
+  resolved "https://registry.npmmirror.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
+  integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==
+
+lodash.foreach@^4.5.0:
+  version "4.5.0"
+  resolved "https://registry.npmmirror.com/lodash.foreach/-/lodash.foreach-4.5.0.tgz#1a6a35eace401280c7f06dddec35165ab27e3e53"
+  integrity sha512-aEXTF4d+m05rVOAUG3z4vZZ4xVexLKZGF0lIxuHZ1Hplpk/3B6Z1+/ICICYRLm7c41Z2xiejbkCkJoTlypoXhQ==
+
+lodash.isequal@^4.5.0:
+  version "4.5.0"
+  resolved "https://registry.npmmirror.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
+  integrity sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==
+
+lodash.merge@^4.6.2:
+  version "4.6.2"
+  resolved "https://registry.npmmirror.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
+  integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
+
+lodash.throttle@^4.1.1:
+  version "4.1.1"
+  resolved "https://registry.npmmirror.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4"
+  integrity sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==
+
+lodash.toarray@^4.4.0:
+  version "4.4.0"
+  resolved "https://registry.npmmirror.com/lodash.toarray/-/lodash.toarray-4.4.0.tgz#24c4bfcd6b2fba38bfd0594db1179d8e9b656561"
+  integrity sha512-QyffEA3i5dma5q2490+SgCvDN0pXLmRGSyAANuVi0HQ01Pkfr9fuoKQW8wm1wGBnJITs/mS7wQvS6VshUEBFCw==
+
+lodash@^4.17.21:
+  version "4.17.21"
+  resolved "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
+  integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
+
+lru-cache@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.npmmirror.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
+  integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==
+  dependencies:
+    yallist "^4.0.0"
+
+magic-string@^0.25.7:
+  version "0.25.9"
+  resolved "https://registry.npmmirror.com/magic-string/-/magic-string-0.25.9.tgz#de7f9faf91ef8a1c91d02c2e5314c8277dbcdd1c"
+  integrity sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==
+  dependencies:
+    sourcemap-codec "^1.4.8"
+
+memoize-one@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.npmmirror.com/memoize-one/-/memoize-one-6.0.0.tgz#b2591b871ed82948aee4727dc6abceeeac8c1045"
+  integrity sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==
+
+merge2@^1.3.0, merge2@^1.4.1:
+  version "1.4.1"
+  resolved "https://registry.npmmirror.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
+  integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
+
+micromatch@^4.0.4:
+  version "4.0.5"
+  resolved "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6"
+  integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==
+  dependencies:
+    braces "^3.0.2"
+    picomatch "^2.3.1"
+
+mime-db@1.52.0:
+  version "1.52.0"
+  resolved "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
+  integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
+
+mime-match@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.npmmirror.com/mime-match/-/mime-match-1.0.2.tgz#3f87c31e9af1a5fd485fb9db134428b23bbb7ba8"
+  integrity sha512-VXp/ugGDVh3eCLOBCiHZMYWQaTNUHv2IJrut+yXA6+JbLPXHglHwfS/5A5L0ll+jkCY7fIzRJcH6OIunF+c6Cg==
+  dependencies:
+    wildcard "^1.1.0"
+
+mime-types@^2.1.12:
+  version "2.1.35"
+  resolved "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
+  integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
+  dependencies:
+    mime-db "1.52.0"
+
+minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2:
+  version "3.1.2"
+  resolved "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
+  integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
+  dependencies:
+    brace-expansion "^1.1.7"
+
+mitt@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.npmmirror.com/mitt/-/mitt-2.1.0.tgz#f740577c23176c6205b121b2973514eade1b2230"
+  integrity sha512-ILj2TpLiysu2wkBbWjAmww7TkZb65aiQO+DkVdUTBpBXq+MHYiETENkKFMtsJZX1Lf4pe4QOrTSjIfUwN5lRdg==
+
+mitt@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.npmmirror.com/mitt/-/mitt-3.0.0.tgz#69ef9bd5c80ff6f57473e8d89326d01c414be0bd"
+  integrity sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ==
+
+ms@2.1.2:
+  version "2.1.2"
+  resolved "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
+  integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
+
+namespace-emitter@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.npmmirror.com/namespace-emitter/-/namespace-emitter-2.0.1.tgz#978d51361c61313b4e6b8cf6f3853d08dfa2b17c"
+  integrity sha512-N/sMKHniSDJBjfrkbS/tpkPj4RAbvW3mr8UAzvlMHyun93XEm83IAvhWtJVHo+RHn/oO8Job5YN4b+wRjSVp5g==
+
+nanoid@^3.1.25, nanoid@^3.2.0, nanoid@^3.3.4:
+  version "3.3.4"
+  resolved "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab"
+  integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==
+
+natural-compare@^1.4.0:
+  version "1.4.0"
+  resolved "https://registry.npmmirror.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
+  integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
+
+neo-async@^2.6.2:
+  version "2.6.2"
+  resolved "https://registry.npmmirror.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f"
+  integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==
+
+next-tick@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.npmmirror.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb"
+  integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==
+
+normalize-path@^3.0.0, normalize-path@~3.0.0:
+  version "3.0.0"
+  resolved "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
+  integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
+
+normalize-wheel-es@^1.1.2:
+  version "1.1.2"
+  resolved "https://registry.npmmirror.com/normalize-wheel-es/-/normalize-wheel-es-1.1.2.tgz#285e43676a62d687bf145e33452ea6be435162d0"
+  integrity sha512-scX83plWJXYH1J4+BhAuIHadROzxX0UBF3+HuZNY2Ks8BciE7tSTQ+5JhTsvzjaO0/EJdm4JBGrfObKxFf3Png==
+
+nprogress@^0.2.0:
+  version "0.2.0"
+  resolved "https://registry.npmmirror.com/nprogress/-/nprogress-0.2.0.tgz#cb8f34c53213d895723fcbab907e9422adbcafb1"
+  integrity sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==
+
+nth-check@^2.0.1:
+  version "2.1.1"
+  resolved "https://registry.npmmirror.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d"
+  integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==
+  dependencies:
+    boolbase "^1.0.0"
+
+once@^1.3.0:
+  version "1.4.0"
+  resolved "https://registry.npmmirror.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
+  integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
+  dependencies:
+    wrappy "1"
+
+optionator@^0.9.1:
+  version "0.9.1"
+  resolved "https://registry.npmmirror.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499"
+  integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==
+  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"
+
+parent-module@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.npmmirror.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2"
+  integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==
+  dependencies:
+    callsites "^3.0.0"
+
+path-is-absolute@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.npmmirror.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
+  integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==
+
+path-key@^3.1.0:
+  version "3.1.1"
+  resolved "https://registry.npmmirror.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
+  integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
+
+path-parse@^1.0.7:
+  version "1.0.7"
+  resolved "https://registry.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
+  integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
+
+path-type@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.npmmirror.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
+  integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
+
+picocolors@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.npmmirror.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
+  integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
+
+picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1:
+  version "2.3.1"
+  resolved "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
+  integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
+
+pinia@^2.0.14:
+  version "2.0.14"
+  resolved "https://registry.npmmirror.com/pinia/-/pinia-2.0.14.tgz#0837898c20291ebac982bbfca95c8d3c6099925f"
+  integrity sha512-0nPuZR4TetT/WcLN+feMSjWJku3SQU7dBbXC6uw+R6FLQJCsg+/0pzXyD82T1FmAYe0lsx+jnEDQ1BLgkRKlxA==
+  dependencies:
+    "@vue/devtools-api" "^6.1.4"
+    vue-demi "*"
+
+postcss-selector-parser@^6.0.9:
+  version "6.0.10"
+  resolved "https://registry.npmmirror.com/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz#79b61e2c0d1bfc2602d549e11d0876256f8df88d"
+  integrity sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==
+  dependencies:
+    cssesc "^3.0.0"
+    util-deprecate "^1.0.2"
+
+postcss@^8.1.10, postcss@^8.4.13:
+  version "8.4.14"
+  resolved "https://registry.npmmirror.com/postcss/-/postcss-8.4.14.tgz#ee9274d5622b4858c1007a74d76e42e56fd21caf"
+  integrity sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==
+  dependencies:
+    nanoid "^3.3.4"
+    picocolors "^1.0.0"
+    source-map-js "^1.0.2"
+
+preact@^10.5.13:
+  version "10.7.3"
+  resolved "https://registry.npmmirror.com/preact/-/preact-10.7.3.tgz#f98c09a29cb8dbb22e5fc824a1edcc377fc42b5a"
+  integrity sha512-giqJXP8VbtA1tyGa3f1n9wiN7PrHtONrDyE3T+ifjr/tTkg+2N4d/6sjC9WyJKv8wM7rOYDveqy5ZoFmYlwo4w==
+
+prelude-ls@^1.2.1:
+  version "1.2.1"
+  resolved "https://registry.npmmirror.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
+  integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
+
+prettier@^2.6.2:
+  version "2.6.2"
+  resolved "https://registry.npmmirror.com/prettier/-/prettier-2.6.2.tgz#e26d71a18a74c3d0f0597f55f01fb6c06c206032"
+  integrity sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew==
+
+print-js@^1.6.0:
+  version "1.6.0"
+  resolved "https://registry.npmmirror.com/print-js/-/print-js-1.6.0.tgz#692b046cf31992b46afa6c6d8a9db1c69d431d1f"
+  integrity sha512-BfnOIzSKbqGRtO4o0rnj/K3681BSd2QUrsIZy/+WdCIugjIswjmx3lDEZpXB2ruGf9d4b3YNINri81+J0FsBWg==
+
+prismjs@^1.23.0:
+  version "1.28.0"
+  resolved "https://registry.npmmirror.com/prismjs/-/prismjs-1.28.0.tgz#0d8f561fa0f7cf6ebca901747828b149147044b6"
+  integrity sha512-8aaXdYvl1F7iC7Xm1spqSaY/OJBpYW3v+KJ+F17iYxvdc8sfjW194COK5wVhMZX45tGteiBQgdvD/nhxcRwylw==
+
+punycode@^2.1.0:
+  version "2.1.1"
+  resolved "https://registry.npmmirror.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
+  integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
+
+qrcodejs2-fixes@^0.0.2:
+  version "0.0.2"
+  resolved "https://registry.npmmirror.com/qrcodejs2-fixes/-/qrcodejs2-fixes-0.0.2.tgz#a2f5d7816100073d1c09088ac75a34404d025b30"
+  integrity sha512-wMUXYMOixAEJlLnjk5MbLiFaz0gQObWYm/TIFWB5+j7sTY5gPyr09Cx1EpcLYbsgfFdN3wHjrKAhZofTuCBGhg==
+
+queue-microtask@^1.2.2:
+  version "1.2.3"
+  resolved "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
+  integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
+
+readdirp@~3.6.0:
+  version "3.6.0"
+  resolved "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
+  integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==
+  dependencies:
+    picomatch "^2.2.1"
+
+regenerator-runtime@^0.13.4:
+  version "0.13.9"
+  resolved "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52"
+  integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==
+
+regexpp@^3.2.0:
+  version "3.2.0"
+  resolved "https://registry.npmmirror.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2"
+  integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==
+
+resolve-from@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.npmmirror.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
+  integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
+
+resolve@^1.22.0:
+  version "1.22.0"
+  resolved "https://registry.npmmirror.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198"
+  integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==
+  dependencies:
+    is-core-module "^2.8.1"
+    path-parse "^1.0.7"
+    supports-preserve-symlinks-flag "^1.0.0"
+
+reusify@^1.0.4:
+  version "1.0.4"
+  resolved "https://registry.npmmirror.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
+  integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
+
+rimraf@^3.0.2:
+  version "3.0.2"
+  resolved "https://registry.npmmirror.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
+  integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
+  dependencies:
+    glob "^7.1.3"
+
+rollup@^2.59.0:
+  version "2.75.6"
+  resolved "https://registry.npmmirror.com/rollup/-/rollup-2.75.6.tgz#ac4dc8600f95942a0180f61c7c9d6200e374b439"
+  integrity sha512-OEf0TgpC9vU6WGROJIk1JA3LR5vk/yvqlzxqdrE2CzzXnqKXNzbAwlWUXis8RS3ZPe7LAq+YUxsRa0l3r27MLA==
+  optionalDependencies:
+    fsevents "~2.3.2"
+
+run-parallel@^1.1.9:
+  version "1.2.0"
+  resolved "https://registry.npmmirror.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee"
+  integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==
+  dependencies:
+    queue-microtask "^1.2.2"
+
+sass-loader@^13.0.0:
+  version "13.0.0"
+  resolved "https://registry.npmmirror.com/sass-loader/-/sass-loader-13.0.0.tgz#0b4bff0289951ed21240bca54453eca3dbda1713"
+  integrity sha512-IHCFecI+rbPvXE2zO/mqdVFe8MU7ElGrwga9hh2H65Ru4iaBJAMRteum1c4Gsxi9Cq1FOtTEDd6+/AEYuQDM4Q==
+  dependencies:
+    klona "^2.0.4"
+    neo-async "^2.6.2"
+
+sass@^1.52.2:
+  version "1.52.3"
+  resolved "https://registry.npmmirror.com/sass/-/sass-1.52.3.tgz#b7cc7ffea2341ccc9a0c4fd372bf1b3f9be1b6cb"
+  integrity sha512-LNNPJ9lafx+j1ArtA7GyEJm9eawXN8KlA1+5dF6IZyoONg1Tyo/g+muOsENWJH/2Q1FHbbV4UwliU0cXMa/VIA==
+  dependencies:
+    chokidar ">=3.0.0 <4.0.0"
+    immutable "^4.0.0"
+    source-map-js ">=0.6.2 <2.0.0"
+
+screenfull@^6.0.1:
+  version "6.0.1"
+  resolved "https://registry.npmmirror.com/screenfull/-/screenfull-6.0.1.tgz#3b71e6f06b72d817a8d3be73c45ebe71fa8da1ce"
+  integrity sha512-yzQW+j4zMUBQC51xxWaoDYjxOtl8Kn+xvue3p6v/fv2pIi1jH4AldgVLU8TBfFVgH2x3VXlf3+YiA/AYIPlaew==
+
+scroll-into-view-if-needed@^2.2.28:
+  version "2.2.29"
+  resolved "https://registry.npmmirror.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.29.tgz#551791a84b7e2287706511f8c68161e4990ab885"
+  integrity sha512-hxpAR6AN+Gh53AdAimHM6C8oTN1ppwVZITihix+WqalywBeFcQ6LdQP5ABNl26nX8GTEL7VT+b8lKpdqq65wXg==
+  dependencies:
+    compute-scroll-into-view "^1.0.17"
+
+select@^1.1.2:
+  version "1.1.2"
+  resolved "https://registry.npmmirror.com/select/-/select-1.1.2.tgz#0e7350acdec80b1108528786ec1d4418d11b396d"
+  integrity sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA==
+
+semver@^7.3.5, semver@^7.3.6, semver@^7.3.7:
+  version "7.3.7"
+  resolved "https://registry.npmmirror.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f"
+  integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==
+  dependencies:
+    lru-cache "^6.0.0"
+
+shebang-command@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
+  integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==
+  dependencies:
+    shebang-regex "^3.0.0"
+
+shebang-regex@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
+  integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
+
+slash@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.npmmirror.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
+  integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
+
+slate-history@^0.66.0:
+  version "0.66.0"
+  resolved "https://registry.npmmirror.com/slate-history/-/slate-history-0.66.0.tgz#ac63fddb903098ceb4c944433e3f75fe63acf940"
+  integrity sha512-6MWpxGQZiMvSINlCbMW43E2YBSVMCMCIwQfBzGssjWw4kb0qfvj0pIdblWNRQZD0hR6WHP+dHHgGSeVdMWzfng==
+  dependencies:
+    is-plain-object "^5.0.0"
+
+slate@^0.72.0:
+  version "0.72.8"
+  resolved "https://registry.npmmirror.com/slate/-/slate-0.72.8.tgz#5a018edf24e45448655293a68bfbcf563aa5ba81"
+  integrity sha512-/nJwTswQgnRurpK+bGJFH1oM7naD5qDmHd89JyiKNT2oOKD8marW0QSBtuFnwEbL5aGCS8AmrhXQgNOsn4osAw==
+  dependencies:
+    immer "^9.0.6"
+    is-plain-object "^5.0.0"
+    tiny-warning "^1.0.3"
+
+snabbdom@^3.1.0:
+  version "3.5.0"
+  resolved "https://registry.npmmirror.com/snabbdom/-/snabbdom-3.5.0.tgz#e75acbdedb26ea327c75028a433ba064db0ccf6e"
+  integrity sha512-Ff5BKG18KrrPuskHJlA9aujPHqEabItaDl96l7ZZndF4zt5AYSczz7ZjjgQAX5IBd5cd25lw9NfgX21yVUJ+9g==
+
+sortablejs@^1.15.0:
+  version "1.15.0"
+  resolved "https://registry.npmmirror.com/sortablejs/-/sortablejs-1.15.0.tgz#53230b8aa3502bb77a29e2005808ffdb4a5f7e2a"
+  integrity sha512-bv9qgVMjUMf89wAvM6AxVvS/4MX3sPeN0+agqShejLU5z5GX4C75ow1O2e5k4L6XItUyAK3gH6AxSbXrOM5e8w==
+
+"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
+  integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
+
+source-map@0.6.1, source-map@^0.6.1:
+  version "0.6.1"
+  resolved "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
+  integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
+
+sourcemap-codec@^1.4.8:
+  version "1.4.8"
+  resolved "https://registry.npmmirror.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4"
+  integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==
+
+splitpanes@^3.1.1:
+  version "3.1.1"
+  resolved "https://registry.npmmirror.com/splitpanes/-/splitpanes-3.1.1.tgz#be806205681a87c34075f1a06192df684e370e32"
+  integrity sha512-VUkxDJfIGSvTM/fm/+OSrx8ha9URwE/9B8FPvfzoBuAxVELIHBWpsfnJXIXv77zVwuex//QQL4kTU9SDBPeHjA==
+
+ssr-window@^3.0.0-alpha.1:
+  version "3.0.0"
+  resolved "https://registry.npmmirror.com/ssr-window/-/ssr-window-3.0.0.tgz#fd5b82801638943e0cc704c4691801435af7ac37"
+  integrity sha512-q+8UfWDg9Itrg0yWK7oe5p/XRCJpJF9OBtXfOPgSJl+u3Xd5KI328RUEvUqSMVM9CiQUEf1QdBzJMkYGErj9QA==
+
+strip-ansi@^6.0.1:
+  version "6.0.1"
+  resolved "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
+  integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
+  dependencies:
+    ansi-regex "^5.0.1"
+
+strip-json-comments@^3.1.0, strip-json-comments@^3.1.1:
+  version "3.1.1"
+  resolved "https://registry.npmmirror.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
+  integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
+
+supports-color@^7.1.0:
+  version "7.2.0"
+  resolved "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
+  integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
+  dependencies:
+    has-flag "^4.0.0"
+
+supports-preserve-symlinks-flag@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.npmmirror.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
+  integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
+
+text-table@^0.2.0:
+  version "0.2.0"
+  resolved "https://registry.npmmirror.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
+  integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==
+
+tiny-emitter@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.npmmirror.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423"
+  integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==
+
+tiny-warning@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.npmmirror.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754"
+  integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==
+
+to-regex-range@^5.0.1:
+  version "5.0.1"
+  resolved "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
+  integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
+  dependencies:
+    is-number "^7.0.0"
+
+tslib@2.3.0:
+  version "2.3.0"
+  resolved "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e"
+  integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==
+
+tslib@^1.8.1:
+  version "1.14.1"
+  resolved "https://registry.npmmirror.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
+  integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
+
+tsutils@^3.21.0:
+  version "3.21.0"
+  resolved "https://registry.npmmirror.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623"
+  integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==
+  dependencies:
+    tslib "^1.8.1"
+
+type-check@^0.4.0, type-check@~0.4.0:
+  version "0.4.0"
+  resolved "https://registry.npmmirror.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1"
+  integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==
+  dependencies:
+    prelude-ls "^1.2.1"
+
+type-fest@^0.20.2:
+  version "0.20.2"
+  resolved "https://registry.npmmirror.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
+  integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
+
+type@^1.0.1:
+  version "1.2.0"
+  resolved "https://registry.npmmirror.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0"
+  integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==
+
+type@^2.5.0:
+  version "2.6.0"
+  resolved "https://registry.npmmirror.com/type/-/type-2.6.0.tgz#3ca6099af5981d36ca86b78442973694278a219f"
+  integrity sha512-eiDBDOmkih5pMbo9OqsqPRGMljLodLcwd5XD5JbtNB0o89xZAwynY9EdCDsJU7LtcVCClu9DvM7/0Ep1hYX3EQ==
+
+typescript@^4.7.3:
+  version "4.7.3"
+  resolved "https://registry.npmmirror.com/typescript/-/typescript-4.7.3.tgz#8364b502d5257b540f9de4c40be84c98e23a129d"
+  integrity sha512-WOkT3XYvrpXx4vMMqlD+8R8R37fZkjyLGlxavMc4iB8lrl8L0DeTcHbYgw/v0N/z9wAFsgBhcsF0ruoySS22mA==
+
+uri-js@^4.2.2:
+  version "4.4.1"
+  resolved "https://registry.npmmirror.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"
+  integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==
+  dependencies:
+    punycode "^2.1.0"
+
+util-deprecate@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
+  integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
+
+v8-compile-cache@^2.0.3:
+  version "2.3.0"
+  resolved "https://registry.npmmirror.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee"
+  integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==
+
+vite@^2.9.9:
+  version "2.9.10"
+  resolved "https://registry.npmmirror.com/vite/-/vite-2.9.10.tgz#f574d96655622c2e0fbc662edd0ed199c60fe91a"
+  integrity sha512-TwZRuSMYjpTurLqXspct+HZE7ONiW9d+wSWgvADGxhDPPyoIcNywY+RX4ng+QpK30DCa1l/oZgi2PLZDibhzbQ==
+  dependencies:
+    esbuild "^0.14.27"
+    postcss "^8.4.13"
+    resolve "^1.22.0"
+    rollup "^2.59.0"
+  optionalDependencies:
+    fsevents "~2.3.2"
+
+vue-clipboard3@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.npmmirror.com/vue-clipboard3/-/vue-clipboard3-2.0.0.tgz#79b026c765c0f6a5cde18a477c2dbfc7d3b9f178"
+  integrity sha512-Q9S7dzWGax7LN5iiSPcu/K1GGm2gcBBlYwmMsUc5/16N6w90cbKow3FnPmPs95sungns4yvd9/+JhbAznECS2A==
+  dependencies:
+    clipboard "^2.0.6"
+
+vue-demi@*:
+  version "0.13.1"
+  resolved "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.13.1.tgz#7604904c88be338418a10abbc94d5b8caa14cb8c"
+  integrity sha512-xmkJ56koG3ptpLnpgmIzk9/4nFf4CqduSJbUM0OdPoU87NwRuZ6x49OLhjSa/fC15fV+5CbEnrxU4oyE022svg==
+
+vue-eslint-parser@^9.0.1, vue-eslint-parser@^9.0.2:
+  version "9.0.2"
+  resolved "https://registry.npmmirror.com/vue-eslint-parser/-/vue-eslint-parser-9.0.2.tgz#d2535516f3f55adb387939427fe741065eb7948a"
+  integrity sha512-uCPQwTGjOtAYrwnU+76pYxalhjsh7iFBsHwBqDHiOPTxtICDaraO4Szw54WFTNZTAEsgHHzqFOu1mmnBOBRzDA==
+  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"
+
+vue-grid-layout@^3.0.0-beta1:
+  version "3.0.0-beta1"
+  resolved "https://registry.npmmirror.com/vue-grid-layout/-/vue-grid-layout-3.0.0-beta1.tgz#f8ce8eb7dbb1ff58f64820332afcc12c8cf873aa"
+  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"
+
+vue-i18n@^9.1.10:
+  version "9.1.10"
+  resolved "https://registry.npmmirror.com/vue-i18n/-/vue-i18n-9.1.10.tgz#7ad516b89ba28debb90fc4181c9a2faec9ad97f9"
+  integrity sha512-jpr7gV5KPk4n+sSPdpZT8Qx3XzTcNDWffRlHV/cT2NUyEf+sEgTTmLvnBAibjOFJ0zsUyZlVTAWH5DDnYep+1g==
+  dependencies:
+    "@intlify/core-base" "9.1.10"
+    "@intlify/shared" "9.1.10"
+    "@intlify/vue-devtools" "9.1.10"
+    "@vue/devtools-api" "^6.0.0-beta.7"
+
+vue-router@^4.0.15:
+  version "4.0.15"
+  resolved "https://registry.npmmirror.com/vue-router/-/vue-router-4.0.15.tgz#b4a0661efe197f8c724e0f233308f8776e2c3667"
+  integrity sha512-xa+pIN9ZqORdIW1MkN2+d9Ui2pCM1b/UMgwYUCZOiFYHAvz/slKKBDha8DLrh5aCG/RibtrpyhKjKOZ85tYyWg==
+  dependencies:
+    "@vue/devtools-api" "^6.0.0"
+
+vue@^3.2.36:
+  version "3.2.37"
+  resolved "https://registry.npmmirror.com/vue/-/vue-3.2.37.tgz#da220ccb618d78579d25b06c7c21498ca4e5452e"
+  integrity sha512-bOKEZxrm8Eh+fveCqS1/NkG/n6aMidsI6hahas7pa0w/l7jkbssJVsRhVDs07IdDq7h9KHswZOgItnwJAgtVtQ==
+  dependencies:
+    "@vue/compiler-dom" "3.2.37"
+    "@vue/compiler-sfc" "3.2.37"
+    "@vue/runtime-dom" "3.2.37"
+    "@vue/server-renderer" "3.2.37"
+    "@vue/shared" "3.2.37"
+
+which@^2.0.1:
+  version "2.0.2"
+  resolved "https://registry.npmmirror.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
+  integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
+  dependencies:
+    isexe "^2.0.0"
+
+wildcard@^1.1.0:
+  version "1.1.2"
+  resolved "https://registry.npmmirror.com/wildcard/-/wildcard-1.1.2.tgz#a7020453084d8cd2efe70ba9d3696263de1710a5"
+  integrity sha512-DXukZJxpHA8LuotRwL0pP1+rS6CS7FF2qStDDE1C7DDg2rLud2PXRMuEDYIPhgEezwnlHNL4c+N6MfMTjCGTng==
+
+word-wrap@^1.2.3:
+  version "1.2.3"
+  resolved "https://registry.npmmirror.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
+  integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==
+
+wrappy@1:
+  version "1.0.2"
+  resolved "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
+  integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
+
+xml-name-validator@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.npmmirror.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835"
+  integrity sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==
+
+yallist@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
+  integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
+
+zrender@5.3.1, zrender@^5.1.1:
+  version "5.3.1"
+  resolved "https://registry.npmmirror.com/zrender/-/zrender-5.3.1.tgz#fa8e63ac7e719cfd563831fe8c42a9756c5af384"
+  integrity sha512-7olqIjy0gWfznKr6vgfnGBk7y4UtdMvdwFmK92vVQsQeDPyzkHW1OlrLEKg6GHz1W5ePf0FeN1q2vkl/HFqhXw==
+  dependencies:
+    tslib "2.3.0"

--
Gitblit v1.9.2