commit 0929f44e4ccd58b9c3ca99ee4606059bd1b6e20d Author: XiaoXinPro 14 IAH5R <568170040@qq.com> Date: Tue Oct 24 11:06:06 2023 +0800 upload diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..dd76572 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,14 @@ +# http://editorconfig.org +root = true + +# 表示所有文件适用 +[*] +charset = utf-8 # 设置文件字符集为 utf-8 +end_of_line = lf # 控制换行类型(lf | cr | crlf) +indent_style = tab # 缩进风格(tab | space) +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..a42f23a --- /dev/null +++ b/.env.development @@ -0,0 +1,10 @@ +## 开发环境 + +# 变量必须以 VITE_ 为前缀才能暴露给外部读取 +NODE_ENV='development' + +VITE_APP_TITLE = 'Metavatar-PC' +VITE_APP_PORT = 3000 +## VITE_APP_BASE_API = 'https://photo.metavatar.cc/C' +VITE_APP_BASE_API = 'https://test.metavatar.cc/C' + diff --git a/.env.production b/.env.production new file mode 100644 index 0000000..13936ee --- /dev/null +++ b/.env.production @@ -0,0 +1,8 @@ +## 生产环境 + +# 变量必须以 VITE_ 为前缀才能暴露给外部读取 +NODE_ENV='production' + +VITE_APP_TITLE = 'Metavatar-PC' +VITE_APP_PORT = 3000 +VITE_APP_BASE_API = 'https://neuver.metavatar.cc/C' diff --git a/.env.test b/.env.test new file mode 100644 index 0000000..46459b7 --- /dev/null +++ b/.env.test @@ -0,0 +1,9 @@ +## 开发环境 + +# 变量必须以 VITE_ 为前缀才能暴露给外部读取 +NODE_ENV='test' + +VITE_APP_TITLE = 'Metavatar-PC' +VITE_APP_PORT = 3000 +# VITE_APP_BASE_API = 'https://photo.metavatar.cc/C' +VITE_APP_BASE_API = 'https://test.metavatar.cc/C' 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..9f36942 --- /dev/null +++ b/.eslintrc-auto-import.json @@ -0,0 +1,269 @@ +{ + "globals": { + "EffectScope": true, + "ElForm": true, + "ElMessage": true, + "ElMessageBox": true, + "ElTree": true, + "asyncComputed": true, + "autoResetRef": true, + "computed": true, + "computedAsync": true, + "computedEager": true, + "computedInject": true, + "computedWithControl": true, + "controlledComputed": true, + "controlledRef": true, + "createApp": true, + "createEventHook": true, + "createGlobalState": true, + "createInjectionState": true, + "createReactiveFn": true, + "createSharedComposable": true, + "createUnrefFn": true, + "customRef": true, + "debouncedRef": true, + "debouncedWatch": true, + "defineAsyncComponent": true, + "defineComponent": true, + "eagerComputed": true, + "effectScope": true, + "extendRef": true, + "getCurrentInstance": true, + "getCurrentScope": true, + "h": true, + "ignorableWatch": true, + "inject": true, + "isDefined": true, + "isProxy": true, + "isReactive": true, + "isReadonly": true, + "isRef": true, + "makeDestructurable": true, + "markRaw": true, + "nextTick": true, + "onActivated": true, + "onBeforeMount": true, + "onBeforeUnmount": true, + "onBeforeUpdate": true, + "onClickOutside": true, + "onDeactivated": true, + "onErrorCaptured": true, + "onKeyStroke": true, + "onLongPress": true, + "onMounted": true, + "onRenderTracked": true, + "onRenderTriggered": true, + "onScopeDispose": true, + "onServerPrefetch": true, + "onStartTyping": true, + "onUnmounted": true, + "onUpdated": true, + "pausableWatch": true, + "provide": true, + "reactify": true, + "reactifyObject": true, + "reactive": true, + "reactiveComputed": true, + "reactiveOmit": true, + "reactivePick": true, + "readonly": true, + "ref": true, + "refAutoReset": true, + "refDebounced": true, + "refDefault": true, + "refThrottled": true, + "refWithControl": true, + "resolveComponent": true, + "resolveDirective": true, + "resolveRef": true, + "resolveUnref": true, + "shallowReactive": true, + "shallowReadonly": true, + "shallowRef": true, + "syncRef": true, + "syncRefs": true, + "templateRef": true, + "throttledRef": true, + "throttledWatch": true, + "toRaw": true, + "toReactive": true, + "toRef": true, + "toRefs": true, + "triggerRef": true, + "tryOnBeforeMount": true, + "tryOnBeforeUnmount": true, + "tryOnMounted": true, + "tryOnScopeDispose": true, + "tryOnUnmounted": true, + "unref": true, + "unrefElement": true, + "until": true, + "useActiveElement": true, + "useArrayEvery": true, + "useArrayFilter": true, + "useArrayFind": true, + "useArrayFindIndex": true, + "useArrayFindLast": true, + "useArrayJoin": true, + "useArrayMap": true, + "useArrayReduce": true, + "useArraySome": true, + "useArrayUnique": true, + "useAsyncQueue": true, + "useAsyncState": true, + "useAttrs": true, + "useBase64": true, + "useBattery": true, + "useBluetooth": true, + "useBreakpoints": true, + "useBroadcastChannel": true, + "useBrowserLocation": true, + "useCached": true, + "useClipboard": true, + "useCloned": true, + "useColorMode": true, + "useConfirmDialog": true, + "useCounter": true, + "useCssModule": true, + "useCssVar": true, + "useCssVars": true, + "useCurrentElement": true, + "useCycleList": true, + "useDark": true, + "useDateFormat": true, + "useDebounce": true, + "useDebounceFn": true, + "useDebouncedRefHistory": true, + "useDeviceMotion": true, + "useDeviceOrientation": true, + "useDevicePixelRatio": true, + "useDevicesList": true, + "useDisplayMedia": true, + "useDocumentVisibility": true, + "useDraggable": true, + "useDropZone": true, + "useElementBounding": true, + "useElementByPoint": true, + "useElementHover": true, + "useElementSize": true, + "useElementVisibility": true, + "useEventBus": true, + "useEventListener": true, + "useEventSource": true, + "useEyeDropper": true, + "useFavicon": true, + "useFetch": true, + "useFileDialog": true, + "useFileSystemAccess": true, + "useFocus": true, + "useFocusWithin": true, + "useFps": true, + "useFullscreen": true, + "useGamepad": true, + "useGeolocation": true, + "useIdle": true, + "useImage": true, + "useInfiniteScroll": true, + "useIntersectionObserver": true, + "useInterval": true, + "useIntervalFn": true, + "useKeyModifier": true, + "useLastChanged": true, + "useLocalStorage": true, + "useMagicKeys": true, + "useManualRefHistory": true, + "useMediaControls": true, + "useMediaQuery": true, + "useMemoize": true, + "useMemory": true, + "useMounted": true, + "useMouse": true, + "useMouseInElement": true, + "useMousePressed": true, + "useMutationObserver": true, + "useNavigatorLanguage": true, + "useNetwork": true, + "useNow": true, + "useObjectUrl": true, + "useOffsetPagination": true, + "useOnline": true, + "usePageLeave": true, + "useParallax": true, + "usePermission": true, + "usePointer": true, + "usePointerLock": true, + "usePointerSwipe": true, + "usePreferredColorScheme": true, + "usePreferredContrast": true, + "usePreferredDark": true, + "usePreferredLanguages": true, + "usePreferredReducedMotion": true, + "usePrevious": true, + "useRafFn": true, + "useRefHistory": true, + "useResizeObserver": true, + "useScreenOrientation": true, + "useScreenSafeArea": true, + "useScriptTag": true, + "useScroll": true, + "useScrollLock": true, + "useSessionStorage": true, + "useShare": true, + "useSlots": true, + "useSorted": true, + "useSpeechRecognition": true, + "useSpeechSynthesis": true, + "useStepper": true, + "useStorage": true, + "useStorageAsync": true, + "useStyleTag": true, + "useSupported": true, + "useSwipe": true, + "useTemplateRefsList": true, + "useTextDirection": true, + "useTextSelection": true, + "useTextareaAutosize": true, + "useThrottle": true, + "useThrottleFn": true, + "useThrottledRefHistory": true, + "useTimeAgo": true, + "useTimeout": true, + "useTimeoutFn": true, + "useTimeoutPoll": true, + "useTimestamp": true, + "useTitle": true, + "useToNumber": true, + "useToString": true, + "useToggle": true, + "useTransition": true, + "useUrlSearchParams": true, + "useUserMedia": true, + "useVModel": true, + "useVModels": true, + "useVibrate": true, + "useVirtualList": true, + "useWakeLock": true, + "useWebNotification": true, + "useWebSocket": true, + "useWebWorker": true, + "useWebWorkerFn": true, + "useWindowFocus": true, + "useWindowScroll": true, + "useWindowSize": true, + "watch": true, + "watchArray": true, + "watchAtMost": true, + "watchDebounced": true, + "watchEffect": true, + "watchIgnorable": true, + "watchOnce": true, + "watchPausable": true, + "watchPostEffect": true, + "watchSyncEffect": true, + "watchThrottled": true, + "watchTriggerable": true, + "watchWithFilter": true, + "whenever": true + } +} \ No newline at end of file diff --git a/.eslintrc.cjs b/.eslintrc.cjs new file mode 100644 index 0000000..eb5a4c4 --- /dev/null +++ b/.eslintrc.cjs @@ -0,0 +1,33 @@ +module.exports = { + env: { + browser: true, + es2021: true, + node: true, + }, + parser: "vue-eslint-parser", + // https://eslint.vuejs.org/user-guide/#bundle-configurations + extends: [ + "eslint:recommended", + "plugin:vue/vue3-essential", + "plugin:@typescript-eslint/recommended", + "./.eslintrc-auto-import.json", + ], + parserOptions: { + ecmaVersion: "latest", + sourceType: "module", + parser: "@typescript-eslint/parser", + }, + plugins: ["vue", "@typescript-eslint"], + rules: { + "vue/multi-word-component-names": "off", // 关闭组件名必须多字: https://eslint.vuejs.org/rules/multi-word-component-names.html + "@typescript-eslint/no-empty-function": "off", // 关闭空方法检查 + "@typescript-eslint/no-explicit-any": "off", // 关闭any类型的警告 + "vue/no-v-model-argument": "off", + "@typescript-eslint/no-non-null-assertion": "off", + }, + // https://eslint.org/docs/latest/use/configure/language-options#specifying-globals + globals: { + DialogOption: "readonly", + OptionType: "readonly", + }, +}; diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d5c3e83 --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +node_modules +.DS_Store +dist +dist-ssr +*.local + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln +*.local + +package-lock.json +pnpm-lock.yaml 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..d751f74 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,10 @@ +dist +node_modules +public +.husky +.vscode +.idea +*.sh +*.md + +src/assets diff --git a/.prettierrc.cjs b/.prettierrc.cjs new file mode 100644 index 0000000..d39f28c --- /dev/null +++ b/.prettierrc.cjs @@ -0,0 +1,36 @@ +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 文件中是否缩进 \ No newline at end of file diff --git a/src/assets/icons/chart.svg b/src/assets/icons/chart.svg new file mode 100644 index 0000000..27728fb --- /dev/null +++ b/src/assets/icons/chart.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..ad4bc15 --- /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..5b5057f --- /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..aa13cd7 --- /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..e5708ea --- /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..212e6c2 --- /dev/null +++ b/src/assets/icons/close_other.svg @@ -0,0 +1 @@ + diff --git a/src/assets/icons/close_right.svg b/src/assets/icons/close_right.svg new file mode 100644 index 0000000..14d3cf3 --- /dev/null +++ b/src/assets/icons/close_right.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/coupon.svg b/src/assets/icons/coupon.svg new file mode 100644 index 0000000..2f952b2 --- /dev/null +++ b/src/assets/icons/coupon.svg @@ -0,0 +1 @@ + diff --git a/src/assets/icons/dashboard.svg b/src/assets/icons/dashboard.svg new file mode 100644 index 0000000..5317d37 --- /dev/null +++ b/src/assets/icons/dashboard.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..22a8278 --- /dev/null +++ b/src/assets/icons/dict.svg @@ -0,0 +1,18 @@ + + + + + + + diff --git a/src/assets/icons/dict_item.svg b/src/assets/icons/dict_item.svg new file mode 100644 index 0000000..903109a --- /dev/null +++ b/src/assets/icons/dict_item.svg @@ -0,0 +1,12 @@ + + + + + + + + + diff --git a/src/assets/icons/document.svg b/src/assets/icons/document.svg new file mode 100644 index 0000000..918ae33 --- /dev/null +++ b/src/assets/icons/document.svg @@ -0,0 +1 @@ + diff --git a/src/assets/icons/download.svg b/src/assets/icons/download.svg new file mode 100644 index 0000000..61ec1f9 --- /dev/null +++ b/src/assets/icons/download.svg @@ -0,0 +1 @@ + diff --git a/src/assets/icons/drag.svg b/src/assets/icons/drag.svg new file mode 100644 index 0000000..4185d3c --- /dev/null +++ b/src/assets/icons/drag.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..d26101f --- /dev/null +++ b/src/assets/icons/edit.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/exit-fullscreen.svg b/src/assets/icons/exit-fullscreen.svg new file mode 100644 index 0000000..485c128 --- /dev/null +++ b/src/assets/icons/exit-fullscreen.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..88dcc98 --- /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/fullscreen.svg b/src/assets/icons/fullscreen.svg new file mode 100644 index 0000000..0e86b6f --- /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..db0a0d4 --- /dev/null +++ b/src/assets/icons/github.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/goods-list.svg b/src/assets/icons/goods-list.svg new file mode 100644 index 0000000..fcb971e --- /dev/null +++ b/src/assets/icons/goods-list.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/goods.svg b/src/assets/icons/goods.svg new file mode 100644 index 0000000..60c1c73 --- /dev/null +++ b/src/assets/icons/goods.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/guide.svg b/src/assets/icons/guide.svg new file mode 100644 index 0000000..b271001 --- /dev/null +++ b/src/assets/icons/guide.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..48f4e24 --- /dev/null +++ b/src/assets/icons/homepage.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/lab.svg b/src/assets/icons/lab.svg new file mode 100644 index 0000000..d4d60aa --- /dev/null +++ b/src/assets/icons/lab.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..d2dd693 --- /dev/null +++ b/src/assets/icons/language.svg @@ -0,0 +1 @@ + diff --git a/src/assets/icons/link.svg b/src/assets/icons/link.svg new file mode 100644 index 0000000..9748d53 --- /dev/null +++ b/src/assets/icons/link.svg @@ -0,0 +1 @@ + diff --git a/src/assets/icons/menu.svg b/src/assets/icons/menu.svg new file mode 100644 index 0000000..92c364c --- /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..ea1ddef --- /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..60f7acf --- /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..bc308cb --- /dev/null +++ b/src/assets/icons/monitor.svg @@ -0,0 +1,2 @@ + \ No newline at end of file diff --git a/src/assets/icons/multi_level.svg b/src/assets/icons/multi_level.svg new file mode 100644 index 0000000..a1a2792 --- /dev/null +++ b/src/assets/icons/multi_level.svg @@ -0,0 +1 @@ + diff --git a/src/assets/icons/nested.svg b/src/assets/icons/nested.svg new file mode 100644 index 0000000..06713a8 --- /dev/null +++ b/src/assets/icons/nested.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/number.svg b/src/assets/icons/number.svg new file mode 100644 index 0000000..ad5ce9a --- /dev/null +++ b/src/assets/icons/number.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..8f2107e --- /dev/null +++ b/src/assets/icons/order.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/password.svg b/src/assets/icons/password.svg new file mode 100644 index 0000000..6c64def --- /dev/null +++ b/src/assets/icons/password.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..383b82d --- /dev/null +++ b/src/assets/icons/peoples.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/perm.svg b/src/assets/icons/perm.svg new file mode 100644 index 0000000..b38d065 --- /dev/null +++ b/src/assets/icons/perm.svg @@ -0,0 +1 @@ + diff --git a/src/assets/icons/publish.svg b/src/assets/icons/publish.svg new file mode 100644 index 0000000..e9b489c --- /dev/null +++ b/src/assets/icons/publish.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/rabbitmq.svg b/src/assets/icons/rabbitmq.svg new file mode 100644 index 0000000..65aa198 --- /dev/null +++ b/src/assets/icons/rabbitmq.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/rate.svg b/src/assets/icons/rate.svg new file mode 100644 index 0000000..aa3b14d --- /dev/null +++ b/src/assets/icons/rate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/redis.svg b/src/assets/icons/redis.svg new file mode 100644 index 0000000..2f1d62d --- /dev/null +++ b/src/assets/icons/redis.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..1f549f1 --- /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..c484b13 --- /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..bcd9d2e --- /dev/null +++ b/src/assets/icons/security.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/shopping.svg b/src/assets/icons/shopping.svg new file mode 100644 index 0000000..8d2b4bf --- /dev/null +++ b/src/assets/icons/shopping.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..ddb25b8 --- /dev/null +++ b/src/assets/icons/size.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/skill.svg b/src/assets/icons/skill.svg new file mode 100644 index 0000000..a3b7312 --- /dev/null +++ b/src/assets/icons/skill.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..e3b7e2d --- /dev/null +++ b/src/assets/icons/system.svg @@ -0,0 +1 @@ + diff --git a/src/assets/icons/theme.svg b/src/assets/icons/theme.svg new file mode 100644 index 0000000..5982a2f --- /dev/null +++ b/src/assets/icons/theme.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..d40a414 --- /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..e4c7b38 --- /dev/null +++ b/src/assets/icons/user.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/uv.svg b/src/assets/icons/uv.svg new file mode 100644 index 0000000..ca4c301 --- /dev/null +++ b/src/assets/icons/uv.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/valid_code.svg b/src/assets/icons/valid_code.svg new file mode 100644 index 0000000..39bf478 --- /dev/null +++ b/src/assets/icons/valid_code.svg @@ -0,0 +1,9 @@ + + + + diff --git a/src/assets/icons/verify_code.svg b/src/assets/icons/verify_code.svg new file mode 100644 index 0000000..39bf478 --- /dev/null +++ b/src/assets/icons/verify_code.svg @@ -0,0 +1,9 @@ + + + + diff --git a/src/assets/img/16776641647875906_FBIpSdzR_matting.png b/src/assets/img/16776641647875906_FBIpSdzR_matting.png new file mode 100644 index 0000000..1475bc5 Binary files /dev/null and b/src/assets/img/16776641647875906_FBIpSdzR_matting.png differ diff --git a/src/assets/img/Email.png b/src/assets/img/Email.png new file mode 100644 index 0000000..dcf6898 Binary files /dev/null and b/src/assets/img/Email.png differ diff --git a/src/assets/img/Scale.png b/src/assets/img/Scale.png new file mode 100644 index 0000000..ead2abf Binary files /dev/null and b/src/assets/img/Scale.png differ diff --git a/src/assets/img/Snipaste_2023-04-18_11-44-37.png b/src/assets/img/Snipaste_2023-04-18_11-44-37.png new file mode 100644 index 0000000..4a74f27 Binary files /dev/null and b/src/assets/img/Snipaste_2023-04-18_11-44-37.png differ diff --git a/src/assets/img/Snipaste_2023-04-18_11-44-45.png b/src/assets/img/Snipaste_2023-04-18_11-44-45.png new file mode 100644 index 0000000..3727e2d Binary files /dev/null and b/src/assets/img/Snipaste_2023-04-18_11-44-45.png differ diff --git a/src/assets/img/Upgrades.png b/src/assets/img/Upgrades.png new file mode 100644 index 0000000..946fe19 Binary files /dev/null and b/src/assets/img/Upgrades.png differ diff --git a/src/assets/img/arrowLeft.png b/src/assets/img/arrowLeft.png new file mode 100644 index 0000000..96f8b67 Binary files /dev/null and b/src/assets/img/arrowLeft.png differ diff --git a/src/assets/img/arrowRight.png b/src/assets/img/arrowRight.png new file mode 100644 index 0000000..9ba851e Binary files /dev/null and b/src/assets/img/arrowRight.png differ diff --git a/src/assets/img/code.png b/src/assets/img/code.png new file mode 100644 index 0000000..e8ba3ba Binary files /dev/null and b/src/assets/img/code.png differ diff --git a/src/assets/img/delete.png b/src/assets/img/delete.png new file mode 100644 index 0000000..c200380 Binary files /dev/null and b/src/assets/img/delete.png differ diff --git a/src/assets/img/delete2.png b/src/assets/img/delete2.png new file mode 100644 index 0000000..dbc9705 Binary files /dev/null and b/src/assets/img/delete2.png differ diff --git a/src/assets/img/false1.png b/src/assets/img/false1.png new file mode 100644 index 0000000..b9d3e55 Binary files /dev/null and b/src/assets/img/false1.png differ diff --git a/src/assets/img/false2.png b/src/assets/img/false2.png new file mode 100644 index 0000000..72b5850 Binary files /dev/null and b/src/assets/img/false2.png differ diff --git a/src/assets/img/false3.png b/src/assets/img/false3.png new file mode 100644 index 0000000..0f01c8d Binary files /dev/null and b/src/assets/img/false3.png differ diff --git a/src/assets/img/left-top-logo.png b/src/assets/img/left-top-logo.png new file mode 100644 index 0000000..9d7f00b Binary files /dev/null and b/src/assets/img/left-top-logo.png differ diff --git a/src/assets/img/phone.png b/src/assets/img/phone.png new file mode 100644 index 0000000..4557147 Binary files /dev/null and b/src/assets/img/phone.png differ diff --git a/src/assets/img/star.png b/src/assets/img/star.png new file mode 100644 index 0000000..3b0137f Binary files /dev/null and b/src/assets/img/star.png differ diff --git a/src/assets/img/tips.png b/src/assets/img/tips.png new file mode 100644 index 0000000..8299116 Binary files /dev/null and b/src/assets/img/tips.png differ diff --git a/src/assets/img/trueImg.png b/src/assets/img/trueImg.png new file mode 100644 index 0000000..86937aa Binary files /dev/null and b/src/assets/img/trueImg.png differ diff --git a/src/assets/img/uploadImg.png b/src/assets/img/uploadImg.png new file mode 100644 index 0000000..0de27df Binary files /dev/null and b/src/assets/img/uploadImg.png differ diff --git a/src/assets/index/indicator.png b/src/assets/index/indicator.png new file mode 100644 index 0000000..2f53da6 Binary files /dev/null and b/src/assets/index/indicator.png differ diff --git a/src/components/Breadcrumb/index.vue b/src/components/Breadcrumb/index.vue new file mode 100644 index 0000000..a17fc3f --- /dev/null +++ b/src/components/Breadcrumb/index.vue @@ -0,0 +1,103 @@ + + + + + {{ translateRouteTitleI18n(item.meta.title) }} + + {{ translateRouteTitleI18n(item.meta.title) }} + + + + + + + + + diff --git a/src/components/EditPosition copy.vue b/src/components/EditPosition copy.vue new file mode 100644 index 0000000..1221d39 --- /dev/null +++ b/src/components/EditPosition copy.vue @@ -0,0 +1,492 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/EditPosition.vue b/src/components/EditPosition.vue new file mode 100644 index 0000000..b74c953 --- /dev/null +++ b/src/components/EditPosition.vue @@ -0,0 +1,553 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{ $t("createVideo.SubtitlesTip") }} + + + + + + + + diff --git a/src/components/GithubCorner/index.vue b/src/components/GithubCorner/index.vue new file mode 100644 index 0000000..6fadd6c --- /dev/null +++ b/src/components/GithubCorner/index.vue @@ -0,0 +1,62 @@ + + + + + + + + + + + diff --git a/src/components/Hamburger/index.vue b/src/components/Hamburger/index.vue new file mode 100644 index 0000000..374a1b5 --- /dev/null +++ b/src/components/Hamburger/index.vue @@ -0,0 +1,46 @@ + + + + + + + + + + + diff --git a/src/components/IconSelect/index.vue b/src/components/IconSelect/index.vue new file mode 100644 index 0000000..ba8e787 --- /dev/null +++ b/src/components/IconSelect/index.vue @@ -0,0 +1,164 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/LangSelect/index.vue b/src/components/LangSelect/index.vue new file mode 100644 index 0000000..dce6178 --- /dev/null +++ b/src/components/LangSelect/index.vue @@ -0,0 +1,46 @@ + + + + + + + + + 中文 + + + English + + + + + + + + + diff --git a/src/components/Pagination/index.vue b/src/components/Pagination/index.vue new file mode 100644 index 0000000..24c5ed0 --- /dev/null +++ b/src/components/Pagination/index.vue @@ -0,0 +1,88 @@ + + + + + + + + + diff --git a/src/components/SizeSelect/index.vue b/src/components/SizeSelect/index.vue new file mode 100644 index 0000000..a48f914 --- /dev/null +++ b/src/components/SizeSelect/index.vue @@ -0,0 +1,36 @@ + + + + + + + + + + + {{ item.label }} + + + + + diff --git a/src/components/SvgIcon/index.vue b/src/components/SvgIcon/index.vue new file mode 100644 index 0000000..e9d145e --- /dev/null +++ b/src/components/SvgIcon/index.vue @@ -0,0 +1,43 @@ + + + + + + + + + diff --git a/src/components/Upload/MultiUpload.vue b/src/components/Upload/MultiUpload.vue new file mode 100644 index 0000000..1194509 --- /dev/null +++ b/src/components/Upload/MultiUpload.vue @@ -0,0 +1,139 @@ + + + + + + + + + + + + + diff --git a/src/components/Upload/SingleUpload.vue b/src/components/Upload/SingleUpload.vue new file mode 100644 index 0000000..6cacc62 --- /dev/null +++ b/src/components/Upload/SingleUpload.vue @@ -0,0 +1,81 @@ + + + + + + + + + + + + + diff --git a/src/components/WangEditor/index.vue b/src/components/WangEditor/index.vue new file mode 100644 index 0000000..c9b11f6 --- /dev/null +++ b/src/components/WangEditor/index.vue @@ -0,0 +1,74 @@ + + + + + + + + + + + + diff --git a/src/components/cutImg.vue b/src/components/cutImg.vue new file mode 100644 index 0000000..bbb3ee1 --- /dev/null +++ b/src/components/cutImg.vue @@ -0,0 +1,226 @@ + + + + + + + + + + + + + + + + + + Cancel + Confirm + + + + + + + + + + diff --git a/src/components/cutImg1.vue b/src/components/cutImg1.vue new file mode 100644 index 0000000..43ea72d --- /dev/null +++ b/src/components/cutImg1.vue @@ -0,0 +1,128 @@ + + + + + + + + + + + + + + + + + + Cancel + + Confirm + + + + + + + + + diff --git a/src/components/myLoading.vue b/src/components/myLoading.vue new file mode 100644 index 0000000..7685b5d --- /dev/null +++ b/src/components/myLoading.vue @@ -0,0 +1,52 @@ + + + + + + + + + {{ $t("myCreating.loading") }} + + + + + + diff --git a/src/components/myLoadingIcon.vue b/src/components/myLoadingIcon.vue new file mode 100644 index 0000000..84b1e7e --- /dev/null +++ b/src/components/myLoadingIcon.vue @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + diff --git a/src/components/talkUs.vue b/src/components/talkUs.vue new file mode 100644 index 0000000..7124de4 --- /dev/null +++ b/src/components/talkUs.vue @@ -0,0 +1,114 @@ + + + + + + + + + +86 138 0013 8000 + + + + + {{ lanChange == "zh-cn" ? "微信" : "Wechat " }} + + + + + + {{ lanChange == "zh-cn" ? "发送邮件" : "Send Email" }} + + + + + {{ $t(`public.close`) }} + + + + + + + {{ lanChange == "zh-cn" ? "暂未开放,敬请期待" : "COMING SOON" }} + + + + + + diff --git a/src/directive/index.ts b/src/directive/index.ts new file mode 100644 index 0000000..960fa44 --- /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..b8d0dee --- /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(); + 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(); + 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/MenuTypeEnum.ts b/src/enums/MenuTypeEnum.ts new file mode 100644 index 0000000..96c612e --- /dev/null +++ b/src/enums/MenuTypeEnum.ts @@ -0,0 +1,19 @@ +export enum MenuTypeEnum { + /** + * 目录 + */ + CATALOG = 'CATALOG', + /** + * 菜单 + */ + MENU = 'MENU', + + /** + * 按钮 + */ + BUTTON = 'BUTTON', + /** + * 外链 + */ + EXTLINK = 'EXTLINK' +} diff --git a/src/lang/index.ts b/src/lang/index.ts new file mode 100644 index 0000000..7f3ad89 --- /dev/null +++ b/src/lang/index.ts @@ -0,0 +1,25 @@ +import { createI18n } from 'vue-i18n'; +import { useAppStore } from '@/store/modules/app'; + +const appStore = useAppStore(); +// 本地语言包 +import enLocale from './package/en'; +import zhCnLocale from './package/zh-cn'; + +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..f65e762 --- /dev/null +++ b/src/lang/package/en.ts @@ -0,0 +1,226 @@ +export default { + // 路由国际化 + route: { + dashboard: "Dashboard", + document: "Document", + }, + // 公用 + public: { + cancel: "Cancel", + confirm: "Confirm", + username: "Username", + avatar: "Avatar", + close: "Close", + }, + // 登录页面国际化 + login: { + // title: "Metavatar-MetaTalker", + title: "MetaTalker", + username: "Username", + email: "Phone", + password: "Password", + confirmPassword: "Confirm password", + login: "Sign in", + register: "Sign up", + loginBtn: "Sign in", + registerBtn: "Sign up", + verifyCode: "Verify code", + signIn: "Already got an account? Sign in here", + signUp: "Don't have an account? Sign up here", + forgetPwd: "I forgot my password", + forgetPwdTitle: "Forgot password", + SendEmail: "Send SMS Code", + resetPassword: "Reset password", + setNewPwd: "Set new password", + newPwd: "New password", + oldPwd: "Old password", + confirmNewPwd: "Confirm new password", + confirm: "Confirm", + gohome: "Back to home page", + SMSCode: "SMS Code", + SMSLogin: "SMS login", + pwsLogin: "Account password login", + sendSMS: "Send SMS", + setInvitatioCode: "Invitation Code", + invitatioCode: "Invitation Code", + }, + + // 导航栏国际化 + navbar: { + account: "Account", + logout: "Logout", + goLogin: "Go and log in", + LanguageChange: "Language switching", + }, + + // 创作视频 + createVideo: { + title: "Create Video", + availableMould: "Available moulds", + personalWarehouse: "Personal warehouse", + typeScript: "Type your script", + uploadAudio: "Upload audio", + recordedAudio: "Recorded audio", + startRecorded: "Start recording", + stopRecorded: "Stop recording", + aiScript: "Ai script", + typeTitle: "Type a title", + confirm: "Confirm", + aiText: "Please input here", + languages: "Languages", + voices: "Voices", + styles: "Styles", + reselect: "Reselect", + sureUpload: "Confirm upload", + generateVideo: "Generate video", + saveVideo: "Save manuscript", + select: "Please select", + music: "Music", + Aipicture: "Ai-generated pictures", + AipictureTip: "Please customize your picture", + chooseMusic: "Choose music", + searchMusic: "Search music", + copywriting: "Copywriting", + BackgroundImage: "Background Image", + comingSoon: "COMING SOON", + material: "Material", + subtitles: "Subtitles", + sex: "Sex", + videoRatio: "Video Ratio", + all: "All", + male: "Male", + female: "Female", + landscape: "Landscape", + portrait: "Portrait", + systemBGI: "System background diagram", + personBGI: "Person background diagram", + systemMaterial: "System Material", + personMaterial: "Person Material", + abscissa: "abscissa", + ordinate: "ordinate", + hierarchy: "hierarchy", + ratio: "ratio", + entered: "entered", + tooltip2: "Add Pause", + SubtitlesTip: "Subtitles will be shown here", + subtitlesSwitch: "Subtitles Switch", + }, + + // 我的视频 + myCreating: { + title: "My Creating", + search: "Search...", + name: "Name", + date: "Date", + videoTitle: "Video title:", + download: "Download", + delete: "Delete", + more: "More", + loading: "Loading , just a moment", + noList: "Nothing here. How about making one ?", + goCreate: "Go create !", + manuscript: "Manuscript", + generating: "Generating", + generationFailed: "Failed", + generationSucceed: "Succeed", + manuscriptText: "Manuscript", + generatingText: "The video is generating, Please wait a moment", + generationFailedText: "The video generate is failed, Reason:", + edit: "Edit", + upSharpness: "Upgrade image quality", + upSharpnessTips: + "If you are satisfied with the preview, you can click here to improve the video sharpness ~", + freeUserTip: "Free user-generated videos are limited to 5 minute", + hasNewVideo: "You have a new video status!", + reset: "reset", + }, + + // 个人账户 + account: { + title: "My Account", + reSetPwd: "Reset password", + editInfo: "Edit personal information", + until: "Until", + getMore: "UPGRADE", + copyCode: "Copy invitation code", + saveCode: "Save QR code", + buyHistory: "OrderHistory", + inviteHistory: "InviteHistory", + orderID: "OrderID", + price: "Price", + date: "Date", + successed: "Successed", + failed: "Failed", + msg1: "Obtain one video production time", + inputCode: "Please enter the invitation code", + submit: "Submit", + noListOrder: "没有数据哦,快去邀请更多人加入我们吧~", + noListInvite: "No data, please invite more people to join us", + }, + + // 商店 + shop: { + title: "Shop", + Trial: "Trial", + Lite: "Lite", + Pro: "Pro", + Adv: "Advanced", + Ent: "Enterprise", + selectPlan: "SELECT PLAN", + WeChatPay: "WeChatPay", + Alipay: "Alipay", + score: "Credits", + day: "day", + second: "seconds", + yes: "yes", + no: "no", + InviteGetscore: "Invite users to get score", + createVideoPrice: "Create video prices", + AIGeneratesPhotoPrices: "AI generates photo unit prices", + UnitChargeDuration: "Unit charge duration", + Language: "Language", + sound: "Sound", + IndividualVideoDuration: "Individual video duration", + Watermark: "Watermark", + AIGeneratedPortrait: "AI generated portrait", + PhotoSinging: "Photo singing", + AIGeneratedText: "The number of times AI generated text", + VideoResolution: "Video resolution", + DigitalPersonTemplate: "Digital person template", + talkUs: "Talk With Us", + }, + + userModel: { + title: "User Model", + }, + + voiceModel: { + title: "Voice Model", + }, + + API: { + title: "API Access", + comingSoon: "Coming Soon", + }, + + apiMangage: { + title: "API Mangage", + }, + + signSucceed: { + title: "Confirm your email", + succeed: "Sign Succeed !", + welcome: "Welcome to Metavatar !", + createSuccess: + "Your account has been created successfully. Enjoy yourself !", + expired: + "Your ticket is expired. You can click the button to resend new email then", + sendSuccess: "The email has been resent, you can check it now", + goLogin: "Login now", + reSend: "Resend", + }, + + modelMangage: { + title: "Model Set", + }, +}; diff --git a/src/lang/package/zh-cn.ts b/src/lang/package/zh-cn.ts new file mode 100644 index 0000000..439489e --- /dev/null +++ b/src/lang/package/zh-cn.ts @@ -0,0 +1,225 @@ +export default { + // 路由国际化 + route: { + dashboard: "首页", + document: "项目文档", + }, + // 公用 + public: { + cancel: "取消", + confirm: "确定", + username: "用户名", + avatar: "头像", + close: "关闭", + }, + // 登录页面国际化 + login: { + title: "慧影", + username: "用户名", + email: "手机号", + password: "密码", + confirmPassword: "确认密码", + login: "登录", + register: "注册", + loginBtn: "登 录", + registerBtn: "注 册", + verifyCode: "验证码", + // signIn: "已有账号?现在登录!", + // signUp: "没有账号?现在加入!", + signIn: "立即登录", + signUp: "立即注册", + forgetPwd: "忘记密码", + forgetPwdTitle: "忘记密码", + SendEmail: "发送短信验证码", + resetPassword: "重置密码", + setNewPwd: "设置新密码", + newPwd: "新密码", + oldPwd: "旧密码", + confirmNewPwd: "确认新密码", + confirm: "确定", + gohome: "进入首页", + SMSCode: "短信验证码", + SMSLogin: "短信登录", + pwsLogin: "账号密码登录", + sendSMS: "发送短信验证码", + setInvitatioCode: "填写邀请码", + invitatioCode: "邀请码", + }, + + // 导航栏国际化 + navbar: { + account: "账户", + logout: "注销", + goLogin: "登录", + LanguageChange: "语言切换", + }, + + // 创作视频 + createVideo: { + title: "创作视频", + availableMould: "可用模板", + personalWarehouse: "个人仓库", + typeScript: "输入文案", + uploadAudio: "上传音频", + recordedAudio: "录制音频", + startRecorded: "开始录制", + stopRecorded: "停止录制", + aiScript: "AI生成文本", + typeTitle: "输入标题", + confirm: "确定", + aiText: "在此输入", + languages: "选择语言", + voices: "选择声音", + styles: "选择风格", + reselect: "重新选择", + sureUpload: "确认上传", + generateVideo: "生成视频", + saveVideo: "保存草稿", + select: "请选择", + music: "音乐", + Aipicture: "Ai生成图片", + AipictureTip: "请自定义您的图片", + chooseMusic: "选择音乐", + searchMusic: "搜索音乐", + copywriting: "文案", + comingSoon: "敬请期待", + BackgroundImage: "背景", + material: "素材", + subtitles: "字幕", + sex: "性别", + videoRatio: "视频比例", + all: "全部", + male: "男性", + female: "女性", + landscape: "横屏", + portrait: "竖屏", + systemBGI: "系统背景", + personBGI: "个人背景", + systemMaterial: "系统素材", + personMaterial: "个人素材", + abscissa: "横坐标", + ordinate: "纵坐标", + hierarchy: "层级", + ratio: "缩放倍率", + entered: "已输入", + tooltip2: "添加停顿", + SubtitlesTip: "字幕将在此处展示", + subtitlesSwitch: "字幕开关", + }, + + // 我的视频 + myCreating: { + title: "我的视频", + search: "搜索:我的视频", + name: "名称", + date: "时间", + videoTitle: "视频标题:", + download: "下载", + delete: "删除", + more: "更多", + loading: "加载中,请稍等哦", + noList: "暂无没有作品哦,快去尝试制作你的第一个视频吧!", + goCreate: "去创作", + manuscript: "草稿", + generating: "生成中", + generationFailed: "生成失败", + generationSucceed: "生成成功", + manuscriptText: "草稿", + generatingText: "视频正在生成中,请稍等哦", + generationFailedText: "视频生成失败。原因:", + edit: "编辑", + upSharpness: "升级画质", + upSharpnessTips: "如果对预览效果满意,您可以点击此处来提升视频清晰度~", + freeUserTip: "免费用户生成视频时长限制为5分钟", + hasNewVideo: "您有新的视频状态!", + reset: "重置", + }, + + // 个人账户 + account: { + title: "个人账户", + reSetPwd: "修改密码", + editInfo: "编辑个人信息", + until: "有效期至", + getMore: "升级套餐", + copyCode: "复制邀请码", + saveCode: "保存二维码", + buyHistory: "购买记录", + inviteHistory: "邀请记录", + orderID: "订单号", + price: "支付金额", + date: "购买时间", + successed: "支付成功", + failed: "支付失败", + msg1: "获得视频制作次数一次", + inputCode: "请输入邀请码", + submit: "提交", + noListOrder: "没有数据哦,快去邀请更多人加入我们吧~", + noListInvite: "没有数据哦,快去邀请更多人加入我们吧~", + }, + + // 商店 + shop: { + title: "商店", + Trial: "尝鲜版", + Lite: "基础版", + Pro: "专业版", + Adv: "加强版", + Ent: "企业版", + selectPlan: "方案选择", + WeChatPay: "微信支付", + Alipay: "支付宝支付", + score: "积分", + day: "天", + second: "秒", + yes: "是", + no: "否", + InviteGetscore: "邀请用户得到的积分", + createVideoPrice: "创建视频单价", + AIGeneratesPhotoPrices: "AI生成照片单价", + UnitChargeDuration: "单价收费时长", + Language: "语言", + sound: "声音", + IndividualVideoDuration: "单个视频时长", + Watermark: "水印", + AIGeneratedPortrait: "AI生成人像", + PhotoSinging: "照片唱歌", + AIGeneratedText: "AI生成脚本次数", + VideoResolution: "视频分辨率", + DigitalPersonTemplate: "数字人模版", + talkUs: "联系我们", + }, + + userModel: { + title: "定制分身", + }, + + voiceModel: { + title: "定制声纹", + }, + + API: { + title: "API 接口", + comingSoon: "敬请期待", + }, + + apiMangage: { + title: "API 管理", + }, + + signSucceed: { + title: "确认邮箱", + succeed: "注册成功", + welcome: "欢迎加入Metavatar !", + createSuccess: "您的账户已成功创建", + expired: + "您的通行密钥已过期。稍后,您可以点击下面的按钮重新发送一封新的邮件。", + sendSuccess: "您的邮件已重新发送,请注意查收。", + goLogin: "即将登录", + reSend: "重新发送", + }, + + modelMangage: { + title: "模板管理", + }, +}; diff --git a/src/layout/components/AppMain.vue b/src/layout/components/AppMain.vue new file mode 100644 index 0000000..1a6a781 --- /dev/null +++ b/src/layout/components/AppMain.vue @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + diff --git a/src/layout/components/Navbar.vue b/src/layout/components/Navbar.vue new file mode 100644 index 0000000..1e78c03 --- /dev/null +++ b/src/layout/components/Navbar.vue @@ -0,0 +1,181 @@ + + + + + + + + + + + + + diff --git a/src/layout/components/Sidebar/Link.vue b/src/layout/components/Sidebar/Link.vue new file mode 100644 index 0000000..8e9337c --- /dev/null +++ b/src/layout/components/Sidebar/Link.vue @@ -0,0 +1,37 @@ + + + + + + + + + + diff --git a/src/layout/components/Sidebar/Logo.vue b/src/layout/components/Sidebar/Logo.vue new file mode 100644 index 0000000..969ba57 --- /dev/null +++ b/src/layout/components/Sidebar/Logo.vue @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + diff --git a/src/layout/components/Sidebar/SidebarItem.vue b/src/layout/components/Sidebar/SidebarItem.vue new file mode 100644 index 0000000..ebb6e2d --- /dev/null +++ b/src/layout/components/Sidebar/SidebarItem.vue @@ -0,0 +1,121 @@ + + + + + + + + + + {{ translateRouteTitleI18n(onlyOneChild.meta.title) }} + + + + + + + + + + {{ + translateRouteTitleI18n(item.meta.title) + }} + + + + + + diff --git a/src/layout/components/Sidebar/index.vue b/src/layout/components/Sidebar/index.vue new file mode 100644 index 0000000..d9bb2cb --- /dev/null +++ b/src/layout/components/Sidebar/index.vue @@ -0,0 +1,130 @@ + + + + + + + + + + {{ $t("createVideo.title") }} + + + + + + {{ $t("createVideo.title") }}123 + + + + + + + + diff --git a/src/layout/components/index.ts b/src/layout/components/index.ts new file mode 100644 index 0000000..26b6ec9 --- /dev/null +++ b/src/layout/components/index.ts @@ -0,0 +1,3 @@ +export { default as Navbar } from './Navbar.vue'; +export { default as AppMain } from './AppMain.vue'; + diff --git a/src/layout/index copy 2.vue b/src/layout/index copy 2.vue new file mode 100644 index 0000000..464d82f --- /dev/null +++ b/src/layout/index copy 2.vue @@ -0,0 +1,119 @@ + + + + + + + + + + + + + + + + + diff --git a/src/layout/index copy.vue b/src/layout/index copy.vue new file mode 100644 index 0000000..3d3ca28 --- /dev/null +++ b/src/layout/index copy.vue @@ -0,0 +1,131 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/layout/index.vue b/src/layout/index.vue new file mode 100644 index 0000000..8a3651a --- /dev/null +++ b/src/layout/index.vue @@ -0,0 +1,259 @@ + + + + + + + + + + + + + + {{ $t("modelMangage.title") }} + + + + + + + + {{ $t("apiMangage.title") }} + + + + + + + + + + + + + + + {{ $t("navbar.goLogin") }} + + + + {{ $t("navbar.logout") }} + + + + + + + + + + + + + + diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..cf0103d --- /dev/null +++ b/src/main.ts @@ -0,0 +1,39 @@ +import { createApp } from "vue"; +import App from "./App.vue"; +import router from "@/router"; +import { setupStore } from "@/store"; +import { setupDirective } from "@/directive"; + +import "@/permission"; + +// 本地SVG图标 +import "virtual:svg-icons-register"; + +// 国际化 +import i18n from "@/lang/index"; + +// 样式 +import "element-plus/theme-chalk/dark/css-vars.css"; +import "@/styles/index.scss"; +import "uno.css"; +import "@/styles/my.scss"; // 引入自己定义的样式 +import 'amfe-flexible'; +import ElementPlus from "element-plus"; +// 引入组件 +import myLoading from "@/components/myLoading.vue"; +import myLoadingIcon from "@/components/myLoadingIcon.vue"; +import VueCropper from 'vue-cropper'; +import 'vue-cropper/dist/index.css' + +const app = createApp(App); +// 全局注册 自定义指令(directive) +setupDirective(app); +// 全局注册 状态管理(store) +setupStore(app); +// element + +app.use(ElementPlus); + +app.use(router).use(i18n).use(VueCropper).mount("#app"); +// 注册全局组件 .component('VueCropper',VueCropper) +app.component('myLoading',myLoading).component('myLoadingIcon',myLoadingIcon) \ No newline at end of file diff --git a/src/permission.ts b/src/permission.ts new file mode 100644 index 0000000..c13c976 --- /dev/null +++ b/src/permission.ts @@ -0,0 +1,45 @@ +import router from "@/router"; +import { usePermissionStoreHook } from "@/store/modules/permission"; +import { number } from "echarts"; + +import NProgress from "nprogress"; +import "nprogress/nprogress.css"; +NProgress.configure({ showSpinner: false }); // 进度条 + +const permissionStore = usePermissionStoreHook(); + +// 白名单路由 +const whiteList = ["/login", "/signSucceed",'/createVideo','/moveImg']; + +router.beforeEach(async (to, from, next) => { + NProgress.start(); + const AccessToken = localStorage.getItem("AccessToken"); + // 已登录 + if (AccessToken) { + // 重置密码时可以去往login页面 + if ((to.params.type as string) == "4") { + next(); + } + if (to.query.resetPwd) { + next(); + } + if (to.path === "/login") { + next({ path: "/createVideo" }); + } else { + next(); + } + // 未登录 + } else { + // 未登录可以访问白名单页面 + if (whiteList.indexOf(to.path) !== -1) { + next(); + } else { + // 不在白名单内,跳转登录页 + next({ path: "/login" }); + } + } +}); + +router.afterEach(() => { + NProgress.done(); +}); diff --git a/src/router/index.ts b/src/router/index.ts new file mode 100644 index 0000000..9598aa7 --- /dev/null +++ b/src/router/index.ts @@ -0,0 +1,132 @@ +import { createRouter, createWebHashHistory, RouteRecordRaw } from "vue-router"; +import { getCurrentInstance } from "vue"; + +export const Layout = () => import("@/layout/index.vue"); + +// 静态路由 +export const constantRoutes: RouteRecordRaw[] = [ + + // 登录 + { + path: "/login", + component: () => import("@/views/login/index.vue"), + meta: { hidden: true }, + name:'login' + }, + { + path: "/signSucceed", + component: () => import("@/views/signSucceed/index.vue"), + meta: { hidden: true }, + }, + // 创建视频 + { + path: "/", + component: Layout, + redirect: "/createVideo", + children: [ + { + path: "createVideo", + component: () => import("@/views/createVideo/index.vue"), + name: "createVideo", + meta: { + title: "createVideo", + icon: "homepage", + affix: true, + name: "createVideo.title", + }, + }, + ], + }, + // 视频列表 + { + path: "/", + component: Layout, + redirect: "/myCreating", + children: [ + { + path: "myCreating", + component: () => import("@/views/myCreating/index.vue"), + name: "myCreating", + meta: { + title: "myCreating", + icon: "homepage", + affix: true, + name: "myCreating.title", + }, + }, + ], + }, { + path: "/", + component: Layout, + redirect: "/myStore", + children: [ + { + path: "myStore", + component: () => import("@/views/myStore/index.vue"), + name: "myStore", + meta: { + title: "myStore", + icon: "homepage", + affix: true, + name: "myStore.title", + }, + }, + ], + }, { + path: "/", + component: Layout, + redirect: "/myAPI", + children: [ + { + path: "myAPI", + component: () => import("@/views/myAPI/index.vue"), + name: "myAPI", + meta: { + title: "myAPI", + icon: "homepage", + affix: true, + name: "API.title", + }, + }, + ], + }, + { + path: "/", + component: Layout, + redirect: "/moveImg", + children: [ + { + path: "moveImg", + component: () => import("@/views/moveImg/index.vue"), + name: "moveImg", + meta: { + title: "moveImg", + icon: "homepage", + affix: true, + name: "moveImg.title", + }, + }, + ], + }, + +]; + +/** + * 创建路由 + */ +const router = createRouter({ + history: createWebHashHistory(), + routes: constantRoutes as RouteRecordRaw[], + // 刷新时,滚动条位置还原 + // 2023-5-23 添加平滑滚动 YWQ + scrollBehavior: () => ({ behavior: "smooth", 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..190f4f7 --- /dev/null +++ b/src/settings.ts @@ -0,0 +1,62 @@ +// 系统设置 +interface DefaultSettings { + /** + * 系统title + */ + title: string; + + /** + * 是否显示设置 + */ + showSettings: boolean; + /** + * 是否显示多标签导航 + */ + tagsView: boolean; + /** + *是否固定头部 + */ + fixedHeader: boolean; + /** + * 是否显示侧边栏Logo + */ + sidebarLogo: boolean; + /** + * 导航栏布局 + */ + layout: string; + /** + * 主题模式 + */ + theme: string; + + /** + * 布局大小 + */ + size: string; + + /** + * 语言 + */ + language: string; +} + +const defaultSettings: DefaultSettings = { + title: "Metavatar-慧影", + showSettings: false, + tagsView: false, + fixedHeader: false, + sidebarLogo: true, + layout: "left", + /** + * 主题模式 + * + * dark:暗黑模式 + * light: 明亮模式 + */ + theme: "dark", + size: "default", // default |large |small + language: "zh-cn", // zh-cn| en +}; + +export default defaultSettings; diff --git a/src/store/index.ts b/src/store/index.ts new file mode 100644 index 0000000..e4e5a6b --- /dev/null +++ b/src/store/index.ts @@ -0,0 +1,14 @@ +import type { App } from 'vue'; +import { createPinia } from 'pinia'; +import piniaPluginPersistedstate from 'pinia-plugin-persistedstate' + +const store = createPinia(); + +store.use(piniaPluginPersistedstate ) + +// 全局注册 store +export function setupStore(app: App) { + app.use(store); +} + +export { store }; diff --git a/src/store/modules/app.ts b/src/store/modules/app.ts new file mode 100644 index 0000000..cd1c186 --- /dev/null +++ b/src/store/modules/app.ts @@ -0,0 +1,85 @@ +import { defineStore } from "pinia"; +import { useStorage } from "@vueuse/core"; +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"; + +// 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 locale = computed(() => { + if (language?.value == "en") { + return en; + } else { + return zhCn; + } + }); + + // actions + function toggleSidebar(withoutAnimation: boolean) { + sidebar.opened = !sidebar.opened; + sidebar.withoutAnimation = withoutAnimation; + if (sidebar.opened) { + sidebarStatus.value = "opened"; + } else { + sidebarStatus.value = "closed"; + } + } + + function closeSideBar(withoutAnimation: boolean) { + sidebar.opened = false; + sidebar.withoutAnimation = withoutAnimation; + sidebarStatus.value = "closed"; + } + + function openSideBar(withoutAnimation: boolean) { + sidebar.opened = true; + sidebar.withoutAnimation = withoutAnimation; + 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; + } + + return { + device, + sidebar, + language, + locale, + size, + toggleDevice, + changeSize, + changeLanguage, + toggleSidebar, + closeSideBar, + openSideBar, + }; +}); diff --git a/src/store/modules/permission.ts b/src/store/modules/permission.ts new file mode 100644 index 0000000..8a69655 --- /dev/null +++ b/src/store/modules/permission.ts @@ -0,0 +1,106 @@ +import { RouteRecordRaw } from "vue-router"; +import { defineStore } from "pinia"; +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 !== undefined) { + return (route.meta.roles as string[]).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 (hasPermission(roles, tmpRoute)) { + if (tmpRoute.component?.toString() == "Layout") { + tmpRoute.component = Layout; + console.log(); + } 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); + }); + }); + } + return { routes, setRoutes, generateRoutes }; +}); + +// 非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..69466c7 --- /dev/null +++ b/src/store/modules/settings.ts @@ -0,0 +1,48 @@ +import { defineStore } from "pinia"; +import defaultSettings from "@/settings"; +import { useStorage } from "@vueuse/core"; + +export const useSettingsStore = defineStore("setting", () => { + // state + + const tagsView = useStorage("tagsView", defaultSettings.tagsView); + + const showSettings = ref(defaultSettings.showSettings); + const fixedHeader = ref(defaultSettings.fixedHeader); + const sidebarLogo = ref(defaultSettings.sidebarLogo); + + const layout = useStorage("layout", defaultSettings.layout); + + // actions + function changeSetting(param: { key: string; value: any }) { + const { key, value } = param; + switch (key) { + case "showSettings": + showSettings.value = value; + break; + case "fixedHeader": + fixedHeader.value = value; + break; + case "tagsView": + tagsView.value = value; + break; + case "sidevarLogo": + sidebarLogo.value = value; + break; + case "layout": + layout.value = value; + break; + default: + break; + } + } + + return { + showSettings, + tagsView, + fixedHeader, + sidebarLogo, + layout, + changeSetting, + }; +}); diff --git a/src/store/modules/tagsView.ts b/src/store/modules/tagsView.ts new file mode 100644 index 0000000..93c09a5 --- /dev/null +++ b/src/store/modules/tagsView.ts @@ -0,0 +1,218 @@ +import { defineStore } from "pinia"; +import { ref } from "vue"; +import { RouteLocationNormalized } from "vue-router"; + +export interface TagView extends Partial { + title?: string; +} + +// setup +export const useTagsViewStore = defineStore("tagsView", () => { + // state + const visitedViews = ref([]); + const cachedViews = ref([]); + + // actions + function addVisitedView(view: TagView) { + if (visitedViews.value.some((v) => v.path === view.path)) return; + if (view.meta && view.meta.affix) { + visitedViews.value.unshift( + Object.assign({}, view, { + title: view.meta?.title || "no-name", + }) + ); + } else { + visitedViews.value.push( + Object.assign({}, view, { + title: view.meta?.title || "no-name", + }) + ); + } + } + + function addCachedView(view: TagView) { + const viewName = view.name as string; + if (cachedViews.value.includes(viewName)) return; + if (view.meta?.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 as string; + 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.meta?.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) => { + // affix:true 固定tag,例如“首页” + if (index >= currIndex || (item.meta && item.meta.affix)) { + return true; + } + + const cacheIndex = cachedViews.value.indexOf(item.name as string); + 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) => { + // affix:true 固定tag,例如“首页” + if (index <= currIndex || (item.meta && item.meta.affix)) { + return true; + } + + const cacheIndex = cachedViews.value.indexOf(item.name as string); + if (cacheIndex > -1) { + cachedViews.value.splice(cacheIndex, 1); + } + return false; + }); + resolve({ + visitedViews: [...visitedViews.value], + }); + }); + } + + function delAllViews() { + return new Promise((resolve) => { + const affixTags = visitedViews.value.filter((tag) => tag.meta?.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.meta?.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..72d806d --- /dev/null +++ b/src/store/modules/user.ts @@ -0,0 +1,104 @@ +import { defineStore } from "pinia"; + +import { loginApi, logoutApi } from "@/api/auth"; +import { getUserInfo } from "@/api/user"; +import { resetRouter } from "@/router"; +import { store } from "@/store"; + +import { LoginData } from "@/api/auth/types"; +import { UserInfo } from "@/api/user/types"; + +import { useStorage } from "@vueuse/core"; + +export const useUserStore = defineStore("user", () => { + // state + const token = useStorage("accessToken", ""); + const nickname = ref(""); + const avatar = ref(""); + const roles = ref>([]); // 用户角色编码集合 → 判断路由权限 + const perms = ref>([]); // 用户权限编码集合 → 判断按钮权限 + + /** + * 登录调用 + * + * @param {LoginData} + * @returns + */ + function login(loginData: LoginData) { + return new Promise((resolve, reject) => { + loginApi(loginData) + .then((response) => { + const { tokenType, accessToken } = response.data; + token.value = tokenType + " " + accessToken; // Bearer eyJhbGciOiJIUzI1NiJ9.xxx.xxx + resolve(); + }) + .catch((error) => { + reject(error); + }); + }); + } + + // 获取信息(用户昵称、头像、角色集合、权限集合) + function getInfo() { + return new Promise((resolve, reject) => { + getUserInfo() + .then(({ data }) => { + if (!data) { + return reject("Verification failed, please Login again."); + } + if (!data.roles || data.roles.length <= 0) { + reject("getUserInfo: roles must be a non-null array!"); + } + nickname.value = data.nickname; + avatar.value = data.avatar; + roles.value = data.roles; + perms.value = data.perms; + resolve(data); + }) + .catch((error) => { + reject(error); + }); + }); + } + + // 注销 + function logout() { + return new Promise((resolve, reject) => { + logoutApi() + .then(() => { + resetRouter(); + resetToken(); + location.reload(); // 清空路由 + resolve(); + }) + .catch((error) => { + reject(error); + }); + }); + } + + // 重置 + function resetToken() { + token.value = ""; + nickname.value = ""; + avatar.value = ""; + roles.value = []; + perms.value = []; + } + return { + token, + nickname, + avatar, + roles, + perms, + login, + getInfo, + logout, + resetToken, + }; +}); + +// 非setup +export function useUserStoreHook() { + return useUserStore(store); +} diff --git a/src/store/modules/userInfo.js b/src/store/modules/userInfo.js new file mode 100644 index 0000000..bfd1626 --- /dev/null +++ b/src/store/modules/userInfo.js @@ -0,0 +1,27 @@ +import { defineStore } from "pinia"; +import { store } from "@/store"; +import { + getUserInfoApi, +} from "../../apis/myAccount"; +export const userInfoModules = defineStore("userInfo", () => { + // state + const userInfo = ref({ + avatarUrl: null + }) + + // 登录获取用户信息 + async function getUserInfo() { + const res = await getUserInfoApi() + userInfo.value = res.data + } + // 退出清除用户信息 + function loginOut(params) { + userInfo.value = { + avatarUrl: null + } + } + return { userInfo, getUserInfo, loginOut }; +}, + { + persist: true, + }); \ No newline at end of file diff --git a/src/styles/dark.scss b/src/styles/dark.scss new file mode 100644 index 0000000..5b719ff --- /dev/null +++ b/src/styles/dark.scss @@ -0,0 +1,33 @@ +html.dark { + --menuBg: var(--el-bg-color-overlay); + --menuText: #fff; + --menuActiveText: var(--el-menu-active-color); + --menuHover: rgb(0 0 0 / 20%); + --subMenuBg: var(--el-menu-bg-color); + --subMenuActiveText: var(--el-menu-active-color); + --subMenuHover: rgb(0 0 0 / 20%); + + .navbar { + color: var(--el-text-color-regular); + background-color: var(--el-bg-color); + + .setting-container .setting-item:hover { + background: var(--el-fill-color-light); + } + } + + .right-panel-btn { + background-color: var(--el-color-primary-dark); + } + + .svg-icon, + svg { + fill: var(--el-text-color-regular); + } + + .sidebar-container { + .el-menu-item.is-active .svg-icon { + fill: var(--el-color-primary); + } + } +} diff --git a/src/styles/index.scss b/src/styles/index.scss new file mode 100644 index 0000000..5f0e69f --- /dev/null +++ b/src/styles/index.scss @@ -0,0 +1,16 @@ +@import "./sidebar"; +@import "./reset"; +@import "./dark"; + +.app-container { + padding: 20px; + + .search { + 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: 4px; + box-shadow: var(--el-box-shadow-light); + } +} diff --git a/src/styles/my.scss b/src/styles/my.scss new file mode 100644 index 0000000..128f32a --- /dev/null +++ b/src/styles/my.scss @@ -0,0 +1,125 @@ +body { + font-family: Arial, Microsoft YaHei, 黑体, 宋体, sans-serif; + font-size: 14px; +} + +.inmiddle { + position: absolute; + // top: 50%; + left: 50%; + transform: translateX(-50%); + // transform: translate(-50%, -50%); +} + +div { + cursor: default; +} + +// 鼠标放上去变小手 +.pointer { + cursor: pointer; +} + +.myBtn { + padding: 5px 10px; + text-align: center; + border: 1px solid #000; + border-radius: 5px; + user-select: none; +} + +p { + margin: 0; +} + +// y轴滚动盒子auto +.scrolly { + overflow-y: auto; + /* 启用Y轴滚动 */ + // overflow-y: scroll; /* 启用Y轴滚动 */ + scrollbar-width: thin; + /* 在支持的浏览器上显示细的滚动条 */ +} + +.scrolly::-webkit-scrollbar { + display: none; + width: 0; +} + +// 主题色 +.themeColor { + background: linear-gradient(225deg, + rgb(81 217 202 / 100%), + rgb(114 100 245 / 100%)); +} + +// 自定义按钮 +.customBtn { + padding: 10px 15px; + text-align: center; + background-color: #fff; + color: #000; + transition: all .3s; + border: 1px solid #000; + border-radius: 15px; + cursor: pointer; + + &:hover { + background-color: #000; + color: #fff; + } +} + +// element公共样式 +.el-dialog { + border-radius: 30px !important; +} + +// :deep(.el-input__wrapper.is-focus) { +// box-shadow: 0 0 0 1px #000 !important; +// } + +//#region 滚动条样式 +/* 定义滚动条的样式 */ +::-webkit-scrollbar { + width: 8px; + /* 滚动条宽度 */ + // background-color: #f5f5f5; + /* 滚动条背景色 */ +} + +/* 定义滚动条轨道的样式 */ +::-webkit-scrollbar-track { + // background-color: #f5f5f5; + /* 滚动条轨道背景色 */ +} + +/* 定义滚动条滑块的样式 */ +::-webkit-scrollbar-thumb { + background-color: #888; + /* 滚动条滑块颜色 */ + border-radius: 4px; + /* 滚动条滑块圆角 */ +} + +/* 定义滚动条滑块悬停时的样式 */ +::-webkit-scrollbar-thumb:hover { + background-color: #555; + /* 滚动条滑块悬停时的颜色 */ +} + +//#endregion ---------------------------- + +//#region div内图片居中公共样式 +.boxCentreImg { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} + +//#endregion ---------------------------- + +//#region + +//#endregion ---------------------------- \ No newline at end of file diff --git a/src/styles/reset.scss b/src/styles/reset.scss new file mode 100644 index 0000000..4367236 --- /dev/null +++ b/src/styles/reset.scss @@ -0,0 +1,80 @@ +*, +::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 { + vertical-align: -0.15em; //因icon大小被设置为和字体大小一致,而span等标签的下边缘会和字体的基线对齐,故需设置一个往下的偏移比例,来纠正视觉上的未对齐效果 +} + +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; +} + +div { + padding: 0; + margin: 0; +} diff --git a/src/styles/sidebar.scss b/src/styles/sidebar.scss new file mode 100644 index 0000000..eb19352 --- /dev/null +++ b/src/styles/sidebar.scss @@ -0,0 +1,209 @@ +#app { + .main-container { + position: relative; + min-height: 100%; + margin-left: $sideBarWidth; + transition: margin-left 0.28s; + } + + .sidebar-container { + position: fixed; + top: 0; + bottom: 0; + left: 0; + z-index: 1001; + width: $sideBarWidth !important; + height: 100%; + overflow: hidden; + background-color: $menuBg; + transition: width 0.28s; + + // reset element-ui css + .horizontal-collapse-transition { + transition: 0s width ease-in-out, 0s padding-left ease-in-out, + 0s padding-right ease-in-out; + } + + .scrollbar-wrapper { + overflow-x: hidden !important; + } + + .el-scrollbar__bar.is-vertical { + right: 0; + } + + .el-scrollbar { + height: 100%; + } + + &.has-logo { + // background-color: #1b213a; + background-color: #000; + + .el-scrollbar { + height: calc(100% - 50px); + } + } + + .is-horizontal { + display: none; + } + + .svg-icon { + margin-right: 16px; + } + + .sub-el-icon { + margin-right: 12px; + margin-left: -2px; + } + + .el-menu { + width: 100% !important; + height: 100%; + border: none; + } + + // menu hover + .el-sub-menu__title { + &:hover { + background-color: $menuHover !important; + } + } + + .is-active>.el-sub-menu__title { + color: $subMenuActiveText !important; + } + + & .nest-menu .el-sub-menu>.el-sub-menu__title, + & .el-sub-menu .el-menu-item { + min-width: $sideBarWidth !important; + background-color: $subMenuBg !important; + + &:hover { + background-color: $subMenuHover !important; + } + } + } + + .hideSidebar { + .sidebar-container { + // width: 54px !important; + + .svg-icon { + margin-right: 0; + } + } + + .main-container { + // margin-left: 54px; + } + + .el-sub-menu { + overflow: hidden; + + &>.el-sub-menu__title { + padding: 0 !important; + + .svg-icon { + margin-left: 20px; + } + + .sub-el-icon { + margin-left: 19px; + } + + .el-sub-menu__icon-arrow { + display: none; + } + } + } + + .el-menu--collapse { + .el-sub-menu { + &>.el-sub-menu__title { + &>span { + display: inline-block; + width: 0; + height: 0; + overflow: hidden; + visibility: hidden; + } + } + } + } + } + + .el-menu--collapse .el-menu .el-sub-menu { + min-width: $sideBarWidth !important; + } + + // mobile responsive + .mobile { + .main-container { + margin-left: 0; + } + + .sidebar-container { + width: $sideBarWidth !important; + transition: transform 0.28s; + } + + &.hideSidebar { + .sidebar-container { + pointer-events: none; + transition-duration: 0.3s; + transform: translate3d(-$sideBarWidth, 0, 0); + } + } + } + + .withoutAnimation { + + .main-container, + .sidebar-container { + transition: none; + } + } +} + +// when menu collapsed +.el-menu--vertical { + &>.el-menu { + .svg-icon { + margin-right: 16px; + } + + .sub-el-icon { + margin-right: 12px; + margin-left: -2px; + } + } + + .nest-menu .el-sub-menu>.el-sub-menu__title, + .el-menu-item { + &:hover { + // you can use $subMenuHover + background-color: $menuHover !important; + } + } + + // the scroll bar appears when the subMenu is too long + >.el-menu--popup { + max-height: 100vh; + overflow-y: auto; + + &::-webkit-scrollbar-track-piece { + background: #d3dce6; + } + + &::-webkit-scrollbar { + width: 6px; + } + + &::-webkit-scrollbar-thumb { + background: #99a9bf; + border-radius: 20px; + } + } +} \ No newline at end of file diff --git a/src/styles/variables.module.scss b/src/styles/variables.module.scss new file mode 100644 index 0000000..cbc114a --- /dev/null +++ b/src/styles/variables.module.scss @@ -0,0 +1,6 @@ +// 导出 variables.module.scss 变量提供给TypeScript使用 +:export { + menuBg: $menuBg; + menuText: $menuText; + menuActiveText: $menuActiveText +} \ No newline at end of file diff --git a/src/styles/variables.scss b/src/styles/variables.scss new file mode 100644 index 0000000..9aa38cc --- /dev/null +++ b/src/styles/variables.scss @@ -0,0 +1,27 @@ +// 全局SCSS变量 + +:root { + --menuBg: #304156; + --menuText: #bfcbd9; + --menuActiveText: #409eff; + // 左侧菜单鼠标经过颜色 + --menuHover: #000; + // --menuHover: #263445; + // 左侧子菜单颜色 + --subMenuBg: #000; + // --subMenuBg: #1f2d3d; + --subMenuActiveText: #f4f4f5; + // --subMenuHover: #001528; + --subMenuHover: #000; +} + +$menuBg: var(--menuBg); +$menuText: var(--menuText); +$menuActiveText: var(--menuActiveText); +$menuHover: var(--menuHover); + +$subMenuBg: var(--subMenuBg); +$subMenuActiveText: var(--subMenuActiveText); +$subMenuHover: var(--subMenuHover); + +$sideBarWidth: 210px; \ No newline at end of file diff --git a/src/types/auto-imports.d.ts b/src/types/auto-imports.d.ts new file mode 100644 index 0000000..e5c7bdb --- /dev/null +++ b/src/types/auto-imports.d.ts @@ -0,0 +1,538 @@ +// 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 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 createReactiveFn: typeof import('@vueuse/core')['createReactiveFn'] + const createSharedComposable: typeof import('@vueuse/core')['createSharedComposable'] + const createUnrefFn: typeof import('@vueuse/core')['createUnrefFn'] + const customRef: typeof import('vue')['customRef'] + const debouncedRef: typeof import('@vueuse/core')['debouncedRef'] + const debouncedWatch: typeof import('@vueuse/core')['debouncedWatch'] + const defineAsyncComponent: typeof import('vue')['defineAsyncComponent'] + const defineComponent: typeof import('vue')['defineComponent'] + const eagerComputed: typeof import('@vueuse/core')['eagerComputed'] + const effectScope: typeof import('vue')['effectScope'] + const extendRef: typeof import('@vueuse/core')['extendRef'] + const getCurrentInstance: typeof import('vue')['getCurrentInstance'] + const getCurrentScope: typeof import('vue')['getCurrentScope'] + const h: typeof import('vue')['h'] + const ignorableWatch: typeof import('@vueuse/core')['ignorableWatch'] + const inject: typeof import('vue')['inject'] + const isDefined: typeof import('@vueuse/core')['isDefined'] + const isProxy: typeof import('vue')['isProxy'] + const isReactive: typeof import('vue')['isReactive'] + const isReadonly: typeof import('vue')['isReadonly'] + const isRef: typeof import('vue')['isRef'] + const makeDestructurable: typeof import('@vueuse/core')['makeDestructurable'] + const markRaw: typeof import('vue')['markRaw'] + const nextTick: typeof import('vue')['nextTick'] + const onActivated: typeof import('vue')['onActivated'] + const onBeforeMount: typeof import('vue')['onBeforeMount'] + const onBeforeUnmount: typeof import('vue')['onBeforeUnmount'] + const onBeforeUpdate: typeof import('vue')['onBeforeUpdate'] + const onClickOutside: typeof import('@vueuse/core')['onClickOutside'] + const onDeactivated: typeof import('vue')['onDeactivated'] + const onErrorCaptured: typeof import('vue')['onErrorCaptured'] + const onKeyStroke: typeof import('@vueuse/core')['onKeyStroke'] + const onLongPress: typeof import('@vueuse/core')['onLongPress'] + const onMounted: typeof import('vue')['onMounted'] + const onRenderTracked: typeof import('vue')['onRenderTracked'] + const onRenderTriggered: typeof import('vue')['onRenderTriggered'] + const onScopeDispose: typeof import('vue')['onScopeDispose'] + const onServerPrefetch: typeof import('vue')['onServerPrefetch'] + const onStartTyping: typeof import('@vueuse/core')['onStartTyping'] + const onUnmounted: typeof import('vue')['onUnmounted'] + const onUpdated: typeof import('vue')['onUpdated'] + const pausableWatch: typeof import('@vueuse/core')['pausableWatch'] + const provide: typeof import('vue')['provide'] + const reactify: typeof import('@vueuse/core')['reactify'] + const reactifyObject: typeof import('@vueuse/core')['reactifyObject'] + const reactive: typeof import('vue')['reactive'] + const reactiveComputed: typeof import('@vueuse/core')['reactiveComputed'] + const reactiveOmit: typeof import('@vueuse/core')['reactiveOmit'] + const reactivePick: typeof import('@vueuse/core')['reactivePick'] + const readonly: typeof import('vue')['readonly'] + const ref: typeof import('vue')['ref'] + const refAutoReset: typeof import('@vueuse/core')['refAutoReset'] + const refDebounced: typeof import('@vueuse/core')['refDebounced'] + const refDefault: typeof import('@vueuse/core')['refDefault'] + const refThrottled: typeof import('@vueuse/core')['refThrottled'] + const refWithControl: typeof import('@vueuse/core')['refWithControl'] + const resolveComponent: typeof import('vue')['resolveComponent'] + const resolveDirective: typeof import('vue')['resolveDirective'] + const resolveRef: typeof import('@vueuse/core')['resolveRef'] + const resolveUnref: typeof import('@vueuse/core')['resolveUnref'] + const shallowReactive: typeof import('vue')['shallowReactive'] + const shallowReadonly: typeof import('vue')['shallowReadonly'] + const shallowRef: typeof import('vue')['shallowRef'] + const syncRef: typeof import('@vueuse/core')['syncRef'] + const syncRefs: typeof import('@vueuse/core')['syncRefs'] + const templateRef: typeof import('@vueuse/core')['templateRef'] + const throttledRef: typeof import('@vueuse/core')['throttledRef'] + const throttledWatch: typeof import('@vueuse/core')['throttledWatch'] + const toRaw: typeof import('vue')['toRaw'] + const toReactive: typeof import('@vueuse/core')['toReactive'] + const toRef: typeof import('vue')['toRef'] + const toRefs: typeof import('vue')['toRefs'] + const triggerRef: typeof import('vue')['triggerRef'] + const tryOnBeforeMount: typeof import('@vueuse/core')['tryOnBeforeMount'] + const tryOnBeforeUnmount: typeof import('@vueuse/core')['tryOnBeforeUnmount'] + const tryOnMounted: typeof import('@vueuse/core')['tryOnMounted'] + const tryOnScopeDispose: typeof import('@vueuse/core')['tryOnScopeDispose'] + const tryOnUnmounted: typeof import('@vueuse/core')['tryOnUnmounted'] + const unref: typeof import('vue')['unref'] + const unrefElement: typeof import('@vueuse/core')['unrefElement'] + const until: typeof import('@vueuse/core')['until'] + const useActiveElement: typeof import('@vueuse/core')['useActiveElement'] + const useArrayEvery: typeof import('@vueuse/core')['useArrayEvery'] + const useArrayFilter: typeof import('@vueuse/core')['useArrayFilter'] + const useArrayFind: typeof import('@vueuse/core')['useArrayFind'] + const useArrayFindIndex: typeof import('@vueuse/core')['useArrayFindIndex'] + const useArrayFindLast: typeof import('@vueuse/core')['useArrayFindLast'] + 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 useCloned: typeof import('@vueuse/core')['useCloned'] + const useColorMode: typeof import('@vueuse/core')['useColorMode'] + const useConfirmDialog: typeof import('@vueuse/core')['useConfirmDialog'] + const useCounter: typeof import('@vueuse/core')['useCounter'] + const useCssModule: typeof import('vue')['useCssModule'] + const useCssVar: typeof import('@vueuse/core')['useCssVar'] + const useCssVars: typeof import('vue')['useCssVars'] + const useCurrentElement: typeof import('@vueuse/core')['useCurrentElement'] + const useCycleList: typeof import('@vueuse/core')['useCycleList'] + const useDark: typeof import('@vueuse/core')['useDark'] + const useDateFormat: typeof import('@vueuse/core')['useDateFormat'] + const useDebounce: typeof import('@vueuse/core')['useDebounce'] + const useDebounceFn: typeof import('@vueuse/core')['useDebounceFn'] + const useDebouncedRefHistory: typeof import('@vueuse/core')['useDebouncedRefHistory'] + const useDeviceMotion: typeof import('@vueuse/core')['useDeviceMotion'] + const useDeviceOrientation: typeof import('@vueuse/core')['useDeviceOrientation'] + const useDevicePixelRatio: typeof import('@vueuse/core')['useDevicePixelRatio'] + const useDevicesList: typeof import('@vueuse/core')['useDevicesList'] + const useDisplayMedia: typeof import('@vueuse/core')['useDisplayMedia'] + const useDocumentVisibility: typeof import('@vueuse/core')['useDocumentVisibility'] + const useDraggable: typeof import('@vueuse/core')['useDraggable'] + const useDropZone: typeof import('@vueuse/core')['useDropZone'] + const useElementBounding: typeof import('@vueuse/core')['useElementBounding'] + const useElementByPoint: typeof import('@vueuse/core')['useElementByPoint'] + const useElementHover: typeof import('@vueuse/core')['useElementHover'] + const useElementSize: typeof import('@vueuse/core')['useElementSize'] + const useElementVisibility: typeof import('@vueuse/core')['useElementVisibility'] + const useEventBus: typeof import('@vueuse/core')['useEventBus'] + const useEventListener: typeof import('@vueuse/core')['useEventListener'] + const useEventSource: typeof import('@vueuse/core')['useEventSource'] + const useEyeDropper: typeof import('@vueuse/core')['useEyeDropper'] + const useFavicon: typeof import('@vueuse/core')['useFavicon'] + const useFetch: typeof import('@vueuse/core')['useFetch'] + const useFileDialog: typeof import('@vueuse/core')['useFileDialog'] + const useFileSystemAccess: typeof import('@vueuse/core')['useFileSystemAccess'] + const useFocus: typeof import('@vueuse/core')['useFocus'] + const useFocusWithin: typeof import('@vueuse/core')['useFocusWithin'] + const useFps: typeof import('@vueuse/core')['useFps'] + const useFullscreen: typeof import('@vueuse/core')['useFullscreen'] + const useGamepad: typeof import('@vueuse/core')['useGamepad'] + const useGeolocation: typeof import('@vueuse/core')['useGeolocation'] + const useIdle: typeof import('@vueuse/core')['useIdle'] + const useImage: typeof import('@vueuse/core')['useImage'] + const useInfiniteScroll: typeof import('@vueuse/core')['useInfiniteScroll'] + const useIntersectionObserver: typeof import('@vueuse/core')['useIntersectionObserver'] + const useInterval: typeof import('@vueuse/core')['useInterval'] + const useIntervalFn: typeof import('@vueuse/core')['useIntervalFn'] + const useKeyModifier: typeof import('@vueuse/core')['useKeyModifier'] + const useLastChanged: typeof import('@vueuse/core')['useLastChanged'] + const useLocalStorage: typeof import('@vueuse/core')['useLocalStorage'] + const useMagicKeys: typeof import('@vueuse/core')['useMagicKeys'] + const useManualRefHistory: typeof import('@vueuse/core')['useManualRefHistory'] + const useMediaControls: typeof import('@vueuse/core')['useMediaControls'] + const useMediaQuery: typeof import('@vueuse/core')['useMediaQuery'] + const useMemoize: typeof import('@vueuse/core')['useMemoize'] + const useMemory: typeof import('@vueuse/core')['useMemory'] + const useMounted: typeof import('@vueuse/core')['useMounted'] + const useMouse: typeof import('@vueuse/core')['useMouse'] + const useMouseInElement: typeof import('@vueuse/core')['useMouseInElement'] + const useMousePressed: typeof import('@vueuse/core')['useMousePressed'] + const useMutationObserver: typeof import('@vueuse/core')['useMutationObserver'] + const useNavigatorLanguage: typeof import('@vueuse/core')['useNavigatorLanguage'] + const useNetwork: typeof import('@vueuse/core')['useNetwork'] + const useNow: typeof import('@vueuse/core')['useNow'] + const useObjectUrl: typeof import('@vueuse/core')['useObjectUrl'] + const useOffsetPagination: typeof import('@vueuse/core')['useOffsetPagination'] + const useOnline: typeof import('@vueuse/core')['useOnline'] + const usePageLeave: typeof import('@vueuse/core')['usePageLeave'] + const useParallax: typeof import('@vueuse/core')['useParallax'] + const usePermission: typeof import('@vueuse/core')['usePermission'] + const usePointer: typeof import('@vueuse/core')['usePointer'] + const 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 useScreenOrientation: typeof import('@vueuse/core')['useScreenOrientation'] + const useScreenSafeArea: typeof import('@vueuse/core')['useScreenSafeArea'] + const useScriptTag: typeof import('@vueuse/core')['useScriptTag'] + const useScroll: typeof import('@vueuse/core')['useScroll'] + const useScrollLock: typeof import('@vueuse/core')['useScrollLock'] + const useSessionStorage: typeof import('@vueuse/core')['useSessionStorage'] + const useShare: typeof import('@vueuse/core')['useShare'] + const useSlots: typeof import('vue')['useSlots'] + const useSorted: typeof import('@vueuse/core')['useSorted'] + const useSpeechRecognition: typeof import('@vueuse/core')['useSpeechRecognition'] + const useSpeechSynthesis: typeof import('@vueuse/core')['useSpeechSynthesis'] + const useStepper: typeof import('@vueuse/core')['useStepper'] + const useStorage: typeof import('@vueuse/core')['useStorage'] + const useStorageAsync: typeof import('@vueuse/core')['useStorageAsync'] + const useStyleTag: typeof import('@vueuse/core')['useStyleTag'] + const useSupported: typeof import('@vueuse/core')['useSupported'] + const useSwipe: typeof import('@vueuse/core')['useSwipe'] + const useTemplateRefsList: typeof import('@vueuse/core')['useTemplateRefsList'] + const useTextDirection: typeof import('@vueuse/core')['useTextDirection'] + const useTextSelection: typeof import('@vueuse/core')['useTextSelection'] + const useTextareaAutosize: typeof import('@vueuse/core')['useTextareaAutosize'] + const useThrottle: typeof import('@vueuse/core')['useThrottle'] + const useThrottleFn: typeof import('@vueuse/core')['useThrottleFn'] + const useThrottledRefHistory: typeof import('@vueuse/core')['useThrottledRefHistory'] + const useTimeAgo: typeof import('@vueuse/core')['useTimeAgo'] + const useTimeout: typeof import('@vueuse/core')['useTimeout'] + const useTimeoutFn: typeof import('@vueuse/core')['useTimeoutFn'] + const useTimeoutPoll: typeof import('@vueuse/core')['useTimeoutPoll'] + const useTimestamp: typeof import('@vueuse/core')['useTimestamp'] + const useTitle: typeof import('@vueuse/core')['useTitle'] + const useToNumber: typeof import('@vueuse/core')['useToNumber'] + const useToString: typeof import('@vueuse/core')['useToString'] + const useToggle: typeof import('@vueuse/core')['useToggle'] + const useTransition: typeof import('@vueuse/core')['useTransition'] + const useUrlSearchParams: typeof import('@vueuse/core')['useUrlSearchParams'] + const useUserMedia: typeof import('@vueuse/core')['useUserMedia'] + const useVModel: typeof import('@vueuse/core')['useVModel'] + const useVModels: typeof import('@vueuse/core')['useVModels'] + const useVibrate: typeof import('@vueuse/core')['useVibrate'] + const useVirtualList: typeof import('@vueuse/core')['useVirtualList'] + const useWakeLock: typeof import('@vueuse/core')['useWakeLock'] + const useWebNotification: typeof import('@vueuse/core')['useWebNotification'] + const useWebSocket: typeof import('@vueuse/core')['useWebSocket'] + const useWebWorker: typeof import('@vueuse/core')['useWebWorker'] + const useWebWorkerFn: typeof import('@vueuse/core')['useWebWorkerFn'] + const useWindowFocus: typeof import('@vueuse/core')['useWindowFocus'] + const useWindowScroll: typeof import('@vueuse/core')['useWindowScroll'] + const useWindowSize: typeof import('@vueuse/core')['useWindowSize'] + const watch: typeof import('vue')['watch'] + const watchArray: typeof import('@vueuse/core')['watchArray'] + const watchAtMost: typeof import('@vueuse/core')['watchAtMost'] + const watchDebounced: typeof import('@vueuse/core')['watchDebounced'] + const watchEffect: typeof import('vue')['watchEffect'] + const watchIgnorable: typeof import('@vueuse/core')['watchIgnorable'] + const watchOnce: typeof import('@vueuse/core')['watchOnce'] + const watchPausable: typeof import('@vueuse/core')['watchPausable'] + const watchPostEffect: typeof import('vue')['watchPostEffect'] + const watchSyncEffect: typeof import('vue')['watchSyncEffect'] + const watchThrottled: typeof import('@vueuse/core')['watchThrottled'] + const watchTriggerable: typeof import('@vueuse/core')['watchTriggerable'] + const watchWithFilter: typeof import('@vueuse/core')['watchWithFilter'] + const whenever: typeof import('@vueuse/core')['whenever'] +} +// for vue template auto import +import { UnwrapRef } from 'vue' +declare module 'vue' { + interface ComponentCustomProperties { + readonly EffectScope: UnwrapRef + readonly ElForm: UnwrapRef + readonly ElMessage: UnwrapRef + readonly ElMessageBox: UnwrapRef + readonly asyncComputed: UnwrapRef + readonly autoResetRef: UnwrapRef + readonly computed: UnwrapRef + readonly computedAsync: UnwrapRef + readonly computedEager: UnwrapRef + readonly computedInject: UnwrapRef + readonly computedWithControl: UnwrapRef + readonly controlledComputed: UnwrapRef + readonly controlledRef: UnwrapRef + readonly createApp: UnwrapRef + readonly createEventHook: UnwrapRef + readonly createGlobalState: UnwrapRef + readonly createInjectionState: UnwrapRef + readonly createReactiveFn: UnwrapRef + readonly createSharedComposable: UnwrapRef + readonly createUnrefFn: UnwrapRef + readonly customRef: UnwrapRef + readonly debouncedRef: UnwrapRef + readonly debouncedWatch: UnwrapRef + readonly defineAsyncComponent: UnwrapRef + readonly defineComponent: UnwrapRef + readonly eagerComputed: UnwrapRef + readonly effectScope: UnwrapRef + readonly extendRef: UnwrapRef + readonly getCurrentInstance: UnwrapRef + readonly getCurrentScope: UnwrapRef + readonly h: UnwrapRef + readonly ignorableWatch: UnwrapRef + readonly inject: UnwrapRef + readonly isDefined: UnwrapRef + readonly isProxy: UnwrapRef + readonly isReactive: UnwrapRef + readonly isReadonly: UnwrapRef + readonly isRef: UnwrapRef + readonly makeDestructurable: UnwrapRef + readonly markRaw: UnwrapRef + readonly nextTick: UnwrapRef + readonly onActivated: UnwrapRef + readonly onBeforeMount: UnwrapRef + readonly onBeforeUnmount: UnwrapRef + readonly onBeforeUpdate: UnwrapRef + readonly onClickOutside: UnwrapRef + readonly onDeactivated: UnwrapRef + readonly onErrorCaptured: UnwrapRef + readonly onKeyStroke: UnwrapRef + readonly onLongPress: UnwrapRef + readonly onMounted: UnwrapRef + readonly onRenderTracked: UnwrapRef + readonly onRenderTriggered: UnwrapRef + readonly onScopeDispose: UnwrapRef + readonly onServerPrefetch: UnwrapRef + readonly onStartTyping: UnwrapRef + readonly onUnmounted: UnwrapRef + readonly onUpdated: UnwrapRef + readonly pausableWatch: UnwrapRef + readonly provide: UnwrapRef + readonly reactify: UnwrapRef + readonly reactifyObject: UnwrapRef + readonly reactive: UnwrapRef + readonly reactiveComputed: UnwrapRef + readonly reactiveOmit: UnwrapRef + readonly reactivePick: UnwrapRef + readonly readonly: UnwrapRef + readonly ref: UnwrapRef + readonly refAutoReset: UnwrapRef + readonly refDebounced: UnwrapRef + readonly refDefault: UnwrapRef + readonly refThrottled: UnwrapRef + readonly refWithControl: UnwrapRef + readonly resolveComponent: UnwrapRef + readonly resolveDirective: UnwrapRef + readonly resolveRef: UnwrapRef + readonly resolveUnref: UnwrapRef + readonly shallowReactive: UnwrapRef + readonly shallowReadonly: UnwrapRef + readonly shallowRef: UnwrapRef + readonly syncRef: UnwrapRef + readonly syncRefs: UnwrapRef + readonly templateRef: UnwrapRef + readonly throttledRef: UnwrapRef + readonly throttledWatch: UnwrapRef + readonly toRaw: UnwrapRef + readonly toReactive: UnwrapRef + readonly toRef: UnwrapRef + readonly toRefs: UnwrapRef + readonly triggerRef: UnwrapRef + readonly tryOnBeforeMount: UnwrapRef + readonly tryOnBeforeUnmount: UnwrapRef + readonly tryOnMounted: UnwrapRef + readonly tryOnScopeDispose: UnwrapRef + readonly tryOnUnmounted: UnwrapRef + readonly unref: UnwrapRef + readonly unrefElement: UnwrapRef + readonly until: UnwrapRef + readonly useActiveElement: UnwrapRef + readonly useArrayEvery: UnwrapRef + readonly useArrayFilter: UnwrapRef + readonly useArrayFind: UnwrapRef + readonly useArrayFindIndex: UnwrapRef + readonly useArrayFindLast: UnwrapRef + readonly useArrayJoin: UnwrapRef + readonly useArrayMap: UnwrapRef + readonly useArrayReduce: UnwrapRef + readonly useArraySome: UnwrapRef + readonly useArrayUnique: UnwrapRef + readonly useAsyncQueue: UnwrapRef + readonly useAsyncState: UnwrapRef + readonly useAttrs: UnwrapRef + readonly useBase64: UnwrapRef + readonly useBattery: UnwrapRef + readonly useBluetooth: UnwrapRef + readonly useBreakpoints: UnwrapRef + readonly useBroadcastChannel: UnwrapRef + readonly useBrowserLocation: UnwrapRef + readonly useCached: UnwrapRef + readonly useClipboard: UnwrapRef + readonly useCloned: UnwrapRef + readonly useColorMode: UnwrapRef + readonly useConfirmDialog: UnwrapRef + readonly useCounter: UnwrapRef + readonly useCssModule: UnwrapRef + readonly useCssVar: UnwrapRef + readonly useCssVars: UnwrapRef + readonly useCurrentElement: UnwrapRef + readonly useCycleList: UnwrapRef + readonly useDark: UnwrapRef + readonly useDateFormat: UnwrapRef + readonly useDebounce: UnwrapRef + readonly useDebounceFn: UnwrapRef + readonly useDebouncedRefHistory: UnwrapRef + readonly useDeviceMotion: UnwrapRef + readonly useDeviceOrientation: UnwrapRef + readonly useDevicePixelRatio: UnwrapRef + readonly useDevicesList: UnwrapRef + readonly useDisplayMedia: UnwrapRef + readonly useDocumentVisibility: UnwrapRef + readonly useDraggable: UnwrapRef + readonly useDropZone: UnwrapRef + readonly useElementBounding: UnwrapRef + readonly useElementByPoint: UnwrapRef + readonly useElementHover: UnwrapRef + readonly useElementSize: UnwrapRef + readonly useElementVisibility: UnwrapRef + readonly useEventBus: UnwrapRef + readonly useEventListener: UnwrapRef + readonly useEventSource: UnwrapRef + readonly useEyeDropper: UnwrapRef + readonly useFavicon: UnwrapRef + readonly useFetch: UnwrapRef + readonly useFileDialog: UnwrapRef + readonly useFileSystemAccess: UnwrapRef + readonly useFocus: UnwrapRef + readonly useFocusWithin: UnwrapRef + readonly useFps: UnwrapRef + readonly useFullscreen: UnwrapRef + readonly useGamepad: UnwrapRef + readonly useGeolocation: UnwrapRef + readonly useIdle: UnwrapRef + readonly useImage: UnwrapRef + readonly useInfiniteScroll: UnwrapRef + readonly useIntersectionObserver: UnwrapRef + readonly useInterval: UnwrapRef + readonly useIntervalFn: UnwrapRef + readonly useKeyModifier: UnwrapRef + readonly useLastChanged: UnwrapRef + readonly useLocalStorage: UnwrapRef + readonly useMagicKeys: UnwrapRef + readonly useManualRefHistory: UnwrapRef + readonly useMediaControls: UnwrapRef + readonly useMediaQuery: UnwrapRef + readonly useMemoize: UnwrapRef + readonly useMemory: UnwrapRef + readonly useMounted: UnwrapRef + readonly useMouse: UnwrapRef + readonly useMouseInElement: UnwrapRef + readonly useMousePressed: UnwrapRef + readonly useMutationObserver: UnwrapRef + readonly useNavigatorLanguage: UnwrapRef + readonly useNetwork: UnwrapRef + readonly useNow: UnwrapRef + readonly useObjectUrl: UnwrapRef + readonly useOffsetPagination: UnwrapRef + readonly useOnline: UnwrapRef + readonly usePageLeave: UnwrapRef + readonly useParallax: UnwrapRef + readonly usePermission: UnwrapRef + readonly usePointer: UnwrapRef + readonly usePointerLock: UnwrapRef + readonly usePointerSwipe: UnwrapRef + readonly usePreferredColorScheme: UnwrapRef + readonly usePreferredContrast: UnwrapRef + readonly usePreferredDark: UnwrapRef + readonly usePreferredLanguages: UnwrapRef + readonly usePreferredReducedMotion: UnwrapRef + readonly usePrevious: UnwrapRef + readonly useRafFn: UnwrapRef + readonly useRefHistory: UnwrapRef + readonly useResizeObserver: UnwrapRef + readonly useScreenOrientation: UnwrapRef + readonly useScreenSafeArea: UnwrapRef + readonly useScriptTag: UnwrapRef + readonly useScroll: UnwrapRef + readonly useScrollLock: UnwrapRef + readonly useSessionStorage: UnwrapRef + readonly useShare: UnwrapRef + readonly useSlots: UnwrapRef + readonly useSorted: UnwrapRef + readonly useSpeechRecognition: UnwrapRef + readonly useSpeechSynthesis: UnwrapRef + readonly useStepper: UnwrapRef + readonly useStorage: UnwrapRef + readonly useStorageAsync: UnwrapRef + readonly useStyleTag: UnwrapRef + readonly useSupported: UnwrapRef + readonly useSwipe: UnwrapRef + readonly useTemplateRefsList: UnwrapRef + readonly useTextDirection: UnwrapRef + readonly useTextSelection: UnwrapRef + readonly useTextareaAutosize: UnwrapRef + readonly useThrottle: UnwrapRef + readonly useThrottleFn: UnwrapRef + readonly useThrottledRefHistory: UnwrapRef + readonly useTimeAgo: UnwrapRef + readonly useTimeout: UnwrapRef + readonly useTimeoutFn: UnwrapRef + readonly useTimeoutPoll: UnwrapRef + readonly useTimestamp: UnwrapRef + readonly useTitle: UnwrapRef + readonly useToNumber: UnwrapRef + readonly useToString: UnwrapRef + readonly useToggle: UnwrapRef + readonly useTransition: UnwrapRef + readonly useUrlSearchParams: UnwrapRef