Browse Source

合并冰洁代码

wuheng 1 year ago
parent
commit
3f42c1a433
63 changed files with 3578 additions and 573 deletions
  1. 1 1
      .env
  2. 1 1
      mock/api/auth.ts
  3. 1 1
      mock/api/route.ts
  4. 6 1
      package.json
  5. 144 0
      pnpm-lock.yaml
  6. 12 17
      src/App.vue
  7. 121 0
      src/components/View/pdfPreview.vue
  8. 1 0
      src/composables/router.ts
  9. 1 3
      src/config/regexp.ts
  10. 2 2
      src/config/service.ts
  11. 23 28
      src/constants/business.ts
  12. 1 0
      src/hooks/business/use-table.ts
  13. BIN
      src/java.pdf
  14. 3 2
      src/locales/lang/zh-cn.ts
  15. 2 1
      src/router/guard/dynamic.ts
  16. 2 1
      src/router/guard/permission.ts
  17. 11 0
      src/router/modules/management.ts
  18. 35 8
      src/service/api/auth.ts
  19. 0 0
      src/service/api/man.ts
  20. 166 0
      src/service/api/sort.ts
  21. 7 5
      src/service/api/user.ts
  22. 6 1
      src/service/request/index.ts
  23. 12 13
      src/service/request/instance.ts
  24. 6 3
      src/service/request/request.ts
  25. 9 4
      src/store/modules/auth/helpers.ts
  26. 46 43
      src/store/modules/auth/index.ts
  27. 7 8
      src/store/modules/route/index.ts
  28. 0 29
      src/typings/api.copy.ts
  29. 9 16
      src/typings/api.d.ts
  30. 11 10
      src/typings/business.d.ts
  31. 2 0
      src/typings/page-route.d.ts
  32. 6 0
      src/typings/system.d.ts
  33. 21 9
      src/utils/crypto/index.ts
  34. 7 20
      src/utils/form/rule.ts
  35. 0 1
      src/utils/router/auth.ts
  36. 1 0
      src/utils/router/cache.ts
  37. 0 1
      src/utils/router/menu.ts
  38. 10 5
      src/utils/service/handler.ts
  39. 1 2
      src/utils/storage/local.ts
  40. 356 0
      src/views/_builtin/login/components/pwd-login/components/verifition/Verify.vue
  41. 260 0
      src/views/_builtin/login/components/pwd-login/components/verifition/Verify/VerifyPoints.vue
  42. 549 0
      src/views/_builtin/login/components/pwd-login/components/verifition/Verify/VerifySlide.vue
  43. 27 0
      src/views/_builtin/login/components/pwd-login/components/verifition/api/index.js
  44. 11 0
      src/views/_builtin/login/components/pwd-login/components/verifition/utils/ase.js
  45. 30 0
      src/views/_builtin/login/components/pwd-login/components/verifition/utils/axios.js
  46. 35 0
      src/views/_builtin/login/components/pwd-login/components/verifition/utils/util.js
  47. 110 12
      src/views/_builtin/login/components/pwd-login/index.vue
  48. 0 1
      src/views/crud/demo/crud.tsx
  49. 5 0
      src/views/dashboard/analysis/index.vue
  50. 1 0
      src/views/index.ts
  51. 87 4
      src/views/management/auth/index.vue
  52. 4 13
      src/views/management/role/index.vue
  53. 16 19
      src/views/management/role/queryUser.vue
  54. 14 16
      src/views/management/role/userPa.vue
  55. 49 0
      src/views/management/sort/api.ts
  56. 88 0
      src/views/management/sort/components/column-search.vue
  57. 206 0
      src/views/management/sort/components/table-action-add.vue
  58. 181 0
      src/views/management/sort/crud.tsx
  59. 260 0
      src/views/management/sort/index.vue
  60. 315 0
      src/views/management/sort/indexcopy.vue
  61. 97 99
      src/views/management/user/components/table-action-modal.vue
  62. 183 46
      src/views/management/user/index.vue
  63. 0 127
      src/views/management/user/indexCopy.vue

+ 1 - 1
.env

@@ -7,7 +7,7 @@ VITE_APP_TITLE=爱扣钉教务系统
 VITE_APP_DESC=四福科技-爱扣钉-教务
 
 # 权限路由模式: static | dynamic
-VITE_AUTH_ROUTE_MODE=static
+VITE_AUTH_ROUTE_MODE = static
 
 # 路由首页(根路由重定向), 用于static模式的权限路由,dynamic模式取决于后端返回的路由首页
 VITE_ROUTE_HOME_PATH=/dashboard/analysis

+ 1 - 1
mock/api/auth.ts

@@ -34,7 +34,7 @@ const apis: MockMethod[] = [
         };
       }
 
-      const findItem = userModel.find(item => item.userName === userName && item.password === password);
+      const findItem = userModel.find(item => item.username === userName && item.password === password);
 
       if (findItem) {
         return {

+ 1 - 1
mock/api/route.ts

@@ -10,7 +10,7 @@ const apis: MockMethod[] = [
 
       const routeHomeName: AuthRoute.LastDegreeRouteKey = 'dashboard_analysis';
 
-      const role = userModel.find(item => item.userId === userId)?.userRole || 'user';
+      const role = userModel.find(item => item.id === userId)?.userRole || 'user';
 
       const filterRoutes = routeModel[role];
 

+ 6 - 1
package.json

@@ -82,10 +82,15 @@
     "vditor": "^3.9.2",
     "vue": "3.3.4",
     "vue-i18n": "^9.2.2",
+    "vue-monoplasty-slide-verify": "^1.3.1",
+    "vue-pdf-embed": "^1.1.6",
     "vue-router": "^4.2.1",
+    "vue-slider-vertify": "^0.0.1",
+    "vue3-pdfjs": "^0.1.6",
     "vuedraggable": "^4.1.0",
     "wangeditor": "^4.7.15",
-    "xgplayer": "^3.0.2"
+    "xgplayer": "^3.0.2",
+    "xlsx": "^0.18.5"
   },
   "devDependencies": {
     "@amap/amap-jsapi-types": "^0.0.13",

+ 144 - 0
pnpm-lock.yaml

@@ -91,9 +91,21 @@ dependencies:
   vue-i18n:
     specifier: ^9.2.2
     version: 9.2.2(vue@3.3.4)
+  vue-monoplasty-slide-verify:
+    specifier: ^1.3.1
+    version: 1.3.1
+  vue-pdf-embed:
+    specifier: ^1.1.6
+    version: 1.1.6(vue@3.3.4)
   vue-router:
     specifier: ^4.2.1
     version: 4.2.1(vue@3.3.4)
+  vue-slider-vertify:
+    specifier: ^0.0.1
+    version: 0.0.1
+  vue3-pdfjs:
+    specifier: ^0.1.6
+    version: 0.1.6
   vuedraggable:
     specifier: ^4.1.0
     version: 4.1.0(vue@3.3.4)
@@ -103,6 +115,9 @@ dependencies:
   xgplayer:
     specifier: ^3.0.2
     version: 3.0.2(core-js@3.30.1)
+  xlsx:
+    specifier: ^0.18.5
+    version: 0.18.5
 
 devDependencies:
   '@amap/amap-jsapi-types':
@@ -4281,6 +4296,14 @@ packages:
       '@vue/compiler-core': 3.3.4
       '@vue/shared': 3.3.4
 
+  /@vue/compiler-sfc@2.7.14:
+    resolution: {integrity: sha512-aNmNHyLPsw+sVvlQFQ2/8sjNuLtK54TC6cuKnVzAY93ks4ZBrvwQSnkkIh7bsbNhum5hJBS00wSDipQ937f5DA==}
+    dependencies:
+      '@babel/parser': 7.21.8
+      postcss: 8.4.23
+      source-map: 0.6.1
+    dev: false
+
   /@vue/compiler-sfc@3.3.4:
     resolution: {integrity: sha512-6y/d8uw+5TkCuzBkgLS0v3lSM3hJDntFEiUORM11pQ/hKvkhSKZrXW6i69UyXlJQisJxuUEJKAWEqWbWsLeNKQ==}
     dependencies:
@@ -4613,6 +4636,11 @@ packages:
     engines: {node: '>= 10.0.0'}
     dev: false
 
+  /adler-32@1.3.1:
+    resolution: {integrity: sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==}
+    engines: {node: '>=0.8'}
+    dev: false
+
   /agent-base@6.0.2:
     resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==}
     engines: {node: '>= 6.0.0'}
@@ -5246,6 +5274,14 @@ packages:
     resolution: {integrity: sha512-8aUpZ7sjhlOyiNsg+pgcrTTPUXKh+rg544QYHSvQErljVEKJzvkYkCR/hUFeeVoEfTToUtY9cUKNRC7+c45YkA==}
     dev: true
 
+  /cfb@1.2.2:
+    resolution: {integrity: sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==}
+    engines: {node: '>=0.8'}
+    dependencies:
+      adler-32: 1.3.1
+      crc-32: 1.2.2
+    dev: false
+
   /chalk@1.1.3:
     resolution: {integrity: sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==}
     engines: {node: '>=0.10.0'}
@@ -5385,6 +5421,11 @@ packages:
     resolution: {integrity: sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==}
     engines: {node: '>=0.8'}
 
+  /codepage@1.15.0:
+    resolution: {integrity: sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==}
+    engines: {node: '>=0.8'}
+    dev: false
+
   /collection-visit@1.0.0:
     resolution: {integrity: sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==}
     engines: {node: '>=0.10.0'}
@@ -5741,6 +5782,12 @@ packages:
       '@xmldom/xmldom': 0.8.7
     dev: false
 
+  /crc-32@1.2.2:
+    resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==}
+    engines: {node: '>=0.8'}
+    hasBin: true
+    dev: false
+
   /cropperjs@1.5.13:
     resolution: {integrity: sha512-by7jKAo73y5/Do0K6sxdTKHgndY0NMjG2bEdgeJxycbcmHuCiMXqw8sxy5C5Y5WTOTcDGmbT7Sr5CgKOXR06OA==}
     dev: false
@@ -6280,6 +6327,11 @@ packages:
       domelementtype: 2.3.0
     dev: true
 
+  /dommatrix@1.0.3:
+    resolution: {integrity: sha512-l32Xp/TLgWb8ReqbVJAFIvXmY7go4nTxxlWiAFyhoQw9RKEOHBZNnyGvJWqDVSPmq3Y9HlM4npqF/T6VMOXhww==}
+    deprecated: dommatrix is no longer maintained. Please use @thednp/dommatrix.
+    dev: false
+
   /domutils@1.7.0:
     resolution: {integrity: sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==}
     dependencies:
@@ -7570,6 +7622,11 @@ packages:
     engines: {node: '>=10'}
     dev: true
 
+  /frac@1.1.2:
+    resolution: {integrity: sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==}
+    engines: {node: '>=0.8'}
+    dev: false
+
   /fragment-cache@0.2.1:
     resolution: {integrity: sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==}
     engines: {node: '>=0.10.0'}
@@ -10335,6 +10392,18 @@ packages:
       through: 2.3.8
     dev: false
 
+  /pdfjs-dist@2.16.105:
+    resolution: {integrity: sha512-J4dn41spsAwUxCpEoVf6GVoz908IAA3mYiLmNxg8J9kfRXc2jxpbUepcP0ocp0alVNLFthTAM8DZ1RaHh8sU0A==}
+    peerDependencies:
+      worker-loader: ^3.0.8
+    peerDependenciesMeta:
+      worker-loader:
+        optional: true
+    dependencies:
+      dommatrix: 1.0.3
+      web-streams-polyfill: 3.2.1
+    dev: false
+
   /perfect-debounce@1.0.0:
     resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==}
     dev: true
@@ -11550,6 +11619,13 @@ packages:
       through: 2.3.8
     dev: true
 
+  /ssf@0.11.2:
+    resolution: {integrity: sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==}
+    engines: {node: '>=0.8'}
+    dependencies:
+      frac: 1.1.2
+    dev: false
+
   /ssr-window@3.0.0:
     resolution: {integrity: sha512-q+8UfWDg9Itrg0yWK7oe5p/XRCJpJF9OBtXfOPgSJl+u3Xd5KI328RUEvUqSMVM9CiQUEf1QdBzJMkYGErj9QA==}
     dev: false
@@ -12821,6 +12897,21 @@ packages:
       vue: 3.3.4
     dev: false
 
+  /vue-monoplasty-slide-verify@1.3.1:
+    resolution: {integrity: sha512-oMP9RdBo/2M2D8CcEE1IJCXKWOGPUyFNKFgMwj8+BMEA5Je4wF3jUbCnQe/hNNmV1cUBdeTNp0w/TdlP1A96SQ==}
+    engines: {node: '>= 6.0.0', npm: '>= 3.0.0'}
+    dependencies:
+      vue: 2.7.14
+    dev: false
+
+  /vue-pdf-embed@1.1.6(vue@3.3.4):
+    resolution: {integrity: sha512-CRQIw8OxiD6H1n8KT2zVWbp/00fA3PgSV/JYJ0Ut+FdC1jHrRDHNBj3BvaRVwZFZg3EJ8LLjyEDYxWWUMOjrDw==}
+    peerDependencies:
+      vue: ^2.x || ^3.x
+    dependencies:
+      vue: 3.3.4
+    dev: false
+
   /vue-router@4.2.1(vue@3.3.4):
     resolution: {integrity: sha512-nW28EeifEp8Abc5AfmAShy5ZKGsGzjcnZ3L1yc2DYUo+MqbBClrRP9yda3dIekM4I50/KnEwo1wkBLf7kHH5Cw==}
     peerDependencies:
@@ -12830,6 +12921,13 @@ packages:
       vue: 3.3.4
     dev: false
 
+  /vue-slider-vertify@0.0.1:
+    resolution: {integrity: sha512-0nWYalhUKOX/FERDO/UIMHvJLnn+qffnecyrB0/NblCzc+AvECW1MA78kOvggx2AIOsqGGqTTlx/Nwwm86DAoA==}
+    dependencies:
+      prismjs: 1.29.0
+      vue: 3.3.4
+    dev: false
+
   /vue-template-compiler@2.7.14:
     resolution: {integrity: sha512-zyA5Y3ArvVG0NacJDkkzJuPQDF8RFeRlzV2vLeSnhSpieO6LK2OVbdLPi5MPPs09Ii+gMO8nY4S3iKQxBxDmWQ==}
     dependencies:
@@ -12849,6 +12947,23 @@ packages:
       typescript: 5.0.4
     dev: true
 
+  /vue3-pdfjs@0.1.6:
+    resolution: {integrity: sha512-7UaWbsp8wNqB0y/rRlyo5yRb0S+XOkkSpmdUuS267Dhi07Pt4RFEetQ8inrpf/aTFJwGnW0Uc/UE4p376s+Zmw==}
+    engines: {node: '>=10.0.0'}
+    dependencies:
+      pdfjs-dist: 2.16.105
+      vue: 3.3.4
+    transitivePeerDependencies:
+      - worker-loader
+    dev: false
+
+  /vue@2.7.14:
+    resolution: {integrity: sha512-b2qkFyOM0kwqWFuQmgd4o+uHGU7T+2z3T+WQp8UBjADfEv2n4FEMffzBmCKNP0IGzOEEfYjvtcC62xaSKeQDrQ==}
+    dependencies:
+      '@vue/compiler-sfc': 2.7.14
+      csstype: 3.1.2
+    dev: false
+
   /vue@3.3.4:
     resolution: {integrity: sha512-VTyEYn3yvIeY1Py0WaYGZsXnz3y5UnGi62GjVEqvEGPl6nxbOrCXbVOTQWBEJUqAyTUk2uJ5JLVnYJ6ZzGbrSw==}
     dependencies:
@@ -12899,6 +13014,11 @@ packages:
       tslib: 2.5.0
     dev: false
 
+  /web-streams-polyfill@3.2.1:
+    resolution: {integrity: sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==}
+    engines: {node: '>= 8'}
+    dev: false
+
   /webidl-conversions@4.0.2:
     resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==}
     dev: true
@@ -12982,6 +13102,11 @@ packages:
       semver: 5.7.1
     dev: false
 
+  /wmf@1.0.2:
+    resolution: {integrity: sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==}
+    engines: {node: '>=0.8'}
+    dev: false
+
   /wolfy87-eventemitter@5.2.9:
     resolution: {integrity: sha512-P+6vtWyuDw+MB01X7UeF8TaHBvbCovf4HPEMF/SV7BdDc1SMTiBy13SRD71lQh4ExFTG1d/WNzDGDCyOKSMblw==}
     dev: false
@@ -12990,6 +13115,11 @@ packages:
     resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==}
     engines: {node: '>=0.10.0'}
 
+  /word@0.3.0:
+    resolution: {integrity: sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==}
+    engines: {node: '>=0.8'}
+    dev: false
+
   /wordwrap@1.0.0:
     resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==}
     dev: true
@@ -13206,6 +13336,20 @@ packages:
       xgplayer-subtitles: 1.1.1(core-js@3.30.1)
     dev: false
 
+  /xlsx@0.18.5:
+    resolution: {integrity: sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==}
+    engines: {node: '>=0.8'}
+    hasBin: true
+    dependencies:
+      adler-32: 1.3.1
+      cfb: 1.2.2
+      codepage: 1.15.0
+      crc-32: 1.2.2
+      ssf: 0.11.2
+      wmf: 1.0.2
+      word: 0.3.0
+    dev: false
+
   /xml-name-validator@4.0.0:
     resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==}
     engines: {node: '>=12'}

+ 12 - 17
src/App.vue

@@ -1,17 +1,12 @@
 <template>
-  <n-config-provider
-    :theme="theme.naiveTheme"
-    :theme-overrides="theme.naiveThemeOverrides"
-    :locale="zhCN"
-    :date-locale="dateZhCN"
-    class="h-full"
-  >
-    <naive-provider>
-      <fs-ui-context>
-        <router-view />
-      </fs-ui-context>
-    </naive-provider>
-  </n-config-provider>
+	<n-config-provider :theme="theme.naiveTheme" :theme-overrides="theme.naiveThemeOverrides" :locale="zhCN"
+		:date-locale="dateZhCN" class="h-full">
+		<naive-provider>
+			<fs-ui-context>
+				<router-view />
+			</fs-ui-context>
+		</naive-provider>
+	</n-config-provider>
 </template>
 
 <script setup lang="ts">
@@ -30,10 +25,10 @@ subscribeStore();
 useGlobalEvents();
 
 watch(
-  () => locale.value,
-  () => {
-    document.title = route.meta.i18nTitle ? t(route.meta.i18nTitle) : route.meta.title;
-  }
+	() => locale.value,
+	() => {
+		document.title = route.meta.i18nTitle ? t(route.meta.i18nTitle) : route.meta.title;
+	}
 );
 </script>
 

+ 121 - 0
src/components/View/pdfPreview.vue

@@ -0,0 +1,121 @@
+<template>
+	<div class="pdf-preview">
+		<div class="pdf-wrap">
+			<vue-pdf-embed :source="state.source" :style="scale" class="vue-pdf-embed" :page="state.pageNum" />
+		</div>
+		<div class="page-tool">
+			<div class="page-tool-item" @click="lastPage">上一页</div>
+			<div class="page-tool-item" @click="nextPage">下一页</div>
+			<div class="page-tool-item">{{ state.pageNum }}/{{ state.numPages }}</div>
+			<div class="page-tool-item" @click="pageZoomOut">放大</div>
+			<div class="page-tool-item" @click="pageZoomIn">缩小</div>
+		</div>
+	</div>
+</template>
+<script setup lang="ts">
+import VuePdfEmbed from "vue-pdf-embed";
+import { createLoadingTask } from "vue3-pdfjs";
+import { reactive, onMounted, computed } from "vue";
+
+const props = defineProps({
+	pdfUrl: {
+		type: String,
+		required: true
+	}
+})
+const state = reactive({
+	source: props.pdfUrl,
+	pageNum: 1,
+	scale: 1, // 缩放比例
+	numPages: 0, // 总页数
+});
+
+const scale = computed(() => `transform:scale(${state.scale})`)
+function lastPage() {
+	if (state.pageNum > 1) {
+		state.pageNum -= 1;
+	}
+}
+function nextPage() {
+	if (state.pageNum < state.numPages) {
+		state.pageNum += 1;
+	}
+}
+function pageZoomOut() {
+	if (state.scale < 2) {
+		state.scale += 0.1;
+	}
+}
+function pageZoomIn() {
+	if (state.scale > 1) {
+		state.scale -= 0.1;
+	}
+}
+
+onMounted(() => {
+	const loadingTask = createLoadingTask(state.source);
+	loadingTask.promise.then((pdf: { numPages: number }) => {
+		state.numPages = pdf.numPages;
+	});
+});
+
+</script>
+<style lang="css" scoped>
+.pdf-preview {
+	position: relative;
+	height: 100vh;
+	padding: 20px 0;
+	box-sizing: border-box;
+	background: rgb(66, 66, 66);
+}
+
+.vue-pdf-embed {
+	text-align: center;
+	width: 515px;
+	border: 1px solid #e5e5e5;
+	margin: 0 auto;
+	box-sizing: border-box;
+}
+
+.pdf-preview {
+	position: relative;
+	height: 100vh;
+	padding: 20px 0;
+	box-sizing: border-box;
+	background-color: e9e9e9;
+}
+
+.pdf-wrap {
+	overflow-y: auto;
+}
+
+.vue-pdf-embed {
+	text-align: center;
+	width: 515px;
+	border: 1px solid #e5e5e5;
+	margin: 0 auto;
+	box-sizing: border-box;
+}
+
+.page-tool {
+	position: absolute;
+	bottom: 35px;
+	padding-left: 15px;
+	padding-right: 15px;
+	display: flex;
+	align-items: center;
+	background: rgb(66, 66, 66);
+	color: white;
+	border-radius: 19px;
+	z-index: 100;
+	cursor: pointer;
+	margin-left: 50%;
+	transform: translateX(-50%);
+}
+
+.page-tool-item {
+	padding: 8px 15px;
+	padding-left: 10px;
+	cursor: pointer;
+}
+</style>

+ 1 - 0
src/composables/router.ts

@@ -17,6 +17,7 @@ export function useRouterPush(inSetup = true) {
    */
   function routerPush(to: RouteLocationRaw, newTab = false) {
     if (newTab) {
+			console.log(newTab);
       const routerData = router.resolve(to);
       window.open(routerData.href, '_blank');
       return Promise.resolve();

+ 1 - 3
src/config/regexp.ts

@@ -6,11 +6,9 @@ export const REGEXP_PHONE =
 export const REGEXP_EMAIL = /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/;
 
 /** 密码正则(密码为6-18位数字/字符/符号的组合) */
-// export const REGEXP_PWD =
+export const REGEXP_PWD = /^([0-9]{6,18}|[a-zA-Z]{6,18}|[^0-9a-zA-Z]{6,18})$/;
 // /^(?![0-9]+$)(?![a-z]+$)(?![A-Z]+$)(?!([^(0-9a-zA-Z)]|[()])+$)(?!^.*[\u4E00-\u9FA5].*$)([^(0-9a-zA-Z)]|[()]|[a-z]|[A-Z]|[0-9]){6,18}$/;
 
-export const REGEXP_PWD = /^[(0-9a-zA-Z)]{6,18}$/;
-
 /** 6位数字验证码正则 */
 export const REGEXP_CODE_SIX = /^\d{6}$/;
 

+ 2 - 2
src/config/service.ts

@@ -42,5 +42,5 @@ export const ERROR_STATUS = {
 /** 不弹出错误信息的code */
 export const NO_ERROR_MSG_CODE: (string | number)[] = [];
 
-/** token失效需要刷新token的code(这里的66666只是个例子,需要将后端表示token过期的code填进来) */
-export const REFRESH_TOKEN_CODE: (string | number)[] = [66666];
+/** token失效需要刷新token的code(需要将后端表示token过期的code填进来) */
+export const REFRESH_TOKEN_CODE: (string | number)[] = ['token'];

+ 23 - 28
src/constants/business.ts

@@ -1,42 +1,37 @@
 export const loginModuleLabels: Record<UnionKey.LoginModule, string> = {
-  'pwd-login': '账密登录',
-  register: '注册'
+  'pwd-login': '账密登录'
 };
 
-export const userRoleLabels: Record<Auth.RoleType, string> = {
-  super: '超级管理员',
-  admin: '管理员',
-  user: '普通用户'
-};
+// export const userRoleLabels: Record<Auth.RoleType, string> = {
+//   super: '超级管理员',
+//   admin: '管理员',
+//   user: '普通用户'
+// };
 
-export const userRoleOptions: Common.OptionWithKey<Auth.RoleType>[] = [
-  { value: 'super', label: userRoleLabels.super },
-  { value: 'admin', label: userRoleLabels.admin },
-  { value: 'user', label: userRoleLabels.user }
-];
+// export const userRoleOptions: Common.OptionWithKey<Auth.RoleType>[] = [
+//   { value: 'super', label: userRoleLabels.super },
+//   { value: 'admin', label: userRoleLabels.admin },
+//   { value: 'user', label: userRoleLabels.user }
+// ];
 
 /** 用户性别 */
-export const genderLabels: Record<UserManagement.GenderKey, string> = {
-  0: '女',
-  1: '男'
-};
+// export const genderLabels: Record<UserManagement.GenderKey, string> = {
+//   0: '女',
+//   1: '男'
+// };
 
-export const genderOptions: Common.OptionWithKey<UserManagement.GenderKey>[] = [
-  { value: '0', label: genderLabels['0'] },
-  { value: '1', label: genderLabels['1'] }
-];
+// export const genderOptions: Common.OptionWithKey<UserManagement.GenderKey>[] = [
+//   { value: '0', label: genderLabels['0'] },
+//   { value: '1', label: genderLabels['1'] }
+// ];
 
 /** 用户状态 */
 export const userStatusLabels: Record<UserManagement.UserStatusKey, string> = {
-  1: '启用',
-  2: '禁用',
-  3: '冻结',
-  4: '软删除'
+  Y: 'Y',
+  N: 'N'
 };
 
 export const userStatusOptions: Common.OptionWithKey<UserManagement.UserStatusKey>[] = [
-  { value: '1', label: userStatusLabels['1'] },
-  { value: '2', label: userStatusLabels['2'] },
-  { value: '3', label: userStatusLabels['3'] },
-  { value: '4', label: userStatusLabels['4'] }
+  { value: 'Y', label: userStatusLabels.Y },
+  { value: 'N', label: userStatusLabels.N }
 ];

+ 1 - 0
src/hooks/business/use-table.ts

@@ -32,6 +32,7 @@ type ApiFn<Params = ApiParams, TableData = Record<string, unknown>> = (
  */
 type TransformedTableData<TableData = Record<string, unknown>> = {
   data: TableData[];
+	status:boolean;
   pageNum: number;
   pageSize: number;
   total: number;

BIN
src/java.pdf


+ 3 - 2
src/locales/lang/zh-cn.ts

@@ -1,4 +1,4 @@
-import type { LocaleMessages } from 'vue-i18n';
+ import type { LocaleMessages } from 'vue-i18n';
 
 const locale: LocaleMessages<I18nType.Schema> = {
   message: {
@@ -75,7 +75,8 @@ const locale: LocaleMessages<I18nType.Schema> = {
         auth: '权限管理',
         role: '角色管理',
         route: '路由管理',
-        user: '用户管理'
+        user: '用户管理',
+        sort: '课程分类'
       },
       about: '关于'
     }

+ 2 - 1
src/router/guard/dynamic.ts

@@ -12,7 +12,8 @@ export async function createDynamicRouteGuard(
   next: NavigationGuardNext
 ) {
   const route = useRouteStore();
-  const isLogin = Boolean(localStg.get('token'));
+  //const isLogin = Boolean(localStg.get('token'));
+	const isLogin = Boolean(localStorage.getItem('token'));
 
   // 初始化权限路由
   if (!route.isInitAuthRoute) {

+ 2 - 1
src/router/guard/permission.ts

@@ -22,7 +22,8 @@ export async function createPermissionGuard(
   }
 
   const auth = useAuthStore();
-  const isLogin = Boolean(localStg.get('token'));
+  //const isLogin = Boolean(localStg.get('token'));
+	const isLogin = Boolean(localStorage.getItem('token'));
   const permissions = to.meta.permissions || [];
   const needLogin = Boolean(to.meta?.requiresAuth) || Boolean(permissions.length);
   const hasPermission = !permissions.length || permissions.includes(auth.userInfo.userRole);

+ 11 - 0
src/router/modules/management.ts

@@ -36,6 +36,17 @@ const management: AuthRoute.Route = {
         icon: 'ic:round-manage-accounts'
       }
     },
+    {
+      name: 'management_sort',
+      path: '/management/sort',
+      component: 'self',
+      meta: {
+        title: '课程分类',
+        i18nTitle: 'message.routes.management.sort',
+        requiresAuth: true,
+        icon: 'material-symbols:sort'
+      }
+    },
     {
       name: 'management_route',
       path: '/management/route',

+ 35 - 8
src/service/api/auth.ts

@@ -1,4 +1,5 @@
-import { mockRequest } from '../request';
+import { localStg } from '~/src/utils';
+import { request,mockRequest } from '../request';
 
 /**
  * 获取验证码
@@ -6,21 +7,47 @@ import { mockRequest } from '../request';
  * @returns - 返回boolean值表示是否发送成功
  */
 export function fetchSmsCode(phone: string) {
-  return mockRequest.post<boolean>('/getSmsCode', { phone });
+  return request.post<boolean>('/getSmsCode', { phone });
 }
 
+// 参数接口
+export interface AdminLoginParams {
+  username?: string;
+  passwd?: string;
+	captchaVerification?: string
+}
+
+// 响应接口
+export interface AdminLoginRes {
+  status: boolean;
+  msg: string;
+  data: Record<string, unknown>;
+}
+/**
+ * 管理员登录
+ * @param {object} params AdminPojo
+ * @param {string} params.username
+ * @param {string} params.passwd
+ * @returns
+ */
+// export function adminLogin(params: AdminLoginParams) {
+//   return request.post(`/adminLogin`, params);
+// }
 /**
  * 登录
  * @param userName - 用户名
  * @param password - 密码
+ * @param captchaVerification - 验证码
  */
-export function fetchLogin(userName: string, password: string) {
-  return mockRequest.post<ApiAuth.Token>('/login', { userName, password });
+export function fetchLogin(params: AdminLoginParams) {
+  let res = request.post('/adminLogin',params);
+  return  res;	
 }
 
 /** 获取用户信息 */
-export function fetchUserInfo() {
-  return mockRequest.get<ApiAuth.UserInfo>('/getUserInfo');
+export function fetchUserInfo(param:string) {
+	let res = request.get('/getUserInfo')
+  return res;
 }
 
 /**
@@ -28,7 +55,7 @@ export function fetchUserInfo() {
  * @param userId - 用户id
  * @description 后端根据用户id查询到对应的角色类型,并将路由筛选出对应角色的路由数据返回前端
  */
-export function fetchUserRoutes(userId: string) {
+export function fetchUserRoutes(userId: number) {
   return mockRequest.post<ApiRoute.Route>('/getUserRoutes', { userId });
 }
 
@@ -37,5 +64,5 @@ export function fetchUserRoutes(userId: string) {
  * @param refreshToken
  */
 export function fetchUpdateToken(refreshToken: string) {
-  return mockRequest.post<ApiAuth.Token>('/updateToken', { refreshToken });
+  return request.post<ApiAuth.Token>('/refreshToken',  refreshToken );
 }

+ 0 - 0
src/service/api/man.ts


+ 166 - 0
src/service/api/sort.ts

@@ -0,0 +1,166 @@
+import { request } from '../request';
+
+
+// 查询全部课程类别
+// 响应接口
+export interface SelectAll_1Res {
+  status: boolean;
+  msg: string;
+  data: Record<string, unknown>;
+}
+
+/**
+ * 查询全部课程类别
+ * @returns
+ */
+export function selectAll_1() {
+  return request.get(`/selectAll`);
+}
+
+
+// 添加课程类别
+// 参数接口
+export interface AddEasEduCategoryParams {
+  id?: number|null;
+  name?: string|null;
+  description?: string|null;
+  createTime?: string;
+  modifyTime?: string;
+  createUid?: number|null;
+  disabled?: string|null;
+}
+
+// 响应接口
+export interface AddEasEduCategoryRes {
+  status: boolean;
+  msg: string;
+  data: Record<string, unknown>;
+}
+
+/**
+ * 添加课程类别
+ * @param {object} params EasEduCategory
+ * @param {number} params.id
+ * @param {string} params.name 学科名称
+ * @param {string} params.description 学科描述
+ * @param {object} params.createTime 创建时间
+ * @param {object} params.modifyTime 修改时间
+ * @param {number} params.createUid 创建用户ID
+ * @param {string} params.disabled 状态
+ * @returns
+ */
+export function addEasEduCategory(params: AddEasEduCategoryParams){
+  return request.post(`/addEasEduCategory`, params);
+}
+
+
+// 根据Id删除课程类别
+// 响应接口
+export interface DeleteByIdRes {
+  status: boolean;
+  msg: string;
+  data: string;
+}
+
+/**
+ * 根据Id删除课程类别
+ * @param {string} id
+  * @returns
+ */
+export function deleteById(id: number){
+  return request.delete(`/deleteById/${id}`);
+}
+
+
+
+// 课程类别更新
+// 参数接口
+export interface UpdateEasEduCategoryParams {
+  id?: number|null;
+  name?: string|null;
+  description?: string|null;
+  createTime?:  string|null;
+  modifyTime?: string;
+  createUid?: number|null;
+  disabled?: string|null;
+}
+
+// 响应接口
+export interface UpdateEasEduCategoryRes {
+  status: boolean;
+  msg: string;
+  data: Record<string, unknown>;
+}
+
+/**
+ * 课程类别更新
+ * @param {object} params EasEduCategory
+ * @param {number} params.id
+ * @param {string} params.name 学科名称
+ * @param {string} params.description 学科描述
+ * @param {object} params.createTime 创建时间
+ * @param {object} params.modifyTime 修改时间
+ * @param {number} params.createUid 创建用户ID
+ * @param {string} params.disabled 状态
+ * @returns
+ */
+export function updateEasEduCategory(params: UpdateEasEduCategoryParams) {
+  return request.post(`/updateEasEduCategory`, params);
+}
+
+
+// 根据Id查询对应课程类别
+// 响应接口
+export interface SelectById_1Res {
+  status: boolean;
+  msg: string;
+  data: Record<string, unknown>;
+}
+
+/**
+ * 根据Id查询对应课程类别
+ * @param {string} id
+  * @returns
+ */
+export function selectById_1(id: number) {
+  return request.get(`/selectById?id=${id}`);
+}
+
+
+// 根据条件进行查询课程类别
+// 参数接口
+export interface SelectByCondition_1Params {
+  id?: number | null;
+  name?: string|null;
+  description?: string|null;
+  createTime?: null|string;
+  modifyTime?: null|string;
+  createUid?: null|number;
+  disabled?: string|null;
+}
+
+// 响应接口
+export interface SelectByCondition_1Res {
+  status: boolean;
+  msg: string;
+  data: Record<string, unknown>;
+  total: number;
+}
+
+/**
+ * 根据条件进行查询课程类别
+ * @param {string} pageNum
+ * @param {string} pageSize
+ * @param {object} params EasEduCategory
+ * @param {number} params.id
+ * @param {string} params.name 学科名称
+ * @param {string} params.description 学科描述
+ * @param {object} params.createTime 创建时间
+ * @param {object} params.modifyTime 修改时间
+ * @param {number} params.createUid 创建用户ID
+ * @param {string} params.disabled 状态
+ * @returns
+ */
+export function selectByCondition_1(pageNum: number, pageSize: number, params: SelectByCondition_1Params){
+  return request.post(`/selectByCondition?pageNum=${pageNum}&pageSize=${pageSize}`, params);
+}

+ 7 - 5
src/service/api/user.ts

@@ -41,7 +41,7 @@ export interface QueryParams {
   description?: string;
   isActive?: string;
   createTime?: string;
-  modifyTime?:string;
+  modifyTime?: string;
   createUid?: number;
   disabled?: string;
 }
@@ -67,7 +67,7 @@ export interface QueryRes {
  * @param {string} params.description 权限描述
  * @returns
  */
-export function query(pageNum: number, pageSize: number, params: QueryParams){
+export function query(pageNum: number, pageSize: number, params: QueryParams) {
   return request.post(`/permission/query?pageNum=${pageNum}&pageSize=${pageSize}`, params);
 }
 // 参数接口
@@ -95,8 +95,8 @@ export interface Query_1Res {
 /**
  * 查询部门
  * @param {string} pageNum
-  * @param {string} pageSize
-  * @param {object} params EasSysDepartment
+ * @param {string} pageSize
+ * @param {object} params EasSysDepartment
  * @param {number} params.id
  * @param {string} params.depname 部门名称
  * @param {string} params.address 部门地址
@@ -109,6 +109,8 @@ export interface Query_1Res {
  * @param {string} params.disabled 状态
  * @returns
  */
-export function query_1(pageNum: number, pageSize: number, params: Query_1Params){
+export function query_1(pageNum: number, pageSize: number, params: Query_1Params) {
   return request.post(`/department/query?pageNum=${pageNum}&pageSize=${pageSize}`, params);
 }
+
+

+ 6 - 1
src/service/request/index.ts

@@ -5,6 +5,11 @@ const { url, proxyPattern } = getServiceEnvConfig(import.meta.env);
 
 const isHttpProxy = import.meta.env.VITE_HTTP_PROXY === 'Y';
 
-export const request = createRequest({ baseURL: isHttpProxy ? proxyPattern : url });
+export const request = createRequest({ baseURL: isHttpProxy ? proxyPattern : url,
+	headers: {
+		'X-Requested-With': 'XMLHttpRequest',
+    'Content-Type': 'application/json; charset=UTF-8'
+	}
+});
 
 export const mockRequest = createRequest({ baseURL: '/mock' });

+ 12 - 13
src/service/request/instance.ts

@@ -31,7 +31,8 @@ export default class CustomAxiosInstance {
       codeKey: 'status',
       dataKey: 'data',
       msgKey: 'code',
-      successCode: true
+      successCode: true,
+			total:'total'
     }
   ) {
     this.backendConfig = backendConfig;
@@ -44,33 +45,31 @@ export default class CustomAxiosInstance {
     this.instance.interceptors.request.use(
       async config => {
         const handleConfig = { ...config };
+				console.log(handleConfig.headers);
         if (handleConfig.headers) {
           // 数据转换
           const contentType = handleConfig.headers['Content-Type'] as UnionKey.ContentType;
           handleConfig.data = await transformRequestData(handleConfig.data, contentType);
           // 设置token
-          handleConfig.headers.Authorization = localStg.get('token') || '';
+          handleConfig.headers["Authorization"]= localStorage.getItem('token') ;
         }
         return handleConfig;
       },
       (axiosError: AxiosError) => {
         const error = handleAxiosError(axiosError);
-        return handleServiceResult(error, null);
+        return handleServiceResult(error, null,null);
       }
     );
     this.instance.interceptors.response.use(
       (async response => {
         const { status } = response;
-        if (status === 200 || status < 300 || status === 304 || response.status === 200) {
+        if (status === 200 || status < 300 || status === 304 ) {
           const backend = response.data;
-          const { codeKey, dataKey, successCode } = this.backendConfig;
+          const { codeKey, dataKey, successCode,total} = this.backendConfig;
           // 请求成功
-          console.log(response.status);
-          if (backend[codeKey] === successCode || response.status === 200) {
-            console.log('fanhui');
-            return handleServiceResult(null, backend[dataKey]);
+          if (backend[codeKey] === successCode ) {
+            return handleServiceResult(null, backend[dataKey],backend[total]);
           }
-
           // token失效, 刷新token
           if (REFRESH_TOKEN_CODE.includes(backend[codeKey])) {
             const config = await handleRefreshToken(response.config);
@@ -80,14 +79,14 @@ export default class CustomAxiosInstance {
           }
 
           const error = handleBackendError(backend, this.backendConfig);
-          return handleServiceResult(error, null);
+          return handleServiceResult(error, null,null);
         }
         const error = handleResponseError(response);
-        return handleServiceResult(error, null);
+        return handleServiceResult(error, null,null);
       }) as (response: AxiosResponse<any, any>) => Promise<AxiosResponse<any, any>>,
       (axiosError: AxiosError) => {
         const error = handleAxiosError(axiosError);
-        return handleServiceResult(error, null);
+        return handleServiceResult(error, null,null);
       }
     );
   }

+ 6 - 3
src/service/request/request.ts

@@ -20,7 +20,6 @@ interface RequestParam {
  */
 export function createRequest(axiosConfig: AxiosRequestConfig, backendConfig?: Service.BackendResultConfig) {
   const customInstance = new CustomAxiosInstance(axiosConfig, backendConfig);
-
   /**
    * 异步promise请求
    * @param param - 请求参数
@@ -28,6 +27,7 @@ export function createRequest(axiosConfig: AxiosRequestConfig, backendConfig?: S
    * - method: 请求方法(默认get)
    * - data: 请求的body的data
    * - axiosConfig: axios配置
+	 * - total
    */
   async function asyncRequest<T>(param: RequestParam): Promise<Service.RequestResult<T>> {
     const { url } = param;
@@ -94,6 +94,7 @@ interface RequestResultHook<T = any> {
   error: Ref<Service.RequestError | null>;
   loading: Ref<boolean>;
   network: Ref<boolean>;
+	total:Ref<number>;
 }
 
 /**
@@ -119,11 +120,12 @@ export function createHookRequest(axiosConfig: AxiosRequestConfig, backendConfig
     startLoading();
     const data = ref<T | null>(null) as Ref<T | null>;
     const error = ref<Service.RequestError | null>(null);
-
+		const total = ref<number>() as Ref<number>;
     function handleRequestResult(response: any) {
       const res = response as Service.RequestResult<T>;
       data.value = res.data;
       error.value = res.error;
+			total.value= Number (res.total)
       endLoading();
       setNetwork(window.navigator.onLine);
     }
@@ -140,7 +142,8 @@ export function createHookRequest(axiosConfig: AxiosRequestConfig, backendConfig
       data,
       error,
       loading,
-      network
+      network,
+			total
     };
   }
 

+ 9 - 4
src/store/modules/auth/helpers.ts

@@ -2,18 +2,23 @@ import { localStg } from '@/utils';
 
 /** 获取token */
 export function getToken() {
-  return localStg.get('token') || '';
+  return localStorage.getItem('token');
 }
 
 /** 获取用户信息 */
 export function getUserInfo() {
   const emptyInfo: Auth.UserInfo = {
-    userId: '',
-    userName: '',
-    userRole: 'user'
+    id: null,
+		username: '',
+		phone:null,
+		email:'',
+		permissions:[],
+		departments:[],
+    userRole: 'admin'
   };
   const userInfo: Auth.UserInfo = localStg.get('userInfo') || emptyInfo;
 
+	console.log(userInfo);
   return userInfo;
 }
 

+ 46 - 43
src/store/modules/auth/index.ts

@@ -1,9 +1,9 @@
 import { unref, nextTick } from 'vue';
 import { defineStore } from 'pinia';
 import { router } from '@/router';
-import { fetchLogin, fetchUserInfo } from '@/service';
+import { fetchLogin, fetchUserInfo,AdminLoginParams } from '@/service';
 import { useRouterPush } from '@/composables';
-import { localStg } from '@/utils';
+// import { localStg } from '@/utils';
 import { useTabStore } from '../tab';
 import { useRouteStore } from '../route';
 import { getToken, getUserInfo, clearAuthStorage } from './helpers';
@@ -20,7 +20,7 @@ interface AuthState {
 export const useAuthStore = defineStore('auth-store', {
   state: (): AuthState => ({
     userInfo: getUserInfo(),
-    token: getToken(),
+    token: getToken() as string,
     loginLoading: false
   }),
   getters: {
@@ -60,8 +60,8 @@ export const useAuthStore = defineStore('auth-store', {
       const loginSuccess = await this.loginByToken(backendToken);
 
       if (loginSuccess) {
+				console.log(loginSuccess);
         await route.initAuthRoute();
-
         // 跳转登录后的地址
         toLoginRedirect();
 
@@ -69,7 +69,7 @@ export const useAuthStore = defineStore('auth-store', {
         if (route.isInitAuthRoute) {
           window.$notification?.success({
             title: '登录成功!',
-            content: `欢迎回来,${this.userInfo.userName}!`,
+            content: `欢迎回来,${this.userInfo.username}!`,
             duration: 3000
           });
         }
@@ -84,39 +84,42 @@ export const useAuthStore = defineStore('auth-store', {
      * 根据token进行登录
      * @param backendToken - 返回的token
      */
+
     async loginByToken(backendToken: ApiAuth.Token) {
       let successFlag = false;
-
       // 先把token存储到缓存中(后面接口的请求头需要token)
       const { token, refreshToken } = backendToken;
-      localStg.set('token', token);
-      localStg.set('refreshToken', refreshToken);
-
+      localStorage.setItem('token', token);
+      localStorage.setItem('refreshToken', refreshToken);
+			const params=	localStorage.getItem('token');
       // 获取用户信息
-      const { data } = await fetchUserInfo();
+      const { data } = await fetchUserInfo(params as string);
+			console.log(data);
       if (data) {
         // 成功后把用户信息存储到缓存中
-        localStg.set('userInfo', data);
-
+        localStorage.setItem('userInfo', JSON.stringify(data));
+				console.log(localStorage.getItem('userInfo'));
         // 更新状态
-        this.userInfo = data;
+				this.userInfo = data as Auth.UserInfo;
         this.token = token;
-
         successFlag = true;
       }
-
+			console.log(successFlag);
       return successFlag;
+
     },
+
     /**
      * 登录
      * @param userName - 用户名
      * @param password - 密码
      */
-    async login(userName: string, password: string) {
+
+    async login(params:AdminLoginParams) {
       this.loginLoading = true;
-      const { data } = await fetchLogin(userName, password);
+      const { data } = await fetchLogin(params);
       if (data) {
-        await this.handleActionAfterLogin(data);
+        await this.handleActionAfterLogin(data as ApiAuth.Token);
       }
       this.loginLoading = false;
     },
@@ -124,30 +127,30 @@ export const useAuthStore = defineStore('auth-store', {
      * 更换用户权限(切换账号)
      * @param userRole
      */
-    async updateUserRole(userRole: Auth.RoleType) {
-      const { resetRouteStore, initAuthRoute } = useRouteStore();
-
-      const accounts: Record<Auth.RoleType, { userName: string; password: string }> = {
-        super: {
-          userName: 'Super',
-          password: 'super123'
-        },
-        admin: {
-          userName: 'Admin',
-          password: 'admin123'
-        },
-        user: {
-          userName: 'User01',
-          password: 'user01123'
-        }
-      };
-      const { userName, password } = accounts[userRole];
-      const { data } = await fetchLogin(userName, password);
-      if (data) {
-        await this.loginByToken(data);
-        resetRouteStore();
-        initAuthRoute();
-      }
-    }
+    // async updateUserRole(userRole: Auth.RoleType) {
+    //   const { resetRouteStore, initAuthRoute } = useRouteStore();
+
+    //   const accounts: Record<Auth.RoleType, { userName: string; password: string }> = {
+    //     super: {
+    //       userName: 'Super',
+    //       password: 'super123'
+    //     },
+    //     admin: {
+    //       userName: 'Admin',
+    //       password: 'admin123'
+    //     },
+    //     user: {
+    //       userName: 'User01',
+    //       password: 'user01123'
+    //     }
+    //   };
+    //   const { userName, password } = accounts[userRole];
+    //   const { data } = await fetchLogin(userName, password);
+    //   if (data) {
+    //     await this.loginByToken(data);
+    //     resetRouteStore();
+    //     initAuthRoute();
+    //   }
+    // }
   }
 });

+ 7 - 8
src/store/modules/route/index.ts

@@ -91,8 +91,9 @@ export const useRouteStore = defineStore('route-store', {
       vueRoutes.forEach(route => {
         router.addRoute(route);
       });
-
+			console.log(vueRoutes);
       this.cacheRoutes = getCacheRoutes(vueRoutes);
+			console.log(this.cacheRoutes);
     },
     /** 动态路由模式下:更新根路由的重定向 */
     handleUpdateRootRedirect(routeKey: AuthRoute.AllRouteKey) {
@@ -110,13 +111,12 @@ export const useRouteStore = defineStore('route-store', {
       const { resetAuthStore } = useAuthStore();
       const { initHomeTab } = useTabStore();
 
-      const { userId } = localStg.get('userInfo') || {};
-
-      if (!userId) {
+      const { id } = localStorage.getItem('userInfo');
+      if (!id) {
         throw new Error('userId 不能为空!');
       }
 
-      const { error, data } = await fetchUserRoutes(userId);
+      const { error, data } = await fetchUserRoutes(id);
 
       if (!error) {
         this.routeHomeName = data.home;
@@ -134,12 +134,11 @@ export const useRouteStore = defineStore('route-store', {
     async initStaticRoute() {
       const { initHomeTab } = useTabStore();
       const auth = useAuthStore();
-
+			console.log(auth);
       const routes = filterAuthRoutesByUserPermission(staticRoutes, auth.userInfo.userRole);
+			console.log(routes);
       this.handleAuthRoute(routes);
-
       initHomeTab(this.routeHomeName, router);
-
       this.isInitAuthRoute = true;
     },
     /** 初始化权限路由 */

+ 0 - 29
src/typings/api.copy.ts

@@ -1,29 +0,0 @@
-
-declare namespace ApiUserMa {
-  interface User {
-    /** 用户id */
-		id?: number;
-    /** 用户名 */
-    userName: string | null;
-    /** 用户年龄 */
-    age: number | null;
-    /**
-     * 用户性别
-     * - 0: 女
-     * - 1: 男
-     */
-    gender: '0' | '1' | null;
-    /** 用户手机号码 */
-    phone: string;
-    /** 用户邮箱 */
-    email: string | null;
-    /**
-     * 用户状态
-     * - 1: 启用
-     * - 2: 禁用
-     * - 3: 冻结
-     * - 4: 软删除
-     */
-    userStatus: '1' | '2' | '3' | '4' | null;
-  }
-}

+ 9 - 16
src/typings/api.d.ts

@@ -25,28 +25,21 @@ declare namespace ApiRoute {
 declare namespace ApiUserManagement {
   interface User {
     /** 用户id */
-    id: string;
+    id: number ;
     /** 用户名 */
-    userName: string | null;
+    name: string | null;
     /** 用户年龄 */
-    age: number | null;
-    /**
-     * 用户性别
-     * - 0: 女
-     * - 1: 男
-     */
-    gender: '0' | '1' | null;
+    description: string | null;
+    createTime: string;
     /** 用户手机号码 */
-    phone: string;
+    modifyTime: string;
     /** 用户邮箱 */
-    email: string | null;
+    createUid: number|null;
     /**
      * 用户状态
-     * - 1: 启用
-     * - 2: 禁用
-     * - 3: 冻结
-     * - 4: 软删除
+     * - Y: 启用
+     * - N: 禁用
      */
-    userStatus: '1' | '2' | '3' | '4' | null;
+    disabled: 'Y' | 'N'|null ;
   }
 }

+ 11 - 10
src/typings/business.d.ts

@@ -6,14 +6,18 @@ declare namespace Auth {
    * - admin: 管理员
    * - user: 用户
    */
-  type RoleType = 'super' | 'admin' | 'user';
+  type RoleType = 'admin';
 
   /** 用户信息 */
   interface UserInfo {
     /** 用户id */
-    userId: string;
+    id: number | null;
     /** 用户名 */
-    userName: string;
+    username: string;
+    phone: number | null;
+    email: string;
+    permissions: [];
+    departments: [];
     /** 用户角色类型 */
     userRole: RoleType;
   }
@@ -24,7 +28,7 @@ declare namespace UserManagement {
     /** 序号 */
     index: number;
     /** 表格的key(id) */
-    key: string;
+    key: number;
   }
 
   /**
@@ -32,14 +36,11 @@ declare namespace UserManagement {
    * - 0: 女
    * - 1: 男
    */
-  type GenderKey = NonNullable<User['gender']>;
+  // type GenderKey = NonNullable<User['gender']>;
 
   /**
    * 用户状态
-   * - 1: 启用
-   * - 2: 禁用
-   * - 3: 冻结
-   * - 4: 软删除
+
    */
-  type UserStatusKey = NonNullable<User['userStatus']>;
+  type UserStatusKey = NonNullable<User['disabled']>;
 }

+ 2 - 0
src/typings/page-route.d.ts

@@ -56,6 +56,7 @@ declare namespace PageRoute {
     | 'management_auth'
     | 'management_role'
     | 'management_route'
+    | 'management_sort'
     | 'management_user'
     | 'multi-menu'
     | 'multi-menu_first'
@@ -113,6 +114,7 @@ declare namespace PageRoute {
     | 'management_auth'
     | 'management_role'
     | 'management_route'
+    | 'management_sort'
     | 'management_user'
     | 'multi-menu_first_second-new_third'
     | 'multi-menu_first_second'

+ 6 - 0
src/typings/system.d.ts

@@ -16,6 +16,7 @@ declare namespace Service {
     code: string | number;
     /** 错误信息 */
     msg: string;
+
   }
 
   /** 后端接口返回的数据结构配置 */
@@ -28,6 +29,7 @@ declare namespace Service {
     msgKey: string;
     /** 后端业务上定义的成功请求的状态 */
     successCode: boolean;
+		total:string
   }
 
   /** 自定义的请求成功结果 */
@@ -36,6 +38,7 @@ declare namespace Service {
     error: null;
     /** 请求数据 */
     data: T;
+		total:string;
   }
 
   /** 自定义的请求失败结果 */
@@ -44,6 +47,7 @@ declare namespace Service {
     error: RequestError;
     /** 请求数据 */
     data: null;
+		total:null;
   }
 
   /** 自定义的请求结果 */
@@ -379,6 +383,8 @@ declare namespace I18nType {
         role: string;
         route: string;
         user: string;
+			  sort: string;
+				usdt:string;
       };
       about: string;
     };

+ 21 - 9
src/utils/crypto/index.ts

@@ -1,14 +1,23 @@
 import CryptoJS from 'crypto-js';
 
-const CryptoSecret = '__CryptoJS_Secret__';
+const keyword = 'eas-key-password';
 
 /**
  * 加密数据
  * @param data - 数据
  */
 export function encrypto(data: any) {
-  const newData = JSON.stringify(data);
-  return CryptoJS.AES.encrypt(newData, CryptoSecret).toString();
+	const time = Date.now();
+	 //转码
+	 const wordStr = CryptoJS.enc.Utf8.parse(time + "" + data);
+	 const key = CryptoJS.enc.Utf8.parse(keyword);
+  // const newData = JSON.stringify(data);
+	//加密
+  const cryptoStr = CryptoJS.AES.encrypt(wordStr, key, {
+    mode: CryptoJS.mode.ECB, //模式
+    padding: CryptoJS.pad.Pkcs7, //补零
+  });
+ return cryptoStr.toString();
 }
 
 /**
@@ -16,10 +25,13 @@ export function encrypto(data: any) {
  * @param cipherText - 密文
  */
 export function decrypto(cipherText: string) {
-  const bytes = CryptoJS.AES.decrypt(cipherText, CryptoSecret);
-  const originalText = bytes.toString(CryptoJS.enc.Utf8);
-  if (originalText) {
-    return JSON.parse(originalText);
-  }
-  return null;
+	const key = CryptoJS.enc.Utf8.parse(keyword);
+	//解密
+  const cryptoStr = CryptoJS.AES.decrypt(cipherText, key, {
+    mode: CryptoJS.mode.ECB, //模式
+    padding: CryptoJS.pad.Pkcs7, //补零
+  });
+
+  return CryptoJS.enc.Utf8.stringify(cryptoStr).toString();
+
 }

+ 7 - 20
src/utils/form/rule.ts

@@ -1,6 +1,6 @@
 import type { Ref } from 'vue';
 import type { FormItemRule } from 'naive-ui';
-import { REGEXP_CODE_SIX, REGEXP_EMAIL, REGEXP_PHONE, REGEXP_PWD } from '@/config';
+import { REGEXP_PWD } from '@/config';
 
 /** 创建自定义错误信息的必填表单规则 */
 export const createRequiredFormRule = (message = '不能为空'): FormItemRule => ({ required: true, message });
@@ -10,30 +10,17 @@ export const requiredFormRule = createRequiredFormRule();
 /** 表单规则 */
 interface CustomFormRules {
   /** 手机号码 */
-  phone: FormItemRule[];
-  /** 密码 */
-  pwd: FormItemRule[];
-  /** 验证码 */
-  code: FormItemRule[];
-  /** 邮箱 */
-  email: FormItemRule[];
+  createTime: FormItemRule[];
+	pwd:FormItemRule[];
 }
 
 /** 表单规则 */
 export const formRules: CustomFormRules = {
-  phone: [
-    createRequiredFormRule('请输入手机号码'),
-    { pattern: REGEXP_PHONE, message: '手机号码格式错误', trigger: 'input' }
+  createTime: [
+    createRequiredFormRule('请输入创建时间'),
+    { pattern: '', message: '不能为空', trigger: 'input' }
   ],
-  pwd: [
-    createRequiredFormRule('请输入密码'),
-    { pattern: REGEXP_PWD, message: '密码为6-18位数字/字符/符号,至少2种组合', trigger: 'input' }
-  ],
-  code: [
-    createRequiredFormRule('请输入验证码'),
-    { pattern: REGEXP_CODE_SIX, message: '验证码格式错误', trigger: 'input' }
-  ],
-  email: [{ pattern: REGEXP_EMAIL, message: '邮箱格式错误', trigger: 'blur' }]
+	pwd: [{ pattern: REGEXP_PWD, message: '密码格式错误', trigger: 'blur' }]
 };
 
 /** 是否为空字符串 */

+ 0 - 1
src/utils/router/auth.ts

@@ -16,7 +16,6 @@ function filterAuthRouteByUserPermission(route: AuthRoute.Route, permission: Aut
   const filterRoute = { ...route };
   const hasPermission =
     !route.meta.permissions || permission === 'super' || route.meta.permissions.includes(permission);
-
   if (filterRoute.children) {
     const filterChildren = filterRoute.children.map(item => filterAuthRouteByUserPermission(item, permission)).flat(1);
     Object.assign(filterRoute, { children: filterChildren });

+ 1 - 0
src/utils/router/cache.ts

@@ -16,6 +16,7 @@ export function getCacheRoutes(routes: RouteRecordRaw[]) {
       });
     }
   });
+	console.log(cacheNames);
   return cacheNames;
 }
 

+ 0 - 1
src/utils/router/menu.ts

@@ -95,7 +95,6 @@ function addPartialProps(config: {
   const item = { ...config.menu };
 
   const { icon, localIcon, children } = config;
-
   if (localIcon) {
     Object.assign(item, { icon: iconRender({ localIcon }) });
   }

+ 10 - 5
src/utils/service/handler.ts

@@ -1,15 +1,17 @@
 /** 统一失败和成功的请求结果的数据类型 */
-export async function handleServiceResult<T = any>(error: Service.RequestError | null, data: any) {
+export async function handleServiceResult<T = any>(error: Service.RequestError | null, data: any,total:any) {
   if (error) {
     const fail: Service.FailedResult = {
       error,
-      data: null
+      data: null,
+			total:null,
     };
     return fail;
   }
   const success: Service.SuccessResult<T> = {
     error: null,
-    data
+    data,
+		total
   };
   return success;
 }
@@ -26,7 +28,8 @@ export function adapter<T extends Service.ServiceAdapter>(
     if (flag) {
       result = {
         error: item.error,
-        data: null
+        data: null,
+				total:null
       };
     }
     return flag;
@@ -34,9 +37,11 @@ export function adapter<T extends Service.ServiceAdapter>(
 
   if (!hasError) {
     const adapterFunArgs = args.map(item => item.data);
+		const adapterFunArgss=args.map(item => item.total);
     result = {
       error: null,
-      data: adapterFun(...adapterFunArgs)
+      data: adapterFun(...adapterFunArgs),
+			total:adapterFun(...adapterFunArgss),
     };
   }
 

+ 1 - 2
src/utils/storage/local.ts

@@ -1,5 +1,5 @@
 import { decrypto, encrypto } from '../crypto';
-interface StorageData<T> {
+ interface StorageData<T> {
   value: T;
   expire: number | null;
 }
@@ -7,7 +7,6 @@ interface StorageData<T> {
 function createLocalStorage<T extends StorageInterface.Local = StorageInterface.Local>() {
   /** 默认缓存期限为7天 */
   const DEFAULT_CACHE_TIME = 60 * 60 * 24 * 7;
-
   function set<K extends keyof T>(key: K, value: T[K], expire: number | null = DEFAULT_CACHE_TIME) {
     const storageData: StorageData<T[K]> = {
       value,

File diff suppressed because it is too large
+ 356 - 0
src/views/_builtin/login/components/pwd-login/components/verifition/Verify.vue


+ 260 - 0
src/views/_builtin/login/components/pwd-login/components/verifition/Verify/VerifyPoints.vue

@@ -0,0 +1,260 @@
+<template>
+    <div style="position: relative"
+        >
+        <div class="verify-img-out">
+            <div class="verify-img-panel" :style="{'width': setSize.imgWidth,
+                                                   'height': setSize.imgHeight,
+                                                   'background-size' : setSize.imgWidth + ' '+ setSize.imgHeight,
+                                                   'margin-bottom': vSpace + 'px'}"
+                                                    >
+                <div class="verify-refresh" style="z-index:3" @click="refresh" v-show="showRefresh">
+                    <i class="iconfont icon-refresh"></i>
+                </div>
+                <img :src="'data:image/png;base64,'+pointBackImgBase" 
+                ref="canvas"
+                alt=""  style="width:100%;height:100%;display:block"
+                @click="bindingClick?canvasClick($event):undefined">
+
+                <div v-for="(tempPoint, index) in tempPoints" :key="index" class="point-area"
+                     :style="{
+                        'background-color':'#1abd6c',
+                        color:'#fff',
+                        'z-index':9999,
+                        width:'20px',
+                        height:'20px',
+                        'text-align':'center',
+                        'line-height':'20px',
+                        'border-radius': '50%',
+                        position:'absolute',
+                        top:parseInt(tempPoint.y-10) + 'px',
+                        left:parseInt(tempPoint.x-10) + 'px'
+                     }">
+                    {{index + 1}}
+                </div>
+            </div>
+        </div>
+        <!-- 'height': this.barSize.height, -->
+        <div class="verify-bar-area"
+             :style="{'width': setSize.imgWidth,
+                      'color': this.barAreaColor,
+                      'border-color': this.barAreaBorderColor,
+                      'line-height':this.barSize.height}">
+            <span class="verify-msg">{{text}}</span>
+        </div>
+    </div>
+</template>
+<script type="text/babel">
+    /**
+     * VerifyPoints
+     * @description 点选
+     * */
+    import {resetSize, _code_chars, _code_color1, _code_color2} from './../utils/util'
+    import {aesEncrypt} from "./../utils/ase"
+    import {reqGet,reqCheck}  from "./../api/index"
+    import { computed, onMounted, reactive, ref,watch,nextTick,toRefs, watchEffect,getCurrentInstance} from 'vue';
+    export default {
+        name: 'VerifyPoints',
+        props: {
+            //弹出式pop,固定fixed
+            mode: {
+                type: String,
+                default: 'fixed'
+            },
+            captchaType:{
+                type:String,
+            },
+            //间隔
+            vSpace: {
+                type: Number,
+                default: 5
+            },
+            imgSize: {
+                type: Object,
+                default() {
+                    return {
+                        width: '310px',
+                        height: '155px'
+                    }
+                }
+            },
+            barSize: {
+                type: Object,
+                default() {
+                    return {
+                        width: '310px',
+                        height: '40px'
+                    }
+                }
+            }
+        },
+        setup(props,context){
+            const {mode,captchaType,vSpace,imgSize,barSize} = toRefs(props)
+            const { proxy } = getCurrentInstance();
+            let secretKey = ref(''),           //后端返回的ase加密秘钥
+                checkNum = ref(3),             //默认需要点击的字数
+                fontPos = reactive([]),            //选中的坐标信息
+                checkPosArr = reactive([]),        //用户点击的坐标
+                num = ref(1),                 //点击的记数
+                pointBackImgBase = ref(''),    //后端获取到的背景图片
+                poinTextList = reactive([]),        //后端返回的点击字体顺序
+                backToken = ref(''),           //后端返回的token值
+                setSize = reactive({
+                    imgHeight: 0,
+                    imgWidth: 0,
+                    barHeight: 0,
+                    barWidth: 0
+                }),
+                tempPoints = reactive([]),
+                text = ref(''),
+                barAreaColor = ref(undefined),
+                barAreaBorderColor = ref(undefined),
+                showRefresh = ref(true),
+                bindingClick = ref(true)
+
+                
+               
+
+                const init = ()=>{
+                    //加载页面
+                    fontPos.splice(0, fontPos.length)
+                    checkPosArr.splice(0, checkPosArr.length)
+                    num.value = 1
+                    getPictrue();
+                    nextTick(() => {
+                        let {imgHeight,imgWidth,barHeight,barWidth} = resetSize(proxy)
+                        setSize.imgHeight = imgHeight
+                        setSize.imgWidth = imgWidth
+                        setSize.barHeight = barHeight
+                        setSize.barWidth = barWidth
+                        proxy.$parent.$emit('ready', proxy)
+                    })
+                }
+                 onMounted(()=>{
+                    // 禁止拖拽
+                    init()
+                    proxy.$el.onselectstart = function () {
+                        return false
+                    }
+                })
+                const canvas = ref(null)
+                const canvasClick = (e)=>{
+                    checkPosArr.push(getMousePos(canvas, e));
+                    if (num.value == checkNum.value) {
+                        num.value = createPoint(getMousePos(canvas, e));
+                        //按比例转换坐标值
+                        let arr = pointTransfrom(checkPosArr,setSize)
+                        checkPosArr.length = 0
+                        checkPosArr.push(...arr);
+                        //等创建坐标执行完
+                        setTimeout(() => {
+                            // var flag = this.comparePos(this.fontPos, this.checkPosArr);
+                            //发送后端请求
+                            var captchaVerification = secretKey.value? aesEncrypt(backToken.value+'---'+JSON.stringify(checkPosArr),secretKey.value):backToken.value+'---'+JSON.stringify(checkPosArr)
+                            let data = {
+                                captchaType:captchaType.value,
+                                "pointJson":secretKey.value? aesEncrypt(JSON.stringify(checkPosArr),secretKey.value):JSON.stringify(checkPosArr),
+                                "token":backToken.value
+                            }
+                            reqCheck(data).then(res=>{
+                                if (res.repCode == "0000") {
+                                    barAreaColor.value = '#4cae4c'
+                                    barAreaBorderColor.value = '#5cb85c'
+                                    text.value = '验证成功'
+                                    bindingClick.value = false
+                                    if (mode.value=='pop') {
+                                        setTimeout(()=>{
+                                            proxy.$parent.clickShow = false;
+                                            refresh();
+                                        },1500)
+                                    }
+                                    proxy.$parent.$emit('success', {captchaVerification})
+                                }else{
+                                    proxy.$parent.$emit('error', proxy)
+                                    barAreaColor.value = '#d9534f'
+                                    barAreaBorderColor.value = '#d9534f'
+                                    text.value = '验证失败'
+                                    setTimeout(() => {
+                                        refresh();
+                                    }, 700);
+                                }
+                            })
+                        }, 400);
+                    }
+                    if (num.value < checkNum.value) {
+                        num.value = createPoint(getMousePos(canvas, e));
+                    }
+                }
+                 //获取坐标
+                const getMousePos = function (obj, e) {
+                    var x = e.offsetX 
+                    var y = e.offsetY 
+                    return {x, y}
+                }
+                //创建坐标点
+                const createPoint = function (pos) {
+                    tempPoints.push(Object.assign({}, pos))
+                    return num.value+1;
+                }
+                const refresh = function () {
+                    tempPoints.splice(0, tempPoints.length)
+                    barAreaColor.value = '#000'
+                    barAreaBorderColor.value = '#ddd'
+                    bindingClick.value = true
+                    fontPos.splice(0, fontPos.length)
+                    checkPosArr.splice(0, checkPosArr.length)
+                    num.value = 1
+                    getPictrue();
+                    text.value = '验证失败'
+                    showRefresh.value = true
+                }
+
+                // 请求背景图片和验证图片
+                function getPictrue() {
+                    let data = {
+                        captchaType:captchaType.value
+                    }
+                    reqGet(data).then(res=>{
+                        if (res.repCode == "0000") {
+                            pointBackImgBase.value = res.repData.originalImageBase64
+                            backToken.value = res.repData.token
+                            secretKey.value = res.repData.secretKey
+                            poinTextList.value = res.repData.wordList
+                            text.value = '请依次点击【' + poinTextList.value.join(",") + '】'
+                        }else{
+                            text.value = res.repMsg;
+                        }
+                    })
+                }
+                //坐标转换函数
+                const pointTransfrom = function(pointArr,imgSize){
+                    var newPointArr = pointArr.map(p=>{
+                        let x = Math.round(310 * p.x/parseInt(imgSize.imgWidth)) 
+                        let y =Math.round(155 * p.y/parseInt(imgSize.imgHeight)) 
+                        return {x,y}
+                    })
+                    return newPointArr
+                }
+                return {
+                    secretKey,
+                    checkNum,
+                    fontPos,
+                    checkPosArr,
+                    num,
+                    pointBackImgBase,
+                    poinTextList,
+                    backToken,
+                    setSize,
+                    tempPoints,
+                    text,
+                    barAreaColor,
+                    barAreaBorderColor,
+                    showRefresh,
+                    bindingClick,
+                    init,
+                    canvas,
+                    canvasClick,
+                    getMousePos,createPoint,refresh,getPictrue,pointTransfrom
+                }
+        },
+    }
+</script>

File diff suppressed because it is too large
+ 549 - 0
src/views/_builtin/login/components/pwd-login/components/verifition/Verify/VerifySlide.vue


+ 27 - 0
src/views/_builtin/login/components/pwd-login/components/verifition/api/index.js

@@ -0,0 +1,27 @@
+/**
+ * 此处可直接引用自己项目封装好的 axios 配合后端联调
+ */
+
+
+import request from "./../utils/axios"  //组件内部封装的axios
+// import request from "@/api/axios.js"       //调用项目封装的axios
+
+//获取验证图片  以及token
+export function reqGet(data) {
+	return  request({
+        url: '/proxy-pattern/verify.get',
+        method: 'post',
+        data
+    })
+}
+
+//滑动或者点选验证
+export function reqCheck(data) {
+	return  request({
+        url: '/proxy-pattern/verify.check',
+        method: 'post',
+        data
+    })
+}
+
+

+ 11 - 0
src/views/_builtin/login/components/pwd-login/components/verifition/utils/ase.js

@@ -0,0 +1,11 @@
+import CryptoJS from 'crypto-js'
+/**
+ * @word 要加密的内容
+ * @keyWord String  服务器随机返回的关键字
+ *  */
+export function aesEncrypt(word,keyWord="XwKsGlMcdPMEhR1B"){
+  var key = CryptoJS.enc.Utf8.parse(keyWord);
+  var srcs = CryptoJS.enc.Utf8.parse(word);
+  var encrypted = CryptoJS.AES.encrypt(srcs, key, {mode:CryptoJS.mode.ECB,padding: CryptoJS.pad.Pkcs7});
+  return encrypted.toString();
+}

+ 30 - 0
src/views/_builtin/login/components/pwd-login/components/verifition/utils/axios.js

@@ -0,0 +1,30 @@
+import axios from 'axios';
+
+axios.defaults.baseURL = '/';
+
+const service = axios.create({
+  timeout: 40000,
+  headers: {
+    'X-Requested-With': 'XMLHttpRequest',
+    'Content-Type': 'application/json; charset=UTF-8'
+  },
+})
+service.interceptors.request.use(
+  config => {
+    return config
+  },
+  error => {
+    Promise.reject(error)
+  }
+)
+
+// response interceptor
+service.interceptors.response.use(
+  response => {
+    const res = response.data;
+    return res
+  },
+  error => {
+  }
+)
+export default service

+ 35 - 0
src/views/_builtin/login/components/pwd-login/components/verifition/utils/util.js

@@ -0,0 +1,35 @@
+export function resetSize(vm) {
+    var img_width, img_height, bar_width, bar_height;	//图片的宽度、高度,移动条的宽度、高度
+
+    var parentWidth = vm.$el.parentNode.offsetWidth || window.offsetWidth
+    var parentHeight = vm.$el.parentNode.offsetHeight || window.offsetHeight
+    if (vm.imgSize.width.indexOf('%') != -1) {
+        img_width = parseInt(vm.imgSize.width) / 100 * parentWidth + 'px'
+    } else {
+        img_width = vm.imgSize.width;
+    }
+
+    if (vm.imgSize.height.indexOf('%') != -1) {
+        img_height = parseInt(vm.imgSize.height) / 100 * parentHeight + 'px'
+    } else {
+        img_height = vm.imgSize.height
+    }
+
+    if (vm.barSize.width.indexOf('%') != -1) {
+        bar_width = parseInt(vm.barSize.width) / 100 * parentWidth + 'px'
+    } else {
+        bar_width = vm.barSize.width
+    }
+
+    if (vm.barSize.height.indexOf('%') != -1) {
+        bar_height = parseInt(vm.barSize.height) / 100 * parentHeight + 'px'
+    } else {
+        bar_height = vm.barSize.height
+    }
+
+    return {imgWidth: img_width, imgHeight: img_height, barWidth: bar_width, barHeight: bar_height}
+}
+
+export const _code_chars = [1, 2, 3, 4, 5, 6, 7, 8, 9, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
+export const _code_color1 = ['#fffff0', '#f0ffff', '#f0fff0', '#fff0f0']
+export const _code_color2 = ['#FF0033', '#006699', '#993366', '#FF9900', '#66CC66', '#FF33CC']

File diff suppressed because it is too large
+ 110 - 12
src/views/_builtin/login/components/pwd-login/index.vue


+ 0 - 1
src/views/crud/demo/crud.tsx

@@ -64,7 +64,6 @@ export default function createCrudOptions(): CreateCrudOptionsRet {
           }
         },
         select: {
-          title: '状态',
           search: { show: true },
           type: 'dict-select',
           dict: dict({

+ 5 - 0
src/views/dashboard/analysis/index.vue

@@ -2,12 +2,17 @@
   <n-space :vertical="true" :size="16">
     <top-chart />
     <data-card />
+		<div>
+			<PDFView :pdfUrl="jsPdf" />
+		</div>
     <bottom-part />
   </n-space>
 </template>
 
 <script lang="ts" setup>
 import { BottomPart, DataCard, TopChart } from './components';
+import PDFView from "../../.././components/View/pdfPreview.vue"
+import jsPdf from "../../../java.pdf"
 </script>
 
 <style scoped></style>

+ 1 - 0
src/views/index.ts

@@ -35,6 +35,7 @@ export const views: Record<
   management_auth: () => import('./management/auth/index.vue'),
   management_role: () => import('./management/role/index.vue'),
   management_route: () => import('./management/route/index.vue'),
+  management_sort: () => import('./management/sort/index.vue'),
   management_user: () => import('./management/user/index.vue'),
   'multi-menu_first_second-new_third': () => import('./multi-menu/first/second-new/third/index.vue'),
   'multi-menu_first_second': () => import('./multi-menu/first/second/index.vue'),

+ 87 - 4
src/views/management/auth/index.vue

@@ -1,9 +1,92 @@
 <template>
-  <div>权限管理</div>
+  <div class="h-full overflow-hidden">
+    <n-card title="权限管理" :bordered="false" class="rounded-16px shadow-sm">
+      <n-data-table :columns="columns" :data="tableData" :pagination="pagination" />
+    </n-card>
+  </div>
 </template>
 
-<script setup lang="tsx">
+<script setup lang="ts">
+import { ref } from 'vue';
+import type { Ref } from 'vue';
+import type { DataTableColumns, PaginationProps } from 'naive-ui';
+import { query } from '~/src/service/api/user';
+import type { QueryParams } from '~/src/service/api/user';
 
-</script>
+const tableData = ref<QueryParams[]>([]);
+const pagination: PaginationProps = ref({
+  page: 1,
+  pageSize: 10,
+  showSizePicker: true,
+  pageSizes: [10, 20, 50]
+  // onChange: (page: number) => {
+  //   // 处理页码变化
+  // },
+  // onUpdatePageSize: (pageSize: number) => {
+  //   // 处理每页显示数量变化
+  // }
+}).value;
+
+async function getTableData() {
+  const pageNum = pagination.page as number;
+  const pageSize = pagination.pageSize as number;
+  const params: QueryParams = {};
+
+  query(pageNum, pageSize, params).then(res => {
+    // console.log(res);
+    tableData.value = res.data as [];
+  });
+}
 
-<style scoped></style>
+const columns: Ref<DataTableColumns<QueryParams>> = ref([
+  {
+    type: 'selection',
+    align: 'center'
+  },
+  {
+    key: 'name',
+    title: '部门名称',
+    align: 'center'
+  },
+  {
+    key: 'description',
+    title: '部门地址',
+    align: 'center'
+  },
+  {
+    key: 'isActive',
+    title: '是否激活',
+    align: 'center',
+    render: (row: QueryParams) => {
+      return row.isActive ? '是' : '否';
+    }
+  },
+  {
+    key: 'createTime',
+    title: '创建时间',
+    align: 'center'
+  },
+  {
+    key: 'modifyTime',
+    title: '修改时间',
+    align: 'center'
+  },
+  {
+    key: 'createUid',
+    title: '创建用户ID',
+    align: 'center'
+  },
+  {
+    key: 'disabled',
+    title: '状态',
+    align: 'center'
+  }
+]) as Ref<DataTableColumns<QueryParams>>;
+
+function init() {
+  getTableData();
+}
+
+// 初始化
+init();
+</script>

+ 4 - 13
src/views/management/role/index.vue

@@ -28,21 +28,12 @@ const pagination: PaginationProps = ref({
 }).value;
 
 async function getTableData() {
-  const pageNum = pagination.page as number;
-  const pageSize = pagination.pageSize as number;
-
-  const params: QueryParams = {
-    name: '',
-    description: '',
-    isActive: '',
-    createTime: '',
-    modifyTime: '',
-    createUid: 0,
-    disabled: ''
-  };
+  const pageNum = pagination.page as number;//页码
+  const pageSize = pagination.pageSize as number;//当前所在数
+  const params: QueryParams = {};
 
   query(pageNum, pageSize, params).then(res => {
-    console.log(res);
+    // console.log(res);
     tableData.value = res.data as [];
   });
 }

+ 16 - 19
src/views/management/role/queryUser.vue

@@ -1,36 +1,33 @@
 <template>
-	<div class="h-full overflow-hidden">
-		<n-card title="权限管理" :bordered="false" class="rounded-16px shadow-sm">
-			<n-data-table :columns="columns" :data="tableData" :pagination="pagination" />
-		</n-card>
-	</div>
+  <div class="h-full overflow-hidden">
+    <n-card title="权限管理" :bordered="false" class="rounded-16px shadow-sm">
+      <n-data-table :columns="columns" :data="tableData" :pagination="pagination" />
+    </n-card>
+  </div>
 </template>
 
 <script setup lang="ts">
 import { ref } from 'vue';
 import type { Ref } from 'vue';
-
 import type { DataTableColumns, PaginationProps } from 'naive-ui';
-
 import { query } from '~/src/service/api/user';
 import type { QueryParams } from '~/src/service/api/user';
 
 const tableData = ref<QueryParams[]>([]);
 
-
 async function getTableData() {
-	const pageNum = pagination.page as number;
-	const pageSize = pagination.pageSize as number;
+  const pageNum = pagination.page as number;
+  const pageSize = pagination.pageSize as number;
 
-	const params: QueryParams = {
-		name: '',
-		description: '',
-		isActive: '',
-		createTime: "",
-		modifyTime: "" ,
-		createUid: 0,
-		disabled: '',
-	};
+  const params: QueryParams = {
+    name: '',
+    description: '',
+    isActive: '',
+    createTime: '',
+    modifyTime: "" ,
+    createUid: 0,
+    disabled: '',
+  };
 
 	query(pageNum, pageSize, params).then((res) => {
 			console.log(res);

+ 14 - 16
src/views/management/role/userPa.vue

@@ -8,33 +8,31 @@
 import { reactive } from 'vue';
 import { defineComponent, ref } from 'vue';
 import type { QueryParams } from '~/src/service/api/user';
-import { query } from '~/src/service/api/user';
 
 export default defineComponent({
   setup() {
-		const pageNum = ref(2)
-		const pageSize = ref<any>(10)
-		const queryData = reactive<QueryParams>({})
-		function queryList (){
-			console.log(1);
+    const pageNum = ref(2);
+    const pageSize = ref<any>(10);
+    const queryData = reactive<QueryParams>({});
+    function queryList() {
+      console.log(1);
 
-			// query(1, 10, queryData).then(r => {
-			// 	console.log(r);
-			// });
-		}
+      // query(1, 10, queryData).then(r => {
+      // 	console.log(r);
+      // });
+    }
 
     return {
-			// 每页条数, 可自定义 page-size
+      // 每页条数, 可自定义 page-size
       pageNum,
-			//page-count
+      // page-count
       pageSize,
-			queryList,
-			queryData,
-			// page: ref(2),
+      queryList,
+      queryData
+      // page: ref(2),
       // pageSize: ref(20),
       // queryData:reactive({})
     };
-
   }
 });
 </script>

+ 49 - 0
src/views/management/sort/api.ts

@@ -0,0 +1,49 @@
+import type { UserPageQuery } from '@fast-crud/fast-crud';
+import { request } from '@/service/request';
+
+// 响应接口
+export interface SelectAll_1Res {
+  status: boolean;
+  msg: string;
+  data: Record<string, unknown>;
+}
+
+const apiPrefix = ``;
+
+export type HeaderGroupRecord = {
+  id: number;
+  [key: string]: any;
+};
+
+function resHandle(res: any) {
+  return res.data;
+}
+export async function selectAll_1(query: UserPageQuery) {
+  const res = await request.get(`/selectAll`, query);
+  return resHandle(res);
+}
+
+export async function AddObj(obj: HeaderGroupRecord) {
+  const res = await request.post(`${apiPrefix}/add`, obj);
+  return resHandle(res);
+}
+
+export async function UpdateObj(obj: HeaderGroupRecord) {
+  const res = await request.post(`${apiPrefix}/update`, obj);
+  return resHandle(res);
+}
+
+export async function DelObj(id: number) {
+  const res = await request.post(`${apiPrefix}/delete`, { id });
+  return resHandle(res);
+}
+
+export async function GetObj(id: number) {
+  const res = await request.get(`${apiPrefix}/info`, { params: { id } });
+  return resHandle(res);
+}
+
+export async function BatchDelete(ids: number[]) {
+  const res = await request.post(`${apiPrefix}/batchDelete`, { ids });
+  return resHandle(res);
+}

+ 88 - 0
src/views/management/sort/components/column-search.vue

@@ -0,0 +1,88 @@
+<template>
+	<n-space>
+		<n-input-group>
+			<n-input-group>
+				 <span class="w-80px mr-5px line-height-33.99px">学科名称</span> <n-input :style="{ width: '31%', marginRight: '2%' }"
+					clearable />
+				<span class="w-80px mr-5px line-height-33.99px">学科描述</span> <n-input :style="{ width: '31%' ,marginRight:'2%'}" clearable/>
+    		<span class="w-100px line-height-33.99px">创建用户ID</span> <n-input-number :style="{ width: '22%',marginRight:'10%' }" clearable />
+			</n-input-group>
+			<n-input-group>
+			  <span class="line-height-33.99px mr-5px w-70px">创建时间</span>
+				<n-date-picker :style="{ width: '13%' }" />
+				<n-time-picker :style="{ width: '14.2%', marginRight: '2%' }" />
+				<span class="line-height-33.99px mr-5px w-70px">修改时间</span>
+				<n-date-picker :style="{ width: '13%' }" />
+				<n-time-picker :style="{ width: '14.2%', marginRight: '2%' }" />
+				<span class="w-50px  line-height-33.99px">状态</span>
+				<n-select :options="selectOptions" :style="{ width: '21%', marginRight: '2%' }" clearable />
+			</n-input-group>
+			<span class="w-40px mr-5px line-height-33.99px">页码</span> <n-input-number :style="{ width: '48%', marginRight: '2%' }"
+				placeholder="请输入页码..." clearable v-model="pagination.page" @change="searchCondition" />
+			<span class="w-40px mr-5px line-height-33.99px">条数</span> <n-input-number :style="{ width: '48%' }"
+				placeholder="请输入数据条数..." clearable v-model="pagination.pageSize" @change="searchCondition" />
+			 <n-button type="primary" ghost @click="searchCondition()">
+				搜索
+			</n-button>
+		</n-input-group>
+	</n-space>
+</template>
+
+<script lang="ts">
+import { defineComponent, ref } from 'vue'
+import { NButton, NSpace } from 'naive-ui';
+import type { PaginationProps } from 'naive-ui';
+
+import { selectByCondition_1 } from '~/src/service/api/sort';
+import type { SelectByCondition_1Params } from '~/src/service/api/sort';
+
+const searchData = ref<any[]>([]);
+const pagination: PaginationProps = ref({
+	page: 1,
+	pageSize: 10,
+	showSizePicker: true,
+	pageSizes: [10, 20, 50],
+	onChange: (page: number) => {
+		// 处理页码变化
+		pagination.page = page;
+	},
+	onUpdatePageSize: (pageSize: number) => {
+		// 处理每页显示数量变化
+		pagination.pageSize = pageSize;
+		pagination.page = 1;
+	}
+}).value;
+
+function searchCondition() {
+	const pageNum = pagination.page as number;
+	const pageSize = pagination.pageSize as number;
+	const params: SelectByCondition_1Params = {};
+	console.log(pageNum, pageSize);
+
+	selectByCondition_1(pageNum, pageSize, params).then(res => {
+		// console.log(res);
+		searchData.value = res.data as [];
+		console.log(searchData.value);
+	});
+}
+
+export default defineComponent({
+	setup() {
+		return {
+			searchCondition,
+			searchData,
+			pagination,
+			selectOptions: ref([
+				{
+					label: 'Y',
+					value: 'Y'
+				},
+				{
+					label: 'N',
+					value: 'N'
+				}
+			]),
+		}
+	}
+})
+</script>

+ 206 - 0
src/views/management/sort/components/table-action-add.vue

@@ -0,0 +1,206 @@
+<template>
+	<n-modal v-model:show="modalVisible" preset="card" :title="title" class="w-700px">
+		<n-form ref="formRef" label-placement="left" :label-width="100" :model="formModel" :rules="rules">
+			<n-grid :cols="24" :x-gap="18">
+				<!-- <n-form-item-grid-item :span="12" label="ID" path="id">
+					<n-input-number v-model:value="formModel.id" />
+				</n-form-item-grid-item> -->
+				<n-form-item-grid-item :span="12" label="学科名称" path="name">
+					<n-input v-model:value="formModel.name" clearable />
+				</n-form-item-grid-item>
+				<!-- <n-form-item-grid-item :span="12" label="创建时间" path="createTime">
+					  <n-date-picker v-model:value="timestamp" type="datetime" clearable />
+					<n-input v-model:value="formModel.createTime" />
+				</n-form-item-grid-item> -->
+				<n-form-item-grid-item :span="12" label="学科描述" path="description">
+					<n-input v-model:value="formModel.description" clearable />
+					<!-- <n-radio-group v-model:value="formModel.description">
+						<n-radio v-for="item in genderOptions" :key="item.value" :value="item.value">{{ item.label }}</n-radio>
+					</n-radio-group> -->
+				</n-form-item-grid-item>
+				<!-- <n-form-item-grid-item :span="12" label="修改时间" path="email">
+					<n-date-picker v-show="props.type==='edit'" v-model:value="formModel.modifyTime" type="datetime" clearable />
+				</n-form-item-grid-item> -->
+				<n-form-item-grid-item :span="12" label="创建用户ID" path="createUid">
+					<n-input-number v-model:value="formModel.createUid" />
+				</n-form-item-grid-item>
+				<n-form-item-grid-item :span="12" label="状态" path="disabled">
+					<n-select v-model:value="formModel.disabled" :options="userStatusOptions" />
+				</n-form-item-grid-item>
+			</n-grid>
+			<n-space class="w-full pt-16px" :size="24" justify="end">
+				<n-button class="w-72px" @click="closeModal">取消</n-button>
+				<n-button class="w-72px" type="primary" @click="handleSubmit">确定</n-button>
+			</n-space>
+		</n-form>
+	</n-modal>
+</template>
+
+<script setup lang="ts">
+import { ref, computed, reactive, watch } from 'vue';
+import type { FormInst, FormItemRule } from 'naive-ui';
+import { userStatusOptions } from '@/constants';
+import {createRequiredFormRule } from '@/utils';
+import dayjs from 'dayjs';
+import { addEasEduCategory, AddEasEduCategoryParams,updateEasEduCategory,UpdateEasEduCategoryParams,SelectByCondition_1Params } from '~/src/service/api/sort';
+
+
+export interface Props {
+	/** 弹窗可见性 */
+	visible: boolean;
+	/**
+	 * 弹窗类型
+	 * add: 新增
+	 * edit: 编辑
+	 */
+	type?: 'add' | 'edit' ;
+	/** 编辑的表格行数据 */
+	editData?: SelectByCondition_1Params | null;
+}
+
+export type ModalType = NonNullable<Props['type']>;
+
+defineOptions({ name: 'TableActionAdd' });
+
+const props = withDefaults(defineProps<Props>(), {
+	type: 'add'||'edit',
+	editData: null,
+});
+
+interface Emits {
+	(e: 'update:visible', visible: boolean): void;
+	  /** 点击协议 */
+	(e: 'searchCondition'): void;
+}
+
+const emit = defineEmits<Emits>();
+
+const modalVisible = computed({
+	get() {
+		return props.visible;
+	},
+	set(visible) {
+		emit('update:visible', visible);
+	}
+});
+
+const closeModal = () => {
+	modalVisible.value = false;
+};
+
+const title = computed(() => {
+	const titles: Record<ModalType, string> = {
+		add: '添加用户',
+		edit: '编辑用户',
+	};
+	// console.log(titles[props.type]);
+	return titles[props.type];
+
+});
+
+const formRef = ref<HTMLElement & FormInst>();
+
+
+interface FormModel extends Pick<SelectByCondition_1Params, 'id' |'name' | 'description' | 'createUid' | 'disabled'|'createTime'> {
+  modifyTime: string;
+}
+
+const formModel = reactive<FormModel>(createDefaultFormModel());
+
+const rules: Record<keyof FormModel, FormItemRule | FormItemRule[]> = {
+	id: createRequiredFormRule('请输入用户名'),
+	name: createRequiredFormRule('请输入学科名称'),
+	description: createRequiredFormRule('请进行部门描述'),
+	createTime:createRequiredFormRule('请输入创建时间'),
+	modifyTime:createRequiredFormRule('请输入修改时间'),
+	createUid: createRequiredFormRule('请输入创建用户ID'),
+	disabled: createRequiredFormRule('请选择用户状态')
+};
+// 'createTime' |'modifyTime'|
+function createDefaultFormModel(): FormModel {
+	return {
+		id: null,
+		name: '',
+		description: '',
+		createTime: '',
+		modifyTime:'',
+		createUid:null,
+		disabled: null,
+	};
+}
+
+function handleUpdateFormModel(model: SelectByCondition_1Params) {
+	Object.assign(formModel, model);
+}
+
+function handleUpdateFormModelByModalType() {
+	const handlers: Record<ModalType, () => void> = {
+		add: () => {
+			const defaultFormModel = createDefaultFormModel();
+			handleUpdateFormModel(defaultFormModel);
+		},
+		edit: () => {
+			if (props.editData) {
+				console.log(props.editData);
+				handleUpdateFormModel(props.editData);
+			}
+		},
+	};
+
+	handlers[props.type]();
+}
+console.log(props.type);
+async function handleSubmit() {
+	await formRef.value?.validate()
+  if (props.type === 'add') {
+    const params: AddEasEduCategoryParams = {
+      // id: formModel.id,
+      name: formModel.name,
+      description: formModel.description,
+      createUid: formModel.createUid,
+      disabled: formModel.disabled
+    };
+    // 调用新增接口
+    addEasEduCategory(params)
+      .then(res => {
+        console.log(res);
+        window.$message?.success('新增成功!');
+  			emit('searchCondition')
+        closeModal();
+      })
+      .catch(error => {
+        console.error(error);
+        window.$message?.error('新增失败!');
+      });
+  } else {
+	   const currentTime = dayjs().format('YYYY-MM-DD HH:mm:ss');
+ 		 handleUpdateFormModel({ modifyTime: currentTime });
+     const params: UpdateEasEduCategoryParams = {
+				id: props.editData?.id, // 编辑数据的id
+				name: formModel.name,
+				description: formModel.description,
+				createTime: formModel.createTime,
+				modifyTime: formModel.modifyTime,
+				createUid: formModel.createUid,
+				disabled: formModel.disabled
+     };
+    // 调用更新接口
+    updateEasEduCategory(params)
+      .then(res => {
+        console.log(res);
+        window.$message?.success('更新成功!');
+				emit('searchCondition')
+        closeModal();
+      })
+  }
+}
+watch(
+	() => props.visible,
+	newValue => {
+		if (newValue) {
+			handleUpdateFormModelByModalType();
+		}
+	}
+);
+</script>
+

+ 181 - 0
src/views/management/sort/crud.tsx

@@ -0,0 +1,181 @@
+import type { AddReq, CreateCrudOptionsRet, DelReq, EditReq, UserPageQuery, UserPageRes } from '@fast-crud/fast-crud';
+import { dict } from '@fast-crud/fast-crud';
+import dayjs from 'dayjs';
+import * as api from './api';
+import { selectAll_1 } from './api';
+export default function createCrudOptions(): CreateCrudOptionsRet {
+	const pageRequest = async (query: any): Promise<any> => {
+    // 调用你自己的函数从接口获取数据
+    const data = await selectAll_1(query);
+    return {
+      rows: data,
+      total: data.length
+    };
+  };
+  const editRequest = async (ctx: EditReq) => {
+    const { form, row } = ctx;
+    form.id = row.id;
+    return api.UpdateObj(form);
+  };
+  const delRequest = async (ctx: DelReq) => {
+    const { row } = ctx;
+    return api.DelObj(row.id);
+  };
+
+  const addRequest = async (req: AddReq) => {
+    const { form } = req;
+    return api.AddObj(form);
+  };
+  return {
+    crudOptions: {
+      container: {
+        is: 'fs-layout-card'
+      },
+      request: {
+        pageRequest,
+        addRequest,
+        editRequest,
+        delRequest
+      },
+      columns: {
+        id: {
+          title: 'ID',
+          key: 'id',
+          type: 'number',
+					search: { show: true },
+          column: {
+            width: 50,
+						align:'center',
+          },
+          form: {
+            show: false
+          }
+        },
+				name: {
+          title: '部门名称',
+					search: { show: true },
+          key: 'name',
+          type: 'text',
+          column: {
+            width: 150,
+						align:'center',
+          },
+          form: {
+            show: true
+          }
+        },
+        // select: {
+        //   title: '状态',
+        //   search: { show: true },
+        //   type: 'dict-select',
+				// 	align: 'center',
+        //   // dict: dict({
+        //   //   url: '/mock/crud/demo/dict'
+        //   // })
+        // },
+        description: {
+          title: '部门地址',
+					key: 'description',
+          type: 'text',
+          search: { show: true },
+					column: {
+            width: 200,
+						align:'center',
+          },
+        },
+        createTime: {
+					key: 'createTime',
+          title: '创建时间',
+					type: 'text',
+					column: {
+            width: 250,
+						align:'center',
+          },
+					search: { show: true },
+          // naive 默认仅支持数字类型时间戳作为日期输入与输出
+          // 字符串类型的时间需要转换格式
+          valueBuilder(context) {
+            const { value, row, key } = context;
+            if (value) {
+              // naive 默认仅支持时间戳作为日期输入与输出
+              row[key] = dayjs(value).valueOf();
+            }
+          },
+          valueResolve(context) {
+            const { value, form, key } = context;
+            if (value) {
+              form[key] = dayjs(value).format('YYYY-MM-DD HH:mm:ss');
+            }
+          }
+        },
+        modifyTime: {
+          title: '修改时间',
+					key: 'modifyTime',
+					type: 'text',
+					align: 'center',
+					column: {
+            width: 250,
+						align:'center',
+          },
+          // naive 默认仅支持数字类型时间戳作为日期输入与输出
+          // 字符串类型的时间需要转换格式
+          valueBuilder(context) {
+            const { value, row, key } = context;
+            if (value) {
+              // naive 默认仅支持时间戳作为日期输入与输出
+              row[key] = dayjs(value).valueOf();
+            }
+          },
+          valueResolve(context) {
+            const { value, form, key } = context;
+            if (value) {
+              form[key] = dayjs(value).format('YYYY-MM-DD HH:mm:ss');
+            }
+          }
+        },
+				createUid: {
+          title: '创建用户ID',
+					key: 'createUid',
+          type: 'file-uploader',
+					column: {
+            width: 250,
+						align:'center',
+          },
+        },
+				disabled: {
+					key: 'disabled',
+					title: '状态',
+					type: 'dict-select',
+					column: {
+            width: 100,
+						align:'center',
+          },
+					dict: dict({
+            url: '/mock/crud/demo/dict'
+          }),
+          search: { show: true }
+        },
+        // richtext: {
+        //   title: '富文本',
+        //   type: 'editor-wang5',
+        //   column: {
+        //     // cell中不显示
+        //     show: false
+        //   },
+        //   form: {
+        //     col: {
+        //       // 横跨两列
+        //       span: 24
+        //     },
+        //     component: {
+        //       style: {
+        //         height: '300px'
+        //       }
+        //     }
+        //   }
+        // }
+      },
+
+    }
+  };
+}

+ 260 - 0
src/views/management/sort/index.vue

@@ -0,0 +1,260 @@
+<template>
+	<div class="h-full overflow-hidden">
+		<n-scrollbar>
+			<n-card title="课程分类" :bordered="false" class="rounded-16px shadow-sm">
+				<n-space class="pb-14px" justify="space-between">
+					<!-- 加入查询组件 -->
+					<n-space>
+						<n-button type="primary" @click="addTableData">
+							<icon-ic-round-plus class="mr-4px text-20px" />
+							新增
+						</n-button>
+						<n-button type="error" @click="deleteTableData">
+							<icon-ic-round-delete class="mr-4px text-20px" />
+							删除
+						</n-button>
+						<!-- 添加查询按钮 -->
+						<n-button type="primary" @click="">
+							<icon-simple-line-icons:magnifier class="mr-4px text-16px" />
+							查询
+						</n-button>
+						<div class="mr-5px"></div>
+						<n-input-group>
+							<n-input-number :style="{ width: '98%', marginRight: '2%' }" v-model:value="conditionParams.id"
+								placeholder="请输入ID..." clearable />
+							<n-input :style="{ width: '98%', marginRight: '2%' }" :value="conditionParams.name"
+								@input="event => conditionParams.name = event" placeholder="请输入学科名称..." clearable />
+							<n-input :style="{ width: '98%', marginRight: '2%' }" :value="conditionParams.description"
+								@input="event => conditionParams.description = event" placeholder="请输入学科描述..." clearable />
+							<n-input-number :style="{ width: '120%', marginRight: '2%' }" v-model:value="conditionParams.createUid"
+								placeholder="请输入用户ID..." clearable />
+							<n-input :style="{ width: '98%', marginRight: '2%' }" v-model:value="conditionParams.createTime"
+								@input="event => conditionParams.createTime = event" placeholder="请输入创建时间..." clearable />
+							<n-input :style="{ width: '98%', marginRight: '2%' }" v-model:value="conditionParams.modifyTime"
+								@input="event => conditionParams.modifyTime = event" placeholder="请输入修改时间..." clearable />
+							<n-select :options="userStatusOptions" :style="{ width: '98%', marginRight: '2%' }"
+								v-model:value="conditionParams.disabled" @input="event => conditionParams.disabled = event"
+								placeholder="输入查询状态..." clearable />
+							<n-button type="primary" ghost @click="searchCondition()">
+								搜索
+							</n-button>
+						</n-input-group>
+					</n-space>
+					<n-space align="center" :size="18">
+						<n-button size="small" type="primary" @click="searchCondition">
+							<icon-mdi-refresh class="mr-4px text-16px" :class="{ 'animate-spin': loading }" />
+							刷新表格
+						</n-button>
+					</n-space>
+				</n-space>
+				<n-data-table :columns="columns" :data="tableData" :loading="loading" :row-key="rowKey"
+					@update:checked-row-keys="handleCheck" />
+				<n-pagination  class="flex-justify-end"  v-model:page="pagination.page"  v-model:page-size="pagination.pageSize" :item-count="pagination.itemCount"    show-size-picker :page-sizes="[10, 20, 30, 40]" :on-update:page="(page: number)=>{pagination.page = page; searchCondition()}"  :on-update:page-size="(pageSize: number)=>{pagination.pageSize = pageSize; searchCondition()}" />
+				<table-action-add v-model:visible="visible" :type="modalType" @searchCondition="searchCondition"
+					:pagination="pagination" :edit-data="(editData as SelectByCondition_1Params)" />
+			</n-card>
+		</n-scrollbar>
+	</div>
+		<!-- <div>
+			<PDFView :pdfUrl="tableData" />
+		</div> -->
+</template>
+
+<script setup lang="tsx" >
+import { userStatusOptions } from '@/constants';
+import { ref, Ref, reactive, watch } from 'vue'
+import { NButton, NSpace, NTag, NPopconfirm } from 'naive-ui';
+import type { DataTableColumns, PaginationProps } from 'naive-ui';
+import { useBoolean, useLoading } from '@/hooks';
+import type { DataTableRowKey } from 'naive-ui'
+import TableActionAdd from './components/table-action-add.vue'
+import type { ModalType } from './components/table-action-add.vue';
+import { userStatusLabels } from '@/constants';
+import { deleteById } from '~/src/service/api/sort';
+import { selectByCondition_1 } from '~/src/service/api/sort';
+import type { SelectByCondition_1Params } from '~/src/service/api/sort';
+// import PDFView from "../../.././components/View/pdfPreview.vue"
+type RowData = {
+	key: number
+	id: number
+	name: string
+	description: string
+	createTime: string
+	modifyTime: string
+	createUid: number
+	disabled: string
+}
+const conditionParams: SelectByCondition_1Params = reactive({
+	id: null,
+	name: null,
+	description: null,
+	createUid: null,
+	createTime: null,
+	modifyTime: null,
+	disabled: null
+});
+
+const { loading, startLoading, endLoading } = useLoading(false);
+const { bool: visible, setTrue: openModal } = useBoolean();
+const tableData = ref<SelectByCondition_1Params[]>([]);
+
+const pagination: PaginationProps = ref({
+	page: 1,
+	pageSize: 10,
+	showSizePicker: true,
+	itemCount: 1,
+}).value;
+
+async function searchCondition() {
+	startLoading();
+	const res = await selectByCondition_1(pagination.page as number, pagination.pageSize as number, conditionParams);
+	tableData.value = res.data as [];
+  pagination.itemCount = Number(res.total);
+	endLoading();
+}
+
+function init() {
+	searchCondition();
+}
+// 在条件改变时立即触发搜索
+watch(conditionParams, () => {
+	if (Object.values(conditionParams).every(value => value === '' || value === null)) {
+		// 如果所有条件参数都为空,则查询所有数据
+		searchCondition();
+	}
+	searchCondition();
+});
+// 初始化
+init();
+
+const checkedRowKeysRef = ref<DataTableRowKey[]>([])
+const s = ref<DataTableRowKey[]>([])
+
+const modalType = ref<ModalType>('add' || 'edit');
+
+function setModalType(type: ModalType) {
+	modalType.value = type;
+}
+// 添加
+function addTableData() {
+	openModal();
+	setModalType('add');
+}
+
+const editData = ref<SelectByCondition_1Params | null>(null);
+function setEditData(data: SelectByCondition_1Params | null) {
+	editData.value = data;
+}
+function handleEditTable(rowId: number) {
+	const findItem = tableData.value.find(item => item.id === rowId);
+	setModalType('edit');
+	openModal();
+	if (findItem) {
+		setEditData(findItem);
+	}
+}
+
+function handleDeleteTable(rowId: number) {
+	const rowIdNumber = Number(rowId);
+	deleteById(rowIdNumber).then(() => {
+		searchCondition(); // 删除完成后刷新表格数据
+	})
+
+}
+const rowKey = (row: RowData) => row.id
+function handleCheck(rowKeys: DataTableRowKey[]) {
+	checkedRowKeysRef.value = rowKeys
+}
+async function deleteTableData() {
+	const ids = checkedRowKeysRef.value.filter((item) => !s.value?.includes(item));
+	s.value = checkedRowKeysRef.value
+	if (ids.length !== 0) {
+		for (const id of ids) {
+			await deleteById(id as number).then((res) => {
+			})
+		}
+		window.$message?.success('删除成功!');
+		searchCondition(); // 删除完成后刷新表格数据
+	} else {
+		// 没有选择任何行时的操作
+		window.$message?.error('未选择要删除的行');
+	}
+}
+
+const columns: Ref<DataTableColumns<UserManagement.User>> = ref([
+	{
+		type: 'selection',
+		align: 'center',
+	},
+	{
+		key: 'id',
+		title: "ID",
+		align: 'center',
+
+	},
+	{
+		key: 'name',
+		title: '学科名称',
+		align: 'center'
+	},
+	{
+		key: 'description',
+		title: '学科描述',
+		align: 'center'
+	},
+	{
+		key: 'createTime',
+		title: '创建时间',
+		align: 'center'
+	},
+	{
+		key: 'modifyTime',
+		title: '修改时间',
+		align: 'center'
+	},
+	{
+		key: 'createUid',
+		title: '创建用户ID',
+		align: 'center'
+	},
+	{
+		key: 'disabled',
+		title: '状态',
+		align: 'center',
+
+		render: row => {
+			if (row.disabled) {
+				const tagTypes: Record<UserManagement.UserStatusKey, NaiveUI.ThemeColor> = {
+					'N': 'error',
+					'Y': 'success',
+				};
+				return <NTag type={tagTypes[row.disabled]}>{userStatusLabels[row.disabled]}</NTag>;
+			}
+			return
+		}
+	},
+	{
+		key: 'actions',
+		title: '操作',
+		align: 'center',
+		render: row => {
+			return (
+				<NSpace justify={'center'}>
+					<NButton size={'small'} onClick={() => handleEditTable(row.id)} >
+						编辑
+					</NButton>
+					<NPopconfirm onPositiveClick={() => handleDeleteTable(row.id)}>
+						{{
+							default: () => '确认删除',
+							trigger: () => <NButton size={'small'}>删除</NButton>
+						}}
+					</NPopconfirm>
+					<NButton size={'small'} onClick={() => handleEditTable(row.id)} >
+						查看
+					</NButton>
+				</NSpace>
+			);
+		}
+	},
+])
+</script>

+ 315 - 0
src/views/management/sort/indexcopy.vue

@@ -0,0 +1,315 @@
+<template>
+	<div class="h-full overflow-hidden">
+		<n-card title="课程分类" :bordered="false" class="rounded-16px shadow-sm">
+			<n-space class="pb-12px" justify="space-between">
+				<!-- 加入查询组件 -->
+				<column-search v-if="showSearch" @close="handleCloseSearch" />
+				<n-space v-else>
+					<n-button type="primary" :data="addData" @click="addTableData">
+						<icon-ic-round-plus class="mr-4px text-20px" />
+						新增
+					</n-button>
+					<n-button type="error" @click="deleteTableData" @update:checked-row-keys="handleCheck">
+						<icon-ic-round-delete class="mr-4px text-20px" />
+						删除
+					</n-button>
+					<n-button type="success">
+						<icon-uil:export class="mr-4px text-20px" />
+						导出Excel
+					</n-button>
+					<!-- 添加查询按钮 -->
+					<n-button size="small" type="primary" @click="handleOpenSearch">
+						<icon-simple-line-icons:magnifier class="mr-4px text-16px" />
+						查询
+					</n-button>
+				</n-space>
+				<n-space align="center" :size="18">
+					<n-button size="small" type="primary" @click="getTableData">
+						<icon-mdi-refresh class="mr-4px text-16px" :class="{ 'animate-spin': loading }" />
+						刷新表格
+					</n-button>
+					<column-search v-model:columns="columns" />
+				</n-space>
+			</n-space>
+			<n-data-table :columns="columns" :data="tableData" :loading="loading" :pagination="pagination" :row-key="rowKey"
+				@update:checked-row-keys="handleCheck" />
+			<table-action-add v-model:visible="visible" :type="modalType" :edit-data="editData" />
+		</n-card>
+		<!-- <n-input-group>
+      <n-input :style="{ width: '100%' }" clearable/>
+    </n-input-group>
+		<n-input-group>
+			<span class="line-height-33.99px mr-5px w-70px">创建时间</span>
+      <n-date-picker :style="{ width: '13%' }"/>
+      <n-time-picker :style="{ width: '14.2%' ,marginRight:'2%'}"/>
+			<span class="line-height-33.99px mr-5px w-70px">修改时间</span>
+      <n-date-picker :style="{ width: '13%' }"/>
+      <n-time-picker :style="{ width: '14.2%' ,marginRight:'2%'}"/>
+				<span class="w-50px  line-height-33.99px">状态</span>
+				<n-select  :options="selectOptions" :style="{ width: '21%' ,marginRight:'2%'}"  clearable/>
+				<n-button type="primary" ghost>
+      	  搜索
+    		</n-button>
+    </n-input-group> -->
+	</div>
+</template>
+
+<script lang="tsx" >
+import { defineComponent, ref } from 'vue'
+import { NButton, NSpace, NTag, NPopconfirm } from 'naive-ui';
+import type { DataTableColumns, PaginationProps } from 'naive-ui';
+import { useBoolean, useLoading } from '@/hooks';
+import type { DataTableRowKey } from 'naive-ui'
+import TableActionAdd from './components/table-action-add.vue'
+import type { ModalType } from './components/table-action-add.vue';
+// import {  userStatusLabels } from '@/constants';
+import { userStatusLabels } from '@/constants';
+import { selectAll_1, deleteById } from '~/src/service/api/sort';
+import type { AddEasEduCategoryParams } from '~/src/service/api/sort';
+import ColumnSearch from './components/column-search.vue';
+type RowData = {
+	key: number
+	id: number
+	name: string
+	description: string
+	createTime: string
+	modifyTime: string
+	createUid: number
+	disabled: string
+}
+const { loading, startLoading, endLoading } = useLoading(false);
+const { bool: visible, setTrue: openModal } = useBoolean();
+
+const tableData = ref<any[]>([]);
+
+// const checkedRowKeysRef = ref<DataTableRowKey[]>([])
+const pagination: PaginationProps = ref({
+	page: 1,
+	pageSize: 10,
+	showSizePicker: true,
+	pageSizes: [10, 20, 50],
+	onChange: (page: number) => {
+		// 处理页码变化
+		pagination.page = page;
+	},
+	onUpdatePageSize: (pageSize: number) => {
+		// 处理每页显示数量变化
+		pagination.pageSize = pageSize;
+		pagination.page = 1;
+	}
+}).value;
+
+
+export async function getTableData() {
+	startLoading();
+	selectAll_1().then(res => {
+		setTimeout(() => {
+			tableData.value = res.data as [];
+			endLoading();
+		}, 1000);
+	});
+}
+
+const checkedRowKeysRef = ref<DataTableRowKey[]>([])
+
+async function deleteTableData() {
+	const ids = checkedRowKeysRef.value;
+	// console.log(ids);
+	if (ids.length !== 0) {
+		console.log(ids);
+		for (let id of ids) {
+			// 调用删除接口
+			console.log(id);
+			await deleteById(id as number)
+				.then((res) => {
+					const deleteRes = res.data;
+					if (!deleteRes)
+						console.log(`成功删除课程,ID: ${id}`);
+					window.$message?.success('删除成功!');
+				})
+				.catch((error) => {
+					// 错误处理
+					console.error(`删除课程发生错误,ID: ${id}`, error);
+					window.$message?.error('删除失败!');
+				});
+		}
+		getTableData(); // 删除完成后刷新表格数据
+	} else {
+		// 没有选择任何行时的操作
+		console.warn('未选择要删除的行');
+	}
+}
+
+const createColumns = (): DataTableColumns<UserManagement.User> => [
+	{
+		type: 'selection',
+		align: 'center',
+	},
+	{
+		key: 'id',
+		title: "ID",
+		align: 'center'
+	},
+	{
+		key: 'name',
+		title: '学科名称',
+		align: 'center'
+	},
+	{
+		key: 'description',
+		title: '学科描述',
+		align: 'center'
+	},
+	{
+		key: 'createTime',
+		title: '创建时间',
+		align: 'center'
+	},
+	{
+		key: 'modifyTime',
+		title: '修改时间',
+		align: 'center'
+	},
+	{
+		key: 'createUid',
+		title: '创建用户ID',
+		align: 'center'
+	},
+	{
+		key: 'disabled',
+		title: '状态',
+		align: 'center',
+
+		render: row => {
+			if (row.disabled) {
+				const tagTypes: Record<UserManagement.UserStatusKey, NaiveUI.ThemeColor> = {
+					'N': 'error',
+					'Y': 'success',
+				};
+				return <NTag type={tagTypes[row.disabled]}>{userStatusLabels[row.disabled]}</NTag>;
+			}
+			return
+		}
+	},
+	{
+		key: 'actions',
+		title: '操作',
+		align: 'center',
+		render: row => {
+			return (
+				<NSpace justify={'center'}>
+					<NButton size={'small'} onClick={() => handleEditTable(row.id)} >
+						编辑
+					</NButton>
+					<NPopconfirm onPositiveClick={() => handleDeleteTable(row.id)}>
+						{{
+							default: () => '确认删除',
+							trigger: () => <NButton size={'small'}>删除</NButton>
+						}}
+					</NPopconfirm>
+				</NSpace>
+			);
+		}
+
+	},
+]
+
+const modalType = ref<ModalType>('add' || 'edit');
+
+function setModalType(type: ModalType) {
+	modalType.value = type;
+	// console.log(modalType.value);
+}
+
+const addData = ref<AddEasEduCategoryParams[]>([]);
+function addTableData() {
+	openModal();
+	setModalType('add');
+	getTableData(); // 删除完成后刷新表格数据
+	// const params: AddEasEduCategoryParams = {};
+	// addEasEduCategory(params).then(res => {
+	// 	console.log(res);
+	// 	addData.value = res.data as [];
+	// });
+}
+
+const editData = ref<UserManagement.User | null>(null);
+function setEditData(data: UserManagement.User | null) {
+	editData.value = data;
+}
+console.log(tableData.value);
+function handleEditTable(rowId: number | null) {
+	const findItem = tableData.value.find(item => item.id === rowId);
+	setModalType('edit');
+	openModal();
+	if (findItem) {
+		setEditData(findItem);
+	}
+	// console.log(findItem);
+	getTableData(); // 编辑完成后刷新表格数据
+}
+
+function handleDeleteTable(rowId: number | null) {
+	const findItem = tableData.value.find(item => item.id === rowId);
+	if (findItem) {
+		deleteById(findItem.id).then((r) => {
+			console.log(r);
+			getTableData(); // 编辑完成后刷新表格数据
+		})
+	}
+}
+
+const showSearch = ref(false)
+
+function handleOpenSearch() {
+	// 点击查询时显示搜索框
+	showSearch.value = true;
+}
+function handleCloseSearch() {
+	// 关闭搜索框,并重新显示主页面
+	showSearch.value = false;
+}
+
+
+function init() {
+	getTableData();
+}
+
+// 初始化
+init();
+
+export default defineComponent({
+	components: {
+		TableActionAdd,
+		ColumnSearch
+	},
+	setup() {
+		// const checkedRowKeysRef = ref<DataTableRowKey[]>([])
+		return {
+			showSearch,
+			tableData,
+			addData,
+			loading,
+			modalType,
+			setEditData,
+			editData,
+			handleCloseSearch,
+			getTableData,
+			addTableData,
+			setModalType,
+			deleteTableData,
+			handleEditTable,
+			handleOpenSearch,
+			visible,
+			columns: createColumns(),
+			checkedRowKeys: checkedRowKeysRef,
+			pagination,
+			rowKey: (row: RowData) => row.id,
+			handleCheck(rowKeys: DataTableRowKey[]) {
+				checkedRowKeysRef.value = rowKeys
+				console.log(checkedRowKeysRef.value);
+			}
+		}
+	}
+})
+</script>

+ 97 - 99
src/views/management/user/components/table-action-modal.vue

@@ -1,150 +1,148 @@
 <template>
-  <n-modal v-model:show="modalVisible" preset="card" :title="title" class="w-700px">
-    <n-form ref="formRef" label-placement="left" :label-width="80" :model="formModel" :rules="rules">
-      <n-grid :cols="24" :x-gap="18">
-        <n-form-item-grid-item :span="12" label="用户名" path="userName">
-          <n-input v-model:value="formModel.userName" />
-        </n-form-item-grid-item>
-        <n-form-item-grid-item :span="12" label="年龄" path="age">
-          <n-input-number v-model:value="formModel.age" clearable />
-        </n-form-item-grid-item>
-        <n-form-item-grid-item :span="12" label="性别" path="gender">
-          <n-radio-group v-model:value="formModel.gender">
-            <n-radio v-for="item in genderOptions" :key="item.value" :value="item.value">{{ item.label }}</n-radio>
-          </n-radio-group>
-        </n-form-item-grid-item>
-        <n-form-item-grid-item :span="12" label="手机号" path="phone">
-          <n-input v-model:value="formModel.phone" />
-        </n-form-item-grid-item>
-        <n-form-item-grid-item :span="12" label="邮箱" path="email">
-          <n-input v-model:value="formModel.email" />
-        </n-form-item-grid-item>
-        <n-form-item-grid-item :span="12" label="状态" path="userStatus">
-          <n-select v-model:value="formModel.userStatus" :options="userStatusOptions" />
-        </n-form-item-grid-item>
-      </n-grid>
-      <n-space class="w-full pt-16px" :size="24" justify="end">
-        <n-button class="w-72px" @click="closeModal">取消</n-button>
-        <n-button class="w-72px" type="primary" @click="handleSubmit">确定</n-button>
-      </n-space>
-    </n-form>
-  </n-modal>
+	<n-modal v-model:show="modalVisible" preset="card" :title="title" class="w-700px">
+		<n-form ref="formRef" label-placement="left" :label-width="100" :model="formModel" :rules="rules">
+			<n-grid :cols="24" :x-gap="18">
+				<n-form-item-grid-item :span="12" label="学科名称" path="name">
+					<n-input v-model:value="formModel.name" clearable />
+				</n-form-item-grid-item>
+				<n-form-item-grid-item :span="12" label="学科描述" path="description">
+					<n-input v-model:value="formModel.description" clearable />
+				</n-form-item-grid-item>
+				<n-form-item-grid-item :span="12" label="创建用户ID" path="createUid">
+					<n-input-number v-model:value="formModel.createUid" />
+				</n-form-item-grid-item>
+				<n-form-item-grid-item :span="12" label="状态" path="disabled">
+					<n-select v-model:value="formModel.disabled" :options="userStatusOptions" />
+				</n-form-item-grid-item>
+			</n-grid>
+			<n-space class="w-full pt-16px" :size="24" justify="end">
+				<n-button class="w-72px" @click="closeModal">取消</n-button>
+				<n-button class="w-72px" type="primary" @click="handleSubmit">确定</n-button>
+			</n-space>
+		</n-form>
+	</n-modal>
 </template>
 
 <script setup lang="ts">
 import { ref, computed, reactive, watch } from 'vue';
 import type { FormInst, FormItemRule } from 'naive-ui';
-import { genderOptions, userStatusOptions } from '@/constants';
-import { formRules, createRequiredFormRule } from '@/utils';
+import { userStatusOptions } from '@/constants';
+import {createRequiredFormRule } from '@/utils';
+import { addEasEduCategory, AddEasEduCategoryParams } from '~/src/service/api/sort';
 
 export interface Props {
-  /** 弹窗可见性 */
-  visible: boolean;
-  /**
-   * 弹窗类型
-   * add: 新增
-   * edit: 编辑
-   */
-  type?: 'add' | 'edit';
-  /** 编辑的表格行数据 */
-  editData?: UserManagement.User | null;
+	/** 弹窗可见性 */
+	visible: boolean;
+	/**
+	 * 弹窗类型
+	 * add: 新增
+	 * edit: 编辑
+	 */
+	type?: 'add' | 'edit' ;
+	/** 编辑的表格行数据 */
+	editData?: UserManagement.User | null;
 }
 
 export type ModalType = NonNullable<Props['type']>;
 
-defineOptions({ name: 'TableActionModal' });
+defineOptions({ name: 'TableActionAdd' });
 
 const props = withDefaults(defineProps<Props>(), {
-  type: 'add',
-  editData: null
+	type: 'add',
+	editData: null,
 });
 
 interface Emits {
-  (e: 'update:visible', visible: boolean): void;
+	(e: 'update:visible', visible: boolean): void;
 }
-
 const emit = defineEmits<Emits>();
 
 const modalVisible = computed({
-  get() {
-    return props.visible;
-  },
-  set(visible) {
-    emit('update:visible', visible);
-  }
+	get() {
+		return props.visible;
+	},
+	set(visible) {
+		emit('update:visible', visible);
+	}
 });
 const closeModal = () => {
-  modalVisible.value = false;
+	modalVisible.value = false;
 };
 
 const title = computed(() => {
-  const titles: Record<ModalType, string> = {
-    add: '添加用户',
-    edit: '编辑用户'
-  };
-  return titles[props.type];
+	const titles: Record<ModalType, string> = {
+		add: '添加用户',
+		edit: '编辑用户',
+	};
+	return titles[props.type];
 });
 
 const formRef = ref<HTMLElement & FormInst>();
 
-type FormModel = Pick<UserManagement.User, 'userName' | 'age' | 'gender' | 'phone' | 'email' | 'userStatus'>;
+type FormModel = Pick<UserManagement.User,  | 'name' | 'description' |  'createUid' | 'disabled'>;
 
 const formModel = reactive<FormModel>(createDefaultFormModel());
 
 const rules: Record<keyof FormModel, FormItemRule | FormItemRule[]> = {
-  userName: createRequiredFormRule('请输入用户名'),
-  age: createRequiredFormRule('请输入年龄'),
-  gender: createRequiredFormRule('请选择性别'),
-  phone: formRules.phone,
-  email: formRules.email,
-  userStatus: createRequiredFormRule('请选择用户状态')
-};
+	name: createRequiredFormRule('请输入学科名称'),
+	description: createRequiredFormRule('请进行部门描述'),
+	createUid: createRequiredFormRule('请输入创建用户ID'),
+	disabled: createRequiredFormRule('请选择用户状态')
 
+};
 function createDefaultFormModel(): FormModel {
-  return {
-    userName: '',
-    age: null,
-    gender: null,
-    phone: '',
-    email: null,
-    userStatus: null
-  };
+	return {
+		name: '',
+		description: '',
+		createUid:null,
+		disabled: null,
+	};
 }
 
 function handleUpdateFormModel(model: Partial<FormModel>) {
-  Object.assign(formModel, model);
+	Object.assign(formModel, model);
 }
 
 function handleUpdateFormModelByModalType() {
-  const handlers: Record<ModalType, () => void> = {
-    add: () => {
-      const defaultFormModel = createDefaultFormModel();
-      handleUpdateFormModel(defaultFormModel);
-    },
-    edit: () => {
-      if (props.editData) {
-        handleUpdateFormModel(props.editData);
-      }
-    }
-  };
-
-  handlers[props.type]();
+	const handlers: Record<ModalType, () => void> = {
+		add: () => {
+			const defaultFormModel = createDefaultFormModel();
+			handleUpdateFormModel(defaultFormModel);
+		},
+		edit: () => {
+			if (props.editData) {
+				handleUpdateFormModel(props.editData);
+			}
+		},
+	};
+	handlers[props.type]();
 }
 
 async function handleSubmit() {
   await formRef.value?.validate();
-  window.$message?.success('新增成功!');
-  closeModal();
+  const params: AddEasEduCategoryParams = {
+    name: formModel.name,
+    description: formModel.description,
+    createUid: formModel.createUid,
+    disabled: formModel.disabled
+  };
+  addEasEduCategory(params)
+    .then(res => {
+      window.$message?.success('新增成功!');
+      closeModal();
+    })
+    .catch(error => {
+      console.error(error);
+      window.$message?.error('新增失败!');
+    });
 }
-
 watch(
-  () => props.visible,
-  newValue => {
-    if (newValue) {
-      handleUpdateFormModelByModalType();
-    }
-  }
+	() => props.visible,
+	newValue => {
+		if (newValue) {
+			handleUpdateFormModelByModalType();
+		}
+	}
 );
 </script>
-
 <style scoped></style>

+ 183 - 46
src/views/management/user/index.vue

@@ -1,73 +1,137 @@
 <template>
 	<div class="h-full overflow-hidden">
-		<n-card title="权限管理" :bordered="false" class="rounded-16px shadow-sm">
-			<n-data-table :columns="columns" :data="tableData" :pagination="pagination" />
+		<n-card title="用户管理" :bordered="false" class="rounded-16px shadow-sm">
+			<n-space class="pb-12px" justify="space-between">
+				<n-space>
+					<n-button type="primary" @click="addTableData">
+						<icon-ic-round-plus class="mr-4px text-20px" />
+						新增
+					</n-button>
+					<n-button type="error" @click="deleteTableData">
+						<icon-ic-round-delete class="mr-4px text-20px" />
+						删除
+					</n-button>
+					<n-button type="success">
+						<icon-uil:export class="mr-4px text-20px" />
+						导出Excel
+					</n-button>
+				</n-space>
+				<n-space align="center" :size="18">
+					<n-button size="small" type="primary" @click="getTableData">
+						<icon-mdi-refresh class="mr-4px text-16px" :class="{ 'animate-spin': loading }" />
+						刷新表格
+					</n-button>
+					<column-setting v-model:columns="columns" />
+				</n-space>
+			</n-space>
+			<n-data-table :columns="columns" :data="tableData" :loading="loading" :pagination="pagination" :row-key="rowKey"
+				@update:checked-row-keys="handleCheck"/>
+			<table-action-modal v-model:visible="visible" :type="modalType" :edit-data="editData" />
 		</n-card>
 	</div>
 </template>
 
-<script setup lang="ts">
-import { ref } from 'vue';
+<script setup lang="tsx">
+import {  ref } from 'vue';
 import type { Ref } from 'vue';
-
+import { NButton, NPopconfirm, NSpace, NTag } from 'naive-ui';
 import type { DataTableColumns, PaginationProps } from 'naive-ui';
+import {  userStatusLabels } from '@/constants';
+// import { fetchUserList } from '@/service';
+import { useBoolean, useLoading } from '@/hooks';
+import type { DataTableRowKey } from 'naive-ui'
+import TableActionModal from './components/table-action-modal.vue';
+import type { ModalType } from './components/table-action-modal.vue';
+import ColumnSetting from './components/column-setting.vue';
+import { selectAll_1,deleteById } from '~/src/service/api/sort';
+// import type { AddEasEduCategoryParams } from '~/src/service/api/sort';
+const { loading, startLoading, endLoading } = useLoading(false);
+const { bool: visible, setTrue: openModal } = useBoolean();
+
+const tableData = ref<UserManagement.User[]>([]);
+// function setTableData(data: UserManagement.User[]) {
+// 	tableData.value = data;
+// }
+
+ async function getTableData() {
+	startLoading();
+	selectAll_1().then(res => {
+		setTimeout(() => {
+			// console.log(res);
+			tableData.value = res.data as [];
+			endLoading();
+		}, 1000);
+	});
+}
+
 
-import { query_1 } from '~/src/service/api/user';
-import type { Query_1Params } from '~/src/service/api/user';
+type RowData = {
+	key: number
+	id: number
+	name: string
+	description: string
+	createTime: string
+	modifyTime: string
+	createUid: number
+	disabled: string
+}
 
-const tableData = ref<Query_1Params[]>([]);
+const rowKey= (row: RowData) => row.id
 
+function	handleCheck(rowKeys: DataTableRowKey[]) {
+	checkedRowKeysRef.value = rowKeys
+	console.log(checkedRowKeysRef.value);
+}
 
-async function getTableData() {
-	const pageNum = pagination.page as number;
-	const pageSize = pagination.pageSize as number;
 
-	const params: Query_1Params = {
-		depname: '',
-		address: '',
-		phone: '',
-		email: '',
-		manager: '',
-		createTime: "",
-		modifyTime: "" ,
-		createUid: 0,
-		disabled: '',
-	};
+const checkedRowKeysRef = ref<DataTableRowKey[]>([])
 
-	query_1(pageNum, pageSize, params).then((res) => {
-			console.log(res);
-			tableData.value =  res.data as[]
-		})
+async function deleteTableData() {
+  const ids = checkedRowKeysRef.value;
+	// console.log(ids);
+  if (ids.length !== 0) {
+		console.log(ids);
+    for (let id of ids) {
+      // 调用删除接口
+			console.log(id);
+      await deleteById(id as number)
+        .then((res) => {
+          const deleteRes = res.data;
+					if(!deleteRes)
+            console.log(`成功删除课程,ID: ${id}`);
+            window.$message?.success('删除成功!');
+        })
+        .catch((error) => {
+          // 错误处理
+          console.error(`删除课程发生错误,ID: ${id}`, error);
+          window.$message?.error('删除失败!');
+        });
+    }
+    getTableData(); // 删除完成后刷新表格数据
+  } else {
+    // 没有选择任何行时的操作
+    console.warn('未选择要删除的行');
+  }
 }
 
-const columns: Ref<DataTableColumns<Query_1Params>> = ref([
+const columns: Ref<DataTableColumns<UserManagement.User>> = ref([
 	{
 		type: 'selection',
-		align: 'center'
-	},
-	{
-		key: 'depname',
-		title: '部门名称',
-		align: 'center'
+		align: 'center',
 	},
 	{
-		key: 'address',
-		title: '部门地址',
+		key: 'id',
+		title: "ID",
 		align: 'center'
 	},
 	{
-		key: 'phone',
-		title: '部门电话',
+		key: 'name',
+		title: '学科名称',
 		align: 'center'
 	},
 	{
-		key: 'email',
-		title: '部门电子邮箱',
-		align: 'center',
-	},
-	{
-		key: 'manager',
-		title: '部门负责人',
+		key: 'description',
+		title: '学科描述',
 		align: 'center'
 	},
 	{
@@ -88,21 +152,92 @@ const columns: Ref<DataTableColumns<Query_1Params>> = ref([
 	{
 		key: 'disabled',
 		title: '状态',
-		align: 'center'
+		align: 'center',
+		render: row => {
+			if (row.disabled) {
+				const tagTypes: Record<UserManagement.UserStatusKey, NaiveUI.ThemeColor> = {
+					'Y': 'success',
+					'N': 'error',
+				};
+
+				return <NTag type={tagTypes[row.disabled]}>{userStatusLabels[row.disabled]}</NTag>;
+			}
+			return <span></span>;
+		}
 	},
+	{
+		key: 'actions',
+		title: '操作',
+		align: 'center',
+		render: row => {
+			return (
+				<NSpace justify={'center'}>
+					<NButton size={'small'} onClick={() => handleEditTable(row.id)}>
+						编辑
+					</NButton>
+					<NPopconfirm onPositiveClick={() => handleDeleteTable(row.id)}>
+						{{
+							default: () => '确认删除',
+							trigger: () => <NButton size={'small'}>删除</NButton>
+						}}
+					</NPopconfirm>
+				</NSpace>
+			);
+		}
+	}
+]) as Ref<DataTableColumns<UserManagement.User>>;
+
+const modalType = ref<ModalType>('add');
+
+function setModalType(type: ModalType) {
+	modalType.value = type;
+}
+
+function addTableData() {
+	openModal();
+	getTableData(); // 删除完成后刷新表格数据
+	setModalType('add');
+}
+
+
+const editData = ref<UserManagement.User | null>(null);
 
-]) as Ref<DataTableColumns<Query_1Params>>;
+function setEditData(data: UserManagement.User | null) {
+	editData.value = data;
+}
+
+// function handleAddTable() {
+// 	openModal();
+// 	setModalType('add');
+// }
+
+function handleEditTable(rowId: string) {
+	const findItem = tableData.value.find(item => item.id === rowId);
+	if (findItem) {
+		setEditData(findItem);
+	}
+	setModalType('edit');
+	openModal();
+}
+
+function handleDeleteTable(rowId: string) {
+	window.$message?.info(`点击了删除,rowId为${rowId}`);
+}
 
 const pagination: PaginationProps = ref({
 	page: 1,
+
 	pageSize: 10,
 	showSizePicker: true,
 	pageSizes: [10, 20, 50],
 	onChange: (page: number) => {
 		// 处理页码变化
+		pagination.page = page;
 	},
 	onUpdatePageSize: (pageSize: number) => {
 		// 处理每页显示数量变化
+		pagination.pageSize = pageSize;
+		pagination.page = 1;
 	}
 }).value;
 
@@ -113,3 +248,5 @@ function init() {
 // 初始化
 init();
 </script>
+
+<style scoped></style>

+ 0 - 127
src/views/management/user/indexCopy.vue

@@ -1,127 +0,0 @@
-<template>
-	<div class="h-full overflow-hidden">
-		<n-card title="权限管理" :bordered="false" class="rounded-16px shadow-sm">
-			<n-data-table :columns="columns" :data="tableData" :pagination="pagination" />
-		</n-card>
-	</div>
-</template>
-
-<script setup lang="tsx">
-import { ref } from 'vue';
-import type { Ref } from 'vue';
-
-import type { DataTableColumns, PaginationProps } from 'naive-ui';
-
-import { query_1 } from '~/src/service/api/user';
-import type { Query_1Params } from '~/src/service/api/user';
-
-const tableData = ref<Query_1Params[]>([]);
-
-function setTableData(data: Query_1Params[]) {
-	tableData.value = data;
-}
-
-async function getTableData() {
-	const pageNum = pagination.page as number;
-	const pageSize = pagination.pageSize as number;
-
-	const params: Query_1Params = {
-		depname: '',
-		address: '',
-		phone: '',
-		email: '',
-		manager: '',
-		createTime: "",
-		modifyTime: "" ,
-		createUid: 0,
-		disabled: '',
-	};
-
-	query_1(pageNum, pageSize, params)
-		.then(response => {
-			console.log(response);
-
-			const data: Query_1Params[] = response.data as Query_1Params[]; // 使用类型断言
-			setTimeout(() => {
-				setTableData(data);
-			}, 1000);
-		})
-		.catch(error => {
-			// 处理错误情况
-		});
-
-}
-
-const columns: Ref<DataTableColumns<Query_1Params>> = ref([
-	{
-		type: 'selection',
-		align: 'center'
-	},
-	{
-		key: 'depname',
-		title: '部门名称',
-		align: 'center'
-	},
-	{
-		key: 'address',
-		title: '部门地址',
-		align: 'center'
-	},
-	{
-		key: 'phone',
-		title: '部门电话',
-		align: 'center'
-	},
-	{
-		key: 'email',
-		title: '部门电子邮箱',
-		align: 'center',
-	},
-	{
-		key: 'manager',
-		title: '部门负责人',
-		align: 'center'
-	},
-	{
-		key: 'createTime',
-		title: '创建时间',
-		align: 'center'
-	},
-	{
-		key: 'modifyTime',
-		title: '修改时间',
-		align: 'center'
-	},
-	{
-		key: 'createUid',
-		title: '创建用户ID',
-		align: 'center'
-	},
-	{
-		key: 'disabled',
-		title: '状态',
-		align: 'center'
-	},
-
-]) as Ref<DataTableColumns<Query_1Params>>;
-
-const pagination: PaginationProps = ref({
-	page: 1,
-	pageSize: 10,
-	showSizePicker: true,
-	pageSizes: [10, 20, 50],
-	onChange: (page: number) => {
-		// 处理页码变化
-	},
-	onUpdatePageSize: (pageSize: number) => {
-		// 处理每页显示数量变化
-	}
-}).value;
-
-function init() {
-	getTableData();
-}
-
-// 初始化
-init();
-</script>

Some files were not shown because too many files changed in this diff