wuheng 1 ano atrás
pai
commit
dddaf3f0b7
43 arquivos alterados com 1171 adições e 698 exclusões
  1. 1 0
      package.json
  2. 103 3
      pnpm-lock.yaml
  3. 17 12
      src/App.vue
  4. 1 0
      src/assets/svg-icon/academic-cap.svg
  5. 3 1
      src/locales/lang/en.ts
  6. 3 2
      src/locales/lang/zh-cn.ts
  7. 2 0
      src/main.ts
  8. 1 0
      src/plugins/assets.ts
  9. 44 3
      src/plugins/fast-crud/index.tsx
  10. 11 0
      src/router/modules/management.ts
  11. 0 1
      src/router/routes/index.ts
  12. 28 17
      src/service/api/auth.ts
  13. 5 4
      src/service/request/index.ts
  14. 11 12
      src/service/request/instance.ts
  15. 5 7
      src/store/modules/auth/helpers.ts
  16. 18 9
      src/store/modules/auth/index.ts
  17. 0 4
      src/store/modules/route/index.ts
  18. 6 0
      src/styles/css/global.css
  19. 2 0
      src/typings/page-route.d.ts
  20. 5 6
      src/typings/system.d.ts
  21. 13 16
      src/utils/crypto/index.ts
  22. 11 0
      src/utils/form/date.ts
  23. 1 0
      src/utils/form/index.ts
  24. 0 1
      src/utils/router/cache.ts
  25. 1 1
      src/utils/router/regexp.ts
  26. 4 4
      src/utils/storage/local.ts
  27. 3 3
      src/utils/storage/session.ts
  28. 0 0
      src/views/_builtin/login/components/pwd-login/components/validate/Verify.vue
  29. 0 0
      src/views/_builtin/login/components/pwd-login/components/validate/Verify/VerifyPoints.vue
  30. 583 0
      src/views/_builtin/login/components/pwd-login/components/validate/Verify/VerifySlide.vue
  31. 24 0
      src/views/_builtin/login/components/pwd-login/components/validate/api/index.js
  32. 0 0
      src/views/_builtin/login/components/pwd-login/components/validate/utils/ase.js
  33. 0 0
      src/views/_builtin/login/components/pwd-login/components/validate/utils/axios.js
  34. 0 0
      src/views/_builtin/login/components/pwd-login/components/validate/utils/util.js
  35. 0 549
      src/views/_builtin/login/components/pwd-login/components/verifition/Verify/VerifySlide.vue
  36. 0 27
      src/views/_builtin/login/components/pwd-login/components/verifition/api/index.js
  37. 41 15
      src/views/_builtin/login/components/pwd-login/index.vue
  38. 3 1
      src/views/_builtin/login/index.vue
  39. 1 0
      src/views/index.ts
  40. 53 0
      src/views/management/student/api.ts
  41. 149 0
      src/views/management/student/crud.ts
  42. 17 0
      src/views/management/student/index.vue
  43. 1 0
      tsconfig.json

+ 1 - 0
package.json

@@ -65,6 +65,7 @@
     "@fast-crud/ui-naive": "^1.13.6",
     "@soybeanjs/vue-materials": "^0.1.9",
     "@vueuse/core": "^10.1.2",
+    "ant-design-vue": "4.0.0-rc.6",
     "axios": "1.4.0",
     "clipboard": "^2.0.11",
     "colord": "^2.9.3",

+ 103 - 3
pnpm-lock.yaml

@@ -40,6 +40,9 @@ dependencies:
   '@vueuse/core':
     specifier: ^10.1.2
     version: 10.1.2(vue@3.3.4)
+  ant-design-vue:
+    specifier: 4.0.0-rc.6
+    version: 4.0.0-rc.6(vue@3.3.4)
   axios:
     specifier: 1.4.0
     version: 1.4.0
@@ -2716,6 +2719,14 @@ packages:
     resolution: {integrity: sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==}
     dev: true
 
+  /@emotion/hash@0.9.1:
+    resolution: {integrity: sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==}
+    dev: false
+
+  /@emotion/unitless@0.8.1:
+    resolution: {integrity: sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==}
+    dev: false
+
   /@esbuild-kit/cjs-loader@2.4.2:
     resolution: {integrity: sha512-BDXFbYOJzT/NBEtp71cvsrGPwGAMGRB/349rwKuoxNSiKjPraNNnlK6MIIabViCjqZugu6j+xeMDlEkWdHHJSg==}
     dependencies:
@@ -3410,6 +3421,13 @@ packages:
     engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
     dev: true
 
+  /@simonwep/pickr@1.8.2:
+    resolution: {integrity: sha512-/l5w8BIkrpP6n1xsetx9MWPWlU6OblN5YgZZphxan0Tq4BByTCETL6lyIeY8lagalS2Nbt4F2W034KHLIiunKA==}
+    dependencies:
+      core-js: 3.30.1
+      nanopop: 2.3.0
+    dev: false
+
   /@sindresorhus/is@5.3.0:
     resolution: {integrity: sha512-CX6t4SYQ37lzxicAqsBtxA3OseeoVrh9cSJ5PFYam0GksYlupRfy1A+Q4aYD3zvcfECLc0zO2u+ZnR2UYKvCrw==}
     engines: {node: '>=14.16'}
@@ -4781,6 +4799,37 @@ packages:
     engines: {node: '>=12'}
     dev: true
 
+  /ant-design-vue@4.0.0-rc.6(vue@3.3.4):
+    resolution: {integrity: sha512-j+GAhgC1p1+nmQVbaEeY2miZ1h+8jBLlxTESX93MwcshVaYTkZNhiyddtl92VvEDJTedzuX+1oT3TP5wG/+tHg==}
+    engines: {node: '>=12.22.0'}
+    peerDependencies:
+      vue: '>=3.2.0'
+    dependencies:
+      '@ant-design/colors': 6.0.0
+      '@ant-design/icons-vue': 6.1.0(vue@3.3.4)
+      '@babel/runtime': 7.21.5
+      '@ctrl/tinycolor': 3.6.0
+      '@emotion/hash': 0.9.1
+      '@emotion/unitless': 0.8.1
+      '@simonwep/pickr': 1.8.2
+      array-tree-filter: 2.1.0
+      async-validator: 4.2.5
+      csstype: 3.1.2
+      dayjs: 1.11.7
+      dom-align: 1.12.4
+      dom-scroll-into-view: 2.0.1
+      lodash: 4.17.21
+      lodash-es: 4.17.21
+      resize-observer-polyfill: 1.5.1
+      scroll-into-view-if-needed: 2.2.31
+      shallow-equal: 1.2.1
+      stylis: 4.3.0
+      throttle-debounce: 5.0.0
+      vue: 3.3.4
+      vue-types: 3.0.2(vue@3.3.4)
+      warning: 4.0.3
+    dev: false
+
   /any-promise@1.3.0:
     resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}
     dev: false
@@ -4846,6 +4895,10 @@ packages:
       is-string: 1.0.7
     dev: true
 
+  /array-tree-filter@2.1.0:
+    resolution: {integrity: sha512-4ROwICNlNw/Hqa9v+rk5h22KjmzB1JGTMVKP2AKJBOCgb0yL0ASf0+YvCcLNNwquOHNX48jkeZIJ3a+oOQqKcw==}
+    dev: false
+
   /array-union@2.1.0:
     resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==}
     engines: {node: '>=8'}
@@ -4936,7 +4989,6 @@ packages:
 
   /async-validator@4.2.5:
     resolution: {integrity: sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==}
-    dev: true
 
   /async@3.2.4:
     resolution: {integrity: sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==}
@@ -6285,6 +6337,14 @@ packages:
       esutils: 2.0.3
     dev: true
 
+  /dom-align@1.12.4:
+    resolution: {integrity: sha512-R8LUSEay/68zE5c8/3BDxiTEvgb4xZTF0RKmAHfiEVN3klfIpXfi2/QCoiWPccVQ0J/ZGdz9OjzL4uJEP/MRAw==}
+    dev: false
+
+  /dom-scroll-into-view@2.0.1:
+    resolution: {integrity: sha512-bvVTQe1lfaUr1oFzZX80ce9KLDlZ3iU+XGNE/bz9HnGdklTieqsbmsLHe+rT2XWqopvL0PckkYqN7ksmm5pe3w==}
+    dev: false
+
   /dom-serializer@0.2.2:
     resolution: {integrity: sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==}
     dependencies:
@@ -8624,6 +8684,11 @@ packages:
       isobject: 3.0.1
     dev: true
 
+  /is-plain-object@3.0.1:
+    resolution: {integrity: sha512-Xnpx182SBMrr/aBik8y+GuR4U1L9FqMSojwDQwPMmxyC6bvEqly9UBCxhauBF5vNh2gwWJNX6oDV7O+OM4z34g==}
+    engines: {node: '>=0.10.0'}
+    dev: false
+
   /is-plain-object@5.0.0:
     resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==}
     engines: {node: '>=0.10.0'}
@@ -8812,7 +8877,6 @@ packages:
 
   /js-tokens@4.0.0:
     resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
-    dev: true
 
   /js-yaml@4.1.0:
     resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
@@ -9213,7 +9277,6 @@ packages:
     hasBin: true
     dependencies:
       js-tokens: 4.0.0
-    dev: true
 
   /lowercase-keys@3.0.0:
     resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==}
@@ -9727,6 +9790,10 @@ packages:
       - supports-color
     dev: true
 
+  /nanopop@2.3.0:
+    resolution: {integrity: sha512-fzN+T2K7/Ah25XU02MJkPZ5q4Tj5FpjmIYq4rvoHX4yb16HzFdCO6JxFFn5Y/oBhQ8no8fUZavnyIv9/+xkBBw==}
+    dev: false
+
   /natural-compare-lite@1.4.0:
     resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==}
     dev: true
@@ -11044,6 +11111,10 @@ packages:
     engines: {node: '>=0.10.0'}
     dev: true
 
+  /resize-observer-polyfill@1.5.1:
+    resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==}
+    dev: false
+
   /resolve-alpn@1.2.1:
     resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==}
     dev: true
@@ -11329,6 +11400,10 @@ packages:
     resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
     dev: false
 
+  /shallow-equal@1.2.1:
+    resolution: {integrity: sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==}
+    dev: false
+
   /shebang-command@2.0.0:
     resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
     engines: {node: '>=8'}
@@ -11874,6 +11949,10 @@ packages:
       inline-style-parser: 0.1.1
     dev: true
 
+  /stylis@4.3.0:
+    resolution: {integrity: sha512-E87pIogpwUsUwXw7dNyU4QDjdgVMy52m+XEOPEKUn161cCzWjjhPSQhByfd1CcNvrOLnXQ6OnnZDwnJrz/Z4YQ==}
+    dev: false
+
   /suf-log@2.5.3:
     resolution: {integrity: sha512-KvC8OPjzdNOe+xQ4XWJV2whQA0aM1kGVczMQ8+dStAO6KfEB140JEVQ9dE76ONZ0/Ylf67ni4tILPJB41U0eow==}
     dependencies:
@@ -12044,6 +12123,11 @@ packages:
       any-promise: 1.3.0
     dev: false
 
+  /throttle-debounce@5.0.0:
+    resolution: {integrity: sha512-2iQTSgkkc1Zyk0MeVrt/3BvuOXYPl/R8Z0U2xxo9rjwNciaHDG3R+Lm6dh4EeUci49DanvBnuqI6jshoQQRGEg==}
+    engines: {node: '>=12.22'}
+    dev: false
+
   /through2@2.0.5:
     resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==}
     dependencies:
@@ -12947,6 +13031,16 @@ packages:
       typescript: 5.0.4
     dev: true
 
+  /vue-types@3.0.2(vue@3.3.4):
+    resolution: {integrity: sha512-IwUC0Aq2zwaXqy74h4WCvFCUtoV0iSWr0snWnE9TnU18S66GAQyqQbRf2qfJtUuiFsBf6qp0MEwdonlwznlcrw==}
+    engines: {node: '>=10.15.0'}
+    peerDependencies:
+      vue: ^3.0.0
+    dependencies:
+      is-plain-object: 3.0.1
+      vue: 3.3.4
+    dev: false
+
   /vue3-pdfjs@0.1.6:
     resolution: {integrity: sha512-7UaWbsp8wNqB0y/rRlyo5yRb0S+XOkkSpmdUuS267Dhi07Pt4RFEetQ8inrpf/aTFJwGnW0Uc/UE4p376s+Zmw==}
     engines: {node: '>=10.0.0'}
@@ -13014,6 +13108,12 @@ packages:
       tslib: 2.5.0
     dev: false
 
+  /warning@4.0.3:
+    resolution: {integrity: sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==}
+    dependencies:
+      loose-envify: 1.4.0
+    dev: false
+
   /web-streams-polyfill@3.2.1:
     resolution: {integrity: sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==}
     engines: {node: '>= 8'}

+ 17 - 12
src/App.vue

@@ -1,12 +1,17 @@
 <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">
@@ -25,10 +30,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>
 

+ 1 - 0
src/assets/svg-icon/academic-cap.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 20 20"><path fill="currentColor" d="M10.394 2.08a1 1 0 0 0-.788 0l-7 3a1 1 0 0 0 0 1.84L5.25 8.051a.999.999 0 0 1 .356-.257l4-1.714a1 1 0 1 1 .788 1.838l-2.727 1.17l1.94.831a1 1 0 0 0 .787 0l7-3a1 1 0 0 0 0-1.838l-7-3ZM3.31 9.397L5 10.12v4.102a8.969 8.969 0 0 0-1.05-.174a1 1 0 0 1-.89-.89a11.115 11.115 0 0 1 .25-3.762Zm5.99 7.176A9.026 9.026 0 0 0 7 14.935v-3.957l1.818.78a3 3 0 0 0 2.364 0l5.508-2.361a11.026 11.026 0 0 1 .25 3.762a1 1 0 0 1-.89.89a8.968 8.968 0 0 0-5.35 2.524a1 1 0 0 1-1.4 0ZM6 18a1 1 0 0 0 1-1v-2.065a8.935 8.935 0 0 0-2-.712V17a1 1 0 0 0 1 1Z"/></svg>

+ 3 - 1
src/locales/lang/en.ts

@@ -75,7 +75,9 @@ const locale: LocaleMessages<I18nType.Schema> = {
         auth: 'Auth',
         role: 'Role',
         route: 'Route',
-        user: 'User'
+        user: 'User',
+        sort: 'category',
+        student: 'Student'
       },
       about: 'About'
     }

+ 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: {
@@ -76,7 +76,8 @@ const locale: LocaleMessages<I18nType.Schema> = {
         role: '角色管理',
         route: '路由管理',
         user: '用户管理',
-        sort: '课程分类'
+        sort: '课程分类',
+        student: '学生管理'
       },
       about: '关于'
     }

+ 2 - 0
src/main.ts

@@ -1,4 +1,5 @@
 import { createApp } from 'vue';
+import DatePicker from 'ant-design-vue/lib/date-picker';
 import App from './App.vue';
 import AppLoading from './components/common/app-loading.vue';
 import { setupDirectives } from './directives';
@@ -18,6 +19,7 @@ async function setupApp() {
 
   const app = createApp(App);
 
+  app.use(DatePicker);
   // store plugin: pinia
   setupStore(app);
 

+ 1 - 0
src/plugins/assets.ts

@@ -5,6 +5,7 @@ import 'swiper/css/navigation';
 import 'swiper/css/pagination';
 import 'virtual:svg-icons-register';
 import '../styles/css/global.css';
+import 'ant-design-vue/lib/date-picker/style';
 
 /** import static assets: css, js , font and so on. - [引入静态资源,css、js和字体文件等] */
 export default function setupAssets() {}

+ 44 - 3
src/plugins/fast-crud/index.tsx

@@ -1,10 +1,10 @@
 import type { App } from 'vue';
 import type { FsSetupOptions, PageQuery } from '@fast-crud/fast-crud';
 // eslint-disable-next-line import/order
-import { FastCrud } from '@fast-crud/fast-crud';
+import { FastCrud, useTypes } from '@fast-crud/fast-crud';
 import '@fast-crud/fast-crud/dist/style.css';
 import './common.scss';
-
+import locale from 'ant-design-vue/es/date-picker/locale/zh_CN';
 import type { FsUploaderOptions } from '@fast-crud/fast-extends';
 import {
   FsExtendsCopyable,
@@ -160,8 +160,49 @@ function install(app: App, options: FsSetupOpts = {}) {
   app.use(FsExtendsJson);
   app.use(FsExtendsTime);
   app.use(FsExtendsCopyable);
-}
 
+  const { addTypes } = useTypes();
+  const easDate = {
+    easDate: {
+      form: {
+        component: {
+          locale,
+          name: 'a-date-picker',
+          format: 'YYYY-MM-DD',
+          valueFormat: 'YYYY-MM-DD hh:mm',
+          vModel: 'value'
+        }
+      },
+      column: {
+        component: {
+          name: 'fs-date-format',
+          format: 'YYYY-MM-DD'
+        }
+      }
+    }
+  };
+  const easDateTime = {
+    easDateTime: {
+      form: {
+        component: {
+          locale,
+          name: 'a-date-picker',
+          format: 'YYYY-MM-DD hh:mm',
+          valueFormat: 'YYYY-MM-DD hh:mm',
+          vModel: 'value'
+        }
+      },
+      column: {
+        component: {
+          name: 'fs-date-format',
+          format: 'YYYY-MM-DD hh:mm'
+        }
+      }
+    }
+  };
+  addTypes(easDate);
+  addTypes(easDateTime);
+}
 export default {
   install
 };

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

@@ -36,6 +36,17 @@ const management: AuthRoute.Route = {
         icon: 'ic:round-manage-accounts'
       }
     },
+    {
+      name: 'management_student',
+      path: '/management/student',
+      component: 'self',
+      meta: {
+        title: '学员管理',
+        i18nTitle: 'message.routes.management.student',
+        requiresAuth: true,
+        localIcon: 'academic-cap'
+      }
+    },
     {
       name: 'management_sort',
       path: '/management/sort',

+ 0 - 1
src/router/routes/index.ts

@@ -56,7 +56,6 @@ export const constantRoutes: AuthRoute.Route[] = [
       singleLayout: 'blank'
     }
   },
-  // 匹配无效路径的路由
   {
     name: 'not-found',
     path: '/:pathMatch(.*)*',

+ 28 - 17
src/service/api/auth.ts

@@ -1,5 +1,5 @@
 import { localStg } from '~/src/utils';
-import { request,mockRequest } from '../request';
+import { request, mockRequest } from '../request';
 
 /**
  * 获取验证码
@@ -12,9 +12,9 @@ export function fetchSmsCode(phone: string) {
 
 // 参数接口
 export interface AdminLoginParams {
-  username?: string;
-  passwd?: string;
-	captchaVerification?: string
+  username: string;
+  passwd: string;
+  captchaVerification: string;
 }
 
 // 响应接口
@@ -23,13 +23,13 @@ export interface AdminLoginRes {
   msg: string;
   data: Record<string, unknown>;
 }
-/**
- * 管理员登录
- * @param {object} params AdminPojo
- * @param {string} params.username
- * @param {string} params.passwd
- * @returns
- */
+// /**
+//  * 管理员登录
+//  * @param {object} params AdminPojo
+//  * @param {string} params.username
+//  * @param {string} params.passwd
+//  * @returns
+//  */
 // export function adminLogin(params: AdminLoginParams) {
 //   return request.post(`/adminLogin`, params);
 // }
@@ -40,13 +40,24 @@ export interface AdminLoginRes {
  * @param captchaVerification - 验证码
  */
 export function fetchLogin(params: AdminLoginParams) {
-  let res = request.post('/adminLogin',params);
-  return  res;	
+  const res = request.post('/login/adminLogin', params);
+  return res;
+}
+
+/**
+ * 登录
+ * @param userName - 用户名
+ * @param password - 密码
+ * @param captchaVerification - 验证码
+ */
+export function fetchStudentLogin(params: AdminLoginParams) {
+  const res = request.post('/login/studentLogin', params);
+  return res;
 }
 
 /** 获取用户信息 */
-export function fetchUserInfo(param:string) {
-	let res = request.get('/getUserInfo')
+export function fetchUserInfo() {
+  const res = request.get('/login/getUserInfo');
   return res;
 }
 
@@ -56,7 +67,7 @@ export function fetchUserInfo(param:string) {
  * @description 后端根据用户id查询到对应的角色类型,并将路由筛选出对应角色的路由数据返回前端
  */
 export function fetchUserRoutes(userId: number) {
-  return mockRequest.post<ApiRoute.Route>('/getUserRoutes', { userId });
+  return mockRequest.post<ApiRoute.Route>('/login/getUserRoutes', { userId });
 }
 
 /**
@@ -64,5 +75,5 @@ export function fetchUserRoutes(userId: number) {
  * @param refreshToken
  */
 export function fetchUpdateToken(refreshToken: string) {
-  return request.post<ApiAuth.Token>('/refreshToken',  refreshToken );
+  return request.post<ApiAuth.Token>('/login/refreshToken', refreshToken);
 }

+ 5 - 4
src/service/request/index.ts

@@ -5,11 +5,12 @@ const { url, proxyPattern } = getServiceEnvConfig(import.meta.env);
 
 const isHttpProxy = import.meta.env.VITE_HTTP_PROXY === 'Y';
 
-export const request = createRequest({ baseURL: isHttpProxy ? proxyPattern : url,
-	headers: {
-		'X-Requested-With': 'XMLHttpRequest',
+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' });

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

@@ -30,9 +30,9 @@ export default class CustomAxiosInstance {
     backendConfig: Service.BackendResultConfig = {
       codeKey: 'status',
       dataKey: 'data',
-      msgKey: 'code',
+      msgKey: 'msg',
       successCode: true,
-			total:'total'
+      total: 'total'
     }
   ) {
     this.backendConfig = backendConfig;
@@ -45,30 +45,29 @@ 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"]= localStorage.getItem('token') ;
+          handleConfig.headers.Authorization = localStorage.getItem('token');
         }
         return handleConfig;
       },
       (axiosError: AxiosError) => {
         const error = handleAxiosError(axiosError);
-        return handleServiceResult(error, null,null);
+        return handleServiceResult(error, null, null);
       }
     );
     this.instance.interceptors.response.use(
       (async response => {
         const { status } = response;
-        if (status === 200 || status < 300 || status === 304 ) {
+        if (status === 200 || status < 300 || status === 304) {
           const backend = response.data;
-          const { codeKey, dataKey, successCode,total} = this.backendConfig;
+          const { codeKey, dataKey, successCode, total } = this.backendConfig;
           // 请求成功
-          if (backend[codeKey] === successCode ) {
-            return handleServiceResult(null, backend[dataKey],backend[total]);
+          if (backend[codeKey] === successCode) {
+            return handleServiceResult(null, backend[dataKey], backend[total]);
           }
           // token失效, 刷新token
           if (REFRESH_TOKEN_CODE.includes(backend[codeKey])) {
@@ -79,14 +78,14 @@ export default class CustomAxiosInstance {
           }
 
           const error = handleBackendError(backend, this.backendConfig);
-          return handleServiceResult(error, null,null);
+          return handleServiceResult(error, null, null);
         }
         const error = handleResponseError(response);
-        return handleServiceResult(error, null,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,null);
+        return handleServiceResult(error, null, null);
       }
     );
   }

+ 5 - 7
src/store/modules/auth/helpers.ts

@@ -9,16 +9,14 @@ export function getToken() {
 export function getUserInfo() {
   const emptyInfo: Auth.UserInfo = {
     id: null,
-		username: '',
-		phone:null,
-		email:'',
-		permissions:[],
-		departments:[],
+    username: '',
+    phone: null,
+    email: '',
+    permissions: [],
+    departments: [],
     userRole: 'admin'
   };
   const userInfo: Auth.UserInfo = localStg.get('userInfo') || emptyInfo;
-
-	console.log(userInfo);
   return userInfo;
 }
 

+ 18 - 9
src/store/modules/auth/index.ts

@@ -1,7 +1,8 @@
 import { unref, nextTick } from 'vue';
 import { defineStore } from 'pinia';
 import { router } from '@/router';
-import { fetchLogin, fetchUserInfo,AdminLoginParams } from '@/service';
+import type { AdminLoginParams } from '@/service';
+import { fetchLogin, fetchUserInfo, fetchStudentLogin } from '@/service';
 import { useRouterPush } from '@/composables';
 // import { localStg } from '@/utils';
 import { useTabStore } from '../tab';
@@ -60,7 +61,6 @@ export const useAuthStore = defineStore('auth-store', {
       const loginSuccess = await this.loginByToken(backendToken);
 
       if (loginSuccess) {
-				console.log(loginSuccess);
         await route.initAuthRoute();
         // 跳转登录后的地址
         toLoginRedirect();
@@ -91,22 +91,29 @@ export const useAuthStore = defineStore('auth-store', {
       const { token, refreshToken } = backendToken;
       localStorage.setItem('token', token);
       localStorage.setItem('refreshToken', refreshToken);
-			const params=	localStorage.getItem('token');
+      const params = localStorage.getItem('token');
       // 获取用户信息
       const { data } = await fetchUserInfo(params as string);
-			console.log(data);
       if (data) {
         // 成功后把用户信息存储到缓存中
         localStorage.setItem('userInfo', JSON.stringify(data));
-				console.log(localStorage.getItem('userInfo'));
         // 更新状态
-				this.userInfo = data as Auth.UserInfo;
+        this.userInfo = data as Auth.UserInfo;
         this.token = token;
         successFlag = true;
       }
-			console.log(successFlag);
       return successFlag;
+    },
 
+    async studentLogin(params: AdminLoginParams) {
+      this.loginLoading = true;
+      const { data } = await fetchStudentLogin(params);
+      if (data) {
+        await this.handleActionAfterLogin(data as ApiAuth.Token);
+        return true;
+      }
+      this.loginLoading = false;
+      return false;
     },
 
     /**
@@ -115,14 +122,16 @@ export const useAuthStore = defineStore('auth-store', {
      * @param password - 密码
      */
 
-    async login(params:AdminLoginParams) {
+    async login(params: AdminLoginParams) {
       this.loginLoading = true;
       const { data } = await fetchLogin(params);
       if (data) {
         await this.handleActionAfterLogin(data as ApiAuth.Token);
+        return true;
       }
       this.loginLoading = false;
-    },
+      return false;
+    }
     /**
      * 更换用户权限(切换账号)
      * @param userRole

+ 0 - 4
src/store/modules/route/index.ts

@@ -91,9 +91,7 @@ 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) {
@@ -134,9 +132,7 @@ 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;

+ 6 - 0
src/styles/css/global.css

@@ -6,3 +6,9 @@ body,
 #app {
 	height: 100%;
 }
+.ant-picker-dropdown {
+	z-index: 3000;
+}
+.ant-picker {
+	border-radius: 4px;
+}

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

@@ -57,6 +57,7 @@ declare namespace PageRoute {
     | 'management_role'
     | 'management_route'
     | 'management_sort'
+    | 'management_student'
     | 'management_user'
     | 'multi-menu'
     | 'multi-menu_first'
@@ -115,6 +116,7 @@ declare namespace PageRoute {
     | 'management_role'
     | 'management_route'
     | 'management_sort'
+    | 'management_student'
     | 'management_user'
     | 'multi-menu_first_second-new_third'
     | 'multi-menu_first_second'

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

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

+ 13 - 16
src/utils/crypto/index.ts

@@ -6,32 +6,29 @@ const keyword = 'eas-key-password';
  * 加密数据
  * @param data - 数据
  */
-export function encrypto(data: any) {
-	const time = Date.now();
-	 //转码
-	 const wordStr = CryptoJS.enc.Utf8.parse(time + "" + data);
-	 const key = CryptoJS.enc.Utf8.parse(keyword);
-  // const newData = JSON.stringify(data);
-	//加密
+export function encryption(data: any) {
+  const time = Date.now();
+  // 转码
+  const wordStr = CryptoJS.enc.Utf8.parse(String(time) + data);
+  const key = CryptoJS.enc.Utf8.parse(keyword);
   const cryptoStr = CryptoJS.AES.encrypt(wordStr, key, {
-    mode: CryptoJS.mode.ECB, //模式
-    padding: CryptoJS.pad.Pkcs7, //补零
+    mode: CryptoJS.mode.ECB, // 模式
+    padding: CryptoJS.pad.Pkcs7 // 补零
   });
- return cryptoStr.toString();
+  return cryptoStr.toString();
 }
 
 /**
  * 解密数据
  * @param cipherText - 密文
  */
-export function decrypto(cipherText: string) {
-	const key = CryptoJS.enc.Utf8.parse(keyword);
-	//解密
+export function decryption(cipherText: string) {
+  const key = CryptoJS.enc.Utf8.parse(keyword);
+  // 解密
   const cryptoStr = CryptoJS.AES.decrypt(cipherText, key, {
-    mode: CryptoJS.mode.ECB, //模式
-    padding: CryptoJS.pad.Pkcs7, //补零
+    mode: CryptoJS.mode.ECB, // 模式
+    padding: CryptoJS.pad.Pkcs7 // 补零
   });
 
   return CryptoJS.enc.Utf8.stringify(cryptoStr).toString();
-
 }

+ 11 - 0
src/utils/form/date.ts

@@ -0,0 +1,11 @@
+export const formatTimestamp = (timestamp: number): string => {
+  const date = new Date(timestamp);
+
+  const year = date.getUTCFullYear();
+  const month = `0${date.getUTCMonth() + 1}`.slice(-2);
+  const day = `0${date.getUTCDate()}`.slice(-2);
+  const hour = `0${date.getUTCHours()}`.slice(-2);
+  const minute = `0${date.getUTCMinutes()}`.slice(-2);
+
+  return `${year}-${month}-${day} ${hour}:${minute}`;
+};

+ 1 - 0
src/utils/form/index.ts

@@ -1 +1,2 @@
 export * from './rule';
+export * from './date';

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

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

+ 1 - 1
src/utils/router/regexp.ts

@@ -1,5 +1,5 @@
 /** 获取登录页面模块的动态路由的正则 */
 export function getLoginModuleRegExp() {
-  const modules: UnionKey.LoginModule[] = ['pwd-login', 'code-login', 'register', 'reset-pwd', 'bind-wechat'];
+  const modules: UnionKey.LoginModule[] = ['pwd-login'];
   return modules.join('|');
 }

+ 4 - 4
src/utils/storage/local.ts

@@ -1,5 +1,5 @@
-import { decrypto, encrypto } from '../crypto';
- interface StorageData<T> {
+import { decryption, encryption } from '../crypto';
+interface StorageData<T> {
   value: T;
   expire: number | null;
 }
@@ -12,7 +12,7 @@ function createLocalStorage<T extends StorageInterface.Local = StorageInterface.
       value,
       expire: expire !== null ? new Date().getTime() + expire * 1000 : null
     };
-    const json = encrypto(storageData);
+    const json = encryption(storageData);
     window.localStorage.setItem(key as string, json);
   }
 
@@ -21,7 +21,7 @@ function createLocalStorage<T extends StorageInterface.Local = StorageInterface.
     if (json) {
       let storageData: StorageData<T[K]> | null = null;
       try {
-        storageData = decrypto(json);
+        storageData = decryption(json);
       } catch {
         // 防止解析失败
       }

+ 3 - 3
src/utils/storage/session.ts

@@ -1,8 +1,8 @@
-import { decrypto, encrypto } from '../crypto';
+import { decryption, encryption } from '../crypto';
 
 function createSessionStorage<T extends StorageInterface.Session = StorageInterface.Session>() {
   function set<K extends keyof T>(key: K, value: T[K]) {
-    const json = encrypto(value);
+    const json = encryption(value);
     sessionStorage.setItem(key as string, json);
   }
   function get<K extends keyof T>(key: K) {
@@ -10,7 +10,7 @@ function createSessionStorage<T extends StorageInterface.Session = StorageInterf
     let data: T[K] | null = null;
     if (json) {
       try {
-        data = decrypto(json);
+        data = decryption(json);
       } catch {
         // 防止解析失败
       }

+ 0 - 0
src/views/_builtin/login/components/pwd-login/components/verifition/Verify.vue → src/views/_builtin/login/components/pwd-login/components/validate/Verify.vue


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


Diferenças do arquivo suprimidas por serem muito extensas
+ 583 - 0
src/views/_builtin/login/components/pwd-login/components/validate/Verify/VerifySlide.vue


+ 24 - 0
src/views/_builtin/login/components/pwd-login/components/validate/api/index.js

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

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


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


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


Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 549
src/views/_builtin/login/components/pwd-login/components/verifition/Verify/VerifySlide.vue


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

@@ -1,27 +0,0 @@
-/**
- * 此处可直接引用自己项目封装好的 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
-    })
-}
-
-

+ 41 - 15
src/views/_builtin/login/components/pwd-login/index.vue

@@ -1,10 +1,10 @@
 <template>
   <n-form ref="formRef" :model="model" :rules="rules" size="large" :show-label="false">
     <n-form-item path="userName">
-      <n-input v-model:value="model.userName" placeholder="请输入用户名" />
+      <n-input v-model:value="model.username" placeholder="请输入用户名" />
     </n-form-item>
     <n-form-item path="password">
-      <n-input v-model:value="model.password" type="password" show-password-on="click" placeholder="请输入密码" />
+      <n-input v-model:value="model.passwd" type="password" show-password-on="click" placeholder="请输入密码" />
     </n-form-item>
     <n-space :vertical="true" :size="24">
       <div class="flex-y-center justify-between">
@@ -20,7 +20,7 @@
       >
         确定
       </n-button>
-      <other-account @login="handleLoginOtherAccount" />
+      <other-account @change="handleLoginOtherAccount" />
     </n-space>
   </n-form>
   <div v-show="mode === 'pop'" class="mask">
@@ -41,17 +41,26 @@
 <script setup lang="ts">
 import { reactive, ref } from 'vue';
 import type { FormInst, FormRules } from 'naive-ui';
+import type { AdminLoginParams } from '@/service';
 import { useAuthStore } from '@/store';
 import { formRules } from '@/utils';
-import { encrypto } from '@/utils/crypto/index';
+import { encryption } from '@/utils/crypto/index';
 import { OtherAccount } from './components';
-import VerifySlide from './components/verifition/Verify/VerifySlide.vue';
+import VerifySlide from './components/validate/Verify/VerifySlide.vue';
 
 const mode = ref('push');
 const closeBox = () => {
   mode.value = 'push';
 };
 
+const loginType = ref<string>('admin');
+
+const model = reactive<AdminLoginParams>({
+  username: 'admin',
+  passwd: '123456',
+  captchaVerification: ''
+});
+
 function verifySlideClose(result: any) {
   model.captchaVerification = result.captchaVerification;
   mode.value = 'push';
@@ -59,13 +68,20 @@ function verifySlideClose(result: any) {
 }
 
 const auth = useAuthStore();
-const { login } = useAuthStore();
+const { login, studentLogin } = useAuthStore();
+interface loginByType {
+  [index: string]: (params: AdminLoginParams) => Promise<boolean>;
+}
+const userLoginType: loginByType = {
+  admin: (params: AdminLoginParams) => {
+    return login(params);
+  },
+  student: (params: AdminLoginParams) => {
+    return studentLogin(params);
+  }
+};
+
 const formRef = ref<HTMLElement & FormInst>();
-const model = reactive({
-  userName: 'admin',
-  password: '123456',
-  captchaVerification: ''
-});
 
 const rules: FormRules = {
   password: formRules.pwd
@@ -74,9 +90,19 @@ const rememberMe = ref(false);
 async function handleSubmit() {
   mode.value = 'pop';
   await formRef.value?.validate();
-  const { userName, password, captchaVerification } = model;
-  if (captchaVerification) {
-    login({ username: userName, passwd: encrypto(password), captchaVerification });
+  const { username, passwd, captchaVerification } = model;
+  if (captchaVerification !== '') {
+    const result = await userLoginType[loginType.value]({
+      username,
+      passwd: encryption(passwd),
+      captchaVerification
+    });
+    if (result === false) {
+      mode.value = 'push';
+      if (model.captchaVerification !== '') {
+        model.captchaVerification = '';
+      }
+    }
   } else {
     mode.value = 'pop';
   }
@@ -84,7 +110,7 @@ async function handleSubmit() {
 
 function handleLoginOtherAccount(param: { type: string }) {
   const { type } = param;
-  console.log(type);
+  loginType.value = type;
 }
 </script>
 

+ 3 - 1
src/views/_builtin/login/index.vue

@@ -15,7 +15,9 @@
           <h3 class="text-18px text-primary font-medium">{{ activeModule.label }}</h3>
           <div class="pt-24px">
             <transition name="fade-slide" mode="out-in" appear>
-              <component :is="activeModule.component" />
+              <div>
+                <component :is="activeModule.component" />
+              </div>
             </transition>
           </div>
         </main>

+ 1 - 0
src/views/index.ts

@@ -36,6 +36,7 @@ export const views: Record<
   management_role: () => import('./management/role/index.vue'),
   management_route: () => import('./management/route/index.vue'),
   management_sort: () => import('./management/sort/index.vue'),
+  management_student: () => import('./management/student/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'),

+ 53 - 0
src/views/management/student/api.ts

@@ -0,0 +1,53 @@
+import { request } from '@/service/request';
+
+// 参数接口
+export interface Add_Params {
+  id?: number;
+  archiveNumber?: string;
+  studentNumber?: string;
+  studentName?: string;
+  gender?: string;
+  birthdate?: Record<string, unknown>;
+  address?: string;
+  phone?: string;
+  email?: string;
+  enrollmentDate?: Record<string, unknown>;
+  graduationDate?: Record<string, unknown>;
+  grade?: number;
+  major?: string;
+  minor?: string;
+  university?: string;
+  createTime?: Record<string, unknown>;
+  modifyTime?: Record<string, unknown>;
+  admissionsId?: number;
+  managerId?: number;
+  createUid?: number;
+  studentIdnumber?: string;
+}
+
+export interface Add_6Res {
+  status: boolean;
+  msg: string;
+  data: Record<string, unknown>;
+  code: number;
+}
+
+export function editRequest(params: Add_Params): Promise<Service.RequestResult<Add_6Res>> {
+  return request.post(`/update`, params);
+}
+
+export function pageRequest(
+  pageNum: number,
+  pageSize: number,
+  params: Add_Params
+): Promise<Service.RequestResult<Add_6Res>> {
+  return request.post(`/query?pageNum=${pageNum}&pageSize=${pageSize}`, params);
+}
+
+export function delRequest(id: number): Promise<Service.RequestResult<Add_6Res>> {
+  return request.post(`/del/${id}`);
+}
+
+export function addRequest(params: Add_Params): Promise<Service.RequestResult<Add_6Res>> {
+  return request.post(`/add`, params);
+}

+ 149 - 0
src/views/management/student/crud.ts

@@ -0,0 +1,149 @@
+import type { CreateCrudOptionsRet } from '@fast-crud/fast-crud';
+import { dict } from '@fast-crud/fast-crud';
+import { addRequest, delRequest, editRequest, pageRequest } from './api';
+
+function curd(): CreateCrudOptionsRet {
+  return {
+    crudOptions: {
+      request: {
+        pageRequest: async ({ page, query }) => {
+          const { total, data } = await pageRequest(page.offset + 1, page.limit, query);
+          return { records: data, total, currentPage: page.offset, pageSize: page.limit };
+        },
+        addRequest: ({ form }) => {
+          return addRequest(form);
+        },
+        editRequest: ({ form }) => {
+          editRequest(form);
+        },
+        delRequest: ({ row }) => {
+          return delRequest(row.id);
+        }
+      },
+      columns: {
+        studentName: {
+          title: '姓名',
+          type: 'text',
+          search: { show: true },
+          column: {
+            resizable: true,
+            width: 60,
+            align: 'center',
+            fixed: 'left'
+          }
+        },
+        phone: {
+          title: '手机',
+          type: 'text',
+          search: { show: true },
+          column: {
+            resizable: true,
+            width: 120,
+            align: 'center',
+            fixed: 'left'
+          }
+        },
+        studentIdnumber: {
+          title: '身份证号',
+          type: 'text',
+          search: { show: true },
+          column: {
+            resizable: true,
+            width: 180,
+            align: 'center',
+            fixed: 'left'
+          }
+        },
+        university: {
+          title: '学校',
+          type: 'text',
+          search: { show: true },
+          column: {
+            resizable: true,
+            width: 120,
+            align: 'center',
+            fixed: 'left'
+          }
+        },
+        address: {
+          title: '住址',
+          type: 'text',
+          search: { show: true },
+          column: {
+            resizable: true,
+            width: 180,
+            align: 'center',
+            fixed: 'left'
+          }
+        },
+        email: {
+          title: '邮箱',
+          type: 'text',
+          search: { show: true },
+          column: {
+            resizable: true,
+            width: 180,
+            align: 'center',
+            fixed: 'left'
+          }
+        },
+        birthdate: {
+          title: '生日',
+          type: 'easDate',
+          search: { show: true },
+          column: {
+            resizable: true,
+            align: 'center',
+            width: 120
+          }
+        },
+        studentNumber: {
+          title: '学生档案号',
+          type: 'text',
+          search: { show: true },
+          column: {
+            resizable: true,
+            align: 'center',
+            width: 200
+          },
+          form: {
+            show: false
+          }
+        },
+        enrollmentDate: {
+          title: '入学时间',
+          type: 'easDate',
+          search: { show: true },
+          column: {
+            resizable: true,
+            width: 100
+          }
+        },
+        graduationDate: {
+          title: '毕业时间',
+          type: 'easDate',
+          search: { show: true },
+          column: {
+            resizable: true,
+            width: 100
+          }
+        },
+        gender: {
+          title: '性别',
+          type: 'dict-select',
+          dict: dict({
+            data: [
+              { value: 'M', label: '男' },
+              { value: 'F', label: '女' }
+            ]
+          }),
+          column: {
+            resizable: true,
+            width: 40
+          }
+        }
+      }
+    }
+  };
+}
+export default curd;

+ 17 - 0
src/views/management/student/index.vue

@@ -0,0 +1,17 @@
+<template>
+  <div class="h-full">
+    <fs-crud ref="crudRef" v-bind="crudBinding" />
+  </div>
+</template>
+
+<script setup lang="ts">
+import { onMounted } from 'vue';
+import { useFs } from '@fast-crud/fast-crud';
+import createCrudOptions from './crud';
+const { crudRef, crudBinding, crudExpose } = useFs({ createCrudOptions });
+onMounted(() => {
+  crudExpose.doRefresh();
+});
+</script>
+
+<style scoped></style>

+ 1 - 0
tsconfig.json

@@ -5,6 +5,7 @@
     "target": "ESNext",
     "lib": ["DOM", "ESNext"],
     "strict": true,
+		"allowJs": true,
     "esModuleInterop": true,
     "allowSyntheticDefaultImports": true,
     "jsx": "preserve",

Alguns arquivos não foram mostrados porque muitos arquivos mudaram nesse diff