From 251d2411f235e23209d57173857e05b637729ce8 Mon Sep 17 00:00:00 2001
From: LiuHao <liuhaoai545@gmail.com>
Date: 星期日, 02 四月 2023 01:01:56 +0800
Subject: [PATCH] refactor ts

---
 src/assets/styles/index.scss                  |  190 
 src/directive/index.ts                        |    9 
 src/api/demo/demo.ts                          |   55 
 src/api/system/dept/index.ts                  |   62 
 src/layout/index.vue                          |   51 
 src/views/system/tenant/index.vue             |  576 
 src/views/system/user/authRole.vue            |  179 
 src/views/monitor/operlog/index.vue           |  492 
 src/views/monitor/cache/index.vue             |  301 
 src/views/error/404.vue                       |   46 
 src/api/system/dept/types.ts                  |   45 
 src/api/monitor/online/types.ts               |   15 
 src/components/SizeSelect/index.vue           |   49 
 src/views/system/oss/index.vue                |  597 
 src/components/Breadcrumb/index.vue           |   56 
 .env.development                              |    1 
 src/directive/permission/index.ts             |   44 
 src/utils/validate.ts                         |   92 
 src/components/TopNav/index.vue               |  101 
 src/assets/styles/mixin.scss                  |   92 
 src/views/system/role/index.vue               |  826 -
 src/types/element.d.ts                        |    1 
 src/views/system/oss/config.vue               |  492 
 Vite/plugins/index.ts                         |   18 
 src/api/system/dict/type/types.ts             |   21 
 src/layout/components/index.ts                |    4 
 src/assets/styles/variables.module.scss       |   52 
 src/plugins/svgicon.ts                        |   10 
 vite.config.ts                                |  112 
 src/views/error/401.vue                       |   53 
 src/enums/layout/LayoutEnum.ts                |    4 
 src/views/system/dict/data.vue                |  564 
 src/views/tool/gen/editTable.vue              |  327 
 src/api/system/dict/type/index.ts             |   62 
 src/api/system/post/types.ts                  |   23 
 src/views/tool/gen/importTable.vue            |  146 
 src/api/system/post/index.ts                  |   46 
 Vite/plugins/icons.ts                         |    8 
 src/views/system/dept/index.vue               |  494 
 src/store/modules/user.ts                     |   83 
 src/utils/i18n.ts                             |   12 
 src/main.ts                                   |   68 
 src/utils/index.ts                            |  318 
 src/plugins/index.ts                          |   24 
 src/utils/theme.ts                            |   52 
 src/views/system/user/profile/userInfo.vue    |   77 
 src/components/iFrame/index.vue               |   18 
 src/api/tool/gen/types.ts                     |  178 
 src/api/tool/gen/index.ts                     |   87 
 src/assets/styles/btn.scss                    |  124 
 src/plugins/modal.ts                          |   81 
 src/views/register.vue                        |  234 
 .prettierignore                               |    9 
 .eslintrc.js                                  |   41 
 src/layout/components/Sidebar/Logo.vue        |   44 
 index.html                                    |  380 
 src/utils/scroll-to.ts                        |   65 
 src/types/router.d.ts                         |   35 
 Vite/plugins/svg-icon.ts                      |    9 
 src/plugins/download.ts                       |   60 
 src/utils/permission.ts                       |   51 
 src/views/tool/gen/basicInfoForm.vue          |   76 
 src/components/Pagination/index.vue           |   59 
 src/lang/index.ts                             |   45 
 src/utils/jsencrypt.ts                        |   29 
 tsconfig.json                                 |   26 
 src/utils/auth.ts                             |    9 
 src/utils/dict.ts                             |   27 
 src/plugins/cache.ts                          |   77 
 src/api/menu.ts                               |   11 
 Vite/plugins/unocss.ts                        |   13 
 src/components/FileUpload/index.vue           |  155 
 src/views/system/dict/index.vue               |  455 
 src/api/system/dict/data/index.ts             |   53 
 src/types/global.d.ts                         |   83 
 src/api/system/dict/data/types.ts             |   29 
 src/views/redirect/index.vue                  |    4 
 src/views/system/role/selectUser.vue          |  203 
 src/components/HeaderSearch/index.vue         |  122 
 src/animate.ts                                |   48 
 src/api/system/tenantPackage/index.ts         |   59 
 src/components/SvgIcon/index.vue              |   56 
 src/components/RightToolbar/index.vue         |   79 
 src/views/monitor/online/index.vue            |  174 
 src/types/module.d.ts                         |   28 
 src/components/IconSelect/index.vue           |  155 
 src/views/system/user/index.vue               | 1066 +-
 src/views/login.vue                           |  265 
 src/views/tool/gen/genInfoForm.vue            |  501 
 src/layout/components/IframeToggle/index.vue  |   30 
 src/api/system/tenantPackage/types.ts         |   22 
 package.json                                  |  119 
 src/components/RuoYiGit/index.vue             |    6 
 src/App.vue                                   |    4 
 src/assets/images/login-background.jpg        |    0 
 src/api/system/menu/index.ts                  |   70 
 src/api/demo/types.ts                         |   55 
 src/store/modules/dict.ts                     |   78 
 src/views/monitor/logininfor/index.vue        |  328 
 src/assets/styles/ruoyi.scss                  |   91 
 Vite/plugins/components.ts                    |   17 
 html/ie.html                                  |  102 
 src/store/modules/permission.ts               |  144 
 src/api/system/menu/types.ts                  |   69 
 src/utils/errorCode.ts                        |    7 
 src/views/system/tenantPackage/index.vue      |  453 
 src/settings.ts                               |   55 
 src/components/ImageUpload/index.vue          |  164 
 src/plugins/auth.ts                           |   60 
 src/enums/RespEnum.ts                         |   90 
 src/layout/components/Sidebar/Link.vue        |   16 
 src/api/system/ossConfig/types.ts             |   38 
 src/components/Hamburger/index.vue            |   29 
 src/views/tool/gen/index.vue                  |  460 
 src/components/ParentView/index.vue           |    4 
 Vite/plugins/auto-import.ts                   |   24 
 src/components/ImagePreview/index.vue         |   31 
 src/api/monitor/loginInfo/types.ts            |   20 
 src/api/system/notice/types.ts                |   26 
 src/components/Editor/index.vue               |  104 
 src/api/system/ossConfig/index.ts             |   60 
 src/api/system/notice/index.ts                |   45 
 src/views/system/menu/index.vue               |  824 -
 src/layout/components/Sidebar/index.vue       |   59 
 src/components/RuoYiDoc/index.vue             |    8 
 src/lang/en.ts                                |   25 
 src/assets/styles/sidebar.scss                |  386 
 src/assets/images/profile.jpg                 |    0 
 .eslintrc-auto-import.json                    |  281 
 src/store/modules/tagsView.ts                 |  198 
 src/api/login.ts                              |   81 
 src/assets/styles/element-ui.scss             |  101 
 src/layout/components/TagsView/index.vue      |  236 
 src/types/axios.d.ts                          |   10 
 src/api/types.ts                              |   54 
 src/permission.ts                             |   61 
 src/api/monitor/loginInfo/index.ts            |   36 
 src/components/TreeSelect/index.vue           |   88 
 src/views/system/user/profile/resetPwd.vue    |   71 
 src/layout/components/AppMain.vue             |   62 
 src/utils/ruoyi.ts                            |  247 
 .eslintignore                                 |   17 
 src/views/index.vue                           |  227 
 src/store/modules/app.ts                      |   73 
 src/views/system/user/profile/userAvatar.vue  |  204 
 src/components/Screenfull/index.vue           |   16 
 src/utils/dynamicTitle.ts                     |   14 
 src/enums/MenuTypeEnum.ts                     |   15 
 src/directive/common/copyText.ts              |   66 
 src/lang/zh-cn.ts                             |   24 
 src/layout/components/InnerLink/index.vue     |   20 
 src/views/monitor/xxljob/index.vue            |   12 
 src/views/system/role/authUser.vue            |  246 
 src/enums/SettingTypeEnum.ts                  |   16 
 src/plugins/tab.ts                            |   65 
 public/favicon.ico                            |    0 
 src/router/index.ts                           |  179 
 src/types/setting.d.ts                        |   70 
 src/views/tool/build/index.vue                |    4 
 src/components/IconSelect/requireIcons.ts     |    7 
 src/views/system/post/index.vue               |  445 
 src/utils/request.ts                          |  159 
 src/views/system/user/profile/index.vue       |  160 
 src/assets/styles/transition.scss             |   26 
 src/types/env.d.ts                            |   74 
 README.md                                     |   72 
 src/api/demo/tree.ts                          |   46 
 src/api/system/config/index.ts                |   74 
 src/layout/components/TagsView/ScrollPane.vue |   43 
 src/api/monitor/operlog/index.ts              |   28 
 src/api/monitor/operlog/types.ts              |   52 
 .env.production                               |    4 
 src/views/system/notice/index.vue             |  461 
 src/api/system/oss/index.ts                   |   28 
 src/layout/components/Settings/index.vue      |  211 
 src/api/system/oss/types.ts                   |   22 
 src/views/system/config/index.vue             |  447 
 src/types/components.d.ts                     |   82 
 src/api/system/config/types.ts                |   23 
 src/layout/components/Navbar.vue              |  228 
 src/types/auto-imports.d.ts                   |  574 +
 src/api/monitor/online/index.ts               |   20 
 src/views/monitor/admin/index.vue             |   12 
 src/store/modules/settings.ts                 |   54 
 src/layout/components/Sidebar/SidebarItem.vue |   81 
 src/api/system/user/types.ts                  |   84 
 src/components/DictTag/index.vue              |   65 
 src/api/monitor/cache/types.ts                |    7 
 src/views/demo/tree/index.vue                 |  410 
 commitlint.config.js                          |   22 
 src/assets/logo/logo.png                      |    0 
 src/api/system/user/index.ts                  |  180 
 src/store/index.ts                            |    3 
 /dev/null                                     |   10 
 src/api/system/tenant/types.ts                |   46 
 src/views/demo/demo/index.vue                 |  648 
 src/api/monitor/cache/index.ts                |   59 
 src/api/system/role/index.ts                  |  144 
 src/api/system/role/types.ts                  |   52 
 .prettierrc.cjs                               |   46 
 src/api/system/tenant/index.ts                |   89 
 201 files changed, 15,432 insertions(+), 9,056 deletions(-)

diff --git a/.env.development b/.env.development
index 85af030..60496f4 100644
--- a/.env.development
+++ b/.env.development
@@ -15,3 +15,4 @@
 
 # xxl-job 鎺у埗鍙板湴鍧�
 VITE_APP_XXL_JOB_ADMIN = 'http://localhost:9100/xxl-job-admin'
+VITE_APP_PORT = 3000
diff --git a/.env.production b/.env.production
index 7b3b042..acc3b1e 100644
--- a/.env.production
+++ b/.env.production
@@ -17,4 +17,6 @@
 VITE_APP_BASE_API = '/prod-api'
 
 # 鏄惁鍦ㄦ墦鍖呮椂寮�鍚帇缂╋紝鏀寔 gzip 鍜� brotli
-VITE_BUILD_COMPRESS = gzip
\ No newline at end of file
+VITE_BUILD_COMPRESS = gzip
+
+VITE_APP_PORT = 3000
diff --git a/.eslintignore b/.eslintignore
new file mode 100644
index 0000000..c4d771a
--- /dev/null
+++ b/.eslintignore
@@ -0,0 +1,17 @@
+*.sh
+node_modules
+*.md
+*.woff
+*.ttf
+.vscode
+.idea
+dist
+/public
+/docs
+.husky
+.local
+/bin
+.eslintrc.js
+prettier.config.js
+src/assets
+tailwind.config.js
\ No newline at end of file
diff --git a/.eslintrc-auto-import.json b/.eslintrc-auto-import.json
new file mode 100644
index 0000000..d8190fd
--- /dev/null
+++ b/.eslintrc-auto-import.json
@@ -0,0 +1,281 @@
+{
+	"globals": {
+		"useRouter": true,
+		"useRoute": true,
+		"EffectScope": true,
+		"ElTable": true,
+		"ElSelect": true,
+		"ElUpload": true,
+		"ElForm": true,
+		"ElTree": true,
+		"ElMessage": true,
+		"ElMessageBox": true,
+		"asyncComputed": true,
+		"autoResetRef": true,
+		"computed": true,
+		"computedAsync": true,
+		"computedEager": true,
+		"computedInject": true,
+		"computedWithControl": true,
+		"controlledComputed": true,
+		"controlledRef": true,
+		"createApp": true,
+		"createEventHook": true,
+		"createGlobalState": true,
+		"createInjectionState": true,
+		"createReactiveFn": true,
+		"createSharedComposable": true,
+		"createUnrefFn": true,
+		"customRef": true,
+		"debouncedRef": true,
+		"debouncedWatch": true,
+		"defineAsyncComponent": true,
+		"defineComponent": true,
+		"eagerComputed": true,
+		"effectScope": true,
+		"extendRef": true,
+		"getCurrentInstance": true,
+		"getCurrentScope": true,
+		"h": true,
+		"ignorableWatch": true,
+		"inject": true,
+		"isDefined": true,
+		"isProxy": true,
+		"isReactive": true,
+		"isReadonly": true,
+		"isRef": true,
+		"makeDestructurable": true,
+		"markRaw": true,
+		"nextTick": true,
+		"onActivated": true,
+		"onBeforeMount": true,
+		"onBeforeUnmount": true,
+		"onBeforeUpdate": true,
+		"onClickOutside": true,
+		"onDeactivated": true,
+		"onErrorCaptured": true,
+		"onKeyStroke": true,
+		"onLongPress": true,
+		"onMounted": true,
+		"onRenderTracked": true,
+		"onRenderTriggered": true,
+		"onScopeDispose": true,
+		"onServerPrefetch": true,
+		"onStartTyping": true,
+		"onUnmounted": true,
+		"onUpdated": true,
+		"pausableWatch": true,
+		"provide": true,
+		"reactify": true,
+		"reactifyObject": true,
+		"reactive": true,
+		"reactiveComputed": true,
+		"reactiveOmit": true,
+		"reactivePick": true,
+		"readonly": true,
+		"ref": true,
+		"refAutoReset": true,
+		"refDebounced": true,
+		"refDefault": true,
+		"refThrottled": true,
+		"refWithControl": true,
+		"resolveComponent": true,
+		"resolveDirective": true,
+		"resolveRef": true,
+		"resolveUnref": true,
+		"shallowReactive": true,
+		"shallowReadonly": true,
+		"shallowRef": true,
+		"syncRef": true,
+		"syncRefs": true,
+		"templateRef": true,
+		"throttledRef": true,
+		"throttledWatch": true,
+		"toRaw": true,
+		"toReactive": true,
+		"toRef": true,
+		"toRefs": true,
+		"triggerRef": true,
+		"tryOnBeforeMount": true,
+		"tryOnBeforeUnmount": true,
+		"tryOnMounted": true,
+		"tryOnScopeDispose": true,
+		"tryOnUnmounted": true,
+		"unref": true,
+		"unrefElement": true,
+		"until": true,
+		"useActiveElement": true,
+		"useArrayEvery": true,
+		"useArrayFilter": true,
+		"useArrayFind": true,
+		"useArrayFindIndex": true,
+		"useArrayFindLast": true,
+		"useArrayJoin": true,
+		"useArrayMap": true,
+		"useArrayReduce": true,
+		"useArraySome": true,
+		"useArrayUnique": true,
+		"useAsyncQueue": true,
+		"useAsyncState": true,
+		"useAttrs": true,
+		"useBase64": true,
+		"useBattery": true,
+		"useBluetooth": true,
+		"useBreakpoints": true,
+		"useBroadcastChannel": true,
+		"useBrowserLocation": true,
+		"useCached": true,
+		"useClipboard": true,
+		"useCloned": true,
+		"useColorMode": true,
+		"useConfirmDialog": true,
+		"useCounter": true,
+		"useCssModule": true,
+		"useCssVar": true,
+		"useCssVars": true,
+		"useCurrentElement": true,
+		"useCycleList": true,
+		"useDark": true,
+		"useDateFormat": true,
+		"useDebounce": true,
+		"useDebounceFn": true,
+		"useDebouncedRefHistory": true,
+		"useDeviceMotion": true,
+		"useDeviceOrientation": true,
+		"useDevicePixelRatio": true,
+		"useDevicesList": true,
+		"useDisplayMedia": true,
+		"useDocumentVisibility": true,
+		"useDraggable": true,
+		"useDropZone": true,
+		"useElementBounding": true,
+		"useElementByPoint": true,
+		"useElementHover": true,
+		"useElementSize": true,
+		"useElementVisibility": true,
+		"useEventBus": true,
+		"useEventListener": true,
+		"useEventSource": true,
+		"useEyeDropper": true,
+		"useFavicon": true,
+		"useFetch": true,
+		"useFileDialog": true,
+		"useFileSystemAccess": true,
+		"useFocus": true,
+		"useFocusWithin": true,
+		"useFps": true,
+		"useFullscreen": true,
+		"useGamepad": true,
+		"useGeolocation": true,
+		"useIdle": true,
+		"useImage": true,
+		"useInfiniteScroll": true,
+		"useIntersectionObserver": true,
+		"useInterval": true,
+		"useIntervalFn": true,
+		"useKeyModifier": true,
+		"useLastChanged": true,
+		"useLocalStorage": true,
+		"useMagicKeys": true,
+		"useManualRefHistory": true,
+		"useMediaControls": true,
+		"useMediaQuery": true,
+		"useMemoize": true,
+		"useMemory": true,
+		"useMounted": true,
+		"useMouse": true,
+		"useMouseInElement": true,
+		"useMousePressed": true,
+		"useMutationObserver": true,
+		"useNavigatorLanguage": true,
+		"useNetwork": true,
+		"useNow": true,
+		"useObjectUrl": true,
+		"useOffsetPagination": true,
+		"useOnline": true,
+		"usePageLeave": true,
+		"useParallax": true,
+		"usePermission": true,
+		"usePointer": true,
+		"usePointerLock": true,
+		"usePointerSwipe": true,
+		"usePreferredColorScheme": true,
+		"usePreferredContrast": true,
+		"usePreferredDark": true,
+		"usePreferredLanguages": true,
+		"usePreferredReducedMotion": true,
+		"usePrevious": true,
+		"useRafFn": true,
+		"useRefHistory": true,
+		"useResizeObserver": true,
+		"useScreenOrientation": true,
+		"useScreenSafeArea": true,
+		"useScriptTag": true,
+		"useScroll": true,
+		"useScrollLock": true,
+		"useSessionStorage": true,
+		"useShare": true,
+		"useSlots": true,
+		"useSorted": true,
+		"useSpeechRecognition": true,
+		"useSpeechSynthesis": true,
+		"useStepper": true,
+		"useStorage": true,
+		"useStorageAsync": true,
+		"useStyleTag": true,
+		"useSupported": true,
+		"useSwipe": true,
+		"useTemplateRefsList": true,
+		"useTextDirection": true,
+		"useTextSelection": true,
+		"useTextareaAutosize": true,
+		"useThrottle": true,
+		"useThrottleFn": true,
+		"useThrottledRefHistory": true,
+		"useTimeAgo": true,
+		"useTimeout": true,
+		"useTimeoutFn": true,
+		"useTimeoutPoll": true,
+		"useTimestamp": true,
+		"useTitle": true,
+		"useToNumber": true,
+		"useToString": true,
+		"useToggle": true,
+		"useTransition": true,
+		"useUrlSearchParams": true,
+		"useUserMedia": true,
+		"useVModel": true,
+		"useVModels": true,
+		"useVibrate": true,
+		"useVirtualList": true,
+		"useWakeLock": true,
+		"useWebNotification": true,
+		"useWebSocket": true,
+		"useWebWorker": true,
+		"useWebWorkerFn": true,
+		"useWindowFocus": true,
+		"useWindowScroll": true,
+		"useWindowSize": true,
+		"watch": true,
+		"watchArray": true,
+		"watchAtMost": true,
+		"watchDebounced": true,
+		"watchEffect": true,
+		"watchIgnorable": true,
+		"watchOnce": true,
+		"watchPausable": true,
+		"watchPostEffect": true,
+		"watchSyncEffect": true,
+		"watchThrottled": true,
+		"watchTriggerable": true,
+		"watchWithFilter": true,
+		"whenever": true,
+		"ImportOption": true,
+		"TreeType": true,
+		"FieldOption": true,
+		"PageData": true,
+		"storeToRefs": true,
+		"DictDataOption": true,
+		"UploadOption": true
+	}
+}
diff --git a/.eslintrc.js b/.eslintrc.js
new file mode 100644
index 0000000..518d476
--- /dev/null
+++ b/.eslintrc.js
@@ -0,0 +1,41 @@
+module.exports = {
+	env: {
+		browser: true,
+		es2021: true,
+		node: true
+	},
+	parser: 'vue-eslint-parser',
+	extends: [
+		'eslint:recommended',
+		'plugin:vue/vue3-essential',
+		'plugin:@typescript-eslint/recommended',
+		'./.eslintrc-auto-import.json',
+		'plugin:prettier/recommended'
+	],
+	parserOptions: {
+		ecmaVersion: '2020',
+		sourceType: 'module',
+		parser: '@typescript-eslint/parser'
+	},
+	plugins: ['vue', '@typescript-eslint'],
+	rules: {
+		'vue/multi-word-component-names': 'off',
+		'@typescript-eslint/no-empty-function': 'off',
+		'@typescript-eslint/no-explicit-any': 'off',
+		'vue/no-v-model-argument': 'off',
+		'@typescript-eslint/ban-types': [
+			'error',
+			{
+				// 鍏抽棴绌虹被鍨嬫鏌� {}
+				extendDefaults: true,
+				types: {
+					'{}': false
+				}
+			}
+		]
+	},
+	globals: {
+		DialogOption: 'readonly',
+		OptionType: 'readonly'
+	}
+};
diff --git a/.prettierignore b/.prettierignore
new file mode 100644
index 0000000..d251d2e
--- /dev/null
+++ b/.prettierignore
@@ -0,0 +1,9 @@
+/dist/*
+.local
+.output.js
+/node_modules/**
+
+**/*.svg
+**/*.sh
+
+/public/*
\ No newline at end of file
diff --git a/.prettierrc.cjs b/.prettierrc.cjs
new file mode 100644
index 0000000..3df4406
--- /dev/null
+++ b/.prettierrc.cjs
@@ -0,0 +1,46 @@
+/**
+ * 浠g爜鏍煎紡鍖栭厤缃�
+ */
+module.exports = {
+	// 涓�琛屾渶澶氬灏戜釜瀛楃
+	printWidth: 150,
+	// 鎸囧畾姣忎釜缂╄繘绾у埆鐨勭┖鏍兼暟
+	tabWidth: 2,
+	// 浣跨敤鍒惰〃绗﹁�屼笉鏄┖鏍肩缉杩涜
+	useTabs: true,
+	// 鍦ㄨ鍙ユ湯灏炬槸鍚﹂渶瑕佸垎鍙�
+	semi: true,
+	// 鏄惁浣跨敤鍗曞紩鍙�
+	singleQuote: true,
+	// 鏇存敼寮曠敤瀵硅薄灞炴�х殑鏃堕棿 鍙�夊��"<as-needed|consistent|preserve>"
+	quoteProps: 'as-needed',
+	// 鍦↗SX涓娇鐢ㄥ崟寮曞彿鑰屼笉鏄弻寮曞彿
+	jsxSingleQuote: false,
+	// 澶氳鏃跺敖鍙兘鎵撳嵃灏鹃殢閫楀彿銆傦紙渚嬪锛屽崟琛屾暟缁勬案杩滀笉浼氬嚭鐜伴�楀彿缁撳熬銆傦級 鍙�夊��"<none|es5|all>"锛岄粯璁one
+	trailingComma: 'none',
+	// 鍦ㄥ璞℃枃瀛椾腑鐨勬嫭鍙蜂箣闂存墦鍗扮┖鏍�
+	bracketSpacing: true,
+	// jsx 鏍囩鐨勫弽灏栨嫭鍙烽渶瑕佹崲琛�
+	jsxBracketSameLine: false,
+	embeddedLanguageFormatting: 'off',
+	// 鍦ㄥ崟鐙殑绠ご鍑芥暟鍙傛暟鍛ㄥ洿鍖呮嫭鎷彿 always锛�(x) => x \ avoid锛歺 => x
+	arrowParens: 'always',
+	// 杩欎袱涓�夐」鍙敤浜庢牸寮忓寲浠ョ粰瀹氬瓧绗﹀亸绉婚噺锛堝垎鍒寘鎷拰涓嶅寘鎷級寮�濮嬪拰缁撴潫鐨勪唬鐮�
+	rangeStart: 0,
+	rangeEnd: Infinity,
+	// 鎸囧畾瑕佷娇鐢ㄧ殑瑙f瀽鍣紝涓嶉渶瑕佸啓鏂囦欢寮�澶寸殑 @prettier
+	requirePragma: false,
+	// 涓嶉渶瑕佽嚜鍔ㄥ湪鏂囦欢寮�澶存彃鍏� @prettier
+	insertPragma: false,
+	// 浣跨敤榛樿鐨勬姌琛屾爣鍑� always\never\preserve
+	proseWrap: 'preserve',
+	// 鎸囧畾HTML鏂囦欢鐨勫叏灞�绌烘牸鏁忔劅搴� css\strict\ignore
+	htmlWhitespaceSensitivity: 'css',
+	// Vue鏂囦欢鑴氭湰鍜屾牱寮忔爣绛剧缉杩�
+	vueIndentScriptAndStyle: false,
+	//鍦� windows 鎿嶄綔绯荤粺涓崲琛岀閫氬父鏄洖杞� (CR) 鍔犳崲琛屽垎闅旂 (LF)锛屼篃灏辨槸鍥炶溅鎹㈣(CRLF)锛�
+	//鐒惰�屽湪 Linux 鍜� Unix 涓彧浣跨敤绠�鍗曠殑鎹㈣鍒嗛殧绗� (LF)銆�
+	//瀵瑰簲鐨勬帶鍒跺瓧绗︿负 "\n" (LF) 鍜� "\r\n"(CRLF)銆俛uto鎰忎负淇濇寔鐜版湁鐨勮灏�
+	// 鎹㈣绗︿娇鐢� lf 缁撳熬鏄� 鍙�夊��"<auto|lf|crlf|cr>"
+	endOfLine: 'auto'
+};
diff --git a/README.md b/README.md
index 615a0a3..f1df2bc 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,62 @@
 ## 骞冲彴绠�浠�
 
-* 鏈粨搴撲负鍓嶇鎶�鏈爤 [Vue3](https://v3.cn.vuejs.org) + [Element Plus](https://element-plus.org/zh-CN) + [Vite](https://cn.vitejs.dev) 鐗堟湰銆�
-* 閰嶅鍚庣浠g爜浠撳簱鍦板潃 
-* [RuoYi-Vue-Plus 5.X(娉ㄦ剰鐗堟湰鍙�)](https://gitee.com/dromara/RuoYi-Vue-Plus)
-* [RuoYi-Cloud-Plus 2.X(娉ㄦ剰鐗堟湰鍙�)](https://gitee.com/dromara/RuoYi-Cloud-Plus)
+## 骞冲彴绠�浠�
+
+- 鏈粨搴撲负鍓嶇鎶�鏈爤 [Vue3](https://v3.cn.vuejs.org) + [Element Plus](https://element-plus.org/zh-CN) + [Vite](https://cn.vitejs.dev) 鐗堟湰銆�
+- 閰嶅鍚庣浠g爜浠撳簱鍦板潃
+- [RuoYi-Vue-Plus 5.X(娉ㄦ剰鐗堟湰鍙�)](https://gitee.com/dromara/RuoYi-Vue-Plus)
+- [RuoYi-Cloud-Plus 2.X(娉ㄦ剰鐗堟湰鍙�)](https://gitee.com/dromara/RuoYi-Cloud-Plus)
+
+## 鍓嶇杩愯
+
+```bash
+# 鍏嬮殕椤圭洰
+git clone https://gitee.com/JavaLionLi/plus-ui.git
+
+# 瀹夎渚濊禆
+npm install --registry=https://registry.npmmirror.com
+
+# 鍚姩鏈嶅姟
+npm run dev
+
+# 鏋勫缓娴嬭瘯鐜 yarn build:stage
+# 鏋勫缓鐢熶骇鐜 yarn build:prod
+# 鍓嶇璁块棶鍦板潃 http://localhost:80
+```
+
+## 鏈鏋朵笌 RuoYi 鐨勪笟鍔″樊寮�
+
+| 涓氬姟         | 鍔熻兘璇存槑                                                        | 鏈鏋� | RuoYi                          |
+| ------------ | --------------------------------------------------------------- | ------ | ------------------------------ |
+| 绉熸埛绠$悊     | 绯荤粺鍐呯鎴风殑绠$悊 濡�:绉熸埛濂楅銆佽繃鏈熸椂闂淬�佺敤鎴锋暟閲忋�佷紒涓氫俊鎭瓑    | 鏀寔   | 鏃�                             |
+| 绉熸埛濂楅绠$悊 | 绯荤粺鍐呯鎴锋墍鑳戒娇鐢ㄧ殑濂楅绠$悊 濡�:濂楅鍐呮墍鍖呭惈鐨勮彍鍗曠瓑            | 鏀寔   | 鏃�                             |
+| 鐢ㄦ埛绠$悊     | 鐢ㄦ埛鐨勭鐞嗛厤缃� 濡�:鏂板鐢ㄦ埛銆佸垎閰嶇敤鎴锋墍灞為儴闂ㄣ�佽鑹层�佸矖浣嶇瓑      | 鏀寔   | 鏀寔                           |
+| 閮ㄩ棬绠$悊     | 閰嶇疆绯荤粺缁勭粐鏈烘瀯锛堝叕鍙搞�侀儴闂ㄣ�佸皬缁勶級 鏍戠粨鏋勫睍鐜版敮鎸佹暟鎹潈闄�     | 鏀寔   | 鏀寔                           |
+| 宀椾綅绠$悊     | 閰嶇疆绯荤粺鐢ㄦ埛鎵�灞炴媴浠昏亴鍔�                                        | 鏀寔   | 鏀寔                           |
+| 鑿滃崟绠$悊     | 閰嶇疆绯荤粺鑿滃崟銆佹搷浣滄潈闄愩�佹寜閽潈闄愭爣璇嗙瓑                          | 鏀寔   | 鏀寔                           |
+| 瑙掕壊绠$悊     | 瑙掕壊鑿滃崟鏉冮檺鍒嗛厤銆佽缃鑹叉寜鏈烘瀯杩涜鏁版嵁鑼冨洿鏉冮檺鍒掑垎            | 鏀寔   | 鏀寔                           |
+| 瀛楀吀绠$悊     | 瀵圭郴缁熶腑缁忓父浣跨敤鐨勪竴浜涜緝涓哄浐瀹氱殑鏁版嵁杩涜缁存姢                    | 鏀寔   | 鏀寔                           |
+| 鍙傛暟绠$悊     | 瀵圭郴缁熷姩鎬侀厤缃父鐢ㄥ弬鏁�                                          | 鏀寔   | 鏀寔                           |
+| 閫氱煡鍏憡     | 绯荤粺閫氱煡鍏憡淇℃伅鍙戝竷缁存姢                                        | 鏀寔   | 鏀寔                           |
+| 鎿嶄綔鏃ュ織     | 绯荤粺姝e父鎿嶄綔鏃ュ織璁板綍鍜屾煡璇� 绯荤粺寮傚父淇℃伅鏃ュ織璁板綍鍜屾煡璇�           | 鏀寔   | 鏀寔                           |
+| 鐧诲綍鏃ュ織     | 绯荤粺鐧诲綍鏃ュ織璁板綍鏌ヨ鍖呭惈鐧诲綍寮傚父                                | 鏀寔   | 鏀寔                           |
+| 鏂囦欢绠$悊     | 绯荤粺鏂囦欢灞曠ず銆佷笂浼犮�佷笅杞姐�佸垹闄ょ瓑绠$悊                            | 鏀寔   | 鏃�                             |
+| 鏂囦欢閰嶇疆绠$悊 | 绯荤粺鏂囦欢涓婁紶銆佷笅杞芥墍闇�瑕佺殑閰嶇疆淇℃伅鍔ㄦ�佹坊鍔犮�佷慨鏀广�佸垹闄ょ瓑绠$悊    | 鏀寔   | 鏃�                             |
+| 鍦ㄧ嚎鐢ㄦ埛绠$悊 | 宸茬櫥褰曠郴缁熺殑鍦ㄧ嚎鐢ㄦ埛淇℃伅鐩戞帶涓庡己鍒惰涪鍑烘搷浣�                      | 鏀寔   | 鏀寔                           |
+| 瀹氭椂浠诲姟     | 杩愯鎶ヨ〃銆佷换鍔$鐞�(娣诲姞銆佷慨鏀广�佸垹闄�)銆佹棩蹇楃鐞嗐�佹墽琛屽櫒绠$悊绛�    | 鏀寔   | 浠呮敮鎸佷换鍔′笌鏃ュ織绠$悊           |
+| 浠g爜鐢熸垚     | 澶氭暟鎹簮鍓嶅悗绔唬鐮佺殑鐢熸垚锛坖ava銆乭tml銆亁ml銆乻ql锛夋敮鎸� CRUD 涓嬭浇  | 鏀寔   | 浠呮敮鎸佸崟鏁版嵁婧�                 |
+| 绯荤粺鎺ュ彛     | 鏍规嵁涓氬姟浠g爜鑷姩鐢熸垚鐩稿叧鐨� api 鎺ュ彛鏂囨。                         | 鏀寔   | 鏀寔                           |
+| 鏈嶅姟鐩戞帶     | 鐩戣闆嗙兢绯荤粺 CPU銆佸唴瀛樸�佺鐩樸�佸爢鏍堛�佸湪绾挎棩蹇椼�丼pring 鐩稿叧閰嶇疆绛� | 鏀寔   | 浠呮敮鎸佸崟鏈� CPU銆佸唴瀛樸�佺鐩樼洃鎺� |
+| 缂撳瓨鐩戞帶     | 瀵圭郴缁熺殑缂撳瓨淇℃伅鏌ヨ锛屽懡浠ょ粺璁$瓑銆�                              | 鏀寔   | 鏀寔                           |
+| 鍦ㄧ嚎鏋勫缓鍣�   | 鎷栧姩琛ㄥ崟鍏冪礌鐢熸垚鐩稿簲鐨� HTML 浠g爜銆�                              | 鏀寔   | 鏀寔                           |
+| 浣跨敤妗堜緥     | 绯荤粺鐨勪竴浜涘姛鑳芥渚�                                              | 鏀寔   | 涓嶆敮鎸�                         |
+
+## 婕旂ず鍥�
+
+- 鏈粨搴撲负鍓嶇鎶�鏈爤 [Vue3](https://v3.cn.vuejs.org) + [Element Plus](https://element-plus.org/zh-CN) + [Vite](https://cn.vitejs.dev) 鐗堟湰銆�
+- 閰嶅鍚庣浠g爜浠撳簱鍦板潃
+- [RuoYi-Vue-Plus 5.X(娉ㄦ剰鐗堟湰鍙�)](https://gitee.com/dromara/RuoYi-Vue-Plus)
+- [RuoYi-Cloud-Plus 2.X(娉ㄦ剰鐗堟湰鍙�)](https://gitee.com/dromara/RuoYi-Cloud-Plus)
 
 ## 鍓嶇杩愯
 
@@ -23,6 +76,7 @@
 ```
 
 ## 鍚庣鏀归��
+
 鍙傝�冨悗绔唬鐮佸唴 `ruoyi-gen/resources/vm/vue/v3/readme.txt` 璇存槑
 
 ## 鍐呯疆鍔熻兘
@@ -40,11 +94,11 @@
 11. 鐧诲綍鏃ュ織锛氱郴缁熺櫥褰曟棩蹇楄褰曟煡璇㈠寘鍚櫥褰曞紓甯搞��
 12. 鍦ㄧ嚎鐢ㄦ埛锛氬綋鍓嶇郴缁熶腑娲昏穬鐢ㄦ埛鐘舵�佺洃鎺с��
 13. 瀹氭椂浠诲姟锛氬湪绾匡紙娣诲姞銆佷慨鏀广�佸垹闄�)浠诲姟璋冨害鍖呭惈鎵ц缁撴灉鏃ュ織銆�
-14. 浠g爜鐢熸垚锛氬墠鍚庣浠g爜鐨勭敓鎴愶紙java銆乭tml銆亁ml銆乻ql锛夋敮鎸丆RUD涓嬭浇 銆�
-15. 绯荤粺鎺ュ彛锛氭牴鎹笟鍔′唬鐮佽嚜鍔ㄧ敓鎴愮浉鍏崇殑api鎺ュ彛鏂囨。銆�
-16. 鏈嶅姟鐩戞帶锛氱洃瑙嗗綋鍓嶇郴缁烠PU銆佸唴瀛樸�佺鐩樸�佸爢鏍堢瓑鐩稿叧淇℃伅銆�
+14. 浠g爜鐢熸垚锛氬墠鍚庣浠g爜鐨勭敓鎴愶紙java銆乭tml銆亁ml銆乻ql锛夋敮鎸� CRUD 涓嬭浇 銆�
+15. 绯荤粺鎺ュ彛锛氭牴鎹笟鍔′唬鐮佽嚜鍔ㄧ敓鎴愮浉鍏崇殑 api 鎺ュ彛鏂囨。銆�
+16. 鏈嶅姟鐩戞帶锛氱洃瑙嗗綋鍓嶇郴缁� CPU銆佸唴瀛樸�佺鐩樸�佸爢鏍堢瓑鐩稿叧淇℃伅銆�
 17. 缂撳瓨鐩戞帶锛氬绯荤粺鐨勭紦瀛樹俊鎭煡璇紝鍛戒护缁熻绛夈��
-18. 鍦ㄧ嚎鏋勫缓鍣細鎷栧姩琛ㄥ崟鍏冪礌鐢熸垚鐩稿簲鐨凥TML浠g爜銆�
+18. 鍦ㄧ嚎鏋勫缓鍣細鎷栧姩琛ㄥ崟鍏冪礌鐢熸垚鐩稿簲鐨� HTML 浠g爜銆�
 
 ## 婕旂ず鍥�
 
@@ -81,4 +135,4 @@
         <td><img src="https://oscimg.oschina.net/oscnet/b6115bc8c31de52951982e509930b20684a.jpg"/></td>
         <td><img src="https://oscimg.oschina.net/oscnet/up-5e4daac0bb59612c5038448acbcef235e3a.png"/></td>
     </tr>
-</table>
\ No newline at end of file
+</table>
diff --git a/Vite/plugins/auto-import.ts b/Vite/plugins/auto-import.ts
new file mode 100644
index 0000000..c7cee4b
--- /dev/null
+++ b/Vite/plugins/auto-import.ts
@@ -0,0 +1,24 @@
+import AutoImport from 'unplugin-auto-import/vite';
+import { ElementPlusResolver } from 'unplugin-vue-components/resolvers';
+import IconsResolver from 'unplugin-icons/resolver';
+
+export default (path: any) => {
+	return AutoImport({
+		// 鑷姩瀵煎叆 Vue 鐩稿叧鍑芥暟
+		imports: ['vue', 'vue-router', '@vueuse/core', 'pinia'],
+		eslintrc: {
+			enabled: false,
+			filepath: './.eslintrc-auto-import.json',
+			globalsPropValue: true
+		},
+		resolvers: [
+			// 鑷姩瀵煎叆 Element Plus 鐩稿叧鍑芥暟ElMessage, ElMessageBox... (甯︽牱寮�)
+			ElementPlusResolver(),
+			IconsResolver({
+				prefix: 'Icon'
+			})
+		],
+		vueTemplate: true, // 鏄惁鍦� vue 妯℃澘涓嚜鍔ㄥ鍏�
+		dts: path.resolve(path.resolve(__dirname, '../../src'), 'types', 'auto-imports.d.ts')
+	});
+};
diff --git a/Vite/plugins/components.ts b/Vite/plugins/components.ts
new file mode 100644
index 0000000..dbc5b19
--- /dev/null
+++ b/Vite/plugins/components.ts
@@ -0,0 +1,17 @@
+import Components from 'unplugin-vue-components/vite';
+import { ElementPlusResolver } from 'unplugin-vue-components/resolvers';
+import IconsResolver from 'unplugin-icons/resolver';
+
+export default (path: any) => {
+	return Components({
+		resolvers: [
+			// 鑷姩瀵煎叆 Element Plus 缁勪欢
+			ElementPlusResolver(),
+			// 鑷姩娉ㄥ唽鍥炬爣缁勪欢
+			IconsResolver({
+				enabledCollections: ['ep']
+			})
+		],
+		dts: path.resolve(path.resolve(__dirname, '../../src'), 'types', 'components.d.ts')
+	});
+};
diff --git a/Vite/plugins/icons.ts b/Vite/plugins/icons.ts
new file mode 100644
index 0000000..174572c
--- /dev/null
+++ b/Vite/plugins/icons.ts
@@ -0,0 +1,8 @@
+import Icons from 'unplugin-icons/vite';
+
+export default () => {
+	return Icons({
+		// 鑷姩瀹夎鍥炬爣搴�
+		autoInstall: true
+	});
+};
diff --git a/Vite/plugins/index.ts b/Vite/plugins/index.ts
new file mode 100644
index 0000000..001b56c
--- /dev/null
+++ b/Vite/plugins/index.ts
@@ -0,0 +1,18 @@
+import vue from '@vitejs/plugin-vue';
+import createUnoCss from './unocss';
+import createAutoImport from './auto-import';
+import createComponents from './components';
+import createIcons from './icons';
+import createSvgIconsPlugin from './svg-icon';
+import path from 'path';
+
+export default (viteEnv, isBuild = false): [] => {
+	const vitePlusgins: any = [];
+	vitePlusgins.push(vue());
+	vitePlusgins.push(createUnoCss());
+	vitePlusgins.push(createAutoImport(path));
+	vitePlusgins.push(createComponents(path));
+	vitePlusgins.push(createIcons());
+	vitePlusgins.push(createSvgIconsPlugin(path));
+	return vitePlusgins;
+};
diff --git a/Vite/plugins/svg-icon.ts b/Vite/plugins/svg-icon.ts
new file mode 100644
index 0000000..0d83ce0
--- /dev/null
+++ b/Vite/plugins/svg-icon.ts
@@ -0,0 +1,9 @@
+import { createSvgIconsPlugin } from 'vite-plugin-svg-icons';
+export default (path: any) => {
+	return createSvgIconsPlugin({
+		// 鎸囧畾闇�瑕佺紦瀛樼殑鍥炬爣鏂囦欢澶�
+		iconDirs: [path.resolve(path.resolve(__dirname, '../../src'), 'assets/icons/svg')],
+		// 鎸囧畾symbolId鏍煎紡
+		symbolId: 'icon-[dir]-[name]'
+	});
+};
diff --git a/Vite/plugins/unocss.ts b/Vite/plugins/unocss.ts
new file mode 100644
index 0000000..e9e3254
--- /dev/null
+++ b/Vite/plugins/unocss.ts
@@ -0,0 +1,13 @@
+import UnoCss from 'unocss/vite';
+import { presetUno, presetAttributify, presetIcons } from 'unocss';
+
+export default () => {
+	return UnoCss({
+		presets: [presetUno(), presetAttributify(), presetIcons()],
+		// rules: [['search', {}]],
+		shortcuts: {
+			'panel-title':
+				'pb-[5px] font-sans leading-[1.1] font-medium text-base text-[#6379bb] border-b border-b-solid border-[var(--el-border-color-light)] mb-5 mt-0'
+		}
+	});
+};
diff --git a/commitlint.config.js b/commitlint.config.js
new file mode 100644
index 0000000..21c37d7
--- /dev/null
+++ b/commitlint.config.js
@@ -0,0 +1,22 @@
+module.exports = {
+	extends: ['@commitlint/config-conventional'],
+	rules: {
+		'type-enum': [
+			2,
+			'always',
+			[
+				'feat', // 鏂板姛鑳� feature
+				'fix', // 淇 bug
+				'docs', // 鏂囨。娉ㄩ噴
+				'style', // 浠g爜鏍煎紡
+				'refactor', // 閲嶆瀯
+				'perf', // 鎬ц兘浼樺寲
+				'test', // 澧炲姞娴嬭瘯
+				'chore', // 鏋勫缓杩囩▼鎴栬緟鍔╁伐鍏风殑鍙樺姩
+				'revert', // 鍥為��
+				'build' // 鎵撳寘
+			]
+		],
+		'subject-case': [0]
+	}
+};
diff --git a/html/ie.html b/html/ie.html
index 052ffcd..52fdef6 100644
--- a/html/ie.html
+++ b/html/ie.html
@@ -1,46 +1,60 @@
-
 <!DOCTYPE html>
 <html lang="zh-CN">
-<head>
-    <meta charset="UTF-8" />
-    <title>璇峰崌绾ф偍鐨勬祻瑙堝櫒</title>
-    <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1" >
-    <meta name="renderer" content="webkit">
-    <base target="_blank" />
-    <style type="text/css">
-        html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video{border:0;font-size:100%;font:inherit;vertical-align:baseline;margin:0;padding:0}article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section{display:block}body{line-height:1}ol,ul{list-style:none}blockquote,q{quotes:none}blockquote:before,blockquote:after,q:before,q:after{content:none}table{border-collapse:collapse;border-spacing:0}
-        a{text-decoration:none;color:#0072c6;}a:hover{text-decoration:none;color:#004d8c;}
-        body{width:960px;margin:0 auto;padding:10px;font-size:14px;line-height:24px;color:#454545;font-family:'Microsoft YaHei UI','Microsoft YaHei',DengXian,SimSun,'Segoe UI',Tahoma,Helvetica,sans-serif;overflow-y:scroll}
-        h1{font-size:40px;line-height:80px;font-weight:100;margin-bottom:10px;}
-        h2{font-size:20px;line-height:25px;font-weight:100;margin:10px 0;}
-        em{color:red}
-        p{margin-bottom:10px;}
-        hr{margin:20px 0;border:0;border-top:1px solid #dadada}
-        span{display:block;font-size:12px;line-height:12px;}
-        .clean{clear:both;}
-        .browser{padding:10px 10px;}
-        .browser li{width:auto;padding:0 80px;margin-top:30px;height:34px;line-height:22px;float:left;list-style:none;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACIAAADMCAYAAAAWCXEwAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKTWlDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVN3WJP3Fj7f92UPVkLY8LGXbIEAIiOsCMgQWaIQkgBhhBASQMWFiApWFBURnEhVxILVCkidiOKgKLhnQYqIWotVXDjuH9yntX167+3t+9f7vOec5/zOec8PgBESJpHmomoAOVKFPDrYH49PSMTJvYACFUjgBCAQ5svCZwXFAADwA3l4fnSwP/wBr28AAgBw1S4kEsfh/4O6UCZXACCRAOAiEucLAZBSAMguVMgUAMgYALBTs2QKAJQAAGx5fEIiAKoNAOz0ST4FANipk9wXANiiHKkIAI0BAJkoRyQCQLsAYFWBUiwCwMIAoKxAIi4EwK4BgFm2MkcCgL0FAHaOWJAPQGAAgJlCLMwAIDgCAEMeE80DIEwDoDDSv+CpX3CFuEgBAMDLlc2XS9IzFLiV0Bp38vDg4iHiwmyxQmEXKRBmCeQinJebIxNI5wNMzgwAABr50cH+OD+Q5+bk4eZm52zv9MWi/mvwbyI+IfHf/ryMAgQAEE7P79pf5eXWA3DHAbB1v2upWwDaVgBo3/ldM9sJoFoK0Hr5i3k4/EAenqFQyDwdHAoLC+0lYqG9MOOLPv8z4W/gi372/EAe/tt68ABxmkCZrcCjg/1xYW52rlKO58sEQjFu9+cj/seFf/2OKdHiNLFcLBWK8ViJuFAiTcd5uVKRRCHJleIS6X8y8R+W/QmTdw0ArIZPwE62B7XLbMB+7gECiw5Y0nYAQH7zLYwaC5EAEGc0Mnn3AACTv/mPQCsBAM2XpOMAALzoGFyolBdMxggAAESggSqwQQcMwRSswA6cwR28wBcCYQZEQAwkwDwQQgbkgBwKoRiWQRlUwDrYBLWwAxqgEZrhELTBMTgN5+ASXIHrcBcGYBiewhi8hgkEQcgIE2EhOogRYo7YIs4IF5mOBCJhSDSSgKQg6YgUUSLFyHKkAqlCapFdSCPyLXIUOY1cQPqQ28ggMor8irxHMZSBslED1AJ1QLmoHxqKxqBz0XQ0D12AlqJr0Rq0Hj2AtqKn0UvodXQAfYqOY4DRMQ5mjNlhXIyHRWCJWBomxxZj5Vg1Vo81Yx1YN3YVG8CeYe8IJAKLgBPsCF6EEMJsgpCQR1hMWEOoJewjtBK6CFcJg4Qxwicik6hPtCV6EvnEeGI6sZBYRqwm7iEeIZ4lXicOE1+TSCQOyZLkTgohJZAySQtJa0jbSC2kU6Q+0hBpnEwm65Btyd7kCLKArCCXkbeQD5BPkvvJw+S3FDrFiOJMCaIkUqSUEko1ZT/lBKWfMkKZoKpRzame1AiqiDqfWkltoHZQL1OHqRM0dZolzZsWQ8ukLaPV0JppZ2n3aC/pdLoJ3YMeRZfQl9Jr6Afp5+mD9HcMDYYNg8dIYigZaxl7GacYtxkvmUymBdOXmchUMNcyG5lnmA+Yb1VYKvYqfBWRyhKVOpVWlX6V56pUVXNVP9V5qgtUq1UPq15WfaZGVbNQ46kJ1Bar1akdVbupNq7OUndSj1DPUV+jvl/9gvpjDbKGhUaghkijVGO3xhmNIRbGMmXxWELWclYD6yxrmE1iW7L57Ex2Bfsbdi97TFNDc6pmrGaRZp3mcc0BDsax4PA52ZxKziHODc57LQMtPy2x1mqtZq1+rTfaetq+2mLtcu0W7eva73VwnUCdLJ31Om0693UJuja6UbqFutt1z+o+02PreekJ9cr1Dund0Uf1bfSj9Rfq79bv0R83MDQINpAZbDE4Y/DMkGPoa5hpuNHwhOGoEctoupHEaKPRSaMnuCbuh2fjNXgXPmasbxxirDTeZdxrPGFiaTLbpMSkxeS+Kc2Ua5pmutG003TMzMgs3KzYrMnsjjnVnGueYb7ZvNv8jYWlRZzFSos2i8eW2pZ8ywWWTZb3rJhWPlZ5VvVW16xJ1lzrLOtt1ldsUBtXmwybOpvLtqitm63Edptt3xTiFI8p0in1U27aMez87ArsmuwG7Tn2YfYl9m32zx3MHBId1jt0O3xydHXMdmxwvOuk4TTDqcSpw+lXZxtnoXOd8zUXpkuQyxKXdpcXU22niqdun3rLleUa7rrStdP1o5u7m9yt2W3U3cw9xX2r+00umxvJXcM970H08PdY4nHM452nm6fC85DnL152Xlle+70eT7OcJp7WMG3I28Rb4L3Le2A6Pj1l+s7pAz7GPgKfep+Hvqa+It89viN+1n6Zfgf8nvs7+sv9j/i/4XnyFvFOBWABwQHlAb2BGoGzA2sDHwSZBKUHNQWNBbsGLww+FUIMCQ1ZH3KTb8AX8hv5YzPcZyya0RXKCJ0VWhv6MMwmTB7WEY6GzwjfEH5vpvlM6cy2CIjgR2yIuB9pGZkX+X0UKSoyqi7qUbRTdHF09yzWrORZ+2e9jvGPqYy5O9tqtnJ2Z6xqbFJsY+ybuIC4qriBeIf4RfGXEnQTJAntieTE2MQ9ieNzAudsmjOc5JpUlnRjruXcorkX5unOy553PFk1WZB8OIWYEpeyP+WDIEJQLxhP5aduTR0T8oSbhU9FvqKNolGxt7hKPJLmnVaV9jjdO31D+miGT0Z1xjMJT1IreZEZkrkj801WRNberM/ZcdktOZSclJyjUg1plrQr1zC3KLdPZisrkw3keeZtyhuTh8r35CP5c/PbFWyFTNGjtFKuUA4WTC+oK3hbGFt4uEi9SFrUM99m/ur5IwuCFny9kLBQuLCz2Lh4WfHgIr9FuxYji1MXdy4xXVK6ZHhp8NJ9y2jLspb9UOJYUlXyannc8o5Sg9KlpUMrglc0lamUycturvRauWMVYZVkVe9ql9VbVn8qF5VfrHCsqK74sEa45uJXTl/VfPV5bdra3kq3yu3rSOuk626s91m/r0q9akHV0IbwDa0b8Y3lG19tSt50oXpq9Y7NtM3KzQM1YTXtW8y2rNvyoTaj9nqdf13LVv2tq7e+2Sba1r/dd3vzDoMdFTve75TsvLUreFdrvUV99W7S7oLdjxpiG7q/5n7duEd3T8Wej3ulewf2Re/ranRvbNyvv7+yCW1SNo0eSDpw5ZuAb9qb7Zp3tXBaKg7CQeXBJ9+mfHvjUOihzsPcw83fmX+39QjrSHkr0jq/dawto22gPaG97+iMo50dXh1Hvrf/fu8x42N1xzWPV56gnSg98fnkgpPjp2Snnp1OPz3Umdx590z8mWtdUV29Z0PPnj8XdO5Mt1/3yfPe549d8Lxw9CL3Ytslt0utPa49R35w/eFIr1tv62X3y+1XPK509E3rO9Hv03/6asDVc9f41y5dn3m978bsG7duJt0cuCW69fh29u0XdwruTNxdeo94r/y+2v3qB/oP6n+0/rFlwG3g+GDAYM/DWQ/vDgmHnv6U/9OH4dJHzEfVI0YjjY+dHx8bDRq98mTOk+GnsqcTz8p+Vv9563Or59/94vtLz1j82PAL+YvPv655qfNy76uprzrHI8cfvM55PfGm/K3O233vuO+638e9H5ko/ED+UPPR+mPHp9BP9z7nfP78L/eE8/sl0p8zAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAC7ESURBVHja5Lx5dFRV1rBfgHwYRQQVtB26ZWhtabtfeUGxGxFbUGZF8RMHGkVbRkekVYiKisicVhE0gEwBokgDAhEMMSSQkAECwcxkrlRSqVTqJqnxzs/vj5t7qUyAvr9e37fWV2vtleSm6p6n9t5nn733OVU2RaUaEP5PiqJSbeMXPBTA5/Xhzk9Vnd9vo3HFx21E2LYJX9IRgh6npvyCe9uaqS4K4C3IpXHFx9S99CTuJ8Z0KLVjRlA7ZgTuJ8ZgXxmJL+kIlwAkXBQk6HFq9pWRVA8fSvXwodYgdS892a6EA1UNvouqwXdR99KTeAtyfz2IL+kI1cOHYh9wqwVwKWJqpXbMCOv19gG3Imzb1JF2OgZxfr/NukH4jcNVfyEAE8IU+4BbKet1PfaVke3BtA/i/H6b8aIBt7a4mWmaC0nr55vmqRp8F5V33Mm5LhHtwbQF8SUdsSDCb1I1+K42g1xIWgOYYh9wK+e6RCBs29QxSIWus37aJM51iWjx4so77mwD1d5AHQ1eecedlN9yuyVlva6nrNf14Q7cEmRn4W7u3T2E9ME3UX7L7W1uZg5Weced1s3sA2613ql5LXzQjuRclwjcT4wxTXQeRHC7GLdnHPeensiCVwa3e0PznZk3EbZtwluQa0kofz8NcVNxr++Ce30XnNuv61Bcu7viXt8Fvyu7JYipjfGHxzD+8Bh2j+7fAiZcC+Y0zPDIbCyD6DyV6DyVeDcIQR2C39J4oieNJ3oSOnkVcnZ35Ozu6MVdDHF0N6S4C43OqJYg/0ydzb27hzDx0FjuPT2R+asfa6OVsl7X40s6QoWus/CQk6fWZPHChhxe3lbMCxtyrN9TyxSQSwidvMoC0XK6tRGybPjSRmOuNUKVo4Zxe8YxIu4+Jh4ay/jDY7j39MQWWjnXJYLGFR9Toes8tSaLiavTrIHDxfxfapkCwW8hy9YuhCmhk1fR1FRnaCS1NM4yy8RDYy2tjIkZRXq/HtYsCnqc2sJDTkYsTrU00J6YkEJQR7M/eEGY0MmrcOenqjZA2JmyzTJLuJiOe65LBHUvPUmGR2bE4lQmrk7jqTVZHcrE1WkMWpRIdJ4KnpUXBCHLRl3e16EWIOEaMU00/vAY9na/gsYVH/NdgYe+8w9bMBeSQYsSWXjICcFvL2ga+dhlFwcJ10rjio/ZklprgbSWiavTWvzdd/5hXt5W/OtATC201sq9u4eQ+PVijmSW0nf+YQYtSmTQosR2gUYsTmXQokT6zj9saeRCpmkJ0hxD2gOZeGgsI+Lu45+ps7FXlFmDmDDtSd/5h+k7/zCpZQpa9cwOQciyIR+77LyzFhXlMyZmFOP2jLP8orVWRsTdR2ppHFtSa+k6ZZM1WHvSdcomwyxySceayO4OWTY88TdirygzUkWf18eL2//RQiutYcwYE/Q4tagDOUQ8uo6uUzbRZ3qMJV2nbCLi0XU8tSbrolNXzu6OfOyylgEN4NOkaO5acw/j9ozr0ET37h5imehIZimPL91rAfSZHsOQBfuISS7E7vaTETeX0MmrOoQInbwK+dhlNKWsahni0zPSuGvNPW1M1BrI1NrOwt0WkCn2ijJSS+MYt2ccuQk3oxd36RCi8URPY+HLT1VbgGiSzPsx71laCddMe2Yygf6ZOtuScXvG0XfJn/n8YL+LQnjibyQ34WZ8Xl/bfKSoKL+FVi4EYwKZcu/uIQzaPoExMaPQcrq1ADFX33AI1+6u1OV9HVI6ShU/TYqm75I/dwjTHtDEQ2MZt2ccg7ZPaGGScIDWEBlxc42UoSMQ00StYdoDCgcbtH0Cbx+8p40ZTIBwiFM7RmB3+y+exZvT2YRpDdR6ZoVrw1xRWwN44m/Euf06A6Ki7NLrmnDNmH7TEdSg7RP4/GA/yLK1GdwEKNzSk1M7RlDlqPl1JefOlG2MXTGmXaAxMaMsB/XE34h4tH+7ANlrB7T2iV8OAlDlqOH9mPcsIBPKlF3R16Ad7GwlxoVberYAKCrKv1ghfmkg5sPldLIzZVsLqLErxpC9doAlp3aMICNurlGyVpRdSAu/HqS1Q58rd1JUlI87P1UtKsrHXlGG3e1HCOoov+x2wiX3RxT+o49L1IgutXxVUCfDIxNfLraQDI+M3e3/NdCXbhohqBNfLrIsVzZqmoT6dmXG0SBLTrmJLxd/CVRLECXcDGFaSC1TmHE0yKg4B0P2uxiy38WoOAePHaptAfHYoVqG7HcxcGc5o+IcfFfgsbQUPoYoSa213BbE78oGucTSwpJTbobFFjNgbQHdvi6g8/Z6Om+vZ8h+VxsQE7T/97UMWFvA+Og0UvIryfDIZBQ4CeXvt8a5IAhAY/RImlJWUaHrPHaolhuXFXHN+8e58qNcbomq5P6t3xG973WePLzPgnnsUG0LiP7f1zJwZzk3LisyctfSOFxOJ4lfLzYToQubxu/KpmpWBFWzInguOokrP8ql7/zDRMxLpFfUabasHwlZNnITbmbgznI6b6+3Bu7/fa2lrW5fF9Ar6jQD1hYwLLaYx5fupdi+EiGok748koa4qa010xKkKWUV2UM7kd6vB7tH9yfpnUFkLzQiZOGWnmgHO9N4oie9ok5bA4YPbkqvqNNc8/5xIuYl8tSaLOLLRXambENXF+PxNJD0ziAanVHhYaEliH1lJD/1iqD0qSsIzu2M/N550TZ3QjvYmS3rR1qDtwdhgpgwnabGMj46zRjQsxJdXYw7P1X1pY0GuaRjkMKxPah5qxuV8y6nct7l1LzVDfdyo6miHexM+ou9mblwKfdv/Y77t37HNe8fbwMQDhIxL5FOU2PZklqLJjUYdU7wWxBuN+ricBAF0KQG6pcNovZpw0fCQao/MEBcu7tSOLYHjnu7EZzbmeDczqyfNokrP8ptMXi4XDnzAJ0n72TIgn1oUoMB4VlpgIjj24I0payi9KkrqHj+Ssth2wM5c38f8p68D2nbHKRtc3h86d42A/eZHsOVMw9Y0nXKJmxDvyS1NA70z8Gz0qh5hNvbzpr6ZYMofzyiBUwLkOVdjfR/eVcao0dSl/d1aHx0GhHzEi0TXDnzAJ2mxtJpaixdp2yypM/0GLrcs5D3Y94ztNDsK7qjuxmzDBBz2rYGqZoVQc1b3dr4yfppk+g8eWeLd91aAxGPrqPbyKV0G7mUiEfXMWdz+nmQ0Jsgn1AbT/SkMXrkeZC6vK9DpU9d0S5I5bzLqf6gq6UV7WBn5q9+zDJBuEQ8us4SE6LLPQvpcs9CjmSW4ndlo1XPNBxWLiE34WbSX+wNapEBEsrfT/njERSO7WGBmDA1b3Wj9KkrSO/Xg1WjBjJl/CT+8sQ8a0BT/eGDhwN0uWchXe94ia07YkE+oSLc3gxyQt2yfiSrRg0E+YRqgRSO7UHh2B4UT7ragqmcdznFk67mp14ROO7txpTxk7AN/bLFgN1GLsU29EvrejiACdG59xQjKgu3GzVP9UwIvcmCVwYb102NmBHVBDFNVDUrgjP39yF98E0E5xox5Dcj5lsDhwOYQObg4dK59xR2RV8D4njEo/0NIEd3dkVfgy9t9HkfMTWSO6pXG63kjupF8aSrqXj+SoJzO1M573KmjJ/Eb0bM5y9PzGPBK4Mp3GKUEFvWj+Q3I+a3AOjcewp/eWKesUQ0T1mz2att7oSU9+F5EE2SqXvpSbKHdrIGNmHCoapmRVgh33LezZ3QNncyloGDnVnwyuA2IFvWj0Q+dplREzu6Wy0r9/KubVvg9pWRpPfrwZn7+1haMSHCxdSM/J4RWWufjiC9Xw/m9PgtN9w0uo1JbrhpNI0njAXTrAIbT/TEvb4LjdEj2641vqQjpPfrQfrgm1qYKHxKlz51BbmjerFj4G2WtAYwtWDKglcGG2ZoXrldu43AWDUrAmnbnLaRVZMayHvyPn7qZThoa38pfeoKap+OIDi3M6tGDeSGm0a3GTT82g03jeaGm0bj3H4d8rHLrN0I93LDpDsG3kb68si2a425hfZTrwjSB9/UBiZcM+YM6ghoyvhJpL/Ym+yFhknc67tYQVF+z3gjc3r8Fuf32zpOFTMeHXpRGDNfMYF2j+7PqlEDWTVqIOkv9rZ8SNvcCff6LlTOu9yK1Okv9mZOj9+S8ehQNKmBDhs17vxU9adeES1gwoHKH49oFyhcwhfKynmXWzOu4vkryR7aieyhnQjl7+84QzNNJGzbxN7uV1gw7WmntYZaLw2mmNdrn44ge2gnztzfx9od7zBnDa9t0pdHtgsTDhRustaaCndwEyLj0aG481PVS9r3FSUJj6eBrConMZHvnodpntrh2gkHCgcLl/TBN7G3+xXGLMlIo0LXjU7ixeoaUZIQ3C7OlTtJya8kJvJddgy8DctvWgGFaylcHPd2Y2/3K5jT47esGjWQrTtiyapy4nI6jUrvUmpfUytFRfmkZ6SxdUcs66dNYsfA2ywNtQBrJeb/dgy8jZjId/kx4YgF4fP6Ln1L3uyhhWvnSGYpOw6lEBP5LuunTWLDAw+x4YGHrAi74YGHWD9tEuunTSIm8l227ohtAyBK0i8/pNDagTVJxuf1YXf7OVfuJKvKMF16RhrpGWkcySwlJb+SrCqn1awRgjqaJP9nO0b/Zxo1v+ahS0ZqKJ9QCX5rJMyhN42aRj6h/udB5BKjiAp+i64uNrJ2M0Vs3rUiy4aU92G42X49iCYZDZjUMoX4ctFIcILfGgVU6E0LwEyCxKP98aWNxpc2GvFof+RjlyHlfdjxWnOxh93tJya5kIWHnDx2qJbnopP4NCmaYvtKC0LL6WYkQps70RA3laaUVbjzU1V7RRn2ijK8BbkWUJsM7VIAog7k8MyuPKtD1AJA/9zQQpYN9/oubFk/kpkLl7J4a0KbtrdZa/vSRrfMWS8GcSSzlGd25TH5VIjptTpR9T5SS+OMsrHZD3RHd7SDnTm1YwSzY2KsTtL46DSei07iSGZpm/tKeR8a5gnf0+vI8zfE5zAstpjptTrvifBJeeZ5LTQDkGXDtbsr0fte59mjDmaWaUyv1ZlZpvH3XJlRcQ6Grj5OTHJhy/t7VhrpwMVAog7kMCrOwcs+nZWaccak2L7S0oLpC6d2jGDJiUyWN8E6FVZqsLwJ5ruwYO5O9jFoUSIb4nPOT+/gtxf3kZjkQobFFreAaHRGGZoQbm+hhWd25fHsUQevHilgbo7bAmoNM2S/i6Grj3Mks9Tolcgn1Hb39MzHuXInw9edZrJd4z3xPISuLrYgCrf0ZOuOWKLzVFLLFDIKmlfr5EJmHMxhfoWvDczkUyELxl5RduFUUZNkIvdm8+BpkZd9eocQPyYc6XDnocpRQ+TebObmuFmptdTK5FMhBqwt4K1vMi4cWTMKnIyKczDZrvFJeWaHEBdrbVc5aphxMIflTR1rJaPA2TFI1IEc7k72tZwdYRCLtyZc6h4MMcmF7WrlwRSRAWsLiNyb3T6Iz+vjmV15jIpztIHwxN/I7JgY4svFS47CHk9DG62Y5hm4s5zx0Wntb0CnlikMiy3m06ToFpFSO9iZnSnbeGZXHkcyS8kocF6SHMksZc7m9AuaJyW/si3IltRaZsfEGNM09KZVs2bEzWV5EyzLlXn1SEG7MuNgTruy5JS73dlzd7IvPMi1BIlJLmRnyjbLJFawar7ZHi5NdrSS9jRyd7KPXlGnzQDXyjSlcYY2mk1SuKUnS05kslI7f9M9/HKgdaoh74nn/cR02NV7M9t2A9A/t/qf2uZOvB/zHvNdxk3Mm0bV+36VzK8wxHTWVutPmEbkE6q1hjQ3/yefCvGeeB7k1SPGlLsUeeubDOtnezJnczpvfZPBuXJnGEjzAqSri9FyulG4pSf3b/3OCvErNQNmxsEczpU70ST5kuWXJc9yiZXemQ3du5N9TK/VedmnW1qZm+M+v3r+gpTS42nA42nA5XRa4vE0hFd8zSDBb63cInvtAAYtSuTuZB+T7ZoFYy7tz+zK6+igQZtHRoGTyL3ZLab4M7vyGB+dxpAF+1i8NaEliLmWyNndsa+MZPi60/T/vpaJhTKT7ZqllZWaoZW3vsnA42m4IMS5cifPRScxN8fNeyK87NOZXqszsdDITa55/3i4dgVb0OPUTG2IR/vjzk9Vt6Qau5R3J/uYWCi3MJEJM2dzOkcyS80Q3WKrPia50IIIX2cmnwrxYIpIr6jTPBed1Mo0apFgpv0NcVMR3C5ESWLO5nS6fV3Ag6fFdmHmV/iYcTCHyL3ZRB3IsSRybzbP7MpjfoWvXYj+39cyZME+c7aEgTQ36smy0RA31dostrv9DF193IIJ9xcTxgSam+O2xAQwg9fMMo2JhTIPnjYgBi1KbC+RPq8REyR8iT9X7rRgWptpvssYLBwqHGB6rc7fc2ULYsh+F4MWJbLjUErH09c8ytcaxNTMCxtyGLC2oIUDT6/VO5TJdkMLJsTAneUMWpTYNotvE0eaj3rKxy6zun2t69mdKdt4fOley4lN35ls11pIOIC51D8XnWQu9xcGUQCteibyscuM5n31TKNqD5fm1H9DfA7PRScxdPVxhsUWMyy22Dq4MGS/i2GxxQxfd9oC2HEopb1WVcdtCU2Sqcv7OmTWpGbRLOV9SCh/P0GPUwvPvDIKnMQkFxK5N5s5m9N5LjqJ56KTeOubDFbvzSQlv7LN1P5FxzZ8Xp918v8SWk5WsWStLbr0a5oLHRdY/+GjPP8vtq7+0yCiJOHz+hDcLlxOJ2bzxeV0Irhdlk/9x0B8Xh9VjhoEt6s5rZTaFU1qQHC7qHLU/PpZ05EGqhw1uJxO0CVESSIlv5KoAznM2ZxufTJgzuZ0og7kkJJfaR1mcjmdVDlqflkc6ahSs1eUWdMzJrmQQYsSrYMJNy4raiHmYQWzD2IC2SvKLpa/dAzi8/qsc6cZBU6GLNjHlTMPcEtUJVMSdd45qRGdp7KxDOvDPu+c1JhxNMgtUZVcOfMAQxbss0K7vaLsQqbq+GCtCbEhPodOU2O58qNcZhwNsrMK4t0Xlp1VMONokCs/yqXT1FgrE7sATPvbJK0hblxWxDsnNWugvc7zcqFry3JlbomqbANzSdskpk9kFDjpOmWTpQnzne6sMgbbWWWYY8kpN0tOuYnOU1v8z9TcOyc1blxWRNcpmwwz6dLFjxr7vD5rY+eO13YSMS+Rh/co1iAby4wBluXKLDnl5rsCD1lVxk7FdwUelpxysyxXbvHcjWUwYb9CxLxE7nhtp7X10spELUHMMiHqQA6dJ+9k8KYaJh1u6ZRLTrnZklrb+hS3lURtSa1lySm39fyNZTAlUWfwpho6T95p1rqtS5LzICapJsmWNkbEBpiSqLMs1/gY3DsntfAuT4tDlkrYtci92bxzUmNjmaG9KYk6I2IDbbTStsBqjhma1EBKfiVdp2xiwNoCHt6jMOmwxjsnNev46KWUkaIksfCQk2W5Mu+c1Jh0WGPCfoUBawvoOmWT1d4Miy3nQczIuXpvJp2mxjJ4Uw0T9hsg09KM6fhcdBIxyYWXJM9FJzHjaJBpaTDpsAEzeFMNnabGGhVec+RtA1LlqAFd4vGley0Q8wZTEnWmpWGdWX3sUC3PHnW0K+b/n0qoZ1oaTEszfCQc5PGle0GXwv0k7PxI87S9EMjMMo35rvMdILPDbErrzlA4iOmw4SBh0/iXgUxLg8mnQvw9V2Zmmdau/D1XtpoxpiYe3qPw8B6FW6IqreOCvwpkWhqMinMwaFEi46PTfrFMXG38HLr6OHe8ttPykXZNYzrr4q0JdJoay4C1BS2cdfCmGuZsTrd6Hv/T5ozZJ7no9L1xWZE1fU0bD193unXx3GESFZNcyIb4nDazaUN8Dh6PkTy1O307CmgT9itM2K9YWnkuOumi26wTV6dZR43NXOXKj3LpPHknEY+us0DaDWiWnwCr92bSdcomBm+q4eE9ShsThTXh2jRn5mxOZ/CmmjYzZkRsgE5TY40Q33bhu/iiF66VcJjh604TuTfbUnnk3myGrzttQZgzZtJhzQrvfabHWGNccNELnz2tfSUcJjxADVhbwIC1BdYsMyOp+fyH9yhWGnAks/TS0gDTV4qK8q2NxU5TY7klqrIFTDhQ6+gZ/hwzdoSbpKgo/9LPj5hnR8yUwEwVw810MRkRG7BSRXPpLyrKv/RUsT2YI5mlLZLnEbEBK1q2lhGxASt5vuO1nZY5ioryL5TJX7icENwuioryjV1rr4+oAzkMWbDvouXEkAX7iDqQg8/rQ5MaLgZxaQWWJslWSWkWWBkFzl9UYP2PvgjFPNrj8/osM/2YcIQfE46QnpFmfL7K7SLocWpBj1Mz6+D0jLQWzzPb3b/6aI8SVnCbvXTTVOZxno6kqCjfKlPNUH4pIP9XPGz/N319UFnrf2iKLGi6LmggqCBoIOi6JuiqIqCrgqIrgqyrgoYu6JpiiK4LKgigCpquCCEdQdVVAU0VdP2iMGW29tplmtbcQNQ1QEXXNDQdQGsWHZBbvdQsKkTQfaiaBJrc/PyLPpQ2zqqbL9U10GV0TUbTZUCyQAoaJPaVinx5RmbVKZnVWRpf56r8WKlQFww2Q4bf8VdMXwsEtfkdGb97xSAb8yRG7df4zYYQ3deEsK2WsK1UsK1U6LIqxJWfKQzcEODVw0GS7KbG1F8Pout6C7WuL5Dpv1PBtlLEFgWXfyHTY61Ery91rvkiwLWfB7h6jcxV/5LoskLF9gl0+tjLI7FesuuxzKnrHeqneQdL143Bjacj6wqg4ZFUph8JYvusCdsXIldvhGvXi/T+SuS6dQrXrZO4fp3Ib76UuH5NiD6fi1z/mcgNnwa5epWMbbHG1StEvsoSjbeoq2i60h6MYNN1XTAhNF1vdlBoVFSG7/Nh+1Ti2o1Brl8v03uDyDVfN3DDVz5u+FKh15cKvdbp9FoHvT5X6PW5wjVr4LrPda6NkugTJdL1EwXbIpkVx5sdGaXZ8S9gGgNIJ6ipPHgghO3TED23h+ixTafXZpmb1ofos0ml+9dw1VcaV3wapMvKIF1WSVz+qULPzxV6faZw9Wc613yq0Xt1iN9Ehei+WMG2QObz03JHDtxsGk07P2XRmZ/hx7ZG5rqtMjdubqTHFonrNov8doPMZRvA9pmPqz8X+MNWhb/tkrg/VuGWaJXLPmmk85Imen6m0+sz6BMlcsNqP9etVujysU63jwIcrwy1N6UFm6Zrgma4KKBxrE7lyq999PnaT58dcMNWjV5bFa7d6sP2lcj/+szP6/FNHK2SqQtpSKqIKItUN2psyJH52yYXtkV+uq9UuP5fMj1XqVy9WuWGFSE6LQgxbHMQv6kVXW92B12wKZouSEjGNNMVJvwgYdugcGOsym+2q/TZqnD9dh3bVz5u3h4guVJtnpJa808zkJlBMMS7SQG6vB/gimUKvVdK9Fmu0nu5zLXLZGzvaWzLDhggmoysqwYIKoKqG+rKqVO5douP62JUfvutxg2xCn1iZTpv0rgpRuF0XQAIgRJElSUURUWWZWRZRpFlgrIKeIEg7yaC7X2FXkslei+XDVkmY1sQ4pFNDaA3hwcdNF0XbGjNZwNQWXZaxrZV5XexMjftFLnpW4ne34rYNvjZUywBQUJqEEkMoEk6oqIgySqipCCKEt6Qis8fRNEaAB+TtijYInV6Lwtx7VKRPstkIj5S6PGBRGFtwFCgApquCDYFTQANXZeZkiARsVPnlu9kfhcr0/cbiYivA4w94DM0oet4VQVJUQiJGiFRIiTKBEMSAX+QhoBIvU/C1SQCfpIKGrl8kZerFitcu0Tkuk9ErlsiYXtDYuMpYyobE0gVbIouC6DiDsgMiwtx406Z/rs0+u6WGPCNSI8tIZbnSoCCEvITkBRkWSMUkAgEJbz+EE2+IA3eAPUNjTR6fNTWSni9PuoFN/d8KtBpkcg1n3jp82GQ3h/6sc33seAHb/P6pYOmCTY0VQCNEkHhrgMhfrdL5k/fafT/XqT/boU+sRI/2r0AhESFYFDCF1TwBSWa/CE8TQHcjQFcDX6cdQGq63w43PWU1AoEmup4emMjtvl+enzop/d7Aa57N4Btvo/Z37jCHBzBpuqaAHDOHWDo/iD99in8+XuZO/er/H6fxsB/h0irDgGqoYGAhOAL0eALUd/oo87TRK2nCUddI3anQKXTTUl1DUVVNXga6nh2mwvb6066L3Bz3btOekU2YXtd5MVNDmuVVtEFm6brAmiUu4OMPODnjv0idx+UGHpQ4q6DEnf928+h0iCg0egN0OgXqW8MUCd4cXm81LgbqHIJlDs9lFd5KK90U1hWQ3GlgLOqlrs/rsQ2q45rFjq57q0yukc6sL3iYc62akBDR0fRNcGmq5oAQYSAyuQEibsPhnjgkMYD8T4ePOTnv/ZrfJrtBTWE4A3ibvRTJ3hx1jdRXddApbOeMoebEruL3Ao3p8vqOVVSR1JuDZkFtbywvgDb0zl0eqmanm+Wct2bFdhmlvP2Po/hH6qIrmiCTdNUAVVElTVeyfTz10My435UGHNE5JGfJIYf1ZiV4kFo8uILBKirD+LwBHC43Dhq6ymurqfAUU9ORS05RSU0NHmQVRW/JCMqOho6354U6DEri04z8+nxWim2fxSx8ZgLEAlJCqoiCzZZUwVZVECDjUVNDD8s8sRRlSmJOs8mwbPHZJ466iO2yI8aDNJU56a8tpGqaicOZx2FVfWcLa8lq7CMBn8IHfAGZQKSik/SQAoBOjEZtdiezqTTS/l0fzmPrFIBNB9CUCcoSYJN0TTBKymgS5TXBXn8pwCTj8lMT1WZmarx+nGR2Rk680+GOFleh9/bgMtZR3V1HYWVLvJKajiTV0pVjRsV8IVEgrLaLApeERSCAAx5/xS2+48zZvlZAmKIQFMTHq+PppAi2DRdFQIyyKIfRImoMz6ePO7lpUyJl08r/PN0iMjTEh9kS6zNEUgp92GvaaDAXstZh4DLG0JoChAMyviDMn5Jxi/K+EISIVnFr0h4JWPZ33a8mNteSCI6vhpZbMDhaqChyYfHHxRsmhYURBECoRDoMvkukVfTFN7IlHk7W+aDXIlVOSHW5ob4qhi2F4v8WNLIiSov5wLgkVVERSUYMqa2LyTjF1UCkkpQ1vGLImJAxCsai2SdKFJQ6aG0ooqK+gBuVxOCTxBsuq4IkqQSFCVCkgyqzg8lXt5J9/H+WViVJ7G+KMSOEoVdJSp77DJxdRrH3Rq5goLDJyMERRqCIt6QbPiHqBAQFSRJJSCrhGSZJklDUs/nIefsNRRXe3DWefE0NjUf21BURFEiGDRWVH9I5Nu8Rt7Pk/lXocbWIpFvKzT2VSr8YJdIcEqk1Svke2TsPhV3SMYTEmkISngDCr6QTFBSCUkqQUnFL2kEJUNLflFF1aGuyUepow6HuxG34DdyVkVRkCQFUVLxBWR0ScEfFPmuuIG1hTIxpSr/rpA46FBIqJHJdGmcqVPJa1Co9MrUBiTcQQlPQKYhoNAUUvCJCn5JJSApBCTZEr8oEVJU/IpKiaOOmnov9Q1+QyOqqiErGqKiIYk6/mAATQ4QalRItPvZU+EnvkrmxxqJRJdIVp1KTr1GQaNChVei2idTE9BwBRTqAzKeoEyjKNMkKvglhaCkNAMZogAeX4DS6npcDQE8jYHmM0aajqLqyLJOSNbwSTJev0woEKCxyU9OdZCEkgAJ1UGSBYWsBo3cRihq0qj0KVT5ZBwBjdqQRn1IRhBVGiWVRlklqOiIikZQ1hAV4ytjJE2n0ummqt6LU/AjNAYEm64jaBqoqo6iaEiKhiirBESVhkAQr9eH0ChSUu3nVGkdGY4mUmt8ZLoC5DWoFDUplHpVKnw6VT6ZWn+IuqCEJ6TQEFINzUgSflXFJ8nUe304XALVdQ3UNwaob/TT5A0ZILoO4TCyrBKSZHxBGcEfxNPgpdETwO32U+ZoIKesnrPlHrLtbn6urCfPXk+B3U2R3cO5qgbOVTVQUilwrkKgtEqguLKe4sp6yhwNlNg9VLkEhKYgjd4QTX6RYFA+X2Dpuo6maaiqiqqqKIqGKKn4QwrekERjIIC70YenMUBjk0S9EKK23our3ovb48Xj8SI0BfD4ROq9IdyNQeoa/Lg8AZxuPzV1PuobRASfguAN0egP4Q1KBEMykqwKNkAxMnpDNM1oSxhQGrKiI6oqTapIkyTiDYUIiDLBkEwoICOGjHghKxqKqqCoEooqEVJFgkqIkBIiKIsEpBB+MYA/FMAXkgiICiHRmK2KoilWo6bZRIKu61bjRdd1QdEQVBVBkzRBlVRBFhVBVTRBUXRBknVB1hAUECQQNF0XUHVB13RB0XRBVDRBUjRBUTVBUlRBlBRBlGQhJGuCJOuCouiCpuqCqqpl/7Eemqor5HnS2Ja/hPezpvCP1PuYlfo3vvo5EnfA0baH9qs+CKZpBIIh7DUuyuw1lNprqHDU4mnwoqoamq5xyn2YVTkv8cKJO3n+TH+eTB7Ao/H9eSr+TnbmrfyfgdiddZzKKaK0yklhuYN6oWVfvabay+6Tu3gzaSJPpPZm9E9XMmnvH1n60wKSanZypuEg35WuZlrCMLb9vPSXgzicdWTkFLX7vya5Dq/spk62s8v1AW+cu53ns29kSd6z/Fi9mZ/L8tpqVFfZeHYxBe7MSwdJy85v8Xd1oJwDFRtZlTeTD88+wcKsMSzMGsv8rL8wNbMnc7LuJN6xg6AcsF6TW1xBkzfQct9P8pDrSkfT1QuDKKrKz8UV1t+V3kKi89/m1YyhvHlyMPOz/ouFZ4fwYe59fJAzjLfO3s66wuep8p7jbF0iUTkzOe76/rzZ6jxUVteGtch06gL2C4PIikJFtcv6e3/ZeuamDOHNU//NivwxfFY8jnXlE/iyYiKflz/Eh4WD2Gv/CL/YQIJjI2+dvJvXTt7FtJS+LPt5OvVBY383KEoUlFaGzSz5wqb5ubC0WSsyG3PfZUbKnXzw8wOsKX6EdWUT+NI+nq8cY1nrGMnikjuJd0Xhld1sr3iTt37+IyuLHmZN0WMszxnPzLSBvJnxMMWNPxv7vUITLrdw8VlzMswnNud+xD+O3cGy3LF8ce5R1pZN4IuKsXzlGM0X1SP4uPJ2jgpraJAcfFb+CJHnbuOz8pF8UT6OL0om8nnRJFblPcrLaXfxxolROHzGd2idq7xIHBEavTQFQwAcLNvMP5Lu5JOcsawpmsRnJROIKnuYtVWjWVP9Vz6q7McRz0pUTSa2Zh6LSgeytOJPfGa/j3UVY1lTMoFPz01kdcEjLM95hNmp/8UHmU+j6MYnlrJyz3UMknHW0IbDW8rLyfexIGs4nxU8zqqi8Xx07gGiKkfyheN+ltnvJEFYGdYOFWlUqjniWcGK8iFElQ1jTek4Pi2awOqCR1iZ9wgfnx3Hs4l9+aHc+BqH2voGRFFqC+JpaEKSjOR2Y84iZqX8majcx1ieN57Xc+/hvXPD+aziAZaX30VGY0yH0/1s00E+KR7KquL7+ezceFbnT2BFzkSW5Uzg7VP38UbKQ3hCdc1aKWoLktHsG06/nbnJ9/H+6VGsyJnIC9l38kreMNaUPsKSkkHsdy26aABMcK3lw4L/5l9FY1mdP56lOeP55Ox4Psh+mOeT7+BAyUZj17O8qiWIKMkUlNoBOFQaw4zkQSw+M5bZp+7in7mPsKnkFVade4DPSsfTJNVeFCSk+lhbPIVl+Q+wMnccS8+OY/GZsXxwZjTTj9/OkqwXACi3O/H5A+dBKhy1lFQac33t2bf5R/KdvJnxFxadnkSyYzuf5j3BssIR/Kt4DBvLp/NF2dOsqXiSNRVPsKbyCeNnxZN8XjaFz4ufJrr4Bf5V8Agr8h5iWc5YPs4ey4enR/P+6YeYnfZn3kh9CAUfqgz2Gtd5kLOFpZTYjUMHH516jmlJA3jjxHCO2XexteBtFpwZyqqC0awo+huLCv7Eu4W38V7x73mvtD/vl/Xl/bJ+vFfye94tuo2F+X/g3dw/szT/b6zIHcMnZ0fz0ZmHWXT6ISKzRvJq5mBeSh5MSeNZyzyyrBggWTlFlNsNssiMKYz9oQe7i/9FmmM/r6bezZKfx7Is5yGW5f+NFYUjWHXuflaXDmN12V+JKhtGVNkwVpX9lZXFw1lRNILl+Q/ySc6DfHRmFIuyRhF5ciRvZ/6NNzPvZ3baIJ5N+AM/1xsfXcg9V47XH2wLMidpFE/9eAcVQg7Lsp7j9fShfHTmIT4+M4rIrKG8ljGAeSf78eaZfszP7sc/z/bln9n9mH+mH29m9eO1jP7MPfF7ZibfxvSE3zP1UD+eiruVxw/cxIT9fRj+764Mje3M6bqjAOQVl+MPhgyQvHPllFQapnkhfgRf5y7haNV3PJvwe945+QDvnnyAf2bcQ0zR22S7fySzbj+Z7n2cdO/jZP1eTtbvI9O9j8y6fWS49pHm3Edq9T6OV+0luXIPRyt2k1C+i/jybzhYupUfSrfjV40wX1zhQNN0A8RR66bEbjjr5p+Xc9IRz9snJvJ88h94O/N+3s64j1dS7mJLXuT/v0e/vT6qa93nnVXXdXLOlRtJi6qSWLmL8Yd682rGvcxLG8qbJ4byRuoQXj56L+UNuRcdoDHk5kDJNvaXbuZA2Rb2l21hX9nX7C3byNaCKJKr4pqnbw3+QLBlQDttxn4dPsh4hseP3sjcjP/m5dRBvJYymNdTBjMtvh8rT865KMja0wsZvqsr4/f3ZNyBnjx88CpGxV3BiAM2bt5iY8PPKwz/KKlsG1lDooTgCRJAYPKR/jyb2pcZaQOZdfyPzDn+J145/l/MSfojU364lW05yzuE2F30FU/80JcZSQN5+fifmH38Tmam3MGM1Dt4LOE6pv90DyHFCGLZ+SXtL3pn88rJCR5hbPy1TEq6jqnJv2XGsduZdfwPzD52By8n/5FZSX9g8sGbeDflGU7VHMUTqKMhVM/Z2hMsSZ/JY3G38I/E25l77I/MOv4HZhy/nRkptzE1+Rbu+beNhMrvjLEKSi+cj0T+8AaPZfTi2eQ/8Gj89fz96C3MSB7AjOTfMzPpNmYn3c7MowN4/IdrmXKoPy8l3MtLP/2Fpw7fxiMHr+HFxH7MTrqNmUm/56XkAbyY3I/pyb/jr/tsRJ542hqnOGydaRdkxv6J/DXBxvflX/Fd0Rru2W3jmYTrmZnUnxlJ/ZhxtB+zjg5g1tH+vJBwM1Pjr+fZ+Ot5PuFmZiX2Y9ZR43kvJfXlpeR+PJ90M3/da2Nm4gME5MZ2c5F2QV5OeYA/7rZxrOYgANE/f8S933ViTFxXZiX1ZfbRvsxK7MusxFuZnXgrs8JkZuKtzEi8lZlHf8espL48Gd+Lu3fbeDVpLA1BY+kvc7T7ZTktQUQlyLQjg/nzv20cyo+zrsdX7OKR/bcybLeNp368hpd+uok5ib9lbuKtzfI75ib+jtmJv2PGT7fwfMJveOj7zty/O4JPs+YjKsYUdTc04Wloav/YRusLz/04lAeTIsgsPENewfnc0is1EH32Qx47MICH913F+O//F+O/t/H4wW7877gIHtnfhXHfd2Hs91cyZl9v3k19lgLPaev15TV1NDR6Oz4/0vrC26ceYVhcL45X/GB4d2Eljf7Q+cJI9pHqiGPVqVeZd+wRZicOZ0bCvbyS9DAfpD3PnnNfUuO3ny9NVI2T+eVI8oVPGrUB2ZsfzX1HehJTtMK6FgyJZOYW0+gXf1EIz8wro9LhvKTn2lrugkMoFOS5n/7C0APXYK8tb3GepMrh5HB8Cmknz5JbXEpBSQVlFbVU2N0UlVWRW1RK1s95/JCQzMkzPyPLMpqm4ff7CQQChEIhJElCURQ0TcPsVOm6fn6tCT+oUOkq4bGE27n/qzv4KeMIwVCQQCBAbV0ttXW1VFRWkJ19lrS0DJKSj5F4NInk5OOcPHmK/Px8amtrcbvd1NTU4HQ6cbvdNDU1WTCyLKOqaguYDmvfgNzE4bIYdpWv4UT5EezuMkQl9B877PT/DQC7cLwx8LR3hQAAAABJRU5ErkJggg==) no-repeat;padding-left:40px}
-        .browser .browser-firefox{background-position:0 -34px}
-        .browser .browser-ie{background-position:0 -68px;margin-left:0px}
-        .browser .browser-360{background-position:0 -170px;margin-left: -27px}
-    </style>
-</head>
-<body style="margin-top:50px">
-<h1>璇峰崌绾ф偍鐨勬祻瑙堝櫒锛屼互渚挎垜浠洿濂界殑涓烘偍鎻愪緵鏈嶅姟锛�</h1>
-<p>鎮ㄦ鍦ㄤ娇鐢� Internet Explorer 鐨勬棭鏈熺増鏈紙IE11浠ヤ笅鐗堟湰鎴栦娇鐢ㄨ鍐呮牳鐨勬祻瑙堝櫒锛夈�傝繖鎰忓懗鐫�鍦ㄥ崌绾ф祻瑙堝櫒鍓嶏紝鎮ㄥ皢鏃犳硶璁块棶姝ょ綉绔欍��</p>
-<hr>
-<h2>璇锋敞鎰忥細寰蒋鍏徃瀵筗indows XP 鍙� Internet Explorer 鏃╂湡鐗堟湰鐨勬敮鎸佸凡缁忕粨鏉�</h2>
-<p>鑷� 2016 骞� 1 鏈� 12 鏃ヨ捣锛孧icrosoft 涓嶅啀涓� IE 11 浠ヤ笅鐗堟湰鎻愪緵鐩稿簲鏀寔鍜屾洿鏂般�傛病鏈夊叧閿殑娴忚鍣ㄥ畨鍏ㄦ洿鏂帮紝鎮ㄧ殑鐢佃剳鍙兘鏄撳彈鏈夊鐥呮瘨銆侀棿璋嶈蒋浠跺拰鍏朵粬鎭舵剰杞欢鐨勬敾鍑伙紝瀹冧滑鍙互绐冨彇鎴栨崯瀹虫偍鐨勪笟鍔℃暟鎹拰淇℃伅銆傝鍙傞槄 <a href="https://www.microsoft.com/zh-cn/WindowsForBusiness/End-of-IE-support">寰蒋瀵� Internet Explorer 鏃╂湡鐗堟湰鐨勬敮鎸佸皢浜� 2016 骞� 1 鏈� 12 鏃ョ粨鏉熺殑璇存槑</a> 銆�</p>
-<hr>
-<h2>鎮ㄥ彲浠ラ�夋嫨鏇村厛杩涚殑娴忚鍣�</h2>
-<p>鎺ㄨ崘浣跨敤浠ヤ笅娴忚鍣ㄧ殑鏈�鏂扮増鏈�傚鏋滄偍鐨勭數鑴戝凡鏈変互涓嬫祻瑙堝櫒鐨勬渶鏂扮増鏈垯鐩存帴浣跨敤璇ユ祻瑙堝櫒璁块棶鍗冲彲銆�</p>
-<ul class="browser">
-    <li class="browser-chrome"><a href="https://www.google.cn/chrome/browser/desktop/index.html?hl=zh-CN&standalone=1"> 璋锋瓕娴忚鍣�<span>Google Chrome</span></a></li>
-    <li class="browser-firefox"><a href="https://www.mozilla.org/zh-CN/firefox/new/"> 鐏嫄娴忚鍣�<span>Mozilla Firefox</span></a></li>
-    <li class="browser-ie"><a href="https://windows.microsoft.com/zh-cn/internet-explorer/download-ie"> IE 11 娴忚鍣�<span>Internet Explorer</span></a></li>
-    <li class="browser-360"><a href="http://se.360.cn/"> 360瀹夊叏娴忚鍣�<span>360 Chrome</span></a></li>
-    <div class="clean"></div>
-</ul>
-<hr>
-</body>
-</html>
\ No newline at end of file
+	<head>
+		<meta charset="UTF-8" />
+		<title>璇峰崌绾ф偍鐨勬祻瑙堝櫒</title>
+		<meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1" />
+		<meta name="renderer" content="webkit" />
+		<base target="_blank" />
+		<style type="text/css">
+			html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video{border:0;font-size:100%;font:inherit;vertical-align:baseline;margin:0;padding:0}article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section{display:block}body{line-height:1}ol,ul{list-style:none}blockquote,q{quotes:none}blockquote:before,blockquote:after,q:before,q:after{content:none}table{border-collapse:collapse;border-spacing:0}
+			a{text-decoration:none;color:#0072c6;}a:hover{text-decoration:none;color:#004d8c;}
+			body{width:960px;margin:0 auto;padding:10px;font-size:14px;line-height:24px;color:#454545;font-family:'Microsoft YaHei UI','Microsoft YaHei',DengXian,SimSun,'Segoe UI',Tahoma,Helvetica,sans-serif;overflow-y:scroll}
+			h1{font-size:40px;line-height:80px;font-weight:100;margin-bottom:10px;}
+			h2{font-size:20px;line-height:25px;font-weight:100;margin:10px 0;}
+			em{color:red}
+			p{margin-bottom:10px;}
+			hr{margin:20px 0;border:0;border-top:1px solid #dadada}
+			span{display:block;font-size:12px;line-height:12px;}
+			.clean{clear:both;}
+			.browser{padding:10px 10px;}
+			.browser li{width:auto;padding:0 80px;margin-top:30px;height:34px;line-height:22px;float:left;list-style:none;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACIAAADMCAYAAAAWCXEwAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKTWlDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVN3WJP3Fj7f92UPVkLY8LGXbIEAIiOsCMgQWaIQkgBhhBASQMWFiApWFBURnEhVxILVCkidiOKgKLhnQYqIWotVXDjuH9yntX167+3t+9f7vOec5/zOec8PgBESJpHmomoAOVKFPDrYH49PSMTJvYACFUjgBCAQ5svCZwXFAADwA3l4fnSwP/wBr28AAgBw1S4kEsfh/4O6UCZXACCRAOAiEucLAZBSAMguVMgUAMgYALBTs2QKAJQAAGx5fEIiAKoNAOz0ST4FANipk9wXANiiHKkIAI0BAJkoRyQCQLsAYFWBUiwCwMIAoKxAIi4EwK4BgFm2MkcCgL0FAHaOWJAPQGAAgJlCLMwAIDgCAEMeE80DIEwDoDDSv+CpX3CFuEgBAMDLlc2XS9IzFLiV0Bp38vDg4iHiwmyxQmEXKRBmCeQinJebIxNI5wNMzgwAABr50cH+OD+Q5+bk4eZm52zv9MWi/mvwbyI+IfHf/ryMAgQAEE7P79pf5eXWA3DHAbB1v2upWwDaVgBo3/ldM9sJoFoK0Hr5i3k4/EAenqFQyDwdHAoLC+0lYqG9MOOLPv8z4W/gi372/EAe/tt68ABxmkCZrcCjg/1xYW52rlKO58sEQjFu9+cj/seFf/2OKdHiNLFcLBWK8ViJuFAiTcd5uVKRRCHJleIS6X8y8R+W/QmTdw0ArIZPwE62B7XLbMB+7gECiw5Y0nYAQH7zLYwaC5EAEGc0Mnn3AACTv/mPQCsBAM2XpOMAALzoGFyolBdMxggAAESggSqwQQcMwRSswA6cwR28wBcCYQZEQAwkwDwQQgbkgBwKoRiWQRlUwDrYBLWwAxqgEZrhELTBMTgN5+ASXIHrcBcGYBiewhi8hgkEQcgIE2EhOogRYo7YIs4IF5mOBCJhSDSSgKQg6YgUUSLFyHKkAqlCapFdSCPyLXIUOY1cQPqQ28ggMor8irxHMZSBslED1AJ1QLmoHxqKxqBz0XQ0D12AlqJr0Rq0Hj2AtqKn0UvodXQAfYqOY4DRMQ5mjNlhXIyHRWCJWBomxxZj5Vg1Vo81Yx1YN3YVG8CeYe8IJAKLgBPsCF6EEMJsgpCQR1hMWEOoJewjtBK6CFcJg4Qxwicik6hPtCV6EvnEeGI6sZBYRqwm7iEeIZ4lXicOE1+TSCQOyZLkTgohJZAySQtJa0jbSC2kU6Q+0hBpnEwm65Btyd7kCLKArCCXkbeQD5BPkvvJw+S3FDrFiOJMCaIkUqSUEko1ZT/lBKWfMkKZoKpRzame1AiqiDqfWkltoHZQL1OHqRM0dZolzZsWQ8ukLaPV0JppZ2n3aC/pdLoJ3YMeRZfQl9Jr6Afp5+mD9HcMDYYNg8dIYigZaxl7GacYtxkvmUymBdOXmchUMNcyG5lnmA+Yb1VYKvYqfBWRyhKVOpVWlX6V56pUVXNVP9V5qgtUq1UPq15WfaZGVbNQ46kJ1Bar1akdVbupNq7OUndSj1DPUV+jvl/9gvpjDbKGhUaghkijVGO3xhmNIRbGMmXxWELWclYD6yxrmE1iW7L57Ex2Bfsbdi97TFNDc6pmrGaRZp3mcc0BDsax4PA52ZxKziHODc57LQMtPy2x1mqtZq1+rTfaetq+2mLtcu0W7eva73VwnUCdLJ31Om0693UJuja6UbqFutt1z+o+02PreekJ9cr1Dund0Uf1bfSj9Rfq79bv0R83MDQINpAZbDE4Y/DMkGPoa5hpuNHwhOGoEctoupHEaKPRSaMnuCbuh2fjNXgXPmasbxxirDTeZdxrPGFiaTLbpMSkxeS+Kc2Ua5pmutG003TMzMgs3KzYrMnsjjnVnGueYb7ZvNv8jYWlRZzFSos2i8eW2pZ8ywWWTZb3rJhWPlZ5VvVW16xJ1lzrLOtt1ldsUBtXmwybOpvLtqitm63Edptt3xTiFI8p0in1U27aMez87ArsmuwG7Tn2YfYl9m32zx3MHBId1jt0O3xydHXMdmxwvOuk4TTDqcSpw+lXZxtnoXOd8zUXpkuQyxKXdpcXU22niqdun3rLleUa7rrStdP1o5u7m9yt2W3U3cw9xX2r+00umxvJXcM970H08PdY4nHM452nm6fC85DnL152Xlle+70eT7OcJp7WMG3I28Rb4L3Le2A6Pj1l+s7pAz7GPgKfep+Hvqa+It89viN+1n6Zfgf8nvs7+sv9j/i/4XnyFvFOBWABwQHlAb2BGoGzA2sDHwSZBKUHNQWNBbsGLww+FUIMCQ1ZH3KTb8AX8hv5YzPcZyya0RXKCJ0VWhv6MMwmTB7WEY6GzwjfEH5vpvlM6cy2CIjgR2yIuB9pGZkX+X0UKSoyqi7qUbRTdHF09yzWrORZ+2e9jvGPqYy5O9tqtnJ2Z6xqbFJsY+ybuIC4qriBeIf4RfGXEnQTJAntieTE2MQ9ieNzAudsmjOc5JpUlnRjruXcorkX5unOy553PFk1WZB8OIWYEpeyP+WDIEJQLxhP5aduTR0T8oSbhU9FvqKNolGxt7hKPJLmnVaV9jjdO31D+miGT0Z1xjMJT1IreZEZkrkj801WRNberM/ZcdktOZSclJyjUg1plrQr1zC3KLdPZisrkw3keeZtyhuTh8r35CP5c/PbFWyFTNGjtFKuUA4WTC+oK3hbGFt4uEi9SFrUM99m/ur5IwuCFny9kLBQuLCz2Lh4WfHgIr9FuxYji1MXdy4xXVK6ZHhp8NJ9y2jLspb9UOJYUlXyannc8o5Sg9KlpUMrglc0lamUycturvRauWMVYZVkVe9ql9VbVn8qF5VfrHCsqK74sEa45uJXTl/VfPV5bdra3kq3yu3rSOuk626s91m/r0q9akHV0IbwDa0b8Y3lG19tSt50oXpq9Y7NtM3KzQM1YTXtW8y2rNvyoTaj9nqdf13LVv2tq7e+2Sba1r/dd3vzDoMdFTve75TsvLUreFdrvUV99W7S7oLdjxpiG7q/5n7duEd3T8Wej3ulewf2Re/ranRvbNyvv7+yCW1SNo0eSDpw5ZuAb9qb7Zp3tXBaKg7CQeXBJ9+mfHvjUOihzsPcw83fmX+39QjrSHkr0jq/dawto22gPaG97+iMo50dXh1Hvrf/fu8x42N1xzWPV56gnSg98fnkgpPjp2Snnp1OPz3Umdx590z8mWtdUV29Z0PPnj8XdO5Mt1/3yfPe549d8Lxw9CL3Ytslt0utPa49R35w/eFIr1tv62X3y+1XPK509E3rO9Hv03/6asDVc9f41y5dn3m978bsG7duJt0cuCW69fh29u0XdwruTNxdeo94r/y+2v3qB/oP6n+0/rFlwG3g+GDAYM/DWQ/vDgmHnv6U/9OH4dJHzEfVI0YjjY+dHx8bDRq98mTOk+GnsqcTz8p+Vv9563Or59/94vtLz1j82PAL+YvPv655qfNy76uprzrHI8cfvM55PfGm/K3O233vuO+638e9H5ko/ED+UPPR+mPHp9BP9z7nfP78L/eE8/sl0p8zAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAC7ESURBVHja5Lx5dFRV1rBfgHwYRQQVtB26ZWhtabtfeUGxGxFbUGZF8RMHGkVbRkekVYiKisicVhE0gEwBokgDAhEMMSSQkAECwcxkrlRSqVTqJqnxzs/vj5t7qUyAvr9e37fWV2vtleSm6p6n9t5nn733OVU2RaUaEP5PiqJSbeMXPBTA5/Xhzk9Vnd9vo3HFx21E2LYJX9IRgh6npvyCe9uaqS4K4C3IpXHFx9S99CTuJ8Z0KLVjRlA7ZgTuJ8ZgXxmJL+kIlwAkXBQk6HFq9pWRVA8fSvXwodYgdS892a6EA1UNvouqwXdR99KTeAtyfz2IL+kI1cOHYh9wqwVwKWJqpXbMCOv19gG3Imzb1JF2OgZxfr/NukH4jcNVfyEAE8IU+4BbKet1PfaVke3BtA/i/H6b8aIBt7a4mWmaC0nr55vmqRp8F5V33Mm5LhHtwbQF8SUdsSDCb1I1+K42g1xIWgOYYh9wK+e6RCBs29QxSIWus37aJM51iWjx4so77mwD1d5AHQ1eecedlN9yuyVlva6nrNf14Q7cEmRn4W7u3T2E9ME3UX7L7W1uZg5Weced1s3sA2613ql5LXzQjuRclwjcT4wxTXQeRHC7GLdnHPeensiCVwa3e0PznZk3EbZtwluQa0kofz8NcVNxr++Ce30XnNuv61Bcu7viXt8Fvyu7JYipjfGHxzD+8Bh2j+7fAiZcC+Y0zPDIbCyD6DyV6DyVeDcIQR2C39J4oieNJ3oSOnkVcnZ35Ozu6MVdDHF0N6S4C43OqJYg/0ydzb27hzDx0FjuPT2R+asfa6OVsl7X40s6QoWus/CQk6fWZPHChhxe3lbMCxtyrN9TyxSQSwidvMoC0XK6tRGybPjSRmOuNUKVo4Zxe8YxIu4+Jh4ay/jDY7j39MQWWjnXJYLGFR9Toes8tSaLiavTrIHDxfxfapkCwW8hy9YuhCmhk1fR1FRnaCS1NM4yy8RDYy2tjIkZRXq/HtYsCnqc2sJDTkYsTrU00J6YkEJQR7M/eEGY0MmrcOenqjZA2JmyzTJLuJiOe65LBHUvPUmGR2bE4lQmrk7jqTVZHcrE1WkMWpRIdJ4KnpUXBCHLRl3e16EWIOEaMU00/vAY9na/gsYVH/NdgYe+8w9bMBeSQYsSWXjICcFvL2ga+dhlFwcJ10rjio/ZklprgbSWiavTWvzdd/5hXt5W/OtATC201sq9u4eQ+PVijmSW0nf+YQYtSmTQosR2gUYsTmXQokT6zj9saeRCpmkJ0hxD2gOZeGgsI+Lu45+ps7FXlFmDmDDtSd/5h+k7/zCpZQpa9cwOQciyIR+77LyzFhXlMyZmFOP2jLP8orVWRsTdR2ppHFtSa+k6ZZM1WHvSdcomwyxySceayO4OWTY88TdirygzUkWf18eL2//RQiutYcwYE/Q4tagDOUQ8uo6uUzbRZ3qMJV2nbCLi0XU8tSbrolNXzu6OfOyylgEN4NOkaO5acw/j9ozr0ET37h5imehIZimPL91rAfSZHsOQBfuISS7E7vaTETeX0MmrOoQInbwK+dhlNKWsahni0zPSuGvNPW1M1BrI1NrOwt0WkCn2ijJSS+MYt2ccuQk3oxd36RCi8URPY+HLT1VbgGiSzPsx71laCddMe2Yygf6ZOtuScXvG0XfJn/n8YL+LQnjibyQ34WZ8Xl/bfKSoKL+FVi4EYwKZcu/uIQzaPoExMaPQcrq1ADFX33AI1+6u1OV9HVI6ShU/TYqm75I/dwjTHtDEQ2MZt2ccg7ZPaGGScIDWEBlxc42UoSMQ00StYdoDCgcbtH0Cbx+8p40ZTIBwiFM7RmB3+y+exZvT2YRpDdR6ZoVrw1xRWwN44m/Euf06A6Ki7NLrmnDNmH7TEdSg7RP4/GA/yLK1GdwEKNzSk1M7RlDlqPl1JefOlG2MXTGmXaAxMaMsB/XE34h4tH+7ANlrB7T2iV8OAlDlqOH9mPcsIBPKlF3R16Ad7GwlxoVberYAKCrKv1ghfmkg5sPldLIzZVsLqLErxpC9doAlp3aMICNurlGyVpRdSAu/HqS1Q58rd1JUlI87P1UtKsrHXlGG3e1HCOoov+x2wiX3RxT+o49L1IgutXxVUCfDIxNfLraQDI+M3e3/NdCXbhohqBNfLrIsVzZqmoT6dmXG0SBLTrmJLxd/CVRLECXcDGFaSC1TmHE0yKg4B0P2uxiy38WoOAePHaptAfHYoVqG7HcxcGc5o+IcfFfgsbQUPoYoSa213BbE78oGucTSwpJTbobFFjNgbQHdvi6g8/Z6Om+vZ8h+VxsQE7T/97UMWFvA+Og0UvIryfDIZBQ4CeXvt8a5IAhAY/RImlJWUaHrPHaolhuXFXHN+8e58qNcbomq5P6t3xG973WePLzPgnnsUG0LiP7f1zJwZzk3LisyctfSOFxOJ4lfLzYToQubxu/KpmpWBFWzInguOokrP8ql7/zDRMxLpFfUabasHwlZNnITbmbgznI6b6+3Bu7/fa2lrW5fF9Ar6jQD1hYwLLaYx5fupdi+EiGok748koa4qa010xKkKWUV2UM7kd6vB7tH9yfpnUFkLzQiZOGWnmgHO9N4oie9ok5bA4YPbkqvqNNc8/5xIuYl8tSaLOLLRXambENXF+PxNJD0ziAanVHhYaEliH1lJD/1iqD0qSsIzu2M/N550TZ3QjvYmS3rR1qDtwdhgpgwnabGMj46zRjQsxJdXYw7P1X1pY0GuaRjkMKxPah5qxuV8y6nct7l1LzVDfdyo6miHexM+ou9mblwKfdv/Y77t37HNe8fbwMQDhIxL5FOU2PZklqLJjUYdU7wWxBuN+ricBAF0KQG6pcNovZpw0fCQao/MEBcu7tSOLYHjnu7EZzbmeDczqyfNokrP8ptMXi4XDnzAJ0n72TIgn1oUoMB4VlpgIjj24I0payi9KkrqHj+Ssth2wM5c38f8p68D2nbHKRtc3h86d42A/eZHsOVMw9Y0nXKJmxDvyS1NA70z8Gz0qh5hNvbzpr6ZYMofzyiBUwLkOVdjfR/eVcao0dSl/d1aHx0GhHzEi0TXDnzAJ2mxtJpaixdp2yypM/0GLrcs5D3Y94ztNDsK7qjuxmzDBBz2rYGqZoVQc1b3dr4yfppk+g8eWeLd91aAxGPrqPbyKV0G7mUiEfXMWdz+nmQ0Jsgn1AbT/SkMXrkeZC6vK9DpU9d0S5I5bzLqf6gq6UV7WBn5q9+zDJBuEQ8us4SE6LLPQvpcs9CjmSW4ndlo1XPNBxWLiE34WbSX+wNapEBEsrfT/njERSO7WGBmDA1b3Wj9KkrSO/Xg1WjBjJl/CT+8sQ8a0BT/eGDhwN0uWchXe94ia07YkE+oSLc3gxyQt2yfiSrRg0E+YRqgRSO7UHh2B4UT7ragqmcdznFk67mp14ROO7txpTxk7AN/bLFgN1GLsU29EvrejiACdG59xQjKgu3GzVP9UwIvcmCVwYb102NmBHVBDFNVDUrgjP39yF98E0E5xox5Dcj5lsDhwOYQObg4dK59xR2RV8D4njEo/0NIEd3dkVfgy9t9HkfMTWSO6pXG63kjupF8aSrqXj+SoJzO1M573KmjJ/Eb0bM5y9PzGPBK4Mp3GKUEFvWj+Q3I+a3AOjcewp/eWKesUQ0T1mz2att7oSU9+F5EE2SqXvpSbKHdrIGNmHCoapmRVgh33LezZ3QNncyloGDnVnwyuA2IFvWj0Q+dplREzu6Wy0r9/KubVvg9pWRpPfrwZn7+1haMSHCxdSM/J4RWWufjiC9Xw/m9PgtN9w0uo1JbrhpNI0njAXTrAIbT/TEvb4LjdEj2641vqQjpPfrQfrgm1qYKHxKlz51BbmjerFj4G2WtAYwtWDKglcGG2ZoXrldu43AWDUrAmnbnLaRVZMayHvyPn7qZThoa38pfeoKap+OIDi3M6tGDeSGm0a3GTT82g03jeaGm0bj3H4d8rHLrN0I93LDpDsG3kb68si2a425hfZTrwjSB9/UBiZcM+YM6ghoyvhJpL/Ym+yFhknc67tYQVF+z3gjc3r8Fuf32zpOFTMeHXpRGDNfMYF2j+7PqlEDWTVqIOkv9rZ8SNvcCff6LlTOu9yK1Okv9mZOj9+S8ehQNKmBDhs17vxU9adeES1gwoHKH49oFyhcwhfKynmXWzOu4vkryR7aieyhnQjl7+84QzNNJGzbxN7uV1gw7WmntYZaLw2mmNdrn44ge2gnztzfx9od7zBnDa9t0pdHtgsTDhRustaaCndwEyLj0aG481PVS9r3FSUJj6eBrConMZHvnodpntrh2gkHCgcLl/TBN7G3+xXGLMlIo0LXjU7ixeoaUZIQ3C7OlTtJya8kJvJddgy8DctvWgGFaylcHPd2Y2/3K5jT47esGjWQrTtiyapy4nI6jUrvUmpfUytFRfmkZ6SxdUcs66dNYsfA2ywNtQBrJeb/dgy8jZjId/kx4YgF4fP6Ln1L3uyhhWvnSGYpOw6lEBP5LuunTWLDAw+x4YGHrAi74YGHWD9tEuunTSIm8l227ohtAyBK0i8/pNDagTVJxuf1YXf7OVfuJKvKMF16RhrpGWkcySwlJb+SrCqn1awRgjqaJP9nO0b/Zxo1v+ahS0ZqKJ9QCX5rJMyhN42aRj6h/udB5BKjiAp+i64uNrJ2M0Vs3rUiy4aU92G42X49iCYZDZjUMoX4ctFIcILfGgVU6E0LwEyCxKP98aWNxpc2GvFof+RjlyHlfdjxWnOxh93tJya5kIWHnDx2qJbnopP4NCmaYvtKC0LL6WYkQps70RA3laaUVbjzU1V7RRn2ijK8BbkWUJsM7VIAog7k8MyuPKtD1AJA/9zQQpYN9/oubFk/kpkLl7J4a0KbtrdZa/vSRrfMWS8GcSSzlGd25TH5VIjptTpR9T5SS+OMsrHZD3RHd7SDnTm1YwSzY2KsTtL46DSei07iSGZpm/tKeR8a5gnf0+vI8zfE5zAstpjptTrvifBJeeZ5LTQDkGXDtbsr0fte59mjDmaWaUyv1ZlZpvH3XJlRcQ6Grj5OTHJhy/t7VhrpwMVAog7kMCrOwcs+nZWaccak2L7S0oLpC6d2jGDJiUyWN8E6FVZqsLwJ5ruwYO5O9jFoUSIb4nPOT+/gtxf3kZjkQobFFreAaHRGGZoQbm+hhWd25fHsUQevHilgbo7bAmoNM2S/i6Grj3Mks9Tolcgn1Hb39MzHuXInw9edZrJd4z3xPISuLrYgCrf0ZOuOWKLzVFLLFDIKmlfr5EJmHMxhfoWvDczkUyELxl5RduFUUZNkIvdm8+BpkZd9eocQPyYc6XDnocpRQ+TebObmuFmptdTK5FMhBqwt4K1vMi4cWTMKnIyKczDZrvFJeWaHEBdrbVc5aphxMIflTR1rJaPA2TFI1IEc7k72tZwdYRCLtyZc6h4MMcmF7WrlwRSRAWsLiNyb3T6Iz+vjmV15jIpztIHwxN/I7JgY4svFS47CHk9DG62Y5hm4s5zx0Wntb0CnlikMiy3m06ToFpFSO9iZnSnbeGZXHkcyS8kocF6SHMksZc7m9AuaJyW/si3IltRaZsfEGNM09KZVs2bEzWV5EyzLlXn1SEG7MuNgTruy5JS73dlzd7IvPMi1BIlJLmRnyjbLJFawar7ZHi5NdrSS9jRyd7KPXlGnzQDXyjSlcYY2mk1SuKUnS05kslI7f9M9/HKgdaoh74nn/cR02NV7M9t2A9A/t/qf2uZOvB/zHvNdxk3Mm0bV+36VzK8wxHTWVutPmEbkE6q1hjQ3/yefCvGeeB7k1SPGlLsUeeubDOtnezJnczpvfZPBuXJnGEjzAqSri9FyulG4pSf3b/3OCvErNQNmxsEczpU70ST5kuWXJc9yiZXemQ3du5N9TK/VedmnW1qZm+M+v3r+gpTS42nA42nA5XRa4vE0hFd8zSDBb63cInvtAAYtSuTuZB+T7ZoFYy7tz+zK6+igQZtHRoGTyL3ZLab4M7vyGB+dxpAF+1i8NaEliLmWyNndsa+MZPi60/T/vpaJhTKT7ZqllZWaoZW3vsnA42m4IMS5cifPRScxN8fNeyK87NOZXqszsdDITa55/3i4dgVb0OPUTG2IR/vjzk9Vt6Qau5R3J/uYWCi3MJEJM2dzOkcyS80Q3WKrPia50IIIX2cmnwrxYIpIr6jTPBed1Mo0apFgpv0NcVMR3C5ESWLO5nS6fV3Ag6fFdmHmV/iYcTCHyL3ZRB3IsSRybzbP7MpjfoWvXYj+39cyZME+c7aEgTQ36smy0RA31dostrv9DF193IIJ9xcTxgSam+O2xAQwg9fMMo2JhTIPnjYgBi1KbC+RPq8REyR8iT9X7rRgWptpvssYLBwqHGB6rc7fc2ULYsh+F4MWJbLjUErH09c8ytcaxNTMCxtyGLC2oIUDT6/VO5TJdkMLJsTAneUMWpTYNotvE0eaj3rKxy6zun2t69mdKdt4fOley4lN35ls11pIOIC51D8XnWQu9xcGUQCteibyscuM5n31TKNqD5fm1H9DfA7PRScxdPVxhsUWMyy22Dq4MGS/i2GxxQxfd9oC2HEopb1WVcdtCU2Sqcv7OmTWpGbRLOV9SCh/P0GPUwvPvDIKnMQkFxK5N5s5m9N5LjqJ56KTeOubDFbvzSQlv7LN1P5FxzZ8Xp918v8SWk5WsWStLbr0a5oLHRdY/+GjPP8vtq7+0yCiJOHz+hDcLlxOJ2bzxeV0Irhdlk/9x0B8Xh9VjhoEt6s5rZTaFU1qQHC7qHLU/PpZ05EGqhw1uJxO0CVESSIlv5KoAznM2ZxufTJgzuZ0og7kkJJfaR1mcjmdVDlqflkc6ahSs1eUWdMzJrmQQYsSrYMJNy4raiHmYQWzD2IC2SvKLpa/dAzi8/qsc6cZBU6GLNjHlTMPcEtUJVMSdd45qRGdp7KxDOvDPu+c1JhxNMgtUZVcOfMAQxbss0K7vaLsQqbq+GCtCbEhPodOU2O58qNcZhwNsrMK4t0Xlp1VMONokCs/yqXT1FgrE7sATPvbJK0hblxWxDsnNWugvc7zcqFry3JlbomqbANzSdskpk9kFDjpOmWTpQnzne6sMgbbWWWYY8kpN0tOuYnOU1v8z9TcOyc1blxWRNcpmwwz6dLFjxr7vD5rY+eO13YSMS+Rh/co1iAby4wBluXKLDnl5rsCD1lVxk7FdwUelpxysyxXbvHcjWUwYb9CxLxE7nhtp7X10spELUHMMiHqQA6dJ+9k8KYaJh1u6ZRLTrnZklrb+hS3lURtSa1lySm39fyNZTAlUWfwpho6T95p1rqtS5LzICapJsmWNkbEBpiSqLMs1/gY3DsntfAuT4tDlkrYtci92bxzUmNjmaG9KYk6I2IDbbTStsBqjhma1EBKfiVdp2xiwNoCHt6jMOmwxjsnNev46KWUkaIksfCQk2W5Mu+c1Jh0WGPCfoUBawvoOmWT1d4Miy3nQczIuXpvJp2mxjJ4Uw0T9hsg09KM6fhcdBIxyYWXJM9FJzHjaJBpaTDpsAEzeFMNnabGGhVec+RtA1LlqAFd4vGley0Q8wZTEnWmpWGdWX3sUC3PHnW0K+b/n0qoZ1oaTEszfCQc5PGle0GXwv0k7PxI87S9EMjMMo35rvMdILPDbErrzlA4iOmw4SBh0/iXgUxLg8mnQvw9V2Zmmdau/D1XtpoxpiYe3qPw8B6FW6IqreOCvwpkWhqMinMwaFEi46PTfrFMXG38HLr6OHe8ttPykXZNYzrr4q0JdJoay4C1BS2cdfCmGuZsTrd6Hv/T5ozZJ7no9L1xWZE1fU0bD193unXx3GESFZNcyIb4nDazaUN8Dh6PkTy1O307CmgT9itM2K9YWnkuOumi26wTV6dZR43NXOXKj3LpPHknEY+us0DaDWiWnwCr92bSdcomBm+q4eE9ShsThTXh2jRn5mxOZ/CmmjYzZkRsgE5TY40Q33bhu/iiF66VcJjh604TuTfbUnnk3myGrzttQZgzZtJhzQrvfabHWGNccNELnz2tfSUcJjxADVhbwIC1BdYsMyOp+fyH9yhWGnAks/TS0gDTV4qK8q2NxU5TY7klqrIFTDhQ6+gZ/hwzdoSbpKgo/9LPj5hnR8yUwEwVw810MRkRG7BSRXPpLyrKv/RUsT2YI5mlLZLnEbEBK1q2lhGxASt5vuO1nZY5ioryL5TJX7icENwuioryjV1rr4+oAzkMWbDvouXEkAX7iDqQg8/rQ5MaLgZxaQWWJslWSWkWWBkFzl9UYP2PvgjFPNrj8/osM/2YcIQfE46QnpFmfL7K7SLocWpBj1Mz6+D0jLQWzzPb3b/6aI8SVnCbvXTTVOZxno6kqCjfKlPNUH4pIP9XPGz/N319UFnrf2iKLGi6LmggqCBoIOi6JuiqIqCrgqIrgqyrgoYu6JpiiK4LKgigCpquCCEdQdVVAU0VdP2iMGW29tplmtbcQNQ1QEXXNDQdQGsWHZBbvdQsKkTQfaiaBJrc/PyLPpQ2zqqbL9U10GV0TUbTZUCyQAoaJPaVinx5RmbVKZnVWRpf56r8WKlQFww2Q4bf8VdMXwsEtfkdGb97xSAb8yRG7df4zYYQ3deEsK2WsK1UsK1U6LIqxJWfKQzcEODVw0GS7KbG1F8Pout6C7WuL5Dpv1PBtlLEFgWXfyHTY61Ery91rvkiwLWfB7h6jcxV/5LoskLF9gl0+tjLI7FesuuxzKnrHeqneQdL143Bjacj6wqg4ZFUph8JYvusCdsXIldvhGvXi/T+SuS6dQrXrZO4fp3Ib76UuH5NiD6fi1z/mcgNnwa5epWMbbHG1StEvsoSjbeoq2i60h6MYNN1XTAhNF1vdlBoVFSG7/Nh+1Ti2o1Brl8v03uDyDVfN3DDVz5u+FKh15cKvdbp9FoHvT5X6PW5wjVr4LrPda6NkugTJdL1EwXbIpkVx5sdGaXZ8S9gGgNIJ6ipPHgghO3TED23h+ixTafXZpmb1ofos0ml+9dw1VcaV3wapMvKIF1WSVz+qULPzxV6faZw9Wc613yq0Xt1iN9Ehei+WMG2QObz03JHDtxsGk07P2XRmZ/hx7ZG5rqtMjdubqTHFonrNov8doPMZRvA9pmPqz8X+MNWhb/tkrg/VuGWaJXLPmmk85Imen6m0+sz6BMlcsNqP9etVujysU63jwIcrwy1N6UFm6Zrgma4KKBxrE7lyq999PnaT58dcMNWjV5bFa7d6sP2lcj/+szP6/FNHK2SqQtpSKqIKItUN2psyJH52yYXtkV+uq9UuP5fMj1XqVy9WuWGFSE6LQgxbHMQv6kVXW92B12wKZouSEjGNNMVJvwgYdugcGOsym+2q/TZqnD9dh3bVz5u3h4guVJtnpJa808zkJlBMMS7SQG6vB/gimUKvVdK9Fmu0nu5zLXLZGzvaWzLDhggmoysqwYIKoKqG+rKqVO5douP62JUfvutxg2xCn1iZTpv0rgpRuF0XQAIgRJElSUURUWWZWRZRpFlgrIKeIEg7yaC7X2FXkslei+XDVkmY1sQ4pFNDaA3hwcdNF0XbGjNZwNQWXZaxrZV5XexMjftFLnpW4ne34rYNvjZUywBQUJqEEkMoEk6oqIgySqipCCKEt6Qis8fRNEaAB+TtijYInV6Lwtx7VKRPstkIj5S6PGBRGFtwFCgApquCDYFTQANXZeZkiARsVPnlu9kfhcr0/cbiYivA4w94DM0oet4VQVJUQiJGiFRIiTKBEMSAX+QhoBIvU/C1SQCfpIKGrl8kZerFitcu0Tkuk9ErlsiYXtDYuMpYyobE0gVbIouC6DiDsgMiwtx406Z/rs0+u6WGPCNSI8tIZbnSoCCEvITkBRkWSMUkAgEJbz+EE2+IA3eAPUNjTR6fNTWSni9PuoFN/d8KtBpkcg1n3jp82GQ3h/6sc33seAHb/P6pYOmCTY0VQCNEkHhrgMhfrdL5k/fafT/XqT/boU+sRI/2r0AhESFYFDCF1TwBSWa/CE8TQHcjQFcDX6cdQGq63w43PWU1AoEmup4emMjtvl+enzop/d7Aa57N4Btvo/Z37jCHBzBpuqaAHDOHWDo/iD99in8+XuZO/er/H6fxsB/h0irDgGqoYGAhOAL0eALUd/oo87TRK2nCUddI3anQKXTTUl1DUVVNXga6nh2mwvb6066L3Bz3btOekU2YXtd5MVNDmuVVtEFm6brAmiUu4OMPODnjv0idx+UGHpQ4q6DEnf928+h0iCg0egN0OgXqW8MUCd4cXm81LgbqHIJlDs9lFd5KK90U1hWQ3GlgLOqlrs/rsQ2q45rFjq57q0yukc6sL3iYc62akBDR0fRNcGmq5oAQYSAyuQEibsPhnjgkMYD8T4ePOTnv/ZrfJrtBTWE4A3ibvRTJ3hx1jdRXddApbOeMoebEruL3Ao3p8vqOVVSR1JuDZkFtbywvgDb0zl0eqmanm+Wct2bFdhmlvP2Po/hH6qIrmiCTdNUAVVElTVeyfTz10My435UGHNE5JGfJIYf1ZiV4kFo8uILBKirD+LwBHC43Dhq6ymurqfAUU9ORS05RSU0NHmQVRW/JCMqOho6354U6DEri04z8+nxWim2fxSx8ZgLEAlJCqoiCzZZUwVZVECDjUVNDD8s8sRRlSmJOs8mwbPHZJ466iO2yI8aDNJU56a8tpGqaicOZx2FVfWcLa8lq7CMBn8IHfAGZQKSik/SQAoBOjEZtdiezqTTS/l0fzmPrFIBNB9CUCcoSYJN0TTBKymgS5TXBXn8pwCTj8lMT1WZmarx+nGR2Rk680+GOFleh9/bgMtZR3V1HYWVLvJKajiTV0pVjRsV8IVEgrLaLApeERSCAAx5/xS2+48zZvlZAmKIQFMTHq+PppAi2DRdFQIyyKIfRImoMz6ePO7lpUyJl08r/PN0iMjTEh9kS6zNEUgp92GvaaDAXstZh4DLG0JoChAMyviDMn5Jxi/K+EISIVnFr0h4JWPZ33a8mNteSCI6vhpZbMDhaqChyYfHHxRsmhYURBECoRDoMvkukVfTFN7IlHk7W+aDXIlVOSHW5ob4qhi2F4v8WNLIiSov5wLgkVVERSUYMqa2LyTjF1UCkkpQ1vGLImJAxCsai2SdKFJQ6aG0ooqK+gBuVxOCTxBsuq4IkqQSFCVCkgyqzg8lXt5J9/H+WViVJ7G+KMSOEoVdJSp77DJxdRrH3Rq5goLDJyMERRqCIt6QbPiHqBAQFSRJJSCrhGSZJklDUs/nIefsNRRXe3DWefE0NjUf21BURFEiGDRWVH9I5Nu8Rt7Pk/lXocbWIpFvKzT2VSr8YJdIcEqk1Svke2TsPhV3SMYTEmkISngDCr6QTFBSCUkqQUnFL2kEJUNLflFF1aGuyUepow6HuxG34DdyVkVRkCQFUVLxBWR0ScEfFPmuuIG1hTIxpSr/rpA46FBIqJHJdGmcqVPJa1Co9MrUBiTcQQlPQKYhoNAUUvCJCn5JJSApBCTZEr8oEVJU/IpKiaOOmnov9Q1+QyOqqiErGqKiIYk6/mAATQ4QalRItPvZU+EnvkrmxxqJRJdIVp1KTr1GQaNChVei2idTE9BwBRTqAzKeoEyjKNMkKvglhaCkNAMZogAeX4DS6npcDQE8jYHmM0aajqLqyLJOSNbwSTJev0woEKCxyU9OdZCEkgAJ1UGSBYWsBo3cRihq0qj0KVT5ZBwBjdqQRn1IRhBVGiWVRlklqOiIikZQ1hAV4ytjJE2n0ummqt6LU/AjNAYEm64jaBqoqo6iaEiKhiirBESVhkAQr9eH0ChSUu3nVGkdGY4mUmt8ZLoC5DWoFDUplHpVKnw6VT6ZWn+IuqCEJ6TQEFINzUgSflXFJ8nUe304XALVdQ3UNwaob/TT5A0ZILoO4TCyrBKSZHxBGcEfxNPgpdETwO32U+ZoIKesnrPlHrLtbn6urCfPXk+B3U2R3cO5qgbOVTVQUilwrkKgtEqguLKe4sp6yhwNlNg9VLkEhKYgjd4QTX6RYFA+X2Dpuo6maaiqiqqqKIqGKKn4QwrekERjIIC70YenMUBjk0S9EKK23our3ovb48Xj8SI0BfD4ROq9IdyNQeoa/Lg8AZxuPzV1PuobRASfguAN0egP4Q1KBEMykqwKNkAxMnpDNM1oSxhQGrKiI6oqTapIkyTiDYUIiDLBkEwoICOGjHghKxqKqqCoEooqEVJFgkqIkBIiKIsEpBB+MYA/FMAXkgiICiHRmK2KoilWo6bZRIKu61bjRdd1QdEQVBVBkzRBlVRBFhVBVTRBUXRBknVB1hAUECQQNF0XUHVB13RB0XRBVDRBUjRBUTVBUlRBlBRBlGQhJGuCJOuCouiCpuqCqqpl/7Eemqor5HnS2Ja/hPezpvCP1PuYlfo3vvo5EnfA0baH9qs+CKZpBIIh7DUuyuw1lNprqHDU4mnwoqoamq5xyn2YVTkv8cKJO3n+TH+eTB7Ao/H9eSr+TnbmrfyfgdiddZzKKaK0yklhuYN6oWVfvabay+6Tu3gzaSJPpPZm9E9XMmnvH1n60wKSanZypuEg35WuZlrCMLb9vPSXgzicdWTkFLX7vya5Dq/spk62s8v1AW+cu53ns29kSd6z/Fi9mZ/L8tpqVFfZeHYxBe7MSwdJy85v8Xd1oJwDFRtZlTeTD88+wcKsMSzMGsv8rL8wNbMnc7LuJN6xg6AcsF6TW1xBkzfQct9P8pDrSkfT1QuDKKrKz8UV1t+V3kKi89/m1YyhvHlyMPOz/ouFZ4fwYe59fJAzjLfO3s66wuep8p7jbF0iUTkzOe76/rzZ6jxUVteGtch06gL2C4PIikJFtcv6e3/ZeuamDOHNU//NivwxfFY8jnXlE/iyYiKflz/Eh4WD2Gv/CL/YQIJjI2+dvJvXTt7FtJS+LPt5OvVBY383KEoUlFaGzSz5wqb5ubC0WSsyG3PfZUbKnXzw8wOsKX6EdWUT+NI+nq8cY1nrGMnikjuJd0Xhld1sr3iTt37+IyuLHmZN0WMszxnPzLSBvJnxMMWNPxv7vUITLrdw8VlzMswnNud+xD+O3cGy3LF8ce5R1pZN4IuKsXzlGM0X1SP4uPJ2jgpraJAcfFb+CJHnbuOz8pF8UT6OL0om8nnRJFblPcrLaXfxxolROHzGd2idq7xIHBEavTQFQwAcLNvMP5Lu5JOcsawpmsRnJROIKnuYtVWjWVP9Vz6q7McRz0pUTSa2Zh6LSgeytOJPfGa/j3UVY1lTMoFPz01kdcEjLM95hNmp/8UHmU+j6MYnlrJyz3UMknHW0IbDW8rLyfexIGs4nxU8zqqi8Xx07gGiKkfyheN+ltnvJEFYGdYOFWlUqjniWcGK8iFElQ1jTek4Pi2awOqCR1iZ9wgfnx3Hs4l9+aHc+BqH2voGRFFqC+JpaEKSjOR2Y84iZqX8majcx1ieN57Xc+/hvXPD+aziAZaX30VGY0yH0/1s00E+KR7KquL7+ezceFbnT2BFzkSW5Uzg7VP38UbKQ3hCdc1aKWoLktHsG06/nbnJ9/H+6VGsyJnIC9l38kreMNaUPsKSkkHsdy26aABMcK3lw4L/5l9FY1mdP56lOeP55Ox4Psh+mOeT7+BAyUZj17O8qiWIKMkUlNoBOFQaw4zkQSw+M5bZp+7in7mPsKnkFVade4DPSsfTJNVeFCSk+lhbPIVl+Q+wMnccS8+OY/GZsXxwZjTTj9/OkqwXACi3O/H5A+dBKhy1lFQac33t2bf5R/KdvJnxFxadnkSyYzuf5j3BssIR/Kt4DBvLp/NF2dOsqXiSNRVPsKbyCeNnxZN8XjaFz4ufJrr4Bf5V8Agr8h5iWc5YPs4ey4enR/P+6YeYnfZn3kh9CAUfqgz2Gtd5kLOFpZTYjUMHH516jmlJA3jjxHCO2XexteBtFpwZyqqC0awo+huLCv7Eu4W38V7x73mvtD/vl/Xl/bJ+vFfye94tuo2F+X/g3dw/szT/b6zIHcMnZ0fz0ZmHWXT6ISKzRvJq5mBeSh5MSeNZyzyyrBggWTlFlNsNssiMKYz9oQe7i/9FmmM/r6bezZKfx7Is5yGW5f+NFYUjWHXuflaXDmN12V+JKhtGVNkwVpX9lZXFw1lRNILl+Q/ySc6DfHRmFIuyRhF5ciRvZ/6NNzPvZ3baIJ5N+AM/1xsfXcg9V47XH2wLMidpFE/9eAcVQg7Lsp7j9fShfHTmIT4+M4rIrKG8ljGAeSf78eaZfszP7sc/z/bln9n9mH+mH29m9eO1jP7MPfF7ZibfxvSE3zP1UD+eiruVxw/cxIT9fRj+764Mje3M6bqjAOQVl+MPhgyQvHPllFQapnkhfgRf5y7haNV3PJvwe945+QDvnnyAf2bcQ0zR22S7fySzbj+Z7n2cdO/jZP1eTtbvI9O9j8y6fWS49pHm3Edq9T6OV+0luXIPRyt2k1C+i/jybzhYupUfSrfjV40wX1zhQNN0A8RR66bEbjjr5p+Xc9IRz9snJvJ88h94O/N+3s64j1dS7mJLXuT/v0e/vT6qa93nnVXXdXLOlRtJi6qSWLmL8Yd682rGvcxLG8qbJ4byRuoQXj56L+UNuRcdoDHk5kDJNvaXbuZA2Rb2l21hX9nX7C3byNaCKJKr4pqnbw3+QLBlQDttxn4dPsh4hseP3sjcjP/m5dRBvJYymNdTBjMtvh8rT865KMja0wsZvqsr4/f3ZNyBnjx88CpGxV3BiAM2bt5iY8PPKwz/KKlsG1lDooTgCRJAYPKR/jyb2pcZaQOZdfyPzDn+J145/l/MSfojU364lW05yzuE2F30FU/80JcZSQN5+fifmH38Tmam3MGM1Dt4LOE6pv90DyHFCGLZ+SXtL3pn88rJCR5hbPy1TEq6jqnJv2XGsduZdfwPzD52By8n/5FZSX9g8sGbeDflGU7VHMUTqKMhVM/Z2hMsSZ/JY3G38I/E25l77I/MOv4HZhy/nRkptzE1+Rbu+beNhMrvjLEKSi+cj0T+8AaPZfTi2eQ/8Gj89fz96C3MSB7AjOTfMzPpNmYn3c7MowN4/IdrmXKoPy8l3MtLP/2Fpw7fxiMHr+HFxH7MTrqNmUm/56XkAbyY3I/pyb/jr/tsRJ542hqnOGydaRdkxv6J/DXBxvflX/Fd0Rru2W3jmYTrmZnUnxlJ/ZhxtB+zjg5g1tH+vJBwM1Pjr+fZ+Ot5PuFmZiX2Y9ZR43kvJfXlpeR+PJ90M3/da2Nm4gME5MZ2c5F2QV5OeYA/7rZxrOYgANE/f8S933ViTFxXZiX1ZfbRvsxK7MusxFuZnXgrs8JkZuKtzEi8lZlHf8espL48Gd+Lu3fbeDVpLA1BY+kvc7T7ZTktQUQlyLQjg/nzv20cyo+zrsdX7OKR/bcybLeNp368hpd+uok5ib9lbuKtzfI75ib+jtmJv2PGT7fwfMJveOj7zty/O4JPs+YjKsYUdTc04Wloav/YRusLz/04lAeTIsgsPENewfnc0is1EH32Qx47MICH913F+O//F+O/t/H4wW7877gIHtnfhXHfd2Hs91cyZl9v3k19lgLPaev15TV1NDR6Oz4/0vrC26ceYVhcL45X/GB4d2Eljf7Q+cJI9pHqiGPVqVeZd+wRZicOZ0bCvbyS9DAfpD3PnnNfUuO3ny9NVI2T+eVI8oVPGrUB2ZsfzX1HehJTtMK6FgyJZOYW0+gXf1EIz8wro9LhvKTn2lrugkMoFOS5n/7C0APXYK8tb3GepMrh5HB8Cmknz5JbXEpBSQVlFbVU2N0UlVWRW1RK1s95/JCQzMkzPyPLMpqm4ff7CQQChEIhJElCURQ0TcPsVOm6fn6tCT+oUOkq4bGE27n/qzv4KeMIwVCQQCBAbV0ttXW1VFRWkJ19lrS0DJKSj5F4NInk5OOcPHmK/Px8amtrcbvd1NTU4HQ6cbvdNDU1WTCyLKOqaguYDmvfgNzE4bIYdpWv4UT5EezuMkQl9B877PT/DQC7cLwx8LR3hQAAAABJRU5ErkJggg==) no-repeat;padding-left:40px}
+			.browser .browser-firefox{background-position:0 -34px}
+			.browser .browser-ie{background-position:0 -68px;margin-left:0px}
+			.browser .browser-360{background-position:0 -170px;margin-left: -27px}
+		</style>
+	</head>
+	<body style="margin-top:50px">
+		<h1>璇峰崌绾ф偍鐨勬祻瑙堝櫒锛屼互渚挎垜浠洿濂界殑涓烘偍鎻愪緵鏈嶅姟锛�</h1>
+		<p>鎮ㄦ鍦ㄤ娇鐢� Internet Explorer 鐨勬棭鏈熺増鏈紙IE11浠ヤ笅鐗堟湰鎴栦娇鐢ㄨ鍐呮牳鐨勬祻瑙堝櫒锛夈�傝繖鎰忓懗鐫�鍦ㄥ崌绾ф祻瑙堝櫒鍓嶏紝鎮ㄥ皢鏃犳硶璁块棶姝ょ綉绔欍��</p>
+		<hr />
+		<h2>璇锋敞鎰忥細寰蒋鍏徃瀵筗indows XP 鍙� Internet Explorer 鏃╂湡鐗堟湰鐨勬敮鎸佸凡缁忕粨鏉�</h2>
+		<p>
+			鑷� 2016 骞� 1 鏈� 12 鏃ヨ捣锛孧icrosoft 涓嶅啀涓� IE 11
+			浠ヤ笅鐗堟湰鎻愪緵鐩稿簲鏀寔鍜屾洿鏂般�傛病鏈夊叧閿殑娴忚鍣ㄥ畨鍏ㄦ洿鏂帮紝鎮ㄧ殑鐢佃剳鍙兘鏄撳彈鏈夊鐥呮瘨銆侀棿璋嶈蒋浠跺拰鍏朵粬鎭舵剰杞欢鐨勬敾鍑伙紝瀹冧滑鍙互绐冨彇鎴栨崯瀹虫偍鐨勪笟鍔℃暟鎹拰淇℃伅銆傝鍙傞槄
+			<a href="https://www.microsoft.com/zh-cn/WindowsForBusiness/End-of-IE-support"
+				>寰蒋瀵� Internet Explorer 鏃╂湡鐗堟湰鐨勬敮鎸佸皢浜� 2016 骞� 1 鏈� 12 鏃ョ粨鏉熺殑璇存槑</a
+			>
+			銆�
+		</p>
+		<hr />
+		<h2>鎮ㄥ彲浠ラ�夋嫨鏇村厛杩涚殑娴忚鍣�</h2>
+		<p>鎺ㄨ崘浣跨敤浠ヤ笅娴忚鍣ㄧ殑鏈�鏂扮増鏈�傚鏋滄偍鐨勭數鑴戝凡鏈変互涓嬫祻瑙堝櫒鐨勬渶鏂扮増鏈垯鐩存帴浣跨敤璇ユ祻瑙堝櫒璁块棶鍗冲彲銆�</p>
+		<ul class="browser">
+			<li class="browser-chrome">
+				<a href="https://www.google.cn/chrome/browser/desktop/index.html?hl=zh-CN&standalone=1"> 璋锋瓕娴忚鍣�<span>Google Chrome</span></a>
+			</li>
+			<li class="browser-firefox">
+				<a href="https://www.mozilla.org/zh-CN/firefox/new/"> 鐏嫄娴忚鍣�<span>Mozilla Firefox</span></a>
+			</li>
+			<li class="browser-ie">
+				<a href="https://windows.microsoft.com/zh-cn/internet-explorer/download-ie"> IE 11 娴忚鍣�<span>Internet Explorer</span></a>
+			</li>
+			<li class="browser-360">
+				<a href="http://se.360.cn/"> 360瀹夊叏娴忚鍣�<span>360 Chrome</span></a>
+			</li>
+			<div class="clean"></div>
+		</ul>
+		<hr />
+	</body>
+</html>
diff --git a/index.html b/index.html
index 7e47963..e641b01 100644
--- a/index.html
+++ b/index.html
@@ -1,215 +1,217 @@
 <!DOCTYPE html>
 <html>
+	<head>
+		<meta charset="utf-8" />
+		<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
+		<meta name="renderer" content="webkit" />
+		<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
+		<link rel="icon" href="/favicon.ico" />
+		<title>RuoYi-Vue-Plus澶氱鎴风鐞嗙郴缁�</title>
+		<!--[if lt IE 11
+			]><script>
+				window.location.href='/html/ie.html';
+			</script><!
+		[endif]-->
+		<style>
+			html,
+			body,
+			#app {
+			  height: 100%;
+			  margin: 0px;
+			  padding: 0px;
+			}
 
-<head>
-  <meta charset="utf-8">
-  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
-  <meta name="renderer" content="webkit">
-  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
-  <link rel="icon" href="/favicon.ico">
-  <title>RuoYi-Vue-Plus澶氱鎴风鐞嗙郴缁�</title>
-  <!--[if lt IE 11]><script>window.location.href='/html/ie.html';</script><![endif]-->
-  <style>
-    html,
-    body,
-    #app {
-      height: 100%;
-      margin: 0px;
-      padding: 0px;
-    }
+			.chromeframe {
+			  margin: 0.2em 0;
+			  background: #ccc;
+			  color: #000;
+			  padding: 0.2em 0;
+			}
 
-    .chromeframe {
-      margin: 0.2em 0;
-      background: #ccc;
-      color: #000;
-      padding: 0.2em 0;
-    }
+			#loader-wrapper {
+			  position: fixed;
+			  top: 0;
+			  left: 0;
+			  width: 100%;
+			  height: 100%;
+			  z-index: 999999;
+			}
 
-    #loader-wrapper {
-      position: fixed;
-      top: 0;
-      left: 0;
-      width: 100%;
-      height: 100%;
-      z-index: 999999;
-    }
+			#loader {
+			  display: block;
+			  position: relative;
+			  left: 50%;
+			  top: 50%;
+			  width: 150px;
+			  height: 150px;
+			  margin: -75px 0 0 -75px;
+			  border-radius: 50%;
+			  border: 3px solid transparent;
+			  border-top-color: #FFF;
+			  -webkit-animation: spin 2s linear infinite;
+			  -ms-animation: spin 2s linear infinite;
+			  -moz-animation: spin 2s linear infinite;
+			  -o-animation: spin 2s linear infinite;
+			  animation: spin 2s linear infinite;
+			  z-index: 1001;
+			}
 
-    #loader {
-      display: block;
-      position: relative;
-      left: 50%;
-      top: 50%;
-      width: 150px;
-      height: 150px;
-      margin: -75px 0 0 -75px;
-      border-radius: 50%;
-      border: 3px solid transparent;
-      border-top-color: #FFF;
-      -webkit-animation: spin 2s linear infinite;
-      -ms-animation: spin 2s linear infinite;
-      -moz-animation: spin 2s linear infinite;
-      -o-animation: spin 2s linear infinite;
-      animation: spin 2s linear infinite;
-      z-index: 1001;
-    }
+			#loader:before {
+			  content: "";
+			  position: absolute;
+			  top: 5px;
+			  left: 5px;
+			  right: 5px;
+			  bottom: 5px;
+			  border-radius: 50%;
+			  border: 3px solid transparent;
+			  border-top-color: #FFF;
+			  -webkit-animation: spin 3s linear infinite;
+			  -moz-animation: spin 3s linear infinite;
+			  -o-animation: spin 3s linear infinite;
+			  -ms-animation: spin 3s linear infinite;
+			  animation: spin 3s linear infinite;
+			}
 
-    #loader:before {
-      content: "";
-      position: absolute;
-      top: 5px;
-      left: 5px;
-      right: 5px;
-      bottom: 5px;
-      border-radius: 50%;
-      border: 3px solid transparent;
-      border-top-color: #FFF;
-      -webkit-animation: spin 3s linear infinite;
-      -moz-animation: spin 3s linear infinite;
-      -o-animation: spin 3s linear infinite;
-      -ms-animation: spin 3s linear infinite;
-      animation: spin 3s linear infinite;
-    }
-
-    #loader:after {
-      content: "";
-      position: absolute;
-      top: 15px;
-      left: 15px;
-      right: 15px;
-      bottom: 15px;
-      border-radius: 50%;
-      border: 3px solid transparent;
-      border-top-color: #FFF;
-      -moz-animation: spin 1.5s linear infinite;
-      -o-animation: spin 1.5s linear infinite;
-      -ms-animation: spin 1.5s linear infinite;
-      -webkit-animation: spin 1.5s linear infinite;
-      animation: spin 1.5s linear infinite;
-    }
+			#loader:after {
+			  content: "";
+			  position: absolute;
+			  top: 15px;
+			  left: 15px;
+			  right: 15px;
+			  bottom: 15px;
+			  border-radius: 50%;
+			  border: 3px solid transparent;
+			  border-top-color: #FFF;
+			  -moz-animation: spin 1.5s linear infinite;
+			  -o-animation: spin 1.5s linear infinite;
+			  -ms-animation: spin 1.5s linear infinite;
+			  -webkit-animation: spin 1.5s linear infinite;
+			  animation: spin 1.5s linear infinite;
+			}
 
 
-    @-webkit-keyframes spin {
-      0% {
-        -webkit-transform: rotate(0deg);
-        -ms-transform: rotate(0deg);
-        transform: rotate(0deg);
-      }
+			@-webkit-keyframes spin {
+			  0% {
+			    -webkit-transform: rotate(0deg);
+			    -ms-transform: rotate(0deg);
+			    transform: rotate(0deg);
+			  }
 
-      100% {
-        -webkit-transform: rotate(360deg);
-        -ms-transform: rotate(360deg);
-        transform: rotate(360deg);
-      }
-    }
+			  100% {
+			    -webkit-transform: rotate(360deg);
+			    -ms-transform: rotate(360deg);
+			    transform: rotate(360deg);
+			  }
+			}
 
-    @keyframes spin {
-      0% {
-        -webkit-transform: rotate(0deg);
-        -ms-transform: rotate(0deg);
-        transform: rotate(0deg);
-      }
+			@keyframes spin {
+			  0% {
+			    -webkit-transform: rotate(0deg);
+			    -ms-transform: rotate(0deg);
+			    transform: rotate(0deg);
+			  }
 
-      100% {
-        -webkit-transform: rotate(360deg);
-        -ms-transform: rotate(360deg);
-        transform: rotate(360deg);
-      }
-    }
+			  100% {
+			    -webkit-transform: rotate(360deg);
+			    -ms-transform: rotate(360deg);
+			    transform: rotate(360deg);
+			  }
+			}
 
 
-    #loader-wrapper .loader-section {
-      position: fixed;
-      top: 0;
-      width: 51%;
-      height: 100%;
-      background: #7171C6;
-      z-index: 1000;
-      -webkit-transform: translateX(0);
-      -ms-transform: translateX(0);
-      transform: translateX(0);
-    }
+			#loader-wrapper .loader-section {
+			  position: fixed;
+			  top: 0;
+			  width: 51%;
+			  height: 100%;
+			  background: #7171C6;
+			  z-index: 1000;
+			  -webkit-transform: translateX(0);
+			  -ms-transform: translateX(0);
+			  transform: translateX(0);
+			}
 
-    #loader-wrapper .loader-section.section-left {
-      left: 0;
-    }
+			#loader-wrapper .loader-section.section-left {
+			  left: 0;
+			}
 
-    #loader-wrapper .loader-section.section-right {
-      right: 0;
-    }
+			#loader-wrapper .loader-section.section-right {
+			  right: 0;
+			}
 
 
-    .loaded #loader-wrapper .loader-section.section-left {
-      -webkit-transform: translateX(-100%);
-      -ms-transform: translateX(-100%);
-      transform: translateX(-100%);
-      -webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
-      transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
-    }
+			.loaded #loader-wrapper .loader-section.section-left {
+			  -webkit-transform: translateX(-100%);
+			  -ms-transform: translateX(-100%);
+			  transform: translateX(-100%);
+			  -webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
+			  transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
+			}
 
-    .loaded #loader-wrapper .loader-section.section-right {
-      -webkit-transform: translateX(100%);
-      -ms-transform: translateX(100%);
-      transform: translateX(100%);
-      -webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
-      transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
-    }
+			.loaded #loader-wrapper .loader-section.section-right {
+			  -webkit-transform: translateX(100%);
+			  -ms-transform: translateX(100%);
+			  transform: translateX(100%);
+			  -webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
+			  transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
+			}
 
-    .loaded #loader {
-      opacity: 0;
-      -webkit-transition: all 0.3s ease-out;
-      transition: all 0.3s ease-out;
-    }
+			.loaded #loader {
+			  opacity: 0;
+			  -webkit-transition: all 0.3s ease-out;
+			  transition: all 0.3s ease-out;
+			}
 
-    .loaded #loader-wrapper {
-      visibility: hidden;
-      -webkit-transform: translateY(-100%);
-      -ms-transform: translateY(-100%);
-      transform: translateY(-100%);
-      -webkit-transition: all 0.3s 1s ease-out;
-      transition: all 0.3s 1s ease-out;
-    }
+			.loaded #loader-wrapper {
+			  visibility: hidden;
+			  -webkit-transform: translateY(-100%);
+			  -ms-transform: translateY(-100%);
+			  transform: translateY(-100%);
+			  -webkit-transition: all 0.3s 1s ease-out;
+			  transition: all 0.3s 1s ease-out;
+			}
 
-    .no-js #loader-wrapper {
-      display: none;
-    }
+			.no-js #loader-wrapper {
+			  display: none;
+			}
 
-    .no-js h1 {
-      color: #222222;
-    }
+			.no-js h1 {
+			  color: #222222;
+			}
 
-    #loader-wrapper .load_title {
-      font-family: 'Open Sans';
-      color: #FFF;
-      font-size: 19px;
-      width: 100%;
-      text-align: center;
-      z-index: 9999999999999;
-      position: absolute;
-      top: 60%;
-      opacity: 1;
-      line-height: 30px;
-    }
+			#loader-wrapper .load_title {
+			  font-family: 'Open Sans';
+			  color: #FFF;
+			  font-size: 19px;
+			  width: 100%;
+			  text-align: center;
+			  z-index: 9999999999999;
+			  position: absolute;
+			  top: 60%;
+			  opacity: 1;
+			  line-height: 30px;
+			}
 
-    #loader-wrapper .load_title span {
-      font-weight: normal;
-      font-style: italic;
-      font-size: 13px;
-      color: #FFF;
-      opacity: 0.5;
-    }
-  </style>
-</head>
+			#loader-wrapper .load_title span {
+			  font-weight: normal;
+			  font-style: italic;
+			  font-size: 13px;
+			  color: #FFF;
+			  opacity: 0.5;
+			}
+		</style>
+	</head>
 
-<body>
-  <div id="app">
-    <div id="loader-wrapper">
-      <div id="loader"></div>
-      <div class="loader-section section-left"></div>
-      <div class="loader-section section-right"></div>
-      <div class="load_title">姝e湪鍔犺浇绯荤粺璧勬簮锛岃鑰愬績绛夊緟</div>
-    </div>
-  </div>
-  <script type="module" src="/src/main.js"></script>
-</body>
-
-</html>
\ No newline at end of file
+	<body>
+		<div id="app">
+			<div id="loader-wrapper">
+				<div id="loader"></div>
+				<div class="loader-section section-left"></div>
+				<div class="loader-section section-right"></div>
+				<div class="load_title">姝e湪鍔犺浇绯荤粺璧勬簮锛岃鑰愬績绛夊緟</div>
+			</div>
+		</div>
+		<script type="module" src="/src/main.ts"></script>
+	</body>
+</html>
diff --git a/package.json b/package.json
index 65e9ef0..c71573e 100644
--- a/package.json
+++ b/package.json
@@ -1,43 +1,80 @@
 {
-  "name": "ruoyi-vue-plus",
-  "version": "5.0.0-SNAPSHOT",
-  "description": "RuoYi-Vue-Plus澶氱鎴风鐞嗙郴缁�",
-  "author": "LionLi",
-  "license": "MIT",
-  "scripts": {
-    "dev": "vite",
-    "build:prod": "vite build",
-    "preview": "vite preview"
-  },
-  "repository": {
-    "type": "git",
-    "url": "https://gitee.com/JavaLionLi/plus-ui.git"
-  },
-  "dependencies": {
-    "@element-plus/icons-vue": "2.0.10",
-    "@vueup/vue-quill": "1.1.0",
-    "@vueuse/core": "9.5.0",
-    "axios": "0.27.2",
-    "echarts": "5.4.0",
-    "element-plus": "2.2.27",
-    "file-saver": "2.0.5",
-    "fuse.js": "6.6.2",
-    "js-cookie": "3.0.1",
-    "jsencrypt": "3.3.1",
-    "nprogress": "0.2.0",
-    "pinia": "2.0.22",
-    "vue": "3.2.45",
-    "vue-cropper": "1.0.3",
-    "vue-router": "4.1.4"
-  },
-  "devDependencies": {
-    "@vitejs/plugin-vue": "3.1.0",
-    "@vue/compiler-sfc": "3.2.45",
-    "sass": "1.56.1",
-    "unplugin-auto-import": "0.11.4",
-    "vite": "3.2.3",
-    "vite-plugin-compression": "0.5.1",
-    "vite-plugin-svg-icons": "2.0.1",
-    "vite-plugin-vue-setup-extend": "0.4.0"
-  }
+	"name": "ruoyi-vue-plus",
+	"version": "5.0.0-SNAPSHOT",
+	"description": "RuoYi-Vue-Plus澶氱鎴风鐞嗙郴缁�",
+	"author": "LionLi",
+	"license": "MIT",
+	"scripts": {
+		"dev": "vite serve --mode development",
+		"build:prod": "vite build --mode production &&vue-tsc --noEmit",
+		"preview": "vite preview",
+		"lint": "eslint src/**/*.{ts,js,vue} --fix",
+		"prepare": "husky install",
+		"prettier": "prettier --write ."
+	},
+	"repository": {
+		"type": "git",
+		"url": "https://gitee.com/JavaLionLi/plus-ui.git"
+	},
+	"dependencies": {
+		"@element-plus/icons-vue": "2.1.0",
+		"@vueup/vue-quill": "1.1.0",
+		"@vueuse/core": "9.5.0",
+		"animate.css": "4.1.1",
+		"await-to-js": "^3.0.0",
+		"axios": "^1.3.4",
+		"echarts": "5.4.0",
+		"element-plus": "2.2.27",
+		"file-saver": "2.0.5",
+		"fuse.js": "6.6.2",
+		"js-cookie": "3.0.1",
+		"jsencrypt": "3.3.1",
+		"nprogress": "0.2.0",
+		"path-browserify": "1.0.1",
+		"path-to-regexp": "6.2.0",
+		"pinia": "2.0.22",
+		"screenfull": "6.0.0",
+		"vue": "3.2.45",
+		"vue-cropper": "1.0.3",
+		"vue-i18n": "9.2.2",
+		"vue-router": "4.1.4"
+	},
+	"devDependencies": {
+		"@iconify/json": "^2.2.40",
+		"@intlify/unplugin-vue-i18n": "0.8.2",
+		"@types/file-saver": "2.0.5",
+		"@types/js-cookie": "3.0.3",
+		"@types/node": "18.14.2",
+		"@types/nprogress": "0.2.0",
+		"@types/path-browserify": "^1.0.0",
+		"@typescript-eslint/eslint-plugin": "5.56.0",
+		"@typescript-eslint/parser": "5.56.0",
+		"@unocss/preset-attributify": "^0.50.6",
+		"@unocss/preset-icons": "^0.50.6",
+		"@unocss/preset-uno": "^0.50.6",
+		"@vitejs/plugin-vue": "4.0.0",
+		"@vue/compiler-sfc": "3.2.45",
+		"autoprefixer": "10.4.14",
+		"eslint": "8.36.0",
+		"eslint-config-prettier": "8.8.0",
+		"eslint-plugin-prettier": "4.2.1",
+		"eslint-plugin-vue": "9.9.0",
+		"fast-glob": "^3.2.11",
+		"husky": "7.0.4",
+		"postcss": "^8.4.21",
+		"prettier": "2.8.6",
+		"sass": "1.56.1",
+		"typescript": "4.9.5",
+		"unocss": "^0.50.6",
+		"unplugin-auto-import": "0.13.0",
+		"unplugin-icons": "0.15.1",
+		"unplugin-vue-components": "0.23.0",
+		"vite": "4.1.4",
+		"vite-plugin-compression": "0.5.1",
+		"vite-plugin-svg-icons": "2.0.1",
+		"vite-plugin-vue-setup-extend": "0.4.0",
+		"vitest": "^0.29.7",
+		"vue-eslint-parser": "9.1.0",
+		"vue-tsc": "0.35.0"
+	}
 }
diff --git a/public/favicon.ico b/public/favicon.ico
index e263760..3f919d8 100644
--- a/public/favicon.ico
+++ b/public/favicon.ico
Binary files differ
diff --git a/src/App.vue b/src/App.vue
index 31839f2..0f25eb6 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -1,8 +1,8 @@
 <template>
-  <router-view />
+	<router-view />
 </template>
 
-<script setup>
+<script setup lang="ts">
 import useSettingsStore from '@/store/modules/settings'
 import { handleThemeStyle } from '@/utils/theme'
 
diff --git a/src/animate.ts b/src/animate.ts
new file mode 100644
index 0000000..add6be8
--- /dev/null
+++ b/src/animate.ts
@@ -0,0 +1,48 @@
+// 鍓嶇紑
+const animatePrefix = 'animate__animated ';
+// 寮�鍚殢鏈哄姩鐢� 闅忔満鍔ㄧ敾鍊�
+const animateList: string[] = [
+	animatePrefix + 'animate__pulse',
+	animatePrefix + 'animate__rubberBand',
+	animatePrefix + 'animate__bounceIn',
+	animatePrefix + 'animate__bounceInLeft',
+	animatePrefix + 'animate__fadeIn',
+	animatePrefix + 'animate__fadeInLeft',
+	animatePrefix + 'animate__fadeInDown',
+	animatePrefix + 'animate__fadeInUp',
+	animatePrefix + 'animate__flipInX',
+	animatePrefix + 'animate__lightSpeedInLeft',
+	animatePrefix + 'animate__rotateInDownLeft',
+	animatePrefix + 'animate__rollIn',
+	animatePrefix + 'animate__rotateInDownLeft',
+	animatePrefix + 'animate__zoomIn',
+	animatePrefix + 'animate__zoomInDown',
+	animatePrefix + 'animate__slideInLeft',
+	animatePrefix + 'animate__lightSpeedIn'
+];
+// 鍏抽棴闅忔満鍔ㄧ敾鍚庣殑榛樿鏁堟灉
+const defaultAnimate = animatePrefix + 'animate__bounceIn';
+// 鎼滅储闅愯棌鏄剧ず鍔ㄧ敾
+const searchAnimate = {
+	enter: animatePrefix + 'animate__flipInX',
+	leave: animatePrefix + 'animate__bounceOut'
+};
+
+// 鑿滃崟鎼滅储鍔ㄧ敾
+const menuSearchAnimate = {
+	enter: animatePrefix + 'animate__fadeInLeft',
+	leave: animatePrefix + 'animate__fadeOutLeft'
+};
+// logo鍔ㄧ敾
+const logoAnimate = {
+	enter: animatePrefix + 'animate__bounceIn',
+	leave: animatePrefix + 'animate__bounceOut'
+};
+
+export default {
+	animateList,
+	defaultAnimate,
+	searchAnimate,
+	menuSearchAnimate,
+	logoAnimate
+};
diff --git a/src/api/demo/demo.js b/src/api/demo/demo.js
deleted file mode 100644
index 04d4025..0000000
--- a/src/api/demo/demo.js
+++ /dev/null
@@ -1,54 +0,0 @@
-import request from '@/utils/request'
-
-// 鏌ヨ娴嬭瘯鍗曡〃鍒楄〃
-export function listDemo(query) {
-  return request({
-    url: '/demo/demo/list',
-    method: 'get',
-    params: query
-  })
-}
-
-// 鑷畾涔夊垎椤垫帴鍙�
-export function pageDemo(query) {
-  return request({
-    url: '/demo/demo/page',
-    method: 'get',
-    params: query
-  })
-}
-
-// 鏌ヨ娴嬭瘯鍗曡〃璇︾粏
-export function getDemo(id) {
-  return request({
-    url: '/demo/demo/' + id,
-    method: 'get'
-  })
-}
-
-// 鏂板娴嬭瘯鍗曡〃
-export function addDemo(data) {
-  return request({
-    url: '/demo/demo',
-    method: 'post',
-    data: data
-  })
-}
-
-// 淇敼娴嬭瘯鍗曡〃
-export function updateDemo(data) {
-  return request({
-    url: '/demo/demo',
-    method: 'put',
-    data: data
-  })
-}
-
-// 鍒犻櫎娴嬭瘯鍗曡〃
-export function delDemo(id) {
-  return request({
-    url: '/demo/demo/' + id,
-    method: 'delete'
-  })
-}
-
diff --git a/src/api/demo/demo.ts b/src/api/demo/demo.ts
new file mode 100644
index 0000000..9ae5c74
--- /dev/null
+++ b/src/api/demo/demo.ts
@@ -0,0 +1,55 @@
+import request from '@/utils/request';
+import { DemoForm, DemoQuery, DemoVO } from './types';
+import { AxiosPromise } from 'axios';
+
+// 鏌ヨ娴嬭瘯鍗曡〃鍒楄〃
+export function listDemo(query: DemoQuery): AxiosPromise<DemoVO[]> {
+	return request({
+		url: '/demo/demo/list',
+		method: 'get',
+		params: query
+	});
+}
+
+// 鑷畾涔夊垎椤垫帴鍙�
+export function pageDemo(query: DemoQuery): AxiosPromise<DemoVO[]> {
+	return request({
+		url: '/demo/demo/page',
+		method: 'get',
+		params: query
+	});
+}
+
+// 鏌ヨ娴嬭瘯鍗曡〃璇︾粏
+export function getDemo(id: string | number): AxiosPromise<DemoVO> {
+	return request({
+		url: '/demo/demo/' + id,
+		method: 'get'
+	});
+}
+
+// 鏂板娴嬭瘯鍗曡〃
+export function addDemo(data: DemoForm) {
+	return request({
+		url: '/demo/demo',
+		method: 'post',
+		data: data
+	});
+}
+
+// 淇敼娴嬭瘯鍗曡〃
+export function updateDemo(data: DemoForm) {
+	return request({
+		url: '/demo/demo',
+		method: 'put',
+		data: data
+	});
+}
+
+// 鍒犻櫎娴嬭瘯鍗曡〃
+export function delDemo(id: string | number | Array<string | number>) {
+	return request({
+		url: '/demo/demo/' + id,
+		method: 'delete'
+	});
+}
diff --git a/src/api/demo/tree.js b/src/api/demo/tree.js
deleted file mode 100644
index 4c7ebc0..0000000
--- a/src/api/demo/tree.js
+++ /dev/null
@@ -1,44 +0,0 @@
-import request from '@/utils/request'
-
-// 鏌ヨ娴嬭瘯鏍戣〃鍒楄〃
-export function listTree(query) {
-  return request({
-    url: '/demo/tree/list',
-    method: 'get',
-    params: query
-  })
-}
-
-// 鏌ヨ娴嬭瘯鏍戣〃璇︾粏
-export function getTree(id) {
-  return request({
-    url: '/demo/tree/' + id,
-    method: 'get'
-  })
-}
-
-// 鏂板娴嬭瘯鏍戣〃
-export function addTree(data) {
-  return request({
-    url: '/demo/tree',
-    method: 'post',
-    data: data
-  })
-}
-
-// 淇敼娴嬭瘯鏍戣〃
-export function updateTree(data) {
-  return request({
-    url: '/demo/tree',
-    method: 'put',
-    data: data
-  })
-}
-
-// 鍒犻櫎娴嬭瘯鏍戣〃
-export function delTree(id) {
-  return request({
-    url: '/demo/tree/' + id,
-    method: 'delete'
-  })
-}
diff --git a/src/api/demo/tree.ts b/src/api/demo/tree.ts
new file mode 100644
index 0000000..7c36a39
--- /dev/null
+++ b/src/api/demo/tree.ts
@@ -0,0 +1,46 @@
+import { AxiosPromise } from 'axios';
+import request from '@/utils/request';
+import { DemoTreeForm, DemoTreeVO, DemoTreeQuery } from './types';
+
+// 鏌ヨ娴嬭瘯鏍戣〃鍒楄〃
+export function listTree(query?: DemoTreeQuery): AxiosPromise<DemoTreeVO[]> {
+	return request({
+		url: '/demo/tree/list',
+		method: 'get',
+		params: query
+	});
+}
+
+// 鏌ヨ娴嬭瘯鏍戣〃璇︾粏
+export function getTree(id: string | number): AxiosPromise<DemoTreeVO> {
+	return request({
+		url: '/demo/tree/' + id,
+		method: 'get'
+	});
+}
+
+// 鏂板娴嬭瘯鏍戣〃
+export function addTree(data: DemoTreeForm) {
+	return request({
+		url: '/demo/tree',
+		method: 'post',
+		data: data
+	});
+}
+
+// 淇敼娴嬭瘯鏍戣〃
+export function updateTree(data: DemoTreeForm) {
+	return request({
+		url: '/demo/tree',
+		method: 'put',
+		data: data
+	});
+}
+
+// 鍒犻櫎娴嬭瘯鏍戣〃
+export function delTree(id: string | number | Array<string | number>) {
+	return request({
+		url: '/demo/tree/' + id,
+		method: 'delete'
+	});
+}
diff --git a/src/api/demo/types.ts b/src/api/demo/types.ts
new file mode 100644
index 0000000..ae8ec70
--- /dev/null
+++ b/src/api/demo/types.ts
@@ -0,0 +1,55 @@
+export interface DemoVO extends BaseEntity {
+	id: number | string;
+	deptId: number | string;
+	userId: number | string;
+	orderNum: number;
+	testKey: string;
+	value: string;
+	createByName: string;
+	updateByName?: any;
+}
+
+export interface DemoQuery extends PageQuery {
+	testKey: string;
+	value: string;
+	createTime: string;
+}
+export interface DemoForm {
+	id: string | number | undefined;
+	deptId: string | number | undefined;
+	userId: string | number | undefined;
+	orderNum: number;
+	testKey: string;
+	value: string;
+	version: string;
+	ossConfigId: string | number | undefined;
+	createTime?: string;
+}
+
+export interface DemoTreeVO extends BaseEntity {
+	id: number | string;
+	parentId: number | string;
+	deptId: number | string;
+	userId: number | string;
+	treeName: string;
+	children?: DemoTreeVO[];
+}
+
+export interface DemoTreeQuery {
+	treeName: string;
+	createTime: string;
+}
+
+export interface DemoTreeForm {
+	id: string | number | undefined;
+	parentId: string | number | undefined;
+	deptId: string | number | undefined;
+	userId: string | number | undefined;
+	treeName: string;
+}
+
+export interface DemoTreeOptionsType {
+	id: string | number;
+	treeName: string;
+	children?: DemoTreeOptionsType[];
+}
diff --git a/src/api/login.js b/src/api/login.js
deleted file mode 100644
index 8c36ba7..0000000
--- a/src/api/login.js
+++ /dev/null
@@ -1,83 +0,0 @@
-import request from '@/utils/request'
-
-// 鐧诲綍鏂规硶
-export function login(tenantId, username, password, code, uuid) {
-  const data = {
-    tenantId,
-    username,
-    password,
-    code,
-    uuid
-  }
-  return request({
-    url: '/auth/login',
-    headers: {
-      isToken: false
-    },
-    method: 'post',
-    data: data
-  })
-}
-
-// 娉ㄥ唽鏂规硶
-export function register(data) {
-  return request({
-    url: '/auth/register',
-    headers: {
-      isToken: false
-    },
-    method: 'post',
-    data: data
-  })
-}
-
-// 鑾峰彇鐢ㄦ埛璇︾粏淇℃伅
-export function getInfo() {
-  return request({
-    url: '/system/user/getInfo',
-    method: 'get'
-  })
-}
-
-// 閫�鍑烘柟娉�
-export function logout() {
-  return request({
-    url: '/auth/logout',
-    method: 'post'
-  })
-}
-
-// 鑾峰彇楠岃瘉鐮�
-export function getCodeImg() {
-  return request({
-    url: '/code',
-    headers: {
-      isToken: false
-    },
-    method: 'get',
-    timeout: 20000
-  })
-}
-
-// 鐭俊楠岃瘉鐮�
-export function getCodeSms() {
-  return request({
-    url: '/sms/code',
-    headers: {
-      isToken: false
-    },
-    method: 'get',
-    timeout: 20000
-  })
-}
-
-// 鑾峰彇绉熸埛鍒楄〃
-export function getTenantList() {
-  return request({
-    url: '/auth/tenant/list',
-    headers: {
-      isToken: false
-    },
-    method: 'get'
-  })
-}
diff --git a/src/api/login.ts b/src/api/login.ts
new file mode 100644
index 0000000..b20e1a8
--- /dev/null
+++ b/src/api/login.ts
@@ -0,0 +1,81 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { LoginData, LoginResult, VerifyCodeResult, TenantInfo } from './types';
+import { UserInfo } from '@/api/system/user/types';
+
+/**
+ * @param data {LoginData}
+ * @returns
+ */
+export function login(data: LoginData): AxiosPromise<LoginResult> {
+	const params = {
+		tenantId: data.tenantId,
+		username: data.username.trim(),
+		password: data.password,
+		code: data.code,
+		uuid: data.uuid
+	};
+	return request({
+		url: '/auth/login',
+		headers: {
+			isToken: false
+		},
+		method: 'post',
+		data: params
+	});
+}
+
+// 娉ㄥ唽鏂规硶
+export function register(data: any) {
+	return request({
+		url: '/auth/register',
+		headers: {
+			isToken: false
+		},
+		method: 'post',
+		data: data
+	});
+}
+
+/**
+ * 娉ㄩ攢
+ */
+export function logout() {
+	return request({
+		url: '/auth/logout',
+		method: 'post'
+	});
+}
+
+/**
+ * 鑾峰彇楠岃瘉鐮�
+ */
+export function getCodeImg(): AxiosPromise<VerifyCodeResult> {
+	return request({
+		url: '/code',
+		headers: {
+			isToken: false
+		},
+		method: 'get',
+		timeout: 20000
+	});
+}
+
+// 鑾峰彇鐢ㄦ埛璇︾粏淇℃伅
+export function getInfo(): AxiosPromise<UserInfo> {
+	return request({
+		url: '/system/user/getInfo',
+		method: 'get'
+	});
+}
+
+// 鑾峰彇绉熸埛鍒楄〃
+export function getTenantList(): AxiosPromise<TenantInfo> {
+	return request({
+		url: '/auth/tenant/list',
+		headers: {
+			isToken: false
+		},
+		method: 'get'
+	});
+}
diff --git a/src/api/menu.js b/src/api/menu.js
deleted file mode 100644
index 845efd7..0000000
--- a/src/api/menu.js
+++ /dev/null
@@ -1,9 +0,0 @@
-import request from '@/utils/request'
-
-// 鑾峰彇璺敱
-export const getRouters = () => {
-  return request({
-    url: '/system/menu/getRouters',
-    method: 'get'
-  })
-}
\ No newline at end of file
diff --git a/src/api/menu.ts b/src/api/menu.ts
new file mode 100644
index 0000000..7aae599
--- /dev/null
+++ b/src/api/menu.ts
@@ -0,0 +1,11 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { RouteRecordRaw } from 'vue-router';
+
+// 鑾峰彇璺敱
+export function getRouters(): AxiosPromise<RouteRecordRaw[]> {
+	return request({
+		url: '/system/menu/getRouters',
+		method: 'get'
+	});
+}
diff --git a/src/api/monitor/cache.js b/src/api/monitor/cache.js
deleted file mode 100644
index 45a9003..0000000
--- a/src/api/monitor/cache.js
+++ /dev/null
@@ -1,57 +0,0 @@
-import request from '@/utils/request'
-
-// 鏌ヨ缂撳瓨璇︾粏
-export function getCache() {
-  return request({
-    url: '/monitor/cache',
-    method: 'get'
-  })
-}
-
-// 鏌ヨ缂撳瓨鍚嶇О鍒楄〃
-export function listCacheName() {
-  return request({
-    url: '/monitor/cache/getNames',
-    method: 'get'
-  })
-}
-
-// 鏌ヨ缂撳瓨閿悕鍒楄〃
-export function listCacheKey(cacheName) {
-  return request({
-    url: '/monitor/cache/getKeys/' + cacheName,
-    method: 'get'
-  })
-}
-
-// 鏌ヨ缂撳瓨鍐呭
-export function getCacheValue(cacheName, cacheKey) {
-  return request({
-    url: '/monitor/cache/getValue/' + cacheName + '/' + cacheKey,
-    method: 'get'
-  })
-}
-
-// 娓呯悊鎸囧畾鍚嶇О缂撳瓨
-export function clearCacheName(cacheName) {
-  return request({
-    url: '/monitor/cache/clearCacheName/' + cacheName,
-    method: 'delete'
-  })
-}
-
-// 娓呯悊鎸囧畾閿悕缂撳瓨
-export function clearCacheKey(cacheName, cacheKey) {
-  return request({
-    url: '/monitor/cache/clearCacheKey/' + cacheName + '/'  + cacheKey,
-    method: 'delete'
-  })
-}
-
-// 娓呯悊鍏ㄩ儴缂撳瓨
-export function clearCacheAll() {
-  return request({
-    url: '/monitor/cache/clearCacheAll',
-    method: 'delete'
-  })
-}
diff --git a/src/api/monitor/cache/index.ts b/src/api/monitor/cache/index.ts
new file mode 100644
index 0000000..5c16aaf
--- /dev/null
+++ b/src/api/monitor/cache/index.ts
@@ -0,0 +1,59 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { CacheVO } from './types';
+
+// 鏌ヨ缂撳瓨璇︾粏
+export function getCache(): AxiosPromise<CacheVO> {
+	return request({
+		url: '/monitor/cache',
+		method: 'get'
+	});
+}
+
+// 鏌ヨ缂撳瓨鍚嶇О鍒楄〃
+export function listCacheName() {
+	return request({
+		url: '/monitor/cache/getNames',
+		method: 'get'
+	});
+}
+
+// 鏌ヨ缂撳瓨閿悕鍒楄〃
+export function listCacheKey(cacheName: string) {
+	return request({
+		url: '/monitor/cache/getKeys/' + cacheName,
+		method: 'get'
+	});
+}
+
+// 鏌ヨ缂撳瓨鍐呭
+export function getCacheValue(cacheName: string, cacheKey: string) {
+	return request({
+		url: '/monitor/cache/getValue/' + cacheName + '/' + cacheKey,
+		method: 'get'
+	});
+}
+
+// 娓呯悊鎸囧畾鍚嶇О缂撳瓨
+export function clearCacheName(cacheName: string) {
+	return request({
+		url: '/monitor/cache/clearCacheName/' + cacheName,
+		method: 'delete'
+	});
+}
+
+// 娓呯悊鎸囧畾閿悕缂撳瓨
+export function clearCacheKey(cacheName: string, cacheKey: string) {
+	return request({
+		url: '/monitor/cache/clearCacheKey/' + cacheName + '/' + cacheKey,
+		method: 'delete'
+	});
+}
+
+// 娓呯悊鍏ㄩ儴缂撳瓨
+export function clearCacheAll() {
+	return request({
+		url: '/monitor/cache/clearCacheAll',
+		method: 'delete'
+	});
+}
diff --git a/src/api/monitor/cache/types.ts b/src/api/monitor/cache/types.ts
new file mode 100644
index 0000000..540fafd
--- /dev/null
+++ b/src/api/monitor/cache/types.ts
@@ -0,0 +1,7 @@
+export interface CacheVO {
+	commandStats: Array<{ name: string; value: string }>;
+
+	dbSize: number;
+
+	info: { [key: string]: string };
+}
diff --git a/src/api/monitor/loginInfo/index.ts b/src/api/monitor/loginInfo/index.ts
new file mode 100644
index 0000000..482a74a
--- /dev/null
+++ b/src/api/monitor/loginInfo/index.ts
@@ -0,0 +1,36 @@
+import request from '@/utils/request';
+import { LoginInfoQuery, LoginInfoVO } from './types';
+import { AxiosPromise } from 'axios';
+
+// 鏌ヨ鐧诲綍鏃ュ織鍒楄〃
+export function list(query: LoginInfoQuery): AxiosPromise<LoginInfoVO[]> {
+	return request({
+		url: '/monitor/logininfor/list',
+		method: 'get',
+		params: query
+	});
+}
+
+// 鍒犻櫎鐧诲綍鏃ュ織
+export function delLoginInfo(infoId: string | number | Array<string | number>) {
+	return request({
+		url: '/monitor/logininfor/' + infoId,
+		method: 'delete'
+	});
+}
+
+// 瑙i攣鐢ㄦ埛鐧诲綍鐘舵��
+export function unlockLoginInfo(userName: string | Array<string>) {
+	return request({
+		url: '/monitor/logininfor/unlock/' + userName,
+		method: 'get'
+	});
+}
+
+// 娓呯┖鐧诲綍鏃ュ織
+export function cleanLoginInfo() {
+	return request({
+		url: '/monitor/logininfor/clean',
+		method: 'delete'
+	});
+}
diff --git a/src/api/monitor/loginInfo/types.ts b/src/api/monitor/loginInfo/types.ts
new file mode 100644
index 0000000..c037b7a
--- /dev/null
+++ b/src/api/monitor/loginInfo/types.ts
@@ -0,0 +1,20 @@
+export interface LoginInfoVO {
+	infoId: string | number;
+	tenantId: string | number;
+	userName: string;
+	status: string;
+	ipaddr: string;
+	loginLocation: string;
+	browser: string;
+	os: string;
+	msg: string;
+	loginTime: string;
+}
+
+export interface LoginInfoQuery extends PageQuery {
+	ipaddr: string;
+	userName: string;
+	status: string;
+	orderByColumn: string;
+	isAsc: string;
+}
diff --git a/src/api/monitor/logininfor.js b/src/api/monitor/logininfor.js
deleted file mode 100644
index 4d112b7..0000000
--- a/src/api/monitor/logininfor.js
+++ /dev/null
@@ -1,34 +0,0 @@
-import request from '@/utils/request'
-
-// 鏌ヨ鐧诲綍鏃ュ織鍒楄〃
-export function list(query) {
-  return request({
-    url: '/monitor/logininfor/list',
-    method: 'get',
-    params: query
-  })
-}
-
-// 鍒犻櫎鐧诲綍鏃ュ織
-export function delLogininfor(infoId) {
-  return request({
-    url: '/monitor/logininfor/' + infoId,
-    method: 'delete'
-  })
-}
-
-// 瑙i攣鐢ㄦ埛鐧诲綍鐘舵��
-export function unlockLogininfor(userName) {
-  return request({
-    url: '/monitor/logininfor/unlock/' + userName,
-    method: 'get'
-  })
-}
-
-// 娓呯┖鐧诲綍鏃ュ織
-export function cleanLogininfor() {
-  return request({
-    url: '/monitor/logininfor/clean',
-    method: 'delete'
-  })
-}
diff --git a/src/api/monitor/online.js b/src/api/monitor/online.js
deleted file mode 100644
index bd22137..0000000
--- a/src/api/monitor/online.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import request from '@/utils/request'
-
-// 鏌ヨ鍦ㄧ嚎鐢ㄦ埛鍒楄〃
-export function list(query) {
-  return request({
-    url: '/monitor/online/list',
-    method: 'get',
-    params: query
-  })
-}
-
-// 寮洪��鐢ㄦ埛
-export function forceLogout(tokenId) {
-  return request({
-    url: '/monitor/online/' + tokenId,
-    method: 'delete'
-  })
-}
diff --git a/src/api/monitor/online/index.ts b/src/api/monitor/online/index.ts
new file mode 100644
index 0000000..bedbde4
--- /dev/null
+++ b/src/api/monitor/online/index.ts
@@ -0,0 +1,20 @@
+import request from '@/utils/request';
+import { OnlineQuery, OnlineVO } from './types';
+import { AxiosPromise } from 'axios';
+
+// 鏌ヨ鍦ㄧ嚎鐢ㄦ埛鍒楄〃
+export function list(query: OnlineQuery): AxiosPromise<OnlineVO[]> {
+	return request({
+		url: '/monitor/online/list',
+		method: 'get',
+		params: query
+	});
+}
+
+// 寮洪��鐢ㄦ埛
+export function forceLogout(tokenId: string) {
+	return request({
+		url: '/monitor/online/' + tokenId,
+		method: 'delete'
+	});
+}
diff --git a/src/api/monitor/online/types.ts b/src/api/monitor/online/types.ts
new file mode 100644
index 0000000..f873d59
--- /dev/null
+++ b/src/api/monitor/online/types.ts
@@ -0,0 +1,15 @@
+export interface OnlineQuery extends PageQuery {
+	ipaddr: string;
+	userName: string;
+}
+
+export interface OnlineVO extends BaseEntity {
+	tokenId: string;
+	deptName: string;
+	userName: string;
+	ipaddr: string;
+	loginLocation: string;
+	browser: string;
+	os: string;
+	loginTime: number;
+}
diff --git a/src/api/monitor/operlog.js b/src/api/monitor/operlog.js
deleted file mode 100644
index a04bca8..0000000
--- a/src/api/monitor/operlog.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import request from '@/utils/request'
-
-// 鏌ヨ鎿嶄綔鏃ュ織鍒楄〃
-export function list(query) {
-  return request({
-    url: '/monitor/operlog/list',
-    method: 'get',
-    params: query
-  })
-}
-
-// 鍒犻櫎鎿嶄綔鏃ュ織
-export function delOperlog(operId) {
-  return request({
-    url: '/monitor/operlog/' + operId,
-    method: 'delete'
-  })
-}
-
-// 娓呯┖鎿嶄綔鏃ュ織
-export function cleanOperlog() {
-  return request({
-    url: '/monitor/operlog/clean',
-    method: 'delete'
-  })
-}
diff --git a/src/api/monitor/operlog/index.ts b/src/api/monitor/operlog/index.ts
new file mode 100644
index 0000000..d7e5faa
--- /dev/null
+++ b/src/api/monitor/operlog/index.ts
@@ -0,0 +1,28 @@
+import request from '@/utils/request';
+import { OperLogQuery, OperLogVO } from './types';
+import { AxiosPromise } from 'axios';
+
+// 鏌ヨ鎿嶄綔鏃ュ織鍒楄〃
+export function list(query: OperLogQuery): AxiosPromise<OperLogVO[]> {
+	return request({
+		url: '/monitor/operlog/list',
+		method: 'get',
+		params: query
+	});
+}
+
+// 鍒犻櫎鎿嶄綔鏃ュ織
+export function delOperlog(operId: string | number | Array<string | number>) {
+	return request({
+		url: '/monitor/operlog/' + operId,
+		method: 'delete'
+	});
+}
+
+// 娓呯┖鎿嶄綔鏃ュ織
+export function cleanOperlog() {
+	return request({
+		url: '/monitor/operlog/clean',
+		method: 'delete'
+	});
+}
diff --git a/src/api/monitor/operlog/types.ts b/src/api/monitor/operlog/types.ts
new file mode 100644
index 0000000..71bc00c
--- /dev/null
+++ b/src/api/monitor/operlog/types.ts
@@ -0,0 +1,52 @@
+export interface OperLogQuery extends PageQuery {
+	title: string;
+	operName: string;
+	businessType: string;
+	status: string;
+	orderByColumn: string;
+	isAsc: string;
+}
+
+export interface OperLogVO extends BaseEntity {
+	operId: string | number;
+	tenantId: string;
+	title: string;
+	businessType: number;
+	businessTypes: number[] | undefined;
+	method: string;
+	requestMethod: string;
+	operatorType: number;
+	operName: string;
+	deptName: string;
+	operUrl: string;
+	operIp: string;
+	operLocation: string;
+	operParam: string;
+	jsonResult: string;
+	status: number;
+	errorMsg: string;
+	operTime: string;
+	costTime: number;
+}
+
+export interface OperLogForm {
+	operId: number | string | undefined;
+	tenantId: string | number | undefined;
+	title: string;
+	businessType: number;
+	businessTypes: number[] | undefined;
+	method: string;
+	requestMethod: string;
+	operatorType: number;
+	operName: string;
+	deptName: string;
+	operUrl: string;
+	operIp: string;
+	operLocation: string;
+	operParam: string;
+	jsonResult: string;
+	status: number;
+	errorMsg: string;
+	operTime: string;
+	costTime: number;
+}
diff --git a/src/api/system/config.js b/src/api/system/config.js
deleted file mode 100644
index 9b93886..0000000
--- a/src/api/system/config.js
+++ /dev/null
@@ -1,72 +0,0 @@
-import request from '@/utils/request'
-
-// 鏌ヨ鍙傛暟鍒楄〃
-export function listConfig(query) {
-  return request({
-    url: '/system/config/list',
-    method: 'get',
-    params: query
-  })
-}
-
-// 鏌ヨ鍙傛暟璇︾粏
-export function getConfig(configId) {
-  return request({
-    url: '/system/config/' + configId,
-    method: 'get'
-  })
-}
-
-// 鏍规嵁鍙傛暟閿悕鏌ヨ鍙傛暟鍊�
-export function getConfigKey(configKey) {
-  return request({
-    url: '/system/config/configKey/' + configKey,
-    method: 'get'
-  })
-}
-
-// 鏂板鍙傛暟閰嶇疆
-export function addConfig(data) {
-  return request({
-    url: '/system/config',
-    method: 'post',
-    data: data
-  })
-}
-
-// 淇敼鍙傛暟閰嶇疆
-export function updateConfig(data) {
-  return request({
-    url: '/system/config',
-    method: 'put',
-    data: data
-  })
-}
-
-// 淇敼鍙傛暟閰嶇疆
-export function updateConfigByKey(key, value) {
-  return request({
-    url: '/system/config/updateByKey',
-    method: 'put',
-    data: {
-      configKey: key,
-      configValue: value
-    }
-  })
-}
-
-// 鍒犻櫎鍙傛暟閰嶇疆
-export function delConfig(configId) {
-  return request({
-    url: '/system/config/' + configId,
-    method: 'delete'
-  })
-}
-
-// 鍒锋柊鍙傛暟缂撳瓨
-export function refreshCache() {
-  return request({
-    url: '/system/config/refreshCache',
-    method: 'delete'
-  })
-}
diff --git a/src/api/system/config/index.ts b/src/api/system/config/index.ts
new file mode 100644
index 0000000..ad87668
--- /dev/null
+++ b/src/api/system/config/index.ts
@@ -0,0 +1,74 @@
+import request from '@/utils/request';
+import { ConfigForm, ConfigQuery, ConfigVO } from './types';
+import { AxiosPromise } from 'axios';
+
+// 鏌ヨ鍙傛暟鍒楄〃
+export function listConfig(query: ConfigQuery): AxiosPromise<ConfigVO[]> {
+	return request({
+		url: '/system/config/list',
+		method: 'get',
+		params: query
+	});
+}
+
+// 鏌ヨ鍙傛暟璇︾粏
+export function getConfig(configId: string | number): AxiosPromise<ConfigVO> {
+	return request({
+		url: '/system/config/' + configId,
+		method: 'get'
+	});
+}
+
+// 鏍规嵁鍙傛暟閿悕鏌ヨ鍙傛暟鍊�
+export function getConfigKey(configKey: string): AxiosPromise<ConfigVO> {
+	return request({
+		url: '/system/config/configKey/' + configKey,
+		method: 'get'
+	});
+}
+
+// 鏂板鍙傛暟閰嶇疆
+export function addConfig(data: ConfigForm) {
+	return request({
+		url: '/system/config',
+		method: 'post',
+		data: data
+	});
+}
+
+// 淇敼鍙傛暟閰嶇疆
+export function updateConfig(data: ConfigForm) {
+	return request({
+		url: '/system/config',
+		method: 'put',
+		data: data
+	});
+}
+
+// 淇敼鍙傛暟閰嶇疆
+export function updateConfigByKey(key: string, value: any) {
+	return request({
+		url: '/system/config/updateByKey',
+		method: 'put',
+		data: {
+			configKey: key,
+			configValue: value
+		}
+	});
+}
+
+// 鍒犻櫎鍙傛暟閰嶇疆
+export function delConfig(configId: string | number | Array<string | number>) {
+	return request({
+		url: '/system/config/' + configId,
+		method: 'delete'
+	});
+}
+
+// 鍒锋柊鍙傛暟缂撳瓨
+export function refreshCache() {
+	return request({
+		url: '/system/config/refreshCache',
+		method: 'delete'
+	});
+}
diff --git a/src/api/system/config/types.ts b/src/api/system/config/types.ts
new file mode 100644
index 0000000..f51f91d
--- /dev/null
+++ b/src/api/system/config/types.ts
@@ -0,0 +1,23 @@
+export interface ConfigVO extends BaseEntity {
+	configId: number | string;
+	configName: string;
+	configKey: string;
+	configValue: string;
+	configType: string;
+	remark: string;
+}
+
+export interface ConfigForm {
+	configId: number | string | undefined;
+	configName: string;
+	configKey: string;
+	configValue: string;
+	configType: string;
+	remark: string;
+}
+
+export interface ConfigQuery extends PageQuery {
+	configName: string;
+	configKey: string;
+	configType: string;
+}
diff --git a/src/api/system/dept.js b/src/api/system/dept.js
deleted file mode 100644
index fc943cd..0000000
--- a/src/api/system/dept.js
+++ /dev/null
@@ -1,52 +0,0 @@
-import request from '@/utils/request'
-
-// 鏌ヨ閮ㄩ棬鍒楄〃
-export function listDept(query) {
-  return request({
-    url: '/system/dept/list',
-    method: 'get',
-    params: query
-  })
-}
-
-// 鏌ヨ閮ㄩ棬鍒楄〃锛堟帓闄よ妭鐐癸級
-export function listDeptExcludeChild(deptId) {
-  return request({
-    url: '/system/dept/list/exclude/' + deptId,
-    method: 'get'
-  })
-}
-
-// 鏌ヨ閮ㄩ棬璇︾粏
-export function getDept(deptId) {
-  return request({
-    url: '/system/dept/' + deptId,
-    method: 'get'
-  })
-}
-
-// 鏂板閮ㄩ棬
-export function addDept(data) {
-  return request({
-    url: '/system/dept',
-    method: 'post',
-    data: data
-  })
-}
-
-// 淇敼閮ㄩ棬
-export function updateDept(data) {
-  return request({
-    url: '/system/dept',
-    method: 'put',
-    data: data
-  })
-}
-
-// 鍒犻櫎閮ㄩ棬
-export function delDept(deptId) {
-  return request({
-    url: '/system/dept/' + deptId,
-    method: 'delete'
-  })
-}
\ No newline at end of file
diff --git a/src/api/system/dept/index.ts b/src/api/system/dept/index.ts
new file mode 100644
index 0000000..85dcf1a
--- /dev/null
+++ b/src/api/system/dept/index.ts
@@ -0,0 +1,62 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { DeptForm, DeptQuery, DeptVO } from './types';
+
+// 鏌ヨ閮ㄩ棬鍒楄〃
+export const listDept = (query?: DeptQuery) => {
+	return request({
+		url: '/system/dept/list',
+		method: 'get',
+		params: query
+	});
+};
+
+// 鏌ヨ閮ㄩ棬鍒楄〃锛堟帓闄よ妭鐐癸級
+export const listDeptExcludeChild = (deptId: string | number): AxiosPromise<DeptVO[]> => {
+	return request({
+		url: '/system/dept/list/exclude/' + deptId,
+		method: 'get'
+	});
+};
+
+// 鏌ヨ閮ㄩ棬璇︾粏
+export const getDept = (deptId: string | number): AxiosPromise<DeptVO> => {
+	return request({
+		url: '/system/dept/' + deptId,
+		method: 'get'
+	});
+};
+
+// 鏌ヨ閮ㄩ棬涓嬫媺鏍戠粨鏋�
+export const treeselect = (): AxiosPromise<DeptVO[]> => {
+	return request({
+		url: '/system/dept/treeselect',
+		method: 'get'
+	});
+};
+
+// 鏂板閮ㄩ棬
+export const addDept = (data: DeptForm) => {
+	return request({
+		url: '/system/dept',
+		method: 'post',
+		data: data
+	});
+};
+
+// 淇敼閮ㄩ棬
+export const updateDept = (data: DeptForm) => {
+	return request({
+		url: '/system/dept',
+		method: 'put',
+		data: data
+	});
+};
+
+// 鍒犻櫎閮ㄩ棬
+export const delDept = (deptId: number | string) => {
+	return request({
+		url: '/system/dept/' + deptId,
+		method: 'delete'
+	});
+};
diff --git a/src/api/system/dept/types.ts b/src/api/system/dept/types.ts
new file mode 100644
index 0000000..9fe52e5
--- /dev/null
+++ b/src/api/system/dept/types.ts
@@ -0,0 +1,45 @@
+/**
+ * 閮ㄩ棬鏌ヨ鍙傛暟
+ */
+export interface DeptQuery extends PageQuery {
+	deptName?: string;
+	status?: number;
+}
+
+/**
+ * 閮ㄩ棬绫诲瀷
+ */
+export interface DeptVO extends BaseEntity {
+	id: number | string;
+	parentName: string;
+	parentId: number | string;
+	children: DeptVO[];
+	deptId: number | string;
+	deptName: string;
+	orderNum: number;
+	leader: string;
+	phone: string;
+	email: string;
+	status: string;
+	delFlag: string;
+	ancestors: string;
+	menuId: string | number;
+}
+
+/**
+ * 閮ㄩ棬琛ㄥ崟绫诲瀷
+ */
+export interface DeptForm {
+	parentName?: string;
+	parentId?: number | string;
+	children?: DeptForm[];
+	deptId?: number | string;
+	deptName?: string;
+	orderNum?: number;
+	leader?: string;
+	phone?: string;
+	email?: string;
+	status?: string;
+	delFlag?: string;
+	ancestors?: string;
+}
diff --git a/src/api/system/dict/data.js b/src/api/system/dict/data.js
deleted file mode 100644
index 6c9eb79..0000000
--- a/src/api/system/dict/data.js
+++ /dev/null
@@ -1,52 +0,0 @@
-import request from '@/utils/request'
-
-// 鏌ヨ瀛楀吀鏁版嵁鍒楄〃
-export function listData(query) {
-  return request({
-    url: '/system/dict/data/list',
-    method: 'get',
-    params: query
-  })
-}
-
-// 鏌ヨ瀛楀吀鏁版嵁璇︾粏
-export function getData(dictCode) {
-  return request({
-    url: '/system/dict/data/' + dictCode,
-    method: 'get'
-  })
-}
-
-// 鏍规嵁瀛楀吀绫诲瀷鏌ヨ瀛楀吀鏁版嵁淇℃伅
-export function getDicts(dictType) {
-  return request({
-    url: '/system/dict/data/type/' + dictType,
-    method: 'get'
-  })
-}
-
-// 鏂板瀛楀吀鏁版嵁
-export function addData(data) {
-  return request({
-    url: '/system/dict/data',
-    method: 'post',
-    data: data
-  })
-}
-
-// 淇敼瀛楀吀鏁版嵁
-export function updateData(data) {
-  return request({
-    url: '/system/dict/data',
-    method: 'put',
-    data: data
-  })
-}
-
-// 鍒犻櫎瀛楀吀鏁版嵁
-export function delData(dictCode) {
-  return request({
-    url: '/system/dict/data/' + dictCode,
-    method: 'delete'
-  })
-}
diff --git a/src/api/system/dict/data/index.ts b/src/api/system/dict/data/index.ts
new file mode 100644
index 0000000..e36b5a3
--- /dev/null
+++ b/src/api/system/dict/data/index.ts
@@ -0,0 +1,53 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { DictDataForm, DictDataQuery, DictDataVO } from './types';
+// 鏍规嵁瀛楀吀绫诲瀷鏌ヨ瀛楀吀鏁版嵁淇℃伅
+export function getDicts(dictType: string): AxiosPromise<DictDataVO[]> {
+	return request({
+		url: '/system/dict/data/type/' + dictType,
+		method: 'get'
+	});
+}
+
+// 鏌ヨ瀛楀吀鏁版嵁鍒楄〃
+export function listData(query: DictDataQuery): AxiosPromise<DictDataVO[]> {
+	return request({
+		url: '/system/dict/data/list',
+		method: 'get',
+		params: query
+	});
+}
+
+// 鏌ヨ瀛楀吀鏁版嵁璇︾粏
+export function getData(dictCode: string | number): AxiosPromise<DictDataVO> {
+	return request({
+		url: '/system/dict/data/' + dictCode,
+		method: 'get'
+	});
+}
+
+// 鏂板瀛楀吀鏁版嵁
+export function addData(data: DictDataForm) {
+	return request({
+		url: '/system/dict/data',
+		method: 'post',
+		data: data
+	});
+}
+
+// 淇敼瀛楀吀鏁版嵁
+export function updateData(data: DictDataForm) {
+	return request({
+		url: '/system/dict/data',
+		method: 'put',
+		data: data
+	});
+}
+
+// 鍒犻櫎瀛楀吀鏁版嵁
+export function delData(dictCode: string | number | Array<string | number>) {
+	return request({
+		url: '/system/dict/data/' + dictCode,
+		method: 'delete'
+	});
+}
diff --git a/src/api/system/dict/data/types.ts b/src/api/system/dict/data/types.ts
new file mode 100644
index 0000000..bed715d
--- /dev/null
+++ b/src/api/system/dict/data/types.ts
@@ -0,0 +1,29 @@
+export interface DictDataQuery extends PageQuery {
+	dictName: string;
+	dictType: string;
+	status: string;
+	dictLabel: string;
+}
+
+export interface DictDataVO extends BaseEntity {
+	dictCode: string;
+	dictLabel: string;
+	dictValue: string;
+	cssClass: string;
+	listClass: ElTagType;
+	dictSort: number;
+	status: string;
+	remark: string;
+}
+
+export interface DictDataForm {
+	dictType?: string;
+	dictCode: string | undefined;
+	dictLabel: string;
+	dictValue: string;
+	cssClass: string;
+	listClass: ElTagType;
+	dictSort: number;
+	status: string;
+	remark: string;
+}
diff --git a/src/api/system/dict/type.js b/src/api/system/dict/type.js
deleted file mode 100644
index a0254ba..0000000
--- a/src/api/system/dict/type.js
+++ /dev/null
@@ -1,60 +0,0 @@
-import request from '@/utils/request'
-
-// 鏌ヨ瀛楀吀绫诲瀷鍒楄〃
-export function listType(query) {
-  return request({
-    url: '/system/dict/type/list',
-    method: 'get',
-    params: query
-  })
-}
-
-// 鏌ヨ瀛楀吀绫诲瀷璇︾粏
-export function getType(dictId) {
-  return request({
-    url: '/system/dict/type/' + dictId,
-    method: 'get'
-  })
-}
-
-// 鏂板瀛楀吀绫诲瀷
-export function addType(data) {
-  return request({
-    url: '/system/dict/type',
-    method: 'post',
-    data: data
-  })
-}
-
-// 淇敼瀛楀吀绫诲瀷
-export function updateType(data) {
-  return request({
-    url: '/system/dict/type',
-    method: 'put',
-    data: data
-  })
-}
-
-// 鍒犻櫎瀛楀吀绫诲瀷
-export function delType(dictId) {
-  return request({
-    url: '/system/dict/type/' + dictId,
-    method: 'delete'
-  })
-}
-
-// 鍒锋柊瀛楀吀缂撳瓨
-export function refreshCache() {
-  return request({
-    url: '/system/dict/type/refreshCache',
-    method: 'delete'
-  })
-}
-
-// 鑾峰彇瀛楀吀閫夋嫨妗嗗垪琛�
-export function optionselect() {
-  return request({
-    url: '/system/dict/type/optionselect',
-    method: 'get'
-  })
-}
diff --git a/src/api/system/dict/type/index.ts b/src/api/system/dict/type/index.ts
new file mode 100644
index 0000000..5acebee
--- /dev/null
+++ b/src/api/system/dict/type/index.ts
@@ -0,0 +1,62 @@
+import request from '@/utils/request';
+import { DictTypeForm, DictTypeVO, DictTypeQuery } from './types';
+import { AxiosPromise } from 'axios';
+
+// 鏌ヨ瀛楀吀绫诲瀷鍒楄〃
+export function listType(query: DictTypeQuery): AxiosPromise<DictTypeVO[]> {
+	return request({
+		url: '/system/dict/type/list',
+		method: 'get',
+		params: query
+	});
+}
+
+// 鏌ヨ瀛楀吀绫诲瀷璇︾粏
+export function getType(dictId: number | string): AxiosPromise<DictTypeVO> {
+	return request({
+		url: '/system/dict/type/' + dictId,
+		method: 'get'
+	});
+}
+
+// 鏂板瀛楀吀绫诲瀷
+export function addType(data: DictTypeForm) {
+	return request({
+		url: '/system/dict/type',
+		method: 'post',
+		data: data
+	});
+}
+
+// 淇敼瀛楀吀绫诲瀷
+export function updateType(data: DictTypeForm) {
+	return request({
+		url: '/system/dict/type',
+		method: 'put',
+		data: data
+	});
+}
+
+// 鍒犻櫎瀛楀吀绫诲瀷
+export function delType(dictId: string | number | Array<string | number>) {
+	return request({
+		url: '/system/dict/type/' + dictId,
+		method: 'delete'
+	});
+}
+
+// 鍒锋柊瀛楀吀缂撳瓨
+export function refreshCache() {
+	return request({
+		url: '/system/dict/type/refreshCache',
+		method: 'delete'
+	});
+}
+
+// 鑾峰彇瀛楀吀閫夋嫨妗嗗垪琛�
+export function optionselect(): AxiosPromise<DictTypeVO[]> {
+	return request({
+		url: '/system/dict/type/optionselect',
+		method: 'get'
+	});
+}
diff --git a/src/api/system/dict/type/types.ts b/src/api/system/dict/type/types.ts
new file mode 100644
index 0000000..67ad07c
--- /dev/null
+++ b/src/api/system/dict/type/types.ts
@@ -0,0 +1,21 @@
+export interface DictTypeVO extends BaseEntity {
+	dictId: number | string;
+	dictName: string;
+	dictType: string;
+	status: string;
+	remark: string;
+}
+
+export interface DictTypeForm {
+	dictId: number | string | undefined;
+	dictName: string;
+	dictType: string;
+	status: string;
+	remark: string;
+}
+
+export interface DictTypeQuery extends PageQuery {
+	dictName: string;
+	dictType: string;
+	status: string;
+}
diff --git a/src/api/system/menu.js b/src/api/system/menu.js
deleted file mode 100644
index e9f475c..0000000
--- a/src/api/system/menu.js
+++ /dev/null
@@ -1,68 +0,0 @@
-import request from '@/utils/request'
-
-// 鏌ヨ鑿滃崟鍒楄〃
-export function listMenu(query) {
-  return request({
-    url: '/system/menu/list',
-    method: 'get',
-    params: query
-  })
-}
-
-// 鏌ヨ鑿滃崟璇︾粏
-export function getMenu(menuId) {
-  return request({
-    url: '/system/menu/' + menuId,
-    method: 'get'
-  })
-}
-
-// 鏌ヨ鑿滃崟涓嬫媺鏍戠粨鏋�
-export function treeselect() {
-  return request({
-    url: '/system/menu/treeselect',
-    method: 'get'
-  })
-}
-
-// 鏍规嵁瑙掕壊ID鏌ヨ鑿滃崟涓嬫媺鏍戠粨鏋�
-export function roleMenuTreeselect(roleId) {
-  return request({
-    url: '/system/menu/roleMenuTreeselect/' + roleId,
-    method: 'get'
-  })
-}
-
-// 鏍规嵁瑙掕壊ID鏌ヨ鑿滃崟涓嬫媺鏍戠粨鏋�
-export function tenantPackageMenuTreeselect(packageId) {
-  return request({
-    url: '/system/menu/tenantPackageMenuTreeselect/' + packageId,
-    method: 'get'
-  })
-}
-
-// 鏂板鑿滃崟
-export function addMenu(data) {
-  return request({
-    url: '/system/menu',
-    method: 'post',
-    data: data
-  })
-}
-
-// 淇敼鑿滃崟
-export function updateMenu(data) {
-  return request({
-    url: '/system/menu',
-    method: 'put',
-    data: data
-  })
-}
-
-// 鍒犻櫎鑿滃崟
-export function delMenu(menuId) {
-  return request({
-    url: '/system/menu/' + menuId,
-    method: 'delete'
-  })
-}
\ No newline at end of file
diff --git a/src/api/system/menu/index.ts b/src/api/system/menu/index.ts
new file mode 100644
index 0000000..82f76b0
--- /dev/null
+++ b/src/api/system/menu/index.ts
@@ -0,0 +1,70 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { MenuQuery, MenuVO, MenuForm, MenuTreeOption, RoleMenuTree } from './types';
+
+// 鏌ヨ鑿滃崟鍒楄〃
+export const listMenu = (query?: MenuQuery): AxiosPromise<MenuVO[]> => {
+	return request({
+		url: '/system/menu/list',
+		method: 'get',
+		params: query
+	});
+};
+
+// 鏌ヨ鑿滃崟璇︾粏
+export const getMenu = (menuId: string | number): AxiosPromise<MenuVO> => {
+	return request({
+		url: '/system/menu/' + menuId,
+		method: 'get'
+	});
+};
+
+// 鏌ヨ鑿滃崟涓嬫媺鏍戠粨鏋�
+export const treeselect = (): AxiosPromise<MenuTreeOption[]> => {
+	return request({
+		url: '/system/menu/treeselect',
+		method: 'get'
+	});
+};
+
+// 鏍规嵁瑙掕壊ID鏌ヨ鑿滃崟涓嬫媺鏍戠粨鏋�
+export const roleMenuTreeselect = (roleId: string | number): AxiosPromise<RoleMenuTree> => {
+	return request({
+		url: '/system/menu/roleMenuTreeselect/' + roleId,
+		method: 'get'
+	});
+};
+
+// 鏍规嵁瑙掕壊ID鏌ヨ鑿滃崟涓嬫媺鏍戠粨鏋�
+export const tenantPackageMenuTreeselect = (packageId: string | number): AxiosPromise<RoleMenuTree> => {
+	return request({
+		url: '/system/menu/tenantPackageMenuTreeselect/' + packageId,
+		method: 'get'
+	});
+};
+
+// 鏂板鑿滃崟
+export const addMenu = (data: MenuForm) => {
+	return request({
+		url: '/system/menu',
+		method: 'post',
+		data: data
+	});
+};
+
+// 淇敼鑿滃崟
+export const updateMenu = (data: MenuForm) => {
+	return request({
+		url: '/system/menu',
+		method: 'put',
+		data: data
+	});
+};
+
+// 鍒犻櫎鑿滃崟
+export const delMenu = (menuId: string | number) => {
+	return request({
+		url: '/system/menu/' + menuId,
+		method: 'delete'
+	});
+};
diff --git a/src/api/system/menu/types.ts b/src/api/system/menu/types.ts
new file mode 100644
index 0000000..2eb43e5
--- /dev/null
+++ b/src/api/system/menu/types.ts
@@ -0,0 +1,69 @@
+import { MenuTypeEnum } from '@/enums/MenuTypeEnum';
+
+/**
+ * 鑿滃崟鏍戝舰缁撴瀯绫诲瀷
+ */
+export interface MenuTreeOption {
+	id: string | number;
+	label: string;
+	parentId: string | number;
+	weight: number;
+	children?: MenuTreeOption[];
+}
+
+export interface RoleMenuTree {
+	menus: MenuTreeOption[];
+	checkedKeys: string[];
+}
+
+/**
+ * 鑿滃崟鏌ヨ鍙傛暟绫诲瀷
+ */
+export interface MenuQuery {
+	keywords?: string;
+	menuName?: string;
+	status?: string;
+}
+
+/**
+ * 鑿滃崟瑙嗗浘瀵硅薄绫诲瀷
+ */
+export interface MenuVO extends BaseEntity {
+	parentName: string;
+	parentId: string | number;
+	children: MenuVO[];
+	menuId: string | number;
+	menuName: string;
+	orderNum: number;
+	path: string;
+	component: string;
+	queryParam: string;
+	isFrame: string;
+	isCache: string;
+	menuType: MenuTypeEnum;
+	visible: string;
+	status: string;
+	icon: string;
+	remark: string;
+}
+
+export interface MenuForm {
+	parentName?: string;
+	parentId?: string | number;
+	children?: MenuForm[];
+	menuId?: string | number;
+	menuName: string;
+	orderNum: number;
+	path: string;
+	component?: string;
+	queryParam?: string;
+	isFrame?: string;
+	isCache?: string;
+	menuType?: MenuTypeEnum;
+	visible?: string;
+	status?: string;
+	icon?: string;
+	remark?: string;
+	query?: string;
+	perms?: string;
+}
diff --git a/src/api/system/notice.js b/src/api/system/notice.js
deleted file mode 100644
index c274ea5..0000000
--- a/src/api/system/notice.js
+++ /dev/null
@@ -1,44 +0,0 @@
-import request from '@/utils/request'
-
-// 鏌ヨ鍏憡鍒楄〃
-export function listNotice(query) {
-  return request({
-    url: '/system/notice/list',
-    method: 'get',
-    params: query
-  })
-}
-
-// 鏌ヨ鍏憡璇︾粏
-export function getNotice(noticeId) {
-  return request({
-    url: '/system/notice/' + noticeId,
-    method: 'get'
-  })
-}
-
-// 鏂板鍏憡
-export function addNotice(data) {
-  return request({
-    url: '/system/notice',
-    method: 'post',
-    data: data
-  })
-}
-
-// 淇敼鍏憡
-export function updateNotice(data) {
-  return request({
-    url: '/system/notice',
-    method: 'put',
-    data: data
-  })
-}
-
-// 鍒犻櫎鍏憡
-export function delNotice(noticeId) {
-  return request({
-    url: '/system/notice/' + noticeId,
-    method: 'delete'
-  })
-}
\ No newline at end of file
diff --git a/src/api/system/notice/index.ts b/src/api/system/notice/index.ts
new file mode 100644
index 0000000..f08fbab
--- /dev/null
+++ b/src/api/system/notice/index.ts
@@ -0,0 +1,45 @@
+import request from '@/utils/request';
+import { NoticeForm, NoticeQuery, NoticeVO } from './types';
+import { AxiosPromise } from 'axios';
+// 鏌ヨ鍏憡鍒楄〃
+export function listNotice(query: NoticeQuery): AxiosPromise<NoticeVO[]> {
+	return request({
+		url: '/system/notice/list',
+		method: 'get',
+		params: query
+	});
+}
+
+// 鏌ヨ鍏憡璇︾粏
+export function getNotice(noticeId: string | number): AxiosPromise<NoticeVO> {
+	return request({
+		url: '/system/notice/' + noticeId,
+		method: 'get'
+	});
+}
+
+// 鏂板鍏憡
+export function addNotice(data: NoticeForm) {
+	return request({
+		url: '/system/notice',
+		method: 'post',
+		data: data
+	});
+}
+
+// 淇敼鍏憡
+export function updateNotice(data: NoticeForm) {
+	return request({
+		url: '/system/notice',
+		method: 'put',
+		data: data
+	});
+}
+
+// 鍒犻櫎鍏憡
+export function delNotice(noticeId: string | number | Array<string | number>) {
+	return request({
+		url: '/system/notice/' + noticeId,
+		method: 'delete'
+	});
+}
diff --git a/src/api/system/notice/types.ts b/src/api/system/notice/types.ts
new file mode 100644
index 0000000..74a38fc
--- /dev/null
+++ b/src/api/system/notice/types.ts
@@ -0,0 +1,26 @@
+export interface NoticeVO extends BaseEntity {
+	noticeId: number;
+	noticeTitle: string;
+	noticeType: string;
+	noticeContent: string;
+	status: string;
+	remark: string;
+	createByName: string;
+}
+
+export interface NoticeQuery extends PageQuery {
+	noticeTitle: string;
+	createByName: string;
+	status: string;
+	noticeType: string;
+}
+
+export interface NoticeForm {
+	noticeId: number | string | undefined;
+	noticeTitle: string;
+	noticeType: string;
+	noticeContent: string;
+	status: string;
+	remark: string;
+	createByName: string;
+}
diff --git a/src/api/system/oss.js b/src/api/system/oss.js
deleted file mode 100644
index 7d80026..0000000
--- a/src/api/system/oss.js
+++ /dev/null
@@ -1,27 +0,0 @@
-import request from '@/utils/request'
-
-// 鏌ヨOSS瀵硅薄瀛樺偍鍒楄〃
-export function listOss(query) {
-  return request({
-    url: '/system/oss/list',
-    method: 'get',
-    params: query
-  })
-}
-
-// 鏌ヨOSS瀵硅薄鍩轰簬id涓�
-export function listByIds(ossId) {
-  return request({
-    url: '/system/oss/listByIds/' + ossId,
-    method: 'get'
-  })
-}
-
-// 鍒犻櫎OSS瀵硅薄瀛樺偍
-export function delOss(ossId) {
-  return request({
-    url: '/system/oss/' + ossId,
-    method: 'delete'
-  })
-}
-
diff --git a/src/api/system/oss/index.ts b/src/api/system/oss/index.ts
new file mode 100644
index 0000000..9735a4d
--- /dev/null
+++ b/src/api/system/oss/index.ts
@@ -0,0 +1,28 @@
+import request from '@/utils/request';
+import { OssQuery, OssVO } from './types';
+import { AxiosPromise } from 'axios';
+
+// 鏌ヨOSS瀵硅薄瀛樺偍鍒楄〃
+export function listOss(query: OssQuery): AxiosPromise<OssVO[]> {
+	return request({
+		url: '/system/oss/list',
+		method: 'get',
+		params: query
+	});
+}
+
+// 鏌ヨOSS瀵硅薄鍩轰簬id涓�
+export function listByIds(ossId: string | number): AxiosPromise<OssVO[]> {
+	return request({
+		url: '/system/oss/listByIds/' + ossId,
+		method: 'get'
+	});
+}
+
+// 鍒犻櫎OSS瀵硅薄瀛樺偍
+export function delOss(ossId: string | number | Array<string | number>) {
+	return request({
+		url: '/system/oss/' + ossId,
+		method: 'delete'
+	});
+}
diff --git a/src/api/system/oss/types.ts b/src/api/system/oss/types.ts
new file mode 100644
index 0000000..ee30efd
--- /dev/null
+++ b/src/api/system/oss/types.ts
@@ -0,0 +1,22 @@
+export interface OssVO extends BaseEntity {
+	ossId: string | number;
+	fileName: string;
+	originalName: string;
+	fileSuffix: string;
+	url: string;
+	createByName: string;
+	service: string;
+}
+
+export interface OssQuery extends PageQuery {
+	fileName: string;
+	originalName: string;
+	fileSuffix: string;
+	createTime: string;
+	service: string;
+	orderByColumn: string;
+	isAsc: string;
+}
+export interface OssForm {
+	file: undefined | string;
+}
diff --git a/src/api/system/ossConfig.js b/src/api/system/ossConfig.js
deleted file mode 100644
index f290762..0000000
--- a/src/api/system/ossConfig.js
+++ /dev/null
@@ -1,58 +0,0 @@
-import request from '@/utils/request'
-
-// 鏌ヨ瀵硅薄瀛樺偍閰嶇疆鍒楄〃
-export function listOssConfig(query) {
-  return request({
-    url: '/system/oss/config/list',
-    method: 'get',
-    params: query
-  })
-}
-
-// 鏌ヨ瀵硅薄瀛樺偍閰嶇疆璇︾粏
-export function getOssConfig(ossConfigId) {
-  return request({
-    url: '/system/oss/config/' + ossConfigId,
-    method: 'get'
-  })
-}
-
-// 鏂板瀵硅薄瀛樺偍閰嶇疆
-export function addOssConfig(data) {
-  return request({
-    url: '/system/oss/config',
-    method: 'post',
-    data: data
-  })
-}
-
-// 淇敼瀵硅薄瀛樺偍閰嶇疆
-export function updateOssConfig(data) {
-  return request({
-    url: '/system/oss/config',
-    method: 'put',
-    data: data
-  })
-}
-
-// 鍒犻櫎瀵硅薄瀛樺偍閰嶇疆
-export function delOssConfig(ossConfigId) {
-  return request({
-    url: '/system/oss/config/' + ossConfigId,
-    method: 'delete'
-  })
-}
-
-// 瀵硅薄瀛樺偍鐘舵�佷慨鏀�
-export function changeOssConfigStatus(ossConfigId, status, configKey) {
-  const data = {
-    ossConfigId,
-    status,
-    configKey
-  }
-  return request({
-    url: '/system/oss/config/changeStatus',
-    method: 'put',
-    data: data
-  })
-}
diff --git a/src/api/system/ossConfig/index.ts b/src/api/system/ossConfig/index.ts
new file mode 100644
index 0000000..99a7a2f
--- /dev/null
+++ b/src/api/system/ossConfig/index.ts
@@ -0,0 +1,60 @@
+import request from '@/utils/request';
+import { OssConfigForm, OssConfigQuery, OssConfigVO } from './types';
+import { AxiosPromise } from 'axios';
+
+// 鏌ヨ瀵硅薄瀛樺偍閰嶇疆鍒楄〃
+export function listOssConfig(query: OssConfigQuery): AxiosPromise<OssConfigVO[]> {
+	return request({
+		url: '/system/oss/config/list',
+		method: 'get',
+		params: query
+	});
+}
+
+// 鏌ヨ瀵硅薄瀛樺偍閰嶇疆璇︾粏
+export function getOssConfig(ossConfigId: string | number): AxiosPromise<OssConfigVO> {
+	return request({
+		url: '/system/oss/config/' + ossConfigId,
+		method: 'get'
+	});
+}
+
+// 鏂板瀵硅薄瀛樺偍閰嶇疆
+export function addOssConfig(data: OssConfigForm) {
+	return request({
+		url: '/system/oss/config',
+		method: 'post',
+		data: data
+	});
+}
+
+// 淇敼瀵硅薄瀛樺偍閰嶇疆
+export function updateOssConfig(data: OssConfigForm) {
+	return request({
+		url: '/system/oss/config',
+		method: 'put',
+		data: data
+	});
+}
+
+// 鍒犻櫎瀵硅薄瀛樺偍閰嶇疆
+export function delOssConfig(ossConfigId: string | number | Array<string | number>) {
+	return request({
+		url: '/system/oss/config/' + ossConfigId,
+		method: 'delete'
+	});
+}
+
+// 瀵硅薄瀛樺偍鐘舵�佷慨鏀�
+export function changeOssConfigStatus(ossConfigId: string | number, status: string, configKey: string) {
+	const data = {
+		ossConfigId,
+		status,
+		configKey
+	};
+	return request({
+		url: '/system/oss/config/changeStatus',
+		method: 'put',
+		data: data
+	});
+}
diff --git a/src/api/system/ossConfig/types.ts b/src/api/system/ossConfig/types.ts
new file mode 100644
index 0000000..1d1c5c1
--- /dev/null
+++ b/src/api/system/ossConfig/types.ts
@@ -0,0 +1,38 @@
+export interface OssConfigVO extends BaseEntity {
+	ossConfigId: number | string;
+	configKey: string;
+	accessKey: string;
+	secretKey: string;
+	bucketName: string;
+	prefix: string;
+	endpoint: string;
+	domain: string;
+	isHttps: string;
+	region: string;
+	status: string;
+	ext1: string;
+	remark: string;
+	accessPolicy: string;
+}
+
+export interface OssConfigQuery extends PageQuery {
+	configKey: string;
+	bucketName: string;
+	status: string;
+}
+
+export interface OssConfigForm {
+	ossConfigId: string | number | undefined;
+	configKey: string;
+	accessKey: string;
+	secretKey: string;
+	bucketName: string;
+	prefix: string;
+	endpoint: string;
+	domain: string;
+	isHttps: string;
+	accessPolicy: string;
+	region: string;
+	status: string;
+	remark: string;
+}
diff --git a/src/api/system/post.js b/src/api/system/post.js
deleted file mode 100644
index 1a8e9ca..0000000
--- a/src/api/system/post.js
+++ /dev/null
@@ -1,44 +0,0 @@
-import request from '@/utils/request'
-
-// 鏌ヨ宀椾綅鍒楄〃
-export function listPost(query) {
-  return request({
-    url: '/system/post/list',
-    method: 'get',
-    params: query
-  })
-}
-
-// 鏌ヨ宀椾綅璇︾粏
-export function getPost(postId) {
-  return request({
-    url: '/system/post/' + postId,
-    method: 'get'
-  })
-}
-
-// 鏂板宀椾綅
-export function addPost(data) {
-  return request({
-    url: '/system/post',
-    method: 'post',
-    data: data
-  })
-}
-
-// 淇敼宀椾綅
-export function updatePost(data) {
-  return request({
-    url: '/system/post',
-    method: 'put',
-    data: data
-  })
-}
-
-// 鍒犻櫎宀椾綅
-export function delPost(postId) {
-  return request({
-    url: '/system/post/' + postId,
-    method: 'delete'
-  })
-}
diff --git a/src/api/system/post/index.ts b/src/api/system/post/index.ts
new file mode 100644
index 0000000..84d5615
--- /dev/null
+++ b/src/api/system/post/index.ts
@@ -0,0 +1,46 @@
+import request from '@/utils/request';
+import { PostForm, PostQuery, PostVO } from './types';
+import { AxiosPromise } from 'axios';
+
+// 鏌ヨ宀椾綅鍒楄〃
+export function listPost(query: PostQuery): AxiosPromise<PostVO[]> {
+	return request({
+		url: '/system/post/list',
+		method: 'get',
+		params: query
+	});
+}
+
+// 鏌ヨ宀椾綅璇︾粏
+export function getPost(postId: string | number): AxiosPromise<PostVO> {
+	return request({
+		url: '/system/post/' + postId,
+		method: 'get'
+	});
+}
+
+// 鏂板宀椾綅
+export function addPost(data: PostForm) {
+	return request({
+		url: '/system/post',
+		method: 'post',
+		data: data
+	});
+}
+
+// 淇敼宀椾綅
+export function updatePost(data: PostForm) {
+	return request({
+		url: '/system/post',
+		method: 'put',
+		data: data
+	});
+}
+
+// 鍒犻櫎宀椾綅
+export function delPost(postId: string | number | (string | number)[]) {
+	return request({
+		url: '/system/post/' + postId,
+		method: 'delete'
+	});
+}
diff --git a/src/api/system/post/types.ts b/src/api/system/post/types.ts
new file mode 100644
index 0000000..079619a
--- /dev/null
+++ b/src/api/system/post/types.ts
@@ -0,0 +1,23 @@
+export interface PostVO extends BaseEntity {
+	postId: number | string;
+	postCode: string;
+	postName: string;
+	postSort: number;
+	status: string;
+	remark: string;
+}
+
+export interface PostForm {
+	postId: number | string | undefined;
+	postCode: string;
+	postName: string;
+	postSort: number;
+	status: string;
+	remark: string;
+}
+
+export interface PostQuery extends PageQuery {
+	postCode: string;
+	postName: string;
+	status: string;
+}
diff --git a/src/api/system/role.js b/src/api/system/role.js
deleted file mode 100644
index f13e6f4..0000000
--- a/src/api/system/role.js
+++ /dev/null
@@ -1,119 +0,0 @@
-import request from '@/utils/request'
-
-// 鏌ヨ瑙掕壊鍒楄〃
-export function listRole(query) {
-  return request({
-    url: '/system/role/list',
-    method: 'get',
-    params: query
-  })
-}
-
-// 鏌ヨ瑙掕壊璇︾粏
-export function getRole(roleId) {
-  return request({
-    url: '/system/role/' + roleId,
-    method: 'get'
-  })
-}
-
-// 鏂板瑙掕壊
-export function addRole(data) {
-  return request({
-    url: '/system/role',
-    method: 'post',
-    data: data
-  })
-}
-
-// 淇敼瑙掕壊
-export function updateRole(data) {
-  return request({
-    url: '/system/role',
-    method: 'put',
-    data: data
-  })
-}
-
-// 瑙掕壊鏁版嵁鏉冮檺
-export function dataScope(data) {
-  return request({
-    url: '/system/role/dataScope',
-    method: 'put',
-    data: data
-  })
-}
-
-// 瑙掕壊鐘舵�佷慨鏀�
-export function changeRoleStatus(roleId, status) {
-  const data = {
-    roleId,
-    status
-  }
-  return request({
-    url: '/system/role/changeStatus',
-    method: 'put',
-    data: data
-  })
-}
-
-// 鍒犻櫎瑙掕壊
-export function delRole(roleId) {
-  return request({
-    url: '/system/role/' + roleId,
-    method: 'delete'
-  })
-}
-
-// 鏌ヨ瑙掕壊宸叉巿鏉冪敤鎴峰垪琛�
-export function allocatedUserList(query) {
-  return request({
-    url: '/system/role/authUser/allocatedList',
-    method: 'get',
-    params: query
-  })
-}
-
-// 鏌ヨ瑙掕壊鏈巿鏉冪敤鎴峰垪琛�
-export function unallocatedUserList(query) {
-  return request({
-    url: '/system/role/authUser/unallocatedList',
-    method: 'get',
-    params: query
-  })
-}
-
-// 鍙栨秷鐢ㄦ埛鎺堟潈瑙掕壊
-export function authUserCancel(data) {
-  return request({
-    url: '/system/role/authUser/cancel',
-    method: 'put',
-    data: data
-  })
-}
-
-// 鎵归噺鍙栨秷鐢ㄦ埛鎺堟潈瑙掕壊
-export function authUserCancelAll(data) {
-  return request({
-    url: '/system/role/authUser/cancelAll',
-    method: 'put',
-    params: data
-  })
-}
-
-// 鎺堟潈鐢ㄦ埛閫夋嫨
-export function authUserSelectAll(data) {
-  return request({
-    url: '/system/role/authUser/selectAll',
-    method: 'put',
-    params: data
-  })
-}
-
-// 鏍规嵁瑙掕壊ID鏌ヨ閮ㄩ棬鏍戠粨鏋�
-export function deptTreeSelect(roleId) {
-  return request({
-    url: '/system/role/deptTree/' + roleId,
-    method: 'get'
-  })
-}
diff --git a/src/api/system/role/index.ts b/src/api/system/role/index.ts
new file mode 100644
index 0000000..04633de
--- /dev/null
+++ b/src/api/system/role/index.ts
@@ -0,0 +1,144 @@
+import { UserVO } from '@/api/system/user/types';
+import { UserQuery } from '@/api/system/user/types';
+import { AxiosPromise } from 'axios';
+import { RoleQuery, RoleVO, RoleDeptTree } from './types';
+import request from '@/utils/request';
+
+export const listRole = (query: RoleQuery): AxiosPromise<RoleVO[]> => {
+	return request({
+		url: '/system/role/list',
+		method: 'get',
+		params: query
+	});
+};
+
+/**
+ * 鏌ヨ瑙掕壊璇︾粏
+ */
+export const getRole = (roleId: string | number): AxiosPromise<RoleVO> => {
+	return request({
+		url: '/system/role/' + roleId,
+		method: 'get'
+	});
+};
+
+/**
+ * 鏂板瑙掕壊
+ */
+export const addRole = (data: any) => {
+	return request({
+		url: '/system/role',
+		method: 'post',
+		data: data
+	});
+};
+
+/**
+ * 淇敼瑙掕壊
+ * @param data
+ */
+export const updateRole = (data: any) => {
+	return request({
+		url: '/system/role',
+		method: 'put',
+		data: data
+	});
+};
+
+/**
+ * 瑙掕壊鏁版嵁鏉冮檺
+ */
+export const dataScope = (data: any) => {
+	return request({
+		url: '/system/role/dataScope',
+		method: 'put',
+		data: data
+	});
+};
+
+/**
+ * 瑙掕壊鐘舵�佷慨鏀�
+ */
+export const changeRoleStatus = (roleId: string | number, status: string) => {
+	const data = {
+		roleId,
+		status
+	};
+	return request({
+		url: '/system/role/changeStatus',
+		method: 'put',
+		data: data
+	});
+};
+
+/**
+ * 鍒犻櫎瑙掕壊
+ */
+export const delRole = (roleId: Array<string | number> | string | number) => {
+	return request({
+		url: '/system/role/' + roleId,
+		method: 'delete'
+	});
+};
+
+/**
+ * 鏌ヨ瑙掕壊宸叉巿鏉冪敤鎴峰垪琛�
+ */
+export const allocatedUserList = (query: UserQuery): AxiosPromise<UserVO[]> => {
+	return request({
+		url: '/system/role/authUser/allocatedList',
+		method: 'get',
+		params: query
+	});
+};
+
+/**
+ * 鏌ヨ瑙掕壊鏈巿鏉冪敤鎴峰垪琛�
+ */
+export const unallocatedUserList = (query: UserQuery): AxiosPromise<UserVO[]> => {
+	return request({
+		url: '/system/role/authUser/unallocatedList',
+		method: 'get',
+		params: query
+	});
+};
+
+/**
+ * 鍙栨秷鐢ㄦ埛鎺堟潈瑙掕壊
+ */
+export const authUserCancel = (data: any) => {
+	return request({
+		url: '/system/role/authUser/cancel',
+		method: 'put',
+		data: data
+	});
+};
+
+/**
+ * 鎵归噺鍙栨秷鐢ㄦ埛鎺堟潈瑙掕壊
+ */
+export const authUserCancelAll = (data: any) => {
+	return request({
+		url: '/system/role/authUser/cancelAll',
+		method: 'put',
+		params: data
+	});
+};
+
+/**
+ * 鎺堟潈鐢ㄦ埛閫夋嫨
+ */
+export const authUserSelectAll = (data: any) => {
+	return request({
+		url: '/system/role/authUser/selectAll',
+		method: 'put',
+		params: data
+	});
+};
+// 鏍规嵁瑙掕壊ID鏌ヨ閮ㄩ棬鏍戠粨鏋�
+export const deptTreeSelect = (roleId: string | number): AxiosPromise<RoleDeptTree> => {
+	return request({
+		url: '/system/role/deptTree/' + roleId,
+		method: 'get'
+	});
+};
diff --git a/src/api/system/role/types.ts b/src/api/system/role/types.ts
new file mode 100644
index 0000000..df741d7
--- /dev/null
+++ b/src/api/system/role/types.ts
@@ -0,0 +1,52 @@
+/**
+ * 鑿滃崟鏍戝舰缁撴瀯绫诲瀷
+ */
+export interface DeptTreeOption {
+	id: string;
+	label: string;
+	parentId: string;
+	weight: number;
+	children?: DeptTreeOption[];
+}
+
+export interface RoleDeptTree {
+	checkedKeys: string[];
+	depts: DeptTreeOption[];
+}
+
+export interface RoleVO extends BaseEntity {
+	roleId: string | number;
+	roleName: string;
+	roleKey: string;
+	roleSort: number;
+	dataScope: string;
+	menuCheckStrictly: boolean;
+	deptCheckStrictly: boolean;
+	status: string;
+	delFlag: string;
+	remark?: any;
+	flag: boolean;
+	menuIds?: Array<string | number>;
+	deptIds?: Array<string | number>;
+	admin: boolean;
+}
+
+export interface RoleQuery extends PageQuery {
+	roleName: string;
+	roleKey: string;
+	status: string;
+}
+
+export interface RoleForm {
+	roleName: string;
+	roleKey: string;
+	roleSort: number;
+	status: string;
+	menuCheckStrictly: boolean;
+	deptCheckStrictly: boolean;
+	remark: string;
+	dataScope?: number;
+	roleId: string | undefined;
+	menuIds: Array<string | number>;
+	deptIds: Array<string | number>;
+}
diff --git a/src/api/system/tenant.js b/src/api/system/tenant.js
deleted file mode 100644
index 82535af..0000000
--- a/src/api/system/tenant.js
+++ /dev/null
@@ -1,88 +0,0 @@
-import request from '@/utils/request'
-
-// 鏌ヨ绉熸埛鍒楄〃
-export function listTenant(query) {
-  return request({
-    url: '/system/tenant/list',
-    method: 'get',
-    params: query
-  })
-}
-
-// 鏌ヨ绉熸埛璇︾粏
-export function getTenant(id) {
-  return request({
-    url: '/system/tenant/' + id,
-    method: 'get'
-  })
-}
-
-// 鏂板绉熸埛
-export function addTenant(data) {
-  return request({
-    url: '/system/tenant',
-    method: 'post',
-    data: data
-  })
-}
-
-// 淇敼绉熸埛
-export function updateTenant(data) {
-  return request({
-    url: '/system/tenant',
-    method: 'put',
-    data: data
-  })
-}
-
-// 绉熸埛鐘舵�佷慨鏀�
-export function changeTenantStatus(id, tenantId, status) {
-  const data = {
-    id,
-    tenantId,
-    status
-  }
-  return request({
-    url: '/system/tenant/changeStatus',
-    method: 'put',
-    data: data
-  })
-}
-
-// 鍒犻櫎绉熸埛
-export function delTenant(id) {
-  return request({
-    url: '/system/tenant/' + id,
-    method: 'delete'
-  })
-}
-
-// 鍔ㄦ�佸垏鎹㈢鎴�
-export function dynamicTenant(tenantId) {
-  return request({
-    url: '/system/tenant/dynamic/' + tenantId,
-    method: 'get'
-  })
-}
-
-// 娓呴櫎鍔ㄦ�佺鎴�
-export function dynamicClear() {
-  return request({
-    url: '/system/tenant/dynamic/clear',
-    method: 'get'
-  })
-}
-
-// 鍚屾绉熸埛濂楅
-export function syncTenantPackage(tenantId, packageId) {
-  const data = {
-    tenantId,
-    packageId
-  }
-  return request({
-    url: '/system/tenant/syncTenantPackage',
-    method: 'get',
-    params: data
-  })
-}
-
diff --git a/src/api/system/tenant/index.ts b/src/api/system/tenant/index.ts
new file mode 100644
index 0000000..92c78f3
--- /dev/null
+++ b/src/api/system/tenant/index.ts
@@ -0,0 +1,89 @@
+import request from '@/utils/request';
+import { TenantForm, TenantQuery, TenantVO } from './types';
+import { AxiosPromise } from 'axios';
+
+// 鏌ヨ绉熸埛鍒楄〃
+export function listTenant(query: TenantQuery): AxiosPromise<TenantVO[]> {
+	return request({
+		url: '/system/tenant/list',
+		method: 'get',
+		params: query
+	});
+}
+
+// 鏌ヨ绉熸埛璇︾粏
+export function getTenant(id: string | number): AxiosPromise<TenantVO> {
+	return request({
+		url: '/system/tenant/' + id,
+		method: 'get'
+	});
+}
+
+// 鏂板绉熸埛
+export function addTenant(data: TenantForm) {
+	return request({
+		url: '/system/tenant',
+		method: 'post',
+		data: data
+	});
+}
+
+// 淇敼绉熸埛
+export function updateTenant(data: TenantForm) {
+	return request({
+		url: '/system/tenant',
+		method: 'put',
+		data: data
+	});
+}
+
+// 绉熸埛鐘舵�佷慨鏀�
+export function changeTenantStatus(id: string | number, tenantId: string | number, status: string) {
+	const data = {
+		id,
+		tenantId,
+		status
+	};
+	return request({
+		url: '/system/tenant/changeStatus',
+		method: 'put',
+		data: data
+	});
+}
+
+// 鍒犻櫎绉熸埛
+export function delTenant(id: string | number | Array<string | number>) {
+	return request({
+		url: '/system/tenant/' + id,
+		method: 'delete'
+	});
+}
+
+// 鍔ㄦ�佸垏鎹㈢鎴�
+export function dynamicTenant(tenantId: string | number) {
+	return request({
+		url: '/system/tenant/dynamic/' + tenantId,
+		method: 'get'
+	});
+}
+
+// 娓呴櫎鍔ㄦ�佺鎴�
+export function dynamicClear() {
+	return request({
+		url: '/system/tenant/dynamic/clear',
+		method: 'get'
+	});
+}
+
+// 鍚屾绉熸埛濂楅
+export function syncTenantPackage(tenantId: string | number, packageId: string | number) {
+	const data = {
+		tenantId,
+		packageId
+	};
+	return request({
+		url: '/system/tenant/syncTenantPackage',
+		method: 'get',
+		params: data
+	});
+}
diff --git a/src/api/system/tenant/types.ts b/src/api/system/tenant/types.ts
new file mode 100644
index 0000000..92cb991
--- /dev/null
+++ b/src/api/system/tenant/types.ts
@@ -0,0 +1,46 @@
+export interface TenantVO extends BaseEntity {
+	id: number | string;
+	tenantId: number | string;
+	username: string;
+	contactUserName: string;
+	contactPhone: string;
+	companyName: string;
+	licenseNumber: string;
+	address: string;
+	domain: string;
+	intro: string;
+	remark: string;
+	packageId: string | number;
+	expireTime: string;
+	accountCount: number;
+	status: string;
+}
+
+export interface TenantQuery extends PageQuery {
+	tenantId: string | number;
+
+	contactUserName: string;
+
+	contactPhone: string;
+
+	companyName: string;
+}
+
+export interface TenantForm {
+	id: number | string | undefined;
+	tenantId: number | string | undefined;
+	username: string;
+	password: string;
+	contactUserName: string;
+	contactPhone: string;
+	companyName: string;
+	licenseNumber: string;
+	domain: string;
+	address: string;
+	intro: string;
+	remark: string;
+	packageId: string | number;
+	expireTime: string;
+	accountCount: number;
+	status: string;
+}
diff --git a/src/api/system/tenantPackage.js b/src/api/system/tenantPackage.js
deleted file mode 100644
index 91ac8f8..0000000
--- a/src/api/system/tenantPackage.js
+++ /dev/null
@@ -1,58 +0,0 @@
-import request from '@/utils/request'
-
-// 鏌ヨ绉熸埛濂楅鍒楄〃
-export function listTenantPackage(query) {
-  return request({
-    url: '/system/tenant/package/list',
-    method: 'get',
-    params: query
-  })
-}
-
-// 鏌ヨ绉熸埛濂楅璇︾粏
-export function getTenantPackage(packageId) {
-  return request({
-    url: '/system/tenant/package/' + packageId,
-    method: 'get'
-  })
-}
-
-// 鏂板绉熸埛濂楅
-export function addTenantPackage(data) {
-  return request({
-    url: '/system/tenant/package',
-    method: 'post',
-    data: data
-  })
-}
-
-// 淇敼绉熸埛濂楅
-export function updateTenantPackage(data) {
-  return request({
-    url: '/system/tenant/package',
-    method: 'put',
-    data: data
-  })
-}
-
-// 绉熸埛濂楅鐘舵�佷慨鏀�
-export function changePackageStatus(packageId, status) {
-  const data = {
-    packageId,
-    status
-  }
-  return request({
-    url: '/system/tenant/package/changeStatus',
-    method: 'put',
-    data: data
-  })
-}
-
-
-// 鍒犻櫎绉熸埛濂楅
-export function delTenantPackage(packageId) {
-  return request({
-    url: '/system/tenant/package/' + packageId,
-    method: 'delete'
-  })
-}
diff --git a/src/api/system/tenantPackage/index.ts b/src/api/system/tenantPackage/index.ts
new file mode 100644
index 0000000..af26b65
--- /dev/null
+++ b/src/api/system/tenantPackage/index.ts
@@ -0,0 +1,59 @@
+import request from '@/utils/request';
+import { TenantPkgForm, TenantPkgQuery, TenantPkgVO } from './types';
+import { AxiosPromise } from 'axios';
+
+// 鏌ヨ绉熸埛濂楅鍒楄〃
+export function listTenantPackage(query?: TenantPkgQuery): AxiosPromise<TenantPkgVO[]> {
+	return request({
+		url: '/system/tenant/package/list',
+		method: 'get',
+		params: query
+	});
+}
+
+// 鏌ヨ绉熸埛濂楅璇︾粏
+export function getTenantPackage(packageId: string | number): AxiosPromise<TenantPkgVO> {
+	return request({
+		url: '/system/tenant/package/' + packageId,
+		method: 'get'
+	});
+}
+
+// 鏂板绉熸埛濂楅
+export function addTenantPackage(data: TenantPkgForm) {
+	return request({
+		url: '/system/tenant/package',
+		method: 'post',
+		data: data
+	});
+}
+
+// 淇敼绉熸埛濂楅
+export function updateTenantPackage(data: TenantPkgForm) {
+	return request({
+		url: '/system/tenant/package',
+		method: 'put',
+		data: data
+	});
+}
+
+// 绉熸埛濂楅鐘舵�佷慨鏀�
+export function changePackageStatus(packageId: number | string, status: string) {
+	const data = {
+		packageId,
+		status
+	};
+	return request({
+		url: '/system/tenant/package/changeStatus',
+		method: 'put',
+		data: data
+	});
+}
+
+// 鍒犻櫎绉熸埛濂楅
+export function delTenantPackage(packageId: string | number | Array<string | number>) {
+	return request({
+		url: '/system/tenant/package/' + packageId,
+		method: 'delete'
+	});
+}
diff --git a/src/api/system/tenantPackage/types.ts b/src/api/system/tenantPackage/types.ts
new file mode 100644
index 0000000..cee4fc0
--- /dev/null
+++ b/src/api/system/tenantPackage/types.ts
@@ -0,0 +1,22 @@
+export interface TenantPkgVO extends BaseEntity {
+	packageId: string | number;
+	packageName: string;
+	menuIds: string;
+	remark: string;
+	menuCheckStrictly: boolean;
+	status: string;
+}
+
+export interface TenantPkgQuery extends PageQuery {
+	packageName: string;
+	status: string;
+}
+
+export interface TenantPkgForm {
+	packageId: string | number | undefined;
+	packageName: string;
+	menuIds: string;
+	remark: string;
+	menuCheckStrictly: boolean;
+	status: string;
+}
diff --git a/src/api/system/user.js b/src/api/system/user.js
deleted file mode 100644
index f2f76ef..0000000
--- a/src/api/system/user.js
+++ /dev/null
@@ -1,135 +0,0 @@
-import request from '@/utils/request'
-import { parseStrEmpty } from "@/utils/ruoyi";
-
-// 鏌ヨ鐢ㄦ埛鍒楄〃
-export function listUser(query) {
-  return request({
-    url: '/system/user/list',
-    method: 'get',
-    params: query
-  })
-}
-
-// 鏌ヨ鐢ㄦ埛璇︾粏
-export function getUser(userId) {
-  return request({
-    url: '/system/user/' + parseStrEmpty(userId),
-    method: 'get'
-  })
-}
-
-// 鏂板鐢ㄦ埛
-export function addUser(data) {
-  return request({
-    url: '/system/user',
-    method: 'post',
-    data: data
-  })
-}
-
-// 淇敼鐢ㄦ埛
-export function updateUser(data) {
-  return request({
-    url: '/system/user',
-    method: 'put',
-    data: data
-  })
-}
-
-// 鍒犻櫎鐢ㄦ埛
-export function delUser(userId) {
-  return request({
-    url: '/system/user/' + userId,
-    method: 'delete'
-  })
-}
-
-// 鐢ㄦ埛瀵嗙爜閲嶇疆
-export function resetUserPwd(userId, password) {
-  const data = {
-    userId,
-    password
-  }
-  return request({
-    url: '/system/user/resetPwd',
-    method: 'put',
-    data: data
-  })
-}
-
-// 鐢ㄦ埛鐘舵�佷慨鏀�
-export function changeUserStatus(userId, status) {
-  const data = {
-    userId,
-    status
-  }
-  return request({
-    url: '/system/user/changeStatus',
-    method: 'put',
-    data: data
-  })
-}
-
-// 鏌ヨ鐢ㄦ埛涓汉淇℃伅
-export function getUserProfile() {
-  return request({
-    url: '/system/user/profile',
-    method: 'get'
-  })
-}
-
-// 淇敼鐢ㄦ埛涓汉淇℃伅
-export function updateUserProfile(data) {
-  return request({
-    url: '/system/user/profile',
-    method: 'put',
-    data: data
-  })
-}
-
-// 鐢ㄦ埛瀵嗙爜閲嶇疆
-export function updateUserPwd(oldPassword, newPassword) {
-  const data = {
-    oldPassword,
-    newPassword
-  }
-  return request({
-    url: '/system/user/profile/updatePwd',
-    method: 'put',
-    params: data
-  })
-}
-
-// 鐢ㄦ埛澶村儚涓婁紶
-export function uploadAvatar(data) {
-  return request({
-    url: '/system/user/profile/avatar',
-    method: 'post',
-    data: data
-  })
-}
-
-// 鏌ヨ鎺堟潈瑙掕壊
-export function getAuthRole(userId) {
-  return request({
-    url: '/system/user/authRole/' + userId,
-    method: 'get'
-  })
-}
-
-// 淇濆瓨鎺堟潈瑙掕壊
-export function updateAuthRole(data) {
-  return request({
-    url: '/system/user/authRole',
-    method: 'put',
-    params: data
-  })
-}
-
-// 鏌ヨ閮ㄩ棬涓嬫媺鏍戠粨鏋�
-export function deptTreeSelect() {
-  return request({
-    url: '/system/user/deptTree',
-    method: 'get'
-  })
-}
diff --git a/src/api/system/user/index.ts b/src/api/system/user/index.ts
new file mode 100644
index 0000000..d25e16c
--- /dev/null
+++ b/src/api/system/user/index.ts
@@ -0,0 +1,180 @@
+import { DeptVO } from './../dept/types';
+import { RoleVO } from '@/api/system/role/types';
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { UserForm, UserQuery, UserVO, UserInfoVO } from './types';
+import { parseStrEmpty } from '@/utils/ruoyi';
+
+/**
+ * 鏌ヨ鐢ㄦ埛鍒楄〃
+ * @param query
+ */
+export function listUser(query: UserQuery): AxiosPromise<UserVO[]> {
+	return request({
+		url: '/system/user/list',
+		method: 'get',
+		params: query
+	});
+}
+
+/**
+ * 鑾峰彇鐢ㄦ埛璇︽儏
+ * @param userId
+ */
+export function getUser(userId?: string | number): AxiosPromise<UserInfoVO> {
+	return request({
+		url: '/system/user/' + parseStrEmpty(userId),
+		method: 'get'
+	});
+}
+
+/**
+ * 鏂板鐢ㄦ埛
+ */
+export function addUser(data: UserForm) {
+	return request({
+		url: '/system/user',
+		method: 'post',
+		data: data
+	});
+}
+
+/**
+ * 淇敼鐢ㄦ埛
+ */
+export function updateUser(data: UserForm) {
+	return request({
+		url: '/system/user',
+		method: 'put',
+		data: data
+	});
+}
+
+/**
+ * 鍒犻櫎鐢ㄦ埛
+ * @param userId 鐢ㄦ埛ID
+ */
+export function delUser(userId: Array<string | number> | string | number) {
+	return request({
+		url: '/system/user/' + userId,
+		method: 'delete'
+	});
+}
+
+/**
+ * 鐢ㄦ埛瀵嗙爜閲嶇疆
+ * @param userId 鐢ㄦ埛ID
+ * @param password 瀵嗙爜
+ */
+export function resetUserPwd(userId: string | number, password: string) {
+	const data = {
+		userId,
+		password
+	};
+	return request({
+		url: '/system/user/resetPwd',
+		method: 'put',
+		data: data
+	});
+}
+
+/**
+ * 鐢ㄦ埛鐘舵�佷慨鏀�
+ * @param userId 鐢ㄦ埛ID
+ * @param status 鐢ㄦ埛鐘舵��
+ */
+export function changeUserStatus(userId: number | string, status: string) {
+	const data = {
+		userId,
+		status
+	};
+	return request({
+		url: '/system/user/changeStatus',
+		method: 'put',
+		data: data
+	});
+}
+
+/**
+ * 鏌ヨ鐢ㄦ埛涓汉淇℃伅
+ */
+export function getUserProfile(): AxiosPromise<UserInfoVO> {
+	return request({
+		url: '/system/user/profile',
+		method: 'get'
+	});
+}
+
+/**
+ * 淇敼鐢ㄦ埛涓汉淇℃伅
+ * @param data 鐢ㄦ埛淇℃伅
+ */
+export function updateUserProfile(data: UserForm) {
+	return request({
+		url: '/system/user/profile',
+		method: 'put',
+		data: data
+	});
+}
+
+/**
+ * 鐢ㄦ埛瀵嗙爜閲嶇疆
+ * @param oldPassword 鏃у瘑鐮�
+ * @param newPassword 鏂板瘑鐮�
+ */
+export function updateUserPwd(oldPassword: string, newPassword: string) {
+	const data = {
+		oldPassword,
+		newPassword
+	};
+	return request({
+		url: '/system/user/profile/updatePwd',
+		method: 'put',
+		params: data
+	});
+}
+
+/**
+ * 鐢ㄦ埛澶村儚涓婁紶
+ * @param data 澶村儚鏂囦欢
+ */
+export function uploadAvatar(data: FormData) {
+	return request({
+		url: '/system/user/profile/avatar',
+		method: 'post',
+		data: data
+	});
+}
+
+/**
+ * 鏌ヨ鎺堟潈瑙掕壊
+ * @param userId 鐢ㄦ埛ID
+ */
+export function getAuthRole(userId: string | number): AxiosPromise<{ user: UserVO; roles: RoleVO[] }> {
+	return request({
+		url: '/system/user/authRole/' + userId,
+		method: 'get'
+	});
+}
+
+/**
+ * 淇濆瓨鎺堟潈瑙掕壊
+ * @param data 鐢ㄦ埛ID
+ */
+export function updateAuthRole(data: { userId: string; roleIds: string }) {
+	return request({
+		url: '/system/user/authRole',
+		method: 'put',
+		params: data
+	});
+}
+
+/**
+ * 鏌ヨ閮ㄩ棬涓嬫媺鏍戠粨鏋�
+ */
+export function deptTreeSelect(): AxiosPromise<DeptVO[]> {
+	return request({
+		url: '/system/user/deptTree',
+		method: 'get'
+	});
+}
diff --git a/src/api/system/user/types.ts b/src/api/system/user/types.ts
new file mode 100644
index 0000000..997b6d4
--- /dev/null
+++ b/src/api/system/user/types.ts
@@ -0,0 +1,84 @@
+import { DeptVO } from './../dept/types';
+import { RoleVO } from '@/api/system/role/types';
+import { PostVO } from '@/api/system/post/types';
+
+/**
+ * 鐢ㄦ埛淇℃伅
+ */
+export interface UserInfo {
+	user: UserVO;
+	roles: string[];
+	permissions: string[];
+}
+
+/**
+ * 鐢ㄦ埛鏌ヨ瀵硅薄绫诲瀷
+ */
+export interface UserQuery extends PageQuery {
+	userName?: string;
+	phonenumber?: string;
+	status?: string;
+	deptId?: string | number;
+	roleId?: string | number;
+}
+
+/**
+ * 鐢ㄦ埛杩斿洖瀵硅薄
+ */
+export interface UserVO extends BaseEntity {
+	userId: string | number;
+	deptId: number;
+	userName: string;
+	nickName: string;
+	userType: string;
+	email: string;
+	phonenumber: string;
+	sex: string;
+	avatar: string;
+	status: string;
+	delFlag: string;
+	loginIp: string;
+	loginDate: string;
+	remark: string;
+	dept: DeptVO;
+	roles: RoleVO[];
+	roleIds: any;
+	postIds: any;
+	roleId: any;
+	admin: boolean;
+}
+
+/**
+ * 鐢ㄦ埛琛ㄥ崟绫诲瀷
+ */
+export interface UserForm {
+	id?: string;
+	userId?: string;
+	deptId?: number;
+	userName: string;
+	nickName?: string;
+	password: string;
+	phonenumber?: string;
+	email?: string;
+	sex?: string;
+	status: string;
+	remark?: string;
+	postIds: string[];
+	roleIds: string[];
+}
+
+export interface UserInfoVO {
+	user: UserVO;
+	roles: RoleVO[];
+	roleIds: string[];
+	posts: PostVO[];
+	postIds: string[];
+	roleGroup: string;
+	postGroup: string;
+}
+
+export interface ResetPwdForm {
+	oldPassword: string;
+	newPassword: string;
+	confirmPassword: string;
+}
diff --git a/src/api/tool/gen.js b/src/api/tool/gen.js
deleted file mode 100644
index 441791c..0000000
--- a/src/api/tool/gen.js
+++ /dev/null
@@ -1,85 +0,0 @@
-import request from '@/utils/request'
-
-// 鏌ヨ鐢熸垚琛ㄦ暟鎹�
-export function listTable(query) {
-  return request({
-    headers: { 'datasource': localStorage.getItem("dataName") },
-    url: '/tool/gen/list',
-    method: 'get',
-    params: query
-  })
-}
-// 鏌ヨdb鏁版嵁搴撳垪琛�
-export function listDbTable(query) {
-  return request({
-    headers: { 'datasource': localStorage.getItem("dataName") },
-    url: '/tool/gen/db/list',
-    method: 'get',
-    params: query
-  })
-}
-
-// 鏌ヨ琛ㄨ缁嗕俊鎭�
-export function getGenTable(tableId) {
-  return request({
-    headers: { 'datasource': localStorage.getItem("dataName") },
-    url: '/tool/gen/' + tableId,
-    method: 'get'
-  })
-}
-
-// 淇敼浠g爜鐢熸垚淇℃伅
-export function updateGenTable(data) {
-  return request({
-    headers: { 'datasource': localStorage.getItem("dataName") },
-    url: '/tool/gen',
-    method: 'put',
-    data: data
-  })
-}
-
-// 瀵煎叆琛�
-export function importTable(data) {
-  return request({
-    headers: { 'datasource': localStorage.getItem("dataName") },
-    url: '/tool/gen/importTable',
-    method: 'post',
-    params: data
-  })
-}
-
-// 棰勮鐢熸垚浠g爜
-export function previewTable(tableId) {
-  return request({
-    headers: { 'datasource': localStorage.getItem("dataName") },
-    url: '/tool/gen/preview/' + tableId,
-    method: 'get'
-  })
-}
-
-// 鍒犻櫎琛ㄦ暟鎹�
-export function delTable(tableId) {
-  return request({
-    headers: { 'datasource': localStorage.getItem("dataName") },
-    url: '/tool/gen/' + tableId,
-    method: 'delete'
-  })
-}
-
-// 鐢熸垚浠g爜锛堣嚜瀹氫箟璺緞锛�
-export function genCode(tableName) {
-  return request({
-    headers: { 'datasource': localStorage.getItem("dataName") },
-    url: '/tool/gen/genCode/' + tableName,
-    method: 'get'
-  })
-}
-
-// 鍚屾鏁版嵁搴�
-export function synchDb(tableName) {
-  return request({
-    headers: { 'datasource': localStorage.getItem("dataName") },
-    url: '/tool/gen/synchDb/' + tableName,
-    method: 'get'
-  })
-}
diff --git a/src/api/tool/gen/index.ts b/src/api/tool/gen/index.ts
new file mode 100644
index 0000000..9b0f2f0
--- /dev/null
+++ b/src/api/tool/gen/index.ts
@@ -0,0 +1,87 @@
+import request from '@/utils/request';
+import { DbTableQuery, DbTableVO, TableQuery, TableVO, GenTableVO, DbTableForm } from './types';
+import { AxiosPromise } from 'axios';
+
+// 鏌ヨ鐢熸垚琛ㄦ暟鎹�
+export const listTable = (query: TableQuery): AxiosPromise<TableVO[]> => {
+	return request({
+		headers: { datasource: localStorage.getItem('dataName') },
+		url: '/tool/gen/list',
+		method: 'get',
+		params: query
+	});
+};
+// 鏌ヨdb鏁版嵁搴撳垪琛�
+export const listDbTable = (query: DbTableQuery): AxiosPromise<DbTableVO[]> => {
+	return request({
+		headers: { datasource: localStorage.getItem('dataName') },
+		url: '/tool/gen/db/list',
+		method: 'get',
+		params: query
+	});
+};
+
+// 鏌ヨ琛ㄨ缁嗕俊鎭�
+export const getGenTable = (tableId: string | number): AxiosPromise<GenTableVO> => {
+	return request({
+		headers: { datasource: localStorage.getItem('dataName') },
+		url: '/tool/gen/' + tableId,
+		method: 'get'
+	});
+};
+
+// 淇敼浠g爜鐢熸垚淇℃伅
+export const updateGenTable = (data: DbTableForm) => {
+	return request({
+		headers: { datasource: localStorage.getItem('dataName') },
+		url: '/tool/gen',
+		method: 'put',
+		data: data
+	});
+};
+
+// 瀵煎叆琛�
+export const importTable = (data: { tables: string }) => {
+	return request({
+		headers: { datasource: localStorage.getItem('dataName') },
+		url: '/tool/gen/importTable',
+		method: 'post',
+		params: data
+	});
+};
+
+// 棰勮鐢熸垚浠g爜
+export const previewTable = (tableId: string | number) => {
+	return request({
+		headers: { datasource: localStorage.getItem('dataName') },
+		url: '/tool/gen/preview/' + tableId,
+		method: 'get'
+	});
+};
+
+// 鍒犻櫎琛ㄦ暟鎹�
+export const delTable = (tableId: string | number | Array<string | number>) => {
+	return request({
+		headers: { datasource: localStorage.getItem('dataName') },
+		url: '/tool/gen/' + tableId,
+		method: 'delete'
+	});
+};
+
+// 鐢熸垚浠g爜锛堣嚜瀹氫箟璺緞锛�
+export const genCode = (tableName: string) => {
+	return request({
+		headers: { datasource: localStorage.getItem('dataName') },
+		url: '/tool/gen/genCode/' + tableName,
+		method: 'get'
+	});
+};
+
+// 鍚屾鏁版嵁搴�
+export const synchDb = (tableName: string) => {
+	return request({
+		headers: { datasource: localStorage.getItem('dataName') },
+		url: '/tool/gen/synchDb/' + tableName,
+		method: 'get'
+	});
+};
diff --git a/src/api/tool/gen/types.ts b/src/api/tool/gen/types.ts
new file mode 100644
index 0000000..1acb0ff
--- /dev/null
+++ b/src/api/tool/gen/types.ts
@@ -0,0 +1,178 @@
+export interface TableVO extends BaseEntity {
+	createDept: number | string;
+	tableId: string | number;
+	tableName: string;
+	tableComment: string;
+	subTableName?: any;
+	subTableFkName?: any;
+	className: string;
+	tplCategory: string;
+	packageName: string;
+	moduleName: string;
+	businessName: string;
+	functionName: string;
+	functionAuthor: string;
+	genType: string;
+	genPath: string;
+	pkColumn?: any;
+	columns?: any;
+	options?: any;
+	remark?: any;
+	treeCode?: any;
+	treeParentCode?: any;
+	treeName?: any;
+	menuIds?: any;
+	parentMenuId?: any;
+	parentMenuName?: any;
+	tree: boolean;
+	crud: boolean;
+}
+
+export interface TableQuery extends PageQuery {
+	tableName: string;
+	tableComment: string;
+	dataName: string;
+}
+
+export interface DbColumnVO extends BaseEntity {
+	createDept?: any;
+	columnId?: any;
+	tableId?: any;
+	columnName?: any;
+	columnComment?: any;
+	columnType?: any;
+	javaType?: any;
+	javaField?: any;
+	isPk?: any;
+	isIncrement?: any;
+	isRequired?: any;
+	isInsert?: any;
+	isEdit?: any;
+	isList?: any;
+	isQuery?: any;
+	queryType?: any;
+	htmlType?: any;
+	dictType?: any;
+	sort?: any;
+	increment: boolean;
+	capJavaField?: any;
+	usableColumn: boolean;
+	superColumn: boolean;
+	list: boolean;
+	pk: boolean;
+	insert: boolean;
+	edit: boolean;
+	query: boolean;
+	required: boolean;
+}
+
+export interface DbTableVO {
+	createDept?: any;
+	tableId?: any;
+	tableName: string;
+	tableComment: string;
+	subTableName?: any;
+	subTableFkName?: any;
+	className?: any;
+	tplCategory?: any;
+	packageName?: any;
+	moduleName?: any;
+	businessName?: any;
+	functionName?: any;
+	functionAuthor?: any;
+	genType?: any;
+	genPath?: any;
+	pkColumn?: any;
+	columns: DbColumnVO[];
+	options?: any;
+	remark?: any;
+	treeCode?: any;
+	treeParentCode?: any;
+	treeName?: any;
+	menuIds?: any;
+	parentMenuId?: any;
+	parentMenuName?: any;
+	tree: boolean;
+	crud: boolean;
+}
+
+export interface DbTableQuery extends PageQuery {
+	tableName: string;
+	tableComment: string;
+}
+
+export interface GenTableVO {
+	info: DbTableVO;
+	rows: DbColumnVO[];
+	tables: DbTableVO[];
+}
+
+export interface DbColumnForm extends BaseEntity {
+	createDept: number;
+	columnId: string;
+	tableId: string;
+	columnName: string;
+	columnComment: string;
+	columnType: string;
+	javaType: string;
+	javaField: string;
+	isPk: string;
+	isIncrement: string;
+	isRequired: string;
+	isInsert?: any;
+	isEdit: string;
+	isList: string;
+	isQuery?: any;
+	queryType: string;
+	htmlType: string;
+	dictType: string;
+	sort: number;
+	increment: boolean;
+	capJavaField: string;
+	usableColumn: boolean;
+	superColumn: boolean;
+	list: boolean;
+	pk: boolean;
+	insert: boolean;
+	edit: boolean;
+	query: boolean;
+	required: boolean;
+}
+
+export interface DbParamForm {
+	treeCode?: any;
+	treeName?: any;
+	treeParentCode?: any;
+	parentMenuId: string;
+}
+
+export interface DbTableForm extends BaseEntity {
+	createDept?: any;
+	tableId: string | string;
+	tableName: string;
+	tableComment: string;
+	subTableName?: any;
+	subTableFkName?: any;
+	className: string;
+	tplCategory: string;
+	packageName: string;
+	moduleName: string;
+	businessName: string;
+	functionName: string;
+	functionAuthor: string;
+	genType: string;
+	genPath: string;
+	pkColumn?: any;
+	columns: DbColumnForm[];
+	options: string;
+	remark?: any;
+	treeCode?: any;
+	treeParentCode?: any;
+	treeName?: any;
+	menuIds?: any;
+	parentMenuId: string;
+	parentMenuName?: any;
+	tree: boolean;
+	crud: boolean;
+	params: DbParamForm;
+}
diff --git a/src/api/types.ts b/src/api/types.ts
new file mode 100644
index 0000000..5ecfb17
--- /dev/null
+++ b/src/api/types.ts
@@ -0,0 +1,54 @@
+/**
+ * 娉ㄥ唽
+ */
+export type RegisterForm = {
+	tenantId: string;
+	username: string;
+	password: string;
+	confirmPassword?: string;
+	code?: string;
+	uuid?: string;
+	userType?: string;
+};
+
+/**
+ * 鐧诲綍璇锋眰
+ */
+export interface LoginData {
+	tenantId: string;
+	username: string;
+	password: string;
+	rememberMe?: boolean;
+	code?: string;
+	uuid?: string;
+}
+
+/**
+ * 鐧诲綍鍝嶅簲
+ */
+export interface LoginResult {
+	token: string;
+}
+
+/**
+ * 楠岃瘉鐮佽繑鍥�
+ */
+export interface VerifyCodeResult {
+	captchaEnabled: boolean;
+	uuid?: string;
+	img?: string;
+}
+
+/**
+ * 绉熸埛
+ */
+export interface TenantVO {
+	companyName: string;
+	domain: any;
+	tenantId: string;
+}
+
+export interface TenantInfo {
+	tenantEnabled: boolean;
+	voList: TenantVO[];
+}
diff --git a/src/assets/images/login-background.jpg b/src/assets/images/login-background.jpg
index 8a89eb8..fa6408b 100644
--- a/src/assets/images/login-background.jpg
+++ b/src/assets/images/login-background.jpg
Binary files differ
diff --git a/src/assets/images/profile.jpg b/src/assets/images/profile.jpg
index b3a940b..f4fde57 100644
--- a/src/assets/images/profile.jpg
+++ b/src/assets/images/profile.jpg
Binary files differ
diff --git a/src/assets/logo/logo.png b/src/assets/logo/logo.png
index e263760..3f919d8 100644
--- a/src/assets/logo/logo.png
+++ b/src/assets/logo/logo.png
Binary files differ
diff --git a/src/assets/styles/btn.scss b/src/assets/styles/btn.scss
index 3590d8d..a1d854a 100644
--- a/src/assets/styles/btn.scss
+++ b/src/assets/styles/btn.scss
@@ -1,99 +1,99 @@
 @import './variables.module.scss';
 
 @mixin colorBtn($color) {
-  background: $color;
+	background: $color;
 
-  &:hover {
-    color: $color;
+	&:hover {
+		color: $color;
 
-    &:before,
-    &:after {
-      background: $color;
-    }
-  }
+		&:before,
+		&:after {
+			background: $color;
+		}
+	}
 }
 
 .blue-btn {
-  @include colorBtn($blue)
+	@include colorBtn($blue);
 }
 
 .light-blue-btn {
-  @include colorBtn($light-blue)
+	@include colorBtn($light-blue);
 }
 
 .red-btn {
-  @include colorBtn($red)
+	@include colorBtn($red);
 }
 
 .pink-btn {
-  @include colorBtn($pink)
+	@include colorBtn($pink);
 }
 
 .green-btn {
-  @include colorBtn($green)
+	@include colorBtn($green);
 }
 
 .tiffany-btn {
-  @include colorBtn($tiffany)
+	@include colorBtn($tiffany);
 }
 
 .yellow-btn {
-  @include colorBtn($yellow)
+	@include colorBtn($yellow);
 }
 
 .pan-btn {
-  font-size: 14px;
-  color: #fff;
-  padding: 14px 36px;
-  border-radius: 8px;
-  border: none;
-  outline: none;
-  transition: 600ms ease all;
-  position: relative;
-  display: inline-block;
+	font-size: 14px;
+	color: #fff;
+	padding: 14px 36px;
+	border-radius: 8px;
+	border: none;
+	outline: none;
+	transition: 600ms ease all;
+	position: relative;
+	display: inline-block;
 
-  &:hover {
-    background: #fff;
+	&:hover {
+		background: #fff;
 
-    &:before,
-    &:after {
-      width: 100%;
-      transition: 600ms ease all;
-    }
-  }
+		&:before,
+		&:after {
+			width: 100%;
+			transition: 600ms ease all;
+		}
+	}
 
-  &:before,
-  &:after {
-    content: '';
-    position: absolute;
-    top: 0;
-    right: 0;
-    height: 2px;
-    width: 0;
-    transition: 400ms ease all;
-  }
+	&:before,
+	&:after {
+		content: '';
+		position: absolute;
+		top: 0;
+		right: 0;
+		height: 2px;
+		width: 0;
+		transition: 400ms ease all;
+	}
 
-  &::after {
-    right: inherit;
-    top: inherit;
-    left: 0;
-    bottom: 0;
-  }
+	&::after {
+		right: inherit;
+		top: inherit;
+		left: 0;
+		bottom: 0;
+	}
 }
 
 .custom-button {
-  display: inline-block;
-  line-height: 1;
-  white-space: nowrap;
-  cursor: pointer;
-  background: #fff;
-  color: #fff;
-  -webkit-appearance: none;
-  text-align: center;
-  box-sizing: border-box;
-  outline: 0;
-  margin: 0;
-  padding: 10px 15px;
-  font-size: 14px;
-  border-radius: 4px;
+	display: inline-block;
+	line-height: 1;
+	white-space: nowrap;
+	cursor: pointer;
+	background: #fff;
+	color: #fff;
+	-webkit-appearance: none;
+	text-align: center;
+	box-sizing: border-box;
+	outline: 0;
+	margin: 0;
+	padding: 10px 15px;
+	font-size: 14px;
+	border-radius: 4px;
 }
diff --git a/src/assets/styles/element-ui.scss b/src/assets/styles/element-ui.scss
index 0f175f2..cad21e6 100644
--- a/src/assets/styles/element-ui.scss
+++ b/src/assets/styles/element-ui.scss
@@ -1,96 +1,101 @@
 // cover some element-ui styles
 
+.el-divider--horizontal {
+	margin-bottom: 10px;
+	margin-top: 10px;
+}
+
 .el-breadcrumb__inner,
 .el-breadcrumb__inner a {
-  font-weight: 400 !important;
+	font-weight: 400 !important;
 }
 
 .el-upload {
-  input[type="file"] {
-    display: none !important;
-  }
+	input[type='file'] {
+		display: none !important;
+	}
 }
 
 .el-upload__input {
-  display: none;
+	display: none;
 }
 
 .cell {
-  .el-tag {
-    margin-right: 0px;
-  }
+	.el-tag {
+		margin-right: 0px;
+	}
 }
 
 .small-padding {
-  .cell {
-    padding-left: 5px;
-    padding-right: 5px;
-  }
+	.cell {
+		padding-left: 5px;
+		padding-right: 5px;
+	}
 }
 
 .fixed-width {
-  .el-button--mini {
-    padding: 7px 10px;
-    width: 60px;
-  }
+	.el-button--mini {
+		padding: 7px 10px;
+		width: 60px;
+	}
 }
 
 .status-col {
-  .cell {
-    padding: 0 10px;
-    text-align: center;
+	.cell {
+		padding: 0 10px;
+		text-align: center;
 
-    .el-tag {
-      margin-right: 0px;
-    }
-  }
+		.el-tag {
+			margin-right: 0px;
+		}
+	}
 }
 
 // to fixed https://github.com/ElemeFE/element/issues/2461
 .el-dialog {
-  transform: none;
-  left: 0;
-  position: relative;
-  margin: 0 auto;
+	transform: none;
+	left: 0;
+	position: relative;
+	margin: 0 auto;
 }
 
 // refine element ui upload
 .upload-container {
-  .el-upload {
-    width: 100%;
+	.el-upload {
+		width: 100%;
 
-    .el-upload-dragger {
-      width: 100%;
-      height: 200px;
-    }
-  }
+		.el-upload-dragger {
+			width: 100%;
+			height: 200px;
+		}
+	}
 }
 
 // dropdown
 .el-dropdown-menu {
-  a {
-    display: block
-  }
+	a {
+		display: block;
+	}
 }
 
 // fix date-picker ui bug in filter-item
 .el-range-editor.el-input__inner {
-  display: inline-flex !important;
+	display: inline-flex !important;
 }
 
 // to fix el-date-picker css style
 .el-range-separator {
-  box-sizing: content-box;
+	box-sizing: content-box;
 }
 
-.el-menu--collapse
-  > div
-  > .el-submenu
-  > .el-submenu__title
-  .el-submenu__icon-arrow {
-  display: none;
+.el-menu--collapse > div > .el-submenu > .el-submenu__title .el-submenu__icon-arrow {
+	display: none;
 }
 
-.el-dropdown .el-dropdown-link{
-  color: var(--el-color-primary) !important;
-}
\ No newline at end of file
+.el-dropdown .el-dropdown-link {
+	color: var(--el-color-primary) !important;
+}
+
+.el-dialog {
+	border-radius: 3% !important;
+}
diff --git a/src/assets/styles/index.scss b/src/assets/styles/index.scss
index 29dd2b9..db6fe82 100644
--- a/src/assets/styles/index.scss
+++ b/src/assets/styles/index.scss
@@ -5,189 +5,203 @@
 @import './sidebar.scss';
 @import './btn.scss';
 @import './ruoyi.scss';
-
+@import 'animate.css';
+//@import 'element-plus/dist/index.css';
 body {
-  height: 100%;
-  margin: 0;
-  -moz-osx-font-smoothing: grayscale;
-  -webkit-font-smoothing: antialiased;
-  text-rendering: optimizeLegibility;
-  font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif;
+	height: 100%;
+	margin: 0;
+	-moz-osx-font-smoothing: grayscale;
+	-webkit-font-smoothing: antialiased;
+	text-rendering: optimizeLegibility;
+	font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif;
 }
 
 label {
-  font-weight: 700;
+	font-weight: 700;
 }
 
 html {
-  height: 100%;
-  box-sizing: border-box;
+	height: 100%;
+	box-sizing: border-box;
 }
 
 #app {
-  height: 100%;
+	height: 100%;
 }
 
 *,
 *:before,
 *:after {
-  box-sizing: inherit;
+	box-sizing: inherit;
 }
 
 .no-padding {
-  padding: 0px !important;
+	padding: 0px !important;
 }
 
 .padding-content {
-  padding: 4px 0;
+	padding: 4px 0;
 }
 
 a:focus,
 a:active {
-  outline: none;
+	outline: none;
 }
 
 a,
 a:focus,
 a:hover {
-  cursor: pointer;
-  color: inherit;
-  text-decoration: none;
+	cursor: pointer;
+	color: inherit;
+	text-decoration: none;
 }
 
 div:focus {
-  outline: none;
+	outline: none;
 }
 
 .fr {
-  float: right;
+	float: right;
 }
 
 .fl {
-  float: left;
+	float: left;
 }
 
 .pr-5 {
-  padding-right: 5px;
+	padding-right: 5px;
 }
 
 .pl-5 {
-  padding-left: 5px;
+	padding-left: 5px;
 }
 
 .block {
-  display: block;
+	display: block;
 }
 
 .pointer {
-  cursor: pointer;
+	cursor: pointer;
 }
 
 .inlineBlock {
-  display: block;
+	display: block;
 }
 
 .clearfix {
-  &:after {
-    visibility: hidden;
-    display: block;
-    font-size: 0;
-    content: " ";
-    clear: both;
-    height: 0;
-  }
+	&:after {
+		visibility: hidden;
+		display: block;
+		font-size: 0;
+		content: ' ';
+		clear: both;
+		height: 0;
+	}
 }
 
 aside {
-  background: #eef1f6;
-  padding: 8px 24px;
-  margin-bottom: 20px;
-  border-radius: 2px;
-  display: block;
-  line-height: 32px;
-  font-size: 16px;
-  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
-  color: #2c3e50;
-  -webkit-font-smoothing: antialiased;
-  -moz-osx-font-smoothing: grayscale;
+	background: #eef1f6;
+	padding: 8px 24px;
+	margin-bottom: 20px;
+	border-radius: 2px;
+	display: block;
+	line-height: 32px;
+	font-size: 16px;
+	font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
+		sans-serif;
+	color: #2c3e50;
+	-webkit-font-smoothing: antialiased;
+	-moz-osx-font-smoothing: grayscale;
 
-  a {
-    color: #337ab7;
-    cursor: pointer;
+	a {
+		color: #337ab7;
+		cursor: pointer;
 
-    &:hover {
-      color: rgb(32, 160, 255);
-    }
-  }
+		&:hover {
+			color: rgb(32, 160, 255);
+		}
+	}
 }
 
 //main-container鍏ㄥ眬鏍峰紡
 .app-container {
-  padding: 20px;
+	padding: 20px;
+}
+// search闈㈡澘鏍峰紡
+.panel,
+.search {
+	margin-bottom: 0.75rem;
+	border-radius: 0.25rem;
+	border: 1px solid var(--el-border-color-light);
+	background-color: var(--el-bg-color-overlay);
+	padding: 0.75rem;
+	--tw-shadow: var(--el-box-shadow-light);
+	--tw-shadow-colored: var(--el-box-shadow-light);
+	box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
 }
 
 .components-container {
-  margin: 30px 50px;
-  position: relative;
+	margin: 30px 50px;
+	position: relative;
 }
 
 .pagination-container {
-  margin-top: 30px;
+	margin-top: 30px;
 }
 
 .text-center {
-  text-align: center
+	text-align: center;
 }
 
 .sub-navbar {
-  height: 50px;
-  line-height: 50px;
-  position: relative;
-  width: 100%;
-  text-align: right;
-  padding-right: 20px;
-  transition: 600ms ease position;
-  background: linear-gradient(90deg, rgba(32, 182, 249, 1) 0%, rgba(32, 182, 249, 1) 0%, rgba(33, 120, 241, 1) 100%, rgba(33, 120, 241, 1) 100%);
+	height: 50px;
+	line-height: 50px;
+	position: relative;
+	width: 100%;
+	text-align: right;
+	padding-right: 20px;
+	transition: 600ms ease position;
+	background: linear-gradient(90deg, rgba(32, 182, 249, 1) 0%, rgba(32, 182, 249, 1) 0%, rgba(33, 120, 241, 1) 100%, rgba(33, 120, 241, 1) 100%);
 
-  .subtitle {
-    font-size: 20px;
-    color: #fff;
-  }
+	.subtitle {
+		font-size: 20px;
+		color: #fff;
+	}
 
-  &.draft {
-    background: #d0d0d0;
-  }
+	&.draft {
+		background: #d0d0d0;
+	}
 
-  &.deleted {
-    background: #d0d0d0;
-  }
+	&.deleted {
+		background: #d0d0d0;
+	}
 }
 
 .link-type,
 .link-type:focus {
-  color: #337ab7;
-  cursor: pointer;
+	color: #337ab7;
+	cursor: pointer;
 
-  &:hover {
-    color: rgb(32, 160, 255);
-  }
+	&:hover {
+		color: rgb(32, 160, 255);
+	}
 }
 
 .filter-container {
-  padding-bottom: 10px;
+	padding-bottom: 10px;
 
-  .filter-item {
-    display: inline-block;
-    vertical-align: middle;
-    margin-bottom: 10px;
-  }
+	.filter-item {
+		display: inline-block;
+		vertical-align: middle;
+		margin-bottom: 10px;
+	}
 }
 
 //refine vue-multiselect plugin
 .multiselect {
-  line-height: 16px;
+	line-height: 16px;
 }
 
 .multiselect--active {
-  z-index: 1000 !important;
+	z-index: 1000 !important;
 }
diff --git a/src/assets/styles/mixin.scss b/src/assets/styles/mixin.scss
index 06fa061..b5e121c 100644
--- a/src/assets/styles/mixin.scss
+++ b/src/assets/styles/mixin.scss
@@ -1,66 +1,60 @@
 @mixin clearfix {
-  &:after {
-    content: "";
-    display: table;
-    clear: both;
-  }
+	&:after {
+		content: '';
+		display: table;
+		clear: both;
+	}
 }
 
 @mixin scrollBar {
-  &::-webkit-scrollbar-track-piece {
-    background: #d3dce6;
-  }
+	&::-webkit-scrollbar-track-piece {
+		background: #d3dce6;
+	}
 
-  &::-webkit-scrollbar {
-    width: 6px;
-  }
+	&::-webkit-scrollbar {
+		width: 6px;
+	}
 
-  &::-webkit-scrollbar-thumb {
-    background: #99a9bf;
-    border-radius: 20px;
-  }
+	&::-webkit-scrollbar-thumb {
+		background: #99a9bf;
+		border-radius: 20px;
+	}
 }
 
 @mixin relative {
-  position: relative;
-  width: 100%;
-  height: 100%;
+	position: relative;
+	width: 100%;
+	height: 100%;
 }
 
 @mixin pct($pct) {
-  width: #{$pct};
-  position: relative;
-  margin: 0 auto;
+	width: #{$pct};
+	position: relative;
+	margin: 0 auto;
 }
 
 @mixin triangle($width, $height, $color, $direction) {
-  $width: $width/2;
-  $color-border-style: $height solid $color;
-  $transparent-border-style: $width solid transparent;
-  height: 0;
-  width: 0;
+	$width: $width/2;
+	$color-border-style: $height solid $color;
+	$transparent-border-style: $width solid transparent;
+	height: 0;
+	width: 0;
 
-  @if $direction==up {
-    border-bottom: $color-border-style;
-    border-left: $transparent-border-style;
-    border-right: $transparent-border-style;
-  }
-
-  @else if $direction==right {
-    border-left: $color-border-style;
-    border-top: $transparent-border-style;
-    border-bottom: $transparent-border-style;
-  }
-
-  @else if $direction==down {
-    border-top: $color-border-style;
-    border-left: $transparent-border-style;
-    border-right: $transparent-border-style;
-  }
-
-  @else if $direction==left {
-    border-right: $color-border-style;
-    border-top: $transparent-border-style;
-    border-bottom: $transparent-border-style;
-  }
+	@if $direction==up {
+		border-bottom: $color-border-style;
+		border-left: $transparent-border-style;
+		border-right: $transparent-border-style;
+	} @else if $direction==right {
+		border-left: $color-border-style;
+		border-top: $transparent-border-style;
+		border-bottom: $transparent-border-style;
+	} @else if $direction==down {
+		border-top: $color-border-style;
+		border-left: $transparent-border-style;
+		border-right: $transparent-border-style;
+	} @else if $direction==left {
+		border-right: $color-border-style;
+		border-top: $transparent-border-style;
+		border-bottom: $transparent-border-style;
+	}
 }
diff --git a/src/assets/styles/ruoyi.scss b/src/assets/styles/ruoyi.scss
index 88f3702..bb2ca5c 100644
--- a/src/assets/styles/ruoyi.scss
+++ b/src/assets/styles/ruoyi.scss
@@ -1,9 +1,9 @@
- /**
+/**
  * 閫氱敤css鏍峰紡甯冨眬澶勭悊
  * Copyright (c) 2019 ruoyi
  */
 
- /** 鍩虹閫氱敤 **/
+/** 鍩虹閫氱敤 **/
 .pt5 {
 	padding-top: 5px;
 }
@@ -53,7 +53,18 @@
 	margin-left: 20px;
 }
 
-.h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 {
+.h1,
+.h2,
+.h3,
+.h4,
+.h5,
+.h6,
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
 	font-family: inherit;
 	font-weight: 500;
 	line-height: 1.1;
@@ -75,7 +86,8 @@
 }
 
 .el-table {
-	.el-table__header-wrapper, .el-table__fixed-header-wrapper {
+	.el-table__header-wrapper,
+	.el-table__fixed-header-wrapper {
 		th {
 			word-break: break-word;
 			background-color: #f8f8f9 !important;
@@ -85,7 +97,7 @@
 		}
 	}
 	.el-table__body-wrapper {
-		.el-button [class*="el-icon-"] + span {
+		.el-button [class*='el-icon-'] + span {
 			margin-left: 1px;
 		}
 	}
@@ -93,11 +105,11 @@
 
 /** 琛ㄥ崟甯冨眬 **/
 .form-header {
-    font-size:15px;
-	color:#6379bb;
-	border-bottom:1px solid #ddd;
-	margin:8px 10px 25px 10px;
-	padding-bottom:5px
+	font-size: 15px;
+	color: #6379bb;
+	border-bottom: 1px solid #ddd;
+	margin: 8px 10px 25px 10px;
+	padding-bottom: 5px;
 }
 
 /** 琛ㄦ牸甯冨眬 **/
@@ -111,25 +123,25 @@
 
 /* tree border */
 .tree-border {
-    margin-top: 5px;
-    border: 1px solid #e5e6e7;
-    background: #FFFFFF none;
-    border-radius:4px;
-    width: 100%;
+	margin-top: 5px;
+	border: 1px solid #e5e6e7;
+	background: #ffffff none;
+	border-radius: 4px;
+	width: 100%;
 }
 
 .pagination-container .el-pagination {
-	right: 0;
-	position: absolute;
+	//right: 0;
+	//position: absolute;
 }
 
-@media ( max-width : 768px) {
-  .pagination-container .el-pagination > .el-pagination__jump {
-    display: none !important;
-  }
-  .pagination-container .el-pagination > .el-pagination__sizes {
-    display: none !important;
-  }
+@media (max-width: 768px) {
+	.pagination-container .el-pagination > .el-pagination__jump {
+		display: none !important;
+	}
+	.pagination-container .el-pagination > .el-pagination__sizes {
+		display: none !important;
+	}
 }
 
 .el-table .fixed-width .el-button--small {
@@ -141,11 +153,12 @@
 /** 琛ㄦ牸鏇村鎿嶄綔涓嬫媺鏍峰紡 */
 .el-table .el-dropdown-link {
 	cursor: pointer;
-	color: #409EFF;
+	color: #409eff;
 	margin-left: 10px;
 }
 
-.el-table .el-dropdown, .el-icon-arrow-down {
+.el-table .el-dropdown,
+.el-icon-arrow-down {
 	font-size: 12px;
 }
 
@@ -196,22 +209,22 @@
 /* button color */
 .el-button--cyan.is-active,
 .el-button--cyan:active {
-  background: #20B2AA;
-  border-color: #20B2AA;
-  color: #FFFFFF;
+	background: #20b2aa;
+	border-color: #20b2aa;
+	color: #ffffff;
 }
 
 .el-button--cyan:focus,
 .el-button--cyan:hover {
-  background: #48D1CC;
-  border-color: #48D1CC;
-  color: #FFFFFF;
+	background: #48d1cc;
+	border-color: #48d1cc;
+	color: #ffffff;
 }
 
 .el-button--cyan {
-  background-color: #20B2AA;
-  border-color: #20B2AA;
-  color: #FFFFFF;
+	background-color: #20b2aa;
+	border-color: #20b2aa;
+	color: #ffffff;
 }
 
 /* text color */
@@ -265,10 +278,10 @@
 }
 
 /* 鎷栨嫿鍒楁牱寮� */
-.sortable-ghost{
-	opacity: .8;
-	color: #fff!important;
-	background: #42b983!important;
+.sortable-ghost {
+	opacity: 0.8;
+	color: #fff !important;
+	background: #42b983 !important;
 }
 
 /* 琛ㄦ牸鍙充晶宸ュ叿鏍忔牱寮� */
diff --git a/src/assets/styles/sidebar.scss b/src/assets/styles/sidebar.scss
index 0808812..abe6f3a 100644
--- a/src/assets/styles/sidebar.scss
+++ b/src/assets/styles/sidebar.scss
@@ -1,238 +1,236 @@
 #app {
+	.main-container {
+		min-height: 100%;
+		transition: margin-left 0.28s;
+		margin-left: $base-sidebar-width;
+		position: relative;
+	}
 
-  .main-container {
-    min-height: 100%;
-    transition: margin-left .28s;
-    margin-left: $base-sidebar-width;
-    position: relative;
-  }
+	.sidebarHide {
+		margin-left: 0 !important;
+	}
 
-  .sidebarHide {
-    margin-left: 0!important;
-  }
+	.sidebar-container {
+		-webkit-transition: width 0.28s;
+		transition: width 0.28s;
+		width: $base-sidebar-width !important;
+		background-color: $base-menu-background;
+		height: 100%;
+		position: fixed;
+		font-size: 0px;
+		top: 0;
+		bottom: 0;
+		left: 0;
+		z-index: 1001;
+		overflow: hidden;
+		-webkit-box-shadow: 2px 0 6px rgba(0, 21, 41, 0.35);
+		box-shadow: 2px 0 6px rgba(0, 21, 41, 0.35);
 
-  .sidebar-container {
-    -webkit-transition: width .28s;
-    transition: width 0.28s;
-    width: $base-sidebar-width !important;
-    background-color: $base-menu-background;
-    height: 100%;
-    position: fixed;
-    font-size: 0px;
-    top: 0;
-    bottom: 0;
-    left: 0;
-    z-index: 1001;
-    overflow: hidden;
-    -webkit-box-shadow: 2px 0 6px rgba(0,21,41,.35);
-    box-shadow: 2px 0 6px rgba(0,21,41,.35);
+		// reset element-ui css
+		.horizontal-collapse-transition {
+			transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out;
+		}
 
-    // reset element-ui css
-    .horizontal-collapse-transition {
-      transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out;
-    }
+		.scrollbar-wrapper {
+			overflow-x: hidden !important;
+		}
 
-    .scrollbar-wrapper {
-      overflow-x: hidden !important;
-    }
+		.el-scrollbar__bar.is-vertical {
+			right: 0px;
+		}
 
-    .el-scrollbar__bar.is-vertical {
-      right: 0px;
-    }
+		.el-scrollbar {
+			height: 100%;
+		}
 
-    .el-scrollbar {
-      height: 100%;
-    }
+		&.has-logo {
+			.el-scrollbar {
+				height: calc(100% - 50px);
+			}
+		}
 
-    &.has-logo {
-      .el-scrollbar {
-        height: calc(100% - 50px);
-      }
-    }
+		.is-horizontal {
+			display: none;
+		}
 
-    .is-horizontal {
-      display: none;
-    }
+		a {
+			display: inline-block;
+			width: 100%;
+			overflow: hidden;
+		}
 
-    a {
-      display: inline-block;
-      width: 100%;
-      overflow: hidden;
-    }
+		.svg-icon {
+			margin-right: 16px;
+		}
 
-    .svg-icon {
-      margin-right: 16px;
-    }
+		.el-menu {
+			border: none;
+			height: 100%;
+			width: 100% !important;
+		}
 
-    .el-menu {
-      border: none;
-      height: 100%;
-      width: 100% !important;
-    }
+		.el-menu-item,
+		.menu-title {
+			overflow: hidden !important;
+			text-overflow: ellipsis !important;
+			white-space: nowrap !important;
+		}
 
-    .el-menu-item, .menu-title {
-      overflow: hidden !important;
-      text-overflow: ellipsis !important;
-      white-space: nowrap !important;
-    }
+		.el-menu-item .el-menu-tooltip__trigger {
+			display: inline-block !important;
+		}
 
-    .el-menu-item .el-menu-tooltip__trigger {
-      display: inline-block !important;
-    }
+		// menu hover
+		.sub-menu-title-noDropdown,
+		.el-sub-menu__title {
+			&:hover {
+				background-color: rgba(0, 0, 0, 0.06) !important;
+			}
+		}
 
-    // menu hover
-    .sub-menu-title-noDropdown,
-    .el-sub-menu__title {
-      &:hover {
-        background-color: rgba(0, 0, 0, 0.06) !important;
-      }
-    }
+		& .theme-dark .is-active > .el-sub-menu__title {
+			color: $base-menu-color-active !important;
+		}
 
-    & .theme-dark .is-active > .el-sub-menu__title {
-      color: $base-menu-color-active !important;
-    }
+		& .nest-menu .el-sub-menu > .el-sub-menu__title,
+		& .el-sub-menu .el-menu-item {
+			min-width: $base-sidebar-width !important;
 
-    & .nest-menu .el-sub-menu>.el-sub-menu__title,
-    & .el-sub-menu .el-menu-item {
-      min-width: $base-sidebar-width !important;
+			&:hover {
+				background-color: rgba(0, 0, 0, 0.06) !important;
+			}
+		}
 
-      &:hover {
-        background-color: rgba(0, 0, 0, 0.06) !important;
-      }
-    }
+		& .theme-dark .nest-menu .el-sub-menu > .el-sub-menu__title,
+		& .theme-dark .el-sub-menu .el-menu-item {
+			background-color: $base-sub-menu-background !important;
 
-    & .theme-dark .nest-menu .el-sub-menu>.el-sub-menu__title,
-    & .theme-dark .el-sub-menu .el-menu-item {
-      background-color: $base-sub-menu-background !important;
+			&:hover {
+				background-color: $base-sub-menu-hover !important;
+			}
+		}
+	}
 
-      &:hover {
-        background-color: $base-sub-menu-hover !important;
-      }
-    }
-  }
+	.hideSidebar {
+		.sidebar-container {
+			width: 54px !important;
+		}
 
-  .hideSidebar {
-    .sidebar-container {
-      width: 54px !important;
-    }
+		.main-container {
+			margin-left: 54px;
+		}
 
-    .main-container {
-      margin-left: 54px;
-    }
+		.sub-menu-title-noDropdown {
+			padding: 0 !important;
+			position: relative;
 
-    .sub-menu-title-noDropdown {
-      padding: 0 !important;
-      position: relative;
+			.el-tooltip {
+				padding: 0 !important;
 
-      .el-tooltip {
-        padding: 0 !important;
+				.svg-icon {
+					margin-left: 20px;
+				}
+			}
+		}
 
-        .svg-icon {
-          margin-left: 20px;
-        }
-      }
-    }
+		.el-sub-menu {
+			overflow: hidden;
 
-    .el-sub-menu {
-      overflow: hidden;
+			& > .el-sub-menu__title {
+				padding: 0 !important;
 
-      &>.el-sub-menu__title {
-        padding: 0 !important;
+				.svg-icon {
+					margin-left: 20px;
+				}
+			}
+		}
 
-        .svg-icon {
-          margin-left: 20px;
-        }
+		.el-menu--collapse {
+			.el-sub-menu {
+				& > .el-sub-menu__title {
+					& > span {
+						height: 0;
+						width: 0;
+						overflow: hidden;
+						visibility: hidden;
+						display: inline-block;
+					}
+					& > i {
+						height: 0;
+						width: 0;
+						overflow: hidden;
+						visibility: hidden;
+						display: inline-block;
+					}
+				}
+			}
+		}
+	}
 
-      }
-    }
+	.el-menu--collapse .el-menu .el-sub-menu {
+		min-width: $base-sidebar-width !important;
+	}
 
-    .el-menu--collapse {
-      .el-sub-menu {
-        &>.el-sub-menu__title {
-          &>span {
-            height: 0;
-            width: 0;
-            overflow: hidden;
-            visibility: hidden;
-            display: inline-block;
-          }
-          &>i {
-            height: 0;
-            width: 0;
-            overflow: hidden;
-            visibility: hidden;
-            display: inline-block;
-          }
-        }
-      }
-    }
-  }
+	// mobile responsive
+	.mobile {
+		.main-container {
+			margin-left: 0px;
+		}
 
-  .el-menu--collapse .el-menu .el-sub-menu {
-    min-width: $base-sidebar-width !important;
-  }
+		.sidebar-container {
+			transition: transform 0.28s;
+			width: $base-sidebar-width !important;
+		}
 
-  // mobile responsive
-  .mobile {
-    .main-container {
-      margin-left: 0px;
-    }
+		&.hideSidebar {
+			.sidebar-container {
+				pointer-events: none;
+				transition-duration: 0.3s;
+				transform: translate3d(-$base-sidebar-width, 0, 0);
+			}
+		}
+	}
 
-    .sidebar-container {
-      transition: transform .28s;
-      width: $base-sidebar-width !important;
-    }
-
-    &.hideSidebar {
-      .sidebar-container {
-        pointer-events: none;
-        transition-duration: 0.3s;
-        transform: translate3d(-$base-sidebar-width, 0, 0);
-      }
-    }
-  }
-
-  .withoutAnimation {
-
-    .main-container,
-    .sidebar-container {
-      transition: none;
-    }
-  }
+	.withoutAnimation {
+		.main-container,
+		.sidebar-container {
+			transition: none;
+		}
+	}
 }
 
 // when menu collapsed
 .el-menu--vertical {
-  &>.el-menu {
-    .svg-icon {
-      margin-right: 16px;
-    }
-  }
+	& > .el-menu {
+		.svg-icon {
+			margin-right: 16px;
+		}
+	}
 
-  .nest-menu .el-sub-menu>.el-sub-menu__title,
-  .el-menu-item {
-    &:hover {
-      // you can use $sub-menuHover
-      background-color: rgba(0, 0, 0, 0.06) !important;
-    }
-  }
+	.nest-menu .el-sub-menu > .el-sub-menu__title,
+	.el-menu-item {
+		&:hover {
+			// you can use $sub-menuHover
+			background-color: rgba(0, 0, 0, 0.06) !important;
+		}
+	}
 
-  // the scroll bar appears when the sub-menu is too long
-  >.el-menu--popup {
-    max-height: 100vh;
-    overflow-y: auto;
+	// the scroll bar appears when the sub-menu is too long
+	> .el-menu--popup {
+		max-height: 100vh;
+		overflow-y: auto;
 
-    &::-webkit-scrollbar-track-piece {
-      background: #d3dce6;
-    }
+		&::-webkit-scrollbar-track-piece {
+			background: #d3dce6;
+		}
 
-    &::-webkit-scrollbar {
-      width: 6px;
-    }
+		&::-webkit-scrollbar {
+			width: 6px;
+		}
 
-    &::-webkit-scrollbar-thumb {
-      background: #99a9bf;
-      border-radius: 20px;
-    }
-  }
+		&::-webkit-scrollbar-thumb {
+			background: #99a9bf;
+			border-radius: 20px;
+		}
+	}
 }
diff --git a/src/assets/styles/transition.scss b/src/assets/styles/transition.scss
index eb49895..1e43947 100644
--- a/src/assets/styles/transition.scss
+++ b/src/assets/styles/transition.scss
@@ -3,51 +3,51 @@
 /* fade */
 .fade-enter-active,
 .fade-leave-active {
-  transition: opacity 0.28s;
+	transition: opacity 0.28s;
 }
 
 .fade-enter,
 .fade-leave-active {
-  opacity: 0;
+	opacity: 0;
 }
 
 /* fade-transform */
 .fade-transform--move,
 .fade-transform-leave-active,
 .fade-transform-enter-active {
-  transition: all .5s;
+	transition: all 0.5s;
 }
 
 .fade-transform-leave-active {
-  position: absolute;
+	position: absolute;
 }
 
 .fade-transform-enter {
-  opacity: 0;
-  transform: translateX(-30px);
+	opacity: 0;
+	transform: translateX(-30px);
 }
 
 .fade-transform-leave-to {
-  opacity: 0;
-  transform: translateX(30px);
+	opacity: 0;
+	transform: translateX(30px);
 }
 
 /* breadcrumb transition */
 .breadcrumb-enter-active,
 .breadcrumb-leave-active {
-  transition: all .5s;
+	transition: all 0.5s;
 }
 
 .breadcrumb-enter,
 .breadcrumb-leave-active {
-  opacity: 0;
-  transform: translateX(20px);
+	opacity: 0;
+	transform: translateX(20px);
 }
 
 .breadcrumb-move {
-  transition: all .5s;
+	transition: all 0.5s;
 }
 
 .breadcrumb-leave-active {
-  position: absolute;
+	position: absolute;
 }
diff --git a/src/assets/styles/variables.module.scss b/src/assets/styles/variables.module.scss
index 3dbfaa7..d43b407 100644
--- a/src/assets/styles/variables.module.scss
+++ b/src/assets/styles/variables.module.scss
@@ -1,12 +1,12 @@
 // base color
 $blue: #324157;
-$light-blue: #3A71A8;
-$red: #C03639;
-$pink: #E65D6E;
-$green: #30B08F;
-$tiffany: #4AB7BD;
-$yellow: #FEC171;
-$panGreen: #30B08F;
+$light-blue: #3a71a8;
+$red: #c03639;
+$pink: #e65d6e;
+$green: #30b08f;
+$tiffany: #4ab7bd;
+$yellow: #fec171;
+$panGreen: #30b08f;
 
 // 榛樿鑿滃崟涓婚椋庢牸
 $base-menu-color: #bfcbd9;
@@ -36,10 +36,10 @@
 $base-sub-menu-hover:#001528;
 */
 
-$--color-primary: #409EFF;
-$--color-success: #67C23A;
-$--color-warning: #E6A23C;
-$--color-danger: #F56C6C;
+$--color-primary: #409eff;
+$--color-success: #67c23a;
+$--color-warning: #e6a23c;
+$--color-danger: #f56c6c;
 $--color-info: #909399;
 
 $base-sidebar-width: 200px;
@@ -47,19 +47,19 @@
 // the :export directive is the magic sauce for webpack
 // https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass
 :export {
-  menuColor: $base-menu-color;
-  menuLightColor: $base-menu-light-color;
-  menuColorActive: $base-menu-color-active;
-  menuBackground: $base-menu-background;
-  menuLightBackground: $base-menu-light-background;
-  subMenuBackground: $base-sub-menu-background;
-  subMenuHover: $base-sub-menu-hover;
-  sideBarWidth: $base-sidebar-width;
-  logoTitleColor: $base-logo-title-color;
-  logoLightTitleColor: $base-logo-light-title-color;
-  primaryColor: $--color-primary;
-  successColor: $--color-success;
-  dangerColor: $--color-danger;
-  infoColor: $--color-info;
-  warningColor: $--color-warning;
+	menuColor: $base-menu-color;
+	menuLightColor: $base-menu-light-color;
+	menuColorActive: $base-menu-color-active;
+	menuBackground: $base-menu-background;
+	menuLightBackground: $base-menu-light-background;
+	subMenuBackground: $base-sub-menu-background;
+	subMenuHover: $base-sub-menu-hover;
+	sideBarWidth: $base-sidebar-width;
+	logoTitleColor: $base-logo-title-color;
+	logoLightTitleColor: $base-logo-light-title-color;
+	primaryColor: $--color-primary;
+	successColor: $--color-success;
+	dangerColor: $--color-danger;
+	infoColor: $--color-info;
+	warningColor: $--color-warning;
 }
diff --git a/src/components/Breadcrumb/index.vue b/src/components/Breadcrumb/index.vue
index 489cba1..1d138e4 100644
--- a/src/components/Breadcrumb/index.vue
+++ b/src/components/Breadcrumb/index.vue
@@ -1,57 +1,55 @@
-<template>
-  <el-breadcrumb class="app-breadcrumb" separator="/">
-    <transition-group name="breadcrumb">
-      <el-breadcrumb-item v-for="(item,index) in levelList" :key="item.path">
-        <span v-if="item.redirect === 'noRedirect' || index == levelList.length - 1" class="no-redirect">{{ item.meta.title }}</span>
-        <a v-else @click.prevent="handleLink(item)">{{ item.meta.title }}</a>
-      </el-breadcrumb-item>
-    </transition-group>
-  </el-breadcrumb>
-</template>
+<script setup lang="ts">
+import { RouteLocationMatched } from 'vue-router'
 
-<script setup>
 const route = useRoute();
 const router = useRouter();
-const levelList = ref([])
+const levelList = ref<RouteLocationMatched[]>([])
 
-function getBreadcrumb() {
+const getBreadcrumb = () => {
   // only show routes with meta.title
   let matched = route.matched.filter(item => item.meta && item.meta.title);
   const first = matched[0]
   // 鍒ゆ柇鏄惁涓洪椤�
   if (!isDashboard(first)) {
-    matched = [{ path: '/index', meta: { title: '棣栭〉' } }].concat(matched)
+    matched = ([{ path: '/index', meta: { title: '棣栭〉' } }] as any).concat(matched)
   }
-
   levelList.value = matched.filter(item => item.meta && item.meta.title && item.meta.breadcrumb !== false)
 }
-function isDashboard(route) {
-  const name = route && route.name
+const isDashboard = (route: RouteLocationMatched) => {
+  const name = route && route.name as string
   if (!name) {
     return false
   }
   return name.trim() === 'Index'
 }
-function handleLink(item) {
+const handleLink = (item: RouteLocationMatched) => {
   const { redirect, path } = item
-  if (redirect) {
-    router.push(redirect)
-    return
-  }
-  router.push(path)
+  redirect ? router.push(redirect as string) : router.push(path)
 }
 
 watchEffect(() => {
   // if you go to the redirect page, do not update the breadcrumbs
-  if (route.path.startsWith('/redirect/')) {
-    return
-  }
+  if (route.path.startsWith('/redirect/')) return
   getBreadcrumb()
 })
-getBreadcrumb();
+onMounted(() => {
+  getBreadcrumb();
+})
 </script>
 
-<style lang='scss' scoped>
+<template>
+	<el-breadcrumb class="app-breadcrumb" separator="/">
+		<transition-group name="breadcrumb">
+			<el-breadcrumb-item v-for="(item, index) in levelList" :key="item.path">
+				<span v-if="item.redirect === 'noRedirect' || index == levelList.length - 1" class="no-redirect">{{
+          item.meta?.title }}</span>
+				<a v-else @click.prevent="handleLink(item)">{{ item.meta?.title }}</a>
+			</el-breadcrumb-item>
+		</transition-group>
+	</el-breadcrumb>
+</template>
+
+<style lang="scss" scoped>
 .app-breadcrumb.el-breadcrumb {
   display: inline-block;
   font-size: 14px;
@@ -63,4 +61,4 @@
     cursor: text;
   }
 }
-</style>
\ No newline at end of file
+</style>
diff --git a/src/components/DictTag/index.vue b/src/components/DictTag/index.vue
index c03a1a6..7b7a657 100644
--- a/src/components/DictTag/index.vue
+++ b/src/components/DictTag/index.vue
@@ -1,31 +1,10 @@
-<template>
-  <div>
-    <template v-for="(item, index) in options">
-      <template v-if="values.includes(item.value)">
-        <span
-          v-if="item.elTagType == 'default' || item.elTagType == ''"
-          :key="item.value"
-          :index="index"
-          :class="item.elTagClass"
-        >{{ item.label }}</span>
-        <el-tag
-          v-else
-          :disable-transitions="true"
-          :key="item.value + ''"
-          :index="index"
-          :type="item.elTagType === 'primary' ? '' : item.elTagType"
-          :class="item.elTagClass"
-        >{{ item.label }}</el-tag>
-      </template>
-    </template>
-  </div>
-</template>
+<script setup lang="ts">
+import { PropType } from 'vue';
 
-<script setup>
 const props = defineProps({
   // 鏁版嵁
   options: {
-    type: Array,
+    type: Array as PropType<DictDataOption[]>,
     default: null,
   },
   // 褰撳墠鐨勫��
@@ -33,17 +12,41 @@
 })
 
 const values = computed(() => {
-  if (props.value !== null && typeof props.value !== 'undefined') {
-    return Array.isArray(props.value) ? props.value : [String(props.value)];
-  } else {
-    return [];
-  }
+	if (props.value !== null && typeof props.value !== 'undefined') {
+		return Array.isArray(props.value) ? props.value : [String(props.value)];
+	} else {
+		return [];
+	}
 })
-
 </script>
+
+<template>
+	<div>
+		<template v-for="(item, index) in options">
+			<template v-if="values.includes(item.value)">
+				<span
+					v-if="item.elTagType == 'default' || item.elTagType == ''"
+					:key="item.value"
+					:index="index"
+					:class="item.elTagClass"
+					>{{ item.label }}</span
+				>
+				<el-tag
+					v-else
+					:disable-transitions="true"
+					:key="item.value + ''"
+					:index="index"
+					:type="item.elTagType === 'primary' ? '' : item.elTagType"
+					:class="item.elTagClass"
+					>{{ item.label }}</el-tag
+				>
+			</template>
+		</template>
+	</div>
+</template>
 
 <style scoped>
 .el-tag + .el-tag {
   margin-left: 10px;
 }
-</style>
\ No newline at end of file
+</style>
diff --git a/src/components/Editor/index.vue b/src/components/Editor/index.vue
index bc8da7b..b7c18c3 100644
--- a/src/components/Editor/index.vue
+++ b/src/components/Editor/index.vue
@@ -1,36 +1,8 @@
-<template>
-  <div>
-    <el-upload
-        :action="uploadUrl"
-        :before-upload="handleBeforeUpload"
-        :on-success="handleUploadSuccess"
-        :on-error="handleUploadError"
-        class="editor-img-uploader"
-        name="file"
-        :show-file-list="false"
-        :headers="headers"
-        style="display: none"
-        ref="uploadRef"
-        v-if="type == 'url'"
-    >
-    </el-upload>
-    <div class="editor">
-      <quill-editor
-          ref="myQuillEditor"
-          v-model:content="content"
-          contentType="html"
-          @textChange="(e) => $emit('update:modelValue', content)"
-          :options="options"
-          :style="styles"
-      />
-    </div>
-  </div>
-</template>
-
-<script setup>
+<script setup lang="ts">
 import { QuillEditor, Quill } from '@vueup/vue-quill';
 import '@vueup/vue-quill/dist/vue-quill.snow.css';
 import { getToken } from "@/utils/auth";
+import { ComponentInternalInstance } from "vue";
 
 const props = defineProps({
   /* 缂栬緫鍣ㄧ殑鍐呭 */
@@ -64,10 +36,12 @@
   }
 });
 
-const { proxy } = getCurrentInstance();
-// 涓婁紶鐨勫浘鐗囨湇鍔″櫒鍦板潃
-const uploadUrl = ref(import.meta.env.VITE_APP_BASE_API + "/system/oss/upload");
-const headers = ref({ Authorization: "Bearer " + getToken() });
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
+const upload = reactive<UploadOption>({
+  headers: { Authorization: "Bearer " + getToken() },
+  url: import.meta.env.VITE_APP_BASE_API + '/system/oss/upload'
+})
 const myQuillEditor = ref();
 
 const options = ref({
@@ -90,10 +64,10 @@
         ["link", "image", "video"]                       // 閾炬帴銆佸浘鐗囥�佽棰�
       ],
       handlers: {
-        image: function (value) {
+        image: function (value: any) {
           if (value) {
             // 璋冪敤element鍥剧墖涓婁紶
-            document.querySelector(".editor-img-uploader>.el-upload").click();
+            (document.querySelector(".editor-img-uploader>.el-upload") as HTMLDivElement)?.click();
           } else {
             Quill.format("image", true);
           }
@@ -106,7 +80,7 @@
 });
 
 const styles = computed(() => {
-  let style = {};
+  let style: any = {};
   if (props.minHeight) {
     style.minHeight = `${props.minHeight}px`;
   }
@@ -118,50 +92,78 @@
 
 const content = ref("");
 watch(() => props.modelValue, (v) => {
-  if (v !== content) {
+  if (v !== content.value) {
     content.value = v === undefined ? "<p></p>" : v;
   }
 }, { immediate: true });
 
 // 鍥剧墖涓婁紶鎴愬姛杩斿洖鍥剧墖鍦板潃
-function handleUploadSuccess(res, file) {
+const handleUploadSuccess = (res: any) => {
   // 鑾峰彇瀵屾枃鏈疄渚�
   let quill = toRaw(myQuillEditor.value).getQuill();
   // 濡傛灉涓婁紶鎴愬姛
-  if (res.code == 200) {
+  if (res.code === 200) {
     // 鑾峰彇鍏夋爣浣嶇疆
     let length = quill.selection.savedRange.index;
     // 鎻掑叆鍥剧墖锛宺es涓烘湇鍔″櫒杩斿洖鐨勫浘鐗囬摼鎺ュ湴鍧�
     quill.insertEmbed(length, "image", res.data.url);
     // 璋冩暣鍏夋爣鍒版渶鍚�
     quill.setSelection(length + 1);
-    proxy.$modal.closeLoading();
+    proxy?.$modal.closeLoading();
   } else {
-    proxy.$modal.loading(res.msg);
-    proxy.$modal.closeLoading();
+    proxy?.$modal.loading(res.msg);
+    proxy?.$modal.closeLoading();
   }
 }
 
 // 鍥剧墖涓婁紶鍓嶆嫤鎴�
-function handleBeforeUpload(file) {
+const handleBeforeUpload = (file: any) => {
   // 鏍℃鏂囦欢澶у皬
   if (props.fileSize) {
     const isLt = file.size / 1024 / 1024 < props.fileSize;
     if (!isLt) {
-      proxy.$modal.msgError(`涓婁紶鏂囦欢澶у皬涓嶈兘瓒呰繃 ${props.fileSize} MB!`);
+      proxy?.$modal.msgError(`涓婁紶鏂囦欢澶у皬涓嶈兘瓒呰繃 ${props.fileSize} MB!`);
       return false;
     }
   }
-  proxy.$modal.loading("姝e湪涓婁紶鏂囦欢锛岃绋嶅��...");
+  proxy?.$modal.loading('姝e湪涓婁紶鏂囦欢锛岃绋嶅��...');
   return true;
 }
 
 // 鍥剧墖澶辫触鎷︽埅
-function handleUploadError(err) {
-  proxy.$modal.msgError("涓婁紶鏂囦欢澶辫触");
+const handleUploadError = (err: any) => {
+  console.error(err);
+  proxy?.$modal.msgError('涓婁紶鏂囦欢澶辫触');
 }
-
 </script>
+
+<template>
+	<div>
+		<el-upload
+			:action="upload.url"
+			:before-upload="handleBeforeUpload"
+			:on-success="handleUploadSuccess"
+			:on-error="handleUploadError"
+			class="editor-img-uploader"
+			name="file"
+			:show-file-list="false"
+			:headers="upload.headers"
+			style="display: none"
+			v-if="type === 'url'"
+		>
+		</el-upload>
+		<div class="editor">
+			<quill-editor
+				ref="myQuillEditor"
+				v-model:content="content"
+				contentType="html"
+				@textChange="(e: any) => $emit('update:modelValue', content)"
+				:options="options"
+				:style="styles"
+			/>
+		</div>
+	</div>
+</template>
 
 <style>
 .editor, .ql-toolbar {
@@ -175,9 +177,9 @@
   content: "璇疯緭鍏ラ摼鎺ュ湴鍧�:";
 }
 .ql-snow .ql-tooltip.ql-editing a.ql-action::after {
-  border-right: 0px;
+  border-right: 0;
   content: "淇濆瓨";
-  padding-right: 0px;
+  padding-right: 0;
 }
 
 .ql-snow .ql-tooltip[data-mode="video"]::before {
diff --git a/src/components/FileUpload/index.vue b/src/components/FileUpload/index.vue
index a5c2734..cc24f36 100644
--- a/src/components/FileUpload/index.vue
+++ b/src/components/FileUpload/index.vue
@@ -1,46 +1,8 @@
-<template>
-  <div class="upload-file">
-    <el-upload
-      multiple
-      :action="uploadFileUrl"
-      :before-upload="handleBeforeUpload"
-      :file-list="fileList"
-      :limit="limit"
-      :on-error="handleUploadError"
-      :on-exceed="handleExceed"
-      :on-success="handleUploadSuccess"
-      :show-file-list="false"
-      :headers="headers"
-      class="upload-file-uploader"
-      ref="fileUpload"
-    >
-      <!-- 涓婁紶鎸夐挳 -->
-      <el-button type="primary">閫夊彇鏂囦欢</el-button>
-    </el-upload>
-    <!-- 涓婁紶鎻愮ず -->
-    <div class="el-upload__tip" v-if="showTip">
-      璇蜂笂浼�
-      <template v-if="fileSize"> 澶у皬涓嶈秴杩� <b style="color: #f56c6c">{{ fileSize }}MB</b> </template>
-      <template v-if="fileType"> 鏍煎紡涓� <b style="color: #f56c6c">{{ fileType.join("/") }}</b> </template>
-      鐨勬枃浠�
-    </div>
-    <!-- 鏂囦欢鍒楄〃 -->
-    <transition-group class="upload-file-list el-upload-list el-upload-list--text" name="el-fade-in-linear" tag="ul">
-      <li :key="file.uid" class="el-upload-list__item ele-upload-list__item-content" v-for="(file, index) in fileList">
-        <el-link :href="`${file.url}`" :underline="false" target="_blank">
-          <span class="el-icon-document"> {{ getFileName(file.name) }} </span>
-        </el-link>
-        <div class="ele-upload-list__item-content-action">
-          <el-link :underline="false" @click="handleDelete(index)" type="danger">鍒犻櫎</el-link>
-        </div>
-      </li>
-    </transition-group>
-  </div>
-</template>
-
-<script setup>
+<script setup lang="ts">
 import { getToken } from "@/utils/auth";
 import { listByIds, delOss } from "@/api/system/oss";
+import { ComponentInternalInstance } from "vue";
+import { ElUpload, UploadFile } from "element-plus";
 
 const props = defineProps({
   modelValue: [String, Object, Array],
@@ -66,32 +28,35 @@
   }
 });
 
-const { proxy } = getCurrentInstance();
-const emit = defineEmits();
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const emit = defineEmits(['update:modelValue']);
 const number = ref(0);
-const uploadList = ref([]);
+const uploadList = ref<any[]>([]);
+
 const baseUrl = import.meta.env.VITE_APP_BASE_API;
 const uploadFileUrl = ref(baseUrl + "/system/oss/upload"); // 涓婁紶鏂囦欢鏈嶅姟鍣ㄥ湴鍧�
 const headers = ref({ Authorization: "Bearer " + getToken() });
-const fileList = ref([]);
+
+const fileList = ref<any[]>([]);
 const showTip = computed(
   () => props.isShowTip && (props.fileType || props.fileSize)
 );
+
+const fileUploadRef = ref(ElUpload);
 
 watch(() => props.modelValue, async val => {
   if (val) {
     let temp = 1;
     // 棣栧厛灏嗗�艰浆涓烘暟缁�
-    let list;
+    let list = [];
     if (Array.isArray(val)) {
       list = val;
     } else {
-      await listByIds(val).then(res => {
-        list = res.data.map(oss => {
-          oss = { name: oss.originalName, url: oss.url, ossId: oss.ossId };
-          return oss;
+      const res =  await listByIds(val as string)
+      list = res.data.map((oss) => {
+          const data = { name: oss.originalName, url: oss.url, ossId: oss.ossId };
+          return data;
         });
-      })
     }
     // 鐒跺悗灏嗘暟缁勮浆涓哄璞℃暟缁�
     fileList.value = list.map(item => {
@@ -106,14 +71,14 @@
 },{ deep: true, immediate: true });
 
 // 涓婁紶鍓嶆牎妫�鏍煎紡鍜屽ぇ灏�
-function handleBeforeUpload(file) {
+const handleBeforeUpload = (file: any) => {
   // 鏍℃鏂囦欢绫诲瀷
   if (props.fileType.length) {
     const fileName = file.name.split('.');
     const fileExt = fileName[fileName.length - 1];
     const isTypeOk = props.fileType.indexOf(fileExt) >= 0;
     if (!isTypeOk) {
-      proxy.$modal.msgError(`鏂囦欢鏍煎紡涓嶆纭�, 璇蜂笂浼�${props.fileType.join("/")}鏍煎紡鏂囦欢!`);
+      proxy?.$modal.msgError(`鏂囦欢鏍煎紡涓嶆纭�, 璇蜂笂浼�${props.fileType.join("/")}鏍煎紡鏂囦欢!`);
       return false;
     }
   }
@@ -121,41 +86,41 @@
   if (props.fileSize) {
     const isLt = file.size / 1024 / 1024 < props.fileSize;
     if (!isLt) {
-      proxy.$modal.msgError(`涓婁紶鏂囦欢澶у皬涓嶈兘瓒呰繃 ${props.fileSize} MB!`);
+      proxy?.$modal.msgError(`涓婁紶鏂囦欢澶у皬涓嶈兘瓒呰繃 ${props.fileSize} MB!`);
       return false;
     }
   }
-  proxy.$modal.loading("姝e湪涓婁紶鏂囦欢锛岃绋嶅��...");
+  proxy?.$modal.loading("姝e湪涓婁紶鏂囦欢锛岃绋嶅��...");
   number.value++;
   return true;
 }
 
 // 鏂囦欢涓暟瓒呭嚭
-function handleExceed() {
-  proxy.$modal.msgError(`涓婁紶鏂囦欢鏁伴噺涓嶈兘瓒呰繃 ${props.limit} 涓�!`);
+const handleExceed = () => {
+  proxy?.$modal.msgError(`涓婁紶鏂囦欢鏁伴噺涓嶈兘瓒呰繃 ${props.limit} 涓�!`);
 }
 
 // 涓婁紶澶辫触
-function handleUploadError(err) {
-  proxy.$modal.msgError("涓婁紶鏂囦欢澶辫触");
+const handleUploadError = () => {
+  proxy?.$modal.msgError("涓婁紶鏂囦欢澶辫触");
 }
 
 // 涓婁紶鎴愬姛鍥炶皟
-function handleUploadSuccess(res, file) {
+const handleUploadSuccess = (res:any, file: UploadFile) => {
   if (res.code === 200) {
     uploadList.value.push({ name: res.data.fileName, url: res.data.url, ossId: res.data.ossId });
     uploadedSuccessfully();
   } else {
     number.value--;
-    proxy.$modal.closeLoading();
-    proxy.$modal.msgError(res.msg);
-    proxy.$refs.fileUpload.handleRemove(file);
+    proxy?.$modal.closeLoading();
+    proxy?.$modal.msgError(res.msg);
+    fileUploadRef.value.handleRemove(file);
     uploadedSuccessfully();
   }
 }
 
 // 鍒犻櫎鏂囦欢
-function handleDelete(index) {
+const handleDelete = (index: number) => {
   let ossId = fileList.value[index].ossId;
   delOss(ossId);
   fileList.value.splice(index, 1);
@@ -163,18 +128,18 @@
 }
 
 // 涓婁紶缁撴潫澶勭悊
-function uploadedSuccessfully() {
+const uploadedSuccessfully =() => {
   if (number.value > 0 && uploadList.value.length === number.value) {
     fileList.value = fileList.value.filter(f => f.url !== undefined).concat(uploadList.value);
     uploadList.value = [];
     number.value = 0;
     emit("update:modelValue", listToString(fileList.value));
-    proxy.$modal.closeLoading();
+    proxy?.$modal.closeLoading();
   }
 }
 
 // 鑾峰彇鏂囦欢鍚嶇О
-function getFileName(name) {
+const getFileName = (name: string) => {
   // 濡傛灉鏄痷rl閭d箞鍙栨渶鍚庣殑鍚嶅瓧 濡傛灉涓嶆槸鐩存帴杩斿洖
   if (name.lastIndexOf("/") > -1) {
     return name.slice(name.lastIndexOf("/") + 1);
@@ -184,18 +149,62 @@
 }
 
 // 瀵硅薄杞垚鎸囧畾瀛楃涓插垎闅�
-function listToString(list, separator) {
+const listToString = (list: any[], separator?: string) => {
   let strs = "";
   separator = separator || ",";
-  for (let i in list) {
-    if(list[i].ossId) {
-      strs += list[i].ossId + separator;
+  list.forEach(item => {
+    if (item.ossId) {
+      strs += item.ossId + separator;
     }
-  }
-  return strs != "" ? strs.substr(0, strs.length - 1) : "";
+  })
+  return strs != "" ? strs.substring(0, strs.length - 1) : "";
 }
 </script>
 
+<template>
+	<div class="upload-file">
+		<el-upload
+			multiple
+			:action="uploadFileUrl"
+			:before-upload="handleBeforeUpload"
+			:file-list="fileList"
+			:limit="limit"
+			:on-error="handleUploadError"
+			:on-exceed="handleExceed"
+			:on-success="handleUploadSuccess"
+			:show-file-list="false"
+			:headers="headers"
+			class="upload-file-uploader"
+			ref="fileUploadRef"
+		>
+			<!-- 涓婁紶鎸夐挳 -->
+			<el-button type="primary">閫夊彇鏂囦欢</el-button>
+		</el-upload>
+		<!-- 涓婁紶鎻愮ず -->
+		<div class="el-upload__tip" v-if="showTip">
+			璇蜂笂浼�
+			<template v-if="fileSize">
+				澶у皬涓嶈秴杩� <b style="color: #f56c6c">{{ fileSize }}MB</b>
+			</template>
+			<template v-if="fileType">
+				鏍煎紡涓� <b style="color: #f56c6c">{{ fileType.join("/") }}</b>
+			</template>
+			鐨勬枃浠�
+		</div>
+		<!-- 鏂囦欢鍒楄〃 -->
+		<transition-group class="upload-file-list el-upload-list el-upload-list--text" name="el-fade-in-linear" tag="ul">
+			<li :key="file.uid" class="el-upload-list__item ele-upload-list__item-content" v-for="(file, index) in fileList">
+				<el-link :href="`${file.url}`" :underline="false" target="_blank">
+					<span class="el-icon-document"> {{ getFileName(file.name) }} </span>
+				</el-link>
+				<div class="ele-upload-list__item-content-action">
+					<el-link :underline="false" @click="handleDelete(index)" type="danger">鍒犻櫎</el-link>
+				</div>
+			</li>
+		</transition-group>
+	</div>
+</template>
+
 <style scoped lang="scss">
 .upload-file-uploader {
   margin-bottom: 5px;
diff --git a/src/components/Hamburger/index.vue b/src/components/Hamburger/index.vue
index 18c201e..ab3c60f 100644
--- a/src/components/Hamburger/index.vue
+++ b/src/components/Hamburger/index.vue
@@ -1,19 +1,4 @@
-<template>
-  <div style="padding: 0 15px;" @click="toggleClick">
-    <svg
-      :class="{'is-active':isActive}"
-      class="hamburger"
-      viewBox="0 0 1024 1024"
-      xmlns="http://www.w3.org/2000/svg"
-      width="64"
-      height="64"
-    >
-      <path d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM142.4 642.1L298.7 519a8.84 8.84 0 0 0 0-13.9L142.4 381.9c-5.8-4.6-14.4-.5-14.4 6.9v246.3a8.9 8.9 0 0 0 14.4 7z" />
-    </svg>
-  </div>
-</template>
-
-<script setup>
+<script setup lang="ts">
 defineProps({
   isActive: {
     type: Boolean,
@@ -21,12 +6,22 @@
   }
 })
 
-const emit = defineEmits()
+const emit = defineEmits(['toggleClick'])
 const toggleClick = () => {
   emit('toggleClick');
 }
 </script>
 
+<template>
+	<div style="padding: 0 15px;" @click="toggleClick">
+		<svg :class="{'is-active':isActive}" class="hamburger" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="64" height="64">
+			<path
+				d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM142.4 642.1L298.7 519a8.84 8.84 0 0 0 0-13.9L142.4 381.9c-5.8-4.6-14.4-.5-14.4 6.9v246.3a8.9 8.9 0 0 0 14.4 7z"
+			/>
+		</svg>
+	</div>
+</template>
+
 <style scoped>
 .hamburger {
   display: inline-block;
diff --git a/src/components/HeaderSearch/index.vue b/src/components/HeaderSearch/index.vue
index 543559b..e6b9531 100644
--- a/src/components/HeaderSearch/index.vue
+++ b/src/components/HeaderSearch/index.vue
@@ -1,49 +1,36 @@
-<template>
-  <div :class="{ 'show': show }" class="header-search">
-    <svg-icon class-name="search-icon" icon-class="search" @click.stop="click" />
-    <el-select
-      ref="headerSearchSelectRef"
-      v-model="search"
-      :remote-method="querySearch"
-      filterable
-      default-first-option
-      remote
-      placeholder="Search"
-      class="header-search-select"
-      @change="change"
-    >
-      <el-option v-for="option in options" :key="option.item.path" :value="option.item" :label="option.item.title.join(' > ')" />
-    </el-select>
-  </div>
-</template>
-
-<script setup>
+<script setup lang="ts">
 import Fuse from 'fuse.js'
 import { getNormalPath } from '@/utils/ruoyi'
 import { isHttp } from '@/utils/validate'
 import usePermissionStore from '@/store/modules/permission'
+import { RouteOption } from 'vue-router'
+
+type Router = Array<{
+  path: string;
+  title: string[];
+}>
 
 const search = ref('');
-const options = ref([]);
-const searchPool = ref([]);
+const options = ref<any>([]);
+const searchPool = ref<Router>([]);
 const show = ref(false);
-const fuse = ref(undefined);
-const headerSearchSelectRef = ref(null);
+const fuse = ref();
+const headerSearchSelectRef = ref(ElSelect);
 const router = useRouter();
 const routes = computed(() => usePermissionStore().routes);
 
-function click() {
+const click = () => {
   show.value = !show.value
   if (show.value) {
     headerSearchSelectRef.value && headerSearchSelectRef.value.focus()
   }
 };
-function close() {
+const close = () => {
   headerSearchSelectRef.value && headerSearchSelectRef.value.blur()
   options.value = []
   show.value = false
 }
-function change(val) {
+const change = (val: any) => {
   const path = val.path;
   if (isHttp(path)) {
     // http(s):// 璺緞鏂扮獥鍙f墦寮�
@@ -52,14 +39,13 @@
   } else {
     router.push(path)
   }
-
   search.value = ''
   options.value = []
   nextTick(() => {
     show.value = false
   })
 }
-function initFuse(list) {
+const initFuse = (list: Router) => {
   fuse.value = new Fuse(list, {
     shouldSort: true,
     threshold: 0.4,
@@ -77,39 +63,36 @@
 }
 // Filter out the routes that can be displayed in the sidebar
 // And generate the internationalized title
-function generateRoutes(routes, basePath = '', prefixTitle = []) {
-  let res = []
-
-  for (const r of routes) {
+const generateRoutes = (routes: RouteOption[], basePath = '', prefixTitle: string[] = []) => {
+  let res: Router = []
+  routes.forEach(r => {
     // skip hidden router
-    if (r.hidden) { continue }
-    const p = r.path.length > 0 && r.path[0] === '/' ? r.path : '/' + r.path;
-    const data = {
-      path: !isHttp(r.path) ? getNormalPath(basePath + p) : r.path,
-      title: [...prefixTitle]
-    }
-
-    if (r.meta && r.meta.title) {
-      data.title = [...data.title, r.meta.title]
-
-      if (r.redirect !== 'noRedirect') {
-        // only push the routes with title
-        // special case: need to exclude parent router without redirect
-        res.push(data)
+    if (!r.hidden) {
+      const p = r.path.length > 0 && r.path[0] === '/' ? r.path : '/' + r.path;
+      const data = {
+        path: !isHttp(r.path) ? getNormalPath(basePath + p) : r.path,
+        title: [...prefixTitle]
+      }
+      if (r.meta && r.meta.title) {
+        data.title = [...data.title, r.meta.title];
+        if (r.redirect !== 'noRedirect') {
+          // only push the routes with title
+          // special case: need to exclude parent router without redirect
+          res.push(data);
+        }
+      }
+      // recursive child routes
+      if (r.children) {
+        const tempRoutes = generateRoutes(r.children, data.path, data.title);
+        if (tempRoutes.length >= 1) {
+          res = [...res, ...tempRoutes];
+        }
       }
     }
-
-    // recursive child routes
-    if (r.children) {
-      const tempRoutes = generateRoutes(r.children, data.path, data.title)
-      if (tempRoutes.length >= 1) {
-        res = [...res, ...tempRoutes]
-      }
-    }
-  }
-  return res
+  })
+  return res;
 }
-function querySearch(query) {
+const querySearch = (query: string) => {
   if (query !== '') {
     options.value = fuse.value.search(query)
   } else {
@@ -138,7 +121,26 @@
 })
 </script>
 
-<style lang='scss' scoped>
+<template>
+	<div :class="{ 'show': show }" class="header-search">
+		<svg-icon class-name="search-icon" icon-class="search" @click.stop="click" />
+		<el-select
+			ref="headerSearchSelectRef"
+			v-model="search"
+			:remote-method="querySearch"
+			filterable
+			default-first-option
+			remote
+			placeholder="Search"
+			class="header-search-select"
+			@change="change"
+		>
+			<el-option v-for="option in options" :key="option.item.path" :value="option.item" :label="option.item.title.join(' > ')" />
+		</el-select>
+	</div>
+</template>
+
+<style lang="scss" scoped>
 .header-search {
   font-size: 0 !important;
 
@@ -176,4 +178,4 @@
     }
   }
 }
-</style>
\ No newline at end of file
+</style>
diff --git a/src/components/IconSelect/index.vue b/src/components/IconSelect/index.vue
index 5b06806..97c601e 100644
--- a/src/components/IconSelect/index.vue
+++ b/src/components/IconSelect/index.vue
@@ -1,74 +1,105 @@
-<template>
-  <div class="icon-body">
-    <el-input
-      v-model="iconName"
-      style="position: relative;"
-      clearable
-      placeholder="璇疯緭鍏ュ浘鏍囧悕绉�"
-      @clear="filterIcons"
-      @input="filterIcons"
-    >
-      <template #suffix><i class="el-icon-search el-input__icon" /></template>
-    </el-input>
-    <div class="icon-list">
-      <div v-for="(item, index) in iconList" :key="index" @click="selectedIcon(item)">
-        <svg-icon :icon-class="item" style="height: 30px;width: 16px;" />
-        <span>{{ item }}</span>
-      </div>
-    </div>
-  </div>
-</template>
+<script setup lang="ts">
+import icons from '@/components/IconSelect/requireIcons';
 
-<script setup>
-import icons from './requireIcons'
+const props = defineProps({
+  modelValue: {
+    type: String,
+    require: true
+  },
+  width: {
+    type: String,
+    require: false,
+    default: '400px'
+  }
+});
 
-const iconName = ref('');
-const iconList = ref(icons);
-const emit = defineEmits(['selected']);
+const emit = defineEmits(['update:modelValue']);
+const visible = ref(false);
+const { modelValue, width } = toRefs(props);
+const iconNames = ref<string[]>(icons);
 
-function filterIcons() {
-  iconList.value = icons
-  if (iconName.value) {
-    iconList.value = icons.filter(item => item.indexOf(iconName.value) !== -1)
+const filterValue = ref('');
+
+/**
+ * 绛涢�夊浘鏍�
+ */
+const filterIcons = () => {
+  if (filterValue.value) {
+    iconNames.value = icons.filter(iconName =>
+      iconName.includes(filterValue.value)
+    );
+  } else {
+    iconNames.value = icons;
   }
 }
 
-function selectedIcon(name) {
-  emit('selected', name)
-  document.body.click()
+/**
+ * 閫夋嫨鍥炬爣
+ * @param iconName 閫夋嫨鐨勫浘鏍囧悕绉�
+ */
+const selectedIcon = (iconName: string) => {
+  emit('update:modelValue', iconName);
+  visible.value = false;
 }
-
-function reset() {
-  iconName.value = ''
-  iconList.value = icons
-}
-
-defineExpose({
-  reset
-})
 </script>
 
-<style lang='scss' scoped>
-.icon-body {
-  width: 100%;
-  padding: 10px;
-  .icon-list {
-    height: 200px;
-    overflow-y: scroll;
-    div {
-      height: 30px;
-      line-height: 30px;
-      margin-bottom: -5px;
-      cursor: pointer;
-      width: 33%;
-      float: left;
-    }
-    span {
-      display: inline-block;
-      vertical-align: -0.15em;
-      fill: currentColor;
-      overflow: hidden;
+<template>
+	<div class="relative" :style="{ width: width }">
+		<el-input v-model="modelValue" readonly @click="visible = !visible" placeholder="鐐瑰嚮閫夋嫨鍥炬爣">
+			<template #prepend>
+				<svg-icon :icon-class="modelValue as string"></svg-icon>
+			</template>
+		</el-input>
+
+		<el-popover shadow="none" :visible="visible" placement="bottom-end" trigger="click" :width="450">
+			<template #reference>
+				<div @click="visible = !visible" class="cursor-pointer text-[#999] absolute right-[10px] top-0 height-[32px] leading-[32px]">
+					<i-ep-caret-top v-show="visible"></i-ep-caret-top>
+					<i-ep-caret-bottom v-show="!visible"></i-ep-caret-bottom>
+				</div>
+			</template>
+
+			<el-input class="p-2" v-model="filterValue" placeholder="鎼滅储鍥炬爣" clearable @input="filterIcons" />
+
+			<el-scrollbar height="w-[200px]">
+				<ul class="icon-list">
+					<el-tooltip v-for="(iconName, index) in iconNames" :key="index" :content="iconName" placement="bottom" effect="light">
+						<li class="icon-item" @click="selectedIcon(iconName)">
+							<svg-icon color="var(--el-text-color-regular)" :icon-class="iconName" />
+						</li>
+					</el-tooltip>
+				</ul>
+			</el-scrollbar>
+		</el-popover>
+	</div>
+</template>
+
+<style scoped lang="scss">
+.el-divider--horizontal {
+  margin: 10px auto !important;
+}
+.icon-list {
+  display: flex;
+  flex-wrap: wrap;
+  padding-left: 10px;
+  margin-top: 10px;
+
+  .icon-item {
+    cursor: pointer;
+    width: 10%;
+    margin: 0 10px 10px 0;
+    padding: 5px;
+    display: flex;
+    flex-direction: column;
+    justify-items: center;
+    align-items: center;
+    border: 1px solid #ccc;
+    &:hover {
+      border-color: var(--el-color-primary);
+      color: var(--el-color-primary);
+      transition: all 0.2s;
+      transform: scaleX(1.1);
     }
   }
 }
-</style>
\ No newline at end of file
+</style>
diff --git a/src/components/IconSelect/requireIcons.js b/src/components/IconSelect/requireIcons.js
deleted file mode 100644
index ac22fd7..0000000
--- a/src/components/IconSelect/requireIcons.js
+++ /dev/null
@@ -1,8 +0,0 @@
-let icons = []
-const modules = import.meta.glob('./../../assets/icons/svg/*.svg');
-for (const path in modules) {
-  const p = path.split('assets/icons/svg/')[1].split('.svg')[0];
-  icons.push(p);
-}
-
-export default icons
\ No newline at end of file
diff --git a/src/components/IconSelect/requireIcons.ts b/src/components/IconSelect/requireIcons.ts
new file mode 100644
index 0000000..31ed49c
--- /dev/null
+++ b/src/components/IconSelect/requireIcons.ts
@@ -0,0 +1,7 @@
+const icons: string[] = [];
+const modules = import.meta.glob('./../../assets/icons/svg/*.svg');
+for (const path in modules) {
+	const p = path.split('assets/icons/svg/')[1].split('.svg')[0];
+	icons.push(p);
+}
+export default icons;
diff --git a/src/components/ImagePreview/index.vue b/src/components/ImagePreview/index.vue
index 1dfe123..45f8326 100644
--- a/src/components/ImagePreview/index.vue
+++ b/src/components/ImagePreview/index.vue
@@ -1,21 +1,4 @@
-<template>
-  <el-image
-    :src="`${realSrc}`"
-    fit="cover"
-    :style="`width:${realWidth};height:${realHeight};`"
-    :preview-src-list="realSrcList"
-    preview-teleported
-  >
-    <template #error>
-      <div class="image-slot">
-        <el-icon><picture-filled /></el-icon>
-      </div>
-    </template>
-  </el-image>
-</template>
-
-<script setup>
-
+<script setup lang="ts">
 const props = defineProps({
   src: {
     type: String,
@@ -44,7 +27,7 @@
     return;
   }
   let real_src_list = props.src.split(",");
-  let srcList = [];
+  let srcList:string[] = [];
   real_src_list.forEach(item => {
     return srcList.push(item);
   });
@@ -60,6 +43,16 @@
 );
 </script>
 
+<template>
+	<el-image :src="`${realSrc}`" fit="cover" :style="`width:${realWidth};height:${realHeight};`" :preview-src-list="realSrcList" preview-teleported>
+		<template #error>
+			<div class="image-slot">
+				<el-icon><picture-filled /></el-icon>
+			</div>
+		</template>
+	</el-image>
+</template>
+
 <style lang="scss" scoped>
 .el-image {
   border-radius: 5px;
diff --git a/src/components/ImageUpload/index.vue b/src/components/ImageUpload/index.vue
index 8cad89c..9753d0c 100644
--- a/src/components/ImageUpload/index.vue
+++ b/src/components/ImageUpload/index.vue
@@ -1,53 +1,9 @@
-<template>
-  <div class="component-upload-image">
-    <el-upload
-      multiple
-      :action="uploadImgUrl"
-      list-type="picture-card"
-      :on-success="handleUploadSuccess"
-      :before-upload="handleBeforeUpload"
-      :limit="limit"
-      :on-error="handleUploadError"
-      :on-exceed="handleExceed"
-      ref="imageUpload"
-      :before-remove="handleDelete"
-      :show-file-list="true"
-      :headers="headers"
-      :file-list="fileList"
-      :on-preview="handlePictureCardPreview"
-      :class="{ hide: fileList.length >= limit }"
-    >
-      <el-icon class="avatar-uploader-icon"><plus /></el-icon>
-    </el-upload>
-    <!-- 涓婁紶鎻愮ず -->
-    <div class="el-upload__tip" v-if="showTip">
-      璇蜂笂浼�
-      <template v-if="fileSize">
-        澶у皬涓嶈秴杩� <b style="color: #f56c6c">{{ fileSize }}MB</b>
-      </template>
-      <template v-if="fileType">
-        鏍煎紡涓� <b style="color: #f56c6c">{{ fileType.join("/") }}</b>
-      </template>
-      鐨勬枃浠�
-    </div>
-
-    <el-dialog
-      v-model="dialogVisible"
-      title="棰勮"
-      width="800px"
-      append-to-body
-    >
-      <img
-        :src="dialogImageUrl"
-        style="display: block; max-width: 100%; margin: 0 auto"
-      />
-    </el-dialog>
-  </div>
-</template>
-
-<script setup>
+<script setup lang="ts">
 import { getToken } from "@/utils/auth";
 import { listByIds, delOss } from "@/api/system/oss";
+import { ComponentInternalInstance, PropType } from "vue";
+import { OssVO } from "@/api/system/oss/types";
+import { ElUpload, UploadFile } from "element-plus";
 
 const props = defineProps({
   modelValue: [String, Object, Array],
@@ -63,7 +19,7 @@
   },
   // 鏂囦欢绫诲瀷, 渚嬪['png', 'jpg', 'jpeg']
   fileType: {
-    type: Array,
+    type: Array as PropType<string[]>,
     default: () => ["png", "jpg", "jpeg"],
   },
   // 鏄惁鏄剧ず鎻愮ず
@@ -73,41 +29,45 @@
   },
 });
 
-const { proxy } = getCurrentInstance();
-const emit = defineEmits();
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const emit = defineEmits(['update:modelValue']);
 const number = ref(0);
-const uploadList = ref([]);
+const uploadList = ref<any[]>([]);
 const dialogImageUrl = ref("");
 const dialogVisible = ref(false);
+
 const baseUrl = import.meta.env.VITE_APP_BASE_API;
 const uploadImgUrl = ref(baseUrl + "/system/oss/upload"); // 涓婁紶鐨勫浘鐗囨湇鍔″櫒鍦板潃
 const headers = ref({ Authorization: "Bearer " + getToken() });
-const fileList = ref([]);
+
+const fileList = ref<any[]>([]);
 const showTip = computed(
   () => props.isShowTip && (props.fileType || props.fileSize)
 );
 
+const imageUploadRef = ref(ElUpload);
+
 watch(() => props.modelValue, async val => {
   if (val) {
     // 棣栧厛灏嗗�艰浆涓烘暟缁�
-    let list;
+    let list:OssVO[] = [];
     if (Array.isArray(val)) {
-      list = val;
+      list = val as OssVO[];
     } else {
-      await listByIds(val).then(res => {
-        list = res.data;
-      })
+      const res = await listByIds(val as string)
+      list = res.data
     }
     // 鐒跺悗灏嗘暟缁勮浆涓哄璞℃暟缁�
     fileList.value = list.map(item => {
       // 瀛楃涓插洖鏄惧鐞� 濡傛灉姝ゅ瀛樼殑鏄痷rl鍙洿鎺ュ洖鏄� 濡傛灉瀛樼殑鏄痠d闇�瑕佽皟鐢ㄦ帴鍙f煡鍑烘潵
+      let itemData;
       if (typeof item === "string") {
-        item = { name: item, url: item };
+        itemData = { name: item, url: item };
       } else {
         // 姝ゅname浣跨敤ossId 闃叉鍒犻櫎鍑虹幇閲嶅悕
-        item = { name: item.ossId, url: item.url, ossId: item.ossId };
+        itemData = { name: item.ossId, url: item.url, ossId: item.ossId };
       }
-      return item;
+      return itemData;
     });
   } else {
     fileList.value = [];
@@ -115,8 +75,8 @@
   }
 },{ deep: true, immediate: true });
 
-// 涓婁紶鍓峫oading鍔犺浇
-function handleBeforeUpload(file) {
+/** 涓婁紶鍓峫oading鍔犺浇 */
+const handleBeforeUpload = (file: any) => {
   let isImg = false;
   if (props.fileType.length) {
     let fileExtension = "";
@@ -132,7 +92,7 @@
     isImg = file.type.indexOf("image") > -1;
   }
   if (!isImg) {
-    proxy.$modal.msgError(
+    proxy?.$modal.msgError(
       `鏂囦欢鏍煎紡涓嶆纭�, 璇蜂笂浼�${props.fileType.join("/")}鍥剧墖鏍煎紡鏂囦欢!`
     );
     return false;
@@ -140,35 +100,35 @@
   if (props.fileSize) {
     const isLt = file.size / 1024 / 1024 < props.fileSize;
     if (!isLt) {
-      proxy.$modal.msgError(`涓婁紶澶村儚鍥剧墖澶у皬涓嶈兘瓒呰繃 ${props.fileSize} MB!`);
+      proxy?.$modal.msgError(`涓婁紶澶村儚鍥剧墖澶у皬涓嶈兘瓒呰繃 ${props.fileSize} MB!`);
       return false;
     }
   }
-  proxy.$modal.loading("姝e湪涓婁紶鍥剧墖锛岃绋嶅��...");
+  proxy?.$modal.loading("姝e湪涓婁紶鍥剧墖锛岃绋嶅��...");
   number.value++;
 }
 
 // 鏂囦欢涓暟瓒呭嚭
-function handleExceed() {
-  proxy.$modal.msgError(`涓婁紶鏂囦欢鏁伴噺涓嶈兘瓒呰繃 ${props.limit} 涓�!`);
+const handleExceed = () => {
+  proxy?.$modal.msgError(`涓婁紶鏂囦欢鏁伴噺涓嶈兘瓒呰繃 ${props.limit} 涓�!`);
 }
 
 // 涓婁紶鎴愬姛鍥炶皟
-function handleUploadSuccess(res, file) {
+const handleUploadSuccess = (res: any, file: UploadFile) => {
   if (res.code === 200) {
     uploadList.value.push({ name: res.data.fileName, url: res.data.url, ossId: res.data.ossId });
     uploadedSuccessfully();
   } else {
     number.value--;
-    proxy.$modal.closeLoading();
-    proxy.$modal.msgError(res.msg);
-    proxy.$refs.imageUpload.handleRemove(file);
+    proxy?.$modal.closeLoading();
+    proxy?.$modal.msgError(res.msg);
+    imageUploadRef.value.handleRemove(file);
     uploadedSuccessfully();
   }
 }
 
 // 鍒犻櫎鍥剧墖
-function handleDelete(file) {
+const handleDelete = (file: UploadFile): boolean => {
   const findex = fileList.value.map(f => f.name).indexOf(file.name);
   if (findex > -1 && uploadList.value.length === number.value) {
     let ossId = fileList.value[findex].ossId;
@@ -177,33 +137,34 @@
     emit("update:modelValue", listToString(fileList.value));
     return false;
   }
+  return true;
 }
 
 // 涓婁紶缁撴潫澶勭悊
-function uploadedSuccessfully() {
+const uploadedSuccessfully = () => {
   if (number.value > 0 && uploadList.value.length === number.value) {
     fileList.value = fileList.value.filter(f => f.url !== undefined).concat(uploadList.value);
     uploadList.value = [];
     number.value = 0;
     emit("update:modelValue", listToString(fileList.value));
-    proxy.$modal.closeLoading();
+    proxy?.$modal.closeLoading();
   }
 }
 
 // 涓婁紶澶辫触
-function handleUploadError(res) {
-  proxy.$modal.msgError("涓婁紶鍥剧墖澶辫触");
-  proxy.$modal.closeLoading();
+const handleUploadError = () => {
+  proxy?.$modal.msgError("涓婁紶鍥剧墖澶辫触");
+  proxy?.$modal.closeLoading();
 }
 
 // 棰勮
-function handlePictureCardPreview(file) {
+const handlePictureCardPreview = (file: any) => {
   dialogImageUrl.value = file.url;
   dialogVisible.value = true;
 }
 
 // 瀵硅薄杞垚鎸囧畾瀛楃涓插垎闅�
-function listToString(list, separator) {
+const listToString = (list: any[], separator?: string) => {
   let strs = "";
   separator = separator || ",";
   for (let i in list) {
@@ -211,13 +172,52 @@
       strs += list[i].ossId + separator;
     }
   }
-  return strs != "" ? strs.substr(0, strs.length - 1) : "";
+  return strs != "" ? strs.substring(0, strs.length - 1) : "";
 }
 </script>
+
+<template>
+	<div class="component-upload-image">
+		<el-upload
+			multiple
+			:action="uploadImgUrl"
+			list-type="picture-card"
+			:on-success="handleUploadSuccess"
+			:before-upload="handleBeforeUpload"
+			:limit="limit"
+			:on-error="handleUploadError"
+			:on-exceed="handleExceed"
+			ref="imageUpload"
+			:before-remove="handleDelete"
+			:show-file-list="true"
+			:headers="headers"
+			:file-list="fileList"
+			:on-preview="handlePictureCardPreview"
+			:class="{ hide: fileList.length >= limit }"
+		>
+			<el-icon class="avatar-uploader-icon"><plus /></el-icon>
+		</el-upload>
+		<!-- 涓婁紶鎻愮ず -->
+		<div class="el-upload__tip" v-if="showTip">
+			璇蜂笂浼�
+			<template v-if="fileSize">
+				澶у皬涓嶈秴杩� <b style="color: #f56c6c">{{ fileSize }}MB</b>
+			</template>
+			<template v-if="fileType">
+				鏍煎紡涓� <b style="color: #f56c6c">{{ fileType.join("/") }}</b>
+			</template>
+			鐨勬枃浠�
+		</div>
+
+		<el-dialog v-model="dialogVisible" title="棰勮" width="800px" append-to-body>
+			<img :src="dialogImageUrl" style="display: block; max-width: 100%; margin: 0 auto" />
+		</el-dialog>
+	</div>
+</template>
 
 <style scoped lang="scss">
 // .el-upload--picture-card 鎺у埗鍔犲彿閮ㄥ垎
 :deep(.hide .el-upload--picture-card) {
     display: none;
 }
-</style>
\ No newline at end of file
+</style>
diff --git a/src/components/Pagination/index.vue b/src/components/Pagination/index.vue
index 38de953..40bc584 100644
--- a/src/components/Pagination/index.vue
+++ b/src/components/Pagination/index.vue
@@ -1,21 +1,12 @@
-<template>
-  <div :class="{ 'hidden': hidden }" class="pagination-container">
-    <el-pagination
-      :background="background"
-      v-model:current-page="currentPage"
-      v-model:page-size="pageSize"
-      :layout="layout"
-      :page-sizes="pageSizes"
-      :pager-count="pagerCount"
-      :total="total"
-      @size-change="handleSizeChange"
-      @current-change="handleCurrentChange"
-    />
-  </div>
-</template>
+<script lang="ts">
+export default {
+  name: 'Pagination'
+}
+</script>
 
-<script setup>
+<script setup lang="ts">
 import { scrollTo } from '@/utils/scroll-to'
+import { PropType } from "vue";
 
 const props = defineProps({
   total: {
@@ -31,7 +22,7 @@
     default: 20
   },
   pageSizes: {
-    type: Array,
+    type: Array as PropType<number[]>,
     default() {
       return [10, 20, 30, 50]
     }
@@ -56,10 +47,14 @@
   hidden: {
     type: Boolean,
     default: false
+  },
+  float: {
+    type: String,
+    default: 'right'
   }
 })
 
-const emit = defineEmits();
+const emit = defineEmits(['update:page', 'update:limit', 'pagination']);
 const currentPage = computed({
   get() {
     return props.page
@@ -76,7 +71,7 @@
     emit('update:limit', val)
   }
 })
-function handleSizeChange(val) {
+function handleSizeChange(val: number) {
   if (currentPage.value * val > props.total) {
     currentPage.value = 1
   }
@@ -85,21 +80,39 @@
     scrollTo(0, 800)
   }
 }
-function handleCurrentChange(val) {
+function handleCurrentChange(val: number) {
   emit('pagination', { page: val, limit: pageSize.value })
   if (props.autoScroll) {
     scrollTo(0, 800)
   }
 }
-
 </script>
 
-<style scoped>
+<template>
+	<div :class="{ 'hidden': hidden }" class="pagination-container">
+		<el-pagination
+			:background="background"
+			v-model:current-page="currentPage"
+			v-model:page-size="pageSize"
+			:layout="layout"
+			:page-sizes="pageSizes"
+			:pager-count="pagerCount"
+			:total="total"
+			@size-change="handleSizeChange"
+			@current-change="handleCurrentChange"
+		/>
+	</div>
+</template>
+
+<style lang="scss" scoped>
 .pagination-container {
   background: #fff;
   padding: 32px 16px;
+  .el-pagination{
+    float: v-bind(float);
+  }
 }
 .pagination-container.hidden {
   display: none;
 }
-</style>
\ No newline at end of file
+</style>
diff --git a/src/components/ParentView/index.vue b/src/components/ParentView/index.vue
index 7bf6148..4437b0a 100644
--- a/src/components/ParentView/index.vue
+++ b/src/components/ParentView/index.vue
@@ -1,3 +1,3 @@
-<template >
-  <router-view />
+<template>
+	<router-view />
 </template>
diff --git a/src/components/RightToolbar/index.vue b/src/components/RightToolbar/index.vue
index becb12c..fd94360 100644
--- a/src/components/RightToolbar/index.vue
+++ b/src/components/RightToolbar/index.vue
@@ -1,35 +1,14 @@
-<template>
-  <div class="top-right-btn" :style="style">
-    <el-row>
-      <el-tooltip class="item" effect="dark" :content="showSearch ? '闅愯棌鎼滅储' : '鏄剧ず鎼滅储'" placement="top" v-if="search">
-        <el-button circle icon="Search" @click="toggleSearch()" />
-      </el-tooltip>
-      <el-tooltip class="item" effect="dark" content="鍒锋柊" placement="top">
-        <el-button circle icon="Refresh" @click="refresh()" />
-      </el-tooltip>
-      <el-tooltip class="item" effect="dark" content="鏄鹃殣鍒�" placement="top" v-if="columns">
-        <el-button circle icon="Menu" @click="showColumn()" />
-      </el-tooltip>
-    </el-row>
-    <el-dialog :title="title" v-model="open" append-to-body>
-      <el-transfer
-        :titles="['鏄剧ず', '闅愯棌']"
-        v-model="value"
-        :data="columns"
-        @change="dataChange"
-      ></el-transfer>
-    </el-dialog>
-  </div>
-</template>
+<script setup lang="ts">
+import { TransferKey } from "element-plus";
+import { PropType } from "vue";
 
-<script setup>
 const props = defineProps({
   showSearch: {
     type: Boolean,
     default: true,
   },
   columns: {
-    type: Array,
+    type: Array as PropType<FieldOption[]>,
   },
   search: {
     type: Boolean,
@@ -44,14 +23,14 @@
 const emits = defineEmits(['update:showSearch', 'queryTable']);
 
 // 鏄鹃殣鏁版嵁
-const value = ref([]);
+const value = ref<Array<number>>([]);
 // 寮瑰嚭灞傛爣棰�
 const title = ref("鏄剧ず/闅愯棌");
 // 鏄惁鏄剧ず寮瑰嚭灞�
 const open = ref(false);
 
 const style = computed(() => {
-  const ret = {};
+  const ret: any = {};
   if (props.gutter) {
     ret.marginRight = `${props.gutter / 2}px`;
   }
@@ -69,27 +48,47 @@
 }
 
 // 鍙充晶鍒楄〃鍏冪礌鍙樺寲
-function dataChange(data) {
-  for (let item in props.columns) {
-    const key = props.columns[item].key;
-    props.columns[item].visible = !data.includes(key);
-  }
+function dataChange(data: TransferKey[]) {
+  props.columns?.forEach((item) => {
+    item.visible = !data.includes(item.key);
+  })
 }
 
 // 鎵撳紑鏄鹃殣鍒梔ialog
-function showColumn() {
+const showColumn = () => {
   open.value = true;
 }
 
 // 鏄鹃殣鍒楀垵濮嬮粯璁ら殣钘忓垪
-for (let item in props.columns) {
-  if (props.columns[item].visible === false) {
-    value.value.push(parseInt(item));
-  }
-}
+onMounted(() => {
+  props.columns?.forEach((item) => {
+    if (!item.visible) {
+      value.value.push(item.key);
+    }
+  })
+})
 </script>
 
-<style lang='scss' scoped>
+<template>
+	<div class="top-right-btn" :style="style">
+		<el-row>
+			<el-tooltip class="item" effect="dark" :content="showSearch ? '闅愯棌鎼滅储' : '鏄剧ず鎼滅储'" placement="top" v-if="search">
+				<el-button circle icon="Search" @click="toggleSearch()" />
+			</el-tooltip>
+			<el-tooltip class="item" effect="dark" content="鍒锋柊" placement="top">
+				<el-button circle icon="Refresh" @click="refresh()" />
+			</el-tooltip>
+			<el-tooltip class="item" effect="dark" content="鏄鹃殣鍒�" placement="top" v-if="columns">
+				<el-button circle icon="Menu" @click="showColumn()" />
+			</el-tooltip>
+		</el-row>
+		<el-dialog :title="title" v-model="open" append-to-body>
+			<el-transfer :titles="['鏄剧ず', '闅愯棌']" v-model="value" :data="columns" @change="dataChange"></el-transfer>
+		</el-dialog>
+	</div>
+</template>
+
+<style lang="scss" scoped>
 :deep(.el-transfer__button) {
   border-radius: 50%;
   display: block;
@@ -102,4 +101,4 @@
 .my-el-transfer {
   text-align: center;
 }
-</style>
\ No newline at end of file
+</style>
diff --git a/src/components/RuoYi/Doc/index.vue b/src/components/RuoYiDoc/index.vue
similarity index 68%
rename from src/components/RuoYi/Doc/index.vue
rename to src/components/RuoYiDoc/index.vue
index 3ad4266..9dc9c5f 100644
--- a/src/components/RuoYi/Doc/index.vue
+++ b/src/components/RuoYiDoc/index.vue
@@ -1,7 +1,7 @@
 <template>
-  <div>
-    <svg-icon icon-class="question" @click="goto" />
-  </div>
+	<div>
+		<svg-icon icon-class="question" @click="goto" />
+	</div>
 </template>
 
 <script setup>
@@ -10,4 +10,4 @@
 function goto() {
   window.open(url.value)
 }
-</script>
\ No newline at end of file
+</script>
diff --git a/src/components/RuoYi/Git/index.vue b/src/components/RuoYiGit/index.vue
similarity index 69%
rename from src/components/RuoYi/Git/index.vue
rename to src/components/RuoYiGit/index.vue
index 82393eb..3682250 100644
--- a/src/components/RuoYi/Git/index.vue
+++ b/src/components/RuoYiGit/index.vue
@@ -1,7 +1,7 @@
 <template>
-  <div>
-    <svg-icon icon-class="github" @click="goto" />
-  </div>
+	<div>
+		<svg-icon icon-class="github" @click="goto" />
+	</div>
 </template>
 
 <script setup>
diff --git a/src/components/Screenfull/index.vue b/src/components/Screenfull/index.vue
index 7ad28ea..3922f58 100644
--- a/src/components/Screenfull/index.vue
+++ b/src/components/Screenfull/index.vue
@@ -1,16 +1,14 @@
 <template>
-  <div>
-    <svg-icon :icon-class="isFullscreen ? 'exit-fullscreen' : 'fullscreen'" @click="toggle" />
-  </div>
+	<div>
+		<svg-icon :icon-class="isFullscreen ? 'exit-fullscreen' : 'fullscreen'" @click="toggle" />
+	</div>
 </template>
 
-<script setup>
-import { useFullscreen } from '@vueuse/core'
-
-const { isFullscreen, enter, exit, toggle } = useFullscreen();
+<script setup lang="ts">
+const { isFullscreen, toggle } = useFullscreen();
 </script>
 
-<style lang='scss' scoped>
+<style lang="scss" scoped>
 .screenfull-svg {
   display: inline-block;
   cursor: pointer;
@@ -19,4 +17,4 @@
   height: 20px;
   vertical-align: 10px;
 }
-</style>
\ No newline at end of file
+</style>
diff --git a/src/components/SizeSelect/index.vue b/src/components/SizeSelect/index.vue
index 4c2e7e9..a2b38d6 100644
--- a/src/components/SizeSelect/index.vue
+++ b/src/components/SizeSelect/index.vue
@@ -1,45 +1,44 @@
-<template>
-  <div>
-    <el-dropdown trigger="click" @command="handleSetSize">
-      <div class="size-icon--style">
-        <svg-icon class-name="size-icon" icon-class="size" />
-      </div>
-      <template #dropdown>
-        <el-dropdown-menu>
-          <el-dropdown-item v-for="item of sizeOptions" :key="item.value" :disabled="size === item.value" :command="item.value">
-            {{ item.label }}
-          </el-dropdown-item>
-        </el-dropdown-menu>
-      </template>
-    </el-dropdown>
-  </div>
-</template>
-
-<script setup>
+<script setup lang="ts">
 import useAppStore from "@/store/modules/app";
+import { ComponentInternalInstance } from "vue";
 
 const appStore = useAppStore();
 const size = computed(() => appStore.size);
-const route = useRoute();
-const router = useRouter();
-const { proxy } = getCurrentInstance();
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 const sizeOptions = ref([
   { label: "杈冨ぇ", value: "large" },
   { label: "榛樿", value: "default" },
   { label: "绋嶅皬", value: "small" },
 ]);
 
-function handleSetSize(size) {
-  proxy.$modal.loading("姝e湪璁剧疆甯冨眬澶у皬锛岃绋嶅��...");
+const handleSetSize = (size: string) => {
+  proxy?.$modal.loading("姝e湪璁剧疆甯冨眬澶у皬锛岃绋嶅��...");
   appStore.setSize(size);
   setTimeout("window.location.reload()", 1000);
 }
 </script>
 
-<style lang='scss' scoped>
+<template>
+	<div>
+		<el-dropdown trigger="click" @command="handleSetSize">
+			<div class="size-icon--style">
+				<svg-icon class-name="size-icon" icon-class="size" />
+			</div>
+			<template #dropdown>
+				<el-dropdown-menu>
+					<el-dropdown-item v-for="item of sizeOptions" :key="item.value" :disabled="size === item.value" :command="item.value">
+						{{ item.label }}
+					</el-dropdown-item>
+				</el-dropdown-menu>
+			</template>
+		</el-dropdown>
+	</div>
+</template>
+
+<style lang="scss" scoped>
 .size-icon--style {
   font-size: 18px;
   line-height: 50px;
   padding-right: 7px;
 }
-</style>
\ No newline at end of file
+</style>
diff --git a/src/components/SvgIcon/index.vue b/src/components/SvgIcon/index.vue
index 8c101f6..661ae51 100644
--- a/src/components/SvgIcon/index.vue
+++ b/src/components/SvgIcon/index.vue
@@ -1,39 +1,33 @@
-<template>
-  <svg :class="svgClass" aria-hidden="true">
-    <use :xlink:href="iconName" :fill="color" />
-  </svg>
-</template>
-
-<script>
-export default defineComponent({
-  props: {
-    iconClass: {
-      type: String,
-      required: true
-    },
-    className: {
-      type: String,
-      default: ''
-    },
-    color: {
-      type: String,
-      default: ''
-    },
+<script setup lang="ts">
+const props = defineProps({
+  iconClass: {
+    type: String,
+    required: true
   },
-  setup(props) {
-    return {
-      iconName: computed(() => `#icon-${props.iconClass}`),
-      svgClass: computed(() => {
-        if (props.className) {
-          return `svg-icon ${props.className}`
-        }
-        return 'svg-icon'
-      })
-    }
+  className: {
+    type: String,
+    default: ''
+  },
+  color: {
+    type: String,
+    default: ''
+  },
+})
+const iconName =  computed(() => `#icon-${props.iconClass}`);
+const svgClass = computed(() => {
+  if (props.className) {
+    return `svg-icon ${props.className}`
   }
+  return 'svg-icon'
 })
 </script>
 
+<template>
+	<svg :class="svgClass" aria-hidden="true">
+		<use :xlink:href="iconName" :fill="color" />
+	</svg>
+</template>
+
 <style scope lang="scss">
 .sub-el-icon,
 .nav-icon {
diff --git a/src/components/SvgIcon/svgicon.js b/src/components/SvgIcon/svgicon.js
deleted file mode 100644
index 4431719..0000000
--- a/src/components/SvgIcon/svgicon.js
+++ /dev/null
@@ -1,10 +0,0 @@
-import * as components from '@element-plus/icons-vue'
-
-export default {
-    install: (app) => {
-        for (const key in components) {
-            const componentConfig = components[key];
-            app.component(componentConfig.name, componentConfig);
-        }
-    },
-};
diff --git a/src/components/TopNav/index.vue b/src/components/TopNav/index.vue
index ac1d2e5..ea16377 100644
--- a/src/components/TopNav/index.vue
+++ b/src/components/TopNav/index.vue
@@ -1,44 +1,15 @@
-<template>
-  <el-menu
-    :default-active="activeMenu"
-    mode="horizontal"
-    @select="handleSelect"
-    :ellipsis="false"
-  >
-    <template v-for="(item, index) in topMenus">
-      <el-menu-item :style="{'--theme': theme}" :index="item.path" :key="index" v-if="index < visibleNumber"
-        ><svg-icon :icon-class="item.meta.icon" />
-        {{ item.meta.title }}</el-menu-item
-      >
-    </template>
-
-    <!-- 椤堕儴鑿滃崟瓒呭嚭鏁伴噺鎶樺彔 -->
-    <el-sub-menu :style="{'--theme': theme}" index="more" v-if="topMenus.length > visibleNumber">
-      <template #title>鏇村鑿滃崟</template>
-      <template v-for="(item, index) in topMenus">
-        <el-menu-item
-          :index="item.path"
-          :key="index"
-          v-if="index >= visibleNumber"
-          ><svg-icon :icon-class="item.meta.icon" />
-          {{ item.meta.title }}</el-menu-item
-        >
-      </template>
-    </el-sub-menu>
-  </el-menu>
-</template>
-
-<script setup>
-import { constantRoutes } from "@/router"
-import { isHttp } from '@/utils/validate'
-import useAppStore from '@/store/modules/app'
-import useSettingsStore from '@/store/modules/settings'
-import usePermissionStore from '@/store/modules/permission'
+<script setup lang="ts">
+import { constantRoutes } from '@/router';
+import { isHttp } from '@/utils/validate';
+import useAppStore from '@/store/modules/app';
+import useSettingsStore from '@/store/modules/settings';
+import usePermissionStore from '@/store/modules/permission';
+import { RouteOption } from 'vue-router';
 
 // 椤堕儴鏍忓垵濮嬫暟
-const visibleNumber = ref(null);
+const visibleNumber = ref<number>(-1);
 // 褰撳墠婵�娲昏彍鍗曠殑 index
-const currentIndex = ref(null);
+const currentIndex = ref<string>();
 // 闅愯棌渚ц竟鏍忚矾鐢�
 const hideList = ['/index', '/user/profile'];
 
@@ -55,12 +26,12 @@
 
 // 椤堕儴鏄剧ず鑿滃崟
 const topMenus = computed(() => {
-  let topMenus = [];
+  let topMenus:RouteOption[] = [];
   routers.value.map((menu) => {
     if (menu.hidden !== true) {
       // 鍏煎椤堕儴鏍忎竴绾ц彍鍗曞唴閮ㄨ烦杞�
       if (menu.path === "/") {
-          topMenus.push(menu.children[0]);
+          topMenus.push(menu.children? menu.children[0] : menu);
       } else {
           topMenus.push(menu);
       }
@@ -71,21 +42,21 @@
 
 // 璁剧疆瀛愯矾鐢�
 const childrenMenus = computed(() => {
-  let childrenMenus = [];
+  let childrenMenus:RouteOption[] = [];
   routers.value.map((router) => {
-    for (let item in router.children) {
-      if (router.children[item].parentPath === undefined) {
+    router.children?.forEach((item) => {
+      if (item.parentPath === undefined) {
         if(router.path === "/") {
-          router.children[item].path = "/" + router.children[item].path;
+          item.path = "/" + item.path;
         } else {
-          if(!isHttp(router.children[item].path)) {
-            router.children[item].path = router.path + "/" + router.children[item].path;
+          if(!isHttp(item.path)) {
+            item.path = router.path + "/" + item.path;
           }
         }
-        router.children[item].parentPath = router.path;
+        item.parentPath = router.path;
       }
-      childrenMenus.push(router.children[item]);
-    }
+      childrenMenus.push(item);
+    })
   })
   return constantRoutes.concat(childrenMenus);
 })
@@ -108,12 +79,12 @@
   return activePath;
 })
 
-function setVisibleNumber() {
+const setVisibleNumber = () => {
   const width = document.body.getBoundingClientRect().width / 3;
-  visibleNumber.value = parseInt(width / 85);
+  visibleNumber.value = parseInt(String(width / 85));
 }
 
-function handleSelect(key, keyPath) {
+const handleSelect = (key: string, keyPath: string[]) => {
   currentIndex.value = key;
   const route = routers.value.find(item => item.path === key);
   if (isHttp(key)) {
@@ -121,7 +92,7 @@
     window.open(key, "_blank");
   } else if (!route || !route.children) {
     // 娌℃湁瀛愯矾鐢辫矾寰勫唴閮ㄦ墦寮�
-    router.push({ path: key });
+    router.push({ path: key, fullPath: '' });
     appStore.toggleSideBarHide(true);
   } else {
     // 鏄剧ず宸︿晶鑱斿姩鑿滃崟
@@ -130,8 +101,8 @@
   }
 }
 
-function activeRoutes(key) {
-  let routes = [];
+const activeRoutes = (key: string) => {
+  let routes:RouteOption[] = [];
   if (childrenMenus.value && childrenMenus.value.length > 0) {
     childrenMenus.value.map((item) => {
       if (key == item.parentPath || (key == "index" && "" == item.path)) {
@@ -159,6 +130,26 @@
 })
 </script>
 
+<template>
+	<el-menu :default-active="activeMenu" mode="horizontal" @select="handleSelect" :ellipsis="false">
+		<template v-for="(item, index) in topMenus">
+			<el-menu-item :style="{'--theme': theme}" :index="item.path" :key="index" v-if="index < visibleNumber"
+				><svg-icon :icon-class="item.meta ? item.meta.icon : '' " /> {{ item.meta?.title }}</el-menu-item
+			>
+		</template>
+
+		<!-- 椤堕儴鑿滃崟瓒呭嚭鏁伴噺鎶樺彔 -->
+		<el-sub-menu :style="{'--theme': theme}" index="more" v-if="topMenus.length > visibleNumber">
+			<template #title>鏇村鑿滃崟</template>
+			<template v-for="(item, index) in topMenus">
+				<el-menu-item :index="item.path" :key="index" v-if="index >= visibleNumber"
+					><svg-icon :icon-class="item.meta ? item.meta.icon : '' " /> {{ item.meta?.title }}</el-menu-item
+				>
+			</template>
+		</el-sub-menu>
+	</el-menu>
+</template>
+
 <style lang="scss">
 .topmenu-container.el-menu--horizontal > .el-menu-item {
   float: left;
diff --git a/src/components/TreeSelect/index.vue b/src/components/TreeSelect/index.vue
index 4ff0e76..8e5f567 100644
--- a/src/components/TreeSelect/index.vue
+++ b/src/components/TreeSelect/index.vue
@@ -1,36 +1,5 @@
-<template>
-  <div class="el-tree-select">
-    <el-select
-      style="width: 100%"
-      v-model="valueId"
-      ref="treeSelect"
-      :filterable="true"
-      :clearable="true"
-      @clear="clearHandle"
-      :filter-method="selectFilterData"
-      :placeholder="placeholder"
-    >
-      <el-option :value="valueId" :label="valueTitle">
-        <el-tree
-          id="tree-option"
-          ref="selectTree"
-          :accordion="accordion"
-          :data="options"
-          :props="objMap"
-          :node-key="objMap.value"
-          :expand-on-click-node="false"
-          :default-expanded-keys="defaultExpandedKey"
-          :filter-node-method="filterNode"
-          @node-click="handleNodeClick"
-        ></el-tree>
-      </el-option>
-    </el-select>
-  </div>
-</template>
-
-<script setup>
-
-const { proxy } = getCurrentInstance();
+<script setup lang="ts">
+import { ElTreeSelect } from 'element-plus'
 
 const props = defineProps({
   /* 閰嶇疆椤� */
@@ -68,6 +37,9 @@
   }
 })
 
+
+const selectTree = ref(ElTreeSelect);
+
 const emit = defineEmits(['update:value']);
 
 const valueId = computed({
@@ -77,16 +49,16 @@
   }
 });
 const valueTitle = ref('');
-const defaultExpandedKey = ref([]);
+const defaultExpandedKey = ref<any[]>([]);
 
 function initHandle() {
   nextTick(() => {
     const selectedValue = valueId.value;
     if(selectedValue !== null && typeof (selectedValue) !== 'undefined') {
-      const node = proxy.$refs.selectTree.getNode(selectedValue)
+      const node = selectTree.value.getNode(selectedValue)
       if (node) {
         valueTitle.value = node.data[props.objMap.label]
-        proxy.$refs.selectTree.setCurrentKey(selectedValue) // 璁剧疆榛樿閫変腑
+        selectTree.value.setCurrentKey(selectedValue) // 璁剧疆榛樿閫変腑
         defaultExpandedKey.value = [selectedValue] // 璁剧疆榛樿灞曞紑
       }
     } else {
@@ -94,17 +66,17 @@
     }
   })
 }
-function handleNodeClick(node) {
+function handleNodeClick(node: any) {
   valueTitle.value = node[props.objMap.label]
   valueId.value = node[props.objMap.value];
   defaultExpandedKey.value = [];
-  proxy.$refs.treeSelect.blur()
+  selectTree.value.blur()
   selectFilterData('')
 }
-function selectFilterData(val) {
-  proxy.$refs.selectTree.filter(val)
+function selectFilterData(val: any) {
+  selectTree.value.filter(val)
 }
-function filterNode(value, data) {
+function filterNode(value: any, data: any) {
   if (!value) return true
   return data[props.objMap['label']].indexOf(value) !== -1
 }
@@ -128,7 +100,7 @@
 })
 </script>
 
-<style lang='scss' scoped>
+<style lang="scss" scoped>
 @import "@/assets/styles/variables.module.scss";
 .el-scrollbar .el-scrollbar__view .el-select-dropdown__item {
   padding: 0;
@@ -153,4 +125,34 @@
   background-color: mix(#fff, $--color-primary, 90%);
   color: $--color-primary;
 }
-</style>
\ No newline at end of file
+</style>
+
+<template>
+	<div class="el-tree-select">
+		<el-select
+			style="width: 100%"
+			v-model="valueId"
+			ref="treeSelect"
+			:filterable="true"
+			:clearable="true"
+			@clear="clearHandle"
+			:filter-method="selectFilterData"
+			:placeholder="placeholder"
+		>
+			<el-option :value="valueId" :label="valueTitle">
+				<el-tree
+					id="tree-option"
+					ref="selectTree"
+					:accordion="accordion"
+					:data="options"
+					:props="objMap"
+					:node-key="objMap.value"
+					:expand-on-click-node="false"
+					:default-expanded-keys="defaultExpandedKey"
+					:filter-node-method="filterNode"
+					@node-click="handleNodeClick"
+				></el-tree>
+			</el-option>
+		</el-select>
+	</div>
+</template>
diff --git a/src/components/iFrame/index.vue b/src/components/iFrame/index.vue
index 091b1a2..aff55e7 100644
--- a/src/components/iFrame/index.vue
+++ b/src/components/iFrame/index.vue
@@ -1,14 +1,4 @@
-<template>
-  <div v-loading="loading" :style="'height:' + height">
-    <iframe 
-      :src="url" 
-      frameborder="no" 
-      style="width: 100%; height: 100%" 
-      scrolling="auto" />
-  </div>
-</template>
-
-<script setup>
+<script setup lang="ts">
 const props = defineProps({
   src: {
     type: String,
@@ -29,3 +19,9 @@
   };
 })
 </script>
+
+<template>
+	<div v-loading="loading" :style="'height:' + height">
+		<iframe :src="url" frameborder="no" style="width: 100%; height: 100%" scrolling="auto" />
+	</div>
+</template>
diff --git a/src/directive/common/copyText.js b/src/directive/common/copyText.js
deleted file mode 100644
index 7063df8..0000000
--- a/src/directive/common/copyText.js
+++ /dev/null
@@ -1,66 +0,0 @@
-/**
-* v-copyText 澶嶅埗鏂囨湰鍐呭
-* Copyright (c) 2022 ruoyi
-*/
-
-export default {
-  beforeMount(el, { value, arg }) {
-    if (arg === "callback") {
-      el.$copyCallback = value;
-    } else {
-      el.$copyValue = value;
-      const handler = () => {
-        copyTextToClipboard(el.$copyValue);
-        if (el.$copyCallback) {
-          el.$copyCallback(el.$copyValue);
-        }
-      };
-      el.addEventListener("click", handler);
-      el.$destroyCopy = () => el.removeEventListener("click", handler);
-    }
-  }
-}
-
-function copyTextToClipboard(input, { target = document.body } = {}) {
-  const element = document.createElement('textarea');
-  const previouslyFocusedElement = document.activeElement;
-
-  element.value = input;
-
-  // Prevent keyboard from showing on mobile
-  element.setAttribute('readonly', '');
-
-  element.style.contain = 'strict';
-  element.style.position = 'absolute';
-  element.style.left = '-9999px';
-  element.style.fontSize = '12pt'; // Prevent zooming on iOS
-
-  const selection = document.getSelection();
-  const originalRange = selection.rangeCount > 0 && selection.getRangeAt(0);
-
-  target.append(element);
-  element.select();
-
-  // Explicit selection workaround for iOS
-  element.selectionStart = 0;
-  element.selectionEnd = input.length;
-
-  let isSuccess = false;
-  try {
-    isSuccess = document.execCommand('copy');
-  } catch { }
-
-  element.remove();
-
-  if (originalRange) {
-    selection.removeAllRanges();
-    selection.addRange(originalRange);
-  }
-
-  // Get the focus back on the previously focused element, if any
-  if (previouslyFocusedElement) {
-    previouslyFocusedElement.focus();
-  }
-
-  return isSuccess;
-}
diff --git a/src/directive/common/copyText.ts b/src/directive/common/copyText.ts
new file mode 100644
index 0000000..b924b8f
--- /dev/null
+++ b/src/directive/common/copyText.ts
@@ -0,0 +1,66 @@
+/**
+ * v-copyText 澶嶅埗鏂囨湰鍐呭
+ * Copyright (c) 2022 ruoyi
+ */
+
+export default {
+	beforeMount(el: any, { value, arg }: any) {
+		if (arg === 'callback') {
+			el.$copyCallback = value;
+		} else {
+			el.$copyValue = value;
+			const handler = () => {
+				copyTextToClipboard(el.$copyValue);
+				if (el.$copyCallback) {
+					el.$copyCallback(el.$copyValue);
+				}
+			};
+			el.addEventListener('click', handler);
+			el.$destroyCopy = () => el.removeEventListener('click', handler);
+		}
+	}
+};
+
+function copyTextToClipboard(input: string, { target = document.body } = {}) {
+	const element = document.createElement('textarea');
+	const previouslyFocusedElement = document.activeElement as HTMLInputElement;
+	element.value = input;
+	// Prevent keyboard from showing on mobile
+	element.setAttribute('readonly', '');
+
+	element.style.contain = 'strict';
+	element.style.position = 'absolute';
+	element.style.left = '-9999px';
+	element.style.fontSize = '12pt'; // Prevent zooming on iOS
+
+	const selection = document.getSelection();
+	let originalRange;
+	if (selection) {
+		originalRange = selection?.rangeCount > 0 && selection.getRangeAt(0);
+	}
+	target.append(element);
+	element.select();
+
+	// Explicit selection workaround for iOS
+	element.selectionStart = 0;
+	element.selectionEnd = input.length;
+
+	let isSuccess = false;
+	try {
+		isSuccess = document.execCommand('copy');
+	} catch (err) {
+		console.error(err);
+	}
+	element.remove();
+
+	if (originalRange) {
+		selection?.removeAllRanges();
+		selection?.addRange(originalRange);
+	}
+
+	// Get the focus back on the previously focused element, if any
+	if (previouslyFocusedElement) {
+		previouslyFocusedElement.focus();
+	}
+	return isSuccess;
+}
diff --git a/src/directive/index.js b/src/directive/index.js
deleted file mode 100644
index 86b8f88..0000000
--- a/src/directive/index.js
+++ /dev/null
@@ -1,9 +0,0 @@
-import hasRole from './permission/hasRole'
-import hasPermi from './permission/hasPermi'
-import copyText from './common/copyText'
-
-export default function directive(app){
-  app.directive('hasRole', hasRole)
-  app.directive('hasPermi', hasPermi)
-  app.directive('copyText', copyText)
-}
\ No newline at end of file
diff --git a/src/directive/index.ts b/src/directive/index.ts
new file mode 100644
index 0000000..500cf12
--- /dev/null
+++ b/src/directive/index.ts
@@ -0,0 +1,9 @@
+import copyText from './common/copyText';
+import { hasPermi, hasRoles } from './permission';
+import { App } from 'vue';
+
+export default (app: App) => {
+	app.directive('copyText', copyText);
+	app.directive('hasPermi', hasPermi);
+	app.directive('hasRoles', hasRoles);
+};
diff --git a/src/directive/permission/hasPermi.js b/src/directive/permission/hasPermi.js
deleted file mode 100644
index 44ef3f8..0000000
--- a/src/directive/permission/hasPermi.js
+++ /dev/null
@@ -1,28 +0,0 @@
- /**
- * v-hasPermi 鎿嶄綔鏉冮檺澶勭悊
- * Copyright (c) 2019 ruoyi
- */
- 
-import useUserStore from '@/store/modules/user'
-
-export default {
-  mounted(el, binding, vnode) {
-    const { value } = binding
-    const all_permission = "*:*:*";
-    const permissions = useUserStore().permissions
-
-    if (value && value instanceof Array && value.length > 0) {
-      const permissionFlag = value
-
-      const hasPermissions = permissions.some(permission => {
-        return all_permission === permission || permissionFlag.includes(permission)
-      })
-
-      if (!hasPermissions) {
-        el.parentNode && el.parentNode.removeChild(el)
-      }
-    } else {
-      throw new Error(`璇疯缃搷浣滄潈闄愭爣绛惧�糮)
-    }
-  }
-}
diff --git a/src/directive/permission/hasRole.js b/src/directive/permission/hasRole.js
deleted file mode 100644
index 8774bdd..0000000
--- a/src/directive/permission/hasRole.js
+++ /dev/null
@@ -1,28 +0,0 @@
- /**
- * v-hasRole 瑙掕壊鏉冮檺澶勭悊
- * Copyright (c) 2019 ruoyi
- */
- 
-import useUserStore from '@/store/modules/user'
-
-export default {
-  mounted(el, binding, vnode) {
-    const { value } = binding
-    const super_admin = "admin";
-    const roles = useUserStore().roles
-
-    if (value && value instanceof Array && value.length > 0) {
-      const roleFlag = value
-
-      const hasRole = roles.some(role => {
-        return super_admin === role || roleFlag.includes(role)
-      })
-
-      if (!hasRole) {
-        el.parentNode && el.parentNode.removeChild(el)
-      }
-    } else {
-      throw new Error(`璇疯缃鑹叉潈闄愭爣绛惧�糮)
-    }
-  }
-}
diff --git a/src/directive/permission/index.ts b/src/directive/permission/index.ts
new file mode 100644
index 0000000..c52b949
--- /dev/null
+++ b/src/directive/permission/index.ts
@@ -0,0 +1,44 @@
+import { Directive, DirectiveBinding } from 'vue';
+import useUserStore from '@/store/modules/user';
+/**
+ * 鎿嶄綔鏉冮檺澶勭悊
+ */
+export const hasPermi: Directive = {
+	mounted(el: HTMLElement, binding: DirectiveBinding) {
+		const { permissions } = useUserStore();
+		// 銆屽叾浠栬鑹层�嶆寜閽潈闄愭牎楠�
+		const { value } = binding;
+		if (value && value instanceof Array && value.length > 0) {
+			const hasPermission = permissions.some((permi) => {
+				return permi === '*:*:*' || value.includes(permi);
+			});
+			if (!hasPermission) {
+				el.parentNode && el.parentNode.removeChild(el);
+				return false;
+			}
+		} else {
+			throw new Error("check perms! Like v-has-permi=\"['sys:user:add','sys:user:edit']\"");
+		}
+	}
+};
+
+/**
+ * 瑙掕壊鏉冮檺澶勭悊
+ */
+export const hasRoles: Directive = {
+	mounted(el: HTMLElement, binding: DirectiveBinding) {
+		const { value } = binding;
+		const { roles } = useUserStore();
+		if (value && value instanceof Array && value.length > 0) {
+			const hasRole = roles.some((role) => {
+				return role === 'admin' || value.includes(role);
+			});
+			if (!hasRole) {
+				el.parentNode && el.parentNode.removeChild(el);
+				return false;
+			}
+		} else {
+			throw new Error("check roles! Like v-has-roles=\"['admin','test']\"");
+		}
+	}
+};
diff --git a/src/enums/MenuTypeEnum.ts b/src/enums/MenuTypeEnum.ts
new file mode 100644
index 0000000..ceb22fb
--- /dev/null
+++ b/src/enums/MenuTypeEnum.ts
@@ -0,0 +1,15 @@
+export enum MenuTypeEnum {
+	/**
+	 * 鐩綍
+	 */
+	M = 'M',
+	/**
+	 * 鑿滃崟
+	 */
+	C = 'C',
+
+	/**
+	 * 鎸夐挳
+	 */
+	F = 'F'
+}
diff --git a/src/enums/RespEnum.ts b/src/enums/RespEnum.ts
new file mode 100644
index 0000000..e723fa8
--- /dev/null
+++ b/src/enums/RespEnum.ts
@@ -0,0 +1,90 @@
+export enum HttpStatus {
+	/**
+	 * 鎿嶄綔鎴愬姛
+	 */
+	SUCCESS = 200,
+	/**
+	 * 瀵硅薄鍒涘缓鎴愬姛
+	 */
+	CREATED = 201,
+	/**
+	 * 璇锋眰宸茬粡琚帴鍙�
+	 */
+	ACCEPTED = 202,
+	/**
+	 * 鎿嶄綔宸茬粡鎵ц鎴愬姛锛屼絾鏄病鏈夎繑鍥炴暟鎹�
+	 */
+	NO_CONTENT = 204,
+	/**
+	 * 璧勬簮宸茬粡琚Щ闄�
+	 */
+	MOVED_PERM = 301,
+	/**
+	 * 閲嶅畾鍚�
+	 */
+	SEE_OTHER = 303,
+	/**
+	 * 璧勬簮娌℃湁琚慨鏀�
+	 */
+	NOT_MODIFIED = 304,
+	/**
+	 * 鍙傛暟鍒楄〃閿欒锛堢己灏戯紝鏍煎紡涓嶅尮閰嶏級
+	 */
+	PARAM_ERROR = 400,
+	/**
+	 * 鏈巿鏉�
+	 */
+	UNAUTHORIZED = 401,
+	/**
+	 * 璁块棶鍙楅檺锛屾巿鏉冭繃鏈�
+	 */
+	FORBIDDEN = 403,
+	/**
+	 * 璧勬簮锛屾湇鍔℃湭鎵惧埌
+	 */
+	NOT_FOUND = 404,
+	/**
+	 * 涓嶅厑璁哥殑http鏂规硶
+	 */
+	BAD_METHOD = 405,
+	/**
+	 * 璧勬簮鍐茬獊锛屾垨鑰呰祫婧愯閿�
+	 */
+	CONFLICT = 409,
+	/**
+	 * 涓嶆敮鎸佺殑鏁版嵁锛屽獟浣撶被鍨�
+	 */
+	UNSUPPORTED_TYPE = 415,
+	/**
+	 * 绯荤粺鍐呴儴閿欒
+	 */
+	SERVER_ERROR = 500,
+	/**
+	 * 鎺ュ彛鏈疄鐜�
+	 */
+	NOT_IMPLEMENTED = 501,
+	/**
+	 * 鏈嶅姟涓嶅彲鐢紝杩囪浇鎴栬�呯淮鎶�
+	 */
+	BAD_GATEWAY = 502,
+	/**
+	 * 缃戝叧瓒呮椂
+	 */
+	GATEWAY_TIMEOUT = 504,
+	/**
+	 * 鏈煡閿欒
+	 */
+	UNKNOWN_ERROR = 520,
+	/**
+	 * 鏈嶅姟鏈煡閿欒
+	 */
+	SERVICE_ERROR = 521,
+	/**
+	 * 鏁版嵁搴撴湭鐭ラ敊璇�
+	 */
+	DATABASE_ERROR = 522,
+	/**
+	 * 绯荤粺璀﹀憡娑堟伅
+	 */
+	WARN = 601
+}
diff --git a/src/enums/SettingTypeEnum.ts b/src/enums/SettingTypeEnum.ts
new file mode 100644
index 0000000..6589b37
--- /dev/null
+++ b/src/enums/SettingTypeEnum.ts
@@ -0,0 +1,16 @@
+export enum SettingTypeEnum {
+	TITLE = 'title',
+	THEME = 'theme',
+	SIDE_THEME = 'sideTheme',
+	SHOW_SETTINGS = 'showSettings',
+	TOP_NAV = 'topNav',
+	TAGS_VIEW = 'tagsView',
+	FIXED_HEADER = 'fixedHeader',
+	SIDEBAR_LOGO = 'sidebarLogo',
+	DYNAMIC_TITLE = 'dynamicTitle',
+	ANIMATION_ENABLE = 'animationEnable',
+	LAYOUT = 'layout',
+	DARK = 'dark',
+
+	LAYOUT_SETTING = 'layout-setting'
+}
diff --git a/src/enums/layout/LayoutEnum.ts b/src/enums/layout/LayoutEnum.ts
new file mode 100644
index 0000000..4c26219
--- /dev/null
+++ b/src/enums/layout/LayoutEnum.ts
@@ -0,0 +1,4 @@
+export enum ThemeEnum {
+	DARK = 'theme-dark',
+	LIGHT = 'theme-light'
+}
diff --git a/src/lang/en.ts b/src/lang/en.ts
new file mode 100644
index 0000000..37dfed6
--- /dev/null
+++ b/src/lang/en.ts
@@ -0,0 +1,25 @@
+export default {
+	// 璺敱鍥介檯鍖�
+	route: {
+		dashboard: 'Dashboard',
+		document: 'Document'
+	},
+	// 鐧诲綍椤甸潰鍥介檯鍖�
+	login: {
+		title: 'vue3-element-admin',
+		username: 'Username',
+		password: 'Password',
+		login: 'Login',
+		code: 'Verification Code',
+		copyright: '',
+		icp: '',
+		thirdPartyLogin: 'third-party login'
+	},
+	// 瀵艰埅鏍忓浗闄呭寲
+	navbar: {
+		dashboard: 'Dashboard',
+		logout: 'Logout',
+		document: 'Document',
+		gitee: 'Gitee'
+	}
+};
diff --git a/src/lang/index.ts b/src/lang/index.ts
new file mode 100644
index 0000000..1048211
--- /dev/null
+++ b/src/lang/index.ts
@@ -0,0 +1,45 @@
+// 鑷畾涔夊浗闄呭寲閰嶇疆
+import { createI18n } from 'vue-i18n';
+
+// 鏈湴璇█鍖�
+import enLocale from './en';
+import zhCnLocale from './zh-cn';
+
+const messages = {
+	'zh-cn': {
+		...zhCnLocale
+	},
+	en: {
+		...enLocale
+	}
+};
+
+/**
+ * 鑾峰彇褰撳墠绯荤粺浣跨敤璇█瀛楃涓�
+ *
+ * @returns zh-cn|en ...
+ */
+export const getLanguage = () => {
+	// 鏈湴缂撳瓨鑾峰彇
+	let language = localStorage.getItem('language');
+	if (language) {
+		return language;
+	}
+	// 娴忚鍣ㄤ娇鐢ㄨ瑷�
+	language = navigator.language.toLowerCase();
+	const locales = Object.keys(messages);
+	for (const locale of locales) {
+		if (language.indexOf(locale) > -1) {
+			return locale;
+		}
+	}
+	return 'zh-cn';
+};
+
+const i18n = createI18n({
+	legacy: false,
+	locale: getLanguage(),
+	messages: messages
+});
+
+export default i18n;
diff --git a/src/lang/zh-cn.ts b/src/lang/zh-cn.ts
new file mode 100644
index 0000000..5f7d5b3
--- /dev/null
+++ b/src/lang/zh-cn.ts
@@ -0,0 +1,24 @@
+export default {
+	// 璺敱鍥介檯鍖�
+	route: {
+		dashboard: '棣栭〉',
+		document: '椤圭洰鏂囨。'
+	},
+	// 鐧诲綍椤甸潰鍥介檯鍖�
+	login: {
+		title: 'vue3-element-admin',
+		username: '鐢ㄦ埛鍚�',
+		password: '瀵嗙爜',
+		login: '鐧� 褰�',
+		code: '璇疯緭鍏ラ獙璇佺爜',
+		copyright: '',
+		icp: '',
+		thirdPartyLogin: '绗笁鏂圭櫥褰�'
+	},
+	navbar: {
+		dashboard: '棣栭〉',
+		logout: '娉ㄩ攢',
+		document: '椤圭洰鏂囨。',
+		gitee: '鐮佷簯'
+	}
+};
diff --git a/src/layout/components/AppMain.vue b/src/layout/components/AppMain.vue
index 5b51fcf..e600479 100644
--- a/src/layout/components/AppMain.vue
+++ b/src/layout/components/AppMain.vue
@@ -1,22 +1,42 @@
-<template>
-  <section class="app-main">
-    <router-view v-slot="{ Component, route }">
-      <transition name="fade-transform" mode="out-in">
-        <keep-alive :include="tagsViewStore.cachedViews">
-          <component v-if="!route.meta.link" :is="Component" :key="route.path"/>
-        </keep-alive>
-      </transition>
-    </router-view>
-    <iframe-toggle />
-  </section>
-</template>
-
-<script setup>
-import iframeToggle from "./IframeToggle/index"
-import useTagsViewStore from '@/store/modules/tagsView'
-
-const tagsViewStore = useTagsViewStore()
+<script lang="ts">
+export default {
+  name: 'AppMin'
+}
 </script>
+
+<script setup lang="ts">
+import useTagsViewStore from '@/store/modules/tagsView';
+import useSettingsStore from '@/store/modules/settings';
+import IframeToggle  from './IframeToggle/index.vue'
+import { ComponentInternalInstance } from "vue";
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const tagsViewStore = useTagsViewStore();
+
+// 闅忔満鍔ㄧ敾闆嗗悎
+const animante = ref<string>('');
+const animationEnable = ref(useSettingsStore().animationEnable);
+watch(()=> useSettingsStore().animationEnable, (val) => {
+  animationEnable.value = val;
+  if (val) {
+    animante.value = proxy?.animate.animateList[Math.round(Math.random() * proxy?.animate.animateList.length)] as string;
+  } else {
+    animante.value = proxy?.animate.defaultAnimate as string;
+  }
+}, { immediate: true });
+</script>
+
+<template>
+	<section class="app-main">
+		<router-view v-slot="{ Component, route }">
+			<transition :enter-active-class="animante" mode="out-in">
+				<keep-alive :include="tagsViewStore.cachedViews">
+					<component v-if="!route.meta.link" :is="Component" :key="route.path" />
+				</keep-alive>
+			</transition>
+		</router-view>
+		<iframe-toggle />
+	</section>
+</template>
 
 <style lang="scss" scoped>
 .app-main {
@@ -27,7 +47,7 @@
   overflow: hidden;
 }
 
-.fixed-header + .app-main {
+.fixed-header+.app-main {
   padding-top: 50px;
 }
 
@@ -37,7 +57,7 @@
     min-height: calc(100vh - 84px);
   }
 
-  .fixed-header + .app-main {
+  .fixed-header+.app-main {
     padding-top: 84px;
   }
 }
@@ -50,4 +70,4 @@
     padding-right: 17px;
   }
 }
-</style>
\ No newline at end of file
+</style>
diff --git a/src/layout/components/IframeToggle/index.vue b/src/layout/components/IframeToggle/index.vue
index 2e07fe8..f5d167c 100644
--- a/src/layout/components/IframeToggle/index.vue
+++ b/src/layout/components/IframeToggle/index.vue
@@ -1,19 +1,19 @@
-<template>
-  <transition-group name="fade-transform" mode="out-in">
-    <inner-link
-      v-for="(item, index) in tagsViewStore.iframeViews"
-      :key="item.path"
-      :iframeId="'iframe' + index"
-      v-show="route.path === item.path"
-      :src="item.meta.link"
-    ></inner-link>
-  </transition-group>
-</template>
-
-<script setup>
-import InnerLink from "../InnerLink/index"
-import useTagsViewStore from '@/store/modules/tagsView'
+<script setup lang="ts">
+import InnerLink from "../InnerLink/index.vue";
+import useTagsViewStore from '@/store/modules/tagsView';
 
 const route = useRoute();
 const tagsViewStore = useTagsViewStore()
 </script>
+
+<template>
+	<transition-group name="fade-transform" mode="out-in">
+		<inner-link
+			v-for="(item, index) in tagsViewStore.iframeViews"
+			:key="item.path"
+			:iframeId="'iframe' + index"
+			v-show="route.path === item.path"
+			:src="item.meta ? item.meta.link : ''"
+		></inner-link>
+	</transition-group>
+</template>
diff --git a/src/layout/components/InnerLink/index.vue b/src/layout/components/InnerLink/index.vue
index 53a903c..c1b8bbb 100644
--- a/src/layout/components/InnerLink/index.vue
+++ b/src/layout/components/InnerLink/index.vue
@@ -1,15 +1,4 @@
-<template>
-  <div :style="'height:' + height">
-    <iframe
-      :id="iframeId"
-      style="width: 100%; height: 100%"
-      :src="src"
-      frameborder="no"
-    ></iframe>
-  </div>
-</template>
-
-<script setup>
+<script setup lang="ts">
 const props = defineProps({
   src: {
     type: String,
@@ -19,6 +8,11 @@
     type: String
   }
 });
-
 const height = ref(document.documentElement.clientHeight - 94.5 + "px");
 </script>
+
+<template>
+	<div :style="'height:' + height">
+		<iframe :id="iframeId" style="width: 100%; height: 100%" :src="src" frameborder="no"></iframe>
+	</div>
+</template>
diff --git a/src/layout/components/Navbar.vue b/src/layout/components/Navbar.vue
index 4972956..2df43d7 100644
--- a/src/layout/components/Navbar.vue
+++ b/src/layout/components/Navbar.vue
@@ -1,165 +1,155 @@
-<template>
-  <div class="navbar">
-    <hamburger id="hamburger-container" :is-active="appStore.sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />
-    <breadcrumb id="breadcrumb-container" class="breadcrumb-container" v-if="!settingsStore.topNav" />
-    <top-nav id="topmenu-container" class="topmenu-container" v-if="settingsStore.topNav" />
-
-    <div class="right-menu flex align-center">
-      <template v-if="appStore.device !== 'mobile'">
-        <el-select v-model="companyName"
-                   clearable
-                   filterable
-                   reserve-keyword
-                   placeholder="璇烽�夋嫨绉熸埛"
-                   v-if="userId === 1"
-                   @change="dynamicTenantEvent"
-                   @clear="dynamicClearEvent">
-          <el-option
-              v-for="item in tenantList"
-              :key="item.tenantId"
-              :label="item.companyName"
-              :value="item.tenantId">
-          </el-option>
-          <template #prefix><svg-icon icon-class="company" class="el-input__icon input-icon" /></template>
-        </el-select>
-
-        <header-search id="header-search" class="right-menu-item" />
-
-        <el-tooltip content="婧愮爜鍦板潃" effect="dark" placement="bottom">
-          <ruo-yi-git id="ruoyi-git" class="right-menu-item hover-effect" />
-        </el-tooltip>
-
-        <el-tooltip content="鏂囨。鍦板潃" effect="dark" placement="bottom">
-          <ruo-yi-doc id="ruoyi-doc" class="right-menu-item hover-effect" />
-        </el-tooltip>
-
-        <screenfull id="screenfull" class="right-menu-item hover-effect" />
-
-        <el-tooltip content="甯冨眬澶у皬" effect="dark" placement="bottom">
-          <size-select id="size-select" class="right-menu-item hover-effect" />
-        </el-tooltip>
-      </template>
-      <div class="avatar-container">
-        <el-dropdown @command="handleCommand" class="right-menu-item hover-effect" trigger="click">
-          <div class="avatar-wrapper">
-            <img :src="userStore.avatar" class="user-avatar" />
-            <el-icon><caret-bottom /></el-icon>
-          </div>
-          <template #dropdown>
-            <el-dropdown-menu>
-              <router-link to="/user/profile" v-if="!dynamic">
-                <el-dropdown-item>涓汉涓績</el-dropdown-item>
-              </router-link>
-              <el-dropdown-item command="setLayout">
-                <span>甯冨眬璁剧疆</span>
-              </el-dropdown-item>
-              <el-dropdown-item divided command="logout">
-                <span>閫�鍑虹櫥褰�</span>
-              </el-dropdown-item>
-            </el-dropdown-menu>
-          </template>
-        </el-dropdown>
-      </div>
-    </div>
-  </div>
-</template>
-
-<script setup>
-import { ElMessageBox } from 'element-plus'
-import Breadcrumb from '@/components/Breadcrumb'
-import TopNav from '@/components/TopNav'
-import Hamburger from '@/components/Hamburger'
-import Screenfull from '@/components/Screenfull'
-import SizeSelect from '@/components/SizeSelect'
-import HeaderSearch from '@/components/HeaderSearch'
-import RuoYiGit from '@/components/RuoYi/Git'
-import RuoYiDoc from '@/components/RuoYi/Doc'
+<script setup lang="ts">
 import useAppStore from '@/store/modules/app'
 import useUserStore from '@/store/modules/user'
 import useSettingsStore from '@/store/modules/settings'
 import { getTenantList } from "@/api/login";
 import { dynamicClear, dynamicTenant } from "@/api/system/tenant";
+import { ComponentInternalInstance } from "vue";
+import { TenantVO } from "@/api/types";
 
 const appStore = useAppStore()
 const userStore = useUserStore()
 const settingsStore = useSettingsStore()
 
-const { proxy } = getCurrentInstance();
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 
 const userId = ref(userStore.userId);
 const companyName = ref(undefined);
-const tenantList = ref([]);
+const tenantList = ref<TenantVO[]>([]);
 // 鏄惁鍒囨崲浜嗙鎴�
 const dynamic = ref(false);
+// 绉熸埛寮�鍏�
+const tenantEnabled = ref(true);
 
 // 鍔ㄦ�佸垏鎹�
-function dynamicTenantEvent(tenantId) {
+const dynamicTenantEvent = async (tenantId: string) => {
   if (companyName.value != null && companyName.value !== '') {
-    dynamicTenant(tenantId).then(res => {
-      dynamic.value = true;
-      proxy.$tab.closeAllPage()
-      proxy.$router.push('/')
-    });
+    await dynamicTenant(tenantId);
+    dynamic.value = true;
+    proxy?.$tab.closeAllPage();
+    proxy?.$router.push('/');
   }
 }
 
-function dynamicClearEvent() {
-  dynamicClear().then(res => {
-    dynamic.value = false;
-    proxy.$tab.closeAllPage()
-    proxy.$router.push('/')
-  });
+const dynamicClearEvent = async () => {
+  await dynamicClear();
+  dynamic.value = false;
+  proxy?.$tab.closeAllPage();
+  proxy?.$router.push('/')
 }
 
-// 绉熸埛鍒楄〃
-function initTenantList() {
-  getTenantList().then(res => {
-    tenantList.value = res.data;
-  });
+/** 绉熸埛鍒楄〃 */
+const initTenantList = async () => {
+  const { data } = await getTenantList();
+  tenantEnabled.value = data.tenantEnabled === undefined ? true : data.tenantEnabled;
+  if (tenantEnabled.value) {
+      tenantList.value = data.voList;
+  }
 }
 
 defineExpose({
   initTenantList,
 })
 
-function toggleSideBar() {
+const toggleSideBar = () => {
   appStore.toggleSideBar()
 }
 
-function handleCommand(command) {
-  switch (command) {
-    case "setLayout":
-      setLayout();
-      break;
-    case "logout":
-      logout();
-      break;
-    default:
-      break;
-  }
-}
-
-function logout() {
-  ElMessageBox.confirm('纭畾娉ㄩ攢骞堕��鍑虹郴缁熷悧锛�', '鎻愮ず', {
+const logout = async () => {
+  await ElMessageBox.confirm('纭畾娉ㄩ攢骞堕��鍑虹郴缁熷悧锛�', '鎻愮ず', {
     confirmButtonText: '纭畾',
     cancelButtonText: '鍙栨秷',
     type: 'warning'
-  }).then(() => {
-    userStore.logOut().then(() => {
-      location.href = import.meta.env.VITE_APP_CONTEXT_PATH + 'index';
-    })
-  }).catch(() => { });
+  })
+  await userStore.logout()
+  location.href = import.meta.env.VITE_APP_CONTEXT_PATH + 'index';
 }
 
 const emits = defineEmits(['setLayout'])
-function setLayout() {
+const setLayout = () => {
   emits('setLayout');
+}
+// 瀹氫箟Command鏂规硶瀵硅薄 閫氳繃key鐩存帴璋冪敤鏂规硶
+const commandMap: {[key: string]: any} = {
+  setLayout,
+  logout
+};
+const handleCommand = (command: string) => {
+  // 鍒ゆ柇鏄惁瀛樺湪璇ユ柟娉�
+  if (commandMap[command]) {
+    commandMap[command]();
+  }
 }
 </script>
 
-<style lang='scss' scoped>
+<template>
+	<div class="navbar">
+		<hamburger id="hamburger-container" :is-active="appStore.sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />
+		<breadcrumb id="breadcrumb-container" class="breadcrumb-container" v-if="!settingsStore.topNav" />
+		<top-nav id="topmenu-container" class="topmenu-container" v-if="settingsStore.topNav" />
 
-:deep .el-select .el-input__wrapper {
+		<div class="right-menu flex align-center">
+			<template v-if="appStore.device !== 'mobile'">
+				<el-select
+					v-model="companyName"
+					clearable
+					filterable
+					reserve-keyword
+					placeholder="璇烽�夋嫨绉熸埛"
+					v-if="userId === 1 && tenantEnabled"
+					@change="dynamicTenantEvent"
+					@clear="dynamicClearEvent"
+				>
+					<el-option v-for="item in tenantList" :key="item.tenantId" :label="item.companyName" :value="item.tenantId"> </el-option>
+					<template #prefix><svg-icon icon-class="company" class="el-input__icon input-icon" /></template>
+				</el-select>
+
+				<header-search id="header-search" class="right-menu-item" />
+
+				<el-tooltip content="婧愮爜鍦板潃" effect="dark" placement="bottom">
+					<ruo-yi-git id="ruoyi-git" class="right-menu-item hover-effect" />
+				</el-tooltip>
+
+				<el-tooltip content="鏂囨。鍦板潃" effect="dark" placement="bottom">
+					<ruo-yi-doc id="ruoyi-doc" class="right-menu-item hover-effect" />
+				</el-tooltip>
+
+				<el-tooltip content="鍏ㄥ睆" effect="dark" placement="bottom">
+					<screenfull id="screenfull" class="right-menu-item hover-effect" />
+				</el-tooltip>
+
+				<el-tooltip content="甯冨眬澶у皬" effect="dark" placement="bottom">
+					<size-select id="size-select" class="right-menu-item hover-effect" />
+				</el-tooltip>
+			</template>
+			<div class="avatar-container">
+				<el-dropdown @command="handleCommand" class="right-menu-item hover-effect" trigger="click">
+					<div class="avatar-wrapper">
+						<img :src="userStore.avatar" class="user-avatar" />
+						<el-icon><caret-bottom /></el-icon>
+					</div>
+					<template #dropdown>
+						<el-dropdown-menu>
+							<router-link to="/user/profile" v-if="!dynamic">
+								<el-dropdown-item>涓汉涓績</el-dropdown-item>
+							</router-link>
+							<el-dropdown-item command="setLayout">
+								<span>甯冨眬璁剧疆</span>
+							</el-dropdown-item>
+							<el-dropdown-item divided command="logout">
+								<span>閫�鍑虹櫥褰�</span>
+							</el-dropdown-item>
+						</el-dropdown-menu>
+					</template>
+				</el-dropdown>
+			</div>
+		</div>
+	</div>
+</template>
+
+<style lang="scss" scoped>
+
+:deep(.el-select .el-input__wrapper) {
   height:30px;
 }
 
diff --git a/src/layout/components/Settings/index.vue b/src/layout/components/Settings/index.vue
index 3360959..132a7ed 100644
--- a/src/layout/components/Settings/index.vue
+++ b/src/layout/components/Settings/index.vue
@@ -1,95 +1,13 @@
-<template>
-  <el-drawer v-model="showSettings" :withHeader="false" direction="rtl" size="300px">
-    <div class="setting-drawer-title">
-      <h3 class="drawer-title">涓婚椋庢牸璁剧疆</h3>
-    </div>
-    <div class="setting-drawer-block-checbox">
-      <div class="setting-drawer-block-checbox-item" @click="handleTheme('theme-dark')">
-        <img src="@/assets/images/dark.svg" alt="dark" />
-        <div v-if="sideTheme === 'theme-dark'" class="setting-drawer-block-checbox-selectIcon" style="display: block;">
-          <i aria-label="鍥炬爣: check" class="anticon anticon-check">
-            <svg viewBox="64 64 896 896" data-icon="check" width="1em" height="1em" :fill="theme" aria-hidden="true" focusable="false" class>
-              <path d="M912 190h-69.9c-9.8 0-19.1 4.5-25.1 12.2L404.7 724.5 207 474a32 32 0 0 0-25.1-12.2H112c-6.7 0-10.4 7.7-6.3 12.9l273.9 347c12.8 16.2 37.4 16.2 50.3 0l488.4-618.9c4.1-5.1.4-12.8-6.3-12.8z" />
-            </svg>
-          </i>
-        </div>
-      </div>
-      <div class="setting-drawer-block-checbox-item" @click="handleTheme('theme-light')">
-        <img src="@/assets/images/light.svg" alt="light" />
-        <div v-if="sideTheme === 'theme-light'" class="setting-drawer-block-checbox-selectIcon" style="display: block;">
-          <i aria-label="鍥炬爣: check" class="anticon anticon-check">
-            <svg viewBox="64 64 896 896" data-icon="check" width="1em" height="1em" :fill="theme" aria-hidden="true" focusable="false" class>
-              <path d="M912 190h-69.9c-9.8 0-19.1 4.5-25.1 12.2L404.7 724.5 207 474a32 32 0 0 0-25.1-12.2H112c-6.7 0-10.4 7.7-6.3 12.9l273.9 347c12.8 16.2 37.4 16.2 50.3 0l488.4-618.9c4.1-5.1.4-12.8-6.3-12.8z" />
-            </svg>
-          </i>
-        </div>
-      </div>
-    </div>
-    <div class="drawer-item">
-      <span>涓婚棰滆壊</span>
-      <span class="comp-style">
-        <el-color-picker v-model="theme" :predefine="predefineColors" @change="themeChange"/>
-      </span>
-    </div>
-    <el-divider />
-
-    <h3 class="drawer-title">绯荤粺甯冨眬閰嶇疆</h3>
-
-    <div class="drawer-item">
-      <span>寮�鍚� TopNav</span>
-      <span class="comp-style">
-        <el-switch v-model="topNav" class="drawer-switch" />
-      </span>
-    </div>
-
-    <div class="drawer-item">
-      <span>寮�鍚� Tags-Views</span>
-      <span class="comp-style">
-        <el-switch v-model="tagsView" class="drawer-switch" />
-      </span>
-    </div>
-
-    <div class="drawer-item">
-      <span>鍥哄畾 Header</span>
-      <span class="comp-style">
-        <el-switch v-model="fixedHeader" class="drawer-switch" />
-      </span>
-    </div>
-
-    <div class="drawer-item">
-      <span>鏄剧ず Logo</span>
-      <span class="comp-style">
-        <el-switch v-model="sidebarLogo" class="drawer-switch" />
-      </span>
-    </div>
-
-    <div class="drawer-item">
-      <span>鍔ㄦ�佹爣棰�</span>
-      <span class="comp-style">
-        <el-switch v-model="dynamicTitle" class="drawer-switch" />
-      </span>
-    </div>
-
-    <el-divider />
-
-    <el-button type="primary" plain icon="DocumentAdd" @click="saveSetting">淇濆瓨閰嶇疆</el-button>
-    <el-button plain icon="Refresh" @click="resetSetting">閲嶇疆閰嶇疆</el-button>
-  </el-drawer>
-
-</template>
-
-<script setup>
-import variables from '@/assets/styles/variables.module.scss'
-import originElementPlus from 'element-plus/theme-chalk/index.css'
-import axios from 'axios'
-import { ElLoading, ElMessage } from 'element-plus'
+<script setup lang="ts">
 import { useDynamicTitle } from '@/utils/dynamicTitle'
 import useAppStore from '@/store/modules/app'
 import useSettingsStore from '@/store/modules/settings'
 import usePermissionStore from '@/store/modules/permission'
 import { handleThemeStyle } from '@/utils/theme'
+import { ComponentInternalInstance } from "vue";
+import { SettingTypeEnum } from "@/enums/SettingTypeEnum";
 
-const { proxy } = getCurrentInstance();
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 const appStore = useAppStore()
 const settingsStore = useSettingsStore()
 const permissionStore = usePermissionStore()
@@ -103,7 +21,7 @@
 const topNav = computed({
   get: () => storeSettings.value.topNav,
   set: (val) => {
-    settingsStore.changeSetting({ key: 'topNav', value: val })
+    settingsStore.changeSetting({ key: SettingTypeEnum.TOP_NAV, value: val })
     if (!val) {
       appStore.toggleSideBarHide(false);
       permissionStore.setSidebarRouters(permissionStore.defaultRoutes);
@@ -114,44 +32,46 @@
 const tagsView = computed({
   get: () => storeSettings.value.tagsView,
   set: (val) => {
-    settingsStore.changeSetting({ key: 'tagsView', value: val })
+    settingsStore.changeSetting({ key: SettingTypeEnum.TAGS_VIEW, value: val })
   }
 })
 /**鏄惁闇�瑕佸浐瀹氬ご閮� */
 const fixedHeader = computed({
   get: () => storeSettings.value.fixedHeader,
   set: (val) => {
-    settingsStore.changeSetting({ key: 'fixedHeader', value: val })
+    settingsStore.changeSetting({ key: SettingTypeEnum.FIXED_HEADER, value: val })
   }
 })
 /**鏄惁闇�瑕佷晶杈规爮鐨刲ogo */
 const sidebarLogo = computed({
   get: () => storeSettings.value.sidebarLogo,
   set: (val) => {
-    settingsStore.changeSetting({ key: 'sidebarLogo', value: val })
+    settingsStore.changeSetting({ key: SettingTypeEnum.SIDEBAR_LOGO, value: val })
   }
 })
 /**鏄惁闇�瑕佷晶杈规爮鐨勫姩鎬佺綉椤电殑title */
 const dynamicTitle = computed({
   get: () => storeSettings.value.dynamicTitle,
   set: (val) => {
-    settingsStore.changeSetting({ key: 'dynamicTitle', value: val })
+    settingsStore.changeSetting({ key: SettingTypeEnum.DYNAMIC_TITLE, value: val })
     // 鍔ㄦ�佽缃綉椤垫爣棰�
     useDynamicTitle()
   }
 })
 
-function themeChange(val) {
-  settingsStore.changeSetting({ key: 'theme', value: val })
+const themeChange = (val: string | null) => {
+  settingsStore.changeSetting({ key: SettingTypeEnum.THEME, value: val })
   theme.value = val;
-  handleThemeStyle(val);
+  if (val) {
+    handleThemeStyle(val);
+  }
 }
-function handleTheme(val) {
-  settingsStore.changeSetting({ key: 'sideTheme', value: val })
+const handleTheme = (val: string) => {
+  settingsStore.changeSetting({ key: SettingTypeEnum.SIDE_THEME, value: val })
   sideTheme.value = val;
 }
-function saveSetting() {
-  proxy.$modal.loading("姝e湪淇濆瓨鍒版湰鍦帮紝璇风◢鍊�...");
+const saveSetting = () => {
+  proxy?.$modal.loading("姝e湪淇濆瓨鍒版湰鍦帮紝璇风◢鍊�...");
   let layoutSetting = {
     "topNav": storeSettings.value.topNav,
     "tagsView": storeSettings.value.tagsView,
@@ -162,14 +82,14 @@
     "theme": storeSettings.value.theme
   };
   localStorage.setItem("layout-setting", JSON.stringify(layoutSetting));
-  setTimeout(proxy.$modal.closeLoading(), 1000)
+  setTimeout(() => {proxy?.$modal.closeLoading()}, 1000)
 }
-function resetSetting() {
-  proxy.$modal.loading("姝e湪娓呴櫎璁剧疆缂撳瓨骞跺埛鏂帮紝璇风◢鍊�...");
+const resetSetting = () => {
+  proxy?.$modal.loading("姝e湪娓呴櫎璁剧疆缂撳瓨骞跺埛鏂帮紝璇风◢鍊�...");
   localStorage.removeItem("layout-setting")
   setTimeout("window.location.reload()", 1000)
 }
-function openSetting() {
+const openSetting = () => {
   showSettings.value = true;
 }
 
@@ -178,7 +98,90 @@
 })
 </script>
 
-<style lang='scss' scoped>
+<template>
+	<el-drawer v-model="showSettings" :withHeader="false" direction="rtl" size="300px">
+		<div class="setting-drawer-title">
+			<h3 class="drawer-title">涓婚椋庢牸璁剧疆</h3>
+		</div>
+		<div class="setting-drawer-block-checbox">
+			<div class="setting-drawer-block-checbox-item" @click="handleTheme('theme-dark')">
+				<img src="@/assets/images/dark.svg" alt="dark" />
+				<div v-if="sideTheme === 'theme-dark'" class="setting-drawer-block-checbox-selectIcon" style="display: block;">
+					<i aria-label="鍥炬爣: check" class="anticon anticon-check">
+						<svg viewBox="64 64 896 896" data-icon="check" width="1em" height="1em" :fill="theme" aria-hidden="true" focusable="false" class>
+							<path
+								d="M912 190h-69.9c-9.8 0-19.1 4.5-25.1 12.2L404.7 724.5 207 474a32 32 0 0 0-25.1-12.2H112c-6.7 0-10.4 7.7-6.3 12.9l273.9 347c12.8 16.2 37.4 16.2 50.3 0l488.4-618.9c4.1-5.1.4-12.8-6.3-12.8z"
+							/>
+						</svg>
+					</i>
+				</div>
+			</div>
+			<div class="setting-drawer-block-checbox-item" @click="handleTheme('theme-light')">
+				<img src="@/assets/images/light.svg" alt="light" />
+				<div v-if="sideTheme === 'theme-light'" class="setting-drawer-block-checbox-selectIcon" style="display: block;">
+					<i aria-label="鍥炬爣: check" class="anticon anticon-check">
+						<svg viewBox="64 64 896 896" data-icon="check" width="1em" height="1em" :fill="theme" aria-hidden="true" focusable="false" class>
+							<path
+								d="M912 190h-69.9c-9.8 0-19.1 4.5-25.1 12.2L404.7 724.5 207 474a32 32 0 0 0-25.1-12.2H112c-6.7 0-10.4 7.7-6.3 12.9l273.9 347c12.8 16.2 37.4 16.2 50.3 0l488.4-618.9c4.1-5.1.4-12.8-6.3-12.8z"
+							/>
+						</svg>
+					</i>
+				</div>
+			</div>
+		</div>
+		<div class="drawer-item">
+			<span>涓婚棰滆壊</span>
+			<span class="comp-style">
+				<el-color-picker v-model="theme" :predefine="predefineColors" @change="themeChange" />
+			</span>
+		</div>
+		<el-divider />
+
+		<h3 class="drawer-title">绯荤粺甯冨眬閰嶇疆</h3>
+
+		<div class="drawer-item">
+			<span>寮�鍚� TopNav</span>
+			<span class="comp-style">
+				<el-switch v-model="topNav" class="drawer-switch" />
+			</span>
+		</div>
+
+		<div class="drawer-item">
+			<span>寮�鍚� Tags-Views</span>
+			<span class="comp-style">
+				<el-switch v-model="tagsView" class="drawer-switch" />
+			</span>
+		</div>
+
+		<div class="drawer-item">
+			<span>鍥哄畾 Header</span>
+			<span class="comp-style">
+				<el-switch v-model="fixedHeader" class="drawer-switch" />
+			</span>
+		</div>
+
+		<div class="drawer-item">
+			<span>鏄剧ず Logo</span>
+			<span class="comp-style">
+				<el-switch v-model="sidebarLogo" class="drawer-switch" />
+			</span>
+		</div>
+
+		<div class="drawer-item">
+			<span>鍔ㄦ�佹爣棰�</span>
+			<span class="comp-style">
+				<el-switch v-model="dynamicTitle" class="drawer-switch" />
+			</span>
+		</div>
+
+		<el-divider />
+
+		<el-button type="primary" plain icon="DocumentAdd" @click="saveSetting">淇濆瓨閰嶇疆</el-button>
+		<el-button plain icon="Refresh" @click="resetSetting">閲嶇疆閰嶇疆</el-button>
+	</el-drawer>
+</template>
+
+<style lang="scss" scoped>
 .setting-drawer-title {
   margin-bottom: 12px;
   color: rgba(0, 0, 0, 0.85);
@@ -238,4 +241,4 @@
     margin: -3px 8px 0px 0px;
   }
 }
-</style>
\ No newline at end of file
+</style>
diff --git a/src/layout/components/Sidebar/Link.vue b/src/layout/components/Sidebar/Link.vue
index 8011431..02f0307 100644
--- a/src/layout/components/Sidebar/Link.vue
+++ b/src/layout/components/Sidebar/Link.vue
@@ -1,10 +1,4 @@
-<template>
-  <component :is="type" v-bind="linkProps()">
-    <slot />
-  </component>
-</template>
-
-<script setup>
+<script setup lang="ts">
 import { isExternal } from '@/utils/validate'
 
 const props = defineProps({
@@ -15,7 +9,7 @@
 })
 
 const isExt = computed(() => {
-  return isExternal(props.to)
+  return isExternal(props.to as string)
 })
 
 const type = computed(() => {
@@ -38,3 +32,9 @@
   }
 }
 </script>
+
+<template>
+	<component :is="type" v-bind="linkProps()">
+		<slot />
+	</component>
+</template>
diff --git a/src/layout/components/Sidebar/Logo.vue b/src/layout/components/Sidebar/Logo.vue
index 2cd9e0b..66df720 100644
--- a/src/layout/components/Sidebar/Logo.vue
+++ b/src/layout/components/Sidebar/Logo.vue
@@ -1,22 +1,9 @@
-<template>
-  <div class="sidebar-logo-container" :class="{ 'collapse': collapse }" :style="{ backgroundColor: sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground }">
-    <transition name="sidebarLogoFade">
-      <router-link v-if="collapse" key="collapse" class="sidebar-logo-link" to="/">
-        <img v-if="logo" :src="logo" class="sidebar-logo" />
-        <h1 v-else class="sidebar-title" :style="{ color: sideTheme === 'theme-dark' ? variables.logoTitleColor : variables.logoLightTitleColor }">{{ title }}</h1>
-      </router-link>
-      <router-link v-else key="expand" class="sidebar-logo-link" to="/">
-        <img v-if="logo" :src="logo" class="sidebar-logo" />
-        <h1 class="sidebar-title" :style="{ color: sideTheme === 'theme-dark' ? variables.logoTitleColor : variables.logoLightTitleColor }">{{ title }}</h1>
-      </router-link>
-    </transition>
-  </div>
-</template>
-
-<script setup>
+<script setup lang="ts">
 import variables from '@/assets/styles/variables.module.scss'
 import logo from '@/assets/logo/logo.png'
 import useSettingsStore from '@/store/modules/settings'
+import { ComponentInternalInstance } from "vue";
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 
 defineProps({
   collapse: {
@@ -29,6 +16,29 @@
 const settingsStore = useSettingsStore();
 const sideTheme = computed(() => settingsStore.sideTheme);
 </script>
+
+<template>
+	<div
+		class="sidebar-logo-container"
+		:class="{ 'collapse': collapse }"
+		:style="{ backgroundColor: sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground }"
+	>
+		<transition :enter-active-class="proxy?.animate.logoAnimate.enter" mode="out-in">
+			<router-link v-if="collapse" key="collapse" class="sidebar-logo-link" to="/">
+				<img v-if="logo" :src="logo" class="sidebar-logo" />
+				<h1 v-else class="sidebar-title" :style="{ color: sideTheme === 'theme-dark' ? variables.logoTitleColor : variables.logoLightTitleColor }">
+					{{ title }}
+				</h1>
+			</router-link>
+			<router-link v-else key="expand" class="sidebar-logo-link" to="/">
+				<img v-if="logo" :src="logo" class="sidebar-logo" />
+				<h1 class="sidebar-title" :style="{ color: sideTheme === 'theme-dark' ? variables.logoTitleColor : variables.logoLightTitleColor }">
+					{{ title }}
+				</h1>
+			</router-link>
+		</transition>
+	</div>
+</template>
 
 <style lang="scss" scoped>
 .sidebarLogoFade-enter-active {
@@ -78,4 +88,4 @@
     }
   }
 }
-</style>
\ No newline at end of file
+</style>
diff --git a/src/layout/components/Sidebar/SidebarItem.vue b/src/layout/components/Sidebar/SidebarItem.vue
index c423fb1..0c5831f 100644
--- a/src/layout/components/Sidebar/SidebarItem.vue
+++ b/src/layout/components/Sidebar/SidebarItem.vue
@@ -1,41 +1,14 @@
-<template>
-  <div v-if="!item.hidden">
-    <template v-if="hasOneShowingChild(item.children, item) && (!onlyOneChild.children || onlyOneChild.noShowingChildren) && !item.alwaysShow">
-      <app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path, onlyOneChild.query)">
-        <el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{ 'submenu-title-noDropdown': !isNest }">
-          <svg-icon :icon-class="onlyOneChild.meta.icon || (item.meta && item.meta.icon)"/>
-          <template #title><span class="menu-title" :title="hasTitle(onlyOneChild.meta.title)">{{ onlyOneChild.meta.title }}</span></template>
-        </el-menu-item>
-      </app-link>
-    </template>
-
-    <el-sub-menu v-else ref="subMenu" :index="resolvePath(item.path)" popper-append-to-body>
-      <template v-if="item.meta" #title>
-        <svg-icon :icon-class="item.meta && item.meta.icon" />
-        <span class="menu-title" :title="hasTitle(item.meta.title)">{{ item.meta.title }}</span>
-      </template>
-
-      <sidebar-item
-        v-for="child in item.children"
-        :key="child.path"
-        :is-nest="true"
-        :item="child"
-        :base-path="resolvePath(child.path)"
-        class="nest-menu"
-      />
-    </el-sub-menu>
-  </div>
-</template>
-
-<script setup>
+<script setup lang="ts">
 import { isExternal } from '@/utils/validate'
-import AppLink from './Link'
+import AppLink from './Link.vue'
 import { getNormalPath } from '@/utils/ruoyi'
+import { RouteOption } from "vue-router";
+import { PropType } from "vue";
 
 const props = defineProps({
   // route object
   item: {
-    type: Object,
+    type: Object as PropType<RouteOption>,
     required: true
   },
   isNest: {
@@ -48,9 +21,9 @@
   }
 })
 
-const onlyOneChild = ref({});
+const onlyOneChild = ref<any>({});
 
-function hasOneShowingChild(children = [], parent) {
+const hasOneShowingChild = (children:RouteOption[] = [], parent: RouteOption) => {
   if (!children) {
     children = [];
   }
@@ -78,7 +51,7 @@
   return false
 };
 
-function resolvePath(routePath, routeQuery) {
+const resolvePath = (routePath:string, routeQuery?:string): any => {
   if (isExternal(routePath)) {
     return routePath
   }
@@ -92,11 +65,41 @@
   return getNormalPath(props.basePath + '/' + routePath)
 }
 
-function hasTitle(title){
-  if (title.length > 5) {
-    return title;
-  } else {
+const hasTitle = (title: string | undefined): string => {
+  if(!title || title.length <= 5) {
     return "";
   }
+  return title;
 }
 </script>
+
+<template>
+	<div v-if="!item.hidden">
+		<template v-if="hasOneShowingChild(item.children, item) && (!onlyOneChild.children || onlyOneChild.noShowingChildren) && !item.alwaysShow">
+			<app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path, onlyOneChild.query)">
+				<el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{ 'submenu-title-noDropdown': !isNest }">
+					<svg-icon :icon-class="onlyOneChild.meta.icon || (item.meta && item.meta.icon)" />
+					<template #title>
+						<span class="menu-title" :title="hasTitle(onlyOneChild.meta.title)">{{ onlyOneChild.meta.title }}</span>
+					</template>
+				</el-menu-item>
+			</app-link>
+		</template>
+
+		<el-sub-menu v-else ref="subMenu" :index="resolvePath(item.path)" teleported>
+			<template v-if="item.meta" #title>
+				<svg-icon :icon-class="item.meta ? item.meta.icon : '' " />
+				<span class="menu-title" :title="hasTitle(item.meta?.title)">{{ item.meta?.title }}</span>
+			</template>
+
+			<sidebar-item
+				v-for="child in item.children"
+				:key="child.path"
+				:is-nest="true"
+				:item="child as RouteOption"
+				:base-path="resolvePath(child.path)"
+				class="nest-menu"
+			/>
+		</el-sub-menu>
+	</div>
+</template>
diff --git a/src/layout/components/Sidebar/index.vue b/src/layout/components/Sidebar/index.vue
index 9b14dfc..fad0d02 100644
--- a/src/layout/components/Sidebar/index.vue
+++ b/src/layout/components/Sidebar/index.vue
@@ -1,35 +1,12 @@
-<template>
-  <div :class="{ 'has-logo': showLogo }" :style="{ backgroundColor: sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground }">
-    <logo v-if="showLogo" :collapse="isCollapse" />
-    <el-scrollbar :class="sideTheme" wrap-class="scrollbar-wrapper">
-      <el-menu
-        :default-active="activeMenu"
-        :collapse="isCollapse"
-        :background-color="sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground"
-        :text-color="sideTheme === 'theme-dark' ? variables.menuColor : variables.menuLightColor"
-        :unique-opened="true"
-        :active-text-color="theme"
-        :collapse-transition="false"
-        mode="vertical"
-      >
-        <sidebar-item
-          v-for="(route, index) in sidebarRouters"
-          :key="route.path + index"
-          :item="route"
-          :base-path="route.path"
-        />
-      </el-menu>
-    </el-scrollbar>
-  </div>
-</template>
-
-<script setup>
-import Logo from './Logo'
-import SidebarItem from './SidebarItem'
+<script setup lang="ts">
+import Logo from './Logo.vue'
+import SidebarItem from './SidebarItem.vue'
 import variables from '@/assets/styles/variables.module.scss'
 import useAppStore from '@/store/modules/app'
 import useSettingsStore from '@/store/modules/settings'
 import usePermissionStore from '@/store/modules/permission'
+import { ComponentInternalInstance } from "vue";
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 
 const route = useRoute();
 const appStore = useAppStore()
@@ -47,8 +24,32 @@
   // if set path, the sidebar will highlight the path you set
   if (meta.activeMenu) {
     return meta.activeMenu;
-  }
+	}
   return path;
 })
 
+const bgColor = computed(() => sideTheme.value === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground);
+const textColor = computed(() => sideTheme.value === 'theme-dark' ? variables.menuColor : variables.menuLightColor);
 </script>
+
+<template>
+	<div :class="{ 'has-logo': showLogo }" :style="{ backgroundColor: bgColor }">
+		<logo v-if="showLogo" :collapse="isCollapse" />
+		<el-scrollbar :class="sideTheme" wrap-class="scrollbar-wrapper">
+			<transition :enter-active-class="proxy?.animate.menuSearchAnimate.enter" mode="out-in">
+				<el-menu
+					:default-active="activeMenu as string"
+					:collapse="isCollapse"
+					:background-color="bgColor"
+					:text-color="textColor"
+					:unique-opened="true"
+					:active-text-color="theme"
+					:collapse-transition="false"
+					mode="vertical"
+				>
+					<sidebar-item v-for="(route, index) in sidebarRouters" :key="route.path + index" :item="route" :base-path="route.path" />
+				</el-menu>
+			</transition>
+		</el-scrollbar>
+	</div>
+</template>
diff --git a/src/layout/components/TagsView/ScrollPane.vue b/src/layout/components/TagsView/ScrollPane.vue
index 5c2977a..b5b4b6d 100644
--- a/src/layout/components/TagsView/ScrollPane.vue
+++ b/src/layout/components/TagsView/ScrollPane.vue
@@ -1,21 +1,11 @@
-<template>
-  <el-scrollbar
-    ref="scrollContainer"
-    :vertical="false"
-    class="scroll-container"
-    @wheel.prevent="handleScroll"
-  >
-    <slot />
-  </el-scrollbar>
-</template>
-
-<script setup>
+<script setup lang="ts">
 import useTagsViewStore from '@/store/modules/tagsView'
-
+import { ElScrollbar } from 'element-plus';
+import { TagView } from 'vue-router'
 const tagAndTagSpacing = ref(4);
-const { proxy } = getCurrentInstance();
 
-const scrollWrapper = computed(() => proxy.$refs.scrollContainer.$refs.wrapRef);
+const scrollContainerRef = ref(ElScrollbar)
+const scrollWrapper = computed(() => scrollContainerRef.value.$refs.wrapRef);
 
 onMounted(() => {
   scrollWrapper.value.addEventListener('scroll', emitScroll, true)
@@ -24,12 +14,12 @@
   scrollWrapper.value.removeEventListener('scroll', emitScroll)
 })
 
-function handleScroll(e) {
-  const eventDelta = e.wheelDelta || -e.deltaY * 40
+const handleScroll = (e: WheelEvent) => {
+  const eventDelta = (e as any).wheelDelta || -e.deltaY * 40
   const $scrollWrapper = scrollWrapper.value;
   $scrollWrapper.scrollLeft = $scrollWrapper.scrollLeft + eventDelta / 4
 }
-const emits = defineEmits()
+const emits = defineEmits(['scroll'])
 const emitScroll = () => {
   emits('scroll')
 }
@@ -37,8 +27,8 @@
 const tagsViewStore = useTagsViewStore()
 const visitedViews = computed(() => tagsViewStore.visitedViews);
 
-function moveToTarget(currentTag) {
-  const $container = proxy.$refs.scrollContainer.$el
+const moveToTarget = (currentTag: TagView) => {
+  const $container = scrollContainerRef.value.$el
   const $containerWidth = $container.offsetWidth
   const $scrollWrapper = scrollWrapper.value;
 
@@ -56,10 +46,11 @@
   } else if (lastTag === currentTag) {
     $scrollWrapper.scrollLeft = $scrollWrapper.scrollWidth - $containerWidth
   } else {
-    const tagListDom = document.getElementsByClassName('tags-view-item');
+    const tagListDom: any = document.getElementsByClassName('tags-view-item');
     const currentIndex = visitedViews.value.findIndex(item => item === currentTag)
     let prevTag = null
     let nextTag = null
+
     for (const k in tagListDom) {
       if (k !== 'length' && Object.hasOwnProperty.call(tagListDom, k)) {
         if (tagListDom[k].dataset.path === visitedViews.value[currentIndex - 1].path) {
@@ -89,7 +80,13 @@
 })
 </script>
 
-<style lang='scss' scoped>
+<template>
+	<el-scrollbar ref="scrollContainerRef" :vertical="false" class="scroll-container" @wheel.prevent="handleScroll">
+		<slot />
+	</el-scrollbar>
+</template>
+
+<style lang="scss" scoped>
 .scroll-container {
   white-space: nowrap;
   position: relative;
@@ -102,4 +99,4 @@
     height: 49px;
   }
 }
-</style>
\ No newline at end of file
+</style>
diff --git a/src/layout/components/TagsView/index.vue b/src/layout/components/TagsView/index.vue
index 1f99623..9f4db62 100644
--- a/src/layout/components/TagsView/index.vue
+++ b/src/layout/components/TagsView/index.vue
@@ -1,61 +1,20 @@
-<template>
-  <div id="tags-view-container" class="tags-view-container">
-    <scroll-pane ref="scrollPaneRef" class="tags-view-wrapper" @scroll="handleScroll">
-      <router-link
-        v-for="tag in visitedViews"
-        :key="tag.path"
-        :data-path="tag.path"
-        :class="isActive(tag) ? 'active' : ''"
-        :to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }"
-        class="tags-view-item"
-        :style="activeStyle(tag)"
-        @click.middle="!isAffix(tag) ? closeSelectedTag(tag) : ''"
-        @contextmenu.prevent="openMenu(tag, $event)"
-      >
-        {{ tag.title }}
-        <span v-if="!isAffix(tag)" @click.prevent.stop="closeSelectedTag(tag)">
-          <close class="el-icon-close" style="width: 1em; height: 1em;vertical-align: middle;" />
-        </span>
-      </router-link>
-    </scroll-pane>
-    <ul v-show="visible" :style="{ left: left + 'px', top: top + 'px' }" class="contextmenu">
-      <li @click="refreshSelectedTag(selectedTag)">
-        <refresh-right style="width: 1em; height: 1em;" /> 鍒锋柊椤甸潰
-      </li>
-      <li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)">
-        <close style="width: 1em; height: 1em;" /> 鍏抽棴褰撳墠
-      </li>
-      <li @click="closeOthersTags">
-        <circle-close style="width: 1em; height: 1em;" /> 鍏抽棴鍏朵粬
-      </li>
-      <li v-if="!isFirstView()" @click="closeLeftTags">
-        <back style="width: 1em; height: 1em;" /> 鍏抽棴宸︿晶
-      </li>
-      <li v-if="!isLastView()" @click="closeRightTags">
-        <right style="width: 1em; height: 1em;" /> 鍏抽棴鍙充晶
-      </li>
-      <li @click="closeAllTags(selectedTag)">
-        <circle-close style="width: 1em; height: 1em;" /> 鍏ㄩ儴鍏抽棴
-      </li>
-    </ul>
-  </div>
-</template>
-
-<script setup>
-import ScrollPane from './ScrollPane'
+<script setup lang="ts">
+import ScrollPane from './ScrollPane.vue'
 import { getNormalPath } from '@/utils/ruoyi'
-import useTagsViewStore from '@/store/modules/tagsView'
+import useTagsViewStore from "@/store/modules/tagsView";
 import useSettingsStore from '@/store/modules/settings'
 import usePermissionStore from '@/store/modules/permission'
+import { ComponentInternalInstance } from "vue";
+import { RouteOption, TagView, RouteLocationRaw } from "vue-router";
 
 const visible = ref(false);
 const top = ref(0);
 const left = ref(0);
-const selectedTag = ref({});
-const affixTags = ref([]);
-const scrollPaneRef = ref(null);
+const selectedTag = ref<TagView>({});
+const affixTags = ref<TagView[]>([]);
+const scrollPaneRef = ref(ScrollPane);
 
-const { proxy } = getCurrentInstance();
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 const route = useRoute();
 const router = useRouter();
 
@@ -64,53 +23,49 @@
 const theme = computed(() => useSettingsStore().theme);
 
 watch(route, () => {
-  addTags()
-  moveToCurrentTag()
+  addTags();
+  moveToCurrentTag();
 })
 watch(visible, (value) => {
   if (value) {
-    document.body.addEventListener('click', closeMenu)
+    document.body.addEventListener('click', closeMenu);
   } else {
-    document.body.removeEventListener('click', closeMenu)
+    document.body.removeEventListener('click', closeMenu);
   }
 })
-onMounted(() => {
-  initTags()
-  addTags()
-})
 
-function isActive(r) {
-  return r.path === route.path
+const isActive = (r: TagView): boolean => {
+  return r.path === route.path;
 }
-function activeStyle(tag) {
+const activeStyle = (tag: TagView) => {
   if (!isActive(tag)) return {};
   return {
     "background-color": theme.value,
     "border-color": theme.value
   };
 }
-function isAffix(tag) {
-  return tag.meta && tag.meta.affix
+const isAffix = (tag: TagView) => {
+  return tag.meta && tag.meta.affix;
 }
-function isFirstView() {
+const isFirstView = () => {
   try {
-    return selectedTag.value.fullPath === '/index' || selectedTag.value.fullPath === visitedViews.value[1].fullPath
+    return selectedTag.value.fullPath === '/index' || selectedTag.value.fullPath === visitedViews.value[1].fullPath;
   } catch (err) {
-    return false
+    return false;
   }
 }
-function isLastView() {
+const isLastView = () => {
   try {
-    return selectedTag.value.fullPath === visitedViews.value[visitedViews.value.length - 1].fullPath
+    return selectedTag.value.fullPath === visitedViews.value[visitedViews.value.length - 1].fullPath;
   } catch (err) {
-    return false
+    return false;
   }
 }
-function filterAffixTags(routes, basePath = '') {
-  let tags = []
+const filterAffixTags = (routes:RouteOption [], basePath = '') => {
+  let tags:TagView[] = []
   routes.forEach(route => {
     if (route.meta && route.meta.affix) {
-      const tagPath = getNormalPath(basePath + '/' + route.path)
+      const tagPath = getNormalPath(basePath + '/' + route.path);
       tags.push({
         fullPath: tagPath,
         path: tagPath,
@@ -119,129 +74,166 @@
       })
     }
     if (route.children) {
-      const tempTags = filterAffixTags(route.children, route.path)
+      const tempTags = filterAffixTags(route.children, route.path);
       if (tempTags.length >= 1) {
-        tags = [...tags, ...tempTags]
+        tags = [...tags, ...tempTags];
       }
     }
   })
   return tags
 }
-function initTags() {
+const initTags = () => {
   const res = filterAffixTags(routes.value);
   affixTags.value = res;
   for (const tag of res) {
     // Must have tag name
     if (tag.name) {
-       useTagsViewStore().addVisitedView(tag)
+      useTagsViewStore().addVisitedView(tag);
     }
   }
 }
-function addTags() {
-  const { name } = route
+const addTags = () => {
+  const { name } = route;
   if (name) {
-    useTagsViewStore().addView(route)
+    useTagsViewStore().addView(route);
     if (route.meta.link) {
       useTagsViewStore().addIframeView(route);
     }
   }
   return false
 }
-function moveToCurrentTag() {
+const moveToCurrentTag = () => {
   nextTick(() => {
     for (const r of visitedViews.value) {
       if (r.path === route.path) {
         scrollPaneRef.value.moveToTarget(r);
         // when query is different then update
         if (r.fullPath !== route.fullPath) {
-          useTagsViewStore().updateVisitedView(route)
+          useTagsViewStore().updateVisitedView(route);
         }
       }
     }
   })
 }
-function refreshSelectedTag(view) {
-  proxy.$tab.refreshPage(view);
+const refreshSelectedTag = (view: TagView) => {
+  proxy?.$tab.refreshPage(view);
   if (route.meta.link) {
     useTagsViewStore().delIframeView(route);
   }
 }
-function closeSelectedTag(view) {
-  proxy.$tab.closePage(view).then(({ visitedViews }) => {
+const closeSelectedTag = (view: TagView) => {
+  proxy?.$tab.closePage(view).then(({ visitedViews }: any) => {
     if (isActive(view)) {
-      toLastView(visitedViews, view)
+      toLastView(visitedViews, view);
     }
   })
 }
-function closeRightTags() {
-  proxy.$tab.closeRightPage(selectedTag.value).then(visitedViews => {
+const closeRightTags = () => {
+  proxy?.$tab.closeRightPage(selectedTag.value).then(visitedViews => {
     if (!visitedViews.find(i => i.fullPath === route.fullPath)) {
-      toLastView(visitedViews)
+      toLastView(visitedViews);
     }
   })
 }
-function closeLeftTags() {
-  proxy.$tab.closeLeftPage(selectedTag.value).then(visitedViews => {
+const closeLeftTags = () => {
+  proxy?.$tab.closeLeftPage(selectedTag.value).then(visitedViews => {
     if (!visitedViews.find(i => i.fullPath === route.fullPath)) {
-      toLastView(visitedViews)
+      toLastView(visitedViews);
     }
   })
 }
-function closeOthersTags() {
-  router.push(selectedTag.value).catch(() => { });
-  proxy.$tab.closeOtherPage(selectedTag.value).then(() => {
-    moveToCurrentTag()
+const closeOthersTags = () => {
+  router.push(selectedTag.value as RouteLocationRaw).catch(() => { });
+  proxy?.$tab.closeOtherPage(selectedTag.value).then(() => {
+    moveToCurrentTag();
   })
 }
-function closeAllTags(view) {
-  proxy.$tab.closeAllPage().then(({ visitedViews }) => {
+const closeAllTags = (view: TagView) => {
+  proxy?.$tab.closeAllPage().then(({ visitedViews }) => {
     if (affixTags.value.some(tag => tag.path === route.path)) {
-      return
+      return;
     }
-    toLastView(visitedViews, view)
+    toLastView(visitedViews, view);
   })
 }
-function toLastView(visitedViews, view) {
-  const latestView = visitedViews.slice(-1)[0]
+const toLastView = (visitedViews:TagView[], view?: TagView) => {
+  const latestView = visitedViews.slice(-1)[0];
   if (latestView) {
-    router.push(latestView.fullPath)
+    router.push(latestView.fullPath as string);
   } else {
     // now the default is to redirect to the home page if there is no tags-view,
     // you can adjust it according to your needs.
-    if (view.name === 'Dashboard') {
+    if (view?.name === 'Dashboard') {
       // to reload home page
-      router.replace({ path: '/redirect' + view.fullPath })
+      router.replace({ path: '/redirect' + view?.fullPath });
     } else {
-      router.push('/')
+      router.push('/');
     }
   }
 }
-function openMenu(tag, e) {
-  const menuMinWidth = 105
-  const offsetLeft = proxy.$el.getBoundingClientRect().left // container margin left
-  const offsetWidth = proxy.$el.offsetWidth // container width
-  const maxLeft = offsetWidth - menuMinWidth // left boundary
-  const l = e.clientX - offsetLeft + 15 // 15: margin right
+const openMenu = (tag: TagView, e: MouseEvent) => {
+  const menuMinWidth = 105;
+  const offsetLeft = proxy?.$el.getBoundingClientRect().left; // container margin left
+  const offsetWidth = proxy?.$el.offsetWidth; // container width
+  const maxLeft = offsetWidth - menuMinWidth; // left boundary
+  const l = e.clientX - offsetLeft + 15; // 15: margin right
 
   if (l > maxLeft) {
-    left.value = maxLeft
+    left.value = maxLeft;
   } else {
-    left.value = l
+    left.value = l;
   }
 
   top.value = e.clientY
-  visible.value = true
-  selectedTag.value = tag
+  visible.value = true;
+  selectedTag.value = tag;
 }
-function closeMenu() {
-  visible.value = false
+const closeMenu = () => {
+  visible.value = false;
 }
-function handleScroll() {
-  closeMenu()
+const handleScroll = () => {
+  closeMenu();
 }
+
+
+onMounted(() => {
+  initTags();
+  addTags();
+})
 </script>
 
-<style lang='scss' scoped>
+<template>
+	<div id="tags-view-container" class="tags-view-container">
+		<scroll-pane ref="scrollPaneRef" class="tags-view-wrapper" @scroll="handleScroll">
+			<router-link
+				v-for="tag in visitedViews"
+				:key="tag.path"
+				:data-path="tag.path"
+				:class="isActive(tag) ? 'active' : ''"
+				:to="{ path: tag.path ? tag.path : '', query: tag.query, fullPath: tag.fullPath ? tag.fullPath : '' }"
+				class="tags-view-item"
+				:style="activeStyle(tag)"
+				@click.middle="!isAffix(tag) ? closeSelectedTag(tag) : ''"
+				@contextmenu.prevent="openMenu(tag, $event)"
+			>
+				{{ tag.title }}
+				<span v-if="!isAffix(tag)" @click.prevent.stop="closeSelectedTag(tag)">
+					<close class="el-icon-close" style="width: 1em; height: 1em;vertical-align: middle;" />
+				</span>
+			</router-link>
+		</scroll-pane>
+		<ul v-show="visible" :style="{ left: left + 'px', top: top + 'px' }" class="contextmenu">
+			<li @click="refreshSelectedTag(selectedTag)"><refresh-right style="width: 1em; height: 1em;" /> 鍒锋柊椤甸潰</li>
+			<li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)"><close style="width: 1em; height: 1em;" /> 鍏抽棴褰撳墠</li>
+			<li @click="closeOthersTags"><circle-close style="width: 1em; height: 1em;" /> 鍏抽棴鍏朵粬</li>
+			<li v-if="!isFirstView()" @click="closeLeftTags"><back style="width: 1em; height: 1em;" /> 鍏抽棴宸︿晶</li>
+			<li v-if="!isLastView()" @click="closeRightTags"><right style="width: 1em; height: 1em;" /> 鍏抽棴鍙充晶</li>
+			<li @click="closeAllTags(selectedTag)"><circle-close style="width: 1em; height: 1em;" /> 鍏ㄩ儴鍏抽棴</li>
+		</ul>
+	</div>
+</template>
+
+<style lang="scss" scoped>
 .tags-view-container {
   height: 34px;
   width: 100%;
@@ -335,4 +327,4 @@
     }
   }
 }
-</style>
\ No newline at end of file
+</style>
diff --git a/src/layout/components/index.js b/src/layout/components/index.js
deleted file mode 100644
index fd57731..0000000
--- a/src/layout/components/index.js
+++ /dev/null
@@ -1,4 +0,0 @@
-export { default as AppMain } from './AppMain'
-export { default as Navbar } from './Navbar'
-export { default as Settings } from './Settings'
-export { default as TagsView } from './TagsView/index.vue'
diff --git a/src/layout/components/index.ts b/src/layout/components/index.ts
new file mode 100644
index 0000000..47c83e1
--- /dev/null
+++ b/src/layout/components/index.ts
@@ -0,0 +1,4 @@
+export { default as AppMain } from './AppMain.vue';
+export { default as Navbar } from './Navbar.vue';
+export { default as Settings } from './Settings/index.vue';
+export { default as TagsView } from './TagsView/index.vue';
diff --git a/src/layout/index.vue b/src/layout/index.vue
index da55d54..673b0fd 100644
--- a/src/layout/index.vue
+++ b/src/layout/index.vue
@@ -1,30 +1,11 @@
-<template>
-  <div :class="classObj" class="app-wrapper" :style="{ '--current-color': theme }">
-    <div v-if="device === 'mobile' && sidebar.opened" class="drawer-bg" @click="handleClickOutside"/>
-    <sidebar v-if="!sidebar.hide" class="sidebar-container" />
-    <div :class="{ hasTagsView: needTagsView, sidebarHide: sidebar.hide }" class="main-container">
-      <div :class="{ 'fixed-header': fixedHeader }">
-        <navbar ref="navbarRef" @setLayout="setLayout" />
-        <tags-view v-if="needTagsView" />
-      </div>
-      <app-main />
-      <settings ref="settingRef" />
-    </div>
-  </div>
-</template>
-
-<script setup>
-import { useWindowSize } from '@vueuse/core'
-import Sidebar from './components/Sidebar/index.vue'
+<script setup lang="ts">
+import SideBar from './components/Sidebar/index.vue'
 import { AppMain, Navbar, Settings, TagsView } from './components'
-import defaultSettings from '@/settings'
-
 import useAppStore from '@/store/modules/app'
 import useSettingsStore from '@/store/modules/settings'
 
 const settingsStore = useSettingsStore()
 const theme = computed(() => settingsStore.theme);
-const sideTheme = computed(() => settingsStore.sideTheme);
 const sidebar = computed(() => useAppStore().sidebar);
 const device = computed(() => useAppStore().device);
 const needTagsView = computed(() => settingsStore.tagsView);
@@ -37,8 +18,7 @@
   mobile: device.value === 'mobile'
 }))
 
-const { proxy } = getCurrentInstance();
-const { width, height } = useWindowSize();
+const { width } = useWindowSize();
 const WIDTH = 992; // refer to Bootstrap's responsive design
 
 watchEffect(() => {
@@ -53,7 +33,8 @@
   }
 })
 
-const navbarRef = ref(null);
+const navbarRef = ref(Navbar);
+const settingRef = ref(Settings);
 
 onMounted(() => {
   nextTick(() => {
@@ -61,15 +42,29 @@
   })
 })
 
-function handleClickOutside() {
+const handleClickOutside = () => {
   useAppStore().closeSideBar({ withoutAnimation: false })
 }
 
-const settingRef = ref(null);
-function setLayout() {
+const setLayout = () => {
   settingRef.value.openSetting();
 }
 </script>
+
+<template>
+	<div :class="classObj" class="app-wrapper" :style="{ '--current-color': theme }">
+		<div v-if="device === 'mobile' && sidebar.opened" class="drawer-bg" @click="handleClickOutside" />
+		<side-bar v-if="!sidebar.hide" class="sidebar-container" />
+		<div :class="{ hasTagsView: needTagsView, sidebarHide: sidebar.hide }" class="main-container">
+			<div :class="{ 'fixed-header': fixedHeader }">
+				<navbar ref="navbarRef" @setLayout="setLayout" />
+				<tags-view v-if="needTagsView" />
+			</div>
+			<app-main />
+			<settings ref="settingRef" />
+		</div>
+	</div>
+</template>
 
 <style lang="scss" scoped>
   @import "@/assets/styles/mixin.scss";
@@ -117,4 +112,4 @@
 .mobile .fixed-header {
   width: 100%;
 }
-</style>
\ No newline at end of file
+</style>
diff --git a/src/main.js b/src/main.js
deleted file mode 100644
index fcdb585..0000000
--- a/src/main.js
+++ /dev/null
@@ -1,88 +0,0 @@
-import { createApp } from 'vue'
-
-import Cookies from 'js-cookie'
-
-import ElementPlus from 'element-plus'
-import locale from 'element-plus/lib/locale/lang/zh-cn' // 涓枃璇█
-
-import '@/assets/styles/index.scss' // global css
-import App from './App'
-import store from './store'
-import router from './router'
-import directive from './directive' // directive
-
-// 娉ㄥ唽鎸囦护
-import plugins from './plugins' // plugins
-import { download } from '@/utils/request'
-
-// svg鍥炬爣
-import 'virtual:svg-icons-register'
-import SvgIcon from '@/components/SvgIcon'
-import elementIcons from '@/components/SvgIcon/svgicon'
-
-import './permission' // permission control
-
-import { useDict } from '@/utils/dict'
-import { getConfigKey, updateConfigByKey } from "@/api/system/config";
-import { parseTime, resetForm, addDateRange, handleTree, selectDictLabel, selectDictLabels } from '@/utils/ruoyi'
-
-// 鍒嗛〉缁勪欢
-import Pagination from '@/components/Pagination'
-// 鑷畾涔夎〃鏍煎伐鍏风粍浠�
-import RightToolbar from '@/components/RightToolbar'
-// 瀵屾枃鏈粍浠�
-import Editor from "@/components/Editor"
-// 鏂囦欢涓婁紶缁勪欢
-import FileUpload from "@/components/FileUpload"
-// 鍥剧墖涓婁紶缁勪欢
-import ImageUpload from "@/components/ImageUpload"
-// 鍥剧墖棰勮缁勪欢
-import ImagePreview from "@/components/ImagePreview"
-// 鑷畾涔夋爲閫夋嫨缁勪欢
-import TreeSelect from '@/components/TreeSelect'
-// 瀛楀吀鏍囩缁勪欢
-import DictTag from '@/components/DictTag'
-
-const app = createApp(App)
-
-// 鍏ㄥ眬鏂规硶鎸傝浇
-app.config.globalProperties.useDict = useDict
-app.config.globalProperties.getConfigKey = getConfigKey
-app.config.globalProperties.updateConfigByKey = updateConfigByKey
-app.config.globalProperties.download = download
-app.config.globalProperties.parseTime = parseTime
-app.config.globalProperties.resetForm = resetForm
-app.config.globalProperties.handleTree = handleTree
-app.config.globalProperties.addDateRange = addDateRange
-app.config.globalProperties.selectDictLabel = selectDictLabel
-app.config.globalProperties.selectDictLabels = selectDictLabels
-
-// 鍏ㄥ眬缁勪欢鎸傝浇
-app.component('DictTag', DictTag)
-app.component('Pagination', Pagination)
-app.component('TreeSelect', TreeSelect)
-app.component('FileUpload', FileUpload)
-app.component('ImageUpload', ImageUpload)
-app.component('ImagePreview', ImagePreview)
-app.component('RightToolbar', RightToolbar)
-app.component('Editor', Editor)
-
-app.use(router)
-app.use(store)
-app.use(plugins)
-app.use(elementIcons)
-app.component('svg-icon', SvgIcon)
-
-directive(app)
-
-// 浣跨敤element-plus 骞朵笖璁剧疆鍏ㄥ眬鐨勫ぇ灏�
-app.use(ElementPlus, {
-  locale: locale,
-  // 鏀寔 large銆乨efault銆乻mall
-  size: Cookies.get('size') || 'default'
-})
-
-// 淇敼 el-dialog 榛樿鐐瑰嚮閬収涓轰笉鍏抽棴
-app._context.components.ElDialog.props.closeOnClickModal.default = false
-
-app.mount('#app')
diff --git a/src/main.ts b/src/main.ts
new file mode 100644
index 0000000..bef444b
--- /dev/null
+++ b/src/main.ts
@@ -0,0 +1,68 @@
+import { createApp } from 'vue';
+import Cookies from 'js-cookie';
+// element-plus
+import ElementPlus from 'element-plus';
+import locale from 'element-plus/lib/locale/lang/zh-cn';
+
+// global css
+import 'uno.css';
+import '@/assets/styles/index.scss';
+import 'element-plus/theme-chalk/dark/css-vars.css';
+
+// App銆乺outer銆乻tore
+import App from './App.vue';
+import store from './store';
+import router from './router';
+
+// 鑷畾涔夋寚浠�
+import directive from './directive';
+
+// 娉ㄥ唽鎻掍欢
+import plugins from './plugins/index'; // plugins
+import { download } from '@/utils/request';
+
+// 棰勮鍔ㄧ敾
+import animate from './animate';
+
+// svg鍥炬爣
+import 'virtual:svg-icons-register';
+import ElementIcons from '@/plugins/svgicon';
+
+// permission control
+import './permission';
+
+import { useDict } from '@/utils/dict';
+import { getConfigKey, updateConfigByKey } from '@/api/system/config';
+import { parseTime, addDateRange, handleTree, selectDictLabel, selectDictLabels } from '@/utils/ruoyi';
+
+const app = createApp(App);
+// 鍏ㄥ眬鏂规硶鎸傝浇
+app.config.globalProperties.useDict = useDict;
+app.config.globalProperties.getConfigKey = getConfigKey;
+app.config.globalProperties.updateConfigByKey = updateConfigByKey;
+app.config.globalProperties.download = download;
+app.config.globalProperties.parseTime = parseTime;
+app.config.globalProperties.handleTree = handleTree;
+app.config.globalProperties.addDateRange = addDateRange;
+app.config.globalProperties.selectDictLabel = selectDictLabel;
+app.config.globalProperties.selectDictLabels = selectDictLabels;
+app.config.globalProperties.animate = animate;
+
+app.use(ElementIcons);
+app.use(router);
+app.use(store);
+app.use(plugins);
+// 鑷畾涔夋寚浠�
+directive(app);
+
+// 浣跨敤element-plus 骞朵笖璁剧疆鍏ㄥ眬鐨勫ぇ灏�
+app.use(ElementPlus, {
+	locale: locale,
+	// 鏀寔 large銆乨efault銆乻mall
+	size: Cookies.get('size') || 'default'
+});
+
+// 淇敼 el-dialog 榛樿鐐瑰嚮閬収涓轰笉鍏抽棴
+(app._context.components.ElDialog as any).props.closeOnClickModal.default = false;
+
+app.mount('#app');
diff --git a/src/permission.js b/src/permission.js
deleted file mode 100644
index a474e0e..0000000
--- a/src/permission.js
+++ /dev/null
@@ -1,63 +0,0 @@
-import router from './router'
-import { ElMessage } from 'element-plus'
-import NProgress from 'nprogress'
-import 'nprogress/nprogress.css'
-import { getToken } from '@/utils/auth'
-import { isHttp } from '@/utils/validate'
-import { isRelogin } from '@/utils/request'
-import useUserStore from '@/store/modules/user'
-import useSettingsStore from '@/store/modules/settings'
-import usePermissionStore from '@/store/modules/permission'
-
-NProgress.configure({ showSpinner: false });
-
-const whiteList = ['/login', '/register'];
-
-router.beforeEach((to, from, next) => {
-  NProgress.start()
-  if (getToken()) {
-    to.meta.title && useSettingsStore().setTitle(to.meta.title)
-    /* has token*/
-    if (to.path === '/login') {
-      next({ path: '/' })
-      NProgress.done()
-    } else {
-      if (useUserStore().roles.length === 0) {
-        isRelogin.show = true
-        // 鍒ゆ柇褰撳墠鐢ㄦ埛鏄惁宸叉媺鍙栧畬user_info淇℃伅
-        useUserStore().getInfo().then(() => {
-          isRelogin.show = false
-          usePermissionStore().generateRoutes().then(accessRoutes => {
-            // 鏍规嵁roles鏉冮檺鐢熸垚鍙闂殑璺敱琛�
-            accessRoutes.forEach(route => {
-              if (!isHttp(route.path)) {
-                router.addRoute(route) // 鍔ㄦ�佹坊鍔犲彲璁块棶璺敱琛�
-              }
-            })
-            next({ ...to, replace: true }) // hack鏂规硶 纭繚addRoutes宸插畬鎴�
-          })
-        }).catch(err => {
-          useUserStore().logOut().then(() => {
-            ElMessage.error(err)
-            next({ path: '/' })
-          })
-        })
-      } else {
-        next()
-      }
-    }
-  } else {
-    // 娌℃湁token
-    if (whiteList.indexOf(to.path) !== -1) {
-      // 鍦ㄥ厤鐧诲綍鐧藉悕鍗曪紝鐩存帴杩涘叆
-      next()
-    } else {
-      next(`/login?redirect=${to.fullPath}`) // 鍚﹀垯鍏ㄩ儴閲嶅畾鍚戝埌鐧诲綍椤�
-      NProgress.done()
-    }
-  }
-})
-
-router.afterEach(() => {
-  NProgress.done()
-})
diff --git a/src/permission.ts b/src/permission.ts
new file mode 100644
index 0000000..3c76965
--- /dev/null
+++ b/src/permission.ts
@@ -0,0 +1,61 @@
+import { to as tos } from 'await-to-js';
+import router from './router';
+import NProgress from 'nprogress';
+import 'nprogress/nprogress.css';
+import { getToken } from '@/utils/auth';
+import { isHttp } from '@/utils/validate';
+import { isRelogin } from '@/utils/request';
+import useUserStore from '@/store/modules/user';
+import useSettingsStore from '@/store/modules/settings';
+import usePermissionStore from '@/store/modules/permission';
+
+NProgress.configure({ showSpinner: false });
+const whiteList = ['/login', '/register'];
+
+router.beforeEach(async (to, from, next) => {
+	NProgress.start();
+	if (getToken()) {
+		to.meta.title && useSettingsStore().setTitle(to.meta.title as string);
+		/* has token*/
+		if (to.path === '/login') {
+			next({ path: '/' });
+			NProgress.done();
+		} else {
+			if (useUserStore().roles.length === 0) {
+				isRelogin.show = true;
+				// 鍒ゆ柇褰撳墠鐢ㄦ埛鏄惁宸叉媺鍙栧畬user_info淇℃伅
+				const [err] = await tos(useUserStore().getInfo());
+				if (err) {
+					await useUserStore().logout();
+					ElMessage.error(err);
+					next({ path: '/' });
+				} else {
+					isRelogin.show = false;
+					const accessRoutes = await usePermissionStore().generateRoutes();
+					// 鏍规嵁roles鏉冮檺鐢熸垚鍙闂殑璺敱琛�
+					accessRoutes.forEach((route) => {
+						if (!isHttp(route.path)) {
+							router.addRoute(route); // 鍔ㄦ�佹坊鍔犲彲璁块棶璺敱琛�
+						}
+					});
+					next({ ...to, replace: true }); // hack鏂规硶 纭繚addRoutes宸插畬鎴�
+				}
+			} else {
+				next();
+			}
+		}
+	} else {
+		// 娌℃湁token
+		if (whiteList.indexOf(to.path) !== -1) {
+			// 鍦ㄥ厤鐧诲綍鐧藉悕鍗曪紝鐩存帴杩涘叆
+			next();
+		} else {
+			next(`/login?redirect=${to.fullPath}`); // 鍚﹀垯鍏ㄩ儴閲嶅畾鍚戝埌鐧诲綍椤�
+			NProgress.done();
+		}
+	}
+});
+
+router.afterEach(() => {
+	NProgress.done();
+});
diff --git a/src/plugins/auth.js b/src/plugins/auth.js
deleted file mode 100644
index 5e8c28d..0000000
--- a/src/plugins/auth.js
+++ /dev/null
@@ -1,60 +0,0 @@
-import useUserStore from '@/store/modules/user'
-
-function authPermission(permission) {
-  const all_permission = "*:*:*";
-  const permissions = useUserStore().permissions
-  if (permission && permission.length > 0) {
-    return permissions.some(v => {
-      return all_permission === v || v === permission
-    })
-  } else {
-    return false
-  }
-}
-
-function authRole(role) {
-  const super_admin = "admin";
-  const roles = useUserStore().roles
-  if (role && role.length > 0) {
-    return roles.some(v => {
-      return super_admin === v || v === role
-    })
-  } else {
-    return false
-  }
-}
-
-export default {
-  // 楠岃瘉鐢ㄦ埛鏄惁鍏峰鏌愭潈闄�
-  hasPermi(permission) {
-    return authPermission(permission);
-  },
-  // 楠岃瘉鐢ㄦ埛鏄惁鍚湁鎸囧畾鏉冮檺锛屽彧闇�鍖呭惈鍏朵腑涓�涓�
-  hasPermiOr(permissions) {
-    return permissions.some(item => {
-      return authPermission(item)
-    })
-  },
-  // 楠岃瘉鐢ㄦ埛鏄惁鍚湁鎸囧畾鏉冮檺锛屽繀椤诲叏閮ㄦ嫢鏈�
-  hasPermiAnd(permissions) {
-    return permissions.every(item => {
-      return authPermission(item)
-    })
-  },
-  // 楠岃瘉鐢ㄦ埛鏄惁鍏峰鏌愯鑹�
-  hasRole(role) {
-    return authRole(role);
-  },
-  // 楠岃瘉鐢ㄦ埛鏄惁鍚湁鎸囧畾瑙掕壊锛屽彧闇�鍖呭惈鍏朵腑涓�涓�
-  hasRoleOr(roles) {
-    return roles.some(item => {
-      return authRole(item)
-    })
-  },
-  // 楠岃瘉鐢ㄦ埛鏄惁鍚湁鎸囧畾瑙掕壊锛屽繀椤诲叏閮ㄦ嫢鏈�
-  hasRoleAnd(roles) {
-    return roles.every(item => {
-      return authRole(item)
-    })
-  }
-}
diff --git a/src/plugins/auth.ts b/src/plugins/auth.ts
new file mode 100644
index 0000000..f9e63b7
--- /dev/null
+++ b/src/plugins/auth.ts
@@ -0,0 +1,60 @@
+import useUserStore from '@/store/modules/user';
+
+const authPermission = (permission: string): boolean => {
+	const all_permission = '*:*:*';
+	const permissions: string[] = useUserStore().permissions;
+	if (permission && permission.length > 0) {
+		return permissions.some((v) => {
+			return all_permission === v || v === permission;
+		});
+	} else {
+		return false;
+	}
+};
+
+const authRole = (role: string): boolean => {
+	const super_admin = 'admin';
+	const roles = useUserStore().roles;
+	if (role && role.length > 0) {
+		return roles.some((v) => {
+			return super_admin === v || v === role;
+		});
+	} else {
+		return false;
+	}
+};
+
+export default {
+	// 楠岃瘉鐢ㄦ埛鏄惁鍏峰鏌愭潈闄�
+	hasPermi(permission: string): boolean {
+		return authPermission(permission);
+	},
+	// 楠岃瘉鐢ㄦ埛鏄惁鍚湁鎸囧畾鏉冮檺锛屽彧闇�鍖呭惈鍏朵腑涓�涓�
+	hasPermiOr(permissions: string[]): boolean {
+		return permissions.some((item) => {
+			return authPermission(item);
+		});
+	},
+	// 楠岃瘉鐢ㄦ埛鏄惁鍚湁鎸囧畾鏉冮檺锛屽繀椤诲叏閮ㄦ嫢鏈�
+	hasPermiAnd(permissions: string[]): boolean {
+		return permissions.every((item) => {
+			return authPermission(item);
+		});
+	},
+	// 楠岃瘉鐢ㄦ埛鏄惁鍏峰鏌愯鑹�
+	hasRole(role: string): boolean {
+		return authRole(role);
+	},
+	// 楠岃瘉鐢ㄦ埛鏄惁鍚湁鎸囧畾瑙掕壊锛屽彧闇�鍖呭惈鍏朵腑涓�涓�
+	hasRoleOr(roles: string[]): boolean {
+		return roles.some((item) => {
+			return authRole(item);
+		});
+	},
+	// 楠岃瘉鐢ㄦ埛鏄惁鍚湁鎸囧畾瑙掕壊锛屽繀椤诲叏閮ㄦ嫢鏈�
+	hasRoleAnd(roles: string[]): boolean {
+		return roles.every((item) => {
+			return authRole(item);
+		});
+	}
+};
diff --git a/src/plugins/cache.js b/src/plugins/cache.js
deleted file mode 100644
index 6b5c00b..0000000
--- a/src/plugins/cache.js
+++ /dev/null
@@ -1,77 +0,0 @@
-const sessionCache = {
-  set (key, value) {
-    if (!sessionStorage) {
-      return
-    }
-    if (key != null && value != null) {
-      sessionStorage.setItem(key, value)
-    }
-  },
-  get (key) {
-    if (!sessionStorage) {
-      return null
-    }
-    if (key == null) {
-      return null
-    }
-    return sessionStorage.getItem(key)
-  },
-  setJSON (key, jsonValue) {
-    if (jsonValue != null) {
-      this.set(key, JSON.stringify(jsonValue))
-    }
-  },
-  getJSON (key) {
-    const value = this.get(key)
-    if (value != null) {
-      return JSON.parse(value)
-    }
-  },
-  remove (key) {
-    sessionStorage.removeItem(key);
-  }
-}
-const localCache = {
-  set (key, value) {
-    if (!localStorage) {
-      return
-    }
-    if (key != null && value != null) {
-      localStorage.setItem(key, value)
-    }
-  },
-  get (key) {
-    if (!localStorage) {
-      return null
-    }
-    if (key == null) {
-      return null
-    }
-    return localStorage.getItem(key)
-  },
-  setJSON (key, jsonValue) {
-    if (jsonValue != null) {
-      this.set(key, JSON.stringify(jsonValue))
-    }
-  },
-  getJSON (key) {
-    const value = this.get(key)
-    if (value != null) {
-      return JSON.parse(value)
-    }
-  },
-  remove (key) {
-    localStorage.removeItem(key);
-  }
-}
-
-export default {
-  /**
-   * 浼氳瘽绾х紦瀛�
-   */
-  session: sessionCache,
-  /**
-   * 鏈湴缂撳瓨
-   */
-  local: localCache
-}
diff --git a/src/plugins/cache.ts b/src/plugins/cache.ts
new file mode 100644
index 0000000..c3cc7eb
--- /dev/null
+++ b/src/plugins/cache.ts
@@ -0,0 +1,77 @@
+const sessionCache = {
+	set(key: string, value: any) {
+		if (!sessionStorage) {
+			return;
+		}
+		if (key != null && value != null) {
+			sessionStorage.setItem(key, value);
+		}
+	},
+	get(key: string) {
+		if (!sessionStorage) {
+			return null;
+		}
+		if (key == null) {
+			return null;
+		}
+		return sessionStorage.getItem(key);
+	},
+	setJSON(key: string, jsonValue: any) {
+		if (jsonValue != null) {
+			this.set(key, JSON.stringify(jsonValue));
+		}
+	},
+	getJSON(key: string) {
+		const value = this.get(key);
+		if (value != null) {
+			return JSON.parse(value);
+		}
+	},
+	remove(key: string) {
+		sessionStorage.removeItem(key);
+	}
+};
+const localCache = {
+	set(key: string, value: any) {
+		if (!localStorage) {
+			return;
+		}
+		if (key != null && value != null) {
+			localStorage.setItem(key, value);
+		}
+	},
+	get(key: string) {
+		if (!localStorage) {
+			return null;
+		}
+		if (key == null) {
+			return null;
+		}
+		return localStorage.getItem(key);
+	},
+	setJSON(key: string, jsonValue: any) {
+		if (jsonValue != null) {
+			this.set(key, JSON.stringify(jsonValue));
+		}
+	},
+	getJSON(key: string) {
+		const value = this.get(key);
+		if (value != null) {
+			return JSON.parse(value);
+		}
+	},
+	remove(key: string) {
+		localStorage.removeItem(key);
+	}
+};
+
+export default {
+	/**
+	 * 浼氳瘽绾х紦瀛�
+	 */
+	session: sessionCache,
+	/**
+	 * 鏈湴缂撳瓨
+	 */
+	local: localCache
+};
diff --git a/src/plugins/download.js b/src/plugins/download.js
deleted file mode 100644
index 0613085..0000000
--- a/src/plugins/download.js
+++ /dev/null
@@ -1,65 +0,0 @@
-import axios from 'axios'
-import { ElLoading, ElMessage } from 'element-plus'
-import { saveAs } from 'file-saver'
-import { getToken } from '@/utils/auth'
-import errorCode from '@/utils/errorCode'
-import { blobValidate } from '@/utils/ruoyi'
-
-const baseURL = import.meta.env.VITE_APP_BASE_API
-let downloadLoadingInstance;
-
-export default {
-  oss(ossId) {
-    var url = baseURL + '/system/oss/download/' + ossId
-    downloadLoadingInstance = ElLoading.service({ text: "姝e湪涓嬭浇鏁版嵁锛岃绋嶅��", background: "rgba(0, 0, 0, 0.7)", })
-    axios({
-      method: 'get',
-      url: url,
-      responseType: 'blob',
-      headers: { 'Authorization': 'Bearer ' + getToken() }
-    }).then((res) => {
-      const isBlob = blobValidate(res.data);
-      if (isBlob) {
-        const blob = new Blob([res.data], { type: 'application/octet-stream' })
-        this.saveAs(blob, decodeURIComponent(res.headers['download-filename']))
-      } else {
-        this.printErrMsg(res.data);
-      }
-      downloadLoadingInstance.close();
-    }).catch((r) => {
-      console.error(r)
-      Message.error('涓嬭浇鏂囦欢鍑虹幇閿欒锛岃鑱旂郴绠$悊鍛橈紒')
-      downloadLoadingInstance.close();
-    })
-  },
-  zip(url, name) {
-    var url = baseURL + url
-    axios({
-      method: 'get',
-      url: url,
-      responseType: 'blob',
-      headers: {
-        'Authorization': 'Bearer ' + getToken(),
-        'datasource': localStorage.getItem("dataName")
-      }
-    }).then((res) => {
-      const isBlob = blobValidate(res.data);
-      if (isBlob) {
-        const blob = new Blob([res.data], { type: 'application/zip' })
-        this.saveAs(blob, name)
-      } else {
-        this.printErrMsg(res.data);
-      }
-    })
-  },
-  saveAs(text, name, opts) {
-    saveAs(text, name, opts);
-  },
-  async printErrMsg(data) {
-    const resText = await data.text();
-    const rspObj = JSON.parse(resText);
-    const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default']
-    ElMessage.error(errMsg);
-  }
-}
-
diff --git a/src/plugins/download.ts b/src/plugins/download.ts
new file mode 100644
index 0000000..3e963a7
--- /dev/null
+++ b/src/plugins/download.ts
@@ -0,0 +1,60 @@
+import axios from 'axios';
+import FileSaver from 'file-saver';
+import { getToken } from '@/utils/auth';
+import errorCode from '@/utils/errorCode';
+import { blobValidate } from '@/utils/ruoyi';
+import { LoadingInstance } from 'element-plus/es/components/loading/src/loading';
+
+const baseURL = import.meta.env.VITE_APP_BASE_API;
+let downloadLoadingInstance: LoadingInstance;
+export default {
+	async oss(ossId: string | number) {
+		const url = baseURL + '/system/oss/download/' + ossId;
+		downloadLoadingInstance = ElLoading.service({ text: '姝e湪涓嬭浇鏁版嵁锛岃绋嶅��', background: 'rgba(0, 0, 0, 0.7)' });
+		try {
+			const res = await axios({
+				method: 'get',
+				url: url,
+				responseType: 'blob',
+				headers: { Authorization: 'Bearer ' + getToken() }
+			});
+			const isBlob = blobValidate(res.data);
+			if (isBlob) {
+				const blob = new Blob([res.data], { type: 'application/octet-stream' });
+				FileSaver.saveAs(blob, decodeURIComponent(res.headers['download-filename'] as string));
+			} else {
+				this.printErrMsg(res.data);
+			}
+			downloadLoadingInstance.close();
+		} catch (r) {
+			console.error(r);
+			ElMessage.error('涓嬭浇鏂囦欢鍑虹幇閿欒锛岃鑱旂郴绠$悊鍛橈紒');
+			downloadLoadingInstance.close();
+		}
+	},
+	async zip(url: string, name: string) {
+		url = baseURL + url;
+		const res = await axios({
+			method: 'get',
+			url: url,
+			responseType: 'blob',
+			headers: {
+				Authorization: 'Bearer ' + getToken(),
+				datasource: localStorage.getItem('dataName')
+			}
+		});
+		const isBlob = blobValidate(res.data);
+		if (isBlob) {
+			const blob = new Blob([res.data], { type: 'application/zip' });
+			FileSaver.saveAs(blob, name);
+		} else {
+			this.printErrMsg(res.data);
+		}
+	},
+	async printErrMsg(data: any) {
+		const resText = await data.text();
+		const rspObj = JSON.parse(resText);
+		const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default'];
+		ElMessage.error(errMsg);
+	}
+};
diff --git a/src/plugins/index.js b/src/plugins/index.js
deleted file mode 100644
index 47d1b41..0000000
--- a/src/plugins/index.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import tab from './tab'
-import auth from './auth'
-import cache from './cache'
-import modal from './modal'
-import download from './download'
-
-export default function installPlugins(app){
-  // 椤电鎿嶄綔
-  app.config.globalProperties.$tab = tab
-  // 璁よ瘉瀵硅薄
-  app.config.globalProperties.$auth = auth
-  // 缂撳瓨瀵硅薄
-  app.config.globalProperties.$cache = cache
-  // 妯℃�佹瀵硅薄
-  app.config.globalProperties.$modal = modal
-  // 涓嬭浇鏂囦欢
-  app.config.globalProperties.$download = download
-}
diff --git a/src/plugins/index.ts b/src/plugins/index.ts
new file mode 100644
index 0000000..dc49340
--- /dev/null
+++ b/src/plugins/index.ts
@@ -0,0 +1,24 @@
+import modal from './modal';
+import tab from './tab';
+import download from './download';
+import cache from './cache';
+import auth from './auth';
+
+import { App } from 'vue';
+
+export default function installPlugin(app: App) {
+	// 椤电鎿嶄綔
+	app.config.globalProperties.$tab = tab;
+
+	// 妯℃�佹瀵硅薄
+	app.config.globalProperties.$modal = modal;
+
+	// 缂撳瓨瀵硅薄
+	app.config.globalProperties.$cache = cache;
+
+	// 涓嬭浇鏂囦欢
+	app.config.globalProperties.$download = download;
+
+	// 璁よ瘉瀵硅薄
+	app.config.globalProperties.$auth = auth;
+}
diff --git a/src/plugins/modal.js b/src/plugins/modal.js
deleted file mode 100644
index b59e14d..0000000
--- a/src/plugins/modal.js
+++ /dev/null
@@ -1,82 +0,0 @@
-import { ElMessage, ElMessageBox, ElNotification, ElLoading } from 'element-plus'
-
-let loadingInstance;
-
-export default {
-  // 娑堟伅鎻愮ず
-  msg(content) {
-    ElMessage.info(content)
-  },
-  // 閿欒娑堟伅
-  msgError(content) {
-    ElMessage.error(content)
-  },
-  // 鎴愬姛娑堟伅
-  msgSuccess(content) {
-    ElMessage.success(content)
-  },
-  // 璀﹀憡娑堟伅
-  msgWarning(content) {
-    ElMessage.warning(content)
-  },
-  // 寮瑰嚭鎻愮ず
-  alert(content) {
-    ElMessageBox.alert(content, "绯荤粺鎻愮ず")
-  },
-  // 閿欒鎻愮ず
-  alertError(content) {
-    ElMessageBox.alert(content, "绯荤粺鎻愮ず", { type: 'error' })
-  },
-  // 鎴愬姛鎻愮ず
-  alertSuccess(content) {
-    ElMessageBox.alert(content, "绯荤粺鎻愮ず", { type: 'success' })
-  },
-  // 璀﹀憡鎻愮ず
-  alertWarning(content) {
-    ElMessageBox.alert(content, "绯荤粺鎻愮ず", { type: 'warning' })
-  },
-  // 閫氱煡鎻愮ず
-  notify(content) {
-    ElNotification.info(content)
-  },
-  // 閿欒閫氱煡
-  notifyError(content) {
-    ElNotification.error(content);
-  },
-  // 鎴愬姛閫氱煡
-  notifySuccess(content) {
-    ElNotification.success(content)
-  },
-  // 璀﹀憡閫氱煡
-  notifyWarning(content) {
-    ElNotification.warning(content)
-  },
-  // 纭绐椾綋
-  confirm(content) {
-    return ElMessageBox.confirm(content, "绯荤粺鎻愮ず", {
-      confirmButtonText: '纭畾',
-      cancelButtonText: '鍙栨秷',
-      type: "warning",
-    })
-  },
-  // 鎻愪氦鍐呭
-  prompt(content) {
-    return ElMessageBox.prompt(content, "绯荤粺鎻愮ず", {
-      confirmButtonText: '纭畾',
-      cancelButtonText: '鍙栨秷',
-      type: "warning",
-    })
-  },
-  // 鎵撳紑閬僵灞�
-  loading(content) {
-    loadingInstance = ElLoading.service({
-      lock: true,
-      text: content,
-      background: "rgba(0, 0, 0, 0.7)",
-    })
-  },
-  // 鍏抽棴閬僵灞�
-  closeLoading() {
-    loadingInstance.close();
-  }
-}
diff --git a/src/plugins/modal.ts b/src/plugins/modal.ts
new file mode 100644
index 0000000..c18cb0b
--- /dev/null
+++ b/src/plugins/modal.ts
@@ -0,0 +1,81 @@
+import { ElMessage, ElMessageBox, ElNotification, ElLoading, MessageBoxData } from 'element-plus';
+import { LoadingInstance } from 'element-plus/es/components/loading/src/loading';
+let loadingInstance: LoadingInstance;
+export default {
+	// 娑堟伅鎻愮ず
+	msg(content: string) {
+		ElMessage.info(content);
+	},
+	// 閿欒娑堟伅
+	msgError(content: string) {
+		ElMessage.error(content);
+	},
+	// 鎴愬姛娑堟伅
+	msgSuccess(content: string) {
+		ElMessage.success(content);
+	},
+	// 璀﹀憡娑堟伅
+	msgWarning(content: string) {
+		ElMessage.warning(content);
+	},
+	// 寮瑰嚭鎻愮ず
+	alert(content: string) {
+		ElMessageBox.alert(content, '绯荤粺鎻愮ず');
+	},
+	// 閿欒鎻愮ず
+	alertError(content: string) {
+		ElMessageBox.alert(content, '绯荤粺鎻愮ず', { type: 'error' });
+	},
+	// 鎴愬姛鎻愮ず
+	alertSuccess(content: string, s: string, p: { dangerouslyUseHTMLString: boolean }) {
+		ElMessageBox.alert(content, '绯荤粺鎻愮ず', { type: 'success' });
+	},
+	// 璀﹀憡鎻愮ず
+	alertWarning(content: string) {
+		ElMessageBox.alert(content, '绯荤粺鎻愮ず', { type: 'warning' });
+	},
+	// 閫氱煡鎻愮ず
+	notify(content: string) {
+		ElNotification.info(content);
+	},
+	// 閿欒閫氱煡
+	notifyError(content: string) {
+		ElNotification.error(content);
+	},
+	// 鎴愬姛閫氱煡
+	notifySuccess(content: string) {
+		ElNotification.success(content);
+	},
+	// 璀﹀憡閫氱煡
+	notifyWarning(content: string) {
+		ElNotification.warning(content);
+	},
+	// 纭绐椾綋
+	confirm(content: string): Promise<MessageBoxData> {
+		return ElMessageBox.confirm(content, '绯荤粺鎻愮ず', {
+			confirmButtonText: '纭畾',
+			cancelButtonText: '鍙栨秷',
+			type: 'warning'
+		});
+	},
+	// 鎻愪氦鍐呭
+	prompt(content: string) {
+		return ElMessageBox.prompt(content, '绯荤粺鎻愮ず', {
+			confirmButtonText: '纭畾',
+			cancelButtonText: '鍙栨秷',
+			type: 'warning'
+		});
+	},
+	// 鎵撳紑閬僵灞�
+	loading(content: string) {
+		loadingInstance = ElLoading.service({
+			lock: true,
+			text: content,
+			background: 'rgba(0, 0, 0, 0.7)'
+		});
+	},
+	// 鍏抽棴閬僵灞�
+	closeLoading() {
+		loadingInstance.close();
+	}
+};
diff --git a/src/plugins/svgicon.ts b/src/plugins/svgicon.ts
new file mode 100644
index 0000000..2975b0e
--- /dev/null
+++ b/src/plugins/svgicon.ts
@@ -0,0 +1,10 @@
+import * as ElementPlusIconsVue from '@element-plus/icons-vue';
+import { App } from 'vue';
+
+export default {
+	install: (app: App) => {
+		for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
+			app.component(key, component);
+		}
+	}
+};
diff --git a/src/plugins/tab.js b/src/plugins/tab.js
deleted file mode 100644
index 59e7006..0000000
--- a/src/plugins/tab.js
+++ /dev/null
@@ -1,65 +0,0 @@
-import useTagsViewStore from '@/store/modules/tagsView'
-import router from '@/router'
-
-export default {
-  // 鍒锋柊褰撳墠tab椤电
-  refreshPage(obj) {
-    const { path, query, matched } = router.currentRoute.value;
-    if (obj === undefined) {
-      matched.forEach((m) => {
-        if (m.components && m.components.default && m.components.default.name) {
-          if (!['Layout', 'ParentView'].includes(m.components.default.name)) {
-            obj = { name: m.components.default.name, path: path, query: query };
-          }
-        }
-      });
-    }
-    return useTagsViewStore().delCachedView(obj).then(() => {
-      const { path, query } = obj
-      router.replace({
-        path: '/redirect' + path,
-        query: query
-      })
-    })
-  },
-  // 鍏抽棴褰撳墠tab椤电锛屾墦寮�鏂伴〉绛�
-  closeOpenPage(obj) {
-    useTagsViewStore().delView(router.currentRoute.value);
-    if (obj !== undefined) {
-      return router.push(obj);
-    }
-  },
-  // 鍏抽棴鎸囧畾tab椤电
-  closePage(obj) {
-    if (obj === undefined) {
-      return useTagsViewStore().delView(router.currentRoute.value).then(({ lastPath }) => {
-        return router.push(lastPath || '/index');
-      });
-    }
-    return useTagsViewStore().delView(obj);
-  },
-  // 鍏抽棴鎵�鏈塼ab椤电
-  closeAllPage() {
-    return useTagsViewStore().delAllViews();
-  },
-  // 鍏抽棴宸︿晶tab椤电
-  closeLeftPage(obj) {
-    return useTagsViewStore().delLeftTags(obj || router.currentRoute.value);
-  },
-  // 鍏抽棴鍙充晶tab椤电
-  closeRightPage(obj) {
-    return useTagsViewStore().delRightTags(obj || router.currentRoute.value);
-  },
-  // 鍏抽棴鍏朵粬tab椤电
-  closeOtherPage(obj) {
-    return useTagsViewStore().delOthersViews(obj || router.currentRoute.value);
-  },
-  // 鎵撳紑tab椤电
-  openPage(url) {
-    return router.push(url);
-  },
-  // 淇敼tab椤电
-  updatePage(obj) {
-    return useTagsViewStore().updateVisitedView(obj);
-  }
-}
diff --git a/src/plugins/tab.ts b/src/plugins/tab.ts
new file mode 100644
index 0000000..ad6f1c3
--- /dev/null
+++ b/src/plugins/tab.ts
@@ -0,0 +1,65 @@
+import { useTagsViewStore } from '@/store/modules/tagsView';
+import router from '@/router';
+import { TagView, RouteLocationRaw } from 'vue-router';
+
+export default {
+	// 鍒锋柊褰撳墠tab椤电
+	async refreshPage(obj: TagView): Promise<void> {
+		const { path, query, matched } = router.currentRoute.value;
+		if (obj === undefined) {
+			matched.forEach((m) => {
+				if (m.components && m.components.default && m.components.default.name) {
+					if (!['Layout', 'ParentView'].includes(m.components.default.name)) {
+						obj = { name: m.components.default.name, path: path, query: query };
+					}
+				}
+			});
+		}
+		// prettier-ignore
+		await useTagsViewStore().delCachedView(obj)
+		router.replace({
+			path: '/redirect' + obj.path,
+			query: obj.query
+		});
+	},
+	// 鍏抽棴褰撳墠tab椤电锛屾墦寮�鏂伴〉绛�
+	closeOpenPage(obj: RouteLocationRaw): void {
+		useTagsViewStore().delView(router.currentRoute.value);
+		if (obj !== undefined) {
+			router.push(obj);
+		}
+	},
+	// 鍏抽棴鎸囧畾tab椤电
+	async closePage(obj?: TagView): Promise<{ visitedViews: TagView[]; cachedViews: string[] } | any> {
+		if (obj === undefined) {
+			// prettier-ignore
+			const { lastPath } = await useTagsViewStore().delView(router.currentRoute.value) as any
+			return router.push(lastPath || '/index');
+		}
+		return useTagsViewStore().delView(obj);
+	},
+	// 鍏抽棴鎵�鏈塼ab椤电
+	closeAllPage() {
+		return useTagsViewStore().delAllViews();
+	},
+	// 鍏抽棴宸︿晶tab椤电
+	closeLeftPage(obj: TagView) {
+		return useTagsViewStore().delLeftTags(obj || router.currentRoute.value);
+	},
+	// 鍏抽棴鍙充晶tab椤电
+	closeRightPage(obj: TagView) {
+		return useTagsViewStore().delRightTags(obj || router.currentRoute.value);
+	},
+	// 鍏抽棴鍏朵粬tab椤电
+	closeOtherPage(obj: TagView) {
+		return useTagsViewStore().delOthersViews(obj || router.currentRoute.value);
+	},
+	// 鎵撳紑tab椤电
+	openPage(url: RouteLocationRaw) {
+		return router.push(url);
+	},
+	// 淇敼tab椤电
+	updatePage(obj: TagView) {
+		return useTagsViewStore().updateVisitedView(obj);
+	}
+};
diff --git a/src/router/index.js b/src/router/index.js
deleted file mode 100644
index b2bebbf..0000000
--- a/src/router/index.js
+++ /dev/null
@@ -1,175 +0,0 @@
-import { createWebHistory, createRouter } from 'vue-router'
-/* Layout */
-import Layout from '@/layout'
-
-/**
- * Note: 璺敱閰嶇疆椤�
- *
- * hidden: true                     // 褰撹缃� true 鐨勬椂鍊欒璺敱涓嶄細鍐嶄晶杈规爮鍑虹幇 濡�401锛宭ogin绛夐〉闈紝鎴栬�呭涓�浜涚紪杈戦〉闈�/edit/1
- * alwaysShow: true                 // 褰撲綘涓�涓矾鐢变笅闈㈢殑 children 澹版槑鐨勮矾鐢卞ぇ浜�1涓椂锛岃嚜鍔ㄤ細鍙樻垚宓屽鐨勬ā寮�--濡傜粍浠堕〉闈�
- *                                  // 鍙湁涓�涓椂锛屼細灏嗛偅涓瓙璺敱褰撳仛鏍硅矾鐢辨樉绀哄湪渚ц竟鏍�--濡傚紩瀵奸〉闈�
- *                                  // 鑻ヤ綘鎯充笉绠¤矾鐢变笅闈㈢殑 children 澹版槑鐨勪釜鏁伴兘鏄剧ず浣犵殑鏍硅矾鐢�
- *                                  // 浣犲彲浠ヨ缃� alwaysShow: true锛岃繖鏍峰畠灏变細蹇界暐涔嬪墠瀹氫箟鐨勮鍒欙紝涓�鐩存樉绀烘牴璺敱
- * redirect: noRedirect             // 褰撹缃� noRedirect 鐨勬椂鍊欒璺敱鍦ㄩ潰鍖呭睉瀵艰埅涓笉鍙鐐瑰嚮
- * name:'router-name'               // 璁惧畾璺敱鐨勫悕瀛楋紝涓�瀹氳濉啓涓嶇劧浣跨敤<keep-alive>鏃朵細鍑虹幇鍚勭闂
- * query: '{"id": 1, "name": "ry"}' // 璁块棶璺敱鐨勯粯璁や紶閫掑弬鏁�
- * roles: ['admin', 'common']       // 璁块棶璺敱鐨勮鑹叉潈闄�
- * permissions: ['a:a:a', 'b:b:b']  // 璁块棶璺敱鐨勮彍鍗曟潈闄�
- * meta : {
-    noCache: true                   // 濡傛灉璁剧疆涓簍rue锛屽垯涓嶄細琚� <keep-alive> 缂撳瓨(榛樿 false)
-    title: 'title'                  // 璁剧疆璇ヨ矾鐢卞湪渚ц竟鏍忓拰闈㈠寘灞戜腑灞曠ず鐨勫悕瀛�
-    icon: 'svg-name'                // 璁剧疆璇ヨ矾鐢辩殑鍥炬爣锛屽搴旇矾寰剆rc/assets/icons/svg
-    breadcrumb: false               // 濡傛灉璁剧疆涓篺alse锛屽垯涓嶄細鍦╞readcrumb闈㈠寘灞戜腑鏄剧ず
-    activeMenu: '/system/user'      // 褰撹矾鐢辫缃簡璇ュ睘鎬э紝鍒欎細楂樹寒鐩稿搴旂殑渚ц竟鏍忋��
-  }
- */
-
-// 鍏叡璺敱
-export const constantRoutes = [
-  {
-    path: '/redirect',
-    component: Layout,
-    hidden: true,
-    children: [
-      {
-        path: '/redirect/:path(.*)',
-        component: () => import('@/views/redirect/index.vue')
-      }
-    ]
-  },
-  {
-    path: '/login',
-    component: () => import('@/views/login'),
-    hidden: true
-  },
-  {
-    path: '/register',
-    component: () => import('@/views/register'),
-    hidden: true
-  },
-  {
-    path: "/:pathMatch(.*)*",
-    component: () => import('@/views/error/404'),
-    hidden: true
-  },
-  {
-    path: '/401',
-    component: () => import('@/views/error/401'),
-    hidden: true
-  },
-  {
-    path: '',
-    component: Layout,
-    redirect: '/index',
-    children: [
-      {
-        path: '/index',
-        component: () => import('@/views/index'),
-        name: 'Index',
-        meta: { title: '棣栭〉', icon: 'dashboard', affix: true }
-      }
-    ]
-  },
-  {
-    path: '/user',
-    component: Layout,
-    hidden: true,
-    redirect: 'noredirect',
-    children: [
-      {
-        path: 'profile',
-        component: () => import('@/views/system/user/profile/index'),
-        name: 'Profile',
-        meta: { title: '涓汉涓績', icon: 'user' }
-      }
-    ]
-  }
-]
-
-// 鍔ㄦ�佽矾鐢憋紝鍩轰簬鐢ㄦ埛鏉冮檺鍔ㄦ�佸幓鍔犺浇
-export const dynamicRoutes = [
-  {
-    path: '/system/user-auth',
-    component: Layout,
-    hidden: true,
-    permissions: ['system:user:edit'],
-    children: [
-      {
-        path: 'role/:userId(\\d+)',
-        component: () => import('@/views/system/user/authRole'),
-        name: 'AuthRole',
-        meta: { title: '鍒嗛厤瑙掕壊', activeMenu: '/system/user' }
-      }
-    ]
-  },
-  {
-    path: '/system/role-auth',
-    component: Layout,
-    hidden: true,
-    permissions: ['system:role:edit'],
-    children: [
-      {
-        path: 'user/:roleId(\\d+)',
-        component: () => import('@/views/system/role/authUser'),
-        name: 'AuthUser',
-        meta: { title: '鍒嗛厤鐢ㄦ埛', activeMenu: '/system/role' }
-      }
-    ]
-  },
-  {
-    path: '/system/dict-data',
-    component: Layout,
-    hidden: true,
-    permissions: ['system:dict:list'],
-    children: [
-      {
-        path: 'index/:dictId(\\d+)',
-        component: () => import('@/views/system/dict/data'),
-        name: 'Data',
-        meta: { title: '瀛楀吀鏁版嵁', activeMenu: '/system/dict' }
-      }
-    ]
-  },
-  {
-    path: '/system/oss-config',
-    component: Layout,
-    hidden: true,
-    permissions: ['monitor:job:list'],
-    children: [
-      {
-        path: 'index',
-        component: () => import('@/views/system/oss/config'),
-        name: 'OssConfig',
-        meta: { title: '閰嶇疆绠$悊', activeMenu: '/system/oss'}
-      }
-    ]
-  },
-  {
-    path: '/tool/gen-edit',
-    component: Layout,
-    hidden: true,
-    permissions: ['tool:gen:edit'],
-    children: [
-      {
-        path: 'index/:tableId(\\d+)',
-        component: () => import('@/views/tool/gen/editTable'),
-        name: 'GenEdit',
-        meta: { title: '淇敼鐢熸垚閰嶇疆', activeMenu: '/tool/gen' }
-      }
-    ]
-  }
-]
-
-const router = createRouter({
-  history: createWebHistory(import.meta.env.VITE_APP_CONTEXT_PATH),
-  routes: constantRoutes,
-  scrollBehavior(to, from, savedPosition) {
-    if (savedPosition) {
-      return savedPosition
-    } else {
-      return { top: 0 }
-    }
-  },
-});
-
-export default router;
diff --git a/src/router/index.ts b/src/router/index.ts
new file mode 100644
index 0000000..5c5388c
--- /dev/null
+++ b/src/router/index.ts
@@ -0,0 +1,179 @@
+import { createWebHistory, createRouter, RouteOption } from 'vue-router';
+/* Layout */
+import Layout from '@/layout/index.vue';
+
+/**
+ * Note: 璺敱閰嶇疆椤�
+ *
+ * hidden: true                     // 褰撹缃� true 鐨勬椂鍊欒璺敱涓嶄細鍐嶄晶杈规爮鍑虹幇 濡�401锛宭ogin绛夐〉闈紝鎴栬�呭涓�浜涚紪杈戦〉闈�/edit/1
+ * alwaysShow: true                 // 褰撲綘涓�涓矾鐢变笅闈㈢殑 children 澹版槑鐨勮矾鐢卞ぇ浜�1涓椂锛岃嚜鍔ㄤ細鍙樻垚宓屽鐨勬ā寮�--濡傜粍浠堕〉闈�
+ *                                  // 鍙湁涓�涓椂锛屼細灏嗛偅涓瓙璺敱褰撳仛鏍硅矾鐢辨樉绀哄湪渚ц竟鏍�--濡傚紩瀵奸〉闈�
+ *                                  // 鑻ヤ綘鎯充笉绠¤矾鐢变笅闈㈢殑 children 澹版槑鐨勪釜鏁伴兘鏄剧ず浣犵殑鏍硅矾鐢�
+ *                                  // 浣犲彲浠ヨ缃� alwaysShow: true锛岃繖鏍峰畠灏变細蹇界暐涔嬪墠瀹氫箟鐨勮鍒欙紝涓�鐩存樉绀烘牴璺敱
+ * redirect: noRedirect             // 褰撹缃� noRedirect 鐨勬椂鍊欒璺敱鍦ㄩ潰鍖呭睉瀵艰埅涓笉鍙鐐瑰嚮
+ * name:'router-name'               // 璁惧畾璺敱鐨勫悕瀛楋紝涓�瀹氳濉啓涓嶇劧浣跨敤<keep-alive>鏃朵細鍑虹幇鍚勭闂
+ * query: '{"id": 1, "name": "ry"}' // 璁块棶璺敱鐨勯粯璁や紶閫掑弬鏁�
+ * roles: ['admin', 'common']       // 璁块棶璺敱鐨勮鑹叉潈闄�
+ * permissions: ['a:a:a', 'b:b:b']  // 璁块棶璺敱鐨勮彍鍗曟潈闄�
+ * meta : {
+    noCache: true                   // 濡傛灉璁剧疆涓簍rue锛屽垯涓嶄細琚� <keep-alive> 缂撳瓨(榛樿 false)
+    title: 'title'                  // 璁剧疆璇ヨ矾鐢卞湪渚ц竟鏍忓拰闈㈠寘灞戜腑灞曠ず鐨勫悕瀛�
+    icon: 'svg-name'                // 璁剧疆璇ヨ矾鐢辩殑鍥炬爣锛屽搴旇矾寰剆rc/assets/icons/svg
+    breadcrumb: false               // 濡傛灉璁剧疆涓篺alse锛屽垯涓嶄細鍦╞readcrumb闈㈠寘灞戜腑鏄剧ず
+    activeMenu: '/system/user'      // 褰撹矾鐢辫缃簡璇ュ睘鎬э紝鍒欎細楂樹寒鐩稿搴旂殑渚ц竟鏍忋��
+  }
+ */
+
+// 鍏叡璺敱
+export const constantRoutes: RouteOption[] = [
+	{
+		path: '/redirect',
+		component: Layout,
+		hidden: true,
+		children: [
+			{
+				path: '/redirect/:path(.*)',
+				component: () => import('@/views/redirect/index.vue')
+			}
+		]
+	},
+	{
+		path: '/login',
+		component: () => import('@/views/login.vue'),
+		hidden: true
+	},
+	{
+		path: '/register',
+		component: () => import('@/views/register.vue'),
+		hidden: true
+	},
+	{
+		path: '/:pathMatch(.*)*',
+		component: () => import('@/views/error/404.vue'),
+		hidden: true
+	},
+	{
+		path: '/401',
+		component: () => import('@/views/error/401.vue'),
+		hidden: true
+	},
+	{
+		path: '',
+		component: Layout,
+		redirect: '/index',
+		children: [
+			{
+				path: '/index',
+				component: () => import('@/views/index.vue'),
+				name: 'Index',
+				meta: { title: '棣栭〉', icon: 'dashboard', affix: true }
+			}
+		]
+	},
+	{
+		path: '/user',
+		component: Layout,
+		hidden: true,
+		redirect: 'noredirect',
+		children: [
+			{
+				path: 'profile',
+				component: () => import('@/views/system/user/profile/index.vue'),
+				name: 'Profile',
+				meta: { title: '涓汉涓績', icon: 'user' }
+			}
+		]
+	}
+];
+
+// 鍔ㄦ�佽矾鐢憋紝鍩轰簬鐢ㄦ埛鏉冮檺鍔ㄦ�佸幓鍔犺浇
+export const dynamicRoutes: RouteOption[] = [
+	{
+		path: '/system/user-auth',
+		component: Layout,
+		hidden: true,
+		permissions: ['system:user:edit'],
+		children: [
+			{
+				path: 'role/:userId(\\d+)',
+				component: () => import('@/views/system/user/authRole.vue'),
+				name: 'AuthRole',
+				meta: { title: '鍒嗛厤瑙掕壊', activeMenu: '/system/user', icon: '' }
+			}
+		]
+	},
+	{
+		path: '/system/role-auth',
+		component: Layout,
+		hidden: true,
+		permissions: ['system:role:edit'],
+		children: [
+			{
+				path: 'user/:roleId(\\d+)',
+				component: () => import('@/views/system/role/authUser.vue'),
+				name: 'AuthUser',
+				meta: { title: '鍒嗛厤鐢ㄦ埛', activeMenu: '/system/role', icon: '' }
+			}
+		]
+	},
+	{
+		path: '/system/dict-data',
+		component: Layout,
+		hidden: true,
+		permissions: ['system:dict:list'],
+		children: [
+			{
+				path: 'index/:dictId(\\d+)',
+				component: () => import('@/views/system/dict/data.vue'),
+				name: 'Data',
+				meta: { title: '瀛楀吀鏁版嵁', activeMenu: '/system/dict', icon: '' }
+			}
+		]
+	},
+	{
+		path: '/system/oss-config',
+		component: Layout,
+		hidden: true,
+		permissions: ['monitor:job:list'],
+		children: [
+			{
+				path: 'index',
+				component: () => import('@/views/system/oss/config.vue'),
+				name: 'OssConfig',
+				meta: { title: '閰嶇疆绠$悊', activeMenu: '/system/oss', icon: '' }
+			}
+		]
+	},
+	{
+		path: '/tool/gen-edit',
+		component: Layout,
+		hidden: true,
+		permissions: ['tool:gen:edit'],
+		children: [
+			{
+				path: 'index/:tableId(\\d+)',
+				component: () => import('@/views/tool/gen/editTable.vue'),
+				name: 'GenEdit',
+				meta: { title: '淇敼鐢熸垚閰嶇疆', activeMenu: '/tool/gen', icon: '' }
+			}
+		]
+	}
+];
+
+/**
+ * 鍒涘缓璺敱
+ */
+const router = createRouter({
+	history: createWebHistory(import.meta.env.VITE_APP_CONTEXT_PATH),
+	routes: constantRoutes,
+	// 鍒锋柊鏃讹紝婊氬姩鏉′綅缃繕鍘�
+	scrollBehavior(to, from, savedPosition) {
+		if (savedPosition) {
+			return savedPosition;
+		} else {
+			return { top: 0 };
+		}
+	}
+});
+
+export default router;
diff --git a/src/settings.js b/src/settings.js
deleted file mode 100644
index 10e1db4..0000000
--- a/src/settings.js
+++ /dev/null
@@ -1,47 +0,0 @@
-export default {
-  /**
-   * 缃戦〉鏍囬
-   */
-  title: import.meta.env.VITE_APP_TITLE,
-  /**
-   * 渚ц竟鏍忎富棰� 娣辫壊涓婚theme-dark锛屾祬鑹蹭富棰榯heme-light
-   */
-  sideTheme: 'theme-dark',
-  /**
-   * 鏄惁绯荤粺甯冨眬閰嶇疆
-   */
-  showSettings: false,
-
-  /**
-   * 鏄惁鏄剧ず椤堕儴瀵艰埅
-   */
-  topNav: false,
-
-  /**
-   * 鏄惁鏄剧ず tagsView
-   */
-  tagsView: true,
-
-  /**
-   * 鏄惁鍥哄畾澶撮儴
-   */
-  fixedHeader: false,
-
-  /**
-   * 鏄惁鏄剧ずlogo
-   */
-  sidebarLogo: true,
-
-  /**
-   * 鏄惁鏄剧ず鍔ㄦ�佹爣棰�
-   */
-  dynamicTitle: false,
-
-  /**
-   * @type {string | array} 'production' | ['production', 'development']
-   * @description Need show err logs component.
-   * The default is only used in the production env
-   * If you want to also use it in dev, you can pass ['production', 'development']
-   */
-  errorLog: 'production'
-}
diff --git a/src/settings.ts b/src/settings.ts
new file mode 100644
index 0000000..25f7995
--- /dev/null
+++ b/src/settings.ts
@@ -0,0 +1,55 @@
+const setting: DefaultSettings = {
+	/**
+	 * 缃戦〉鏍囬
+	 */
+	title: import.meta.env.VITE_APP_TITLE,
+
+	theme: '#409EFF',
+
+	/**
+	 * 渚ц竟鏍忎富棰� 娣辫壊涓婚theme-dark锛屾祬鑹蹭富棰榯heme-light
+	 */
+	sideTheme: 'theme-dark',
+	/**
+	 * 鏄惁绯荤粺甯冨眬閰嶇疆
+	 */
+	showSettings: false,
+
+	/**
+	 * 鏄惁鏄剧ず椤堕儴瀵艰埅
+	 */
+	topNav: false,
+
+	/**
+	 * 鏄惁鏄剧ず tagsView
+	 */
+	tagsView: true,
+
+	/**
+	 * 鏄惁鍥哄畾澶撮儴
+	 */
+	fixedHeader: false,
+
+	/**
+	 * 鏄惁鏄剧ずlogo
+	 */
+	sidebarLogo: true,
+
+	/**
+	 * 鏄惁鏄剧ず鍔ㄦ�佹爣棰�
+	 */
+	dynamicTitle: false,
+
+	/**
+	 * @type {string | array} 'production' | ['production', 'development']
+	 * @description Need show err logs component.
+	 * The default is only used in the production env
+	 * If you want to also use it in dev, you can pass ['production', 'development']
+	 */
+	errorLog: 'production',
+
+	animationEnable: false,
+
+	dark: false
+};
+export default setting;
diff --git a/src/store/index.js b/src/store/index.js
deleted file mode 100644
index f10f389..0000000
--- a/src/store/index.js
+++ /dev/null
@@ -1,3 +0,0 @@
-const store = createPinia()
-
-export default store
\ No newline at end of file
diff --git a/src/store/index.ts b/src/store/index.ts
new file mode 100644
index 0000000..069d54e
--- /dev/null
+++ b/src/store/index.ts
@@ -0,0 +1,3 @@
+const store = createPinia();
+
+export default store;
diff --git a/src/store/modules/app.js b/src/store/modules/app.js
deleted file mode 100644
index 0b57159..0000000
--- a/src/store/modules/app.js
+++ /dev/null
@@ -1,46 +0,0 @@
-import Cookies from 'js-cookie'
-
-const useAppStore = defineStore(
-  'app',
-  {
-    state: () => ({
-      sidebar: {
-        opened: Cookies.get('sidebarStatus') ? !!+Cookies.get('sidebarStatus') : true,
-        withoutAnimation: false,
-        hide: false
-      },
-      device: 'desktop',
-      size: Cookies.get('size') || 'default'
-    }),
-    actions: {
-      toggleSideBar(withoutAnimation) {
-        if (this.sidebar.hide) {
-          return false;
-        }
-        this.sidebar.opened = !this.sidebar.opened
-        this.sidebar.withoutAnimation = withoutAnimation
-        if (this.sidebar.opened) {
-          Cookies.set('sidebarStatus', 1)
-        } else {
-          Cookies.set('sidebarStatus', 0)
-        }
-      },
-      closeSideBar({ withoutAnimation }) {
-        Cookies.set('sidebarStatus', 0)
-        this.sidebar.opened = false
-        this.sidebar.withoutAnimation = withoutAnimation
-      },
-      toggleDevice(device) {
-        this.device = device
-      },
-      setSize(size) {
-        this.size = size;
-        Cookies.set('size', size)
-      },
-      toggleSideBarHide(status) {
-        this.sidebar.hide = status
-      }
-    }
-  })
-
-export default useAppStore
diff --git a/src/store/modules/app.ts b/src/store/modules/app.ts
new file mode 100644
index 0000000..b53dbad
--- /dev/null
+++ b/src/store/modules/app.ts
@@ -0,0 +1,73 @@
+import Cookies from 'js-cookie';
+import zhCn from 'element-plus/es/locale/lang/zh-cn';
+import en from 'element-plus/es/locale/lang/en';
+
+export const useAppStore = defineStore('app', () => {
+	const sidebarStatus = Cookies.get('sidebarStatus');
+	const sidebar = reactive({
+		opened: sidebarStatus ? !!+sidebarStatus : true,
+		withoutAnimation: false,
+		hide: false
+	});
+	const device = ref<string>('desktop');
+	const size = ref(Cookies.get('size') || 'default');
+	// 璇█
+	const language = ref(Cookies.get('language'));
+	const locale = computed(() => {
+		if (language?.value == 'en') {
+			return en;
+		} else {
+			return zhCn;
+		}
+	});
+
+	const toggleSideBar = (withoutAnimation?: boolean) => {
+		if (sidebar.hide) {
+			return false;
+		}
+
+		sidebar.opened = !sidebar.opened;
+		sidebar.withoutAnimation = withoutAnimation as boolean;
+		if (sidebar.opened) {
+			Cookies.set('sidebarStatus', '1');
+		} else {
+			Cookies.set('sidebarStatus', '0');
+		}
+	};
+
+	const closeSideBar = ({ withoutAnimation }: any): void => {
+		Cookies.set('sidebarStatus', '0');
+		sidebar.opened = false;
+		sidebar.withoutAnimation = withoutAnimation;
+	};
+	const toggleDevice = (d: string): void => {
+		device.value = d;
+	};
+	const setSize = (s: string): void => {
+		size.value = s;
+		Cookies.set('size', s);
+	};
+	const toggleSideBarHide = (status: boolean): void => {
+		sidebar.hide = status;
+	};
+
+	const changeLanguage = (val: string): void => {
+		language.value = val;
+	};
+
+	return {
+		device,
+		sidebar,
+		language,
+		locale,
+		size,
+		changeLanguage,
+		toggleSideBar,
+		closeSideBar,
+		toggleDevice,
+		setSize,
+		toggleSideBarHide
+	};
+});
+
+export default useAppStore;
diff --git a/src/store/modules/dict.js b/src/store/modules/dict.js
deleted file mode 100644
index 27fc308..0000000
--- a/src/store/modules/dict.js
+++ /dev/null
@@ -1,57 +0,0 @@
-const useDictStore = defineStore(
-  'dict',
-  {
-    state: () => ({
-      dict: new Array()
-    }),
-    actions: {
-      // 鑾峰彇瀛楀吀
-      getDict(_key) {
-        if (_key == null && _key == "") {
-          return null;
-        }
-        try {
-          for (let i = 0; i < this.dict.length; i++) {
-            if (this.dict[i].key == _key) {
-              return this.dict[i].value;
-            }
-          }
-        } catch (e) {
-          return null;
-        }
-      },
-      // 璁剧疆瀛楀吀
-      setDict(_key, value) {
-        if (_key !== null && _key !== "") {
-          this.dict.push({
-            key: _key,
-            value: value
-          });
-        }
-      },
-      // 鍒犻櫎瀛楀吀
-      removeDict(_key) {
-        var bln = false;
-        try {
-          for (let i = 0; i < this.dict.length; i++) {
-            if (this.dict[i].key == _key) {
-              this.dict.splice(i, 1);
-              return true;
-            }
-          }
-        } catch (e) {
-          bln = false;
-        }
-        return bln;
-      },
-      // 娓呯┖瀛楀吀
-      cleanDict() {
-        this.dict = new Array();
-      },
-      // 鍒濆瀛楀吀
-      initDict() {
-      }
-    }
-  })
-
-export default useDictStore
diff --git a/src/store/modules/dict.ts b/src/store/modules/dict.ts
new file mode 100644
index 0000000..50b8871
--- /dev/null
+++ b/src/store/modules/dict.ts
@@ -0,0 +1,78 @@
+export const useDictStore = defineStore('dict', () => {
+	const dict = ref<
+		Array<{
+			key: string;
+			value: DictDataOption[];
+		}>
+	>([]);
+
+	/**
+	 * 鑾峰彇瀛楀吀
+	 * @param _key 瀛楀吀key
+	 */
+	const getDict = (_key: string): DictDataOption[] | null => {
+		if (_key == null && _key == '') {
+			return null;
+		}
+		try {
+			for (let i = 0; i < dict.value.length; i++) {
+				if (dict.value[i].key == _key) {
+					return dict.value[i].value;
+				}
+			}
+		} catch (e) {
+			return null;
+		}
+		return null;
+	};
+
+	/**
+	 * 璁剧疆瀛楀吀
+	 * @param _key 瀛楀吀key
+	 * @param _value 瀛楀吀value
+	 */
+	const setDict = (_key: string, _value: DictDataOption[]) => {
+		if (_key !== null && _key !== '') {
+			dict.value.push({
+				key: _key,
+				value: _value
+			});
+		}
+	};
+
+	/**
+	 * 鍒犻櫎瀛楀吀
+	 * @param _key
+	 */
+	const removeDict = (_key: string): boolean => {
+		let bln = false;
+		try {
+			for (let i = 0; i < dict.value.length; i++) {
+				if (dict.value[i].key == _key) {
+					dict.value.splice(i, 1);
+					return true;
+				}
+			}
+		} catch (e) {
+			bln = false;
+		}
+		return bln;
+	};
+
+	/**
+	 * 娓呯┖瀛楀吀
+	 */
+	const cleanDict = (): void => {
+		dict.value = [];
+	};
+
+	return {
+		dict,
+		getDict,
+		setDict,
+		removeDict,
+		cleanDict
+	};
+});
+
+export default useDictStore;
diff --git a/src/store/modules/permission.js b/src/store/modules/permission.js
deleted file mode 100644
index ef506e9..0000000
--- a/src/store/modules/permission.js
+++ /dev/null
@@ -1,138 +0,0 @@
-import auth from '@/plugins/auth'
-import router, { constantRoutes, dynamicRoutes } from '@/router'
-import { getRouters } from '@/api/menu'
-import Layout from '@/layout/index'
-import ParentView from '@/components/ParentView'
-import InnerLink from '@/layout/components/InnerLink'
-
-// 鍖归厤views閲岄潰鎵�鏈夌殑.vue鏂囦欢
-const modules = import.meta.glob('./../../views/**/*.vue')
-
-const usePermissionStore = defineStore(
-  'permission',
-  {
-    state: () => ({
-      routes: [],
-      addRoutes: [],
-      defaultRoutes: [],
-      topbarRouters: [],
-      sidebarRouters: []
-    }),
-    actions: {
-      setRoutes(routes) {
-        this.addRoutes = routes
-        this.routes = constantRoutes.concat(routes)
-      },
-      setDefaultRoutes(routes) {
-        this.defaultRoutes = constantRoutes.concat(routes)
-      },
-      setTopbarRoutes(routes) {
-        this.topbarRouters = routes
-      },
-      setSidebarRouters(routes) {
-        this.sidebarRouters = routes
-      },
-      generateRoutes(roles) {
-        return new Promise(resolve => {
-          // 鍚戝悗绔姹傝矾鐢辨暟鎹�
-          getRouters().then(res => {
-            const sdata = JSON.parse(JSON.stringify(res.data))
-            const rdata = JSON.parse(JSON.stringify(res.data))
-            const defaultData = JSON.parse(JSON.stringify(res.data))
-            const sidebarRoutes = filterAsyncRouter(sdata)
-            const rewriteRoutes = filterAsyncRouter(rdata, false, true)
-            const defaultRoutes = filterAsyncRouter(defaultData)
-            const asyncRoutes = filterDynamicRoutes(dynamicRoutes)
-            asyncRoutes.forEach(route => { router.addRoute(route) })
-            this.setRoutes(rewriteRoutes)
-            this.setSidebarRouters(constantRoutes.concat(sidebarRoutes))
-            this.setDefaultRoutes(sidebarRoutes)
-            this.setTopbarRoutes(defaultRoutes)
-            resolve(rewriteRoutes)
-          })
-        })
-      }
-    }
-  })
-
-// 閬嶅巻鍚庡彴浼犳潵鐨勮矾鐢卞瓧绗︿覆锛岃浆鎹负缁勪欢瀵硅薄
-function filterAsyncRouter(asyncRouterMap, lastRouter = false, type = false) {
-  return asyncRouterMap.filter(route => {
-    if (type && route.children) {
-      route.children = filterChildren(route.children)
-    }
-    if (route.component) {
-      // Layout ParentView 缁勪欢鐗规畩澶勭悊
-      if (route.component === 'Layout') {
-        route.component = Layout
-      } else if (route.component === 'ParentView') {
-        route.component = ParentView
-      } else if (route.component === 'InnerLink') {
-        route.component = InnerLink
-      } else {
-        route.component = loadView(route.component)
-      }
-    }
-    if (route.children != null && route.children && route.children.length) {
-      route.children = filterAsyncRouter(route.children, route, type)
-    } else {
-      delete route['children']
-      delete route['redirect']
-    }
-    return true
-  })
-}
-
-function filterChildren(childrenMap, lastRouter = false) {
-  var children = []
-  childrenMap.forEach((el, index) => {
-    if (el.children && el.children.length) {
-      if (el.component === 'ParentView' && !lastRouter) {
-        el.children.forEach(c => {
-          c.path = el.path + '/' + c.path
-          if (c.children && c.children.length) {
-            children = children.concat(filterChildren(c.children, c))
-            return
-          }
-          children.push(c)
-        })
-        return
-      }
-    }
-    if (lastRouter) {
-      el.path = lastRouter.path + '/' + el.path
-    }
-    children = children.concat(el)
-  })
-  return children
-}
-
-// 鍔ㄦ�佽矾鐢遍亶鍘嗭紝楠岃瘉鏄惁鍏峰鏉冮檺
-export function filterDynamicRoutes(routes) {
-  const res = []
-  routes.forEach(route => {
-    if (route.permissions) {
-      if (auth.hasPermiOr(route.permissions)) {
-        res.push(route)
-      }
-    } else if (route.roles) {
-      if (auth.hasRoleOr(route.roles)) {
-        res.push(route)
-      }
-    }
-  })
-  return res
-}
-
-export const loadView = (view) => {
-  let res;
-  for (const path in modules) {
-    const dir = path.split('views/')[1].split('.vue')[0];
-    if (dir === view) {
-      res = () => modules[path]();
-    }
-  }
-  return res;
-}
-
-export default usePermissionStore
diff --git a/src/store/modules/permission.ts b/src/store/modules/permission.ts
new file mode 100644
index 0000000..271518e
--- /dev/null
+++ b/src/store/modules/permission.ts
@@ -0,0 +1,144 @@
+import { defineStore } from 'pinia';
+import router, { constantRoutes, dynamicRoutes } from '@/router';
+import store from '@/store';
+import { getRouters } from '@/api/menu';
+import Layout from '@/layout/index.vue';
+import ParentView from '@/components/ParentView/index.vue';
+import InnerLink from '@/layout/components/InnerLink/index.vue';
+import auth from '@/plugins/auth';
+import { RouteOption } from 'vue-router';
+// 鍖归厤views閲岄潰鎵�鏈夌殑.vue鏂囦欢
+const modules = import.meta.glob('./../../views/**/*.vue');
+
+export const usePermissionStore = defineStore('permission', () => {
+	const routes = ref<RouteOption[]>([]);
+	const addRoutes = ref<RouteOption[]>([]);
+	const defaultRoutes = ref<RouteOption[]>([]);
+	const topbarRouters = ref<RouteOption[]>([]);
+	const sidebarRouters = ref<RouteOption[]>([]);
+
+	const setRoutes = (newRoutes: RouteOption[]): void => {
+		addRoutes.value = newRoutes;
+		routes.value = constantRoutes.concat(newRoutes);
+	};
+	const setDefaultRoutes = (routes: RouteOption[]): void => {
+		defaultRoutes.value = constantRoutes.concat(routes);
+	};
+	const setTopbarRoutes = (routes: RouteOption[]): void => {
+		topbarRouters.value = routes;
+	};
+	const setSidebarRouters = (routes: RouteOption[]): void => {
+		sidebarRouters.value = routes;
+	};
+	const generateRoutes = async (): Promise<RouteOption[]> => {
+		const res = await getRouters();
+		const { data } = res;
+		const sdata = JSON.parse(JSON.stringify(data));
+		const rdata = JSON.parse(JSON.stringify(data));
+		const defaultData = JSON.parse(JSON.stringify(data));
+		const sidebarRoutes = filterAsyncRouter(sdata);
+		const rewriteRoutes = filterAsyncRouter(rdata, undefined, true);
+		const defaultRoutes = filterAsyncRouter(defaultData);
+		const asyncRoutes = filterDynamicRoutes(dynamicRoutes);
+		asyncRoutes.forEach((route) => {
+			router.addRoute(route);
+		});
+		setRoutes(rewriteRoutes);
+		setSidebarRouters(constantRoutes.concat(sidebarRoutes));
+		setDefaultRoutes(sidebarRoutes);
+		setTopbarRoutes(defaultRoutes);
+		return new Promise<RouteOption[]>((resolve) => resolve(rewriteRoutes));
+	};
+
+	/**
+	 * 閬嶅巻鍚庡彴浼犳潵鐨勮矾鐢卞瓧绗︿覆锛岃浆鎹负缁勪欢瀵硅薄
+	 * @param asyncRouterMap 鍚庡彴浼犳潵鐨勮矾鐢卞瓧绗︿覆
+	 * @param lastRouter 涓婁竴绾ц矾鐢�
+	 * @param type 鏄惁鏄噸鍐欒矾鐢�
+	 */
+	const filterAsyncRouter = (asyncRouterMap: RouteOption[], lastRouter?: RouteOption, type = false): RouteOption[] => {
+		return asyncRouterMap.filter((route) => {
+			if (type && route.children) {
+				route.children = filterChildren(route.children, undefined);
+			}
+			if (route.component) {
+				// Layout ParentView 缁勪欢鐗规畩澶勭悊
+				if (route.component === 'Layout') {
+					route.component = Layout;
+				} else if (route.component === 'ParentView') {
+					route.component = ParentView;
+				} else if (route.component === 'InnerLink') {
+					route.component = InnerLink;
+				} else {
+					route.component = loadView(route.component);
+				}
+			}
+			if (route.children != null && route.children && route.children.length) {
+				route.children = filterAsyncRouter(route.children, route, type);
+			} else {
+				delete route.children;
+				delete route.redirect;
+			}
+			return true;
+		});
+	};
+	const filterChildren = (childrenMap: RouteOption[], lastRouter?: RouteOption): RouteOption[] => {
+		let children: RouteOption[] = [];
+		childrenMap.forEach((el) => {
+			if (el.children && el.children.length) {
+				if (el.component === 'ParentView' && !lastRouter) {
+					el.children.forEach((c) => {
+						c.path = el.path + '/' + c.path;
+						if (c.children && c.children.length) {
+							children = children.concat(filterChildren(c.children, c));
+							return;
+						}
+						children.push(c);
+					});
+					return;
+				}
+			}
+			if (lastRouter) {
+				el.path = lastRouter.path + '/' + el.path;
+			}
+			children = children.concat(el);
+		});
+		return children;
+	};
+	return { routes, setRoutes, generateRoutes, setSidebarRouters, topbarRouters, sidebarRouters, defaultRoutes };
+});
+
+// 鍔ㄦ�佽矾鐢遍亶鍘嗭紝楠岃瘉鏄惁鍏峰鏉冮檺
+export const filterDynamicRoutes = (routes: RouteOption[]) => {
+	const res: RouteOption[] = [];
+	routes.forEach((route) => {
+		if (route.permissions) {
+			if (auth.hasPermiOr(route.permissions)) {
+				res.push(route);
+			}
+		} else if (route.roles) {
+			if (auth.hasRoleOr(route.roles)) {
+				res.push(route);
+			}
+		}
+	});
+	return res;
+};
+
+export const loadView = (view: any) => {
+	let res;
+	for (const path in modules) {
+		const dir = path.split('views/')[1].split('.vue')[0];
+		if (dir === view) {
+			res = () => modules[path]();
+		}
+	}
+	return res;
+};
+
+// 闈瀞etup
+export const usePermissionStoreHook = () => {
+	return usePermissionStore(store);
+};
+
+export default usePermissionStore;
diff --git a/src/store/modules/settings.js b/src/store/modules/settings.js
deleted file mode 100644
index 22b7336..0000000
--- a/src/store/modules/settings.js
+++ /dev/null
@@ -1,38 +0,0 @@
-import defaultSettings from '@/settings'
-import { useDynamicTitle } from '@/utils/dynamicTitle'
-
-const { sideTheme, showSettings, topNav, tagsView, fixedHeader, sidebarLogo, dynamicTitle } = defaultSettings
-
-const storageSetting = JSON.parse(localStorage.getItem('layout-setting')) || ''
-
-const useSettingsStore = defineStore(
-  'settings',
-  {
-    state: () => ({
-      title: '',
-      theme: storageSetting.theme || '#409EFF',
-      sideTheme: storageSetting.sideTheme || sideTheme,
-      showSettings: showSettings,
-      topNav: storageSetting.topNav === undefined ? topNav : storageSetting.topNav,
-      tagsView: storageSetting.tagsView === undefined ? tagsView : storageSetting.tagsView,
-      fixedHeader: storageSetting.fixedHeader === undefined ? fixedHeader : storageSetting.fixedHeader,
-      sidebarLogo: storageSetting.sidebarLogo === undefined ? sidebarLogo : storageSetting.sidebarLogo,
-      dynamicTitle: storageSetting.dynamicTitle === undefined ? dynamicTitle : storageSetting.dynamicTitle
-    }),
-    actions: {
-      // 淇敼甯冨眬璁剧疆
-      changeSetting(data) {
-        const { key, value } = data
-        if (this.hasOwnProperty(key)) {
-          this[key] = value
-        }
-      },
-      // 璁剧疆缃戦〉鏍囬
-      setTitle(title) {
-        this.title = title
-        useDynamicTitle();
-      }
-    }
-  })
-
-export default useSettingsStore
diff --git a/src/store/modules/settings.ts b/src/store/modules/settings.ts
new file mode 100644
index 0000000..32119cd
--- /dev/null
+++ b/src/store/modules/settings.ts
@@ -0,0 +1,54 @@
+import { defineStore } from 'pinia';
+import defaultSettings from '@/settings';
+import { SettingTypeEnum } from '@/enums/SettingTypeEnum';
+import { useDynamicTitle } from '@/utils/dynamicTitle';
+import { Ref } from 'vue';
+
+export const useSettingsStore = defineStore('setting', () => {
+	const storageSetting = JSON.parse(localStorage.getItem('layout-setting') || '{}');
+
+	const prop: { [key: string]: Ref<any> } = {
+		title: ref<string>(''),
+		theme: ref<string>(storageSetting.theme || defaultSettings.theme),
+		sideTheme: ref<string>(storageSetting.sideTheme || defaultSettings.sideTheme),
+		showSettings: ref<boolean>(storageSetting.showSettings),
+		topNav: ref<boolean>(storageSetting.topNav || defaultSettings.topNav),
+		tagsView: ref<boolean>(storageSetting.tagsView || defaultSettings.tagsView),
+		fixedHeader: ref<boolean>(storageSetting.fixedHeader || defaultSettings.fixedHeader),
+		sidebarLogo: ref<boolean>(storageSetting.sidebarLogo || defaultSettings.sidebarLogo),
+		dynamicTitle: ref<boolean>(storageSetting.dynamicTitle || defaultSettings.dynamicTitle),
+		animationEnable: ref<boolean>(storageSetting.animationEnable || defaultSettings.animationEnable),
+		dark: ref<boolean>(storageSetting.dark || defaultSettings.dark)
+	};
+
+	const { title, theme, sideTheme, showSettings, topNav, tagsView, fixedHeader, sidebarLogo, dynamicTitle, animationEnable, dark } = prop;
+
+	// actions
+	const changeSetting = (param: { key: SettingTypeEnum; value: any }) => {
+		const { key, value } = param;
+		if (key in prop) {
+			prop[key].value = value;
+		}
+	};
+	const setTitle = (value: string) => {
+		title.value = value;
+		useDynamicTitle();
+	};
+	return {
+		title,
+		theme,
+		sideTheme,
+		showSettings,
+		topNav,
+		tagsView,
+		fixedHeader,
+		sidebarLogo,
+		dynamicTitle,
+		animationEnable,
+		dark,
+		changeSetting,
+		setTitle
+	};
+});
+
+export default useSettingsStore;
diff --git a/src/store/modules/tagsView.js b/src/store/modules/tagsView.js
deleted file mode 100644
index 9d07f33..0000000
--- a/src/store/modules/tagsView.js
+++ /dev/null
@@ -1,182 +0,0 @@
-const useTagsViewStore = defineStore(
-  'tags-view',
-  {
-    state: () => ({
-      visitedViews: [],
-      cachedViews: [],
-      iframeViews: []
-    }),
-    actions: {
-      addView(view) {
-        this.addVisitedView(view)
-        this.addCachedView(view)
-      },
-      addIframeView(view) {
-        if (this.iframeViews.some(v => v.path === view.path)) return
-        this.iframeViews.push(
-          Object.assign({}, view, {
-            title: view.meta.title || 'no-name'
-          })
-        )
-      },
-      addVisitedView(view) {
-        if (this.visitedViews.some(v => v.path === view.path)) return
-        this.visitedViews.push(
-          Object.assign({}, view, {
-            title: view.meta.title || 'no-name'
-          })
-        )
-      },
-      addCachedView(view) {
-        if (this.cachedViews.includes(view.name)) return
-        if (!view.meta.noCache) {
-          this.cachedViews.push(view.name)
-        }
-      },
-      delView(view) {
-        return new Promise(resolve => {
-          this.delVisitedView(view)
-          this.delCachedView(view)
-          resolve({
-            visitedViews: [...this.visitedViews],
-            cachedViews: [...this.cachedViews]
-          })
-        })
-      },
-      delVisitedView(view) {
-        return new Promise(resolve => {
-          for (const [i, v] of this.visitedViews.entries()) {
-            if (v.path === view.path) {
-              this.visitedViews.splice(i, 1)
-              break
-            }
-          }
-          this.iframeViews = this.iframeViews.filter(item => item.path !== view.path)
-          resolve([...this.visitedViews])
-        })
-      },
-      delIframeView(view) {
-        return new Promise(resolve => {
-          this.iframeViews = this.iframeViews.filter(item => item.path !== view.path)
-          resolve([...this.iframeViews])
-        })
-      },
-      delCachedView(view) {
-        return new Promise(resolve => {
-          const index = this.cachedViews.indexOf(view.name)
-          index > -1 && this.cachedViews.splice(index, 1)
-          resolve([...this.cachedViews])
-        })
-      },
-      delOthersViews(view) {
-        return new Promise(resolve => {
-          this.delOthersVisitedViews(view)
-          this.delOthersCachedViews(view)
-          resolve({
-            visitedViews: [...this.visitedViews],
-            cachedViews: [...this.cachedViews]
-          })
-        })
-      },
-      delOthersVisitedViews(view) {
-        return new Promise(resolve => {
-          this.visitedViews = this.visitedViews.filter(v => {
-            return v.meta.affix || v.path === view.path
-          })
-          this.iframeViews = this.iframeViews.filter(item => item.path === view.path)
-          resolve([...this.visitedViews])
-        })
-      },
-      delOthersCachedViews(view) {
-        return new Promise(resolve => {
-          const index = this.cachedViews.indexOf(view.name)
-          if (index > -1) {
-            this.cachedViews = this.cachedViews.slice(index, index + 1)
-          } else {
-            this.cachedViews = []
-          }
-          resolve([...this.cachedViews])
-        })
-      },
-      delAllViews(view) {
-        return new Promise(resolve => {
-          this.delAllVisitedViews(view)
-          this.delAllCachedViews(view)
-          resolve({
-            visitedViews: [...this.visitedViews],
-            cachedViews: [...this.cachedViews]
-          })
-        })
-      },
-      delAllVisitedViews(view) {
-        return new Promise(resolve => {
-          const affixTags = this.visitedViews.filter(tag => tag.meta.affix)
-          this.visitedViews = affixTags
-          this.iframeViews = []
-          resolve([...this.visitedViews])
-        })
-      },
-      delAllCachedViews(view) {
-        return new Promise(resolve => {
-          this.cachedViews = []
-          resolve([...this.cachedViews])
-        })
-      },
-      updateVisitedView(view) {
-        for (let v of this.visitedViews) {
-          if (v.path === view.path) {
-            v = Object.assign(v, view)
-            break
-          }
-        }
-      },
-      delRightTags(view) {
-        return new Promise(resolve => {
-          const index = this.visitedViews.findIndex(v => v.path === view.path)
-          if (index === -1) {
-            return
-          }
-          this.visitedViews = this.visitedViews.filter((item, idx) => {
-            if (idx <= index || (item.meta && item.meta.affix)) {
-              return true
-            }
-            const i = this.cachedViews.indexOf(item.name)
-            if (i > -1) {
-              this.cachedViews.splice(i, 1)
-            }
-            if(item.meta.link) {
-              const fi = this.iframeViews.findIndex(v => v.path === item.path)
-              this.iframeViews.splice(fi, 1)
-            }
-            return false
-          })
-          resolve([...this.visitedViews])
-        })
-      },
-      delLeftTags(view) {
-        return new Promise(resolve => {
-          const index = this.visitedViews.findIndex(v => v.path === view.path)
-          if (index === -1) {
-            return
-          }
-          this.visitedViews = this.visitedViews.filter((item, idx) => {
-            if (idx >= index || (item.meta && item.meta.affix)) {
-              return true
-            }
-            const i = this.cachedViews.indexOf(item.name)
-            if (i > -1) {
-              this.cachedViews.splice(i, 1)
-            }
-            if(item.meta.link) {
-              const fi = this.iframeViews.findIndex(v => v.path === item.path)
-              this.iframeViews.splice(fi, 1)
-            }
-            return false
-          })
-          resolve([...this.visitedViews])
-        })
-      }
-    }
-  })
-
-export default useTagsViewStore
diff --git a/src/store/modules/tagsView.ts b/src/store/modules/tagsView.ts
new file mode 100644
index 0000000..c0fdf5d
--- /dev/null
+++ b/src/store/modules/tagsView.ts
@@ -0,0 +1,198 @@
+import { TagView } from 'vue-router';
+
+export const useTagsViewStore = defineStore('tagsView', () => {
+	const visitedViews = ref<TagView[]>([]);
+	const cachedViews = ref<string[]>([]);
+	const iframeViews = ref<TagView[]>([]);
+
+	const addView = (view: TagView) => {
+		addVisitedView(view);
+		addCachedView(view);
+	};
+
+	const addIframeView = (view: TagView): void => {
+		if (iframeViews.value.some((v) => v.path === view.path)) return;
+		iframeViews.value.push(
+			Object.assign({}, view, {
+				title: view.meta?.title || 'no-name'
+			})
+		);
+	};
+	const delIframeView = (view: TagView): Promise<TagView[]> => {
+		return new Promise((resolve) => {
+			iframeViews.value = iframeViews.value.filter((item) => item.path !== view.path);
+			resolve([...iframeViews.value]);
+		});
+	};
+	const addVisitedView = (view: TagView): void => {
+		if (visitedViews.value.some((v) => v.path === view.path)) return;
+		visitedViews.value.push(
+			Object.assign({}, view, {
+				title: view.meta?.title || 'no-name'
+			})
+		);
+	};
+	const delView = (view: TagView): Promise<{ visitedViews: TagView[]; cachedViews: string[] }> => {
+		return new Promise((resolve) => {
+			delVisitedView(view);
+			delCachedView(view);
+			resolve({
+				visitedViews: [...visitedViews.value],
+				cachedViews: [...cachedViews.value]
+			});
+		});
+	};
+
+	const delVisitedView = (view: TagView): Promise<TagView[]> => {
+		return new Promise((resolve) => {
+			for (const [i, v] of visitedViews.value.entries()) {
+				if (v.path === view.path) {
+					visitedViews.value.splice(i, 1);
+					break;
+				}
+			}
+			resolve([...visitedViews.value]);
+		});
+	};
+	const delCachedView = (view: TagView): Promise<string[]> => {
+		const viewName = view.name as string;
+		return new Promise((resolve) => {
+			const index = cachedViews.value.indexOf(viewName);
+			index > -1 && cachedViews.value.splice(index, 1);
+			resolve([...cachedViews.value]);
+		});
+	};
+	const delOthersViews = (view: TagView): Promise<{ visitedViews: TagView[]; cachedViews: string[] }> => {
+		return new Promise((resolve) => {
+			delOthersVisitedViews(view);
+			delOthersCachedViews(view);
+			resolve({
+				visitedViews: [...visitedViews.value],
+				cachedViews: [...cachedViews.value]
+			});
+		});
+	};
+
+	const delOthersVisitedViews = (view: TagView): Promise<TagView[]> => {
+		return new Promise((resolve) => {
+			visitedViews.value = visitedViews.value.filter((v) => {
+				return v.meta?.affix || v.path === view.path;
+			});
+			resolve([...visitedViews.value]);
+		});
+	};
+	const delOthersCachedViews = (view: TagView): Promise<string[]> => {
+		const viewName = view.name as string;
+		return new Promise((resolve) => {
+			const index = cachedViews.value.indexOf(viewName);
+			if (index > -1) {
+				cachedViews.value = cachedViews.value.slice(index, index + 1);
+			} else {
+				cachedViews.value = [];
+			}
+			resolve([...cachedViews.value]);
+		});
+	};
+
+	const delAllViews = (): Promise<{ visitedViews: TagView[]; cachedViews: string[] }> => {
+		return new Promise((resolve) => {
+			delAllVisitedViews();
+			delAllCachedViews();
+			resolve({
+				visitedViews: [...visitedViews.value],
+				cachedViews: [...cachedViews.value]
+			});
+		});
+	};
+	const delAllVisitedViews = (): Promise<TagView[]> => {
+		return new Promise((resolve) => {
+			visitedViews.value = visitedViews.value.filter((tag) => tag.meta?.affix);
+			resolve([...visitedViews.value]);
+		});
+	};
+
+	const delAllCachedViews = (): Promise<string[]> => {
+		return new Promise((resolve) => {
+			cachedViews.value = [];
+			resolve([...cachedViews.value]);
+		});
+	};
+
+	const updateVisitedView = (view: TagView): void => {
+		for (let v of visitedViews.value) {
+			if (v.path === view.path) {
+				v = Object.assign(v, view);
+				break;
+			}
+		}
+	};
+	const delRightTags = (view: TagView): Promise<TagView[]> => {
+		return new Promise((resolve) => {
+			const index = visitedViews.value.findIndex((v) => v.path === view.path);
+			if (index === -1) {
+				return;
+			}
+			visitedViews.value = visitedViews.value.filter((item, idx) => {
+				if (idx <= index || (item.meta && item.meta.affix)) {
+					return true;
+				}
+				const i = cachedViews.value.indexOf(item.name as string);
+				if (i > -1) {
+					cachedViews.value.splice(i, 1);
+				}
+				return false;
+			});
+			resolve([...visitedViews.value]);
+		});
+	};
+	const delLeftTags = (view: TagView): Promise<TagView[]> => {
+		return new Promise((resolve) => {
+			const index = visitedViews.value.findIndex((v) => v.path === view.path);
+			if (index === -1) {
+				return;
+			}
+			visitedViews.value = visitedViews.value.filter((item, idx) => {
+				if (idx >= index || (item.meta && item.meta.affix)) {
+					return true;
+				}
+				const i = cachedViews.value.indexOf(item.name as string);
+				if (i > -1) {
+					cachedViews.value.splice(i, 1);
+				}
+				return false;
+			});
+			resolve([...visitedViews.value]);
+		});
+	};
+
+	const addCachedView = (view: TagView): void => {
+		const viewName = view.name as string;
+		if (cachedViews.value.includes(viewName)) return;
+		if (!view.meta?.noCache) {
+			cachedViews.value.push(viewName);
+		}
+	};
+
+	return {
+		visitedViews,
+		cachedViews,
+		iframeViews,
+		addVisitedView,
+		addCachedView,
+		delVisitedView,
+		delCachedView,
+		updateVisitedView,
+		addView,
+		delView,
+		delAllViews,
+		delAllVisitedViews,
+		delAllCachedViews,
+		delOthersViews,
+		delRightTags,
+		delLeftTags,
+		addIframeView,
+		delIframeView
+	};
+});
+
+export default useTagsViewStore;
diff --git a/src/store/modules/user.js b/src/store/modules/user.js
deleted file mode 100644
index 77b8b15..0000000
--- a/src/store/modules/user.js
+++ /dev/null
@@ -1,73 +0,0 @@
-import { login, logout, getInfo } from '@/api/login'
-import { getToken, setToken, removeToken } from '@/utils/auth'
-import defAva from '@/assets/images/profile.jpg'
-
-const useUserStore = defineStore(
-  'user',
-  {
-    state: () => ({
-      userId: undefined,
-      token: getToken(),
-      name: '',
-      avatar: '',
-      roles: [],
-      permissions: []
-    }),
-    actions: {
-      // 鐧诲綍
-      login(userInfo) {
-        const tenantId = userInfo.tenantId.trim()
-        const username = userInfo.username.trim()
-        const password = userInfo.password
-        const code = userInfo.code
-        const uuid = userInfo.uuid
-        return new Promise((resolve, reject) => {
-          login(tenantId, username, password, code, uuid).then(res => {
-            setToken(res.data.token)
-            this.token = res.data.token
-            resolve()
-          }).catch(error => {
-            reject(error)
-          })
-        })
-      },
-      // 鑾峰彇鐢ㄦ埛淇℃伅
-      getInfo() {
-        return new Promise((resolve, reject) => {
-          getInfo().then(res => {
-            const user = res.data.user
-            const avatar = (user.avatar == "" || user.avatar == null) ? defAva : user.avatar;
-
-            if (res.data.roles && res.data.roles.length > 0) { // 楠岃瘉杩斿洖鐨剅oles鏄惁鏄竴涓潪绌烘暟缁�
-              this.roles = res.data.roles
-              this.permissions = res.data.permissions
-            } else {
-              this.roles = ['ROLE_DEFAULT']
-            }
-            this.name = user.userName
-            this.avatar = avatar;
-            this.userId = user.userId;
-            resolve(res)
-          }).catch(error => {
-            reject(error)
-          })
-        })
-      },
-      // 閫�鍑虹郴缁�
-      logOut() {
-        return new Promise((resolve, reject) => {
-          logout(this.token).then(() => {
-            this.token = ''
-            this.roles = []
-            this.permissions = []
-            removeToken()
-            resolve()
-          }).catch(error => {
-            reject(error)
-          })
-        })
-      }
-    }
-  })
-
-export default useUserStore
diff --git a/src/store/modules/user.ts b/src/store/modules/user.ts
new file mode 100644
index 0000000..dafec8b
--- /dev/null
+++ b/src/store/modules/user.ts
@@ -0,0 +1,83 @@
+import { to } from 'await-to-js';
+import defAva from '@/assets/images/profile.jpg';
+import store from '@/store';
+import { getToken, removeToken, setToken } from '@/utils/auth';
+import { login as loginApi, logout as logoutApi, getInfo as getUserInfo } from '@/api/login';
+import { LoginData } from '@/api/types';
+
+export const useUserStore = defineStore('user', () => {
+	const token = ref(getToken());
+	const name = ref('');
+	const nickname = ref('');
+	const userId = ref<string | number>('');
+	const avatar = ref('');
+	const roles = ref<Array<string>>([]); // 鐢ㄦ埛瑙掕壊缂栫爜闆嗗悎 鈫� 鍒ゆ柇璺敱鏉冮檺
+	const permissions = ref<Array<string>>([]); // 鐢ㄦ埛鏉冮檺缂栫爜闆嗗悎 鈫� 鍒ゆ柇鎸夐挳鏉冮檺
+
+	/**
+	 * 鐧诲綍
+	 * @param userInfo
+	 * @returns
+	 */
+	const login = async (userInfo: LoginData): Promise<void> => {
+		const [err, res] = await to(loginApi(userInfo));
+		if (res) {
+			const data = res.data;
+			setToken(data.token);
+			token.value = data.token;
+			return Promise.resolve();
+		}
+		return Promise.reject(err);
+	};
+
+	// 鑾峰彇鐢ㄦ埛淇℃伅
+	const getInfo = async (): Promise<void> => {
+		const [err, res] = await to(getUserInfo());
+		if (res) {
+			const data = res.data;
+			const user = data.user;
+			const profile = user.avatar == '' || user.avatar == null ? defAva : user.avatar;
+
+			if (data.roles && data.roles.length > 0) {
+				// 楠岃瘉杩斿洖鐨剅oles鏄惁鏄竴涓潪绌烘暟缁�
+				roles.value = data.roles;
+				permissions.value = data.permissions;
+			} else {
+				roles.value = ['ROLE_DEFAULT'];
+			}
+			name.value = user.userName;
+			nickname.value = user.nickName;
+			avatar.value = profile;
+			userId.value = user.userId;
+			return Promise.resolve();
+		}
+		return Promise.reject(err);
+	};
+
+	// 娉ㄩ攢
+	const logout = async (): Promise<void> => {
+		await logoutApi();
+		token.value = '';
+		roles.value = [];
+		permissions.value = [];
+		removeToken();
+	};
+
+	return {
+		userId,
+		token,
+		nickname,
+		avatar,
+		roles,
+		permissions,
+		login,
+		getInfo,
+		logout
+	};
+});
+
+export default useUserStore;
+// 闈瀞etup
+export function useUserStoreHook() {
+	return useUserStore(store);
+}
diff --git a/src/types/auto-imports.d.ts b/src/types/auto-imports.d.ts
new file mode 100644
index 0000000..6ced9ee
--- /dev/null
+++ b/src/types/auto-imports.d.ts
@@ -0,0 +1,574 @@
+// Generated by 'unplugin-auto-import'
+export {}
+declare global {
+  const EffectScope: typeof import('vue')['EffectScope']
+  const ElForm: typeof import('element-plus/es')['ElForm']
+  const ElLoading: typeof import('element-plus/es')['ElLoading']
+  const ElMessage: typeof import('element-plus/es')['ElMessage']
+  const ElMessageBox: typeof import('element-plus/es')['ElMessageBox']
+  const ElNotification: typeof import('element-plus/es')['ElNotification']
+  const ElSelect: typeof import('element-plus/es')['ElSelect']
+  const ElTable: typeof import('element-plus/es')['ElTable']
+  const ElTreeSelect: typeof import('element-plus/es')['ElTreeSelect']
+  const acceptHMRUpdate: typeof import('pinia')['acceptHMRUpdate']
+  const asyncComputed: typeof import('@vueuse/core')['asyncComputed']
+  const autoResetRef: typeof import('@vueuse/core')['autoResetRef']
+  const computed: typeof import('vue')['computed']
+  const computedAsync: typeof import('@vueuse/core')['computedAsync']
+  const computedEager: typeof import('@vueuse/core')['computedEager']
+  const computedInject: typeof import('@vueuse/core')['computedInject']
+  const computedWithControl: typeof import('@vueuse/core')['computedWithControl']
+  const controlledComputed: typeof import('@vueuse/core')['controlledComputed']
+  const controlledRef: typeof import('@vueuse/core')['controlledRef']
+  const createApp: typeof import('vue')['createApp']
+  const createEventHook: typeof import('@vueuse/core')['createEventHook']
+  const createGlobalState: typeof import('@vueuse/core')['createGlobalState']
+  const createInjectionState: typeof import('@vueuse/core')['createInjectionState']
+  const createPinia: typeof import('pinia')['createPinia']
+  const createReactiveFn: typeof import('@vueuse/core')['createReactiveFn']
+  const createSharedComposable: typeof import('@vueuse/core')['createSharedComposable']
+  const createUnrefFn: typeof import('@vueuse/core')['createUnrefFn']
+  const customRef: typeof import('vue')['customRef']
+  const debouncedRef: typeof import('@vueuse/core')['debouncedRef']
+  const debouncedWatch: typeof import('@vueuse/core')['debouncedWatch']
+  const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
+  const defineComponent: typeof import('vue')['defineComponent']
+  const defineStore: typeof import('pinia')['defineStore']
+  const eagerComputed: typeof import('@vueuse/core')['eagerComputed']
+  const effectScope: typeof import('vue')['effectScope']
+  const extendRef: typeof import('@vueuse/core')['extendRef']
+  const getActivePinia: typeof import('pinia')['getActivePinia']
+  const getCurrentInstance: typeof import('vue')['getCurrentInstance']
+  const getCurrentScope: typeof import('vue')['getCurrentScope']
+  const h: typeof import('vue')['h']
+  const ignorableWatch: typeof import('@vueuse/core')['ignorableWatch']
+  const inject: typeof import('vue')['inject']
+  const isDefined: typeof import('@vueuse/core')['isDefined']
+  const isProxy: typeof import('vue')['isProxy']
+  const isReactive: typeof import('vue')['isReactive']
+  const isReadonly: typeof import('vue')['isReadonly']
+  const isRef: typeof import('vue')['isRef']
+  const makeDestructurable: typeof import('@vueuse/core')['makeDestructurable']
+  const mapActions: typeof import('pinia')['mapActions']
+  const mapGetters: typeof import('pinia')['mapGetters']
+  const mapState: typeof import('pinia')['mapState']
+  const mapStores: typeof import('pinia')['mapStores']
+  const mapWritableState: typeof import('pinia')['mapWritableState']
+  const markRaw: typeof import('vue')['markRaw']
+  const nextTick: typeof import('vue')['nextTick']
+  const onActivated: typeof import('vue')['onActivated']
+  const onBeforeMount: typeof import('vue')['onBeforeMount']
+  const onBeforeRouteLeave: typeof import('vue-router')['onBeforeRouteLeave']
+  const onBeforeRouteUpdate: typeof import('vue-router')['onBeforeRouteUpdate']
+  const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
+  const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
+  const onClickOutside: typeof import('@vueuse/core')['onClickOutside']
+  const onDeactivated: typeof import('vue')['onDeactivated']
+  const onErrorCaptured: typeof import('vue')['onErrorCaptured']
+  const onKeyStroke: typeof import('@vueuse/core')['onKeyStroke']
+  const onLongPress: typeof import('@vueuse/core')['onLongPress']
+  const onMounted: typeof import('vue')['onMounted']
+  const onRenderTracked: typeof import('vue')['onRenderTracked']
+  const onRenderTriggered: typeof import('vue')['onRenderTriggered']
+  const onScopeDispose: typeof import('vue')['onScopeDispose']
+  const onServerPrefetch: typeof import('vue')['onServerPrefetch']
+  const onStartTyping: typeof import('@vueuse/core')['onStartTyping']
+  const onUnmounted: typeof import('vue')['onUnmounted']
+  const onUpdated: typeof import('vue')['onUpdated']
+  const pausableWatch: typeof import('@vueuse/core')['pausableWatch']
+  const provide: typeof import('vue')['provide']
+  const reactify: typeof import('@vueuse/core')['reactify']
+  const reactifyObject: typeof import('@vueuse/core')['reactifyObject']
+  const reactive: typeof import('vue')['reactive']
+  const reactiveComputed: typeof import('@vueuse/core')['reactiveComputed']
+  const reactiveOmit: typeof import('@vueuse/core')['reactiveOmit']
+  const reactivePick: typeof import('@vueuse/core')['reactivePick']
+  const readonly: typeof import('vue')['readonly']
+  const ref: typeof import('vue')['ref']
+  const refAutoReset: typeof import('@vueuse/core')['refAutoReset']
+  const refDebounced: typeof import('@vueuse/core')['refDebounced']
+  const refDefault: typeof import('@vueuse/core')['refDefault']
+  const refThrottled: typeof import('@vueuse/core')['refThrottled']
+  const refWithControl: typeof import('@vueuse/core')['refWithControl']
+  const resolveComponent: typeof import('vue')['resolveComponent']
+  const resolveDirective: typeof import('vue')['resolveDirective']
+  const resolveRef: typeof import('@vueuse/core')['resolveRef']
+  const resolveUnref: typeof import('@vueuse/core')['resolveUnref']
+  const setActivePinia: typeof import('pinia')['setActivePinia']
+  const setMapStoreSuffix: typeof import('pinia')['setMapStoreSuffix']
+  const shallowReactive: typeof import('vue')['shallowReactive']
+  const shallowReadonly: typeof import('vue')['shallowReadonly']
+  const shallowRef: typeof import('vue')['shallowRef']
+  const storeToRefs: typeof import('pinia')['storeToRefs']
+  const syncRef: typeof import('@vueuse/core')['syncRef']
+  const syncRefs: typeof import('@vueuse/core')['syncRefs']
+  const templateRef: typeof import('@vueuse/core')['templateRef']
+  const throttledRef: typeof import('@vueuse/core')['throttledRef']
+  const throttledWatch: typeof import('@vueuse/core')['throttledWatch']
+  const toRaw: typeof import('vue')['toRaw']
+  const toReactive: typeof import('@vueuse/core')['toReactive']
+  const toRef: typeof import('vue')['toRef']
+  const toRefs: typeof import('vue')['toRefs']
+  const triggerRef: typeof import('vue')['triggerRef']
+  const tryOnBeforeMount: typeof import('@vueuse/core')['tryOnBeforeMount']
+  const tryOnBeforeUnmount: typeof import('@vueuse/core')['tryOnBeforeUnmount']
+  const tryOnMounted: typeof import('@vueuse/core')['tryOnMounted']
+  const tryOnScopeDispose: typeof import('@vueuse/core')['tryOnScopeDispose']
+  const tryOnUnmounted: typeof import('@vueuse/core')['tryOnUnmounted']
+  const unref: typeof import('vue')['unref']
+  const unrefElement: typeof import('@vueuse/core')['unrefElement']
+  const until: typeof import('@vueuse/core')['until']
+  const useActiveElement: typeof import('@vueuse/core')['useActiveElement']
+  const useArrayEvery: typeof import('@vueuse/core')['useArrayEvery']
+  const useArrayFilter: typeof import('@vueuse/core')['useArrayFilter']
+  const useArrayFind: typeof import('@vueuse/core')['useArrayFind']
+  const useArrayFindIndex: typeof import('@vueuse/core')['useArrayFindIndex']
+  const useArrayJoin: typeof import('@vueuse/core')['useArrayJoin']
+  const useArrayMap: typeof import('@vueuse/core')['useArrayMap']
+  const useArrayReduce: typeof import('@vueuse/core')['useArrayReduce']
+  const useArraySome: typeof import('@vueuse/core')['useArraySome']
+  const useAsyncQueue: typeof import('@vueuse/core')['useAsyncQueue']
+  const useAsyncState: typeof import('@vueuse/core')['useAsyncState']
+  const useAttrs: typeof import('vue')['useAttrs']
+  const useBase64: typeof import('@vueuse/core')['useBase64']
+  const useBattery: typeof import('@vueuse/core')['useBattery']
+  const useBluetooth: typeof import('@vueuse/core')['useBluetooth']
+  const useBreakpoints: typeof import('@vueuse/core')['useBreakpoints']
+  const useBroadcastChannel: typeof import('@vueuse/core')['useBroadcastChannel']
+  const useBrowserLocation: typeof import('@vueuse/core')['useBrowserLocation']
+  const useCached: typeof import('@vueuse/core')['useCached']
+  const useClipboard: typeof import('@vueuse/core')['useClipboard']
+  const useCloned: typeof import('@vueuse/core')['useCloned']
+  const useColorMode: typeof import('@vueuse/core')['useColorMode']
+  const useConfirmDialog: typeof import('@vueuse/core')['useConfirmDialog']
+  const useCounter: typeof import('@vueuse/core')['useCounter']
+  const useCssModule: typeof import('vue')['useCssModule']
+  const useCssVar: typeof import('@vueuse/core')['useCssVar']
+  const useCssVars: typeof import('vue')['useCssVars']
+  const useCurrentElement: typeof import('@vueuse/core')['useCurrentElement']
+  const useCycleList: typeof import('@vueuse/core')['useCycleList']
+  const useDark: typeof import('@vueuse/core')['useDark']
+  const useDateFormat: typeof import('@vueuse/core')['useDateFormat']
+  const useDebounce: typeof import('@vueuse/core')['useDebounce']
+  const useDebounceFn: typeof import('@vueuse/core')['useDebounceFn']
+  const useDebouncedRefHistory: typeof import('@vueuse/core')['useDebouncedRefHistory']
+  const useDeviceMotion: typeof import('@vueuse/core')['useDeviceMotion']
+  const useDeviceOrientation: typeof import('@vueuse/core')['useDeviceOrientation']
+  const useDevicePixelRatio: typeof import('@vueuse/core')['useDevicePixelRatio']
+  const useDevicesList: typeof import('@vueuse/core')['useDevicesList']
+  const useDisplayMedia: typeof import('@vueuse/core')['useDisplayMedia']
+  const useDocumentVisibility: typeof import('@vueuse/core')['useDocumentVisibility']
+  const useDraggable: typeof import('@vueuse/core')['useDraggable']
+  const useDropZone: typeof import('@vueuse/core')['useDropZone']
+  const useElementBounding: typeof import('@vueuse/core')['useElementBounding']
+  const useElementByPoint: typeof import('@vueuse/core')['useElementByPoint']
+  const useElementHover: typeof import('@vueuse/core')['useElementHover']
+  const useElementSize: typeof import('@vueuse/core')['useElementSize']
+  const useElementVisibility: typeof import('@vueuse/core')['useElementVisibility']
+  const useEventBus: typeof import('@vueuse/core')['useEventBus']
+  const useEventListener: typeof import('@vueuse/core')['useEventListener']
+  const useEventSource: typeof import('@vueuse/core')['useEventSource']
+  const useEyeDropper: typeof import('@vueuse/core')['useEyeDropper']
+  const useFavicon: typeof import('@vueuse/core')['useFavicon']
+  const useFetch: typeof import('@vueuse/core')['useFetch']
+  const useFileDialog: typeof import('@vueuse/core')['useFileDialog']
+  const useFileSystemAccess: typeof import('@vueuse/core')['useFileSystemAccess']
+  const useFocus: typeof import('@vueuse/core')['useFocus']
+  const useFocusWithin: typeof import('@vueuse/core')['useFocusWithin']
+  const useFps: typeof import('@vueuse/core')['useFps']
+  const useFullscreen: typeof import('@vueuse/core')['useFullscreen']
+  const useGamepad: typeof import('@vueuse/core')['useGamepad']
+  const useGeolocation: typeof import('@vueuse/core')['useGeolocation']
+  const useIdle: typeof import('@vueuse/core')['useIdle']
+  const useImage: typeof import('@vueuse/core')['useImage']
+  const useInfiniteScroll: typeof import('@vueuse/core')['useInfiniteScroll']
+  const useIntersectionObserver: typeof import('@vueuse/core')['useIntersectionObserver']
+  const useInterval: typeof import('@vueuse/core')['useInterval']
+  const useIntervalFn: typeof import('@vueuse/core')['useIntervalFn']
+  const useKeyModifier: typeof import('@vueuse/core')['useKeyModifier']
+  const useLastChanged: typeof import('@vueuse/core')['useLastChanged']
+  const useLink: typeof import('vue-router')['useLink']
+  const useLocalStorage: typeof import('@vueuse/core')['useLocalStorage']
+  const useMagicKeys: typeof import('@vueuse/core')['useMagicKeys']
+  const useManualRefHistory: typeof import('@vueuse/core')['useManualRefHistory']
+  const useMediaControls: typeof import('@vueuse/core')['useMediaControls']
+  const useMediaQuery: typeof import('@vueuse/core')['useMediaQuery']
+  const useMemoize: typeof import('@vueuse/core')['useMemoize']
+  const useMemory: typeof import('@vueuse/core')['useMemory']
+  const useMounted: typeof import('@vueuse/core')['useMounted']
+  const useMouse: typeof import('@vueuse/core')['useMouse']
+  const useMouseInElement: typeof import('@vueuse/core')['useMouseInElement']
+  const useMousePressed: typeof import('@vueuse/core')['useMousePressed']
+  const useMutationObserver: typeof import('@vueuse/core')['useMutationObserver']
+  const useNavigatorLanguage: typeof import('@vueuse/core')['useNavigatorLanguage']
+  const useNetwork: typeof import('@vueuse/core')['useNetwork']
+  const useNow: typeof import('@vueuse/core')['useNow']
+  const useObjectUrl: typeof import('@vueuse/core')['useObjectUrl']
+  const useOffsetPagination: typeof import('@vueuse/core')['useOffsetPagination']
+  const useOnline: typeof import('@vueuse/core')['useOnline']
+  const usePageLeave: typeof import('@vueuse/core')['usePageLeave']
+  const useParallax: typeof import('@vueuse/core')['useParallax']
+  const usePermission: typeof import('@vueuse/core')['usePermission']
+  const usePointer: typeof import('@vueuse/core')['usePointer']
+  const usePointerSwipe: typeof import('@vueuse/core')['usePointerSwipe']
+  const usePreferredColorScheme: typeof import('@vueuse/core')['usePreferredColorScheme']
+  const usePreferredContrast: typeof import('@vueuse/core')['usePreferredContrast']
+  const usePreferredDark: typeof import('@vueuse/core')['usePreferredDark']
+  const usePreferredLanguages: typeof import('@vueuse/core')['usePreferredLanguages']
+  const usePreferredReducedMotion: typeof import('@vueuse/core')['usePreferredReducedMotion']
+  const useRafFn: typeof import('@vueuse/core')['useRafFn']
+  const useRefHistory: typeof import('@vueuse/core')['useRefHistory']
+  const useResizeObserver: typeof import('@vueuse/core')['useResizeObserver']
+  const useRoute: typeof import('vue-router')['useRoute']
+  const useRouter: typeof import('vue-router')['useRouter']
+  const useScreenOrientation: typeof import('@vueuse/core')['useScreenOrientation']
+  const useScreenSafeArea: typeof import('@vueuse/core')['useScreenSafeArea']
+  const useScriptTag: typeof import('@vueuse/core')['useScriptTag']
+  const useScroll: typeof import('@vueuse/core')['useScroll']
+  const useScrollLock: typeof import('@vueuse/core')['useScrollLock']
+  const useSessionStorage: typeof import('@vueuse/core')['useSessionStorage']
+  const useShare: typeof import('@vueuse/core')['useShare']
+  const useSlots: typeof import('vue')['useSlots']
+  const useSorted: typeof import('@vueuse/core')['useSorted']
+  const useSpeechRecognition: typeof import('@vueuse/core')['useSpeechRecognition']
+  const useSpeechSynthesis: typeof import('@vueuse/core')['useSpeechSynthesis']
+  const useStepper: typeof import('@vueuse/core')['useStepper']
+  const useStorage: typeof import('@vueuse/core')['useStorage']
+  const useStorageAsync: typeof import('@vueuse/core')['useStorageAsync']
+  const useStyleTag: typeof import('@vueuse/core')['useStyleTag']
+  const useSupported: typeof import('@vueuse/core')['useSupported']
+  const useSwipe: typeof import('@vueuse/core')['useSwipe']
+  const useTemplateRefsList: typeof import('@vueuse/core')['useTemplateRefsList']
+  const useTextDirection: typeof import('@vueuse/core')['useTextDirection']
+  const useTextSelection: typeof import('@vueuse/core')['useTextSelection']
+  const useTextareaAutosize: typeof import('@vueuse/core')['useTextareaAutosize']
+  const useThrottle: typeof import('@vueuse/core')['useThrottle']
+  const useThrottleFn: typeof import('@vueuse/core')['useThrottleFn']
+  const useThrottledRefHistory: typeof import('@vueuse/core')['useThrottledRefHistory']
+  const useTimeAgo: typeof import('@vueuse/core')['useTimeAgo']
+  const useTimeout: typeof import('@vueuse/core')['useTimeout']
+  const useTimeoutFn: typeof import('@vueuse/core')['useTimeoutFn']
+  const useTimeoutPoll: typeof import('@vueuse/core')['useTimeoutPoll']
+  const useTimestamp: typeof import('@vueuse/core')['useTimestamp']
+  const useTitle: typeof import('@vueuse/core')['useTitle']
+  const useToNumber: typeof import('@vueuse/core')['useToNumber']
+  const useToString: typeof import('@vueuse/core')['useToString']
+  const useToggle: typeof import('@vueuse/core')['useToggle']
+  const useTransition: typeof import('@vueuse/core')['useTransition']
+  const useUrlSearchParams: typeof import('@vueuse/core')['useUrlSearchParams']
+  const useUserMedia: typeof import('@vueuse/core')['useUserMedia']
+  const useVModel: typeof import('@vueuse/core')['useVModel']
+  const useVModels: typeof import('@vueuse/core')['useVModels']
+  const useVibrate: typeof import('@vueuse/core')['useVibrate']
+  const useVirtualList: typeof import('@vueuse/core')['useVirtualList']
+  const useWakeLock: typeof import('@vueuse/core')['useWakeLock']
+  const useWebNotification: typeof import('@vueuse/core')['useWebNotification']
+  const useWebSocket: typeof import('@vueuse/core')['useWebSocket']
+  const useWebWorker: typeof import('@vueuse/core')['useWebWorker']
+  const useWebWorkerFn: typeof import('@vueuse/core')['useWebWorkerFn']
+  const useWindowFocus: typeof import('@vueuse/core')['useWindowFocus']
+  const useWindowScroll: typeof import('@vueuse/core')['useWindowScroll']
+  const useWindowSize: typeof import('@vueuse/core')['useWindowSize']
+  const watch: typeof import('vue')['watch']
+  const watchArray: typeof import('@vueuse/core')['watchArray']
+  const watchAtMost: typeof import('@vueuse/core')['watchAtMost']
+  const watchDebounced: typeof import('@vueuse/core')['watchDebounced']
+  const watchEffect: typeof import('vue')['watchEffect']
+  const watchIgnorable: typeof import('@vueuse/core')['watchIgnorable']
+  const watchOnce: typeof import('@vueuse/core')['watchOnce']
+  const watchPausable: typeof import('@vueuse/core')['watchPausable']
+  const watchPostEffect: typeof import('vue')['watchPostEffect']
+  const watchSyncEffect: typeof import('vue')['watchSyncEffect']
+  const watchThrottled: typeof import('@vueuse/core')['watchThrottled']
+  const watchTriggerable: typeof import('@vueuse/core')['watchTriggerable']
+  const watchWithFilter: typeof import('@vueuse/core')['watchWithFilter']
+  const whenever: typeof import('@vueuse/core')['whenever']
+}
+// for vue template auto import
+import { UnwrapRef } from 'vue'
+declare module 'vue' {
+  interface ComponentCustomProperties {
+    readonly EffectScope: UnwrapRef<typeof import('vue')['EffectScope']>
+    readonly ElForm: UnwrapRef<typeof import('element-plus/es')['ElForm']>
+    readonly ElLoading: UnwrapRef<typeof import('element-plus/es')['ElLoading']>
+    readonly ElMessage: UnwrapRef<typeof import('element-plus/es')['ElMessage']>
+    readonly ElMessageBox: UnwrapRef<typeof import('element-plus/es')['ElMessageBox']>
+    readonly ElNotification: UnwrapRef<typeof import('element-plus/es')['ElNotification']>
+    readonly ElSelect: UnwrapRef<typeof import('element-plus/es')['ElSelect']>
+    readonly ElTable: UnwrapRef<typeof import('element-plus/es')['ElTable']>
+    readonly ElTreeSelect: UnwrapRef<typeof import('element-plus/es')['ElTreeSelect']>
+    readonly acceptHMRUpdate: UnwrapRef<typeof import('pinia')['acceptHMRUpdate']>
+    readonly asyncComputed: UnwrapRef<typeof import('@vueuse/core')['asyncComputed']>
+    readonly autoResetRef: UnwrapRef<typeof import('@vueuse/core')['autoResetRef']>
+    readonly computed: UnwrapRef<typeof import('vue')['computed']>
+    readonly computedAsync: UnwrapRef<typeof import('@vueuse/core')['computedAsync']>
+    readonly computedEager: UnwrapRef<typeof import('@vueuse/core')['computedEager']>
+    readonly computedInject: UnwrapRef<typeof import('@vueuse/core')['computedInject']>
+    readonly computedWithControl: UnwrapRef<typeof import('@vueuse/core')['computedWithControl']>
+    readonly controlledComputed: UnwrapRef<typeof import('@vueuse/core')['controlledComputed']>
+    readonly controlledRef: UnwrapRef<typeof import('@vueuse/core')['controlledRef']>
+    readonly createApp: UnwrapRef<typeof import('vue')['createApp']>
+    readonly createEventHook: UnwrapRef<typeof import('@vueuse/core')['createEventHook']>
+    readonly createGlobalState: UnwrapRef<typeof import('@vueuse/core')['createGlobalState']>
+    readonly createInjectionState: UnwrapRef<typeof import('@vueuse/core')['createInjectionState']>
+    readonly createPinia: UnwrapRef<typeof import('pinia')['createPinia']>
+    readonly createReactiveFn: UnwrapRef<typeof import('@vueuse/core')['createReactiveFn']>
+    readonly createSharedComposable: UnwrapRef<typeof import('@vueuse/core')['createSharedComposable']>
+    readonly createUnrefFn: UnwrapRef<typeof import('@vueuse/core')['createUnrefFn']>
+    readonly customRef: UnwrapRef<typeof import('vue')['customRef']>
+    readonly debouncedRef: UnwrapRef<typeof import('@vueuse/core')['debouncedRef']>
+    readonly debouncedWatch: UnwrapRef<typeof import('@vueuse/core')['debouncedWatch']>
+    readonly defineAsyncComponent: UnwrapRef<typeof import('vue')['defineAsyncComponent']>
+    readonly defineComponent: UnwrapRef<typeof import('vue')['defineComponent']>
+    readonly defineStore: UnwrapRef<typeof import('pinia')['defineStore']>
+    readonly eagerComputed: UnwrapRef<typeof import('@vueuse/core')['eagerComputed']>
+    readonly effectScope: UnwrapRef<typeof import('vue')['effectScope']>
+    readonly extendRef: UnwrapRef<typeof import('@vueuse/core')['extendRef']>
+    readonly getActivePinia: UnwrapRef<typeof import('pinia')['getActivePinia']>
+    readonly getCurrentInstance: UnwrapRef<typeof import('vue')['getCurrentInstance']>
+    readonly getCurrentScope: UnwrapRef<typeof import('vue')['getCurrentScope']>
+    readonly h: UnwrapRef<typeof import('vue')['h']>
+    readonly ignorableWatch: UnwrapRef<typeof import('@vueuse/core')['ignorableWatch']>
+    readonly inject: UnwrapRef<typeof import('vue')['inject']>
+    readonly isDefined: UnwrapRef<typeof import('@vueuse/core')['isDefined']>
+    readonly isProxy: UnwrapRef<typeof import('vue')['isProxy']>
+    readonly isReactive: UnwrapRef<typeof import('vue')['isReactive']>
+    readonly isReadonly: UnwrapRef<typeof import('vue')['isReadonly']>
+    readonly isRef: UnwrapRef<typeof import('vue')['isRef']>
+    readonly makeDestructurable: UnwrapRef<typeof import('@vueuse/core')['makeDestructurable']>
+    readonly mapActions: UnwrapRef<typeof import('pinia')['mapActions']>
+    readonly mapGetters: UnwrapRef<typeof import('pinia')['mapGetters']>
+    readonly mapState: UnwrapRef<typeof import('pinia')['mapState']>
+    readonly mapStores: UnwrapRef<typeof import('pinia')['mapStores']>
+    readonly mapWritableState: UnwrapRef<typeof import('pinia')['mapWritableState']>
+    readonly markRaw: UnwrapRef<typeof import('vue')['markRaw']>
+    readonly nextTick: UnwrapRef<typeof import('vue')['nextTick']>
+    readonly onActivated: UnwrapRef<typeof import('vue')['onActivated']>
+    readonly onBeforeMount: UnwrapRef<typeof import('vue')['onBeforeMount']>
+    readonly onBeforeRouteLeave: UnwrapRef<typeof import('vue-router')['onBeforeRouteLeave']>
+    readonly onBeforeRouteUpdate: UnwrapRef<typeof import('vue-router')['onBeforeRouteUpdate']>
+    readonly onBeforeUnmount: UnwrapRef<typeof import('vue')['onBeforeUnmount']>
+    readonly onBeforeUpdate: UnwrapRef<typeof import('vue')['onBeforeUpdate']>
+    readonly onClickOutside: UnwrapRef<typeof import('@vueuse/core')['onClickOutside']>
+    readonly onDeactivated: UnwrapRef<typeof import('vue')['onDeactivated']>
+    readonly onErrorCaptured: UnwrapRef<typeof import('vue')['onErrorCaptured']>
+    readonly onKeyStroke: UnwrapRef<typeof import('@vueuse/core')['onKeyStroke']>
+    readonly onLongPress: UnwrapRef<typeof import('@vueuse/core')['onLongPress']>
+    readonly onMounted: UnwrapRef<typeof import('vue')['onMounted']>
+    readonly onRenderTracked: UnwrapRef<typeof import('vue')['onRenderTracked']>
+    readonly onRenderTriggered: UnwrapRef<typeof import('vue')['onRenderTriggered']>
+    readonly onScopeDispose: UnwrapRef<typeof import('vue')['onScopeDispose']>
+    readonly onServerPrefetch: UnwrapRef<typeof import('vue')['onServerPrefetch']>
+    readonly onStartTyping: UnwrapRef<typeof import('@vueuse/core')['onStartTyping']>
+    readonly onUnmounted: UnwrapRef<typeof import('vue')['onUnmounted']>
+    readonly onUpdated: UnwrapRef<typeof import('vue')['onUpdated']>
+    readonly pausableWatch: UnwrapRef<typeof import('@vueuse/core')['pausableWatch']>
+    readonly provide: UnwrapRef<typeof import('vue')['provide']>
+    readonly reactify: UnwrapRef<typeof import('@vueuse/core')['reactify']>
+    readonly reactifyObject: UnwrapRef<typeof import('@vueuse/core')['reactifyObject']>
+    readonly reactive: UnwrapRef<typeof import('vue')['reactive']>
+    readonly reactiveComputed: UnwrapRef<typeof import('@vueuse/core')['reactiveComputed']>
+    readonly reactiveOmit: UnwrapRef<typeof import('@vueuse/core')['reactiveOmit']>
+    readonly reactivePick: UnwrapRef<typeof import('@vueuse/core')['reactivePick']>
+    readonly readonly: UnwrapRef<typeof import('vue')['readonly']>
+    readonly ref: UnwrapRef<typeof import('vue')['ref']>
+    readonly refAutoReset: UnwrapRef<typeof import('@vueuse/core')['refAutoReset']>
+    readonly refDebounced: UnwrapRef<typeof import('@vueuse/core')['refDebounced']>
+    readonly refDefault: UnwrapRef<typeof import('@vueuse/core')['refDefault']>
+    readonly refThrottled: UnwrapRef<typeof import('@vueuse/core')['refThrottled']>
+    readonly refWithControl: UnwrapRef<typeof import('@vueuse/core')['refWithControl']>
+    readonly resolveComponent: UnwrapRef<typeof import('vue')['resolveComponent']>
+    readonly resolveDirective: UnwrapRef<typeof import('vue')['resolveDirective']>
+    readonly resolveRef: UnwrapRef<typeof import('@vueuse/core')['resolveRef']>
+    readonly resolveUnref: UnwrapRef<typeof import('@vueuse/core')['resolveUnref']>
+    readonly setActivePinia: UnwrapRef<typeof import('pinia')['setActivePinia']>
+    readonly setMapStoreSuffix: UnwrapRef<typeof import('pinia')['setMapStoreSuffix']>
+    readonly shallowReactive: UnwrapRef<typeof import('vue')['shallowReactive']>
+    readonly shallowReadonly: UnwrapRef<typeof import('vue')['shallowReadonly']>
+    readonly shallowRef: UnwrapRef<typeof import('vue')['shallowRef']>
+    readonly storeToRefs: UnwrapRef<typeof import('pinia')['storeToRefs']>
+    readonly syncRef: UnwrapRef<typeof import('@vueuse/core')['syncRef']>
+    readonly syncRefs: UnwrapRef<typeof import('@vueuse/core')['syncRefs']>
+    readonly templateRef: UnwrapRef<typeof import('@vueuse/core')['templateRef']>
+    readonly throttledRef: UnwrapRef<typeof import('@vueuse/core')['throttledRef']>
+    readonly throttledWatch: UnwrapRef<typeof import('@vueuse/core')['throttledWatch']>
+    readonly toRaw: UnwrapRef<typeof import('vue')['toRaw']>
+    readonly toReactive: UnwrapRef<typeof import('@vueuse/core')['toReactive']>
+    readonly toRef: UnwrapRef<typeof import('vue')['toRef']>
+    readonly toRefs: UnwrapRef<typeof import('vue')['toRefs']>
+    readonly triggerRef: UnwrapRef<typeof import('vue')['triggerRef']>
+    readonly tryOnBeforeMount: UnwrapRef<typeof import('@vueuse/core')['tryOnBeforeMount']>
+    readonly tryOnBeforeUnmount: UnwrapRef<typeof import('@vueuse/core')['tryOnBeforeUnmount']>
+    readonly tryOnMounted: UnwrapRef<typeof import('@vueuse/core')['tryOnMounted']>
+    readonly tryOnScopeDispose: UnwrapRef<typeof import('@vueuse/core')['tryOnScopeDispose']>
+    readonly tryOnUnmounted: UnwrapRef<typeof import('@vueuse/core')['tryOnUnmounted']>
+    readonly unref: UnwrapRef<typeof import('vue')['unref']>
+    readonly unrefElement: UnwrapRef<typeof import('@vueuse/core')['unrefElement']>
+    readonly until: UnwrapRef<typeof import('@vueuse/core')['until']>
+    readonly useActiveElement: UnwrapRef<typeof import('@vueuse/core')['useActiveElement']>
+    readonly useArrayEvery: UnwrapRef<typeof import('@vueuse/core')['useArrayEvery']>
+    readonly useArrayFilter: UnwrapRef<typeof import('@vueuse/core')['useArrayFilter']>
+    readonly useArrayFind: UnwrapRef<typeof import('@vueuse/core')['useArrayFind']>
+    readonly useArrayFindIndex: UnwrapRef<typeof import('@vueuse/core')['useArrayFindIndex']>
+    readonly useArrayJoin: UnwrapRef<typeof import('@vueuse/core')['useArrayJoin']>
+    readonly useArrayMap: UnwrapRef<typeof import('@vueuse/core')['useArrayMap']>
+    readonly useArrayReduce: UnwrapRef<typeof import('@vueuse/core')['useArrayReduce']>
+    readonly useArraySome: UnwrapRef<typeof import('@vueuse/core')['useArraySome']>
+    readonly useAsyncQueue: UnwrapRef<typeof import('@vueuse/core')['useAsyncQueue']>
+    readonly useAsyncState: UnwrapRef<typeof import('@vueuse/core')['useAsyncState']>
+    readonly useAttrs: UnwrapRef<typeof import('vue')['useAttrs']>
+    readonly useBase64: UnwrapRef<typeof import('@vueuse/core')['useBase64']>
+    readonly useBattery: UnwrapRef<typeof import('@vueuse/core')['useBattery']>
+    readonly useBluetooth: UnwrapRef<typeof import('@vueuse/core')['useBluetooth']>
+    readonly useBreakpoints: UnwrapRef<typeof import('@vueuse/core')['useBreakpoints']>
+    readonly useBroadcastChannel: UnwrapRef<typeof import('@vueuse/core')['useBroadcastChannel']>
+    readonly useBrowserLocation: UnwrapRef<typeof import('@vueuse/core')['useBrowserLocation']>
+    readonly useCached: UnwrapRef<typeof import('@vueuse/core')['useCached']>
+    readonly useClipboard: UnwrapRef<typeof import('@vueuse/core')['useClipboard']>
+    readonly useCloned: UnwrapRef<typeof import('@vueuse/core')['useCloned']>
+    readonly useColorMode: UnwrapRef<typeof import('@vueuse/core')['useColorMode']>
+    readonly useConfirmDialog: UnwrapRef<typeof import('@vueuse/core')['useConfirmDialog']>
+    readonly useCounter: UnwrapRef<typeof import('@vueuse/core')['useCounter']>
+    readonly useCssModule: UnwrapRef<typeof import('vue')['useCssModule']>
+    readonly useCssVar: UnwrapRef<typeof import('@vueuse/core')['useCssVar']>
+    readonly useCssVars: UnwrapRef<typeof import('vue')['useCssVars']>
+    readonly useCurrentElement: UnwrapRef<typeof import('@vueuse/core')['useCurrentElement']>
+    readonly useCycleList: UnwrapRef<typeof import('@vueuse/core')['useCycleList']>
+    readonly useDark: UnwrapRef<typeof import('@vueuse/core')['useDark']>
+    readonly useDateFormat: UnwrapRef<typeof import('@vueuse/core')['useDateFormat']>
+    readonly useDebounce: UnwrapRef<typeof import('@vueuse/core')['useDebounce']>
+    readonly useDebounceFn: UnwrapRef<typeof import('@vueuse/core')['useDebounceFn']>
+    readonly useDebouncedRefHistory: UnwrapRef<typeof import('@vueuse/core')['useDebouncedRefHistory']>
+    readonly useDeviceMotion: UnwrapRef<typeof import('@vueuse/core')['useDeviceMotion']>
+    readonly useDeviceOrientation: UnwrapRef<typeof import('@vueuse/core')['useDeviceOrientation']>
+    readonly useDevicePixelRatio: UnwrapRef<typeof import('@vueuse/core')['useDevicePixelRatio']>
+    readonly useDevicesList: UnwrapRef<typeof import('@vueuse/core')['useDevicesList']>
+    readonly useDisplayMedia: UnwrapRef<typeof import('@vueuse/core')['useDisplayMedia']>
+    readonly useDocumentVisibility: UnwrapRef<typeof import('@vueuse/core')['useDocumentVisibility']>
+    readonly useDraggable: UnwrapRef<typeof import('@vueuse/core')['useDraggable']>
+    readonly useDropZone: UnwrapRef<typeof import('@vueuse/core')['useDropZone']>
+    readonly useElementBounding: UnwrapRef<typeof import('@vueuse/core')['useElementBounding']>
+    readonly useElementByPoint: UnwrapRef<typeof import('@vueuse/core')['useElementByPoint']>
+    readonly useElementHover: UnwrapRef<typeof import('@vueuse/core')['useElementHover']>
+    readonly useElementSize: UnwrapRef<typeof import('@vueuse/core')['useElementSize']>
+    readonly useElementVisibility: UnwrapRef<typeof import('@vueuse/core')['useElementVisibility']>
+    readonly useEventBus: UnwrapRef<typeof import('@vueuse/core')['useEventBus']>
+    readonly useEventListener: UnwrapRef<typeof import('@vueuse/core')['useEventListener']>
+    readonly useEventSource: UnwrapRef<typeof import('@vueuse/core')['useEventSource']>
+    readonly useEyeDropper: UnwrapRef<typeof import('@vueuse/core')['useEyeDropper']>
+    readonly useFavicon: UnwrapRef<typeof import('@vueuse/core')['useFavicon']>
+    readonly useFetch: UnwrapRef<typeof import('@vueuse/core')['useFetch']>
+    readonly useFileDialog: UnwrapRef<typeof import('@vueuse/core')['useFileDialog']>
+    readonly useFileSystemAccess: UnwrapRef<typeof import('@vueuse/core')['useFileSystemAccess']>
+    readonly useFocus: UnwrapRef<typeof import('@vueuse/core')['useFocus']>
+    readonly useFocusWithin: UnwrapRef<typeof import('@vueuse/core')['useFocusWithin']>
+    readonly useFps: UnwrapRef<typeof import('@vueuse/core')['useFps']>
+    readonly useFullscreen: UnwrapRef<typeof import('@vueuse/core')['useFullscreen']>
+    readonly useGamepad: UnwrapRef<typeof import('@vueuse/core')['useGamepad']>
+    readonly useGeolocation: UnwrapRef<typeof import('@vueuse/core')['useGeolocation']>
+    readonly useIdle: UnwrapRef<typeof import('@vueuse/core')['useIdle']>
+    readonly useImage: UnwrapRef<typeof import('@vueuse/core')['useImage']>
+    readonly useInfiniteScroll: UnwrapRef<typeof import('@vueuse/core')['useInfiniteScroll']>
+    readonly useIntersectionObserver: UnwrapRef<typeof import('@vueuse/core')['useIntersectionObserver']>
+    readonly useInterval: UnwrapRef<typeof import('@vueuse/core')['useInterval']>
+    readonly useIntervalFn: UnwrapRef<typeof import('@vueuse/core')['useIntervalFn']>
+    readonly useKeyModifier: UnwrapRef<typeof import('@vueuse/core')['useKeyModifier']>
+    readonly useLastChanged: UnwrapRef<typeof import('@vueuse/core')['useLastChanged']>
+    readonly useLink: UnwrapRef<typeof import('vue-router')['useLink']>
+    readonly useLocalStorage: UnwrapRef<typeof import('@vueuse/core')['useLocalStorage']>
+    readonly useMagicKeys: UnwrapRef<typeof import('@vueuse/core')['useMagicKeys']>
+    readonly useManualRefHistory: UnwrapRef<typeof import('@vueuse/core')['useManualRefHistory']>
+    readonly useMediaControls: UnwrapRef<typeof import('@vueuse/core')['useMediaControls']>
+    readonly useMediaQuery: UnwrapRef<typeof import('@vueuse/core')['useMediaQuery']>
+    readonly useMemoize: UnwrapRef<typeof import('@vueuse/core')['useMemoize']>
+    readonly useMemory: UnwrapRef<typeof import('@vueuse/core')['useMemory']>
+    readonly useMounted: UnwrapRef<typeof import('@vueuse/core')['useMounted']>
+    readonly useMouse: UnwrapRef<typeof import('@vueuse/core')['useMouse']>
+    readonly useMouseInElement: UnwrapRef<typeof import('@vueuse/core')['useMouseInElement']>
+    readonly useMousePressed: UnwrapRef<typeof import('@vueuse/core')['useMousePressed']>
+    readonly useMutationObserver: UnwrapRef<typeof import('@vueuse/core')['useMutationObserver']>
+    readonly useNavigatorLanguage: UnwrapRef<typeof import('@vueuse/core')['useNavigatorLanguage']>
+    readonly useNetwork: UnwrapRef<typeof import('@vueuse/core')['useNetwork']>
+    readonly useNow: UnwrapRef<typeof import('@vueuse/core')['useNow']>
+    readonly useObjectUrl: UnwrapRef<typeof import('@vueuse/core')['useObjectUrl']>
+    readonly useOffsetPagination: UnwrapRef<typeof import('@vueuse/core')['useOffsetPagination']>
+    readonly useOnline: UnwrapRef<typeof import('@vueuse/core')['useOnline']>
+    readonly usePageLeave: UnwrapRef<typeof import('@vueuse/core')['usePageLeave']>
+    readonly useParallax: UnwrapRef<typeof import('@vueuse/core')['useParallax']>
+    readonly usePermission: UnwrapRef<typeof import('@vueuse/core')['usePermission']>
+    readonly usePointer: UnwrapRef<typeof import('@vueuse/core')['usePointer']>
+    readonly usePointerSwipe: UnwrapRef<typeof import('@vueuse/core')['usePointerSwipe']>
+    readonly usePreferredColorScheme: UnwrapRef<typeof import('@vueuse/core')['usePreferredColorScheme']>
+    readonly usePreferredContrast: UnwrapRef<typeof import('@vueuse/core')['usePreferredContrast']>
+    readonly usePreferredDark: UnwrapRef<typeof import('@vueuse/core')['usePreferredDark']>
+    readonly usePreferredLanguages: UnwrapRef<typeof import('@vueuse/core')['usePreferredLanguages']>
+    readonly usePreferredReducedMotion: UnwrapRef<typeof import('@vueuse/core')['usePreferredReducedMotion']>
+    readonly useRafFn: UnwrapRef<typeof import('@vueuse/core')['useRafFn']>
+    readonly useRefHistory: UnwrapRef<typeof import('@vueuse/core')['useRefHistory']>
+    readonly useResizeObserver: UnwrapRef<typeof import('@vueuse/core')['useResizeObserver']>
+    readonly useRoute: UnwrapRef<typeof import('vue-router')['useRoute']>
+    readonly useRouter: UnwrapRef<typeof import('vue-router')['useRouter']>
+    readonly useScreenOrientation: UnwrapRef<typeof import('@vueuse/core')['useScreenOrientation']>
+    readonly useScreenSafeArea: UnwrapRef<typeof import('@vueuse/core')['useScreenSafeArea']>
+    readonly useScriptTag: UnwrapRef<typeof import('@vueuse/core')['useScriptTag']>
+    readonly useScroll: UnwrapRef<typeof import('@vueuse/core')['useScroll']>
+    readonly useScrollLock: UnwrapRef<typeof import('@vueuse/core')['useScrollLock']>
+    readonly useSessionStorage: UnwrapRef<typeof import('@vueuse/core')['useSessionStorage']>
+    readonly useShare: UnwrapRef<typeof import('@vueuse/core')['useShare']>
+    readonly useSlots: UnwrapRef<typeof import('vue')['useSlots']>
+    readonly useSorted: UnwrapRef<typeof import('@vueuse/core')['useSorted']>
+    readonly useSpeechRecognition: UnwrapRef<typeof import('@vueuse/core')['useSpeechRecognition']>
+    readonly useSpeechSynthesis: UnwrapRef<typeof import('@vueuse/core')['useSpeechSynthesis']>
+    readonly useStepper: UnwrapRef<typeof import('@vueuse/core')['useStepper']>
+    readonly useStorage: UnwrapRef<typeof import('@vueuse/core')['useStorage']>
+    readonly useStorageAsync: UnwrapRef<typeof import('@vueuse/core')['useStorageAsync']>
+    readonly useStyleTag: UnwrapRef<typeof import('@vueuse/core')['useStyleTag']>
+    readonly useSupported: UnwrapRef<typeof import('@vueuse/core')['useSupported']>
+    readonly useSwipe: UnwrapRef<typeof import('@vueuse/core')['useSwipe']>
+    readonly useTemplateRefsList: UnwrapRef<typeof import('@vueuse/core')['useTemplateRefsList']>
+    readonly useTextDirection: UnwrapRef<typeof import('@vueuse/core')['useTextDirection']>
+    readonly useTextSelection: UnwrapRef<typeof import('@vueuse/core')['useTextSelection']>
+    readonly useTextareaAutosize: UnwrapRef<typeof import('@vueuse/core')['useTextareaAutosize']>
+    readonly useThrottle: UnwrapRef<typeof import('@vueuse/core')['useThrottle']>
+    readonly useThrottleFn: UnwrapRef<typeof import('@vueuse/core')['useThrottleFn']>
+    readonly useThrottledRefHistory: UnwrapRef<typeof import('@vueuse/core')['useThrottledRefHistory']>
+    readonly useTimeAgo: UnwrapRef<typeof import('@vueuse/core')['useTimeAgo']>
+    readonly useTimeout: UnwrapRef<typeof import('@vueuse/core')['useTimeout']>
+    readonly useTimeoutFn: UnwrapRef<typeof import('@vueuse/core')['useTimeoutFn']>
+    readonly useTimeoutPoll: UnwrapRef<typeof import('@vueuse/core')['useTimeoutPoll']>
+    readonly useTimestamp: UnwrapRef<typeof import('@vueuse/core')['useTimestamp']>
+    readonly useTitle: UnwrapRef<typeof import('@vueuse/core')['useTitle']>
+    readonly useToNumber: UnwrapRef<typeof import('@vueuse/core')['useToNumber']>
+    readonly useToString: UnwrapRef<typeof import('@vueuse/core')['useToString']>
+    readonly useToggle: UnwrapRef<typeof import('@vueuse/core')['useToggle']>
+    readonly useTransition: UnwrapRef<typeof import('@vueuse/core')['useTransition']>
+    readonly useUrlSearchParams: UnwrapRef<typeof import('@vueuse/core')['useUrlSearchParams']>
+    readonly useUserMedia: UnwrapRef<typeof import('@vueuse/core')['useUserMedia']>
+    readonly useVModel: UnwrapRef<typeof import('@vueuse/core')['useVModel']>
+    readonly useVModels: UnwrapRef<typeof import('@vueuse/core')['useVModels']>
+    readonly useVibrate: UnwrapRef<typeof import('@vueuse/core')['useVibrate']>
+    readonly useVirtualList: UnwrapRef<typeof import('@vueuse/core')['useVirtualList']>
+    readonly useWakeLock: UnwrapRef<typeof import('@vueuse/core')['useWakeLock']>
+    readonly useWebNotification: UnwrapRef<typeof import('@vueuse/core')['useWebNotification']>
+    readonly useWebSocket: UnwrapRef<typeof import('@vueuse/core')['useWebSocket']>
+    readonly useWebWorker: UnwrapRef<typeof import('@vueuse/core')['useWebWorker']>
+    readonly useWebWorkerFn: UnwrapRef<typeof import('@vueuse/core')['useWebWorkerFn']>
+    readonly useWindowFocus: UnwrapRef<typeof import('@vueuse/core')['useWindowFocus']>
+    readonly useWindowScroll: UnwrapRef<typeof import('@vueuse/core')['useWindowScroll']>
+    readonly useWindowSize: UnwrapRef<typeof import('@vueuse/core')['useWindowSize']>
+    readonly watch: UnwrapRef<typeof import('vue')['watch']>
+    readonly watchArray: UnwrapRef<typeof import('@vueuse/core')['watchArray']>
+    readonly watchAtMost: UnwrapRef<typeof import('@vueuse/core')['watchAtMost']>
+    readonly watchDebounced: UnwrapRef<typeof import('@vueuse/core')['watchDebounced']>
+    readonly watchEffect: UnwrapRef<typeof import('vue')['watchEffect']>
+    readonly watchIgnorable: UnwrapRef<typeof import('@vueuse/core')['watchIgnorable']>
+    readonly watchOnce: UnwrapRef<typeof import('@vueuse/core')['watchOnce']>
+    readonly watchPausable: UnwrapRef<typeof import('@vueuse/core')['watchPausable']>
+    readonly watchPostEffect: UnwrapRef<typeof import('vue')['watchPostEffect']>
+    readonly watchSyncEffect: UnwrapRef<typeof import('vue')['watchSyncEffect']>
+    readonly watchThrottled: UnwrapRef<typeof import('@vueuse/core')['watchThrottled']>
+    readonly watchTriggerable: UnwrapRef<typeof import('@vueuse/core')['watchTriggerable']>
+    readonly watchWithFilter: UnwrapRef<typeof import('@vueuse/core')['watchWithFilter']>
+    readonly whenever: UnwrapRef<typeof import('@vueuse/core')['whenever']>
+  }
+}
diff --git a/src/types/axios.d.ts b/src/types/axios.d.ts
new file mode 100644
index 0000000..54237d4
--- /dev/null
+++ b/src/types/axios.d.ts
@@ -0,0 +1,10 @@
+import axios from 'axios';
+
+declare module 'axios' {
+	export interface AxiosResponse<T = any> {
+		code: number;
+		msg: string;
+		rows: T;
+		total: number;
+	}
+}
diff --git a/src/types/components.d.ts b/src/types/components.d.ts
new file mode 100644
index 0000000..1c855e4
--- /dev/null
+++ b/src/types/components.d.ts
@@ -0,0 +1,82 @@
+// generated by unplugin-vue-components
+// We suggest you to commit this file into source control
+// Read more: https://github.com/vuejs/core/pull/3399
+import '@vue/runtime-core'
+
+export {}
+
+declare module '@vue/runtime-core' {
+  export interface GlobalComponents {
+    Breadcrumb: typeof import('./../components/Breadcrumb/index.vue')['default']
+    DictTag: typeof import('./../components/DictTag/index.vue')['default']
+    Editor: typeof import('./../components/Editor/index.vue')['default']
+    ElBreadcrumb: typeof import('element-plus/es')['ElBreadcrumb']
+    ElBreadcrumbItem: typeof import('element-plus/es')['ElBreadcrumbItem']
+    ElButton: typeof import('element-plus/es')['ElButton']
+    ElCard: typeof import('element-plus/es')['ElCard']
+    ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
+    ElCol: typeof import('element-plus/es')['ElCol']
+    ElColorPicker: typeof import('element-plus/es')['ElColorPicker']
+    ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
+    ElDialog: typeof import('element-plus/es')['ElDialog']
+    ElDivider: typeof import('element-plus/es')['ElDivider']
+    ElDrawer: typeof import('element-plus/es')['ElDrawer']
+    ElDropdown: typeof import('element-plus/es')['ElDropdown']
+    ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
+    ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
+    ElForm: typeof import('element-plus/es')['ElForm']
+    ElFormItem: typeof import('element-plus/es')['ElFormItem']
+    ElIcon: typeof import('element-plus/es')['ElIcon']
+    ElImage: typeof import('element-plus/es')['ElImage']
+    ElInput: typeof import('element-plus/es')['ElInput']
+    ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
+    ElLink: typeof import('element-plus/es')['ElLink']
+    ElMenu: typeof import('element-plus/es')['ElMenu']
+    ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
+    ElOption: typeof import('element-plus/es')['ElOption']
+    ElPagination: typeof import('element-plus/es')['ElPagination']
+    ElPopover: typeof import('element-plus/es')['ElPopover']
+    ElRadio: typeof import('element-plus/es')['ElRadio']
+    ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
+    ElRow: typeof import('element-plus/es')['ElRow']
+    ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
+    ElSelect: typeof import('element-plus/es')['ElSelect']
+    ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
+    ElSwitch: typeof import('element-plus/es')['ElSwitch']
+    ElTable: typeof import('element-plus/es')['ElTable']
+    ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
+    ElTabPane: typeof import('element-plus/es')['ElTabPane']
+    ElTabs: typeof import('element-plus/es')['ElTabs']
+    ElTag: typeof import('element-plus/es')['ElTag']
+    ElTooltip: typeof import('element-plus/es')['ElTooltip']
+    ElTransfer: typeof import('element-plus/es')['ElTransfer']
+    ElTree: typeof import('element-plus/es')['ElTree']
+    ElTreeSelect: typeof import('element-plus/es')['ElTreeSelect']
+    ElUpload: typeof import('element-plus/es')['ElUpload']
+    FileUpload: typeof import('./../components/FileUpload/index.vue')['default']
+    Hamburger: typeof import('./../components/Hamburger/index.vue')['default']
+    HeaderSearch: typeof import('./../components/HeaderSearch/index.vue')['default']
+    IconSelect: typeof import('./../components/IconSelect/index.vue')['default']
+    IEpCaretBottom: typeof import('~icons/ep/caret-bottom')['default']
+    IEpCaretTop: typeof import('~icons/ep/caret-top')['default']
+    IEpUploadFilled: typeof import('~icons/ep/upload-filled')['default']
+    IFrame: typeof import('./../components/iFrame/index.vue')['default']
+    ImagePreview: typeof import('./../components/ImagePreview/index.vue')['default']
+    ImageUpload: typeof import('./../components/ImageUpload/index.vue')['default']
+    Pagination: typeof import('./../components/Pagination/index.vue')['default']
+    ParentView: typeof import('./../components/ParentView/index.vue')['default']
+    RightToolbar: typeof import('./../components/RightToolbar/index.vue')['default']
+    RouterLink: typeof import('vue-router')['RouterLink']
+    RouterView: typeof import('vue-router')['RouterView']
+    RuoYiDoc: typeof import('./../components/RuoYiDoc/index.vue')['default']
+    RuoYiGit: typeof import('./../components/RuoYiGit/index.vue')['default']
+    Screenfull: typeof import('./../components/Screenfull/index.vue')['default']
+    SizeSelect: typeof import('./../components/SizeSelect/index.vue')['default']
+    SvgIcon: typeof import('./../components/SvgIcon/index.vue')['default']
+    TopNav: typeof import('./../components/TopNav/index.vue')['default']
+    TreeSelect: typeof import('./../components/TreeSelect/index.vue')['default']
+  }
+  export interface ComponentCustomProperties {
+    vLoading: typeof import('element-plus/es')['ElLoadingDirective']
+  }
+}
diff --git a/src/types/element.d.ts b/src/types/element.d.ts
new file mode 100644
index 0000000..8632484
--- /dev/null
+++ b/src/types/element.d.ts
@@ -0,0 +1 @@
+declare type ElTagType = '' | 'success' | 'warning' | 'info' | 'danger' | 'default' | 'primary';
diff --git a/src/types/env.d.ts b/src/types/env.d.ts
new file mode 100644
index 0000000..33b3450
--- /dev/null
+++ b/src/types/env.d.ts
@@ -0,0 +1,74 @@
+declare module '*.vue' {
+	import { DefineComponent } from 'vue';
+	const component: DefineComponent<{}, {}, any>;
+	export default component;
+}
+declare module '*.avif' {
+	const src: string;
+	export default src;
+}
+
+declare module '*.bmp' {
+	const src: string;
+	export default src;
+}
+
+declare module '*.gif' {
+	const src: string;
+	export default src;
+}
+
+declare module '*.jpg' {
+	const src: string;
+	export default src;
+}
+
+declare module '*.jpeg' {
+	const src: string;
+	export default src;
+}
+
+declare module '*.png' {
+	const src: string;
+	export default src;
+}
+
+declare module '*.webp' {
+	const src: string;
+	export default src;
+}
+
+declare module '*.svg' {
+	const src: string;
+	export default src;
+}
+
+declare module '*.module.css' {
+	const classes: { readonly [key: string]: string };
+	export default classes;
+}
+
+declare module '*.module.scss' {
+	const classes: { readonly [key: string]: string };
+	export default classes;
+}
+
+declare module '*.module.sass' {
+	const classes: { readonly [key: string]: string };
+	export default classes;
+}
+// 鐜鍙橀噺
+interface ImportMetaEnv {
+	VITE_APP_TITLE: string;
+	VITE_APP_PORT: number;
+	VITE_APP_BASE_API: string;
+	VITE_APP_BASE_URL: string;
+	VITE_APP_CONTEXT_PATH: string;
+	VITE_APP_MONITRO_ADMIN: string;
+	VITE_APP_XXL_JOB_ADMIN: string;
+	VITE_APP_ENV: string;
+}
+interface ImportMeta {
+	readonly env: ImportMetaEnv;
+	// readonly glob: any;
+}
diff --git a/src/types/global.d.ts b/src/types/global.d.ts
new file mode 100644
index 0000000..81bc816
--- /dev/null
+++ b/src/types/global.d.ts
@@ -0,0 +1,83 @@
+import { FormRules } from 'element-plus';
+declare global {
+	/**
+	 * 鐣岄潰瀛楁闅愯棌灞炴��
+	 */
+	interface FieldOption {
+		key: number;
+		label: string;
+		visible: boolean;
+	}
+
+	/**
+	 * 寮圭獥灞炴��
+	 */
+	interface DialogOption {
+		/**
+		 * 寮圭獥鏍囬
+		 */
+		title?: string;
+		/**
+		 * 鏄惁鏄剧ず
+		 */
+		visible: boolean;
+	}
+
+	interface UploadOption {
+		/** 璁剧疆涓婁紶鐨勮姹傚ご閮� */
+		headers: { [key: string]: any };
+
+		/** 涓婁紶鐨勫湴鍧� */
+		url: string;
+	}
+
+	/**
+	 * 瀵煎叆灞炴��
+	 */
+	interface ImportOption extends UploadOption {
+		/** 鏄惁鏄剧ず寮瑰嚭灞� */
+		open: boolean;
+		/** 寮瑰嚭灞傛爣棰� */
+		title: string;
+		/** 鏄惁绂佺敤涓婁紶 */
+		isUploading: boolean;
+
+		/** 鍏朵粬鍙傛暟 */
+		[key: string]: any;
+	}
+	/**
+	 * 瀛楀吀鏁版嵁  鏁版嵁閰嶇疆
+	 */
+	interface DictDataOption {
+		label: string;
+		value: string;
+		elTagType?: ElTagType;
+		elTagClass?: string;
+	}
+
+	interface BaseEntity {
+		createBy?: any;
+		createTime?: string;
+		updateBy?: any;
+		updateTime?: any;
+	}
+
+	/**
+	 * 鍒嗛〉鏁版嵁
+	 * T : 琛ㄥ崟鏁版嵁
+	 * D : 鏌ヨ鍙傛暟
+	 */
+	interface PageData<T, D> {
+		form: T;
+		queryParams: D;
+		rules: FormRules;
+	}
+	/**
+	 * 鍒嗛〉鏌ヨ鍙傛暟
+	 */
+	interface PageQuery {
+		pageNum: number;
+		pageSize: number;
+	}
+}
+export {};
diff --git a/src/types/module.d.ts b/src/types/module.d.ts
new file mode 100644
index 0000000..82ff47f
--- /dev/null
+++ b/src/types/module.d.ts
@@ -0,0 +1,28 @@
+import modal from '@/plugins/modal';
+import tab from '@/plugins/tab';
+import { useDict } from '@/utils/dict';
+import { addDateRange, handleTree, selectDictLabel, selectDictLabels, parseTime } from '@/utils/ruoyi';
+import { getConfigKey, updateConfigByKey } from '@/api/system/config';
+import { download as download1 } from '@/utils/request';
+import download from '@/plugins/download';
+import animate from '@/animate';
+
+declare module 'vue' {
+	export interface ComponentCustomProperties {
+		// 鍏ㄥ眬鏂规硶澹版槑
+		$modal: typeof modal;
+		$tab: typeof tab;
+		$download: typeof download;
+		animate: typeof animate;
+
+		useDict: typeof useDict;
+		addDateRange: typeof addDateRange;
+		download: typeof download1;
+		handleTree: typeof handleTree;
+		getConfigKey: typeof getConfigKey;
+		updateConfigByKey: typeof updateConfigByKey;
+		selectDictLabel: typeof selectDictLabel;
+		selectDictLabels: typeof selectDictLabels;
+		parseTime: typeof parseTime;
+	}
+}
diff --git a/src/types/router.d.ts b/src/types/router.d.ts
new file mode 100644
index 0000000..bea70a6
--- /dev/null
+++ b/src/types/router.d.ts
@@ -0,0 +1,35 @@
+import { RouteRecordRaw } from 'vue-router';
+
+declare module 'vue-router' {
+	type RouteOption = {
+		hidden?: boolean;
+		permissions?: string[];
+		roles?: string[];
+		component?: any;
+		children?: RouteOption[];
+		alwaysShow?: boolean;
+		parentPath?: string;
+		meta?: {
+			title: string;
+			icon?: string;
+		};
+	} & RouteRecordRaw;
+
+	interface _RouteLocationBase {
+		children?: RouteOption[];
+	}
+
+	interface RouteLocationOptions {
+		fullPath?: string;
+	}
+
+	interface TagView extends Partial<_RouteLocationBase> {
+		title?: string;
+		meta?: {
+			link?: string;
+			title?: string;
+			affix?: boolean;
+			noCache?: boolean;
+		};
+	}
+}
diff --git a/src/types/setting.d.ts b/src/types/setting.d.ts
new file mode 100644
index 0000000..0d936a1
--- /dev/null
+++ b/src/types/setting.d.ts
@@ -0,0 +1,70 @@
+declare type DefaultSettings = {
+	/**
+	 * 缃戦〉鏍囬
+	 */
+	title?: string;
+
+	/**
+	 * 渚ц竟鏍忎富棰� theme-dark | theme-light
+	 */
+	sideTheme?: string;
+
+	/**
+	 * 鏄惁鏄剧ず绯荤粺甯冨眬璁剧疆
+	 */
+	showSettings?: boolean;
+
+	/**
+	 * 鏄惁鏄剧ず椤堕儴瀵艰埅
+	 */
+	topNav?: boolean;
+
+	/**
+	 * 鏄惁鏄剧ず澶氭爣绛惧鑸�
+	 */
+	tagsView?: boolean;
+	/**
+	 * 鏄惁鍥哄畾澶撮儴
+	 */
+	fixedHeader?: boolean;
+	/**
+	 * 鏄惁鏄剧ず渚ц竟鏍廘ogo
+	 */
+	sidebarLogo?: boolean;
+	/**
+	 * 瀵艰埅鏍忓竷灞�
+	 */
+	layout?: string;
+	/**
+	 * 涓婚妯″紡
+	 */
+	theme?: string;
+
+	/**
+	 * 甯冨眬澶у皬
+	 */
+	size?: string;
+
+	/**
+	 * 璇█
+	 */
+	language?: string;
+
+	/**
+	 * 鏄惁鏄剧ず鍔ㄦ�佹爣棰�
+	 */
+	dynamicTitle?: boolean;
+	/**
+	 * 鏄惁鍚敤鍔ㄧ敾鏁堟灉
+	 */
+	animationEnable?: boolean;
+	/**
+	 *  鏄惁鍚敤鏆楅粦妯″紡
+	 *
+	 * true:鏆楅粦妯″紡
+	 * false: 鏄庝寒妯″紡
+	 */
+	dark?: boolean;
+
+	errorLog?: string;
+};
diff --git a/src/utils/auth.js b/src/utils/auth.js
deleted file mode 100644
index 08a43d6..0000000
--- a/src/utils/auth.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import Cookies from 'js-cookie'
-
-const TokenKey = 'Admin-Token'
-
-export function getToken() {
-  return Cookies.get(TokenKey)
-}
-
-export function setToken(token) {
-  return Cookies.set(TokenKey, token)
-}
-
-export function removeToken() {
-  return Cookies.remove(TokenKey)
-}
diff --git a/src/utils/auth.ts b/src/utils/auth.ts
new file mode 100644
index 0000000..b753896
--- /dev/null
+++ b/src/utils/auth.ts
@@ -0,0 +1,9 @@
+import Cookies from 'js-cookie';
+
+const TokenKey = 'Admin-Token';
+
+export const getToken = () => Cookies.get(TokenKey);
+
+export const setToken = (token: string) => Cookies.set(TokenKey, token);
+
+export const removeToken = () => Cookies.remove(TokenKey);
diff --git a/src/utils/dict.js b/src/utils/dict.js
deleted file mode 100644
index 9648f14..0000000
--- a/src/utils/dict.js
+++ /dev/null
@@ -1,24 +0,0 @@
-import useDictStore from '@/store/modules/dict'
-import { getDicts } from '@/api/system/dict/data'
-
-/**
- * 鑾峰彇瀛楀吀鏁版嵁
- */
-export function useDict(...args) {
-  const res = ref({});
-  return (() => {
-    args.forEach((dictType, index) => {
-      res.value[dictType] = [];
-      const dicts = useDictStore().getDict(dictType);
-      if (dicts) {
-        res.value[dictType] = dicts;
-      } else {
-        getDicts(dictType).then(resp => {
-          res.value[dictType] = resp.data.map(p => ({ label: p.dictLabel, value: p.dictValue, elTagType: p.listClass, elTagClass: p.cssClass }))
-          useDictStore().setDict(dictType, res.value[dictType]);
-        })
-      }
-    })
-    return toRefs(res.value);
-  })()
-}
\ No newline at end of file
diff --git a/src/utils/dict.ts b/src/utils/dict.ts
new file mode 100644
index 0000000..23948e5
--- /dev/null
+++ b/src/utils/dict.ts
@@ -0,0 +1,27 @@
+import { getDicts } from '@/api/system/dict/data';
+import { useDictStore } from '@/store/modules/dict';
+/**
+ * 鑾峰彇瀛楀吀鏁版嵁
+ */
+export const useDict = (...args: string[]): { [key: string]: DictDataOption[] } => {
+	const res = ref<{
+		[key: string]: DictDataOption[];
+	}>({});
+	return (() => {
+		args.forEach(async (dictType) => {
+			res.value[dictType] = [];
+			const dicts = useDictStore().getDict(dictType);
+			if (dicts) {
+				res.value[dictType] = dicts;
+			} else {
+				await getDicts(dictType).then((resp) => {
+					res.value[dictType] = resp.data.map(
+						(p): DictDataOption => ({ label: p.dictLabel, value: p.dictValue, elTagType: p.listClass, elTagClass: p.cssClass })
+					);
+					useDictStore().setDict(dictType, res.value[dictType]);
+				});
+			}
+		});
+		return res.value;
+	})();
+};
diff --git a/src/utils/dynamicTitle.js b/src/utils/dynamicTitle.js
deleted file mode 100644
index 64404b2..0000000
--- a/src/utils/dynamicTitle.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import store from '@/store'
-import defaultSettings from '@/settings'
-import useSettingsStore from '@/store/modules/settings'
-
-/**
- * 鍔ㄦ�佷慨鏀规爣棰�
- */
-export function useDynamicTitle() {
-  const settingsStore = useSettingsStore();
-  if (settingsStore.dynamicTitle) {
-    document.title = settingsStore.title + ' - ' + defaultSettings.title;
-  } else {
-    document.title = defaultSettings.title;
-  }
-}
\ No newline at end of file
diff --git a/src/utils/dynamicTitle.ts b/src/utils/dynamicTitle.ts
new file mode 100644
index 0000000..f6ba1d9
--- /dev/null
+++ b/src/utils/dynamicTitle.ts
@@ -0,0 +1,14 @@
+import defaultSettings from '@/settings';
+import { useSettingsStore } from '@/store/modules/settings';
+
+/**
+ * 鍔ㄦ�佷慨鏀规爣棰�
+ */
+export const useDynamicTitle = () => {
+	const settingsStore = useSettingsStore();
+	if (settingsStore.dynamicTitle) {
+		document.title = settingsStore.title + ' - ' + import.meta.env.VITE_APP_TITLE;
+	} else {
+		document.title = defaultSettings.title as string;
+	}
+};
diff --git a/src/utils/errorCode.js b/src/utils/errorCode.js
deleted file mode 100644
index d2111ee..0000000
--- a/src/utils/errorCode.js
+++ /dev/null
@@ -1,6 +0,0 @@
-export default {
-  '401': '璁よ瘉澶辫触锛屾棤娉曡闂郴缁熻祫婧�',
-  '403': '褰撳墠鎿嶄綔娌℃湁鏉冮檺',
-  '404': '璁块棶璧勬簮涓嶅瓨鍦�',
-  'default': '绯荤粺鏈煡閿欒锛岃鍙嶉缁欑鐞嗗憳'
-}
diff --git a/src/utils/errorCode.ts b/src/utils/errorCode.ts
new file mode 100644
index 0000000..f541b21
--- /dev/null
+++ b/src/utils/errorCode.ts
@@ -0,0 +1,7 @@
+export const errorCode: any = {
+	'401': '璁よ瘉澶辫触锛屾棤娉曡闂郴缁熻祫婧�',
+	'403': '褰撳墠鎿嶄綔娌℃湁鏉冮檺',
+	'404': '璁块棶璧勬簮涓嶅瓨鍦�',
+	default: '绯荤粺鏈煡閿欒锛岃鍙嶉缁欑鐞嗗憳'
+};
+export default errorCode;
diff --git a/src/utils/i18n.ts b/src/utils/i18n.ts
new file mode 100644
index 0000000..758c847
--- /dev/null
+++ b/src/utils/i18n.ts
@@ -0,0 +1,12 @@
+// translate router.meta.title, be used in breadcrumb sidebar tagsview
+import i18n from '@/lang/index';
+
+export const translateRouteTitleI18n = (title: string): string => {
+	// 鍒ゆ柇鏄惁瀛樺湪鍥介檯鍖栭厤缃紝濡傛灉娌℃湁鍘熺敓杩斿洖
+	const hasKey = i18n.global.te('route.' + title);
+	if (hasKey) {
+		const translatedTitle = i18n.global.t('route.' + title);
+		return translatedTitle;
+	}
+	return title;
+};
diff --git a/src/utils/index.js b/src/utils/index.js
deleted file mode 100644
index 4e65504..0000000
--- a/src/utils/index.js
+++ /dev/null
@@ -1,390 +0,0 @@
-import { parseTime } from './ruoyi'
-
-/**
- * 琛ㄦ牸鏃堕棿鏍煎紡鍖�
- */
-export function formatDate(cellValue) {
-  if (cellValue == null || cellValue == "") return "";
-  var date = new Date(cellValue) 
-  var year = date.getFullYear()
-  var month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1
-  var day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate() 
-  var hours = date.getHours() < 10 ? '0' + date.getHours() : date.getHours() 
-  var minutes = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes() 
-  var seconds = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds()
-  return year + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ':' + seconds
-}
-
-/**
- * @param {number} time
- * @param {string} option
- * @returns {string}
- */
-export function formatTime(time, option) {
-  if (('' + time).length === 10) {
-    time = parseInt(time) * 1000
-  } else {
-    time = +time
-  }
-  const d = new Date(time)
-  const now = Date.now()
-
-  const diff = (now - d) / 1000
-
-  if (diff < 30) {
-    return '鍒氬垰'
-  } else if (diff < 3600) {
-    // less 1 hour
-    return Math.ceil(diff / 60) + '鍒嗛挓鍓�'
-  } else if (diff < 3600 * 24) {
-    return Math.ceil(diff / 3600) + '灏忔椂鍓�'
-  } else if (diff < 3600 * 24 * 2) {
-    return '1澶╁墠'
-  }
-  if (option) {
-    return parseTime(time, option)
-  } else {
-    return (
-      d.getMonth() +
-      1 +
-      '鏈�' +
-      d.getDate() +
-      '鏃�' +
-      d.getHours() +
-      '鏃�' +
-      d.getMinutes() +
-      '鍒�'
-    )
-  }
-}
-
-/**
- * @param {string} url
- * @returns {Object}
- */
-export function getQueryObject(url) {
-  url = url == null ? window.location.href : url
-  const search = url.substring(url.lastIndexOf('?') + 1)
-  const obj = {}
-  const reg = /([^?&=]+)=([^?&=]*)/g
-  search.replace(reg, (rs, $1, $2) => {
-    const name = decodeURIComponent($1)
-    let val = decodeURIComponent($2)
-    val = String(val)
-    obj[name] = val
-    return rs
-  })
-  return obj
-}
-
-/**
- * @param {string} input value
- * @returns {number} output value
- */
-export function byteLength(str) {
-  // returns the byte length of an utf8 string
-  let s = str.length
-  for (var i = str.length - 1; i >= 0; i--) {
-    const code = str.charCodeAt(i)
-    if (code > 0x7f && code <= 0x7ff) s++
-    else if (code > 0x7ff && code <= 0xffff) s += 2
-    if (code >= 0xDC00 && code <= 0xDFFF) i--
-  }
-  return s
-}
-
-/**
- * @param {Array} actual
- * @returns {Array}
- */
-export function cleanArray(actual) {
-  const newArray = []
-  for (let i = 0; i < actual.length; i++) {
-    if (actual[i]) {
-      newArray.push(actual[i])
-    }
-  }
-  return newArray
-}
-
-/**
- * @param {Object} json
- * @returns {Array}
- */
-export function param(json) {
-  if (!json) return ''
-  return cleanArray(
-    Object.keys(json).map(key => {
-      if (json[key] === undefined) return ''
-      return encodeURIComponent(key) + '=' + encodeURIComponent(json[key])
-    })
-  ).join('&')
-}
-
-/**
- * @param {string} url
- * @returns {Object}
- */
-export function param2Obj(url) {
-  const search = decodeURIComponent(url.split('?')[1]).replace(/\+/g, ' ')
-  if (!search) {
-    return {}
-  }
-  const obj = {}
-  const searchArr = search.split('&')
-  searchArr.forEach(v => {
-    const index = v.indexOf('=')
-    if (index !== -1) {
-      const name = v.substring(0, index)
-      const val = v.substring(index + 1, v.length)
-      obj[name] = val
-    }
-  })
-  return obj
-}
-
-/**
- * @param {string} val
- * @returns {string}
- */
-export function html2Text(val) {
-  const div = document.createElement('div')
-  div.innerHTML = val
-  return div.textContent || div.innerText
-}
-
-/**
- * Merges two objects, giving the last one precedence
- * @param {Object} target
- * @param {(Object|Array)} source
- * @returns {Object}
- */
-export function objectMerge(target, source) {
-  if (typeof target !== 'object') {
-    target = {}
-  }
-  if (Array.isArray(source)) {
-    return source.slice()
-  }
-  Object.keys(source).forEach(property => {
-    const sourceProperty = source[property]
-    if (typeof sourceProperty === 'object') {
-      target[property] = objectMerge(target[property], sourceProperty)
-    } else {
-      target[property] = sourceProperty
-    }
-  })
-  return target
-}
-
-/**
- * @param {HTMLElement} element
- * @param {string} className
- */
-export function toggleClass(element, className) {
-  if (!element || !className) {
-    return
-  }
-  let classString = element.className
-  const nameIndex = classString.indexOf(className)
-  if (nameIndex === -1) {
-    classString += '' + className
-  } else {
-    classString =
-      classString.substr(0, nameIndex) +
-      classString.substr(nameIndex + className.length)
-  }
-  element.className = classString
-}
-
-/**
- * @param {string} type
- * @returns {Date}
- */
-export function getTime(type) {
-  if (type === 'start') {
-    return new Date().getTime() - 3600 * 1000 * 24 * 90
-  } else {
-    return new Date(new Date().toDateString())
-  }
-}
-
-/**
- * @param {Function} func
- * @param {number} wait
- * @param {boolean} immediate
- * @return {*}
- */
-export function debounce(func, wait, immediate) {
-  let timeout, args, context, timestamp, result
-
-  const later = function() {
-    // 鎹笂涓�娆¤Е鍙戞椂闂撮棿闅�
-    const last = +new Date() - timestamp
-
-    // 涓婃琚寘瑁呭嚱鏁拌璋冪敤鏃堕棿闂撮殧 last 灏忎簬璁惧畾鏃堕棿闂撮殧 wait
-    if (last < wait && last > 0) {
-      timeout = setTimeout(later, wait - last)
-    } else {
-      timeout = null
-      // 濡傛灉璁惧畾涓篿mmediate===true锛屽洜涓哄紑濮嬭竟鐣屽凡缁忚皟鐢ㄨ繃浜嗘澶勬棤闇�璋冪敤
-      if (!immediate) {
-        result = func.apply(context, args)
-        if (!timeout) context = args = null
-      }
-    }
-  }
-
-  return function(...args) {
-    context = this
-    timestamp = +new Date()
-    const callNow = immediate && !timeout
-    // 濡傛灉寤舵椂涓嶅瓨鍦紝閲嶆柊璁惧畾寤舵椂
-    if (!timeout) timeout = setTimeout(later, wait)
-    if (callNow) {
-      result = func.apply(context, args)
-      context = args = null
-    }
-
-    return result
-  }
-}
-
-/**
- * This is just a simple version of deep copy
- * Has a lot of edge cases bug
- * If you want to use a perfect deep copy, use lodash's _.cloneDeep
- * @param {Object} source
- * @returns {Object}
- */
-export function deepClone(source) {
-  if (!source && typeof source !== 'object') {
-    throw new Error('error arguments', 'deepClone')
-  }
-  const targetObj = source.constructor === Array ? [] : {}
-  Object.keys(source).forEach(keys => {
-    if (source[keys] && typeof source[keys] === 'object') {
-      targetObj[keys] = deepClone(source[keys])
-    } else {
-      targetObj[keys] = source[keys]
-    }
-  })
-  return targetObj
-}
-
-/**
- * @param {Array} arr
- * @returns {Array}
- */
-export function uniqueArr(arr) {
-  return Array.from(new Set(arr))
-}
-
-/**
- * @returns {string}
- */
-export function createUniqueString() {
-  const timestamp = +new Date() + ''
-  const randomNum = parseInt((1 + Math.random()) * 65536) + ''
-  return (+(randomNum + timestamp)).toString(32)
-}
-
-/**
- * Check if an element has a class
- * @param {HTMLElement} elm
- * @param {string} cls
- * @returns {boolean}
- */
-export function hasClass(ele, cls) {
-  return !!ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)'))
-}
-
-/**
- * Add class to element
- * @param {HTMLElement} elm
- * @param {string} cls
- */
-export function addClass(ele, cls) {
-  if (!hasClass(ele, cls)) ele.className += ' ' + cls
-}
-
-/**
- * Remove class from element
- * @param {HTMLElement} elm
- * @param {string} cls
- */
-export function removeClass(ele, cls) {
-  if (hasClass(ele, cls)) {
-    const reg = new RegExp('(\\s|^)' + cls + '(\\s|$)')
-    ele.className = ele.className.replace(reg, ' ')
-  }
-}
-
-export function makeMap(str, expectsLowerCase) {
-  const map = Object.create(null)
-  const list = str.split(',')
-  for (let i = 0; i < list.length; i++) {
-    map[list[i]] = true
-  }
-  return expectsLowerCase
-    ? val => map[val.toLowerCase()]
-    : val => map[val]
-}
- 
-export const exportDefault = 'export default '
-
-export const beautifierConf = {
-  html: {
-    indent_size: '2',
-    indent_char: ' ',
-    max_preserve_newlines: '-1',
-    preserve_newlines: false,
-    keep_array_indentation: false,
-    break_chained_methods: false,
-    indent_scripts: 'separate',
-    brace_style: 'end-expand',
-    space_before_conditional: true,
-    unescape_strings: false,
-    jslint_happy: false,
-    end_with_newline: true,
-    wrap_line_length: '110',
-    indent_inner_html: true,
-    comma_first: false,
-    e4x: true,
-    indent_empty_lines: true
-  },
-  js: {
-    indent_size: '2',
-    indent_char: ' ',
-    max_preserve_newlines: '-1',
-    preserve_newlines: false,
-    keep_array_indentation: false,
-    break_chained_methods: false,
-    indent_scripts: 'normal',
-    brace_style: 'end-expand',
-    space_before_conditional: true,
-    unescape_strings: false,
-    jslint_happy: true,
-    end_with_newline: true,
-    wrap_line_length: '110',
-    indent_inner_html: true,
-    comma_first: false,
-    e4x: true,
-    indent_empty_lines: true
-  }
-}
-
-// 棣栧瓧姣嶅ぇ灏�
-export function titleCase(str) {
-  return str.replace(/( |^)[a-z]/g, L => L.toUpperCase())
-}
-
-// 涓嬪垝杞┘宄�
-export function camelCase(str) {
-  return str.replace(/_[a-z]/g, str1 => str1.substr(-1).toUpperCase())
-}
-
-export function isNumberStr(str) {
-  return /^[+-]?(0|([1-9]\d*))(\.\d+)?$/g.test(str)
-}
- 
diff --git a/src/utils/index.ts b/src/utils/index.ts
new file mode 100644
index 0000000..fb1a97b
--- /dev/null
+++ b/src/utils/index.ts
@@ -0,0 +1,318 @@
+import { parseTime } from '@/utils/ruoyi';
+
+/**
+ * 琛ㄦ牸鏃堕棿鏍煎紡鍖�
+ */
+export const formatDate = (cellValue: string) => {
+	if (cellValue == null || cellValue == '') return '';
+	const date = new Date(cellValue);
+	const year = date.getFullYear();
+	const month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1;
+	const day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate();
+	const hours = date.getHours() < 10 ? '0' + date.getHours() : date.getHours();
+	const minutes = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes();
+	const seconds = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds();
+	return year + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ':' + seconds;
+};
+
+/**
+ * @param {number} time
+ * @param {string} option
+ * @returns {string}
+ */
+export const formatTime = (time: string, option: string) => {
+	let t: number;
+	if (('' + time).length === 10) {
+		t = parseInt(time) * 1000;
+	} else {
+		t = +time;
+	}
+	const d: any = new Date(t);
+	const now = Date.now();
+
+	const diff = (now - d) / 1000;
+
+	if (diff < 30) {
+		return '鍒氬垰';
+	} else if (diff < 3600) {
+		// less 1 hour
+		return Math.ceil(diff / 60) + '鍒嗛挓鍓�';
+	} else if (diff < 3600 * 24) {
+		return Math.ceil(diff / 3600) + '灏忔椂鍓�';
+	} else if (diff < 3600 * 24 * 2) {
+		return '1澶╁墠';
+	}
+	if (option) {
+		return parseTime(t, option);
+	} else {
+		return d.getMonth() + 1 + '鏈�' + d.getDate() + '鏃�' + d.getHours() + '鏃�' + d.getMinutes() + '鍒�';
+	}
+};
+
+/**
+ * @param {string} url
+ * @returns {Object}
+ */
+export const getQueryObject = (url: string) => {
+	url = url == null ? window.location.href : url;
+	const search = url.substring(url.lastIndexOf('?') + 1);
+	const obj: { [key: string]: string } = {};
+	const reg = /([^?&=]+)=([^?&=]*)/g;
+	search.replace(reg, (rs, $1, $2) => {
+		const name = decodeURIComponent($1);
+		let val = decodeURIComponent($2);
+		val = String(val);
+		obj[name] = val;
+		return rs;
+	});
+	return obj;
+};
+
+/**
+ * @param {string} input value
+ * @returns {number} output value
+ */
+export const byteLength = (str: string) => {
+	// returns the byte length of an utf8 string
+	let s = str.length;
+	for (let i = str.length - 1; i >= 0; i--) {
+		const code = str.charCodeAt(i);
+		if (code > 0x7f && code <= 0x7ff) s++;
+		else if (code > 0x7ff && code <= 0xffff) s += 2;
+		if (code >= 0xdc00 && code <= 0xdfff) i--;
+	}
+	return s;
+};
+
+/**
+ * @param {Array} actual
+ * @returns {Array}
+ */
+export const cleanArray = (actual: Array<any>) => {
+	const newArray = [];
+	for (let i = 0; i < actual.length; i++) {
+		if (actual[i]) {
+			newArray.push(actual[i]);
+		}
+	}
+	return newArray;
+};
+
+/**
+ * @param {Object} json
+ * @returns {Array}
+ */
+export const param = (json: any) => {
+	if (!json) return '';
+	return cleanArray(
+		Object.keys(json).map((key) => {
+			if (json[key] === undefined) return '';
+			return encodeURIComponent(key) + '=' + encodeURIComponent(json[key]);
+		})
+	).join('&');
+};
+
+/**
+ * @param {string} url
+ * @returns {Object}
+ */
+export const param2Obj = (url: string) => {
+	const search = decodeURIComponent(url.split('?')[1]).replace(/\+/g, ' ');
+	if (!search) {
+		return {};
+	}
+	const obj: any = {};
+	const searchArr = search.split('&');
+	searchArr.forEach((v) => {
+		const index = v.indexOf('=');
+		if (index !== -1) {
+			const name = v.substring(0, index);
+			const val = v.substring(index + 1, v.length);
+			obj[name] = val;
+		}
+	});
+	return obj;
+};
+
+/**
+ * @param {string} val
+ * @returns {string}
+ */
+export const html2Text = (val: string) => {
+	const div = document.createElement('div');
+	div.innerHTML = val;
+	return div.textContent || div.innerText;
+};
+
+/**
+ * Merges two objects, giving the last one precedence
+ * @param {Object} target
+ * @param {(Object|Array)} source
+ * @returns {Object}
+ */
+export const objectMerge = (target: any, source: any | any[]) => {
+	if (typeof target !== 'object') {
+		target = {};
+	}
+	if (Array.isArray(source)) {
+		return source.slice();
+	}
+	Object.keys(source).forEach((property) => {
+		const sourceProperty = source[property];
+		if (typeof sourceProperty === 'object') {
+			target[property] = objectMerge(target[property], sourceProperty);
+		} else {
+			target[property] = sourceProperty;
+		}
+	});
+	return target;
+};
+
+/**
+ * @param {HTMLElement} element
+ * @param {string} className
+ */
+export const toggleClass = (element: HTMLElement, className: string) => {
+	if (!element || !className) {
+		return;
+	}
+	let classString = element.className;
+	const nameIndex = classString.indexOf(className);
+	if (nameIndex === -1) {
+		classString += '' + className;
+	} else {
+		classString = classString.substring(0, nameIndex) + classString.substring(nameIndex + className.length);
+	}
+	element.className = classString;
+};
+
+/**
+ * @param {string} type
+ * @returns {Date}
+ */
+export const getTime = (type: string) => {
+	if (type === 'start') {
+		return new Date().getTime() - 3600 * 1000 * 24 * 90;
+	} else {
+		return new Date(new Date().toDateString());
+	}
+};
+
+/**
+ * @param {Function} func
+ * @param {number} wait
+ * @param {boolean} immediate
+ * @return {*}
+ */
+export const debounce = (func: any, wait: number, immediate: boolean) => {
+	let timeout: any, args: any, context: any, timestamp: any, result: any;
+
+	const later = function () {
+		// 鎹笂涓�娆¤Е鍙戞椂闂撮棿闅�
+		const last = +new Date() - timestamp;
+
+		// 涓婃琚寘瑁呭嚱鏁拌璋冪敤鏃堕棿闂撮殧 last 灏忎簬璁惧畾鏃堕棿闂撮殧 wait
+		if (last < wait && last > 0) {
+			timeout = setTimeout(later, wait - last);
+		} else {
+			timeout = null;
+			// 濡傛灉璁惧畾涓篿mmediate===true锛屽洜涓哄紑濮嬭竟鐣屽凡缁忚皟鐢ㄨ繃浜嗘澶勬棤闇�璋冪敤
+			if (!immediate) {
+				result = func.apply(context, args);
+				if (!timeout) context = args = null;
+			}
+		}
+	};
+
+	return (...args: any) => {
+		context = this;
+		timestamp = +new Date();
+		const callNow = immediate && !timeout;
+		// 濡傛灉寤舵椂涓嶅瓨鍦紝閲嶆柊璁惧畾寤舵椂
+		if (!timeout) timeout = setTimeout(later, wait);
+		if (callNow) {
+			result = func.apply(context, args);
+			context = args = null;
+		}
+		return result;
+	};
+};
+
+/**
+ * This is just a simple version of deep copy
+ * Has a lot of edge cases bug
+ * If you want to use a perfect deep copy, use lodash's _.cloneDeep
+ * @param {Object} source
+ * @returns {Object}
+ */
+export const deepClone = (source: any) => {
+	if (!source && typeof source !== 'object') {
+		throw new Error('error arguments', 'deepClone' as any);
+	}
+	const targetObj: any = source.constructor === Array ? [] : {};
+	Object.keys(source).forEach((keys) => {
+		if (source[keys] && typeof source[keys] === 'object') {
+			targetObj[keys] = deepClone(source[keys]);
+		} else {
+			targetObj[keys] = source[keys];
+		}
+	});
+	return targetObj;
+};
+
+/**
+ * @param {Array} arr
+ * @returns {Array}
+ */
+export const uniqueArr = (arr: any) => {
+	return Array.from(new Set(arr));
+};
+
+/**
+ * @returns {string}
+ */
+export const createUniqueString = (): string => {
+	const timestamp = +new Date() + '';
+	const num = (1 + Math.random()) * 65536;
+	const randomNum = parseInt(num + '');
+	return (+(randomNum + timestamp)).toString(32);
+};
+
+/**
+ * Check if an element has a class
+ * @param ele
+ * @param {string} cls
+ * @returns {boolean}
+ */
+export const hasClass = (ele: HTMLElement, cls: string): boolean => {
+	return !!ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)'));
+};
+
+/**
+ * Add class to element
+ * @param ele
+ * @param {string} cls
+ */
+export const addClass = (ele: HTMLElement, cls: string) => {
+	if (!hasClass(ele, cls)) ele.className += ' ' + cls;
+};
+
+/**
+ * Remove class from element
+ * @param ele
+ * @param {string} cls
+ */
+export const removeClass = (ele: HTMLElement, cls: string) => {
+	if (hasClass(ele, cls)) {
+		const reg = new RegExp('(\\s|^)' + cls + '(\\s|$)');
+		ele.className = ele.className.replace(reg, ' ');
+	}
+};
+
+/**
+ * @param {string} path
+ * @returns {Boolean}
+ */
+export const isExternal = (path: string) => {
+	return /^(https?:|http?:|mailto:|tel:)/.test(path);
+};
diff --git a/src/utils/jsencrypt.js b/src/utils/jsencrypt.js
deleted file mode 100644
index 78d9523..0000000
--- a/src/utils/jsencrypt.js
+++ /dev/null
@@ -1,30 +0,0 @@
-import JSEncrypt from 'jsencrypt/bin/jsencrypt.min'
-
-// 瀵嗛挜瀵圭敓鎴� http://web.chacuo.net/netrsakeypair
-
-const publicKey = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdH\n' +
-  'nzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ=='
-
-const privateKey = 'MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqhHyZfSsYourNxaY\n' +
-  '7Nt+PrgrxkiA50efORdI5U5lsW79MmFnusUA355oaSXcLhu5xxB38SMSyP2KvuKN\n' +
-  'PuH3owIDAQABAkAfoiLyL+Z4lf4Myxk6xUDgLaWGximj20CUf+5BKKnlrK+Ed8gA\n' +
-  'kM0HqoTt2UZwA5E2MzS4EI2gjfQhz5X28uqxAiEA3wNFxfrCZlSZHb0gn2zDpWow\n' +
-  'cSxQAgiCstxGUoOqlW8CIQDDOerGKH5OmCJ4Z21v+F25WaHYPxCFMvwxpcw99Ecv\n' +
-  'DQIgIdhDTIqD2jfYjPTY8Jj3EDGPbH2HHuffvflECt3Ek60CIQCFRlCkHpi7hthh\n' +
-  'YhovyloRYsM+IS9h/0BzlEAuO0ktMQIgSPT3aFAgJYwKpqRYKlLDVcflZFCKY7u3\n' +
-  'UP8iWi1Qw0Y='
-
-// 鍔犲瘑
-export function encrypt(txt) {
-  const encryptor = new JSEncrypt()
-  encryptor.setPublicKey(publicKey) // 璁剧疆鍏挜
-  return encryptor.encrypt(txt) // 瀵规暟鎹繘琛屽姞瀵�
-}
-
-// 瑙e瘑
-export function decrypt(txt) {
-  const encryptor = new JSEncrypt()
-  encryptor.setPrivateKey(privateKey) // 璁剧疆绉侀挜
-  return encryptor.decrypt(txt) // 瀵规暟鎹繘琛岃В瀵�
-}
-
diff --git a/src/utils/jsencrypt.ts b/src/utils/jsencrypt.ts
new file mode 100644
index 0000000..d26209b
--- /dev/null
+++ b/src/utils/jsencrypt.ts
@@ -0,0 +1,29 @@
+import JSEncrypt from 'jsencrypt';
+// 瀵嗛挜瀵圭敓鎴� http://web.chacuo.net/netrsakeypair
+
+const publicKey =
+	'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdH\n' + 'nzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ==';
+
+const privateKey =
+	'MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqhHyZfSsYourNxaY\n' +
+	'7Nt+PrgrxkiA50efORdI5U5lsW79MmFnusUA355oaSXcLhu5xxB38SMSyP2KvuKN\n' +
+	'PuH3owIDAQABAkAfoiLyL+Z4lf4Myxk6xUDgLaWGximj20CUf+5BKKnlrK+Ed8gA\n' +
+	'kM0HqoTt2UZwA5E2MzS4EI2gjfQhz5X28uqxAiEA3wNFxfrCZlSZHb0gn2zDpWow\n' +
+	'cSxQAgiCstxGUoOqlW8CIQDDOerGKH5OmCJ4Z21v+F25WaHYPxCFMvwxpcw99Ecv\n' +
+	'DQIgIdhDTIqD2jfYjPTY8Jj3EDGPbH2HHuffvflECt3Ek60CIQCFRlCkHpi7hthh\n' +
+	'YhovyloRYsM+IS9h/0BzlEAuO0ktMQIgSPT3aFAgJYwKpqRYKlLDVcflZFCKY7u3\n' +
+	'UP8iWi1Qw0Y=';
+
+// 鍔犲瘑
+export const encrypt = (txt: string) => {
+	const encryptor = new JSEncrypt();
+	encryptor.setPublicKey(publicKey); // 璁剧疆鍏挜
+	return encryptor.encrypt(txt); // 瀵规暟鎹繘琛屽姞瀵�
+};
+
+// 瑙e瘑
+export const decrypt = (txt: string) => {
+	const encryptor = new JSEncrypt();
+	encryptor.setPrivateKey(privateKey); // 璁剧疆绉侀挜
+	return encryptor.decrypt(txt); // 瀵规暟鎹繘琛岃В瀵�
+};
diff --git a/src/utils/permission.js b/src/utils/permission.js
deleted file mode 100644
index 93fee87..0000000
--- a/src/utils/permission.js
+++ /dev/null
@@ -1,51 +0,0 @@
-import useUserStore from '@/store/modules/user'
-
-/**
- * 瀛楃鏉冮檺鏍¢獙
- * @param {Array} value 鏍¢獙鍊�
- * @returns {Boolean}
- */
-export function checkPermi(value) {
-  if (value && value instanceof Array && value.length > 0) {
-    const permissions = useUserStore().permissions
-    const permissionDatas = value
-    const all_permission = "*:*:*";
-
-    const hasPermission = permissions.some(permission => {
-      return all_permission === permission || permissionDatas.includes(permission)
-    })
-
-    if (!hasPermission) {
-      return false
-    }
-    return true
-  } else {
-    console.error(`need roles! Like checkPermi="['system:user:add','system:user:edit']"`)
-    return false
-  }
-}
-
-/**
- * 瑙掕壊鏉冮檺鏍¢獙
- * @param {Array} value 鏍¢獙鍊�
- * @returns {Boolean}
- */
-export function checkRole(value) {
-  if (value && value instanceof Array && value.length > 0) {
-    const roles = useUserStore().roles
-    const permissionRoles = value
-    const super_admin = "admin";
-
-    const hasRole = roles.some(role => {
-      return super_admin === role || permissionRoles.includes(role)
-    })
-
-    if (!hasRole) {
-      return false
-    }
-    return true
-  } else {
-    console.error(`need roles! Like checkRole="['admin','editor']"`)
-    return false
-  }
-}
\ No newline at end of file
diff --git a/src/utils/permission.ts b/src/utils/permission.ts
new file mode 100644
index 0000000..7b51dbc
--- /dev/null
+++ b/src/utils/permission.ts
@@ -0,0 +1,51 @@
+import useUserStore from '@/store/modules/user';
+
+/**
+ * 瀛楃鏉冮檺鏍¢獙
+ * @param {Array} value 鏍¢獙鍊�
+ * @returns {Boolean}
+ */
+export const checkPermi = (value: any) => {
+	if (value && value instanceof Array && value.length > 0) {
+		const permissions = useUserStore().permissions;
+		const permissionDatas = value;
+		const all_permission = '*:*:*';
+
+		const hasPermission = permissions.some((permission) => {
+			return all_permission === permission || permissionDatas.includes(permission);
+		});
+
+		if (!hasPermission) {
+			return false;
+		}
+		return true;
+	} else {
+		console.error(`need roles! Like checkPermi="['system:user:add','system:user:edit']"`);
+		return false;
+	}
+};
+
+/**
+ * 瑙掕壊鏉冮檺鏍¢獙
+ * @param {Array} value 鏍¢獙鍊�
+ * @returns {Boolean}
+ */
+export const checkRole = (value: any): boolean => {
+	if (value && value instanceof Array && value.length > 0) {
+		const roles = useUserStore().roles;
+		const permissionRoles = value;
+		const super_admin = 'admin';
+
+		const hasRole = roles.some((role) => {
+			return super_admin === role || permissionRoles.includes(role);
+		});
+
+		if (!hasRole) {
+			return false;
+		}
+		return true;
+	} else {
+		console.error(`need roles! Like checkRole="['admin','editor']"`);
+		return false;
+	}
+};
diff --git a/src/utils/request.js b/src/utils/request.js
deleted file mode 100644
index 1da3509..0000000
--- a/src/utils/request.js
+++ /dev/null
@@ -1,148 +0,0 @@
-import axios from 'axios'
-import { ElNotification , ElMessageBox, ElMessage, ElLoading } from 'element-plus'
-import { getToken } from '@/utils/auth'
-import errorCode from '@/utils/errorCode'
-import { tansParams, blobValidate } from '@/utils/ruoyi'
-import cache from '@/plugins/cache'
-import { saveAs } from 'file-saver'
-import useUserStore from '@/store/modules/user'
-
-let downloadLoadingInstance;
-// 鏄惁鏄剧ず閲嶆柊鐧诲綍
-export let isRelogin = { show: false };
-
-axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
-// 瀵瑰簲鍥介檯鍖栬祫婧愭枃浠跺悗缂�
-axios.defaults.headers['Content-Language'] = 'zh_CN'
-// 鍒涘缓axios瀹炰緥
-const service = axios.create({
-  // axios涓姹傞厤缃湁baseURL閫夐」锛岃〃绀鸿姹俇RL鍏叡閮ㄥ垎
-  baseURL: import.meta.env.VITE_APP_BASE_API,
-  // 瓒呮椂
-  timeout: 10000
-})
-
-// request鎷︽埅鍣�
-service.interceptors.request.use(config => {
-  // 鏄惁闇�瑕佽缃� token
-  const isToken = (config.headers || {}).isToken === false
-  // 鏄惁闇�瑕侀槻姝㈡暟鎹噸澶嶆彁浜�
-  const isRepeatSubmit = (config.headers || {}).repeatSubmit === false
-  if (getToken() && !isToken) {
-    config.headers['Authorization'] = 'Bearer ' + getToken() // 璁╂瘡涓姹傛惡甯﹁嚜瀹氫箟token 璇锋牴鎹疄闄呮儏鍐佃嚜琛屼慨鏀�
-  }
-  // get璇锋眰鏄犲皠params鍙傛暟
-  if (config.method === 'get' && config.params) {
-    let url = config.url + '?' + tansParams(config.params);
-    url = url.slice(0, -1);
-    config.params = {};
-    config.url = url;
-  }
-  if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) {
-    const requestObj = {
-      url: config.url,
-      data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data,
-      time: new Date().getTime()
-    }
-    const sessionObj = cache.session.getJSON('sessionObj')
-    if (sessionObj === undefined || sessionObj === null || sessionObj === '') {
-      cache.session.setJSON('sessionObj', requestObj)
-    } else {
-      const s_url = sessionObj.url;                // 璇锋眰鍦板潃
-      const s_data = sessionObj.data;              // 璇锋眰鏁版嵁
-      const s_time = sessionObj.time;              // 璇锋眰鏃堕棿
-      const interval = 1000;                       // 闂撮殧鏃堕棿(ms)锛屽皬浜庢鏃堕棿瑙嗕负閲嶅鎻愪氦
-      if (s_data === requestObj.data && requestObj.time - s_time < interval && s_url === requestObj.url) {
-        const message = '鏁版嵁姝e湪澶勭悊锛岃鍕块噸澶嶆彁浜�';
-        console.warn(`[${s_url}]: ` + message)
-        return Promise.reject(new Error(message))
-      } else {
-        cache.session.setJSON('sessionObj', requestObj)
-      }
-    }
-  }
-  return config
-}, error => {
-    console.log(error)
-    Promise.reject(error)
-})
-
-// 鍝嶅簲鎷︽埅鍣�
-service.interceptors.response.use(res => {
-    // 鏈缃姸鎬佺爜鍒欓粯璁ゆ垚鍔熺姸鎬�
-    const code = res.data.code || 200;
-    // 鑾峰彇閿欒淇℃伅
-    const msg = errorCode[code] || res.data.msg || errorCode['default']
-    // 浜岃繘鍒舵暟鎹垯鐩存帴杩斿洖
-    if (res.request.responseType ===  'blob' || res.request.responseType ===  'arraybuffer') {
-      return res.data
-    }
-    if (code === 401) {
-      if (!isRelogin.show) {
-        isRelogin.show = true;
-        ElMessageBox.confirm('鐧诲綍鐘舵�佸凡杩囨湡锛屾偍鍙互缁х画鐣欏湪璇ラ〉闈紝鎴栬�呴噸鏂扮櫥褰�', '绯荤粺鎻愮ず', { confirmButtonText: '閲嶆柊鐧诲綍', cancelButtonText: '鍙栨秷', type: 'warning' }).then(() => {
-          isRelogin.show = false;
-          useUserStore().logOut().then(() => {
-            location.href = import.meta.env.VITE_APP_CONTEXT_PATH + 'index';
-          })
-      }).catch(() => {
-        isRelogin.show = false;
-      });
-    }
-      return Promise.reject('鏃犳晥鐨勪細璇濓紝鎴栬�呬細璇濆凡杩囨湡锛岃閲嶆柊鐧诲綍銆�')
-    } else if (code === 500) {
-      ElMessage({ message: msg, type: 'error' })
-      return Promise.reject(new Error(msg))
-    } else if (code === 601) {
-      ElMessage({ message: msg, type: 'warning' })
-      return Promise.reject(new Error(msg))
-    } else if (code !== 200) {
-      ElNotification.error({ title: msg })
-      return Promise.reject('error')
-    } else {
-      return  Promise.resolve(res.data)
-    }
-  },
-  error => {
-    console.log('err' + error)
-    let { message } = error;
-    if (message == "Network Error") {
-      message = "鍚庣鎺ュ彛杩炴帴寮傚父";
-    } else if (message.includes("timeout")) {
-      message = "绯荤粺鎺ュ彛璇锋眰瓒呮椂";
-    } else if (message.includes("Request failed with status code")) {
-      message = "绯荤粺鎺ュ彛" + message.substr(message.length - 3) + "寮傚父";
-    }
-    ElMessage({ message: message, type: 'error', duration: 5 * 1000 })
-    return Promise.reject(error)
-  }
-)
-
-// 閫氱敤涓嬭浇鏂规硶
-export function download(url, params, filename, config) {
-  downloadLoadingInstance = ElLoading.service({ text: "姝e湪涓嬭浇鏁版嵁锛岃绋嶅��", background: "rgba(0, 0, 0, 0.7)", })
-  return service.post(url, params, {
-    transformRequest: [(params) => { return tansParams(params) }],
-    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
-    responseType: 'blob',
-    ...config
-  }).then(async (data) => {
-    const isBlob = blobValidate(data);
-    if (isBlob) {
-      const blob = new Blob([data])
-      saveAs(blob, filename)
-    } else {
-      const resText = await data.text();
-      const rspObj = JSON.parse(resText);
-      const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default']
-      ElMessage.error(errMsg);
-    }
-    downloadLoadingInstance.close();
-  }).catch((r) => {
-    console.error(r)
-    ElMessage.error('涓嬭浇鏂囦欢鍑虹幇閿欒锛岃鑱旂郴绠$悊鍛橈紒')
-    downloadLoadingInstance.close();
-  })
-}
-
-export default service
diff --git a/src/utils/request.ts b/src/utils/request.ts
new file mode 100644
index 0000000..1376fbd
--- /dev/null
+++ b/src/utils/request.ts
@@ -0,0 +1,159 @@
+import axios, { InternalAxiosRequestConfig } from 'axios';
+import { useUserStore } from '@/store/modules/user';
+import { getToken } from '@/utils/auth';
+import { tansParams, blobValidate } from '@/utils/ruoyi';
+import cache from '@/plugins/cache';
+import { HttpStatus } from '@/enums/RespEnum';
+import { errorCode } from '@/utils/errorCode';
+import { LoadingInstance } from 'element-plus/es/components/loading/src/loading';
+import FileSaver from 'file-saver';
+
+let downloadLoadingInstance: LoadingInstance;
+// 鏄惁鏄剧ず閲嶆柊鐧诲綍
+export const isRelogin = { show: false };
+
+axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8';
+// 瀵瑰簲鍥介檯鍖栬祫婧愭枃浠跺悗缂�
+axios.defaults.headers['Content-Language'] = 'zh_CN';
+// 鍒涘缓 axios 瀹炰緥
+const service = axios.create({
+	baseURL: import.meta.env.VITE_APP_BASE_API,
+	timeout: 50000
+});
+
+// 璇锋眰鎷︽埅鍣�
+service.interceptors.request.use(
+	(config: InternalAxiosRequestConfig) => {
+		const isToken = (config.headers || {}).isToken === false;
+		// 鏄惁闇�瑕侀槻姝㈡暟鎹噸澶嶆彁浜�
+		const isRepeatSubmit = !(config.headers || {}).repeatSubmit;
+		if (getToken() && !isToken) {
+			config.headers['Authorization'] = 'Bearer ' + getToken(); // 璁╂瘡涓姹傛惡甯﹁嚜瀹氫箟token 璇锋牴鎹疄闄呮儏鍐佃嚜琛屼慨鏀�
+		}
+		// get璇锋眰鏄犲皠params鍙傛暟
+		if (config.method === 'get' && config.params) {
+			let url = config.url + '?' + tansParams(config.params);
+			url = url.slice(0, -1);
+			config.params = {};
+			config.url = url;
+		}
+
+		if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) {
+			const requestObj = {
+				url: config.url,
+				data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data,
+				time: new Date().getTime()
+			};
+			const sessionObj = cache.session.getJSON('sessionObj');
+			if (sessionObj === undefined || sessionObj === null || sessionObj === '') {
+				cache.session.setJSON('sessionObj', requestObj);
+			} else {
+				const s_url = sessionObj.url; // 璇锋眰鍦板潃
+				const s_data = sessionObj.data; // 璇锋眰鏁版嵁
+				const s_time = sessionObj.time; // 璇锋眰鏃堕棿
+				const interval = 500; // 闂撮殧鏃堕棿(ms)锛屽皬浜庢鏃堕棿瑙嗕负閲嶅鎻愪氦
+				if (s_data === requestObj.data && requestObj.time - s_time < interval && s_url === requestObj.url) {
+					const message = '鏁版嵁姝e湪澶勭悊锛岃鍕块噸澶嶆彁浜�';
+					console.warn(`[${s_url}]: ` + message);
+					return Promise.reject(new Error(message));
+				} else {
+					cache.session.setJSON('sessionObj', requestObj);
+				}
+			}
+		}
+		return config;
+	},
+	(error: any) => {
+		console.log(error);
+		return Promise.reject(error);
+	}
+);
+
+// 鍝嶅簲鎷︽埅鍣�
+service.interceptors.response.use(
+	(res) => {
+		// 鏈缃姸鎬佺爜鍒欓粯璁ゆ垚鍔熺姸鎬�
+		const code = res.data.code || HttpStatus.SUCCESS;
+		// 鑾峰彇閿欒淇℃伅
+		const msg = errorCode[code] || res.data.msg || errorCode['default'];
+		// 浜岃繘鍒舵暟鎹垯鐩存帴杩斿洖
+		if (res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer') {
+			return res.data;
+		}
+		if (code === 401) {
+			// prettier-ignore
+			if (!isRelogin.show) {
+				isRelogin.show = true;
+				ElMessageBox.confirm('鐧诲綍鐘舵�佸凡杩囨湡锛屾偍鍙互缁х画鐣欏湪璇ラ〉闈紝鎴栬�呴噸鏂扮櫥褰�', '绯荤粺鎻愮ず', {
+					confirmButtonText: '閲嶆柊鐧诲綍',
+					cancelButtonText: '鍙栨秷',
+					type: 'warning'
+				}).then(() => {
+					isRelogin.show = false;
+					useUserStore().logout().then(() => {
+							location.href = import.meta.env.VITE_APP_CONTEXT_PATH + 'index';
+						});
+				}).catch(() => {
+					isRelogin.show = false;
+				});
+			}
+			return Promise.reject('鏃犳晥鐨勪細璇濓紝鎴栬�呬細璇濆凡杩囨湡锛岃閲嶆柊鐧诲綍銆�');
+		} else if (code === HttpStatus.SERVER_ERROR) {
+			console.log(msg);
+			ElMessage({ message: msg, type: 'error' });
+			return Promise.reject(new Error(msg));
+		} else if (code === HttpStatus.WARN) {
+			ElMessage({ message: msg, type: 'warning' });
+			return Promise.reject(new Error(msg));
+		} else if (code !== HttpStatus.SUCCESS) {
+			ElNotification.error({ title: msg });
+			return Promise.reject('error');
+		} else {
+			return Promise.resolve(res.data);
+		}
+	},
+	(error) => {
+		let { message } = error;
+		if (message == 'Network Error') {
+			message = '鍚庣鎺ュ彛杩炴帴寮傚父';
+		} else if (message.includes('timeout')) {
+			message = '绯荤粺鎺ュ彛璇锋眰瓒呮椂';
+		} else if (message.includes('Request failed with status code')) {
+			message = '绯荤粺鎺ュ彛' + message.substr(message.length - 3) + '寮傚父';
+		}
+		ElMessage({ message: message, type: 'error', duration: 5 * 1000 });
+		return Promise.reject(error);
+	}
+);
+// 閫氱敤涓嬭浇鏂规硶
+export function download(url: string, params: any, fileName: string) {
+	downloadLoadingInstance = ElLoading.service({ text: '姝e湪涓嬭浇鏁版嵁锛岃绋嶅��', background: 'rgba(0, 0, 0, 0.7)' });
+	// prettier-ignore
+	return service.post(url, params, {
+			transformRequest: [
+				(params) => {
+					return tansParams(params);
+				}
+			],
+			headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
+			responseType: 'blob'
+		}).then(async (resp) => {
+			const isLogin = blobValidate(resp);
+			if (isLogin) {
+				const blob = new Blob([resp as any]);
+        FileSaver.saveAs(blob, fileName);
+			} else {
+				const resText = await resp.data.text();
+				const rspObj = JSON.parse(resText);
+				const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default'];
+				ElMessage.error(errMsg);
+			}
+			downloadLoadingInstance.close();
+		}).catch((r) => {
+			console.error(r);
+			ElMessage.error('涓嬭浇鏂囦欢鍑虹幇閿欒锛岃鑱旂郴绠$悊鍛橈紒');
+			downloadLoadingInstance.close();
+		});
+}
+// 瀵煎嚭 axios 瀹炰緥
+export default service;
diff --git a/src/utils/ruoyi.js b/src/utils/ruoyi.js
deleted file mode 100644
index 4efca08..0000000
--- a/src/utils/ruoyi.js
+++ /dev/null
@@ -1,246 +0,0 @@
-
-
-/**
- * 閫氱敤js鏂规硶灏佽澶勭悊
- * Copyright (c) 2019 ruoyi
- */
-
-// 鏃ユ湡鏍煎紡鍖�
-export function parseTime(time, pattern) {
-  if (arguments.length === 0 || !time) {
-    return null
-  }
-  const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}'
-  let date
-  if (typeof time === 'object') {
-    date = time
-  } else {
-    if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) {
-      time = parseInt(time)
-    } else if (typeof time === 'string') {
-      time = time.replace(new RegExp(/-/gm), '/').replace('T', ' ').replace(new RegExp(/\.[\d]{3}/gm), '');
-    }
-    if ((typeof time === 'number') && (time.toString().length === 10)) {
-      time = time * 1000
-    }
-    date = new Date(time)
-  }
-  const formatObj = {
-    y: date.getFullYear(),
-    m: date.getMonth() + 1,
-    d: date.getDate(),
-    h: date.getHours(),
-    i: date.getMinutes(),
-    s: date.getSeconds(),
-    a: date.getDay()
-  }
-  const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
-    let value = formatObj[key]
-    // Note: getDay() returns 0 on Sunday
-    if (key === 'a') { return ['鏃�', '涓�', '浜�', '涓�', '鍥�', '浜�', '鍏�'][value] }
-    if (result.length > 0 && value < 10) {
-      value = '0' + value
-    }
-    return value || 0
-  })
-  return time_str
-}
-
-// 琛ㄥ崟閲嶇疆
-export function resetForm(refName) {
-  if (this.$refs[refName]) {
-    this.$refs[refName].resetFields();
-  }
-}
-
-// 娣诲姞鏃ユ湡鑼冨洿
-export function addDateRange(params, dateRange, propName) {
-  let search = params;
-  search.params = typeof (search.params) === 'object' && search.params !== null && !Array.isArray(search.params) ? search.params : {};
-  dateRange = Array.isArray(dateRange) ? dateRange : [];
-  if (typeof (propName) === 'undefined') {
-    search.params['beginTime'] = dateRange[0];
-    search.params['endTime'] = dateRange[1];
-  } else {
-    search.params['begin' + propName] = dateRange[0];
-    search.params['end' + propName] = dateRange[1];
-  }
-  return search;
-}
-
-// 鍥炴樉鏁版嵁瀛楀吀
-export function selectDictLabel(datas, value) {
-  if (value === undefined) {
-    return "";
-  }
-  var actions = [];
-  Object.keys(datas).some((key) => {
-    if (datas[key].value == ('' + value)) {
-      actions.push(datas[key].label);
-      return true;
-    }
-  })
-  if (actions.length === 0) {
-    actions.push(value);
-  }
-  return actions.join('');
-}
-
-// 鍥炴樉鏁版嵁瀛楀吀锛堝瓧绗︿覆鏁扮粍锛�
-export function selectDictLabels(datas, value, separator) {
-  if (value === undefined || value.length ===0) {
-    return "";
-  }
-  if (Array.isArray(value)) {
-    value = value.join(",");
-  }
-  var actions = [];
-  var currentSeparator = undefined === separator ? "," : separator;
-  var temp = value.split(currentSeparator);
-  Object.keys(value.split(currentSeparator)).some((val) => {
-    var match = false;
-    Object.keys(datas).some((key) => {
-      if (datas[key].value == ('' + temp[val])) {
-        actions.push(datas[key].label + currentSeparator);
-        match = true;
-      }
-    })
-    if (!match) {
-      actions.push(temp[val] + currentSeparator);
-    }
-  })
-  return actions.join('').substring(0, actions.join('').length - 1);
-}
-
-// 瀛楃涓叉牸寮忓寲(%s )
-export function sprintf(str) {
-  var args = arguments, flag = true, i = 1;
-  str = str.replace(/%s/g, function () {
-    var arg = args[i++];
-    if (typeof arg === 'undefined') {
-      flag = false;
-      return '';
-    }
-    return arg;
-  });
-  return flag ? str : '';
-}
-
-// 杞崲瀛楃涓诧紝undefined,null绛夎浆鍖栦负""
-export function parseStrEmpty(str) {
-  if (!str || str == "undefined" || str == "null") {
-    return "";
-  }
-  return str;
-}
-
-// 鏁版嵁鍚堝苟
-export function mergeRecursive(source, target) {
-  for (var p in target) {
-    try {
-      if (target[p].constructor == Object) {
-        source[p] = mergeRecursive(source[p], target[p]);
-      } else {
-        source[p] = target[p];
-      }
-    } catch (e) {
-      source[p] = target[p];
-    }
-  }
-  return source;
-};
-
-/**
- * 鏋勯�犳爲鍨嬬粨鏋勬暟鎹�
- * @param {*} data 鏁版嵁婧�
- * @param {*} id id瀛楁 榛樿 'id'
- * @param {*} parentId 鐖惰妭鐐瑰瓧娈� 榛樿 'parentId'
- * @param {*} children 瀛╁瓙鑺傜偣瀛楁 榛樿 'children'
- */
-export function handleTree(data, id, parentId, children) {
-  let config = {
-    id: id || 'id',
-    parentId: parentId || 'parentId',
-    childrenList: children || 'children'
-  };
-
-  var childrenListMap = {};
-  var nodeIds = {};
-  var tree = [];
-
-  for (let d of data) {
-    let parentId = d[config.parentId];
-    if (childrenListMap[parentId] == null) {
-      childrenListMap[parentId] = [];
-    }
-    nodeIds[d[config.id]] = d;
-    childrenListMap[parentId].push(d);
-  }
-
-  for (let d of data) {
-    let parentId = d[config.parentId];
-    if (nodeIds[parentId] == null) {
-      tree.push(d);
-    }
-  }
-
-  for (let t of tree) {
-    adaptToChildrenList(t);
-  }
-
-  function adaptToChildrenList(o) {
-    if (childrenListMap[o[config.id]] !== null) {
-      o[config.childrenList] = childrenListMap[o[config.id]];
-    }
-    if (o[config.childrenList]) {
-      for (let c of o[config.childrenList]) {
-        adaptToChildrenList(c);
-      }
-    }
-  }
-  return tree;
-}
-
-/**
-* 鍙傛暟澶勭悊
-* @param {*} params  鍙傛暟
-*/
-export function tansParams(params) {
-  let result = ''
-  for (const propName of Object.keys(params)) {
-    const value = params[propName];
-    var part = encodeURIComponent(propName) + "=";
-    if (value !== null && value !== "" && typeof (value) !== "undefined") {
-      if (typeof value === 'object') {
-        for (const key of Object.keys(value)) {
-          if (value[key] !== null && value[key] !== "" && typeof (value[key]) !== 'undefined') {
-            let params = propName + '[' + key + ']';
-            var subPart = encodeURIComponent(params) + "=";
-            result += subPart + encodeURIComponent(value[key]) + "&";
-          }
-        }
-      } else {
-        result += part + encodeURIComponent(value) + "&";
-      }
-    }
-  }
-  return result
-}
-
-
-// 杩斿洖椤圭洰璺緞
-export function getNormalPath(p) {
-  if (p.length === 0 || !p || p == 'undefined') {
-    return p
-  };
-  let res = p.replace('//', '/')
-  if (res[res.length - 1] === '/') {
-    return res.slice(0, res.length - 1)
-  }
-  return res;
-}
-
-// 楠岃瘉鏄惁涓篵lob鏍煎紡
-export function blobValidate(data) {
-  return data.type !== 'application/json'
-}
diff --git a/src/utils/ruoyi.ts b/src/utils/ruoyi.ts
new file mode 100644
index 0000000..05691d8
--- /dev/null
+++ b/src/utils/ruoyi.ts
@@ -0,0 +1,247 @@
+// 鏃ユ湡鏍煎紡鍖�
+export function parseTime(time: any, pattern?: string) {
+	if (arguments.length === 0 || !time) {
+		return null;
+	}
+	const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}';
+	let date;
+	if (typeof time === 'object') {
+		date = time;
+	} else {
+		if (typeof time === 'string' && /^[0-9]+$/.test(time)) {
+			time = parseInt(time);
+		} else if (typeof time === 'string') {
+			time = time
+				.replace(new RegExp(/-/gm), '/')
+				.replace('T', ' ')
+				.replace(new RegExp(/\.[\d]{3}/gm), '');
+		}
+		if (typeof time === 'number' && time.toString().length === 10) {
+			time = time * 1000;
+		}
+		date = new Date(time);
+	}
+	const formatObj: { [key: string]: any } = {
+		y: date.getFullYear(),
+		m: date.getMonth() + 1,
+		d: date.getDate(),
+		h: date.getHours(),
+		i: date.getMinutes(),
+		s: date.getSeconds(),
+		a: date.getDay()
+	};
+	return format.replace(/{(y|m|d|h|i|s|a)+}/g, (result: string, key: string) => {
+		let value = formatObj[key];
+		// Note: getDay() returns 0 on Sunday
+		if (key === 'a') {
+			return ['鏃�', '涓�', '浜�', '涓�', '鍥�', '浜�', '鍏�'][value];
+		}
+		if (result.length > 0 && value < 10) {
+			value = '0' + value;
+		}
+		return value || 0;
+	});
+}
+
+/**
+ * 娣诲姞鏃ユ湡鑼冨洿
+ * @param params
+ * @param dateRange
+ * @param propName
+ */
+export const addDateRange = (params: any, dateRange: any[], propName?: string) => {
+	const search = params;
+	search.params = typeof search.params === 'object' && search.params !== null && !Array.isArray(search.params) ? search.params : {};
+	dateRange = Array.isArray(dateRange) ? dateRange : [];
+	if (typeof propName === 'undefined') {
+		search.params['beginTime'] = dateRange[0];
+		search.params['endTime'] = dateRange[1];
+	} else {
+		search.params['begin' + propName] = dateRange[0];
+		search.params['end' + propName] = dateRange[1];
+	}
+	return search;
+};
+
+// 鍥炴樉鏁版嵁瀛楀吀
+export const selectDictLabel = (datas: any, value: number | string) => {
+	if (value === undefined) {
+		return '';
+	}
+	const actions = [];
+	Object.keys(datas).some((key) => {
+		if (datas[key].value == '' + value) {
+			actions.push(datas[key].label);
+			return true;
+		}
+	});
+	if (actions.length === 0) {
+		actions.push(value);
+	}
+	return actions.join('');
+};
+
+// 鍥炴樉鏁版嵁瀛楀吀锛堝瓧绗︿覆鏁扮粍锛�
+export const selectDictLabels = (datas: any, value: any, separator: any) => {
+	if (value === undefined || value.length === 0) {
+		return '';
+	}
+	if (Array.isArray(value)) {
+		value = value.join(',');
+	}
+	const actions: any[] = [];
+	const currentSeparator = undefined === separator ? ',' : separator;
+	const temp = value.split(currentSeparator);
+	Object.keys(value.split(currentSeparator)).some((val) => {
+		let match = false;
+		Object.keys(datas).some((key) => {
+			if (datas[key].value == '' + temp[val]) {
+				actions.push(datas[key].label + currentSeparator);
+				match = true;
+			}
+		});
+		if (!match) {
+			actions.push(temp[val] + currentSeparator);
+		}
+	});
+	return actions.join('').substring(0, actions.join('').length - 1);
+};
+
+// 瀛楃涓叉牸寮忓寲(%s )
+export function sprintf(str: string) {
+	if (arguments.length !== 0) {
+		let flag = true,
+			i = 1;
+		str = str.replace(/%s/g, function () {
+			const arg = arguments[i++];
+			if (typeof arg === 'undefined') {
+				flag = false;
+				return '';
+			}
+			return arg;
+		});
+		return flag ? str : '';
+	}
+}
+
+// 杞崲瀛楃涓诧紝undefined,null绛夎浆鍖栦负""
+export const parseStrEmpty = (str: any) => {
+	if (!str || str == 'undefined' || str == 'null') {
+		return '';
+	}
+	return str;
+};
+
+// 鏁版嵁鍚堝苟
+export const mergeRecursive = (source: any, target: any) => {
+	for (const p in target) {
+		try {
+			if (target[p].constructor == Object) {
+				source[p] = mergeRecursive(source[p], target[p]);
+			} else {
+				source[p] = target[p];
+			}
+		} catch (e) {
+			source[p] = target[p];
+		}
+	}
+	return source;
+};
+
+/**
+ * 鏋勯�犳爲鍨嬬粨鏋勬暟鎹�
+ * @param {*} data 鏁版嵁婧�
+ * @param {*} id id瀛楁 榛樿 'id'
+ * @param {*} parentId 鐖惰妭鐐瑰瓧娈� 榛樿 'parentId'
+ * @param {*} children 瀛╁瓙鑺傜偣瀛楁 榛樿 'children'
+ */
+export const handleTree = <T>(data: any[], id?: string, parentId?: string, children?: string): T[] => {
+	const config: {
+		id: string;
+		parentId: string;
+		childrenList: string;
+	} = {
+		id: id || 'id',
+		parentId: parentId || 'parentId',
+		childrenList: children || 'children'
+	};
+
+	const childrenListMap: any = {};
+	const nodeIds: any = {};
+	const tree: T[] = [];
+
+	for (const d of data) {
+		const parentId = d[config.parentId];
+		if (childrenListMap[parentId] == null) {
+			childrenListMap[parentId] = [];
+		}
+		nodeIds[d[config.id]] = d;
+		childrenListMap[parentId].push(d);
+	}
+
+	for (const d of data) {
+		const parentId = d[config.parentId];
+		if (nodeIds[parentId] == null) {
+			tree.push(d);
+		}
+	}
+	const adaptToChildrenList = (o: any) => {
+		if (childrenListMap[o[config.id]] !== null) {
+			o[config.childrenList] = childrenListMap[o[config.id]];
+		}
+		if (o[config.childrenList]) {
+			for (const c of o[config.childrenList]) {
+				adaptToChildrenList(c);
+			}
+		}
+	};
+
+	for (const t of tree) {
+		adaptToChildrenList(t);
+	}
+
+	return tree;
+};
+
+/**
+ * 鍙傛暟澶勭悊
+ * @param {*} params  鍙傛暟
+ */
+export const tansParams = (params: any) => {
+	let result = '';
+	for (const propName of Object.keys(params)) {
+		const value = params[propName];
+		const part = encodeURIComponent(propName) + '=';
+		if (value !== null && value !== '' && typeof value !== 'undefined') {
+			if (typeof value === 'object') {
+				for (const key of Object.keys(value)) {
+					if (value[key] !== null && value[key] !== '' && typeof value[key] !== 'undefined') {
+						const params = propName + '[' + key + ']';
+						const subPart = encodeURIComponent(params) + '=';
+						result += subPart + encodeURIComponent(value[key]) + '&';
+					}
+				}
+			} else {
+				result += part + encodeURIComponent(value) + '&';
+			}
+		}
+	}
+	return result;
+};
+
+// 杩斿洖椤圭洰璺緞
+export const getNormalPath = (p: string): string => {
+	if (p.length === 0 || !p || p === 'undefined') {
+		return p;
+	}
+	const res = p.replace('//', '/');
+	if (res[res.length - 1] === '/') {
+		return res.slice(0, res.length - 1);
+	}
+	return res;
+};
+
+// 楠岃瘉鏄惁涓篵lob鏍煎紡
+export const blobValidate = (data: any) => {
+	return data.type !== 'application/json';
+};
diff --git a/src/utils/scroll-to.js b/src/utils/scroll-to.js
deleted file mode 100644
index c5d8e04..0000000
--- a/src/utils/scroll-to.js
+++ /dev/null
@@ -1,58 +0,0 @@
-Math.easeInOutQuad = function(t, b, c, d) {
-  t /= d / 2
-  if (t < 1) {
-    return c / 2 * t * t + b
-  }
-  t--
-  return -c / 2 * (t * (t - 2) - 1) + b
-}
-
-// requestAnimationFrame for Smart Animating http://goo.gl/sx5sts
-var requestAnimFrame = (function() {
-  return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function(callback) { window.setTimeout(callback, 1000 / 60) }
-})()
-
-/**
- * Because it's so fucking difficult to detect the scrolling element, just move them all
- * @param {number} amount
- */
-function move(amount) {
-  document.documentElement.scrollTop = amount
-  document.body.parentNode.scrollTop = amount
-  document.body.scrollTop = amount
-}
-
-function position() {
-  return document.documentElement.scrollTop || document.body.parentNode.scrollTop || document.body.scrollTop
-}
-
-/**
- * @param {number} to
- * @param {number} duration
- * @param {Function} callback
- */
-export function scrollTo(to, duration, callback) {
-  const start = position()
-  const change = to - start
-  const increment = 20
-  let currentTime = 0
-  duration = (typeof (duration) === 'undefined') ? 500 : duration
-  var animateScroll = function() {
-    // increment the time
-    currentTime += increment
-    // find the value with the quadratic in-out easing function
-    var val = Math.easeInOutQuad(currentTime, start, change, duration)
-    // move the document.body
-    move(val)
-    // do the animation unless its over
-    if (currentTime < duration) {
-      requestAnimFrame(animateScroll)
-    } else {
-      if (callback && typeof (callback) === 'function') {
-        // the animation is done so lets callback
-        callback()
-      }
-    }
-  }
-  animateScroll()
-}
diff --git a/src/utils/scroll-to.ts b/src/utils/scroll-to.ts
new file mode 100644
index 0000000..bbb4709
--- /dev/null
+++ b/src/utils/scroll-to.ts
@@ -0,0 +1,65 @@
+const easeInOutQuad = (t: number, b: number, c: number, d: number) => {
+	t /= d / 2;
+	if (t < 1) {
+		return (c / 2) * t * t + b;
+	}
+	t--;
+	return (-c / 2) * (t * (t - 2) - 1) + b;
+};
+
+// requestAnimationFrame for Smart Animating http://goo.gl/sx5sts
+const requestAnimFrame = (function () {
+	return (
+		window.requestAnimationFrame ||
+		(window as any).webkitRequestAnimationFrame ||
+		(window as any).mozRequestAnimationFrame ||
+		function (callback) {
+			window.setTimeout(callback, 1000 / 60);
+		}
+	);
+})();
+
+/**
+ * Because it's so fucking difficult to detect the scrolling element, just move them all
+ * @param {number} amount
+ */
+const move = (amount: number) => {
+	document.documentElement.scrollTop = amount;
+	(document.body.parentNode as HTMLElement).scrollTop = amount;
+	document.body.scrollTop = amount;
+};
+
+const position = () => {
+	return document.documentElement.scrollTop || (document.body.parentNode as HTMLElement).scrollTop || document.body.scrollTop;
+};
+
+/**
+ * @param {number} to
+ * @param {number} duration
+ * @param {Function} callback
+ */
+export const scrollTo = (to: number, duration: number, callback?: any) => {
+	const start = position();
+	const change = to - start;
+	const increment = 20;
+	let currentTime = 0;
+	duration = typeof duration === 'undefined' ? 500 : duration;
+	const animateScroll = function () {
+		// increment the time
+		currentTime += increment;
+		// find the value with the quadratic in-out easing function
+		const val = easeInOutQuad(currentTime, start, change, duration);
+		// move the document.body
+		move(val);
+		// do the animation unless its over
+		if (currentTime < duration) {
+			requestAnimFrame(animateScroll);
+		} else {
+			if (callback && typeof callback === 'function') {
+				// the animation is done so lets callback
+				callback();
+			}
+		}
+	};
+	animateScroll();
+};
diff --git a/src/utils/theme.js b/src/utils/theme.js
deleted file mode 100644
index f4badc6..0000000
--- a/src/utils/theme.js
+++ /dev/null
@@ -1,49 +0,0 @@
-// 澶勭悊涓婚鏍峰紡
-export function handleThemeStyle(theme) {
-	document.documentElement.style.setProperty('--el-color-primary', theme)
-	for (let i = 1; i <= 9; i++) {
-		document.documentElement.style.setProperty(`--el-color-primary-light-${i}`, `${getLightColor(theme, i / 10)}`)
-	}
-	for (let i = 1; i <= 9; i++) {
-		document.documentElement.style.setProperty(`--el-color-primary-dark-${i}`, `${getDarkColor(theme, i / 10)}`)
-	}
-}
-
-// hex棰滆壊杞瑀gb棰滆壊
-export function hexToRgb(str) {
-	str = str.replace('#', '')
-	let hexs = str.match(/../g)
-	for (let i = 0; i < 3; i++) {
-		hexs[i] = parseInt(hexs[i], 16)
-	}
-	return hexs
-}
-
-// rgb棰滆壊杞琀ex棰滆壊
-export function rgbToHex(r, g, b) {
-	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('')}`
-}
-
-// 鍙樻祬棰滆壊鍊�
-export function getLightColor(color, level) {
-	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])
-}
-
-// 鍙樻繁棰滆壊鍊�
-export function getDarkColor(color, level) {
-	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])
-}
diff --git a/src/utils/theme.ts b/src/utils/theme.ts
new file mode 100644
index 0000000..2ec6187
--- /dev/null
+++ b/src/utils/theme.ts
@@ -0,0 +1,52 @@
+// 澶勭悊涓婚鏍峰紡
+export const handleThemeStyle = (theme: string) => {
+	document.documentElement.style.setProperty('--el-color-primary', theme);
+	for (let i = 1; i <= 9; i++) {
+		document.documentElement.style.setProperty(`--el-color-primary-light-${i}`, `${getLightColor(theme, i / 10)}`);
+	}
+	for (let i = 1; i <= 9; i++) {
+		document.documentElement.style.setProperty(`--el-color-primary-dark-${i}`, `${getDarkColor(theme, i / 10)}`);
+	}
+};
+
+// hex棰滆壊杞瑀gb棰滆壊
+export const hexToRgb = (str: string): string[] => {
+	str = str.replace('#', '');
+	const hexs = str.match(/../g);
+	for (let i = 0; i < 3; i++) {
+		if (hexs) {
+			hexs[i] = String(parseInt(hexs[i], 16));
+		}
+	}
+	return hexs ? hexs : [];
+};
+
+// rgb棰滆壊杞琀ex棰滆壊
+export const rgbToHex = (r: string, g: string, b: string) => {
+	const hexs = [Number(r).toString(16), Number(g).toString(16), Number(b).toString(16)];
+	for (let i = 0; i < 3; i++) {
+		if (hexs[i].length == 1) {
+			hexs[i] = `0${hexs[i]}`;
+		}
+	}
+	return `#${hexs.join('')}`;
+};
+
+// 鍙樻祬棰滆壊鍊�
+export const getLightColor = (color: string, level: number) => {
+	const rgb = hexToRgb(color);
+	for (let i = 0; i < 3; i++) {
+		const s = (255 - Number(rgb[i])) * level + Number(rgb[i]);
+		rgb[i] = String(Math.floor(s));
+	}
+	return rgbToHex(rgb[0], rgb[1], rgb[2]);
+};
+
+// 鍙樻繁棰滆壊鍊�
+export const getDarkColor = (color: string, level: number) => {
+	const rgb = hexToRgb(color);
+	for (let i = 0; i < 3; i++) {
+		rgb[i] = String(Math.floor(Number(rgb[i]) * (1 - level)));
+	}
+	return rgbToHex(rgb[0], rgb[1], rgb[2]);
+};
diff --git a/src/utils/validate.js b/src/utils/validate.js
deleted file mode 100644
index 702add4..0000000
--- a/src/utils/validate.js
+++ /dev/null
@@ -1,93 +0,0 @@
-/**
- * 鍒ゆ柇url鏄惁鏄痟ttp鎴杊ttps 
- * @param {string} path
- * @returns {Boolean}
- */
- export function isHttp(url) {
-  return url.indexOf('http://') !== -1 || url.indexOf('https://') !== -1
-}
-
-/**
- * 鍒ゆ柇path鏄惁涓哄閾�
- * @param {string} path
- * @returns {Boolean}
- */
- export function isExternal(path) {
-  return /^(https?:|mailto:|tel:)/.test(path)
-}
-
-/**
- * @param {string} str
- * @returns {Boolean}
- */
-export function validUsername(str) {
-  const valid_map = ['admin', 'editor']
-  return valid_map.indexOf(str.trim()) >= 0
-}
-
-/**
- * @param {string} url
- * @returns {Boolean}
- */
-export function validURL(url) {
-  const reg = /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/
-  return reg.test(url)
-}
-
-/**
- * @param {string} str
- * @returns {Boolean}
- */
-export function validLowerCase(str) {
-  const reg = /^[a-z]+$/
-  return reg.test(str)
-}
-
-/**
- * @param {string} str
- * @returns {Boolean}
- */
-export function validUpperCase(str) {
-  const reg = /^[A-Z]+$/
-  return reg.test(str)
-}
-
-/**
- * @param {string} str
- * @returns {Boolean}
- */
-export function validAlphabets(str) {
-  const reg = /^[A-Za-z]+$/
-  return reg.test(str)
-}
-
-/**
- * @param {string} email
- * @returns {Boolean}
- */
-export function validEmail(email) {
-  const reg = /^(([^<>()\[\]\\.,;:\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,}))$/
-  return reg.test(email)
-}
-
-/**
- * @param {string} str
- * @returns {Boolean}
- */
-export function isString(str) {
-  if (typeof str === 'string' || str instanceof String) {
-    return true
-  }
-  return false
-}
-
-/**
- * @param {Array} arg
- * @returns {Boolean}
- */
-export function isArray(arg) {
-  if (typeof Array.isArray === 'undefined') {
-    return Object.prototype.toString.call(arg) === '[object Array]'
-  }
-  return Array.isArray(arg)
-}
diff --git a/src/utils/validate.ts b/src/utils/validate.ts
new file mode 100644
index 0000000..c1752fa
--- /dev/null
+++ b/src/utils/validate.ts
@@ -0,0 +1,92 @@
+/**
+ * 鍒ゆ柇url鏄惁鏄痟ttp鎴杊ttps
+ * @returns {Boolean}
+ * @param url
+ */
+export const isHttp = (url: string): boolean => {
+	return url.indexOf('http://') !== -1 || url.indexOf('https://') !== -1;
+};
+
+/**
+ * 鍒ゆ柇path鏄惁涓哄閾�
+ * @param {string} path
+ * @returns {Boolean}
+ */
+export const isExternal = (path: string) => {
+	return /^(https?:|mailto:|tel:)/.test(path);
+};
+
+/**
+ * @param {string} str
+ * @returns {Boolean}
+ */
+export const validUsername = (str: string) => {
+	const valid_map = ['admin', 'editor'];
+	return valid_map.indexOf(str.trim()) >= 0;
+};
+
+/**
+ * @param {string} url
+ * @returns {Boolean}
+ */
+export const validURL = (url: string) => {
+	const reg =
+		/^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/;
+	return reg.test(url);
+};
+
+/**
+ * @param {string} str
+ * @returns {Boolean}
+ */
+export const validLowerCase = (str: string) => {
+	const reg = /^[a-z]+$/;
+	return reg.test(str);
+};
+
+/**
+ * @param {string} str
+ * @returns {Boolean}
+ */
+export const validUpperCase = (str: string) => {
+	const reg = /^[A-Z]+$/;
+	return reg.test(str);
+};
+
+/**
+ * @param {string} str
+ * @returns {Boolean}
+ */
+export const validAlphabets = (str: string) => {
+	const reg = /^[A-Za-z]+$/;
+	return reg.test(str);
+};
+
+/**
+ * @param {string} email
+ * @returns {Boolean}
+ */
+export const validEmail = (email: string) => {
+	const reg =
+		/^(([^<>()\[\]\\.,;:\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,}))$/;
+	return reg.test(email);
+};
+
+/**
+ * @param {string} str
+ * @returns {Boolean}
+ */
+export const isString = (str: any) => {
+	return typeof str === 'string' || str instanceof String;
+};
+
+/**
+ * @param {Array} arg
+ * @returns {Boolean}
+ */
+export const isArray = (arg: string | string[]) => {
+	if (typeof Array.isArray === 'undefined') {
+		return Object.prototype.toString.call(arg) === '[object Array]';
+	}
+	return Array.isArray(arg);
+};
diff --git a/src/views/demo/demo/index.vue b/src/views/demo/demo/index.vue
index 362d5b2..17c5be8 100644
--- a/src/views/demo/demo/index.vue
+++ b/src/views/demo/demo/index.vue
@@ -1,403 +1,357 @@
-<template>
-  <div class="app-container">
-    <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch">
-      <el-form-item label="key閿�" prop="testKey">
-        <el-input
-          v-model="queryParams.testKey"
-          placeholder="璇疯緭鍏ey閿�"
-          clearable
-          style="width: 200px"
-          @keyup.enter="handleQuery"
-        />
-      </el-form-item>
-      <el-form-item label="鍊�" prop="value">
-        <el-input
-          v-model="queryParams.value"
-          placeholder="璇疯緭鍏ュ��"
-          clearable
-          style="width: 200px"
-          @keyup.enter="handleQuery"
-        />
-      </el-form-item>
-      <el-form-item label="鍒涘缓鏃堕棿">
-        <el-date-picker
-          v-model="daterangeCreateTime"
-          value-format="YYYY-MM-DD HH:mm:ss"
-          type="daterange"
-          range-separator="-"
-          start-placeholder="寮�濮嬫棩鏈�"
-          end-placeholder="缁撴潫鏃ユ湡"
-          :default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"
-        ></el-date-picker>
-      </el-form-item>
-      <el-form-item>
-        <el-button type="primary" icon="search" @click="handleQuery">鎼滅储</el-button>
-        <el-button type="primary" icon="search" @click="handlePage">鎼滅储(鑷畾涔夊垎椤垫帴鍙�)</el-button>
-        <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
-      </el-form-item>
-    </el-form>
-
-    <el-row :gutter="10" class="mb8">
-      <el-col :span="1.5">
-        <el-button
-          type="primary"
-          plain
-          icon="Plus"
-          @click="handleAdd"
-          v-hasPermi="['demo:demo:add']"
-        >鏂板</el-button>
-      </el-col>
-      <el-col :span="1.5">
-        <el-button
-          type="success"
-          plain
-          icon="Edit"
-          :disabled="single"
-          @click="handleUpdate"
-          v-hasPermi="['demo:demo:edit']"
-        >淇敼</el-button>
-      </el-col>
-      <el-col :span="1.5">
-        <el-button
-          type="danger"
-          plain
-          icon="Delete"
-          :disabled="multiple"
-          @click="handleDelete"
-          v-hasPermi="['demo:demo:remove']"
-        >鍒犻櫎</el-button>
-      </el-col>
-      <el-col :span="1.5">
-        <el-button
-          type="info"
-          plain
-          icon="Upload"
-          @click="handleImport"
-          v-hasPermi="['demo:demo:import']"
-        >瀵煎叆(鏍¢獙)</el-button>
-      </el-col>
-      <el-col :span="1.5">
-        <el-button
-          type="warning"
-          plain
-          icon="Download"
-          @click="handleExport"
-          v-hasPermi="['demo:demo:export']"
-        >瀵煎嚭</el-button>
-      </el-col>
-      <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
-    </el-row>
-
-    <el-table v-loading="loading" :data="demoList" @selection-change="handleSelectionChange">
-      <el-table-column type="selection" width="55" align="center" />
-      <el-table-column label="涓婚敭" align="center" prop="id" v-if="columns[0].visible"/>
-      <el-table-column label="閮ㄩ棬id" align="center" prop="deptId" v-if="columns[1].visible"/>
-      <el-table-column label="鐢ㄦ埛id" align="center" prop="userId" v-if="columns[2].visible"/>
-      <el-table-column label="鎺掑簭鍙�" align="center" prop="orderNum" v-if="columns[3].visible"/>
-      <el-table-column label="key閿�" align="center" prop="testKey" v-if="columns[4].visible"/>
-      <el-table-column label="鍊�" align="center" prop="value" v-if="columns[5].visible"/>
-      <el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime" v-if="columns[6].visible" width="180">
-        <template #default="scope">
-          <span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>
-        </template>
-      </el-table-column>
-      <el-table-column label="鍒涘缓浜�" align="center" prop="createByName" v-if="columns[7].visible" />
-      <el-table-column label="鏇存柊鏃堕棿" align="center" prop="updateTime" v-if="columns[8].visible" width="180">
-        <template #default="scope">
-          <span>{{ parseTime(scope.row.updateTime, '{y}-{m}-{d}') }}</span>
-        </template>
-      </el-table-column>
-      <el-table-column label="鏇存柊浜�" align="center" prop="updateByName" v-if="columns[9].visible" />
-      <el-table-column label="鎿嶄綔" align="center" width="150" class-name="small-padding fixed-width">
-        <template #default="scope">
-          <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['demo:demo:edit']">淇敼</el-button>
-          <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['demo:demo:remove']">鍒犻櫎</el-button>
-        </template>
-      </el-table-column>
-    </el-table>
-
-    <pagination
-      v-show="total > 0"
-      :total="total"
-      v-model:page="queryParams.pageNum"
-      v-model:limit="queryParams.pageSize"
-      @pagination="getList"
-    />
-
-    <!-- 娣诲姞鎴栦慨鏀规祴璇曞崟琛ㄥ璇濇 -->
-    <el-dialog :title="title" v-model="open" width="500px" append-to-body>
-      <el-form ref="demoRef" :model="form" :rules="rules" label-width="80px">
-        <el-form-item label="閮ㄩ棬id" prop="deptId">
-          <el-input v-model="form.deptId" placeholder="璇疯緭鍏ラ儴闂╥d" />
-        </el-form-item>
-        <el-form-item label="鐢ㄦ埛id" prop="userId">
-          <el-input v-model="form.userId" placeholder="璇疯緭鍏ョ敤鎴穒d" />
-        </el-form-item>
-        <el-form-item label="鎺掑簭鍙�" prop="orderNum">
-          <el-input v-model="form.orderNum" placeholder="璇疯緭鍏ユ帓搴忓彿" />
-        </el-form-item>
-        <el-form-item label="key閿�" prop="testKey">
-          <el-input v-model="form.testKey" placeholder="璇疯緭鍏ey閿�" />
-        </el-form-item>
-        <el-form-item label="鍊�" prop="value">
-          <el-input v-model="form.value" placeholder="璇疯緭鍏ュ��" />
-        </el-form-item>
-        <el-form-item label="鍒涘缓鏃堕棿" prop="createTime">
-          <el-date-picker clearable
-                          v-model="form.createTime"
-                          type="datetime"
-                          value-format="YYYY-MM-DD HH:mm:ss"
-                          placeholder="閫夋嫨鍒涘缓鏃堕棿">
-          </el-date-picker>
-        </el-form-item>
-      </el-form>
-      <template #footer>
-        <div class="dialog-footer">
-          <el-button :loading="buttonLoading" type="primary" @click="submitForm">纭� 瀹�</el-button>
-          <el-button @click="cancel">鍙� 娑�</el-button>
-        </div>
-      </template>
-    </el-dialog>
-    <!-- 鐢ㄦ埛瀵煎叆瀵硅瘽妗� -->
-    <el-dialog :title="upload.title" :visible.sync="upload.open" width="400px" append-to-body>
-      <el-upload
-        ref="uploadRef"
-        :limit="1"
-        accept=".xlsx, .xls"
-        :headers="upload.headers"
-        :action="upload.url + '?updateSupport=' + upload.updateSupport"
-        :disabled="upload.isUploading"
-        :on-progress="handleFileUploadProgress"
-        :on-success="handleFileSuccess"
-        :auto-upload="false"
-        drag
-      >
-        <i class="el-icon-upload"></i>
-        <div class="el-upload__text">灏嗘枃浠舵嫋鍒版澶勶紝鎴�<em>鐐瑰嚮涓婁紶</em></div>
-      </el-upload>
-      <div slot="footer" class="dialog-footer">
-        <el-button type="primary" @click="submitFileForm">纭� 瀹�</el-button>
-        <el-button @click="upload.open = false">鍙� 娑�</el-button>
-      </div>
-    </el-dialog>
-  </div>
-</template>
-
-<script setup name="Demo">
+<script setup name="Demo" lang="ts">
 import { listDemo, pageDemo, getDemo, delDemo, addDemo, updateDemo } from "@/api/demo/demo";
-import {getToken} from "@/utils/auth";
+import { getToken } from "@/utils/auth";
+import { ComponentInternalInstance } from "vue";
+import { ElUpload, UploadFile, UploadFiles, DateModelType } from 'element-plus';
+import { DemoForm, DemoQuery, DemoVO } from "@/api/demo/types";
 
-const { proxy } = getCurrentInstance();
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 
-const demoList = ref([]);
-const open = ref(false);
+const demoList = ref<DemoVO[]>([]);
 const buttonLoading = ref(false);
 const loading = ref(true);
 const showSearch = ref(true);
-const ids = ref([]);
+const ids = ref<Array<string | number>>([])
 const single = ref(true);
 const multiple = ref(true);
 const total = ref(0);
-const title = ref("");
-const daterangeCreateTime = ref([]);
+const daterangeCreateTime = ref<[DateModelType, DateModelType]>(['', '']);
 
-/*** 鐢ㄦ埛瀵煎叆鍙傛暟 */
-const upload = reactive({
-  // 鏄惁鏄剧ず寮瑰嚭灞傦紙鐢ㄦ埛瀵煎叆锛�
-  open: false,
-  // 寮瑰嚭灞傛爣棰橈紙鐢ㄦ埛瀵煎叆锛�
-  title: "",
-  // 鏄惁绂佺敤涓婁紶
-  isUploading: false,
-  // 璁剧疆涓婁紶鐨勮姹傚ご閮�
-  headers: { Authorization: "Bearer " + getToken() },
-  // 涓婁紶鐨勫湴鍧�
-  url: import.meta.env.VITE_APP_BASE_API + "demo/demo/importData"
+const demoFormRef = ref(ElForm);
+const queryFormRef = ref(ElForm);
+const uploadRef = ref(ElUpload);
+
+const dialog = reactive<DialogOption>({
+	visible: false,
+	title: ''
 });
 
+/** 鐢ㄦ埛瀵煎叆鍙傛暟 */
+const upload = reactive<ImportOption>({
+	// 鏄惁鏄剧ず寮瑰嚭灞傦紙鐢ㄦ埛瀵煎叆锛�
+	open: false,
+	// 寮瑰嚭灞傛爣棰橈紙鐢ㄦ埛瀵煎叆锛�
+	title: "",
+	// 鏄惁绂佺敤涓婁紶
+	isUploading: false,
+	// 璁剧疆涓婁紶鐨勮姹傚ご閮�
+	headers: { Authorization: "Bearer " + getToken() },
+	// 涓婁紶鐨勫湴鍧�
+	url: import.meta.env.VITE_APP_BASE_API + "demo/demo/importData"
+})
+
 // 鍒楁樉闅愪俊鎭�
-const columns = ref([
-  { key: 0, label: `涓婚敭`, visible: false },
-  { key: 1, label: `閮ㄩ棬id`, visible: true },
-  { key: 2, label: `鐢ㄦ埛id`, visible: true },
-  { key: 3, label: `鎺掑簭鍙穈, visible: true },
-  { key: 4, label: `key閿甡, visible: true },
-  { key: 5, label: `鍊糮, visible: true },
-  { key: 6, label: `鍒涘缓鏃堕棿`, visible: true },
-  { key: 7, label: `鍒涘缓浜篳, visible: true },
-  { key: 8, label: `鏇存柊鏃堕棿`, visible: true },
-  { key: 9, label: `鏇存柊浜篳, visible: true }
+const columns = ref<FieldOption[]>([
+	{ key: 0, label: `涓婚敭`, visible: false },
+	{ key: 1, label: `閮ㄩ棬id`, visible: true },
+	{ key: 2, label: `鐢ㄦ埛id`, visible: true },
+	{ key: 3, label: `鎺掑簭鍙穈, visible: true },
+	{ key: 4, label: `key閿甡, visible: true },
+	{ key: 5, label: `鍊糮, visible: true },
+	{ key: 6, label: `鍒涘缓鏃堕棿`, visible: true },
+	{ key: 7, label: `鍒涘缓浜篳, visible: true },
+	{ key: 8, label: `鏇存柊鏃堕棿`, visible: true },
+	{ key: 9, label: `鏇存柊浜篳, visible: true }
 ]);
 
-const data = reactive({
-  form: {},
-  queryParams: {
-    pageNum: 1,
-    pageSize: 10,
-    testKey: undefined,
-    value: undefined,
-    createTime: undefined,
-  },
-  rules: {
-    testKey: [
-      { required: true, message: "key閿笉鑳戒负绌�", trigger: "blur" }
-    ],
-    value: [
-      { required: true, message: "鍊间笉鑳戒负绌�", trigger: "blur" }
-    ],
-  }
+const initDataForm: DemoForm = {
+	id: undefined,
+	deptId: undefined,
+	userId: undefined,
+	orderNum: 0,
+	testKey: '',
+	value: '',
+	version: '',
+	ossConfigId: undefined,
+}
+const data = reactive<PageData<DemoForm, DemoQuery>>({
+	form: { ...initDataForm },
+	queryParams: {
+		pageNum: 1,
+		pageSize: 10,
+		testKey: '',
+		value: '',
+		createTime: '',
+	},
+	rules: {
+		testKey: [{ required: true, message: "key閿笉鑳戒负绌�", trigger: "blur" }],
+		value: [{ required: true, message: "鍊间笉鑳戒负绌�", trigger: "blur" }],
+	}
 });
 
 const { queryParams, form, rules } = toRefs(data);
 
 /** 鏌ヨOSS瀵硅薄瀛樺偍鍒楄〃 */
-function getList() {
-  loading.value = true;
-  listDemo(proxy.addDateRange(queryParams.value, daterangeCreateTime.value, "CreateTime")).then(response => {
-    demoList.value = response.rows;
-    total.value = response.total;
-    loading.value = false;
-  });
+const getList = async () => {
+	loading.value = true;
+	const res = await listDemo(proxy?.addDateRange(queryParams.value, daterangeCreateTime.value, "CreateTime"));
+	demoList.value = res.rows;
+	total.value = res.total;
+	loading.value = false;
 }
 /** 鑷畾涔夊垎椤垫煡璇� */
-function getPage() {
-  loading.value = true;
-  pageDemo(proxy.addDateRange(queryParams.value, daterangeCreateTime.value, "CreateTime")).then(response => {
-    demoList.value = response.rows;
-    total.value = response.total;
-    loading.value = false;
-  });
+const getPage = async () => {
+	loading.value = true;
+	const res = await pageDemo(proxy?.addDateRange(queryParams.value, daterangeCreateTime.value, "CreateTime"));
+	demoList.value = res.rows;
+	total.value = res.total;
+	loading.value = false;
 }
 /** 鍙栨秷鎸夐挳 */
-function cancel() {
-  open.value = false;
-  reset();
+const cancel = () => {
+	reset();
+	dialog.visible = false;
 }
 /** 琛ㄥ崟閲嶇疆 */
-function reset() {
-  form.value = {
-    id: undefined,
-    deptId: undefined,
-    userId: undefined,
-    orderNum: undefined,
-    testKey: undefined,
-    value: undefined,
-    version: undefined,
-    createTime: undefined,
-    createBy: undefined,
-    updateTime: undefined,
-    updateBy: undefined,
-    delFlag: undefined
-  };
-  proxy.resetForm("demoRef");
+const reset = () => {
+	form.value = { ...initDataForm };
+	demoFormRef.value.resetFields();
 }
 /** 鎼滅储鎸夐挳鎿嶄綔 */
-function handleQuery() {
-  queryParams.value.pageNum = 1;
-  getList();
+const handleQuery = () => {
+	queryParams.value.pageNum = 1;
+	getList();
 }
 /** 鎼滅储鎸夐挳鎿嶄綔 */
-function handlePage() {
-  queryParams.value.pageNum = 1;
-  getList();
+const handlePage = () => {
+	queryParams.value.pageNum = 1;
+	getList();
 }
 /** 閲嶇疆鎸夐挳鎿嶄綔 */
-function resetQuery() {
-  daterangeCreateTime.value = [];
-  proxy.resetForm("queryRef");
-  handleQuery();
+const resetQuery = () => {
+	daterangeCreateTime.value = ['', ''];
+	queryFormRef.value.resetFields();
+	handleQuery();
 }
 /** 閫夋嫨鏉℃暟  */
-function handleSelectionChange(selection) {
-  ids.value = selection.map(item => item.id);
-  single.value = selection.length != 1;
-  multiple.value = !selection.length;
+const handleSelectionChange = (selection: DemoVO[]) => {
+	ids.value = selection.map(item => item.id);
+	single.value = selection.length != 1;
+	multiple.value = !selection.length;
 }
 /** 鏂板鎸夐挳鎿嶄綔 */
-function handleAdd() {
-  reset();
-  open.value = true;
-  title.value = "娣诲姞娴嬭瘯鍗曡〃";
+const handleAdd = () => {
+	dialog.visible = true;
+	dialog.title = "娣诲姞娴嬭瘯鍗曡〃";
+	nextTick(() => {
+		reset();
+	})
 }
 /** 淇敼鎸夐挳鎿嶄綔 */
-function handleUpdate(row) {
-  loading.value = true;
-  reset();
-  const ids = row.id || ids.value;
-  getDemo(ids).then((response) => {
-    loading.value = false;
-    form.value = response.data;
-    open.value = true;
-    title.value = "淇敼娴嬭瘯鍗曡〃";
-  });
+const handleUpdate = async (row?: DemoVO) => {
+	loading.value = true;
+	dialog.visible = true;
+	dialog.title = "淇敼娴嬭瘯鍗曡〃";
+	const _ids = row?.id || ids.value[0];
+	const res = await getDemo(_ids);
+	nextTick(() => {
+		reset();
+		Object.assign(form.value, res.data)
+		loading.value = false;
+	})
 }
 /** 鎻愪氦鎸夐挳 */
-function submitForm() {
-  proxy.$refs["demoRef"].validate(valid => {
-    if (valid) {
-      buttonLoading.value = true;
-      if (form.value.ossConfigId != null) {
-        updateDemo(form.value).then(response => {
-          proxy.$modal.msgSuccess("淇敼鎴愬姛");
-          open.value = false;
-          getList();
-        }).finally(() => {
-          buttonLoading.value = false;
-        });
-      } else {
-        addDemo(form.value).then(response => {
-          proxy.$modal.msgSuccess("鏂板鎴愬姛");
-          open.value = false;
-          getList();
-        }).finally(() => {
-          buttonLoading.value = false;
-        });
-      }
-    }
-  });
+const submitForm = () => {
+	demoFormRef.value.validate(async (valid: boolean) => {
+		if (valid) {
+			buttonLoading.value = true;
+			if (form.value.ossConfigId) {
+				await updateDemo(form.value).finally(() => buttonLoading.value = false);
+			} else {
+				await addDemo(form.value).finally(() => buttonLoading.value = false);
+			}
+			proxy?.$modal.msgSuccess("鎿嶄綔鎴愬姛");
+			dialog.visible = false;
+			getList();
+		}
+	});
 }
 /** 鍒犻櫎鎸夐挳鎿嶄綔 */
-function handleDelete(row) {
-  const ids = row.id || ids.value;
-  proxy.$modal.confirm('鏄惁纭鍒犻櫎娴嬭瘯鍗曡〃缂栧彿涓�"' + ids + '"鐨勬暟鎹」?').then(() => {
-    loading.value = true;
-    return delDemo(ids);
-  }).then(() => {
-    loading.value = false;
-    getList();
-    proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
-  }).finally(() => {
-    loading.value = false;
-  });
+const handleDelete = async (row?: DemoVO) => {
+	const _ids = row?.id || ids.value;
+	await proxy?.$modal.confirm('鏄惁纭鍒犻櫎娴嬭瘯鍗曡〃缂栧彿涓�"' + _ids + '"鐨勬暟鎹」?');
+	await delDemo(_ids).finally(() => loading.value = false);
+	loading.value = false;
+	getList();
+	proxy?.$modal.msgSuccess("鍒犻櫎鎴愬姛");
 }
 /** 瀵煎叆鎸夐挳鎿嶄綔 */
-function handleImport() {
-  upload.title = "娴嬭瘯瀵煎叆";
-  upload.open = true;
+const handleImport = () => {
+	upload.title = "娴嬭瘯瀵煎叆";
+	upload.open = true;
 }
 /** 瀵煎嚭鎸夐挳鎿嶄綔 */
-function handleExport() {
-  proxy.download("demo/demo/export", {
-    ...queryParams.value,
-  },`demo_${new Date().getTime()}.xlsx`);
+const handleExport = () => {
+	proxy?.download("demo/demo/export", {
+		...queryParams.value,
+	}, `demo_${new Date().getTime()}.xlsx`);
 }
 /**鏂囦欢涓婁紶涓鐞� */
-const handleFileUploadProgress = (event, file, fileList) => {
-  upload.isUploading = true;
+const handleFileUploadProgress = () => {
+	upload.isUploading = true;
 }
 /** 鏂囦欢涓婁紶鎴愬姛澶勭悊 */
-const handleFileSuccess = (response, file, fileList) => {
-  upload.open = false;
-  upload.isUploading = false;
-  proxy.$refs["uploadRef"].clearFiles();
-  proxy.$alert(response.msg, "瀵煎叆缁撴灉", { dangerouslyUseHTMLString: true });
-  getList();
+const handleFileSuccess = (res: any, file: UploadFile, fileList: UploadFiles) => {
+	upload.open = false;
+	upload.isUploading = false;
+	uploadRef.value.clearFiles();
+	ElMessageBox.alert(res.msg, "瀵煎叆缁撴灉", { dangerouslyUseHTMLString: true });
+	getList();
 }
 /** 鎻愪氦涓婁紶鏂囦欢 */
 function submitFileForm() {
-  proxy.$refs["uploadRef"].submit();
+	uploadRef.value.submit();
 }
 
-getList()
-getPage()
+onMounted(() => {
+	getList()
+	getPage()
+})
 </script>
+
+<template>
+	<div class="p-2">
+		<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
+			<div class="search" v-show="showSearch">
+				<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
+					<el-form-item label="key閿�" prop="testKey">
+						<el-input v-model="queryParams.testKey" placeholder="璇疯緭鍏ey閿�" clearable style="width: 200px" @keyup.enter="handleQuery" />
+					</el-form-item>
+					<el-form-item label="鍊�" prop="value">
+						<el-input v-model="queryParams.value" placeholder="璇疯緭鍏ュ��" clearable style="width: 200px" @keyup.enter="handleQuery" />
+					</el-form-item>
+					<el-form-item label="鍒涘缓鏃堕棿">
+						<el-date-picker
+							v-model="daterangeCreateTime"
+							value-format="YYYY-MM-DD HH:mm:ss"
+							type="daterange"
+							range-separator="-"
+							start-placeholder="寮�濮嬫棩鏈�"
+							end-placeholder="缁撴潫鏃ユ湡"
+							:default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"
+						></el-date-picker>
+					</el-form-item>
+					<el-form-item>
+						<el-button type="primary" icon="search" @click="handleQuery">鎼滅储</el-button>
+						<el-button type="primary" icon="search" @click="handlePage">鎼滅储(鑷畾涔夊垎椤垫帴鍙�)</el-button>
+						<el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
+					</el-form-item>
+				</el-form>
+			</div>
+		</transition>
+
+		<el-card shadow="never">
+			<template #header>
+				<el-row :gutter="10" class="mb8">
+					<el-col :span="1.5">
+						<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['demo:demo:add']">鏂板</el-button>
+					</el-col>
+					<el-col :span="1.5">
+						<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['demo:demo:edit']">淇敼</el-button>
+					</el-col>
+					<el-col :span="1.5">
+						<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['demo:demo:remove']">
+							鍒犻櫎
+						</el-button>
+					</el-col>
+					<el-col :span="1.5">
+						<el-button type="info" plain icon="Upload" @click="handleImport" v-hasPermi="['demo:demo:import']">瀵煎叆(鏍¢獙)</el-button>
+					</el-col>
+					<el-col :span="1.5">
+						<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['demo:demo:export']">瀵煎嚭</el-button>
+					</el-col>
+					<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+				</el-row>
+			</template>
+
+			<el-table v-loading="loading" :data="demoList" @selection-change="handleSelectionChange">
+				<el-table-column type="selection" width="55" align="center" />
+				<el-table-column label="涓婚敭" align="center" prop="id" v-if="columns[0].visible" />
+				<el-table-column label="閮ㄩ棬id" align="center" prop="deptId" v-if="columns[1].visible" />
+				<el-table-column label="鐢ㄦ埛id" align="center" prop="userId" v-if="columns[2].visible" />
+				<el-table-column label="鎺掑簭鍙�" align="center" prop="orderNum" v-if="columns[3].visible" />
+				<el-table-column label="key閿�" align="center" prop="testKey" v-if="columns[4].visible" />
+				<el-table-column label="鍊�" align="center" prop="value" v-if="columns[5].visible" />
+				<el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime" v-if="columns[6].visible" width="180">
+					<template #default="scope">
+						<span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>
+					</template>
+				</el-table-column>
+				<el-table-column label="鍒涘缓浜�" align="center" prop="createByName" v-if="columns[7].visible" />
+				<el-table-column label="鏇存柊鏃堕棿" align="center" prop="updateTime" v-if="columns[8].visible" width="180">
+					<template #default="scope">
+						<span>{{ parseTime(scope.row.updateTime, '{y}-{m}-{d}') }}</span>
+					</template>
+				</el-table-column>
+				<el-table-column label="鏇存柊浜�" align="center" prop="updateByName" v-if="columns[9].visible" />
+				<el-table-column label="鎿嶄綔" fixed="right" align="center" width="150" class-name="small-padding fixed-width">
+					<template #default="scope">
+						<el-tooltip content="淇敼" placement="top">
+							<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['demo:demo:edit']"></el-button>
+						</el-tooltip>
+						<el-tooltip content="淇敼" placement="top">
+							<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['demo:demo:remove']"></el-button>
+						</el-tooltip>
+					</template>
+				</el-table-column>
+			</el-table>
+
+			<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
+		</el-card>
+
+		<!-- 娣诲姞鎴栦慨鏀规祴璇曞崟琛ㄥ璇濇 -->
+		<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
+			<el-form ref="demoFormRef" :model="form" :rules="rules" label-width="80px">
+				<el-form-item label="閮ㄩ棬id" prop="deptId">
+					<el-input v-model="form.deptId" placeholder="璇疯緭鍏ラ儴闂╥d" />
+				</el-form-item>
+				<el-form-item label="鐢ㄦ埛id" prop="userId">
+					<el-input v-model="form.userId" placeholder="璇疯緭鍏ョ敤鎴穒d" />
+				</el-form-item>
+				<el-form-item label="鎺掑簭鍙�" prop="orderNum">
+					<el-input v-model="form.orderNum" placeholder="璇疯緭鍏ユ帓搴忓彿" />
+				</el-form-item>
+				<el-form-item label="key閿�" prop="testKey">
+					<el-input v-model="form.testKey" placeholder="璇疯緭鍏ey閿�" />
+				</el-form-item>
+				<el-form-item label="鍊�" prop="value">
+					<el-input v-model="form.value" placeholder="璇疯緭鍏ュ��" />
+				</el-form-item>
+				<el-form-item label="鍒涘缓鏃堕棿" prop="createTime">
+					<el-date-picker clearable v-model="form.createTime" type="datetime" value-format="YYYY-MM-DD HH:mm:ss" placeholder="閫夋嫨鍒涘缓鏃堕棿">
+					</el-date-picker>
+				</el-form-item>
+			</el-form>
+			<template #footer>
+				<div class="dialog-footer">
+					<el-button :loading="buttonLoading" type="primary" @click="submitForm">纭� 瀹�</el-button>
+					<el-button @click="cancel">鍙� 娑�</el-button>
+				</div>
+			</template>
+		</el-dialog>
+		<!-- 鐢ㄦ埛瀵煎叆瀵硅瘽妗� -->
+		<el-dialog :title="upload.title" v-model="upload.open" width="400px" append-to-body>
+			<el-upload
+				ref="uploadRef"
+				:limit="1"
+				accept=".xlsx, .xls"
+				:headers="upload.headers"
+				:action="upload.url + '?updateSupport=' + upload.updateSupport"
+				:disabled="upload.isUploading"
+				:on-progress="handleFileUploadProgress"
+				:on-success="handleFileSuccess"
+				:auto-upload="false"
+				drag
+			>
+				<i class="el-icon-upload"></i>
+				<div class="el-upload__text">灏嗘枃浠舵嫋鍒版澶勶紝鎴�<em>鐐瑰嚮涓婁紶</em></div>
+			</el-upload>
+			<template #footer>
+				<div class="dialog-footer">
+					<el-button type="primary" @click="submitFileForm">纭� 瀹�</el-button>
+					<el-button @click="upload.open = false">鍙� 娑�</el-button>
+				</div>
+			</template>
+		</el-dialog>
+	</div>
+</template>
diff --git a/src/views/demo/tree/index.vue b/src/views/demo/tree/index.vue
index 4fa8ffd..f3f13e1 100644
--- a/src/views/demo/tree/index.vue
+++ b/src/views/demo/tree/index.vue
@@ -1,131 +1,31 @@
-<template>
-  <div class="app-container">
-    <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch">
-      <el-form-item label="鏍戣妭鐐瑰悕" prop="treeName">
-        <el-input
-          v-model="queryParams.treeName"
-          placeholder="璇疯緭鍏ユ爲鑺傜偣鍚�"
-          clearable
-          style="width: 200px"
-          @keyup.enter="handleQuery"
-        />
-      </el-form-item>
-      <el-form-item label="鍒涘缓鏃堕棿">
-        <el-date-picker
-          v-model="daterangeCreateTime"
-          value-format="YYYY-MM-DD HH:mm:ss"
-          type="daterange"
-          range-separator="-"
-          start-placeholder="寮�濮嬫棩鏈�"
-          end-placeholder="缁撴潫鏃ユ湡"
-          :default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"
-        ></el-date-picker>
-      </el-form-item>
-      <el-form-item>
-        <el-button type="primary" icon="search" @click="handleQuery">鎼滅储</el-button>
-        <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
-      </el-form-item>
-    </el-form>
+<script setup name="Tree" lang="ts">
+import { listTree, getTree, delTree, addTree, updateTree } from '@/api/demo/tree';
+import { DemoTreeVO, DemoTreeForm, DemoTreeOptionsType, DemoTreeQuery } from '@/api/demo/types';
+import { ComponentInternalInstance } from 'vue';
+import { ElTree, ElForm, ElTable, DateModelType } from 'element-plus';
 
-    <el-row :gutter="10" class="mb8">
-      <el-col :span="1.5">
-        <el-button
-          type="primary"
-          plain
-          icon="Plus"
-          @click="handleAdd"
-          v-hasPermi="['demo:tree:add']"
-        >鏂板</el-button>
-      </el-col>
-      <el-col :span="1.5">
-        <el-button
-            type="info"
-            plain
-            icon="Sort"
-            @click="toggleExpandAll"
-        >灞曞紑/鎶樺彔</el-button>
-      </el-col>
-      <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
-    </el-row>
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 
-    <el-table
-      v-if="refreshTable"
-      v-loading="loading"
-      :data="treeList"
-      row-key="id"
-      :default-expand-all="isExpandAll"
-      :tree-props="{children: 'children', hasChildren: 'hasChildren'}"
-    >
-      <el-table-column label="鐖秈d" prop="parentId" v-if="columns[0].visible" />
-      <el-table-column label="閮ㄩ棬id" align="center" prop="deptId" v-if="columns[1].visible" />
-      <el-table-column label="鐢ㄦ埛id" align="center" prop="userId" v-if="columns[2].visible" />
-      <el-table-column label="鏍戣妭鐐瑰悕" align="center" prop="treeName" v-if="columns[3].visible" />
-      <el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime" v-if="columns[4].visible" width="180">
-        <template #default="scope">
-          <span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>
-        </template>
-      </el-table-column>
-      <el-table-column label="鎿嶄綔" align="center" width="150" class-name="small-padding fixed-width">
-        <template #default="scope">
-          <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['demo:tree:edit']">淇敼</el-button>
-          <el-button link type="primary" icon="Plus" @click="handleAdd(scope.row)" v-hasPermi="['demo:tree:add']">鏂板</el-button>
-          <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['demo:tree:remove']">鍒犻櫎</el-button>
-        </template>
-      </el-table-column>
-    </el-table>
-
-    <!-- 娣诲姞鎴栦慨鏀规祴璇曟爲琛ㄥ璇濇 -->
-    <el-dialog :title="title" v-model="open" width="500px" append-to-body>
-      <el-form ref="treeRef" :model="form" :rules="rules" label-width="80px">
-        <el-form-item label="鐖秈d" prop="parentId">
-          <treeselect
-              v-model:value="form.parentId"
-              :options="treeOptions"
-              :objMap="{ value: 'id', label: 'true_name', children: 'children' }"
-              placeholder="璇烽�夋嫨鐖秈d"
-          />
-        </el-form-item>
-        <el-form-item label="閮ㄩ棬id" prop="deptId">
-          <el-input v-model="form.deptId" placeholder="璇疯緭鍏ラ儴闂╥d" />
-        </el-form-item>
-        <el-form-item label="鐢ㄦ埛id" prop="userId">
-          <el-input v-model="form.userId" placeholder="璇疯緭鍏ョ敤鎴穒d" />
-        </el-form-item>
-        <el-form-item label="鏍戣妭鐐瑰悕" prop="treeName">
-          <el-input v-model="form.treeName" placeholder="璇疯緭鍏ユ爲鑺傜偣鍚�" />
-        </el-form-item>
-      </el-form>
-      <template #footer>
-        <div class="dialog-footer">
-          <el-button :loading="buttonLoading" type="primary" @click="submitForm">纭� 瀹�</el-button>
-          <el-button @click="cancel">鍙� 娑�</el-button>
-        </div>
-      </template>
-    </el-dialog>
-  </div>
-</template>
-
-<script setup name="Tree">
-import { listTree, getTree, delTree, addTree, updateTree } from "@/api/demo/tree";
-
-const { proxy } = getCurrentInstance();
-
-const treeList = ref([]);
-const open = ref(false);
+const treeList = ref<DemoTreeVO[]>([]);
 const buttonLoading = ref(false);
 const loading = ref(true);
 const showSearch = ref(true);
-const single = ref(true);
-const multiple = ref(true);
-const total = ref(0);
-const title = ref("");
 const isExpandAll = ref(true);
 const refreshTable = ref(true);
-const treeOptions = ref(undefined);
-const daterangeCreateTime = ref([]);
+const treeOptions = ref<DemoTreeOptionsType[]>([]);
+const daterangeCreateTime = ref<[DateModelType, DateModelType]>(['', '']);
+
+const treeRef = ref(ElTree);
+const qeuryFormRef = ref(ElForm);
+const demoTreeTableRef = ref(ElTable)
+
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
 
 // 鍒楁樉闅愪俊鎭�
-const columns = ref([
+const columns = ref<FieldOption[]>([
   { key: 0, label: `鐖秈d`, visible: false },
   { key: 1, label: `閮ㄩ棬id`, visible: true },
   { key: 2, label: `鐢ㄦ埛id`, visible: true },
@@ -133,128 +33,132 @@
   { key: 4, label: `鍒涘缓鏃堕棿`, visible: true }
 ]);
 
-const data = reactive({
+const initFormData = {
+  id: undefined,
+  parentId: undefined,
+  deptId: undefined,
+  userId: undefined,
+  treeName: ''
+}
+
+const data = reactive<PageData<DemoTreeForm, DemoTreeQuery>>({
   // 鏌ヨ鍙傛暟
   queryParams: {
-    treeName: null,
-    createTime: null,
+    treeName: '',
+    createTime: '',
   },
   // 琛ㄥ崟鍙傛暟
-  form: {},
+  form: {...initFormData},
   // 琛ㄥ崟鏍¢獙
   rules: {
-    treeName: [
-      { required: true, message: "鏍戣妭鐐瑰悕涓嶈兘涓虹┖", trigger: "blur" }
-    ],
+    treeName: [{ required: true, message: "鏍戣妭鐐瑰悕涓嶈兘涓虹┖", trigger: "blur" }],
   }
 });
 
 const { queryParams, form, rules } = toRefs(data);
-
 /** 鏌ヨ娴嬭瘯鏍戣〃鍒楄〃 */
-function getList() {
+const getList = () => {
   loading.value = true;
-  listTree(proxy.addDateRange(queryParams.value, daterangeCreateTime.value, "CreateTime")).then(response => {
-    treeList.value = proxy.handleTree(response.data, "id", "parentId");
-    total.value = response.total;
+  listTree(proxy?.addDateRange(queryParams.value, daterangeCreateTime.value, "CreateTime")).then(res => {
+    const data = proxy?.handleTree<DemoTreeVO>(res.data, "id", "parentId");
+    if (data) {
+      treeList.value = data
+    }
     loading.value = false;
   });
 }
+
 /** 鏌ヨ閮ㄩ棬涓嬫媺鏍戠粨鏋� */
-async function getTreeselect() {
-  await listTree().then(response => {
-    treeOptions.value = [];
-    const data = { id: 0, treeName: '椤剁骇鑺傜偣', children: [] };
-    data.children = proxy.handleTree(response.data, "id", "parentId");
-    treeOptions.value.push(data);
+const getTreeSelect = async () => {
+  listTree(proxy?.addDateRange(queryParams.value, daterangeCreateTime.value, "CreateTime")).then(res => {
+  const topData: DemoTreeOptionsType = { id: 0, treeName: '椤剁骇鑺傜偣', children: [] };
+  topData.children = proxy?.handleTree<DemoTreeOptionsType>(res.data, "id", "parentId");
+  treeOptions.value.push(topData);
   });
 }
 /** 鍙栨秷鎸夐挳 */
-function cancel() {
-  open.value = false;
+const cancel = () => {
   reset();
+  dialog.visible = false;
 }
 /** 琛ㄥ崟閲嶇疆 */
-function reset() {
-  form.value = {
-    id: undefined,
-    parentId: undefined,
-    deptId: undefined,
-    userId: undefined,
-    treeName: undefined,
-    version: undefined,
-    createTime: undefined,
-    createBy: undefined,
-    updateTime: undefined,
-    updateBy: undefined,
-    delFlag: undefined
-  };
-  proxy.resetForm("treeRef");
+const reset = () => {
+  form.value = {...initFormData}
+  treeRef.value.resetFields();
 }
 /** 鎼滅储鎸夐挳鎿嶄綔 */
-function handleQuery() {
+const handleQuery = () => {
   getList();
 }
 /** 閲嶇疆鎸夐挳鎿嶄綔 */
-function resetQuery() {
-  daterangeCreateTime.value = [];
-  proxy.resetForm("queryRef");
+const resetQuery = () => {
+  daterangeCreateTime.value = ['', ''];
+  qeuryFormRef.value.resetFields();
   handleQuery();
 }
 /** 鏂板鎸夐挳鎿嶄綔 */
-async function handleAdd(row) {
-  reset();
-  await getTreeselect();
-  if (row != null && row.id) {
-    form.value.parentId = row.id;
-  } else {
-    form.value.parentId = 0;
-  }
-  open.value = true;
-  title.value = "娣诲姞娴嬭瘯鏍戣〃";
+const handleAdd = (row?: DemoTreeVO) => {
+  dialog.visible = true;
+  dialog.title = "娣诲姞娴嬭瘯鏍戣〃";
+  nextTick(() => {
+    reset();
+    getTreeSelect();
+    if (row != null && row.id) {
+      form.value.parentId = row.id;
+    } else {
+      form.value.parentId = 0;
+    }
+  })
 }
 
 /** 灞曞紑/鎶樺彔鎿嶄綔 */
-function toggleExpandAll() {
-  refreshTable.value = false;
+const handleToggleExpandAll = () => {
   isExpandAll.value = !isExpandAll.value;
-  nextTick(() => {
-    refreshTable.value = true;
-  });
+  toggleExpandAll(treeList.value, isExpandAll.value)
+}
+/** 灞曞紑/鎶樺彔鎵�鏈� */
+const toggleExpandAll = (data: DemoTreeVO[], status: boolean) => {
+  data.forEach((item) => {
+    demoTreeTableRef.value.toggleRowExpansion(item, status)
+    if(item.children && item.children.length > 0) toggleExpandAll(item.children, status)
+  })
 }
 
 /** 淇敼鎸夐挳鎿嶄綔 */
-async function handleUpdate(row) {
+const handleUpdate = async (row: DemoTreeVO) => {
   loading.value = true;
-  reset();
-  await getTreeselect();
-  if (row != null) {
-    form.value.parentId = row.id;
-  }
-  getTree(row.id).then((response) => {
-    loading.value = false;
-    form.value = response.data;
-    open.value = true;
-    title.value = "淇敼娴嬭瘯鏍戣〃";
-  });
+  dialog.visible = true;
+  dialog.title = "淇敼娴嬭瘯鏍戣〃";
+  nextTick(async () => {
+    reset();
+    getTreeSelect();
+    if (row) {
+      form.value.parentId = row.id;
+    }
+    getTree(row.id).then((response) => {
+      loading.value = false;
+      form.value = response.data;
+
+    });
+  })
 }
 /** 鎻愪氦鎸夐挳 */
-function submitForm() {
-  proxy.$refs["treeRef"].validate(valid => {
+const submitForm = () => {
+  treeRef.value.validate((valid: boolean) => {
     if (valid) {
       buttonLoading.value = true;
-      if (form.value.ossConfigId != null) {
-        updateTree(form.value).then(response => {
-          proxy.$modal.msgSuccess("淇敼鎴愬姛");
-          open.value = false;
+      if (form.value.id != null) {
+        updateTree(form.value).then(() => {
+          proxy?.$modal.msgSuccess("淇敼鎴愬姛");
+          dialog.visible = false;
           getList();
         }).finally(() => {
           buttonLoading.value = false;
         });
       } else {
-        addTree(form.value).then(response => {
-          proxy.$modal.msgSuccess("鏂板鎴愬姛");
-          open.value = false;
+        addTree(form.value).then(() => {
+          proxy?.$modal.msgSuccess("鏂板鎴愬姛");
+          dialog.visible = false;
           getList();
         }).finally(() => {
           buttonLoading.value = false;
@@ -264,18 +168,126 @@
   });
 }
 /** 鍒犻櫎鎸夐挳鎿嶄綔 */
-function handleDelete(row) {
-  proxy.$modal.confirm('鏄惁纭鍒犻櫎娴嬭瘯鍗曡〃缂栧彿涓�"' + row.id + '"鐨勬暟鎹」?').then(() => {
+const handleDelete = (row: DemoTreeVO) => {
+  proxy?.$modal.confirm('鏄惁纭鍒犻櫎娴嬭瘯鍗曡〃缂栧彿涓�"' + row.id + '"鐨勬暟鎹」?').then(() => {
     loading.value = true;
     return delTree(row.id);
   }).then(() => {
     loading.value = false;
     getList();
-    proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+    proxy?.$modal.msgSuccess("鍒犻櫎鎴愬姛");
   }).finally(() => {
     loading.value = false;
   });
 }
-
-getList()
+onMounted(() => {
+  getList()
+})
 </script>
+
+<template>
+	<div class="p-2">
+		<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
+			<div class="search" v-show="showSearch">
+				<el-form :model="queryParams" ref="qeuryFormRef" :inline="true" label-width="68px">
+					<el-form-item label="鏍戣妭鐐瑰悕" prop="treeName">
+						<el-input v-model="queryParams.treeName" placeholder="璇疯緭鍏ユ爲鑺傜偣鍚�" clearable style="width: 200px" @keyup.enter="handleQuery" />
+					</el-form-item>
+					<el-form-item label="鍒涘缓鏃堕棿">
+						<el-date-picker
+							v-model="daterangeCreateTime"
+							value-format="YYYY-MM-DD HH:mm:ss"
+							type="daterange"
+							range-separator="-"
+							start-placeholder="寮�濮嬫棩鏈�"
+							end-placeholder="缁撴潫鏃ユ湡"
+							:default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"
+						></el-date-picker>
+					</el-form-item>
+					<el-form-item>
+						<el-button type="primary" icon="search" @click="handleQuery">鎼滅储</el-button>
+						<el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
+					</el-form-item>
+				</el-form>
+			</div>
+		</transition>
+
+		<el-card shadow="never">
+			<template #header>
+				<el-row :gutter="10">
+					<el-col :span="1.5">
+						<el-button type="primary" plain icon="Plus" @click="handleAdd()" v-hasPermi="['demo:tree:add']">鏂板</el-button>
+					</el-col>
+					<el-col :span="1.5">
+						<el-button type="info" plain icon="Sort" @click="handleToggleExpandAll">灞曞紑/鎶樺彔</el-button>
+					</el-col>
+					<right-toolbar :columns="columns" v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+				</el-row>
+			</template>
+
+			<el-table
+				v-if="refreshTable"
+				v-loading="loading"
+				:data="treeList"
+				row-key="id"
+				:default-expand-all="isExpandAll"
+				:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
+				ref="demoTreeTableRef"
+			>
+				<el-table-column label="鐖秈d" prop="parentId" v-if="columns[0].visible" />
+				<el-table-column label="閮ㄩ棬id" align="center" prop="deptId" v-if="columns[1].visible" />
+				<el-table-column label="鐢ㄦ埛id" align="center" prop="userId" v-if="columns[2].visible" />
+				<el-table-column label="鏍戣妭鐐瑰悕" align="center" prop="treeName" v-if="columns[3].visible" />
+				<el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime" v-if="columns[4].visible" width="180">
+					<template #default="scope">
+						<span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>
+					</template>
+				</el-table-column>
+				<el-table-column label="鎿嶄綔" fixed="right" align="center" width="150" class-name="small-padding fixed-width">
+					<template #default="scope">
+						<el-tooltip content="淇敼" placement="top">
+							<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['demo:tree:edit']"></el-button>
+						</el-tooltip>
+						<el-tooltip content="鏂板" placement="top">
+							<el-button link type="primary" icon="Plus" @click="handleAdd(scope.row)" v-hasPermi="['demo:tree:add']"></el-button>
+						</el-tooltip>
+						<el-tooltip content="鍒犻櫎" placement="top">
+							<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['demo:tree:remove']"></el-button>
+						</el-tooltip>
+					</template>
+				</el-table-column>
+			</el-table>
+		</el-card>
+
+		<!-- 娣诲姞鎴栦慨鏀规祴璇曟爲琛ㄥ璇濇 -->
+		<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
+			<el-form ref="treeRef" :model="form" :rules="rules" label-width="80px">
+				<el-form-item label="鐖秈d" prop="parentId">
+					<el-tree-select
+						v-model="form.parentId"
+						:data="treeOptions"
+						:props="{ value: 'id', label: 'treeName', children: 'children' }"
+						value-key="id"
+						check-strictly
+						placeholder="璇烽�夋嫨鐖秈d"
+					/>
+				</el-form-item>
+				<el-form-item label="閮ㄩ棬id" prop="deptId">
+					<el-input v-model="form.deptId" placeholder="璇疯緭鍏ラ儴闂╥d" />
+				</el-form-item>
+				<el-form-item label="鐢ㄦ埛id" prop="userId">
+					<el-input v-model="form.userId" placeholder="璇疯緭鍏ョ敤鎴穒d" />
+				</el-form-item>
+				<el-form-item label="鏍戣妭鐐瑰悕" prop="treeName">
+					<el-input v-model="form.treeName" placeholder="璇疯緭鍏ユ爲鑺傜偣鍚�" />
+				</el-form-item>
+			</el-form>
+			<template #footer>
+				<div class="dialog-footer">
+					<el-button :loading="buttonLoading" type="primary" @click="submitForm">纭� 瀹�</el-button>
+					<el-button @click="cancel">鍙� 娑�</el-button>
+				</div>
+			</template>
+		</el-dialog>
+	</div>
+</template>
diff --git a/src/views/error/401.vue b/src/views/error/401.vue
index 1ba3792..e163a31 100644
--- a/src/views/error/401.vue
+++ b/src/views/error/401.vue
@@ -1,42 +1,37 @@
 <template>
-  <div class="errPage-container">
-    <el-button icon="arrow-left" class="pan-back-btn" @click="back">
-      杩斿洖
-    </el-button>
-    <el-row>
-      <el-col :span="12">
-        <h1 class="text-jumbo text-ginormous">
-          401閿欒!
-        </h1>
-        <h2>鎮ㄦ病鏈夎闂潈闄愶紒</h2>
-        <h6>瀵逛笉璧凤紝鎮ㄦ病鏈夎闂潈闄愶紝璇蜂笉瑕佽繘琛岄潪娉曟搷浣滐紒鎮ㄥ彲浠ヨ繑鍥炰富椤甸潰</h6>
-        <ul class="list-unstyled">
-          <li class="link-type">
-            <router-link to="/">
-              鍥為椤�
-            </router-link>
-          </li>
-        </ul>
-      </el-col>
-      <el-col :span="12">
-        <img :src="errGif" width="313" height="428" alt="Girl has dropped her ice cream.">
-      </el-col>
-    </el-row>
-  </div>
+	<div class="errPage-container">
+		<el-button icon="arrow-left" class="pan-back-btn" @click="back"> 杩斿洖 </el-button>
+		<el-row>
+			<el-col :span="12">
+				<h1 class="text-jumbo text-ginormous">401閿欒!</h1>
+				<h2>鎮ㄦ病鏈夎闂潈闄愶紒</h2>
+				<h6>瀵逛笉璧凤紝鎮ㄦ病鏈夎闂潈闄愶紝璇蜂笉瑕佽繘琛岄潪娉曟搷浣滐紒鎮ㄥ彲浠ヨ繑鍥炰富椤甸潰</h6>
+				<ul class="list-unstyled">
+					<li class="link-type">
+						<router-link to="/"> 鍥為椤� </router-link>
+					</li>
+				</ul>
+			</el-col>
+			<el-col :span="12">
+				<img :src="errGif" width="313" height="428" alt="Girl has dropped her ice cream." />
+			</el-col>
+		</el-row>
+	</div>
 </template>
 
-<script setup>
-import errImage from "@/assets/401_images/401.gif";
+<script setup lang="ts">
+import errImage from '@/assets/401_images/401.gif';
+import { ComponentInternalInstance } from "vue";
 
-let { proxy } = getCurrentInstance();
+let { proxy } = getCurrentInstance() as ComponentInternalInstance;
 
 const errGif = ref(errImage + "?" + +new Date());
 
 function back() {
-  if (proxy.$route.query.noGoBack) {
+  if (proxy?.$route.query.noGoBack) {
     proxy.$router.push({ path: "/" });
   } else {
-    proxy.$router.go(-1);
+    proxy?.$router.go(-1);
   }
 }
 </script>
diff --git a/src/views/error/404.vue b/src/views/error/404.vue
index f205303..aef1848 100644
--- a/src/views/error/404.vue
+++ b/src/views/error/404.vue
@@ -1,31 +1,27 @@
 <template>
-  <div class="wscn-http404-container">
-    <div class="wscn-http404">
-      <div class="pic-404">
-        <img class="pic-404__parent" src="@/assets/404_images/404.png" alt="404">
-        <img class="pic-404__child left" src="@/assets/404_images/404_cloud.png" alt="404">
-        <img class="pic-404__child mid" src="@/assets/404_images/404_cloud.png" alt="404">
-        <img class="pic-404__child right" src="@/assets/404_images/404_cloud.png" alt="404">
-      </div>
-      <div class="bullshit">
-        <div class="bullshit__oops">
-          404閿欒!
-        </div>
-        <div class="bullshit__headline">
-          {{ message }}
-        </div>
-        <div class="bullshit__info">
-          瀵逛笉璧凤紝鎮ㄦ鍦ㄥ鎵剧殑椤甸潰涓嶅瓨鍦ㄣ�傚皾璇曟鏌RL鐨勯敊璇紝鐒跺悗鎸夋祻瑙堝櫒涓婄殑鍒锋柊鎸夐挳鎴栧皾璇曞湪鎴戜滑鐨勫簲鐢ㄧ▼搴忎腑鎵惧埌鍏朵粬鍐呭銆�
-        </div>
-        <router-link to="/index" class="bullshit__return-home">
-          杩斿洖棣栭〉
-        </router-link>
-      </div>
-    </div>
-  </div>
+	<div class="wscn-http404-container">
+		<div class="wscn-http404">
+			<div class="pic-404">
+				<img class="pic-404__parent" src="@/assets/404_images/404.png" alt="404" />
+				<img class="pic-404__child left" src="@/assets/404_images/404_cloud.png" alt="404" />
+				<img class="pic-404__child mid" src="@/assets/404_images/404_cloud.png" alt="404" />
+				<img class="pic-404__child right" src="@/assets/404_images/404_cloud.png" alt="404" />
+			</div>
+			<div class="bullshit">
+				<div class="bullshit__oops">404閿欒!</div>
+				<div class="bullshit__headline">
+					{{ message }}
+				</div>
+				<div class="bullshit__info">
+					瀵逛笉璧凤紝鎮ㄦ鍦ㄥ鎵剧殑椤甸潰涓嶅瓨鍦ㄣ�傚皾璇曟鏌RL鐨勯敊璇紝鐒跺悗鎸夋祻瑙堝櫒涓婄殑鍒锋柊鎸夐挳鎴栧皾璇曞湪鎴戜滑鐨勫簲鐢ㄧ▼搴忎腑鎵惧埌鍏朵粬鍐呭銆�
+				</div>
+				<router-link to="/index" class="bullshit__return-home"> 杩斿洖棣栭〉 </router-link>
+			</div>
+		</div>
+	</div>
 </template>
 
-<script setup>
+<script setup lang="ts">
 let message = computed(() => {
   return '鎵句笉鍒扮綉椤碉紒'
 })
diff --git a/src/views/index.vue b/src/views/index.vue
index b032a26..724f629 100644
--- a/src/views/index.vue
+++ b/src/views/index.vue
@@ -1,139 +1,103 @@
 <template>
-  <div class="app-container home">
-    <el-row :gutter="20">
-      <el-col :sm="24" :lg="12" style="padding-left: 20px">
-        <h2>RuoYi-Vue-Plus澶氱鎴风鐞嗙郴缁�</h2>
-        <p>
-          RuoYi-Vue-Plus 鏄熀浜� RuoYi-Vue 閽堝 鍒嗗竷寮忛泦缇� 鍦烘櫙鍗囩骇(涓嶅吋瀹瑰師妗嗘灦)
-          <br/>
-          * 鍓嶇寮�鍙戞鏋� Vue3銆乀S銆丒lement Plus<br/>
-          * 鍚庣寮�鍙戞鏋� Spring Boot<br/>
-          * 瀹瑰櫒妗嗘灦 Undertow 鍩轰簬 Netty 鐨勯珮鎬ц兘瀹瑰櫒<br/>
-          * 鏉冮檺璁よ瘉妗嗘灦 Sa-Token 鏀寔澶氱粓绔璇佺郴缁�<br/>
-          * 鍏崇郴鏁版嵁搴� MySQL 閫傞厤 8.X 鏈�浣� 5.7<br/>
-          * 缂撳瓨鏁版嵁搴� Redis 閫傞厤 6.X 鏈�浣� 4.X<br/>
-          * 鏁版嵁搴撴鏋� Mybatis-Plus 蹇�� CRUD 澧炲姞寮�鍙戞晥鐜�<br/>
-          * 鏁版嵁搴撴鏋� p6spy 鏇村己鍔茬殑 SQL 鍒嗘瀽<br/>
-          * 澶氭暟鎹簮妗嗘灦 dynamic-datasource 鏀寔涓讳粠涓庡绉嶇被鏁版嵁搴撳紓鏋�<br/>
-          * 搴忓垪鍖栨鏋� Jackson 缁熶竴浣跨敤 jackson 楂樻晥鍙潬<br/>
-          * Redis瀹㈡埛绔� Redisson 鎬ц兘寮哄姴銆丄PI涓板瘜<br/>
-          * 鍒嗗竷寮忛檺娴� Redisson 鍏ㄥ眬銆佽姹侷P銆侀泦缇D 澶氱闄愭祦<br/>
-          * 鍒嗗竷寮忛攣 Lock4j 娉ㄨВ閿併�佸伐鍏烽攣 澶氱澶氭牱<br/>
-          * 鍒嗗竷寮忓箓绛� Lock4j 鍩轰簬鍒嗗竷寮忛攣瀹炵幇<br/>
-          * 鍒嗗竷寮忛摼璺拷韪� SkyWalking 鏀寔閾捐矾杩借釜銆佺綉鏍煎垎鏋愩�佸害閲忚仛鍚堛�佸彲瑙嗗寲<br/>
-          * 鍒嗗竷寮忎换鍔¤皟搴� Xxl-Job 楂樻�ц兘 楂樺彲闈� 鏄撴墿灞�<br/>
-          * 鏂囦欢瀛樺偍 Minio 鏈湴瀛樺偍<br/>
-          * 鏂囦欢瀛樺偍 涓冪墰銆侀樋閲屻�佽吘璁�	浜戝瓨鍌�<br/>
-          * 鐩戞帶妗嗘灦 SpringBoot-Admin 鍏ㄦ柟浣嶆湇鍔$洃鎺�<br/>
-          * 鏍¢獙妗嗘灦 Validation 澧炲己鎺ュ彛瀹夊叏鎬� 涓ヨ皑鎬�<br/>
-          * Excel妗嗘灦 Alibaba EasyExcel 鎬ц兘浼樺紓 鎵╁睍鎬у己<br/>
-          * 鏂囨。妗嗘灦 SpringDoc銆乯avadoc 鏃犳敞瑙i浂鍏ヤ镜鍩轰簬java娉ㄩ噴<br/>
-          * 宸ュ叿绫绘鏋� Hutool銆丩ombok 鍑忓皯浠g爜鍐椾綑 澧炲姞瀹夊叏鎬�<br/>
-          * 浠g爜鐢熸垚鍣� 閫傞厤MP銆丼pringDoc瑙勮寖鍖栦唬鐮� 涓�閿敓鎴愬墠鍚庣浠g爜<br/>
-          * 閮ㄧ讲鏂瑰紡 Docker 瀹瑰櫒缂栨帓 涓�閿儴缃蹭笟鍔¢泦缇�<br/>
-          * 鍥介檯鍖� SpringMessage Spring鏍囧噯鍥介檯鍖栨柟妗�<br/>
-        </p>
-        <p>
-          <b>褰撳墠鐗堟湰:</b> <span>v5.0.0</span>
-        </p>
-        <p>
-          <el-tag type="danger">&yen;鍏嶈垂寮�婧�</el-tag>
-        </p>
-        <p>
-          <el-button
-            type="primary"
-            icon="Cloudy"
-            plain
-            @click="goTarget('https://gitee.com/dromara/RuoYi-Vue-Plus')"
-            >璁块棶鐮佷簯</el-button
-          >
-          <el-button
-            type="primary"
-            icon="Cloudy"
-            plain
-            @click="goTarget('https://github.com/dromara/RuoYi-Vue-Plus')"
-            >璁块棶GitHub</el-button
-          >
-          <el-button
-            type="primary"
-            icon="Cloudy"
-            plain
-            @click="goTarget('https://javalionli.gitee.io/plus-doc/#/ruoyi-vue-plus/changlog')"
-          >鏇存柊鏃ュ織</el-button
-          >
-        </p>
-      </el-col>
+	<div class="app-container home">
+		<el-row :gutter="20">
+			<el-col :sm="24" :lg="12" style="padding-left: 20px">
+				<h2>RuoYi-Vue-Plus澶氱鎴风鐞嗙郴缁�</h2>
+				<p>
+					RuoYi-Vue-Plus 鏄熀浜� RuoYi-Vue 閽堝 鍒嗗竷寮忛泦缇� 鍦烘櫙鍗囩骇(涓嶅吋瀹瑰師妗嗘灦)
+					<br />
+					* 鍓嶇寮�鍙戞鏋� Vue3銆乀S銆丒lement Plus<br />
+					* 鍚庣寮�鍙戞鏋� Spring Boot<br />
+					* 瀹瑰櫒妗嗘灦 Undertow 鍩轰簬 Netty 鐨勯珮鎬ц兘瀹瑰櫒<br />
+					* 鏉冮檺璁よ瘉妗嗘灦 Sa-Token 鏀寔澶氱粓绔璇佺郴缁�<br />
+					* 鍏崇郴鏁版嵁搴� MySQL 閫傞厤 8.X 鏈�浣� 5.7<br />
+					* 缂撳瓨鏁版嵁搴� Redis 閫傞厤 6.X 鏈�浣� 4.X<br />
+					* 鏁版嵁搴撴鏋� Mybatis-Plus 蹇�� CRUD 澧炲姞寮�鍙戞晥鐜�<br />
+					* 鏁版嵁搴撴鏋� p6spy 鏇村己鍔茬殑 SQL 鍒嗘瀽<br />
+					* 澶氭暟鎹簮妗嗘灦 dynamic-datasource 鏀寔涓讳粠涓庡绉嶇被鏁版嵁搴撳紓鏋�<br />
+					* 搴忓垪鍖栨鏋� Jackson 缁熶竴浣跨敤 jackson 楂樻晥鍙潬<br />
+					* Redis瀹㈡埛绔� Redisson 鎬ц兘寮哄姴銆丄PI涓板瘜<br />
+					* 鍒嗗竷寮忛檺娴� Redisson 鍏ㄥ眬銆佽姹侷P銆侀泦缇D 澶氱闄愭祦<br />
+					* 鍒嗗竷寮忛攣 Lock4j 娉ㄨВ閿併�佸伐鍏烽攣 澶氱澶氭牱<br />
+					* 鍒嗗竷寮忓箓绛� Lock4j 鍩轰簬鍒嗗竷寮忛攣瀹炵幇<br />
+					* 鍒嗗竷寮忛摼璺拷韪� SkyWalking 鏀寔閾捐矾杩借釜銆佺綉鏍煎垎鏋愩�佸害閲忚仛鍚堛�佸彲瑙嗗寲<br />
+					* 鍒嗗竷寮忎换鍔¤皟搴� Xxl-Job 楂樻�ц兘 楂樺彲闈� 鏄撴墿灞�<br />
+					* 鏂囦欢瀛樺偍 Minio 鏈湴瀛樺偍<br />
+					* 鏂囦欢瀛樺偍 涓冪墰銆侀樋閲屻�佽吘璁� 浜戝瓨鍌�<br />
+					* 鐩戞帶妗嗘灦 SpringBoot-Admin 鍏ㄦ柟浣嶆湇鍔$洃鎺�<br />
+					* 鏍¢獙妗嗘灦 Validation 澧炲己鎺ュ彛瀹夊叏鎬� 涓ヨ皑鎬�<br />
+					* Excel妗嗘灦 Alibaba EasyExcel 鎬ц兘浼樺紓 鎵╁睍鎬у己<br />
+					* 鏂囨。妗嗘灦 SpringDoc銆乯avadoc 鏃犳敞瑙i浂鍏ヤ镜鍩轰簬java娉ㄩ噴<br />
+					* 宸ュ叿绫绘鏋� Hutool銆丩ombok 鍑忓皯浠g爜鍐椾綑 澧炲姞瀹夊叏鎬�<br />
+					* 浠g爜鐢熸垚鍣� 閫傞厤MP銆丼pringDoc瑙勮寖鍖栦唬鐮� 涓�閿敓鎴愬墠鍚庣浠g爜<br />
+					* 閮ㄧ讲鏂瑰紡 Docker 瀹瑰櫒缂栨帓 涓�閿儴缃蹭笟鍔¢泦缇�<br />
+					* 鍥介檯鍖� SpringMessage Spring鏍囧噯鍥介檯鍖栨柟妗�<br />
+				</p>
+				<p><b>褰撳墠鐗堟湰:</b> <span>v5.0.0</span></p>
+				<p>
+					<el-tag type="danger">&yen;鍏嶈垂寮�婧�</el-tag>
+				</p>
+				<p>
+					<el-button type="primary" icon="Cloudy" plain @click="goTarget('https://gitee.com/dromara/RuoYi-Vue-Plus')">璁块棶鐮佷簯</el-button>
+					<el-button type="primary" icon="Cloudy" plain @click="goTarget('https://github.com/dromara/RuoYi-Vue-Plus')">璁块棶GitHub</el-button>
+					<el-button type="primary" icon="Cloudy" plain @click="goTarget('https://javalionli.gitee.io/plus-doc/#/ruoyi-vue-plus/changlog')"
+						>鏇存柊鏃ュ織</el-button
+					>
+				</p>
+			</el-col>
 
-      <el-col :sm="24" :lg="12" style="padding-left: 20px">
-        <h2>RuoYi-Cloud-Plus澶氱鎴峰井鏈嶅姟绠$悊绯荤粺</h2>
-        <p>
-          RuoYi-Cloud-Plus 寰湇鍔¢�氱敤鏉冮檺绠$悊绯荤粺 閲嶅啓 RuoYi-Cloud 鍏ㄦ柟浣嶅崌绾�(涓嶅吋瀹瑰師妗嗘灦)
-          <br/>
-          * 鍓嶇寮�鍙戞鏋� Vue3銆乀S銆丒lement UI<br/>
-          * 鍚庣寮�鍙戞鏋� Spring Boot<br/>
-          * 寰湇鍔″紑鍙戞鏋� Spring Cloud銆丼pring Cloud Alibaba<br/>
-          * 瀹瑰櫒妗嗘灦 Undertow 鍩轰簬 XNIO 鐨勯珮鎬ц兘瀹瑰櫒<br/>
-          * 鏉冮檺璁よ瘉妗嗘灦 Sa-Token銆丣wt 鏀寔澶氱粓绔璇佺郴缁�<br/>
-          * 鍏崇郴鏁版嵁搴� MySQL 閫傞厤 8.X 鏈�浣� 5.7<br/>
-          * 鍏崇郴鏁版嵁搴� Oracle 閫傞厤 11g 12c<br/>
-          * 鍏崇郴鏁版嵁搴� PostgreSQL 閫傞厤 13 14<br/>
-          * 鍏崇郴鏁版嵁搴� SQLServer 閫傞厤 2017 2019<br/>
-          * 缂撳瓨鏁版嵁搴� Redis 閫傞厤 6.X 鏈�浣� 5.X<br/>
-          * 鍒嗗竷寮忔敞鍐屼腑蹇� Alibaba Nacos 閲囩敤2.X 鍩轰簬GRPC閫氫俊楂樻�ц兘<br/>
-          * 鍒嗗竷寮忛厤缃腑蹇� Alibaba Nacos 閲囩敤2.X 鍩轰簬GRPC閫氫俊楂樻�ц兘<br/>
-          * 鏈嶅姟缃戝叧 Spring Cloud Gateway 鍝嶅簲寮忛珮鎬ц兘缃戝叧<br/>
-          * 璐熻浇鍧囪  Spring Cloud Loadbalancer 璐熻浇鍧囪 澶勭悊<br/>
-          * RPC杩滅▼璋冪敤 Apache Dubbo 鍘熺敓鎬佷娇鐢ㄤ綋楠屻�侀珮鎬ц兘<br/>
-          * 鍒嗗竷寮忛檺娴佺啍鏂� Alibaba Sentinel 鏃犱镜鍏ャ�侀珮鎵╁睍<br/>
-          * 鍒嗗竷寮忎簨鍔� Alibaba Seata 鏃犱镜鍏ャ�侀珮鎵╁睍 鏀寔 鍥涚妯″紡<br/>
-          * 鍒嗗竷寮忔秷鎭槦鍒� Spring Cloud Stream 闂ㄩ潰妗嗘灦鍏煎鍚勭MQ闆嗘垚<br/>
-          * 鍒嗗竷寮忔秷鎭槦鍒� Apache Kafka 楂樻�ц兘楂橀�熷害<br/>
-          * 鍒嗗竷寮忔秷鎭槦鍒� Apache RocketMQ 楂樺彲鐢ㄥ姛鑳藉鏍�<br/>
-          * 鍒嗗竷寮忔秷鎭槦鍒� RabbitMQ 鏀寔鍚勭鎵╁睍鎻掍欢鍔熻兘澶氭牱鎬�<br/>
-          * 鍒嗗竷寮忔悳绱㈠紩鎿� ElasticSearch 涓氱晫鐭ュ悕<br/>
-          * 鍒嗗竷寮忛摼璺拷韪� Apache SkyWalking 閾捐矾杩借釜銆佺綉鏍煎垎鏋愩�佸害閲忚仛鍚堛�佸彲瑙嗗寲<br/>
-          * 鍒嗗竷寮忔棩蹇椾腑蹇� ELK 涓氱晫鎴愮啛瑙e喅鏂规<br/>
-          * 鍒嗗竷寮忕洃鎺� Prometheus銆丟rafana 鍏ㄦ柟浣嶆�ц兘鐩戞帶<br/>
-          * 鍏朵綑涓� Vue 鐗堟湰涓�鑷�<br/>
-        </p>
-        <p>
-          <b>褰撳墠鐗堟湰:</b> <span>v2.0.0</span>
-        </p>
-        <p>
-          <el-tag type="danger">&yen;鍏嶈垂寮�婧�</el-tag>
-        </p>
-        <p>
-          <el-button
-              type="primary"
-              icon="Cloudy"
-              plain
-              @click="goTarget('https://gitee.com/dromara/RuoYi-Cloud-Plus')"
-          >璁块棶鐮佷簯</el-button
-          >
-          <el-button
-              type="primary"
-              icon="Cloudy"
-              plain
-              @click="goTarget('https://github.com/dromara/RuoYi-Cloud-Plus')"
-          >璁块棶GitHub</el-button
-          >
-          <el-button
-              type="primary"
-              icon="Cloudy"
-              plain
-              @click="goTarget('https://javalionli.gitee.io/plus-doc/#/ruoyi-cloud-plus/changlog')"
-          >鏇存柊鏃ュ織</el-button
-          >
-        </p>
-      </el-col>
-    </el-row>
-    <el-divider />
-  </div>
+			<el-col :sm="24" :lg="12" style="padding-left: 20px">
+				<h2>RuoYi-Cloud-Plus澶氱鎴峰井鏈嶅姟绠$悊绯荤粺</h2>
+				<p>
+					RuoYi-Cloud-Plus 寰湇鍔¢�氱敤鏉冮檺绠$悊绯荤粺 閲嶅啓 RuoYi-Cloud 鍏ㄦ柟浣嶅崌绾�(涓嶅吋瀹瑰師妗嗘灦)
+					<br />
+					* 鍓嶇寮�鍙戞鏋� Vue3銆乀S銆丒lement UI<br />
+					* 鍚庣寮�鍙戞鏋� Spring Boot<br />
+					* 寰湇鍔″紑鍙戞鏋� Spring Cloud銆丼pring Cloud Alibaba<br />
+					* 瀹瑰櫒妗嗘灦 Undertow 鍩轰簬 XNIO 鐨勯珮鎬ц兘瀹瑰櫒<br />
+					* 鏉冮檺璁よ瘉妗嗘灦 Sa-Token銆丣wt 鏀寔澶氱粓绔璇佺郴缁�<br />
+					* 鍏崇郴鏁版嵁搴� MySQL 閫傞厤 8.X 鏈�浣� 5.7<br />
+					* 鍏崇郴鏁版嵁搴� Oracle 閫傞厤 11g 12c<br />
+					* 鍏崇郴鏁版嵁搴� PostgreSQL 閫傞厤 13 14<br />
+					* 鍏崇郴鏁版嵁搴� SQLServer 閫傞厤 2017 2019<br />
+					* 缂撳瓨鏁版嵁搴� Redis 閫傞厤 6.X 鏈�浣� 5.X<br />
+					* 鍒嗗竷寮忔敞鍐屼腑蹇� Alibaba Nacos 閲囩敤2.X 鍩轰簬GRPC閫氫俊楂樻�ц兘<br />
+					* 鍒嗗竷寮忛厤缃腑蹇� Alibaba Nacos 閲囩敤2.X 鍩轰簬GRPC閫氫俊楂樻�ц兘<br />
+					* 鏈嶅姟缃戝叧 Spring Cloud Gateway 鍝嶅簲寮忛珮鎬ц兘缃戝叧<br />
+					* 璐熻浇鍧囪  Spring Cloud Loadbalancer 璐熻浇鍧囪 澶勭悊<br />
+					* RPC杩滅▼璋冪敤 Apache Dubbo 鍘熺敓鎬佷娇鐢ㄤ綋楠屻�侀珮鎬ц兘<br />
+					* 鍒嗗竷寮忛檺娴佺啍鏂� Alibaba Sentinel 鏃犱镜鍏ャ�侀珮鎵╁睍<br />
+					* 鍒嗗竷寮忎簨鍔� Alibaba Seata 鏃犱镜鍏ャ�侀珮鎵╁睍 鏀寔 鍥涚妯″紡<br />
+					* 鍒嗗竷寮忔秷鎭槦鍒� Spring Cloud Stream 闂ㄩ潰妗嗘灦鍏煎鍚勭MQ闆嗘垚<br />
+					* 鍒嗗竷寮忔秷鎭槦鍒� Apache Kafka 楂樻�ц兘楂橀�熷害<br />
+					* 鍒嗗竷寮忔秷鎭槦鍒� Apache RocketMQ 楂樺彲鐢ㄥ姛鑳藉鏍�<br />
+					* 鍒嗗竷寮忔秷鎭槦鍒� RabbitMQ 鏀寔鍚勭鎵╁睍鎻掍欢鍔熻兘澶氭牱鎬�<br />
+					* 鍒嗗竷寮忔悳绱㈠紩鎿� ElasticSearch 涓氱晫鐭ュ悕<br />
+					* 鍒嗗竷寮忛摼璺拷韪� Apache SkyWalking 閾捐矾杩借釜銆佺綉鏍煎垎鏋愩�佸害閲忚仛鍚堛�佸彲瑙嗗寲<br />
+					* 鍒嗗竷寮忔棩蹇椾腑蹇� ELK 涓氱晫鎴愮啛瑙e喅鏂规<br />
+					* 鍒嗗竷寮忕洃鎺� Prometheus銆丟rafana 鍏ㄦ柟浣嶆�ц兘鐩戞帶<br />
+					* 鍏朵綑涓� Vue 鐗堟湰涓�鑷�<br />
+				</p>
+				<p><b>褰撳墠鐗堟湰:</b> <span>v2.0.0</span></p>
+				<p>
+					<el-tag type="danger">&yen;鍏嶈垂寮�婧�</el-tag>
+				</p>
+				<p>
+					<el-button type="primary" icon="Cloudy" plain @click="goTarget('https://gitee.com/dromara/RuoYi-Cloud-Plus')">璁块棶鐮佷簯</el-button>
+					<el-button type="primary" icon="Cloudy" plain @click="goTarget('https://github.com/dromara/RuoYi-Cloud-Plus')">璁块棶GitHub</el-button>
+					<el-button type="primary" icon="Cloudy" plain @click="goTarget('https://javalionli.gitee.io/plus-doc/#/ruoyi-cloud-plus/changlog')"
+						>鏇存柊鏃ュ織</el-button
+					>
+				</p>
+			</el-col>
+		</el-row>
+		<el-divider />
+	</div>
 </template>
 
-<script setup name="Index">
+<script setup name="Index" lang="ts">
 
-function goTarget(url) {
+function goTarget(url:string) {
   window.open(url, '__blank')
 }
 </script>
@@ -201,4 +165,3 @@
   }
 }
 </style>
-
diff --git a/src/views/login.vue b/src/views/login.vue
index 4174090..8a29fda 100644
--- a/src/views/login.vue
+++ b/src/views/login.vue
@@ -1,185 +1,176 @@
-<template>
-  <div class="login">
-    <el-form ref="loginRef" :model="loginForm" :rules="loginRules" class="login-form">
-      <h3 class="title">RuoYi-Vue-Plus澶氱鎴风鐞嗙郴缁�</h3>
-      <el-form-item prop="tenantId">
-        <el-select v-model="loginForm.tenantId" filterable placeholder="璇烽�夋嫨/杈撳叆鍏徃鍚嶇О" style="width: 100%">
-          <el-option
-              v-for="item in tenantList"
-              :key="item.tenantId"
-              :label="item.companyName"
-              :value="item.tenantId">
-          </el-option>
-          <template #prefix><svg-icon icon-class="company" class="el-input__icon input-icon" /></template>
-        </el-select>
-      </el-form-item>
-      <el-form-item prop="username">
-        <el-input
-          v-model="loginForm.username"
-          type="text"
-          size="large"
-          auto-complete="off"
-          placeholder="璐﹀彿"
-        >
-          <template #prefix><svg-icon icon-class="user" class="el-input__icon input-icon" /></template>
-        </el-input>
-      </el-form-item>
-      <el-form-item prop="password">
-        <el-input
-          v-model="loginForm.password"
-          type="password"
-          size="large"
-          auto-complete="off"
-          placeholder="瀵嗙爜"
-          @keyup.enter="handleLogin"
-        >
-          <template #prefix><svg-icon icon-class="password" class="el-input__icon input-icon" /></template>
-        </el-input>
-      </el-form-item>
-      <el-form-item prop="code" v-if="captchaEnabled">
-        <el-input
-          v-model="loginForm.code"
-          size="large"
-          auto-complete="off"
-          placeholder="楠岃瘉鐮�"
-          style="width: 63%"
-          @keyup.enter="handleLogin"
-        >
-          <template #prefix><svg-icon icon-class="validCode" class="el-input__icon input-icon" /></template>
-        </el-input>
-        <div class="login-code">
-          <img :src="codeUrl" @click="getCode" class="login-code-img"/>
-        </div>
-      </el-form-item>
-      <el-checkbox v-model="loginForm.rememberMe" style="margin:0px 0px 25px 0px;">璁颁綇瀵嗙爜</el-checkbox>
-      <el-form-item style="width:100%;">
-        <el-button
-          :loading="loading"
-          size="large"
-          type="primary"
-          style="width:100%;"
-          @click.prevent="handleLogin"
-        >
-          <span v-if="!loading">鐧� 褰�</span>
-          <span v-else>鐧� 褰� 涓�...</span>
-        </el-button>
-        <div style="float: right;" v-if="register">
-          <router-link class="link-type" :to="'/register'">绔嬪嵆娉ㄥ唽</router-link>
-        </div>
-      </el-form-item>
-    </el-form>
-    <!--  搴曢儴  -->
-    <div class="el-login-footer">
-      <span>Copyright 漏 2018-2023 ruoyi.vip All Rights Reserved.</span>
-    </div>
-  </div>
-</template>
+<script setup lang="ts">
+import { getCodeImg, getTenantList } from '@/api/login';
+import Cookies from 'js-cookie';
+import { encrypt, decrypt } from '@/utils/jsencrypt';
+import { useUserStore } from '@/store/modules/user';
+import { LoginData, TenantVO } from '@/api/types';
+import { FormRules } from 'element-plus';
+import { to } from 'await-to-js';
 
-<script setup>
-import { getCodeImg, getTenantList } from "@/api/login";
-import Cookies from "js-cookie";
-import { encrypt, decrypt } from "@/utils/jsencrypt";
-import useUserStore from '@/store/modules/user'
-
-const userStore = useUserStore()
+const userStore = useUserStore();
 const router = useRouter();
-const { proxy } = getCurrentInstance();
 
-const loginForm = ref({
+const loginForm = ref<LoginData>({
   tenantId: "000000",
-  username: "admin",
-  password: "admin123",
+  username: 'admin',
+  password: 'admin123',
   rememberMe: false,
-  code: "",
-  uuid: ""
+  code: '',
+  uuid: ''
 });
 
-const loginRules = {
+const loginRules: FormRules = {
   tenantId: [{ required: true, trigger: "blur", message: "璇疯緭鍏ユ偍鐨勭鎴风紪鍙�" }],
-  username: [{ required: true, trigger: "blur", message: "璇疯緭鍏ユ偍鐨勮处鍙�" }],
-  password: [{ required: true, trigger: "blur", message: "璇疯緭鍏ユ偍鐨勫瘑鐮�" }],
-  code: [{ required: true, trigger: "change", message: "璇疯緭鍏ラ獙璇佺爜" }]
+  username: [{ required: true, trigger: 'blur', message: '璇疯緭鍏ユ偍鐨勮处鍙�' }],
+  password: [{ required: true, trigger: 'blur', message: '璇疯緭鍏ユ偍鐨勫瘑鐮�' }],
+  code: [{ required: true, trigger: 'change', message: '璇疯緭鍏ラ獙璇佺爜' }]
 };
 
-const codeUrl = ref("");
+const codeUrl = ref('');
 const loading = ref(false);
 // 楠岃瘉鐮佸紑鍏�
 const captchaEnabled = ref(true);
+// 绉熸埛寮�鍏�
+const tenantEnabled = ref(true);
+
+
 // 娉ㄥ唽寮�鍏�
 const register = ref(false);
 const redirect = ref(undefined);
+const loginRef = ref(ElForm);
 // 绉熸埛鍒楄〃
-const tenantList = ref([]);
+const tenantList = ref<TenantVO[]>([]);
 
-function handleLogin() {
-  proxy.$refs.loginRef.validate(valid => {
+const handleLogin = () => {
+  loginRef.value.validate(async (valid:boolean, fields: any) => {
     if (valid) {
       loading.value = true;
       // 鍕鹃�変簡闇�瑕佽浣忓瘑鐮佽缃湪 cookie 涓缃浣忕敤鎴峰悕鍜屽瘑鐮�
       if (loginForm.value.rememberMe) {
         Cookies.set("tenantId", loginForm.value.tenantId, { expires: 30 });
-        Cookies.set("username", loginForm.value.username, { expires: 30 });
-        Cookies.set("password", encrypt(loginForm.value.password), { expires: 30 });
-        Cookies.set("rememberMe", loginForm.value.rememberMe, { expires: 30 });
+        Cookies.set('username', loginForm.value.username, { expires: 30 });
+        Cookies.set('password', String(encrypt(loginForm.value.password)), { expires: 30 });
+        Cookies.set('rememberMe', String(loginForm.value.rememberMe), { expires: 30 });
       } else {
         // 鍚﹀垯绉婚櫎
         Cookies.remove("tenantId");
-        Cookies.remove("username");
-        Cookies.remove("password");
-        Cookies.remove("rememberMe");
+        Cookies.remove('username');
+        Cookies.remove('password');
+        Cookies.remove('rememberMe');
       }
       // 璋冪敤action鐨勭櫥褰曟柟娉�
-      userStore.login(loginForm.value).then(() => {
-        router.push({ path: redirect.value || "/" });
-      }).catch(() => {
+      // prittier-ignore
+      const [err] = await to(userStore.login(loginForm.value));
+      if (!err) {
+        await router.push({ path: redirect.value || '/' });
+      } else {
         loading.value = false;
         // 閲嶆柊鑾峰彇楠岃瘉鐮�
         if (captchaEnabled.value) {
-          getCode();
+          await getCode();
         }
-      });
+      }
+    } else {
+      console.log('error submit!', fields);
     }
   });
-}
+};
 
-function getCode() {
-  getCodeImg().then(res => {
-    captchaEnabled.value = res.data.captchaEnabled === undefined ? true : res.data.captchaEnabled;
-    if (captchaEnabled.value) {
-      codeUrl.value = "data:image/gif;base64," + res.data.img;
-      loginForm.value.uuid = res.data.uuid;
-    }
-  });
-}
+/**
+ * 鑾峰彇楠岃瘉鐮�
+ */
+const getCode = async () => {
+  const res = await getCodeImg();
+  const { data } = res;
+  captchaEnabled.value = data.captchaEnabled === undefined ? true : data.captchaEnabled;
+  if (captchaEnabled.value) {
+    codeUrl.value = 'data:image/gif;base64,' + data.img;
+    loginForm.value.uuid = data.uuid;
+  }
+};
 
-function initTenantList() {
-  getTenantList().then(res => {
-    tenantList.value = res.data;
-    if (tenantList.value != null && tenantList.value.length !== 0) {
-      loginForm.value.tenantId = tenantList.value[0].tenantId;
-    }
-  });
-}
-
-function getCookie() {
+const getCookie = () => {
   const tenantId = Cookies.get("tenantId");
-  const username = Cookies.get("username");
-  const password = Cookies.get("password");
-  const rememberMe = Cookies.get("rememberMe");
+  const username = Cookies.get('username');
+  const password = Cookies.get('password');
+  const rememberMe = Cookies.get('rememberMe');
   loginForm.value = {
     tenantId: tenantId === undefined ? loginForm.value.tenantId : tenantId,
     username: username === undefined ? loginForm.value.username : username,
-    password: password === undefined ? loginForm.value.password : decrypt(password),
+    password: password === undefined ? loginForm.value.password : (decrypt(password) as string),
     rememberMe: rememberMe === undefined ? false : Boolean(rememberMe)
   };
 }
 
-getCode();
-initTenantList();
-getCookie();
+
+/**
+ * 鑾峰彇绉熸埛鍒楄〃
+ */
+const initTenantList = async () => {
+  const { data } = await getTenantList();
+  tenantEnabled.value = data.tenantEnabled === undefined ? true : data.tenantEnabled;
+  if (tenantEnabled.value) {
+    tenantList.value = data.voList;
+    if (tenantList.value != null && tenantList.value.length !== 0) {
+      loginForm.value.tenantId = tenantList.value[0].tenantId;
+    }
+  }
+}
+
+onMounted(() => {
+  getCode();
+  initTenantList();
+  getCookie();
+});
 </script>
 
-<style lang='scss' scoped>
+<template>
+	<div class="login">
+		<el-form ref="loginRef" :model="loginForm" :rules="loginRules" class="login-form">
+			<h3 class="title">RuoYi-Vue-Plus澶氱鎴风鐞嗙郴缁�</h3>
+			<el-form-item prop="tenantId" v-if="tenantEnabled">
+				<el-select v-model="loginForm.tenantId" filterable placeholder="璇烽�夋嫨/杈撳叆鍏徃鍚嶇О" style="width: 100%">
+					<el-option v-for="item in tenantList" :key="item.tenantId" :label="item.companyName" :value="item.tenantId"> </el-option>
+					<template #prefix><svg-icon icon-class="company" class="el-input__icon input-icon" /></template>
+				</el-select>
+			</el-form-item>
+			<el-form-item prop="username">
+				<el-input v-model="loginForm.username" type="text" size="large" auto-complete="off" placeholder="璐﹀彿">
+					<template #prefix><svg-icon icon-class="user" class="el-input__icon input-icon" /></template>
+				</el-input>
+			</el-form-item>
+			<el-form-item prop="password">
+				<el-input v-model="loginForm.password" type="password" size="large" auto-complete="off" placeholder="瀵嗙爜" @keyup.enter="handleLogin">
+					<template #prefix><svg-icon icon-class="password" class="el-input__icon input-icon" /></template>
+				</el-input>
+			</el-form-item>
+			<el-form-item prop="code" v-if="captchaEnabled">
+				<el-input v-model="loginForm.code" size="large" auto-complete="off" placeholder="楠岃瘉鐮�" style="width: 63%" @keyup.enter="handleLogin">
+					<template #prefix><svg-icon icon-class="validCode" class="el-input__icon input-icon" /></template>
+				</el-input>
+				<div class="login-code">
+					<img :src="codeUrl" @click="getCode" class="login-code-img" />
+				</div>
+			</el-form-item>
+			<el-checkbox v-model="loginForm.rememberMe" style="margin:0px 0px 25px 0px;">璁颁綇瀵嗙爜</el-checkbox>
+			<el-form-item style="width:100%;">
+				<el-button :loading="loading" size="large" type="primary" style="width:100%;" @click.prevent="handleLogin">
+					<span v-if="!loading">鐧� 褰�</span>
+					<span v-else>鐧� 褰� 涓�...</span>
+				</el-button>
+				<div style="float: right;" v-if="register">
+					<router-link class="link-type" :to="'/register'">绔嬪嵆娉ㄥ唽</router-link>
+				</div>
+			</el-form-item>
+		</el-form>
+		<!--  搴曢儴  -->
+		<div class="el-login-footer">
+			<span>Copyright 漏 2018-2023 ruoyi.vip All Rights Reserved.</span>
+		</div>
+	</div>
+</template>
+
+<style lang="scss" scoped>
 .login {
   display: flex;
   justify-content: center;
@@ -233,7 +224,7 @@
   width: 100%;
   text-align: center;
   color: #fff;
-  font-family: Arial;
+  font-family: Arial,serif;
   font-size: 12px;
   letter-spacing: 1px;
 }
diff --git a/src/views/monitor/admin/index.vue b/src/views/monitor/admin/index.vue
index 1b13d64..4f958a1 100644
--- a/src/views/monitor/admin/index.vue
+++ b/src/views/monitor/admin/index.vue
@@ -1,13 +1,9 @@
 <template>
-  <div>
-    <i-frame v-model:src="url"></i-frame>
-  </div>
+	<div>
+		<i-frame v-model:src="url"></i-frame>
+	</div>
 </template>
 
-<script setup>
-import iFrame from '@/components/iFrame'
-
-import { ref } from 'vue';
-
+<script setup lang="ts">
 const url = ref(import.meta.env.VITE_APP_MONITRO_ADMIN);
 </script>
diff --git a/src/views/monitor/cache/index.vue b/src/views/monitor/cache/index.vue
index b3fadf7..65082b0 100644
--- a/src/views/monitor/cache/index.vue
+++ b/src/views/monitor/cache/index.vue
@@ -1,129 +1,188 @@
-<template>
-  <div class="app-container">
-    <el-row>
-      <el-col :span="24" class="card-box">
-        <el-card>
-          <template #header><Monitor style="width: 1em; height: 1em; vertical-align: middle;" /> <span style="vertical-align: middle;">鍩烘湰淇℃伅</span></template>
-          <div class="el-table el-table--enable-row-hover el-table--medium">
-            <table cellspacing="0" style="width: 100%">
-              <tbody>
-                <tr>
-                  <td class="el-table__cell is-leaf"><div class="cell">Redis鐗堟湰</div></td>
-                  <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.redis_version }}</div></td>
-                  <td class="el-table__cell is-leaf"><div class="cell">杩愯妯″紡</div></td>
-                  <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.redis_mode == "standalone" ? "鍗曟満" : "闆嗙兢" }}</div></td>
-                  <td class="el-table__cell is-leaf"><div class="cell">绔彛</div></td>
-                  <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.tcp_port }}</div></td>
-                  <td class="el-table__cell is-leaf"><div class="cell">瀹㈡埛绔暟</div></td>
-                  <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.connected_clients }}</div></td>
-                </tr>
-                <tr>
-                  <td class="el-table__cell is-leaf"><div class="cell">杩愯鏃堕棿(澶�)</div></td>
-                  <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.uptime_in_days }}</div></td>
-                  <td class="el-table__cell is-leaf"><div class="cell">浣跨敤鍐呭瓨</div></td>
-                  <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.used_memory_human }}</div></td>
-                  <td class="el-table__cell is-leaf"><div class="cell">浣跨敤CPU</div></td>
-                  <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ parseFloat(cache.info.used_cpu_user_children).toFixed(2) }}</div></td>
-                  <td class="el-table__cell is-leaf"><div class="cell">鍐呭瓨閰嶇疆</div></td>
-                  <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.maxmemory_human }}</div></td>
-                </tr>
-                <tr>
-                  <td class="el-table__cell is-leaf"><div class="cell">AOF鏄惁寮�鍚�</div></td>
-                  <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.aof_enabled == "0" ? "鍚�" : "鏄�" }}</div></td>
-                  <td class="el-table__cell is-leaf"><div class="cell">RDB鏄惁鎴愬姛</div></td>
-                  <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.rdb_last_bgsave_status }}</div></td>
-                  <td class="el-table__cell is-leaf"><div class="cell">Key鏁伴噺</div></td>
-                  <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.dbSize">{{ cache.dbSize }} </div></td>
-                  <td class="el-table__cell is-leaf"><div class="cell">缃戠粶鍏ュ彛/鍑哄彛</div></td>
-                  <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.instantaneous_input_kbps }}kps/{{cache.info.instantaneous_output_kbps}}kps</div></td>
-                </tr>
-              </tbody>
-            </table>
-          </div>
-        </el-card>
-      </el-col>
-
-      <el-col :span="12" class="card-box">
-        <el-card>
-          <template #header><PieChart style="width: 1em; height: 1em; vertical-align: middle;" /> <span style="vertical-align: middle;">鍛戒护缁熻</span></template>
-          <div class="el-table el-table--enable-row-hover el-table--medium">
-            <div ref="commandstats" style="height: 420px" />
-          </div>
-        </el-card>
-      </el-col>
-
-      <el-col :span="12" class="card-box">
-        <el-card>
-          <template #header><Odometer style="width: 1em; height: 1em; vertical-align: middle;" /> <span style="vertical-align: middle;">鍐呭瓨淇℃伅</span></template>
-          <div class="el-table el-table--enable-row-hover el-table--medium">
-            <div ref="usedmemory" style="height: 420px" />
-          </div>
-        </el-card>
-      </el-col>
-    </el-row>
-  </div>
-</template>
-
-<script setup name="Cache">
+<script setup name="Cache" lang="ts">
 import { getCache } from '@/api/monitor/cache';
 import * as echarts from 'echarts';
+import { ComponentInternalInstance } from "vue";
 
-const cache = ref([]);
-const commandstats = ref(null);
-const usedmemory = ref(null);
-const { proxy } = getCurrentInstance();
+const cache = ref<any>({});
+const commandstats = ref();
+const usedmemory = ref();
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 
-function getList() {
-  proxy.$modal.loading("姝e湪鍔犺浇缂撳瓨鐩戞帶鏁版嵁锛岃绋嶅�欙紒");
-  getCache().then(response => {
-    proxy.$modal.closeLoading();
-    cache.value = response.data;
+const getList = async () => {
+	proxy?.$modal.loading("姝e湪鍔犺浇缂撳瓨鐩戞帶鏁版嵁锛岃绋嶅�欙紒");
+	const res = await getCache();
+	proxy?.$modal.closeLoading();
+	cache.value = res.data;
+	const commandstatsIntance = echarts.init(commandstats.value, "macarons");
+	commandstatsIntance.setOption({
+		tooltip: {
+			trigger: "item",
+			formatter: "{a} <br/>{b} : {c} ({d}%)"
+		},
+		series: [
+			{
+				name: "鍛戒护",
+				type: "pie",
+				roseType: "radius",
+				radius: [15, 95],
+				center: ["50%", "38%"],
+				data: res.data.commandStats,
+				animationEasing: "cubicInOut",
+				animationDuration: 1000
+			}
+		]
+	});
 
-    const commandstatsIntance = echarts.init(commandstats.value, "macarons");
-    commandstatsIntance.setOption({
-      tooltip: {
-        trigger: "item",
-        formatter: "{a} <br/>{b} : {c} ({d}%)"
-      },
-      series: [
-        {
-          name: "鍛戒护",
-          type: "pie",
-          roseType: "radius",
-          radius: [15, 95],
-          center: ["50%", "38%"],
-          data: response.data.commandStats,
-          animationEasing: "cubicInOut",
-          animationDuration: 1000
-        }
-      ]
-    });
-
-    const usedmemoryInstance = echarts.init(usedmemory.value, "macarons");
-    usedmemoryInstance.setOption({
-      tooltip: {
-        formatter: "{b} <br/>{a} : " + cache.value.info.used_memory_human
-      },
-      series: [
-        {
-          name: "宄板��",
-          type: "gauge",
-          min: 0,
-          max: 1000,
-          detail: {
-            formatter: cache.value.info.used_memory_human
-          },
-          data: [
-            {
-              value: parseFloat(cache.value.info.used_memory_human),
-              name: "鍐呭瓨娑堣��"
-            }
-          ]
-        }
-      ]
-    })
-  })
+	const usedmemoryInstance = echarts.init(usedmemory.value, "macarons");
+	usedmemoryInstance.setOption({
+		tooltip: {
+			formatter: "{b} <br/>{a} : " + cache.value.info.used_memory_human
+		},
+		series: [
+			{
+				name: "宄板��",
+				type: "gauge",
+				min: 0,
+				max: 1000,
+				detail: {
+					formatter: cache.value.info.used_memory_human
+				},
+				data: [
+					{
+						value: parseFloat(cache.value.info.used_memory_human),
+						name: "鍐呭瓨娑堣��"
+					}
+				]
+			}
+		]
+	})
 }
 
-getList();
+onMounted(() => {
+	getList();
+})
 </script>
+<template>
+	<div class="p-2">
+		<el-row>
+			<el-col :span="24" class="card-box">
+				<el-card>
+					<template #header>
+						<Monitor style="width: 1em; height: 1em; vertical-align: middle;" />
+						<span style="vertical-align: middle;">鍩烘湰淇℃伅</span>
+					</template>
+
+					<div class="el-table el-table--enable-row-hover el-table--medium">
+						<table style="width: 100%">
+							<tbody>
+								<tr>
+									<td class="el-table__cell is-leaf">
+										<div class="cell">Redis鐗堟湰</div>
+									</td>
+									<td class="el-table__cell is-leaf">
+										<div class="cell" v-if="cache.info">{{ cache.info.redis_version }}</div>
+									</td>
+									<td class="el-table__cell is-leaf">
+										<div class="cell">杩愯妯″紡</div>
+									</td>
+									<td class="el-table__cell is-leaf">
+										<div class="cell" v-if="cache.info">{{ cache.info.redis_mode === "standalone" ? "鍗曟満" : "闆嗙兢" }}</div>
+									</td>
+									<td class="el-table__cell is-leaf">
+										<div class="cell">绔彛</div>
+									</td>
+									<td class="el-table__cell is-leaf">
+										<div class="cell" v-if="cache.info">{{ cache.info.tcp_port }}</div>
+									</td>
+									<td class="el-table__cell is-leaf">
+										<div class="cell">瀹㈡埛绔暟</div>
+									</td>
+									<td class="el-table__cell is-leaf">
+										<div class="cell" v-if="cache.info">{{ cache.info.connected_clients }}</div>
+									</td>
+								</tr>
+								<tr>
+									<td class="el-table__cell is-leaf">
+										<div class="cell">杩愯鏃堕棿(澶�)</div>
+									</td>
+									<td class="el-table__cell is-leaf">
+										<div class="cell" v-if="cache.info">{{ cache.info.uptime_in_days }}</div>
+									</td>
+									<td class="el-table__cell is-leaf">
+										<div class="cell">浣跨敤鍐呭瓨</div>
+									</td>
+									<td class="el-table__cell is-leaf">
+										<div class="cell" v-if="cache.info">{{ cache.info.used_memory_human }}</div>
+									</td>
+									<td class="el-table__cell is-leaf">
+										<div class="cell">浣跨敤CPU</div>
+									</td>
+									<td class="el-table__cell is-leaf">
+										<div class="cell" v-if="cache.info">{{ parseFloat(cache.info.used_cpu_user_children).toFixed(2) }}</div>
+									</td>
+									<td class="el-table__cell is-leaf">
+										<div class="cell">鍐呭瓨閰嶇疆</div>
+									</td>
+									<td class="el-table__cell is-leaf">
+										<div class="cell" v-if="cache.info">{{ cache.info.maxmemory_human }}</div>
+									</td>
+								</tr>
+								<tr>
+									<td class="el-table__cell is-leaf">
+										<div class="cell">AOF鏄惁寮�鍚�</div>
+									</td>
+									<td class="el-table__cell is-leaf">
+										<div class="cell" v-if="cache.info">{{ cache.info.aof_enabled === "0" ? "鍚�" : "鏄�" }}</div>
+									</td>
+									<td class="el-table__cell is-leaf">
+										<div class="cell">RDB鏄惁鎴愬姛</div>
+									</td>
+									<td class="el-table__cell is-leaf">
+										<div class="cell" v-if="cache.info">{{ cache.info.rdb_last_bgsave_status }}</div>
+									</td>
+									<td class="el-table__cell is-leaf">
+										<div class="cell">Key鏁伴噺</div>
+									</td>
+									<td class="el-table__cell is-leaf">
+										<div class="cell" v-if="cache.dbSize">{{ cache.dbSize }}</div>
+									</td>
+									<td class="el-table__cell is-leaf">
+										<div class="cell">缃戠粶鍏ュ彛/鍑哄彛</div>
+									</td>
+									<td class="el-table__cell is-leaf">
+										<div class="cell" v-if="cache.info">
+											{{ cache.info.instantaneous_input_kbps }}kps/{{ cache.info.instantaneous_output_kbps }}kps
+										</div>
+									</td>
+								</tr>
+							</tbody>
+						</table>
+					</div>
+				</el-card>
+			</el-col>
+
+			<el-col :span="12" class="card-box">
+				<el-card>
+					<template #header>
+						<PieChart style="width: 1em; height: 1em; vertical-align: middle;" />
+						<span style="vertical-align: middle;">鍛戒护缁熻</span>
+					</template>
+					<div class="el-table el-table--enable-row-hover el-table--medium">
+						<div ref="commandstats" style="height: 420px" />
+					</div>
+				</el-card>
+			</el-col>
+
+			<el-col :span="12" class="card-box">
+				<el-card>
+					<template #header>
+						<Odometer style="width: 1em; height: 1em; vertical-align: middle;" /> <span style="vertical-align: middle;">鍐呭瓨淇℃伅</span>
+					</template>
+					<div class="el-table el-table--enable-row-hover el-table--medium">
+						<div ref="usedmemory" style="height: 420px" />
+					</div>
+				</el-card>
+			</el-col>
+		</el-row>
+	</div>
+</template>
diff --git a/src/views/monitor/logininfor/index.vue b/src/views/monitor/logininfor/index.vue
index bd58a5d..32e5149 100644
--- a/src/views/monitor/logininfor/index.vue
+++ b/src/views/monitor/logininfor/index.vue
@@ -1,225 +1,197 @@
-<template>
-   <div class="app-container">
-      <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px">
-         <el-form-item label="鐧诲綍鍦板潃" prop="ipaddr">
-            <el-input
-               v-model="queryParams.ipaddr"
-               placeholder="璇疯緭鍏ョ櫥褰曞湴鍧�"
-               clearable
-               style="width: 240px;"
-               @keyup.enter="handleQuery"
-            />
-         </el-form-item>
-         <el-form-item label="鐢ㄦ埛鍚嶇О" prop="userName">
-            <el-input
-               v-model="queryParams.userName"
-               placeholder="璇疯緭鍏ョ敤鎴峰悕绉�"
-               clearable
-               style="width: 240px;"
-               @keyup.enter="handleQuery"
-            />
-         </el-form-item>
-         <el-form-item label="鐘舵��" prop="status">
-            <el-select
-               v-model="queryParams.status"
-               placeholder="鐧诲綍鐘舵��"
-               clearable
-               style="width: 240px"
-            >
-               <el-option
-                  v-for="dict in sys_common_status"
-                  :key="dict.value"
-                  :label="dict.label"
-                  :value="dict.value"
-               />
-            </el-select>
-         </el-form-item>
-         <el-form-item label="鐧诲綍鏃堕棿" style="width: 308px">
-            <el-date-picker
-               v-model="dateRange"
-               value-format="YYYY-MM-DD HH:mm:ss"
-               type="daterange"
-               range-separator="-"
-               start-placeholder="寮�濮嬫棩鏈�"
-               end-placeholder="缁撴潫鏃ユ湡"
-               :default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"
-            ></el-date-picker>
-         </el-form-item>
-         <el-form-item>
-            <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
-            <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
-         </el-form-item>
-      </el-form>
+<script setup name="Logininfor" lang="ts">
+import { list, delLoginInfo, cleanLoginInfo, unlockLoginInfo } from "@/api/monitor/loginInfo";
+import { ComponentInternalInstance } from "vue";
+import { LoginInfoQuery, LoginInfoVO } from "@/api/monitor/loginInfo/types";
+import { DateModelType } from 'element-plus';
 
-      <el-row :gutter="10" class="mb8">
-         <el-col :span="1.5">
-            <el-button
-               type="danger"
-               plain
-               icon="Delete"
-               :disabled="multiple"
-               @click="handleDelete"
-               v-hasPermi="['monitor:logininfor:remove']"
-            >鍒犻櫎</el-button>
-         </el-col>
-         <el-col :span="1.5">
-            <el-button
-               type="danger"
-               plain
-               icon="Delete"
-               @click="handleClean"
-               v-hasPermi="['monitor:logininfor:remove']"
-            >娓呯┖</el-button>
-         </el-col>
-         <el-col :span="1.5">
-            <el-button
-               type="primary"
-               plain
-               icon="Unlock"
-               :disabled="single"
-               @click="handleUnlock"
-               v-hasPermi="['monitor:logininfor:unlock']"
-            >瑙i攣</el-button>
-         </el-col>
-         <el-col :span="1.5">
-            <el-button
-               type="warning"
-               plain
-               icon="Download"
-               @click="handleExport"
-               v-hasPermi="['monitor:logininfor:export']"
-            >瀵煎嚭</el-button>
-         </el-col>
-         <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
-      </el-row>
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { sys_common_status } = toRefs<any>(proxy?.useDict("sys_common_status"));
 
-      <el-table ref="logininforRef" v-loading="loading" :data="logininforList" @selection-change="handleSelectionChange" :default-sort="defaultSort" @sort-change="handleSortChange">
-         <el-table-column type="selection" width="55" align="center" />
-         <el-table-column label="璁块棶缂栧彿" align="center" prop="infoId" />
-         <el-table-column label="鐢ㄦ埛鍚嶇О" align="center" prop="userName" :show-overflow-tooltip="true" sortable="custom" :sort-orders="['descending', 'ascending']" />
-         <el-table-column label="鍦板潃" align="center" prop="ipaddr" :show-overflow-tooltip="true" />
-         <el-table-column label="鐧诲綍鍦扮偣" align="center" prop="loginLocation" :show-overflow-tooltip="true" />
-         <el-table-column label="鎿嶄綔绯荤粺" align="center" prop="os" :show-overflow-tooltip="true" />
-         <el-table-column label="娴忚鍣�" align="center" prop="browser" :show-overflow-tooltip="true" />
-         <el-table-column label="鐧诲綍鐘舵��" align="center" prop="status">
-            <template #default="scope">
-               <dict-tag :options="sys_common_status" :value="scope.row.status" />
-            </template>
-         </el-table-column>
-         <el-table-column label="鎻忚堪" align="center" prop="msg" :show-overflow-tooltip="true" />
-         <el-table-column label="璁块棶鏃堕棿" align="center" prop="loginTime" sortable="custom" :sort-orders="['descending', 'ascending']" width="180">
-            <template #default="scope">
-               <span>{{ parseTime(scope.row.loginTime) }}</span>
-            </template>
-         </el-table-column>
-      </el-table>
-
-      <pagination
-         v-show="total > 0"
-         :total="total"
-         v-model:page="queryParams.pageNum"
-         v-model:limit="queryParams.pageSize"
-         @pagination="getList"
-      />
-   </div>
-</template>
-
-<script setup name="Logininfor">
-import { list, delLogininfor, cleanLogininfor, unlockLogininfor } from "@/api/monitor/logininfor";
-
-const { proxy } = getCurrentInstance();
-const { sys_common_status } = proxy.useDict("sys_common_status");
-
-const logininforList = ref([]);
+const loginInfoList = ref<LoginInfoVO[]>([]);
 const loading = ref(true);
 const showSearch = ref(true);
-const ids = ref([]);
+const ids = ref<Array<number | string>>([]);
 const single = ref(true);
 const multiple = ref(true);
-const selectName = ref("");
+const selectName = ref<Array<string>>([]);
 const total = ref(0);
-const dateRange = ref([]);
-const defaultSort = ref({ prop: "loginTime", order: "descending" });
+const dateRange = ref<[DateModelType,DateModelType]>(['', '']);
+const defaultSort = ref<any>({ prop: "loginTime", order: "descending" });
 
+const queryFormRef = ref(ElForm);
+const loginInfoTableRef = ref(ElTable);
 // 鏌ヨ鍙傛暟
-const queryParams = ref({
+const queryParams = ref<LoginInfoQuery>({
   pageNum: 1,
   pageSize: 10,
-  ipaddr: undefined,
-  userName: undefined,
-  status: undefined,
-  orderByColumn: undefined,
-  isAsc: undefined
+  ipaddr: '',
+  userName: '',
+  status: '',
+  orderByColumn: defaultSort.value.prop,
+  isAsc: defaultSort.value.order
 });
 
 /** 鏌ヨ鐧诲綍鏃ュ織鍒楄〃 */
-function getList() {
+const getList = async () => {
   loading.value = true;
-  list(proxy.addDateRange(queryParams.value, dateRange.value)).then(response => {
-    logininforList.value = response.rows;
-    total.value = response.total;
-    loading.value = false;
-  });
+	const res = await list(proxy?.addDateRange(queryParams.value, dateRange.value));
+	loginInfoList.value = res.rows;
+	total.value = res.total;
+	loading.value = false;
 }
 /** 鎼滅储鎸夐挳鎿嶄綔 */
-function handleQuery() {
+const handleQuery = () => {
   queryParams.value.pageNum = 1;
   getList();
 }
 /** 閲嶇疆鎸夐挳鎿嶄綔 */
-function resetQuery() {
-  dateRange.value = [];
-  proxy.resetForm("queryRef");
+const resetQuery = () => {
+  dateRange.value = ['', ''];
+  queryFormRef.value.resetFields();
   queryParams.value.pageNum = 1;
-  proxy.$refs["logininforRef"].sort(defaultSort.value.prop, defaultSort.value.order);
+  loginInfoTableRef.value.sort(defaultSort.value.prop, defaultSort.value.order);
 }
 /** 澶氶�夋閫変腑鏁版嵁 */
-function handleSelectionChange(selection) {
+const handleSelectionChange = (selection: LoginInfoVO[]) => {
   ids.value = selection.map(item => item.infoId);
   multiple.value = !selection.length;
   single.value = selection.length != 1;
   selectName.value = selection.map(item => item.userName);
 }
 /** 鎺掑簭瑙﹀彂浜嬩欢 */
-function handleSortChange(column, prop, order) {
+const handleSortChange = (column: any) => {
   queryParams.value.orderByColumn = column.prop;
   queryParams.value.isAsc = column.order;
   getList();
 }
 /** 鍒犻櫎鎸夐挳鎿嶄綔 */
-function handleDelete(row) {
-  const infoIds = row.infoId || ids.value;
-  proxy.$modal.confirm('鏄惁纭鍒犻櫎璁块棶缂栧彿涓�"' + infoIds + '"鐨勬暟鎹」?').then(function () {
-    return delLogininfor(infoIds);
-  }).then(() => {
-    getList();
-    proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
-  }).catch(() => {});
+const handleDelete = async (row?: LoginInfoVO) => {
+  const infoIds = row?.infoId || ids.value;
+	await proxy?.$modal.confirm('鏄惁纭鍒犻櫎璁块棶缂栧彿涓�"' + infoIds + '"鐨勬暟鎹」?');
+	await delLoginInfo(infoIds);
+	getList();
+	proxy?.$modal.msgSuccess("鍒犻櫎鎴愬姛");
 }
 /** 娓呯┖鎸夐挳鎿嶄綔 */
-function handleClean() {
-  proxy.$modal.confirm("鏄惁纭娓呯┖鎵�鏈夌櫥褰曟棩蹇楁暟鎹」?").then(function () {
-    return cleanLogininfor();
-  }).then(() => {
-    getList();
-    proxy.$modal.msgSuccess("娓呯┖鎴愬姛");
-  }).catch(() => {});
+const handleClean = async () => {
+	await proxy?.$modal.confirm("鏄惁纭娓呯┖鎵�鏈夌櫥褰曟棩蹇楁暟鎹」?");
+	await cleanLoginInfo();
+	getList();
+	proxy?.$modal.msgSuccess("娓呯┖鎴愬姛");
 }
 /** 瑙i攣鎸夐挳鎿嶄綔 */
-function handleUnlock() {
+const handleUnlock = async () => {
   const username = selectName.value;
-  proxy.$modal.confirm('鏄惁纭瑙i攣鐢ㄦ埛"' + username + '"鏁版嵁椤�?').then(function () {
-    return unlockLogininfor(username);
-  }).then(() => {
-    proxy.$modal.msgSuccess("鐢ㄦ埛" + username + "瑙i攣鎴愬姛");
-  }).catch(() => {});
+	await proxy?.$modal.confirm('鏄惁纭瑙i攣鐢ㄦ埛"' + username + '"鏁版嵁椤�?');
+	await unlockLoginInfo(username);
+	proxy?.$modal.msgSuccess("鐢ㄦ埛" + username + "瑙i攣鎴愬姛");
 }
 /** 瀵煎嚭鎸夐挳鎿嶄綔 */
-function handleExport() {
-  proxy.download("monitor/logininfor/export", {
+const handleExport = () => {
+  proxy?.download("monitor/logininfor/export", {
     ...queryParams.value,
   }, `config_${new Date().getTime()}.xlsx`);
 }
 
-getList();
+onMounted(() => {
+  getList();
+})
 </script>
+
+<template>
+	<div class="p-2">
+		<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
+			<div class="search" v-show="showSearch">
+				<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
+					<el-form-item label="鐧诲綍鍦板潃" prop="ipaddr">
+						<el-input v-model="queryParams.ipaddr" placeholder="璇疯緭鍏ョ櫥褰曞湴鍧�" clearable style="width: 240px;" @keyup.enter="handleQuery" />
+					</el-form-item>
+					<el-form-item label="鐢ㄦ埛鍚嶇О" prop="userName">
+						<el-input v-model="queryParams.userName" placeholder="璇疯緭鍏ョ敤鎴峰悕绉�" clearable style="width: 240px;" @keyup.enter="handleQuery" />
+					</el-form-item>
+					<el-form-item label="鐘舵��" prop="status">
+						<el-select v-model="queryParams.status" placeholder="鐧诲綍鐘舵��" clearable style="width: 240px">
+							<el-option v-for="dict in sys_common_status" :key="dict.value" :label="dict.label" :value="dict.value" />
+						</el-select>
+					</el-form-item>
+					<el-form-item label="鐧诲綍鏃堕棿" style="width: 308px">
+						<el-date-picker
+							v-model="dateRange"
+							value-format="YYYY-MM-DD HH:mm:ss"
+							type="daterange"
+							range-separator="-"
+							start-placeholder="寮�濮嬫棩鏈�"
+							end-placeholder="缁撴潫鏃ユ湡"
+							:default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"
+						></el-date-picker>
+					</el-form-item>
+					<el-form-item>
+						<el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
+						<el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
+					</el-form-item>
+				</el-form>
+			</div>
+		</transition>
+
+		<el-card shadow="never">
+			<template #header>
+				<el-row :gutter="10" class="mb8">
+					<el-col :span="1.5">
+						<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['monitor:logininfor:remove']">
+							鍒犻櫎
+						</el-button>
+					</el-col>
+					<el-col :span="1.5">
+						<el-button type="danger" plain icon="Delete" @click="handleClean" v-hasPermi="['monitor:logininfor:remove']">娓呯┖</el-button>
+					</el-col>
+					<el-col :span="1.5">
+						<el-button type="primary" plain icon="Unlock" :disabled="single" @click="handleUnlock" v-hasPermi="['monitor:logininfor:unlock']">
+							瑙i攣
+						</el-button>
+					</el-col>
+					<el-col :span="1.5">
+						<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['monitor:logininfor:export']">瀵煎嚭</el-button>
+					</el-col>
+					<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+				</el-row>
+			</template>
+
+			<el-table
+				ref="loginInfoTableRef"
+				v-loading="loading"
+				:data="loginInfoList"
+				@selection-change="handleSelectionChange"
+				:default-sort="defaultSort"
+				@sort-change="handleSortChange"
+			>
+				<el-table-column type="selection" width="55" align="center" />
+				<el-table-column label="璁块棶缂栧彿" align="center" prop="infoId" />
+				<el-table-column
+					label="鐢ㄦ埛鍚嶇О"
+					align="center"
+					prop="userName"
+					:show-overflow-tooltip="true"
+					sortable="custom"
+					:sort-orders="['descending', 'ascending']"
+				/>
+				<el-table-column label="鍦板潃" align="center" prop="ipaddr" :show-overflow-tooltip="true" />
+				<el-table-column label="鐧诲綍鍦扮偣" align="center" prop="loginLocation" :show-overflow-tooltip="true" />
+				<el-table-column label="鎿嶄綔绯荤粺" align="center" prop="os" :show-overflow-tooltip="true" />
+				<el-table-column label="娴忚鍣�" align="center" prop="browser" :show-overflow-tooltip="true" />
+				<el-table-column label="鐧诲綍鐘舵��" align="center" prop="status">
+					<template #default="scope">
+						<dict-tag :options="sys_common_status" :value="scope.row.status" />
+					</template>
+				</el-table-column>
+				<el-table-column label="鎻忚堪" align="center" prop="msg" :show-overflow-tooltip="true" />
+				<el-table-column label="璁块棶鏃堕棿" align="center" prop="loginTime" sortable="custom" :sort-orders="['descending', 'ascending']" width="180">
+					<template #default="scope">
+						<span>{{ parseTime(scope.row.loginTime) }}</span>
+					</template>
+				</el-table-column>
+			</el-table>
+
+			<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
+		</el-card>
+	</div>
+</template>
diff --git a/src/views/monitor/online/index.vue b/src/views/monitor/online/index.vue
index eb17ebc..1084d47 100644
--- a/src/views/monitor/online/index.vue
+++ b/src/views/monitor/online/index.vue
@@ -1,106 +1,104 @@
-<template>
-   <div class="app-container">
-      <el-form :model="queryParams" ref="queryRef" :inline="true">
-         <el-form-item label="鐧诲綍鍦板潃" prop="ipaddr">
-            <el-input
-               v-model="queryParams.ipaddr"
-               placeholder="璇疯緭鍏ョ櫥褰曞湴鍧�"
-               clearable
-               style="width: 200px"
-               @keyup.enter="handleQuery"
-            />
-         </el-form-item>
-         <el-form-item label="鐢ㄦ埛鍚嶇О" prop="userName">
-            <el-input
-               v-model="queryParams.userName"
-               placeholder="璇疯緭鍏ョ敤鎴峰悕绉�"
-               clearable
-               style="width: 200px"
-               @keyup.enter="handleQuery"
-            />
-         </el-form-item>
-         <el-form-item>
-            <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
-            <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
-         </el-form-item>
-      </el-form>
-      <el-table
-         v-loading="loading"
-         :data="onlineList.slice((pageNum - 1) * pageSize, pageNum * pageSize)"
-         style="width: 100%;"
-      >
-         <el-table-column label="搴忓彿" width="50" type="index" align="center">
-            <template #default="scope">
-               <span>{{ (pageNum - 1) * pageSize + scope.$index + 1 }}</span>
-            </template>
-         </el-table-column>
-         <el-table-column label="浼氳瘽缂栧彿" align="center" prop="tokenId" :show-overflow-tooltip="true" />
-         <el-table-column label="鐧诲綍鍚嶇О" align="center" prop="userName" :show-overflow-tooltip="true" />
-         <el-table-column label="鎵�灞為儴闂�" align="center" prop="deptName" :show-overflow-tooltip="true" />
-         <el-table-column label="涓绘満" align="center" prop="ipaddr" :show-overflow-tooltip="true" />
-         <el-table-column label="鐧诲綍鍦扮偣" align="center" prop="loginLocation" :show-overflow-tooltip="true" />
-         <el-table-column label="鎿嶄綔绯荤粺" align="center" prop="os" :show-overflow-tooltip="true" />
-         <el-table-column label="娴忚鍣�" align="center" prop="browser" :show-overflow-tooltip="true" />
-         <el-table-column label="鐧诲綍鏃堕棿" align="center" prop="loginTime" width="180">
-            <template #default="scope">
-               <span>{{ parseTime(scope.row.loginTime) }}</span>
-            </template>
-         </el-table-column>
-         <el-table-column label="鎿嶄綔" align="center" class-name="small-padding fixed-width">
-            <template #default="scope">
-               <el-button link type="primary" icon="Delete" @click="handleForceLogout(scope.row)" v-hasPermi="['monitor:online:forceLogout']">寮洪��</el-button>
-            </template>
-         </el-table-column>
-      </el-table>
-
-      <pagination v-show="total > 0" :total="total" v-model:page="pageNum" v-model:limit="pageSize" />
-   </div>
-</template>
-
-<script setup name="Online">
+<script setup name="Online" lang="ts">
 import { forceLogout, list as initData } from "@/api/monitor/online";
+import { ComponentInternalInstance } from "vue";
+import { OnlineQuery, OnlineVO } from "@/api/monitor/online/types";
 
-const { proxy } = getCurrentInstance();
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 
-const onlineList = ref([]);
+const onlineList = ref<OnlineVO[]>([]);
 const loading = ref(true);
 const total = ref(0);
-const pageNum = ref(1);
-const pageSize = ref(10);
 
-const queryParams = ref({
-  ipaddr: undefined,
-  userName: undefined
+const queryFormRef = ref(ElForm);
+
+const queryParams = ref<OnlineQuery>({
+	pageNum: 1,
+	pageSize: 10,
+	ipaddr: '',
+	userName: ''
 });
 
 /** 鏌ヨ鐧诲綍鏃ュ織鍒楄〃 */
-function getList() {
-  loading.value = true;
-  initData(queryParams.value).then(response => {
-    onlineList.value = response.rows;
-    total.value = response.total;
-    loading.value = false;
-  });
+const getList = async () => {
+	loading.value = true;
+	const res = await initData(queryParams.value);
+	onlineList.value = res.rows;
+	total.value = res.total;
+	loading.value = false;
 }
 /** 鎼滅储鎸夐挳鎿嶄綔 */
-function handleQuery() {
-  pageNum.value = 1;
-  getList();
+const handleQuery = () => {
+	queryParams.value.pageNum = 1;
+	getList();
 }
 /** 閲嶇疆鎸夐挳鎿嶄綔 */
-function resetQuery() {
-  proxy.resetForm("queryRef");
-  handleQuery();
+const resetQuery = () => {
+	queryFormRef.value.resetFields();
+	handleQuery();
 }
 /** 寮洪��鎸夐挳鎿嶄綔 */
-function handleForceLogout(row) {
-    proxy.$modal.confirm('鏄惁纭寮洪��鍚嶇О涓�"' + row.userName + '"鐨勭敤鎴�?').then(function () {
-  return forceLogout(row.tokenId);
-  }).then(() => {
-    getList();
-    proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
-  }).catch(() => {});
+const handleForceLogout = async (row: OnlineVO) => {
+	await proxy?.$modal.confirm('鏄惁纭寮洪��鍚嶇О涓�"' + row.userName + '"鐨勭敤鎴�?');
+	await forceLogout(row.tokenId);
+	getList();
+	proxy?.$modal.msgSuccess("鍒犻櫎鎴愬姛");
 }
 
-getList();
+onMounted(() => {
+	getList();
+})
 </script>
+
+<template>
+	<div class="p-2">
+		<div class="search">
+			<el-form :model="queryParams" ref="queryFormRef" :inline="true">
+				<el-form-item label="鐧诲綍鍦板潃" prop="ipaddr">
+					<el-input v-model="queryParams.ipaddr" placeholder="璇疯緭鍏ョ櫥褰曞湴鍧�" clearable style="width: 200px" @keyup.enter="handleQuery" />
+				</el-form-item>
+				<el-form-item label="鐢ㄦ埛鍚嶇О" prop="userName">
+					<el-input v-model="queryParams.userName" placeholder="璇疯緭鍏ョ敤鎴峰悕绉�" clearable style="width: 200px" @keyup.enter="handleQuery" />
+				</el-form-item>
+				<el-form-item>
+					<el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
+					<el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
+				</el-form-item>
+			</el-form>
+		</div>
+		<div class="panel">
+			<el-table
+				v-loading="loading"
+				:data="onlineList.slice((queryParams.pageNum - 1) * queryParams.pageSize, queryParams.pageNum * queryParams.pageSize)"
+				style="width: 100%;"
+			>
+				<el-table-column label="搴忓彿" width="50" type="index" align="center">
+					<template #default="scope">
+						<span>{{ (queryParams.pageNum - 1) * queryParams.pageSize + scope.$index + 1 }}</span>
+					</template>
+				</el-table-column>
+				<el-table-column label="浼氳瘽缂栧彿" align="center" prop="tokenId" :show-overflow-tooltip="true" />
+				<el-table-column label="鐧诲綍鍚嶇О" align="center" prop="userName" :show-overflow-tooltip="true" />
+				<el-table-column label="鎵�灞為儴闂�" align="center" prop="deptName" :show-overflow-tooltip="true" />
+				<el-table-column label="涓绘満" align="center" prop="ipaddr" :show-overflow-tooltip="true" />
+				<el-table-column label="鐧诲綍鍦扮偣" align="center" prop="loginLocation" :show-overflow-tooltip="true" />
+				<el-table-column label="鎿嶄綔绯荤粺" align="center" prop="os" :show-overflow-tooltip="true" />
+				<el-table-column label="娴忚鍣�" align="center" prop="browser" :show-overflow-tooltip="true" />
+				<el-table-column label="鐧诲綍鏃堕棿" align="center" prop="loginTime" width="180">
+					<template #default="scope">
+						<span>{{ parseTime(scope.row.loginTime) }}</span>
+					</template>
+				</el-table-column>
+				<el-table-column label="鎿嶄綔" align="center" class-name="small-padding fixed-width">
+					<template #default="scope">
+						<el-tooltip content="寮洪��" placement="top">
+							<el-button link type="primary" icon="Delete" @click="handleForceLogout(scope.row)" v-hasPermi="['monitor:online:forceLogout']">
+							</el-button>
+						</el-tooltip>
+					</template>
+				</el-table-column>
+			</el-table>
+
+			<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" />
+		</div>
+	</div>
+</template>
diff --git a/src/views/monitor/operlog/index.vue b/src/views/monitor/operlog/index.vue
index d1760f8..0316c00 100644
--- a/src/views/monitor/operlog/index.vue
+++ b/src/views/monitor/operlog/index.vue
@@ -1,291 +1,295 @@
-<template>
-   <div class="app-container">
-      <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px">
-         <el-form-item label="绯荤粺妯″潡" prop="title">
-            <el-input
-               v-model="queryParams.title"
-               placeholder="璇疯緭鍏ョ郴缁熸ā鍧�"
-               clearable
-               style="width: 240px;"
-               @keyup.enter="handleQuery"
-            />
-         </el-form-item>
-         <el-form-item label="鎿嶄綔浜哄憳" prop="operName">
-            <el-input
-               v-model="queryParams.operName"
-               placeholder="璇疯緭鍏ユ搷浣滀汉鍛�"
-               clearable
-               style="width: 240px;"
-               @keyup.enter="handleQuery"
-            />
-         </el-form-item>
-         <el-form-item label="绫诲瀷" prop="businessType">
-            <el-select
-               v-model="queryParams.businessType"
-               placeholder="鎿嶄綔绫诲瀷"
-               clearable
-               style="width: 240px"
-            >
-               <el-option
-                  v-for="dict in sys_oper_type"
-                  :key="dict.value"
-                  :label="dict.label"
-                  :value="dict.value"
-               />
-            </el-select>
-         </el-form-item>
-         <el-form-item label="鐘舵��" prop="status">
-            <el-select
-               v-model="queryParams.status"
-               placeholder="鎿嶄綔鐘舵��"
-               clearable
-               style="width: 240px"
-            >
-               <el-option
-                  v-for="dict in sys_common_status"
-                  :key="dict.value"
-                  :label="dict.label"
-                  :value="dict.value"
-               />
-            </el-select>
-         </el-form-item>
-         <el-form-item label="鎿嶄綔鏃堕棿" style="width: 308px">
-            <el-date-picker
-               v-model="dateRange"
-               value-format="YYYY-MM-DD HH:mm:ss"
-               type="daterange"
-               range-separator="-"
-               start-placeholder="寮�濮嬫棩鏈�"
-               end-placeholder="缁撴潫鏃ユ湡"
-               :default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"
-            ></el-date-picker>
-         </el-form-item>
-         <el-form-item>
-            <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
-            <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
-         </el-form-item>
-      </el-form>
+<script setup name="Operlog" lang="ts">
+import { list, delOperlog, cleanOperlog } from '@/api/monitor/operlog';
+import { ComponentInternalInstance } from 'vue';
+import { OperLogForm, OperLogQuery, OperLogVO } from '@/api/monitor/operlog/types';
+import { DateModelType } from 'element-plus';
 
-      <el-row :gutter="10" class="mb8">
-         <el-col :span="1.5">
-            <el-button
-               type="danger"
-               plain
-               icon="Delete"
-               :disabled="multiple"
-               @click="handleDelete"
-               v-hasPermi="['monitor:operlog:remove']"
-            >鍒犻櫎</el-button>
-         </el-col>
-         <el-col :span="1.5">
-            <el-button
-               type="danger"
-               plain
-               icon="Delete"
-               @click="handleClean"
-               v-hasPermi="['monitor:operlog:remove']"
-            >娓呯┖</el-button>
-         </el-col>
-         <el-col :span="1.5">
-            <el-button
-               type="warning"
-               plain
-               icon="Download"
-               @click="handleExport"
-               v-hasPermi="['monitor:operlog:export']"
-            >瀵煎嚭</el-button>
-         </el-col>
-         <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
-      </el-row>
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { sys_oper_type, sys_common_status } = toRefs<any>(proxy?.useDict("sys_oper_type","sys_common_status"));
 
-      <el-table ref="operlogRef" v-loading="loading" :data="operlogList" @selection-change="handleSelectionChange" :default-sort="defaultSort" @sort-change="handleSortChange">
-         <el-table-column type="selection" width="50" align="center" />
-         <el-table-column label="鏃ュ織缂栧彿" align="center" prop="operId" />
-         <el-table-column label="绯荤粺妯″潡" align="center" prop="title" :show-overflow-tooltip="true" />
-         <el-table-column label="鎿嶄綔绫诲瀷" align="center" prop="businessType">
-            <template #default="scope">
-               <dict-tag :options="sys_oper_type" :value="scope.row.businessType" />
-            </template>
-         </el-table-column>
-         <el-table-column label="鎿嶄綔浜哄憳" align="center" width="110" prop="operName" :show-overflow-tooltip="true" sortable="custom" :sort-orders="['descending', 'ascending']" />
-         <el-table-column label="涓绘満" align="center" prop="operIp" width="130" :show-overflow-tooltip="true" />
-         <el-table-column label="鎿嶄綔鐘舵��" align="center" prop="status">
-            <template #default="scope">
-               <dict-tag :options="sys_common_status" :value="scope.row.status" />
-            </template>
-         </el-table-column>
-         <el-table-column label="鎿嶄綔鏃ユ湡" align="center" prop="operTime" width="180" sortable="custom" :sort-orders="['descending', 'ascending']">
-            <template #default="scope">
-               <span>{{ parseTime(scope.row.operTime) }}</span>
-            </template>
-         </el-table-column>
-         <el-table-column label="娑堣�楁椂闂�" align="center" prop="costTime" width="110" :show-overflow-tooltip="true" sortable="custom" :sort-orders="['descending', 'ascending']">
-            <template #default="scope">
-               <span>{{ scope.row.costTime }}姣</span>
-            </template>
-         </el-table-column>
-         <el-table-column label="鎿嶄綔" align="center" class-name="small-padding fixed-width">
-            <template #default="scope">
-               <el-button link type="primary" icon="View" @click="handleView(scope.row, scope.index)" v-hasPermi="['monitor:operlog:query']">璇︾粏</el-button>
-            </template>
-         </el-table-column>
-      </el-table>
-
-      <pagination
-         v-show="total > 0"
-         :total="total"
-         v-model:page="queryParams.pageNum"
-         v-model:limit="queryParams.pageSize"
-         @pagination="getList"
-      />
-
-      <!-- 鎿嶄綔鏃ュ織璇︾粏 -->
-      <el-dialog title="鎿嶄綔鏃ュ織璇︾粏" v-model="open" width="700px" append-to-body>
-         <el-form :model="form" label-width="100px">
-            <el-row>
-               <el-col :span="12">
-                  <el-form-item label="鎿嶄綔妯″潡锛�">{{ form.title }} / {{ typeFormat(form) }}</el-form-item>
-                  <el-form-item
-                    label="鐧诲綍淇℃伅锛�"
-                  >{{ form.operName }} / {{ form.operIp }} / {{ form.operLocation }}</el-form-item>
-               </el-col>
-               <el-col :span="12">
-                  <el-form-item label="璇锋眰鍦板潃锛�">{{ form.operUrl }}</el-form-item>
-                  <el-form-item label="璇锋眰鏂瑰紡锛�">{{ form.requestMethod }}</el-form-item>
-               </el-col>
-               <el-col :span="24">
-                  <el-form-item label="鎿嶄綔鏂规硶锛�">{{ form.method }}</el-form-item>
-               </el-col>
-               <el-col :span="24">
-                  <el-form-item label="璇锋眰鍙傛暟锛�">{{ form.operParam }}</el-form-item>
-               </el-col>
-               <el-col :span="24">
-                  <el-form-item label="杩斿洖鍙傛暟锛�">{{ form.jsonResult }}</el-form-item>
-               </el-col>
-               <el-col :span="6">
-                  <el-form-item label="鎿嶄綔鐘舵�侊細">
-                     <div v-if="form.status === 0">姝e父</div>
-                     <div v-else-if="form.status === 1">澶辫触</div>
-                  </el-form-item>
-               </el-col>
-               <el-col :span="8">
-                  <el-form-item label="娑堣�楁椂闂达細">{{ form.costTime }}姣</el-form-item>
-               </el-col>
-               <el-col :span="10">
-                  <el-form-item label="鎿嶄綔鏃堕棿锛�">{{ parseTime(form.operTime) }}</el-form-item>
-               </el-col>
-               <el-col :span="24">
-                  <el-form-item label="寮傚父淇℃伅锛�" v-if="form.status === 1">{{ form.errorMsg }}</el-form-item>
-               </el-col>
-            </el-row>
-         </el-form>
-         <template #footer>
-            <div class="dialog-footer">
-               <el-button @click="open = false">鍏� 闂�</el-button>
-            </div>
-         </template>
-      </el-dialog>
-   </div>
-</template>
-
-<script setup name="Operlog">
-import { list, delOperlog, cleanOperlog } from "@/api/monitor/operlog";
-
-const { proxy } = getCurrentInstance();
-const { sys_oper_type, sys_common_status } = proxy.useDict("sys_oper_type","sys_common_status");
-
-const operlogList = ref([]);
-const open = ref(false);
+const operlogList = ref<OperLogVO[]>([]);
 const loading = ref(true);
 const showSearch = ref(true);
-const ids = ref([]);
-const single = ref(true);
+const ids = ref<Array<number | string>>([]);
 const multiple = ref(true);
 const total = ref(0);
-const title = ref("");
-const dateRange = ref([]);
-const defaultSort = ref({ prop: "operTime", order: "descending" });
+const dateRange = ref<[DateModelType, DateModelType]>(['', '']);
+const defaultSort = ref<any>({ prop: "operTime", order: "descending" });
 
-const data = reactive({
-  form: {},
+const operLogTableRef = ref(ElTable);
+const queryFormRef = ref(ElForm);
+
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+
+
+const data = reactive<PageData<OperLogForm, OperLogQuery>>({
+  form: {
+    operId: undefined,
+    tenantId: undefined,
+    title: '',
+    businessType: 0,
+    businessTypes: undefined,
+    method: '',
+    requestMethod: '',
+    operatorType: 0,
+    operName: '',
+    deptName: '',
+    operUrl: '',
+    operIp: '',
+    operLocation: '',
+    operParam: '',
+    jsonResult: '',
+    status: 0,
+    errorMsg: '',
+    operTime: '',
+    costTime: 0
+  },
   queryParams: {
     pageNum: 1,
     pageSize: 10,
-    title: undefined,
-    operName: undefined,
-    businessType: undefined,
-    status: undefined
-  }
+    title: '',
+    operName: '',
+    businessType: '',
+    status: '',
+    orderByColumn: defaultSort.value.prop,
+    isAsc: defaultSort.value.order
+  },
+  rules: {}
 });
 
 const { queryParams, form } = toRefs(data);
 
 /** 鏌ヨ鐧诲綍鏃ュ織 */
-function getList() {
+const getList = async () => {
   loading.value = true;
-  list(proxy.addDateRange(queryParams.value, dateRange.value)).then(response => {
-    operlogList.value = response.rows;
-    total.value = response.total;
-    loading.value = false;
-  });
+	const res = await list(proxy?.addDateRange(queryParams.value, dateRange.value));
+	operlogList.value = res.rows;
+	total.value = res.total;
+	loading.value = false;
 }
 /** 鎿嶄綔鏃ュ織绫诲瀷瀛楀吀缈昏瘧 */
-function typeFormat(row, column) {
-  return proxy.selectDictLabel(sys_oper_type.value, row.businessType);
+const typeFormat = (row: OperLogForm) => {
+  return proxy?.selectDictLabel(sys_oper_type.value, row.businessType);
 }
 /** 鎼滅储鎸夐挳鎿嶄綔 */
-function handleQuery() {
+const handleQuery = () => {
   queryParams.value.pageNum = 1;
   getList();
 }
 /** 閲嶇疆鎸夐挳鎿嶄綔 */
-function resetQuery() {
-  dateRange.value = [];
-  proxy.resetForm("queryRef");
+const resetQuery = () => {
+  dateRange.value = ['', ''];
+  queryFormRef.value.resetFields();
   queryParams.value.pageNum = 1;
-  proxy.$refs["operlogRef"].sort(defaultSort.value.prop, defaultSort.value.order);
+  operLogTableRef.value.sort(defaultSort.value.prop, defaultSort.value.order);
 }
 /** 澶氶�夋閫変腑鏁版嵁 */
-function handleSelectionChange(selection) {
+const handleSelectionChange = (selection: OperLogVO[]) => {
   ids.value = selection.map(item => item.operId);
   multiple.value = !selection.length;
 }
 /** 鎺掑簭瑙﹀彂浜嬩欢 */
-function handleSortChange(column, prop, order) {
+const handleSortChange = (column: any) => {
   queryParams.value.orderByColumn = column.prop;
   queryParams.value.isAsc = column.order;
   getList();
 }
 /** 璇︾粏鎸夐挳鎿嶄綔 */
-function handleView(row) {
-  open.value = true;
+const handleView = (row: OperLogVO) => {
+  dialog.visible = true;
   form.value = row;
 }
 /** 鍒犻櫎鎸夐挳鎿嶄綔 */
-function handleDelete(row) {
-  const operIds = row.operId || ids.value;
-  proxy.$modal.confirm('鏄惁纭鍒犻櫎鏃ュ織缂栧彿涓�"' + operIds + '"鐨勬暟鎹」?').then(function () {
-    return delOperlog(operIds);
-  }).then(() => {
-    getList();
-    proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
-  }).catch(() => {});
+const handleDelete = async (row?: OperLogVO) => {
+  const operIds = row?.operId || ids.value;
+	await proxy?.$modal.confirm('鏄惁纭鍒犻櫎鏃ュ織缂栧彿涓�"' + operIds + '"鐨勬暟鎹」?');
+	await delOperlog(operIds);
+	getList();
+	proxy?.$modal.msgSuccess("鍒犻櫎鎴愬姛");
 }
+
 /** 娓呯┖鎸夐挳鎿嶄綔 */
-function handleClean() {
-  proxy.$modal.confirm("鏄惁纭娓呯┖鎵�鏈夋搷浣滄棩蹇楁暟鎹」?").then(function () {
-    return cleanOperlog();
-  }).then(() => {
-    getList();
-    proxy.$modal.msgSuccess("娓呯┖鎴愬姛");
-  }).catch(() => {});
+const handleClean = async () => {
+	await proxy?.$modal.confirm("鏄惁纭娓呯┖鎵�鏈夋搷浣滄棩蹇楁暟鎹」?");
+	await cleanOperlog();
+	getList();
+	proxy?.$modal.msgSuccess("娓呯┖鎴愬姛");
 }
+
 /** 瀵煎嚭鎸夐挳鎿嶄綔 */
-function handleExport() {
-  proxy.download("monitor/operlog/export",{
+const handleExport = () => {
+  proxy?.download("monitor/operlog/export", {
     ...queryParams.value,
   }, `config_${new Date().getTime()}.xlsx`);
 }
-
-getList();
+onMounted(() => {
+  getList();
+})
 </script>
+<template>
+	<div class="p-2">
+		<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
+			<div class="search" v-show="showSearch">
+				<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
+					<el-form-item label="绯荤粺妯″潡" prop="title">
+						<el-input v-model="queryParams.title" placeholder="璇疯緭鍏ョ郴缁熸ā鍧�" clearable style="width: 240px;" @keyup.enter="handleQuery" />
+					</el-form-item>
+					<el-form-item label="鎿嶄綔浜哄憳" prop="operName">
+						<el-input v-model="queryParams.operName" placeholder="璇疯緭鍏ユ搷浣滀汉鍛�" clearable style="width: 240px;" @keyup.enter="handleQuery" />
+					</el-form-item>
+					<el-form-item label="绫诲瀷" prop="businessType">
+						<el-select v-model="queryParams.businessType" placeholder="鎿嶄綔绫诲瀷" clearable style="width: 240px">
+							<el-option v-for="dict in sys_oper_type" :key="dict.value" :label="dict.label" :value="dict.value" />
+						</el-select>
+					</el-form-item>
+					<el-form-item label="鐘舵��" prop="status">
+						<el-select v-model="queryParams.status" placeholder="鎿嶄綔鐘舵��" clearable style="width: 240px">
+							<el-option v-for="dict in sys_common_status" :key="dict.value" :label="dict.label" :value="dict.value" />
+						</el-select>
+					</el-form-item>
+					<el-form-item label="鎿嶄綔鏃堕棿" style="width: 308px">
+						<el-date-picker
+							v-model="dateRange"
+							value-format="YYYY-MM-DD HH:mm:ss"
+							type="daterange"
+							range-separator="-"
+							start-placeholder="寮�濮嬫棩鏈�"
+							end-placeholder="缁撴潫鏃ユ湡"
+							:default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"
+						></el-date-picker>
+					</el-form-item>
+					<el-form-item>
+						<el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
+						<el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
+					</el-form-item>
+				</el-form>
+			</div>
+		</transition>
+
+		<el-card shadow="never">
+			<template #header>
+				<el-row :gutter="10" class="mb8">
+					<el-col :span="1.5">
+						<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['monitor:operlog:remove']">
+							鍒犻櫎
+						</el-button>
+					</el-col>
+					<el-col :span="1.5">
+						<el-button type="danger" plain icon="WarnTriangleFilled" @click="handleClean" v-hasPermi="['monitor:operlog:remove']">娓呯┖</el-button>
+					</el-col>
+					<el-col :span="1.5">
+						<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['monitor:operlog:export']">瀵煎嚭</el-button>
+					</el-col>
+					<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+				</el-row>
+			</template>
+
+			<el-table
+				ref="operLogTableRef"
+				v-loading="loading"
+				:data="operlogList"
+				@selection-change="handleSelectionChange"
+				:default-sort="defaultSort"
+				@sort-change="handleSortChange"
+			>
+				<el-table-column type="selection" width="50" align="center" />
+				<el-table-column label="鏃ュ織缂栧彿" align="center" prop="operId" />
+				<el-table-column label="绯荤粺妯″潡" align="center" prop="title" :show-overflow-tooltip="true" />
+				<el-table-column label="鎿嶄綔绫诲瀷" align="center" prop="businessType">
+					<template #default="scope">
+						<dict-tag :options="sys_oper_type" :value="scope.row.businessType" />
+					</template>
+				</el-table-column>
+				<el-table-column
+					label="鎿嶄綔浜哄憳"
+					align="center"
+					width="110"
+					prop="operName"
+					:show-overflow-tooltip="true"
+					sortable="custom"
+					:sort-orders="['descending', 'ascending']"
+				/>
+				<el-table-column label="涓绘満" align="center" prop="operIp" width="130" :show-overflow-tooltip="true" />
+				<el-table-column label="鎿嶄綔鐘舵��" align="center" prop="status">
+					<template #default="scope">
+						<dict-tag :options="sys_common_status" :value="scope.row.status" />
+					</template>
+				</el-table-column>
+				<el-table-column label="鎿嶄綔鏃ユ湡" align="center" prop="operTime" width="180" sortable="custom" :sort-orders="['descending', 'ascending']">
+					<template #default="scope">
+						<span>{{ parseTime(scope.row.operTime) }}</span>
+					</template>
+				</el-table-column>
+				<el-table-column
+					label="娑堣�楁椂闂�"
+					align="center"
+					prop="costTime"
+					width="110"
+					:show-overflow-tooltip="true"
+					sortable="custom"
+					:sort-orders="['descending', 'ascending']"
+				>
+					<template #default="scope">
+						<span>{{ scope.row.costTime }}姣</span>
+					</template>
+				</el-table-column>
+				<el-table-column label="鎿嶄綔" fixed="right" align="center" class-name="small-padding fixed-width">
+					<template #default="scope">
+						<el-tooltip content="璇︾粏" placement="top">
+							<el-button link type="primary" icon="View" @click="handleView(scope.row)" v-hasPermi="['monitor:operlog:query']"> </el-button>
+						</el-tooltip>
+					</template>
+				</el-table-column>
+			</el-table>
+
+			<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
+		</el-card>
+		<!-- 鎿嶄綔鏃ュ織璇︾粏 -->
+		<el-dialog title="鎿嶄綔鏃ュ織璇︾粏" v-model="dialog.visible" width="700px" append-to-body>
+			<el-form :model="form" label-width="100px">
+				<el-row>
+					<el-col :span="12">
+						<el-form-item label="鎿嶄綔妯″潡锛�">{{ form.title }} / {{ typeFormat(form) }}</el-form-item>
+						<el-form-item label="鐧诲綍淇℃伅锛�">{{ form.operName }} / {{ form.operIp }} / {{ form.operLocation }}</el-form-item>
+					</el-col>
+					<el-col :span="12">
+						<el-form-item label="璇锋眰鍦板潃锛�">{{ form.operUrl }}</el-form-item>
+						<el-form-item label="璇锋眰鏂瑰紡锛�">{{ form.requestMethod }}</el-form-item>
+					</el-col>
+					<el-col :span="24">
+						<el-form-item label="鎿嶄綔鏂规硶锛�">{{ form.method }}</el-form-item>
+					</el-col>
+					<el-col :span="24">
+						<el-form-item label="璇锋眰鍙傛暟锛�">{{form.operParam}}</el-form-item>
+					</el-col>
+					<el-col :span="24">
+						<el-form-item label="杩斿洖鍙傛暟锛�">{{ form.jsonResult }}</el-form-item>
+					</el-col>
+					<el-col :span="6">
+						<el-form-item label="鎿嶄綔鐘舵�侊細">
+							<div v-if="form.status === 0">姝e父</div>
+							<div v-else-if="form.status === 1">澶辫触</div>
+						</el-form-item>
+					</el-col>
+					<el-col :span="8">
+						<el-form-item label="娑堣�楁椂闂达細">{{ form.costTime }}姣</el-form-item>
+					</el-col>
+					<el-col :span="10">
+						<el-form-item label="鎿嶄綔鏃堕棿锛�">{{ parseTime(form.operTime) }}</el-form-item>
+					</el-col>
+					<el-col :span="24">
+						<el-form-item label="寮傚父淇℃伅锛�" v-if="form.status === 1">{{ form.errorMsg }}</el-form-item>
+					</el-col>
+				</el-row>
+			</el-form>
+			<template #footer>
+				<div class="dialog-footer">
+					<el-button @click="dialog.visible = false">鍏� 闂�</el-button>
+				</div>
+			</template>
+		</el-dialog>
+	</div>
+</template>
diff --git a/src/views/monitor/xxljob/index.vue b/src/views/monitor/xxljob/index.vue
index 9a73f52..d9e9e43 100644
--- a/src/views/monitor/xxljob/index.vue
+++ b/src/views/monitor/xxljob/index.vue
@@ -1,13 +1,9 @@
 <template>
-  <div>
-    <i-frame v-model:src="url"></i-frame>
-  </div>
+	<div>
+		<i-frame v-model:src="url"></i-frame>
+	</div>
 </template>
 
-<script setup>
-import iFrame from '@/components/iFrame'
-
-import { ref } from 'vue';
-
+<script setup lang="ts">
 const url = ref(import.meta.env.VITE_APP_XXL_JOB_ADMIN);
 </script>
diff --git a/src/views/redirect/index.vue b/src/views/redirect/index.vue
index a469960..48333ba 100644
--- a/src/views/redirect/index.vue
+++ b/src/views/redirect/index.vue
@@ -1,5 +1,5 @@
 <template>
-  <div></div>
+	<div></div>
 </template>
 
 <script setup>
@@ -11,4 +11,4 @@
 const { path } = params
 
 router.replace({ path: '/' + path, query })
-</script>
\ No newline at end of file
+</script>
diff --git a/src/views/register.vue b/src/views/register.vue
index d794768..ce7a901 100644
--- a/src/views/register.vue
+++ b/src/views/register.vue
@@ -1,99 +1,12 @@
-<template>
-  <div class="register">
-    <el-form ref="registerRef" :model="registerForm" :rules="registerRules" class="register-form">
-      <h3 class="title">RuoYi-Vue-Plus澶氱鎴风鐞嗙郴缁�</h3>
-      <el-form-item prop="tenantId">
-        <el-select v-model="registerForm.tenantId" filterable placeholder="璇烽�夋嫨/杈撳叆鍏徃鍚嶇О" style="width: 100%">
-          <el-option
-              v-for="item in tenantList"
-              :key="item.tenantId"
-              :label="item.companyName"
-              :value="item.tenantId">
-          </el-option>
-          <template #prefix><svg-icon icon-class="company" class="el-input__icon input-icon" /></template>
-        </el-select>
-      </el-form-item>
-      <el-form-item prop="username">
-        <el-input 
-          v-model="registerForm.username" 
-          type="text" 
-          size="large" 
-          auto-complete="off" 
-          placeholder="璐﹀彿"
-        >
-          <template #prefix><svg-icon icon-class="user" class="el-input__icon input-icon" /></template>
-        </el-input>
-      </el-form-item>
-      <el-form-item prop="password">
-        <el-input
-          v-model="registerForm.password"
-          type="password"
-          size="large" 
-          auto-complete="off"
-          placeholder="瀵嗙爜"
-          @keyup.enter="handleRegister"
-        >
-          <template #prefix><svg-icon icon-class="password" class="el-input__icon input-icon" /></template>
-        </el-input>
-      </el-form-item>
-      <el-form-item prop="confirmPassword">
-        <el-input
-          v-model="registerForm.confirmPassword"
-          type="password"
-          size="large" 
-          auto-complete="off"
-          placeholder="纭瀵嗙爜"
-          @keyup.enter="handleRegister"
-        >
-          <template #prefix><svg-icon icon-class="password" class="el-input__icon input-icon" /></template>
-        </el-input>
-      </el-form-item>
-      <el-form-item prop="code" v-if="captchaEnabled">
-        <el-input
-          size="large" 
-          v-model="registerForm.code"
-          auto-complete="off"
-          placeholder="楠岃瘉鐮�"
-          style="width: 63%"
-          @keyup.enter="handleRegister"
-        >
-          <template #prefix><svg-icon icon-class="validCode" class="el-input__icon input-icon" /></template>
-        </el-input>
-        <div class="register-code">
-          <img :src="codeUrl" @click="getCode" class="register-code-img"/>
-        </div>
-      </el-form-item>
-      <el-form-item style="width:100%;">
-        <el-button
-          :loading="loading"
-          size="large" 
-          type="primary"
-          style="width:100%;"
-          @click.prevent="handleRegister"
-        >
-          <span v-if="!loading">娉� 鍐�</span>
-          <span v-else>娉� 鍐� 涓�...</span>
-        </el-button>
-        <div style="float: right;">
-          <router-link class="link-type" :to="'/login'">浣跨敤宸叉湁璐︽埛鐧诲綍</router-link>
-        </div>
-      </el-form-item>
-    </el-form>
-    <!--  搴曢儴  -->
-    <div class="el-register-footer">
-      <span>Copyright 漏 2018-2023 ruoyi.vip All Rights Reserved.</span>
-    </div>
-  </div>
-</template>
-
-<script setup>
-import { ElMessageBox } from "element-plus";
-import { getCodeImg, register, getTenantList } from "@/api/login";
+<script setup lang="ts">
+import { getCodeImg, register, getTenantList } from '@/api/login';
+import { RegisterForm, TenantVO } from '@/api/types';
+import { FormRules } from 'element-plus';
+import { to } from 'await-to-js';
 
 const router = useRouter();
-const { proxy } = getCurrentInstance();
 
-const registerForm = ref({
+const registerForm = ref<RegisterForm>({
   tenantId: "",
   username: "",
   password: "",
@@ -103,7 +16,11 @@
   userType: "sys_user"
 });
 
-const equalToPassword = (rule, value, callback) => {
+// 绉熸埛寮�鍏�
+const tenantEnabled = ref(true);
+
+
+const equalToPassword = (rule: any, value: string, callback: any) => {
   if (registerForm.value.password !== value) {
     callback(new Error("涓ゆ杈撳叆鐨勫瘑鐮佷笉涓�鑷�"));
   } else {
@@ -111,7 +28,7 @@
   }
 };
 
-const registerRules = {
+const registerRules: FormRules = {
   tenantId: [
     { required: true, trigger: "blur", message: "璇疯緭鍏ユ偍鐨勭鎴风紪鍙�" }
   ],
@@ -129,59 +46,119 @@
   ],
   code: [{ required: true, trigger: "change", message: "璇疯緭鍏ラ獙璇佺爜" }]
 };
-
 const codeUrl = ref("");
 const loading = ref(false);
 const captchaEnabled = ref(true);
+const registerRef = ref(ElForm);
 // 绉熸埛鍒楄〃
-const tenantList = ref([]);
+const tenantList = ref<TenantVO[]>([]);
 
-function handleRegister() {
-  proxy.$refs.registerRef.validate(valid => {
+const handleRegister = () => {
+  registerRef.value.validate(async (valid: boolean) => {
     if (valid) {
       loading.value = true;
-      register(registerForm.value).then(res => {
+      const [err] = await to(register(registerForm.value));
+      if (!err) {
         const username = registerForm.value.username;
-        ElMessageBox.alert("<font color='red'>鎭枩浣狅紝鎮ㄧ殑璐﹀彿 " + username + " 娉ㄥ唽鎴愬姛锛�</font>", "绯荤粺鎻愮ず", {
+        await ElMessageBox.alert("<font color='red'>鎭枩浣狅紝鎮ㄧ殑璐﹀彿 " + username + " 娉ㄥ唽鎴愬姛锛�</font>", "绯荤粺鎻愮ず", {
           dangerouslyUseHTMLString: true,
           type: "success",
-        }).then(() => {
-          router.push("/login");
-        }).catch(() => {});
-      }).catch(() => {
+        });
+        await router.push("/login");
+      } else {
         loading.value = false;
         if (captchaEnabled) {
           getCode();
         }
-      });
+      }
     }
   });
 }
 
-function getCode() {
-  getCodeImg().then(res => {
-    captchaEnabled.value = res.captchaEnabled === undefined ? true : res.captchaEnabled;
+const getCode = async () => {
+  const { data } = await getCodeImg();
+  captchaEnabled.value = data.captchaEnabled === undefined ? true : data.captchaEnabled;
     if (captchaEnabled.value) {
-      codeUrl.value = "data:image/gif;base64," + res.img;
-      registerForm.value.uuid = res.uuid;
+      codeUrl.value = "data:image/gif;base64," + data.img;
+      registerForm.value.uuid = data.uuid;
     }
-  });
 }
 
-function initTenantList() {
-  getTenantList().then(res => {
-    tenantList.value = res.data;
-    if (tenantList.value != null && tenantList.value.length !== 0) {
-      loginForm.value.tenantId = tenantList.value[0].tenantId;
+const initTenantList = async () => {
+  const { data } = await getTenantList();
+    tenantEnabled.value = data.tenantEnabled === undefined ? true : data.tenantEnabled;
+    if (tenantEnabled.value) {
+      tenantList.value = data.voList;
+      if (tenantList.value != null && tenantList.value.length !== 0) {
+          registerForm.value.tenantId = tenantList.value[0].tenantId;
+      }
     }
-  });
 }
 
-getCode();
-initTenantList();
+onMounted(() => {
+  getCode();
+  initTenantList();
+})
 </script>
 
-<style lang='scss' scoped>
+<template>
+	<div class="register">
+		<el-form ref="registerRef" :model="registerForm" :rules="registerRules" class="register-form">
+			<h3 class="title">RuoYi-Vue-Plus澶氱鎴风鐞嗙郴缁�</h3>
+			<el-form-item prop="tenantId" v-if="tenantEnabled">
+				<el-select v-model="registerForm.tenantId" filterable placeholder="璇烽�夋嫨/杈撳叆鍏徃鍚嶇О" style="width: 100%">
+					<el-option v-for="item in tenantList" :key="item.tenantId" :label="item.companyName" :value="item.tenantId"> </el-option>
+					<template #prefix><svg-icon icon-class="company" class="el-input__icon input-icon" /></template>
+				</el-select>
+			</el-form-item>
+			<el-form-item prop="username">
+				<el-input v-model="registerForm.username" type="text" size="large" auto-complete="off" placeholder="璐﹀彿">
+					<template #prefix><svg-icon icon-class="user" class="el-input__icon input-icon" /></template>
+				</el-input>
+			</el-form-item>
+			<el-form-item prop="password">
+				<el-input v-model="registerForm.password" type="password" size="large" auto-complete="off" placeholder="瀵嗙爜" @keyup.enter="handleRegister">
+					<template #prefix><svg-icon icon-class="password" class="el-input__icon input-icon" /></template>
+				</el-input>
+			</el-form-item>
+			<el-form-item prop="confirmPassword">
+				<el-input
+					v-model="registerForm.confirmPassword"
+					type="password"
+					size="large"
+					auto-complete="off"
+					placeholder="纭瀵嗙爜"
+					@keyup.enter="handleRegister"
+				>
+					<template #prefix><svg-icon icon-class="password" class="el-input__icon input-icon" /></template>
+				</el-input>
+			</el-form-item>
+			<el-form-item prop="code" v-if="captchaEnabled">
+				<el-input size="large" v-model="registerForm.code" auto-complete="off" placeholder="楠岃瘉鐮�" style="width: 63%" @keyup.enter="handleRegister">
+					<template #prefix><svg-icon icon-class="validCode" class="el-input__icon input-icon" /></template>
+				</el-input>
+				<div class="register-code">
+					<img :src="codeUrl" @click="getCode" class="register-code-img" />
+				</div>
+			</el-form-item>
+			<el-form-item style="width:100%;">
+				<el-button :loading="loading" size="large" type="primary" style="width:100%;" @click.prevent="handleRegister">
+					<span v-if="!loading">娉� 鍐�</span>
+					<span v-else>娉� 鍐� 涓�...</span>
+				</el-button>
+				<div style="float: right;">
+					<router-link class="link-type" :to="'/login'">浣跨敤宸叉湁璐︽埛鐧诲綍</router-link>
+				</div>
+			</el-form-item>
+		</el-form>
+		<!--  搴曢儴  -->
+		<div class="el-register-footer">
+			<span>Copyright 漏 2018-2023 ruoyi.vip All Rights Reserved.</span>
+		</div>
+	</div>
+</template>
+
+<style lang="scss" scoped>
 .register {
   display: flex;
   justify-content: center;
@@ -190,8 +167,9 @@
   background-image: url("../assets/images/login-background.jpg");
   background-size: cover;
 }
+
 .title {
-  margin: 0px auto 30px auto;
+  margin: 0 auto 30px auto;
   text-align: center;
   color: #707070;
 }
@@ -201,32 +179,39 @@
   background: #ffffff;
   width: 400px;
   padding: 25px 25px 5px 25px;
+
   .el-input {
     height: 40px;
+
     input {
       height: 40px;
     }
   }
+
   .input-icon {
     height: 39px;
     width: 14px;
-    margin-left: 0px;
+    margin-left: 0;
   }
 }
+
 .register-tip {
   font-size: 13px;
   text-align: center;
   color: #bfbfbf;
 }
+
 .register-code {
   width: 33%;
   height: 40px;
   float: right;
+
   img {
     cursor: pointer;
     vertical-align: middle;
   }
 }
+
 .el-register-footer {
   height: 40px;
   line-height: 40px;
@@ -235,10 +220,11 @@
   width: 100%;
   text-align: center;
   color: #fff;
-  font-family: Arial;
+  font-family: Arial, serif;
   font-size: 12px;
   letter-spacing: 1px;
 }
+
 .register-code-img {
   height: 40px;
   padding-left: 12px;
diff --git a/src/views/system/config/index.vue b/src/views/system/config/index.vue
index f1c0e0b..09a0493 100644
--- a/src/views/system/config/index.vue
+++ b/src/views/system/config/index.vue
@@ -1,195 +1,43 @@
-<template>
-   <div class="app-container">
-      <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px">
-         <el-form-item label="鍙傛暟鍚嶇О" prop="configName">
-            <el-input
-               v-model="queryParams.configName"
-               placeholder="璇疯緭鍏ュ弬鏁板悕绉�"
-               clearable
-               style="width: 240px"
-               @keyup.enter="handleQuery"
-            />
-         </el-form-item>
-         <el-form-item label="鍙傛暟閿悕" prop="configKey">
-            <el-input
-               v-model="queryParams.configKey"
-               placeholder="璇疯緭鍏ュ弬鏁伴敭鍚�"
-               clearable
-               style="width: 240px"
-               @keyup.enter="handleQuery"
-            />
-         </el-form-item>
-         <el-form-item label="绯荤粺鍐呯疆" prop="configType">
-            <el-select v-model="queryParams.configType" placeholder="绯荤粺鍐呯疆" clearable>
-               <el-option
-                  v-for="dict in sys_yes_no"
-                  :key="dict.value"
-                  :label="dict.label"
-                  :value="dict.value"
-               />
-            </el-select>
-         </el-form-item>
-         <el-form-item label="鍒涘缓鏃堕棿" style="width: 308px;">
-            <el-date-picker
-               v-model="dateRange"
-               value-format="YYYY-MM-DD HH:mm:ss"
-               type="daterange"
-               range-separator="-"
-               start-placeholder="寮�濮嬫棩鏈�"
-               end-placeholder="缁撴潫鏃ユ湡"
-               :default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"
-            ></el-date-picker>
-         </el-form-item>
-         <el-form-item>
-            <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
-            <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
-         </el-form-item>
-      </el-form>
-
-      <el-row :gutter="10" class="mb8">
-         <el-col :span="1.5">
-            <el-button
-               type="primary"
-               plain
-               icon="Plus"
-               @click="handleAdd"
-               v-hasPermi="['system:config:add']"
-            >鏂板</el-button>
-         </el-col>
-         <el-col :span="1.5">
-            <el-button
-               type="success"
-               plain
-               icon="Edit"
-               :disabled="single"
-               @click="handleUpdate"
-               v-hasPermi="['system:config:edit']"
-            >淇敼</el-button>
-         </el-col>
-         <el-col :span="1.5">
-            <el-button
-               type="danger"
-               plain
-               icon="Delete"
-               :disabled="multiple"
-               @click="handleDelete"
-               v-hasPermi="['system:config:remove']"
-            >鍒犻櫎</el-button>
-         </el-col>
-         <el-col :span="1.5">
-            <el-button
-               type="warning"
-               plain
-               icon="Download"
-               @click="handleExport"
-               v-hasPermi="['system:config:export']"
-            >瀵煎嚭</el-button>
-         </el-col>
-         <el-col :span="1.5">
-            <el-button
-               type="danger"
-               plain
-               icon="Refresh"
-               @click="handleRefreshCache"
-               v-hasPermi="['system:config:remove']"
-            >鍒锋柊缂撳瓨</el-button>
-         </el-col>
-         <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
-      </el-row>
-
-      <el-table v-loading="loading" :data="configList" @selection-change="handleSelectionChange">
-         <el-table-column type="selection" width="55" align="center" />
-         <el-table-column label="鍙傛暟涓婚敭" align="center" prop="configId" v-if="false" />
-         <el-table-column label="鍙傛暟鍚嶇О" align="center" prop="configName" :show-overflow-tooltip="true" />
-         <el-table-column label="鍙傛暟閿悕" align="center" prop="configKey" :show-overflow-tooltip="true" />
-         <el-table-column label="鍙傛暟閿��" align="center" prop="configValue" :show-overflow-tooltip="true" />
-         <el-table-column label="绯荤粺鍐呯疆" align="center" prop="configType">
-            <template #default="scope">
-               <dict-tag :options="sys_yes_no" :value="scope.row.configType" />
-            </template>
-         </el-table-column>
-         <el-table-column label="澶囨敞" align="center" prop="remark" :show-overflow-tooltip="true" />
-         <el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime" width="180">
-            <template #default="scope">
-               <span>{{ parseTime(scope.row.createTime) }}</span>
-            </template>
-         </el-table-column>
-         <el-table-column label="鎿嶄綔" align="center" width="150" class-name="small-padding fixed-width">
-            <template #default="scope">
-               <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:config:edit']" >淇敼</el-button>
-               <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:config:remove']">鍒犻櫎</el-button>
-            </template>
-         </el-table-column>
-      </el-table>
-
-      <pagination
-         v-show="total > 0"
-         :total="total"
-         v-model:page="queryParams.pageNum"
-         v-model:limit="queryParams.pageSize"
-         @pagination="getList"
-      />
-
-      <!-- 娣诲姞鎴栦慨鏀瑰弬鏁伴厤缃璇濇 -->
-      <el-dialog :title="title" v-model="open" width="500px" append-to-body>
-         <el-form ref="configRef" :model="form" :rules="rules" label-width="80px">
-            <el-form-item label="鍙傛暟鍚嶇О" prop="configName">
-               <el-input v-model="form.configName" placeholder="璇疯緭鍏ュ弬鏁板悕绉�" />
-            </el-form-item>
-            <el-form-item label="鍙傛暟閿悕" prop="configKey">
-               <el-input v-model="form.configKey" placeholder="璇疯緭鍏ュ弬鏁伴敭鍚�" />
-            </el-form-item>
-            <el-form-item label="鍙傛暟閿��" prop="configValue">
-               <el-input v-model="form.configValue" placeholder="璇疯緭鍏ュ弬鏁伴敭鍊�" />
-            </el-form-item>
-            <el-form-item label="绯荤粺鍐呯疆" prop="configType">
-               <el-radio-group v-model="form.configType">
-                  <el-radio
-                     v-for="dict in sys_yes_no"
-                     :key="dict.value"
-                     :label="dict.value"
-                  >{{ dict.label }}</el-radio>
-               </el-radio-group>
-            </el-form-item>
-            <el-form-item label="澶囨敞" prop="remark">
-               <el-input v-model="form.remark" type="textarea" placeholder="璇疯緭鍏ュ唴瀹�" />
-            </el-form-item>
-         </el-form>
-         <template #footer>
-            <div class="dialog-footer">
-               <el-button type="primary" @click="submitForm">纭� 瀹�</el-button>
-               <el-button @click="cancel">鍙� 娑�</el-button>
-            </div>
-         </template>
-      </el-dialog>
-   </div>
-</template>
-
-<script setup name="Config">
+<script setup name="Config" lang="ts">
 import { listConfig, getConfig, delConfig, addConfig, updateConfig, refreshCache } from "@/api/system/config";
+import { ConfigForm, ConfigQuery, ConfigVO } from "@/api/system/config/types";
+import { ComponentInternalInstance } from "vue";
+import { DateModelType } from 'element-plus';
 
-const { proxy } = getCurrentInstance();
-const { sys_yes_no } = proxy.useDict("sys_yes_no");
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { sys_yes_no } = toRefs<any>(proxy?.useDict("sys_yes_no"));
 
-const configList = ref([]);
-const open = ref(false);
+const configList = ref<ConfigVO[]>([]);
 const loading = ref(true);
 const showSearch = ref(true);
-const ids = ref([]);
+const ids = ref<Array<number | string>>([]);
 const single = ref(true);
 const multiple = ref(true);
 const total = ref(0);
-const title = ref("");
-const dateRange = ref([]);
+const dateRange = ref<[DateModelType, DateModelType]>(['', '']);
 
-const data = reactive({
-  form: {},
+const queryFormRef = ref(ElForm);
+const configFormRef = ref(ElForm);
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+const initFormData: ConfigForm = {
+  configId: undefined,
+  configName: '',
+  configKey: '',
+  configValue: '',
+  configType: "Y",
+  remark: ''
+}
+const data = reactive<PageData<ConfigForm, ConfigQuery>>({
+  form: {...initFormData},
   queryParams: {
     pageNum: 1,
     pageSize: 10,
-    configName: undefined,
-    configKey: undefined,
-    configType: undefined
+    configName: '',
+    configKey: '',
+    configType: '',
   },
   rules: {
     configName: [{ required: true, message: "鍙傛暟鍚嶇О涓嶈兘涓虹┖", trigger: "blur" }],
@@ -201,106 +49,213 @@
 const { queryParams, form, rules } = toRefs(data);
 
 /** 鏌ヨ鍙傛暟鍒楄〃 */
-function getList() {
+const getList = async () => {
   loading.value = true;
-  listConfig(proxy.addDateRange(queryParams.value, dateRange.value)).then(response => {
-    configList.value = response.rows;
-    total.value = response.total;
-    loading.value = false;
-  });
+	const res = await listConfig(proxy?.addDateRange(queryParams.value, dateRange.value));
+	configList.value = res.rows;
+	total.value = res.total;
+	loading.value = false;
 }
 /** 鍙栨秷鎸夐挳 */
-function cancel() {
-  open.value = false;
+const cancel = () => {
   reset();
+  dialog.visible = false;
 }
 /** 琛ㄥ崟閲嶇疆 */
-function reset() {
-  form.value = {
-    configId: undefined,
-    configName: undefined,
-    configKey: undefined,
-    configValue: undefined,
-    configType: "Y",
-    remark: undefined
-  };
-  proxy.resetForm("configRef");
+const reset = () => {
+	form.value = {...initFormData};
+  configFormRef.value.resetFields();
 }
 /** 鎼滅储鎸夐挳鎿嶄綔 */
-function handleQuery() {
+const handleQuery = () => {
   queryParams.value.pageNum = 1;
   getList();
 }
 /** 閲嶇疆鎸夐挳鎿嶄綔 */
-function resetQuery() {
-  dateRange.value = [];
-  proxy.resetForm("queryRef");
+const resetQuery = () => {
+  dateRange.value = ['', ''];
+  queryFormRef.value.resetFields();
   handleQuery();
 }
 /** 澶氶�夋閫変腑鏁版嵁 */
-function handleSelectionChange(selection) {
+const handleSelectionChange = (selection: ConfigVO[]) => {
   ids.value = selection.map(item => item.configId);
   single.value = selection.length != 1;
   multiple.value = !selection.length;
 }
 /** 鏂板鎸夐挳鎿嶄綔 */
-function handleAdd() {
-  reset();
-  open.value = true;
-  title.value = "娣诲姞鍙傛暟";
+const handleAdd = () => {
+  dialog.visible = true;
+  dialog.title = "娣诲姞鍙傛暟";
+  nextTick(() => {
+    reset();
+  })
 }
 /** 淇敼鎸夐挳鎿嶄綔 */
-function handleUpdate(row) {
-  reset();
-  const configId = row.configId || ids.value;
-  getConfig(configId).then(response => {
-    form.value = response.data;
-    open.value = true;
-    title.value = "淇敼鍙傛暟";
-  });
+const handleUpdate = (row?: ConfigVO) => {
+	dialog.visible = true;
+	dialog.title = "淇敼鍙傛暟";
+	const configId = row?.configId || ids.value[0];
+  nextTick(async () => {
+		reset();
+		const res = await getConfig(configId);
+		form.value = res.data;
+	})
 }
 /** 鎻愪氦鎸夐挳 */
-function submitForm() {
-  proxy.$refs["configRef"].validate(valid => {
-    if (valid) {
-      if (form.value.configId != undefined) {
-        updateConfig(form.value).then(response => {
-          proxy.$modal.msgSuccess("淇敼鎴愬姛");
-          open.value = false;
-          getList();
-        });
-      } else {
-        addConfig(form.value).then(response => {
-          proxy.$modal.msgSuccess("鏂板鎴愬姛");
-          open.value = false;
-          getList();
-        });
-      }
+const submitForm = () => {
+  configFormRef.value.validate(async (valid: boolean) => {
+		if (valid) {
+			form.value.configId ? await updateConfig(form.value) : await addConfig(form.value);
+			proxy?.$modal.msgSuccess("鎿嶄綔鎴愬姛");
+			dialog.visible = false;
+			getList();
     }
   });
 }
 /** 鍒犻櫎鎸夐挳鎿嶄綔 */
-function handleDelete(row) {
-  const configIds = row.configId || ids.value;
-  proxy.$modal.confirm('鏄惁纭鍒犻櫎鍙傛暟缂栧彿涓�"' + configIds + '"鐨勬暟鎹」锛�').then(function () {
-    return delConfig(configIds);
-  }).then(() => {
-    getList();
-    proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
-  }).catch(() => {});
+const handleDelete = async (row?: ConfigVO) => {
+  const configIds = row?.configId || ids.value;
+	await proxy?.$modal.confirm('鏄惁纭鍒犻櫎鍙傛暟缂栧彿涓�"' + configIds + '"鐨勬暟鎹」锛�');
+	await delConfig(configIds);
+	getList();
+	proxy?.$modal.msgSuccess("鍒犻櫎鎴愬姛");
 }
 /** 瀵煎嚭鎸夐挳鎿嶄綔 */
-function handleExport() {
-  proxy.download("system/config/export", {
+const handleExport = () => {
+  proxy?.download("system/config/export", {
     ...queryParams.value
   }, `config_${new Date().getTime()}.xlsx`);
 }
 /** 鍒锋柊缂撳瓨鎸夐挳鎿嶄綔 */
-function handleRefreshCache() {
-  refreshCache().then(() => {
-    proxy.$modal.msgSuccess("鍒锋柊缂撳瓨鎴愬姛");
-  });
+const handleRefreshCache = async () => {
+	await refreshCache();
+	proxy?.$modal.msgSuccess("鍒锋柊缂撳瓨鎴愬姛");
 }
 
-getList();
+onMounted(() => {
+	getList();
+})
 </script>
+
+<template>
+	<div class="p-2">
+		<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
+			<div class="search" v-show="showSearch">
+				<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
+					<el-form-item label="鍙傛暟鍚嶇О" prop="configName">
+						<el-input v-model="queryParams.configName" placeholder="璇疯緭鍏ュ弬鏁板悕绉�" clearable style="width: 240px" @keyup.enter="handleQuery" />
+					</el-form-item>
+					<el-form-item label="鍙傛暟閿悕" prop="configKey">
+						<el-input v-model="queryParams.configKey" placeholder="璇疯緭鍏ュ弬鏁伴敭鍚�" clearable style="width: 240px" @keyup.enter="handleQuery" />
+					</el-form-item>
+					<el-form-item label="绯荤粺鍐呯疆" prop="configType">
+						<el-select v-model="queryParams.configType" placeholder="绯荤粺鍐呯疆" clearable>
+							<el-option v-for="dict in sys_yes_no" :key="dict.value" :label="dict.label" :value="dict.value" />
+						</el-select>
+					</el-form-item>
+					<el-form-item label="鍒涘缓鏃堕棿" style="width: 308px;">
+						<el-date-picker
+							v-model="dateRange"
+							value-format="YYYY-MM-DD HH:mm:ss"
+							type="daterange"
+							range-separator="-"
+							start-placeholder="寮�濮嬫棩鏈�"
+							end-placeholder="缁撴潫鏃ユ湡"
+							:default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"
+						></el-date-picker>
+					</el-form-item>
+					<el-form-item>
+						<el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
+						<el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
+					</el-form-item>
+				</el-form>
+			</div>
+		</transition>
+		<el-card shadow="never">
+			<template #header>
+				<el-row :gutter="10" class="mb8">
+					<el-col :span="1.5">
+						<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:config:add']">鏂板</el-button>
+					</el-col>
+					<el-col :span="1.5">
+						<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['system:config:edit']">
+							淇敼
+						</el-button>
+					</el-col>
+					<el-col :span="1.5">
+						<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['system:config:remove']">
+							鍒犻櫎
+						</el-button>
+					</el-col>
+					<el-col :span="1.5">
+						<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['system:config:export']">瀵煎嚭</el-button>
+					</el-col>
+					<el-col :span="1.5">
+						<el-button type="danger" plain icon="Refresh" @click="handleRefreshCache" v-hasPermi="['system:config:remove']">鍒锋柊缂撳瓨</el-button>
+					</el-col>
+					<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+				</el-row>
+			</template>
+
+			<el-table v-loading="loading" :data="configList" @selection-change="handleSelectionChange">
+				<el-table-column type="selection" width="55" align="center" />
+				<el-table-column label="鍙傛暟涓婚敭" align="center" prop="configId" v-if="false" />
+				<el-table-column label="鍙傛暟鍚嶇О" align="center" prop="configName" :show-overflow-tooltip="true" />
+				<el-table-column label="鍙傛暟閿悕" align="center" prop="configKey" :show-overflow-tooltip="true" />
+				<el-table-column label="鍙傛暟閿��" align="center" prop="configValue" :show-overflow-tooltip="true" />
+				<el-table-column label="绯荤粺鍐呯疆" align="center" prop="configType">
+					<template #default="scope">
+						<dict-tag :options="sys_yes_no" :value="scope.row.configType" />
+					</template>
+				</el-table-column>
+				<el-table-column label="澶囨敞" align="center" prop="remark" :show-overflow-tooltip="true" />
+				<el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime" width="180">
+					<template #default="scope">
+						<span>{{ parseTime(scope.row.createTime) }}</span>
+					</template>
+				</el-table-column>
+				<el-table-column label="鎿嶄綔" align="center" width="150" class-name="small-padding fixed-width">
+					<template #default="scope">
+						<el-tooltip content="淇敼" placement="top">
+							<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:config:edit']"></el-button>
+						</el-tooltip>
+						<el-tooltip content="鍒犻櫎" placement="top">
+							<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:config:remove']"></el-button>
+						</el-tooltip>
+					</template>
+				</el-table-column>
+			</el-table>
+			<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
+		</el-card>
+
+		<!-- 娣诲姞鎴栦慨鏀瑰弬鏁伴厤缃璇濇 -->
+		<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
+			<el-form ref="configFormRef" :model="form" :rules="rules" label-width="80px">
+				<el-form-item label="鍙傛暟鍚嶇О" prop="configName">
+					<el-input v-model="form.configName" placeholder="璇疯緭鍏ュ弬鏁板悕绉�" />
+				</el-form-item>
+				<el-form-item label="鍙傛暟閿悕" prop="configKey">
+					<el-input v-model="form.configKey" placeholder="璇疯緭鍏ュ弬鏁伴敭鍚�" />
+				</el-form-item>
+				<el-form-item label="鍙傛暟閿��" prop="configValue">
+					<el-input v-model="form.configValue" placeholder="璇疯緭鍏ュ弬鏁伴敭鍊�" />
+				</el-form-item>
+				<el-form-item label="绯荤粺鍐呯疆" prop="configType">
+					<el-radio-group v-model="form.configType">
+						<el-radio v-for="dict in sys_yes_no" :key="dict.value" :label="dict.value">{{ dict.label }}</el-radio>
+					</el-radio-group>
+				</el-form-item>
+				<el-form-item label="澶囨敞" prop="remark">
+					<el-input v-model="form.remark" type="textarea" placeholder="璇疯緭鍏ュ唴瀹�" />
+				</el-form-item>
+			</el-form>
+			<template #footer>
+				<div class="dialog-footer">
+					<el-button type="primary" @click="submitForm">纭� 瀹�</el-button>
+					<el-button @click="cancel">鍙� 娑�</el-button>
+				</div>
+			</template>
+		</el-dialog>
+	</div>
+</template>
diff --git a/src/views/system/dept/index.vue b/src/views/system/dept/index.vue
index 10d3f8d..b560d5f 100644
--- a/src/views/system/dept/index.vue
+++ b/src/views/system/dept/index.vue
@@ -1,163 +1,49 @@
-<template>
-   <div class="app-container">
-      <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch">
-         <el-form-item label="閮ㄩ棬鍚嶇О" prop="deptName">
-            <el-input
-               v-model="queryParams.deptName"
-               placeholder="璇疯緭鍏ラ儴闂ㄥ悕绉�"
-               clearable
-               style="width: 200px"
-               @keyup.enter="handleQuery"
-            />
-         </el-form-item>
-         <el-form-item label="鐘舵��" prop="status">
-            <el-select v-model="queryParams.status" placeholder="閮ㄩ棬鐘舵��" clearable style="width: 200px">
-               <el-option
-                  v-for="dict in sys_normal_disable"
-                  :key="dict.value"
-                  :label="dict.label"
-                  :value="dict.value"
-               />
-            </el-select>
-         </el-form-item>
-         <el-form-item>
-            <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
-            <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
-         </el-form-item>
-      </el-form>
+<script setup name="Dept" lang="ts">
+import { listDept, getDept, delDept, addDept, updateDept, listDeptExcludeChild } from "@/api/system/dept"
+import { ComponentInternalInstance } from 'vue';
+import { DeptForm, DeptQuery, DeptVO } from "@/api/system/dept/types";
 
-      <el-row :gutter="10" class="mb8">
-         <el-col :span="1.5">
-            <el-button
-               type="primary"
-               plain
-               icon="Plus"
-               @click="handleAdd"
-               v-hasPermi="['system:dept:add']"
-            >鏂板</el-button>
-         </el-col>
-         <el-col :span="1.5">
-            <el-button
-               type="info"
-               plain
-               icon="Sort"
-               @click="toggleExpandAll"
-            >灞曞紑/鎶樺彔</el-button>
-         </el-col>
-         <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
-      </el-row>
+interface DeptOptionsType {
+  deptId: number | string;
+  deptName: string;
+  children: DeptOptionsType[];
 
-      <el-table
-         v-if="refreshTable"
-         v-loading="loading"
-         :data="deptList"
-         row-key="deptId"
-         :default-expand-all="isExpandAll"
-         :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
-      >
-         <el-table-column prop="deptName" label="閮ㄩ棬鍚嶇О" width="260"></el-table-column>
-         <el-table-column prop="orderNum" label="鎺掑簭" width="200"></el-table-column>
-         <el-table-column prop="status" label="鐘舵��" width="100">
-            <template #default="scope">
-               <dict-tag :options="sys_normal_disable" :value="scope.row.status" />
-            </template>
-         </el-table-column>
-         <el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime" width="200">
-            <template #default="scope">
-               <span>{{ parseTime(scope.row.createTime) }}</span>
-            </template>
-         </el-table-column>
-         <el-table-column label="鎿嶄綔" align="center" class-name="small-padding fixed-width">
-            <template #default="scope">
-               <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:dept:edit']">淇敼</el-button>
-               <el-button link type="primary" icon="Plus" @click="handleAdd(scope.row)" v-hasPermi="['system:dept:add']">鏂板</el-button>
-               <el-button v-if="scope.row.parentId != 0" link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:dept:remove']">鍒犻櫎</el-button>
-            </template>
-         </el-table-column>
-      </el-table>
+}
 
-      <!-- 娣诲姞鎴栦慨鏀归儴闂ㄥ璇濇 -->
-      <el-dialog :title="title" v-model="open" width="600px" append-to-body>
-         <el-form ref="deptRef" :model="form" :rules="rules" label-width="80px">
-            <el-row>
-               <el-col :span="24" v-if="form.parentId !== 0">
-                  <el-form-item label="涓婄骇閮ㄩ棬" prop="parentId">
-                     <el-tree-select
-                        v-model="form.parentId"
-                        :data="deptOptions"
-                        :props="{ value: 'deptId', label: 'deptName', children: 'children' }"
-                        value-key="deptId"
-                        placeholder="閫夋嫨涓婄骇閮ㄩ棬"
-                        check-strictly
-                     />
-                  </el-form-item>
-               </el-col>
-               <el-col :span="12">
-                  <el-form-item label="閮ㄩ棬鍚嶇О" prop="deptName">
-                     <el-input v-model="form.deptName" placeholder="璇疯緭鍏ラ儴闂ㄥ悕绉�" />
-                  </el-form-item>
-               </el-col>
-               <el-col :span="12">
-                  <el-form-item label="鏄剧ず鎺掑簭" prop="orderNum">
-                     <el-input-number v-model="form.orderNum" controls-position="right" :min="0" />
-                  </el-form-item>
-               </el-col>
-               <el-col :span="12">
-                  <el-form-item label="璐熻矗浜�" prop="leader">
-                     <el-input v-model="form.leader" placeholder="璇疯緭鍏ヨ礋璐d汉" maxlength="20" />
-                  </el-form-item>
-               </el-col>
-               <el-col :span="12">
-                  <el-form-item label="鑱旂郴鐢佃瘽" prop="phone">
-                     <el-input v-model="form.phone" placeholder="璇疯緭鍏ヨ仈绯荤數璇�" maxlength="11" />
-                  </el-form-item>
-               </el-col>
-               <el-col :span="12">
-                  <el-form-item label="閭" prop="email">
-                     <el-input v-model="form.email" placeholder="璇疯緭鍏ラ偖绠�" maxlength="50" />
-                  </el-form-item>
-               </el-col>
-               <el-col :span="12">
-                  <el-form-item label="閮ㄩ棬鐘舵��">
-                     <el-radio-group v-model="form.status">
-                        <el-radio
-                           v-for="dict in sys_normal_disable"
-                           :key="dict.value"
-                           :label="dict.value"
-                        >{{ dict.label }}</el-radio>
-                     </el-radio-group>
-                  </el-form-item>
-               </el-col>
-            </el-row>
-         </el-form>
-         <template #footer>
-            <div class="dialog-footer">
-               <el-button type="primary" @click="submitForm">纭� 瀹�</el-button>
-               <el-button @click="cancel">鍙� 娑�</el-button>
-            </div>
-         </template>
-      </el-dialog>
-   </div>
-</template>
+const { proxy } = getCurrentInstance() as ComponentInternalInstance
+const { sys_normal_disable } = toRefs<any>(proxy?.useDict("sys_normal_disable"));
 
-<script setup name="Dept">
-import { listDept, getDept, delDept, addDept, updateDept, listDeptExcludeChild } from "@/api/system/dept";
+const deptList = ref<DeptVO[]>([])
+const loading = ref(true)
+const showSearch = ref(true)
+const deptOptions = ref<DeptOptionsType[]>([])
+const isExpandAll = ref(true)
 
-const { proxy } = getCurrentInstance();
-const { sys_normal_disable } = proxy.useDict("sys_normal_disable");
 
-const deptList = ref([]);
-const open = ref(false);
-const loading = ref(true);
-const showSearch = ref(true);
-const title = ref("");
-const deptOptions = ref([]);
-const isExpandAll = ref(true);
-const refreshTable = ref(true);
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
 
-const data = reactive({
-  form: {},
+const deptTableRef = ref(ElTable);
+const queryFormRef = ref(ElForm);
+const deptFormRef = ref(ElForm);
+
+const initFormData: DeptForm = {
+  deptId: undefined,
+  parentId: undefined,
+  deptName: undefined,
+  orderNum: 0,
+  leader: undefined,
+  phone: undefined,
+  email: undefined,
+  status: "0"
+}
+const data = reactive<PageData<DeptForm, DeptQuery>>({
+  form: {...initFormData},
   queryParams: {
+    pageNum: 1,
+    pageSize: 10,
     deptName: undefined,
     status: undefined
   },
@@ -168,111 +54,239 @@
     email: [{ type: "email", message: "璇疯緭鍏ユ纭殑閭鍦板潃", trigger: ["blur", "change"] }],
     phone: [{ pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: "璇疯緭鍏ユ纭殑鎵嬫満鍙风爜", trigger: "blur" }]
   },
-});
+})
 
-const { queryParams, form, rules } = toRefs(data);
+const { queryParams, form, rules } = toRefs<PageData<DeptForm, DeptQuery>>(data)
 
-/** 鏌ヨ閮ㄩ棬鍒楄〃 */
-function getList() {
-  loading.value = true;
-  listDept(queryParams.value).then(response => {
-    deptList.value = proxy.handleTree(response.data, "deptId");
-    loading.value = false;
-  });
+/** 鏌ヨ鑿滃崟鍒楄〃 */
+const getList = async () => {
+	loading.value = true;
+  const res = await listDept(queryParams.value);
+	const data = proxy?.handleTree<DeptVO>(res.data, "deptId")
+	if (data) {
+		deptList.value = data
+	}
+	loading.value = false
 }
 /** 鍙栨秷鎸夐挳 */
-function cancel() {
-  open.value = false;
-  reset();
+const cancel = () => {
+  reset()
+  dialog.visible = false
 }
 /** 琛ㄥ崟閲嶇疆 */
-function reset() {
-  form.value = {
-    deptId: undefined,
-    parentId: undefined,
-    deptName: undefined,
-    orderNum: 0,
-    leader: undefined,
-    phone: undefined,
-    email: undefined,
-    status: "0"
-  };
-  proxy.resetForm("deptRef");
+const reset = () => {
+  form.value = {...initFormData};
+  deptFormRef.value.resetFields();
 }
+
 /** 鎼滅储鎸夐挳鎿嶄綔 */
-function handleQuery() {
+const handleQuery = () => {
   getList();
 }
 /** 閲嶇疆鎸夐挳鎿嶄綔 */
-function resetQuery() {
-  proxy.resetForm("queryRef");
-  handleQuery();
+const resetQuery = () => {
+  queryFormRef.value.resetFields();
+  handleQuery()
 }
 /** 鏂板鎸夐挳鎿嶄綔 */
-function handleAdd(row) {
-  reset();
-  listDept().then(response => {
-    deptOptions.value = proxy.handleTree(response.data, "deptId");
-  });
-  if (row != undefined) {
-    form.value.parentId = row.deptId;
-  }
-  open.value = true;
-  title.value = "娣诲姞閮ㄩ棬";
+const handleAdd = (row?: DeptVO) => {
+  listDept().then(res => {
+    const data = proxy?.handleTree<DeptOptionsType>(res.data, "deptId");
+    if (data) {
+      deptOptions.value = data
+      dialog.visible = true;
+      dialog.title = "娣诲姞閮ㄩ棬";
+      nextTick(() => {
+        reset();
+        if (row && row.deptId) {
+          form.value.parentId = row?.parentId;
+        }
+      })
+    }
+  })
 }
 /** 灞曞紑/鎶樺彔鎿嶄綔 */
-function toggleExpandAll() {
-  refreshTable.value = false;
+const handleToggleExpandAll = () => {
   isExpandAll.value = !isExpandAll.value;
-  nextTick(() => {
-    refreshTable.value = true;
-  });
+  toggleExpandAll(deptList.value, isExpandAll.value)
 }
-/** 淇敼鎸夐挳鎿嶄綔 */
-function handleUpdate(row) {
-  reset();
-  getDept(row.deptId).then(response => {
-    form.value = response.data;
-    open.value = true;
-    title.value = "淇敼閮ㄩ棬";
-    listDeptExcludeChild(row.deptId).then(response => {
-      deptOptions.value = proxy.handleTree(response.data, "deptId");
-      if (deptOptions.value.length == 0) {
-        const noResultsOptions = { deptId: proxy.form.parentId, deptName: proxy.form.parentName, children: [] };
-        deptOptions.value.push(noResultsOptions);
-      }
-    });
-  });
-}
-/** 鎻愪氦鎸夐挳 */
-function submitForm() {
-  proxy.$refs["deptRef"].validate(valid => {
-    if (valid) {
-      if (form.value.deptId != undefined) {
-        updateDept(form.value).then(response => {
-          proxy.$modal.msgSuccess("淇敼鎴愬姛");
-          open.value = false;
-          getList();
-        });
-      } else {
-        addDept(form.value).then(response => {
-          proxy.$modal.msgSuccess("鏂板鎴愬姛");
-          open.value = false;
-          getList();
-        });
-      }
-    }
-  });
-}
-/** 鍒犻櫎鎸夐挳鎿嶄綔 */
-function handleDelete(row) {
-  proxy.$modal.confirm('鏄惁纭鍒犻櫎鍚嶇О涓�"' + row.deptName + '"鐨勬暟鎹」?').then(function() {
-    return delDept(row.deptId);
-  }).then(() => {
-    getList();
-    proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
-  }).catch(() => {});
+/** 灞曞紑/鎶樺彔鎵�鏈� */
+const toggleExpandAll = (data: DeptVO[], status: boolean) => {
+  data.forEach((item) => {
+    deptTableRef.value.toggleRowExpansion(item, status)
+    if(item.children && item.children.length > 0) toggleExpandAll(item.children, status)
+  })
 }
 
-getList();
+/** 淇敼鎸夐挳鎿嶄綔 */
+const handleUpdate = async (row: DeptVO) => {
+	const res = await getDept(row.deptId);
+	dialog.visible = true;
+	dialog.title = "淇敼閮ㄩ棬";
+	nextTick(async () => {
+		reset();
+		form.value = res.data
+		const response = await listDeptExcludeChild(row.deptId);
+		const data = proxy?.handleTree<DeptOptionsType>(response.data, "deptId")
+		if (data) {
+			deptOptions.value = data;
+			if (data.length === 0) {
+				const noResultsOptions: DeptOptionsType = { deptId: res.data.parentId, deptName: res.data.parentName, children: [] };
+				deptOptions.value.push(noResultsOptions);
+			}
+		}
+	})
+}
+/** 鎻愪氦鎸夐挳 */
+const submitForm = () => {
+  deptFormRef.value.validate(async (valid: boolean) => {
+		if (valid) {
+			form.value.deptId ? await updateDept(form.value) : await addDept(form.value);
+			proxy?.$modal.msgSuccess("鎿嶄綔鎴愬姛");
+			dialog.visible = false;
+			getList();
+    }
+  })
+}
+/** 鍒犻櫎鎸夐挳鎿嶄綔 */
+const handleDelete = async (row: DeptVO) => {
+	await proxy?.$modal.confirm('鏄惁纭鍒犻櫎鍚嶇О涓�"' + row.deptName + '"鐨勬暟鎹」?');
+	await delDept(row.deptId);
+	getList();
+	proxy?.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+}
+
+onMounted(() => {
+  getList();
+});
 </script>
+
+<template>
+	<div class="p-2">
+		<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
+			<div class="search" v-show="showSearch">
+				<el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="68px">
+					<el-form-item label="鑿滃崟鍚嶇О" prop="menuName">
+						<el-input v-model="queryParams.deptName" placeholder="璇疯緭鍏ラ儴闂ㄥ悕绉�" clearable @keyup.enter="handleQuery" />
+					</el-form-item>
+					<el-form-item label="鐘舵��" prop="status">
+						<el-select v-model="queryParams.status" placeholder="閮ㄩ棬鐘舵��" clearable>
+							<el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" />
+						</el-select>
+					</el-form-item>
+					<el-form-item>
+						<el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
+						<el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
+					</el-form-item>
+				</el-form>
+			</div>
+		</transition>
+
+		<el-card shadow="never">
+			<template #header>
+				<el-row :gutter="10">
+					<el-col :span="1.5">
+						<el-button type="primary" plain icon="Plus" @click="handleAdd()" v-hasPermi="['system:dept:add']">鏂板 </el-button>
+					</el-col>
+					<el-col :span="1.5">
+						<el-button type="info" plain icon="Sort" @click="handleToggleExpandAll">灞曞紑/鎶樺彔</el-button>
+					</el-col>
+					<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+				</el-row>
+			</template>
+
+			<el-table
+				v-loading="loading"
+				:data="deptList"
+				row-key="deptId"
+				:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
+				ref="deptTableRef"
+				:default-expand-all="isExpandAll"
+			>
+				<el-table-column prop="deptName" label="閮ㄩ棬鍚嶇О" width="260"></el-table-column>
+				<el-table-column prop="orderNum" align="center" label="鎺掑簭" width="200"></el-table-column>
+				<el-table-column prop="status" align="center" label="鐘舵��" width="100">
+					<template #default="scope">
+						<dict-tag :options="sys_normal_disable" :value="scope.row.status" />
+					</template>
+				</el-table-column>
+				<el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime" width="200">
+					<template #default="scope">
+						<span>{{ parseTime(scope.row.createTime) }}</span>
+					</template>
+				</el-table-column>
+				<el-table-column fixed="right" align="center" label="鎿嶄綔">
+					<template #default="scope">
+						<el-tooltip content="淇敼" placement="top">
+							<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:dept:edit']" />
+						</el-tooltip>
+						<el-tooltip content="鏂板" placement="top">
+							<el-button link type="primary" icon="Plus" @click="handleAdd(scope.row)" v-hasPermi="['system:dept:add']" />
+						</el-tooltip>
+						<el-tooltip content="鍒犻櫎" placement="top">
+							<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:dept:remove']" />
+						</el-tooltip>
+					</template>
+				</el-table-column>
+			</el-table>
+		</el-card>
+
+		<el-dialog :title="dialog.title" v-model="dialog.visible" destroy-on-close append-to-bod width="600px">
+			<el-form ref="deptFormRef" :model="form" :rules="rules" label-width="80px">
+				<el-row>
+					<el-col :span="24" v-if="form.parentId !== 0">
+						<el-form-item label="涓婄骇閮ㄩ棬" prop="parentId">
+							<el-tree-select
+								v-model="form.parentId"
+								:data="deptOptions"
+								:props="{ value: 'deptId', label: 'deptName', children: 'children' }"
+								value-key="deptId"
+								placeholder="閫夋嫨涓婄骇閮ㄩ棬"
+								check-strictly
+							/>
+						</el-form-item>
+					</el-col>
+					<el-col :span="12">
+						<el-form-item label="閮ㄩ棬鍚嶇О" prop="deptName">
+							<el-input v-model="form.deptName" placeholder="璇疯緭鍏ラ儴闂ㄥ悕绉�" />
+						</el-form-item>
+					</el-col>
+					<el-col :span="12">
+						<el-form-item label="鏄剧ず鎺掑簭" prop="orderNum">
+							<el-input-number v-model="form.orderNum" controls-position="right" :min="0" />
+						</el-form-item>
+					</el-col>
+					<el-col :span="12">
+						<el-form-item label="璐熻矗浜�" prop="leader">
+							<el-input v-model="form.leader" placeholder="璇疯緭鍏ヨ礋璐d汉" maxlength="20" />
+						</el-form-item>
+					</el-col>
+					<el-col :span="12">
+						<el-form-item label="鑱旂郴鐢佃瘽" prop="phone">
+							<el-input v-model="form.phone" placeholder="璇疯緭鍏ヨ仈绯荤數璇�" maxlength="11" />
+						</el-form-item>
+					</el-col>
+					<el-col :span="12">
+						<el-form-item label="閭" prop="email">
+							<el-input v-model="form.email" placeholder="璇疯緭鍏ラ偖绠�" maxlength="50" />
+						</el-form-item>
+					</el-col>
+					<el-col :span="12">
+						<el-form-item label="閮ㄩ棬鐘舵��">
+							<el-radio-group v-model="form.status">
+								<el-radio v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.value">{{ dict.label
+								}}</el-radio>
+							</el-radio-group>
+						</el-form-item>
+					</el-col>
+				</el-row>
+			</el-form>
+			<template #footer>
+				<div class="dialog-footer">
+					<el-button type="primary" @click="submitForm">纭� 瀹�</el-button>
+					<el-button @click="cancel">鍙� 娑�</el-button>
+				</div>
+			</template>
+		</el-dialog>
+	</div>
+</template>
diff --git a/src/views/system/dict/data.vue b/src/views/system/dict/data.vue
index fb97c60..f5c2dbe 100644
--- a/src/views/system/dict/data.vue
+++ b/src/views/system/dict/data.vue
@@ -1,350 +1,312 @@
-<template>
-   <div class="app-container">
-      <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch">
-         <el-form-item label="瀛楀吀鍚嶇О" prop="dictType">
-            <el-select v-model="queryParams.dictType" style="width: 200px">
-               <el-option
-                  v-for="item in typeOptions"
-                  :key="item.dictId"
-                  :label="item.dictName"
-                  :value="item.dictType"
-               />
-            </el-select>
-         </el-form-item>
-         <el-form-item label="瀛楀吀鏍囩" prop="dictLabel">
-            <el-input
-               v-model="queryParams.dictLabel"
-               placeholder="璇疯緭鍏ュ瓧鍏告爣绛�"
-               clearable
-               style="width: 200px"
-               @keyup.enter="handleQuery"
-            />
-         </el-form-item>
-         <el-form-item label="鐘舵��" prop="status">
-            <el-select v-model="queryParams.status" placeholder="鏁版嵁鐘舵��" clearable style="width: 200px">
-               <el-option
-                  v-for="dict in sys_normal_disable"
-                  :key="dict.value"
-                  :label="dict.label"
-                  :value="dict.value"
-               />
-            </el-select>
-         </el-form-item>
-         <el-form-item>
-            <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
-            <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
-         </el-form-item>
-      </el-form>
-
-      <el-row :gutter="10" class="mb8">
-         <el-col :span="1.5">
-            <el-button
-               type="primary"
-               plain
-               icon="Plus"
-               @click="handleAdd"
-               v-hasPermi="['system:dict:add']"
-            >鏂板</el-button>
-         </el-col>
-         <el-col :span="1.5">
-            <el-button
-               type="success"
-               plain
-               icon="Edit"
-               :disabled="single"
-               @click="handleUpdate"
-               v-hasPermi="['system:dict:edit']"
-            >淇敼</el-button>
-         </el-col>
-         <el-col :span="1.5">
-            <el-button
-               type="danger"
-               plain
-               icon="Delete"
-               :disabled="multiple"
-               @click="handleDelete"
-               v-hasPermi="['system:dict:remove']"
-            >鍒犻櫎</el-button>
-         </el-col>
-         <el-col :span="1.5">
-            <el-button
-               type="warning"
-               plain
-               icon="Download"
-               @click="handleExport"
-               v-hasPermi="['system:dict:export']"
-            >瀵煎嚭</el-button>
-         </el-col>
-         <el-col :span="1.5">
-            <el-button
-               type="warning"
-               plain
-               icon="Close"
-               @click="handleClose"
-            >鍏抽棴</el-button>
-         </el-col>
-         <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
-      </el-row>
-
-      <el-table v-loading="loading" :data="dataList" @selection-change="handleSelectionChange">
-         <el-table-column type="selection" width="55" align="center" />
-         <el-table-column label="瀛楀吀缂栫爜" align="center" prop="dictCode" v-if="false" />
-         <el-table-column label="瀛楀吀鏍囩" align="center" prop="dictLabel">
-            <template #default="scope">
-               <span v-if="scope.row.listClass == '' || scope.row.listClass == 'default'">{{ scope.row.dictLabel }}</span>
-               <el-tag v-else :type="scope.row.listClass == 'primary' ? '' : scope.row.listClass">{{ scope.row.dictLabel }}</el-tag>
-            </template>
-         </el-table-column>
-         <el-table-column label="瀛楀吀閿��" align="center" prop="dictValue" />
-         <el-table-column label="瀛楀吀鎺掑簭" align="center" prop="dictSort" />
-         <el-table-column label="鐘舵��" align="center" prop="status">
-            <template #default="scope">
-               <dict-tag :options="sys_normal_disable" :value="scope.row.status" />
-            </template>
-         </el-table-column>
-         <el-table-column label="澶囨敞" align="center" prop="remark" :show-overflow-tooltip="true" />
-         <el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime" width="180">
-            <template #default="scope">
-               <span>{{ parseTime(scope.row.createTime) }}</span>
-            </template>
-         </el-table-column>
-         <el-table-column label="鎿嶄綔" align="center" width="160" class-name="small-padding fixed-width">
-            <template #default="scope">
-               <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:dict:edit']">淇敼</el-button>
-               <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:dict:remove']">鍒犻櫎</el-button>
-            </template>
-         </el-table-column>
-      </el-table>
-
-      <pagination
-         v-show="total > 0"
-         :total="total"
-         v-model:page="queryParams.pageNum"
-         v-model:limit="queryParams.pageSize"
-         @pagination="getList"
-      />
-
-      <!-- 娣诲姞鎴栦慨鏀瑰弬鏁伴厤缃璇濇 -->
-      <el-dialog :title="title" v-model="open" width="500px" append-to-body>
-         <el-form ref="dataRef" :model="form" :rules="rules" label-width="80px">
-            <el-form-item label="瀛楀吀绫诲瀷">
-               <el-input v-model="form.dictType" :disabled="true" />
-            </el-form-item>
-            <el-form-item label="鏁版嵁鏍囩" prop="dictLabel">
-               <el-input v-model="form.dictLabel" placeholder="璇疯緭鍏ユ暟鎹爣绛�" />
-            </el-form-item>
-            <el-form-item label="鏁版嵁閿��" prop="dictValue">
-               <el-input v-model="form.dictValue" placeholder="璇疯緭鍏ユ暟鎹敭鍊�" />
-            </el-form-item>
-            <el-form-item label="鏍峰紡灞炴��" prop="cssClass">
-               <el-input v-model="form.cssClass" placeholder="璇疯緭鍏ユ牱寮忓睘鎬�" />
-            </el-form-item>
-            <el-form-item label="鏄剧ず鎺掑簭" prop="dictSort">
-               <el-input-number v-model="form.dictSort" controls-position="right" :min="0" />
-            </el-form-item>
-            <el-form-item label="鍥炴樉鏍峰紡" prop="listClass">
-               <el-select v-model="form.listClass">
-                  <el-option
-                     v-for="item in listClassOptions"
-                     :key="item.value"
-                     :label="item.label + '(' + item.value + ')'"
-                     :value="item.value"
-                  ></el-option>
-               </el-select>
-            </el-form-item>
-            <el-form-item label="鐘舵��" prop="status">
-               <el-radio-group v-model="form.status">
-                  <el-radio
-                     v-for="dict in sys_normal_disable"
-                     :key="dict.value"
-                     :label="dict.value"
-                  >{{ dict.label }}</el-radio>
-               </el-radio-group>
-            </el-form-item>
-            <el-form-item label="澶囨敞" prop="remark">
-               <el-input v-model="form.remark" type="textarea" placeholder="璇疯緭鍏ュ唴瀹�"></el-input>
-            </el-form-item>
-         </el-form>
-         <template #footer>
-            <div class="dialog-footer">
-               <el-button type="primary" @click="submitForm">纭� 瀹�</el-button>
-               <el-button @click="cancel">鍙� 娑�</el-button>
-            </div>
-         </template>
-      </el-dialog>
-   </div>
-</template>
-
-<script setup name="Data">
+<script setup name="Data" lang="ts">
 import useDictStore from '@/store/modules/dict'
 import { optionselect as getDictOptionselect, getType } from "@/api/system/dict/type";
 import { listData, getData, delData, addData, updateData } from "@/api/system/dict/data";
+import { DictTypeVO } from '@/api/system/dict/type/types';
+import { ComponentInternalInstance } from "vue";
+import { DictDataForm, DictDataQuery, DictDataVO } from "@/api/system/dict/data/types";
+import { ElForm } from 'element-plus';
 
-const { proxy } = getCurrentInstance();
-const { sys_normal_disable } = proxy.useDict("sys_normal_disable");
+const { proxy } = getCurrentInstance() as ComponentInternalInstance
+const { sys_normal_disable } = toRefs<any>(proxy?.useDict("sys_normal_disable"));
+const route = useRoute();
 
-const dataList = ref([]);
-const open = ref(false);
+const dataList = ref<DictDataVO[]>([]);
 const loading = ref(true);
 const showSearch = ref(true);
-const ids = ref([]);
+const ids = ref<Array<string | number>>([]);
 const single = ref(true);
 const multiple = ref(true);
 const total = ref(0);
-const title = ref("");
 const defaultDictType = ref("");
-const typeOptions = ref([]);
-const route = useRoute();
+const typeOptions = ref<DictTypeVO[]>([]);
+
+const dataFormRef = ref(ElForm);
+const queryFormRef = ref(ElForm);
+
+
+const dialog = reactive<DialogOption>({
+	visible: false,
+	title: ''
+});
+
 // 鏁版嵁鏍囩鍥炴樉鏍峰紡
-const listClassOptions = ref([
-  { value: "default", label: "榛樿" }, 
-  { value: "primary", label: "涓昏" }, 
-  { value: "success", label: "鎴愬姛" },
-  { value: "info", label: "淇℃伅" },
-  { value: "warning", label: "璀﹀憡" },
-  { value: "danger", label: "鍗遍櫓" }
+const listClassOptions = ref<Array<{ value: string, label: string }>>([
+	{ value: "default", label: "榛樿" },
+	{ value: "primary", label: "涓昏" },
+	{ value: "success", label: "鎴愬姛" },
+	{ value: "info", label: "淇℃伅" },
+	{ value: "warning", label: "璀﹀憡" },
+	{ value: "danger", label: "鍗遍櫓" }
 ]);
 
-const data = reactive({
-  form: {},
-  queryParams: {
-    pageNum: 1,
-    pageSize: 10,
-    dictName: undefined,
-    dictType: undefined,
-    status: undefined
-  },
-  rules: {
-    dictLabel: [{ required: true, message: "鏁版嵁鏍囩涓嶈兘涓虹┖", trigger: "blur" }],
-    dictValue: [{ required: true, message: "鏁版嵁閿�间笉鑳戒负绌�", trigger: "blur" }],
-    dictSort: [{ required: true, message: "鏁版嵁椤哄簭涓嶈兘涓虹┖", trigger: "blur" }]
-  }
+const initFormData: DictDataForm = {
+	dictCode: undefined,
+	dictLabel: '',
+	dictValue: '',
+	cssClass: '',
+	listClass: "default",
+	dictSort: 0,
+	status: "0",
+	remark: ''
+}
+const data = reactive<PageData<DictDataForm, DictDataQuery>>({
+	form: { ...initFormData },
+	queryParams: {
+		pageNum: 1,
+		pageSize: 10,
+		dictName: '',
+		dictType: '',
+		status: '',
+		dictLabel: ''
+	},
+	rules: {
+		dictLabel: [{ required: true, message: "鏁版嵁鏍囩涓嶈兘涓虹┖", trigger: "blur" }],
+		dictValue: [{ required: true, message: "鏁版嵁閿�间笉鑳戒负绌�", trigger: "blur" }],
+		dictSort: [{ required: true, message: "鏁版嵁椤哄簭涓嶈兘涓虹┖", trigger: "blur" }]
+	}
 });
 
 const { queryParams, form, rules } = toRefs(data);
 
 /** 鏌ヨ瀛楀吀绫诲瀷璇︾粏 */
-function getTypes(dictId) {
-  getType(dictId).then(response => {
-    queryParams.value.dictType = response.data.dictType;
-    defaultDictType.value = response.data.dictType;
-    getList();
-  });
+const getTypes = async (dictId: string | number) => {
+	const { data } = await getType(dictId);
+	queryParams.value.dictType = data.dictType;
+	defaultDictType.value = data.dictType;
+	getList();
 }
 
 /** 鏌ヨ瀛楀吀绫诲瀷鍒楄〃 */
-function getTypeList() {
-  getDictOptionselect().then(response => {
-    typeOptions.value = response.data;
-  });
+const getTypeList = async () => {
+	const res = await getDictOptionselect()
+	typeOptions.value = res.data;
 }
 /** 鏌ヨ瀛楀吀鏁版嵁鍒楄〃 */
-function getList() {
-  loading.value = true;
-  listData(queryParams.value).then(response => {
-    dataList.value = response.rows;
-    total.value = response.total;
-    loading.value = false;
-  });
+const getList = async () => {
+	loading.value = true;
+	const res = await listData(queryParams.value);
+	dataList.value = res.rows;
+	total.value = res.total;
+	loading.value = false;
 }
 /** 鍙栨秷鎸夐挳 */
-function cancel() {
-  open.value = false;
-  reset();
+const cancel = () => {
+	dialog.visible = false;
+	reset();
 }
 /** 琛ㄥ崟閲嶇疆 */
-function reset() {
-  form.value = {
-    dictCode: undefined,
-    dictLabel: undefined,
-    dictValue: undefined,
-    cssClass: undefined,
-    listClass: "default",
-    dictSort: 0,
-    status: "0",
-    remark: undefined
-  };
-  proxy.resetForm("dataRef");
+const reset = () => {
+	form.value = { ...initFormData };
+	dataFormRef.value.resetFields();
 }
 /** 鎼滅储鎸夐挳鎿嶄綔 */
-function handleQuery() {
-  queryParams.value.pageNum = 1;
-  getList();
+const handleQuery = () => {
+	queryParams.value.pageNum = 1;
+	getList();
 }
 /** 杩斿洖鎸夐挳鎿嶄綔 */
-function handleClose() {
-  const obj = { path: "/system/dict" };
-  proxy.$tab.closeOpenPage(obj);
+const handleClose = () => {
+	const obj = { path: "/system/dict" };
+	proxy?.$tab.closeOpenPage(obj);
 }
 /** 閲嶇疆鎸夐挳鎿嶄綔 */
-function resetQuery() {
-  proxy.resetForm("queryRef");
-  queryParams.value.dictType = defaultDictType;
-  handleQuery();
+const resetQuery = () => {
+	queryFormRef.value.resetFields();
+	queryParams.value.dictType = defaultDictType.value;
+	handleQuery();
 }
 /** 鏂板鎸夐挳鎿嶄綔 */
-function handleAdd() {
-  reset();
-  open.value = true;
-  title.value = "娣诲姞瀛楀吀鏁版嵁";
-  form.value.dictType = queryParams.value.dictType;
+const handleAdd = () => {
+	dialog.visible = true;
+	dialog.title = "娣诲姞瀛楀吀鏁版嵁";
+	nextTick(() => {
+		reset();
+		form.value.dictType = queryParams.value.dictType;
+	})
 }
 /** 澶氶�夋閫変腑鏁版嵁 */
-function handleSelectionChange(selection) {
-  ids.value = selection.map(item => item.dictCode);
-  single.value = selection.length != 1;
-  multiple.value = !selection.length;
+const handleSelectionChange = (selection: DictDataVO[]) => {
+	ids.value = selection.map(item => item.dictCode);
+	single.value = selection.length != 1;
+	multiple.value = !selection.length;
 }
 /** 淇敼鎸夐挳鎿嶄綔 */
-function handleUpdate(row) {
-  reset();
-  const dictCode = row.dictCode || ids.value;
-  getData(dictCode).then(response => {
-    form.value = response.data;
-    open.value = true;
-    title.value = "淇敼瀛楀吀鏁版嵁";
-  });
+const handleUpdate = (row?: DictDataVO) => {
+	const dictCode = row?.dictCode || ids.value[0];
+	dialog.visible = true;
+	dialog.title = "淇敼瀛楀吀鏁版嵁";
+	nextTick(async () => {
+		const res =  await getData(dictCode);
+		reset();
+		form.value = res.data;
+	})
 }
 /** 鎻愪氦鎸夐挳 */
-function submitForm() {
-  proxy.$refs["dataRef"].validate(valid => {
-    if (valid) {
-      if (form.value.dictCode != undefined) {
-        updateData(form.value).then(response => {
-          useDictStore().removeDict(queryParams.value.dictType);
-          proxy.$modal.msgSuccess("淇敼鎴愬姛");
-          open.value = false;
-          getList();
-        });
-      } else {
-        addData(form.value).then(response => {
-          useDictStore().removeDict(queryParams.value.dictType);
-          proxy.$modal.msgSuccess("鏂板鎴愬姛");
-          open.value = false;
-          getList();
-        });
-      }
-    }
-  });
+const submitForm = () => {
+	dataFormRef.value.validate(async (valid: boolean) => {
+		if (valid) {
+			form.value.dictCode ? await updateData(form.value) : await addData(form.value);
+			useDictStore().removeDict(queryParams.value.dictType);
+			proxy?.$modal.msgSuccess("鎿嶄綔鎴愬姛");
+			dialog.visible = false;
+			getList();
+
+		}
+	});
 }
 /** 鍒犻櫎鎸夐挳鎿嶄綔 */
-function handleDelete(row) {
-  const dictCodes = row.dictCode || ids.value;
-  proxy.$modal.confirm('鏄惁纭鍒犻櫎瀛楀吀缂栫爜涓�"' + dictCodes + '"鐨勬暟鎹」锛�').then(function() {
-    return delData(dictCodes);
-  }).then(() => {
-    getList();
-    proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
-    useDictStore().removeDict(queryParams.value.dictType);
-  }).catch(() => {});
+const handleDelete = async (row?: DictDataVO) => {
+	const dictCodes = row?.dictCode || ids.value;
+	await proxy?.$modal.confirm('鏄惁纭鍒犻櫎瀛楀吀缂栫爜涓�"' + dictCodes + '"鐨勬暟鎹」锛�');
+	await delData(dictCodes);
+	getList();
+	proxy?.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+	useDictStore().removeDict(queryParams.value.dictType);
+
 }
 /** 瀵煎嚭鎸夐挳鎿嶄綔 */
-function handleExport() {
-  proxy.download("system/dict/data/export", {
-    ...queryParams.value
-  }, `dict_data_${new Date().getTime()}.xlsx`);
+const handleExport = () => {
+	proxy?.download("system/dict/data/export", {
+		...queryParams.value
+	}, `dict_data_${new Date().getTime()}.xlsx`);
 }
 
-getTypes(route.params && route.params.dictId);
-getTypeList();
+onMounted(() => {
+	getTypes(route.params && route.params.dictId as string);
+	getTypeList();
+})
 </script>
+
+<template>
+	<div class="p-2">
+		<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
+			<div class="search" v-show="showSearch">
+				<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
+					<el-form-item label="瀛楀吀鍚嶇О" prop="dictType">
+						<el-select v-model="queryParams.dictType" style="width: 200px">
+							<el-option v-for="item in typeOptions" :key="item.dictId" :label="item.dictName" :value="item.dictType" />
+						</el-select>
+					</el-form-item>
+					<el-form-item label="瀛楀吀鏍囩" prop="dictLabel">
+						<el-input v-model="queryParams.dictLabel" placeholder="璇疯緭鍏ュ瓧鍏告爣绛�" clearable style="width: 200px" @keyup.enter="handleQuery" />
+					</el-form-item>
+					<el-form-item label="鐘舵��" prop="status">
+						<el-select v-model="queryParams.status" placeholder="鏁版嵁鐘舵��" clearable style="width: 200px">
+							<el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" />
+						</el-select>
+					</el-form-item>
+					<el-form-item>
+						<el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
+						<el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
+					</el-form-item>
+				</el-form>
+			</div>
+		</transition>
+		<el-card shadow="never">
+			<template #header>
+				<el-row :gutter="10" class="mb8">
+					<el-col :span="1.5">
+						<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:dict:add']">鏂板</el-button>
+					</el-col>
+					<el-col :span="1.5">
+						<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['system:dict:edit']">淇敼</el-button>
+					</el-col>
+					<el-col :span="1.5">
+						<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['system:dict:remove']">
+							鍒犻櫎
+						</el-button>
+					</el-col>
+					<el-col :span="1.5">
+						<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['system:dict:export']">瀵煎嚭</el-button>
+					</el-col>
+					<el-col :span="1.5">
+						<el-button type="warning" plain icon="Close" @click="handleClose">鍏抽棴</el-button>
+					</el-col>
+					<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+				</el-row>
+			</template>
+
+			<el-table v-loading="loading" :data="dataList" @selection-change="handleSelectionChange">
+				<el-table-column type="selection" width="55" align="center" />
+				<el-table-column label="瀛楀吀缂栫爜" align="center" prop="dictCode" v-if="false" />
+				<el-table-column label="瀛楀吀鏍囩" align="center" prop="dictLabel">
+					<template #default="scope">
+						<span v-if="scope.row.listClass === '' || scope.row.listClass === 'default'">{{ scope.row.dictLabel }}</span>
+						<el-tag v-else :type="scope.row.listClass === 'primary' ? '' : scope.row.listClass">{{ scope.row.dictLabel
+						}}</el-tag>
+					</template>
+				</el-table-column>
+				<el-table-column label="瀛楀吀閿��" align="center" prop="dictValue" />
+				<el-table-column label="瀛楀吀鎺掑簭" align="center" prop="dictSort" />
+				<el-table-column label="鐘舵��" align="center" prop="status">
+					<template #default="scope">
+						<dict-tag :options="sys_normal_disable" :value="scope.row.status" />
+					</template>
+				</el-table-column>
+				<el-table-column label="澶囨敞" align="center" prop="remark" :show-overflow-tooltip="true" />
+				<el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime" width="180">
+					<template #default="scope">
+						<span>{{ parseTime(scope.row.createTime) }}</span>
+					</template>
+				</el-table-column>
+				<el-table-column label="鎿嶄綔" align="center" width="160" class-name="small-padding fixed-width">
+					<template #default="scope">
+						<el-tooltip content="淇敼" placement="top">
+							<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:dict:edit']"></el-button>
+						</el-tooltip>
+						<el-tooltip content="鍒犻櫎" placement="top">
+							<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:dict:remove']"></el-button>
+						</el-tooltip>
+					</template>
+				</el-table-column>
+			</el-table>
+
+			<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
+		</el-card>
+		<!-- 娣诲姞鎴栦慨鏀瑰弬鏁伴厤缃璇濇 -->
+		<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
+			<el-form ref="dataFormRef" :model="form" :rules="rules" label-width="80px">
+				<el-form-item label="瀛楀吀绫诲瀷">
+					<el-input v-model="form.dictType" :disabled="true" />
+				</el-form-item>
+				<el-form-item label="鏁版嵁鏍囩" prop="dictLabel">
+					<el-input v-model="form.dictLabel" placeholder="璇疯緭鍏ユ暟鎹爣绛�" />
+				</el-form-item>
+				<el-form-item label="鏁版嵁閿��" prop="dictValue">
+					<el-input v-model="form.dictValue" placeholder="璇疯緭鍏ユ暟鎹敭鍊�" />
+				</el-form-item>
+				<el-form-item label="鏍峰紡灞炴��" prop="cssClass">
+					<el-input v-model="form.cssClass" placeholder="璇疯緭鍏ユ牱寮忓睘鎬�" />
+				</el-form-item>
+				<el-form-item label="鏄剧ず鎺掑簭" prop="dictSort">
+					<el-input-number v-model="form.dictSort" controls-position="right" :min="0" />
+				</el-form-item>
+				<el-form-item label="鍥炴樉鏍峰紡" prop="listClass">
+					<el-select v-model="form.listClass">
+						<el-option
+							v-for="item in listClassOptions"
+							:key="item.value"
+							:label="item.label + '(' + item.value + ')'"
+							:value="item.value"
+						></el-option>
+					</el-select>
+				</el-form-item>
+				<el-form-item label="鐘舵��" prop="status">
+					<el-radio-group v-model="form.status">
+						<el-radio v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.value">{{ dict.label }}</el-radio>
+					</el-radio-group>
+				</el-form-item>
+				<el-form-item label="澶囨敞" prop="remark">
+					<el-input v-model="form.remark" type="textarea" placeholder="璇疯緭鍏ュ唴瀹�"></el-input>
+				</el-form-item>
+			</el-form>
+			<template #footer>
+				<div class="dialog-footer">
+					<el-button type="primary" @click="submitForm">纭� 瀹�</el-button>
+					<el-button @click="cancel">鍙� 娑�</el-button>
+				</div>
+			</template>
+		</el-dialog>
+	</div>
+</template>
diff --git a/src/views/system/dict/index.vue b/src/views/system/dict/index.vue
index 48f3a16..9407f51 100644
--- a/src/views/system/dict/index.vue
+++ b/src/views/system/dict/index.vue
@@ -1,203 +1,46 @@
-<template>
-   <div class="app-container">
-      <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px">
-         <el-form-item label="瀛楀吀鍚嶇О" prop="dictName">
-            <el-input
-               v-model="queryParams.dictName"
-               placeholder="璇疯緭鍏ュ瓧鍏稿悕绉�"
-               clearable
-               style="width: 240px"
-               @keyup.enter="handleQuery"
-            />
-         </el-form-item>
-         <el-form-item label="瀛楀吀绫诲瀷" prop="dictType">
-            <el-input
-               v-model="queryParams.dictType"
-               placeholder="璇疯緭鍏ュ瓧鍏哥被鍨�"
-               clearable
-               style="width: 240px"
-               @keyup.enter="handleQuery"
-            />
-         </el-form-item>
-         <el-form-item label="鐘舵��" prop="status">
-            <el-select
-               v-model="queryParams.status"
-               placeholder="瀛楀吀鐘舵��"
-               clearable
-               style="width: 240px"
-            >
-               <el-option
-                  v-for="dict in sys_normal_disable"
-                  :key="dict.value"
-                  :label="dict.label"
-                  :value="dict.value"
-               />
-            </el-select>
-         </el-form-item>
-         <el-form-item label="鍒涘缓鏃堕棿" style="width: 308px">
-            <el-date-picker
-               v-model="dateRange"
-               value-format="YYYY-MM-DD HH:mm:ss"
-               type="daterange"
-               range-separator="-"
-               start-placeholder="寮�濮嬫棩鏈�"
-               end-placeholder="缁撴潫鏃ユ湡"
-               :default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"
-            ></el-date-picker>
-         </el-form-item>
-         <el-form-item>
-            <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
-            <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
-         </el-form-item>
-      </el-form>
-
-      <el-row :gutter="10" class="mb8">
-         <el-col :span="1.5">
-            <el-button
-               type="primary"
-               plain
-               icon="Plus"
-               @click="handleAdd"
-               v-hasPermi="['system:dict:add']"
-            >鏂板</el-button>
-         </el-col>
-         <el-col :span="1.5">
-            <el-button
-               type="success"
-               plain
-               icon="Edit"
-               :disabled="single"
-               @click="handleUpdate"
-               v-hasPermi="['system:dict:edit']"
-            >淇敼</el-button>
-         </el-col>
-         <el-col :span="1.5">
-            <el-button
-               type="danger"
-               plain
-               icon="Delete"
-               :disabled="multiple"
-               @click="handleDelete"
-               v-hasPermi="['system:dict:remove']"
-            >鍒犻櫎</el-button>
-         </el-col>
-         <el-col :span="1.5">
-            <el-button
-               type="warning"
-               plain
-               icon="Download"
-               @click="handleExport"
-               v-hasPermi="['system:dict:export']"
-            >瀵煎嚭</el-button>
-         </el-col>
-         <el-col :span="1.5">
-            <el-button
-               type="danger"
-               plain
-               icon="Refresh"
-               @click="handleRefreshCache"
-               v-hasPermi="['system:dict:remove']"
-            >鍒锋柊缂撳瓨</el-button>
-         </el-col>
-         <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
-      </el-row>
-
-      <el-table v-loading="loading" :data="typeList" @selection-change="handleSelectionChange">
-         <el-table-column type="selection" width="55" align="center" />
-         <el-table-column label="瀛楀吀缂栧彿" align="center" prop="dictId" v-if="false" />
-         <el-table-column label="瀛楀吀鍚嶇О" align="center" prop="dictName" :show-overflow-tooltip="true"/>
-         <el-table-column label="瀛楀吀绫诲瀷" align="center" :show-overflow-tooltip="true">
-            <template #default="scope">
-               <router-link :to="'/system/dict-data/index/' + scope.row.dictId" class="link-type">
-                  <span>{{ scope.row.dictType }}</span>
-               </router-link>
-            </template>
-         </el-table-column>
-         <el-table-column label="鐘舵��" align="center" prop="status">
-            <template #default="scope">
-               <dict-tag :options="sys_normal_disable" :value="scope.row.status" />
-            </template>
-         </el-table-column>
-         <el-table-column label="澶囨敞" align="center" prop="remark" :show-overflow-tooltip="true" />
-         <el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime" width="180">
-            <template #default="scope">
-               <span>{{ parseTime(scope.row.createTime) }}</span>
-            </template>
-         </el-table-column>
-         <el-table-column label="鎿嶄綔" align="center" width="160" class-name="small-padding fixed-width">
-            <template #default="scope">
-               <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:dict:edit']">淇敼</el-button>
-               <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:dict:remove']">鍒犻櫎</el-button>
-            </template>
-         </el-table-column>
-      </el-table>
-
-      <pagination
-         v-show="total > 0"
-         :total="total"
-         v-model:page="queryParams.pageNum"
-         v-model:limit="queryParams.pageSize"
-         @pagination="getList"
-      />
-
-      <!-- 娣诲姞鎴栦慨鏀瑰弬鏁伴厤缃璇濇 -->
-      <el-dialog :title="title" v-model="open" width="500px" append-to-body>
-         <el-form ref="dictRef" :model="form" :rules="rules" label-width="80px">
-            <el-form-item label="瀛楀吀鍚嶇О" prop="dictName">
-               <el-input v-model="form.dictName" placeholder="璇疯緭鍏ュ瓧鍏稿悕绉�" />
-            </el-form-item>
-            <el-form-item label="瀛楀吀绫诲瀷" prop="dictType">
-               <el-input v-model="form.dictType" placeholder="璇疯緭鍏ュ瓧鍏哥被鍨�" />
-            </el-form-item>
-            <el-form-item label="鐘舵��" prop="status">
-               <el-radio-group v-model="form.status">
-                  <el-radio
-                     v-for="dict in sys_normal_disable"
-                     :key="dict.value"
-                     :label="dict.value"
-                  >{{ dict.label }}</el-radio>
-               </el-radio-group>
-            </el-form-item>
-            <el-form-item label="澶囨敞" prop="remark">
-               <el-input v-model="form.remark" type="textarea" placeholder="璇疯緭鍏ュ唴瀹�"></el-input>
-            </el-form-item>
-         </el-form>
-         <template #footer>
-            <div class="dialog-footer">
-               <el-button type="primary" @click="submitForm">纭� 瀹�</el-button>
-               <el-button @click="cancel">鍙� 娑�</el-button>
-            </div>
-         </template>
-      </el-dialog>
-   </div>
-</template>
-
-<script setup name="Dict">
+<script setup name="Dict" lang="ts">
 import useDictStore from '@/store/modules/dict'
 import { listType, getType, delType, addType, updateType, refreshCache } from "@/api/system/dict/type";
+import { ComponentInternalInstance } from "vue";
+import { DictTypeForm, DictTypeQuery, DictTypeVO } from "@/api/system/dict/type/types";
+import { DateModelType } from 'element-plus';
 
-const { proxy } = getCurrentInstance();
-const { sys_normal_disable } = proxy.useDict("sys_normal_disable");
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { sys_normal_disable } = toRefs<any>(proxy?.useDict("sys_normal_disable"))
 
-const typeList = ref([]);
-const open = ref(false);
+const typeList = ref<DictTypeVO[]>([]);
 const loading = ref(true);
 const showSearch = ref(true);
-const ids = ref([]);
+const ids = ref<Array<number | string>>([]);
 const single = ref(true);
 const multiple = ref(true);
 const total = ref(0);
-const title = ref("");
-const dateRange = ref([]);
+const dateRange = ref<[DateModelType, DateModelType]>(['', '']);
 
-const data = reactive({
-  form: {},
+const dictFormRef = ref(ElForm);
+const queryFormRef = ref(ElForm);
+
+
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+
+const initFormData: DictTypeForm = {
+  dictId: undefined,
+  dictName: '',
+  dictType: '',
+  status: "0",
+  remark: ''
+}
+const data = reactive<PageData<DictTypeForm, DictTypeQuery>>({
+  form: {...initFormData},
   queryParams: {
     pageNum: 1,
     pageSize: 10,
-    dictName: undefined,
-    dictType: undefined,
-    status: undefined
+    dictName: '',
+    dictType: '',
+    status: ''
   },
   rules: {
     dictName: [{ required: true, message: "瀛楀吀鍚嶇О涓嶈兘涓虹┖", trigger: "blur" }],
@@ -208,106 +51,216 @@
 const { queryParams, form, rules } = toRefs(data);
 
 /** 鏌ヨ瀛楀吀绫诲瀷鍒楄〃 */
-function getList() {
+const getList = () => {
   loading.value = true;
-  listType(proxy.addDateRange(queryParams.value, dateRange.value)).then(response => {
-    typeList.value = response.rows;
-    total.value = response.total;
+  listType(proxy?.addDateRange(queryParams.value, dateRange.value)).then(res => {
+    typeList.value = res.rows;
+    total.value = res.total;
     loading.value = false;
   });
 }
 /** 鍙栨秷鎸夐挳 */
-function cancel() {
-  open.value = false;
+const cancel = () => {
   reset();
+  dialog.visible = false;
 }
 /** 琛ㄥ崟閲嶇疆 */
-function reset() {
-  form.value = {
-    dictId: undefined,
-    dictName: undefined,
-    dictType: undefined,
-    status: "0",
-    remark: undefined
-  };
-  proxy.resetForm("dictRef");
+const reset = () => {
+	form.value = {...initFormData};
+	dictFormRef.value.resetFields();
 }
 /** 鎼滅储鎸夐挳鎿嶄綔 */
-function handleQuery() {
+const handleQuery = () => {
   queryParams.value.pageNum = 1;
   getList();
 }
 /** 閲嶇疆鎸夐挳鎿嶄綔 */
-function resetQuery() {
-  dateRange.value = [];
-  proxy.resetForm("queryRef");
+const resetQuery = () => {
+  dateRange.value = ['', ''];
+  queryFormRef.value.resetFields();
   handleQuery();
 }
 /** 鏂板鎸夐挳鎿嶄綔 */
-function handleAdd() {
-  reset();
-  open.value = true;
-  title.value = "娣诲姞瀛楀吀绫诲瀷";
+const handleAdd = () => {
+  dialog.visible = true;
+  dialog.title = "娣诲姞瀛楀吀绫诲瀷";
+	nextTick(() => {
+		reset();
+	})
 }
 /** 澶氶�夋閫変腑鏁版嵁 */
-function handleSelectionChange(selection) {
+const handleSelectionChange = (selection: DictTypeVO[]) =>  {
   ids.value = selection.map(item => item.dictId);
   single.value = selection.length != 1;
   multiple.value = !selection.length;
 }
 /** 淇敼鎸夐挳鎿嶄綔 */
-function handleUpdate(row) {
-  reset();
-  const dictId = row.dictId || ids.value;
-  getType(dictId).then(response => {
-    form.value = response.data;
-    open.value = true;
-    title.value = "淇敼瀛楀吀绫诲瀷";
-  });
+const handleUpdate = (row?: DictTypeVO) => {
+	dialog.visible = true;
+	dialog.title = "淇敼瀛楀吀绫诲瀷";
+	const dictId = row?.dictId || ids.value[0];
+	nextTick(async () => {
+		reset();
+		const res = await getType(dictId);
+    form.value = res.data;
+	})
+
 }
 /** 鎻愪氦鎸夐挳 */
-function submitForm() {
-  proxy.$refs["dictRef"].validate(valid => {
-    if (valid) {
-      if (form.value.dictId != undefined) {
-        updateType(form.value).then(response => {
-          proxy.$modal.msgSuccess("淇敼鎴愬姛");
-          open.value = false;
-          getList();
-        });
-      } else {
-        addType(form.value).then(response => {
-          proxy.$modal.msgSuccess("鏂板鎴愬姛");
-          open.value = false;
-          getList();
-        });
-      }
+const submitForm = () => {
+  dictFormRef.value.validate(async (valid: boolean) => {
+		if (valid) {
+			form.value.dictId ? await updateType(form.value) : await addType(form.value);
+			proxy?.$modal.msgSuccess("鎿嶄綔鎴愬姛");
+			dialog.visible = false;
+			getList();
     }
   });
 }
 /** 鍒犻櫎鎸夐挳鎿嶄綔 */
-function handleDelete(row) {
-  const dictIds = row.dictId || ids.value;
-  proxy.$modal.confirm('鏄惁纭鍒犻櫎瀛楀吀缂栧彿涓�"' + dictIds + '"鐨勬暟鎹」锛�').then(function() {
-    return delType(dictIds);
-  }).then(() => {
-    getList();
-    proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
-  }).catch(() => {});
+const handleDelete = async (row?: DictTypeVO) => {
+  const dictIds = row?.dictId || ids.value;
+	await proxy?.$modal.confirm('鏄惁纭鍒犻櫎瀛楀吀缂栧彿涓�"' + dictIds + '"鐨勬暟鎹」锛�');
+	await delType(dictIds);
+	getList();
+	proxy?.$modal.msgSuccess("鍒犻櫎鎴愬姛");
 }
 /** 瀵煎嚭鎸夐挳鎿嶄綔 */
-function handleExport() {
-  proxy.download("system/dict/type/export", {
+const handleExport = () => {
+  proxy?.download("system/dict/type/export", {
     ...queryParams.value
   }, `dict_${new Date().getTime()}.xlsx`);
 }
 /** 鍒锋柊缂撳瓨鎸夐挳鎿嶄綔 */
-function handleRefreshCache() {
-  refreshCache().then(() => {
-    proxy.$modal.msgSuccess("鍒锋柊鎴愬姛");
-    useDictStore().cleanDict();
-  });
+const handleRefreshCache = async () => {
+	await refreshCache();
+	proxy?.$modal.msgSuccess("鍒锋柊鎴愬姛");
+	useDictStore().cleanDict();
 }
 
-getList();
+onMounted(()=>{
+  getList();
+})
 </script>
+
+<template>
+	<div class="p-2">
+		<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
+			<div class="search" v-show="showSearch">
+				<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
+					<el-form-item label="瀛楀吀鍚嶇О" prop="dictName">
+						<el-input v-model="queryParams.dictName" placeholder="璇疯緭鍏ュ瓧鍏稿悕绉�" clearable style="width: 240px" @keyup.enter="handleQuery" />
+					</el-form-item>
+					<el-form-item label="瀛楀吀绫诲瀷" prop="dictType">
+						<el-input v-model="queryParams.dictType" placeholder="璇疯緭鍏ュ瓧鍏哥被鍨�" clearable style="width: 240px" @keyup.enter="handleQuery" />
+					</el-form-item>
+					<el-form-item label="鐘舵��" prop="status">
+						<el-select v-model="queryParams.status" placeholder="瀛楀吀鐘舵��" clearable style="width: 240px">
+							<el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" />
+						</el-select>
+					</el-form-item>
+					<el-form-item label="鍒涘缓鏃堕棿" style="width: 308px">
+						<el-date-picker
+							v-model="dateRange"
+							value-format="YYYY-MM-DD HH:mm:ss"
+							type="daterange"
+							range-separator="-"
+							start-placeholder="寮�濮嬫棩鏈�"
+							end-placeholder="缁撴潫鏃ユ湡"
+							:default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"
+						></el-date-picker>
+					</el-form-item>
+					<el-form-item>
+						<el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
+						<el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
+					</el-form-item>
+				</el-form>
+			</div>
+		</transition>
+		<el-card shadow="never">
+			<template #header>
+				<el-row :gutter="10" class="mb8">
+					<el-col :span="1.5">
+						<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:dict:add']">鏂板</el-button>
+					</el-col>
+					<el-col :span="1.5">
+						<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['system:dict:edit']">淇敼</el-button>
+					</el-col>
+					<el-col :span="1.5">
+						<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['system:dict:remove']">
+							鍒犻櫎
+						</el-button>
+					</el-col>
+					<el-col :span="1.5">
+						<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['system:dict:export']">瀵煎嚭</el-button>
+					</el-col>
+					<el-col :span="1.5">
+						<el-button type="danger" plain icon="Refresh" @click="handleRefreshCache" v-hasPermi="['system:dict:remove']">鍒锋柊缂撳瓨</el-button>
+					</el-col>
+					<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+				</el-row>
+			</template>
+
+			<el-table v-loading="loading" :data="typeList" @selection-change="handleSelectionChange">
+				<el-table-column type="selection" width="55" align="center" />
+				<el-table-column label="瀛楀吀缂栧彿" align="center" prop="dictId" v-if="false" />
+				<el-table-column label="瀛楀吀鍚嶇О" align="center" prop="dictName" :show-overflow-tooltip="true" />
+				<el-table-column label="瀛楀吀绫诲瀷" align="center" :show-overflow-tooltip="true">
+					<template #default="scope">
+						<router-link :to="'/system/dict-data/index/' + scope.row.dictId" class="link-type">
+							<span>{{ scope.row.dictType }}</span>
+						</router-link>
+					</template>
+				</el-table-column>
+				<el-table-column label="鐘舵��" align="center" prop="status">
+					<template #default="scope">
+						<dict-tag :options="sys_normal_disable" :value="scope.row.status" />
+					</template>
+				</el-table-column>
+				<el-table-column label="澶囨敞" align="center" prop="remark" :show-overflow-tooltip="true" />
+				<el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime" width="180">
+					<template #default="scope">
+						<span>{{ parseTime(scope.row.createTime) }}</span>
+					</template>
+				</el-table-column>
+				<el-table-column label="鎿嶄綔" align="center" width="160" class-name="small-padding fixed-width">
+					<template #default="scope">
+						<el-tooltip content="淇敼" placement="top">
+							<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:dict:edit']"></el-button>
+						</el-tooltip>
+						<el-tooltip content="鍒犻櫎" placement="top">
+							<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:dict:remove']"></el-button>
+						</el-tooltip>
+					</template>
+				</el-table-column>
+			</el-table>
+
+			<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
+		</el-card>
+		<!-- 娣诲姞鎴栦慨鏀瑰弬鏁伴厤缃璇濇 -->
+		<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
+			<el-form ref="dictFormRef" :model="form" :rules="rules" label-width="80px">
+				<el-form-item label="瀛楀吀鍚嶇О" prop="dictName">
+					<el-input v-model="form.dictName" placeholder="璇疯緭鍏ュ瓧鍏稿悕绉�" />
+				</el-form-item>
+				<el-form-item label="瀛楀吀绫诲瀷" prop="dictType">
+					<el-input v-model="form.dictType" placeholder="璇疯緭鍏ュ瓧鍏哥被鍨�" />
+				</el-form-item>
+				<el-form-item label="鐘舵��" prop="status">
+					<el-radio-group v-model="form.status">
+						<el-radio v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.value">{{ dict.label }}</el-radio>
+					</el-radio-group>
+				</el-form-item>
+				<el-form-item label="澶囨敞" prop="remark">
+					<el-input v-model="form.remark" type="textarea" placeholder="璇疯緭鍏ュ唴瀹�"></el-input>
+				</el-form-item>
+			</el-form>
+			<template #footer>
+				<div class="dialog-footer">
+					<el-button type="primary" @click="submitForm">纭� 瀹�</el-button>
+					<el-button @click="cancel">鍙� 娑�</el-button>
+				</div>
+			</template>
+		</el-dialog>
+	</div>
+</template>
diff --git a/src/views/system/menu/index.vue b/src/views/system/menu/index.vue
index 764849f..3cf9257 100644
--- a/src/views/system/menu/index.vue
+++ b/src/views/system/menu/index.vue
@@ -1,441 +1,417 @@
-<template>
-   <div class="app-container">
-      <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch">
-         <el-form-item label="鑿滃崟鍚嶇О" prop="menuName">
-            <el-input
-               v-model="queryParams.menuName"
-               placeholder="璇疯緭鍏ヨ彍鍗曞悕绉�"
-               clearable
-               style="width: 200px"
-               @keyup.enter="handleQuery"
-            />
-         </el-form-item>
-         <el-form-item label="鐘舵��" prop="status">
-            <el-select v-model="queryParams.status" placeholder="鑿滃崟鐘舵��" clearable style="width: 200px">
-               <el-option
-                  v-for="dict in sys_normal_disable"
-                  :key="dict.value"
-                  :label="dict.label"
-                  :value="dict.value"
-               />
-            </el-select>
-         </el-form-item>
-         <el-form-item>
-            <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
-            <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
-         </el-form-item>
-      </el-form>
+<script setup name="Menu" lang="ts">
+import { addMenu, delMenu, getMenu, listMenu, updateMenu } from '@/api/system/menu';
+import { MenuForm, MenuQuery, MenuVO } from '@/api/system/menu/types';
+import { ComponentInternalInstance } from 'vue';
+import { MenuTypeEnum } from '@/enums/MenuTypeEnum';
+import { ElTable, ElForm } from 'element-plus';
 
-      <el-row :gutter="10" class="mb8">
-         <el-col :span="1.5">
-            <el-button
-               type="primary"
-               plain
-               icon="Plus"
-               @click="handleAdd"
-               v-hasPermi="['system:menu:add']"
-            >鏂板</el-button>
-         </el-col>
-         <el-col :span="1.5">
-            <el-button 
-               type="info"
-               plain
-               icon="Sort"
-               @click="toggleExpandAll"
-            >灞曞紑/鎶樺彔</el-button>
-         </el-col>
-         <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
-      </el-row>
+interface MenuOptionsType {
+	menuId: number;
+	menuName: string;
+	children: MenuOptionsType[] | undefined;
+}
 
-      <el-table
-         v-if="refreshTable"
-         v-loading="loading"
-         :data="menuList"
-         row-key="menuId"
-         :default-expand-all="isExpandAll"
-         :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
-      >
-         <el-table-column prop="menuName" label="鑿滃崟鍚嶇О" :show-overflow-tooltip="true" width="160"></el-table-column>
-         <el-table-column prop="icon" label="鍥炬爣" align="center" width="100">
-            <template #default="scope">
-               <svg-icon :icon-class="scope.row.icon" />
-            </template>
-         </el-table-column>
-         <el-table-column prop="orderNum" label="鎺掑簭" width="60"></el-table-column>
-         <el-table-column prop="perms" label="鏉冮檺鏍囪瘑" :show-overflow-tooltip="true"></el-table-column>
-         <el-table-column prop="component" label="缁勪欢璺緞" :show-overflow-tooltip="true"></el-table-column>
-         <el-table-column prop="status" label="鐘舵��" width="80">
-            <template #default="scope">
-               <dict-tag :options="sys_normal_disable" :value="scope.row.status" />
-            </template>
-         </el-table-column>
-         <el-table-column label="鍒涘缓鏃堕棿" align="center" width="160" prop="createTime">
-            <template #default="scope">
-               <span>{{ parseTime(scope.row.createTime) }}</span>
-            </template>
-         </el-table-column>
-         <el-table-column label="鎿嶄綔" align="center" width="210" class-name="small-padding fixed-width">
-            <template #default="scope">
-               <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:menu:edit']">淇敼</el-button>
-               <el-button link type="primary" icon="Plus" @click="handleAdd(scope.row)" v-hasPermi="['system:menu:add']">鏂板</el-button>
-               <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:menu:remove']">鍒犻櫎</el-button>
-            </template>
-         </el-table-column>
-      </el-table>
+const { proxy } = getCurrentInstance() as ComponentInternalInstance
+const { sys_show_hide, sys_normal_disable } = toRefs<any>(proxy?.useDict("sys_show_hide", "sys_normal_disable"));
 
-      <!-- 娣诲姞鎴栦慨鏀硅彍鍗曞璇濇 -->
-      <el-dialog :title="title" v-model="open" width="680px" append-to-body>
-         <el-form ref="menuRef" :model="form" :rules="rules" label-width="100px">
-            <el-row>
-               <el-col :span="24">
-                  <el-form-item label="涓婄骇鑿滃崟">
-                     <el-tree-select
-                        v-model="form.parentId"
-                        :data="menuOptions"
-                        :props="{ value: 'menuId', label: 'menuName', children: 'children' }"
-                        value-key="menuId"
-                        placeholder="閫夋嫨涓婄骇鑿滃崟"
-                        check-strictly
-                     />
-                  </el-form-item>
-               </el-col>
-               <el-col :span="24">
-                  <el-form-item label="鑿滃崟绫诲瀷" prop="menuType">
-                     <el-radio-group v-model="form.menuType">
-                        <el-radio label="M">鐩綍</el-radio>
-                        <el-radio label="C">鑿滃崟</el-radio>
-                        <el-radio label="F">鎸夐挳</el-radio>
-                     </el-radio-group>
-                  </el-form-item>
-               </el-col>
-               <el-col :span="24" v-if="form.menuType != 'F'">
-                  <el-form-item label="鑿滃崟鍥炬爣" prop="icon">
-                     <el-popover
-                        placement="bottom-start"
-                        :width="540"
-                        v-model:visible="showChooseIcon"
-                        trigger="click"
-                        @show="showSelectIcon"
-                     >
-                        <template #reference>
-                           <el-input v-model="form.icon" placeholder="鐐瑰嚮閫夋嫨鍥炬爣" @blur="showSelectIcon" v-click-outside="hideSelectIcon" readonly>
-                              <template #prefix>
-                                 <svg-icon
-                                    v-if="form.icon"
-                                    :icon-class="form.icon"
-                                    class="el-input__icon"
-                                    style="height: 32px;width: 16px;"
-                                 />
-                                 <el-icon v-else style="height: 32px;width: 16px;"><search /></el-icon>
-                              </template>
-                           </el-input>
-                        </template>
-                        <icon-select ref="iconSelectRef" @selected="selected" />
-                     </el-popover>
-                  </el-form-item>
-               </el-col>
-               <el-col :span="12">
-                  <el-form-item label="鑿滃崟鍚嶇О" prop="menuName">
-                     <el-input v-model="form.menuName" placeholder="璇疯緭鍏ヨ彍鍗曞悕绉�" />
-                  </el-form-item>
-               </el-col>
-               <el-col :span="12">
-                  <el-form-item label="鏄剧ず鎺掑簭" prop="orderNum">
-                     <el-input-number v-model="form.orderNum" controls-position="right" :min="0" />
-                  </el-form-item>
-               </el-col>
-               <el-col :span="12" v-if="form.menuType != 'F'">
-                  <el-form-item>
-                     <template #label>
-                        <span>
-                           <el-tooltip content="閫夋嫨鏄閾惧垯璺敱鍦板潃闇�瑕佷互`http(s)://`寮�澶�" placement="top">
-                              <el-icon><question-filled /></el-icon>
-                           </el-tooltip>鏄惁澶栭摼
-                        </span>
-                     </template>
-                     <el-radio-group v-model="form.isFrame">
-                        <el-radio label="0">鏄�</el-radio>
-                        <el-radio label="1">鍚�</el-radio>
-                     </el-radio-group>
-                  </el-form-item>
-               </el-col>
-               <el-col :span="12" v-if="form.menuType != 'F'">
-                  <el-form-item prop="path">
-                     <template #label>
-                        <span>
-                           <el-tooltip content="璁块棶鐨勮矾鐢卞湴鍧�锛屽锛歚user`锛屽澶栫綉鍦板潃闇�鍐呴摼璁块棶鍒欎互`http(s)://`寮�澶�" placement="top">
-                              <el-icon><question-filled /></el-icon>
-                           </el-tooltip>
-                           璺敱鍦板潃
-                        </span>
-                     </template>
-                     <el-input v-model="form.path" placeholder="璇疯緭鍏ヨ矾鐢卞湴鍧�" />
-                  </el-form-item>
-               </el-col>
-               <el-col :span="12" v-if="form.menuType == 'C'">
-                  <el-form-item prop="component">
-                     <template #label>
-                        <span>
-                           <el-tooltip content="璁块棶鐨勭粍浠惰矾寰勶紝濡傦細`system/user/index`锛岄粯璁ゅ湪`views`鐩綍涓�" placement="top">
-                              <el-icon><question-filled /></el-icon>
-                           </el-tooltip>
-                           缁勪欢璺緞
-                        </span>
-                     </template>
-                     <el-input v-model="form.component" placeholder="璇疯緭鍏ョ粍浠惰矾寰�" />
-                  </el-form-item>
-               </el-col>
-               <el-col :span="12" v-if="form.menuType != 'M'">
-                  <el-form-item>
-                     <el-input v-model="form.perms" placeholder="璇疯緭鍏ユ潈闄愭爣璇�" maxlength="100" />
-                     <template #label>
-                        <span>
-                           <el-tooltip content="鎺у埗鍣ㄤ腑瀹氫箟鐨勬潈闄愬瓧绗︼紝濡傦細@SaCheckPermission('system:user:list')" placement="top">
-                              <el-icon><question-filled /></el-icon>
-                           </el-tooltip>
-                           鏉冮檺瀛楃
-                        </span>
-                     </template>
-                  </el-form-item>
-               </el-col>
-               <el-col :span="12" v-if="form.menuType == 'C'">
-                  <el-form-item>
-                     <el-input v-model="form.queryParam" placeholder="璇疯緭鍏ヨ矾鐢卞弬鏁�" maxlength="255" />
-                     <template #label>
-                        <span>
-                           <el-tooltip content='璁块棶璺敱鐨勯粯璁や紶閫掑弬鏁帮紝濡傦細`{"id": 1, "name": "ry"}`' placement="top">
-                              <el-icon><question-filled /></el-icon>
-                           </el-tooltip>
-                           璺敱鍙傛暟
-                        </span>
-                     </template>
-                  </el-form-item>
-               </el-col>
-               <el-col :span="12" v-if="form.menuType == 'C'">
-                  <el-form-item>
-                     <template #label>
-                        <span>
-                           <el-tooltip content="閫夋嫨鏄垯浼氳`keep-alive`缂撳瓨锛岄渶瑕佸尮閰嶇粍浠剁殑`name`鍜屽湴鍧�淇濇寔涓�鑷�" placement="top">
-                              <el-icon><question-filled /></el-icon>
-                           </el-tooltip>
-                           鏄惁缂撳瓨
-                        </span>
-                     </template>
-                     <el-radio-group v-model="form.isCache">
-                        <el-radio label="0">缂撳瓨</el-radio>
-                        <el-radio label="1">涓嶇紦瀛�</el-radio>
-                     </el-radio-group>
-                  </el-form-item>
-               </el-col>
-               <el-col :span="12" v-if="form.menuType != 'F'">
-                  <el-form-item>
-                     <template #label>
-                        <span>
-                           <el-tooltip content="閫夋嫨闅愯棌鍒欒矾鐢卞皢涓嶄細鍑虹幇鍦ㄤ晶杈规爮锛屼絾浠嶇劧鍙互璁块棶" placement="top">
-                              <el-icon><question-filled /></el-icon>
-                           </el-tooltip>
-                           鏄剧ず鐘舵��
-                        </span>
-                     </template>
-                     <el-radio-group v-model="form.visible">
-                        <el-radio
-                           v-for="dict in sys_show_hide"
-                           :key="dict.value"
-                           :label="dict.value"
-                        >{{ dict.label }}</el-radio>
-                     </el-radio-group>
-                  </el-form-item>
-               </el-col>
-               <el-col :span="12" v-if="form.menuType != 'F'">
-                  <el-form-item>
-                     <template #label>
-                        <span>
-                           <el-tooltip content="閫夋嫨鍋滅敤鍒欒矾鐢卞皢涓嶄細鍑虹幇鍦ㄤ晶杈规爮锛屼篃涓嶈兘琚闂�" placement="top">
-                              <el-icon><question-filled /></el-icon>
-                           </el-tooltip>
-                           鑿滃崟鐘舵��
-                        </span>
-                     </template>
-                     <el-radio-group v-model="form.status">
-                        <el-radio
-                           v-for="dict in sys_normal_disable"
-                           :key="dict.value"
-                           :label="dict.value"
-                        >{{ dict.label }}</el-radio>
-                     </el-radio-group>
-                  </el-form-item>
-               </el-col>
-            </el-row>
-         </el-form>
-         <template #footer>
-            <div class="dialog-footer">
-               <el-button type="primary" @click="submitForm">纭� 瀹�</el-button>
-               <el-button @click="cancel">鍙� 娑�</el-button>
-            </div>
-         </template>
-      </el-dialog>
-   </div>
-</template>
+const menuList = ref<MenuVO[]>([])
+const loading = ref(true)
+const showSearch = ref(true)
+const menuOptions = ref<MenuOptionsType[]>([])
+const isExpandAll = ref(false)
 
-<script setup name="Menu">
-import { addMenu, delMenu, getMenu, listMenu, updateMenu } from "@/api/system/menu";
-import SvgIcon from "@/components/SvgIcon";
-import IconSelect from "@/components/IconSelect";
-import { ClickOutside as vClickOutside } from 'element-plus'
-
-const { proxy } = getCurrentInstance();
-const { sys_show_hide, sys_normal_disable } = proxy.useDict("sys_show_hide", "sys_normal_disable");
-
-const menuList = ref([]);
-const open = ref(false);
-const loading = ref(true);
-const showSearch = ref(true);
-const title = ref("");
-const menuOptions = ref([]);
-const isExpandAll = ref(false);
-const refreshTable = ref(true);
-const showChooseIcon = ref(false);
-const iconSelectRef = ref(null);
-
-const data = reactive({
-  form: {},
-  queryParams: {
-    menuName: undefined,
-    visible: undefined
-  },
-  rules: {
-    menuName: [{ required: true, message: "鑿滃崟鍚嶇О涓嶈兘涓虹┖", trigger: "blur" }],
-    orderNum: [{ required: true, message: "鑿滃崟椤哄簭涓嶈兘涓虹┖", trigger: "blur" }],
-    path: [{ required: true, message: "璺敱鍦板潃涓嶈兘涓虹┖", trigger: "blur" }]
-  },
+const dialog = reactive<DialogOption>({
+	visible: false,
+	title: ''
 });
 
-const { queryParams, form, rules } = toRefs(data);
+const queryFormRef = ref(ElForm);
+const menuFormRef = ref(ElForm);
+const initFormData = {
+	path: '',
+	menuId: undefined,
+	parentId: 0,
+	menuName: '',
+	icon: '',
+	menuType: MenuTypeEnum.M,
+	orderNum: 1,
+	isFrame: "1",
+	isCache: "0",
+	visible: "0",
+	status: "0"
+}
+const data = reactive<PageData<MenuForm, MenuQuery>>({
+	form: { ...initFormData },
+	queryParams: {
+		menuName: undefined,
+		status: undefined
+	},
+	rules: {
+		menuName: [{ required: true, message: "鑿滃崟鍚嶇О涓嶈兘涓虹┖", trigger: "blur" }],
+		orderNum: [{ required: true, message: "鑿滃崟椤哄簭涓嶈兘涓虹┖", trigger: "blur" }],
+		path: [{ required: true, message: "璺敱鍦板潃涓嶈兘涓虹┖", trigger: "blur" }]
+	},
+})
 
+const menuTableRef = ref(ElTable);
+
+const { queryParams, form, rules } = toRefs<PageData<MenuForm, MenuQuery>>(data)
 /** 鏌ヨ鑿滃崟鍒楄〃 */
-function getList() {
-  loading.value = true;
-  listMenu(queryParams.value).then(response => {
-    menuList.value = proxy.handleTree(response.data, "menuId");
-    loading.value = false;
-  });
+const getList = async () => {
+	loading.value = true
+	const res = await listMenu(queryParams.value);
+	const data = proxy?.handleTree<MenuVO>(res.data, "menuId")
+	if (data) {
+		menuList.value = data
+	}
+	loading.value = false
 }
 /** 鏌ヨ鑿滃崟涓嬫媺鏍戠粨鏋� */
-function getTreeselect() {
-  menuOptions.value = [];
-  listMenu().then(response => {
-    const menu = { menuId: 0, menuName: "涓荤被鐩�", children: [] };
-    menu.children = proxy.handleTree(response.data, "menuId");
-    menuOptions.value.push(menu);
-  });
+const getTreeselect = async () => {
+	menuOptions.value = []
+	const response = await listMenu();
+	const menu: MenuOptionsType = { menuId: 0, menuName: "涓荤被鐩�", children: [] }
+	menu.children = proxy?.handleTree<MenuOptionsType>(response.data, "menuId")
+	menuOptions.value.push(menu)
 }
 /** 鍙栨秷鎸夐挳 */
-function cancel() {
-  open.value = false;
-  reset();
+const cancel = () => {
+	reset()
+	dialog.visible = false
 }
 /** 琛ㄥ崟閲嶇疆 */
-function reset() {
-  form.value = {
-    menuId: undefined,
-    parentId: 0,
-    menuName: undefined,
-    icon: undefined,
-    menuType: "M",
-    orderNum: undefined,
-    isFrame: "1",
-    isCache: "0",
-    visible: "0",
-    status: "0"
-  };
-  proxy.resetForm("menuRef");
-}
-/** 灞曠ず涓嬫媺鍥炬爣 */
-function showSelectIcon() {
-  iconSelectRef.value.reset();
-  showChooseIcon.value = true;
-}
-/** 閫夋嫨鍥炬爣 */
-function selected(name) {
-  form.value.icon = name;
-  showChooseIcon.value = false;
-}
-/** 鍥炬爣澶栧眰鐐瑰嚮闅愯棌涓嬫媺鍒楄〃 */
-function hideSelectIcon(event) {
-  var elem = event.relatedTarget || event.srcElement || event.target || event.currentTarget;
-  var className = elem.className;
-  if (className !== "el-input__inner") {
-    showChooseIcon.value = false;
-  }
-}
-/** 鎼滅储鎸夐挳鎿嶄綔 */
-function handleQuery() {
-  getList();
-}
-/** 閲嶇疆鎸夐挳鎿嶄綔 */
-function resetQuery() {
-  proxy.resetForm("queryRef");
-  handleQuery();
-}
-/** 鏂板鎸夐挳鎿嶄綔 */
-function handleAdd(row) {
-  reset();
-  getTreeselect();
-  if (row != null && row.menuId) {
-    form.value.parentId = row.menuId;
-  } else {
-    form.value.parentId = 0;
-  }
-  open.value = true;
-  title.value = "娣诲姞鑿滃崟";
-}
-/** 灞曞紑/鎶樺彔鎿嶄綔 */
-function toggleExpandAll() {
-  refreshTable.value = false;
-  isExpandAll.value = !isExpandAll.value;
-  nextTick(() => {
-    refreshTable.value = true;
-  });
-}
-/** 淇敼鎸夐挳鎿嶄綔 */
-async function handleUpdate(row) {
-  reset();
-  await getTreeselect();
-  getMenu(row.menuId).then(response => {
-    form.value = response.data;
-    open.value = true;
-    title.value = "淇敼鑿滃崟";
-  });
-}
-/** 鎻愪氦鎸夐挳 */
-function submitForm() {
-  proxy.$refs["menuRef"].validate(valid => {
-    if (valid) {
-      if (form.value.menuId != undefined) {
-        updateMenu(form.value).then(response => {
-          proxy.$modal.msgSuccess("淇敼鎴愬姛");
-          open.value = false;
-          getList();
-        });
-      } else {
-        addMenu(form.value).then(response => {
-          proxy.$modal.msgSuccess("鏂板鎴愬姛");
-          open.value = false;
-          getList();
-        });
-      }
-    }
-  });
-}
-/** 鍒犻櫎鎸夐挳鎿嶄綔 */
-function handleDelete(row) {
-  proxy.$modal.confirm('鏄惁纭鍒犻櫎鍚嶇О涓�"' + row.menuName + '"鐨勬暟鎹」?').then(function() {
-    return delMenu(row.menuId);
-  }).then(() => {
-    getList();
-    proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
-  }).catch(() => {});
+const reset = () => {
+	form.value = { ...initFormData };
+	menuFormRef.value.resetFields();
 }
 
-getList();
+/** 鎼滅储鎸夐挳鎿嶄綔 */
+const handleQuery = () => {
+	getList();
+}
+/** 閲嶇疆鎸夐挳鎿嶄綔 */
+const resetQuery = () => {
+	queryFormRef.value.resetFields();
+	handleQuery();
+}
+/** 鏂板鎸夐挳鎿嶄綔 */
+const handleAdd = (row?: MenuVO) => {
+	dialog.visible = true;
+	dialog.title = "娣诲姞鑿滃崟";
+	getTreeselect();
+	nextTick(() => {
+		reset();
+		row && row.menuId ? form.value.parentId = row.menuId : form.value.parentId = 0;
+	})
+
+}
+/** 灞曞紑/鎶樺彔鎿嶄綔 */
+const handleToggleExpandAll = () => {
+	isExpandAll.value = !isExpandAll.value;
+	toggleExpandAll(menuList.value, isExpandAll.value)
+}
+/** 灞曞紑/鎶樺彔鎵�鏈� */
+const toggleExpandAll = (data: MenuVO[], status: boolean) => {
+	data.forEach((item: MenuVO) => {
+		menuTableRef.value.toggleRowExpansion(item, status)
+		if (item.children && item.children.length > 0) toggleExpandAll(item.children, status)
+	})
+}
+/** 淇敼鎸夐挳鎿嶄綔 */
+const handleUpdate = async (row: MenuVO) => {
+	await getTreeselect();
+	dialog.visible = true;
+	dialog.title = "淇敼鑿滃崟";
+	await nextTick(async () => {
+		if (row.menuId) {
+			const { data } = await getMenu(row.menuId);
+			reset();
+			form.value = data;
+		}
+	})
+
+}
+/** 鎻愪氦鎸夐挳 */
+const submitForm = () => {
+	menuFormRef.value.validate(async (valid: boolean) => {
+		if (valid) {
+			form.value.menuId ? await updateMenu(form.value) : await addMenu(form.value);
+			proxy?.$modal.msgSuccess("鎿嶄綔鎴愬姛");
+			dialog.visible = false;
+			getList();
+		}
+	})
+}
+/** 鍒犻櫎鎸夐挳鎿嶄綔 */
+const handleDelete = async (row: MenuVO) => {
+	await proxy?.$modal.confirm('鏄惁纭鍒犻櫎鍚嶇О涓�"' + row.menuName + '"鐨勬暟鎹」?');
+	await delMenu(row.menuId);
+	getList();
+	proxy?.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+}
+
+onMounted(() => {
+	getList();
+});
 </script>
+
+<template>
+	<div class="p-2">
+		<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
+			<div class="search" v-show="showSearch">
+				<el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="68px">
+					<el-form-item label="鑿滃崟鍚嶇О" prop="menuName">
+						<el-input v-model="queryParams.menuName" placeholder="璇疯緭鍏ヨ彍鍗曞悕绉�" clearable @keyup.enter="handleQuery" />
+					</el-form-item>
+					<el-form-item label="鐘舵��" prop="status">
+						<el-select v-model="queryParams.status" placeholder="鑿滃崟鐘舵��" clearable>
+							<el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" />
+						</el-select>
+					</el-form-item>
+					<el-form-item>
+						<el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
+						<el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
+					</el-form-item>
+				</el-form>
+			</div>
+		</transition>
+
+		<el-card shadow="never">
+			<template #header>
+				<el-row :gutter="10">
+					<el-col :span="1.5">
+						<el-button type="primary" plain icon="Plus" @click="handleAdd()" v-hasPermi="['system:menu:add']">鏂板 </el-button>
+					</el-col>
+					<el-col :span="1.5">
+						<el-button type="info" plain icon="Sort" @click="handleToggleExpandAll">灞曞紑/鎶樺彔</el-button>
+					</el-col>
+					<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+				</el-row>
+			</template>
+
+			<el-table
+				v-loading="loading"
+				:data="menuList"
+				row-key="menuId"
+				:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
+				border
+				ref="menuTableRef"
+				:default-expand-all="isExpandAll"
+			>
+				<el-table-column prop="menuName" label="鑿滃崟鍚嶇О" :show-overflow-tooltip="true" width="160"></el-table-column>
+				<el-table-column prop="icon" label="鍥炬爣" align="center" width="100">
+					<template #default="scope">
+						<svg-icon :icon-class="scope.row.icon" />
+					</template>
+				</el-table-column>
+				<el-table-column prop="orderNum" label="鎺掑簭" width="60"></el-table-column>
+				<el-table-column prop="perms" label="鏉冮檺鏍囪瘑" :show-overflow-tooltip="true"></el-table-column>
+				<el-table-column prop="component" label="缁勪欢璺緞" :show-overflow-tooltip="true"></el-table-column>
+				<el-table-column prop="status" label="鐘舵��" width="80">
+					<template #default="scope">
+						<dict-tag :options="sys_normal_disable" :value="scope.row.status" />
+					</template>
+				</el-table-column>
+				<el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime">
+					<template #default="scope">
+						<span>{{ scope.row.createTime }}</span>
+					</template>
+				</el-table-column>
+				<el-table-column fixed="right" label="鎿嶄綔" width="180">
+					<template #default="scope">
+						<el-tooltip content="淇敼" placement="top">
+							<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:menu:edit']" />
+						</el-tooltip>
+						<el-tooltip content="鏂板" placement="top">
+							<el-button link type="primary" icon="Plus" @click="handleAdd(scope.row)" v-hasPermi="['system:menu:add']" />
+						</el-tooltip>
+						<el-tooltip content="鍒犻櫎" placement="top">
+							<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:menu:remove']" />
+						</el-tooltip>
+					</template>
+				</el-table-column>
+			</el-table>
+		</el-card>
+
+		<el-dialog :title="dialog.title" v-model="dialog.visible" destroy-on-close append-to-bod width="750px">
+			<el-form ref="menuFormRef" :model="form" :rules="rules" label-width="100px">
+				<el-row>
+					<el-col :span="24">
+						<el-form-item label="涓婄骇鑿滃崟">
+							<el-tree-select
+								v-model="form.parentId"
+								:data="menuOptions"
+								:props="{ value: 'menuId', label: 'menuName', children: 'children' }"
+								value-key="menuId"
+								placeholder="閫夋嫨涓婄骇鑿滃崟"
+								check-strictly
+							/>
+						</el-form-item>
+					</el-col>
+					<el-col :span="24">
+						<el-form-item label="鑿滃崟绫诲瀷" prop="menuType">
+							<el-radio-group v-model="form.menuType">
+								<el-radio label="M">鐩綍</el-radio>
+								<el-radio label="C">鑿滃崟</el-radio>
+								<el-radio label="F">鎸夐挳</el-radio>
+							</el-radio-group>
+						</el-form-item>
+					</el-col>
+					<el-col :span="24" v-if="form.menuType !== 'F'">
+						<el-form-item label="鑿滃崟鍥炬爣" prop="icon">
+							<!-- 鍥炬爣閫夋嫨鍣� -->
+							<icon-select v-model="form.icon" />
+						</el-form-item>
+					</el-col>
+					<el-col :span="12">
+						<el-form-item label="鑿滃崟鍚嶇О" prop="menuName">
+							<el-input v-model="form.menuName" placeholder="璇疯緭鍏ヨ彍鍗曞悕绉�" />
+						</el-form-item>
+					</el-col>
+					<el-col :span="12">
+						<el-form-item label="鏄剧ず鎺掑簭" prop="orderNum">
+							<el-input-number v-model="form.orderNum" controls-position="right" :min="0" />
+						</el-form-item>
+					</el-col>
+					<el-col :span="12" v-if="form.menuType !== 'F'">
+						<el-form-item>
+							<template #label>
+								<span>
+									<el-tooltip content="閫夋嫨鏄閾惧垯璺敱鍦板潃闇�瑕佷互`http(s)://`寮�澶�" placement="top">
+										<el-icon>
+											<question-filled />
+										</el-icon> </el-tooltip
+									>鏄惁澶栭摼
+								</span>
+							</template>
+							<el-radio-group v-model="form.isFrame">
+								<el-radio label="0">鏄�</el-radio>
+								<el-radio label="1">鍚�</el-radio>
+							</el-radio-group>
+						</el-form-item>
+					</el-col>
+					<el-col :span="12" v-if="form.menuType !== 'F'">
+						<el-form-item prop="path">
+							<template #label>
+								<span>
+									<el-tooltip content="璁块棶鐨勮矾鐢卞湴鍧�锛屽锛歚user`锛屽澶栫綉鍦板潃闇�鍐呴摼璁块棶鍒欎互`http(s)://`寮�澶�" placement="top">
+										<el-icon>
+											<question-filled />
+										</el-icon>
+									</el-tooltip>
+									璺敱鍦板潃
+								</span>
+							</template>
+							<el-input v-model="form.path" placeholder="璇疯緭鍏ヨ矾鐢卞湴鍧�" />
+						</el-form-item>
+					</el-col>
+					<el-col :span="12" v-if="form.menuType === 'C'">
+						<el-form-item prop="component">
+							<template #label>
+								<span>
+									<el-tooltip content="璁块棶鐨勭粍浠惰矾寰勶紝濡傦細`system/user/index`锛岄粯璁ゅ湪`views`鐩綍涓�" placement="top">
+										<el-icon>
+											<question-filled />
+										</el-icon>
+									</el-tooltip>
+									缁勪欢璺緞
+								</span>
+							</template>
+							<el-input v-model="form.component" placeholder="璇疯緭鍏ョ粍浠惰矾寰�" />
+						</el-form-item>
+					</el-col>
+					<el-col :span="12" v-if="form.menuType !== 'M'">
+						<el-form-item>
+							<el-input v-model="form.perms" placeholder="璇疯緭鍏ユ潈闄愭爣璇�" maxlength="100" />
+							<template #label>
+								<span>
+									<el-tooltip content="鎺у埗鍣ㄤ腑瀹氫箟鐨勬潈闄愬瓧绗︼紝濡傦細@PreAuthorize(`@ss.hasPermi('system:user:list')`)" placement="top">
+										<el-icon>
+											<question-filled />
+										</el-icon>
+									</el-tooltip>
+									鏉冮檺瀛楃
+								</span>
+							</template>
+						</el-form-item>
+					</el-col>
+					<el-col :span="12" v-if="form.menuType === 'C'">
+						<el-form-item>
+							<el-input v-model="form.query" placeholder="璇疯緭鍏ヨ矾鐢卞弬鏁�" maxlength="255" />
+							<template #label>
+								<span>
+									<el-tooltip content='璁块棶璺敱鐨勯粯璁や紶閫掑弬鏁帮紝濡傦細`{"id": 1, "name": "ry"}`' placement="top">
+										<el-icon>
+											<question-filled />
+										</el-icon>
+									</el-tooltip>
+									璺敱鍙傛暟
+								</span>
+							</template>
+						</el-form-item>
+					</el-col>
+					<el-col :span="12" v-if="form.menuType === 'C'">
+						<el-form-item>
+							<template #label>
+								<span>
+									<el-tooltip content="閫夋嫨鏄垯浼氳`keep-alive`缂撳瓨锛岄渶瑕佸尮閰嶇粍浠剁殑`name`鍜屽湴鍧�淇濇寔涓�鑷�" placement="top">
+										<el-icon>
+											<question-filled />
+										</el-icon>
+									</el-tooltip>
+									鏄惁缂撳瓨
+								</span>
+							</template>
+							<el-radio-group v-model="form.isCache">
+								<el-radio label="0">缂撳瓨</el-radio>
+								<el-radio label="1">涓嶇紦瀛�</el-radio>
+							</el-radio-group>
+						</el-form-item>
+					</el-col>
+					<el-col :span="12" v-if="form.menuType !== 'F'">
+						<el-form-item>
+							<template #label>
+								<span>
+									<el-tooltip content="閫夋嫨闅愯棌鍒欒矾鐢卞皢涓嶄細鍑虹幇鍦ㄤ晶杈规爮锛屼絾浠嶇劧鍙互璁块棶" placement="top">
+										<el-icon>
+											<question-filled />
+										</el-icon>
+									</el-tooltip>
+									鏄剧ず鐘舵��
+								</span>
+							</template>
+							<el-radio-group v-model="form.visible">
+								<el-radio v-for="dict in sys_show_hide" :key="dict.value" :label="dict.value">{{ dict.label }} </el-radio>
+							</el-radio-group>
+						</el-form-item>
+					</el-col>
+					<el-col :span="12" v-if="form.menuType !== 'F'">
+						<el-form-item>
+							<template #label>
+								<span>
+									<el-tooltip content="閫夋嫨鍋滅敤鍒欒矾鐢卞皢涓嶄細鍑虹幇鍦ㄤ晶杈规爮锛屼篃涓嶈兘琚闂�" placement="top">
+										<el-icon>
+											<question-filled />
+										</el-icon>
+									</el-tooltip>
+									鑿滃崟鐘舵��
+								</span>
+							</template>
+							<el-radio-group v-model="form.status">
+								<el-radio v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.value">
+									{{ dict.label }}
+								</el-radio>
+							</el-radio-group>
+						</el-form-item>
+					</el-col>
+				</el-row>
+			</el-form>
+			<template #footer>
+				<div class="dialog-footer">
+					<el-button type="primary" @click="submitForm">纭� 瀹�</el-button>
+					<el-button @click="cancel">鍙� 娑�</el-button>
+				</div>
+			</template>
+		</el-dialog>
+	</div>
+</template>
diff --git a/src/views/system/notice/index.vue b/src/views/system/notice/index.vue
index 7e04255..ab68a1a 100644
--- a/src/views/system/notice/index.vue
+++ b/src/views/system/notice/index.vue
@@ -1,283 +1,248 @@
-<template>
-   <div class="app-container">
-      <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch">
-         <el-form-item label="鍏憡鏍囬" prop="noticeTitle">
-            <el-input
-               v-model="queryParams.noticeTitle"
-               placeholder="璇疯緭鍏ュ叕鍛婃爣棰�"
-               clearable
-               style="width: 200px"
-               @keyup.enter="handleQuery"
-            />
-         </el-form-item>
-         <el-form-item label="鎿嶄綔浜哄憳" prop="createByName">
-            <el-input
-               v-model="queryParams.createByName"
-               placeholder="璇疯緭鍏ユ搷浣滀汉鍛�"
-               clearable
-               style="width: 200px"
-               @keyup.enter="handleQuery"
-            />
-         </el-form-item>
-         <el-form-item label="绫诲瀷" prop="noticeType">
-            <el-select v-model="queryParams.noticeType" placeholder="鍏憡绫诲瀷" clearable style="width: 200px">
-               <el-option
-                  v-for="dict in sys_notice_type"
-                  :key="dict.value"
-                  :label="dict.label"
-                  :value="dict.value"
-               />
-            </el-select>
-         </el-form-item>
-         <el-form-item>
-            <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
-            <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
-         </el-form-item>
-      </el-form>
-
-      <el-row :gutter="10" class="mb8">
-         <el-col :span="1.5">
-            <el-button
-               type="primary"
-               plain
-               icon="Plus"
-               @click="handleAdd"
-               v-hasPermi="['system:notice:add']"
-            >鏂板</el-button>
-         </el-col>
-         <el-col :span="1.5">
-            <el-button
-               type="success"
-               plain
-               icon="Edit"
-               :disabled="single"
-               @click="handleUpdate"
-               v-hasPermi="['system:notice:edit']"
-            >淇敼</el-button>
-         </el-col>
-         <el-col :span="1.5">
-            <el-button
-               type="danger"
-               plain
-               icon="Delete"
-               :disabled="multiple"
-               @click="handleDelete"
-               v-hasPermi="['system:notice:remove']"
-            >鍒犻櫎</el-button>
-         </el-col>
-         <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
-      </el-row>
-
-      <el-table v-loading="loading" :data="noticeList" @selection-change="handleSelectionChange">
-         <el-table-column type="selection" width="55" align="center" />
-         <el-table-column label="搴忓彿" align="center" prop="noticeId" width="100" v-if="false" />
-         <el-table-column
-            label="鍏憡鏍囬"
-            align="center"
-            prop="noticeTitle"
-            :show-overflow-tooltip="true"
-         />
-         <el-table-column label="鍏憡绫诲瀷" align="center" prop="noticeType" width="100">
-            <template #default="scope">
-               <dict-tag :options="sys_notice_type" :value="scope.row.noticeType" />
-            </template>
-         </el-table-column>
-         <el-table-column label="鐘舵��" align="center" prop="status" width="100">
-            <template #default="scope">
-               <dict-tag :options="sys_notice_status" :value="scope.row.status" />
-            </template>
-         </el-table-column>
-         <el-table-column label="鍒涘缓鑰�" align="center" prop="createByName" width="100" />
-         <el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime" width="100">
-            <template #default="scope">
-               <span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>
-            </template>
-         </el-table-column>
-         <el-table-column label="鎿嶄綔" align="center" class-name="small-padding fixed-width">
-            <template #default="scope">
-               <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:notice:edit']">淇敼</el-button>
-               <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:notice:remove']" >鍒犻櫎</el-button>
-            </template>
-         </el-table-column>
-      </el-table>
-
-      <pagination
-         v-show="total > 0"
-         :total="total"
-         v-model:page="queryParams.pageNum"
-         v-model:limit="queryParams.pageSize"
-         @pagination="getList"
-      />
-
-      <!-- 娣诲姞鎴栦慨鏀瑰叕鍛婂璇濇 -->
-      <el-dialog :title="title" v-model="open" width="780px" append-to-body>
-         <el-form ref="noticeRef" :model="form" :rules="rules" label-width="80px">
-            <el-row>
-               <el-col :span="12">
-                  <el-form-item label="鍏憡鏍囬" prop="noticeTitle">
-                     <el-input v-model="form.noticeTitle" placeholder="璇疯緭鍏ュ叕鍛婃爣棰�" />
-                  </el-form-item>
-               </el-col>
-               <el-col :span="12">
-                  <el-form-item label="鍏憡绫诲瀷" prop="noticeType">
-                     <el-select v-model="form.noticeType" placeholder="璇烽�夋嫨">
-                        <el-option
-                           v-for="dict in sys_notice_type"
-                           :key="dict.value"
-                           :label="dict.label"
-                           :value="dict.value"
-                        ></el-option>
-                     </el-select>
-                  </el-form-item>
-               </el-col>
-               <el-col :span="24">
-                  <el-form-item label="鐘舵��">
-                     <el-radio-group v-model="form.status">
-                        <el-radio
-                           v-for="dict in sys_notice_status"
-                           :key="dict.value"
-                           :label="dict.value"
-                        >{{ dict.label }}</el-radio>
-                     </el-radio-group>
-                  </el-form-item>
-               </el-col>
-               <el-col :span="24">
-                  <el-form-item label="鍐呭">
-                    <editor v-model="form.noticeContent" :min-height="192"/>
-                  </el-form-item>
-               </el-col>
-            </el-row>
-         </el-form>
-         <template #footer>
-            <div class="dialog-footer">
-               <el-button type="primary" @click="submitForm">纭� 瀹�</el-button>
-               <el-button @click="cancel">鍙� 娑�</el-button>
-            </div>
-         </template>
-      </el-dialog>
-   </div>
-</template>
-
-<script setup name="Notice">
+<script setup name="Notice" lang="ts">
 import { listNotice, getNotice, delNotice, addNotice, updateNotice } from "@/api/system/notice";
+import { ComponentInternalInstance } from "vue";
+import { NoticeForm, NoticeQuery, NoticeVO } from "@/api/system/notice/types";
+import { ElForm } from 'element-plus';
 
-const { proxy } = getCurrentInstance();
-const { sys_notice_status, sys_notice_type } = proxy.useDict("sys_notice_status", "sys_notice_type");
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { sys_notice_status, sys_notice_type } = toRefs<any>(proxy?.useDict("sys_notice_status", "sys_notice_type"));
 
-const noticeList = ref([]);
-const open = ref(false);
+const noticeList = ref<NoticeVO[]>([]);
 const loading = ref(true);
 const showSearch = ref(true);
-const ids = ref([]);
+const ids = ref<Array<string | number>>([]);
 const single = ref(true);
 const multiple = ref(true);
 const total = ref(0);
-const title = ref("");
 
-const data = reactive({
-  form: {},
-  queryParams: {
-    pageNum: 1,
-    pageSize: 10,
-    noticeTitle: undefined,
-    createByName: undefined,
-    status: undefined
-  },
-  rules: {
-    noticeTitle: [{ required: true, message: "鍏憡鏍囬涓嶈兘涓虹┖", trigger: "blur" }],
-    noticeType: [{ required: true, message: "鍏憡绫诲瀷涓嶈兘涓虹┖", trigger: "change" }]
-  },
+const queryFormRef = ref(ElForm);
+const noticeFormRef = ref(ElForm);
+
+
+const dialog = reactive<DialogOption>({
+	visible: false,
+	title: ''
+});
+
+const initFormData: NoticeForm = {
+	noticeId: undefined,
+	noticeTitle: '',
+	noticeType: '',
+	noticeContent: '',
+	status: "0",
+	remark: '',
+	createByName: ''
+}
+const data = reactive<PageData<NoticeForm, NoticeQuery>>({
+	form: { ...initFormData },
+	queryParams: {
+		pageNum: 1,
+		pageSize: 10,
+		noticeTitle: '',
+		createByName: '',
+		status: '',
+		noticeType: ''
+	},
+	rules: {
+		noticeTitle: [{ required: true, message: "鍏憡鏍囬涓嶈兘涓虹┖", trigger: "blur" }],
+		noticeType: [{ required: true, message: "鍏憡绫诲瀷涓嶈兘涓虹┖", trigger: "change" }]
+	},
 });
 
 const { queryParams, form, rules } = toRefs(data);
 
 /** 鏌ヨ鍏憡鍒楄〃 */
-function getList() {
-  loading.value = true;
-  listNotice(queryParams.value).then(response => {
-    noticeList.value = response.rows;
-    total.value = response.total;
-    loading.value = false;
-  });
+const getList = async () => {
+	loading.value = true;
+	const res = await listNotice(queryParams.value);
+	noticeList.value = res.rows;
+	total.value = res.total;
+	loading.value = false;
 }
 /** 鍙栨秷鎸夐挳 */
-function cancel() {
-  open.value = false;
-  reset();
+const cancel = () => {
+	reset();
+	dialog.visible = false;
 }
 /** 琛ㄥ崟閲嶇疆 */
-function reset() {
-  form.value = {
-    noticeId: undefined,
-    noticeTitle: undefined,
-    noticeType: undefined,
-    noticeContent: undefined,
-    status: "0"
-  };
-  proxy.resetForm("noticeRef");
+const reset = () => {
+	form.value = { ...initFormData };
+	noticeFormRef.value.resetFields();
 }
 /** 鎼滅储鎸夐挳鎿嶄綔 */
-function handleQuery() {
-  queryParams.value.pageNum = 1;
-  getList();
+const handleQuery = () => {
+	queryParams.value.pageNum = 1;
+	getList();
 }
 /** 閲嶇疆鎸夐挳鎿嶄綔 */
-function resetQuery() {
-  proxy.resetForm("queryRef");
-  handleQuery();
+const resetQuery = () => {
+	queryFormRef.value.resetFields();
+	handleQuery();
 }
 /** 澶氶�夋閫変腑鏁版嵁 */
-function handleSelectionChange(selection) {
-  ids.value = selection.map(item => item.noticeId);
-  single.value = selection.length != 1;
-  multiple.value = !selection.length;
+const handleSelectionChange = (selection: NoticeVO[]) => {
+	ids.value = selection.map(item => item.noticeId);
+	single.value = selection.length != 1;
+	multiple.value = !selection.length;
 }
 /** 鏂板鎸夐挳鎿嶄綔 */
-function handleAdd() {
-  reset();
-  open.value = true;
-  title.value = "娣诲姞鍏憡";
+const handleAdd = () => {
+	dialog.visible = true;
+	dialog.title = "娣诲姞鍏憡";
+	nextTick(() => {
+		reset();
+	})
 }
 /**淇敼鎸夐挳鎿嶄綔 */
-function handleUpdate(row) {
-  reset();
-  const noticeId = row.noticeId || ids.value;
-  getNotice(noticeId).then(response => {
-    form.value = response.data;
-    open.value = true;
-    title.value = "淇敼鍏憡";
-  });
+const handleUpdate = (row?: NoticeVO) => {
+	dialog.visible = true;
+	dialog.title = "淇敼鍏憡";
+	nextTick(async () => {
+		const noticeId = row?.noticeId || ids.value[0];
+		reset();
+		const { data } = await getNotice(noticeId);
+		form.value = data;
+	})
 }
 /** 鎻愪氦鎸夐挳 */
-function submitForm() {
-  proxy.$refs["noticeRef"].validate(valid => {
-    if (valid) {
-      if (form.value.noticeId != undefined) {
-        updateNotice(form.value).then(response => {
-          proxy.$modal.msgSuccess("淇敼鎴愬姛");
-          open.value = false;
-          getList();
-        });
-      } else {
-        addNotice(form.value).then(response => {
-          proxy.$modal.msgSuccess("鏂板鎴愬姛");
-          open.value = false;
-          getList();
-        });
-      }
-    }
-  });
+const submitForm = () => {
+	noticeFormRef.value.validate(async (valid: boolean) => {
+		if (valid) {
+			form.value.noticeId ? await updateNotice(form.value) : await addNotice(form.value);
+			proxy?.$modal.msgSuccess("淇敼鎴愬姛");
+			dialog.visible = false;
+			getList();
+		}
+	});
 }
 /** 鍒犻櫎鎸夐挳鎿嶄綔 */
-function handleDelete(row) {
-  const noticeIds = row.noticeId || ids.value
-  proxy.$modal.confirm('鏄惁纭鍒犻櫎鍏憡缂栧彿涓�"' + noticeIds + '"鐨勬暟鎹」锛�').then(function() {
-    return delNotice(noticeIds);
-  }).then(() => {
-    getList();
-    proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
-  }).catch(() => {});
+const handleDelete = async (row?: NoticeVO) => {
+	const noticeIds = row?.noticeId || ids.value
+	await proxy?.$modal.confirm('鏄惁纭鍒犻櫎鍏憡缂栧彿涓�"' + noticeIds + '"鐨勬暟鎹」锛�');
+	await delNotice(noticeIds);
+	getList();
+	proxy?.$modal.msgSuccess("鍒犻櫎鎴愬姛");
 }
 
-getList();
+onMounted(() => {
+	getList();
+})
 </script>
+<template>
+	<div class="p-2">
+		<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
+			<div class="search" v-show="showSearch">
+				<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
+					<el-form-item label="鍏憡鏍囬" prop="noticeTitle">
+						<el-input v-model="queryParams.noticeTitle" placeholder="璇疯緭鍏ュ叕鍛婃爣棰�" clearable style="width: 200px" @keyup.enter="handleQuery" />
+					</el-form-item>
+					<el-form-item label="鎿嶄綔浜哄憳" prop="createByName">
+						<el-input v-model="queryParams.createByName" placeholder="璇疯緭鍏ユ搷浣滀汉鍛�" clearable style="width: 200px" @keyup.enter="handleQuery" />
+					</el-form-item>
+					<el-form-item label="绫诲瀷" prop="noticeType">
+						<el-select v-model="queryParams.noticeType" placeholder="鍏憡绫诲瀷" clearable style="width: 200px">
+							<el-option v-for="dict in sys_notice_type" :key="dict.value" :label="dict.label" :value="dict.value" />
+						</el-select>
+					</el-form-item>
+					<el-form-item>
+						<el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
+						<el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
+					</el-form-item>
+				</el-form>
+			</div>
+		</transition>
+
+		<el-card shadow="never">
+			<template #header>
+				<el-row :gutter="10" class="mb8">
+					<el-col :span="1.5">
+						<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:notice:add']">鏂板</el-button>
+					</el-col>
+					<el-col :span="1.5">
+						<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['system:notice:edit']"
+							>淇敼</el-button
+						>
+					</el-col>
+					<el-col :span="1.5">
+						<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['system:notice:remove']">
+							鍒犻櫎
+						</el-button>
+					</el-col>
+					<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+				</el-row>
+			</template>
+
+			<el-table v-loading="loading" :data="noticeList" @selection-change="handleSelectionChange">
+				<el-table-column type="selection" width="55" align="center" />
+				<el-table-column label="搴忓彿" align="center" prop="noticeId" width="100" v-if="false" />
+				<el-table-column label="鍏憡鏍囬" align="center" prop="noticeTitle" :show-overflow-tooltip="true" />
+				<el-table-column label="鍏憡绫诲瀷" align="center" prop="noticeType" width="100">
+					<template #default="scope">
+						<dict-tag :options="sys_notice_type" :value="scope.row.noticeType" />
+					</template>
+				</el-table-column>
+				<el-table-column label="鐘舵��" align="center" prop="status" width="100">
+					<template #default="scope">
+						<dict-tag :options="sys_notice_status" :value="scope.row.status" />
+					</template>
+				</el-table-column>
+				<el-table-column label="鍒涘缓鑰�" align="center" prop="createByName" width="100" />
+				<el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime" width="100">
+					<template #default="scope">
+						<span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>
+					</template>
+				</el-table-column>
+				<el-table-column label="鎿嶄綔" align="center" class-name="small-padding fixed-width">
+					<template #default="scope">
+						<el-tooltip content="淇敼" placement="top">
+							<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:notice:edit']"></el-button>
+						</el-tooltip>
+						<el-tooltip content="鍒犻櫎" placement="top">
+							<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:notice:remove']"></el-button>
+						</el-tooltip>
+					</template>
+				</el-table-column>
+			</el-table>
+
+			<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
+		</el-card>
+		<!-- 娣诲姞鎴栦慨鏀瑰叕鍛婂璇濇 -->
+		<el-dialog :title="dialog.title" v-model="dialog.visible" width="780px" append-to-body>
+			<el-form ref="noticeFormRef" :model="form" :rules="rules" label-width="80px">
+				<el-row>
+					<el-col :span="12">
+						<el-form-item label="鍏憡鏍囬" prop="noticeTitle">
+							<el-input v-model="form.noticeTitle" placeholder="璇疯緭鍏ュ叕鍛婃爣棰�" />
+						</el-form-item>
+					</el-col>
+					<el-col :span="12">
+						<el-form-item label="鍏憡绫诲瀷" prop="noticeType">
+							<el-select v-model="form.noticeType" placeholder="璇烽�夋嫨">
+								<el-option v-for="dict in sys_notice_type" :key="dict.value" :label="dict.label" :value="dict.value"></el-option>
+							</el-select>
+						</el-form-item>
+					</el-col>
+					<el-col :span="24">
+						<el-form-item label="鐘舵��">
+							<el-radio-group v-model="form.status">
+								<el-radio v-for="dict in sys_notice_status" :key="dict.value" :label="dict.value">{{ dict.label
+								}}</el-radio>
+							</el-radio-group>
+						</el-form-item>
+					</el-col>
+					<el-col :span="24">
+						<el-form-item label="鍐呭">
+							<editor v-model="form.noticeContent" :min-height="192" />
+						</el-form-item>
+					</el-col>
+				</el-row>
+			</el-form>
+			<template #footer>
+				<div class="dialog-footer">
+					<el-button type="primary" @click="submitForm">纭� 瀹�</el-button>
+					<el-button @click="cancel">鍙� 娑�</el-button>
+				</div>
+			</template>
+		</el-dialog>
+	</div>
+</template>
diff --git a/src/views/system/oss/config.vue b/src/views/system/oss/config.vue
index 4f2efa1..7b31987 100644
--- a/src/views/system/oss/config.vue
+++ b/src/views/system/oss/config.vue
@@ -1,170 +1,4 @@
-<template>
-  <div class="app-container">
-    <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch">
-      <el-form-item label="閰嶇疆key" prop="configKey">
-        <el-input
-          v-model="queryParams.configKey"
-          placeholder="閰嶇疆key"
-          clearable
-          style="width: 200px"
-          @keyup.enter="handleQuery"
-        />
-      </el-form-item>
-      <el-form-item label="妗跺悕绉�" prop="bucketName">
-        <el-input
-          v-model="queryParams.bucketName"
-          placeholder="璇疯緭鍏ユ《鍚嶇О"
-          clearable
-          style="width: 200px"
-          @keyup.enter="handleQuery"
-        />
-      </el-form-item>
-      <el-form-item label="鏄惁榛樿" prop="status">
-        <el-select v-model="queryParams.status" placeholder="璇烽�夋嫨鐘舵��" clearable style="width: 200px">
-          <el-option key="0" label="鏄�" value="0"/>
-          <el-option key="1" label="鍚�" value="1"/>
-        </el-select>
-      </el-form-item>
-      <el-form-item>
-        <el-button type="primary" icon="search" @click="handleQuery">鎼滅储</el-button>
-        <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
-      </el-form-item>
-    </el-form>
-
-    <el-row :gutter="10" class="mb8">
-      <el-col :span="1.5">
-        <el-button
-          type="primary"
-          plain
-          icon="Plus"
-          @click="handleAdd"
-          v-hasPermi="['system:oss:add']"
-        >鏂板</el-button>
-      </el-col>
-      <el-col :span="1.5">
-        <el-button
-          type="success"
-          plain
-          icon="Edit"
-          :disabled="single"
-          @click="handleUpdate"
-          v-hasPermi="['system:oss:edit']"
-        >淇敼</el-button>
-      </el-col>
-      <el-col :span="1.5">
-        <el-button
-          type="danger"
-          plain
-          icon="Delete"
-          :disabled="multiple"
-          @click="handleDelete"
-          v-hasPermi="['system:oss:remove']"
-        >鍒犻櫎</el-button>
-      </el-col>
-      <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
-    </el-row>
-
-    <el-table v-loading="loading" :data="ossConfigList" @selection-change="handleSelectionChange">
-      <el-table-column type="selection" width="55" align="center" />
-      <el-table-column label="涓诲缓" align="center" prop="ossConfigId" v-if="columns[0].visible"/>
-      <el-table-column label="閰嶇疆key" align="center" prop="configKey" v-if="columns[1].visible" />
-      <el-table-column label="璁块棶绔欑偣" align="center" prop="endpoint" v-if="columns[2].visible" width="200" />
-      <el-table-column label="鑷畾涔夊煙鍚�" align="center" prop="domain" v-if="columns[3].visible" width="200" />
-      <el-table-column label="妗跺悕绉�" align="center" prop="bucketName" v-if="columns[4].visible" />
-      <el-table-column label="鍓嶇紑" align="center" prop="prefix" v-if="columns[5].visible" />
-      <el-table-column label="鍩�" align="center" prop="region" v-if="columns[6].visible" />
-      <el-table-column label="妗舵潈闄愮被鍨�" align="center" prop="accessPolicy" v-if="columns[7].visible" >
-        <template #default="scope">
-          <el-tag type="warning" v-if="scope.row.accessPolicy === '0'">private</el-tag>
-          <el-tag type="success" v-if="scope.row.accessPolicy === '1'">public</el-tag>
-          <el-tag type="info" v-if="scope.row.accessPolicy === '2'">custom</el-tag>
-        </template>
-      </el-table-column>
-      <el-table-column label="鏄惁榛樿" align="center" prop="status" v-if="columns[8].visible">
-        <template #default="scope">
-          <el-switch
-            v-model="scope.row.status"
-            active-value="0"
-            inactive-value="1"
-            @change="handleStatusChange(scope.row)"
-          ></el-switch>
-        </template>
-      </el-table-column>
-      <el-table-column label="鎿嶄綔" align="center" width="150" class-name="small-padding fixed-width">
-        <template #default="scope">
-          <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:oss:edit']">淇敼</el-button>
-          <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:oss:remove']">鍒犻櫎</el-button>
-        </template>
-      </el-table-column>
-    </el-table>
-
-    <pagination
-      v-show="total > 0"
-      :total="total"
-      v-model:page="queryParams.pageNum"
-      v-model:limit="queryParams.pageSize"
-      @pagination="getList"
-    />
-
-    <!-- 娣诲姞鎴栦慨鏀瑰璞″瓨鍌ㄩ厤缃璇濇 -->
-    <el-dialog :title="title" v-model="open" width="800px" append-to-body>
-      <el-form ref="ossConfigRef" :model="form" :rules="rules" label-width="120px">
-        <el-form-item label="閰嶇疆key" prop="configKey">
-          <el-input v-model="form.configKey" placeholder="璇疯緭鍏ラ厤缃甼ey" />
-        </el-form-item>
-        <el-form-item label="璁块棶绔欑偣" prop="endpoint">
-          <el-input v-model="form.endpoint" placeholder="璇疯緭鍏ヨ闂珯鐐�" />
-        </el-form-item>
-        <el-form-item label="鑷畾涔夊煙鍚�" prop="domain">
-          <el-input v-model="form.domain" placeholder="璇疯緭鍏ヨ嚜瀹氫箟鍩熷悕" />
-        </el-form-item>
-        <el-form-item label="accessKey" prop="accessKey">
-          <el-input v-model="form.accessKey" placeholder="璇疯緭鍏ccessKey" />
-        </el-form-item>
-        <el-form-item label="secretKey" prop="secretKey">
-          <el-input v-model="form.secretKey" placeholder="璇疯緭鍏ョ閽�" show-password />
-        </el-form-item>
-        <el-form-item label="妗跺悕绉�" prop="bucketName">
-          <el-input v-model="form.bucketName" placeholder="璇疯緭鍏ユ《鍚嶇О" />
-        </el-form-item>
-        <el-form-item label="鍓嶇紑" prop="prefix">
-          <el-input v-model="form.prefix" placeholder="璇疯緭鍏ュ墠缂�" />
-        </el-form-item>
-        <el-form-item label="鏄惁HTTPS">
-          <el-radio-group v-model="form.isHttps">
-            <el-radio
-              v-for="dict in sys_yes_no"
-              :key="dict.value"
-              :label="dict.value"
-            >{{dict.label}}</el-radio>
-          </el-radio-group>
-        </el-form-item>
-        <el-form-item label="妗舵潈闄愮被鍨�">
-          <el-radio-group v-model="form.accessPolicy">
-            <el-radio label="0">private</el-radio>
-            <el-radio label="1">public</el-radio>
-            <el-radio label="2">custom</el-radio>
-          </el-radio-group>
-        </el-form-item>
-        <el-form-item label="鍩�" prop="region">
-          <el-input v-model="form.region" placeholder="璇疯緭鍏ュ煙" />
-        </el-form-item>
-        <el-form-item label="澶囨敞" prop="remark">
-          <el-input v-model="form.remark" type="textarea" placeholder="璇疯緭鍏ュ唴瀹�" />
-        </el-form-item>
-      </el-form>
-      <template #footer>
-        <div class="dialog-footer">
-          <el-button :loading="buttonLoading" type="primary" @click="submitForm">纭� 瀹�</el-button>
-          <el-button @click="cancel">鍙� 娑�</el-button>
-        </div>
-      </template>
-    </el-dialog>
-  </div>
-</template>
-
-<script setup name="OssConfig">
-
+<script setup name="OssConfig" lang="ts">
 import {
   listOssConfig,
   getOssConfig,
@@ -173,23 +7,33 @@
   updateOssConfig,
   changeOssConfigStatus
 } from "@/api/system/ossConfig";
+import { ComponentInternalInstance } from "vue";
+import { OssConfigForm, OssConfigQuery, OssConfigVO } from "@/api/system/ossConfig/types";
+import { ElForm } from 'element-plus';
 
-const { proxy } = getCurrentInstance();
-const { sys_yes_no } = proxy.useDict("sys_yes_no");
 
-const ossConfigList = ref([]);
-const open = ref(false);
+const { proxy } = getCurrentInstance() as ComponentInternalInstance
+const { sys_yes_no } = toRefs<any>(proxy?.useDict("sys_yes_no"));
+
+const ossConfigList = ref<OssConfigVO[]>([]);
 const buttonLoading = ref(false);
 const loading = ref(true);
 const showSearch = ref(true);
-const ids = ref([]);
+const ids = ref<Array<number | string>>([]);
 const single = ref(true);
 const multiple = ref(true);
 const total = ref(0);
-const title = ref("");
+
+const queryFormRef = ref(ElForm);
+const ossConfigFormRef = ref(ElForm);
+
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
 
 // 鍒楁樉闅愪俊鎭�
-const columns = ref([
+const columns = ref<FieldOption[]>([
   { key: 0, label: `涓诲缓`, visible: true },
   { key: 1, label: `閰嶇疆key`, visible: false },
   { key: 2, label: `璁块棶绔欑偣`, visible: true },
@@ -201,20 +45,34 @@
   { key: 8, label: `鐘舵�乣, visible: true }
 ]);
 
-const data = reactive({
-  form: {},
+
+const initFormData: OssConfigForm = {
+  ossConfigId: undefined,
+  configKey: '',
+  accessKey: '',
+  secretKey: '',
+  bucketName: '',
+  prefix: '',
+  endpoint: '',
+  domain: '',
+  isHttps: "N",
+  accessPolicy: "1",
+  region: '',
+  status: "1",
+  remark: '',
+}
+const data = reactive<PageData<OssConfigForm, OssConfigQuery>>({
+  form: { ...initFormData },
   // 鏌ヨ鍙傛暟
   queryParams: {
     pageNum: 1,
     pageSize: 10,
-    configKey: undefined,
-    bucketName: undefined,
-    status: undefined,
+    configKey: '',
+    bucketName: '',
+    status: '',
   },
   rules: {
-    configKey: [
-      { required: true, message: "configKey涓嶈兘涓虹┖", trigger: "blur" },
-    ],
+    configKey: [{ required: true, message: "configKey涓嶈兘涓虹┖", trigger: "blur" },],
     accessKey: [
       { required: true, message: "accessKey涓嶈兘涓虹┖", trigger: "blur" },
       {
@@ -251,132 +109,238 @@
         trigger: "blur",
       },
     ],
-    accessPolicy:[
-      { required: true, message: "accessPolicy涓嶈兘涓虹┖", trigger: "blur" }
-    ]
+    accessPolicy: [{ required: true, message: "accessPolicy涓嶈兘涓虹┖", trigger: "blur" }]
   }
 });
 
 const { queryParams, form, rules } = toRefs(data);
 
 /** 鏌ヨ瀵硅薄瀛樺偍閰嶇疆鍒楄〃 */
-function getList() {
+const getList = async () => {
   loading.value = true;
-  listOssConfig(queryParams.value).then((response) => {
-    ossConfigList.value = response.rows;
-    total.value = response.total;
-    loading.value = false;
-  });
+  const res = await listOssConfig(queryParams.value);
+  ossConfigList.value = res.rows;
+  total.value = res.total;
+  loading.value = false;
 }
 /** 鍙栨秷鎸夐挳 */
-function cancel() {
-  open.value = false;
+const cancel = () => {
+  dialog.visible = false;
   reset();
 }
 /** 琛ㄥ崟閲嶇疆 */
-function reset() {
-  form.value = {
-    ossConfigId: undefined,
-    configKey: undefined,
-    accessKey: undefined,
-    secretKey: undefined,
-    bucketName: undefined,
-    prefix: undefined,
-    endpoint: undefined,
-    domain: undefined,
-    isHttps: "N",
-    accessPolicy: "1",
-    region: undefined,
-    status: "1",
-    remark: undefined,
-  };
-  proxy.resetForm("ossConfigRef");
+const reset = () => {
+  form.value = { ...initFormData };
+  ossConfigFormRef.value.resetFields();
 }
 /** 鎼滅储鎸夐挳鎿嶄綔 */
-function handleQuery() {
+const handleQuery = () => {
   queryParams.value.pageNum = 1;
   getList();
 }
 /** 閲嶇疆鎸夐挳鎿嶄綔 */
-function resetQuery() {
-  proxy.resetForm("queryRef");
+const resetQuery = () => {
+  queryFormRef.value.resetFields();
   handleQuery();
 }
 /** 閫夋嫨鏉℃暟  */
-function handleSelectionChange(selection) {
+const handleSelectionChange = (selection: OssConfigVO[]) => {
   ids.value = selection.map(item => item.ossConfigId);
   single.value = selection.length != 1;
   multiple.value = !selection.length;
 }
 /** 鏂板鎸夐挳鎿嶄綔 */
-function handleAdd() {
-  reset();
-  open.value = true;
-  title.value = "娣诲姞瀵硅薄瀛樺偍閰嶇疆";
+const handleAdd = () => {
+  dialog.visible = true;
+  dialog.title = "娣诲姞瀵硅薄瀛樺偍閰嶇疆";
+  nextTick(() => {
+    reset();
+  })
 }
 /** 淇敼鎸夐挳鎿嶄綔 */
-function handleUpdate(row) {
+const handleUpdate = (row?: OssConfigVO) => {
   loading.value = true;
-  reset();
-  const ossConfigId = row.ossConfigId || ids.value;
-  getOssConfig(ossConfigId).then((response) => {
+  dialog.visible = true;
+  dialog.title = "淇敼瀵硅薄瀛樺偍閰嶇疆";
+  const ossConfigId = row?.ossConfigId || ids.value[0];
+  nextTick(async () => {
+    reset();
+    const res = await getOssConfig(ossConfigId);
     loading.value = false;
-    form.value = response.data;
-    open.value = true;
-    title.value = "淇敼瀵硅薄瀛樺偍閰嶇疆";
-  });
+    form.value = res.data;
+  })
 }
 /** 鎻愪氦鎸夐挳 */
-function submitForm() {
-  proxy.$refs["ossConfigRef"].validate(valid => {
+const submitForm = () => {
+  ossConfigFormRef.value.validate(async (valid: boolean) => {
     if (valid) {
       buttonLoading.value = true;
-      if (form.value.ossConfigId != null) {
-        updateOssConfig(form.value).then(response => {
-          proxy.$modal.msgSuccess("淇敼鎴愬姛");
-          open.value = false;
-          getList();
-        }).finally(() => {
-          buttonLoading.value = false;
-        });
+      if (form.value.ossConfigId) {
+        await updateOssConfig(form.value).finally(() => buttonLoading.value = false);
       } else {
-        addOssConfig(this.form).then(response => {
-          proxy.$modal.msgSuccess("鏂板鎴愬姛");
-          open.value = false;
-          getList();
-        }).finally(() => {
-          buttonLoading.value = false;
-        });
+        await addOssConfig(form.value).finally(() => buttonLoading.value = false);
       }
+      proxy?.$modal.msgSuccess("鏂板鎴愬姛");
+      dialog.visible = false;
+      getList();
     }
   });
 }
-/** 鐢ㄦ埛鐘舵�佷慨鏀�  */
-function handleStatusChange(row) {
+/** 鐘舵�佷慨鏀�  */
+const  handleStatusChange = async (row: OssConfigVO) => {
   let text = row.status === "0" ? "鍚敤" : "鍋滅敤";
-  proxy.$modal.confirm('纭瑕�"' + text + '""' + row.configKey + '"閰嶇疆鍚�?').then(() => {
-    return changeOssConfigStatus(row.ossConfigId, row.status, row.configKey);
-  }).then(() => {
+  try {
+    await proxy?.$modal.confirm('纭瑕�"' + text + '""' + row.configKey + '"閰嶇疆鍚�?');
+    await changeOssConfigStatus(row.ossConfigId, row.status, row.configKey);
     getList()
-    proxy.$modal.msgSuccess(text + "鎴愬姛");
-  }).catch(function () {
+    proxy?.$modal.msgSuccess(text + "鎴愬姛");
+  } catch { return } finally {
     row.status = row.status === "0" ? "1" : "0";
-  });
+  }
+
 }
 /** 鍒犻櫎鎸夐挳鎿嶄綔 */
-function handleDelete(row) {
-  const ossConfigIds = row.ossConfigId || ids.value;
-  proxy.$modal.confirm('鏄惁纭鍒犻櫎OSS閰嶇疆缂栧彿涓�"' + ossConfigIds + '"鐨勬暟鎹」?').then(() => {
-    loading.value = true;
-    return delOssConfig(ossConfigIds);
-  }).then(() => {
-    loading.value = false;
-    getList();
-    proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
-  }).finally(() => {
-    loading.value = false;
-  });
+const handleDelete = async (row?: OssConfigVO) => {
+  const ossConfigIds = row?.ossConfigId || ids.value;
+  await proxy?.$modal.confirm('鏄惁纭鍒犻櫎OSS閰嶇疆缂栧彿涓�"' + ossConfigIds + '"鐨勬暟鎹」?');
+  loading.value = true;
+  await delOssConfig(ossConfigIds).finally(() => loading.value = false);
+  getList();
+  proxy?.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+
 }
 
-getList();
+onMounted(() => {
+  getList();
+})
 </script>
+<template>
+	<div class="p-2">
+		<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
+			<div class="search" v-show="showSearch">
+				<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
+					<el-form-item label="閰嶇疆key" prop="configKey">
+						<el-input v-model="queryParams.configKey" placeholder="閰嶇疆key" clearable style="width: 200px" @keyup.enter="handleQuery" />
+					</el-form-item>
+					<el-form-item label="妗跺悕绉�" prop="bucketName">
+						<el-input v-model="queryParams.bucketName" placeholder="璇疯緭鍏ユ《鍚嶇О" clearable style="width: 200px" @keyup.enter="handleQuery" />
+					</el-form-item>
+					<el-form-item label="鏄惁榛樿" prop="status">
+						<el-select v-model="queryParams.status" placeholder="璇烽�夋嫨鐘舵��" clearable style="width: 200px">
+							<el-option key="0" label="鏄�" value="0" />
+							<el-option key="1" label="鍚�" value="1" />
+						</el-select>
+					</el-form-item>
+					<el-form-item>
+						<el-button type="primary" icon="search" @click="handleQuery">鎼滅储</el-button>
+						<el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
+					</el-form-item>
+				</el-form>
+			</div>
+		</transition>
+
+		<el-card shadow="never">
+			<template #header>
+				<el-row :gutter="10" class="mb8">
+					<el-col :span="1.5">
+						<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:oss:add']">鏂板</el-button>
+					</el-col>
+					<el-col :span="1.5">
+						<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['system:oss:edit']">淇敼</el-button>
+					</el-col>
+					<el-col :span="1.5">
+						<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['system:oss:remove']">
+							鍒犻櫎
+						</el-button>
+					</el-col>
+					<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+				</el-row>
+			</template>
+
+			<el-table v-loading="loading" :data="ossConfigList" @selection-change="handleSelectionChange">
+				<el-table-column type="selection" width="55" align="center" />
+				<el-table-column label="涓诲缓" align="center" prop="ossConfigId" v-if="columns[0].visible" />
+				<el-table-column label="閰嶇疆key" align="center" prop="configKey" v-if="columns[1].visible" />
+				<el-table-column label="璁块棶绔欑偣" align="center" prop="endpoint" v-if="columns[2].visible" width="200" />
+				<el-table-column label="鑷畾涔夊煙鍚�" align="center" prop="domain" v-if="columns[3].visible" width="200" />
+				<el-table-column label="妗跺悕绉�" align="center" prop="bucketName" v-if="columns[4].visible" />
+				<el-table-column label="鍓嶇紑" align="center" prop="prefix" v-if="columns[5].visible" />
+				<el-table-column label="鍩�" align="center" prop="region" v-if="columns[6].visible" />
+				<el-table-column label="妗舵潈闄愮被鍨�" align="center" prop="accessPolicy" v-if="columns[7].visible">
+					<template #default="scope">
+						<el-tag type="warning" v-if="scope.row.accessPolicy === '0'">private</el-tag>
+						<el-tag type="success" v-if="scope.row.accessPolicy === '1'">public</el-tag>
+						<el-tag type="info" v-if="scope.row.accessPolicy === '2'">custom</el-tag>
+					</template>
+				</el-table-column>
+				<el-table-column label="鏄惁榛樿" align="center" prop="status" v-if="columns[8].visible">
+					<template #default="scope">
+						<el-switch v-model="scope.row.status" active-value="0" inactive-value="1" @change="handleStatusChange(scope.row)"></el-switch>
+					</template>
+				</el-table-column>
+				<el-table-column label="鎿嶄綔" align="center" width="150" class-name="small-padding fixed-width">
+					<template #default="scope">
+						<el-tooltip content="淇敼" placement="top">
+							<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:oss:edit']"></el-button>
+						</el-tooltip>
+						<el-tooltip content="鍒犻櫎" placement="top">
+							<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:oss:remove']"></el-button>
+						</el-tooltip>
+					</template>
+				</el-table-column>
+			</el-table>
+
+			<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
+		</el-card>
+		<!-- 娣诲姞鎴栦慨鏀瑰璞″瓨鍌ㄩ厤缃璇濇 -->
+		<el-dialog :title="dialog.title" v-model="dialog.visible" width="800px" append-to-body>
+			<el-form ref="ossConfigFormRef" :model="form" :rules="rules" label-width="120px">
+				<el-form-item label="閰嶇疆key" prop="configKey">
+					<el-input v-model="form.configKey" placeholder="璇疯緭鍏ラ厤缃甼ey" />
+				</el-form-item>
+				<el-form-item label="璁块棶绔欑偣" prop="endpoint">
+					<el-input v-model="form.endpoint" placeholder="璇疯緭鍏ヨ闂珯鐐�" />
+				</el-form-item>
+				<el-form-item label="鑷畾涔夊煙鍚�" prop="domain">
+					<el-input v-model="form.domain" placeholder="璇疯緭鍏ヨ嚜瀹氫箟鍩熷悕" />
+				</el-form-item>
+				<el-form-item label="accessKey" prop="accessKey">
+					<el-input v-model="form.accessKey" placeholder="璇疯緭鍏ccessKey" />
+				</el-form-item>
+				<el-form-item label="secretKey" prop="secretKey">
+					<el-input v-model="form.secretKey" placeholder="璇疯緭鍏ョ閽�" show-password />
+				</el-form-item>
+				<el-form-item label="妗跺悕绉�" prop="bucketName">
+					<el-input v-model="form.bucketName" placeholder="璇疯緭鍏ユ《鍚嶇О" />
+				</el-form-item>
+				<el-form-item label="鍓嶇紑" prop="prefix">
+					<el-input v-model="form.prefix" placeholder="璇疯緭鍏ュ墠缂�" />
+				</el-form-item>
+				<el-form-item label="鏄惁HTTPS">
+					<el-radio-group v-model="form.isHttps">
+						<el-radio v-for="dict in sys_yes_no" :key="dict.value" :label="dict.value">{{ dict.label }}</el-radio>
+					</el-radio-group>
+				</el-form-item>
+				<el-form-item label="妗舵潈闄愮被鍨�">
+					<el-radio-group v-model="form.accessPolicy">
+						<el-radio label="0">private</el-radio>
+						<el-radio label="1">public</el-radio>
+						<el-radio label="2">custom</el-radio>
+					</el-radio-group>
+				</el-form-item>
+				<el-form-item label="鍩�" prop="region">
+					<el-input v-model="form.region" placeholder="璇疯緭鍏ュ煙" />
+				</el-form-item>
+				<el-form-item label="澶囨敞" prop="remark">
+					<el-input v-model="form.remark" type="textarea" placeholder="璇疯緭鍏ュ唴瀹�" />
+				</el-form-item>
+			</el-form>
+			<template #footer>
+				<div class="dialog-footer">
+					<el-button :loading="buttonLoading" type="primary" @click="submitForm">纭� 瀹�</el-button>
+					<el-button @click="cancel">鍙� 娑�</el-button>
+				</div>
+			</template>
+		</el-dialog>
+	</div>
+</template>
diff --git a/src/views/system/oss/index.vue b/src/views/system/oss/index.vue
index f3c1a08..2ffe927 100644
--- a/src/views/system/oss/index.vue
+++ b/src/views/system/oss/index.vue
@@ -1,363 +1,342 @@
-<template>
-  <div class="app-container">
-    <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch">
-      <el-form-item label="鏂囦欢鍚�" prop="fileName">
-        <el-input
-          v-model="queryParams.fileName"
-          placeholder="璇疯緭鍏ユ枃浠跺悕"
-          clearable
-          style="width: 200px"
-          @keyup.enter="handleQuery"
-        />
-      </el-form-item>
-      <el-form-item label="鍘熷悕" prop="originalName">
-        <el-input
-          v-model="queryParams.originalName"
-          placeholder="璇疯緭鍏ュ師鍚�"
-          clearable
-          style="width: 200px"
-          @keyup.enter="handleQuery"
-        />
-      </el-form-item>
-      <el-form-item label="鏂囦欢鍚庣紑" prop="fileSuffix">
-        <el-input
-          v-model="queryParams.fileSuffix"
-          placeholder="璇疯緭鍏ユ枃浠跺悗缂�"
-          clearable
-          style="width: 200px"
-          @keyup.enter="handleQuery"
-        />
-      </el-form-item>
-      <el-form-item label="鍒涘缓鏃堕棿">
-        <el-date-picker
-          v-model="daterangeCreateTime"
-          value-format="YYYY-MM-DD HH:mm:ss"
-          type="daterange"
-          range-separator="-"
-          start-placeholder="寮�濮嬫棩鏈�"
-          end-placeholder="缁撴潫鏃ユ湡"
-          :default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"
-        ></el-date-picker>
-      </el-form-item>
-      <el-form-item label="鏈嶅姟鍟�" prop="service">
-        <el-input
-          v-model="queryParams.service"
-          placeholder="璇疯緭鍏ユ湇鍔″晢"
-          clearable
-          style="width: 200px"
-          @keyup.enter="handleQuery"
-        />
-      </el-form-item>
-      <el-form-item>
-        <el-button type="primary" icon="search" @click="handleQuery">鎼滅储</el-button>
-        <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
-      </el-form-item>
-    </el-form>
-
-    <el-row :gutter="10" class="mb8">
-      <el-col :span="1.5">
-        <el-button
-          type="primary"
-          plain
-          icon="Plus"
-          @click="handleFile"
-          v-hasPermi="['system:oss:upload']"
-        >涓婁紶鏂囦欢</el-button>
-      </el-col>
-      <el-col :span="1.5">
-        <el-button
-          type="primary"
-          plain
-          icon="Plus"
-          @click="handleImage"
-          v-hasPermi="['system:oss:upload']"
-        >涓婁紶鍥剧墖</el-button>
-      </el-col>
-      <el-col :span="1.5">
-        <el-button
-          type="danger"
-          plain
-          icon="Delete"
-          :disabled="multiple"
-          @click="handleDelete"
-          v-hasPermi="['system:oss:remove']"
-        >鍒犻櫎</el-button>
-      </el-col>
-      <el-col :span="1.5">
-        <el-button
-          :type="previewListResource ? 'danger' : 'warning'"
-          plain
-          @click="handlePreviewListResource(!previewListResource)"
-          v-hasPermi="['system:oss:edit']"
-        >棰勮寮�鍏� : {{previewListResource ? "绂佺敤" : "鍚敤"}}</el-button>
-      </el-col>
-      <el-col :span="1.5">
-        <el-button
-          type="info"
-          plain
-          icon="Operation"
-          @click="handleOssConfig"
-          v-hasPermi="['system:oss:list']"
-        >閰嶇疆绠$悊</el-button>
-      </el-col>
-      <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
-    </el-row>
-
-    <el-table v-loading="loading" :data="ossList" @selection-change="handleSelectionChange"
-              :header-cell-class-name="handleHeaderClass"
-              @header-click="handleHeaderCLick"
-              v-if="showTable">
-      <el-table-column type="selection" width="55" align="center" />
-      <el-table-column label="瀵硅薄瀛樺偍涓婚敭" align="center" prop="ossId" v-if="false"/>
-      <el-table-column label="鏂囦欢鍚�" align="center" prop="fileName" />
-      <el-table-column label="鍘熷悕" align="center" prop="originalName" />
-      <el-table-column label="鏂囦欢鍚庣紑" align="center" prop="fileSuffix" />
-      <el-table-column label="鏂囦欢灞曠ず" align="center" prop="url">
-        <template #default="scope">
-          <ImagePreview
-            v-if="previewListResource && checkFileSuffix(scope.row.fileSuffix)"
-            :width="100" :height="100"
-            :src="scope.row.url"
-            :preview-src-list="[scope.row.url]"/>
-          <span v-text="scope.row.url"
-                v-if="!checkFileSuffix(scope.row.fileSuffix) || !previewListResource"/>
-        </template>
-      </el-table-column>
-      <el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime" width="180" sortable="custom">
-        <template #default="scope">
-          <span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>
-        </template>
-      </el-table-column>
-      <el-table-column label="涓婁紶浜�" align="center" prop="createByName" />
-      <el-table-column label="鏈嶅姟鍟�" align="center" prop="service" sortable="custom"/>
-      <el-table-column label="鎿嶄綔" align="center" class-name="small-padding fixed-width">
-        <template #default="scope">
-          <el-button link type="primary" icon="Edit" @click="handleDownload(scope.row)" v-hasPermi="['system:oss:download']">淇敼</el-button>
-          <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:oss:remove']">鍒犻櫎</el-button>
-        </template>
-      </el-table-column>
-    </el-table>
-
-    <pagination
-      v-show="total>0"
-      :total="total"
-      v-model:page="queryParams.pageNum"
-      v-model:limit="queryParams.pageSize"
-      @pagination="getList"
-    />
-
-    <!-- 娣诲姞鎴栦慨鏀筄SS瀵硅薄瀛樺偍瀵硅瘽妗� -->
-    <el-dialog :title="title" v-model="open" width="500px" append-to-body>
-      <el-form ref="ossRef" :model="form" :rules="rules" label-width="80px">
-        <el-form-item label="鏂囦欢鍚�">
-          <fileUpload v-model="form.file" v-if="type === 0"/>
-          <imageUpload v-model="form.file" v-if="type === 1"/>
-        </el-form-item>
-      </el-form>
-      <template #footer>
-        <div class="dialog-footer">
-          <el-button :loading="buttonLoading" type="primary" @click="submitForm">纭� 瀹�</el-button>
-          <el-button @click="cancel">鍙� 娑�</el-button>
-        </div>
-      </template>
-    </el-dialog>
-  </div>
-</template>
-
-<script setup name="Oss">
+<script setup name="Oss" lang="ts">
 import { listOss, delOss } from "@/api/system/oss";
 import ImagePreview from "@/components/ImagePreview/index.vue";
+import { ComponentInternalInstance } from "vue";
+import { OssForm, OssQuery, OssVO } from "@/api/system/oss/types";
+import { DateModelType } from 'element-plus';
 
 const router = useRouter();
-const { proxy } = getCurrentInstance();
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 
-const ossList = ref([]);
-const open = ref(false);
+const ossList = ref<OssVO[]>([]);
 const showTable = ref(true);
 const buttonLoading = ref(false);
 const loading = ref(true);
 const showSearch = ref(true);
-const ids = ref([]);
+const ids = ref<Array<string | number>>([]);
 const single = ref(true);
 const multiple = ref(true);
 const total = ref(0);
-const title = ref("");
 const type = ref(0);
 const previewListResource = ref(true);
-const daterangeCreateTime = ref([]);
-// 榛樿鎺掑簭
-const defaultSort = ref({prop: 'createTime', order: 'ascending'});
+const daterangeCreateTime = ref<[DateModelType, DateModelType]>(['', '']);
 
-const data = reactive({
-  form: {},
-  // 鏌ヨ鍙傛暟
-  queryParams: {
-    pageNum: 1,
-    pageSize: 10,
-    fileName: undefined,
-    originalName: undefined,
-    fileSuffix: undefined,
-    createTime: undefined,
-    service: undefined
-  },
-  rules: {
-    file: [
-      { required: true, message: "鏂囦欢涓嶈兘涓虹┖", trigger: "blur" }
-    ]
-  }
+const dialog = reactive<DialogOption>({
+	visible: false,
+	title: ''
+});
+
+// 榛樿鎺掑簭
+const defaultSort = ref({ prop: 'createTime', order: 'ascending' });
+
+const ossFormRef = ref(ElForm);
+const queryFormRef = ref(ElForm);
+
+const initFormData = {
+	file: undefined,
+}
+const data = reactive<PageData<OssForm, OssQuery>>({
+	form: { ...initFormData },
+	// 鏌ヨ鍙傛暟
+	queryParams: {
+		pageNum: 1,
+		pageSize: 10,
+		fileName: '',
+		originalName: '',
+		fileSuffix: '',
+		createTime: '',
+		service: '',
+		orderByColumn: defaultSort.value.prop,
+		isAsc: defaultSort.value.order
+	},
+	rules: {
+		file: [
+			{ required: true, message: "鏂囦欢涓嶈兘涓虹┖", trigger: "blur" }
+		]
+	}
 });
 
 const { queryParams, form, rules } = toRefs(data);
 
 /** 鏌ヨOSS瀵硅薄瀛樺偍鍒楄〃 */
-function getList() {
-  loading.value = true;
-  proxy.getConfigKey("sys.oss.previewListResource").then(response => {
-    previewListResource.value = response.msg === undefined ? true : response.msg === 'true';
-  });
-  listOss(proxy.addDateRange(queryParams.value, daterangeCreateTime.value, "CreateTime")).then(response => {
-    ossList.value = response.rows;
-    total.value = response.total;
-    loading.value = false;
-    showTable.value = true;
-  });
+const getList = async () => {
+	loading.value = true;
+	const res = await proxy?.getConfigKey("sys.oss.previewListResource");
+	previewListResource.value = res?.msg === undefined ? true : res.msg === 'true';
+	const response = await listOss(proxy?.addDateRange(queryParams.value, daterangeCreateTime.value, "CreateTime"));
+	ossList.value = response.rows;
+	total.value = response.total;
+	loading.value = false;
+	showTable.value = true;
 }
-function checkFileSuffix(fileSuffix) {
-  let arr = ["png", "jpg", "jpeg"];
-  return arr.some(type => {
-    return fileSuffix.indexOf(type) > -1;
-  });
+function checkFileSuffix(fileSuffix: string[]) {
+	let arr = ["png", "jpg", "jpeg"];
+	return arr.some(type => {
+		return fileSuffix.indexOf(type) > -1;
+	});
 }
 /** 鍙栨秷鎸夐挳 */
 function cancel() {
-  open.value = false;
-  reset();
+	dialog.visible = false;
+	reset();
 }
 /** 琛ㄥ崟閲嶇疆 */
 function reset() {
-  form.value = {
-    file: undefined,
-  };
-  proxy.resetForm("ossRef");
+	form.value = { ...initFormData };
+	ossFormRef.value.resetFields();
 }
 /** 鎼滅储鎸夐挳鎿嶄綔 */
 function handleQuery() {
-  queryParams.value.pageNum = 1;
-  getList();
+	queryParams.value.pageNum = 1;
+	getList();
 }
 /** 閲嶇疆鎸夐挳鎿嶄綔 */
 function resetQuery() {
-  showTable.value = false;
-  daterangeCreateTime.value = [];
-  proxy.resetForm("queryRef");
-  queryParams.value.orderByColumn = defaultSort.value.prop;
-  queryParams.value.isAsc = defaultSort.value.order;
-  handleQuery();
+	showTable.value = false;
+	daterangeCreateTime.value = ['', ''];
+	queryFormRef.value.resetFields();
+	queryParams.value.orderByColumn = defaultSort.value.prop;
+	queryParams.value.isAsc = defaultSort.value.order;
+	handleQuery();
 }
 /** 閫夋嫨鏉℃暟  */
-function handleSelectionChange(selection) {
-  ids.value = selection.map(item => item.ossId);
-  single.value = selection.length != 1;
-  multiple.value = !selection.length;
+function handleSelectionChange(selection: OssVO[]) {
+	ids.value = selection.map(item => item.ossId);
+	single.value = selection.length != 1;
+	multiple.value = !selection.length;
 }
-// 璁剧疆鍒楃殑鎺掑簭涓烘垜浠嚜瀹氫箟鐨勬帓搴�
-function handleHeaderClass({column}) {
-  column.order = column.multiOrder
+/** 璁剧疆鍒楃殑鎺掑簭涓烘垜浠嚜瀹氫箟鐨勬帓搴� */
+const handleHeaderClass = ({ column }: any): any => {
+	column.order = column.multiOrder
 }
-// 鐐瑰嚮琛ㄥご杩涜鎺掑簭
-function handleHeaderCLick(column) {
-  if (column.sortable !== 'custom') {
-    return
-  }
-  switch (column.multiOrder) {
-    case 'descending':
-      column.multiOrder = 'ascending';
-      break;
-    case 'ascending':
-      column.multiOrder = '';
-      break;
-    default:
-      column.multiOrder = 'descending';
-      break;
-  }
-  handleOrderChange(column.property, column.multiOrder)
+/** 鐐瑰嚮琛ㄥご杩涜鎺掑簭 */
+const handleHeaderCLick = (column: any) => {
+	if (column.sortable !== 'custom') {
+		return
+	}
+	switch (column.multiOrder) {
+		case 'descending':
+			column.multiOrder = 'ascending';
+			break;
+		case 'ascending':
+			column.multiOrder = '';
+			break;
+		default:
+			column.multiOrder = 'descending';
+			break;
+	}
+	handleOrderChange(column.property, column.multiOrder)
 }
-function handleOrderChange(prop, order) {
-  let orderByArr = queryParams.value.orderByColumn ? queryParams.value.orderByColumn.split(",") : [];
-  let isAscArr = queryParams.value.isAsc ? queryParams.value.isAsc.split(",") : [];
-  let propIndex = orderByArr.indexOf(prop)
-  if (propIndex !== -1) {
-    if (order) {
-      //鎺掑簭閲屽凡瀛樺湪 鍙慨鏀规帓搴�
-      isAscArr[propIndex] = order;
-    } else {
-      //濡傛灉order涓簄ull 鍒欏垹闄ゆ帓搴忓瓧娈靛拰灞炴��
-      isAscArr.splice(propIndex, 1);//鍒犻櫎鎺掑簭
-      orderByArr.splice(propIndex, 1);//鍒犻櫎灞炴��
-    }
-  } else {
-    //鎺掑簭閲屼笉瀛樺湪鍒欐柊澧炴帓搴�
-    orderByArr.push(prop);
-    isAscArr.push(order);
-  }
-  //鍚堝苟鎺掑簭
-  queryParams.value.orderByColumn = orderByArr.join(",");
-  queryParams.value.isAsc = isAscArr.join(",");
-  getList();
+const handleOrderChange = (prop: string, order: string) => {
+	let orderByArr = queryParams.value.orderByColumn ? queryParams.value.orderByColumn.split(",") : [];
+	let isAscArr = queryParams.value.isAsc ? queryParams.value.isAsc.split(",") : [];
+	let propIndex = orderByArr.indexOf(prop)
+	if (propIndex !== -1) {
+		if (order) {
+			//鎺掑簭閲屽凡瀛樺湪 鍙慨鏀规帓搴�
+			isAscArr[propIndex] = order;
+		} else {
+			//濡傛灉order涓簄ull 鍒欏垹闄ゆ帓搴忓瓧娈靛拰灞炴��
+			isAscArr.splice(propIndex, 1);//鍒犻櫎鎺掑簭
+			orderByArr.splice(propIndex, 1);//鍒犻櫎灞炴��
+		}
+	} else {
+		//鎺掑簭閲屼笉瀛樺湪鍒欐柊澧炴帓搴�
+		orderByArr.push(prop);
+		isAscArr.push(order);
+	}
+	//鍚堝苟鎺掑簭
+	queryParams.value.orderByColumn = orderByArr.join(",");
+	queryParams.value.isAsc = isAscArr.join(",");
+	getList();
 }
 /** 浠诲姟鏃ュ織鍒楄〃鏌ヨ */
-function handleOssConfig() {
-  router.push('/system/oss-config/index')
+const handleOssConfig = () => {
+	router.push('/system/oss-config/index')
 }
 /** 鏂囦欢鎸夐挳鎿嶄綔 */
-function handleFile() {
-  reset();
-  open.value = true;
-  title.value = "涓婁紶鏂囦欢";
-  type.value = 0;
+const handleFile = () => {
+	dialog.visible = true;
+	dialog.title = "涓婁紶鏂囦欢";
+	nextTick(() => {
+		reset();
+		type.value = 0;
+	})
 }
 /** 鍥剧墖鎸夐挳鎿嶄綔 */
-function handleImage() {
-  reset();
-  open.value = true;
-  title.value = "涓婁紶鍥剧墖";
-  type.value = 1;
+const handleImage = () => {
+	dialog.visible = true;
+	dialog.title = "涓婁紶鍥剧墖";
+	nextTick(() => {
+		reset();
+		type.value = 1;
+	})
 }
 /** 鎻愪氦鎸夐挳 */
-function submitForm() {
-  open.value = false;
-  getList();
+const submitForm = () => {
+	dialog.visible = false;
+	getList();
 }
 /** 涓嬭浇鎸夐挳鎿嶄綔 */
-function handleDownload(row) {
-  proxy.$download.oss(row.ossId)
+const handleDownload = (row: OssVO) => {
+	proxy?.$download.oss(row.ossId)
 }
 /** 鐢ㄦ埛鐘舵�佷慨鏀�  */
-function handlePreviewListResource(previewListResource) {
-  let text = previewListResource ? "鍚敤" : "鍋滅敤";
-  proxy.$modal.confirm('纭瑕�"' + text + '""棰勮鍒楄〃鍥剧墖"閰嶇疆鍚�?').then(() => {
-    return proxy.updateConfigByKey("sys.oss.previewListResource", previewListResource);
-  }).then(() => {
-    getList()
-    proxy.$modal.msgSuccess(text + "鎴愬姛");
-  }).catch(function () {
-    previewListResource.value = previewListResource.value !== true;
-  });
+const handlePreviewListResource = async (preview: boolean) => {
+	let text = preview ? "鍚敤" : "鍋滅敤";
+	try {
+		await proxy?.$modal.confirm('纭瑕�"' + text + '""棰勮鍒楄〃鍥剧墖"閰嶇疆鍚�?');
+		await proxy?.updateConfigByKey("sys.oss.previewListResource", preview);
+		getList()
+		proxy?.$modal.msgSuccess(text + "鎴愬姛");
+	} catch {
+		previewListResource.value = previewListResource.value !== true;
+	}
+
+
 }
 /** 鍒犻櫎鎸夐挳鎿嶄綔 */
-function handleDelete(row) {
-  const ossIds = row.ossId || ids.value;
-  proxy.$modal.confirm('鏄惁纭鍒犻櫎OSS瀵硅薄瀛樺偍缂栧彿涓�"' + ossIds + '"鐨勬暟鎹」?').then(() => {
-    loading.value = true;
-    return delOss(ossIds);
-  }).then(() => {
-    loading.value = false;
-    getList();
-    proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
-  }).finally(() => {
-    loading.value = false;
-  });
+const handleDelete = async (row?: OssVO) => {
+	const ossIds = row?.ossId || ids.value;
+	await proxy?.$modal.confirm('鏄惁纭鍒犻櫎OSS瀵硅薄瀛樺偍缂栧彿涓�"' + ossIds + '"鐨勬暟鎹」?');
+	loading.value = true;
+	await delOss(ossIds).finally(() => loading.value = false);
+	getList();
+	proxy?.$modal.msgSuccess("鍒犻櫎鎴愬姛");
 }
-
-getList();
+onMounted(() => {
+	getList();
+})
 </script>
+
+<template>
+	<div class="p-2">
+		<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
+			<div class="search" v-show="showSearch">
+				<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
+					<el-form-item label="鏂囦欢鍚�" prop="fileName">
+						<el-input v-model="queryParams.fileName" placeholder="璇疯緭鍏ユ枃浠跺悕" clearable style="width: 200px" @keyup.enter="handleQuery" />
+					</el-form-item>
+					<el-form-item label="鍘熷悕" prop="originalName">
+						<el-input v-model="queryParams.originalName" placeholder="璇疯緭鍏ュ師鍚�" clearable style="width: 200px" @keyup.enter="handleQuery" />
+					</el-form-item>
+					<el-form-item label="鏂囦欢鍚庣紑" prop="fileSuffix">
+						<el-input v-model="queryParams.fileSuffix" placeholder="璇疯緭鍏ユ枃浠跺悗缂�" clearable style="width: 200px" @keyup.enter="handleQuery" />
+					</el-form-item>
+					<el-form-item label="鍒涘缓鏃堕棿">
+						<el-date-picker
+							v-model="daterangeCreateTime"
+							value-format="YYYY-MM-DD HH:mm:ss"
+							type="daterange"
+							range-separator="-"
+							start-placeholder="寮�濮嬫棩鏈�"
+							end-placeholder="缁撴潫鏃ユ湡"
+							:default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"
+						></el-date-picker>
+					</el-form-item>
+					<el-form-item label="鏈嶅姟鍟�" prop="service">
+						<el-input v-model="queryParams.service" placeholder="璇疯緭鍏ユ湇鍔″晢" clearable style="width: 200px" @keyup.enter="handleQuery" />
+					</el-form-item>
+					<el-form-item>
+						<el-button type="primary" icon="search" @click="handleQuery">鎼滅储</el-button>
+						<el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
+					</el-form-item>
+				</el-form>
+			</div>
+		</transition>
+
+		<el-card shadow="never">
+			<template #header>
+				<el-row :gutter="10" class="mb8">
+					<el-col :span="1.5">
+						<el-button type="primary" plain icon="Upload" @click="handleFile" v-hasPermi="['system:oss:upload']">涓婁紶鏂囦欢</el-button>
+					</el-col>
+					<el-col :span="1.5">
+						<el-button type="primary" plain icon="Upload" @click="handleImage" v-hasPermi="['system:oss:upload']">涓婁紶鍥剧墖</el-button>
+					</el-col>
+					<el-col :span="1.5">
+						<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['system:oss:remove']">
+							鍒犻櫎
+						</el-button>
+					</el-col>
+					<el-col :span="1.5">
+						<el-button
+							:type="previewListResource ? 'danger' : 'warning'"
+							plain
+							@click="handlePreviewListResource(!previewListResource)"
+							v-hasPermi="['system:oss:edit']"
+							>棰勮寮�鍏� :
+							{{
+								previewListResource ? "绂佺敤" : "鍚敤" }}</el-button
+						>
+					</el-col>
+					<el-col :span="1.5">
+						<el-button type="info" plain icon="Operation" @click="handleOssConfig" v-hasPermi="['system:oss:list']">閰嶇疆绠$悊</el-button>
+					</el-col>
+					<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+				</el-row>
+			</template>
+
+			<el-table
+				v-loading="loading"
+				:data="ossList"
+				@selection-change="handleSelectionChange"
+				:header-cell-class-name="handleHeaderClass"
+				@header-click="handleHeaderCLick"
+				v-if="showTable"
+			>
+				<el-table-column type="selection" width="55" align="center" />
+				<el-table-column label="瀵硅薄瀛樺偍涓婚敭" align="center" prop="ossId" v-if="false" />
+				<el-table-column label="鏂囦欢鍚�" align="center" prop="fileName" />
+				<el-table-column label="鍘熷悕" align="center" prop="originalName" />
+				<el-table-column label="鏂囦欢鍚庣紑" align="center" prop="fileSuffix" />
+				<el-table-column label="鏂囦欢灞曠ず" align="center" prop="url">
+					<template #default="scope">
+						<ImagePreview
+							v-if="previewListResource && checkFileSuffix(scope.row.fileSuffix)"
+							:width="100"
+							:height="100"
+							:src="scope.row.url"
+							:preview-src-list="[scope.row.url]"
+						/>
+						<span v-text="scope.row.url" v-if="!checkFileSuffix(scope.row.fileSuffix) || !previewListResource" />
+					</template>
+				</el-table-column>
+				<el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime" width="180" sortable="custom">
+					<template #default="scope">
+						<span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>
+					</template>
+				</el-table-column>
+				<el-table-column label="涓婁紶浜�" align="center" prop="createByName" />
+				<el-table-column label="鏈嶅姟鍟�" align="center" prop="service" sortable="custom" />
+				<el-table-column label="鎿嶄綔" align="center" class-name="small-padding fixed-width">
+					<template #default="scope">
+						<el-tooltip content="涓嬭浇" placement="top">
+							<el-button link type="primary" icon="Download" @click="handleDownload(scope.row)" v-hasPermi="['system:oss:download']"></el-button>
+						</el-tooltip>
+						<el-tooltip content="鍒犻櫎" placement="top">
+							<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:oss:remove']"></el-button>
+						</el-tooltip>
+					</template>
+				</el-table-column>
+			</el-table>
+
+			<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
+		</el-card>
+		<!-- 娣诲姞鎴栦慨鏀筄SS瀵硅薄瀛樺偍瀵硅瘽妗� -->
+		<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
+			<el-form ref="ossFormRef" :model="form" :rules="rules" label-width="80px">
+				<el-form-item label="鏂囦欢鍚�">
+					<fileUpload v-model="form.file" v-if="type === 0" />
+					<imageUpload v-model="form.file" v-if="type === 1" />
+				</el-form-item>
+			</el-form>
+			<template #footer>
+				<div class="dialog-footer">
+					<el-button :loading="buttonLoading" type="primary" @click="submitForm">纭� 瀹�</el-button>
+					<el-button @click="cancel">鍙� 娑�</el-button>
+				</div>
+			</template>
+		</el-dialog>
+	</div>
+</template>
diff --git a/src/views/system/post/index.vue b/src/views/system/post/index.vue
index e40fbea..666764b 100644
--- a/src/views/system/post/index.vue
+++ b/src/views/system/post/index.vue
@@ -1,277 +1,240 @@
-<template>
-   <div class="app-container">
-      <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch">
-         <el-form-item label="宀椾綅缂栫爜" prop="postCode">
-            <el-input
-               v-model="queryParams.postCode"
-               placeholder="璇疯緭鍏ュ矖浣嶇紪鐮�"
-               clearable
-               style="width: 200px"
-               @keyup.enter="handleQuery"
-            />
-         </el-form-item>
-         <el-form-item label="宀椾綅鍚嶇О" prop="postName">
-            <el-input
-               v-model="queryParams.postName"
-               placeholder="璇疯緭鍏ュ矖浣嶅悕绉�"
-               clearable
-               style="width: 200px"
-               @keyup.enter="handleQuery"
-            />
-         </el-form-item>
-         <el-form-item label="鐘舵��" prop="status">
-            <el-select v-model="queryParams.status" placeholder="宀椾綅鐘舵��" clearable style="width: 200px">
-               <el-option
-                  v-for="dict in sys_normal_disable"
-                  :key="dict.value"
-                  :label="dict.label"
-                  :value="dict.value"
-               />
-            </el-select>
-         </el-form-item>
-         <el-form-item>
-            <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
-            <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
-         </el-form-item>
-      </el-form>
-
-      <el-row :gutter="10" class="mb8">
-         <el-col :span="1.5">
-            <el-button
-               type="primary"
-               plain
-               icon="Plus"
-               @click="handleAdd"
-               v-hasPermi="['system:post:add']"
-            >鏂板</el-button>
-         </el-col>
-         <el-col :span="1.5">
-            <el-button
-               type="success"
-               plain
-               icon="Edit"
-               :disabled="single"
-               @click="handleUpdate"
-               v-hasPermi="['system:post:edit']"
-            >淇敼</el-button>
-         </el-col>
-         <el-col :span="1.5">
-            <el-button
-               type="danger"
-               plain
-               icon="Delete"
-               :disabled="multiple"
-               @click="handleDelete"
-               v-hasPermi="['system:post:remove']"
-            >鍒犻櫎</el-button>
-         </el-col>
-         <el-col :span="1.5">
-            <el-button
-               type="warning"
-               plain
-               icon="Download"
-               @click="handleExport"
-               v-hasPermi="['system:post:export']"
-            >瀵煎嚭</el-button>
-         </el-col>
-         <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
-      </el-row>
-
-      <el-table v-loading="loading" :data="postList" @selection-change="handleSelectionChange">
-         <el-table-column type="selection" width="55" align="center" />
-         <el-table-column label="宀椾綅缂栧彿" align="center" prop="postId" v-if="false" />
-         <el-table-column label="宀椾綅缂栫爜" align="center" prop="postCode" />
-         <el-table-column label="宀椾綅鍚嶇О" align="center" prop="postName" />
-         <el-table-column label="宀椾綅鎺掑簭" align="center" prop="postSort" />
-         <el-table-column label="鐘舵��" align="center" prop="status">
-            <template #default="scope">
-               <dict-tag :options="sys_normal_disable" :value="scope.row.status" />
-            </template>
-         </el-table-column>
-         <el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime" width="180">
-            <template #default="scope">
-               <span>{{ parseTime(scope.row.createTime) }}</span>
-            </template>
-         </el-table-column>
-         <el-table-column label="鎿嶄綔" width="180" align="center" class-name="small-padding fixed-width">
-            <template #default="scope">
-               <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:post:edit']">淇敼</el-button>
-               <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:post:remove']">鍒犻櫎</el-button>
-            </template>
-         </el-table-column>
-      </el-table>
-
-      <pagination
-         v-show="total > 0"
-         :total="total"
-         v-model:page="queryParams.pageNum"
-         v-model:limit="queryParams.pageSize"
-         @pagination="getList"
-      />
-
-      <!-- 娣诲姞鎴栦慨鏀瑰矖浣嶅璇濇 -->
-      <el-dialog :title="title" v-model="open" width="500px" append-to-body>
-         <el-form ref="postRef" :model="form" :rules="rules" label-width="80px">
-            <el-form-item label="宀椾綅鍚嶇О" prop="postName">
-               <el-input v-model="form.postName" placeholder="璇疯緭鍏ュ矖浣嶅悕绉�" />
-            </el-form-item>
-            <el-form-item label="宀椾綅缂栫爜" prop="postCode">
-               <el-input v-model="form.postCode" placeholder="璇疯緭鍏ョ紪鐮佸悕绉�" />
-            </el-form-item>
-            <el-form-item label="宀椾綅椤哄簭" prop="postSort">
-               <el-input-number v-model="form.postSort" controls-position="right" :min="0" />
-            </el-form-item>
-            <el-form-item label="宀椾綅鐘舵��" prop="status">
-               <el-radio-group v-model="form.status">
-                  <el-radio
-                     v-for="dict in sys_normal_disable"
-                     :key="dict.value"
-                     :label="dict.value"
-                  >{{ dict.label }}</el-radio>
-               </el-radio-group>
-            </el-form-item>
-            <el-form-item label="澶囨敞" prop="remark">
-               <el-input v-model="form.remark" type="textarea" placeholder="璇疯緭鍏ュ唴瀹�" />
-            </el-form-item>
-         </el-form>
-         <template #footer>
-            <div class="dialog-footer">
-               <el-button type="primary" @click="submitForm">纭� 瀹�</el-button>
-               <el-button @click="cancel">鍙� 娑�</el-button>
-            </div>
-         </template>
-      </el-dialog>
-   </div>
-</template>
-
-<script setup name="Post">
+<script setup name="Post" lang="ts">
 import { listPost, addPost, delPost, getPost, updatePost } from "@/api/system/post";
+import { PostForm, PostQuery, PostVO } from "@/api/system/post/types";
+import { ComponentInternalInstance } from "vue";
 
-const { proxy } = getCurrentInstance();
-const { sys_normal_disable } = proxy.useDict("sys_normal_disable");
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { sys_normal_disable } = toRefs<any>(proxy?.useDict("sys_normal_disable"));
 
-const postList = ref([]);
-const open = ref(false);
+const postList = ref<PostVO[]>([]);
 const loading = ref(true);
 const showSearch = ref(true);
-const ids = ref([]);
+const ids = ref<Array<number | string>>([]);
 const single = ref(true);
 const multiple = ref(true);
 const total = ref(0);
-const title = ref("");
 
-const data = reactive({
-  form: {},
-  queryParams: {
-    pageNum: 1,
-    pageSize: 10,
-    postCode: undefined,
-    postName: undefined,
-    status: undefined
-  },
-  rules: {
-    postName: [{ required: true, message: "宀椾綅鍚嶇О涓嶈兘涓虹┖", trigger: "blur" }],
-    postCode: [{ required: true, message: "宀椾綅缂栫爜涓嶈兘涓虹┖", trigger: "blur" }],
-    postSort: [{ required: true, message: "宀椾綅椤哄簭涓嶈兘涓虹┖", trigger: "blur" }],
-  }
+const postFormRef = ref(ElForm);
+const queryFormRef = ref(ElForm);
+
+const dialog = reactive<DialogOption>({
+	visible: false,
+	title: ''
 });
 
-const { queryParams, form, rules } = toRefs(data);
+const initFormData: PostForm = {
+	postId: undefined,
+	postCode: '',
+	postName: '',
+	postSort: 0,
+	status: "0",
+	remark: ''
+}
+
+const data = reactive<PageData<PostForm, PostQuery>>({
+	form: {...initFormData},
+	queryParams: {
+		pageNum: 1,
+		pageSize: 10,
+		postCode: '',
+		postName: '',
+		status: ''
+	},
+	rules: {
+		postName: [{ required: true, message: "宀椾綅鍚嶇О涓嶈兘涓虹┖", trigger: "blur" }],
+		postCode: [{ required: true, message: "宀椾綅缂栫爜涓嶈兘涓虹┖", trigger: "blur" }],
+		postSort: [{ required: true, message: "宀椾綅椤哄簭涓嶈兘涓虹┖", trigger: "blur" }],
+	}
+});
+
+const { queryParams, form, rules } = toRefs<PageData<PostForm, PostQuery>>(data);
 
 /** 鏌ヨ宀椾綅鍒楄〃 */
-function getList() {
-  loading.value = true;
-  listPost(queryParams.value).then(response => {
-    postList.value = response.rows;
-    total.value = response.total;
-    loading.value = false;
-  });
+const getList = async () => {
+	loading.value = true;
+	const res = await listPost(queryParams.value);
+	postList.value = res.rows;
+	total.value = res.total;
+	loading.value = false;
 }
 /** 鍙栨秷鎸夐挳 */
-function cancel() {
-  open.value = false;
-  reset();
+const cancel = () => {
+	reset();
+	dialog.visible = false;
 }
 /** 琛ㄥ崟閲嶇疆 */
-function reset() {
-  form.value = {
-    postId: undefined,
-    postCode: undefined,
-    postName: undefined,
-    postSort: 0,
-    status: "0",
-    remark: undefined
-  };
-  proxy.resetForm("postRef");
+const reset = () => {
+	form.value = {...initFormData};
+	postFormRef.value.resetFields();
 }
 /** 鎼滅储鎸夐挳鎿嶄綔 */
-function handleQuery() {
-  queryParams.value.pageNum = 1;
-  getList();
+const handleQuery = () => {
+	queryParams.value.pageNum = 1;
+	getList();
 }
 /** 閲嶇疆鎸夐挳鎿嶄綔 */
-function resetQuery() {
-  proxy.resetForm("queryRef");
-  handleQuery();
+const resetQuery = () => {
+	queryFormRef.value.resetFields();
+	handleQuery();
 }
 /** 澶氶�夋閫変腑鏁版嵁 */
-function handleSelectionChange(selection) {
-  ids.value = selection.map(item => item.postId);
-  single.value = selection.length != 1;
-  multiple.value = !selection.length;
+const handleSelectionChange = (selection: PostVO[]) => {
+	ids.value = selection.map(item => item.postId);
+	single.value = selection.length != 1;
+	multiple.value = !selection.length;
 }
 /** 鏂板鎸夐挳鎿嶄綔 */
-function handleAdd() {
-  reset();
-  open.value = true;
-  title.value = "娣诲姞宀椾綅";
+const handleAdd = () => {
+	dialog.visible = true;
+	dialog.title = "娣诲姞宀椾綅";
+	nextTick(() => {
+		reset();
+	})
 }
 /** 淇敼鎸夐挳鎿嶄綔 */
-function handleUpdate(row) {
-  reset();
-  const postId = row.postId || ids.value;
-  getPost(postId).then(response => {
-    form.value = response.data;
-    open.value = true;
-    title.value = "淇敼宀椾綅";
-  });
+const handleUpdate = (row?: PostVO) => {
+	dialog.visible = true;
+	dialog.title = "淇敼宀椾綅";
+	nextTick(async () => {
+		reset();
+		const postId = row?.postId || ids.value[0];
+		const res = await getPost(postId);
+		form.value = res.data;
+	})
 }
 /** 鎻愪氦鎸夐挳 */
-function submitForm() {
-  proxy.$refs["postRef"].validate(valid => {
-    if (valid) {
-      if (form.value.postId != undefined) {
-        updatePost(form.value).then(response => {
-          proxy.$modal.msgSuccess("淇敼鎴愬姛");
-          open.value = false;
-          getList();
-        });
-      } else {
-        addPost(form.value).then(response => {
-          proxy.$modal.msgSuccess("鏂板鎴愬姛");
-          open.value = false;
-          getList();
-        });
-      }
-    }
-  });
+const submitForm = () => {
+	postFormRef.value.validate(async (valid: boolean) => {
+		if (valid) {
+			form.value.postId ? await updatePost(form.value) : await addPost(form.value);
+			proxy?.$modal.msgSuccess("鎿嶄綔鎴愬姛");
+			dialog.visible = false;
+			getList();
+		}
+	});
 }
 /** 鍒犻櫎鎸夐挳鎿嶄綔 */
-function handleDelete(row) {
-  const postIds = row.postId || ids.value;
-  proxy.$modal.confirm('鏄惁纭鍒犻櫎宀椾綅缂栧彿涓�"' + postIds + '"鐨勬暟鎹」锛�').then(function() {
-    return delPost(postIds);
-  }).then(() => {
-    getList();
-    proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
-  }).catch(() => {});
+const handleDelete = async (row?: PostVO) => {
+	const postIds = row?.postId || ids.value;
+	await proxy?.$modal.confirm('鏄惁纭鍒犻櫎宀椾綅缂栧彿涓�"' + postIds + '"鐨勬暟鎹」锛�');
+	await delPost(postIds);
+	getList();
+	proxy?.$modal.msgSuccess("鍒犻櫎鎴愬姛");
 }
 /** 瀵煎嚭鎸夐挳鎿嶄綔 */
-function handleExport() {
-  proxy.download("system/post/export", {
-    ...queryParams.value
-  }, `post_${new Date().getTime()}.xlsx`);
+const handleExport = () => {
+	proxy?.download("system/post/export", {
+		...queryParams.value
+	}, `post_${new Date().getTime()}.xlsx`);
 }
 
-getList();
+onMounted(() => {
+	getList();
+});
 </script>
+
+<template>
+	<div class="p-2">
+		<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
+			<div class="search" v-show="showSearch">
+				<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="70">
+					<el-form-item label="宀椾綅缂栫爜" prop="postCode">
+						<el-input v-model="queryParams.postCode" placeholder="璇疯緭鍏ュ矖浣嶇紪鐮�" clearable style="width: 200px" @keyup.enter="handleQuery" />
+					</el-form-item>
+					<el-form-item label="宀椾綅鍚嶇О" prop="postName">
+						<el-input v-model="queryParams.postName" placeholder="璇疯緭鍏ュ矖浣嶅悕绉�" clearable style="width: 200px" @keyup.enter="handleQuery" />
+					</el-form-item>
+					<el-form-item label="鐘舵��" prop="status">
+						<el-select v-model="queryParams.status" placeholder="宀椾綅鐘舵��" clearable style="width: 200px">
+							<el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" />
+						</el-select>
+					</el-form-item>
+					<el-form-item>
+						<el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
+						<el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
+					</el-form-item>
+				</el-form>
+			</div>
+		</transition>
+		<el-card shadow="never">
+			<template #header>
+				<el-row :gutter="10" class="mb8">
+					<el-col :span="1.5">
+						<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:post:add']">鏂板</el-button>
+					</el-col>
+					<el-col :span="1.5">
+						<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['system:post:edit']">淇敼</el-button>
+					</el-col>
+					<el-col :span="1.5">
+						<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['system:post:remove']">
+							鍒犻櫎
+						</el-button>
+					</el-col>
+					<el-col :span="1.5">
+						<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['system:post:export']">瀵煎嚭</el-button>
+					</el-col>
+					<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+				</el-row>
+			</template>
+
+			<el-table v-loading="loading" :data="postList" @selection-change="handleSelectionChange">
+				<el-table-column type="selection" width="55" align="center" />
+				<el-table-column label="宀椾綅缂栧彿" align="center" prop="postId" v-if="false" />
+				<el-table-column label="宀椾綅缂栫爜" align="center" prop="postCode" />
+				<el-table-column label="宀椾綅鍚嶇О" align="center" prop="postName" />
+				<el-table-column label="宀椾綅鎺掑簭" align="center" prop="postSort" />
+				<el-table-column label="鐘舵��" align="center" prop="status">
+					<template #default="scope">
+						<dict-tag :options="sys_normal_disable" :value="scope.row.status" />
+					</template>
+				</el-table-column>
+				<el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime" width="180">
+					<template #default="scope">
+						<span>{{ parseTime(scope.row.createTime) }}</span>
+					</template>
+				</el-table-column>
+				<el-table-column label="鎿嶄綔" width="180" align="center" class-name="small-padding fixed-width">
+					<template #default="scope">
+						<el-tooltip content="淇敼" placement="top">
+							<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:post:edit']"></el-button>
+						</el-tooltip>
+						<el-tooltip content="鍒犻櫎" placement="top">
+							<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:post:remove']"></el-button>
+						</el-tooltip>
+					</template>
+				</el-table-column>
+			</el-table>
+
+			<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
+		</el-card>
+
+		<!-- 娣诲姞鎴栦慨鏀瑰矖浣嶅璇濇 -->
+		<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
+			<el-form ref="postFormRef" :model="form" :rules="rules" label-width="80px">
+				<el-form-item label="宀椾綅鍚嶇О" prop="postName">
+					<el-input v-model="form.postName" placeholder="璇疯緭鍏ュ矖浣嶅悕绉�" />
+				</el-form-item>
+				<el-form-item label="宀椾綅缂栫爜" prop="postCode">
+					<el-input v-model="form.postCode" placeholder="璇疯緭鍏ョ紪鐮佸悕绉�" />
+				</el-form-item>
+				<el-form-item label="宀椾綅椤哄簭" prop="postSort">
+					<el-input-number v-model="form.postSort" controls-position="right" :min="0" />
+				</el-form-item>
+				<el-form-item label="宀椾綅鐘舵��" prop="status">
+					<el-radio-group v-model="form.status">
+						<el-radio v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.value">{{ dict.label }}</el-radio>
+					</el-radio-group>
+				</el-form-item>
+				<el-form-item label="澶囨敞" prop="remark">
+					<el-input v-model="form.remark" type="textarea" placeholder="璇疯緭鍏ュ唴瀹�" />
+				</el-form-item>
+			</el-form>
+			<template #footer>
+				<div class="dialog-footer">
+					<el-button type="primary" @click="submitForm">纭� 瀹�</el-button>
+					<el-button @click="cancel">鍙� 娑�</el-button>
+				</div>
+			</template>
+		</el-dialog>
+	</div>
+</template>
diff --git a/src/views/system/role/authUser.vue b/src/views/system/role/authUser.vue
index 66b5f5e..a9e2225 100644
--- a/src/views/system/role/authUser.vue
+++ b/src/views/system/role/authUser.vue
@@ -1,172 +1,158 @@
-
-<template>
-   <div class="app-container">
-      <el-form :model="queryParams" ref="queryRef" v-show="showSearch" :inline="true">
-         <el-form-item label="鐢ㄦ埛鍚嶇О" prop="userName">
-            <el-input
-               v-model="queryParams.userName"
-               placeholder="璇疯緭鍏ョ敤鎴峰悕绉�"
-               clearable
-               style="width: 240px"
-               @keyup.enter="handleQuery"
-            />
-         </el-form-item>
-         <el-form-item label="鎵嬫満鍙风爜" prop="phonenumber">
-            <el-input
-               v-model="queryParams.phonenumber"
-               placeholder="璇疯緭鍏ユ墜鏈哄彿鐮�"
-               clearable
-               style="width: 240px"
-               @keyup.enter="handleQuery"
-            />
-         </el-form-item>
-         <el-form-item>
-            <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
-            <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
-         </el-form-item>
-      </el-form>
-
-      <el-row :gutter="10" class="mb8">
-         <el-col :span="1.5">
-            <el-button
-               type="primary"
-               plain
-               icon="Plus"
-               @click="openSelectUser"
-               v-hasPermi="['system:role:add']"
-            >娣诲姞鐢ㄦ埛</el-button>
-         </el-col>
-         <el-col :span="1.5">
-            <el-button
-               type="danger"
-               plain
-               icon="CircleClose"
-               :disabled="multiple"
-               @click="cancelAuthUserAll"
-               v-hasPermi="['system:role:remove']"
-            >鎵归噺鍙栨秷鎺堟潈</el-button>
-         </el-col>
-         <el-col :span="1.5">
-            <el-button 
-               type="warning" 
-               plain 
-               icon="Close"
-               @click="handleClose"
-            >鍏抽棴</el-button>
-         </el-col>
-         <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
-      </el-row>
-
-      <el-table v-loading="loading" :data="userList" @selection-change="handleSelectionChange">
-         <el-table-column type="selection" width="55" align="center" />
-         <el-table-column label="鐢ㄦ埛鍚嶇О" prop="userName" :show-overflow-tooltip="true" />
-         <el-table-column label="鐢ㄦ埛鏄电О" prop="nickName" :show-overflow-tooltip="true" />
-         <el-table-column label="閭" prop="email" :show-overflow-tooltip="true" />
-         <el-table-column label="鎵嬫満" prop="phonenumber" :show-overflow-tooltip="true" />
-         <el-table-column label="鐘舵��" align="center" prop="status">
-            <template #default="scope">
-               <dict-tag :options="sys_normal_disable" :value="scope.row.status" />
-            </template>
-         </el-table-column>
-         <el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime" width="180">
-            <template #default="scope">
-               <span>{{ parseTime(scope.row.createTime) }}</span>
-            </template>
-         </el-table-column>
-         <el-table-column label="鎿嶄綔" align="center" class-name="small-padding fixed-width">
-            <template #default="scope">
-               <el-button link type="primary" icon="CircleClose" @click="cancelAuthUser(scope.row)" v-hasPermi="['system:role:remove']">鍙栨秷鎺堟潈</el-button>
-            </template>
-         </el-table-column>
-      </el-table>
-
-      <pagination
-         v-show="total > 0"
-         :total="total"
-         v-model:page="queryParams.pageNum"
-         v-model:limit="queryParams.pageSize"
-         @pagination="getList"
-      />
-      <select-user ref="selectRef" :roleId="queryParams.roleId" @ok="handleQuery" />
-   </div>
-</template>
-
-<script setup name="AuthUser">
-import selectUser from "./selectUser";
+<script setup name="AuthUser" lang="ts">
 import { allocatedUserList, authUserCancel, authUserCancelAll } from "@/api/system/role";
+import { UserQuery } from "@/api/system/user/types";
+import { ComponentInternalInstance } from "vue";
+import { UserVO } from "@/api/system/user/types";
+import SelectUser from "./selectUser.vue";
+// import { ElForm, ElSelect} from 'element-plus';
+
 
 const route = useRoute();
-const { proxy } = getCurrentInstance();
-const { sys_normal_disable } = proxy.useDict("sys_normal_disable");
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { sys_normal_disable } = toRefs<any>(proxy?.useDict("sys_normal_disable"));
 
-const userList = ref([]);
+const userList = ref<UserVO[]>([]);
 const loading = ref(true);
 const showSearch = ref(true);
 const multiple = ref(true);
 const total = ref(0);
-const userIds = ref([]);
+const userIds = ref<Array<string | number>>([]);
 
-const queryParams = reactive({
+const queryFormRef = ref(ElForm);
+const selectRef = ref(SelectUser);
+
+const queryParams = reactive<UserQuery>({
   pageNum: 1,
   pageSize: 10,
-  roleId: route.params.roleId,
+  roleId: route.params.roleId as string,
   userName: undefined,
   phonenumber: undefined,
 });
 
 /** 鏌ヨ鎺堟潈鐢ㄦ埛鍒楄〃 */
-function getList() {
+const getList = async () => {
   loading.value = true;
-  allocatedUserList(queryParams).then(response => {
-    userList.value = response.rows;
-    total.value = response.total;
-    loading.value = false;
-  });
+	const res = await allocatedUserList(queryParams);
+	userList.value = res.rows;
+	total.value = res.total;
+	loading.value = false;
 }
 // 杩斿洖鎸夐挳
-function handleClose() {
+const handleClose = () => {
   const obj = { path: "/system/role" };
-  proxy.$tab.closeOpenPage(obj);
+  proxy?.$tab.closeOpenPage(obj);
 }
 /** 鎼滅储鎸夐挳鎿嶄綔 */
-function handleQuery() {
+const handleQuery=() => {
   queryParams.pageNum = 1;
   getList();
 }
 /** 閲嶇疆鎸夐挳鎿嶄綔 */
-function resetQuery() {
-  proxy.resetForm("queryRef");
+const resetQuery=() =>{
+  queryFormRef.value.resetFields();
   handleQuery();
 }
 // 澶氶�夋閫変腑鏁版嵁
-function handleSelectionChange(selection) {
+const handleSelectionChange = (selection: UserVO[]) =>{
   userIds.value = selection.map(item => item.userId);
   multiple.value = !selection.length;
 }
 /** 鎵撳紑鎺堟潈鐢ㄦ埛琛ㄥ脊绐� */
-function openSelectUser() {
-  proxy.$refs["selectRef"].show();
+const openSelectUser = () => {
+  selectRef.value.show();
 }
 /** 鍙栨秷鎺堟潈鎸夐挳鎿嶄綔 */
-function cancelAuthUser(row) {
-  proxy.$modal.confirm('纭瑕佸彇娑堣鐢ㄦ埛"' + row.userName + '"瑙掕壊鍚楋紵').then(function () {
-    return authUserCancel({ userId: row.userId, roleId: queryParams.roleId });
-  }).then(() => {
-    getList();
-    proxy.$modal.msgSuccess("鍙栨秷鎺堟潈鎴愬姛");
-  }).catch(() => {});
+const cancelAuthUser = async (row: UserVO) => {
+	await proxy?.$modal.confirm('纭瑕佸彇娑堣鐢ㄦ埛"' + row.userName + '"瑙掕壊鍚楋紵');
+	await authUserCancel({ userId: row.userId, roleId: queryParams.roleId });
+	getList();
+	proxy?.$modal.msgSuccess("鍙栨秷鎺堟潈鎴愬姛");
 }
 /** 鎵归噺鍙栨秷鎺堟潈鎸夐挳鎿嶄綔 */
-function cancelAuthUserAll(row) {
+const cancelAuthUserAll = async () => {
   const roleId = queryParams.roleId;
   const uIds = userIds.value.join(",");
-  proxy.$modal.confirm("鏄惁鍙栨秷閫変腑鐢ㄦ埛鎺堟潈鏁版嵁椤�?").then(function () {
-    return authUserCancelAll({ roleId: roleId, userIds: uIds });
-  }).then(() => {
-    getList();
-    proxy.$modal.msgSuccess("鍙栨秷鎺堟潈鎴愬姛");
-  }).catch(() => {});
+	await proxy?.$modal.confirm("鏄惁鍙栨秷閫変腑鐢ㄦ埛鎺堟潈鏁版嵁椤�?");
+	await authUserCancelAll({ roleId: roleId, userIds: uIds });
+	getList();
+	proxy?.$modal.msgSuccess("鍙栨秷鎺堟潈鎴愬姛");
 }
 
-getList();
+onMounted(() => {
+  getList();
+});
 </script>
+
+<template>
+	<div class="p-2">
+		<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
+			<div class="search" v-show="showSearch">
+				<el-form :model="queryParams" ref="queryFormRef" :inline="true">
+					<el-form-item label="鐢ㄦ埛鍚嶇О" prop="userName">
+						<el-input v-model="queryParams.userName" placeholder="璇疯緭鍏ョ敤鎴峰悕绉�" clearable style="width: 240px" @keyup.enter="handleQuery" />
+					</el-form-item>
+					<el-form-item label="鎵嬫満鍙风爜" prop="phonenumber">
+						<el-input v-model="queryParams.phonenumber" placeholder="璇疯緭鍏ユ墜鏈哄彿鐮�" clearable style="width: 240px" @keyup.enter="handleQuery" />
+					</el-form-item>
+					<el-form-item>
+						<el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
+						<el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
+					</el-form-item>
+				</el-form>
+			</div>
+		</transition>
+		<el-card shadow="never">
+			<template #header>
+				<el-row :gutter="10">
+					<el-col :span="1.5">
+						<el-button type="primary" plain icon="Plus" @click="openSelectUser" v-hasPermi="['system:role:add']">娣诲姞鐢ㄦ埛</el-button>
+					</el-col>
+					<el-col :span="1.5">
+						<el-button type="danger" plain icon="CircleClose" :disabled="multiple" @click="cancelAuthUserAll" v-hasPermi="['system:role:remove']">
+							鎵归噺鍙栨秷鎺堟潈
+						</el-button>
+					</el-col>
+					<el-col :span="1.5">
+						<el-button type="warning" plain icon="Close" @click="handleClose">鍏抽棴</el-button>
+					</el-col>
+					<right-toolbar v-model:showSearch="showSearch" @queryTable="getList" :search="true"></right-toolbar>
+				</el-row>
+			</template>
+			<el-table v-loading="loading" :data="userList" @selection-change="handleSelectionChange">
+				<el-table-column type="selection" width="55" align="center" />
+				<el-table-column label="鐢ㄦ埛鍚嶇О" prop="userName" :show-overflow-tooltip="true" />
+				<el-table-column label="鐢ㄦ埛鏄电О" prop="nickName" :show-overflow-tooltip="true" />
+				<el-table-column label="閭" prop="email" :show-overflow-tooltip="true" />
+				<el-table-column label="鎵嬫満" prop="phonenumber" :show-overflow-tooltip="true" />
+				<el-table-column label="鐘舵��" align="center" prop="status">
+					<template #default="scope">
+						<dict-tag :options="sys_normal_disable" :value="scope.row.status" />
+					</template>
+				</el-table-column>
+				<el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime" width="180">
+					<template #default="scope">
+						<span>{{ scope.row.createTime }}</span>
+					</template>
+				</el-table-column>
+				<el-table-column label="鎿嶄綔" align="center" class-name="small-padding fixed-width">
+					<template #default="scope">
+						<el-tooltip content="鍙栨秷鎺堟潈" placement="top">
+							<el-button link type="primary" icon="CircleClose" @click="cancelAuthUser(scope.row)" v-hasPermi="['system:role:remove']"> </el-button>
+						</el-tooltip>
+					</template>
+				</el-table-column>
+			</el-table>
+
+			<pagination
+				v-show="total > 0"
+				:total="total"
+				v-model:page="queryParams.pageNum"
+				v-model:limit="queryParams.pageSize"
+				@pagination="handleQuery"
+			/>
+			<select-user ref="selectRef" :roleId="queryParams.roleId" @ok="handleQuery" />
+		</el-card>
+	</div>
+</template>
+
+<style lang="scss" scoped></style>
diff --git a/src/views/system/role/index.vue b/src/views/system/role/index.vue
index 9050599..f64c697 100644
--- a/src/views/system/role/index.vue
+++ b/src/views/system/role/index.vue
@@ -1,274 +1,30 @@
-<template>
-   <div class="app-container">
-      <el-form :model="queryParams" ref="queryRef" v-show="showSearch" :inline="true" label-width="68px">
-         <el-form-item label="瑙掕壊鍚嶇О" prop="roleName">
-            <el-input
-               v-model="queryParams.roleName"
-               placeholder="璇疯緭鍏ヨ鑹插悕绉�"
-               clearable
-               style="width: 240px"
-               @keyup.enter="handleQuery"
-            />
-         </el-form-item>
-         <el-form-item label="鏉冮檺瀛楃" prop="roleKey">
-            <el-input
-               v-model="queryParams.roleKey"
-               placeholder="璇疯緭鍏ユ潈闄愬瓧绗�"
-               clearable
-               style="width: 240px"
-               @keyup.enter="handleQuery"
-            />
-         </el-form-item>
-         <el-form-item label="鐘舵��" prop="status">
-            <el-select
-               v-model="queryParams.status"
-               placeholder="瑙掕壊鐘舵��"
-               clearable
-               style="width: 240px"
-            >
-               <el-option
-                  v-for="dict in sys_normal_disable"
-                  :key="dict.value"
-                  :label="dict.label"
-                  :value="dict.value"
-               />
-            </el-select>
-         </el-form-item>
-         <el-form-item label="鍒涘缓鏃堕棿" style="width: 308px">
-            <el-date-picker
-               v-model="dateRange"
-               value-format="YYYY-MM-DD HH:mm:ss"
-               type="daterange"
-               range-separator="-"
-               start-placeholder="寮�濮嬫棩鏈�"
-               end-placeholder="缁撴潫鏃ユ湡"
-               :default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"
-            ></el-date-picker>
-         </el-form-item>
-         <el-form-item>
-            <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
-            <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
-         </el-form-item>
-      </el-form>
-      <el-row :gutter="10" class="mb8">
-         <el-col :span="1.5">
-            <el-button
-               type="primary"
-               plain
-               icon="Plus"
-               @click="handleAdd"
-               v-hasPermi="['system:role:add']"
-            >鏂板</el-button>
-         </el-col>
-         <el-col :span="1.5">
-            <el-button
-               type="success"
-               plain
-               icon="Edit"
-               :disabled="single"
-               @click="handleUpdate"
-               v-hasPermi="['system:role:edit']"
-            >淇敼</el-button>
-         </el-col>
-         <el-col :span="1.5">
-            <el-button
-               type="danger"
-               plain
-               icon="Delete"
-               :disabled="multiple"
-               @click="handleDelete"
-               v-hasPermi="['system:role:remove']"
-            >鍒犻櫎</el-button>
-         </el-col>
-         <el-col :span="1.5">
-            <el-button
-               type="warning"
-               plain
-               icon="Download"
-               @click="handleExport"
-               v-hasPermi="['system:role:export']"
-            >瀵煎嚭</el-button>
-         </el-col>
-         <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
-      </el-row>
-
-      <!-- 琛ㄦ牸鏁版嵁 -->
-      <el-table v-loading="loading" :data="roleList" @selection-change="handleSelectionChange">
-         <el-table-column type="selection" width="55" align="center" />
-         <el-table-column label="瑙掕壊缂栧彿" prop="roleId" width="120" v-if="false" />
-         <el-table-column label="瑙掕壊鍚嶇О" prop="roleName" :show-overflow-tooltip="true" width="150" />
-         <el-table-column label="鏉冮檺瀛楃" prop="roleKey" :show-overflow-tooltip="true" width="150" />
-         <el-table-column label="鏄剧ず椤哄簭" prop="roleSort" width="100" />
-         <el-table-column label="鐘舵��" align="center" width="100">
-            <template #default="scope">
-               <el-switch
-                  v-model="scope.row.status"
-                  active-value="0"
-                  inactive-value="1"
-                  @change="handleStatusChange(scope.row)"
-               ></el-switch>
-            </template>
-         </el-table-column>
-         <el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime">
-            <template #default="scope">
-               <span>{{ parseTime(scope.row.createTime) }}</span>
-            </template>
-         </el-table-column>
-         <el-table-column label="鎿嶄綔" align="center" class-name="small-padding fixed-width">
-            <template #default="scope">
-              <el-tooltip content="淇敼" placement="top" v-if="scope.row.roleId !== 1">
-                <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:role:edit']"></el-button>
-              </el-tooltip>
-              <el-tooltip content="鍒犻櫎" placement="top" v-if="scope.row.roleId !== 1">
-                <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:role:remove']"></el-button>
-              </el-tooltip>
-              <el-tooltip content="鏁版嵁鏉冮檺" placement="top" v-if="scope.row.roleId !== 1">
-                <el-button link type="primary" icon="CircleCheck" @click="handleDataScope(scope.row)" v-hasPermi="['system:role:edit']"></el-button>
-              </el-tooltip>
-              <el-tooltip content="鍒嗛厤鐢ㄦ埛" placement="top" v-if="scope.row.roleId !== 1">
-                <el-button link type="primary" icon="User" @click="handleAuthUser(scope.row)" v-hasPermi="['system:role:edit']"></el-button>
-              </el-tooltip>
-            </template>
-         </el-table-column>
-      </el-table>
-
-      <pagination
-         v-show="total > 0"
-         :total="total"
-         v-model:page="queryParams.pageNum"
-         v-model:limit="queryParams.pageSize"
-         @pagination="getList"
-      />
-
-      <!-- 娣诲姞鎴栦慨鏀硅鑹查厤缃璇濇 -->
-      <el-dialog :title="title" v-model="open" width="500px" append-to-body>
-         <el-form ref="roleRef" :model="form" :rules="rules" label-width="100px">
-            <el-form-item label="瑙掕壊鍚嶇О" prop="roleName">
-               <el-input v-model="form.roleName" placeholder="璇疯緭鍏ヨ鑹插悕绉�" />
-            </el-form-item>
-            <el-form-item prop="roleKey">
-               <template #label>
-                  <span>
-                     <el-tooltip content="鎺у埗鍣ㄤ腑瀹氫箟鐨勬潈闄愬瓧绗︼紝濡傦細@PreAuthorize(`@ss.hasRole('admin')`)" placement="top">
-                        <el-icon><question-filled /></el-icon>
-                     </el-tooltip>
-                     鏉冮檺瀛楃
-                  </span>
-               </template>
-               <el-input v-model="form.roleKey" placeholder="璇疯緭鍏ユ潈闄愬瓧绗�" />
-            </el-form-item>
-            <el-form-item label="瑙掕壊椤哄簭" prop="roleSort">
-               <el-input-number v-model="form.roleSort" controls-position="right" :min="0" />
-            </el-form-item>
-            <el-form-item label="鐘舵��">
-               <el-radio-group v-model="form.status">
-                  <el-radio
-                     v-for="dict in sys_normal_disable"
-                     :key="dict.value"
-                     :label="dict.value"
-                  >{{ dict.label }}</el-radio>
-               </el-radio-group>
-            </el-form-item>
-            <el-form-item label="鑿滃崟鏉冮檺">
-               <el-checkbox v-model="menuExpand" @change="handleCheckedTreeExpand($event, 'menu')">灞曞紑/鎶樺彔</el-checkbox>
-               <el-checkbox v-model="menuNodeAll" @change="handleCheckedTreeNodeAll($event, 'menu')">鍏ㄩ��/鍏ㄤ笉閫�</el-checkbox>
-               <el-checkbox v-model="form.menuCheckStrictly" @change="handleCheckedTreeConnect($event, 'menu')">鐖跺瓙鑱斿姩</el-checkbox>
-               <el-tree
-                  class="tree-border"
-                  :data="menuOptions"
-                  show-checkbox
-                  ref="menuRef"
-                  node-key="id"
-                  :check-strictly="!form.menuCheckStrictly"
-                  empty-text="鍔犺浇涓紝璇风◢鍊�"
-                  :props="{ label: 'label', children: 'children' }"
-               ></el-tree>
-            </el-form-item>
-            <el-form-item label="澶囨敞">
-               <el-input v-model="form.remark" type="textarea" placeholder="璇疯緭鍏ュ唴瀹�"></el-input>
-            </el-form-item>
-         </el-form>
-         <template #footer>
-            <div class="dialog-footer">
-               <el-button type="primary" @click="submitForm">纭� 瀹�</el-button>
-               <el-button @click="cancel">鍙� 娑�</el-button>
-            </div>
-         </template>
-      </el-dialog>
-
-      <!-- 鍒嗛厤瑙掕壊鏁版嵁鏉冮檺瀵硅瘽妗� -->
-      <el-dialog :title="title" v-model="openDataScope" width="500px" append-to-body>
-         <el-form :model="form" label-width="80px">
-            <el-form-item label="瑙掕壊鍚嶇О">
-               <el-input v-model="form.roleName" :disabled="true" />
-            </el-form-item>
-            <el-form-item label="鏉冮檺瀛楃">
-               <el-input v-model="form.roleKey" :disabled="true" />
-            </el-form-item>
-            <el-form-item label="鏉冮檺鑼冨洿">
-               <el-select v-model="form.dataScope" @change="dataScopeSelectChange">
-                  <el-option
-                     v-for="item in dataScopeOptions"
-                     :key="item.value"
-                     :label="item.label"
-                     :value="item.value"
-                  ></el-option>
-               </el-select>
-            </el-form-item>
-            <el-form-item label="鏁版嵁鏉冮檺" v-show="form.dataScope == 2">
-               <el-checkbox v-model="deptExpand" @change="handleCheckedTreeExpand($event, 'dept')">灞曞紑/鎶樺彔</el-checkbox>
-               <el-checkbox v-model="deptNodeAll" @change="handleCheckedTreeNodeAll($event, 'dept')">鍏ㄩ��/鍏ㄤ笉閫�</el-checkbox>
-               <el-checkbox v-model="form.deptCheckStrictly" @change="handleCheckedTreeConnect($event, 'dept')">鐖跺瓙鑱斿姩</el-checkbox>
-               <el-tree
-                  class="tree-border"
-                  :data="deptOptions"
-                  show-checkbox
-                  default-expand-all
-                  ref="deptRef"
-                  node-key="id"
-                  :check-strictly="!form.deptCheckStrictly"
-                  empty-text="鍔犺浇涓紝璇风◢鍊�"
-                  :props="{ label: 'label', children: 'children' }"
-               ></el-tree>
-            </el-form-item>
-         </el-form>
-         <template #footer>
-            <div class="dialog-footer">
-               <el-button type="primary" @click="submitDataScope">纭� 瀹�</el-button>
-               <el-button @click="cancelDataScope">鍙� 娑�</el-button>
-            </div>
-         </template>
-      </el-dialog>
-   </div>
-</template>
-
-<script setup name="Role">
+<script setup name="Role" lang="ts">
 import { addRole, changeRoleStatus, dataScope, delRole, getRole, listRole, updateRole, deptTreeSelect } from "@/api/system/role";
-import { roleMenuTreeselect, treeselect as menuTreeselect } from "@/api/system/menu";
+import { roleMenuTreeselect, treeselect as menuTreeselect } from '@/api/system/menu/index';
+import { RoleVO, RoleForm, RoleQuery, DeptTreeOption } from '@/api/system/role/types';
+import { MenuTreeOption, RoleMenuTree } from '@/api/system/menu/types';
+import { ComponentInternalInstance } from 'vue';
+import { ElTree, ElForm, DateModelType } from 'element-plus';
 
 const router = useRouter();
-const { proxy } = getCurrentInstance();
-const { sys_normal_disable } = proxy.useDict("sys_normal_disable");
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { sys_normal_disable } = toRefs<any>(proxy?.useDict('sys_normal_disable'));
 
-const roleList = ref([]);
-const open = ref(false);
-const loading = ref(true);
-const showSearch = ref(true);
-const ids = ref([]);
-const single = ref(true);
-const multiple = ref(true);
-const total = ref(0);
-const title = ref("");
-const dateRange = ref([]);
-const menuOptions = ref([]);
-const menuExpand = ref(false);
-const menuNodeAll = ref(false);
-const deptExpand = ref(true);
-const deptNodeAll = ref(false);
-const deptOptions = ref([]);
-const openDataScope = ref(false);
-const menuRef = ref(null);
-const deptRef = ref(null);
+const roleList = ref<RoleVO[]>();
+const loading = ref(true)
+const showSearch = ref(true)
+const ids = ref<Array<string | number>>([])
+const single = ref(true)
+const multiple = ref(true)
+const total = ref(0)
+const dateRange = ref<[DateModelType, DateModelType]>(['', ''])
+const menuOptions = ref<MenuTreeOption[]>([])
+const menuExpand = ref(false)
+const menuNodeAll = ref(false)
+const deptExpand = ref(true)
+const deptNodeAll = ref(false)
+const deptOptions = ref<DeptTreeOption[]>([])
+const openDataScope = ref(false)
 
 /** 鏁版嵁鑼冨洿閫夐」*/
 const dataScopeOptions = ref([
@@ -277,179 +33,189 @@
   { value: "3", label: "鏈儴闂ㄦ暟鎹潈闄�" },
   { value: "4", label: "鏈儴闂ㄥ強浠ヤ笅鏁版嵁鏉冮檺" },
   { value: "5", label: "浠呮湰浜烘暟鎹潈闄�" }
-]);
+])
 
-const data = reactive({
-  form: {},
+const queryFormRef = ref(ElForm);
+const roleFormRef = ref(ElForm);
+const dataScopeRef = ref(ElForm);
+const menuRef = ref(ElTree);
+const deptRef = ref(ElTree);
+
+const initForm: RoleForm = {
+  roleId: undefined,
+  roleSort: 1,
+  status: '0',
+  roleName: '',
+  roleKey: '',
+  menuCheckStrictly: true,
+  deptCheckStrictly: true,
+  remark: '',
+  dataScope: 1,
+  menuIds: [],
+  deptIds: [],
+}
+
+const data = reactive<PageData<RoleForm, RoleQuery>>({
+  form: {...initForm},
   queryParams: {
     pageNum: 1,
     pageSize: 10,
-    roleName: undefined,
-    roleKey: undefined,
-    status: undefined
+    roleName: '',
+    roleKey: '',
+    status: '',
   },
   rules: {
     roleName: [{ required: true, message: "瑙掕壊鍚嶇О涓嶈兘涓虹┖", trigger: "blur" }],
     roleKey: [{ required: true, message: "鏉冮檺瀛楃涓嶈兘涓虹┖", trigger: "blur" }],
     roleSort: [{ required: true, message: "瑙掕壊椤哄簭涓嶈兘涓虹┖", trigger: "blur" }]
-  },
+  }
+})
+const { form, queryParams, rules } = toRefs(data)
+
+
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
 });
 
-const { queryParams, form, rules } = toRefs(data);
 
-/** 鏌ヨ瑙掕壊鍒楄〃 */
-function getList() {
-  loading.value = true;
-  listRole(proxy.addDateRange(queryParams.value, dateRange.value)).then(response => {
-    roleList.value = response.rows;
-    total.value = response.total;
-    loading.value = false;
-  });
+/**
+ * 鏌ヨ瑙掕壊鍒楄〃
+ */
+const getList = () => {
+  loading.value = true
+  listRole(proxy?.addDateRange(queryParams.value, dateRange.value)).then(res => {
+    roleList.value = res.rows
+    total.value = res.total
+    loading.value = false
+  })
 }
-/** 鎼滅储鎸夐挳鎿嶄綔 */
-function handleQuery() {
+
+/**
+ * 鎼滅储鎸夐挳鎿嶄綔
+ */
+const handleQuery = () => {
   queryParams.value.pageNum = 1;
   getList();
 }
-/** 閲嶇疆鎸夐挳鎿嶄綔 */
-function resetQuery() {
-  dateRange.value = [];
-  proxy.resetForm("queryRef");
+
+/** 閲嶇疆 */
+const resetQuery = () => {
+  dateRange.value = ['', '']
+  queryFormRef.value.resetFields();
   handleQuery();
 }
-/** 鍒犻櫎鎸夐挳鎿嶄綔 */
-function handleDelete(row) {
-  const roleIds = row.roleId || ids.value;
-  proxy.$modal.confirm('鏄惁纭鍒犻櫎瑙掕壊缂栧彿涓�"' + roleIds + '"鐨勬暟鎹」?').then(function () {
-    return delRole(roleIds);
-  }).then(() => {
-    getList();
-    proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
-  }).catch(() => {});
+/**鍒犻櫎鎸夐挳鎿嶄綔 */
+const handleDelete = async (row?: RoleVO) => {
+  const roleids = row?.roleId || ids.value;
+  await proxy?.$modal.confirm('鏄惁纭鍒犻櫎瑙掕壊缂栧彿涓�' + roleids + '鏁版嵁椤圭洰');
+  await delRole(roleids);
+  getList();
+  proxy?.$modal.msgSuccess('鍒犻櫎鎴愬姛');
 }
+
 /** 瀵煎嚭鎸夐挳鎿嶄綔 */
-function handleExport() {
-  proxy.download("system/role/export", {
+const handleExport = () => {
+  proxy?.download("system/role/export", {
     ...queryParams.value,
-  }, `role_${new Date().getTime()}.xlsx`);
+  }, `role_${new Date().getTime()}.xlsx`)
 }
 /** 澶氶�夋閫変腑鏁版嵁 */
-function handleSelectionChange(selection) {
-  ids.value = selection.map(item => item.roleId);
+const handleSelectionChange = (selection: RoleVO[]) => {
+  ids.value = selection.map((item: RoleVO) => item.roleId);
   single.value = selection.length != 1;
   multiple.value = !selection.length;
 }
+
 /** 瑙掕壊鐘舵�佷慨鏀� */
-function handleStatusChange(row) {
+const handleStatusChange = async (row: RoleVO) => {
   let text = row.status === "0" ? "鍚敤" : "鍋滅敤";
-  proxy.$modal.confirm('纭瑕�"' + text + '""' + row.roleName + '"瑙掕壊鍚�?').then(function () {
-    return changeRoleStatus(row.roleId, row.status);
-  }).then(() => {
-    proxy.$modal.msgSuccess(text + "鎴愬姛");
-  }).catch(function () {
+  try {
+    await proxy?.$modal.confirm('纭瑕�"' + text + '""' + row.roleName + '"瑙掕壊鍚�?');
+    await changeRoleStatus(row.roleId, row.status);
+  proxy?.$modal.msgSuccess(text + "鎴愬姛");
+  } catch {
     row.status = row.status === "0" ? "1" : "0";
-  });
-}
-/** 鏇村鎿嶄綔 */
-function handleCommand(command, row) {
-  switch (command) {
-    case "handleDataScope":
-      handleDataScope(row);
-      break;
-    case "handleAuthUser":
-      handleAuthUser(row);
-      break;
-    default:
-      break;
   }
 }
+
 /** 鍒嗛厤鐢ㄦ埛 */
-function handleAuthUser(row) {
+const handleAuthUser = (row: RoleVO) => {
   router.push("/system/role-auth/user/" + row.roleId);
 }
+
 /** 鏌ヨ鑿滃崟鏍戠粨鏋� */
-function getMenuTreeselect() {
-  menuTreeselect().then(response => {
-    menuOptions.value = response.data;
-  });
+const getMenuTreeselect = async () => {
+  const res = await menuTreeselect();
+  menuOptions.value = res.data;
 }
 /** 鎵�鏈夐儴闂ㄨ妭鐐规暟鎹� */
-function getDeptAllCheckedKeys() {
+const getDeptAllCheckedKeys = () => {
   // 鐩墠琚�変腑鐨勯儴闂ㄨ妭鐐�
   let checkedKeys = deptRef.value.getCheckedKeys();
   // 鍗婇�変腑鐨勯儴闂ㄨ妭鐐�
   let halfCheckedKeys = deptRef.value.getHalfCheckedKeys();
   checkedKeys.unshift.apply(checkedKeys, halfCheckedKeys);
-  return checkedKeys;
+  return checkedKeys
 }
 /** 閲嶇疆鏂板鐨勮〃鍗曚互鍙婂叾浠栨暟鎹�  */
-function reset() {
-  if (menuRef.value != undefined) {
-    menuRef.value.setCheckedKeys([]);
-  }
-  menuExpand.value = false;
-  menuNodeAll.value = false;
-  deptExpand.value = true;
-  deptNodeAll.value = false;
-  form.value = {
-    roleId: undefined,
-    roleName: undefined,
-    roleKey: undefined,
-    roleSort: 0,
-    status: "0",
-    menuIds: [],
-    deptIds: [],
-    menuCheckStrictly: true,
-    deptCheckStrictly: true,
-    remark: undefined
-  };
-  proxy.resetForm("roleRef");
+const reset = () => {
+  menuRef.value.setCheckedKeys([]);
+  menuExpand.value = false
+  menuNodeAll.value = false
+  deptExpand.value = true
+  deptNodeAll.value = false
+  form.value = initForm
+  roleFormRef.value.resetFields();
+
 }
+
 /** 娣诲姞瑙掕壊 */
-function handleAdd() {
-  reset();
-  getMenuTreeselect();
-  open.value = true;
-  title.value = "娣诲姞瑙掕壊";
+const handleAdd = () => {
+  dialog.visible = true;
+  dialog.title = "娣诲姞瑙掕壊";
+  nextTick(() => {
+    reset();
+    getMenuTreeselect();
+  })
 }
 /** 淇敼瑙掕壊 */
-function handleUpdate(row) {
-  reset();
-  const roleId = row.roleId || ids.value;
-  const roleMenu = getRoleMenuTreeselect(roleId);
-  getRole(roleId).then(response => {
-    form.value = response.data;
+const handleUpdate = async (row?: RoleVO) => {
+  const roleId = row?.roleId || ids.value[0]
+  const roleMenu = getRoleMenuTreeselect(roleId)
+  const { data } = await getRole(roleId);
+  dialog.visible = true;
+  dialog.title = "淇敼瑙掕壊";
+  nextTick(() => {
+    reset();
+    Object.assign(form.value, data);
     form.value.roleSort = Number(form.value.roleSort);
-    open.value = true;
-    nextTick(() => {
-      roleMenu.then((res) => {
-        let checkedKeys = res.data.checkedKeys;
-        checkedKeys.forEach((v) => {
-          nextTick(() => {
-            menuRef.value.setChecked(v, true, false);
-          });
-        });
-      });
-    });
-    title.value = "淇敼瑙掕壊";
-  });
+    nextTick(async () => {
+      const res = await roleMenu;
+      let checkedKeys = res.checkedKeys;
+      checkedKeys.forEach((v) => {
+        nextTick(() => {
+          menuRef.value.setChecked(v, true, false);
+        })
+      })
+    })
+  })
 }
 /** 鏍规嵁瑙掕壊ID鏌ヨ鑿滃崟鏍戠粨鏋� */
-function getRoleMenuTreeselect(roleId) {
-  return roleMenuTreeselect(roleId).then(response => {
-    menuOptions.value = response.data.menus;
-    return response;
-  });
+const getRoleMenuTreeselect = (roleId: string | number) => {
+  return roleMenuTreeselect(roleId).then((res): RoleMenuTree => {
+    menuOptions.value = res.data.menus;
+    return res.data;
+  })
 }
 /** 鏍规嵁瑙掕壊ID鏌ヨ閮ㄩ棬鏍戠粨鏋� */
-function getDeptTree(roleId) {
-  return deptTreeSelect(roleId).then(response => {
-    deptOptions.value = response.data.depts;
-    return response;
-  });
+const getRoleDeptTreeSelect = async (roleId: string | number) => {
+  const res = await deptTreeSelect(roleId);
+  deptOptions.value = res.data.depts;
+  return res.data;
 }
 /** 鏍戞潈闄愶紙灞曞紑/鎶樺彔锛�*/
-function handleCheckedTreeExpand(value, type) {
+const handleCheckedTreeExpand = (value: any, type: string) => {
   if (type == "menu") {
     let treeList = menuOptions.value;
     for (let i = 0; i < treeList.length; i++) {
@@ -463,7 +229,7 @@
   }
 }
 /** 鏍戞潈闄愶紙鍏ㄩ��/鍏ㄤ笉閫夛級 */
-function handleCheckedTreeNodeAll(value, type) {
+const handleCheckedTreeNodeAll = (value: any, type: string) => {
   if (type == "menu") {
     menuRef.value.setCheckedNodes(value ? menuOptions.value : []);
   } else if (type == "dept") {
@@ -471,15 +237,15 @@
   }
 }
 /** 鏍戞潈闄愶紙鐖跺瓙鑱斿姩锛� */
-function handleCheckedTreeConnect(value, type) {
+const handleCheckedTreeConnect = (value: any, type: string) => {
   if (type == "menu") {
-    form.value.menuCheckStrictly = value ? true : false;
+    form.value.menuCheckStrictly = value;
   } else if (type == "dept") {
-    form.value.deptCheckStrictly = value ? true : false;
+    form.value.deptCheckStrictly = value;
   }
 }
 /** 鎵�鏈夎彍鍗曡妭鐐规暟鎹� */
-function getMenuAllCheckedKeys() {
+const getMenuAllCheckedKeys = () => {
   // 鐩墠琚�変腑鐨勮彍鍗曡妭鐐�
   let checkedKeys = menuRef.value.getCheckedKeys();
   // 鍗婇�変腑鐨勮彍鍗曡妭鐐�
@@ -488,73 +254,255 @@
   return checkedKeys;
 }
 /** 鎻愪氦鎸夐挳 */
-function submitForm() {
-  proxy.$refs["roleRef"].validate(valid => {
+const submitForm = () => {
+  roleFormRef.value.validate(async (valid: boolean) => {
     if (valid) {
-      if (form.value.roleId != undefined) {
-        form.value.menuIds = getMenuAllCheckedKeys();
-        updateRole(form.value).then(response => {
-          proxy.$modal.msgSuccess("淇敼鎴愬姛");
-          open.value = false;
-          getList();
-        });
-      } else {
-        form.value.menuIds = getMenuAllCheckedKeys();
-        addRole(form.value).then(response => {
-          proxy.$modal.msgSuccess("鏂板鎴愬姛");
-          open.value = false;
-          getList();
-        });
-      }
+      form.value.menuIds = getMenuAllCheckedKeys()
+      form.value.roleId ? await updateRole(form.value) : await addRole(form.value);
+      proxy?.$modal.msgSuccess("鎿嶄綔鎴愬姛")
+      dialog.visible = false
+      getList()
     }
-  });
+  })
 }
 /** 鍙栨秷鎸夐挳 */
-function cancel() {
-  open.value = false;
-  reset();
+const cancel = () => {
+  reset()
+  dialog.visible = false;
 }
 /** 閫夋嫨瑙掕壊鏉冮檺鑼冨洿瑙﹀彂 */
-function dataScopeSelectChange(value) {
+const dataScopeSelectChange = (value: string) => {
   if (value !== "2") {
-    deptRef.value.setCheckedKeys([]);
+    deptRef.value.setCheckedKeys([])
   }
 }
 /** 鍒嗛厤鏁版嵁鏉冮檺鎿嶄綔 */
-function handleDataScope(row) {
-  reset();
-  const deptTreeSelect = getDeptTree(row.roleId);
-  getRole(row.roleId).then(response => {
-    form.value = response.data;
-    openDataScope.value = true;
+const handleDataScope = async (row: RoleVO) => {
+  const roleDeptTreeselect = getRoleDeptTreeSelect(row.roleId);
+  const response = await getRole(row.roleId);
+  Object.assign(form.value, response.data);
+  openDataScope.value = true;
+  dialog.title = "鍒嗛厤鏁版嵁鏉冮檺";
+  nextTick(async () => {
+    const res = await roleDeptTreeselect;
     nextTick(() => {
-      deptTreeSelect.then(res => {
-        nextTick(() => {
-          if (deptRef.value) {
-            deptRef.value.setCheckedKeys(res.data.checkedKeys);
-          }
-        });
-      });
-    });
-    title.value = "鍒嗛厤鏁版嵁鏉冮檺";
-  });
+      if (deptRef.value) {
+        deptRef.value.setCheckedKeys(res.checkedKeys);
+      }
+    })
+  })
 }
 /** 鎻愪氦鎸夐挳锛堟暟鎹潈闄愶級 */
-function submitDataScope() {
-  if (form.value.roleId != undefined) {
+const submitDataScope = async () => {
+  if (form.value.roleId) {
     form.value.deptIds = getDeptAllCheckedKeys();
-    dataScope(form.value).then(response => {
-      proxy.$modal.msgSuccess("淇敼鎴愬姛");
-      openDataScope.value = false;
-      getList();
-    });
+    await dataScope(form.value);
+    proxy?.$modal.msgSuccess("淇敼鎴愬姛");
+    openDataScope.value = false;
+    getList();
   }
 }
 /** 鍙栨秷鎸夐挳锛堟暟鎹潈闄愶級*/
-function cancelDataScope() {
+const cancelDataScope = () => {
+  dataScopeRef.value.resetFields();
+  form.value = {...initForm};
   openDataScope.value = false;
-  reset();
 }
 
-getList();
+onMounted(() => {
+  getList();
+});
 </script>
+
+<template>
+	<div class="p-2">
+		<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
+			<div class="search" v-show="showSearch">
+				<el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="68px">
+					<el-form-item label="瑙掕壊鍚嶇О" prop="roleName">
+						<el-input v-model="queryParams.roleName" placeholder="璇疯緭鍏ヨ鑹插悕绉�" clearable style="width: 240px" @keyup.enter="handleQuery" />
+					</el-form-item>
+					<el-form-item label="鏉冮檺瀛楃" prop="roleKey">
+						<el-input v-model="queryParams.roleKey" placeholder="璇疯緭鍏ユ潈闄愬瓧绗�" clearable style="width: 240px" @keyup.enter="handleQuery" />
+					</el-form-item>
+					<el-form-item label="鐘舵��" prop="status">
+						<el-select v-model="queryParams.status" placeholder="瑙掕壊鐘舵��" clearable style="width: 240px">
+							<el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" />
+						</el-select>
+					</el-form-item>
+					<el-form-item label="鍒涘缓鏃堕棿" style="width: 308px">
+						<el-date-picker
+							v-model="dateRange"
+							value-format="YYYY-MM-DD"
+							type="daterange"
+							range-separator="-"
+							start-placeholder="寮�濮嬫棩鏈�"
+							end-placeholder="缁撴潫鏃ユ湡"
+							:default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"
+						></el-date-picker>
+					</el-form-item>
+
+					<el-form-item>
+						<el-button type="primary" @click="handleQuery" icon="Search">鎼滅储</el-button>
+						<el-button @click="resetQuery" icon="Refresh">閲嶇疆</el-button>
+					</el-form-item>
+				</el-form>
+			</div>
+		</transition>
+
+		<el-card shadow="never">
+			<template #header>
+				<el-row :gutter="10">
+					<el-col :span="1.5">
+						<el-button type="primary" @click="handleAdd()" icon="Plus" v-hasPermi="['system:role:add']">鏂板</el-button>
+					</el-col>
+					<el-col :span="1.5">
+						<el-button type="success" @click="handleUpdate()" :disabled="single" icon="Edit" v-hasPermi="['system:role:edit']">淇敼</el-button>
+					</el-col>
+					<el-col :span="1.5">
+						<el-button type="danger" :disabled="ids.length === 0" @click="handleDelete()" v-hasPermi="['system:role:delete']">鍒犻櫎</el-button>
+					</el-col>
+					<el-col :span="1.5">
+						<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['system:role:export']">瀵煎嚭</el-button>
+					</el-col>
+					<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+				</el-row>
+			</template>
+
+			<el-table ref="roleTableRef" v-loading="loading" :data="roleList" @selection-change="handleSelectionChange">
+				<el-table-column type="selection" width="55" align="center" />
+				<el-table-column label="瑙掕壊缂栧彿" prop="roleId" width="120" />
+				<el-table-column label="瑙掕壊鍚嶇О" prop="roleName" :show-overflow-tooltip="true" width="150" />
+				<el-table-column label="鏉冮檺瀛楃" prop="roleKey" :show-overflow-tooltip="true" width="150" />
+				<el-table-column label="鏄剧ず椤哄簭" prop="roleSort" width="100" />
+				<el-table-column label="鐘舵��" align="center" width="100">
+					<template #default="scope">
+						<el-switch v-model="scope.row.status" active-value="0" inactive-value="1" @change="handleStatusChange(scope.row)"></el-switch>
+					</template>
+				</el-table-column>
+				<el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime">
+					<template #default="scope">
+						<span>{{ parseTime(scope.row.createTime) }}</span>
+					</template>
+				</el-table-column>
+
+				<el-table-column fixed="right" label="鎿嶄綔" width="180">
+					<template #default="scope">
+						<el-tooltip content="淇敼" placement="top" v-if="scope.row.roleId !== 1">
+							<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:role:edit']"></el-button>
+						</el-tooltip>
+						<el-tooltip content="鍒犻櫎" placement="top" v-if="scope.row.roleId !== 1">
+							<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:role:remove']"></el-button>
+						</el-tooltip>
+						<el-tooltip content="鏁版嵁鏉冮檺" placement="top" v-if="scope.row.roleId !== 1">
+							<el-button link type="primary" icon="CircleCheck" @click="handleDataScope(scope.row)" v-hasPermi="['system:role:edit']"></el-button>
+						</el-tooltip>
+						<el-tooltip content="鍒嗛厤鐢ㄦ埛" placement="top" v-if="scope.row.roleId !== 1">
+							<el-button link type="primary" icon="User" @click="handleAuthUser(scope.row)" v-hasPermi="['system:role:edit']"></el-button>
+						</el-tooltip>
+					</template>
+				</el-table-column>
+			</el-table>
+
+			<pagination
+				v-if="total > 0"
+				v-model:total="total"
+				v-model:page="queryParams.pageNum"
+				v-model:limit="queryParams.pageSize"
+				@pagination="handleQuery"
+			/>
+		</el-card>
+
+		<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
+			<el-form ref="roleFormRef" :model="form" :rules="rules" label-width="100px">
+				<el-form-item label="瑙掕壊鍚嶇О" prop="roleName">
+					<el-input v-model="form.roleName" placeholder="璇疯緭鍏ヨ鑹插悕绉�" />
+				</el-form-item>
+				<el-form-item prop="roleKey">
+					<template #label>
+						<span>
+							<el-tooltip content="鎺у埗鍣ㄤ腑瀹氫箟鐨勬潈闄愬瓧绗︼紝濡傦細@PreAuthorize(`@ss.hasRole('admin')`)" placement="top">
+								<el-icon><question-filled /></el-icon>
+							</el-tooltip>
+							鏉冮檺瀛楃
+						</span>
+					</template>
+					<el-input v-model="form.roleKey" placeholder="璇疯緭鍏ユ潈闄愬瓧绗�" />
+				</el-form-item>
+				<el-form-item label="瑙掕壊椤哄簭" prop="roleSort">
+					<el-input-number v-model="form.roleSort" controls-position="right" :min="0" />
+				</el-form-item>
+				<el-form-item label="鐘舵��">
+					<el-radio-group v-model="form.status">
+						<el-radio v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.value">{{
+                dict.label
+						}}</el-radio>
+					</el-radio-group>
+				</el-form-item>
+				<el-form-item label="鑿滃崟鏉冮檺">
+					<el-checkbox v-model="menuExpand" @change="handleCheckedTreeExpand($event, 'menu')">灞曞紑/鎶樺彔</el-checkbox>
+					<el-checkbox v-model="menuNodeAll" @change="handleCheckedTreeNodeAll($event, 'menu')">鍏ㄩ��/鍏ㄤ笉閫�</el-checkbox>
+					<el-checkbox v-model="form.menuCheckStrictly" @change="handleCheckedTreeConnect($event, 'menu')">鐖跺瓙鑱斿姩</el-checkbox>
+					<el-tree
+						class="tree-border"
+						:data="menuOptions"
+						show-checkbox
+						ref="menuRef"
+						node-key="id"
+						:check-strictly="!form.menuCheckStrictly"
+						empty-text="鍔犺浇涓紝璇风◢鍊�"
+						:props="{ label: 'label', children: 'children' }"
+					></el-tree>
+				</el-form-item>
+				<el-form-item label="澶囨敞">
+					<el-input v-model="form.remark" type="textarea" placeholder="璇疯緭鍏ュ唴瀹�"></el-input>
+				</el-form-item>
+			</el-form>
+			<template #footer>
+				<div class="dialog-footer">
+					<el-button type="primary" @click="submitForm">纭� 瀹�</el-button>
+					<el-button @click="cancel">鍙� 娑�</el-button>
+				</div>
+			</template>
+		</el-dialog>
+
+		<!-- 鍒嗛厤瑙掕壊鏁版嵁鏉冮檺瀵硅瘽妗� -->
+		<el-dialog :title="dialog.title" v-model="openDataScope" width="500px" append-to-body>
+			<el-form :model="form" label-width="80px" ref="dataScopeRef">
+				<el-form-item label="瑙掕壊鍚嶇О">
+					<el-input v-model="form.roleName" :disabled="true" />
+				</el-form-item>
+				<el-form-item label="鏉冮檺瀛楃">
+					<el-input v-model="form.roleKey" :disabled="true" />
+				</el-form-item>
+				<el-form-item label="鏉冮檺鑼冨洿">
+					<el-select v-model="form.dataScope" @change="dataScopeSelectChange">
+						<el-option v-for="item in dataScopeOptions" :key="item.value" :label="item.label" :value="item.value"></el-option>
+					</el-select>
+				</el-form-item>
+				<el-form-item label="鏁版嵁鏉冮檺" v-show="form.dataScope === 2">
+					<el-checkbox v-model="deptExpand" @change="handleCheckedTreeExpand($event, 'dept')">灞曞紑/鎶樺彔</el-checkbox>
+					<el-checkbox v-model="deptNodeAll" @change="handleCheckedTreeNodeAll($event, 'dept')">鍏ㄩ��/鍏ㄤ笉閫�</el-checkbox>
+					<el-checkbox v-model="form.deptCheckStrictly" @change="handleCheckedTreeConnect($event, 'dept')">鐖跺瓙鑱斿姩</el-checkbox>
+					<el-tree
+						class="tree-border"
+						:data="deptOptions"
+						show-checkbox
+						default-expand-all
+						ref="deptRef"
+						node-key="id"
+						:check-strictly="!form.deptCheckStrictly"
+						empty-text="鍔犺浇涓紝璇风◢鍊�"
+						:props="{ label: 'label', children: 'children' }"
+					></el-tree>
+				</el-form-item>
+			</el-form>
+			<template #footer>
+				<div class="dialog-footer">
+					<el-button type="primary" @click="submitDataScope">纭� 瀹�</el-button>
+					<el-button @click="cancelDataScope">鍙� 娑�</el-button>
+				</div>
+			</template>
+		</el-dialog>
+	</div>
+</template>
diff --git a/src/views/system/role/selectUser.vue b/src/views/system/role/selectUser.vue
index 9be1ec9..ccae9e1 100644
--- a/src/views/system/role/selectUser.vue
+++ b/src/views/system/role/selectUser.vue
@@ -1,140 +1,133 @@
-<template>
-   <!-- 鎺堟潈鐢ㄦ埛 -->
-   <el-dialog title="閫夋嫨鐢ㄦ埛" v-model="visible" width="800px" top="5vh" append-to-body>
-      <el-form :model="queryParams" ref="queryRef" :inline="true">
-         <el-form-item label="鐢ㄦ埛鍚嶇О" prop="userName">
-            <el-input
-               v-model="queryParams.userName"
-               placeholder="璇疯緭鍏ョ敤鎴峰悕绉�"
-               clearable
-               style="width: 200px"
-               @keyup.enter="handleQuery"
-            />
-         </el-form-item>
-         <el-form-item label="鎵嬫満鍙风爜" prop="phonenumber">
-            <el-input
-               v-model="queryParams.phonenumber"
-               placeholder="璇疯緭鍏ユ墜鏈哄彿鐮�"
-               clearable
-               style="width: 200px"
-               @keyup.enter="handleQuery"
-            />
-         </el-form-item>
-         <el-form-item>
-            <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
-            <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
-         </el-form-item>
-      </el-form>
-      <el-row>
-         <el-table @row-click="clickRow" ref="refTable" :data="userList" @selection-change="handleSelectionChange" height="260px">
-            <el-table-column type="selection" width="55"></el-table-column>
-            <el-table-column label="鐢ㄦ埛鍚嶇О" prop="userName" :show-overflow-tooltip="true" />
-            <el-table-column label="鐢ㄦ埛鏄电О" prop="nickName" :show-overflow-tooltip="true" />
-            <el-table-column label="閭" prop="email" :show-overflow-tooltip="true" />
-            <el-table-column label="鎵嬫満" prop="phonenumber" :show-overflow-tooltip="true" />
-            <el-table-column label="鐘舵��" align="center" prop="status">
-               <template #default="scope">
-                  <dict-tag :options="sys_normal_disable" :value="scope.row.status" />
-               </template>
-            </el-table-column>
-            <el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime" width="180">
-               <template #default="scope">
-                  <span>{{ parseTime(scope.row.createTime) }}</span>
-               </template>
-            </el-table-column>
-         </el-table>
-         <pagination
-            v-show="total > 0"
-            :total="total"
-            v-model:page="queryParams.pageNum"
-            v-model:limit="queryParams.pageSize"
-            @pagination="getList"
-         />
-      </el-row>
-      <template #footer>
-         <div class="dialog-footer">
-            <el-button type="primary" @click="handleSelectUser">纭� 瀹�</el-button>
-            <el-button @click="visible = false">鍙� 娑�</el-button>
-         </div>
-      </template>
-   </el-dialog>
-</template>
-
-<script setup name="SelectUser">
+<script setup name="SelectUser" lang="ts">
 import { authUserSelectAll, unallocatedUserList } from "@/api/system/role";
+import { UserVO } from '@/api/system/user/types';
+import { UserQuery } from '@/api/system/user/types';
+import { ComponentInternalInstance } from 'vue';
+import { ElForm, ElTable } from 'element-plus';
+
 
 const props = defineProps({
   roleId: {
     type: [Number, String]
   }
-});
+})
 
-const { proxy } = getCurrentInstance();
-const { sys_normal_disable } = proxy.useDict("sys_normal_disable");
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { sys_normal_disable } = toRefs<any>(proxy?.useDict('sys_normal_disable'));
 
-const userList = ref([]);
+const userList = ref<UserVO[]>([]);
 const visible = ref(false);
 const total = ref(0);
-const userIds = ref([]);
+const userIds = ref<Array<string | number>>([]);
 
-const queryParams = reactive({
+const queryParams = reactive<UserQuery>({
   pageNum: 1,
   pageSize: 10,
   roleId: undefined,
   userName: undefined,
   phonenumber: undefined
-});
+})
 
-// 鏄剧ず寮规
-function show() {
+const tableRef = ref(ElTable);
+const queryFormRef = ref(ElForm);
+
+const show = () => {
   queryParams.roleId = props.roleId;
   getList();
   visible.value = true;
 }
-/**閫夋嫨琛� */
-function clickRow(row) {
-  proxy.$refs["refTable"].toggleRowSelection(row);
+
+/**
+ * 閫夋嫨琛�
+ */
+const clickRow = (row: any) => {
+  tableRef.value.toggleRowSelection(row);
 }
-// 澶氶�夋閫変腑鏁版嵁
-function handleSelectionChange(selection) {
-  userIds.value = selection.map(item => item.userId);
+/** 澶氶�夋閫変腑鏁版嵁 */
+const handleSelectionChange = (selection: UserVO[]) => {
+  userIds.value = selection.map((item: UserVO) => item.userId);
 }
-// 鏌ヨ琛ㄦ暟鎹�
-function getList() {
-  unallocatedUserList(queryParams).then(res => {
-    userList.value = res.rows;
-    total.value = res.total;
-  });
+
+/** 鏌ヨ鏁版嵁 */
+const getList = async () => {
+	const res = await unallocatedUserList(queryParams);
+	userList.value = res.rows;
+	total.value = res.total;
 }
 /** 鎼滅储鎸夐挳鎿嶄綔 */
-function handleQuery() {
+const handleQuery = () => {
   queryParams.pageNum = 1;
   getList();
 }
 /** 閲嶇疆鎸夐挳鎿嶄綔 */
-function resetQuery() {
-  proxy.resetForm("queryRef");
-  handleQuery();
-}
-const emit = defineEmits(["ok"]);
-/** 閫夋嫨鎺堟潈鐢ㄦ埛鎿嶄綔 */
-function handleSelectUser() {
-  const roleId = queryParams.roleId;
-  const uIds = userIds.value.join(",");
-  if (uIds == "") {
-    proxy.$modal.msgError("璇烽�夋嫨瑕佸垎閰嶇殑鐢ㄦ埛");
-    return;
-  }
-  authUserSelectAll({ roleId: roleId, userIds: uIds }).then(res => {
-    proxy.$modal.msgSuccess(res.msg);
-    if (res.code === 200) {
-      visible.value = false;
-      emit("ok");
-    }
-  });
+const resetQuery = () => {
+  queryFormRef.value.resetFields();
+  getList();
 }
 
+const emit = defineEmits(["ok"]);
+/**閫夋嫨鎺堟潈鐢ㄦ埛鎿嶄綔 */
+const handleSelectUser = async () => {
+  const roleId = queryParams.roleId;
+  const ids = userIds.value.join(',');
+  if (ids == "") {
+    proxy?.$modal.msgError('璇烽�夋嫨瑕佸垎閰嶇殑鐢ㄦ埛');
+    return;
+  }
+	await authUserSelectAll({ roleId, userIds: ids });
+	proxy?.$modal.msgSuccess('鍒嗛厤鎴愬姛');
+	emit('ok');
+	visible.value = false;
+}
+// 鏆撮湶
 defineExpose({
   show,
 });
 </script>
+
+<template>
+	<el-row>
+		<el-dialog title="閫夋嫨鐢ㄦ埛" v-model="visible" width="800px" top="5vh" append-to-body>
+			<el-form :model="queryParams" ref="queryFormRef" :inline="true">
+				<el-form-item label="鐢ㄦ埛鍚嶇О" prop="userName">
+					<el-input v-model="queryParams.userName" placeholder="璇疯緭鍏ョ敤鎴峰悕绉�" clearable @keyup.enter="handleQuery" />
+				</el-form-item>
+				<el-form-item label="鎵嬫満鍙风爜" prop="phonenumber">
+					<el-input v-model="queryParams.phonenumber" placeholder="璇疯緭鍏ユ墜鏈哄彿鐮�" clearable @keyup.enter="handleQuery" />
+				</el-form-item>
+				<el-form-item>
+					<el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
+					<el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
+				</el-form-item>
+			</el-form>
+			<el-row>
+				<el-table @row-click="clickRow" ref="tableRef" :data="userList" @selection-change="handleSelectionChange" height="260px">
+					<el-table-column type="selection" width="55"></el-table-column>
+					<el-table-column label="鐢ㄦ埛鍚嶇О" prop="userName" :show-overflow-tooltip="true" />
+					<el-table-column label="鐢ㄦ埛鏄电О" prop="nickName" :show-overflow-tooltip="true" />
+					<el-table-column label="閭" prop="email" :show-overflow-tooltip="true" />
+					<el-table-column label="鎵嬫満" prop="phonenumber" :show-overflow-tooltip="true" />
+					<el-table-column label="鐘舵��" align="center" prop="status">
+						<template #default="scope">
+							<dict-tag :options="sys_normal_disable" :value="scope.row.status" />
+						</template>
+					</el-table-column>
+					<el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime" width="180">
+						<template #default="scope">
+							<span>{{ parseTime(scope.row.createTime) }}</span>
+						</template>
+					</el-table-column>
+				</el-table>
+				<pagination v-if="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
+			</el-row>
+			<template #footer>
+				<div class="dialog-footer">
+					<el-button type="primary" @click="handleSelectUser">纭� 瀹�</el-button>
+					<el-button @click="visible = false">鍙� 娑�</el-button>
+				</div>
+			</template>
+		</el-dialog>
+	</el-row>
+</template>
+
+<style scoped></style>
diff --git a/src/views/system/tenant/index.vue b/src/views/system/tenant/index.vue
index 6ba11bd..8feddb8 100644
--- a/src/views/system/tenant/index.vue
+++ b/src/views/system/tenant/index.vue
@@ -1,234 +1,66 @@
-<template>
-  <div class="app-container">
-    <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px">
-      <el-form-item label="绉熸埛缂栧彿" prop="tenantId">
-        <el-input
-          v-model="queryParams.tenantId"
-          placeholder="璇疯緭鍏ョ鎴风紪鍙�"
-          clearable
-          @keyup.enter="handleQuery"
-        />
-      </el-form-item>
-      <el-form-item label="鑱旂郴浜�" prop="contactUserName">
-        <el-input
-          v-model="queryParams.contactUserName"
-          placeholder="璇疯緭鍏ヨ仈绯讳汉"
-          clearable
-          @keyup.enter="handleQuery"
-        />
-      </el-form-item>
-      <el-form-item label="鑱旂郴鐢佃瘽" prop="contactPhone">
-        <el-input
-          v-model="queryParams.contactPhone"
-          placeholder="璇疯緭鍏ヨ仈绯荤數璇�"
-          clearable
-          @keyup.enter="handleQuery"
-        />
-      </el-form-item>
-      <el-form-item label="浼佷笟鍚嶇О" prop="companyName">
-        <el-input
-          v-model="queryParams.companyName"
-          placeholder="璇疯緭鍏ヤ紒涓氬悕绉�"
-          clearable
-          @keyup.enter="handleQuery"
-        />
-      </el-form-item>
-      <el-form-item>
-        <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
-        <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
-      </el-form-item>
-    </el-form>
+<script setup name="Tenant" lang="ts">
+import { listTenant, getTenant, delTenant, addTenant, updateTenant, changeTenantStatus, syncTenantPackage} from '@/api/system/tenant';
+import { listTenantPackage } from '@/api/system/tenantPackage';
+import { TenantForm, TenantQuery, TenantVO } from '@/api/system/tenant/types';
+import { TenantPkgVO } from '@/api/system/tenantPackage/types';
+import { ComponentInternalInstance } from 'vue';
+import to from 'await-to-js';
 
-    <el-row :gutter="10" class="mb8">
-      <el-col :span="1.5">
-        <el-button
-          type="primary"
-          plain
-          icon="Plus"
-          @click="handleAdd"
-          v-hasPermi="['system:tenant:add']"
-        >鏂板</el-button>
-      </el-col>
-      <el-col :span="1.5">
-        <el-button
-          type="success"
-          plain
-          icon="Edit"
-          :disabled="single"
-          @click="handleUpdate"
-          v-hasPermi="['system:tenant:edit']"
-        >淇敼</el-button>
-      </el-col>
-      <el-col :span="1.5">
-        <el-button
-          type="danger"
-          plain
-          icon="Delete"
-          :disabled="multiple"
-          @click="handleDelete"
-          v-hasPermi="['system:tenant:remove']"
-        >鍒犻櫎</el-button>
-      </el-col>
-      <el-col :span="1.5">
-        <el-button
-          type="warning"
-          plain
-          icon="Download"
-          @click="handleExport"
-          v-hasPermi="['system:tenant:export']"
-        >瀵煎嚭</el-button>
-      </el-col>
-      <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
-    </el-row>
 
-    <el-table v-loading="loading" :data="tenantList" @selection-change="handleSelectionChange">
-      <el-table-column type="selection" width="55" align="center" />
-      <el-table-column label="id" align="center" prop="id" v-if="false"/>
-      <el-table-column label="绉熸埛缂栧彿" align="center" prop="tenantId" />
-      <el-table-column label="鑱旂郴浜�" align="center" prop="contactUserName" />
-      <el-table-column label="鑱旂郴鐢佃瘽" align="center" prop="contactPhone" />
-      <el-table-column label="浼佷笟鍚嶇О" align="center" prop="companyName" />
-      <el-table-column label="绀句細淇$敤浠g爜" align="center" prop="licenseNumber" />
-      <el-table-column label="杩囨湡鏃堕棿" align="center" prop="expireTime" width="180">
-        <template #default="scope">
-          <span>{{ parseTime(scope.row.expireTime, '{y}-{m}-{d}') }}</span>
-        </template>
-      </el-table-column>
-      <el-table-column label="绉熸埛鐘舵��" align="center" prop="status">
-        <template #default="scope">
-          <el-switch
-            v-model="scope.row.status"
-            active-value="0"
-            inactive-value="1"
-            @change="handleStatusChange(scope.row)"
-          ></el-switch>
-        </template>
-      </el-table-column>
-      <el-table-column label="鎿嶄綔" align="center" class-name="small-padding fixed-width">
-        <template #default="scope">
-          <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:tenant:edit']">淇敼</el-button>
-          <el-button link type="primary" icon="Edit" @click="handleSyncTenantPackage(scope.row)" v-hasPermi="['system:tenant:edit']">鍚屾濂楅</el-button>
-          <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:tenant:remove']">鍒犻櫎</el-button>
-        </template>
-      </el-table-column>
-    </el-table>
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 
-    <pagination
-      v-show="total>0"
-      :total="total"
-      :page.sync="queryParams.pageNum"
-      :limit.sync="queryParams.pageSize"
-      @pagination="getList"
-    />
-
-    <!-- 娣诲姞鎴栦慨鏀圭鎴峰璇濇 -->
-    <el-dialog :title="title" v-model="open" width="500px" append-to-body>
-      <el-form ref="tenantRef" :model="form" :rules="rules" label-width="80px">
-        <el-form-item label="浼佷笟鍚嶇О" prop="companyName">
-          <el-input v-model="form.companyName" placeholder="璇疯緭鍏ヤ紒涓氬悕绉�" />
-        </el-form-item>
-        <el-form-item label="鑱旂郴浜�" prop="contactUserName">
-          <el-input v-model="form.contactUserName" placeholder="璇疯緭鍏ヨ仈绯讳汉" />
-        </el-form-item>
-        <el-form-item label="鑱旂郴鐢佃瘽" prop="contactPhone">
-          <el-input v-model="form.contactPhone" placeholder="璇疯緭鍏ヨ仈绯荤數璇�" />
-        </el-form-item>
-        <el-form-item v-if="form.id == undefined" label="鐢ㄦ埛鍚�" prop="username">
-          <el-input v-model="form.username" placeholder="璇疯緭鍏ョ郴缁熺敤鎴峰悕" maxlength="30"/>
-        </el-form-item>
-        <el-form-item v-if="form.id == undefined" label="鐢ㄦ埛瀵嗙爜" prop="password">
-          <el-input type="password" v-model="form.password" placeholder="璇疯緭鍏ョ郴缁熺敤鎴峰瘑鐮�" maxlength="20"/>
-        </el-form-item>
-        <el-form-item label="绉熸埛濂楅" prop="packageId">
-          <el-select v-model="form.packageId" :disabled="form.tenantId" placeholder="璇烽�夋嫨绉熸埛濂楅" clearable style="width: 100%">
-            <el-option v-for="item in packageList" :key="item.packageId" :label="item.packageName" :value="item.packageId"/>
-          </el-select>
-        </el-form-item>
-        <el-form-item label="杩囨湡鏃堕棿" prop="expireTime">
-          <el-date-picker clearable
-                          v-model="form.expireTime"
-                          type="datetime"
-                          value-format="YYYY-MM-DD HH:mm:ss"
-                          placeholder="璇烽�夋嫨杩囨湡鏃堕棿">
-          </el-date-picker>
-        </el-form-item>
-        <el-form-item label="鐢ㄦ埛鏁伴噺" prop="accountCount">
-          <el-input v-model="form.accountCount" placeholder="璇疯緭鍏ョ敤鎴锋暟閲�" />
-        </el-form-item>
-        <el-form-item label="鍦板潃" prop="address">
-          <el-input v-model="form.address" placeholder="璇疯緭鍏ュ湴鍧�" />
-        </el-form-item>
-        <el-form-item label="浼佷笟浠g爜" prop="licenseNumber">
-          <el-input v-model="form.licenseNumber" placeholder="璇疯緭鍏ョ粺涓�绀句細淇$敤浠g爜" />
-        </el-form-item>
-        <el-form-item label="浼佷笟绠�浠�" prop="intro">
-          <el-input type="textarea" v-model="form.intro" placeholder="璇疯緭鍏ヤ紒涓氱畝浠�" />
-        </el-form-item>
-        <el-form-item label="澶囨敞" prop="remark">
-          <el-input v-model="form.remark" placeholder="璇疯緭鍏ュ娉�" />
-        </el-form-item>
-      </el-form>
-      <div slot="footer" class="dialog-footer">
-        <el-button :loading="buttonLoading" type="primary" @click="submitForm">纭� 瀹�</el-button>
-        <el-button @click="cancel">鍙� 娑�</el-button>
-      </div>
-    </el-dialog>
-  </div>
-</template>
-
-<script setup name="Tenant">
-import { listTenant, getTenant, delTenant, addTenant, updateTenant, changeTenantStatus, syncTenantPackage} from "@/api/system/tenant";
-import { listTenantPackage } from "@/api/system/tenantPackage";
-
-const { proxy } = getCurrentInstance();
-
-const tenantList = ref([]);
-const packageList = ref([]);
-const open = ref(false);
+const tenantList = ref<TenantVO[]>([]);
+const packageList = ref<TenantPkgVO[]>([]);
 const buttonLoading = ref(false);
 const loading = ref(true);
 const showSearch = ref(true);
-const ids = ref([]);
+const ids = ref<Array<string | number>>([]);
 const single = ref(true);
 const multiple = ref(true);
 const total = ref(0);
-const title = ref("");
 
-const data = reactive({
-  form: {},
-  packageList: [],
+const queryFormRef = ref(ElForm);
+const tenantFormRef = ref(ElForm);
+
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+
+const initFormData: TenantForm = {
+  id: undefined,
+  tenantId: undefined,
+  contactUserName: '',
+  contactPhone: '',
+  username: '',
+  password: '',
+  companyName: '',
+  licenseNumber: '',
+  domain: '',
+  address: '',
+  intro: '',
+  remark: '',
+  packageId: '',
+  expireTime: '',
+  accountCount: 0,
+  status: '0',
+}
+const data = reactive<PageData<TenantForm, TenantQuery>>({
+  form: {...initFormData},
   queryParams: {
     pageNum: 1,
     pageSize: 10,
-    tenantId: undefined,
-    contactUserName: undefined,
-    contactPhone: undefined,
-    companyName: undefined,
-    licenseNumber: undefined,
-    address: undefined,
-    intro: undefined,
-    domain: undefined,
-    packageId: undefined,
-    expireTime: undefined,
-    accountCount: undefined,
-    status: undefined,
+    tenantId: '',
+    contactUserName: '',
+    contactPhone: '',
+    companyName: ''
   },
   rules: {
-    id: [
-      { required: true, message: "id涓嶈兘涓虹┖", trigger: "blur" }
-    ],
-    tenantId: [
-      { required: true, message: "绉熸埛缂栧彿涓嶈兘涓虹┖", trigger: "blur" }
-    ],
-    contactUserName: [
-      { required: true, message: "鑱旂郴浜轰笉鑳戒负绌�", trigger: "blur" }
-    ],
-    contactPhone: [
-      { required: true, message: "鑱旂郴鐢佃瘽涓嶈兘涓虹┖", trigger: "blur" }
-    ],
-    companyName: [
-      { required: true, message: "浼佷笟鍚嶇О涓嶈兘涓虹┖", trigger: "blur" }
-    ],
+    id: [{ required: true, message: "id涓嶈兘涓虹┖", trigger: "blur" }],
+    tenantId: [{ required: true, message: "绉熸埛缂栧彿涓嶈兘涓虹┖", trigger: "blur" }],
+    contactUserName: [{ required: true, message: "鑱旂郴浜轰笉鑳戒负绌�", trigger: "blur" }],
+    contactPhone: [{ required: true, message: "鑱旂郴鐢佃瘽涓嶈兘涓虹┖", trigger: "blur" }],
+    companyName: [{ required: true, message: "浼佷笟鍚嶇О涓嶈兘涓虹┖", trigger: "blur" }],
     username: [
       { required: true, message: "鐢ㄦ埛鍚嶄笉鑳戒负绌�", trigger: "blur" },
       { min: 2, max: 20, message: '鐢ㄦ埛鍚嶇О闀垮害蹇呴』浠嬩簬 2 鍜� 20 涔嬮棿', trigger: 'blur' }
@@ -243,166 +75,280 @@
 const { queryParams, form, rules } = toRefs(data);
 
 /** 鏌ヨ鎵�鏈夌鎴峰椁� */
-function getTenantPackage() {
-  listTenantPackage().then(res =>{
-    packageList.value = res.rows;
-  })
+const getTenantPackage = async () => {
+  const res = await listTenantPackage()
+  packageList.value = res.rows;
 }
 
 /** 鏌ヨ绉熸埛鍒楄〃 */
-function getList() {
+const getList = async () => {
   loading.value = true;
-  listTenant(queryParams.value).then(response => {
-    tenantList.value = response.rows;
-    total.value = response.total;
-    loading.value = false;
-  });
+  const res = await listTenant(queryParams.value);
+  tenantList.value = res.rows;
+  total.value = res.total;
+  loading.value = false;
 }
 
 // 绉熸埛濂楅鐘舵�佷慨鏀�
-function handleStatusChange(row) {
+const handleStatusChange = async (row: TenantVO) => {
   let text = row.status === "0" ? "鍚敤" : "鍋滅敤";
-  proxy.$modal.confirm('纭瑕�"' + text + '""' + row.companyName + '"绉熸埛鍚楋紵').then(function() {
-    return changeTenantStatus(row.id, row.tenantId, row.status);
-  }).then(() => {
-    proxy.$modal.msgSuccess(text + "鎴愬姛");
-  }).catch(function() {
+  try {
+    await proxy?.$modal.confirm('纭瑕�"' + text + '""' + row.companyName + '"绉熸埛鍚楋紵');
+    await changeTenantStatus(row.id, row.tenantId, row.status);
+    proxy?.$modal.msgSuccess(text + "鎴愬姛");
+  } catch {
     row.status = row.status === "0" ? "1" : "0";
-  });
+  }
+
+
 }
 
 // 鍙栨秷鎸夐挳
-function cancel() {
-  open.value = false;
+const cancel = () => {
   reset();
+  dialog.visible = false;
 }
 
 // 琛ㄥ崟閲嶇疆
-function reset() {
-  form.value = {
-    id: undefined,
-    tenantId: undefined,
-    contactUserName: undefined,
-    contactPhone: undefined,
-    username: undefined,
-    password: undefined,
-    companyName: undefined,
-    licenseNumber: undefined,
-    address: undefined,
-    intro: undefined,
-    remark: undefined,
-    packageId: undefined,
-    expireTime: undefined,
-    accountCount: undefined,
-    status: undefined
-  };
-  proxy.resetForm("tenantRef");
+const reset = () => {
+  form.value = {...initFormData};
+  tenantFormRef.value.resetFields();
 }
 
 /** 鎼滅储鎸夐挳鎿嶄綔 */
-function handleQuery() {
+const handleQuery = () => {
   queryParams.value.pageNum = 1;
   getList();
 }
 
 /** 閲嶇疆鎸夐挳鎿嶄綔 */
-function resetQuery() {
-  proxy.resetForm("queryRef");
+const resetQuery = () => {
+  queryFormRef.value.resetFields();
   handleQuery();
 }
 
 // 澶氶�夋閫変腑鏁版嵁
-function handleSelectionChange(selection) {
+const handleSelectionChange = (selection: TenantVO[]) => {
   ids.value = selection.map(item => item.id);
   single.value = selection.length != 1;
   multiple.value = !selection.length;
 }
 
 /** 鏂板鎸夐挳鎿嶄綔 */
-function handleAdd() {
-  reset();
-  getTenantPackage();
-  open.value = true;
-  title.value = "娣诲姞绉熸埛";
+const handleAdd = () => {
+  dialog.visible = true;
+  dialog.title = "娣诲姞绉熸埛";
+  nextTick(() => {
+    reset();
+    getTenantPackage();
+  })
 }
 
 /** 淇敼鎸夐挳鎿嶄綔 */
-function handleUpdate(row) {
-  loading.value = true
-  reset();
-  getTenantPackage();
-  const _id = row.id || ids.value
-  getTenant(_id).then(response => {
+const handleUpdate = (row?: TenantVO) => {
+  loading.value = true;
+  dialog.visible = true;
+  dialog.title = "淇敼绉熸埛";
+  nextTick(async () => {
+    reset();
+    getTenantPackage();
+    const _id = row?.id || ids.value[0];
+    const res = await getTenant(_id);
     loading.value = false;
-    form.value = response.data;
-    open.value = true;
-    title.value = "淇敼绉熸埛";
-  });
+    Object.assign(form.value, res.data)
+  })
 }
 
 /** 鎻愪氦鎸夐挳 */
-function submitForm() {
-  proxy.$refs["tenantRef"].validate(valid => {
+const submitForm = () => {
+  tenantFormRef.value.validate(async (valid: boolean) => {
     if (valid) {
       buttonLoading.value = true;
-      if (form.value.id != null) {
-        updateTenant(form.value).then(response => {
-          proxy.$modal.msgSuccess("淇敼鎴愬姛");
-          open.value = false;
-          getList();
-        }).finally(() => {
-          buttonLoading.value = false;
-        });
+      if (form.value.id) {
+        await updateTenant(form.value).finally(() => buttonLoading.value = false);
       } else {
-        addTenant(form.value).then(response => {
-          proxy.$modal.msgSuccess("鏂板鎴愬姛");
-          open.value = false;
-          getList();
-        }).finally(() => {
-          buttonLoading.value = false;
-        });
+        await addTenant(form.value).finally(() => buttonLoading.value = false);
       }
+      proxy?.$modal.msgSuccess("鎿嶄綔鎴愬姛");
+      dialog.visible = false;
+      getList();
     }
   });
 }
 
 /** 鍒犻櫎鎸夐挳鎿嶄綔 */
-function handleDelete(row) {
-  const _ids = row.id || ids.value;
-  proxy.$modal.confirm('鏄惁纭鍒犻櫎绉熸埛缂栧彿涓�"' + _ids + '"鐨勬暟鎹」锛�').then(function() {
-    loading.value = true;
-    return delTenant(_ids);
-  }).then(() => {
-    loading.value = true;
-    getList();
-    proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
-  }).catch(() => {
-  }).finally(() => {
-    loading.value = false;
-  });
+const handleDelete = async (row?: TenantVO) => {
+  const _ids = row?.id || ids.value;
+  await proxy?.$modal.confirm('鏄惁纭鍒犻櫎绉熸埛缂栧彿涓�"' + _ids + '"鐨勬暟鎹」锛�')
+  loading.value = true;
+  await delTenant(_ids).finally(() => loading.value = false);
+  getList();
+  proxy?.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+
+
 }
 
 /** 鍚屾绉熸埛濂楅鎸夐挳鎿嶄綔 */
-function handleSyncTenantPackage(row) {
-  proxy.$modal.confirm('鏄惁纭鍚屾绉熸埛濂楅绉熸埛缂栧彿涓�"' + row.tenantId + '"鐨勬暟鎹」锛�').then(() => {
+const handleSyncTenantPackage = async (row: TenantVO) => {
+  try {
+    await proxy?.$modal.confirm('鏄惁纭鍚屾绉熸埛濂楅绉熸埛缂栧彿涓�"' + row.tenantId + '"鐨勬暟鎹」锛�');
     loading.value = true;
-    return syncTenantPackage(row.tenantId, row.packageId);
-  }).then(() => {
-    loading.value = true;
+    await syncTenantPackage(row.tenantId, row.packageId);
     getList();
-    proxy.$modal.msgSuccess("鍚屾鎴愬姛");
-  }).catch(() => {
-  }).finally(() => {
+    proxy?.$modal.msgSuccess("鍚屾鎴愬姛");
+  } catch {return} finally {
     loading.value = false;
-  });
+  }
 }
 
 /** 瀵煎嚭鎸夐挳鎿嶄綔 */
-function handleExport() {
-  proxy.download('system/tenant/export', {
+const handleExport = () => {
+  proxy?.download('system/tenant/export', {
     ...queryParams.value
   }, `tenant_${new Date().getTime()}.xlsx`)
 }
 
-getList();
+onMounted(() => {
+  getList();
+})
 </script>
+
+<template>
+	<div class="p-2">
+		<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
+			<div class="search" v-show="showSearch">
+				<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
+					<el-form-item label="绉熸埛缂栧彿" prop="tenantId">
+						<el-input v-model="queryParams.tenantId" placeholder="璇疯緭鍏ョ鎴风紪鍙�" clearable @keyup.enter="handleQuery" />
+					</el-form-item>
+					<el-form-item label="鑱旂郴浜�" prop="contactUserName">
+						<el-input v-model="queryParams.contactUserName" placeholder="璇疯緭鍏ヨ仈绯讳汉" clearable @keyup.enter="handleQuery" />
+					</el-form-item>
+					<el-form-item label="鑱旂郴鐢佃瘽" prop="contactPhone">
+						<el-input v-model="queryParams.contactPhone" placeholder="璇疯緭鍏ヨ仈绯荤數璇�" clearable @keyup.enter="handleQuery" />
+					</el-form-item>
+					<el-form-item label="浼佷笟鍚嶇О" prop="companyName">
+						<el-input v-model="queryParams.companyName" placeholder="璇疯緭鍏ヤ紒涓氬悕绉�" clearable @keyup.enter="handleQuery" />
+					</el-form-item>
+					<el-form-item>
+						<el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
+						<el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
+					</el-form-item>
+				</el-form>
+			</div>
+		</transition>
+
+		<el-card shadow="never">
+			<template #header>
+				<el-row :gutter="10" class="mb8">
+					<el-col :span="1.5">
+						<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:tenant:add']">鏂板</el-button>
+					</el-col>
+					<el-col :span="1.5">
+						<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['system:tenant:edit']"
+							>淇敼</el-button
+						>
+					</el-col>
+					<el-col :span="1.5">
+						<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['system:tenant:remove']">
+							鍒犻櫎
+						</el-button>
+					</el-col>
+					<el-col :span="1.5">
+						<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['system:tenant:export']">瀵煎嚭</el-button>
+					</el-col>
+					<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+				</el-row>
+			</template>
+
+			<el-table v-loading="loading" :data="tenantList" @selection-change="handleSelectionChange">
+				<el-table-column type="selection" width="55" align="center" />
+				<el-table-column label="id" align="center" prop="id" v-if="false" />
+				<el-table-column label="绉熸埛缂栧彿" align="center" prop="tenantId" />
+				<el-table-column label="鑱旂郴浜�" align="center" prop="contactUserName" />
+				<el-table-column label="鑱旂郴鐢佃瘽" align="center" prop="contactPhone" />
+				<el-table-column label="浼佷笟鍚嶇О" align="center" prop="companyName" />
+				<el-table-column label="绀句細淇$敤浠g爜" align="center" prop="licenseNumber" />
+				<el-table-column label="杩囨湡鏃堕棿" align="center" prop="expireTime" width="180">
+					<template #default="scope">
+						<span>{{ parseTime(scope.row.expireTime, '{y}-{m}-{d}') }}</span>
+					</template>
+				</el-table-column>
+				<el-table-column label="绉熸埛鐘舵��" align="center" prop="status">
+					<template #default="scope">
+						<el-switch v-model="scope.row.status" active-value="0" inactive-value="1" @change="handleStatusChange(scope.row)"></el-switch>
+					</template>
+				</el-table-column>
+				<el-table-column label="鎿嶄綔" align="center" class-name="small-padding fixed-width">
+					<template #default="scope">
+						<el-tooltip content="淇敼" placement="top">
+							<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:tenant:edit']"></el-button>
+						</el-tooltip>
+						<el-tooltip content="鍚屾濂楅" placement="top">
+							<el-button link type="primary" icon="Refresh" @click="handleSyncTenantPackage(scope.row)" v-hasPermi="['system:tenant:edit']">
+							</el-button>
+						</el-tooltip>
+						<el-tooltip content="鍒犻櫎" placement="top">
+							<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:tenant:remove']"></el-button>
+						</el-tooltip>
+					</template>
+				</el-table-column>
+			</el-table>
+
+			<pagination v-show="total>0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
+		</el-card>
+		<!-- 娣诲姞鎴栦慨鏀圭鎴峰璇濇 -->
+		<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
+			<el-form ref="tenantFormRef" :model="form" :rules="rules" label-width="80px">
+				<el-form-item label="浼佷笟鍚嶇О" prop="companyName">
+					<el-input v-model="form.companyName" placeholder="璇疯緭鍏ヤ紒涓氬悕绉�" />
+				</el-form-item>
+				<el-form-item label="鑱旂郴浜�" prop="contactUserName">
+					<el-input v-model="form.contactUserName" placeholder="璇疯緭鍏ヨ仈绯讳汉" />
+				</el-form-item>
+				<el-form-item label="鑱旂郴鐢佃瘽" prop="contactPhone">
+					<el-input v-model="form.contactPhone" placeholder="璇疯緭鍏ヨ仈绯荤數璇�" />
+				</el-form-item>
+				<el-form-item v-if="!form.id" label="鐢ㄦ埛鍚�" prop="username">
+					<el-input v-model="form.username" placeholder="璇疯緭鍏ョ郴缁熺敤鎴峰悕" maxlength="30" />
+				</el-form-item>
+				<el-form-item v-if="!form.id" label="鐢ㄦ埛瀵嗙爜" prop="password">
+					<el-input type="password" v-model="form.password" placeholder="璇疯緭鍏ョ郴缁熺敤鎴峰瘑鐮�" maxlength="20" />
+				</el-form-item>
+				<el-form-item label="绉熸埛濂楅" prop="packageId">
+					<el-select v-model="form.packageId" :disabled="!!form.tenantId" placeholder="璇烽�夋嫨绉熸埛濂楅" clearable style="width: 100%">
+						<el-option v-for="item in packageList" :key="item.packageId" :label="item.packageName" :value="item.packageId" />
+					</el-select>
+				</el-form-item>
+				<el-form-item label="杩囨湡鏃堕棿" prop="expireTime">
+					<el-date-picker clearable v-model="form.expireTime" type="datetime" value-format="YYYY-MM-DD HH:mm:ss" placeholder="璇烽�夋嫨杩囨湡鏃堕棿">
+					</el-date-picker>
+				</el-form-item>
+				<el-form-item label="鐢ㄦ埛鏁伴噺" prop="accountCount">
+					<el-input v-model="form.accountCount" placeholder="璇疯緭鍏ョ敤鎴锋暟閲�" />
+				</el-form-item>
+				<el-form-item label="缁戝畾鍩熷悕" prop="domain">
+					<el-input v-model="form.domain" placeholder="璇疯緭鍏ョ粦瀹氬煙鍚�" />
+				</el-form-item>
+				<el-form-item label="浼佷笟鍦板潃" prop="address">
+					<el-input v-model="form.address" placeholder="璇疯緭鍏ヤ紒涓氬湴鍧�" />
+				</el-form-item>
+				<el-form-item label="浼佷笟浠g爜" prop="licenseNumber">
+					<el-input v-model="form.licenseNumber" placeholder="璇疯緭鍏ョ粺涓�绀句細淇$敤浠g爜" />
+				</el-form-item>
+				<el-form-item label="浼佷笟绠�浠�" prop="intro">
+					<el-input type="textarea" v-model="form.intro" placeholder="璇疯緭鍏ヤ紒涓氱畝浠�" />
+				</el-form-item>
+				<el-form-item label="澶囨敞" prop="remark">
+					<el-input v-model="form.remark" placeholder="璇疯緭鍏ュ娉�" />
+				</el-form-item>
+			</el-form>
+			<template #footer>
+				<div class="dialog-footer">
+					<el-button :loading="buttonLoading" type="primary" @click="submitForm">纭� 瀹�</el-button>
+					<el-button @click="cancel">鍙� 娑�</el-button>
+				</div>
+			</template>
+		</el-dialog>
+	</div>
+</template>
diff --git a/src/views/system/tenantPackage/index.vue b/src/views/system/tenantPackage/index.vue
index ae33d13..6a3a494 100644
--- a/src/views/system/tenantPackage/index.vue
+++ b/src/views/system/tenantPackage/index.vue
@@ -1,362 +1,333 @@
-<template>
-  <div class="app-container">
-    <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px">
-      <el-form-item label="濂楅鍚嶇О" prop="packageName">
-        <el-input
-          v-model="queryParams.packageName"
-          placeholder="璇疯緭鍏ュ椁愬悕绉�"
-          clearable
-          @keyup.enter="handleQuery"
-        />
-      </el-form-item>
-      <el-form-item>
-        <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
-        <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
-      </el-form-item>
-    </el-form>
-
-    <el-row :gutter="10" class="mb8">
-      <el-col :span="1.5">
-        <el-button
-          type="primary"
-          plain
-          icon="Plus"
-          @click="handleAdd"
-          v-hasPermi="['system:tenantPackage:add']"
-        >鏂板</el-button>
-      </el-col>
-      <el-col :span="1.5">
-        <el-button
-          type="success"
-          plain
-          icon="Edit"
-          :disabled="single"
-          @click="handleUpdate"
-          v-hasPermi="['system:tenantPackage:edit']"
-        >淇敼</el-button>
-      </el-col>
-      <el-col :span="1.5">
-        <el-button
-          type="danger"
-          plain
-          icon="Delete"
-          :disabled="multiple"
-          @click="handleDelete"
-          v-hasPermi="['system:tenantPackage:remove']"
-        >鍒犻櫎</el-button>
-      </el-col>
-      <el-col :span="1.5">
-        <el-button
-          type="warning"
-          plain
-          icon="Download"
-          @click="handleExport"
-          v-hasPermi="['system:tenantPackage:export']"
-        >瀵煎嚭</el-button>
-      </el-col>
-      <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
-    </el-row>
-
-    <el-table v-loading="loading" :data="tenantPackageList" @selection-change="handleSelectionChange">
-      <el-table-column type="selection" width="55" align="center" />
-      <el-table-column label="绉熸埛濂楅id" align="center" prop="packageId" v-if="false"/>
-      <el-table-column label="濂楅鍚嶇О" align="center" prop="packageName" />
-      <el-table-column label="澶囨敞" align="center" prop="remark" />
-      <el-table-column label="鐘舵��" align="center" prop="status" >
-        <template #default="scope">
-          <el-switch
-            v-model="scope.row.status"
-            active-value="0"
-            inactive-value="1"
-            @change="handleStatusChange(scope.row)"
-          ></el-switch>
-        </template>
-      </el-table-column>
-      <el-table-column label="鎿嶄綔" align="center" class-name="small-padding fixed-width">
-        <template #default="scope">
-          <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:tenantPackage:edit']">淇敼</el-button>
-          <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:tenantPackage:remove']">鍒犻櫎</el-button>
-        </template>
-      </el-table-column>
-    </el-table>
-
-    <pagination
-      v-show="total>0"
-      :total="total"
-      :page.sync="queryParams.pageNum"
-      :limit.sync="queryParams.pageSize"
-      @pagination="getList"
-    />
-
-    <!-- 娣诲姞鎴栦慨鏀圭鎴峰椁愬璇濇 -->
-    <el-dialog :title="title" v-model="open" width="500px" append-to-body>
-      <el-form ref="tenantPackageRef" :model="form" :rules="rules" label-width="80px">
-        <el-form-item label="濂楅鍚嶇О" prop="packageName">
-          <el-input v-model="form.packageName" placeholder="璇疯緭鍏ュ椁愬悕绉�" />
-        </el-form-item>
-        <el-form-item label="鍏宠仈鑿滃崟">
-          <el-checkbox v-model="menuExpand" @change="handleCheckedTreeExpand($event, 'menu')">灞曞紑/鎶樺彔</el-checkbox>
-          <el-checkbox v-model="menuNodeAll" @change="handleCheckedTreeNodeAll($event, 'menu')">鍏ㄩ��/鍏ㄤ笉閫�</el-checkbox>
-          <el-checkbox v-model="form.menuCheckStrictly" @change="handleCheckedTreeConnect($event, 'menu')">鐖跺瓙鑱斿姩</el-checkbox>
-          <el-tree
-            class="tree-border"
-            :data="menuOptions"
-            show-checkbox
-            ref="menuRef"
-            node-key="id"
-            :check-strictly="!form.menuCheckStrictly"
-            empty-text="鍔犺浇涓紝璇风◢鍊�"
-            :props="{ label: 'label', children: 'children' }"
-          ></el-tree>
-        </el-form-item>
-        <el-form-item label="澶囨敞" prop="remark">
-          <el-input v-model="form.remark" placeholder="璇疯緭鍏ュ娉�" />
-        </el-form-item>
-      </el-form>
-      <div slot="footer" class="dialog-footer">
-        <el-button :loading="buttonLoading" type="primary" @click="submitForm">纭� 瀹�</el-button>
-        <el-button @click="cancel">鍙� 娑�</el-button>
-      </div>
-    </el-dialog>
-  </div>
-</template>
-
-<script setup name="TenantPackage">
+<script setup name="TenantPackage" lang="ts">
 import { listTenantPackage, getTenantPackage, delTenantPackage, addTenantPackage, updateTenantPackage, changePackageStatus } from "@/api/system/tenantPackage";
 import { treeselect as menuTreeselect, tenantPackageMenuTreeselect } from "@/api/system/menu";
+import { ComponentInternalInstance } from "vue";
+import { TenantPkgForm, TenantPkgQuery, TenantPkgVO } from "@/api/system/tenantPackage/types";
+import { MenuTreeOption } from "@/api/system/menu/types";
+import { CheckboxValueType, ElTree, ElForm } from 'element-plus';
+import to from "await-to-js";
 
-const { proxy } = getCurrentInstance();
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 
-const tenantPackageList = ref([]);
-const open = ref(false);
+const tenantPackageList = ref<TenantPkgVO[]>([]);
 const buttonLoading = ref(false);
 const loading = ref(true);
 const showSearch = ref(true);
-const ids = ref([]);
+const ids = ref<Array<string | number>>([]);
 const single = ref(true);
 const multiple = ref(true);
 const total = ref(0);
-const title = ref("");
 const menuExpand = ref(false);
 const menuNodeAll = ref(false);
-const menuOptions = ref([]);
+const menuOptions = ref<MenuTreeOption[]>([]);
 
-const data = reactive({
-  form: {},
+const menuTreeRef = ref(ElTree);
+const queryFormRef = ref(ElForm);
+const tenantPackageFormRef = ref(ElForm);
+
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+
+
+const initFormData: TenantPkgForm = {
+  packageId: undefined,
+  packageName: '',
+  menuIds: '',
+  remark: '',
+  menuCheckStrictly: true,
+  status: ''
+};
+const data = reactive<PageData<TenantPkgForm, TenantPkgQuery>>({
+  form: {...initFormData},
   queryParams: {
     pageNum: 1,
     pageSize: 10,
-    packageName: undefined,
-    status: undefined,
+    packageName: '',
+    status: '',
   },
   rules: {
-    packageId: [
-      { required: true, message: "绉熸埛濂楅id涓嶈兘涓虹┖", trigger: "blur" }
-    ],
-    packageName: [
-      { required: true, message: "濂楅鍚嶇О涓嶈兘涓虹┖", trigger: "blur" }
-    ]
+    packageId: [{ required: true, message: "绉熸埛濂楅id涓嶈兘涓虹┖", trigger: "blur" }],
+    packageName: [{ required: true, message: "濂楅鍚嶇О涓嶈兘涓虹┖", trigger: "blur" }]
   }
 });
 
 const { queryParams, form, rules } = toRefs(data);
 
 /** 鏌ヨ鑿滃崟鏍戠粨鏋� */
-function getMenuTreeselect() {
-  menuTreeselect().then(response => {
-    menuOptions.value = response.data;
-  });
+const getMenuTreeselect = async() => {
+  const { data } = await menuTreeselect();
+  menuOptions.value = data;
 }
 
 // 鎵�鏈夎彍鍗曡妭鐐规暟鎹�
-function getMenuAllCheckedKeys() {
+const getMenuAllCheckedKeys = () => {
   // 鐩墠琚�変腑鐨勮彍鍗曡妭鐐�
-  let checkedKeys = proxy.$refs.menuRef.getCheckedKeys();
+  let checkedKeys = menuTreeRef.value.getCheckedKeys();
   // 鍗婇�変腑鐨勮彍鍗曡妭鐐�
-  let halfCheckedKeys = proxy.$refs.menuRef.getHalfCheckedKeys();
+  let halfCheckedKeys = menuTreeRef.value.getHalfCheckedKeys();
   checkedKeys.unshift.apply(checkedKeys, halfCheckedKeys);
   return checkedKeys;
 }
 
 /** 鏍规嵁绉熸埛濂楅ID鏌ヨ鑿滃崟鏍戠粨鏋� */
-function getPackageMenuTreeselect(packageId) {
-  return tenantPackageMenuTreeselect(packageId).then(response => {
-    menuOptions.value = response.data.menus;
-    return response;
-  });
+const getPackageMenuTreeselect = async(packageId: string | number) => {
+  const res = await tenantPackageMenuTreeselect(packageId);
+  menuOptions.value = res.data.menus;
+  return Promise.resolve(res);
 }
 
 /** 鏌ヨ绉熸埛濂楅鍒楄〃 */
-function getList() {
+const getList = async () => {
   loading.value = true;
-  listTenantPackage(queryParams.value).then(response => {
-    tenantPackageList.value = response.rows;
-    total.value = response.total;
-    loading.value = false;
-  });
+  const res = await listTenantPackage(queryParams.value);
+  tenantPackageList.value = res.rows;
+  total.value = res.total;
+  loading.value = false;
 }
 
 // 绉熸埛濂楅鐘舵�佷慨鏀�
-function handleStatusChange(row) {
+const handleStatusChange = async (row: TenantPkgVO) => {
   let text = row.status === "0" ? "鍚敤" : "鍋滅敤";
-  proxy.$modal.confirm('纭瑕�"' + text + '""' + row.packageName + '"濂楅鍚楋紵').then(function() {
-    return changePackageStatus(row.packageId, row.status);
-  }).then(() => {
-    proxy.$modal.msgSuccess(text + "鎴愬姛");
-  }).catch(function() {
+  const [err] = await to(proxy?.$modal.confirm('纭瑕�"' + text + '""' + row.packageName + '"濂楅鍚楋紵') as Promise<any>)
+  if (err) {
     row.status = row.status === "0" ? "1" : "0";
-  });
+  } else {
+    await changePackageStatus(row.packageId, row.status);
+    proxy?.$modal.msgSuccess(text + "鎴愬姛");
+  }
 }
 
 // 鍙栨秷鎸夐挳
-function cancel() {
-  open.value = false;
+const cancel = () => {
   reset();
+  dialog.visible = false;
 }
 
 // 琛ㄥ崟閲嶇疆
-function reset() {
-  if (proxy.$refs.menuRef != undefined) {
-    proxy.$refs.menuRef.setCheckedKeys([]);
-  }
+const reset = () => {
+  menuTreeRef.value.setCheckedKeys([]);
   menuExpand.value = false;
   menuNodeAll.value = false;
-  form.value = {
-    packageId: undefined,
-    packageName: undefined,
-    menuIds: undefined,
-    remark: undefined,
-    menuCheckStrictly: true,
-    status: undefined
-  };
-  proxy.resetForm("tenantPackageRef");
+  form.value = {...initFormData};
+  tenantPackageFormRef.value.resetFields();
 }
 
 /** 鎼滅储鎸夐挳鎿嶄綔 */
-function handleQuery() {
+const handleQuery = () => {
   queryParams.value.pageNum = 1;
   getList();
 }
 
 /** 閲嶇疆鎸夐挳鎿嶄綔 */
-function resetQuery() {
-  proxy.resetForm("queryRef");
+const resetQuery = () => {
+  queryFormRef.value.resetFields();
   handleQuery();
 }
 
 // 澶氶�夋閫変腑鏁版嵁
-function handleSelectionChange(selection) {
+const handleSelectionChange = (selection: TenantPkgVO[]) => {
   ids.value = selection.map(item => item.packageId);
   single.value = selection.length != 1;
   multiple.value = !selection.length;
 }
 
 // 鏍戞潈闄愶紙灞曞紑/鎶樺彔锛�
-function handleCheckedTreeExpand(value, type) {
+const handleCheckedTreeExpand = (value: CheckboxValueType, type: string) => {
   if (type == 'menu') {
     let treeList = menuOptions.value;
     for (let i = 0; i < treeList.length; i++) {
-      proxy.$refs.menuRef.store.nodesMap[treeList[i].id].expanded = value;
+      menuTreeRef.value.store.nodesMap[treeList[i].id].expanded = value;
     }
   }
 }
 
 // 鏍戞潈闄愶紙鍏ㄩ��/鍏ㄤ笉閫夛級
-function handleCheckedTreeNodeAll(value, type) {
+const handleCheckedTreeNodeAll = (value: CheckboxValueType, type: string) => {
   if (type == 'menu') {
-    proxy.$refs.menuRef.setCheckedNodes(value ? this.menuOptions: []);
+    menuTreeRef.value.setCheckedNodes(value ? menuOptions.value: []);
   }
 }
 
 // 鏍戞潈闄愶紙鐖跺瓙鑱斿姩锛�
-function handleCheckedTreeConnect(value, type) {
+const handleCheckedTreeConnect = (value: CheckboxValueType, type: string) => {
   if (type == 'menu') {
-    form.value.menuCheckStrictly = value ? true: false;
+    form.value.menuCheckStrictly = value as boolean;
   }
 }
 
 /** 鏂板鎸夐挳鎿嶄綔 */
-function handleAdd() {
-  reset();
-  getMenuTreeselect();
-  open.value = true;
-  title.value = "娣诲姞绉熸埛濂楅";
+const handleAdd = () => {
+  dialog.visible = true;
+  dialog.title = "娣诲姞绉熸埛濂楅";
+  nextTick(() => {
+    reset();
+    getMenuTreeselect();
+  })
 }
 
 /** 淇敼鎸夐挳鎿嶄綔 */
-function handleUpdate(row) {
+const handleUpdate = (row?: TenantPkgVO) => {
   loading.value = true
-  reset();
-  const _packageId = row.packageId || ids.value
-  const packageMenu = getPackageMenuTreeselect(_packageId);
-  getTenantPackage(_packageId).then(response => {
+  dialog.visible = true;
+  dialog.title = "淇敼绉熸埛濂楅";
+  nextTick(async () => {
+    reset();
+    const _packageId = row?.packageId || ids.value[0];
+    const packageMenu = getPackageMenuTreeselect(_packageId);
+    const response = await getTenantPackage(_packageId);
     loading.value = false;
     form.value = response.data;
-    open.value = true;
-    nextTick(() => {
-      packageMenu.then(res => {
-        let checkedKeys = res.data.checkedKeys
+    nextTick(async () => {
+      const res = await packageMenu;
+      let checkedKeys = res.data.checkedKeys
         checkedKeys.forEach((v) => {
           nextTick(() => {
-            proxy.$refs.menuRef.setChecked(v, true ,false);
+            menuTreeRef.value.setChecked(v, true ,false);
           })
         })
-      });
     });
-    title.value = "淇敼绉熸埛濂楅";
-  });
+  })
 }
 
 /** 鎻愪氦鎸夐挳 */
-function submitForm() {
-  proxy.$refs["tenantPackageRef"].validate(valid => {
+const submitForm = () => {
+  tenantPackageFormRef.value.validate(async (valid: boolean) => {
     if (valid) {
       buttonLoading.value = true;
+      form.value.menuIds = getMenuAllCheckedKeys();
       if (form.value.packageId != null) {
-        form.value.menuIds = getMenuAllCheckedKeys();
-        updateTenantPackage(form.value).then(response => {
-          proxy.$modal.msgSuccess("淇敼鎴愬姛");
-          open.value = false;
-          getList();
-        }).finally(() => {
-          buttonLoading.value = false;
-        });
+        await updateTenantPackage(form.value).finally(() => buttonLoading.value = false);
       } else {
-        form.value.menuIds = getMenuAllCheckedKeys();
-        addTenantPackage(form.value).then(response => {
-          proxy.$modal.msgSuccess("鏂板鎴愬姛");
-          open.value = false;
-          getList();
-        }).finally(() => {
-          buttonLoading.value = false;
-        });
+        await addTenantPackage(form.value).finally(() => buttonLoading.value = false);
       }
+      proxy?.$modal.msgSuccess("鎿嶄綔鎴愬姛");
+      dialog.visible = false;
+      getList();
     }
   });
 }
 
 /** 鍒犻櫎鎸夐挳鎿嶄綔 */
-function handleDelete(row) {
-  const _packageIds = row.packageId || ids.value;
-  proxy.$modal.confirm('鏄惁纭鍒犻櫎绉熸埛濂楅缂栧彿涓�"' + _packageIds + '"鐨勬暟鎹」锛�').then(function() {
-    loading.value = true;
-    return delTenantPackage(_packageIds);
-  }).then(() => {
-    loading.value = true;
-    getList();
-    proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
-  }).catch(() => {
-  }).finally(() => {
-    loading.value = false;
+const handleDelete = async (row?: TenantPkgVO) => {
+  const _packageIds = row?.packageId || ids.value;
+  await proxy?.$modal.confirm('鏄惁纭鍒犻櫎绉熸埛濂楅缂栧彿涓�"' + _packageIds + '"鐨勬暟鎹」锛�').finally(() => {
+  loading.value = false;
   });
+  await delTenantPackage(_packageIds);
+  loading.value = true;
+  getList();
+  proxy?.$modal.msgSuccess("鍒犻櫎鎴愬姛");
 }
 
 /** 瀵煎嚭鎸夐挳鎿嶄綔 */
-function handleExport() {
-  proxy.download('system/tenantPackage/export', {
+const handleExport = () => {
+  proxy?.download('system/tenantPackage/export', {
     ...queryParams.value
   }, `tenantPackage_${new Date().getTime()}.xlsx`)
 }
 
-getList();
+onMounted(() => {
+  getList();
+})
 </script>
+
+<template>
+	<div class="p-2">
+		<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
+			<div class="search" v-show="showSearch">
+				<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
+					<el-form-item label="濂楅鍚嶇О" prop="packageName">
+						<el-input v-model="queryParams.packageName" placeholder="璇疯緭鍏ュ椁愬悕绉�" clearable @keyup.enter="handleQuery" />
+					</el-form-item>
+					<el-form-item>
+						<el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
+						<el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
+					</el-form-item>
+				</el-form>
+			</div>
+		</transition>
+
+		<el-card shadow="never">
+			<template #header>
+				<el-row :gutter="10" class="mb8">
+					<el-col :span="1.5">
+						<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:tenantPackage:add']">鏂板</el-button>
+					</el-col>
+					<el-col :span="1.5">
+						<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['system:tenantPackage:edit']"
+							>淇敼</el-button
+						>
+					</el-col>
+					<el-col :span="1.5">
+						<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['system:tenantPackage:remove']"
+							>鍒犻櫎</el-button
+						>
+					</el-col>
+					<el-col :span="1.5">
+						<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['system:tenantPackage:export']">瀵煎嚭</el-button>
+					</el-col>
+					<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+				</el-row>
+			</template>
+
+			<el-table v-loading="loading" :data="tenantPackageList" @selection-change="handleSelectionChange">
+				<el-table-column type="selection" width="55" align="center" />
+				<el-table-column label="绉熸埛濂楅id" align="center" prop="packageId" v-if="false" />
+				<el-table-column label="濂楅鍚嶇О" align="center" prop="packageName" />
+				<el-table-column label="澶囨敞" align="center" prop="remark" />
+				<el-table-column label="鐘舵��" align="center" prop="status">
+					<template #default="scope">
+						<el-switch v-model="scope.row.status" active-value="0" inactive-value="1" @click="handleStatusChange(scope.row)"></el-switch>
+					</template>
+				</el-table-column>
+				<el-table-column label="鎿嶄綔" align="center" class-name="small-padding fixed-width">
+					<template #default="scope">
+						<el-tooltip content="淇敼" placement="top">
+							<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:tenantPackage:edit']"></el-button>
+						</el-tooltip>
+						<el-tooltip content="鍒犻櫎" placement="top">
+							<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:tenantPackage:remove']"> </el-button>
+						</el-tooltip>
+					</template>
+				</el-table-column>
+			</el-table>
+
+			<pagination v-show="total>0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
+		</el-card>
+
+		<!-- 娣诲姞鎴栦慨鏀圭鎴峰椁愬璇濇 -->
+		<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
+			<el-form ref="tenantPackageFormRef" :model="form" :rules="rules" label-width="80px">
+				<el-form-item label="濂楅鍚嶇О" prop="packageName">
+					<el-input v-model="form.packageName" placeholder="璇疯緭鍏ュ椁愬悕绉�" />
+				</el-form-item>
+				<el-form-item label="鍏宠仈鑿滃崟">
+					<el-checkbox v-model="menuExpand" @change="handleCheckedTreeExpand($event, 'menu')">灞曞紑/鎶樺彔</el-checkbox>
+					<el-checkbox v-model="menuNodeAll" @change="handleCheckedTreeNodeAll($event, 'menu')">鍏ㄩ��/鍏ㄤ笉閫�</el-checkbox>
+					<el-checkbox v-model="form.menuCheckStrictly" @change="handleCheckedTreeConnect($event, 'menu')">鐖跺瓙鑱斿姩</el-checkbox>
+					<el-tree
+						class="tree-border"
+						:data="menuOptions"
+						show-checkbox
+						ref="menuTreeRef"
+						node-key="id"
+						:check-strictly="!form.menuCheckStrictly"
+						empty-text="鍔犺浇涓紝璇风◢鍊�"
+						:props="{ label: 'label', children: 'children' }"
+					></el-tree>
+				</el-form-item>
+				<el-form-item label="澶囨敞" prop="remark">
+					<el-input v-model="form.remark" placeholder="璇疯緭鍏ュ娉�" />
+				</el-form-item>
+			</el-form>
+			<template #footer>
+				<div class="dialog-footer">
+					<el-button :loading="buttonLoading" type="primary" @click="submitForm">纭� 瀹�</el-button>
+					<el-button @click="cancel">鍙� 娑�</el-button>
+				</div>
+			</template>
+		</el-dialog>
+	</div>
+</template>
diff --git a/src/views/system/user/authRole.vue b/src/views/system/user/authRole.vue
index 5f1f2fe..0616f67 100644
--- a/src/views/system/user/authRole.vue
+++ b/src/views/system/user/authRole.vue
@@ -1,112 +1,127 @@
-<template>
-   <div class="app-container">
-      <h4 class="form-header h4">鍩烘湰淇℃伅</h4>
-      <el-form :model="form" label-width="80px">
-         <el-row>
-            <el-col :span="8" :offset="2">
-               <el-form-item label="鐢ㄦ埛鏄电О" prop="nickName">
-                  <el-input v-model="form.nickName" disabled />
-               </el-form-item>
-            </el-col>
-            <el-col :span="8" :offset="2">
-               <el-form-item label="鐧诲綍璐﹀彿" prop="userName">
-                  <el-input v-model="form.userName" disabled />
-               </el-form-item>
-            </el-col>
-         </el-row>
-      </el-form>
-
-      <h4 class="form-header h4">瑙掕壊淇℃伅</h4>
-      <el-table v-loading="loading" :row-key="getRowKey" @row-click="clickRow" ref="roleRef" @selection-change="handleSelectionChange" :data="roles.slice((pageNum - 1) * pageSize, pageNum * pageSize)">
-         <el-table-column label="搴忓彿" width="55" type="index" align="center">
-            <template #default="scope">
-               <span>{{ (pageNum - 1) * pageSize + scope.$index + 1 }}</span>
-            </template>
-         </el-table-column>
-         <el-table-column type="selection" :reserve-selection="true" width="55"></el-table-column>
-         <el-table-column label="瑙掕壊缂栧彿" align="center" prop="roleId" />
-         <el-table-column label="瑙掕壊鍚嶇О" align="center" prop="roleName" />
-         <el-table-column label="鏉冮檺瀛楃" align="center" prop="roleKey" />
-         <el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime" width="180">
-            <template #default="scope">
-               <span>{{ parseTime(scope.row.createTime) }}</span>
-            </template>
-         </el-table-column>
-      </el-table>
-
-      <pagination v-show="total > 0" :total="total" v-model:page="pageNum" v-model:limit="pageSize" />
-
-      <el-form label-width="100px">
-         <div style="text-align: center;margin-left:-120px;margin-top:30px;">
-            <el-button type="primary" @click="submitForm()">鎻愪氦</el-button>
-            <el-button @click="close()">杩斿洖</el-button>
-         </div>
-      </el-form>
-   </div>
-</template>
-
-<script setup name="AuthRole">
-import { getAuthRole, updateAuthRole } from "@/api/system/user";
-
+<script setup name="AuthRole" lang="ts">
+import { RoleVO } from '@/api/system/role/types';
+import { getAuthRole, updateAuthRole } from '@/api/system/user';
+import { UserForm } from '@/api/system/user/types';
+import { ElTable } from "element-plus";
+import { ComponentInternalInstance } from 'vue';
 const route = useRoute();
-const { proxy } = getCurrentInstance();
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 
 const loading = ref(true);
 const total = ref(0);
 const pageNum = ref(1);
 const pageSize = ref(10);
-const roleIds = ref([]);
-const roles = ref([]);
-const form = ref({
+const roleIds = ref<Array<string | number>>([]);
+const roles = ref<RoleVO[]>([]);
+const form = ref<Partial<UserForm>>({
   nickName: undefined,
-  userName: undefined,
+  userName: '',
   userId: undefined
 });
 
+const tableRef = ref(ElTable)
+
 /** 鍗曞嚮閫変腑琛屾暟鎹� */
-function clickRow(row) {
-  proxy.$refs["roleRef"].toggleRowSelection(row);
+const clickRow = (row: RoleVO) => {
+  tableRef.value.toggleRowSelection(row);
 };
 /** 澶氶�夋閫変腑鏁版嵁 */
-function handleSelectionChange(selection) {
+const handleSelectionChange = (selection: RoleVO[]) => {
   roleIds.value = selection.map(item => item.roleId);
 };
 /** 淇濆瓨閫変腑鐨勬暟鎹紪鍙� */
-function getRowKey(row) {
-  return row.roleId;
+const getRowKey = (row: RoleVO): string => {
+  return String(row.roleId);
 };
 /** 鍏抽棴鎸夐挳 */
-function close() {
+const close = () => {
   const obj = { path: "/system/user" };
-  proxy.$tab.closeOpenPage(obj);
+  proxy?.$tab.closeOpenPage(obj);
 };
 /** 鎻愪氦鎸夐挳 */
-function submitForm() {
+const submitForm = async () => {
   const userId = form.value.userId;
   const rIds = roleIds.value.join(",");
-  updateAuthRole({ userId: userId, roleIds: rIds }).then(response => {
-    proxy.$modal.msgSuccess("鎺堟潈鎴愬姛");
-    close();
-  });
+  await updateAuthRole({ userId: userId as string, roleIds: rIds })
+	proxy?.$modal.msgSuccess("鎺堟潈鎴愬姛");
+	close();
 };
 
-(() => {
+const getList = async() => {
   const userId = route.params && route.params.userId;
   if (userId) {
     loading.value = true;
-    getAuthRole(userId).then(response => {
-      form.value = response.data.user;
-      roles.value = response.data.roles;
-      total.value = roles.value.length;
-      nextTick(() => {
-        roles.value.forEach(row => {
-          if (row.flag) {
-            proxy.$refs["roleRef"].toggleRowSelection(row);
-          }
-        });
-      });
-      loading.value = false;
-    });
+		const res = await getAuthRole(userId as string);
+		Object.assign(form.value, res.data.user)
+		Object.assign(roles.value, res.data.roles)
+		total.value = roles.value.length;
+		await nextTick(() => {
+			roles.value.forEach(row => {
+				if (row?.flag) {
+					tableRef.value.toggleRowSelection(row);
+				}
+			});
+		});
+		loading.value = false;
   }
-})();
+}
+onMounted(() => {
+  getList();
+})
 </script>
+
+<template>
+	<div class="p-2">
+		<div class="panel">
+			<h4 class="panel-title">鍩烘湰淇℃伅</h4>
+			<el-form :model="form" label-width="80px" :inline="true">
+				<el-row :gutter="10">
+					<el-col :span="2.5">
+						<el-form-item label="鐢ㄦ埛鏄电О" prop="nickName">
+							<el-input v-model="form.nickName" disabled />
+						</el-form-item>
+					</el-col>
+					<el-col :span="2.5">
+						<el-form-item label="鐧诲綍璐﹀彿" prop="userName">
+							<el-input v-model="form.userName" disabled />
+						</el-form-item>
+					</el-col>
+				</el-row>
+			</el-form>
+		</div>
+		<div class="panel">
+			<h4 class="panel-title">瑙掕壊淇℃伅</h4>
+			<div>
+				<el-table
+					v-loading="loading"
+					:row-key="getRowKey"
+					@row-click="clickRow"
+					ref="tableRef"
+					@selection-change="handleSelectionChange"
+					:data="roles.slice((pageNum - 1) * pageSize, pageNum * pageSize)"
+				>
+					<el-table-column label="搴忓彿" width="55" type="index" align="center">
+						<template #default="scope">
+							<span>{{ (pageNum - 1) * pageSize + scope.$index + 1 }}</span>
+						</template>
+					</el-table-column>
+					<el-table-column type="selection" :reserve-selection="true" width="55"></el-table-column>
+					<el-table-column label="瑙掕壊缂栧彿" align="center" prop="roleId" />
+					<el-table-column label="瑙掕壊鍚嶇О" align="center" prop="roleName" />
+					<el-table-column label="鏉冮檺瀛楃" align="center" prop="roleKey" />
+					<el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime" width="180">
+						<template #default="scope">
+							<span>{{ parseTime(scope.row.createTime) }}</span>
+						</template>
+					</el-table-column>
+				</el-table>
+				<pagination v-show="total > 0" :total="total" v-model:page="pageNum" v-model:limit="pageSize" />
+				<div style="text-align: center;margin-left:-120px;margin-top:30px;">
+					<el-button type="primary" @click="submitForm()">鎻愪氦</el-button>
+					<el-button @click="close()">杩斿洖</el-button>
+				</div>
+				<div></div>
+			</div>
+		</div>
+	</div>
+</template>
diff --git a/src/views/system/user/index.vue b/src/views/system/user/index.vue
index 4deae86..0746f7a 100644
--- a/src/views/system/user/index.vue
+++ b/src/views/system/user/index.vue
@@ -1,360 +1,43 @@
-<template>
-   <div class="app-container">
-      <el-row :gutter="20">
-         <!--閮ㄩ棬鏁版嵁-->
-         <el-col :span="4" :xs="24">
-            <div class="head-container">
-               <el-input
-                  v-model="deptName"
-                  placeholder="璇疯緭鍏ラ儴闂ㄥ悕绉�"
-                  clearable
-                  prefix-icon="Search"
-                  style="margin-bottom: 20px"
-               />
-            </div>
-            <div class="head-container">
-               <el-tree
-                  :data="deptOptions"
-                  :props="{ label: 'label', children: 'children' }"
-                  :expand-on-click-node="false"
-                  :filter-node-method="filterNode"
-                  ref="deptTreeRef"
-                  node-key="id"
-                  highlight-current
-                  default-expand-all
-                  @node-click="handleNodeClick"
-               />
-            </div>
-         </el-col>
-         <!--鐢ㄦ埛鏁版嵁-->
-         <el-col :span="20" :xs="24">
-            <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px">
-               <el-form-item label="鐢ㄦ埛鍚嶇О" prop="userName">
-                  <el-input
-                     v-model="queryParams.userName"
-                     placeholder="璇疯緭鍏ョ敤鎴峰悕绉�"
-                     clearable
-                     style="width: 240px"
-                     @keyup.enter="handleQuery"
-                  />
-               </el-form-item>
-               <el-form-item label="鎵嬫満鍙风爜" prop="phonenumber">
-                  <el-input
-                     v-model="queryParams.phonenumber"
-                     placeholder="璇疯緭鍏ユ墜鏈哄彿鐮�"
-                     clearable
-                     style="width: 240px"
-                     @keyup.enter="handleQuery"
-                  />
-               </el-form-item>
-               <el-form-item label="鐘舵��" prop="status">
-                  <el-select
-                     v-model="queryParams.status"
-                     placeholder="鐢ㄦ埛鐘舵��"
-                     clearable
-                     style="width: 240px"
-                  >
-                     <el-option
-                        v-for="dict in sys_normal_disable"
-                        :key="dict.value"
-                        :label="dict.label"
-                        :value="dict.value"
-                     />
-                  </el-select>
-               </el-form-item>
-               <el-form-item label="鍒涘缓鏃堕棿" style="width: 308px;">
-                  <el-date-picker
-                     v-model="dateRange"
-                     value-format="YYYY-MM-DD HH:mm:ss"
-                     type="daterange"
-                     range-separator="-"
-                     start-placeholder="寮�濮嬫棩鏈�"
-                     end-placeholder="缁撴潫鏃ユ湡"
-                     :default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"
-                  ></el-date-picker>
-               </el-form-item>
-               <el-form-item>
-                  <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
-                  <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
-               </el-form-item>
-            </el-form>
-
-            <el-row :gutter="10" class="mb8">
-               <el-col :span="1.5">
-                  <el-button
-                     type="primary"
-                     plain
-                     icon="Plus"
-                     @click="handleAdd"
-                     v-hasPermi="['system:user:add']"
-                  >鏂板</el-button>
-               </el-col>
-               <el-col :span="1.5">
-                  <el-button
-                     type="success"
-                     plain
-                     icon="Edit"
-                     :disabled="single"
-                     @click="handleUpdate"
-                     v-hasPermi="['system:user:edit']"
-                  >淇敼</el-button>
-               </el-col>
-               <el-col :span="1.5">
-                  <el-button
-                     type="danger"
-                     plain
-                     icon="Delete"
-                     :disabled="multiple"
-                     @click="handleDelete"
-                     v-hasPermi="['system:user:remove']"
-                  >鍒犻櫎</el-button>
-               </el-col>
-               <el-col :span="1.5">
-                  <el-button
-                     type="info"
-                     plain
-                     icon="Upload"
-                     @click="handleImport"
-                     v-hasPermi="['system:user:import']"
-                  >瀵煎叆</el-button>
-               </el-col>
-               <el-col :span="1.5">
-                  <el-button
-                     type="warning"
-                     plain
-                     icon="Download"
-                     @click="handleExport"
-                     v-hasPermi="['system:user:export']"
-                  >瀵煎嚭</el-button>
-               </el-col>
-               <right-toolbar v-model:showSearch="showSearch" @queryTable="getList" :columns="columns"></right-toolbar>
-            </el-row>
-
-            <el-table v-loading="loading" :data="userList" @selection-change="handleSelectionChange">
-               <el-table-column type="selection" width="50" align="center" />
-               <el-table-column label="鐢ㄦ埛缂栧彿" align="center" key="userId" prop="userId" v-if="columns[0].visible" />
-               <el-table-column label="鐢ㄦ埛鍚嶇О" align="center" key="userName" prop="userName" v-if="columns[1].visible" :show-overflow-tooltip="true" />
-               <el-table-column label="鐢ㄦ埛鏄电О" align="center" key="nickName" prop="nickName" v-if="columns[2].visible" :show-overflow-tooltip="true" />
-               <el-table-column label="閮ㄩ棬" align="center" key="deptName" prop="dept.deptName" v-if="columns[3].visible" :show-overflow-tooltip="true" />
-               <el-table-column label="鎵嬫満鍙风爜" align="center" key="phonenumber" prop="phonenumber" v-if="columns[4].visible" width="120" />
-               <el-table-column label="鐘舵��" align="center" key="status" v-if="columns[5].visible">
-                  <template #default="scope">
-                     <el-switch
-                        v-model="scope.row.status"
-                        active-value="0"
-                        inactive-value="1"
-                        @change="handleStatusChange(scope.row)"
-                     ></el-switch>
-                  </template>
-               </el-table-column>
-               <el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime" v-if="columns[6].visible" width="160">
-                  <template #default="scope">
-                     <span>{{ parseTime(scope.row.createTime) }}</span>
-                  </template>
-               </el-table-column>
-               <el-table-column label="鎿嶄綔" align="center" width="150" class-name="small-padding fixed-width">
-                  <template #default="scope">
-                     <el-tooltip content="淇敼" placement="top" v-if="scope.row.userId !== 1">
-                        <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:user:edit']"></el-button>
-                     </el-tooltip>
-                     <el-tooltip content="鍒犻櫎" placement="top" v-if="scope.row.userId !== 1">
-                        <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:user:remove']"></el-button>
-                     </el-tooltip>
-                     <el-tooltip content="閲嶇疆瀵嗙爜" placement="top" v-if="scope.row.userId !== 1">
-                         <el-button link type="primary" icon="Key" @click="handleResetPwd(scope.row)" v-hasPermi="['system:user:resetPwd']"></el-button>
-                     </el-tooltip>
-                     <el-tooltip content="鍒嗛厤瑙掕壊" placement="top" v-if="scope.row.userId !== 1">
-                        <el-button link type="primary" icon="CircleCheck" @click="handleAuthRole(scope.row)" v-hasPermi="['system:user:edit']"></el-button>
-                     </el-tooltip>
-                  </template>
-               </el-table-column>
-            </el-table>
-            <pagination
-               v-show="total > 0"
-               :total="total"
-               v-model:page="queryParams.pageNum"
-               v-model:limit="queryParams.pageSize"
-               @pagination="getList"
-            />
-         </el-col>
-      </el-row>
-
-      <!-- 娣诲姞鎴栦慨鏀圭敤鎴烽厤缃璇濇 -->
-      <el-dialog :title="title" v-model="open" width="600px" append-to-body>
-         <el-form :model="form" :rules="rules" ref="userRef" label-width="80px">
-            <el-row>
-               <el-col :span="12">
-                  <el-form-item label="鐢ㄦ埛鏄电О" prop="nickName">
-                     <el-input v-model="form.nickName" placeholder="璇疯緭鍏ョ敤鎴锋樀绉�" maxlength="30" />
-                  </el-form-item>
-               </el-col>
-               <el-col :span="12">
-                  <el-form-item label="褰掑睘閮ㄩ棬" prop="deptId">
-                     <el-tree-select
-                        v-model="form.deptId"
-                        :data="deptOptions"
-                        :props="{ value: 'id', label: 'label', children: 'children' }"
-                        value-key="id"
-                        placeholder="璇烽�夋嫨褰掑睘閮ㄩ棬"
-                        check-strictly
-                     />
-                  </el-form-item>
-               </el-col>
-            </el-row>
-            <el-row>
-               <el-col :span="12">
-                  <el-form-item label="鎵嬫満鍙风爜" prop="phonenumber">
-                     <el-input v-model="form.phonenumber" placeholder="璇疯緭鍏ユ墜鏈哄彿鐮�" maxlength="11" />
-                  </el-form-item>
-               </el-col>
-               <el-col :span="12">
-                  <el-form-item label="閭" prop="email">
-                     <el-input v-model="form.email" placeholder="璇疯緭鍏ラ偖绠�" maxlength="50" />
-                  </el-form-item>
-               </el-col>
-            </el-row>
-            <el-row>
-               <el-col :span="12">
-                  <el-form-item v-if="form.userId == undefined" label="鐢ㄦ埛鍚嶇О" prop="userName">
-                     <el-input v-model="form.userName" placeholder="璇疯緭鍏ョ敤鎴峰悕绉�" maxlength="30" />
-                  </el-form-item>
-               </el-col>
-               <el-col :span="12">
-                  <el-form-item v-if="form.userId == undefined" label="鐢ㄦ埛瀵嗙爜" prop="password">
-                     <el-input v-model="form.password" placeholder="璇疯緭鍏ョ敤鎴峰瘑鐮�" type="password" maxlength="20" show-password />
-                  </el-form-item>
-               </el-col>
-            </el-row>
-            <el-row>
-               <el-col :span="12">
-                  <el-form-item label="鐢ㄦ埛鎬у埆">
-                     <el-select v-model="form.sex" placeholder="璇烽�夋嫨">
-                        <el-option
-                           v-for="dict in sys_user_sex"
-                           :key="dict.value"
-                           :label="dict.label"
-                           :value="dict.value"
-                        ></el-option>
-                     </el-select>
-                  </el-form-item>
-               </el-col>
-               <el-col :span="12">
-                  <el-form-item label="鐘舵��">
-                     <el-radio-group v-model="form.status">
-                        <el-radio
-                           v-for="dict in sys_normal_disable"
-                           :key="dict.value"
-                           :label="dict.value"
-                        >{{ dict.label }}</el-radio>
-                     </el-radio-group>
-                  </el-form-item>
-               </el-col>
-            </el-row>
-            <el-row>
-               <el-col :span="12">
-                  <el-form-item label="宀椾綅">
-                     <el-select v-model="form.postIds" multiple placeholder="璇烽�夋嫨">
-                        <el-option
-                           v-for="item in postOptions"
-                           :key="item.postId"
-                           :label="item.postName"
-                           :value="item.postId"
-                           :disabled="item.status == 1"
-                        ></el-option>
-                     </el-select>
-                  </el-form-item>
-               </el-col>
-               <el-col :span="12">
-                  <el-form-item label="瑙掕壊">
-                     <el-select v-model="form.roleIds" multiple placeholder="璇烽�夋嫨">
-                        <el-option
-                           v-for="item in roleOptions"
-                           :key="item.roleId"
-                           :label="item.roleName"
-                           :value="item.roleId"
-                           :disabled="item.status == 1"
-                        ></el-option>
-                     </el-select>
-                  </el-form-item>
-               </el-col>
-            </el-row>
-            <el-row>
-               <el-col :span="24">
-                  <el-form-item label="澶囨敞">
-                     <el-input v-model="form.remark" type="textarea" placeholder="璇疯緭鍏ュ唴瀹�"></el-input>
-                  </el-form-item>
-               </el-col>
-            </el-row>
-         </el-form>
-         <template #footer>
-            <div class="dialog-footer">
-               <el-button type="primary" @click="submitForm">纭� 瀹�</el-button>
-               <el-button @click="cancel">鍙� 娑�</el-button>
-            </div>
-         </template>
-      </el-dialog>
-
-      <!-- 鐢ㄦ埛瀵煎叆瀵硅瘽妗� -->
-      <el-dialog :title="upload.title" v-model="upload.open" width="400px" append-to-body>
-         <el-upload
-            ref="uploadRef"
-            :limit="1"
-            accept=".xlsx, .xls"
-            :headers="upload.headers"
-            :action="upload.url + '?updateSupport=' + upload.updateSupport"
-            :disabled="upload.isUploading"
-            :on-progress="handleFileUploadProgress"
-            :on-success="handleFileSuccess"
-            :auto-upload="false"
-            drag
-         >
-            <el-icon class="el-icon--upload"><upload-filled /></el-icon>
-            <div class="el-upload__text">灏嗘枃浠舵嫋鍒版澶勶紝鎴�<em>鐐瑰嚮涓婁紶</em></div>
-            <template #tip>
-               <div class="el-upload__tip text-center">
-                  <div class="el-upload__tip">
-                     <el-checkbox v-model="upload.updateSupport" />鏄惁鏇存柊宸茬粡瀛樺湪鐨勭敤鎴锋暟鎹�
-                  </div>
-                  <span>浠呭厑璁稿鍏ls銆亁lsx鏍煎紡鏂囦欢銆�</span>
-                  <el-link type="primary" :underline="false" style="font-size:12px;vertical-align: baseline;" @click="importTemplate">涓嬭浇妯℃澘</el-link>
-               </div>
-            </template>
-         </el-upload>
-         <template #footer>
-            <div class="dialog-footer">
-               <el-button type="primary" @click="submitFileForm">纭� 瀹�</el-button>
-               <el-button @click="upload.open = false">鍙� 娑�</el-button>
-            </div>
-         </template>
-      </el-dialog>
-   </div>
-</template>
-
-<script setup name="User">
+<script setup name="User" lang="ts">
+import {
+  changeUserStatus,
+  listUser,
+  resetUserPwd,
+  delUser,
+  getUser,
+  updateUser,
+  addUser,
+  deptTreeSelect
+} from "@/api/system/user"
+import { UserForm, UserQuery, UserVO } from '@/api/system/user/types';
+import { ComponentInternalInstance } from "vue";
 import { getToken } from "@/utils/auth";
-import { changeUserStatus, listUser, resetUserPwd, delUser, getUser, updateUser, addUser, deptTreeSelect } from "@/api/system/user";
-
+import { treeselect } from "@/api/system/dept";
+import { DeptVO } from "@/api/system/dept/types";
+import { RoleVO } from "@/api/system/role/types";
+import { PostVO } from "@/api/system/post/types";
+import { DateModelType, ElTree, ElUpload, UploadFile, UploadFiles, ElForm } from 'element-plus';
+import { to } from "await-to-js";
 const router = useRouter();
-const { proxy } = getCurrentInstance();
-const { sys_normal_disable, sys_user_sex } = proxy.useDict("sys_normal_disable", "sys_user_sex");
+const { proxy } = getCurrentInstance() as ComponentInternalInstance
+const { sys_normal_disable, sys_user_sex } = toRefs<any>(proxy?.useDict('sys_normal_disable', 'sys_user_sex'));
 
-const userList = ref([]);
-const open = ref(false);
+
+const userList = ref<UserVO[]>();
 const loading = ref(true);
-const showSearch = ref(true);
-const ids = ref([]);
+const showSearch = ref(true)
+const ids = ref<Array<number | string>>([]);
 const single = ref(true);
 const multiple = ref(true);
 const total = ref(0);
-const title = ref("");
-const dateRange = ref([]);
-const deptName = ref("");
-const deptOptions = ref(undefined);
-const initPassword = ref(undefined);
-const postOptions = ref([]);
-const roleOptions = ref([]);
+const dateRange = ref<[DateModelType, DateModelType]>(['','']);
+const deptName = ref('');
+const deptOptions = ref<DeptVO[]>([]);
+const initPassword = ref('123456');
+const postOptions = ref<PostVO[]>([]);
+const roleOptions = ref<RoleVO[]>([]);
 /*** 鐢ㄦ埛瀵煎叆鍙傛暟 */
-const upload = reactive({
+const upload = reactive<ImportOption>({
   // 鏄惁鏄剧ず寮瑰嚭灞傦紙鐢ㄦ埛瀵煎叆锛�
   open: false,
   // 寮瑰嚭灞傛爣棰橈紙鐢ㄦ埛瀵煎叆锛�
@@ -367,27 +50,52 @@
   headers: { Authorization: "Bearer " + getToken() },
   // 涓婁紶鐨勫湴鍧�
   url: import.meta.env.VITE_APP_BASE_API + "/system/user/importData"
-});
+})
 // 鍒楁樉闅愪俊鎭�
-const columns = ref([
-  { key: 0, label: `鐢ㄦ埛缂栧彿`, visible: false },
+const columns = ref<FieldOption[]>([
+  { key: 0, label: `鐢ㄦ埛缂栧彿`, visible: true },
   { key: 1, label: `鐢ㄦ埛鍚嶇О`, visible: true },
   { key: 2, label: `鐢ㄦ埛鏄电О`, visible: true },
   { key: 3, label: `閮ㄩ棬`, visible: true },
   { key: 4, label: `鎵嬫満鍙风爜`, visible: true },
   { key: 5, label: `鐘舵�乣, visible: true },
   { key: 6, label: `鍒涘缓鏃堕棿`, visible: true }
-]);
+])
 
-const data = reactive({
-  form: {},
+
+const deptTreeRef = ref(ElTree);
+const queryFormRef = ref(ElForm);
+const userFormRef = ref(ElForm);
+const uploadRef = ref(ElUpload);
+
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+
+const initFormData: UserForm = {
+  userId: undefined,
+  deptId: undefined,
+  userName: '',
+  nickName: undefined,
+  password: '',
+  phonenumber: undefined,
+  email: undefined,
+  sex: undefined,
+  status: "0",
+  remark: '',
+  postIds: [],
+  roleIds: []
+}
+const data = reactive<PageData<UserForm, UserQuery>>({
+  form: { ...initFormData },
   queryParams: {
     pageNum: 1,
     pageSize: 10,
-    userName: undefined,
-    phonenumber: undefined,
-    status: undefined,
-    deptId: undefined
+    userName: '',
+    phonenumber: '',
+    status: '',
+    deptId: ''
   },
   rules: {
     userName: [{ required: true, message: "鐢ㄦ埛鍚嶇О涓嶈兘涓虹┖", trigger: "blur" }, { min: 2, max: 20, message: "鐢ㄦ埛鍚嶇О闀垮害蹇呴』浠嬩簬 2 鍜� 20 涔嬮棿", trigger: "blur" }],
@@ -396,213 +104,519 @@
     email: [{ type: "email", message: "璇疯緭鍏ユ纭殑閭鍦板潃", trigger: ["blur", "change"] }],
     phonenumber: [{ pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: "璇疯緭鍏ユ纭殑鎵嬫満鍙风爜", trigger: "blur" }]
   }
-});
+})
 
-const { queryParams, form, rules } = toRefs(data);
+const { queryParams, form, rules } = toRefs<PageData<UserForm, UserQuery>>(data)
 
 /** 閫氳繃鏉′欢杩囨护鑺傜偣  */
-const filterNode = (value, data) => {
-  if (!value) return true;
-  return data.label.indexOf(value) !== -1;
-};
+const filterNode = (value: string, data: any) => {
+  if (!value) return true
+  return data.label.indexOf(value) !== -1
+}
 /** 鏍规嵁鍚嶇О绛涢�夐儴闂ㄦ爲 */
-watch(deptName, val => {
-  proxy.$refs["deptTreeRef"].filter(val);
-});
+watchEffect(
+    () => {deptTreeRef.value.filter(deptName.value);},
+    {
+      flush: 'post' // watchEffect浼氬湪DOM鎸傝浇鎴栬�呮洿鏂颁箣鍓嶅氨浼氳Е鍙戯紝姝ゅ睘鎬ф帶鍒跺湪DOM鍏冪礌鏇存柊鍚庤繍琛�
+    }
+);
+
 /** 鏌ヨ閮ㄩ棬涓嬫媺鏍戠粨鏋� */
-function getDeptTree() {
-  deptTreeSelect().then(response => {
-    deptOptions.value = response.data;
-  });
+const getTreeSelect = async () => {
+  const res = await deptTreeSelect();
+	deptOptions.value = res.data;
 };
+
 /** 鏌ヨ鐢ㄦ埛鍒楄〃 */
-function getList() {
+const getList = async () => {
   loading.value = true;
-  listUser(proxy.addDateRange(queryParams.value, dateRange.value)).then(res => {
-    loading.value = false;
-    userList.value = res.rows;
-    total.value = res.total;
-  });
-};
+  const res = await listUser(proxy?.addDateRange(queryParams.value, dateRange.value));
+	loading.value = false;
+	userList.value = res.rows;
+	total.value = res.total;
+}
+
 /** 鑺傜偣鍗曞嚮浜嬩欢 */
-function handleNodeClick(data) {
+const handleNodeClick = (data: DeptVO) => {
   queryParams.value.deptId = data.id;
-  handleQuery();
-};
+  handleQuery()
+}
+
+
 /** 鎼滅储鎸夐挳鎿嶄綔 */
-function handleQuery() {
-  queryParams.value.pageNum = 1;
-  getList();
-};
+const handleQuery = () => {
+  queryParams.value.pageNum = 1
+  getList()
+}
 /** 閲嶇疆鎸夐挳鎿嶄綔 */
-function resetQuery() {
-  dateRange.value = [];
-  proxy.resetForm("queryRef");
-  queryParams.value.deptId = undefined;
-  proxy.$refs.deptTreeRef.setCurrentKey(null);
+const resetQuery = () => {
+  dateRange.value = ['','']
+  queryFormRef.value.resetFields();
+  queryParams.value.pageNum = 1;
   handleQuery();
-};
+}
+
 /** 鍒犻櫎鎸夐挳鎿嶄綔 */
-function handleDelete(row) {
-  const userIds = row.userId || ids.value;
-  proxy.$modal.confirm('鏄惁纭鍒犻櫎鐢ㄦ埛缂栧彿涓�"' + userIds + '"鐨勬暟鎹」锛�').then(function () {
-    return delUser(userIds);
-  }).then(() => {
-    getList();
-    proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
-  }).catch(() => {});
-};
-/** 瀵煎嚭鎸夐挳鎿嶄綔 */
-function handleExport() {
-  proxy.download("system/user/export", {
-    ...queryParams.value,
-  },`user_${new Date().getTime()}.xlsx`);
-};
+const handleDelete = async (row?: UserVO) => {
+  const userIds = row?.userId || ids.value;
+  const [err] = await to(proxy?.$modal.confirm('鏄惁纭鍒犻櫎鐢ㄦ埛缂栧彿涓�"' + userIds + '"鐨勬暟鎹」锛�') as any);
+	if (!err) {
+		await delUser(userIds);
+		await getList();
+		proxy?.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+	}
+}
+
 /** 鐢ㄦ埛鐘舵�佷慨鏀�  */
-function handleStatusChange(row) {
-  let text = row.status === "0" ? "鍚敤" : "鍋滅敤";
-  proxy.$modal.confirm('纭瑕�"' + text + '""' + row.userName + '"鐢ㄦ埛鍚�?').then(function () {
-    return changeUserStatus(row.userId, row.status);
-  }).then(() => {
-    proxy.$modal.msgSuccess(text + "鎴愬姛");
-  }).catch(function () {
-    row.status = row.status === "0" ? "1" : "0";
-  });
-};
-/** 鏇村鎿嶄綔 */
-function handleCommand(command, row) {
-  switch (command) {
-    case "handleResetPwd":
-      handleResetPwd(row);
-      break;
-    case "handleAuthRole":
-      handleAuthRole(row);
-      break;
-    default:
-      break;
-  }
-};
+const handleStatusChange = async (row: UserVO) => {
+	let text = row.status === "0" ? "鍚敤" : "鍋滅敤"
+	const [err] = await to(proxy?.$modal.confirm('纭瑕�"' + text + '""' + row.userName + '"鐢ㄦ埛鍚�?') as any)
+	if(err) {
+		row.status = row.status === "0" ? "1" : "0";
+	} else {
+		await changeUserStatus(row.userId, row.status);
+		proxy?.$modal.msgSuccess(text + "鎴愬姛");
+	}
+
+}
 /** 璺宠浆瑙掕壊鍒嗛厤 */
-function handleAuthRole(row) {
+const handleAuthRole = (row: UserVO) => {
   const userId = row.userId;
   router.push("/system/user-auth/role/" + userId);
-};
+}
+
 /** 閲嶇疆瀵嗙爜鎸夐挳鎿嶄綔 */
-function handleResetPwd(row) {
-  proxy.$prompt('璇疯緭鍏�"' + row.userName + '"鐨勬柊瀵嗙爜', "鎻愮ず", {
+const handleResetPwd = async (row: UserVO) => {
+  const [err, res] = await to(ElMessageBox.prompt('璇疯緭鍏�"' + row.userName + '"鐨勬柊瀵嗙爜', "鎻愮ず", {
     confirmButtonText: "纭畾",
     cancelButtonText: "鍙栨秷",
     closeOnClickModal: false,
     inputPattern: /^.{5,20}$/,
     inputErrorMessage: "鐢ㄦ埛瀵嗙爜闀垮害蹇呴』浠嬩簬 5 鍜� 20 涔嬮棿",
-  }).then(({ value }) => {
-    resetUserPwd(row.userId, value).then(response => {
-      proxy.$modal.msgSuccess("淇敼鎴愬姛锛屾柊瀵嗙爜鏄細" + value);
-    });
-  }).catch(() => {});
-};
+	}))
+	if (!err) {
+		await resetUserPwd(row.userId, res.value);
+		proxy?.$modal.msgSuccess("淇敼鎴愬姛锛屾柊瀵嗙爜鏄細" + res.value);
+	}
+}
+
 /** 閫夋嫨鏉℃暟  */
-function handleSelectionChange(selection) {
-  ids.value = selection.map(item => item.userId);
+const handleSelectionChange = (selection: UserVO[]) => {
+  ids.value = selection.map((item) => item.userId);
   single.value = selection.length != 1;
   multiple.value = !selection.length;
-};
+}
+
 /** 瀵煎叆鎸夐挳鎿嶄綔 */
-function handleImport() {
+const handleImport = () => {
   upload.title = "鐢ㄦ埛瀵煎叆";
   upload.open = true;
+}
+/** 瀵煎嚭鎸夐挳鎿嶄綔 */
+const handleExport = () => {
+  proxy?.download("system/user/export", {
+    ...queryParams.value,
+  }, `user_${new Date().getTime()}.xlsx`);
 };
 /** 涓嬭浇妯℃澘鎿嶄綔 */
-function importTemplate() {
-  proxy.download("system/user/importTemplate", {
+const importTemplate = () => {
+  proxy?.download("system/user/importTemplate", {
   }, `user_template_${new Date().getTime()}.xlsx`);
-};
+}
+
 /**鏂囦欢涓婁紶涓鐞� */
-const handleFileUploadProgress = (event, file, fileList) => {
+const handleFileUploadProgress = () => {
   upload.isUploading = true;
-};
+}
 /** 鏂囦欢涓婁紶鎴愬姛澶勭悊 */
-const handleFileSuccess = (response, file, fileList) => {
+const handleFileSuccess = (response: any, file: UploadFile, fileList: UploadFiles) => {
   upload.open = false;
   upload.isUploading = false;
-  proxy.$refs["uploadRef"].handleRemove(file);
-  proxy.$alert("<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" + response.msg + "</div>", "瀵煎叆缁撴灉", { dangerouslyUseHTMLString: true });
+  uploadRef.value.handleRemove(file);
+  ElMessageBox.alert("<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" + response.msg + "</div>", "瀵煎叆缁撴灉", { dangerouslyUseHTMLString: true });
   getList();
-};
+}
+
 /** 鎻愪氦涓婁紶鏂囦欢 */
 function submitFileForm() {
-  proxy.$refs["uploadRef"].submit();
-};
-/** 閲嶇疆鎿嶄綔琛ㄥ崟 */
-function reset() {
-  form.value = {
-    userId: undefined,
-    deptId: undefined,
-    userName: undefined,
-    nickName: undefined,
-    password: undefined,
-    phonenumber: undefined,
-    email: undefined,
-    sex: undefined,
-    status: "0",
-    remark: undefined,
-    postIds: [],
-    roleIds: []
-  };
-  proxy.resetForm("userRef");
-};
-/** 鍙栨秷鎸夐挳 */
-function cancel() {
-  open.value = false;
-  reset();
-};
-/** 鏂板鎸夐挳鎿嶄綔 */
-function handleAdd() {
-  reset();
-  getUser().then(response => {
-    postOptions.value = response.data.posts;
-    roleOptions.value = response.data.roles;
-    open.value = true;
-    title.value = "娣诲姞鐢ㄦ埛";
-    form.value.password = initPassword.value;
-  });
-};
-/** 淇敼鎸夐挳鎿嶄綔 */
-function handleUpdate(row) {
-  reset();
-  const userId = row.userId || ids.value;
-  getUser(userId).then(response => {
-    form.value = response.data.user;
-    postOptions.value = response.data.posts;
-    roleOptions.value = response.data.roles;
-    form.value.postIds = response.data.postIds;
-    form.value.roleIds = response.data.roleIds;
-    open.value = true;
-    title.value = "淇敼鐢ㄦ埛";
-    form.password = "";
-  });
-};
-/** 鎻愪氦鎸夐挳 */
-function submitForm() {
-  proxy.$refs["userRef"].validate(valid => {
-    if (valid) {
-      if (form.value.userId != undefined) {
-        updateUser(form.value).then(response => {
-          proxy.$modal.msgSuccess("淇敼鎴愬姛");
-          open.value = false;
-          getList();
-        });
-      } else {
-        addUser(form.value).then(response => {
-          proxy.$modal.msgSuccess("鏂板鎴愬姛");
-          open.value = false;
-          getList();
-        });
-      }
-    }
-  });
-};
+  uploadRef.value.submit();
+}
 
-getDeptTree();
-getList();
+/** 鍒濆鍖栭儴闂ㄦ暟鎹� */
+const initTreeData = async () => {
+  // 鍒ゆ柇閮ㄩ棬鐨勬暟鎹槸鍚﹀瓨鍦紝瀛樺湪涓嶈幏鍙栵紝涓嶅瓨鍦ㄥ垯鑾峰彇
+  if (deptOptions.value === undefined) {
+    const { data } = await treeselect();
+		deptOptions.value = data;
+  }
+}
+
+
+/** 閲嶇疆鎿嶄綔琛ㄥ崟 */
+const reset = () => {
+  form.value = { ...initFormData };
+  userFormRef.value.resetFields();
+}
+/** 鍙栨秷鎸夐挳 */
+const cancel = () => {
+  reset();
+  dialog.visible = false;
+}
+
+/** 鏂板鎸夐挳鎿嶄綔 */
+const handleAdd = () => {
+	dialog.visible = true;
+	dialog.title = "鏂板鐢ㄦ埛";
+	nextTick(async () => {
+		reset();
+		await initTreeData();
+		const { data } = await getUser();
+		postOptions.value = data.posts;
+		roleOptions.value = data.roles;
+		form.value.password = initPassword.value;
+	})
+}
+/** 淇敼鎸夐挳鎿嶄綔 */
+const handleUpdate = (row?: UserForm) => {
+	dialog.visible = true;
+	dialog.title = "淇敼鐢ㄦ埛";
+	nextTick(async () => {
+		reset();
+		await initTreeData();
+		const userId = row?.userId || ids.value[0]
+		const { data } = await getUser(userId)
+		Object.assign(form.value, data.user);
+		postOptions.value = data.posts;
+		roleOptions.value = data.roles;
+		form.value.postIds = data.postIds;
+		form.value.roleIds = data.roleIds;
+		form.value.password = "";
+	})
+
+}
+
+/** 鎻愪氦鎸夐挳 */
+const submitForm = () => {
+  userFormRef.value.validate(async (valid: boolean) => {
+    if (valid) {
+			form.value.userId ? await updateUser(form.value) : await addUser(form.value);
+			proxy?.$modal.msgSuccess("鎿嶄綔鎴愬姛");
+			dialog.visible = false;
+			await getList();
+    }
+  })
+}
+
+
+/**
+ * 鍏抽棴鐢ㄦ埛寮圭獥
+ */
+const closeDialog = () => {
+  dialog.visible = false;
+  resetForm();
+}
+
+/**
+ * 閲嶇疆琛ㄥ崟
+ */
+const resetForm = () => {
+  userFormRef.value.resetFields();
+  userFormRef.value.clearValidate();
+
+  form.value.id = undefined;
+  form.value.status = '1';
+}
+onMounted(() => {
+  getTreeSelect() // 鍒濆鍖栭儴闂ㄦ暟鎹�
+  getList() // 鍒濆鍖栧垪琛ㄦ暟鎹�
+});
 </script>
+
+<template>
+	<div class="p-2">
+		<el-row :gutter="20">
+			<!-- 閮ㄩ棬鏍� -->
+			<el-col :lg="4" :xs="24" style="">
+				<el-card shadow="never">
+					<el-input v-model="deptName" placeholder="璇疯緭鍏ラ儴闂ㄥ悕绉�" prefix-icon="Search" clearable />
+					<el-tree
+						class="mt-2"
+						ref="deptTreeRef"
+						:data="deptOptions"
+						:props="{ label: 'label', children: 'children' }"
+						:expand-on-click-node="false"
+						:filter-node-method="filterNode"
+						highlight-current
+						default-expand-all
+						@node-click="handleNodeClick"
+					></el-tree>
+				</el-card>
+			</el-col>
+			<el-col :lg="20" :xs="24">
+				<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
+					<div class="search" v-show="showSearch">
+						<el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="68px">
+							<el-form-item label="鐢ㄦ埛鍚嶇О" prop="userName">
+								<el-input v-model="queryParams.userName" placeholder="璇疯緭鍏ョ敤鎴峰悕绉�" clearable style="width: 240px" @keyup.enter="handleQuery" />
+							</el-form-item>
+							<el-form-item label="鎵嬫満鍙风爜" prop="phonenumber">
+								<el-input v-model="queryParams.phonenumber" placeholder="璇疯緭鍏ユ墜鏈哄彿鐮�" clearable style="width: 240px" @keyup.enter="handleQuery" />
+							</el-form-item>
+
+							<el-form-item label="鐘舵��" prop="status">
+								<el-select v-model="queryParams.status" placeholder="鐢ㄦ埛鐘舵��" clearable style="width: 240px">
+									<el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" />
+								</el-select>
+							</el-form-item>
+							<el-form-item label="鍒涘缓鏃堕棿" style="width: 308px;">
+								<el-date-picker
+									v-model="dateRange"
+									value-format="YYYY-MM-DD"
+									type="daterange"
+									range-separator="-"
+									start-placeholder="寮�濮嬫棩鏈�"
+									end-placeholder="缁撴潫鏃ユ湡"
+								></el-date-picker>
+							</el-form-item>
+							<el-form-item>
+								<el-button type="primary" @click="handleQuery" icon="Search">鎼滅储</el-button>
+								<el-button @click="resetQuery" icon="Refresh">閲嶇疆</el-button>
+							</el-form-item>
+						</el-form>
+					</div>
+				</transition>
+
+				<el-card shadow="never">
+					<template #header>
+						<el-row :gutter="10">
+							<el-col :span="1.5">
+								<el-button type="primary" @click="handleAdd()" v-has-permi="['sys:user:add']" icon="Plus">鏂板</el-button>
+							</el-col>
+							<el-col :span="1.5">
+								<el-button type="success" @click="handleUpdate()" :disabled="single" v-has-permi="['sys:user:add']" icon="Edit">淇敼</el-button>
+							</el-col>
+							<el-col :span="1.5">
+								<el-button type="danger" @click="handleDelete()" :disabled="multiple" v-has-permi="['sys:user:delete']" icon="Delete">
+									鍒犻櫎
+								</el-button>
+							</el-col>
+							<el-col :span="1.5">
+								<el-dropdown class="mt-[1px]">
+									<el-button type="info">
+										鏇村
+										<el-icon class="el-icon--right"><arrow-down /></el-icon
+									></el-button>
+									<template #dropdown>
+										<el-dropdown-menu>
+											<el-dropdown-item @click="importTemplate" icon="Download">涓嬭浇妯℃澘</el-dropdown-item>
+											<el-dropdown-item @click="handleImport" icon="Top"> 瀵煎叆鏁版嵁</el-dropdown-item>
+											<el-dropdown-item @click="handleExport" icon="Download"> 瀵煎嚭鏁版嵁</el-dropdown-item>
+										</el-dropdown-menu>
+									</template>
+								</el-dropdown>
+							</el-col>
+							<right-toolbar v-model:showSearch="showSearch" @queryTable="getList" :columns="columns" :search="true"></right-toolbar>
+						</el-row>
+					</template>
+
+					<el-table v-loading="loading" :data="userList" @selection-change="handleSelectionChange">
+						<el-table-column type="selection" width="50" align="center" />
+						<el-table-column label="鐢ㄦ埛缂栧彿" align="center" key="userId" prop="userId" v-if="columns[0].visible" />
+						<el-table-column label="鐢ㄦ埛鍚嶇О" align="center" key="userName" prop="userName" v-if="columns[1].visible" :show-overflow-tooltip="true" />
+						<el-table-column label="鐢ㄦ埛鏄电О" align="center" key="nickName" prop="nickName" v-if="columns[2].visible" :show-overflow-tooltip="true" />
+						<el-table-column
+							label="閮ㄩ棬"
+							align="center"
+							key="deptName"
+							prop="dept.deptName"
+							v-if="columns[3].visible"
+							:show-overflow-tooltip="true"
+						/>
+						<el-table-column label="鎵嬫満鍙风爜" align="center" key="phonenumber" prop="phonenumber" v-if="columns[4].visible" width="120" />
+						<el-table-column label="鐘舵��" align="center" key="status" v-if="columns[5].visible">
+							<template #default="scope">
+								<el-switch v-model="scope.row.status" active-value="0" inactive-value="1" @change="handleStatusChange(scope.row)"></el-switch>
+							</template>
+						</el-table-column>
+
+						<el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime" v-if="columns[6].visible" width="160">
+							<template #default="scope">
+								<span>{{ scope.row.createTime }}</span>
+							</template>
+						</el-table-column>
+
+						<el-table-column label="鎿嶄綔" fixed="right" width="180" class-name="small-padding fixed-width">
+							<template #default="scope">
+								<el-tooltip content="淇敼" placement="top" v-if="scope.row.userId !== 1">
+									<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:user:edit']"></el-button>
+								</el-tooltip>
+								<el-tooltip content="鍒犻櫎" placement="top" v-if="scope.row.userId !== 1">
+									<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:user:remove']"></el-button>
+								</el-tooltip>
+
+								<el-tooltip content="閲嶇疆瀵嗙爜" placement="top" v-if="scope.row.userId !== 1">
+									<el-button link type="primary" icon="Key" @click="handleResetPwd(scope.row)" v-hasPermi="['system:user:resetPwd']"></el-button>
+								</el-tooltip>
+
+								<el-tooltip content="鍒嗛厤瑙掕壊" placement="top" v-if="scope.row.userId !== 1">
+									<el-button link type="primary" icon="CircleCheck" @click="handleAuthRole(scope.row)" v-hasPermi="['system:user:edit']"></el-button>
+								</el-tooltip>
+							</template>
+						</el-table-column>
+					</el-table>
+
+					<pagination
+						v-show="total > 0"
+						:total="total"
+						v-model:page="queryParams.pageNum"
+						v-model:limit="queryParams.pageSize"
+						@pagination="handleQuery"
+					/>
+				</el-card>
+			</el-col>
+		</el-row>
+
+		<!-- 娣诲姞鎴栦慨鏀圭敤鎴烽厤缃璇濇 -->
+		<el-dialog :title="dialog.title" v-model="dialog.visible" width="600px" append-to-body @close="closeDialog">
+			<el-form :model="form" :rules="rules" ref="userFormRef" label-width="80px">
+				<el-row>
+					<el-col :span="12">
+						<el-form-item label="鐢ㄦ埛鏄电О" prop="nickName">
+							<el-input v-model="form.nickName" placeholder="璇疯緭鍏ョ敤鎴锋樀绉�" maxlength="30" />
+						</el-form-item>
+					</el-col>
+					<el-col :span="12">
+						<el-form-item label="褰掑睘閮ㄩ棬" prop="deptId">
+							<el-tree-select
+								v-model="form.deptId"
+								:data="deptOptions"
+								:props="{ value: 'id', label: 'label', children: 'children' }"
+								value-key="id"
+								placeholder="璇烽�夋嫨褰掑睘閮ㄩ棬"
+								check-strictly
+							/>
+						</el-form-item>
+					</el-col>
+				</el-row>
+				<el-row>
+					<el-col :span="12">
+						<el-form-item label="鎵嬫満鍙风爜" prop="phonenumber">
+							<el-input v-model="form.phonenumber" placeholder="璇疯緭鍏ユ墜鏈哄彿鐮�" maxlength="11" />
+						</el-form-item>
+					</el-col>
+					<el-col :span="12">
+						<el-form-item label="閭" prop="email">
+							<el-input v-model="form.email" placeholder="璇疯緭鍏ラ偖绠�" maxlength="50" />
+						</el-form-item>
+					</el-col>
+				</el-row>
+				<el-row>
+					<el-col :span="12">
+						<el-form-item v-if="form.userId == undefined" label="鐢ㄦ埛鍚嶇О" prop="userName">
+							<el-input v-model="form.userName" placeholder="璇疯緭鍏ョ敤鎴峰悕绉�" maxlength="30" />
+						</el-form-item>
+					</el-col>
+					<el-col :span="12">
+						<el-form-item v-if="form.userId == undefined" label="鐢ㄦ埛瀵嗙爜" prop="password">
+							<el-input v-model="form.password" placeholder="璇疯緭鍏ョ敤鎴峰瘑鐮�" type="password" maxlength="20" show-password />
+						</el-form-item>
+					</el-col>
+				</el-row>
+				<el-row>
+					<el-col :span="12">
+						<el-form-item label="鐢ㄦ埛鎬у埆">
+							<el-select v-model="form.sex" placeholder="璇烽�夋嫨">
+								<el-option v-for="dict in sys_user_sex" :key="dict.value" :label="dict.label" :value="dict.value"></el-option>
+							</el-select>
+						</el-form-item>
+					</el-col>
+					<el-col :span="12">
+						<el-form-item label="鐘舵��">
+							<el-radio-group v-model="form.status">
+								<el-radio v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.value">{{
+                    dict.label }}</el-radio>
+							</el-radio-group>
+						</el-form-item>
+					</el-col>
+				</el-row>
+				<el-row>
+					<el-col :span="12">
+						<el-form-item label="宀椾綅">
+							<el-select v-model="form.postIds" multiple placeholder="璇烽�夋嫨">
+								<el-option
+									v-for="item in postOptions"
+									:key="item.postId"
+									:label="item.postName"
+									:value="item.postId"
+									:disabled="item.status == '1'"
+								></el-option>
+							</el-select>
+						</el-form-item>
+					</el-col>
+					<el-col :span="12">
+						<el-form-item label="瑙掕壊">
+							<el-select v-model="form.roleIds" multiple placeholder="璇烽�夋嫨">
+								<el-option
+									v-for="item in roleOptions"
+									:key="item.roleId"
+									:label="item.roleName"
+									:value="item.roleId"
+									:disabled="item.status == '1'"
+								></el-option>
+							</el-select>
+						</el-form-item>
+					</el-col>
+				</el-row>
+				<el-row>
+					<el-col :span="24">
+						<el-form-item label="澶囨敞">
+							<el-input v-model="form.remark" type="textarea" placeholder="璇疯緭鍏ュ唴瀹�"></el-input>
+						</el-form-item>
+					</el-col>
+				</el-row>
+			</el-form>
+			<template #footer>
+				<div class="dialog-footer">
+					<el-button type="primary" @click="submitForm">纭� 瀹�</el-button>
+					<el-button @click="cancel()">鍙� 娑�</el-button>
+				</div>
+			</template>
+		</el-dialog>
+
+		<!-- 鐢ㄦ埛瀵煎叆瀵硅瘽妗� -->
+		<el-dialog :title="upload.title" v-model="upload.open" width="400px" append-to-body>
+			<el-upload
+				ref="uploadRef"
+				:limit="1"
+				accept=".xlsx, .xls"
+				:headers="upload.headers"
+				:action="upload.url + '?updateSupport=' + upload.updateSupport"
+				:disabled="upload.isUploading"
+				:on-progress="handleFileUploadProgress"
+				:on-success="handleFileSuccess"
+				:auto-upload="false"
+				drag
+			>
+				<el-icon class="el-icon--upload">
+					<i-ep-upload-filled />
+				</el-icon>
+				<div class="el-upload__text">灏嗘枃浠舵嫋鍒版澶勶紝鎴�<em>鐐瑰嚮涓婁紶</em></div>
+				<template #tip>
+					<div class="text-center el-upload__tip">
+						<div class="el-upload__tip"><el-checkbox v-model="upload.updateSupport" />鏄惁鏇存柊宸茬粡瀛樺湪鐨勭敤鎴锋暟鎹�</div>
+						<span>浠呭厑璁稿鍏ls銆亁lsx鏍煎紡鏂囦欢銆�</span>
+						<el-link type="primary" :underline="false" style="font-size:12px;vertical-align: baseline;" @click="importTemplate">涓嬭浇妯℃澘</el-link>
+					</div>
+				</template>
+			</el-upload>
+			<template #footer>
+				<div class="dialog-footer">
+					<el-button type="primary" @click="submitFileForm">纭� 瀹�</el-button>
+					<el-button @click="upload.open = false">鍙� 娑�</el-button>
+				</div>
+			</template>
+		</el-dialog>
+	</div>
+</template>
+
+<style lang="scss" scoped></style>
diff --git a/src/views/system/user/profile/index.vue b/src/views/system/user/profile/index.vue
index 09e4c32..40a5156 100644
--- a/src/views/system/user/profile/index.vue
+++ b/src/views/system/user/profile/index.vue
@@ -1,87 +1,91 @@
-<template>
-   <div class="app-container">
-      <el-row :gutter="20">
-         <el-col :span="6" :xs="24">
-            <el-card class="box-card">
-               <template v-slot:header>
-                 <div class="clearfix">
-                   <span>涓汉淇℃伅</span>
-                 </div>
-               </template>
-               <div>
-                  <div class="text-center">
-                     <userAvatar :user="state.user" />
-                  </div>
-                  <ul class="list-group list-group-striped">
-                     <li class="list-group-item">
-                        <svg-icon icon-class="user" />鐢ㄦ埛鍚嶇О
-                        <div class="pull-right">{{ state.user.userName }}</div>
-                     </li>
-                     <li class="list-group-item">
-                        <svg-icon icon-class="phone" />鎵嬫満鍙风爜
-                        <div class="pull-right">{{ state.user.phonenumber }}</div>
-                     </li>
-                     <li class="list-group-item">
-                        <svg-icon icon-class="email" />鐢ㄦ埛閭
-                        <div class="pull-right">{{ state.user.email }}</div>
-                     </li>
-                     <li class="list-group-item">
-                        <svg-icon icon-class="tree" />鎵�灞為儴闂�
-                        <div class="pull-right" v-if="state.user.dept">{{ state.user.dept.deptName }} / {{ state.postGroup }}</div>
-                     </li>
-                     <li class="list-group-item">
-                        <svg-icon icon-class="peoples" />鎵�灞炶鑹�
-                        <div class="pull-right">{{ state.roleGroup }}</div>
-                     </li>
-                     <li class="list-group-item">
-                        <svg-icon icon-class="date" />鍒涘缓鏃ユ湡
-                        <div class="pull-right">{{ state.user.createTime }}</div>
-                     </li>
-                  </ul>
-               </div>
-            </el-card>
-         </el-col>
-         <el-col :span="18" :xs="24">
-            <el-card>
-               <template v-slot:header>
-                 <div class="clearfix">
-                   <span>鍩烘湰璧勬枡</span>
-                 </div>
-               </template>
-               <el-tabs v-model="activeTab">
-                  <el-tab-pane label="鍩烘湰璧勬枡" name="userinfo">
-                     <userInfo :user="state.user" />
-                  </el-tab-pane>
-                  <el-tab-pane label="淇敼瀵嗙爜" name="resetPwd">
-                     <resetPwd />
-                  </el-tab-pane>
-               </el-tabs>
-            </el-card>
-         </el-col>
-      </el-row>
-   </div>
-</template>
-
-<script setup name="Profile">
-import userAvatar from "./userAvatar";
-import userInfo from "./userInfo";
-import resetPwd from "./resetPwd";
+<script setup name="Profile" lang="ts">
+import userAvatar from "./userAvatar.vue";
+import userInfo from "./userInfo.vue";
+import resetPwd from "./resetPwd.vue";
 import { getUserProfile } from "@/api/system/user";
 
 const activeTab = ref("userinfo");
-const state = reactive({
+const state = ref<{ user: any; roleGroup: string;  postGroup: string}>({
   user: {},
-  roleGroup: {},
-  postGroup: {}
+  roleGroup: '',
+  postGroup: ''
 });
 
-function getUser() {
-  getUserProfile().then(response => {
-    state.user = response.data.user;
-    state.roleGroup = response.data.roleGroup;
-    state.postGroup = response.data.postGroup;
-  });
+const userForm = ref({});
+
+const getUser = async () => {
+	const res = await getUserProfile();
+	state.value.user = res.data.user;
+	userForm.value = { ...res.data.user }
+	state.value.roleGroup = res.data.roleGroup;
+	state.value.postGroup = res.data.postGroup;
 };
 
-getUser();
+onMounted(() => {
+	getUser();
+})
 </script>
+
+<template>
+	<div class="p-2">
+		<el-row :gutter="20">
+			<el-col :span="6" :xs="24">
+				<el-card class="box-card">
+					<template v-slot:header>
+						<div class="clearfix">
+							<span>涓汉淇℃伅</span>
+						</div>
+					</template>
+					<div>
+						<div class="text-center">
+							<userAvatar :user="state.user" />
+						</div>
+						<ul class="list-group list-group-striped">
+							<li class="list-group-item">
+								<svg-icon icon-class="user" />鐢ㄦ埛鍚嶇О
+								<div class="pull-right">{{ state.user.userName }}</div>
+							</li>
+							<li class="list-group-item">
+								<svg-icon icon-class="phone" />鎵嬫満鍙风爜
+								<div class="pull-right">{{ state.user.phonenumber }}</div>
+							</li>
+							<li class="list-group-item">
+								<svg-icon icon-class="email" />鐢ㄦ埛閭
+								<div class="pull-right">{{ state.user.email }}</div>
+							</li>
+							<li class="list-group-item">
+								<svg-icon icon-class="tree" />鎵�灞為儴闂�
+								<div class="pull-right" v-if="state.user.dept">{{ state.user.dept.deptName }} / {{ state.postGroup }}</div>
+							</li>
+							<li class="list-group-item">
+								<svg-icon icon-class="peoples" />鎵�灞炶鑹�
+								<div class="pull-right">{{ state.roleGroup }}</div>
+							</li>
+							<li class="list-group-item">
+								<svg-icon icon-class="date" />鍒涘缓鏃ユ湡
+								<div class="pull-right">{{ state.user.createTime }}</div>
+							</li>
+						</ul>
+					</div>
+				</el-card>
+			</el-col>
+			<el-col :span="18" :xs="24">
+				<el-card>
+					<template v-slot:header>
+						<div class="clearfix">
+							<span>鍩烘湰璧勬枡</span>
+						</div>
+					</template>
+					<el-tabs v-model="activeTab">
+						<el-tab-pane label="鍩烘湰璧勬枡" name="userinfo">
+							<userInfo :user="userForm" />
+						</el-tab-pane>
+						<el-tab-pane label="淇敼瀵嗙爜" name="resetPwd">
+							<resetPwd />
+						</el-tab-pane>
+					</el-tabs>
+				</el-card>
+			</el-col>
+		</el-row>
+	</div>
+</template>
diff --git a/src/views/system/user/profile/resetPwd.vue b/src/views/system/user/profile/resetPwd.vue
index dec2d79..0207342 100644
--- a/src/views/system/user/profile/resetPwd.vue
+++ b/src/views/system/user/profile/resetPwd.vue
@@ -1,34 +1,22 @@
-<template>
-   <el-form ref="pwdRef" :model="user" :rules="rules" label-width="80px">
-      <el-form-item label="鏃у瘑鐮�" prop="oldPassword">
-         <el-input v-model="user.oldPassword" placeholder="璇疯緭鍏ユ棫瀵嗙爜" type="password" show-password />
-      </el-form-item>
-      <el-form-item label="鏂板瘑鐮�" prop="newPassword">
-         <el-input v-model="user.newPassword" placeholder="璇疯緭鍏ユ柊瀵嗙爜" type="password" show-password />
-      </el-form-item>
-      <el-form-item label="纭瀵嗙爜" prop="confirmPassword">
-         <el-input v-model="user.confirmPassword" placeholder="璇风‘璁ゆ柊瀵嗙爜" type="password" show-password/>
-      </el-form-item>
-      <el-form-item>
-      <el-button type="primary" @click="submit">淇濆瓨</el-button>
-      <el-button type="danger" @click="close">鍏抽棴</el-button>
-      </el-form-item>
-   </el-form>
-</template>
+<script setup lang="ts">
+import { updateUserPwd } from '@/api/system/user';
+import { ComponentInternalInstance } from 'vue';
+import { ResetPwdForm } from '@/api/system/user/types'
+import { ElForm } from 'element-plus';
 
-<script setup>
-import { updateUserPwd } from "@/api/system/user";
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 
-const { proxy } = getCurrentInstance();
 
-const user = reactive({
-  oldPassword: undefined,
-  newPassword: undefined,
-  confirmPassword: undefined
+const pwdRef = ref(ElForm);
+
+const user = ref<ResetPwdForm>({
+  oldPassword: '',
+  newPassword: '',
+  confirmPassword: ''
 });
 
-const equalToPassword = (rule, value, callback) => {
-  if (user.newPassword !== value) {
+const equalToPassword = (rule: any, value: string, callback: any) => {
+  if (user.value.newPassword !== value) {
     callback(new Error("涓ゆ杈撳叆鐨勫瘑鐮佷笉涓�鑷�"));
   } else {
     callback();
@@ -41,17 +29,34 @@
 });
 
 /** 鎻愪氦鎸夐挳 */
-function submit() {
-  proxy.$refs.pwdRef.validate(valid => {
+const submit = () => {
+  pwdRef.value.validate(async (valid: boolean) => {
     if (valid) {
-      updateUserPwd(user.oldPassword, user.newPassword).then(response => {
-        proxy.$modal.msgSuccess("淇敼鎴愬姛");
-      });
+      await updateUserPwd(user.value.oldPassword, user.value.newPassword)
+      proxy?.$modal.msgSuccess("淇敼鎴愬姛");
     }
   });
 };
 /** 鍏抽棴鎸夐挳 */
-function close() {
-  proxy.$tab.closePage();
+const close = () => {
+  proxy?.$tab.closePage();
 };
 </script>
+
+<template>
+	<el-form ref="pwdRef" :model="user" :rules="rules" label-width="80px">
+		<el-form-item label="鏃у瘑鐮�" prop="oldPassword">
+			<el-input v-model="user.oldPassword" placeholder="璇疯緭鍏ユ棫瀵嗙爜" type="password" show-password />
+		</el-form-item>
+		<el-form-item label="鏂板瘑鐮�" prop="newPassword">
+			<el-input v-model="user.newPassword" placeholder="璇疯緭鍏ユ柊瀵嗙爜" type="password" show-password />
+		</el-form-item>
+		<el-form-item label="纭瀵嗙爜" prop="confirmPassword">
+			<el-input v-model="user.confirmPassword" placeholder="璇风‘璁ゆ柊瀵嗙爜" type="password" show-password />
+		</el-form-item>
+		<el-form-item>
+			<el-button type="primary" @click="submit">淇濆瓨</el-button>
+			<el-button type="danger" @click="close">鍏抽棴</el-button>
+		</el-form-item>
+	</el-form>
+</template>
diff --git a/src/views/system/user/profile/userAvatar.vue b/src/views/system/user/profile/userAvatar.vue
index f576c95..6094301 100644
--- a/src/views/system/user/profile/userAvatar.vue
+++ b/src/views/system/user/profile/userAvatar.vue
@@ -1,150 +1,160 @@
-<template>
-  <div class="user-info-head" @click="editCropper()">
-    <img :src="options.img" title="鐐瑰嚮涓婁紶澶村儚" class="img-circle img-lg" />
-    <el-dialog :title="title" v-model="open" width="800px" append-to-body @opened="modalOpened" @close="closeDialog">
-      <el-row>
-        <el-col :xs="24" :md="12" :style="{ height: '350px' }">
-          <vue-cropper
-            ref="cropper"
-            :img="options.img"
-            :info="true"
-            :autoCrop="options.autoCrop"
-            :autoCropWidth="options.autoCropWidth"
-            :autoCropHeight="options.autoCropHeight"
-            :fixedBox="options.fixedBox"
-            :outputType="options.outputType"
-            @realTime="realTime"
-            v-if="visible"
-          />
-        </el-col>
-        <el-col :xs="24" :md="12" :style="{ height: '350px' }">
-          <div class="avatar-upload-preview">
-            <img :src="options.previews.url" :style="options.previews.img" />
-          </div>
-        </el-col>
-      </el-row>
-      <br />
-      <el-row>
-        <el-col :lg="2" :md="2">
-          <el-upload
-            action="#"
-            :http-request="requestUpload"
-            :show-file-list="false"
-            :before-upload="beforeUpload"
-          >
-            <el-button>
-              閫夋嫨
-              <el-icon class="el-icon--right"><Upload /></el-icon>
-            </el-button>
-          </el-upload>
-        </el-col>
-        <el-col :lg="{ span: 1, offset: 2 }" :md="2">
-          <el-button icon="Plus" @click="changeScale(1)"></el-button>
-        </el-col>
-        <el-col :lg="{ span: 1, offset: 1 }" :md="2">
-          <el-button icon="Minus" @click="changeScale(-1)"></el-button>
-        </el-col>
-        <el-col :lg="{ span: 1, offset: 1 }" :md="2">
-          <el-button icon="RefreshLeft" @click="rotateLeft()"></el-button>
-        </el-col>
-        <el-col :lg="{ span: 1, offset: 1 }" :md="2">
-          <el-button icon="RefreshRight" @click="rotateRight()"></el-button>
-        </el-col>
-        <el-col :lg="{ span: 2, offset: 6 }" :md="2">
-          <el-button type="primary" @click="uploadImg()">鎻� 浜�</el-button>
-        </el-col>
-      </el-row>
-    </el-dialog>
-  </div>
-</template>
-
-<script setup>
+<script setup lang="ts">
 import "vue-cropper/dist/index.css";
 import { VueCropper } from "vue-cropper";
 import { uploadAvatar } from "@/api/system/user";
 import useUserStore from "@/store/modules/user";
+import { ComponentInternalInstance } from "vue";
+
+interface Options {
+  img: string | ArrayBuffer | null // 瑁佸壀鍥剧墖鐨勫湴鍧�
+  autoCrop: boolean // 鏄惁榛樿鐢熸垚鎴浘妗�
+  autoCropWidth: number // 榛樿鐢熸垚鎴浘妗嗗搴�
+  autoCropHeight: number // 榛樿鐢熸垚鎴浘妗嗛珮搴�
+  fixedBox: boolean // 鍥哄畾鎴浘妗嗗ぇ灏� 涓嶅厑璁告敼鍙�
+  fileName: string
+  previews: any // 棰勮鏁版嵁
+  outputType: string
+  visible: boolean
+}
+
 
 const userStore = useUserStore();
-const { proxy } = getCurrentInstance();
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 
 const open = ref(false);
 const visible = ref(false);
 const title = ref("淇敼澶村儚");
 
+const cropper = ref<any>({});
 //鍥剧墖瑁佸壀鏁版嵁
-const options = reactive({
-  img: userStore.avatar, // 瑁佸壀鍥剧墖鐨勫湴鍧�
-  autoCrop: true, // 鏄惁榛樿鐢熸垚鎴浘妗�
-  autoCropWidth: 200, // 榛樿鐢熸垚鎴浘妗嗗搴�
-  autoCropHeight: 200, // 榛樿鐢熸垚鎴浘妗嗛珮搴�
-  fixedBox: true, // 鍥哄畾鎴浘妗嗗ぇ灏� 涓嶅厑璁告敼鍙�
-  outputType: "png", // 榛樿鐢熸垚鎴浘涓篜NG鏍煎紡
-  filename: '',
-  previews: {} //棰勮鏁版嵁
+const options = reactive<Options>({
+  img: userStore.avatar,
+  autoCrop: true,
+  autoCropWidth: 200,
+  autoCropHeight: 200,
+  fixedBox: true,
+  outputType: "png",
+  fileName: '',
+  previews: {},
+  visible: false
 });
 
 /** 缂栬緫澶村儚 */
-function editCropper() {
+const editCropper = () => {
   open.value = true;
 }
 /** 鎵撳紑寮瑰嚭灞傜粨鏉熸椂鐨勫洖璋� */
-function modalOpened() {
+const modalOpened = () => {
   visible.value = true;
 }
 /** 瑕嗙洊榛樿涓婁紶琛屼负 */
-function requestUpload() {}
+const requestUpload = (): any => {}
 /** 鍚戝乏鏃嬭浆 */
-function rotateLeft() {
-  proxy.$refs.cropper.rotateLeft();
+const rotateLeft = () => {
+  cropper.value.rotateLeft();
 }
 /** 鍚戝彸鏃嬭浆 */
-function rotateRight() {
-  proxy.$refs.cropper.rotateRight();
+const rotateRight = () => {
+  cropper.value.rotateRight();
 }
 /** 鍥剧墖缂╂斁 */
-function changeScale(num) {
+const changeScale = (num: number) => {
   num = num || 1;
-  proxy.$refs.cropper.changeScale(num);
+  cropper.value.changeScale(num);
 }
 /** 涓婁紶棰勫鐞� */
-function beforeUpload(file) {
+const beforeUpload = (file: any) => {
   if (file.type.indexOf("image/") == -1) {
-    proxy.$modal.msgError("鏂囦欢鏍煎紡閿欒锛岃涓婁紶鍥剧墖绫诲瀷,濡傦細JPG锛孭NG鍚庣紑鐨勬枃浠躲��");
+    proxy?.$modal.msgError("鏂囦欢鏍煎紡閿欒锛岃涓婁紶鍥剧墖绫诲瀷,濡傦細JPG锛孭NG鍚庣紑鐨勬枃浠躲��");
   } else {
     const reader = new FileReader();
     reader.readAsDataURL(file);
     reader.onload = () => {
       options.img = reader.result;
-      options.filename = file.name;
+      options.fileName = file.name;
     };
   }
 }
 /** 涓婁紶鍥剧墖 */
-function uploadImg() {
-  proxy.$refs.cropper.getCropBlob(data => {
+const uploadImg = async () => {
+  cropper.value.getCropBlob(async (data: any) => {
     let formData = new FormData();
-    formData.append("avatarfile", data, options.filename);
-    uploadAvatar(formData).then(response => {
-      open.value = false;
-      options.img = response.data.imgUrl;
-      userStore.avatar = options.img;
-      proxy.$modal.msgSuccess("淇敼鎴愬姛");
-      visible.value = false;
-    });
+    formData.append("avatarfile", data, options.fileName);
+    const res = await uploadAvatar(formData);
+    open.value = false;
+    options.img = res.data.imgUrl;
+    userStore.avatar = options.img as string;
+    proxy?.$modal.msgSuccess("淇敼鎴愬姛");
+    visible.value = false;
   });
 }
 /** 瀹炴椂棰勮 */
-function realTime(data) {
+const realTime = (data: any) => {
   options.previews = data;
 }
 /** 鍏抽棴绐楀彛 */
-function closeDialog() {
+const closeDialog = () => {
   options.img = userStore.avatar;
   options.visible = false;
 }
 </script>
 
-<style lang='scss' scoped>
+<template>
+	<div class="user-info-head" @click="editCropper()">
+		<img :src="options.img as string" title="鐐瑰嚮涓婁紶澶村儚" class="img-circle img-lg" />
+		<el-dialog :title="title" v-model="open" width="800px" append-to-body @opened="modalOpened" @close="closeDialog">
+			<el-row>
+				<el-col :xs="24" :md="12" :style="{ height: '350px' }">
+					<vue-cropper
+						ref="cropper"
+						:img="options.img"
+						:info="true"
+						:autoCrop="options.autoCrop"
+						:autoCropWidth="options.autoCropWidth"
+						:autoCropHeight="options.autoCropHeight"
+						:fixedBox="options.fixedBox"
+						:outputType="options.outputType"
+						@realTime="realTime"
+						v-if="visible"
+					/>
+				</el-col>
+				<el-col :xs="24" :md="12" :style="{ height: '350px' }">
+					<div class="avatar-upload-preview">
+						<img :src="options.previews.url" :style="options.previews.img" />
+					</div>
+				</el-col>
+			</el-row>
+			<br />
+			<el-row>
+				<el-col :lg="2" :md="2">
+					<el-upload action="#" :http-request="requestUpload" :show-file-list="false" :before-upload="beforeUpload">
+						<el-button>
+							閫夋嫨
+							<el-icon class="el-icon--right"><Upload /></el-icon>
+						</el-button>
+					</el-upload>
+				</el-col>
+				<el-col :lg="{ span: 1, offset: 2 }" :md="2">
+					<el-button icon="Plus" @click="changeScale(1)"></el-button>
+				</el-col>
+				<el-col :lg="{ span: 1, offset: 1 }" :md="2">
+					<el-button icon="Minus" @click="changeScale(-1)"></el-button>
+				</el-col>
+				<el-col :lg="{ span: 1, offset: 1 }" :md="2">
+					<el-button icon="RefreshLeft" @click="rotateLeft()"></el-button>
+				</el-col>
+				<el-col :lg="{ span: 1, offset: 1 }" :md="2">
+					<el-button icon="RefreshRight" @click="rotateRight()"></el-button>
+				</el-col>
+				<el-col :lg="{ span: 2, offset: 6 }" :md="2">
+					<el-button type="primary" @click="uploadImg()">鎻� 浜�</el-button>
+				</el-col>
+			</el-row>
+		</el-dialog>
+	</div>
+</template>
+
+<style lang="scss" scoped>
 .user-info-head {
   position: relative;
   display: inline-block;
@@ -168,4 +178,4 @@
   line-height: 110px;
   border-radius: 50%;
 }
-</style>
\ No newline at end of file
+</style>
diff --git a/src/views/system/user/profile/userInfo.vue b/src/views/system/user/profile/userInfo.vue
index 2d62c84..13cc29b 100644
--- a/src/views/system/user/profile/userInfo.vue
+++ b/src/views/system/user/profile/userInfo.vue
@@ -1,56 +1,63 @@
-<template>
-   <el-form ref="userRef" :model="user" :rules="rules" label-width="80px">
-      <el-form-item label="鐢ㄦ埛鏄电О" prop="nickName">
-         <el-input v-model="user.nickName" maxlength="30" />
-      </el-form-item>
-      <el-form-item label="鎵嬫満鍙风爜" prop="phonenumber">
-         <el-input v-model="user.phonenumber" maxlength="11" />
-      </el-form-item>
-      <el-form-item label="閭" prop="email">
-         <el-input v-model="user.email" maxlength="50" />
-      </el-form-item>
-      <el-form-item label="鎬у埆">
-         <el-radio-group v-model="user.sex">
-            <el-radio label="0">鐢�</el-radio>
-            <el-radio label="1">濂�</el-radio>
-         </el-radio-group>
-      </el-form-item>
-      <el-form-item>
-      <el-button type="primary" @click="submit">淇濆瓨</el-button>
-      <el-button type="danger" @click="close">鍏抽棴</el-button>
-      </el-form-item>
-   </el-form>
-</template>
-
-<script setup>
+<script setup lang="ts">
 import { updateUserProfile } from "@/api/system/user";
+import { FormRules } from "element-plus";
+import { ComponentInternalInstance } from "vue";
+import { PropType } from "vue";
+import { ElForm } from "element-plus";
 
 const props = defineProps({
   user: {
-    type: Object
+    type: Object as PropType<any>,
   }
 });
+const userForm = computed(() => props.user);
 
-const { proxy } = getCurrentInstance();
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 
-const rules = ref({
+const userRef = ref(ElForm);
+
+const rules = ref<FormRules>({
   nickName: [{ required: true, message: "鐢ㄦ埛鏄电О涓嶈兘涓虹┖", trigger: "blur" }],
   email: [{ required: true, message: "閭鍦板潃涓嶈兘涓虹┖", trigger: "blur" }, { type: "email", message: "璇疯緭鍏ユ纭殑閭鍦板潃", trigger: ["blur", "change"] }],
   phonenumber: [{ required: true, message: "鎵嬫満鍙风爜涓嶈兘涓虹┖", trigger: "blur" }, { pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: "璇疯緭鍏ユ纭殑鎵嬫満鍙风爜", trigger: "blur" }],
 });
 
+
 /** 鎻愪氦鎸夐挳 */
-function submit() {
-  proxy.$refs.userRef.validate(valid => {
+const submit = () => {
+  userRef.value.validate(async (valid: boolean) => {
     if (valid) {
-      updateUserProfile(props.user).then(response => {
-        proxy.$modal.msgSuccess("淇敼鎴愬姛");
-      });
+      await updateUserProfile(props.user)
+      proxy?.$modal.msgSuccess("淇敼鎴愬姛");
     }
   });
 };
 /** 鍏抽棴鎸夐挳 */
-function close() {
-  proxy.$tab.closePage();
+const close = () => {
+  proxy?.$tab.closePage();
 };
 </script>
+
+<template>
+	<el-form ref="userRef" :model="userForm" :rules="rules" label-width="80px">
+		<el-form-item label="鐢ㄦ埛鏄电О" prop="nickName">
+			<el-input v-model="userForm.nickName" maxlength="30" />
+		</el-form-item>
+		<el-form-item label="鎵嬫満鍙风爜" prop="phonenumber">
+			<el-input v-model="userForm.phonenumber" maxlength="11" />
+		</el-form-item>
+		<el-form-item label="閭" prop="email">
+			<el-input v-model="userForm.email" maxlength="50" />
+		</el-form-item>
+		<el-form-item label="鎬у埆">
+			<el-radio-group v-model="userForm.sex">
+				<el-radio label="0">鐢�</el-radio>
+				<el-radio label="1">濂�</el-radio>
+			</el-radio-group>
+		</el-form-item>
+		<el-form-item>
+			<el-button type="primary" @click="submit">淇濆瓨</el-button>
+			<el-button type="danger" @click="close">鍏抽棴</el-button>
+		</el-form-item>
+	</el-form>
+</template>
diff --git a/src/views/tool/build/index.vue b/src/views/tool/build/index.vue
index c3543a9..4d2b809 100644
--- a/src/views/tool/build/index.vue
+++ b/src/views/tool/build/index.vue
@@ -1,3 +1,3 @@
 <template>
-   <div> 琛ㄥ崟鏋勫缓 <svg-icon icon-class="build" /> </div>
-</template>
\ No newline at end of file
+	<div>琛ㄥ崟鏋勫缓 <svg-icon icon-class="build" /></div>
+</template>
diff --git a/src/views/tool/gen/basicInfoForm.vue b/src/views/tool/gen/basicInfoForm.vue
index 39c8515..9e06929 100644
--- a/src/views/tool/gen/basicInfoForm.vue
+++ b/src/views/tool/gen/basicInfoForm.vue
@@ -1,42 +1,16 @@
-<template>
-  <el-form ref="basicInfoForm" :model="info" :rules="rules" label-width="150px">
-    <el-row>
-      <el-col :span="12">
-        <el-form-item label="琛ㄥ悕绉�" prop="tableName">
-          <el-input placeholder="璇疯緭鍏ヤ粨搴撳悕绉�" v-model="info.tableName" />
-        </el-form-item>
-      </el-col>
-      <el-col :span="12">
-        <el-form-item label="琛ㄦ弿杩�" prop="tableComment">
-          <el-input placeholder="璇疯緭鍏�" v-model="info.tableComment" />
-        </el-form-item>
-      </el-col>
-      <el-col :span="12">
-        <el-form-item label="瀹炰綋绫诲悕绉�" prop="className">
-          <el-input placeholder="璇疯緭鍏�" v-model="info.className" />
-        </el-form-item>
-      </el-col>
-      <el-col :span="12">
-        <el-form-item label="浣滆��" prop="functionAuthor">
-          <el-input placeholder="璇疯緭鍏�" v-model="info.functionAuthor" />
-        </el-form-item>
-      </el-col>
-      <el-col :span="24">
-        <el-form-item label="澶囨敞" prop="remark">
-          <el-input type="textarea" :rows="3" v-model="info.remark"></el-input>
-        </el-form-item>
-      </el-col>
-    </el-row>
-  </el-form>
-</template>
+<script setup lang="ts">
+import { PropType } from 'vue';
 
-<script setup>
-defineProps({
+const prop = defineProps({
   info: {
-    type: Object,
-    default: null
+    type: Object as PropType<any>,
+    default: () => {
+			return {};
+		}
   }
 });
+
+const infoForm = computed(() => prop.info)
 
 // 琛ㄥ崟鏍¢獙
 const rules = ref({
@@ -46,3 +20,35 @@
   functionAuthor: [{ required: true, message: "璇疯緭鍏ヤ綔鑰�", trigger: "blur" }]
 });
 </script>
+
+<template>
+	<el-form ref="basicInfoForm" :model="infoForm" :rules="rules" label-width="150px">
+		<el-row>
+			<el-col :span="12">
+				<el-form-item label="琛ㄥ悕绉�" prop="tableName">
+					<el-input placeholder="璇疯緭鍏ヤ粨搴撳悕绉�" v-model="infoForm.tableName" />
+				</el-form-item>
+			</el-col>
+			<el-col :span="12">
+				<el-form-item label="琛ㄦ弿杩�" prop="tableComment">
+					<el-input placeholder="璇疯緭鍏�" v-model="infoForm.tableComment" />
+				</el-form-item>
+			</el-col>
+			<el-col :span="12">
+				<el-form-item label="瀹炰綋绫诲悕绉�" prop="className">
+					<el-input placeholder="璇疯緭鍏�" v-model="infoForm.className" />
+				</el-form-item>
+			</el-col>
+			<el-col :span="12">
+				<el-form-item label="浣滆��" prop="functionAuthor">
+					<el-input placeholder="璇疯緭鍏�" v-model="infoForm.functionAuthor" />
+				</el-form-item>
+			</el-col>
+			<el-col :span="24">
+				<el-form-item label="澶囨敞" prop="remark">
+					<el-input type="textarea" :rows="3" v-model="infoForm.remark"></el-input>
+				</el-form-item>
+			</el-col>
+		</el-row>
+	</el-form>
+</template>
diff --git a/src/views/tool/gen/editTable.vue b/src/views/tool/gen/editTable.vue
index a61556c..be1b392 100644
--- a/src/views/tool/gen/editTable.vue
+++ b/src/views/tool/gen/editTable.vue
@@ -1,198 +1,189 @@
-<template>
-  <el-card>
-    <el-tabs v-model="activeName">
-      <el-tab-pane label="鍩烘湰淇℃伅" name="basic">
-        <basic-info-form ref="basicInfo" :info="info" />
-      </el-tab-pane>
-      <el-tab-pane label="瀛楁淇℃伅" name="columnInfo">
-        <el-table ref="dragTable" :data="columns" row-key="columnId" :max-height="tableHeight">
-          <el-table-column label="搴忓彿" type="index" min-width="5%"/>
-          <el-table-column
-            label="瀛楁鍒楀悕"
-            prop="columnName"
-            min-width="10%"
-            :show-overflow-tooltip="true"
-          />
-          <el-table-column label="瀛楁鎻忚堪" min-width="10%">
-            <template #default="scope">
-              <el-input v-model="scope.row.columnComment"></el-input>
-            </template>
-          </el-table-column>
-          <el-table-column
-            label="鐗╃悊绫诲瀷"
-            prop="columnType"
-            min-width="10%"
-            :show-overflow-tooltip="true"
-          />
-          <el-table-column label="Java绫诲瀷" min-width="11%">
-            <template #default="scope">
-              <el-select v-model="scope.row.javaType">
-                <el-option label="Long" value="Long" />
-                <el-option label="String" value="String" />
-                <el-option label="Integer" value="Integer" />
-                <el-option label="Double" value="Double" />
-                <el-option label="BigDecimal" value="BigDecimal" />
-                <el-option label="Date" value="Date" />
-                <el-option label="Boolean" value="Boolean" />
-              </el-select>
-            </template>
-          </el-table-column>
-          <el-table-column label="java灞炴��" min-width="10%">
-            <template #default="scope">
-              <el-input v-model="scope.row.javaField"></el-input>
-            </template>
-          </el-table-column>
-
-          <el-table-column label="鎻掑叆" min-width="5%">
-            <template #default="scope">
-              <el-checkbox true-label="1" false-label="0" v-model="scope.row.isInsert"></el-checkbox>
-            </template>
-          </el-table-column>
-          <el-table-column label="缂栬緫" min-width="5%">
-            <template #default="scope">
-              <el-checkbox true-label="1" false-label="0" v-model="scope.row.isEdit"></el-checkbox>
-            </template>
-          </el-table-column>
-          <el-table-column label="鍒楄〃" min-width="5%">
-            <template #default="scope">
-              <el-checkbox true-label="1" false-label="0" v-model="scope.row.isList"></el-checkbox>
-            </template>
-          </el-table-column>
-          <el-table-column label="鏌ヨ" min-width="5%">
-            <template #default="scope">
-              <el-checkbox true-label="1" false-label="0" v-model="scope.row.isQuery"></el-checkbox>
-            </template>
-          </el-table-column>
-          <el-table-column label="鏌ヨ鏂瑰紡" min-width="10%">
-            <template #default="scope">
-              <el-select v-model="scope.row.queryType">
-                <el-option label="=" value="EQ" />
-                <el-option label="!=" value="NE" />
-                <el-option label=">" value="GT" />
-                <el-option label=">=" value="GE" />
-                <el-option label="<" value="LT" />
-                <el-option label="<=" value="LE" />
-                <el-option label="LIKE" value="LIKE" />
-                <el-option label="BETWEEN" value="BETWEEN" />
-              </el-select>
-            </template>
-          </el-table-column>
-          <el-table-column label="蹇呭~" min-width="5%">
-            <template #default="scope">
-              <el-checkbox true-label="1" false-label="0" v-model="scope.row.isRequired"></el-checkbox>
-            </template>
-          </el-table-column>
-          <el-table-column label="鏄剧ず绫诲瀷" min-width="12%">
-            <template #default="scope">
-              <el-select v-model="scope.row.htmlType">
-                <el-option label="鏂囨湰妗�" value="input" />
-                <el-option label="鏂囨湰鍩�" value="textarea" />
-                <el-option label="涓嬫媺妗�" value="select" />
-                <el-option label="鍗曢�夋" value="radio" />
-                <el-option label="澶嶉�夋" value="checkbox" />
-                <el-option label="鏃ユ湡鎺т欢" value="datetime" />
-                <el-option label="鍥剧墖涓婁紶" value="imageUpload" />
-                <el-option label="鏂囦欢涓婁紶" value="fileUpload" />
-                <el-option label="瀵屾枃鏈帶浠�" value="editor" />
-              </el-select>
-            </template>
-          </el-table-column>
-          <el-table-column label="瀛楀吀绫诲瀷" min-width="12%">
-            <template #default="scope">
-              <el-select v-model="scope.row.dictType" clearable filterable placeholder="璇烽�夋嫨">
-                <el-option
-                  v-for="dict in dictOptions"
-                  :key="dict.dictType"
-                  :label="dict.dictName"
-                  :value="dict.dictType">
-                  <span style="float: left">{{ dict.dictName }}</span>
-                  <span style="float: right; color: #8492a6; font-size: 13px">{{ dict.dictType }}</span>
-              </el-option>
-              </el-select>
-            </template>
-          </el-table-column>
-        </el-table>
-      </el-tab-pane>
-      <el-tab-pane label="鐢熸垚淇℃伅" name="genInfo">
-        <gen-info-form ref="genInfo" :info="info" :tables="tables" />
-      </el-tab-pane>
-    </el-tabs>
-    <el-form label-width="100px">
-      <div style="text-align: center;margin-left:-100px;margin-top:10px;">
-        <el-button type="primary" @click="submitForm()">鎻愪氦</el-button>
-        <el-button @click="close()">杩斿洖</el-button>
-      </div>
-    </el-form>
-  </el-card>
-</template>
-
-<script setup name="GenEdit">
-import { getGenTable, updateGenTable } from "@/api/tool/gen";
-import { optionselect as getDictOptionselect } from "@/api/system/dict/type";
-import basicInfoForm from "./basicInfoForm";
-import genInfoForm from "./genInfoForm";
+<script setup name="GenEdit" lang="ts">
+import { getGenTable, updateGenTable } from '@/api/tool/gen';
+import { DbColumnVO, DbTableVO } from '@/api/tool/gen/types';
+import { optionselect as getDictOptionselect } from '@/api/system/dict/type';
+import { DictTypeVO } from '@/api/system/dict/type/types';
+import basicInfoForm from './basicInfoForm.vue';
+import genInfoForm from "./genInfoForm.vue";
+import { ComponentInternalInstance } from "vue";
 
 const route = useRoute();
-const { proxy } = getCurrentInstance();
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 
-const activeName = ref("columnInfo");
-const tableHeight = ref(document.documentElement.scrollHeight - 245 + "px");
-const tables = ref([]);
-const columns = ref([]);
-const dictOptions = ref([]);
-const info = ref({});
+const activeName = ref('columnInfo');
+const tableHeight = ref(document.documentElement.scrollHeight - 245 + 'px');
+const tables = ref<DbTableVO[]>([]);
+const columns = ref<DbColumnVO[]>([]);
+const dictOptions = ref<DictTypeVO[]>([]);
+const info = ref<Partial<DbTableVO>>({});
+
+const basicInfo = ref(basicInfoForm);
+const genInfo = ref(genInfoForm);
 
 /** 鎻愪氦鎸夐挳 */
-function submitForm() {
-  const basicForm = proxy.$refs.basicInfo.$refs.basicInfoForm;
-  const genForm = proxy.$refs.genInfo.$refs.genInfoForm;
-  Promise.all([basicForm, genForm].map(getFormPromise)).then(res => {
+const submitForm = () => {
+  const basicForm = basicInfo.value.$refs.basicInfoForm;
+  const genForm = genInfo.value.$refs.genInfoForm;
+
+  Promise.all([basicForm, genForm].map(getFormPromise)).then(async res => {
     const validateResult = res.every(item => !!item);
     if (validateResult) {
-      const genTable = Object.assign({}, info.value);
+      const genTable: any = Object.assign({}, info.value);
       genTable.columns = columns.value;
       genTable.params = {
-        treeCode: info.value.treeCode,
+        treeCode: info.value?.treeCode,
         treeName: info.value.treeName,
         treeParentCode: info.value.treeParentCode,
         parentMenuId: info.value.parentMenuId
-      };
-      updateGenTable(genTable).then(res => {
-        proxy.$modal.msgSuccess(res.msg);
-        if (res.code === 200) {
-          close();
-        }
-      });
+			};
+			console.log(genTable)
+			const response = await updateGenTable(genTable);
+			proxy?.$modal.msgSuccess(response.msg);
+			if (response.code === 200) {
+				close();
+			}
     } else {
-      proxy.$modal.msgError("琛ㄥ崟鏍¢獙鏈�氳繃锛岃閲嶆柊妫�鏌ユ彁浜ゅ唴瀹�");
+      proxy?.$modal.msgError("琛ㄥ崟鏍¢獙鏈�氳繃锛岃閲嶆柊妫�鏌ユ彁浜ゅ唴瀹�");
     }
   });
 }
-function getFormPromise(form) {
+const getFormPromise = (form: any) => {
   return new Promise(resolve => {
-    form.validate(res => {
+    form.validate((res: any) => {
       resolve(res);
     });
   });
 }
-function close() {
+const close = () => {
   const obj = { path: "/tool/gen", query: { t: Date.now(), pageNum: route.query.pageNum } };
-  proxy.$tab.closeOpenPage(obj);
+  proxy?.$tab.closeOpenPage(obj);
 }
 
-(() => {
-  const tableId = route.params && route.params.tableId;
+(async () => {
+  const tableId = route.params && route.params.tableId as string;
   if (tableId) {
     // 鑾峰彇琛ㄨ缁嗕俊鎭�
-    getGenTable(tableId).then(res => {
-      columns.value = res.data.rows;
-      info.value = res.data.info;
-      tables.value = res.data.tables;
-    });
+		const res = await getGenTable(tableId);
+		columns.value = res.data.rows;
+		info.value = res.data.info;
+		tables.value = res.data.tables;
     /** 鏌ヨ瀛楀吀涓嬫媺鍒楄〃 */
-    getDictOptionselect().then(response => {
-      dictOptions.value = response.data;
-    });
+		const response = await getDictOptionselect();
+		dictOptions.value = response.data;
   }
 })();
 </script>
+
+<template>
+	<el-card>
+		<el-tabs v-model="activeName">
+			<el-tab-pane label="鍩烘湰淇℃伅" name="basic">
+				<basic-info-form ref="basicInfo" :info="info" />
+			</el-tab-pane>
+			<el-tab-pane label="瀛楁淇℃伅" name="columnInfo">
+				<el-table ref="dragTable" :data="columns" row-key="columnId" :max-height="tableHeight">
+					<el-table-column label="搴忓彿" type="index" min-width="5%" />
+					<el-table-column label="瀛楁鍒楀悕" prop="columnName" min-width="10%" :show-overflow-tooltip="true" />
+					<el-table-column label="瀛楁鎻忚堪" min-width="10%">
+						<template #default="scope">
+							<el-input v-model="scope.row.columnComment"></el-input>
+						</template>
+					</el-table-column>
+					<el-table-column label="鐗╃悊绫诲瀷" prop="columnType" min-width="10%" :show-overflow-tooltip="true" />
+					<el-table-column label="Java绫诲瀷" min-width="11%">
+						<template #default="scope">
+							<el-select v-model="scope.row.javaType">
+								<el-option label="Long" value="Long" />
+								<el-option label="String" value="String" />
+								<el-option label="Integer" value="Integer" />
+								<el-option label="Double" value="Double" />
+								<el-option label="BigDecimal" value="BigDecimal" />
+								<el-option label="Date" value="Date" />
+								<el-option label="Boolean" value="Boolean" />
+							</el-select>
+						</template>
+					</el-table-column>
+					<el-table-column label="java灞炴��" min-width="10%">
+						<template #default="scope">
+							<el-input v-model="scope.row.javaField"></el-input>
+						</template>
+					</el-table-column>
+
+					<el-table-column label="鎻掑叆" min-width="5%">
+						<template #default="scope">
+							<el-checkbox true-label="1" false-label="0" v-model="scope.row.isInsert"></el-checkbox>
+						</template>
+					</el-table-column>
+					<el-table-column label="缂栬緫" min-width="5%">
+						<template #default="scope">
+							<el-checkbox true-label="1" false-label="0" v-model="scope.row.isEdit"></el-checkbox>
+						</template>
+					</el-table-column>
+					<el-table-column label="鍒楄〃" min-width="5%">
+						<template #default="scope">
+							<el-checkbox true-label="1" false-label="0" v-model="scope.row.isList"></el-checkbox>
+						</template>
+					</el-table-column>
+					<el-table-column label="鏌ヨ" min-width="5%">
+						<template #default="scope">
+							<el-checkbox true-label="1" false-label="0" v-model="scope.row.isQuery"></el-checkbox>
+						</template>
+					</el-table-column>
+					<el-table-column label="鏌ヨ鏂瑰紡" min-width="10%">
+						<template #default="scope">
+							<el-select v-model="scope.row.queryType">
+								<el-option label="=" value="EQ" />
+								<el-option label="!=" value="NE" />
+								<el-option label=">" value="GT" />
+								<el-option label=">=" value="GE" />
+								<el-option label="<" value="LT" />
+								<el-option label="<=" value="LE" />
+								<el-option label="LIKE" value="LIKE" />
+								<el-option label="BETWEEN" value="BETWEEN" />
+							</el-select>
+						</template>
+					</el-table-column>
+					<el-table-column label="蹇呭~" min-width="5%">
+						<template #default="scope">
+							<el-checkbox true-label="1" false-label="0" v-model="scope.row.isRequired"></el-checkbox>
+						</template>
+					</el-table-column>
+					<el-table-column label="鏄剧ず绫诲瀷" min-width="12%">
+						<template #default="scope">
+							<el-select v-model="scope.row.htmlType">
+								<el-option label="鏂囨湰妗�" value="input" />
+								<el-option label="鏂囨湰鍩�" value="textarea" />
+								<el-option label="涓嬫媺妗�" value="select" />
+								<el-option label="鍗曢�夋" value="radio" />
+								<el-option label="澶嶉�夋" value="checkbox" />
+								<el-option label="鏃ユ湡鎺т欢" value="datetime" />
+								<el-option label="鍥剧墖涓婁紶" value="imageUpload" />
+								<el-option label="鏂囦欢涓婁紶" value="fileUpload" />
+								<el-option label="瀵屾枃鏈帶浠�" value="editor" />
+							</el-select>
+						</template>
+					</el-table-column>
+					<el-table-column label="瀛楀吀绫诲瀷" min-width="12%">
+						<template #default="scope">
+							<el-select v-model="scope.row.dictType" clearable filterable placeholder="璇烽�夋嫨">
+								<el-option v-for="dict in dictOptions" :key="dict.dictType" :label="dict.dictName" :value="dict.dictType">
+									<span style="float: left">{{ dict.dictName }}</span>
+									<span style="float: right; color: #8492a6; font-size: 13px">{{ dict.dictType }}</span>
+								</el-option>
+							</el-select>
+						</template>
+					</el-table-column>
+				</el-table>
+			</el-tab-pane>
+			<el-tab-pane label="鐢熸垚淇℃伅" name="genInfo">
+				<gen-info-form ref="genInfo" :info="info" :tables="tables" />
+			</el-tab-pane>
+		</el-tabs>
+		<el-form label-width="100px">
+			<div style="text-align: center;margin-left:-100px;margin-top:10px;">
+				<el-button type="primary" @click="submitForm()">鎻愪氦</el-button>
+				<el-button @click="close()">杩斿洖</el-button>
+			</div>
+		</el-form>
+	</el-card>
+</template>
diff --git a/src/views/tool/gen/genInfoForm.vue b/src/views/tool/gen/genInfoForm.vue
index d48276e..b738de8 100644
--- a/src/views/tool/gen/genInfoForm.vue
+++ b/src/views/tool/gen/genInfoForm.vue
@@ -1,243 +1,31 @@
-<template>
-  <el-form ref="genInfoForm" :model="info" :rules="rules" label-width="150px">
-    <el-row>
-      <el-col :span="12">
-        <el-form-item prop="tplCategory">
-          <template #label>鐢熸垚妯℃澘</template>
-          <el-select v-model="info.tplCategory" @change="tplSelectChange">
-            <el-option label="鍗曡〃锛堝鍒犳敼鏌ワ級" value="crud" />
-            <el-option label="鏍戣〃锛堝鍒犳敼鏌ワ級" value="tree" />
-          </el-select>
-        </el-form-item>
-      </el-col>
+<script setup lang="ts">
+import { listMenu } from '@/api/system/menu';
+import { ComponentInternalInstance, PropType } from 'vue';
 
-      <el-col :span="12">
-        <el-form-item prop="packageName">
-          <template #label>
-            鐢熸垚鍖呰矾寰�
-            <el-tooltip content="鐢熸垚鍦ㄥ摢涓猨ava鍖呬笅锛屼緥濡� com.ruoyi.system" placement="top">
-              <el-icon><question-filled /></el-icon>
-            </el-tooltip>
-          </template>
-          <el-input v-model="info.packageName" />
-        </el-form-item>
-      </el-col>
+interface MenuOptionsType {
+  menuId: number;
+  menuName: string;
+  children: MenuOptionsType[] | undefined;
+}
 
-      <el-col :span="12">
-        <el-form-item prop="moduleName">
-          <template #label>
-            鐢熸垚妯″潡鍚�
-            <el-tooltip content="鍙悊瑙d负瀛愮郴缁熷悕锛屼緥濡� system" placement="top">
-              <el-icon><question-filled /></el-icon>
-            </el-tooltip>
-          </template>
-          <el-input v-model="info.moduleName" />
-        </el-form-item>
-      </el-col>
-
-      <el-col :span="12">
-        <el-form-item prop="businessName">
-          <template #label>
-            鐢熸垚涓氬姟鍚�
-            <el-tooltip content="鍙悊瑙d负鍔熻兘鑻辨枃鍚嶏紝渚嬪 user" placement="top">
-              <el-icon><question-filled /></el-icon>
-            </el-tooltip>
-          </template>
-          <el-input v-model="info.businessName" />
-        </el-form-item>
-      </el-col>
-
-      <el-col :span="12">
-        <el-form-item prop="functionName">
-          <template #label>
-            鐢熸垚鍔熻兘鍚�
-            <el-tooltip content="鐢ㄤ綔绫绘弿杩帮紝渚嬪 鐢ㄦ埛" placement="top">
-              <el-icon><question-filled /></el-icon>
-            </el-tooltip>
-          </template>
-          <el-input v-model="info.functionName" />
-        </el-form-item>
-      </el-col>
-
-      <el-col :span="12">
-        <el-form-item>
-          <template #label>
-            涓婄骇鑿滃崟
-            <el-tooltip content="鍒嗛厤鍒版寚瀹氳彍鍗曚笅锛屼緥濡� 绯荤粺绠$悊" placement="top">
-              <el-icon><question-filled /></el-icon>
-            </el-tooltip>
-          </template>
-          <tree-select
-            v-model:value="info.parentMenuId"
-            :options="menuOptions"
-            :objMap="{ value: 'menuId', label: 'menuName', children: 'children' }"
-            placeholder="璇烽�夋嫨绯荤粺鑿滃崟"
-          />
-        </el-form-item>
-      </el-col>
-
-      <el-col :span="12">
-        <el-form-item prop="genType">
-          <template #label>
-            鐢熸垚浠g爜鏂瑰紡
-            <el-tooltip content="榛樿涓簔ip鍘嬬缉鍖呬笅杞斤紝涔熷彲浠ヨ嚜瀹氫箟鐢熸垚璺緞" placement="top">
-              <el-icon><question-filled /></el-icon>
-            </el-tooltip>
-          </template>
-          <el-radio v-model="info.genType" label="0">zip鍘嬬缉鍖�</el-radio>
-          <el-radio v-model="info.genType" label="1">鑷畾涔夎矾寰�</el-radio>
-        </el-form-item>
-      </el-col>
-
-      <el-col :span="24" v-if="info.genType == '1'">
-        <el-form-item prop="genPath">
-          <template #label>
-            鑷畾涔夎矾寰�
-            <el-tooltip content="濉啓纾佺洏缁濆璺緞锛岃嫢涓嶅~鍐欙紝鍒欑敓鎴愬埌褰撳墠Web椤圭洰涓�" placement="top">
-              <el-icon><question-filled /></el-icon>
-            </el-tooltip>
-          </template>
-          <el-input v-model="info.genPath">
-            <template #append>
-              <el-dropdown>
-                <el-button type="primary">
-                  鏈�杩戣矾寰勫揩閫熼�夋嫨
-                  <i class="el-icon-arrow-down el-icon--right"></i>
-                </el-button>
-                <template #dropdown>
-                  <el-dropdown-menu>
-                    <el-dropdown-item @click="info.genPath = '/'">鎭㈠榛樿鐨勭敓鎴愬熀纭�璺緞</el-dropdown-item>
-                  </el-dropdown-menu>
-                </template>
-              </el-dropdown>
-            </template>
-          </el-input>
-        </el-form-item>
-      </el-col>
-    </el-row>
-    
-    <template v-if="info.tplCategory == 'tree'">
-      <h4 class="form-header">鍏朵粬淇℃伅</h4>
-      <el-row v-show="info.tplCategory == 'tree'">
-        <el-col :span="12">
-          <el-form-item>
-            <template #label>
-              鏍戠紪鐮佸瓧娈�
-              <el-tooltip content="鏍戞樉绀虹殑缂栫爜瀛楁鍚嶏紝 濡傦細dept_id" placement="top">
-                <el-icon><question-filled /></el-icon>
-              </el-tooltip>
-            </template>
-            <el-select v-model="info.treeCode" placeholder="璇烽�夋嫨">
-              <el-option
-                v-for="(column, index) in info.columns"
-                :key="index"
-                :label="column.columnName + '锛�' + column.columnComment"
-                :value="column.columnName"
-              ></el-option>
-            </el-select>
-          </el-form-item>
-        </el-col>
-        <el-col :span="12">
-          <el-form-item>
-            <template #label>
-              鏍戠埗缂栫爜瀛楁
-              <el-tooltip content="鏍戞樉绀虹殑鐖剁紪鐮佸瓧娈靛悕锛� 濡傦細parent_Id" placement="top">
-                <el-icon><question-filled /></el-icon>
-              </el-tooltip>
-            </template>
-            <el-select v-model="info.treeParentCode" placeholder="璇烽�夋嫨">
-              <el-option
-                v-for="(column, index) in info.columns"
-                :key="index"
-                :label="column.columnName + '锛�' + column.columnComment"
-                :value="column.columnName"
-              ></el-option>
-            </el-select>
-          </el-form-item>
-        </el-col>
-        <el-col :span="12">
-          <el-form-item>
-            <template #label>
-              鏍戝悕绉板瓧娈�
-              <el-tooltip content="鏍戣妭鐐圭殑鏄剧ず鍚嶇О瀛楁鍚嶏紝 濡傦細dept_name" placement="top">
-                <el-icon><question-filled /></el-icon>
-              </el-tooltip>
-            </template>
-            <el-select v-model="info.treeName" placeholder="璇烽�夋嫨">
-              <el-option
-                v-for="(column, index) in info.columns"
-                :key="index"
-                :label="column.columnName + '锛�' + column.columnComment"
-                :value="column.columnName"
-              ></el-option>
-            </el-select>
-          </el-form-item>
-        </el-col>
-      </el-row>
-    </template>
-
-    <template v-if="info.tplCategory == 'sub'">
-      <h4 class="form-header">鍏宠仈淇℃伅</h4>
-      <el-row>
-        <el-col :span="12">
-          <el-form-item>
-            <template #label>
-              鍏宠仈瀛愯〃鐨勮〃鍚�
-              <el-tooltip content="鍏宠仈瀛愯〃鐨勮〃鍚嶏紝 濡傦細sys_user" placement="top">
-                <el-icon><question-filled /></el-icon>
-              </el-tooltip>
-            </template>
-            <el-select v-model="info.subTableName" placeholder="璇烽�夋嫨" @change="subSelectChange">
-              <el-option
-                v-for="(table, index) in tables"
-                :key="index"
-                :label="table.tableName + '锛�' + table.tableComment"
-                :value="table.tableName"
-              ></el-option>
-            </el-select>
-          </el-form-item>
-        </el-col>
-        <el-col :span="12">
-          <el-form-item>
-            <template #label>
-              瀛愯〃鍏宠仈鐨勫閿悕
-              <el-tooltip content="瀛愯〃鍏宠仈鐨勫閿悕锛� 濡傦細user_id" placement="top">
-                <el-icon><question-filled /></el-icon>
-              </el-tooltip>
-            </template>
-            <el-select v-model="info.subTableFkName" placeholder="璇烽�夋嫨">
-              <el-option
-                v-for="(column, index) in subColumns"
-                :key="index"
-                :label="column.columnName + '锛�' + column.columnComment"
-                :value="column.columnName"
-              ></el-option>
-            </el-select>
-          </el-form-item>
-        </el-col>
-      </el-row>
-    </template>
-
-  </el-form>
-</template>
-
-<script setup>
-import { listMenu } from "@/api/system/menu";
-
-const subColumns = ref([]);
-const menuOptions = ref([]);
-const { proxy } = getCurrentInstance();
+const subColumns = ref<any>([]);
+const menuOptions = ref<Array<MenuOptionsType>>([]);
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 
 const props = defineProps({
   info: {
-    type: Object,
+    type: Object as PropType<any>,
     default: null
   },
   tables: {
-    type: Array,
+    type: Array as PropType<any[]>,
     default: null
   }
 });
+
+const infoForm = computed(() => props.info);
+
+const table = computed(() => props.tables);
 
 // 琛ㄥ崟鏍¢獙
 const rules = ref({
@@ -247,34 +35,255 @@
   businessName: [{ required: true, message: "璇疯緭鍏ョ敓鎴愪笟鍔″悕", trigger: "blur" }],
   functionName: [{ required: true, message: "璇疯緭鍏ョ敓鎴愬姛鑳藉悕", trigger: "blur" }]
 });
-function subSelectChange(value) {
-  props.info.subTableFkName = "";
+const subSelectChange = () => {
+  infoForm.value.subTableFkName = "";
 }
-function tplSelectChange(value) {
+const tplSelectChange = (value: string) => {
   if (value !== "sub") {
-    props.info.subTableName = "";
-    props.info.subTableFkName = "";
+    infoForm.value.subTableName = "";
+		infoForm.value.subTableFkName = "";
   }
 }
-function setSubTableColumns(value) {
-  for (var item in props.tables) {
-    const name = props.tables[item].tableName;
-    if (value === name) {
-      subColumns.value = props.tables[item].columns;
-      break;
+const setSubTableColumns = (value: string) => {
+	table.value.forEach(item => {
+		const name = item.tableName;
+		if (value === name) {
+			subColumns.value = item.columns;
+			return;
     }
-  }
+	})
 }
 /** 鏌ヨ鑿滃崟涓嬫媺鏍戠粨鏋� */
-function getMenuTreeselect() {
-  listMenu().then(response => {
-    menuOptions.value = proxy.handleTree(response.data, "menuId");
-  });
+const getMenuTreeselect = async () => {
+	const res = await listMenu();
+	const data = proxy?.handleTree<MenuOptionsType>(res.data, "menuId");
+	if (data) {
+		menuOptions.value = data
+	}
 }
 
 watch(() => props.info.subTableName, val => {
   setSubTableColumns(val);
 });
 
-getMenuTreeselect();
+onMounted(() => {
+	getMenuTreeselect();
+})
 </script>
+
+<template>
+	<el-form ref="genInfoForm" :model="infoForm" :rules="rules" label-width="150px">
+		<el-row>
+			<el-col :span="12">
+				<el-form-item prop="tplCategory">
+					<template #label>鐢熸垚妯℃澘</template>
+					<el-select v-model="infoForm.tplCategory" @change="tplSelectChange">
+						<el-option label="鍗曡〃锛堝鍒犳敼鏌ワ級" value="crud" />
+						<el-option label="鏍戣〃锛堝鍒犳敼鏌ワ級" value="tree" />
+					</el-select>
+				</el-form-item>
+			</el-col>
+
+			<el-col :span="12">
+				<el-form-item prop="packageName">
+					<template #label>
+						鐢熸垚鍖呰矾寰�
+						<el-tooltip content="鐢熸垚鍦ㄥ摢涓猨ava鍖呬笅锛屼緥濡� com.ruoyi.system" placement="top">
+							<el-icon><question-filled /></el-icon>
+						</el-tooltip>
+					</template>
+					<el-input v-model="infoForm.packageName" />
+				</el-form-item>
+			</el-col>
+
+			<el-col :span="12">
+				<el-form-item prop="moduleName">
+					<template #label>
+						鐢熸垚妯″潡鍚�
+						<el-tooltip content="鍙悊瑙d负瀛愮郴缁熷悕锛屼緥濡� system" placement="top">
+							<el-icon><question-filled /></el-icon>
+						</el-tooltip>
+					</template>
+					<el-input v-model="infoForm.moduleName" />
+				</el-form-item>
+			</el-col>
+
+			<el-col :span="12">
+				<el-form-item prop="businessName">
+					<template #label>
+						鐢熸垚涓氬姟鍚�
+						<el-tooltip content="鍙悊瑙d负鍔熻兘鑻辨枃鍚嶏紝渚嬪 user" placement="top">
+							<el-icon><question-filled /></el-icon>
+						</el-tooltip>
+					</template>
+					<el-input v-model="infoForm.businessName" />
+				</el-form-item>
+			</el-col>
+
+			<el-col :span="12">
+				<el-form-item prop="functionName">
+					<template #label>
+						鐢熸垚鍔熻兘鍚�
+						<el-tooltip content="鐢ㄤ綔绫绘弿杩帮紝渚嬪 鐢ㄦ埛" placement="top">
+							<el-icon><question-filled /></el-icon>
+						</el-tooltip>
+					</template>
+					<el-input v-model="infoForm.functionName" />
+				</el-form-item>
+			</el-col>
+
+			<el-col :span="12">
+				<el-form-item>
+					<template #label>
+						涓婄骇鑿滃崟
+						<el-tooltip content="鍒嗛厤鍒版寚瀹氳彍鍗曚笅锛屼緥濡� 绯荤粺绠$悊" placement="top">
+							<el-icon><question-filled /></el-icon>
+						</el-tooltip>
+					</template>
+					<tree-select
+						v-model:value="infoForm.parentMenuId"
+						:options="menuOptions"
+						:objMap="{ value: 'menuId', label: 'menuName', children: 'children' }"
+						placeholder="璇烽�夋嫨绯荤粺鑿滃崟"
+					/>
+				</el-form-item>
+			</el-col>
+
+			<el-col :span="12">
+				<el-form-item prop="genType">
+					<template #label>
+						鐢熸垚浠g爜鏂瑰紡
+						<el-tooltip content="榛樿涓簔ip鍘嬬缉鍖呬笅杞斤紝涔熷彲浠ヨ嚜瀹氫箟鐢熸垚璺緞" placement="top">
+							<el-icon><question-filled /></el-icon>
+						</el-tooltip>
+					</template>
+					<el-radio v-model="infoForm.genType" label="0">zip鍘嬬缉鍖�</el-radio>
+					<el-radio v-model="infoForm.genType" label="1">鑷畾涔夎矾寰�</el-radio>
+				</el-form-item>
+			</el-col>
+
+			<el-col :span="24" v-if="infoForm.genType == '1'">
+				<el-form-item prop="genPath">
+					<template #label>
+						鑷畾涔夎矾寰�
+						<el-tooltip content="濉啓纾佺洏缁濆璺緞锛岃嫢涓嶅~鍐欙紝鍒欑敓鎴愬埌褰撳墠Web椤圭洰涓�" placement="top">
+							<el-icon><question-filled /></el-icon>
+						</el-tooltip>
+					</template>
+					<el-input v-model="infoForm.genPath">
+						<template #append>
+							<el-dropdown>
+								<el-button type="primary">
+									鏈�杩戣矾寰勫揩閫熼�夋嫨
+									<i class="el-icon-arrow-down el-icon--right"></i>
+								</el-button>
+								<template #dropdown>
+									<el-dropdown-menu>
+										<el-dropdown-item @click="infoForm.genPath = '/'">鎭㈠榛樿鐨勭敓鎴愬熀纭�璺緞</el-dropdown-item>
+									</el-dropdown-menu>
+								</template>
+							</el-dropdown>
+						</template>
+					</el-input>
+				</el-form-item>
+			</el-col>
+		</el-row>
+
+		<template v-if="info.tplCategory == 'tree'">
+			<h4 class="form-header">鍏朵粬淇℃伅</h4>
+			<el-row v-show="info.tplCategory == 'tree'">
+				<el-col :span="12">
+					<el-form-item>
+						<template #label>
+							鏍戠紪鐮佸瓧娈�
+							<el-tooltip content="鏍戞樉绀虹殑缂栫爜瀛楁鍚嶏紝 濡傦細dept_id" placement="top">
+								<el-icon><question-filled /></el-icon>
+							</el-tooltip>
+						</template>
+						<el-select v-model="infoForm.treeCode" placeholder="璇烽�夋嫨">
+							<el-option
+								v-for="(column, index) in info.columns"
+								:key="index"
+								:label="column.columnName + '锛�' + column.columnComment"
+								:value="column.columnName"
+							></el-option>
+						</el-select>
+					</el-form-item>
+				</el-col>
+				<el-col :span="12">
+					<el-form-item>
+						<template #label>
+							鏍戠埗缂栫爜瀛楁
+							<el-tooltip content="鏍戞樉绀虹殑鐖剁紪鐮佸瓧娈靛悕锛� 濡傦細parent_Id" placement="top">
+								<el-icon><question-filled /></el-icon>
+							</el-tooltip>
+						</template>
+						<el-select v-model="infoForm.treeParentCode" placeholder="璇烽�夋嫨">
+							<el-option
+								v-for="(column, index) in infoForm.columns"
+								:key="index"
+								:label="column.columnName + '锛�' + column.columnComment"
+								:value="column.columnName"
+							></el-option>
+						</el-select>
+					</el-form-item>
+				</el-col>
+				<el-col :span="12">
+					<el-form-item>
+						<template #label>
+							鏍戝悕绉板瓧娈�
+							<el-tooltip content="鏍戣妭鐐圭殑鏄剧ず鍚嶇О瀛楁鍚嶏紝 濡傦細dept_name" placement="top">
+								<el-icon><question-filled /></el-icon>
+							</el-tooltip>
+						</template>
+						<el-select v-model="infoForm.treeName" placeholder="璇烽�夋嫨">
+							<el-option
+								v-for="(column, index) in info.columns"
+								:key="index"
+								:label="column.columnName + '锛�' + column.columnComment"
+								:value="column.columnName"
+							></el-option>
+						</el-select>
+					</el-form-item>
+				</el-col>
+			</el-row>
+		</template>
+
+		<template v-if="info.tplCategory == 'sub'">
+			<h4 class="form-header">鍏宠仈淇℃伅</h4>
+			<el-row>
+				<el-col :span="12">
+					<el-form-item>
+						<template #label>
+							鍏宠仈瀛愯〃鐨勮〃鍚�
+							<el-tooltip content="鍏宠仈瀛愯〃鐨勮〃鍚嶏紝 濡傦細sys_user" placement="top">
+								<el-icon><question-filled /></el-icon>
+							</el-tooltip>
+						</template>
+						<el-select v-model="infoForm.subTableName" placeholder="璇烽�夋嫨" @change="subSelectChange">
+							<el-option v-for="(t, index) in table" :key="index" :label="t.tableName + '锛�' + t.tableComment" :value="t.tableName"></el-option>
+						</el-select>
+					</el-form-item>
+				</el-col>
+				<el-col :span="12">
+					<el-form-item>
+						<template #label>
+							瀛愯〃鍏宠仈鐨勫閿悕
+							<el-tooltip content="瀛愯〃鍏宠仈鐨勫閿悕锛� 濡傦細user_id" placement="top">
+								<el-icon><question-filled /></el-icon>
+							</el-tooltip>
+						</template>
+						<el-select v-model="infoForm.subTableFkName" placeholder="璇烽�夋嫨">
+							<el-option
+								v-for="(column, index) in subColumns"
+								:key="index"
+								:label="column.columnName + '锛�' + column.columnComment"
+								:value="column.columnName"
+							></el-option>
+						</el-select>
+					</el-form-item>
+				</el-col>
+			</el-row>
+		</template>
+	</el-form>
+</template>
diff --git a/src/views/tool/gen/importTable.vue b/src/views/tool/gen/importTable.vue
index 33b5633..63c6d4a 100644
--- a/src/views/tool/gen/importTable.vue
+++ b/src/views/tool/gen/importTable.vue
@@ -1,118 +1,106 @@
-<template>
-  <!-- 瀵煎叆琛� -->
-  <el-dialog title="瀵煎叆琛�" v-model="visible" width="800px" top="5vh" append-to-body>
-    <el-form :model="queryParams" ref="queryRef" :inline="true">
-      <el-form-item label="琛ㄥ悕绉�" prop="tableName">
-        <el-input
-          v-model="queryParams.tableName"
-          placeholder="璇疯緭鍏ヨ〃鍚嶇О"
-          clearable
-          @keyup.enter="handleQuery"
-        />
-      </el-form-item>
-      <el-form-item label="琛ㄦ弿杩�" prop="tableComment">
-        <el-input
-          v-model="queryParams.tableComment"
-          placeholder="璇疯緭鍏ヨ〃鎻忚堪"
-          clearable
-          @keyup.enter="handleQuery"
-        />
-      </el-form-item>
-      <el-form-item>
-        <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
-        <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
-      </el-form-item>
-    </el-form>
-    <el-row>
-      <el-table @row-click="clickRow" ref="table" :data="dbTableList" @selection-change="handleSelectionChange" height="260px">
-        <el-table-column type="selection" width="55"></el-table-column>
-        <el-table-column prop="tableName" label="琛ㄥ悕绉�" :show-overflow-tooltip="true"></el-table-column>
-        <el-table-column prop="tableComment" label="琛ㄦ弿杩�" :show-overflow-tooltip="true"></el-table-column>
-        <el-table-column prop="createTime" label="鍒涘缓鏃堕棿"></el-table-column>
-        <el-table-column prop="updateTime" label="鏇存柊鏃堕棿"></el-table-column>
-      </el-table>
-      <pagination
-        v-show="total>0"
-        :total="total"
-        v-model:page="queryParams.pageNum"
-        v-model:limit="queryParams.pageSize"
-        @pagination="getList"
-      />
-    </el-row>
-    <template #footer>
-      <div class="dialog-footer">
-        <el-button type="primary" @click="handleImportTable">纭� 瀹�</el-button>
-        <el-button @click="visible = false">鍙� 娑�</el-button>
-      </div>
-    </template>
-  </el-dialog>
-</template>
-
-<script setup>
-import { listDbTable, importTable } from "@/api/tool/gen";
+<script setup lang="ts">
+import { listDbTable, importTable } from '@/api/tool/gen';
+import { DbTableQuery, DbTableVO } from '@/api/tool/gen/types';
+import { ComponentInternalInstance } from 'vue';
+import { ElTable, ElForm } from 'element-plus';
 
 const total = ref(0);
 const visible = ref(false);
-const tables = ref([]);
-const dbTableList = ref([]);
-const { proxy } = getCurrentInstance();
+const tables = ref<Array<string>>([]);
+const dbTableList = ref<Array<DbTableVO>>([]);
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 
-const queryParams = reactive({
+const tableRef = ref(ElTable);
+const queryFormRef = ref(ElForm);
+
+const queryParams = reactive<DbTableQuery>({
   pageNum: 1,
   pageSize: 10,
-  tableName: undefined,
-  tableComment: undefined
+  tableName: '',
+  tableComment: ''
 });
 
 const emit = defineEmits(["ok"]);
 
 /** 鏌ヨ鍙傛暟鍒楄〃 */
-function show() {
+const show = () => {
   getList();
   visible.value = true;
 }
 /** 鍗曞嚮閫夋嫨琛� */
-function clickRow(row) {
-  proxy.$refs.table.toggleRowSelection(row);
+const clickRow = (row: DbTableVO) => {
+  tableRef.value.toggleRowSelection(row);
 }
 /** 澶氶�夋閫変腑鏁版嵁 */
-function handleSelectionChange(selection) {
+const handleSelectionChange = (selection: DbTableVO[]) => {
   tables.value = selection.map(item => item.tableName);
 }
 /** 鏌ヨ琛ㄦ暟鎹� */
-function getList() {
-  listDbTable(queryParams).then(res => {
-    dbTableList.value = res.rows;
-    total.value = res.total;
-  });
+const getList = async () => {
+	const res = await listDbTable(queryParams);
+	dbTableList.value = res.rows;
+	total.value = res.total;
 }
 /** 鎼滅储鎸夐挳鎿嶄綔 */
-function handleQuery() {
+const handleQuery = () => {
   queryParams.pageNum = 1;
   getList();
 }
 /** 閲嶇疆鎸夐挳鎿嶄綔 */
-function resetQuery() {
-  proxy.resetForm("queryRef");
+const resetQuery = () => {
+	queryFormRef.value.resetFields();
   handleQuery();
 }
 /** 瀵煎叆鎸夐挳鎿嶄綔 */
-function handleImportTable() {
+const handleImportTable = async () => {
   const tableNames = tables.value.join(",");
   if (tableNames == "") {
-    proxy.$modal.msgError("璇烽�夋嫨瑕佸鍏ョ殑琛�");
+    proxy?.$modal.msgError("璇烽�夋嫨瑕佸鍏ョ殑琛�");
     return;
   }
-  importTable({ tables: tableNames }).then(res => {
-    proxy.$modal.msgSuccess(res.msg);
-    if (res.code === 200) {
-      visible.value = false;
-      emit("ok");
-    }
-  });
+	const res = await importTable({ tables: tableNames });
+	proxy?.$modal.msgSuccess(res.msg);
+	if (res.code === 200) {
+		visible.value = false;
+		emit("ok");
+	}
 }
 
 defineExpose({
   show,
 });
 </script>
+
+<template>
+	<!-- 瀵煎叆琛� -->
+	<el-dialog title="瀵煎叆琛�" v-model="visible" width="800px" top="5vh" append-to-body>
+		<el-form :model="queryParams" ref="queryFormRef" :inline="true">
+			<el-form-item label="琛ㄥ悕绉�" prop="tableName">
+				<el-input v-model="queryParams.tableName" placeholder="璇疯緭鍏ヨ〃鍚嶇О" clearable @keyup.enter="handleQuery" />
+			</el-form-item>
+			<el-form-item label="琛ㄦ弿杩�" prop="tableComment">
+				<el-input v-model="queryParams.tableComment" placeholder="璇疯緭鍏ヨ〃鎻忚堪" clearable @keyup.enter="handleQuery" />
+			</el-form-item>
+			<el-form-item>
+				<el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
+				<el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
+			</el-form-item>
+		</el-form>
+		<el-row>
+			<el-table @row-click="clickRow" ref="tableRef" :data="dbTableList" @selection-change="handleSelectionChange" height="260px">
+				<el-table-column type="selection" width="55"></el-table-column>
+				<el-table-column prop="tableName" label="琛ㄥ悕绉�" :show-overflow-tooltip="true"></el-table-column>
+				<el-table-column prop="tableComment" label="琛ㄦ弿杩�" :show-overflow-tooltip="true"></el-table-column>
+				<el-table-column prop="createTime" label="鍒涘缓鏃堕棿"></el-table-column>
+				<el-table-column prop="updateTime" label="鏇存柊鏃堕棿"></el-table-column>
+			</el-table>
+			<pagination v-show="total>0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
+		</el-row>
+		<template #footer>
+			<div class="dialog-footer">
+				<el-button type="primary" @click="handleImportTable">纭� 瀹�</el-button>
+				<el-button @click="visible = false">鍙� 娑�</el-button>
+			</div>
+		</template>
+	</el-dialog>
+</template>
diff --git a/src/views/tool/gen/index.vue b/src/views/tool/gen/index.vue
index fb2fc9e..22d15d3 100644
--- a/src/views/tool/gen/index.vue
+++ b/src/views/tool/gen/index.vue
@@ -1,296 +1,244 @@
-<template>
-  <div class="app-container">
-    <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch">
-      <el-form-item label="鏁版嵁婧�" prop="dataName">
-        <el-input
-            v-model="queryParams.dataName"
-            placeholder="璇疯緭鍏ユ暟鎹簮鍚嶇О"
-            clearable
-            style="width: 200px"
-            @keyup.enter="handleQuery"
-        />
-      </el-form-item>
-      <el-form-item label="琛ㄥ悕绉�" prop="tableName">
-        <el-input
-          v-model="queryParams.tableName"
-          placeholder="璇疯緭鍏ヨ〃鍚嶇О"
-          clearable
-          style="width: 200px"
-          @keyup.enter="handleQuery"
-        />
-      </el-form-item>
-      <el-form-item label="琛ㄦ弿杩�" prop="tableComment">
-        <el-input
-          v-model="queryParams.tableComment"
-          placeholder="璇疯緭鍏ヨ〃鎻忚堪"
-          clearable
-          style="width: 200px"
-          @keyup.enter="handleQuery"
-        />
-      </el-form-item>
-      <el-form-item label="鍒涘缓鏃堕棿" style="width: 308px">
-        <el-date-picker
-          v-model="dateRange"
-          value-format="YYYY-MM-DD"
-          type="daterange"
-          range-separator="-"
-          start-placeholder="寮�濮嬫棩鏈�"
-          end-placeholder="缁撴潫鏃ユ湡"
-        ></el-date-picker>
-      </el-form-item>
-      <el-form-item>
-        <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
-        <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
-      </el-form-item>
-    </el-form>
-
-    <el-row :gutter="10" class="mb8">
-      <el-col :span="1.5">
-        <el-button
-          type="primary"
-          plain
-          icon="Download"
-          @click="handleGenTable"
-          v-hasPermi="['tool:gen:code']"
-        >鐢熸垚</el-button>
-      </el-col>
-      <el-col :span="1.5">
-        <el-button
-          type="info"
-          plain
-          icon="Upload"
-          @click="openImportTable"
-          v-hasPermi="['tool:gen:import']"
-        >瀵煎叆</el-button>
-      </el-col>
-      <el-col :span="1.5">
-        <el-button
-          type="success"
-          plain
-          icon="Edit"
-          :disabled="single"
-          @click="handleEditTable"
-          v-hasPermi="['tool:gen:edit']"
-        >淇敼</el-button>
-      </el-col>
-      <el-col :span="1.5">
-        <el-button
-          type="danger"
-          plain
-          icon="Delete"
-          :disabled="multiple"
-          @click="handleDelete"
-          v-hasPermi="['tool:gen:remove']"
-        >鍒犻櫎</el-button>
-      </el-col>
-      <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
-    </el-row>
-
-    <el-table v-loading="loading" :data="tableList" @selection-change="handleSelectionChange">
-      <el-table-column type="selection" align="center" width="55"></el-table-column>
-      <el-table-column label="搴忓彿" type="index" width="50" align="center">
-        <template #default="scope">
-          <span>{{(queryParams.pageNum - 1) * queryParams.pageSize + scope.$index + 1}}</span>
-        </template>
-      </el-table-column>
-      <el-table-column
-        label="琛ㄥ悕绉�"
-        align="center"
-        prop="tableName"
-        :show-overflow-tooltip="true"
-      />
-      <el-table-column
-        label="琛ㄦ弿杩�"
-        align="center"
-        prop="tableComment"
-        :show-overflow-tooltip="true"
-      />
-      <el-table-column
-        label="瀹炰綋"
-        align="center"
-        prop="className"
-        :show-overflow-tooltip="true"
-      />
-      <el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime" width="160" />
-      <el-table-column label="鏇存柊鏃堕棿" align="center" prop="updateTime" width="160" />
-      <el-table-column label="鎿嶄綔" align="center" width="330" class-name="small-padding fixed-width">
-        <template #default="scope">
-          <el-tooltip content="棰勮" placement="top">
-            <el-button link type="primary" icon="View" @click="handlePreview(scope.row)" v-hasPermi="['tool:gen:preview']"></el-button>
-          </el-tooltip>
-          <el-tooltip content="缂栬緫" placement="top">
-            <el-button link type="primary" icon="Edit" @click="handleEditTable(scope.row)" v-hasPermi="['tool:gen:edit']"></el-button>
-          </el-tooltip>
-          <el-tooltip content="鍒犻櫎" placement="top">
-            <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['tool:gen:remove']"></el-button>
-          </el-tooltip>
-          <el-tooltip content="鍚屾" placement="top">
-            <el-button link type="primary" icon="Refresh" @click="handleSynchDb(scope.row)" v-hasPermi="['tool:gen:edit']"></el-button>
-          </el-tooltip>
-          <el-tooltip content="鐢熸垚浠g爜" placement="top">
-            <el-button link type="primary" icon="Download" @click="handleGenTable(scope.row)" v-hasPermi="['tool:gen:code']"></el-button>
-          </el-tooltip>
-        </template>
-      </el-table-column>
-    </el-table>
-    <pagination
-      v-show="total>0"
-      :total="total"
-      v-model:page="queryParams.pageNum"
-      v-model:limit="queryParams.pageSize"
-      @pagination="getList"
-    />
-    <!-- 棰勮鐣岄潰 -->
-    <el-dialog :title="preview.title" v-model="preview.open" width="80%" top="5vh" append-to-body class="scrollbar">
-      <el-tabs v-model="preview.activeName">
-        <el-tab-pane
-          v-for="(value, key) in preview.data"
-          :label="key.substring(key.lastIndexOf('/')+1,key.indexOf('.vm'))"
-          :name="key.substring(key.lastIndexOf('/')+1,key.indexOf('.vm'))"
-          :key="value"
-        >
-          <el-link :underline="false" icon="DocumentCopy" v-copyText="value" v-copyText:callback="copyTextSuccess" style="float:right">&nbsp;澶嶅埗</el-link>
-          <pre>{{ value }}</pre>
-        </el-tab-pane>
-      </el-tabs>
-    </el-dialog>
-    <import-table ref="importRef" @ok="handleQuery" />
-  </div>
-</template>
-
-<script setup name="Gen">
-import { listTable, previewTable, delTable, genCode, synchDb } from "@/api/tool/gen";
-import router from "@/router";
-import importTable from "./importTable";
+<script setup name="Gen" lang="ts">
+import { listTable, previewTable, delTable, genCode, synchDb } from '@/api/tool/gen';
+import { TableQuery, TableVO } from '@/api/tool/gen/types';
+import router from '@/router';
+import importTable from './importTable.vue';
+import { ComponentInternalInstance } from 'vue';
+import { ElForm, DateModelType } from 'element-plus';
 
 const route = useRoute();
-const { proxy } = getCurrentInstance();
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 
-const tableList = ref([]);
+const tableList = ref<TableVO[]>([]);
 const loading = ref(true);
 const showSearch = ref(true);
-const ids = ref([]);
+const ids = ref<Array<string | number>>([]);
 const single = ref(true);
 const multiple = ref(true);
 const total = ref(0);
-const tableNames = ref([]);
-const dateRange = ref([]);
+const tableNames = ref<Array<string>>([]);
+const dateRange = ref<[DateModelType, DateModelType]>(['', '']);
 const uniqueId = ref("");
 
-const data = reactive({
-  queryParams: {
-    pageNum: 1,
-    pageSize: 10,
-    tableName: undefined,
-    tableComment: undefined,
-    dataName: "master"
-  },
-  preview: {
-    open: false,
-    title: "浠g爜棰勮",
-    data: {},
-    activeName: "domain.java"
-  }
+const queryFormRef = ref(ElForm);
+const importRef = ref(importTable);
+
+const queryParams = ref<TableQuery>({
+	pageNum: 1,
+	pageSize: 10,
+	tableName: '',
+	tableComment: '',
+	dataName: "master"
+})
+
+const preview = ref <any>({
+	data: {},
+	activeName: 'domain.java'
+})
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: '浠g爜棰勮'
 });
 
-const { queryParams, preview } = toRefs(data);
-
-localStorage.setItem("dataName", queryParams.value.dataName);
+localStorage.setItem('dataName', queryParams.value.dataName);
 
 onActivated(() => {
-  const time = route.query.t;
-  if (time != null && time != uniqueId.value) {
-    uniqueId.value = time;
-    queryParams.value.pageNum = Number(route.query.pageNum);
-    dateRange.value = [];
-    proxy.resetForm("queryForm");
-    getList();
-  }
+	const time = route.query.t;
+	if (time != null && time != uniqueId.value) {
+		uniqueId.value = time as string;
+		queryParams.value.pageNum = Number(route.query.pageNum);
+		dateRange.value = ['', ''];
+		queryFormRef.value.resetFields();
+		getList();
+	}
 })
 
 /** 鏌ヨ琛ㄩ泦鍚� */
-function getList() {
-  loading.value = true;
-  listTable(proxy.addDateRange(queryParams.value, dateRange.value)).then(response => {
-    tableList.value = response.rows;
-    total.value = response.total;
-    loading.value = false;
-  });
+const getList = async () => {
+	loading.value = true;
+	const res = await listTable(proxy?.addDateRange(queryParams.value, dateRange.value));
+	tableList.value = res.rows;
+	total.value = res.total;
+	loading.value = false;
 }
 /** 鎼滅储鎸夐挳鎿嶄綔 */
-function handleQuery() {
-  localStorage.setItem("dataName", queryParams.value.dataName);
-  queryParams.value.pageNum = 1;
-  getList();
+const handleQuery = () => {
+	localStorage.setItem('dataName', queryParams.value.dataName);
+	queryParams.value.pageNum = 1;
+	getList();
 }
 /** 鐢熸垚浠g爜鎿嶄綔 */
-function handleGenTable(row) {
-  const tbNames = row.tableName || tableNames.value;
-  if (tbNames == "") {
-    proxy.$modal.msgError("璇烽�夋嫨瑕佺敓鎴愮殑鏁版嵁");
-    return;
-  }
-  if (row.genType === "1") {
-    genCode(row.tableName).then(response => {
-      proxy.$modal.msgSuccess("鎴愬姛鐢熸垚鍒拌嚜瀹氫箟璺緞锛�" + row.genPath);
-    });
-  } else {
-    proxy.$download.zip("/tool/gen/batchGenCode?tables=" + tbNames, "ruoyi.zip");
-  }
+const handleGenTable = async (row?: TableVO) => {
+	const tbNames = row?.tableName || tableNames.value;
+	if (tbNames == "") {
+		proxy?.$modal.msgError('璇烽�夋嫨瑕佺敓鎴愮殑鏁版嵁');
+		return;
+	}
+	if (row?.genType === "1") {
+		await genCode(row.tableName);
+		proxy?.$modal.msgSuccess('鎴愬姛鐢熸垚鍒拌嚜瀹氫箟璺緞锛�' + row.genPath);
+	} else {
+		proxy?.$download.zip('/tool/gen/batchGenCode?tables=' + tbNames, 'ruoyi.zip');
+	}
 }
 /** 鍚屾鏁版嵁搴撴搷浣� */
-function handleSynchDb(row) {
-  const tableName = row.tableName;
-  proxy.$modal.confirm('纭瑕佸己鍒跺悓姝�"' + tableName + '"琛ㄧ粨鏋勫悧锛�').then(function () {
-    return synchDb(tableName);
-  }).then(() => {
-    proxy.$modal.msgSuccess("鍚屾鎴愬姛");
-  }).catch(() => {});
+const handleSynchDb = async (row: TableVO) => {
+	const tableName = row.tableName;
+	await proxy?.$modal.confirm('纭瑕佸己鍒跺悓姝�"' + tableName + '"琛ㄧ粨鏋勫悧锛�');
+	await synchDb(tableName);
+	proxy?.$modal.msgSuccess('鍚屾鎴愬姛');
 }
 /** 鎵撳紑瀵煎叆琛ㄥ脊绐� */
-function openImportTable() {
-  proxy.$refs["importRef"].show();
+const openImportTable = () => {
+	importRef.value.show();
 }
 /** 閲嶇疆鎸夐挳鎿嶄綔 */
-function resetQuery() {
-  dateRange.value = [];
-  proxy.resetForm("queryRef");
-  handleQuery();
+const resetQuery = () => {
+	dateRange.value = ['', ''];
+	queryFormRef.value.resetFields();
+	handleQuery();
 }
 /** 棰勮鎸夐挳 */
-function handlePreview(row) {
-  previewTable(row.tableId).then(response => {
-    preview.value.data = response.data;
-    preview.value.open = true;
-    preview.value.activeName = "domain.java";
-  });
+const handlePreview = async (row: TableVO) => {
+	const res = await previewTable(row.tableId);
+	preview.value.data = res.data;
+	dialog.visible = true;
+	preview.value.activeName = 'domain.java';
 }
 /** 澶嶅埗浠g爜鎴愬姛 */
-function copyTextSuccess() {
-  proxy.$modal.msgSuccess("澶嶅埗鎴愬姛");
+const copyTextSuccess = () => {
+	proxy?.$modal.msgSuccess('澶嶅埗鎴愬姛');
 }
 // 澶氶�夋閫変腑鏁版嵁
-function handleSelectionChange(selection) {
-  ids.value = selection.map(item => item.tableId);
-  tableNames.value = selection.map(item => item.tableName);
-  single.value = selection.length != 1;
-  multiple.value = !selection.length;
+const handleSelectionChange = (selection: TableVO[]) => {
+	ids.value = selection.map(item => item.tableId);
+	tableNames.value = selection.map(item => item.tableName);
+	single.value = selection.length != 1;
+	multiple.value = !selection.length;
 }
 /** 淇敼鎸夐挳鎿嶄綔 */
-function handleEditTable(row) {
-  const tableId = row.tableId || ids.value[0];
-  router.push({ path: "/tool/gen-edit/index/" + tableId, query: { pageNum: queryParams.value.pageNum } });
+const handleEditTable = (row?: TableVO) => {
+	const tableId = row?.tableId || ids.value[0];
+	router.push({ path: '/tool/gen-edit/index/' + tableId, query: { pageNum: queryParams.value.pageNum } });
 }
 /** 鍒犻櫎鎸夐挳鎿嶄綔 */
-function handleDelete(row) {
-  const tableIds = row.tableId || ids.value;
-  proxy.$modal.confirm('鏄惁纭鍒犻櫎琛ㄧ紪鍙蜂负"' + tableIds + '"鐨勬暟鎹」锛�').then(function () {
-    return delTable(tableIds);
-  }).then(() => {
-    getList();
-    proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
-  }).catch(() => {});
+const handleDelete = async (row?: TableVO) => {
+	const tableIds = row?.tableId || ids.value;
+	await proxy?.$modal.confirm('鏄惁纭鍒犻櫎琛ㄧ紪鍙蜂负"' + tableIds + '"鐨勬暟鎹」锛�');
+	await delTable(tableIds);
+	getList();
+	proxy?.$modal.msgSuccess('鍒犻櫎鎴愬姛');
 }
 
-getList();
+onMounted(() => {
+	getList();
+})
 </script>
+
+<template>
+	<div class="p-2">
+		<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
+			<div class="search" v-show="showSearch">
+				<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
+					<el-form-item label="鏁版嵁婧�" prop="dataName">
+						<el-input v-model="queryParams.dataName" placeholder="璇疯緭鍏ユ暟鎹簮鍚嶇О" clearable style="width: 200px" @keyup.enter="handleQuery" />
+					</el-form-item>
+					<el-form-item label="琛ㄥ悕绉�" prop="tableName">
+						<el-input v-model="queryParams.tableName" placeholder="璇疯緭鍏ヨ〃鍚嶇О" clearable style="width: 200px" @keyup.enter="handleQuery" />
+					</el-form-item>
+					<el-form-item label="琛ㄦ弿杩�" prop="tableComment">
+						<el-input v-model="queryParams.tableComment" placeholder="璇疯緭鍏ヨ〃鎻忚堪" clearable style="width: 200px" @keyup.enter="handleQuery" />
+					</el-form-item>
+					<el-form-item label="鍒涘缓鏃堕棿" style="width: 308px">
+						<el-date-picker
+							v-model="dateRange"
+							value-format="YYYY-MM-DD"
+							type="daterange"
+							range-separator="-"
+							start-placeholder="寮�濮嬫棩鏈�"
+							end-placeholder="缁撴潫鏃ユ湡"
+						></el-date-picker>
+					</el-form-item>
+					<el-form-item>
+						<el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
+						<el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
+					</el-form-item>
+				</el-form>
+			</div>
+		</transition>
+
+		<el-card shadow="never">
+			<template #header>
+				<el-row :gutter="10" class="mb8">
+					<el-col :span="1.5">
+						<el-button type="primary" plain icon="Download" @click="handleGenTable()" v-hasPermi="['tool:gen:code']">鐢熸垚</el-button>
+					</el-col>
+					<el-col :span="1.5">
+						<el-button type="info" plain icon="Upload" @click="openImportTable" v-hasPermi="['tool:gen:import']">瀵煎叆</el-button>
+					</el-col>
+					<el-col :span="1.5">
+						<el-button type="success" plain icon="Edit" :disabled="single" @click="handleEditTable()" v-hasPermi="['tool:gen:edit']">淇敼</el-button>
+					</el-col>
+					<el-col :span="1.5">
+						<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['tool:gen:remove']">
+							鍒犻櫎
+						</el-button>
+					</el-col>
+					<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+				</el-row>
+			</template>
+
+			<el-table v-loading="loading" :data="tableList" @selection-change="handleSelectionChange">
+				<el-table-column type="selection" align="center" width="55"></el-table-column>
+				<el-table-column label="搴忓彿" type="index" width="50" align="center">
+					<template #default="scope">
+						<span>{{ (queryParams.pageNum - 1) * queryParams.pageSize + scope.$index + 1 }}</span>
+					</template>
+				</el-table-column>
+				<el-table-column label="琛ㄥ悕绉�" align="center" prop="tableName" :show-overflow-tooltip="true" />
+				<el-table-column label="琛ㄦ弿杩�" align="center" prop="tableComment" :show-overflow-tooltip="true" />
+				<el-table-column label="瀹炰綋" align="center" prop="className" :show-overflow-tooltip="true" />
+				<el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime" width="160" />
+				<el-table-column label="鏇存柊鏃堕棿" align="center" prop="updateTime" width="160" />
+				<el-table-column label="鎿嶄綔" align="center" width="330" class-name="small-padding fixed-width">
+					<template #default="scope">
+						<el-tooltip content="棰勮" placement="top">
+							<el-button link type="primary" icon="View" @click="handlePreview(scope.row)" v-hasPermi="['tool:gen:preview']"></el-button>
+						</el-tooltip>
+						<el-tooltip content="缂栬緫" placement="top">
+							<el-button link type="primary" icon="Edit" @click="handleEditTable(scope.row)" v-hasPermi="['tool:gen:edit']"></el-button>
+						</el-tooltip>
+						<el-tooltip content="鍒犻櫎" placement="top">
+							<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['tool:gen:remove']"></el-button>
+						</el-tooltip>
+						<el-tooltip content="鍚屾" placement="top">
+							<el-button link type="primary" icon="Refresh" @click="handleSynchDb(scope.row)" v-hasPermi="['tool:gen:edit']"></el-button>
+						</el-tooltip>
+						<el-tooltip content="鐢熸垚浠g爜" placement="top">
+							<el-button link type="primary" icon="Download" @click="handleGenTable(scope.row)" v-hasPermi="['tool:gen:code']"></el-button>
+						</el-tooltip>
+					</template>
+				</el-table-column>
+			</el-table>
+			<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
+		</el-card>
+
+		<!-- 棰勮鐣岄潰 -->
+		<el-dialog :title="dialog.title" v-model="dialog.visible" width="80%" top="5vh" append-to-body class="scrollbar">
+			<el-tabs v-model="preview.activeName">
+				<el-tab-pane
+					v-for="(value, key) in preview.data"
+					:label="(key as any).substring((key as any).lastIndexOf('/') + 1, (key as any).indexOf('.vm'))"
+					:name="(key as any).substring((key as any).lastIndexOf('/') + 1, (key as any).indexOf('.vm'))"
+					:key="value"
+				>
+					<el-link :underline="false" icon="DocumentCopy" v-copyText="value" v-copyText:callback="copyTextSuccess" style="float:right"
+						>&nbsp;澶嶅埗</el-link
+					>
+					<pre>{{ value }}</pre>
+				</el-tab-pane>
+			</el-tabs>
+		</el-dialog>
+		<import-table ref="importRef" @ok="handleQuery" />
+	</div>
+</template>
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..0272f98
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,26 @@
+{
+	"compilerOptions": {
+		"target": "es2022",
+		"useDefineForClassFields": true,
+		"module": "esnext",
+		"moduleResolution": "node",
+		"strict": true,
+		"jsx": "preserve",
+		"sourceMap": true,
+		"resolveJsonModule": true,
+		"esModuleInterop": true,
+		"lib": ["esnext", "dom"],
+		"baseUrl": ".",
+		"allowJs": true,
+		"paths": {
+			"@/*": ["src/*"]
+		},
+		"types": ["vite/client"],
+		"skipLibCheck": true,
+		// 鍏佽榛樿瀵煎叆
+		"allowSyntheticDefaultImports": true,
+		"forceConsistentCasingInFileNames": true
+	},
+	"include": ["src/**/*.ts", "src/**/*.vue", "src/types/**/*.d.ts"],
+	"exclude": ["node_modules", "dist", "**/*.js"]
+}
diff --git a/vite.config.js b/vite.config.js
deleted file mode 100644
index d260bbc..0000000
--- a/vite.config.js
+++ /dev/null
@@ -1,57 +0,0 @@
-import { defineConfig, loadEnv } from 'vite'
-import path from 'path'
-import createVitePlugins from './vite/plugins'
-
-// https://vitejs.dev/config/
-export default defineConfig(({ mode, command }) => {
-  const env = loadEnv(mode, process.cwd())
-  return {
-    // 閮ㄧ讲鐢熶骇鐜鍜屽紑鍙戠幆澧冧笅鐨刄RL銆�
-    // 榛樿鎯呭喌涓嬶紝vite 浼氬亣璁句綘鐨勫簲鐢ㄦ槸琚儴缃插湪涓�涓煙鍚嶇殑鏍硅矾寰勪笂
-    // 渚嬪 https://www.ruoyi.vip/銆傚鏋滃簲鐢ㄨ閮ㄧ讲鍦ㄤ竴涓瓙璺緞涓婏紝浣犲氨闇�瑕佺敤杩欎釜閫夐」鎸囧畾杩欎釜瀛愯矾寰勩�備緥濡傦紝濡傛灉浣犵殑搴旂敤琚儴缃插湪 https://www.ruoyi.vip/admin/锛屽垯璁剧疆 baseUrl 涓� /admin/銆�
-    base: env.VITE_APP_CONTEXT_PATH,
-    plugins: createVitePlugins(env, command === 'build'),
-    resolve: {
-      // https://cn.vitejs.dev/config/#resolve-alias
-      alias: {
-        // 璁剧疆璺緞
-        '~': path.resolve(__dirname, './'),
-        // 璁剧疆鍒悕
-        '@': path.resolve(__dirname, './src')
-      },
-      // https://cn.vitejs.dev/config/#resolve-extensions
-      extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue']
-    },
-    // vite 鐩稿叧閰嶇疆
-    server: {
-      port: 80,
-      host: true,
-      open: true,
-      proxy: {
-        // https://cn.vitejs.dev/config/#server-proxy
-        '/dev-api': {
-          target: 'http://localhost:8080',
-          changeOrigin: true,
-          rewrite: (p) => p.replace(/^\/dev-api/, '')
-        }
-      }
-    },
-    //fix:error:stdin>:7356:1: warning: "@charset" must be the first rule in the file
-    css: {
-      postcss: {
-        plugins: [
-          {
-            postcssPlugin: 'internal:charset-removal',
-            AtRule: {
-              charset: (atRule) => {
-                if (atRule.name === 'charset') {
-                  atRule.remove();
-                }
-              }
-            }
-          }
-        ]
-      }
-    }
-  }
-})
diff --git a/vite.config.ts b/vite.config.ts
new file mode 100644
index 0000000..2c8ced0
--- /dev/null
+++ b/vite.config.ts
@@ -0,0 +1,112 @@
+import { UserConfig, ConfigEnv, loadEnv, defineConfig } from 'vite';
+
+import createPlugins from './Vite/plugins';
+
+import path from 'path';
+export default defineConfig(({ mode, command }: ConfigEnv): UserConfig => {
+	const env = loadEnv(mode, process.cwd());
+	return {
+		// 閮ㄧ讲鐢熶骇鐜鍜屽紑鍙戠幆澧冧笅鐨刄RL銆�
+		// 榛樿鎯呭喌涓嬶紝vite 浼氬亣璁句綘鐨勫簲鐢ㄦ槸琚儴缃插湪涓�涓煙鍚嶇殑鏍硅矾寰勪笂
+		// 渚嬪 https://www.ruoyi.vip/銆傚鏋滃簲鐢ㄨ閮ㄧ讲鍦ㄤ竴涓瓙璺緞涓婏紝浣犲氨闇�瑕佺敤杩欎釜閫夐」鎸囧畾杩欎釜瀛愯矾寰勩�備緥濡傦紝濡傛灉浣犵殑搴旂敤琚儴缃插湪 https://www.ruoyi.vip/admin/锛屽垯璁剧疆 baseUrl 涓� /admin/銆�
+		base: env.VITE_APP_CONTEXT_PATH,
+		resolve: {
+			alias: {
+				'~': path.resolve(__dirname, './'),
+				'@': path.resolve(__dirname, './src'),
+				'vue-i18n': 'vue-i18n/dist/vue-i18n.cjs.js'
+			},
+			extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue']
+		},
+		// https://cn.vitejs.dev/config/#resolve-extensions
+		plugins: createPlugins(env, command === 'build'),
+		server: {
+			host: '0.0.0.0',
+			port: Number(env.VITE_APP_PORT),
+			open: true,
+			proxy: {
+				[env.VITE_APP_BASE_API]: {
+					target: 'http://localhost:8080',
+					changeOrigin: true,
+					rewrite: (path) => path.replace(new RegExp('^' + env.VITE_APP_BASE_API), '')
+				}
+			}
+		},
+		css: {
+			preprocessorOptions: {
+				scss: {
+					javascriptEnabled: true
+				}
+			},
+			postcss: {
+				plugins: [
+					{
+						postcssPlugin: 'internal:charset-removal',
+						AtRule: {
+							charset: (atRule) => {
+								if (atRule.name === 'charset') {
+									atRule.remove();
+								}
+							}
+						}
+					}
+				]
+			}
+		},
+		// 棰勭紪璇�
+		optimizeDeps: {
+			include: [
+				'vue',
+				'vue-router',
+				'pinia',
+				'axios',
+				'@vueuse/core',
+				'path-to-regexp',
+				'echarts',
+				'@wangeditor/editor',
+				'@wangeditor/editor-for-vue',
+				'vue-i18n',
+
+				'@iconify/iconify',
+
+				'element-plus/es/components/form/style/css',
+				'element-plus/es/components/form-item/style/css',
+				'element-plus/es/components/button/style/css',
+				'element-plus/es/components/input/style/css',
+				'element-plus/es/components/input-number/style/css',
+				'element-plus/es/components/switch/style/css',
+				'element-plus/es/components/upload/style/css',
+				'element-plus/es/components/menu/style/css',
+				'element-plus/es/components/col/style/css',
+				'element-plus/es/components/icon/style/css',
+				'element-plus/es/components/row/style/css',
+				'element-plus/es/components/tag/style/css',
+				'element-plus/es/components/dialog/style/css',
+				'element-plus/es/components/loading/style/css',
+				'element-plus/es/components/radio/style/css',
+				'element-plus/es/components/radio-group/style/css',
+				'element-plus/es/components/popover/style/css',
+				'element-plus/es/components/scrollbar/style/css',
+				'element-plus/es/components/tooltip/style/css',
+				'element-plus/es/components/dropdown/style/css',
+				'element-plus/es/components/dropdown-menu/style/css',
+				'element-plus/es/components/dropdown-item/style/css',
+				'element-plus/es/components/sub-menu/style/css',
+				'element-plus/es/components/menu-item/style/css',
+				'element-plus/es/components/divider/style/css',
+				'element-plus/es/components/card/style/css',
+				'element-plus/es/components/link/style/css',
+				'element-plus/es/components/breadcrumb/style/css',
+				'element-plus/es/components/breadcrumb-item/style/css',
+				'element-plus/es/components/table/style/css',
+				'element-plus/es/components/tree-select/style/css',
+				'element-plus/es/components/table-column/style/css',
+				'element-plus/es/components/select/style/css',
+				'element-plus/es/components/option/style/css',
+				'element-plus/es/components/pagination/style/css',
+				'element-plus/es/components/tree/style/css',
+				'element-plus/es/components/alert/style/css'
+			]
+		}
+	};
+});
diff --git a/vite/plugins/auto-import.js b/vite/plugins/auto-import.js
deleted file mode 100644
index a5d3576..0000000
--- a/vite/plugins/auto-import.js
+++ /dev/null
@@ -1,12 +0,0 @@
-import autoImport from 'unplugin-auto-import/vite'
-
-export default function createAutoImport() {
-    return autoImport({
-        imports: [
-            'vue',
-            'vue-router',
-            'pinia'
-        ],
-        dts: false
-    })
-}
diff --git a/vite/plugins/compression.js b/vite/plugins/compression.js
deleted file mode 100644
index e90aaec..0000000
--- a/vite/plugins/compression.js
+++ /dev/null
@@ -1,28 +0,0 @@
-import compression from 'vite-plugin-compression'
-
-export default function createCompression(env) {
-    const { VITE_BUILD_COMPRESS } = env
-    const plugin = []
-    if (VITE_BUILD_COMPRESS) {
-        const compressList = VITE_BUILD_COMPRESS.split(',')
-        if (compressList.includes('gzip')) {
-            // http://doc.ruoyi.vip/ruoyi-vue/other/faq.html#浣跨敤gzip瑙e帇缂╅潤鎬佹枃浠�
-            plugin.push(
-                compression({
-                    ext: '.gz',
-                    deleteOriginFile: false
-                })
-            )
-        }
-        if (compressList.includes('brotli')) {
-            plugin.push(
-                compression({
-                    ext: '.br',
-                    algorithm: 'brotliCompress',
-                    deleteOriginFile: false
-                })
-            )
-        }
-    }
-    return plugin
-}
diff --git a/vite/plugins/index.js b/vite/plugins/index.js
deleted file mode 100644
index 10e17c3..0000000
--- a/vite/plugins/index.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import vue from '@vitejs/plugin-vue'
-
-import createAutoImport from './auto-import'
-import createSvgIcon from './svg-icon'
-import createCompression from './compression'
-import createSetupExtend from './setup-extend'
-
-export default function createVitePlugins(viteEnv, isBuild = false) {
-    const vitePlugins = [vue()]
-    vitePlugins.push(createAutoImport())
-	vitePlugins.push(createSetupExtend())
-    vitePlugins.push(createSvgIcon(isBuild))
-	isBuild && vitePlugins.push(...createCompression(viteEnv))
-    return vitePlugins
-}
diff --git a/vite/plugins/setup-extend.js b/vite/plugins/setup-extend.js
deleted file mode 100644
index a4980f3..0000000
--- a/vite/plugins/setup-extend.js
+++ /dev/null
@@ -1,5 +0,0 @@
-import setupExtend from 'vite-plugin-vue-setup-extend'
-
-export default function createSetupExtend() {
-    return setupExtend()
-}
diff --git a/vite/plugins/svg-icon.js b/vite/plugins/svg-icon.js
deleted file mode 100644
index 30a4140..0000000
--- a/vite/plugins/svg-icon.js
+++ /dev/null
@@ -1,10 +0,0 @@
-import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
-import path from 'path'
-
-export default function createSvgIcon(isBuild) {
-    return createSvgIconsPlugin({
-		iconDirs: [path.resolve(process.cwd(), 'src/assets/icons/svg')],
-        symbolId: 'icon-[dir]-[name]',
-        svgoOptions: isBuild
-    })
-}

--
Gitblit v1.9.3