commit 48a6f3cabc4e4e22cb4bb14cf8d675cf2222df7d Author: admin <> Date: Sun Sep 8 11:01:57 2024 +0800 feat: init diff --git a/.env.development b/.env.development new file mode 100644 index 0000000..2922f76 --- /dev/null +++ b/.env.development @@ -0,0 +1,14 @@ +# 应用端口 +VITE_APP_PORT = 3000 + +# 代理前缀 +VITE_APP_BASE_API = '/' +# /test/sh0001/api + +# 线上接口地址 +VITE_APP_API_URL = 'https://cloud.52zaisheng.cn' +# 开发接口地址 +# VITE_APP_API_URL = http://localhost:8989 + +# 是否启用 Mock 服务 +VITE_MOCK_DEV_SERVER = false diff --git a/.env.production b/.env.production new file mode 100644 index 0000000..7e0146f --- /dev/null +++ b/.env.production @@ -0,0 +1,3 @@ +# 代理前缀 +VITE_APP_BASE_API = '/test' + diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..43af40f --- /dev/null +++ b/.eslintignore @@ -0,0 +1,14 @@ +dist +node_modules +public +.husky +.vscode +.idea +*.sh +*.md + +src/assets + +.eslintrc.cjs +.prettierrc.cjs +.stylelintrc.cjs diff --git a/.eslintrc-auto-import.json b/.eslintrc-auto-import.json new file mode 100644 index 0000000..f4738bf --- /dev/null +++ b/.eslintrc-auto-import.json @@ -0,0 +1,284 @@ +{ + "globals": { + "Component": true, + "ComponentPublicInstance": true, + "ComputedRef": true, + "EffectScope": true, + "ElMessage": true, + "ElMessageBox": true, + "ElNotification": true, + "InjectionKey": true, + "PropType": true, + "Ref": true, + "VNode": true, + "asyncComputed": true, + "autoResetRef": true, + "computed": true, + "computedAsync": true, + "computedEager": true, + "computedInject": true, + "computedWithControl": true, + "controlledComputed": true, + "controlledRef": true, + "createApp": true, + "createEventHook": true, + "createGlobalState": true, + "createInjectionState": true, + "createReactiveFn": true, + "createReusableTemplate": true, + "createSharedComposable": true, + "createTemplatePromise": true, + "createUnrefFn": true, + "customRef": true, + "debouncedRef": true, + "debouncedWatch": true, + "defineAsyncComponent": true, + "defineComponent": true, + "eagerComputed": true, + "effectScope": true, + "extendRef": true, + "getCurrentInstance": true, + "getCurrentScope": true, + "h": true, + "ignorableWatch": true, + "inject": true, + "isDefined": true, + "isProxy": true, + "isReactive": true, + "isReadonly": true, + "isRef": true, + "makeDestructurable": true, + "markRaw": true, + "nextTick": true, + "onActivated": true, + "onBeforeMount": true, + "onBeforeUnmount": true, + "onBeforeUpdate": true, + "onClickOutside": true, + "onDeactivated": true, + "onErrorCaptured": true, + "onKeyStroke": true, + "onLongPress": true, + "onMounted": true, + "onRenderTracked": true, + "onRenderTriggered": true, + "onScopeDispose": true, + "onServerPrefetch": true, + "onStartTyping": true, + "onUnmounted": true, + "onUpdated": true, + "pausableWatch": true, + "provide": true, + "reactify": true, + "reactifyObject": true, + "reactive": true, + "reactiveComputed": true, + "reactiveOmit": true, + "reactivePick": true, + "readonly": true, + "ref": true, + "refAutoReset": true, + "refDebounced": true, + "refDefault": true, + "refThrottled": true, + "refWithControl": true, + "resolveComponent": true, + "resolveRef": true, + "resolveUnref": true, + "shallowReactive": true, + "shallowReadonly": true, + "shallowRef": true, + "syncRef": true, + "syncRefs": true, + "templateRef": true, + "throttledRef": true, + "throttledWatch": true, + "toRaw": true, + "toReactive": true, + "toRef": true, + "toRefs": true, + "toValue": true, + "triggerRef": true, + "tryOnBeforeMount": true, + "tryOnBeforeUnmount": true, + "tryOnMounted": true, + "tryOnScopeDispose": true, + "tryOnUnmounted": true, + "unref": true, + "unrefElement": true, + "until": true, + "useActiveElement": true, + "useAnimate": true, + "useArrayDifference": true, + "useArrayEvery": true, + "useArrayFilter": true, + "useArrayFind": true, + "useArrayFindIndex": true, + "useArrayFindLast": true, + "useArrayIncludes": true, + "useArrayJoin": true, + "useArrayMap": true, + "useArrayReduce": true, + "useArraySome": true, + "useArrayUnique": true, + "useAsyncQueue": true, + "useAsyncState": true, + "useAttrs": true, + "useBase64": true, + "useBattery": true, + "useBluetooth": true, + "useBreakpoints": true, + "useBroadcastChannel": true, + "useBrowserLocation": true, + "useCached": true, + "useClipboard": true, + "useCloned": true, + "useColorMode": true, + "useConfirmDialog": true, + "useCounter": true, + "useCssModule": true, + "useCssVar": true, + "useCssVars": true, + "useCurrentElement": true, + "useCycleList": true, + "useDark": true, + "useDateFormat": true, + "useDebounce": true, + "useDebounceFn": true, + "useDebouncedRefHistory": true, + "useDeviceMotion": true, + "useDeviceOrientation": true, + "useDevicePixelRatio": true, + "useDevicesList": true, + "useDisplayMedia": true, + "useDocumentVisibility": true, + "useDraggable": true, + "useDropZone": true, + "useElementBounding": true, + "useElementByPoint": true, + "useElementHover": true, + "useElementSize": true, + "useElementVisibility": true, + "useEventBus": true, + "useEventListener": true, + "useEventSource": true, + "useEyeDropper": true, + "useFavicon": true, + "useFetch": true, + "useFileDialog": true, + "useFileSystemAccess": true, + "useFocus": true, + "useFocusWithin": true, + "useFps": true, + "useFullscreen": true, + "useGamepad": true, + "useGeolocation": true, + "useIdle": true, + "useImage": true, + "useInfiniteScroll": true, + "useIntersectionObserver": true, + "useInterval": true, + "useIntervalFn": true, + "useKeyModifier": true, + "useLastChanged": true, + "useLocalStorage": true, + "useMagicKeys": true, + "useManualRefHistory": true, + "useMediaControls": true, + "useMediaQuery": true, + "useMemoize": true, + "useMemory": true, + "useMounted": true, + "useMouse": true, + "useMouseInElement": true, + "useMousePressed": true, + "useMutationObserver": true, + "useNavigatorLanguage": true, + "useNetwork": true, + "useNow": true, + "useObjectUrl": true, + "useOffsetPagination": true, + "useOnline": true, + "usePageLeave": true, + "useParallax": true, + "useParentElement": true, + "usePerformanceObserver": true, + "usePermission": true, + "usePointer": true, + "usePointerLock": true, + "usePointerSwipe": true, + "usePreferredColorScheme": true, + "usePreferredContrast": true, + "usePreferredDark": true, + "usePreferredLanguages": true, + "usePreferredReducedMotion": true, + "usePrevious": true, + "useRafFn": true, + "useRefHistory": true, + "useResizeObserver": true, + "useScreenOrientation": true, + "useScreenSafeArea": true, + "useScriptTag": true, + "useScroll": true, + "useScrollLock": true, + "useSessionStorage": true, + "useShare": true, + "useSlots": true, + "useSorted": true, + "useSpeechRecognition": true, + "useSpeechSynthesis": true, + "useStepper": true, + "useStorage": true, + "useStorageAsync": true, + "useStyleTag": true, + "useSupported": true, + "useSwipe": true, + "useTemplateRefsList": true, + "useTextDirection": true, + "useTextSelection": true, + "useTextareaAutosize": true, + "useThrottle": true, + "useThrottleFn": true, + "useThrottledRefHistory": true, + "useTimeAgo": true, + "useTimeout": true, + "useTimeoutFn": true, + "useTimeoutPoll": true, + "useTimestamp": true, + "useTitle": true, + "useToNumber": true, + "useToString": true, + "useToggle": true, + "useTransition": true, + "useUrlSearchParams": true, + "useUserMedia": true, + "useVModel": true, + "useVModels": true, + "useVibrate": true, + "useVirtualList": true, + "useWakeLock": true, + "useWebNotification": true, + "useWebSocket": true, + "useWebWorker": true, + "useWebWorkerFn": true, + "useWindowFocus": true, + "useWindowScroll": true, + "useWindowSize": true, + "watch": true, + "watchArray": true, + "watchAtMost": true, + "watchDebounced": true, + "watchDeep": true, + "watchEffect": true, + "watchIgnorable": true, + "watchImmediate": true, + "watchOnce": true, + "watchPausable": true, + "watchPostEffect": true, + "watchSyncEffect": true, + "watchThrottled": true, + "watchTriggerable": true, + "watchWithFilter": true, + "whenever": true + } +} diff --git a/.eslintrc.cjs b/.eslintrc.cjs new file mode 100644 index 0000000..8ae169a --- /dev/null +++ b/.eslintrc.cjs @@ -0,0 +1,88 @@ +module.exports = { + root: true, + env: { + browser: true, + es2021: true, + node: true, + }, + parser: "vue-eslint-parser", + extends: [ + // https://eslint.vuejs.org/user-guide/#usage + "plugin:vue/vue3-recommended", + "./.eslintrc-auto-import.json", + "prettier", + "plugin:@typescript-eslint/recommended", + "plugin:prettier/recommended", + ], + parserOptions: { + ecmaVersion: "latest", + sourceType: "module", + parser: "@typescript-eslint/parser", + project: "./tsconfig.*?.json", + createDefaultProgram: false, + extraFileExtensions: [".vue"], + }, + plugins: ["vue", "@typescript-eslint"], + rules: { + // https://eslint.vuejs.org/rules/#priority-a-essential-error-prevention + "vue/multi-word-component-names": "off", + "vue/no-v-model-argument": "off", + "vue/script-setup-uses-vars": "error", + "vue/no-reserved-component-names": "off", + "vue/custom-event-name-casing": "off", + "vue/attributes-order": "off", + "vue/one-component-per-file": "off", + "vue/html-closing-bracket-newline": "off", + "vue/max-attributes-per-line": "off", + "vue/multiline-html-element-content-newline": "off", + "vue/singleline-html-element-content-newline": "off", + "vue/attribute-hyphenation": "off", + "vue/require-default-prop": "off", + "vue/require-explicit-emits": "off", + "vue/html-self-closing": [ + "error", + { + html: { + void: "always", + normal: "never", + component: "always", + }, + svg: "always", + math: "always", + }, + ], + + "@typescript-eslint/no-empty-function": "off", // 关闭空方法检查 + "@typescript-eslint/no-explicit-any": "off", // 关闭any类型的警告 + "@typescript-eslint/no-non-null-assertion": "off", + "@typescript-eslint/ban-ts-ignore": "off", + "@typescript-eslint/ban-ts-comment": "off", + "@typescript-eslint/ban-types": "off", + "@typescript-eslint/explicit-function-return-type": "off", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-var-requires": "off", + "@typescript-eslint/no-empty-function": "off", + "@typescript-eslint/no-use-before-define": "off", + "@typescript-eslint/no-non-null-assertion": "off", + "@typescript-eslint/explicit-module-boundary-types": "off", + "@typescript-eslint/no-unused-vars": "off", + + "prettier/prettier": [ + "error", + { + useTabs: false, // 不使用制表符 + }, + ], + }, + // eslint不能对html文件生效 + overrides: [ + { + files: ["*.html"], + processor: "vue/.vue", + }, + ], + // https://eslint.org/docs/latest/use/configure/language-options#specifying-globals + globals: { + OptionType: "readonly", + }, +}; diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fee6999 --- /dev/null +++ b/.gitignore @@ -0,0 +1,18 @@ +node_modules +.DS_Store +dist +dist-ssr +*.local +.history + +# Editor directories and files +.idea +*.suo +*.ntvs* +*.njsproj +*.sln +*.local + +package-lock.json +pnpm-lock.yaml +stats.html diff --git a/.husky/commit-msg b/.husky/commit-msg new file mode 100755 index 0000000..fd2bf70 --- /dev/null +++ b/.husky/commit-msg @@ -0,0 +1 @@ +npx --no-install commitlint --edit $1 diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 0000000..b18d041 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1 @@ +npm run lint:lint-staged diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..f3e9850 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,11 @@ +dist +node_modules +public +.husky +.vscode +.idea +*.sh +*.md + +src/assets +stats.html diff --git a/.prettierrc.cjs b/.prettierrc.cjs new file mode 100644 index 0000000..419b91e --- /dev/null +++ b/.prettierrc.cjs @@ -0,0 +1,46 @@ +module.exports = { + // (x)=>{},单个参数箭头函数是否显示小括号。(always:始终显示;avoid:省略括号。默认:always) + arrowParens: "always", + // 开始标签的右尖括号是否跟随在最后一行属性末尾,默认false + bracketSameLine: false, + // 对象字面量的括号之间打印空格 (true - Example: { foo: bar } ; false - Example: {foo:bar}) + bracketSpacing: true, + // 是否格式化一些文件中被嵌入的代码片段的风格(auto|off;默认auto) + embeddedLanguageFormatting: "auto", + // 指定 HTML 文件的空格敏感度 (css|strict|ignore;默认css) + htmlWhitespaceSensitivity: "ignore", + // 当文件已经被 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 文件中是否缩进 ", + "" + ], + "description": "Vue3.0" + } +} diff --git a/.vscode/vue3.2.code-snippets b/.vscode/vue3.2.code-snippets new file mode 100644 index 0000000..a083940 --- /dev/null +++ b/.vscode/vue3.2.code-snippets @@ -0,0 +1,17 @@ +{ + "Vue3.2+快速生成模板": { + "scope": "vue", + "prefix": "Vue3.2+", + "body": [ + "", + "", + "", + "", + "", + "" + ], + "description": "Vue3.2+" + } +} diff --git a/.vscode/vue3.3.code-snippets b/.vscode/vue3.3.code-snippets new file mode 100644 index 0000000..705e04f --- /dev/null +++ b/.vscode/vue3.3.code-snippets @@ -0,0 +1,21 @@ +{ + "Vue3.3+defineOptions快速生成模板": { + "scope": "vue", + "prefix": "Vue3.3+", + "body": [ + "", + "", + "", + "", + "", + "" + ], + "description": "Vue3.3+defineOptions快速生成模板" + } +} diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..fd2cef3 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,386 @@ + +# 2.11.5 (2024/6/18) + +## ✨ feat + +- 支持后端文件导入([#142](https://github.com/youlaitech/vue3-element-admin/pull/142)) [@cshaptx4869](https://github.com/cshaptx4869) + + +## 🐛 fix +- vue-dev-tools 插件导致菜单路由切换卡死,暂时关闭 ([28349e](https://github.com/youlaitech/vue3-element-admin/commit/28349efe147afab36531ba148eaac3a448fe6c71)) [@haoxianrui](https://github.com/haoxianrui) + + + +# 2.11.4 (2024/6/16) + +## ✨ feat + +- 操作栏增加render配置参数([#138](https://github.com/youlaitech/vue3-element-admin/pull/140)) [@cshaptx4869](https://github.com/cshaptx4869) +- 左侧工具栏增加type配置参数([#141](https://github.com/youlaitech/vue3-element-admin/pull/141)) [@diamont1001](https://github.com/diamont1001) + +## ♻️ refactor +- 更换权限分配弹窗类型为 drawer 并添加父子联动开关([2d9193](https://github.com/youlaitech/vue3-element-admin/commit/2d9193c47fd224f01f82b9c0b2bbeb5e7cb33584)) [@haoxianrui](https://github.com/haoxianrui) + + + +# 2.11.3 (2024/6/11) + +## ✨ feat + +- 支持默认工具栏的导入([#138](https://github.com/youlaitech/vue3-element-admin/pull/138)) [@cshaptx4869](https://github.com/cshaptx4869) +- 添加CURD导入示例([19e7bb](https://github.com/youlaitech/vue3-element-admin/commit/eab91effd6a01d5a3d9257249c8d06aa252b3bf8)) [@cshaptx4869](https://github.com/cshaptx4869) + +## ♻️ refactor +- 修改导出全量数据选项文本([904fec](https://github.com/youlaitech/vue3-element-admin/commit/904fecad65217650482fcdbb10ffb7f3d27eb9ea)) [@cshaptx4869](https://github.com/cshaptx4869) + +## 🐛 fix +- 菜单列表未适配el-icon导致图标不显示问题修复([e72b68](https://github.com/youlaitech/vue3-element-admin/commit/e72b68337562b5a7ea24ad55bbe00023e1266b40)) [@haoxianrui](https://github.com/haoxianrui) + +# 2.11.2 (2024/6/8) + +## ✨ feat + +- 支持表格远程筛选([#131](https://github.com/youlaitech/vue3-element-admin/pull/131)) [@cshaptx4869](https://github.com/cshaptx4869) +- 支持标签输入框([#132](https://github.com/youlaitech/vue3-element-admin/pull/132)) [@cshaptx4869](https://github.com/cshaptx4869) +- 表单项支持tips配置([#133](https://github.com/youlaitech/vue3-element-admin/pull/133)) [@cshaptx4869](https://github.com/cshaptx4869) +- 前端导出支持全量数据([#134](https://github.com/youlaitech/vue3-element-admin/pull/134)) [@cshaptx4869](https://github.com/cshaptx4869) +- 支持选中数据导出([#135](https://github.com/youlaitech/vue3-element-admin/pull/135)) [@cshaptx4869](https://github.com/cshaptx4869) +- 表格默认工具栏的导出、搜索按钮增加权限点控制([883128](https://github.com/youlaitech/vue3-element-admin/commit/8831289b655f2cc086ecdababaa89f8d8a087c42)) [@cshaptx4869](https://github.com/cshaptx4869) +- 页签title支持动态设置([23876a](https://github.com/youlaitech/vue3-element-admin/commit/23876aa396143bf77cb5c86af8d6023d9ff6555a)) [@haoxianrui](https://github.com/haoxianrui) + +## ♻️ refactor +- 默认工具栏支持自定义([#136](https://github.com/youlaitech/vue3-element-admin/pull/136)) [@cshaptx4869](https://github.com/cshaptx4869) +- 未配置全量导出接口时选项隐藏([eab91ef](https://github.com/youlaitech/vue3-element-admin/commit/eab91effd6a01d5a3d9257249c8d06aa252b3bf8)) [@cshaptx4869](https://github.com/cshaptx4869) + +## 🐛 fix +- 修复注销登出后redirect跳转路由参数丢失([5626017](https://github.com/youlaitech/vue3-element-admin/commit/562601736731afd20bb1a5140d856f6515720159)) [@haoxianrui](https://github.com/haoxianrui) + +# 2.11.1 (2024/6/6) + +## ✨ feat + +- 增加pagination、request、parseData配置参数([#119](https://github.com/youlaitech/vue3-element-admin/pull/119)) [@cshaptx4869](https://github.com/cshaptx4869) +- 增加返回顶部功能([#120](https://github.com/youlaitech/vue3-element-admin/pull/120)) [@cshaptx4869](https://github.com/cshaptx4869) +- 支持前端导出([#126](https://github.com/youlaitech/vue3-element-admin/pull/126)) [@cshaptx4869](https://github.com/cshaptx4869) + +## ♻️ refactor +- 重构布局样式(解决页面抖动问题)([#116](https://github.com/youlaitech/vue3-element-admin/pull/116)) [@cshaptx4869](https://github.com/cshaptx4869) +- 修改CURD示例编辑弹窗尺寸([#121](https://github.com/youlaitech/vue3-element-admin/pull/121)) [@cshaptx4869](https://github.com/cshaptx4869) +- 统一注册vue插件([#122](https://github.com/youlaitech/vue3-element-admin/pull/122)) [@cshaptx4869](https://github.com/cshaptx4869) +- 默认主题跟随系统([#128](https://github.com/youlaitech/vue3-element-admin/pull/128)) [@cshaptx4869](https://github.com/cshaptx4869) +- 增加"scss.lint.unknownAtRules": "ignore"代码,解决style中使用@apply提示unknow at rules@apply提示问题([Gitee#22](https://gitee.com/youlaiorg/vue3-element-admin/pulls/22)) [@zjsy521](https://gitee.com/zjsy521) + +## 🐛 fix +- 修复左侧布局移动端菜单弹出样式 ([#117](https://github.com/youlaitech/vue3-element-admin/pull/117)) [@cshaptx4869](https://github.com/cshaptx4869) + +- 修复编辑后未清空id再新增菜单覆盖的问题([0e78eeb](https://github.com/youlaitech/vue3-element-admin/commit/0e78eeb75008fa8e9732b1b4e7d7a1ea345c7a1b)) [@haoxianrui](https://github.com/haoxianrui) +- 修复水印层级问题([#123](https://github.com/youlaitech/vue3-element-admin/pull/123)) [@cshaptx4869](https://github.com/cshaptx4869) +- 修复混合布局样式问题([#124](https://github.com/youlaitech/vue3-element-admin/pull/124)) [@cshaptx4869](https://github.com/cshaptx4869) +- 修复关闭弹窗时没有clearValidate问题([#125](https://github.com/youlaitech/vue3-element-admin/pull/125)) [@andm31](https://github.com/andm31) + + + +# 2.11.0 (2024/5/27) + +## ✨ feat +- 菜单添加路由参数设置(author by [haoxianrui](https://github.com/haoxianrui)) +- 增加列表选择组件(author by [cshaptx4869](https://github.com/cshaptx4869)) +- 增加列表选择组件使用示例(author by [cshaptx4869](https://github.com/cshaptx4869)) +- 增加defaultToolbar配置参数(author by [cshaptx4869](https://github.com/cshaptx4869)) +- 表单弹窗支持drawer模式(author by [cshaptx4869](https://github.com/cshaptx4869)) +- 表单项增加computed和watchEffect配置(author by [cshaptx4869](https://github.com/cshaptx4869)) +- 支持switch属性修改(author by [cshaptx4869](https://github.com/cshaptx4869)) +- 表单项增加文本类型支持(author by [cshaptx4869](https://github.com/cshaptx4869)) +- 列表列增加show配置项(author by [cshaptx4869](https://github.com/cshaptx4869)) +- 支持搜索表单显隐控制(author by [cshaptx4869](https://github.com/cshaptx4869)) +- 支持input属性修改(author by [cshaptx4869](https://github.com/cshaptx4869)) +- search配置新增函数能力拓展(author by [xiudaozhe](https://github.com/xiudaozhe)) +- 表格新增列设置控制(author by [haoxianrui](https://github.com/haoxianrui)) +- 搜索添加展开和收缩(author by [haoxianrui](https://github.com/haoxianrui)) +- watch函数增加配置项参数返回(author by [cshaptx4869](https://github.com/cshaptx4869)) + +## ♻️ refactor +- 重构图标选择组件(author by [cshaptx4869](https://github.com/cshaptx4869)) +- 重构列表选择组件默认样式 (author by [cshaptx4869](https://github.com/cshaptx4869)) +- 加强对话框表单组件和列表选择组件(author by [cshaptx4869](https://github.com/cshaptx4869)) +- routeMeta增加alwaysShow字段声明(author by [cshaptx4869](https://github.com/cshaptx4869)) +- 分页组件增加溢出滚动效果(author by [cshaptx4869](https://github.com/cshaptx4869)) +- 修正登录表单的Ref类型(author by [cshaptx4869](https://github.com/cshaptx4869)) +- 点击表格刷新按钮不重置页码(author by [cshaptx4869](https://github.com/cshaptx4869)) +- 筛选列超出一定高度滚动(author by [cshaptx4869](https://github.com/cshaptx4869)) +- 优化加强initFn函数,表单项增加initFn函数(author by [cshaptx4869](https://github.com/cshaptx4869)) +- 重构watch、computed、watchEffect调用(author by [cshaptx4869](https://github.com/cshaptx4869)) +- 修改操作成功提示(author by [cshaptx4869](https://github.com/cshaptx4869)) +- PageSearch 改用card作为容器,样式改用unocss写法(author by [cshaptx4869](https://github.com/cshaptx4869)) +- 优化首页 loading 动画效果author by [haoxianrui](https://github.com/haoxianrui)) + + +## 🐛 fix +- 路由是否始终显示不限制只有顶级目录才有的配置,开放至菜单 (author by [haoxianrui](https://github.com/haoxianrui)) +- sockjs-client 报错 global is not defined 导致开发环境无法打开 WebSocket 页面问题修复 (author by [haoxianrui](https://github.com/haoxianrui)) +- 发送用户重启密码功能,最少为6位字符(小于6位登陆时不允许的问题) (author by [dreamnyj](https://gitee.com/dreamnyj)) +- 修复系统设置面板滚动条问题(author by [cshaptx4869](https://github.com/cshaptx4869)) +- 修复表单插槽失效问题(author by [cshaptx4869](https://github.com/cshaptx4869)) +- 修改tagsview刷新丢失query问题(author by [xiudaozhe](https://github.com/xiudaozhe)) + +## 📦️ build +- 升级 NPM 包版本至最新 (author by [haoxianrui](https://github.com/haoxianrui)) + +## ⚙️ ci +- 规整脚本执行命令(author by [cshaptx4869](https://github.com/cshaptx4869)) + + +# 2.10.1 (2024/5/4) + +## ♻️ refactor +- 抽离CURD的使用部分代码为Hooks实现(author by [cshaptx4869](https://github.com/cshaptx4869)) +- 修改CURD导入权限点标识名(author by [cshaptx4869](https://github.com/cshaptx4869)) +- cURD表单字段支持watch监听(author by [cshaptx4869](https://github.com/cshaptx4869)) +- cURD表单input支持number修饰(author by [cshaptx4869](https://github.com/cshaptx4869)) +- cURD表单组件支持checkbox多选框(author by [cshaptx4869](https://github.com/cshaptx4869)) +- 优化axios响应数据TS类型提示(author by [cshaptx4869](https://github.com/cshaptx4869)) +- 修改CURD表单组件自定义类型的attrs传值(author by [cshaptx4869](https://github.com/cshaptx4869)) +- 同步重置密码按钮权限标识重命名(author by [haoxianrui](https://github.com/haoxianrui)) +- 重构API为静态方法实现模块化管理,并将types.ts重命名为model.ts用于存放接口模型定义(author by [haoxianrui](https://github.com/haoxianrui)) + + +## 🐛 fix +- sockjs-client 报错 global is not defined 导致开发环境无法打开 WebSocket 页面问题修复 (author by [haoxianrui](https://github.com/haoxianrui)) +- 主题颜色设置覆盖暗黑模式下el-table行激活的背景色问题修复 (author by [haoxianrui](https://github.com/haoxianrui)) +- 修复因API接口调整而影响的调用页面的问题 (author by [haoxianrui](https://github.com/haoxianrui)) + +## 📦️ build +- 升级 NPM 包版本至最新 (author by [haoxianrui](https://github.com/haoxianrui)) + + +# 2.10.0 (2024/4/26) +## ✨ feat +- 封装增删改查组件(author by [cshaptx4869](https://github.com/cshaptx4869)) +- 集成 vite-plugin-vue-devtools 插件(author by [Tricker39](https://github.com/Tricker39)) +- 增加CURD配置化实现(author by [cshaptx4869](https://github.com/cshaptx4869)) + + +# 2.9.3 (2024/04/14) +## ✨ feat +- 增加vue文件代码片段(author by [cshaptx4869](https://github.com/cshaptx4869)) +- 菜单 hover 背景色添加值全局SCSS变量进行控制(author by [haoxianrui](https://github.com/haoxianrui)) + +## ♻️ refactor +- 加强基础国际化(author by [cshaptx4869](https://github.com/cshaptx4869)) +- 增加语言和布局大小枚举类型(author by [cshaptx4869](https://github.com/cshaptx4869)) +- 增加侧边栏状态枚举类型(author by [cshaptx4869](https://github.com/cshaptx4869)) +- 使用布局枚举替换字面量(author by [haoxianrui](https://github.com/haoxianrui)) +- 控制台使用静态数据循环渲染(author by [april](mailto:april@zen-game.cn)) +- 本地缓存的 token 变量重命名(author by [haoxianrui](https://github.com/haoxianrui)) +- 完善 Vite 环境变量类型声明(author by [haoxianrui](https://github.com/haoxianrui)) + +## 🐛 fix +- 修复构建时提示iconComponent.name可能为undefined的报错 (author by [wangji1042](https://github.com/wangji1042)) +- 修复浏览器密码自动填充时可能存在的报错 (author by [cshaptx4869](https://github.com/cshaptx4869)) +- 修复eslint报错(author by [cshaptx4869](https://github.com/cshaptx4869)) +- 移动端下点击左侧菜单节点后关闭侧边栏(author by [haoxianrui](https://github.com/haoxianrui)) +- 添加 size 类型断言修复类型报错(author by [haoxianrui](https://github.com/haoxianrui)) + +## 📦️ build +- husky9.x版本适配 (author by [cshaptx4869](https://github.com/cshaptx4869)) +- 升级 npm 包版本至最新(author by [haoxianrui](https://github.com/haoxianrui)) + +# 2.9.2 (2024/03/05) +## ✨ feat +- vscode开发扩展推荐(author by [cshaptx4869](https://github.com/cshaptx4869)) +- 完善基础增删改查Mock接口(author by [haoxianrui](https://github.com/haoxianrui)) + +## ♻️ refactor +- 修改login密码框功能实现(author by [cshaptx4869](https://github.com/cshaptx4869)) +- 弱化页面进入动画效果(author by [cshaptx4869](https://github.com/cshaptx4869)) +- 取消推荐TypeScript Vue Plugin (author by [cshaptx4869](https://github.com/cshaptx4869)) +- 网站加载动画替换 (author by [haoxianrui](https://github.com/haoxianrui)) +- 优化主题和主题色监听,避免多个页面重复初始化 (author by [haoxianrui](https://github.com/haoxianrui)) + +## 🐛 fix +- AppMain 高度在非固定头部不正确导致出现滚动条问题修复 (author by [haoxianrui](https://github.com/haoxianrui)) +- 修复混合模式开启固定Head时的样式问题 (author by [cshaptx4869](https://github.com/cshaptx4869)) +- 设置面板统一字体大小 (author by [cshaptx4869](https://github.com/cshaptx4869)) + +## 📦️build +- 通过env配置控制mock服务 (author by [cshaptx4869](https://github.com/cshaptx4869)) +- 升级依赖包至最新版本 (author by [haoxianrui](https://github.com/haoxianrui)) +- 定义vite全局常量替换项目标题和版本 (author by [cshaptx4869](https://github.com/cshaptx4869)) + +# 2.9.1 (2024/02/28) +## ♻️ refactor +- 项目配置按钮移入navbar(author by [cshaptx4869](https://github.com/cshaptx4869)) +- 优化user数据定义(author by [cshaptx4869](https://github.com/cshaptx4869)) +- 统一设置栏的 SVG 图标风格 + +## 🐛 fix +- 规整一些开发依赖(author by [cshaptx4869](https://github.com/cshaptx4869)) +- 修复登录页主题切换问题 (author by [cshaptx4869](https://github.com/cshaptx4869)) + +## 🚀 pref + +- 压缩图片资源 (author by [cshaptx4869](https://github.com/cshaptx4869)) + + +# 2.9.0 (2024/02/25) + +## ✨ feat +- 引入 animate.css 动画库 +- 新增水印和配置 +- 动态路由菜单支持 element plus 的图标 + +## ♻️ refactor +- Layout 布局重构和相关问题修复 +- sass 使用 @use 替代 @import 引入外部文件指令 + +## 🐛 fix +- 修复管理页面部分弹窗无法打开问题 +- 主题颜色设置按钮 hover 等未变化问题修复 + + +# 2.8.1 (2024/01/10) + +## ✨ feat +- 替换 Mock 解决方案 vite-plugin-mock 为 vite-plugin-mock-dev-server 适配 Vite5 + +# 2.8.0 (2023/12/27) + +## ⬆️ chore +- 升级 Vite4 至 Vite5 + +# 2.7.1 (2023/12/12) + +## ♻️ refactor +- 将打包后的文件进行分类 (author by [ityangzhiwen](https://gitee.com/ityangzhiwen)) + +# 2.7.0 (2023/11/19) + +## ♻️ refactor +- 代码重构优化 +- 修改自动导入组件类型声明文件路径 +- 完善 typescript 类型 + +## 🐛 fix +- 修复管理页面部分弹窗无法打开问题 + + +# 2.7.0 (2023/11/19) + +## ♻️ refactor +- 代码重构 +- 修改自动导入组件类型声明文件路径 +- 完善 typescript 类型 + +## 🐛 fix +- 修复管理页面部分弹窗无法打开问题 + + +# 2.6.3 (2023/10/22) + +## ✨ feat +- 菜单管理新增目录只有一级子路由是否始终显示(alwaysShow)和路由页面是否缓存(keepAlive)的配置 +- 接口文档新增 swagger、knife4j +- 引入和支持 tsx + +## ♻️ refactor +- 代码瘦身,整理并删除未使用的 svg +- 控制台样式优化 + +## 🐛 fix +- 菜单栏折叠和展开的图标暗黑模式显示问题修复 + + +# 2.6.2 (2023/10/11) + +## 🐛 fix +- 主题设置未持久化问题 +- UnoCSS 插件无智能提示 + +## ♻️ refactor +- WebSocket 演示样式和代码优化 +- 用户管理代码重构 + +# 2.6.1 (2023/9/4) + +## 🐛 fix +- 导航顶部模式、混合模式样式在固定 Header 出现的样式问题修复 +- 固定 Header 没有持久化问题修复 +- 字典回显兼容 String 和 Number 类型 + +# 2.6.0 (2023/8/24)💥💥💥 + +## ✨ feat +- 导航顶部模式、混合模式支持(author by [april-tong](https://april-tong.com/)) +- 平台文档(内嵌)(author by [april-tong](https://april-tong.com/)) + +# 2.5.0 (2023/8/8) + +## ✨ feat +- 新增 Mock(author by [ygcaicn](https://github.com/ygcaicn)) +- 图标 DEMO(author by [ygcaicn](https://github.com/ygcaicn)) + +## 🐛 fix +- 字典支持 Number 类型 + +# 2.4.1 (2023/7/20) + +## ✨ feat +- 整合 vite-plugin-compression 插件打包优化(3.66MB → 1.58MB) (author by [april-tong](https://april-tong.com/)) +- 字典组件封装(author by [haoxr](https://juejin.cn/user/4187394044331261/posts)) + +## 🐛 fix +- 分页组件hidden无效 +- 签名无法保存至后端 +- Git 提交 stylelint 校验部分机器报错 + +# 2.4.0 (2023/6/17) + +## ✨ feat +- 新增组件标签输入框(author by [april-tong](https://april-tong.com/)) +- 新增组件签名(author by [april-tong](https://april-tong.com/)) +- 新增组件表格(author by [april-tong](https://april-tong.com/)) +- Echarts 图表添加下载功能 author by [april-tong](https://april-tong.com/)) + +## ♻️ refactor +- 限制包管理器为 pnpm 和 node 版本16+ +- 自定义组件自动导入配置 +- 搜索框样式写法优化 + +## 🐛 fix +- 用户导入的部门回显成数字问题修复 + +## ⬆️ chore +- element-plus 版本升级 2.3.5 → 2.3.6 + +# 2.3.1 (2023/5/21) + +## 🔄 refactor +- 组件示例文件名称优化 + +# 2.2.2 (2023/5/11) + +## ✨ feat +- 组件封装示例添加源码地址 +- 角色、菜单、部门、字段按钮添加权限控制 + + +# 2.3.0 (2023/5/12) + +## ⬆️ chore +- vue 版本升级 3.2.45 → 3.3.1 ([CHANGELOG](https://github.com/vuejs/core/blob/main/CHANGELOG.md)) +- vite 版本升级 4.3.1 → 4.3.5 + +## ♻️ refactor +- 使用 vue 3.3 版本新特性 `defineOptions` 在 `setup` 定义组件名称,移除重复的 `script` 标签 + +# 2.2.2 (2023/5/11) + +## ✨ feat +- 用户新增提交添加 `vueUse` 的 `useDebounceFn` 函数实现按钮防抖节流 + + +# 2.2.1 (2023/4/25) + +## 🐛 fix +- 图标选择器组件使用 `onClickOutside` 未排除下拉弹出框元素导致无法输入搜索。 + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..9825cba --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021-present 有来开源组织 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.en-US.md b/README.en-US.md new file mode 100644 index 0000000..68cc337 --- /dev/null +++ b/README.en-US.md @@ -0,0 +1,161 @@ +
+ + + + + + + +
中文 | English
+
+ + +![](https://foruda.gitee.com/images/1708618984641188532/a7cca095_716974.png "rainbow.png") + +
+ 👀 Live Preview | 📖 Read Documentation +
+ + +## Introduction + +[vue3-element-admin](https://gitee.com/youlaiorg/vue3-element-admin) is a free and open-source admin template for backend management frontend, built with popular technologies such as Vue3, Vite5, TypeScript, Element-Plus, and Pinia (with accompanying [backend source code](https://gitee.com/youlaiorg/youlai-boot)). + + + + +## Project Features + +- **Simple and Easy-to-use**: Upgraded version of [vue-element-admin](https://gitee.com/panjiachen/vue-element-admin) for Vue3, with minimal encapsulation and easy to get started. + +- **Data Interaction**: Support both local `Mock` data and remote API. Comes with [Java backend source code](https://gitee.com/youlaiorg/youlai-boot) and online API documentation. + +- **Permission Management**: Complete permission system for users, roles, menus, dictionaries, and departments. + +- **Essential Infrastructure**: Dynamic routing, button permissions, internationalization, code style, Git commit conventions, and common component encapsulation. + +- **Continuous Updates**: Since 2021, the project has maintained an open-source status with continuous updates, integrating new tools and dependencies in real time, and has accumulated a broad user base. + +## Project Preview + +![Light Mode](https://foruda.gitee.com/images/1709651876583793739/0ba1ee1c_716974.png) + +![Dark Mode](https://foruda.gitee.com/images/1709651875494206224/2a2b0b53_716974.png) + +![API Documentation](https://foruda.gitee.com/images/1687755822857820115/96054330_716974.png) + +## Project Links + +| Project | Gitee | Github | GitCode | +| ---- | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | +| Frontend | [vue3-element-admin](https://gitee.com/youlaiorg/vue3-element-admin) | [vue3-element-admin](https://github.com/youlaitech/vue3-element-admin) | [vue3-element-admin](https://gitcode.net/youlai/vue3-element-admin) | +| Backend | [youlai-boot](https://gitee.com/youlaiorg/youlai-boot) | [youlai-boot](https://github.com/haoxianrui/youlai-boot.git) | [youlai-boot](https://gitcode.net/youlai/youlai-boot) | + +## Environment Setup + +| Environment | Name and Version | Download Link | +| -------------------- | :----------------------------------------------------------- | ------------------------------------------------------------ | +| **Development Tool** | VSCode | [Download](https://code.visualstudio.com/Download) | +| **Runtime Environment** | Node ≥18 | [Download](http://nodejs.cn/download) | + + +## Project Setup + +```bash +# Clone the repository +git clone https://gitee.com/youlaiorg/vue3-element-admin.git + +# Change directory +cd vue3-element-admin + +# Install pnpm +npm install pnpm -g + +# Install dependencies +pnpm install + +# Start the project +pnpm run dev +``` + +## Project Deployment + +```bash +# Build the project +pnpm run build + +# Upload files to the remote server +Copy the files generated in the `dist` directory to the `/usr/share/nginx/html` directory. + +# nginx.cofig configuration +server { + listen 80; + server_name localhost; + location / { + root /usr/share/nginx/html; + index index.html index.htm; + } + # Reverse proxy configuration + location /prod-api/ { + proxy_pass http://vapi.youlai.tech/; # Replace vapi.youlai.tech with your backend API address + } +} +``` + +## Local Mock + +The project supports both online API and local mock API. By default, it uses the online API. If you want to switch to the mock API, modify the value of `VITE_MOCK_DEV_SERVER` in the `.env.development` file to `true`. + +## Backend API + +> If you have a basic understanding of Java development, follow these steps to convert online API to local backend API and set up a full-stack development environment. + +1. Get the backend source code based on `Java` and `SpringBoot` from [youlai-boot](https://gitee.com/youlaiorg/youlai-boot.git). +2. Follow the instructions in the backend project's README.md to set up the local environment. +3. Modify the value of `VITE_APP_API_URL` in the `.env.development` file to `http://localhost:8989`, replacing it with the backend API URL. + +## Notes + +- **Auto import plugin is disabled by default** + + Component type declarations have been automatically generated for the template project. If you add and use new components, follow the instructions in the screenshot to enable automatic generation. After automatic generation is complete, remember to set it back to `false` to avoid conflicts. + + ![](https://foruda.gitee.com/images/1687755823137387608/412ea803_716974.png) + +- **Blank page when accessing the project** + + Try upgrading your browser, as older browser engines may not support certain new JavaScript syntax, such as optional chaining operator `?.`. + +- **Red highlight on project components, functions, and imports** + + Restart VSCode to try again. + +- **Other issues** + + If you have any other issues or suggestions, please open an [issue](https://gitee.com/youlaiorg/vue3-element-admin/issues/new). + +## Project Documentation + +- [Building a Backend Management System from Scratch with Vue3, Vite, TypeScript, and Element-Plus](https://blog.csdn.net/u013737132/article/details/130191394) + +- [ESLint+Prettier+Stylelint+EditorConfig for Standardized and Unified Frontend Code Style](https://blog.csdn.net/u013737132/article/details/130190788) +- [Git Commit Conventions with Husky, Lint-staged, Commitlint, Commitizen, and cz-git](https://blog.csdn.net/u013737132/article/details/130191363) + +## Commit Conventions + +Execute `pnpm run commit` to invoke interactive git commit and complete the information input and selection according to the prompts. + +![](https://foruda.gitee.com/images/1687755823165218215/c1705416_716974.png) + +## Community 🚀 + +> **Follow "Youlai Tech" WeChat Official Account to get the QR code for the community.** +> +> If the QR code for the community has expired, please add my WeChat (haoxianrui) and indicate whether you are interested in "Frontend", "Backend", or "Full Stack" to get the latest QR code. +> +> This measure is taken to ensure the quality of the community and prevent marketing advertising from infiltrating. Thank you for your understanding! + +| Official Account | Community | +|:----:|:----:| +| ![Youlai Tech WeChat Official Account QR Code](https://foruda.gitee.com/images/1687689212187063809/3c69eaee_716974.png) | ![Community QR Code](https://foruda.gitee.com/images/1687689212139273561/6a65ef69_716974.png) | + diff --git a/README.md b/README.md new file mode 100644 index 0000000..5566178 --- /dev/null +++ b/README.md @@ -0,0 +1,182 @@ +
+ + + + + + + +
中文 | English
+
+ +![](https://foruda.gitee.com/images/1708618984641188532/a7cca095_716974.png "rainbow.png") + +
+ 👀 在线预览 | 📖 阅读文档 +
+ +## 项目简介 + +[vue3-element-admin](https://gitee.com/youlaiorg/vue3-element-admin) 是基于 Vue3 + Vite5+ TypeScript5 + Element-Plus + Pinia 等主流技术栈构建的免费开源的后台管理前端模板(配套[后端源码](https://gitee.com/youlaiorg/youlai-boot))。 + + +## 项目特色 + +- **简洁易用**:基于 [vue-element-admin](https://gitee.com/panjiachen/vue-element-admin) 升级的 Vue3 版本,无过渡封装 ,易上手。 + +- **数据交互**:同时支持本地 `Mock` 和线上接口,配套 [Java 后端源码](https://gitee.com/youlaiorg/youlai-boot)和[在线接口文档](https://www.apifox.cn/apidoc/shared-195e783f-4d85-4235-a038-eec696de4ea5)。 + +- **权限管理**:用户、角色、菜单、字典、部门等完善的权限系统功能。 + +- **基础设施**:动态路由、按钮权限、国际化、代码规范、Git 提交规范、常用组件封装。 + +- **持续更新**:项目持续开源更新,实时更新工具和依赖。 + + + +## 项目预览 + +![明亮模式](https://foruda.gitee.com/images/1709651876583793739/0ba1ee1c_716974.png) + +![暗黑模式](https://foruda.gitee.com/images/1709651875494206224/2a2b0b53_716974.png) + +![接口文档](https://foruda.gitee.com/images/1687755822857820115/96054330_716974.png) + +## 项目地址 + +| 项目 | Gitee | Github | +| ---- | ------------------------------------------------------------ | ------------------------------------------------------------ | +| 前端 | [vue3-element-admin](https://gitee.com/youlaiorg/vue3-element-admin) | [vue3-element-admin](https://github.com/youlaitech/vue3-element-admin) | [vue3-element-admin](https://gitcode.net/youlai/vue3-element-admin) | +| 精简版 | [vue3-element-admin-thin](https://gitee.com/cshaptx4869/vue3-element-admin-thin) | [vue3-element-admin-thin](https://github.com/youlaitech/vue3-element-admin-thin) | +| 后端 | [youlai-boot](https://gitee.com/youlaiorg/youlai-boot) | [youlai-boot](https://github.com/haoxianrui/youlai-boot.git) | + +## 环境准备 + +| 环境 | 名称版本 | 下载地址 | +| -------------------- | :----------------------------------------------------------- | ------------------------------------------------------------ | +| **开发工具** | VSCode | [下载](https://code.visualstudio.com/Download) | +| **运行环境** | Node ≥18 (其中 20.6.0 版本不可用) | [下载](http://nodejs.cn/download) | + + +## 项目启动 + +```bash +# 克隆代码 +git clone https://gitee.com/youlaiorg/vue3-element-admin.git + +# 切换目录 +cd vue3-element-admin + +# 安装 pnpm +npm install pnpm -g + +# 安装依赖 +pnpm install + +# 启动运行 +pnpm run dev +``` + + + +## 项目部署 + +```bash +# 项目打包 +pnpm run build + +# 上传文件至远程服务器 +将打包生成在 `dist` 目录下的文件拷贝至 `/usr/share/nginx/html` 目录 + +# nginx.cofig 配置 +server { + listen 80; + server_name localhost; + location / { + root /usr/share/nginx/html; + index index.html index.htm; + } + # 反向代理配置 + location /prod-api/ { + # vapi.youlai.tech 替换后端API地址,注意保留后面的斜杠 / + proxy_pass http://vapi.youlai.tech/; + } +} +``` + +## 本地Mock + +项目同时支持在线和本地 Mock 接口,默认使用线上接口,如需替换为 Mock 接口,修改文件 `.env.development` 的 `VITE_MOCK_DEV_SERVER` 为 `true` **即可**。 + +## 后端接口 + +> 如果您具备Java开发基础,按照以下步骤将在线接口转为本地后端接口,创建企业级前后端分离开发环境,助您走向全栈之路。 + +1. 获取基于 `Java` 和 `SpringBoot` 开发的后端 [youlai-boot](https://gitee.com/youlaiorg/youlai-boot.git) 源码。 +2. 根据后端工程的说明文档 [README.md](https://gitee.com/youlaiorg/youlai-boot#%E9%A1%B9%E7%9B%AE%E8%BF%90%E8%A1%8C) 完成本地启动。 +3. 修改 `.env.development` 文件中的 `VITE_APP_API_URL` 的值,将其从 http://vapi.youlai.tech 更改为 http://localhost:8989。 + + +## 注意事项 + +- **自动导入插件自动生成默认关闭** + + 模板项目的组件类型声明已自动生成。如果添加和使用新的组件,请按照图示方法开启自动生成。在自动生成完成后,记得将其设置为 `false`,避免重复执行引发冲突。 + + ![](https://foruda.gitee.com/images/1687755823137387608/412ea803_716974.png) + +- **项目启动浏览器访问空白** + + 请升级浏览器尝试,低版本浏览器内核可能不支持某些新的 JavaScript 语法,比如可选链操作符 `?.`。 + +- **项目同步仓库更新升级** + + 项目同步仓库更新升级之后,建议 `pnpm install` 安装更新依赖之后启动 。 + +- **项目组件、函数和引用爆红** + + 重启 VSCode 尝试 + +- **其他问题** + + 如果有其他问题或者建议,建议 [ISSUE](https://gitee.com/youlaiorg/vue3-element-admin/issues/new) + + + +## 项目文档 + +- [基于 Vue3 + Vite + TypeScript + Element-Plus 从0到1搭建后台管理系统](https://blog.csdn.net/u013737132/article/details/130191394) + +- [ESLint+Prettier+Stylelint+EditorConfig 约束和统一前端代码规范](https://blog.csdn.net/u013737132/article/details/130190788) +- [Husky + Lint-staged + Commitlint + Commitizen + cz-git 配置 Git 提交规范](https://blog.csdn.net/u013737132/article/details/130191363) + + +## 提交规范 + +执行 `pnpm run commit` 唤起 git commit 交互,根据提示完成信息的输入和选择。 + +![](https://foruda.gitee.com/images/1687755823165218215/c1705416_716974.png) + + +## 项目统计 + +![Alt](https://repobeats.axiom.co/api/embed/aa7cca3d6fa9c308fc659fa6e09af9a1910506c3.svg "Repobeats analytics image") + + +Thanks to all the contributors! + +[![contributors](https://contrib.rocks/image?repo=youlaitech/vue3-element-admin)](https://github.com/youlaitech/vue3-element-admin/graphs/contributors) + + +## 交流群🚀 + +> **关注「有来技术」公众号,获取交流群二维码。** +> +> 如果交流群的二维码过期,请加微信(haoxianrui)并备注「前端」、「后端」或「全栈」以获取最新二维码。 +> +> 为确保交流群质量,防止营销广告人群混入,我们采取了此措施。望各位理解! + +| 公众号 | 交流群 | +|:----:|:----:| +| ![有来技术公众号二维码](https://foruda.gitee.com/images/1687689212187063809/3c69eaee_716974.png) | ![交流群二维码](https://foruda.gitee.com/images/1687689212139273561/6a65ef69_716974.png) | + diff --git a/commitlint.config.cjs b/commitlint.config.cjs new file mode 100644 index 0000000..89f188e --- /dev/null +++ b/commitlint.config.cjs @@ -0,0 +1,93 @@ +module.exports = { + // 继承的规则 + extends: ["@commitlint/config-conventional"], + // 自定义规则 + rules: { + // @see https://commitlint.js.org/#/reference-rules + + // 提交类型枚举,git提交type必须是以下类型 + "type-enum": [ + 2, + "always", + [ + "feat", // 新增功能 + "fix", // 修复缺陷 + "docs", // 文档变更 + "style", // 代码格式(不影响功能,例如空格、分号等格式修正) + "refactor", // 代码重构(不包括 bug 修复、功能新增) + "perf", // 性能优化 + "test", // 添加疏漏测试或已有测试改动 + "build", // 构建流程、外部依赖变更(如升级 npm 包、修改 webpack 配置等) + "ci", // 修改 CI 配置、脚本 + "revert", // 回滚 commit + "chore", // 对构建过程或辅助工具和库的更改(不影响源文件、测试用例) + ], + ], + "subject-case": [0], // subject大小写不做校验 + }, + + prompt: { + messages: { + type: "选择你要提交的类型 :", + scope: "选择一个提交范围(可选):", + customScope: "请输入自定义的提交范围 :", + subject: "填写简短精炼的变更描述 :\n", + body: '填写更加详细的变更描述(可选)。使用 "|" 换行 :\n', + breaking: '列举非兼容性重大的变更(可选)。使用 "|" 换行 :\n', + footerPrefixesSelect: "选择关联issue前缀(可选):", + customFooterPrefix: "输入自定义issue前缀 :", + footer: "列举关联issue (可选) 例如: #31, #I3244 :\n", + generatingByAI: "正在通过 AI 生成你的提交简短描述...", + generatedSelectByAI: "选择一个 AI 生成的简短描述:", + confirmCommit: "是否提交或修改commit ?", + }, + // prettier-ignore + types: [ + { value: "feat", name: "特性: ✨ 新增功能", emoji: ":sparkles:" }, + { value: "fix", name: "修复: 🐛 修复缺陷", emoji: ":bug:" }, + { value: "docs", name: "文档: 📝 文档变更(更新README文件,或者注释)", emoji: ":memo:" }, + { value: "style", name: "格式: 🌈 代码格式(空格、格式化、缺失的分号等)", emoji: ":lipstick:" }, + { value: "refactor", name: "重构: 🔄 代码重构(不修复错误也不添加特性的代码更改)", emoji: ":recycle:" }, + { value: "perf", name: "性能: 🚀 性能优化", emoji: ":zap:" }, + { value: "test", name: "测试: 🧪 添加疏漏测试或已有测试改动", emoji: ":white_check_mark:"}, + { value: "build", name: "构建: 📦️ 构建流程、外部依赖变更(如升级 npm 包、修改 vite 配置等)", emoji: ":package:"}, + { value: "ci", name: "集成: ⚙️ 修改 CI 配置、脚本", emoji: ":ferris_wheel:"}, + { value: "revert", name: "回退: ↩️ 回滚 commit",emoji: ":rewind:"}, + { value: "chore", name: "其他: 🛠️ 对构建过程或辅助工具和库的更改(不影响源文件、测试用例)", emoji: ":hammer:"}, + ], + useEmoji: true, + emojiAlign: "center", + useAI: false, + aiNumber: 1, + themeColorCode: "", + scopes: [], + allowCustomScopes: true, + allowEmptyScopes: true, + customScopesAlign: "bottom", + customScopesAlias: "custom", + emptyScopesAlias: "empty", + upperCaseSubject: false, + markBreakingChangeMode: false, + allowBreakingChanges: ["feat", "fix"], + breaklineNumber: 100, + breaklineChar: "|", + skipQuestions: [], + issuePrefixes: [ + { value: "closed", name: "closed: ISSUES has been processed" }, + ], + customIssuePrefixAlign: "top", + emptyIssuePrefixAlias: "skip", + customIssuePrefixAlias: "custom", + allowCustomIssuePrefix: true, + allowEmptyIssuePrefix: true, + confirmColorize: true, + maxHeaderLength: Infinity, + maxSubjectLength: Infinity, + minSubjectLength: 0, + scopeOverrides: undefined, + defaultBody: "", + defaultIssues: "", + defaultScope: "", + defaultSubject: "", + }, +}; diff --git a/index.html b/index.html new file mode 100644 index 0000000..bd224a6 --- /dev/null +++ b/index.html @@ -0,0 +1,61 @@ + + + + + + + + + vue3-element-admin + + + +
+
+
+ + + + + diff --git a/licenses/vue-element-admin/LICENSE b/licenses/vue-element-admin/LICENSE new file mode 100644 index 0000000..3fce384 --- /dev/null +++ b/licenses/vue-element-admin/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017-present PanJiaChen + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/licenses/vue3-element-admin/LICENSE b/licenses/vue3-element-admin/LICENSE new file mode 100644 index 0000000..9825cba --- /dev/null +++ b/licenses/vue3-element-admin/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021-present 有来开源组织 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/mock/auth.mock.ts b/mock/auth.mock.ts new file mode 100644 index 0000000..90f08f7 --- /dev/null +++ b/mock/auth.mock.ts @@ -0,0 +1,43 @@ +import { defineMock } from "./base"; + +export default defineMock([ + { + url: "auth/captcha", + method: ["GET"], + body: { + code: "00000", + data: { + captchaKey: "534b8ef2b0a24121bec76391ddd159f9", + captchaBase64: + "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHgAAAAkCAIAAADNSmkJAAAFKUlEQVR4Xu2ZXUwcVRiGV70wMWo08V5NvPXCrDbFaGpMaZW2hqQxaoiJTRsaMBCNSYtpa2JTKiFSelFa+Q/QZcMWqEhBlh+htbEpZhMrBQrlJ0hBywLLyrJ0WZbje3bqOvPNLHPWrDvdOE9ONmfe78zkzMs335wzWJhJQrBQweS/wTQ6QWgYHdoIOcecOe05O+t2WkutO+p2ZF3Ksg/YV9ZW6FATYajR3nveg60H9327r3O8c35lHgp+r05dPdJzBL73TPSQ8SaCKIxGLsPlop+K0JHrEkPuoT31e5qGmmjARACF0agYyGVNlyVm/pzZXrN9fHGcBkz0UBid+31u93i3XFFT80vN8cvHqWqih8Lo1NpUqS5vwh3vnd223VQ10UNh9NbyrcFQUK6oCawHUipSqGqiB83oBf+CXFGDMp1mS6OqiR4Ko7FexkpOrqhpHGw82nOUqiZ6KIzGrkRuorW0dJMmOy+hOCfYGzb2RBFv6HRO0gEJw/U7y+pgL1bwmTxexN6sZ31TdEwEhdG+gA+7EqyXpUO1uZH20cWL8hMTRt1N9tBXzCJrOIRoCPJpSO2RAp4HmtCdIfZ+2JWgEBN9LbR28seTGU0Zue1tMLp+YIAMSADzfvbkKX4/eb28j4YODiGin3heqmIlLja5hAUCu+nmGY3JWKvpMAlqNGgebsauBOvlqSX+JEx7p7EbTLen53XlzfmWUioqXikrc68Y8N2juJ/fyVsNChGHEE//rBANYWaZz+TRQqpLaBgNsPfDrgSpbS21YtV87IdjrlkX9JZbt5DOma2t9ITo5F+5glN22WwL/n+yDv00mw06orKxOqQ5+J04hhViwzAXETIcJDVm8uxZqktoGx2Nj9t43Wgaul/ERQiGQvtbWnDWgZYW9CXlQFjZ/7ciyHNn+Z2MexTimIeLz59TiIln0M1e+IbPpOAaDUnEYPTi6iqKxpbycs/qKo1tCslfKcffPn9enuMiPPY1vxO/ckeFQ4h46cdGqUWoidE/y54q5tPY5WDrGzQqIXot4BgchEE57e00IMCw2/1qZSVO/7SjA78o9INzcxsbrL+fnTnDDh9mmZn8F30oG1Hm+nABv5mQMopDS/h1HxtqTzWbABMe9sxpPoe9zezeOo1GELqWhPS8t46M0IAYHbdvR1aHbaOjbjfLz2eFhez6dba4yAfgF30o0BFVE8+Mjh/wFxPI+I5mAEHU6Ls+38vhTFwOBGhMDF8gkFpbC5ffsdv/uBs6dIj19dExEtARVXv9YNbop8NFY3aZ6gRRo+tu3IBHnzmdNCBMXldXJKPfL74WzWUJRE+coDUknqsOdZXQbAJYwluVTbOZI3Qt8GFzMwxyjo3RgBiN4fr+elXVpZGRLWXl6PdOTtJBSlBDUK/lnIrjOlrtqWYTQDJaF6FrTXu9sOa1ysrVoM5HVE1GFxZQcyJ/p+xzv6K/rbr6N6+XDpUBl0tKFIrbz78qWB6YnWFMCBld4XLBms+7df75ook/GNzb0GCV7U1Qfz9p64TyQWNjYD3qe9rj4SMJtQP3MyjSDPzWIRHPjH7X4YAvfXoPuyZf9Pbi3PcuXIh4mp3NllYC6XY79C+jl2o8PBipxjnBttn4MgMNnWgfcRJGPI2OL8hTj3LloIlmRicvBhiNykvecpqoa3RSY4DRcLAwyicuOepVR1JjgNFYHWONHL04czTX0UmNAUYD7Pr+xc4wqTHGaBb2OtZvHUmNYUazcA2J6etdUmOk0f8rTKMTxF91RG0D1SwYGwAAAABJRU5ErkJggg==", + }, + msg: "一切ok", + }, + }, + + { + url: "auth/login", + method: ["POST"], + body: { + code: "00000", + data: { + accessToken: + "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZG1pbiIsImp0aSI6IjE2YWJkNTlkOTAxNzQwZDliYmI3ZjczODBhZDkyNzNhIiwidXNlcklkIjoyLCJ1c2VybmFtZSI6ImFkbWluIiwiZGVwdElkIjoxLCJkYXRhU2NvcGUiOjEsImF1dGhvcml0aWVzIjpbIlJPTEVfQURNSU4iXSwiZXhwIjoxNjkxMTAzMzgyfQ.P4cuIfmPepl3HuguhMS7NXn5a7IUPpsLbmtA_rHOhHk", + tokenType: "Bearer", + refreshToken: null, + expires: null, + }, + msg: "一切ok", + }, + }, + + { + url: "auth/logout", + method: ["DELETE"], + body: { + code: "00000", + data: {}, + msg: "string", + }, + }, +]); diff --git a/mock/base.ts b/mock/base.ts new file mode 100644 index 0000000..438e1c1 --- /dev/null +++ b/mock/base.ts @@ -0,0 +1,10 @@ +import path from "path"; +import { createDefineMock } from "vite-plugin-mock-dev-server"; + +export const defineMock = createDefineMock((mock) => { + // 拼接url + mock.url = path.join( + import.meta.env.VITE_APP_BASE_API + "/api/v1/", + mock.url + ); +}); diff --git a/mock/dept.mock.ts b/mock/dept.mock.ts new file mode 100644 index 0000000..e9b6d1b --- /dev/null +++ b/mock/dept.mock.ts @@ -0,0 +1,153 @@ +import { defineMock } from "./base"; + +export default defineMock([ + { + url: "dept/options", + method: ["GET"], + body: { + code: "00000", + data: [ + { + value: 1, + label: "有来技术", + children: [ + { + value: 2, + label: "研发部门", + }, + { + value: 3, + label: "测试部门", + }, + ], + }, + ], + msg: "一切ok", + }, + }, + + { + url: "dept", + method: ["GET"], + body: { + code: "00000", + data: [ + { + id: 1, + parentId: 0, + name: "有来技术", + code: "YOULAI", + sort: 1, + status: 1, + children: [ + { + id: 2, + parentId: 1, + name: "研发部门", + code: "RD001", + sort: 1, + status: 1, + children: [], + createTime: null, + updateTime: "2022-04-19 12:46", + }, + { + id: 3, + parentId: 1, + name: "测试部门", + code: "QA001", + sort: 1, + status: 1, + children: [], + createTime: null, + updateTime: "2022-04-19 12:46", + }, + ], + createTime: null, + updateTime: null, + }, + ], + msg: "一切ok", + }, + }, + + // 新增部门 + { + url: "dept", + method: ["POST"], + body({ body }) { + return { + code: "00000", + data: null, + msg: "新增部门" + body.name + "成功", + }; + }, + }, + + // 获取部门表单数据 + { + url: "dept/:id/form", + method: ["GET"], + body: ({ params }) => { + return { + code: "00000", + data: deptMap[params.id], + msg: "一切ok", + }; + }, + }, + + // 修改部门 + { + url: "dept/:id", + method: ["PUT"], + body({ body }) { + return { + code: "00000", + data: null, + msg: "修改部门" + body.name + "成功", + }; + }, + }, + + // 删除部门 + { + url: "dept/:id", + method: ["DELETE"], + body({ params }) { + return { + code: "00000", + data: null, + msg: "删除部门" + params.id + "成功", + }; + }, + }, +]); + +// 部门映射表数据 +const deptMap: Record = { + 1: { + id: 1, + name: "有来技术", + code: "YOULAI", + parentId: 0, + status: 1, + sort: 1, + }, + 2: { + id: 2, + name: "研发部门", + code: "RD001", + parentId: 1, + status: 1, + sort: 1, + }, + 3: { + id: 3, + name: "测试部门", + code: "QA001", + parentId: 1, + status: 1, + sort: 1, + }, +}; diff --git a/mock/dict.mock.ts b/mock/dict.mock.ts new file mode 100644 index 0000000..63b4b2c --- /dev/null +++ b/mock/dict.mock.ts @@ -0,0 +1,168 @@ +import { defineMock } from "./base"; + +export default defineMock([ + { + url: "dict/:code/options", + method: ["GET"], + body: ({ params }) => { + const code = params.code; + + let list = null; + + if (code == "gender") { + list = [ + { + value: "1", + label: "男", + }, + { + value: "2", + label: "女", + }, + { + value: "0", + label: "保密", + }, + ]; + } + + return { + code: "00000", + data: list, + msg: "一切ok", + }; + }, + }, + + { + url: "dict/page", + method: ["GET"], + body: { + code: "00000", + data: { + list: [ + { + id: 1, + name: "性别", + code: "gender", + status: 1, + dictItems: [ + { + id: 1, + name: "男", + value: "1", + sort: 1, + status: 1, + }, + { + id: 2, + name: "女", + value: "2", + sort: 2, + status: 1, + }, + { + id: 3, + name: "保密", + value: "0", + sort: 3, + status: 1, + }, + ], + }, + ], + total: 1, + }, + msg: "一切ok", + }, + }, + + // 新增字典 + { + url: "dict", + method: ["POST"], + body({ body }) { + return { + code: "00000", + data: null, + msg: "新增字典" + body.name + "成功", + }; + }, + }, + + // 获取字典表单数据 + { + url: "dict/:id/form", + method: ["GET"], + body: ({ params }) => { + return { + code: "00000", + data: dictMap[params.id], + msg: "一切ok", + }; + }, + }, + + // 修改字典 + { + url: "dict/:id", + method: ["PUT"], + body({ body }) { + return { + code: "00000", + data: null, + msg: "修改字典" + body.name + "成功", + }; + }, + }, + + // 删除字典 + { + url: "dict/:id", + method: ["DELETE"], + body({ params }) { + return { + code: "00000", + data: null, + msg: "删除字典" + params.id + "成功", + }; + }, + }, +]); + +// 字典映射表数据 +const dictMap: Record = { + 1: { + code: "00000", + data: { + id: 1, + name: "性别", + code: "gender", + status: 1, + dictItems: [ + { + id: 1, + name: "男", + value: "1", + sort: 1, + status: 1, + }, + { + id: 2, + name: "女", + value: "2", + sort: 2, + status: 1, + }, + { + id: 3, + name: "未知", + value: "0", + sort: 3, + status: 1, + }, + ], + }, + msg: "一切ok", + }, +}; diff --git a/mock/log.mock.ts b/mock/log.mock.ts new file mode 100644 index 0000000..08e63f2 --- /dev/null +++ b/mock/log.mock.ts @@ -0,0 +1,225 @@ +import { defineMock } from "./base"; + +export default defineMock([ + { + url: "logs/page", + method: ["GET"], + body: { + code: "00000", + data: { + list: [ + { + id: 36192, + module: "菜单", + content: "菜单列表", + requestUri: "/api/v1/menus", + method: null, + ip: "183.156.148.241", + region: "浙江省 杭州市", + browser: "Chrome 109.0.0.0", + os: "OSX", + executionTime: 5, + createBy: null, + createTime: "2024-07-07 20:38:47", + operator: "系统管理员", + }, + { + id: 36190, + module: "字典", + content: "字典分页列表", + requestUri: "/api/v1/dict/page", + method: null, + ip: "183.156.148.241", + region: "浙江省 杭州市", + browser: "Chrome 109.0.0.0", + os: "OSX", + executionTime: 9, + createBy: null, + createTime: "2024-07-07 20:38:45", + operator: "系统管理员", + }, + { + id: 36193, + module: "部门", + content: "部门列表", + requestUri: "/api/v1/dept", + method: null, + ip: "192.168.31.134", + region: "0 内网IP", + browser: "Chrome 125.0.0.0", + os: "Windows 10 or Windows Server 2016", + executionTime: 27, + createBy: null, + createTime: "2024-07-07 20:38:45", + operator: "系统管理员", + }, + { + id: 36191, + module: "菜单", + content: "菜单列表", + requestUri: "/api/v1/menus", + method: null, + ip: "192.168.31.134", + region: "0 内网IP", + browser: "Chrome 125.0.0.0", + os: "Windows 10 or Windows Server 2016", + executionTime: 39, + createBy: null, + createTime: "2024-07-07 20:38:44", + operator: "系统管理员", + }, + { + id: 36189, + module: "角色", + content: "角色分页列表", + requestUri: "/api/v1/roles/page", + method: null, + ip: "192.168.31.134", + region: "0 内网IP", + browser: "Chrome 125.0.0.0", + os: "Windows 10 or Windows Server 2016", + executionTime: 55, + createBy: null, + createTime: "2024-07-07 20:38:43", + operator: "系统管理员", + }, + { + id: 36188, + module: "用户", + content: "用户分页列表", + requestUri: "/api/v1/users/page", + method: null, + ip: "192.168.31.134", + region: "0 内网IP", + browser: "Chrome 125.0.0.0", + os: "Windows 10 or Windows Server 2016", + executionTime: 92, + createBy: null, + createTime: "2024-07-07 20:38:42", + operator: "系统管理员", + }, + { + id: 36187, + module: "登录", + content: "登录", + requestUri: "/api/v1/auth/login", + method: null, + ip: "192.168.31.134", + region: "0 内网IP", + browser: "Chrome 125.0.0.0", + os: "Windows 10 or Windows Server 2016", + executionTime: 19340, + createBy: null, + createTime: "2024-07-07 20:38:09", + operator: "系统管理员", + }, + { + id: 36186, + module: "登录", + content: "登录", + requestUri: "/api/v1/auth/login", + method: null, + ip: "192.168.31.134", + region: "0 内网IP", + browser: "Chrome 125.0.0.0", + os: "Windows 10 or Windows Server 2016", + executionTime: 19869, + createBy: null, + createTime: "2024-07-07 20:37:59", + operator: "系统管理员", + }, + { + id: 36185, + module: "登录", + content: "登录", + requestUri: "/api/v1/auth/login", + method: null, + ip: "112.103.111.59", + region: "黑龙江省 哈尔滨市", + browser: "Chrome 97.0.4692.98", + os: "Android", + executionTime: 96, + createBy: null, + createTime: "2024-07-07 20:37:21", + operator: "系统管理员", + }, + { + id: 36184, + module: "登录", + content: "登录", + requestUri: "/api/v1/auth/login", + method: null, + ip: "114.86.204.190", + region: "上海 上海市", + browser: "Chrome 125.0.0.0", + os: "Windows 10 or Windows Server 2016", + executionTime: 89, + createBy: null, + createTime: "2024-07-07 20:29:37", + operator: "系统管理员", + }, + ], + total: 36188, + }, + msg: "一切ok", + }, + }, + { + url: "logs/visit-trend", + method: ["GET"], + body: { + code: "00000", + data: { + dates: [ + "2024-06-30", + "2024-07-01", + "2024-07-02", + "2024-07-03", + "2024-07-04", + "2024-07-05", + "2024-07-06", + "2024-07-07", + ], + pvList: [1751, 5168, 4882, 5301, 4721, 4885, 1901, 1003], + uvList: null, + ipList: [207, 566, 565, 631, 579, 496, 222, 152], + }, + msg: "一切ok", + }, + }, + + { + url: "logs/visit-stats", + method: ["GET"], + body: { + code: "00000", + data: [ + { + type: "pv", + title: "浏览量", + todayCount: 1003, + totalCount: 36193, + growthRate: -0.35, + granularityLabel: "日", + }, + { + type: "uv", + title: "访客数", + todayCount: 100, + totalCount: 2000, + growthRate: 0, + granularityLabel: "日", + }, + { + type: "ip", + title: "IP数", + todayCount: 152, + totalCount: 3234, + growthRate: -0.2, + granularityLabel: "日", + }, + ], + msg: "一切ok", + }, + }, +]); diff --git a/mock/menu.mock.ts b/mock/menu.mock.ts new file mode 100644 index 0000000..a31ef4f --- /dev/null +++ b/mock/menu.mock.ts @@ -0,0 +1,1620 @@ +import { defineMock } from "./base"; + +export default defineMock([ + { + url: "menus/routes", + method: ["GET"], + body: { + code: "00000", + data: [ + { + path: "/system", + component: "Layout", + redirect: "/system/user", + name: "/system", + meta: { + title: "系统管理", + icon: "system", + hidden: false, + alwaysShow: false, + params: null, + }, + children: [ + { + path: "user", + component: "system/user/index", + name: "User", + meta: { + title: "用户管理", + icon: "el-icon-User", + hidden: false, + keepAlive: true, + alwaysShow: false, + params: null, + }, + }, + { + path: "role", + component: "system/role/index", + name: "Role", + meta: { + title: "角色管理", + icon: "role", + hidden: false, + keepAlive: true, + alwaysShow: false, + params: null, + }, + }, + { + path: "menu", + component: "system/menu/index", + name: "Menu", + meta: { + title: "菜单管理", + icon: "menu", + hidden: false, + keepAlive: true, + alwaysShow: false, + params: null, + }, + }, + { + path: "dept", + component: "system/dept/index", + name: "Dept", + meta: { + title: "部门管理", + icon: "tree", + hidden: false, + keepAlive: true, + alwaysShow: false, + params: null, + }, + }, + { + path: "dict", + component: "system/dict/index", + name: "Dict", + meta: { + title: "字典管理", + icon: "dict", + hidden: false, + keepAlive: true, + alwaysShow: false, + params: null, + }, + }, + ], + }, + { + path: "/api", + component: "Layout", + name: "/api", + meta: { + title: "接口文档", + icon: "api", + hidden: false, + alwaysShow: true, + params: null, + }, + children: [ + { + path: "apifox", + component: "demo/api/apifox", + name: "Apifox", + meta: { + title: "Apifox", + icon: "api", + hidden: false, + keepAlive: true, + alwaysShow: false, + params: null, + }, + }, + ], + }, + { + path: "/doc", + component: "Layout", + redirect: "https://juejin.cn/post/7228990409909108793", + name: "/doc", + meta: { + title: "平台文档", + icon: "document", + hidden: false, + alwaysShow: false, + params: null, + }, + children: [ + { + path: "internal-doc", + component: "demo/internal-doc", + name: "InternalDoc", + meta: { + title: "平台文档(内嵌)", + icon: "document", + hidden: false, + alwaysShow: false, + params: null, + }, + }, + { + path: "https://juejin.cn/post/7228990409909108793", + name: "Https://juejin.cn/post/7228990409909108793", + meta: { + title: "平台文档(外链)", + icon: "el-icon-Link", + hidden: false, + alwaysShow: false, + params: null, + }, + }, + ], + }, + { + path: "/multi-level", + component: "Layout", + name: "/multiLevel", + meta: { + title: "多级菜单", + icon: "cascader", + hidden: false, + alwaysShow: true, + params: null, + }, + children: [ + { + path: "multi-level1", + component: "demo/multi-level/level1", + name: "MultiLevel1", + meta: { + title: "菜单一级", + icon: "", + hidden: false, + alwaysShow: true, + params: null, + }, + children: [ + { + path: "multi-level2", + component: "demo/multi-level/children/level2", + name: "MultiLevel2", + meta: { + title: "菜单二级", + icon: "", + hidden: false, + alwaysShow: false, + params: null, + }, + children: [ + { + path: "multi-level3-1", + component: "demo/multi-level/children/children/level3-1", + name: "MultiLevel31", + meta: { + title: "菜单三级-1", + icon: "", + hidden: false, + keepAlive: true, + alwaysShow: false, + params: null, + }, + }, + { + path: "multi-level3-2", + component: "demo/multi-level/children/children/level3-2", + name: "MultiLevel32", + meta: { + title: "菜单三级-2", + icon: "", + hidden: false, + keepAlive: true, + alwaysShow: false, + params: null, + }, + }, + ], + }, + ], + }, + ], + }, + { + path: "/component", + component: "Layout", + name: "/component", + meta: { + title: "组件封装", + icon: "menu", + hidden: false, + alwaysShow: false, + params: null, + }, + children: [ + { + path: "curd", + component: "demo/curd/index", + name: "Curd", + meta: { + title: "增删改查", + icon: "", + hidden: false, + keepAlive: true, + alwaysShow: false, + params: null, + }, + }, + { + path: "table-select", + component: "demo/table-select/index", + name: "TableSelect", + meta: { + title: "列表选择器", + icon: "", + hidden: false, + keepAlive: true, + alwaysShow: false, + params: null, + }, + }, + { + path: "wang-editor", + component: "demo/wang-editor", + name: "WangEditor", + meta: { + title: "富文本编辑器", + icon: "", + hidden: false, + keepAlive: true, + alwaysShow: false, + params: null, + }, + }, + { + path: "upload", + component: "demo/upload", + name: "Upload", + meta: { + title: "图片上传", + icon: "", + hidden: false, + keepAlive: true, + alwaysShow: false, + params: null, + }, + }, + { + path: "icon-selector", + component: "demo/icon-selector", + name: "IconSelector", + meta: { + title: "图标选择器", + icon: "", + hidden: false, + keepAlive: true, + alwaysShow: false, + params: null, + }, + }, + { + path: "dict-demo", + component: "demo/dict", + name: "DictDemo", + meta: { + title: "字典组件", + icon: "", + hidden: false, + keepAlive: true, + alwaysShow: false, + params: null, + }, + }, + ], + }, + { + path: "/route-param", + component: "Layout", + name: "/routeParam", + meta: { + title: "路由参数", + icon: "el-icon-ElementPlus", + hidden: false, + alwaysShow: true, + params: null, + }, + children: [ + { + path: "route-param-type1", + component: "demo/route-param", + name: "RouteParamType1", + meta: { + title: "参数(type=1)", + icon: "el-icon-Star", + hidden: false, + keepAlive: true, + alwaysShow: false, + params: { + type: "1", + }, + }, + }, + { + path: "route-param-type2", + component: "demo/route-param", + name: "RouteParamType2", + meta: { + title: "参数(type=2)", + icon: "el-icon-StarFilled", + hidden: false, + keepAlive: true, + alwaysShow: false, + params: { + type: "2", + }, + }, + }, + ], + }, + { + path: "/function", + component: "Layout", + name: "/function", + meta: { + title: "功能演示", + icon: "menu", + hidden: false, + alwaysShow: false, + params: null, + }, + children: [ + { + path: "icon-demo", + component: "demo/icons", + name: "IconDemo", + meta: { + title: "Icons", + icon: "el-icon-Notification", + hidden: false, + keepAlive: true, + alwaysShow: false, + params: null, + }, + }, + { + path: "/function/websocket", + component: "demo/websocket", + name: "/function/websocket", + meta: { + title: "Websocket", + icon: "", + hidden: false, + keepAlive: true, + alwaysShow: false, + params: null, + }, + }, + { + path: "other/:id", + component: "demo/other", + name: "Other/:id", + meta: { + title: "敬请期待...", + icon: "", + hidden: false, + alwaysShow: false, + params: null, + }, + }, + ], + }, + ], + msg: "一切ok", + }, + }, + + { + url: "menus", + method: ["GET"], + body: { + code: "00000", + data: [ + { + id: 1, + parentId: 0, + name: "系统管理", + type: "CATALOG", + routeName: "", + routePath: "/system", + component: "Layout", + sort: 1, + visible: 1, + icon: "system", + redirect: "/system/user", + perm: null, + children: [ + { + id: 2, + parentId: 1, + name: "用户管理", + type: "MENU", + routeName: "User", + routePath: "user", + component: "system/user/index", + sort: 1, + visible: 1, + icon: "el-icon-User", + redirect: null, + perm: null, + children: [ + { + id: 105, + parentId: 2, + name: "用户查询", + type: "BUTTON", + routeName: null, + routePath: "", + component: null, + sort: 0, + visible: 1, + icon: "", + redirect: null, + perm: "sys:user:query", + children: [], + }, + { + id: 31, + parentId: 2, + name: "用户新增", + type: "BUTTON", + routeName: null, + routePath: "", + component: null, + sort: 1, + visible: 1, + icon: "", + redirect: "", + perm: "sys:user:add", + children: [], + }, + { + id: 32, + parentId: 2, + name: "用户编辑", + type: "BUTTON", + routeName: null, + routePath: "", + component: null, + sort: 2, + visible: 1, + icon: "", + redirect: "", + perm: "sys:user:edit", + children: [], + }, + { + id: 33, + parentId: 2, + name: "用户删除", + type: "BUTTON", + routeName: null, + routePath: "", + component: null, + sort: 3, + visible: 1, + icon: "", + redirect: "", + perm: "sys:user:delete", + children: [], + }, + { + id: 88, + parentId: 2, + name: "重置密码", + type: "BUTTON", + routeName: null, + routePath: "", + component: null, + sort: 4, + visible: 1, + icon: "", + redirect: null, + perm: "sys:user:password:reset", + children: [], + }, + { + id: 106, + parentId: 2, + name: "用户导入", + type: "BUTTON", + routeName: null, + routePath: "", + component: null, + sort: 5, + visible: 1, + icon: "", + redirect: null, + perm: "sys:user:import", + children: [], + }, + { + id: 107, + parentId: 2, + name: "用户导出", + type: "BUTTON", + routeName: null, + routePath: "", + component: null, + sort: 6, + visible: 1, + icon: "", + redirect: null, + perm: "sys:user:export", + children: [], + }, + ], + }, + { + id: 3, + parentId: 1, + name: "角色管理", + type: "MENU", + routeName: "Role", + routePath: "role", + component: "system/role/index", + sort: 2, + visible: 1, + icon: "role", + redirect: null, + perm: null, + children: [ + { + id: 70, + parentId: 3, + name: "角色新增", + type: "BUTTON", + routeName: null, + routePath: "", + component: null, + sort: 1, + visible: 1, + icon: "", + redirect: null, + perm: "sys:role:add", + children: [], + }, + { + id: 71, + parentId: 3, + name: "角色编辑", + type: "BUTTON", + routeName: null, + routePath: "", + component: null, + sort: 2, + visible: 1, + icon: "", + redirect: null, + perm: "sys:role:edit", + children: [], + }, + { + id: 72, + parentId: 3, + name: "角色删除", + type: "BUTTON", + routeName: null, + routePath: "", + component: null, + sort: 3, + visible: 1, + icon: "", + redirect: null, + perm: "sys:role:delete", + children: [], + }, + ], + }, + { + id: 4, + parentId: 1, + name: "菜单管理", + type: "MENU", + routeName: "Menu", + routePath: "menu", + component: "system/menu/index", + sort: 3, + visible: 1, + icon: "menu", + redirect: null, + perm: null, + children: [ + { + id: 73, + parentId: 4, + name: "菜单新增", + type: "BUTTON", + routeName: null, + routePath: "", + component: null, + sort: 1, + visible: 1, + icon: "", + redirect: null, + perm: "sys:menu:add", + children: [], + }, + { + id: 75, + parentId: 4, + name: "菜单删除", + type: "BUTTON", + routeName: null, + routePath: "", + component: null, + sort: 3, + visible: 1, + icon: "", + redirect: null, + perm: "sys:menu:delete", + children: [], + }, + { + id: 74, + parentId: 4, + name: "菜单编辑", + type: "BUTTON", + routeName: null, + routePath: "", + component: null, + sort: 3, + visible: 1, + icon: "", + redirect: null, + perm: "sys:menu:edit", + children: [], + }, + ], + }, + { + id: 5, + parentId: 1, + name: "部门管理", + type: "MENU", + routeName: "Dept", + routePath: "dept", + component: "system/dept/index", + sort: 4, + visible: 1, + icon: "tree", + redirect: null, + perm: null, + children: [ + { + id: 76, + parentId: 5, + name: "部门新增", + type: "BUTTON", + routeName: null, + routePath: "", + component: null, + sort: 1, + visible: 1, + icon: "", + redirect: null, + perm: "sys:dept:add", + children: [], + }, + { + id: 77, + parentId: 5, + name: "部门编辑", + type: "BUTTON", + routeName: null, + routePath: "", + component: null, + sort: 2, + visible: 1, + icon: "", + redirect: null, + perm: "sys:dept:edit", + children: [], + }, + { + id: 78, + parentId: 5, + name: "部门删除", + type: "BUTTON", + routeName: null, + routePath: "", + component: null, + sort: 3, + visible: 1, + icon: "", + redirect: null, + perm: "sys:dept:delete", + children: [], + }, + ], + }, + { + id: 6, + parentId: 1, + name: "字典管理", + type: "MENU", + routeName: "Dict", + routePath: "dict", + component: "system/dict/index", + sort: 5, + visible: 1, + icon: "dict", + redirect: null, + perm: null, + children: [ + { + id: 79, + parentId: 6, + name: "字典类型新增", + type: "BUTTON", + routeName: null, + routePath: "", + component: null, + sort: 1, + visible: 1, + icon: "", + redirect: null, + perm: "sys:dict_type:add", + children: [], + }, + { + id: 81, + parentId: 6, + name: "字典类型编辑", + type: "BUTTON", + routeName: null, + routePath: "", + component: null, + sort: 2, + visible: 1, + icon: "", + redirect: null, + perm: "sys:dict_type:edit", + children: [], + }, + { + id: 84, + parentId: 6, + name: "字典类型删除", + type: "BUTTON", + routeName: null, + routePath: "", + component: null, + sort: 3, + visible: 1, + icon: "", + redirect: null, + perm: "sys:dict_type:delete", + children: [], + }, + { + id: 85, + parentId: 6, + name: "字典数据新增", + type: "BUTTON", + routeName: null, + routePath: "", + component: null, + sort: 4, + visible: 1, + icon: "", + redirect: null, + perm: "sys:dict:add", + children: [], + }, + { + id: 86, + parentId: 6, + name: "字典数据编辑", + type: "BUTTON", + routeName: null, + routePath: "", + component: null, + sort: 5, + visible: 1, + icon: "", + redirect: null, + perm: "sys:dict:edit", + children: [], + }, + { + id: 87, + parentId: 6, + name: "字典数据删除", + type: "BUTTON", + routeName: null, + routePath: "", + component: null, + sort: 6, + visible: 1, + icon: "", + redirect: null, + perm: "sys:dict:delete", + children: [], + }, + ], + }, + ], + }, + { + id: 40, + parentId: 0, + name: "接口文档", + type: "CATALOG", + routeName: null, + routePath: "/api", + component: "Layout", + sort: 7, + visible: 1, + icon: "api", + redirect: "", + perm: null, + children: [ + { + id: 41, + parentId: 40, + name: "Apifox", + type: "MENU", + routeName: null, + routePath: "apifox", + component: "demo/api/apifox", + sort: 1, + visible: 1, + icon: "api", + redirect: "", + perm: null, + children: [], + }, + ], + }, + { + id: 26, + parentId: 0, + name: "平台文档", + type: "CATALOG", + routeName: null, + routePath: "/doc", + component: "Layout", + sort: 8, + visible: 1, + icon: "document", + redirect: "https://juejin.cn/post/7228990409909108793", + perm: null, + children: [ + { + id: 102, + parentId: 26, + name: "平台文档(内嵌)", + type: "EXTLINK", + routeName: null, + routePath: "internal-doc", + component: "demo/internal-doc", + sort: 1, + visible: 1, + icon: "document", + redirect: "", + perm: null, + children: [], + }, + { + id: 30, + parentId: 26, + name: "平台文档(外链)", + type: "EXTLINK", + routeName: null, + routePath: "https://juejin.cn/post/7228990409909108793", + component: "", + sort: 2, + visible: 1, + icon: "link", + redirect: "", + perm: null, + children: [], + }, + ], + }, + { + id: 20, + parentId: 0, + name: "多级菜单", + type: "CATALOG", + routeName: null, + routePath: "/multi-level", + component: "Layout", + sort: 9, + visible: 1, + icon: "cascader", + redirect: "", + perm: null, + children: [ + { + id: 21, + parentId: 20, + name: "菜单一级", + type: "MENU", + routeName: null, + routePath: "multi-level1", + component: "demo/multi-level/level1", + sort: 1, + visible: 1, + icon: "", + redirect: "", + perm: null, + children: [ + { + id: 22, + parentId: 21, + name: "菜单二级", + type: "MENU", + routeName: null, + routePath: "multi-level2", + component: "demo/multi-level/children/level2", + sort: 1, + visible: 1, + icon: "", + redirect: null, + perm: null, + children: [ + { + id: 23, + parentId: 22, + name: "菜单三级-1", + type: "MENU", + routeName: null, + routePath: "multi-level3-1", + component: "demo/multi-level/children/children/level3-1", + sort: 1, + visible: 1, + icon: "", + redirect: "", + perm: null, + children: [], + }, + { + id: 24, + parentId: 22, + name: "菜单三级-2", + type: "MENU", + routeName: null, + routePath: "multi-level3-2", + component: "demo/multi-level/children/children/level3-2", + sort: 2, + visible: 1, + icon: "", + redirect: "", + perm: null, + children: [], + }, + ], + }, + ], + }, + ], + }, + { + id: 36, + parentId: 0, + name: "组件封装", + type: "CATALOG", + routeName: null, + routePath: "/component", + component: "Layout", + sort: 10, + visible: 1, + icon: "menu", + redirect: "", + perm: null, + children: [ + { + id: 108, + parentId: 36, + name: "增删改查", + type: "MENU", + routeName: null, + routePath: "curd", + component: "demo/curd/index", + sort: 0, + visible: 1, + icon: "", + redirect: "", + perm: null, + children: [], + }, + { + id: 109, + parentId: 36, + name: "列表选择器", + type: "MENU", + routeName: null, + routePath: "table-select", + component: "demo/table-select/index", + sort: 1, + visible: 1, + icon: "", + redirect: "", + perm: null, + children: [], + }, + { + id: 37, + parentId: 36, + name: "富文本编辑器", + type: "MENU", + routeName: null, + routePath: "wang-editor", + component: "demo/wang-editor", + sort: 2, + visible: 1, + icon: "", + redirect: "", + perm: null, + children: [], + }, + { + id: 38, + parentId: 36, + name: "图片上传", + type: "MENU", + routeName: null, + routePath: "upload", + component: "demo/upload", + sort: 3, + visible: 1, + icon: "", + redirect: "", + perm: null, + children: [], + }, + { + id: 95, + parentId: 36, + name: "字典组件", + type: "MENU", + routeName: null, + routePath: "dict-demo", + component: "demo/dict", + sort: 4, + visible: 1, + icon: "", + redirect: "", + perm: null, + children: [], + }, + { + id: 39, + parentId: 36, + name: "图标选择器", + type: "MENU", + routeName: null, + routePath: "icon-selector", + component: "demo/icon-selector", + sort: 4, + visible: 1, + icon: "", + redirect: "", + perm: null, + children: [], + }, + ], + }, + { + id: 110, + parentId: 0, + name: "路由参数", + type: "CATALOG", + routeName: null, + routePath: "/route-param", + component: "Layout", + sort: 11, + visible: 1, + icon: "el-icon-ElementPlus", + redirect: null, + perm: null, + children: [ + { + id: 111, + parentId: 110, + name: "参数(type=1)", + type: "MENU", + routeName: null, + routePath: "route-param-type1", + component: "demo/route-param", + sort: 1, + visible: 1, + icon: "el-icon-Star", + redirect: null, + perm: null, + children: [], + }, + { + id: 112, + parentId: 110, + name: "参数(type=2)", + type: "MENU", + routeName: null, + routePath: "route-param-type2", + component: "demo/route-param", + sort: 2, + visible: 1, + icon: "el-icon-StarFilled", + redirect: null, + perm: null, + children: [], + }, + ], + }, + { + id: 89, + parentId: 0, + name: "功能演示", + type: "CATALOG", + routeName: null, + routePath: "/function", + component: "Layout", + sort: 12, + visible: 1, + icon: "menu", + redirect: "", + perm: null, + children: [ + { + id: 97, + parentId: 89, + name: "Icons", + type: "MENU", + routeName: null, + routePath: "icon-demo", + component: "demo/icons", + sort: 2, + visible: 1, + icon: "el-icon-Notification", + redirect: "", + perm: null, + children: [], + }, + { + id: 90, + parentId: 89, + name: "Websocket", + type: "MENU", + routeName: null, + routePath: "/function/websocket", + component: "demo/websocket", + sort: 3, + visible: 1, + icon: "", + redirect: "", + perm: null, + children: [], + }, + { + id: 91, + parentId: 89, + name: "敬请期待...", + type: "CATALOG", + routeName: null, + routePath: "other/:id", + component: "demo/other", + sort: 4, + visible: 1, + icon: "", + redirect: "", + perm: null, + children: [], + }, + ], + }, + ], + msg: "一切ok", + }, + }, + + { + url: "menus/options", + method: ["GET"], + body: { + code: "00000", + data: [ + { + value: 1, + label: "系统管理", + children: [ + { + value: 2, + label: "用户管理", + children: [ + { + value: 105, + label: "用户查询", + }, + { + value: 31, + label: "用户新增", + }, + { + value: 32, + label: "用户编辑", + }, + { + value: 33, + label: "用户删除", + }, + { + value: 88, + label: "重置密码", + }, + { + value: 106, + label: "用户导入", + }, + { + value: 107, + label: "用户导出", + }, + ], + }, + { + value: 3, + label: "角色管理", + children: [ + { + value: 70, + label: "角色新增", + }, + { + value: 71, + label: "角色编辑", + }, + { + value: 72, + label: "角色删除", + }, + ], + }, + { + value: 4, + label: "菜单管理", + children: [ + { + value: 73, + label: "菜单新增", + }, + { + value: 75, + label: "菜单删除", + }, + { + value: 74, + label: "菜单编辑", + }, + ], + }, + { + value: 5, + label: "部门管理", + children: [ + { + value: 76, + label: "部门新增", + }, + { + value: 77, + label: "部门编辑", + }, + { + value: 78, + label: "部门删除", + }, + ], + }, + { + value: 6, + label: "字典管理", + children: [ + { + value: 79, + label: "字典类型新增", + }, + { + value: 81, + label: "字典类型编辑", + }, + { + value: 84, + label: "字典类型删除", + }, + { + value: 85, + label: "字典数据新增", + }, + { + value: 86, + label: "字典数据编辑", + }, + { + value: 87, + label: "字典数据删除", + }, + ], + }, + ], + }, + { + value: 40, + label: "接口文档", + children: [ + { + value: 41, + label: "Apifox", + }, + ], + }, + { + value: 26, + label: "平台文档", + children: [ + { + value: 102, + label: "平台文档(内嵌)", + }, + { + value: 30, + label: "平台文档(外链)", + }, + ], + }, + { + value: 20, + label: "多级菜单", + children: [ + { + value: 21, + label: "菜单一级", + children: [ + { + value: 22, + label: "菜单二级", + children: [ + { + value: 23, + label: "菜单三级-1", + }, + { + value: 24, + label: "菜单三级-2", + }, + ], + }, + ], + }, + ], + }, + { + value: 36, + label: "组件封装", + children: [ + { + value: 108, + label: "增删改查", + }, + { + value: 109, + label: "列表选择器", + }, + { + value: 37, + label: "富文本编辑器", + }, + { + value: 38, + label: "图片上传", + }, + { + value: 95, + label: "字典组件", + }, + { + value: 39, + label: "图标选择器", + }, + ], + }, + { + value: 110, + label: "路由参数", + children: [ + { + value: 111, + label: "参数(type=1)", + }, + { + value: 112, + label: "参数(type=2)", + }, + ], + }, + { + value: 89, + label: "功能演示", + children: [ + { + value: 97, + label: "Icons", + }, + { + value: 90, + label: "Websocket", + }, + { + value: 91, + label: "敬请期待...", + }, + ], + }, + ], + msg: "一切ok", + }, + }, + + // 新增菜单 + { + url: "menus", + method: ["POST"], + body({ body }) { + return { + code: "00000", + data: null, + msg: "新增菜单" + body.name + "成功", + }; + }, + }, + + // 获取菜单表单数据 + { + url: "menus/:id/form", + method: ["GET"], + body: ({ params }) => { + return { + code: "00000", + data: menuMap[params.id], + msg: "一切ok", + }; + }, + }, + + // 修改菜单 + { + url: "menus/:id", + method: ["PUT"], + body({ body }) { + return { + code: "00000", + data: null, + msg: "修改菜单" + body.name + "成功", + }; + }, + }, + + // 删除菜单 + { + url: "menus/:id", + method: ["DELETE"], + body({ params }) { + return { + code: "00000", + data: null, + msg: "删除菜单" + params.id + "成功", + }; + }, + }, +]); + +// 菜单映射表数据 +const menuMap: Record = { + 1: { + id: 1, + parentId: 0, + name: "系统管理", + type: "CATALOG", + routeName: "", + routePath: "/system", + component: "Layout", + perm: null, + visible: 1, + sort: 1, + icon: "system", + redirect: "/system/user", + keepAlive: null, + alwaysShow: null, + params: null, + }, + 2: { + id: 2, + parentId: 1, + name: "用户管理", + type: "MENU", + routeName: "User", + routePath: "user", + component: "system/user/index", + perm: null, + visible: 1, + sort: 1, + icon: "user", + redirect: null, + keepAlive: 1, + alwaysShow: null, + }, + 3: { + id: 3, + parentId: 1, + name: "角色管理", + type: "MENU", + routeName: "Role", + routePath: "role", + component: "system/role/index", + perm: null, + visible: 1, + sort: 2, + icon: "role", + redirect: null, + keepAlive: 1, + alwaysShow: null, + }, + 4: { + id: 4, + parentId: 1, + name: "菜单管理", + type: "MENU", + routeName: "Menu", + routePath: "menu", + component: "system/menu/index", + perm: null, + visible: 1, + sort: 3, + icon: "menu", + redirect: null, + keepAlive: 1, + alwaysShow: null, + }, + 5: { + id: 5, + parentId: 1, + name: "部门管理", + type: "MENU", + routeName: "Dept", + routePath: "dept", + component: "system/dept/index", + perm: null, + visible: 1, + sort: 4, + icon: "tree", + redirect: null, + keepAlive: 1, + alwaysShow: null, + }, + 6: { + id: 6, + parentId: 1, + name: "字典管理", + type: "MENU", + routeName: "Dict", + routePath: "dict", + component: "system/dict/index", + perm: null, + visible: 1, + sort: 5, + icon: "dict", + redirect: null, + keepAlive: 1, + alwaysShow: null, + }, +}; diff --git a/mock/role.mock.ts b/mock/role.mock.ts new file mode 100644 index 0000000..cc87026 --- /dev/null +++ b/mock/role.mock.ts @@ -0,0 +1,335 @@ +import { defineMock } from "./base"; + +export default defineMock([ + { + url: "roles/options", + method: ["GET"], + body: { + code: "00000", + data: [ + { + value: 2, + label: "系统管理员", + }, + { + value: 4, + label: "系统管理员1", + }, + { + value: 5, + label: "系统管理员2", + }, + { + value: 6, + label: "系统管理员3", + }, + { + value: 7, + label: "系统管理员4", + }, + { + value: 8, + label: "系统管理员5", + }, + { + value: 9, + label: "系统管理员6", + }, + { + value: 10, + label: "系统管理员7", + }, + { + value: 11, + label: "系统管理员8", + }, + { + value: 12, + label: "系统管理员9", + }, + { + value: 3, + label: "访问游客", + }, + ], + msg: "一切ok", + }, + }, + + { + url: "roles/page", + method: ["GET"], + body: { + code: "00000", + data: { + list: [ + { + id: 2, + name: "系统管理员", + code: "ADMIN", + status: 1, + sort: 2, + createTime: "2021-03-25 12:39:54", + updateTime: null, + }, + { + id: 3, + name: "访问游客", + code: "GUEST", + status: 1, + sort: 3, + createTime: "2021-05-26 15:49:05", + updateTime: "2019-05-05 16:00:00", + }, + { + id: 4, + name: "系统管理员1", + code: "ADMIN1", + status: 1, + sort: 2, + createTime: "2021-03-25 12:39:54", + updateTime: null, + }, + { + id: 5, + name: "系统管理员2", + code: "ADMIN2", + status: 1, + sort: 2, + createTime: "2021-03-25 12:39:54", + updateTime: null, + }, + { + id: 6, + name: "系统管理员3", + code: "ADMIN3", + status: 1, + sort: 2, + createTime: "2021-03-25 12:39:54", + updateTime: null, + }, + { + id: 7, + name: "系统管理员4", + code: "ADMIN4", + status: 1, + sort: 2, + createTime: "2021-03-25 12:39:54", + updateTime: null, + }, + { + id: 8, + name: "系统管理员5", + code: "ADMIN5", + status: 1, + sort: 2, + createTime: "2021-03-25 12:39:54", + updateTime: null, + }, + { + id: 9, + name: "系统管理员6", + code: "ADMIN6", + status: 1, + sort: 2, + createTime: "2021-03-25 12:39:54", + updateTime: "2023-12-04 11:43:15", + }, + { + id: 10, + name: "系统管理员7", + code: "ADMIN7", + status: 1, + sort: 2, + createTime: "2021-03-25 12:39:54", + updateTime: null, + }, + { + id: 11, + name: "系统管理员8", + code: "ADMIN8", + status: 1, + sort: 2, + createTime: "2021-03-25 12:39:54", + updateTime: null, + }, + ], + total: 10, + }, + msg: "一切ok", + }, + }, + + // 新增角色 + { + url: "roles", + method: ["POST"], + body({ body }) { + return { + code: "00000", + data: null, + msg: "新增角色" + body.name + "成功", + }; + }, + }, + + // 获取角色表单数据 + { + url: "roles/:id/form", + method: ["GET"], + body: ({ params }) => { + return { + code: "00000", + data: roleMap[params.id], + msg: "一切ok", + }; + }, + }, + // 修改角色 + { + url: "roles/:id", + method: ["PUT"], + body({ body }) { + return { + code: "00000", + data: null, + msg: "修改角色" + body.name + "成功", + }; + }, + }, + + // 删除角色 + { + url: "roles/:id", + method: ["DELETE"], + body({ params }) { + return { + code: "00000", + data: null, + msg: "删除角色" + params.id + "成功", + }; + }, + }, + // 获取角色拥有的菜单ID + { + url: "roles/:id/menuIds", + method: ["GET"], + body: ({ params }) => { + return { + code: "00000", + data: [ + 1, 2, 31, 32, 33, 88, 3, 70, 71, 72, 4, 73, 75, 74, 5, 76, 77, 78, 6, + 79, 81, 84, 85, 86, 87, 40, 41, 26, 30, 20, 21, 22, 23, 24, 89, 90, + 91, 36, 37, 38, 39, 93, 94, 95, 97, 102, 89, 90, 91, 93, 94, 95, 97, + 102, 103, 104, + ], + msg: "一切ok", + }; + }, + }, + // 保存角色菜单 + { + url: "roles/:id/menus", + method: ["PUT"], + body: { + code: "00000", + data: null, + msg: "一切ok", + }, + }, +]); + +// 角色映射表数据 +const roleMap: Record = { + 2: { + id: 2, + name: "系统管理员", + code: "ADMIN", + status: 1, + sort: 2, + createTime: "2021-03-25 12:39:54", + updateTime: null, + }, + 3: { + id: 3, + name: "访问游客", + code: "GUEST", + status: 1, + sort: 3, + createTime: "2021-05-26 15:49:05", + updateTime: "2019-05-05 16:00:00", + }, + 4: { + id: 4, + name: "系统管理员1", + code: "ADMIN1", + status: 1, + sort: 2, + createTime: "2021-03-25 12:39:54", + updateTime: null, + }, + 5: { + id: 5, + name: "系统管理员2", + code: "ADMIN2", + status: 1, + sort: 2, + createTime: "2021-03-25 12:39:54", + updateTime: null, + }, + + 6: { + id: 6, + name: "系统管理员3", + code: "ADMIN3", + status: 1, + sort: 2, + createTime: "2021-03-25 12:39:54", + updateTime: null, + }, + 7: { + id: 7, + name: "系统管理员4", + code: "ADMIN4", + status: 1, + sort: 2, + createTime: "2021-03-25 12:39:54", + updateTime: null, + }, + 8: { + id: 8, + name: "系统管理员5", + code: "ADMIN5", + status: 1, + sort: 2, + createTime: "2021-03-25 12:39:54", + updateTime: null, + }, + 9: { + id: 9, + name: "系统管理员6", + code: "ADMIN6", + status: 1, + sort: 2, + createTime: "2021-03-25 12:39:54", + updateTime: "2023-12-04 11:43:15", + }, + 10: { + id: 10, + name: "系统管理员7", + code: "ADMIN7", + status: 1, + sort: 2, + createTime: "2021-03-25 12:39:54", + updateTime: null, + }, + 11: { + id: 11, + name: "系统管理员8", + code: "ADMIN8", + status: 1, + sort: 2, + createTime: "2021-03-25 12:39:54", + updateTime: null, + }, +}; diff --git a/mock/user.mock.ts b/mock/user.mock.ts new file mode 100644 index 0000000..bc9041e --- /dev/null +++ b/mock/user.mock.ts @@ -0,0 +1,189 @@ +import { defineMock } from "./base"; + +export default defineMock([ + { + url: "users/me", + method: ["GET"], + body: { + code: "00000", + data: { + userId: 2, + nickname: "系统管理员", + username: "admin", + avatar: + "https://oss.youlai.tech/youlai-boot/2023/05/16/811270ef31f548af9cffc026dfc3777b.gif", + roles: ["ROOT"], + perms: [ + "sys:menu:delete", + "sys:dept:edit", + "sys:dict_type:add", + "sys:dict:edit", + "sys:dict:delete", + "sys:dict_type:edit", + "sys:menu:add", + "sys:user:add", + "sys:role:edit", + "sys:dept:delete", + "sys:user:edit", + "sys:user:delete", + "sys:user:password:reset", + "sys:dept:add", + "sys:role:delete", + "sys:dict_type:delete", + "sys:menu:edit", + "sys:dict:add", + "sys:role:add", + "sys:user:query", + "sys:user:export", + ], + }, + msg: "一切ok", + }, + }, + + { + url: "users/page", + method: ["GET"], + body: { + code: "00000", + data: { + list: [ + { + id: 2, + username: "admin", + nickname: "系统管理员", + mobile: "17621210366", + gender: 1, + avatar: + "https://oss.youlai.tech/youlai-boot/2023/05/16/811270ef31f548af9cffc026dfc3777b.gif", + email: "", + status: 1, + deptId: 1, + roleIds: [2], + }, + { + id: 3, + username: "test", + nickname: "测试小用户", + mobile: "17621210366", + gender: 1, + avatar: + "https://oss.youlai.tech/youlai-boot/2023/05/16/811270ef31f548af9cffc026dfc3777b.gif", + email: "youlaitech@163.com", + status: 1, + deptId: 3, + roleIds: [3], + }, + ], + total: 2, + }, + msg: "一切ok", + }, + }, + + // 新增用户 + { + url: "users", + method: ["POST"], + body({ body }) { + return { + code: "00000", + data: null, + msg: "新增用户" + body.nickname + "成功", + }; + }, + }, + + // 获取用户表单数据 + { + url: "users/:userId/form", + method: ["GET"], + body: ({ params }) => { + return { + code: "00000", + data: userMap[params.userId], + msg: "一切ok", + }; + }, + }, + // 修改用户 + { + url: "users/:userId", + method: ["PUT"], + body({ body }) { + return { + code: "00000", + data: null, + msg: "修改用户" + body.nickname + "成功", + }; + }, + }, + + // 删除用户 + { + url: "users/:userId", + method: ["DELETE"], + body({ params }) { + return { + code: "00000", + data: null, + msg: "删除用户" + params.id + "成功", + }; + }, + }, + + // 重置密码 + { + url: "users/:userId/password", + method: ["PATCH"], + body({ query }) { + return { + code: "00000", + data: null, + msg: "重置密码成功,新密码为:" + query.password, + }; + }, + }, + + // 导出Excel + { + url: "users/_export", + method: ["GET"], + headers: { + "Content-Disposition": + "attachment; filename=%E7%94%A8%E6%88%B7%E5%88%97%E8%A1%A8.xlsx", + "Content-Type": + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + }, + }, +]); + +// 用户映射表数据 +const userMap: Record = { + 2: { + id: 2, + username: "admin", + nickname: "系统管理员", + mobile: "17621210366", + gender: 1, + avatar: + "https://oss.youlai.tech/youlai-boot/2023/05/16/811270ef31f548af9cffc026dfc3777b.gif", + email: "", + status: 1, + deptId: 1, + roleIds: [2], + }, + 3: { + id: 3, + username: "test", + nickname: "测试小用户", + mobile: "17621210366", + gender: 1, + avatar: + "https://oss.youlai.tech/youlai-boot/2023/05/16/811270ef31f548af9cffc026dfc3777b.gif", + email: "youlaitech@163.com", + status: 1, + deptId: 3, + roleIds: [3], + }, +}; diff --git a/package.json b/package.json new file mode 100644 index 0000000..c79d7eb --- /dev/null +++ b/package.json @@ -0,0 +1,133 @@ +{ + "name": "在生万有货场ERP后台", + "version": "1.0.0", + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "vue-tsc --noEmit & vite build", + "preview": "vite preview", + "build-only": "vite build", + "type-check": "vue-tsc --noEmit", + "lint:eslint": "eslint --fix --ext .ts,.js,.vue ./src ", + "lint:prettier": "prettier --write \"**/*.{js,cjs,ts,json,tsx,css,less,scss,vue,html,md}\"", + "lint:stylelint": "stylelint \"**/*.{css,scss,vue}\" --fix --allow-empty-input", + "lint:lint-staged": "lint-staged", + "preinstall": "npx only-allow pnpm", + "prepare": "husky", + "commit": "git-cz" + }, + "config": { + "commitizen": { + "path": "node_modules/cz-git" + } + }, + "lint-staged": { + "*.{js,ts}": [ + "eslint --fix", + "prettier --write" + ], + "*.{cjs,json}": [ + "prettier --write" + ], + "*.{vue,html}": [ + "eslint --fix", + "prettier --write", + "stylelint --fix --allow-empty-input" + ], + "*.{scss,css}": [ + "stylelint --fix --allow-empty-input", + "prettier --write" + ], + "*.md": [ + "prettier --write" + ] + }, + "dependencies": { + "@element-plus/icons-vue": "^2.3.1", + "@vueuse/core": "^10.11.0", + "@wangeditor/editor": "^5.1.23", + "@wangeditor/editor-for-vue": "5.1.10", + "animate.css": "^4.1.1", + "axios": "^1.7.2", + "color": "^4.2.3", + "echarts": "^5.5.1", + "element-plus": "^2.7.6", + "exceljs": "^4.4.0", + "json-bigint": "^1.0.0", + "lodash-es": "^4.17.21", + "net": "^1.0.2", + "nprogress": "^0.2.0", + "path-browserify": "^1.0.1", + "path-to-regexp": "^6.2.2", + "pinia": "^2.1.7", + "pinia-plugin-persistedstate": "^3.2.1", + "sockjs-client": "1.6.1", + "sortablejs": "^1.15.2", + "stompjs": "^2.3.3", + "underscore": "^1.13.7", + "vue": "^3.4.31", + "vue-i18n": "9.9.1", + "vue-router": "^4.4.0", + "vue3-license-plate": "^1.0.2", + "ws": "^8.18.0" + }, + "devDependencies": { + "@commitlint/cli": "^18.6.1", + "@commitlint/config-conventional": "^18.6.3", + "@iconify-json/ep": "^1.1.15", + "@types/color": "^3.0.6", + "@types/lodash": "^4.17.6", + "@types/node": "^20.14.10", + "@types/nprogress": "^0.2.3", + "@types/path-browserify": "^1.0.2", + "@types/sockjs-client": "^1.5.4", + "@types/sortablejs": "^1.15.8", + "@types/stompjs": "^2.3.9", + "@types/underscore": "^1.11.15", + "@typescript-eslint/eslint-plugin": "^7.15.0", + "@typescript-eslint/parser": "^7.15.0", + "@vitejs/plugin-vue": "^5.0.5", + "@vitejs/plugin-vue-jsx": "^3.1.0", + "autoprefixer": "^10.4.19", + "commitizen": "^4.3.0", + "cz-git": "^1.9.3", + "eslint": "^8.57.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-import": "^2.29.1", + "eslint-plugin-prettier": "^5.1.3", + "eslint-plugin-vue": "^9.27.0", + "fast-glob": "^3.3.2", + "husky": "^9.0.11", + "lint-staged": "^15.2.7", + "postcss": "^8.4.39", + "postcss-html": "^1.7.0", + "postcss-scss": "^4.0.9", + "prettier": "^3.3.2", + "sass": "^1.77.6", + "stylelint": "^16.6.1", + "stylelint-config-html": "^1.1.0", + "stylelint-config-recess-order": "^4.6.0", + "stylelint-config-recommended-scss": "^14.0.0", + "stylelint-config-recommended-vue": "^1.5.0", + "stylelint-config-standard": "^36.0.1", + "terser": "^5.31.1", + "typescript": "^5.5.3", + "unocss": "^0.58.9", + "unplugin-auto-import": "^0.17.6", + "unplugin-icons": "^0.18.5", + "unplugin-vue-components": "^0.26.0", + "vite": "^5.3.3", + "vite-plugin-mock-dev-server": "^1.5.1", + "vite-plugin-svg-icons": "^2.0.1", + "vite-plugin-vue-devtools": "^7.3.5", + "vue-tsc": "^2.0.26" + }, + "repository": "https://gitee.com/youlaiorg/vue3-element-admin.git", + "author": "有来开源组织", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "packageManager": "pnpm@9.1.3+sha512.7c2ea089e1a6af306409c4fc8c4f0897bdac32b772016196c469d9428f1fe2d5a21daf8ad6512762654ac645b5d9136bb210ec9a00afa8dbc4677843ba362ecd" +} diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..df36fcf Binary files /dev/null and b/public/favicon.ico differ diff --git a/src/App.vue b/src/App.vue new file mode 100644 index 0000000..b1d99d1 --- /dev/null +++ b/src/App.vue @@ -0,0 +1,37 @@ + + + diff --git a/src/api/auth.ts b/src/api/auth.ts new file mode 100644 index 0000000..9918a36 --- /dev/null +++ b/src/api/auth.ts @@ -0,0 +1,81 @@ +import request from "@/utils/request"; +import { MenuVO } from "./menu"; +import { UserForm } from "./user"; + +const AUTH_BASE_URL = "/api"; + +class AuthAPI { + /** 登录 接口*/ + static login(data: LoginData) { + return request({ + url: `/api/user/loginPhone`, + method: "get", + params: data, + }); + } + + /** 注销 接口*/ + static logout() { + return request({ + url: `/api/user/logOut`, + method: "POST", + }); + } + + /** 获取验证码 接口*/ + static getCaptcha(data: { phone: string }) { + return request({ + url: `/api/user/sendMsg`, + method: "get", + params: data, + }); + } + + // 查询手机号绑定货场 + static getCommonDbPhone(data: { phone: string }) { + return request({ + url: `/api/db/getCommonDbPhone`, + method: "get", + params: data, + }); + } +} + +export default AuthAPI; + +/** 登录请求参数 */ +export interface LoginData { + /** 用户名 */ + phone: string; + /** 密码 */ + code: string; +} + +/** 登录响应 */ +export interface LoginResult { + /** 访问token */ + token?: string; + menusList?: MenuVO[]; + roleList?: any; + userId?: string; + userInfo?: UserForm; + userName?: string; +} + +/** 验证码响应 */ +export interface CaptchaResult { + /** 验证码缓存key */ + captchaKey: string; + /** 验证码图片Base64字符串 */ + captchaBase64: string; +} + +// 查询手机号绑定货场相应 +export interface DbPhoneResult { + id: number; + mechanismName: string; + phone: string; + mechanismCode: string; + desc: string; + isDefault: string; +} diff --git a/src/api/dept.ts b/src/api/dept.ts new file mode 100644 index 0000000..b30a21e --- /dev/null +++ b/src/api/dept.ts @@ -0,0 +1,130 @@ +import request from "@/utils/request"; + +const DEPT_BASE_URL = "/api/v1/dept"; + +class DeptAPI { + /** + * 获取部门列表 + * + * @param queryParams 查询参数(可选) + * @returns 部门树形表格数据 + */ + static getList(queryParams?: DeptQuery) { + return request({ + url: `${DEPT_BASE_URL}`, + method: "get", + params: queryParams, + }); + } + + /** 获取部门下拉列表 */ + static getOptions() { + return request({ + url: `${DEPT_BASE_URL}/options`, + method: "get", + }); + } + + /** + * 获取部门表单数据 + * + * @param id 部门ID + * @returns 部门表单数据 + */ + static getFormData(id: number) { + return request({ + url: `${DEPT_BASE_URL}/${id}/form`, + method: "get", + }); + } + + /** + * 新增部门 + * + * @param data 部门表单数据 + * @returns 请求结果 + */ + static add(data: DeptForm) { + return request({ + url: `${DEPT_BASE_URL}`, + method: "post", + data: data, + }); + } + + /** + * 修改部门 + * + * @param id 部门ID + * @param data 部门表单数据 + * @returns 请求结果 + */ + static update(id: number, data: DeptForm) { + return request({ + url: `${DEPT_BASE_URL}/${id}`, + method: "put", + data: data, + }); + } + + /** + * 删除部门 + * + * @param ids 部门ID,多个以英文逗号(,)分隔 + * @returns 请求结果 + */ + static deleteByIds(ids: string) { + return request({ + url: `${DEPT_BASE_URL}/${ids}`, + method: "delete", + }); + } +} + +export default DeptAPI; + +/** 部门查询参数 */ +export interface DeptQuery { + /** 搜索关键字 */ + keywords?: string; + /** 状态 */ + status?: number; +} + +/** 部门类型 */ +export interface DeptVO { + /** 子部门 */ + children?: DeptVO[]; + /** 创建时间 */ + createTime?: Date; + /** 部门ID */ + id?: number; + /** 部门名称 */ + name?: string; + /** 部门编号 */ + code?: string; + /** 父部门ID */ + parentId?: number; + /** 排序 */ + sort?: number; + /** 状态(1:启用;0:禁用) */ + status?: number; + /** 修改时间 */ + updateTime?: Date; +} + +/** 部门表单类型 */ +export interface DeptForm { + /** 部门ID(新增不填) */ + id?: number; + /** 部门名称 */ + name?: string; + /** 部门编号 */ + code?: string; + /** 父部门ID */ + parentId: number; + /** 排序 */ + sort?: number; + /** 状态(1:启用;0:禁用) */ + status?: number; +} diff --git a/src/api/dict.ts b/src/api/dict.ts new file mode 100644 index 0000000..b951200 --- /dev/null +++ b/src/api/dict.ts @@ -0,0 +1,183 @@ +import request from "@/utils/request"; + +const DICT_BASE_URL = "/api/v1/dict"; + +class DictAPI { + /** + * 获取字典分页列表 + * + * @param queryParams 查询参数 + * @returns 字典分页结果 + */ + static getPage(queryParams: DictPageQuery) { + return request>({ + url: `${DICT_BASE_URL}/page`, + method: "get", + params: queryParams, + }); + } + + /** + * 获取字典表单数据 + * + * @param id 字典ID + * @returns 字典表单数据 + */ + static getFormData(id: number) { + return request>({ + url: `${DICT_BASE_URL}/${id}/form`, + method: "get", + }); + } + + /** + * 新增字典 + * + * @param data 字典表单数据 + * @returns 请求结果 + */ + static add(data: DictForm) { + return request({ + url: `${DICT_BASE_URL}`, + method: "post", + data: data, + }); + } + + /** + * 修改字典 + * + * @param id 字典ID + * @param data 字典表单数据 + * @returns 请求结果 + */ + static update(id: number, data: DictForm) { + return request({ + url: `${DICT_BASE_URL}/${id}`, + method: "put", + data: data, + }); + } + + /** + * 删除字典 + * + * @param ids 字典ID,多个以英文逗号(,)分隔 + * @returns 请求结果 + */ + static deleteByIds(ids: string) { + return request({ + url: `${DICT_BASE_URL}/${ids}`, + method: "delete", + }); + } + + /** + * 获取字典的数据项 + * + * @param typeCode 字典编码 + * @returns 字典数据项 + */ + static getOptions(code: string) { + return request({ + url: `${DICT_BASE_URL}/${code}/options`, + method: "get", + }); + } +} + +export default DictAPI; + +/** + * 字典查询参数 + */ +export interface DictPageQuery extends PageQuery { + /** + * 关键字(字典名称/编码) + */ + keywords?: string; +} + +/** + * 字典分页对象 + */ +export interface DictPageVO { + /** + * 字典ID + */ + id: number; + /** + * 字典名称 + */ + name: string; + /** + * 字典编码 + */ + code: string; + /** + * 字典状态(1-启用,0-禁用) + */ + status: number; + /** + * 字典项列表 + */ + dictItems: DictItem[]; +} + +/** + * 字典项 + */ +export interface DictItem { + /** + * 字典项ID + */ + id?: number; + /** + * 字典项名称 + */ + name?: string; + /** + * 字典项值 + */ + value?: string; + /** + * 排序 + */ + sort?: number; + /** + * 状态(1-启用,0-禁用) + */ + status?: number; +} + +// TypeScript 类型声明 + +/** + * 字典 + */ +export interface DictForm { + /** + * 字典ID + */ + id?: number; + /** + * 字典名称 + */ + name?: string; + /** + * 字典编码 + */ + code?: string; + /** + * 字典状态(1-启用,0-禁用) + */ + status?: number; + /** + * 备注 + */ + remark?: string; + /** + * 字典数据项列表 + */ + dictItems?: DictItem[]; +} diff --git a/src/api/file.ts b/src/api/file.ts new file mode 100644 index 0000000..3203195 --- /dev/null +++ b/src/api/file.ts @@ -0,0 +1,46 @@ +import request from "@/utils/request"; + +class FileAPI { + /** + * 上传文件 + * + * @param file + */ + static upload(file: File) { + const formData = new FormData(); + formData.append("file", file); + return request({ + url: "/api/v1/files", + method: "post", + data: formData, + headers: { + "Content-Type": "multipart/form-data", + }, + }); + } + + /** + * 删除文件 + * + * @param filePath 文件完整路径 + */ + static deleteByPath(filePath?: string) { + return request({ + url: "/api/v1/files", + method: "delete", + params: { filePath: filePath }, + }); + } +} + +export default FileAPI; + +/** + * 文件API类型声明 + */ +export interface FileInfo { + /** 文件名 */ + name: string; + /** 文件路径 */ + url: string; +} diff --git a/src/api/menu.ts b/src/api/menu.ts new file mode 100644 index 0000000..11e9dd3 --- /dev/null +++ b/src/api/menu.ts @@ -0,0 +1,216 @@ +import request from "@/utils/request"; + +class MenuAPI { + /** + * 获取当前用户的路由列表 + *

+ * 无需传入角色,后端解析token获取角色自行判断是否拥有路由的权限 + * + * @returns 路由列表 + */ + static getRoutes() { + return request({ + url: `/api/new/menus/routes`, + method: "get", + }); + } + + /** + * 获取菜单树形列表 + * + * @param queryParams 查询参数 + * @returns 菜单树形列表 + */ + static getList(queryParams: MenuQuery) { + return request({ + url: `/api/new/menus/listMenus`, + method: "get", + params: queryParams, + }); + } + + /** + * 获取菜单下拉数据源 + * + * @returns 菜单下拉数据源 + */ + static getOptions() { + return request({ + url: `/api/new/menus/options`, + method: "get", + }); + } + + /** + * 获取菜单表单数据 + * + * @param id 菜单ID + */ + static getFormData(data: { id: number }) { + return request({ + url: `/api/new/menus/getMenuForm`, + method: "get", + params: data, + }); + } + + /** + * 添加菜单 + * + * @param data 菜单表单数据 + * @returns 请求结果 + */ + static add(data: MenuForm) { + return request({ + url: `/api/new/menus/addMenu`, + method: "post", + data: data, + }); + } + + /** + * 修改菜单 + * + * @param id 菜单ID + * @param data 菜单表单数据 + * @returns 请求结果 + */ + static update(id: string, data: MenuForm) { + return request({ + url: `/api/new/menus/updateMenu`, + method: "post", + data: data, + }); + } + + /** + * 删除菜单 + * + * @param id 菜单ID + * @returns 请求结果 + */ + static deleteById(data: { id: number }) { + return request({ + url: `/api/new/menus/deleteMenu`, + method: "get", + params: data, + }); + } + + static updateMenuVisible(data: { menuId: number; visible: number }) { + return request({ + url: `/api/new/menus/updateMenuVisible`, + method: "get", + params: data, + }); + } +} + +export default MenuAPI; + +import { MenuTypeEnum } from "@/enums/MenuTypeEnum"; + +/** 菜单查询参数 */ +export interface MenuQuery { + /** 搜索关键字 */ + name?: string; +} + +/** 菜单视图对象 */ +export interface MenuVO { + /** 子菜单 */ + children?: MenuVO[]; + /** 组件路径 */ + component?: string; + /** ICON */ + icon?: string; + /** 菜单ID */ + id?: number; + /** 菜单名称 */ + name?: string; + /** 父菜单ID */ + parentId?: number; + /** 按钮权限标识 */ + perm?: string; + /** 跳转路径 */ + redirect?: string; + /** 路由名称 */ + routeName?: string; + /** 路由相对路径 */ + routePath?: string; + /** 菜单排序(数字越小排名越靠前) */ + sort?: number; + /** 菜单 */ + type?: MenuTypeEnum; + /** 菜单是否可见(1:显示;0:隐藏) */ + visible?: number; +} + +/** 菜单表单对象 */ +export interface MenuForm { + /** 菜单ID */ + id?: string; + /** 父菜单ID */ + parentId?: number; + /** 菜单名称 */ + name?: string; + /** 菜单是否可见(1-是 0-否) */ + visible: number; + /** ICON */ + icon?: string; + /** 排序 */ + sort?: number; + /** 路由名称 */ + routeName?: string; + /** 路由路径 */ + routePath?: string; + /** 组件路径 */ + component?: string; + /** 跳转路由路径 */ + redirect?: string; + /** 菜单 */ + type?: MenuTypeEnum; + /** 权限标识 */ + perm?: string; + /** 【菜单】是否开启页面缓存 */ + keepAlive?: number; + /** 【目录】只有一个子路由是否始终显示 */ + alwaysShow?: number; + /** 参数 */ + params?: KeyValue[]; +} + +interface KeyValue { + key: string; + value: string; +} + +/** RouteVO,路由对象 */ +export interface RouteVO { + /** 子路由列表 */ + children: RouteVO[]; + /** 组件路径 */ + component?: string; + /** 路由属性 */ + meta?: Meta; + /** 路由名称 */ + name?: string; + /** 路由路径 */ + path?: string; + /** 跳转链接 */ + redirect?: string; +} + +/** Meta,路由属性 */ +export interface Meta { + /** 【目录】只有一个子路由是否始终显示 */ + alwaysShow?: boolean; + /** 是否隐藏(true-是 false-否) */ + hidden?: boolean; + /** ICON */ + icon?: string; + /** 【菜单】是否开启页面缓存 */ + keepAlive?: boolean; + /** 路由title */ + title?: string; +} diff --git a/src/api/role.ts b/src/api/role.ts new file mode 100644 index 0000000..e4e7fd5 --- /dev/null +++ b/src/api/role.ts @@ -0,0 +1,122 @@ +import request from "@/utils/request"; + +class RoleAPI { + /** 获取角色分页数据 */ + static getPage(queryParams?: RolePageQuery) { + return request>({ + url: `/api/role/getPageRole`, + method: "get", + params: queryParams, + }); + } + + static getAllRole() { + return request({ + url: `/api/role/getRoleList`, + method: "get", + }); + } + + /** 添加角色 */ + static addRole(data: RoleForm) { + return request({ + url: `api/role/addRole`, + method: "post", + data: data, + }); + } + + /** + * 更新角色 + * + * @param id 角色ID + * @param data 角色表单数据 + */ + static updateRole(data: RoleForm) { + return request({ + url: `/api/role/updateRole`, + method: "post", + data: data, + }); + } + + static getMenuList() { + return request({ + url: `/api/menus/getList`, + method: "get", + }); + } + + static queryRoleById(data: { roleId: number }) { + return request({ + url: `/api/role/getMenusRole`, + method: "get", + params: data, + }); + } +} + +export default RoleAPI; + +/** 角色分页查询参数 */ +export interface RolePageQuery extends PageQuery { + /** 搜索关键字 */ + roleName?: string; +} + +/** 角色分页对象 */ +export interface RoleDTO { + createTime?: string; + createUserId?: number; + createUserName?: string; + id: number; + isDeleted?: boolean; + roleCode?: string; + roleName?: string; + state?: number; + updateTime?: string; + updateUserId?: number; + updateUserName?: string; +} + +/** 角色表单对象 */ +export interface RoleForm { + id?: number; + roleName?: string; + list?: Menu[]; + isDeleted?: boolean; +} + +export interface Menu { + menusId?: number; + state?: number; +} + +export interface MenuDTO { + createTime?: string; + createUserId?: number; + createUserName?: string; + id?: number; + name?: string; + note?: string; + parentId?: string; + permission?: string; + sort?: number; + state?: string; + type?: number; + updateTime?: string; + updateUserId?: number; + updateUserName?: string; + url?: string; +} +export interface MenuRoleDTO { + createTime?: string; + createUserId?: number; + id?: number; + menusId?: number; + menusName?: string; + roleId?: number; + state?: number; + updateTime?: string; + updateUserId?: number; +} diff --git a/src/api/user.ts b/src/api/user.ts new file mode 100644 index 0000000..330313d --- /dev/null +++ b/src/api/user.ts @@ -0,0 +1,118 @@ +import request from "@/utils/request"; +import { RoleDTO } from "./role"; + +class UserAPI { + /** + * 获取用户分页列表 + * + * @param queryParams 查询参数 + */ + static getPage(queryParams: PageQuery) { + return request>({ + url: `/api/user/getUserPage`, + method: "get", + params: queryParams, + }); + } + + /** + * 添加用户 + * + * @param data 用户表单数据 + */ + static add(data: UserForm) { + return request({ + url: `/api/user/addUser`, + method: "post", + data: data, + }); + } + + /** + * 修改用户 + * + * @param id 用户ID + * @param data 用户表单数据 + */ + static updateUserById(data: UserForm) { + return request({ + url: `/api/user/updateUserById`, + method: "post", + data: data, + }); + } + + /** + * 批量删除用户,多个以英文逗号(,)分割 + * + * @param ids 用户ID字符串,多个以英文逗号(,)分割 + */ + static updateUserByIdOffline(data: { id: number }) { + return request({ + url: `/api/user/updateUserByIdOffline`, + method: "post", + data: data, + }); + } + + /** + * 获取用户信息 + */ + + static getUserInfo() { + return request({ + url: `/api/user/getUserInfo`, + method: "GET", + }); + } +} + +export default UserAPI; + +/** + * 用户分页查询对象 + */ +export interface UserPageQuery extends PageQuery { + /** 搜索关键字 */ + name?: string; + phone?: string; + roleId?: number; +} + +/** 用户分页对象 */ +export interface UserPageVO { + areaId?: string; + cardCode?: string; + citiyId?: string; + createTime?: string; + createUserId?: number; + createUserName?: string; + factoryAddress?: string; + factoryName?: string; + gender?: number; + id: number; + isDeleted?: string; + name?: string; + password?: string; + phone?: string; + provinceId?: string; + roleVos?: RoleDTO[]; + state?: number; + updateTime?: string; + updateUserId?: number; + updateUserName?: string; + userName?: string; + userType?: number; +} + +/** 用户表单类型 */ +export interface UserForm { + userType?: number; + gender?: number; + name?: string; + roleName?: string; + roleIds?: number[]; + phone?: string; + id?: number; + roleId?: number; +} diff --git a/src/assets/font_4666703_de1boqvna1d/demo.css b/src/assets/font_4666703_de1boqvna1d/demo.css new file mode 100644 index 0000000..a67054a --- /dev/null +++ b/src/assets/font_4666703_de1boqvna1d/demo.css @@ -0,0 +1,539 @@ +/* Logo 字体 */ +@font-face { + font-family: "iconfont logo"; + src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834'); + src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') format('embedded-opentype'), + url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'), + url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'), + url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') format('svg'); +} + +.logo { + font-family: "iconfont logo"; + font-size: 160px; + font-style: normal; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +/* tabs */ +.nav-tabs { + position: relative; +} + +.nav-tabs .nav-more { + position: absolute; + right: 0; + bottom: 0; + height: 42px; + line-height: 42px; + color: #666; +} + +#tabs { + border-bottom: 1px solid #eee; +} + +#tabs li { + cursor: pointer; + width: 100px; + height: 40px; + line-height: 40px; + text-align: center; + font-size: 16px; + border-bottom: 2px solid transparent; + position: relative; + z-index: 1; + margin-bottom: -1px; + color: #666; +} + + +#tabs .active { + border-bottom-color: #f00; + color: #222; +} + +.tab-container .content { + display: none; +} + +/* 页面布局 */ +.main { + padding: 30px 100px; + width: 960px; + margin: 0 auto; +} + +.main .logo { + color: #333; + text-align: left; + margin-bottom: 30px; + line-height: 1; + height: 110px; + margin-top: -50px; + overflow: hidden; + *zoom: 1; +} + +.main .logo a { + font-size: 160px; + color: #333; +} + +.helps { + margin-top: 40px; +} + +.helps pre { + padding: 20px; + margin: 10px 0; + border: solid 1px #e7e1cd; + background-color: #fffdef; + overflow: auto; +} + +.icon_lists { + width: 100% !important; + overflow: hidden; + *zoom: 1; +} + +.icon_lists li { + width: 100px; + margin-bottom: 10px; + margin-right: 20px; + text-align: center; + list-style: none !important; + cursor: default; +} + +.icon_lists li .code-name { + line-height: 1.2; +} + +.icon_lists .icon { + display: block; + height: 100px; + line-height: 100px; + font-size: 42px; + margin: 10px auto; + color: #333; + -webkit-transition: font-size 0.25s linear, width 0.25s linear; + -moz-transition: font-size 0.25s linear, width 0.25s linear; + transition: font-size 0.25s linear, width 0.25s linear; +} + +.icon_lists .icon:hover { + font-size: 100px; +} + +.icon_lists .svg-icon { + /* 通过设置 font-size 来改变图标大小 */ + width: 1em; + /* 图标和文字相邻时,垂直对齐 */ + vertical-align: -0.15em; + /* 通过设置 color 来改变 SVG 的颜色/fill */ + fill: currentColor; + /* path 和 stroke 溢出 viewBox 部分在 IE 下会显示 + normalize.css 中也包含这行 */ + overflow: hidden; +} + +.icon_lists li .name, +.icon_lists li .code-name { + color: #666; +} + +/* markdown 样式 */ +.markdown { + color: #666; + font-size: 14px; + line-height: 1.8; +} + +.highlight { + line-height: 1.5; +} + +.markdown img { + vertical-align: middle; + max-width: 100%; +} + +.markdown h1 { + color: #404040; + font-weight: 500; + line-height: 40px; + margin-bottom: 24px; +} + +.markdown h2, +.markdown h3, +.markdown h4, +.markdown h5, +.markdown h6 { + color: #404040; + margin: 1.6em 0 0.6em 0; + font-weight: 500; + clear: both; +} + +.markdown h1 { + font-size: 28px; +} + +.markdown h2 { + font-size: 22px; +} + +.markdown h3 { + font-size: 16px; +} + +.markdown h4 { + font-size: 14px; +} + +.markdown h5 { + font-size: 12px; +} + +.markdown h6 { + font-size: 12px; +} + +.markdown hr { + height: 1px; + border: 0; + background: #e9e9e9; + margin: 16px 0; + clear: both; +} + +.markdown p { + margin: 1em 0; +} + +.markdown>p, +.markdown>blockquote, +.markdown>.highlight, +.markdown>ol, +.markdown>ul { + width: 80%; +} + +.markdown ul>li { + list-style: circle; +} + +.markdown>ul li, +.markdown blockquote ul>li { + margin-left: 20px; + padding-left: 4px; +} + +.markdown>ul li p, +.markdown>ol li p { + margin: 0.6em 0; +} + +.markdown ol>li { + list-style: decimal; +} + +.markdown>ol li, +.markdown blockquote ol>li { + margin-left: 20px; + padding-left: 4px; +} + +.markdown code { + margin: 0 3px; + padding: 0 5px; + background: #eee; + border-radius: 3px; +} + +.markdown strong, +.markdown b { + font-weight: 600; +} + +.markdown>table { + border-collapse: collapse; + border-spacing: 0px; + empty-cells: show; + border: 1px solid #e9e9e9; + width: 95%; + margin-bottom: 24px; +} + +.markdown>table th { + white-space: nowrap; + color: #333; + font-weight: 600; +} + +.markdown>table th, +.markdown>table td { + border: 1px solid #e9e9e9; + padding: 8px 16px; + text-align: left; +} + +.markdown>table th { + background: #F7F7F7; +} + +.markdown blockquote { + font-size: 90%; + color: #999; + border-left: 4px solid #e9e9e9; + padding-left: 0.8em; + margin: 1em 0; +} + +.markdown blockquote p { + margin: 0; +} + +.markdown .anchor { + opacity: 0; + transition: opacity 0.3s ease; + margin-left: 8px; +} + +.markdown .waiting { + color: #ccc; +} + +.markdown h1:hover .anchor, +.markdown h2:hover .anchor, +.markdown h3:hover .anchor, +.markdown h4:hover .anchor, +.markdown h5:hover .anchor, +.markdown h6:hover .anchor { + opacity: 1; + display: inline-block; +} + +.markdown>br, +.markdown>p>br { + clear: both; +} + + +.hljs { + display: block; + background: white; + padding: 0.5em; + color: #333333; + overflow-x: auto; +} + +.hljs-comment, +.hljs-meta { + color: #969896; +} + +.hljs-string, +.hljs-variable, +.hljs-template-variable, +.hljs-strong, +.hljs-emphasis, +.hljs-quote { + color: #df5000; +} + +.hljs-keyword, +.hljs-selector-tag, +.hljs-type { + color: #a71d5d; +} + +.hljs-literal, +.hljs-symbol, +.hljs-bullet, +.hljs-attribute { + color: #0086b3; +} + +.hljs-section, +.hljs-name { + color: #63a35c; +} + +.hljs-tag { + color: #333333; +} + +.hljs-title, +.hljs-attr, +.hljs-selector-id, +.hljs-selector-class, +.hljs-selector-attr, +.hljs-selector-pseudo { + color: #795da3; +} + +.hljs-addition { + color: #55a532; + background-color: #eaffea; +} + +.hljs-deletion { + color: #bd2c00; + background-color: #ffecec; +} + +.hljs-link { + text-decoration: underline; +} + +/* 代码高亮 */ +/* PrismJS 1.15.0 +https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */ +/** + * prism.js default theme for JavaScript, CSS and HTML + * Based on dabblet (http://dabblet.com) + * @author Lea Verou + */ +code[class*="language-"], +pre[class*="language-"] { + color: black; + background: none; + text-shadow: 0 1px white; + font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + line-height: 1.5; + + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; +} + +pre[class*="language-"]::-moz-selection, +pre[class*="language-"] ::-moz-selection, +code[class*="language-"]::-moz-selection, +code[class*="language-"] ::-moz-selection { + text-shadow: none; + background: #b3d4fc; +} + +pre[class*="language-"]::selection, +pre[class*="language-"] ::selection, +code[class*="language-"]::selection, +code[class*="language-"] ::selection { + text-shadow: none; + background: #b3d4fc; +} + +@media print { + + code[class*="language-"], + pre[class*="language-"] { + text-shadow: none; + } +} + +/* Code blocks */ +pre[class*="language-"] { + padding: 1em; + margin: .5em 0; + overflow: auto; +} + +:not(pre)>code[class*="language-"], +pre[class*="language-"] { + background: #f5f2f0; +} + +/* Inline code */ +:not(pre)>code[class*="language-"] { + padding: .1em; + border-radius: .3em; + white-space: normal; +} + +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: slategray; +} + +.token.punctuation { + color: #999; +} + +.namespace { + opacity: .7; +} + +.token.property, +.token.tag, +.token.boolean, +.token.number, +.token.constant, +.token.symbol, +.token.deleted { + color: #905; +} + +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.inserted { + color: #690; +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string { + color: #9a6e3a; + background: hsla(0, 0%, 100%, .5); +} + +.token.atrule, +.token.attr-value, +.token.keyword { + color: #07a; +} + +.token.function, +.token.class-name { + color: #DD4A68; +} + +.token.regex, +.token.important, +.token.variable { + color: #e90; +} + +.token.important, +.token.bold { + font-weight: bold; +} + +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} diff --git a/src/assets/font_4666703_de1boqvna1d/demo_index.html b/src/assets/font_4666703_de1boqvna1d/demo_index.html new file mode 100644 index 0000000..0a81304 --- /dev/null +++ b/src/assets/font_4666703_de1boqvna1d/demo_index.html @@ -0,0 +1,373 @@ + + + + + iconfont Demo + + + + + + + + + + + + + +

+

+ + +

+ +
+
+
    + +
  • + +
    设置
    +
    &#xe643;
    +
  • + +
  • + +
    出库
    +
    &#xe602;
    +
  • + +
  • + +
    入库
    +
    &#xe606;
    +
  • + +
  • + +
    参数
    +
    &#xe655;
    +
  • + +
  • + +
    数据看板
    +
    &#xeb66;
    +
  • + +
  • + +
    收货
    +
    &#xe607;
    +
  • + +
  • + +
    我的账本
    +
    &#xe629;
    +
  • + +
  • + +
    出货
    +
    &#xe653;
    +
  • + +
+
+

Unicode 引用

+
+ +

Unicode 是字体在网页端最原始的应用方式,特点是:

+
    +
  • 支持按字体的方式去动态调整图标大小,颜色等等。
  • +
  • 默认情况下不支持多色,直接添加多色图标会自动去色。
  • +
+
+

注意:新版 iconfont 支持两种方式引用多色图标:SVG symbol 引用方式和彩色字体图标模式。(使用彩色字体图标需要在「编辑项目」中开启「彩色」选项后并重新生成。)

+
+

Unicode 使用步骤如下:

+

第一步:拷贝项目下面生成的 @font-face

+
@font-face {
+  font-family: 'iconfont';
+  src: url('iconfont.woff2?t=1724812140996') format('woff2'),
+       url('iconfont.woff?t=1724812140996') format('woff'),
+       url('iconfont.ttf?t=1724812140996') format('truetype'),
+       url('iconfont.svg?t=1724812140996#iconfont') format('svg');
+}
+
+

第二步:定义使用 iconfont 的样式

+
.iconfont {
+  font-family: "iconfont" !important;
+  font-size: 16px;
+  font-style: normal;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+

第三步:挑选相应图标并获取字体编码,应用于页面

+
+<span class="iconfont">&#x33;</span>
+
+
+

"iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。

+
+
+
+
+
    + +
  • + +
    + 设置 +
    +
    .icon-shezhi +
    +
  • + +
  • + +
    + 出库 +
    +
    .icon-chuku +
    +
  • + +
  • + +
    + 入库 +
    +
    .icon-ruku +
    +
  • + +
  • + +
    + 参数 +
    +
    .icon-canshu +
    +
  • + +
  • + +
    + 数据看板 +
    +
    .icon-shujukanban +
    +
  • + +
  • + +
    + 收货 +
    +
    .icon-shouhuo +
    +
  • + +
  • + +
    + 我的账本 +
    +
    .icon-wodezhangben +
    +
  • + +
  • + +
    + 出货 +
    +
    .icon-chuhuo +
    +
  • + +
+
+

font-class 引用

+
+ +

font-class 是 Unicode 使用方式的一种变种,主要是解决 Unicode 书写不直观,语意不明确的问题。

+

与 Unicode 使用方式相比,具有如下特点:

+
    +
  • 相比于 Unicode 语意明确,书写更直观。可以很容易分辨这个 icon 是什么。
  • +
  • 因为使用 class 来定义图标,所以当要替换图标时,只需要修改 class 里面的 Unicode 引用。
  • +
+

使用步骤如下:

+

第一步:引入项目下面生成的 fontclass 代码:

+
<link rel="stylesheet" href="./iconfont.css">
+
+

第二步:挑选相应图标并获取类名,应用于页面:

+
<span class="iconfont icon-xxx"></span>
+
+
+

" + iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。

+
+
+
+
+
    + +
  • + +
    设置
    +
    #icon-shezhi
    +
  • + +
  • + +
    出库
    +
    #icon-chuku
    +
  • + +
  • + +
    入库
    +
    #icon-ruku
    +
  • + +
  • + +
    参数
    +
    #icon-canshu
    +
  • + +
  • + +
    数据看板
    +
    #icon-shujukanban
    +
  • + +
  • + +
    收货
    +
    #icon-shouhuo
    +
  • + +
  • + +
    我的账本
    +
    #icon-wodezhangben
    +
  • + +
  • + +
    出货
    +
    #icon-chuhuo
    +
  • + +
+
+

Symbol 引用

+
+ +

这是一种全新的使用方式,应该说这才是未来的主流,也是平台目前推荐的用法。相关介绍可以参考这篇文章 + 这种用法其实是做了一个 SVG 的集合,与另外两种相比具有如下特点:

+
    +
  • 支持多色图标了,不再受单色限制。
  • +
  • 通过一些技巧,支持像字体那样,通过 font-size, color 来调整样式。
  • +
  • 兼容性较差,支持 IE9+,及现代浏览器。
  • +
  • 浏览器渲染 SVG 的性能一般,还不如 png。
  • +
+

使用步骤如下:

+

第一步:引入项目下面生成的 symbol 代码:

+
<script src="./iconfont.js"></script>
+
+

第二步:加入通用 CSS 代码(引入一次就行):

+
<style>
+.icon {
+  width: 1em;
+  height: 1em;
+  vertical-align: -0.15em;
+  fill: currentColor;
+  overflow: hidden;
+}
+</style>
+
+

第三步:挑选相应图标并获取类名,应用于页面:

+
<svg class="icon" aria-hidden="true">
+  <use xlink:href="#icon-xxx"></use>
+</svg>
+
+
+
+ +
+
+ + + diff --git a/src/assets/font_4666703_de1boqvna1d/iconfont.css b/src/assets/font_4666703_de1boqvna1d/iconfont.css new file mode 100644 index 0000000..2344441 --- /dev/null +++ b/src/assets/font_4666703_de1boqvna1d/iconfont.css @@ -0,0 +1,48 @@ +@font-face { + font-family: "iconfont"; /* Project id 4666703 */ + src: url('iconfont.woff2?t=1724812140996') format('woff2'), + url('iconfont.woff?t=1724812140996') format('woff'), + url('iconfont.ttf?t=1724812140996') format('truetype'), + url('iconfont.svg?t=1724812140996#iconfont') format('svg'); +} + +.iconfont { + font-family: "iconfont" !important; + font-size: 16px; + font-style: normal; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.icon-shezhi:before { + content: "\e643"; +} + +.icon-chuku:before { + content: "\e602"; +} + +.icon-ruku:before { + content: "\e606"; +} + +.icon-canshu:before { + content: "\e655"; +} + +.icon-shujukanban:before { + content: "\eb66"; +} + +.icon-shouhuo:before { + content: "\e607"; +} + +.icon-wodezhangben:before { + content: "\e629"; +} + +.icon-chuhuo:before { + content: "\e653"; +} + diff --git a/src/assets/font_4666703_de1boqvna1d/iconfont.js b/src/assets/font_4666703_de1boqvna1d/iconfont.js new file mode 100644 index 0000000..137ad92 --- /dev/null +++ b/src/assets/font_4666703_de1boqvna1d/iconfont.js @@ -0,0 +1 @@ +window._iconfont_svg_string_4666703='',(e=>{var c=(t=(t=document.getElementsByTagName("script"))[t.length-1]).getAttribute("data-injectcss"),t=t.getAttribute("data-disable-injectsvg");if(!t){var a,l,h,o,n,v=function(c,t){t.parentNode.insertBefore(c,t)};if(c&&!e.__iconfont__svg__cssinject__){e.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(c){console&&console.log(c)}}a=function(){var c,t=document.createElement("div");t.innerHTML=e._iconfont_svg_string_4666703,(t=t.getElementsByTagName("svg")[0])&&(t.setAttribute("aria-hidden","true"),t.style.position="absolute",t.style.width=0,t.style.height=0,t.style.overflow="hidden",t=t,(c=document.body).firstChild?v(t,c.firstChild):c.appendChild(t))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(a,0):(l=function(){document.removeEventListener("DOMContentLoaded",l,!1),a()},document.addEventListener("DOMContentLoaded",l,!1)):document.attachEvent&&(h=a,o=e.document,n=!1,s(),o.onreadystatechange=function(){"complete"==o.readyState&&(o.onreadystatechange=null,i())})}function i(){n||(n=!0,h())}function s(){try{o.documentElement.doScroll("left")}catch(c){return void setTimeout(s,50)}i()}})(window); \ No newline at end of file diff --git a/src/assets/font_4666703_de1boqvna1d/iconfont.json b/src/assets/font_4666703_de1boqvna1d/iconfont.json new file mode 100644 index 0000000..7a896b2 --- /dev/null +++ b/src/assets/font_4666703_de1boqvna1d/iconfont.json @@ -0,0 +1,65 @@ +{ + "id": "4666703", + "name": "ava", + "font_family": "iconfont", + "css_prefix_text": "icon-", + "description": "爱梵达图标集合", + "glyphs": [ + { + "icon_id": "1167181", + "name": "设置", + "font_class": "shezhi", + "unicode": "e643", + "unicode_decimal": 58947 + }, + { + "icon_id": "3239723", + "name": "出库", + "font_class": "chuku", + "unicode": "e602", + "unicode_decimal": 58882 + }, + { + "icon_id": "3239733", + "name": "入库", + "font_class": "ruku", + "unicode": "e606", + "unicode_decimal": 58886 + }, + { + "icon_id": "3851337", + "name": "参数", + "font_class": "canshu", + "unicode": "e655", + "unicode_decimal": 58965 + }, + { + "icon_id": "3868281", + "name": "数据看板", + "font_class": "shujukanban", + "unicode": "eb66", + "unicode_decimal": 60262 + }, + { + "icon_id": "9922131", + "name": "收货", + "font_class": "shouhuo", + "unicode": "e607", + "unicode_decimal": 58887 + }, + { + "icon_id": "10037047", + "name": "我的账本", + "font_class": "wodezhangben", + "unicode": "e629", + "unicode_decimal": 58921 + }, + { + "icon_id": "11586060", + "name": "出货", + "font_class": "chuhuo", + "unicode": "e653", + "unicode_decimal": 58963 + } + ] +} diff --git a/src/assets/font_4666703_de1boqvna1d/iconfont.svg b/src/assets/font_4666703_de1boqvna1d/iconfont.svg new file mode 100644 index 0000000..2bed2e8 --- /dev/null +++ b/src/assets/font_4666703_de1boqvna1d/iconfont.svg @@ -0,0 +1,35 @@ + + + + Created by iconfont + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/font_4666703_de1boqvna1d/iconfont.ttf b/src/assets/font_4666703_de1boqvna1d/iconfont.ttf new file mode 100644 index 0000000..34fd0e2 Binary files /dev/null and b/src/assets/font_4666703_de1boqvna1d/iconfont.ttf differ diff --git a/src/assets/font_4666703_de1boqvna1d/iconfont.woff b/src/assets/font_4666703_de1boqvna1d/iconfont.woff new file mode 100644 index 0000000..d4f58e3 Binary files /dev/null and b/src/assets/font_4666703_de1boqvna1d/iconfont.woff differ diff --git a/src/assets/font_4666703_de1boqvna1d/iconfont.woff2 b/src/assets/font_4666703_de1boqvna1d/iconfont.woff2 new file mode 100644 index 0000000..a616896 Binary files /dev/null and b/src/assets/font_4666703_de1boqvna1d/iconfont.woff2 differ diff --git a/src/assets/icons/api.svg b/src/assets/icons/api.svg new file mode 100644 index 0000000..0181bdd --- /dev/null +++ b/src/assets/icons/api.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/backtop.svg b/src/assets/icons/backtop.svg new file mode 100644 index 0000000..f8e6aa0 --- /dev/null +++ b/src/assets/icons/backtop.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/captcha.svg b/src/assets/icons/captcha.svg new file mode 100644 index 0000000..8b1da30 --- /dev/null +++ b/src/assets/icons/captcha.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/cascader.svg b/src/assets/icons/cascader.svg new file mode 100644 index 0000000..57209bf --- /dev/null +++ b/src/assets/icons/cascader.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/client.svg b/src/assets/icons/client.svg new file mode 100644 index 0000000..7373b3d --- /dev/null +++ b/src/assets/icons/client.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/close.svg b/src/assets/icons/close.svg new file mode 100644 index 0000000..e99c978 --- /dev/null +++ b/src/assets/icons/close.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/close_all.svg b/src/assets/icons/close_all.svg new file mode 100644 index 0000000..2005198 --- /dev/null +++ b/src/assets/icons/close_all.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/close_left.svg b/src/assets/icons/close_left.svg new file mode 100644 index 0000000..fc5cf71 --- /dev/null +++ b/src/assets/icons/close_left.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/close_other.svg b/src/assets/icons/close_other.svg new file mode 100644 index 0000000..27ffc32 --- /dev/null +++ b/src/assets/icons/close_other.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/close_right.svg b/src/assets/icons/close_right.svg new file mode 100644 index 0000000..b96dc1c --- /dev/null +++ b/src/assets/icons/close_right.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/collapse.svg b/src/assets/icons/collapse.svg new file mode 100644 index 0000000..1507568 --- /dev/null +++ b/src/assets/icons/collapse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/dict.svg b/src/assets/icons/dict.svg new file mode 100644 index 0000000..db60220 --- /dev/null +++ b/src/assets/icons/dict.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/document.svg b/src/assets/icons/document.svg new file mode 100644 index 0000000..aaa0574 --- /dev/null +++ b/src/assets/icons/document.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/download.svg b/src/assets/icons/download.svg new file mode 100644 index 0000000..a8077dc --- /dev/null +++ b/src/assets/icons/download.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/fullscreen-exit.svg b/src/assets/icons/fullscreen-exit.svg new file mode 100644 index 0000000..2452f2b --- /dev/null +++ b/src/assets/icons/fullscreen-exit.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/fullscreen.svg b/src/assets/icons/fullscreen.svg new file mode 100644 index 0000000..4b6ee11 --- /dev/null +++ b/src/assets/icons/fullscreen.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/github.svg b/src/assets/icons/github.svg new file mode 100644 index 0000000..1adfa4e --- /dev/null +++ b/src/assets/icons/github.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/homepage.svg b/src/assets/icons/homepage.svg new file mode 100644 index 0000000..1e1feab --- /dev/null +++ b/src/assets/icons/homepage.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/ip.svg b/src/assets/icons/ip.svg new file mode 100644 index 0000000..087f3bb --- /dev/null +++ b/src/assets/icons/ip.svg @@ -0,0 +1 @@ + diff --git a/src/assets/icons/language.svg b/src/assets/icons/language.svg new file mode 100644 index 0000000..e754062 --- /dev/null +++ b/src/assets/icons/language.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/menu.svg b/src/assets/icons/menu.svg new file mode 100644 index 0000000..f5875d3 --- /dev/null +++ b/src/assets/icons/menu.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/message.svg b/src/assets/icons/message.svg new file mode 100644 index 0000000..deacdc3 --- /dev/null +++ b/src/assets/icons/message.svg @@ -0,0 +1 @@ + diff --git a/src/assets/icons/monitor.svg b/src/assets/icons/monitor.svg new file mode 100644 index 0000000..f153b9c --- /dev/null +++ b/src/assets/icons/monitor.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/project.svg b/src/assets/icons/project.svg new file mode 100644 index 0000000..eaf6a12 --- /dev/null +++ b/src/assets/icons/project.svg @@ -0,0 +1 @@ + diff --git a/src/assets/icons/pv.svg b/src/assets/icons/pv.svg new file mode 100644 index 0000000..cd7da06 --- /dev/null +++ b/src/assets/icons/pv.svg @@ -0,0 +1 @@ + diff --git a/src/assets/icons/refresh.svg b/src/assets/icons/refresh.svg new file mode 100644 index 0000000..e598ed1 --- /dev/null +++ b/src/assets/icons/refresh.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/role.svg b/src/assets/icons/role.svg new file mode 100644 index 0000000..5d25278 --- /dev/null +++ b/src/assets/icons/role.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/setting.svg b/src/assets/icons/setting.svg new file mode 100644 index 0000000..fbc4945 --- /dev/null +++ b/src/assets/icons/setting.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/size.svg b/src/assets/icons/size.svg new file mode 100644 index 0000000..f92f852 --- /dev/null +++ b/src/assets/icons/size.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/system.svg b/src/assets/icons/system.svg new file mode 100644 index 0000000..2e6045b --- /dev/null +++ b/src/assets/icons/system.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/table.svg b/src/assets/icons/table.svg new file mode 100644 index 0000000..1a16abb --- /dev/null +++ b/src/assets/icons/table.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/todo.svg b/src/assets/icons/todo.svg new file mode 100644 index 0000000..f48e667 --- /dev/null +++ b/src/assets/icons/todo.svg @@ -0,0 +1 @@ + diff --git a/src/assets/icons/tree.svg b/src/assets/icons/tree.svg new file mode 100644 index 0000000..51aea8f --- /dev/null +++ b/src/assets/icons/tree.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/user.svg b/src/assets/icons/user.svg new file mode 100644 index 0000000..8e693ec --- /dev/null +++ b/src/assets/icons/user.svg @@ -0,0 +1 @@ + diff --git a/src/assets/icons/uv.svg b/src/assets/icons/uv.svg new file mode 100644 index 0000000..c5be7eb --- /dev/null +++ b/src/assets/icons/uv.svg @@ -0,0 +1 @@ + diff --git a/src/assets/images/401.gif b/src/assets/images/401.gif new file mode 100644 index 0000000..4c930e7 Binary files /dev/null and b/src/assets/images/401.gif differ diff --git a/src/assets/images/404.png b/src/assets/images/404.png new file mode 100644 index 0000000..4e09829 Binary files /dev/null and b/src/assets/images/404.png differ diff --git a/src/assets/images/404_cloud.png b/src/assets/images/404_cloud.png new file mode 100644 index 0000000..a81d8da Binary files /dev/null and b/src/assets/images/404_cloud.png differ diff --git a/src/assets/images/login-background-dark.jpg b/src/assets/images/login-background-dark.jpg new file mode 100644 index 0000000..50dc817 Binary files /dev/null and b/src/assets/images/login-background-dark.jpg differ diff --git a/src/assets/images/login-background-light.jpg b/src/assets/images/login-background-light.jpg new file mode 100644 index 0000000..993b958 Binary files /dev/null and b/src/assets/images/login-background-light.jpg differ diff --git a/src/assets/logo.png b/src/assets/logo.png new file mode 100644 index 0000000..b1f7f76 Binary files /dev/null and b/src/assets/logo.png differ diff --git a/src/components/AppLink/index.vue b/src/components/AppLink/index.vue new file mode 100644 index 0000000..43bbb30 --- /dev/null +++ b/src/components/AppLink/index.vue @@ -0,0 +1,38 @@ + + + diff --git a/src/components/BaseEcharts/config.ts b/src/components/BaseEcharts/config.ts new file mode 100644 index 0000000..f2356fa --- /dev/null +++ b/src/components/BaseEcharts/config.ts @@ -0,0 +1,74 @@ +// 引入 echarts 核心模块,核心模块提供了 echarts 使用必须要的接口。 +import * as echarts from "echarts/core"; + +// 引入内置组件,组件后缀都为Component +import { + TitleComponent, + TooltipComponent, + GridComponent, + PolarComponent, + AriaComponent, + ParallelComponent, + LegendComponent, + RadarComponent, + ToolboxComponent, + DatasetComponent, // 数据集组件 + DataZoomComponent, + VisualMapComponent, + TimelineComponent, + CalendarComponent, + GraphicComponent, + TransformComponent, // 数据转换器组件(filter, sort) +} from "echarts/components"; + +// 引入渲染器:echarst默认使用canvas渲染,引入 CanvasRenderer 或者 SVGRenderer 是必须的一步 +import { CanvasRenderer, SVGRenderer } from "echarts/renderers"; + +// 标签自动布局、全局过渡动画等特性 +import { LabelLayout, UniversalTransition } from "echarts/features"; + +// 引入图表类型,后缀都为Chart +import { + BarChart, + LineChart, + PieChart, + MapChart, + RadarChart, + PictorialBarChart, +} from "echarts/charts"; + +// 注册必须的组件 +echarts.use([ + // 内置组件 + TitleComponent, + TooltipComponent, + GridComponent, + PolarComponent, + AriaComponent, + ParallelComponent, + LegendComponent, + RadarComponent, + ToolboxComponent, + DatasetComponent, + DataZoomComponent, + VisualMapComponent, + TimelineComponent, + CalendarComponent, + GraphicComponent, + TransformComponent, + // 渲染器 + CanvasRenderer, + SVGRenderer, + // 特性 + LabelLayout, + UniversalTransition, + // 图表 + BarChart, + LineChart, + PieChart, + MapChart, + RadarChart, + PictorialBarChart, +]); + +export default echarts; diff --git a/src/components/BaseEcharts/index.vue b/src/components/BaseEcharts/index.vue new file mode 100644 index 0000000..bbcab82 --- /dev/null +++ b/src/components/BaseEcharts/index.vue @@ -0,0 +1,43 @@ + + + diff --git a/src/components/Breadcrumb/index.vue b/src/components/Breadcrumb/index.vue new file mode 100644 index 0000000..ea2b1af --- /dev/null +++ b/src/components/Breadcrumb/index.vue @@ -0,0 +1,97 @@ + + + + + diff --git a/src/components/CURD/PageContent.vue b/src/components/CURD/PageContent.vue new file mode 100644 index 0000000..7b73799 --- /dev/null +++ b/src/components/CURD/PageContent.vue @@ -0,0 +1,1022 @@ + + + + + diff --git a/src/components/CURD/PageForm.vue b/src/components/CURD/PageForm.vue new file mode 100644 index 0000000..524a6a4 --- /dev/null +++ b/src/components/CURD/PageForm.vue @@ -0,0 +1,174 @@ + + + diff --git a/src/components/CURD/PageModal.vue b/src/components/CURD/PageModal.vue new file mode 100644 index 0000000..3d53e39 --- /dev/null +++ b/src/components/CURD/PageModal.vue @@ -0,0 +1,405 @@ + + + + + diff --git a/src/components/CURD/PageSearch.vue b/src/components/CURD/PageSearch.vue new file mode 100644 index 0000000..0575149 --- /dev/null +++ b/src/components/CURD/PageSearch.vue @@ -0,0 +1,237 @@ + + + + + diff --git a/src/components/CURD/types.ts b/src/components/CURD/types.ts new file mode 100644 index 0000000..97f4d83 --- /dev/null +++ b/src/components/CURD/types.ts @@ -0,0 +1,271 @@ +import type { + DialogProps, + DrawerProps, + FormItemRule, + FormProps, + PaginationProps, + TableProps, + ColProps, +} from "element-plus"; +import PageContent from "./PageContent.vue"; +import PageForm from "./PageForm.vue"; +import PageModal from "./PageModal.vue"; +import PageSearch from "./PageSearch.vue"; + +export type PageSearchInstance = InstanceType; +export type PageContentInstance = InstanceType; +export type PageModalInstance = InstanceType; +export type PageFormInstance = InstanceType; + +export type IObject = Record; + +export interface IOperatData { + name: string; + row: IObject; + column: IObject; + $index: number; +} + +export interface ISearchConfig { + // 页面名称(参与组成权限标识,如sys:user:xxx) + pageName: string; + // 表单项 + formItems: Array<{ + // 组件类型(如input,select等) + type?: "input" | "select" | "tree-select" | "date-picker" | "input-tag"; + // 标签文本 + label: string; + // 标签提示 + tips?: string; + // 键名 + prop: string; + // 组件属性(input-tag组件支持join,btnText,size属性) + attrs?: IObject; + // 初始值 + initialValue?: any; + // 可选项(适用于select组件) + options?: { label: string; value: any }[]; + // 初始化数据函数扩展 + initFn?: (formItem: IObject) => void; + }>; + // 是否开启展开和收缩 + isExpandable?: boolean; + // 默认展示的表单项数量 + showNumber?: number; +} + +export interface IContentConfig { + // 页面名称(参与组成权限标识,如sys:user:xxx) + pageName: string; + // table组件属性 + table?: Omit, "data">; + // pagination组件属性 + pagination?: + | boolean + | Partial< + Omit< + PaginationProps, + "v-model:page-size" | "v-model:current-page" | "total" | "currentPage" + > + >; + // 列表的网络请求函数(需返回promise) + indexAction: (queryParams: T) => Promise; + // 默认的分页相关的请求参数 + request?: { + pageName: string; + limitName: string; + }; + // 数据格式解析的回调函数 + parseData?: (res: any) => { + total: number; + list: IObject[]; + [key: string]: any; + }; + // 修改属性的网络请求函数(需返回promise) + modifyAction?: (data: { + [key: string]: any; + field: string; + value: boolean | string | number; + }) => Promise; + // 删除的网络请求函数(需返回promise) + deleteAction?: (ids: string) => Promise; + // 后端导出的网络请求函数(需返回promise) + exportAction?: (queryParams: T) => Promise; + // 前端全量导出的网络请求函数(需返回promise) + exportsAction?: (queryParams: T) => Promise; + // 导入模板 + importTemplate?: string | (() => Promise); + // 后端导入的网络请求函数(需返回promise) + importAction?: (file: File) => Promise; + // 前端导入的网络请求函数(需返回promise) + importsAction?: (data: IObject[]) => Promise; + // 主键名(默认为id) + pk?: string; + // 表格工具栏(默认支持add,delete,export,也可自定义) + toolbar?: Array< + | "add" + | "delete" + | "import" + | "export" + | { + auth: string; + icon?: string; + name: string; + text: string; + type?: "primary" | "success" | "warning" | "danger" | "info"; + } + >; + // 表格工具栏右侧图标 + defaultToolbar?: Array< + | "refresh" + | "filter" + | "imports" + | "exports" + | "search" + | { + name: string; + icon: string; + title?: string; + auth?: string; + } + >; + // table组件列属性(额外的属性templet,operat,slotName) + cols: Array<{ + type?: "default" | "selection" | "index" | "expand"; + label?: string; + prop?: string; + width?: string | number; + align?: "left" | "center" | "right"; + columnKey?: string; + reserveSelection?: boolean; + // 列是否显示 + show?: boolean; + // 模板 + templet?: + | "image" + | "list" + | "url" + | "switch" + | "input" + | "price" + | "percent" + | "icon" + | "date" + | "tool" + | "custom"; + // image模板相关参数 + imageWidth?: number; + imageHeight?: number; + // list模板相关参数 + selectList?: IObject; + // switch模板相关参数 + activeValue?: boolean | string | number; + inactiveValue?: boolean | string | number; + activeText?: string; + inactiveText?: string; + // input模板相关参数 + inputType?: string; + // price模板相关参数 + priceFormat?: string; + // date模板相关参数 + dateFormat?: string; + // tool模板相关参数 + operat?: Array< + | "edit" + | "delete" + | { + auth: string; + icon?: string; + name: string; + text: string; + type?: "primary" | "success" | "warning" | "danger" | "info"; + render?: (row: IObject) => boolean; + } + >; + // filter值拼接符 + filterJoin?: string; + [key: string]: any; + // 初始化数据函数 + initFn?: (item: IObject) => void; + }>; +} + +export interface IModalConfig { + // 页面名称 + pageName?: string; + // 主键名(主要用于编辑数据,默认为id) + pk?: string; + // 组件类型 + component?: "dialog" | "drawer"; + // dialog组件属性 + dialog?: Partial>; + // drawer组件属性 + drawer?: Partial>; + // form组件属性 + form?: IForm; + // 表单项 + formItems: IFormItems; + // 提交之前处理 + beforeSubmit?: (data: T) => void; + // 提交的网络请求函数(需返回promise) + formAction: (data: T) => Promise; +} + +export type IForm = Partial>; + +// 表单项 +export type IFormItems = Array<{ + // 组件类型(如input,select,radio,custom等,默认input) + type?: + | "input" + | "select" + | "radio" + | "checkbox" + | "tree-select" + | "date-picker" + | "input-number" + | "text" + | "custom"; + // 组件属性 + attrs?: IObject; + // 组件可选项(适用于select,radio,checkbox组件) + options?: Array<{ + label: string; + value: any; + disabled?: boolean; + [key: string]: any; + }>; + // 插槽名(适用于组件类型为custom) + slotName?: string; + // 标签文本 + label: string; + // 标签提示 + tips?: string; + // 键名 + prop: string; + // 验证规则 + rules?: FormItemRule[]; + // 初始值 + initialValue?: any; + // 是否隐藏 + hidden?: boolean; + // layout组件Col属性 + col?: Partial; + // 监听函数 + watch?: (newValue: any, oldValue: any, data: T, items: IObject[]) => void; + // 计算属性函数 + computed?: (data: T) => any; + // 监听收集函数 + watchEffect?: (data: T) => void; + // 初始化数据函数扩展 + initFn?: (item: IObject) => void; +}>; + +export interface IPageForm { + // 主键名(主要用于编辑数据,默认为id) + pk?: string; + // form组件属性 + form?: IForm; + // 表单项 + formItems: IFormItems; +} diff --git a/src/components/CURD/usePage.ts b/src/components/CURD/usePage.ts new file mode 100644 index 0000000..4d410ce --- /dev/null +++ b/src/components/CURD/usePage.ts @@ -0,0 +1,73 @@ +import { ref } from "vue"; +import type { + IObject, + PageContentInstance, + PageModalInstance, + PageSearchInstance, +} from "./types"; + +function usePage() { + const searchRef = ref(); + const contentRef = ref(); + const addModalRef = ref(); + const editModalRef = ref(); + + // 搜索 + function handleQueryClick(queryParams: IObject) { + const filterParams = contentRef.value?.getFilterParams(); + contentRef.value?.fetchPageData({ ...queryParams, ...filterParams }, true); + } + // 重置 + function handleResetClick(queryParams: IObject) { + const filterParams = contentRef.value?.getFilterParams(); + contentRef.value?.fetchPageData({ ...queryParams, ...filterParams }, true); + } + // 新增 + function handleAddClick() { + //显示添加表单 + addModalRef.value?.setModalVisible(); + } + // 编辑 + function handleEditClick(row: IObject) { + //显示编辑表单 根据数据进行填充 + editModalRef.value?.setModalVisible(row); + } + // 表单提交 + function handleSubmitClick() { + //根据检索条件刷新列表数据 + const queryParams = searchRef.value?.getQueryParams(); + contentRef.value?.fetchPageData(queryParams, true); + } + // 导出 + function handleExportClick() { + // 根据检索条件导出数据 + const queryParams = searchRef.value?.getQueryParams(); + contentRef.value?.exportPageData(queryParams); + } + // 搜索显隐 + function handleSearchClick() { + searchRef.value?.toggleVisible(); + } + // 涮选数据 + function handleFilterChange(filterParams: IObject) { + const queryParams = searchRef.value?.getQueryParams(); + contentRef.value?.fetchPageData({ ...queryParams, ...filterParams }, true); + } + + return { + searchRef, + contentRef, + addModalRef, + editModalRef, + handleQueryClick, + handleResetClick, + handleAddClick, + handleEditClick, + handleSubmitClick, + handleExportClick, + handleSearchClick, + handleFilterChange, + }; +} + +export default usePage; diff --git a/src/components/CopyButton/index.vue b/src/components/CopyButton/index.vue new file mode 100644 index 0000000..8e88435 --- /dev/null +++ b/src/components/CopyButton/index.vue @@ -0,0 +1,62 @@ + + + + diff --git a/src/components/Dictionary/index.vue b/src/components/Dictionary/index.vue new file mode 100644 index 0000000..763c2c6 --- /dev/null +++ b/src/components/Dictionary/index.vue @@ -0,0 +1,76 @@ + + + diff --git a/src/components/GithubCorner/index.vue b/src/components/GithubCorner/index.vue new file mode 100644 index 0000000..7f91959 --- /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..6ab2eb6 --- /dev/null +++ b/src/components/Hamburger/index.vue @@ -0,0 +1,39 @@ + + + + diff --git a/src/components/IconSelect/index.vue b/src/components/IconSelect/index.vue new file mode 100644 index 0000000..558438d --- /dev/null +++ b/src/components/IconSelect/index.vue @@ -0,0 +1,237 @@ + + + + + diff --git a/src/components/LangSelect/index.vue b/src/components/LangSelect/index.vue new file mode 100644 index 0000000..2f35582 --- /dev/null +++ b/src/components/LangSelect/index.vue @@ -0,0 +1,47 @@ + + + diff --git a/src/components/Pagination/index.vue b/src/components/Pagination/index.vue new file mode 100644 index 0000000..c080e60 --- /dev/null +++ b/src/components/Pagination/index.vue @@ -0,0 +1,81 @@ + + + + + diff --git a/src/components/SizeSelect/index.vue b/src/components/SizeSelect/index.vue new file mode 100644 index 0000000..d3e83fe --- /dev/null +++ b/src/components/SizeSelect/index.vue @@ -0,0 +1,39 @@ + + + diff --git a/src/components/SvgIcon/index.vue b/src/components/SvgIcon/index.vue new file mode 100644 index 0000000..07b65e8 --- /dev/null +++ b/src/components/SvgIcon/index.vue @@ -0,0 +1,45 @@ + + + + + diff --git a/src/components/TableSelect/index.vue b/src/components/TableSelect/index.vue new file mode 100644 index 0000000..28ad4b6 --- /dev/null +++ b/src/components/TableSelect/index.vue @@ -0,0 +1,371 @@ + + + + + diff --git a/src/components/Upload/MultiUpload.vue b/src/components/Upload/MultiUpload.vue new file mode 100644 index 0000000..a0dfd65 --- /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..5da8039 --- /dev/null +++ b/src/components/Upload/SingleUpload.vue @@ -0,0 +1,77 @@ + + + + + diff --git a/src/components/WangEditor/index.vue b/src/components/WangEditor/index.vue new file mode 100644 index 0000000..bdc4cc8 --- /dev/null +++ b/src/components/WangEditor/index.vue @@ -0,0 +1,73 @@ + + + + + diff --git a/src/directive/index.ts b/src/directive/index.ts new file mode 100644 index 0000000..9c22eb6 --- /dev/null +++ b/src/directive/index.ts @@ -0,0 +1,9 @@ +import type { App } from "vue"; + +import { hasPerm } from "./permission"; + +// 全局注册 directive +export function setupDirective(app: App) { + // 使 v-hasPerm 在所有组件中都可用 + app.directive("hasPerm", hasPerm); +} diff --git a/src/directive/permission/index.ts b/src/directive/permission/index.ts new file mode 100644 index 0000000..7a080e3 --- /dev/null +++ b/src/directive/permission/index.ts @@ -0,0 +1,38 @@ +import { hasAuth } from "@/plugins/permission"; +import { Directive, DirectiveBinding } from "vue"; + +/** + * 按钮权限 + */ +export const hasPerm: Directive = { + mounted(el: HTMLElement, binding: DirectiveBinding) { + // DOM绑定需要的按钮权限标识 + const { value: requiredPerms } = binding; + if (requiredPerms) { + if (!hasAuth(requiredPerms)) { + 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) { + // DOM绑定需要的角色编码 + const { value: requiredRoles } = binding; + if (requiredRoles) { + if (!hasAuth(requiredRoles, "role")) { + el.parentNode && el.parentNode.removeChild(el); + } + } else { + throw new Error("need roles! Like v-has-role=\"['admin','test']\""); + } + }, +}; diff --git a/src/enums/CacheEnum.ts b/src/enums/CacheEnum.ts new file mode 100644 index 0000000..222e98b --- /dev/null +++ b/src/enums/CacheEnum.ts @@ -0,0 +1,10 @@ +/** + * 令牌缓存Key + */ +export const TOKEN_KEY = "accessToken"; + +// 缓存code +export const CODE_KEY = "mechanismCode"; + +// 缓存code +export const CODE_NAME = "mechanismName"; diff --git a/src/enums/DeviceEnum.ts b/src/enums/DeviceEnum.ts new file mode 100644 index 0000000..709bcb3 --- /dev/null +++ b/src/enums/DeviceEnum.ts @@ -0,0 +1,14 @@ +/** + * 设备枚举 + */ +export const enum DeviceEnum { + /** + * 宽屏设备 + */ + DESKTOP = "desktop", + + /** + * 窄屏设备 + */ + MOBILE = "mobile", +} diff --git a/src/enums/LanguageEnum.ts b/src/enums/LanguageEnum.ts new file mode 100644 index 0000000..a50b655 --- /dev/null +++ b/src/enums/LanguageEnum.ts @@ -0,0 +1,14 @@ +/** + * 语言枚举 + */ +export const enum LanguageEnum { + /** + * 中文 + */ + ZH_CN = "zh-cn", + + /** + * 英文 + */ + EN = "en", +} diff --git a/src/enums/LayoutEnum.ts b/src/enums/LayoutEnum.ts new file mode 100644 index 0000000..68be72e --- /dev/null +++ b/src/enums/LayoutEnum.ts @@ -0,0 +1,18 @@ +/** + * 菜单布局枚举 + */ +export const enum LayoutEnum { + /** + * 左侧菜单布局 + */ + LEFT = "left", + /** + * 顶部菜单布局 + */ + TOP = "top", + + /** + * 混合菜单布局 + */ + MIX = "mix", +} diff --git a/src/enums/MenuTypeEnum.ts b/src/enums/MenuTypeEnum.ts new file mode 100644 index 0000000..b69942f --- /dev/null +++ b/src/enums/MenuTypeEnum.ts @@ -0,0 +1,22 @@ +/** + * 菜单类型枚举 + */ +export const enum MenuTypeEnum { + /** + * 目录 + */ + CATALOG = "CATALOG", + /** + * 菜单 + */ + MENU = "MENU", + + /** + * 按钮 + */ + BUTTON = "BUTTON", + /** + * 外链 + */ + EXTLINK = "EXTLINK", +} diff --git a/src/enums/MessageTypeEnum.ts b/src/enums/MessageTypeEnum.ts new file mode 100644 index 0000000..42a6fb9 --- /dev/null +++ b/src/enums/MessageTypeEnum.ts @@ -0,0 +1,15 @@ +/* 消息类型枚举 */ +export const enum MessageTypeEnum { + /* 消息 */ + MESSAGE = "MESSAGE", + /* 通知 */ + NOTICE = "NOTICE", + /* 待办 */ + TODO = "TODO", +} + +export const MessageTypeLabels = { + [MessageTypeEnum.MESSAGE]: "消息", + [MessageTypeEnum.NOTICE]: "通知", + [MessageTypeEnum.TODO]: "待办", +}; diff --git a/src/enums/NoticeTypeEnum.ts b/src/enums/NoticeTypeEnum.ts new file mode 100644 index 0000000..49c0f8e --- /dev/null +++ b/src/enums/NoticeTypeEnum.ts @@ -0,0 +1,30 @@ +/* 通知类型枚举 */ +export const enum NoticeTypeEnum { + /** 系统升级 */ + SYSTEM_UPGRADE = "SYSTEM_UPGRADE", + /** 系统维护 */ + SYSTEM_MAINTENANCE = "SYSTEM_MAINTENANCE", + /** 安全警告 */ + SECURITY_ALERT = "SECURITY_ALERT", + /** 假期通知 */ + HOLIDAY_NOTICE = "HOLIDAY_NOTICE", + /** 公司新闻 */ + COMPANY_NEWS = "COMPANY_NEWS", + /** 其他通知 */ + OTHER = "OTHER", +} + +// 定义标签映射 +const NoticeTypeLabels: Record = { + [NoticeTypeEnum.SYSTEM_UPGRADE]: "系统升级", + [NoticeTypeEnum.SYSTEM_MAINTENANCE]: "系统维护", + [NoticeTypeEnum.SECURITY_ALERT]: "安全警告", + [NoticeTypeEnum.HOLIDAY_NOTICE]: "假期通知", + [NoticeTypeEnum.COMPANY_NEWS]: "公司新闻", + [NoticeTypeEnum.OTHER]: "其他通知", +}; + +// 导出获取标签函数 +export const getNoticeLabel = (type: NoticeTypeEnum): string => { + return NoticeTypeLabels[type] || ""; +}; diff --git a/src/enums/ResultEnum.ts b/src/enums/ResultEnum.ts new file mode 100644 index 0000000..ece59a5 --- /dev/null +++ b/src/enums/ResultEnum.ts @@ -0,0 +1,18 @@ +/** + * 响应码枚举 + */ +export const enum ResultEnum { + /** + * 成功 + */ + SUCCESS = 200, + /** + * 错误 + */ + ERROR = "B0001", + + /** + * 令牌无效或过期 + */ + TOKEN_INVALID = "A0230", +} diff --git a/src/enums/SidebarStatusEnum.ts b/src/enums/SidebarStatusEnum.ts new file mode 100644 index 0000000..a0d877a --- /dev/null +++ b/src/enums/SidebarStatusEnum.ts @@ -0,0 +1,14 @@ +/** + * 侧边栏状态枚举 + */ +export const enum SidebarStatusEnum { + /** + * 展开 + */ + OPENED = "opened", + + /** + * 关闭 + */ + CLOSED = "closed", +} diff --git a/src/enums/SizeEnum.ts b/src/enums/SizeEnum.ts new file mode 100644 index 0000000..e91e913 --- /dev/null +++ b/src/enums/SizeEnum.ts @@ -0,0 +1,19 @@ +/** + * 布局大小枚举 + */ +export const enum SizeEnum { + /** + * 默认 + */ + DEFAULT = "default", + + /** + * 大型 + */ + LARGE = "large", + + /** + * 小型 + */ + SMALL = "small", +} diff --git a/src/enums/ThemeEnum.ts b/src/enums/ThemeEnum.ts new file mode 100644 index 0000000..91458e1 --- /dev/null +++ b/src/enums/ThemeEnum.ts @@ -0,0 +1,18 @@ +/** + * 主题枚举 + */ +export const enum ThemeEnum { + /** + * 明亮主题 + */ + LIGHT = "light", + /** + * 暗黑主题 + */ + DARK = "dark", + + /** + * 系统自动 + */ + AUTO = "auto", +} diff --git a/src/hooks/useEcharts.ts b/src/hooks/useEcharts.ts new file mode 100644 index 0000000..fc42731 --- /dev/null +++ b/src/hooks/useEcharts.ts @@ -0,0 +1,59 @@ +import { + Ref, + shallowRef, + unref, + onMounted, + onDeactivated, + onBeforeUnmount, +} from "vue"; + +import echarts from "@/components/BaseEcharts/config"; + +export type EChartsCoreOption = echarts.EChartsCoreOption; + +const useEcharts = (elRef: Ref, options: EChartsCoreOption) => { + const charts = shallowRef(); + + const setOptions = (options: EChartsCoreOption) => { + charts.value && charts.value.setOption(options); + }; + + // 初始化 + const initCharts = (themeColor?: Array) => { + const el = unref(elRef); + if (!el || !unref(el)) { + return; + } + charts.value = echarts.init(el); + if (themeColor) { + options.color = themeColor; + } + setOptions(options); + }; + + // 重新窗口变化时,重新计算 + const resize = () => { + charts.value && charts.value.resize(); + }; + + onMounted(() => { + window.addEventListener("resize", resize); + }); + + // 页面keepAlive时,不监听页面 + onDeactivated(() => { + window.removeEventListener("resize", resize); + }); + + onBeforeUnmount(() => { + window.removeEventListener("resize", resize); + }); + + return { + initCharts, + setOptions, + resize, + }; +}; + +export { useEcharts }; diff --git a/src/hooks/useWebSocket.ts b/src/hooks/useWebSocket.ts new file mode 100644 index 0000000..ce1c625 --- /dev/null +++ b/src/hooks/useWebSocket.ts @@ -0,0 +1,30 @@ +import { CODE_KEY } from "@/enums/CacheEnum"; +export default function useWebSocket() { + // // 创建 WebSocket 连接 + // ws://cloud.52zaisheng.cn/test/sh0001/api/websocket/orderIn/13100000 + const ws = new WebSocket( + `wss://ifanda.52zaisheng.cn/test/api/websocket/orderIn/${localStorage.getItem(CODE_KEY)}` + ); + // // 连接打开时触发 + // socket.onopen = function () { + // console.log("连接成功"); + // }; + // // 接收到服务器消息时触发 + // socket.onmessage = function (event) { + // console.log("收到消息:", event.data); + // }; + // // 连接关闭时触发 + // socket.onclose = function (event) { + // console.log("连接关闭"); + // }; + // // 连接出错时触发 + // socket.onerror = function (error) { + // console.log("连接出错:", error); + // }; + // // 发送消息到服务器 + // function sendMessage(message: any) { + // socket.send(message); + // } + + return { ws }; +} diff --git a/src/json/index.ts b/src/json/index.ts new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/json/index.ts @@ -0,0 +1 @@ + diff --git a/src/json/menus copy.json b/src/json/menus copy.json new file mode 100644 index 0000000..ecc80bc --- /dev/null +++ b/src/json/menus copy.json @@ -0,0 +1,870 @@ +[ + { + "path": "/self/receive-core", + "component": "Layout", + "name": "/receive-core", + "meta": { + "title": "收货入库", + "icon": "el-icon-operation", + "hidden": false, + "alwaysShow": false, + "params": null + }, + "children": [ + { + "path": "receive-pricing", + "component": "self/receive-core/pricing", + "name": "receive-pricing", + "meta": { + "title": "待定价", + "icon": "el-icon-Star", + "hidden": false, + "keepAlive": true, + "alwaysShow": false, + "params": { + "scaleStatus": 0 + } + } + }, + { + "path": "receive-waiting-tare", + "component": "self/receive-core/pricing", + "name": "receive-waiting-tare", + "meta": { + "title": "待过皮重", + "icon": "el-icon-Star", + "hidden": false, + "keepAlive": true, + "alwaysShow": false, + "params": { + "scaleStatus": 1 + } + } + }, + { + "path": "receive-review", + "component": "self/receive-core/pricing", + "name": "receive-review", + "meta": { + "title": "待审核", + "icon": "el-icon-Star", + "hidden": false, + "keepAlive": true, + "alwaysShow": false, + "params": { + "scaleStatus": 2 + } + } + }, + { + "path": "receive-waiting-pay", + "component": "self/receive-core/pricing", + "name": "receive-waiting-pay", + "meta": { + "title": "待付款", + "icon": "el-icon-Star", + "hidden": false, + "keepAlive": true, + "alwaysShow": false, + "params": { + "scaleStatus": 3 + } + } + }, + { + "path": "receive-pay", + "component": "self/receive-core/pricing", + "name": "receive-pay", + "meta": { + "title": "已付款", + "icon": "el-icon-Star", + "hidden": false, + "keepAlive": true, + "alwaysShow": false, + "params": { + "scaleStatus": 4 + } + } + }, + { + "path": "receive-spl", + "component": "self/receive-core/receive-spl", + "name": "receive-spl", + "meta": { + "title": "收货补单", + "icon": "el-icon-menu", + "hidden": false, + "keepAlive": true, + "alwaysShow": false, + "params": null + } + } + ] + }, + { + "path": "/self/shipment-core", + "component": "Layout", + "name": "/shipment-core", + "meta": { + "title": "出货销售", + "icon": "el-icon-operation", + "hidden": false, + "alwaysShow": false, + "params": null + }, + "children": [ + { + "path": "shipmenting", + "component": "self/shipment-core/shipmenting", + "name": "shipmenting", + "meta": { + "title": "待出货", + "icon": "el-icon-Star", + "hidden": false, + "keepAlive": true, + "alwaysShow": false, + "params": { + "scaleStatus": 0 + } + } + }, + { + "path": "shipment-gross", + "component": "self/shipment-core/shipmenting", + "name": "shipment-gross", + "meta": { + "title": "待过毛重", + "icon": "el-icon-Star", + "hidden": false, + "keepAlive": true, + "alwaysShow": false, + "params": { + "scaleStatus": 1 + } + } + }, + { + "path": "shipment-settlement", + "component": "self/shipment-core/shipmenting", + "name": "shipment-settlement", + "meta": { + "title": "待结算", + "icon": "el-icon-Star", + "hidden": false, + "keepAlive": true, + "alwaysShow": false, + "params": { + "scaleStatus": 2 + } + } + }, + { + "path": "shipment-payment", + "component": "self/shipment-core/shipmenting", + "name": "shipment-payment", + "meta": { + "title": "待收款", + "icon": "el-icon-Star", + "hidden": false, + "keepAlive": true, + "alwaysShow": false, + "params": { + "scaleStatus": 3 + } + } + }, + { + "path": "shipment-paied", + "component": "self/shipment-core/shipmenting", + "name": "shipment-paied", + "meta": { + "title": "已收款", + "icon": "el-icon-Star", + "hidden": false, + "keepAlive": true, + "alwaysShow": false, + "params": { + "scaleStatus": 4 + } + } + }, + { + "path": "shipment-spl", + "component": "self/shipment-core/shipment-spl", + "name": "shipment-spl", + "meta": { + "title": "出货补单", + "icon": "el-icon-menu", + "hidden": false, + "keepAlive": true, + "alwaysShow": false, + "params": null + } + } + ] + }, + { + "path": "/self/receive-data", + "component": "Layout", + "name": "/receive-data", + "meta": { + "title": "收货数据", + "icon": "el-icon-operation", + "hidden": false, + "alwaysShow": false, + "params": null + }, + "children": [ + { + "path": "receive-detail", + "component": "self/receive-data/receive-detail", + "name": "receive-detail", + "meta": { + "title": "收货明细", + "icon": "el-icon-menu", + "hidden": false, + "keepAlive": true, + "alwaysShow": false, + "params": null + } + }, + { + "path": "receive-summary", + "component": "self/receive-data/receive-summary", + "name": "receive-summary", + "meta": { + "title": "收货汇总", + "icon": "el-icon-menu", + "hidden": false, + "keepAlive": true, + "alwaysShow": false, + "params": null + } + }, + { + "path": "receive-cancel", + "component": "self/receive-data/receive-cancel", + "name": "receive-cancel", + "meta": { + "title": "收货作废", + "icon": "el-icon-menu", + "hidden": false, + "keepAlive": true, + "alwaysShow": false, + "params": null + } + }, + { + "path": "supplier-rank", + "component": "self/receive-data/supplier-rank", + "name": "supplier-rank", + "meta": { + "title": "供应商排行", + "icon": "el-icon-menu", + "hidden": false, + "keepAlive": true, + "alwaysShow": false, + "params": null + } + } + ] + }, + { + "path": "/self/shipment-data", + "component": "Layout", + "name": "/shipment-data", + "meta": { + "title": "出货数据", + "icon": "el-icon-operation", + "hidden": false, + "alwaysShow": false, + "params": null + }, + "children": [ + { + "path": "shipment-detail", + "component": "self/shipment-data/shipment-detail", + "name": "shipment-detail", + "meta": { + "title": "出货明细", + "icon": "el-icon-menu", + "hidden": false, + "keepAlive": true, + "alwaysShow": false, + "params": null + } + }, + { + "path": "shipment-summary", + "component": "self/shipment-data/shipment-summary", + "name": "shipment-summary", + "meta": { + "title": "出货汇总", + "icon": "el-icon-menu", + "hidden": false, + "keepAlive": true, + "alwaysShow": false, + "params": null + } + }, + { + "path": "shipment-cancel", + "component": "self/shipment-data/shipment-cancel", + "name": "shipment-cancel", + "meta": { + "title": "出货作废", + "icon": "el-icon-menu", + "hidden": false, + "keepAlive": true, + "alwaysShow": false, + "params": null + } + }, + { + "path": "customer-rank", + "component": "self/shipment-data/customer-rank", + "name": "customer-rank", + "meta": { + "title": "客户排行", + "icon": "el-icon-menu", + "hidden": false, + "keepAlive": true, + "alwaysShow": false, + "params": null + } + } + ] + }, + { + "path": "/self/my-account", + "component": "Layout", + "name": "/my-account", + "meta": { + "title": "我的账本", + "icon": "el-icon-operation", + "hidden": false, + "alwaysShow": false, + "params": null + }, + "children": [ + { + "path": "business-overview", + "component": "self/my-account/business-overview", + "name": "business-overview", + "meta": { + "title": "经营概况", + "icon": "el-icon-menu", + "hidden": false, + "keepAlive": true, + "alwaysShow": false, + "params": null + } + }, + { + "path": "supplier-reconciliation", + "component": "self/my-account/supplier-reconciliation", + "name": "supplier-reconciliation", + "meta": { + "title": "供应商对账", + "icon": "el-icon-menu", + "hidden": false, + "keepAlive": true, + "alwaysShow": false, + "params": null + } + }, + { + "path": "customer-reconciliation", + "component": "self/my-account/customer-reconciliation", + "name": "customer-reconciliation", + "meta": { + "title": "客户对账", + "icon": "el-icon-menu", + "hidden": false, + "keepAlive": true, + "alwaysShow": false, + "params": null + } + }, + { + "path": "payment-details", + "component": "self/my-account/payment-details", + "name": "payment-details", + "meta": { + "title": "付款明细", + "icon": "el-icon-menu", + "hidden": false, + "keepAlive": true, + "alwaysShow": false, + "params": null + } + }, + { + "path": "collection-details", + "component": "self/my-account/collection-details", + "name": "collection-details", + "meta": { + "title": "收款明细", + "icon": "el-icon-menu", + "hidden": false, + "keepAlive": true, + "alwaysShow": false, + "params": null + } + } + ] + }, + { + "path": "/self/params-mgt", + "component": "Layout", + "name": "/params-mgt", + "meta": { + "title": "参数管理", + "icon": "el-icon-operation", + "hidden": false, + "alwaysShow": false, + "params": null + }, + "children": [ + { + "path": "receive-type", + "component": "self/params-mgt/receive-type", + "name": "receive-type", + "meta": { + "title": "收货分类", + "icon": "el-icon-menu", + "hidden": false, + "keepAlive": true, + "alwaysShow": false, + "params": null + } + }, + { + "path": "receive-product", + "component": "self/params-mgt/receive-product", + "name": "receive-product", + "meta": { + "title": "收货产品", + "icon": "el-icon-menu", + "hidden": false, + "keepAlive": true, + "alwaysShow": false, + "params": null + } + }, + { + "path": "shipment-type", + "component": "self/params-mgt/shipment-type", + "name": "shipment-type", + "meta": { + "title": "出货分类", + "icon": "el-icon-menu", + "hidden": false, + "keepAlive": true, + "alwaysShow": false, + "params": null + } + }, + { + "path": "shipment-product", + "component": "self/params-mgt/shipment-product", + "name": "shipment-product", + "meta": { + "title": "出货产品", + "icon": "el-icon-menu", + "hidden": false, + "keepAlive": true, + "alwaysShow": false, + "params": null + } + }, + { + "path": "supplier-type", + "component": "self/params-mgt/supplier-type", + "name": "supplier-type", + "meta": { + "title": "供应商分类", + "icon": "el-icon-menu", + "hidden": false, + "keepAlive": true, + "alwaysShow": false, + "params": null + } + }, + { + "path": "supplier-mgt", + "component": "self/params-mgt/supplier-mgt", + "name": "supplier-mgt", + "meta": { + "title": "供应商管理", + "icon": "el-icon-menu", + "hidden": false, + "keepAlive": true, + "alwaysShow": false, + "params": null + } + }, + { + "path": "customer-mgt", + "component": "self/params-mgt/customer-mgt", + "name": "customer-mgt", + "meta": { + "title": "客户管理", + "icon": "el-icon-user", + "hidden": false, + "keepAlive": true, + "alwaysShow": false, + "params": null + } + }, + { + "path": "stock-card-mgt", + "component": "self/params-mgt/stock-card-mgt", + "name": "stock-card-mgt", + "meta": { + "title": "库存卡管理", + "icon": "el-icon-postcard", + "hidden": false, + "keepAlive": true, + "alwaysShow": false, + "params": null + } + }, + { + "path": "user-mgt", + "component": "self/params-mgt/user-mgt", + "name": "user-mgt", + "meta": { + "title": "用户管理", + "icon": "el-icon-user", + "hidden": false, + "keepAlive": true, + "alwaysShow": false, + "params": null + } + }, + { + "path": "role-mgt", + "component": "self/params-mgt/role-mgt", + "name": "role-mgt", + "meta": { + "title": "角色管理", + "icon": "el-icon-menu", + "hidden": false, + "keepAlive": true, + "alwaysShow": false, + "params": null + } + } + ] + }, + { + "path": "/self/other-config", + "component": "Layout", + "name": "/other-config", + "meta": { + "title": "其他配置", + "icon": "system", + "hidden": false, + "alwaysShow": false, + "params": null + }, + "children": [ + { + "path": "print-template", + "component": "self/other-config/print-template", + "name": "print", + "meta": { + "title": "打印模板", + "icon": "el-icon-printer", + "hidden": false, + "keepAlive": true, + "alwaysShow": false, + "params": null + } + }, + { + "path": "other", + "component": "self/other-config/other", + "name": "config", + "meta": { + "title": "其他配置", + "icon": "el-icon-setting", + "hidden": false, + "keepAlive": true, + "alwaysShow": false, + "params": null + } + } + ] + }, + + { + "path": "/system", + "component": "Layout", + "redirect": "/system/user", + "name": "/system", + "meta": { + "title": "系统管理", + "icon": "system", + "hidden": false, + "alwaysShow": false, + "params": null + }, + "children": [ + { + "path": "user", + "component": "system/user/index", + "name": "User", + "meta": { + "title": "用户管理", + "icon": "el-icon-User", + "hidden": false, + "keepAlive": true, + "alwaysShow": false, + "params": null + } + }, + { + "path": "role", + "component": "system/role/index", + "name": "Role", + "meta": { + "title": "角色管理", + "icon": "role", + "hidden": false, + "keepAlive": true, + "alwaysShow": false, + "params": null + } + }, + { + "path": "menu", + "component": "system/menu/index", + "name": "Menu", + "meta": { + "title": "菜单管理", + "icon": "menu", + "hidden": false, + "keepAlive": true, + "alwaysShow": false, + "params": null + } + }, + { + "path": "dept", + "component": "system/dept/index", + "name": "Dept", + "meta": { + "title": "部门管理", + "icon": "tree", + "hidden": false, + "keepAlive": true, + "alwaysShow": false, + "params": null + } + }, + { + "path": "dict", + "component": "system/dict/index", + "name": "Dict", + "meta": { + "title": "字典管理", + "icon": "dict", + "hidden": false, + "keepAlive": true, + "alwaysShow": false, + "params": null + } + } + ] + }, + { + "path": "/component", + "component": "Layout", + "name": "/component", + "meta": { + "title": "组件封装", + "icon": "menu", + "hidden": false, + "alwaysShow": false, + "params": null + }, + "children": [ + { + "path": "curd", + "component": "demo/curd/index", + "name": "Curd", + "meta": { + "title": "增删改查", + "icon": "", + "hidden": false, + "keepAlive": true, + "alwaysShow": false, + "params": null + } + }, + { + "path": "table-select", + "component": "demo/table-select/index", + "name": "TableSelect", + "meta": { + "title": "列表选择器", + "icon": "", + "hidden": false, + "keepAlive": true, + "alwaysShow": false, + "params": null + } + }, + { + "path": "wang-editor", + "component": "demo/wang-editor", + "name": "WangEditor", + "meta": { + "title": "富文本编辑器", + "icon": "", + "hidden": false, + "keepAlive": true, + "alwaysShow": false, + "params": null + } + }, + { + "path": "upload", + "component": "demo/upload", + "name": "Upload", + "meta": { + "title": "图片上传", + "icon": "", + "hidden": false, + "keepAlive": true, + "alwaysShow": false, + "params": null + } + }, + { + "path": "icon-selector", + "component": "demo/icon-selector", + "name": "IconSelector", + "meta": { + "title": "图标选择器", + "icon": "", + "hidden": false, + "keepAlive": true, + "alwaysShow": false, + "params": null + } + }, + { + "path": "dict-demo", + "component": "demo/dict", + "name": "DictDemo", + "meta": { + "title": "字典组件", + "icon": "", + "hidden": false, + "keepAlive": true, + "alwaysShow": false, + "params": null + } + } + ] + }, + { + "path": "/route-param", + "component": "Layout", + "name": "/routeParam", + "meta": { + "title": "路由参数", + "icon": "el-icon-ElementPlus", + "hidden": false, + "alwaysShow": true, + "params": null + }, + "children": [ + { + "path": "route-param-type1", + "component": "demo/route-param", + "name": "RouteParamType1", + "meta": { + "title": "参数(type=1)", + "icon": "el-icon-Star", + "hidden": false, + "keepAlive": true, + "alwaysShow": false, + "params": { + "type": "1" + } + } + }, + { + "path": "route-param-type2", + "component": "demo/route-param", + "name": "RouteParamType2", + "meta": { + "title": "参数(type=2)", + "icon": "el-icon-StarFilled", + "hidden": false, + "keepAlive": true, + "alwaysShow": false, + "params": { + "type": "2" + } + } + } + ] + }, + { + "path": "/function", + "component": "Layout", + "name": "/function", + "meta": { + "title": "功能演示", + "icon": "menu", + "hidden": false, + "alwaysShow": false, + "params": null + }, + "children": [ + { + "path": "icon-demo", + "component": "demo/icons", + "name": "IconDemo", + "meta": { + "title": "Icons", + "icon": "el-icon-Notification", + "hidden": false, + "keepAlive": true, + "alwaysShow": false, + "params": null + } + }, + { + "path": "/function/websocket", + "component": "demo/websocket", + "name": "/function/websocket", + "meta": { + "title": "Websocket", + "icon": "", + "hidden": false, + "keepAlive": true, + "alwaysShow": false, + "params": null + } + }, + { + "path": "other/:id", + "component": "demo/other", + "name": "Other/:id", + "meta": { + "title": "敬请期待...", + "icon": "", + "hidden": false, + "alwaysShow": false, + "params": null + } + } + ] + } +] diff --git a/src/json/menus.json b/src/json/menus.json new file mode 100644 index 0000000..fe51488 --- /dev/null +++ b/src/json/menus.json @@ -0,0 +1 @@ +[] diff --git a/src/lang/index.ts b/src/lang/index.ts new file mode 100644 index 0000000..ec3e6cc --- /dev/null +++ b/src/lang/index.ts @@ -0,0 +1,31 @@ +import type { App } from "vue"; +import { createI18n } from "vue-i18n"; +import { useAppStoreHook } from "@/store/modules/app"; +// 本地语言包 +import enLocale from "./package/en"; +import zhCnLocale from "./package/zh-cn"; + +const appStore = useAppStoreHook(); + +const messages = { + "zh-cn": { + ...zhCnLocale, + }, + en: { + ...enLocale, + }, +}; + +const i18n = createI18n({ + legacy: false, + locale: appStore.language, + messages: messages, + globalInjection: true, +}); + +// 全局注册 i18n +export function setupI18n(app: App) { + app.use(i18n); +} + +export default i18n; diff --git a/src/lang/package/en.ts b/src/lang/package/en.ts new file mode 100644 index 0000000..cd986b7 --- /dev/null +++ b/src/lang/package/en.ts @@ -0,0 +1,54 @@ +export default { + // 路由国际化 + route: { + dashboard: "Dashboard", + document: "Document", + }, + // 登录页面国际化 + login: { + phone: "Phone", + code: "Code", + login: "Login", + message: { + phone: { + required: "Please enter Phone", + }, + code: { + required: "Please enter Code", + min: "The code can not be less than 4 digits", + }, + }, + }, + // 导航栏国际化 + navbar: { + dashboard: "Dashboard", + logout: "Logout", + document: "Document", + gitee: "Gitee", + }, + sizeSelect: { + tooltip: "Layout Size", + default: "Default", + large: "Large", + small: "Small", + message: { + success: "Switch Layout Size Successful!", + }, + }, + langSelect: { + message: { + success: "Switch Language Successful!", + }, + }, + settings: { + project: "Project Settings", + theme: "Theme", + interface: "Interface", + navigation: "Navigation", + themeColor: "Theme Color", + tagsView: "Tags View", + fixedHeader: "Fixed Header", + sidebarLogo: "Sidebar Logo", + watermark: "Watermark", + }, +}; diff --git a/src/lang/package/zh-cn.ts b/src/lang/package/zh-cn.ts new file mode 100644 index 0000000..8541cb4 --- /dev/null +++ b/src/lang/package/zh-cn.ts @@ -0,0 +1,54 @@ +export default { + // 路由国际化 + route: { + dashboard: "首页", + document: "项目文档", + }, + // 登录页面国际化 + login: { + phone: "用户名", + code: "密码", + login: "登 录", + message: { + phone: { + required: "请输入手机号", + }, + code: { + required: "请输入验证码", + min: "验证码不能少于4位", + }, + }, + }, + // 导航栏国际化 + navbar: { + dashboard: "首页", + logout: "注销登出", + document: "项目文档", + gitee: "项目地址", + }, + sizeSelect: { + tooltip: "布局大小", + default: "默认", + large: "大型", + small: "小型", + message: { + success: "切换布局大小成功!", + }, + }, + langSelect: { + message: { + success: "切换语言成功!", + }, + }, + settings: { + project: "项目配置", + theme: "主题设置", + interface: "界面设置", + navigation: "导航设置", + themeColor: "主题颜色", + tagsView: "开启 Tags-View", + fixedHeader: "固定 Header", + sidebarLogo: "侧边栏 Logo", + watermark: "开启水印", + }, +}; diff --git a/src/layout/components/AppMain/index.vue b/src/layout/components/AppMain/index.vue new file mode 100644 index 0000000..d480f1a --- /dev/null +++ b/src/layout/components/AppMain/index.vue @@ -0,0 +1,37 @@ + + + + + diff --git a/src/layout/components/NavBar/components/NavbarAction.vue b/src/layout/components/NavBar/components/NavbarAction.vue new file mode 100644 index 0000000..ff03d78 --- /dev/null +++ b/src/layout/components/NavBar/components/NavbarAction.vue @@ -0,0 +1,214 @@ + + + diff --git a/src/layout/components/NavBar/index.vue b/src/layout/components/NavBar/index.vue new file mode 100644 index 0000000..c985c0b --- /dev/null +++ b/src/layout/components/NavBar/index.vue @@ -0,0 +1,33 @@ + + + + + diff --git a/src/layout/components/Settings/components/LayoutSelect.vue b/src/layout/components/Settings/components/LayoutSelect.vue new file mode 100644 index 0000000..0b880c1 --- /dev/null +++ b/src/layout/components/Settings/components/LayoutSelect.vue @@ -0,0 +1,108 @@ + + + + + diff --git a/src/layout/components/Settings/components/ThemeColorPicker.vue b/src/layout/components/Settings/components/ThemeColorPicker.vue new file mode 100644 index 0000000..5a0d59c --- /dev/null +++ b/src/layout/components/Settings/components/ThemeColorPicker.vue @@ -0,0 +1,41 @@ + + + + + diff --git a/src/layout/components/Settings/index.vue b/src/layout/components/Settings/index.vue new file mode 100644 index 0000000..ad3782c --- /dev/null +++ b/src/layout/components/Settings/index.vue @@ -0,0 +1,138 @@ + + + + + diff --git a/src/layout/components/Sidebar/components/SidebarLogo.vue b/src/layout/components/Sidebar/components/SidebarLogo.vue new file mode 100644 index 0000000..90fc081 --- /dev/null +++ b/src/layout/components/Sidebar/components/SidebarLogo.vue @@ -0,0 +1,64 @@ + + + + + diff --git a/src/layout/components/Sidebar/components/SidebarMenu.vue b/src/layout/components/Sidebar/components/SidebarMenu.vue new file mode 100644 index 0000000..418c9cd --- /dev/null +++ b/src/layout/components/Sidebar/components/SidebarMenu.vue @@ -0,0 +1,66 @@ + + + + diff --git a/src/layout/components/Sidebar/components/SidebarMenuItem.vue b/src/layout/components/Sidebar/components/SidebarMenuItem.vue new file mode 100644 index 0000000..e45ec8d --- /dev/null +++ b/src/layout/components/Sidebar/components/SidebarMenuItem.vue @@ -0,0 +1,202 @@ + + + + diff --git a/src/layout/components/Sidebar/components/SidebarMenuItemTitle.vue b/src/layout/components/Sidebar/components/SidebarMenuItemTitle.vue new file mode 100644 index 0000000..b05daf8 --- /dev/null +++ b/src/layout/components/Sidebar/components/SidebarMenuItemTitle.vue @@ -0,0 +1,49 @@ + + + + + diff --git a/src/layout/components/Sidebar/components/SidebarMixTopMenu.vue b/src/layout/components/Sidebar/components/SidebarMixTopMenu.vue new file mode 100644 index 0000000..d52fff9 --- /dev/null +++ b/src/layout/components/Sidebar/components/SidebarMixTopMenu.vue @@ -0,0 +1,88 @@ + + + + diff --git a/src/layout/components/Sidebar/index.vue b/src/layout/components/Sidebar/index.vue new file mode 100644 index 0000000..59c6b6b --- /dev/null +++ b/src/layout/components/Sidebar/index.vue @@ -0,0 +1,38 @@ + + + + + diff --git a/src/layout/components/TagsView/index.vue b/src/layout/components/TagsView/index.vue new file mode 100644 index 0000000..031d864 --- /dev/null +++ b/src/layout/components/TagsView/index.vue @@ -0,0 +1,448 @@ + + + + + diff --git a/src/layout/index.vue b/src/layout/index.vue new file mode 100644 index 0000000..31c701e --- /dev/null +++ b/src/layout/index.vue @@ -0,0 +1,303 @@ + + + + + diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..ffa9ee0 --- /dev/null +++ b/src/main.ts @@ -0,0 +1,25 @@ +import { createApp } from "vue"; +import App from "./App.vue"; +import setupPlugins from "@/plugins"; +// 持久化插件 +import persist from "pinia-plugin-persistedstate"; + +// 本地SVG图标 +import "virtual:svg-icons-register"; + +// 样式 +import "element-plus/theme-chalk/dark/css-vars.css"; +import "@/styles/index.scss"; +import "uno.css"; +import "animate.css"; + +import LicensePlate from "vue3-license-plate"; +import "vue3-license-plate/lib/licensePlate.css"; + +import "@/assets/font_4666703_de1boqvna1d/iconfont.css"; +const app = createApp(App); +app.use(setupPlugins); +app.use(LicensePlate); +app.use(createPinia().use(persist)); + +app.mount("#app"); diff --git a/src/plugins/icons.ts b/src/plugins/icons.ts new file mode 100644 index 0000000..fa85ba1 --- /dev/null +++ b/src/plugins/icons.ts @@ -0,0 +1,9 @@ +import type { App } from "vue"; +import * as ElementPlusIconsVue from "@element-plus/icons-vue"; + +// 注册所有图标 +export function setupElIcons(app: App) { + for (const [key, component] of Object.entries(ElementPlusIconsVue)) { + app.component(key, component); + } +} diff --git a/src/plugins/index.ts b/src/plugins/index.ts new file mode 100644 index 0000000..65262c8 --- /dev/null +++ b/src/plugins/index.ts @@ -0,0 +1,24 @@ +import { setupDirective } from "@/directive"; +import { setupI18n } from "@/lang"; +import { setupRouter } from "@/router"; +import { setupStore } from "@/store"; +import type { App } from "vue"; +import { setupElIcons } from "./icons"; +import { setupPermission } from "./permission"; + +export default { + install(app: App) { + // 自定义指令(directive) + setupDirective(app); + // 路由(router) + setupRouter(app); + // 状态管理(store) + setupStore(app); + // 国际化 + setupI18n(app); + // Element-plus图标 + setupElIcons(app); + // 路由守卫 + setupPermission(); + }, +}; diff --git a/src/plugins/permission.ts b/src/plugins/permission.ts new file mode 100644 index 0000000..beafd31 --- /dev/null +++ b/src/plugins/permission.ts @@ -0,0 +1,106 @@ +import { + NavigationGuardNext, + RouteLocationNormalized, + RouteRecordRaw, +} from "vue-router"; + +import NProgress from "@/utils/nprogress"; +import { TOKEN_KEY } from "@/enums/CacheEnum"; +import router from "@/router"; +import { usePermissionStore, useUserStore } from "@/store"; +export function setupPermission() { + // 白名单路由 + const whiteList = ["/login"]; + + router.beforeEach(async (to, from, next) => { + NProgress.start(); + const hasToken = localStorage.getItem(TOKEN_KEY); + + if (hasToken) { + if (to.path === "/login") { + // 如果已登录,跳转到首页 + next({ path: "/" }); + NProgress.done(); + } else { + const userStore = useUserStore(); + + await userStore.getUserInfo(); + const permissionStore = usePermissionStore(); + + const dynamicRoutes = await permissionStore.generateRoutes(); + dynamicRoutes.forEach((route: RouteRecordRaw) => + router.addRoute(route) + ); + + next(); + + // if (permissionStore.routes.length === 0) { + // const dynamicRoutes = await permissionStore.generateRoutes(); + // dynamicRoutes.forEach((route: RouteRecordRaw) => + // router.addRoute(route) + // ); + // next({ ...to, replace: true }); + // } else { + // // 如果未匹配到任何路由,跳转到404页面 + // if (to.matched.length === 0) { + // next(from.name ? { name: from.name } : "/404"); + // } else { + // // 如果路由参数中有 title,覆盖路由元信息中的 title + // const title = + // (to.params.title as string) || (to.query.title as string); + // if (title) { + // to.meta.title = title; + // } + // next(); + // } + // } + } + } else { + // 未登录 + if (whiteList.includes(to.path)) { + next(); // 在白名单,直接进入 + } else { + // 不在白名单,重定向到登录页 + redirectToLogin(to, next); + NProgress.done(); + } + } + }); + + router.afterEach(() => { + NProgress.done(); + }); +} + +/** 重定向到登录页 */ +function redirectToLogin( + to: RouteLocationNormalized, + next: NavigationGuardNext +) { + const params = new URLSearchParams(to.query as Record); + const queryString = params.toString(); + const redirect = queryString ? `${to.path}?${queryString}` : to.path; + next(`/login?redirect=${encodeURIComponent(redirect)}`); +} + +/** 判断是否有权限 */ +export function hasAuth( + value: string | string[], + type: "button" | "role" = "button" +) { + const { roleList, perm } = useUserStore().user; + + if (roleList.length === 0) { + ElMessage.error("当前用户未配置任何权限"); + return false; + } + // 超级管理员 拥有所有权限 + if (type === "button" && roleList[0].roleCode.includes("ROOT")) { + return true; + } + + const auths = type === "button" ? perm : roleList; + return typeof value === "string" + ? auths.includes(value) + : value.some((perm) => auths.includes(perm)); +} diff --git a/src/router/index.ts b/src/router/index.ts new file mode 100644 index 0000000..4b10c47 --- /dev/null +++ b/src/router/index.ts @@ -0,0 +1,138 @@ +import type { App } from "vue"; +import { createRouter, createWebHashHistory, RouteRecordRaw } from "vue-router"; + +export const Layout = () => import("@/layout/index.vue"); +import { usePermissionStore, useUserStore } from "@/store"; + +// 静态路由 +export const constantRoutes: RouteRecordRaw[] = [ + { + path: "/redirect", + component: Layout, + meta: { hidden: true }, + children: [ + { + path: "/redirect/:path(.*)", + component: () => import("@/views/redirect/index.vue"), + }, + ], + }, + + { + path: "/login", + component: () => import("@/views/login/index.vue"), + meta: { hidden: true }, + }, + + { + path: "/", + name: "/", + component: Layout, + redirect: "/dashboard", + children: [ + { + path: "dashboard", + component: () => import("@/views/dashboard/index.vue"), + // 用于 keep-alive 功能,需要与 SFC 中自动推导或显式声明的组件名称一致 + // 参考文档: https://cn.vuejs.org/guide/built-ins/keep-alive.html#include-exclude + name: "Dashboard", + meta: { + title: "dashboard", + icon: "homepage", + affix: true, + keepAlive: true, + hidden: true, + }, + }, + { + path: "401", + component: () => import("@/views/error-page/401.vue"), + meta: { hidden: true }, + }, + { + path: "404", + component: () => import("@/views/error-page/404.vue"), + meta: { hidden: true }, + }, + ], + }, + + // 外部链接 + // { + // path: "/external-link", + // component: Layout, + // children: [ { + // component: () => import("@/views/external-link/index.vue"), + // path: "https://www.cnblogs.com/haoxianrui/", + // meta: { title: "外部链接", icon: "link" }, + // }, + // ], + // }, + // 多级嵌套路由 + /* { + path: '/nested', + component: Layout, + redirect: '/nested/level1/level2', + name: 'Nested', + meta: {title: '多级菜单', icon: 'nested'}, + children: [ + { + path: 'level1', + component: () => import('@/views/nested/level1/index.vue'), + name: 'Level1', + meta: {title: '菜单一级'}, + redirect: '/nested/level1/level2', + children: [ + { + path: 'level2', + component: () => import('@/views/nested/level1/level2/index.vue'), + name: 'Level2', + meta: {title: '菜单二级'}, + redirect: '/nested/level1/level2/level3', + children: [ + { + path: 'level3-1', + component: () => import('@/views/nested/level1/level2/level3/index1.vue'), + name: 'Level3-1', + meta: {title: '菜单三级-1'} + }, + { + path: 'level3-2', + component: () => import('@/views/nested/level1/level2/level3/index2.vue'), + name: 'Level3-2', + meta: {title: '菜单三级-2'} + } + ] + } + ] + }, + ] + }*/ +]; + +/** + * 创建路由 + */ +const router = createRouter({ + history: createWebHashHistory(), + routes: constantRoutes, + // 刷新时,滚动条位置还原 + scrollBehavior: () => ({ left: 0, top: 0 }), +}); + +// 全局注册 router +export function setupRouter(app: App) { + // const permissionStore = usePermissionStore(); + // const dynamicRoutes = permissionStore.routes; + // dynamicRoutes.forEach((route: RouteRecordRaw) => router.addRoute(route)); + app.use(router); +} + +/** + * 重置路由 + */ +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..e25fef2 --- /dev/null +++ b/src/settings.ts @@ -0,0 +1,26 @@ +import { SizeEnum } from "./enums/SizeEnum"; +import { LayoutEnum } from "./enums/LayoutEnum"; +import { ThemeEnum } from "./enums/ThemeEnum"; +import { LanguageEnum } from "./enums/LanguageEnum"; + +const { pkg } = __APP_INFO__; + +const mediaQueryList = window.matchMedia("(prefers-color-scheme: dark)"); + +const defaultSettings: AppSettings = { + title: pkg.name, + version: pkg.version, + showSettings: true, + tagsView: true, + fixedHeader: true, + sidebarLogo: true, + layout: LayoutEnum.LEFT, + theme: mediaQueryList.matches ? ThemeEnum.DARK : ThemeEnum.LIGHT, + size: SizeEnum.DEFAULT, + language: LanguageEnum.ZH_CN, + themeColor: "#409EFF", + watermarkEnabled: false, + watermarkContent: pkg.name, +}; + +export default defaultSettings; diff --git a/src/store/index.ts b/src/store/index.ts new file mode 100644 index 0000000..8dd6ea9 --- /dev/null +++ b/src/store/index.ts @@ -0,0 +1,16 @@ +import type { App } from "vue"; +import { createPinia } from "pinia"; + +const store = createPinia(); + +// 全局注册 store +export function setupStore(app: App) { + app.use(store); +} + +export * from "./modules/app"; +export * from "./modules/permission"; +export * from "./modules/settings"; +export * from "./modules/tagsView"; +export * from "./modules/user"; +export { store }; diff --git a/src/store/modules/app.ts b/src/store/modules/app.ts new file mode 100644 index 0000000..31db0e7 --- /dev/null +++ b/src/store/modules/app.ts @@ -0,0 +1,109 @@ +import defaultSettings from "@/settings"; + +// 导入 Element Plus 中英文语言包 +import zhCn from "element-plus/es/locale/lang/zh-cn"; +import en from "element-plus/es/locale/lang/en"; +import { store } from "@/store"; +import { DeviceEnum } from "@/enums/DeviceEnum"; +import { SidebarStatusEnum } from "@/enums/SidebarStatusEnum"; + +export const useAppStore = defineStore("app", () => { + // 设备类型 + const device = useStorage("device", DeviceEnum.DESKTOP); + // 布局大小 + const size = useStorage("size", defaultSettings.size); + // 语言 + const language = useStorage("language", defaultSettings.language); + // 侧边栏状态 + const sidebarStatus = useStorage("sidebarStatus", SidebarStatusEnum.CLOSED); + const sidebar = reactive({ + opened: sidebarStatus.value === SidebarStatusEnum.OPENED, + withoutAnimation: false, + }); + + // 顶部菜单激活路径 + const activeTopMenuPath = useStorage("activeTopMenuPath", ""); + + /** + * 根据语言标识读取对应的语言包 + */ + const locale = computed(() => { + if (language?.value == "en") { + return en; + } else { + return zhCn; + } + }); + + // 切换侧边栏 + function toggleSidebar() { + sidebar.opened = !sidebar.opened; + sidebarStatus.value = sidebar.opened + ? SidebarStatusEnum.OPENED + : SidebarStatusEnum.CLOSED; + } + + // 关闭侧边栏 + function closeSideBar() { + sidebar.opened = false; + sidebarStatus.value = SidebarStatusEnum.CLOSED; + } + + // 打开侧边栏 + function openSideBar() { + sidebar.opened = true; + sidebarStatus.value = SidebarStatusEnum.OPENED; + } + + // 切换设备 + function toggleDevice(val: string) { + device.value = val; + } + + /** + * 改变布局大小 + * + * @param val 布局大小 default | small | large + */ + function changeSize(val: string) { + size.value = val; + } + /** + * 切换语言 + * + * @param val + */ + function changeLanguage(val: string) { + language.value = val; + } + /** + * 混合模式顶部切换 + */ + function activeTopMenu(val: string) { + activeTopMenuPath.value = val; + } + return { + device, + sidebar, + language, + locale, + size, + activeTopMenu, + toggleDevice, + changeSize, + changeLanguage, + toggleSidebar, + closeSideBar, + openSideBar, + activeTopMenuPath, + }; +}); + +/** + * 用于在组件外部(如在Pinia Store 中)使用 Pinia 提供的 store 实例。 + * 官方文档解释了如何在组件外部使用 Pinia Store: + * https://pinia.vuejs.org/core-concepts/outside-component-usage.html#using-a-store-outside-of-a-component + */ +export function useAppStoreHook() { + return useAppStore(store); +} diff --git a/src/store/modules/permission.ts b/src/store/modules/permission.ts new file mode 100644 index 0000000..80ddbea --- /dev/null +++ b/src/store/modules/permission.ts @@ -0,0 +1,103 @@ +import { RouteRecordRaw } from "vue-router"; +import { constantRoutes } from "@/router"; +import { store } from "@/store"; +import MenuAPI, { RouteVO } from "@/api/menu"; +import router from "@/router"; + +const modules = import.meta.glob("../../views/**/**.vue"); +const Layout = () => import("@/layout/index.vue"); +import menusJson from "@/json/menus.json"; +const menus = menusJson; + +export const usePermissionStore = defineStore("permission", () => { + /** + * 应用中所有的路由列表,包括静态路由和动态路由 + */ + // const routes = ref([]); + const routes = ref( + constantRoutes.concat(transformRoutes(menus as any)) + ); + + /** + * 混合模式左侧菜单列表 + */ + const mixLeftMenus = ref([]); + + /** + * 生成动态路由 + */ + function generateRoutes() { + return new Promise((resolve, reject) => { + const dynamicRoutes = transformRoutes(menus); + routes.value = constantRoutes.concat(dynamicRoutes); + resolve(dynamicRoutes); + // MenuAPI.getRoutes() + // .then((data) => { + // const dynamicRoutes = transformRoutes(data); + // routes.value = constantRoutes.concat(dynamicRoutes); + // resolve(dynamicRoutes); + // }) + // .catch((error) => { + // reject(error); + // }); + }); + } + + /** + * 混合模式菜单下根据顶部菜单路径设置左侧菜单 + * + * @param topMenuPath - 顶部菜单路径 + */ + const setMixLeftMenus = (topMenuPath: string) => { + const matchedItem = routes.value.find((item) => item.path === topMenuPath); + if (matchedItem && matchedItem.children) { + mixLeftMenus.value = matchedItem.children; + } + }; + + return { + routes, + generateRoutes, + mixLeftMenus, + setMixLeftMenus, + }; +}); + +/** + * 转换路由数据为组件 + */ +const transformRoutes = (routes: RouteVO[]) => { + const asyncRoutes: RouteRecordRaw[] = []; + routes.forEach((route) => { + const tmpRoute = { ...route } as RouteRecordRaw; + // 顶级目录,替换为 Layout 组件 + if (tmpRoute.component?.toString() == "Layout") { + tmpRoute.component = Layout; + } else { + // 其他菜单,根据组件路径动态加载组件 + const component = modules[`../../views/${tmpRoute.component}.vue`]; + if (component) { + tmpRoute.component = component; + } else { + tmpRoute.component = modules[`../../views/error-page/404.vue`]; + } + } + + if (tmpRoute.children) { + tmpRoute.children = transformRoutes(route.children); + } + + asyncRoutes.push(tmpRoute); + }); + + return asyncRoutes; +}; + +/** + * 用于在组件外部(如在Pinia Store 中)使用 Pinia 提供的 store 实例。 + * 官方文档解释了如何在组件外部使用 Pinia Store: + * https://pinia.vuejs.org/core-concepts/outside-component-usage.html#using-a-store-outside-of-a-component + */ +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..a581e90 --- /dev/null +++ b/src/store/modules/settings.ts @@ -0,0 +1,125 @@ +import defaultSettings from "@/settings"; +import { ThemeEnum } from "@/enums/ThemeEnum"; +import Color from "color"; + +type SettingsValue = boolean | string; + +export const useSettingsStore = defineStore("setting", () => { + // 是否显示设置 + const settingsVisible = ref(false); + // 是否显示标签视图 + const tagsView = useStorage("tagsView", defaultSettings.tagsView); + // 是否显示侧边栏logo + const sidebarLogo = useStorage( + "sidebarLogo", + defaultSettings.sidebarLogo + ); + // 是否固定头部 + const fixedHeader = useStorage( + "fixedHeader", + defaultSettings.fixedHeader + ); + // 布局模式:left-左侧模式(默认) top-顶部模式 mix-混合模式 + const layout = useStorage("layout", defaultSettings.layout); + // 主题颜色 + const themeColor = useStorage( + "themeColor", + defaultSettings.themeColor + ); + // 主题:light-亮色(默认) dark-暗色 + const theme = useStorage("theme", defaultSettings.theme); + // 是否开启水印 + const watermarkEnabled = useStorage( + "watermarkEnabled", + defaultSettings.watermarkEnabled + ); + + watch( + [theme, themeColor], + ([newTheme, newThemeColor], [oldTheme, oldThemeColor]) => { + if (newTheme !== oldTheme) { + if (newTheme === ThemeEnum.DARK) { + document.documentElement.classList.add("dark"); + } else { + document.documentElement.classList.remove("dark"); + } + } + + if (newThemeColor !== oldThemeColor) { + const rootStyle = document.documentElement.style; + rootStyle.setProperty(`--el-color-primary`, newThemeColor); + rootStyle.setProperty(`--el-color-primary-dark-2`, newThemeColor); + + for (let i = 1; i < 10; i++) { + rootStyle.setProperty( + `--el-color-primary-light-${i}`, + `${Color(newThemeColor).alpha(1 - i * 0.1)}` + ); + } + } + }, + { + immediate: true, // 立即执行,确保在侦听器创建时执行一次 + } + ); + + const settingsMap: Record> = { + fixedHeader, + tagsView, + sidebarLogo, + layout, + watermarkEnabled, + }; + + function changeSetting({ + key, + value, + }: { + key: string; + value: SettingsValue; + }) { + const setting = settingsMap[key]; + if (setting) { + setting.value = value; + } + } + + /** + * 切换主题 + */ + function changeTheme(val: string) { + theme.value = val; + } + + /** + * 切换主题颜色 + * + * @param color 主题颜色 + * + */ + function changeThemeColor(color: string) { + themeColor.value = color; + } + + /** + * 切换布局 + */ + function changeLayout(val: string) { + layout.value = val; + } + + return { + settingsVisible, + tagsView, + fixedHeader, + sidebarLogo, + layout, + themeColor, + theme, + watermarkEnabled, + changeSetting, + changeTheme, + changeThemeColor, + changeLayout, + }; +}); diff --git a/src/store/modules/tagsView.ts b/src/store/modules/tagsView.ts new file mode 100644 index 0000000..a92e4fe --- /dev/null +++ b/src/store/modules/tagsView.ts @@ -0,0 +1,253 @@ +export const useTagsViewStore = defineStore("tagsView", () => { + const visitedViews = ref([]); + const cachedViews = ref([]); + const router = useRouter(); + const route = useRoute(); + /** + * 添加已访问视图到已访问视图列表中 + */ + function addVisitedView(view: TagView) { + // 如果已经存在于已访问的视图列表中,则不再添加 + if (visitedViews.value.some((v) => v.path === view.path)) { + return; + } + // 如果视图是固定的(affix),则在已访问的视图列表的开头添加 + if (view.affix) { + visitedViews.value.unshift(view); + } else { + // 如果视图不是固定的,则在已访问的视图列表的末尾添加 + visitedViews.value.push(view); + } + } + + /** + * 添加缓存视图到缓存视图列表中 + */ + function addCachedView(view: TagView) { + const viewName = view.name; + // 如果缓存视图名称已经存在于缓存视图列表中,则不再添加 + if (cachedViews.value.includes(viewName)) { + return; + } + + // 如果视图需要缓存(keepAlive),则将其路由名称添加到缓存视图列表中 + if (view.keepAlive) { + cachedViews.value.push(viewName); + } + } + + /** + * 从已访问视图列表中删除指定的视图 + */ + function delVisitedView(view: TagView) { + return new Promise((resolve) => { + for (const [i, v] of visitedViews.value.entries()) { + // 找到与指定视图路径匹配的视图,在已访问视图列表中删除该视图 + if (v.path === view.path) { + visitedViews.value.splice(i, 1); + break; + } + } + resolve([...visitedViews.value]); + }); + } + + function delCachedView(view: TagView) { + const viewName = view.name; + return new Promise((resolve) => { + const index = cachedViews.value.indexOf(viewName); + index > -1 && cachedViews.value.splice(index, 1); + resolve([...cachedViews.value]); + }); + } + + function delOtherVisitedViews(view: TagView) { + return new Promise((resolve) => { + visitedViews.value = visitedViews.value.filter((v) => { + return v?.affix || v.path === view.path; + }); + resolve([...visitedViews.value]); + }); + } + + function delOtherCachedViews(view: TagView) { + const viewName = view.name as string; + return new Promise((resolve) => { + const index = cachedViews.value.indexOf(viewName); + if (index > -1) { + cachedViews.value = cachedViews.value.slice(index, index + 1); + } else { + // if index = -1, there is no cached tags + cachedViews.value = []; + } + resolve([...cachedViews.value]); + }); + } + + function updateVisitedView(view: TagView) { + for (let v of visitedViews.value) { + if (v.path === view.path) { + v = Object.assign(v, view); + break; + } + } + } + + function addView(view: TagView) { + addVisitedView(view); + addCachedView(view); + } + + function delView(view: TagView) { + return new Promise((resolve) => { + delVisitedView(view); + delCachedView(view); + resolve({ + visitedViews: [...visitedViews.value], + cachedViews: [...cachedViews.value], + }); + }); + } + + function delOtherViews(view: TagView) { + return new Promise((resolve) => { + delOtherVisitedViews(view); + delOtherCachedViews(view); + resolve({ + visitedViews: [...visitedViews.value], + cachedViews: [...cachedViews.value], + }); + }); + } + + function delLeftViews(view: TagView) { + return new Promise((resolve) => { + const currIndex = visitedViews.value.findIndex( + (v) => v.path === view.path + ); + if (currIndex === -1) { + return; + } + visitedViews.value = visitedViews.value.filter((item, index) => { + if (index >= currIndex || item?.affix) { + return true; + } + + const cacheIndex = cachedViews.value.indexOf(item.name); + if (cacheIndex > -1) { + cachedViews.value.splice(cacheIndex, 1); + } + return false; + }); + resolve({ + visitedViews: [...visitedViews.value], + }); + }); + } + function delRightViews(view: TagView) { + return new Promise((resolve) => { + const currIndex = visitedViews.value.findIndex( + (v) => v.path === view.path + ); + if (currIndex === -1) { + return; + } + visitedViews.value = visitedViews.value.filter((item, index) => { + if (index <= currIndex || item?.affix) { + return true; + } + }); + resolve({ + visitedViews: [...visitedViews.value], + }); + }); + } + + function delAllViews() { + return new Promise((resolve) => { + const affixTags = visitedViews.value.filter((tag) => tag?.affix); + visitedViews.value = affixTags; + cachedViews.value = []; + resolve({ + visitedViews: [...visitedViews.value], + cachedViews: [...cachedViews.value], + }); + }); + } + + function delAllVisitedViews() { + return new Promise((resolve) => { + const affixTags = visitedViews.value.filter((tag) => tag?.affix); + visitedViews.value = affixTags; + resolve([...visitedViews.value]); + }); + } + + function delAllCachedViews() { + return new Promise((resolve) => { + cachedViews.value = []; + resolve([...cachedViews.value]); + }); + } + + /** + * 关闭当前tagView + */ + function closeCurrentView() { + const tags: TagView = { + name: route.name as string, + title: route.meta.title as string, + path: route.path, + fullPath: route.fullPath, + affix: route.meta?.affix, + keepAlive: route.meta?.keepAlive, + query: route.query, + }; + delView(tags).then((res: any) => { + if (isActive(tags)) { + toLastView(res.visitedViews, tags); + } + }); + } + function isActive(tag: TagView) { + return tag.path === route.path; + } + + function toLastView(visitedViews: TagView[], view?: TagView) { + const latestView = visitedViews.slice(-1)[0]; + if (latestView && latestView.fullPath) { + router.push(latestView.fullPath); + } else { + // now the default is to redirect to the home page if there is no tags-view, + // you can adjust it according to your needs. + if (view?.name === "Dashboard") { + // to reload home page + router.replace("/redirect" + view.fullPath); + } else { + router.push("/"); + } + } + } + return { + visitedViews, + cachedViews, + addVisitedView, + addCachedView, + delVisitedView, + delCachedView, + delOtherVisitedViews, + delOtherCachedViews, + updateVisitedView, + addView, + delView, + delOtherViews, + delLeftViews, + delRightViews, + delAllViews, + delAllVisitedViews, + delAllCachedViews, + closeCurrentView, + isActive, + toLastView, + }; +}); diff --git a/src/store/modules/user.ts b/src/store/modules/user.ts new file mode 100644 index 0000000..2cfc8c0 --- /dev/null +++ b/src/store/modules/user.ts @@ -0,0 +1,104 @@ +import AuthAPI, { LoginData } from "@/api/auth"; +import {} from "@/api/user"; +import router, { resetRouter } from "@/router"; +import { store } from "@/store"; +import UserAPI from "@/api/user"; + +import { TOKEN_KEY, CODE_KEY } from "@/enums/CacheEnum"; + +export const useUserStore = defineStore( + "user", + () => { + const user = ref(); + /** + * 登录 + * + * @param {LoginData} + * @returns + */ + function login(loginData: LoginData) { + return new Promise((resolve, reject) => { + AuthAPI.login(loginData) + .then((data) => { + const { token } = data; + localStorage.setItem(TOKEN_KEY, token); // Bearer eyJhbGciOiJIUzI1NiJ9.xxx.xxx + user.value = data; + resolve(); + }) + .catch((error) => { + reject(error); + }); + }); + } + + function getUserInfo() { + return new Promise((resolve, reject) => { + UserAPI.getUserInfo() + .then((data) => { + if (!data) { + reject("Verification failed, please Login again."); + return; + } + if (!data.roleList || data.roleList.length <= 0) { + reject("getUserInfo: roles must be a non-null array!"); + return; + } + Object.assign(user.value, { ...data }); + resolve(data); + }) + .catch((error) => { + resetToken(); + reject(error); + }); + }); + } + + // user logout + function logout() { + return new Promise((resolve, reject) => { + AuthAPI.logout() + .then(() => { + localStorage.setItem(TOKEN_KEY, ""); + localStorage.setItem(CODE_KEY, ""); + location.reload(); // 清空路由 + resolve(); + }) + .catch((error) => { + reject(error); + }); + }); + } + + // remove token + function resetToken() { + console.log("resetToken"); + return new Promise((resolve) => { + localStorage.setItem(TOKEN_KEY, ""); + localStorage.setItem(CODE_KEY, ""); + resetRouter(); + resolve(); + }); + } + + return { + user, + login, + getUserInfo, + logout, + resetToken, + }; + }, + { + // 配置持久化 + persist: true, + } +); + +/** + * 用于在组件外部(如在Pinia Store 中)使用 Pinia 提供的 store 实例。 + * 官方文档解释了如何在组件外部使用 Pinia Store: + * https://pinia.vuejs.org/core-concepts/outside-component-usage.html#using-a-store-outside-of-a-component + */ +export function useUserStoreHook() { + return useUserStore(store); +} diff --git a/src/styles/index.scss b/src/styles/index.scss new file mode 100644 index 0000000..6bc9497 --- /dev/null +++ b/src/styles/index.scss @@ -0,0 +1,87 @@ +@use "./reset"; + +.app-container { + padding: 15px; +} + +.search-container { + padding: 18px 0 0 10px; + margin-bottom: 10px; + background-color: var(--el-bg-color-overlay); + border: 1px solid var(--el-border-color-light); + border-radius: 4px; + box-shadow: var(--el-box-shadow-light); +} + +.table-container > .el-card__header { + padding: calc(var(--el-card-padding) - 8px) var(--el-card-padding); +} + +.link-type, +.link-type:focus { + color: #337ab7; + cursor: pointer; + + &:hover { + color: rgb(32 160 255); + } +} + +.container-title-first { + padding: 10px 0; + font-size: 16px; + font-weight: 500; +} + +.el-drawer__header { + margin-bottom: 0 !important; +} + +.f-s-14 { + font-size: 14px; +} + +.l-h-30 { + line-height: 30px; +} + +/* +车牌号样式 +*/ +.license-plate_container { + min-width: 275px; + + .input_container { + .input_box { + height: 30px !important; + + &:last-of-type { + border-style: solid !important; + } + } + + .input_box + .input_box { + margin-left: 5px !important; + } + } + + .button { + padding: 0 !important; + font-size: 12px !important; + } +} + +/* +表单样式 +*/ +.form-container { + padding: 20px; + 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); +} + +.el-upload-dragger { + padding: 10px !important; +} diff --git a/src/styles/login.scss b/src/styles/login.scss new file mode 100644 index 0000000..ede3489 --- /dev/null +++ b/src/styles/login.scss @@ -0,0 +1,81 @@ +.login-container { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + height: 100%; + overflow-y: auto; + background: url("@/assets/images/login-background-light.jpg") no-repeat center + right; + + .top-bar { + position: absolute; + top: 0; + left: 0; + display: flex; + align-items: center; + justify-content: flex-end; + width: 100%; + padding: 10px; + } + + .login-card { + width: 400px; + background: transparent; + border: none; + border-radius: 4%; + + @media (width <= 640px) { + width: 340px; + } + + .input-wrapper { + display: flex; + align-items: center; + width: 100%; + } + + .captcha-image { + height: 48px; + cursor: pointer; + border-top-right-radius: 6px; + border-bottom-right-radius: 6px; + } + } + + .icp-info { + position: absolute; + bottom: 4px; + font-size: 12px; + text-align: center; + } + + .el-form-item { + background: var(--el-input-bg-color); + border: 1px solid var(--el-border-color); + border-radius: 5px; + } + + .el-input { + .el-input__wrapper { + padding: 0; + background-color: transparent; + box-shadow: none; + + &.is-focus, + &:hover { + box-shadow: none !important; + } + + input:-webkit-autofill { + /* 通过延时渲染背景色变相去除背景颜色 */ + transition: background-color 1000s ease-in-out 0s; + } + } + } +} + +html.dark .login-container { + background: url("@/assets/images/login-background-dark.jpg") no-repeat center + right; +} diff --git a/src/styles/reset.scss b/src/styles/reset.scss new file mode 100644 index 0000000..a20f04a --- /dev/null +++ b/src/styles/reset.scss @@ -0,0 +1,76 @@ +*, +::before, +::after { + box-sizing: border-box; + border-color: currentcolor; + border-style: solid; + border-width: 0; +} + +#app { + width: 100%; + height: 100%; +} + +html { + box-sizing: border-box; + width: 100%; + height: 100%; + line-height: 1.5; + tab-size: 4; + text-size-adjust: 100%; +} + +body { + width: 100%; + height: 100%; + margin: 0; + font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", + "Microsoft YaHei", "微软雅黑", Arial, sans-serif; + line-height: inherit; + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + text-rendering: optimizelegibility; +} + +a { + color: inherit; + text-decoration: inherit; +} + +img, +svg { + display: inline-block; +} + +svg { + // 因icon大小被设置为和字体大小一致,而span等标签的下边缘会和字体的基线对齐,故需设置一个往下的偏移比例,来纠正视觉上的未对齐效果 + vertical-align: -0.15em; +} + +ul, +li { + padding: 0; + margin: 0; + list-style: none; +} + +*, +*::before, +*::after { + box-sizing: inherit; +} + +a, +a:focus, +a:hover { + color: inherit; + text-decoration: none; + cursor: pointer; +} + +a:focus, +a:active, +div:focus { + outline: none; +} diff --git a/src/styles/variables.module.scss b/src/styles/variables.module.scss new file mode 100644 index 0000000..20a624d --- /dev/null +++ b/src/styles/variables.module.scss @@ -0,0 +1,11 @@ +/* stylelint-disable property-no-unknown */ +:export { + sidebar-width: $sidebar-width; + navbar-height: $navbar-height; + tags-view-height: $tags-view-height; + menu-background: $menu-background; + menu-text: $menu-text; + menu-active-text: $menu-active-text; + menu-hover: $menu-hover; +} +/* stylelint-enable property-no-unknown */ diff --git a/src/styles/variables.scss b/src/styles/variables.scss new file mode 100644 index 0000000..42f9718 --- /dev/null +++ b/src/styles/variables.scss @@ -0,0 +1,34 @@ +/** 全局SCSS变量 */ + +:root { + --menu-background: #304156; + --menu-text: #bfcbd9; + --menu-active-text: var(--el-menu-active-color); + --menu-hover: #263445; + --sidebar-logo-background: #2d3748; + + // 修复表格 fixed 列被选中后由于透明色导致叠字的 bug + .el-table { + --el-table-current-row-bg-color: rgb(235 243 250); + } +} + +/** 暗黑主题 */ +html.dark { + --menu-background: var(--el-bg-color-overlay); + --menu-text: #fff; + --menu-active-text: var(--el-menu-active-color); + --menu-hover: rgb(0 0 0 / 20%); + --sidebar-logo-background: rgb(0 0 0 / 20%); +} + +$menu-background: var(--menu-background); // 菜单背景色 +$menu-text: var(--menu-text); // 菜单文字颜色 +$menu-active-text: var(--menu-active-text); // 菜单激活文字颜色 +$menu-hover: var(--menu-hover); // 菜单悬停背景色 +$sidebar-logo-background: var(--sidebar-logo-background); // 侧边栏 Logo 背景色 + +$sidebar-width: 210px; // 侧边栏宽度 +$sidebar-width-collapsed: 54px; // 侧边栏收缩宽度 +$navbar-height: 50px; // 导航栏高度 +$tags-view-height: 34px; // TagsView 高度 diff --git a/src/types/auto-imports.d.ts b/src/types/auto-imports.d.ts new file mode 100644 index 0000000..f4a814d --- /dev/null +++ b/src/types/auto-imports.d.ts @@ -0,0 +1,1780 @@ +/* eslint-disable */ +/* prettier-ignore */ +// @ts-nocheck +// noinspection JSUnusedGlobalSymbols +// Generated by unplugin-auto-import +export {} +declare global { + const EffectScope: (typeof import("vue"))["EffectScope"]; + const ElForm: (typeof import("element-plus/es"))["ElForm"]; + const ElMessage: (typeof import("element-plus/es"))["ElMessage"]; + const ElMessageBox: (typeof import("element-plus/es"))["ElMessageBox"]; + const ElNotification: (typeof import("element-plus/es"))["ElNotification"]; + const ElTree: (typeof import("element-plus/es"))["ElTree"]; + const acceptHMRUpdate: (typeof import("pinia"))["acceptHMRUpdate"]; + const asyncComputed: (typeof import("@vueuse/core"))["asyncComputed"]; + const autoResetRef: (typeof import("@vueuse/core"))["autoResetRef"]; + const computed: (typeof import("vue"))["computed"]; + const computedAsync: (typeof import("@vueuse/core"))["computedAsync"]; + const computedEager: (typeof import("@vueuse/core"))["computedEager"]; + const computedInject: (typeof import("@vueuse/core"))["computedInject"]; + const computedWithControl: (typeof import("@vueuse/core"))["computedWithControl"]; + const controlledComputed: (typeof import("@vueuse/core"))["controlledComputed"]; + const controlledRef: (typeof import("@vueuse/core"))["controlledRef"]; + const createApp: (typeof import("vue"))["createApp"]; + const createEventHook: (typeof import("@vueuse/core"))["createEventHook"]; + const createGlobalState: (typeof import("@vueuse/core"))["createGlobalState"]; + const createInjectionState: (typeof import("@vueuse/core"))["createInjectionState"]; + const createPinia: (typeof import("pinia"))["createPinia"]; + const createReactiveFn: (typeof import("@vueuse/core"))["createReactiveFn"]; + const createReusableTemplate: (typeof import("@vueuse/core"))["createReusableTemplate"]; + const createSharedComposable: (typeof import("@vueuse/core"))["createSharedComposable"]; + const createTemplatePromise: (typeof import("@vueuse/core"))["createTemplatePromise"]; + const createUnrefFn: (typeof import("@vueuse/core"))["createUnrefFn"]; + const customRef: (typeof import("vue"))["customRef"]; + const debouncedRef: (typeof import("@vueuse/core"))["debouncedRef"]; + const debouncedWatch: (typeof import("@vueuse/core"))["debouncedWatch"]; + const defineAsyncComponent: (typeof import("vue"))["defineAsyncComponent"]; + const defineComponent: (typeof import("vue"))["defineComponent"]; + const defineStore: (typeof import("pinia"))["defineStore"]; + const eagerComputed: (typeof import("@vueuse/core"))["eagerComputed"]; + const effectScope: (typeof import("vue"))["effectScope"]; + const extendRef: (typeof import("@vueuse/core"))["extendRef"]; + const getActivePinia: (typeof import("pinia"))["getActivePinia"]; + const getCurrentInstance: (typeof import("vue"))["getCurrentInstance"]; + const getCurrentScope: (typeof import("vue"))["getCurrentScope"]; + const h: (typeof import("vue"))["h"]; + const ignorableWatch: (typeof import("@vueuse/core"))["ignorableWatch"]; + const inject: (typeof import("vue"))["inject"]; + const injectLocal: (typeof import("@vueuse/core"))["injectLocal"]; + const isDefined: (typeof import("@vueuse/core"))["isDefined"]; + const isProxy: (typeof import("vue"))["isProxy"]; + const isReactive: (typeof import("vue"))["isReactive"]; + const isReadonly: (typeof import("vue"))["isReadonly"]; + const isRef: (typeof import("vue"))["isRef"]; + const makeDestructurable: (typeof import("@vueuse/core"))["makeDestructurable"]; + const mapActions: (typeof import("pinia"))["mapActions"]; + const mapGetters: (typeof import("pinia"))["mapGetters"]; + const mapState: (typeof import("pinia"))["mapState"]; + const mapStores: (typeof import("pinia"))["mapStores"]; + const mapWritableState: (typeof import("pinia"))["mapWritableState"]; + const markRaw: (typeof import("vue"))["markRaw"]; + const nextTick: (typeof import("vue"))["nextTick"]; + const onActivated: (typeof import("vue"))["onActivated"]; + const onBeforeMount: (typeof import("vue"))["onBeforeMount"]; + const onBeforeRouteLeave: (typeof import("vue-router"))["onBeforeRouteLeave"]; + const onBeforeRouteUpdate: (typeof import("vue-router"))["onBeforeRouteUpdate"]; + const onBeforeUnmount: (typeof import("vue"))["onBeforeUnmount"]; + const onBeforeUpdate: (typeof import("vue"))["onBeforeUpdate"]; + const onClickOutside: (typeof import("@vueuse/core"))["onClickOutside"]; + const onDeactivated: (typeof import("vue"))["onDeactivated"]; + const onErrorCaptured: (typeof import("vue"))["onErrorCaptured"]; + const onKeyStroke: (typeof import("@vueuse/core"))["onKeyStroke"]; + const onLongPress: (typeof import("@vueuse/core"))["onLongPress"]; + const onMounted: (typeof import("vue"))["onMounted"]; + const onRenderTracked: (typeof import("vue"))["onRenderTracked"]; + const onRenderTriggered: (typeof import("vue"))["onRenderTriggered"]; + const onScopeDispose: (typeof import("vue"))["onScopeDispose"]; + const onServerPrefetch: (typeof import("vue"))["onServerPrefetch"]; + const onStartTyping: (typeof import("@vueuse/core"))["onStartTyping"]; + const onUnmounted: (typeof import("vue"))["onUnmounted"]; + const onUpdated: (typeof import("vue"))["onUpdated"]; + const pausableWatch: (typeof import("@vueuse/core"))["pausableWatch"]; + const provide: (typeof import("vue"))["provide"]; + const provideLocal: (typeof import("@vueuse/core"))["provideLocal"]; + const reactify: (typeof import("@vueuse/core"))["reactify"]; + const reactifyObject: (typeof import("@vueuse/core"))["reactifyObject"]; + const reactive: (typeof import("vue"))["reactive"]; + const reactiveComputed: (typeof import("@vueuse/core"))["reactiveComputed"]; + const reactiveOmit: (typeof import("@vueuse/core"))["reactiveOmit"]; + const reactivePick: (typeof import("@vueuse/core"))["reactivePick"]; + const readonly: (typeof import("vue"))["readonly"]; + const ref: (typeof import("vue"))["ref"]; + const refAutoReset: (typeof import("@vueuse/core"))["refAutoReset"]; + const refDebounced: (typeof import("@vueuse/core"))["refDebounced"]; + const refDefault: (typeof import("@vueuse/core"))["refDefault"]; + const refThrottled: (typeof import("@vueuse/core"))["refThrottled"]; + const refWithControl: (typeof import("@vueuse/core"))["refWithControl"]; + const resolveComponent: (typeof import("vue"))["resolveComponent"]; + const resolveRef: (typeof import("@vueuse/core"))["resolveRef"]; + const resolveUnref: (typeof import("@vueuse/core"))["resolveUnref"]; + const setActivePinia: (typeof import("pinia"))["setActivePinia"]; + const setMapStoreSuffix: (typeof import("pinia"))["setMapStoreSuffix"]; + const shallowReactive: (typeof import("vue"))["shallowReactive"]; + const shallowReadonly: (typeof import("vue"))["shallowReadonly"]; + const shallowRef: (typeof import("vue"))["shallowRef"]; + const storeToRefs: (typeof import("pinia"))["storeToRefs"]; + const syncRef: (typeof import("@vueuse/core"))["syncRef"]; + const syncRefs: (typeof import("@vueuse/core"))["syncRefs"]; + const templateRef: (typeof import("@vueuse/core"))["templateRef"]; + const throttledRef: (typeof import("@vueuse/core"))["throttledRef"]; + const throttledWatch: (typeof import("@vueuse/core"))["throttledWatch"]; + const toRaw: (typeof import("vue"))["toRaw"]; + const toReactive: (typeof import("@vueuse/core"))["toReactive"]; + const toRef: (typeof import("vue"))["toRef"]; + const toRefs: (typeof import("vue"))["toRefs"]; + const toValue: (typeof import("vue"))["toValue"]; + const triggerRef: (typeof import("vue"))["triggerRef"]; + const tryOnBeforeMount: (typeof import("@vueuse/core"))["tryOnBeforeMount"]; + const tryOnBeforeUnmount: (typeof import("@vueuse/core"))["tryOnBeforeUnmount"]; + const tryOnMounted: (typeof import("@vueuse/core"))["tryOnMounted"]; + const tryOnScopeDispose: (typeof import("@vueuse/core"))["tryOnScopeDispose"]; + const tryOnUnmounted: (typeof import("@vueuse/core"))["tryOnUnmounted"]; + const unref: (typeof import("vue"))["unref"]; + const unrefElement: (typeof import("@vueuse/core"))["unrefElement"]; + const until: (typeof import("@vueuse/core"))["until"]; + const useActiveElement: (typeof import("@vueuse/core"))["useActiveElement"]; + const useAnimate: (typeof import("@vueuse/core"))["useAnimate"]; + const useArrayDifference: (typeof import("@vueuse/core"))["useArrayDifference"]; + const useArrayEvery: (typeof import("@vueuse/core"))["useArrayEvery"]; + const useArrayFilter: (typeof import("@vueuse/core"))["useArrayFilter"]; + const useArrayFind: (typeof import("@vueuse/core"))["useArrayFind"]; + const useArrayFindIndex: (typeof import("@vueuse/core"))["useArrayFindIndex"]; + const useArrayFindLast: (typeof import("@vueuse/core"))["useArrayFindLast"]; + const useArrayIncludes: (typeof import("@vueuse/core"))["useArrayIncludes"]; + const useArrayJoin: (typeof import("@vueuse/core"))["useArrayJoin"]; + const useArrayMap: (typeof import("@vueuse/core"))["useArrayMap"]; + const useArrayReduce: (typeof import("@vueuse/core"))["useArrayReduce"]; + const useArraySome: (typeof import("@vueuse/core"))["useArraySome"]; + const useArrayUnique: (typeof import("@vueuse/core"))["useArrayUnique"]; + const useAsyncQueue: (typeof import("@vueuse/core"))["useAsyncQueue"]; + const useAsyncState: (typeof import("@vueuse/core"))["useAsyncState"]; + const useAttrs: (typeof import("vue"))["useAttrs"]; + const useBase64: (typeof import("@vueuse/core"))["useBase64"]; + const useBattery: (typeof import("@vueuse/core"))["useBattery"]; + const useBluetooth: (typeof import("@vueuse/core"))["useBluetooth"]; + const useBreakpoints: (typeof import("@vueuse/core"))["useBreakpoints"]; + const useBroadcastChannel: (typeof import("@vueuse/core"))["useBroadcastChannel"]; + const useBrowserLocation: (typeof import("@vueuse/core"))["useBrowserLocation"]; + const useCached: (typeof import("@vueuse/core"))["useCached"]; + const useClipboard: (typeof import("@vueuse/core"))["useClipboard"]; + const useClipboardItems: (typeof import("@vueuse/core"))["useClipboardItems"]; + const useCloned: (typeof import("@vueuse/core"))["useCloned"]; + const useColorMode: (typeof import("@vueuse/core"))["useColorMode"]; + const useConfirmDialog: (typeof import("@vueuse/core"))["useConfirmDialog"]; + const useCounter: (typeof import("@vueuse/core"))["useCounter"]; + const useCssModule: (typeof import("vue"))["useCssModule"]; + const useCssVar: (typeof import("@vueuse/core"))["useCssVar"]; + const useCssVars: (typeof import("vue"))["useCssVars"]; + const useCurrentElement: (typeof import("@vueuse/core"))["useCurrentElement"]; + const useCycleList: (typeof import("@vueuse/core"))["useCycleList"]; + const useDark: (typeof import("@vueuse/core"))["useDark"]; + const useDateFormat: (typeof import("@vueuse/core"))["useDateFormat"]; + const useDebounce: (typeof import("@vueuse/core"))["useDebounce"]; + const useDebounceFn: (typeof import("@vueuse/core"))["useDebounceFn"]; + const useDebouncedRefHistory: (typeof import("@vueuse/core"))["useDebouncedRefHistory"]; + const useDeviceMotion: (typeof import("@vueuse/core"))["useDeviceMotion"]; + const useDeviceOrientation: (typeof import("@vueuse/core"))["useDeviceOrientation"]; + const useDevicePixelRatio: (typeof import("@vueuse/core"))["useDevicePixelRatio"]; + const useDevicesList: (typeof import("@vueuse/core"))["useDevicesList"]; + const useDisplayMedia: (typeof import("@vueuse/core"))["useDisplayMedia"]; + const useDocumentVisibility: (typeof import("@vueuse/core"))["useDocumentVisibility"]; + const useDraggable: (typeof import("@vueuse/core"))["useDraggable"]; + const useDropZone: (typeof import("@vueuse/core"))["useDropZone"]; + const useElementBounding: (typeof import("@vueuse/core"))["useElementBounding"]; + const useElementByPoint: (typeof import("@vueuse/core"))["useElementByPoint"]; + const useElementHover: (typeof import("@vueuse/core"))["useElementHover"]; + const useElementSize: (typeof import("@vueuse/core"))["useElementSize"]; + const useElementVisibility: (typeof import("@vueuse/core"))["useElementVisibility"]; + const useEventBus: (typeof import("@vueuse/core"))["useEventBus"]; + const useEventListener: (typeof import("@vueuse/core"))["useEventListener"]; + const useEventSource: (typeof import("@vueuse/core"))["useEventSource"]; + const useEyeDropper: (typeof import("@vueuse/core"))["useEyeDropper"]; + const useFavicon: (typeof import("@vueuse/core"))["useFavicon"]; + const useFetch: (typeof import("@vueuse/core"))["useFetch"]; + const useFileDialog: (typeof import("@vueuse/core"))["useFileDialog"]; + const useFileSystemAccess: (typeof import("@vueuse/core"))["useFileSystemAccess"]; + const useFocus: (typeof import("@vueuse/core"))["useFocus"]; + const useFocusWithin: (typeof import("@vueuse/core"))["useFocusWithin"]; + const useFps: (typeof import("@vueuse/core"))["useFps"]; + const useFullscreen: (typeof import("@vueuse/core"))["useFullscreen"]; + const useGamepad: (typeof import("@vueuse/core"))["useGamepad"]; + const useGeolocation: (typeof import("@vueuse/core"))["useGeolocation"]; + const useI18n: (typeof import("vue-i18n"))["useI18n"]; + const useIdle: (typeof import("@vueuse/core"))["useIdle"]; + const useImage: (typeof import("@vueuse/core"))["useImage"]; + const useInfiniteScroll: (typeof import("@vueuse/core"))["useInfiniteScroll"]; + const useIntersectionObserver: (typeof import("@vueuse/core"))["useIntersectionObserver"]; + const useInterval: (typeof import("@vueuse/core"))["useInterval"]; + const useIntervalFn: (typeof import("@vueuse/core"))["useIntervalFn"]; + const useKeyModifier: (typeof import("@vueuse/core"))["useKeyModifier"]; + const useLastChanged: (typeof import("@vueuse/core"))["useLastChanged"]; + const useLink: (typeof import("vue-router"))["useLink"]; + const useLocalStorage: (typeof import("@vueuse/core"))["useLocalStorage"]; + const useMagicKeys: (typeof import("@vueuse/core"))["useMagicKeys"]; + const useManualRefHistory: (typeof import("@vueuse/core"))["useManualRefHistory"]; + const useMediaControls: (typeof import("@vueuse/core"))["useMediaControls"]; + const useMediaQuery: (typeof import("@vueuse/core"))["useMediaQuery"]; + const useMemoize: (typeof import("@vueuse/core"))["useMemoize"]; + const useMemory: (typeof import("@vueuse/core"))["useMemory"]; + const useMounted: (typeof import("@vueuse/core"))["useMounted"]; + const useMouse: (typeof import("@vueuse/core"))["useMouse"]; + const useMouseInElement: (typeof import("@vueuse/core"))["useMouseInElement"]; + const useMousePressed: (typeof import("@vueuse/core"))["useMousePressed"]; + const useMutationObserver: (typeof import("@vueuse/core"))["useMutationObserver"]; + const useNavigatorLanguage: (typeof import("@vueuse/core"))["useNavigatorLanguage"]; + const useNetwork: (typeof import("@vueuse/core"))["useNetwork"]; + const useNow: (typeof import("@vueuse/core"))["useNow"]; + const useObjectUrl: (typeof import("@vueuse/core"))["useObjectUrl"]; + const useOffsetPagination: (typeof import("@vueuse/core"))["useOffsetPagination"]; + const useOnline: (typeof import("@vueuse/core"))["useOnline"]; + const usePageLeave: (typeof import("@vueuse/core"))["usePageLeave"]; + const useParallax: (typeof import("@vueuse/core"))["useParallax"]; + const useParentElement: (typeof import("@vueuse/core"))["useParentElement"]; + const usePerformanceObserver: (typeof import("@vueuse/core"))["usePerformanceObserver"]; + const usePermission: (typeof import("@vueuse/core"))["usePermission"]; + const usePointer: (typeof import("@vueuse/core"))["usePointer"]; + const usePointerLock: (typeof import("@vueuse/core"))["usePointerLock"]; + const usePointerSwipe: (typeof import("@vueuse/core"))["usePointerSwipe"]; + const usePreferredColorScheme: (typeof import("@vueuse/core"))["usePreferredColorScheme"]; + const usePreferredContrast: (typeof import("@vueuse/core"))["usePreferredContrast"]; + const usePreferredDark: (typeof import("@vueuse/core"))["usePreferredDark"]; + const usePreferredLanguages: (typeof import("@vueuse/core"))["usePreferredLanguages"]; + const usePreferredReducedMotion: (typeof import("@vueuse/core"))["usePreferredReducedMotion"]; + const usePrevious: (typeof import("@vueuse/core"))["usePrevious"]; + const useRafFn: (typeof import("@vueuse/core"))["useRafFn"]; + const useRefHistory: (typeof import("@vueuse/core"))["useRefHistory"]; + const useResizeObserver: (typeof import("@vueuse/core"))["useResizeObserver"]; + const useRoute: (typeof import("vue-router"))["useRoute"]; + const useRouter: (typeof import("vue-router"))["useRouter"]; + const useScreenOrientation: (typeof import("@vueuse/core"))["useScreenOrientation"]; + const useScreenSafeArea: (typeof import("@vueuse/core"))["useScreenSafeArea"]; + const useScriptTag: (typeof import("@vueuse/core"))["useScriptTag"]; + const useScroll: (typeof import("@vueuse/core"))["useScroll"]; + const useScrollLock: (typeof import("@vueuse/core"))["useScrollLock"]; + const useSessionStorage: (typeof import("@vueuse/core"))["useSessionStorage"]; + const useShare: (typeof import("@vueuse/core"))["useShare"]; + const useSlots: (typeof import("vue"))["useSlots"]; + const useSorted: (typeof import("@vueuse/core"))["useSorted"]; + const useSpeechRecognition: (typeof import("@vueuse/core"))["useSpeechRecognition"]; + const useSpeechSynthesis: (typeof import("@vueuse/core"))["useSpeechSynthesis"]; + const useStepper: (typeof import("@vueuse/core"))["useStepper"]; + const useStorage: (typeof import("@vueuse/core"))["useStorage"]; + const useStorageAsync: (typeof import("@vueuse/core"))["useStorageAsync"]; + const useStyleTag: (typeof import("@vueuse/core"))["useStyleTag"]; + const useSupported: (typeof import("@vueuse/core"))["useSupported"]; + const useSwipe: (typeof import("@vueuse/core"))["useSwipe"]; + const useTemplateRefsList: (typeof import("@vueuse/core"))["useTemplateRefsList"]; + const useTextDirection: (typeof import("@vueuse/core"))["useTextDirection"]; + const useTextSelection: (typeof import("@vueuse/core"))["useTextSelection"]; + const useTextareaAutosize: (typeof import("@vueuse/core"))["useTextareaAutosize"]; + const useThrottle: (typeof import("@vueuse/core"))["useThrottle"]; + const useThrottleFn: (typeof import("@vueuse/core"))["useThrottleFn"]; + const useThrottledRefHistory: (typeof import("@vueuse/core"))["useThrottledRefHistory"]; + const useTimeAgo: (typeof import("@vueuse/core"))["useTimeAgo"]; + const useTimeout: (typeof import("@vueuse/core"))["useTimeout"]; + const useTimeoutFn: (typeof import("@vueuse/core"))["useTimeoutFn"]; + const useTimeoutPoll: (typeof import("@vueuse/core"))["useTimeoutPoll"]; + const useTimestamp: (typeof import("@vueuse/core"))["useTimestamp"]; + const useTitle: (typeof import("@vueuse/core"))["useTitle"]; + const useToNumber: (typeof import("@vueuse/core"))["useToNumber"]; + const useToString: (typeof import("@vueuse/core"))["useToString"]; + const useToggle: (typeof import("@vueuse/core"))["useToggle"]; + const useTransition: (typeof import("@vueuse/core"))["useTransition"]; + const useUrlSearchParams: (typeof import("@vueuse/core"))["useUrlSearchParams"]; + const useUserMedia: (typeof import("@vueuse/core"))["useUserMedia"]; + const useVModel: (typeof import("@vueuse/core"))["useVModel"]; + const useVModels: (typeof import("@vueuse/core"))["useVModels"]; + const useVibrate: (typeof import("@vueuse/core"))["useVibrate"]; + const useVirtualList: (typeof import("@vueuse/core"))["useVirtualList"]; + const useWakeLock: (typeof import("@vueuse/core"))["useWakeLock"]; + const useWebNotification: (typeof import("@vueuse/core"))["useWebNotification"]; + const useWebSocket: (typeof import("@vueuse/core"))["useWebSocket"]; + const useWebWorker: (typeof import("@vueuse/core"))["useWebWorker"]; + const useWebWorkerFn: (typeof import("@vueuse/core"))["useWebWorkerFn"]; + const useWindowFocus: (typeof import("@vueuse/core"))["useWindowFocus"]; + const useWindowScroll: (typeof import("@vueuse/core"))["useWindowScroll"]; + const useWindowSize: (typeof import("@vueuse/core"))["useWindowSize"]; + const watch: (typeof import("vue"))["watch"]; + const watchArray: (typeof import("@vueuse/core"))["watchArray"]; + const watchAtMost: (typeof import("@vueuse/core"))["watchAtMost"]; + const watchDebounced: (typeof import("@vueuse/core"))["watchDebounced"]; + const watchDeep: (typeof import("@vueuse/core"))["watchDeep"]; + const watchEffect: (typeof import("vue"))["watchEffect"]; + const watchIgnorable: (typeof import("@vueuse/core"))["watchIgnorable"]; + const watchImmediate: (typeof import("@vueuse/core"))["watchImmediate"]; + const watchOnce: (typeof import("@vueuse/core"))["watchOnce"]; + const watchPausable: (typeof import("@vueuse/core"))["watchPausable"]; + const watchPostEffect: (typeof import("vue"))["watchPostEffect"]; + const watchSyncEffect: (typeof import("vue"))["watchSyncEffect"]; + const watchThrottled: (typeof import("@vueuse/core"))["watchThrottled"]; + const watchTriggerable: (typeof import("@vueuse/core"))["watchTriggerable"]; + const watchWithFilter: (typeof import("@vueuse/core"))["watchWithFilter"]; + const whenever: (typeof import("@vueuse/core"))["whenever"]; +} +// for type re-export +declare global { + // @ts-ignore + export type { + Component, + ComponentPublicInstance, + ComputedRef, + ExtractDefaultPropTypes, + ExtractPropTypes, + ExtractPublicPropTypes, + InjectionKey, + PropType, + Ref, + VNode, + WritableComputedRef, + } from "vue"; + import("vue"); +} +// for vue template auto import +import { UnwrapRef } from "vue"; +declare module "vue" { + interface GlobalComponents {} + interface ComponentCustomProperties { + readonly EffectScope: UnwrapRef<(typeof import("vue"))["EffectScope"]>; + readonly ElMessage: UnwrapRef< + (typeof import("element-plus/es"))["ElMessage"] + >; + readonly ElMessageBox: UnwrapRef< + (typeof import("element-plus/es"))["ElMessageBox"] + >; + readonly acceptHMRUpdate: UnwrapRef< + (typeof import("pinia"))["acceptHMRUpdate"] + >; + readonly asyncComputed: UnwrapRef< + (typeof import("@vueuse/core"))["asyncComputed"] + >; + readonly autoResetRef: UnwrapRef< + (typeof import("@vueuse/core"))["autoResetRef"] + >; + readonly computed: UnwrapRef<(typeof import("vue"))["computed"]>; + readonly computedAsync: UnwrapRef< + (typeof import("@vueuse/core"))["computedAsync"] + >; + readonly computedEager: UnwrapRef< + (typeof import("@vueuse/core"))["computedEager"] + >; + readonly computedInject: UnwrapRef< + (typeof import("@vueuse/core"))["computedInject"] + >; + readonly computedWithControl: UnwrapRef< + (typeof import("@vueuse/core"))["computedWithControl"] + >; + readonly controlledComputed: UnwrapRef< + (typeof import("@vueuse/core"))["controlledComputed"] + >; + readonly controlledRef: UnwrapRef< + (typeof import("@vueuse/core"))["controlledRef"] + >; + readonly createApp: UnwrapRef<(typeof import("vue"))["createApp"]>; + readonly createEventHook: UnwrapRef< + (typeof import("@vueuse/core"))["createEventHook"] + >; + readonly createGlobalState: UnwrapRef< + (typeof import("@vueuse/core"))["createGlobalState"] + >; + readonly createInjectionState: UnwrapRef< + (typeof import("@vueuse/core"))["createInjectionState"] + >; + readonly createPinia: UnwrapRef<(typeof import("pinia"))["createPinia"]>; + readonly createReactiveFn: UnwrapRef< + (typeof import("@vueuse/core"))["createReactiveFn"] + >; + readonly createReusableTemplate: UnwrapRef< + (typeof import("@vueuse/core"))["createReusableTemplate"] + >; + readonly createSharedComposable: UnwrapRef< + (typeof import("@vueuse/core"))["createSharedComposable"] + >; + readonly createTemplatePromise: UnwrapRef< + (typeof import("@vueuse/core"))["createTemplatePromise"] + >; + readonly createUnrefFn: UnwrapRef< + (typeof import("@vueuse/core"))["createUnrefFn"] + >; + readonly customRef: UnwrapRef<(typeof import("vue"))["customRef"]>; + readonly debouncedRef: UnwrapRef< + (typeof import("@vueuse/core"))["debouncedRef"] + >; + readonly debouncedWatch: UnwrapRef< + (typeof import("@vueuse/core"))["debouncedWatch"] + >; + readonly defineAsyncComponent: UnwrapRef< + (typeof import("vue"))["defineAsyncComponent"] + >; + readonly defineComponent: UnwrapRef< + (typeof import("vue"))["defineComponent"] + >; + readonly defineStore: UnwrapRef<(typeof import("pinia"))["defineStore"]>; + readonly eagerComputed: UnwrapRef< + (typeof import("@vueuse/core"))["eagerComputed"] + >; + readonly effectScope: UnwrapRef<(typeof import("vue"))["effectScope"]>; + readonly extendRef: UnwrapRef<(typeof import("@vueuse/core"))["extendRef"]>; + readonly getActivePinia: UnwrapRef< + (typeof import("pinia"))["getActivePinia"] + >; + readonly getCurrentInstance: UnwrapRef< + (typeof import("vue"))["getCurrentInstance"] + >; + readonly getCurrentScope: UnwrapRef< + (typeof import("vue"))["getCurrentScope"] + >; + readonly h: UnwrapRef<(typeof import("vue"))["h"]>; + readonly ignorableWatch: UnwrapRef< + (typeof import("@vueuse/core"))["ignorableWatch"] + >; + readonly inject: UnwrapRef<(typeof import("vue"))["inject"]>; + readonly injectLocal: UnwrapRef< + (typeof import("@vueuse/core"))["injectLocal"] + >; + readonly isDefined: UnwrapRef<(typeof import("@vueuse/core"))["isDefined"]>; + readonly isProxy: UnwrapRef<(typeof import("vue"))["isProxy"]>; + readonly isReactive: UnwrapRef<(typeof import("vue"))["isReactive"]>; + readonly isReadonly: UnwrapRef<(typeof import("vue"))["isReadonly"]>; + readonly isRef: UnwrapRef<(typeof import("vue"))["isRef"]>; + readonly makeDestructurable: UnwrapRef< + (typeof import("@vueuse/core"))["makeDestructurable"] + >; + readonly mapActions: UnwrapRef<(typeof import("pinia"))["mapActions"]>; + readonly mapGetters: UnwrapRef<(typeof import("pinia"))["mapGetters"]>; + readonly mapState: UnwrapRef<(typeof import("pinia"))["mapState"]>; + readonly mapStores: UnwrapRef<(typeof import("pinia"))["mapStores"]>; + readonly mapWritableState: UnwrapRef< + (typeof import("pinia"))["mapWritableState"] + >; + readonly markRaw: UnwrapRef<(typeof import("vue"))["markRaw"]>; + readonly nextTick: UnwrapRef<(typeof import("vue"))["nextTick"]>; + readonly onActivated: UnwrapRef<(typeof import("vue"))["onActivated"]>; + readonly onBeforeMount: UnwrapRef<(typeof import("vue"))["onBeforeMount"]>; + readonly onBeforeRouteLeave: UnwrapRef< + (typeof import("vue-router"))["onBeforeRouteLeave"] + >; + readonly onBeforeRouteUpdate: UnwrapRef< + (typeof import("vue-router"))["onBeforeRouteUpdate"] + >; + readonly onBeforeUnmount: UnwrapRef< + (typeof import("vue"))["onBeforeUnmount"] + >; + readonly onBeforeUpdate: UnwrapRef< + (typeof import("vue"))["onBeforeUpdate"] + >; + readonly onClickOutside: UnwrapRef< + (typeof import("@vueuse/core"))["onClickOutside"] + >; + readonly onDeactivated: UnwrapRef<(typeof import("vue"))["onDeactivated"]>; + readonly onErrorCaptured: UnwrapRef< + (typeof import("vue"))["onErrorCaptured"] + >; + readonly onKeyStroke: UnwrapRef< + (typeof import("@vueuse/core"))["onKeyStroke"] + >; + readonly onLongPress: UnwrapRef< + (typeof import("@vueuse/core"))["onLongPress"] + >; + readonly onMounted: UnwrapRef<(typeof import("vue"))["onMounted"]>; + readonly onRenderTracked: UnwrapRef< + (typeof import("vue"))["onRenderTracked"] + >; + readonly onRenderTriggered: UnwrapRef< + (typeof import("vue"))["onRenderTriggered"] + >; + readonly onScopeDispose: UnwrapRef< + (typeof import("vue"))["onScopeDispose"] + >; + readonly onServerPrefetch: UnwrapRef< + (typeof import("vue"))["onServerPrefetch"] + >; + readonly onStartTyping: UnwrapRef< + (typeof import("@vueuse/core"))["onStartTyping"] + >; + readonly onUnmounted: UnwrapRef<(typeof import("vue"))["onUnmounted"]>; + readonly onUpdated: UnwrapRef<(typeof import("vue"))["onUpdated"]>; + readonly pausableWatch: UnwrapRef< + (typeof import("@vueuse/core"))["pausableWatch"] + >; + readonly provide: UnwrapRef<(typeof import("vue"))["provide"]>; + readonly provideLocal: UnwrapRef< + (typeof import("@vueuse/core"))["provideLocal"] + >; + readonly reactify: UnwrapRef<(typeof import("@vueuse/core"))["reactify"]>; + readonly reactifyObject: UnwrapRef< + (typeof import("@vueuse/core"))["reactifyObject"] + >; + readonly reactive: UnwrapRef<(typeof import("vue"))["reactive"]>; + readonly reactiveComputed: UnwrapRef< + (typeof import("@vueuse/core"))["reactiveComputed"] + >; + readonly reactiveOmit: UnwrapRef< + (typeof import("@vueuse/core"))["reactiveOmit"] + >; + readonly reactivePick: UnwrapRef< + (typeof import("@vueuse/core"))["reactivePick"] + >; + readonly readonly: UnwrapRef<(typeof import("vue"))["readonly"]>; + readonly ref: UnwrapRef<(typeof import("vue"))["ref"]>; + readonly refAutoReset: UnwrapRef< + (typeof import("@vueuse/core"))["refAutoReset"] + >; + readonly refDebounced: UnwrapRef< + (typeof import("@vueuse/core"))["refDebounced"] + >; + readonly refDefault: UnwrapRef< + (typeof import("@vueuse/core"))["refDefault"] + >; + readonly refThrottled: UnwrapRef< + (typeof import("@vueuse/core"))["refThrottled"] + >; + readonly refWithControl: UnwrapRef< + (typeof import("@vueuse/core"))["refWithControl"] + >; + readonly resolveComponent: UnwrapRef< + (typeof import("vue"))["resolveComponent"] + >; + readonly resolveRef: UnwrapRef< + (typeof import("@vueuse/core"))["resolveRef"] + >; + readonly resolveUnref: UnwrapRef< + (typeof import("@vueuse/core"))["resolveUnref"] + >; + readonly setActivePinia: UnwrapRef< + (typeof import("pinia"))["setActivePinia"] + >; + readonly setMapStoreSuffix: UnwrapRef< + (typeof import("pinia"))["setMapStoreSuffix"] + >; + readonly shallowReactive: UnwrapRef< + (typeof import("vue"))["shallowReactive"] + >; + readonly shallowReadonly: UnwrapRef< + (typeof import("vue"))["shallowReadonly"] + >; + readonly shallowRef: UnwrapRef<(typeof import("vue"))["shallowRef"]>; + readonly storeToRefs: UnwrapRef<(typeof import("pinia"))["storeToRefs"]>; + readonly syncRef: UnwrapRef<(typeof import("@vueuse/core"))["syncRef"]>; + readonly syncRefs: UnwrapRef<(typeof import("@vueuse/core"))["syncRefs"]>; + readonly templateRef: UnwrapRef< + (typeof import("@vueuse/core"))["templateRef"] + >; + readonly throttledRef: UnwrapRef< + (typeof import("@vueuse/core"))["throttledRef"] + >; + readonly throttledWatch: UnwrapRef< + (typeof import("@vueuse/core"))["throttledWatch"] + >; + readonly toRaw: UnwrapRef<(typeof import("vue"))["toRaw"]>; + readonly toReactive: UnwrapRef< + (typeof import("@vueuse/core"))["toReactive"] + >; + readonly toRef: UnwrapRef<(typeof import("vue"))["toRef"]>; + readonly toRefs: UnwrapRef<(typeof import("vue"))["toRefs"]>; + readonly toValue: UnwrapRef<(typeof import("vue"))["toValue"]>; + readonly triggerRef: UnwrapRef<(typeof import("vue"))["triggerRef"]>; + readonly tryOnBeforeMount: UnwrapRef< + (typeof import("@vueuse/core"))["tryOnBeforeMount"] + >; + readonly tryOnBeforeUnmount: UnwrapRef< + (typeof import("@vueuse/core"))["tryOnBeforeUnmount"] + >; + readonly tryOnMounted: UnwrapRef< + (typeof import("@vueuse/core"))["tryOnMounted"] + >; + readonly tryOnScopeDispose: UnwrapRef< + (typeof import("@vueuse/core"))["tryOnScopeDispose"] + >; + readonly tryOnUnmounted: UnwrapRef< + (typeof import("@vueuse/core"))["tryOnUnmounted"] + >; + readonly unref: UnwrapRef<(typeof import("vue"))["unref"]>; + readonly unrefElement: UnwrapRef< + (typeof import("@vueuse/core"))["unrefElement"] + >; + readonly until: UnwrapRef<(typeof import("@vueuse/core"))["until"]>; + readonly useActiveElement: UnwrapRef< + (typeof import("@vueuse/core"))["useActiveElement"] + >; + readonly useAnimate: UnwrapRef< + (typeof import("@vueuse/core"))["useAnimate"] + >; + readonly useArrayDifference: UnwrapRef< + (typeof import("@vueuse/core"))["useArrayDifference"] + >; + readonly useArrayEvery: UnwrapRef< + (typeof import("@vueuse/core"))["useArrayEvery"] + >; + readonly useArrayFilter: UnwrapRef< + (typeof import("@vueuse/core"))["useArrayFilter"] + >; + readonly useArrayFind: UnwrapRef< + (typeof import("@vueuse/core"))["useArrayFind"] + >; + readonly useArrayFindIndex: UnwrapRef< + (typeof import("@vueuse/core"))["useArrayFindIndex"] + >; + readonly useArrayFindLast: UnwrapRef< + (typeof import("@vueuse/core"))["useArrayFindLast"] + >; + readonly useArrayIncludes: UnwrapRef< + (typeof import("@vueuse/core"))["useArrayIncludes"] + >; + readonly useArrayJoin: UnwrapRef< + (typeof import("@vueuse/core"))["useArrayJoin"] + >; + readonly useArrayMap: UnwrapRef< + (typeof import("@vueuse/core"))["useArrayMap"] + >; + readonly useArrayReduce: UnwrapRef< + (typeof import("@vueuse/core"))["useArrayReduce"] + >; + readonly useArraySome: UnwrapRef< + (typeof import("@vueuse/core"))["useArraySome"] + >; + readonly useArrayUnique: UnwrapRef< + (typeof import("@vueuse/core"))["useArrayUnique"] + >; + readonly useAsyncQueue: UnwrapRef< + (typeof import("@vueuse/core"))["useAsyncQueue"] + >; + readonly useAsyncState: UnwrapRef< + (typeof import("@vueuse/core"))["useAsyncState"] + >; + readonly useAttrs: UnwrapRef<(typeof import("vue"))["useAttrs"]>; + readonly useBase64: UnwrapRef<(typeof import("@vueuse/core"))["useBase64"]>; + readonly useBattery: UnwrapRef< + (typeof import("@vueuse/core"))["useBattery"] + >; + readonly useBluetooth: UnwrapRef< + (typeof import("@vueuse/core"))["useBluetooth"] + >; + readonly useBreakpoints: UnwrapRef< + (typeof import("@vueuse/core"))["useBreakpoints"] + >; + readonly useBroadcastChannel: UnwrapRef< + (typeof import("@vueuse/core"))["useBroadcastChannel"] + >; + readonly useBrowserLocation: UnwrapRef< + (typeof import("@vueuse/core"))["useBrowserLocation"] + >; + readonly useCached: UnwrapRef<(typeof import("@vueuse/core"))["useCached"]>; + readonly useClipboard: UnwrapRef< + (typeof import("@vueuse/core"))["useClipboard"] + >; + readonly useClipboardItems: UnwrapRef< + (typeof import("@vueuse/core"))["useClipboardItems"] + >; + readonly useCloned: UnwrapRef<(typeof import("@vueuse/core"))["useCloned"]>; + readonly useColorMode: UnwrapRef< + (typeof import("@vueuse/core"))["useColorMode"] + >; + readonly useConfirmDialog: UnwrapRef< + (typeof import("@vueuse/core"))["useConfirmDialog"] + >; + readonly useCounter: UnwrapRef< + (typeof import("@vueuse/core"))["useCounter"] + >; + readonly useCssModule: UnwrapRef<(typeof import("vue"))["useCssModule"]>; + readonly useCssVar: UnwrapRef<(typeof import("@vueuse/core"))["useCssVar"]>; + readonly useCssVars: UnwrapRef<(typeof import("vue"))["useCssVars"]>; + readonly useCurrentElement: UnwrapRef< + (typeof import("@vueuse/core"))["useCurrentElement"] + >; + readonly useCycleList: UnwrapRef< + (typeof import("@vueuse/core"))["useCycleList"] + >; + readonly useDark: UnwrapRef<(typeof import("@vueuse/core"))["useDark"]>; + readonly useDateFormat: UnwrapRef< + (typeof import("@vueuse/core"))["useDateFormat"] + >; + readonly useDebounce: UnwrapRef< + (typeof import("@vueuse/core"))["useDebounce"] + >; + readonly useDebounceFn: UnwrapRef< + (typeof import("@vueuse/core"))["useDebounceFn"] + >; + readonly useDebouncedRefHistory: UnwrapRef< + (typeof import("@vueuse/core"))["useDebouncedRefHistory"] + >; + readonly useDeviceMotion: UnwrapRef< + (typeof import("@vueuse/core"))["useDeviceMotion"] + >; + readonly useDeviceOrientation: UnwrapRef< + (typeof import("@vueuse/core"))["useDeviceOrientation"] + >; + readonly useDevicePixelRatio: UnwrapRef< + (typeof import("@vueuse/core"))["useDevicePixelRatio"] + >; + readonly useDevicesList: UnwrapRef< + (typeof import("@vueuse/core"))["useDevicesList"] + >; + readonly useDisplayMedia: UnwrapRef< + (typeof import("@vueuse/core"))["useDisplayMedia"] + >; + readonly useDocumentVisibility: UnwrapRef< + (typeof import("@vueuse/core"))["useDocumentVisibility"] + >; + readonly useDraggable: UnwrapRef< + (typeof import("@vueuse/core"))["useDraggable"] + >; + readonly useDropZone: UnwrapRef< + (typeof import("@vueuse/core"))["useDropZone"] + >; + readonly useElementBounding: UnwrapRef< + (typeof import("@vueuse/core"))["useElementBounding"] + >; + readonly useElementByPoint: UnwrapRef< + (typeof import("@vueuse/core"))["useElementByPoint"] + >; + readonly useElementHover: UnwrapRef< + (typeof import("@vueuse/core"))["useElementHover"] + >; + readonly useElementSize: UnwrapRef< + (typeof import("@vueuse/core"))["useElementSize"] + >; + readonly useElementVisibility: UnwrapRef< + (typeof import("@vueuse/core"))["useElementVisibility"] + >; + readonly useEventBus: UnwrapRef< + (typeof import("@vueuse/core"))["useEventBus"] + >; + readonly useEventListener: UnwrapRef< + (typeof import("@vueuse/core"))["useEventListener"] + >; + readonly useEventSource: UnwrapRef< + (typeof import("@vueuse/core"))["useEventSource"] + >; + readonly useEyeDropper: UnwrapRef< + (typeof import("@vueuse/core"))["useEyeDropper"] + >; + readonly useFavicon: UnwrapRef< + (typeof import("@vueuse/core"))["useFavicon"] + >; + readonly useFetch: UnwrapRef<(typeof import("@vueuse/core"))["useFetch"]>; + readonly useFileDialog: UnwrapRef< + (typeof import("@vueuse/core"))["useFileDialog"] + >; + readonly useFileSystemAccess: UnwrapRef< + (typeof import("@vueuse/core"))["useFileSystemAccess"] + >; + readonly useFocus: UnwrapRef<(typeof import("@vueuse/core"))["useFocus"]>; + readonly useFocusWithin: UnwrapRef< + (typeof import("@vueuse/core"))["useFocusWithin"] + >; + readonly useFps: UnwrapRef<(typeof import("@vueuse/core"))["useFps"]>; + readonly useFullscreen: UnwrapRef< + (typeof import("@vueuse/core"))["useFullscreen"] + >; + readonly useGamepad: UnwrapRef< + (typeof import("@vueuse/core"))["useGamepad"] + >; + readonly useGeolocation: UnwrapRef< + (typeof import("@vueuse/core"))["useGeolocation"] + >; + readonly useI18n: UnwrapRef<(typeof import("vue-i18n"))["useI18n"]>; + readonly useIdle: UnwrapRef<(typeof import("@vueuse/core"))["useIdle"]>; + readonly useImage: UnwrapRef<(typeof import("@vueuse/core"))["useImage"]>; + readonly useInfiniteScroll: UnwrapRef< + (typeof import("@vueuse/core"))["useInfiniteScroll"] + >; + readonly useIntersectionObserver: UnwrapRef< + (typeof import("@vueuse/core"))["useIntersectionObserver"] + >; + readonly useInterval: UnwrapRef< + (typeof import("@vueuse/core"))["useInterval"] + >; + readonly useIntervalFn: UnwrapRef< + (typeof import("@vueuse/core"))["useIntervalFn"] + >; + readonly useKeyModifier: UnwrapRef< + (typeof import("@vueuse/core"))["useKeyModifier"] + >; + readonly useLastChanged: UnwrapRef< + (typeof import("@vueuse/core"))["useLastChanged"] + >; + readonly useLink: UnwrapRef<(typeof import("vue-router"))["useLink"]>; + readonly useLocalStorage: UnwrapRef< + (typeof import("@vueuse/core"))["useLocalStorage"] + >; + readonly useMagicKeys: UnwrapRef< + (typeof import("@vueuse/core"))["useMagicKeys"] + >; + readonly useManualRefHistory: UnwrapRef< + (typeof import("@vueuse/core"))["useManualRefHistory"] + >; + readonly useMediaControls: UnwrapRef< + (typeof import("@vueuse/core"))["useMediaControls"] + >; + readonly useMediaQuery: UnwrapRef< + (typeof import("@vueuse/core"))["useMediaQuery"] + >; + readonly useMemoize: UnwrapRef< + (typeof import("@vueuse/core"))["useMemoize"] + >; + readonly useMemory: UnwrapRef<(typeof import("@vueuse/core"))["useMemory"]>; + readonly useMounted: UnwrapRef< + (typeof import("@vueuse/core"))["useMounted"] + >; + readonly useMouse: UnwrapRef<(typeof import("@vueuse/core"))["useMouse"]>; + readonly useMouseInElement: UnwrapRef< + (typeof import("@vueuse/core"))["useMouseInElement"] + >; + readonly useMousePressed: UnwrapRef< + (typeof import("@vueuse/core"))["useMousePressed"] + >; + readonly useMutationObserver: UnwrapRef< + (typeof import("@vueuse/core"))["useMutationObserver"] + >; + readonly useNavigatorLanguage: UnwrapRef< + (typeof import("@vueuse/core"))["useNavigatorLanguage"] + >; + readonly useNetwork: UnwrapRef< + (typeof import("@vueuse/core"))["useNetwork"] + >; + readonly useNow: UnwrapRef<(typeof import("@vueuse/core"))["useNow"]>; + readonly useObjectUrl: UnwrapRef< + (typeof import("@vueuse/core"))["useObjectUrl"] + >; + readonly useOffsetPagination: UnwrapRef< + (typeof import("@vueuse/core"))["useOffsetPagination"] + >; + readonly useOnline: UnwrapRef<(typeof import("@vueuse/core"))["useOnline"]>; + readonly usePageLeave: UnwrapRef< + (typeof import("@vueuse/core"))["usePageLeave"] + >; + readonly useParallax: UnwrapRef< + (typeof import("@vueuse/core"))["useParallax"] + >; + readonly useParentElement: UnwrapRef< + (typeof import("@vueuse/core"))["useParentElement"] + >; + readonly usePerformanceObserver: UnwrapRef< + (typeof import("@vueuse/core"))["usePerformanceObserver"] + >; + readonly usePermission: UnwrapRef< + (typeof import("@vueuse/core"))["usePermission"] + >; + readonly usePointer: UnwrapRef< + (typeof import("@vueuse/core"))["usePointer"] + >; + readonly usePointerLock: UnwrapRef< + (typeof import("@vueuse/core"))["usePointerLock"] + >; + readonly usePointerSwipe: UnwrapRef< + (typeof import("@vueuse/core"))["usePointerSwipe"] + >; + readonly usePreferredColorScheme: UnwrapRef< + (typeof import("@vueuse/core"))["usePreferredColorScheme"] + >; + readonly usePreferredContrast: UnwrapRef< + (typeof import("@vueuse/core"))["usePreferredContrast"] + >; + readonly usePreferredDark: UnwrapRef< + (typeof import("@vueuse/core"))["usePreferredDark"] + >; + readonly usePreferredLanguages: UnwrapRef< + (typeof import("@vueuse/core"))["usePreferredLanguages"] + >; + readonly usePreferredReducedMotion: UnwrapRef< + (typeof import("@vueuse/core"))["usePreferredReducedMotion"] + >; + readonly usePrevious: UnwrapRef< + (typeof import("@vueuse/core"))["usePrevious"] + >; + readonly useRafFn: UnwrapRef<(typeof import("@vueuse/core"))["useRafFn"]>; + readonly useRefHistory: UnwrapRef< + (typeof import("@vueuse/core"))["useRefHistory"] + >; + readonly useResizeObserver: UnwrapRef< + (typeof import("@vueuse/core"))["useResizeObserver"] + >; + readonly useRoute: UnwrapRef<(typeof import("vue-router"))["useRoute"]>; + readonly useRouter: UnwrapRef<(typeof import("vue-router"))["useRouter"]>; + readonly useScreenOrientation: UnwrapRef< + (typeof import("@vueuse/core"))["useScreenOrientation"] + >; + readonly useScreenSafeArea: UnwrapRef< + (typeof import("@vueuse/core"))["useScreenSafeArea"] + >; + readonly useScriptTag: UnwrapRef< + (typeof import("@vueuse/core"))["useScriptTag"] + >; + readonly useScroll: UnwrapRef<(typeof import("@vueuse/core"))["useScroll"]>; + readonly useScrollLock: UnwrapRef< + (typeof import("@vueuse/core"))["useScrollLock"] + >; + readonly useSessionStorage: UnwrapRef< + (typeof import("@vueuse/core"))["useSessionStorage"] + >; + readonly useShare: UnwrapRef<(typeof import("@vueuse/core"))["useShare"]>; + readonly useSlots: UnwrapRef<(typeof import("vue"))["useSlots"]>; + readonly useSorted: UnwrapRef<(typeof import("@vueuse/core"))["useSorted"]>; + readonly useSpeechRecognition: UnwrapRef< + (typeof import("@vueuse/core"))["useSpeechRecognition"] + >; + readonly useSpeechSynthesis: UnwrapRef< + (typeof import("@vueuse/core"))["useSpeechSynthesis"] + >; + readonly useStepper: UnwrapRef< + (typeof import("@vueuse/core"))["useStepper"] + >; + readonly useStorage: UnwrapRef< + (typeof import("@vueuse/core"))["useStorage"] + >; + readonly useStorageAsync: UnwrapRef< + (typeof import("@vueuse/core"))["useStorageAsync"] + >; + readonly useStyleTag: UnwrapRef< + (typeof import("@vueuse/core"))["useStyleTag"] + >; + readonly useSupported: UnwrapRef< + (typeof import("@vueuse/core"))["useSupported"] + >; + readonly useSwipe: UnwrapRef<(typeof import("@vueuse/core"))["useSwipe"]>; + readonly useTemplateRefsList: UnwrapRef< + (typeof import("@vueuse/core"))["useTemplateRefsList"] + >; + readonly useTextDirection: UnwrapRef< + (typeof import("@vueuse/core"))["useTextDirection"] + >; + readonly useTextSelection: UnwrapRef< + (typeof import("@vueuse/core"))["useTextSelection"] + >; + readonly useTextareaAutosize: UnwrapRef< + (typeof import("@vueuse/core"))["useTextareaAutosize"] + >; + readonly useThrottle: UnwrapRef< + (typeof import("@vueuse/core"))["useThrottle"] + >; + readonly useThrottleFn: UnwrapRef< + (typeof import("@vueuse/core"))["useThrottleFn"] + >; + readonly useThrottledRefHistory: UnwrapRef< + (typeof import("@vueuse/core"))["useThrottledRefHistory"] + >; + readonly useTimeAgo: UnwrapRef< + (typeof import("@vueuse/core"))["useTimeAgo"] + >; + readonly useTimeout: UnwrapRef< + (typeof import("@vueuse/core"))["useTimeout"] + >; + readonly useTimeoutFn: UnwrapRef< + (typeof import("@vueuse/core"))["useTimeoutFn"] + >; + readonly useTimeoutPoll: UnwrapRef< + (typeof import("@vueuse/core"))["useTimeoutPoll"] + >; + readonly useTimestamp: UnwrapRef< + (typeof import("@vueuse/core"))["useTimestamp"] + >; + readonly useTitle: UnwrapRef<(typeof import("@vueuse/core"))["useTitle"]>; + readonly useToNumber: UnwrapRef< + (typeof import("@vueuse/core"))["useToNumber"] + >; + readonly useToString: UnwrapRef< + (typeof import("@vueuse/core"))["useToString"] + >; + readonly useToggle: UnwrapRef<(typeof import("@vueuse/core"))["useToggle"]>; + readonly useTransition: UnwrapRef< + (typeof import("@vueuse/core"))["useTransition"] + >; + readonly useUrlSearchParams: UnwrapRef< + (typeof import("@vueuse/core"))["useUrlSearchParams"] + >; + readonly useUserMedia: UnwrapRef< + (typeof import("@vueuse/core"))["useUserMedia"] + >; + readonly useVModel: UnwrapRef<(typeof import("@vueuse/core"))["useVModel"]>; + readonly useVModels: UnwrapRef< + (typeof import("@vueuse/core"))["useVModels"] + >; + readonly useVibrate: UnwrapRef< + (typeof import("@vueuse/core"))["useVibrate"] + >; + readonly useVirtualList: UnwrapRef< + (typeof import("@vueuse/core"))["useVirtualList"] + >; + readonly useWakeLock: UnwrapRef< + (typeof import("@vueuse/core"))["useWakeLock"] + >; + readonly useWebNotification: UnwrapRef< + (typeof import("@vueuse/core"))["useWebNotification"] + >; + readonly useWebSocket: UnwrapRef< + (typeof import("@vueuse/core"))["useWebSocket"] + >; + readonly useWebWorker: UnwrapRef< + (typeof import("@vueuse/core"))["useWebWorker"] + >; + readonly useWebWorkerFn: UnwrapRef< + (typeof import("@vueuse/core"))["useWebWorkerFn"] + >; + readonly useWindowFocus: UnwrapRef< + (typeof import("@vueuse/core"))["useWindowFocus"] + >; + readonly useWindowScroll: UnwrapRef< + (typeof import("@vueuse/core"))["useWindowScroll"] + >; + readonly useWindowSize: UnwrapRef< + (typeof import("@vueuse/core"))["useWindowSize"] + >; + readonly watch: UnwrapRef<(typeof import("vue"))["watch"]>; + readonly watchArray: UnwrapRef< + (typeof import("@vueuse/core"))["watchArray"] + >; + readonly watchAtMost: UnwrapRef< + (typeof import("@vueuse/core"))["watchAtMost"] + >; + readonly watchDebounced: UnwrapRef< + (typeof import("@vueuse/core"))["watchDebounced"] + >; + readonly watchDeep: UnwrapRef<(typeof import("@vueuse/core"))["watchDeep"]>; + readonly watchEffect: UnwrapRef<(typeof import("vue"))["watchEffect"]>; + readonly watchIgnorable: UnwrapRef< + (typeof import("@vueuse/core"))["watchIgnorable"] + >; + readonly watchImmediate: UnwrapRef< + (typeof import("@vueuse/core"))["watchImmediate"] + >; + readonly watchOnce: UnwrapRef<(typeof import("@vueuse/core"))["watchOnce"]>; + readonly watchPausable: UnwrapRef< + (typeof import("@vueuse/core"))["watchPausable"] + >; + readonly watchPostEffect: UnwrapRef< + (typeof import("vue"))["watchPostEffect"] + >; + readonly watchSyncEffect: UnwrapRef< + (typeof import("vue"))["watchSyncEffect"] + >; + readonly watchThrottled: UnwrapRef< + (typeof import("@vueuse/core"))["watchThrottled"] + >; + readonly watchTriggerable: UnwrapRef< + (typeof import("@vueuse/core"))["watchTriggerable"] + >; + readonly watchWithFilter: UnwrapRef< + (typeof import("@vueuse/core"))["watchWithFilter"] + >; + readonly whenever: UnwrapRef<(typeof import("@vueuse/core"))["whenever"]>; + } +} +declare module "@vue/runtime-core" { + interface GlobalComponents {} + interface ComponentCustomProperties { + readonly EffectScope: UnwrapRef<(typeof import("vue"))["EffectScope"]>; + readonly ElMessage: UnwrapRef< + (typeof import("element-plus/es"))["ElMessage"] + >; + readonly ElMessageBox: UnwrapRef< + (typeof import("element-plus/es"))["ElMessageBox"] + >; + readonly acceptHMRUpdate: UnwrapRef< + (typeof import("pinia"))["acceptHMRUpdate"] + >; + readonly asyncComputed: UnwrapRef< + (typeof import("@vueuse/core"))["asyncComputed"] + >; + readonly autoResetRef: UnwrapRef< + (typeof import("@vueuse/core"))["autoResetRef"] + >; + readonly computed: UnwrapRef<(typeof import("vue"))["computed"]>; + readonly computedAsync: UnwrapRef< + (typeof import("@vueuse/core"))["computedAsync"] + >; + readonly computedEager: UnwrapRef< + (typeof import("@vueuse/core"))["computedEager"] + >; + readonly computedInject: UnwrapRef< + (typeof import("@vueuse/core"))["computedInject"] + >; + readonly computedWithControl: UnwrapRef< + (typeof import("@vueuse/core"))["computedWithControl"] + >; + readonly controlledComputed: UnwrapRef< + (typeof import("@vueuse/core"))["controlledComputed"] + >; + readonly controlledRef: UnwrapRef< + (typeof import("@vueuse/core"))["controlledRef"] + >; + readonly createApp: UnwrapRef<(typeof import("vue"))["createApp"]>; + readonly createEventHook: UnwrapRef< + (typeof import("@vueuse/core"))["createEventHook"] + >; + readonly createGlobalState: UnwrapRef< + (typeof import("@vueuse/core"))["createGlobalState"] + >; + readonly createInjectionState: UnwrapRef< + (typeof import("@vueuse/core"))["createInjectionState"] + >; + readonly createPinia: UnwrapRef<(typeof import("pinia"))["createPinia"]>; + readonly createReactiveFn: UnwrapRef< + (typeof import("@vueuse/core"))["createReactiveFn"] + >; + readonly createReusableTemplate: UnwrapRef< + (typeof import("@vueuse/core"))["createReusableTemplate"] + >; + readonly createSharedComposable: UnwrapRef< + (typeof import("@vueuse/core"))["createSharedComposable"] + >; + readonly createTemplatePromise: UnwrapRef< + (typeof import("@vueuse/core"))["createTemplatePromise"] + >; + readonly createUnrefFn: UnwrapRef< + (typeof import("@vueuse/core"))["createUnrefFn"] + >; + readonly customRef: UnwrapRef<(typeof import("vue"))["customRef"]>; + readonly debouncedRef: UnwrapRef< + (typeof import("@vueuse/core"))["debouncedRef"] + >; + readonly debouncedWatch: UnwrapRef< + (typeof import("@vueuse/core"))["debouncedWatch"] + >; + readonly defineAsyncComponent: UnwrapRef< + (typeof import("vue"))["defineAsyncComponent"] + >; + readonly defineComponent: UnwrapRef< + (typeof import("vue"))["defineComponent"] + >; + readonly defineStore: UnwrapRef<(typeof import("pinia"))["defineStore"]>; + readonly eagerComputed: UnwrapRef< + (typeof import("@vueuse/core"))["eagerComputed"] + >; + readonly effectScope: UnwrapRef<(typeof import("vue"))["effectScope"]>; + readonly extendRef: UnwrapRef<(typeof import("@vueuse/core"))["extendRef"]>; + readonly getActivePinia: UnwrapRef< + (typeof import("pinia"))["getActivePinia"] + >; + readonly getCurrentInstance: UnwrapRef< + (typeof import("vue"))["getCurrentInstance"] + >; + readonly getCurrentScope: UnwrapRef< + (typeof import("vue"))["getCurrentScope"] + >; + readonly h: UnwrapRef<(typeof import("vue"))["h"]>; + readonly ignorableWatch: UnwrapRef< + (typeof import("@vueuse/core"))["ignorableWatch"] + >; + readonly inject: UnwrapRef<(typeof import("vue"))["inject"]>; + readonly injectLocal: UnwrapRef< + (typeof import("@vueuse/core"))["injectLocal"] + >; + readonly isDefined: UnwrapRef<(typeof import("@vueuse/core"))["isDefined"]>; + readonly isProxy: UnwrapRef<(typeof import("vue"))["isProxy"]>; + readonly isReactive: UnwrapRef<(typeof import("vue"))["isReactive"]>; + readonly isReadonly: UnwrapRef<(typeof import("vue"))["isReadonly"]>; + readonly isRef: UnwrapRef<(typeof import("vue"))["isRef"]>; + readonly makeDestructurable: UnwrapRef< + (typeof import("@vueuse/core"))["makeDestructurable"] + >; + readonly mapActions: UnwrapRef<(typeof import("pinia"))["mapActions"]>; + readonly mapGetters: UnwrapRef<(typeof import("pinia"))["mapGetters"]>; + readonly mapState: UnwrapRef<(typeof import("pinia"))["mapState"]>; + readonly mapStores: UnwrapRef<(typeof import("pinia"))["mapStores"]>; + readonly mapWritableState: UnwrapRef< + (typeof import("pinia"))["mapWritableState"] + >; + readonly markRaw: UnwrapRef<(typeof import("vue"))["markRaw"]>; + readonly nextTick: UnwrapRef<(typeof import("vue"))["nextTick"]>; + readonly onActivated: UnwrapRef<(typeof import("vue"))["onActivated"]>; + readonly onBeforeMount: UnwrapRef<(typeof import("vue"))["onBeforeMount"]>; + readonly onBeforeRouteLeave: UnwrapRef< + (typeof import("vue-router"))["onBeforeRouteLeave"] + >; + readonly onBeforeRouteUpdate: UnwrapRef< + (typeof import("vue-router"))["onBeforeRouteUpdate"] + >; + readonly onBeforeUnmount: UnwrapRef< + (typeof import("vue"))["onBeforeUnmount"] + >; + readonly onBeforeUpdate: UnwrapRef< + (typeof import("vue"))["onBeforeUpdate"] + >; + readonly onClickOutside: UnwrapRef< + (typeof import("@vueuse/core"))["onClickOutside"] + >; + readonly onDeactivated: UnwrapRef<(typeof import("vue"))["onDeactivated"]>; + readonly onErrorCaptured: UnwrapRef< + (typeof import("vue"))["onErrorCaptured"] + >; + readonly onKeyStroke: UnwrapRef< + (typeof import("@vueuse/core"))["onKeyStroke"] + >; + readonly onLongPress: UnwrapRef< + (typeof import("@vueuse/core"))["onLongPress"] + >; + readonly onMounted: UnwrapRef<(typeof import("vue"))["onMounted"]>; + readonly onRenderTracked: UnwrapRef< + (typeof import("vue"))["onRenderTracked"] + >; + readonly onRenderTriggered: UnwrapRef< + (typeof import("vue"))["onRenderTriggered"] + >; + readonly onScopeDispose: UnwrapRef< + (typeof import("vue"))["onScopeDispose"] + >; + readonly onServerPrefetch: UnwrapRef< + (typeof import("vue"))["onServerPrefetch"] + >; + readonly onStartTyping: UnwrapRef< + (typeof import("@vueuse/core"))["onStartTyping"] + >; + readonly onUnmounted: UnwrapRef<(typeof import("vue"))["onUnmounted"]>; + readonly onUpdated: UnwrapRef<(typeof import("vue"))["onUpdated"]>; + readonly pausableWatch: UnwrapRef< + (typeof import("@vueuse/core"))["pausableWatch"] + >; + readonly provide: UnwrapRef<(typeof import("vue"))["provide"]>; + readonly provideLocal: UnwrapRef< + (typeof import("@vueuse/core"))["provideLocal"] + >; + readonly reactify: UnwrapRef<(typeof import("@vueuse/core"))["reactify"]>; + readonly reactifyObject: UnwrapRef< + (typeof import("@vueuse/core"))["reactifyObject"] + >; + readonly reactive: UnwrapRef<(typeof import("vue"))["reactive"]>; + readonly reactiveComputed: UnwrapRef< + (typeof import("@vueuse/core"))["reactiveComputed"] + >; + readonly reactiveOmit: UnwrapRef< + (typeof import("@vueuse/core"))["reactiveOmit"] + >; + readonly reactivePick: UnwrapRef< + (typeof import("@vueuse/core"))["reactivePick"] + >; + readonly readonly: UnwrapRef<(typeof import("vue"))["readonly"]>; + readonly ref: UnwrapRef<(typeof import("vue"))["ref"]>; + readonly refAutoReset: UnwrapRef< + (typeof import("@vueuse/core"))["refAutoReset"] + >; + readonly refDebounced: UnwrapRef< + (typeof import("@vueuse/core"))["refDebounced"] + >; + readonly refDefault: UnwrapRef< + (typeof import("@vueuse/core"))["refDefault"] + >; + readonly refThrottled: UnwrapRef< + (typeof import("@vueuse/core"))["refThrottled"] + >; + readonly refWithControl: UnwrapRef< + (typeof import("@vueuse/core"))["refWithControl"] + >; + readonly resolveComponent: UnwrapRef< + (typeof import("vue"))["resolveComponent"] + >; + readonly resolveRef: UnwrapRef< + (typeof import("@vueuse/core"))["resolveRef"] + >; + readonly resolveUnref: UnwrapRef< + (typeof import("@vueuse/core"))["resolveUnref"] + >; + readonly setActivePinia: UnwrapRef< + (typeof import("pinia"))["setActivePinia"] + >; + readonly setMapStoreSuffix: UnwrapRef< + (typeof import("pinia"))["setMapStoreSuffix"] + >; + readonly shallowReactive: UnwrapRef< + (typeof import("vue"))["shallowReactive"] + >; + readonly shallowReadonly: UnwrapRef< + (typeof import("vue"))["shallowReadonly"] + >; + readonly shallowRef: UnwrapRef<(typeof import("vue"))["shallowRef"]>; + readonly storeToRefs: UnwrapRef<(typeof import("pinia"))["storeToRefs"]>; + readonly syncRef: UnwrapRef<(typeof import("@vueuse/core"))["syncRef"]>; + readonly syncRefs: UnwrapRef<(typeof import("@vueuse/core"))["syncRefs"]>; + readonly templateRef: UnwrapRef< + (typeof import("@vueuse/core"))["templateRef"] + >; + readonly throttledRef: UnwrapRef< + (typeof import("@vueuse/core"))["throttledRef"] + >; + readonly throttledWatch: UnwrapRef< + (typeof import("@vueuse/core"))["throttledWatch"] + >; + readonly toRaw: UnwrapRef<(typeof import("vue"))["toRaw"]>; + readonly toReactive: UnwrapRef< + (typeof import("@vueuse/core"))["toReactive"] + >; + readonly toRef: UnwrapRef<(typeof import("vue"))["toRef"]>; + readonly toRefs: UnwrapRef<(typeof import("vue"))["toRefs"]>; + readonly toValue: UnwrapRef<(typeof import("vue"))["toValue"]>; + readonly triggerRef: UnwrapRef<(typeof import("vue"))["triggerRef"]>; + readonly tryOnBeforeMount: UnwrapRef< + (typeof import("@vueuse/core"))["tryOnBeforeMount"] + >; + readonly tryOnBeforeUnmount: UnwrapRef< + (typeof import("@vueuse/core"))["tryOnBeforeUnmount"] + >; + readonly tryOnMounted: UnwrapRef< + (typeof import("@vueuse/core"))["tryOnMounted"] + >; + readonly tryOnScopeDispose: UnwrapRef< + (typeof import("@vueuse/core"))["tryOnScopeDispose"] + >; + readonly tryOnUnmounted: UnwrapRef< + (typeof import("@vueuse/core"))["tryOnUnmounted"] + >; + readonly unref: UnwrapRef<(typeof import("vue"))["unref"]>; + readonly unrefElement: UnwrapRef< + (typeof import("@vueuse/core"))["unrefElement"] + >; + readonly until: UnwrapRef<(typeof import("@vueuse/core"))["until"]>; + readonly useActiveElement: UnwrapRef< + (typeof import("@vueuse/core"))["useActiveElement"] + >; + readonly useAnimate: UnwrapRef< + (typeof import("@vueuse/core"))["useAnimate"] + >; + readonly useArrayDifference: UnwrapRef< + (typeof import("@vueuse/core"))["useArrayDifference"] + >; + readonly useArrayEvery: UnwrapRef< + (typeof import("@vueuse/core"))["useArrayEvery"] + >; + readonly useArrayFilter: UnwrapRef< + (typeof import("@vueuse/core"))["useArrayFilter"] + >; + readonly useArrayFind: UnwrapRef< + (typeof import("@vueuse/core"))["useArrayFind"] + >; + readonly useArrayFindIndex: UnwrapRef< + (typeof import("@vueuse/core"))["useArrayFindIndex"] + >; + readonly useArrayFindLast: UnwrapRef< + (typeof import("@vueuse/core"))["useArrayFindLast"] + >; + readonly useArrayIncludes: UnwrapRef< + (typeof import("@vueuse/core"))["useArrayIncludes"] + >; + readonly useArrayJoin: UnwrapRef< + (typeof import("@vueuse/core"))["useArrayJoin"] + >; + readonly useArrayMap: UnwrapRef< + (typeof import("@vueuse/core"))["useArrayMap"] + >; + readonly useArrayReduce: UnwrapRef< + (typeof import("@vueuse/core"))["useArrayReduce"] + >; + readonly useArraySome: UnwrapRef< + (typeof import("@vueuse/core"))["useArraySome"] + >; + readonly useArrayUnique: UnwrapRef< + (typeof import("@vueuse/core"))["useArrayUnique"] + >; + readonly useAsyncQueue: UnwrapRef< + (typeof import("@vueuse/core"))["useAsyncQueue"] + >; + readonly useAsyncState: UnwrapRef< + (typeof import("@vueuse/core"))["useAsyncState"] + >; + readonly useAttrs: UnwrapRef<(typeof import("vue"))["useAttrs"]>; + readonly useBase64: UnwrapRef<(typeof import("@vueuse/core"))["useBase64"]>; + readonly useBattery: UnwrapRef< + (typeof import("@vueuse/core"))["useBattery"] + >; + readonly useBluetooth: UnwrapRef< + (typeof import("@vueuse/core"))["useBluetooth"] + >; + readonly useBreakpoints: UnwrapRef< + (typeof import("@vueuse/core"))["useBreakpoints"] + >; + readonly useBroadcastChannel: UnwrapRef< + (typeof import("@vueuse/core"))["useBroadcastChannel"] + >; + readonly useBrowserLocation: UnwrapRef< + (typeof import("@vueuse/core"))["useBrowserLocation"] + >; + readonly useCached: UnwrapRef<(typeof import("@vueuse/core"))["useCached"]>; + readonly useClipboard: UnwrapRef< + (typeof import("@vueuse/core"))["useClipboard"] + >; + readonly useClipboardItems: UnwrapRef< + (typeof import("@vueuse/core"))["useClipboardItems"] + >; + readonly useCloned: UnwrapRef<(typeof import("@vueuse/core"))["useCloned"]>; + readonly useColorMode: UnwrapRef< + (typeof import("@vueuse/core"))["useColorMode"] + >; + readonly useConfirmDialog: UnwrapRef< + (typeof import("@vueuse/core"))["useConfirmDialog"] + >; + readonly useCounter: UnwrapRef< + (typeof import("@vueuse/core"))["useCounter"] + >; + readonly useCssModule: UnwrapRef<(typeof import("vue"))["useCssModule"]>; + readonly useCssVar: UnwrapRef<(typeof import("@vueuse/core"))["useCssVar"]>; + readonly useCssVars: UnwrapRef<(typeof import("vue"))["useCssVars"]>; + readonly useCurrentElement: UnwrapRef< + (typeof import("@vueuse/core"))["useCurrentElement"] + >; + readonly useCycleList: UnwrapRef< + (typeof import("@vueuse/core"))["useCycleList"] + >; + readonly useDark: UnwrapRef<(typeof import("@vueuse/core"))["useDark"]>; + readonly useDateFormat: UnwrapRef< + (typeof import("@vueuse/core"))["useDateFormat"] + >; + readonly useDebounce: UnwrapRef< + (typeof import("@vueuse/core"))["useDebounce"] + >; + readonly useDebounceFn: UnwrapRef< + (typeof import("@vueuse/core"))["useDebounceFn"] + >; + readonly useDebouncedRefHistory: UnwrapRef< + (typeof import("@vueuse/core"))["useDebouncedRefHistory"] + >; + readonly useDeviceMotion: UnwrapRef< + (typeof import("@vueuse/core"))["useDeviceMotion"] + >; + readonly useDeviceOrientation: UnwrapRef< + (typeof import("@vueuse/core"))["useDeviceOrientation"] + >; + readonly useDevicePixelRatio: UnwrapRef< + (typeof import("@vueuse/core"))["useDevicePixelRatio"] + >; + readonly useDevicesList: UnwrapRef< + (typeof import("@vueuse/core"))["useDevicesList"] + >; + readonly useDisplayMedia: UnwrapRef< + (typeof import("@vueuse/core"))["useDisplayMedia"] + >; + readonly useDocumentVisibility: UnwrapRef< + (typeof import("@vueuse/core"))["useDocumentVisibility"] + >; + readonly useDraggable: UnwrapRef< + (typeof import("@vueuse/core"))["useDraggable"] + >; + readonly useDropZone: UnwrapRef< + (typeof import("@vueuse/core"))["useDropZone"] + >; + readonly useElementBounding: UnwrapRef< + (typeof import("@vueuse/core"))["useElementBounding"] + >; + readonly useElementByPoint: UnwrapRef< + (typeof import("@vueuse/core"))["useElementByPoint"] + >; + readonly useElementHover: UnwrapRef< + (typeof import("@vueuse/core"))["useElementHover"] + >; + readonly useElementSize: UnwrapRef< + (typeof import("@vueuse/core"))["useElementSize"] + >; + readonly useElementVisibility: UnwrapRef< + (typeof import("@vueuse/core"))["useElementVisibility"] + >; + readonly useEventBus: UnwrapRef< + (typeof import("@vueuse/core"))["useEventBus"] + >; + readonly useEventListener: UnwrapRef< + (typeof import("@vueuse/core"))["useEventListener"] + >; + readonly useEventSource: UnwrapRef< + (typeof import("@vueuse/core"))["useEventSource"] + >; + readonly useEyeDropper: UnwrapRef< + (typeof import("@vueuse/core"))["useEyeDropper"] + >; + readonly useFavicon: UnwrapRef< + (typeof import("@vueuse/core"))["useFavicon"] + >; + readonly useFetch: UnwrapRef<(typeof import("@vueuse/core"))["useFetch"]>; + readonly useFileDialog: UnwrapRef< + (typeof import("@vueuse/core"))["useFileDialog"] + >; + readonly useFileSystemAccess: UnwrapRef< + (typeof import("@vueuse/core"))["useFileSystemAccess"] + >; + readonly useFocus: UnwrapRef<(typeof import("@vueuse/core"))["useFocus"]>; + readonly useFocusWithin: UnwrapRef< + (typeof import("@vueuse/core"))["useFocusWithin"] + >; + readonly useFps: UnwrapRef<(typeof import("@vueuse/core"))["useFps"]>; + readonly useFullscreen: UnwrapRef< + (typeof import("@vueuse/core"))["useFullscreen"] + >; + readonly useGamepad: UnwrapRef< + (typeof import("@vueuse/core"))["useGamepad"] + >; + readonly useGeolocation: UnwrapRef< + (typeof import("@vueuse/core"))["useGeolocation"] + >; + readonly useI18n: UnwrapRef<(typeof import("vue-i18n"))["useI18n"]>; + readonly useIdle: UnwrapRef<(typeof import("@vueuse/core"))["useIdle"]>; + readonly useImage: UnwrapRef<(typeof import("@vueuse/core"))["useImage"]>; + readonly useInfiniteScroll: UnwrapRef< + (typeof import("@vueuse/core"))["useInfiniteScroll"] + >; + readonly useIntersectionObserver: UnwrapRef< + (typeof import("@vueuse/core"))["useIntersectionObserver"] + >; + readonly useInterval: UnwrapRef< + (typeof import("@vueuse/core"))["useInterval"] + >; + readonly useIntervalFn: UnwrapRef< + (typeof import("@vueuse/core"))["useIntervalFn"] + >; + readonly useKeyModifier: UnwrapRef< + (typeof import("@vueuse/core"))["useKeyModifier"] + >; + readonly useLastChanged: UnwrapRef< + (typeof import("@vueuse/core"))["useLastChanged"] + >; + readonly useLink: UnwrapRef<(typeof import("vue-router"))["useLink"]>; + readonly useLocalStorage: UnwrapRef< + (typeof import("@vueuse/core"))["useLocalStorage"] + >; + readonly useMagicKeys: UnwrapRef< + (typeof import("@vueuse/core"))["useMagicKeys"] + >; + readonly useManualRefHistory: UnwrapRef< + (typeof import("@vueuse/core"))["useManualRefHistory"] + >; + readonly useMediaControls: UnwrapRef< + (typeof import("@vueuse/core"))["useMediaControls"] + >; + readonly useMediaQuery: UnwrapRef< + (typeof import("@vueuse/core"))["useMediaQuery"] + >; + readonly useMemoize: UnwrapRef< + (typeof import("@vueuse/core"))["useMemoize"] + >; + readonly useMemory: UnwrapRef<(typeof import("@vueuse/core"))["useMemory"]>; + readonly useMounted: UnwrapRef< + (typeof import("@vueuse/core"))["useMounted"] + >; + readonly useMouse: UnwrapRef<(typeof import("@vueuse/core"))["useMouse"]>; + readonly useMouseInElement: UnwrapRef< + (typeof import("@vueuse/core"))["useMouseInElement"] + >; + readonly useMousePressed: UnwrapRef< + (typeof import("@vueuse/core"))["useMousePressed"] + >; + readonly useMutationObserver: UnwrapRef< + (typeof import("@vueuse/core"))["useMutationObserver"] + >; + readonly useNavigatorLanguage: UnwrapRef< + (typeof import("@vueuse/core"))["useNavigatorLanguage"] + >; + readonly useNetwork: UnwrapRef< + (typeof import("@vueuse/core"))["useNetwork"] + >; + readonly useNow: UnwrapRef<(typeof import("@vueuse/core"))["useNow"]>; + readonly useObjectUrl: UnwrapRef< + (typeof import("@vueuse/core"))["useObjectUrl"] + >; + readonly useOffsetPagination: UnwrapRef< + (typeof import("@vueuse/core"))["useOffsetPagination"] + >; + readonly useOnline: UnwrapRef<(typeof import("@vueuse/core"))["useOnline"]>; + readonly usePageLeave: UnwrapRef< + (typeof import("@vueuse/core"))["usePageLeave"] + >; + readonly useParallax: UnwrapRef< + (typeof import("@vueuse/core"))["useParallax"] + >; + readonly useParentElement: UnwrapRef< + (typeof import("@vueuse/core"))["useParentElement"] + >; + readonly usePerformanceObserver: UnwrapRef< + (typeof import("@vueuse/core"))["usePerformanceObserver"] + >; + readonly usePermission: UnwrapRef< + (typeof import("@vueuse/core"))["usePermission"] + >; + readonly usePointer: UnwrapRef< + (typeof import("@vueuse/core"))["usePointer"] + >; + readonly usePointerLock: UnwrapRef< + (typeof import("@vueuse/core"))["usePointerLock"] + >; + readonly usePointerSwipe: UnwrapRef< + (typeof import("@vueuse/core"))["usePointerSwipe"] + >; + readonly usePreferredColorScheme: UnwrapRef< + (typeof import("@vueuse/core"))["usePreferredColorScheme"] + >; + readonly usePreferredContrast: UnwrapRef< + (typeof import("@vueuse/core"))["usePreferredContrast"] + >; + readonly usePreferredDark: UnwrapRef< + (typeof import("@vueuse/core"))["usePreferredDark"] + >; + readonly usePreferredLanguages: UnwrapRef< + (typeof import("@vueuse/core"))["usePreferredLanguages"] + >; + readonly usePreferredReducedMotion: UnwrapRef< + (typeof import("@vueuse/core"))["usePreferredReducedMotion"] + >; + readonly usePrevious: UnwrapRef< + (typeof import("@vueuse/core"))["usePrevious"] + >; + readonly useRafFn: UnwrapRef<(typeof import("@vueuse/core"))["useRafFn"]>; + readonly useRefHistory: UnwrapRef< + (typeof import("@vueuse/core"))["useRefHistory"] + >; + readonly useResizeObserver: UnwrapRef< + (typeof import("@vueuse/core"))["useResizeObserver"] + >; + readonly useRoute: UnwrapRef<(typeof import("vue-router"))["useRoute"]>; + readonly useRouter: UnwrapRef<(typeof import("vue-router"))["useRouter"]>; + readonly useScreenOrientation: UnwrapRef< + (typeof import("@vueuse/core"))["useScreenOrientation"] + >; + readonly useScreenSafeArea: UnwrapRef< + (typeof import("@vueuse/core"))["useScreenSafeArea"] + >; + readonly useScriptTag: UnwrapRef< + (typeof import("@vueuse/core"))["useScriptTag"] + >; + readonly useScroll: UnwrapRef<(typeof import("@vueuse/core"))["useScroll"]>; + readonly useScrollLock: UnwrapRef< + (typeof import("@vueuse/core"))["useScrollLock"] + >; + readonly useSessionStorage: UnwrapRef< + (typeof import("@vueuse/core"))["useSessionStorage"] + >; + readonly useShare: UnwrapRef<(typeof import("@vueuse/core"))["useShare"]>; + readonly useSlots: UnwrapRef<(typeof import("vue"))["useSlots"]>; + readonly useSorted: UnwrapRef<(typeof import("@vueuse/core"))["useSorted"]>; + readonly useSpeechRecognition: UnwrapRef< + (typeof import("@vueuse/core"))["useSpeechRecognition"] + >; + readonly useSpeechSynthesis: UnwrapRef< + (typeof import("@vueuse/core"))["useSpeechSynthesis"] + >; + readonly useStepper: UnwrapRef< + (typeof import("@vueuse/core"))["useStepper"] + >; + readonly useStorage: UnwrapRef< + (typeof import("@vueuse/core"))["useStorage"] + >; + readonly useStorageAsync: UnwrapRef< + (typeof import("@vueuse/core"))["useStorageAsync"] + >; + readonly useStyleTag: UnwrapRef< + (typeof import("@vueuse/core"))["useStyleTag"] + >; + readonly useSupported: UnwrapRef< + (typeof import("@vueuse/core"))["useSupported"] + >; + readonly useSwipe: UnwrapRef<(typeof import("@vueuse/core"))["useSwipe"]>; + readonly useTemplateRefsList: UnwrapRef< + (typeof import("@vueuse/core"))["useTemplateRefsList"] + >; + readonly useTextDirection: UnwrapRef< + (typeof import("@vueuse/core"))["useTextDirection"] + >; + readonly useTextSelection: UnwrapRef< + (typeof import("@vueuse/core"))["useTextSelection"] + >; + readonly useTextareaAutosize: UnwrapRef< + (typeof import("@vueuse/core"))["useTextareaAutosize"] + >; + readonly useThrottle: UnwrapRef< + (typeof import("@vueuse/core"))["useThrottle"] + >; + readonly useThrottleFn: UnwrapRef< + (typeof import("@vueuse/core"))["useThrottleFn"] + >; + readonly useThrottledRefHistory: UnwrapRef< + (typeof import("@vueuse/core"))["useThrottledRefHistory"] + >; + readonly useTimeAgo: UnwrapRef< + (typeof import("@vueuse/core"))["useTimeAgo"] + >; + readonly useTimeout: UnwrapRef< + (typeof import("@vueuse/core"))["useTimeout"] + >; + readonly useTimeoutFn: UnwrapRef< + (typeof import("@vueuse/core"))["useTimeoutFn"] + >; + readonly useTimeoutPoll: UnwrapRef< + (typeof import("@vueuse/core"))["useTimeoutPoll"] + >; + readonly useTimestamp: UnwrapRef< + (typeof import("@vueuse/core"))["useTimestamp"] + >; + readonly useTitle: UnwrapRef<(typeof import("@vueuse/core"))["useTitle"]>; + readonly useToNumber: UnwrapRef< + (typeof import("@vueuse/core"))["useToNumber"] + >; + readonly useToString: UnwrapRef< + (typeof import("@vueuse/core"))["useToString"] + >; + readonly useToggle: UnwrapRef<(typeof import("@vueuse/core"))["useToggle"]>; + readonly useTransition: UnwrapRef< + (typeof import("@vueuse/core"))["useTransition"] + >; + readonly useUrlSearchParams: UnwrapRef< + (typeof import("@vueuse/core"))["useUrlSearchParams"] + >; + readonly useUserMedia: UnwrapRef< + (typeof import("@vueuse/core"))["useUserMedia"] + >; + readonly useVModel: UnwrapRef<(typeof import("@vueuse/core"))["useVModel"]>; + readonly useVModels: UnwrapRef< + (typeof import("@vueuse/core"))["useVModels"] + >; + readonly useVibrate: UnwrapRef< + (typeof import("@vueuse/core"))["useVibrate"] + >; + readonly useVirtualList: UnwrapRef< + (typeof import("@vueuse/core"))["useVirtualList"] + >; + readonly useWakeLock: UnwrapRef< + (typeof import("@vueuse/core"))["useWakeLock"] + >; + readonly useWebNotification: UnwrapRef< + (typeof import("@vueuse/core"))["useWebNotification"] + >; + readonly useWebSocket: UnwrapRef< + (typeof import("@vueuse/core"))["useWebSocket"] + >; + readonly useWebWorker: UnwrapRef< + (typeof import("@vueuse/core"))["useWebWorker"] + >; + readonly useWebWorkerFn: UnwrapRef< + (typeof import("@vueuse/core"))["useWebWorkerFn"] + >; + readonly useWindowFocus: UnwrapRef< + (typeof import("@vueuse/core"))["useWindowFocus"] + >; + readonly useWindowScroll: UnwrapRef< + (typeof import("@vueuse/core"))["useWindowScroll"] + >; + readonly useWindowSize: UnwrapRef< + (typeof import("@vueuse/core"))["useWindowSize"] + >; + readonly watch: UnwrapRef<(typeof import("vue"))["watch"]>; + readonly watchArray: UnwrapRef< + (typeof import("@vueuse/core"))["watchArray"] + >; + readonly watchAtMost: UnwrapRef< + (typeof import("@vueuse/core"))["watchAtMost"] + >; + readonly watchDebounced: UnwrapRef< + (typeof import("@vueuse/core"))["watchDebounced"] + >; + readonly watchDeep: UnwrapRef<(typeof import("@vueuse/core"))["watchDeep"]>; + readonly watchEffect: UnwrapRef<(typeof import("vue"))["watchEffect"]>; + readonly watchIgnorable: UnwrapRef< + (typeof import("@vueuse/core"))["watchIgnorable"] + >; + readonly watchImmediate: UnwrapRef< + (typeof import("@vueuse/core"))["watchImmediate"] + >; + readonly watchOnce: UnwrapRef<(typeof import("@vueuse/core"))["watchOnce"]>; + readonly watchPausable: UnwrapRef< + (typeof import("@vueuse/core"))["watchPausable"] + >; + readonly watchPostEffect: UnwrapRef< + (typeof import("vue"))["watchPostEffect"] + >; + readonly watchSyncEffect: UnwrapRef< + (typeof import("vue"))["watchSyncEffect"] + >; + readonly watchThrottled: UnwrapRef< + (typeof import("@vueuse/core"))["watchThrottled"] + >; + readonly watchTriggerable: UnwrapRef< + (typeof import("@vueuse/core"))["watchTriggerable"] + >; + readonly watchWithFilter: UnwrapRef< + (typeof import("@vueuse/core"))["watchWithFilter"] + >; + readonly whenever: UnwrapRef<(typeof import("@vueuse/core"))["whenever"]>; + } +} diff --git a/src/types/components.d.ts b/src/types/components.d.ts new file mode 100644 index 0000000..094c767 --- /dev/null +++ b/src/types/components.d.ts @@ -0,0 +1,107 @@ +/* eslint-disable */ +/* prettier-ignore */ +// @ts-nocheck +// Generated by unplugin-vue-components +// Read more: https://github.com/vuejs/core/pull/3399 +export {} + +declare module "vue" { + export interface GlobalComponents { + AppLink: (typeof import("./../components/AppLink/index.vue"))["default"]; + AppMain: (typeof import("./../layout/components/AppMain/index.vue"))["default"]; + VisitTrend: (typeof import("./../views/dashboard/components/VisitTrend.vue"))["default"]; + Breadcrumb: (typeof import("./../components/Breadcrumb/index.vue"))["default"]; + CopyButton: (typeof import("./../components/CopyButton/index.vue"))["default"]; + CURD: (typeof import("./../components/CURD/index.vue"))["default"]; + DeptTree: (typeof import("./../views/system/user/components/dept-tree.vue"))["default"]; + UserImport: (typeof import("./../views/system/user/components/user-import.vue"))["default"]; + Dictionary: (typeof import("./../components/Dictionary/index.vue"))["default"]; + DictItem: (typeof import("./../views/system/dict/components/dict-item.vue"))["default"]; + ElBacktop: (typeof import("element-plus/es"))["ElBacktop"]; + ElBreadcrumb: (typeof import("element-plus/es"))["ElBreadcrumb"]; + ElBreadcrumbItem: (typeof import("element-plus/es"))["ElBreadcrumbItem"]; + ElButton: (typeof import("element-plus/es"))["ElButton"]; + ElCard: (typeof import("element-plus/es"))["ElCard"]; + ElCheckbox: (typeof import("element-plus/es"))["ElCheckbox"]; + ElCheckboxGroup: (typeof import("element-plus/es"))["ElCheckboxGroup"]; + ElCol: (typeof import("element-plus/es"))["ElCol"]; + ElColorPicker: (typeof import("element-plus/es"))["ElColorPicker"]; + ElConfigProvider: (typeof import("element-plus/es"))["ElConfigProvider"]; + ElDatePicker: (typeof import("element-plus/es"))["ElDatePicker"]; + ElDialog: (typeof import("element-plus/es"))["ElDialog"]; + ElDivider: (typeof import("element-plus/es"))["ElDivider"]; + ElDrawer: (typeof import("element-plus/es"))["ElDrawer"]; + ElDropdown: (typeof import("element-plus/es"))["ElDropdown"]; + ElDropdownItem: (typeof import("element-plus/es"))["ElDropdownItem"]; + ElDropdownMenu: (typeof import("element-plus/es"))["ElDropdownMenu"]; + ElForm: (typeof import("element-plus/es"))["ElForm"]; + ElFormItem: (typeof import("element-plus/es"))["ElFormItem"]; + ElIcon: (typeof import("element-plus/es"))["ElIcon"]; + ElImage: (typeof import("element-plus/es"))["ElImage"]; + ElInput: (typeof import("element-plus/es"))["ElInput"]; + ElInputNumber: (typeof import("element-plus/es"))["ElInputNumber"]; + ElLink: (typeof import("element-plus/es"))["ElLink"]; + ElMenu: (typeof import("element-plus/es"))["ElMenu"]; + ElMenuItem: (typeof import("element-plus/es"))["ElMenuItem"]; + ElOption: (typeof import("element-plus/es"))["ElOption"]; + ElPagination: (typeof import("element-plus/es"))["ElPagination"]; + ElPopover: (typeof import("element-plus/es"))["ElPopover"]; + ElRadio: (typeof import("element-plus/es"))["ElRadio"]; + ElRadioGroup: (typeof import("element-plus/es"))["ElRadioGroup"]; + ElRow: (typeof import("element-plus/es"))["ElRow"]; + ElScrollbar: (typeof import("element-plus/es"))["ElScrollbar"]; + ElSelect: (typeof import("element-plus/es"))["ElSelect"]; + ElStatistic: (typeof import("element-plus/es"))["ElStatistic"]; + ElSubMenu: (typeof import("element-plus/es"))["ElSubMenu"]; + ElSwitch: (typeof import("element-plus/es"))["ElSwitch"]; + ElTable: (typeof import("element-plus/es"))["ElTable"]; + ElTableColumn: (typeof import("element-plus/es"))["ElTableColumn"]; + ElTag: (typeof import("element-plus/es"))["ElTag"]; + ElText: (typeof import("element-plus/es"))["ElText"]; + ElTooltip: (typeof import("element-plus/es"))["ElTooltip"]; + ElTreeSelect: (typeof import("element-plus/es"))["ElTreeSelect"]; + ElUpload: (typeof import("element-plus/es"))["ElUpload"]; + ElWatermark: (typeof import("element-plus/es"))["ElWatermark"]; + ElSkeleton: (typeof import("element-plus/es"))["ElSkeleton"]; + Form: (typeof import("./../components/CURD/Form.vue"))["default"]; + FunnelChart: (typeof import("./../views/dashboard/components/FunnelChart.vue"))["default"]; + GithubCorner: (typeof import("./../components/GithubCorner/index.vue"))["default"]; + Hamburger: (typeof import("./../components/Hamburger/index.vue"))["default"]; + IconSelect: (typeof import("./../components/IconSelect/index.vue"))["default"]; + IEpArrowDown: (typeof import("~icons/ep/arrow-down"))["default"]; + IEpArrowUp: (typeof import("~icons/ep/arrow-up"))["default"]; + IEpClose: (typeof import("~icons/ep/close"))["default"]; + IEpDownload: (typeof import("~icons/ep/download"))["default"]; + LangSelect: (typeof import("./../components/LangSelect/index.vue"))["default"]; + LayoutSelect: (typeof import("./../layout/components/Settings/components/LayoutSelect.vue"))["default"]; + MultiUpload: (typeof import("./../components/Upload/MultiUpload.vue"))["default"]; + NavBar: (typeof import("./../layout/components/NavBar/index.vue"))["default"]; + NavbarAction: (typeof import("./../layout/components/NavBar/components/NavbarAction.vue"))["default"]; + PageContent: (typeof import("./../components/CURD/PageContent.vue"))["default"]; + PageForm: (typeof import("./../components/CURD/PageForm.vue"))["default"]; + PageModal: (typeof import("./../components/CURD/PageModal.vue"))["default"]; + PageSearch: (typeof import("./../components/CURD/PageSearch.vue"))["default"]; + Pagination: (typeof import("./../components/Pagination/index.vue"))["default"]; + PieChart: (typeof import("./../views/dashboard/components/PieChart.vue"))["default"]; + RadarChart: (typeof import("./../views/dashboard/components/RadarChart.vue"))["default"]; + RouterLink: (typeof import("vue-router"))["RouterLink"]; + RouterView: (typeof import("vue-router"))["RouterView"]; + Settings: (typeof import("./../layout/components/Settings/index.vue"))["default"]; + Sidebar: (typeof import("./../layout/components/Sidebar/index.vue"))["default"]; + SidebarLogo: (typeof import("./../layout/components/Sidebar/components/SidebarLogo.vue"))["default"]; + SidebarMenu: (typeof import("./../layout/components/Sidebar/components/SidebarMenu.vue"))["default"]; + SidebarMenuItem: (typeof import("./../layout/components/Sidebar/components/SidebarMenuItem.vue"))["default"]; + SidebarMenuItemTitle: (typeof import("./../layout/components/Sidebar/components/SidebarMenuItemTitle.vue"))["default"]; + SidebarMixTopMenu: (typeof import("./../layout/components/Sidebar/components/SidebarMixTopMenu.vue"))["default"]; + SingleUpload: (typeof import("./../components/Upload/SingleUpload.vue"))["default"]; + SizeSelect: (typeof import("./../components/SizeSelect/index.vue"))["default"]; + SvgIcon: (typeof import("./../components/SvgIcon/index.vue"))["default"]; + TableSelect: (typeof import("./../components/TableSelect/index.vue"))["default"]; + TagsView: (typeof import("./../layout/components/TagsView/index.vue"))["default"]; + ThemeColorPicker: (typeof import("./../layout/components/Settings/components/ThemeColorPicker.vue"))["default"]; + WangEditor: (typeof import("./../components/WangEditor/index.vue"))["default"]; + } + export interface ComponentCustomProperties { + vLoading: (typeof import("element-plus/es"))["ElLoadingDirective"]; + } +} diff --git a/src/types/env.d.ts b/src/types/env.d.ts new file mode 100644 index 0000000..7ed1377 --- /dev/null +++ b/src/types/env.d.ts @@ -0,0 +1,40 @@ +// https://cn.vitejs.dev/guide/env-and-mode + +declare module "*.vue" { + import { DefineComponent } from "vue"; + // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types + const component: DefineComponent<{}, {}, any>; + export default component; +} + +// TypeScript 类型提示都为 string: https://github.com/vitejs/vite/issues/6930 +interface ImportMetaEnv { + /** 应用端口 */ + VITE_APP_PORT: number; + /** API 基础路径(代理前缀) */ + VITE_APP_BASE_API: string; + /** API 地址 */ + VITE_APP_API_URL: string; + /** 是否开启 Mock 服务 */ + VITE_MOCK_DEV_SERVER: boolean; +} + +interface ImportMeta { + readonly env: ImportMetaEnv; +} + +/** + * 平台的名称、版本、运行所需的`node`版本、依赖、构建时间的类型提示 + */ +declare const __APP_INFO__: { + pkg: { + name: string; + version: string; + engines: { + node: string; + }; + dependencies: Record; + devDependencies: Record; + }; + buildTimestamp: number; +}; diff --git a/src/types/global.d.ts b/src/types/global.d.ts new file mode 100644 index 0000000..146d5bf --- /dev/null +++ b/src/types/global.d.ts @@ -0,0 +1,95 @@ +declare global { + /** + * 响应数据 + */ + interface ResponseData { + code: string; + data: T; + msg: string; + } + + /** + * 分页查询参数 + */ + interface PageQuery { + pageNum: number; + pageSize: number; + } + + /** + * 分页响应对象 + */ + interface PageResult { + /** 数据列表 */ + list: T; + /** 总数 */ + total: number; + } + + /** + * 页签对象 + */ + interface TagView { + /** 页签名称 */ + name: string; + /** 页签标题 */ + title: string; + /** 页签路由路径 */ + path: string; + /** 页签路由完整路径 */ + fullPath: string; + /** 页签图标 */ + icon?: string; + /** 是否固定页签 */ + affix?: boolean; + /** 是否开启缓存 */ + keepAlive?: boolean; + /** 路由查询参数 */ + query?: any; + } + + /** + * 系统设置 + */ + interface AppSettings { + /** 系统标题 */ + title: string; + /** 系统版本 */ + version: string; + /** 是否显示设置 */ + showSettings: boolean; + /** 是否固定头部 */ + fixedHeader: boolean; + /** 是否显示多标签导航 */ + tagsView: boolean; + /** 是否显示侧边栏Logo */ + sidebarLogo: boolean; + /** 导航栏布局(left|top|mix) */ + layout: string; + /** 主题颜色 */ + themeColor: string; + /** 主题模式(dark|light) */ + theme: string; + /** 布局大小(default |large |small) */ + size: string; + /** 语言( zh-cn| en) */ + language: string; + /** 是否开启水印 */ + watermarkEnabled: boolean; + /** 水印内容 */ + watermarkContent: string; + } + + /** + * 组件数据源 + */ + interface OptionType { + /** 值 */ + value: string | number; + /** 文本 */ + label: string; + /** 子列表 */ + children?: OptionType[]; + } +} +export {}; diff --git a/src/types/json-bigint.d.ts b/src/types/json-bigint.d.ts new file mode 100644 index 0000000..a3f4812 --- /dev/null +++ b/src/types/json-bigint.d.ts @@ -0,0 +1 @@ +declare module "json-bigint"; diff --git a/src/types/router.d.ts b/src/types/router.d.ts new file mode 100644 index 0000000..a175ea1 --- /dev/null +++ b/src/types/router.d.ts @@ -0,0 +1,54 @@ +import "vue-router"; + +declare module "vue-router" { + // https://router.vuejs.org/zh/guide/advanced/meta.html#typescript + // 可以通过扩展 RouteMeta 接口来输入 meta 字段 + interface RouteMeta { + /** + * 菜单名称 + * @example 'Dashboard' + */ + title?: string; + + /** + * 菜单图标 + * @example 'el-icon-edit' + */ + icon?: string; + + /** + * 是否隐藏菜单项 + * true 隐藏, false 显示 + * @default false + */ + hidden?: boolean; + + /** + * 始终显示父级菜单,即使只有一个子菜单 + * true 显示父级菜单, false 隐藏父级菜单,显示唯一子节点 + * @default false + */ + alwaysShow?: boolean; + + /** + * 是否固定在页签上 + * true 固定, false 不固定 + * @default false + */ + affix?: boolean; + + /** + * 是否缓存页面 + * true 缓存, false 不缓存 + * @default false + */ + keepAlive?: boolean; + + /** + * 是否在面包屑导航中隐藏 + * true 隐藏, false 显示 + * @default false + */ + breadcrumb?: boolean; + } +} diff --git a/src/types/socket.d.ts b/src/types/socket.d.ts new file mode 100644 index 0000000..15a8ed7 --- /dev/null +++ b/src/types/socket.d.ts @@ -0,0 +1,6 @@ +// https://github.com/sockjs/sockjs-client/issues/565 + +declare module "sockjs-client/dist/sockjs.min.js" { + import Client from "sockjs-client"; + export default Client; +} diff --git a/src/types/underscore.d.ts b/src/types/underscore.d.ts new file mode 100644 index 0000000..75dfca8 --- /dev/null +++ b/src/types/underscore.d.ts @@ -0,0 +1 @@ +declare module "underscore"; diff --git a/src/types/vue3-license-plate.d.ts b/src/types/vue3-license-plate.d.ts new file mode 100644 index 0000000..f84ab2c --- /dev/null +++ b/src/types/vue3-license-plate.d.ts @@ -0,0 +1 @@ +declare module "vue3-license-plate"; diff --git a/src/utils/enum.ts b/src/utils/enum.ts new file mode 100644 index 0000000..10b33bc --- /dev/null +++ b/src/utils/enum.ts @@ -0,0 +1,121 @@ +// 1收货2出货 +export enum OrderType { + Receive = 1, + Shipment = 2, + Pay = 3, + Income = 4, +} +// 扣杂状态:0扣杂1扣点 +export enum ButtonType { + BuckleMiscellaneous = 0, + Points = 1, +} +// 称重方式:0:有皮重 1:零皮重 +export enum WeighingMethod { + Yes = 0, + No = 1, +} +// 多品类:0:单品类 1:多品类 +export enum MultiCategory { + Single = 0, + Multiple = 1, +} + +// 0:普通资源1:皮/毛 传参只有0,1 +export enum ImagesType { + NORMARL = 0, + Tare = 1, + GROSSWEIGHT = 2, + // Settlement = 3, + // Payment = 4 +} +//磅单状态:0:待定价1:待过皮2:待审核3:已审核待支付4:已支付 +// 磅单状态:0:待出货1:待过毛2:待审核3:已审未付4:已审已付 +export enum ScaleStatus { + ToBePriced = 0, + ToBeTare = 1, + ToBeReview = 2, + ToBePay = 3, + Paid = 4, +} +export enum ShipmentScaleStatus { + ToBeShipment = 0, + ToBeGrossWeight = 1, + ToBeShipmentReview = 2, + ToBeShipmentPay = 3, + ShipmentPaid = 4, +} +// 1 出货结算 2 暂不收款 3 立即收款 +export enum ScaleStatusBtnType { + ShipmentSettlement = 1, + ShipmentNoPay = 2, + ShipmentPay = 3, +} +// 支付方式:0:未支付,1:现金支付,2:银行卡支付,3:线上支付(微信)4:支付宝 +export enum PaymentMethod { + NoPay = 0, + Cash = 1, + BankCard = 2, + WeChat = 3, + Alipay = 4, +} +//用户类型0:刷脸1:刷卡 +export enum UserType { + Face = 0, + Card = 1, +} + +export enum UsersType { + Staff = 1, + Purchaser = 2, + Customer = 3, +} + +// 提货方式:0:送货1:自提 +export enum DeliveryMethod { + Deliver = 0, + SelfPickup = 1, +} + +export enum DeviceType { + Printer = 0, + Weighbridge = 1, +} + +// 1=出库(客户)2=入库(供应商) +export enum StockCardType { + Shipment = 1, + Receive = 2, +} + +// 今日 昨日 本月 本年 自定义 +export enum TimeRange { + Yesterday = 1, + Today = 2, + Week = 3, + Month = 4, + Year = 5, +} + +// 消息跳转类型 0=收货待定价1=收货待审核2=待出库3=出库审核 +export enum MsgType { + ToBePriced = 0, + ToBeReview = 1, + ToBeShipment = 2, + ToBeShipmentReview = 3, +} + +export enum settlementConfig { + // 正常计算、抹分、抹角分、抹元、分位四舍五入、角位四舍五入、元位四舍五入 + NORMAL = 1, + REMOVEF = 2, + REMOVEJ = 3, + REMOVEY = 4, + HALFF = 5, + HALFJ = 6, + HALFY = 7, +} + +export enum photoEnum { + ERROR = "https://backend-common.obs.cn-east-3.myhuaweicloud.com/static/110/error.jpg", +} diff --git a/src/utils/i18n.ts b/src/utils/i18n.ts new file mode 100644 index 0000000..17ed904 --- /dev/null +++ b/src/utils/i18n.ts @@ -0,0 +1,12 @@ +// translate router.meta.title, be used in breadcrumb sidebar tagsview +import i18n from "@/lang/index"; + +export function translateRouteTitle(title: any) { + // 判断是否存在国际化配置,如果没有原生返回 + const hasKey = i18n.global.te("route." + title); + if (hasKey) { + const translatedTitle = i18n.global.t("route." + title); + return translatedTitle; + } + return title; +} diff --git a/src/utils/index.ts b/src/utils/index.ts new file mode 100644 index 0000000..dfd88e9 --- /dev/null +++ b/src/utils/index.ts @@ -0,0 +1,477 @@ +import { TimeRange } from "./enum"; +/** + * Check if an element has a class + * @param {HTMLElement} ele + * @param {string} cls + * @returns {boolean} + */ +export function hasClass(ele: HTMLElement, cls: string) { + return !!ele.className.match(new RegExp("(\\s|^)" + cls + "(\\s|$)")); +} + +/** + * Add class to element + * @param {HTMLElement} ele + * @param {string} cls + */ +export function addClass(ele: HTMLElement, cls: string) { + if (!hasClass(ele, cls)) ele.className += " " + cls; +} + +/** + * Remove class from element + * @param {HTMLElement} ele + * @param {string} cls + */ +export function removeClass(ele: HTMLElement, cls: string) { + if (hasClass(ele, cls)) { + const reg = new RegExp("(\\s|^)" + cls + "(\\s|$)"); + ele.className = ele.className.replace(reg, " "); + } +} + +/** + * 判断是否是外部链接 + * + * @param {string} path + * @returns {Boolean} + */ +export function isExternal(path: string) { + const isExternal = /^(https?:|http?:|mailto:|tel:)/.test(path); + return isExternal; +} + +// 格式化金额 +export function formatMoney( + number: any, + decimals?: any, + dec_point?: any, + thousands_sep?: any, + roundtag?: any +) { + /* + * 参数说明: + * number:要格式化的数字 + * decimals:保留几位小数 + * dec_point:小数点符号 + * thousands_sep:千分位符号 + * roundtag:舍入参数,默认 "ceil" 向上取,"floor"向下取,"round" 四舍五入 + * */ + if (!number) { + number = 0; + } + if (!decimals) { + decimals = 0; //默认保留2位小数 + } + if (!dec_point) { + dec_point = "."; + } + if (!thousands_sep) { + thousands_sep = ","; + } + if (!roundtag) { + roundtag = "round"; + } + number = (number + "").replace(/[^0-9+-Ee.]/g, ""); + roundtag = roundtag || "ceil"; //"ceil","floor","round" + const n = !isFinite(+number) ? 0 : +number; + const prec = !isFinite(+decimals) ? 0 : Math.abs(decimals); + const sep = typeof thousands_sep === "undefined" ? "," : thousands_sep; + const dec = typeof dec_point === "undefined" ? "." : dec_point; + let s: any = ""; + const toFixedFix = function (n: any, prec: any) { + const k = Math.pow(10, prec); + + return ( + "" + + parseFloat( + (Math as any) + [roundtag](parseFloat((n * k).toFixed(prec * 2))) + .toFixed(prec * 2) + ) / + k + ); + }; + s = (prec ? toFixedFix(n, prec) : "" + Math.round(n)).split("."); + const re = /(-?\d+)(\d{3})/; + while (re.test(s[0])) { + s[0] = s[0].replace(re, "$1" + sep + "$2"); + } + + if ((s[1] || "").length < prec) { + s[1] = s[1] || ""; + s[1] += new Array(prec - s[1].length + 1).join("0"); + } + return s.join(dec); +} + +export function formatDate(time: any, cFormat: string) { + const format = cFormat || "{y}-{m}-{d}"; + const date = new Date(time); + const formatObj: any = { + //年 + y: date.getFullYear(), + //月 + m: date.getMonth() + 1, + //日 + d: date.getDate(), + //小时 + h: date.getHours(), + //分钟 + i: date.getMinutes(), + //秒 + s: date.getSeconds(), + //星期 + a: date.getDay(), + }; + const time_str = format.replace(/{([ymdhisa])+}/g, (result, key) => { + const value = formatObj[key]; + // Note: getDay() returns 0 on Sunday + if (key === "a") { + //如果key是a,就是星期,格式化成一~日 + //例如formatDate(new Date(), '{y}-{m}-{d}-{h}-{i}-{s}-{a}'); + //会输出2021-10-29-00-00-00-五 + //星期的value会返回0-6,['日', '一', '二', '三', '四', '五', '六'][2]代表周二 + return ["日", "一", "二", "三", "四", "五", "六"][value]; + } + //padStart用于字符串头部补全,2个字符,如果不够前面补0 + return value.toString().padStart(2, "0"); + }); + return time_str; +} + +export function getCurrentMonthStartAndEnd() { + const now = new Date(); + const currentMonthStart = new Date(now.getFullYear(), now.getMonth(), 1); + const currentMonthEnd = new Date(now.getFullYear(), now.getMonth() + 1, 0); + + return { + start: currentMonthStart, + end: currentMonthEnd, + }; +} + +export function getCurrentYearStartAndEnd() { + const now = new Date(); + const currentYearStart = new Date(now.getFullYear(), 0, 1); // 0 代表一月 + const currentYearEnd = (new Date(now.getFullYear() + 1, 0, 1) as any) - 1; // 下一年的一月一日减一毫秒 + + return { + start: currentYearStart, + end: currentYearEnd, + }; +} + +export function getCurrentWeekStartAndEnd() { + // let startTime = getMonday("s", 0); + // let endTime = getMonday("e", 0); + // const time1 = new Date(getMonday("s", 0)).getTime(); + // const time2 = new Date(new Date(new Date().setHours(0, 0, 0))).getTime(); + // if (time1 > time2) { + // startTime = getMonday("s", -1); + // endTime = getMonday("e", -1); + // } + var currentDate = new Date(); + + // 获取本周第一天的日期 + var firstDay = new Date( + currentDate.setDate(currentDate.getDate() - currentDate.getDay() + 1) + ); + + // 获取本周最后一天的日期 + var lastDay = new Date( + currentDate.setDate(currentDate.getDate() - currentDate.getDay() + 7) + ); + + // 格式化日期为字符串,例如:"YYYY-MM-DD" + var format = function (date: any) { + var year = date.getFullYear(); + var month = date.getMonth() + 1; + var day = date.getDate(); + return year + "-" + month + "-" + day; + }; + + return { + start: format(firstDay), + end: format(lastDay), + }; +} + +function getMonday(type: string, dates: number) { + const now = new Date(); + const nowTime = now.getTime(); + const day = now.getDay(); + const longTime = 24 * 60 * 60 * 1000; + const n = longTime * 7 * (dates || 0); + let dd: any = null; + if (type == "s") { + dd = nowTime - (day - 1) * longTime + n; + } + if (type == "e") { + dd = nowTime + (7 - day) * longTime + n; + } + dd = new Date(dd); + const y = dd.getFullYear(); + let m = dd.getMonth() + 1; + let d = dd.getDate(); + m = m < 10 ? "0" + m : m; + d = d < 10 ? "0" + d : d; + return y + "-" + m + "-" + d; +} + +export function timeShortcuts() { + return [ + { + text: "今天", + value: () => { + const start = new Date(); + const end = new Date(); + start.setHours(0, 0, 0); + end.setHours(23, 59, 59); + return [start, end]; + }, + }, + { + text: "昨天", + value: () => { + const start = new Date(); + const end = new Date(); + start.setTime(start.getTime() - 3600 * 1000 * 24); + end.setTime(end.getTime() - 3600 * 1000 * 24); + start.setHours(0, 0, 0); + end.setHours(23, 59, 59); + return [start, end]; + }, + }, + { + text: "最近一周", + value: () => { + const end = new Date(); + const start = new Date(); + start.setTime(start.getTime() - 3600 * 1000 * 24 * 7); + return [start, end]; + }, + }, + { + text: "最近一个月", + value: () => { + const end = new Date(); + const start = new Date(); + start.setTime(start.getTime() - 3600 * 1000 * 24 * 30); + return [start, end]; + }, + }, + { + text: "最近三个月", + value: () => { + const end = new Date(); + const start = new Date(); + start.setTime(start.getTime() - 3600 * 1000 * 24 * 90); + return [start, end]; + }, + }, + ]; +} + +export function getPaymentMethod(paymentMethod: any) { + return ["", "现金", "转账", "微信", "支付宝"][paymentMethod]; +} + +export function getWeighingMethod(index: any) { + return ["有皮重", "零皮重"][index]; +} + +export function getMultiCategory(index: any) { + return ["单品类", "多品类"][index]; +} + +// 两个浮点数求和 +export function num_add(num1: number, num2: number) { + let r1, r2; + try { + r1 = num1.toString().split(".")[1].length; + } catch (e) { + r1 = 0; + } + try { + r2 = num2.toString().split(".")[1].length; + } catch (e) { + r2 = 0; + } + const m = Math.pow(10, Math.max(r1, r2)); + return Number(Math.round(num1 * m + num2 * m) / m); +} + +// 两个浮点数相减 +export function num_subtract(num1: number, num2: number) { + let r1, r2; + try { + r1 = num1.toString().split(".")[1].length; + } catch (e) { + r1 = 0; + } + try { + r2 = num2.toString().split(".")[1].length; + } catch (e) { + r2 = 0; + } + const m = Math.pow(10, Math.max(r1, r2)); + const n = r1 >= r2 ? r1 : r2; + return Number(Number((Math.round(num1 * m - num2 * m) / m).toFixed(n))); +} + +// 两个浮点数相乘 +export function num_multiply(num1: number, num2: number) { + let m = 0; + const s1 = num1.toString(); + const s2 = num2.toString(); + try { + m += s1.split(".")[1].length; + } catch (e) {} + try { + m += s2.split(".")[1].length; + } catch (e) {} + return Number( + (Number(s1.replace(".", "")) * Number(s2.replace(".", ""))) / + Math.pow(10, m) + ); +} + +// 两个浮点数相除 +export function num_divide(num1: number, num2: number) { + let t1, t2; + try { + t1 = num1.toString().split(".")[1].length; + } catch (e) { + t1 = 0; + } + try { + t2 = num2.toString().split(".")[1].length; + } catch (e) { + t2 = 0; + } + const r1 = Number(num1.toString().replace(".", "")); + const r2 = Number(num2.toString().replace(".", "")); + return Number((r1 / r2) * Math.pow(10, t2 - t1)); +} + +export function isNullV(item: any) { + return item === null ? "-" : item; +} + +// 四舍五入到指定的小数位数 +export function roundToDecimalPlace(number: number, decimalPlaces: number) { + const factor = Math.pow(10, decimalPlaces); + return Math.round(number * factor) / factor; +} + +export function floorToDecimalPlace(number: number, decimalPlaces: number) { + const factor = Math.pow(10, decimalPlaces); + return Math.floor(number * factor) / factor; +} + +// { id: 1, name: "正常计算" }, +// { id: 2, name: "抹分" }, +// { id: 3, name: "抹角分" }, +// { id: 4, name: "抹元" }, +// { id: 5, name: "分位四舍五入" }, +// { id: 6, name: "角位四舍五入" }, +// { id: 7, name: "元位四舍五入" }, +export function getNumberValue( + settlementConfig: number, + tempTotalPrice: string +) { + let totalPrice = 0; + if (settlementConfig === 1) { + totalPrice = parseFloat(tempTotalPrice); + } else if (settlementConfig === 2) { + totalPrice = floorToDecimalPlace(parseFloat(tempTotalPrice), 1); + } else if (settlementConfig === 3) { + totalPrice = floorToDecimalPlace(parseFloat(tempTotalPrice), 0); + } else if (settlementConfig === 4) { + totalPrice = floorToDecimalPlace(parseFloat(tempTotalPrice), -1); + } else if (settlementConfig === 5) { + totalPrice = roundToDecimalPlace(parseFloat(tempTotalPrice), 1); + } else if (settlementConfig === 6) { + totalPrice = roundToDecimalPlace(parseFloat(tempTotalPrice), 0); + } else if (settlementConfig === 7) { + totalPrice = roundToDecimalPlace(parseFloat(tempTotalPrice), -1); + } else { + totalPrice = parseFloat(tempTotalPrice); + } + return totalPrice; +} + +export function isFormDataEmpty(formData: any) { + let isEmpty = true; + for (const [key, value] of formData.entries()) { + isEmpty = false; + break; + } + return isEmpty; +} +export const timeList = [ + { + id: 1, + name: "昨日", + }, + { + id: 2, + name: "今日", + }, + { + id: 3, + name: "本周", + }, + { + id: 4, + name: "本月", + }, + { + id: 5, + name: "本年", + }, +]; + +export function timeRange(id: number) { + const today = new Date(); + const yesterday = new Date((today as any) - 24 * 60 * 60 * 1000); + let startTime = ""; + let endTime = ""; + if (id === TimeRange.Today) { + startTime = formatDate(today, "{y}-{m}-{d}"); + endTime = formatDate(today, "{y}-{m}-{d}"); + } else if (id === TimeRange.Yesterday) { + startTime = formatDate(yesterday, "{y}-{m}-{d}"); + endTime = formatDate(yesterday, "{y}-{m}-{d}"); + } else if (id === TimeRange.Month) { + startTime = formatDate(getCurrentMonthStartAndEnd().start, "{y}-{m}-{d}"); + endTime = formatDate(getCurrentMonthStartAndEnd().end, "{y}-{m}-{d}"); + } else if (id === TimeRange.Year) { + startTime = formatDate(getCurrentYearStartAndEnd().start, "{y}-{m}-{d}"); + endTime = formatDate(getCurrentYearStartAndEnd().end, "{y}-{m}-{d}"); + } else if (id === TimeRange.Week) { + startTime = formatDate(getCurrentWeekStartAndEnd().start, "{y}-{m}-{d}"); + endTime = formatDate(getCurrentWeekStartAndEnd().end, "{y}-{m}-{d}"); + } + return { startTime: startTime, endTime: endTime }; +} + +export function downLoadExcel(content: any, name: string) { + // const blob = new Blob([content], { type: "application/vnd.ms-excel" }); + const blob = new Blob([content], { + type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8", + }); + + const fileName = name; + if ("download" in document.createElement("a")) { + const elink = document.createElement("a"); + elink.download = fileName; + elink.style.display = "none"; + elink.href = URL.createObjectURL(blob); + document.body.appendChild(elink); + elink.click(); + URL.revokeObjectURL(elink.href); + document.body.removeChild(elink); + } +} diff --git a/src/utils/nprogress.ts b/src/utils/nprogress.ts new file mode 100644 index 0000000..c1d5f23 --- /dev/null +++ b/src/utils/nprogress.ts @@ -0,0 +1,18 @@ +import NProgress from "nprogress"; +import "nprogress/nprogress.css"; + +// 进度条 +NProgress.configure({ + // 动画方式 + easing: "ease", + // 递增进度条的速度 + speed: 500, + // 是否显示加载ico + showSpinner: false, + // 自动递增间隔 + trickleSpeed: 200, + // 初始化时的最小百分比 + minimum: 0.3, +}); + +export default NProgress; diff --git a/src/utils/request.ts b/src/utils/request.ts new file mode 100644 index 0000000..89790a1 --- /dev/null +++ b/src/utils/request.ts @@ -0,0 +1,101 @@ +import axios, { InternalAxiosRequestConfig, AxiosResponse } from "axios"; +import { useUserStoreHook } from "@/store/modules/user"; +import { ResultEnum } from "@/enums/ResultEnum"; +import { TOKEN_KEY, CODE_KEY, CODE_NAME } from "@/enums/CacheEnum"; +import jsonBig from "json-bigint"; +// 创建 axios 实例 +const service = axios.create({ + baseURL: import.meta.env.VITE_APP_BASE_API, + timeout: 50000, + headers: { + "Access-Control-Allow-Origin": "*", + "Content-Type": "application/json;charset=utf-8", + }, + transformResponse: [ + function (data) { + //transformResponse这个配置项可以拦截接口返回的内容进行处理 + if ( + data.type === "application/vnd.ms-excel" || + data.type === "text/xml" + ) { + return data; + } else { + try { + // 如果大数字类型转换成功则返回转换的数据结果 + return jsonBig.parse(data); + } catch (err) { + // 如果转换失败,代表没有长数字可转,正常解析并返回 + return JSON.parse(data); + } + } + }, + ], +}); + +// 请求拦截器 +service.interceptors.request.use( + (config: InternalAxiosRequestConfig) => { + const accessToken = localStorage.getItem(TOKEN_KEY); + if (accessToken) { + config.headers["x-userToken"] = accessToken; + } + if (config.url && config.url.indexOf("/api/db/getCommonDbPhone") === -1) { + const codeKey = localStorage.getItem(CODE_KEY); + if (codeKey) { + config.headers[CODE_KEY] = codeKey; + } + const codeName = localStorage.getItem(CODE_NAME); + if (codeName) { + config.headers[CODE_NAME] = codeName; + } + } + return config; + }, + (error: any) => { + return Promise.reject(error); + } +); + +// 响应拦截器 +service.interceptors.response.use( + (response: AxiosResponse) => { + // 检查配置的响应类型是否为二进制类型('blob' 或 'arraybuffer'), 如果是,直接返回响应对象 + if ( + response.headers["content-type"].indexOf("application/vnd.ms-excel") > -1 + ) { + return response.data; + } + + const { code, data, message } = response.data; + if (code === ResultEnum.SUCCESS) { + return data; + } + + ElMessage.error(message || "系统出错"); + return Promise.reject(new Error(message || "Error")); + }, + (error: any) => { + // 异常处理 + if (error.response.data) { + const { code, message } = error.response.data; + if (code === ResultEnum.TOKEN_INVALID) { + ElNotification({ + title: "提示", + message: "您的会话已过期,请重新登录", + type: "info", + }); + useUserStoreHook() + .resetToken() + .then(() => { + location.reload(); + }); + } else { + ElMessage.error(message || "系统出错"); + } + } + return Promise.reject(error.message); + } +); + +// 导出 axios 实例 +export default service; diff --git a/src/views/dashboard/components/VisitTrend.vue b/src/views/dashboard/components/VisitTrend.vue new file mode 100644 index 0000000..e8ec7a9 --- /dev/null +++ b/src/views/dashboard/components/VisitTrend.vue @@ -0,0 +1,205 @@ + + + + + diff --git a/src/views/dashboard/index.vue b/src/views/dashboard/index.vue new file mode 100644 index 0000000..32c03f8 --- /dev/null +++ b/src/views/dashboard/index.vue @@ -0,0 +1,177 @@ + + + + + diff --git a/src/views/error-page/401.vue b/src/views/error-page/401.vue new file mode 100644 index 0000000..afb3e59 --- /dev/null +++ b/src/views/error-page/401.vue @@ -0,0 +1,109 @@ + + + + + diff --git a/src/views/error-page/404.vue b/src/views/error-page/404.vue new file mode 100644 index 0000000..cb56f5d --- /dev/null +++ b/src/views/error-page/404.vue @@ -0,0 +1,264 @@ + + + + + diff --git a/src/views/login/index.vue b/src/views/login/index.vue new file mode 100644 index 0000000..8e051e3 --- /dev/null +++ b/src/views/login/index.vue @@ -0,0 +1,265 @@ + + + + + diff --git a/src/views/redirect/index.vue b/src/views/redirect/index.vue new file mode 100644 index 0000000..4215bf6 --- /dev/null +++ b/src/views/redirect/index.vue @@ -0,0 +1,15 @@ + + + diff --git a/src/views/system/menu/index.vue b/src/views/system/menu/index.vue new file mode 100644 index 0000000..10212c5 --- /dev/null +++ b/src/views/system/menu/index.vue @@ -0,0 +1,644 @@ + + + diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..441cb93 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,28 @@ +{ + "compilerOptions": { + "target": "esnext", + "useDefineForClassFields": true, + "module": "esnext", + "moduleResolution": "node", + "strict": true, + "noLib": false, + "sourceMap": true, + "resolveJsonModule": true, + "esModuleInterop": true, + "lib": ["esnext", "dom"], + "baseUrl": ".", + "allowJs": true, + "skipLibCheck": true, + "allowSyntheticDefaultImports": true, + "forceConsistentCasingInFileNames": true, + "jsx": "preserve", + "jsxFactory": "h", + "jsxFragmentFactory": "Fragment", + "paths": { + "@/*": ["src/*"] + }, + "types": ["vite/client", "unplugin-icons/types/vue", "element-plus/global"] + }, + "include": ["mock/**/*.ts", "src/**/*.ts", "src/**/*.vue", "vite.config.ts"], + "exclude": ["node_modules", "dist", "**/*.js"] +} diff --git a/uno.config.ts b/uno.config.ts new file mode 100644 index 0000000..b952fe4 --- /dev/null +++ b/uno.config.ts @@ -0,0 +1,43 @@ +// uno.config.ts +import { + defineConfig, + presetAttributify, + presetIcons, + presetTypography, + presetUno, + presetWebFonts, + transformerDirectives, + transformerVariantGroup, +} from "unocss"; + +export default defineConfig({ + shortcuts: { + "flex-center": "flex justify-center items-center", + "flex-x-center": "flex justify-center", + "flex-y-center": "flex items-center", + "wh-full": "w-full h-full", + "flex-x-between": "flex items-center justify-between", + "flex-x-end": "flex items-center justify-end", + "absolute-lt": "absolute left-0 top-0", + "absolute-rt": "absolute right-0 top-0 ", + "fixed-lt": "fixed left-0 top-0", + }, + theme: { + colors: { + primary: "var(--el-color-primary)", + primary_dark: "var(--el-color-primary-light-5)", + }, + }, + presets: [ + presetUno(), + presetAttributify(), + presetIcons(), + presetTypography(), + presetWebFonts({ + fonts: { + // ... + }, + }), + ], + transformers: [transformerDirectives(), transformerVariantGroup()], +}); diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..da5a462 --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,262 @@ +import vue from "@vitejs/plugin-vue"; +import vueJsx from "@vitejs/plugin-vue-jsx"; +import { UserConfig, ConfigEnv, loadEnv, defineConfig } from "vite"; + +import AutoImport from "unplugin-auto-import/vite"; +import Components from "unplugin-vue-components/vite"; +import { ElementPlusResolver } from "unplugin-vue-components/resolvers"; +import Icons from "unplugin-icons/vite"; +import IconsResolver from "unplugin-icons/resolver"; + +import { createSvgIconsPlugin } from "vite-plugin-svg-icons"; +import mockDevServerPlugin from "vite-plugin-mock-dev-server"; + +import UnoCSS from "unocss/vite"; +import { resolve } from "path"; +import { + name, + version, + engines, + dependencies, + devDependencies, +} from "./package.json"; + +// https://devtools-next.vuejs.org/ +import VueDevTools from "vite-plugin-vue-devtools"; + +/** 平台的名称、版本、运行所需的`node`版本、依赖、构建时间的类型提示 */ +const __APP_INFO__ = { + pkg: { name, version, engines, dependencies, devDependencies }, + buildTimestamp: Date.now(), +}; + +const pathSrc = resolve(__dirname, "src"); +// https://cn.vitejs.dev/config +export default defineConfig(({ mode }: ConfigEnv): UserConfig => { + const env = loadEnv(mode, process.cwd()); + return { + resolve: { + alias: { + "@": pathSrc, + }, + }, + css: { + // CSS 预处理器 + preprocessorOptions: { + // 定义全局 SCSS 变量 + scss: { + javascriptEnabled: true, + additionalData: ` + @use "@/styles/variables.scss" as *; + `, + }, + }, + }, + server: { + // 允许IP访问 + host: "0.0.0.0", + // 应用端口 (默认:3000) + port: Number(env.VITE_APP_PORT), + // 运行是否自动打开浏览器 + open: true, + proxy: { + /** 代理前缀为 /dev-api 的请求 */ + // [env.VITE_APP_BASE_API]: { + // changeOrigin: true, + // // 接口地址 + // target: env.VITE_APP_API_URL, + // rewrite: (path) => + // path.replace(new RegExp("^" + env.VITE_APP_BASE_API), ""), + // }, + "^/api": { + target: "https://ifanda.52zaisheng.cn/test", + changeOrigin: true, + secure: true, + rewrite: (path) => path.replace(/^\/api/, "/api"), + }, + }, + }, + plugins: [ + vue(), + // jsx、tsx语法支持 + vueJsx(), + // MOCK 服务 + env.VITE_MOCK_DEV_SERVER === "true" ? mockDevServerPlugin() : null, + UnoCSS({ + hmrTopLevelAwait: false, + }), + // 自动导入参考: https://github.com/sxzz/element-plus-best-practices/blob/main/vite.config.ts + AutoImport({ + // 自动导入 Vue 相关函数,如:ref, reactive, toRef 等 + imports: ["vue", "@vueuse/core", "pinia", "vue-router", "vue-i18n"], + resolvers: [ + // 自动导入 Element Plus 相关函数,如:ElMessage, ElMessageBox... (带样式) + ElementPlusResolver(), + // 自动导入图标组件 + IconsResolver({}), + ], + eslintrc: { + // 是否自动生成 eslint 规则,建议生成之后设置 false + enabled: false, + // 指定自动导入函数 eslint 规则的文件 + filepath: "./.eslintrc-auto-import.json", + globalsPropValue: true, + }, + // 是否在 vue 模板中自动导入 + vueTemplate: true, + // 指定自动导入函数TS类型声明文件路径 (false:关闭自动生成) + dts: false, + // dts: "src/types/auto-imports.d.ts", + }), + Components({ + resolvers: [ + // 自动导入 Element Plus 组件 + ElementPlusResolver(), + // 自动注册图标组件 + IconsResolver({ + // element-plus图标库,其他图标库 https://icon-sets.iconify.design/ + enabledCollections: ["ep"], + }), + ], + // 指定自定义组件位置(默认:src/components) + dirs: ["src/components", "src/**/components"], + // 指定自动导入组件TS类型声明文件路径 (false:关闭自动生成) + dts: false, + // dts: "src/types/components.d.ts", + }), + Icons({ + // 自动安装图标库 + autoInstall: true, + }), + createSvgIconsPlugin({ + // 指定需要缓存的图标文件夹 + iconDirs: [resolve(pathSrc, "assets/icons")], + // 指定symbolId格式 + symbolId: "icon-[dir]-[name]", + }), + /* VueDevTools({ + openInEditorHost: `http://localhost:${env.VITE_APP_PORT}`, + }), */ + ], + // 预加载项目必需的组件 + optimizeDeps: { + include: [ + "vue", + "vue-router", + "pinia", + "axios", + "@vueuse/core", + "sortablejs", + "path-to-regexp", + "echarts", + "@wangeditor/editor", + "@wangeditor/editor-for-vue", + "vue-i18n", + "path-browserify", + "element-plus/es/components/form/style/css", + "element-plus/es/components/form-item/style/css", + "element-plus/es/components/button/style/css", + "element-plus/es/components/input/style/css", + "element-plus/es/components/input-number/style/css", + "element-plus/es/components/switch/style/css", + "element-plus/es/components/upload/style/css", + "element-plus/es/components/menu/style/css", + "element-plus/es/components/col/style/css", + "element-plus/es/components/icon/style/css", + "element-plus/es/components/row/style/css", + "element-plus/es/components/tag/style/css", + "element-plus/es/components/dialog/style/css", + "element-plus/es/components/loading/style/css", + "element-plus/es/components/radio/style/css", + "element-plus/es/components/radio-group/style/css", + "element-plus/es/components/popover/style/css", + "element-plus/es/components/scrollbar/style/css", + "element-plus/es/components/tooltip/style/css", + "element-plus/es/components/dropdown/style/css", + "element-plus/es/components/dropdown-menu/style/css", + "element-plus/es/components/dropdown-item/style/css", + "element-plus/es/components/sub-menu/style/css", + "element-plus/es/components/menu-item/style/css", + "element-plus/es/components/divider/style/css", + "element-plus/es/components/card/style/css", + "element-plus/es/components/link/style/css", + "element-plus/es/components/breadcrumb/style/css", + "element-plus/es/components/breadcrumb-item/style/css", + "element-plus/es/components/table/style/css", + "element-plus/es/components/tree-select/style/css", + "element-plus/es/components/table-column/style/css", + "element-plus/es/components/select/style/css", + "element-plus/es/components/option/style/css", + "element-plus/es/components/pagination/style/css", + "element-plus/es/components/tree/style/css", + "element-plus/es/components/alert/style/css", + "element-plus/es/components/radio-button/style/css", + "element-plus/es/components/checkbox-group/style/css", + "element-plus/es/components/checkbox/style/css", + "element-plus/es/components/tabs/style/css", + "element-plus/es/components/tab-pane/style/css", + "element-plus/es/components/rate/style/css", + "element-plus/es/components/date-picker/style/css", + "element-plus/es/components/notification/style/css", + "element-plus/es/components/image/style/css", + "element-plus/es/components/statistic/style/css", + "element-plus/es/components/watermark/style/css", + "element-plus/es/components/config-provider/style/css", + "element-plus/es/components/text/style/css", + "element-plus/es/components/drawer/style/css", + "element-plus/es/components/color-picker/style/css", + "element-plus/es/components/backtop/style/css", + "element-plus/es/components/message-box/style/css", + "element-plus/es/components/skeleton/style/css", + "element-plus/es/components/skeleton/style/css", + "element-plus/es/components/skeleton-item/style/css", + "element-plus/es/components/badge/style/css", + ], + }, + // 构建配置 + build: { + chunkSizeWarningLimit: 2000, // 消除打包大小超过500kb警告 + minify: "terser", // Vite 2.6.x 以上需要配置 minify: "terser", terserOptions 才能生效 + terserOptions: { + compress: { + keep_infinity: true, // 防止 Infinity 被压缩成 1/0,这可能会导致 Chrome 上的性能问题 + drop_console: true, // 生产环境去除 console + drop_debugger: true, // 生产环境去除 debugger + }, + format: { + comments: false, // 删除注释 + }, + }, + rollupOptions: { + output: { + // manualChunks: { + // "vue-i18n": ["vue-i18n"], + // }, + // 用于从入口点创建的块的打包输出格式[name]表示文件名,[hash]表示该文件内容hash值 + entryFileNames: "js/[name].[hash].js", + // 用于命名代码拆分时创建的共享块的输出命名 + chunkFileNames: "js/[name].[hash].js", + // 用于输出静态资源的命名,[ext]表示文件扩展名 + assetFileNames: (assetInfo: any) => { + const info = assetInfo.name.split("."); + let extType = info[info.length - 1]; + // console.log('文件信息', assetInfo.name) + if ( + /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/i.test(assetInfo.name) + ) { + extType = "media"; + } else if (/\.(png|jpe?g|gif|svg)(\?.*)?$/.test(assetInfo.name)) { + extType = "img"; + } else if (/\.(woff2?|eot|ttf|otf)(\?.*)?$/i.test(assetInfo.name)) { + extType = "fonts"; + } + return `${extType}/[name].[hash].[ext]`; + }, + }, + }, + }, + define: { + __APP_INFO__: JSON.stringify(__APP_INFO__), + }, + }; +});