commit e96599b4113a2d2ae657516cd4431df28cb8b91b Author: 李小林 <320730042@qq.com> Date: Mon Apr 29 17:11:45 2024 +0800 first commit diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..00ee2de --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +# http://editorconfig.org +root = true + +# 表示所有文件适用 +[*] +charset = utf-8 # 设置文件字符集为 utf-8 +end_of_line = lf # 控制换行类型(lf | cr | crlf) +indent_style = space # 缩进风格(tab | space) +indent_size = 2 # 缩进大小 +insert_final_newline = true # 始终在文件末尾插入一个新行 + +# 表示仅 md 文件适用以下规则 +[*.md] +max_line_length = off # 关闭最大行长度限制 +trim_trailing_whitespace = false # 关闭末尾空格修剪 diff --git a/.env.development b/.env.development new file mode 100644 index 0000000..4bf2cce --- /dev/null +++ b/.env.development @@ -0,0 +1,16 @@ +## 开发环境 +NODE_ENV='development' + +# 应用端口 +VITE_APP_PORT = 3000 + +# 代理前缀 +VITE_APP_BASE_API = '/dev-api' + +# 线上接口地址 +#VITE_APP_API_URL = http://vapi.youlai.tech +# 开发接口地址 +VITE_APP_API_URL = http://localhost:8989 + +# 是否启用 Mock 服务 +VITE_MOCK_DEV_SERVER = false diff --git a/.env.production b/.env.production new file mode 100644 index 0000000..a2d828c --- /dev/null +++ b/.env.production @@ -0,0 +1,6 @@ +## 生产环境 +NODE_ENV='production' + +# 代理前缀 +VITE_APP_BASE_API = '/prod-api' + diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..43af40f --- /dev/null +++ b/.eslintignore @@ -0,0 +1,14 @@ +dist +node_modules +public +.husky +.vscode +.idea +*.sh +*.md + +src/assets + +.eslintrc.cjs +.prettierrc.cjs +.stylelintrc.cjs diff --git a/.eslintrc-auto-import.json b/.eslintrc-auto-import.json new file mode 100644 index 0000000..f4738bf --- /dev/null +++ b/.eslintrc-auto-import.json @@ -0,0 +1,284 @@ +{ + "globals": { + "Component": true, + "ComponentPublicInstance": true, + "ComputedRef": true, + "EffectScope": true, + "ElMessage": true, + "ElMessageBox": true, + "ElNotification": true, + "InjectionKey": true, + "PropType": true, + "Ref": true, + "VNode": 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, + "createReusableTemplate": true, + "createSharedComposable": true, + "createTemplatePromise": 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, + "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, + "toValue": true, + "triggerRef": true, + "tryOnBeforeMount": true, + "tryOnBeforeUnmount": true, + "tryOnMounted": true, + "tryOnScopeDispose": true, + "tryOnUnmounted": true, + "unref": true, + "unrefElement": true, + "until": true, + "useActiveElement": true, + "useAnimate": true, + "useArrayDifference": true, + "useArrayEvery": true, + "useArrayFilter": true, + "useArrayFind": true, + "useArrayFindIndex": true, + "useArrayFindLast": true, + "useArrayIncludes": 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, + "useParentElement": true, + "usePerformanceObserver": 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, + "watchDeep": true, + "watchEffect": true, + "watchIgnorable": true, + "watchImmediate": true, + "watchOnce": true, + "watchPausable": true, + "watchPostEffect": true, + "watchSyncEffect": true, + "watchThrottled": true, + "watchTriggerable": true, + "watchWithFilter": true, + "whenever": true + } +} diff --git a/.eslintrc.cjs b/.eslintrc.cjs new file mode 100644 index 0000000..8ae169a --- /dev/null +++ b/.eslintrc.cjs @@ -0,0 +1,88 @@ +module.exports = { + root: true, + env: { + browser: true, + es2021: true, + node: true, + }, + parser: "vue-eslint-parser", + extends: [ + // https://eslint.vuejs.org/user-guide/#usage + "plugin:vue/vue3-recommended", + "./.eslintrc-auto-import.json", + "prettier", + "plugin:@typescript-eslint/recommended", + "plugin:prettier/recommended", + ], + parserOptions: { + ecmaVersion: "latest", + sourceType: "module", + parser: "@typescript-eslint/parser", + project: "./tsconfig.*?.json", + createDefaultProgram: false, + extraFileExtensions: [".vue"], + }, + plugins: ["vue", "@typescript-eslint"], + rules: { + // https://eslint.vuejs.org/rules/#priority-a-essential-error-prevention + "vue/multi-word-component-names": "off", + "vue/no-v-model-argument": "off", + "vue/script-setup-uses-vars": "error", + "vue/no-reserved-component-names": "off", + "vue/custom-event-name-casing": "off", + "vue/attributes-order": "off", + "vue/one-component-per-file": "off", + "vue/html-closing-bracket-newline": "off", + "vue/max-attributes-per-line": "off", + "vue/multiline-html-element-content-newline": "off", + "vue/singleline-html-element-content-newline": "off", + "vue/attribute-hyphenation": "off", + "vue/require-default-prop": "off", + "vue/require-explicit-emits": "off", + "vue/html-self-closing": [ + "error", + { + html: { + void: "always", + normal: "never", + component: "always", + }, + svg: "always", + math: "always", + }, + ], + + "@typescript-eslint/no-empty-function": "off", // 关闭空方法检查 + "@typescript-eslint/no-explicit-any": "off", // 关闭any类型的警告 + "@typescript-eslint/no-non-null-assertion": "off", + "@typescript-eslint/ban-ts-ignore": "off", + "@typescript-eslint/ban-ts-comment": "off", + "@typescript-eslint/ban-types": "off", + "@typescript-eslint/explicit-function-return-type": "off", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-var-requires": "off", + "@typescript-eslint/no-empty-function": "off", + "@typescript-eslint/no-use-before-define": "off", + "@typescript-eslint/no-non-null-assertion": "off", + "@typescript-eslint/explicit-module-boundary-types": "off", + "@typescript-eslint/no-unused-vars": "off", + + "prettier/prettier": [ + "error", + { + useTabs: false, // 不使用制表符 + }, + ], + }, + // eslint不能对html文件生效 + overrides: [ + { + files: ["*.html"], + processor: "vue/.vue", + }, + ], + // https://eslint.org/docs/latest/use/configure/language-options#specifying-globals + globals: { + OptionType: "readonly", + }, +}; diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fee6999 --- /dev/null +++ b/.gitignore @@ -0,0 +1,18 @@ +node_modules +.DS_Store +dist +dist-ssr +*.local +.history + +# Editor directories and files +.idea +*.suo +*.ntvs* +*.njsproj +*.sln +*.local + +package-lock.json +pnpm-lock.yaml +stats.html diff --git a/.husky/commit-msg b/.husky/commit-msg new file mode 100644 index 0000000..e8511ea --- /dev/null +++ b/.husky/commit-msg @@ -0,0 +1,4 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +npx --no-install commitlint --edit $1 diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 0000000..37568d1 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,4 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +npm run lint:lint-staged diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..f3e9850 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,11 @@ +dist +node_modules +public +.husky +.vscode +.idea +*.sh +*.md + +src/assets +stats.html diff --git a/.prettierrc.cjs b/.prettierrc.cjs new file mode 100644 index 0000000..347fb32 --- /dev/null +++ b/.prettierrc.cjs @@ -0,0 +1,46 @@ +module.exports = { + // (x)=>{},单个参数箭头函数是否显示小括号。(always:始终显示;avoid:省略括号。默认:always) + arrowParens: "always", + // 开始标签的右尖括号是否跟随在最后一行属性末尾,默认false + bracketSameLine: false, + // 对象字面量的括号之间打印空格 (true - Example: { foo: bar } ; false - Example: {foo:bar}) + bracketSpacing: true, + // 是否格式化一些文件中被嵌入的代码片段的风格(auto|off;默认auto) + embeddedLanguageFormatting: "auto", + // 指定 HTML 文件的空格敏感度 (css|strict|ignore;默认css) + htmlWhitespaceSensitivity: "css", + // 当文件已经被 Prettier 格式化之后,是否会在文件顶部插入一个特殊的 @format 标记,默认false + insertPragma: false, + // 在 JSX 中使用单引号替代双引号,默认false + jsxSingleQuote: false, + // 每行最多字符数量,超出换行(默认80) + printWidth: 80, + // 超出打印宽度 (always | never | preserve ) + proseWrap: "preserve", + // 对象属性是否使用引号(as-needed | consistent | preserve;默认as-needed:对象的属性需要加引号才添加;) + quoteProps: "as-needed", + // 是否只格式化在文件顶部包含特定注释(@prettier| @format)的文件,默认false + requirePragma: false, + // 结尾添加分号 + semi: true, + // 使用单引号 (true:单引号;false:双引号) + singleQuote: false, + // 缩进空格数,默认2个空格 + tabWidth: 2, + // 元素末尾是否加逗号,默认es5: ES5中的 objects, arrays 等会添加逗号,TypeScript 中的 type 后不加逗号 + trailingComma: "es5", + // 指定缩进方式,空格或tab,默认false,即使用空格 + useTabs: false, + // vue 文件中是否缩进 + diff --git a/package.json b/package.json new file mode 100644 index 0000000..2ace145 --- /dev/null +++ b/package.json @@ -0,0 +1,120 @@ +{ + "name": "ITMS终端综合管理系统", + "version": "2.9.2", + "private": true, + "type": "module", + "scripts": { + "preinstall": "npx only-allow pnpm", + "dev": "vite serve --mode development", + "build:prod": "vite build --mode production && vue-tsc --noEmit", + "prepare": "husky install", + "lint:eslint": "eslint --fix --ext .ts,.js,.vue ./src ", + "lint:prettier": "prettier --write \"**/*.{js,cjs,ts,json,tsx,css,less,scss,vue,html,md}\"", + "lint:stylelint": "stylelint \"**/*.{css,scss,vue}\" --fix", + "lint:lint-staged": "lint-staged", + "commit": "git-cz" + }, + "config": { + "commitizen": { + "path": "node_modules/cz-git" + } + }, + "lint-staged": { + "*.{js,ts}": [ + "eslint --fix", + "prettier --write" + ], + "*.{cjs,json}": [ + "prettier --write" + ], + "*.{vue,html}": [ + "eslint --fix", + "prettier --write", + "stylelint --fix" + ], + "*.{scss,css}": [ + "stylelint --fix", + "prettier --write" + ], + "*.md": [ + "prettier --write" + ] + }, + "dependencies": { + "@element-plus/icons-vue": "^2.3.1", + "@vueuse/core": "^10.9.0", + "@wangeditor/editor": "^5.1.23", + "@wangeditor/editor-for-vue": "5.1.10", + "animate.css": "^4.1.1", + "axios": "^1.6.7", + "echarts": "^5.5.0", + "element-plus": "^2.6.0", + "lodash-es": "^4.17.21", + "net": "^1.0.2", + "nprogress": "^0.2.0", + "path-browserify": "^1.0.1", + "path-to-regexp": "^6.2.1", + "pinia": "^2.1.7", + "sockjs-client": "1.6.1", + "sortablejs": "^1.15.2", + "stompjs": "^2.3.3", + "vue": "^3.4.21", + "vue-i18n": "9.9.1", + "vue-router": "^4.3.0", + "xlsx": "^0.18.5" + }, + "devDependencies": { + "@commitlint/cli": "^18.6.1", + "@commitlint/config-conventional": "^18.6.2", + "@iconify-json/ep": "^1.1.14", + "@types/lodash": "^4.14.202", + "@types/node": "^20.11.24", + "@types/nprogress": "^0.2.3", + "@types/path-browserify": "^1.0.2", + "@types/sockjs-client": "^1.5.4", + "@types/sortablejs": "^1.15.8", + "@types/stompjs": "^2.3.9", + "@typescript-eslint/eslint-plugin": "^7.1.1", + "@typescript-eslint/parser": "^7.1.1", + "@vitejs/plugin-vue": "^5.0.4", + "@vitejs/plugin-vue-jsx": "^3.1.0", + "autoprefixer": "^10.4.18", + "commitizen": "^4.3.0", + "cz-git": "^1.9.0", + "eslint": "^8.57.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-import": "^2.29.1", + "eslint-plugin-prettier": "^5.1.3", + "eslint-plugin-vue": "^9.22.0", + "fast-glob": "^3.3.2", + "husky": "^9.0.11", + "lint-staged": "^15.2.2", + "postcss": "^8.4.35", + "postcss-html": "^1.6.0", + "postcss-scss": "^4.0.9", + "prettier": "^3.2.5", + "sass": "^1.71.1", + "stylelint": "^16.2.1", + "stylelint-config-html": "^1.1.0", + "stylelint-config-recess-order": "^4.6.0", + "stylelint-config-recommended-scss": "^14.0.0", + "stylelint-config-recommended-vue": "^1.5.0", + "stylelint-config-standard": "^36.0.0", + "terser": "^5.28.1", + "typescript": "^5.3.3", + "unocss": "^0.58.5", + "unplugin-auto-import": "^0.17.5", + "unplugin-icons": "^0.18.5", + "unplugin-vue-components": "^0.26.0", + "vite": "^5.1.5", + "vite-plugin-mock-dev-server": "^1.4.7", + "vite-plugin-svg-icons": "^2.0.1", + "vue-tsc": "^2.0.4" + }, + "repository": "https://bellmann.com", + "author": "bellmann", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } +} diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..8071065 Binary files /dev/null and b/public/favicon.ico differ diff --git a/src/App.vue b/src/App.vue new file mode 100644 index 0000000..066b137 --- /dev/null +++ b/src/App.vue @@ -0,0 +1,39 @@ + + + + + diff --git a/src/api/article.ts b/src/api/article.ts new file mode 100644 index 0000000..2547a4b --- /dev/null +++ b/src/api/article.ts @@ -0,0 +1,88 @@ +import request from "@/utils/request"; + +export interface ArticleQuery { + page?: number; + limit?: number; + sort?: string; + title?: string; + type?: string; + importance?: number; +} + +export interface ArticleDetail { + id: number; + timestamp: number; + title: string; + type: string; + status: string; + importance: number; + content?: string; + remark?: string; +} + +export interface ArticleCreate { + type: string; + timestamp: Date; + title: string; + status?: string; + importance?: number; + remark?: string; +} + +export interface ArticleUpdate { + id: number; + type?: string; + timestamp?: Date; + title?: string; + status?: string; + importance?: number; + remark?: string; +} + +export function fetchList(query: ArticleQuery) { + return request({ + url: "/api/v1/article/list", + method: "get", + params: query, + }); +} + +export function fetchArticle(id: number) { + return request({ + url: "/api/v1/article/detail", + method: "get", + params: { id }, + }); +} + +export function fetchPv(id: number) { + return request({ + url: "/api/v1/article/pv", + method: "get", + params: { id }, + }); +} + +export function createArticle(data: ArticleCreate) { + return request({ + url: "/api/v1/article/create", + method: "post", + data, + }); +} + +export function updateArticle(data: ArticleUpdate) { + return request({ + url: "/api/v1/article/update", + method: "post", + data, + }); +} + +export function deleteArticle(id: number) { + return request({ + url: "/api/v1/article/delete", + method: "post", + data: { id }, + }); +} diff --git a/src/api/auth/index.ts b/src/api/auth/index.ts new file mode 100644 index 0000000..645628e --- /dev/null +++ b/src/api/auth/index.ts @@ -0,0 +1,45 @@ +import request from "@/utils/request"; +import { AxiosPromise } from "axios"; +import { CaptchaResult, LoginData, LoginResult } from "./types"; + +/** + * 登录API + * + * @param data {LoginData} + * @returns + */ +export function loginApi(data: LoginData): AxiosPromise { + const formData = new FormData(); + formData.append("username", data.username); + formData.append("password", data.password); + formData.append("captchaKey", data.captchaKey || ""); + formData.append("captchaCode", data.captchaCode || ""); + return request({ + url: "/api/v1/auth/login", + method: "post", + data: formData, + headers: { + "Content-Type": "multipart/form-data", + }, + }); +} + +/** + * 注销API + */ +export function logoutApi() { + return request({ + url: "/api/v1/auth/logout", + method: "delete", + }); +} + +/** + * 获取验证码 + */ +export function getCaptchaApi(): AxiosPromise { + return request({ + url: "/api/v1/auth/captcha", + method: "get", + }); +} diff --git a/src/api/auth/types.ts b/src/api/auth/types.ts new file mode 100644 index 0000000..0da661e --- /dev/null +++ b/src/api/auth/types.ts @@ -0,0 +1,59 @@ +/** + * 登录请求参数 + */ +export interface LoginData { + /** + * 用户名 + */ + username: string; + /** + * 密码 + */ + password: string; + + /** + * 验证码缓存key + */ + captchaKey?: string; + + /** + * 验证码 + */ + captchaCode?: string; +} + +/** + * 登录响应 + */ +export interface LoginResult { + /** + * 访问token + */ + accessToken?: string; + /** + * 过期时间(单位:毫秒) + */ + expires?: number; + /** + * 刷新token + */ + refreshToken?: string; + /** + * token 类型 + */ + tokenType?: string; +} + +/** + * 验证码响应 + */ +export interface CaptchaResult { + /** + * 验证码缓存key + */ + captchaKey: string; + /** + * 验证码图片Base64字符串 + */ + captchaBase64: string; +} diff --git a/src/api/dept/index.ts b/src/api/dept/index.ts new file mode 100644 index 0000000..f5e0466 --- /dev/null +++ b/src/api/dept/index.ts @@ -0,0 +1,77 @@ +import request from "@/utils/request"; +import { AxiosPromise } from "axios"; +import { DeptForm, DeptQuery, DeptVO } from "./types"; + +/** + * 部门树形表格 + * + * @param queryParams + */ +export function listDepts(queryParams?: DeptQuery): AxiosPromise { + return request({ + url: "/api/v1/dept", + method: "get", + params: queryParams, + }); +} + +/** + * 部门下拉列表 + */ +export function getDeptOptions(): AxiosPromise { + return request({ + url: "/api/v1/dept/options", + method: "get", + }); +} + +/** + * 获取部门详情 + * + * @param id + */ +export function getDeptForm(id: number): AxiosPromise { + return request({ + url: "/api/v1/dept/" + id + "/form", + method: "get", + }); +} + +/** + * 新增部门 + * + * @param data + */ +export function addDept(data: DeptForm) { + return request({ + url: "/api/v1/dept", + method: "post", + data: data, + }); +} + +/** + * 修改部门 + * + * @param id + * @param data + */ +export function updateDept(id: number, data: DeptForm) { + return request({ + url: "/api/v1/dept/" + id, + method: "put", + data: data, + }); +} + +/** + * 删除部门 + * + * @param ids + */ +export function deleteDept(ids: string) { + return request({ + url: "/api/v1/dept/" + ids, + method: "delete", + }); +} diff --git a/src/api/dept/types.ts b/src/api/dept/types.ts new file mode 100644 index 0000000..408c39c --- /dev/null +++ b/src/api/dept/types.ts @@ -0,0 +1,71 @@ +/** + * 部门查询参数 + */ +export interface DeptQuery { + keywords?: string; + status?: number; +} + +/** + * 部门类型 + */ +export interface DeptVO { + /** + * 子部门 + */ + children?: DeptVO[]; + /** + * 创建时间 + */ + createTime?: Date; + /** + * 部门ID + */ + id?: number; + /** + * 部门名称 + */ + name?: string; + /** + * 父部门ID + */ + parentId?: number; + /** + * 排序 + */ + sort?: number; + /** + * 状态(1:启用;0:禁用) + */ + status?: number; + /** + * 修改时间 + */ + updateTime?: Date; +} + +/** + * 部门表单类型 + */ +export interface DeptForm { + /** + * 部门ID(新增不填) + */ + id?: number; + /** + * 部门名称 + */ + name?: string; + /** + * 父部门ID + */ + parentId: number; + /** + * 排序 + */ + sort?: number; + /** + * 状态(1:启用;0:禁用) + */ + status?: number; +} diff --git a/src/api/device-type-ver/index.ts b/src/api/device-type-ver/index.ts new file mode 100644 index 0000000..9dba39d --- /dev/null +++ b/src/api/device-type-ver/index.ts @@ -0,0 +1,69 @@ +import { + DeviceTypeToVerPageResult, + DeviceTypeToVerQuery, + DeviceTypeVerForm, +} from "@/api/device-type-ver/types"; +import request from "@/utils/request"; +import { AxiosPromise } from "axios"; + +export function deviceTypeToVerPage( + data: DeviceTypeToVerQuery +): AxiosPromise { + return request({ + url: "/api/device-type-ver/v1/device-type/page", + method: "POST", + data, + }); +} + +export function updateTypeAndVersionState(typeAndVerId: number) { + return request({ + url: `/api/device-type-ver/v1/audit-soft-state/${typeAndVerId}`, + method: "PUT", + }); +} + +export function updateTypeNewSoftState(typeAndVerId: number) { + return request({ + url: `/api/device-type-ver/v1/audit-new-soft-state/${typeAndVerId}`, + method: "PUT", + }); +} + +export function copySoftVerToOtherOui(devTypeId: any, typeAndVerId: number) { + return request({ + url: `/api/device-type-ver/v1/audit-new-soft-state/${typeAndVerId}/${devTypeId}`, + method: "PUT", + }); +} +export function deleteDeviceTypeVersionById(typeAndVerId: number) { + return request({ + url: `/api/device-type-ver/v1/delete-device-type/${typeAndVerId}`, + method: "DELETE", + }); +} +export function deleteDeviceTypeVersionFile( + typeAndVerId: number, + fileId: number +) { + return request({ + url: `/api/device-type-ver/v1/delete-device-ver-file/${typeAndVerId}/${fileId}`, + method: "DELETE", + }); +} +export function findDevTypeVerEditForm( + typeAndVerId: number +): AxiosPromise { + return request({ + url: `/api/device-type-ver/v1/find-dev-type-ver-edit-form/${typeAndVerId}`, + method: "GET", + }); +} + +export function editDevTypeVerForm(data: DeviceTypeVerForm) { + return request({ + url: "/api/device-type-ver/v1/edit-dev-type-ver-form", + method: "POST", + data, + }); +} diff --git a/src/api/device-type-ver/types.ts b/src/api/device-type-ver/types.ts new file mode 100644 index 0000000..00ab325 --- /dev/null +++ b/src/api/device-type-ver/types.ts @@ -0,0 +1,33 @@ +export interface DeviceTypeToVerQuery extends PageQuery { + devTypeId?: number; +} +export interface DeviceTypeToVerVO { + typeAndVerId?: number; + softVer?: string; + fileName?: string; + fileId?: string; + fileCreateTime?: string; + auditTime?: string; + auditUserName?: string; + devTypeVerStatus?: string; + newSoftStatus?: string; +} +export type DeviceTypeToVerPageResult = PageResult; + +export interface DeviceTypeVerForm { + typeAndVerId?: number; + softVer?: string; + auditTime?: string; + auditUserName?: string; + connectReqUsername?: string; + connectReqPassword?: string; + acsUrlPassword?: string; + devTypeVerDesc?: string; + provisionFlag?: string; + devAccessType?: string; + devTypeName?: string; + devTypeNameNew?: string; + oldCASeparation?: string; + specVer?: string; +} + diff --git a/src/api/device-type/index.ts b/src/api/device-type/index.ts new file mode 100644 index 0000000..ef26932 --- /dev/null +++ b/src/api/device-type/index.ts @@ -0,0 +1,24 @@ +import { + DeviceTypePageResult, + DeviceTypeQuery, + DeviceTypeVO, +} from "@/api/device-type/types"; +import request from "@/utils/request"; +import { AxiosPromise } from "axios"; + +export function getDeviceTypePage( + data: DeviceTypeQuery +): AxiosPromise { + return request({ + url: "/api/dev-type/v1/page", + method: "POST", + data, + }); +} + +export function findDevTypeById(devTypeId: number): AxiosPromise { + return request({ + url: `/api/dev-type/v1/${devTypeId}`, + method: "GET", + }); +} diff --git a/src/api/device-type/types.ts b/src/api/device-type/types.ts new file mode 100644 index 0000000..cc49bb4 --- /dev/null +++ b/src/api/device-type/types.ts @@ -0,0 +1,31 @@ +import { DictTypePageVO } from "@/api/dict/types"; + +export interface DeviceTypeQuery extends PageQuery { + devVendorName?: string; + devVendorOui?: string; + devTypeName?: string; + devHardVer?: string; +} + +export interface DeviceTypePageVO { + devTypeId?: number; + devVendorName?: string; + devVendorOui?: string; + devTypeName?: string; + devHardVer?: string; + typeAndVerId?: number; + devTypeVerStatus?: string; + tr069VerName?: string; + devTypeNameNew?: string; + softVer?: string; +} +export type DeviceTypePageResult = PageResult; + +export interface DeviceTypeVO { + devTypeId?: number; + devVendorName?: string; + devVendorOui?: string; + devTypeName?: string; + devHardVer?: string; + devTypeDesc?: string; +} diff --git a/src/api/dict/index.ts b/src/api/dict/index.ts new file mode 100644 index 0000000..635297e --- /dev/null +++ b/src/api/dict/index.ts @@ -0,0 +1,150 @@ +import request from "@/utils/request"; +import { AxiosPromise } from "axios"; +import { + DictTypeQuery, + DictTypePageResult, + DictTypeForm, + DictQuery, + DictForm, + DictPageResult, +} from "./types"; + +/** + * 字典类型分页列表 + * + * @param queryParams + */ +export function getDictTypePage( + queryParams: DictTypeQuery +): AxiosPromise { + return request({ + url: "/api/v1/dict/types/page", + method: "get", + params: queryParams, + }); +} + +/** + * 字典类型表单数据 + * + * @param id + */ +export function getDictTypeForm(id: number): AxiosPromise { + return request({ + url: "/api/v1/dict/types/" + id + "/form", + method: "get", + }); +} + +/** + * 新增字典类型 + * + * @param data + */ +export function addDictType(data: DictTypeForm) { + return request({ + url: "/api/v1/dict/types", + method: "post", + data: data, + }); +} + +/** + * 修改字典类型 + * + * @param id + * @param data + */ +export function updateDictType(id: number, data: DictTypeForm) { + return request({ + url: "/api/v1/dict/types/" + id, + method: "put", + data: data, + }); +} + +/** + * 删除字典类型 + */ +export function deleteDictTypes(ids: string) { + return request({ + url: "/api/v1/dict/types/" + ids, + method: "delete", + }); +} + +/** + * 获取字典类型的数据项 + * + * @param typeCode 字典类型编码 + */ +export function getDictOptions(typeCode: string): AxiosPromise { + return request({ + url: "/api/v1/dict/" + typeCode + "/options", + method: "get", + }); +} + +/** + * 字典分页列表 + */ +export function getDictPage( + queryParams: DictQuery +): AxiosPromise { + return request({ + url: "/api/v1/dict/page", + method: "get", + params: queryParams, + }); +} + +/** + * 获取字典表单数据 + * + * @param id + */ +export function getDictFormData(id: number): AxiosPromise { + return request({ + url: "/api/v1/dict/" + id + "/form", + method: "get", + }); +} + +/** + * 新增字典 + * + * @param data + */ +export function addDict(data: DictForm) { + return request({ + url: "/api/v1/dict", + method: "post", + data: data, + }); +} + +/** + * 修改字典项 + * + * @param id + * @param data + */ +export function updateDict(id: number, data: DictForm) { + return request({ + url: "/api/v1/dict/" + id, + method: "put", + data: data, + }); +} + +/** + * 删除字典 + * + * @param ids 字典项ID,多个以英文逗号(,)分割 + */ +export function deleteDict(ids: string) { + return request({ + url: "/api/v1/dict/" + ids, + method: "delete", + }); +} diff --git a/src/api/dict/types.ts b/src/api/dict/types.ts new file mode 100644 index 0000000..315da6d --- /dev/null +++ b/src/api/dict/types.ts @@ -0,0 +1,142 @@ +/** + * 字典类型查询参数 + */ +export interface DictTypeQuery extends PageQuery { + /** + * 关键字(字典类型名称/编码) + */ + keywords?: string; +} + +/** + * 字典类型分页对象 + */ +export interface DictTypePageVO { + /** + * 字典类型ID + */ + id: number; + /** + * 类型编码 + */ + code: string; + /** + * 类型名称 + */ + name: string; + /** + * 状态(1:启用;0:禁用) + */ + status?: number; + /** + * 备注 + */ + remark?: string; +} + +/** + * 字典分页项类型声明 + */ +export type DictTypePageResult = PageResult; + +/** + * 字典表单类型声明 + */ +export interface DictTypeForm { + /** + * 字典类型ID + */ + id?: number; + /** + * 类型名称 + */ + name?: string; + /** + * 类型编码 + */ + code?: string; + /** + * 类型状态:1:启用;0:禁用 + */ + status: number; + /** + * 备注 + */ + remark?: string; +} + +/** + * 字典查询参数 + */ +export interface DictQuery extends PageQuery { + /** + * 字典项名称 + */ + name?: string; + /** + * 字典类型编码 + */ + typeCode?: string; +} + +/** + * 字典分页对象 + */ +export interface DictPageVO { + /** + * 字典ID + */ + id?: number; + /** + * 字典名称 + */ + name?: string; + /** + * 状态(1:启用;0:禁用) + */ + status?: number; + /** + * 字典值 + */ + value?: string; +} + +/** + * 字典分页 + */ +export type DictPageResult = PageResult; + +/** + * 字典表单 + */ +export interface DictForm { + /** + * 字典ID + */ + id?: number; + /** + * 字典名称 + */ + name?: string; + /** + * 排序 + */ + sort?: number; + /** + * 状态(1:启用;0:禁用) + */ + status?: number; + /** + * 类型编码 + */ + typeCode?: string; + /** + * 值 + */ + value?: string; + + /** + * 备注 + */ + remark?: string; +} diff --git a/src/api/domain/index.ts b/src/api/domain/index.ts new file mode 100644 index 0000000..ab88c2d --- /dev/null +++ b/src/api/domain/index.ts @@ -0,0 +1,49 @@ +import { DomainForm, DomainQuery, DomainVo } from "@/api/domain/types"; +import request from "@/utils/request"; +import { AxiosPromise } from "axios"; + +export function getDomainList( + data: DomainQuery +): AxiosPromise> { + return request({ + url: "/api/domain/v1/list", + method: "POST", + data, + }); +} + +export function getDomainOptions(): AxiosPromise { + return request({ + url: "/api/domain/v1/options", + method: "GET", + }); +} + +export function addDomain(data: DomainForm) { + return request({ + url: "/api/domain/v1/add_domain", + method: "POST", + data, + }); +} + +export function editDomain(data: DomainForm) { + return request({ + url: "/api/domain/v1/edit_domain", + method: "POST", + data, + }); +} +export function deleteDomain(groupId: number) { + return request({ + url: `/api/domain/v1/${groupId}`, + method: "DELETE", + }); +} + +export function getDomainByGroupId(groupId: number): AxiosPromise { + return request({ + url: `/api/domain/v1/${groupId}`, + method: "GET", + }); +} diff --git a/src/api/domain/types.ts b/src/api/domain/types.ts new file mode 100644 index 0000000..17da989 --- /dev/null +++ b/src/api/domain/types.ts @@ -0,0 +1,29 @@ +export interface DomainQuery extends PageQuery { + keywords?: string; +} + +export interface DomainVo { + id?: number; + + groupId?: number; + + groupName?: string; + + parentGroupId?: number; + + description?: string; + + children?: Array; +} + +export interface DomainForm { + id?: number; + + groupId?: number; + + groupName?: string; + + parentGroupId?: number; + + description?: string; +} diff --git a/src/api/file-list/index.ts b/src/api/file-list/index.ts new file mode 100644 index 0000000..bcf0f00 --- /dev/null +++ b/src/api/file-list/index.ts @@ -0,0 +1,13 @@ +import { FileListPageResult, FileListQuery } from "@/api/file-list/types"; +import { AxiosPromise } from "axios"; +import request from "@/utils/request"; + +export function getPageData( + data: FileListQuery +): AxiosPromise { + return request({ + url: "/api/file_list/v1/page", + method: "POST", + data, + }); +} diff --git a/src/api/file-list/types.ts b/src/api/file-list/types.ts new file mode 100644 index 0000000..c05ca59 --- /dev/null +++ b/src/api/file-list/types.ts @@ -0,0 +1,43 @@ +import { FileServerVO } from "@/api/file-server/types"; + +export interface FileListQuery extends PageQuery { + fileName?: string; + fileType?: string; + fileServerId?: number | undefined; +} + +export interface FileListVO { + fileId?: number; + + /** + * 上传者 + */ + userName?: string; + + /** + * 文件类型 + */ + fileType?: string; + + /** + * 文件名称 + */ + fileName?: string; + + /** + * 文件上传时间 + */ + + fileCreateTime?: string; + + /** + * 文件描述 + */ + fileDesc?: string; + + /** + * 文件服务器ID + */ + fileServId?: number; +} +export type FileListPageResult = PageResult; diff --git a/src/api/file-server/index.ts b/src/api/file-server/index.ts new file mode 100644 index 0000000..3cc41f3 --- /dev/null +++ b/src/api/file-server/index.ts @@ -0,0 +1,30 @@ +import request from "@/utils/request"; +import { AxiosPromise } from "axios"; +import { FileServerForm, FileServerPageResult } from "@/api/file-server/types"; + +export function getFileServerPage( + data: PageQuery +): AxiosPromise { + return request({ + url: "/api/file_server/v1/page", + method: "POST", + data, + }); +} + +export function findFileServerFormById( + id: number +): AxiosPromise { + return request({ + url: `/api/file_server/v1/${id}`, + method: "GET", + }); +} + +export function editFileServer(data: FileServerForm) { + return request({ + url: "/api/file_server/v1/edit", + method: "POST", + data, + }); +} diff --git a/src/api/file-server/types.ts b/src/api/file-server/types.ts new file mode 100644 index 0000000..2adc91e --- /dev/null +++ b/src/api/file-server/types.ts @@ -0,0 +1,39 @@ +import { DictTypePageVO } from "@/api/dict/types"; + +export interface FileServerVO { + fileServId?: number; + + fileServName?: string; + + supportMode?: string; + + ftpIp?: string; + + ftpPort?: string; + + ftpUserName?: string; + + ftpRootDir?: string; +} + +export type FileServerPageResult = PageResult; + +export interface FileServerForm { + fileServId?: number; + + fileServName?: string; + + supportMode?: string; + + ftpIp?: string; + + ftpPort?: string; + + ftpUserName?: string; + + ftpRootDir?: string; + + ftpVerifyPassword?: string; + + ftpPassword?: string; +} diff --git a/src/api/file/index.ts b/src/api/file/index.ts new file mode 100644 index 0000000..16fcec7 --- /dev/null +++ b/src/api/file/index.ts @@ -0,0 +1,33 @@ +import request from "@/utils/request"; +import { AxiosPromise } from "axios"; +import { FileInfo } from "./types"; + +/** + * 上传文件 + * + * @param file + */ +export function uploadFileApi(file: File): AxiosPromise { + const formData = new FormData(); + formData.append("file", file); + return request({ + url: "/api/file-option/v1/upload", + method: "POST", + data: formData, + headers: { + "Content-Type": "multipart/form-data", + }, + }); +} + +/** + * 文件下载 + * @param fileId + */ +export function downloadFileApi(fileId: number) { + return request({ + url: `/api/file-option/v1/download/${fileId}`, + method: "GET", + responseType: "blob", + }); +} diff --git a/src/api/file/types.ts b/src/api/file/types.ts new file mode 100644 index 0000000..22b2be5 --- /dev/null +++ b/src/api/file/types.ts @@ -0,0 +1,7 @@ +/** + * 文件API类型声明 + */ +export interface FileInfo { + name: string; + url: string; +} diff --git a/src/api/menu/index.ts b/src/api/menu/index.ts new file mode 100644 index 0000000..032e0a9 --- /dev/null +++ b/src/api/menu/index.ts @@ -0,0 +1,87 @@ +import request from "@/utils/request"; +import { AxiosPromise } from "axios"; +import { MenuQuery, MenuVO, MenuForm } from "./types"; + +/** + * 获取路由列表 + */ +export function listRoutes() { + return request({ + url: "/api/v1/menus/routes", + method: "get", + }); +} + +/** + * 获取菜单树形列表 + * + * @param queryParams + */ +export function listMenus(queryParams: MenuQuery): AxiosPromise { + return request({ + url: "/api/v1/menus", + method: "get", + params: queryParams, + }); +} + +/** + * 获取菜单下拉树形列表 + */ +export function getMenuOptions(): AxiosPromise { + return request({ + url: "/api/v1/menus/options", + method: "get", + }); +} + +/** + * 获取菜单表单数据 + * + * @param id + */ +export function getMenuForm(id: number): AxiosPromise { + return request({ + url: "/api/v1/menus/" + id + "/form", + method: "get", + }); +} + +/** + * 添加菜单 + * + * @param data + */ +export function addMenu(data: MenuForm) { + return request({ + url: "/api/v1/menus", + method: "post", + data: data, + }); +} + +/** + * 修改菜单 + * + * @param id + * @param data + */ +export function updateMenu(id: string, data: MenuForm) { + return request({ + url: "/api/v1/menus/" + id, + method: "put", + data: data, + }); +} + +/** + * 删除菜单 + * + * @param id 菜单ID + */ +export function deleteMenu(id: number) { + return request({ + url: "/api/v1/menus/" + id, + method: "delete", + }); +} diff --git a/src/api/menu/types.ts b/src/api/menu/types.ts new file mode 100644 index 0000000..38101e8 --- /dev/null +++ b/src/api/menu/types.ts @@ -0,0 +1,124 @@ +import { MenuTypeEnum } from "@/enums/MenuTypeEnum"; + +/** + * 菜单查询参数类型 + */ +export interface MenuQuery { + keywords?: string; +} + +/** + * 菜单视图对象类型 + */ +export interface MenuVO { + /** + * 子菜单 + */ + children?: MenuVO[]; + /** + * 组件路径 + */ + component?: string; + /** + * ICON + */ + icon?: string; + /** + * 菜单ID + */ + id?: number; + /** + * 菜单名称 + */ + name?: string; + /** + * 父菜单ID + */ + parentId?: number; + /** + * 按钮权限标识 + */ + perm?: string; + /** + * 跳转路径 + */ + redirect?: string; + /** + * 路由名称 + */ + routeName?: string; + /** + * 路由相对路径 + */ + routePath?: string; + /** + * 菜单排序(数字越小排名越靠前) + */ + sort?: number; + /** + * 菜单类型 + */ + type?: MenuTypeEnum; + /** + * 菜单是否可见(1:显示;0:隐藏) + */ + visible?: number; +} + +/** + * 菜单表单对象类型 + */ +export interface MenuForm { + /** + * 菜单ID + */ + id?: string; + /** + * 父菜单ID + */ + parentId?: number; + /** + * 菜单名称 + */ + name?: string; + /** + * 菜单是否可见(1:是;0:否;) + */ + visible: number; + icon?: string; + /** + * 排序 + */ + sort: number; + /** + * 组件路径 + */ + component?: string; + /** + * 路由路径 + */ + path?: string; + /** + * 跳转路由路径 + */ + redirect?: string; + + /** + * 菜单类型 + */ + type: MenuTypeEnum; + + /** + * 权限标识 + */ + perm?: string; + /** + * 【菜单】是否开启页面缓存 + */ + keepAlive?: number; + + /** + * 【目录】只有一个子路由是否始终显示 + */ + alwaysShow?: number; +} diff --git a/src/api/role/index.ts b/src/api/role/index.ts new file mode 100644 index 0000000..fdbc65c --- /dev/null +++ b/src/api/role/index.ts @@ -0,0 +1,112 @@ +import request from "@/utils/request"; +import { AxiosPromise } from "axios"; +import { RoleQuery, RolePageResult, RoleForm } from "./types"; + +/** + * 获取角色分页数据 + * + * @param queryParams + */ +export function getRolePage( + queryParams?: RoleQuery +): AxiosPromise { + return request({ + url: "/api/v1/roles/page", + method: "get", + params: queryParams, + }); +} + +/** + * 获取角色下拉数据 + * + * @param queryParams + */ +export function getRoleOptions( + queryParams?: RoleQuery +): AxiosPromise { + return request({ + url: "/api/v1/roles/options", + method: "get", + params: queryParams, + }); +} + +/** + * 获取角色的菜单ID集合 + * + * @param queryParams + */ +export function getRoleMenuIds(roleId: number): AxiosPromise { + return request({ + url: "/api/v1/roles/" + roleId + "/menuIds", + method: "get", + }); +} + +/** + * 分配菜单权限给角色 + * + * @param queryParams + */ +export function updateRoleMenus( + roleId: number, + data: number[] +): AxiosPromise { + return request({ + url: "/api/v1/roles/" + roleId + "/menus", + method: "put", + data: data, + }); +} + +/** + * 获取角色详情 + * + * @param id + */ +export function getRoleForm(id: number): AxiosPromise { + return request({ + url: "/api/v1/roles/" + id + "/form", + method: "get", + }); +} + +/** + * 添加角色 + * + * @param data + */ +export function addRole(data: RoleForm) { + return request({ + url: "/api/v1/roles", + method: "post", + data: data, + }); +} + +/** + * 更新角色 + * + * @param id + * @param data + */ +export function updateRole(id: number, data: RoleForm) { + return request({ + url: "/api/v1/roles/" + id, + method: "put", + data: data, + }); +} + +/** + * 批量删除角色,多个以英文逗号(,)分割 + * + * @param ids + */ +export function deleteRoles(ids: string) { + return request({ + url: "/api/v1/roles/" + ids, + method: "delete", + }); +} diff --git a/src/api/role/types.ts b/src/api/role/types.ts new file mode 100644 index 0000000..2d259c7 --- /dev/null +++ b/src/api/role/types.ts @@ -0,0 +1,78 @@ +/** + * 角色查询参数 + */ +export interface RoleQuery extends PageQuery { + keywords?: string; +} + +/** + * 角色分页对象 + */ +export interface RolePageVO { + /** + * 角色编码 + */ + code?: string; + + /** + * 角色ID + */ + id?: number; + /** + * 角色名称 + */ + name?: string; + /** + * 排序 + */ + sort?: number; + /** + * 角色状态 + */ + status?: number; + /** + * 创建时间 + */ + createTime?: Date; + /** + * 修改时间 + */ + updateTime?: Date; +} + +/** + * 角色分页 + */ +export type RolePageResult = PageResult; + +/** + * 角色表单对象 + */ +export interface RoleForm { + /** + * 角色ID + */ + id?: number; + + /** + * 角色编码 + */ + code: string; + /** + * 数据权限 + */ + dataScope?: number; + + /** + * 角色名称 + */ + name: string; + /** + * 排序 + */ + sort?: number; + /** + * 角色状态(1-正常;0-停用) + */ + status?: number; +} diff --git a/src/api/type-ver-ext/index.ts b/src/api/type-ver-ext/index.ts new file mode 100644 index 0000000..4a97017 --- /dev/null +++ b/src/api/type-ver-ext/index.ts @@ -0,0 +1,19 @@ +import { AxiosPromise } from "axios"; +import { CapabilityForm } from "@/api/type-ver-ext/types"; +import request from "@/utils/request"; + +export function getCapabilityForm( + typeAndVerId: string +): AxiosPromise { + return request({ + url: `/api/type-ver-ext/v1/${typeAndVerId}`, + method: "GET", + }); +} +export function editCapabilityForm(data: CapabilityForm) { + return request({ + url: `/api/type-ver-ext/v1/edit`, + method: "POST", + data, + }); +} diff --git a/src/api/type-ver-ext/types.ts b/src/api/type-ver-ext/types.ts new file mode 100644 index 0000000..174b9fd --- /dev/null +++ b/src/api/type-ver-ext/types.ts @@ -0,0 +1,16 @@ +export interface CapabilityForm { + typeAndVerId?: string | number; + lanNumber?: string; + voipPortNumber?: string; + gePortNumber?: string; + fePortNumber?: string; + wifiType?: string; + wifiConfig?: string; + voipProtocol?: string; + capability?: string; + mesh?: string; + ywg?: string; + tr143?: string; + ipv6Enable?: string; + reboot?: string; +} diff --git a/src/api/user/index.ts b/src/api/user/index.ts new file mode 100644 index 0000000..3c7087a --- /dev/null +++ b/src/api/user/index.ts @@ -0,0 +1,140 @@ +import request from "@/utils/request"; +import { AxiosPromise } from "axios"; +import { UserForm, UserInfo, UserPageVO, UserQuery } from "./types"; + +/** + * 登录成功后获取用户信息(昵称、头像、权限集合和角色集合) + */ +export function getUserInfoApi(): AxiosPromise { + return request({ + url: "/api/v1/users/me", + method: "get", + }); +} + +/** + * 获取用户分页列表 + * + * @param queryParams + */ +export function getUserPage( + queryParams: UserQuery +): AxiosPromise> { + return request({ + url: "/api/v1/users/page", + method: "get", + params: queryParams, + }); +} + +/** + * 获取用户表单详情 + * + * @param userId + */ +export function getUserForm(userId: number): AxiosPromise { + return request({ + url: "/api/v1/users/" + userId + "/form", + method: "get", + }); +} + +/** + * 添加用户 + * + * @param data + */ +export function addUser(data: any) { + return request({ + url: "/api/v1/users", + method: "post", + data: data, + }); +} + +/** + * 修改用户 + * + * @param id + * @param data + */ +export function updateUser(id: number, data: UserForm) { + return request({ + url: "/api/v1/users/" + id, + method: "put", + data: data, + }); +} + +/** + * 修改用户密码 + * + * @param id + * @param password + */ +export function updateUserPassword(id: number, password: string) { + return request({ + url: "/api/v1/users/" + id + "/password", + method: "patch", + params: { password: password }, + }); +} + +/** + * 删除用户 + * + * @param ids + */ +export function deleteUsers(ids: string) { + return request({ + url: "/api/v1/users/" + ids, + method: "delete", + }); +} + +/** + * 下载用户导入模板 + * + * @returns + */ +export function downloadTemplateApi() { + return request({ + url: "/api/v1/users/template", + method: "get", + responseType: "arraybuffer", + }); +} + +/** + * 导出用户 + * + * @param queryParams + * @returns + */ +export function exportUser(queryParams: UserQuery) { + return request({ + url: "/api/v1/users/_export", + method: "get", + params: queryParams, + responseType: "arraybuffer", + }); +} + +/** + * 导入用户 + * + * @param file + */ +export function importUser(deptId: number, file: File) { + const formData = new FormData(); + formData.append("file", file); + return request({ + url: "/api/v1/users/_import", + method: "post", + params: { deptId: deptId }, + data: formData, + headers: { + "Content-Type": "multipart/form-data", + }, + }); +} diff --git a/src/api/user/types.ts b/src/api/user/types.ts new file mode 100644 index 0000000..bd6335c --- /dev/null +++ b/src/api/user/types.ts @@ -0,0 +1,124 @@ +/** + * 登录用户信息 + */ +export interface UserInfo { + userId?: number; + username?: string; + nickname?: string; + avatar?: string; + roles: string[]; + perms: string[]; +} + +/** + * 用户查询对象类型 + */ +export interface UserQuery extends PageQuery { + keywords?: string; + status?: number; + deptId?: number; + startTime?: string; + endTime?: string; + groupId?: number; +} + +/** + * 用户分页对象 + */ +export interface UserPageVO { + /** + * 用户头像地址 + */ + avatar?: string; + /** + * 创建时间 + */ + createTime?: Date; + /** + * 部门名称 + */ + deptName?: string; + /** + * 用户邮箱 + */ + email?: string; + /** + * 性别 + */ + genderLabel?: string; + /** + * 用户ID + */ + id?: number; + /** + * 手机号 + */ + mobile?: string; + /** + * 用户昵称 + */ + nickname?: string; + /** + * 角色名称,多个使用英文逗号(,)分割 + */ + roleNames?: string; + /** + * 用户状态(1:启用;0:禁用) + */ + status?: number; + /** + * 用户名 + */ + username?: string; + /** + * xi + */ + groupName?: string; +} + +/** + * 用户表单类型 + */ +export interface UserForm { + /** + * 用户头像 + */ + avatar?: string; + /** + * 部门ID + */ + deptId?: number; + /** + * 邮箱 + */ + email?: string; + /** + * 性别 + */ + gender?: number; + /** + * 用户ID + */ + id?: number; + mobile?: string; + /** + * 昵称 + */ + nickname?: string; + /** + * 角色ID集合 + */ + roleIds?: number[]; + /** + * 用户状态(1:正常;0:禁用) + */ + status?: number; + /** + * 用户名 + */ + username?: string; + /** + * 系统管理域 + */ + domainGroupId?: number; +} diff --git a/src/assets/icons/api.svg b/src/assets/icons/api.svg new file mode 100644 index 0000000..0181bdd --- /dev/null +++ b/src/assets/icons/api.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/captcha.svg b/src/assets/icons/captcha.svg new file mode 100644 index 0000000..8b1da30 --- /dev/null +++ b/src/assets/icons/captcha.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/cascader.svg b/src/assets/icons/cascader.svg new file mode 100644 index 0000000..57209bf --- /dev/null +++ b/src/assets/icons/cascader.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/client.svg b/src/assets/icons/client.svg new file mode 100644 index 0000000..7373b3d --- /dev/null +++ b/src/assets/icons/client.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/close.svg b/src/assets/icons/close.svg new file mode 100644 index 0000000..e99c978 --- /dev/null +++ b/src/assets/icons/close.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/close_all.svg b/src/assets/icons/close_all.svg new file mode 100644 index 0000000..2005198 --- /dev/null +++ b/src/assets/icons/close_all.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/close_left.svg b/src/assets/icons/close_left.svg new file mode 100644 index 0000000..fc5cf71 --- /dev/null +++ b/src/assets/icons/close_left.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/close_other.svg b/src/assets/icons/close_other.svg new file mode 100644 index 0000000..27ffc32 --- /dev/null +++ b/src/assets/icons/close_other.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/close_right.svg b/src/assets/icons/close_right.svg new file mode 100644 index 0000000..b96dc1c --- /dev/null +++ b/src/assets/icons/close_right.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/dict.svg b/src/assets/icons/dict.svg new file mode 100644 index 0000000..db60220 --- /dev/null +++ b/src/assets/icons/dict.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/document.svg b/src/assets/icons/document.svg new file mode 100644 index 0000000..aaa0574 --- /dev/null +++ b/src/assets/icons/document.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/download.svg b/src/assets/icons/download.svg new file mode 100644 index 0000000..a8077dc --- /dev/null +++ b/src/assets/icons/download.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/edit.svg b/src/assets/icons/edit.svg new file mode 100644 index 0000000..82152b5 --- /dev/null +++ b/src/assets/icons/edit.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/eye-open.svg b/src/assets/icons/eye-open.svg new file mode 100644 index 0000000..b80fd2b --- /dev/null +++ b/src/assets/icons/eye-open.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/eye.svg b/src/assets/icons/eye.svg new file mode 100644 index 0000000..16ed2d8 --- /dev/null +++ b/src/assets/icons/eye.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/folder.svg b/src/assets/icons/folder.svg new file mode 100644 index 0000000..f4c406d --- /dev/null +++ b/src/assets/icons/folder.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/fullscreen-exit.svg b/src/assets/icons/fullscreen-exit.svg new file mode 100644 index 0000000..2452f2b --- /dev/null +++ b/src/assets/icons/fullscreen-exit.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/fullscreen.svg b/src/assets/icons/fullscreen.svg new file mode 100644 index 0000000..4b6ee11 --- /dev/null +++ b/src/assets/icons/fullscreen.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/github.svg b/src/assets/icons/github.svg new file mode 100644 index 0000000..1adfa4e --- /dev/null +++ b/src/assets/icons/github.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/homepage.svg b/src/assets/icons/homepage.svg new file mode 100644 index 0000000..1e1feab --- /dev/null +++ b/src/assets/icons/homepage.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/indent-decrease.svg b/src/assets/icons/indent-decrease.svg new file mode 100644 index 0000000..1507568 --- /dev/null +++ b/src/assets/icons/indent-decrease.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/ip.svg b/src/assets/icons/ip.svg new file mode 100644 index 0000000..a50d9bb --- /dev/null +++ b/src/assets/icons/ip.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/language.svg b/src/assets/icons/language.svg new file mode 100644 index 0000000..e754062 --- /dev/null +++ b/src/assets/icons/language.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/link.svg b/src/assets/icons/link.svg new file mode 100644 index 0000000..15a5c62 --- /dev/null +++ b/src/assets/icons/link.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/menu.svg b/src/assets/icons/menu.svg new file mode 100644 index 0000000..f5875d3 --- /dev/null +++ b/src/assets/icons/menu.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/message.svg b/src/assets/icons/message.svg new file mode 100644 index 0000000..c83af99 --- /dev/null +++ b/src/assets/icons/message.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/money.svg b/src/assets/icons/money.svg new file mode 100644 index 0000000..db7a2b4 --- /dev/null +++ b/src/assets/icons/money.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/monitor.svg b/src/assets/icons/monitor.svg new file mode 100644 index 0000000..f153b9c --- /dev/null +++ b/src/assets/icons/monitor.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/moon.svg b/src/assets/icons/moon.svg new file mode 100644 index 0000000..1b66489 --- /dev/null +++ b/src/assets/icons/moon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/order.svg b/src/assets/icons/order.svg new file mode 100644 index 0000000..7ba029e --- /dev/null +++ b/src/assets/icons/order.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/peoples.svg b/src/assets/icons/peoples.svg new file mode 100644 index 0000000..5b21639 --- /dev/null +++ b/src/assets/icons/peoples.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/project.svg b/src/assets/icons/project.svg new file mode 100644 index 0000000..e74a210 --- /dev/null +++ b/src/assets/icons/project.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/publish.svg b/src/assets/icons/publish.svg new file mode 100644 index 0000000..d41061f --- /dev/null +++ b/src/assets/icons/publish.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/refresh.svg b/src/assets/icons/refresh.svg new file mode 100644 index 0000000..e598ed1 --- /dev/null +++ b/src/assets/icons/refresh.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/role.svg b/src/assets/icons/role.svg new file mode 100644 index 0000000..5d25278 --- /dev/null +++ b/src/assets/icons/role.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/security.svg b/src/assets/icons/security.svg new file mode 100644 index 0000000..0e64336 --- /dev/null +++ b/src/assets/icons/security.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/setting.svg b/src/assets/icons/setting.svg new file mode 100644 index 0000000..fbc4945 --- /dev/null +++ b/src/assets/icons/setting.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/size.svg b/src/assets/icons/size.svg new file mode 100644 index 0000000..f92f852 --- /dev/null +++ b/src/assets/icons/size.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/sunny.svg b/src/assets/icons/sunny.svg new file mode 100644 index 0000000..655a72e --- /dev/null +++ b/src/assets/icons/sunny.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/system.svg b/src/assets/icons/system.svg new file mode 100644 index 0000000..2e6045b --- /dev/null +++ b/src/assets/icons/system.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/table.svg b/src/assets/icons/table.svg new file mode 100644 index 0000000..1a16abb --- /dev/null +++ b/src/assets/icons/table.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/todolist.svg b/src/assets/icons/todolist.svg new file mode 100644 index 0000000..cd55ade --- /dev/null +++ b/src/assets/icons/todolist.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/tree.svg b/src/assets/icons/tree.svg new file mode 100644 index 0000000..51aea8f --- /dev/null +++ b/src/assets/icons/tree.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/user.svg b/src/assets/icons/user.svg new file mode 100644 index 0000000..0015cc4 --- /dev/null +++ b/src/assets/icons/user.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/visit.svg b/src/assets/icons/visit.svg new file mode 100644 index 0000000..997f690 --- /dev/null +++ b/src/assets/icons/visit.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/images/401.gif b/src/assets/images/401.gif new file mode 100644 index 0000000..4c930e7 Binary files /dev/null and b/src/assets/images/401.gif differ diff --git a/src/assets/images/404.png b/src/assets/images/404.png new file mode 100644 index 0000000..4e09829 Binary files /dev/null and b/src/assets/images/404.png differ diff --git a/src/assets/images/404_cloud.png b/src/assets/images/404_cloud.png new file mode 100644 index 0000000..a81d8da Binary files /dev/null and b/src/assets/images/404_cloud.png differ diff --git a/src/assets/images/login-bg-dark.jpg b/src/assets/images/login-bg-dark.jpg new file mode 100644 index 0000000..50dc817 Binary files /dev/null and b/src/assets/images/login-bg-dark.jpg differ diff --git a/src/assets/images/login-bg.jpg b/src/assets/images/login-bg.jpg new file mode 100644 index 0000000..993b958 Binary files /dev/null and b/src/assets/images/login-bg.jpg differ diff --git a/src/assets/logo.png b/src/assets/logo.png new file mode 100644 index 0000000..feb87da Binary files /dev/null and b/src/assets/logo.png differ diff --git a/src/components/AppLink/index.vue b/src/components/AppLink/index.vue new file mode 100644 index 0000000..a3afe25 --- /dev/null +++ b/src/components/AppLink/index.vue @@ -0,0 +1,40 @@ + + + diff --git a/src/components/Breadcrumb/index.vue b/src/components/Breadcrumb/index.vue new file mode 100644 index 0000000..df9849d --- /dev/null +++ b/src/components/Breadcrumb/index.vue @@ -0,0 +1,97 @@ + + + + + diff --git a/src/components/Dictionary/index.vue b/src/components/Dictionary/index.vue new file mode 100644 index 0000000..b2e01d0 --- /dev/null +++ b/src/components/Dictionary/index.vue @@ -0,0 +1,73 @@ + + + diff --git a/src/components/Hamburger/index.vue b/src/components/Hamburger/index.vue new file mode 100644 index 0000000..e544db7 --- /dev/null +++ b/src/components/Hamburger/index.vue @@ -0,0 +1,39 @@ + + + + diff --git a/src/components/IconSelect/index.vue b/src/components/IconSelect/index.vue new file mode 100644 index 0000000..2c52504 --- /dev/null +++ b/src/components/IconSelect/index.vue @@ -0,0 +1,200 @@ + + + + + diff --git a/src/components/LangSelect/index.vue b/src/components/LangSelect/index.vue new file mode 100644 index 0000000..993af5d --- /dev/null +++ b/src/components/LangSelect/index.vue @@ -0,0 +1,45 @@ + + + diff --git a/src/components/Pagination/index.vue b/src/components/Pagination/index.vue new file mode 100644 index 0000000..4065131 --- /dev/null +++ b/src/components/Pagination/index.vue @@ -0,0 +1,79 @@ + + + + + diff --git a/src/components/SizeSelect/index.vue b/src/components/SizeSelect/index.vue new file mode 100644 index 0000000..a5aa790 --- /dev/null +++ b/src/components/SizeSelect/index.vue @@ -0,0 +1,34 @@ + + + diff --git a/src/components/SvgIcon/index.vue b/src/components/SvgIcon/index.vue new file mode 100644 index 0000000..07b65e8 --- /dev/null +++ b/src/components/SvgIcon/index.vue @@ -0,0 +1,45 @@ + + + + + diff --git a/src/components/Table/AnyTable.vue b/src/components/Table/AnyTable.vue new file mode 100644 index 0000000..545a163 --- /dev/null +++ b/src/components/Table/AnyTable.vue @@ -0,0 +1,40 @@ + + + + + + + diff --git a/src/components/Table/TableBar.vue b/src/components/Table/TableBar.vue new file mode 100644 index 0000000..1d8d863 --- /dev/null +++ b/src/components/Table/TableBar.vue @@ -0,0 +1,120 @@ + + + + + diff --git a/src/components/WangEditor/index.vue b/src/components/WangEditor/index.vue new file mode 100644 index 0000000..2f4c678 --- /dev/null +++ b/src/components/WangEditor/index.vue @@ -0,0 +1,74 @@ + + + + + diff --git a/src/directive/index.ts b/src/directive/index.ts new file mode 100644 index 0000000..9c22eb6 --- /dev/null +++ b/src/directive/index.ts @@ -0,0 +1,9 @@ +import type { App } from "vue"; + +import { hasPerm } from "./permission"; + +// 全局注册 directive +export function setupDirective(app: App) { + // 使 v-hasPerm 在所有组件中都可用 + app.directive("hasPerm", hasPerm); +} diff --git a/src/directive/permission/index.ts b/src/directive/permission/index.ts new file mode 100644 index 0000000..2bd2c70 --- /dev/null +++ b/src/directive/permission/index.ts @@ -0,0 +1,55 @@ +import { useUserStoreHook } from "@/store/modules/user"; +import { Directive, DirectiveBinding } from "vue"; + +/** + * 按钮权限 + */ +export const hasPerm: Directive = { + mounted(el: HTMLElement, binding: DirectiveBinding) { + // 「超级管理员」拥有所有的按钮权限 + const { roles, perms } = useUserStoreHook().user; + if (roles.includes("ROOT")) { + return true; + } + // 「其他角色」按钮权限校验 + const { value } = binding; + if (value) { + const requiredPerms = value; // DOM绑定需要的按钮权限标识 + + const hasPerm = perms?.some((perm) => { + return requiredPerms.includes(perm); + }); + + if (!hasPerm) { + el.parentNode && el.parentNode.removeChild(el); + } + } else { + throw new Error( + "need perms! Like v-has-perm=\"['sys:user:add','sys:user:edit']\"" + ); + } + }, +}; + +/** + * 角色权限 + */ +export const hasRole: Directive = { + mounted(el: HTMLElement, binding: DirectiveBinding) { + const { value } = binding; + + if (value) { + const requiredRoles = value; // DOM绑定需要的角色编码 + const { roles } = useUserStoreHook().user; + const hasRole = roles.some((perm) => { + return requiredRoles.includes(perm); + }); + + if (!hasRole) { + el.parentNode && el.parentNode.removeChild(el); + } + } else { + throw new Error("need roles! Like v-has-role=\"['admin','test']\""); + } + }, +}; diff --git a/src/enums/LayoutEnum.ts b/src/enums/LayoutEnum.ts new file mode 100644 index 0000000..dd9b043 --- /dev/null +++ b/src/enums/LayoutEnum.ts @@ -0,0 +1,18 @@ +/** + * 菜单布局枚举 + */ +export enum LayoutEnum { + /** + * 左侧菜单布局 + */ + LEFT = "left", + /** + * 顶部菜单布局 + */ + TOP = "top", + + /** + * 混合菜单布局 + */ + MIX = "mix", +} diff --git a/src/enums/MenuTypeEnum.ts b/src/enums/MenuTypeEnum.ts new file mode 100644 index 0000000..38e92fc --- /dev/null +++ b/src/enums/MenuTypeEnum.ts @@ -0,0 +1,22 @@ +/** + * 菜单类型枚举 + */ +export enum MenuTypeEnum { + /** + * 目录 + */ + CATALOG = "CATALOG", + /** + * 菜单 + */ + MENU = "MENU", + + /** + * 按钮 + */ + BUTTON = "BUTTON", + /** + * 外链 + */ + EXTLINK = "EXTLINK", +} diff --git a/src/enums/ThemeEnum.ts b/src/enums/ThemeEnum.ts new file mode 100644 index 0000000..6102fa6 --- /dev/null +++ b/src/enums/ThemeEnum.ts @@ -0,0 +1,18 @@ +/** + * 主题枚举 + */ +export enum ThemeEnum { + /** + * 明亮主题 + */ + LIGHT = "light", + /** + * 暗黑主题 + */ + DARK = "dark", + + /** + * 系统自动 + */ + AUTO = "auto", +} diff --git a/src/lang/index.ts b/src/lang/index.ts new file mode 100644 index 0000000..dad4395 --- /dev/null +++ b/src/lang/index.ts @@ -0,0 +1,25 @@ +import { createI18n } from "vue-i18n"; +import { useAppStoreHook } from "@/store/modules/app"; +// 本地语言包 +import enLocale from "./package/en"; +import zhCnLocale from "./package/zh-cn"; + +const appStore = useAppStoreHook(); + +const messages = { + "zh-cn": { + ...zhCnLocale, + }, + en: { + ...enLocale, + }, +}; + +const i18n = createI18n({ + legacy: false, + locale: appStore.language, + messages: messages, + globalInjection: true, +}); + +export default i18n; diff --git a/src/lang/package/en.ts b/src/lang/package/en.ts new file mode 100644 index 0000000..e14790e --- /dev/null +++ b/src/lang/package/en.ts @@ -0,0 +1,19 @@ +export default { + // 路由国际化 + route: { + dashboard: "Dashboard", + document: "Document", + }, + // 登录页面国际化 + login: { + username: "Username", + password: "Password", + login: "Login", + captchaCode: "Verify Code", + }, + // 导航栏国际化 + navbar: { + dashboard: "Dashboard", + logout: "Logout", + }, +}; diff --git a/src/lang/package/zh-cn.ts b/src/lang/package/zh-cn.ts new file mode 100644 index 0000000..5a35f00 --- /dev/null +++ b/src/lang/package/zh-cn.ts @@ -0,0 +1,19 @@ +export default { + // 路由国际化 + route: { + dashboard: "工作台", + document: "项目文档", + }, + // 登录页面国际化 + login: { + username: "用户名", + password: "密码", + login: "登 录", + captchaCode: "验证码", + }, + // 导航栏国际化 + navbar: { + dashboard: "工作台", + logout: "注销", + }, +}; diff --git a/src/layout/components/AppMain/index.vue b/src/layout/components/AppMain/index.vue new file mode 100644 index 0000000..c8017b2 --- /dev/null +++ b/src/layout/components/AppMain/index.vue @@ -0,0 +1,82 @@ + + + + + diff --git a/src/layout/components/NavBar/components/NavbarLeft.vue b/src/layout/components/NavBar/components/NavbarLeft.vue new file mode 100644 index 0000000..937391f --- /dev/null +++ b/src/layout/components/NavBar/components/NavbarLeft.vue @@ -0,0 +1,19 @@ + + + diff --git a/src/layout/components/NavBar/components/NavbarRight.vue b/src/layout/components/NavBar/components/NavbarRight.vue new file mode 100644 index 0000000..363f20f --- /dev/null +++ b/src/layout/components/NavBar/components/NavbarRight.vue @@ -0,0 +1,116 @@ + + + diff --git a/src/layout/components/NavBar/index.vue b/src/layout/components/NavBar/index.vue new file mode 100644 index 0000000..9fe18a2 --- /dev/null +++ b/src/layout/components/NavBar/index.vue @@ -0,0 +1,18 @@ + + + diff --git a/src/layout/components/Settings/components/LayoutSelect.vue b/src/layout/components/Settings/components/LayoutSelect.vue new file mode 100644 index 0000000..89e9a91 --- /dev/null +++ b/src/layout/components/Settings/components/LayoutSelect.vue @@ -0,0 +1,108 @@ + + + + + diff --git a/src/layout/components/Settings/components/ThemeColorPicker.vue b/src/layout/components/Settings/components/ThemeColorPicker.vue new file mode 100644 index 0000000..5a0d59c --- /dev/null +++ b/src/layout/components/Settings/components/ThemeColorPicker.vue @@ -0,0 +1,41 @@ + + + + + diff --git a/src/layout/components/Settings/index.vue b/src/layout/components/Settings/index.vue new file mode 100644 index 0000000..0460bf4 --- /dev/null +++ b/src/layout/components/Settings/index.vue @@ -0,0 +1,141 @@ + + + + + diff --git a/src/layout/components/Sidebar/components/SidebarLogo.vue b/src/layout/components/Sidebar/components/SidebarLogo.vue new file mode 100644 index 0000000..b873563 --- /dev/null +++ b/src/layout/components/Sidebar/components/SidebarLogo.vue @@ -0,0 +1,63 @@ + + + + + diff --git a/src/layout/components/Sidebar/components/SidebarMenu.vue b/src/layout/components/Sidebar/components/SidebarMenu.vue new file mode 100644 index 0000000..5c5c6d3 --- /dev/null +++ b/src/layout/components/Sidebar/components/SidebarMenu.vue @@ -0,0 +1,82 @@ + + + + + diff --git a/src/layout/components/Sidebar/components/SidebarMenuItem.vue b/src/layout/components/Sidebar/components/SidebarMenuItem.vue new file mode 100644 index 0000000..552fa17 --- /dev/null +++ b/src/layout/components/Sidebar/components/SidebarMenuItem.vue @@ -0,0 +1,191 @@ + + + + diff --git a/src/layout/components/Sidebar/components/SidebarMenuItemTitle.vue b/src/layout/components/Sidebar/components/SidebarMenuItemTitle.vue new file mode 100644 index 0000000..b9045d9 --- /dev/null +++ b/src/layout/components/Sidebar/components/SidebarMenuItemTitle.vue @@ -0,0 +1,42 @@ + + + + + diff --git a/src/layout/components/Sidebar/components/SidebarMixTopMenu.vue b/src/layout/components/Sidebar/components/SidebarMixTopMenu.vue new file mode 100644 index 0000000..8a03e7d --- /dev/null +++ b/src/layout/components/Sidebar/components/SidebarMixTopMenu.vue @@ -0,0 +1,83 @@ + + + + diff --git a/src/layout/components/Sidebar/index.vue b/src/layout/components/Sidebar/index.vue new file mode 100644 index 0000000..8956f90 --- /dev/null +++ b/src/layout/components/Sidebar/index.vue @@ -0,0 +1,37 @@ + + + + + diff --git a/src/layout/components/TagsView/index.vue b/src/layout/components/TagsView/index.vue new file mode 100644 index 0000000..a84aa74 --- /dev/null +++ b/src/layout/components/TagsView/index.vue @@ -0,0 +1,470 @@ + + + + + diff --git a/src/layout/index.vue b/src/layout/index.vue new file mode 100644 index 0000000..bab1716 --- /dev/null +++ b/src/layout/index.vue @@ -0,0 +1,343 @@ + + + + + diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..0c6c44e --- /dev/null +++ b/src/main.ts @@ -0,0 +1,29 @@ +import { createApp } from "vue"; +import App from "./App.vue"; +import router from "@/router"; +import { setupStore } from "@/store"; +import { setupDirective } from "@/directive"; +import { setupElIcons, setupI18n, setupPermission } from "@/plugins"; + +// 本地SVG图标 +import "virtual:svg-icons-register"; + +// 样式 +import "element-plus/theme-chalk/dark/css-vars.css"; +import "@/styles/index.scss"; +import "@/styles/table.scss"; +import "uno.css"; +import "animate.css"; + +const app = createApp(App); +// 全局注册 自定义指令(directive) +setupDirective(app); +// 全局注册 状态管理(store) +setupStore(app); +// 全局注册Element-plus图标 +setupElIcons(app); +// 国际化 +setupI18n(app); +// 注册动态路由 +setupPermission(); +app.use(router).mount("#app"); diff --git a/src/plugins/i18n.ts b/src/plugins/i18n.ts new file mode 100644 index 0000000..16915cc --- /dev/null +++ b/src/plugins/i18n.ts @@ -0,0 +1,7 @@ +// 国际化 +import i18n from "@/lang/index"; +import type { App } from "vue"; + +export function setupI18n(app: App) { + app.use(i18n); +} diff --git a/src/plugins/icons.ts b/src/plugins/icons.ts new file mode 100644 index 0000000..fa85ba1 --- /dev/null +++ b/src/plugins/icons.ts @@ -0,0 +1,9 @@ +import type { App } from "vue"; +import * as ElementPlusIconsVue from "@element-plus/icons-vue"; + +// 注册所有图标 +export function setupElIcons(app: App) { + for (const [key, component] of Object.entries(ElementPlusIconsVue)) { + app.component(key, component); + } +} diff --git a/src/plugins/index.ts b/src/plugins/index.ts new file mode 100644 index 0000000..b54ee18 --- /dev/null +++ b/src/plugins/index.ts @@ -0,0 +1,3 @@ +export * from "./icons"; +export * from "./i18n"; +export * from "./permission"; diff --git a/src/plugins/permission.ts b/src/plugins/permission.ts new file mode 100644 index 0000000..9fbce9f --- /dev/null +++ b/src/plugins/permission.ts @@ -0,0 +1,60 @@ +import router from "@/router"; +import { useUserStore } from "@/store/modules/user"; +import { usePermissionStore } from "@/store/modules/permission"; +import NProgress from "@/utils/nprogress"; + +export function setupPermission() { + // 白名单路由 + const whiteList = ["/login"]; + + router.beforeEach(async (to, from, next) => { + NProgress.start(); + const hasToken = localStorage.getItem("token"); + if (hasToken) { + if (to.path === "/login") { + // 如果已登录,跳转首页 + next({ path: "/" }); + NProgress.done(); + } else { + const userStore = useUserStore(); + const hasRoles = + userStore.user.roles && userStore.user.roles.length > 0; + if (hasRoles) { + // 未匹配到任何路由,跳转404 + if (to.matched.length === 0) { + from.name ? next({ name: from.name }) : next("/404"); + } else { + next(); + } + } else { + const permissionStore = usePermissionStore(); + try { + const { roles } = await userStore.getUserInfo(); + const accessRoutes = await permissionStore.generateRoutes(roles); + accessRoutes.forEach((route) => { + router.addRoute(route); + }); + next({ ...to, replace: true }); + } catch (error) { + // 移除 token 并跳转登录页 + await userStore.resetToken(); + next(`/login?redirect=${to.path}`); + NProgress.done(); + } + } + } + } else { + // 未登录可以访问白名单页面 + if (whiteList.indexOf(to.path) !== -1) { + next(); + } else { + next(`/login?redirect=${to.path}`); + NProgress.done(); + } + } + }); + + router.afterEach(() => { + NProgress.done(); + }); +} diff --git a/src/router/index.ts b/src/router/index.ts new file mode 100644 index 0000000..4e80cd3 --- /dev/null +++ b/src/router/index.ts @@ -0,0 +1,104 @@ +import { createRouter, createWebHashHistory, RouteRecordRaw } from "vue-router"; + +export const Layout = () => import("@/layout/index.vue"); + +// 静态路由 +export const constantRoutes: RouteRecordRaw[] = [ + { + path: "/redirect", + component: Layout, + meta: { hidden: true }, + children: [ + { + path: "/redirect/:path(.*)", + component: () => import("@/views/redirect/index.vue"), + }, + ], + }, + + { + path: "/login", + component: () => import("@/views/login/index.vue"), + meta: { hidden: true }, + }, + { + path: "/table", + component: () => import("@/views/table/index.vue"), + meta: { hidden: true }, + }, + + { + path: "/", + name: "/", + component: Layout, + redirect: "/dashboard", + children: [ + { + path: "dashboard", + component: () => import("@/views/dashboard/index.vue"), + name: "Dashboard", // 用于 keep-alive, 必须与SFC自动推导或者显示声明的组件name一致 + // https://cn.vuejs.org/guide/built-ins/keep-alive.html#include-exclude + meta: { + title: "dashboard", + icon: "homepage", + affix: true, + keepAlive: true, + alwaysShow: false, + }, + }, + { + path: "401", + component: () => import("@/views/error-page/401.vue"), + meta: { hidden: true }, + }, + { + path: "404", + component: () => import("@/views/error-page/404.vue"), + meta: { hidden: true }, + }, + { + path: "/resources/file_list", + name: "FileList", + component: () => import("@/views/resources/file-list/index.vue"), + meta: { hidden: true, keepAlive: true, title: "文件列表" }, + }, + { + path: "/resources/device_type_to_ver/:devTypeId", + name: "DeviceTypeToVer", + component: () => + import( + "@/views/resources/device-type/components/DeviceTypeToVer.vue" + ), + meta: { hidden: true, keepAlive: true, title: "设备类型详情" }, + }, + { + path: "/resources/device_type_version_edit/:devTypeId/:typeAndVerId", + name: "DeviceTypeVersionEdit", + component: () => + import( + "@/views/resources/device-type/components/DeviceTypeVersionEdit.vue" + ), + meta: { hidden: true, keepAlive: true, title: "设备类型软件编辑" }, + }, + ], + }, +]; + +/** + * 创建路由 + */ +const router = createRouter({ + history: createWebHashHistory(), + routes: constantRoutes as RouteRecordRaw[], + // 刷新时,滚动条位置还原 + scrollBehavior: () => ({ left: 0, top: 0 }), +}); + +/** + * 重置路由 + */ +export function resetRouter() { + router.replace({ path: "/login" }); +} + +export default router; diff --git a/src/settings.ts b/src/settings.ts new file mode 100644 index 0000000..92bde20 --- /dev/null +++ b/src/settings.ts @@ -0,0 +1,19 @@ +const { pkg } = __APP_INFO__; + +const defaultSettings: AppSettings = { + title: pkg.name, + version: pkg.version, + showSettings: false, + tagsView: true, + fixedHeader: true, + sidebarLogo: true, + layout: "left", + theme: "light", + size: "default", + language: "zh-cn", + themeColor: "#00C3EE", + watermarkEnabled: true, + watermarkContent: pkg.name, +}; + +export default defaultSettings; diff --git a/src/store/index.ts b/src/store/index.ts new file mode 100644 index 0000000..8dd6ea9 --- /dev/null +++ b/src/store/index.ts @@ -0,0 +1,16 @@ +import type { App } from "vue"; +import { createPinia } from "pinia"; + +const store = createPinia(); + +// 全局注册 store +export function setupStore(app: App) { + app.use(store); +} + +export * from "./modules/app"; +export * from "./modules/permission"; +export * from "./modules/settings"; +export * from "./modules/tagsView"; +export * from "./modules/user"; +export { store }; diff --git a/src/store/modules/app.ts b/src/store/modules/app.ts new file mode 100644 index 0000000..4ca5ba9 --- /dev/null +++ b/src/store/modules/app.ts @@ -0,0 +1,95 @@ +import defaultSettings from "@/settings"; + +// 导入 Element Plus 中英文语言包 +import zhCn from "element-plus/es/locale/lang/zh-cn"; +import en from "element-plus/es/locale/lang/en"; +import { store } from "@/store"; + +// setup +export const useAppStore = defineStore("app", () => { + // state + const device = useStorage("device", "desktop"); + const size = useStorage("size", defaultSettings.size); + const language = useStorage("language", defaultSettings.language); + + const sidebarStatus = useStorage("sidebarStatus", "closed"); + + const sidebar = reactive({ + opened: sidebarStatus.value !== "closed", + withoutAnimation: false, + }); + const activeTopMenuPath = useStorage("activeTopMenuPath", ""); + /** + * 根据语言标识读取对应的语言包 + */ + const locale = computed(() => { + if (language?.value == "en") { + return en; + } else { + return zhCn; + } + }); + + // actions + function toggleSidebar() { + sidebar.opened = !sidebar.opened; + if (sidebar.opened) { + sidebarStatus.value = "opened"; + } else { + sidebarStatus.value = "closed"; + } + } + + function closeSideBar() { + sidebar.opened = false; + sidebarStatus.value = "closed"; + } + + function openSideBar() { + sidebar.opened = true; + sidebarStatus.value = "opened"; + } + + function toggleDevice(val: string) { + device.value = val; + } + + function changeSize(val: string) { + size.value = val; + } + /** + * 切换语言 + * + * @param val + */ + function changeLanguage(val: string) { + language.value = val; + } + /** + * 混合模式顶部切换 + */ + function activeTopMenu(val: string) { + activeTopMenuPath.value = val; + } + return { + device, + sidebar, + language, + locale, + size, + activeTopMenu, + toggleDevice, + changeSize, + changeLanguage, + toggleSidebar, + closeSideBar, + openSideBar, + activeTopMenuPath, + }; +}); + +// 手动提供给 useStore() 函数 pinia 实例 +// https://pinia.vuejs.org/zh/core-concepts/outside-component-usage.html#using-a-store-outside-of-a-component +export function useAppStoreHook() { + return useAppStore(store); +} diff --git a/src/store/modules/permission.ts b/src/store/modules/permission.ts new file mode 100644 index 0000000..175d07c --- /dev/null +++ b/src/store/modules/permission.ts @@ -0,0 +1,122 @@ +import { RouteRecordRaw } from "vue-router"; +import { constantRoutes } from "@/router"; +import { store } from "@/store"; +import { listRoutes } from "@/api/menu"; + +const modules = import.meta.glob("../../views/**/**.vue"); +const Layout = () => import("@/layout/index.vue"); + +/** + * Use meta.role to determine if the current user has permission + * + * @param roles 用户角色集合 + * @param route 路由 + * @returns + */ +const hasPermission = (roles: string[], route: RouteRecordRaw) => { + if (route.meta && route.meta.roles) { + // 角色【超级管理员】拥有所有权限,忽略校验 + if (roles.includes("ROOT")) { + return true; + } + return roles.some((role) => { + if (route.meta?.roles) { + return route.meta.roles.includes(role); + } + }); + } + return false; +}; + +/** + * 递归过滤有权限的异步(动态)路由 + * + * @param routes 接口返回的异步(动态)路由 + * @param roles 用户角色集合 + * @returns 返回用户有权限的异步(动态)路由 + */ +const filterAsyncRoutes = (routes: RouteRecordRaw[], roles: string[]) => { + const asyncRoutes: RouteRecordRaw[] = []; + + routes.forEach((route) => { + const tmpRoute = { ...route }; // ES6扩展运算符复制新对象 + if (!route.name) { + tmpRoute.name = route.path; + } + // 判断用户(角色)是否有该路由的访问权限 + if (hasPermission(roles, tmpRoute)) { + if (tmpRoute.component?.toString() == "Layout") { + tmpRoute.component = Layout; + } else { + const component = modules[`../../views/${tmpRoute.component}.vue`]; + if (component) { + tmpRoute.component = component; + } else { + tmpRoute.component = modules[`../../views/error-page/404.vue`]; + } + } + + if (tmpRoute.children) { + tmpRoute.children = filterAsyncRoutes(tmpRoute.children, roles); + } + + asyncRoutes.push(tmpRoute); + } + }); + + return asyncRoutes; +}; + +// setup +export const usePermissionStore = defineStore("permission", () => { + // state + const routes = ref([]); + + // actions + function setRoutes(newRoutes: RouteRecordRaw[]) { + routes.value = constantRoutes.concat(newRoutes); + } + /** + * 生成动态路由 + * + * @param roles 用户角色集合 + * @returns + */ + function generateRoutes(roles: string[]) { + return new Promise((resolve, reject) => { + // 接口获取所有路由 + listRoutes() + .then(({ data: asyncRoutes }) => { + // 根据角色获取有访问权限的路由 + const accessedRoutes = filterAsyncRoutes(asyncRoutes, roles); + setRoutes(accessedRoutes); + resolve(accessedRoutes); + }) + .catch((error) => { + reject(error); + }); + }); + } + /** + * 获取与激活的顶部菜单项相关的混合模式左侧菜单集合 + */ + const mixLeftMenus = ref([]); + function setMixLeftMenus(topMenuPath: string) { + const matchedItem = routes.value.find((item) => item.path === topMenuPath); + if (matchedItem && matchedItem.children) { + mixLeftMenus.value = matchedItem.children; + } + } + return { + routes, + setRoutes, + generateRoutes, + mixLeftMenus, + setMixLeftMenus, + }; +}); + +// 非setup +export function usePermissionStoreHook() { + return usePermissionStore(store); +} diff --git a/src/store/modules/settings.ts b/src/store/modules/settings.ts new file mode 100644 index 0000000..1a54daa --- /dev/null +++ b/src/store/modules/settings.ts @@ -0,0 +1,124 @@ +import defaultSettings from "@/settings"; +import { genMixColor } from "@/utils/color"; +import { setStyleProperty } from "@/utils"; +import { ThemeEnum } from "@/enums/ThemeEnum"; + +type SettingsValue = boolean | string; + +export const useSettingsStore = defineStore("setting", () => { + // 是否显示设置 + const settingsVisible = ref(false); + // 是否显示标签视图 + const tagsView = useStorage("tagsView", defaultSettings.tagsView); + // 是否显示侧边栏logo + const sidebarLogo = useStorage( + "sidebarLogo", + defaultSettings.sidebarLogo + ); + // 是否固定头部 + const fixedHeader = useStorage( + "fixedHeader", + defaultSettings.fixedHeader + ); + // 布局模式:left-左侧模式(默认) top-顶部模式 mix-混合模式 + const layout = useStorage("layout", defaultSettings.layout); + // 主题颜色 + const themeColor = useStorage( + "themeColor", + defaultSettings.themeColor + ); + // 主题:light-亮色(默认) dark-暗色 + const theme = useStorage("theme", defaultSettings.theme); + // 是否开启水印 + const watermarkEnabled = useStorage( + "watermarkEnabled", + defaultSettings.watermarkEnabled + ); + + watch( + [theme, themeColor], + ([newTheme, newThemeColor], [oldTheme, oldThemeColor]) => { + if (newTheme !== oldTheme) { + if (newTheme === ThemeEnum.DARK) { + document.documentElement.classList.add("dark"); + } else { + document.documentElement.classList.remove("dark"); + } + } + + if (newThemeColor !== oldThemeColor) { + const { DEFAULT, dark, light } = genMixColor(newThemeColor); + setStyleProperty(`--el-color-primary`, DEFAULT); + setStyleProperty(`--el-color-primary-dark-2`, dark[2]); + setStyleProperty(`--el-color-primary-light-3`, light[3]); + setStyleProperty(`--el-color-primary-light-5`, light[5]); + setStyleProperty(`--el-color-primary-light-7`, light[7]); + setStyleProperty(`--el-color-primary-light-8`, light[8]); + setStyleProperty(`--el-color-primary-light-9`, light[9]); + } + }, + { + immediate: true, // 立即执行,确保在侦听器创建时执行一次 + } + ); + + const settingsMap: Record> = { + fixedHeader, + tagsView, + sidebarLogo, + layout, + watermarkEnabled, + }; + + function changeSetting({ + key, + value, + }: { + key: string; + value: SettingsValue; + }) { + const setting = settingsMap[key]; + if (setting) { + setting.value = value; + } + } + + /** + * 切换主题 + */ + function changeTheme(val: string) { + theme.value = val; + } + + /** + * 切换主题颜色 + * + * @param color 主题颜色 + * + */ + function changeThemeColor(color: string) { + themeColor.value = color; + } + + /** + * 切换布局 + */ + function changeLayout(val: string) { + layout.value = val; + } + + return { + settingsVisible, + tagsView, + fixedHeader, + sidebarLogo, + layout, + themeColor, + theme, + watermarkEnabled, + changeSetting, + changeTheme, + changeThemeColor, + changeLayout, + }; +}); diff --git a/src/store/modules/tagsView.ts b/src/store/modules/tagsView.ts new file mode 100644 index 0000000..9e66c10 --- /dev/null +++ b/src/store/modules/tagsView.ts @@ -0,0 +1,211 @@ +export const useTagsViewStore = defineStore("tagsView", () => { + const visitedViews = ref([]); + const cachedViews = ref([]); + + /** + * 添加已访问视图到已访问视图列表中 + */ + function addVisitedView(view: TagView) { + // 如果已经存在于已访问的视图列表中,则不再添加 + if (visitedViews.value.some((v) => v.path === view.path)) { + return; + } + // 如果视图是固定的(affix),则在已访问的视图列表的开头添加 + if (view.affix) { + visitedViews.value.unshift(view); + } else { + // 如果视图不是固定的,则在已访问的视图列表的末尾添加 + visitedViews.value.push(view); + } + } + + /** + * 添加缓存视图到缓存视图列表中 + */ + function addCachedView(view: TagView) { + const viewName = view.name; + // 如果缓存视图名称已经存在于缓存视图列表中,则不再添加 + if (cachedViews.value.includes(viewName)) { + return; + } + + // 如果视图需要缓存(keepAlive),则将其路由名称添加到缓存视图列表中 + if (view.keepAlive) { + cachedViews.value.push(viewName); + } + } + + /** + * 从已访问视图列表中删除指定的视图 + */ + function delVisitedView(view: 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]); + }); + } + + function delCachedView(view: TagView) { + const viewName = view.name; + return new Promise((resolve) => { + const index = cachedViews.value.indexOf(viewName); + index > -1 && cachedViews.value.splice(index, 1); + resolve([...cachedViews.value]); + }); + } + + function delOtherVisitedViews(view: TagView) { + return new Promise((resolve) => { + visitedViews.value = visitedViews.value.filter((v) => { + return v?.affix || v.path === view.path; + }); + resolve([...visitedViews.value]); + }); + } + + function delOtherCachedViews(view: TagView) { + 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 { + // if index = -1, there is no cached tags + cachedViews.value = []; + } + resolve([...cachedViews.value]); + }); + } + + function updateVisitedView(view: TagView) { + for (let v of visitedViews.value) { + if (v.path === view.path) { + v = Object.assign(v, view); + break; + } + } + } + + function addView(view: TagView) { + addVisitedView(view); + addCachedView(view); + } + + function delView(view: TagView) { + return new Promise((resolve) => { + delVisitedView(view); + delCachedView(view); + resolve({ + visitedViews: [...visitedViews.value], + cachedViews: [...cachedViews.value], + }); + }); + } + + function delOtherViews(view: TagView) { + return new Promise((resolve) => { + delOtherVisitedViews(view); + delOtherCachedViews(view); + resolve({ + visitedViews: [...visitedViews.value], + cachedViews: [...cachedViews.value], + }); + }); + } + + function delLeftViews(view: TagView) { + return new Promise((resolve) => { + const currIndex = visitedViews.value.findIndex( + (v) => v.path === view.path + ); + if (currIndex === -1) { + return; + } + visitedViews.value = visitedViews.value.filter((item, index) => { + if (index >= currIndex || item?.affix) { + return true; + } + + const cacheIndex = cachedViews.value.indexOf(item.name); + if (cacheIndex > -1) { + cachedViews.value.splice(cacheIndex, 1); + } + return false; + }); + resolve({ + visitedViews: [...visitedViews.value], + }); + }); + } + function delRightViews(view: TagView) { + return new Promise((resolve) => { + const currIndex = visitedViews.value.findIndex( + (v) => v.path === view.path + ); + if (currIndex === -1) { + return; + } + visitedViews.value = visitedViews.value.filter((item, index) => { + if (index <= currIndex || item?.affix) { + return true; + } + }); + resolve({ + visitedViews: [...visitedViews.value], + }); + }); + } + + function delAllViews() { + return new Promise((resolve) => { + const affixTags = visitedViews.value.filter((tag) => tag?.affix); + visitedViews.value = affixTags; + cachedViews.value = []; + resolve({ + visitedViews: [...visitedViews.value], + cachedViews: [...cachedViews.value], + }); + }); + } + + function delAllVisitedViews() { + return new Promise((resolve) => { + const affixTags = visitedViews.value.filter((tag) => tag?.affix); + visitedViews.value = affixTags; + resolve([...visitedViews.value]); + }); + } + + function delAllCachedViews() { + return new Promise((resolve) => { + cachedViews.value = []; + resolve([...cachedViews.value]); + }); + } + + return { + visitedViews, + cachedViews, + addVisitedView, + addCachedView, + delVisitedView, + delCachedView, + delOtherVisitedViews, + delOtherCachedViews, + updateVisitedView, + addView, + delView, + delOtherViews, + delLeftViews, + delRightViews, + delAllViews, + delAllVisitedViews, + delAllCachedViews, + }; +}); diff --git a/src/store/modules/user.ts b/src/store/modules/user.ts new file mode 100644 index 0000000..e6637dd --- /dev/null +++ b/src/store/modules/user.ts @@ -0,0 +1,94 @@ +import { loginApi, logoutApi } from "@/api/auth"; +import { getUserInfoApi } from "@/api/user"; +import { resetRouter } from "@/router"; +import { store } from "@/store"; + +import { LoginData } from "@/api/auth/types"; +import { UserInfo } from "@/api/user/types"; + +export const useUserStore = defineStore("user", () => { + const user = ref({ + roles: [], + perms: [], + }); + + /** + * 登录 + * + * @param {LoginData} + * @returns + */ + function login(loginData: LoginData) { + return new Promise((resolve, reject) => { + loginApi(loginData) + .then((response) => { + const { tokenType, accessToken } = response.data; + localStorage.setItem("token", tokenType + " " + accessToken); // Bearer eyJhbGciOiJIUzI1NiJ9.xxx.xxx + resolve(); + }) + .catch((error) => { + reject(error); + }); + }); + } + + // 获取信息(用户昵称、头像、角色集合、权限集合) + function getUserInfo() { + return new Promise((resolve, reject) => { + getUserInfoApi() + .then(({ data }) => { + if (!data) { + reject("Verification failed, please Login again."); + return; + } + if (!data.roles || data.roles.length <= 0) { + reject("getUserInfo: roles must be a non-null array!"); + return; + } + Object.assign(user.value, { ...data }); + resolve(data); + }) + .catch((error) => { + reject(error); + }); + }); + } + + // user logout + function logout() { + return new Promise((resolve, reject) => { + logoutApi() + .then(() => { + localStorage.setItem("token", ""); + location.reload(); // 清空路由 + resolve(); + }) + .catch((error) => { + reject(error); + }); + }); + } + + // remove token + function resetToken() { + console.log("resetToken"); + return new Promise((resolve) => { + localStorage.setItem("token", ""); + resetRouter(); + resolve(); + }); + } + + return { + user, + login, + getUserInfo, + logout, + resetToken, + }; +}); + +// 非setup +export function useUserStoreHook() { + return useUserStore(store); +} diff --git a/src/styles/index.scss b/src/styles/index.scss new file mode 100644 index 0000000..38db443 --- /dev/null +++ b/src/styles/index.scss @@ -0,0 +1,71 @@ +@use "./reset"; + +.app-container { + padding: 10px; + border-radius: 8px; +} + +.search-container { + padding: 18px 0 0 10px; + margin-bottom: 10px; + background-color: var(--el-bg-color-overlay); + border: 1px solid var(--el-border-color-light); + border-radius: 8px; + //box-shadow: var(--el-box-shadow-light); +} + +.table-container > .el-card__header { + padding: calc(var(--el-card-padding) - 8px) var(--el-card-padding); +} +.table-container { + border-radius: 8px !important; +} + +.link-type, +.link-type:focus { + color: #337ab7; + cursor: pointer; + + &:hover { + color: rgb(32 160 255); + } +} + +.any-table { + background-color: #fff; + overflow: hidden; + border-radius: 10px; + border-top: 1px solid var(--el-border-color-light); + border-left: 1px solid var(--el-border-color-light); + border-right: 1px solid var(--el-border-color-light); +} + +.com-dialog { + .el-dialog { + margin: 0 !important; + &__header { + height: 40px; + padding: 0; + margin-right: 0 !important; + border-bottom: 1px solid var(--el-border-color); + } + &__body { + padding: 15px !important; + } + &__footer { + border-top: 1px solid var(--el-border-color); + } + &__headerbtn { + top: 0; + } + } +} + +.table-head-parent { + display: flex; + justify-content: space-between; +} +.head-parent-right { + display: flex; + justify-content: flex-end; +} diff --git a/src/styles/reset.scss b/src/styles/reset.scss new file mode 100644 index 0000000..a20f04a --- /dev/null +++ b/src/styles/reset.scss @@ -0,0 +1,76 @@ +*, +::before, +::after { + box-sizing: border-box; + border-color: currentcolor; + border-style: solid; + border-width: 0; +} + +#app { + width: 100%; + height: 100%; +} + +html { + box-sizing: border-box; + width: 100%; + height: 100%; + line-height: 1.5; + tab-size: 4; + text-size-adjust: 100%; +} + +body { + width: 100%; + height: 100%; + margin: 0; + font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", + "Microsoft YaHei", "微软雅黑", Arial, sans-serif; + line-height: inherit; + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + text-rendering: optimizelegibility; +} + +a { + color: inherit; + text-decoration: inherit; +} + +img, +svg { + display: inline-block; +} + +svg { + // 因icon大小被设置为和字体大小一致,而span等标签的下边缘会和字体的基线对齐,故需设置一个往下的偏移比例,来纠正视觉上的未对齐效果 + vertical-align: -0.15em; +} + +ul, +li { + padding: 0; + margin: 0; + list-style: none; +} + +*, +*::before, +*::after { + box-sizing: inherit; +} + +a, +a:focus, +a:hover { + color: inherit; + text-decoration: none; + cursor: pointer; +} + +a:focus, +a:active, +div:focus { + outline: none; +} diff --git a/src/styles/table.scss b/src/styles/table.scss new file mode 100644 index 0000000..f065e3d --- /dev/null +++ b/src/styles/table.scss @@ -0,0 +1,48 @@ +.any-table { + .el-table--enable-row-transition .el-table__body td.el-table__cell { + transition: none; + } + //hover 背景色 + .el-table--enable-row-hover .el-table__body tr:hover > td { + background-color: #fafafa !important; + } + + .el-table { + thead th:first-of-type { + padding-left: 20px; + } + + thead th:last-of-type { + padding-right: 20px; + } + + td:first-of-type { + padding-left: 20px; + } + + td:last-of-type { + padding-right: 20px; + } + + thead { + tr { + th { + background: #fff; + font-weight: 500; + } + } + } + + tr { + height: 55px !important; + } + + th { + color: #76838f !important; + } + + td { + color: #76838f !important; + } + } +} diff --git a/src/styles/variables.module.scss b/src/styles/variables.module.scss new file mode 100644 index 0000000..70aa10b --- /dev/null +++ b/src/styles/variables.module.scss @@ -0,0 +1,10 @@ +/* stylelint-disable property-no-unknown */ +:export { + sidebar-width: $sidebar-width; + navbar-height: $navbar-height; + menu-background: $menu-background; + menu-text: $menu-text; + menu-active-text: $menu-active-text; + menu-hover: $menu-hover; +} +/* stylelint-enable property-no-unknown */ diff --git a/src/styles/variables.scss b/src/styles/variables.scss new file mode 100644 index 0000000..ca0514c --- /dev/null +++ b/src/styles/variables.scss @@ -0,0 +1,29 @@ +/** 全局SCSS变量 */ + +:root { + --menu-background: var(--el-bg-color-page); + --menu-text: #68758e; + --menu-active-text: var(--el-menu-active-color); + --menu-hover: #263445; + --sidebar-logo-background: #fff; +} + +/** 暗黑主题 */ +html.dark { + --menu-background: var(--el-bg-color-overlay); + --menu-text: #fff; + --menu-active-text: var(--el-menu-active-color); + --menu-hover: rgb(0 0 0 / 20%); + --sidebar-logo-background: rgb(0 0 0 / 20%); +} + +$menu-background: var(--menu-background); // 菜单背景色 +$menu-text: var(--menu-text); // 菜单文字颜色 +$menu-active-text: var(--menu-active-text); // 菜单激活文字颜色 +$menu-hover: var(--menu-hover); // 菜单悬停背景色 +$sidebar-logo-background: var(--el-bg-color-page); // 侧边栏 Logo 背景色 + +$sidebar-width: 210px; // 侧边栏宽度 +$sidebar-width-collapsed: 54px; // 侧边栏收缩宽度 +$navbar-height: 50px; // 导航栏高度 +$tags-view-height: 34px; // TagsView 高度 diff --git a/src/typings/auto-imports.d.ts b/src/typings/auto-imports.d.ts new file mode 100644 index 0000000..21e11af --- /dev/null +++ b/src/typings/auto-imports.d.ts @@ -0,0 +1,1783 @@ +/* eslint-disable */ +/* prettier-ignore */ +// @ts-nocheck +// noinspection JSUnusedGlobalSymbols +// Generated by unplugin-auto-import +export {} +declare global { + const EffectScope: (typeof import("vue"))["EffectScope"]; + const ElForm: (typeof import("element-plus/es"))["ElForm"]; + const ElMessage: (typeof import("element-plus/es"))["ElMessage"]; + const ElMessageBox: (typeof import("element-plus/es"))["ElMessageBox"]; + const ElTree: (typeof import("element-plus/es"))["ElTree"]; + 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 createReusableTemplate: (typeof import("@vueuse/core"))["createReusableTemplate"]; + const createSharedComposable: (typeof import("@vueuse/core"))["createSharedComposable"]; + const createTemplatePromise: (typeof import("@vueuse/core"))["createTemplatePromise"]; + 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 injectLocal: (typeof import("@vueuse/core"))["injectLocal"]; + 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 provideLocal: (typeof import("@vueuse/core"))["provideLocal"]; + 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 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 toValue: (typeof import("vue"))["toValue"]; + 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 useAnimate: (typeof import("@vueuse/core"))["useAnimate"]; + const useArrayDifference: (typeof import("@vueuse/core"))["useArrayDifference"]; + 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 useArrayFindLast: (typeof import("@vueuse/core"))["useArrayFindLast"]; + const useArrayIncludes: (typeof import("@vueuse/core"))["useArrayIncludes"]; + 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 useArrayUnique: (typeof import("@vueuse/core"))["useArrayUnique"]; + 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 useClipboardItems: (typeof import("@vueuse/core"))["useClipboardItems"]; + 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 useI18n: (typeof import("vue-i18n"))["useI18n"]; + 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 useParentElement: (typeof import("@vueuse/core"))["useParentElement"]; + const usePerformanceObserver: (typeof import("@vueuse/core"))["usePerformanceObserver"]; + const usePermission: (typeof import("@vueuse/core"))["usePermission"]; + const usePointer: (typeof import("@vueuse/core"))["usePointer"]; + const usePointerLock: (typeof import("@vueuse/core"))["usePointerLock"]; + 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 usePrevious: (typeof import("@vueuse/core"))["usePrevious"]; + 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 watchDeep: (typeof import("@vueuse/core"))["watchDeep"]; + const watchEffect: (typeof import("vue"))["watchEffect"]; + const watchIgnorable: (typeof import("@vueuse/core"))["watchIgnorable"]; + const watchImmediate: (typeof import("@vueuse/core"))["watchImmediate"]; + 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 type re-export +declare global { + // @ts-ignore + export type { + Component, + ComponentPublicInstance, + ComputedRef, + ExtractDefaultPropTypes, + ExtractPropTypes, + ExtractPublicPropTypes, + InjectionKey, + PropType, + Ref, + VNode, + WritableComputedRef, + } from "vue"; + import("vue"); +} +// for vue template auto import +import { UnwrapRef } from "vue"; +declare module "vue" { + interface GlobalComponents {} + interface ComponentCustomProperties { + readonly EffectScope: UnwrapRef<(typeof import("vue"))["EffectScope"]>; + readonly ElForm: UnwrapRef<(typeof import("element-plus/es"))["ElForm"]>; + readonly ElMessage: UnwrapRef< + (typeof import("element-plus/es"))["ElMessage"] + >; + readonly ElMessageBox: UnwrapRef< + (typeof import("element-plus/es"))["ElMessageBox"] + >; + readonly ElTree: UnwrapRef<(typeof import("element-plus/es"))["ElTree"]>; + 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 createReusableTemplate: UnwrapRef< + (typeof import("@vueuse/core"))["createReusableTemplate"] + >; + readonly createSharedComposable: UnwrapRef< + (typeof import("@vueuse/core"))["createSharedComposable"] + >; + readonly createTemplatePromise: UnwrapRef< + (typeof import("@vueuse/core"))["createTemplatePromise"] + >; + 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 injectLocal: UnwrapRef< + (typeof import("@vueuse/core"))["injectLocal"] + >; + 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 provideLocal: UnwrapRef< + (typeof import("@vueuse/core"))["provideLocal"] + >; + 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 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 toValue: UnwrapRef<(typeof import("vue"))["toValue"]>; + 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 useAnimate: UnwrapRef< + (typeof import("@vueuse/core"))["useAnimate"] + >; + readonly useArrayDifference: UnwrapRef< + (typeof import("@vueuse/core"))["useArrayDifference"] + >; + 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 useArrayFindLast: UnwrapRef< + (typeof import("@vueuse/core"))["useArrayFindLast"] + >; + readonly useArrayIncludes: UnwrapRef< + (typeof import("@vueuse/core"))["useArrayIncludes"] + >; + 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 useArrayUnique: UnwrapRef< + (typeof import("@vueuse/core"))["useArrayUnique"] + >; + 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 useClipboardItems: UnwrapRef< + (typeof import("@vueuse/core"))["useClipboardItems"] + >; + 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 useI18n: UnwrapRef<(typeof import("vue-i18n"))["useI18n"]>; + 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 useParentElement: UnwrapRef< + (typeof import("@vueuse/core"))["useParentElement"] + >; + readonly usePerformanceObserver: UnwrapRef< + (typeof import("@vueuse/core"))["usePerformanceObserver"] + >; + readonly usePermission: UnwrapRef< + (typeof import("@vueuse/core"))["usePermission"] + >; + readonly usePointer: UnwrapRef< + (typeof import("@vueuse/core"))["usePointer"] + >; + readonly usePointerLock: UnwrapRef< + (typeof import("@vueuse/core"))["usePointerLock"] + >; + 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 usePrevious: UnwrapRef< + (typeof import("@vueuse/core"))["usePrevious"] + >; + 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 watchDeep: UnwrapRef<(typeof import("@vueuse/core"))["watchDeep"]>; + readonly watchEffect: UnwrapRef<(typeof import("vue"))["watchEffect"]>; + readonly watchIgnorable: UnwrapRef< + (typeof import("@vueuse/core"))["watchIgnorable"] + >; + readonly watchImmediate: UnwrapRef< + (typeof import("@vueuse/core"))["watchImmediate"] + >; + 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"]>; + } +} +declare module "@vue/runtime-core" { + interface GlobalComponents {} + interface ComponentCustomProperties { + readonly EffectScope: UnwrapRef<(typeof import("vue"))["EffectScope"]>; + readonly ElForm: UnwrapRef<(typeof import("element-plus/es"))["ElForm"]>; + readonly ElMessage: UnwrapRef< + (typeof import("element-plus/es"))["ElMessage"] + >; + readonly ElMessageBox: UnwrapRef< + (typeof import("element-plus/es"))["ElMessageBox"] + >; + readonly ElTree: UnwrapRef<(typeof import("element-plus/es"))["ElTree"]>; + 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 createReusableTemplate: UnwrapRef< + (typeof import("@vueuse/core"))["createReusableTemplate"] + >; + readonly createSharedComposable: UnwrapRef< + (typeof import("@vueuse/core"))["createSharedComposable"] + >; + readonly createTemplatePromise: UnwrapRef< + (typeof import("@vueuse/core"))["createTemplatePromise"] + >; + 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 injectLocal: UnwrapRef< + (typeof import("@vueuse/core"))["injectLocal"] + >; + 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 provideLocal: UnwrapRef< + (typeof import("@vueuse/core"))["provideLocal"] + >; + 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 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 toValue: UnwrapRef<(typeof import("vue"))["toValue"]>; + 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 useAnimate: UnwrapRef< + (typeof import("@vueuse/core"))["useAnimate"] + >; + readonly useArrayDifference: UnwrapRef< + (typeof import("@vueuse/core"))["useArrayDifference"] + >; + 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 useArrayFindLast: UnwrapRef< + (typeof import("@vueuse/core"))["useArrayFindLast"] + >; + readonly useArrayIncludes: UnwrapRef< + (typeof import("@vueuse/core"))["useArrayIncludes"] + >; + 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 useArrayUnique: UnwrapRef< + (typeof import("@vueuse/core"))["useArrayUnique"] + >; + 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 useClipboardItems: UnwrapRef< + (typeof import("@vueuse/core"))["useClipboardItems"] + >; + 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 useI18n: UnwrapRef<(typeof import("vue-i18n"))["useI18n"]>; + 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 useParentElement: UnwrapRef< + (typeof import("@vueuse/core"))["useParentElement"] + >; + readonly usePerformanceObserver: UnwrapRef< + (typeof import("@vueuse/core"))["usePerformanceObserver"] + >; + readonly usePermission: UnwrapRef< + (typeof import("@vueuse/core"))["usePermission"] + >; + readonly usePointer: UnwrapRef< + (typeof import("@vueuse/core"))["usePointer"] + >; + readonly usePointerLock: UnwrapRef< + (typeof import("@vueuse/core"))["usePointerLock"] + >; + 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 usePrevious: UnwrapRef< + (typeof import("@vueuse/core"))["usePrevious"] + >; + 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 watchDeep: UnwrapRef<(typeof import("@vueuse/core"))["watchDeep"]>; + readonly watchEffect: UnwrapRef<(typeof import("vue"))["watchEffect"]>; + readonly watchIgnorable: UnwrapRef< + (typeof import("@vueuse/core"))["watchIgnorable"] + >; + readonly watchImmediate: UnwrapRef< + (typeof import("@vueuse/core"))["watchImmediate"] + >; + 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/typings/components.d.ts b/src/typings/components.d.ts new file mode 100644 index 0000000..7827c3c --- /dev/null +++ b/src/typings/components.d.ts @@ -0,0 +1,115 @@ +/* eslint-disable */ +/* prettier-ignore */ +// @ts-nocheck +// Generated by unplugin-vue-components +// Read more: https://github.com/vuejs/core/pull/3399 +export {} + +declare module "vue" { + export interface GlobalComponents { + AppLink: (typeof import("./../components/AppLink/index.vue"))["default"]; + AppMain: (typeof import("./../layout/components/AppMain/index.vue"))["default"]; + BarChart: (typeof import("./../views/dashboard/components/BarChart.vue"))["default"]; + Breadcrumb: (typeof import("./../components/Breadcrumb/index.vue"))["default"]; + DeptTree: (typeof import("./../views/system/user/components/dept-tree.vue"))["default"]; + Dictionary: (typeof import("./../components/Dictionary/index.vue"))["default"]; + DictItem: (typeof import("./../views/system/dict/components/dict-item.vue"))["default"]; + ElAlert: (typeof import("element-plus/es"))["ElAlert"]; + 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"]; + ElCol: (typeof import("element-plus/es"))["ElCol"]; + ElColorPicker: (typeof import("element-plus/es"))["ElColorPicker"]; + ElConfigProvider: (typeof import("element-plus/es"))["ElConfigProvider"]; + 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"]; + ElStatistic: (typeof import("element-plus/es"))["ElStatistic"]; + 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"]; + ElText: (typeof import("element-plus/es"))["ElText"]; + ElTooltip: (typeof import("element-plus/es"))["ElTooltip"]; + ElTree: (typeof import("element-plus/es"))["ElTree"]; + ElTreeSelect: (typeof import("element-plus/es"))["ElTreeSelect"]; + ElUpload: (typeof import("element-plus/es"))["ElUpload"]; + ElWatermark: (typeof import("element-plus/es"))["ElWatermark"]; + FunnelChart: (typeof import("./../views/dashboard/components/FunnelChart.vue"))["default"]; + GithubCorner: (typeof import("./../components/GithubCorner/index.vue"))["default"]; + Hamburger: (typeof import("./../components/Hamburger/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"]; + IEpClose: (typeof import("~icons/ep/close"))["default"]; + IEpCollection: (typeof import("~icons/ep/collection"))["default"]; + IEpDelete: (typeof import("~icons/ep/delete"))["default"]; + IEpDownload: (typeof import("~icons/ep/download"))["default"]; + IEpEdit: (typeof import("~icons/ep/edit"))["default"]; + IEpPlus: (typeof import("~icons/ep/plus"))["default"]; + IEpPosition: (typeof import("~icons/ep/position"))["default"]; + IEpQuestionFilled: (typeof import("~icons/ep/question-filled"))["default"]; + IEpRefresh: (typeof import("~icons/ep/refresh"))["default"]; + IEpRefreshLeft: (typeof import("~icons/ep/refresh-left"))["default"]; + IEpSearch: (typeof import("~icons/ep/search"))["default"]; + IEpSetting: (typeof import("~icons/ep/setting"))["default"]; + IEpTop: (typeof import("~icons/ep/top"))["default"]; + IEpUploadFilled: (typeof import("~icons/ep/upload-filled"))["default"]; + LangSelect: (typeof import("./../components/LangSelect/index.vue"))["default"]; + LayoutSelect: (typeof import("./../layout/components/Settings/components/LayoutSelect.vue"))["default"]; + MultiUpload: (typeof import("./../components/Upload/MultiUpload.vue"))["default"]; + NavBar: (typeof import("./../layout/components/NavBar/index.vue"))["default"]; + NavbarLeft: (typeof import("./../layout/components/NavBar/components/NavbarLeft.vue"))["default"]; + NavbarRight: (typeof import("./../layout/components/NavBar/components/NavbarRight.vue"))["default"]; + Pagination: (typeof import("./../components/Pagination/index.vue"))["default"]; + PieChart: (typeof import("./../views/dashboard/components/PieChart.vue"))["default"]; + RadarChart: (typeof import("./../views/dashboard/components/RadarChart.vue"))["default"]; + RightPanel: (typeof import("./../components/RightPanel/index.vue"))["default"]; + RouterLink: (typeof import("vue-router"))["RouterLink"]; + RouterView: (typeof import("vue-router"))["RouterView"]; + Settings: (typeof import("./../layout/components/Settings/index.vue"))["default"]; + Sidebar: (typeof import("./../layout/components/Sidebar/index.vue"))["default"]; + SidebarLogo: (typeof import("./../layout/components/Sidebar/components/SidebarLogo.vue"))["default"]; + SidebarMenu: (typeof import("./../layout/components/Sidebar/components/SidebarMenu.vue"))["default"]; + SidebarMenuItem: (typeof import("./../layout/components/Sidebar/components/SidebarMenuItem.vue"))["default"]; + SidebarMenuItemTitle: (typeof import("./../layout/components/Sidebar/components/SidebarMenuItemTitle.vue"))["default"]; + SidebarMixTopMenu: (typeof import("./../layout/components/Sidebar/components/SidebarMixTopMenu.vue"))["default"]; + SingleUpload: (typeof import("./../components/Upload/SingleUpload.vue"))["default"]; + SizeSelect: (typeof import("./../components/SizeSelect/index.vue"))["default"]; + SvgIcon: (typeof import("./../components/SvgIcon/index.vue"))["default"]; + TagsView: (typeof import("./../layout/components/TagsView/index.vue"))["default"]; + ThemeColorPicker: (typeof import("./../layout/components/Settings/components/ThemeColorPicker.vue"))["default"]; + WangEditor: (typeof import("./../components/WangEditor/index.vue"))["default"]; + TableBar: (typeof import("./../components/Table/TableBar.vue"))["default"]; + AnyTable: (typeof import("./../components/Table/AnyTable.vue"))["default"]; + + } + export interface ComponentCustomProperties { + vLoading: (typeof import("element-plus/es"))["ElLoadingDirective"]; + } +} diff --git a/src/typings/env.d.ts b/src/typings/env.d.ts new file mode 100644 index 0000000..f8a60fb --- /dev/null +++ b/src/typings/env.d.ts @@ -0,0 +1,36 @@ +/// + +declare module "*.vue" { + import { DefineComponent } from "vue"; + // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types + const component: DefineComponent<{}, {}, any>; + export default component; +} + +interface ImportMetaEnv { + /** 应用端口 */ + VITE_APP_PORT: string; + /** API 基础路径 */ + VITE_APP_BASE_API: string; + VITE_APP_API_URL: string; +} + +interface ImportMeta { + readonly env: ImportMetaEnv; +} + +/** + * 平台的名称、版本、运行所需的`node`版本、依赖、构建时间的类型提示 + */ +declare const __APP_INFO__: { + pkg: { + name: string; + version: string; + engines: { + node: string; + }; + dependencies: Record; + devDependencies: Record; + }; + buildTimestamp: number; +}; diff --git a/src/typings/global.d.ts b/src/typings/global.d.ts new file mode 100644 index 0000000..9889628 --- /dev/null +++ b/src/typings/global.d.ts @@ -0,0 +1,96 @@ +declare global { + /** + * 分页查询参数 + */ + interface PageQuery { + pageNum: number; + pageSize: number; + } + + /** + * 分页响应对象 + */ + interface PageResult { + /** 数据列表 */ + list: T; + /** 总数 */ + total: number; + } + + /** + * 页签对象 + */ + interface TagView { + /** 页签名称 */ + name: string; + /** 页签标题 */ + title: string; + /** 页签路由路径 */ + path: string; + /** 页签路由完整路径 */ + fullPath: string; + /** 页签图标 */ + icon?: string; + /** 是否固定页签 */ + affix?: boolean; + /** 是否开启缓存 */ + keepAlive?: boolean; + /** 路由查询参数 */ + query?: any; + } + + /** + * 系统设置 + */ + interface AppSettings { + /** 系统标题 */ + title: string; + /** 系统版本 */ + version: string; + /** 是否显示设置 */ + showSettings: boolean; + /** 是否固定头部 */ + fixedHeader: boolean; + /** 是否显示多标签导航 */ + tagsView: boolean; + /** 是否显示侧边栏Logo */ + sidebarLogo: boolean; + /** 导航栏布局(left|top|mix) */ + layout: string; + /** 主题颜色 */ + themeColor: string; + /** 主题模式(dark|light) */ + theme: string; + /** 布局大小(default |large |small) */ + size: string; + /** 语言( zh-cn| en) */ + language: string; + /** 是否开启水印 */ + watermarkEnabled: boolean; + /** 水印内容 */ + watermarkContent: string; + } + + /** + * 组件数据源 + */ + interface OptionType { + /** 值 */ + value: string | number; + /** 文本 */ + label: string; + /** 子列表 */ + children?: OptionType[]; + } + + /** + * 表格列 + */ + interface TableColumns { + /** 列名称 */ + name?: string; + /** 是否显示 */ + show?: boolean; + } +} +export {}; diff --git a/src/typings/router.d.ts b/src/typings/router.d.ts new file mode 100644 index 0000000..99e187b --- /dev/null +++ b/src/typings/router.d.ts @@ -0,0 +1,22 @@ +import "vue-router"; + +declare module "vue-router" { + // https://router.vuejs.org/zh/guide/advanced/meta.html#typescript + // 可以通过扩展 RouteMeta 接口来输入 meta 字段 + interface RouteMeta { + /** 菜单名称 */ + title?: string; + /** 菜单图标 */ + icon?: string; + /** 菜单是否隐藏 */ + hidden?: boolean; + /** 是否固定页签 */ + affix?: boolean; + /** 是否缓存页面 */ + keepAlive?: boolean; + /** 是否在面包屑上隐藏 */ + breadcrumb?: boolean; + /** 拥有菜单权限的角色编码集合 */ + roles?: string[]; + } +} diff --git a/src/typings/shims-vue.d.ts b/src/typings/shims-vue.d.ts new file mode 100644 index 0000000..abbc931 --- /dev/null +++ b/src/typings/shims-vue.d.ts @@ -0,0 +1 @@ +declare module "xlsx/xlsx.mjs"; diff --git a/src/utils/color.ts b/src/utils/color.ts new file mode 100644 index 0000000..2503584 --- /dev/null +++ b/src/utils/color.ts @@ -0,0 +1,287 @@ +/** + * 颜色生成 + */ +type RGB = { + r: number; + g: number; + b: number; +}; +type HSL = { + h: number; + s: number; + l: number; +}; +type HEX = + | "0" + | "1" + | "2" + | "3" + | "4" + | "5" + | "6" + | "7" + | "8" + | "9" + | "A" + | "B" + | "C" + | "D" + | "E" + | "F"; + +const RGBUnit = 255; +const HEX_MAP: Record = { + 0: 0, + 1: 1, + 2: 2, + 3: 3, + 4: 4, + 5: 5, + 6: 6, + 7: 7, + 8: 8, + 9: 9, + A: 10, + B: 11, + C: 12, + D: 13, + E: 14, + F: 15, +}; +const rgbWhite = { + r: 255, + g: 255, + b: 255, +}; +const rgbBlack = { + r: 0, + g: 0, + b: 0, +}; + +/** + * RGB颜色转HSL颜色值 + * @param r 红色值 + * @param g 绿色值 + * @param b 蓝色值 + * @returns { h: [0, 360]; s: [0, 1]; l: [0, 1] } + */ +function rgbToHsl(rgb: RGB): HSL { + let { r, g, b } = rgb; + const hsl = { + h: 0, + s: 0, + l: 0, + }; + + // 计算rgb基数 ∈ [0, 1] + r /= RGBUnit; + g /= RGBUnit; + b /= RGBUnit; + const max = Math.max(r, g, b); + const min = Math.min(r, g, b); + + // 计算h + if (max === min) { + hsl.h = 0; + } else if (max === r) { + hsl.h = 60 * ((g - b) / (max - min)) + (g >= b ? 0 : 360); + } else if (max === g) { + hsl.h = 60 * ((b - r) / (max - min)) + 120; + } else { + hsl.h = 60 * ((r - g) / (max - min)) + 240; + } + hsl.h = hsl.h > 360 ? hsl.h - 360 : hsl.h; + + // 计算l + hsl.l = (max + min) / 2; + + // 计算s + if (hsl.l === 0 || max === min) { + // 灰/白/黑 + hsl.s = 0; + } else if (hsl.l > 0 && hsl.l <= 0.5) { + hsl.s = (max - min) / (max + min); + } else { + hsl.s = (max - min) / (2 - (max + min)); + } + + return hsl; +} + +/** + * hsl -> rgb + * @param h [0, 360] + * @param s [0, 1] + * @param l [0, 1] + * @returns RGB + */ +function hslToRgb(hsl: HSL): RGB { + const { h, s, l } = hsl; + const q = l < 0.5 ? l * (1 + s) : l + s - l * s; + const p = 2 * l - q; + const hUnit = h / 360; // 色相转换为 [0, 1] + + const Cr = fillCircleVal(hUnit + 1 / 3); + const Cg = fillCircleVal(hUnit); + const Cb = fillCircleVal(hUnit - 1 / 3); + + // 保持 [0, 1] 环状取值 + function fillCircleVal(val: number): number { + return val < 0 ? val + 1 : val > 1 ? val - 1 : val; + } + + function computedRgb(val: number): number { + let colorVal: number; + if (val < 1 / 6) { + colorVal = p + (q - p) * 6 * val; + } else if (val >= 1 / 6 && val < 1 / 2) { + colorVal = q; + } else if (val >= 1 / 2 && val < 2 / 3) { + colorVal = p + (q - p) * 6 * (2 / 3 - val); + } else { + colorVal = p; + } + return colorVal * 255; + } + + return { + r: Number(computedRgb(Cr).toFixed(0)), + g: Number(computedRgb(Cg).toFixed(0)), + b: Number(computedRgb(Cb).toFixed(0)), + }; +} + +/** + * 16进制颜色转换RGB + * @param color #rrggbb + * @returns RGB + */ +function hexToRGB(hex: string): RGB { + hex = hex.toUpperCase(); + const hexRegExp = /^#([0-9A-F]{6})$/; + if (!hexRegExp.test(hex)) { + throw new Error("请传入合法的16进制颜色值,eg: #FF0000"); + } + + const hexValArr = (hexRegExp.exec(hex)?.[1] || "000000").split( + "" + ) as Array; + + return { + r: HEX_MAP[hexValArr[0]] * 16 + HEX_MAP[hexValArr[1]], + g: HEX_MAP[hexValArr[2]] * 16 + HEX_MAP[hexValArr[3]], + b: HEX_MAP[hexValArr[4]] * 16 + HEX_MAP[hexValArr[5]], + }; +} + +/** + * rgb 转 16进制 + * @param rgb RGB + * @returns #HEX{6} + */ +function rgbToHex(rgb: RGB): string { + const HEX_MAP_REVERSE: Record = {}; + for (const key in HEX_MAP) { + HEX_MAP_REVERSE[HEX_MAP[key as HEX]] = key as HEX; + } + function getRemainderAndQuotient(val: number): string { + val = Math.round(val); + return `${HEX_MAP_REVERSE[Math.floor(val / 16)]}${ + HEX_MAP_REVERSE[val % 16] + }`; + } + + return `#${getRemainderAndQuotient(rgb.r)}${getRemainderAndQuotient( + rgb.g + )}${getRemainderAndQuotient(rgb.b)}`; +} + +// hsl 转 16进制 +function hslToHex(hsl: HSL): string { + return rgbToHex(hslToRgb(hsl)); +} + +// 16进制 转 hsl +function hexToHsl(hex: string): HSL { + return rgbToHsl(hexToRGB(hex)); +} + +// 生成混合色(混黑 + 混白) +function genMixColor(base: string | RGB | HSL): { + DEFAULT: string; + dark: { + 1: string; + 2: string; + 3: string; + 4: string; + 5: string; + 6: string; + 7: string; + 8: string; + 9: string; + }; + light: { + 1: string; + 2: string; + 3: string; + 4: string; + 5: string; + 6: string; + 7: string; + 8: string; + 9: string; + }; +} { + // 基准色统一转换为RGB + if (typeof base === "string") { + base = hexToRGB(base); + } else if ("h" in base) { + base = hslToRgb(base); + } + + // 混合色 + function mix(color: RGB, mixColor: RGB, weight: number): RGB { + return { + r: color.r * (1 - weight) + mixColor.r * weight, + g: color.g * (1 - weight) + mixColor.g * weight, + b: color.b * (1 - weight) + mixColor.b * weight, + }; + } + + return { + DEFAULT: rgbToHex(base), + dark: { + 1: rgbToHex(mix(base, rgbBlack, 0.1)), + 2: rgbToHex(mix(base, rgbBlack, 0.2)), + 3: rgbToHex(mix(base, rgbBlack, 0.3)), + 4: rgbToHex(mix(base, rgbBlack, 0.4)), + 5: rgbToHex(mix(base, rgbBlack, 0.5)), + 6: rgbToHex(mix(base, rgbBlack, 0.6)), + 7: rgbToHex(mix(base, rgbBlack, 0.7)), + 8: rgbToHex(mix(base, rgbBlack, 0.78)), + 9: rgbToHex(mix(base, rgbBlack, 0.85)), + }, + light: { + 1: rgbToHex(mix(base, rgbWhite, 0.1)), + 2: rgbToHex(mix(base, rgbWhite, 0.2)), + 3: rgbToHex(mix(base, rgbWhite, 0.3)), + 4: rgbToHex(mix(base, rgbWhite, 0.4)), + 5: rgbToHex(mix(base, rgbWhite, 0.5)), + 6: rgbToHex(mix(base, rgbWhite, 0.6)), + 7: rgbToHex(mix(base, rgbWhite, 0.7)), + 8: rgbToHex(mix(base, rgbWhite, 0.78)), + 9: rgbToHex(mix(base, rgbWhite, 0.85)), + }, + }; +} + +export { + genMixColor, + rgbToHsl, + rgbToHex, + hslToRgb, + hslToHex, + hexToRGB, + hexToHsl, +}; diff --git a/src/utils/i18n.ts b/src/utils/i18n.ts new file mode 100644 index 0000000..17ed904 --- /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 function translateRouteTitle(title: any) { + // 判断是否存在国际化配置,如果没有原生返回 + 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.ts b/src/utils/index.ts new file mode 100644 index 0000000..f8fd449 --- /dev/null +++ b/src/utils/index.ts @@ -0,0 +1,80 @@ +import { AxiosResponse } from "axios"; + +/** + * Check if an element has a class + * @param {HTMLElement} ele + * @param {string} cls + * @returns {boolean} + */ +export function hasClass(ele: HTMLElement, cls: string) { + return !!ele.className.match(new RegExp("(\\s|^)" + cls + "(\\s|$)")); +} + +/** + * Add class to element + * @param {HTMLElement} ele + * @param {string} cls + */ +export function addClass(ele: HTMLElement, cls: string) { + if (!hasClass(ele, cls)) ele.className += " " + cls; +} + +/** + * Remove class from element + * @param {HTMLElement} ele + * @param {string} cls + */ +export function 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 function isExternal(path: string) { + const isExternal = /^(https?:|http?:|mailto:|tel:)/.test(path); + return isExternal; +} + +/** + * 设置Style属性 + * + * @param propName + * @param value + */ +export function setStyleProperty(propName: string, value: string) { + document.documentElement.style.setProperty(propName, value); +} + +export function downloadHook(res: AxiosResponse) { + const blob = new Blob([res.data]); + const contentDisposition = res.headers["content-disposition"]; + const pattern = new RegExp("filename=([^;]+\\.[^.;]+);*"); + const result = pattern.exec(contentDisposition); + // 使用decodeURI对名字进行解码 + let fileName = ""; + if (result) { + fileName = decodeURI(result[1]); + console.log(fileName); + } + const downloadElement = document.createElement("a"); + // 创建下载的链接 + const href = window.URL.createObjectURL(blob); + downloadElement.style.display = "none"; + downloadElement.href = href; + // 下载后文件名 + downloadElement.download = fileName; + document.body.appendChild(downloadElement); + // 点击下载 + downloadElement.click(); + // 下载完成移除元素 + document.body.removeChild(downloadElement); + // 释放掉blob对象 + window.URL.revokeObjectURL(href); +} diff --git a/src/utils/nprogress.ts b/src/utils/nprogress.ts new file mode 100644 index 0000000..c1d5f23 --- /dev/null +++ b/src/utils/nprogress.ts @@ -0,0 +1,18 @@ +import NProgress from "nprogress"; +import "nprogress/nprogress.css"; + +// 进度条 +NProgress.configure({ + // 动画方式 + easing: "ease", + // 递增进度条的速度 + speed: 500, + // 是否显示加载ico + showSpinner: false, + // 自动递增间隔 + trickleSpeed: 200, + // 初始化时的最小百分比 + minimum: 0.3, +}); + +export default NProgress; diff --git a/src/utils/request.ts b/src/utils/request.ts new file mode 100644 index 0000000..fe1c6b8 --- /dev/null +++ b/src/utils/request.ts @@ -0,0 +1,66 @@ +import axios, { InternalAxiosRequestConfig, AxiosResponse } from "axios"; +import { useUserStoreHook } from "@/store/modules/user"; + +// 创建 axios 实例 +const service = axios.create({ + baseURL: import.meta.env.VITE_APP_BASE_API, + timeout: 50000, + headers: { "Content-Type": "application/json;charset=utf-8" }, +}); + +// 请求拦截器 +service.interceptors.request.use( + (config: InternalAxiosRequestConfig) => { + const accessToken = localStorage.getItem("token"); + if (accessToken) { + config.headers.Authorization = accessToken; + } + return config; + }, + (error: any) => { + return Promise.reject(error); + } +); + +// 响应拦截器 +service.interceptors.response.use( + (response: AxiosResponse) => { + const { code, msg } = response.data; + if (code === "00000") { + return response.data; + } + // 响应数据为二进制流处理(Excel导出) + if (response.data instanceof ArrayBuffer) { + return response; + } + if (response.data instanceof Blob) { + return response; + } + ElMessage.error(msg || "系统出错"); + return Promise.reject(new Error(msg || "Error")); + }, + (error: any) => { + if (error.response.data) { + const { code, msg } = error.response.data; + // token 过期,重新登录 + if (code === "A0230") { + ElMessageBox.confirm("当前页面已失效,请重新登录", "提示", { + confirmButtonText: "确定", + cancelButtonText: "取消", + type: "warning", + }).then(() => { + const userStore = useUserStoreHook(); + userStore.resetToken().then(() => { + location.reload(); + }); + }); + } else { + ElMessage.error(msg || "系统出错"); + } + } + return Promise.reject(error.message); + } +); + +// 导出 axios 实例 +export default service; diff --git a/src/views/dashboard/index.vue b/src/views/dashboard/index.vue new file mode 100644 index 0000000..3e82a35 --- /dev/null +++ b/src/views/dashboard/index.vue @@ -0,0 +1,90 @@ + + + + + diff --git a/src/views/error-page/401.vue b/src/views/error-page/401.vue new file mode 100644 index 0000000..79b11aa --- /dev/null +++ b/src/views/error-page/401.vue @@ -0,0 +1,113 @@ + + + + + diff --git a/src/views/error-page/404.vue b/src/views/error-page/404.vue new file mode 100644 index 0000000..4e68995 --- /dev/null +++ b/src/views/error-page/404.vue @@ -0,0 +1,276 @@ + + + + + + + + diff --git a/src/views/login/index.vue b/src/views/login/index.vue new file mode 100644 index 0000000..7550599 --- /dev/null +++ b/src/views/login/index.vue @@ -0,0 +1,282 @@ + + + + + diff --git a/src/views/redirect/index.vue b/src/views/redirect/index.vue new file mode 100644 index 0000000..4215bf6 --- /dev/null +++ b/src/views/redirect/index.vue @@ -0,0 +1,15 @@ + + + diff --git a/src/views/resources/device-type/components/DeviceTypeToVer.vue b/src/views/resources/device-type/components/DeviceTypeToVer.vue new file mode 100644 index 0000000..9fc4773 --- /dev/null +++ b/src/views/resources/device-type/components/DeviceTypeToVer.vue @@ -0,0 +1,347 @@ + + + + + + diff --git a/src/views/resources/device-type/components/DeviceTypeVersionEdit.vue b/src/views/resources/device-type/components/DeviceTypeVersionEdit.vue new file mode 100644 index 0000000..d30c95b --- /dev/null +++ b/src/views/resources/device-type/components/DeviceTypeVersionEdit.vue @@ -0,0 +1,531 @@ + + + + + + diff --git a/src/views/resources/device-type/index.vue b/src/views/resources/device-type/index.vue new file mode 100644 index 0000000..920ab43 --- /dev/null +++ b/src/views/resources/device-type/index.vue @@ -0,0 +1,212 @@ + + + + + diff --git a/src/views/resources/file-list/components/FolderTree.vue b/src/views/resources/file-list/components/FolderTree.vue new file mode 100644 index 0000000..a804df7 --- /dev/null +++ b/src/views/resources/file-list/components/FolderTree.vue @@ -0,0 +1,92 @@ + + + + + + diff --git a/src/views/resources/file-list/index.vue b/src/views/resources/file-list/index.vue new file mode 100644 index 0000000..759fce5 --- /dev/null +++ b/src/views/resources/file-list/index.vue @@ -0,0 +1,124 @@ + + + + + diff --git a/src/views/resources/file-server/components/FileServerForm.vue b/src/views/resources/file-server/components/FileServerForm.vue new file mode 100644 index 0000000..74605f5 --- /dev/null +++ b/src/views/resources/file-server/components/FileServerForm.vue @@ -0,0 +1,153 @@ + + + + + diff --git a/src/views/resources/file-server/index.vue b/src/views/resources/file-server/index.vue new file mode 100644 index 0000000..2cdd9e7 --- /dev/null +++ b/src/views/resources/file-server/index.vue @@ -0,0 +1,108 @@ + + + + + + diff --git a/src/views/system/dept/index.vue b/src/views/system/dept/index.vue new file mode 100644 index 0000000..78f329d --- /dev/null +++ b/src/views/system/dept/index.vue @@ -0,0 +1,335 @@ + + + + + diff --git a/src/views/system/dict/components/dict-item.vue b/src/views/system/dict/components/dict-item.vue new file mode 100644 index 0000000..59c03c2 --- /dev/null +++ b/src/views/system/dict/components/dict-item.vue @@ -0,0 +1,341 @@ + + + + + diff --git a/src/views/system/dict/index.vue b/src/views/system/dict/index.vue new file mode 100644 index 0000000..00bf327 --- /dev/null +++ b/src/views/system/dict/index.vue @@ -0,0 +1,349 @@ + + + + + diff --git a/src/views/system/domain/components/DomainForm.vue b/src/views/system/domain/components/DomainForm.vue new file mode 100644 index 0000000..f98f4ff --- /dev/null +++ b/src/views/system/domain/components/DomainForm.vue @@ -0,0 +1,155 @@ + + + + + diff --git a/src/views/system/domain/index.vue b/src/views/system/domain/index.vue new file mode 100644 index 0000000..b6089d2 --- /dev/null +++ b/src/views/system/domain/index.vue @@ -0,0 +1,127 @@ + + + + + diff --git a/src/views/system/menu/index.vue b/src/views/system/menu/index.vue new file mode 100644 index 0000000..48fd933 --- /dev/null +++ b/src/views/system/menu/index.vue @@ -0,0 +1,524 @@ + + + + diff --git a/src/views/system/role/index.vue b/src/views/system/role/index.vue new file mode 100644 index 0000000..6780e8b --- /dev/null +++ b/src/views/system/role/index.vue @@ -0,0 +1,418 @@ + + + + diff --git a/src/views/system/user/components/dept-tree.vue b/src/views/system/user/components/dept-tree.vue new file mode 100644 index 0000000..6394b32 --- /dev/null +++ b/src/views/system/user/components/dept-tree.vue @@ -0,0 +1,69 @@ + + + + diff --git a/src/views/system/user/index.vue b/src/views/system/user/index.vue new file mode 100644 index 0000000..03b5846 --- /dev/null +++ b/src/views/system/user/index.vue @@ -0,0 +1,727 @@ + + + + + diff --git a/src/views/table/index.vue b/src/views/table/index.vue new file mode 100644 index 0000000..feb396e --- /dev/null +++ b/src/views/table/index.vue @@ -0,0 +1,44 @@ + + + + + diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..6d1e993 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,35 @@ +{ + "compilerOptions": { + "target": "esnext", + "useDefineForClassFields": true, + "module": "esnext", + "moduleResolution": "node", + "strict": true, + "noLib": false, + "sourceMap": true, + "resolveJsonModule": true, + "esModuleInterop": true, + "lib": ["esnext", "dom"], + "baseUrl": ".", + "allowJs": true, + "paths": { + "@/*": ["src/*"] + }, + "types": ["vite/client", "unplugin-icons/types/vue", "element-plus/global"], + "skipLibCheck": true /* Skip type checking all .d.ts files. */, + "allowSyntheticDefaultImports": true /* 允许默认导入 */, + "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, + + "jsx": "preserve", + "jsxFactory": "h", + "jsxFragmentFactory": "Fragment" + }, + "include": [ + "src/**/*.ts", + "src/**/*.vue", + "src/typings/**/*.d.ts", + "mock/**/*.ts", + "vite.config.ts" + ], + "exclude": ["node_modules", "dist", "**/*.js"] +} diff --git a/uno.config.ts b/uno.config.ts new file mode 100644 index 0000000..b952fe4 --- /dev/null +++ b/uno.config.ts @@ -0,0 +1,43 @@ +// uno.config.ts +import { + defineConfig, + presetAttributify, + presetIcons, + presetTypography, + presetUno, + presetWebFonts, + transformerDirectives, + transformerVariantGroup, +} from "unocss"; + +export default defineConfig({ + shortcuts: { + "flex-center": "flex justify-center items-center", + "flex-x-center": "flex justify-center", + "flex-y-center": "flex items-center", + "wh-full": "w-full h-full", + "flex-x-between": "flex items-center justify-between", + "flex-x-end": "flex items-center justify-end", + "absolute-lt": "absolute left-0 top-0", + "absolute-rt": "absolute right-0 top-0 ", + "fixed-lt": "fixed left-0 top-0", + }, + theme: { + colors: { + primary: "var(--el-color-primary)", + primary_dark: "var(--el-color-primary-light-5)", + }, + }, + presets: [ + presetUno(), + presetAttributify(), + presetIcons(), + presetTypography(), + presetWebFonts({ + fonts: { + // ... + }, + }), + ], + transformers: [transformerDirectives(), transformerVariantGroup()], +}); diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..69f7ea2 --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,237 @@ +import vue from "@vitejs/plugin-vue"; +import vueJsx from "@vitejs/plugin-vue-jsx"; +import { UserConfig, ConfigEnv, loadEnv, defineConfig } from "vite"; + +import AutoImport from "unplugin-auto-import/vite"; +import Components from "unplugin-vue-components/vite"; +import { ElementPlusResolver } from "unplugin-vue-components/resolvers"; +import Icons from "unplugin-icons/vite"; +import IconsResolver from "unplugin-icons/resolver"; + +import { createSvgIconsPlugin } from "vite-plugin-svg-icons"; +import mockDevServerPlugin from "vite-plugin-mock-dev-server"; + +import UnoCSS from "unocss/vite"; +import { resolve } from "path"; +import { + name, + version, + engines, + dependencies, + devDependencies, +} from "./package.json"; + +/** 平台的名称、版本、运行所需的`node`版本、依赖、构建时间的类型提示 */ +const __APP_INFO__ = { + pkg: { name, version, engines, dependencies, devDependencies }, + buildTimestamp: Date.now(), +}; + +const pathSrc = resolve(__dirname, "src"); +// https://cn.vitejs.dev/config +export default defineConfig(({ mode }: ConfigEnv): UserConfig => { + const env = loadEnv(mode, process.cwd()); + return { + resolve: { + alias: { + "@": pathSrc, + }, + }, + css: { + // CSS 预处理器 + preprocessorOptions: { + // 定义全局 SCSS 变量 + scss: { + javascriptEnabled: true, + additionalData: ` + @use "@/styles/variables.scss" as *; + `, + }, + }, + }, + server: { + // 允许IP访问 + host: "0.0.0.0", + // 应用端口 (默认:3000) + port: Number(env.VITE_APP_PORT), + // 运行是否自动打开浏览器 + open: true, + proxy: { + /** + * 代理前缀为 /dev-api 的请求 + */ + [env.VITE_APP_BASE_API]: { + changeOrigin: true, + // 接口地址 + target: env.VITE_APP_API_URL, + rewrite: (path) => + path.replace(new RegExp("^" + env.VITE_APP_BASE_API), ""), + }, + }, + }, + plugins: [ + vue(), + // jsx、tsx语法支持 + vueJsx(), + // MOCK 服务 + env.VITE_MOCK_DEV_SERVER === "true" ? mockDevServerPlugin() : null, + UnoCSS({ + hmrTopLevelAwait: false, + }), + // 自动导入参考: https://github.com/sxzz/element-plus-best-practices/blob/main/vite.config.ts + AutoImport({ + // 自动导入 Vue 相关函数,如:ref, reactive, toRef 等 + imports: ["vue", "@vueuse/core", "pinia", "vue-router", "vue-i18n"], + // 自动导入 Element Plus 相关函数,如:ElMessage, ElMessageBox... (带样式) + resolvers: [ElementPlusResolver(), IconsResolver({})], + eslintrc: { + enabled: false, + filepath: "./.eslintrc-auto-import.json", + globalsPropValue: true, + }, + vueTemplate: true, + // 配置文件生成位置(false:关闭自动生成) + dts: false, + // dts: "src/typings/auto-imports.d.ts", + }), + + Components({ + resolvers: [ + // 自动导入 Element Plus 组件 + ElementPlusResolver(), + // 自动注册图标组件 + IconsResolver({ enabledCollections: ["ep"] }), + ], + // 指定自定义组件位置(默认:src/components) + dirs: ["src/components", "src/**/components"], + // 配置文件位置 (false:关闭自动生成) + dts: false, + // dts: "src/typings/components.d.ts", + }), + + Icons({ + autoInstall: true, + }), + createSvgIconsPlugin({ + // 指定需要缓存的图标文件夹 + iconDirs: [resolve(pathSrc, "assets/icons")], + // 指定symbolId格式 + symbolId: "icon-[dir]-[name]", + }), + ], + // 预加载项目必需的组件 + optimizeDeps: { + include: [ + "vue", + "vue-router", + "pinia", + "axios", + "@vueuse/core", + "sortablejs", + "path-to-regexp", + "echarts", + "@wangeditor/editor", + "@wangeditor/editor-for-vue", + "vue-i18n", + "path-browserify", + "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", + "element-plus/es/components/radio-button/style/css", + "element-plus/es/components/checkbox-group/style/css", + "element-plus/es/components/checkbox/style/css", + "element-plus/es/components/tabs/style/css", + "element-plus/es/components/tab-pane/style/css", + "element-plus/es/components/rate/style/css", + "element-plus/es/components/date-picker/style/css", + "element-plus/es/components/notification/style/css", + "element-plus/es/components/image/style/css", + "element-plus/es/components/statistic/style/css", + "element-plus/es/components/watermark/style/css", + "element-plus/es/components/config-provider/style/css", + "element-plus/es/components/text/style/css", + "element-plus/es/components/drawer/style/css", + "element-plus/es/components/color-picker/style/css", + ], + }, + // 构建配置 + build: { + chunkSizeWarningLimit: 2000, // 消除打包大小超过500kb警告 + minify: "terser", // Vite 2.6.x 以上需要配置 minify: "terser", terserOptions 才能生效 + terserOptions: { + compress: { + keep_infinity: true, // 防止 Infinity 被压缩成 1/0,这可能会导致 Chrome 上的性能问题 + drop_console: true, // 生产环境去除 console + drop_debugger: true, // 生产环境去除 debugger + }, + format: { + comments: false, // 删除注释 + }, + }, + rollupOptions: { + output: { + // manualChunks: { + // "vue-i18n": ["vue-i18n"], + // }, + // 用于从入口点创建的块的打包输出格式[name]表示文件名,[hash]表示该文件内容hash值 + entryFileNames: "js/[name].[hash].js", + // 用于命名代码拆分时创建的共享块的输出命名 + chunkFileNames: "js/[name].[hash].js", + // 用于输出静态资源的命名,[ext]表示文件扩展名 + assetFileNames: (assetInfo: any) => { + const info = assetInfo.name.split("."); + let extType = info[info.length - 1]; + // console.log('文件信息', assetInfo.name) + if ( + /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/i.test(assetInfo.name) + ) { + extType = "media"; + } else if (/\.(png|jpe?g|gif|svg)(\?.*)?$/.test(assetInfo.name)) { + extType = "img"; + } else if (/\.(woff2?|eot|ttf|otf)(\?.*)?$/i.test(assetInfo.name)) { + extType = "fonts"; + } + return `${extType}/[name].[hash].[ext]`; + }, + }, + }, + }, + define: { + __APP_INFO__: JSON.stringify(__APP_INFO__), + }, + }; +});