zs 10 tháng trước cách đây
commit
47bfca17d5
82 tập tin đã thay đổi với 17846 bổ sung0 xóa
  1. 17 0
      .env.development
  2. 17 0
      .env.production
  3. 288 0
      .eslintrc-auto-import.json
  4. 33 0
      .eslintrc.cjs
  5. 31 0
      .gitignore
  6. 8 0
      .prettierrc.json
  7. 7 0
      .vscode/extensions.json
  8. 35 0
      README.md
  9. 13 0
      index.html
  10. 10 0
      jsconfig.json
  11. 12110 0
      package-lock.json
  12. 52 0
      package.json
  13. BIN
      public/favicon.ico
  14. BIN
      public/images/code.jpg
  15. BIN
      public/images/logo-jilin.png
  16. BIN
      public/images/logo-jilinbai.png
  17. BIN
      public/images/logo.png
  18. 31 0
      src/App.vue
  19. 0 0
      src/assets/base.css
  20. 95 0
      src/assets/icon/iconfont.css
  21. 1 0
      src/assets/icon/iconfont.js
  22. 149 0
      src/assets/icon/iconfont.json
  23. BIN
      src/assets/icon/iconfont.ttf
  24. BIN
      src/assets/icon/iconfont.woff
  25. BIN
      src/assets/icon/iconfont.woff2
  26. 1 0
      src/assets/logo.svg
  27. 19 0
      src/assets/main.css
  28. 67 0
      src/components/AdaptiveView/index.vue
  29. 63 0
      src/components/WangEditor/index.vue
  30. 96 0
      src/components/custom/custom-upload.vue
  31. 85 0
      src/components/dataV/mixin/autoResize.js
  32. 60 0
      src/components/dataV/myMain.vue
  33. 47 0
      src/components/dataV/util/index.js
  34. 7 0
      src/components/index.js
  35. 122 0
      src/layout/site.js
  36. 37 0
      src/main.js
  37. 45 0
      src/router/index.js
  38. 16 0
      src/settings.js
  39. 25 0
      src/store/api/login.js
  40. 54 0
      src/store/api/platform/achievement.js
  41. 55 0
      src/store/api/platform/chat.js
  42. 45 0
      src/store/api/platform/collection.js
  43. 54 0
      src/store/api/platform/demand.js
  44. 40 0
      src/store/api/platform/design.js
  45. 45 0
      src/store/api/platform/match.js
  46. 40 0
      src/store/api/platform/news.js
  47. 54 0
      src/store/api/platform/project.js
  48. 49 0
      src/store/api/platform/sign.js
  49. 54 0
      src/store/api/platform/supply.js
  50. 37 0
      src/store/api/platform/tool.js
  51. 40 0
      src/store/api/system/dictData.js
  52. 40 0
      src/store/api/system/dictType.js
  53. 40 0
      src/store/api/system/menus.js
  54. 40 0
      src/store/api/system/role.js
  55. 40 0
      src/store/api/system/tags.js
  56. 40 0
      src/store/api/user/admin.js
  57. 40 0
      src/store/api/user/association.js
  58. 54 0
      src/store/api/user/company.js
  59. 40 0
      src/store/api/user/competition.js
  60. 54 0
      src/store/api/user/expert.js
  61. 40 0
      src/store/api/user/incubator.js
  62. 40 0
      src/store/api/user/investment.js
  63. 40 0
      src/store/api/user/state.js
  64. 40 0
      src/store/api/user/unit.js
  65. 45 0
      src/store/api/user/user.js
  66. 12 0
      src/store/index.js
  67. 94 0
      src/store/modules/app.js
  68. 206 0
      src/store/modules/tagsView.js
  69. 23 0
      src/store/user.js
  70. 118 0
      src/styles/variables.scss
  71. 129 0
      src/utils/axios-wrapper.js
  72. 45 0
      src/utils/base-methods.js
  73. 23 0
      src/utils/checkResult.js
  74. 1964 0
      src/utils/city.js
  75. 61 0
      src/utils/error-code.js
  76. 26 0
      src/utils/file.js
  77. 35 0
      src/utils/util-methods.js
  78. 11 0
      src/utils/variable.js
  79. 135 0
      src/utils/websocket.js
  80. 24 0
      src/views/home/index.vue
  81. 24 0
      src/views/loading/index.vue
  82. 169 0
      vite.config.js

+ 17 - 0
.env.development

@@ -0,0 +1,17 @@
+## 开发环境
+NODE_ENV='development'
+
+# 应用端口
+VITE_APP_PORT = 3003
+
+# 代理前缀
+VITE_APP_BASE_API = '/cxyy/api'
+
+VITE_APP_BASE_APIWS ='/websocket/api'
+
+VITE_APP_HOME = "http://localhost:3000/"
+
+VITE_APP_HOST = "http://192.168.1.197"
+
+VITE_BASE_URL = "/cxyyWeb"
+VITE_OUT_DIR = "cxyyWeb"

+ 17 - 0
.env.production

@@ -0,0 +1,17 @@
+## 生产环境
+NODE_ENV='production'
+
+# 应用端口
+VITE_APP_PORT = 3003
+
+# 代理前缀
+VITE_APP_BASE_API = '/cxyy/api'
+
+VITE_APP_BASE_APIWS ='/websocket/api'
+
+VITE_APP_HOST = "http://10.120.114.6"
+
+VITE_APP_HOME = "/cxyyAdmin"
+
+VITE_BASE_URL = "/cxyyWeb"
+VITE_OUT_DIR = "cxyyWeb"

+ 288 - 0
.eslintrc-auto-import.json

@@ -0,0 +1,288 @@
+{
+  "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,
+    "defineStore": true,
+    "useRoute": true,
+    "useRouter": true,
+    "useI18n": true 
+  }
+}

+ 33 - 0
.eslintrc.cjs

@@ -0,0 +1,33 @@
+/* eslint-env node */
+require('@rushstack/eslint-patch/modern-module-resolution')
+
+module.exports = {
+  root: true,
+  extends: [
+    'plugin:vue/vue3-essential',
+    'eslint:recommended',
+    '@vue/eslint-config-prettier/skip-formatting',
+    './.eslintrc-auto-import.json'
+  ],
+  parserOptions: {
+    ecmaVersion: 'latest'
+  },
+  rules: {
+    'vue/multi-word-component-names': 0,
+    'max-len': [
+      'warn',
+      {
+        code: 200
+      }
+    ],
+    'prettier/prettier': [
+      'warn',
+      {
+        singleQuote: true,
+        bracketSpacing: true,
+        jsxBracketSameLine: true,
+        printWidth: 200
+      }
+    ]
+  }
+}

+ 31 - 0
.gitignore

@@ -0,0 +1,31 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+cxyyWeb*
+
+node_modules
+.DS_Store
+dist
+dist-ssr
+coverage
+*.local
+
+/cypress/videos/
+/cypress/screenshots/
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
+
+*.tsbuildinfo

+ 8 - 0
.prettierrc.json

@@ -0,0 +1,8 @@
+{
+  "$schema": "https://json.schemastore.org/prettierrc",
+  "semi": false,
+  "tabWidth": 2,
+  "singleQuote": true,
+  "printWidth": 100,
+  "trailingComma": "none"
+}

+ 7 - 0
.vscode/extensions.json

@@ -0,0 +1,7 @@
+{
+  "recommendations": [
+    "Vue.volar",
+    "dbaeumer.vscode-eslint",
+    "esbenp.prettier-vscode"
+  ]
+}

+ 35 - 0
README.md

@@ -0,0 +1,35 @@
+# web
+
+This template should help get you started developing with Vue 3 in Vite.
+
+## Recommended IDE Setup
+
+[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur).
+
+## Customize configuration
+
+See [Vite Configuration Reference](https://vitejs.dev/config/).
+
+## Project Setup
+
+```sh
+npm install
+```
+
+### Compile and Hot-Reload for Development
+
+```sh
+npm run dev
+```
+
+### Compile and Minify for Production
+
+```sh
+npm run build
+```
+
+### Lint with [ESLint](https://eslint.org/)
+
+```sh
+npm run lint
+```

+ 13 - 0
index.html

@@ -0,0 +1,13 @@
+<!doctype html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <link rel="icon" href="/favicon.ico" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <title>产学研用协同创新数字化平台</title>
+  </head>
+  <body>
+    <div id="app"></div>
+    <script type="module" src="/src/main.js"></script>
+  </body>
+</html>

+ 10 - 0
jsconfig.json

@@ -0,0 +1,10 @@
+{
+  "compilerOptions": {
+    "baseUrl": "./",
+    "paths": {
+      "@/*": ["./src/*"]
+    }
+  },
+  "exclude": ["node_modules", "dist"],
+  "include": ["src/**/*.vue", "**/*.js"]
+}

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 12110 - 0
package-lock.json


+ 52 - 0
package.json

@@ -0,0 +1,52 @@
+{
+  "name": "web",
+  "version": "0.0.0",
+  "private": true,
+  "type": "module",
+  "scripts": {
+    "dev": "vite",
+    "build": "vite build",
+    "preview": "vite preview",
+    "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore",
+    "format": "prettier --write src/"
+  },
+  "dependencies": {
+    "@element-plus/icons-vue": "^2.3.1",
+    "@vueuse/core": "^10.7.2",
+    "@vueuse/integrations": "^10.9.0",
+    "@wangeditor/editor": "^5.1.23",
+    "@wangeditor/editor-for-vue": "5.1.10",
+    "ant-design-vue": "^4.0.8",
+    "axios": "^1.6.7",
+    "dayjs": "^1.11.10",
+    "default-passive-events": "^2.0.0",
+    "echarts": "^5.5.0",
+    "element-plus": "^2.5.6",
+    "lodash-es": "^4.17.21",
+    "moment": "^2.30.1",
+    "nprogress": "^0.2.0",
+    "path-browserify": "^1.0.1",
+    "path-to-regexp": "^6.2.1",
+    "pinia": "^2.1.7",
+    "universal-cookie": "^7.1.0",
+    "vue": "^3.4.15",
+    "vue-i18n": "^9.9.1",
+    "vue-router": "^4.2.5",
+    "vue3-seamless-scroll": "^2.0.1"
+  },
+  "devDependencies": {
+    "@rushstack/eslint-patch": "^1.3.3",
+    "@vitejs/plugin-vue": "^5.0.3",
+    "@vue/eslint-config-prettier": "^8.0.0",
+    "eslint": "^8.49.0",
+    "eslint-plugin-vue": "^9.17.0",
+    "prettier": "^3.0.3",
+    "sass": "^1.71.0",
+    "unplugin-auto-import": "^0.17.5",
+    "unplugin-icons": "^0.18.5",
+    "unplugin-vue-components": "^0.26.0",
+    "vite": "^5.0.11",
+    "vite-plugin-inspect": "^0.8.3",
+    "vite-plugin-svg-icons": "^2.0.1"
+  }
+}

BIN
public/favicon.ico


BIN
public/images/code.jpg


BIN
public/images/logo-jilin.png


BIN
public/images/logo-jilinbai.png


BIN
public/images/logo.png


+ 31 - 0
src/App.vue

@@ -0,0 +1,31 @@
+<script setup>
+import zhCN from 'ant-design-vue/es/locale/zh_CN'
+</script>
+
+<template>
+  <a-config-provider :locale="zhCN">
+    <router-view />
+  </a-config-provider>
+</template>
+
+<style lang="scss">
+body {
+  margin: 0;
+}
+.w_1200 {
+  width: 1200px;
+  margin: 0 auto;
+}
+.textOver {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+.textMore {
+  display: -webkit-box;
+  -webkit-line-clamp: 2;
+  -webkit-box-orient: vertical;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+</style>

+ 0 - 0
src/assets/base.css


+ 95 - 0
src/assets/icon/iconfont.css

@@ -0,0 +1,95 @@
+@font-face {
+  font-family: 'iconfont'; /* Project id 4079354 */
+  src:
+    url('iconfont.woff2?t=1685509924120') format('woff2'),
+    url('iconfont.woff?t=1685509924120') format('woff'),
+    url('iconfont.ttf?t=1685509924120') format('truetype');
+}
+
+.iconfont {
+  font-family: 'iconfont' !important;
+  font-size: 16px;
+  font-style: normal;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+.icon-dengji:before {
+  content: '\e610';
+}
+
+.icon-shenhezhong:before {
+  content: '\e682';
+}
+
+.icon-dkw_shenheweitongguo:before {
+  content: '\e604';
+}
+
+.icon-shenhetongguo:before {
+  content: '\e668';
+}
+
+.icon-hearts-fill:before {
+  content: '\e702';
+}
+
+.icon-xiaoyouhui:before {
+  content: '\e601';
+}
+
+.icon-youjiantou-copy:before {
+  content: '\e654';
+}
+
+.icon-6ruxueshijian:before {
+  content: '\e88a';
+}
+
+.icon-zhuanyezhuanyeke:before {
+  content: '\e6a1';
+}
+
+.icon-commpany:before {
+  content: '\e612';
+}
+
+.icon-zhiwuguanli:before {
+  content: '\e60f';
+}
+
+.icon-guanzhu:before {
+  content: '\e611';
+}
+
+.icon-guanzhu1:before {
+  content: '\e600';
+}
+
+.icon-edu-line:before {
+  content: '\e63a';
+}
+
+.icon-gender:before {
+  content: '\e63e';
+}
+
+.icon-xingming:before {
+  content: '\e640';
+}
+
+.icon-jibenxinxi:before {
+  content: '\e67c';
+}
+
+.icon-iocn_be_concern:before {
+  content: '\e607';
+}
+
+.icon-tupianshangchuan:before {
+  content: '\e639';
+}
+
+.icon-wodeguanzhu:before {
+  content: '\e8bc';
+}

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 1 - 0
src/assets/icon/iconfont.js


+ 149 - 0
src/assets/icon/iconfont.json

@@ -0,0 +1,149 @@
+{
+  "id": "4079354",
+  "name": "校友信息登记",
+  "font_family": "iconfont",
+  "css_prefix_text": "icon-",
+  "description": "",
+  "glyphs": [
+    {
+      "icon_id": "1327399",
+      "name": "登记",
+      "font_class": "dengji",
+      "unicode": "e610",
+      "unicode_decimal": 58896
+    },
+    {
+      "icon_id": "1480896",
+      "name": "审核中",
+      "font_class": "shenhezhong",
+      "unicode": "e682",
+      "unicode_decimal": 59010
+    },
+    {
+      "icon_id": "2078809",
+      "name": "dkw_审核未通过",
+      "font_class": "dkw_shenheweitongguo",
+      "unicode": "e604",
+      "unicode_decimal": 58884
+    },
+    {
+      "icon_id": "10087856",
+      "name": "审核通过",
+      "font_class": "shenhetongguo",
+      "unicode": "e668",
+      "unicode_decimal": 58984
+    },
+    {
+      "icon_id": "35094512",
+      "name": "关心",
+      "font_class": "hearts-fill",
+      "unicode": "e702",
+      "unicode_decimal": 59138
+    },
+    {
+      "icon_id": "2726796",
+      "name": "校友会",
+      "font_class": "xiaoyouhui",
+      "unicode": "e601",
+      "unicode_decimal": 58881
+    },
+    {
+      "icon_id": "10515596",
+      "name": "右箭头",
+      "font_class": "youjiantou-copy",
+      "unicode": "e654",
+      "unicode_decimal": 58964
+    },
+    {
+      "icon_id": "518145",
+      "name": "6 入学时间",
+      "font_class": "6ruxueshijian",
+      "unicode": "e88a",
+      "unicode_decimal": 59530
+    },
+    {
+      "icon_id": "16365912",
+      "name": "专业 专业课",
+      "font_class": "zhuanyezhuanyeke",
+      "unicode": "e6a1",
+      "unicode_decimal": 59041
+    },
+    {
+      "icon_id": "376346",
+      "name": "工作单位",
+      "font_class": "commpany",
+      "unicode": "e612",
+      "unicode_decimal": 58898
+    },
+    {
+      "icon_id": "11672365",
+      "name": "职务管理",
+      "font_class": "zhiwuguanli",
+      "unicode": "e60f",
+      "unicode_decimal": 58895
+    },
+    {
+      "icon_id": "8712978",
+      "name": "关注",
+      "font_class": "guanzhu",
+      "unicode": "e611",
+      "unicode_decimal": 58897
+    },
+    {
+      "icon_id": "9714399",
+      "name": "关注",
+      "font_class": "guanzhu1",
+      "unicode": "e600",
+      "unicode_decimal": 58880
+    },
+    {
+      "icon_id": "6119296",
+      "name": "学历",
+      "font_class": "edu-line",
+      "unicode": "e63a",
+      "unicode_decimal": 58938
+    },
+    {
+      "icon_id": "6183156",
+      "name": "性别",
+      "font_class": "gender",
+      "unicode": "e63e",
+      "unicode_decimal": 58942
+    },
+    {
+      "icon_id": "20764666",
+      "name": "姓名",
+      "font_class": "xingming",
+      "unicode": "e640",
+      "unicode_decimal": 58944
+    },
+    {
+      "icon_id": "6856906",
+      "name": "基本信息",
+      "font_class": "jibenxinxi",
+      "unicode": "e67c",
+      "unicode_decimal": 59004
+    },
+    {
+      "icon_id": "1048854",
+      "name": "被关注",
+      "font_class": "iocn_be_concern",
+      "unicode": "e607",
+      "unicode_decimal": 58887
+    },
+    {
+      "icon_id": "2506206",
+      "name": "图片上传",
+      "font_class": "tupianshangchuan",
+      "unicode": "e639",
+      "unicode_decimal": 58937
+    },
+    {
+      "icon_id": "11372718",
+      "name": "我的关注",
+      "font_class": "wodeguanzhu",
+      "unicode": "e8bc",
+      "unicode_decimal": 59580
+    }
+  ]
+}

BIN
src/assets/icon/iconfont.ttf


BIN
src/assets/icon/iconfont.woff


BIN
src/assets/icon/iconfont.woff2


+ 1 - 0
src/assets/logo.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>

+ 19 - 0
src/assets/main.css

@@ -0,0 +1,19 @@
+body {
+  margin: 0;
+}
+
+.w_1200 {
+  width: 1200px;
+  margin: 0 auto;
+}
+
+p {
+  margin: 0;
+  padding: 0;
+}
+
+.textOver {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}

+ 67 - 0
src/components/AdaptiveView/index.vue

@@ -0,0 +1,67 @@
+<template>
+  <div class="ScreenAdapter" :style="style">
+    <slot></slot>
+  </div>
+</template>
+<script>
+import { onMounted, reactive } from 'vue'
+export default {
+  props: {
+    width: {
+      type: String,
+      default: window.innerWidth - 17
+    },
+    height: {
+      type: String,
+      default: window.innerHeight
+    }
+  },
+  setup(props) {
+    const style = reactive({
+      width: props.width + 'px',
+      height: props.height + 'px',
+      transform: 'scale(1) translate(-50%, -50%)'
+    })
+    const Debounce = (fn, t) => {
+      const delay = t || 500
+      let timer
+      return function () {
+        const args = arguments
+        if (timer) {
+          clearTimeout(timer)
+        }
+        timer = setTimeout(() => {
+          timer = null
+          fn.apply(args)
+        }, delay)
+      }
+    }
+    // 获取放大缩小比例
+    const getScale = () => {
+      const w = window.innerWidth / props.width
+      const h = window.innerHeight / props.height
+      return w < h ? w : h
+    }
+    // 设置比例
+    const setScale = () => {
+      style.transform = 'scale(' + getScale() + ') translate(-50%, -50%)'
+    }
+    onMounted(() => {
+      setScale()
+      window.onresize = Debounce(setScale, 1000)
+    })
+    return {
+      style
+    }
+  }
+}
+</script>
+<style scoped>
+.ScreenAdapter {
+  transform-origin: 0 0;
+  position: absolute;
+  left: 50%;
+  top: 50%;
+  transition: 0.3s;
+}
+</style>

+ 63 - 0
src/components/WangEditor/index.vue

@@ -0,0 +1,63 @@
+<template>
+  <div class="editor-wrapper">
+    <!-- 工具栏 -->
+    <Toolbar id="toolbar-container" :editor="editorRef" :default-config="toolbarConfig" :mode="mode" />
+    <!-- 编辑器 -->
+    <Editor style="height: 350px" id="editor-container" v-model="modelValue" :default-config="editorConfig" :mode="mode" @on-change="handleChange" @on-created="handleCreated" />
+  </div>
+</template>
+
+<script setup>
+import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
+
+// API 引用
+import { uploadFileApi } from '@/utils/file'
+
+const props = defineProps({
+  modelValue: {
+    type: [String],
+    default: ''
+  }
+})
+
+const emit = defineEmits(['update:modelValue'])
+
+const modelValue = useVModel(props, 'modelValue', emit)
+
+const editorRef = shallowRef() // 编辑器实例,必须用 shallowRef
+const mode = ref('default') // 编辑器模式
+const toolbarConfig = ref({}) // 工具条配置
+// 编辑器配置
+const editorConfig = ref({
+  placeholder: '请输入内容...',
+  MENU_CONF: {
+    uploadImage: {
+      // 自定义图片上传
+      async customUpload(file, insertFn) {
+        uploadFileApi(file).then((response) => {
+          const { errcode, uri } = response.data
+          const url = `${import.meta.env.VITE_APP_HOST}${uri}`
+          if (errcode === 0) insertFn(url)
+        })
+      }
+    }
+  }
+})
+
+const handleCreated = (editor) => {
+  editorRef.value = editor // 记录 editor 实例,重要!
+}
+
+function handleChange(editor) {
+  modelValue.value = editor.getHtml()
+}
+
+// 组件销毁时,也及时销毁编辑器
+onBeforeUnmount(() => {
+  const editor = editorRef.value
+  if (editor == null) return
+  editor.destroy()
+})
+</script>
+
+<style src="@wangeditor/editor/dist/css/style.css"></style>

+ 96 - 0
src/components/custom/custom-upload.vue

@@ -0,0 +1,96 @@
+<template>
+  <div id="c-upload">
+    <el-upload
+      v-if="url"
+      ref="upload"
+      :action="url"
+      :limit="limit"
+      :accept="accept"
+      :file-list="list"
+      :list-type="listType"
+      :on-exceed="outLimit"
+      :on-preview="filePreview"
+      :on-success="onSuccess"
+      :before-remove="onRemove">
+      <el-button type="primary">选择文件</el-button>
+      <template #tip v-if="tip">
+        <p style="color: #ff0000">{{ tip }}</p>
+      </template>
+    </el-upload>
+    <el-dialog v-model="dialog.show" append-to-body>
+      <img width="100%" :src="dialog.url" alt="" />
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import { ElMessage } from 'element-plus'
+import { omit, cloneDeep, isArray } from 'lodash-es'
+
+let dialog = ref({ show: false, url: '' })
+const props = defineProps({
+  url: { type: String, default: () => '' },
+  limit: { type: Number, default: () => 6 },
+  accept: { type: String, default: () => 'image/png, image/jpeg' },
+  listType: { type: String, default: () => 'text' }, //'text' | 'picture' | 'picture-card'
+  tip: { type: String, default: () => undefined },
+  list: { type: Array, default: () => [] },
+  model: { type: String, default: () => '' }
+})
+// 图片上传地址
+const { url } = toRefs(props)
+// 可上传文件数目
+const { limit } = toRefs(props)
+// 接收上传的文件类型
+const { accept } = toRefs(props)
+// 文件列表的类型--picture-card---picture
+const { listType } = toRefs(props)
+// 文件提醒
+const { tip } = toRefs(props)
+// 已有数据,赋值,预览
+const { list } = toRefs(props)
+const { model } = toRefs(props)
+// const list = ref<UploadUserFile[]>([]);
+
+const emit = defineEmits(['change'])
+// 图片预览
+const filePreview = (file) => {
+  // this.dialog = { show: true, url: file.url };
+  window.open(file.url)
+};
+// 只允许上传多少个文件
+const outLimit = () => {
+  ElMessage.error(`只允许上传${limit.value}个文件`)
+};
+// 上传成功,response:成功信息,file:图片信息,fileList:图片列表
+const onSuccess = (response, file) => {
+  if (response.errcode !== 0) {
+    ElMessage({ type: 'error', message: '删除成功' })
+    return
+  }
+  let ponse = omit(response, ['errcode', 'errmsg'])
+  let arr = cloneDeep(list)
+  if (isArray(list.value)) {
+    arr.value.push({ ...ponse, name: file.name, url: `${import.meta.env.VITE_APP_HOST}${response.uri}` })
+  } else {
+    arr.value = [{ ...ponse, name: file.name, url: `${import.meta.env.VITE_APP_HOST}${response.uri}` }]
+  }
+  emit('change', { model: model.value, value: arr.value })
+}
+// 删除图片
+// file: { id: any; uri: string }, fileList: any
+const onRemove = () => {
+  // let arr: Ref<ListItem[]> = _.cloneDeep(list);
+  // let info = arr.value.filter((f) => f.id != file.id);
+  // emit('change', info);
+  return true
+};
+
+// #endregion
+</script>
+
+<style lang="scss" scoped>
+#c-upload {
+  width: 100%;
+}
+</style>

+ 85 - 0
src/components/dataV/mixin/autoResize.js

@@ -0,0 +1,85 @@
+import { debounce, observerDomResize } from '../util/index'
+
+export default {
+  data () {
+    return {
+      dom: '',
+
+      width: 0,
+      height: 0,
+
+      debounceInitWHFun: '',
+
+      domObserver: ''
+    }
+  },
+  methods: {
+    async autoResizeMixinInit () {
+      const { initWH, getDebounceInitWHFun, bindDomResizeCallback, afterAutoResizeMixinInit } = this
+
+      await initWH(false)
+
+      getDebounceInitWHFun()
+
+      bindDomResizeCallback()
+
+      if (typeof afterAutoResizeMixinInit === 'function') afterAutoResizeMixinInit()
+    },
+    initWH (resize = true) {
+      const { $nextTick, $refs, ref, onResize } = this
+
+      return new Promise(resolve => {
+        $nextTick((_) => {
+          const dom = (this.dom = $refs[ref])
+          // this.width = dom ? dom.clientWidth : 0
+          // this.height = dom ? dom.clientHeight : 0
+          this.width = window.innerWidth
+          this.height = window.innerHeight
+
+          if (!dom) {
+            console.warn('DataV: Failed to get dom node, component rendering may be abnormal!')
+          } else if (!this.width || !this.height) {
+            console.warn('DataV: Component width or height is 0px, rendering abnormality may occur!')
+          }
+
+          if (typeof onResize === 'function' && resize) onResize()
+
+          resolve()
+        })
+      })
+    },
+    getDebounceInitWHFun () {
+      const { initWH } = this
+
+      this.debounceInitWHFun = debounce(100, initWH)
+    },
+    bindDomResizeCallback () {
+      const { dom, debounceInitWHFun } = this
+
+      this.domObserver = observerDomResize(dom, debounceInitWHFun)
+
+      window.addEventListener('resize', debounceInitWHFun)
+    },
+    unbindDomResizeCallback () {
+      let { domObserver, debounceInitWHFun } = this
+
+      if (!domObserver) return
+
+      domObserver.disconnect()
+      domObserver.takeRecords()
+      domObserver = null
+
+      window.removeEventListener('resize', debounceInitWHFun)
+    }
+  },
+  mounted () {
+    const { autoResizeMixinInit } = this
+
+    autoResizeMixinInit()
+  },
+  beforeDestroy () {
+    const { unbindDomResizeCallback } = this
+
+    unbindDomResizeCallback()
+  }
+}

+ 60 - 0
src/components/dataV/myMain.vue

@@ -0,0 +1,60 @@
+<template>
+  <div id="dv-full-screen-container" :ref="ref">
+    <template v-if="ready">
+      <slot></slot>
+    </template>
+  </div>
+</template>
+
+<script>
+import autoResize from './mixin/autoResize.js'
+
+export default {
+  name: 'DvFullScreenContainer',
+  mixins: [autoResize],
+  data() {
+    return {
+      ref: 'full-screen-container',
+      allWidth: 0,
+      allHeight: 0,
+      scale: 0,
+      datavRoot: '',
+      ready: false
+    }
+  },
+  methods: {
+    afterAutoResizeMixinInit() {
+      const { initConfig, setAppScale } = this
+
+      initConfig()
+
+      setAppScale()
+
+      this.ready = true
+    },
+    initConfig() {
+      const { dom } = this
+      // const { width } = screen
+      let width = window.innerWidth
+      let height = window.innerHeight
+      this.allWidth = width
+      this.allHeight = height
+
+      dom.style.width = `${width - 20}px`
+      dom.style.height = `${height}px`
+    },
+    setAppScale() {
+      const { allWidth, allHeight, dom } = this
+      const currentWidth = window.outerWidth
+      const currentHeight = window.innerHeight
+      // dom.style.transform = `scale(${currentWidth / allWidth})`
+      dom.style.transform = `scaleY(${currentHeight / allHeight}) scaleX(${currentWidth / allWidth})`
+    },
+    onResize() {
+      const { setAppScale } = this
+
+      setAppScale()
+    }
+  }
+}
+</script>

+ 47 - 0
src/components/dataV/util/index.js

@@ -0,0 +1,47 @@
+export function randomExtend(minNum, maxNum) {
+  if (arguments.length === 1) {
+    return parseInt(Math.random() * minNum + 1, 10)
+  } else {
+    return parseInt(Math.random() * (maxNum - minNum + 1) + minNum, 10)
+  }
+}
+
+export function debounce(delay, callback) {
+  let lastTime
+
+  return function () {
+    clearTimeout(lastTime)
+
+    const [that, args] = [this, arguments]
+
+    lastTime = setTimeout(() => {
+      callback.apply(that, args)
+    }, delay)
+  }
+}
+
+export function observerDomResize(dom, callback) {
+  const MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver
+
+  const observer = new MutationObserver(callback)
+
+  observer.observe(dom, { attributes: true, attributeFilter: ['style'], attributeOldValue: true })
+
+  return observer
+}
+
+export function getPointDistance(pointOne, pointTwo) {
+  const minusX = Math.abs(pointOne[0] - pointTwo[0])
+
+  const minusY = Math.abs(pointOne[1] - pointTwo[1])
+
+  return Math.sqrt(minusX * minusX + minusY * minusY)
+}
+
+export function uuid(hasHyphen) {
+  return (hasHyphen ? 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx' : 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx').replace(/[xy]/g, function (c) {
+    const r = (Math.random() * 16) | 0
+    const v = c == 'x' ? r : (r & 0x3) | 0x8
+    return v.toString(16)
+  })
+}

+ 7 - 0
src/components/index.js

@@ -0,0 +1,7 @@
+export default function globalComponents(app) {
+  const components = import.meta.glob('./**/**.{vue,tsx}', { eager: true }) //获取文件夹及其嵌套的多级子文件夹
+  for (let [key, value] of Object.entries(components)) {
+    const name = key.replace('./', '').split('/')[0]
+    app.component(value.default.name || name, value.default)
+  }
+}

+ 122 - 0
src/layout/site.js

@@ -0,0 +1,122 @@
+// 图片引入
+import logo from '/images/logo.png'
+import Code from '/images/code.jpg'
+// 网站基本设置
+export const siteInfo = {
+  display: false,
+  zhTitle: '产学研用协同创新数字化平台',
+  zhEnglish:
+    'Collaborative Innovation Digital Platform for Industry University Research Application',
+  logoUrl: logo
+}
+// 网站底部信息
+export const footInfo = {
+  Phone: '0431-81509921',
+  Email: 'gyyzhglb@jlitri.com',
+  Address: '吉林省长春市汽车经济技术开发区兴顺路1366号',
+  Copyright: 'Copyright ©2022  吉林省工业技术研究院集团有限公司  All Rights Reserved ',
+  Company: '吉林省工业技术研究院集团有限公司',
+  Code
+}
+// 目录设置
+export const menuList = [
+  { key: '1', title: '首页', route: 'one', English: 'Home', label: '首页' },
+  { key: '2', title: '信息发布', route: 'two', English: 'Information Release', label: '信息发布' },
+  {
+    key: '3',
+    title: '创新中心',
+    route: 'thr',
+    English: 'News Information',
+    label: '创新中心',
+    children: [
+      {
+        key: '3-1',
+        title: '需求中心',
+        label: '需求中心',
+        children: [
+          { key: '3-1-1', route: 'demand', title: '技术需求', label: '技术需求', value: '0' },
+          { key: '3-1-2', route: 'demand', title: '人才需求', label: '人才需求', value: '1' },
+          { key: '3-1-3', route: 'demand', title: '资金需求', label: '资金需求', value: '2' },
+          { key: '3-1-4', route: 'demand', title: '合作伙伴', label: '合作伙伴', value: '3' },
+          { key: '3-1-5', route: 'demand', title: '知识产权', label: '知识产权', value: '4' },
+          { key: '3-1-6', route: 'demand', title: '市场需求', label: '市场需求', value: '5' },
+          { key: '3-1-7', route: 'demand', title: '政策支持', label: '政策支持', value: '6' },
+          { key: '3-1-8', route: 'demand', title: '其他', label: '其他', value: '7' }
+        ]
+      },
+      {
+        key: '3-2',
+        title: '供方中心',
+        label: '供方中心',
+        children: [
+          { key: '3-2-1', route: 'supply', title: '技术支持', label: '技术支持', value: '0' },
+          { key: '3-2-1', route: 'supply', title: '培训教育', label: '培训教育', value: '1' },
+          { key: '3-2-3', route: 'supply', title: '资金支持', label: '资金支持', value: '2' },
+          { key: '3-2-4', route: 'supply', title: '合作机会', label: '合作机会', value: '3' },
+          {
+            key: '3-2-5',
+            route: 'supply',
+            title: '知识产权管理',
+            label: '知识产权管理',
+            value: '4'
+          },
+          { key: '3-2-6', route: 'supply', title: '市场推广', label: '市场推广', value: '5' },
+          { key: '3-2-7', route: 'supply', title: '政策支持', label: '政策支持', value: '6' },
+          { key: '3-2-8', route: 'supply', title: '其他', label: '其他', value: '7' }
+        ]
+      }
+    ]
+  },
+  { key: '4', title: '信息检索', route: 'four', English: 'Expert Tank', label: '信息检索' },
+  { key: '5', title: '双创活动', route: 'five', English: 'Authorized Operator', label: '双创活动' },
+  { key: '6', title: '中试平台', route: 'six', English: 'Project Selection', label: '中试平台' },
+  {
+    key: '7',
+    title: '服务支撑',
+    route: 'seven',
+    English: 'Innovation Competition',
+    label: '服务支撑'
+  },
+  {
+    key: '8',
+    title: '产业集群',
+    route: 'eight',
+    English: 'Innovation Competition',
+    label: '产业集群'
+  },
+  {
+    key: '9',
+    title: '成果展示',
+    route: 'nine',
+    English: 'Innovation Competition',
+    label: '成果展示'
+  },
+  {
+    key: '10',
+    title: '信息库',
+    route: 'ten',
+    English: 'Innovation Competition',
+    label: '信息库',
+    children: [
+      { key: '10-1', route: 'expert', title: '专家库', label: '专家库', value: '0' },
+      { key: '10-2', route: 'demand', title: '供需库', label: '供需库', value: '1' },
+      { key: '10-3', route: 'company', title: '企业库', label: '企业库', value: '2' },
+      { key: '10-4', route: 'project', title: '项目库', label: '项目库', value: '3' }
+    ]
+  },
+  { key: '11', title: '孵化器', route: 'eleven', English: 'Achievement Display', label: '孵化器' },
+  {
+    key: '12',
+    title: '产业孵化大脑',
+    route: 'twelve',
+    English: 'Achievement Display',
+    label: '产业孵化大脑'
+  },
+  {
+    key: '13',
+    title: '行研产研',
+    route: 'thirteen',
+    English: 'Research Development',
+    label: '行研产研'
+  }
+]

+ 37 - 0
src/main.js

@@ -0,0 +1,37 @@
+import { createApp } from 'vue'
+import { setupStore } from '@/store'
+
+import App from './App.vue'
+import router from './router'
+
+// element
+import ElementPlus from 'element-plus'
+import 'element-plus/dist/index.css'
+import locale from 'element-plus/es/locale/lang/zh-cn'
+import * as ElementPlusIconsVue from '@element-plus/icons-vue'
+
+// 请求检查函数
+import { InitCheckResult } from './utils/checkResult'
+import { InitVariable } from './utils/variable'
+// 组件
+import globalComponents from '@/components'
+// 自动滚动
+import vue3SeamlessScroll from 'vue3-seamless-scroll'
+// Antd
+import Antd from 'ant-design-vue'
+import 'ant-design-vue/dist/reset.css'
+import 'default-passive-events'
+
+const app = createApp(App)
+globalComponents(app)
+setupStore(app)
+for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
+  app.component(key, component)
+}
+app.use(router)
+app.use(Antd)
+app.use(ElementPlus, { locale })
+app.use(vue3SeamlessScroll, { name: 'vue3SeamlessScroll' })
+InitCheckResult(app)
+InitVariable(app)
+app.mount('#app')

+ 45 - 0
src/router/index.js

@@ -0,0 +1,45 @@
+import { createRouter, createWebHistory } from 'vue-router'
+import { UserStore } from '@/store/user'
+import axios from 'axios'
+const router = createRouter({
+  history: createWebHistory(import.meta.env.BASE_URL),
+  routes: [
+    {
+      path: '/',
+      redirect: '/home'
+    },
+    {
+      path: '/home',
+      name: 'home',
+      meta: { title: '产学研用协同创新数字化平台' },
+      component: () => import('@/views/home/index.vue')
+    },
+    {
+      path: '/loading',
+      name: 'loading',
+      meta: { title: '产学研用协同创新数字化平台' },
+      component: () => import('@/views/loading/index.vue')
+    }
+  ]
+})
+router.beforeEach(async (to, from, next) => {
+  const userStore = UserStore()
+  document.title = `${to.meta.title} `
+  const token = localStorage.getItem('token')
+  if (token) {
+    const res = await axios.request({
+      method: 'get',
+      url: `${import.meta.env.VITE_APP_BASE_API}/token/tokenView`,
+      responseType: 'json',
+      headers: {
+        token: token
+      }
+    })
+    if (res.data.errcode === 0) userStore.setUser(res.data.data)
+    next()
+  } else next()
+})
+router.afterEach(() => {
+  window.scrollTo(0, 0)
+})
+export default router

+ 16 - 0
src/settings.js

@@ -0,0 +1,16 @@
+const defaultSettings = {
+  title: 'admin-template',
+  version: 'v0.0.1',
+  showSettings: true,
+  tagsView: true,
+  fixedHeader: false,
+  sidebarLogo: true,
+  layout: 'left',
+  theme: 'light',
+  size: 'default',
+  language: 'zh-cn',
+  themeColor: '#409EFF',
+  watermark: { enabled: false, content: '福瑞科技' }
+}
+
+export default defaultSettings

+ 25 - 0
src/store/api/login.js

@@ -0,0 +1,25 @@
+import { defineStore } from 'pinia'
+import { get, omit } from 'lodash-es'
+import { AxiosWrapper } from '@/utils/axios-wrapper'
+const axios = new AxiosWrapper()
+export const LoginStore = defineStore('login', () => {
+  const login = async (payload) => {
+    const type = get(payload, 'type')
+    const np = omit(payload, 'type')
+    const res = await axios.$post(`/login/${type}`, np)
+    return res
+  }
+  const rp = async (payload) => {
+    const type = get(payload, 'type')
+    const np = omit(payload, 'type')
+    const res = await axios.$post(`/login/updatePwd/${type}`, np)
+    return res
+  }
+  const rpNoNewPassword = async (payload) => {
+    const type = get(payload, 'type')
+    const np = omit(payload, 'type')
+    const res = await axios.$post(`/login/resetPwd/${type}`, np)
+    return res
+  }
+  return { login, rp, rpNoNewPassword }
+})

+ 54 - 0
src/store/api/platform/achievement.js

@@ -0,0 +1,54 @@
+import { defineStore } from 'pinia'
+import { AxiosWrapper } from '@/utils/axios-wrapper'
+import { get } from 'lodash-es'
+const url = '/achievement'
+const axios = new AxiosWrapper()
+
+export const AchievementStore = defineStore('achievement', () => {
+  const query = async ({ skip = 0, limit = undefined, ...info } = {}) => {
+    let cond = {}
+    if (skip) cond.skip = skip
+    if (limit) cond.limit = limit
+    cond = { ...cond, ...info }
+    const res = await axios.$get(`${url}`, cond)
+    return res
+  }
+  const list = async ({ skip = 0, limit = undefined, ...info } = {}) => {
+    let cond = {}
+    if (skip) cond.skip = skip
+    if (limit) cond.limit = limit
+    cond = { ...cond, ...info }
+    const res = await axios.$get(`${url}/list`, cond)
+    return res
+  }
+  const fetch = async (payload) => {
+    const res = await axios.$get(`${url}/${payload}`)
+    return res
+  }
+  const detail = async (payload) => {
+    const res = await axios.$get(`${url}/detail/${payload}`)
+    return res
+  }
+  const create = async (payload) => {
+    const res = await axios.$post(`${url}`, payload)
+    return res
+  }
+  const update = async (payload) => {
+    const id = get(payload, 'id', get(payload, '_id'))
+    const res = await axios.$post(`${url}/${id}`, payload)
+    return res
+  }
+  const del = async (payload) => {
+    const res = await axios.$delete(`${url}/${payload}`)
+    return res
+  }
+  return {
+    query,
+    list,
+    fetch,
+    detail,
+    create,
+    update,
+    del
+  }
+})

+ 55 - 0
src/store/api/platform/chat.js

@@ -0,0 +1,55 @@
+import { defineStore } from 'pinia'
+import { AxiosWrapper } from '@/utils/axios-wrapper'
+import { get } from 'lodash-es'
+const url = '/chat'
+const axios = new AxiosWrapper()
+
+export const ChatStore = defineStore('chat', () => {
+  const query = async ({ skip = 0, limit = undefined, ...info } = {}) => {
+    let cond = {}
+    if (skip) cond.skip = skip
+    if (limit) cond.limit = limit
+    cond = { ...cond, ...info }
+    const res = await axios.$get(`${url}`, cond)
+    return res
+  }
+  const chat = async (cond) => {
+    const res = await axios.$get(`${url}/chat`, cond)
+    return res
+  }
+  const read = async (cond) => {
+    const res = await axios.$get(`${url}/read`, cond)
+    return res
+  }
+  const fetch = async (payload) => {
+    const res = await axios.$get(`${url}/${payload}`)
+    return res
+  }
+  const create = async (payload) => {
+    const res = await axios.$post(`${url}`, payload)
+    return res
+  }
+  const cancel = async (payload) => {
+    const res = await axios.$post(`${url}/cancel`, payload)
+    return res
+  }
+  const update = async (payload) => {
+    const id = get(payload, 'id', get(payload, '_id'))
+    const res = await axios.$post(`${url}/${id}`, payload)
+    return res
+  }
+  const del = async (payload) => {
+    const res = await axios.$delete(`${url}/${payload}`)
+    return res
+  }
+  return {
+    query,
+    chat,
+    read,
+    fetch,
+    create,
+    update,
+    cancel,
+    del
+  }
+})

+ 45 - 0
src/store/api/platform/collection.js

@@ -0,0 +1,45 @@
+import { defineStore } from 'pinia'
+import { AxiosWrapper } from '@/utils/axios-wrapper'
+import { get } from 'lodash-es'
+const url = '/collection'
+const axios = new AxiosWrapper()
+
+export const CollectionStore = defineStore('collection', () => {
+  const query = async ({ skip = 0, limit = undefined, ...info } = {}) => {
+    let cond = {}
+    if (skip) cond.skip = skip
+    if (limit) cond.limit = limit
+    cond = { ...cond, ...info }
+    const res = await axios.$get(`${url}`, cond)
+    return res
+  }
+  const fetch = async (payload) => {
+    const res = await axios.$get(`${url}/${payload}`)
+    return res
+  }
+  const create = async (payload) => {
+    const res = await axios.$post(`${url}`, payload)
+    return res
+  }
+  const cancel = async (payload) => {
+    const res = await axios.$post(`${url}/cancel`, payload)
+    return res
+  }
+  const update = async (payload) => {
+    const id = get(payload, 'id', get(payload, '_id'))
+    const res = await axios.$post(`${url}/${id}`, payload)
+    return res
+  }
+  const del = async (payload) => {
+    const res = await axios.$delete(`${url}/${payload}`)
+    return res
+  }
+  return {
+    query,
+    fetch,
+    create,
+    update,
+    cancel,
+    del
+  }
+})

+ 54 - 0
src/store/api/platform/demand.js

@@ -0,0 +1,54 @@
+import { defineStore } from 'pinia'
+import { AxiosWrapper } from '@/utils/axios-wrapper'
+import { get } from 'lodash-es'
+const url = '/demand'
+const axios = new AxiosWrapper()
+
+export const DemandStore = defineStore('demand', () => {
+  const query = async ({ skip = 0, limit = undefined, ...info } = {}) => {
+    let cond = {}
+    if (skip) cond.skip = skip
+    if (limit) cond.limit = limit
+    cond = { ...cond, ...info }
+    const res = await axios.$get(`${url}`, cond)
+    return res
+  }
+  const list = async ({ skip = 0, limit = undefined, ...info } = {}) => {
+    let cond = {}
+    if (skip) cond.skip = skip
+    if (limit) cond.limit = limit
+    cond = { ...cond, ...info }
+    const res = await axios.$get(`${url}/list`, cond)
+    return res
+  }
+  const fetch = async (payload) => {
+    const res = await axios.$get(`${url}/${payload}`)
+    return res
+  }
+  const detail = async (payload) => {
+    const res = await axios.$get(`${url}/detail/${payload}`)
+    return res
+  }
+  const create = async (payload) => {
+    const res = await axios.$post(`${url}`, payload)
+    return res
+  }
+  const update = async (payload) => {
+    const id = get(payload, 'id', get(payload, '_id'))
+    const res = await axios.$post(`${url}/${id}`, payload)
+    return res
+  }
+  const del = async (payload) => {
+    const res = await axios.$delete(`${url}/${payload}`)
+    return res
+  }
+  return {
+    query,
+    list,
+    fetch,
+    create,
+    detail,
+    update,
+    del
+  }
+})

+ 40 - 0
src/store/api/platform/design.js

@@ -0,0 +1,40 @@
+import { defineStore } from 'pinia'
+import { AxiosWrapper } from '@/utils/axios-wrapper'
+import { get } from 'lodash-es'
+const url = '/design'
+const axios = new AxiosWrapper()
+
+export const DesignStore = defineStore('design', () => {
+  const query = async ({ skip = 0, limit = undefined, ...info } = {}) => {
+    let cond = {}
+    if (skip) cond.skip = skip
+    if (limit) cond.limit = limit
+    cond = { ...cond, ...info }
+    const res = await axios.$get(`${url}`, cond)
+    return res
+  }
+  const fetch = async (payload) => {
+    const res = await axios.$get(`${url}/${payload}`)
+    return res
+  }
+  const create = async (payload) => {
+    const res = await axios.$post(`${url}`, payload)
+    return res
+  }
+  const update = async (payload) => {
+    const id = get(payload, 'id', get(payload, '_id'))
+    const res = await axios.$post(`${url}/${id}`, payload)
+    return res
+  }
+  const del = async (payload) => {
+    const res = await axios.$delete(`${url}/${payload}`)
+    return res
+  }
+  return {
+    query,
+    fetch,
+    create,
+    update,
+    del
+  }
+})

+ 45 - 0
src/store/api/platform/match.js

@@ -0,0 +1,45 @@
+import { defineStore } from 'pinia'
+import { AxiosWrapper } from '@/utils/axios-wrapper'
+import { get } from 'lodash-es'
+const url = '/match'
+const axios = new AxiosWrapper()
+
+export const MatchStore = defineStore('match', () => {
+  const query = async ({ skip = 0, limit = undefined, ...info } = {}) => {
+    let cond = {}
+    if (skip) cond.skip = skip
+    if (limit) cond.limit = limit
+    cond = { ...cond, ...info }
+    const res = await axios.$get(`${url}`, cond)
+    return res
+  }
+  const fetch = async (payload) => {
+    const res = await axios.$get(`${url}/${payload}`)
+    return res
+  }
+  const detail = async (payload) => {
+    const res = await axios.$get(`${url}/detail/${payload}`)
+    return res
+  }
+  const create = async (payload) => {
+    const res = await axios.$post(`${url}`, payload)
+    return res
+  }
+  const update = async (payload) => {
+    const id = get(payload, 'id', get(payload, '_id'))
+    const res = await axios.$post(`${url}/${id}`, payload)
+    return res
+  }
+  const del = async (payload) => {
+    const res = await axios.$delete(`${url}/${payload}`)
+    return res
+  }
+  return {
+    query,
+    fetch,
+    detail,
+    create,
+    update,
+    del
+  }
+})

+ 40 - 0
src/store/api/platform/news.js

@@ -0,0 +1,40 @@
+import { defineStore } from 'pinia'
+import { AxiosWrapper } from '@/utils/axios-wrapper'
+import { get } from 'lodash-es'
+const url = '/news'
+const axios = new AxiosWrapper()
+
+export const NewsStore = defineStore('news', () => {
+  const query = async ({ skip = 0, limit = undefined, ...info } = {}) => {
+    let cond = {}
+    if (skip) cond.skip = skip
+    if (limit) cond.limit = limit
+    cond = { ...cond, ...info }
+    const res = await axios.$get(`${url}`, cond)
+    return res
+  }
+  const fetch = async (payload) => {
+    const res = await axios.$get(`${url}/${payload}`)
+    return res
+  }
+  const create = async (payload) => {
+    const res = await axios.$post(`${url}`, payload)
+    return res
+  }
+  const update = async (payload) => {
+    const id = get(payload, 'id', get(payload, '_id'))
+    const res = await axios.$post(`${url}/${id}`, payload)
+    return res
+  }
+  const del = async (payload) => {
+    const res = await axios.$delete(`${url}/${payload}`)
+    return res
+  }
+  return {
+    query,
+    fetch,
+    create,
+    update,
+    del
+  }
+})

+ 54 - 0
src/store/api/platform/project.js

@@ -0,0 +1,54 @@
+import { defineStore } from 'pinia'
+import { AxiosWrapper } from '@/utils/axios-wrapper'
+import { get } from 'lodash-es'
+const url = '/project'
+const axios = new AxiosWrapper()
+
+export const ProjectStore = defineStore('project', () => {
+  const query = async ({ skip = 0, limit = undefined, ...info } = {}) => {
+    let cond = {}
+    if (skip) cond.skip = skip
+    if (limit) cond.limit = limit
+    cond = { ...cond, ...info }
+    const res = await axios.$get(`${url}`, cond)
+    return res
+  }
+  const list = async ({ skip = 0, limit = undefined, ...info } = {}) => {
+    let cond = {}
+    if (skip) cond.skip = skip
+    if (limit) cond.limit = limit
+    cond = { ...cond, ...info }
+    const res = await axios.$get(`${url}/list`, cond)
+    return res
+  }
+  const fetch = async (payload) => {
+    const res = await axios.$get(`${url}/${payload}`)
+    return res
+  }
+  const detail = async (payload) => {
+    const res = await axios.$get(`${url}/detail/${payload}`)
+    return res
+  }
+  const create = async (payload) => {
+    const res = await axios.$post(`${url}`, payload)
+    return res
+  }
+  const update = async (payload) => {
+    const id = get(payload, 'id', get(payload, '_id'))
+    const res = await axios.$post(`${url}/${id}`, payload)
+    return res
+  }
+  const del = async (payload) => {
+    const res = await axios.$delete(`${url}/${payload}`)
+    return res
+  }
+  return {
+    query,
+    list,
+    fetch,
+    detail,
+    create,
+    update,
+    del
+  }
+})

+ 49 - 0
src/store/api/platform/sign.js

@@ -0,0 +1,49 @@
+import { defineStore } from 'pinia'
+import { AxiosWrapper } from '@/utils/axios-wrapper'
+import { get } from 'lodash-es'
+const url = '/sign'
+const axios = new AxiosWrapper()
+
+export const SignStore = defineStore('sign', () => {
+  const query = async ({ skip = 0, limit = undefined, ...info } = {}) => {
+    let cond = {}
+    if (skip) cond.skip = skip
+    if (limit) cond.limit = limit
+    cond = { ...cond, ...info }
+    const res = await axios.$get(`${url}`, cond)
+    return res
+  }
+  const sign = async ({ skip = 0, limit = undefined, ...info } = {}) => {
+    let cond = {}
+    if (skip) cond.skip = skip
+    if (limit) cond.limit = limit
+    cond = { ...cond, ...info }
+    const res = await axios.$get(`${url}/sign`, cond)
+    return res
+  }
+  const fetch = async (payload) => {
+    const res = await axios.$get(`${url}/${payload}`)
+    return res
+  }
+  const create = async (payload) => {
+    const res = await axios.$post(`${url}`, payload)
+    return res
+  }
+  const update = async (payload) => {
+    const id = get(payload, 'id', get(payload, '_id'))
+    const res = await axios.$post(`${url}/${id}`, payload)
+    return res
+  }
+  const del = async (payload) => {
+    const res = await axios.$delete(`${url}/${payload}`)
+    return res
+  }
+  return {
+    query,
+    sign,
+    fetch,
+    create,
+    update,
+    del
+  }
+})

+ 54 - 0
src/store/api/platform/supply.js

@@ -0,0 +1,54 @@
+import { defineStore } from 'pinia'
+import { AxiosWrapper } from '@/utils/axios-wrapper'
+import { get } from 'lodash-es'
+const url = '/supply'
+const axios = new AxiosWrapper()
+
+export const SupplyStore = defineStore('supply', () => {
+  const query = async ({ skip = 0, limit = undefined, ...info } = {}) => {
+    let cond = {}
+    if (skip) cond.skip = skip
+    if (limit) cond.limit = limit
+    cond = { ...cond, ...info }
+    const res = await axios.$get(`${url}`, cond)
+    return res
+  }
+  const list = async ({ skip = 0, limit = undefined, ...info } = {}) => {
+    let cond = {}
+    if (skip) cond.skip = skip
+    if (limit) cond.limit = limit
+    cond = { ...cond, ...info }
+    const res = await axios.$get(`${url}/list`, cond)
+    return res
+  }
+  const fetch = async (payload) => {
+    const res = await axios.$get(`${url}/${payload}`)
+    return res
+  }
+  const detail = async (payload) => {
+    const res = await axios.$get(`${url}/detail/${payload}`)
+    return res
+  }
+  const create = async (payload) => {
+    const res = await axios.$post(`${url}`, payload)
+    return res
+  }
+  const update = async (payload) => {
+    const id = get(payload, 'id', get(payload, '_id'))
+    const res = await axios.$post(`${url}/${id}`, payload)
+    return res
+  }
+  const del = async (payload) => {
+    const res = await axios.$delete(`${url}/${payload}`)
+    return res
+  }
+  return {
+    query,
+    list,
+    fetch,
+    create,
+    detail,
+    update,
+    del
+  }
+})

+ 37 - 0
src/store/api/platform/tool.js

@@ -0,0 +1,37 @@
+import { defineStore } from 'pinia'
+import { AxiosWrapper } from '@/utils/axios-wrapper'
+const url = '/tool'
+const axios = new AxiosWrapper()
+
+export const ToolStore = defineStore('tool', () => {
+  const query = async ({ skip = 0, limit = undefined, ...info } = {}) => {
+    let cond = {}
+    if (skip) cond.skip = skip
+    if (limit) cond.limit = limit
+    cond = { ...cond, ...info }
+    const res = await axios.$get(`${url}/query`, cond)
+    return res
+  }
+  const getCollection = async ({ skip = 0, limit = undefined, ...info } = {}) => {
+    let cond = {}
+    if (skip) cond.skip = skip
+    if (limit) cond.limit = limit
+    cond = { ...cond, ...info }
+    const res = await axios.$get(`${url}/MyCollection`, cond)
+    return res
+  }
+  const total = async (info) => {
+    const res = await axios.$get(`${url}/getTotal`, info)
+    return res
+  }
+  const getTotal = async () => {
+    const res = await axios.$get(`${url}/CollectionTotal`)
+    return res
+  }
+  return {
+    query,
+    total,
+    getCollection,
+    getTotal
+  }
+})

+ 40 - 0
src/store/api/system/dictData.js

@@ -0,0 +1,40 @@
+import { defineStore } from 'pinia'
+import { AxiosWrapper } from '@/utils/axios-wrapper'
+import { get } from 'lodash-es'
+const url = '/dictData'
+const axios = new AxiosWrapper()
+
+export const DictDataStore = defineStore('dictData', () => {
+  const query = async ({ skip = 0, limit = undefined, ...info } = {}) => {
+    let cond = {}
+    if (skip) cond.skip = skip
+    if (limit) cond.limit = limit
+    cond = { ...cond, ...info }
+    const res = await axios.$get(`${url}`, cond)
+    return res
+  }
+  const fetch = async (payload) => {
+    const res = await axios.$get(`${url}/${payload}`)
+    return res
+  }
+  const create = async (payload) => {
+    const res = await axios.$post(`${url}`, payload)
+    return res
+  }
+  const update = async (payload) => {
+    const id = get(payload, 'id', get(payload, '_id'))
+    const res = await axios.$post(`${url}/${id}`, payload)
+    return res
+  }
+  const del = async (payload) => {
+    const res = await axios.$delete(`${url}/${payload}`)
+    return res
+  }
+  return {
+    query,
+    fetch,
+    create,
+    update,
+    del
+  }
+})

+ 40 - 0
src/store/api/system/dictType.js

@@ -0,0 +1,40 @@
+import { defineStore } from 'pinia'
+import { AxiosWrapper } from '@/utils/axios-wrapper'
+import { get } from 'lodash-es'
+const url = '/dictType'
+const axios = new AxiosWrapper()
+
+export const DictTypeStore = defineStore('dictType', () => {
+  const query = async ({ skip = 0, limit = undefined, ...info } = {}) => {
+    let cond = {}
+    if (skip) cond.skip = skip
+    if (limit) cond.limit = limit
+    cond = { ...cond, ...info }
+    const res = await axios.$get(`${url}`, cond)
+    return res
+  }
+  const fetch = async (payload) => {
+    const res = await axios.$get(`${url}/${payload}`)
+    return res
+  }
+  const create = async (payload) => {
+    const res = await axios.$post(`${url}`, payload)
+    return res
+  }
+  const update = async (payload) => {
+    const id = get(payload, 'id', get(payload, '_id'))
+    const res = await axios.$post(`${url}/${id}`, payload)
+    return res
+  }
+  const del = async (payload) => {
+    const res = await axios.$delete(`${url}/${payload}`)
+    return res
+  }
+  return {
+    query,
+    fetch,
+    create,
+    update,
+    del
+  }
+})

+ 40 - 0
src/store/api/system/menus.js

@@ -0,0 +1,40 @@
+import { defineStore } from 'pinia'
+import { AxiosWrapper } from '@/utils/axios-wrapper'
+import { get } from 'lodash-es'
+const url = '/menus'
+const axios = new AxiosWrapper()
+
+export const MenusStore = defineStore('menus', () => {
+  const query = async ({ skip = 0, limit = undefined, ...info } = {}) => {
+    let cond = {}
+    if (skip) cond.skip = skip
+    if (limit) cond.limit = limit
+    cond = { ...cond, ...info }
+    const res = await axios.$get(`${url}`, cond)
+    return res
+  }
+  const fetch = async (payload) => {
+    const res = await axios.$get(`${url}/${payload}`)
+    return res
+  }
+  const create = async (payload) => {
+    const res = await axios.$post(`${url}`, payload)
+    return res
+  }
+  const update = async (payload) => {
+    const id = get(payload, 'id', get(payload, '_id'))
+    const res = await axios.$post(`${url}/${id}`, payload)
+    return res
+  }
+  const del = async (payload) => {
+    const res = await axios.$delete(`${url}/${payload}`)
+    return res
+  }
+  return {
+    query,
+    fetch,
+    create,
+    update,
+    del
+  }
+})

+ 40 - 0
src/store/api/system/role.js

@@ -0,0 +1,40 @@
+import { defineStore } from 'pinia'
+import { AxiosWrapper } from '@/utils/axios-wrapper'
+import { get } from 'lodash-es'
+const url = '/role'
+const axios = new AxiosWrapper()
+
+export const RoleStore = defineStore('role', () => {
+  const query = async ({ skip = 0, limit = undefined, ...info } = {}) => {
+    let cond = {}
+    if (skip) cond.skip = skip
+    if (limit) cond.limit = limit
+    cond = { ...cond, ...info }
+    const res = await axios.$get(`${url}`, cond)
+    return res
+  }
+  const fetch = async (payload) => {
+    const res = await axios.$get(`${url}/${payload}`)
+    return res
+  }
+  const create = async (payload) => {
+    const res = await axios.$post(`${url}`, payload)
+    return res
+  }
+  const update = async (payload) => {
+    const id = get(payload, 'id', get(payload, '_id'))
+    const res = await axios.$post(`${url}/${id}`, payload)
+    return res
+  }
+  const del = async (payload) => {
+    const res = await axios.$delete(`${url}/${payload}`)
+    return res
+  }
+  return {
+    query,
+    fetch,
+    create,
+    update,
+    del
+  }
+})

+ 40 - 0
src/store/api/system/tags.js

@@ -0,0 +1,40 @@
+import { defineStore } from 'pinia'
+import { AxiosWrapper } from '@/utils/axios-wrapper'
+import { get } from 'lodash-es'
+const url = '/tags'
+const axios = new AxiosWrapper()
+
+export const TagsStore = defineStore('tags', () => {
+  const query = async ({ skip = 0, limit = undefined, ...info } = {}) => {
+    let cond = {}
+    if (skip) cond.skip = skip
+    if (limit) cond.limit = limit
+    cond = { ...cond, ...info }
+    const res = await axios.$get(`${url}`, cond)
+    return res
+  }
+  const fetch = async (payload) => {
+    const res = await axios.$get(`${url}/${payload}`)
+    return res
+  }
+  const create = async (payload) => {
+    const res = await axios.$post(`${url}`, payload)
+    return res
+  }
+  const update = async (payload) => {
+    const id = get(payload, 'id', get(payload, '_id'))
+    const res = await axios.$post(`${url}/${id}`, payload)
+    return res
+  }
+  const del = async (payload) => {
+    const res = await axios.$delete(`${url}/${payload}`)
+    return res
+  }
+  return {
+    query,
+    fetch,
+    create,
+    update,
+    del
+  }
+})

+ 40 - 0
src/store/api/user/admin.js

@@ -0,0 +1,40 @@
+import { defineStore } from 'pinia'
+import { AxiosWrapper } from '@/utils/axios-wrapper'
+import { get } from 'lodash-es'
+const url = '/admin'
+const axios = new AxiosWrapper()
+
+export const AdminStore = defineStore('admin', () => {
+  const query = async ({ skip = 0, limit = undefined, ...info } = {}) => {
+    let cond = {}
+    if (skip) cond.skip = skip
+    if (limit) cond.limit = limit
+    cond = { ...cond, ...info }
+    const res = await axios.$get(`${url}`, cond)
+    return res
+  }
+  const fetch = async (payload) => {
+    const res = await axios.$get(`${url}/${payload}`)
+    return res
+  }
+  const create = async (payload) => {
+    const res = await axios.$post(`${url}`, payload)
+    return res
+  }
+  const update = async (payload) => {
+    const id = get(payload, 'id', get(payload, '_id'))
+    const res = await axios.$post(`${url}/${id}`, payload)
+    return res
+  }
+  const del = async (payload) => {
+    const res = await axios.$delete(`${url}/${payload}`)
+    return res
+  }
+  return {
+    query,
+    fetch,
+    create,
+    update,
+    del
+  }
+})

+ 40 - 0
src/store/api/user/association.js

@@ -0,0 +1,40 @@
+import { defineStore } from 'pinia'
+import { AxiosWrapper } from '@/utils/axios-wrapper'
+import { get } from 'lodash-es'
+const url = '/association'
+const axios = new AxiosWrapper()
+
+export const AssociationStore = defineStore('association', () => {
+  const query = async ({ skip = 0, limit = undefined, ...info } = {}) => {
+    let cond = {}
+    if (skip) cond.skip = skip
+    if (limit) cond.limit = limit
+    cond = { ...cond, ...info }
+    const res = await axios.$get(`${url}`, cond)
+    return res
+  }
+  const fetch = async (payload) => {
+    const res = await axios.$get(`${url}/${payload}`)
+    return res
+  }
+  const create = async (payload) => {
+    const res = await axios.$post(`${url}`, payload)
+    return res
+  }
+  const update = async (payload) => {
+    const id = get(payload, 'id', get(payload, '_id'))
+    const res = await axios.$post(`${url}/${id}`, payload)
+    return res
+  }
+  const del = async (payload) => {
+    const res = await axios.$delete(`${url}/${payload}`)
+    return res
+  }
+  return {
+    query,
+    fetch,
+    create,
+    update,
+    del
+  }
+})

+ 54 - 0
src/store/api/user/company.js

@@ -0,0 +1,54 @@
+import { defineStore } from 'pinia'
+import { AxiosWrapper } from '@/utils/axios-wrapper'
+import { get } from 'lodash-es'
+const url = '/company'
+const axios = new AxiosWrapper()
+
+export const CompanyStore = defineStore('company', () => {
+  const query = async ({ skip = 0, limit = undefined, ...info } = {}) => {
+    let cond = {}
+    if (skip) cond.skip = skip
+    if (limit) cond.limit = limit
+    cond = { ...cond, ...info }
+    const res = await axios.$get(`${url}`, cond)
+    return res
+  }
+  const list = async ({ skip = 0, limit = undefined, ...info } = {}) => {
+    let cond = {}
+    if (skip) cond.skip = skip
+    if (limit) cond.limit = limit
+    cond = { ...cond, ...info }
+    const res = await axios.$get(`${url}/list`, cond)
+    return res
+  }
+  const fetch = async (payload) => {
+    const res = await axios.$get(`${url}/${payload}`)
+    return res
+  }
+  const detail = async (payload) => {
+    const res = await axios.$get(`${url}/detail/${payload}`)
+    return res
+  }
+  const create = async (payload) => {
+    const res = await axios.$post(`${url}`, payload)
+    return res
+  }
+  const update = async (payload) => {
+    const id = get(payload, 'id', get(payload, '_id'))
+    const res = await axios.$post(`${url}/${id}`, payload)
+    return res
+  }
+  const del = async (payload) => {
+    const res = await axios.$delete(`${url}/${payload}`)
+    return res
+  }
+  return {
+    query,
+    list,
+    fetch,
+    detail,
+    create,
+    update,
+    del
+  }
+})

+ 40 - 0
src/store/api/user/competition.js

@@ -0,0 +1,40 @@
+import { defineStore } from 'pinia'
+import { AxiosWrapper } from '@/utils/axios-wrapper'
+import { get } from 'lodash-es'
+const url = '/competition'
+const axios = new AxiosWrapper()
+
+export const CompetitionStore = defineStore('competition', () => {
+  const query = async ({ skip = 0, limit = undefined, ...info } = {}) => {
+    let cond = {}
+    if (skip) cond.skip = skip
+    if (limit) cond.limit = limit
+    cond = { ...cond, ...info }
+    const res = await axios.$get(`${url}`, cond)
+    return res
+  }
+  const fetch = async (payload) => {
+    const res = await axios.$get(`${url}/${payload}`)
+    return res
+  }
+  const create = async (payload) => {
+    const res = await axios.$post(`${url}`, payload)
+    return res
+  }
+  const update = async (payload) => {
+    const id = get(payload, 'id', get(payload, '_id'))
+    const res = await axios.$post(`${url}/${id}`, payload)
+    return res
+  }
+  const del = async (payload) => {
+    const res = await axios.$delete(`${url}/${payload}`)
+    return res
+  }
+  return {
+    query,
+    fetch,
+    create,
+    update,
+    del
+  }
+})

+ 54 - 0
src/store/api/user/expert.js

@@ -0,0 +1,54 @@
+import { defineStore } from 'pinia'
+import { AxiosWrapper } from '@/utils/axios-wrapper'
+import { get } from 'lodash-es'
+const url = '/expert'
+const axios = new AxiosWrapper()
+
+export const ExpertStore = defineStore('expert', () => {
+  const query = async ({ skip = 0, limit = undefined, ...info } = {}) => {
+    let cond = {}
+    if (skip) cond.skip = skip
+    if (limit) cond.limit = limit
+    cond = { ...cond, ...info }
+    const res = await axios.$get(`${url}`, cond)
+    return res
+  }
+  const list = async ({ skip = 0, limit = undefined, ...info } = {}) => {
+    let cond = {}
+    if (skip) cond.skip = skip
+    if (limit) cond.limit = limit
+    cond = { ...cond, ...info }
+    const res = await axios.$get(`${url}/list`, cond)
+    return res
+  }
+  const fetch = async (payload) => {
+    const res = await axios.$get(`${url}/${payload}`)
+    return res
+  }
+  const detail = async (payload) => {
+    const res = await axios.$get(`${url}/detail/${payload}`)
+    return res
+  }
+  const create = async (payload) => {
+    const res = await axios.$post(`${url}`, payload)
+    return res
+  }
+  const update = async (payload) => {
+    const id = get(payload, 'id', get(payload, '_id'))
+    const res = await axios.$post(`${url}/${id}`, payload)
+    return res
+  }
+  const del = async (payload) => {
+    const res = await axios.$delete(`${url}/${payload}`)
+    return res
+  }
+  return {
+    query,
+    list,
+    fetch,
+    detail,
+    create,
+    update,
+    del
+  }
+})

+ 40 - 0
src/store/api/user/incubator.js

@@ -0,0 +1,40 @@
+import { defineStore } from 'pinia'
+import { AxiosWrapper } from '@/utils/axios-wrapper'
+import { get } from 'lodash-es'
+const url = '/incubator'
+const axios = new AxiosWrapper()
+
+export const IncubatorStore = defineStore('incubator', () => {
+  const query = async ({ skip = 0, limit = undefined, ...info } = {}) => {
+    let cond = {}
+    if (skip) cond.skip = skip
+    if (limit) cond.limit = limit
+    cond = { ...cond, ...info }
+    const res = await axios.$get(`${url}`, cond)
+    return res
+  }
+  const fetch = async (payload) => {
+    const res = await axios.$get(`${url}/${payload}`)
+    return res
+  }
+  const create = async (payload) => {
+    const res = await axios.$post(`${url}`, payload)
+    return res
+  }
+  const update = async (payload) => {
+    const id = get(payload, 'id', get(payload, '_id'))
+    const res = await axios.$post(`${url}/${id}`, payload)
+    return res
+  }
+  const del = async (payload) => {
+    const res = await axios.$delete(`${url}/${payload}`)
+    return res
+  }
+  return {
+    query,
+    fetch,
+    create,
+    update,
+    del
+  }
+})

+ 40 - 0
src/store/api/user/investment.js

@@ -0,0 +1,40 @@
+import { defineStore } from 'pinia'
+import { AxiosWrapper } from '@/utils/axios-wrapper'
+import { get } from 'lodash-es'
+const url = '/investment'
+const axios = new AxiosWrapper()
+
+export const InvestmentStore = defineStore('investment', () => {
+  const query = async ({ skip = 0, limit = undefined, ...info } = {}) => {
+    let cond = {}
+    if (skip) cond.skip = skip
+    if (limit) cond.limit = limit
+    cond = { ...cond, ...info }
+    const res = await axios.$get(`${url}`, cond)
+    return res
+  }
+  const fetch = async (payload) => {
+    const res = await axios.$get(`${url}/${payload}`)
+    return res
+  }
+  const create = async (payload) => {
+    const res = await axios.$post(`${url}`, payload)
+    return res
+  }
+  const update = async (payload) => {
+    const id = get(payload, 'id', get(payload, '_id'))
+    const res = await axios.$post(`${url}/${id}`, payload)
+    return res
+  }
+  const del = async (payload) => {
+    const res = await axios.$delete(`${url}/${payload}`)
+    return res
+  }
+  return {
+    query,
+    fetch,
+    create,
+    update,
+    del
+  }
+})

+ 40 - 0
src/store/api/user/state.js

@@ -0,0 +1,40 @@
+import { defineStore } from 'pinia'
+import { AxiosWrapper } from '@/utils/axios-wrapper'
+import { get } from 'lodash-es'
+const url = '/state'
+const axios = new AxiosWrapper()
+
+export const StateStore = defineStore('state', () => {
+  const query = async ({ skip = 0, limit = undefined, ...info } = {}) => {
+    let cond = {}
+    if (skip) cond.skip = skip
+    if (limit) cond.limit = limit
+    cond = { ...cond, ...info }
+    const res = await axios.$get(`${url}`, cond)
+    return res
+  }
+  const fetch = async (payload) => {
+    const res = await axios.$get(`${url}/${payload}`)
+    return res
+  }
+  const create = async (payload) => {
+    const res = await axios.$post(`${url}`, payload)
+    return res
+  }
+  const update = async (payload) => {
+    const id = get(payload, 'id', get(payload, '_id'))
+    const res = await axios.$post(`${url}/${id}`, payload)
+    return res
+  }
+  const del = async (payload) => {
+    const res = await axios.$delete(`${url}/${payload}`)
+    return res
+  }
+  return {
+    query,
+    fetch,
+    create,
+    update,
+    del
+  }
+})

+ 40 - 0
src/store/api/user/unit.js

@@ -0,0 +1,40 @@
+import { defineStore } from 'pinia'
+import { AxiosWrapper } from '@/utils/axios-wrapper'
+import { get } from 'lodash-es'
+const url = '/unit'
+const axios = new AxiosWrapper()
+
+export const UnitStore = defineStore('unit', () => {
+  const query = async ({ skip = 0, limit = undefined, ...info } = {}) => {
+    let cond = {}
+    if (skip) cond.skip = skip
+    if (limit) cond.limit = limit
+    cond = { ...cond, ...info }
+    const res = await axios.$get(`${url}`, cond)
+    return res
+  }
+  const fetch = async (payload) => {
+    const res = await axios.$get(`${url}/${payload}`)
+    return res
+  }
+  const create = async (payload) => {
+    const res = await axios.$post(`${url}`, payload)
+    return res
+  }
+  const update = async (payload) => {
+    const id = get(payload, 'id', get(payload, '_id'))
+    const res = await axios.$post(`${url}/${id}`, payload)
+    return res
+  }
+  const del = async (payload) => {
+    const res = await axios.$delete(`${url}/${payload}`)
+    return res
+  }
+  return {
+    query,
+    fetch,
+    create,
+    update,
+    del
+  }
+})

+ 45 - 0
src/store/api/user/user.js

@@ -0,0 +1,45 @@
+import { defineStore } from 'pinia'
+import { AxiosWrapper } from '@/utils/axios-wrapper'
+import { get } from 'lodash-es'
+const url = '/user'
+const axios = new AxiosWrapper()
+
+export const UsersStore = defineStore('users', () => {
+  const query = async ({ skip = 0, limit = undefined, ...info } = {}) => {
+    let cond = {}
+    if (skip) cond.skip = skip
+    if (limit) cond.limit = limit
+    cond = { ...cond, ...info }
+    const res = await axios.$get(`${url}`, cond)
+    return res
+  }
+  const fetch = async (payload) => {
+    const res = await axios.$get(`${url}/${payload}`)
+    return res
+  }
+  const detail = async (payload) => {
+    const res = await axios.$get(`${url}/detail/${payload}`)
+    return res
+  }
+  const create = async (payload) => {
+    const res = await axios.$post(`${url}`, payload)
+    return res
+  }
+  const update = async (payload) => {
+    const id = get(payload, 'id', get(payload, '_id'))
+    const res = await axios.$post(`${url}/${id}`, payload)
+    return res
+  }
+  const del = async (payload) => {
+    const res = await axios.$delete(`${url}/${payload}`)
+    return res
+  }
+  return {
+    query,
+    fetch,
+    detail,
+    create,
+    update,
+    del
+  }
+})

+ 12 - 0
src/store/index.js

@@ -0,0 +1,12 @@
+import { createPinia } from 'pinia'
+
+const store = createPinia()
+
+// 全局注册 store
+export function setupStore(app) {
+  app.use(store)
+}
+
+export * from './modules/app'
+export * from './modules/tagsView'
+export { store }

+ 94 - 0
src/store/modules/app.js

@@ -0,0 +1,94 @@
+import defaultSettings from '@/settings'
+import { useStorage } from '@vueuse/core'
+import { useCookies } from '@vueuse/integrations/useCookies'
+// 导入 Element Plus 中英文语言包
+import zhCn from 'element-plus/es/locale/lang/zh-cn'
+import en from 'element-plus/es/locale/lang/en'
+const cookies = useCookies()
+
+// setup
+export const useAppStore = defineStore('app', () => {
+  // state
+  const device = useStorage('device', 'desktop')
+  const size = useStorage('size', defaultSettings.size)
+  let lang = cookies.get('locale')
+  if(!lang) lang = defaultSettings.language
+  const language = useStorage('language', lang)
+  const sidebarStatus = useStorage('sidebarStatus', 'closed')
+
+  const sidebar = reactive({
+    opened: sidebarStatus.value !== 'closed',
+    withoutAnimation: false
+  })
+  const activeTopMenuPath = useStorage('activeTopMenuPath', '')
+  /**
+   * 根据语言标识读取对应的语言包
+   */
+  const locale = computed(() => {
+    if (language?.value == 'en-us') {
+      return en
+    } else {
+      return zhCn
+    }
+  })
+
+  // actions
+  function toggleSidebar() {
+    sidebar.opened = !sidebar.opened
+    if (sidebar.opened) {
+      sidebarStatus.value = 'opened'
+    } else {
+      sidebarStatus.value = 'closed'
+    }
+  }
+
+  function closeSideBar() {
+    sidebar.opened = false
+    sidebarStatus.value = 'closed'
+  }
+
+  function openSideBar() {
+    sidebar.opened = true
+    sidebarStatus.value = 'opened'
+  }
+
+  function toggleDevice(val) {
+    device.value = val
+  }
+
+  function changeSize(val) {
+    size.value = val
+  }
+  /**
+   * 切换语言
+   *
+   * @param val
+   */
+  function changeLanguage(val) {
+    language.value = val
+    // cookie切换
+    cookies.set('locale', val, { path: '/' })
+  }
+  /**
+   * 混合模式顶部切换
+   */
+  function activeTopMenu(val) {
+    activeTopMenuPath.value = val
+  }
+
+  return {
+    device,
+    sidebar,
+    language,
+    locale,
+    size,
+    activeTopMenu,
+    toggleDevice,
+    changeSize,
+    changeLanguage,
+    toggleSidebar,
+    closeSideBar,
+    openSideBar,
+    activeTopMenuPath
+  }
+})

+ 206 - 0
src/store/modules/tagsView.js

@@ -0,0 +1,206 @@
+export const useTagsViewStore = defineStore('tagsView', () => {
+  const visitedViews = ref([{ affix: true, fullPath: '/', keepAlive: true, name: 'home', path: '/', title: 'home' }])
+  const cachedViews = ref([])
+
+  /**
+   * 添加已访问视图到已访问视图列表中
+   */
+  function addVisitedView(view) {
+    // 如果已经存在于已访问的视图列表中,则不再添加
+    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) {
+    const viewName = view.name
+    // 如果缓存视图名称已经存在于缓存视图列表中,则不再添加
+    if (cachedViews.value.includes(viewName)) {
+      return
+    }
+    // 如果视图需要缓存(keepAlive),则将其路由名称添加到缓存视图列表中
+    if (view.keepAlive) {
+      cachedViews.value.push(viewName)
+    }
+  }
+
+  /**
+   * 从已访问视图列表中删除指定的视图
+   */
+  function delVisitedView(view) {
+    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) {
+    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) {
+    return new Promise((resolve) => {
+      visitedViews.value = visitedViews.value.filter((v) => {
+        return v?.affix || v.path === view.path
+      })
+      resolve([...visitedViews.value])
+    })
+  }
+
+  function delOtherCachedViews(view) {
+    const viewName = view.name
+    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) {
+    for (let v of visitedViews.value) {
+      if (v.path === view.path) {
+        v = Object.assign(v, view)
+        break
+      }
+    }
+  }
+
+  function addView(view) {
+    addVisitedView(view)
+    addCachedView(view)
+  }
+
+  function delView(view) {
+    return new Promise((resolve) => {
+      delVisitedView(view)
+      delCachedView(view)
+      resolve({
+        visitedViews: [...visitedViews.value],
+        cachedViews: [...cachedViews.value]
+      })
+    })
+  }
+
+  function delOtherViews(view) {
+    return new Promise((resolve) => {
+      delOtherVisitedViews(view)
+      delOtherCachedViews(view)
+      resolve({
+        visitedViews: [...visitedViews.value],
+        cachedViews: [...cachedViews.value]
+      })
+    })
+  }
+
+  function delLeftViews(view) {
+    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) {
+    return new Promise((resolve) => {
+      const currIndex = visitedViews.value.findIndex((v) => v.path === view.path)
+      if (currIndex === -1) {
+        return
+      }
+      visitedViews.value = visitedViews.value.filter((item, index) => {
+        if (index <= currIndex || item?.affix) {
+          return true
+        }
+      })
+      resolve({
+        visitedViews: [...visitedViews.value]
+      })
+    })
+  }
+
+  function delAllViews() {
+    return new Promise((resolve) => {
+      const affixTags = visitedViews.value.filter((tag) => tag?.affix)
+      visitedViews.value = affixTags
+      cachedViews.value = []
+      resolve({
+        visitedViews: [...visitedViews.value],
+        cachedViews: [...cachedViews.value]
+      })
+    })
+  }
+
+  function delAllVisitedViews() {
+    return new Promise((resolve) => {
+      const affixTags = visitedViews.value.filter((tag) => tag?.affix)
+      visitedViews.value = affixTags
+      resolve([...visitedViews.value])
+    })
+  }
+
+  function delAllCachedViews() {
+    return new Promise((resolve) => {
+      cachedViews.value = []
+      resolve([...cachedViews.value])
+    })
+  }
+
+  return {
+    visitedViews,
+    cachedViews,
+    addVisitedView,
+    addCachedView,
+    delVisitedView,
+    delCachedView,
+    delOtherVisitedViews,
+    delOtherCachedViews,
+    updateVisitedView,
+    addView,
+    delView,
+    delOtherViews,
+    delLeftViews,
+    delRightViews,
+    delAllViews,
+    delAllVisitedViews,
+    delAllCachedViews
+  }
+})

+ 23 - 0
src/store/user.js

@@ -0,0 +1,23 @@
+import { defineStore } from 'pinia'
+export const UserStore = defineStore('user', () => {
+  const user = ref({})
+  const menus = ref([])
+  /**
+   * 将用户信息存起来;用户信息是在路由变更时,路由前置守卫去服务进行兑换而来
+   * @param {String} payload token串
+   */
+  const setUser = (payload) => {
+    user.value = payload
+  }
+  /**
+   * 清除用户信息并删除token
+   */
+  const logOut = () => {
+    user.value = {}
+    localStorage.removeItem('token')
+  }
+  const setMenus = (payload) => {
+    menus.value = payload
+  }
+  return { user, setUser, logOut, menus, setMenus }
+})

+ 118 - 0
src/styles/variables.scss

@@ -0,0 +1,118 @@
+// 全局变量文件,比如命名为 _variables.scss
+$global-font-size-12: 12px;
+$global-font-size-12: 13px;
+$global-font-size-14: 14px;
+$global-font-size-15: 14px;
+$global-font-size-16: 16px;
+$global-font-size-17: 17px;
+$global-font-size-18: 18px;
+$global-font-size-19: 19px;
+$global-font-size-20: 20px;
+$global-font-size-22: 22px;
+$global-font-size-23: 23px;
+$global-font-size-24: 24px;
+$global-font-size-25: 25px;
+$global-font-size-26: 26px;
+$global-font-size-30: 30px;
+/* 全局覆盖antd表格表头样式 */
+.ant-table-thead > tr > th {
+  font-size: 18px; /* 设置表头字体大小 */
+}
+
+/* 全局覆盖antd表格表体样式 */
+.ant-table-tbody > tr > td {
+  font-size: 16px; /* 设置表体字体大小 */
+}
+
+.el-form-item__label {
+  font-size: 18px !important; /* 设置标签字体大小 */
+}
+
+.el-input__inner {
+  font-size: 16px !important; /* 设置输入框字体大小 */
+}
+
+/* custom-element-plus.css */
+.el-tabs__item {
+  font-size: 18px !important; /* 设置标签页的字体大小 */
+}
+
+.el-tabs__item-text {
+  font-size: 18px !important; /* 确保标签页文本也有相同的字体大小 */
+}
+
+/* custom-antd.css */
+.ant-tabs-tab {
+  font-size: 18px; /* 或者你想要的任何字体大小 */
+}
+
+/* 如果需要增大标签页标题的字体大小 */
+.ant-tabs-tab-btn {
+  font-size: 18px; /* 保持与上面一致或按需调整 */
+}
+
+/* custom-element-ui.css */
+.el-radio__label {
+  font-size: 16px !important; /* 设置你想要的字体大小 */
+}
+
+/* 如果你还想调整单选按钮本身的字体大小(通常是内部的 span),你也可以添加如下规则 */
+.el-radio__original {
+  font-size: 16px !important; /* 确保内部元素也有相应的字体大小 */
+}
+
+.el-button {
+  font-size: 16px !important; /* 设置按钮的字体大小为你想要的尺寸 */
+}
+/* custom-element-plus.css */
+.el-select-dropdown__item {
+  font-size: 16px !important; /* 设置下拉选项的字体大小 */
+}
+
+.el-cascader__menu-item {
+  font-size: 16px !important; /* 设置级联选择器菜单项的字体大小 */
+}
+
+/* 如果需要调整级联选择器输入框的字体大小 */
+.el-cascader__input {
+  font-size: 16px !important; /* 设置输入框内的字体大小 */
+}
+/* custom-element-plus.css */
+.el-pagination__item {
+  font-size: 16px !important; /* 设置分页项(如页码、跳转按钮等)的字体大小 */
+}
+
+/* 如果需要调整页码选择器的字体大小 */
+.el-select-dropdown__item {
+  font-size: 16px !important; /* 分页选择器下拉项字体大小 */
+}
+
+/* 如果需要调整输入框的字体大小 */
+.el-pagination__editor {
+  font-size: 16px !important; /* 分页输入框的字体大小 */
+}
+
+/* 如果需要调整跳转按钮的字体大小 */
+.el-pagination__go-button {
+  font-size: 16px !important; /* 跳转按钮的字体大小 */
+}
+/* custom-antd-vue.css */
+.ant-descriptions-item-content {
+  font-size: 16px !important; /* 设置描述列表项内容的字体大小 */
+}
+
+/* 如果需要调整标题的字体大小 */
+.ant-descriptions-title {
+  font-size: 18px !important; /* 设置描述列表标题的字体大小 */
+}
+/* custom-antd.css */
+.ant-checkbox-wrapper {
+  font-size: 18px; /* 设置复选框标签的字体大小 */
+}
+
+/* 如果你还想调整复选框内部的文字大小(例如当使用 indeterminate 状态时) */
+.ant-checkbox-input {
+  & + .ant-checkbox-inner {
+    font-size: 16px; /* 设置复选框内部文字的字体大小 */
+  }
+}

+ 129 - 0
src/utils/axios-wrapper.js

@@ -0,0 +1,129 @@
+/* eslint-disable no-console */
+/* eslint-disable no-param-reassign */
+
+import { get, isObject } from 'lodash-es'
+import Axios from 'axios'
+import { trimData, isNullOrUndefined } from './util-methods'
+import { ErrorCode } from './error-code'
+
+let currentRequests = 0
+
+export class AxiosWrapper {
+  constructor({ baseUrl = import.meta.env.VITE_APP_BASE_API, unwrap = true } = {}) {
+    this.baseUrl = baseUrl
+    this.unwrap = unwrap
+  }
+  baseUrl
+  unwrap
+
+  // 替换uri中的参数变量
+  static merge(uri, query) {
+    if (!uri.includes(':')) {
+      return uri
+    }
+    const keys = []
+    const regexp = /\/:([a-z0-9_]+)/gi
+    let res
+    // eslint-disable-next-line no-cond-assign
+    while ((res = regexp.exec(uri)) != null) {
+      keys.push(res[1])
+    }
+    keys.forEach((key) => {
+      const val = get(query, key)
+      if (!isNullOrUndefined(val)) {
+        uri = uri.replace(`:${key}`, `${val}`)
+      }
+    })
+    return uri
+  }
+
+  $get(uri, query, options) {
+    return this.$request(uri, undefined, query, options)
+  }
+
+  $post(uri, data = {}, query, options) {
+    return this.$request(uri, data, query, options)
+  }
+  $delete(uri, data = {}, query, options = {}) {
+    options = { ...options, method: 'delete' }
+    return this.$request(uri, data, query, options)
+  }
+  async $request(uri, data, query, options) {
+    if (query && isObject(query)) {
+      const keys = Object.keys(query)
+      for (const key of keys) {
+        const val = get(query, key)
+        if (val === '') {
+          delete query[key]
+        }
+      }
+    }
+    if (isObject(query) && isObject(options)) {
+      options = { ...options, params: query, method: 'get' }
+    } else if (isObject(query) && !query.params) {
+      options = { params: query }
+    } else if (isObject(query) && query.params) {
+      options = query
+    }
+    if (!options) options = {}
+    if (options.params) options.params = trimData(options.params, null, null)
+    const params = get(options, 'params')
+    const url = AxiosWrapper.merge(uri, params)
+    currentRequests += 1
+    // Indicator.open({
+    //   spinnerType: 'fading-circle',
+    // });
+    try {
+      let returnData
+      const axios = Axios.create({
+        baseURL: this.baseUrl,
+        withCredentials: true
+      })
+      // if (util.token && util.token !== null) axios.defaults.headers.common.Authorization = util.token;
+      const token = localStorage.getItem('token')
+      const apiToken = localStorage.getItem('apiToken')
+      if (token) axios.defaults.headers.common['token'] = token
+      if (apiToken) axios.defaults.headers.common['api-token'] = apiToken
+      const res = await axios.request({
+        method: isNullOrUndefined(data) ? 'get' : 'post',
+        url,
+        data,
+        responseType: 'json',
+        ...options
+      })
+      const returnRes = res.data
+      const { errcode, errmsg, details } = returnRes
+      if (errcode) {
+        console.warn(`[${uri}] fail: ${errcode}-${errmsg} ${details}`)
+        return returnRes
+      }
+      // unwrap data
+      if (this.unwrap) {
+        returnData = returnRes
+      }
+      // 处理apiToken
+      const { apiToken: at, ...others } = returnData
+      if (at) localStorage.setItem('apiToken', at)
+      return others
+    } catch (err) {
+      let errmsg = '接口请求失败,请稍后重试'
+      if (err.response) {
+        const { status } = err.response
+        if (status === 401) errmsg = '用户认证失败,请重新登录'
+        if (status === 403) errmsg = '当前用户不允许执行该操作'
+      }
+      console.error(
+        `[AxiosWrapper] 接口请求失败: ${err.config && err.config.url} - 
+        ${err.message}`
+      )
+      return { errcode: ErrorCode.SERVICE_FAULT, errmsg, details: err.message }
+    } finally {
+      /* eslint-disable */
+      currentRequests -= 1
+      if (currentRequests <= 0) {
+        currentRequests = 0
+        // Indicator.close();
+      }
+    }
+  }
+}

+ 45 - 0
src/utils/base-methods.js

@@ -0,0 +1,45 @@
+import { cloneDeep, get } from 'lodash-es'
+const InitBaseMethods = (store) => {
+  const $checkRes = inject('$checkRes')
+  let limit = inject('limit')
+  const data = ref([])
+  const total = ref(0)
+  const searchForm = ref({})
+  const form = ref({})
+  const b_search = async (query) => {
+    const info = { skip: query.skip, limit: query.limit, ...searchForm.value, is_del: '0' }
+    const res = await store.query(info)
+    if (res.errcode == '0') {
+      data.value = res.data
+      total.value = res.total
+    }
+  }
+  const b_delete = async (data) => {
+    const res = await store.del(data._id)
+    if ($checkRes(res, true)) {
+      b_search({ skip: 0, limit })
+    }
+  }
+  const b_save = async () => {
+    const data = cloneDeep(form.value)
+    let res
+    if (get(data, '_id')) res = await store.update(data)
+    else res = await store.create(data)
+    if ($checkRes(res, true)) {
+      b_search({ skip: 0, limit })
+    }
+  }
+  return {
+    $checkRes,
+    limit,
+    data,
+    total,
+    searchForm,
+    form,
+    b_search,
+    b_delete,
+    b_save
+  }
+}
+
+export default InitBaseMethods

+ 23 - 0
src/utils/checkResult.js

@@ -0,0 +1,23 @@
+import { isFunction, isString } from 'lodash-es'
+import { ElMessage } from 'element-plus'
+export const checkResult = (res, okText, errText) => {
+  const { errcode = 0, errmsg } = res || {}
+  if (errcode === 0) {
+    if (isFunction(okText)) {
+      return okText()
+    }
+    if (isString(okText)) ElMessage.success(okText)
+    else if (okText) ElMessage.success('操作成功')
+    return true
+  }
+  if (isFunction(errText)) {
+    return errText()
+  }
+  ElMessage.error(errText || errmsg)
+  // Message({ message: _errText || errmsg, duration: 60000 });
+  return false
+}
+
+export const InitCheckResult = (app) => {
+  app.provide('$checkRes', checkResult)
+}

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 1964 - 0
src/utils/city.js


+ 61 - 0
src/utils/error-code.js

@@ -0,0 +1,61 @@
+const UNKNOW = 'UNKNOW'
+const BADPARAM = 'BADPARAM'
+const NETWORK = 'NETWORK'
+const JSON_ERROR = 'JSON_ERROR'
+const USER_NOT_EXIST = 'USER_NOT_EXIST'
+const BAD_PASSWORD = 'BAD_PASSWORD'
+const NOT_LOGIN = 'NOT_LOGIN'
+const ACCESS_DENIED = 'ACCESS_DENIED'
+const DATA_NOT_EXIST = 'DATA_NOT_EXIST'
+const DATA_EXISTED = 'DATA_EXISTED'
+const DATA_INVALID = 'DATA_INVALID'
+const VERIFYCODE_INVALID = 'VERIFYCODE_INVALID'
+const SERVICE_FAULT = 'SERVICE_FAULT'
+const DATABASE_FAULT = 'DATABASE_FAULT'
+const FILE_FAULT = 'FILE_FAULT'
+const USER_NOT_BIND = 'USER_NOT_BIND'
+const BUSINESS = 'BUSINESS'
+
+const ErrorCode = {
+  [UNKNOW]: -1,
+  [BADPARAM]: -2,
+  [NETWORK]: -3,
+  [JSON_ERROR]: -4,
+  [USER_NOT_EXIST]: -5,
+  [BAD_PASSWORD]: -6,
+  [NOT_LOGIN]: -7,
+  [ACCESS_DENIED]: -8,
+  [DATA_NOT_EXIST]: -9,
+  [DATA_EXISTED]: -10,
+  [DATA_INVALID]: -11,
+  [VERIFYCODE_INVALID]: -12,
+  [SERVICE_FAULT]: -13,
+  [DATABASE_FAULT]: -14,
+  [FILE_FAULT]: -15,
+  [USER_NOT_BIND]: -16,
+  [BUSINESS]: -100
+}
+
+const errmsg = () => {
+  return {
+    UNKNOW: 'unknow',
+    BADPARAM: 'bad param',
+    NETWORK: 'network',
+    JSON_ERROR: 'json error',
+    USER_NOT_EXIST: 'user not exist',
+    BAD_PASSWORD: 'bad password',
+    NOT_LOGIN: 'not login',
+    ACCESS_DENIED: 'access denied',
+    DATA_NOT_EXIST: 'data not exist',
+    DATA_EXISTED: 'data existed',
+    DATA_INVALID: 'data invalid',
+    VERIFYCODE_INVALID: 'verifycode invalid',
+    SERVICE_FAULT: 'service fault',
+    DATABASE_FAULT: 'database fault',
+    FILE_FAULT: 'file fault',
+    USER_NOT_BIND: 'user not bind',
+    BUSINESS: 'business error'
+  }
+}
+
+export { ErrorCode, errmsg }

+ 26 - 0
src/utils/file.js

@@ -0,0 +1,26 @@
+import Axios from 'axios'
+/**
+ * 上传文件
+ *
+ * @param file
+ */
+export async function uploadFileApi(file) {
+  const formData = new FormData()
+  formData.append('file', file)
+  const axios = Axios.create({
+    baseURL: '/files',
+    withCredentials: true
+  })
+  const token = localStorage.getItem('token')
+  const apiToken = localStorage.getItem('apiToken')
+  if (token) axios.defaults.headers.common['token'] = token
+  if (apiToken) axios.defaults.headers.common['api-token'] = apiToken
+  return await axios.request({
+    url: '/web/template/upload',
+    method: 'post',
+    data: formData,
+    headers: {
+      'Content-Type': 'multipart/form-data'
+    }
+  })
+}

+ 35 - 0
src/utils/util-methods.js

@@ -0,0 +1,35 @@
+import { isUndefined, isString, isNumber, isBoolean } from 'lodash-es'
+export const trimData = (data, exclude = [], include) => {
+  if (data === null || data === undefined) {
+    return data
+  }
+
+  for (const key in data) {
+    if (isUndefined(data[key]) || (exclude && exclude.indexOf(key) !== -1)) delete data[key]
+    if (include && include.indexOf(key) === -1) delete data[key]
+  }
+  return data
+}
+// 检查email格式
+export const isEmail = function (val) {
+  // 允许汉字、英文字母、数字
+  return /^[A-Za-z0-9\u4e00-\u9fa5]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/.test(val)
+}
+
+// 是否为空
+export const isNullOrUndefined = function (val) {
+  return val === null || val === undefined
+}
+
+// 转换为boolean
+export const toBoolean = function (val) {
+  if (isString(val) && (val.toLowerCase() === 'true' || val.toLowerCase() !== '0')) {
+    val = true
+  } else if (isNumber(val) && val !== 0) {
+    val = true
+  } else {
+    val = isBoolean(val) && val
+  }
+
+  return val
+}

+ 11 - 0
src/utils/variable.js

@@ -0,0 +1,11 @@
+const variable = {
+  limit: 12
+}
+
+export const InitVariable = (app) => {
+  for (const key in variable) {
+    if (Object.hasOwnProperty.call(variable, key)) {
+      app.provide([key], variable[key])
+    }
+  }
+}

+ 135 - 0
src/utils/websocket.js

@@ -0,0 +1,135 @@
+let websock = null
+let messageCallback = null
+let resCallback = null
+let errorCallback = null
+let wsUrl = ''
+let tryTime = 0
+let interval = null
+
+// 接收ws后端返回的数据
+function websocketonmessage(e) {
+  if (e.data instanceof Blob && e.data.size === 0) {
+    //心跳
+    messageCallback(e.data)
+  } else {
+    //返回数据
+    messageCallback(JSON.parse(e.data))
+  }
+}
+
+/**
+ * 发起websocket连接
+ * @param {Object} agentData 需要向后台传递的参数数据
+ */
+function websocketSend(agentData) {
+  // 加延迟是为了尽量让ws连接状态变为OPEN
+  setTimeout(() => {
+    // 添加状态判断,当为OPEN时,发送消息
+    if (websock.readyState === websock.OPEN) {
+      // websock.OPEN = 1
+      // 发给后端的数据需要字符串化
+      if (agentData == 'ping') {
+        //发送心跳
+        const pingMsg = new Uint8Array()
+        websock.send(pingMsg)
+      } else {
+        //发送消息
+        websock.send(JSON.stringify(agentData))
+      }
+    }
+    if (websock.readyState === websock.CLOSED) {
+      // websock.CLOSED = 3
+      console.log('websock.readyState=3', 'ws连接断开')
+      clearInterval(interval)
+      errorCallback()
+    }
+  }, 3000)
+}
+
+//向后端发送消息
+export function websocketSendMess(agentData) {
+  websock.send(JSON.stringify(agentData))
+}
+
+// 关闭ws连接
+function websocketclose(e) {
+  // e.code === 1000  表示正常关闭。 无论为何目的而创建, 该链接都已成功完成任务。
+  // e.code !== 1000  表示非正常关闭。
+  //可以根据code情况判断 是否要重连
+  if (e) {
+    console.log('ws连接异常,请稍候重试')
+    clearInterval(interval)
+    errorCallback()
+    // 如果需要设置异常重连则可替换为下面的代码,自行进行测试
+    //重新连接几次后 是否继续重新 自行判断tryTime
+    setTimeout(function () {
+      websock = null
+      tryTime++
+      sendWebsocket(wsUrl, messageCallback, resCallback, errorCallback)
+      console.log(`第${tryTime}次重连`)
+    }, 3 * 1000)
+  }
+}
+// 建立ws连接
+function websocketOpen(e) {
+  tryTime = 0
+  resCallback(e)
+}
+
+// 初始化weosocket
+function initWebSocket() {
+  if (typeof WebSocket === 'undefined') {
+    console.log('您的浏览器不支持WebSocket,无法获取数据')
+    return false
+  }
+
+  // ws请求完整地址
+  websock = new WebSocket(wsUrl)
+
+  websock.onmessage = function (e) {
+    websocketonmessage(e)
+  }
+  websock.onopen = function () {
+    websocketOpen()
+  }
+  websock.onerror = function () {
+    console.log('ws连接异常,请稍候重试')
+    closeWebsocket()
+    errorCallback()
+  }
+  websock.onclose = function (e) {
+    websocketclose(e)
+  }
+}
+
+/**
+ * 发起websocket请求函数
+ * @param {string} url ws连接地址
+ * @param {function} successCallback 接收到ws数据,对数据进行处理的回调函数
+ * @param {function} errCallback ws连接错误的回调函数
+ * @param {function} resorCallback ws连接成功的回调函数
+ */
+export function sendWebsocket(url, successCallback, errCallback, resorCallback) {
+  wsUrl = url
+  initWebSocket()
+  messageCallback = successCallback
+  resCallback = resorCallback
+  errorCallback = errCallback
+  //   websocketSend(agentData);
+  //保持心跳
+  clearInterval(interval)
+  interval = setInterval(() => {
+    websocketSend('ping')
+  }, 1000 * 5)
+}
+
+/**
+ * 关闭websocket函数
+ */
+export function closeWebsocket() {
+  if (websock) {
+    clearInterval(interval)
+    websock.close() // 关闭websocket
+    websock.onclose() // 关闭websocket
+  }
+}

+ 24 - 0
src/views/home/index.vue

@@ -0,0 +1,24 @@
+<template>
+  <div class="main">
+    <el-row>
+      <el-col :span="24" class="animate__animated animate__backInRight" v-loading="loading">
+        首页
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script setup>
+import { siteInfo, footInfo, menuList } from '@/layout/site'
+import { UserStore } from '@/store/user'
+const userStore = UserStore()
+const user = computed(() => userStore.user)
+// 加载中
+const loading = ref(false)
+// 路由
+const router = useRouter()
+</script>
+<style scoped lang="scss">
+.main {
+}
+</style>

+ 24 - 0
src/views/loading/index.vue

@@ -0,0 +1,24 @@
+<template>
+  <div class="loading">
+    <a-spin size="large" />
+    <div class="text">正在努力查询中...</div>
+  </div>
+</template>
+
+<script setup></script>
+
+<style lang="scss" scoped>
+.loading {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  height: 100vh;
+  width: 100%;
+  background: linear-gradient(135deg, #a1c4fd, #c2e9fb);
+  .text {
+    margin: 10px 0 0 0;
+    font-size: $global-font-size-20;
+  }
+}
+</style>

+ 169 - 0
vite.config.js

@@ -0,0 +1,169 @@
+import AutoImport from 'unplugin-auto-import/vite'
+import Components from 'unplugin-vue-components/vite'
+import Icons from 'unplugin-icons/vite'
+import IconsResolver from 'unplugin-icons/resolver'
+import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
+import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
+import { defineConfig, loadEnv } from 'vite'
+import vue from '@vitejs/plugin-vue'
+import Inspect from 'vite-plugin-inspect'
+import path from 'path'
+// eslint-disable-next-line no-undef
+const pathSrc = path.resolve(__dirname, 'src')
+// https://vitejs.dev/config/
+export default defineConfig(({ mode }) => {
+  // eslint-disable-next-line no-undef
+  const env = loadEnv(mode, process.cwd())
+  return {
+    // 静态路径
+    base: env.VITE_BASE_URL,
+    // 打包名称
+    build: {
+      outDir: env.VITE_OUT_DIR
+    },
+    server: {
+      // 允许IP访问
+      host: '0.0.0.0',
+      // 应用端口 (默认:3000)
+      port: Number(env.VITE_APP_PORT),
+      // 运行是否自动打开浏览器
+      open: true,
+      proxy: {
+        '/files': {
+          target: 'http://192.168.1.197', // https://broadcast.waityou24.cn
+          changeOrigin: true
+        },
+        /**
+         * env.VITE_APP_BASE_API: /dev-api
+         */
+        [env.VITE_APP_BASE_API]: {
+          changeOrigin: true,
+          target: 'http://127.0.0.1:9700'
+        }
+      }
+    },
+    resolve: {
+      alias: {
+        '@': pathSrc
+      }
+    },
+    css: {
+      // CSS 预处理器
+      preprocessorOptions: {
+        // 定义全局 SCSS 变量
+        scss: {
+          additionalData: `@import "@/styles/variables.scss";`
+        }
+      }
+    },
+    plugins: [
+      vue(),
+      // // 自动导入参考: https://github.com/sxzz/element-plus-best-practices/blob/main/vite.config.ts
+      AutoImport({
+        // 自动导入 Vue 相关函数,如:ref, reactive, toRef 等
+        imports: ['vue', '@vueuse/core', 'pinia', 'vue-router', 'vue-i18n'],
+        // 自动导入 Element Plus 相关函数,如:ElMessage, ElMessageBox... (带样式)
+        resolvers: [ElementPlusResolver(), IconsResolver({})],
+        eslintrc: {
+          enabled: false,
+          filepath: './.eslintrc-auto-import.json',
+          globalsPropValue: true
+        },
+        vueTemplate: true
+        // 配置文件生成位置(false:关闭自动生成)
+        //dts: false,
+        // dts: path.resolve(pathSrc, 'auto-imports.d.ts')
+      }),
+      Components({
+        resolvers: [
+          // 自动导入 Element Plus 组件
+          ElementPlusResolver(),
+          // 自动注册图标组件
+          IconsResolver({ enabledCollections: ['ep'] })
+        ],
+        // 指定自定义组件位置(默认:src/components)
+        dirs: ['src/components', 'src/**/components']
+        // 配置文件位置 (false:关闭自动生成)
+        //dts: false,
+        // dts: path.resolve(pathSrc, 'components.d.ts')
+      }),
+      Icons({
+        autoInstall: true
+      }),
+      createSvgIconsPlugin({
+        // 指定需要缓存的图标文件夹
+        iconDirs: [path.resolve(pathSrc, 'assets/icons')],
+        // 指定symbolId格式
+        symbolId: 'icon-[dir]-[name]'
+      }),
+      Inspect()
+    ],
+    // 预加载项目必需的组件
+    optimizeDeps: {
+      include: [
+        'vue',
+        'vue-router',
+        'pinia',
+        'axios',
+        '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',
+        'vue-i18n',
+        'element-plus/es/components/text/style/css',
+        '@vueuse/core'
+        // 'sortablejs',
+        // 'path-to-regexp',
+        // 'echarts',
+        // '@wangeditor/editor',
+        // '@wangeditor/editor-for-vue',
+        // 'path-browserify'
+      ]
+    }
+  }
+})